mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-07 23:16:53 +00:00
exchanges: Initial context propagation (#744)
* gct: phase one context awareness pass * exchanges: context propagation pass * common/requester: force context requirement * gctcli/exchanges: linter fix * rpcserver: fix test using dummy rpc server * backtester: fix comments * grpc: add correct cancel and timeout for commands * rpcserver_test: add comment on dummy server * common: deprecated SendHTTPGetRequest * linter: fix * linter: turn on no context check * apichecker: fix context linter issue * binance: use param context * common: remove checks as this gets executed before main * common: change mutex to RW as clients can be used by multiple go routines. * common: remove init and JIT default client. Unexport global variables and add protection. * common: Add comments * bithumb: after dinner mints fix
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package backtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
@@ -245,7 +246,7 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange
|
||||
}
|
||||
if makerFee == 0 || takerFee == 0 {
|
||||
var apiMakerFee, apiTakerFee float64
|
||||
apiMakerFee, apiTakerFee = getFees(exch, pair)
|
||||
apiMakerFee, apiTakerFee = getFees(context.TODO(), exch, pair)
|
||||
if makerFee == 0 {
|
||||
makerFee = apiMakerFee
|
||||
}
|
||||
@@ -400,26 +401,28 @@ func (bt *BackTest) setupBot(cfg *config.Config, bot *engine.Engine) error {
|
||||
}
|
||||
|
||||
// getFees will return an exchange's fee rate from GCT's wrapper function
|
||||
func getFees(exch gctexchange.IBotExchange, fPair currency.Pair) (makerFee, takerFee float64) {
|
||||
func getFees(ctx context.Context, exch gctexchange.IBotExchange, fPair currency.Pair) (makerFee, takerFee float64) {
|
||||
var err error
|
||||
takerFee, err = exch.GetFeeByType(&gctexchange.FeeBuilder{
|
||||
FeeType: gctexchange.OfflineTradeFee,
|
||||
Pair: fPair,
|
||||
IsMaker: false,
|
||||
PurchasePrice: 1,
|
||||
Amount: 1,
|
||||
})
|
||||
takerFee, err = exch.GetFeeByType(ctx,
|
||||
&gctexchange.FeeBuilder{
|
||||
FeeType: gctexchange.OfflineTradeFee,
|
||||
Pair: fPair,
|
||||
IsMaker: false,
|
||||
PurchasePrice: 1,
|
||||
Amount: 1,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf(log.BackTester, "Could not retrieve taker fee for %v. %v", exch.GetName(), err)
|
||||
}
|
||||
|
||||
makerFee, err = exch.GetFeeByType(&gctexchange.FeeBuilder{
|
||||
FeeType: gctexchange.OfflineTradeFee,
|
||||
Pair: fPair,
|
||||
IsMaker: true,
|
||||
PurchasePrice: 1,
|
||||
Amount: 1,
|
||||
})
|
||||
makerFee, err = exch.GetFeeByType(ctx,
|
||||
&gctexchange.FeeBuilder{
|
||||
FeeType: gctexchange.OfflineTradeFee,
|
||||
Pair: fPair,
|
||||
IsMaker: true,
|
||||
PurchasePrice: 1,
|
||||
Amount: 1,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf(log.BackTester, "Could not retrieve maker fee for %v. %v", exch.GetName(), err)
|
||||
}
|
||||
@@ -621,7 +624,7 @@ func loadAPIData(cfg *config.Config, exch gctexchange.IBotExchange, fPair curren
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
candles, err := api.LoadData(
|
||||
candles, err := api.LoadData(context.TODO(),
|
||||
dataType,
|
||||
cfg.DataSettings.APIData.StartDate,
|
||||
cfg.DataSettings.APIData.EndDate,
|
||||
@@ -942,7 +945,7 @@ func (bt *BackTest) RunLive() error {
|
||||
// from live. Its purpose is to be able to perform strategy analysis against current data
|
||||
func (bt *BackTest) loadLiveDataLoop(resp *kline.DataFromKline, cfg *config.Config, exch gctexchange.IBotExchange, fPair currency.Pair, a asset.Item, dataType int64) {
|
||||
startDate := time.Now()
|
||||
candles, err := live.LoadData(
|
||||
candles, err := live.LoadData(context.TODO(),
|
||||
exch,
|
||||
dataType,
|
||||
cfg.DataSettings.Interval,
|
||||
@@ -981,7 +984,7 @@ func (bt *BackTest) loadLiveData(resp *kline.DataFromKline, cfg *config.Config,
|
||||
if exch == nil {
|
||||
return errNilExchange
|
||||
}
|
||||
candles, err := live.LoadData(
|
||||
candles, err := live.LoadData(context.TODO(),
|
||||
exch,
|
||||
dataType,
|
||||
cfg.DataSettings.Interval,
|
||||
@@ -992,7 +995,7 @@ func (bt *BackTest) loadLiveData(resp *kline.DataFromKline, cfg *config.Config,
|
||||
}
|
||||
|
||||
resp.Item.Candles = append(resp.Item.Candles, candles.Candles...)
|
||||
_, err = exch.FetchOrderbook(fPair, a)
|
||||
_, err = exch.FetchOrderbook(context.TODO(), fPair, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -14,12 +15,12 @@ import (
|
||||
)
|
||||
|
||||
// LoadData retrieves data from a GoCryptoTrader exchange wrapper which calls the exchange's API
|
||||
func LoadData(dataType int64, startDate, endDate time.Time, interval time.Duration, exch exchange.IBotExchange, fPair currency.Pair, a asset.Item) (*kline.Item, error) {
|
||||
func LoadData(ctx context.Context, dataType int64, startDate, endDate time.Time, interval time.Duration, exch exchange.IBotExchange, fPair currency.Pair, a asset.Item) (*kline.Item, error) {
|
||||
var candles kline.Item
|
||||
var err error
|
||||
switch dataType {
|
||||
case common.DataCandle:
|
||||
candles, err = exch.GetHistoricCandlesExtended(
|
||||
candles, err = exch.GetHistoricCandlesExtended(ctx,
|
||||
fPair,
|
||||
a,
|
||||
startDate,
|
||||
@@ -30,7 +31,7 @@ func LoadData(dataType int64, startDate, endDate time.Time, interval time.Durati
|
||||
}
|
||||
case common.DataTrade:
|
||||
var trades []trade.Data
|
||||
trades, err = exch.GetHistoricTrades(
|
||||
trades, err = exch.GetHistoricTrades(ctx,
|
||||
fPair,
|
||||
a,
|
||||
startDate,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -37,7 +38,8 @@ func TestLoadCandles(t *testing.T) {
|
||||
interval := gctkline.OneMin
|
||||
a := asset.Spot
|
||||
var data *gctkline.Item
|
||||
data, err = LoadData(common.DataCandle, tt1, tt2, interval.Duration(), exch, cp, a)
|
||||
data, err = LoadData(context.Background(),
|
||||
common.DataCandle, tt1, tt2, interval.Duration(), exch, cp, a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -45,7 +47,8 @@ func TestLoadCandles(t *testing.T) {
|
||||
t.Error("expected candles")
|
||||
}
|
||||
|
||||
_, err = LoadData(-1, tt1, tt2, interval.Duration(), exch, cp, a)
|
||||
_, err = LoadData(context.Background(),
|
||||
-1, tt1, tt2, interval.Duration(), exch, cp, a)
|
||||
if !errors.Is(err, common.ErrInvalidDataType) {
|
||||
t.Errorf("expected '%v' received '%v'", err, common.ErrInvalidDataType)
|
||||
}
|
||||
@@ -73,7 +76,8 @@ func TestLoadTrades(t *testing.T) {
|
||||
tt2 := time.Now().Round(interval.Duration())
|
||||
a := asset.Spot
|
||||
var data *gctkline.Item
|
||||
data, err = LoadData(common.DataTrade, tt1, tt2, interval.Duration(), exch, cp, a)
|
||||
data, err = LoadData(context.Background(),
|
||||
common.DataTrade, tt1, tt2, interval.Duration(), exch, cp, a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package live
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -14,12 +15,12 @@ import (
|
||||
)
|
||||
|
||||
// LoadData retrieves data from a GoCryptoTrader exchange wrapper which calls the exchange's API for the latest interval
|
||||
func LoadData(exch exchange.IBotExchange, dataType int64, interval time.Duration, fPair currency.Pair, a asset.Item) (*kline.Item, error) {
|
||||
func LoadData(ctx context.Context, exch exchange.IBotExchange, dataType int64, interval time.Duration, fPair currency.Pair, a asset.Item) (*kline.Item, error) {
|
||||
var candles kline.Item
|
||||
var err error
|
||||
switch dataType {
|
||||
case common.DataCandle:
|
||||
candles, err = exch.GetHistoricCandles(
|
||||
candles, err = exch.GetHistoricCandles(ctx,
|
||||
fPair,
|
||||
a,
|
||||
time.Now().Add(-interval*2), // multiplied by 2 to ensure the latest candle is always included
|
||||
@@ -30,7 +31,11 @@ func LoadData(exch exchange.IBotExchange, dataType int64, interval time.Duration
|
||||
}
|
||||
case common.DataTrade:
|
||||
var trades []trade.Data
|
||||
trades, err = exch.GetHistoricTrades(fPair, a, time.Now().Add(-interval*2), time.Now()) // multiplied by 2 to ensure the latest candle is always included
|
||||
trades, err = exch.GetHistoricTrades(ctx,
|
||||
fPair,
|
||||
a,
|
||||
time.Now().Add(-interval*2), // multiplied by 2 to ensure the latest candle is always included
|
||||
time.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -41,10 +46,10 @@ func LoadData(exch exchange.IBotExchange, dataType int64, interval time.Duration
|
||||
}
|
||||
base := exch.GetBase()
|
||||
if len(candles.Candles) <= 1 && base.GetSupportedFeatures().RESTCapabilities.TradeHistory {
|
||||
trades, err = exch.GetHistoricTrades(
|
||||
trades, err = exch.GetHistoricTrades(ctx,
|
||||
fPair,
|
||||
a,
|
||||
time.Now().Add(-interval), // multiplied by 2 to ensure the latest candle is always included
|
||||
time.Now().Add(-interval),
|
||||
time.Now())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not retrieve live trade data for %v %v %v, %v", exch.GetName(), a, fPair, err)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package live
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
@@ -36,14 +37,15 @@ func TestLoadCandles(t *testing.T) {
|
||||
ConfigFormat: pFormat,
|
||||
}
|
||||
var data *gctkline.Item
|
||||
data, err = LoadData(exch, common.DataCandle, interval.Duration(), cp1, a)
|
||||
data, err = LoadData(context.Background(),
|
||||
exch, common.DataCandle, interval.Duration(), cp1, a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(data.Candles) == 0 {
|
||||
t.Error("expected candles")
|
||||
}
|
||||
_, err = LoadData(exch, -1, interval.Duration(), cp1, a)
|
||||
_, err = LoadData(context.Background(), exch, -1, interval.Duration(), cp1, a)
|
||||
if !errors.Is(err, common.ErrInvalidDataType) {
|
||||
t.Errorf("expected '%v' received '%v'", err, common.ErrInvalidDataType)
|
||||
}
|
||||
@@ -71,7 +73,7 @@ func TestLoadTrades(t *testing.T) {
|
||||
ConfigFormat: pFormat,
|
||||
}
|
||||
var data *gctkline.Item
|
||||
data, err = LoadData(exch, common.DataTrade, interval.Duration(), cp1, a)
|
||||
data, err = LoadData(context.Background(), exch, common.DataTrade, interval.Duration(), cp1, a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package exchange
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
@@ -108,7 +109,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, bot *engine.En
|
||||
return f, err
|
||||
}
|
||||
|
||||
orderID, err := e.placeOrder(adjustedPrice, limitReducedAmount, cs.UseRealOrders, cs.CanUseExchangeLimits, f, bot)
|
||||
orderID, err := e.placeOrder(context.TODO(), adjustedPrice, limitReducedAmount, cs.UseRealOrders, cs.CanUseExchangeLimits, f, bot)
|
||||
if err != nil {
|
||||
if f.GetDirection() == gctorder.Buy {
|
||||
f.SetDirection(common.CouldNotBuy)
|
||||
@@ -198,7 +199,7 @@ func reduceAmountToFitPortfolioLimit(adjustedPrice, amount, sizedPortfolioTotal
|
||||
return amount
|
||||
}
|
||||
|
||||
func (e *Exchange) placeOrder(price, amount float64, useRealOrders, useExchangeLimits bool, f *fill.Fill, bot *engine.Engine) (string, error) {
|
||||
func (e *Exchange) placeOrder(ctx context.Context, price, amount float64, useRealOrders, useExchangeLimits bool, f *fill.Fill, bot *engine.Engine) (string, error) {
|
||||
if f == nil {
|
||||
return "", common.ErrNilEvent
|
||||
}
|
||||
@@ -222,7 +223,7 @@ func (e *Exchange) placeOrder(price, amount float64, useRealOrders, useExchangeL
|
||||
}
|
||||
|
||||
if useRealOrders {
|
||||
resp, err := bot.OrderManager.Submit(o)
|
||||
resp, err := bot.OrderManager.Submit(ctx, o)
|
||||
if resp != nil {
|
||||
orderID = resp.OrderID
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package exchange
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -151,30 +152,30 @@ func TestPlaceOrder(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
e := Exchange{}
|
||||
_, err = e.placeOrder(1, 1, false, true, nil, nil)
|
||||
_, err = e.placeOrder(context.Background(), 1, 1, false, true, nil, nil)
|
||||
if !errors.Is(err, common.ErrNilEvent) {
|
||||
t.Errorf("expected: %v, received %v", common.ErrNilEvent, err)
|
||||
}
|
||||
f := &fill.Fill{}
|
||||
_, err = e.placeOrder(1, 1, false, true, f, bot)
|
||||
_, err = e.placeOrder(context.Background(), 1, 1, false, true, f, bot)
|
||||
if err != nil && err.Error() != "order exchange name must be specified" {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
f.Exchange = testExchange
|
||||
_, err = e.placeOrder(1, 1, false, true, f, bot)
|
||||
_, err = e.placeOrder(context.Background(), 1, 1, false, true, f, bot)
|
||||
if !errors.Is(err, gctorder.ErrPairIsEmpty) {
|
||||
t.Errorf("expected: %v, received %v", gctorder.ErrPairIsEmpty, err)
|
||||
}
|
||||
f.CurrencyPair = currency.NewPair(currency.BTC, currency.USDT)
|
||||
f.AssetType = asset.Spot
|
||||
f.Direction = gctorder.Buy
|
||||
_, err = e.placeOrder(1, 1, false, true, f, bot)
|
||||
_, err = e.placeOrder(context.Background(), 1, 1, false, true, f, bot)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = e.placeOrder(1, 1, true, true, f, bot)
|
||||
_, err = e.placeOrder(context.Background(), 1, 1, true, true, f, bot)
|
||||
if err != nil && !strings.Contains(err.Error(), "unset/default API keys") {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -203,7 +204,7 @@ func TestExecuteOrder(t *testing.T) {
|
||||
|
||||
p := currency.NewPair(currency.BTC, currency.USDT)
|
||||
a := asset.Spot
|
||||
_, err = exch.FetchOrderbook(p, a)
|
||||
_, err = exch.FetchOrderbook(context.Background(), p, a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -298,12 +299,12 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) {
|
||||
}
|
||||
p := currency.NewPair(currency.BTC, currency.USDT)
|
||||
a := asset.Spot
|
||||
_, err = exch.FetchOrderbook(p, a)
|
||||
_, err = exch.FetchOrderbook(context.Background(), p, a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = exch.UpdateOrderExecutionLimits(asset.Spot)
|
||||
err = exch.UpdateOrderExecutionLimits(context.Background(), asset.Spot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package slippage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
@@ -22,7 +23,7 @@ func TestCalculateSlippageByOrderbook(t *testing.T) {
|
||||
b := bitstamp.Bitstamp{}
|
||||
b.SetDefaults()
|
||||
cp := currency.NewPair(currency.BTC, currency.USD)
|
||||
ob, err := b.FetchOrderbook(cp, asset.Spot)
|
||||
ob, err := b.FetchOrderbook(context.Background(), cp, asset.Spot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user