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 <ryan.oharareid@thrasher.io>
This commit is contained in:
Ryan O'Hara-Reid
2022-05-06 12:27:21 +10:00
committed by GitHub
parent d735effc8e
commit cdcc9630de
60 changed files with 1375 additions and 802 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -82,7 +82,6 @@ func TestAddTradesToBuffer(t *testing.T) {
AssetType: asset.Spot,
Price: -1,
Amount: -1,
Side: "",
},
}...)
if err != nil {

View File

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

View File

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

View File

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

View File

@@ -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: "",
},