Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

strategy:irr: a mean reversion based on box of klines in same direction #989

Merged
merged 11 commits into from
Nov 2, 2022
27 changes: 11 additions & 16 deletions config/irr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,23 @@ sessions:
binance:
exchange: binance
envVarPrefix: binance
max:
exchange: max
envVarPrefix: max
ftx:
exchange: ftx
envVarPrefix: ftx

exchangeStrategies:
- on: binance
irr:
symbol: BTCBUSD
interval: 1m
window: 120
amount: 5_000.0
# in milliseconds(ms)
# must > 10 ms
hftInterval: 1000
# qty per trade
quantity: 0.001
# Draw pnl
drawGraph: true
graphPNLPath: "./pnl.png"
graphCumPNLPath: "./cumpnl.png"

backtest:
sessions:
- binance
startTime: "2022-09-01"
endTime: "2022-10-04"
symbols:
- BTCBUSD
accounts:
binance:
takerFeeRate: 0.0
balances:
BUSD: 5_000.0
29 changes: 21 additions & 8 deletions pkg/strategy/irr/draw.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,36 @@ import (
"github.com/wcharczuk/go-chart/v2"
)

func (s *Strategy) InitDrawCommands(profit, cumProfit types.Series) {
bbgo.RegisterCommand("/pnl", "Draw PNL(%) per trade", func(reply interact.Reply) {
func (s *Strategy) InitDrawCommands(profit, cumProfit, cumProfitDollar types.Series) {
bbgo.RegisterCommand("/rt", "Draw Return Rate(%) Per Trade", func(reply interact.Reply) {

canvas := DrawPNL(s.InstanceID(), profit)
var buffer bytes.Buffer
if err := canvas.Render(chart.PNG, &buffer); err != nil {
log.WithError(err).Errorf("cannot render pnl in drift")
reply.Message(fmt.Sprintf("[error] cannot render pnl in ewo: %v", err))
log.WithError(err).Errorf("cannot render return in irr")
reply.Message(fmt.Sprintf("[error] cannot render return in irr: %v", err))
return
}
bbgo.SendPhoto(&buffer)
})
bbgo.RegisterCommand("/cumpnl", "Draw Cummulative PNL(Quote)", func(reply interact.Reply) {
bbgo.RegisterCommand("/nav", "Draw Net Assets Value", func(reply interact.Reply) {

canvas := DrawCumPNL(s.InstanceID(), cumProfit)
var buffer bytes.Buffer
if err := canvas.Render(chart.PNG, &buffer); err != nil {
log.WithError(err).Errorf("cannot render cumpnl in drift")
reply.Message(fmt.Sprintf("[error] canot render cumpnl in drift: %v", err))
log.WithError(err).Errorf("cannot render nav in irr")
reply.Message(fmt.Sprintf("[error] canot render nav in irr: %v", err))
return
}
bbgo.SendPhoto(&buffer)
})
bbgo.RegisterCommand("/pnl", "Draw Cumulative Profit & Loss", func(reply interact.Reply) {

canvas := DrawCumPNL(s.InstanceID(), cumProfitDollar)
var buffer bytes.Buffer
if err := canvas.Render(chart.PNG, &buffer); err != nil {
log.WithError(err).Errorf("cannot render pnl in irr")
reply.Message(fmt.Sprintf("[error] canot render pnl in irr: %v", err))
return
}
bbgo.SendPhoto(&buffer)
Expand Down Expand Up @@ -77,7 +90,7 @@ func DrawPNL(instanceID string, profit types.Series) *types.Canvas {

func DrawCumPNL(instanceID string, cumProfit types.Series) *types.Canvas {
canvas := types.NewCanvas(instanceID)
canvas.PlotRaw("cummulative pnl", cumProfit, cumProfit.Length())
canvas.PlotRaw("cumulative pnl", cumProfit, cumProfit.Length())
canvas.YAxis = chart.YAxis{
ValueFormatter: func(v interface{}) string {
if vf, isFloat := v.(float64); isFloat {
Expand Down
24 changes: 8 additions & 16 deletions pkg/strategy/irr/neg_return_rate.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,21 @@ type NRR struct {

var _ types.SeriesExtend = &NRR{}

func (inc *NRR) Update(price float64) {
func (inc *NRR) Update(openPrice, closePrice float64) {
if inc.SeriesBase.Series == nil {
inc.SeriesBase.Series = inc
inc.Prices = types.NewQueue(inc.Window)
}
inc.Prices.Update(price)
inc.Prices.Update(closePrice)
if inc.Prices.Length() < inc.Window {
return
}
irr := (inc.Prices.Last() / inc.Prices.Index(inc.Window-1)) - 1
// D0
irr := openPrice - closePrice
// D1
// -1*((inc.Prices.Last() / inc.Prices.Index(inc.Window-1)) - 1)

inc.Values.Push(-irr) // neg ret here
inc.Values.Push(irr) // neg ret here
inc.RankedValues.Push(inc.Rank(inc.RankingWindow).Last() / float64(inc.RankingWindow)) // ranked neg ret here

}
Expand Down Expand Up @@ -75,7 +78,7 @@ func (inc *NRR) PushK(k types.KLine) {
return
}

inc.Update(indicator.KLineClosePriceMapper(k))
inc.Update(indicator.KLineOpenPriceMapper(k), indicator.KLineClosePriceMapper(k))
inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last())
}
Expand All @@ -86,14 +89,3 @@ func (inc *NRR) LoadK(allKLines []types.KLine) {
}
inc.EmitUpdate(inc.Last())
}

//func calculateReturn(klines []types.KLine, window int, val KLineValueMapper) (float64, error) {
// length := len(klines)
// if length == 0 || length < window {
// return 0.0, fmt.Errorf("insufficient elements for calculating VOL with window = %d", window)
// }
//
// rate := val(klines[length-1])/val(klines[length-2]) - 1
//
// return rate, nil
//}
Loading