mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-17 15:09:59 +00:00
ftx: add basic exchange order execution limits for lower bound (#725)
* ftx: add basic exchange order execution limits for lower bound * ftx/binance: conform to standard logger output * ftx: fix field from SizeIncrement -> MinProvideSize * limits: fix whoopsie * limit: add tests * ftx: fix comment
This commit is contained in:
@@ -321,7 +321,7 @@ func (b *Binance) Run() {
|
||||
err = b.UpdateOrderExecutionLimits(a[x])
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys,
|
||||
"Could not set %s exchange exchange limits: %v",
|
||||
"%s failed to set exchange order execution limits. Err: %v",
|
||||
b.Name,
|
||||
err)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
)
|
||||
@@ -1378,3 +1379,46 @@ func (f *FTX) SubaccountTransfer(coin currency.Code, source, destination string,
|
||||
}
|
||||
return &resp.Data, nil
|
||||
}
|
||||
|
||||
// FetchExchangeLimits fetches spot order execution limits
|
||||
func (f *FTX) FetchExchangeLimits() ([]order.MinMaxLevel, error) {
|
||||
data, err := f.GetMarkets()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var limits []order.MinMaxLevel
|
||||
for x := range data {
|
||||
if !data[x].Enabled {
|
||||
continue
|
||||
}
|
||||
var cp currency.Pair
|
||||
var a asset.Item
|
||||
switch data[x].MarketType {
|
||||
case "future":
|
||||
a = asset.Futures
|
||||
cp, err = currency.NewPairFromString(data[x].Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "spot":
|
||||
a = asset.Spot
|
||||
cp, err = currency.NewPairFromStrings(data[x].BaseCurrency, data[x].QuoteCurrency)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled data type %s, cannot process exchange limit",
|
||||
data[x].MarketType)
|
||||
}
|
||||
|
||||
limits = append(limits, order.MinMaxLevel{
|
||||
Pair: cp,
|
||||
Asset: a,
|
||||
StepPrice: data[x].PriceIncrement,
|
||||
StepAmount: data[x].SizeIncrement,
|
||||
MinAmount: data[x].MinProvideSize,
|
||||
})
|
||||
}
|
||||
return limits, nil
|
||||
}
|
||||
|
||||
@@ -1679,3 +1679,29 @@ func TestStakeRequest(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateOrderExecutionLimits(t *testing.T) {
|
||||
err := f.UpdateOrderExecutionLimits("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cp := currency.NewPair(currency.BTC, currency.USD)
|
||||
limit, err := f.GetOrderExecutionLimits(asset.Spot, cp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = limit.Conforms(33000, 0.00001, order.Limit)
|
||||
if !errors.Is(err, order.ErrAmountBelowMin) {
|
||||
t.Fatalf("expected error %v but received %v",
|
||||
order.ErrAmountBelowMin,
|
||||
err)
|
||||
}
|
||||
|
||||
err = limit.Conforms(33000, 0.0001, order.Limit)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("expected error %v but received %v",
|
||||
nil,
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,24 +55,27 @@ type LendingInfoData struct {
|
||||
|
||||
// MarketData stores market data
|
||||
type MarketData struct {
|
||||
Name string `json:"name"`
|
||||
BaseCurrency string `json:"baseCurrency"`
|
||||
QuoteCurrency string `json:"quoteCurrency"`
|
||||
MarketType string `json:"type"`
|
||||
Underlying string `json:"underlying"`
|
||||
Change1h float64 `json:"change1h"`
|
||||
Change24h float64 `json:"change24h"`
|
||||
ChangeBod float64 `json:"changeBod"`
|
||||
QuoteVolume24h float64 `json:"quoteVolume24h"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Ask float64 `json:"ask"`
|
||||
Bid float64 `json:"bid"`
|
||||
Last float64 `json:"last"`
|
||||
USDVolume24h float64 `json:"volumeUSD24h"`
|
||||
MinProvideSize float64 `json:"minProvideSize"`
|
||||
PriceIncrement float64 `json:"priceIncrement"`
|
||||
SizeIncrement float64 `json:"sizeIncrement"`
|
||||
Restricted bool `json:"restricted"`
|
||||
Name string `json:"name"`
|
||||
BaseCurrency string `json:"baseCurrency"`
|
||||
QuoteCurrency string `json:"quoteCurrency"`
|
||||
MarketType string `json:"type"`
|
||||
Underlying string `json:"underlying"`
|
||||
Change1h float64 `json:"change1h"`
|
||||
Change24h float64 `json:"change24h"`
|
||||
ChangeBod float64 `json:"changeBod"`
|
||||
QuoteVolume24h float64 `json:"quoteVolume24h"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Ask float64 `json:"ask"`
|
||||
Bid float64 `json:"bid"`
|
||||
Last float64 `json:"last"`
|
||||
USDVolume24h float64 `json:"volumeUSD24h"`
|
||||
MinProvideSize float64 `json:"minProvideSize"`
|
||||
PriceIncrement float64 `json:"priceIncrement"`
|
||||
SizeIncrement float64 `json:"sizeIncrement"`
|
||||
Restricted bool `json:"restricted"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
Price float64 `json:"price"`
|
||||
HighLeverageFeeExempt bool `json:"highLeverageFeeExempt"`
|
||||
}
|
||||
|
||||
// OData stores orderdata in orderbook
|
||||
|
||||
@@ -218,11 +218,19 @@ func (f *FTX) Run() {
|
||||
f.PrintEnabledPairs()
|
||||
}
|
||||
|
||||
err := f.UpdateOrderExecutionLimits("")
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys,
|
||||
"%s failed to set exchange order execution limits. Err: %v",
|
||||
f.Name,
|
||||
err)
|
||||
}
|
||||
|
||||
if !f.GetEnabledFeatures().AutoPairUpdates {
|
||||
return
|
||||
}
|
||||
|
||||
err := f.UpdateTradablePairs(false)
|
||||
err = f.UpdateTradablePairs(false)
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys,
|
||||
"%s failed to update tradable pairs. Err: %s",
|
||||
@@ -1113,3 +1121,12 @@ func (f *FTX) GetHistoricCandlesExtended(p currency.Pair, a asset.Item, start, e
|
||||
ret.SortCandlesByTimestamp(false)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits sets exchange executions for a required asset type
|
||||
func (f *FTX) UpdateOrderExecutionLimits(_ asset.Item) error {
|
||||
limits, err := f.FetchExchangeLimits()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot update exchange execution limits: %w", err)
|
||||
}
|
||||
return f.LoadLimits(limits)
|
||||
}
|
||||
|
||||
@@ -98,6 +98,11 @@ func (e *ExecutionLimits) LoadLimits(levels []MinMaxLevel) error {
|
||||
}
|
||||
|
||||
for x := range levels {
|
||||
if !levels[x].Asset.IsValid() {
|
||||
return fmt.Errorf("cannot load levels for '%s': %w",
|
||||
levels[x].Asset,
|
||||
asset.ErrNotSupported)
|
||||
}
|
||||
m1, ok := e.m[levels[x].Asset]
|
||||
if !ok {
|
||||
m1 = make(map[*currency.Item]map[*currency.Item]*Limits)
|
||||
@@ -116,7 +121,9 @@ func (e *ExecutionLimits) LoadLimits(levels []MinMaxLevel) error {
|
||||
m2[levels[x].Pair.Quote.Item] = limit
|
||||
}
|
||||
|
||||
if levels[x].MinPrice > levels[x].MaxPrice {
|
||||
if levels[x].MinPrice > 0 &&
|
||||
levels[x].MaxPrice > 0 &&
|
||||
levels[x].MinPrice > levels[x].MaxPrice {
|
||||
return fmt.Errorf("%w for %s %s supplied min: %f max: %f",
|
||||
errInvalidPriceLevels,
|
||||
levels[x].Asset,
|
||||
@@ -125,7 +132,9 @@ func (e *ExecutionLimits) LoadLimits(levels []MinMaxLevel) error {
|
||||
levels[x].MaxPrice)
|
||||
}
|
||||
|
||||
if levels[x].MinAmount > levels[x].MaxAmount {
|
||||
if levels[x].MinAmount > 0 &&
|
||||
levels[x].MaxAmount > 0 &&
|
||||
levels[x].MinAmount > levels[x].MaxAmount {
|
||||
return fmt.Errorf("%w for %s %s supplied min: %f max: %f",
|
||||
errInvalidAmountLevels,
|
||||
levels[x].Asset,
|
||||
|
||||
@@ -20,6 +20,22 @@ func TestLoadLimits(t *testing.T) {
|
||||
t.Fatalf("expected error %v but received %v", errCannotLoadLimit, err)
|
||||
}
|
||||
|
||||
invalidAsset := []MinMaxLevel{
|
||||
{
|
||||
Pair: btcusd,
|
||||
MinPrice: 100000,
|
||||
MaxPrice: 1000000,
|
||||
MinAmount: 1,
|
||||
MaxAmount: 10,
|
||||
},
|
||||
}
|
||||
err = e.LoadLimits(invalidAsset)
|
||||
if !errors.Is(err, asset.ErrNotSupported) {
|
||||
t.Fatalf("expected error %v but received %v",
|
||||
asset.ErrNotSupported,
|
||||
err)
|
||||
}
|
||||
|
||||
newLimits := []MinMaxLevel{
|
||||
{
|
||||
Pair: btcusd,
|
||||
@@ -79,6 +95,32 @@ func TestLoadLimits(t *testing.T) {
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("expected error %v but received %v", nil, err)
|
||||
}
|
||||
|
||||
noCompare := []MinMaxLevel{
|
||||
{
|
||||
Pair: btcusd,
|
||||
Asset: asset.Spot,
|
||||
MinAmount: 10,
|
||||
},
|
||||
}
|
||||
|
||||
err = e.LoadLimits(noCompare)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("expected error %v but received %v", nil, err)
|
||||
}
|
||||
|
||||
noCompare = []MinMaxLevel{
|
||||
{
|
||||
Pair: btcusd,
|
||||
Asset: asset.Spot,
|
||||
MinPrice: 10,
|
||||
},
|
||||
}
|
||||
|
||||
err = e.LoadLimits(noCompare)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("expected error %v but received %v", nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderExecutionLimits(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user