mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-02 23:16:51 +00:00
Add EXMO exchange support
This commit is contained in:
@@ -28,6 +28,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
|||||||
| BTCC | Yes | Yes | No |
|
| BTCC | Yes | Yes | No |
|
||||||
| BTCMarkets | Yes | NA | NA |
|
| BTCMarkets | Yes | NA | NA |
|
||||||
| COINUT | Yes | No | NA |
|
| COINUT | Yes | No | NA |
|
||||||
|
| Exmo | Yes | NA | NA |
|
||||||
| GDAX(Coinbase) | Yes | Yes | No|
|
| GDAX(Coinbase) | Yes | Yes | No|
|
||||||
| Gemini | Yes | NA | NA |
|
| Gemini | Yes | NA | NA |
|
||||||
| HitBTC | Yes | Yes | NA |
|
| HitBTC | Yes | Yes | NA |
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
{
|
{
|
||||||
"Address": "1JCe8z4jJVNXSjohjM4i9Hh813dLCNx2Sy",
|
"Address": "1JCe8z4jJVNXSjohjM4i9Hh813dLCNx2Sy",
|
||||||
"CoinType": "BTC",
|
"CoinType": "BTC",
|
||||||
"Balance": 53000.0074432,
|
"Balance": 53000.0124432,
|
||||||
"Description": ""
|
"Description": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
{
|
{
|
||||||
"Address": "0xb794f5ea0ba39494ce839613fffba74279579268",
|
"Address": "0xb794f5ea0ba39494ce839613fffba74279579268",
|
||||||
"CoinType": "ETH",
|
"CoinType": "ETH",
|
||||||
"Balance": 1925000.288118,
|
"Balance": 1925000.2881249,
|
||||||
"Description": ""
|
"Description": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -254,6 +254,30 @@
|
|||||||
"Uppercase": true
|
"Uppercase": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Name": "EXMO",
|
||||||
|
"Enabled": true,
|
||||||
|
"Verbose": false,
|
||||||
|
"Websocket": false,
|
||||||
|
"UseSandbox": false,
|
||||||
|
"RESTPollingDelay": 10,
|
||||||
|
"AuthenticatedAPISupport": false,
|
||||||
|
"APIKey": "Key",
|
||||||
|
"APISecret": "Secret",
|
||||||
|
"AvailablePairs": "XRP_USD,XMR_USD,BTC_USD,ETH_USD,ETH_UAH,ETC_BTC,ETC_USD,XRP_BTC,ETH_USDT,USD_RUB,XMR_EUR,BTC_USDT,BTC_PLN,BCH_ETH,DASH_BTC,ETH_PLN,ZEC_BTC,ZEC_EUR,BCH_BTC,LTC_BTC,WAVES_RUB,KICK_ETH,ETH_EUR,ETH_RUB,USDT_USD,ZEC_RUB,USDT_RUB,DOGE_BTC,ETH_BTC,LTC_EUR,BTC_EUR,BTC_UAH,BCH_USD,BCH_RUB,DASH_USD,DASH_RUB,ZEC_USD,XMR_BTC,BTC_RUB,KICK_BTC,ETH_LTC,ETC_RUB,LTC_USD,LTC_RUB,XRP_RUB,WAVES_BTC",
|
||||||
|
"EnabledPairs": "BTC_USD,LTC_USD",
|
||||||
|
"BaseCurrencies": "USD,EUR,RUB,PLN,UAH",
|
||||||
|
"AssetTypes": "SPOT",
|
||||||
|
"ConfigCurrencyPairFormat": {
|
||||||
|
"Uppercase": true,
|
||||||
|
"Delimiter": "_"
|
||||||
|
},
|
||||||
|
"RequestCurrencyPairFormat": {
|
||||||
|
"Uppercase": true,
|
||||||
|
"Delimiter": "_",
|
||||||
|
"Separator": ","
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Name": "GDAX",
|
"Name": "GDAX",
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
@@ -330,7 +354,7 @@
|
|||||||
"AuthenticatedAPISupport": false,
|
"AuthenticatedAPISupport": false,
|
||||||
"APIKey": "Key",
|
"APIKey": "Key",
|
||||||
"APISecret": "Secret",
|
"APISecret": "Secret",
|
||||||
"AvailablePairs": "OMG-USDT,LINK-BTC,NAS-ETH,EOS-ETH,SWFTC-BTC,XEM-USDT,ZEC-USDT,DASH-BTC,PAY-BTC,EVX-BTC,MDS-ETH,TNT-BTC,QASH-ETH,SMT-ETH,TRX-ETH,THETA-USDT,LUN-ETH,RUFF-ETH,BCH-BTC,ELA-ETH,IOST-ETH,TNB-BTC,GNX-ETH,THETA-BTC,SNT-USDT,DAT-BTC,SOC-ETH,EOS-USDT,CHAT-ETH,MANA-BTC,SMT-USDT,XRP-BTC,LTC-USDT,QTUM-USDT,LET-BTC,BCD-BTC,SNT-BTC,CVC-USDT,ELF-ETH,GNT-ETH,UTK-BTC,SBTC-BTC,NEO-USDT,MCO-BTC,OST-ETH,HT-BTC,RCN-BTC,BT2-BTC,QUN-BTC,HSR-ETH,TOPC-ETH,SALT-ETH,AIDOC-ETH,WAX-BTC,CVC-ETH,DTA-ETH,BTC-USDT,MEE-ETH,POWR-ETH,GAS-ETH,ADX-ETH,NEO-BTC,SALT-BTC,LET-USDT,BTM-BTC,EKO-ETH,BAT-ETH,EKO-BTC,SRN-BTC,APPC-BTC,OCN-ETH,CMT-BTC,VEN-ETH,QTUM-ETH,REQ-BTC,BIFI-BTC,BTM-ETH,ICX-BTC,OCN-BTC,ZEC-BTC,ACT-BTC,DGD-ETH,DAT-ETH,ETC-USDT,OST-BTC,IOST-USDT,STK-ETH,MCO-ETH,HT-ETH,STORJ-BTC,HSR-BTC,QUN-ETH,SOC-BTC,ELF-BTC,CMT-ETH,VEN-BTC,GNT-BTC,DBC-BTC,STORJ-USDT,WAX-ETH,TRX-BTC,POWR-BTC,DTA-USDT,DTA-BTC,SNC-BTC,ZIL-BTC,MEE-BTC,LSK-BTC,NAS-BTC,TNB-ETH,SWFTC-ETH,LTC-BTC,EOS-BTC,LINK-ETH,IOST-BTC,YEE-BTC,HT-USDT,RUFF-BTC,RDN-BTC,LUN-BTC,GNX-BTC,ELA-BTC,LET-ETH,EVX-ETH,AST-BTC,ACT-ETH,BCH-USDT,DASH-USDT,ICX-ETH,BCX-BTC,PROPY-ETH,DGD-BTC,XRP-USDT,ZIL-ETH,ZRX-BTC,THETA-ETH,ETH-BTC,SNC-ETH,DBC-ETH,REQ-ETH,WICC-ETH,SMT-BTC,LSK-ETH,RPX-BTC,TNT-ETH,SRN-ETH,ETH-USDT,ITC-BTC,OMG-BTC,PAY-ETH,STK-BTC,VEN-USDT,MDS-BTC,ADX-BTC,ETC-BTC,AIDOC-BTC,KNC-BTC,HSR-USDT,QTUM-BTC,CVC-BTC,QSP-BTC,QSP-ETH,BTG-BTC,BAT-BTC,ZLA-ETH,QASH-BTC,ITC-ETH,XEM-BTC,MANA-ETH,GAS-BTC,CHAT-BTC,BT1-BTC,ZLA-BTC,OMG-ETH,RCN-ETH,UTK-ETH,TOPC-BTC,MTL-BTC,GNT-USDT,APPC-ETH,PROPY-BTC,WICC-BTC,RDN-ETH,ELF-USDT,YEE-ETH",
|
"AvailablePairs": "OMG-USDT,LINK-BTC,NAS-ETH,EOS-ETH,SWFTC-BTC,XEM-USDT,ZEC-USDT,DASH-BTC,PAY-BTC,EVX-BTC,MDS-ETH,TNT-BTC,QASH-ETH,SMT-ETH,TRX-ETH,THETA-USDT,LUN-ETH,RUFF-ETH,BCH-BTC,ELA-ETH,IOST-ETH,TNB-BTC,GNX-ETH,THETA-BTC,SNT-USDT,DAT-BTC,SOC-ETH,EOS-USDT,CHAT-ETH,MANA-BTC,SMT-USDT,XRP-BTC,LTC-USDT,QTUM-USDT,LET-BTC,BCD-BTC,SNT-BTC,CVC-USDT,ELF-ETH,GNT-ETH,UTK-BTC,SBTC-BTC,NEO-USDT,MCO-BTC,OST-ETH,HT-BTC,RCN-BTC,BT2-BTC,QUN-BTC,HSR-ETH,TOPC-ETH,SALT-ETH,AIDOC-ETH,WAX-BTC,CVC-ETH,DTA-ETH,BTC-USDT,MEE-ETH,POWR-ETH,GAS-ETH,ADX-ETH,NEO-BTC,SALT-BTC,LET-USDT,BTM-BTC,EKO-ETH,BAT-ETH,EKO-BTC,SRN-BTC,APPC-BTC,OCN-ETH,CMT-BTC,VEN-ETH,QTUM-ETH,REQ-BTC,BIFI-BTC,BTM-ETH,ICX-BTC,OCN-BTC,ZEC-BTC,ACT-BTC,DGD-ETH,DAT-ETH,ETC-USDT,OST-BTC,IOST-USDT,STK-ETH,MCO-ETH,HT-ETH,STORJ-BTC,HSR-BTC,QUN-ETH,SOC-BTC,ELF-BTC,CMT-ETH,VEN-BTC,GNT-BTC,DBC-BTC,STORJ-USDT,WAX-ETH,TRX-BTC,POWR-BTC,DTA-USDT,DTA-BTC,SNC-BTC,ZIL-BTC,MEE-BTC,LSK-BTC,NAS-BTC,TNB-ETH,SWFTC-ETH,LTC-BTC,EOS-BTC,LINK-ETH,IOST-BTC,YEE-BTC,HT-USDT,RUFF-BTC,RDN-BTC,LUN-BTC,GNX-BTC,ELA-BTC,LET-ETH,EVX-ETH,AST-BTC,ACT-ETH,BCH-USDT,DASH-USDT,ICX-ETH,BCX-BTC,MTN-ETH,PROPY-ETH,DGD-BTC,XRP-USDT,ZIL-ETH,ZRX-BTC,THETA-ETH,ETH-BTC,SNC-ETH,DBC-ETH,REQ-ETH,WICC-ETH,SMT-BTC,LSK-ETH,RPX-BTC,TNT-ETH,SRN-ETH,ETH-USDT,ITC-BTC,OMG-BTC,PAY-ETH,STK-BTC,VEN-USDT,MDS-BTC,ADX-BTC,ETC-BTC,AIDOC-BTC,KNC-BTC,HSR-USDT,QTUM-BTC,CVC-BTC,QSP-BTC,QSP-ETH,BTG-BTC,BAT-BTC,ZLA-ETH,QASH-BTC,ITC-ETH,XEM-BTC,MANA-ETH,GAS-BTC,MTN-BTC,CHAT-BTC,BT1-BTC,ZLA-BTC,OMG-ETH,RCN-ETH,UTK-ETH,TOPC-BTC,MTL-BTC,GNT-USDT,APPC-ETH,PROPY-BTC,WICC-BTC,RDN-ETH,ELF-USDT,YEE-ETH",
|
||||||
"EnabledPairs": "BTC-USDT",
|
"EnabledPairs": "BTC-USDT",
|
||||||
"BaseCurrencies": "USD",
|
"BaseCurrencies": "USD",
|
||||||
"AssetTypes": "SPOT",
|
"AssetTypes": "SPOT",
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ import (
|
|||||||
"github.com/thrasher-/gocryptotrader/exchanges/btcc"
|
"github.com/thrasher-/gocryptotrader/exchanges/btcc"
|
||||||
"github.com/thrasher-/gocryptotrader/exchanges/btcmarkets"
|
"github.com/thrasher-/gocryptotrader/exchanges/btcmarkets"
|
||||||
"github.com/thrasher-/gocryptotrader/exchanges/coinut"
|
"github.com/thrasher-/gocryptotrader/exchanges/coinut"
|
||||||
|
"github.com/thrasher-/gocryptotrader/exchanges/exmo"
|
||||||
"github.com/thrasher-/gocryptotrader/exchanges/gdax"
|
"github.com/thrasher-/gocryptotrader/exchanges/gdax"
|
||||||
"github.com/thrasher-/gocryptotrader/exchanges/gemini"
|
"github.com/thrasher-/gocryptotrader/exchanges/gemini"
|
||||||
"github.com/thrasher-/gocryptotrader/exchanges/hitbtc"
|
"github.com/thrasher-/gocryptotrader/exchanges/hitbtc"
|
||||||
"github.com/thrasher-/gocryptotrader/exchanges/huobi"
|
"github.com/thrasher-/gocryptotrader/exchanges/huobi"
|
||||||
"github.com/thrasher-/gocryptotrader/exchanges/itbit"
|
"github.com/thrasher-/gocryptotrader/exchanges/itbit"
|
||||||
"github.com/thrasher-/gocryptotrader/exchanges/kraken"
|
"github.com/thrasher-/gocryptotrader/exchanges/kraken"
|
||||||
@@ -147,6 +148,8 @@ func LoadExchange(name string) error {
|
|||||||
exch = new(btcmarkets.BTCMarkets)
|
exch = new(btcmarkets.BTCMarkets)
|
||||||
case "coinut":
|
case "coinut":
|
||||||
exch = new(coinut.COINUT)
|
exch = new(coinut.COINUT)
|
||||||
|
case "exmo":
|
||||||
|
exch = new(exmo.EXMO)
|
||||||
case "gdax":
|
case "gdax":
|
||||||
exch = new(gdax.GDAX)
|
exch = new(gdax.GDAX)
|
||||||
case "gemini":
|
case "gemini":
|
||||||
|
|||||||
359
exchanges/exmo/exmo.go
Normal file
359
exchanges/exmo/exmo.go
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
package exmo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/thrasher-/gocryptotrader/common"
|
||||||
|
"github.com/thrasher-/gocryptotrader/config"
|
||||||
|
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||||
|
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
exmoAPIURL = "https://api.exmo.com"
|
||||||
|
exmoAPIVersion = "1"
|
||||||
|
|
||||||
|
exmoTrades = "trades"
|
||||||
|
exmoOrderbook = "order_book"
|
||||||
|
exmoTicker = "ticker"
|
||||||
|
exmoPairSettings = "pair_settings"
|
||||||
|
exmoCurrency = "currency"
|
||||||
|
exmoUserInfo = "user_info"
|
||||||
|
exmoOrderCreate = "order_create"
|
||||||
|
exmoOrderCancel = "order_cancel"
|
||||||
|
exmoOpenOrders = "user_open_orders"
|
||||||
|
exmoUserTrades = "user_trades"
|
||||||
|
exmoCancelledOrders = "user_cancelled_orders"
|
||||||
|
exmoOrderTrades = "order_trades"
|
||||||
|
exmoRequiredAmount = "required_amount"
|
||||||
|
exmoDepositAddress = "deposit_address"
|
||||||
|
exmoWithdrawCrypt = "withdraw_crypt"
|
||||||
|
exmoGetWithdrawTXID = "withdraw_get_txid"
|
||||||
|
exmoExcodeCreate = "excode_create"
|
||||||
|
exmoExcodeLoad = "excode_load"
|
||||||
|
exmoWalletHistory = "wallet_history"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EXMO exchange struct
|
||||||
|
type EXMO struct {
|
||||||
|
exchange.Base
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rate limit: 180 per/minute
|
||||||
|
|
||||||
|
// SetDefaults sets the basic defaults for exmo
|
||||||
|
func (e *EXMO) SetDefaults() {
|
||||||
|
e.Name = "EXMO"
|
||||||
|
e.Enabled = false
|
||||||
|
e.Verbose = false
|
||||||
|
e.Websocket = false
|
||||||
|
e.RESTPollingDelay = 10
|
||||||
|
e.RequestCurrencyPairFormat.Delimiter = "_"
|
||||||
|
e.RequestCurrencyPairFormat.Uppercase = true
|
||||||
|
e.RequestCurrencyPairFormat.Separator = ","
|
||||||
|
e.ConfigCurrencyPairFormat.Delimiter = "_"
|
||||||
|
e.ConfigCurrencyPairFormat.Uppercase = true
|
||||||
|
e.AssetTypes = []string{ticker.Spot}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup takes in the supplied exchange configuration details and sets params
|
||||||
|
func (e *EXMO) Setup(exch config.ExchangeConfig) {
|
||||||
|
if !exch.Enabled {
|
||||||
|
e.SetEnabled(false)
|
||||||
|
} else {
|
||||||
|
e.Enabled = true
|
||||||
|
e.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||||
|
e.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||||
|
e.RESTPollingDelay = exch.RESTPollingDelay
|
||||||
|
e.Verbose = exch.Verbose
|
||||||
|
e.Websocket = exch.Websocket
|
||||||
|
e.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||||
|
e.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||||
|
e.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||||
|
err := e.SetCurrencyPairFormat()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = e.SetAssetTypes()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTrades returns the trades for a symbol or symbols
|
||||||
|
func (e *EXMO) GetTrades(symbol string) (map[string][]Trades, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("pair", symbol)
|
||||||
|
result := make(map[string][]Trades)
|
||||||
|
url := fmt.Sprintf("%s/v%s/%s", exmoAPIURL, exmoAPIVersion, exmoTrades)
|
||||||
|
err := common.SendHTTPGetRequest(common.EncodeURLValues(url, v), true, e.Verbose, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrderbook returns the orderbook for a symbol or symbols
|
||||||
|
func (e *EXMO) GetOrderbook(symbol string) (map[string]Orderbook, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("pair", symbol)
|
||||||
|
result := make(map[string]Orderbook)
|
||||||
|
url := fmt.Sprintf("%s/v%s/%s", exmoAPIURL, exmoAPIVersion, exmoOrderbook)
|
||||||
|
err := common.SendHTTPGetRequest(common.EncodeURLValues(url, v), true, e.Verbose, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTicker returns the ticker for a symbol or symbols
|
||||||
|
func (e *EXMO) GetTicker(symbol string) (map[string]Ticker, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("pair", symbol)
|
||||||
|
result := make(map[string]Ticker)
|
||||||
|
url := fmt.Sprintf("%s/v%s/%s", exmoAPIURL, exmoAPIVersion, exmoTicker)
|
||||||
|
err := common.SendHTTPGetRequest(common.EncodeURLValues(url, v), true, e.Verbose, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPairSettings returns the pair settings for a symbol or symbols
|
||||||
|
func (e *EXMO) GetPairSettings() (map[string]PairSettings, error) {
|
||||||
|
result := make(map[string]PairSettings)
|
||||||
|
url := fmt.Sprintf("%s/v%s/%s", exmoAPIURL, exmoAPIVersion, exmoPairSettings)
|
||||||
|
err := common.SendHTTPGetRequest(url, true, e.Verbose, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrency returns a list of currencies
|
||||||
|
func (e *EXMO) GetCurrency() ([]string, error) {
|
||||||
|
result := []string{}
|
||||||
|
url := fmt.Sprintf("%s/v%s/%s", exmoAPIURL, exmoAPIVersion, exmoCurrency)
|
||||||
|
err := common.SendHTTPGetRequest(url, true, e.Verbose, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserInfo returns the user info
|
||||||
|
func (e *EXMO) GetUserInfo() (UserInfo, error) {
|
||||||
|
var result UserInfo
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoUserInfo, url.Values{}, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrder creates an order
|
||||||
|
// Params: pair, quantity, price and type
|
||||||
|
// Type can be buy, sell, market_buy, market_sell, market_buy_total and market_sell_total
|
||||||
|
func (e *EXMO) CreateOrder(pair, orderType string, price, amount float64) (int64, error) {
|
||||||
|
type response struct {
|
||||||
|
OrderID int64 `json:"order_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("pair", pair)
|
||||||
|
v.Set("type", orderType)
|
||||||
|
v.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
|
||||||
|
v.Set("quantity", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||||
|
|
||||||
|
var result response
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoOrderCreate, v, &result)
|
||||||
|
return result.OrderID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CancelOrder cancels an order by the orderID
|
||||||
|
func (e *EXMO) CancelOrder(orderID int64) error {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("order_id", strconv.FormatInt(orderID, 10))
|
||||||
|
var result interface{}
|
||||||
|
return e.SendAuthenticatedHTTPRequest("POST", exmoOrderCancel, v, &result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOpenOrders returns the users open orders
|
||||||
|
func (e *EXMO) GetOpenOrders() (map[string]OpenOrders, error) {
|
||||||
|
result := make(map[string]OpenOrders)
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoOpenOrders, url.Values{}, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserTrades returns the user trades
|
||||||
|
func (e *EXMO) GetUserTrades(pair, offset, limit string) (map[string][]UserTrades, error) {
|
||||||
|
result := make(map[string][]UserTrades)
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("pair", pair)
|
||||||
|
|
||||||
|
if offset != "" {
|
||||||
|
v.Set("offset", offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit != "" {
|
||||||
|
v.Set("limit", limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoUserTrades, v, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCancelledOrders returns a list of cancelled orders
|
||||||
|
func (e *EXMO) GetCancelledOrders(offset, limit string) ([]CancelledOrder, error) {
|
||||||
|
var result []CancelledOrder
|
||||||
|
v := url.Values{}
|
||||||
|
|
||||||
|
if offset != "" {
|
||||||
|
v.Set("offset", offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit != "" {
|
||||||
|
v.Set("limit", limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoCancelledOrders, v, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrderTrades returns a history of order trade details for the specific orderID
|
||||||
|
func (e *EXMO) GetOrderTrades(orderID int64) (OrderTrades, error) {
|
||||||
|
var result OrderTrades
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("order_id", strconv.FormatInt(orderID, 10))
|
||||||
|
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoOrderTrades, v, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRequiredAmount calculates the sum of buying a certain amount of currency
|
||||||
|
// for the particular currency pair
|
||||||
|
func (e *EXMO) GetRequiredAmount(pair string, amount float64) (RequiredAmount, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("pair", pair)
|
||||||
|
v.Set("quantity", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||||
|
var result RequiredAmount
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoRequiredAmount, v, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDepositAddress returns a list of addresses for cryptocurrency deposits
|
||||||
|
func (e *EXMO) GetDepositAddress() (map[string]string, error) {
|
||||||
|
result := make(map[string]string)
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoDepositAddress, url.Values{}, &result)
|
||||||
|
log.Println(reflect.TypeOf(result).String())
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithdrawCryptocurrency withdraws a cryptocurrency from the exchange to the desired address
|
||||||
|
// NOTE: This API function is available only after request to their tech support team
|
||||||
|
func (e *EXMO) WithdrawCryptocurrency(currency, address, invoice string, amount float64) (int64, error) {
|
||||||
|
type response struct {
|
||||||
|
TaskID int64 `json:"task_id,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("currency", currency)
|
||||||
|
v.Set("address", address)
|
||||||
|
|
||||||
|
if common.StringToUpper(currency) == "XRP" {
|
||||||
|
v.Set(invoice, invoice)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||||
|
var result response
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoWithdrawCrypt, v, &result)
|
||||||
|
return result.TaskID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWithdrawTXID gets the result of a withdrawal request
|
||||||
|
func (e *EXMO) GetWithdrawTXID(taskID int64) (string, error) {
|
||||||
|
type response struct {
|
||||||
|
Status bool `json:"status"`
|
||||||
|
TXID string `json:"txid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("task_id", strconv.FormatInt(taskID, 10))
|
||||||
|
|
||||||
|
var result response
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoGetWithdrawTXID, v, &result)
|
||||||
|
return result.TXID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExcodeCreate creates an EXMO coupon
|
||||||
|
func (e *EXMO) ExcodeCreate(currency string, amount float64) (ExcodeCreate, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("currency", currency)
|
||||||
|
v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||||
|
|
||||||
|
var result ExcodeCreate
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoExcodeCreate, v, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExcodeLoad loads an EXMO coupon
|
||||||
|
func (e *EXMO) ExcodeLoad(excode string) (ExcodeLoad, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("code", excode)
|
||||||
|
|
||||||
|
var result ExcodeLoad
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoExcodeLoad, v, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWalletHistory returns the users deposit/withdrawal history
|
||||||
|
func (e *EXMO) GetWalletHistory(date int64) (WalletHistory, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("date", strconv.FormatInt(date, 10))
|
||||||
|
|
||||||
|
var result WalletHistory
|
||||||
|
err := e.SendAuthenticatedHTTPRequest("POST", exmoWalletHistory, v, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
|
||||||
|
func (e *EXMO) SendAuthenticatedHTTPRequest(method, endpoint string, vals url.Values, result interface{}) error {
|
||||||
|
if !e.AuthenticatedAPISupport {
|
||||||
|
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, e.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Nonce.Get() == 0 {
|
||||||
|
e.Nonce.Set(time.Now().UnixNano())
|
||||||
|
} else {
|
||||||
|
e.Nonce.Inc()
|
||||||
|
}
|
||||||
|
vals.Set("nonce", e.Nonce.String())
|
||||||
|
|
||||||
|
payload := vals.Encode()
|
||||||
|
hash := common.GetHMAC(common.HashSHA512, []byte(payload), []byte(e.APISecret))
|
||||||
|
|
||||||
|
if e.Verbose {
|
||||||
|
log.Printf("Sending %s request to %s with params %s\n", method, endpoint, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := make(map[string]string)
|
||||||
|
headers["Key"] = e.APIKey
|
||||||
|
headers["Sign"] = common.HexEncodeToString(hash)
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/v%s/%s", exmoAPIURL, exmoAPIVersion, endpoint)
|
||||||
|
resp, err := common.SendHTTPRequest(method, path, headers, strings.NewReader(payload))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Verbose {
|
||||||
|
log.Printf("Received raw: \n%s\n", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
var authResp AuthResponse
|
||||||
|
err = common.JSONDecode([]byte(resp), &authResp)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("unable to JSON Unmarshal auth response")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !authResp.Result && authResp.Error != "" {
|
||||||
|
return fmt.Errorf("auth error: %s", authResp.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = common.JSONDecode([]byte(resp), &result)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("unable to JSON Unmarshal response")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
93
exchanges/exmo/exmo_test.go
Normal file
93
exchanges/exmo/exmo_test.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package exmo
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
const (
|
||||||
|
APIKey = ""
|
||||||
|
APISecret = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
e EXMO
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetup(t *testing.T) {
|
||||||
|
e.AuthenticatedAPISupport = true
|
||||||
|
e.APIKey = APIKey
|
||||||
|
e.APISecret = APISecret
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetTrades(t *testing.T) {
|
||||||
|
_, err := e.GetTrades("BTC_USD")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed. Err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetOrderbook(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := e.GetOrderbook("BTC_USD")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed. Err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetTicker(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := e.GetTicker("BTC_USD")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed. Err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPairSettings(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := e.GetPairSettings()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed. Err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCurrency(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := e.GetCurrency()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed. Err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetUserInfo(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
if APIKey == "" || APISecret == "" {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
TestSetup(t)
|
||||||
|
_, err := e.GetUserInfo()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed. Err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetRequiredAmount(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
if APIKey == "" || APISecret == "" {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
TestSetup(t)
|
||||||
|
_, err := e.GetRequiredAmount("BTC_USD", 100)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed. Err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDepositAddress(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
if APIKey == "" || APISecret == "" {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
TestSetup(t)
|
||||||
|
_, err := e.GetDepositAddress()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Test failed. Err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
144
exchanges/exmo/exmo_types.go
Normal file
144
exchanges/exmo/exmo_types.go
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package exmo
|
||||||
|
|
||||||
|
// Trades holds trade data
|
||||||
|
type Trades struct {
|
||||||
|
TradeID int64 `json:"trade_id"`
|
||||||
|
Type string `json:"string"`
|
||||||
|
Quantity float64 `json:"quantity,string"`
|
||||||
|
Price float64 `json:"price,string"`
|
||||||
|
Amount float64 `json:"amount,string"`
|
||||||
|
Date int64 `json:"date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Orderbook holds the orderbook data
|
||||||
|
type Orderbook struct {
|
||||||
|
AskQuantity float64 `json:"ask_quantity,string"`
|
||||||
|
AskAmount float64 `json:"ask_amount,string"`
|
||||||
|
AskTop float64 `json:"ask_top,string"`
|
||||||
|
BidQuantity float64 `json:"bid_quantity,string"`
|
||||||
|
BidTop float64 `json:"bid_top,string"`
|
||||||
|
Ask [][]string `json:"ask"`
|
||||||
|
Bid [][]string `json:"bid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ticker holds the ticker data
|
||||||
|
type Ticker struct {
|
||||||
|
Buy float64 `json:"buy_price,string"`
|
||||||
|
Sell float64 `json:"sell_price,string"`
|
||||||
|
Last float64 `json:"last_trade,string"`
|
||||||
|
High float64 `json:"high,string"`
|
||||||
|
Low float64 `json:"low,string"`
|
||||||
|
Average float64 `json:"average,string"`
|
||||||
|
Volume float64 `json:"vol,string"`
|
||||||
|
VolumeCurrent float64 `json:"vol_curr,string"`
|
||||||
|
Updated int64 `json:"updated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PairSettings holds the pair settings
|
||||||
|
type PairSettings struct {
|
||||||
|
MinQuantity float64 `json:"min_quantity,string"`
|
||||||
|
MaxQuantity float64 `json:"max_quantity,string"`
|
||||||
|
MinPrice float64 `json:"min_price,string"`
|
||||||
|
MaxPrice float64 `json:"max_price,string"`
|
||||||
|
MaxAmount float64 `json:"max_amount,string"`
|
||||||
|
MinAmount float64 `json:"min_amount,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthResponse stores the auth reponse
|
||||||
|
type AuthResponse struct {
|
||||||
|
Result bool `json:"bool"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserInfo stores the user info
|
||||||
|
type UserInfo struct {
|
||||||
|
AuthResponse
|
||||||
|
UID int `json:"uid"`
|
||||||
|
ServerDate int `json:"server_date"`
|
||||||
|
Balances map[string]string `json:"balances"`
|
||||||
|
Reserved map[string]string `json:"reserved"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenOrders stores the order info
|
||||||
|
type OpenOrders struct {
|
||||||
|
OrderID int64 `json:"order_id,string"`
|
||||||
|
Created int64 `json:"created,string"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Pair string `json:"pair"`
|
||||||
|
Price float64 `json:"price,string"`
|
||||||
|
Quantity float64 `json:"quantity,string"`
|
||||||
|
Amount float64 `json:"amount,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserTrades stores the users trade info
|
||||||
|
type UserTrades struct {
|
||||||
|
TradeID int64 `json:"trade_id"`
|
||||||
|
Date int64 `json:"date"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Pair string `json:"pair"`
|
||||||
|
OrderID int64 `json:"order_id"`
|
||||||
|
Quantity float64 `json:"quantity"`
|
||||||
|
Price float64 `json:"price"`
|
||||||
|
Amount float64 `json:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CancelledOrder stores cancelled order data
|
||||||
|
type CancelledOrder struct {
|
||||||
|
Date int64 `json:"date"`
|
||||||
|
OrderID int64 `json:"order_id,string"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Pair string `json:"pair"`
|
||||||
|
Price float64 `json:"price,string"`
|
||||||
|
Quantity float64 `json:"quantity,string"`
|
||||||
|
Amount float64 `json:"amount,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderTrades stores order trade information
|
||||||
|
type OrderTrades struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
InCurrency string `json:"in_currency"`
|
||||||
|
InAmount float64 `json:"in_amount,string"`
|
||||||
|
OutCurrency string `json:"out_currency"`
|
||||||
|
OutAmount float64 `json:"out_amount,string"`
|
||||||
|
Trades []UserTrades `json:"trades"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequiredAmount stores the calculation for buying a certain amount of currency
|
||||||
|
// for a particular currency
|
||||||
|
type RequiredAmount struct {
|
||||||
|
Quantity float64 `json:"quantity,string"`
|
||||||
|
Amount float64 `json:"amount,string"`
|
||||||
|
AvgPrice float64 `json:"avg_price,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExcodeCreate stores the excode create coupon info
|
||||||
|
type ExcodeCreate struct {
|
||||||
|
TaskID int64 `json:"task_id"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Amount float64 `json:"amount,string"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
Balances map[string]string `json:"balances"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExcodeLoad stores the excode load coupon info
|
||||||
|
type ExcodeLoad struct {
|
||||||
|
TaskID int64 `json:"task_id"`
|
||||||
|
Amount float64 `json:"amount,string"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
Balances map[string]string `json:"balances"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalletHistory stores the users wallet history
|
||||||
|
type WalletHistory struct {
|
||||||
|
Begin int64 `json:"begin,string"`
|
||||||
|
End int64 `json:"end,string"`
|
||||||
|
History []struct {
|
||||||
|
Timestamp int64 `json:"dt"`
|
||||||
|
Type string `json:"string"`
|
||||||
|
Currency string `json:"curr"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
Amount float64 `json:"amount,string"`
|
||||||
|
Account string `json:"account,string"`
|
||||||
|
}
|
||||||
|
}
|
||||||
149
exchanges/exmo/exmo_wrapper.go
Normal file
149
exchanges/exmo/exmo_wrapper.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
package exmo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/thrasher-/gocryptotrader/common"
|
||||||
|
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||||
|
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||||
|
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||||
|
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Start starts the EXMO go routine
|
||||||
|
func (e *EXMO) Start() {
|
||||||
|
go e.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run implements the EXMO wrapper
|
||||||
|
func (e *EXMO) Run() {
|
||||||
|
if e.Verbose {
|
||||||
|
log.Printf("%s polling delay: %ds.\n", e.GetName(), e.RESTPollingDelay)
|
||||||
|
log.Printf("%s %d currencies enabled: %s.\n", e.GetName(), len(e.EnabledPairs), e.EnabledPairs)
|
||||||
|
}
|
||||||
|
|
||||||
|
exchangeProducts, err := e.GetPairSettings()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("%s Failed to get available products.\n", e.GetName())
|
||||||
|
} else {
|
||||||
|
var currencies []string
|
||||||
|
for x := range exchangeProducts {
|
||||||
|
currencies = append(currencies, x)
|
||||||
|
}
|
||||||
|
err = e.UpdateAvailableCurrencies(currencies, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("%s Failed to update available currencies.\n", e.GetName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateTicker updates and returns the ticker for a currency pair
|
||||||
|
func (e *EXMO) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||||
|
var tickerPrice ticker.Price
|
||||||
|
pairsCollated, err := exchange.GetAndFormatExchangeCurrencies(e.Name, e.GetEnabledCurrencies())
|
||||||
|
if err != nil {
|
||||||
|
return tickerPrice, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := e.GetTicker(pairsCollated.String())
|
||||||
|
if err != nil {
|
||||||
|
return tickerPrice, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, x := range e.GetEnabledCurrencies() {
|
||||||
|
currency := exchange.FormatExchangeCurrency(e.Name, x).String()
|
||||||
|
var tickerPrice ticker.Price
|
||||||
|
tickerPrice.Pair = x
|
||||||
|
tickerPrice.Last = result[currency].Last
|
||||||
|
tickerPrice.Ask = result[currency].Sell
|
||||||
|
tickerPrice.High = result[currency].High
|
||||||
|
tickerPrice.Bid = result[currency].Buy
|
||||||
|
tickerPrice.Last = result[currency].Last
|
||||||
|
tickerPrice.Low = result[currency].Low
|
||||||
|
tickerPrice.Volume = result[currency].Volume
|
||||||
|
ticker.ProcessTicker(e.Name, x, tickerPrice, assetType)
|
||||||
|
}
|
||||||
|
return ticker.GetTicker(e.Name, p, assetType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTickerPrice returns the ticker for a currency pair
|
||||||
|
func (e *EXMO) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||||
|
tick, err := ticker.GetTicker(e.GetName(), p, assetType)
|
||||||
|
if err != nil {
|
||||||
|
return e.UpdateTicker(p, assetType)
|
||||||
|
}
|
||||||
|
return tick, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrderbookEx returns the orderbook for a currency pair
|
||||||
|
func (e *EXMO) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||||
|
ob, err := orderbook.GetOrderbook(e.GetName(), p, assetType)
|
||||||
|
if err != nil {
|
||||||
|
return e.UpdateOrderbook(p, assetType)
|
||||||
|
}
|
||||||
|
return ob, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||||
|
func (e *EXMO) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||||
|
var orderBook orderbook.Base
|
||||||
|
pairsCollated, err := exchange.GetAndFormatExchangeCurrencies(e.Name, e.GetEnabledCurrencies())
|
||||||
|
if err != nil {
|
||||||
|
return orderBook, err
|
||||||
|
}
|
||||||
|
|
||||||
|
orderbookNew, err := e.GetOrderbook(pairsCollated.String())
|
||||||
|
if err != nil {
|
||||||
|
return orderBook, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, x := range e.GetEnabledCurrencies() {
|
||||||
|
currency := exchange.FormatExchangeCurrency(e.Name, x).String()
|
||||||
|
|
||||||
|
data := orderbookNew[currency]
|
||||||
|
for x := range data.Bid {
|
||||||
|
obData := data.Bid[x]
|
||||||
|
price, _ := strconv.ParseFloat(obData[0], 64)
|
||||||
|
amount, _ := strconv.ParseFloat(obData[1], 64)
|
||||||
|
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Price: price, Amount: amount})
|
||||||
|
}
|
||||||
|
|
||||||
|
for x := range data.Ask {
|
||||||
|
obData := data.Ask[x]
|
||||||
|
price, _ := strconv.ParseFloat(obData[0], 64)
|
||||||
|
amount, _ := strconv.ParseFloat(obData[1], 64)
|
||||||
|
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Price: price, Amount: amount})
|
||||||
|
}
|
||||||
|
orderbook.ProcessOrderbook(e.GetName(), p, orderBook, assetType)
|
||||||
|
}
|
||||||
|
|
||||||
|
orderbook.ProcessOrderbook(e.GetName(), p, orderBook, assetType)
|
||||||
|
return orderbook.GetOrderbook(e.Name, p, assetType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExchangeAccountInfo retrieves balances for all enabled currencies for the
|
||||||
|
// Exmo exchange
|
||||||
|
func (e *EXMO) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||||
|
var response exchange.AccountInfo
|
||||||
|
response.ExchangeName = e.GetName()
|
||||||
|
result, err := e.GetUserInfo()
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for x, y := range result.Balances {
|
||||||
|
var exchangeCurrency exchange.AccountCurrencyInfo
|
||||||
|
exchangeCurrency.CurrencyName = common.StringToUpper(x)
|
||||||
|
for z, w := range result.Reserved {
|
||||||
|
if z == x {
|
||||||
|
avail, _ := strconv.ParseFloat(y, 64)
|
||||||
|
reserved, _ := strconv.ParseFloat(w, 64)
|
||||||
|
exchangeCurrency.TotalValue = avail + reserved
|
||||||
|
exchangeCurrency.Hold = reserved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.Currencies = append(response.Currencies, exchangeCurrency)
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
30
testdata/configtest.json
vendored
30
testdata/configtest.json
vendored
@@ -13,7 +13,7 @@
|
|||||||
{
|
{
|
||||||
"Address": "1JCe8z4jJVNXSjohjM4i9Hh813dLCNx2Sy",
|
"Address": "1JCe8z4jJVNXSjohjM4i9Hh813dLCNx2Sy",
|
||||||
"CoinType": "BTC",
|
"CoinType": "BTC",
|
||||||
"Balance": 53000.0074432,
|
"Balance": 53000.0124432,
|
||||||
"Description": ""
|
"Description": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
{
|
{
|
||||||
"Address": "0xb794f5ea0ba39494ce839613fffba74279579268",
|
"Address": "0xb794f5ea0ba39494ce839613fffba74279579268",
|
||||||
"CoinType": "ETH",
|
"CoinType": "ETH",
|
||||||
"Balance": 1925000.288118,
|
"Balance": 1925000.2881249,
|
||||||
"Description": ""
|
"Description": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -277,6 +277,30 @@
|
|||||||
"Uppercase": true
|
"Uppercase": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Name": "EXMO",
|
||||||
|
"Enabled": true,
|
||||||
|
"Verbose": false,
|
||||||
|
"Websocket": false,
|
||||||
|
"UseSandbox": false,
|
||||||
|
"RESTPollingDelay": 10,
|
||||||
|
"AuthenticatedAPISupport": false,
|
||||||
|
"APIKey": "Key",
|
||||||
|
"APISecret": "Secret",
|
||||||
|
"AvailablePairs": "XRP_USD,XMR_USD,BTC_USD,ETH_USD,ETH_UAH,ETC_BTC,ETC_USD,XRP_BTC,ETH_USDT,USD_RUB,XMR_EUR,BTC_USDT,BTC_PLN,BCH_ETH,DASH_BTC,ETH_PLN,ZEC_BTC,ZEC_EUR,BCH_BTC,LTC_BTC,WAVES_RUB,KICK_ETH,ETH_EUR,ETH_RUB,USDT_USD,ZEC_RUB,USDT_RUB,DOGE_BTC,ETH_BTC,LTC_EUR,BTC_EUR,BTC_UAH,BCH_USD,BCH_RUB,DASH_USD,DASH_RUB,ZEC_USD,XMR_BTC,BTC_RUB,KICK_BTC,ETH_LTC,ETC_RUB,LTC_USD,LTC_RUB,XRP_RUB,WAVES_BTC",
|
||||||
|
"EnabledPairs": "BTC_USD,LTC_USD",
|
||||||
|
"BaseCurrencies": "USD,EUR,RUB,PLN,UAH",
|
||||||
|
"AssetTypes": "SPOT",
|
||||||
|
"ConfigCurrencyPairFormat": {
|
||||||
|
"Uppercase": true,
|
||||||
|
"Delimiter": "_"
|
||||||
|
},
|
||||||
|
"RequestCurrencyPairFormat": {
|
||||||
|
"Uppercase": true,
|
||||||
|
"Delimiter": "_",
|
||||||
|
"Separator": ","
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Name": "GDAX",
|
"Name": "GDAX",
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
@@ -353,7 +377,7 @@
|
|||||||
"AuthenticatedAPISupport": false,
|
"AuthenticatedAPISupport": false,
|
||||||
"APIKey": "Key",
|
"APIKey": "Key",
|
||||||
"APISecret": "Secret",
|
"APISecret": "Secret",
|
||||||
"AvailablePairs": "OMG-USDT,LINK-BTC,NAS-ETH,EOS-ETH,SWFTC-BTC,XEM-USDT,ZEC-USDT,DASH-BTC,PAY-BTC,EVX-BTC,MDS-ETH,TNT-BTC,QASH-ETH,SMT-ETH,TRX-ETH,THETA-USDT,LUN-ETH,RUFF-ETH,BCH-BTC,ELA-ETH,IOST-ETH,TNB-BTC,GNX-ETH,THETA-BTC,SNT-USDT,DAT-BTC,SOC-ETH,EOS-USDT,CHAT-ETH,MANA-BTC,SMT-USDT,XRP-BTC,LTC-USDT,QTUM-USDT,LET-BTC,BCD-BTC,SNT-BTC,CVC-USDT,ELF-ETH,GNT-ETH,UTK-BTC,SBTC-BTC,NEO-USDT,MCO-BTC,OST-ETH,HT-BTC,RCN-BTC,BT2-BTC,QUN-BTC,HSR-ETH,TOPC-ETH,SALT-ETH,AIDOC-ETH,WAX-BTC,CVC-ETH,DTA-ETH,BTC-USDT,MEE-ETH,POWR-ETH,GAS-ETH,ADX-ETH,NEO-BTC,SALT-BTC,LET-USDT,BTM-BTC,EKO-ETH,BAT-ETH,EKO-BTC,SRN-BTC,APPC-BTC,OCN-ETH,CMT-BTC,VEN-ETH,QTUM-ETH,REQ-BTC,BIFI-BTC,BTM-ETH,ICX-BTC,OCN-BTC,ZEC-BTC,ACT-BTC,DGD-ETH,DAT-ETH,ETC-USDT,OST-BTC,IOST-USDT,STK-ETH,MCO-ETH,HT-ETH,STORJ-BTC,HSR-BTC,QUN-ETH,SOC-BTC,ELF-BTC,CMT-ETH,VEN-BTC,GNT-BTC,DBC-BTC,STORJ-USDT,WAX-ETH,TRX-BTC,POWR-BTC,DTA-USDT,DTA-BTC,SNC-BTC,ZIL-BTC,MEE-BTC,LSK-BTC,NAS-BTC,TNB-ETH,SWFTC-ETH,LTC-BTC,EOS-BTC,LINK-ETH,IOST-BTC,YEE-BTC,HT-USDT,RUFF-BTC,RDN-BTC,LUN-BTC,GNX-BTC,ELA-BTC,LET-ETH,EVX-ETH,AST-BTC,ACT-ETH,BCH-USDT,DASH-USDT,ICX-ETH,BCX-BTC,PROPY-ETH,DGD-BTC,XRP-USDT,ZIL-ETH,ZRX-BTC,THETA-ETH,ETH-BTC,SNC-ETH,DBC-ETH,REQ-ETH,WICC-ETH,SMT-BTC,LSK-ETH,RPX-BTC,TNT-ETH,SRN-ETH,ETH-USDT,ITC-BTC,OMG-BTC,PAY-ETH,STK-BTC,VEN-USDT,MDS-BTC,ADX-BTC,ETC-BTC,AIDOC-BTC,KNC-BTC,HSR-USDT,QTUM-BTC,CVC-BTC,QSP-BTC,QSP-ETH,BTG-BTC,BAT-BTC,ZLA-ETH,QASH-BTC,ITC-ETH,XEM-BTC,MANA-ETH,GAS-BTC,CHAT-BTC,BT1-BTC,ZLA-BTC,OMG-ETH,RCN-ETH,UTK-ETH,TOPC-BTC,MTL-BTC,GNT-USDT,APPC-ETH,PROPY-BTC,WICC-BTC,RDN-ETH,ELF-USDT,YEE-ETH",
|
"AvailablePairs": "OMG-USDT,LINK-BTC,NAS-ETH,EOS-ETH,SWFTC-BTC,XEM-USDT,ZEC-USDT,DASH-BTC,PAY-BTC,EVX-BTC,MDS-ETH,TNT-BTC,QASH-ETH,SMT-ETH,TRX-ETH,THETA-USDT,LUN-ETH,RUFF-ETH,BCH-BTC,ELA-ETH,IOST-ETH,TNB-BTC,GNX-ETH,THETA-BTC,SNT-USDT,DAT-BTC,SOC-ETH,EOS-USDT,CHAT-ETH,MANA-BTC,SMT-USDT,XRP-BTC,LTC-USDT,QTUM-USDT,LET-BTC,BCD-BTC,SNT-BTC,CVC-USDT,ELF-ETH,GNT-ETH,UTK-BTC,SBTC-BTC,NEO-USDT,MCO-BTC,OST-ETH,HT-BTC,RCN-BTC,BT2-BTC,QUN-BTC,HSR-ETH,TOPC-ETH,SALT-ETH,AIDOC-ETH,WAX-BTC,CVC-ETH,DTA-ETH,BTC-USDT,MEE-ETH,POWR-ETH,GAS-ETH,ADX-ETH,NEO-BTC,SALT-BTC,LET-USDT,BTM-BTC,EKO-ETH,BAT-ETH,EKO-BTC,SRN-BTC,APPC-BTC,OCN-ETH,CMT-BTC,VEN-ETH,QTUM-ETH,REQ-BTC,BIFI-BTC,BTM-ETH,ICX-BTC,OCN-BTC,ZEC-BTC,ACT-BTC,DGD-ETH,DAT-ETH,ETC-USDT,OST-BTC,IOST-USDT,STK-ETH,MCO-ETH,HT-ETH,STORJ-BTC,HSR-BTC,QUN-ETH,SOC-BTC,ELF-BTC,CMT-ETH,VEN-BTC,GNT-BTC,DBC-BTC,STORJ-USDT,WAX-ETH,TRX-BTC,POWR-BTC,DTA-USDT,DTA-BTC,SNC-BTC,ZIL-BTC,MEE-BTC,LSK-BTC,NAS-BTC,TNB-ETH,SWFTC-ETH,LTC-BTC,EOS-BTC,LINK-ETH,IOST-BTC,YEE-BTC,HT-USDT,RUFF-BTC,RDN-BTC,LUN-BTC,GNX-BTC,ELA-BTC,LET-ETH,EVX-ETH,AST-BTC,ACT-ETH,BCH-USDT,DASH-USDT,ICX-ETH,BCX-BTC,MTN-ETH,PROPY-ETH,DGD-BTC,XRP-USDT,ZIL-ETH,ZRX-BTC,THETA-ETH,ETH-BTC,SNC-ETH,DBC-ETH,REQ-ETH,WICC-ETH,SMT-BTC,LSK-ETH,RPX-BTC,TNT-ETH,SRN-ETH,ETH-USDT,ITC-BTC,OMG-BTC,PAY-ETH,STK-BTC,VEN-USDT,MDS-BTC,ADX-BTC,ETC-BTC,AIDOC-BTC,KNC-BTC,HSR-USDT,QTUM-BTC,CVC-BTC,QSP-BTC,QSP-ETH,BTG-BTC,BAT-BTC,ZLA-ETH,QASH-BTC,ITC-ETH,XEM-BTC,MANA-ETH,GAS-BTC,MTN-BTC,CHAT-BTC,BT1-BTC,ZLA-BTC,OMG-ETH,RCN-ETH,UTK-ETH,TOPC-BTC,MTL-BTC,GNT-USDT,APPC-ETH,PROPY-BTC,WICC-BTC,RDN-ETH,ELF-USDT,YEE-ETH",
|
||||||
"EnabledPairs": "BTC-USDT",
|
"EnabledPairs": "BTC-USDT",
|
||||||
"BaseCurrencies": "USD",
|
"BaseCurrencies": "USD",
|
||||||
"AssetTypes": "SPOT",
|
"AssetTypes": "SPOT",
|
||||||
|
|||||||
Reference in New Issue
Block a user