MINOR: add test for recovery

This commit is contained in:
chiahung 2023-05-22 14:45:39 +08:00
parent 2f050332eb
commit 18a7520fa7
5 changed files with 301 additions and 0 deletions

View 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)
})
}

View File

@ -0,0 +1 @@
orderID,price,quantity,remaining_quantity,side,creationTime,updateTime
1 orderID price quantity remaining_quantity side creationTime updateTime

View File

@ -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
1 orderID price quantity remaining_quantity side creationTime updateTime
2 1 20000.0000000000000000 1.0000000000000000 1.0000000000000000 buy 2023-05-01T08:00:00.000Z 2023-05-01T08:00:00.000Z
3 2 25000.0000000000000000 1.0000000000000000 1.0000000000000000 buy 2023-05-01T08:00:00.000Z 2023-05-01T08:00:00.000Z

View 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
}

View File

@ -0,0 +1 @@
id,price,quantity,side,fee,feeCurrency,orderID,created_at,tradedAt
1 id price quantity side fee feeCurrency orderID created_at tradedAt