mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 08:45:16 +00:00
ftx: support queryClosedOrders
This commit is contained in:
parent
54ca62ac5c
commit
4a5a53ea28
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -156,8 +157,53 @@ func (e *Exchange) QueryOpenOrders(ctx context.Context, symbol string) (orders [
|
|||
return orders, nil
|
||||
}
|
||||
|
||||
// symbol, since and until are all optional. FTX can only query by order created time, not updated time.
|
||||
// FTX doesn't support lastOrderID, so we will query by the time range first, and filter by the lastOrderID.
|
||||
func (e *Exchange) QueryClosedOrders(ctx context.Context, symbol string, since, until time.Time, lastOrderID uint64) (orders []types.Order, err error) {
|
||||
panic("implement me")
|
||||
if since.After(until) {
|
||||
return nil, fmt.Errorf("since can't be after until")
|
||||
}
|
||||
if lastOrderID > 0 {
|
||||
logger.Warn("FTX doesn't support lastOrderID")
|
||||
}
|
||||
limit := 100
|
||||
hasMoreData := true
|
||||
s := since
|
||||
var lastOrder order
|
||||
for hasMoreData {
|
||||
resp, err := e.newRest().OrdersHistory(ctx, symbol, s, until, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !resp.Success {
|
||||
return nil, fmt.Errorf("ftx returns querying orders history failure")
|
||||
}
|
||||
|
||||
sortByCreatedASC(resp.Result)
|
||||
|
||||
for _, r := range resp.Result {
|
||||
// There may be more than one orders at the same time, so also have to check the ID
|
||||
if r.CreatedAt.Before(lastOrder.CreatedAt) || r.ID == lastOrder.ID || r.Status != "closed" {
|
||||
continue
|
||||
}
|
||||
lastOrder = r
|
||||
o, err := toGlobalOrder(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orders = append(orders, o)
|
||||
}
|
||||
hasMoreData = resp.HasMoreData
|
||||
// the start_time and end_time precision is second. There might be more than one orders within one second.
|
||||
s = lastOrder.CreatedAt.Add(-1 * time.Second)
|
||||
}
|
||||
return orders, nil
|
||||
}
|
||||
|
||||
func sortByCreatedASC(orders []order) {
|
||||
sort.Slice(orders, func(i, j int) bool {
|
||||
return orders[i].CreatedAt.Before(orders[j].CreatedAt)
|
||||
})
|
||||
}
|
||||
|
||||
func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) error {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
|
@ -59,3 +60,223 @@ func TestExchange_QueryAccountBalances(t *testing.T) {
|
|||
assert.EqualError(t, err, "ftx returns querying balances failure")
|
||||
assert.Nil(t, resp)
|
||||
}
|
||||
|
||||
func TestExchange_QueryOpenOrders(t *testing.T) {
|
||||
successResp := `
|
||||
{
|
||||
"success": true,
|
||||
"result": [
|
||||
{
|
||||
"createdAt": "2019-03-05T09:56:55.728933+00:00",
|
||||
"filledSize": 10,
|
||||
"future": "XRP-PERP",
|
||||
"id": 9596912,
|
||||
"market": "XRP-PERP",
|
||||
"price": 0.306525,
|
||||
"avgFillPrice": 0.306526,
|
||||
"remainingSize": 31421,
|
||||
"side": "sell",
|
||||
"size": 31431,
|
||||
"status": "open",
|
||||
"type": "limit",
|
||||
"reduceOnly": false,
|
||||
"ioc": false,
|
||||
"postOnly": false,
|
||||
"clientId": null
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, successResp)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
ex := NewExchange("", "", "")
|
||||
serverURL, err := url.Parse(ts.URL)
|
||||
assert.NoError(t, err)
|
||||
ex.restEndpoint = serverURL
|
||||
resp, err := ex.QueryOpenOrders(context.Background(), "XRP-PREP")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, resp, 1)
|
||||
assert.Equal(t, "XRP-PERP", resp[0].Symbol)
|
||||
}
|
||||
|
||||
func TestExchange_QueryClosedOrders(t *testing.T) {
|
||||
t.Run("no closed orders", func(t *testing.T) {
|
||||
successResp := `{"success": true, "result": []}`
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, successResp)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
ex := NewExchange("", "", "")
|
||||
serverURL, err := url.Parse(ts.URL)
|
||||
assert.NoError(t, err)
|
||||
ex.restEndpoint = serverURL
|
||||
resp, err := ex.QueryClosedOrders(context.Background(), "BTC-PERP", time.Time{}, time.Time{}, 100)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, resp, 0)
|
||||
})
|
||||
t.Run("one closed order", func(t *testing.T) {
|
||||
successResp := `
|
||||
{
|
||||
"success": true,
|
||||
"result": [
|
||||
{
|
||||
"avgFillPrice": 10135.25,
|
||||
"clientId": null,
|
||||
"createdAt": "2019-06-27T15:24:03.101197+00:00",
|
||||
"filledSize": 0.001,
|
||||
"future": "BTC-PERP",
|
||||
"id": 257132591,
|
||||
"ioc": false,
|
||||
"market": "BTC-PERP",
|
||||
"postOnly": false,
|
||||
"price": 10135.25,
|
||||
"reduceOnly": false,
|
||||
"remainingSize": 0.0,
|
||||
"side": "buy",
|
||||
"size": 0.001,
|
||||
"status": "closed",
|
||||
"type": "limit"
|
||||
}
|
||||
],
|
||||
"hasMoreData": false
|
||||
}
|
||||
`
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, successResp)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
ex := NewExchange("", "", "")
|
||||
serverURL, err := url.Parse(ts.URL)
|
||||
assert.NoError(t, err)
|
||||
ex.restEndpoint = serverURL
|
||||
resp, err := ex.QueryClosedOrders(context.Background(), "BTC-PERP", time.Time{}, time.Time{}, 100)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, resp, 1)
|
||||
assert.Equal(t, "BTC-PERP", resp[0].Symbol)
|
||||
})
|
||||
|
||||
t.Run("sort the order", func(t *testing.T) {
|
||||
successResp := `
|
||||
{
|
||||
"success": true,
|
||||
"result": [
|
||||
{
|
||||
"status": "closed",
|
||||
"createdAt": "2020-09-01T15:24:03.101197+00:00",
|
||||
"id": 789
|
||||
},
|
||||
{
|
||||
"status": "closed",
|
||||
"createdAt": "2019-03-27T15:24:03.101197+00:00",
|
||||
"id": 123
|
||||
},
|
||||
{
|
||||
"status": "closed",
|
||||
"createdAt": "2019-06-27T15:24:03.101197+00:00",
|
||||
"id": 456
|
||||
},
|
||||
{
|
||||
"status": "new",
|
||||
"createdAt": "2019-06-27T15:24:03.101197+00:00",
|
||||
"id": 999
|
||||
}
|
||||
],
|
||||
"hasMoreData": false
|
||||
}
|
||||
`
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, successResp)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
ex := NewExchange("", "", "")
|
||||
serverURL, err := url.Parse(ts.URL)
|
||||
assert.NoError(t, err)
|
||||
ex.restEndpoint = serverURL
|
||||
resp, err := ex.QueryClosedOrders(context.Background(), "BTC-PERP", time.Time{}, time.Time{}, 100)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, resp, 3)
|
||||
|
||||
expectedOrderID := []uint64{123, 456, 789}
|
||||
for i, o := range resp {
|
||||
assert.Equal(t, expectedOrderID[i], o.OrderID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("receive duplicated orders", func(t *testing.T) {
|
||||
successRespOne := `
|
||||
{
|
||||
"success": true,
|
||||
"result": [
|
||||
{
|
||||
"status": "closed",
|
||||
"createdAt": "2020-09-01T15:24:03.101197+00:00",
|
||||
"id": 123
|
||||
}
|
||||
],
|
||||
"hasMoreData": true
|
||||
}
|
||||
`
|
||||
|
||||
successRespTwo := `
|
||||
{
|
||||
"success": true,
|
||||
"result": [
|
||||
{
|
||||
"clientId": "ignored-by-created-at",
|
||||
"status": "closed",
|
||||
"createdAt": "1999-09-01T15:24:03.101197+00:00",
|
||||
"id": 999
|
||||
},
|
||||
{
|
||||
"clientId": "ignored-by-duplicated-id",
|
||||
"status": "closed",
|
||||
"createdAt": "2020-09-02T15:24:03.101197+00:00",
|
||||
"id": 123
|
||||
},
|
||||
{
|
||||
"clientId": "ignored-duplicated-entry",
|
||||
"status": "closed",
|
||||
"createdAt": "2020-09-01T15:24:03.101197+00:00",
|
||||
"id": 123
|
||||
},
|
||||
{
|
||||
"status": "closed",
|
||||
"createdAt": "2020-09-02T15:24:03.101197+00:00",
|
||||
"id": 456
|
||||
}
|
||||
],
|
||||
"hasMoreData": false
|
||||
}
|
||||
`
|
||||
i := 0
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if i == 0 {
|
||||
i++
|
||||
fmt.Fprintln(w, successRespOne)
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(w, successRespTwo)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
ex := NewExchange("", "", "")
|
||||
serverURL, err := url.Parse(ts.URL)
|
||||
assert.NoError(t, err)
|
||||
ex.restEndpoint = serverURL
|
||||
resp, err := ex.QueryClosedOrders(context.Background(), "BTC-PERP", time.Time{}, time.Time{}, 100)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, resp, 2)
|
||||
expectedOrderID := []uint64{123, 456}
|
||||
for i, o := range resp {
|
||||
assert.Equal(t, expectedOrderID[i], o.OrderID)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -117,11 +117,14 @@ func (r *orderRequest) OpenOrders(ctx context.Context, market string) (ordersRes
|
|||
}
|
||||
|
||||
func (r *orderRequest) OrdersHistory(ctx context.Context, market string, start, end time.Time, limit int) (ordersHistoryResponse, error) {
|
||||
p := map[string]interface{}{
|
||||
"market": market,
|
||||
"limit": limit,
|
||||
}
|
||||
p := make(map[string]interface{})
|
||||
|
||||
if limit > 0 {
|
||||
p["limit"] = limit
|
||||
}
|
||||
if len(market) > 0 {
|
||||
p["market"] = market
|
||||
}
|
||||
if start != (time.Time{}) {
|
||||
p["start_time"] = start.UnixNano() / int64(time.Second)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user