Files
gocryptotrader/exchanges/order/limits_test.go
Ryan O'Hara-Reid 881bab2d5a Exchanges: Add in exchange defined tolerance settings (#647)
* Exchanges: Add in exchange defined tolerance settings to conform to min max amounts/price/notional etc (Initial)

* Add to tests fix linter

* Binance: Implement CMF and usdtMarginFutures fetching of currency information, addr nits

* binance: Add in test for tolerance set up

* exchanges: add in more tolerance settings and add tests

* nits: addr

* fix linter issue

* RPCServer: Use ordermanager instead of going direct to exchange

* Nits: Addr

* nits: glorious addr phase one

* nits: glorious nits phase 2

* exchange: move tolerance -> limits in order package add wrapper function, split binance functions to asset files

* nits: Addr thrasher + also include locking of limits struct when we update via syncer later on

* nits: mdc addr

* nits: glorious nits

* limits: unexport mutex

* limit: revert maths optim. and fix spelling

* limit: Add decimal package

* limit: don't check price on market order

* Orders: Add order execution checks on fake orders so as to always conform to tight specifications even in simulation

* binance: handle case where spot is not enabled but margin is

* backtester: add in amount conforming to back tested events to simulate realistic orders

* rm ln

* order limit: return amount when limit is nil and conformToAmount is requested

* nits: glorious nits + friends

* backtester/orders: fix tests

* nits: glorious nits

* nits: glorious nits

* RMLINE

* nits: more glorious nits!

* nits: pooosh

* binance: fix margin logic

* nits: Add warning, settings log and report item for exchange order execution limits

* backtester: add specific warnings in report output

* backtest: Adjust warnings
2021-03-25 15:47:15 +11:00

320 lines
8.1 KiB
Go

