diff --git a/exchanges/kraken/kraken.go b/exchanges/kraken/kraken.go index 2e4f1979..6304e02f 100644 --- a/exchanges/kraken/kraken.go +++ b/exchanges/kraken/kraken.go @@ -111,40 +111,51 @@ func (k *Kraken) GetFee(cryptoTrade bool) float64 { } // GetServerTime returns current server time -func (k *Kraken) GetServerTime(unixTime bool) (interface{}, error) { - var result GeneralResponse +func (k *Kraken) GetServerTime() (TimeResponse, error) { path := fmt.Sprintf("%s/%s/public/%s", krakenAPIURL, krakenAPIVersion, krakenServerTime) - err := k.SendHTTPRequest(path, &result) - if err != nil { - return nil, fmt.Errorf("getServerTime() error %s", err) + var response struct { + Error []string `json:"error"` + Result TimeResponse `json:"result"` } - if unixTime { - return result.Result["unixtime"], nil + if err := k.SendHTTPRequest(path, &response); err != nil { + return response.Result, err } - return result.Result["rfc1123"], nil + + return response.Result, GetError(response.Error) } // GetAssets returns a full asset list -func (k *Kraken) GetAssets() (interface{}, error) { - var result GeneralResponse +func (k *Kraken) GetAssets() (map[string]Asset, error) { path := fmt.Sprintf("%s/%s/public/%s", krakenAPIURL, krakenAPIVersion, krakenAssets) - return result.Result, k.SendHTTPRequest(path, &result) + var response struct { + Error []string `json:"error"` + Result map[string]Asset `json:"result"` + } + + if err := k.SendHTTPRequest(path, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) } // GetAssetPairs returns a full asset pair list func (k *Kraken) GetAssetPairs() (map[string]AssetPairs, error) { - type Response struct { - Result map[string]AssetPairs `json:"result"` - Error []interface{} `json:"error"` - } - - response := Response{} path := fmt.Sprintf("%s/%s/public/%s", krakenAPIURL, krakenAPIVersion, krakenAssetPairs) - return response.Result, k.SendHTTPRequest(path, &response) + var response struct { + Error []string `json:"error"` + Result map[string]AssetPairs `json:"result"` + } + + if err := k.SendHTTPRequest(path, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) } // GetTicker returns ticker information from kraken @@ -371,208 +382,422 @@ func (k *Kraken) GetSpread(symbol string) ([]Spread, error) { } // GetBalance returns your balance associated with your keys -func (k *Kraken) GetBalance() (interface{}, error) { - return k.SendAuthenticatedHTTPRequest(krakenBalance, url.Values{}) +func (k *Kraken) GetBalance() (map[string]float64, error) { + var response struct { + Error []string `json:"error"` + Result map[string]string `json:"result"` + } + + if err := k.SendAuthenticatedHTTPRequest(krakenBalance, url.Values{}, &response); err != nil { + return nil, err + } + + result := make(map[string]float64) + for curency, balance := range response.Result { + var err error + if result[curency], err = strconv.ParseFloat(balance, 64); err != nil { + return nil, err + } + } + + return result, GetError(response.Error) } // GetTradeBalance returns full information about your trades on Kraken -func (k *Kraken) GetTradeBalance(symbol, asset string) (interface{}, error) { - values := url.Values{} +func (k *Kraken) GetTradeBalance(args ...TradeBalanceOptions) (TradeBalanceInfo, error) { + params := url.Values{} + + if args != nil { + if len(args[0].Aclass) != 0 { + params.Set("aclass", args[0].Aclass) + } + + if len(args[0].Asset) != 0 { + params.Set("asset", args[0].Asset) + } - if len(symbol) > 0 { - values.Set("aclass", symbol) - } - if len(asset) > 0 { - values.Set("asset", asset) } - return k.SendAuthenticatedHTTPRequest(krakenTradeBalance, values) + var response struct { + Error []string `json:"error"` + Result TradeBalanceInfo `json:"result"` + } + + if err := k.SendAuthenticatedHTTPRequest(krakenTradeBalance, params, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) } // GetOpenOrders returns all current open orders -func (k *Kraken) GetOpenOrders(showTrades bool, userref int64) (interface{}, error) { - values := url.Values{} +func (k *Kraken) GetOpenOrders(args ...OrderInfoOptions) (OpenOrders, error) { + params := url.Values{} - if showTrades { - values.Set("trades", "true") + if args[0].Trades { + params.Set("trades", "true") } - if userref != 0 { - values.Set("userref", strconv.FormatInt(userref, 10)) + if args[0].UserRef != 0 { + params.Set("userref", strconv.FormatInt(int64(args[0].UserRef), 10)) } - return k.SendAuthenticatedHTTPRequest(krakenOpenOrders, values) + var response struct { + Error []string `json:"error"` + Result OpenOrders `json:"result"` + } + + if err := k.SendAuthenticatedHTTPRequest(krakenOpenOrders, params, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) } // GetClosedOrders returns a list of closed orders -func (k *Kraken) GetClosedOrders(showTrades bool, userref, start, end, offset int64, closetime string) (interface{}, error) { - values := url.Values{} +func (k *Kraken) GetClosedOrders(args ...GetClosedOrdersOptions) (ClosedOrders, error) { + params := url.Values{} - if showTrades { - values.Set("trades", "true") + if args != nil { + if args[0].Trades { + params.Set("trades", "true") + } + + if args[0].UserRef != 0 { + params.Set("userref", strconv.FormatInt(int64(args[0].UserRef), 10)) + } + + if len(args[0].Start) != 0 { + params.Set("start", args[0].Start) + } + + if len(args[0].End) != 0 { + params.Set("end", args[0].End) + } + + if args[0].Ofs != 0 { + params.Set("ofs", strconv.FormatInt(args[0].Ofs, 10)) + } + + if len(args[0].CloseTime) != 0 { + params.Set("closetime", args[0].CloseTime) + } } - if userref != 0 { - values.Set("userref", strconv.FormatInt(userref, 10)) + var response struct { + Error []string `json:"error"` + Result ClosedOrders `json:"result"` } - if start != 0 { - values.Set("start", strconv.FormatInt(start, 10)) + if err := k.SendAuthenticatedHTTPRequest(krakenClosedOrders, params, &response); err != nil { + return response.Result, err } - if end != 0 { - values.Set("end", strconv.FormatInt(end, 10)) - } - - if offset != 0 { - values.Set("ofs", strconv.FormatInt(offset, 10)) - } - - if len(closetime) > 0 { - values.Set("closetime", closetime) - } - - return k.SendAuthenticatedHTTPRequest(krakenClosedOrders, values) + return response.Result, GetError(response.Error) } // QueryOrdersInfo returns order information -func (k *Kraken) QueryOrdersInfo(showTrades bool, userref, txid int64) (interface{}, error) { - values := url.Values{} - - if showTrades { - values.Set("trades", "true") +func (k *Kraken) QueryOrdersInfo(args OrderInfoOptions, txid string, txids ...string) (map[string]OrderInfo, error) { + params := url.Values{ + "txid": {txid}, } - if userref != 0 { - values.Set("userref", strconv.FormatInt(userref, 10)) + if txids != nil { + params.Set("txid", txid+","+strings.Join(txids, ",")) } - if txid != 0 { - values.Set("txid", strconv.FormatInt(userref, 10)) + if args.Trades { + params.Set("trades", "true") } - return k.SendAuthenticatedHTTPRequest(krakenQueryOrders, values) + if args.UserRef != 0 { + params.Set("userref", strconv.FormatInt(int64(args.UserRef), 10)) + } + + var response struct { + Error []string `json:"error"` + Result map[string]OrderInfo `json:"result"` + } + + if err := k.SendAuthenticatedHTTPRequest(krakenQueryOrders, params, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) } // GetTradesHistory returns trade history information -func (k *Kraken) GetTradesHistory(tradeType string, showRelatedTrades bool, start, end, offset int64) (interface{}, error) { - values := url.Values{} +func (k *Kraken) GetTradesHistory(args ...GetTradesHistoryOptions) (TradesHistory, error) { + params := url.Values{} - if len(tradeType) > 0 { - values.Set("aclass", tradeType) + if args != nil { + if len(args[0].Type) != 0 { + params.Set("type", args[0].Type) + } + + if args[0].Trades { + params.Set("trades", "true") + } + + if len(args[0].Start) != 0 { + params.Set("start", args[0].Start) + } + + if len(args[0].End) != 0 { + params.Set("end", args[0].End) + } + + if args[0].Ofs != 0 { + params.Set("ofs", strconv.FormatInt(args[0].Ofs, 10)) + } } - if showRelatedTrades { - values.Set("trades", "true") + var response struct { + Error []string `json:"error"` + Result TradesHistory `json:"result"` } - if start != 0 { - values.Set("start", strconv.FormatInt(start, 10)) + if err := k.SendAuthenticatedHTTPRequest(krakenTradeHistory, params, &response); err != nil { + return response.Result, err } - if end != 0 { - values.Set("end", strconv.FormatInt(end, 10)) - } - - if offset != 0 { - values.Set("offset", strconv.FormatInt(offset, 10)) - } - - return k.SendAuthenticatedHTTPRequest(krakenTradeHistory, values) + return response.Result, GetError(response.Error) } // QueryTrades returns information on a specific trade -func (k *Kraken) QueryTrades(txid int64, showRelatedTrades bool) (interface{}, error) { - values := url.Values{} - values.Set("txid", strconv.FormatInt(txid, 10)) - - if showRelatedTrades { - values.Set("trades", "true") +func (k *Kraken) QueryTrades(trades bool, txid string, txids ...string) (map[string]TradeInfo, error) { + params := url.Values{ + "txid": {txid}, } - return k.SendAuthenticatedHTTPRequest(krakenQueryTrades, values) + if trades { + params.Set("trades", "true") + } + + if txids != nil { + params.Set("txid", txid+","+strings.Join(txids, ",")) + } + + var response struct { + Error []string `json:"error"` + Result map[string]TradeInfo `json:"result"` + } + + if err := k.SendAuthenticatedHTTPRequest(krakenQueryTrades, params, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) } // OpenPositions returns current open positions -func (k *Kraken) OpenPositions(txid int64, showPL bool) (interface{}, error) { - values := url.Values{} - values.Set("txid", strconv.FormatInt(txid, 10)) +func (k *Kraken) OpenPositions(docalcs bool, txids ...string) (map[string]Position, error) { + params := url.Values{} - if showPL { - values.Set("docalcs", "true") + if txids != nil { + params.Set("txid", strings.Join(txids, ",")) } - return k.SendAuthenticatedHTTPRequest(krakenOpenPositions, values) + if docalcs { + params.Set("docalcs", "true") + } + + var response struct { + Error []string `json:"error"` + Result map[string]Position `json:"result"` + } + + if err := k.SendAuthenticatedHTTPRequest(krakenOpenPositions, params, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) } // GetLedgers returns current ledgers -func (k *Kraken) GetLedgers(symbol, asset, ledgerType string, start, end, offset int64) (interface{}, error) { - values := url.Values{} +func (k *Kraken) GetLedgers(args ...GetLedgersOptions) (Ledgers, error) { + params := url.Values{} - if len(symbol) > 0 { - values.Set("aclass", symbol) + if args != nil { + if len(args[0].Aclass) != 0 { + params.Set("aclass", args[0].Aclass) + } + + if len(args[0].Asset) != 0 { + params.Set("asset", args[0].Asset) + } + + if len(args[0].Type) != 0 { + params.Set("type", args[0].Type) + } + + if len(args[0].Start) != 0 { + params.Set("start", args[0].Start) + } + + if len(args[0].End) != 0 { + params.Set("end", args[0].End) + } + + if args[0].Ofs != 0 { + params.Set("ofs", strconv.FormatInt(args[0].Ofs, 10)) + } } - if len(asset) > 0 { - values.Set("asset", asset) + var response struct { + Error []string `json:"error"` + Result Ledgers `json:"result"` } - if len(ledgerType) > 0 { - values.Set("type", ledgerType) + if err := k.SendAuthenticatedHTTPRequest(krakenLedgers, params, &response); err != nil { + return response.Result, err } - if start != 0 { - values.Set("start", strconv.FormatInt(start, 10)) - } - - if end != 0 { - values.Set("end", strconv.FormatInt(end, 10)) - } - - if offset != 0 { - values.Set("offset", strconv.FormatInt(offset, 10)) - } - - return k.SendAuthenticatedHTTPRequest(krakenLedgers, values) + return response.Result, GetError(response.Error) } // QueryLedgers queries an individual ledger by ID -func (k *Kraken) QueryLedgers(id string) (interface{}, error) { - values := url.Values{} - values.Set("id", id) +func (k *Kraken) QueryLedgers(id string, ids ...string) (map[string]LedgerInfo, error) { + params := url.Values{ + "id": {id}, + } - return k.SendAuthenticatedHTTPRequest(krakenQueryLedgers, values) + if ids != nil { + params.Set("id", id+","+strings.Join(ids, ",")) + } + + var response struct { + Error []string `json:"error"` + Result map[string]LedgerInfo `json:"result"` + } + + if err := k.SendAuthenticatedHTTPRequest(krakenQueryLedgers, params, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) } // GetTradeVolume returns your trade volume by currency -func (k *Kraken) GetTradeVolume(symbol string) (interface{}, error) { - values := url.Values{} - values.Set("pair", symbol) +func (k *Kraken) GetTradeVolume(feeinfo bool, symbol ...string) (TradeVolumeResponse, error) { + params := url.Values{} - return k.SendAuthenticatedHTTPRequest(krakenTradeVolume, values) + if symbol != nil { + params.Set("pair", strings.Join(symbol, ",")) + } + + if feeinfo { + params.Set("fee-info", "true") + } + + var response struct { + Error []string `json:"error"` + Result TradeVolumeResponse `json:"result"` + } + + if err := k.SendAuthenticatedHTTPRequest(krakenTradeVolume, params, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) } // AddOrder adds a new order for Kraken exchange -func (k *Kraken) AddOrder(symbol, side, orderType string, price, price2, volume, leverage, position float64) (interface{}, error) { - values := url.Values{} - values.Set("pairs", symbol) - values.Set("type", side) - values.Set("ordertype", orderType) - values.Set("price", strconv.FormatFloat(price, 'f', -1, 64)) - values.Set("price2", strconv.FormatFloat(price, 'f', -1, 64)) - values.Set("volume", strconv.FormatFloat(volume, 'f', -1, 64)) - values.Set("leverage", strconv.FormatFloat(leverage, 'f', -1, 64)) - values.Set("position", strconv.FormatFloat(position, 'f', -1, 64)) +func (k *Kraken) AddOrder(symbol, side, orderType string, volume, price, price2, leverage float64, args AddOrderOptions) (AddOrderResponse, error) { + params := url.Values{ + "pair": {symbol}, + "type": {side}, + "ordertype": {orderType}, + "volume": {strconv.FormatFloat(volume, 'f', -1, 64)}, + } - return k.SendAuthenticatedHTTPRequest(krakenOrderPlace, values) + if price != 0 { + params.Set("price", strconv.FormatFloat(price, 'f', -1, 64)) + } + + if price2 != 0 { + params.Set("price2", strconv.FormatFloat(price2, 'f', -1, 64)) + } + + if leverage != 0 { + params.Set("leverage", strconv.FormatFloat(leverage, 'f', -1, 64)) + } + + if len(args.Oflags) != 0 { + params.Set("oflags", args.Oflags) + } + + if len(args.StartTm) != 0 { + params.Set("starttm", args.StartTm) + } + + if len(args.ExpireTm) != 0 { + params.Set("expiretm", args.ExpireTm) + } + + if len(args.CloseOrderType) != 0 { + params.Set("close[ordertype]", args.ExpireTm) + } + + if args.ClosePrice != 0 { + params.Set("close[price]", strconv.FormatFloat(args.ClosePrice, 'f', -1, 64)) + } + + if args.ClosePrice2 != 0 { + params.Set("close[price2]", strconv.FormatFloat(args.ClosePrice2, 'f', -1, 64)) + } + + if args.Validate { + params.Set("validate", "true") + } + + var response struct { + Error []string `json:"error"` + Result AddOrderResponse `json:"result"` + } + + if err := k.SendAuthenticatedHTTPRequest(krakenOrderPlace, params, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) } // CancelOrder cancels order by orderID -func (k *Kraken) CancelOrder(orderID int64) (interface{}, error) { - values := url.Values{} - values.Set("txid", strconv.FormatInt(orderID, 10)) +func (k *Kraken) CancelOrder(txid string) (CancelOrderResponse, error) { + values := url.Values{ + "txid": {txid}, + } - return k.SendAuthenticatedHTTPRequest(krakenOrderCancel, values) + var response struct { + Error []string `json:"error"` + Result CancelOrderResponse `json:"result"` + } + + if err := k.SendAuthenticatedHTTPRequest(krakenOrderCancel, values, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) +} + +// GetError parse Exchange errors in response and return the first one +// Error format from API doc: +// error = array of error messages in the format of: +// :[:] +// severity code can be E for error or W for warning +func GetError(errors []string) error { + + for _, e := range errors { + switch e[0] { + case 'W': + log.Printf("Kraken API warning: %v\n", e[1:]) + default: + return fmt.Errorf("Kraken API error: %v", e[1:]) + } + } + + return nil } // SendHTTPRequest sends an unauthenticated HTTP requests @@ -581,9 +806,9 @@ func (k *Kraken) SendHTTPRequest(path string, result interface{}) error { } // SendAuthenticatedHTTPRequest sends an authenticated HTTP request -func (k *Kraken) SendAuthenticatedHTTPRequest(method string, values url.Values) (interface{}, error) { +func (k *Kraken) SendAuthenticatedHTTPRequest(method string, params url.Values, result interface{}) (err error) { if !k.AuthenticatedAPISupport { - return nil, fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, k.Name) + return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, k.Name) } path := fmt.Sprintf("/%s/private/%s", krakenAPIVersion, method) @@ -593,35 +818,24 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, values url.Values) k.Nonce.Inc() } - values.Set("nonce", k.Nonce.String()) + params.Set("nonce", k.Nonce.String()) secret, err := common.Base64Decode(k.APISecret) if err != nil { - return nil, err + return err } - shasum := common.GetSHA256([]byte(values.Get("nonce") + values.Encode())) + encoded := params.Encode() + shasum := common.GetSHA256([]byte(params.Get("nonce") + encoded)) signature := common.Base64Encode(common.GetHMAC(common.HashSHA512, append([]byte(path), shasum...), secret)) if k.Verbose { - log.Printf("Sending POST request to %s, path: %s.", krakenAPIURL, path) + log.Printf("Sending POST request to %s, path: %s, params: %s", krakenAPIURL, path, encoded) } headers := make(map[string]string) headers["API-Key"] = k.APIKey headers["API-Sign"] = signature - var resp interface{} - - err = k.SendPayload("POST", krakenAPIURL+path, headers, strings.NewReader(values.Encode()), &resp, true, k.Verbose) - if err != nil { - return resp, err - } - - data := resp.(map[string]interface{}) - if len(data["error"].([]interface{})) != 0 { - return nil, fmt.Errorf("kraken AuthenticattedHTTPRequest error: %s", data["error"]) - } - - return data["result"].(interface{}), nil + return k.SendPayload("POST", krakenAPIURL+path, headers, strings.NewReader(encoded), result, true, k.Verbose) } diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index 000757aa..eb3c1cae 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -47,11 +47,7 @@ func TestGetFee(t *testing.T) { func TestGetServerTime(t *testing.T) { t.Parallel() - _, err := k.GetServerTime(false) - if err != nil { - t.Error("Test Failed - GetServerTime() error", err) - } - _, err = k.GetServerTime(true) + _, err := k.GetServerTime() if err != nil { t.Error("Test Failed - GetServerTime() error", err) } @@ -123,7 +119,8 @@ func TestGetBalance(t *testing.T) { func TestGetTradeBalance(t *testing.T) { t.Parallel() - _, err := k.GetTradeBalance("", "") + args := TradeBalanceOptions{Asset: "ZEUR"} + _, err := k.GetTradeBalance(args) if err == nil { t.Error("Test Failed - GetTradeBalance() error", err) } @@ -131,7 +128,8 @@ func TestGetTradeBalance(t *testing.T) { func TestGetOpenOrders(t *testing.T) { t.Parallel() - _, err := k.GetOpenOrders(true, 0) + args := OrderInfoOptions{Trades: true} + _, err := k.GetOpenOrders(args) if err == nil { t.Error("Test Failed - GetOpenOrders() error", err) } @@ -139,7 +137,8 @@ func TestGetOpenOrders(t *testing.T) { func TestGetClosedOrders(t *testing.T) { t.Parallel() - _, err := k.GetClosedOrders(true, 0, 0, 0, 0, "") + args := GetClosedOrdersOptions{Trades: true, Start: "OE4KV4-4FVQ5-V7XGPU"} + _, err := k.GetClosedOrders(args) if err == nil { t.Error("Test Failed - GetClosedOrders() error", err) } @@ -147,7 +146,8 @@ func TestGetClosedOrders(t *testing.T) { func TestQueryOrdersInfo(t *testing.T) { t.Parallel() - _, err := k.QueryOrdersInfo(false, 0, 0) + args := OrderInfoOptions{Trades: true} + _, err := k.QueryOrdersInfo(args, "OR6ZFV-AA6TT-CKFFIW", "OAMUAJ-HLVKG-D3QJ5F") if err == nil { t.Error("Test Failed - QueryOrdersInfo() error", err) } @@ -155,7 +155,8 @@ func TestQueryOrdersInfo(t *testing.T) { func TestGetTradesHistory(t *testing.T) { t.Parallel() - _, err := k.GetTradesHistory("", false, 0, 0, 0) + args := GetTradesHistoryOptions{Trades: true, Start: "TMZEDR-VBJN2-NGY6DX", End: "TVRXG2-R62VE-RWP3UW"} + _, err := k.GetTradesHistory(args) if err == nil { t.Error("Test Failed - GetTradesHistory() error", err) } @@ -163,7 +164,7 @@ func TestGetTradesHistory(t *testing.T) { func TestQueryTrades(t *testing.T) { t.Parallel() - _, err := k.QueryTrades(0, false) + _, err := k.QueryTrades(true, "TMZEDR-VBJN2-NGY6DX", "TFLWIB-KTT7L-4TWR3L", "TDVRAH-2H6OS-SLSXRX") if err == nil { t.Error("Test Failed - QueryTrades() error", err) } @@ -171,7 +172,7 @@ func TestQueryTrades(t *testing.T) { func TestOpenPositions(t *testing.T) { t.Parallel() - _, err := k.OpenPositions(0, false) + _, err := k.OpenPositions(false) if err == nil { t.Error("Test Failed - OpenPositions() error", err) } @@ -179,7 +180,8 @@ func TestOpenPositions(t *testing.T) { func TestGetLedgers(t *testing.T) { t.Parallel() - _, err := k.GetLedgers("bla", "bla", "bla", 0, 0, 0) + args := GetLedgersOptions{Start: "LRUHXI-IWECY-K4JYGO", End: "L5NIY7-JZQJD-3J4M2V", Ofs: 15} + _, err := k.GetLedgers(args) if err == nil { t.Error("Test Failed - GetLedgers() error", err) } @@ -187,7 +189,7 @@ func TestGetLedgers(t *testing.T) { func TestQueryLedgers(t *testing.T) { t.Parallel() - _, err := k.QueryLedgers("1337") + _, err := k.QueryLedgers("LVTSFS-NHZVM-EXNZ5M") if err == nil { t.Error("Test Failed - QueryLedgers() error", err) } @@ -195,7 +197,7 @@ func TestQueryLedgers(t *testing.T) { func TestGetTradeVolume(t *testing.T) { t.Parallel() - _, err := k.GetTradeVolume("BCHEUR") + _, err := k.GetTradeVolume(true, "OAVY7T-MV5VK-KHDF5X") if err == nil { t.Error("Test Failed - GetTradeVolume() error", err) } @@ -203,7 +205,8 @@ func TestGetTradeVolume(t *testing.T) { func TestAddOrder(t *testing.T) { t.Parallel() - _, err := k.AddOrder("bla", "bla", "bla", 0, 0, 0, 0, 0) + args := AddOrderOptions{Oflags: "fcib"} + _, err := k.AddOrder("XXBTZUSD", "sell", "market", 0.00000001, 0, 0, 0, args) if err == nil { t.Error("Test Failed - AddOrder() error", err) } @@ -211,7 +214,7 @@ func TestAddOrder(t *testing.T) { func TestCancelOrder(t *testing.T) { t.Parallel() - _, err := k.CancelOrder(1337) + _, err := k.CancelOrder("OAVY7T-MV5VK-KHDF5X") if err == nil { t.Error("Test Failed - CancelOrder() error", err) } diff --git a/exchanges/kraken/kraken_types.go b/exchanges/kraken/kraken_types.go index 47c438f0..764aa28e 100644 --- a/exchanges/kraken/kraken_types.go +++ b/exchanges/kraken/kraken_types.go @@ -1,9 +1,17 @@ package kraken -// GeneralResponse is a generalized response type -type GeneralResponse struct { - Result map[string]interface{} `json:"result"` - Error []interface{} `json:"error"` +// TimeResponse type +type TimeResponse struct { + Unixtime int64 `json:"unixtime"` + Rfc1123 string `json:"rfc1123"` +} + +// Asset holds asset information +type Asset struct { + Altname string `json:"altname"` + AclassBase string `json:"aclass_base"` + Decimals int `json:"decimals"` + DisplayDecimals int `json:"display_decimals"` } // AssetPairs holds asset pair information @@ -92,3 +100,211 @@ type Spread struct { Bid float64 Ask float64 } + +// TradeBalanceOptions type +type TradeBalanceOptions struct { + Aclass string + Asset string +} + +// TradeBalanceInfo type +type TradeBalanceInfo struct { + EquivalentBalance float64 `json:"eb,string"` // combined balance of all currencies + TradeBalance float64 `json:"tb,string"` // combined balance of all equity currencies + MarginAmount float64 `json:"m,string"` // margin amount of open positions + Net float64 `json:"n,string"` // unrealized net profit/loss of open positions + Equity float64 `json:"e,string"` // trade balance + unrealized net profit/loss + FreeMargin float64 `json:"mf,string"` // equity - initial margin (maximum margin available to open new positions) + MarginLevel float64 `json:"ml,string"` // (equity / initial margin) * 100 +} + +// OrderInfo type +type OrderInfo struct { + RefID string `json:"refid"` + UserRef int32 `json:"userref"` + Status string `json:"status"` + OpenTm float64 `json:"opentm"` + StartTm float64 `json:"starttm"` + ExpireTm float64 `json:"expiretm"` + Descr struct { + Pair string `json:"pair"` + Type string `json:"type"` + OrderType string `json:"ordertype"` + Price float64 `json:"price,string"` + Price2 float64 `json:"price2,string"` + Leverage string `json:"leverage"` + Order string `json:"order"` + Close string `json:"close"` + } `json:"descr"` + Vol float64 `json:"vol,string"` + VolExec float64 `json:"vol_exec,string"` + Cost float64 `json:"cost,string"` + Fee float64 `json:"fee,string"` + Price float64 `json:"price,string"` + StopPrice float64 `json:"stopprice,string"` + LimitPrice float64 `json:"limitprice,string"` + Misc string `json:"misc"` + Oflags string `json:"oflags"` + Trades []string `json:"trades"` +} + +// OpenOrders type +type OpenOrders struct { + Open map[string]OrderInfo `json:"open"` + Count int64 `json:"count"` +} + +// ClosedOrders type +type ClosedOrders struct { + Closed map[string]OrderInfo `json:"closed"` + Count int64 `json:"count"` +} + +// GetClosedOrdersOptions type +type GetClosedOrdersOptions struct { + Trades bool + UserRef int32 + Start string + End string + Ofs int64 + CloseTime string +} + +// OrderInfoOptions type +type OrderInfoOptions struct { + Trades bool + UserRef int32 +} + +// GetTradesHistoryOptions type +type GetTradesHistoryOptions struct { + Type string + Trades bool + Start string + End string + Ofs int64 +} + +// TradesHistory type +type TradesHistory struct { + Trades map[string]TradeInfo `json:"trades"` + Count int64 `json:"count"` +} + +// TradeInfo type +type TradeInfo struct { + OrderTxID string `json:"ordertxid"` + Pair string `json:"pair"` + Time float64 `json:"time"` + Type string `json:"type"` + OrderType string `json:"ordertype"` + Price float64 `json:"price,string"` + Cost float64 `json:"cost,string"` + Fee float64 `json:"fee,string"` + Vol float64 `json:"vol,string"` + Margin float64 `json:"margin,string"` + Misc string `json:"misc"` + PosTxID string `json:"postxid"` + Cprice float64 `json:"cprice,string"` + Cfee float64 `json:"cfee,string"` + Cvol float64 `json:"cvol,string"` + Cmargin float64 `json:"cmargin,string"` + Trades []string `json:"trades"` + PosStatus string `json:"posstatus"` +} + +// Position holds the opened position +type Position struct { + Ordertxid string `json:"ordertxid"` + Pair string `json:"pair"` + Time float64 `json:"time"` + Type string `json:"type"` + OrderType string `json:"ordertype"` + Cost float64 `json:"cost,string"` + Fee float64 `json:"fee,string"` + Vol float64 `json:"vol,string"` + VolClosed float64 `json:"vol_closed,string"` + Margin float64 `json:"margin,string"` + Rollovertm int64 `json:"rollovertm,string"` + Misc string `json:"misc"` + Oflags string `json:"oflags"` + PosStatus string `json:"posstatus"` + Net string `json:"net"` + Terms string `json:"terms"` +} + +// GetLedgersOptions type +type GetLedgersOptions struct { + Aclass string + Asset string + Type string + Start string + End string + Ofs int64 +} + +// Ledgers type +type Ledgers struct { + Ledger map[string]LedgerInfo `json:"ledger"` + Count int64 `json:"count"` +} + +// LedgerInfo type +type LedgerInfo struct { + Refid string `json:"refid"` + Time float64 `json:"time"` + Type string `json:"type"` + Aclass string `json:"aclass"` + Asset string `json:"asset"` + Amount float64 `json:"amount,string"` + Fee float64 `json:"fee,string"` + Balance float64 `json:"balance,string"` +} + +// TradeVolumeResponse type +type TradeVolumeResponse struct { + Currency string `json:"currency"` + Volume float64 `json:"volume,string"` + Fees map[string]TradeVolumeFee `json:"fees"` + FeesMaker map[string]TradeVolumeFee `json:"fees_maker"` +} + +// TradeVolumeFee type +type TradeVolumeFee struct { + Fee float64 `json:"fee,string"` + MinFee float64 `json:"minfee,string"` + MaxFee float64 `json:"maxfee,string"` + NextFee float64 `json:"nextfee,string"` + NextVolume float64 `json:"nextvolume,string"` + TierVolume float64 `json:"tiervolume,string"` +} + +// AddOrderResponse type +type AddOrderResponse struct { + Description OrderDescription `json:"descr"` + TransactionIds []string `json:"txid"` +} + +// OrderDescription represents an orders description +type OrderDescription struct { + Close string `json:"close"` + Order string `json:"order"` +} + +// AddOrderOptions represents the AddOrder options +type AddOrderOptions struct { + UserRef int32 + Oflags string + StartTm string + ExpireTm string + CloseOrderType string + ClosePrice float64 + ClosePrice2 float64 + Validate bool +} + +// CancelOrderResponse type +type CancelOrderResponse struct { + Count int64 `json:"count"` + Pending interface{} `json:"pending"` +} diff --git a/exchanges/localbitcoins/localbitcoins.go b/exchanges/localbitcoins/localbitcoins.go index c1abca92..64b1fe08 100644 --- a/exchanges/localbitcoins/localbitcoins.go +++ b/exchanges/localbitcoins/localbitcoins.go @@ -341,37 +341,57 @@ func (l *LocalBitcoins) GetCurrencies() error { // view contacts where the token owner is either buying or selling, respectively. // E.g. /api/dashboard/buyer/. All contacts where the token owner is // participating are returned. -func (l *LocalBitcoins) GetDashboardInfo() (DashBoardInfo, error) { - resp := DashBoardInfo{} +func (l *LocalBitcoins) GetDashboardInfo() ([]DashBoardInfo, error) { + var resp struct { + Data struct { + ContactList []DashBoardInfo `json:"contact_list"` + ContactCount int `json:"contact_count"` + } + } - return resp, + return resp.Data.ContactList, l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIDashboard, nil, &resp) } // GetDashboardReleasedTrades returns a list of all released trades where the // token owner is either a buyer or seller. -func (l *LocalBitcoins) GetDashboardReleasedTrades() (DashBoardInfo, error) { - resp := DashBoardInfo{} +func (l *LocalBitcoins) GetDashboardReleasedTrades() ([]DashBoardInfo, error) { + var resp struct { + Data struct { + ContactList []DashBoardInfo `json:"contact_list"` + ContactCount int `json:"contact_count"` + } + } - return resp, + return resp.Data.ContactList, l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIDashboardReleased, nil, &resp) } // GetDashboardCancelledTrades returns a list of all canceled trades where the // token owner is either a buyer or seller. -func (l *LocalBitcoins) GetDashboardCancelledTrades() (DashBoardInfo, error) { - resp := DashBoardInfo{} +func (l *LocalBitcoins) GetDashboardCancelledTrades() ([]DashBoardInfo, error) { + var resp struct { + Data struct { + ContactList []DashBoardInfo `json:"contact_list"` + ContactCount int `json:"contact_count"` + } + } - return resp, + return resp.Data.ContactList, l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIDashboardCancelled, nil, &resp) } // GetDashboardClosedTrades returns a list of all closed trades where the token // owner is either a buyer or seller. -func (l *LocalBitcoins) GetDashboardClosedTrades() (DashBoardInfo, error) { - resp := DashBoardInfo{} +func (l *LocalBitcoins) GetDashboardClosedTrades() ([]DashBoardInfo, error) { + var resp struct { + Data struct { + ContactList []DashBoardInfo `json:"contact_list"` + ContactCount int `json:"contact_count"` + } + } - return resp, + return resp.Data.ContactList, l.SendAuthenticatedHTTPRequest("GET", localbitcoinsAPIDashboardClosed, nil, &resp) } diff --git a/exchanges/localbitcoins/localbitcoins_types.go b/exchanges/localbitcoins/localbitcoins_types.go index bd82ecbb..264b386f 100644 --- a/exchanges/localbitcoins/localbitcoins_types.go +++ b/exchanges/localbitcoins/localbitcoins_types.go @@ -189,7 +189,7 @@ type DashBoardInfo struct { Buyer struct { Username string `json:"username"` TradeCount string `json:"trade_count"` - FeedbackScore string `json:"feedback_score"` + FeedbackScore int `json:"feedback_score"` Name string `json:"name"` LastOnline string `json:"last_online"` RealName string `json:"real_name"` @@ -200,7 +200,7 @@ type DashBoardInfo struct { Seller struct { Username string `json:"username"` TradeCount string `json:"trade_count"` - FeedbackScore string `json:"feedback_score"` + FeedbackScore int `json:"feedback_score"` Name string `json:"name"` LastOnline string `json:"last_online"` } `json:"seller"` @@ -216,7 +216,7 @@ type DashBoardInfo struct { Advertiser struct { Username string `json:"username"` TradeCount string `json:"trade_count"` - FeedbackScore string `json:"feedback_score"` + FeedbackScore int `json:"feedback_score"` Name string `json:"name"` LastOnline string `json:"last_online"` } `json:"advertiser"`