Skip to content

Commit

Permalink
exchanges: Initial context propagation (thrasher-corp#744)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
shazbert authored Sep 11, 2021
1 parent 72516f7 commit d636049
Show file tree
Hide file tree
Showing 168 changed files with 8,073 additions and 6,984 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ linters:
- nakedret
# - nestif
# - nlreturn
# - noctx
- noctx
- nolintlint
# - prealloc
- rowserrcheck
Expand Down
43 changes: 23 additions & 20 deletions backtester/backtest/backtest.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package backtest

import (
"context"
"errors"
"fmt"
"path/filepath"
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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
}
Expand Down
7 changes: 4 additions & 3 deletions backtester/data/kline/api/api.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"context"
"fmt"
"strings"
"time"
Expand All @@ -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,
Expand All @@ -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,
Expand Down
10 changes: 7 additions & 3 deletions backtester/data/kline/api/api_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"context"
"errors"
"testing"
"time"
Expand Down Expand Up @@ -37,15 +38,17 @@ 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)
}
if len(data.Candles) == 0 {
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)
}
Expand Down Expand Up @@ -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)
}
Expand Down
15 changes: 10 additions & 5 deletions backtester/data/kline/live/live.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package live

import (
"context"
"fmt"
"strings"
"time"
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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)
Expand Down
8 changes: 5 additions & 3 deletions backtester/data/kline/live/live_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package live

import (
"context"
"errors"
"testing"

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down
7 changes: 4 additions & 3 deletions backtester/eventhandlers/exchange/exchange.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package exchange

import (
"context"
"fmt"

"github.com/gofrs/uuid"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
17 changes: 9 additions & 8 deletions backtester/eventhandlers/exchange/exchange_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package exchange

import (
"context"
"errors"
"strings"
"testing"
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down
3 changes: 2 additions & 1 deletion backtester/eventhandlers/exchange/slippage/slippage_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package slippage

import (
"context"
"testing"

"github.com/thrasher-corp/gocryptotrader/currency"
Expand All @@ -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)
}
Expand Down
Loading

0 comments on commit d636049

Please sign in to comment.