diff --git a/LIST_OF_HACKS.md b/LIST_OF_HACKS.md index ffc58cab8..e90c00428 100644 --- a/LIST_OF_HACKS.md +++ b/LIST_OF_HACKS.md @@ -5,6 +5,7 @@ Incomplete list of hacks in the codebase that should be fixed before upgrading to v2.0 which will change the API to Kelp in some way - LOH-1 - support backward-compatible case of not having any pre-specified function +- LOH-2 - support backward-compatible case of defaulting to "mid" price when left unspecified ## Workarounds diff --git a/api/exchange.go b/api/exchange.go index 8354438dd..ea0c30d71 100644 --- a/api/exchange.go +++ b/api/exchange.go @@ -36,8 +36,9 @@ type Account interface { // Ticker encapsulates all the data for a given Trading Pair type Ticker struct { - AskPrice *model.Number - BidPrice *model.Number + AskPrice *model.Number + BidPrice *model.Number + LastPrice *model.Number } // TradesResult is the result of a GetTrades call diff --git a/api/priceFeed.go b/api/priceFeed.go index a93a23c38..2550e6918 100644 --- a/api/priceFeed.go +++ b/api/priceFeed.go @@ -7,14 +7,15 @@ type PriceFeed interface { GetPrice() (float64, error) } +// TODO this should be structured as a specific impl. of the PriceFeed interface // FeedPair is the struct representing a price feed for a trading pair type FeedPair struct { FeedA PriceFeed FeedB PriceFeed } -// GetMidPrice fetches the mid price from this feed pair -func (p *FeedPair) GetMidPrice() (float64, error) { +// GetFeedPairPrice fetches the price by dividing FeedA by FeedB +func (p *FeedPair) GetFeedPairPrice() (float64, error) { pA, err := p.FeedA.GetPrice() if err != nil { return 0, err @@ -26,7 +27,7 @@ func (p *FeedPair) GetMidPrice() (float64, error) { return 0, err } - midPrice := pA / pB - log.Printf("feedPair prices: feedA=%.7f, feedB=%.7f; midPrice=%.7f\n", pA, pB, midPrice) - return midPrice, nil + price := pA / pB + log.Printf("feedPair prices: feedA=%.8f, feedB=%.8f; price=%.8f\n", pA, pB, price) + return price, nil } diff --git a/examples/configs/trader/sample_buysell.cfg b/examples/configs/trader/sample_buysell.cfg index 4357cf038..3f758fe95 100644 --- a/examples/configs/trader/sample_buysell.cfg +++ b/examples/configs/trader/sample_buysell.cfg @@ -19,11 +19,11 @@ DATA_TYPE_A="exchange" # this code can be retrieved from the exchange's website or from the ccxt manual for ccxt-based exchanges. # modifier: # this is a modifier that can be included only for feed type "exchange". -# a modifier allows you to fetch the "mid" price, "ask" price, or "bid" price for now. -# if left unspecified then this is defaulted to "mid" for backwards compatibility (until v2.0 is released) +# a modifier allows you to fetch the "mid" price, "ask" price, "bid" price, or "last" price for now. +# if left unspecified then this is defaulted to "mid" for backwards compatibility (until v2.0 is released) (LOH-2) # uncomment below to use binance, poloniex, or bittrex as your price feed. You will need to set up CCXT to use this, see the "Using CCXT" section in the README for details. # be careful about using USD vs. USDT since some exchanges support only one, or both, or in some cases neither. -#DATA_FEED_A_URL="ccxt-kraken/XLM/USD/mid" +#DATA_FEED_A_URL="ccxt-kraken/XLM/USD/last" #DATA_FEED_A_URL="ccxt-binance/XLM/USDT/ask" #DATA_FEED_A_URL="ccxt-poloniex/XLM/USDT/bid" # bittrex does not have an XLM/USD market so this config lists XLM/BTC instead; you should NOT use this when trying to price an asset based on the XLM/USD price (unless you know what you are doing). diff --git a/examples/configs/trader/sample_sell.cfg b/examples/configs/trader/sample_sell.cfg index 9e9b498e8..7206a4457 100644 --- a/examples/configs/trader/sample_sell.cfg +++ b/examples/configs/trader/sample_sell.cfg @@ -21,11 +21,11 @@ DATA_TYPE_A="exchange" # this code can be retrieved from the exchange's website or from the ccxt manual for ccxt-based exchanges. # modifier: # this is a modifier that can be included only for feed type "exchange". -# a modifier allows you to fetch the "mid" price, "ask" price, or "bid" price for now. -# if left unspecified then this is defaulted to "mid" for backwards compatibility (until v2.0 is released) +# a modifier allows you to fetch the "mid" price, "ask" price, "bid" price, or "last" price for now. +# if left unspecified then this is defaulted to "mid" for backwards compatibility (until v2.0 is released) (LOH-2) # uncomment below to use binance, poloniex, or bittrex as your price feed. You will need to set up CCXT to use this, see the "Using CCXT" section in the README for details. # be careful about using USD vs. USDT since some exchanges support only one, or both, or in some cases neither. -#DATA_FEED_A_URL="ccxt-kraken/XLM/USD/mid" +#DATA_FEED_A_URL="ccxt-kraken/XLM/USD/last" #DATA_FEED_A_URL="ccxt-binance/XLM/USDT/ask" #DATA_FEED_A_URL="ccxt-poloniex/XLM/USDT/bid" # bittrex does not have an XLM/USD market so this config lists XLM/BTC instead; you should NOT use this when trying to price an asset based on the XLM/USD price (unless you know what you are doing). diff --git a/plugins/ccxtExchange.go b/plugins/ccxtExchange.go index 0a5b21643..809578f08 100644 --- a/plugins/ccxtExchange.go +++ b/plugins/ccxtExchange.go @@ -80,16 +80,22 @@ func (c ccxtExchange) GetTickerPrice(pairs []model.TradingPair) (map[model.Tradi askPrice, e := utils.CheckFetchFloat(tickerMap, "ask") if e != nil { - return nil, fmt.Errorf("unable to correctly fetch value from tickerMap: %s", e) + return nil, fmt.Errorf("unable to correctly fetch 'ask' value from tickerMap: %s", e) } bidPrice, e := utils.CheckFetchFloat(tickerMap, "bid") if e != nil { - return nil, fmt.Errorf("unable to correctly fetch value from tickerMap: %s", e) + return nil, fmt.Errorf("unable to correctly fetch 'bid' value from tickerMap: %s", e) + } + lastPrice, e := utils.CheckFetchFloat(tickerMap, "last") + if e != nil { + return nil, fmt.Errorf("unable to correctly fetch 'last' value from tickerMap: %s", e) } + pricePrecision := c.GetOrderConstraints(&p).PricePrecision priceResult[p] = api.Ticker{ - AskPrice: model.NumberFromFloat(askPrice, c.GetOrderConstraints(&p).PricePrecision), - BidPrice: model.NumberFromFloat(bidPrice, c.GetOrderConstraints(&p).PricePrecision), + AskPrice: model.NumberFromFloat(askPrice, pricePrecision), + BidPrice: model.NumberFromFloat(bidPrice, pricePrecision), + LastPrice: model.NumberFromFloat(lastPrice, pricePrecision), } } @@ -114,7 +120,11 @@ func (c ccxtExchange) GetOrderConstraints(pair *model.TradingPair) *model.OrderC if ccxtMarket == nil { panic(fmt.Errorf("CCXT does not have precision and limit data for the passed in market: %s", pairString)) } - oc := model.MakeOrderConstraintsWithCost(ccxtMarket.Precision.Price, ccxtMarket.Precision.Amount, ccxtMarket.Limits.Amount.Min, ccxtMarket.Limits.Cost.Min) + volumePrecision := ccxtMarket.Precision.Amount + if volumePrecision == 0 { + volumePrecision = ccxtMarket.Precision.Price + } + oc := model.MakeOrderConstraintsWithCost(ccxtMarket.Precision.Price, volumePrecision, ccxtMarket.Limits.Amount.Min, ccxtMarket.Limits.Cost.Min) return c.ocOverridesHandler.Apply(pair, oc) } diff --git a/plugins/ccxtExchange_test.go b/plugins/ccxtExchange_test.go index 89889535e..3fcae9338 100644 --- a/plugins/ccxtExchange_test.go +++ b/plugins/ccxtExchange_test.go @@ -3,6 +3,7 @@ package plugins import ( "fmt" "log" + "math" "strconv" "testing" "time" @@ -12,16 +13,22 @@ import ( "github.com/stretchr/testify/assert" ) -var supportedExchanges = []string{"binance"} +var supportedExchanges = []string{"binance", "kraken"} var emptyAPIKey = api.ExchangeAPIKey{} var emptyParams = api.ExchangeParam{} var supportedTradingExchanges = map[string]api.ExchangeAPIKey{ "binance": {}, } -var testOrderConstraints = map[model.TradingPair]model.OrderConstraints{ - *model.MakeTradingPair(model.XLM, model.USDT): *model.MakeOrderConstraints(4, 5, 0.1), - *model.MakeTradingPair(model.XLM, model.BTC): *model.MakeOrderConstraints(8, 4, 1.0), +var testOrderConstraints = map[string]map[model.TradingPair]model.OrderConstraints{ + "binance": map[model.TradingPair]model.OrderConstraints{ + *model.MakeTradingPair(model.XLM, model.USDT): *model.MakeOrderConstraints(4, 5, 0.1), + *model.MakeTradingPair(model.XLM, model.BTC): *model.MakeOrderConstraints(8, 4, 1.0), + }, + "kraken": map[model.TradingPair]model.OrderConstraints{ + *model.MakeTradingPair(model.XLM, model.USD): *model.MakeOrderConstraints(6, 8, 30.0), + *model.MakeTradingPair(model.XLM, model.BTC): *model.MakeOrderConstraints(8, 8, 30.0), + }, } func TestGetTickerPrice_Ccxt(t *testing.T) { @@ -31,7 +38,7 @@ func TestGetTickerPrice_Ccxt(t *testing.T) { for _, exchangeName := range supportedExchanges { t.Run(exchangeName, func(t *testing.T) { - testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints, []api.ExchangeAPIKey{emptyAPIKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) + testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints[exchangeName], []api.ExchangeAPIKey{emptyAPIKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) if !assert.NoError(t, e) { return } @@ -47,6 +54,9 @@ func TestGetTickerPrice_Ccxt(t *testing.T) { ticker := m[pair] assert.True(t, ticker.AskPrice.AsFloat() < 1, ticker.AskPrice.AsString()) + assert.True(t, ticker.BidPrice.AsFloat() < 1, ticker.BidPrice.AsString()) + assert.True(t, ticker.BidPrice.AsFloat() < ticker.AskPrice.AsFloat(), fmt.Sprintf("bid price (%s) should be less than ask price (%s)", ticker.BidPrice.AsString(), ticker.AskPrice.AsString())) + assert.True(t, ticker.LastPrice.AsFloat() < 1, ticker.LastPrice.AsString()) }) } } @@ -58,7 +68,7 @@ func TestGetOrderBook_Ccxt(t *testing.T) { for _, exchangeName := range supportedExchanges { t.Run(exchangeName, func(t *testing.T) { - testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints, []api.ExchangeAPIKey{emptyAPIKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) + testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints[exchangeName], []api.ExchangeAPIKey{emptyAPIKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) if !assert.NoError(t, e) { return } @@ -91,7 +101,7 @@ func TestGetTrades_Ccxt(t *testing.T) { for _, exchangeName := range supportedExchanges { t.Run(exchangeName, func(t *testing.T) { - testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints, []api.ExchangeAPIKey{emptyAPIKey}, []api.ExchangeParam{}, []api.ExchangeHeader{}, false) + testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints[exchangeName], []api.ExchangeAPIKey{emptyAPIKey}, []api.ExchangeParam{}, []api.ExchangeHeader{}, false) if !assert.NoError(t, e) { return } @@ -117,7 +127,7 @@ func TestGetTradeHistory_Ccxt(t *testing.T) { for exchangeName, apiKey := range supportedTradingExchanges { t.Run(exchangeName, func(t *testing.T) { - testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints, []api.ExchangeAPIKey{apiKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) + testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints[exchangeName], []api.ExchangeAPIKey{apiKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) if !assert.NoError(t, e) { return } @@ -144,10 +154,10 @@ func validateTrades(t *testing.T, pair model.TradingPair, trades []model.Trade) if !assert.Equal(t, &pair, trade.Pair) { return } - if !assert.True(t, trade.Price.AsFloat() > 0, fmt.Sprintf("%.7f", trade.Price.AsFloat())) { + if !assert.True(t, trade.Price.AsFloat() > 0, trade.Price.AsString()) { return } - if !assert.True(t, trade.Volume.AsFloat() > 0, fmt.Sprintf("%.7f", trade.Volume.AsFloat())) { + if !assert.True(t, trade.Volume.AsFloat() > 0, trade.Volume.AsString()) { return } if !assert.Equal(t, trade.OrderType, model.OrderTypeLimit) { @@ -169,7 +179,9 @@ func validateTrades(t *testing.T, pair model.TradingPair, trades []model.Trade) assert.Fail(t, "trade.OrderAction should be either OrderActionBuy or OrderActionSell: %v", trade.OrderAction) return } - if !assert.True(t, trade.Cost.AsFloat() > 0, fmt.Sprintf("(price) %s x (volume) %s = (cost) %s", trade.Price.AsString(), trade.Volume.AsString(), trade.Cost.AsString())) { + minPrecision := math.Min(float64(trade.Price.Precision()), float64(trade.Volume.Precision())) + nonZeroCalculatedCost := trade.Price.AsFloat()*trade.Volume.AsFloat() > math.Pow(10, -minPrecision) + if nonZeroCalculatedCost && !assert.True(t, trade.Cost.AsFloat() > 0, fmt.Sprintf("(price) %s x (volume) %s = (cost) %s", trade.Price.AsString(), trade.Volume.AsString(), trade.Cost.AsString())) { return } } @@ -178,7 +190,7 @@ func validateTrades(t *testing.T, pair model.TradingPair, trades []model.Trade) func TestGetLatestTradeCursor_Ccxt(t *testing.T) { for exchangeName, apiKey := range supportedTradingExchanges { t.Run(exchangeName, func(t *testing.T) { - testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints, []api.ExchangeAPIKey{apiKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) + testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints[exchangeName], []api.ExchangeAPIKey{apiKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) if !assert.NoError(t, e) { return } @@ -217,7 +229,7 @@ func TestGetAccountBalances_Ccxt(t *testing.T) { for exchangeName, apiKey := range supportedTradingExchanges { t.Run(exchangeName, func(t *testing.T) { - testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints, []api.ExchangeAPIKey{apiKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) + testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints[exchangeName], []api.ExchangeAPIKey{apiKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) if !assert.NoError(t, e) { return } @@ -262,7 +274,7 @@ func TestGetOpenOrders_Ccxt(t *testing.T) { for exchangeName, apiKey := range supportedTradingExchanges { for _, pair := range tradingPairs { t.Run(exchangeName, func(t *testing.T) { - testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints, []api.ExchangeAPIKey{apiKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) + testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints[exchangeName], []api.ExchangeAPIKey{apiKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) if !assert.NoError(t, e) { return } @@ -377,7 +389,7 @@ func TestAddOrder_Ccxt(t *testing.T) { }, } { t.Run(exchangeName, func(t *testing.T) { - testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints, []api.ExchangeAPIKey{apiKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) + testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints[exchangeName], []api.ExchangeAPIKey{apiKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) if !assert.NoError(t, e) { return } @@ -427,7 +439,7 @@ func TestCancelOrder_Ccxt(t *testing.T) { }, } { t.Run(exchangeName, func(t *testing.T) { - testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints, []api.ExchangeAPIKey{apiKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) + testCcxtExchange, e := makeCcxtExchange(exchangeName, testOrderConstraints[exchangeName], []api.ExchangeAPIKey{apiKey}, []api.ExchangeParam{emptyParams}, []api.ExchangeHeader{}, false) if !assert.NoError(t, e) { return } @@ -473,7 +485,7 @@ func TestGetOrderConstraints_Ccxt_Precision(t *testing.T) { exchangeName: "binance", pair: &model.TradingPair{Base: model.XLM, Quote: model.BTC}, wantPricePrecision: 8, - wantVolPrecision: 0, + wantVolPrecision: 8, }, } diff --git a/plugins/exchangeFeed.go b/plugins/exchangeFeed.go index e208452e6..f409bc851 100644 --- a/plugins/exchangeFeed.go +++ b/plugins/exchangeFeed.go @@ -19,13 +19,17 @@ type exchangeFeed struct { // ensure that it implements PriceFeed var _ api.PriceFeed = &exchangeFeed{} -func newExchangeFeed(name string, tickerAPI *api.TickerAPI, pair *model.TradingPair, modifier string) *exchangeFeed { +func newExchangeFeed(name string, tickerAPI *api.TickerAPI, pair *model.TradingPair, modifier string) (*exchangeFeed, error) { + if modifier != "mid" && modifier != "ask" && modifier != "bid" && modifier != "last" { + return nil, fmt.Errorf("unsupported modifier '%s' on exchange type URL", modifier) + } + return &exchangeFeed{ name: name, tickerAPI: tickerAPI, pairs: []model.TradingPair{*pair}, modifier: modifier, - } + }, nil } // GetPrice impl @@ -41,15 +45,27 @@ func (f *exchangeFeed) GetPrice() (float64, error) { return 0, fmt.Errorf("could not get price for trading pair: %s", f.pairs[0].String()) } - midPrice := (p.BidPrice.AsFloat() + p.AskPrice.AsFloat()) / 2 - var price float64 + midPrice := p.BidPrice.Add(*p.AskPrice).Scale(0.5) + var price *model.Number if f.modifier == "ask" { - price = p.AskPrice.AsFloat() + price = p.AskPrice } else if f.modifier == "bid" { - price = p.BidPrice.AsFloat() + price = p.BidPrice + } else if f.modifier == "last" { + price = p.LastPrice } else { + // LOH-2 - support backward-compatible case of defaulting to "mid" price when left unspecified price = midPrice } - log.Printf("(modifier: %s) price from exchange feed (%s): bidPrice=%.7f, askPrice=%.7f, midPrice=%.7f; price=%.7f", f.modifier, f.name, p.BidPrice.AsFloat(), p.AskPrice.AsFloat(), midPrice, price) - return price, nil + + log.Printf("(modifier: %s) price from exchange feed (%s): bidPrice=%s, askPrice=%s, midPrice=%s, lastTradePrice=%s; price=%s", + f.modifier, + f.name, + p.BidPrice.AsString(), + p.AskPrice.AsString(), + midPrice.AsString(), + p.LastPrice.AsString(), + price.AsString(), + ) + return price.AsFloat(), nil } diff --git a/plugins/krakenExchange.go b/plugins/krakenExchange.go index cc604d1f5..391859058 100644 --- a/plugins/krakenExchange.go +++ b/plugins/krakenExchange.go @@ -322,8 +322,9 @@ func (k *krakenExchange) GetTickerPrice(pairs []model.TradingPair) (map[model.Tr orderConstraints := k.GetOrderConstraints(&p) pairTickerInfo := resp.GetPairTickerInfo(pairsMap[p]) priceResult[p] = api.Ticker{ - AskPrice: model.MustNumberFromString(pairTickerInfo.Ask[0], orderConstraints.PricePrecision), - BidPrice: model.MustNumberFromString(pairTickerInfo.Bid[0], orderConstraints.PricePrecision), + AskPrice: model.MustNumberFromString(pairTickerInfo.Ask[0], orderConstraints.PricePrecision), + BidPrice: model.MustNumberFromString(pairTickerInfo.Bid[0], orderConstraints.PricePrecision), + LastPrice: model.MustNumberFromString(pairTickerInfo.Close[0], orderConstraints.PricePrecision), } } diff --git a/plugins/krakenExchange_test.go b/plugins/krakenExchange_test.go index ca6ce171e..569caafef 100644 --- a/plugins/krakenExchange_test.go +++ b/plugins/krakenExchange_test.go @@ -35,7 +35,7 @@ func TestGetTickerPrice(t *testing.T) { assert.Equal(t, 1, len(m)) ticker := m[pair] - fmt.Printf("ticker price: bid=%.8f, ask=%.8f\n", ticker.BidPrice.AsFloat(), ticker.AskPrice.AsFloat()) + fmt.Printf("ticker price: bid=%s, ask=%s, last=%s\n", ticker.BidPrice.AsString(), ticker.AskPrice.AsString(), ticker.LastPrice.AsString()) if !assert.True(t, ticker.AskPrice.AsFloat() < 1, ticker.AskPrice.AsString()) { return @@ -43,6 +43,12 @@ func TestGetTickerPrice(t *testing.T) { if !assert.True(t, ticker.BidPrice.AsFloat() < 1, ticker.BidPrice.AsString()) { return } + if !assert.True(t, ticker.BidPrice.AsFloat() < ticker.AskPrice.AsFloat(), fmt.Sprintf("bid price (%s) should be less than ask price (%s)", ticker.BidPrice.AsString(), ticker.AskPrice.AsString())) { + return + } + if !assert.True(t, ticker.LastPrice.AsFloat() < 1, ticker.LastPrice.AsString()) { + return + } } func TestGetAccountBalances(t *testing.T) { diff --git a/plugins/priceFeed.go b/plugins/priceFeed.go index 4ce7eb722..04e8a5df4 100644 --- a/plugins/priceFeed.go +++ b/plugins/priceFeed.go @@ -49,13 +49,10 @@ func MakePriceFeed(feedType string, url string) (api.PriceFeed, error) { return nil, fmt.Errorf("invalid format of exchange type URL, needs either 3 or 4 parts after splitting URL by '/', has %d: %s", len(urlParts), url) } - // default to "mid" for backwards compatibility + // LOH-2 - support backward-compatible case of defaulting to "mid" price when left unspecified exchangeModifier := "mid" if len(urlParts) == 4 { exchangeModifier = urlParts[3] - if exchangeModifier != "mid" && exchangeModifier != "ask" && exchangeModifier != "bid" { - return nil, fmt.Errorf("unsupported exchange modifier '%s' on exchange type URL", exchangeModifier) - } } exchange, e := MakeExchange(urlParts[0], true) @@ -75,7 +72,7 @@ func MakePriceFeed(feedType string, url string) (api.PriceFeed, error) { Quote: quoteAsset, } tickerAPI := api.TickerAPI(exchange) - return newExchangeFeed(url, &tickerAPI, &tradingPair, exchangeModifier), nil + return newExchangeFeed(url, &tickerAPI, &tradingPair, exchangeModifier) case "sdex": sdex, e := makeSDEXFeed(url) if e != nil { diff --git a/plugins/staticSpreadLevelProvider.go b/plugins/staticSpreadLevelProvider.go index 31f3cd0d9..0fa30da09 100644 --- a/plugins/staticSpreadLevelProvider.go +++ b/plugins/staticSpreadLevelProvider.go @@ -58,7 +58,7 @@ func makeStaticSpreadLevelProvider(staticLevels []StaticLevel, amountOfBase floa // GetLevels impl. func (p *staticSpreadLevelProvider) GetLevels(maxAssetBase float64, maxAssetQuote float64) ([]api.Level, error) { - midPrice, e := p.pf.GetMidPrice() + midPrice, e := p.pf.GetFeedPairPrice() if e != nil { return nil, fmt.Errorf("mid price couldn't be loaded: %s", e) } diff --git a/support/sdk/ccxt_test.go b/support/sdk/ccxt_test.go index 4cbf7cb3c..a29fd62d6 100644 --- a/support/sdk/ccxt_test.go +++ b/support/sdk/ccxt_test.go @@ -86,6 +86,9 @@ func TestFetchTickers(t *testing.T) { } assert.Equal(t, "BTC/USDT", m["symbol"].(string)) + assert.True(t, m["ask"].(float64) > 0) + assert.True(t, m["bid"].(float64) > 0) + assert.True(t, m["bid"].(float64) < m["ask"].(float64), fmt.Sprintf("bid price (%f) should be less than ask price (%f)", m["bid"].(float64), m["ask"].(float64))) assert.True(t, m["last"].(float64) > 0) }