Feature: Implement funding rates, futures and coin margin (exchange API coverage) (#530)

* ALMOST THERE

* more api wips

* more api thingz

* testing n more api wipz

* more apiz

* more wips

* what is goin on

* more wips

* whip n testing

* testing

* testing

no keys

* remove log

* kraken is broken

ugh

* still broken

* fixing auth funcs + usdtm api docs

* wip

* api stuffs

* whip

* more wips

* whip

* more wip

* api wip n testing

* wip

* wip

* unsaved

* wip n testing

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* whip

* wrapper authenticated functions

* adding asset type and fixing dependencies

* wip

* binance auth wrapper start

* wrapper functionality

* wip

* wip

* wip

* wrapper cancel functions

* order submission for wrappers

* wip

* more error fixing and nits

* websocket beginning n error fix

* wip

* WOW

* glorious n shazzy nits

* useless nits

* wip

* fixing things

* merge stuffs

* crapveyor

* crapveyor rebuild

* probably broke more things than he fixed

* rm lns n other thangs

* hope

* please

* stop it

* done

* ofcourse

* rm vb

* fix lbank

* appveyor please

* float lev

* DONT ASK RYAN FOR HELP EVER

* wip

* wip

* endpoint upgrades continued

* path upgrade

* NeeeNeeeNeeeNeeeNING

* fix stuffs

* fixing time issue

* fixing broken funcs

* glorious nits

* shaz changes

* fixing errors for fundmon

* more error fixing for fundmon

* test running past 30s

* basic changes

* THX AGAIN SHAZBERT

* path system upgrade

* config upgrade

* unsaved stuffs

* broken wip config upgrade

* path system upgrade contd.

* path system upgrade contd

* path upgrade ready for review

* testing verbose removed

* linter stuffs

* appveyor stuffs

* appveyor stuff

* fixed?

* bugfix

* wip

* broken stuff

* fix test

* wierd hack fix

* appveyor pls stop

* error found

* more useless nits

* bitmex err

* broken wip

* broken wip path upgrade change to uint32

* changed url lookups to uint

* WOW

* ready4review

* config fixed HOPEFULLY

* config fix and glorious changes

* efficient way of getting orders and open orders

* binance wrapper logic fixing

* testing, adding tests and fixing lot of errrrrs

* merge master

* appveyor stuffs

* appveyor stuffs

* fmt

* test

* octalLiteral issue fix?

* octalLiteral fix?

* rm vb

* prnt ln to restart

* adding testz

* test fixzzz

* READY FOR REVIEW

* Actually ready now

* FORMATTING

* addressing shazzy n glorious nits

* crapveyor

* rm vb

* small change

* fixing err

* shazbert nits

* review changes

* requested changes

* more requested changes

* noo

* last nit fixes

* restart appveyor

* improving test cov

* Update .golangci.yml

* shazbert changes

* moving pair formatting

* format pair update wip

* path upgrade complete

* error fix

* appveyor linters

* more linters

* remove testexch

* more formatting changes

* changes

* shazbert changes

* checking older requested changes to ensure completion

* wip

* fixing broken code

* error fix

* all fixed

* additional changes

* more changes

* remove commented code

* ftx margin api

* appveyor fixes

* more appveyor issues + test addition

* more appveyor issues + test addition

* remove unnecessary

* testing

* testing, fixing okex api, error fix

* git merge fix

* go sum

* glorious changes and error fix

* rm vb

* more glorious changes and go mod tidy

* fixed now

* okex testing upgrade

* old config migration and batch fetching fix

* added test

* glorious requested changes WIP

* tested and fixed

* go fmted

* go fmt and test fix

* additional funcs and tests for fundingRates

* OKEX tested and fixed

* appveyor fixes

* ineff assign

* 1 glorious change

* error fix

* typo

* shazbert changes

* glorious code changes and path fixing huobi WIP

* adding assetType to accountinfo functions

* fixing panic

* panic fix and updating account info wrappers WIP

* updateaccountinfo updated

* testing WIP binance USDT n Coin Margined and Kraken Futures

* auth functions tested and fixed

* added test

* config reverted

* shazbert and glorious changes

* shazbert and glorious changes

* latest changes and portfolio update

* go fmt change:

* remove commented codes

* improved error checking

* index out of range fix

* rm ln

* critical nit

* glorious latest changes

* appveyor changes

* shazbert change

* easier readability

* latest glorious changes

* shadow dec

* assetstore updated

* last change

* another last change

* merge changes

* go mod tidy

* thrasher requested changes wip

* improving struct layouts

* appveyor go fmt

* remove unnecessary code

* shazbert changes

* small change

* oopsie

* tidy

* configtest reverted

* error fix

* oopsie

* for what

* test patch fix

* insecurities

* fixing tests

* fix config
This commit is contained in:
Adam
2021-02-12 16:19:18 +11:00
committed by GitHub
parent e9bd2ad4d8
commit 504c2fad6d
169 changed files with 227754 additions and 31776 deletions

View File

@@ -0,0 +1,576 @@
package kraken
import (
"sync"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
var (
validOrderTypes = map[order.Type]string{
order.ImmediateOrCancel: "ioc",
order.Limit: "lmt",
order.Stop: "stp",
order.PostOnly: "post",
order.TakeProfit: "take_profit",
}
validSide = []string{"buy", "sell"}
validTriggerSignal = []string{"mark", "index", "last"}
validReduceOnly = []string{"true", "false"}
validBatchOrderType = []string{
"edit", "cancel", "send",
}
)
// WSFuturesTickerData stores ws ticker data for futures websocket
type WSFuturesTickerData struct {
Time int64 `json:"time"`
Feed string `json:"feed"`
ProductID string `json:"product_id"`
Bid float64 `json:"bid"`
Ask float64 `json:"ask"`
BidSize float64 `json:"bid_size"`
AskSize float64 `json:"ask_size"`
Volume float64 `json:"volume"`
DTM float64 `json:"dtm"`
Leverage string `json:"leverage"`
Index float64 `json:"index"`
Premium float64 `json:"premium"`
Last float64 `json:"last"`
Change float64 `json:"change"`
Suspended bool `json:"suspended"`
Tag string `json:"tag"`
Pair string `json:"pair"`
OpenInterest float64 `json:"openinterest"`
MarkPrice float64 `json:"markPrice"`
MaturityTime int64 `json:"maturityTime"`
FundingRate float64 `json:"funding_rate"`
FundingRatePrediction float64 `json:"funding_rate_prediction"`
RelativeFundingRate float64 `json:"relative_funding_rate"`
RelativeFundingRatePrediction float64 `json:"relative_funding_rate_prediction"`
NextFundingRateTime int64 `json:"next_funding_rate_time"`
}
// WsFuturesTradeData stores public trade data for futures websocket
type WsFuturesTradeData struct {
Feed string `json:"feed"`
ProductID string `json:"product_id"`
Trades []struct {
Feed string `json:"feed"`
ProductID string `json:"product_id"`
Side string `json:"side"`
ProductType string `json:"type"`
Seq int64 `json:"seq"`
Time int64 `json:"time"`
Quantity float64 `json:"qty"`
Price float64 `json:"price"`
} `json:"trades"`
}
// WsFuturesTickerLite stores ticker lite data for futures websocket
type WsFuturesTickerLite struct {
Feed string `json:"feed"`
ProductID string `json:"product_id"`
Bid float64 `json:"bid"`
Ask float64 `json:"ask"`
Change float64 `json:"change"`
Premium float64 `json:"premium"`
Volume float64 `json:"volume"`
Tag string `json:"tag"`
Pair string `json:"pair"`
DTM float64 `json:"dtm"`
}
// WsFuturesOB stores orderbook data for futures websocket
type WsFuturesOB struct {
Feed string `json:"feed"`
ProductID string `json:"product_id"`
Seq int64 `json:"seq"`
Bids []wsOBItem `json:"bids"`
Asks []wsOBItem `json:"asks"`
}
type wsOBItem struct {
Price float64 `json:"price"`
Quantity float64 `json:"qty"`
}
// WsVerboseOpenOrders stores verbose open orders data for futures websocket
type WsVerboseOpenOrders struct {
Feed string `json:"feed"`
Account string `json:"account"`
Orders []struct {
Instrument string `json:"instrument"`
Time int64 `json:"time"`
LastUpdateTime int64 `json:"last_update_time"`
Qty float64 `json:"qty"`
Filled float64 `json:"filled"`
LimitPrice float64 `json:"limit_price"`
StopPrice float64 `json:"stop_price"`
OrderType string `json:"type"`
OrderID string `json:"order_id"`
Direction int64 `json:"direction"`
ReduceOnly bool `json:"reduce_only"`
} `json:"orders"`
}
// WsOpenPositions stores open positions data for futures websocket
type WsOpenPositions struct {
Feed string `json:"feed"`
Account string `json:"account"`
Positions []struct {
Instrument string `json:"instrument"`
Balance float64 `json:"balance"`
EntryPrice float64 `json:"entry_price"`
MarkPrice float64 `json:"mark_price"`
IndexPrice float64 `json:"index_price"`
ProfitAndLoss float64 `json:"pnl"`
} `json:"positions"`
}
// WsFuturesAccountLog stores account log data for futures websocket
type WsFuturesAccountLog struct {
Feed string `json:"feed"`
Logs []struct {
ID int64 `json:"id"`
Date string `json:"date"`
Asset string `json:"asset"`
Info string `json:"info"`
BookingUID string `json:"booking_uid"`
MarginAccount string `json:"margin_account"`
OldBalance float64 `json:"old_balance"`
NewBalance float64 `json:"new_balance"`
OldAverageEntry float64 `json:"old_average_entry"`
NewAverageEntry float64 `json:"new_average_entry"`
TradePrice float64 `json:"trade_price"`
MarkPrice float64 `json:"mark_price"`
RealizedPNL float64 `json:"realized_pnl"`
Fee float64 `json:"fee"`
Execution string `json:"execution"`
Collateral string `json:"collateral"`
FundingRate float64 `json:"funding_rate"`
RealizedFunding float64 `json:"realized_funding"`
} `json:"logs"`
}
// WsFuturesFillsData stores fills data for futures websocket
type WsFuturesFillsData struct {
Feed string `json:"feed"`
Account string `json:"account"`
Fills []struct {
Instrument string `json:"instrument"`
Time int64 `json:"time"`
Price float64 `json:"price"`
Seq int64 `json:"seq"`
Buy bool `json:"buy"`
Quantity float64 `json:"qty"`
OrderID string `json:"order_id"`
ClientOrderID string `json:"cli_order_id"`
FillID string `json:"fill_id"`
FillType string `json:"fill_type"`
} `json:"fills"`
}
// WsFuturesOpenOrders stores open orders data for futures websocket
type WsFuturesOpenOrders struct {
Feed string `json:"feed"`
Account string `json:"account"`
Orders []struct {
Instrument string `json:"instrument"`
Time int64 `json:"time"`
LastUpdateTime int64 `json:"last_update_time"`
Qty float64 `json:"qty"`
Filled float64 `json:"filled"`
LimitPrice float64 `json:"limit_price"`
StopPrice float64 `json:"stop_price"`
OrderType string `json:"order_type"`
OrderID string `json:"order_id"`
Direction string `json:"direction"`
ReduceOnly bool `json:"reduce_only"`
} `json:"orders"`
}
// WsAccountBalancesAndMargin stores account balances and margin data for futures websocket
type WsAccountBalancesAndMargin struct {
Seq int64 `json:"seq"`
Feed string `json:"feed"`
Account string `json:"account"`
MarginAccounts []struct {
Name string `json:"name"`
PortfolioValue float64 `json:"pv"`
Balance float64 `json:"balance"`
Funding float64 `json:"funding"`
MaintenanceMargin float64 `json:"mm"`
ProfitAndLoss float64 `json:"pnl"`
InitialMargin float64 `json:"im"`
AM float64 `json:"am"`
} `json:"margin_accounts"`
}
// WsFuturesNotifications stores notifications data for futures websocket
type WsFuturesNotifications struct {
Feed string `json:"feed"`
Notifications []struct {
ID int64 `json:"id"`
NotificationType string `json:"notificationType"`
Priority string `json:"priority"`
Note string `json:"note"`
EffectiveTime int64 `json:"effective_time"`
}
}
type assetTranslatorStore struct {
l sync.RWMutex
Assets map[string]string
}
// FuturesOrderbookData stores orderbook data for futures
type FuturesOrderbookData struct {
ServerTime string `json:"serverTime"`
Orderbook struct {
Bids [][2]float64 `json:"bids"`
Asks [][2]float64 `json:"asks"`
} `json:"orderBook"`
}
// TimeResponse type
type TimeResponse struct {
Unixtime int64 `json:"unixtime"`
Rfc1123 string `json:"rfc1123"`
}
// FuturesInstrumentData stores info for futures market
type FuturesInstrumentData struct {
Instruments []struct {
Symbol string `json:"symbol"`
FutureType string `json:"type"`
Underlying string `json:"underlying"`
LastTradingTime string `json:"lastTradingTime"`
TickSize float64 `json:"tickSize"`
ContractSize float64 `json:"contractSize"`
Tradable bool `json:"tradeable"`
MarginLevels []struct {
Contracts float64 `json:"contracts"`
InitialMargin float64 `json:"initialMargin"`
MaintenanceMargin float64 `json:"maintenanceMargin"`
} `json:"marginLevels"`
} `json:"instruments"`
}
// FuturesTradeHistoryData stores trade history data for futures
type FuturesTradeHistoryData struct {
History []struct {
Time string `json:"time"`
TradeID int64 `json:"trade_id"`
Price float64 `json:"price"`
Size float64 `json:"size"`
Side string `json:"side"`
TradeType string `json:"type"`
} `json:"history"`
}
// FuturesTickerData stores info for futures ticker
type FuturesTickerData struct {
Tickers []struct {
Tag string `json:"tag"`
Pair string `json:"pair"`
Symbol string `json:"symbol"`
MarkPrice float64 `json:"markPrice"`
Bid float64 `json:"bid"`
BidSize float64 `json:"bidSize"`
Ask float64 `json:"ask"`
AskSize float64 `json:"askSize"`
Vol24h float64 `json:"vol24h"`
OpenInterest float64 `json:"openInterest"`
Open24H float64 `json:"open24h"`
Last float64 `json:"last"`
LastTime string `json:"lastTime"`
LastSize float64 `json:"lastSize"`
Suspended bool `json:"suspended"`
FundingRate float64 `json:"fundingRate"`
FundingRatePrediction float64 `json:"fundingRatePrediction"`
} `json:"tickers"`
ServerTime string `json:"serverTime"`
}
// FuturesEditedOrderData stores an edited order's data
type FuturesEditedOrderData struct {
ServerTime string `json:"serverTime"`
EditStatus struct {
Status string `json:"status"`
OrderID string `json:"orderId"`
ReceivedTime string `json:"receivedTime"`
OrderEvents []struct {
Old FuturesOrderData `json:"old"`
New FuturesOrderData `json:"new"`
} `json:"orderEvents"`
ReduceQuantity string `json:"reduceQuantity"`
DataType string `json:"type"`
} `json:"editStatus"`
}
// FuturesSendOrderData stores send order data
type FuturesSendOrderData struct {
SendStatus struct {
OrderID string `json:"orderId"`
Status string `json:"status"`
ReceivedTime string `json:"receivedTime"`
OrderEvents []struct {
UID string `json:"uid"`
Order FuturesOrderData `json:"order"`
Reason string `json:"reason"`
DataType string `json:"type"`
} `json:"orderEvents"`
} `json:"sendStatus"`
ServerTime string `json:"serverTime"`
}
// FuturesOrderData stores order data
type FuturesOrderData struct {
OrderID string `json:"orderId"`
ClientOrderID string `json:"cliOrderId"`
OrderType string `json:"type"`
Symbol string `json:"symbol"`
Side string `json:"side"`
Quantity float64 `json:"quantity"`
Filled float64 `json:"filled"`
LimitPrice float64 `json:"limitPrice"`
ReduceOnly bool `json:"reduceOnly"`
Timestamp string `json:"timestamp"`
LastUpdateTimestamp string `json:"lastUpdateTimestamp"`
}
// FuturesCancelOrderData stores cancel order data for futures
type FuturesCancelOrderData struct {
CancelStatus struct {
Status string `json:"status"`
OrderID string `json:"order_id"`
ReceivedTime string `json:"receivedTime"`
OrderEvents []struct {
UID string `json:"uid"`
Order FuturesOrderData `json:"order"`
DataType string `json:"type"`
} `json:"orderEvents"`
} `json:"cancelStatus"`
ServerTime string `json:"serverTime"`
}
// FuturesFillsData stores fills data
type FuturesFillsData struct {
Fills []struct {
FillID string `json:"fill_id"`
Symbol string `json:"symbol"`
Side string `json:"buy"`
OrderID string `json:"order_id"`
Size float64 `json:"size"`
Price float64 `json:"price"`
FillTime string `json:"fillTime"`
FillType string `json:"fillType"`
} `json:"fills"`
ServerTime string `json:"serverTime"`
}
// FuturesTransferData stores transfer data
type FuturesTransferData struct {
Result string `json:"result"`
ServerTime string `json:"serverTime"`
}
// FuturesOpenPositions stores open positions data for futures
type FuturesOpenPositions struct {
OpenPositions []struct {
Side string `json:"side"`
Symbol string `json:"symbol"`
Price float64 `json:"price"`
FillTime string `json:"fillTime"`
Size float64 `json:"size"`
UnrealizedFunding float64 `json:"unrealizedFunding"`
} `json:"openPositions"`
ServerTime string `json:"serverTime"`
}
// FuturesNotificationData stores notification data
type FuturesNotificationData struct {
Notifications []struct {
NotificationType string `json:"type"`
Priority string `json:"priority"`
Note string `json:"note"`
EffectiveTime string `json:"effectiveTime"`
} `json:"notifcations"`
ServerTime string `json:"serverTime"`
}
// FuturesAccountsData stores account data
type FuturesAccountsData struct {
ServerTime string `json:"serverTime"`
Accounts map[string]AccountsData `json:"accounts"`
}
// AccountsData stores data of an account
type AccountsData struct {
AccType string `json:"type"`
Currency string `json:"currency"`
Balances map[string]float64 `json:"balances"`
Auxiliary struct {
AvailableFunds float64 `json:"af"`
ProfitAndLoss float64 `json:"pnl"`
PortfolioValue float64 `json:"pv"`
} `json:"auxiliary"`
MarginRequirements struct {
InitialMargin float64 `json:"im"`
MaintenanceMargin float64 `json:"mm"`
LiquidationThreshold float64 `json:"lt"`
TerminationThreshold float64 `json:"tt"`
} `json:"marginRequirements"`
TriggerEstimates struct {
InitialMargin float64 `json:"im"`
MaintenanceMargin float64 `json:"mm"`
LiquidationThreshold float64 `json:"lt"`
TerminationThreshold float64 `json:"tt"`
} `json:"triggerEstimates"`
}
// CancelAllOrdersData stores order data for all cancelled orders
type CancelAllOrdersData struct {
CancelStatus struct {
ReceivedTime string `json:"receivedTime"`
CancelOnly string `json:"cancelOnly"`
Status string `json:"status"`
CancelledOrders []struct {
OrderID string `json:"order_id"`
} `json:"cancelledOrders"`
OrderEvents []struct {
UID string `json:"uid"`
} `json:"uid"`
Order FuturesOrderData `json:"order"`
DataType string `json:"type"`
} `json:"cancelStatus"`
ServerTime string `json:"serverTime"`
}
// CancelOrdersAfterData stores data of all orders after a certain time that are cancelled
type CancelOrdersAfterData struct {
Result string `json:"result"`
Status struct {
CurrentTime string `json:"currentTime"`
TriggerTime string `json:"triggerTime"`
} `json:"status"`
ServerTime string `json:"serverTime"`
}
// RecentOrderData stores order data of a recent order
type RecentOrderData struct {
UID string `json:"uid"`
AccountID string `json:"accountId"`
Tradeable string `json:"tradeable"`
Direction string `json:"direction"`
Quantity float64 `json:"quantity,string"`
Filled float64 `json:"filled,string"`
Timestamp string `json:"timestamp"`
LimitPrice float64 `json:"limitPrice,string"`
OrderType string `json:"orderType"`
ClientID string `json:"clientId"`
StopPrice float64 `json:"stopPrice,string"`
}
// FOpenOrdersData stores open orders data for futures
type FOpenOrdersData struct {
OrderID string `json:"order_id"`
ClientOrderID string `json:"cliOrdId"`
Symbol string `json:"symbol"`
Side string `json:"side"`
OrderType string `json:"orderType"`
LimitPrice float64 `json:"limitPrice"`
StopPrice float64 `json:"stopPrice"`
UnfilledSize float64 `json:"unfilledSize"`
ReceivedTime string `json:"receivedTime"`
Status string `json:"status"`
FilledSize float64 `json:"filledSize"`
ReduceOnly bool `json:"reduceOnly"`
TriggerSignal string `json:"triggerSignal"`
LastUpdateTime string `json:"lastUpdateTime"`
}
// FuturesRecentOrdersData stores recent orders data
type FuturesRecentOrdersData struct {
OrderEvents []struct {
Timestamp int64 `json:"timestamp"`
Event struct {
Timestamp string `json:"timestamp"`
UID string `json:"uid"`
OrderPlaced struct {
RecentOrder RecentOrderData `json:"order"`
Reason string `json:"reason"`
} `json:"orderPlaced"`
OrderCancelled struct {
RecentOrder RecentOrderData `json:"order"`
Reason string `json:"reason"`
} `json:"orderCancelled"`
OrderRejected struct {
RecentOrder RecentOrderData `json:"order"`
Reason string `json:"reason"`
} `json:"orderRejected"`
ExecutionEvent struct {
Execution ExecutionData `json:"execution"`
Timestamp string `json:"timestamp"`
Quantity float64 `json:"quantity"`
Price float64 `json:"price"`
MarkPrice float64 `json:"markPrice"`
LimitFilled bool `json:"limitFilled"`
} `json:"executionEvent"`
} `json:"event"`
} `json:"orderEvents"`
}
// BatchOrderData stores batch order data
type BatchOrderData struct {
Result bool `json:"result"`
ServerTime string `json:"serverTime"`
BatchStatus []struct {
Status string `json:"status"`
OrderTag string `json:"order_tag"`
OrderID string `json:"order_id"`
DateTimeReceived string `json:"dateTimeReceieved"`
OrderEvents []struct {
OrderPlaced FuturesOrderData `json:"orderPlaced"`
ReduceOnly bool `json:"reduceOnly"`
Timestamp string `json:"timestamp"`
OldEditedOrder FuturesOrderData `json:"old"`
NewEditedOrder FuturesOrderData `json:"new"`
UID string `json:"uid"`
RequestType string `json:"requestType"`
} `json:"orderEvents"`
} `json:"batchStatus"`
}
// PlaceBatchOrderData stores data required to place a batch order
type PlaceBatchOrderData struct {
PlaceOrderType string `json:"order,omitempty"`
OrderType string `json:"orderType,omitempty"`
OrderTag string `json:"order_tag,omitempty"`
Symbol string `json:"symbol,omitempty"`
Side string `json:"side,omitempty"`
Size float64 `json:"size,omitempty"`
LimitPrice float64 `json:"limitPrice,omitempty"`
StopPrice float64 `json:"stopPrice,omitempty"`
ClientOrderID int64 `json:"cliOrdId,omitempty"`
ReduceOnly string `json:"reduceOnly,omitempty"`
OrderID string `json:"order_id,omitempty"`
}
// ExecutionData stores execution data
type ExecutionData struct {
UID string `json:"uid"`
TakerOrder RecentOrderData `json:"takerOrder"`
}
// FuturesOpenOrdersData stores open orders data for futures
type FuturesOpenOrdersData struct {
Result string `json:"result"`
OpenOrders []FOpenOrdersData `json:"openOrders"`
ServerTime string `json:"serverTime"`
}

View File

@@ -2,6 +2,7 @@ package kraken
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
@@ -15,53 +16,18 @@ import (
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/log"
)
const (
krakenAPIURL = "https://api.kraken.com"
krakenAPIVersion = "0"
krakenServerTime = "Time"
krakenAssets = "Assets"
krakenAssetPairs = "AssetPairs"
krakenTicker = "Ticker"
krakenOHLC = "OHLC"
krakenDepth = "Depth"
krakenTrades = "Trades"
krakenSpread = "Spread"
krakenBalance = "Balance"
krakenTradeBalance = "TradeBalance"
krakenOpenOrders = "OpenOrders"
krakenClosedOrders = "ClosedOrders"
krakenQueryOrders = "QueryOrders"
krakenTradeHistory = "TradesHistory"
krakenQueryTrades = "QueryTrades"
krakenOpenPositions = "OpenPositions"
krakenLedgers = "Ledgers"
krakenQueryLedgers = "QueryLedgers"
krakenTradeVolume = "TradeVolume"
krakenOrderCancel = "CancelOrder"
krakenOrderPlace = "AddOrder"
krakenWithdrawInfo = "WithdrawInfo"
krakenWithdraw = "Withdraw"
krakenDepositMethods = "DepositMethods"
krakenDepositAddresses = "DepositAddresses"
krakenWithdrawStatus = "WithdrawStatus"
krakenWithdrawCancel = "WithdrawCancel"
krakenWebsocketToken = "GetWebSocketsToken"
// Rate limit consts
krakenRateInterval = time.Second
krakenRequestRate = 1
// Status consts
statusOpen = "open"
)
var (
assetTranslator assetTranslatorStore
krakenAPIURL = "https://api.kraken.com"
krakenFuturesURL = "https://futures.kraken.com"
futuresURL = "https://futures.kraken.com/derivatives"
krakenSpotVersion = "0"
krakenFuturesVersion = "3"
)
// Kraken is the overarching type across the alphapoint package
@@ -72,14 +38,14 @@ type Kraken struct {
// GetServerTime returns current server time
func (k *Kraken) GetServerTime() (TimeResponse, error) {
path := fmt.Sprintf("%s/%s/public/%s", k.API.Endpoints.URL, krakenAPIVersion, krakenServerTime)
path := fmt.Sprintf("/%s/public/%s", krakenAPIVersion, krakenServerTime)
var response struct {
Error []string `json:"error"`
Result TimeResponse `json:"result"`
}
if err := k.SendHTTPRequest(path, &response); err != nil {
if err := k.SendHTTPRequest(exchange.RestSpot, path, &response); err != nil {
return response.Result, err
}
@@ -93,55 +59,70 @@ func (k *Kraken) SeedAssets() error {
if err != nil {
return err
}
for k, v := range assets {
assetTranslator.Seed(k, v.Altname)
for orig, val := range assets {
assetTranslator.Seed(orig, val.Altname)
}
assetPairs, err := k.GetAssetPairs()
assetPairs, err := k.GetAssetPairs([]string{}, "")
if err != nil {
return err
}
for k, v := range assetPairs {
assetTranslator.Seed(k, v.Altname)
for k := range assetPairs {
assetTranslator.Seed(k, assetPairs[k].Altname)
}
return nil
}
// GetAssets returns a full asset list
func (k *Kraken) GetAssets() (map[string]*Asset, error) {
path := fmt.Sprintf("%s/%s/public/%s", k.API.Endpoints.URL, krakenAPIVersion, krakenAssets)
path := fmt.Sprintf("/%s/public/%s", krakenAPIVersion, krakenAssets)
var response struct {
Error []string `json:"error"`
Result map[string]*Asset `json:"result"`
}
if err := k.SendHTTPRequest(path, &response); err != nil {
if err := k.SendHTTPRequest(exchange.RestSpot, 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) {
path := fmt.Sprintf("%s/%s/public/%s", k.API.Endpoints.URL, krakenAPIVersion, krakenAssetPairs)
var response struct {
Error []string `json:"error"`
Result map[string]*AssetPairs `json:"result"`
// Parameter 'info' only supports 4 strings: "fees", "leverage", "margin", "info" <- (default)
func (k *Kraken) GetAssetPairs(assetPairs []string, info string) (map[string]AssetPairs, error) {
path := fmt.Sprintf("/%s/public/%s", krakenAPIVersion, krakenAssetPairs)
params := url.Values{}
var assets string
if len(assetPairs) != 0 {
assets = strings.Join(assetPairs, ",")
params.Set("pair", assets)
}
if err := k.SendHTTPRequest(path, &response); err != nil {
var response struct {
Error []string `json:"error"`
Result map[string]AssetPairs `json:"result"`
}
if info != "" {
if info != "margin" && info != "leverage" && info != "fees" && info != "info" {
return response.Result, errors.New("parameter info can only be 'asset', 'margin', 'fees' or 'leverage'")
}
params.Set("info", info)
}
if err := k.SendHTTPRequest(exchange.RestSpot, path+params.Encode(), &response); err != nil {
return response.Result, err
}
return response.Result, GetError(response.Error)
}
// GetTicker returns ticker information from kraken
func (k *Kraken) GetTicker(symbol string) (Ticker, error) {
func (k *Kraken) GetTicker(symbol currency.Pair) (Ticker, error) {
tick := Ticker{}
values := url.Values{}
values.Set("pair", symbol)
symbolValue, err := k.FormatSymbol(symbol, asset.Spot)
if err != nil {
return tick, err
}
values.Set("pair", symbolValue)
type Response struct {
Error []interface{} `json:"error"`
@@ -149,9 +130,9 @@ func (k *Kraken) GetTicker(symbol string) (Ticker, error) {
}
resp := Response{}
path := fmt.Sprintf("%s/%s/public/%s?%s", k.API.Endpoints.URL, krakenAPIVersion, krakenTicker, values.Encode())
path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenTicker, values.Encode())
err := k.SendHTTPRequest(path, &resp)
err = k.SendHTTPRequest(exchange.RestSpot, path, &resp)
if err != nil {
return tick, err
}
@@ -187,9 +168,9 @@ func (k *Kraken) GetTickers(pairList string) (map[string]Ticker, error) {
}
resp := Response{}
path := fmt.Sprintf("%s/%s/public/%s?%s", k.API.Endpoints.URL, krakenAPIVersion, krakenTicker, values.Encode())
path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenTicker, values.Encode())
err := k.SendHTTPRequest(path, &resp)
err := k.SendHTTPRequest(exchange.RestSpot, path, &resp)
if err != nil {
return nil, err
}
@@ -217,9 +198,17 @@ func (k *Kraken) GetTickers(pairList string) (map[string]Ticker, error) {
}
// GetOHLC returns an array of open high low close values of a currency pair
func (k *Kraken) GetOHLC(symbol, interval string) ([]OpenHighLowClose, error) {
func (k *Kraken) GetOHLC(symbol currency.Pair, interval string) ([]OpenHighLowClose, error) {
values := url.Values{}
values.Set("pair", symbol)
symbolValue, err := k.FormatSymbol(symbol, asset.Spot)
if err != nil {
return nil, err
}
translatedAsset := assetTranslator.LookupCurrency(symbolValue)
if translatedAsset == "" {
translatedAsset = symbolValue
}
values.Set("pair", translatedAsset)
values.Set("interval", interval)
type Response struct {
Error []interface{} `json:"error"`
@@ -229,9 +218,9 @@ func (k *Kraken) GetOHLC(symbol, interval string) ([]OpenHighLowClose, error) {
var OHLC []OpenHighLowClose
var result Response
path := fmt.Sprintf("%s/%s/public/%s?%s", k.API.Endpoints.URL, krakenAPIVersion, krakenOHLC, values.Encode())
path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenOHLC, values.Encode())
err := k.SendHTTPRequest(path, &result)
err = k.SendHTTPRequest(exchange.RestSpot, path, &result)
if err != nil {
return OHLC, err
}
@@ -240,12 +229,12 @@ func (k *Kraken) GetOHLC(symbol, interval string) ([]OpenHighLowClose, error) {
return OHLC, fmt.Errorf("getOHLC error: %s", result.Error)
}
_, ok := result.Data[symbol].([]interface{})
_, ok := result.Data[translatedAsset].([]interface{})
if !ok {
return nil, errors.New("invalid data returned")
}
for _, y := range result.Data[symbol].([]interface{}) {
for _, y := range result.Data[translatedAsset].([]interface{}) {
o := OpenHighLowClose{}
for i, x := range y.([]interface{}) {
switch i {
@@ -273,15 +262,17 @@ func (k *Kraken) GetOHLC(symbol, interval string) ([]OpenHighLowClose, error) {
}
// GetDepth returns the orderbook for a particular currency
func (k *Kraken) GetDepth(symbol string) (Orderbook, error) {
values := url.Values{}
values.Set("pair", symbol)
func (k *Kraken) GetDepth(symbol currency.Pair) (Orderbook, error) {
var result interface{}
var orderBook Orderbook
path := fmt.Sprintf("%s/%s/public/%s?%s", k.API.Endpoints.URL, krakenAPIVersion, krakenDepth, values.Encode())
err := k.SendHTTPRequest(path, &result)
values := url.Values{}
symbolValue, err := k.FormatSymbol(symbol, asset.Spot)
if err != nil {
return orderBook, err
}
values.Set("pair", symbolValue)
path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenDepth, values.Encode())
err = k.SendHTTPRequest(exchange.RestSpot, path, &result)
if err != nil {
return orderBook, err
}
@@ -334,16 +325,21 @@ func (k *Kraken) GetDepth(symbol string) (Orderbook, error) {
}
// GetTrades returns current trades on Kraken
func (k *Kraken) GetTrades(symbol string) ([]RecentTrades, error) {
func (k *Kraken) GetTrades(symbol currency.Pair) ([]RecentTrades, error) {
values := url.Values{}
values.Set("pair", symbol)
symbolValue, err := k.FormatSymbol(symbol, asset.Spot)
if err != nil {
return nil, err
}
translatedAsset := assetTranslator.LookupCurrency(symbolValue)
values.Set("pair", translatedAsset)
var recentTrades []RecentTrades
var result interface{}
path := fmt.Sprintf("%s/%s/public/%s?%s", k.API.Endpoints.URL, krakenAPIVersion, krakenTrades, values.Encode())
path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenTrades, values.Encode())
err := k.SendHTTPRequest(path, &result)
err = k.SendHTTPRequest(exchange.RestSpot, path, &result)
if err != nil {
return nil, err
}
@@ -390,7 +386,7 @@ func (k *Kraken) GetTrades(symbol string) ([]RecentTrades, error) {
var trades []interface{}
var tradesForSymbol interface{}
tradesForSymbol, ok = tradeInfo[symbol]
tradesForSymbol, ok = tradeInfo[translatedAsset]
if !ok {
return nil, fmt.Errorf("no data returned for symbol %v", symbol)
}
@@ -440,16 +436,20 @@ func (k *Kraken) GetTrades(symbol string) ([]RecentTrades, error) {
}
// GetSpread returns the full spread on Kraken
func (k *Kraken) GetSpread(symbol string) ([]Spread, error) {
func (k *Kraken) GetSpread(symbol currency.Pair) ([]Spread, error) {
values := url.Values{}
values.Set("pair", symbol)
symbolValue, err := k.FormatSymbol(symbol, asset.Spot)
if err != nil {
return nil, err
}
values.Set("pair", symbolValue)
var peanutButter []Spread
var response interface{}
path := fmt.Sprintf("%s/%s/public/%s?%s", k.API.Endpoints.URL, krakenAPIVersion, krakenSpread, values.Encode())
path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenSpread, values.Encode())
err := k.SendHTTPRequest(path, &response)
err = k.SendHTTPRequest(exchange.RestSpot, path, &response)
if err != nil {
return peanutButter, err
}
@@ -457,7 +457,7 @@ func (k *Kraken) GetSpread(symbol string) ([]Spread, error) {
data := response.(map[string]interface{})
result := data["result"].(map[string]interface{})
for _, x := range result[symbol].([]interface{}) {
for _, x := range result[symbolValue].([]interface{}) {
s := Spread{}
for i, y := range x.([]interface{}) {
switch i {
@@ -481,7 +481,7 @@ func (k *Kraken) GetBalance() (map[string]float64, error) {
Result map[string]string `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenBalance, url.Values{}, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenBalance, url.Values{}, &response); err != nil {
return nil, err
}
@@ -507,7 +507,7 @@ func (k *Kraken) GetWithdrawInfo(currency string, amount float64) (WithdrawInfor
params.Set("key", "")
params.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
if err := k.SendAuthenticatedHTTPRequest(krakenWithdrawInfo, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenWithdrawInfo, params, &response); err != nil {
return response.Result, err
}
@@ -525,7 +525,7 @@ func (k *Kraken) Withdraw(asset, key string, amount float64) (string, error) {
params.Set("key", key)
params.Set("amount", fmt.Sprintf("%f", amount))
if err := k.SendAuthenticatedHTTPRequest(krakenWithdraw, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenWithdraw, params, &response); err != nil {
return response.ReferenceID, err
}
@@ -541,7 +541,7 @@ func (k *Kraken) GetDepositMethods(currency string) ([]DepositMethods, error) {
params := url.Values{}
params.Set("asset", currency)
err := k.SendAuthenticatedHTTPRequest(krakenDepositMethods, params, &response)
err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenDepositMethods, params, &response)
if err != nil {
return response.Result, err
}
@@ -568,7 +568,7 @@ func (k *Kraken) GetTradeBalance(args ...TradeBalanceOptions) (TradeBalanceInfo,
Result TradeBalanceInfo `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenTradeBalance, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenTradeBalance, params, &response); err != nil {
return response.Result, err
}
@@ -592,7 +592,7 @@ func (k *Kraken) GetOpenOrders(args OrderInfoOptions) (OpenOrders, error) {
Result OpenOrders `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenOpenOrders, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenOpenOrders, params, &response); err != nil {
return response.Result, err
}
@@ -632,7 +632,7 @@ func (k *Kraken) GetClosedOrders(args GetClosedOrdersOptions) (ClosedOrders, err
Result ClosedOrders `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenClosedOrders, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenClosedOrders, params, &response); err != nil {
return response.Result, err
}
@@ -662,7 +662,7 @@ func (k *Kraken) QueryOrdersInfo(args OrderInfoOptions, txid string, txids ...st
Result map[string]OrderInfo `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenQueryOrders, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenQueryOrders, params, &response); err != nil {
return response.Result, err
}
@@ -700,7 +700,7 @@ func (k *Kraken) GetTradesHistory(args ...GetTradesHistoryOptions) (TradesHistor
Result TradesHistory `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenTradeHistory, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenTradeHistory, params, &response); err != nil {
return response.Result, err
}
@@ -726,7 +726,7 @@ func (k *Kraken) QueryTrades(trades bool, txid string, txids ...string) (map[str
Result map[string]TradeInfo `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenQueryTrades, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenQueryTrades, params, &response); err != nil {
return response.Result, err
}
@@ -750,7 +750,7 @@ func (k *Kraken) OpenPositions(docalcs bool, txids ...string) (map[string]Positi
Result map[string]Position `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenOpenPositions, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenOpenPositions, params, &response); err != nil {
return response.Result, err
}
@@ -792,7 +792,7 @@ func (k *Kraken) GetLedgers(args ...GetLedgersOptions) (Ledgers, error) {
Result Ledgers `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenLedgers, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenLedgers, params, &response); err != nil {
return response.Result, err
}
@@ -814,7 +814,7 @@ func (k *Kraken) QueryLedgers(id string, ids ...string) (map[string]LedgerInfo,
Result map[string]LedgerInfo `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenQueryLedgers, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenQueryLedgers, params, &response); err != nil {
return response.Result, err
}
@@ -822,23 +822,29 @@ func (k *Kraken) QueryLedgers(id string, ids ...string) (map[string]LedgerInfo,
}
// GetTradeVolume returns your trade volume by currency
func (k *Kraken) GetTradeVolume(feeinfo bool, symbol ...string) (TradeVolumeResponse, error) {
func (k *Kraken) GetTradeVolume(feeinfo bool, symbol ...currency.Pair) (TradeVolumeResponse, error) {
var response struct {
Error []string `json:"error"`
Result TradeVolumeResponse `json:"result"`
}
params := url.Values{}
var formattedPairs []string
for x := range symbol {
symbolValue, err := k.FormatSymbol(symbol[x], asset.Spot)
if err != nil {
return response.Result, err
}
formattedPairs = append(formattedPairs, symbolValue)
}
if symbol != nil {
params.Set("pair", strings.Join(symbol, ","))
params.Set("pair", strings.Join(formattedPairs, ","))
}
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 {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenTradeVolume, params, &response); err != nil {
return response.Result, err
}
@@ -846,9 +852,17 @@ func (k *Kraken) GetTradeVolume(feeinfo bool, symbol ...string) (TradeVolumeResp
}
// AddOrder adds a new order for Kraken exchange
func (k *Kraken) AddOrder(symbol, side, orderType string, volume, price, price2, leverage float64, args *AddOrderOptions) (AddOrderResponse, error) {
func (k *Kraken) AddOrder(symbol currency.Pair, side, orderType string, volume, price, price2, leverage float64, args *AddOrderOptions) (AddOrderResponse, error) {
var response struct {
Error []string `json:"error"`
Result AddOrderResponse `json:"result"`
}
symbolValue, err := k.FormatSymbol(symbol, asset.Spot)
if err != nil {
return response.Result, err
}
params := url.Values{
"pair": {symbol},
"pair": {symbolValue},
"type": {strings.ToLower(side)},
"ordertype": {strings.ToLower(orderType)},
"volume": {strconv.FormatFloat(volume, 'f', -1, 64)},
@@ -894,12 +908,7 @@ func (k *Kraken) AddOrder(symbol, side, orderType string, volume, price, price2,
params.Set("validate", "true")
}
var response struct {
Error []string `json:"error"`
Result AddOrderResponse `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenOrderPlace, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenOrderPlace, params, &response); err != nil {
return response.Result, err
}
@@ -917,7 +926,7 @@ func (k *Kraken) CancelExistingOrder(txid string) (CancelOrderResponse, error) {
Result CancelOrderResponse `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenOrderCancel, values, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenOrderCancel, values, &response); err != nil {
return response.Result, err
}
@@ -944,10 +953,14 @@ func GetError(apiErrors []string) error {
}
// SendHTTPRequest sends an unauthenticated HTTP requests
func (k *Kraken) SendHTTPRequest(path string, result interface{}) error {
func (k *Kraken) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := k.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return k.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: k.Verbose,
HTTPDebugging: k.HTTPDebugging,
@@ -956,12 +969,15 @@ func (k *Kraken) SendHTTPRequest(path string, result interface{}) error {
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
func (k *Kraken) SendAuthenticatedHTTPRequest(method string, params url.Values, result interface{}) (err error) {
func (k *Kraken) SendAuthenticatedHTTPRequest(ep exchange.URL, method string, params url.Values, result interface{}) error {
if !k.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
k.Name)
}
endpoint, err := k.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
path := fmt.Sprintf("/%s/private/%s", krakenAPIVersion, method)
params.Set("nonce", k.Requester.GetNonce(true).String())
@@ -972,7 +988,7 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, params url.Values,
if k.Verbose {
log.Debugf(log.ExchangeSys, "Sending POST request to %s, path: %s, params: %s",
k.API.Endpoints.URL,
endpoint,
path,
encoded)
}
@@ -981,40 +997,49 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, params url.Values,
headers["API-Key"] = k.API.Credentials.Key
headers["API-Sign"] = signature
return k.SendPayload(context.Background(), &request.Item{
interim := json.RawMessage{}
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute))
defer cancel()
err = k.SendPayload(ctx, &request.Item{
Method: http.MethodPost,
Path: k.API.Endpoints.URL + path,
Path: endpoint + path,
Headers: headers,
Body: strings.NewReader(encoded),
Result: result,
Result: &interim,
AuthRequest: true,
NonceEnabled: true,
Verbose: k.Verbose,
HTTPDebugging: k.HTTPDebugging,
HTTPRecording: k.HTTPRecording,
})
if err != nil {
return err
}
var errCap SpotAuthError
if err := json.Unmarshal(interim, &errCap); err == nil {
if len(errCap.Error) != 0 {
return errors.New(errCap.Error[0])
}
}
return json.Unmarshal(interim, result)
}
// GetFee returns an estimate of fee based on type of transaction
func (k *Kraken) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
var fee float64
c := feeBuilder.Pair.Base.String() +
feeBuilder.Pair.Delimiter +
feeBuilder.Pair.Quote.String()
switch feeBuilder.FeeType {
case exchange.CryptocurrencyTradeFee:
feePair, err := k.GetTradeVolume(true, c)
feePair, err := k.GetTradeVolume(true, feeBuilder.Pair)
if err != nil {
return 0, err
}
if feeBuilder.IsMaker {
fee = calculateTradingFee(c,
fee = calculateTradingFee(feePair.Currency,
feePair.FeesMaker,
feeBuilder.PurchasePrice,
feeBuilder.Amount)
} else {
fee = calculateTradingFee(c,
fee = calculateTradingFee(feePair.Currency,
feePair.Fees,
feeBuilder.PurchasePrice,
feeBuilder.Amount)
@@ -1078,7 +1103,7 @@ func (k *Kraken) GetCryptoDepositAddress(method, code string) (string, error) {
values.Set("asset", code)
values.Set("method", method)
err := k.SendAuthenticatedHTTPRequest(krakenDepositAddresses, values, &resp)
err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenDepositAddresses, values, &resp)
if err != nil {
return "", err
}
@@ -1103,7 +1128,7 @@ func (k *Kraken) WithdrawStatus(c currency.Code, method string) ([]WithdrawStatu
params.Set("method", method)
}
if err := k.SendAuthenticatedHTTPRequest(krakenWithdrawStatus, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenWithdrawStatus, params, &response); err != nil {
return response.Result, err
}
@@ -1121,7 +1146,7 @@ func (k *Kraken) WithdrawCancel(c currency.Code, refID string) (bool, error) {
params.Set("asset", c.String())
params.Set("refid", refID)
if err := k.SendAuthenticatedHTTPRequest(krakenWithdrawCancel, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenWithdrawCancel, params, &response); err != nil {
return response.Result, err
}
@@ -1131,7 +1156,7 @@ func (k *Kraken) WithdrawCancel(c currency.Code, refID string) (bool, error) {
// GetWebsocketToken returns a websocket token
func (k *Kraken) GetWebsocketToken() (string, error) {
var response WsTokenResponse
if err := k.SendAuthenticatedHTTPRequest(krakenWebsocketToken, url.Values{}, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenWebsocketToken, url.Values{}, &response); err != nil {
return "", err
}
if len(response.Error) > 0 {

View File

@@ -0,0 +1,307 @@
package kraken
import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
)
// GetFuturesOrderbook gets orderbook data for futures
func (k *Kraken) GetFuturesOrderbook(symbol currency.Pair) (FuturesOrderbookData, error) {
var resp FuturesOrderbookData
params := url.Values{}
symbolValue, err := k.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
return resp, k.SendHTTPRequest(exchange.RestFutures, futuresOrderbook+"?"+params.Encode(), &resp)
}
// GetFuturesMarkets gets a list of futures markets and their data
func (k *Kraken) GetFuturesMarkets() (FuturesInstrumentData, error) {
var resp FuturesInstrumentData
return resp, k.SendHTTPRequest(exchange.RestFutures, futuresInstruments, &resp)
}
// GetFuturesTickers gets a list of futures tickers and their data
func (k *Kraken) GetFuturesTickers() (FuturesTickerData, error) {
var resp FuturesTickerData
return resp, k.SendHTTPRequest(exchange.RestFutures, futuresTickers, &resp)
}
// GetFuturesTradeHistory gets public trade history data for futures
func (k *Kraken) GetFuturesTradeHistory(symbol currency.Pair, lastTime time.Time) (FuturesTradeHistoryData, error) {
var resp FuturesTradeHistoryData
params := url.Values{}
symbolValue, err := k.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
if !lastTime.IsZero() {
params.Set("lastTime", lastTime.Format("2006-01-02T15:04:05.070Z"))
}
return resp, k.SendHTTPRequest(exchange.RestFutures, futuresTradeHistory+"?"+params.Encode(), &resp)
}
// FuturesBatchOrder places a batch order for futures
func (k *Kraken) FuturesBatchOrder(data []PlaceBatchOrderData) (FuturesAccountsData, error) {
var resp FuturesAccountsData
for x := range data {
unformattedPair, err := currency.NewPairFromString(data[x].Symbol)
if err != nil {
return resp, err
}
formattedPair, err := k.FormatExchangeCurrency(unformattedPair, asset.Futures)
if err != nil {
return resp, err
}
data[x].Symbol = formattedPair.String()
}
req := make(map[string]interface{})
req["batchOrder"] = data
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresBatchOrder, nil, req, &resp)
}
// FuturesEditOrder edits a futures order
func (k *Kraken) FuturesEditOrder(orderID, clientOrderID string, size, limitPrice, stopPrice float64) (FuturesAccountsData, error) {
var resp FuturesAccountsData
params := url.Values{}
if orderID != "" {
params.Set("orderId", orderID)
}
if clientOrderID != "" {
params.Set("cliOrderId", clientOrderID)
}
params.Set("size", strconv.FormatFloat(size, 'f', -1, 64))
params.Set("limitPrice", strconv.FormatFloat(limitPrice, 'f', -1, 64))
params.Set("stopPrice", strconv.FormatFloat(stopPrice, 'f', -1, 64))
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresEditOrder, params, nil, &resp)
}
// FuturesSendOrder sends a futures order
func (k *Kraken) FuturesSendOrder(orderType order.Type, symbol currency.Pair, side, triggerSignal, clientOrderID, reduceOnly string,
size, limitPrice, stopPrice float64) (FuturesSendOrderData, error) {
var resp FuturesSendOrderData
oType, ok := validOrderTypes[orderType]
if !ok {
return resp, errors.New("invalid orderType")
}
params := url.Values{}
params.Set("orderType", oType)
symbolValue, err := k.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
if !common.StringDataCompare(validSide, side) {
return resp, errors.New("invalid side")
}
params.Set("side", side)
if triggerSignal != "" {
if !common.StringDataCompare(validTriggerSignal, triggerSignal) {
return resp, errors.New("invalid triggerSignal")
}
params.Set("triggerSignal", triggerSignal)
}
if clientOrderID != "" {
params.Set("cliOrdId", clientOrderID)
}
if reduceOnly != "" {
if !common.StringDataCompare(validReduceOnly, reduceOnly) {
return resp, errors.New("invalid reduceOnly")
}
params.Set("reduceOnly", reduceOnly)
}
params.Set("size", strconv.FormatFloat(size, 'f', -1, 64))
params.Set("limitPrice", strconv.FormatFloat(limitPrice, 'f', -1, 64))
if stopPrice != 0 {
params.Set("stopPrice", strconv.FormatFloat(stopPrice, 'f', -1, 64))
}
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresSendOrder, params, nil, &resp)
}
// FuturesCancelOrder cancels an order
func (k *Kraken) FuturesCancelOrder(orderID, clientOrderID string) (FuturesCancelOrderData, error) {
var resp FuturesCancelOrderData
params := url.Values{}
if orderID != "" {
params.Set("order_id", orderID)
}
if clientOrderID != "" {
params.Set("cliOrdId", clientOrderID)
}
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresCancelOrder, params, nil, &resp)
}
// FuturesGetFills gets order fills for futures
func (k *Kraken) FuturesGetFills(lastFillTime time.Time) (FuturesFillsData, error) {
var resp FuturesFillsData
params := url.Values{}
if !lastFillTime.IsZero() {
params.Set("lastFillTime", lastFillTime.UTC().Format("2006-01-02T15:04:05.999Z"))
}
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresOrderFills, params, nil, &resp)
}
// FuturesTransfer transfers funds between accounts
func (k *Kraken) FuturesTransfer(fromAccount, toAccount, unit string, amount float64) (FuturesTransferData, error) {
var resp FuturesTransferData
req := make(map[string]interface{})
req["fromAccount"] = fromAccount
req["toAccount"] = toAccount
req["unit"] = unit
req["amount"] = amount
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresTransfer, nil, nil, &resp)
}
// FuturesGetOpenPositions gets futures platform's notifications
func (k *Kraken) FuturesGetOpenPositions() (FuturesOpenPositions, error) {
var resp FuturesOpenPositions
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresOpenPositions, nil, nil, &resp)
}
// FuturesNotifications gets futures notifications
func (k *Kraken) FuturesNotifications() (FuturesNotificationData, error) {
var resp FuturesNotificationData
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresNotifications, nil, nil, &resp)
}
// FuturesCancelAllOrders cancels all futures orders for a given symbol or all symbols
func (k *Kraken) FuturesCancelAllOrders(symbol currency.Pair) (CancelAllOrdersData, error) {
var resp CancelAllOrdersData
params := url.Values{}
if symbol != (currency.Pair{}) {
symbolValue, err := k.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
}
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresCancelAllOrders, params, nil, &resp)
}
// FuturesCancelAllOrdersAfter cancels all futures orders for all symbols after a period of time (timeout measured in seconds)
func (k *Kraken) FuturesCancelAllOrdersAfter(timeout int64) (CancelOrdersAfterData, error) {
var resp CancelOrdersAfterData
params := url.Values{}
params.Set("timeout", strconv.FormatInt(timeout, 10))
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresCancelOrdersAfter, params, nil, &resp)
}
// FuturesOpenOrders gets all futures open orders
func (k *Kraken) FuturesOpenOrders() (FuturesOpenOrdersData, error) {
var resp FuturesOpenOrdersData
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresOpenOrders, nil, nil, &resp)
}
// FuturesRecentOrders gets recent futures orders for a symbol or all symbols
func (k *Kraken) FuturesRecentOrders(symbol currency.Pair) (FuturesRecentOrdersData, error) {
var resp FuturesRecentOrdersData
params := url.Values{}
if symbol != (currency.Pair{}) {
symbolValue, err := k.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
}
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresRecentOrders, nil, nil, &resp)
}
// FuturesWithdrawToSpotWallet withdraws currencies from futures wallet to spot wallet
func (k *Kraken) FuturesWithdrawToSpotWallet(currency string, amount float64) (GenericResponse, error) {
var resp GenericResponse
params := url.Values{}
params.Set("currency", currency)
params.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresWithdraw, params, nil, &resp)
}
// FuturesGetTransfers withdraws currencies from futures wallet to spot wallet
func (k *Kraken) FuturesGetTransfers(lastTransferTime time.Time) (GenericResponse, error) {
var resp GenericResponse
params := url.Values{}
if !lastTransferTime.IsZero() {
params.Set("lastTransferTime", lastTransferTime.UTC().Format(time.RFC3339))
}
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresTransfers, params, nil, &resp)
}
// GetFuturesAccountData gets account data for futures
func (k *Kraken) GetFuturesAccountData() (FuturesAccountsData, error) {
var resp FuturesAccountsData
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresAccountData, nil, nil, &resp)
}
func (k *Kraken) signFuturesRequest(endpoint, nonce, data string) string {
message := data + nonce + endpoint
hash := crypto.GetSHA256([]byte(message))
hc := crypto.GetHMAC(crypto.HashSHA512, hash, []byte(k.API.Credentials.Secret))
return base64.StdEncoding.EncodeToString(hc)
}
// SendFuturesAuthRequest will send an auth req
func (k *Kraken) SendFuturesAuthRequest(method, path string, postData url.Values, data map[string]interface{}, result interface{}) error {
if !k.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
k.Name)
}
if postData == nil {
postData = url.Values{}
}
nonce := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
reqData := ""
if len(data) > 0 {
temp, err := json.Marshal(data)
if err != nil {
return err
}
postData.Add("json", string(temp))
reqData = "json=" + string(temp)
}
sig := k.signFuturesRequest(path, nonce, reqData)
headers := map[string]string{
"APIKey": k.API.Credentials.Key,
"Authent": sig,
"Nonce": nonce,
}
interim := json.RawMessage{}
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute))
defer cancel()
err := k.SendPayload(ctx, &request.Item{
Method: method,
Path: futuresURL + common.EncodeURLValues(path, postData),
Headers: headers,
Result: &interim,
AuthRequest: true,
Verbose: k.Verbose,
HTTPDebugging: k.HTTPDebugging,
HTTPRecording: k.HTTPRecording,
})
if err != nil {
return err
}
var errCap AuthErrorData
if err := json.Unmarshal(interim, &errCap); err == nil {
if errCap.Result != "success" && errCap.Error != "" {
return errors.New(errCap.Error)
}
}
return json.Unmarshal(interim, result)
}

View File

@@ -49,7 +49,6 @@ func TestMain(m *testing.M) {
krakenConfig.API.AuthenticatedSupport = true
krakenConfig.API.Credentials.Key = apiKey
krakenConfig.API.Credentials.Secret = apiSecret
krakenConfig.API.Endpoints.WebsocketURL = k.API.Endpoints.WebsocketURL
k.Websocket = sharedtestvalues.NewTestWebsocket()
err = k.Setup(krakenConfig)
if err != nil {
@@ -67,6 +66,301 @@ func TestGetServerTime(t *testing.T) {
}
}
func TestFetchTradablePairs(t *testing.T) {
t.Parallel()
_, err := k.FetchTradablePairs(asset.Futures)
if err != nil {
t.Error(err)
}
}
func TestUpdateTicker(t *testing.T) {
t.Parallel()
sp, err := currency.NewPairFromString("XBTUSD")
if err != nil {
t.Error(err)
}
_, err = k.UpdateTicker(sp, asset.Spot)
if err != nil {
t.Error(err)
}
fp, err := currency.NewPairFromString("pi_xbtusd")
if err != nil {
t.Error(err)
}
_, err = k.UpdateTicker(fp, asset.Futures)
if err != nil {
t.Error(err)
}
}
func TestUpdateOrderbook(t *testing.T) {
t.Parallel()
sp, err := currency.NewPairFromString("BTCEUR")
if err != nil {
t.Error(err)
}
_, err = k.UpdateOrderbook(sp, asset.Spot)
if err != nil {
t.Error(err)
}
fp, err := currency.NewPairFromString("pi_xbtusd")
if err != nil {
t.Error(err)
}
_, err = k.UpdateOrderbook(fp, asset.Futures)
if err != nil {
t.Error(err)
}
}
func TestUpdateAccountInfo(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
_, err := k.UpdateAccountInfo(asset.Spot)
if err != nil {
t.Error(err)
}
}
func TestWrapperGetOrderInfo(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.GetOrderInfo("123", currency.Pair{}, asset.Futures)
if err != nil {
t.Error(err)
}
}
func TestFuturesBatchOrder(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
var data []PlaceBatchOrderData
var tempData PlaceBatchOrderData
tempData.PlaceOrderType = "cancel"
tempData.OrderID = "test123"
tempData.Symbol = "pi_xbtusd"
data = append(data, tempData)
_, err := k.FuturesBatchOrder(data)
if err != nil {
t.Error(err)
}
}
func TestFuturesEditOrder(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
_, err := k.FuturesEditOrder("test123", "", 5.2, 1, 0)
if err != nil {
t.Error(err)
}
}
func TestFuturesSendOrder(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
cp, err := currency.NewPairFromString("PI_XBTUSD")
if err != nil {
t.Error(err)
}
_, err = k.FuturesSendOrder(order.Limit, cp, "buy", "", "", "", 1, 1, 0.9)
if err != nil {
t.Error(err)
}
}
func TestFuturesCancelOrder(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
_, err := k.FuturesCancelOrder("test123", "")
if err != nil {
t.Error(err)
}
}
func TestFuturesGetFills(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.FuturesGetFills(time.Now().Add(-time.Hour * 24))
if err != nil {
t.Error(err)
}
}
func TestFuturesTransfer(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.FuturesTransfer("cash", "futures", "btc", 2)
if err != nil {
t.Error(err)
}
}
func TestFuturesGetOpenPositions(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.FuturesGetOpenPositions()
if err != nil {
t.Error(err)
}
}
func TestFuturesNotifications(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.FuturesNotifications()
if err != nil {
t.Error(err)
}
}
func TestFuturesCancelAllOrders(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
cp, err := currency.NewPairFromString("PI_XBTUSD")
if err != nil {
t.Error(err)
}
_, err = k.FuturesCancelAllOrders(cp)
if err != nil {
t.Error(err)
}
}
func TestGetFuturesAccountData(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.GetFuturesAccountData()
if err != nil {
t.Error(err)
}
}
func TestFuturesCancelAllOrdersAfter(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
_, err := k.FuturesCancelAllOrdersAfter(50)
if err != nil {
t.Error(err)
}
}
func TestFuturesOpenOrders(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.FuturesOpenOrders()
if err != nil {
t.Error(err)
}
}
func TestFuturesRecentOrders(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
cp, err := currency.NewPairFromString("PI_XBTUSD")
if err != nil {
t.Error(err)
}
_, err = k.FuturesRecentOrders(cp)
if err != nil {
t.Error(err)
}
}
func TestFuturesWithdrawToSpotWallet(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
_, err := k.FuturesWithdrawToSpotWallet("xbt", 5)
if err != nil {
t.Error(err)
}
}
func TestFuturesGetTransfers(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.FuturesGetTransfers(time.Now().Add(-time.Hour * 24))
if err != nil {
t.Error(err)
}
}
func TestGetFuturesOrderbook(t *testing.T) {
t.Parallel()
cp, err := currency.NewPairFromString("FI_xbtusd_200925")
if err != nil {
t.Error(err)
}
_, err = k.GetFuturesOrderbook(cp)
if err != nil {
t.Error(err)
}
}
func TestGetFuturesMarkets(t *testing.T) {
t.Parallel()
_, err := k.GetFuturesMarkets()
if err != nil {
t.Error(err)
}
}
func TestGetFuturesTickers(t *testing.T) {
t.Parallel()
_, err := k.GetFuturesTickers()
if err != nil {
t.Error(err)
}
}
func TestGetFuturesTradeHistory(t *testing.T) {
t.Parallel()
cp, err := currency.NewPairFromString("pi_xbtusd")
if err != nil {
t.Error(err)
}
_, err = k.GetFuturesTradeHistory(cp, time.Now().Add(-time.Hour*24))
if err != nil {
t.Error(err)
}
}
// TestGetAssets API endpoint test
func TestGetAssets(t *testing.T) {
t.Parallel()
@@ -137,7 +431,19 @@ func TestLookupCurrency(t *testing.T) {
// TestGetAssetPairs API endpoint test
func TestGetAssetPairs(t *testing.T) {
t.Parallel()
_, err := k.GetAssetPairs()
_, err := k.GetAssetPairs([]string{}, "fees")
if err != nil {
t.Error("GetAssetPairs() error", err)
}
_, err = k.GetAssetPairs([]string{}, "leverage")
if err != nil {
t.Error("GetAssetPairs() error", err)
}
_, err = k.GetAssetPairs([]string{}, "margin")
if err != nil {
t.Error("GetAssetPairs() error", err)
}
_, err = k.GetAssetPairs([]string{}, "")
if err != nil {
t.Error("GetAssetPairs() error", err)
}
@@ -146,7 +452,11 @@ func TestGetAssetPairs(t *testing.T) {
// TestGetTicker API endpoint test
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := k.GetTicker("BCHEUR")
cp, err := currency.NewPairFromString("BCHEUR")
if err != nil {
t.Error(err)
}
_, err = k.GetTicker(cp)
if err != nil {
t.Error("GetTicker() error", err)
}
@@ -164,7 +474,11 @@ func TestGetTickers(t *testing.T) {
// TestGetOHLC API endpoint test
func TestGetOHLC(t *testing.T) {
t.Parallel()
_, err := k.GetOHLC("XXBTZUSD", "1440")
cp, err := currency.NewPairFromString("XXBTZUSD")
if err != nil {
t.Error(err)
}
_, err = k.GetOHLC(cp, "1440")
if err != nil {
t.Error("GetOHLC() error", err)
}
@@ -173,7 +487,11 @@ func TestGetOHLC(t *testing.T) {
// TestGetDepth API endpoint test
func TestGetDepth(t *testing.T) {
t.Parallel()
_, err := k.GetDepth("BCHEUR")
cp, err := currency.NewPairFromString("BCHEUR")
if err != nil {
t.Error(err)
}
_, err = k.GetDepth(cp)
if err != nil {
t.Error("GetDepth() error", err)
}
@@ -182,12 +500,19 @@ func TestGetDepth(t *testing.T) {
// TestGetTrades API endpoint test
func TestGetTrades(t *testing.T) {
t.Parallel()
_, err := k.GetTrades("BCHEUR")
cp, err := currency.NewPairFromString("BCHEUR")
if err != nil {
t.Error(err)
}
_, err = k.GetTrades(cp)
if err != nil {
t.Error("GetTrades() error", err)
}
_, err = k.GetTrades("MADEUP")
cp2, err := currency.NewPairFromString("MADEUP")
if err != nil {
t.Error(err)
}
_, err = k.GetTrades(cp2)
if err == nil {
t.Error("expected error")
}
@@ -196,7 +521,11 @@ func TestGetTrades(t *testing.T) {
// TestGetSpread API endpoint test
func TestGetSpread(t *testing.T) {
t.Parallel()
_, err := k.GetSpread("BCHEUR")
cp, err := currency.NewPairFromString("BCHEUR")
if err != nil {
t.Error(err)
}
_, err = k.GetSpread(cp)
if err != nil {
t.Error("GetSpread() error", err)
}
@@ -301,7 +630,11 @@ func TestQueryLedgers(t *testing.T) {
// TestGetTradeVolume API endpoint test
func TestGetTradeVolume(t *testing.T) {
t.Parallel()
_, err := k.GetTradeVolume(true, "OAVY7T-MV5VK-KHDF5X")
cp, err := currency.NewPairFromString("OAVY7T-MV5VK-KHDF5X")
if err != nil {
t.Error(err)
}
_, err = k.GetTradeVolume(true, cp)
if err == nil {
t.Error("GetTradeVolume() Expected error")
}
@@ -311,7 +644,11 @@ func TestGetTradeVolume(t *testing.T) {
func TestAddOrder(t *testing.T) {
t.Parallel()
args := AddOrderOptions{OrderFlags: "fcib"}
_, err := k.AddOrder("XXBTZUSD",
cp, err := currency.NewPairFromString("XXBTZUSD")
if err != nil {
t.Error(err)
}
_, err = k.AddOrder(cp,
order.Sell.Lower(), order.Limit.Lower(),
0.00000001, 0, 0, 0, &args)
if err == nil {
@@ -447,22 +784,31 @@ func TestFormatWithdrawPermissions(t *testing.T) {
// TestGetActiveOrders wrapper test
func TestGetActiveOrders(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
pair, err := currency.NewPairFromString("LTC_USDT")
if err != nil {
t.Error(err)
}
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
Pairs: currency.Pairs{pair},
}
_, err := k.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
_, err = k.GetActiveOrders(&getOrdersRequest)
if err != nil {
t.Error(err)
}
}
// TestGetOrderHistory wrapper test
func TestGetOrderHistory(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := k.GetOrderHistory(&getOrdersRequest)
@@ -594,18 +940,30 @@ func TestCancelAllExchangeOrders(t *testing.T) {
// TestGetAccountInfo wrapper test
func TestGetAccountInfo(t *testing.T) {
if areTestAPIKeysSet() {
_, err := k.UpdateAccountInfo()
_, err := k.UpdateAccountInfo(asset.Spot)
if err != nil {
// Spot and Futures have separate api keys. Please ensure that the correct one is provided
t.Error("GetAccountInfo() error", err)
}
} else {
_, err := k.UpdateAccountInfo()
_, err := k.UpdateAccountInfo(asset.Spot)
if err == nil {
t.Error("GetAccountInfo() Expected error")
}
}
}
func TestUpdateFuturesAccountInfo(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("API keys not set. Skipping the test")
}
_, err := k.UpdateAccountInfo(asset.Futures)
if err != nil {
// Spot and Futures have separate api keys. Please ensure that the correct one is provided
t.Error(err)
}
}
// TestModifyOrder wrapper test
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
@@ -620,6 +978,7 @@ func TestModifyOrder(t *testing.T) {
// TestWithdraw wrapper test
func TestWithdraw(t *testing.T) {
withdrawCryptoRequest := withdraw.Request{
Exchange: k.Name,
Crypto: withdraw.CryptoRequest{
Address: core.BitcoinDonationAddress,
},
@@ -1335,7 +1694,7 @@ func TestWsOpenOrders(t *testing.T) {
"cost": "0.00000",
"descr": {
"close": "",
"leverage": "0:1",
"leverage": "0.1",
"order": "sell 10.00345345 XBT/USD @ limit 34.50000 with 0:1 leverage",
"ordertype": "limit",
"pair": "XBT/USD",
@@ -1364,7 +1723,7 @@ func TestWsOpenOrders(t *testing.T) {
"cost": "0.00000",
"descr": {
"close": "",
"leverage": "0:1",
"leverage": "0.1",
"order": "sell 0.00000010 XBT/USD @ limit 5334.60000 with 0:1 leverage",
"ordertype": "limit",
"pair": "XBT/USD",
@@ -1393,7 +1752,7 @@ func TestWsOpenOrders(t *testing.T) {
"cost": "0.00000",
"descr": {
"close": "",
"leverage": "0:1",
"leverage": "0.1",
"order": "sell 0.00001000 XBT/USD @ limit 90.40000 with 0:1 leverage",
"ordertype": "limit",
"pair": "XBT/USD",
@@ -1422,7 +1781,7 @@ func TestWsOpenOrders(t *testing.T) {
"cost": "0.00000",
"descr": {
"close": "",
"leverage": "0:1",
"leverage": "0.1",
"order": "sell 0.00001000 XBT/USD @ limit 9.00000 with 0:1 leverage",
"ordertype": "limit",
"pair": "XBT/USD",

View File

@@ -1,22 +1,96 @@
package kraken
import (
"sync"
"time"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
)
type assetTranslatorStore struct {
l sync.RWMutex
Assets map[string]string
const (
krakenAPIVersion = "0"
krakenServerTime = "Time"
krakenAssets = "Assets"
krakenAssetPairs = "AssetPairs?"
krakenTicker = "Ticker"
krakenOHLC = "OHLC"
krakenDepth = "Depth"
krakenTrades = "Trades"
krakenSpread = "Spread"
krakenBalance = "Balance"
krakenTradeBalance = "TradeBalance"
krakenOpenOrders = "OpenOrders"
krakenClosedOrders = "ClosedOrders"
krakenQueryOrders = "QueryOrders"
krakenTradeHistory = "TradesHistory"
krakenQueryTrades = "QueryTrades"
krakenOpenPositions = "OpenPositions"
krakenLedgers = "Ledgers"
krakenQueryLedgers = "QueryLedgers"
krakenTradeVolume = "TradeVolume"
krakenOrderCancel = "CancelOrder"
krakenOrderPlace = "AddOrder"
krakenWithdrawInfo = "WithdrawInfo"
krakenWithdraw = "Withdraw"
krakenDepositMethods = "DepositMethods"
krakenDepositAddresses = "DepositAddresses"
krakenWithdrawStatus = "WithdrawStatus"
krakenWithdrawCancel = "WithdrawCancel"
krakenWebsocketToken = "GetWebSocketsToken"
// Futures
futuresTickers = "/api/v3/tickers"
futuresOrderbook = "/api/v3/orderbook"
futuresInstruments = "/api/v3/instruments"
futuresTradeHistory = "/api/v3/history"
futuresSendOrder = "/api/v3/sendorder"
futuresCancelOrder = "/api/v3/cancelorder"
futuresOrderFills = "/api/v3/fills"
futuresTransfer = "/api/v3/transfer"
futuresOpenPositions = "/api/v3/openpositions"
futuresBatchOrder = "/api/v3/batchorder"
futuresNotifications = "/api/v3/notifications"
futuresAccountData = "/api/v3/accounts"
futuresCancelAllOrders = "/api/v3/cancelallorders"
futuresCancelOrdersAfter = "/api/v3/cancelallordersafter"
futuresOpenOrders = "/api/v3/openorders"
futuresRecentOrders = "/api/v3/recentorders"
futuresWithdraw = "/api/v3/withdrawal"
futuresTransfers = "/api/v3/transfers"
futuresEditOrder = "/api/v3/editorder"
// Rate limit consts
krakenRateInterval = time.Second
krakenRequestRate = 1
// Status consts
statusOpen = "open"
krakenFormat = "2006-01-02T15:04:05.000Z"
)
var (
assetTranslator assetTranslatorStore
)
// GenericResponse stores general response data for functions that only return success
type GenericResponse struct {
Timestamp string `json:"timestamp"`
Result string `json:"result"`
}
// TimeResponse type
type TimeResponse struct {
Unixtime int64 `json:"unixtime"`
Rfc1123 string `json:"rfc1123"`
// AuthErrorData stores authenticated error messages
type AuthErrorData struct {
Result string `json:"result"`
ServerTime string `json:"serverTime"`
Error string `json:"error"`
}
// SpotAuthError stores authenticated error messages
type SpotAuthError struct {
Error []string `json:"error"`
}
// Asset holds asset information
@@ -30,6 +104,7 @@ type Asset struct {
// AssetPairs holds asset pair information
type AssetPairs struct {
Altname string `json:"altname"`
Wsname string `json:"wsname"`
AclassBase string `json:"aclass_base"`
Base string `json:"base"`
AclassQuote string `json:"aclass_quote"`
@@ -45,6 +120,7 @@ type AssetPairs struct {
FeeVolumeCurrency string `json:"fee_volume_currency"`
MarginCall int `json:"margin_call"`
MarginStop int `json:"margin_stop"`
Ordermin string `json:"ordermin"`
}
// Ticker is a standard ticker type
@@ -520,7 +596,7 @@ type WsOpenOrder struct {
Close string `json:"close"`
Price float64 `json:"price,string"`
Price2 float64 `json:"price2,string"`
Leverage string `json:"leverage"`
Leverage float64 `json:"leverage,string"`
Order string `json:"order"`
OrderType string `json:"ordertype"`
Pair string `json:"pair"`
@@ -623,3 +699,11 @@ type WsCancelOrderResponse struct {
RequestID int64 `json:"reqid"`
Count int64 `json:"count"`
}
// OrderVars stores side, status and type for any order/trade
type OrderVars struct {
Side order.Side
Status order.Status
OrderType order.Type
Fee float64
}

View File

@@ -599,7 +599,7 @@ func (k *Kraken) wsProcessOpenOrders(ownOrders interface{}) error {
// allowing correlation between subscriptions and returned data
func (k *Kraken) addNewSubscriptionChannelData(response *wsSubscription) {
// We change the / to - to maintain compatibility with REST/config
var pair currency.Pair
var pair, fPair currency.Pair
if response.Pair != "" {
var err error
pair, err = currency.NewPairFromString(response.Pair)
@@ -607,11 +607,15 @@ func (k *Kraken) addNewSubscriptionChannelData(response *wsSubscription) {
log.Errorf(log.ExchangeSys, "%s exchange error: %s", k.Name, err)
return
}
pair.Delimiter = k.CurrencyPairs.RequestFormat.Delimiter
fPair, err = k.FormatExchangeCurrency(pair, asset.Spot)
if err != nil {
log.Errorf(log.ExchangeSys, "%s exchange error: %s", k.Name, err)
return
}
}
subscriptionChannelPair = append(subscriptionChannelPair, WebsocketChannelData{
Subscription: response.Subscription.Name,
Pair: pair,
Pair: fPair,
ChannelID: response.ChannelID,
})
}

File diff suppressed because it is too large Load Diff