xfunding: refactor state functions and fix transfer out

This commit is contained in:
c9s 2023-03-24 02:52:13 +08:00
parent f3049de2ba
commit 84313dbdf9
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
2 changed files with 71 additions and 49 deletions

View File

@ -291,12 +291,8 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
}
s.mu.Lock()
defer s.mu.Unlock()
s.State.UsedQuoteInvestment = s.State.UsedQuoteInvestment.Add(trade.QuoteQuantity)
if s.State.UsedQuoteInvestment.Compare(s.QuoteInvestment) >= 0 {
s.State.PositionState = PositionClosed
}
s.mu.Unlock()
// if we have trade, try to query the balance and transfer the balance to the futures wallet account
// TODO: handle missing trades here. If the process crashed during the transfer, how to recover?
@ -318,11 +314,14 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
s.futuresOrderExecutor = s.allocateOrderExecutor(ctx, s.futuresSession, instanceID, s.FuturesPosition)
s.futuresOrderExecutor.TradeCollector().OnTrade(func(trade types.Trade, profit fixedpoint.Value, netProfit fixedpoint.Value) {
log.Infof("futures trade: %v", trade)
if s.positionType != types.PositionShort {
return
}
switch s.State.PositionState {
switch s.getPositionState() {
case PositionClosing:
if err := backoff.RetryGeneral(ctx, func() error {
return s.transferOut(ctx, binanceSpot, s.spotMarket.BaseCurrency, trade)
@ -378,7 +377,7 @@ func (s *Strategy) queryAndDetectPremiumIndex(ctx context.Context, binanceFuture
}
func (s *Strategy) sync(ctx context.Context) {
switch s.State.PositionState {
switch s.getPositionState() {
case PositionOpening:
s.increaseSpotPosition(ctx)
s.syncFuturesPosition(ctx)
@ -389,8 +388,7 @@ func (s *Strategy) sync(ctx context.Context) {
}
func (s *Strategy) reduceFuturesPosition(ctx context.Context) {
switch s.State.PositionState {
case PositionOpening, PositionClosed:
if s.notPositionState(PositionClosing) {
return
}
@ -411,8 +409,7 @@ func (s *Strategy) reduceFuturesPosition(ctx context.Context) {
}
if futuresBase.Compare(fixedpoint.Zero) < 0 {
orderPrice := ticker.Sell
orderPrice := ticker.Buy
orderQuantity := futuresBase.Abs()
orderQuantity = fixedpoint.Max(orderQuantity, s.futuresMarket.MinQuantity)
orderQuantity = s.futuresMarket.AdjustQuantityByMinNotional(orderQuantity, orderPrice)
@ -448,7 +445,7 @@ func (s *Strategy) syncFuturesPosition(ctx context.Context) {
return
}
if s.State.PositionState != PositionOpening {
if s.notPositionState(PositionOpening) {
return
}
@ -539,14 +536,15 @@ func (s *Strategy) increaseSpotPosition(ctx context.Context) {
s.mu.Lock()
defer s.mu.Unlock()
if s.State.PositionState != PositionOpening {
if s.notPositionState(PositionOpening) {
return
}
if s.State.UsedQuoteInvestment.Compare(s.QuoteInvestment) >= 0 {
// stop increase the position
s.State.PositionState = PositionReady
s.setPositionState(PositionReady)
// DEBUG CODE - triggering closing position automatically
s.startClosingPosition()
return
}
@ -593,20 +591,16 @@ func (s *Strategy) increaseSpotPosition(ctx context.Context) {
log.Infof("created orders: %+v", createdOrders)
}
func (s *Strategy) detectPremiumIndex(premiumIndex *types.PremiumIndex) (changed bool) {
if s.State.PositionState != PositionClosed {
return changed
}
func (s *Strategy) detectPremiumIndex(premiumIndex *types.PremiumIndex) bool {
fundingRate := premiumIndex.LastFundingRate
log.Infof("last %s funding rate: %s", s.Symbol, fundingRate.Percentage())
if s.ShortFundingRate == nil {
return changed
return false
}
switch s.State.PositionState {
switch s.getPositionState() {
case PositionClosed:
if fundingRate.Compare(s.ShortFundingRate.High) >= 0 {
@ -614,7 +608,7 @@ func (s *Strategy) detectPremiumIndex(premiumIndex *types.PremiumIndex) (changed
fundingRate.Percentage(), s.ShortFundingRate.High.Percentage())
s.startOpeningPosition(types.PositionShort, premiumIndex.Time)
changed = true
return true
}
case PositionReady:
@ -625,25 +619,26 @@ func (s *Strategy) detectPremiumIndex(premiumIndex *types.PremiumIndex) (changed
holdingPeriod := premiumIndex.Time.Sub(s.State.PositionStartTime)
if holdingPeriod < time.Duration(s.MinHoldingPeriod) {
log.Warnf("position holding period %s is less than %s, skip closing", holdingPeriod, s.MinHoldingPeriod)
return
return false
}
s.startClosingPosition()
changed = true
return true
}
}
return changed
return false
}
func (s *Strategy) startOpeningPosition(pt types.PositionType, t time.Time) {
// only open a new position when there is no position
if s.State.PositionState != PositionClosed {
if s.notPositionState(PositionClosed) {
return
}
log.Infof("startOpeningPosition")
s.State.PositionState = PositionOpening
s.setPositionState(PositionOpening)
s.positionType = pt
// reset the transfer stats
@ -654,16 +649,33 @@ func (s *Strategy) startOpeningPosition(pt types.PositionType, t time.Time) {
func (s *Strategy) startClosingPosition() {
// we can't close a position that is not ready
if s.State.PositionState != PositionReady {
if s.notPositionState(PositionReady) {
return
}
log.Infof("startClosingPosition")
s.State.PositionState = PositionClosing
s.setPositionState(PositionClosing)
// reset the transfer stats
s.State.PendingBaseTransfer = fixedpoint.Zero
s.State.TotalBaseTransfer = fixedpoint.Zero
}
func (s *Strategy) setPositionState(state PositionState) {
origState := s.State.PositionState
s.State.PositionState = state
log.Infof("position state transition: %s -> %s", origState.String(), state.String())
}
func (s *Strategy) isPositionState(state PositionState) bool {
return s.State.PositionState == state
}
func (s *Strategy) getPositionState() PositionState {
return s.State.PositionState
}
func (s *Strategy) notPositionState(state PositionState) bool {
return s.State.PositionState != state
}
func (s *Strategy) allocateOrderExecutor(ctx context.Context, session *bbgo.ExchangeSession, instanceID string, position *types.Position) *bbgo.GeneralOrderExecutor {

View File

@ -15,44 +15,54 @@ type FuturesTransfer interface {
func (s *Strategy) transferOut(ctx context.Context, ex FuturesTransfer, currency string, trade types.Trade) error {
// base asset needs BUY trades
if trade.Side == types.SideTypeBuy {
if trade.Side != types.SideTypeBuy {
return nil
}
// if transfer done
if s.State.TotalBaseTransfer.IsZero() {
return nil
}
// de-leverage and get the collateral base quantity for transfer
quantity := trade.Quantity
quantity = quantity.Div(s.Leverage)
balances, err := s.futuresSession.Exchange.QueryAccountBalances(ctx)
if err != nil {
log.Infof("adding to pending base transfer: %s %s + %s", quantity.String(), currency, s.State.PendingBaseTransfer.String())
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
return err
}
b, ok := balances[currency]
if !ok {
log.Infof("adding to pending base transfer: %s %s + %s", quantity.String(), currency, s.State.PendingBaseTransfer.String())
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
return fmt.Errorf("%s balance not found", currency)
}
quantity := trade.Quantity
// add the previous pending base transfer and the current trade quantity
amount := s.State.PendingBaseTransfer.Add(quantity)
if s.Leverage.Compare(fixedpoint.One) > 0 {
// de-leverage and get the collateral base quantity for transfer
quantity = quantity.Div(s.Leverage)
}
// try to transfer more if we enough balance
amount = fixedpoint.Min(amount, b.Available)
// we can only transfer the rest quota (total base transfer)
amount = fixedpoint.Min(s.State.TotalBaseTransfer, amount)
// 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.IsZero() || b.Available.Compare(quantity) < 0 {
log.Infof("adding to pending base transfer: %s %s", quantity, currency)
if amount.IsZero() {
log.Infof("adding to pending base transfer: %s %s + %s ", quantity.String(), currency, s.State.PendingBaseTransfer.String())
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
return nil
}
amount := s.State.PendingBaseTransfer.Add(quantity)
// de-leverage and get the collateral base quantity
collateralBase := s.FuturesPosition.GetBase().Abs().Div(s.Leverage)
_ = collateralBase
pos := s.FuturesPosition.GetBase().Abs().Div(s.Leverage)
rest := pos.Sub(s.State.TotalBaseTransfer)
if rest.Sign() < 0 {
return nil
}
amount = fixedpoint.Min(rest, amount)
// if s.State.TotalBaseTransfer.Compare(collateralBase)
log.Infof("transfering out futures account asset %s %s", amount, currency)
if err := ex.TransferFuturesAccountAsset(ctx, currency, amount, types.TransferOut); err != nil {
@ -62,8 +72,8 @@ func (s *Strategy) transferOut(ctx context.Context, ex FuturesTransfer, currency
// reset pending transfer
s.State.PendingBaseTransfer = fixedpoint.Zero
// record the transfer in the total base transfer
s.State.TotalBaseTransfer = s.State.TotalBaseTransfer.Add(amount)
// reduce the transfer in the total base transfer
s.State.TotalBaseTransfer = s.State.TotalBaseTransfer.Sub(amount)
return nil
}