Skip to content

Commit

Permalink
Add dynamic loading/unloading and reloading of exchanges
Browse files Browse the repository at this point in the history
  • Loading branch information
thrasher- committed Jan 16, 2018
1 parent 34eeed2 commit 4d4c85f
Show file tree
Hide file tree
Showing 10 changed files with 467 additions and 183 deletions.
224 changes: 224 additions & 0 deletions exchange.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
package main

import (
"errors"
"log"

"github.com/thrasher-/gocryptotrader/common"
exchange "github.com/thrasher-/gocryptotrader/exchanges"
"github.com/thrasher-/gocryptotrader/exchanges/anx"
"github.com/thrasher-/gocryptotrader/exchanges/bitfinex"
"github.com/thrasher-/gocryptotrader/exchanges/bitstamp"
"github.com/thrasher-/gocryptotrader/exchanges/bittrex"
"github.com/thrasher-/gocryptotrader/exchanges/btcc"
"github.com/thrasher-/gocryptotrader/exchanges/btcmarkets"
"github.com/thrasher-/gocryptotrader/exchanges/coinut"
"github.com/thrasher-/gocryptotrader/exchanges/gdax"
"github.com/thrasher-/gocryptotrader/exchanges/huobi"
"github.com/thrasher-/gocryptotrader/exchanges/itbit"
"github.com/thrasher-/gocryptotrader/exchanges/kraken"
"github.com/thrasher-/gocryptotrader/exchanges/lakebtc"
"github.com/thrasher-/gocryptotrader/exchanges/liqui"
"github.com/thrasher-/gocryptotrader/exchanges/localbitcoins"
"github.com/thrasher-/gocryptotrader/exchanges/okcoin"
"github.com/thrasher-/gocryptotrader/exchanges/poloniex"
"github.com/thrasher-/gocryptotrader/exchanges/wex"
)

// vars related to exchange functions
var (
ErrNoExchangesLoaded = errors.New("no exchanges have been loaded")
ErrExchangeNotFound = errors.New("exchange not found")
ErrExchangeAlreadyLoaded = errors.New("exchange already loaded")
ErrExchangeFailedToLoad = errors.New("exchange failed to load")
)

// CheckExchangeExists returns true whether or not an exchange has already
// been loaded
func CheckExchangeExists(exchName string) bool {
for x := range bot.exchanges {
if common.StringToLower(bot.exchanges[x].GetName()) == common.StringToLower(exchName) {
return true
}
}
return false
}

// GetExchangeByName returns an exchange given an exchange name
func GetExchangeByName(exchName string) exchange.IBotExchange {
for x := range bot.exchanges {
if common.StringToLower(bot.exchanges[x].GetName()) == common.StringToLower(exchName) {
return bot.exchanges[x]
}
}
return nil
}

// ReloadExchange loads an exchange config by name
func ReloadExchange(name string) error {
nameLower := common.StringToLower(name)

if len(bot.exchanges) == 0 {
return ErrNoExchangesLoaded
}

if !CheckExchangeExists(nameLower) {
return ErrExchangeNotFound
}

exchCfg, err := bot.config.GetExchangeConfig(name)
if err != nil {
return err
}

e := GetExchangeByName(nameLower)
e.Setup(exchCfg)
log.Printf("%s exchange reloaded successfully.\n", name)
return nil
}

// UnloadExchange unloads an exchange by
func UnloadExchange(name string) error {
nameLower := common.StringToLower(name)

if len(bot.exchanges) == 0 {
return ErrNoExchangesLoaded
}

if !CheckExchangeExists(nameLower) {
return ErrExchangeNotFound
}

exchCfg, err := bot.config.GetExchangeConfig(name)
if err != nil {
return err
}

exchCfg.Enabled = false
err = bot.config.UpdateExchangeConfig(exchCfg)
if err != nil {
return err
}

for x := range bot.exchanges {
if bot.exchanges[x].GetName() == name {
bot.exchanges[x].SetEnabled(false)
bot.exchanges = append(bot.exchanges[:x], bot.exchanges[x+1:]...)
return nil
}
}

return ErrExchangeNotFound
}

