Poloniex: Implementation of GetOrderInfo method (#607)

* GetClosedOrder implemented for Kraken and Binance, fixed Binance MARKET order creaton, added rate, fee and cost fileds on SubmitOrder responce

* return Trades on Binance SubmitOrder, new validation methods on Binance and kraken GetClosedOrderInfo

* removed the Binance extra method GetClosedOrder

* func description corrected

* removed price, fee and cost from SimulateOrder response, as we get all necessary info in response to calculate them on client side

* GetClosedOrder implementation moved to GetOrderInfo

* changed GetOrderInfo params

* removed Canceled order.Type used for Kraken

* update QueryOrder in gctscript

* add missed params to QueryOrder validator (gctscript)

* fixed testing issues

* GetClosedOrder implemented for Kraken and Binance, fixed Binance MARKET order creaton, added rate, fee and cost fileds on SubmitOrder responce

* return Trades on Binance SubmitOrder, new validation methods on Binance and kraken GetClosedOrderInfo

* removed the Binance extra method GetClosedOrder

* func description corrected

* removed price, fee and cost from SimulateOrder response, as we get all necessary info in response to calculate them on client side

* GetClosedOrder implementation moved to GetOrderInfo

* changed GetOrderInfo params

* removed Canceled order.Type used for Kraken

* update QueryOrder in gctscript

* add missed params to QueryOrder validator (gctscript)

* fixed testing issues

* pull previous changes

* linter issues fix

* updated query_order exmple in gctscript, fixed params check

* removed orderPair unnecessary conversion

* added wsCancelAllOrders, fixed bugs

* fixed Kraken wsAddOrder method

* cleanup

* CancelBatchOrders implementation

* changed CancelBatchOrders signature

* fixed tests and wrappers

* btcmarkets_test fix

* cleanup

* cleanup

* changed CancelBatchOrders signature

* fmt

* Update configtest.json

* Update configtest.json

* rollback configtest

* refactored Kraken wsHandleData to allow tests

* removed unnecessary error test in TestWsAddOrderJSON

* dependencies updates

* fixed issue with PortfolioSleepDelay set on startup

* add GetWithdrawalsHistory method to exchanges interface

* param name changes

* add extra params for Binance WithdrawStatus method

* add Binance TestWithdrawHistory

* add GetOrderInfo on Poloniex

* linter errors fix

* switch interface type to avoid panic

* Poloniex has no para errror in OrderbookResponse - removed, added seq param (incrementing sequence) for future use

* linter issues fix

* linter issues fix

* dependencies update

* add tests

* refactored unmarshalling of GetAuthenticatedOrderStatus response

* test fix

* linter issues fix

* unmarshaling logic moved to GetAuthenticatedOrderStatus

* forced Status setting on GetAuthenticatedOrderStatus error

* comment edited

Co-authored-by: Vazha Bezhanishvili <vazha.bezhanishvili@elegro.eu>
This commit is contained in:
Vazha
2020-12-15 00:54:17 +02:00
committed by GitHub
parent ddd19ab6d9
commit 622e5dc8c8
11 changed files with 1801 additions and 1388 deletions

View File

@@ -196,6 +196,7 @@ type TradeHistory struct {
Timestamp time.Time
IsMaker bool
FeeAsset string
Total float64
}
// GetOrdersRequest used for GetOrderHistory and GetOpenOrders wrapper functions

View File

@@ -29,6 +29,8 @@ const (
poloniexDepositsWithdrawals = "returnDepositsWithdrawals"
poloniexOrders = "returnOpenOrders"
poloniexTradeHistory = "returnTradeHistory"
poloniexOrderTrades = "returnOrderTrades"
poloniexOrderStatus = "returnOrderStatus"
poloniexOrderCancel = "cancelOrder"
poloniexOrderMove = "moveOrder"
poloniexWithdraw = "withdraw"
@@ -418,6 +420,82 @@ func (p *Poloniex) GetAuthenticatedTradeHistory(start, end, limit int64) (Authen
return mainResult, json.Unmarshal(result, &mainResult.Data)
}
// GetAuthenticatedOrderStatus returns the status of a given orderId.
func (p *Poloniex) GetAuthenticatedOrderStatus(orderID string) (o OrderStatusData, err error) {
values := url.Values{}
if orderID == "" {
return o, fmt.Errorf("no orderID passed")
}
values.Set("orderNumber", orderID)
var rawOrderStatus OrderStatus
err = p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexOrderStatus, values, &rawOrderStatus)
if err != nil {
return o, err
}
switch rawOrderStatus.Success {
case 0: // fail
var errMsg GenericResponse
err = json.Unmarshal(rawOrderStatus.Result, &errMsg)
if err != nil {
return o, err
}
return o, fmt.Errorf(errMsg.Error)
case 1: // success
var status map[string]OrderStatusData
err = json.Unmarshal(rawOrderStatus.Result, &status)
if err != nil {
return o, err
}
for _, o = range status {
return o, err
}
}
return o, err
}
// GetAuthenticatedOrderTrades returns all trades involving a given orderId.
func (p *Poloniex) GetAuthenticatedOrderTrades(orderID string) (o []OrderTrade, err error) {
values := url.Values{}
if orderID == "" {
return nil, fmt.Errorf("no orderId passed")
}
values.Set("orderNumber", orderID)
var result json.RawMessage
err = p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexOrderTrades, values, &result)
if err != nil {
return nil, err
}
if len(result) == 0 {
return nil, fmt.Errorf("received unexpected response")
}
switch result[0] {
case '{': // error message received
var resp GenericResponse
err = json.Unmarshal(result, &resp)
if err != nil {
return nil, err
}
if resp.Error != "" {
err = fmt.Errorf(resp.Error)
}
case '[': // data received
err = json.Unmarshal(result, &o)
default:
return nil, fmt.Errorf("received unexpected response")
}
return o, err
}
// PlaceOrder places a new order on the exchange
func (p *Poloniex) PlaceOrder(currency string, rate, amount float64, immediate, fillOrKill, buy bool) (OrderResponse, error) {
result := OrderResponse{}

View File

@@ -2,6 +2,7 @@ package poloniex
import (
"net/http"
"strings"
"testing"
"time"
@@ -248,6 +249,109 @@ func TestGetOrderHistory(t *testing.T) {
}
}
func TestGetOrderStatus(t *testing.T) {
t.Parallel()
tests := []struct {
name string
mock bool
orderID string
errExpected bool
errMsgExpected string
}{
{
name: "correct order ID",
mock: true,
orderID: "96238912841",
errExpected: false,
errMsgExpected: "",
},
{
name: "wrong order ID",
mock: true,
orderID: "96238912842",
errExpected: true,
errMsgExpected: "Order not found",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
if tt.mock != mockTests {
t.Skip()
}
_, err := p.GetAuthenticatedOrderStatus(tt.orderID)
switch {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not get order status: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
if !tt.errExpected {
t.Errorf("Could not mock get order status: %s", err.Error())
} else if !(strings.Contains(err.Error(), tt.errMsgExpected)) {
t.Errorf("Could not mock get order status: %s", err.Error())
}
case mockTests:
if tt.errExpected {
t.Errorf("Mock get order status expect an error '%s', get no error", tt.errMsgExpected)
}
}
})
}
}
func TestGetOrderTrades(t *testing.T) {
t.Parallel()
tests := []struct {
name string
mock bool
orderID string
errExpected bool
errMsgExpected string
}{
{
name: "correct order ID",
mock: true,
orderID: "96238912841",
errExpected: false,
errMsgExpected: "",
},
{
name: "wrong order ID",
mock: true,
orderID: "96238912842",
errExpected: true,
errMsgExpected: "Order not found",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
if tt.mock != mockTests {
t.Skip()
}
_, err := p.GetAuthenticatedOrderTrades(tt.orderID)
switch {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not get order trades: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
if !(tt.errExpected && strings.Contains(err.Error(), tt.errMsgExpected)) {
t.Errorf("Could not mock get order trades: %s", err)
}
}
})
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// ----------------------------------------------------------------------------------------------------------------------------

View File

@@ -1,6 +1,7 @@
package poloniex
import (
"encoding/json"
"time"
"github.com/thrasher-corp/gocryptotrader/currency"
@@ -36,6 +37,7 @@ type OrderbookResponse struct {
Bids [][]interface{} `json:"bids"`
IsFrozen string `json:"isFrozen"`
Error string `json:"error"`
Seq int64 `json:"seq"`
}
// OrderbookItem holds data on an individual item
@@ -66,6 +68,39 @@ type TradeHistory struct {
Total float64 `json:"total,string"`
}
// OrderStatus holds order status data
type OrderStatus struct {
Result json.RawMessage `json:"result"`
Success int64 `json:"success"`
}
// OrderStatusData defines order status details
type OrderStatusData struct {
Pair string `json:"currencyPair"`
Rate float64 `json:"rate,string"`
Amount float64 `json:"amount,string"`
Total float64 `json:"total,string"`
StartingAmount float64 `json:"startingAmount,string"`
Type string `json:"type"`
Status string `json:"status"`
Date string `json:"date"`
Fee float64 `json:"fee,string"`
}
// OrderTrade holds order trade data
type OrderTrade struct {
Status string `json:"status"`
GlobalTradeID int64 `json:"globalTradeID"`
TradeID int64 `json:"tradeID"`
CurrencyPair string `json:"currencyPair"`
Type string `json:"type"`
Rate float64 `json:"rate,string"`
Amount float64 `json:"amount,string"`
Total float64 `json:"total,string"`
Fee float64 `json:"fee,string"`
Date string `json:"date"`
}
// ChartData holds kline data
type ChartData struct {
Date int64 `json:"date"`

View File

@@ -589,8 +589,64 @@ func (p *Poloniex) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, er
// GetOrderInfo returns order information based on order ID
func (p *Poloniex) GetOrderInfo(orderID string, pair currency.Pair, assetType asset.Item) (order.Detail, error) {
var orderDetail order.Detail
return orderDetail, common.ErrNotYetImplemented
orderInfo := order.Detail{
Exchange: p.Name,
Pair: pair,
}
trades, err := p.GetAuthenticatedOrderTrades(orderID)
if err != nil && !strings.Contains(err.Error(), "Order not found") {
return orderInfo, err
}
for i := range trades {
var tradeHistory order.TradeHistory
tradeHistory.Exchange = p.Name
tradeHistory.Side, err = order.StringToOrderSide(trades[i].Type)
if err != nil {
return orderInfo, err
}
tradeHistory.TID = strconv.FormatInt(trades[i].GlobalTradeID, 10)
tradeHistory.Timestamp, err = time.Parse(common.SimpleTimeFormat, trades[i].Date)
if err != nil {
return orderInfo, err
}
tradeHistory.Price = trades[i].Rate
tradeHistory.Amount = trades[i].Amount
tradeHistory.Total = trades[i].Total
tradeHistory.Fee = trades[i].Fee
orderInfo.Trades = append(orderInfo.Trades, tradeHistory)
}
resp, err := p.GetAuthenticatedOrderStatus(orderID)
if err != nil {
if len(orderInfo.Trades) > 0 { // on closed orders return trades only
if strings.Contains(err.Error(), "Order not found") {
orderInfo.Status = order.Closed
}
return orderInfo, nil
}
return orderInfo, err
}
orderInfo.Status, _ = order.StringToOrderStatus(resp.Status)
orderInfo.Price = resp.Rate
orderInfo.Amount = resp.Amount
orderInfo.Cost = resp.Total
orderInfo.Fee = resp.Fee
orderInfo.TargetAmount = resp.StartingAmount
orderInfo.Side, err = order.StringToOrderSide(resp.Type)
if err != nil {
return orderInfo, err
}
orderInfo.Date, err = time.Parse(common.SimpleTimeFormat, resp.Date)
if err != nil {
return orderInfo, err
}
return orderInfo, nil
}
// GetDepositAddress returns a deposit address for a specified currency