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

Commit

Permalink
fix minVolume amounts when offsetting trades in mirror strategy, closes
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilsaraf authored Mar 28, 2019
1 parent 8d6d450 commit 0576aa1
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 2 deletions.
9 changes: 9 additions & 0 deletions cmd/trade.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ func validateBotConfig(l logger.Logger, botConfig trader.BotConfig) {
if botConfig.IsTradingSdex() && botConfig.Fee == nil {
logger.Fatal(l, fmt.Errorf("The `FEE` object needs to exist in the trader config file when trading on SDEX"))
}

if !botConfig.IsTradingSdex() && botConfig.MinCentralizedBaseVolume == 0.0 {
logger.Fatal(l, fmt.Errorf("need to specify non-zero MIN_CENTRALIZED_BASE_VOLUME config param in trader config file when not trading on SDEX"))
}
}

func init() {
Expand Down Expand Up @@ -283,12 +287,17 @@ func makeBot(
if e != nil {
l.Infof("Unable to set up monitoring for alert type '%s' with the given API key\n", botConfig.AlertType)
}
minCentralizedBaseVolume := &botConfig.MinCentralizedBaseVolume
if botConfig.IsTradingSdex() {
minCentralizedBaseVolume = nil
}
bot := trader.MakeBot(
client,
ieif,
botConfig.AssetBase(),
botConfig.AssetQuote(),
tradingPair,
minCentralizedBaseVolume,
botConfig.TradingAccount(),
sdex,
exchangeShim,
Expand Down
5 changes: 5 additions & 0 deletions examples/configs/trader/sample_mirror.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ VOLUME_DIVIDE_BY=500.0
# in this example the spread is 0.5%
PER_LEVEL_SPREAD=0.005

# minimum volume of base units needed to place an order on the backing exchange
# minimum values for Kraken: https://support.kraken.com/hc/en-us/articles/205893708-What-is-the-minimum-order-size-volume-
# minimum order value for Binance: https://support.binance.com/hc/en-us/articles/115000594711-Trading-Rule
MIN_BASE_VOLUME=30.0

# set to true if you want the bot to offset your trades onto the backing exchange to realize the per_level_spread against each trade
# requires you to specify the EXCHANGE_API_KEYS below
#OFFSET_TRADES=true
Expand Down
4 changes: 4 additions & 0 deletions examples/configs/trader/sample_trader.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ MAX_OP_FEE_STROOPS=5000
# Google authentication.
#ACCEPTABLE_GOOGLE_EMAILS=""

# minimum volume of base units needed to place an order on the non-sdex (centralized) exchange
# minimum values for Kraken: https://support.kraken.com/hc/en-us/articles/205893708-What-is-the-minimum-order-size-volume-
# minimum order value for Binance: https://support.binance.com/hc/en-us/articles/115000594711-Trading-Rule
#MIN_CENTRALIZED_BASE_VOLUME=30.0
# uncomment lines below to use kraken. Can use "sdex" or leave out to trade on the Stellar Decentralized Exchange.
# can alternatively use any of the ccxt-exchanges marked as "Trading" (run `kelp exchanges` for full list)
#TRADING_EXCHANGE="kraken"
Expand Down
48 changes: 48 additions & 0 deletions plugins/ccxtExchange_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,3 +440,51 @@ func TestCancelOrder_Ccxt(t *testing.T) {
}
}
}

func TestGetOrderConstraints_Ccxt_Precision(t *testing.T) {
testCases := []struct {
exchangeName string
pair *model.TradingPair
wantPricePrecision int8
wantVolPrecision int8
}{
{
exchangeName: "kraken",
pair: &model.TradingPair{Base: model.XLM, Quote: model.USD},
wantPricePrecision: 6,
wantVolPrecision: 8,
}, {
exchangeName: "kraken",
pair: &model.TradingPair{Base: model.XLM, Quote: model.BTC},
wantPricePrecision: 8,
wantVolPrecision: 8,
}, {
exchangeName: "binance",
pair: &model.TradingPair{Base: model.XLM, Quote: model.USDT},
wantPricePrecision: 5,
wantVolPrecision: 1,
}, {
exchangeName: "binance",
pair: &model.TradingPair{Base: model.XLM, Quote: model.BTC},
wantPricePrecision: 8,
wantVolPrecision: 0,
},
}

for _, kase := range testCases {
t.Run(kase.exchangeName, func(t *testing.T) {
testCcxtExchange, e := makeCcxtExchange(kase.exchangeName, nil, []api.ExchangeAPIKey{emptyAPIKey}, false)
if !assert.NoError(t, e) {
return
}

result := testCcxtExchange.GetOrderConstraints(kase.pair)
if !assert.Equal(t, kase.wantPricePrecision, result.PricePrecision) {
return
}
if !assert.Equal(t, kase.wantVolPrecision, result.VolumePrecision) {
return
}
})
}
}
16 changes: 14 additions & 2 deletions plugins/mirrorStrategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type mirrorConfig struct {
OrderbookDepth int32 `valid:"-" toml:"ORDERBOOK_DEPTH"`
VolumeDivideBy float64 `valid:"-" toml:"VOLUME_DIVIDE_BY"`
PerLevelSpread float64 `valid:"-" toml:"PER_LEVEL_SPREAD"`
MinBaseVolume float64 `valid:"-" toml:"MIN_BASE_VOLUME"`
OffsetTrades bool `valid:"-" toml:"OFFSET_TRADES"`
ExchangeAPIKeys exchangeAPIKeysToml `valid:"-" toml:"EXCHANGE_API_KEYS"`
}
Expand Down Expand Up @@ -100,6 +101,10 @@ func makeMirrorStrategy(sdex *SDEX, ieif *IEIF, pair *model.TradingPair, baseAss
if e != nil {
return nil, e
}

if config.MinBaseVolume == 0.0 {
return nil, fmt.Errorf("need to specify non-zero MIN_BASE_VOLUME config param in mirror strategy config file")
}
} else {
exchange, e = MakeExchange(config.Exchange, simMode)
if e != nil {
Expand All @@ -115,6 +120,11 @@ func makeMirrorStrategy(sdex *SDEX, ieif *IEIF, pair *model.TradingPair, baseAss
Quote: exchange.GetAssetConverter().MustFromString(config.ExchangeQuote),
}
backingConstraints := exchange.GetOrderConstraints(backingPair)
if config.OffsetTrades {
backingConstraints.MinBaseVolume = *model.NumberFromFloat(config.MinBaseVolume, backingConstraints.VolumePrecision)
}
log.Printf("primaryPair='%s', primaryConstraints=%s\n", pair, primaryConstraints)
log.Printf("backingPair='%s', backingConstraints=%s\n", backingPair, backingConstraints)
return &mirrorStrategy{
sdex: sdex,
ieif: ieif,
Expand Down Expand Up @@ -457,14 +467,15 @@ func (s *mirrorStrategy) HandleFill(trade model.Trade) error {
Volume: newVolume,
Timestamp: nil,
}
log.Printf("offset-attempt | tradeID=%s | tradeBaseAmt=%f | tradeQuoteAmt=%f | tradePriceQuote=%f | newOrderAction=%s | baseSurplusTotal=%f | baseSurplusCommitted=%f | newOrderBaseAmt=%f | newOrderQuoteAmt=%f | newOrderPriceQuote=%f\n",
log.Printf("offset-attempt | tradeID=%s | tradeBaseAmt=%f | tradeQuoteAmt=%f | tradePriceQuote=%f | newOrderAction=%s | baseSurplusTotal=%f | baseSurplusCommitted=%f | minBaseVolume=%f | newOrderBaseAmt=%f | newOrderQuoteAmt=%f | newOrderPriceQuote=%f\n",
trade.TransactionID.String(),
trade.Volume.AsFloat(),
trade.Volume.Multiply(*trade.Price).AsFloat(),
trade.Price.AsFloat(),
newOrderAction.String(),
s.baseSurplus[newOrderAction].total.AsFloat(),
s.baseSurplus[newOrderAction].committed.AsFloat(),
s.backingConstraints.MinBaseVolume.AsFloat(),
newOrder.Volume.AsFloat(),
newOrder.Volume.Multiply(*newOrder.Price).AsFloat(),
newOrder.Price.AsFloat())
Expand All @@ -480,14 +491,15 @@ func (s *mirrorStrategy) HandleFill(trade model.Trade) error {
s.baseSurplus[newOrderAction].total = s.baseSurplus[newOrderAction].total.Subtract(*newVolume)
s.baseSurplus[newOrderAction].committed = s.baseSurplus[newOrderAction].committed.Subtract(*newVolume)

log.Printf("offset-success | tradeID=%s | tradeBaseAmt=%f | tradeQuoteAmt=%f | tradePriceQuote=%f | newOrderAction=%s | baseSurplusTotal=%f | baseSurplusCommitted=%f | newOrderBaseAmt=%f | newOrderQuoteAmt=%f | newOrderPriceQuote=%f | transactionID=%s\n",
log.Printf("offset-success | tradeID=%s | tradeBaseAmt=%f | tradeQuoteAmt=%f | tradePriceQuote=%f | newOrderAction=%s | baseSurplusTotal=%f | baseSurplusCommitted=%f | minBaseVolume=%f | newOrderBaseAmt=%f | newOrderQuoteAmt=%f | newOrderPriceQuote=%f | transactionID=%s\n",
trade.TransactionID.String(),
trade.Volume.AsFloat(),
trade.Volume.Multiply(*trade.Price).AsFloat(),
trade.Price.AsFloat(),
newOrderAction.String(),
s.baseSurplus[newOrderAction].total.AsFloat(),
s.baseSurplus[newOrderAction].committed.AsFloat(),
s.backingConstraints.MinBaseVolume.AsFloat(),
newOrder.Volume.AsFloat(),
newOrder.Volume.Multiply(*newOrder.Price).AsFloat(),
newOrder.Price.AsFloat(),
Expand Down
1 change: 1 addition & 0 deletions trader/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type BotConfig struct {
FillTrackerDeleteCyclesThreshold int64 `valid:"-" toml:"FILL_TRACKER_DELETE_CYCLES_THRESHOLD"`
HorizonURL string `valid:"-" toml:"HORIZON_URL"`
Fee *FeeConfig `valid:"-" toml:"FEE"`
MinCentralizedBaseVolume float64 `valid:"-" toml:"MIN_CENTRALIZED_BASE_VOLUME"`
AlertType string `valid:"-" toml:"ALERT_TYPE"`
AlertAPIKey string `valid:"-" toml:"ALERT_API_KEY"`
MonitoringPort uint16 `valid:"-" toml:"MONITORING_PORT"`
Expand Down
4 changes: 4 additions & 0 deletions trader/trader.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func MakeBot(
assetBase horizon.Asset,
assetQuote horizon.Asset,
tradingPair *model.TradingPair,
minBaseVolume *float64,
tradingAccount string,
sdex *plugins.SDEX,
exchangeShim api.ExchangeShim,
Expand All @@ -70,6 +71,9 @@ func MakeBot(
submitFilters := []plugins.SubmitFilter{}

oc := exchangeShim.GetOrderConstraints(tradingPair)
if minBaseVolume != nil {
oc.MinBaseVolume = *model.NumberFromFloat(*minBaseVolume, oc.VolumePrecision)
}
orderConstraintsFilter := plugins.MakeFilterOrderConstraints(oc, assetBase, assetQuote)
submitFilters = append(submitFilters, orderConstraintsFilter)

Expand Down

0 comments on commit 0576aa1

Please sign in to comment.