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

Commit

Permalink
load orderConstraints for ccxtExchange from CCXT API (#112), closes #109
Browse files Browse the repository at this point in the history
 closes #110
  • Loading branch information
nikhilsaraf authored Feb 22, 2019
1 parent 3894b9a commit 8d28c11
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 23 deletions.
47 changes: 35 additions & 12 deletions plugins/ccxtExchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type ccxtExchange struct {
func makeCcxtExchange(
ccxtBaseURL string,
exchangeName string,
orderConstraints map[model.TradingPair]model.OrderConstraints,
orderConstraintOverrides map[model.TradingPair]model.OrderConstraints,
apiKeys []api.ExchangeAPIKey,
simMode bool,
) (api.Exchange, error) {
Expand All @@ -41,10 +41,14 @@ func makeCcxtExchange(
return nil, fmt.Errorf("error making a ccxt exchange: %s", e)
}

if orderConstraintOverrides == nil {
orderConstraintOverrides = map[model.TradingPair]model.OrderConstraints{}
}

return ccxtExchange{
assetConverter: model.CcxtAssetConverter,
delimiter: "/",
orderConstraints: orderConstraints,
orderConstraints: orderConstraintOverrides,
api: c,
simMode: simMode,
}, nil
Expand Down Expand Up @@ -74,8 +78,8 @@ func (c ccxtExchange) GetTickerPrice(pairs []model.TradingPair) (map[model.Tradi
}

priceResult[p] = api.Ticker{
AskPrice: model.NumberFromFloat(askPrice, c.orderConstraints[p].PricePrecision),
BidPrice: model.NumberFromFloat(bidPrice, c.orderConstraints[p].PricePrecision),
AskPrice: model.NumberFromFloat(askPrice, c.GetOrderConstraints(&p).PricePrecision),
BidPrice: model.NumberFromFloat(bidPrice, c.GetOrderConstraints(&p).PricePrecision),
}
}

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

// GetOrderConstraints impl
func (c ccxtExchange) GetOrderConstraints(pair *model.TradingPair) *model.OrderConstraints {
oc := c.orderConstraints[*pair]
if oc, ok := c.orderConstraints[*pair]; ok {
return &oc
}

pairString, e := pair.ToString(c.assetConverter, c.delimiter)
if e != nil {
// this should never really panic because we would have converted this trading pair to a string previously
panic(e)
}

// load from CCXT's cache
ccxtMarket := c.api.GetMarket(pairString)
if ccxtMarket == nil {
panic(fmt.Errorf("CCXT does not have precision and limit data for the passed in market: %s", pairString))
}
oc := *model.MakeOrderConstraints(ccxtMarket.Precision.Price, ccxtMarket.Precision.Amount, ccxtMarket.Limits.Amount.Min)

// cache it before returning
c.orderConstraints[*pair] = oc

return &oc
}

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

func (c ccxtExchange) readOrders(orders []sdk.CcxtOrder, pair *model.TradingPair, orderAction model.OrderAction) []model.Order {
pricePrecision := c.orderConstraints[*pair].PricePrecision
volumePrecision := c.orderConstraints[*pair].VolumePrecision
pricePrecision := c.GetOrderConstraints(pair).PricePrecision
volumePrecision := c.GetOrderConstraints(pair).VolumePrecision

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

pricePrecision := c.orderConstraints[*pair].PricePrecision
volumePrecision := c.orderConstraints[*pair].VolumePrecision
pricePrecision := c.GetOrderConstraints(pair).PricePrecision
volumePrecision := c.GetOrderConstraints(pair).VolumePrecision

trade := model.Trade{
Order: model.Order{
Expand Down Expand Up @@ -311,14 +334,14 @@ func (c ccxtExchange) convertOpenOrderFromCcxt(pair *model.TradingPair, o sdk.Cc
Pair: pair,
OrderAction: orderAction,
OrderType: model.OrderTypeLimit,
Price: model.NumberFromFloat(o.Price, c.orderConstraints[*pair].PricePrecision),
Volume: model.NumberFromFloat(o.Amount, c.orderConstraints[*pair].VolumePrecision),
Price: model.NumberFromFloat(o.Price, c.GetOrderConstraints(pair).PricePrecision),
Volume: model.NumberFromFloat(o.Amount, c.GetOrderConstraints(pair).VolumePrecision),
Timestamp: ts,
},
ID: o.ID,
StartTime: ts,
ExpireTime: nil,
VolumeExecuted: model.NumberFromFloat(o.Filled, c.orderConstraints[*pair].VolumePrecision),
VolumeExecuted: model.NumberFromFloat(o.Filled, c.GetOrderConstraints(pair).VolumePrecision),
}, nil
}

Expand Down
13 changes: 3 additions & 10 deletions plugins/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,17 +173,10 @@ var exchanges = map[string]ExchangeContainer{
Description: "Binance is a popular centralized cryptocurrency exchange (via ccxt-rest)",
TradeEnabled: true,
makeFn: func(exchangeFactoryData exchangeFactoryData) (api.Exchange, error) {
// https://www.binance.com/api/v1/exchangeInfo
// https://support.binance.com/hc/en-us/articles/115000594711-Trading-Rule
binanceOrderConstraints := map[model.TradingPair]model.OrderConstraints{
*model.MakeTradingPair(model.XLM, model.USDT): *model.MakeOrderConstraints(5, 2, 100.0), // converted USDT value to XLM for minBaseVolume with a lot of slack
*model.MakeTradingPair(model.XLM, model.BTC): *model.MakeOrderConstraints(8, 0, 100.0), // converted BTC value to XLM for minBaseVolume with a lot of slack
}

return makeCcxtExchange(
"http://localhost:3000",
"binance",
binanceOrderConstraints,
nil,
exchangeFactoryData.apiKeys,
exchangeFactoryData.simMode,
)
Expand All @@ -197,7 +190,7 @@ var exchanges = map[string]ExchangeContainer{
return makeCcxtExchange(
"http://localhost:3000",
"poloniex",
map[model.TradingPair]model.OrderConstraints{}, // TODO when enabling trading
nil,
exchangeFactoryData.apiKeys,
exchangeFactoryData.simMode,
)
Expand All @@ -211,7 +204,7 @@ var exchanges = map[string]ExchangeContainer{
return makeCcxtExchange(
"http://localhost:3000",
"bittrex",
map[model.TradingPair]model.OrderConstraints{}, // TODO when enabling trading
nil,
exchangeFactoryData.apiKeys,
exchangeFactoryData.simMode,
)
Expand Down
39 changes: 38 additions & 1 deletion support/sdk/ccxt.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ type Ccxt struct {
ccxtBaseURL string
exchangeName string
instanceName string
markets map[string]CcxtMarket
}

// CcxtMarket represents the result of a LoadMarkets call
type CcxtMarket struct {
// only contains currently needed data
Symbol string `json:"symbol"`
Base string `json:"base"`
Quote string `json:"quote"`
Limits struct {
Amount struct {
Min float64 `json:"min"`
} `json:"amount"`
Price struct {
Min float64 `json:"min"`
} `json:"price"`
} `json:"limits"`
Precision struct {
Amount int8 `json:"amount"`
Price int8 `json:"price"`
} `json:"precision"`
}

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

// load markets to populate fields related to markets
var marketsResponse interface{}
url := c.ccxtBaseURL + pathExchanges + "/" + c.exchangeName + "/" + c.instanceName + "/loadMarkets"
e = networking.JSONRequest(c.httpClient, "POST", url, "", map[string]string{}, nil)
e = networking.JSONRequest(c.httpClient, "POST", url, "", map[string]string{}, &marketsResponse)
if e != nil {
return fmt.Errorf("error loading markets for exchange instance (exchange=%s, instanceName=%s): %s", c.exchangeName, c.instanceName, e)
}
// decode markets and sets it on the ccxt instance
var markets map[string]CcxtMarket
e = mapstructure.Decode(marketsResponse, &markets)
if e != nil {
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)
}
c.markets = markets

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

// GetMarket returns the CcxtMarket instance
func (c *Ccxt) GetMarket(tradingPair string) *CcxtMarket {
if v, ok := c.markets[tradingPair]; ok {
return &v
}
return nil
}

// FetchTicker calls the /fetchTicker endpoint on CCXT, trading pair is the CCXT version of the trading pair
func (c *Ccxt) FetchTicker(tradingPair string) (map[string]interface{}, error) {
e := c.symbolExists(tradingPair)
Expand Down

0 comments on commit 8d28c11

Please sign in to comment.