Skip to content
This repository was archived by the owner on Feb 1, 2024. It is now read-only.

Commit 8d28c11

Browse files
authored
load orderConstraints for ccxtExchange from CCXT API (#112), closes #109 closes #110
1 parent 3894b9a commit 8d28c11

3 files changed

Lines changed: 76 additions & 23 deletions

File tree

plugins/ccxtExchange.go

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type ccxtExchange struct {
2828
func makeCcxtExchange(
2929
ccxtBaseURL string,
3030
exchangeName string,
31-
orderConstraints map[model.TradingPair]model.OrderConstraints,
31+
orderConstraintOverrides map[model.TradingPair]model.OrderConstraints,
3232
apiKeys []api.ExchangeAPIKey,
3333
simMode bool,
3434
) (api.Exchange, error) {
@@ -41,10 +41,14 @@ func makeCcxtExchange(
4141
return nil, fmt.Errorf("error making a ccxt exchange: %s", e)
4242
}
4343

44+
if orderConstraintOverrides == nil {
45+
orderConstraintOverrides = map[model.TradingPair]model.OrderConstraints{}
46+
}
47+
4448
return ccxtExchange{
4549
assetConverter: model.CcxtAssetConverter,
4650
delimiter: "/",
47-
orderConstraints: orderConstraints,
51+
orderConstraints: orderConstraintOverrides,
4852
api: c,
4953
simMode: simMode,
5054
}, nil
@@ -74,8 +78,8 @@ func (c ccxtExchange) GetTickerPrice(pairs []model.TradingPair) (map[model.Tradi
7478
}
7579

7680
priceResult[p] = api.Ticker{
77-
AskPrice: model.NumberFromFloat(askPrice, c.orderConstraints[p].PricePrecision),
78-
BidPrice: model.NumberFromFloat(bidPrice, c.orderConstraints[p].PricePrecision),
81+
AskPrice: model.NumberFromFloat(askPrice, c.GetOrderConstraints(&p).PricePrecision),
82+
BidPrice: model.NumberFromFloat(bidPrice, c.GetOrderConstraints(&p).PricePrecision),
7983
}
8084
}
8185

@@ -89,7 +93,26 @@ func (c ccxtExchange) GetAssetConverter() *model.AssetConverter {
8993

9094
// GetOrderConstraints impl
9195
func (c ccxtExchange) GetOrderConstraints(pair *model.TradingPair) *model.OrderConstraints {
92-
oc := c.orderConstraints[*pair]
96+
if oc, ok := c.orderConstraints[*pair]; ok {
97+
return &oc
98+
}
99+
100+
pairString, e := pair.ToString(c.assetConverter, c.delimiter)
101+
if e != nil {
102+
// this should never really panic because we would have converted this trading pair to a string previously
103+
panic(e)
104+
}
105+
106+
// load from CCXT's cache
107+
ccxtMarket := c.api.GetMarket(pairString)
108+
if ccxtMarket == nil {
109+
panic(fmt.Errorf("CCXT does not have precision and limit data for the passed in market: %s", pairString))
110+
}
111+
oc := *model.MakeOrderConstraints(ccxtMarket.Precision.Price, ccxtMarket.Precision.Amount, ccxtMarket.Limits.Amount.Min)
112+
113+
// cache it before returning
114+
c.orderConstraints[*pair] = oc
115+
93116
return &oc
94117
}
95118

@@ -142,8 +165,8 @@ func (c ccxtExchange) GetOrderBook(pair *model.TradingPair, maxCount int32) (*mo
142165
}
143166

144167
func (c ccxtExchange) readOrders(orders []sdk.CcxtOrder, pair *model.TradingPair, orderAction model.OrderAction) []model.Order {
145-
pricePrecision := c.orderConstraints[*pair].PricePrecision
146-
volumePrecision := c.orderConstraints[*pair].VolumePrecision
168+
pricePrecision := c.GetOrderConstraints(pair).PricePrecision
169+
volumePrecision := c.GetOrderConstraints(pair).VolumePrecision
147170

148171
result := []model.Order{}
149172
for _, o := range orders {
@@ -222,8 +245,8 @@ func (c ccxtExchange) readTrade(pair *model.TradingPair, pairString string, rawT
222245
return nil, fmt.Errorf("expected '%s' for 'symbol' field, got: %s", pairString, rawTrade.Symbol)
223246
}
224247

225-
pricePrecision := c.orderConstraints[*pair].PricePrecision
226-
volumePrecision := c.orderConstraints[*pair].VolumePrecision
248+
pricePrecision := c.GetOrderConstraints(pair).PricePrecision
249+
volumePrecision := c.GetOrderConstraints(pair).VolumePrecision
227250

228251
trade := model.Trade{
229252
Order: model.Order{
@@ -311,14 +334,14 @@ func (c ccxtExchange) convertOpenOrderFromCcxt(pair *model.TradingPair, o sdk.Cc
311334
Pair: pair,
312335
OrderAction: orderAction,
313336
OrderType: model.OrderTypeLimit,
314-
Price: model.NumberFromFloat(o.Price, c.orderConstraints[*pair].PricePrecision),
315-
Volume: model.NumberFromFloat(o.Amount, c.orderConstraints[*pair].VolumePrecision),
337+
Price: model.NumberFromFloat(o.Price, c.GetOrderConstraints(pair).PricePrecision),
338+
Volume: model.NumberFromFloat(o.Amount, c.GetOrderConstraints(pair).VolumePrecision),
316339
Timestamp: ts,
317340
},
318341
ID: o.ID,
319342
StartTime: ts,
320343
ExpireTime: nil,
321-
VolumeExecuted: model.NumberFromFloat(o.Filled, c.orderConstraints[*pair].VolumePrecision),
344+
VolumeExecuted: model.NumberFromFloat(o.Filled, c.GetOrderConstraints(pair).VolumePrecision),
322345
}, nil
323346
}
324347

plugins/factory.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -173,17 +173,10 @@ var exchanges = map[string]ExchangeContainer{
173173
Description: "Binance is a popular centralized cryptocurrency exchange (via ccxt-rest)",
174174
TradeEnabled: true,
175175
makeFn: func(exchangeFactoryData exchangeFactoryData) (api.Exchange, error) {
176-
// https://www.binance.com/api/v1/exchangeInfo
177-
// https://support.binance.com/hc/en-us/articles/115000594711-Trading-Rule
178-
binanceOrderConstraints := map[model.TradingPair]model.OrderConstraints{
179-
*model.MakeTradingPair(model.XLM, model.USDT): *model.MakeOrderConstraints(5, 2, 100.0), // converted USDT value to XLM for minBaseVolume with a lot of slack
180-
*model.MakeTradingPair(model.XLM, model.BTC): *model.MakeOrderConstraints(8, 0, 100.0), // converted BTC value to XLM for minBaseVolume with a lot of slack
181-
}
182-
183176
return makeCcxtExchange(
184177
"http://localhost:3000",
185178
"binance",
186-
binanceOrderConstraints,
179+
nil,
187180
exchangeFactoryData.apiKeys,
188181
exchangeFactoryData.simMode,
189182
)
@@ -197,7 +190,7 @@ var exchanges = map[string]ExchangeContainer{
197190
return makeCcxtExchange(
198191
"http://localhost:3000",
199192
"poloniex",
200-
map[model.TradingPair]model.OrderConstraints{}, // TODO when enabling trading
193+
nil,
201194
exchangeFactoryData.apiKeys,
202195
exchangeFactoryData.simMode,
203196
)
@@ -211,7 +204,7 @@ var exchanges = map[string]ExchangeContainer{
211204
return makeCcxtExchange(
212205
"http://localhost:3000",
213206
"bittrex",
214-
map[model.TradingPair]model.OrderConstraints{}, // TODO when enabling trading
207+
nil,
215208
exchangeFactoryData.apiKeys,
216209
exchangeFactoryData.simMode,
217210
)

support/sdk/ccxt.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,27 @@ type Ccxt struct {
2020
ccxtBaseURL string
2121
exchangeName string
2222
instanceName string
23+
markets map[string]CcxtMarket
24+
}
25+
26+
// CcxtMarket represents the result of a LoadMarkets call
27+
type CcxtMarket struct {
28+
// only contains currently needed data
29+
Symbol string `json:"symbol"`
30+
Base string `json:"base"`
31+
Quote string `json:"quote"`
32+
Limits struct {
33+
Amount struct {
34+
Min float64 `json:"min"`
35+
} `json:"amount"`
36+
Price struct {
37+
Min float64 `json:"min"`
38+
} `json:"price"`
39+
} `json:"limits"`
40+
Precision struct {
41+
Amount int8 `json:"amount"`
42+
Price int8 `json:"price"`
43+
} `json:"precision"`
2344
}
2445

2546
const pathExchanges = "/exchanges"
@@ -88,11 +109,19 @@ func (c *Ccxt) init(apiKey api.ExchangeAPIKey) error {
88109
}
89110

90111
// load markets to populate fields related to markets
112+
var marketsResponse interface{}
91113
url := c.ccxtBaseURL + pathExchanges + "/" + c.exchangeName + "/" + c.instanceName + "/loadMarkets"
92-
e = networking.JSONRequest(c.httpClient, "POST", url, "", map[string]string{}, nil)
114+
e = networking.JSONRequest(c.httpClient, "POST", url, "", map[string]string{}, &marketsResponse)
93115
if e != nil {
94116
return fmt.Errorf("error loading markets for exchange instance (exchange=%s, instanceName=%s): %s", c.exchangeName, c.instanceName, e)
95117
}
118+
// decode markets and sets it on the ccxt instance
119+
var markets map[string]CcxtMarket
120+
e = mapstructure.Decode(marketsResponse, &markets)
121+
if e != nil {
122+
return fmt.Errorf("error converting loadMarkets output to a map of Market for exchange instance (exchange=%s, instanceName=%s): %s", c.exchangeName, c.instanceName, e)
123+
}
124+
c.markets = markets
96125

97126
return nil
98127
}
@@ -180,6 +209,14 @@ func (c *Ccxt) symbolExists(tradingPair string) error {
180209
return fmt.Errorf("trading pair '%s' does not exist in the list of %d symbols on exchange '%s'", tradingPair, len(symbolsList), c.exchangeName)
181210
}
182211

212+
// GetMarket returns the CcxtMarket instance
213+
func (c *Ccxt) GetMarket(tradingPair string) *CcxtMarket {
214+
if v, ok := c.markets[tradingPair]; ok {
215+
return &v
216+
}
217+
return nil
218+
}
219+
183220
// FetchTicker calls the /fetchTicker endpoint on CCXT, trading pair is the CCXT version of the trading pair
184221
func (c *Ccxt) FetchTicker(tradingPair string) (map[string]interface{}, error) {
185222
e := c.symbolExists(tradingPair)

0 commit comments

Comments
 (0)