FEATURE: remove Short

This commit is contained in:
chiahung.lin 2023-12-22 15:50:48 +08:00
parent e35795943d
commit 57282c30d2
8 changed files with 30 additions and 62 deletions

View File

@ -15,12 +15,12 @@ type cancelOrdersByGroupIDApi interface {
func (s *Strategy) placeOpenPositionOrders(ctx context.Context) error { func (s *Strategy) placeOpenPositionOrders(ctx context.Context) error {
s.logger.Infof("[DCA] start placing open position orders") s.logger.Infof("[DCA] start placing open position orders")
price, err := getBestPriceUntilSuccess(ctx, s.Session.Exchange, s.Symbol, s.Short) price, err := getBestPriceUntilSuccess(ctx, s.Session.Exchange, s.Symbol)
if err != nil { if err != nil {
return err return err
} }
orders, err := generateOpenPositionOrders(s.Market, s.Short, s.Budget, price, s.PriceDeviation, s.MaxOrderNum, s.OrderGroupID) orders, err := generateOpenPositionOrders(s.Market, s.Budget, price, s.PriceDeviation, s.MaxOrderNum, s.OrderGroupID)
if err != nil { if err != nil {
return err return err
} }
@ -35,24 +35,17 @@ func (s *Strategy) placeOpenPositionOrders(ctx context.Context) error {
return nil return nil
} }
func getBestPriceUntilSuccess(ctx context.Context, ex types.Exchange, symbol string, short bool) (fixedpoint.Value, error) { func getBestPriceUntilSuccess(ctx context.Context, ex types.Exchange, symbol string) (fixedpoint.Value, error) {
ticker, err := retry.QueryTickerUntilSuccessful(ctx, ex, symbol) ticker, err := retry.QueryTickerUntilSuccessful(ctx, ex, symbol)
if err != nil { if err != nil {
return fixedpoint.Zero, err return fixedpoint.Zero, err
} }
if short { return ticker.Sell, nil
return ticker.Buy, nil
} else {
return ticker.Sell, nil
}
} }
func generateOpenPositionOrders(market types.Market, short bool, budget, price, priceDeviation fixedpoint.Value, maxOrderNum int64, orderGroupID uint32) ([]types.SubmitOrder, error) { func generateOpenPositionOrders(market types.Market, budget, price, priceDeviation fixedpoint.Value, maxOrderNum int64, orderGroupID uint32) ([]types.SubmitOrder, error) {
factor := fixedpoint.One.Sub(priceDeviation) factor := fixedpoint.One.Sub(priceDeviation)
if short {
factor = fixedpoint.One.Add(priceDeviation)
}
// calculate all valid prices // calculate all valid prices
var prices []fixedpoint.Value var prices []fixedpoint.Value
@ -68,15 +61,12 @@ func generateOpenPositionOrders(market types.Market, short bool, budget, price,
prices = append(prices, price) prices = append(prices, price)
} }
notional, orderNum := calculateNotionalAndNum(market, short, budget, prices) notional, orderNum := calculateNotionalAndNum(market, budget, prices)
if orderNum == 0 { if orderNum == 0 {
return nil, fmt.Errorf("failed to calculate notional and num of open position orders, price: %s, budget: %s", price, budget) return nil, fmt.Errorf("failed to calculate notional and num of open position orders, price: %s, budget: %s", price, budget)
} }
side := types.SideTypeBuy side := types.SideTypeBuy
if short {
side = types.SideTypeSell
}
var submitOrders []types.SubmitOrder var submitOrders []types.SubmitOrder
for i := 0; i < orderNum; i++ { for i := 0; i < orderNum; i++ {
@ -99,7 +89,7 @@ func generateOpenPositionOrders(market types.Market, short bool, budget, price,
// calculateNotionalAndNum calculates the notional and num of open position orders // calculateNotionalAndNum calculates the notional and num of open position orders
// DCA2 is notional-based, every order has the same notional // DCA2 is notional-based, every order has the same notional
func calculateNotionalAndNum(market types.Market, short bool, budget fixedpoint.Value, prices []fixedpoint.Value) (fixedpoint.Value, int) { func calculateNotionalAndNum(market types.Market, budget fixedpoint.Value, prices []fixedpoint.Value) (fixedpoint.Value, int) {
for num := len(prices); num > 0; num-- { for num := len(prices); num > 0; num-- {
notional := budget.Div(fixedpoint.NewFromInt(int64(num))) notional := budget.Div(fixedpoint.NewFromInt(int64(num)))
if notional.Compare(market.MinNotional) < 0 { if notional.Compare(market.MinNotional) < 0 {
@ -107,9 +97,6 @@ func calculateNotionalAndNum(market types.Market, short bool, budget fixedpoint.
} }
maxPriceIdx := 0 maxPriceIdx := 0
if short {
maxPriceIdx = num - 1
}
quantity := market.TruncateQuantity(notional.Div(prices[maxPriceIdx])) quantity := market.TruncateQuantity(notional.Div(prices[maxPriceIdx]))
if quantity.Compare(market.MinQuantity) < 0 { if quantity.Compare(market.MinQuantity) < 0 {
continue continue

View File

@ -36,7 +36,6 @@ func newTestStrategy(va ...string) *Strategy {
logger: logrus.NewEntry(logrus.New()), logger: logrus.NewEntry(logrus.New()),
Symbol: symbol, Symbol: symbol,
Market: market, Market: market,
Short: false,
TakeProfitRatio: Number("10%"), TakeProfitRatio: Number("10%"),
} }
return s return s
@ -51,7 +50,7 @@ func TestGenerateOpenPositionOrders(t *testing.T) {
budget := Number("10500") budget := Number("10500")
askPrice := Number("30000") askPrice := Number("30000")
margin := Number("0.05") margin := Number("0.05")
submitOrders, err := generateOpenPositionOrders(strategy.Market, false, budget, askPrice, margin, 4, strategy.OrderGroupID) submitOrders, err := generateOpenPositionOrders(strategy.Market, budget, askPrice, margin, 4, strategy.OrderGroupID)
if !assert.NoError(err) { if !assert.NoError(err) {
return return
} }

View File

@ -39,14 +39,14 @@ func (s *Strategy) recover(ctx context.Context) error {
return err return err
} }
currentRound, err := getCurrentRoundOrders(s.Short, openOrders, closedOrders, s.OrderGroupID) currentRound, err := getCurrentRoundOrders(openOrders, closedOrders, s.OrderGroupID)
if err != nil { if err != nil {
return err return err
} }
debugRoundOrders(s.logger, "current", currentRound) debugRoundOrders(s.logger, "current", currentRound)
// recover state // recover state
state, err := recoverState(ctx, s.Symbol, s.Short, int(s.MaxOrderNum), openOrders, currentRound, s.OrderExecutor.ActiveMakerOrders(), s.OrderExecutor.OrderStore(), s.OrderGroupID) state, err := recoverState(ctx, s.Symbol, int(s.MaxOrderNum), openOrders, currentRound, s.OrderExecutor.ActiveMakerOrders(), s.OrderExecutor.OrderStore(), s.OrderGroupID)
if err != nil { if err != nil {
return err return err
} }
@ -72,7 +72,7 @@ func (s *Strategy) recover(ctx context.Context) error {
} }
// recover state // recover state
func recoverState(ctx context.Context, symbol string, short bool, maxOrderNum int, openOrders []types.Order, currentRound Round, activeOrderBook *bbgo.ActiveOrderBook, orderStore *core.OrderStore, groupID uint32) (State, error) { func recoverState(ctx context.Context, symbol string, maxOrderNum int, openOrders []types.Order, currentRound Round, activeOrderBook *bbgo.ActiveOrderBook, orderStore *core.OrderStore, groupID uint32) (State, error) {
if len(currentRound.OpenPositionOrders) == 0 { if len(currentRound.OpenPositionOrders) == 0 {
// new strategy // new strategy
return WaitToOpenPosition, nil return WaitToOpenPosition, nil
@ -225,15 +225,10 @@ type Round struct {
TakeProfitOrder types.Order TakeProfitOrder types.Order
} }
func getCurrentRoundOrders(short bool, openOrders, closedOrders []types.Order, groupID uint32) (Round, error) { func getCurrentRoundOrders(openOrders, closedOrders []types.Order, groupID uint32) (Round, error) {
openPositionSide := types.SideTypeBuy openPositionSide := types.SideTypeBuy
takeProfitSide := types.SideTypeSell takeProfitSide := types.SideTypeSell
if short {
openPositionSide = types.SideTypeSell
takeProfitSide = types.SideTypeBuy
}
var allOrders []types.Order var allOrders []types.Order
allOrders = append(allOrders, openOrders...) allOrders = append(allOrders, openOrders...)
allOrders = append(allOrders, closedOrders...) allOrders = append(allOrders, closedOrders...)

View File

@ -39,7 +39,7 @@ func Test_GetCurrenctAndLastRoundOrders(t *testing.T) {
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-5*time.Second)), generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-5*time.Second)),
} }
currentRound, err := getCurrentRoundOrders(false, openOrders, closedOrders, 0) currentRound, err := getCurrentRoundOrders(openOrders, closedOrders, 0)
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEqual(t, 0, currentRound.TakeProfitOrder.OrderID) assert.NotEqual(t, 0, currentRound.TakeProfitOrder.OrderID)
@ -72,7 +72,7 @@ func Test_GetCurrenctAndLastRoundOrders(t *testing.T) {
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-17*time.Second)), generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-17*time.Second)),
} }
currentRound, err := getCurrentRoundOrders(false, openOrders, closedOrders, 0) currentRound, err := getCurrentRoundOrders(openOrders, closedOrders, 0)
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEqual(t, 0, currentRound.TakeProfitOrder.OrderID) assert.NotEqual(t, 0, currentRound.TakeProfitOrder.OrderID)
@ -101,7 +101,7 @@ func Test_RecoverState(t *testing.T) {
currentRound := Round{} currentRound := Round{}
activeOrderBook := bbgo.NewActiveOrderBook(symbol) activeOrderBook := bbgo.NewActiveOrderBook(symbol)
orderStore := core.NewOrderStore(symbol) orderStore := core.NewOrderStore(symbol)
state, err := recoverState(context.Background(), symbol, false, 5, openOrders, currentRound, activeOrderBook, orderStore, 0) state, err := recoverState(context.Background(), symbol, 5, openOrders, currentRound, activeOrderBook, orderStore, 0)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, WaitToOpenPosition, state) assert.Equal(t, WaitToOpenPosition, state)
}) })
@ -120,7 +120,7 @@ func Test_RecoverState(t *testing.T) {
} }
orderStore := core.NewOrderStore(symbol) orderStore := core.NewOrderStore(symbol)
activeOrderBook := bbgo.NewActiveOrderBook(symbol) activeOrderBook := bbgo.NewActiveOrderBook(symbol)
state, err := recoverState(context.Background(), symbol, false, 5, openOrders, currentRound, activeOrderBook, orderStore, 0) state, err := recoverState(context.Background(), symbol, 5, openOrders, currentRound, activeOrderBook, orderStore, 0)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, OpenPositionReady, state) assert.Equal(t, OpenPositionReady, state)
}) })
@ -144,7 +144,7 @@ func Test_RecoverState(t *testing.T) {
} }
orderStore := core.NewOrderStore(symbol) orderStore := core.NewOrderStore(symbol)
activeOrderBook := bbgo.NewActiveOrderBook(symbol) activeOrderBook := bbgo.NewActiveOrderBook(symbol)
state, err := recoverState(context.Background(), symbol, false, 5, openOrders, currentRound, activeOrderBook, orderStore, 0) state, err := recoverState(context.Background(), symbol, 5, openOrders, currentRound, activeOrderBook, orderStore, 0)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, OpenPositionOrderFilled, state) assert.Equal(t, OpenPositionOrderFilled, state)
}) })
@ -165,7 +165,7 @@ func Test_RecoverState(t *testing.T) {
} }
orderStore := core.NewOrderStore(symbol) orderStore := core.NewOrderStore(symbol)
activeOrderBook := bbgo.NewActiveOrderBook(symbol) activeOrderBook := bbgo.NewActiveOrderBook(symbol)
state, err := recoverState(context.Background(), symbol, false, 5, openOrders, currentRound, activeOrderBook, orderStore, 0) state, err := recoverState(context.Background(), symbol, 5, openOrders, currentRound, activeOrderBook, orderStore, 0)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, OpenPositionOrdersCancelling, state) assert.Equal(t, OpenPositionOrdersCancelling, state)
}) })
@ -184,7 +184,7 @@ func Test_RecoverState(t *testing.T) {
} }
orderStore := core.NewOrderStore(symbol) orderStore := core.NewOrderStore(symbol)
activeOrderBook := bbgo.NewActiveOrderBook(symbol) activeOrderBook := bbgo.NewActiveOrderBook(symbol)
state, err := recoverState(context.Background(), symbol, false, 5, openOrders, currentRound, activeOrderBook, orderStore, 0) state, err := recoverState(context.Background(), symbol, 5, openOrders, currentRound, activeOrderBook, orderStore, 0)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, OpenPositionOrdersCancelled, state) assert.Equal(t, OpenPositionOrdersCancelled, state)
}) })
@ -206,7 +206,7 @@ func Test_RecoverState(t *testing.T) {
} }
orderStore := core.NewOrderStore(symbol) orderStore := core.NewOrderStore(symbol)
activeOrderBook := bbgo.NewActiveOrderBook(symbol) activeOrderBook := bbgo.NewActiveOrderBook(symbol)
state, err := recoverState(context.Background(), symbol, false, 5, openOrders, currentRound, activeOrderBook, orderStore, 0) state, err := recoverState(context.Background(), symbol, 5, openOrders, currentRound, activeOrderBook, orderStore, 0)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, TakeProfitReady, state) assert.Equal(t, TakeProfitReady, state)
}) })
@ -226,7 +226,7 @@ func Test_RecoverState(t *testing.T) {
} }
orderStore := core.NewOrderStore(symbol) orderStore := core.NewOrderStore(symbol)
activeOrderBook := bbgo.NewActiveOrderBook(symbol) activeOrderBook := bbgo.NewActiveOrderBook(symbol)
state, err := recoverState(context.Background(), symbol, false, 5, openOrders, currentRound, activeOrderBook, orderStore, 0) state, err := recoverState(context.Background(), symbol, 5, openOrders, currentRound, activeOrderBook, orderStore, 0)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, WaitToOpenPosition, state) assert.Equal(t, WaitToOpenPosition, state)
}) })

