mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-23 23:16:49 +00:00
Miscellaneous bug fixes (#513)
* Various bug fixes * Deadlink, cleanup plus bug fixes * Various Kraken fixes * Add convert func for decimal unix timestamps * Convert all test times to UTC * Kraken: Make assets a pointer to prevent excessive copying * Docker slash fix * Address nits plus bump ITBit last checked pairs timestamp * Set pairs to enabled pairs when getting active orders * Use asset translator for UpdateAccountInfo and more checks for the exchange template tool * Address MadCozBadd's nits * Make exchange var 2 chars * Make program more user friendly * Project wide comment checks and exchName check * Fix Huobi indexing bug and use correct pair formatting * Address nits + readme change
This commit is contained in:
@@ -415,6 +415,7 @@ func (b *Binance) SeedLocalCache(p currency.Pair) error {
|
||||
return b.SeedLocalCacheWithBook(p, &ob)
|
||||
}
|
||||
|
||||
// SeedLocalCacheWithBook seeds the local orderbook cache
|
||||
func (b *Binance) SeedLocalCacheWithBook(p currency.Pair, orderbookNew *OrderBook) error {
|
||||
var newOrderBook orderbook.Base
|
||||
for i := range orderbookNew.Bids {
|
||||
|
||||
@@ -42,8 +42,6 @@ const (
|
||||
coinbaseproReports = "reports"
|
||||
coinbaseproTime = "time"
|
||||
coinbaseproMarginTransfer = "profiles/margin-transfer"
|
||||
coinbaseproFunding = "funding"
|
||||
coinbaseproFundingRepay = "funding/repay"
|
||||
coinbaseproPosition = "position"
|
||||
coinbaseproPositionClose = "position/close"
|
||||
coinbaseproPaymentMethod = "payment-methods"
|
||||
@@ -490,37 +488,6 @@ func (c *CoinbasePro) GetFills(orderID, currencyPair string) ([]FillResponse, er
|
||||
c.SendAuthenticatedHTTPRequest(http.MethodGet, uri[1:], nil, &resp)
|
||||
}
|
||||
|
||||
// GetFundingRecords every order placed with a margin profile that draws funding
|
||||
// will create a funding record.
|
||||
//
|
||||
// status - "outstanding", "settled", or "rejected"
|
||||
func (c *CoinbasePro) GetFundingRecords(status string) ([]Funding, error) {
|
||||
var resp []Funding
|
||||
params := url.Values{}
|
||||
params.Set("status", status)
|
||||
|
||||
path := common.EncodeURLValues(c.API.Endpoints.URL+coinbaseproFunding, params)
|
||||
uri := common.GetURIPath(path)
|
||||
|
||||
return resp,
|
||||
c.SendAuthenticatedHTTPRequest(http.MethodGet, uri[1:], nil, &resp)
|
||||
}
|
||||
|
||||
// //////////////////////// Not receiving reply from server /////////////////
|
||||
// RepayFunding repays the older funding records first
|
||||
//
|
||||
// amount - amount of currency to repay
|
||||
// currency - currency, example USD
|
||||
// func (c *CoinbasePro) RepayFunding(amount, currency string) (Funding, error) {
|
||||
// resp := Funding{}
|
||||
// params := make(map[string]interface{})
|
||||
// params["amount"] = amount
|
||||
// params["currency"] = currency
|
||||
//
|
||||
// return resp,
|
||||
// c.SendAuthenticatedHTTPRequest(http.MethodPost, coinbaseproFundingRepay, params, &resp)
|
||||
// }
|
||||
|
||||
// MarginTransfer sends funds between a standard/default profile and a margin
|
||||
// profile.
|
||||
// A deposit will transfer funds from the default profile into the margin
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/core"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
@@ -165,10 +166,6 @@ func TestAuthRequests(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Error("Expecting error")
|
||||
}
|
||||
_, err = c.GetFundingRecords("rejected")
|
||||
if err == nil {
|
||||
t.Error("Expecting error")
|
||||
}
|
||||
marginTransferResponse, err := c.MarginTransfer(1, "withdraw", "13371337-1337-1337-1337-133713371337", "BTC")
|
||||
if marginTransferResponse.ID != "" {
|
||||
t.Error("Expecting no data returned")
|
||||
@@ -925,3 +922,17 @@ func TestStatusToStandardStatus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTime(t *testing.T) {
|
||||
// Rest examples use 2014-11-07T22:19:28.578544Z" and can be safely
|
||||
// unmarhsalled into time.Time
|
||||
|
||||
// All events except for activate use the above, in the below test
|
||||
// we'll use their API docs example
|
||||
r := convert.TimeFromUnixTimestampDecimal(1483736448.299000).UTC()
|
||||
if r.Year() != 2017 ||
|
||||
r.Month().String() != "January" ||
|
||||
r.Day() != 6 {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ type Ticker struct {
|
||||
|
||||
// Trade holds executed trade information
|
||||
type Trade struct {
|
||||
TradeID int64 `json:"trade_id"`
|
||||
Price float64 `json:"price,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
Time string `json:"time"`
|
||||
Side string `json:"side"`
|
||||
TradeID int64 `json:"trade_id"`
|
||||
Price float64 `json:"price,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
Time time.Time `json:"time"`
|
||||
Side string `json:"side"`
|
||||
}
|
||||
|
||||
// History holds historic rate information
|
||||
@@ -49,10 +49,12 @@ type History struct {
|
||||
|
||||
// Stats holds last 24 hr data for coinbasepro
|
||||
type Stats struct {
|
||||
Open float64 `json:"open,string"`
|
||||
High float64 `json:"high,string"`
|
||||
Low float64 `json:"low,string"`
|
||||
Volume float64 `json:"volume,string"`
|
||||
Open float64 `json:"open,string"`
|
||||
High float64 `json:"high,string"`
|
||||
Low float64 `json:"low,string"`
|
||||
Volume float64 `json:"volume,string"`
|
||||
Last float64 `json:"last,string"`
|
||||
Volume30Day float64 `json:"volume_30day,string"`
|
||||
}
|
||||
|
||||
// Currency holds singular currency product information
|
||||
@@ -64,8 +66,8 @@ type Currency struct {
|
||||
|
||||
// ServerTime holds current requested server time information
|
||||
type ServerTime struct {
|
||||
ISO string `json:"iso"`
|
||||
Epoch float64 `json:"epoch"`
|
||||
ISO time.Time `json:"iso"`
|
||||
Epoch float64 `json:"epoch"`
|
||||
}
|
||||
|
||||
// AccountResponse holds the details for the trading accounts
|
||||
@@ -84,7 +86,7 @@ type AccountResponse struct {
|
||||
// AccountLedgerResponse holds account history information
|
||||
type AccountLedgerResponse struct {
|
||||
ID string `json:"id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Balance float64 `json:"balance,string"`
|
||||
Type string `json:"type"`
|
||||
@@ -93,68 +95,68 @@ type AccountLedgerResponse struct {
|
||||
|
||||
// AccountHolds contains the hold information about an account
|
||||
type AccountHolds struct {
|
||||
ID string `json:"id"`
|
||||
AccountID string `json:"account_id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Type string `json:"type"`
|
||||
Reference string `json:"ref"`
|
||||
ID string `json:"id"`
|
||||
AccountID string `json:"account_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Type string `json:"type"`
|
||||
Reference string `json:"ref"`
|
||||
}
|
||||
|
||||
// GeneralizedOrderResponse is the generalized return type across order
|
||||
// placement and information collation
|
||||
type GeneralizedOrderResponse struct {
|
||||
ID string `json:"id"`
|
||||
Price float64 `json:"price,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
ProductID string `json:"product_id"`
|
||||
Side string `json:"side"`
|
||||
Stp string `json:"stp"`
|
||||
Type string `json:"type"`
|
||||
TimeInForce string `json:"time_in_force"`
|
||||
PostOnly bool `json:"post_only"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
FillFees float64 `json:"fill_fees,string"`
|
||||
FilledSize float64 `json:"filled_size,string"`
|
||||
ExecutedValue float64 `json:"executed_value,string"`
|
||||
Status string `json:"status"`
|
||||
Settled bool `json:"settled"`
|
||||
Funds float64 `json:"funds,string"`
|
||||
SpecifiedFunds float64 `json:"specified_funds,string"`
|
||||
DoneReason string `json:"done_reason"`
|
||||
DoneAt string `json:"done_at"`
|
||||
ID string `json:"id"`
|
||||
Price float64 `json:"price,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
ProductID string `json:"product_id"`
|
||||
Side string `json:"side"`
|
||||
Stp string `json:"stp"`
|
||||
Type string `json:"type"`
|
||||
TimeInForce string `json:"time_in_force"`
|
||||
PostOnly bool `json:"post_only"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
FillFees float64 `json:"fill_fees,string"`
|
||||
FilledSize float64 `json:"filled_size,string"`
|
||||
ExecutedValue float64 `json:"executed_value,string"`
|
||||
Status string `json:"status"`
|
||||
Settled bool `json:"settled"`
|
||||
Funds float64 `json:"funds,string"`
|
||||
SpecifiedFunds float64 `json:"specified_funds,string"`
|
||||
DoneReason string `json:"done_reason"`
|
||||
DoneAt string `json:"done_at"`
|
||||
}
|
||||
|
||||
// Funding holds funding data
|
||||
type Funding struct {
|
||||
ID string `json:"id"`
|
||||
OrderID string `json:"order_id"`
|
||||
ProfileID string `json:"profile_id"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Currency string `json:"currency"`
|
||||
RepaidAmount float64 `json:"repaid_amount"`
|
||||
DefaultAmount float64 `json:"default_amount,string"`
|
||||
RepaidDefault bool `json:"repaid_default"`
|
||||
ID string `json:"id"`
|
||||
OrderID string `json:"order_id"`
|
||||
ProfileID string `json:"profile_id"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Currency string `json:"currency"`
|
||||
RepaidAmount float64 `json:"repaid_amount"`
|
||||
DefaultAmount float64 `json:"default_amount,string"`
|
||||
RepaidDefault bool `json:"repaid_default"`
|
||||
}
|
||||
|
||||
// MarginTransfer holds margin transfer details
|
||||
type MarginTransfer struct {
|
||||
CreatedAt string `json:"created_at"`
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
ProfileID string `json:"profile_id"`
|
||||
MarginProfileID string `json:"margin_profile_id"`
|
||||
Type string `json:"type"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Currency string `json:"currency"`
|
||||
AccountID string `json:"account_id"`
|
||||
MarginAccountID string `json:"margin_account_id"`
|
||||
MarginProductID string `json:"margin_product_id"`
|
||||
Status string `json:"status"`
|
||||
Nonce int `json:"nonce"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
ProfileID string `json:"profile_id"`
|
||||
MarginProfileID string `json:"margin_profile_id"`
|
||||
Type string `json:"type"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Currency string `json:"currency"`
|
||||
AccountID string `json:"account_id"`
|
||||
MarginAccountID string `json:"margin_account_id"`
|
||||
MarginProductID string `json:"margin_product_id"`
|
||||
Status string `json:"status"`
|
||||
Nonce int `json:"nonce"`
|
||||
}
|
||||
|
||||
// AccountOverview holds account information returned from position
|
||||
@@ -164,12 +166,12 @@ type AccountOverview struct {
|
||||
MaxFundingValue float64 `json:"max_funding_value,string"`
|
||||
FundingValue float64 `json:"funding_value,string"`
|
||||
OldestOutstanding struct {
|
||||
ID string `json:"id"`
|
||||
OrderID string `json:"order_id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Currency string `json:"currency"`
|
||||
AccountID string `json:"account_id"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
ID string `json:"id"`
|
||||
OrderID string `json:"order_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Currency string `json:"currency"`
|
||||
AccountID string `json:"account_id"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
} `json:"oldest_outstanding"`
|
||||
} `json:"funding"`
|
||||
Accounts struct {
|
||||
@@ -236,10 +238,10 @@ type LimitInfo struct {
|
||||
|
||||
// DepositWithdrawalInfo holds returned deposit information
|
||||
type DepositWithdrawalInfo struct {
|
||||
ID string `json:"id"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Currency string `json:"currency"`
|
||||
PayoutAt string `json:"payout_at"`
|
||||
ID string `json:"id"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
Currency string `json:"currency"`
|
||||
PayoutAt time.Time `json:"payout_at"`
|
||||
}
|
||||
|
||||
// CoinbaseAccounts holds coinbase account information
|
||||
@@ -278,16 +280,16 @@ type CoinbaseAccounts struct {
|
||||
|
||||
// Report holds historical information
|
||||
type Report struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
CompletedAt string `json:"completed_at"`
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
FileURL string `json:"file_url"`
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
CompletedAt time.Time `json:"completed_at"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
FileURL string `json:"file_url"`
|
||||
Params struct {
|
||||
StartDate string `json:"start_date"`
|
||||
EndDate string `json:"end_date"`
|
||||
StartDate time.Time `json:"start_date"`
|
||||
EndDate time.Time `json:"end_date"`
|
||||
} `json:"params"`
|
||||
}
|
||||
|
||||
@@ -336,16 +338,16 @@ type OrderbookResponse struct {
|
||||
|
||||
// FillResponse contains fill information from the exchange
|
||||
type FillResponse struct {
|
||||
TradeID int `json:"trade_id"`
|
||||
ProductID string `json:"product_id"`
|
||||
Price float64 `json:"price,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
OrderID string `json:"order_id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Liquidity string `json:"liquidity"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
Settled bool `json:"settled"`
|
||||
Side string `json:"side"`
|
||||
TradeID int `json:"trade_id"`
|
||||
ProductID string `json:"product_id"`
|
||||
Price float64 `json:"price,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
OrderID string `json:"order_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Liquidity string `json:"liquidity"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
Settled bool `json:"settled"`
|
||||
Side string `json:"side"`
|
||||
}
|
||||
|
||||
// WebsocketSubscribe takes in subscription information
|
||||
|
||||
@@ -177,12 +177,7 @@ func (c *CoinbasePro) wsHandleData(respRaw []byte) error {
|
||||
}
|
||||
ts := wsOrder.Time
|
||||
if wsOrder.Type == "activate" {
|
||||
var one, two int64
|
||||
one, two, err = convert.SplitFloatDecimals(wsOrder.Timestamp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ts = time.Unix(one, two)
|
||||
ts = convert.TimeFromUnixTimestampDecimal(wsOrder.Timestamp)
|
||||
}
|
||||
|
||||
var p currency.Pair
|
||||
|
||||
@@ -311,7 +311,7 @@ func (c *CoinbasePro) UpdateTicker(p currency.Pair, assetType asset.Item) (*tick
|
||||
}
|
||||
|
||||
tickerPrice := &ticker.Price{
|
||||
Last: tick.Size,
|
||||
Last: stats.Last,
|
||||
High: stats.High,
|
||||
Low: stats.Low,
|
||||
Bid: tick.Bid,
|
||||
@@ -498,12 +498,8 @@ func (c *CoinbasePro) GetOrderInfo(orderID string) (order.Detail, error) {
|
||||
if errTSi != nil {
|
||||
return response, fmt.Errorf("error parsing order Side: %s", errTSi)
|
||||
}
|
||||
td, errTd := time.Parse(time.RFC3339, fillResponse[i].CreatedAt)
|
||||
if errTd != nil {
|
||||
return response, fmt.Errorf("error parsing trade created time: %s", errTd)
|
||||
}
|
||||
response.Trades = append(response.Trades, order.TradeHistory{
|
||||
Timestamp: td,
|
||||
Timestamp: fillResponse[i].CreatedAt,
|
||||
TID: string(fillResponse[i].TradeID),
|
||||
Price: fillResponse[i].Price,
|
||||
Amount: fillResponse[i].Size,
|
||||
@@ -607,22 +603,12 @@ func (c *CoinbasePro) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Deta
|
||||
c.GetPairFormat(asset.Spot, false).Delimiter)
|
||||
orderSide := order.Side(strings.ToUpper(respOrders[i].Side))
|
||||
orderType := order.Type(strings.ToUpper(respOrders[i].Type))
|
||||
orderDate, err := time.Parse(time.RFC3339, respOrders[i].CreatedAt)
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys,
|
||||
"Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
|
||||
c.Name,
|
||||
"GetActiveOrders",
|
||||
respOrders[i].ID,
|
||||
respOrders[i].CreatedAt)
|
||||
}
|
||||
|
||||
orders = append(orders, order.Detail{
|
||||
ID: respOrders[i].ID,
|
||||
Amount: respOrders[i].Size,
|
||||
ExecutedAmount: respOrders[i].FilledSize,
|
||||
Type: orderType,
|
||||
Date: orderDate,
|
||||
Date: respOrders[i].CreatedAt,
|
||||
Side: orderSide,
|
||||
Pair: curr,
|
||||
Exchange: c.Name,
|
||||
@@ -654,22 +640,12 @@ func (c *CoinbasePro) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Deta
|
||||
c.GetPairFormat(asset.Spot, false).Delimiter)
|
||||
orderSide := order.Side(strings.ToUpper(respOrders[i].Side))
|
||||
orderType := order.Type(strings.ToUpper(respOrders[i].Type))
|
||||
orderDate, err := time.Parse(time.RFC3339, respOrders[i].CreatedAt)
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys,
|
||||
"Exchange %v Func %v Order %v Could not parse date to unix with value of %v",
|
||||
c.Name,
|
||||
"GetActiveOrders",
|
||||
respOrders[i].ID,
|
||||
respOrders[i].CreatedAt)
|
||||
}
|
||||
|
||||
orders = append(orders, order.Detail{
|
||||
ID: respOrders[i].ID,
|
||||
Amount: respOrders[i].Size,
|
||||
ExecutedAmount: respOrders[i].FilledSize,
|
||||
Type: orderType,
|
||||
Date: orderDate,
|
||||
Date: respOrders[i].CreatedAt,
|
||||
Side: orderSide,
|
||||
Pair: curr,
|
||||
Exchange: c.Name,
|
||||
|
||||
@@ -229,45 +229,60 @@ func (e *EXMO) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderbook
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (e *EXMO) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
|
||||
orderBook := new(orderbook.Base)
|
||||
pairsCollated, err := e.FormatExchangeCurrencies(e.GetEnabledPairs(assetType),
|
||||
assetType)
|
||||
enabledPairs := e.GetEnabledPairs(assetType)
|
||||
pairsCollated, err := e.FormatExchangeCurrencies(enabledPairs, assetType)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := e.GetOrderbook(pairsCollated)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
return nil, err
|
||||
}
|
||||
enabledPairs := e.GetEnabledPairs(assetType)
|
||||
|
||||
for i := range enabledPairs {
|
||||
curr := e.FormatExchangeCurrency(enabledPairs[i], assetType)
|
||||
data, ok := result[curr.String()]
|
||||
data, ok := result[e.FormatExchangeCurrency(enabledPairs[i], assetType).String()]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
var obItems []orderbook.Item
|
||||
orderBook := new(orderbook.Base)
|
||||
for y := range data.Ask {
|
||||
z := data.Ask[y]
|
||||
price, _ := strconv.ParseFloat(z[0], 64)
|
||||
amount, _ := strconv.ParseFloat(z[1], 64)
|
||||
obItems = append(obItems,
|
||||
orderbook.Item{Price: price, Amount: amount})
|
||||
var price, amount float64
|
||||
price, err = strconv.ParseFloat(data.Ask[y][0], 64)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
amount, err = strconv.ParseFloat(data.Ask[y][1], 64)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
|
||||
Price: price,
|
||||
Amount: amount,
|
||||
})
|
||||
}
|
||||
|
||||
orderBook.Asks = obItems
|
||||
obItems = []orderbook.Item{}
|
||||
for y := range data.Bid {
|
||||
z := data.Bid[y]
|
||||
price, _ := strconv.ParseFloat(z[0], 64)
|
||||
amount, _ := strconv.ParseFloat(z[1], 64)
|
||||
obItems = append(obItems,
|
||||
orderbook.Item{Price: price, Amount: amount})
|
||||
var price, amount float64
|
||||
price, err = strconv.ParseFloat(data.Bid[y][0], 64)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
amount, err = strconv.ParseFloat(data.Bid[y][1], 64)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
|
||||
Price: price,
|
||||
Amount: amount,
|
||||
})
|
||||
}
|
||||
|
||||
orderBook.Bids = obItems
|
||||
orderBook.Pair = enabledPairs[i]
|
||||
orderBook.ExchangeName = e.Name
|
||||
orderBook.AssetType = assetType
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/core"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
@@ -743,3 +744,21 @@ func TestWsBalanceUpdate(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTime(t *testing.T) {
|
||||
// Test REST example
|
||||
r := convert.TimeFromUnixTimestampDecimal(1574846296.995313).UTC()
|
||||
if r.Year() != 2019 ||
|
||||
r.Month().String() != "November" ||
|
||||
r.Day() != 27 {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
|
||||
// Test websocket example
|
||||
r = convert.TimeFromUnixTimestampDecimal(1523887354.256974).UTC()
|
||||
if r.Year() != 2018 ||
|
||||
r.Month().String() != "April" ||
|
||||
r.Day() != 16 {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,16 +223,7 @@ func (g *Gateio) wsHandleData(respRaw []byte) error {
|
||||
case 2:
|
||||
oSide = order.Buy
|
||||
}
|
||||
var cTime, cTimeDec, mTime, mTimeDec int64
|
||||
var price, amount, filledTotal, left, fee float64
|
||||
cTime, cTimeDec, err = convert.SplitFloatDecimals(invalidJSON["ctime"].(float64))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mTime, mTimeDec, err = convert.SplitFloatDecimals(invalidJSON["mtime"].(float64))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
price, err = strconv.ParseFloat(invalidJSON["price"].(string), 64)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -271,8 +262,8 @@ func (g *Gateio) wsHandleData(respRaw []byte) error {
|
||||
Side: oSide,
|
||||
Status: oStatus,
|
||||
AssetType: a,
|
||||
Date: time.Unix(cTime, cTimeDec),
|
||||
LastUpdated: time.Unix(mTime, mTimeDec),
|
||||
Date: convert.TimeFromUnixTimestampDecimal(invalidJSON["ctime"].(float64)),
|
||||
LastUpdated: convert.TimeFromUnixTimestampDecimal(invalidJSON["mtime"].(float64)),
|
||||
Pair: p,
|
||||
}
|
||||
case strings.Contains(result.Method, "depth"):
|
||||
|
||||
@@ -592,11 +592,6 @@ func (g *Gateio) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e
|
||||
if resp.WebSocketOrderQueryRecords[j].OrderType == 1 {
|
||||
orderType = order.Limit
|
||||
}
|
||||
firstNum, decNum, err := convert.SplitFloatDecimals(resp.WebSocketOrderQueryRecords[j].Ctime)
|
||||
if err != nil {
|
||||
return orders, err
|
||||
}
|
||||
orderDate := time.Unix(firstNum, decNum)
|
||||
orders = append(orders, order.Detail{
|
||||
Exchange: g.Name,
|
||||
AccountID: strconv.FormatInt(resp.WebSocketOrderQueryRecords[j].User, 10),
|
||||
@@ -604,7 +599,7 @@ func (g *Gateio) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e
|
||||
Pair: currency.NewPairFromString(resp.WebSocketOrderQueryRecords[j].Market),
|
||||
Side: orderSide,
|
||||
Type: orderType,
|
||||
Date: orderDate,
|
||||
Date: convert.TimeFromUnixTimestampDecimal(resp.WebSocketOrderQueryRecords[j].Ctime),
|
||||
Price: resp.WebSocketOrderQueryRecords[j].Price,
|
||||
Amount: resp.WebSocketOrderQueryRecords[j].Amount,
|
||||
ExecutedAmount: resp.WebSocketOrderQueryRecords[j].FilledAmount,
|
||||
|
||||
@@ -519,7 +519,7 @@ func (h *HUOBI) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
|
||||
var params = SpotNewOrderRequestParams{
|
||||
Amount: s.Amount,
|
||||
Source: "api",
|
||||
Symbol: s.Pair.Lower().String(),
|
||||
Symbol: h.FormatExchangeCurrency(s.Pair, s.AssetType).String(),
|
||||
AccountID: int(accountID),
|
||||
}
|
||||
|
||||
@@ -797,28 +797,28 @@ func (h *HUOBI) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, er
|
||||
} else {
|
||||
for i := range req.Pairs {
|
||||
resp, err := h.GetOpenOrders(h.API.Credentials.ClientID,
|
||||
req.Pairs[i].Lower().String(),
|
||||
h.FormatExchangeCurrency(req.Pairs[i], asset.Spot).String(),
|
||||
side,
|
||||
500)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range resp {
|
||||
for x := range resp {
|
||||
orderDetail := order.Detail{
|
||||
ID: strconv.FormatInt(resp[i].ID, 10),
|
||||
Price: resp[i].Price,
|
||||
Amount: resp[i].Amount,
|
||||
ID: strconv.FormatInt(resp[x].ID, 10),
|
||||
Price: resp[x].Price,
|
||||
Amount: resp[x].Amount,
|
||||
Pair: req.Pairs[i],
|
||||
Exchange: h.Name,
|
||||
ExecutedAmount: resp[i].FilledAmount,
|
||||
Date: time.Unix(0, resp[i].CreatedAt*int64(time.Millisecond)),
|
||||
Status: order.Status(resp[i].State),
|
||||
AccountID: strconv.FormatInt(resp[i].AccountID, 10),
|
||||
Fee: resp[i].FilledFees,
|
||||
ExecutedAmount: resp[x].FilledAmount,
|
||||
Date: time.Unix(0, resp[x].CreatedAt*int64(time.Millisecond)),
|
||||
Status: order.Status(resp[x].State),
|
||||
AccountID: strconv.FormatInt(resp[x].AccountID, 10),
|
||||
Fee: resp[x].FilledFees,
|
||||
}
|
||||
|
||||
setOrderSideAndType(resp[i].Type, &orderDetail)
|
||||
setOrderSideAndType(resp[x].Type, &orderDetail)
|
||||
|
||||
orders = append(orders, orderDetail)
|
||||
}
|
||||
@@ -839,7 +839,8 @@ func (h *HUOBI) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er
|
||||
states := "partial-canceled,filled,canceled"
|
||||
var orders []order.Detail
|
||||
for i := range req.Pairs {
|
||||
resp, err := h.GetOrders(req.Pairs[i].Lower().String(),
|
||||
resp, err := h.GetOrders(
|
||||
h.FormatExchangeCurrency(req.Pairs[i], asset.Spot).String(),
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
@@ -851,21 +852,21 @@ func (h *HUOBI) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range resp {
|
||||
for x := range resp {
|
||||
orderDetail := order.Detail{
|
||||
ID: strconv.FormatInt(resp[i].ID, 10),
|
||||
Price: resp[i].Price,
|
||||
Amount: resp[i].Amount,
|
||||
ID: strconv.FormatInt(resp[x].ID, 10),
|
||||
Price: resp[x].Price,
|
||||
Amount: resp[x].Amount,
|
||||
Pair: req.Pairs[i],
|
||||
Exchange: h.Name,
|
||||
ExecutedAmount: resp[i].FilledAmount,
|
||||
Date: time.Unix(0, resp[i].CreatedAt*int64(time.Millisecond)),
|
||||
Status: order.Status(resp[i].State),
|
||||
AccountID: strconv.FormatInt(resp[i].AccountID, 10),
|
||||
Fee: resp[i].FilledFees,
|
||||
ExecutedAmount: resp[x].FilledAmount,
|
||||
Date: time.Unix(0, resp[x].CreatedAt*int64(time.Millisecond)),
|
||||
Status: order.Status(resp[x].State),
|
||||
AccountID: strconv.FormatInt(resp[x].AccountID, 10),
|
||||
Fee: resp[x].FilledFees,
|
||||
}
|
||||
|
||||
setOrderSideAndType(resp[i].Type, &orderDetail)
|
||||
setOrderSideAndType(resp[x].Type, &orderDetail)
|
||||
|
||||
orders = append(orders, orderDetail)
|
||||
}
|
||||
|
||||
@@ -57,7 +57,9 @@ const (
|
||||
krakenRequestRate = 1
|
||||
)
|
||||
|
||||
var assetPairMap map[string]string
|
||||
var (
|
||||
assetTranslator assetTranslatorStore
|
||||
)
|
||||
|
||||
// Kraken is the overarching type across the alphapoint package
|
||||
type Kraken struct {
|
||||
@@ -83,41 +85,54 @@ func (k *Kraken) GetServerTime() (TimeResponse, error) {
|
||||
return response.Result, GetError(response.Error)
|
||||
}
|
||||
|
||||
// SeedAssets seeds Kraken's asset list and stores it in the
|
||||
// asset translator
|
||||
func (k *Kraken) SeedAssets() error {
|
||||
assets, err := k.GetAssets()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range assets {
|
||||
assetTranslator.Seed(k, v.Altname)
|
||||
}
|
||||
|
||||
assetPairs, err := k.GetAssetPairs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range assetPairs {
|
||||
assetTranslator.Seed(k, v.Altname)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAssets returns a full asset list
|
||||
func (k *Kraken) GetAssets() (map[string]Asset, error) {
|
||||
func (k *Kraken) GetAssets() (map[string]*Asset, error) {
|
||||
path := fmt.Sprintf("%s/%s/public/%s", k.API.Endpoints.URL, krakenAPIVersion, krakenAssets)
|
||||
|
||||
var response struct {
|
||||
Error []string `json:"error"`
|
||||
Result map[string]Asset `json:"result"`
|
||||
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) {
|
||||
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"`
|
||||
Error []string `json:"error"`
|
||||
Result map[string]*AssetPairs `json:"result"`
|
||||
}
|
||||
|
||||
if err := k.SendHTTPRequest(path, &response); err != nil {
|
||||
return response.Result, err
|
||||
}
|
||||
for i := range response.Result {
|
||||
if assetPairMap == nil {
|
||||
assetPairMap = make(map[string]string)
|
||||
}
|
||||
assetPairMap[i] = response.Result[i].Altname
|
||||
}
|
||||
|
||||
return response.Result, GetError(response.Error)
|
||||
}
|
||||
|
||||
@@ -1054,3 +1069,53 @@ func (k *Kraken) GetWebsocketToken() (string, error) {
|
||||
}
|
||||
return response.Result.Token, nil
|
||||
}
|
||||
|
||||
// LookupAltname converts a currency into its altname (ZUSD -> USD)
|
||||
func (a *assetTranslatorStore) LookupAltname(target string) string {
|
||||
a.l.RLock()
|
||||
alt, ok := a.Assets[target]
|
||||
if !ok {
|
||||
a.l.RUnlock()
|
||||
return ""
|
||||
}
|
||||
a.l.RUnlock()
|
||||
return alt
|
||||
}
|
||||
|
||||
// LookupAltname converts an altname to its original type (USD -> ZUSD)
|
||||
func (a *assetTranslatorStore) LookupCurrency(target string) string {
|
||||
a.l.RLock()
|
||||
for k, v := range a.Assets {
|
||||
if v == target {
|
||||
a.l.RUnlock()
|
||||
return k
|
||||
}
|
||||
}
|
||||
a.l.RUnlock()
|
||||
return ""
|
||||
}
|
||||
|
||||
// Seed seeds a currency translation pair
|
||||
func (a *assetTranslatorStore) Seed(orig, alt string) {
|
||||
a.l.Lock()
|
||||
if a.Assets == nil {
|
||||
a.Assets = make(map[string]string)
|
||||
}
|
||||
|
||||
_, ok := a.Assets[orig]
|
||||
if ok {
|
||||
a.l.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
a.Assets[orig] = alt
|
||||
a.l.Unlock()
|
||||
}
|
||||
|
||||
// Seeded returns whether or not the asset translator has been seeded
|
||||
func (a *assetTranslatorStore) Seeded() bool {
|
||||
a.l.RLock()
|
||||
isSeeded := len(a.Assets) > 0
|
||||
a.l.RUnlock()
|
||||
return isSeeded
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/core"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
@@ -25,7 +26,6 @@ var wsSetupRan bool
|
||||
const (
|
||||
apiKey = ""
|
||||
apiSecret = ""
|
||||
clientID = ""
|
||||
canManipulateRealOrders = false
|
||||
)
|
||||
|
||||
@@ -35,20 +35,19 @@ func TestMain(m *testing.M) {
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig("../../testdata/configtest.json", true)
|
||||
if err != nil {
|
||||
log.Fatal("Kraken load config error", err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
krakenConfig, err := cfg.GetExchangeConfig("Kraken")
|
||||
if err != nil {
|
||||
log.Fatal("kraken Setup() init error", err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
krakenConfig.API.AuthenticatedSupport = true
|
||||
krakenConfig.API.Credentials.Key = apiKey
|
||||
krakenConfig.API.Credentials.Secret = apiSecret
|
||||
krakenConfig.API.Credentials.ClientID = clientID
|
||||
krakenConfig.API.Endpoints.WebsocketURL = k.API.Endpoints.WebsocketURL
|
||||
err = k.Setup(krakenConfig)
|
||||
if err != nil {
|
||||
log.Fatal("Kraken setup error", err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
k.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
|
||||
k.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
|
||||
@@ -73,6 +72,64 @@ func TestGetAssets(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeedAssetTranslator(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Test currency pair
|
||||
if r := assetTranslator.LookupAltname("XXBTZUSD"); r != "XBTUSD" {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
if r := assetTranslator.LookupCurrency("XBTUSD"); r != "XXBTZUSD" {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
|
||||
// Test fiat currency
|
||||
if r := assetTranslator.LookupAltname("ZUSD"); r != "USD" {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
if r := assetTranslator.LookupCurrency("USD"); r != "ZUSD" {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
|
||||
// Test cryptocurrency
|
||||
if r := assetTranslator.LookupAltname("XXBT"); r != "XBT" {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
if r := assetTranslator.LookupCurrency("XBT"); r != "XXBT" {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeedAssets(t *testing.T) {
|
||||
t.Parallel()
|
||||
var a assetTranslatorStore
|
||||
if r := a.LookupAltname("ZUSD"); r != "" {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
a.Seed("ZUSD", "USD")
|
||||
if r := a.LookupAltname("ZUSD"); r != "USD" {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
a.Seed("ZUSD", "BLA")
|
||||
if r := a.LookupAltname("ZUSD"); r != "USD" {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupCurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
var a assetTranslatorStore
|
||||
if r := a.LookupCurrency("USD"); r != "" {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
a.Seed("ZUSD", "USD")
|
||||
if r := a.LookupCurrency("USD"); r != "ZUSD" {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
if r := a.LookupCurrency("EUR"); r != "" {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetAssetPairs API endpoint test
|
||||
func TestGetAssetPairs(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -413,12 +470,16 @@ func TestGetOrderInfo(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
_, err := k.GetOrderInfo("ImACoolOrderID")
|
||||
_, err := k.GetOrderInfo("OZPTPJ-HVYHF-EDIGXS")
|
||||
if !areTestAPIKeysSet() && err == nil {
|
||||
t.Error("Expecting error")
|
||||
}
|
||||
if areTestAPIKeysSet() && !strings.Contains(err.Error(), "- Order ID not found:") {
|
||||
t.Error("Expected Order ID not found error")
|
||||
if areTestAPIKeysSet() && err != nil {
|
||||
if !strings.Contains(err.Error(), "- Order ID not found:") {
|
||||
t.Error("Expected Order ID not found error")
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,12 +520,8 @@ func TestCancelExchangeOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
|
||||
var orderCancellation = &order.Cancel{
|
||||
ID: "1",
|
||||
WalletAddress: core.BitcoinDonationAddress,
|
||||
AccountID: "1",
|
||||
Pair: currencyPair,
|
||||
ID: "OGEX6P-B5Q74-IGZ72R",
|
||||
}
|
||||
|
||||
err := k.CancelOrder(orderCancellation)
|
||||
@@ -482,16 +539,7 @@ func TestCancelAllExchangeOrders(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
|
||||
var orderCancellation = &order.Cancel{
|
||||
ID: "1",
|
||||
WalletAddress: core.BitcoinDonationAddress,
|
||||
AccountID: "1",
|
||||
Pair: currencyPair,
|
||||
}
|
||||
|
||||
resp, err := k.CancelAllOrders(orderCancellation)
|
||||
|
||||
resp, err := k.CancelAllOrders(&order.Cancel{})
|
||||
if !areTestAPIKeysSet() && err == nil {
|
||||
t.Error("Expecting an error when no keys are set")
|
||||
}
|
||||
@@ -506,7 +554,7 @@ func TestCancelAllExchangeOrders(t *testing.T) {
|
||||
|
||||
// TestGetAccountInfo wrapper test
|
||||
func TestGetAccountInfo(t *testing.T) {
|
||||
if areTestAPIKeysSet() || clientID != "" {
|
||||
if areTestAPIKeysSet() {
|
||||
_, err := k.UpdateAccountInfo()
|
||||
if err != nil {
|
||||
t.Error("GetAccountInfo() error", err)
|
||||
@@ -1355,3 +1403,21 @@ func TestWsCancelOrderJSON(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTime(t *testing.T) {
|
||||
// Test REST example
|
||||
r := convert.TimeFromUnixTimestampDecimal(1373750306.9819).UTC()
|
||||
if r.Year() != 2013 ||
|
||||
r.Month().String() != "July" ||
|
||||
r.Day() != 13 {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
|
||||
// Test Websocket time example
|
||||
r = convert.TimeFromUnixTimestampDecimal(1534614098.345543).UTC()
|
||||
if r.Year() != 2018 ||
|
||||
r.Month().String() != "August" ||
|
||||
r.Day() != 18 {
|
||||
t.Error("unexpected result")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package kraken
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
)
|
||||
|
||||
type assetTranslatorStore struct {
|
||||
l sync.RWMutex
|
||||
Assets map[string]string
|
||||
}
|
||||
|
||||
// TimeResponse type
|
||||
type TimeResponse struct {
|
||||
Unixtime int64 `json:"unixtime"`
|
||||
@@ -133,6 +139,7 @@ type OrderInfo struct {
|
||||
UserRef int32 `json:"userref"`
|
||||
Status string `json:"status"`
|
||||
OpenTime float64 `json:"opentm"`
|
||||
CloseTime float64 `json:"closetm"`
|
||||
StartTime float64 `json:"starttm"`
|
||||
ExpireTime float64 `json:"expiretm"`
|
||||
Description struct {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -303,10 +302,6 @@ func (k *Kraken) wsProcessOwnTrades(ownOrders interface{}) error {
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
txTime, txTimeNano, err := convert.SplitFloatDecimals(val.Time)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trade := order.TradeHistory{
|
||||
Price: val.Price,
|
||||
Amount: val.Vol,
|
||||
@@ -315,7 +310,7 @@ func (k *Kraken) wsProcessOwnTrades(ownOrders interface{}) error {
|
||||
TID: key,
|
||||
Type: oType,
|
||||
Side: oSide,
|
||||
Timestamp: time.Unix(txTime, txTimeNano),
|
||||
Timestamp: convert.TimeFromUnixTimestampDecimal(val.Time),
|
||||
}
|
||||
k.Websocket.DataHandler <- &order.Modify{
|
||||
Exchange: k.Name,
|
||||
@@ -352,10 +347,6 @@ func (k *Kraken) wsProcessOpenOrders(ownOrders interface{}) error {
|
||||
}
|
||||
}
|
||||
if val.Description.Price > 0 {
|
||||
startTime, startTimeNano, err := convert.SplitFloatDecimals(val.StartTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oSide, err := order.StringToOrderSide(val.Description.Type)
|
||||
if err != nil {
|
||||
k.Websocket.DataHandler <- order.ClassificationError{
|
||||
@@ -395,7 +386,7 @@ func (k *Kraken) wsProcessOpenOrders(ownOrders interface{}) error {
|
||||
Side: oSide,
|
||||
Status: oStatus,
|
||||
AssetType: a,
|
||||
Date: time.Unix(startTime, startTimeNano),
|
||||
Date: convert.TimeFromUnixTimestampDecimal(val.OpenTime),
|
||||
Pair: p,
|
||||
}
|
||||
} else {
|
||||
@@ -496,8 +487,6 @@ func (k *Kraken) wsProcessSpread(channelData *WebsocketChannelData, data []inter
|
||||
|
||||
bidVolume := data[3].(string)
|
||||
askVolume := data[4].(string)
|
||||
sec, dec := math.Modf(timeData)
|
||||
spreadTimestamp := time.Unix(int64(sec), int64(dec*(1e9)))
|
||||
if k.Verbose {
|
||||
log.Debugf(log.ExchangeSys,
|
||||
"%v Spread data for '%v' received. Best bid: '%v' Best ask: '%v' Time: '%v', Bid volume '%v', Ask volume '%v'",
|
||||
@@ -505,7 +494,7 @@ func (k *Kraken) wsProcessSpread(channelData *WebsocketChannelData, data []inter
|
||||
channelData.Pair,
|
||||
bestBid,
|
||||
bestAsk,
|
||||
spreadTimestamp,
|
||||
convert.TimeFromUnixTimestampDecimal(timeData),
|
||||
bidVolume,
|
||||
askVolume)
|
||||
}
|
||||
@@ -520,8 +509,6 @@ func (k *Kraken) wsProcessTrades(channelData *WebsocketChannelData, data []inter
|
||||
k.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
sec, dec := math.Modf(timeData)
|
||||
timeUnix := time.Unix(int64(sec), int64(dec*(1e9)))
|
||||
|
||||
price, err := strconv.ParseFloat(trade[0].(string), 64)
|
||||
if err != nil {
|
||||
@@ -544,7 +531,7 @@ func (k *Kraken) wsProcessTrades(channelData *WebsocketChannelData, data []inter
|
||||
Exchange: k.Name,
|
||||
Price: price,
|
||||
Amount: amount,
|
||||
Timestamp: timeUnix,
|
||||
Timestamp: convert.TimeFromUnixTimestampDecimal(timeData),
|
||||
Side: tSide,
|
||||
}
|
||||
}
|
||||
@@ -607,8 +594,7 @@ func (k *Kraken) wsProcessOrderBookPartial(channelData *WebsocketChannelData, as
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sec, dec := math.Modf(timeData)
|
||||
askUpdatedTime := time.Unix(int64(sec), int64(dec*(1e9)))
|
||||
askUpdatedTime := convert.TimeFromUnixTimestampDecimal(timeData)
|
||||
if highestLastUpdate.Before(askUpdatedTime) {
|
||||
highestLastUpdate = askUpdatedTime
|
||||
}
|
||||
@@ -632,8 +618,7 @@ func (k *Kraken) wsProcessOrderBookPartial(channelData *WebsocketChannelData, as
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sec, dec := math.Modf(timeData)
|
||||
bidUpdateTime := time.Unix(int64(sec), int64(dec*(1e9)))
|
||||
bidUpdateTime := convert.TimeFromUnixTimestampDecimal(timeData)
|
||||
if highestLastUpdate.Before(bidUpdateTime) {
|
||||
highestLastUpdate = bidUpdateTime
|
||||
}
|
||||
@@ -682,8 +667,7 @@ func (k *Kraken) wsProcessOrderBookUpdate(channelData *WebsocketChannelData, ask
|
||||
return err
|
||||
}
|
||||
|
||||
sec, dec := math.Modf(timeData)
|
||||
askUpdatedTime := time.Unix(int64(sec), int64(dec*(1e9)))
|
||||
askUpdatedTime := convert.TimeFromUnixTimestampDecimal(timeData)
|
||||
if highestLastUpdate.Before(askUpdatedTime) {
|
||||
highestLastUpdate = askUpdatedTime
|
||||
}
|
||||
@@ -711,8 +695,7 @@ func (k *Kraken) wsProcessOrderBookUpdate(channelData *WebsocketChannelData, ask
|
||||
return err
|
||||
}
|
||||
|
||||
sec, dec := math.Modf(timeData)
|
||||
bidUpdatedTime := time.Unix(int64(sec), int64(dec*(1e9)))
|
||||
bidUpdatedTime := convert.TimeFromUnixTimestampDecimal(timeData)
|
||||
if highestLastUpdate.Before(bidUpdatedTime) {
|
||||
highestLastUpdate = bidUpdatedTime
|
||||
}
|
||||
@@ -737,15 +720,11 @@ func (k *Kraken) wsProcessCandles(channelData *WebsocketChannelData, data []inte
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sec, dec := math.Modf(startTime)
|
||||
startTimeUnix := time.Unix(int64(sec), int64(dec*(1e9)))
|
||||
|
||||
endTime, err := strconv.ParseFloat(data[1].(string), 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sec, dec = math.Modf(endTime)
|
||||
endTimeUnix := time.Unix(int64(sec), int64(dec*(1e9)))
|
||||
|
||||
openPrice, err := strconv.ParseFloat(data[2].(string), 64)
|
||||
if err != nil {
|
||||
@@ -777,8 +756,8 @@ func (k *Kraken) wsProcessCandles(channelData *WebsocketChannelData, data []inte
|
||||
Pair: channelData.Pair,
|
||||
Timestamp: time.Now(),
|
||||
Exchange: k.Name,
|
||||
StartTime: startTimeUnix,
|
||||
CloseTime: endTimeUnix,
|
||||
StartTime: convert.TimeFromUnixTimestampDecimal(startTime),
|
||||
CloseTime: convert.TimeFromUnixTimestampDecimal(endTime),
|
||||
// Candles are sent every 60 seconds
|
||||
Interval: "60",
|
||||
HighPrice: highPrice,
|
||||
|
||||
@@ -150,6 +150,11 @@ func (k *Kraken) Setup(exch *config.ExchangeConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = k.SeedAssets()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = k.Websocket.Setup(
|
||||
&wshandler.WebsocketSetup{
|
||||
Enabled: exch.Features.Enabled.Websocket,
|
||||
@@ -216,7 +221,8 @@ func (k *Kraken) Run() {
|
||||
forceUpdate := false
|
||||
delim := k.GetPairFormat(asset.Spot, false).Delimiter
|
||||
if !common.StringDataContains(k.GetEnabledPairs(asset.Spot).Strings(), delim) ||
|
||||
!common.StringDataContains(k.GetAvailablePairs(asset.Spot).Strings(), delim) {
|
||||
!common.StringDataContains(k.GetAvailablePairs(asset.Spot).Strings(), delim) ||
|
||||
common.StringDataContains(k.GetAvailablePairs(asset.Spot).Strings(), "ZUSD") {
|
||||
enabledPairs := currency.NewPairsFromStrings(
|
||||
[]string{currency.XBT.String() + delim + currency.USD.String()},
|
||||
)
|
||||
@@ -247,6 +253,12 @@ func (k *Kraken) Run() {
|
||||
|
||||
// FetchTradablePairs returns a list of the exchanges tradable pairs
|
||||
func (k *Kraken) FetchTradablePairs(asset asset.Item) ([]string, error) {
|
||||
if !assetTranslator.Seeded() {
|
||||
if err := k.SeedAssets(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
pairs, err := k.GetAssetPairs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -254,21 +266,29 @@ func (k *Kraken) FetchTradablePairs(asset asset.Item) ([]string, error) {
|
||||
|
||||
var products []string
|
||||
for i := range pairs {
|
||||
v := pairs[i]
|
||||
if strings.Contains(v.Altname, ".d") {
|
||||
if strings.Contains(pairs[i].Altname, ".d") {
|
||||
continue
|
||||
}
|
||||
if v.Base[0] == 'X' {
|
||||
if len(v.Base) > 3 {
|
||||
v.Base = v.Base[1:]
|
||||
}
|
||||
|
||||
base := assetTranslator.LookupAltname(pairs[i].Base)
|
||||
if base == "" {
|
||||
log.Warnf(log.ExchangeSys,
|
||||
"%s unable to lookup altname for base currency %s",
|
||||
k.Name,
|
||||
pairs[i].Base)
|
||||
continue
|
||||
}
|
||||
if v.Quote[0] == 'Z' || v.Quote[0] == 'X' {
|
||||
v.Quote = v.Quote[1:]
|
||||
|
||||
quote := assetTranslator.LookupAltname(pairs[i].Quote)
|
||||
if quote == "" {
|
||||
log.Warnf(log.ExchangeSys,
|
||||
"%s unable to lookup altname for quote currency %s",
|
||||
k.Name,
|
||||
pairs[i].Quote)
|
||||
continue
|
||||
}
|
||||
products = append(products, v.Base+
|
||||
k.GetPairFormat(asset, false).Delimiter+
|
||||
v.Quote)
|
||||
products = append(products,
|
||||
base+k.GetPairFormat(asset, false).Delimiter+quote)
|
||||
}
|
||||
return products, nil
|
||||
}
|
||||
@@ -301,8 +321,8 @@ func (k *Kraken) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Pr
|
||||
for c, t := range tickers {
|
||||
pairFmt := k.FormatExchangeCurrency(pairs[i], assetType).String()
|
||||
if !strings.EqualFold(pairFmt, c) {
|
||||
altCurrency, ok := assetPairMap[c]
|
||||
if !ok {
|
||||
altCurrency := assetTranslator.LookupAltname(c)
|
||||
if altCurrency == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.EqualFold(pairFmt, altCurrency) {
|
||||
@@ -389,8 +409,15 @@ func (k *Kraken) UpdateAccountInfo() (account.Holdings, error) {
|
||||
|
||||
var balances []account.Balance
|
||||
for key := range bal {
|
||||
translatedCurrency := assetTranslator.LookupAltname(key)
|
||||
if translatedCurrency == "" {
|
||||
log.Warnf(log.ExchangeSys, "%s unable to translate currency: %s\n",
|
||||
k.Name,
|
||||
key)
|
||||
continue
|
||||
}
|
||||
balances = append(balances, account.Balance{
|
||||
CurrencyName: currency.NewCode(key),
|
||||
CurrencyName: currency.NewCode(translatedCurrency),
|
||||
TotalValue: bal[key],
|
||||
})
|
||||
}
|
||||
@@ -530,10 +557,6 @@ func (k *Kraken) GetOrderInfo(orderID string) (order.Detail, error) {
|
||||
TID: orderInfo.Trades[i],
|
||||
})
|
||||
}
|
||||
firstNum, decNum, err := convert.SplitFloatDecimals(orderInfo.StartTime)
|
||||
if err != nil {
|
||||
return orderDetail, err
|
||||
}
|
||||
side, err := order.StringToOrderSide(orderInfo.Description.Type)
|
||||
if err != nil {
|
||||
return orderDetail, err
|
||||
@@ -548,12 +571,13 @@ func (k *Kraken) GetOrderInfo(orderID string) (order.Detail, error) {
|
||||
}
|
||||
|
||||
orderDetail = order.Detail{
|
||||
Exchange: k.Name,
|
||||
ID: orderID,
|
||||
Pair: currency.NewPairFromString(orderInfo.Description.Pair),
|
||||
Exchange: k.Name,
|
||||
ID: orderID,
|
||||
Pair: currency.NewPairFromFormattedPairs(orderInfo.Description.Pair,
|
||||
k.GetAvailablePairs(asset.Spot), k.GetPairFormat(asset.Spot, true)),
|
||||
Side: side,
|
||||
Type: oType,
|
||||
Date: time.Unix(firstNum, decNum),
|
||||
Date: convert.TimeFromUnixTimestampDecimal(orderInfo.OpenTime),
|
||||
Status: status,
|
||||
Price: orderInfo.Price,
|
||||
Amount: orderInfo.Volume,
|
||||
@@ -647,22 +671,20 @@ func (k *Kraken) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e
|
||||
|
||||
var orders []order.Detail
|
||||
for i := range resp.Open {
|
||||
symbol := currency.NewPairFromString(resp.Open[i].Description.Pair)
|
||||
orderDate := time.Unix(int64(resp.Open[i].StartTime), 0)
|
||||
side := order.Side(strings.ToUpper(resp.Open[i].Description.Type))
|
||||
orderType := order.Type(strings.ToUpper(resp.Open[i].Description.OrderType))
|
||||
|
||||
orders = append(orders, order.Detail{
|
||||
ID: i,
|
||||
Amount: resp.Open[i].Volume,
|
||||
RemainingAmount: (resp.Open[i].Volume - resp.Open[i].VolumeExecuted),
|
||||
ExecutedAmount: resp.Open[i].VolumeExecuted,
|
||||
Exchange: k.Name,
|
||||
Date: orderDate,
|
||||
Date: convert.TimeFromUnixTimestampDecimal(resp.Open[i].OpenTime),
|
||||
Price: resp.Open[i].Description.Price,
|
||||
Side: side,
|
||||
Type: orderType,
|
||||
Pair: symbol,
|
||||
Pair: currency.NewPairFromFormattedPairs(resp.Open[i].Description.Pair,
|
||||
k.GetAvailablePairs(asset.Spot), k.GetPairFormat(asset.Spot, true)),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -690,22 +712,21 @@ func (k *Kraken) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]or
|
||||
|
||||
var orders []order.Detail
|
||||
for i := range resp.Closed {
|
||||
symbol := currency.NewPairFromString(resp.Closed[i].Description.Pair)
|
||||
orderDate := time.Unix(int64(resp.Closed[i].StartTime), 0)
|
||||
side := order.Side(strings.ToUpper(resp.Closed[i].Description.Type))
|
||||
orderType := order.Type(strings.ToUpper(resp.Closed[i].Description.OrderType))
|
||||
|
||||
orders = append(orders, order.Detail{
|
||||
ID: i,
|
||||
Amount: resp.Closed[i].Volume,
|
||||
RemainingAmount: (resp.Closed[i].Volume - resp.Closed[i].VolumeExecuted),
|
||||
ExecutedAmount: resp.Closed[i].VolumeExecuted,
|
||||
Exchange: k.Name,
|
||||
Date: orderDate,
|
||||
Date: convert.TimeFromUnixTimestampDecimal(resp.Closed[i].OpenTime),
|
||||
CloseTime: convert.TimeFromUnixTimestampDecimal(resp.Closed[i].CloseTime),
|
||||
Price: resp.Closed[i].Description.Price,
|
||||
Side: side,
|
||||
Type: orderType,
|
||||
Pair: symbol,
|
||||
Pair: currency.NewPairFromFormattedPairs(resp.Closed[i].Description.Pair,
|
||||
k.GetAvailablePairs(asset.Spot), k.GetPairFormat(asset.Spot, true)),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ type Detail struct {
|
||||
Status Status
|
||||
AssetType asset.Item
|
||||
Date time.Time
|
||||
CloseTime time.Time
|
||||
LastUpdated time.Time
|
||||
Pair currency.Pair
|
||||
Trades []TradeHistory
|
||||
|
||||
@@ -48,6 +48,7 @@ const (
|
||||
poloniexActiveLoans = "returnActiveLoans"
|
||||
poloniexLendingHistory = "returnLendingHistory"
|
||||
poloniexAutoRenew = "toggleAutoRenew"
|
||||
poloniexMaxOrderbookDepth = 100
|
||||
)
|
||||
|
||||
// Poloniex is the overarching type across the poloniex package
|
||||
@@ -96,27 +97,29 @@ func (p *Poloniex) GetOrderbook(currencyPair string, depth int) (OrderbookAll, e
|
||||
if resp.Error != "" {
|
||||
return oba, fmt.Errorf("%s GetOrderbook() error: %s", p.Name, resp.Error)
|
||||
}
|
||||
ob := Orderbook{}
|
||||
var ob Orderbook
|
||||
for x := range resp.Asks {
|
||||
data := resp.Asks[x]
|
||||
price, err := strconv.ParseFloat(data[0].(string), 64)
|
||||
price, err := strconv.ParseFloat(resp.Asks[x][0].(string), 64)
|
||||
if err != nil {
|
||||
return oba, err
|
||||
}
|
||||
amount := data[1].(float64)
|
||||
ob.Asks = append(ob.Asks, OrderbookItem{Price: price, Amount: amount})
|
||||
ob.Asks = append(ob.Asks, OrderbookItem{
|
||||
Price: price,
|
||||
Amount: resp.Asks[x][1].(float64),
|
||||
})
|
||||
}
|
||||
|
||||
for x := range resp.Bids {
|
||||
data := resp.Bids[x]
|
||||
price, err := strconv.ParseFloat(data[0].(string), 64)
|
||||
price, err := strconv.ParseFloat(resp.Bids[x][0].(string), 64)
|
||||
if err != nil {
|
||||
return oba, err
|
||||
}
|
||||
amount := data[1].(float64)
|
||||
ob.Bids = append(ob.Bids, OrderbookItem{Price: price, Amount: amount})
|
||||
ob.Bids = append(ob.Bids, OrderbookItem{
|
||||
Price: price,
|
||||
Amount: resp.Bids[x][1].(float64),
|
||||
})
|
||||
}
|
||||
oba.Data[currencyPair] = Orderbook{Bids: ob.Bids, Asks: ob.Asks}
|
||||
oba.Data[currencyPair] = ob
|
||||
} else {
|
||||
vals.Set("currencyPair", "all")
|
||||
resp := OrderbookResponseAll{}
|
||||
@@ -143,12 +146,12 @@ func (p *Poloniex) GetOrderbook(currencyPair string, depth int) (OrderbookAll, e
|
||||
if err != nil {
|
||||
return oba, err
|
||||
}
|
||||
ob.Asks = append(ob.Asks, OrderbookItem{
|
||||
ob.Bids = append(ob.Bids, OrderbookItem{
|
||||
Price: price,
|
||||
Amount: orderbook.Bids[x][1].(float64),
|
||||
})
|
||||
}
|
||||
oba.Data[currency] = Orderbook{Bids: ob.Bids, Asks: ob.Asks}
|
||||
oba.Data[currency] = ob
|
||||
}
|
||||
}
|
||||
return oba, nil
|
||||
|
||||
@@ -286,10 +286,9 @@ func (p *Poloniex) FetchOrderbook(currencyPair currency.Pair, assetType asset.It
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (p *Poloniex) UpdateOrderbook(currencyPair currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
|
||||
orderBook := new(orderbook.Base)
|
||||
orderbookNew, err := p.GetOrderbook("", 1000)
|
||||
orderbookNew, err := p.GetOrderbook("", poloniexMaxOrderbookDepth)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enabledPairs := p.GetEnabledPairs(assetType)
|
||||
@@ -299,19 +298,20 @@ func (p *Poloniex) UpdateOrderbook(currencyPair currency.Pair, assetType asset.I
|
||||
continue
|
||||
}
|
||||
|
||||
var obItems []orderbook.Item
|
||||
orderBook := new(orderbook.Base)
|
||||
for y := range data.Bids {
|
||||
obItems = append(obItems, orderbook.Item{
|
||||
Amount: data.Bids[y].Amount, Price: data.Bids[y].Price})
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
|
||||
Amount: data.Bids[y].Amount,
|
||||
Price: data.Bids[y].Price,
|
||||
})
|
||||
}
|
||||
orderBook.Bids = obItems
|
||||
|
||||
obItems = []orderbook.Item{}
|
||||
for y := range data.Asks {
|
||||
obItems = append(obItems, orderbook.Item{
|
||||
Amount: data.Asks[y].Amount, Price: data.Asks[y].Price})
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
|
||||
Amount: data.Asks[y].Amount,
|
||||
Price: data.Asks[y].Price,
|
||||
})
|
||||
}
|
||||
orderBook.Asks = obItems
|
||||
orderBook.Pair = enabledPairs[i]
|
||||
orderBook.ExchangeName = p.Name
|
||||
orderBook.AssetType = assetType
|
||||
|
||||
@@ -179,18 +179,17 @@ func (y *Yobit) UpdateTradablePairs(forceUpdate bool) error {
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (y *Yobit) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
|
||||
tickerPrice := new(ticker.Price)
|
||||
pairsCollated, err := y.FormatExchangeCurrencies(y.GetEnabledPairs(assetType), assetType)
|
||||
enabledPairs := y.GetEnabledPairs(assetType)
|
||||
pairsCollated, err := y.FormatExchangeCurrencies(enabledPairs, assetType)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := y.GetTicker(pairsCollated)
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enabledPairs := y.GetEnabledPairs(assetType)
|
||||
for i := range enabledPairs {
|
||||
curr := y.FormatExchangeCurrency(enabledPairs[i], assetType).Lower().String()
|
||||
if _, ok := result[curr]; !ok {
|
||||
|
||||
Reference in New Issue
Block a user