mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-15 07:26:49 +00:00
Add EXMO exchange support
This commit is contained in:
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
|
||||
}
|
||||
Reference in New Issue
Block a user