View File

@ -178,12 +178,11 @@ func (s *Strategy) runOpenPositionOrdersCancelled(ctx context.Context, next Stat
} }
func (s *Strategy) runTakeProfitReady(_ context.Context, next State) { func (s *Strategy) runTakeProfitReady(_ context.Context, next State) {
// wait 3 seconds to avoid position not update
time.Sleep(3 * time.Second)
s.logger.Info("[State] TakeProfitReady - start reseting position and calculate budget for next round") s.logger.Info("[State] TakeProfitReady - start reseting position and calculate budget for next round")
if s.Short { s.Budget = s.Budget.Add(s.Position.Quote)
s.Budget = s.Budget.Add(s.Position.Base)
} else {
s.Budget = s.Budget.Add(s.Position.Quote)
}
// reset position // reset position
s.Position.Reset() s.Position.Reset()

View File

@ -34,7 +34,6 @@ type Strategy struct {
Symbol string `json:"symbol"` Symbol string `json:"symbol"`
// setting // setting
Short bool `json:"short"`
Budget fixedpoint.Value `json:"budget"` Budget fixedpoint.Value `json:"budget"`
MaxOrderNum int64 `json:"maxOrderNum"` MaxOrderNum int64 `json:"maxOrderNum"`
PriceDeviation fixedpoint.Value `json:"priceDeviation"` PriceDeviation fixedpoint.Value `json:"priceDeviation"`
@ -122,10 +121,6 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
s.logger.Infof("[DCA] FILLED ORDER: %s", o.String()) s.logger.Infof("[DCA] FILLED ORDER: %s", o.String())
openPositionSide := types.SideTypeBuy openPositionSide := types.SideTypeBuy
takeProfitSide := types.SideTypeSell takeProfitSide := types.SideTypeSell
if s.Short {
openPositionSide = types.SideTypeSell
takeProfitSide = types.SideTypeBuy
}
switch o.Side { switch o.Side {
case openPositionSide: case openPositionSide:
@ -145,7 +140,7 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
compRes := kline.Close.Compare(s.takeProfitPrice) compRes := kline.Close.Compare(s.takeProfitPrice)
// price doesn't hit the take profit price // price doesn't hit the take profit price
if (s.Short && compRes > 0) || (!s.Short && compRes < 0) { if compRes < 0 {
return return
} }
@ -193,9 +188,6 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
func (s *Strategy) updateTakeProfitPrice() { func (s *Strategy) updateTakeProfitPrice() {
takeProfitRatio := s.TakeProfitRatio takeProfitRatio := s.TakeProfitRatio
if s.Short {
takeProfitRatio = takeProfitRatio.Neg()
}
s.takeProfitPrice = s.Market.TruncatePrice(s.Position.AverageCost.Mul(fixedpoint.One.Add(takeProfitRatio))) s.takeProfitPrice = s.Market.TruncatePrice(s.Position.AverageCost.Mul(fixedpoint.One.Add(takeProfitRatio)))
s.logger.Infof("[DCA] cost: %s, ratio: %s, price: %s", s.Position.AverageCost, takeProfitRatio, s.takeProfitPrice) s.logger.Infof("[DCA] cost: %s, ratio: %s, price: %s", s.Position.AverageCost, takeProfitRatio, s.takeProfitPrice)
} }

View File

@ -9,7 +9,7 @@ import (
func (s *Strategy) placeTakeProfitOrders(ctx context.Context) error { func (s *Strategy) placeTakeProfitOrders(ctx context.Context) error {
s.logger.Info("[DCA] start placing take profit orders") s.logger.Info("[DCA] start placing take profit orders")
order := generateTakeProfitOrder(s.Market, s.Short, s.TakeProfitRatio, s.Position, s.OrderGroupID) order := generateTakeProfitOrder(s.Market, s.TakeProfitRatio, s.Position, s.OrderGroupID)
createdOrders, err := s.OrderExecutor.SubmitOrders(ctx, order) createdOrders, err := s.OrderExecutor.SubmitOrders(ctx, order)
if err != nil { if err != nil {
return err return err
@ -22,12 +22,8 @@ func (s *Strategy) placeTakeProfitOrders(ctx context.Context) error {
return nil return nil
} }
func generateTakeProfitOrder(market types.Market, short bool, takeProfitRatio fixedpoint.Value, position *types.Position, orderGroupID uint32) types.SubmitOrder { func generateTakeProfitOrder(market types.Market, takeProfitRatio fixedpoint.Value, position *types.Position, orderGroupID uint32) types.SubmitOrder {
side := types.SideTypeSell side := types.SideTypeSell
if short {
takeProfitRatio = takeProfitRatio.Neg()
side = types.SideTypeBuy
}
takeProfitPrice := market.TruncatePrice(position.AverageCost.Mul(fixedpoint.One.Add(takeProfitRatio))) takeProfitPrice := market.TruncatePrice(position.AverageCost.Mul(fixedpoint.One.Add(takeProfitRatio)))
return types.SubmitOrder{ return types.SubmitOrder{
Symbol: market.Symbol, Symbol: market.Symbol,

View File

@ -24,7 +24,7 @@ func TestGenerateTakeProfitOrder(t *testing.T) {
FeeCurrency: strategy.Market.BaseCurrency, FeeCurrency: strategy.Market.BaseCurrency,
}) })
o := generateTakeProfitOrder(strategy.Market, false, strategy.TakeProfitRatio, position, strategy.OrderGroupID) o := generateTakeProfitOrder(strategy.Market, strategy.TakeProfitRatio, position, strategy.OrderGroupID)
assert.Equal(Number("31397.09"), o.Price) assert.Equal(Number("31397.09"), o.Price)
assert.Equal(Number("0.9985"), o.Quantity) assert.Equal(Number("0.9985"), o.Quantity)
assert.Equal(types.SideTypeSell, o.Side) assert.Equal(types.SideTypeSell, o.Side)
@ -38,7 +38,7 @@ func TestGenerateTakeProfitOrder(t *testing.T) {
Fee: Number("0.00075"), Fee: Number("0.00075"),
FeeCurrency: strategy.Market.BaseCurrency, FeeCurrency: strategy.Market.BaseCurrency,
}) })
o = generateTakeProfitOrder(strategy.Market, false, strategy.TakeProfitRatio, position, strategy.OrderGroupID) o = generateTakeProfitOrder(strategy.Market, strategy.TakeProfitRatio, position, strategy.OrderGroupID)
assert.Equal(Number("30846.26"), o.Price) assert.Equal(Number("30846.26"), o.Price)
assert.Equal(Number("1.49775"), o.Quantity) assert.Equal(Number("1.49775"), o.Quantity)
assert.Equal(types.SideTypeSell, o.Side) assert.Equal(types.SideTypeSell, o.Side)