From cdcc9630dea6e49d6b00bc0caf45e12f2616a523 Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Fri, 6 May 2022 12:27:21 +1000 Subject: [PATCH] order: slight optimizations (#917) * order: slight optimizations * orders: add benchmarks, small optimize and change order side to uin8 for comparitive optimizations. * orders: continue to convert string type -> uint * orders/backtester: interim move type to orders package, later can expand or deprecate. * orders: handle errors * orders: optimize filters and remove error returns when its clearly not needed * orders: remove log call * backtester: zero value check * orders/futures: zero value -> flag * linter: fix * linter: more fixes * linters: rides again * glorious: nits * common: Add zero value unix check for time values; also addresses glorious nits * glorious scott: nits Co-authored-by: Ryan O'Hara-Reid --- backtester/common/common_types.go | 14 - backtester/eventhandlers/exchange/exchange.go | 18 +- .../portfolio/holdings/holdings.go | 2 +- .../portfolio/holdings/holdings_test.go | 8 +- .../eventhandlers/portfolio/portfolio.go | 45 +- .../eventhandlers/portfolio/portfolio_test.go | 4 +- .../statistics/currencystatistics.go | 2 +- .../statistics/currencystatistics_test.go | 3 +- .../eventhandlers/statistics/statistics.go | 13 +- .../dollarcostaverage/dollarcostaverage.go | 2 +- .../dollarcostaverage_test.go | 4 +- .../eventhandlers/strategies/rsi/rsi.go | 6 +- .../eventhandlers/strategies/rsi/rsi_test.go | 3 +- .../strategies/top2bottom2/top2bottom2.go | 8 +- .../top2bottom2/top2bottom2_test.go | 12 +- backtester/funding/funding_test.go | 52 +- backtester/report/report.go | 7 +- common/common.go | 5 +- common/common_test.go | 10 + engine/order_manager.go | 6 +- engine/order_manager_test.go | 6 +- engine/rpcserver.go | 30 +- engine/rpcserver_test.go | 2 +- exchanges/alphapoint/alphapoint_wrapper.go | 18 +- exchanges/binance/binance_wrapper.go | 49 +- exchanges/bitfinex/bitfinex_wrapper.go | 52 +- exchanges/bithumb/bithumb_websocket_types.go | 3 +- exchanges/bithumb/bithumb_wrapper.go | 20 +- exchanges/bitmex/bitmex_test.go | 16 + exchanges/bitmex/bitmex_wrapper.go | 55 ++- exchanges/bitstamp/bitstamp_wrapper.go | 20 +- exchanges/bittrex/bittrex_wrapper.go | 59 ++- exchanges/btcmarkets/btcmarkets_test.go | 4 +- exchanges/btcmarkets/btcmarkets_wrapper.go | 7 +- exchanges/btse/btse_wrapper.go | 26 +- exchanges/coinbasepro/coinbasepro_wrapper.go | 42 +- exchanges/coinut/coinut_websocket.go | 4 +- exchanges/coinut/coinut_wrapper.go | 108 ++-- exchanges/exmo/exmo_wrapper.go | 26 +- exchanges/gateio/gateio_wrapper.go | 22 +- exchanges/gemini/gemini_wrapper.go | 26 +- exchanges/hitbtc/hitbtc_wrapper.go | 25 +- exchanges/huobi/huobi_wrapper.go | 14 +- exchanges/itbit/itbit_wrapper.go | 39 +- exchanges/kraken/kraken_wrapper.go | 33 +- exchanges/lbank/lbank_test.go | 24 + exchanges/lbank/lbank_wrapper.go | 85 ++-- .../localbitcoins/localbitcoins_types.go | 2 +- .../localbitcoins/localbitcoins_wrapper.go | 24 +- exchanges/okgroup/okgroup_wrapper.go | 35 +- exchanges/order/futures.go | 4 +- exchanges/order/order_test.go | 426 +++++++++------- exchanges/order/order_types.go | 97 ++-- exchanges/order/orders.go | 462 +++++++++++------- exchanges/poloniex/poloniex_wrapper.go | 24 +- exchanges/trade/trade_test.go | 1 - exchanges/yobit/yobit_wrapper.go | 27 +- exchanges/zb/zb_wrapper.go | 12 +- gctscript/modules/gct/exchange.go | 14 +- gctscript/wrappers/validator/validator.go | 10 +- 60 files changed, 1375 insertions(+), 802 deletions(-) diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 4fabbb33..658afae6 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -12,20 +12,6 @@ import ( ) const ( - // DoNothing is an explicit signal for the backtester to not perform an action - // based upon indicator results - DoNothing order.Side = "DO NOTHING" - // TransferredFunds is a status signal to do nothing - TransferredFunds order.Side = "TRANSFERRED FUNDS" - // CouldNotBuy is flagged when a BUY signal is raised in the strategy/signal phase, but the - // portfolio manager or exchange cannot place an order - CouldNotBuy order.Side = "COULD NOT BUY" - // CouldNotSell is flagged when a SELL signal is raised in the strategy/signal phase, but the - // portfolio manager or exchange cannot place an order - CouldNotSell order.Side = "COULD NOT SELL" - // MissingData is signalled during the strategy/signal phase when data has been identified as missing - // No buy or sell events can occur - MissingData order.Side = "MISSING DATA" // CandleStr is a config readable data type to tell the backtester to retrieve candle data CandleStr = "candle" // TradeStr is a config readable data type to tell the backtester to retrieve trade data diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 3b99f00b..b9e01d2b 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -77,11 +77,11 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * if err != nil { switch f.GetDirection() { case gctorder.Buy: - f.SetDirection(common.CouldNotBuy) + f.SetDirection(gctorder.CouldNotBuy) case gctorder.Sell: - f.SetDirection(common.CouldNotSell) + f.SetDirection(gctorder.CouldNotSell) default: - f.SetDirection(common.DoNothing) + f.SetDirection(gctorder.DoNothing) } f.AppendReason(err.Error()) return f, err @@ -117,9 +117,9 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.AppendReason(fundErr.Error()) } if f.GetDirection() == gctorder.Buy { - f.SetDirection(common.CouldNotBuy) + f.SetDirection(gctorder.CouldNotBuy) } else if f.GetDirection() == gctorder.Sell { - f.SetDirection(common.CouldNotSell) + f.SetDirection(gctorder.CouldNotSell) } return f, err } @@ -138,7 +138,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * funds.IncreaseAvailable(limitReducedAmount.Mul(adjustedPrice), f.GetDirection()) } - ords := orderManager.GetOrdersSnapshot("") + ords := orderManager.GetOrdersSnapshot(gctorder.UnknownStatus) for i := range ords { if ords[i].ID != orderID { continue @@ -172,13 +172,13 @@ func verifyOrderWithinLimits(f *fill.Fill, limitReducedAmount decimal.Decimal, c switch f.GetDirection() { case gctorder.Buy: minMax = cs.BuySide - direction = common.CouldNotBuy + direction = gctorder.CouldNotBuy case gctorder.Sell: minMax = cs.SellSide - direction = common.CouldNotSell + direction = gctorder.CouldNotSell default: direction = f.GetDirection() - f.SetDirection(common.DoNothing) + f.SetDirection(gctorder.DoNothing) return fmt.Errorf("%w: %v", errInvalidDirection, direction) } var minOrMax, belowExceed string diff --git a/backtester/eventhandlers/portfolio/holdings/holdings.go b/backtester/eventhandlers/portfolio/holdings/holdings.go index af44b095..e3eecf10 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings.go @@ -73,7 +73,7 @@ func (h *Holding) update(e fill.Event, f funding.IPairReader) { case order.Sell: h.SoldAmount = h.SoldAmount.Add(amount) h.SoldValue = h.SoldAmount.Mul(price) - case common.DoNothing, common.CouldNotSell, common.CouldNotBuy, common.MissingData, common.TransferredFunds, "": + case order.DoNothing, order.CouldNotSell, order.CouldNotBuy, order.MissingData, order.TransferredFunds, order.UnknownSide: } } h.TotalValueLostToVolumeSizing = h.TotalValueLostToVolumeSizing.Add(e.GetClosePrice().Sub(e.GetVolumeAdjustedPrice()).Mul(e.GetAmount())) diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index bc6797a9..e1764482 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -17,13 +17,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) -const ( - testExchange = "binance" -) - -var ( - riskFreeRate = decimal.NewFromFloat(0.03) -) +const testExchange = "binance" func pair(t *testing.T) *funding.Pair { t.Helper() diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 8b207657..ee25b2f5 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -101,7 +101,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi }, Direction: ev.GetDirection(), } - if ev.GetDirection() == "" { + if ev.GetDirection() == gctorder.UnknownSide { return o, errInvalidDirection } @@ -114,20 +114,19 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi ev.Pair()) } - if ev.GetDirection() == common.DoNothing || - ev.GetDirection() == common.MissingData || - ev.GetDirection() == common.TransferredFunds || - ev.GetDirection() == "" { + if ev.GetDirection() == gctorder.DoNothing || + ev.GetDirection() == gctorder.MissingData || + ev.GetDirection() == gctorder.TransferredFunds { return o, nil } if !funds.CanPlaceOrder(ev.GetDirection()) { if ev.GetDirection() == gctorder.Sell { o.AppendReason("no holdings to sell") - o.SetDirection(common.CouldNotSell) + o.SetDirection(gctorder.CouldNotSell) } else if ev.GetDirection() == gctorder.Buy { o.AppendReason("not enough funds to buy") - o.SetDirection(common.CouldNotBuy) + o.SetDirection(gctorder.CouldNotBuy) } ev.SetDirection(o.Direction) return o, nil @@ -160,12 +159,12 @@ func (p *Portfolio) evaluateOrder(d common.Directioner, originalOrderSignal, siz originalOrderSignal.AppendReason(err.Error()) switch d.GetDirection() { case gctorder.Buy: - originalOrderSignal.Direction = common.CouldNotBuy + originalOrderSignal.Direction = gctorder.CouldNotBuy case gctorder.Sell: - originalOrderSignal.Direction = common.CouldNotSell - case common.CouldNotBuy, common.CouldNotSell: + originalOrderSignal.Direction = gctorder.CouldNotSell + case gctorder.CouldNotBuy, gctorder.CouldNotSell: default: - originalOrderSignal.Direction = common.DoNothing + originalOrderSignal.Direction = gctorder.DoNothing } d.SetDirection(originalOrderSignal.Direction) return originalOrderSignal, nil @@ -180,11 +179,11 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi originalOrderSignal.AppendReason(err.Error()) switch originalOrderSignal.Direction { case gctorder.Buy: - originalOrderSignal.Direction = common.CouldNotBuy + originalOrderSignal.Direction = gctorder.CouldNotBuy case gctorder.Sell: - originalOrderSignal.Direction = common.CouldNotSell + originalOrderSignal.Direction = gctorder.CouldNotSell default: - originalOrderSignal.Direction = common.DoNothing + originalOrderSignal.Direction = gctorder.DoNothing } d.SetDirection(originalOrderSignal.Direction) return originalOrderSignal @@ -193,11 +192,11 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi if sizedOrder.Amount.IsZero() { switch originalOrderSignal.Direction { case gctorder.Buy: - originalOrderSignal.Direction = common.CouldNotBuy + originalOrderSignal.Direction = gctorder.CouldNotBuy case gctorder.Sell: - originalOrderSignal.Direction = common.CouldNotSell + originalOrderSignal.Direction = gctorder.CouldNotSell default: - originalOrderSignal.Direction = common.DoNothing + originalOrderSignal.Direction = gctorder.DoNothing } d.SetDirection(originalOrderSignal.Direction) originalOrderSignal.AppendReason("sized order to 0") @@ -210,7 +209,7 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi sizedOrder.AllocatedFunds = sizedOrder.Amount.Mul(sizedOrder.Price) } if err != nil { - sizedOrder.Direction = common.DoNothing + sizedOrder.Direction = gctorder.DoNothing sizedOrder.AppendReason(err.Error()) } return sizedOrder @@ -256,11 +255,11 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IPairReader) (*fill.Fi } direction := ev.GetDirection() - if direction == common.DoNothing || - direction == common.CouldNotBuy || - direction == common.CouldNotSell || - direction == common.MissingData || - direction == "" { + if direction == gctorder.DoNothing || + direction == gctorder.CouldNotBuy || + direction == gctorder.CouldNotSell || + direction == gctorder.MissingData || + direction == gctorder.UnknownSide { fe, ok := ev.(*fill.Fill) if !ok { return nil, fmt.Errorf("%w expected fill event", common.ErrInvalidDataType) diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 86d14056..59fde85b 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -508,7 +508,7 @@ func TestOnSignal(t *testing.T) { t.Error("expected issue") } - s.Direction = common.MissingData + s.Direction = gctorder.MissingData _, err = p.OnSignal(s, &exchange.Settings{}, pair) if err != nil { t.Error(err) @@ -533,7 +533,7 @@ func TestOnSignal(t *testing.T) { if err != nil { t.Error(err) } - if resp.Direction != common.CouldNotBuy { + if resp.Direction != gctorder.CouldNotBuy { t.Errorf("expected common.CouldNotBuy, received %v", resp.Direction) } diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index e593b858..9473f409 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -62,7 +62,7 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e if i == 0 { continue } - if c.Events[i].SignalEvent != nil && c.Events[i].SignalEvent.GetDirection() == common.MissingData { + if c.Events[i].SignalEvent != nil && c.Events[i].SignalEvent.GetDirection() == gctorder.MissingData { c.ShowMissingDataWarning = true } if c.Events[i].DataEvent.GetClosePrice().IsZero() || c.Events[i-1].DataEvent.GetClosePrice().IsZero() { diff --git a/backtester/eventhandlers/statistics/currencystatistics_test.go b/backtester/eventhandlers/statistics/currencystatistics_test.go index a04213b5..cb4f8a07 100644 --- a/backtester/eventhandlers/statistics/currencystatistics_test.go +++ b/backtester/eventhandlers/statistics/currencystatistics_test.go @@ -5,7 +5,6 @@ import ( "time" "github.com/shopspring/decimal" - "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/compliance" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/holdings" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" @@ -106,7 +105,7 @@ func TestCalculateResults(t *testing.T) { SignalEvent: &signal.Signal{ Base: even2, ClosePrice: decimal.NewFromInt(1337), - Direction: common.MissingData, + Direction: order.MissingData, }, } diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index 6f8c990f..9bbf3aa7 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -19,6 +19,7 @@ import ( gctmath "github.com/thrasher-corp/gocryptotrader/common/math" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -308,12 +309,12 @@ func (s *Statistic) PrintAllEventsChronologically() { switch { case currencyStatistic.Events[i].FillEvent != nil: direction := currencyStatistic.Events[i].FillEvent.GetDirection() - if direction == common.CouldNotBuy || - direction == common.CouldNotSell || - direction == common.DoNothing || - direction == common.MissingData || - direction == common.TransferredFunds || - direction == "" { + if direction == gctorder.CouldNotBuy || + direction == gctorder.CouldNotSell || + direction == gctorder.DoNothing || + direction == gctorder.MissingData || + direction == gctorder.TransferredFunds || + direction == gctorder.UnknownSide { results = addEventOutputToTime(results, currencyStatistic.Events[i].FillEvent.GetTime(), fmt.Sprintf("%v %v %v %v | Price: $%v - Direction: %v - Reason: %s", currencyStatistic.Events[i].FillEvent.GetTime().Format(gctcommon.SimpleTimeFormat), diff --git a/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage.go b/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage.go index 9fc3b79f..c5f58f11 100644 --- a/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage.go +++ b/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage.go @@ -47,7 +47,7 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, _ portfol } if !d.HasDataAtTime(d.Latest().GetTime()) { - es.SetDirection(common.MissingData) + es.SetDirection(order.MissingData) es.AppendReason(fmt.Sprintf("missing data at %v, cannot perform any actions", d.Latest().GetTime())) return &es, nil } diff --git a/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage_test.go b/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage_test.go index 9a641d1b..22c4ee28 100644 --- a/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage_test.go +++ b/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage_test.go @@ -80,7 +80,7 @@ func TestOnSignal(t *testing.T) { if err != nil { t.Error(err) } - if resp.GetDirection() != common.MissingData { + if resp.GetDirection() != gctorder.MissingData { t.Error("expected missing data") } @@ -162,7 +162,7 @@ func TestOnSignals(t *testing.T) { if len(resp) != 1 { t.Fatal("expected 1 response") } - if resp[0].GetDirection() != common.MissingData { + if resp[0].GetDirection() != gctorder.MissingData { t.Error("expected missing data") } diff --git a/backtester/eventhandlers/strategies/rsi/rsi.go b/backtester/eventhandlers/strategies/rsi/rsi.go index 159acc67..6504c6f0 100644 --- a/backtester/eventhandlers/strategies/rsi/rsi.go +++ b/backtester/eventhandlers/strategies/rsi/rsi.go @@ -59,7 +59,7 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, _ portfol if offset := d.Offset(); offset <= int(s.rsiPeriod.IntPart()) { es.AppendReason("Not enough data for signal generation") - es.SetDirection(common.DoNothing) + es.SetDirection(order.DoNothing) return &es, nil } @@ -72,7 +72,7 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, _ portfol rsi := indicators.RSI(massagedData, int(s.rsiPeriod.IntPart())) latestRSIValue := decimal.NewFromFloat(rsi[len(rsi)-1]) if !d.HasDataAtTime(d.Latest().GetTime()) { - es.SetDirection(common.MissingData) + es.SetDirection(order.MissingData) es.AppendReason(fmt.Sprintf("missing data at %v, cannot perform any actions. RSI %v", d.Latest().GetTime(), latestRSIValue)) return &es, nil } @@ -83,7 +83,7 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, _ portfol case latestRSIValue.LessThanOrEqual(s.rsiLow): es.SetDirection(order.Buy) default: - es.SetDirection(common.DoNothing) + es.SetDirection(order.DoNothing) } es.AppendReason(fmt.Sprintf("RSI at %v", latestRSIValue)) diff --git a/backtester/eventhandlers/strategies/rsi/rsi_test.go b/backtester/eventhandlers/strategies/rsi/rsi_test.go index 377183d1..df7b44a2 100644 --- a/backtester/eventhandlers/strategies/rsi/rsi_test.go +++ b/backtester/eventhandlers/strategies/rsi/rsi_test.go @@ -17,6 +17,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) func TestName(t *testing.T) { @@ -160,7 +161,7 @@ func TestOnSignal(t *testing.T) { if err != nil { t.Error(err) } - if resp.GetDirection() != common.DoNothing { + if resp.GetDirection() != order.DoNothing { t.Error("expected do nothing") } } diff --git a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2.go b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2.go index 5b90ade1..56f4a5be 100644 --- a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2.go +++ b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2.go @@ -107,7 +107,7 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf if offset <= int(s.mfiPeriod.IntPart()) { es.AppendReason("Not enough data for signal generation") - es.SetDirection(common.DoNothing) + es.SetDirection(order.DoNothing) resp = append(resp, &es) continue } @@ -136,13 +136,13 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf mfi := indicators.MFI(massagedHighData, massagedLowData, massagedCloseData, massagedVolumeData, int(s.mfiPeriod.IntPart())) latestMFI := decimal.NewFromFloat(mfi[len(mfi)-1]) if !d[i].HasDataAtTime(d[i].Latest().GetTime()) { - es.SetDirection(common.MissingData) + es.SetDirection(order.MissingData) es.AppendReason(fmt.Sprintf("missing data at %v, cannot perform any actions. MFI %v", d[i].Latest().GetTime(), latestMFI)) resp = append(resp, &es) continue } - es.SetDirection(common.DoNothing) + es.SetDirection(order.DoNothing) es.AppendReason(fmt.Sprintf("MFI at %v", latestMFI)) funds, err := f.GetFundingForEvent(&es) @@ -183,7 +183,7 @@ func (s *Strategy) selectTopAndBottomPerformers(mfiFundEvents []mfiFundEvent, re } } for i := range mfiFundEvents { - if buyingOrSelling && mfiFundEvents[i].event.GetDirection() == common.DoNothing { + if buyingOrSelling && mfiFundEvents[i].event.GetDirection() == order.DoNothing { mfiFundEvents[i].event.AppendReason("MFI was not in the top or bottom two ranks") } resp = append(resp, mfiFundEvents[i].event) diff --git a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go index 8e6b57b4..99b91b53 100644 --- a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go +++ b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go @@ -171,35 +171,35 @@ func TestSelectTopAndBottomPerformers(t *testing.T) { { event: &signal.Signal{ ClosePrice: decimal.NewFromInt(99), - Direction: common.DoNothing, + Direction: order.DoNothing, }, mfi: decimal.NewFromInt(99), }, { event: &signal.Signal{ ClosePrice: decimal.NewFromInt(98), - Direction: common.DoNothing, + Direction: order.DoNothing, }, mfi: decimal.NewFromInt(98), }, { event: &signal.Signal{ ClosePrice: decimal.NewFromInt(1), - Direction: common.DoNothing, + Direction: order.DoNothing, }, mfi: decimal.NewFromInt(1), }, { event: &signal.Signal{ ClosePrice: decimal.NewFromInt(2), - Direction: common.DoNothing, + Direction: order.DoNothing, }, mfi: decimal.NewFromInt(2), }, { event: &signal.Signal{ ClosePrice: decimal.NewFromInt(50), - Direction: common.DoNothing, + Direction: order.DoNothing, }, mfi: decimal.NewFromInt(50), }, @@ -221,7 +221,7 @@ func TestSelectTopAndBottomPerformers(t *testing.T) { if !resp[i].GetPrice().Equal(decimal.NewFromInt(99)) && !resp[i].GetPrice().Equal(decimal.NewFromInt(98)) { t.Error("expected 99 or 98") } - case common.DoNothing: + case order.DoNothing: if !resp[i].GetPrice().Equal(decimal.NewFromInt(50)) { t.Error("expected 50") } diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index 6da76234..66f1fb46 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -11,7 +11,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" - gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) var ( @@ -484,23 +484,23 @@ func TestReservePair(t *testing.T) { baseItem.pairedWith = quoteItem quoteItem.pairedWith = baseItem pairItems := Pair{Base: baseItem, Quote: quoteItem} - err = pairItems.Reserve(decimal.Zero, gctorder.Buy) + err = pairItems.Reserve(decimal.Zero, order.Buy) if !errors.Is(err, errZeroAmountReceived) { t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) } - err = pairItems.Reserve(elite, gctorder.Buy) + err = pairItems.Reserve(elite, order.Buy) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - err = pairItems.Reserve(decimal.Zero, gctorder.Sell) + err = pairItems.Reserve(decimal.Zero, order.Sell) if !errors.Is(err, errZeroAmountReceived) { t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) } - err = pairItems.Reserve(elite, gctorder.Sell) + err = pairItems.Reserve(elite, order.Sell) if !errors.Is(err, errCannotAllocate) { t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) } - err = pairItems.Reserve(elite, common.DoNothing) + err = pairItems.Reserve(elite, order.DoNothing) if !errors.Is(err, errCannotAllocate) { t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) } @@ -519,46 +519,46 @@ func TestReleasePair(t *testing.T) { baseItem.pairedWith = quoteItem quoteItem.pairedWith = baseItem pairItems := Pair{Base: baseItem, Quote: quoteItem} - err = pairItems.Reserve(decimal.Zero, gctorder.Buy) + err = pairItems.Reserve(decimal.Zero, order.Buy) if !errors.Is(err, errZeroAmountReceived) { t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) } - err = pairItems.Reserve(elite, gctorder.Buy) + err = pairItems.Reserve(elite, order.Buy) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - err = pairItems.Reserve(decimal.Zero, gctorder.Sell) + err = pairItems.Reserve(decimal.Zero, order.Sell) if !errors.Is(err, errZeroAmountReceived) { t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) } - err = pairItems.Reserve(elite, gctorder.Sell) + err = pairItems.Reserve(elite, order.Sell) if !errors.Is(err, errCannotAllocate) { t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) } - err = pairItems.Release(decimal.Zero, decimal.Zero, gctorder.Buy) + err = pairItems.Release(decimal.Zero, decimal.Zero, order.Buy) if !errors.Is(err, errZeroAmountReceived) { t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) } - err = pairItems.Release(elite, decimal.Zero, gctorder.Buy) + err = pairItems.Release(elite, decimal.Zero, order.Buy) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - err = pairItems.Release(elite, decimal.Zero, gctorder.Buy) + err = pairItems.Release(elite, decimal.Zero, order.Buy) if !errors.Is(err, errCannotAllocate) { t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) } - err = pairItems.Release(elite, decimal.Zero, common.DoNothing) + err = pairItems.Release(elite, decimal.Zero, order.DoNothing) if !errors.Is(err, errCannotAllocate) { t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) } - err = pairItems.Release(elite, decimal.Zero, gctorder.Sell) + err = pairItems.Release(elite, decimal.Zero, order.Sell) if !errors.Is(err, errCannotAllocate) { t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) } - err = pairItems.Release(decimal.Zero, decimal.Zero, gctorder.Sell) + err = pairItems.Release(decimal.Zero, decimal.Zero, order.Sell) if !errors.Is(err, errZeroAmountReceived) { t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) } @@ -577,25 +577,25 @@ func TestIncreaseAvailablePair(t *testing.T) { baseItem.pairedWith = quoteItem quoteItem.pairedWith = baseItem pairItems := Pair{Base: baseItem, Quote: quoteItem} - pairItems.IncreaseAvailable(decimal.Zero, gctorder.Buy) + pairItems.IncreaseAvailable(decimal.Zero, order.Buy) if !pairItems.Quote.available.Equal(elite) { t.Errorf("received '%v' expected '%v'", elite, pairItems.Quote.available) } - pairItems.IncreaseAvailable(decimal.Zero, gctorder.Sell) + pairItems.IncreaseAvailable(decimal.Zero, order.Sell) if !pairItems.Base.available.Equal(decimal.Zero) { t.Errorf("received '%v' expected '%v'", decimal.Zero, pairItems.Base.available) } - pairItems.IncreaseAvailable(elite.Neg(), gctorder.Sell) + pairItems.IncreaseAvailable(elite.Neg(), order.Sell) if !pairItems.Quote.available.Equal(elite) { t.Errorf("received '%v' expected '%v'", elite, pairItems.Quote.available) } - pairItems.IncreaseAvailable(elite, gctorder.Buy) + pairItems.IncreaseAvailable(elite, order.Buy) if !pairItems.Base.available.Equal(elite) { t.Errorf("received '%v' expected '%v'", elite, pairItems.Base.available) } - pairItems.IncreaseAvailable(elite, common.DoNothing) + pairItems.IncreaseAvailable(elite, order.DoNothing) if !pairItems.Base.available.Equal(elite) { t.Errorf("received '%v' expected '%v'", elite, pairItems.Base.available) } @@ -607,22 +607,22 @@ func TestCanPlaceOrderPair(t *testing.T) { Base: &Item{}, Quote: &Item{}, } - if p.CanPlaceOrder(common.DoNothing) { + if p.CanPlaceOrder(order.DoNothing) { t.Error("expected false") } - if p.CanPlaceOrder(gctorder.Buy) { + if p.CanPlaceOrder(order.Buy) { t.Error("expected false") } - if p.CanPlaceOrder(gctorder.Sell) { + if p.CanPlaceOrder(order.Sell) { t.Error("expected false") } p.Quote.available = decimal.NewFromInt(32) - if !p.CanPlaceOrder(gctorder.Buy) { + if !p.CanPlaceOrder(order.Buy) { t.Error("expected true") } p.Base.available = decimal.NewFromInt(32) - if !p.CanPlaceOrder(gctorder.Sell) { + if !p.CanPlaceOrder(order.Sell) { t.Error("expected true") } } diff --git a/backtester/report/report.go b/backtester/report/report.go index fa65f43a..c50b68d8 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -9,7 +9,6 @@ import ( "time" "github.com/shopspring/decimal" - "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/log" @@ -209,14 +208,14 @@ func (d *Data) enhanceCandles() error { } if !requiresIteration { if statsForCandles.Events[intVal].SignalEvent.GetTime().Equal(d.OriginalCandles[intVal].Candles[j].Time) && - statsForCandles.Events[intVal].SignalEvent.GetDirection() == common.MissingData && + statsForCandles.Events[intVal].SignalEvent.GetDirection() == order.MissingData && len(enhancedKline.Candles) > 0 { enhancedCandle.copyCloseFromPreviousEvent(&enhancedKline) } } else { for k := range statsForCandles.Events { if statsForCandles.Events[k].SignalEvent.GetTime().Equal(d.OriginalCandles[intVal].Candles[j].Time) && - statsForCandles.Events[k].SignalEvent.GetDirection() == common.MissingData && + statsForCandles.Events[k].SignalEvent.GetDirection() == order.MissingData && len(enhancedKline.Candles) > 0 { enhancedCandle.copyCloseFromPreviousEvent(&enhancedKline) } @@ -261,7 +260,7 @@ func (d *DetailedCandle) copyCloseFromPreviousEvent(enhancedKline *DetailedKline d.Colour = "white" d.Position = "aboveBar" d.Shape = "arrowDown" - d.Text = common.MissingData.String() + d.Text = order.MissingData.String() } // UseDarkMode sets whether to use a dark theme by default diff --git a/common/common.go b/common/common.go index 92cb8113..bfb035af 100644 --- a/common/common.go +++ b/common/common.go @@ -57,6 +57,7 @@ var ( errUserAgentInvalid = errors.New("cannot set invalid user agent") errHTTPClientInvalid = errors.New("custom http client cannot be nil") + zeroValueUnix = time.Unix(0, 0) // ErrTypeAssertFailure defines an error when type assertion fails ErrTypeAssertFailure = errors.New("type assert failure") ) @@ -429,10 +430,10 @@ func (e Errors) Unwrap() error { // StartEndTimeCheck provides some basic checks which occur // frequently in the codebase func StartEndTimeCheck(start, end time.Time) error { - if start.IsZero() { + if start.IsZero() || start.Equal(zeroValueUnix) { return fmt.Errorf("start %w", ErrDateUnset) } - if end.IsZero() { + if end.IsZero() || end.Equal(zeroValueUnix) { return fmt.Errorf("end %w", ErrDateUnset) } if start.After(time.Now()) { diff --git a/common/common_test.go b/common/common_test.go index 3c45e5e6..5d9cc17a 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -655,6 +655,16 @@ func TestParseStartEndDate(t *testing.T) { t.Errorf("received %v, expected %v", err, ErrDateUnset) } + err = StartEndTimeCheck(et, zeroValueUnix) + if !errors.Is(err, ErrDateUnset) { + t.Errorf("received %v, expected %v", err, ErrDateUnset) + } + + err = StartEndTimeCheck(zeroValueUnix, et) + if !errors.Is(err, ErrDateUnset) { + t.Errorf("received %v, expected %v", err, ErrDateUnset) + } + err = StartEndTimeCheck(et, et) if !errors.Is(err, ErrStartEqualsEnd) { t.Errorf("received %v, expected %v", err, ErrStartEqualsEnd) diff --git a/engine/order_manager.go b/engine/order_manager.go index 0864fe98..d4b9a217 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -518,9 +518,7 @@ func (m *OrderManager) GetOrdersSnapshot(s order.Status) []order.Detail { var os []order.Detail for _, v := range m.orderStore.Orders { for i := range v { - if s != v[i].Status && - s != order.AnyStatus && - s != "" { + if s != v[i].Status && s != order.AnyStatus && s != order.UnknownStatus { continue } os = append(os, *v[i]) @@ -685,7 +683,7 @@ func (m *OrderManager) processOrders() { Exchange: exchanges[i].GetName(), } orders := m.orderStore.getActiveOrders(filter) - order.FilterOrdersByCurrencies(&orders, pairs) + order.FilterOrdersByPairs(&orders, pairs) requiresProcessing := make(map[string]bool, len(orders)) for x := range orders { requiresProcessing[orders[x].InternalOrderID] = true diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 4d0094c4..c5aff320 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -879,7 +879,7 @@ func TestProcessOrders(t *testing.T) { t.Errorf("Expected 3 result, got: %d", len(res)) } if res[0].Status != order.Active { - t.Errorf("Order 1 should be active, but status is %s", string(res[0].Status)) + t.Errorf("Order 1 should be active, but status is %s", res[0].Status) } // Order2 is not returned by exch.GetActiveOrders() @@ -892,7 +892,7 @@ func TestProcessOrders(t *testing.T) { t.Errorf("Expected 1 result, got: %d", len(res)) } if res[0].Status != order.Cancelled { - t.Errorf("Order 2 should be cancelled, but status is %s", string(res[0].Status)) + t.Errorf("Order 2 should be cancelled, but status is %s", res[0].Status) } // Order3 is returned by exch.GetActiveOrders(), which will say it is active @@ -904,7 +904,7 @@ func TestProcessOrders(t *testing.T) { t.Errorf("Expected 1 result, got: %d", len(res)) } if res[0].Status != order.Active { - t.Errorf("Order 3 should be active, but status is %s", string(res[0].Status)) + t.Errorf("Order 3 should be active, but status is %s", res[0].Status) } } diff --git a/engine/rpcserver.go b/engine/rpcserver.go index f57bed29..abd07427 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -1195,10 +1195,20 @@ func (s *RPCServer) SubmitOrder(ctx context.Context, r *gctrpc.SubmitOrderReques return nil, err } + side, err := order.StringToOrderSide(r.Side) + if err != nil { + return nil, err + } + + oType, err := order.StringToOrderType(r.OrderType) + if err != nil { + return nil, err + } + submission := &order.Submit{ Pair: p, - Side: order.Side(r.Side), - Type: order.Type(r.OrderType), + Side: side, + Type: oType, Amount: r.Amount, Price: r.Price, ClientID: r.ClientId, @@ -1362,12 +1372,18 @@ func (s *RPCServer) CancelOrder(ctx context.Context, r *gctrpc.CancelOrderReques return nil, err } + var side order.Side + side, err = order.StringToOrderSide(r.Side) + if err != nil { + return nil, err + } + err = s.OrderManager.Cancel(ctx, &order.Cancel{ Exchange: r.Exchange, AccountID: r.AccountId, ID: r.OrderId, - Side: order.Side(r.Side), + Side: side, WalletAddress: r.WalletAddress, Pair: p, AssetType: a, @@ -1402,6 +1418,12 @@ func (s *RPCServer) CancelBatchOrders(ctx context.Context, r *gctrpc.CancelBatch return nil, err } + var side order.Side + side, err = order.StringToOrderSide(r.Side) + if err != nil { + return nil, err + } + status := make(map[string]string) orders := strings.Split(r.OrdersId, ",") request := make([]order.Cancel, len(orders)) @@ -1411,7 +1433,7 @@ func (s *RPCServer) CancelBatchOrders(ctx context.Context, r *gctrpc.CancelBatch request[x] = order.Cancel{ AccountID: r.AccountId, ID: orderID, - Side: order.Side(r.Side), + Side: side, WalletAddress: r.WalletAddress, Pair: pair, AssetType: assetType, diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index eaa6a506..00257947 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -1837,7 +1837,7 @@ func TestGetManagedOrders(t *testing.T) { ClientID: "", WalletAddress: "", Type: order.Limit, - Side: "SELL", + Side: order.Sell, Status: order.New, AssetType: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USDT), diff --git a/exchanges/alphapoint/alphapoint_wrapper.go b/exchanges/alphapoint/alphapoint_wrapper.go index c2752c48..2b4f04e4 100644 --- a/exchanges/alphapoint/alphapoint_wrapper.go +++ b/exchanges/alphapoint/alphapoint_wrapper.go @@ -392,17 +392,16 @@ func (a *Alphapoint) GetActiveOrders(ctx context.Context, req *order.GetOrdersRe orderDetail.Side = orderSideMap[resp[x].OpenOrders[y].Side] orderDetail.Date = time.Unix(resp[x].OpenOrders[y].ReceiveTime, 0) orderDetail.Type = orderTypeMap[resp[x].OpenOrders[y].OrderType] - if orderDetail.Type == "" { - orderDetail.Type = order.UnknownType - } - orders = append(orders, orderDetail) } } order.FilterOrdersByType(&orders, req.Type) order.FilterOrdersBySide(&orders, req.Side) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", a.Name, err) + } return orders, nil } @@ -439,17 +438,16 @@ func (a *Alphapoint) GetOrderHistory(ctx context.Context, req *order.GetOrdersRe orderDetail.Side = orderSideMap[resp[x].OpenOrders[y].Side] orderDetail.Date = time.Unix(resp[x].OpenOrders[y].ReceiveTime, 0) orderDetail.Type = orderTypeMap[resp[x].OpenOrders[y].OrderType] - if orderDetail.Type == "" { - orderDetail.Type = order.UnknownType - } - orders = append(orders, orderDetail) } } order.FilterOrdersByType(&orders, req.Type) order.FilterOrdersBySide(&orders, req.Side) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", a.Name, err) + } return orders, nil } diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index 9eee7eeb..076ed547 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -1181,7 +1181,11 @@ func (b *Binance) GetOrderInfo(ctx context.Context, orderID string, pair currenc if err != nil { return respData, err } - orderSide := order.Side(resp.Side) + var side order.Side + side, err = order.StringToOrderSide(resp.Side) + if err != nil { + return respData, err + } status, err := order.StringToOrderStatus(resp.Status) if err != nil { log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) @@ -1196,7 +1200,7 @@ func (b *Binance) GetOrderInfo(ctx context.Context, orderID string, pair currenc Exchange: b.Name, ID: strconv.FormatInt(resp.OrderID, 10), ClientOrderID: resp.ClientOrderID, - Side: orderSide, + Side: side, Type: orderType, Pair: pair, Cost: resp.CummulativeQuoteQty, @@ -1350,8 +1354,16 @@ func (b *Binance) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque return nil, err } for x := range resp { - orderSide := order.Side(strings.ToUpper(resp[x].Side)) - orderType := order.Type(strings.ToUpper(resp[x].Type)) + var side order.Side + side, err = order.StringToOrderSide(resp[x].Side) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + var orderType order.Type + orderType, err = order.StringToOrderType(resp[x].Type) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } orderStatus, err := order.StringToOrderStatus(resp[i].Status) if err != nil { log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) @@ -1362,7 +1374,7 @@ func (b *Binance) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque Exchange: b.Name, ID: strconv.FormatInt(resp[x].OrderID, 10), ClientOrderID: resp[x].ClientOrderID, - Side: orderSide, + Side: side, Type: orderType, Price: resp[x].Price, Status: orderStatus, @@ -1441,10 +1453,13 @@ func (b *Binance) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque return orders, fmt.Errorf("assetType not supported") } } - order.FilterOrdersByCurrencies(&orders, req.Pairs) + order.FilterOrdersByPairs(&orders, req.Pairs) order.FilterOrdersByType(&orders, req.Type) order.FilterOrdersBySide(&orders, req.Side) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err := order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } return orders, nil } @@ -1470,8 +1485,16 @@ func (b *Binance) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque } for i := range resp { - orderSide := order.Side(strings.ToUpper(resp[i].Side)) - orderType := order.Type(strings.ToUpper(resp[i].Type)) + var side order.Side + side, err = order.StringToOrderSide(resp[x].Side) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + var orderType order.Type + orderType, err = order.StringToOrderType(resp[i].Type) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } orderStatus, err := order.StringToOrderStatus(resp[i].Status) if err != nil { log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) @@ -1497,7 +1520,7 @@ func (b *Binance) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque LastUpdated: resp[i].UpdateTime, Exchange: b.Name, ID: strconv.FormatInt(resp[i].OrderID, 10), - Side: orderSide, + Side: side, Type: orderType, Price: resp[i].Price, Pair: req.Pairs[x], @@ -1628,7 +1651,11 @@ func (b *Binance) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque } order.FilterOrdersByType(&orders, req.Type) order.FilterOrdersBySide(&orders, req.Side) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err := order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + return orders, nil } diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index 1e0086a3..a608603f 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -896,15 +896,21 @@ func (b *Bitfinex) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequ orders := make([]order.Detail, len(resp)) for i := range resp { - orderSide := order.Side(strings.ToUpper(resp[i].Side)) - timestamp, err := strconv.ParseFloat(resp[i].Timestamp, 64) + var side order.Side + side, err = order.StringToOrderSide(resp[i].Side) + if err != nil { + return nil, err + } + var timestamp float64 + timestamp, err = strconv.ParseFloat(resp[i].Timestamp, 64) if err != nil { log.Warnf(log.ExchangeSys, "Unable to convert timestamp '%s', leaving blank", resp[i].Timestamp) } - pair, err := currency.NewPairFromString(resp[i].Symbol) + var pair currency.Pair + pair, err = currency.NewPairFromString(resp[i].Symbol) if err != nil { return nil, err } @@ -914,7 +920,7 @@ func (b *Bitfinex) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequ Date: time.Unix(int64(timestamp), 0), Exchange: b.Name, ID: strconv.FormatInt(resp[i].ID, 10), - Side: orderSide, + Side: side, Price: resp[i].Price, RemainingAmount: resp[i].RemainingAmount, Pair: pair, @@ -938,7 +944,10 @@ func (b *Bitfinex) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequ if orderType == "trailing-stop" { orderDetail.Type = order.TrailingStop } else { - orderDetail.Type = order.Type(strings.ToUpper(orderType)) + orderDetail.Type, err = order.StringToOrderType(orderType) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } } orders[i] = orderDetail @@ -946,8 +955,11 @@ func (b *Bitfinex) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequ order.FilterOrdersBySide(&orders, req.Side) order.FilterOrdersByType(&orders, req.Type) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) - order.FilterOrdersByCurrencies(&orders, req.Pairs) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + order.FilterOrdersByPairs(&orders, req.Pairs) return orders, nil } @@ -965,14 +977,20 @@ func (b *Bitfinex) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequ orders := make([]order.Detail, len(resp)) for i := range resp { - orderSide := order.Side(strings.ToUpper(resp[i].Side)) - timestamp, err := strconv.ParseInt(resp[i].Timestamp, 10, 64) + var side order.Side + side, err = order.StringToOrderSide(resp[i].Side) + if err != nil { + return nil, err + } + var timestamp int64 + timestamp, err = strconv.ParseInt(resp[i].Timestamp, 10, 64) if err != nil { log.Warnf(log.ExchangeSys, "Unable to convert timestamp '%v', leaving blank", resp[i].Timestamp) } orderDate := time.Unix(timestamp, 0) - pair, err := currency.NewPairFromString(resp[i].Symbol) + var pair currency.Pair + pair, err = currency.NewPairFromString(resp[i].Symbol) if err != nil { return nil, err } @@ -982,7 +1000,7 @@ func (b *Bitfinex) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequ Date: orderDate, Exchange: b.Name, ID: strconv.FormatInt(resp[i].ID, 10), - Side: orderSide, + Side: side, Price: resp[i].Price, AverageExecutedPrice: resp[i].AverageExecutionPrice, RemainingAmount: resp[i].RemainingAmount, @@ -1008,7 +1026,10 @@ func (b *Bitfinex) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequ if orderType == "trailing-stop" { orderDetail.Type = order.TrailingStop } else { - orderDetail.Type = order.Type(strings.ToUpper(orderType)) + orderDetail.Type, err = order.StringToOrderType(orderType) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } } orders[i] = orderDetail @@ -1016,11 +1037,14 @@ func (b *Bitfinex) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequ order.FilterOrdersBySide(&orders, req.Side) order.FilterOrdersByType(&orders, req.Type) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } for i := range req.Pairs { b.appendOptionalDelimiter(&req.Pairs[i]) } - order.FilterOrdersByCurrencies(&orders, req.Pairs) + order.FilterOrdersByPairs(&orders, req.Pairs) return orders, nil } diff --git a/exchanges/bithumb/bithumb_websocket_types.go b/exchanges/bithumb/bithumb_websocket_types.go index 9ff0ff8c..cb6dacc6 100644 --- a/exchanges/bithumb/bithumb_websocket_types.go +++ b/exchanges/bithumb/bithumb_websocket_types.go @@ -7,7 +7,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) // WsResponse is a generalised response data structure which will defer @@ -48,7 +47,7 @@ type WsOrderbooks struct { // WsOrderbook defines a singular orderbook tranche type WsOrderbook struct { Symbol currency.Pair `json:"symbol"` - OrderSide order.Side `json:"orderType"` + OrderSide string `json:"orderType"` Price float64 `json:"price,string"` Quantity float64 `json:"quantity,string"` Total int32 `json:"total,string"` diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index 0de4e24b..4ac1dc60 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -702,7 +702,8 @@ func (b *Bithumb) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque var orders []order.Detail for x := range req.Pairs { - resp, err := b.GetOrders(ctx, "", "", "1000", "", req.Pairs[x].Base.String()) + var resp Orders + resp, err = b.GetOrders(ctx, "", "", "1000", "", req.Pairs[x].Base.String()) if err != nil { return nil, err } @@ -737,8 +738,11 @@ func (b *Bithumb) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque } order.FilterOrdersBySide(&orders, req.Side) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) - order.FilterOrdersByCurrencies(&orders, req.Pairs) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + order.FilterOrdersByPairs(&orders, req.Pairs) return orders, nil } @@ -760,7 +764,8 @@ func (b *Bithumb) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque var orders []order.Detail for x := range req.Pairs { - resp, err := b.GetOrders(ctx, "", "", "1000", "", req.Pairs[x].Base.String()) + var resp Orders + resp, err = b.GetOrders(ctx, "", "", "1000", "", req.Pairs[x].Base.String()) if err != nil { return nil, err } @@ -795,8 +800,11 @@ func (b *Bithumb) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque } order.FilterOrdersBySide(&orders, req.Side) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) - order.FilterOrdersByCurrencies(&orders, req.Pairs) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + order.FilterOrdersByPairs(&orders, req.Pairs) return orders, nil } diff --git a/exchanges/bitmex/bitmex_test.go b/exchanges/bitmex/bitmex_test.go index cc2e47f8..4a4767b3 100644 --- a/exchanges/bitmex/bitmex_test.go +++ b/exchanges/bitmex/bitmex_test.go @@ -1174,6 +1174,22 @@ func TestCurrencyNormalization(t *testing.T) { } } +func TestGetOrderType(t *testing.T) { + t.Parallel() + if _, err := b.getOrderType(0); !errors.Is(err, order.ErrTypeIsInvalid) { + t.Fatalf("received: '%v' but expected: '%v'", err, order.ErrTypeIsInvalid) + } + + o, err := b.getOrderType(1) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if o != order.Market { + t.Fatal("unexpected value") + } +} + func TestGetActionFromString(t *testing.T) { t.Parallel() _, err := b.GetActionFromString("meow") diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index b385a8f0..00efcfc6 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -767,16 +767,16 @@ func (b *Bitmex) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques orders := make([]order.Detail, len(resp)) for i := range resp { - orderSide := orderSideMap[resp[i].Side] - orderStatus, err := order.StringToOrderStatus(resp[i].OrdStatus) + var orderStatus order.Status + orderStatus, err = order.StringToOrderStatus(resp[i].OrdStatus) if err != nil { log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) } - orderType := orderTypeMap[resp[i].OrdType] - if orderType == "" { - orderType = order.UnknownType + var oType order.Type + oType, err = b.getOrderType(resp[i].OrdType) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) } - orderDetail := order.Detail{ Date: resp[i].Timestamp, Price: resp[i].Price, @@ -785,9 +785,9 @@ func (b *Bitmex) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques RemainingAmount: resp[i].LeavesQty, Exchange: b.Name, ID: resp[i].OrderID, - Side: orderSide, + Side: orderSideMap[resp[i].Side], Status: orderStatus, - Type: orderType, + Type: oType, Pair: currency.NewPairWithDelimiter(resp[i].Symbol, resp[i].SettlCurrency, format.Delimiter), @@ -798,8 +798,11 @@ func (b *Bitmex) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques order.FilterOrdersBySide(&orders, req.Side) order.FilterOrdersByType(&orders, req.Type) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) - order.FilterOrdersByCurrencies(&orders, req.Pairs) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + order.FilterOrdersByPairs(&orders, req.Pairs) return orders, nil } @@ -825,16 +828,20 @@ func (b *Bitmex) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques orders := make([]order.Detail, len(resp)) for i := range resp { orderSide := orderSideMap[resp[i].Side] - orderStatus, err := order.StringToOrderStatus(resp[i].OrdStatus) + var orderStatus order.Status + orderStatus, err = order.StringToOrderStatus(resp[i].OrdStatus) if err != nil { log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) } - orderType := orderTypeMap[resp[i].OrdType] - if orderType == "" { - orderType = order.UnknownType - } + pair := currency.NewPairWithDelimiter(resp[i].Symbol, resp[i].SettlCurrency, format.Delimiter) + var oType order.Type + oType, err = b.getOrderType(resp[i].OrdType) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + orderDetail := order.Detail{ Price: resp[i].Price, AverageExecutedPrice: resp[i].AvgPx, @@ -847,7 +854,7 @@ func (b *Bitmex) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques ID: resp[i].OrderID, Side: orderSide, Status: orderStatus, - Type: orderType, + Type: oType, Pair: pair, } orderDetail.InferCostsAndTimes() @@ -857,8 +864,11 @@ func (b *Bitmex) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques order.FilterOrdersBySide(&orders, req.Side) order.FilterOrdersByType(&orders, req.Type) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) - order.FilterOrdersByCurrencies(&orders, req.Pairs) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + order.FilterOrdersByPairs(&orders, req.Pairs) return orders, nil } @@ -883,3 +893,12 @@ func (b *Bitmex) GetHistoricCandles(ctx context.Context, pair currency.Pair, a a func (b *Bitmex) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) { return kline.Item{}, common.ErrFunctionNotSupported } + +// getOrderType derives an order type from bitmex int representation +func (b *Bitmex) getOrderType(id int64) (order.Type, error) { + o, ok := orderTypeMap[id] + if !ok { + return order.UnknownType, fmt.Errorf("unhandled order type for '%d': %w", id, order.ErrTypeIsInvalid) + } + return o, nil +} diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index 94003ba9..1cf3fbeb 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -730,7 +730,8 @@ func (b *Bitstamp) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequ orderSide = order.Sell } - tm, err := parseTime(resp[i].DateTime) + var tm time.Time + tm, err = parseTime(resp[i].DateTime) if err != nil { log.Errorf(log.ExchangeSys, "%s GetActiveOrders unable to parse time: %s\n", b.Name, err) @@ -760,8 +761,11 @@ func (b *Bitstamp) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequ } } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) - order.FilterOrdersByCurrencies(&orders, req.Pairs) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + order.FilterOrdersByPairs(&orders, req.Pairs) return orders, nil } @@ -829,7 +833,8 @@ func (b *Bitstamp) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequ format.Delimiter) } - tm, err := parseTime(resp[i].Date) + var tm time.Time + tm, err = parseTime(resp[i].Date) if err != nil { log.Errorf(log.ExchangeSys, "%s GetOrderHistory unable to parse time: %s\n", b.Name, err) @@ -843,8 +848,11 @@ func (b *Bitstamp) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequ }) } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) - order.FilterOrdersByCurrencies(&orders, req.Pairs) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + order.FilterOrdersByPairs(&orders, req.Pairs) return orders, nil } diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index 5325f76e..a0f4a0bb 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "sort" - "strings" "sync" "time" @@ -666,10 +665,12 @@ func (b *Bittrex) ConstructOrderDetail(orderData *OrderData) (order.Detail, erro orderData.ID, err) } - orderType := order.Type(strings.ToUpper(orderData.Type)) + orderType, err := order.StringToOrderType(orderData.Type) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } var orderStatus order.Status - switch orderData.Status { case order.Open.String(): switch orderData.FillQuantity { @@ -778,7 +779,8 @@ func (b *Bittrex) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque resp := make([]order.Detail, 0, len(orderData)) for i := range orderData { - pair, err := currency.NewPairDelimiter(orderData[i].MarketSymbol, + var pair currency.Pair + pair, err = currency.NewPairDelimiter(orderData[i].MarketSymbol, format.Delimiter) if err != nil { log.Errorf(log.ExchangeSys, @@ -788,12 +790,17 @@ func (b *Bittrex) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque orderData[i].ID, err) } - orderType := order.Type(strings.ToUpper(orderData[i].Type)) - orderSide, err := order.StringToOrderSide(orderData[i].Direction) + var orderType order.Type + orderType, err = order.StringToOrderType(orderData[i].Type) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + + var orderSide order.Side + orderSide, err = order.StringToOrderSide(orderData[i].Direction) if err != nil { log.Errorf(log.ExchangeSys, "GetActiveOrders - %s - cannot get order side - %s\n", b.Name, err.Error()) - continue } resp = append(resp, order.Detail{ @@ -812,8 +819,11 @@ func (b *Bittrex) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque } order.FilterOrdersByType(&resp, req.Type) - order.FilterOrdersByTimeRange(&resp, req.StartTime, req.EndTime) - order.FilterOrdersByCurrencies(&resp, req.Pairs) + err = order.FilterOrdersByTimeRange(&resp, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + order.FilterOrdersByPairs(&resp, req.Pairs) b.WsSequenceOrders = sequence return resp, nil @@ -836,18 +846,21 @@ func (b *Bittrex) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque var resp []order.Detail for x := range req.Pairs { - formattedPair, err := b.FormatExchangeCurrency(req.Pairs[x], req.AssetType) + var formattedPair currency.Pair + formattedPair, err = b.FormatExchangeCurrency(req.Pairs[x], req.AssetType) if err != nil { return nil, err } - orderData, err := b.GetOrderHistoryForCurrency(ctx, formattedPair.String()) + var orderData []OrderData + orderData, err = b.GetOrderHistoryForCurrency(ctx, formattedPair.String()) if err != nil { return nil, err } for i := range orderData { - pair, err := currency.NewPairDelimiter(orderData[i].MarketSymbol, + var pair currency.Pair + pair, err = currency.NewPairDelimiter(orderData[i].MarketSymbol, format.Delimiter) if err != nil { log.Errorf(log.ExchangeSys, @@ -857,17 +870,22 @@ func (b *Bittrex) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque orderData[i].ID, err) } - orderType := order.Type(strings.ToUpper(orderData[i].Type)) + var orderType order.Type + orderType, err = order.StringToOrderType(orderData[i].Type) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } - orderSide, err := order.StringToOrderSide(orderData[i].Direction) + var orderSide order.Side + orderSide, err = order.StringToOrderSide(orderData[i].Direction) if err != nil { log.Errorf(log.ExchangeSys, "GetActiveOrders - %s - cannot get order side - %s\n", b.Name, err.Error()) - continue } - orderStatus, err := order.StringToOrderStatus(orderData[i].Status) + + var orderStatus order.Status + orderStatus, err = order.StringToOrderStatus(orderData[i].Status) if err != nil { log.Errorf(log.ExchangeSys, "GetActiveOrders - %s - cannot get order status - %s\n", b.Name, err.Error()) - continue } detail := order.Detail{ @@ -890,8 +908,11 @@ func (b *Bittrex) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque } order.FilterOrdersByType(&resp, req.Type) - order.FilterOrdersByTimeRange(&resp, req.StartTime, req.EndTime) - order.FilterOrdersByCurrencies(&resp, req.Pairs) + err = order.FilterOrdersByTimeRange(&resp, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } + order.FilterOrdersByPairs(&resp, req.Pairs) } return resp, nil diff --git a/exchanges/btcmarkets/btcmarkets_test.go b/exchanges/btcmarkets/btcmarkets_test.go index 603f8e65..beb04243 100644 --- a/exchanges/btcmarkets/btcmarkets_test.go +++ b/exchanges/btcmarkets/btcmarkets_test.go @@ -946,7 +946,7 @@ func TestTrim(t *testing.T) { func TestFormatOrderType(t *testing.T) { t.Parallel() - _, err := b.formatOrderType(order.Type("SWOOON")) + _, err := b.formatOrderType(0) if !errors.Is(err, order.ErrTypeIsInvalid) { t.Fatalf("received: '%v' but expected: '%v'", err, order.ErrTypeIsInvalid) } @@ -999,7 +999,7 @@ func TestFormatOrderType(t *testing.T) { func TestFormatOrderSide(t *testing.T) { t.Parallel() - _, err := b.formatOrderSide("invalid") + _, err := b.formatOrderSide(255) if !errors.Is(err, order.ErrSideIsInvalid) { t.Fatalf("received: '%v' but expected: '%v'", err, order.ErrSideIsInvalid) } diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index a98aff34..6c63708c 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -487,7 +487,7 @@ func (b *BTCMarkets) GetRecentTrades(ctx context.Context, p currency.Pair, asset resp := make([]trade.Data, len(tradeData)) for i := range tradeData { - side := order.Side("") + var side order.Side if tradeData[i].Side != "" { side, err = order.StringToOrderSide(tradeData[i].Side) if err != nil { @@ -829,7 +829,10 @@ func (b *BTCMarkets) GetActiveOrders(ctx context.Context, req *order.GetOrdersRe } } order.FilterOrdersByType(&resp, req.Type) - order.FilterOrdersByTimeRange(&resp, req.StartTime, req.EndTime) + err := order.FilterOrdersByTimeRange(&resp, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } order.FilterOrdersBySide(&resp, req.Side) return resp, nil } diff --git a/exchanges/btse/btse_wrapper.go b/exchanges/btse/btse_wrapper.go index 08393923..a7101041 100644 --- a/exchanges/btse/btse_wrapper.go +++ b/exchanges/btse/btse_wrapper.go @@ -684,13 +684,18 @@ func (b *BTSE) GetOrderInfo(ctx context.Context, orderID string, pair currency.P log.Errorf(log.ExchangeSys, "%s GetOrderInfo unable to parse time: %s\n", b.Name, err) } + var orderSide order.Side + orderSide, err = order.StringToOrderSide(th[i].Side) + if err != nil { + return order.Detail{}, err + } od.Trades = append(od.Trades, order.TradeHistory{ Timestamp: createdAt, TID: th[i].TradeID, Price: th[i].Price, Amount: th[i].Size, Exchange: b.Name, - Side: order.Side(th[i].Side), + Side: orderSide, Fee: th[i].FeeAmount, }) } @@ -854,13 +859,18 @@ func (b *BTSE) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest) b.Name, err) } + var orderSide order.Side + orderSide, err = order.StringToOrderSide(fills[i].Side) + if err != nil { + return nil, err + } openOrder.Trades = append(openOrder.Trades, order.TradeHistory{ Timestamp: createdAt, TID: fills[i].TradeID, Price: fills[i].Price, Amount: fills[i].Size, Exchange: b.Name, - Side: order.Side(fills[i].Side), + Side: orderSide, Fee: fills[i].FeeAmount, }) } @@ -869,7 +879,10 @@ func (b *BTSE) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest) } order.FilterOrdersByType(&orders, req.Type) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err := order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } @@ -914,6 +927,11 @@ func (b *BTSE) GetOrderHistory(ctx context.Context, getOrdersRequest *order.GetO if err != nil { log.Errorf(log.ExchangeSys, "%s %v", b.Name, err) } + var orderSide order.Side + orderSide, err = order.StringToOrderSide(currentOrder[y].Side) + if err != nil { + return nil, err + } orderTime := time.UnixMilli(currentOrder[y].Timestamp) tempOrder := order.Detail{ ID: currentOrder[y].OrderID, @@ -925,7 +943,7 @@ func (b *BTSE) GetOrderHistory(ctx context.Context, getOrdersRequest *order.GetO ExecutedAmount: currentOrder[y].FilledSize, RemainingAmount: currentOrder[y].Size - currentOrder[y].FilledSize, Date: orderTime, - Side: order.Side(currentOrder[y].Side), + Side: orderSide, Status: orderStatus, Pair: orderDeref.Pairs[x], } diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index 6b705f84..4292fa1c 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -6,7 +6,6 @@ import ( "fmt" "sort" "strconv" - "strings" "sync" "time" @@ -783,22 +782,33 @@ func (c *CoinbasePro) GetActiveOrders(ctx context.Context, req *order.GetOrdersR if err != nil { return nil, err } - orderSide := order.Side(strings.ToUpper(respOrders[i].Side)) - orderType := order.Type(strings.ToUpper(respOrders[i].Type)) + var side order.Side + side, err = order.StringToOrderSide(respOrders[i].Side) + if err != nil { + return nil, err + } + var orderType order.Type + orderType, err = order.StringToOrderType(respOrders[i].Type) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", c.Name, err) + } orders[i] = order.Detail{ ID: respOrders[i].ID, Amount: respOrders[i].Size, ExecutedAmount: respOrders[i].FilledSize, Type: orderType, Date: respOrders[i].CreatedAt, - Side: orderSide, + Side: side, Pair: curr, Exchange: c.Name, } } order.FilterOrdersByType(&orders, req.Type) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", c.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } @@ -847,12 +857,21 @@ func (c *CoinbasePro) GetOrderHistory(ctx context.Context, req *order.GetOrdersR if err != nil { return nil, err } - orderSide := order.Side(strings.ToUpper(respOrders[i].Side)) - orderStatus, err := order.StringToOrderStatus(respOrders[i].Status) + var side order.Side + side, err = order.StringToOrderSide(respOrders[i].Side) + if err != nil { + return nil, err + } + var orderStatus order.Status + orderStatus, err = order.StringToOrderStatus(respOrders[i].Status) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", c.Name, err) + } + var orderType order.Type + orderType, err = order.StringToOrderType(respOrders[i].Type) if err != nil { log.Errorf(log.ExchangeSys, "%s %v", c.Name, err) } - orderType := order.Type(strings.ToUpper(respOrders[i].Type)) detail := order.Detail{ ID: respOrders[i].ID, Amount: respOrders[i].Size, @@ -865,7 +884,7 @@ func (c *CoinbasePro) GetOrderHistory(ctx context.Context, req *order.GetOrdersR CloseTime: respOrders[i].DoneAt, Fee: respOrders[i].FillFees, FeeAsset: curr.Quote, - Side: orderSide, + Side: side, Status: orderStatus, Pair: curr, Price: respOrders[i].Price, @@ -876,7 +895,10 @@ func (c *CoinbasePro) GetOrderHistory(ctx context.Context, req *order.GetOrdersR } order.FilterOrdersByType(&orders, req.Type) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", c.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } diff --git a/exchanges/coinut/coinut_websocket.go b/exchanges/coinut/coinut_websocket.go index 617b4405..1caba426 100644 --- a/exchanges/coinut/coinut_websocket.go +++ b/exchanges/coinut/coinut_websocket.go @@ -775,7 +775,7 @@ func (c *COINUT) wsSubmitOrder(o *WsSubmitOrderParameters) (*order.Detail, error orderSubmissionRequest.InstrumentID = c.instrumentMap.LookupID(curr.String()) orderSubmissionRequest.Quantity = o.Amount orderSubmissionRequest.Price = o.Price - orderSubmissionRequest.Side = string(o.Side) + orderSubmissionRequest.Side = o.Side.String() if o.OrderID > 0 { orderSubmissionRequest.OrderID = o.OrderID @@ -816,7 +816,7 @@ func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) ([]order.Detai WsSubmitOrdersRequestData{ Quantity: orders[i].Amount, Price: orders[i].Price, - Side: string(orders[i].Side), + Side: orders[i].Side.String(), InstrumentID: c.instrumentMap.LookupID(curr.String()), ClientOrderID: i + 1, }) diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index c303714c..38bad2c7 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -872,11 +872,12 @@ func (c *COINUT) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques var currenciesToCheck []string if len(req.Pairs) == 0 { for i := range req.Pairs { - fpair, err := c.FormatExchangeCurrency(req.Pairs[i], asset.Spot) + var fPair currency.Pair + fPair, err = c.FormatExchangeCurrency(req.Pairs[i], asset.Spot) if err != nil { return nil, err } - currenciesToCheck = append(currenciesToCheck, fpair.String()) + currenciesToCheck = append(currenciesToCheck, fPair.String()) } } else { for k := range c.instrumentMap.Instruments { @@ -885,17 +886,26 @@ func (c *COINUT) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques } if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() { for x := range currenciesToCheck { - openOrders, err := c.wsGetOpenOrders(currenciesToCheck[x]) + var openOrders *WsUserOpenOrdersResponse + openOrders, err = c.wsGetOpenOrders(currenciesToCheck[x]) if err != nil { return nil, err } for i := range openOrders.Orders { - p, err := currency.NewPairFromString(currenciesToCheck[x]) + var p currency.Pair + p, err = currency.NewPairFromString(currenciesToCheck[x]) if err != nil { return nil, err } - fpair, err := c.FormatExchangeCurrency(p, asset.Spot) + var fPair currency.Pair + fPair, err = c.FormatExchangeCurrency(p, asset.Spot) + if err != nil { + return nil, err + } + + var side order.Side + side, err = order.StringToOrderSide(openOrders.Orders[i].Side) if err != nil { return nil, err } @@ -903,8 +913,8 @@ func (c *COINUT) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques orders = append(orders, order.Detail{ Exchange: c.Name, ID: strconv.FormatInt(openOrders.Orders[i].OrderID, 10), - Pair: fpair, - Side: order.Side(openOrders.Orders[i].Side), + Pair: fPair, + Side: side, Date: time.Unix(0, openOrders.Orders[i].Timestamp), Status: order.Active, Price: openOrders.Orders[i].Price, @@ -917,8 +927,8 @@ func (c *COINUT) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques } else { var instrumentsToUse []int64 for x := range req.Pairs { - curr, err := c.FormatExchangeCurrency(req.Pairs[x], - asset.Spot) + var curr currency.Pair + curr, err = c.FormatExchangeCurrency(req.Pairs[x], asset.Spot) if err != nil { return nil, err } @@ -929,46 +939,55 @@ func (c *COINUT) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques instrumentsToUse = c.instrumentMap.GetInstrumentIDs() } - pairs, err := c.GetEnabledPairs(asset.Spot) + var pairs currency.Pairs + pairs, err = c.GetEnabledPairs(asset.Spot) if err != nil { return nil, err } - format, err := c.GetPairFormat(asset.Spot, true) + var format currency.PairFormat + format, err = c.GetPairFormat(asset.Spot, true) if err != nil { return nil, err } for x := range instrumentsToUse { - openOrders, err := c.GetOpenOrders(ctx, instrumentsToUse[x]) + var openOrders GetOpenOrdersResponse + openOrders, err = c.GetOpenOrders(ctx, instrumentsToUse[x]) if err != nil { return nil, err } for y := range openOrders.Orders { curr := c.instrumentMap.LookupInstrument(instrumentsToUse[x]) - p, err := currency.NewPairFromFormattedPairs(curr, - pairs, - format) + var p currency.Pair + p, err = currency.NewPairFromFormattedPairs(curr, pairs, format) + if err != nil { + return nil, err + } + + var side order.Side + side, err = order.StringToOrderSide(openOrders.Orders[y].Side) if err != nil { return nil, err } - orderSide := order.Side(strings.ToUpper(openOrders.Orders[y].Side)) - orderDate := time.Unix(openOrders.Orders[y].Timestamp, 0) orders = append(orders, order.Detail{ ID: strconv.FormatInt(openOrders.Orders[y].OrderID, 10), Amount: openOrders.Orders[y].Quantity, Price: openOrders.Orders[y].Price, Exchange: c.Name, - Side: orderSide, - Date: orderDate, + Side: side, + Date: time.Unix(openOrders.Orders[y].Timestamp, 0), Pair: p, }) } } } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", c.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } @@ -988,13 +1007,21 @@ func (c *COINUT) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() { for i := range req.Pairs { for j := int64(0); ; j += 100 { - trades, err := c.wsGetTradeHistory(req.Pairs[i], j, 100) + var trades *WsTradeHistoryResponse + trades, err = c.wsGetTradeHistory(req.Pairs[i], j, 100) if err != nil { return allOrders, err } for x := range trades.Trades { curr := c.instrumentMap.LookupInstrument(trades.Trades[x].InstrumentID) - p, err := currency.NewPairFromString(curr) + var p currency.Pair + p, err = currency.NewPairFromString(curr) + if err != nil { + return nil, err + } + + var side order.Side + side, err = order.StringToOrderSide(trades.Trades[x].Side) if err != nil { return nil, err } @@ -1003,7 +1030,7 @@ func (c *COINUT) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques Exchange: c.Name, ID: strconv.FormatInt(trades.Trades[x].OrderID, 10), Pair: p, - Side: order.Side(trades.Trades[x].Side), + Side: side, Date: time.Unix(0, trades.Trades[x].Timestamp), Status: order.Filled, Price: trades.Trades[x].Price, @@ -1022,8 +1049,8 @@ func (c *COINUT) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques } else { var instrumentsToUse []int64 for x := range req.Pairs { - curr, err := c.FormatExchangeCurrency(req.Pairs[x], - asset.Spot) + var curr currency.Pair + curr, err = c.FormatExchangeCurrency(req.Pairs[x], asset.Spot) if err != nil { return nil, err } @@ -1037,46 +1064,55 @@ func (c *COINUT) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques instrumentsToUse = c.instrumentMap.GetInstrumentIDs() } - pairs, err := c.GetEnabledPairs(asset.Spot) + var pairs currency.Pairs + pairs, err = c.GetEnabledPairs(asset.Spot) if err != nil { return nil, err } - format, err := c.GetPairFormat(asset.Spot, true) + var format currency.PairFormat + format, err = c.GetPairFormat(asset.Spot, true) if err != nil { return nil, err } for x := range instrumentsToUse { - orders, err := c.GetTradeHistory(ctx, instrumentsToUse[x], -1, -1) + var orders TradeHistory + orders, err = c.GetTradeHistory(ctx, instrumentsToUse[x], -1, -1) if err != nil { return nil, err } for y := range orders.Trades { curr := c.instrumentMap.LookupInstrument(instrumentsToUse[x]) - p, err := currency.NewPairFromFormattedPairs(curr, - pairs, - format) + var p currency.Pair + p, err = currency.NewPairFromFormattedPairs(curr, pairs, format) + if err != nil { + return nil, err + } + + var side order.Side + side, err = order.StringToOrderSide(orders.Trades[y].Order.Side) if err != nil { return nil, err } - orderSide := order.Side(strings.ToUpper(orders.Trades[y].Order.Side)) - orderDate := time.Unix(orders.Trades[y].Order.Timestamp, 0) allOrders = append(allOrders, order.Detail{ ID: strconv.FormatInt(orders.Trades[y].Order.OrderID, 10), Amount: orders.Trades[y].Order.Quantity, Price: orders.Trades[y].Order.Price, Exchange: c.Name, - Side: orderSide, - Date: orderDate, + Side: side, + Date: time.Unix(orders.Trades[y].Order.Timestamp, 0), Pair: p, }) } } } - order.FilterOrdersByTimeRange(&allOrders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&allOrders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", c.Name, err) + } order.FilterOrdersBySide(&allOrders, req.Side) return allOrders, nil } diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index d6d551a9..05313733 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -650,19 +650,26 @@ func (e *EXMO) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest) return nil, err } orderDate := time.Unix(resp[i].Created, 0) - orderSide := order.Side(strings.ToUpper(resp[i].Type)) + var side order.Side + side, err = order.StringToOrderSide(resp[i].Type) + if err != nil { + return nil, err + } orders = append(orders, order.Detail{ ID: strconv.FormatInt(resp[i].OrderID, 10), Amount: resp[i].Quantity, Date: orderDate, Price: resp[i].Price, - Side: orderSide, + Side: side, Exchange: e.Name, Pair: symbol, }) } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", e.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } @@ -701,7 +708,11 @@ func (e *EXMO) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest) return nil, err } orderDate := time.Unix(allTrades[i].Date, 0) - orderSide := order.Side(strings.ToUpper(allTrades[i].Type)) + var side order.Side + side, err = order.StringToOrderSide(allTrades[i].Type) + if err != nil { + return nil, err + } detail := order.Detail{ ID: strconv.FormatInt(allTrades[i].TradeID, 10), Amount: allTrades[i].Quantity, @@ -710,7 +721,7 @@ func (e *EXMO) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest) CostAsset: pair.Quote, Date: orderDate, Price: allTrades[i].Price, - Side: orderSide, + Side: side, Exchange: e.Name, Pair: pair, } @@ -718,7 +729,10 @@ func (e *EXMO) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest) orders[i] = detail } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err := order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", e.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index 23695304..1b2e6fcf 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -787,7 +787,11 @@ func (g *Gateio) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques if err != nil { return nil, err } - side := order.Side(strings.ToUpper(resp.Orders[i].Type)) + var side order.Side + side, err = order.StringToOrderSide(resp.Orders[i].Type) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", g.Name, err) + } status, err := order.StringToOrderStatus(resp.Orders[i].Status) if err != nil { log.Errorf(log.ExchangeSys, "%s %v", g.Name, err) @@ -807,7 +811,10 @@ func (g *Gateio) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques }) } } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err := order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", g.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } @@ -840,7 +847,11 @@ func (g *Gateio) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques if err != nil { return nil, err } - side := order.Side(strings.ToUpper(trades[i].Type)) + var side order.Side + side, err = order.StringToOrderSide(trades[i].Type) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", g.Name, err) + } orderDate := time.Unix(trades[i].TimeUnix, 0) detail := order.Detail{ ID: strconv.FormatInt(trades[i].OrderID, 10), @@ -857,7 +868,10 @@ func (g *Gateio) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques orders[i] = detail } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", g.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index a53549d3..b92b5bf4 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -7,7 +7,6 @@ import ( "net/url" "sort" "strconv" - "strings" "sync" "time" @@ -710,8 +709,11 @@ func (g *Gemini) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques } else if resp[i].Type == "market buy" || resp[i].Type == "market sell" { orderType = order.Market } - - side := order.Side(strings.ToUpper(resp[i].Type)) + var side order.Side + side, err = order.StringToOrderSide(resp[i].Type) + if err != nil { + return nil, err + } orderDate := time.Unix(resp[i].Timestamp, 0) orders[i] = order.Detail{ @@ -728,10 +730,13 @@ func (g *Gemini) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques } } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", g.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) order.FilterOrdersByType(&orders, req.Type) - order.FilterOrdersByCurrencies(&orders, req.Pairs) + order.FilterOrdersByPairs(&orders, req.Pairs) return orders, nil } @@ -774,7 +779,11 @@ func (g *Gemini) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques orders := make([]order.Detail, len(trades)) for i := range trades { - side := order.Side(strings.ToUpper(trades[i].Type)) + var side order.Side + side, err = order.StringToOrderSide(trades[i].Type) + if err != nil { + return nil, err + } orderDate := time.Unix(trades[i].Timestamp, 0) detail := order.Detail{ @@ -797,7 +806,10 @@ func (g *Gemini) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques orders[i] = detail } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", g.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index 5c9858a6..d1aaa6e3 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -742,7 +742,11 @@ func (h *HitBTC) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques if err != nil { return nil, err } - side := order.Side(strings.ToUpper(allOrders[i].Side)) + var side order.Side + side, err = order.StringToOrderSide(allOrders[i].Side) + if err != nil { + return nil, err + } orders[i] = order.Detail{ ID: allOrders[i].ID, Amount: allOrders[i].Quantity, @@ -754,7 +758,10 @@ func (h *HitBTC) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques } } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", h.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } @@ -792,8 +799,13 @@ func (h *HitBTC) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques if err != nil { return nil, err } - side := order.Side(strings.ToUpper(allOrders[i].Side)) - status, err := order.StringToOrderStatus(allOrders[i].Status) + var side order.Side + side, err = order.StringToOrderSide(allOrders[i].Side) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", h.Name, err) + } + var status order.Status + status, err = order.StringToOrderStatus(allOrders[i].Status) if err != nil { log.Errorf(log.ExchangeSys, "%s %v", h.Name, err) } @@ -815,7 +827,10 @@ func (h *HitBTC) GetOrderHistory(ctx context.Context, req *order.GetOrdersReques orders[i] = detail } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", h.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index 7ebe733d..4b78cb33 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -1351,9 +1351,7 @@ func (h *HUOBI) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest return nil, errors.New("currency must be supplied") } side := "" - if req.Side == order.AnySide || req.Side == "" { - side = "" - } else if req.Side == order.Sell { + if req.Side == order.Sell { side = req.Side.Lower() } if h.Websocket.CanUseAuthenticatedWebsocketForWrapper() { @@ -1527,7 +1525,10 @@ func (h *HUOBI) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest } order.FilterOrdersByType(&orders, req.Type) order.FilterOrdersBySide(&orders, req.Side) - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err := order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", h.Name, err) + } return orders, nil } @@ -1687,7 +1688,10 @@ func (h *HUOBI) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest } } } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err := order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", h.Name, err) + } return orders, nil } diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index c1f0ec7e..4ce4196a 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -6,7 +6,6 @@ import ( "net/url" "sort" "strconv" - "strings" "sync" "time" @@ -551,13 +550,18 @@ func (i *ItBit) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest orders := make([]order.Detail, 0, len(allOrders)) for j := range allOrders { var symbol currency.Pair - symbol, err := currency.NewPairDelimiter(allOrders[j].Instrument, + symbol, err = currency.NewPairDelimiter(allOrders[j].Instrument, format.Delimiter) if err != nil { return nil, err } - side := order.Side(strings.ToUpper(allOrders[j].Side)) - orderDate, err := time.Parse(time.RFC3339, allOrders[j].CreatedTime) + var side order.Side + side, err = order.StringToOrderSide(allOrders[j].Side) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", i.Name, err) + } + var orderDate time.Time + orderDate, err = time.Parse(time.RFC3339, allOrders[j].CreatedTime) if err != nil { log.Errorf(log.ExchangeSys, "Exchange %v Func %v Order %v Could not parse date to unix with value of %v", @@ -579,9 +583,12 @@ func (i *ItBit) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest }) } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", i.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) - order.FilterOrdersByCurrencies(&orders, req.Pairs) + order.FilterOrdersByPairs(&orders, req.Pairs) return orders, nil } @@ -623,13 +630,18 @@ func (i *ItBit) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest if err != nil { return nil, err } - - side := order.Side(strings.ToUpper(allOrders[j].Side)) - status, err := order.StringToOrderStatus(allOrders[j].Status) + var side order.Side + side, err = order.StringToOrderSide(allOrders[j].Side) if err != nil { log.Errorf(log.ExchangeSys, "%s %v", i.Name, err) } - orderDate, err := time.Parse(time.RFC3339, allOrders[j].CreatedTime) + var status order.Status + status, err = order.StringToOrderStatus(allOrders[j].Status) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", i.Name, err) + } + var orderDate time.Time + orderDate, err = time.Parse(time.RFC3339, allOrders[j].CreatedTime) if err != nil { log.Errorf(log.ExchangeSys, "Exchange %v Func %v Order %v Could not parse date to unix with value of %v", @@ -656,9 +668,12 @@ func (i *ItBit) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest orders = append(orders, detail) } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", i.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) - order.FilterOrdersByCurrencies(&orders, req.Pairs) + order.FilterOrdersByPairs(&orders, req.Pairs) return orders, nil } diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 70fc9817..46705ccb 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -1127,8 +1127,16 @@ func (k *Kraken) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques if err != nil { return nil, err } - side := order.Side(strings.ToUpper(resp.Open[i].Description.Type)) - orderType := order.Type(strings.ToUpper(resp.Open[i].Description.OrderType)) + var side order.Side + side, err = order.StringToOrderSide(resp.Open[i].Description.Type) + if err != nil { + return nil, err + } + var orderType order.Type + orderType, err = order.StringToOrderType(resp.Open[i].Description.OrderType) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", k.Name, err) + } orders = append(orders, order.Detail{ ID: i, Amount: resp.Open[i].Volume, @@ -1193,9 +1201,12 @@ func (k *Kraken) GetActiveOrders(ctx context.Context, req *order.GetOrdersReques default: return nil, fmt.Errorf("%s assetType not supported", req.AssetType) } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err := order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", k.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) - order.FilterOrdersByCurrencies(&orders, req.Pairs) + order.FilterOrdersByPairs(&orders, req.Pairs) return orders, nil } @@ -1244,12 +1255,20 @@ func (k *Kraken) GetOrderHistory(ctx context.Context, getOrdersRequest *order.Ge return nil, err } - side := order.Side(strings.ToUpper(resp.Closed[i].Description.Type)) + var side order.Side + side, err = order.StringToOrderSide(resp.Closed[i].Description.Type) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", k.Name, err) + } status, err := order.StringToOrderStatus(resp.Closed[i].Status) if err != nil { log.Errorf(log.ExchangeSys, "%s %v", k.Name, err) } - orderType := order.Type(strings.ToUpper(resp.Closed[i].Description.OrderType)) + var orderType order.Type + orderType, err = order.StringToOrderType(resp.Closed[i].Description.OrderType) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", k.Name, err) + } detail := order.Detail{ ID: i, Amount: resp.Closed[i].Volume, @@ -1414,7 +1433,7 @@ func (k *Kraken) GetOrderHistory(ctx context.Context, getOrdersRequest *order.Ge } order.FilterOrdersBySide(&orders, getOrdersRequest.Side) - order.FilterOrdersByCurrencies(&orders, getOrdersRequest.Pairs) + order.FilterOrdersByPairs(&orders, getOrdersRequest.Pairs) return orders, nil } diff --git a/exchanges/lbank/lbank_test.go b/exchanges/lbank/lbank_test.go index 31501f70..5691f939 100644 --- a/exchanges/lbank/lbank_test.go +++ b/exchanges/lbank/lbank_test.go @@ -567,3 +567,27 @@ func TestUpdateTickers(t *testing.T) { t.Error(err) } } + +func TestGetStatus(t *testing.T) { + t.Parallel() + for _, tt := range []struct { + status int64 + resp order.Status + }{ + {status: -1, resp: order.Cancelled}, + {status: 0, resp: order.Active}, + {status: 1, resp: order.PartiallyFilled}, + {status: 2, resp: order.Filled}, + {status: 4, resp: order.Cancelling}, + {status: 5, resp: order.UnknownStatus}, + } { + tt := tt + t.Run("", func(t *testing.T) { + t.Parallel() + resp := l.GetStatus(tt.status) + if resp != tt.resp { + t.Fatalf("received: '%v' but expected: '%v'", resp, tt.resp) + } + }) + } +} diff --git a/exchanges/lbank/lbank_wrapper.go b/exchanges/lbank/lbank_wrapper.go index 24e0ebf8..1b8893e0 100644 --- a/exchanges/lbank/lbank_wrapper.go +++ b/exchanges/lbank/lbank_wrapper.go @@ -596,30 +596,16 @@ func (l *Lbank) GetOrderInfo(ctx context.Context, orderID string, pair currency. } else { resp.Side = order.Sell } - z := tempResp.Orders[0].Status - switch { - case z == -1: - resp.Status = "cancelled" - case z == 0: - resp.Status = "on trading" - case z == 1: - resp.Status = "filled partially" - case z == 2: - resp.Status = "Filled totally" - case z == 4: - resp.Status = "Cancelling" - default: - resp.Status = "Invalid Order Status" - } + + resp.Status = l.GetStatus(tempResp.Orders[0].Status) resp.Price = tempResp.Orders[0].Price resp.Amount = tempResp.Orders[0].Amount resp.ExecutedAmount = tempResp.Orders[0].DealAmount resp.RemainingAmount = tempResp.Orders[0].Amount - tempResp.Orders[0].DealAmount - resp.Fee, err = l.GetFeeByType(ctx, - &exchange.FeeBuilder{ - FeeType: exchange.CryptocurrencyTradeFee, - Amount: tempResp.Orders[0].Amount, - PurchasePrice: tempResp.Orders[0].Price}) + resp.Fee, err = l.GetFeeByType(ctx, &exchange.FeeBuilder{ + FeeType: exchange.CryptocurrencyTradeFee, + Amount: tempResp.Orders[0].Amount, + PurchasePrice: tempResp.Orders[0].Price}) if err != nil { resp.Fee = lbankFeeNotFound } @@ -696,21 +682,7 @@ func (l *Lbank) GetActiveOrders(ctx context.Context, getOrdersRequest *order.Get } else { resp.Side = order.Sell } - z := tempResp.Orders[0].Status - switch { - case z == -1: - resp.Status = "cancelled" - case z == 1: - resp.Status = "on trading" - case z == 2: - resp.Status = "filled partially" - case z == 3: - resp.Status = "Filled totally" - case z == 4: - resp.Status = "Cancelling" - default: - resp.Status = "Invalid Order Status" - } + resp.Status = l.GetStatus(tempResp.Orders[0].Status) resp.Price = tempResp.Orders[0].Price resp.Amount = tempResp.Orders[0].Amount resp.Date = time.Unix(tempResp.Orders[0].CreateTime, 0) @@ -728,7 +700,7 @@ func (l *Lbank) GetActiveOrders(ctx context.Context, getOrdersRequest *order.Get if getOrdersRequest.Pairs[y].String() != key { continue } - if getOrdersRequest.Side == "ANY" { + if getOrdersRequest.Side == order.AnySide { finalResp = append(finalResp, resp) continue } @@ -791,21 +763,7 @@ func (l *Lbank) GetOrderHistory(ctx context.Context, getOrdersRequest *order.Get } else { resp.Side = order.Sell } - z := tempResp.Orders[x].Status - switch { - case z == -1: - resp.Status = "cancelled" - case z == 1: - resp.Status = "on trading" - case z == 2: - resp.Status = "filled partially" - case z == 3: - resp.Status = "Filled totally" - case z == 4: - resp.Status = "Cancelling" - default: - resp.Status = "Invalid Order Status" - } + resp.Status = l.GetStatus(tempResp.Orders[x].Status) resp.Price = tempResp.Orders[x].Price resp.AverageExecutedPrice = tempResp.Orders[x].AvgPrice resp.Amount = tempResp.Orders[x].Amount @@ -1028,3 +986,28 @@ func (l *Lbank) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pa ret.SortCandlesByTimestamp(false) return ret, nil } + +// GetStatus returns the order.Status from the int representation. +func (l *Lbank) GetStatus(status int64) order.Status { + var oStatus order.Status + switch status { + case -1: + // "cancelled" + oStatus = order.Cancelled + case 0: + // "on trading" + oStatus = order.Active + case 1: + // "filled partially" + oStatus = order.PartiallyFilled + case 2: + // "filled totally" + oStatus = order.Filled + case 4: + // "Cancelling" + oStatus = order.Cancelling + default: + log.Errorf(log.Global, "%s Unhandled Order Status '%v'", l.GetName(), status) + } + return oStatus +} diff --git a/exchanges/localbitcoins/localbitcoins_types.go b/exchanges/localbitcoins/localbitcoins_types.go index 5ac68ced..67e0d7d4 100644 --- a/exchanges/localbitcoins/localbitcoins_types.go +++ b/exchanges/localbitcoins/localbitcoins_types.go @@ -211,7 +211,7 @@ type DashBoardInfo struct { FeeBTC float64 `json:"fee_btc,string"` ExchangeRateUpdatedAt string `json:"exchange_rate_updated_at"` Advertisement struct { - ID int `json:"id"` + ID int64 `json:"id"` TradeType string `json:"trade_type"` Advertiser struct { Username string `json:"username"` diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index 7ae90a09..8f0f25eb 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -562,7 +562,8 @@ func (l *LocalBitcoins) GetActiveOrders(ctx context.Context, getOrdersRequest *o orders := make([]order.Detail, len(resp)) for i := range resp { - orderDate, err := time.Parse(time.RFC3339, resp[i].Data.CreatedAt) + var orderDate time.Time + orderDate, err = time.Parse(time.RFC3339, resp[i].Data.CreatedAt) if err != nil { log.Errorf(log.ExchangeSys, "Exchange %v Func %v Order %v Could not parse date to unix with value of %v", l.Name, @@ -579,7 +580,7 @@ func (l *LocalBitcoins) GetActiveOrders(ctx context.Context, getOrdersRequest *o orders[i] = order.Detail{ Amount: resp[i].Data.AmountBTC, Price: resp[i].Data.Amount, - ID: strconv.FormatInt(int64(resp[i].Data.Advertisement.ID), 10), + ID: strconv.FormatInt(resp[i].Data.Advertisement.ID, 10), Date: orderDate, Fee: resp[i].Data.FeeBTC, Side: side, @@ -590,10 +591,12 @@ func (l *LocalBitcoins) GetActiveOrders(ctx context.Context, getOrdersRequest *o } } - order.FilterOrdersByTimeRange(&orders, getOrdersRequest.StartTime, + err = order.FilterOrdersByTimeRange(&orders, getOrdersRequest.StartTime, getOrdersRequest.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", l.Name, err) + } order.FilterOrdersBySide(&orders, getOrdersRequest.Side) - return orders, nil } @@ -630,7 +633,8 @@ func (l *LocalBitcoins) GetOrderHistory(ctx context.Context, getOrdersRequest *o orders := make([]order.Detail, len(allTrades)) for i := range allTrades { - orderDate, err := time.Parse(time.RFC3339, allTrades[i].Data.CreatedAt) + var orderDate time.Time + orderDate, err = time.Parse(time.RFC3339, allTrades[i].Data.CreatedAt) if err != nil { log.Errorf(log.ExchangeSys, "Exchange %v Func %v Order %v Could not parse date to unix with value of %v", @@ -661,7 +665,8 @@ func (l *LocalBitcoins) GetOrderHistory(ctx context.Context, getOrdersRequest *o status = "Closed" } - orderStatus, err := order.StringToOrderStatus(status) + var orderStatus order.Status + orderStatus, err = order.StringToOrderStatus(status) if err != nil { log.Errorf(log.ExchangeSys, "%s %v", l.Name, err) } @@ -669,7 +674,7 @@ func (l *LocalBitcoins) GetOrderHistory(ctx context.Context, getOrdersRequest *o orders[i] = order.Detail{ Amount: allTrades[i].Data.AmountBTC, Price: allTrades[i].Data.Amount, - ID: strconv.FormatInt(int64(allTrades[i].Data.Advertisement.ID), 10), + ID: strconv.FormatInt(allTrades[i].Data.Advertisement.ID, 10), Date: orderDate, Fee: allTrades[i].Data.FeeBTC, Side: side, @@ -681,8 +686,11 @@ func (l *LocalBitcoins) GetOrderHistory(ctx context.Context, getOrdersRequest *o } } - order.FilterOrdersByTimeRange(&orders, getOrdersRequest.StartTime, + err = order.FilterOrdersByTimeRange(&orders, getOrdersRequest.StartTime, getOrdersRequest.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", l.Name, err) + } order.FilterOrdersBySide(&orders, getOrdersRequest.Side) return orders, nil diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go index 58883014..29cd0f58 100644 --- a/exchanges/okgroup/okgroup_wrapper.go +++ b/exchanges/okgroup/okgroup_wrapper.go @@ -420,6 +420,11 @@ func (o *OKGroup) GetOrderInfo(ctx context.Context, orderID string, pair currenc if err != nil { log.Errorf(log.ExchangeSys, "%s %v", o.Name, err) } + + side, err := order.StringToOrderSide(mOrder.Side) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", o.Name, err) + } resp = order.Detail{ Amount: mOrder.Size, Pair: p, @@ -427,7 +432,7 @@ func (o *OKGroup) GetOrderInfo(ctx context.Context, orderID string, pair currenc Date: mOrder.Timestamp, ExecutedAmount: mOrder.FilledSize, Status: status, - Side: order.Side(mOrder.Side), + Side: side, } return resp, nil } @@ -523,14 +528,24 @@ func (o *OKGroup) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque if err != nil { log.Errorf(log.ExchangeSys, "%s %v", o.Name, err) } + var side order.Side + side, err = order.StringToOrderSide(spotOpenOrders[i].Side) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", o.Name, err) + } + var orderType order.Type + orderType, err = order.StringToOrderType(spotOpenOrders[i].Type) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", o.Name, err) + } resp = append(resp, order.Detail{ ID: spotOpenOrders[i].OrderID, Price: spotOpenOrders[i].Price, Amount: spotOpenOrders[i].Size, Pair: req.Pairs[x], Exchange: o.Name, - Side: order.Side(spotOpenOrders[i].Side), - Type: order.Type(spotOpenOrders[i].Type), + Side: side, + Type: orderType, ExecutedAmount: spotOpenOrders[i].FilledSize, Date: spotOpenOrders[i].Timestamp, Status: status, @@ -569,6 +584,16 @@ func (o *OKGroup) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque if err != nil { log.Errorf(log.ExchangeSys, "%s %v", o.Name, err) } + var side order.Side + side, err = order.StringToOrderSide(spotOrders[i].Side) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", o.Name, err) + } + var orderType order.Type + orderType, err = order.StringToOrderType(spotOrders[i].Type) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", o.Name, err) + } detail := order.Detail{ ID: spotOrders[i].OrderID, Price: spotOrders[i].Price, @@ -578,8 +603,8 @@ func (o *OKGroup) GetOrderHistory(ctx context.Context, req *order.GetOrdersReque RemainingAmount: spotOrders[i].Size - spotOrders[i].FilledSize, Pair: req.Pairs[x], Exchange: o.Name, - Side: order.Side(spotOrders[i].Side), - Type: order.Type(spotOrders[i].Type), + Side: side, + Type: orderType, Date: spotOrders[i].Timestamp, Status: status, } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index b150ce5f..fe43a32f 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -422,7 +422,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if p.asset != d.AssetType { return fmt.Errorf("%w asset '%v' received: '%v'", errOrderNotEqualToTracker, d.AssetType, p.asset) } - if d.Side == "" { + if d.Side == UnknownSide { return ErrSideIsInvalid } if d.ID == "" { @@ -473,7 +473,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { longSide = longSide.Add(decimal.NewFromFloat(p.longPositions[i].Amount)) } - if p.currentDirection == "" { + if p.currentDirection == UnknownSide { p.currentDirection = d.Side } diff --git a/exchanges/order/order_test.go b/exchanges/order/order_test.go index d0eb1398..22eec131 100644 --- a/exchanges/order/order_test.go +++ b/exchanges/order/order_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/gofrs/uuid" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/validate" @@ -16,6 +17,7 @@ import ( var errValidationCheckFailed = errors.New("validation check failed") func TestValidate(t *testing.T) { + t.Parallel() testPair := currency.NewPair(currency.BTC, currency.LTC) tester := []struct { ExpectedErr error @@ -115,7 +117,7 @@ func TestValidate(t *testing.T) { for x := range tester { err := tester[x].Submit.Validate(tester[x].ValidOpts) if !errors.Is(err, tester[x].ExpectedErr) { - t.Errorf("Unexpected result. Got: %v, want: %v", err, tester[x].ExpectedErr) + t.Fatalf("Unexpected result. %d Got: %v, want: %v", x+1, err, tester[x].ExpectedErr) } } } @@ -140,17 +142,16 @@ func TestOrderSides(t *testing.T) { func TestOrderTypes(t *testing.T) { t.Parallel() - var ot Type = "Mo'Money" - - if ot.String() != "Mo'Money" { + var ot Type + if ot.String() != "UNKNOWN" { t.Errorf("unexpected string %s", ot.String()) } - if ot.Lower() != "mo'money" { + if ot.Lower() != "unknown" { t.Errorf("unexpected string %s", ot.Lower()) } - if ot.Title() != "Mo'Money" { + if ot.Title() != "Unknown" { t.Errorf("unexpected string %s", ot.Title()) } } @@ -267,6 +268,27 @@ func TestFilterOrdersByType(t *testing.T) { } } +var filterOrdersByTypeBenchmark = &[]Detail{ + {Type: Limit}, + {Type: Limit}, + {Type: Limit}, + {Type: Limit}, + {Type: Limit}, + {Type: Limit}, + {Type: Limit}, + {Type: Limit}, + {Type: Limit}, + {Type: Limit}, +} + +// 392455 3226 ns/op 15840 B/op 5 allocs/op // PREV +// 9486490 109.5 ns/op 0 B/op 0 allocs/op // CURRENT +func BenchmarkFilterOrdersByType(b *testing.B) { + for x := 0; x < b.N; x++ { + FilterOrdersByType(filterOrdersByTypeBenchmark, Limit) + } +} + func TestFilterOrdersBySide(t *testing.T) { t.Parallel() @@ -296,6 +318,27 @@ func TestFilterOrdersBySide(t *testing.T) { } } +var filterOrdersBySideBenchmark = &[]Detail{ + {Side: Ask}, + {Side: Ask}, + {Side: Ask}, + {Side: Ask}, + {Side: Ask}, + {Side: Ask}, + {Side: Ask}, + {Side: Ask}, + {Side: Ask}, + {Side: Ask}, +} + +// 372594 3049 ns/op 15840 B/op 5 allocs/op // PREV +// 7412187 148.8 ns/op 0 B/op 0 allocs/op // CURRENT +func BenchmarkFilterOrdersBySide(b *testing.B) { + for x := 0; x < b.N; x++ { + FilterOrdersBySide(filterOrdersBySideBenchmark, Ask) + } +} + func TestFilterOrdersByTimeRange(t *testing.T) { t.Parallel() @@ -311,34 +354,78 @@ func TestFilterOrdersByTimeRange(t *testing.T) { }, } - FilterOrdersByTimeRange(&orders, time.Unix(0, 0), time.Unix(0, 0)) + err := FilterOrdersByTimeRange(&orders, time.Unix(0, 0), time.Unix(0, 0)) + if err != nil { + t.Fatal(err) + } if len(orders) != 3 { t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders)) } - FilterOrdersByTimeRange(&orders, time.Unix(100, 0), time.Unix(111, 0)) + err = FilterOrdersByTimeRange(&orders, time.Unix(100, 0), time.Unix(111, 0)) + if err != nil { + t.Fatal(err) + } if len(orders) != 3 { t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders)) } - FilterOrdersByTimeRange(&orders, time.Unix(101, 0), time.Unix(111, 0)) + err = FilterOrdersByTimeRange(&orders, time.Unix(101, 0), time.Unix(111, 0)) + if err != nil { + t.Fatal(err) + } if len(orders) != 2 { t.Errorf("Orders failed to be filtered. Expected %v, received %v", 2, len(orders)) } - FilterOrdersByTimeRange(&orders, time.Unix(200, 0), time.Unix(300, 0)) + err = FilterOrdersByTimeRange(&orders, time.Unix(200, 0), time.Unix(300, 0)) + if err != nil { + t.Fatal(err) + } if len(orders) != 0 { t.Errorf("Orders failed to be filtered. Expected %v, received %v", 0, len(orders)) } orders = append(orders, Detail{}) // test for event no timestamp is set on an order, best to include it - FilterOrdersByTimeRange(&orders, time.Unix(200, 0), time.Unix(300, 0)) + err = FilterOrdersByTimeRange(&orders, time.Unix(200, 0), time.Unix(300, 0)) + if err != nil { + t.Fatal(err) + } if len(orders) != 1 { t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders)) } + + err = FilterOrdersByTimeRange(&orders, time.Unix(300, 0), time.Unix(50, 0)) + if !errors.Is(err, common.ErrStartAfterEnd) { + t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrStartAfterEnd) + } } -func TestFilterOrdersByCurrencies(t *testing.T) { +var filterOrdersByTimeRangeBenchmark = &[]Detail{ + {Date: time.Unix(100, 0)}, + {Date: time.Unix(100, 0)}, + {Date: time.Unix(100, 0)}, + {Date: time.Unix(100, 0)}, + {Date: time.Unix(100, 0)}, + {Date: time.Unix(100, 0)}, + {Date: time.Unix(100, 0)}, + {Date: time.Unix(100, 0)}, + {Date: time.Unix(100, 0)}, + {Date: time.Unix(100, 0)}, +} + +// 390822 3335 ns/op 15840 B/op 5 allocs/op // PREV +// 6201034 172.1 ns/op 0 B/op 0 allocs/op // CURRENT +func BenchmarkFilterOrdersByTimeRange(b *testing.B) { + for x := 0; x < b.N; x++ { + err := FilterOrdersByTimeRange(filterOrdersByTimeRangeBenchmark, time.Unix(50, 0), time.Unix(150, 0)) + if err != nil { + b.Fatal(err) + } + } +} + +func TestFilterOrdersByPairs(t *testing.T) { t.Parallel() var orders = []Detail{ @@ -356,42 +443,64 @@ func TestFilterOrdersByCurrencies(t *testing.T) { currencies := []currency.Pair{currency.NewPair(currency.BTC, currency.USD), currency.NewPair(currency.LTC, currency.EUR), currency.NewPair(currency.DOGE, currency.RUB)} - FilterOrdersByCurrencies(&orders, currencies) + FilterOrdersByPairs(&orders, currencies) if len(orders) != 3 { t.Errorf("Orders failed to be filtered. Expected %v, received %v", 3, len(orders)) } currencies = []currency.Pair{currency.NewPair(currency.BTC, currency.USD), currency.NewPair(currency.LTC, currency.EUR)} - FilterOrdersByCurrencies(&orders, currencies) + FilterOrdersByPairs(&orders, currencies) if len(orders) != 2 { t.Errorf("Orders failed to be filtered. Expected %v, received %v", 2, len(orders)) } currencies = []currency.Pair{currency.NewPair(currency.BTC, currency.USD)} - FilterOrdersByCurrencies(&orders, currencies) + FilterOrdersByPairs(&orders, currencies) if len(orders) != 1 { t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders)) } currencies = []currency.Pair{currency.NewPair(currency.USD, currency.BTC)} - FilterOrdersByCurrencies(&orders, currencies) + FilterOrdersByPairs(&orders, currencies) if len(orders) != 1 { t.Errorf("Reverse Orders failed to be filtered. Expected %v, received %v", 1, len(orders)) } currencies = []currency.Pair{} - FilterOrdersByCurrencies(&orders, currencies) + FilterOrdersByPairs(&orders, currencies) if len(orders) != 1 { t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders)) } currencies = append(currencies, currency.EMPTYPAIR) - FilterOrdersByCurrencies(&orders, currencies) + FilterOrdersByPairs(&orders, currencies) if len(orders) != 1 { t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders)) } } +var filterOrdersByPairsBenchmark = &[]Detail{ + {Pair: currency.NewPair(currency.BTC, currency.USD)}, + {Pair: currency.NewPair(currency.BTC, currency.USD)}, + {Pair: currency.NewPair(currency.BTC, currency.USD)}, + {Pair: currency.NewPair(currency.BTC, currency.USD)}, + {Pair: currency.NewPair(currency.BTC, currency.USD)}, + {Pair: currency.NewPair(currency.BTC, currency.USD)}, + {Pair: currency.NewPair(currency.BTC, currency.USD)}, + {Pair: currency.NewPair(currency.BTC, currency.USD)}, + {Pair: currency.NewPair(currency.BTC, currency.USD)}, + {Pair: currency.NewPair(currency.BTC, currency.USD)}, +} + +// 400032 2977 ns/op 15840 B/op 5 allocs/op // PREV +// 6977242 172.8 ns/op 0 B/op 0 allocs/op // CURRENT +func BenchmarkFilterOrdersByPairs(b *testing.B) { + pairs := []currency.Pair{currency.NewPair(currency.BTC, currency.USD)} + for x := 0; x < b.N; x++ { + FilterOrdersByPairs(filterOrdersByPairsBenchmark, pairs) + } +} + func TestSortOrdersByPrice(t *testing.T) { t.Parallel() @@ -546,41 +655,39 @@ func TestSortOrdersByOrderType(t *testing.T) { } } -var stringsToOrderSide = []struct { - in string - out Side - err error -}{ - {"buy", Buy, nil}, - {"BUY", Buy, nil}, - {"bUy", Buy, nil}, - {"sell", Sell, nil}, - {"SELL", Sell, nil}, - {"sElL", Sell, nil}, - {"bid", Bid, nil}, - {"BID", Bid, nil}, - {"bId", Bid, nil}, - {"ask", Ask, nil}, - {"ASK", Ask, nil}, - {"aSk", Ask, nil}, - {"lOnG", Long, nil}, - {"ShoRt", Short, nil}, - {"any", AnySide, nil}, - {"ANY", AnySide, nil}, - {"aNy", AnySide, nil}, - {"woahMan", Buy, errors.New("WOAHMAN not recognised as order side")}, -} - func TestStringToOrderSide(t *testing.T) { - for i := range stringsToOrderSide { - testData := &stringsToOrderSide[i] + cases := []struct { + in string + out Side + err error + }{ + {"buy", Buy, nil}, + {"BUY", Buy, nil}, + {"bUy", Buy, nil}, + {"sell", Sell, nil}, + {"SELL", Sell, nil}, + {"sElL", Sell, nil}, + {"bid", Bid, nil}, + {"BID", Bid, nil}, + {"bId", Bid, nil}, + {"ask", Ask, nil}, + {"ASK", Ask, nil}, + {"aSk", Ask, nil}, + {"lOnG", Long, nil}, + {"ShoRt", Short, nil}, + {"any", AnySide, nil}, + {"ANY", AnySide, nil}, + {"aNy", AnySide, nil}, + {"woahMan", UnknownSide, errUnrecognisedOrderSide}, + } + for i := range cases { + testData := &cases[i] t.Run(testData.in, func(t *testing.T) { out, err := StringToOrderSide(testData.in) - if err != nil { - if err.Error() != testData.err.Error() { - t.Error("Unexpected error", err) - } - } else if out != testData.out { + if !errors.Is(err, testData.err) { + t.Fatalf("received: '%v' but expected: '%v'", err, testData.err) + } + if out != testData.out { t.Errorf("Unexpected output %v. Expected %v", out, testData.out) } }) @@ -597,53 +704,51 @@ func BenchmarkStringToOrderSide(b *testing.B) { } } -var stringsToOrderType = []struct { - in string - out Type - err error -}{ - {"limit", Limit, nil}, - {"LIMIT", Limit, nil}, - {"lImIt", Limit, nil}, - {"market", Market, nil}, - {"MARKET", Market, nil}, - {"mArKeT", Market, nil}, - {"immediate_or_cancel", ImmediateOrCancel, nil}, - {"IMMEDIATE_OR_CANCEL", ImmediateOrCancel, nil}, - {"iMmEdIaTe_Or_CaNcEl", ImmediateOrCancel, nil}, - {"iMmEdIaTe Or CaNcEl", ImmediateOrCancel, nil}, - {"stop", Stop, nil}, - {"STOP", Stop, nil}, - {"sToP", Stop, nil}, - {"sToP LiMit", StopLimit, nil}, - {"ExchangE sToP Limit", StopLimit, nil}, - {"trailing_stop", TrailingStop, nil}, - {"TRAILING_STOP", TrailingStop, nil}, - {"tRaIlInG_sToP", TrailingStop, nil}, - {"tRaIlInG sToP", TrailingStop, nil}, - {"fOk", FillOrKill, nil}, - {"exchange fOk", FillOrKill, nil}, - {"ios", IOS, nil}, - {"post_ONly", PostOnly, nil}, - {"any", AnyType, nil}, - {"ANY", AnyType, nil}, - {"aNy", AnyType, nil}, - {"trigger", Trigger, nil}, - {"TRIGGER", Trigger, nil}, - {"tRiGgEr", Trigger, nil}, - {"woahMan", UnknownType, errors.New("WOAHMAN not recognised as order type")}, -} - func TestStringToOrderType(t *testing.T) { - for i := range stringsToOrderType { - testData := &stringsToOrderType[i] + cases := []struct { + in string + out Type + err error + }{ + {"limit", Limit, nil}, + {"LIMIT", Limit, nil}, + {"lImIt", Limit, nil}, + {"market", Market, nil}, + {"MARKET", Market, nil}, + {"mArKeT", Market, nil}, + {"immediate_or_cancel", ImmediateOrCancel, nil}, + {"IMMEDIATE_OR_CANCEL", ImmediateOrCancel, nil}, + {"iMmEdIaTe_Or_CaNcEl", ImmediateOrCancel, nil}, + {"iMmEdIaTe Or CaNcEl", ImmediateOrCancel, nil}, + {"stop", Stop, nil}, + {"STOP", Stop, nil}, + {"sToP", Stop, nil}, + {"sToP LiMit", StopLimit, nil}, + {"ExchangE sToP Limit", StopLimit, nil}, + {"trailing_stop", TrailingStop, nil}, + {"TRAILING_STOP", TrailingStop, nil}, + {"tRaIlInG_sToP", TrailingStop, nil}, + {"tRaIlInG sToP", TrailingStop, nil}, + {"fOk", FillOrKill, nil}, + {"exchange fOk", FillOrKill, nil}, + {"ios", IOS, nil}, + {"post_ONly", PostOnly, nil}, + {"any", AnyType, nil}, + {"ANY", AnyType, nil}, + {"aNy", AnyType, nil}, + {"trigger", Trigger, nil}, + {"TRIGGER", Trigger, nil}, + {"tRiGgEr", Trigger, nil}, + {"woahMan", UnknownType, errUnrecognisedOrderType}, + } + for i := range cases { + testData := &cases[i] t.Run(testData.in, func(t *testing.T) { out, err := StringToOrderType(testData.in) - if err != nil { - if err.Error() != testData.err.Error() { - t.Error("Unexpected error", err) - } - } else if out != testData.out { + if !errors.Is(err, testData.err) { + t.Fatalf("received: '%v' but expected: '%v'", err, testData.err) + } + if out != testData.out { t.Errorf("Unexpected output %v. Expected %v", out, testData.out) } }) @@ -705,7 +810,8 @@ var stringsToOrderStatus = []struct { {"partially canceLLed", PartiallyCancelled, nil}, {"opeN", Open, nil}, {"cLosEd", Closed, nil}, - {"woahMan", UnknownStatus, errors.New("WOAHMAN not recognised as order status")}, + {"cancellinG", Cancelling, nil}, + {"woahMan", UnknownStatus, errUnrecognisedOrderStatus}, } func TestStringToOrderStatus(t *testing.T) { @@ -713,11 +819,10 @@ func TestStringToOrderStatus(t *testing.T) { testData := &stringsToOrderStatus[i] t.Run(testData.in, func(t *testing.T) { out, err := StringToOrderStatus(testData.in) - if err != nil { - if err.Error() != testData.err.Error() { - t.Error("Unexpected error", err) - } - } else if out != testData.out { + if !errors.Is(err, testData.err) { + t.Fatalf("received: '%v' but expected: '%v'", err, testData.err) + } + if out != testData.out { t.Errorf("Unexpected output %v. Expected %v", out, testData.out) } }) @@ -736,34 +841,7 @@ func BenchmarkStringToOrderStatus(b *testing.B) { func TestUpdateOrderFromModify(t *testing.T) { var leet = "1337" - od := Detail{ - ImmediateOrCancel: false, - HiddenOrder: false, - FillOrKill: false, - PostOnly: false, - Leverage: 0, - Price: 0, - Amount: 0, - LimitPriceUpper: 0, - LimitPriceLower: 0, - TriggerPrice: 0, - QuoteAmount: 0, - ExecutedAmount: 0, - RemainingAmount: 0, - Fee: 0, - Exchange: "", - ID: "1", - AccountID: "", - ClientID: "", - WalletAddress: "", - Type: "", - Side: "", - Status: "", - Date: time.Time{}, - LastUpdated: time.Time{}, - Pair: currency.EMPTYPAIR, - Trades: nil, - } + od := Detail{ID: "1"} updated := time.Now() pair, err := currency.NewPairFromString("BTCUSD") @@ -792,9 +870,9 @@ func TestUpdateOrderFromModify(t *testing.T) { AccountID: "1", ClientID: "1", WalletAddress: "1", - Type: "1", - Side: "1", - Status: "1", + Type: 1, + Side: 1, + Status: 1, AssetType: 1, LastUpdated: updated, Pair: pair, @@ -859,13 +937,13 @@ func TestUpdateOrderFromModify(t *testing.T) { if od.WalletAddress != "1" { t.Error("Failed to update") } - if od.Type != "1" { + if od.Type != 1 { t.Error("Failed to update") } - if od.Side != "1" { + if od.Side != 1 { t.Error("Failed to update") } - if od.Status != "1" { + if od.Status != 1 { t.Error("Failed to update") } if od.AssetType != 1 { @@ -927,34 +1005,7 @@ func TestUpdateOrderFromModify(t *testing.T) { func TestUpdateOrderFromDetail(t *testing.T) { var leet = "1337" - od := Detail{ - ImmediateOrCancel: false, - HiddenOrder: false, - FillOrKill: false, - PostOnly: false, - Leverage: 0, - Price: 0, - Amount: 0, - LimitPriceUpper: 0, - LimitPriceLower: 0, - TriggerPrice: 0, - QuoteAmount: 0, - ExecutedAmount: 0, - RemainingAmount: 0, - Fee: 0, - Exchange: "test", - ID: "", - AccountID: "", - ClientID: "", - WalletAddress: "", - Type: "", - Side: "", - Status: "", - Date: time.Time{}, - LastUpdated: time.Time{}, - Pair: currency.EMPTYPAIR, - Trades: nil, - } + od := Detail{Exchange: "test"} updated := time.Now() pair, err := currency.NewPairFromString("BTCUSD") @@ -983,9 +1034,9 @@ func TestUpdateOrderFromDetail(t *testing.T) { AccountID: "1", ClientID: "1", WalletAddress: "1", - Type: "1", - Side: "1", - Status: "1", + Type: 1, + Side: 1, + Status: 1, AssetType: 1, LastUpdated: updated, Pair: pair, @@ -1050,13 +1101,13 @@ func TestUpdateOrderFromDetail(t *testing.T) { if od.WalletAddress != "1" { t.Error("Failed to update") } - if od.Type != "1" { + if od.Type != 1 { t.Error("Failed to update") } - if od.Side != "1" { + if od.Side != 1 { t.Error("Failed to update") } - if od.Status != "1" { + if od.Status != 1 { t.Error("Failed to update") } if od.AssetType != 1 { @@ -1373,7 +1424,8 @@ func TestIsActive(t *testing.T) { o Detail expRes bool }{ - 0: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AnyStatus}, true}, + // For now force inactive on any status + 0: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AnyStatus}, false}, 1: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: New}, true}, 2: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Active}, true}, 3: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: PartiallyCancelled}, false}, @@ -1386,7 +1438,8 @@ func TestIsActive(t *testing.T) { 10: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Rejected}, false}, 11: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Expired}, false}, 12: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Hidden}, true}, - 13: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: UnknownStatus}, true}, + // For now force inactive on unknown status + 13: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: UnknownStatus}, false}, 14: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Open}, true}, 15: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AutoDeleverage}, true}, 16: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Closed}, false}, @@ -1395,12 +1448,24 @@ func TestIsActive(t *testing.T) { // specific tests for num, tt := range statusTests { if tt.o.IsActive() != tt.expRes { - t.Errorf("statusTests[%v] failed", num) + t.Fatalf("statusTests[%v] failed", num) } } } -func TestIsInctive(t *testing.T) { +var activeBenchmark = Detail{Status: Pending, Amount: 1} + +// 610732089 2.414 ns/op 0 B/op 0 allocs/op // PREV +// 1000000000 1.188 ns/op 0 B/op 0 allocs/op // CURRENT +func BenchmarkIsActive(b *testing.B) { + for x := 0; x < b.N; x++ { + if !activeBenchmark.IsActive() { + b.Fatal("expected true") + } + } +} + +func TestIsInactive(t *testing.T) { orders := map[int]Detail{ 0: {Amount: 0.0, Status: Active}, 1: {Amount: 1.0, ExecutedAmount: 0.9, Status: Active}, @@ -1428,7 +1493,8 @@ func TestIsInctive(t *testing.T) { o Detail expRes bool }{ - 0: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AnyStatus}, false}, + // For now force inactive on any status + 0: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AnyStatus}, true}, 1: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: New}, false}, 2: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Active}, false}, 3: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: PartiallyCancelled}, true}, @@ -1441,7 +1507,8 @@ func TestIsInctive(t *testing.T) { 10: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Rejected}, true}, 11: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Expired}, true}, 12: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Hidden}, false}, - 13: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: UnknownStatus}, false}, + // For now force inactive on unknown status + 13: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: UnknownStatus}, true}, 14: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Open}, false}, 15: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: AutoDeleverage}, false}, 16: {Detail{Amount: 1.0, ExecutedAmount: 0.0, Status: Closed}, true}, @@ -1455,6 +1522,17 @@ func TestIsInctive(t *testing.T) { } } +var inactiveBenchmark = Detail{Status: Closed, Amount: 1} + +// 1000000000 1.043 ns/op 0 B/op 0 allocs/op // CURRENT +func BenchmarkIsInactive(b *testing.B) { + for x := 0; x < b.N; x++ { + if !inactiveBenchmark.IsInactive() { + b.Fatal("expected true") + } + } +} + func TestGenerateInternalOrderID(t *testing.T) { id, err := uuid.NewV4() if err != nil { diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index b08217aa..d0b7b7e4 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -242,66 +242,73 @@ type GetOrdersRequest struct { } // Status defines order status types -type Status string +type Status uint32 // All order status types const ( - AnyStatus Status = "ANY" - New Status = "NEW" - Active Status = "ACTIVE" - PartiallyCancelled Status = "PARTIALLY_CANCELLED" - PartiallyFilled Status = "PARTIALLY_FILLED" - Filled Status = "FILLED" - Cancelled Status = "CANCELLED" - PendingCancel Status = "PENDING_CANCEL" - InsufficientBalance Status = "INSUFFICIENT_BALANCE" - MarketUnavailable Status = "MARKET_UNAVAILABLE" - Rejected Status = "REJECTED" - Expired Status = "EXPIRED" - Hidden Status = "HIDDEN" - UnknownStatus Status = "UNKNOWN" - Open Status = "OPEN" - AutoDeleverage Status = "ADL" - Closed Status = "CLOSED" - Pending Status = "PENDING" + UnknownStatus Status = 0 + AnyStatus Status = 1 << iota + New + Active + PartiallyCancelled + PartiallyFilled + Filled + Cancelled + PendingCancel + InsufficientBalance + MarketUnavailable + Rejected + Expired + Hidden + Open + AutoDeleverage + Closed + Pending + Cancelling ) // Type enforces a standard for order types across the code base -type Type string +type Type uint16 // Defined package order types const ( - AnyType Type = "ANY" - Limit Type = "LIMIT" - Market Type = "MARKET" - PostOnly Type = "POST_ONLY" - ImmediateOrCancel Type = "IMMEDIATE_OR_CANCEL" - Stop Type = "STOP" - StopLimit Type = "STOP LIMIT" - StopMarket Type = "STOP MARKET" - TakeProfit Type = "TAKE PROFIT" - TakeProfitMarket Type = "TAKE PROFIT MARKET" - TrailingStop Type = "TRAILING_STOP" - FillOrKill Type = "FOK" - IOS Type = "IOS" - UnknownType Type = "UNKNOWN" - Liquidation Type = "LIQUIDATION" - Trigger Type = "TRIGGER" + UnknownType Type = 0 + Limit Type = 1 << iota + Market + PostOnly + ImmediateOrCancel + Stop + StopLimit + StopMarket + TakeProfit + TakeProfitMarket + TrailingStop + FillOrKill + IOS + AnyType + Liquidation + Trigger ) // Side enforces a standard for order sides across the code base -type Side string +type Side uint16 // Order side types const ( - AnySide Side = "ANY" - Buy Side = "BUY" - Sell Side = "SELL" - Bid Side = "BID" - Ask Side = "ASK" - UnknownSide Side = "UNKNOWN" - Long Side = "LONG" - Short Side = "SHORT" + UnknownSide Side = 0 + Buy Side = 1 << iota + Sell + Bid + Ask + AnySide + Long + Short + // Backtester signal types + DoNothing + TransferredFunds + CouldNotBuy + CouldNotSell + MissingData ) // ByPrice used for sorting orders by price diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index f581b0d5..bbd67519 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -14,7 +14,22 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/validate" ) -var errTimeInForceConflict = errors.New("multiple time in force options applied") +const ( + orderSubmissionValidSides = Buy | Sell | Bid | Ask | Long | Short + shortSide = Short | Sell | Ask + longSide = Long | Buy | Bid + inactiveStatuses = Filled | Cancelled | InsufficientBalance | MarketUnavailable | Rejected | PartiallyCancelled | Expired | Closed | AnyStatus | Cancelling + activeStatuses = Active | Open | PartiallyFilled | New | PendingCancel | Hidden | AutoDeleverage | Pending + bypassSideFilter = UnknownSide | AnySide + bypassTypeFilter = UnknownType | AnyType +) + +var ( + errTimeInForceConflict = errors.New("multiple time in force options applied") + errUnrecognisedOrderSide = errors.New("unrecognised order side") + errUnrecognisedOrderType = errors.New("unrecognised order type") + errUnrecognisedOrderStatus = errors.New("unrecognised order status") +) // Validate checks the supplied data and returns whether or not it's valid func (s *Submit) Validate(opt ...validate.Checker) error { @@ -30,10 +45,7 @@ func (s *Submit) Validate(opt ...validate.Checker) error { return ErrAssetNotSet } - if s.Side != Buy && - s.Side != Sell && - s.Side != Bid && - s.Side != Ask { + if s.Side == UnknownSide || orderSubmissionValidSides&s.Side != s.Side { return ErrSideIsInvalid } @@ -145,15 +157,15 @@ func (d *Detail) UpdateOrderFromDetail(m *Detail) { d.WalletAddress = m.WalletAddress updated = true } - if m.Type != "" && m.Type != d.Type { + if m.Type != UnknownType && m.Type != d.Type { d.Type = m.Type updated = true } - if m.Side != "" && m.Side != d.Side { + if m.Side != UnknownSide && m.Side != d.Side { d.Side = m.Side updated = true } - if m.Status != "" && m.Status != d.Status { + if m.Status != UnknownStatus && m.Status != d.Status { d.Status = m.Status updated = true } @@ -161,53 +173,51 @@ func (d *Detail) UpdateOrderFromDetail(m *Detail) { d.AssetType = m.AssetType updated = true } - if m.Trades != nil { - 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 - } + for x := range m.Trades { + var found bool + for y := range d.Trades { + if d.Trades[y].TID != m.Trades[x].TID { + continue } - if !found { - d.Trades = append(d.Trades, m.Trades[x]) + 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 } - m.RemainingAmount -= m.Trades[x].Amount } + 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 @@ -309,15 +319,15 @@ func (d *Detail) UpdateOrderFromModify(m *Modify) { d.WalletAddress = m.WalletAddress updated = true } - if m.Type != "" && m.Type != d.Type { + if m.Type != UnknownType && m.Type != d.Type { d.Type = m.Type updated = true } - if m.Side != "" && m.Side != d.Side { + if m.Side != UnknownSide && m.Side != d.Side { d.Side = m.Side updated = true } - if m.Status != "" && m.Status != d.Status { + if m.Status != UnknownStatus && m.Status != d.Status { d.Status = m.Status updated = true } @@ -325,53 +335,51 @@ func (d *Detail) UpdateOrderFromModify(m *Modify) { d.AssetType = m.AssetType updated = true } - if m.Trades != nil { - 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 - } + for x := range m.Trades { + var found bool + for y := range d.Trades { + if d.Trades[y].TID != m.Trades[x].TID { + continue } - if !found { - d.Trades = append(d.Trades, m.Trades[x]) + 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 } - m.RemainingAmount -= m.Trades[x].Amount } + 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 @@ -401,13 +409,13 @@ func (d *Detail) MatchFilter(f *Filter) bool { if f.ID != "" && d.ID != f.ID { return false } - if f.Type != "" && f.Type != AnyType && d.Type != f.Type { + if f.Type != UnknownType && f.Type != AnyType && d.Type != f.Type { return false } - if f.Side != "" && f.Side != AnySide && d.Side != f.Side { + if f.Side != UnknownSide && f.Side != AnySide && d.Side != f.Side { return false } - if f.Status != "" && f.Status != AnyStatus && d.Status != f.Status { + if f.Status != UnknownStatus && f.Status != AnyStatus && d.Status != f.Status { return false } if f.ClientOrderID != "" && d.ClientOrderID != f.ClientOrderID { @@ -428,25 +436,21 @@ func (d *Detail) MatchFilter(f *Filter) bool { return true } -// IsActive returns true if an order has a status that indicates it is -// currently available on the exchange +// IsActive returns true if an order has a status that indicates it is currently +// available on the exchange func (d *Detail) IsActive() bool { - if d.Amount <= 0 || d.Amount <= d.ExecutedAmount { - return false - } - return d.Status == Active || d.Status == Open || d.Status == PartiallyFilled || d.Status == New || - d.Status == AnyStatus || d.Status == PendingCancel || d.Status == Hidden || d.Status == UnknownStatus || - d.Status == AutoDeleverage || d.Status == Pending + return d.Status != UnknownStatus && + d.Amount > 0 && + d.Amount > d.ExecutedAmount && + activeStatuses&d.Status == d.Status } // IsInactive returns true if an order has a status that indicates it is // currently not available on the exchange func (d *Detail) IsInactive() bool { - if d.Amount <= 0 || d.Amount <= d.ExecutedAmount { - return true - } - return d.Status == Filled || d.Status == Cancelled || d.Status == InsufficientBalance || d.Status == MarketUnavailable || - d.Status == Rejected || d.Status == PartiallyCancelled || d.Status == Expired || d.Status == Closed + return d.Amount <= 0 || + d.Amount <= d.ExecutedAmount || + inactiveStatuses&d.Status == d.Status } // GenerateInternalOrderID sets a new V4 order ID or a V5 order ID if @@ -474,50 +478,151 @@ func (d *Detail) Copy() Detail { // String implements the stringer interface func (t Type) String() string { - return string(t) + switch t { + case AnyType: + return "ANY" + case Limit: + return "LIMIT" + case Market: + return "MARKET" + case PostOnly: + return "POST_ONLY" + case ImmediateOrCancel: + return "IMMEDIATE_OR_CANCEL" + case Stop: + return "STOP" + case StopLimit: + return "STOP LIMIT" + case StopMarket: + return "STOP MARKET" + case TakeProfit: + return "TAKE PROFIT" + case TakeProfitMarket: + return "TAKE PROFIT MARKET" + case TrailingStop: + return "TRAILING_STOP" + case FillOrKill: + return "FOK" + case IOS: + return "IOS" + case Liquidation: + return "LIQUIDATION" + case Trigger: + return "TRIGGER" + default: + return "UNKNOWN" + } } // Lower returns the type lower case string func (t Type) Lower() string { - return strings.ToLower(string(t)) + return strings.ToLower(t.String()) } // Title returns the type titleized, eg "Limit" func (t Type) Title() string { - return strings.Title(strings.ToLower(string(t))) // nolint:staticcheck // Ignore Title usage warning + return strings.Title(strings.ToLower(t.String())) // nolint:staticcheck // Ignore Title usage warning } // String implements the stringer interface func (s Side) String() string { - return string(s) + switch s { + case Buy: + return "BUY" + case Sell: + return "SELL" + case Bid: + return "BID" + case Ask: + return "ASK" + case Long: + return "LONG" + case Short: + return "SHORT" + case AnySide: + return "ANY" + // Backtester signal types below. + case DoNothing: + return "DO NOTHING" + case TransferredFunds: + return "TRANSFERRED FUNDS" + case CouldNotBuy: + return "COULD NOT BUY" + case CouldNotSell: + return "COULD NOT SELL" + case MissingData: + return "MISSING DATA" + default: + return "UNKNOWN" + } } // Lower returns the side lower case string func (s Side) Lower() string { - return strings.ToLower(string(s)) + return strings.ToLower(s.String()) } // Title returns the side titleized, eg "Buy" func (s Side) Title() string { - return strings.Title(strings.ToLower(string(s))) // nolint:staticcheck // Ignore Title usage warning + return strings.Title(strings.ToLower(s.String())) // nolint:staticcheck // Ignore Title usage warning } // IsShort returns if the side is short func (s Side) IsShort() bool { - return s == Short || s == Sell + return s != UnknownSide && shortSide&s == s } // IsLong returns if the side is long func (s Side) IsLong() bool { - return s == Long || s == Buy + return s != UnknownSide && longSide&s == s } // String implements the stringer interface func (s Status) String() string { - return string(s) + switch s { + case AnyStatus: + return "ANY" + case New: + return "NEW" + case Active: + return "ACTIVE" + case PartiallyCancelled: + return "PARTIALLY_CANCELLED" + case PartiallyFilled: + return "PARTIALLY_FILLED" + case Filled: + return "FILLED" + case Cancelled: + return "CANCELLED" + case PendingCancel: + return "PENDING_CANCEL" + case InsufficientBalance: + return "INSUFFICIENT_BALANCE" + case MarketUnavailable: + return "MARKET_UNAVAILABLE" + case Rejected: + return "REJECTED" + case Expired: + return "EXPIRED" + case Hidden: + return "HIDDEN" + case Open: + return "OPEN" + case AutoDeleverage: + return "ADL" + case Closed: + return "CLOSED" + case Pending: + return "PENDING" + case Cancelling: + return "CANCELLING" + default: + return "UNKNOWN" + } } -// InferCostsAndTimes infer order costs using execution information and times when available +// InferCostsAndTimes infer order costs using execution information and times +// when available func (d *Detail) InferCostsAndTimes() { if d.CostAsset.IsEmpty() { d.CostAsset = d.Pair.Quote @@ -547,83 +652,86 @@ func (d *Detail) InferCostsAndTimes() { } } -// FilterOrdersBySide removes any order details that don't match the -// order status provided +// FilterOrdersBySide removes any order details that don't match the order +// status provided func FilterOrdersBySide(orders *[]Detail, side Side) { - if side == "" || side == AnySide { + if bypassSideFilter&side == side || len(*orders) == 0 { return } - var filteredOrders []Detail + target := 0 for i := range *orders { - if strings.EqualFold(string((*orders)[i].Side), string(side)) { - filteredOrders = append(filteredOrders, (*orders)[i]) + if (*orders)[i].Side == side { + (*orders)[target] = (*orders)[i] + target++ } } - - *orders = filteredOrders + *orders = (*orders)[:target] } // FilterOrdersByType removes any order details that don't match the order type // provided func FilterOrdersByType(orders *[]Detail, orderType Type) { - if orderType == "" || orderType == AnyType { + if bypassTypeFilter&orderType == orderType || len(*orders) == 0 { return } - var filteredOrders []Detail + target := 0 for i := range *orders { - if strings.EqualFold(string((*orders)[i].Type), string(orderType)) { - filteredOrders = append(filteredOrders, (*orders)[i]) + if (*orders)[i].Type == orderType { + (*orders)[target] = (*orders)[i] + target++ } } - - *orders = filteredOrders + *orders = (*orders)[:target] } // FilterOrdersByTimeRange removes any OrderDetails outside of the time range -func FilterOrdersByTimeRange(orders *[]Detail, startTime, endTime time.Time) { - if startTime.IsZero() || - endTime.IsZero() || - startTime.Unix() == 0 || - endTime.Unix() == 0 || - endTime.Before(startTime) { - return +func FilterOrdersByTimeRange(orders *[]Detail, startTime, endTime time.Time) error { + if len(*orders) == 0 { + return nil } - var filteredOrders []Detail + if err := common.StartEndTimeCheck(startTime, endTime); err != nil { + if errors.Is(err, common.ErrDateUnset) { + return nil + } + return fmt.Errorf("cannot filter orders by time range %w", err) + } + + target := 0 for i := range *orders { if ((*orders)[i].Date.Unix() >= startTime.Unix() && (*orders)[i].Date.Unix() <= endTime.Unix()) || (*orders)[i].Date.IsZero() { - filteredOrders = append(filteredOrders, (*orders)[i]) + (*orders)[target] = (*orders)[i] + target++ } } - - *orders = filteredOrders + *orders = (*orders)[:target] + return nil } -// FilterOrdersByCurrencies removes any order details that do not match the -// provided currency list. It is forgiving in that the provided currencies can -// match quote or base currencies -func FilterOrdersByCurrencies(orders *[]Detail, currencies []currency.Pair) { - if len(currencies) == 0 { - return - } - if len(currencies) == 1 && currencies[0].IsEmpty() { +// FilterOrdersByPairs removes any order details that do not match the +// provided currency pairs list. It is forgiving in that the provided pairs can +// match quote or base pairs +func FilterOrdersByPairs(orders *[]Detail, pairs []currency.Pair) { + if len(pairs) == 0 || + (len(pairs) == 1 && pairs[0].IsEmpty()) || + len(*orders) == 0 { return } - var filteredOrders []Detail - for i := range *orders { - for _, c := range currencies { - if (*orders)[i].Pair.EqualIncludeReciprocal(c) { - filteredOrders = append(filteredOrders, (*orders)[i]) + target := 0 + for x := range *orders { + for y := range pairs { + if (*orders)[x].Pair.EqualIncludeReciprocal(pairs[y]) { + (*orders)[target] = (*orders)[x] + target++ break } } } - - *orders = filteredOrders + *orders = (*orders)[:target] } func (b ByPrice) Len() int { @@ -735,23 +843,23 @@ func SortOrdersBySide(orders *[]Detail, reverse bool) { // and returning a real Side func StringToOrderSide(side string) (Side, error) { side = strings.ToUpper(side) - switch Side(side) { - case Buy: + switch side { + case Buy.String(): return Buy, nil - case Sell: + case Sell.String(): return Sell, nil - case Bid: + case Bid.String(): return Bid, nil - case Ask: + case Ask.String(): return Ask, nil - case Long: + case Long.String(): return Long, nil - case Short: + case Short.String(): return Short, nil - case AnySide: + case AnySide.String(): return AnySide, nil default: - return UnknownSide, errors.New(side + " not recognised as order side") + return UnknownSide, fmt.Errorf("'%s' %w", side, errUnrecognisedOrderSide) } } @@ -783,7 +891,7 @@ func StringToOrderType(oType string) (Type, error) { case Trigger.String(): return Trigger, nil default: - return UnknownType, errors.New(oType + " not recognised as order type") + return UnknownType, fmt.Errorf("'%v' %w", oType, errUnrecognisedOrderType) } } @@ -822,8 +930,10 @@ func StringToOrderStatus(status string) (Status, error) { return InsufficientBalance, nil case MarketUnavailable.String(): return MarketUnavailable, nil + case Cancelling.String(): + return Cancelling, nil default: - return UnknownStatus, errors.New(status + " not recognised as order status") + return UnknownStatus, fmt.Errorf("'%s' %w", status, errUnrecognisedOrderStatus) } } @@ -891,7 +1001,7 @@ func (g *GetOrdersRequest) Validate(opt ...validate.Checker) error { return ErrGetOrdersRequestIsNil } if !g.AssetType.IsValid() { - return fmt.Errorf("assetType %v not supported", g.AssetType) + return fmt.Errorf("%v %w", g.AssetType, asset.ErrNotSupported) } var errs common.Errors for _, o := range opt { @@ -917,7 +1027,7 @@ func (m *Modify) Validate(opt ...validate.Checker) error { return ErrPairIsEmpty } - if m.AssetType.String() == "" { + if m.AssetType == asset.Empty { return ErrAssetNotSet } diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index e5371151..93481f21 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -843,8 +843,13 @@ func (p *Poloniex) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequ return nil, err } for i := range resp.Data[key] { - orderSide := order.Side(strings.ToUpper(resp.Data[key][i].Type)) - orderDate, err := time.Parse(common.SimpleTimeFormat, resp.Data[key][i].Date) + var orderSide order.Side + orderSide, err = order.StringToOrderSide(resp.Data[key][i].Type) + if err != nil { + return nil, err + } + var orderDate time.Time + orderDate, err = time.Parse(common.SimpleTimeFormat, resp.Data[key][i].Date) if err != nil { log.Errorf(log.ExchangeSys, "Exchange %v Func %v Order %v Could not parse date to unix with value of %v", @@ -866,8 +871,11 @@ func (p *Poloniex) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequ } } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) - order.FilterOrdersByCurrencies(&orders, req.Pairs) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", p.Name, err) + } + order.FilterOrdersByPairs(&orders, req.Pairs) order.FilterOrdersBySide(&orders, req.Side) return orders, nil @@ -902,7 +910,10 @@ func (p *Poloniex) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequ } for i := range resp.Data[key] { - orderSide := order.Side(strings.ToUpper(resp.Data[key][i].Type)) + orderSide, err := order.StringToOrderSide(resp.Data[key][i].Type) + if err != nil { + return nil, err + } orderDate, err := time.Parse(common.SimpleTimeFormat, resp.Data[key][i].Date) if err != nil { @@ -931,9 +942,8 @@ func (p *Poloniex) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequ } } - order.FilterOrdersByCurrencies(&orders, req.Pairs) + order.FilterOrdersByPairs(&orders, req.Pairs) order.FilterOrdersBySide(&orders, req.Side) - return orders, nil } diff --git a/exchanges/trade/trade_test.go b/exchanges/trade/trade_test.go index 82de3968..9944f632 100644 --- a/exchanges/trade/trade_test.go +++ b/exchanges/trade/trade_test.go @@ -82,7 +82,6 @@ func TestAddTradesToBuffer(t *testing.T) { AssetType: asset.Spot, Price: -1, Amount: -1, - Side: "", }, }...) if err != nil { diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index 25ade3a5..b54a1017 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -583,11 +583,13 @@ func (y *Yobit) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest } for x := range req.Pairs { - fCurr, err := y.FormatExchangeCurrency(req.Pairs[x], asset.Spot) + var fCurr currency.Pair + fCurr, err = y.FormatExchangeCurrency(req.Pairs[x], asset.Spot) if err != nil { return nil, err } - resp, err := y.GetOpenOrders(ctx, fCurr.String()) + var resp map[string]ActiveOrders + resp, err = y.GetOpenOrders(ctx, fCurr.String()) if err != nil { return nil, err } @@ -598,21 +600,27 @@ func (y *Yobit) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest if err != nil { return nil, err } - orderDate := time.Unix(int64(resp[id].TimestampCreated), 0) - side := order.Side(strings.ToUpper(resp[id].Type)) + var side order.Side + side, err = order.StringToOrderSide(resp[id].Type) + if err != nil { + return nil, err + } orders = append(orders, order.Detail{ ID: id, Amount: resp[id].Amount, Price: resp[id].Rate, Side: side, - Date: orderDate, + Date: time.Unix(int64(resp[id].TimestampCreated), 0), Pair: symbol, Exchange: y.Name, }) } } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", y.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } @@ -660,7 +668,11 @@ func (y *Yobit) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest return nil, err } orderDate := time.Unix(int64(allOrders[i].Timestamp), 0) - side := order.Side(strings.ToUpper(allOrders[i].Type)) + var side order.Side + side, err = order.StringToOrderSide(allOrders[i].Type) + if err != nil { + return nil, err + } detail := order.Detail{ ID: strconv.FormatFloat(allOrders[i].OrderID, 'f', -1, 64), Amount: allOrders[i].Amount, @@ -678,7 +690,6 @@ func (y *Yobit) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest } order.FilterOrdersBySide(&orders, req.Side) - return orders, nil } diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go index ded75c33..b9616218 100644 --- a/exchanges/zb/zb_wrapper.go +++ b/exchanges/zb/zb_wrapper.go @@ -765,7 +765,10 @@ func (z *ZB) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest) ( } } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", z.Name, err) + } order.FilterOrdersBySide(&orders, req.Side) return orders, nil } @@ -778,7 +781,7 @@ func (z *ZB) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest) ( return nil, err } - if req.Side == order.AnySide || req.Side == "" { + if req.Side == order.AnySide { return nil, errors.New("specific order side is required") } @@ -854,7 +857,10 @@ func (z *ZB) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest) ( orders[i] = detail } - order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + err = order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", z.Name, err) + } return orders, nil } diff --git a/gctscript/modules/gct/exchange.go b/gctscript/modules/gct/exchange.go index fb1dbf3c..c24c1773 100644 --- a/gctscript/modules/gct/exchange.go +++ b/gctscript/modules/gct/exchange.go @@ -452,10 +452,20 @@ func ExchangeOrderSubmit(args ...objects.Object) (objects.Object, error) { return nil, err } + side, err := order.StringToOrderSide(orderSide) + if err != nil { + return nil, err + } + + oType, err := order.StringToOrderType(orderType) + if err != nil { + return nil, err + } + tempSubmit := &order.Submit{ Pair: pair, - Type: order.Type(orderType), - Side: order.Side(orderSide), + Type: oType, + Side: side, Price: orderPrice, Amount: orderAmount, ClientID: orderClientID, diff --git a/gctscript/wrappers/validator/validator.go b/gctscript/wrappers/validator/validator.go index 2ad74549..1ec2abc9 100644 --- a/gctscript/wrappers/validator/validator.go +++ b/gctscript/wrappers/validator/validator.go @@ -124,10 +124,10 @@ func (w Wrapper) QueryOrder(ctx context.Context, exch, _ string, _ currency.Pair AccountID: "hello", ID: "1", Pair: pair, - Side: "ask", - Type: "limit", + Side: order.Ask, + Type: order.Limit, Date: time.Now(), - Status: "cancelled", + Status: order.Cancelled, Price: 1, Amount: 2, ExecutedAmount: 1, @@ -139,8 +139,8 @@ func (w Wrapper) QueryOrder(ctx context.Context, exch, _ string, _ currency.Pair Price: 1, Amount: 2, Exchange: exch, - Type: "limit", - Side: "ask", + Type: order.Limit, + Side: order.Ask, Fee: 0, Description: "", },