package order
import (
"errors"
"testing"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
)
var btcusd = currency.NewPair(currency.BTC, currency.USD)
var ltcusd = currency.NewPair(currency.LTC, currency.USD)
var btcltc = currency.NewPair(currency.BTC, currency.LTC)
func TestLoadLimits(t *testing.T) {
t.Parallel()
e := ExecutionLimits{}
err := e.LoadLimits(nil)
if !errors.Is(err, errCannotLoadLimit) {
t.Fatalf("expected error %v but received %v", errCannotLoadLimit, err)
}
newLimits := []MinMaxLevel{
{
Pair: btcusd,
Asset: asset.Spot,
MinPrice: 100000,
MaxPrice: 1000000,
MinAmount: 1,
MaxAmount: 10,
},
}
err = e.LoadLimits(newLimits)
if !errors.Is(err, nil) {
t.Fatalf("expected error %v but received %v", nil, err)
}
badLimit := []MinMaxLevel{
{
Pair: btcusd,
Asset: asset.Spot,
MinPrice: 2,
MaxPrice: 1,
MinAmount: 1,
MaxAmount: 10,
},
}
err = e.LoadLimits(badLimit)
if !errors.Is(err, errInvalidPriceLevels) {
t.Fatalf("expected error %v but received %v", errInvalidPriceLevels, err)
}
badLimit = []MinMaxLevel{
{
Pair: btcusd,
Asset: asset.Spot,
MinPrice: 1,
MaxPrice: 2,
MinAmount: 10,
MaxAmount: 9,
},
}
err = e.LoadLimits(badLimit)
if !errors.Is(err, errInvalidAmountLevels) {
t.Fatalf("expected error %v but received %v", errInvalidPriceLevels, err)
}
goodLimit := []MinMaxLevel{
{
Pair: btcusd,
Asset: asset.Spot,
},
}
err = e.LoadLimits(goodLimit)
if !errors.Is(err, nil) {
t.Fatalf("expected error %v but received %v", nil, err)
}
}
func TestGetOrderExecutionLimits(t *testing.T) {
t.Parallel()
e := ExecutionLimits{}
_, err := e.GetOrderExecutionLimits(asset.Spot, btcusd)
if !errors.Is(err, ErrExchangeLimitNotLoaded) {
t.Fatalf("expected error %v but received %v", ErrExchangeLimitNotLoaded, err)
}
newLimits := []MinMaxLevel{
{
Pair: btcusd,
Asset: asset.Spot,
MinPrice: 100000,
MaxPrice: 1000000,
MinAmount: 1,
MaxAmount: 10,
},
}
err = e.LoadLimits(newLimits)
if !errors.Is(err, nil) {
t.Fatalf("expected error %v but received %v", errCannotLoadLimit, err)
}
_, err = e.GetOrderExecutionLimits(asset.Futures, ltcusd)
if !errors.Is(err, errExchangeLimitAsset) {
t.Fatalf("expected error %v but received %v", errExchangeLimitAsset, err)
}
_, err = e.GetOrderExecutionLimits(asset.Spot, ltcusd)
if !errors.Is(err, errExchangeLimitBase) {
t.Fatalf("expected error %v but received %v", errExchangeLimitBase, err)
}
_, err = e.GetOrderExecutionLimits(asset.Spot, btcltc)
if !errors.Is(err, errExchangeLimitQuote) {
t.Fatalf("expected error %v but received %v", errExchangeLimitQuote, err)
}
tt, err := e.GetOrderExecutionLimits(asset.Spot, btcusd)
if !errors.Is(err, nil) {
t.Fatalf("expected error %v but received %v", nil, err)
}
if tt.maxAmount != newLimits[0].MaxAmount ||
tt.minAmount != newLimits[0].MinAmount ||
tt.maxPrice != newLimits[0].MaxPrice ||
tt.minPrice != newLimits[0].MinPrice {
t.Fatal("unexpected values")
}
}
func TestCheckLimit(t *testing.T) {
t.Parallel()
e := ExecutionLimits{}
err := e.CheckOrderExecutionLimits(asset.Spot, btcusd, 1337, 1337, Limit)
if !errors.Is(err, nil) {
t.Fatalf("expected error %v but received %v", nil, err)
}
newLimits := []MinMaxLevel{
{
Pair: btcusd,
Asset: asset.Spot,
MinPrice: 100000,
MaxPrice: 1000000,
MinAmount: 1,
MaxAmount: 10,
},
}
err = e.LoadLimits(newLimits)
if !errors.Is(err, nil) {
t.Fatalf("expected error %v but received %v", errCannotLoadLimit, err)
}
err = e.CheckOrderExecutionLimits(asset.Futures, ltcusd, 1337, 1337, Limit)
if !errors.Is(err, errCannotValidateAsset) {
t.Fatalf("expected error %v but received %v", errCannotValidateAsset, err)
}
err = e.CheckOrderExecutionLimits(asset.Spot, ltcusd, 1337, 1337, Limit)
if !errors.Is(err, errCannotValidateBaseCurrency) {
t.Fatalf("expected error %v but received %v", errCannotValidateBaseCurrency, err)
}
err = e.CheckOrderExecutionLimits(asset.Spot, btcltc, 1337, 1337, Limit)
if !errors.Is(err, errCannotValidateQuoteCurrency) {
t.Fatalf("expected error %v but received %v", errCannotValidateQuoteCurrency, err)
}
err = e.CheckOrderExecutionLimits(asset.Spot, btcusd, 1337, 9, Limit)
if !errors.Is(err, ErrPriceBelowMin) {
t.Fatalf("expected error %v but received %v", ErrPriceBelowMin, err)
}
err = e.CheckOrderExecutionLimits(asset.Spot, btcusd, 1000001, 9, Limit)
if !errors.Is(err, ErrPriceExceedsMax) {
t.Fatalf("expected error %v but received %v", ErrPriceExceedsMax, err)
}
err = e.CheckOrderExecutionLimits(asset.Spot, btcusd, 999999, .5, Limit)
if !errors.Is(err, ErrAmountBelowMin) {
t.Fatalf("expected error %v but received %v", ErrAmountBelowMin, err)
}
err = e.CheckOrderExecutionLimits(asset.Spot, btcusd, 999999, 11, Limit)
if !errors.Is(err, ErrAmountExceedsMax) {
t.Fatalf("expected error %v but received %v", ErrAmountExceedsMax, err)
}
err = e.CheckOrderExecutionLimits(asset.Spot, btcusd, 999999, 7, Limit)
if !errors.Is(err, nil) {
t.Fatalf("expected error %v but received %v", nil, err)
}
err = e.CheckOrderExecutionLimits(asset.Spot, btcusd, 999999, 7, Market)
if !errors.Is(err, nil) {
t.Fatalf("expected error %v but received %v", nil, err)
}
}
func TestConforms(t *testing.T) {
t.Parallel()
var tt *Limits
err := tt.Conforms(0, 0, Limit)
if err != nil {
t.Fatal(err)
}
tt = &Limits{
minNotional: 100,
}
err = tt.Conforms(1, 1, Limit)
if !errors.Is(err, ErrNotionalValue) {
t.Fatalf("expected error %v but received %v", ErrNotionalValue, err)
}
err = tt.Conforms(200, .5, Limit)
if !errors.Is(err, nil) {
t.Fatalf("expected error %v but received %v", nil, err)
}
tt.stepIncrementSizePrice = 0.001
err = tt.Conforms(200.0001, .5, Limit)
if !errors.Is(err, ErrPriceExceedsStep) {
t.Fatalf("expected error %v but received %v", ErrPriceExceedsStep, err)
}
err = tt.Conforms(200.004, .5, Limit)
if !errors.Is(err, nil) {
t.Fatalf("expected error %v but received %v", nil, err)
}
tt.stepIncrementSizeAmount = 0.001
err = tt.Conforms(200, .0002, Limit)
if !errors.Is(err, ErrAmountExceedsStep) {
t.Fatalf("expected error %v but received %v", ErrAmountExceedsStep, err)
}
err = tt.Conforms(200000, .003, Limit)
if !errors.Is(err, nil) {
t.Fatalf("expected error %v but received %v", nil, err)
}
tt.minAmount = 1
tt.maxAmount = 10
tt.marketMinQty = 1.1
tt.marketMaxQty = 9.9
err = tt.Conforms(200000, 1, Market)
if !errors.Is(err, ErrMarketAmountBelowMin) {
t.Fatalf("expected error %v but received: %v", ErrMarketAmountBelowMin, err)
}
err = tt.Conforms(200000, 10, Market)
if !errors.Is(err, ErrMarketAmountExceedsMax) {
t.Fatalf("expected error %v but received: %v", ErrMarketAmountExceedsMax, err)
}
tt.marketStepIncrementSize = 10
err = tt.Conforms(200000, 9.1, Market)
if !errors.Is(err, ErrMarketAmountExceedsStep) {
t.Fatalf("expected error %v but received: %v", ErrMarketAmountExceedsStep, err)
}
tt.marketStepIncrementSize = 1
err = tt.Conforms(200000, 9.1, Market)
if !errors.Is(err, nil) {
t.Fatalf("expected error %v but received: %v", nil, err)
}
}
func TestConformToAmount(t *testing.T) {
t.Parallel()
var tt *Limits
if tt.ConformToAmount(1.001) != 1.001 {
t.Fatal("value should not be changed")
}
tt = &Limits{}
val := tt.ConformToAmount(1)
if val != 1 { // If there is no step amount set this should not change
// the inputted amount
t.Fatal("unexpected amount")
}
tt.stepIncrementSizeAmount = 0.001
val = tt.ConformToAmount(1.001)
if val != 1.001 {
t.Error("unexpected amount", val)
}
val = tt.ConformToAmount(0.0001)
if val != 0 {
t.Error("unexpected amount", val)
}
val = tt.ConformToAmount(0.7777)
if val != 0.777 {
t.Error("unexpected amount", val)
}
tt.stepIncrementSizeAmount = 100
val = tt.ConformToAmount(100)
if val != 100 {
t.Fatal("unexpected amount", val)
}
val = tt.ConformToAmount(200)
if val != 200 {
t.Fatal("unexpected amount", val)
}
val = tt.ConformToAmount(150)
if val != 100 {
t.Fatal("unexpected amount", val)
}
}