47 lines
1.5 KiB
Go
47 lines
1.5 KiB
Go
|
|
package report
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/shopspring/decimal"
|
||
|
|
|
||
|
|
"overnight-trading-bot/internal/domain"
|
||
|
|
)
|
||
|
|
|
||
|
|
type DailyInput struct {
|
||
|
|
Date time.Time
|
||
|
|
Mode domain.Mode
|
||
|
|
Signals []domain.Signal
|
||
|
|
Positions []domain.Position
|
||
|
|
AverageSpreadBps decimal.Decimal
|
||
|
|
AverageSlipBps decimal.Decimal
|
||
|
|
RiskStatus string
|
||
|
|
}
|
||
|
|
|
||
|
|
func ComposeDaily(input DailyInput) string {
|
||
|
|
var b strings.Builder
|
||
|
|
fmt.Fprintf(&b, "Дата: %s\n", input.Date.Format("2006-01-02"))
|
||
|
|
fmt.Fprintf(&b, "Режим: %s\n", input.Mode)
|
||
|
|
fmt.Fprintf(&b, "Сигналы: %d\n", len(input.Signals))
|
||
|
|
for _, signal := range input.Signals {
|
||
|
|
fmt.Fprintf(&b, "- %s %s edge=%s reason=%s\n", signal.InstrumentUID, signal.Decision, signal.NetEdgeBps.StringFixed(2), signal.RejectReason)
|
||
|
|
}
|
||
|
|
gross := decimal.Zero
|
||
|
|
net := decimal.Zero
|
||
|
|
commission := decimal.Zero
|
||
|
|
for _, pos := range input.Positions {
|
||
|
|
gross = gross.Add(pos.GrossPnL)
|
||
|
|
net = net.Add(pos.NetPnL)
|
||
|
|
commission = commission.Add(pos.CommissionTotal)
|
||
|
|
}
|
||
|
|
fmt.Fprintf(&b, "Gross PnL: %s\n", gross.StringFixed(2))
|
||
|
|
fmt.Fprintf(&b, "Net PnL: %s\n", net.StringFixed(2))
|
||
|
|
fmt.Fprintf(&b, "Комиссии: %s\n", commission.StringFixed(2))
|
||
|
|
fmt.Fprintf(&b, "Средний spread: %s bps\n", input.AverageSpreadBps.StringFixed(2))
|
||
|
|
fmt.Fprintf(&b, "Среднее проскальзывание: %s bps\n", input.AverageSlipBps.StringFixed(2))
|
||
|
|
fmt.Fprintf(&b, "Risk: %s", input.RiskStatus)
|
||
|
|
return b.String()
|
||
|
|
}
|