xfunding: move position state to state struct

This commit is contained in:
c9s 2023-03-24 00:36:28 +08:00
parent 108bb5deeb
commit c1fbbbe400
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
3 changed files with 58 additions and 44 deletions

View File

@ -1,25 +0,0 @@
// Code generated by "stringer -type=PositionAction"; DO NOT EDIT.
package xfunding
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[PositionNoOp-0]
_ = x[PositionOpening-1]
_ = x[PositionClosing-2]
}
const _PositionAction_name = "PositionNoOpPositionOpeningPositionClosing"
var _PositionAction_index = [...]uint8{0, 12, 27, 42}
func (i PositionAction) String() string {
if i < 0 || i >= PositionAction(len(_PositionAction_index)-1) {
return "PositionAction(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _PositionAction_name[_PositionAction_index[i]:_PositionAction_index[i+1]]
}

View File

@ -0,0 +1,25 @@
// Code generated by "stringer -type=PositionState"; DO NOT EDIT.
package xfunding
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[PositionNoOp-0]
_ = x[PositionOpening-1]
_ = x[PositionClosing-2]
}
const _PositionState_name = "PositionNoOpPositionOpeningPositionClosing"
var _PositionState_index = [...]uint8{0, 12, 27, 42}
func (i PositionState) String() string {
if i < 0 || i >= PositionState(len(_PositionState_index)-1) {
return "PositionState(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _PositionState_name[_PositionState_index[i]:_PositionState_index[i+1]]
}

View File

@ -20,11 +20,15 @@ import (
const ID = "xfunding" const ID = "xfunding"
//go:generate stringer -type=PositionAction // Position State Transitions:
type PositionAction int // NoOp -> Opening | Closing
// Opening -> NoOp -> Closing
// Closing -> NoOp -> Opening
//go:generate stringer -type=PositionState
type PositionState int
const ( const (
PositionNoOp PositionAction = iota PositionNoOp PositionState = iota
PositionOpening PositionOpening
PositionClosing PositionClosing
) )
@ -104,16 +108,17 @@ type Strategy struct {
spotOrderExecutor, futuresOrderExecutor *bbgo.GeneralOrderExecutor spotOrderExecutor, futuresOrderExecutor *bbgo.GeneralOrderExecutor
spotMarket, futuresMarket types.Market spotMarket, futuresMarket types.Market
// positionAction is default to NoOp
positionAction PositionAction
// positionType is the futures position type // positionType is the futures position type
// currently we only support short position for the positive funding rate // currently we only support short position for the positive funding rate
positionType types.PositionType positionType types.PositionType
} }
type State struct { type State struct {
PositionStartTime time.Time `json:"positionStartTime"` PositionStartTime time.Time `json:"positionStartTime"`
// PositionState is default to NoOp
PositionState PositionState
PendingBaseTransfer fixedpoint.Value `json:"pendingBaseTransfer"` PendingBaseTransfer fixedpoint.Value `json:"pendingBaseTransfer"`
TotalBaseTransfer fixedpoint.Value `json:"totalBaseTransfer"` TotalBaseTransfer fixedpoint.Value `json:"totalBaseTransfer"`
UsedQuoteInvestment fixedpoint.Value `json:"usedQuoteInvestment"` UsedQuoteInvestment fixedpoint.Value `json:"usedQuoteInvestment"`
@ -252,6 +257,7 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
if s.State == nil { if s.State == nil {
s.State = &State{ s.State = &State{
PositionState: PositionNoOp,
PendingBaseTransfer: fixedpoint.Zero, PendingBaseTransfer: fixedpoint.Zero,
TotalBaseTransfer: fixedpoint.Zero, TotalBaseTransfer: fixedpoint.Zero,
UsedQuoteInvestment: fixedpoint.Zero, UsedQuoteInvestment: fixedpoint.Zero,
@ -277,7 +283,7 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
return return
} }
switch s.positionAction { switch s.State.PositionState {
case PositionOpening: case PositionOpening:
if trade.Side != types.SideTypeBuy { if trade.Side != types.SideTypeBuy {
log.Errorf("unexpected trade side: %+v, expecting BUY trade", trade) log.Errorf("unexpected trade side: %+v, expecting BUY trade", trade)
@ -289,7 +295,7 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
s.State.UsedQuoteInvestment = s.State.UsedQuoteInvestment.Add(trade.QuoteQuantity) s.State.UsedQuoteInvestment = s.State.UsedQuoteInvestment.Add(trade.QuoteQuantity)
if s.State.UsedQuoteInvestment.Compare(s.QuoteInvestment) >= 0 { if s.State.UsedQuoteInvestment.Compare(s.QuoteInvestment) >= 0 {
s.positionAction = PositionNoOp s.State.PositionState = PositionNoOp
} }
// if we have trade, try to query the balance and transfer the balance to the futures wallet account // if we have trade, try to query the balance and transfer the balance to the futures wallet account
@ -316,7 +322,7 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
return return
} }
switch s.positionAction { switch s.State.PositionState {
case PositionClosing: case PositionClosing:
if err := backoff.RetryGeneral(ctx, func() error { if err := backoff.RetryGeneral(ctx, func() error {
return s.transferOut(ctx, binanceSpot, s.spotMarket.BaseCurrency, trade) return s.transferOut(ctx, binanceSpot, s.spotMarket.BaseCurrency, trade)
@ -367,13 +373,13 @@ func (s *Strategy) queryAndDetectPremiumIndex(ctx context.Context, binanceFuture
log.Infof("premiumIndex: %+v", premiumIndex) log.Infof("premiumIndex: %+v", premiumIndex)
if changed := s.detectPremiumIndex(premiumIndex); changed { if changed := s.detectPremiumIndex(premiumIndex); changed {
log.Infof("position action: %s %s", s.positionType, s.positionAction.String()) log.Infof("position action: %s %s", s.positionType, s.State.PositionState.String())
s.triggerPositionAction(ctx) s.triggerPositionAction(ctx)
} }
} }
func (s *Strategy) triggerPositionAction(ctx context.Context) { func (s *Strategy) triggerPositionAction(ctx context.Context) {
switch s.positionAction { switch s.State.PositionState {
case PositionOpening: case PositionOpening:
s.increaseSpotPosition(ctx) s.increaseSpotPosition(ctx)
s.syncFuturesPosition(ctx) s.syncFuturesPosition(ctx)
@ -384,7 +390,7 @@ func (s *Strategy) triggerPositionAction(ctx context.Context) {
} }
func (s *Strategy) reduceFuturesPosition(ctx context.Context) { func (s *Strategy) reduceFuturesPosition(ctx context.Context) {
switch s.positionAction { switch s.State.PositionState {
case PositionOpening, PositionNoOp: case PositionOpening, PositionNoOp:
return return
} }
@ -443,7 +449,7 @@ func (s *Strategy) syncFuturesPosition(ctx context.Context) {
return return
} }
switch s.positionAction { switch s.State.PositionState {
case PositionClosing: case PositionClosing:
return return
case PositionOpening, PositionNoOp: case PositionOpening, PositionNoOp:
@ -532,7 +538,8 @@ func (s *Strategy) increaseSpotPosition(ctx context.Context) {
log.Errorf("funding long position type is not supported") log.Errorf("funding long position type is not supported")
return return
} }
if s.positionAction != PositionOpening {
if s.State.PositionState != PositionOpening {
return return
} }
@ -540,6 +547,8 @@ func (s *Strategy) increaseSpotPosition(ctx context.Context) {
defer s.mu.Unlock() defer s.mu.Unlock()
if s.State.UsedQuoteInvestment.Compare(s.QuoteInvestment) >= 0 { if s.State.UsedQuoteInvestment.Compare(s.QuoteInvestment) >= 0 {
// stop increase the position
s.State.PositionState = PositionNoOp
return return
} }
@ -586,6 +595,10 @@ func (s *Strategy) increaseSpotPosition(ctx context.Context) {
} }
func (s *Strategy) detectPremiumIndex(premiumIndex *types.PremiumIndex) (changed bool) { func (s *Strategy) detectPremiumIndex(premiumIndex *types.PremiumIndex) (changed bool) {
if s.State.PositionState != PositionNoOp {
return changed
}
fundingRate := premiumIndex.LastFundingRate fundingRate := premiumIndex.LastFundingRate
log.Infof("last %s funding rate: %s", s.Symbol, fundingRate.Percentage()) log.Infof("last %s funding rate: %s", s.Symbol, fundingRate.Percentage())
@ -620,11 +633,12 @@ func (s *Strategy) detectPremiumIndex(premiumIndex *types.PremiumIndex) (changed
} }
func (s *Strategy) startOpeningPosition(pt types.PositionType, t time.Time) { func (s *Strategy) startOpeningPosition(pt types.PositionType, t time.Time) {
if s.positionAction == PositionOpening { // we should only open a new position when there is no op on the position
if s.State.PositionState != PositionNoOp {
return return
} }
s.positionAction = PositionOpening s.State.PositionState = PositionOpening
s.positionType = pt s.positionType = pt
// reset the transfer stats // reset the transfer stats
@ -634,11 +648,11 @@ func (s *Strategy) startOpeningPosition(pt types.PositionType, t time.Time) {
} }
func (s *Strategy) startClosingPosition() { func (s *Strategy) startClosingPosition() {
if s.positionAction == PositionClosing { if s.State.PositionState != PositionNoOp {
return return
} }
s.positionAction = PositionClosing s.State.PositionState = PositionClosing
// reset the transfer stats // reset the transfer stats
s.State.PendingBaseTransfer = fixedpoint.Zero s.State.PendingBaseTransfer = fixedpoint.Zero