mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
Merge pull request #1222 from c9s/c9s/fix-xfunding
This commit is contained in:
commit
63d6c88594
|
@ -347,6 +347,7 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
|
||||||
bbgo.Notify("Spot Position", s.SpotPosition)
|
bbgo.Notify("Spot Position", s.SpotPosition)
|
||||||
bbgo.Notify("Futures Position", s.FuturesPosition)
|
bbgo.Notify("Futures Position", s.FuturesPosition)
|
||||||
bbgo.Notify("Neutral Position", s.NeutralPosition)
|
bbgo.Notify("Neutral Position", s.NeutralPosition)
|
||||||
|
bbgo.Notify("State", s.State.PositionState)
|
||||||
|
|
||||||
// sync funding fee txns
|
// sync funding fee txns
|
||||||
if !s.ProfitStats.LastFundingFeeTime.IsZero() {
|
if !s.ProfitStats.LastFundingFeeTime.IsZero() {
|
||||||
|
@ -356,6 +357,20 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
|
||||||
// TEST CODE:
|
// TEST CODE:
|
||||||
// s.syncFundingFeeRecords(ctx, time.Now().Add(-3*24*time.Hour))
|
// s.syncFundingFeeRecords(ctx, time.Now().Add(-3*24*time.Hour))
|
||||||
|
|
||||||
|
switch s.State.PositionState {
|
||||||
|
case PositionOpening:
|
||||||
|
// transfer all base assets from the spot account into the spot account
|
||||||
|
if err := s.transferIn(ctx, s.binanceSpot, s.spotMarket.BaseCurrency, fixedpoint.Zero); err != nil {
|
||||||
|
log.WithError(err).Errorf("futures asset transfer in error")
|
||||||
|
}
|
||||||
|
|
||||||
|
case PositionClosing, PositionClosed:
|
||||||
|
// transfer all base assets from the futures account back to the spot account
|
||||||
|
if err := s.transferOut(ctx, s.binanceSpot, s.spotMarket.BaseCurrency, fixedpoint.Zero); err != nil {
|
||||||
|
log.WithError(err).Errorf("futures asset transfer out error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.spotOrderExecutor = s.allocateOrderExecutor(ctx, s.spotSession, instanceID, s.SpotPosition)
|
s.spotOrderExecutor = s.allocateOrderExecutor(ctx, s.spotSession, instanceID, s.SpotPosition)
|
||||||
s.spotOrderExecutor.TradeCollector().OnTrade(func(trade types.Trade, profit fixedpoint.Value, netProfit fixedpoint.Value) {
|
s.spotOrderExecutor.TradeCollector().OnTrade(func(trade types.Trade, profit fixedpoint.Value, netProfit fixedpoint.Value) {
|
||||||
// we act differently on the spot account
|
// we act differently on the spot account
|
||||||
|
@ -405,8 +420,11 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
|
||||||
|
|
||||||
switch s.getPositionState() {
|
switch s.getPositionState() {
|
||||||
case PositionClosing:
|
case PositionClosing:
|
||||||
|
// de-leverage and get the collateral base quantity for transfer
|
||||||
|
quantity := trade.Quantity.Div(s.Leverage)
|
||||||
|
|
||||||
if err := backoff.RetryGeneral(ctx, func() error {
|
if err := backoff.RetryGeneral(ctx, func() error {
|
||||||
return s.transferOut(ctx, s.binanceSpot, s.spotMarket.BaseCurrency, trade.Quantity)
|
return s.transferOut(ctx, s.binanceSpot, s.spotMarket.BaseCurrency, quantity)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.WithError(err).Errorf("spot-to-futures transfer in retry failed")
|
log.WithError(err).Errorf("spot-to-futures transfer in retry failed")
|
||||||
return
|
return
|
||||||
|
@ -635,13 +653,39 @@ func (s *Strategy) reduceFuturesPosition(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spotBase := s.SpotPosition.GetBase()
|
||||||
|
if !s.spotMarket.IsDustQuantity(spotBase, s.SpotPosition.AverageCost) {
|
||||||
|
if balance, ok := s.futuresSession.Account.Balance(s.futuresMarket.BaseCurrency); ok && balance.Available.Sign() > 0 {
|
||||||
|
if err := backoff.RetryGeneral(ctx, func() error {
|
||||||
|
return s.transferOut(ctx, s.binanceSpot, s.spotMarket.BaseCurrency, balance.Available)
|
||||||
|
}); err != nil {
|
||||||
|
log.WithError(err).Errorf("spot-to-futures transfer in retry failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if futuresBase.Compare(fixedpoint.Zero) < 0 {
|
if futuresBase.Compare(fixedpoint.Zero) < 0 {
|
||||||
orderPrice := ticker.Buy
|
orderPrice := ticker.Buy
|
||||||
orderQuantity := futuresBase.Abs()
|
orderQuantity := futuresBase.Abs()
|
||||||
orderQuantity = fixedpoint.Max(orderQuantity, s.minQuantity)
|
orderQuantity = fixedpoint.Max(orderQuantity, s.minQuantity)
|
||||||
orderQuantity = s.futuresMarket.AdjustQuantityByMinNotional(orderQuantity, orderPrice)
|
orderQuantity = s.futuresMarket.AdjustQuantityByMinNotional(orderQuantity, orderPrice)
|
||||||
|
|
||||||
if s.futuresMarket.IsDustQuantity(orderQuantity, orderPrice) {
|
if s.futuresMarket.IsDustQuantity(orderQuantity, orderPrice) {
|
||||||
log.Infof("skip futures order with dust quantity %s, market = %+v", orderQuantity.String(), s.futuresMarket)
|
submitOrder := types.SubmitOrder{
|
||||||
|
Symbol: s.Symbol,
|
||||||
|
Side: types.SideTypeBuy,
|
||||||
|
Type: types.OrderTypeLimitMaker,
|
||||||
|
Price: orderPrice,
|
||||||
|
Market: s.futuresMarket,
|
||||||
|
|
||||||
|
// quantity: Cannot be sent with closePosition=true(Close-All)
|
||||||
|
// reduceOnly: Cannot be sent with closePosition=true
|
||||||
|
ClosePosition: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := s.futuresOrderExecutor.SubmitOrders(ctx, submitOrder); err != nil {
|
||||||
|
log.WithError(err).Errorf("can not submit futures order with close position: %+v", submitOrder)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -685,7 +729,7 @@ func (s *Strategy) syncFuturesPosition(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("position comparision: %s (spot) <=> %s (futures)", spotBase.String(), futuresBase.String())
|
log.Infof("syncFuturesPosition: position comparision: %s (spot) <=> %s (futures)", spotBase.String(), futuresBase.String())
|
||||||
|
|
||||||
if futuresBase.Sign() > 0 {
|
if futuresBase.Sign() > 0 {
|
||||||
// unexpected error
|
// unexpected error
|
||||||
|
@ -722,7 +766,8 @@ func (s *Strategy) syncFuturesPosition(ctx context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if - futures position < max futures position, increase it
|
// if - futures position < max futures position, increase it
|
||||||
if futuresBase.Neg().Compare(maxFuturesBasePosition) >= 0 {
|
// posDiff := futuresBase.Abs().Sub(maxFuturesBasePosition)
|
||||||
|
if futuresBase.Abs().Compare(maxFuturesBasePosition) >= 0 {
|
||||||
s.setPositionState(PositionReady)
|
s.setPositionState(PositionReady)
|
||||||
|
|
||||||
bbgo.Notify("Position Ready")
|
bbgo.Notify("Position Ready")
|
||||||
|
@ -745,12 +790,16 @@ func (s *Strategy) syncFuturesPosition(ctx context.Context) {
|
||||||
|
|
||||||
log.Infof("position diff quantity: %s", diffQuantity.String())
|
log.Infof("position diff quantity: %s", diffQuantity.String())
|
||||||
|
|
||||||
orderQuantity := fixedpoint.Max(diffQuantity, s.minQuantity)
|
orderQuantity := diffQuantity
|
||||||
|
orderQuantity = fixedpoint.Max(diffQuantity, s.minQuantity)
|
||||||
orderQuantity = s.futuresMarket.AdjustQuantityByMinNotional(orderQuantity, orderPrice)
|
orderQuantity = s.futuresMarket.AdjustQuantityByMinNotional(orderQuantity, orderPrice)
|
||||||
if s.futuresMarket.IsDustQuantity(orderQuantity, orderPrice) {
|
|
||||||
log.Warnf("unexpected dust quantity, skip futures order with dust quantity %s, market = %+v", orderQuantity.String(), s.futuresMarket)
|
/*
|
||||||
return
|
if s.futuresMarket.IsDustQuantity(orderQuantity, orderPrice) {
|
||||||
}
|
log.Warnf("unexpected dust quantity, skip futures order with dust quantity %s, market = %+v", orderQuantity.String(), s.futuresMarket)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
submitOrder := types.SubmitOrder{
|
submitOrder := types.SubmitOrder{
|
||||||
Symbol: s.Symbol,
|
Symbol: s.Symbol,
|
||||||
|
@ -792,7 +841,7 @@ func (s *Strategy) syncSpotPosition(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("spot/futures positions: %s (spot) <=> %s (futures)", spotBase.String(), futuresBase.String())
|
log.Infof("syncSpotPosition: spot/futures positions: %s (spot) <=> %s (futures)", spotBase.String(), futuresBase.String())
|
||||||
|
|
||||||
if futuresBase.Sign() > 0 {
|
if futuresBase.Sign() > 0 {
|
||||||
// unexpected error
|
// unexpected error
|
||||||
|
@ -800,7 +849,7 @@ func (s *Strategy) syncSpotPosition(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = s.futuresOrderExecutor.GracefulCancel(ctx)
|
_ = s.spotOrderExecutor.GracefulCancel(ctx)
|
||||||
|
|
||||||
ticker, err := s.spotSession.Exchange.QueryTicker(ctx, s.Symbol)
|
ticker, err := s.spotSession.Exchange.QueryTicker(ctx, s.Symbol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -831,27 +880,34 @@ func (s *Strategy) syncSpotPosition(ctx context.Context) {
|
||||||
|
|
||||||
orderPrice := ticker.Sell
|
orderPrice := ticker.Sell
|
||||||
orderQuantity := diffQuantity
|
orderQuantity := diffQuantity
|
||||||
if b, ok := s.spotSession.Account.Balance(s.spotMarket.BaseCurrency); ok {
|
b, ok := s.spotSession.Account.Balance(s.spotMarket.BaseCurrency)
|
||||||
orderQuantity = fixedpoint.Min(b.Available, orderQuantity)
|
if !ok {
|
||||||
}
|
log.Warnf("%s balance not found, can not sync spot position", s.spotMarket.BaseCurrency)
|
||||||
|
|
||||||
// avoid increase the order size
|
|
||||||
if s.spotMarket.IsDustQuantity(orderQuantity, orderPrice) {
|
|
||||||
log.Infof("skip futures order with dust quantity %s, market = %+v", orderQuantity.String(), s.spotMarket)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
createdOrders, err := s.spotOrderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
log.Infof("spot balance: %+v", b)
|
||||||
|
|
||||||
|
orderQuantity = fixedpoint.Min(b.Available, orderQuantity)
|
||||||
|
|
||||||
|
// avoid increase the order size
|
||||||
|
if s.spotMarket.IsDustQuantity(orderQuantity, orderPrice) {
|
||||||
|
log.Infof("skip spot order with dust quantity %s, market=%+v balance=%+v", orderQuantity.String(), s.spotMarket, b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
submitOrder := types.SubmitOrder{
|
||||||
Symbol: s.Symbol,
|
Symbol: s.Symbol,
|
||||||
Side: types.SideTypeSell,
|
Side: types.SideTypeSell,
|
||||||
Type: types.OrderTypeLimitMaker,
|
Type: types.OrderTypeLimitMaker,
|
||||||
Quantity: orderQuantity,
|
Quantity: orderQuantity,
|
||||||
Price: orderPrice,
|
Price: orderPrice,
|
||||||
Market: s.futuresMarket,
|
Market: s.spotMarket,
|
||||||
})
|
}
|
||||||
|
createdOrders, err := s.spotOrderExecutor.SubmitOrders(ctx, submitOrder)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Errorf("can not submit spot order")
|
log.WithError(err).Errorf("can not submit spot order: %+v", submitOrder)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,6 +1148,13 @@ func (s *Strategy) checkAndRestorePositionRisks(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("fetched futures position risks: %+v", positionRisks)
|
||||||
|
|
||||||
|
if len(positionRisks) == 0 {
|
||||||
|
s.FuturesPosition.Reset()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, positionRisk := range positionRisks {
|
for _, positionRisk := range positionRisks {
|
||||||
if positionRisk.Symbol != s.Symbol {
|
if positionRisk.Symbol != s.Symbol {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -41,31 +41,33 @@ func (s *Strategy) resetTransfer(ctx context.Context, ex FuturesTransfer, asset
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) transferOut(ctx context.Context, ex FuturesTransfer, asset string, tradeQuantity fixedpoint.Value) error {
|
func (s *Strategy) transferOut(ctx context.Context, ex FuturesTransfer, asset string, quantity fixedpoint.Value) error {
|
||||||
// if transfer done
|
// if transfer done
|
||||||
if s.State.TotalBaseTransfer.IsZero() {
|
if s.State.TotalBaseTransfer.IsZero() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// de-leverage and get the collateral base quantity for transfer
|
|
||||||
quantity := tradeQuantity.Div(s.Leverage)
|
|
||||||
|
|
||||||
balances, err := s.futuresSession.Exchange.QueryAccountBalances(ctx)
|
balances, err := s.futuresSession.Exchange.QueryAccountBalances(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Infof("adding to pending base transfer: %s %s + %s", quantity.String(), asset, s.State.PendingBaseTransfer.String())
|
log.Infof("balance query error, adding to pending base transfer: %s %s + %s", quantity.String(), asset, s.State.PendingBaseTransfer.String())
|
||||||
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
|
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
b, ok := balances[asset]
|
b, ok := balances[asset]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Infof("adding to pending base transfer: %s %s + %s", quantity.String(), asset, s.State.PendingBaseTransfer.String())
|
log.Infof("balance not found, adding to pending base transfer: %s %s + %s", quantity.String(), asset, s.State.PendingBaseTransfer.String())
|
||||||
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
|
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
|
||||||
return fmt.Errorf("%s balance not found", asset)
|
return fmt.Errorf("%s balance not found", asset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("found futures balance: %+v", b)
|
||||||
|
|
||||||
// add the previous pending base transfer and the current trade quantity
|
// add the previous pending base transfer and the current trade quantity
|
||||||
amount := s.State.PendingBaseTransfer.Add(quantity)
|
amount := b.MaxWithdrawAmount
|
||||||
|
if !quantity.IsZero() {
|
||||||
|
amount = s.State.PendingBaseTransfer.Add(quantity)
|
||||||
|
}
|
||||||
|
|
||||||
// try to transfer more if we enough balance
|
// try to transfer more if we enough balance
|
||||||
amount = fixedpoint.Min(amount, b.MaxWithdrawAmount)
|
amount = fixedpoint.Min(amount, b.MaxWithdrawAmount)
|
||||||
|
@ -75,7 +77,7 @@ func (s *Strategy) transferOut(ctx context.Context, ex FuturesTransfer, asset st
|
||||||
|
|
||||||
// TODO: according to the fee, we might not be able to get enough balance greater than the trade quantity, we can adjust the quantity here
|
// TODO: according to the fee, we might not be able to get enough balance greater than the trade quantity, we can adjust the quantity here
|
||||||
if amount.IsZero() {
|
if amount.IsZero() {
|
||||||
log.Infof("adding to pending base transfer: %s %s + %s ", quantity.String(), asset, s.State.PendingBaseTransfer.String())
|
log.Infof("zero amount, adding to pending base transfer: %s %s + %s ", quantity.String(), asset, s.State.PendingBaseTransfer.String())
|
||||||
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
|
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -111,17 +113,19 @@ func (s *Strategy) transferIn(ctx context.Context, ex FuturesTransfer, asset str
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: according to the fee, we might not be able to get enough balance greater than the trade quantity, we can adjust the quantity here
|
// TODO: according to the fee, we might not be able to get enough balance greater than the trade quantity, we can adjust the quantity here
|
||||||
if b.Available.Compare(quantity) < 0 {
|
if !quantity.IsZero() && b.Available.Compare(quantity) < 0 {
|
||||||
log.Infof("adding to pending base transfer: %s %s", quantity, asset)
|
log.Infof("adding to pending base transfer: %s %s", quantity, asset)
|
||||||
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
|
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
amount := s.State.PendingBaseTransfer.Add(quantity)
|
amount := b.Available
|
||||||
|
if !quantity.IsZero() {
|
||||||
|
amount = s.State.PendingBaseTransfer.Add(quantity)
|
||||||
|
}
|
||||||
|
|
||||||
pos := s.SpotPosition.GetBase().Abs()
|
pos := s.SpotPosition.GetBase().Abs()
|
||||||
rest := pos.Sub(s.State.TotalBaseTransfer)
|
rest := pos.Sub(s.State.TotalBaseTransfer)
|
||||||
|
|
||||||
if rest.Sign() < 0 {
|
if rest.Sign() < 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user