mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
Exchange wrapper test tool (#353)
* Initial commit which runs through all wrapper funcs for one of each currency pair for each asset type for each exchange and then outputs it all into a JSON file * Fixes up data holding responses to allow for good json output after completion * Starting work for reading credentials from a config. Some variable names are more appropriate * Keys can now be read from keys.json and applied to exchanges. Fixes naming bug for console output * Cleans up implementation. Uses templating to output some pretty html report * Changes config to include a bank account to customise for withdrawals. Updates template for warnings for not implemented. Updates template to include jsonified parameters that are sent to the wrapper. Jsonifies the response so you actually understand whats returned. * Adds bank for withdrawals. Adds wallet address for crypto withdrawals * Adds ordersubmission configuration to config. Sets command line overrides. Adds lbank to config. Adds okgroup clientid to config. * Adds missing comma * Adds config to gitignore * Removes because thats not how it works... * Revert go mod changes * Formatting for prettiness * Adds asset type override * Adds verbose, exchangesToExclude, filename CL flags. Removes wg redeclaration. Creates func to check to load exchange before its loaded. Removes double variables and uses flag.Stringvar instead * Prettifies the JSON output * Stringifies the []byteified jsonified wrapper response in the template * Puts types in their own EXCLUSIVE types.go. LOWERCASES COMMAND LINE ARGUMENTS. Wraps vars in var so theres less vars to look at. * Generates wrapperconfig.json on the fly if not present. Adds missing config fields and exchanges when not present. Saves config file on finish. Fixes some formatting on output when its just a number * micro format update * Changes printfs to printlns. Handles config error. Comments on types. Fixes lint issue with shadow err declaration * Fixes linting issue, removes returns after fatals, evaluates assetType, formats template * Stringifies byte output when console output is selected. Verbose mode now includes wrapper responses in console output
This commit is contained in:
3
cmd/exchange_wrapper_issues/.gitignore
vendored
Normal file
3
cmd/exchange_wrapper_issues/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/data.json
|
||||
/output.json
|
||||
/report.html
|
||||
839
cmd/exchange_wrapper_issues/main.go
Normal file
839
cmd/exchange_wrapper_issues/main.go
Normal file
@@ -0,0 +1,839 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/engine"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println("Loading flags...")
|
||||
parseCLFlags()
|
||||
var err error
|
||||
log.Println("Loading engine...")
|
||||
engine.Bot, err = engine.New()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to initialise engine. Err: %s", err)
|
||||
}
|
||||
|
||||
engine.Bot.Settings = engine.Settings{
|
||||
DisableExchangeAutoPairUpdates: true,
|
||||
Verbose: verboseOverride,
|
||||
}
|
||||
|
||||
log.Println("Loading config...")
|
||||
wrapperConfig, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Printf("Error loading config: '%v', generating empty config", err)
|
||||
wrapperConfig = Config{
|
||||
Exchanges: make(map[string]*config.APICredentialsConfig),
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("Loading exchanges..")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for x := range exchange.Exchanges {
|
||||
name := exchange.Exchanges[x]
|
||||
if _, ok := wrapperConfig.Exchanges[name]; !ok {
|
||||
wrapperConfig.Exchanges[strings.ToLower(name)] = &config.APICredentialsConfig{}
|
||||
}
|
||||
if shouldLoadExchange(name) {
|
||||
err = engine.LoadExchange(name, true, &wg)
|
||||
if err != nil {
|
||||
log.Printf("Failed to load exchange %s. Err: %s", name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
log.Println("Done.")
|
||||
|
||||
if withdrawAddressOverride != "" {
|
||||
wrapperConfig.WalletAddress = withdrawAddressOverride
|
||||
}
|
||||
if orderTypeOverride != "LIMIT" {
|
||||
wrapperConfig.OrderSubmission.OrderType = orderTypeOverride
|
||||
}
|
||||
if orderSideOverride != "BUY" {
|
||||
wrapperConfig.OrderSubmission.OrderSide = orderSideOverride
|
||||
}
|
||||
if orderPriceOverride > 0 {
|
||||
wrapperConfig.OrderSubmission.Price = orderPriceOverride
|
||||
}
|
||||
if orderAmountOverride > 0 {
|
||||
wrapperConfig.OrderSubmission.Amount = orderAmountOverride
|
||||
}
|
||||
|
||||
log.Println("Testing exchange wrappers..")
|
||||
var exchangeResponses []ExchangeResponses
|
||||
|
||||
for x := range engine.Bot.Exchanges {
|
||||
base := engine.Bot.Exchanges[x].GetBase()
|
||||
if !base.Config.Enabled {
|
||||
log.Printf("Exchange %v not enabled, skipping", base.GetName())
|
||||
continue
|
||||
}
|
||||
base.Config.Verbose = verboseOverride
|
||||
base.Verbose = verboseOverride
|
||||
base.HTTPDebugging = false
|
||||
base.Config.HTTPDebugging = false
|
||||
wg.Add(1)
|
||||
|
||||
go func(num int) {
|
||||
name := engine.Bot.Exchanges[num].GetName()
|
||||
authenticated := setExchangeAPIKeys(name, wrapperConfig.Exchanges, base)
|
||||
wrapperResult := ExchangeResponses{
|
||||
ID: fmt.Sprintf("Exchange%v", num),
|
||||
ExchangeName: name,
|
||||
APIKeysSet: authenticated,
|
||||
AssetPairResponses: testWrappers(engine.Bot.Exchanges[num], base, &wrapperConfig),
|
||||
}
|
||||
for i := range wrapperResult.AssetPairResponses {
|
||||
wrapperResult.ErrorCount += wrapperResult.AssetPairResponses[i].ErrorCount
|
||||
}
|
||||
exchangeResponses = append(exchangeResponses, wrapperResult)
|
||||
wg.Done()
|
||||
}(x)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
log.Println("Done.")
|
||||
log.Println()
|
||||
|
||||
sort.Slice(exchangeResponses, func(i, j int) bool {
|
||||
return exchangeResponses[i].ExchangeName < exchangeResponses[j].ExchangeName
|
||||
})
|
||||
|
||||
if strings.EqualFold(outputOverride, "Console") {
|
||||
outputToConsole(exchangeResponses)
|
||||
}
|
||||
if strings.EqualFold(outputOverride, "JSON") {
|
||||
outputToJSON(exchangeResponses)
|
||||
}
|
||||
if strings.EqualFold(outputOverride, "HTML") {
|
||||
outputToHTML(exchangeResponses)
|
||||
}
|
||||
|
||||
saveConfig(&wrapperConfig)
|
||||
}
|
||||
|
||||
func parseCLFlags() {
|
||||
flag.StringVar(&exchangesToUseOverride, "exchanges", "", "a + delimited list of exchange names to run tests against eg -exchanges=bitfinex+anx")
|
||||
flag.StringVar(&exchangesToExcludeOverride, "excluded-exchanges", "", "a + delimited list of exchange names to ignore when they're being temperamental eg -exchangesToExlude=lbank")
|
||||
flag.StringVar(&assetTypeOverride, "asset", "", "the asset type to run tests against (where applicable)")
|
||||
flag.StringVar(¤cyPairOverride, "currency", "", "the currency to run tests against (where applicable)")
|
||||
flag.StringVar(&outputOverride, "output", "HTML", "JSON, HTML or Console")
|
||||
flag.BoolVar(&authenticatedOnly, "auth-only", false, "skip any wrapper function that doesn't require auth")
|
||||
flag.BoolVar(&verboseOverride, "verbose", false, "verbose CL output - if console output is selected then wrapper response is included")
|
||||
flag.StringVar(&orderSideOverride, "orderside", "BUY", "the order type for all order based wrapper tests")
|
||||
flag.StringVar(&orderTypeOverride, "ordertype", "LIMIT", "the order type for all order based wrapper tests")
|
||||
flag.Float64Var(&orderAmountOverride, "orderamount", 0, "the order amount for all order based wrapper tests")
|
||||
flag.Float64Var(&orderPriceOverride, "orderprice", 0, "the order price for all order based wrapper tests")
|
||||
flag.StringVar(&withdrawAddressOverride, "withdraw-wallet", "", "withdraw wallet address")
|
||||
flag.StringVar(&outputFileName, "filename", "report", "name of the output file eg 'report'.html or 'report'.json")
|
||||
flag.Parse()
|
||||
|
||||
if exchangesToUseOverride != "" {
|
||||
exchangesToUseList = strings.Split(exchangesToUseOverride, "+")
|
||||
}
|
||||
if exchangesToExcludeOverride != "" {
|
||||
exchangesToExcludeList = strings.Split(exchangesToExcludeOverride, "+")
|
||||
}
|
||||
}
|
||||
|
||||
func shouldLoadExchange(name string) bool {
|
||||
shouldLoadExchange := true
|
||||
if len(exchangesToUseList) > 0 {
|
||||
var found bool
|
||||
for i := range exchangesToUseList {
|
||||
if strings.EqualFold(name, exchangesToUseList[i]) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
shouldLoadExchange = false
|
||||
}
|
||||
}
|
||||
|
||||
if len(exchangesToExcludeList) > 0 {
|
||||
for i := range exchangesToExcludeList {
|
||||
if strings.EqualFold(name, exchangesToExcludeList[i]) {
|
||||
if shouldLoadExchange {
|
||||
shouldLoadExchange = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return shouldLoadExchange
|
||||
}
|
||||
|
||||
func setExchangeAPIKeys(name string, keys map[string]*config.APICredentialsConfig, base *exchange.Base) bool {
|
||||
lowerExchangeName := strings.ToLower(name)
|
||||
|
||||
if base.API.CredentialsValidator.RequiresKey && keys[lowerExchangeName].Key == "" {
|
||||
keys[lowerExchangeName].Key = config.DefaultAPIKey
|
||||
}
|
||||
if base.API.CredentialsValidator.RequiresSecret && keys[lowerExchangeName].Secret == "" {
|
||||
keys[lowerExchangeName].Secret = config.DefaultAPISecret
|
||||
}
|
||||
if base.API.CredentialsValidator.RequiresPEM && keys[lowerExchangeName].PEMKey == "" {
|
||||
keys[lowerExchangeName].PEMKey = "PEM"
|
||||
}
|
||||
if base.API.CredentialsValidator.RequiresClientID && keys[lowerExchangeName].ClientID == "" {
|
||||
keys[lowerExchangeName].ClientID = config.DefaultAPIClientID
|
||||
}
|
||||
if keys[lowerExchangeName].OTPSecret == "" {
|
||||
keys[lowerExchangeName].OTPSecret = "-" // Ensure OTP is available for use
|
||||
}
|
||||
|
||||
base.API.Credentials.Key = keys[lowerExchangeName].Key
|
||||
base.Config.API.Credentials.Key = keys[lowerExchangeName].Key
|
||||
|
||||
base.API.Credentials.Secret = keys[lowerExchangeName].Secret
|
||||
base.Config.API.Credentials.Secret = keys[lowerExchangeName].Secret
|
||||
|
||||
base.API.Credentials.ClientID = keys[lowerExchangeName].ClientID
|
||||
base.Config.API.Credentials.ClientID = keys[lowerExchangeName].ClientID
|
||||
|
||||
if keys[lowerExchangeName].OTPSecret != "-" {
|
||||
base.Config.API.Credentials.OTPSecret = keys[lowerExchangeName].OTPSecret
|
||||
}
|
||||
|
||||
base.API.AuthenticatedSupport = true
|
||||
base.API.AuthenticatedWebsocketSupport = true
|
||||
base.Config.API.AuthenticatedSupport = true
|
||||
base.Config.API.AuthenticatedWebsocketSupport = true
|
||||
|
||||
return base.ValidateAPICredentials()
|
||||
}
|
||||
|
||||
func parseOrderSide(orderSide string) exchange.OrderSide {
|
||||
switch orderSide {
|
||||
case exchange.AnyOrderSide.ToString():
|
||||
return exchange.AnyOrderSide
|
||||
case exchange.BuyOrderSide.ToString():
|
||||
return exchange.BuyOrderSide
|
||||
case exchange.SellOrderSide.ToString():
|
||||
return exchange.SellOrderSide
|
||||
case exchange.BidOrderSide.ToString():
|
||||
return exchange.BidOrderSide
|
||||
case exchange.AskOrderSide.ToString():
|
||||
return exchange.AskOrderSide
|
||||
default:
|
||||
log.Printf("Orderside '%v' not recognised, defaulting to BUY", orderSide)
|
||||
return exchange.BuyOrderSide
|
||||
}
|
||||
}
|
||||
|
||||
func parseOrderType(orderType string) exchange.OrderType {
|
||||
switch orderType {
|
||||
case exchange.AnyOrderType.ToString():
|
||||
return exchange.AnyOrderType
|
||||
case exchange.LimitOrderType.ToString():
|
||||
return exchange.LimitOrderType
|
||||
case exchange.MarketOrderType.ToString():
|
||||
return exchange.MarketOrderType
|
||||
case exchange.ImmediateOrCancelOrderType.ToString():
|
||||
return exchange.ImmediateOrCancelOrderType
|
||||
case exchange.StopOrderType.ToString():
|
||||
return exchange.StopOrderType
|
||||
case exchange.TrailingStopOrderType.ToString():
|
||||
return exchange.TrailingStopOrderType
|
||||
case exchange.UnknownOrderType.ToString():
|
||||
return exchange.UnknownOrderType
|
||||
default:
|
||||
log.Printf("OrderType '%v' not recognised, defaulting to LIMIT", orderTypeOverride)
|
||||
return exchange.LimitOrderType
|
||||
}
|
||||
}
|
||||
|
||||
func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config) []ExchangeAssetPairResponses {
|
||||
var response []ExchangeAssetPairResponses
|
||||
testOrderSide := parseOrderSide(config.OrderSubmission.OrderSide)
|
||||
testOrderType := parseOrderType(config.OrderSubmission.OrderType)
|
||||
assetTypes := base.GetAssetTypes()
|
||||
if assetTypeOverride != "" {
|
||||
if asset.IsValid(asset.Item(assetTypeOverride)) {
|
||||
assetTypes = asset.Items{asset.Item(assetTypeOverride)}
|
||||
} else {
|
||||
log.Printf("%v Asset Type '%v' not recognised, defaulting to exchange defaults", base.GetName(), assetTypeOverride)
|
||||
}
|
||||
}
|
||||
for i := range assetTypes {
|
||||
var msg string
|
||||
var p currency.Pair
|
||||
log.Printf("%v %v", base.GetName(), assetTypes[i])
|
||||
if _, ok := base.Config.CurrencyPairs.Pairs[assetTypes[i]]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case currencyPairOverride != "":
|
||||
p = currency.NewPairFromString(currencyPairOverride)
|
||||
case len(base.Config.CurrencyPairs.Pairs[assetTypes[i]].Enabled) == 0:
|
||||
if len(base.Config.CurrencyPairs.Pairs[assetTypes[i]].Available) == 0 {
|
||||
log.Printf("%v has no enabled or available currencies. Skipping", base.GetName())
|
||||
continue
|
||||
}
|
||||
p = base.Config.CurrencyPairs.Pairs[assetTypes[i]].Available.GetRandomPair()
|
||||
default:
|
||||
p = base.Config.CurrencyPairs.Pairs[assetTypes[i]].Enabled.GetRandomPair()
|
||||
}
|
||||
|
||||
responseContainer := ExchangeAssetPairResponses{
|
||||
AssetType: assetTypes[i],
|
||||
CurrencyPair: p,
|
||||
}
|
||||
|
||||
log.Printf("Setup config for %v %v %v", base.GetName(), assetTypes[i], p)
|
||||
err := e.Setup(base.Config)
|
||||
if err != nil {
|
||||
log.Printf("%v Encountered error reloading config: '%v'", base.GetName(), err)
|
||||
}
|
||||
log.Printf("Executing wrappers for %v %v %v", base.GetName(), assetTypes[i], p)
|
||||
|
||||
if !authenticatedOnly {
|
||||
var r1 ticker.Price
|
||||
r1, err = e.FetchTicker(p, assetTypes[i])
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{p, assetTypes[i]}),
|
||||
Function: "FetchTicker",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r1}),
|
||||
})
|
||||
|
||||
var r2 ticker.Price
|
||||
r2, err = e.UpdateTicker(p, assetTypes[i])
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{p, assetTypes[i]}),
|
||||
Function: "UpdateTicker",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r2}),
|
||||
})
|
||||
|
||||
var r3 orderbook.Base
|
||||
r3, err = e.FetchOrderbook(p, assetTypes[i])
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{p, assetTypes[i]}),
|
||||
Function: "FetchOrderbook",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r3}),
|
||||
})
|
||||
|
||||
var r4 orderbook.Base
|
||||
r4, err = e.UpdateOrderbook(p, assetTypes[i])
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{p, assetTypes[i]}),
|
||||
Function: "UpdateOrderbook",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r4}),
|
||||
})
|
||||
|
||||
var r5 []string
|
||||
r5, err = e.FetchTradablePairs(assetTypes[i])
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{assetTypes[i]}),
|
||||
Function: "FetchTradablePairs",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r5}),
|
||||
})
|
||||
// r6
|
||||
err = e.UpdateTradablePairs(false)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{false}),
|
||||
Function: "UpdateTradablePairs",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{nil}),
|
||||
})
|
||||
}
|
||||
|
||||
var r7 exchange.AccountInfo
|
||||
r7, err = e.GetAccountInfo()
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
Function: "GetAccountInfo",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r7}),
|
||||
})
|
||||
|
||||
var r8 []exchange.TradeHistory
|
||||
r8, err = e.GetExchangeHistory(p, assetTypes[i])
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{p, assetTypes[i]}),
|
||||
Function: "GetExchangeHistory",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r8}),
|
||||
})
|
||||
|
||||
var r9 []exchange.FundHistory
|
||||
r9, err = e.GetFundingHistory()
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
Function: "GetFundingHistory",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r9}),
|
||||
})
|
||||
|
||||
feeType := exchange.FeeBuilder{
|
||||
FeeType: exchange.CryptocurrencyTradeFee,
|
||||
Pair: p,
|
||||
PurchasePrice: config.OrderSubmission.Price,
|
||||
Amount: config.OrderSubmission.Amount,
|
||||
}
|
||||
var r10 float64
|
||||
r10, err = e.GetFeeByType(&feeType)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{feeType}),
|
||||
Function: "GetFeeByType-Trade",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r10}),
|
||||
})
|
||||
|
||||
s := &exchange.OrderSubmission{
|
||||
Pair: p,
|
||||
OrderSide: testOrderSide,
|
||||
OrderType: testOrderType,
|
||||
Amount: config.OrderSubmission.Amount,
|
||||
Price: config.OrderSubmission.Price,
|
||||
ClientID: config.OrderSubmission.OrderID,
|
||||
}
|
||||
var r11 exchange.SubmitOrderResponse
|
||||
r11, err = e.SubmitOrder(s)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{*s}),
|
||||
Function: "SubmitOrder",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r11}),
|
||||
})
|
||||
|
||||
modifyRequest := exchange.ModifyOrder{
|
||||
OrderID: config.OrderSubmission.OrderID,
|
||||
OrderType: testOrderType,
|
||||
OrderSide: testOrderSide,
|
||||
CurrencyPair: p,
|
||||
Price: config.OrderSubmission.Price,
|
||||
Amount: config.OrderSubmission.Amount,
|
||||
}
|
||||
var r12 string
|
||||
r12, err = e.ModifyOrder(&modifyRequest)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{modifyRequest}),
|
||||
Function: "ModifyOrder",
|
||||
Error: msg,
|
||||
Response: r12,
|
||||
})
|
||||
// r13
|
||||
cancelRequest := exchange.OrderCancellation{
|
||||
Side: testOrderSide,
|
||||
CurrencyPair: p,
|
||||
OrderID: config.OrderSubmission.OrderID,
|
||||
}
|
||||
err = e.CancelOrder(&cancelRequest)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{cancelRequest}),
|
||||
Function: "CancelOrder",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{nil}),
|
||||
})
|
||||
|
||||
var r14 exchange.CancelAllOrdersResponse
|
||||
r14, err = e.CancelAllOrders(&cancelRequest)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{cancelRequest}),
|
||||
Function: "CancelAllOrders",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r14}),
|
||||
})
|
||||
|
||||
var r15 exchange.OrderDetail
|
||||
r15, err = e.GetOrderInfo(config.OrderSubmission.OrderID)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{config.OrderSubmission.OrderID}),
|
||||
Function: "GetOrderInfo",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r15}),
|
||||
})
|
||||
|
||||
historyRequest := exchange.GetOrdersRequest{
|
||||
OrderType: testOrderType,
|
||||
OrderSide: testOrderSide,
|
||||
Currencies: []currency.Pair{p},
|
||||
}
|
||||
var r16 []exchange.OrderDetail
|
||||
r16, err = e.GetOrderHistory(&historyRequest)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{historyRequest}),
|
||||
Function: "GetOrderHistory",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r16}),
|
||||
})
|
||||
|
||||
orderRequest := exchange.GetOrdersRequest{
|
||||
OrderType: testOrderType,
|
||||
OrderSide: testOrderSide,
|
||||
Currencies: []currency.Pair{p},
|
||||
}
|
||||
var r17 []exchange.OrderDetail
|
||||
r17, err = e.GetActiveOrders(&orderRequest)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{orderRequest}),
|
||||
Function: "GetActiveOrders",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r17}),
|
||||
})
|
||||
|
||||
var r18 string
|
||||
r18, err = e.GetDepositAddress(p.Base, "")
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{p.Base, ""}),
|
||||
Function: "GetDepositAddress",
|
||||
Error: msg,
|
||||
Response: r18,
|
||||
})
|
||||
|
||||
feeType = exchange.FeeBuilder{
|
||||
FeeType: exchange.CryptocurrencyWithdrawalFee,
|
||||
Pair: p,
|
||||
PurchasePrice: config.OrderSubmission.Price,
|
||||
Amount: config.OrderSubmission.Amount,
|
||||
}
|
||||
var r19 float64
|
||||
r19, err = e.GetFeeByType(&feeType)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{feeType}),
|
||||
Function: "GetFeeByType-Crypto-Withdraw",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r19}),
|
||||
})
|
||||
|
||||
genericWithdrawRequest := exchange.GenericWithdrawRequestInfo{
|
||||
Amount: config.OrderSubmission.Amount,
|
||||
Currency: p.Quote,
|
||||
}
|
||||
withdrawRequest := exchange.CryptoWithdrawRequest{
|
||||
GenericWithdrawRequestInfo: genericWithdrawRequest,
|
||||
Address: withdrawAddressOverride,
|
||||
}
|
||||
var r20 string
|
||||
r20, err = e.WithdrawCryptocurrencyFunds(&withdrawRequest)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{withdrawRequest}),
|
||||
Function: "WithdrawCryptocurrencyFunds",
|
||||
Error: msg,
|
||||
Response: r20,
|
||||
})
|
||||
|
||||
feeType = exchange.FeeBuilder{
|
||||
FeeType: exchange.InternationalBankWithdrawalFee,
|
||||
Pair: p,
|
||||
PurchasePrice: config.OrderSubmission.Price,
|
||||
Amount: config.OrderSubmission.Amount,
|
||||
FiatCurrency: currency.AUD,
|
||||
BankTransactionType: exchange.WireTransfer,
|
||||
}
|
||||
var r21 float64
|
||||
r21, err = e.GetFeeByType(&feeType)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{feeType}),
|
||||
Function: "GetFeeByType-FIAT-Withdraw",
|
||||
Error: msg,
|
||||
Response: jsonifyInterface([]interface{}{r21}),
|
||||
})
|
||||
|
||||
fiatWithdrawRequest := exchange.FiatWithdrawRequest{
|
||||
GenericWithdrawRequestInfo: genericWithdrawRequest,
|
||||
BankAccountName: config.BankDetails.BankAccountName,
|
||||
BankAccountNumber: config.BankDetails.BankAccountNumber,
|
||||
SwiftCode: config.BankDetails.SwiftCode,
|
||||
IBAN: config.BankDetails.Iban,
|
||||
BankCity: config.BankDetails.BankCity,
|
||||
BankName: config.BankDetails.BankName,
|
||||
BankAddress: config.BankDetails.BankAddress,
|
||||
BankCountry: config.BankDetails.BankCountry,
|
||||
BankPostalCode: config.BankDetails.BankPostalCode,
|
||||
BankCode: config.BankDetails.BankCode,
|
||||
IsExpressWire: config.BankDetails.IsExpressWire,
|
||||
RequiresIntermediaryBank: config.BankDetails.RequiresIntermediaryBank,
|
||||
IntermediaryBankName: config.BankDetails.IntermediaryBankName,
|
||||
IntermediaryBankAccountNumber: config.BankDetails.IntermediaryBankAccountNumber,
|
||||
IntermediarySwiftCode: config.BankDetails.IntermediarySwiftCode,
|
||||
IntermediaryIBAN: config.BankDetails.IntermediaryIban,
|
||||
IntermediaryBankCity: config.BankDetails.IntermediaryBankCity,
|
||||
IntermediaryBankAddress: config.BankDetails.IntermediaryBankAddress,
|
||||
IntermediaryBankCountry: config.BankDetails.IntermediaryBankCountry,
|
||||
IntermediaryBankPostalCode: config.BankDetails.IntermediaryBankPostalCode,
|
||||
IntermediaryBankCode: config.BankDetails.IntermediaryBankCode,
|
||||
}
|
||||
var r22 string
|
||||
r22, err = e.WithdrawFiatFunds(&fiatWithdrawRequest)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{fiatWithdrawRequest}),
|
||||
Function: "WithdrawFiatFunds",
|
||||
Error: msg,
|
||||
Response: r22,
|
||||
})
|
||||
|
||||
var r23 string
|
||||
r23, err = e.WithdrawFiatFundsToInternationalBank(&fiatWithdrawRequest)
|
||||
msg = ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
responseContainer.ErrorCount++
|
||||
}
|
||||
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
|
||||
SentParams: jsonifyInterface([]interface{}{fiatWithdrawRequest}),
|
||||
Function: "WithdrawFiatFundsToInternationalBank",
|
||||
Error: msg,
|
||||
Response: r23,
|
||||
})
|
||||
response = append(response, responseContainer)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
func jsonifyInterface(params []interface{}) json.RawMessage {
|
||||
response, _ := json.MarshalIndent(params, "", " ")
|
||||
return response
|
||||
}
|
||||
|
||||
func loadConfig() (Config, error) {
|
||||
var config Config
|
||||
file, err := os.OpenFile("wrapperconfig.json", os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return config, err
|
||||
}
|
||||
defer file.Close()
|
||||
keys, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return config, err
|
||||
}
|
||||
|
||||
err = common.JSONDecode(keys, &config)
|
||||
return config, err
|
||||
}
|
||||
|
||||
func saveConfig(config *Config) {
|
||||
log.Println("JSONifying config...")
|
||||
jsonOutput, err := json.MarshalIndent(config, "", " ")
|
||||
if err != nil {
|
||||
log.Fatalf("Encountered error encoding JSON: %v", err)
|
||||
}
|
||||
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Printf("Encountered error retrieving output directory: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Outputting to: %v", filepath.Join(dir, "wrapperconfig.json"))
|
||||
err = common.WriteFile(filepath.Join(dir, "wrapperconfig.json"), jsonOutput)
|
||||
if err != nil {
|
||||
log.Printf("Encountered error writing to disk: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func outputToJSON(exchangeResponses []ExchangeResponses) {
|
||||
log.Println("JSONifying results...")
|
||||
jsonOutput, err := json.MarshalIndent(exchangeResponses, "", " ")
|
||||
if err != nil {
|
||||
log.Fatalf("Encountered error encoding JSON: %v", err)
|
||||
}
|
||||
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Printf("Encountered error retrieving output directory: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Outputting to: %v", filepath.Join(dir, fmt.Sprintf("%v.json", outputFileName)))
|
||||
err = common.WriteFile(filepath.Join(dir, fmt.Sprintf("%v.json", outputFileName)), jsonOutput)
|
||||
if err != nil {
|
||||
log.Printf("Encountered error writing to disk: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func outputToHTML(exchangeResponses []ExchangeResponses) {
|
||||
log.Println("Generating HTML report...")
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
|
||||
tmpl, err := template.New("report.tmpl").ParseFiles(filepath.Join(dir, "report.tmpl"))
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Outputting to: %v", filepath.Join(dir, fmt.Sprintf("%v.html", outputFileName)))
|
||||
file, err := os.Create(filepath.Join(dir, fmt.Sprintf("%v.html", outputFileName)))
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
err = tmpl.Execute(file, exchangeResponses)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func outputToConsole(exchangeResponses []ExchangeResponses) {
|
||||
var totalErrors int64
|
||||
for i := range exchangeResponses {
|
||||
log.Printf("------------%v Results-------------\n", exchangeResponses[i].ExchangeName)
|
||||
for j := range exchangeResponses[i].AssetPairResponses {
|
||||
for k := range exchangeResponses[i].AssetPairResponses[j].EndpointResponses {
|
||||
log.Printf("%v Result: %v", exchangeResponses[i].ExchangeName, k)
|
||||
log.Printf("Function:\t%v", exchangeResponses[i].AssetPairResponses[j].EndpointResponses[k].Function)
|
||||
log.Printf("AssetType:\t%v", exchangeResponses[i].AssetPairResponses[j].AssetType)
|
||||
log.Printf("Currency:\t%v\n", exchangeResponses[i].AssetPairResponses[j].CurrencyPair)
|
||||
log.Printf("Wrapper Params:\t%s\n", exchangeResponses[i].AssetPairResponses[j].EndpointResponses[k].SentParams)
|
||||
if exchangeResponses[i].AssetPairResponses[j].EndpointResponses[k].Error != "" {
|
||||
totalErrors++
|
||||
log.Printf("Error:\t%v", exchangeResponses[i].AssetPairResponses[j].EndpointResponses[k].Error)
|
||||
} else {
|
||||
log.Print("Error:\tnone")
|
||||
}
|
||||
if verboseOverride {
|
||||
log.Printf("Wrapper Response:\t%s", exchangeResponses[i].AssetPairResponses[j].EndpointResponses[k].Response)
|
||||
}
|
||||
log.Println()
|
||||
}
|
||||
}
|
||||
log.Println()
|
||||
}
|
||||
}
|
||||
155
cmd/exchange_wrapper_issues/report.tmpl
Normal file
155
cmd/exchange_wrapper_issues/report.tmpl
Normal file
File diff suppressed because one or more lines are too long
107
cmd/exchange_wrapper_issues/types.go
Normal file
107
cmd/exchange_wrapper_issues/types.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
)
|
||||
|
||||
// variables for command line overrides
|
||||
var (
|
||||
orderTypeOverride string
|
||||
outputOverride string
|
||||
orderSideOverride string
|
||||
currencyPairOverride string
|
||||
assetTypeOverride string
|
||||
orderPriceOverride float64
|
||||
orderAmountOverride float64
|
||||
withdrawAddressOverride string
|
||||
authenticatedOnly bool
|
||||
verboseOverride bool
|
||||
exchangesToUseOverride string
|
||||
exchangesToExcludeOverride string
|
||||
outputFileName string
|
||||
exchangesToUseList []string
|
||||
exchangesToExcludeList []string
|
||||
)
|
||||
|
||||
// Config the data structure for wrapperconfig.json to store all customisation
|
||||
type Config struct {
|
||||
OrderSubmission OrderSubmission `json:"orderSubmission"`
|
||||
WalletAddress string `json:"withdrawWalletAddress"`
|
||||
BankDetails Bank `json:"bankAccount"`
|
||||
Exchanges map[string]*config.APICredentialsConfig `json:"exchanges"`
|
||||
}
|
||||
|
||||
// Key is the format for wrapperconfig.json to store API credentials
|
||||
type Key struct {
|
||||
APIKey string `json:"apiKey"`
|
||||
APISecret string `json:"apiSecret,omitempty"`
|
||||
ClientID string `json:"clientId,omitempty"`
|
||||
OTPSecret string `json:"otpSecret,omitempty"`
|
||||
}
|
||||
|
||||
// ExchangeResponses contains all responses
|
||||
// associated with an exchange
|
||||
type ExchangeResponses struct {
|
||||
ID string
|
||||
ExchangeName string `json:"exchangeName"`
|
||||
AssetPairResponses []ExchangeAssetPairResponses `json:"responses"`
|
||||
ErrorCount int64 `json:"errorCount"`
|
||||
APIKeysSet bool `json:"apiKeysSet"`
|
||||
}
|
||||
|
||||
// ExchangeAssetPairResponses contains all responses
|
||||
// associated with an asset type and currency pair
|
||||
type ExchangeAssetPairResponses struct {
|
||||
ErrorCount int64 `json:"errorCount"`
|
||||
AssetType asset.Item `json:"asset"`
|
||||
CurrencyPair currency.Pair `json:"currency"`
|
||||
EndpointResponses []EndpointResponse `json:"responses"`
|
||||
}
|
||||
|
||||
// EndpointResponse is the data for an individual wrapper response
|
||||
type EndpointResponse struct {
|
||||
Function string `json:"function"`
|
||||
Error string `json:"error"`
|
||||
Response interface{} `json:"response"`
|
||||
SentParams json.RawMessage `json:"sentParams"`
|
||||
}
|
||||
|
||||
// Bank contains all required data for a wrapper withdrawal request
|
||||
type Bank struct {
|
||||
BankAccountName string `json:"bankAccountName"`
|
||||
BankAccountNumber float64 `json:"bankAccountNumber"`
|
||||
BankAddress string `json:"bankAddress"`
|
||||
BankCity string `json:"bankCity"`
|
||||
BankCountry string `json:"bankCountry"`
|
||||
BankName string `json:"bankName"`
|
||||
BankPostalCode string `json:"bankPostalCode"`
|
||||
Iban string `json:"iban"`
|
||||
IntermediaryBankAccountName string `json:"intermediaryBankAccountName"`
|
||||
IntermediaryBankAccountNumber float64 `json:"intermediaryBankAccountNumber"`
|
||||
IntermediaryBankAddress string `json:"intermediaryBankAddress"`
|
||||
IntermediaryBankCity string `json:"intermediaryBankCity"`
|
||||
IntermediaryBankCountry string `json:"intermediaryBankCountry"`
|
||||
IntermediaryBankName string `json:"intermediaryBankName"`
|
||||
IntermediaryBankPostalCode string `json:"intermediaryBankPostalCode"`
|
||||
IntermediaryIban string `json:"intermediaryIban"`
|
||||
IntermediaryIsExpressWire bool `json:"intermediaryIsExpressWire"`
|
||||
IntermediarySwiftCode string `json:"intermediarySwiftCode"`
|
||||
IsExpressWire bool `json:"isExpressWire"`
|
||||
RequiresIntermediaryBank bool `json:"requiresIntermediaryBank"`
|
||||
SwiftCode string `json:"swiftCode"`
|
||||
BankCode float64 `json:"bankCode"`
|
||||
IntermediaryBankCode float64 `json:"intermediaryBankCode"`
|
||||
}
|
||||
|
||||
// OrderSubmission contains all data required for a wrapper order submission
|
||||
type OrderSubmission struct {
|
||||
OrderSide string `json:"orderSide"`
|
||||
OrderType string `json:"orderType"`
|
||||
Amount float64 `json:"amount"`
|
||||
Price float64 `json:"price"`
|
||||
OrderID string `json:"orderID"`
|
||||
}
|
||||
178
cmd/exchange_wrapper_issues/wrapperconfig.json
Normal file
178
cmd/exchange_wrapper_issues/wrapperconfig.json
Normal file
@@ -0,0 +1,178 @@
|
||||
{
|
||||
"orderSubmission": {
|
||||
"orderSide": "BUY",
|
||||
"orderType": "LIMIT",
|
||||
"amount": 1333333337,
|
||||
"price": 1333333337,
|
||||
"orderID": ""
|
||||
},
|
||||
"withdrawWalletAddress": "",
|
||||
"bankAccount": {
|
||||
"bankAccountName": "bankAccountName",
|
||||
"bankAccountNumber": 1337,
|
||||
"bankAddress": "bankAddress",
|
||||
"bankCity": "bankCity",
|
||||
"bankCountry": "bankCountry",
|
||||
"bankName": "bankName",
|
||||
"bankPostalCode": "bankPostalCode",
|
||||
"iban": "iban",
|
||||
"intermediaryBankAccountName": "intermediaryBankAccountName",
|
||||
"intermediaryBankAccountNumber": 1337,
|
||||
"intermediaryBankAddress": "intermediaryBankAddress",
|
||||
"intermediaryBankCity": "intermediaryBankCity",
|
||||
"intermediaryBankCountry": "intermediaryBankCountry",
|
||||
"intermediaryBankName": "intermediaryBankName",
|
||||
"intermediaryBankPostalCode": "intermediaryBankPostalCode",
|
||||
"intermediaryIban": "intermediaryIban",
|
||||
"intermediaryIsExpressWire": false,
|
||||
"intermediarySwiftCode": "intermediarySwiftCode",
|
||||
"isExpressWire": false,
|
||||
"requiresIntermediaryBank": false,
|
||||
"swiftCode": "swiftCode",
|
||||
"bankCode": 1337,
|
||||
"intermediaryBankCode": 1337
|
||||
},
|
||||
"exchanges": {
|
||||
"alphapoint": {},
|
||||
"anx": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"binance": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"bitfinex": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"bitflyer": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"bithumb": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"bitmex": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"bitstamp": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"clientID": "ClientID",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"bittrex": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"btc markets": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"btse": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"coinbasepro": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"clientID": "ClientID",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"coinut": {
|
||||
"key": "Key",
|
||||
"clientID": "ClientID",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"exmo": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"gateio": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"gemini": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"hitbtc": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"huobi": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"huobihadax": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"itbit": {
|
||||
"secret": "Secret",
|
||||
"clientID": "ClientID",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"kraken": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"lakebtc": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"lbank": {},
|
||||
"localbitcoins": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"okcoin international": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"clientID": "ClientID",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"okex": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"clientID": "ClientID",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"poloniex": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"yobit": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
},
|
||||
"zb": {
|
||||
"key": "Key",
|
||||
"secret": "Secret",
|
||||
"otpSecret": "-"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user