Skip to content

Commit

Permalink
offset_trades: add EXCHANGE_API_KEYS to mirror strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilsaraf committed Dec 3, 2018
1 parent 0460d4a commit 7b6e07b
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 44 deletions.
17 changes: 9 additions & 8 deletions cmd/exchanges.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cmd

import (
"fmt"
"sort"

"github.com/interstellar/kelp/plugins"

Expand All @@ -16,20 +15,22 @@ var exchanagesCmd = &cobra.Command{

func init() {
exchanagesCmd.Run = func(ccmd *cobra.Command, args []string) {
fmt.Printf(" Exchange\tDescription\n")
fmt.Printf(" Exchange\t\tSupports Trading\tDescription\n")
fmt.Printf(" --------------------------------------------------------------------------------\n")
exchanges := plugins.Exchanges()
for _, name := range sortedExchangeKeys(exchanges) {
fmt.Printf(" %-14s%s\n", name, exchanges[name])
fmt.Printf(" %-14s\t%v\t\t\t%s\n", name, exchanges[name].TradeEnabled, exchanges[name].Description)
}
}
}

func sortedExchangeKeys(m map[string]string) []string {
keys := []string{}
for name := range m {
keys = append(keys, name)
func sortedExchangeKeys(m map[string]plugins.ExchangeContainer) []string {
keys := make([]string, len(m))
for k, v := range m {
if len(keys[v.SortOrder]) > 0 && keys[v.SortOrder] != k {
panic(fmt.Errorf("invalid sort order specified for strategies, SortOrder that was repeated: %d", v.SortOrder))
}
keys[v.SortOrder] = k
}
sort.Strings(keys)
return keys
}
7 changes: 6 additions & 1 deletion examples/configs/trader/sample_mirror.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,9 @@ VOLUME_DIVIDE_BY=500.0
PER_LEVEL_SPREAD=0.005

# 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
#OFFSET_TRADES=true
# requires you to specify the EXCHANGE_API_KEYS below
#OFFSET_TRADES=true
# you can use multiple API keys to overcome rate limit concerns
#[[EXCHANGE_API_KEYS]]
#KEY=""
#SECRET=""
87 changes: 62 additions & 25 deletions plugins/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,35 +124,50 @@ func Strategies() map[string]StrategyContainer {
return strategies
}

type exchangeContainer struct {
description string
makeFn func() (api.Exchange, error)
// exchangeFactoryData is a data container that has all the information needed to make an exchange
type exchangeFactoryData struct {
apiKeys []api.ExchangeAPIKey
}

// ExchangeContainer contains the exchange factory method along with some metadata
type ExchangeContainer struct {
SortOrder uint8
Description string
TradeEnabled bool
makeFn func(exchangeFactoryData exchangeFactoryData) (api.Exchange, error)
}

// exchanges is a map of all the exchange integrations available
var exchanges = map[string]exchangeContainer{
"kraken": exchangeContainer{
description: "Kraken is a popular centralized cryptocurrency exchange (https://www.kraken.com/)",
makeFn: func() (api.Exchange, error) {
apiKey := api.ExchangeAPIKey{Key: "", Secret: ""}
return makeKrakenExchange([]api.ExchangeAPIKey{apiKey})
var exchanges = map[string]ExchangeContainer{
"kraken": ExchangeContainer{
SortOrder: 0,
Description: "Kraken is a popular centralized cryptocurrency exchange (https://www.kraken.com/)",
TradeEnabled: true,
makeFn: func(exchangeFactoryData exchangeFactoryData) (api.Exchange, error) {
return makeKrakenExchange(exchangeFactoryData.apiKeys)
},
},
"ccxt-binance": exchangeContainer{
description: "Binance is a popular centralized cryptocurrency exchange (via ccxt-rest) - partial implementation",
makeFn: func() (api.Exchange, error) {
"ccxt-binance": ExchangeContainer{
SortOrder: 1,
Description: "Binance is a popular centralized cryptocurrency exchange (via ccxt-rest)",
TradeEnabled: false,
makeFn: func(exchangeFactoryData exchangeFactoryData) (api.Exchange, error) {
return makeCcxtExchange("http://localhost:3000", "binance")
},
},
"ccxt-poloniex": exchangeContainer{
description: "Poloniex is a popular centralized cryptocurrency exchange (via ccxt-rest) - partial implementation",
makeFn: func() (api.Exchange, error) {
"ccxt-poloniex": ExchangeContainer{
SortOrder: 2,
Description: "Poloniex is a popular centralized cryptocurrency exchange (via ccxt-rest)",
TradeEnabled: false,
makeFn: func(exchangeFactoryData exchangeFactoryData) (api.Exchange, error) {
return makeCcxtExchange("http://localhost:3000", "poloniex")
},
},
"ccxt-bittrex": exchangeContainer{
description: "Bittrex is a popular centralized cryptocurrency exchange (via ccxt-rest) - partial implementation",
makeFn: func() (api.Exchange, error) {
"ccxt-bittrex": ExchangeContainer{
SortOrder: 3,
Description: "Bittrex is a popular centralized cryptocurrency exchange (via ccxt-rest)",
TradeEnabled: false,
makeFn: func(exchangeFactoryData exchangeFactoryData) (api.Exchange, error) {
return makeCcxtExchange("http://localhost:3000", "bittrex")
},
},
Expand All @@ -161,7 +176,10 @@ var exchanges = map[string]exchangeContainer{
// MakeExchange is a factory method to make an exchange based on a given type
func MakeExchange(exchangeType string) (api.Exchange, error) {
if exchange, ok := exchanges[exchangeType]; ok {
x, e := exchange.makeFn()
exchangeAPIKey := api.ExchangeAPIKey{Key: "", Secret: ""}
x, e := exchange.makeFn(exchangeFactoryData{
apiKeys: []api.ExchangeAPIKey{exchangeAPIKey},
})
if e != nil {
return nil, fmt.Errorf("error when making the '%s' exchange: %s", exchangeType, e)
}
Expand All @@ -171,11 +189,30 @@ func MakeExchange(exchangeType string) (api.Exchange, error) {
return nil, fmt.Errorf("invalid exchange type: %s", exchangeType)
}

// Exchanges returns the list of exchanges along with the description
func Exchanges() map[string]string {
m := make(map[string]string, len(exchanges))
for name := range exchanges {
m[name] = exchanges[name].description
// MakeTradingExchange is a factory method to make an exchange based on a given type
func MakeTradingExchange(exchangeType string, apiKeys []api.ExchangeAPIKey) (api.Exchange, error) {
if exchange, ok := exchanges[exchangeType]; ok {
if !exchange.TradeEnabled {
return nil, fmt.Errorf("trading is not enabled on this exchange: %s", exchangeType)
}

if len(apiKeys) == 0 {
return nil, fmt.Errorf("cannot make trading exchange, apiKeys mising")
}

x, e := exchange.makeFn(exchangeFactoryData{
apiKeys: apiKeys,
})
if e != nil {
return nil, fmt.Errorf("error when making the '%s' exchange: %s", exchangeType, e)
}
return x, nil
}
return m

return nil, fmt.Errorf("invalid exchange type: %s", exchangeType)
}

// Exchanges returns the list of exchanges
func Exchanges() map[string]ExchangeContainer {
return exchanges
}
47 changes: 37 additions & 10 deletions plugins/mirrorStrategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,32 @@ import (
"github.com/stellar/go/clients/horizon"
)

type exchangeAPIKeysToml []struct {
Key string `valid:"-" toml:"KEY"`
Secret string `valid:"-" toml:"SECRET"`
}

func (t *exchangeAPIKeysToml) toExchangeAPIKeys() []api.ExchangeAPIKey {
apiKeys := []api.ExchangeAPIKey{}
for _, apiKey := range *t {
apiKeys = append(apiKeys, api.ExchangeAPIKey{
Key: apiKey.Key,
Secret: apiKey.Secret,
})
}
return apiKeys
}

// mirrorConfig contains the configuration params for this strategy
type mirrorConfig struct {
Exchange string `valid:"-" toml:"EXCHANGE"`
ExchangeBase string `valid:"-" toml:"EXCHANGE_BASE"`
ExchangeQuote string `valid:"-" toml:"EXCHANGE_QUOTE"`
OrderbookDepth int32 `valid:"-" toml:"ORDERBOOK_DEPTH"`
VolumeDivideBy float64 `valid:"-" toml:"VOLUME_DIVIDE_BY"`
PerLevelSpread float64 `valid:"-" toml:"PER_LEVEL_SPREAD"`
OffsetTrades bool `valid:"-" toml:"OFFSET_TRADES"`
Exchange string `valid:"-" toml:"EXCHANGE"`
ExchangeBase string `valid:"-" toml:"EXCHANGE_BASE"`
ExchangeQuote string `valid:"-" toml:"EXCHANGE_QUOTE"`
OrderbookDepth int32 `valid:"-" toml:"ORDERBOOK_DEPTH"`
VolumeDivideBy float64 `valid:"-" toml:"VOLUME_DIVIDE_BY"`
PerLevelSpread float64 `valid:"-" toml:"PER_LEVEL_SPREAD"`
OffsetTrades bool `valid:"-" toml:"OFFSET_TRADES"`
ExchangeAPIKeys exchangeAPIKeysToml `valid:"-" toml:"EXCHANGE_API_KEYS"`
}

// String impl.
Expand All @@ -46,9 +63,19 @@ var _ api.FillHandler = &mirrorStrategy{}

// makeMirrorStrategy is a factory method
func makeMirrorStrategy(sdex *SDEX, baseAsset *horizon.Asset, quoteAsset *horizon.Asset, config *mirrorConfig) (api.Strategy, error) {
exchange, e := MakeExchange(config.Exchange)
if e != nil {
return nil, e
var exchange api.Exchange
var e error
if config.OffsetTrades {
exchangeAPIKeys := config.ExchangeAPIKeys.toExchangeAPIKeys()
exchange, e = MakeTradingExchange(config.Exchange, exchangeAPIKeys)
if e != nil {
return nil, e
}
} else {
exchange, e = MakeExchange(config.Exchange)
if e != nil {
return nil, e
}
}

orderbookPair := &model.TradingPair{
Expand Down

0 comments on commit 7b6e07b

Please sign in to comment.