orders: Add derive modify struct method from order.Detail (#948)

* orders: Add derive modify struct method to order.Detail and then subsequent method to derive and standardize response details

* exchanges: call modify method in wrappers

* linter: fixes

* engine/wsroutineman: remove print summary

* glorious: nits, removed modifyOrder functionality for Bithumb. There are not docs to support this.

* Update exchanges/order/orders.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
This commit is contained in:
Ryan O'Hara-Reid
2022-05-30 14:38:21 +10:00
committed by GitHub
parent 293d6104ed
commit a63aa6b616
42 changed files with 298 additions and 427 deletions

View File

@@ -1063,7 +1063,7 @@ func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (b *Binance) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (b *Binance) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -675,35 +675,33 @@ func (b *Bitfinex) SubmitOrder(ctx context.Context, o *order.Submit) (order.Subm
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (b *Bitfinex) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Modify, error) {
func (b *Bitfinex) ModifyOrder(ctx context.Context, action *order.Modify) (*order.ModifyResponse, error) {
if !b.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
return nil, common.ErrNotYetImplemented
}
if err := action.Validate(); err != nil {
return nil, err
}
orderIDInt, err := strconv.ParseInt(action.ID, 10, 64)
if err != nil {
return &order.Modify{ID: action.ID}, err
return &order.ModifyResponse{OrderID: action.ID}, err
}
if b.Websocket.CanUseAuthenticatedWebsocketForWrapper() {
request := WsUpdateOrderRequest{
OrderID: orderIDInt,
Price: action.Price,
Amount: action.Amount,
}
if action.Side == order.Sell && action.Amount > 0 {
request.Amount *= -1
}
err = b.WsModifyOrder(&request)
return &order.Modify{
Exchange: action.Exchange,
AssetType: action.AssetType,
Pair: action.Pair,
ID: action.ID,
Price: action.Price,
Amount: action.Amount,
}, err
wsRequest := WsUpdateOrderRequest{
OrderID: orderIDInt,
Price: action.Price,
Amount: action.Amount,
}
return nil, common.ErrNotYetImplemented
if action.Side == order.Sell && action.Amount > 0 {
wsRequest.Amount *= -1
}
err = b.WsModifyOrder(&wsRequest)
if err != nil {
return nil, err
}
return action.DeriveModifyResponse()
}
// CancelOrder cancels an order by its corresponding ID number

View File

@@ -395,7 +395,7 @@ func (b *Bitflyer) SubmitOrder(_ context.Context, _ *order.Submit) (order.Submit
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (b *Bitflyer) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (b *Bitflyer) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -387,22 +387,6 @@ func (b *Bithumb) PlaceTrade(ctx context.Context, orderCurrency, transactionType
b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, privatePlaceTrade, params, &response)
}
// ModifyTrade modifies an order already on the exchange books
func (b *Bithumb) ModifyTrade(ctx context.Context, orderID, orderCurrency, transactionType string, units float64, price int64) (OrderPlace, error) {
response := OrderPlace{}
params := url.Values{}
params.Set("order_currency", strings.ToUpper(orderCurrency))
params.Set("payment_currency", "KRW")
params.Set("type", strings.ToUpper(transactionType))
params.Set("units", strconv.FormatFloat(units, 'f', -1, 64))
params.Set("price", strconv.FormatInt(price, 10))
params.Set("order_id", orderID)
return response,
b.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, privatePlaceTrade, params, &response)
}
// GetOrderDetails returns specific order details
//
// orderID: Order number registered for purchase/sales

View File

@@ -89,7 +89,6 @@ func (b *Bithumb) SetDefaults() {
GetOrder: true,
CancelOrder: true,
SubmitOrder: true,
ModifyOrder: true,
DepositHistory: true,
WithdrawalHistory: true,
UserTradeHistory: true,
@@ -512,30 +511,8 @@ func (b *Bithumb) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (b *Bithumb) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Modify, error) {
if err := action.Validate(); err != nil {
return nil, err
}
o, err := b.ModifyTrade(ctx,
action.ID,
action.Pair.Base.String(),
action.Side.Lower(),
action.Amount,
int64(action.Price))
if err != nil {
return nil, err
}
return &order.Modify{
Exchange: action.Exchange,
AssetType: action.AssetType,
Pair: action.Pair,
ID: o.Data[0].ContID,
Price: float64(int64(action.Price)),
Amount: action.Amount,
Side: action.Side,
}, nil
func (b *Bithumb) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}
// CancelOrder cancels an order by its corresponding ID number

View File

@@ -313,7 +313,7 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error {
Err: err,
}
}
b.Websocket.DataHandler <- &order.Modify{
b.Websocket.DataHandler <- &order.Detail{
Exchange: b.Name,
ID: response.Data[i].OrderID,
AccountID: strconv.FormatInt(response.Data[i].Account, 10),
@@ -424,7 +424,7 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error {
Err: err,
}
}
b.Websocket.DataHandler <- &order.Modify{
b.Websocket.DataHandler <- &order.Detail{
Price: response.Data[x].Price,
Amount: response.Data[x].OrderQuantity,
Exchange: b.Name,

View File

@@ -609,34 +609,32 @@ func (b *Bitmex) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (b *Bitmex) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Modify, error) {
func (b *Bitmex) ModifyOrder(ctx context.Context, action *order.Modify) (*order.ModifyResponse, error) {
if err := action.Validate(); err != nil {
return nil, err
}
var params OrderAmendParams
if math.Mod(action.Amount, 1) != 0 {
return nil, errors.New("contract amount can not have decimals")
}
params.OrderID = action.ID
params.OrderQty = int32(action.Amount)
params.Price = action.Price
o, err := b.AmendOrder(ctx, &params)
o, err := b.AmendOrder(ctx, &OrderAmendParams{
OrderID: action.ID,
OrderQty: int32(action.Amount),
Price: action.Price})
if err != nil {
return nil, err
}
return &order.Modify{
Exchange: action.Exchange,
AssetType: action.AssetType,
Pair: action.Pair,
ID: o.OrderID,
Price: action.Price,
Amount: float64(params.OrderQty),
}, nil
resp, err := action.DeriveModifyResponse()
if err != nil {
return nil, err
}
resp.OrderID = o.OrderID
resp.RemainingAmount = o.OrderQty
resp.LastUpdated = o.TransactTime
return resp, nil
}
// CancelOrder cancels an order by its corresponding ID number

View File

@@ -561,7 +561,7 @@ func (b *Bitstamp) SubmitOrder(ctx context.Context, s *order.Submit) (order.Subm
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (b *Bitstamp) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (b *Bitstamp) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -615,7 +615,7 @@ func (b *Bittrex) WsProcessUpdateOrder(data *OrderUpdateMessage) error {
}
}
b.Websocket.DataHandler <- &order.Modify{
b.Websocket.DataHandler <- &order.Detail{
ImmediateOrCancel: data.Delta.TimeInForce == string(ImmediateOrCancel),
FillOrKill: data.Delta.TimeInForce == string(GoodTilCancelled),
PostOnly: data.Delta.TimeInForce == string(PostOnlyGoodTilCancelled),

View File

@@ -587,7 +587,7 @@ func (b *Bittrex) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (b *Bittrex) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (b *Bittrex) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -580,7 +580,7 @@ func (b *BTCMarkets) SubmitOrder(ctx context.Context, s *order.Submit) (order.Su
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (b *BTCMarkets) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Modify, error) {
func (b *BTCMarkets) ModifyOrder(ctx context.Context, action *order.Modify) (*order.ModifyResponse, error) {
if err := action.Validate(); err != nil {
return nil, err
}
@@ -588,33 +588,32 @@ func (b *BTCMarkets) ModifyOrder(ctx context.Context, action *order.Modify) (*or
if err != nil {
return nil, err
}
pair, err := currency.NewPairFromString(resp.MarketID)
mod, err := action.DeriveModifyResponse()
if err != nil {
return nil, err
}
side, err := order.StringToOrderSide(resp.Side)
mod.Pair, err = currency.NewPairFromString(resp.MarketID)
if err != nil {
return nil, err
}
orderT, err := order.StringToOrderType(resp.Type)
mod.Side, err = order.StringToOrderSide(resp.Side)
if err != nil {
return nil, err
}
status, err := order.StringToOrderStatus(resp.Status)
mod.Type, err = order.StringToOrderType(resp.Type)
if err != nil {
return nil, err
}
return &order.Modify{
ID: resp.OrderID,
Pair: pair,
Side: side,
Type: orderT,
Date: resp.CreationTime,
Price: resp.Price,
Amount: resp.Amount,
RemainingAmount: resp.OpenAmount,
Status: status,
}, nil
mod.Status, err = order.StringToOrderStatus(resp.Status)
if err != nil {
return nil, err
}
mod.OrderID = resp.OrderID
mod.LastUpdated = resp.CreationTime
mod.Price = resp.Price
mod.Amount = resp.Amount
mod.RemainingAmount = resp.OpenAmount
return mod, nil
}
// CancelOrder cancels an order by its corresponding ID number

View File

@@ -551,7 +551,7 @@ func (b *BTSE) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitRe
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (b *BTSE) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (b *BTSE) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -578,7 +578,7 @@ func (c *CoinbasePro) SubmitOrder(ctx context.Context, s *order.Submit) (order.S
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (c *CoinbasePro) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (c *CoinbasePro) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -194,7 +194,7 @@ func (c *COINUT) wsHandleData(ctx context.Context, respRaw []byte) error {
if err != nil {
return err
}
c.Websocket.DataHandler <- &order.Modify{
c.Websocket.DataHandler <- &order.Detail{
Exchange: c.Name,
ID: strconv.FormatInt(cancel.OrderID, 10),
Status: order.Cancelled,
@@ -208,7 +208,7 @@ func (c *COINUT) wsHandleData(ctx context.Context, respRaw []byte) error {
return err
}
for i := range cancels.Results {
c.Websocket.DataHandler <- &order.Modify{
c.Websocket.DataHandler <- &order.Detail{
Exchange: c.Name,
ID: strconv.FormatInt(cancels.Results[i].OrderID, 10),
Status: order.Cancelled,

View File

@@ -679,7 +679,7 @@ func (c *COINUT) SubmitOrder(ctx context.Context, o *order.Submit) (order.Submit
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (c *COINUT) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (c *COINUT) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -500,7 +500,7 @@ func (e *EXMO) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitRe
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (e *EXMO) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (e *EXMO) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -682,12 +682,16 @@ func (f *FTX) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitRes
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (f *FTX) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Modify, error) {
func (f *FTX) ModifyOrder(ctx context.Context, action *order.Modify) (*order.ModifyResponse, error) {
if err := action.Validate(); err != nil {
return nil, err
}
if action.TriggerPrice != 0 {
var id string
var remainingAmount float64
switch {
case action.TriggerPrice != 0:
var a TriggerOrderData
a, err := f.ModifyTriggerOrder(ctx,
action.ID,
action.Type.String(),
@@ -698,21 +702,10 @@ func (f *FTX) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Mod
if err != nil {
return nil, err
}
return &order.Modify{
Exchange: action.Exchange,
AssetType: action.AssetType,
Pair: action.Pair,
ID: strconv.FormatInt(a.ID, 10),
Price: action.Price,
Amount: action.Amount,
TriggerPrice: action.TriggerPrice,
Type: action.Type,
}, err
}
var o OrderData
var err error
if action.ID == "" {
o, err = f.ModifyOrderByClientID(ctx,
id = strconv.FormatInt(a.ID, 10)
remainingAmount = a.Size - a.FilledSize
case action.ID == "":
o, err := f.ModifyOrderByClientID(ctx,
action.ClientOrderID,
action.ClientOrderID,
action.Price,
@@ -720,8 +713,10 @@ func (f *FTX) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Mod
if err != nil {
return nil, err
}
} else {
o, err = f.ModifyPlacedOrder(ctx,
id = strconv.FormatInt(o.ID, 10)
remainingAmount = o.RemainingSize
default:
o, err := f.ModifyPlacedOrder(ctx,
action.ID,
action.ClientOrderID,
action.Price,
@@ -729,15 +724,16 @@ func (f *FTX) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Mod
if err != nil {
return nil, err
}
id = strconv.FormatInt(o.ID, 10)
remainingAmount = o.RemainingSize
}
return &order.Modify{
Exchange: action.Exchange,
AssetType: action.AssetType,
Pair: action.Pair,
ID: strconv.FormatInt(o.ID, 10),
Price: action.Price,
Amount: action.Amount,
}, err
resp, err := action.DeriveModifyResponse()
if err != nil {
return nil, err
}
resp.OrderID = id
resp.RemainingAmount = remainingAmount
return resp, nil
}
// CancelOrder cancels an order by its corresponding ID number

View File

@@ -544,7 +544,7 @@ func (g *Gateio) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (g *Gateio) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (g *Gateio) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -573,7 +573,7 @@ func (g *Gemini) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (g *Gemini) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (g *Gemini) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -602,7 +602,7 @@ func (h *HitBTC) SubmitOrder(ctx context.Context, o *order.Submit) (order.Submit
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (h *HitBTC) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (h *HitBTC) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -999,7 +999,7 @@ func (h *HUOBI) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (h *HUOBI) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (h *HUOBI) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -52,7 +52,6 @@ type IBotExchange interface {
GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, accountID, chain string) (*deposit.Address, error)
GetAvailableTransferChains(ctx context.Context, cryptocurrency currency.Code) ([]string, error)
GetWithdrawalsHistory(ctx context.Context, code currency.Code) ([]WithdrawalHistory, error)
WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error)
WithdrawFiatFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error)
WithdrawFiatFundsToInternationalBank(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error)
@@ -93,7 +92,7 @@ type IBotExchange interface {
// OrderManagement defines functionality for order management
type OrderManagement interface {
SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error)
ModifyOrder(ctx context.Context, action *order.Modify) (*order.Modify, error)
ModifyOrder(ctx context.Context, action *order.Modify) (*order.ModifyResponse, error)
CancelOrder(ctx context.Context, o *order.Cancel) error
CancelBatchOrders(ctx context.Context, o []order.Cancel) (order.CancelBatchResponse, error)
CancelAllOrders(ctx context.Context, orders *order.Cancel) (order.CancelAllResponse, error)

View File

@@ -431,7 +431,7 @@ func (i *ItBit) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (i *ItBit) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (i *ItBit) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -593,7 +593,7 @@ func (k *Kraken) wsProcessOpenOrders(ownOrders interface{}) error {
if err != nil {
return err
}
k.Websocket.DataHandler <- &order.Modify{
k.Websocket.DataHandler <- &order.Detail{
Leverage: val.Description.Leverage,
Price: val.Price,
Amount: val.Volume,
@@ -611,7 +611,7 @@ func (k *Kraken) wsProcessOpenOrders(ownOrders interface{}) error {
Pair: p,
}
} else {
k.Websocket.DataHandler <- &order.Modify{
k.Websocket.DataHandler <- &order.Detail{
Exchange: k.Name,
ID: key,
Status: oStatus,

View File

@@ -786,7 +786,7 @@ func (k *Kraken) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (k *Kraken) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (k *Kraken) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -485,7 +485,7 @@ func (l *Lbank) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (l *Lbank) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (l *Lbank) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -445,7 +445,7 @@ func (l *LocalBitcoins) SubmitOrder(ctx context.Context, s *order.Submit) (order
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (l *LocalBitcoins) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (l *LocalBitcoins) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -316,7 +316,7 @@ func (o *OKGroup) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submi
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (o *OKGroup) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (o *OKGroup) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -843,7 +843,6 @@ func BenchmarkStringToOrderStatus(b *testing.B) {
}
func TestUpdateOrderFromModify(t *testing.T) {
var leet = "1337"
od := Detail{ID: "1"}
updated := time.Now()
@@ -852,94 +851,50 @@ func TestUpdateOrderFromModify(t *testing.T) {
t.Fatal(err)
}
om := Modify{
om := ModifyResponse{
ImmediateOrCancel: true,
HiddenOrder: true,
FillOrKill: true,
PostOnly: true,
Leverage: 1.0,
Price: 1,
Amount: 1,
LimitPriceUpper: 1,
LimitPriceLower: 1,
TriggerPrice: 1,
QuoteAmount: 1,
ExecutedAmount: 1,
RemainingAmount: 1,
Fee: 1,
Exchange: "1",
InternalOrderID: "1",
ID: "1",
AccountID: "1",
ClientID: "1",
WalletAddress: "1",
Type: 1,
Side: 1,
Status: 1,
AssetType: 1,
LastUpdated: updated,
Pair: pair,
Trades: []TradeHistory{},
}
od.UpdateOrderFromModify(&om)
od.UpdateOrderFromModifyResponse(&om)
if od.InternalOrderID == "1" {
t.Error("Should not be able to update the internal order ID")
}
if !od.ImmediateOrCancel {
t.Error("Failed to update")
}
if !od.HiddenOrder {
t.Error("Failed to update")
}
if !od.FillOrKill {
t.Error("Failed to update")
}
if !od.PostOnly {
t.Error("Failed to update")
}
if od.Leverage != 1 {
t.Error("Failed to update")
}
if od.Price != 1 {
t.Error("Failed to update")
}
if od.Amount != 1 {
t.Error("Failed to update")
}
if od.LimitPriceLower != 1 {
t.Error("Failed to update")
}
if od.LimitPriceUpper != 1 {
t.Error("Failed to update")
}
if od.TriggerPrice != 1 {
t.Error("Failed to update")
}
if od.QuoteAmount != 1 {
t.Error("Failed to update")
}
if od.ExecutedAmount != 1 {
t.Error("Failed to update")
}
if od.RemainingAmount != 1 {
t.Error("Failed to update")
}
if od.Fee != 1 {
t.Error("Failed to update")
}
if od.Exchange != "" {
t.Error("Should not be able to update exchange via modify")
}
if od.ID != "1" {
t.Error("Failed to update")
}
if od.ClientID != "1" {
t.Error("Failed to update")
}
if od.WalletAddress != "1" {
t.Error("Failed to update")
}
if od.Type != 1 {
t.Error("Failed to update")
}
@@ -961,49 +916,6 @@ func TestUpdateOrderFromModify(t *testing.T) {
if od.Trades != nil {
t.Error("Failed to update")
}
om.Trades = append(om.Trades, TradeHistory{TID: "1"}, TradeHistory{TID: "2"})
od.UpdateOrderFromModify(&om)
if len(od.Trades) != 2 {
t.Error("Failed to add trades")
}
om.Trades[0].Exchange = leet
om.Trades[0].Price = 1337
om.Trades[0].Fee = 1337
om.Trades[0].IsMaker = true
om.Trades[0].Timestamp = updated
om.Trades[0].Description = leet
om.Trades[0].Side = UnknownSide
om.Trades[0].Type = UnknownType
om.Trades[0].Amount = 1337
od.UpdateOrderFromModify(&om)
if od.Trades[0].Exchange == leet {
t.Error("Should not be able to update exchange from update")
}
if od.Trades[0].Price != 1337 {
t.Error("Failed to update trades")
}
if od.Trades[0].Fee != 1337 {
t.Error("Failed to update trades")
}
if !od.Trades[0].IsMaker {
t.Error("Failed to update trades")
}
if od.Trades[0].Timestamp != updated {
t.Error("Failed to update trades")
}
if od.Trades[0].Description != leet {
t.Error("Failed to update trades")
}
if od.Trades[0].Side != UnknownSide {
t.Error("Failed to update trades")
}
if od.Trades[0].Type != UnknownType {
t.Error("Failed to update trades")
}
if od.Trades[0].Amount != 1337 {
t.Error("Failed to update trades")
}
}
func TestUpdateOrderFromDetail(t *testing.T) {
@@ -1635,6 +1547,84 @@ func TestDetail_CopyPointerOrderSlice(t *testing.T) {
}
}
func TestDeriveModify(t *testing.T) {
t.Parallel()
var o *Detail
if _, err := o.DeriveModify(); !errors.Is(err, errOrderDetailIsNil) {
t.Fatalf("received: '%v' but expected: '%v'", err, errOrderDetailIsNil)
}
pair := currency.NewPair(currency.BTC, currency.AUD)
o = &Detail{
Exchange: "wow",
ID: "wow2",
ClientOrderID: "wow3",
Type: Market,
Side: Long,
AssetType: asset.Futures,
Pair: pair,
}
mod, err := o.DeriveModify()
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
if mod == nil {
t.Fatal("should not be nil")
}
if mod.Exchange != "wow" ||
mod.ID != "wow2" ||
mod.ClientOrderID != "wow3" ||
mod.Type != Market ||
mod.Side != Long ||
mod.AssetType != asset.Futures ||
!mod.Pair.Equal(pair) {
t.Fatal("unexpected values")
}
}
func TestDeriveModifyResponse(t *testing.T) {
t.Parallel()
var mod *Modify
if _, err := mod.DeriveModifyResponse(); !errors.Is(err, errOrderDetailIsNil) {
t.Fatalf("received: '%v' but expected: '%v'", err, errOrderDetailIsNil)
}
pair := currency.NewPair(currency.BTC, currency.AUD)
mod = &Modify{
Exchange: "wow",
ID: "wow2",
ClientOrderID: "wow3",
Type: Market,
Side: Long,
AssetType: asset.Futures,
Pair: pair,
}
modresp, err := mod.DeriveModifyResponse()
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
if modresp == nil {
t.Fatal("should not be nil")
}
if modresp.Exchange != "wow" ||
modresp.OrderID != "wow2" ||
modresp.ClientOrderID != "wow3" ||
modresp.Type != Market ||
modresp.Side != Long ||
modresp.AssetType != asset.Futures ||
!modresp.Pair.Equal(pair) {
t.Fatal("unexpected values")
}
}
func TestDeriveCancel(t *testing.T) {
t.Parallel()
var o *Detail
@@ -1660,7 +1650,6 @@ func TestDeriveCancel(t *testing.T) {
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
if cancel.Exchange != "wow" ||
cancel.ID != "wow1" ||
cancel.AccountID != "wow2" ||
@@ -1671,6 +1660,6 @@ func TestDeriveCancel(t *testing.T) {
cancel.Side != Long ||
!cancel.Pair.Equal(pair) ||
cancel.AssetType != asset.Futures {
t.Fatal("unexpected values")
t.Fatalf("unexpected values %+v", cancel)
}
}

View File

@@ -83,40 +83,47 @@ type SubmitResponse struct {
// Each exchange has their own requirements, so not all fields
// are required to be populated
type Modify struct {
// Order Identifiers
Exchange string
ID string
ClientOrderID string
Type Type
Side Side
AssetType asset.Item
Pair currency.Pair
// Change fields
ImmediateOrCancel bool
HiddenOrder bool
FillOrKill bool
PostOnly bool
Leverage float64
Price float64
Amount float64
LimitPriceUpper float64
LimitPriceLower float64
TriggerPrice float64
QuoteAmount float64
ExecutedAmount float64
RemainingAmount float64
Fee float64
Exchange string
InternalOrderID string
ID string
ClientOrderID string
AccountID string
ClientID string
WalletAddress string
Type Type
Side Side
Status Status
AssetType asset.Item
Date time.Time
LastUpdated time.Time
Pair currency.Pair
Trades []TradeHistory
}
// ModifyResponse is an order modifying return type
type ModifyResponse struct {
OrderID string
// Order Identifiers
Exchange string
OrderID string
ClientOrderID string
Pair currency.Pair
Type Type
Side Side
Status Status
AssetType asset.Item
// Fields that will be copied over from Modify
ImmediateOrCancel bool
PostOnly bool
Price float64
Amount float64
TriggerPrice float64
// Fields that need to be handled in scope after DeriveModifyResponse()
// if applicable
RemainingAmount float64
Date time.Time
LastUpdated time.Time
}
// Detail contains all properties of an order

View File

@@ -242,26 +242,18 @@ func (d *Detail) UpdateOrderFromDetail(m *Detail) {
}
}
// UpdateOrderFromModify Will update an order detail (used in order management)
// UpdateOrderFromModifyResponse Will update an order detail (used in order management)
// by comparing passed in and existing values
func (d *Detail) UpdateOrderFromModify(m *Modify) {
func (d *Detail) UpdateOrderFromModifyResponse(m *ModifyResponse) {
var updated bool
if m.ID != "" && d.ID != m.ID {
d.ID = m.ID
if m.OrderID != "" && d.ID != m.OrderID {
d.ID = m.OrderID
updated = true
}
if d.ImmediateOrCancel != m.ImmediateOrCancel {
d.ImmediateOrCancel = m.ImmediateOrCancel
updated = true
}
if d.HiddenOrder != m.HiddenOrder {
d.HiddenOrder = m.HiddenOrder
updated = true
}
if d.FillOrKill != m.FillOrKill {
d.FillOrKill = m.FillOrKill
updated = true
}
if m.Price > 0 && m.Price != d.Price {
d.Price = m.Price
updated = true
@@ -270,34 +262,10 @@ func (d *Detail) UpdateOrderFromModify(m *Modify) {
d.Amount = m.Amount
updated = true
}
if m.LimitPriceUpper > 0 && m.LimitPriceUpper != d.LimitPriceUpper {
d.LimitPriceUpper = m.LimitPriceUpper
updated = true
}
if m.LimitPriceLower > 0 && m.LimitPriceLower != d.LimitPriceLower {
d.LimitPriceLower = m.LimitPriceLower
updated = true
}
if m.TriggerPrice > 0 && m.TriggerPrice != d.TriggerPrice {
d.TriggerPrice = m.TriggerPrice
updated = true
}
if m.QuoteAmount > 0 && m.QuoteAmount != d.QuoteAmount {
d.QuoteAmount = m.QuoteAmount
updated = true
}
if m.ExecutedAmount > 0 && m.ExecutedAmount != d.ExecutedAmount {
d.ExecutedAmount = m.ExecutedAmount
updated = true
}
if m.Fee > 0 && m.Fee != d.Fee {
d.Fee = m.Fee
updated = true
}
if m.AccountID != "" && m.AccountID != d.AccountID {
d.AccountID = m.AccountID
updated = true
}
if m.PostOnly != d.PostOnly {
d.PostOnly = m.PostOnly
updated = true
@@ -308,18 +276,6 @@ func (d *Detail) UpdateOrderFromModify(m *Modify) {
d.Pair = m.Pair
updated = true
}
if m.Leverage != 0 && m.Leverage != d.Leverage {
d.Leverage = m.Leverage
updated = true
}
if m.ClientID != "" && m.ClientID != d.ClientID {
d.ClientID = m.ClientID
updated = true
}
if m.WalletAddress != "" && m.WalletAddress != d.WalletAddress {
d.WalletAddress = m.WalletAddress
updated = true
}
if m.Type != UnknownType && m.Type != d.Type {
d.Type = m.Type
updated = true
@@ -336,52 +292,6 @@ func (d *Detail) UpdateOrderFromModify(m *Modify) {
d.AssetType = m.AssetType
updated = true
}
for x := range m.Trades {
var found bool
for y := range d.Trades {
if d.Trades[y].TID != m.Trades[x].TID {
continue
}
found = true
if d.Trades[y].Fee != m.Trades[x].Fee {
d.Trades[y].Fee = m.Trades[x].Fee
updated = true
}
if m.Trades[x].Price != 0 && d.Trades[y].Price != m.Trades[x].Price {
d.Trades[y].Price = m.Trades[x].Price
updated = true
}
if d.Trades[y].Side != m.Trades[x].Side {
d.Trades[y].Side = m.Trades[x].Side
updated = true
}
if d.Trades[y].Type != m.Trades[x].Type {
d.Trades[y].Type = m.Trades[x].Type
updated = true
}
if d.Trades[y].Description != m.Trades[x].Description {
d.Trades[y].Description = m.Trades[x].Description
updated = true
}
if m.Trades[x].Amount != 0 && d.Trades[y].Amount != m.Trades[x].Amount {
d.Trades[y].Amount = m.Trades[x].Amount
updated = true
}
if d.Trades[y].Timestamp != m.Trades[x].Timestamp {
d.Trades[y].Timestamp = m.Trades[x].Timestamp
updated = true
}
if d.Trades[y].IsMaker != m.Trades[x].IsMaker {
d.Trades[y].IsMaker = m.Trades[x].IsMaker
updated = true
}
}
if !found {
d.Trades = append(d.Trades, m.Trades[x])
updated = true
}
m.RemainingAmount -= m.Trades[x].Amount
}
if m.RemainingAmount > 0 && m.RemainingAmount != d.RemainingAmount {
d.RemainingAmount = m.RemainingAmount
updated = true
@@ -494,6 +404,47 @@ func CopyPointerOrderSlice(old []*Detail) []*Detail {
return copySlice
}
// DeriveModify populates a modify struct by the managed order details. Note:
// Price, Amount, Trigger price and order execution bools need to be changed
// in scope. This only derives identifiers for ease.
func (d *Detail) DeriveModify() (*Modify, error) {
if d == nil {
return nil, errOrderDetailIsNil
}
return &Modify{
Exchange: d.Exchange,
ID: d.ID,
ClientOrderID: d.ClientOrderID,
Type: d.Type,
Side: d.Side,
AssetType: d.AssetType,
Pair: d.Pair,
}, nil
}
// DeriveModifyResponse populates a modify response with its identifiers for
// cross exchange standard. NOTE: New OrderID and/or ClientOrderID plus any
// changes *might* need to be populated in scope.
func (m *Modify) DeriveModifyResponse() (*ModifyResponse, error) {
if m == nil {
return nil, errOrderDetailIsNil
}
return &ModifyResponse{
Exchange: m.Exchange,
OrderID: m.ID,
ClientOrderID: m.ClientOrderID,
Type: m.Type,
Side: m.Side,
AssetType: m.AssetType,
Pair: m.Pair,
ImmediateOrCancel: m.ImmediateOrCancel,
PostOnly: m.PostOnly,
Price: m.Price,
Amount: m.Amount,
TriggerPrice: m.TriggerPrice,
}, nil
}
// DeriveCancel populates a cancel struct by the managed order details
func (d *Detail) DeriveCancel() (*Cancel, error) {
if d == nil {

View File

@@ -822,7 +822,7 @@ func (p *Poloniex) processAccountOrderUpdate(notification []interface{}) error {
// null returned so ok check is not needed
clientOrderID, _ := notification[4].(string)
p.Websocket.DataHandler <- &order.Modify{
p.Websocket.DataHandler <- &order.Detail{
Exchange: p.Name,
RemainingAmount: cancelledAmount,
Amount: amount + cancelledAmount,
@@ -1048,7 +1048,7 @@ func (p *Poloniex) processAccountTrades(notification []interface{}) error {
return err
}
p.Websocket.DataHandler <- &order.Modify{
p.Websocket.DataHandler <- &order.Detail{
Exchange: p.Name,
ID: strconv.FormatFloat(orderID, 'f', -1, 64),
Fee: totalFee,
@@ -1080,7 +1080,7 @@ func (p *Poloniex) processAccountKilledOrder(notification []interface{}) error {
// null returned so ok check is not needed
clientOrderID, _ := notification[2].(string)
p.Websocket.DataHandler <- &order.Modify{
p.Websocket.DataHandler <- &order.Detail{
Exchange: p.Name,
ID: strconv.FormatFloat(orderID, 'f', -1, 64),
Status: order.Cancelled,

View File

@@ -567,7 +567,7 @@ func (p *Poloniex) SubmitOrder(ctx context.Context, s *order.Submit) (order.Subm
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (p *Poloniex) ModifyOrder(ctx context.Context, action *order.Modify) (*order.Modify, error) {
func (p *Poloniex) ModifyOrder(ctx context.Context, action *order.Modify) (*order.ModifyResponse, error) {
if err := action.Validate(); err != nil {
return nil, err
}
@@ -587,16 +587,12 @@ func (p *Poloniex) ModifyOrder(ctx context.Context, action *order.Modify) (*orde
return nil, err
}
return &order.Modify{
Exchange: action.Exchange,
AssetType: action.AssetType,
Pair: action.Pair,
ID: strconv.FormatInt(resp.OrderNumber, 10),
Price: action.Price,
Amount: action.Amount,
PostOnly: action.PostOnly,
ImmediateOrCancel: action.ImmediateOrCancel,
}, nil
modResp, err := action.DeriveModifyResponse()
if err != nil {
return nil, err
}
modResp.OrderID = strconv.FormatInt(resp.OrderNumber, 10)
return modResp, nil
}
// CancelOrder cancels an order by its corresponding ID number

View File

@@ -146,7 +146,7 @@ func (c *CustomEx) SubmitOrder(ctx context.Context, s *order.Submit) (order.Subm
return order.SubmitResponse{}, nil
}
func (c *CustomEx) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (c *CustomEx) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, nil
}

View File

@@ -439,7 +439,7 @@ func (y *Yobit) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (y *Yobit) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (y *Yobit) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}

View File

@@ -219,7 +219,7 @@ func (z *ZB) wsHandleData(respRaw []byte) error {
return err
}
z.Websocket.DataHandler <- &order.Modify{
z.Websocket.DataHandler <- &order.Detail{
Exchange: z.Name,
ID: strconv.FormatInt(o.Data.EntrustID, 10),
Pair: p,

View File

@@ -528,7 +528,7 @@ func (z *ZB) SubmitOrder(ctx context.Context, o *order.Submit) (order.SubmitResp
// ModifyOrder will allow of changing orderbook placement and limit to
// market conversion
func (z *ZB) ModifyOrder(_ context.Context, _ *order.Modify) (*order.Modify, error) {
func (z *ZB) ModifyOrder(_ context.Context, _ *order.Modify) (*order.ModifyResponse, error) {
return nil, common.ErrFunctionNotSupported
}