// LoadExchange loads an exchange by name
func LoadExchange(name string) error {
nameLower := common.StringToLower(name)
var exch exchange.IBotExchange

if len(bot.exchanges) > 0 {
if CheckExchangeExists(nameLower) {
return ErrExchangeAlreadyLoaded
}
}

switch nameLower {
case "anx":
exch = new(anx.ANX)
case "bitfinex":
exch = new(bitfinex.Bitfinex)
case "bitstamp":
exch = new(bitstamp.Bitstamp)
case "bittrex":
exch = new(bittrex.Bittrex)
case "btcc":
exch = new(btcc.BTCC)
case "btc markets":
exch = new(btcmarkets.BTCMarkets)
case "coinut":
exch = new(coinut.COINUT)
case "gdax":
exch = new(gdax.GDAX)
case "gemini":
exch = new(gdax.GDAX)
case "huobi":
exch = new(huobi.HUOBI)
case "itbit":
exch = new(itbit.ItBit)
case "kraken":
exch = new(kraken.Kraken)
case "lakebtc":
exch = new(lakebtc.LakeBTC)
case "liqui":
exch = new(liqui.Liqui)
case "localbitcoins":
exch = new(localbitcoins.LocalBitcoins)
case "okcoin china":
exch = new(okcoin.OKCoin)
case "okcoin international":
exch = new(okcoin.OKCoin)
case "poloniex":
exch = new(poloniex.Poloniex)
case "wex":
exch = new(wex.WEX)
default:
return ErrExchangeNotFound
}

if exch == nil {
return ErrExchangeFailedToLoad
}

exch.SetDefaults()
bot.exchanges = append(bot.exchanges, exch)
exchCfg, err := bot.config.GetExchangeConfig(name)
if err != nil {
return err
}

exchCfg.Enabled = true
exch.Setup(exchCfg)
return nil
}

// SetupExchanges sets up the exchanges used by the bot
func SetupExchanges() {
for _, exch := range bot.config.Exchanges {
if CheckExchangeExists(exch.Name) {
e := GetExchangeByName(exch.Name)
if e == nil {
log.Println(ErrExchangeNotFound)
continue
}

err := ReloadExchange(exch.Name)
if err != nil {
log.Printf("ReloadExchange %s failed: %s", exch.Name, err)
continue
}

if !e.IsEnabled() {
UnloadExchange(exch.Name)
continue
}
return

}
if !exch.Enabled {
log.Printf("%s: Exchange support: Disabled", exch.Name)
continue
} else {
err := LoadExchange(exch.Name)
if err != nil {
log.Printf("LoadExchange %s failed: %s", exch.Name, err)
continue
}
}
log.Printf(
"%s: Exchange support: Enabled (Authenticated API support: %s - Verbose mode: %s).\n",
exch.Name,
common.IsEnabled(exch.AuthenticatedAPISupport),
common.IsEnabled(exch.Verbose),
)
}
}
138 changes: 138 additions & 0 deletions exchange_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package main

import (
"testing"

"github.com/thrasher-/gocryptotrader/config"
)

var testSetup = false

func SetupTest(t *testing.T) {
if !testSetup {
bot.config = &config.Cfg
err := bot.config.LoadConfig("./testdata/configtest.json")
if err != nil {
t.Fatalf("Test failed. SetupTest: Failed to load config: %s", err)
}
testSetup = true
}

if CheckExchangeExists("Bitfinex") {
return
}
err := LoadExchange("Bitfinex")
if err != nil {
t.Errorf("Test failed. SetupTest: Failed to load exchange: %s", err)
}
}

