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:
Adrian Gallagher
2020-06-03 12:48:36 +10:00
committed by GitHub
parent c437b408ca
commit 8afee0b4b2
41 changed files with 879 additions and 678 deletions

View File

@@ -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 {

View File

@@ -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

View File

@@ -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")
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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")
}
}

View File

@@ -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"):

View File

@@ -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,

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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")
}
}

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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)),
})
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 {