Skip to content

Commit

Permalink
order: slight optimizations (thrasher-corp#917)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
shazbert and Ryan O'Hara-Reid authored May 6, 2022
1 parent d735eff commit cdcc963
Show file tree
Hide file tree
Showing 60 changed files with 1,375 additions and 802 deletions.
14 changes: 0 additions & 14 deletions backtester/common/common_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 9 additions & 9 deletions backtester/eventhandlers/exchange/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion backtester/eventhandlers/portfolio/holdings/holdings.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand Down
8 changes: 1 addition & 7 deletions backtester/eventhandlers/portfolio/holdings/holdings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
45 changes: 22 additions & 23 deletions backtester/eventhandlers/portfolio/portfolio.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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")
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions backtester/eventhandlers/portfolio/portfolio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}

Expand Down
2 changes: 1 addition & 1 deletion backtester/eventhandlers/statistics/currencystatistics.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -106,7 +105,7 @@ func TestCalculateResults(t *testing.T) {
SignalEvent: &signal.Signal{
Base: even2,
ClosePrice: decimal.NewFromInt(1337),
Direction: common.MissingData,
Direction: order.MissingData,
},
}

Expand Down
13 changes: 7 additions & 6 deletions backtester/eventhandlers/statistics/statistics.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}

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

Expand Down
6 changes: 3 additions & 3 deletions backtester/eventhandlers/strategies/rsi/rsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

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

Expand Down
3 changes: 2 additions & 1 deletion backtester/eventhandlers/strategies/rsi/rsi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit cdcc963

Please sign in to comment.