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"
//go:generate stringer -type=PositionAction
type PositionAction int
// Position State Transitions:
// NoOp -> Opening | Closing
// Opening -> NoOp -> Closing
// Closing -> NoOp -> Opening
//go:generate stringer -type=PositionState
type PositionState int
const (
PositionNoOp PositionAction = iota
PositionNoOp PositionState = iota
PositionOpening
PositionClosing
)
@ -104,16 +108,17 @@ type Strategy struct {
spotOrderExecutor, futuresOrderExecutor *bbgo.GeneralOrderExecutor
spotMarket, futuresMarket types.Market
// positionAction is default to NoOp
positionAction PositionAction
// positionType is the futures position type
// currently we only support short position for the positive funding rate
positionType types.PositionType
}
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"`
TotalBaseTransfer fixedpoint.Value `json:"totalBaseTransfer"`
UsedQuoteInvestment fixedpoint.Value `json:"usedQuoteInvestment"`
@ -252,6 +257,7 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
if s.State == nil {
s.State = &State{
PositionState: PositionNoOp,
PendingBaseTransfer: fixedpoint.Zero,
TotalBaseTransfer: fixedpoint.Zero,
UsedQuoteInvestment: fixedpoint.Zero,
@ -277,7 +283,7 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
return
}
switch s.positionAction {
switch s.State.PositionState {
case PositionOpening:
if trade.Side != types.SideTypeBuy {
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)
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
@ -316,7 +322,7 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
return
}
switch s.positionAction {
switch s.State.PositionState {
case PositionClosing:
if err := backoff.RetryGeneral(ctx, func() error {
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)
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)
}
}
func (s *Strategy) triggerPositionAction(ctx context.Context) {
switch s.positionAction {
switch s.State.PositionState {
case PositionOpening:
s.increaseSpotPosition(ctx)
s.syncFuturesPosition(ctx)
@ -384,7 +390,7 @@ func (s *Strategy) triggerPositionAction(ctx context.Context) {
}
func (s *Strategy) reduceFuturesPosition(ctx context.Context) {
switch s.positionAction {
switch s.State.PositionState {
case PositionOpening, PositionNoOp:
return
}
@ -443,7 +449,7 @@ func (s *Strategy) syncFuturesPosition(ctx context.Context) {
return
}
switch s.positionAction {
switch s.State.PositionState {
case PositionClosing:
return
case PositionOpening, PositionNoOp:
@ -532,7 +538,8 @@ func (s *Strategy) increaseSpotPosition(ctx context.Context) {
log.Errorf("funding long position type is not supported")
return
}
if s.positionAction != PositionOpening {
if s.State.PositionState != PositionOpening {
return
}
@ -540,6 +547,8 @@ func (s *Strategy) increaseSpotPosition(ctx context.Context) {
defer s.mu.Unlock()
if s.State.UsedQuoteInvestment.Compare(s.QuoteInvestment) >= 0 {
// stop increase the position
s.State.PositionState = PositionNoOp
return
}
@ -586,6 +595,10 @@ func (s *Strategy) increaseSpotPosition(ctx context.Context) {
}
func (s *Strategy) detectPremiumIndex(premiumIndex *types.PremiumIndex) (changed bool) {
if s.State.PositionState != PositionNoOp {
return changed
}
fundingRate := premiumIndex.LastFundingRate
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) {
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
}
s.positionAction = PositionOpening
s.State.PositionState = PositionOpening
s.positionType = pt
// reset the transfer stats
@ -634,11 +648,11 @@ func (s *Strategy) startOpeningPosition(pt types.PositionType, t time.Time) {
}
func (s *Strategy) startClosingPosition() {
if s.positionAction == PositionClosing {
if s.State.PositionState != PositionNoOp {
return
}
s.positionAction = PositionClosing
s.State.PositionState = PositionClosing
// reset the transfer stats
s.State.PendingBaseTransfer = fixedpoint.Zero