pkg/exchange: refactor query closed order

This commit is contained in:
Edwin 2024-01-12 15:09:45 +08:00
parent 03449d0c9a
commit fa145a3622
8 changed files with 197 additions and 117 deletions

View File

@ -149,7 +149,7 @@ func toGlobalTrades(orderDetails []okexapi.OrderDetails) ([]types.Trade, error)
return trades, nil return trades, nil
} }
func openOrderToGlobal(order *okexapi.OpenOrder) (*types.Order, error) { func orderDetailToGlobal(order *okexapi.OrderDetail) (*types.Order, error) {
side := toGlobalSide(order.Side) side := toGlobalSide(order.Side)
orderType, err := toGlobalOrderType(order.OrderType) orderType, err := toGlobalOrderType(order.OrderType)

View File

@ -11,13 +11,13 @@ import (
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
func Test_openOrderToGlobal(t *testing.T) { func Test_orderDetailToGlobal(t *testing.T) {
var ( var (
assert = assert.New(t) assert = assert.New(t)
orderId = 665576973905014786 orderId = 665576973905014786
// {"accFillSz":"0","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"","cTime":"1704957916401","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"0","feeCcy":"USDT","fillPx":"","fillSz":"0","fillTime":"","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665576973905014786","ordType":"limit","pnl":"0","posSide":"net","px":"48174.5","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"live","stpId":"","stpMode":"","sz":"0.00001","tag":"","tdMode":"cash","tgtCcy":"","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"","uTime":"1704957916401"} // {"accFillSz":"0","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"","cTime":"1704957916401","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"0","feeCcy":"USDT","fillPx":"","fillSz":"0","fillTime":"","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665576973905014786","ordType":"limit","pnl":"0","posSide":"net","px":"48174.5","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"live","stpId":"","stpMode":"","sz":"0.00001","tag":"","tdMode":"cash","tgtCcy":"","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"","uTime":"1704957916401"}
openOrder = &okexapi.OpenOrder{ openOrder = &okexapi.OrderDetail{
AccumulatedFillSize: fixedpoint.NewFromFloat(0), AccumulatedFillSize: fixedpoint.NewFromFloat(0),
AvgPrice: fixedpoint.NewFromFloat(0), AvgPrice: fixedpoint.NewFromFloat(0),
CreatedTime: types.NewMillisecondTimestampFromInt(1704957916401), CreatedTime: types.NewMillisecondTimestampFromInt(1704957916401),
@ -62,7 +62,7 @@ func Test_openOrderToGlobal(t *testing.T) {
) )
t.Run("succeeds", func(t *testing.T) { t.Run("succeeds", func(t *testing.T) {
order, err := openOrderToGlobal(openOrder) order, err := orderDetailToGlobal(openOrder)
assert.NoError(err) assert.NoError(err)
assert.Equal(expOrder, order) assert.Equal(expOrder, order)
}) })
@ -70,14 +70,14 @@ func Test_openOrderToGlobal(t *testing.T) {
t.Run("unexpected order status", func(t *testing.T) { t.Run("unexpected order status", func(t *testing.T) {
newOrder := *openOrder newOrder := *openOrder
newOrder.State = "xxx" newOrder.State = "xxx"
_, err := openOrderToGlobal(&newOrder) _, err := orderDetailToGlobal(&newOrder)
assert.ErrorContains(err, "xxx") assert.ErrorContains(err, "xxx")
}) })
t.Run("unexpected order type", func(t *testing.T) { t.Run("unexpected order type", func(t *testing.T) {
newOrder := *openOrder newOrder := *openOrder
newOrder.OrderType = "xxx" newOrder.OrderType = "xxx"
_, err := openOrderToGlobal(&newOrder) _, err := orderDetailToGlobal(&newOrder)
assert.ErrorContains(err, "xxx") assert.ErrorContains(err, "xxx")
}) })

View File

@ -24,13 +24,14 @@ var (
marketDataLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 5) marketDataLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 5)
orderRateLimiter = rate.NewLimiter(rate.Every(300*time.Millisecond), 5) orderRateLimiter = rate.NewLimiter(rate.Every(300*time.Millisecond), 5)
queryMarketLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 10) queryMarketLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 10)
queryTickerLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 10) queryTickerLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 10)
queryTickersLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 10) queryTickersLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 10)
queryAccountLimiter = rate.NewLimiter(rate.Every(200*time.Millisecond), 5) queryAccountLimiter = rate.NewLimiter(rate.Every(200*time.Millisecond), 5)
placeOrderLimiter = rate.NewLimiter(rate.Every(30*time.Millisecond), 30) placeOrderLimiter = rate.NewLimiter(rate.Every(30*time.Millisecond), 30)
batchCancelOrderLimiter = rate.NewLimiter(rate.Every(5*time.Millisecond), 200) batchCancelOrderLimiter = rate.NewLimiter(rate.Every(5*time.Millisecond), 200)
queryOpenOrderLimiter = rate.NewLimiter(rate.Every(30*time.Millisecond), 30) queryOpenOrderLimiter = rate.NewLimiter(rate.Every(30*time.Millisecond), 30)
queryClosedOrderRateLimiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 10)
) )
const ( const (
@ -40,6 +41,8 @@ const (
PlatformToken = "OKB" PlatformToken = "OKB"
defaultQueryLimit = 100 defaultQueryLimit = 100
maxHistoricalDataQueryPeriod = 90 * 24 * time.Hour
) )
var log = logrus.WithFields(logrus.Fields{ var log = logrus.WithFields(logrus.Fields{
@ -315,7 +318,7 @@ func (e *Exchange) QueryOpenOrders(ctx context.Context, symbol string) (orders [
} }
for _, o := range openOrders { for _, o := range openOrders {
o, err := openOrderToGlobal(&o) o, err := orderDetailToGlobal(&o.OrderDetail)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to convert order, err: %v", err) return nil, fmt.Errorf("failed to convert order, err: %v", err)
} }
@ -488,27 +491,32 @@ func (e *Exchange) QueryOrderTrades(ctx context.Context, q types.OrderQuery) ([]
/* /*
QueryClosedOrders can query closed orders in last 3 months, there are no time interval limitations, as long as until >= since. QueryClosedOrders can query closed orders in last 3 months, there are no time interval limitations, as long as until >= since.
Please Use lastOrderID as cursor, only return orders later than that order, that order is not included. Please Use lastOrderID as cursor, only return orders later than that order, that order is not included.
If you want to query orders by time range, please just pass since and until. If you want to query all orders within a large time range (e.g. total orders > 100), we recommend using batch.ClosedOrderBatchQuery.
If you want to query by cursor, please pass lastOrderID.
Because it gets the correct response even when you pass all parameters with the right time interval and invalid lastOrderID, like 0. ** since and until are inclusive, you can include the lastTradeId as well. **
Time interval boundary unit is second.
since is inclusive, ex. order created in 1694155903, get response if query since 1694155903, get empty if query since 1694155904
until is not inclusive, ex. order created in 1694155903, get response if query until 1694155904, get empty if query until 1694155903
*/ */
func (e *Exchange) QueryClosedOrders(ctx context.Context, symbol string, since, until time.Time, lastOrderID uint64) ([]types.Order, error) { func (e *Exchange) QueryClosedOrders(ctx context.Context, symbol string, since, until time.Time, lastOrderID uint64) (orders []types.Order, err error) {
if symbol == "" { if symbol == "" {
return nil, ErrSymbolRequired return nil, ErrSymbolRequired
} }
if err := orderRateLimiter.Wait(ctx); err != nil { newSince := since
return nil, fmt.Errorf("query closed order rate limiter wait error: %w", err) now := time.Now()
if time.Since(newSince) > maxHistoricalDataQueryPeriod {
newSince = now.Add(-maxHistoricalDataQueryPeriod)
log.Warnf("!!!OKX EXCHANGE API NOTICE!!! The closed order API cannot query data beyond 90 days from the current date, update %s -> %s", since, newSince)
}
if until.Before(newSince) {
log.Warnf("!!!OKX EXCHANGE API NOTICE!!! The 'until' comes before 'since', update until to now(%s -> %s).", until, now)
until = now
}
if until.Sub(newSince) > maxHistoricalDataQueryPeriod {
return nil, fmt.Errorf("the start time %s and end time %s cannot exceed 90 days", newSince, until)
} }
var lastOrder string if err := queryClosedOrderRateLimiter.Wait(ctx); err != nil {
if lastOrderID <= 0 { return nil, fmt.Errorf("query closed order rate limiter wait error: %w", err)
lastOrder = ""
} else {
lastOrder = strconv.FormatUint(lastOrderID, 10)
} }
res, err := e.client.NewGetOrderHistoryRequest(). res, err := e.client.NewGetOrderHistoryRequest().
@ -516,17 +524,15 @@ func (e *Exchange) QueryClosedOrders(ctx context.Context, symbol string, since,
StartTime(since). StartTime(since).
EndTime(until). EndTime(until).
Limit(defaultQueryLimit). Limit(defaultQueryLimit).
Before(lastOrder). Before(strconv.FormatUint(lastOrderID, 10)).
Do(ctx) Do(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to call get order histories error: %w", err) return nil, fmt.Errorf("failed to call get order histories error: %w", err)
} }
var orders []types.Order
for _, order := range res { for _, order := range res {
o, err2 := toGlobalOrder(&order) o, err2 := orderDetailToGlobal(&order)
if err2 != nil { if err2 != nil {
err = multierr.Append(err, err2) err = multierr.Append(err, err2)
continue continue

View File

@ -3,12 +3,13 @@ package okexapi
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/c9s/bbgo/pkg/types"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"os" "os"
"strconv" "strconv"
"testing" "testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/c9s/bbgo/pkg/testutil" "github.com/c9s/bbgo/pkg/testutil"
) )
@ -160,6 +161,56 @@ func TestClient_OpenOrdersRequest(t *testing.T) {
t.Log(orders) t.Log(orders)
} }
func TestClient_OrderHistoryWithBeforeId(t *testing.T) {
client := getTestClientOrSkip(t)
ctx := context.Background()
orders := []OrderDetail{}
beforeId := int64(0)
for {
//>> [{"accFillSz":"0.00001","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"48174.5","cTime":"1704957916401","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.000385396","feeCcy":"USDT","fillPx":"48174.5","fillSz":"0.00001","fillTime":"1704983881118","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665576973905014786","ordType":"limit","pnl":"0","posSide":"","px":"48174.5","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"0.00001","tag":"","tdMode":"cash","tgtCcy":"","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"472610696","uTime":"1704983881135"}]
//>> [{"accFillSz":"0.00001","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"48074.5","cTime":"1704957905283","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.000384596","feeCcy":"USDT","fillPx":"48074.5","fillSz":"0.00001","fillTime":"1704983824237","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665576927272742919","ordType":"limit","pnl":"0","posSide":"","px":"48074.5","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"0.00001","tag":"","tdMode":"cash","tgtCcy":"","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"472601591","uTime":"1704983824240"}]
//>> [{"accFillSz":"0.00001","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"48073.5","cTime":"1704957892896","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.000384588","feeCcy":"USDT","fillPx":"48073.5","fillSz":"0.00001","fillTime":"1704983824227","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665576875317899302","ordType":"limit","pnl":"0","posSide":"","px":"48073.5","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"0.00001","tag":"","tdMode":"cash","tgtCcy":"","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"472601583","uTime":"1704983824230"}]
//>> [{"accFillSz":"0.00016266","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"45919.8","cTime":"1704852215160","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.00000016266","feeCcy":"BTC","fillPx":"45919.8","fillSz":"0.00016266","fillTime":"1704852215162","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665133630767091729","ordType":"market","pnl":"0","posSide":"","px":"","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"USDT","reduceOnly":"false","side":"buy","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"0.00016266","tag":"","tdMode":"cash","tgtCcy":"base_ccy","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"471113058","uTime":"1704852215163"}]
//>> [{"accFillSz":"0.00087627","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"45647.6","cTime":"1704850530651","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.00000087627","feeCcy":"BTC","fillPx":"45647.6","fillSz":"0.00087627","fillTime":"1704850530652","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665126565424254976","ordType":"market","pnl":"0","posSide":"","px":"","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"USDT","reduceOnly":"false","side":"buy","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"40","tag":"","tdMode":"cash","tgtCcy":"quote_ccy","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"471105716","uTime":"1704850530654"}]
//>> [{"accFillSz":"0.001","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"45661.3","cTime":"1704850506060","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.0456613","feeCcy":"USDT","fillPx":"45661.3","fillSz":"0.001","fillTime":"1704850506061","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665126462282125313","ordType":"market","pnl":"0","posSide":"","px":"","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"0.001","tag":"","tdMode":"cash","tgtCcy":"base_ccy","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"471105593","uTime":"1704850506062"}]
//>> [{"accFillSz":"0.00097361","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"45743","cTime":"1704849690516","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.00000097361","feeCcy":"BTC","fillPx":"45743","fillSz":"0.00097361","fillTime":"1704849690517","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665123041642663944","ordType":"market","pnl":"0","posSide":"","px":"","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"USDT","reduceOnly":"false","side":"buy","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"0.00097361","tag":"","tdMode":"cash","tgtCcy":"base_ccy","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"471100149","uTime":"1704849690519"}]
//>> [{"accFillSz":"0.00080894","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"46728.2","cTime":"1704789666800","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.037800310108","feeCcy":"USDT","fillPx":"46728.2","fillSz":"0.00080894","fillTime":"1704789666801","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"664871283930550273","ordType":"market","pnl":"0","posSide":"","px":"","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"37.8","tag":"","tdMode":"cash","tgtCcy":"quote_ccy","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"470288552","uTime":"1704789666803"}]
//>> [{"accFillSz":"0.00085423","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"46825.3","cTime":"1704789220044","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.00000085423","feeCcy":"BTC","fillPx":"46825.3","fillSz":"0.00085423","fillTime":"1704789220045","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"664869410100072448","ordType":"market","pnl":"0","posSide":"","px":"","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"USDT","reduceOnly":"false","side":"buy","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"40","tag":"","tdMode":"cash","tgtCcy":"quote_ccy","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"470287675","uTime":"1704789220046"}]
c := client.NewGetOrderHistoryRequest().InstrumentID("BTC-USDT").Limit(1).Before(fmt.Sprintf("%d", beforeId))
res, err := c.Do(ctx)
assert.NoError(t, err)
if len(res) != 1 {
break
}
orders = append(orders, res...)
beforeId = int64(res[0].OrderId)
}
t.Log(orders)
}
func TestClient_OrderHistoryByTimeRange(t *testing.T) {
client := getTestClientOrSkip(t)
ctx := context.Background()
startTime := time.Date(2023, 7, 1, 0, 0, 0, 0, time.UTC)
t.Log(time.Since(startTime))
//>> [{"accFillSz":"0.00001","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"48174.5","cTime":"1704957916401","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.000385396","feeCcy":"USDT","fillPx":"48174.5","fillSz":"0.00001","fillTime":"1704983881118","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665576973905014786","ordType":"limit","pnl":"0","posSide":"","px":"48174.5","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"0.00001","tag":"","tdMode":"cash","tgtCcy":"","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"472610696","uTime":"1704983881135"}]
//>> [{"accFillSz":"0.00001","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"48074.5","cTime":"1704957905283","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.000384596","feeCcy":"USDT","fillPx":"48074.5","fillSz":"0.00001","fillTime":"1704983824237","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665576927272742919","ordType":"limit","pnl":"0","posSide":"","px":"48074.5","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"0.00001","tag":"","tdMode":"cash","tgtCcy":"","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"472601591","uTime":"1704983824240"}]
//>> [{"accFillSz":"0.00001","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"48073.5","cTime":"1704957892896","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.000384588","feeCcy":"USDT","fillPx":"48073.5","fillSz":"0.00001","fillTime":"1704983824227","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665576875317899302","ordType":"limit","pnl":"0","posSide":"","px":"48073.5","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"0.00001","tag":"","tdMode":"cash","tgtCcy":"","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"472601583","uTime":"1704983824230"}]
//>> [{"accFillSz":"0.00016266","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"45919.8","cTime":"1704852215160","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.00000016266","feeCcy":"BTC","fillPx":"45919.8","fillSz":"0.00016266","fillTime":"1704852215162","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665133630767091729","ordType":"market","pnl":"0","posSide":"","px":"","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"USDT","reduceOnly":"false","side":"buy","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"0.00016266","tag":"","tdMode":"cash","tgtCcy":"base_ccy","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"471113058","uTime":"1704852215163"}]
//>> [{"accFillSz":"0.00087627","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"45647.6","cTime":"1704850530651","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.00000087627","feeCcy":"BTC","fillPx":"45647.6","fillSz":"0.00087627","fillTime":"1704850530652","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665126565424254976","ordType":"market","pnl":"0","posSide":"","px":"","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"USDT","reduceOnly":"false","side":"buy","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"40","tag":"","tdMode":"cash","tgtCcy":"quote_ccy","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"471105716","uTime":"1704850530654"}]
//>> [{"accFillSz":"0.001","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"45661.3","cTime":"1704850506060","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.0456613","feeCcy":"USDT","fillPx":"45661.3","fillSz":"0.001","fillTime":"1704850506061","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665126462282125313","ordType":"market","pnl":"0","posSide":"","px":"","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"0.001","tag":"","tdMode":"cash","tgtCcy":"base_ccy","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"471105593","uTime":"1704850506062"}]
//>> [{"accFillSz":"0.00097361","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"45743","cTime":"1704849690516","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.00000097361","feeCcy":"BTC","fillPx":"45743","fillSz":"0.00097361","fillTime":"1704849690517","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"665123041642663944","ordType":"market","pnl":"0","posSide":"","px":"","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"USDT","reduceOnly":"false","side":"buy","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"0.00097361","tag":"","tdMode":"cash","tgtCcy":"base_ccy","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"471100149","uTime":"1704849690519"}]
//>> [{"accFillSz":"0.00080894","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"46728.2","cTime":"1704789666800","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.037800310108","feeCcy":"USDT","fillPx":"46728.2","fillSz":"0.00080894","fillTime":"1704789666801","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"664871283930550273","ordType":"market","pnl":"0","posSide":"","px":"","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"BTC","reduceOnly":"false","side":"sell","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"37.8","tag":"","tdMode":"cash","tgtCcy":"quote_ccy","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"470288552","uTime":"1704789666803"}]
//>> [{"accFillSz":"0.00085423","algoClOrdId":"","algoId":"","attachAlgoClOrdId":"","attachAlgoOrds":[],"avgPx":"46825.3","cTime":"1704789220044","cancelSource":"","cancelSourceReason":"","category":"normal","ccy":"","clOrdId":"","fee":"-0.00000085423","feeCcy":"BTC","fillPx":"46825.3","fillSz":"0.00085423","fillTime":"1704789220045","instId":"BTC-USDT","instType":"SPOT","lever":"","ordId":"664869410100072448","ordType":"market","pnl":"0","posSide":"","px":"","pxType":"","pxUsd":"","pxVol":"","quickMgnType":"","rebate":"0","rebateCcy":"USDT","reduceOnly":"false","side":"buy","slOrdPx":"","slTriggerPx":"","slTriggerPxType":"","source":"","state":"filled","stpId":"","stpMode":"","sz":"40","tag":"","tdMode":"cash","tgtCcy":"quote_ccy","tpOrdPx":"","tpTriggerPx":"","tpTriggerPxType":"","tradeId":"470287675","uTime":"1704789220046"}]
c := client.NewGetOrderHistoryRequest().InstrumentID("BTC-USDT").Limit(100).After("665576927272742919").StartTime(types.NewMillisecondTimestampFromInt(1704789220044).Time())
res, err := c.Do(ctx)
assert.NoError(t, err)
t.Log(res)
}
func TestClient_BatchCancelOrderRequest(t *testing.T) { func TestClient_BatchCancelOrderRequest(t *testing.T) {
client := getTestClientOrSkip(t) client := getTestClientOrSkip(t)
ctx := context.Background() ctx := context.Background()

View File

@ -4,94 +4,25 @@ import (
"time" "time"
"github.com/c9s/requestgen" "github.com/c9s/requestgen"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
) )
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data //go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data //go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
type OpenOrder struct { type OpenOrder struct {
AccumulatedFillSize fixedpoint.Value `json:"accFillSz"` OrderDetail
// If none is filled, it will return "". QuickMgnType string `json:"quickMgnType"`
AvgPrice fixedpoint.Value `json:"avgPx"`
CreatedTime types.MillisecondTimestamp `json:"cTime"`
Category string `json:"category"`
ClientOrderId string `json:"clOrdId"`
Fee fixedpoint.Value `json:"fee"`
FeeCurrency string `json:"feeCcy"`
// Last filled time
FillTime types.MillisecondTimestamp `json:"fillTime"`
InstrumentID string `json:"instId"`
InstrumentType InstrumentType `json:"instType"`
OrderId types.StrInt64 `json:"ordId"`
OrderType OrderType `json:"ordType"`
Price fixedpoint.Value `json:"px"`
Side SideType `json:"side"`
State OrderState `json:"state"`
Size fixedpoint.Value `json:"sz"`
TargetCurrency string `json:"tgtCcy"`
UpdatedTime types.MillisecondTimestamp `json:"uTime"`
// Margin currency
// Only applicable to cross MARGIN orders in Single-currency margin.
Currency string `json:"ccy"`
TradeId string `json:"tradeId"`
// Last filled price
FillPrice fixedpoint.Value `json:"fillPx"`
// Last filled quantity
FillSize fixedpoint.Value `json:"fillSz"`
// Leverage, from 0.01 to 125.
// Only applicable to MARGIN/FUTURES/SWAP
Lever string `json:"lever"`
// Profit and loss, Applicable to orders which have a trade and aim to close position. It always is 0 in other conditions
Pnl fixedpoint.Value `json:"pnl"`
PositionSide string `json:"posSide"`
// Options price in USDOnly applicable to options; return "" for other instrument types
PriceUsd fixedpoint.Value `json:"pxUsd"`
// Implied volatility of the options orderOnly applicable to options; return "" for other instrument types
PriceVol fixedpoint.Value `json:"pxVol"`
// Price type of options
PriceType string `json:"pxType"`
// Rebate amount, only applicable to spot and margin, the reward of placing orders from the platform (rebate)
// given to user who has reached the specified trading level. If there is no rebate, this field is "".
Rebate fixedpoint.Value `json:"rebate"`
RebateCcy string `json:"rebateCcy"`
// Client-supplied Algo ID when placing order attaching TP/SL.
AttachAlgoClOrdId string `json:"attachAlgoClOrdId"`
SlOrdPx fixedpoint.Value `json:"slOrdPx"`
SlTriggerPx fixedpoint.Value `json:"slTriggerPx"`
SlTriggerPxType string `json:"slTriggerPxType"`
AttachAlgoOrds []interface{} `json:"attachAlgoOrds"`
Source string `json:"source"`
// Self trade prevention ID. Return "" if self trade prevention is not applicable
StpId string `json:"stpId"`
// Self trade prevention mode. Return "" if self trade prevention is not applicable
StpMode string `json:"stpMode"`
Tag string `json:"tag"`
TradeMode string `json:"tdMode"`
TpOrdPx fixedpoint.Value `json:"tpOrdPx"`
TpTriggerPx fixedpoint.Value `json:"tpTriggerPx"`
TpTriggerPxType string `json:"tpTriggerPxType"`
ReduceOnly string `json:"reduceOnly"`
QuickMgnType string `json:"quickMgnType"`
AlgoClOrdId string `json:"algoClOrdId"`
AlgoId string `json:"algoId"`
} }
//go:generate GetRequest -url "/api/v5/trade/orders-pending" -type GetOpenOrdersRequest -responseDataType []OpenOrder //go:generate GetRequest -url "/api/v5/trade/orders-pending" -type GetOpenOrdersRequest -responseDataType []OpenOrder
type GetOpenOrdersRequest struct { type GetOpenOrdersRequest struct {
client requestgen.AuthenticatedAPIClient client requestgen.AuthenticatedAPIClient
instrumentID *string `param:"instId,query"`
instrumentType InstrumentType `param:"instType,query"` instrumentType InstrumentType `param:"instType,query"`
instrumentID *string `param:"instId,query"`
orderType *OrderType `param:"ordType,query"` orderType *OrderType `param:"ordType,query"`
state *OrderState `param:"state,query"`
state *OrderState `param:"state,query"` category *string `param:"category,query"`
category *string `param:"category,query"`
// Pagination of data to return records earlier than the requested ordId // Pagination of data to return records earlier than the requested ordId
after *string `param:"after,query"` after *string `param:"after,query"`
// Pagination of data to return records newer than the requested ordId // Pagination of data to return records newer than the requested ordId

View File

@ -4,9 +4,81 @@ import (
"time" "time"
"github.com/c9s/requestgen" "github.com/c9s/requestgen"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
) )
//go:generate GetRequest -url "/api/v5/trade/orders-history-archive" -type GetOrderHistoryRequest -responseDataType .APIResponse //go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
type OrderDetail struct {
AccumulatedFillSize fixedpoint.Value `json:"accFillSz"`
// If none is filled, it will return "".
AvgPrice fixedpoint.Value `json:"avgPx"`
CreatedTime types.MillisecondTimestamp `json:"cTime"`
Category string `json:"category"`
ClientOrderId string `json:"clOrdId"`
Fee fixedpoint.Value `json:"fee"`
FeeCurrency string `json:"feeCcy"`
// Last filled time
FillTime types.MillisecondTimestamp `json:"fillTime"`
InstrumentID string `json:"instId"`
InstrumentType InstrumentType `json:"instType"`
OrderId types.StrInt64 `json:"ordId"`
OrderType OrderType `json:"ordType"`
Price fixedpoint.Value `json:"px"`
Side SideType `json:"side"`
State OrderState `json:"state"`
Size fixedpoint.Value `json:"sz"`
TargetCurrency string `json:"tgtCcy"`
UpdatedTime types.MillisecondTimestamp `json:"uTime"`
// Margin currency
// Only applicable to cross MARGIN orders in Single-currency margin.
Currency string `json:"ccy"`
TradeId string `json:"tradeId"`
// Last filled price
FillPrice fixedpoint.Value `json:"fillPx"`
// Last filled quantity
FillSize fixedpoint.Value `json:"fillSz"`
// Leverage, from 0.01 to 125.
// Only applicable to MARGIN/FUTURES/SWAP
Lever string `json:"lever"`
// Profit and loss, Applicable to orders which have a trade and aim to close position. It always is 0 in other conditions
Pnl fixedpoint.Value `json:"pnl"`
PositionSide string `json:"posSide"`
// Options price in USDOnly applicable to options; return "" for other instrument types
PriceUsd fixedpoint.Value `json:"pxUsd"`
// Implied volatility of the options orderOnly applicable to options; return "" for other instrument types
PriceVol fixedpoint.Value `json:"pxVol"`
// Price type of options
PriceType string `json:"pxType"`
// Rebate amount, only applicable to spot and margin, the reward of placing orders from the platform (rebate)
// given to user who has reached the specified trading level. If there is no rebate, this field is "".
Rebate fixedpoint.Value `json:"rebate"`
RebateCcy string `json:"rebateCcy"`
// Client-supplied Algo ID when placing order attaching TP/SL.
AttachAlgoClOrdId string `json:"attachAlgoClOrdId"`
SlOrdPx fixedpoint.Value `json:"slOrdPx"`
SlTriggerPx fixedpoint.Value `json:"slTriggerPx"`
SlTriggerPxType string `json:"slTriggerPxType"`
AttachAlgoOrds []interface{} `json:"attachAlgoOrds"`
Source string `json:"source"`
// Self trade prevention ID. Return "" if self trade prevention is not applicable
StpId string `json:"stpId"`
// Self trade prevention mode. Return "" if self trade prevention is not applicable
StpMode string `json:"stpMode"`
Tag string `json:"tag"`
TradeMode string `json:"tdMode"`
TpOrdPx fixedpoint.Value `json:"tpOrdPx"`
TpTriggerPx fixedpoint.Value `json:"tpTriggerPx"`
TpTriggerPxType string `json:"tpTriggerPxType"`
ReduceOnly string `json:"reduceOnly"`
AlgoClOrdId string `json:"algoClOrdId"`
AlgoId string `json:"algoId"`
}
//go:generate GetRequest -url "/api/v5/trade/orders-history-archive" -type GetOrderHistoryRequest -responseDataType []OrderDetail
type GetOrderHistoryRequest struct { type GetOrderHistoryRequest struct {
client requestgen.AuthenticatedAPIClient client requestgen.AuthenticatedAPIClient
@ -29,8 +101,6 @@ type GetOrderHistoryRequest struct {
limit *uint64 `param:"limit,query"` limit *uint64 `param:"limit,query"`
} }
type OrderList []OrderDetails
// NewGetOrderHistoriesRequest is descending order by createdTime // NewGetOrderHistoriesRequest is descending order by createdTime
func (c *RestClient) NewGetOrderHistoryRequest() *GetOrderHistoryRequest { func (c *RestClient) NewGetOrderHistoryRequest() *GetOrderHistoryRequest {
return &GetOrderHistoryRequest{ return &GetOrderHistoryRequest{

View File

@ -1,4 +1,4 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/v5/trade/orders-history-archive -type GetOrderHistoryRequest -responseDataType .OrderList"; DO NOT EDIT. // Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/v5/trade/orders-history-archive -type GetOrderHistoryRequest -responseDataType []OrderDetail"; DO NOT EDIT.
package okexapi package okexapi
@ -286,7 +286,13 @@ func (g *GetOrderHistoryRequest) GetSlugsMap() (map[string]string, error) {
return slugs, nil return slugs, nil
} }
func (g *GetOrderHistoryRequest) Do(ctx context.Context) (OrderList, error) { // GetPath returns the request path of the API
func (g *GetOrderHistoryRequest) GetPath() string {
return "/api/v5/trade/orders-history-archive"
}
// Do generates the request object and send the request object to the API endpoint
func (g *GetOrderHistoryRequest) Do(ctx context.Context) ([]OrderDetail, error) {
// no body params // no body params
var params interface{} var params interface{}
@ -295,7 +301,9 @@ func (g *GetOrderHistoryRequest) Do(ctx context.Context) (OrderList, error) {
return nil, err return nil, err
} }
apiURL := "/api/v5/trade/orders-history-archive" var apiURL string
apiURL = g.GetPath()
req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params) req, err := g.client.NewAuthenticatedRequest(ctx, "GET", apiURL, query, params)
if err != nil { if err != nil {
@ -311,7 +319,19 @@ func (g *GetOrderHistoryRequest) Do(ctx context.Context) (OrderList, error) {
if err := response.DecodeJSON(&apiResponse); err != nil { if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err return nil, err
} }
var data OrderList
type responseValidator interface {
Validate() error
}
validator, ok := interface{}(apiResponse).(responseValidator)
if ok {
if err := validator.Validate(); err != nil {
return nil, err
}
}
var data []OrderDetail
if err := json.Unmarshal(apiResponse.Data, &data); err != nil { if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err return nil, err
} }

View File

@ -31,6 +31,8 @@ type GetTransactionHistoryRequest struct {
limit *uint64 `param:"limit,query"` limit *uint64 `param:"limit,query"`
} }
type OrderList []OrderDetails
// NewGetOrderHistoriesRequest is descending order by createdTime // NewGetOrderHistoriesRequest is descending order by createdTime
func (c *RestClient) NewGetTransactionHistoryRequest() *GetTransactionHistoryRequest { func (c *RestClient) NewGetTransactionHistoryRequest() *GetTransactionHistoryRequest {
return &GetTransactionHistoryRequest{ return &GetTransactionHistoryRequest{