feature: re-implement heikinashi for elliottwave

This commit is contained in:
zenix 2022-09-05 19:31:52 +09:00
parent 4a878b5596
commit 28802dd107
3 changed files with 76 additions and 16 deletions

View File

@ -25,13 +25,14 @@ exchangeStrategies:
elliottwave:
symbol: BTCUSDT
# kline interval for indicators
interval: 3m
stoploss: 0.15%
interval: 4m
stoploss: 0.5%
windowATR: 14
windowQuick: 3
windowSlow: 13
source: hl2
pendingMinutes: 8
windowQuick: 10
windowSlow: 96
source: hlc3
pendingMinutes: 10
useHeikinAshi: true
drawGraph: true
graphIndicatorPath: "./indicator.png"
@ -107,7 +108,7 @@ sync:
- BTCUSDT
backtest:
startTime: "2022-08-30"
startTime: "2022-08-01"
endTime: "2022-09-30"
symbols:
- BTCUSDT
@ -115,7 +116,7 @@ backtest:
accounts:
binance:
makerFeeRate: 0.000
#takerFeeRate: 0.000
takerFeeRate: 0.000
balances:
BTC: 0
USDT: 5000

View File

@ -0,0 +1,46 @@
package elliottwave
import (
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
type HeikinAshi struct {
Values []types.KLine
size int
}
func NewHeikinAshi(size int) *HeikinAshi {
return &HeikinAshi{
Values: make([]types.KLine, size),
size: size,
}
}
func (inc *HeikinAshi) Last() *types.KLine {
if len(inc.Values) == 0 {
return &types.KLine{}
}
return &inc.Values[len(inc.Values)-1]
}
func (inc *HeikinAshi) Update(kline types.KLine) {
open := kline.Open
cloze := kline.Close
high := kline.High
low := kline.Low
lastOpen := inc.Last().Open
lastClose := inc.Last().Close
newClose := open.Add(high).Add(low).Add(cloze).Div(Four)
newOpen := lastOpen.Add(lastClose).Div(Two)
kline.Close = newClose
kline.Open = newOpen
kline.High = fixedpoint.Max(fixedpoint.Max(high, newOpen), newClose)
kline.Low = fixedpoint.Max(fixedpoint.Min(low, newOpen), newClose)
inc.Values = append(inc.Values, kline)
if len(inc.Values) > inc.size {
inc.Values = inc.Values[len(inc.Values)-inc.size:]
}
}

View File

@ -48,6 +48,7 @@ type Strategy struct {
WindowQuick int `json:"windowQuick"`
WindowSlow int `json:"windowSlow"`
PendingMinutes int `json:"pendingMinutes"`
UseHeikinAshi bool `json:"useHeikinAshi"`
// whether to draw graph or not by the end of backtest
DrawGraph bool `json:"drawGraph"`
@ -61,8 +62,9 @@ type Strategy struct {
*types.ProfitStats `persistence:"profit_stats"`
*types.TradeStats `persistence:"trade_stats"`
ewo *ElliottWave
atr *indicator.ATR
ewo *ElliottWave
atr *indicator.ATR
heikinAshi *HeikinAshi
priceLines *types.Queue
@ -155,12 +157,14 @@ func (s *Strategy) initIndicators(store *bbgo.SerialMarketDataStore) error {
return errors.New("klines not exists")
}
s.startTime = (*klines)[klineLength-1].EndTime.Time()
s.heikinAshi = NewHeikinAshi(500)
for _, kline := range *klines {
source := s.GetSource(&kline).Float64()
s.ewo.Update(source)
s.atr.PushK(kline)
s.priceLines.Update(source)
s.heikinAshi.Update(kline)
}
return nil
}
@ -418,10 +422,17 @@ func (s *Strategy) klineHandler1m(ctx context.Context, kline types.KLine) {
}
func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
s.heikinAshi.Update(kline)
source := s.GetSource(&kline)
sourcef := source.Float64()
s.priceLines.Update(sourcef)
s.ewo.Update(sourcef)
if s.UseHeikinAshi {
source := s.GetSource(s.heikinAshi.Last())
sourcef := source.Float64()
s.ewo.Update(sourcef)
} else {
s.ewo.Update(sourcef)
}
s.atr.PushK(kline)
if s.Status != types.StrategyStatusRunning {
@ -437,9 +448,11 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
s.smartCancel(ctx, pricef)
atr := s.atr.Last()
ewo := types.Array(s.ewo, 3)
shortCondition := ewo[0] < ewo[1] && ewo[1] > ewo[2] || s.sellPrice == 0 && ewo[0] < ewo[1] && ewo[1] < ewo[2]
longCondition := ewo[0] > ewo[1] && ewo[1] < ewo[2] || s.buyPrice == 0 && ewo[0] > ewo[1] && ewo[1] > ewo[2]
ewo := types.Array(s.ewo, 4)
bull := kline.Close.Compare(kline.Open) > 0
shortCondition := ewo[0] < ewo[1] && ewo[1] >= ewo[2] && (ewo[1] <= ewo[2] || ewo[2] >= ewo[3]) || s.sellPrice == 0 && ewo[0] < ewo[1] && ewo[1] < ewo[2]
longCondition := ewo[0] > ewo[1] && ewo[1] <= ewo[2] && (ewo[1] >= ewo[2] || ewo[2] <= ewo[3]) || s.buyPrice == 0 && ewo[0] > ewo[1] && ewo[1] > ewo[2]
exitShortCondition := s.sellPrice > 0 && !shortCondition && s.sellPrice*(1.+stoploss) <= highf || s.sellPrice+atr <= sourcef || s.trailingCheck(highf, "short")
exitLongCondition := s.buyPrice > 0 && !longCondition && s.buyPrice*(1.-stoploss) >= lowf || s.buyPrice-atr >= sourcef || s.trailingCheck(lowf, "long")
@ -451,7 +464,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
}
s.ClosePosition(ctx, fixedpoint.One)
}
if longCondition {
if longCondition && bull {
if err := s.GeneralOrderExecutor.GracefulCancel(ctx); err != nil {
log.WithError(err).Errorf("cannot cancel orders")
return
@ -487,7 +500,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
s.orderPendingCounter[createdOrders[0].OrderID] = s.minutesCounter
return
}
if shortCondition {
if shortCondition && !bull {
if err := s.GeneralOrderExecutor.GracefulCancel(ctx); err != nil {
log.WithError(err).Errorf("cannot cancel orders")
return