2023-11-27 07:55:02 +00:00
|
|
|
package dca2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"math/rand"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/c9s/bbgo/pkg/bbgo"
|
|
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
)
|
|
|
|
|
2023-12-27 03:41:29 +00:00
|
|
|
func generateTestOrder(side types.SideType, status types.OrderStatus, createdAt time.Time) types.Order {
|
2023-11-27 07:55:02 +00:00
|
|
|
return types.Order{
|
|
|
|
OrderID: rand.Uint64(),
|
|
|
|
SubmitOrder: types.SubmitOrder{
|
|
|
|
Side: side,
|
|
|
|
},
|
|
|
|
Status: status,
|
|
|
|
CreationTime: types.Time(createdAt),
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-01-23 08:41:54 +00:00
|
|
|
func Test_GetCurrenctRoundOrders(t *testing.T) {
|
2023-11-27 07:55:02 +00:00
|
|
|
t.Run("case 1", func(t *testing.T) {
|
|
|
|
now := time.Now()
|
|
|
|
openOrders := []types.Order{
|
2023-12-27 03:41:29 +00:00
|
|
|
generateTestOrder(types.SideTypeSell, types.OrderStatusNew, now),
|
2023-11-27 07:55:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
closedOrders := []types.Order{
|
2023-12-27 03:41:29 +00:00
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-1*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-2*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-3*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-4*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-5*time.Second)),
|
2023-11-27 07:55:02 +00:00
|
|
|
}
|
|
|
|
|
2023-12-22 07:50:48 +00:00
|
|
|
currentRound, err := getCurrentRoundOrders(openOrders, closedOrders, 0)
|
2023-11-27 07:55:02 +00:00
|
|
|
|
2023-12-27 03:41:29 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.NotEqual(t, 0, currentRound.TakeProfitOrder.OrderID)
|
|
|
|
assert.Equal(t, 5, len(currentRound.OpenPositionOrders))
|
2023-11-27 07:55:02 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("case 2", func(t *testing.T) {
|
|
|
|
now := time.Now()
|
|
|
|
openOrders := []types.Order{
|
2023-12-27 03:41:29 +00:00
|
|
|
generateTestOrder(types.SideTypeSell, types.OrderStatusNew, now),
|
2023-11-27 07:55:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
closedOrders := []types.Order{
|
2023-12-27 03:41:29 +00:00
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-1*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-2*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-3*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-4*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-5*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeSell, types.OrderStatusFilled, now.Add(-6*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-7*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-8*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-9*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-10*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-11*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeSell, types.OrderStatusFilled, now.Add(-12*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-13*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-14*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-15*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-16*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-17*time.Second)),
|
2023-11-27 07:55:02 +00:00
|
|
|
}
|
|
|
|
|
2023-12-22 07:50:48 +00:00
|
|
|
currentRound, err := getCurrentRoundOrders(openOrders, closedOrders, 0)
|
2023-11-27 07:55:02 +00:00
|
|
|
|
2023-12-27 03:41:29 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.NotEqual(t, 0, currentRound.TakeProfitOrder.OrderID)
|
|
|
|
assert.Equal(t, 5, len(currentRound.OpenPositionOrders))
|
2023-11-27 07:55:02 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
type MockQueryOrders struct {
|
|
|
|
OpenOrders []types.Order
|
|
|
|
ClosedOrders []types.Order
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockQueryOrders) QueryOpenOrders(ctx context.Context, symbol string) ([]types.Order, error) {
|
|
|
|
return m.OpenOrders, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MockQueryOrders) QueryClosedOrdersDesc(ctx context.Context, symbol string, since, until time.Time, lastOrderID uint64) ([]types.Order, error) {
|
|
|
|
return m.ClosedOrders, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func Test_RecoverState(t *testing.T) {
|
2024-01-23 08:41:54 +00:00
|
|
|
strategy := newTestStrategy()
|
2023-11-27 07:55:02 +00:00
|
|
|
|
|
|
|
t.Run("new strategy", func(t *testing.T) {
|
|
|
|
currentRound := Round{}
|
2024-01-23 08:41:54 +00:00
|
|
|
position := types.NewPositionFromMarket(strategy.Market)
|
|
|
|
orderExecutor := bbgo.NewGeneralOrderExecutor(nil, strategy.Symbol, ID, "", position)
|
2024-03-04 12:52:01 +00:00
|
|
|
state, err := recoverState(context.Background(), 5, currentRound, orderExecutor)
|
2023-12-27 03:41:29 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, WaitToOpenPosition, state)
|
2023-11-27 07:55:02 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("at open position stage and no filled order", func(t *testing.T) {
|
|
|
|
now := time.Now()
|
|
|
|
currentRound := Round{
|
2024-01-23 08:41:54 +00:00
|
|
|
OpenPositionOrders: []types.Order{
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusPartiallyFilled, now.Add(-1*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusNew, now.Add(-2*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusNew, now.Add(-3*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusNew, now.Add(-4*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusNew, now.Add(-5*time.Second)),
|
|
|
|
},
|
2023-11-27 07:55:02 +00:00
|
|
|
}
|
2024-01-23 08:41:54 +00:00
|
|
|
position := types.NewPositionFromMarket(strategy.Market)
|
|
|
|
orderExecutor := bbgo.NewGeneralOrderExecutor(nil, strategy.Symbol, ID, "", position)
|
2024-03-04 12:52:01 +00:00
|
|
|
state, err := recoverState(context.Background(), 5, currentRound, orderExecutor)
|
2023-12-27 03:41:29 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, OpenPositionReady, state)
|
2023-11-27 07:55:02 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("at open position stage and there at least one filled order", func(t *testing.T) {
|
|
|
|
now := time.Now()
|
|
|
|
currentRound := Round{
|
|
|
|
OpenPositionOrders: []types.Order{
|
2023-12-27 03:41:29 +00:00
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-1*time.Second)),
|
2024-01-23 08:41:54 +00:00
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusNew, now.Add(-2*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusNew, now.Add(-3*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusNew, now.Add(-4*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusNew, now.Add(-5*time.Second)),
|
2023-11-27 07:55:02 +00:00
|
|
|
},
|
|
|
|
}
|
2024-01-23 08:41:54 +00:00
|
|
|
position := types.NewPositionFromMarket(strategy.Market)
|
|
|
|
orderExecutor := bbgo.NewGeneralOrderExecutor(nil, strategy.Symbol, ID, "", position)
|
2024-03-04 12:52:01 +00:00
|
|
|
state, err := recoverState(context.Background(), 5, currentRound, orderExecutor)
|
2023-12-27 03:41:29 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, OpenPositionOrderFilled, state)
|
2023-11-27 07:55:02 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("open position stage finish, but stop at cancelling", func(t *testing.T) {
|
|
|
|
now := time.Now()
|
|
|
|
currentRound := Round{
|
|
|
|
OpenPositionOrders: []types.Order{
|
2023-12-27 03:41:29 +00:00
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-1*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-2*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-3*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-4*time.Second)),
|
2024-01-23 08:41:54 +00:00
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusNew, now.Add(-5*time.Second)),
|
2023-11-27 07:55:02 +00:00
|
|
|
},
|
|
|
|
}
|
2024-01-23 08:41:54 +00:00
|
|
|
position := types.NewPositionFromMarket(strategy.Market)
|
|
|
|
orderExecutor := bbgo.NewGeneralOrderExecutor(nil, strategy.Symbol, ID, "", position)
|
2024-03-04 12:52:01 +00:00
|
|
|
state, err := recoverState(context.Background(), 5, currentRound, orderExecutor)
|
2023-12-27 03:41:29 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, OpenPositionOrdersCancelling, state)
|
2023-11-27 07:55:02 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("open-position orders are cancelled", func(t *testing.T) {
|
|
|
|
now := time.Now()
|
|
|
|
currentRound := Round{
|
|
|
|
OpenPositionOrders: []types.Order{
|
2023-12-27 03:41:29 +00:00
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-1*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-2*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-3*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-4*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-5*time.Second)),
|
2023-11-27 07:55:02 +00:00
|
|
|
},
|
|
|
|
}
|
2024-01-23 08:41:54 +00:00
|
|
|
position := types.NewPositionFromMarket(strategy.Market)
|
|
|
|
orderExecutor := bbgo.NewGeneralOrderExecutor(nil, strategy.Symbol, ID, "", position)
|
2024-03-04 12:52:01 +00:00
|
|
|
state, err := recoverState(context.Background(), 5, currentRound, orderExecutor)
|
2023-12-27 03:41:29 +00:00
|
|
|
assert.NoError(t, err)
|
2024-01-23 08:41:54 +00:00
|
|
|
assert.Equal(t, OpenPositionOrdersCancelling, state)
|
2023-11-27 07:55:02 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("at take profit stage, and not filled yet", func(t *testing.T) {
|
|
|
|
now := time.Now()
|
|
|
|
currentRound := Round{
|
2024-01-23 08:41:54 +00:00
|
|
|
TakeProfitOrder: generateTestOrder(types.SideTypeSell, types.OrderStatusNew, now),
|
2023-11-27 07:55:02 +00:00
|
|
|
OpenPositionOrders: []types.Order{
|
2023-12-27 03:41:29 +00:00
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-1*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-2*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-3*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-4*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-5*time.Second)),
|
2023-11-27 07:55:02 +00:00
|
|
|
},
|
|
|
|
}
|
2024-01-23 08:41:54 +00:00
|
|
|
position := types.NewPositionFromMarket(strategy.Market)
|
|
|
|
orderExecutor := bbgo.NewGeneralOrderExecutor(nil, strategy.Symbol, ID, "", position)
|
2024-03-04 12:52:01 +00:00
|
|
|
state, err := recoverState(context.Background(), 5, currentRound, orderExecutor)
|
2023-12-27 03:41:29 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, TakeProfitReady, state)
|
2023-11-27 07:55:02 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("at take profit stage, take-profit order filled", func(t *testing.T) {
|
|
|
|
now := time.Now()
|
|
|
|
currentRound := Round{
|
2023-12-27 03:41:29 +00:00
|
|
|
TakeProfitOrder: generateTestOrder(types.SideTypeSell, types.OrderStatusFilled, now),
|
2023-11-27 07:55:02 +00:00
|
|
|
OpenPositionOrders: []types.Order{
|
2023-12-27 03:41:29 +00:00
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusFilled, now.Add(-1*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-2*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-3*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-4*time.Second)),
|
|
|
|
generateTestOrder(types.SideTypeBuy, types.OrderStatusCanceled, now.Add(-5*time.Second)),
|
2023-11-27 07:55:02 +00:00
|
|
|
},
|
|
|
|
}
|
2024-01-23 08:41:54 +00:00
|
|
|
position := types.NewPositionFromMarket(strategy.Market)
|
|
|
|
orderExecutor := bbgo.NewGeneralOrderExecutor(nil, strategy.Symbol, ID, "", position)
|
2024-03-04 12:52:01 +00:00
|
|
|
state, err := recoverState(context.Background(), 5, currentRound, orderExecutor)
|
2023-12-27 03:41:29 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, WaitToOpenPosition, state)
|
2023-11-27 07:55:02 +00:00
|
|
|
})
|
|
|
|
}
|