func CleanupTest(t *testing.T) {
if !CheckExchangeExists("Bitfinex") {
return
}

err := UnloadExchange("Bitfinex")
if err != nil {
t.Fatalf("Test failed. CleanupTest: Failed to unload exchange: %s",
err)
}
}

func TestCheckExchangeExists(t *testing.T) {
SetupTest(t)

if !CheckExchangeExists("Bitfinex") {
t.Errorf("Test failed. TestGetExchangeExists: Unable to find exchange")
}

if CheckExchangeExists("Asdsad") {
t.Errorf("Test failed. TestGetExchangeExists: Non-existant exchange found")
}

CleanupTest(t)
}

func TestGetExchangeByName(t *testing.T) {
SetupTest(t)

exch := GetExchangeByName("Bitfinex")
if exch == nil {
t.Errorf("Test failed. TestGetExchangeByName: Failed to get exchange")
}

if !exch.IsEnabled() {
t.Errorf("Test failed. TestGetExchangeByName: Unexpected result")
}

exch.SetEnabled(false)
bfx := GetExchangeByName("Bitfinex")
if bfx.IsEnabled() {
t.Errorf("Test failed. TestGetExchangeByName: Unexpected result")
}

if exch.GetName() != "Bitfinex" {
t.Errorf("Test failed. TestGetExchangeByName: Unexpected result")
}

exch = GetExchangeByName("Asdasd")
if exch != nil {
t.Errorf("Test failed. TestGetExchangeByName: Non-existant exchange found")
}

CleanupTest(t)
}

func TestReloadExchange(t *testing.T) {
SetupTest(t)

err := ReloadExchange("asdf")
if err != ErrExchangeNotFound {
t.Errorf("Test failed. TestReloadExchange: Incorrect result: %s",
err)
}

err = ReloadExchange("Bitfinex")
if err != nil {
t.Errorf("Test failed. TestReloadExchange: Incorrect result: %s",
err)
}

CleanupTest(t)

err = ReloadExchange("asdf")
if err != ErrNoExchangesLoaded {
t.Errorf("Test failed. TestReloadExchange: Incorrect result: %s",
err)
}
}

func TestUnloadExchange(t *testing.T) {
SetupTest(t)

err := UnloadExchange("asdf")
if err != ErrExchangeNotFound {
t.Errorf("Test failed. TestUnloadExchange: Incorrect result: %s",
err)
}

err = UnloadExchange("Bitfinex")
if err != nil {
t.Errorf("Test failed. TestUnloadExchange: Failed to get exchange. %s",
err)
}

err = UnloadExchange("asdf")
if err != ErrNoExchangesLoaded {
t.Errorf("Test failed. TestUnloadExchange: Incorrect result: %s",
err)
}

CleanupTest(t)
}

func TestSetupExchanges(t *testing.T) {
SetupTest(t)
SetupExchanges()
CleanupTest(t)
}
1 change: 1 addition & 0 deletions exchanges/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type IBotExchange interface {
SetDefaults()
GetName() string
IsEnabled() bool
SetEnabled(bool)
GetTickerPrice(currency pair.CurrencyPair, assetType string) (ticker.Price, error)
UpdateTicker(currency pair.CurrencyPair, assetType string) (ticker.Price, error)
GetOrderbookEx(currency pair.CurrencyPair, assetType string) (orderbook.Base, error)
Expand Down
2 changes: 1 addition & 1 deletion exchanges/okcoin/okcoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (o *OKCoin) SetDefaults() {
o.FuturesValues = []string{"this_week", "next_week", "quarter"}
o.AssetTypes = []string{ticker.Spot}

if !okcoinDefaultsSet {
if okcoinDefaultsSet {
o.AssetTypes = append(o.AssetTypes, o.FuturesValues...)
o.APIUrl = OKCOIN_API_URL
o.Name = "OKCOIN International"
Expand Down
Loading

0 comments on commit 4d4c85f

Please sign in to comment.