FEATURE: [xalign] add notification when order quote is over alert amount

This commit is contained in:
kbearXD 2024-10-24 17:50:01 +08:00
parent 822c2bd5e0
commit b47fc084cb
2 changed files with 102 additions and 12 deletions

View File

@ -47,3 +47,9 @@ crossExchangeStrategies:
USDT: 100 USDT: 100
USDC: 100 USDC: 100
TWD: 3000 TWD: 3000
largeAmountAlert:
quote: USDT
amount: 200
slackMentions:
- '<@USER_ID>'
- '<!subteam^TEAM_ID>'

View File

@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/slack-go/slack"
"golang.org/x/time/rate" "golang.org/x/time/rate"
"github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/bbgo"
@ -39,6 +40,55 @@ type QuoteCurrencyPreference struct {
Sell []string `json:"sell"` Sell []string `json:"sell"`
} }
type AmountAlertConfig struct {
QuoteCurrency string `json:"quoteCurrency"`
Amount fixedpoint.Value `json:"amount"`
SlackMentions []string `json:"slackMentions"`
}
type LargeAmountAlert struct {
QuoteCurrency string
AlertAmount fixedpoint.Value
SlackMentions []string
BaseCurrency string
Side types.SideType
Price fixedpoint.Value
Quantity fixedpoint.Value
Amount fixedpoint.Value
}
func (m *LargeAmountAlert) SlackAttachment() slack.Attachment {
return slack.Attachment{
Color: "red",
Title: fmt.Sprintf("xalign amount alert - try to align %s with quote %s amount %f > %f",
m.BaseCurrency, m.QuoteCurrency, m.Amount.Float64(), m.AlertAmount.Float64()),
Text: strings.Join(m.SlackMentions, " "),
Fields: []slack.AttachmentField{
{
Title: "Base Currency",
Value: m.BaseCurrency,
Short: true,
},
{
Title: "Side",
Value: m.Side.String(),
Short: true,
},
{
Title: "Price",
Value: m.Price.String(),
Short: true,
},
{
Title: "Quantity",
Value: m.Quantity.String(),
Short: true,
},
},
}
}
type Strategy struct { type Strategy struct {
*bbgo.Environment *bbgo.Environment
Interval types.Interval `json:"interval"` Interval types.Interval `json:"interval"`
@ -50,6 +100,7 @@ type Strategy struct {
BalanceToleranceRange fixedpoint.Value `json:"balanceToleranceRange"` BalanceToleranceRange fixedpoint.Value `json:"balanceToleranceRange"`
Duration types.Duration `json:"for"` Duration types.Duration `json:"for"`
MaxAmounts map[string]fixedpoint.Value `json:"maxAmounts"` MaxAmounts map[string]fixedpoint.Value `json:"maxAmounts"`
LargeAmountAlert *AmountAlertConfig `json:"largeAmountAlert"`
SlackNotify bool `json:"slackNotify"` SlackNotify bool `json:"slackNotify"`
SlackNotifyMentions []string `json:"slackNotifyMentions"` SlackNotifyMentions []string `json:"slackNotifyMentions"`
@ -84,7 +135,6 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
} }
func (s *Strategy) CrossSubscribe(sessions map[string]*bbgo.ExchangeSession) { func (s *Strategy) CrossSubscribe(sessions map[string]*bbgo.ExchangeSession) {
} }
func (s *Strategy) Defaults() error { func (s *Strategy) Defaults() error {
@ -191,9 +241,6 @@ func (s *Strategy) detectActiveDeposit(
func (s *Strategy) selectSessionForCurrency( func (s *Strategy) selectSessionForCurrency(
ctx context.Context, sessions map[string]*bbgo.ExchangeSession, currency string, changeQuantity fixedpoint.Value, ctx context.Context, sessions map[string]*bbgo.ExchangeSession, currency string, changeQuantity fixedpoint.Value,
) (*bbgo.ExchangeSession, *types.SubmitOrder) { ) (*bbgo.ExchangeSession, *types.SubmitOrder) {
for _, sessionName := range s.PreferredSessions {
session := sessions[sessionName]
var taker = s.UseTakerOrder var taker = s.UseTakerOrder
var side types.SideType var side types.SideType
var quoteCurrencies []string var quoteCurrencies []string
@ -205,6 +252,9 @@ func (s *Strategy) selectSessionForCurrency(
side = types.SideTypeSell side = types.SideTypeSell
} }
for _, sessionName := range s.PreferredSessions {
session := sessions[sessionName]
for _, fromQuoteCurrency := range quoteCurrencies { for _, fromQuoteCurrency := range quoteCurrencies {
// skip the same currency, because there is no such USDT/USDT market // skip the same currency, because there is no such USDT/USDT market
if currency == fromQuoteCurrency { if currency == fromQuoteCurrency {
@ -405,6 +455,16 @@ func (s *Strategy) CrossRun(ctx context.Context, _ bbgo.OrderExecutionRouter, se
} }
s.priceResolver = pricesolver.NewSimplePriceResolver(markets) s.priceResolver = pricesolver.NewSimplePriceResolver(markets)
for _, session := range s.sessions {
// init the price
marketPrices := session.LastPrices()
for market, price := range marketPrices {
s.priceResolver.Update(market, price)
}
// bind on trade to update price
session.UserDataStream.OnTradeUpdate(s.priceResolver.UpdateFromTrade)
}
bbgo.OnShutdown(ctx, func(ctx context.Context, wg *sync.WaitGroup) { bbgo.OnShutdown(ctx, func(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
@ -528,6 +588,30 @@ func (s *Strategy) align(ctx context.Context, sessions map[string]*bbgo.Exchange
} }
} }
if price, ok := s.priceResolver.ResolvePrice(currency, s.LargeAmountAlert.QuoteCurrency); ok {
quantity := q.Abs()
amount := price.Mul(quantity)
if amount.Compare(s.LargeAmountAlert.Amount) > 0 {
alert := &LargeAmountAlert{
QuoteCurrency: s.LargeAmountAlert.QuoteCurrency,
AlertAmount: s.LargeAmountAlert.Amount,
SlackMentions: s.LargeAmountAlert.SlackMentions,
BaseCurrency: currency,
Price: price,
Quantity: quantity,
Amount: amount,
}
if q.Sign() > 0 {
alert.Side = types.SideTypeBuy
} else {
alert.Side = types.SideTypeSell
}
bbgo.Notify(alert)
}
}
selectedSession, submitOrder := s.selectSessionForCurrency(ctx, sessions, currency, q) selectedSession, submitOrder := s.selectSessionForCurrency(ctx, sessions, currency, q)
if selectedSession != nil && submitOrder != nil { if selectedSession != nil && submitOrder != nil {
log.Infof("placing %s order on %s: %+v", submitOrder.Symbol, selectedSession.Name, submitOrder) log.Infof("placing %s order on %s: %+v", submitOrder.Symbol, selectedSession.Name, submitOrder)