Merge pull request #1484 from c9s/narumi/xgap/check-balance

FEATURE: [xgap] check balance before placing orders
This commit is contained in:
なるみ 2024-01-07 00:26:19 +08:00 committed by GitHub
commit b7a397bb4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -22,7 +22,7 @@ const ID = "xgap"
var log = logrus.WithField("strategy", ID) var log = logrus.WithField("strategy", ID)
var StepPercentageGap = fixedpoint.NewFromFloat(0.05) var StepPercentageGap = fixedpoint.NewFromFloat(0.05)
var NotionModifier = fixedpoint.NewFromFloat(1.01)
var Two = fixedpoint.NewFromInt(2) var Two = fixedpoint.NewFromInt(2)
func init() { func init() {
@ -68,6 +68,7 @@ type Strategy struct {
TradingExchange string `json:"tradingExchange"` TradingExchange string `json:"tradingExchange"`
MinSpread fixedpoint.Value `json:"minSpread"` MinSpread fixedpoint.Value `json:"minSpread"`
Quantity fixedpoint.Value `json:"quantity"` Quantity fixedpoint.Value `json:"quantity"`
DryRun bool `json:"dryRun"`
DailyFeeBudgets map[string]fixedpoint.Value `json:"dailyFeeBudgets,omitempty"` DailyFeeBudgets map[string]fixedpoint.Value `json:"dailyFeeBudgets,omitempty"`
DailyMaxVolume fixedpoint.Value `json:"dailyMaxVolume,omitempty"` DailyMaxVolume fixedpoint.Value `json:"dailyMaxVolume,omitempty"`
@ -320,10 +321,35 @@ func (s *Strategy) placeOrders(ctx context.Context) {
log.Infof("mid price %s", midPrice.String()) log.Infof("mid price %s", midPrice.String())
var balances = s.tradingSession.GetAccount().Balances() var balances = s.tradingSession.GetAccount().Balances()
var quantity = s.tradingMarket.MinQuantity
baseBalance, ok := balances[s.tradingMarket.BaseCurrency]
if !ok {
log.Errorf("base balance %s not found", s.tradingMarket.BaseCurrency)
return
}
quoteBalance, ok := balances[s.tradingMarket.QuoteCurrency]
if !ok {
log.Errorf("quote balance %s not found", s.tradingMarket.QuoteCurrency)
return
}
minQuantity := s.tradingMarket.AdjustQuantityByMinNotional(s.tradingMarket.MinQuantity, price)
if baseBalance.Available.Compare(minQuantity) < 0 {
log.Infof("base balance: %s is not enough, skip", baseBalance.Available.String())
return
}
if quoteBalance.Available.Div(price).Compare(minQuantity) < 0 {
log.Infof("quote balance: %s is not enough, skip", quoteBalance.Available.String())
return
}
maxQuantity := fixedpoint.Min(baseBalance.Available, quoteBalance.Available.Div(price))
quantity := minQuantity
if s.Quantity.Sign() > 0 { if s.Quantity.Sign() > 0 {
quantity = fixedpoint.Min(s.Quantity, s.tradingMarket.MinQuantity) quantity = fixedpoint.Max(s.Quantity, quantity)
} else if s.SimulateVolume { } else if s.SimulateVolume {
s.mu.Lock() s.mu.Lock()
if s.lastTradingKLine.Volume.Sign() > 0 && s.lastSourceKLine.Volume.Sign() > 0 { if s.lastTradingKLine.Volume.Sign() > 0 && s.lastSourceKLine.Volume.Sign() > 0 {
@ -337,15 +363,6 @@ func (s *Strategy) placeOrders(ctx context.Context) {
if volumeDiff.Sign() > 0 { if volumeDiff.Sign() > 0 {
quantity = volumeDiff quantity = volumeDiff
} }
if baseBalance, ok := balances[s.tradingMarket.BaseCurrency]; ok {
quantity = fixedpoint.Min(quantity, baseBalance.Available)
}
if quoteBalance, ok := balances[s.tradingMarket.QuoteCurrency]; ok {
maxQuantity := quoteBalance.Available.Div(price)
quantity = fixedpoint.Min(quantity, maxQuantity)
}
} }
s.mu.Unlock() s.mu.Unlock()
} else { } else {
@ -354,33 +371,37 @@ func (s *Strategy) placeOrders(ctx context.Context) {
quantity = quantity.Mul(fixedpoint.NewFromFloat(jitter)) quantity = quantity.Mul(fixedpoint.NewFromFloat(jitter))
} }
var quoteAmount = price.Mul(quantity) quantity = fixedpoint.Min(quantity, maxQuantity)
if quoteAmount.Compare(s.tradingMarket.MinNotional) <= 0 {
quantity = fixedpoint.Max(
s.tradingMarket.MinQuantity,
s.tradingMarket.MinNotional.Mul(NotionModifier).Div(price))
}
orderForms := []types.SubmitOrder{{ orderForms := []types.SubmitOrder{
{
Symbol: s.Symbol, Symbol: s.Symbol,
Side: types.SideTypeBuy, Side: types.SideTypeBuy,
Type: types.OrderTypeLimit, Type: types.OrderTypeLimit,
Quantity: quantity, Quantity: quantity,
Price: price, Price: price,
Market: s.tradingMarket, Market: s.tradingMarket,
}, { },
{
Symbol: s.Symbol, Symbol: s.Symbol,
Side: types.SideTypeSell, Side: types.SideTypeSell,
Type: types.OrderTypeLimit, Type: types.OrderTypeLimit,
Quantity: quantity, Quantity: quantity,
Price: price, Price: price,
Market: s.tradingMarket, Market: s.tradingMarket,
}} },
createdOrders, err := s.OrderExecutor.SubmitOrders(ctx, orderForms...) }
log.Infof("order forms: %+v", orderForms)
if s.DryRun {
log.Infof("dry run, skip")
return
}
_, err := s.OrderExecutor.SubmitOrders(ctx, orderForms...)
if err != nil { if err != nil {
log.WithError(err).Error("order submit error") log.WithError(err).Error("order submit error")
} }
log.Infof("created orders: %+v", createdOrders)
time.Sleep(time.Second) time.Sleep(time.Second)
} }