mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
Polish websocket code
This commit is contained in:
@@ -413,6 +413,22 @@ func (c *Config) LoadConfig(configPath string) error {
|
||||
return fmt.Errorf(ErrCheckingConfigValues, err)
|
||||
}
|
||||
|
||||
if c.SMS.Enabled {
|
||||
err = c.CheckSMSGlobalConfigValues()
|
||||
if err != nil {
|
||||
log.Print(fmt.Errorf(ErrCheckingConfigValues, err))
|
||||
c.SMS.Enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
if c.Webserver.Enabled {
|
||||
err = c.CheckWebserverConfigValues()
|
||||
if err != nil {
|
||||
log.Print(fmt.Errorf(ErrCheckingConfigValues, err))
|
||||
c.Webserver.Enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
if c.CurrencyPairFormat == nil {
|
||||
c.CurrencyPairFormat = &CurrencyPairFormatConfig{
|
||||
Delimiter: "-",
|
||||
@@ -423,6 +439,43 @@ func (c *Config) LoadConfig(configPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateConfig updates the config with a supplied config file
|
||||
func (c *Config) UpdateConfig(configPath string, newCfg Config) error {
|
||||
if c.Name != newCfg.Name && newCfg.Name != "" {
|
||||
c.Name = newCfg.Name
|
||||
}
|
||||
|
||||
err := newCfg.CheckExchangeConfigValues()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Exchanges = newCfg.Exchanges
|
||||
|
||||
if c.CurrencyPairFormat != newCfg.CurrencyPairFormat {
|
||||
c.CurrencyPairFormat = newCfg.CurrencyPairFormat
|
||||
}
|
||||
|
||||
c.Portfolio = newCfg.Portfolio
|
||||
|
||||
err = newCfg.CheckSMSGlobalConfigValues()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.SMS = newCfg.SMS
|
||||
|
||||
err = c.SaveConfig(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.LoadConfig(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetConfig returns a pointer to a confiuration object
|
||||
func GetConfig() *Config {
|
||||
return &Cfg
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
)
|
||||
|
||||
// GetAllSettings replies to a request with an encoded JSON response about the
|
||||
// trading bots configuration.
|
||||
func GetAllSettings(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(bot.config); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// SaveAllSettings saves all current settings from request body as a JSON
|
||||
// document then reloads state and returns the settings
|
||||
func SaveAllSettings(w http.ResponseWriter, r *http.Request) {
|
||||
//Get the data from the request
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var responseData config.Post
|
||||
jsonerr := decoder.Decode(&responseData)
|
||||
if jsonerr != nil {
|
||||
panic(jsonerr)
|
||||
}
|
||||
//Save change the settings
|
||||
for x := range bot.config.Exchanges {
|
||||
for i := 0; i < len(responseData.Data.Exchanges); i++ {
|
||||
if responseData.Data.Exchanges[i].Name == bot.config.Exchanges[x].Name {
|
||||
bot.config.Exchanges[x].Enabled = responseData.Data.Exchanges[i].Enabled
|
||||
bot.config.Exchanges[x].APIKey = responseData.Data.Exchanges[i].APIKey
|
||||
bot.config.Exchanges[x].APISecret = responseData.Data.Exchanges[i].APISecret
|
||||
bot.config.Exchanges[x].EnabledPairs = responseData.Data.Exchanges[i].EnabledPairs
|
||||
}
|
||||
}
|
||||
}
|
||||
//Reload the configuration
|
||||
err := bot.config.SaveConfig(bot.configFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = bot.config.LoadConfig(bot.configFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
setupBotExchanges()
|
||||
//Return response status
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(bot.config); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigRoutes declares the current routes for config_routes.go
|
||||
var ConfigRoutes = Routes{
|
||||
Route{
|
||||
"GetAllSettings",
|
||||
"GET",
|
||||
"/config/all",
|
||||
GetAllSettings,
|
||||
},
|
||||
|
||||
Route{
|
||||
"SaveAllSettings",
|
||||
"POST",
|
||||
"/config/all/save",
|
||||
SaveAllSettings,
|
||||
},
|
||||
}
|
||||
@@ -68,7 +68,7 @@ var (
|
||||
ErrCurrencyNotFound = errors.New("unable to find specified currency")
|
||||
ErrQueryingYahoo = errors.New("unable to query Yahoo currency values")
|
||||
ErrQueryingYahooZeroCount = errors.New("yahoo returned zero currency data")
|
||||
yahooEnabled = false
|
||||
YahooEnabled = false
|
||||
)
|
||||
|
||||
// IsDefaultCurrency checks if the currency passed in matches the default
|
||||
@@ -182,7 +182,7 @@ func SeedCurrencyData(fiatCurrencies string) error {
|
||||
fiatCurrencies = DefaultCurrencies
|
||||
}
|
||||
|
||||
if yahooEnabled {
|
||||
if YahooEnabled {
|
||||
return QueryYahooCurrencyValues(fiatCurrencies)
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ func ConvertCurrency(amount float64, from, to string) (float64, error) {
|
||||
return amount, nil
|
||||
}
|
||||
|
||||
if yahooEnabled {
|
||||
if YahooEnabled {
|
||||
currency := from + to
|
||||
_, ok := CurrencyStore[currency]
|
||||
if !ok {
|
||||
|
||||
@@ -264,7 +264,7 @@ func TestCheckAndAddCurrency(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSeedCurrencyData(t *testing.T) {
|
||||
if yahooEnabled {
|
||||
if YahooEnabled {
|
||||
currencyRequestDefault := ""
|
||||
currencyRequestUSDAUD := "USD,AUD"
|
||||
currencyRequestObtuse := "WigWham"
|
||||
@@ -292,7 +292,7 @@ func TestSeedCurrencyData(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
yahooEnabled = false
|
||||
YahooEnabled = false
|
||||
err := SeedCurrencyData("")
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. SeedCurrencyData via Fixer. Error: %s", err)
|
||||
@@ -313,7 +313,7 @@ func TestMakecurrencyPairs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConvertCurrency(t *testing.T) {
|
||||
if yahooEnabled {
|
||||
if YahooEnabled {
|
||||
fiatCurrencies := DefaultCurrencies
|
||||
for _, currencyFrom := range common.SplitStrings(fiatCurrencies, ",") {
|
||||
for _, currencyTo := range common.SplitStrings(fiatCurrencies, ",") {
|
||||
@@ -336,7 +336,7 @@ func TestConvertCurrency(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
yahooEnabled = false
|
||||
YahooEnabled = false
|
||||
_, err := ConvertCurrency(1000, "USD", "AUD")
|
||||
if err != nil {
|
||||
t.Errorf("Test failed. ConvertCurrency USD -> AUD. Error %s", err)
|
||||
@@ -383,7 +383,7 @@ func TestFetchFixerCurrencyData(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFetchYahooCurrencyData(t *testing.T) {
|
||||
if !yahooEnabled {
|
||||
if !YahooEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -407,7 +407,7 @@ func TestFetchYahooCurrencyData(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestQueryYahooCurrencyValues(t *testing.T) {
|
||||
if !yahooEnabled {
|
||||
if !YahooEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -2,40 +2,42 @@ package events
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func TestAddEvent(t *testing.T) {
|
||||
eventID, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", "SPOT", actionTest)
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
eventID, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil && eventID != 0 {
|
||||
t.Errorf("Test Failed. AddEvent: Error, %s", err)
|
||||
}
|
||||
eventID, err = AddEvent("ANXX", "price", ">,==", "BTC", "LTC", "SPOT", actionTest)
|
||||
eventID, err = AddEvent("ANXX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err == nil && eventID == 0 {
|
||||
t.Error("Test Failed. AddEvent: Error, error not captured in Exchange")
|
||||
}
|
||||
eventID, err = AddEvent("ANX", "prices", ">,==", "BTC", "LTC", "SPOT", actionTest)
|
||||
eventID, err = AddEvent("ANX", "prices", ">,==", pair, "SPOT", actionTest)
|
||||
if err == nil && eventID == 0 {
|
||||
t.Error("Test Failed. AddEvent: Error, error not captured in Item")
|
||||
}
|
||||
eventID, err = AddEvent("ANX", "price", "3===D", "BTC", "LTC", "SPOT", actionTest)
|
||||
eventID, err = AddEvent("ANX", "price", "3===D", pair, "SPOT", actionTest)
|
||||
if err == nil && eventID == 0 {
|
||||
t.Error("Test Failed. AddEvent: Error, error not captured in Condition")
|
||||
}
|
||||
eventID, err = AddEvent("ANX", "price", ">,==", "BTC", "LTC", "SPOT", "console_prints")
|
||||
if err == nil && eventID == 0 {
|
||||
t.Error("Test Failed. AddEvent: Error, error not captured in Action")
|
||||
}
|
||||
eventID, err = AddEvent("ANX", "price", ">,==", "BATMAN", "ROBIN", "SPOT", actionTest)
|
||||
eventID, err = AddEvent("ANX", "price", ">,==", pair, "SPOT", "console_prints")
|
||||
if err == nil && eventID == 0 {
|
||||
t.Error("Test Failed. AddEvent: Error, error not captured in Action")
|
||||
}
|
||||
|
||||
if !RemoveEvent(eventID) {
|
||||
t.Error("Test Failed. RemoveEvent: Error, error removing event")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveEvent(t *testing.T) {
|
||||
eventID, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", "SPOT", actionTest)
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
eventID, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil && eventID != 0 {
|
||||
t.Errorf("Test Failed. RemoveEvent: Error, %s", err)
|
||||
}
|
||||
@@ -48,15 +50,16 @@ func TestRemoveEvent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetEventCounter(t *testing.T) {
|
||||
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", "SPOT", actionTest)
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
one, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. GetEventCounter: Error, %s", err)
|
||||
}
|
||||
two, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", "SPOT", actionTest)
|
||||
two, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. GetEventCounter: Error, %s", err)
|
||||
}
|
||||
three, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", "SPOT", actionTest)
|
||||
three, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. GetEventCounter: Error, %s", err)
|
||||
}
|
||||
@@ -84,9 +87,10 @@ func TestGetEventCounter(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExecuteAction(t *testing.T) {
|
||||
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", "SPOT", actionTest)
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
one, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. ExecuteAction: Error, %s", err)
|
||||
t.Fatalf("Test Failed. ExecuteAction: Error, %s", err)
|
||||
}
|
||||
isExecuted := Events[one].ExecuteAction()
|
||||
if !isExecuted {
|
||||
@@ -96,17 +100,46 @@ func TestExecuteAction(t *testing.T) {
|
||||
t.Error("Test Failed. ExecuteAction: Error, error removing event")
|
||||
}
|
||||
|
||||
action := actionSMSNotify + "," + "ALL"
|
||||
one, err = AddEvent("ANX", "price", ">,==", pair, "SPOT", action)
|
||||
if err != nil {
|
||||
t.Fatalf("Test Failed. ExecuteAction: Error, %s", err)
|
||||
}
|
||||
|
||||
isExecuted = Events[one].ExecuteAction()
|
||||
if !isExecuted {
|
||||
t.Error("Test Failed. ExecuteAction: Error, error removing event")
|
||||
}
|
||||
if !RemoveEvent(one) {
|
||||
t.Error("Test Failed. ExecuteAction: Error, error removing event")
|
||||
}
|
||||
|
||||
action = actionSMSNotify + "," + "StyleGherkin"
|
||||
one, err = AddEvent("ANX", "price", ">,==", pair, "SPOT", action)
|
||||
if err != nil {
|
||||
t.Fatalf("Test Failed. ExecuteAction: Error, %s", err)
|
||||
}
|
||||
|
||||
isExecuted = Events[one].ExecuteAction()
|
||||
if !isExecuted {
|
||||
t.Error("Test Failed. ExecuteAction: Error, error removing event")
|
||||
}
|
||||
if !RemoveEvent(one) {
|
||||
t.Error("Test Failed. ExecuteAction: Error, error removing event")
|
||||
}
|
||||
|
||||
// More tests when ExecuteAction is expanded
|
||||
}
|
||||
|
||||
func TestEventToString(t *testing.T) {
|
||||
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", "SPOT", actionTest)
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
one, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. EventToString: Error, %s", err)
|
||||
}
|
||||
|
||||
eventString := Events[one].EventToString()
|
||||
if eventString != "If the BTCLTC [SPOT] price on ANX is > == then ACTION_TEST." {
|
||||
eventString := Events[one].String()
|
||||
if eventString != "If the BTCUSD [SPOT] price on ANX is > == then ACTION_TEST." {
|
||||
t.Error("Test Failed. EventToString: Error, incorrect return string")
|
||||
}
|
||||
|
||||
@@ -115,64 +148,118 @@ func TestEventToString(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckCondition(t *testing.T) { //error handling needs to be implemented
|
||||
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", "SPOT", actionTest)
|
||||
func TestCheckCondition(t *testing.T) {
|
||||
// Test invalid currency pair
|
||||
newPair := pair.NewCurrencyPair("A", "B")
|
||||
one, err := AddEvent("ANX", "price", ">=,10", newPair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. EventToString: Error, %s", err)
|
||||
t.Errorf("Test Failed. CheckCondition: Error, %s", err)
|
||||
}
|
||||
conditionBool := Events[one].CheckCondition()
|
||||
if conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
conditionBool := Events[one].CheckCondition()
|
||||
if conditionBool { //check once error handling is implemented
|
||||
t.Error("Test Failed. EventToString: Error, wrong conditional.")
|
||||
// Test last price == 0
|
||||
var tickerNew ticker.Price
|
||||
tickerNew.Last = 0
|
||||
newPair = pair.NewCurrencyPair("BTC", "USD")
|
||||
ticker.ProcessTicker("ANX", newPair, tickerNew, ticker.Spot)
|
||||
Events[one].Pair = newPair
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
// Test last pricce > 0 and conditional logic
|
||||
tickerNew.Last = 11
|
||||
ticker.ProcessTicker("ANX", newPair, tickerNew, ticker.Spot)
|
||||
Events[one].Condition = ">,10"
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if !conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
// Test last price >= 10
|
||||
Events[one].Condition = ">=,10"
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if !conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
// Test last price <= 10
|
||||
Events[one].Condition = "<,100"
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if !conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
// Test last price <= 10
|
||||
Events[one].Condition = "<=,100"
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if !conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
Events[one].Condition = "==,11"
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if !conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
Events[one].Condition = "^,11"
|
||||
conditionBool = Events[one].CheckCondition()
|
||||
if conditionBool {
|
||||
t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
|
||||
}
|
||||
|
||||
if !RemoveEvent(one) {
|
||||
t.Error("Test Failed. EventToString: Error, error removing event")
|
||||
t.Error("Test Failed. CheckCondition: Error, error removing event")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidEvent(t *testing.T) {
|
||||
err := IsValidEvent("ANX", "price", ">,==", actionTest)
|
||||
if err != nil {
|
||||
t.Errorf("Test Failed. IsValidExchange: Error %s", err)
|
||||
t.Errorf("Test Failed. IsValidEvent: %s", err)
|
||||
}
|
||||
err = IsValidEvent("ANX", "price", ">,", actionTest)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed. IsValidExchange: Error")
|
||||
t.Errorf("Test Failed. IsValidEvent: %s", err)
|
||||
}
|
||||
err = IsValidEvent("ANX", "Testy", ">,==", actionTest)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed. IsValidExchange: Error")
|
||||
t.Errorf("Test Failed. IsValidEvent: %s", err)
|
||||
}
|
||||
err = IsValidEvent("Testys", "price", ">,==", actionTest)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed. IsValidExchange: Error")
|
||||
t.Errorf("Test Failed. IsValidEvent: %s", err)
|
||||
}
|
||||
|
||||
action := "blah,blah"
|
||||
err = IsValidEvent("ANX", "price", ">=,10", action)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed. IsValidEvent: %s", err)
|
||||
}
|
||||
|
||||
action = "SMS,blah"
|
||||
err = IsValidEvent("ANX", "price", ">=,10", action)
|
||||
if err == nil {
|
||||
t.Errorf("Test Failed. IsValidEvent: %s", err)
|
||||
}
|
||||
|
||||
//Function tests need to appended to this function when more actions are
|
||||
//implemented
|
||||
}
|
||||
|
||||
func TestCheckEvents(t *testing.T) { //Add error handling
|
||||
//CheckEvents() //check once error handling is implemented
|
||||
}
|
||||
func TestCheckEvents(t *testing.T) {
|
||||
pair := pair.NewCurrencyPair("BTC", "USD")
|
||||
_, err := AddEvent("ANX", "price", ">=,10", pair, "SPOT", actionTest)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed. TestChcheckEvents add event")
|
||||
}
|
||||
|
||||
func TestIsValidCurrency(t *testing.T) {
|
||||
if !IsValidCurrency("BTC") {
|
||||
t.Error("Test Failed - Event_test.go TestIsValidCurrency Error")
|
||||
}
|
||||
if !IsValidCurrency("USD") {
|
||||
t.Error("Test Failed - Event_test.go TestIsValidCurrency Error")
|
||||
}
|
||||
if IsValidCurrency("testy") {
|
||||
t.Error("Test Failed - Event_test.go TestIsValidCurrency Error")
|
||||
}
|
||||
if !IsValidCurrency("USD", "BTC", "USD") {
|
||||
t.Error("Test Failed - Event_test.go TestIsValidCurrency Error")
|
||||
}
|
||||
if IsValidCurrency("USD", "USD", "Wigwham") {
|
||||
t.Error("Test Failed - Event_test.go TestIsValidCurrency Error")
|
||||
}
|
||||
go CheckEvents()
|
||||
}
|
||||
|
||||
func TestIsValidExchange(t *testing.T) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-/gocryptotrader/smsglobal"
|
||||
@@ -32,20 +31,18 @@ var (
|
||||
errInvalidCondition = errors.New("invalid conditional option")
|
||||
errInvalidAction = errors.New("invalid action")
|
||||
errExchangeDisabled = errors.New("desired exchange is disabled")
|
||||
errCurrencyInvalid = errors.New("invalid currency")
|
||||
)
|
||||
|
||||
// Event struct holds the event variables
|
||||
type Event struct {
|
||||
ID int
|
||||
Exchange string
|
||||
Item string
|
||||
Condition string
|
||||
FirstCurrency string
|
||||
Asset string
|
||||
SecondCurrency string
|
||||
Action string
|
||||
Executed bool
|
||||
ID int
|
||||
Exchange string
|
||||
Item string
|
||||
Condition string
|
||||
Pair pair.CurrencyPair
|
||||
Asset string
|
||||
Action string
|
||||
Executed bool
|
||||
}
|
||||
|
||||
// Events variable is a pointer array to the event structures that will be
|
||||
@@ -54,16 +51,12 @@ var Events []*Event
|
||||
|
||||
// AddEvent adds an event to the Events chain and returns an index/eventID
|
||||
// and an error
|
||||
func AddEvent(Exchange, Item, Condition, FirstCurrency, SecondCurrency, Asset, Action string) (int, error) {
|
||||
func AddEvent(Exchange, Item, Condition string, CurrencyPair pair.CurrencyPair, Asset, Action string) (int, error) {
|
||||
err := IsValidEvent(Exchange, Item, Condition, Action)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !IsValidCurrency(FirstCurrency, SecondCurrency) {
|
||||
return 0, errCurrencyInvalid
|
||||
}
|
||||
|
||||
Event := &Event{}
|
||||
|
||||
if len(Events) == 0 {
|
||||
@@ -75,8 +68,7 @@ func AddEvent(Exchange, Item, Condition, FirstCurrency, SecondCurrency, Asset, A
|
||||
Event.Exchange = Exchange
|
||||
Event.Item = Item
|
||||
Event.Condition = Condition
|
||||
Event.FirstCurrency = FirstCurrency
|
||||
Event.SecondCurrency = SecondCurrency
|
||||
Event.Pair = CurrencyPair
|
||||
Event.Asset = Asset
|
||||
Event.Action = Action
|
||||
Event.Executed = false
|
||||
@@ -114,7 +106,7 @@ func (e *Event) ExecuteAction() bool {
|
||||
if common.StringContains(e.Action, ",") {
|
||||
action := common.SplitStrings(e.Action, ",")
|
||||
if action[0] == actionSMSNotify {
|
||||
message := fmt.Sprintf("Event triggered: %s", e.EventToString())
|
||||
message := fmt.Sprintf("Event triggered: %s", e.String())
|
||||
if action[1] == "ALL" {
|
||||
smsglobal.SMSSendToAll(message, config.Cfg)
|
||||
} else {
|
||||
@@ -124,17 +116,17 @@ func (e *Event) ExecuteAction() bool {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Printf("Event triggered: %s", e.EventToString())
|
||||
log.Printf("Event triggered: %s", e.String())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// EventToString turns the structure event into a string
|
||||
func (e *Event) EventToString() string {
|
||||
func (e *Event) String() string {
|
||||
condition := common.SplitStrings(e.Condition, ",")
|
||||
return fmt.Sprintf(
|
||||
"If the %s%s [%s] %s on %s is %s then %s.", e.FirstCurrency, e.SecondCurrency,
|
||||
e.Asset, e.Item, e.Exchange, condition[0]+" "+condition[1], e.Action,
|
||||
"If the %s%s [%s] %s on %s is %s then %s.", e.Pair.FirstCurrency.String(),
|
||||
e.Pair.SecondCurrency.String(), e.Asset, e.Item, e.Exchange, condition[0]+" "+condition[1], e.Action,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -144,12 +136,12 @@ func (e *Event) CheckCondition() bool {
|
||||
condition := common.SplitStrings(e.Condition, ",")
|
||||
targetPrice, _ := strconv.ParseFloat(condition[1], 64)
|
||||
|
||||
ticker, err := ticker.GetTickerByExchange(e.Exchange)
|
||||
t, err := ticker.GetTicker(e.Exchange, e.Pair, e.Asset)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
lastPrice := ticker.Price[pair.CurrencyItem(e.FirstCurrency)][pair.CurrencyItem(e.SecondCurrency)][e.Asset].Last
|
||||
lastPrice := t.Last
|
||||
|
||||
if lastPrice == 0 {
|
||||
return false
|
||||
@@ -226,8 +218,9 @@ func IsValidEvent(Exchange, Item, Condition, Action string) error {
|
||||
return errInvalidAction
|
||||
}
|
||||
|
||||
cfg := config.GetConfig()
|
||||
if action[1] != "ALL" && smsglobal.SMSGetNumberByName(
|
||||
action[1], config.Cfg.SMS) == smsglobal.ErrSMSContactNotFound {
|
||||
action[1], cfg.SMS) == smsglobal.ErrSMSContactNotFound {
|
||||
return errInvalidAction
|
||||
}
|
||||
} else {
|
||||
@@ -260,20 +253,6 @@ func CheckEvents() {
|
||||
}
|
||||
}
|
||||
|
||||
// IsValidCurrency takes in CRYPTO or FIAT currency strings and returns if valid
|
||||
func IsValidCurrency(currencies ...string) bool {
|
||||
for index, whatIsIt := range currencies {
|
||||
whatIsIt = common.StringToUpper(whatIsIt)
|
||||
if currency.IsDefaultCryptocurrency(whatIsIt) || currency.IsDefaultCurrency(whatIsIt) {
|
||||
if len(currencies)-1 == index {
|
||||
return true
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsValidExchange validates the exchange
|
||||
func IsValidExchange(Exchange, configPath string) bool {
|
||||
Exchange = common.StringToUpper(Exchange)
|
||||
|
||||
108
helpers.go
Normal file
108
helpers.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/stats"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// GetSpecificOrderbook returns a specific orderbook given the currency,
|
||||
// exchangeName and assetType
|
||||
func GetSpecificOrderbook(currency, exchangeName, assetType string) (orderbook.Base, error) {
|
||||
var specificOrderbook orderbook.Base
|
||||
var err error
|
||||
for i := 0; i < len(bot.exchanges); i++ {
|
||||
if bot.exchanges[i] != nil {
|
||||
if bot.exchanges[i].IsEnabled() && bot.exchanges[i].GetName() == exchangeName {
|
||||
specificOrderbook, err = bot.exchanges[i].GetOrderbookEx(
|
||||
pair.NewCurrencyPairFromString(currency),
|
||||
assetType,
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return specificOrderbook, err
|
||||
}
|
||||
|
||||
// GetSpecificTicker returns a specific ticker given the currency,
|
||||
// exchangeName and assetType
|
||||
func GetSpecificTicker(currency, exchangeName, assetType string) (ticker.Price, error) {
|
||||
var specificTicker ticker.Price
|
||||
var err error
|
||||
for i := 0; i < len(bot.exchanges); i++ {
|
||||
if bot.exchanges[i] != nil {
|
||||
if bot.exchanges[i].IsEnabled() && bot.exchanges[i].GetName() == exchangeName {
|
||||
specificTicker, err = bot.exchanges[i].GetTickerPrice(
|
||||
pair.NewCurrencyPairFromString(currency),
|
||||
assetType,
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return specificTicker, err
|
||||
}
|
||||
|
||||
// GetCollatedExchangeAccountInfoByCoin collates individual exchange account
|
||||
// information and turns into into a map string of
|
||||
// exchange.AccountCurrencyInfo
|
||||
func GetCollatedExchangeAccountInfoByCoin(accounts []exchange.AccountInfo) map[string]exchange.AccountCurrencyInfo {
|
||||
result := make(map[string]exchange.AccountCurrencyInfo)
|
||||
for i := 0; i < len(accounts); i++ {
|
||||
for j := 0; j < len(accounts[i].Currencies); j++ {
|
||||
currencyName := accounts[i].Currencies[j].CurrencyName
|
||||
avail := accounts[i].Currencies[j].TotalValue
|
||||
onHold := accounts[i].Currencies[j].Hold
|
||||
|
||||
info, ok := result[currencyName]
|
||||
if !ok {
|
||||
accountInfo := exchange.AccountCurrencyInfo{CurrencyName: currencyName, Hold: onHold, TotalValue: avail}
|
||||
result[currencyName] = accountInfo
|
||||
} else {
|
||||
info.Hold += onHold
|
||||
info.TotalValue += avail
|
||||
result[currencyName] = info
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetAccountCurrencyInfoByExchangeName returns info for an exchange
|
||||
func GetAccountCurrencyInfoByExchangeName(accounts []exchange.AccountInfo, exchangeName string) (exchange.AccountInfo, error) {
|
||||
for i := 0; i < len(accounts); i++ {
|
||||
if accounts[i].ExchangeName == exchangeName {
|
||||
return accounts[i], nil
|
||||
}
|
||||
}
|
||||
return exchange.AccountInfo{}, errors.New(exchange.ErrExchangeNotFound)
|
||||
}
|
||||
|
||||
// GetExchangeHighestPriceByCurrencyPair returns the exchange with the highest
|
||||
// price for a given currency pair and asset type
|
||||
func GetExchangeHighestPriceByCurrencyPair(p pair.CurrencyPair, assetType string) (string, error) {
|
||||
result := stats.SortExchangesByPrice(p, assetType, true)
|
||||
if len(result) != 1 {
|
||||
return "", fmt.Errorf("no stats for supplied currency pair and asset type")
|
||||
}
|
||||
|
||||
return result[0].Exchange, nil
|
||||
}
|
||||
|
||||
// GetExchangeLowestPriceByCurrencyPair returns the exchange with the lowest
|
||||
// price for a given currency pair and asset type
|
||||
func GetExchangeLowestPriceByCurrencyPair(p pair.CurrencyPair, assetType string) (string, error) {
|
||||
result := stats.SortExchangesByPrice(p, assetType, false)
|
||||
if len(result) != 1 {
|
||||
return "", fmt.Errorf("no stats for supplied currency pair and asset type")
|
||||
}
|
||||
|
||||
return result[0].Exchange, nil
|
||||
}
|
||||
38
main.go
38
main.go
@@ -119,16 +119,10 @@ func main() {
|
||||
AdjustGoMaxProcs()
|
||||
|
||||
if bot.config.SMS.Enabled {
|
||||
err = bot.config.CheckSMSGlobalConfigValues()
|
||||
if err != nil {
|
||||
log.Println(err) // non fatal event
|
||||
bot.config.SMS.Enabled = false
|
||||
} else {
|
||||
log.Printf(
|
||||
"SMS support enabled. Number of SMS contacts %d.\n",
|
||||
smsglobal.GetEnabledSMSContacts(bot.config.SMS),
|
||||
)
|
||||
}
|
||||
log.Printf(
|
||||
"SMS support enabled. Number of SMS contacts %d.\n",
|
||||
smsglobal.GetEnabledSMSContacts(bot.config.SMS),
|
||||
)
|
||||
} else {
|
||||
log.Println("SMS support disabled.")
|
||||
}
|
||||
@@ -174,7 +168,6 @@ func main() {
|
||||
setupBotExchanges()
|
||||
|
||||
bot.config.RetrieveConfigCurrencyPairs()
|
||||
|
||||
err = currency.SeedCurrencyData(currency.BaseCurrencies)
|
||||
if err != nil {
|
||||
log.Fatalf("Fatal error retrieving config currencies. Error: %s", err)
|
||||
@@ -194,21 +187,14 @@ func main() {
|
||||
go OrderbookUpdaterRoutine()
|
||||
|
||||
if bot.config.Webserver.Enabled {
|
||||
err := bot.config.CheckWebserverConfigValues()
|
||||
if err != nil {
|
||||
log.Println(err) // non fatal event
|
||||
//bot.config.Webserver.Enabled = false
|
||||
} else {
|
||||
listenAddr := bot.config.Webserver.ListenAddress
|
||||
log.Printf(
|
||||
"HTTP RESTful Webserver support enabled. Listen URL: http://%s:%d/\n",
|
||||
common.ExtractHost(listenAddr), common.ExtractPort(listenAddr),
|
||||
)
|
||||
router := NewRouter(bot.exchanges)
|
||||
log.Fatal(http.ListenAndServe(listenAddr, router))
|
||||
}
|
||||
}
|
||||
if !bot.config.Webserver.Enabled {
|
||||
listenAddr := bot.config.Webserver.ListenAddress
|
||||
log.Printf(
|
||||
"HTTP Webserver support enabled. Listen URL: http://%s:%d/\n",
|
||||
common.ExtractHost(listenAddr), common.ExtractPort(listenAddr),
|
||||
)
|
||||
router := NewRouter(bot.exchanges)
|
||||
log.Fatal(http.ListenAndServe(listenAddr, router))
|
||||
} else {
|
||||
log.Println("HTTP RESTful Webserver support disabled.")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
)
|
||||
|
||||
// GetSpecificOrderbook returns a specific orderbook given the currency,
|
||||
// exchangeName and assetType
|
||||
func GetSpecificOrderbook(currency, exchangeName, assetType string) (orderbook.Base, error) {
|
||||
var specificOrderbook orderbook.Base
|
||||
var err error
|
||||
for i := 0; i < len(bot.exchanges); i++ {
|
||||
if bot.exchanges[i] != nil {
|
||||
if bot.exchanges[i].IsEnabled() && bot.exchanges[i].GetName() == exchangeName {
|
||||
specificOrderbook, err = bot.exchanges[i].GetOrderbookEx(
|
||||
pair.NewCurrencyPairFromString(currency),
|
||||
assetType,
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return specificOrderbook, err
|
||||
}
|
||||
|
||||
func jsonOrderbookResponse(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
currency := vars["currency"]
|
||||
exchange := vars["exchangeName"]
|
||||
assetType := vars["assetType"]
|
||||
|
||||
if assetType == "" {
|
||||
assetType = orderbook.Spot
|
||||
}
|
||||
|
||||
response, err := GetSpecificOrderbook(currency, exchange, assetType)
|
||||
if err != nil {
|
||||
log.Printf("Failed to fetch orderbook for %s currency: %s\n", exchange,
|
||||
currency)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// AllEnabledExchangeOrderbooks holds the enabled exchange orderbooks
|
||||
type AllEnabledExchangeOrderbooks struct {
|
||||
Data []EnabledExchangeOrderbooks `json:"data"`
|
||||
}
|
||||
|
||||
// EnabledExchangeOrderbooks is a sub type for singular exchanges and respective
|
||||
// orderbooks
|
||||
type EnabledExchangeOrderbooks struct {
|
||||
ExchangeName string `json:"exchangeName"`
|
||||
ExchangeValues []orderbook.Base `json:"exchangeValues"`
|
||||
}
|
||||
|
||||
// GetAllActiveOrderbooks returns all enabled exchanges orderbooks
|
||||
func GetAllActiveOrderbooks() []EnabledExchangeOrderbooks {
|
||||
var orderbookData []EnabledExchangeOrderbooks
|
||||
|
||||
for _, individualBot := range bot.exchanges {
|
||||
if individualBot != nil && individualBot.IsEnabled() {
|
||||
var individualExchange EnabledExchangeOrderbooks
|
||||
exchangeName := individualBot.GetName()
|
||||
individualExchange.ExchangeName = exchangeName
|
||||
currencies := individualBot.GetEnabledCurrencies()
|
||||
assetTypes, err := exchange.GetExchangeAssetTypes(exchangeName)
|
||||
if err != nil {
|
||||
log.Printf("failed to get %s exchange asset types. Error: %s",
|
||||
exchangeName, err)
|
||||
continue
|
||||
}
|
||||
for _, x := range currencies {
|
||||
currency := x
|
||||
|
||||
var ob orderbook.Base
|
||||
if len(assetTypes) > 1 {
|
||||
for y := range assetTypes {
|
||||
ob, err = individualBot.UpdateOrderbook(currency,
|
||||
assetTypes[y])
|
||||
}
|
||||
} else {
|
||||
ob, err = individualBot.UpdateOrderbook(currency,
|
||||
assetTypes[0])
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("failed to get %s %s orderbook. Error: %s",
|
||||
currency.Pair().String(),
|
||||
exchangeName,
|
||||
err)
|
||||
continue
|
||||
}
|
||||
|
||||
individualExchange.ExchangeValues = append(
|
||||
individualExchange.ExchangeValues, ob,
|
||||
)
|
||||
}
|
||||
orderbookData = append(orderbookData, individualExchange)
|
||||
}
|
||||
}
|
||||
return orderbookData
|
||||
}
|
||||
|
||||
func getAllActiveOrderbooksResponse(w http.ResponseWriter, r *http.Request) {
|
||||
var response AllEnabledExchangeOrderbooks
|
||||
response.Data = GetAllActiveOrderbooks()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// OrderbookRoutes denotes the current exchange orderbook routes
|
||||
var OrderbookRoutes = Routes{
|
||||
Route{
|
||||
"AllActiveExchangesAndOrderbooks",
|
||||
"GET",
|
||||
"/exchanges/orderbook/latest/all",
|
||||
getAllActiveOrderbooksResponse,
|
||||
},
|
||||
Route{
|
||||
"IndividualExchangeOrderbook",
|
||||
"GET",
|
||||
"/exchanges/{exchangeName}/orderbook/latest/{currency}",
|
||||
jsonOrderbookResponse,
|
||||
},
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// RESTGetPortfolio replies to a request with an encoded JSON response of the
|
||||
// portfolio
|
||||
func RESTGetPortfolio(w http.ResponseWriter, r *http.Request) {
|
||||
result := bot.portfolio.GetPortfolioSummary()
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// PortfolioRoutes declares the current routes for config_routes.go
|
||||
var PortfolioRoutes = Routes{
|
||||
Route{
|
||||
"GetPortfolio",
|
||||
"GET",
|
||||
"/portfolio/all",
|
||||
RESTGetPortfolio,
|
||||
},
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Logger logs the requests internally
|
||||
func Logger(inner http.Handler, name string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
|
||||
inner.ServeHTTP(w, r)
|
||||
|
||||
log.Printf(
|
||||
"%s\t%s\t%s\t%s",
|
||||
r.Method,
|
||||
r.RequestURI,
|
||||
name,
|
||||
time.Since(start),
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -2,27 +2,104 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
// RESTLogger logs the requests internally
|
||||
func RESTLogger(inner http.Handler, name string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
|
||||
inner.ServeHTTP(w, r)
|
||||
|
||||
log.Printf(
|
||||
"%s\t%s\t%s\t%s",
|
||||
r.Method,
|
||||
r.RequestURI,
|
||||
name,
|
||||
time.Since(start),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// Route is a sub type that holds the request routes
|
||||
type Route struct {
|
||||
Name string
|
||||
Method string
|
||||
Pattern string
|
||||
HandlerFunc http.HandlerFunc
|
||||
}
|
||||
|
||||
// Routes is an array of all the registered routes
|
||||
type Routes []Route
|
||||
|
||||
var routes = Routes{}
|
||||
|
||||
// NewRouter takes in the exchange interfaces and returns a new multiplexor
|
||||
// router
|
||||
func NewRouter(exchanges []exchange.IBotExchange) *mux.Router {
|
||||
router := mux.NewRouter().StrictSlash(true)
|
||||
allRoutes := append(routes, ExchangeRoutes...)
|
||||
allRoutes = append(allRoutes, ConfigRoutes...)
|
||||
allRoutes = append(allRoutes, PortfolioRoutes...)
|
||||
allRoutes = append(allRoutes, WalletRoutes...)
|
||||
allRoutes = append(allRoutes, IndexRoute...)
|
||||
allRoutes = append(allRoutes, WebsocketRoutes...)
|
||||
allRoutes = append(allRoutes, OrderbookRoutes...)
|
||||
for _, route := range allRoutes {
|
||||
|
||||
routes = Routes{
|
||||
Route{
|
||||
"GetAllSettings",
|
||||
"GET",
|
||||
"/config/all",
|
||||
RESTGetAllSettings,
|
||||
},
|
||||
Route{
|
||||
"SaveAllSettings",
|
||||
"POST",
|
||||
"/config/all/save",
|
||||
RESTSaveAllSettings,
|
||||
},
|
||||
Route{
|
||||
"AllEnabledAccountInfo",
|
||||
"GET",
|
||||
"/exchanges/enabled/accounts/all",
|
||||
RESTGetAllEnabledAccountInfo,
|
||||
},
|
||||
Route{
|
||||
"AllActiveExchangesAndCurrencies",
|
||||
"GET",
|
||||
"/exchanges/enabled/latest/all",
|
||||
RESTGetAllActiveTickers,
|
||||
},
|
||||
Route{
|
||||
"IndividualExchangeAndCurrency",
|
||||
"GET",
|
||||
"/exchanges/{exchangeName}/latest/{currency}",
|
||||
RESTGetTicker,
|
||||
},
|
||||
Route{
|
||||
"GetPortfolio",
|
||||
"GET",
|
||||
"/portfolio/all",
|
||||
RESTGetPortfolio,
|
||||
},
|
||||
Route{
|
||||
"AllActiveExchangesAndOrderbooks",
|
||||
"GET",
|
||||
"/exchanges/orderbook/latest/all",
|
||||
RESTGetAllActiveOrderbooks,
|
||||
},
|
||||
Route{
|
||||
"IndividualExchangeOrderbook",
|
||||
"GET",
|
||||
"/exchanges/{exchangeName}/orderbook/latest/{currency}",
|
||||
RESTGetOrderbook,
|
||||
},
|
||||
}
|
||||
|
||||
for _, route := range routes {
|
||||
var handler http.Handler
|
||||
handler = route.HandlerFunc
|
||||
handler = Logger(handler, route.Name)
|
||||
handler = RESTLogger(handler, route.Name)
|
||||
|
||||
router.
|
||||
Methods(route.Method).
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewRouter(t *testing.T) {
|
||||
if value := NewRouter(bot.exchanges); value.KeepContext {
|
||||
t.Error("Test Failed - Restful_Router_Test.go - NewRouter Error")
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package main
|
||||
|
||||
import "net/http"
|
||||
|
||||
// Route is a sub type that holds the request routes
|
||||
type Route struct {
|
||||
Name string
|
||||
Method string
|
||||
Pattern string
|
||||
HandlerFunc http.HandlerFunc
|
||||
}
|
||||
|
||||
// Routes is an array of all the registered routes
|
||||
type Routes []Route
|
||||
|
||||
var routes = Routes{}
|
||||
297
restful_server.go
Normal file
297
restful_server.go
Normal file
@@ -0,0 +1,297 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// AllEnabledExchangeOrderbooks holds the enabled exchange orderbooks
|
||||
type AllEnabledExchangeOrderbooks struct {
|
||||
Data []EnabledExchangeOrderbooks `json:"data"`
|
||||
}
|
||||
|
||||
// EnabledExchangeOrderbooks is a sub type for singular exchanges and respective
|
||||
// orderbooks
|
||||
type EnabledExchangeOrderbooks struct {
|
||||
ExchangeName string `json:"exchangeName"`
|
||||
ExchangeValues []orderbook.Base `json:"exchangeValues"`
|
||||
}
|
||||
|
||||
// AllEnabledExchangeCurrencies holds the enabled exchange currencies
|
||||
type AllEnabledExchangeCurrencies struct {
|
||||
Data []EnabledExchangeCurrencies `json:"data"`
|
||||
}
|
||||
|
||||
// EnabledExchangeCurrencies is a sub type for singular exchanges and respective
|
||||
// currencies
|
||||
type EnabledExchangeCurrencies struct {
|
||||
ExchangeName string `json:"exchangeName"`
|
||||
ExchangeValues []ticker.Price `json:"exchangeValues"`
|
||||
}
|
||||
|
||||
// AllEnabledExchangeAccounts holds all enabled accounts info
|
||||
type AllEnabledExchangeAccounts struct {
|
||||
Data []exchange.AccountInfo `json:"data"`
|
||||
}
|
||||
|
||||
// RESTfulJSONResponse outputs a JSON response of the req interface
|
||||
func RESTfulJSONResponse(w http.ResponseWriter, r *http.Request, req interface{}) error {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(req); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RESTfulError prints the REST method and error
|
||||
func RESTfulError(method string, err error) {
|
||||
log.Printf("RESTful %s: server failed to send JSON response. Error %s",
|
||||
method, err)
|
||||
}
|
||||
|
||||
// RESTGetAllSettings replies to a request with an encoded JSON response about the
|
||||
// trading bots configuration.
|
||||
func RESTGetAllSettings(w http.ResponseWriter, r *http.Request) {
|
||||
err := RESTfulJSONResponse(w, r, bot.config)
|
||||
if err != nil {
|
||||
RESTfulError(r.Method, err)
|
||||
}
|
||||
}
|
||||
|
||||
// RESTSaveAllSettings saves all current settings from request body as a JSON
|
||||
// document then reloads state and returns the settings
|
||||
func RESTSaveAllSettings(w http.ResponseWriter, r *http.Request) {
|
||||
//Get the data from the request
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var responseData config.Post
|
||||
err := decoder.Decode(&responseData)
|
||||
if err != nil {
|
||||
RESTfulError(r.Method, err)
|
||||
}
|
||||
//Save change the settings
|
||||
err = bot.config.UpdateConfig(bot.configFile, responseData.Data)
|
||||
if err != nil {
|
||||
RESTfulError(r.Method, err)
|
||||
}
|
||||
|
||||
err = RESTfulJSONResponse(w, r, bot.config)
|
||||
if err != nil {
|
||||
RESTfulError(r.Method, err)
|
||||
}
|
||||
}
|
||||
|
||||
// RESTGetOrderbook returns orderbook info for a given currency, exchange and
|
||||
// asset type
|
||||
func RESTGetOrderbook(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
currency := vars["currency"]
|
||||
exchange := vars["exchangeName"]
|
||||
assetType := vars["assetType"]
|
||||
|
||||
if assetType == "" {
|
||||
assetType = orderbook.Spot
|
||||
}
|
||||
|
||||
response, err := GetSpecificOrderbook(currency, exchange, assetType)
|
||||
if err != nil {
|
||||
log.Printf("Failed to fetch orderbook for %s currency: %s\n", exchange,
|
||||
currency)
|
||||
return
|
||||
}
|
||||
|
||||
err = RESTfulJSONResponse(w, r, response)
|
||||
if err != nil {
|
||||
RESTfulError(r.Method, err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllActiveOrderbooks returns all enabled exchanges orderbooks
|
||||
func GetAllActiveOrderbooks() []EnabledExchangeOrderbooks {
|
||||
var orderbookData []EnabledExchangeOrderbooks
|
||||
|
||||
for _, individualBot := range bot.exchanges {
|
||||
if individualBot != nil && individualBot.IsEnabled() {
|
||||
var individualExchange EnabledExchangeOrderbooks
|
||||
exchangeName := individualBot.GetName()
|
||||
individualExchange.ExchangeName = exchangeName
|
||||
currencies := individualBot.GetEnabledCurrencies()
|
||||
assetTypes, err := exchange.GetExchangeAssetTypes(exchangeName)
|
||||
if err != nil {
|
||||
log.Printf("failed to get %s exchange asset types. Error: %s",
|
||||
exchangeName, err)
|
||||
continue
|
||||
}
|
||||
for _, x := range currencies {
|
||||
currency := x
|
||||
|
||||
var ob orderbook.Base
|
||||
if len(assetTypes) > 1 {
|
||||
for y := range assetTypes {
|
||||
ob, err = individualBot.GetOrderbookEx(currency,
|
||||
assetTypes[y])
|
||||
}
|
||||
} else {
|
||||
ob, err = individualBot.GetOrderbookEx(currency,
|
||||
assetTypes[0])
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("failed to get %s %s orderbook. Error: %s",
|
||||
currency.Pair().String(),
|
||||
exchangeName,
|
||||
err)
|
||||
continue
|
||||
}
|
||||
|
||||
individualExchange.ExchangeValues = append(
|
||||
individualExchange.ExchangeValues, ob,
|
||||
)
|
||||
}
|
||||
orderbookData = append(orderbookData, individualExchange)
|
||||
}
|
||||
}
|
||||
return orderbookData
|
||||
}
|
||||
|
||||
// RESTGetAllActiveOrderbooks returns all enabled exchange orderbooks
|
||||
func RESTGetAllActiveOrderbooks(w http.ResponseWriter, r *http.Request) {
|
||||
var response AllEnabledExchangeOrderbooks
|
||||
response.Data = GetAllActiveOrderbooks()
|
||||
|
||||
err := RESTfulJSONResponse(w, r, response)
|
||||
if err != nil {
|
||||
RESTfulError(r.Method, err)
|
||||
}
|
||||
}
|
||||
|
||||
// RESTGetPortfolio returns the bot portfolio
|
||||
func RESTGetPortfolio(w http.ResponseWriter, r *http.Request) {
|
||||
result := bot.portfolio.GetPortfolioSummary()
|
||||
err := RESTfulJSONResponse(w, r, result)
|
||||
if err != nil {
|
||||
RESTfulError(r.Method, err)
|
||||
}
|
||||
}
|
||||
|
||||
// RESTGetTicker returns ticker info for a given currency, exchange and
|
||||
// asset type
|
||||
func RESTGetTicker(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
currency := vars["currency"]
|
||||
exchange := vars["exchangeName"]
|
||||
assetType := vars["assetType"]
|
||||
|
||||
if assetType == "" {
|
||||
assetType = ticker.Spot
|
||||
}
|
||||
response, err := GetSpecificTicker(currency, exchange, assetType)
|
||||
if err != nil {
|
||||
log.Printf("Failed to fetch ticker for %s currency: %s\n", exchange,
|
||||
currency)
|
||||
return
|
||||
}
|
||||
err = RESTfulJSONResponse(w, r, response)
|
||||
if err != nil {
|
||||
RESTfulError(r.Method, err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllActiveTickers returns all enabled exchange tickers
|
||||
func GetAllActiveTickers() []EnabledExchangeCurrencies {
|
||||
var tickerData []EnabledExchangeCurrencies
|
||||
|
||||
for _, individualBot := range bot.exchanges {
|
||||
if individualBot != nil && individualBot.IsEnabled() {
|
||||
var individualExchange EnabledExchangeCurrencies
|
||||
exchangeName := individualBot.GetName()
|
||||
individualExchange.ExchangeName = exchangeName
|
||||
log.Println(
|
||||
"Getting enabled currencies for '" + exchangeName + "'",
|
||||
)
|
||||
currencies := individualBot.GetEnabledCurrencies()
|
||||
for _, x := range currencies {
|
||||
currency := x
|
||||
assetTypes, err := exchange.GetExchangeAssetTypes(exchangeName)
|
||||
if err != nil {
|
||||
log.Printf("failed to get %s exchange asset types. Error: %s",
|
||||
exchangeName, err)
|
||||
continue
|
||||
}
|
||||
var tickerPrice ticker.Price
|
||||
if len(assetTypes) > 1 {
|
||||
for y := range assetTypes {
|
||||
tickerPrice, err = individualBot.GetTickerPrice(currency,
|
||||
assetTypes[y])
|
||||
}
|
||||
} else {
|
||||
tickerPrice, err = individualBot.GetTickerPrice(currency,
|
||||
assetTypes[0])
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("failed to get %s %s ticker. Error: %s",
|
||||
currency.Pair().String(),
|
||||
exchangeName,
|
||||
err)
|
||||
continue
|
||||
}
|
||||
|
||||
individualExchange.ExchangeValues = append(
|
||||
individualExchange.ExchangeValues, tickerPrice,
|
||||
)
|
||||
}
|
||||
tickerData = append(tickerData, individualExchange)
|
||||
}
|
||||
}
|
||||
return tickerData
|
||||
}
|
||||
|
||||
// RESTGetAllActiveTickers returns all active tickers
|
||||
func RESTGetAllActiveTickers(w http.ResponseWriter, r *http.Request) {
|
||||
var response AllEnabledExchangeCurrencies
|
||||
response.Data = GetAllActiveTickers()
|
||||
|
||||
err := RESTfulJSONResponse(w, r, response)
|
||||
if err != nil {
|
||||
RESTfulError(r.Method, err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllEnabledExchangeAccountInfo returns all the current enabled exchanges
|
||||
func GetAllEnabledExchangeAccountInfo() AllEnabledExchangeAccounts {
|
||||
var response AllEnabledExchangeAccounts
|
||||
for _, individualBot := range bot.exchanges {
|
||||
if individualBot != nil && individualBot.IsEnabled() {
|
||||
if !individualBot.GetAuthenticatedAPISupport() {
|
||||
log.Printf("GetAllEnabledExchangeAccountInfo: Skippping %s due to disabled authenticated API support.", individualBot.GetName())
|
||||
continue
|
||||
}
|
||||
individualExchange, err := individualBot.GetExchangeAccountInfo()
|
||||
if err != nil {
|
||||
log.Printf("Error encountered retrieving exchange account info for %s. Error %s",
|
||||
individualBot.GetName(), err)
|
||||
continue
|
||||
}
|
||||
response.Data = append(response.Data, individualExchange)
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
// RESTGetAllEnabledAccountInfo via get request returns JSON response of account
|
||||
// info
|
||||
func RESTGetAllEnabledAccountInfo(w http.ResponseWriter, r *http.Request) {
|
||||
response := GetAllEnabledExchangeAccountInfo()
|
||||
err := RESTfulJSONResponse(w, r, response)
|
||||
if err != nil {
|
||||
RESTfulError(r.Method, err)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package smsglobal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"log"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -44,7 +45,7 @@ func SMSSendToAll(message string, cfg config.Config) {
|
||||
// SMSGetNumberByName returns contact number by supplied name
|
||||
func SMSGetNumberByName(name string, smsCfg config.SMSGlobalConfig) string {
|
||||
for _, contact := range smsCfg.Contacts {
|
||||
if contact.Name == name {
|
||||
if common.StringToUpper(contact.Name) == common.StringToUpper(name) {
|
||||
return contact.Number
|
||||
}
|
||||
}
|
||||
@@ -53,6 +54,10 @@ func SMSGetNumberByName(name string, smsCfg config.SMSGlobalConfig) string {
|
||||
|
||||
// SMSNotify sends a message to an individual contact
|
||||
func SMSNotify(to, message string, cfg config.Config) error {
|
||||
if flag.Lookup("test.v") != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := url.Values{}
|
||||
values.Set("action", "sendsms")
|
||||
values.Set("user", cfg.SMS.Username)
|
||||
|
||||
139
ticker_routes.go
139
ticker_routes.go
@@ -1,139 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func GetSpecificTicker(currency, exchangeName, assetType string) (ticker.Price, error) {
|
||||
var specificTicker ticker.Price
|
||||
var err error
|
||||
for i := 0; i < len(bot.exchanges); i++ {
|
||||
if bot.exchanges[i] != nil {
|
||||
if bot.exchanges[i].IsEnabled() && bot.exchanges[i].GetName() == exchangeName {
|
||||
specificTicker, err = bot.exchanges[i].GetTickerPrice(
|
||||
pair.NewCurrencyPairFromString(currency),
|
||||
assetType,
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return specificTicker, err
|
||||
}
|
||||
|
||||
func jsonTickerResponse(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
currency := vars["currency"]
|
||||
exchange := vars["exchangeName"]
|
||||
assetType := vars["assetType"]
|
||||
|
||||
if assetType == "" {
|
||||
assetType = ticker.Spot
|
||||
}
|
||||
response, err := GetSpecificTicker(currency, exchange, assetType)
|
||||
if err != nil {
|
||||
log.Printf("Failed to fetch ticker for %s currency: %s\n", exchange,
|
||||
currency)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// AllEnabledExchangeCurrencies holds the enabled exchange currencies
|
||||
type AllEnabledExchangeCurrencies struct {
|
||||
Data []EnabledExchangeCurrencies `json:"data"`
|
||||
}
|
||||
|
||||
// EnabledExchangeCurrencies is a sub type for singular exchanges and respective
|
||||
// currencies
|
||||
type EnabledExchangeCurrencies struct {
|
||||
ExchangeName string `json:"exchangeName"`
|
||||
ExchangeValues []ticker.Price `json:"exchangeValues"`
|
||||
}
|
||||
|
||||
func GetAllActiveTickers() []EnabledExchangeCurrencies {
|
||||
var tickerData []EnabledExchangeCurrencies
|
||||
|
||||
for _, individualBot := range bot.exchanges {
|
||||
if individualBot != nil && individualBot.IsEnabled() {
|
||||
var individualExchange EnabledExchangeCurrencies
|
||||
exchangeName := individualBot.GetName()
|
||||
individualExchange.ExchangeName = exchangeName
|
||||
log.Println(
|
||||
"Getting enabled currencies for '" + exchangeName + "'",
|
||||
)
|
||||
currencies := individualBot.GetEnabledCurrencies()
|
||||
for _, x := range currencies {
|
||||
currency := x
|
||||
assetTypes, err := exchange.GetExchangeAssetTypes(exchangeName)
|
||||
if err != nil {
|
||||
log.Printf("failed to get %s exchange asset types. Error: %s",
|
||||
exchangeName, err)
|
||||
continue
|
||||
}
|
||||
var tickerPrice ticker.Price
|
||||
if len(assetTypes) > 1 {
|
||||
for y := range assetTypes {
|
||||
tickerPrice, err = individualBot.UpdateTicker(currency,
|
||||
assetTypes[y])
|
||||
}
|
||||
} else {
|
||||
tickerPrice, err = individualBot.UpdateTicker(currency,
|
||||
assetTypes[0])
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("failed to get %s %s ticker. Error: %s",
|
||||
currency.Pair().String(),
|
||||
exchangeName,
|
||||
err)
|
||||
continue
|
||||
}
|
||||
|
||||
individualExchange.ExchangeValues = append(
|
||||
individualExchange.ExchangeValues, tickerPrice,
|
||||
)
|
||||
}
|
||||
tickerData = append(tickerData, individualExchange)
|
||||
}
|
||||
}
|
||||
return tickerData
|
||||
}
|
||||
|
||||
func getAllActiveTickersResponse(w http.ResponseWriter, r *http.Request) {
|
||||
var response AllEnabledExchangeCurrencies
|
||||
response.Data = GetAllActiveTickers()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ExchangeRoutes denotes the current exchange routes
|
||||
var ExchangeRoutes = Routes{
|
||||
Route{
|
||||
"AllActiveExchangesAndCurrencies",
|
||||
"GET",
|
||||
"/exchanges/enabled/latest/all",
|
||||
getAllActiveTickersResponse,
|
||||
},
|
||||
Route{
|
||||
"IndividualExchangeAndCurrency",
|
||||
"GET",
|
||||
"/exchanges/{exchangeName}/latest/{currency}",
|
||||
jsonTickerResponse,
|
||||
},
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package main
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -74,7 +75,7 @@ func main() {
|
||||
|
||||
log.Println("Connected to websocket!")
|
||||
log.Println("Authenticating..")
|
||||
SendWebsocketAuth("username", "password")
|
||||
SendWebsocketAuth("blah", "blah")
|
||||
|
||||
var wsResp WebsocketEventResponse
|
||||
err = WSConn.ReadJSON(&wsResp)
|
||||
@@ -112,9 +113,22 @@ func main() {
|
||||
|
||||
log.Printf("Fetched config.")
|
||||
|
||||
dataJSON, err := common.JSONEncode(&wsResp.Data)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var resultCfg config.Config
|
||||
err = common.JSONDecode(dataJSON, &resultCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
resultCfg.Name = "TEST"
|
||||
|
||||
req = WebsocketEvent{
|
||||
Event: "SaveConfig",
|
||||
Data: wsResp.Data,
|
||||
Data: resultCfg,
|
||||
}
|
||||
|
||||
log.Println("Saving config..")
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
// AllEnabledExchangeAccounts holds all enabled accounts info
|
||||
type AllEnabledExchangeAccounts struct {
|
||||
Data []exchange.AccountInfo `json:"data"`
|
||||
}
|
||||
|
||||
// GetCollatedExchangeAccountInfoByCoin collates individual exchange account
|
||||
// information and turns into into a map string of
|
||||
// exchange.AccountCurrencyInfo
|
||||
func GetCollatedExchangeAccountInfoByCoin(accounts []exchange.AccountInfo) map[string]exchange.AccountCurrencyInfo {
|
||||
result := make(map[string]exchange.AccountCurrencyInfo)
|
||||
for i := 0; i < len(accounts); i++ {
|
||||
for j := 0; j < len(accounts[i].Currencies); j++ {
|
||||
currencyName := accounts[i].Currencies[j].CurrencyName
|
||||
avail := accounts[i].Currencies[j].TotalValue
|
||||
onHold := accounts[i].Currencies[j].Hold
|
||||
|
||||
info, ok := result[currencyName]
|
||||
if !ok {
|
||||
accountInfo := exchange.AccountCurrencyInfo{CurrencyName: currencyName, Hold: onHold, TotalValue: avail}
|
||||
result[currencyName] = accountInfo
|
||||
} else {
|
||||
info.Hold += onHold
|
||||
info.TotalValue += avail
|
||||
result[currencyName] = info
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetAccountCurrencyInfoByExchangeName returns info for an exchange
|
||||
func GetAccountCurrencyInfoByExchangeName(accounts []exchange.AccountInfo, exchangeName string) (exchange.AccountInfo, error) {
|
||||
for i := 0; i < len(accounts); i++ {
|
||||
if accounts[i].ExchangeName == exchangeName {
|
||||
return accounts[i], nil
|
||||
}
|
||||
}
|
||||
return exchange.AccountInfo{}, errors.New(exchange.ErrExchangeNotFound)
|
||||
}
|
||||
|
||||
// GetAllEnabledExchangeAccountInfo returns all the current enabled exchanges
|
||||
func GetAllEnabledExchangeAccountInfo() AllEnabledExchangeAccounts {
|
||||
var response AllEnabledExchangeAccounts
|
||||
for _, individualBot := range bot.exchanges {
|
||||
if individualBot != nil && individualBot.IsEnabled() {
|
||||
if !individualBot.GetAuthenticatedAPISupport() {
|
||||
log.Printf("GetAllEnabledExchangeAccountInfo: Skippping %s due to disabled authenticated API support.", individualBot.GetName())
|
||||
continue
|
||||
}
|
||||
individualExchange, err := individualBot.GetExchangeAccountInfo()
|
||||
if err != nil {
|
||||
log.Printf("Error encountered retrieving exchange account info for %s. Error %s",
|
||||
individualBot.GetName(), err)
|
||||
continue
|
||||
}
|
||||
response.Data = append(response.Data, individualExchange)
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
// SendAllEnabledAccountInfo via get request returns JSON response of account
|
||||
// info
|
||||
func SendAllEnabledAccountInfo(w http.ResponseWriter, r *http.Request) {
|
||||
response := GetAllEnabledExchangeAccountInfo()
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// WalletRoutes are current routes specified for queries.
|
||||
var WalletRoutes = Routes{
|
||||
Route{
|
||||
"AllEnabledAccountInfo",
|
||||
"GET",
|
||||
"/exchanges/enabled/accounts/all",
|
||||
SendAllEnabledAccountInfo,
|
||||
},
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetCollatedExchangeAccountInfoByCoin(t *testing.T) {
|
||||
GetCollatedExchangeAccountInfoByCoin(GetAllEnabledExchangeAccountInfo().Data)
|
||||
}
|
||||
|
||||
func TestGetAccountCurrencyInfoByExchangeName(t *testing.T) {
|
||||
_, err := GetAccountCurrencyInfoByExchangeName(
|
||||
GetAllEnabledExchangeAccountInfo().Data, "ANX",
|
||||
)
|
||||
if err == nil {
|
||||
t.Error(
|
||||
"Test Failed - Wallet_Routes_Test.go - GetAccountCurrencyInfoByExchangeName",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllEnabledExchangeAccountInfo(t *testing.T) {
|
||||
if value := GetAllEnabledExchangeAccountInfo(); len(value.Data) != 0 {
|
||||
t.Error(
|
||||
"Test Failed - Wallet_Routes_Test.go - GetAllEnabledExchangeAccountInfo",
|
||||
)
|
||||
}
|
||||
}
|
||||
280
websocket.go
280
websocket.go
@@ -9,12 +9,15 @@ import (
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
)
|
||||
|
||||
// Const vars for websocket
|
||||
const (
|
||||
WebsocketResponseSuccess = "OK"
|
||||
)
|
||||
|
||||
// WebsocketRoutes adds ws route to the HTTP server
|
||||
var WebsocketRoutes = Routes{
|
||||
Route{
|
||||
"ws",
|
||||
@@ -24,6 +27,7 @@ var WebsocketRoutes = Routes{
|
||||
},
|
||||
}
|
||||
|
||||
// WebsocketClient stores information related to the websocket client
|
||||
type WebsocketClient struct {
|
||||
ID int
|
||||
Conn *websocket.Conn
|
||||
@@ -31,6 +35,7 @@ type WebsocketClient struct {
|
||||
Authenticated bool
|
||||
}
|
||||
|
||||
// WebsocketEvent is the struct used for websocket events
|
||||
type WebsocketEvent struct {
|
||||
Exchange string `json:"exchange,omitempty"`
|
||||
AssetType string `json:"assetType,omitempty"`
|
||||
@@ -38,20 +43,26 @@ type WebsocketEvent struct {
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
// WebsocketEventResponse is the struct used for websocket event responses
|
||||
type WebsocketEventResponse struct {
|
||||
Event string `json:"event"`
|
||||
Data interface{} `json:"data"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type WebsocketTickerRequest struct {
|
||||
// WebsocketOrderbookTickerRequest is a struct used for ticker and orderbook
|
||||
// requests
|
||||
type WebsocketOrderbookTickerRequest struct {
|
||||
Exchange string `json:"exchangeName"`
|
||||
Currency string `json:"currency"`
|
||||
AssetType string `json:"assetType"`
|
||||
}
|
||||
|
||||
// WebsocketClientHub stores an array of websocket clients
|
||||
var WebsocketClientHub []WebsocketClient
|
||||
|
||||
// WebsocketClientHandler upgrades the HTTP connection to a websocket
|
||||
// compatible one
|
||||
func WebsocketClientHandler(w http.ResponseWriter, r *http.Request) {
|
||||
upgrader := websocket.Upgrader{
|
||||
WriteBufferSize: 1024,
|
||||
@@ -73,6 +84,7 @@ func WebsocketClientHandler(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("New websocket client connected.")
|
||||
}
|
||||
|
||||
// DisconnectWebsocketClient disconnects a websocket client
|
||||
func DisconnectWebsocketClient(id int, err error) {
|
||||
for i := range WebsocketClientHub {
|
||||
if WebsocketClientHub[i].ID == id {
|
||||
@@ -84,6 +96,7 @@ func DisconnectWebsocketClient(id int, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// SendWebsocketMessage sends a websocket message to a specific client
|
||||
func SendWebsocketMessage(id int, data interface{}) error {
|
||||
for _, x := range WebsocketClientHub {
|
||||
if x.ID == id {
|
||||
@@ -93,6 +106,8 @@ func SendWebsocketMessage(id int, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BroadcastWebsocketMessage broadcasts a websocket event message to all
|
||||
// websocket clients
|
||||
func BroadcastWebsocketMessage(evt WebsocketEvent) error {
|
||||
for _, x := range WebsocketClientHub {
|
||||
x.Conn.WriteJSON(evt)
|
||||
@@ -100,29 +115,163 @@ func BroadcastWebsocketMessage(evt WebsocketEvent) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WebsocketAuth is a struct used for
|
||||
type WebsocketAuth struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type wsCommandHandler func(wsClient *websocket.Conn, data interface{}) error
|
||||
|
||||
var wsHandlers = map[string]wsCommandHandler{
|
||||
"getconfig": wsGetConfig,
|
||||
"saveconfig": wsSaveConfig,
|
||||
"getaccountinfo": wsGetAccountInfo,
|
||||
"gettickers": wsGetTickers,
|
||||
"getticker": wsGetTicker,
|
||||
"getorderbooks": wsGetOrderbooks,
|
||||
"getorderbook": wsGetOrderbook,
|
||||
"getexchangerates": wsGetExchangeRates,
|
||||
"getportfolio": wsGetPortfolio,
|
||||
}
|
||||
|
||||
func wsGetConfig(wsClient *websocket.Conn, data interface{}) error {
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "GetConfig",
|
||||
Data: bot.config,
|
||||
}
|
||||
return wsClient.WriteJSON(wsResp)
|
||||
}
|
||||
|
||||
func wsSaveConfig(wsClient *websocket.Conn, data interface{}) error {
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "SaveConfig",
|
||||
}
|
||||
var cfg config.Config
|
||||
err := common.JSONDecode(data.([]byte), &cfg)
|
||||
if err != nil {
|
||||
wsResp.Error = err.Error()
|
||||
err = wsClient.WriteJSON(wsResp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = bot.config.UpdateConfig(bot.configFile, cfg)
|
||||
if err != nil {
|
||||
wsResp.Error = err.Error()
|
||||
err = wsClient.WriteJSON(wsResp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
setupBotExchanges()
|
||||
wsResp.Data = WebsocketResponseSuccess
|
||||
return wsClient.WriteJSON(wsResp)
|
||||
}
|
||||
|
||||
func wsGetAccountInfo(wsClient *websocket.Conn, data interface{}) error {
|
||||
accountInfo := GetAllEnabledExchangeAccountInfo()
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "GetAccountInfo",
|
||||
Data: accountInfo,
|
||||
}
|
||||
return wsClient.WriteJSON(wsResp)
|
||||
}
|
||||
|
||||
func wsGetTickers(wsClient *websocket.Conn, data interface{}) error {
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "GetTickers",
|
||||
}
|
||||
wsResp.Data = GetAllActiveTickers()
|
||||
return wsClient.WriteJSON(wsResp)
|
||||
}
|
||||
|
||||
func wsGetTicker(wsClient *websocket.Conn, data interface{}) error {
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "GetTicker",
|
||||
}
|
||||
var tickerReq WebsocketOrderbookTickerRequest
|
||||
err := common.JSONDecode(data.([]byte), &tickerReq)
|
||||
if err != nil {
|
||||
wsResp.Error = err.Error()
|
||||
wsClient.WriteJSON(wsResp)
|
||||
return err
|
||||
}
|
||||
|
||||
result, err := GetSpecificTicker(tickerReq.Currency,
|
||||
tickerReq.Exchange, tickerReq.AssetType)
|
||||
|
||||
if err != nil {
|
||||
wsResp.Error = err.Error()
|
||||
wsClient.WriteJSON(wsResp)
|
||||
return err
|
||||
}
|
||||
wsResp.Data = result
|
||||
return wsClient.WriteJSON(wsResp)
|
||||
}
|
||||
|
||||
func wsGetOrderbooks(wsClient *websocket.Conn, data interface{}) error {
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "GetOrderbooks",
|
||||
}
|
||||
wsResp.Data = GetAllActiveOrderbooks()
|
||||
return wsClient.WriteJSON(wsResp)
|
||||
}
|
||||
|
||||
func wsGetOrderbook(wsClient *websocket.Conn, data interface{}) error {
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "GetOrderbook",
|
||||
}
|
||||
var orderbookReq WebsocketOrderbookTickerRequest
|
||||
err := common.JSONDecode(data.([]byte), &orderbookReq)
|
||||
if err != nil {
|
||||
wsResp.Error = err.Error()
|
||||
wsClient.WriteJSON(wsResp)
|
||||
return err
|
||||
}
|
||||
|
||||
result, err := GetSpecificOrderbook(orderbookReq.Currency,
|
||||
orderbookReq.Exchange, orderbookReq.AssetType)
|
||||
|
||||
if err != nil {
|
||||
wsResp.Error = err.Error()
|
||||
wsClient.WriteJSON(wsResp)
|
||||
return err
|
||||
}
|
||||
wsResp.Data = result
|
||||
return wsClient.WriteJSON(wsResp)
|
||||
}
|
||||
|
||||
func wsGetExchangeRates(wsClient *websocket.Conn, data interface{}) error {
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "GetExchangeRates",
|
||||
}
|
||||
if currency.YahooEnabled {
|
||||
wsResp.Data = currency.CurrencyStore
|
||||
} else {
|
||||
wsResp.Data = currency.CurrencyStoreFixer
|
||||
}
|
||||
return wsClient.WriteJSON(wsResp)
|
||||
}
|
||||
|
||||
func wsGetPortfolio(wsClient *websocket.Conn, data interface{}) error {
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "GetPortfolio",
|
||||
}
|
||||
wsResp.Data = bot.portfolio.GetPortfolioSummary()
|
||||
return wsClient.WriteJSON(wsResp)
|
||||
}
|
||||
|
||||
// WebsocketHandler Handles websocket client requests
|
||||
func WebsocketHandler() {
|
||||
for {
|
||||
for x := range WebsocketClientHub {
|
||||
msgType, msg, err := WebsocketClientHub[x].Conn.ReadMessage()
|
||||
if err != nil {
|
||||
DisconnectWebsocketClient(x, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if msgType != websocket.TextMessage {
|
||||
DisconnectWebsocketClient(x, err)
|
||||
continue
|
||||
}
|
||||
|
||||
var evt WebsocketEvent
|
||||
err = common.JSONDecode(msg, &evt)
|
||||
err := WebsocketClientHub[x].Conn.ReadJSON(&evt)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
DisconnectWebsocketClient(x, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -137,6 +286,9 @@ func WebsocketHandler() {
|
||||
continue
|
||||
}
|
||||
|
||||
req := common.StringToLower(evt.Event)
|
||||
log.Printf("Websocket req: %s", req)
|
||||
|
||||
if !WebsocketClientHub[x].Authenticated && evt.Event != "auth" {
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "auth",
|
||||
@@ -152,8 +304,8 @@ func WebsocketHandler() {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
hashPW := common.HexEncodeToString(common.GetSHA256([]byte("password")))
|
||||
if auth.Username == "username" && auth.Password == hashPW {
|
||||
hashPW := common.HexEncodeToString(common.GetSHA256([]byte(bot.config.Webserver.AdminUsername)))
|
||||
if auth.Username == bot.config.Webserver.AdminUsername && auth.Password == hashPW {
|
||||
WebsocketClientHub[x].Authenticated = true
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "auth",
|
||||
@@ -172,97 +324,15 @@ func WebsocketHandler() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
switch evt.Event {
|
||||
case "GetConfig":
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "GetConfig",
|
||||
Data: bot.config,
|
||||
}
|
||||
SendWebsocketMessage(x, wsResp)
|
||||
result, ok := wsHandlers[req]
|
||||
if !ok {
|
||||
log.Printf("Websocket unsupported event")
|
||||
continue
|
||||
case "SaveConfig":
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "SaveConfig",
|
||||
}
|
||||
var cfg config.Config
|
||||
err := common.JSONDecode(dataJSON, &cfg)
|
||||
if err != nil {
|
||||
wsResp.Error = err.Error()
|
||||
SendWebsocketMessage(x, wsResp)
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
//Save change the settings
|
||||
for x := range bot.config.Exchanges {
|
||||
for i := 0; i < len(cfg.Exchanges); i++ {
|
||||
if cfg.Exchanges[i].Name == bot.config.Exchanges[x].Name {
|
||||
bot.config.Exchanges[x].Enabled = cfg.Exchanges[i].Enabled
|
||||
bot.config.Exchanges[x].APIKey = cfg.Exchanges[i].APIKey
|
||||
bot.config.Exchanges[x].APISecret = cfg.Exchanges[i].APISecret
|
||||
bot.config.Exchanges[x].EnabledPairs = cfg.Exchanges[i].EnabledPairs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Reload the configuration
|
||||
err = bot.config.SaveConfig(bot.configFile)
|
||||
if err != nil {
|
||||
wsResp.Error = err.Error()
|
||||
SendWebsocketMessage(x, wsResp)
|
||||
continue
|
||||
}
|
||||
err = bot.config.LoadConfig(bot.configFile)
|
||||
if err != nil {
|
||||
wsResp.Error = err.Error()
|
||||
SendWebsocketMessage(x, wsResp)
|
||||
continue
|
||||
}
|
||||
setupBotExchanges()
|
||||
wsResp.Data = WebsocketResponseSuccess
|
||||
SendWebsocketMessage(x, wsResp)
|
||||
continue
|
||||
case "GetAccountInfo":
|
||||
accountInfo := GetAllEnabledExchangeAccountInfo()
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "GetAccountInfo",
|
||||
Data: accountInfo,
|
||||
}
|
||||
SendWebsocketMessage(x, wsResp)
|
||||
continue
|
||||
case "GetTicker":
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "GetTicker",
|
||||
}
|
||||
var tickerReq WebsocketTickerRequest
|
||||
err := common.JSONDecode(dataJSON, &tickerReq)
|
||||
if err != nil {
|
||||
wsResp.Error = err.Error()
|
||||
SendWebsocketMessage(x, wsResp)
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := GetSpecificTicker(tickerReq.Currency,
|
||||
tickerReq.Exchange, tickerReq.AssetType)
|
||||
|
||||
if err != nil {
|
||||
wsResp.Error = err.Error()
|
||||
SendWebsocketMessage(x, wsResp)
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
wsResp.Data = data
|
||||
SendWebsocketMessage(x, wsResp)
|
||||
continue
|
||||
|
||||
case "GetTickers":
|
||||
wsResp := WebsocketEventResponse{
|
||||
Event: "GetTickers",
|
||||
}
|
||||
tickers := GetAllActiveTickers()
|
||||
wsResp.Data = tickers
|
||||
SendWebsocketMessage(x, wsResp)
|
||||
err = result(WebsocketClientHub[x].Conn, dataJSON)
|
||||
if err != nil {
|
||||
log.Printf("Websocket request %s failed. Error %s", evt.Event, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user