mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 14:55:16 +00:00
MINOR: add test for recovery
This commit is contained in:
parent
2f050332eb
commit
18a7520fa7
284
pkg/strategy/grid2/recover_test.go
Normal file
284
pkg/strategy/grid2/recover_test.go
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
package grid2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/csv"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestData struct {
|
||||||
|
Market types.Market `json:"market" yaml:"market"`
|
||||||
|
Strategy Strategy `json:"strategy" yaml:"strategy"`
|
||||||
|
OpenOrders []types.Order `json:"openOrders" yaml:"openOrders"`
|
||||||
|
ClosedOrders []types.Order `json:"closedOrders" yaml:"closedOrders"`
|
||||||
|
Trades []types.Trade `json:"trades" yaml:"trades"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestDataService struct {
|
||||||
|
Orders map[string]types.Order
|
||||||
|
Trades []types.Trade
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestDataService) QueryTrades(ctx context.Context, symbol string, options *types.TradeQueryOptions) ([]types.Trade, error) {
|
||||||
|
var i int = 0
|
||||||
|
if options.LastTradeID != 0 {
|
||||||
|
for idx, trade := range t.Trades {
|
||||||
|
if trade.ID < options.LastTradeID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
i = idx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var trades []types.Trade
|
||||||
|
l := len(t.Trades)
|
||||||
|
for ; i < l && len(trades) < int(options.Limit); i++ {
|
||||||
|
trades = append(trades, t.Trades[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return trades, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestDataService) QueryOrder(ctx context.Context, q types.OrderQuery) (*types.Order, error) {
|
||||||
|
if len(q.OrderID) == 0 {
|
||||||
|
return nil, fmt.Errorf("order id should not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
order, exist := t.Orders[q.OrderID]
|
||||||
|
if !exist {
|
||||||
|
return nil, fmt.Errorf("order not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &order, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStrategy(t *TestData) *Strategy {
|
||||||
|
s := t.Strategy
|
||||||
|
s.Debug = true
|
||||||
|
s.Initialize()
|
||||||
|
s.Market = t.Market
|
||||||
|
s.Position = types.NewPositionFromMarket(t.Market)
|
||||||
|
s.orderExecutor = bbgo.NewGeneralOrderExecutor(&bbgo.ExchangeSession{}, t.Market.Symbol, ID, s.InstanceID(), s.Position)
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTestDataService(t *TestData) *TestDataService {
|
||||||
|
var orders map[string]types.Order = make(map[string]types.Order)
|
||||||
|
for _, order := range t.OpenOrders {
|
||||||
|
orders[strconv.FormatUint(order.OrderID, 10)] = order
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, order := range t.ClosedOrders {
|
||||||
|
orders[strconv.FormatUint(order.OrderID, 10)] = order
|
||||||
|
}
|
||||||
|
|
||||||
|
trades := t.Trades
|
||||||
|
sort.Slice(t.Trades, func(i, j int) bool {
|
||||||
|
return trades[i].ID < trades[j].ID
|
||||||
|
})
|
||||||
|
|
||||||
|
return &TestDataService{
|
||||||
|
Orders: orders,
|
||||||
|
Trades: trades,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readSpec(fileName string) (*TestData, error) {
|
||||||
|
content, err := ioutil.ReadFile(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
market := types.Market{}
|
||||||
|
if err := json.Unmarshal(content, &market); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
strategy := Strategy{}
|
||||||
|
if err := json.Unmarshal(content, &strategy); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := TestData{
|
||||||
|
Market: market,
|
||||||
|
Strategy: strategy,
|
||||||
|
}
|
||||||
|
return &data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readOrdersFromCSV(fileName string) ([]types.Order, error) {
|
||||||
|
csvFile, err := os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer csvFile.Close()
|
||||||
|
csvReader := csv.NewReader(csvFile)
|
||||||
|
|
||||||
|
keys, err := csvReader.Read()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var orders []types.Order
|
||||||
|
for {
|
||||||
|
row, err := csvReader.Read()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(row) != len(keys) {
|
||||||
|
return nil, fmt.Errorf("length of row should be equal to length of keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
var m map[string]interface{} = make(map[string]interface{})
|
||||||
|
for i, key := range keys {
|
||||||
|
if key == "orderID" {
|
||||||
|
x, err := strconv.ParseUint(row[i], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m[key] = x
|
||||||
|
} else {
|
||||||
|
m[key] = row[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
order := types.Order{}
|
||||||
|
if err = json.Unmarshal(b, &order); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
orders = append(orders, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
return orders, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTradesFromCSV(fileName string) ([]types.Trade, error) {
|
||||||
|
csvFile, err := os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer csvFile.Close()
|
||||||
|
csvReader := csv.NewReader(csvFile)
|
||||||
|
|
||||||
|
keys, err := csvReader.Read()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var trades []types.Trade
|
||||||
|
for {
|
||||||
|
row, err := csvReader.Read()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(row) != len(keys) {
|
||||||
|
return nil, fmt.Errorf("length of row should be equal to length of keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
var m map[string]interface{} = make(map[string]interface{})
|
||||||
|
for i, key := range keys {
|
||||||
|
switch key {
|
||||||
|
case "id", "orderID":
|
||||||
|
x, err := strconv.ParseUint(row[i], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m[key] = x
|
||||||
|
default:
|
||||||
|
m[key] = row[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
trade := types.Trade{}
|
||||||
|
if err = json.Unmarshal(b, &trade); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
trades = append(trades, trade)
|
||||||
|
}
|
||||||
|
|
||||||
|
return trades, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTestDataFrom(fileDir string) (*TestData, error) {
|
||||||
|
data, err := readSpec(fmt.Sprintf("%s/spec", fileDir))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
openOrders, err := readOrdersFromCSV(fmt.Sprintf("%s/open_orders.csv", fileDir))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
closedOrders, err := readOrdersFromCSV(fmt.Sprintf("%s/closed_orders.csv", fileDir))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
trades, err := readTradesFromCSV(fmt.Sprintf("%s/trades.csv", fileDir))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data.OpenOrders = openOrders
|
||||||
|
data.ClosedOrders = closedOrders
|
||||||
|
data.Trades = trades
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRecoverByScanningTrades(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
t.Run("test case 1", func(t *testing.T) {
|
||||||
|
fileDir := "recovery_testcase/testcase1/"
|
||||||
|
|
||||||
|
data, err := readTestDataFrom(fileDir)
|
||||||
|
if !assert.NoError(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
testService := NewTestDataService(data)
|
||||||
|
strategy := NewStrategy(data)
|
||||||
|
filledOrders, err := strategy.getFilledOrdersByScanningTrades(context.Background(), testService, testService, data.OpenOrders)
|
||||||
|
if !assert.NoError(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Len(filledOrders, 0)
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
orderID,price,quantity,remaining_quantity,side,creationTime,updateTime
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
orderID,price,quantity,remaining_quantity,side,creationTime,updateTime
|
||||||
|
1,20000.0000000000000000,1.0000000000000000,1.0000000000000000,buy,2023-05-01T08:00:00.000Z,2023-05-01T08:00:00.000Z
|
||||||
|
2,25000.0000000000000000,1.0000000000000000,1.0000000000000000,buy,2023-05-01T08:00:00.000Z,2023-05-01T08:00:00.000Z
|
|
12
pkg/strategy/grid2/recovery_testcase/testcase1/spec
Normal file
12
pkg/strategy/grid2/recovery_testcase/testcase1/spec
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"symbol":"BTCUSDT",
|
||||||
|
"quoteCurrency": "USDT",
|
||||||
|
"baseCurrency": "BTC",
|
||||||
|
"mode":"arithmetic",
|
||||||
|
"lowerPrice":"20000.0",
|
||||||
|
"upperPrice":"30000.0",
|
||||||
|
"gridNumber":3,
|
||||||
|
"quoteInvestment":"45000.0",
|
||||||
|
"tickSize":0.1,
|
||||||
|
"stepSize":0.00000001
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
id,price,quantity,side,fee,feeCurrency,orderID,created_at,tradedAt
|
|
Loading…
Reference in New Issue
Block a user