Кабинет: структура портфеля и ближайшие выплаты за 90 дней (купоны, оферты, погашения, дивиденды)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL
This commit is contained in:
+62
-3
@@ -425,6 +425,58 @@ type lkPosition struct {
|
||||
Note string `json:"note"`
|
||||
}
|
||||
|
||||
// lkUpcoming — ожидаемая выплата по бумаге портфеля.
|
||||
type lkUpcoming struct {
|
||||
Date string `json:"date"`
|
||||
ISIN string `json:"isin"`
|
||||
Security string `json:"security"`
|
||||
Kind string `json:"kind"`
|
||||
PerUnit float64 `json:"per_unit"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
Total float64 `json:"total"`
|
||||
Percent float64 `json:"percent"`
|
||||
}
|
||||
|
||||
// lkSegment — доля вида активов в портфеле для полосы структуры.
|
||||
type lkSegment struct {
|
||||
Kind string
|
||||
Value float64
|
||||
Share float64
|
||||
Color string
|
||||
}
|
||||
|
||||
// portfolioStructure — структура портфеля по видам активов (бумаги по виду + деньги).
|
||||
func portfolioStructure(p lkPortfolio) []lkSegment {
|
||||
if p.Total <= 0 {
|
||||
return nil
|
||||
}
|
||||
colors := map[string]string{"акция": "#e8a33d", "облигация": "#4f7cac", "фонд": "#5f9e7a", "пай": "#5f9e7a", "депозитарная расписка": "#b07aa1", "деньги": "#8e8879"}
|
||||
byKind := map[string]float64{}
|
||||
for _, pos := range p.Positions {
|
||||
k := pos.Kind
|
||||
if k == "" || k == "бумага" {
|
||||
k = "прочее"
|
||||
}
|
||||
byKind[k] += pos.Value
|
||||
}
|
||||
if p.CashRUB > 0 {
|
||||
byKind["деньги"] += p.CashRUB
|
||||
}
|
||||
var out []lkSegment
|
||||
for k, v := range byKind {
|
||||
if v <= 0 {
|
||||
continue
|
||||
}
|
||||
c := colors[k]
|
||||
if c == "" {
|
||||
c = "#c9c3b4"
|
||||
}
|
||||
out = append(out, lkSegment{Kind: k, Value: v, Share: v / p.Total * 100, Color: c})
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Value > out[j].Value })
|
||||
return out
|
||||
}
|
||||
|
||||
// lkOpRow — операция по брокерскому счёту (ответ /api/lk/ops).
|
||||
type lkOpRow struct {
|
||||
Date string `json:"date"`
|
||||
@@ -706,9 +758,10 @@ func (s *Server) buildHandler() http.Handler {
|
||||
q.Set("date", date)
|
||||
}
|
||||
var out struct {
|
||||
Portfolio lkPortfolio `json:"portfolio"`
|
||||
Contract string `json:"contract"`
|
||||
FullName string `json:"full_name"`
|
||||
Portfolio lkPortfolio `json:"portfolio"`
|
||||
Contract string `json:"contract"`
|
||||
FullName string `json:"full_name"`
|
||||
Upcoming []lkUpcoming `json:"upcoming"`
|
||||
}
|
||||
path := "/api/lk/portfolio2"
|
||||
if len(q) > 0 {
|
||||
@@ -722,6 +775,12 @@ func (s *Server) buildHandler() http.Handler {
|
||||
return
|
||||
}
|
||||
data["P"], data["Contract"], data["FullName"] = out.Portfolio, out.Contract, out.FullName
|
||||
data["Structure"], data["Upcoming"] = portfolioStructure(out.Portfolio), out.Upcoming
|
||||
var upTotal float64
|
||||
for _, u := range out.Upcoming {
|
||||
upTotal += u.Total
|
||||
}
|
||||
data["UpcomingTotal"] = upTotal
|
||||
s.render(w, r, "portfolio", data)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user