2022-12-06 02:05:43 +00:00
|
|
|
package grid2
|
|
|
|
|
|
|
|
import (
|
2022-12-15 07:39:16 +00:00
|
|
|
"fmt"
|
|
|
|
"strconv"
|
2022-12-15 07:39:48 +00:00
|
|
|
"time"
|
2022-12-15 07:39:16 +00:00
|
|
|
|
|
|
|
"github.com/slack-go/slack"
|
|
|
|
|
2022-12-06 02:05:43 +00:00
|
|
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
2022-12-15 07:39:16 +00:00
|
|
|
"github.com/c9s/bbgo/pkg/style"
|
2022-12-06 02:05:43 +00:00
|
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
type GridProfitStats struct {
|
|
|
|
Symbol string `json:"symbol"`
|
|
|
|
TotalBaseProfit fixedpoint.Value `json:"totalBaseProfit,omitempty"`
|
|
|
|
TotalQuoteProfit fixedpoint.Value `json:"totalQuoteProfit,omitempty"`
|
|
|
|
FloatProfit fixedpoint.Value `json:"floatProfit,omitempty"`
|
|
|
|
GridProfit fixedpoint.Value `json:"gridProfit,omitempty"`
|
|
|
|
ArbitrageCount int `json:"arbitrageCount,omitempty"`
|
|
|
|
TotalFee map[string]fixedpoint.Value `json:"totalFee,omitempty"`
|
|
|
|
Volume fixedpoint.Value `json:"volume,omitempty"`
|
|
|
|
Market types.Market `json:"market,omitempty"`
|
2022-12-15 07:39:48 +00:00
|
|
|
Since *time.Time `json:"since,omitempty"`
|
2022-12-25 17:35:37 +00:00
|
|
|
InitialOrderID uint64 `json:"initialOrderID"`
|
2022-12-06 02:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func newGridProfitStats(market types.Market) *GridProfitStats {
|
|
|
|
return &GridProfitStats{
|
|
|
|
Symbol: market.Symbol,
|
|
|
|
TotalBaseProfit: fixedpoint.Zero,
|
|
|
|
TotalQuoteProfit: fixedpoint.Zero,
|
|
|
|
FloatProfit: fixedpoint.Zero,
|
|
|
|
GridProfit: fixedpoint.Zero,
|
|
|
|
ArbitrageCount: 0,
|
|
|
|
TotalFee: make(map[string]fixedpoint.Value),
|
|
|
|
Volume: fixedpoint.Zero,
|
|
|
|
Market: market,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *GridProfitStats) AddTrade(trade types.Trade) {
|
|
|
|
if s.TotalFee == nil {
|
|
|
|
s.TotalFee = make(map[string]fixedpoint.Value)
|
|
|
|
}
|
|
|
|
|
|
|
|
if fee, ok := s.TotalFee[trade.FeeCurrency]; ok {
|
|
|
|
s.TotalFee[trade.FeeCurrency] = fee.Add(trade.Fee)
|
|
|
|
} else {
|
|
|
|
s.TotalFee[trade.FeeCurrency] = trade.Fee
|
|
|
|
}
|
2022-12-15 09:47:34 +00:00
|
|
|
|
|
|
|
if s.Since == nil {
|
|
|
|
t := trade.Time.Time()
|
|
|
|
s.Since = &t
|
|
|
|
}
|
2022-12-06 02:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *GridProfitStats) AddProfit(profit *GridProfit) {
|
|
|
|
// increase arbitrage count per profit round
|
|
|
|
s.ArbitrageCount++
|
|
|
|
|
|
|
|
switch profit.Currency {
|
|
|
|
case s.Market.QuoteCurrency:
|
|
|
|
s.TotalQuoteProfit = s.TotalQuoteProfit.Add(profit.Profit)
|
|
|
|
case s.Market.BaseCurrency:
|
|
|
|
s.TotalBaseProfit = s.TotalBaseProfit.Add(profit.Profit)
|
|
|
|
}
|
|
|
|
}
|
2022-12-15 07:39:16 +00:00
|
|
|
|
|
|
|
func (s *GridProfitStats) SlackAttachment() slack.Attachment {
|
|
|
|
var fields = []slack.AttachmentField{
|
|
|
|
{
|
|
|
|
Title: "Arbitrage Count",
|
|
|
|
Value: strconv.Itoa(s.ArbitrageCount),
|
|
|
|
Short: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
if !s.FloatProfit.IsZero() {
|
|
|
|
fields = append(fields, slack.AttachmentField{
|
|
|
|
Title: "Float Profit",
|
|
|
|
Value: style.PnLSignString(s.FloatProfit),
|
|
|
|
Short: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if !s.GridProfit.IsZero() {
|
|
|
|
fields = append(fields, slack.AttachmentField{
|
|
|
|
Title: "Total Grid Profit",
|
|
|
|
Value: style.PnLSignString(s.GridProfit),
|
|
|
|
Short: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if !s.TotalQuoteProfit.IsZero() {
|
|
|
|
fields = append(fields, slack.AttachmentField{
|
|
|
|
Title: "Total Quote Profit",
|
|
|
|
Value: style.PnLSignString(s.TotalQuoteProfit),
|
|
|
|
Short: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if !s.TotalBaseProfit.IsZero() {
|
|
|
|
fields = append(fields, slack.AttachmentField{
|
|
|
|
Title: "Total Base Profit",
|
|
|
|
Value: style.PnLSignString(s.TotalBaseProfit),
|
|
|
|
Short: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(s.TotalFee) > 0 {
|
|
|
|
for feeCurrency, fee := range s.TotalFee {
|
|
|
|
fields = append(fields, slack.AttachmentField{
|
|
|
|
Title: fmt.Sprintf("Fee (%s)", feeCurrency),
|
|
|
|
Value: fee.String() + " " + feeCurrency,
|
|
|
|
Short: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 09:47:34 +00:00
|
|
|
footer := "Total grid profit stats"
|
|
|
|
if s.Since != nil {
|
|
|
|
footer += fmt.Sprintf(" since %s", s.Since.String())
|
|
|
|
}
|
|
|
|
|
2022-12-15 10:09:43 +00:00
|
|
|
title := fmt.Sprintf("%s Grid Profit Stats", s.Symbol)
|
2022-12-15 07:39:16 +00:00
|
|
|
return slack.Attachment{
|
2022-12-15 10:09:43 +00:00
|
|
|
Title: title,
|
2022-12-15 07:39:16 +00:00
|
|
|
Color: "warning",
|
|
|
|
Fields: fields,
|
2022-12-15 09:47:34 +00:00
|
|
|
Footer: footer,
|
2022-12-15 07:39:16 +00:00
|
|
|
}
|
|
|
|
}
|
2023-02-06 08:59:50 +00:00
|
|
|
|
|
|
|
func (s *GridProfitStats) String() string {
|
|
|
|
return s.PlainText()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *GridProfitStats) PlainText() string {
|
|
|
|
var o string
|
|
|
|
|
|
|
|
o = fmt.Sprintf("%s Grid Profit Stats", s.Symbol)
|
|
|
|
|
|
|
|
o += fmt.Sprintf(" Arbitrage count: %d", s.ArbitrageCount)
|
|
|
|
|
|
|
|
if !s.FloatProfit.IsZero() {
|
|
|
|
o += " Float profit: " + style.PnLSignString(s.FloatProfit)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !s.GridProfit.IsZero() {
|
|
|
|
o += " Grid profit: " + style.PnLSignString(s.GridProfit)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !s.TotalQuoteProfit.IsZero() {
|
|
|
|
o += " Total quote profit: " + style.PnLSignString(s.TotalQuoteProfit) + " " + s.Market.QuoteCurrency
|
|
|
|
}
|
|
|
|
|
|
|
|
if !s.TotalBaseProfit.IsZero() {
|
|
|
|
o += " Total base profit: " + style.PnLSignString(s.TotalBaseProfit) + " " + s.Market.BaseCurrency
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(s.TotalFee) > 0 {
|
|
|
|
for feeCurrency, fee := range s.TotalFee {
|
|
|
|
o += fmt.Sprintf(" Fee (%s)", feeCurrency) + fee.String() + " " + feeCurrency
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if s.Since != nil {
|
|
|
|
o += fmt.Sprintf(" Since %s", s.Since.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
return o
|
|
|
|
}
|