mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
Merge pull request #1473 from c9s/kbearXD/dca2/remove-short
FEATURE: remove Short
This commit is contained in:
commit
d08aebabad
|
@ -15,12 +15,12 @@ type cancelOrdersByGroupIDApi interface {
|
|||
|
||||
func (s *Strategy) placeOpenPositionOrders(ctx context.Context) error {
|
||||
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 {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -35,24 +35,17 @@ func (s *Strategy) placeOpenPositionOrders(ctx context.Context) error {
|
|||
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)
|
||||
if err != nil {
|
||||
return fixedpoint.Zero, err
|
||||
}
|
||||
|
||||
if short {
|
||||
return ticker.Buy, nil
|
||||
} else {
|
||||
return ticker.Sell, nil
|
||||
}
|
||||
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)
|
||||
if short {
|
||||
factor = fixedpoint.One.Add(priceDeviation)
|
||||
}
|
||||
|
||||
// calculate all valid prices
|
||||
var prices []fixedpoint.Value
|
||||
|
@ -68,15 +61,12 @@ func generateOpenPositionOrders(market types.Market, short bool, budget, price,
|
|||
prices = append(prices, price)
|
||||
}
|
||||
|
||||
notional, orderNum := calculateNotionalAndNum(market, short, budget, prices)
|
||||
notional, orderNum := calculateNotionalAndNum(market, budget, prices)
|
||||
if orderNum == 0 {
|
||||
return nil, fmt.Errorf("failed to calculate notional and num of open position orders, price: %s, budget: %s", price, budget)
|
||||
}
|
||||
|
||||
side := types.SideTypeBuy
|
||||
if short {
|
||||
side = types.SideTypeSell
|
||||
}
|
||||
|
||||
var submitOrders []types.SubmitOrder
|
||||
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
|
||||
// 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-- {
|
||||
notional := budget.Div(fixedpoint.NewFromInt(int64(num)))
|
||||
if notional.Compare(market.MinNotional) < 0 {
|
||||
|
@ -107,9 +97,6 @@ func calculateNotionalAndNum(market types.Market, short bool, budget fixedpoint.
|
|||
}
|
||||
|
||||
maxPriceIdx := 0
|
||||
if short {
|
||||
maxPriceIdx = num - 1
|
||||
}
|
||||
quantity := market.TruncateQuantity(notional.Div(prices[maxPriceIdx]))
|
||||
if quantity.Compare(market.MinQuantity) < 0 {
|
||||
continue
|
||||
|
|
|
@ -36,7 +36,6 @@ func newTestStrategy(va ...string) *Strategy {
|
|||
logger: logrus.NewEntry(logrus.New()),
|
||||
Symbol: symbol,
|
||||
Market: market,
|
||||
Short: false,
|
||||
TakeProfitRatio: Number("10%"),
|
||||
}
|
||||
return s
|
||||
|
@ -51,7 +50,7 @@ func TestGenerateOpenPositionOrders(t *testing.T) {
|
|||
budget := Number("10500")
|
||||
askPrice := Number("30000")
|
||||
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) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -39,14 +39,14 @@ func (s *Strategy) recover(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
currentRound, err := getCurrentRoundOrders(s.Short, openOrders, closedOrders, s.OrderGroupID)
|
||||
currentRound, err := getCurrentRoundOrders(openOrders, closedOrders, s.OrderGroupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
debugRoundOrders(s.logger, "current", currentRound)
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ func (s *Strategy) recover(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
// new strategy
|
||||
return WaitToOpenPosition, nil
|
||||
|
@ -225,15 +225,10 @@ type Round struct {
|
|||
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
|
||||
takeProfitSide := types.SideTypeSell
|
||||
|
||||
if short {
|
||||
openPositionSide = types.SideTypeSell
|
||||
takeProfitSide = types.SideTypeBuy
|
||||
}
|
||||
|
||||
var allOrders []types.Order
|
||||
allOrders = append(allOrders, openOrders...)
|
||||
allOrders = append(allOrders, closedOrders...)
|
||||
|
|
|
@ -39,7 +39,7 @@ func Test_GetCurrenctAndLastRoundOrders(t *testing.T) {
|
|||
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.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)),
|
||||
}
|
||||
|
||||
currentRound, err := getCurrentRoundOrders(false, openOrders, closedOrders, 0)
|
||||
currentRound, err := getCurrentRoundOrders(openOrders, closedOrders, 0)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, 0, currentRound.TakeProfitOrder.OrderID)
|
||||
|
@ -101,7 +101,7 @@ func Test_RecoverState(t *testing.T) {
|
|||
currentRound := Round{}
|
||||
activeOrderBook := bbgo.NewActiveOrderBook(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.Equal(t, WaitToOpenPosition, state)
|
||||
})
|
||||
|
@ -120,7 +120,7 @@ func Test_RecoverState(t *testing.T) {
|
|||
}
|
||||
orderStore := core.NewOrderStore(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.Equal(t, OpenPositionReady, state)
|
||||
})
|
||||
|
@ -144,7 +144,7 @@ func Test_RecoverState(t *testing.T) {
|
|||
}
|
||||
orderStore := core.NewOrderStore(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.Equal(t, OpenPositionOrderFilled, state)
|
||||
})
|
||||
|
@ -165,7 +165,7 @@ func Test_RecoverState(t *testing.T) {
|
|||
}
|
||||
orderStore := core.NewOrderStore(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.Equal(t, OpenPositionOrdersCancelling, state)
|
||||
})
|
||||
|
@ -184,7 +184,7 @@ func Test_RecoverState(t *testing.T) {
|
|||
}
|
||||
orderStore := core.NewOrderStore(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.Equal(t, OpenPositionOrdersCancelled, state)
|
||||
})
|
||||
|
@ -206,7 +206,7 @@ func Test_RecoverState(t *testing.T) {
|
|||
}
|
||||
orderStore := core.NewOrderStore(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.Equal(t, TakeProfitReady, state)
|
||||
})
|
||||
|
@ -226,7 +226,7 @@ func Test_RecoverState(t *testing.T) {
|
|||
}
|
||||
orderStore := core.NewOrderStore(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.Equal(t, WaitToOpenPosition, state)
|
||||
})
|
||||
|
|
|
@ -178,12 +178,11 @@ func (s *Strategy) runOpenPositionOrdersCancelled(ctx context.Context, next Stat
|
|||
}
|
||||
|
||||
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")
|
||||
if s.Short {
|
||||
s.Budget = s.Budget.Add(s.Position.Base)
|
||||
} else {
|
||||
s.Budget = s.Budget.Add(s.Position.Quote)
|
||||
}
|
||||
s.Budget = s.Budget.Add(s.Position.Quote)
|
||||
|
||||
// reset position
|
||||
s.Position.Reset()
|
||||
|
|
|
@ -34,7 +34,6 @@ type Strategy struct {
|
|||
Symbol string `json:"symbol"`
|
||||
|
||||
// setting
|
||||
Short bool `json:"short"`
|
||||
Budget fixedpoint.Value `json:"budget"`
|
||||
MaxOrderNum int64 `json:"maxOrderNum"`
|
||||
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())
|
||||
openPositionSide := types.SideTypeBuy
|
||||
takeProfitSide := types.SideTypeSell
|
||||
if s.Short {
|
||||
openPositionSide = types.SideTypeSell
|
||||
takeProfitSide = types.SideTypeBuy
|
||||
}
|
||||
|
||||
switch o.Side {
|
||||
case openPositionSide:
|
||||
|
@ -145,7 +140,7 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
|
|||
|
||||
compRes := kline.Close.Compare(s.takeProfitPrice)
|
||||
// price doesn't hit the take profit price
|
||||
if (s.Short && compRes > 0) || (!s.Short && compRes < 0) {
|
||||
if compRes < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -193,9 +188,6 @@ func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.
|
|||
|
||||
func (s *Strategy) updateTakeProfitPrice() {
|
||||
takeProfitRatio := s.TakeProfitRatio
|
||||
if s.Short {
|
||||
takeProfitRatio = takeProfitRatio.Neg()
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
func (s *Strategy) placeTakeProfitOrders(ctx context.Context) error {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -22,12 +22,8 @@ func (s *Strategy) placeTakeProfitOrders(ctx context.Context) error {
|
|||
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
|
||||
if short {
|
||||
takeProfitRatio = takeProfitRatio.Neg()
|
||||
side = types.SideTypeBuy
|
||||
}
|
||||
takeProfitPrice := market.TruncatePrice(position.AverageCost.Mul(fixedpoint.One.Add(takeProfitRatio)))
|
||||
return types.SubmitOrder{
|
||||
Symbol: market.Symbol,
|
||||
|
|
|
@ -24,7 +24,7 @@ func TestGenerateTakeProfitOrder(t *testing.T) {
|
|||
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("0.9985"), o.Quantity)
|
||||
assert.Equal(types.SideTypeSell, o.Side)
|
||||
|
@ -38,7 +38,7 @@ func TestGenerateTakeProfitOrder(t *testing.T) {
|
|||
Fee: Number("0.00075"),
|
||||
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("1.49775"), o.Quantity)
|
||||
assert.Equal(types.SideTypeSell, o.Side)
|
||||
|
|
Loading…
Reference in New Issue
Block a user