fix: wrong balance, wrong bottom/peak, feature: stdev

This commit is contained in:
zenix 2022-05-11 20:56:56 +09:00 committed by Austin Liu
parent 88cbafe936
commit 5fa9e930d3
2 changed files with 61 additions and 52 deletions

View File

@ -195,6 +195,24 @@ func (s *Strategy) SetupIndicators() {
if s.UseHeikinAshi { if s.UseHeikinAshi {
s.heikinAshi = NewHeikinAshi(50) s.heikinAshi = NewHeikinAshi(50)
store.OnKLineWindowUpdate(func(interval types.Interval, window types.KLineWindow) { store.OnKLineWindowUpdate(func(interval types.Interval, window types.KLineWindow) {
if interval == s.atr.Interval {
if s.atr.RMA == nil {
for _, kline := range window {
s.atr.Update(
kline.High.Float64(),
kline.Low.Float64(),
kline.Close.Float64(),
)
}
} else {
kline := window[len(window)-1]
s.atr.Update(
kline.High.Float64(),
kline.Low.Float64(),
kline.Close.Float64(),
)
}
}
if s.Interval != interval { if s.Interval != interval {
return return
} }
@ -224,10 +242,6 @@ func (s *Strategy) SetupIndicators() {
ema5.Update(cloze) ema5.Update(cloze)
ema34.Update(cloze) ema34.Update(cloze)
} }
s.atr.Update(
s.heikinAshi.High.Last(),
s.heikinAshi.Low.Last(),
s.heikinAshi.Close.Last())
}) })
s.ma5 = ema5 s.ma5 = ema5
s.ma34 = ema34 s.ma34 = ema34
@ -249,10 +263,6 @@ func (s *Strategy) SetupIndicators() {
sma5.Update(cloze) sma5.Update(cloze)
sma34.Update(cloze) sma34.Update(cloze)
} }
s.atr.Update(
s.heikinAshi.High.Last(),
s.heikinAshi.Low.Last(),
s.heikinAshi.Close.Last())
}) })
s.ma5 = sma5 s.ma5 = sma5
s.ma34 = sma34 s.ma34 = sma34
@ -282,10 +292,6 @@ func (s *Strategy) SetupIndicators() {
evwma5.UpdateVal(price, vol) evwma5.UpdateVal(price, vol)
evwma34.UpdateVal(price, vol) evwma34.UpdateVal(price, vol)
} }
s.atr.Update(
s.heikinAshi.High.Last(),
s.heikinAshi.Low.Last(),
s.heikinAshi.Close.Last())
}) })
s.ma5 = evwma5 s.ma5 = evwma5
s.ma34 = evwma34 s.ma34 = evwma34
@ -321,20 +327,10 @@ func (s *Strategy) SetupIndicators() {
for _, kline := range window { for _, kline := range window {
evwma5.Update(kline) evwma5.Update(kline)
evwma34.Update(kline) evwma34.Update(kline)
s.atr.Update(
kline.High.Float64(),
kline.Low.Float64(),
kline.Close.Float64(),
)
} }
} else { } else {
evwma5.Update(window[len(window)-1]) evwma5.Update(window[len(window)-1])
evwma34.Update(window[len(window)-1]) evwma34.Update(window[len(window)-1])
s.atr.Update(
window[len(window)-1].High.Float64(),
window[len(window)-1].Low.Float64(),
window[len(window)-1].Close.Float64(),
)
} }
}) })
s.ma5 = evwma5 s.ma5 = evwma5
@ -477,8 +473,8 @@ func (s *Strategy) validateOrder(order *types.SubmitOrder) bool {
// * buy signal on crossover // * buy signal on crossover
// * sell signal on crossunder // * sell signal on crossunder
// - and filtered by the following rules: // - and filtered by the following rules:
// * buy: prev buy signal ON and current sell signal OFF, kline Close > Open, Close > ma(Window=5), ewo > Mean(ewo, Window=5) // * buy: prev buy signal ON and current sell signal OFF, kline Close > Open, Close > ma(Window=5), ewo > Mean(ewo, Window=10) + 2 * Stdev(ewo, Window=10)
// * sell: prev buy signal OFF and current sell signal ON, kline Close < Open, Close < ma(Window=5), ewo < Mean(ewo, Window=5) // * sell: prev buy signal OFF and current sell signal ON, kline Close < Open, Close < ma(Window=5), ewo < Mean(ewo, Window=10) - 2 * Stdev(ewo, Window=10)
// Cancel and repost on non-fully filed orders every 1m within Window=1 // Cancel and repost on non-fully filed orders every 1m within Window=1
// //
// ps: kline might refer to heikinashi or normal ohlc // ps: kline might refer to heikinashi or normal ohlc
@ -503,19 +499,24 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
if !profit.IsZero() { if !profit.IsZero() {
log.Warnf("generate profit: %v, netprofit: %v, trade: %v", profit, netprofit, trade) log.Warnf("generate profit: %v, netprofit: %v, trade: %v", profit, netprofit, trade)
} }
balances := session.GetAccount().Balances()
baseBalance := balances[s.Market.BaseCurrency].Available
quoteBalance := balances[s.Market.QuoteCurrency].Available
if trade.Side == types.SideTypeBuy { if trade.Side == types.SideTypeBuy {
if sellPrice.IsZero() { if baseBalance.IsZero() {
buyPrice = trade.Price
s.peakPrice = trade.Price
} else {
sellPrice = fixedpoint.Zero sellPrice = fixedpoint.Zero
} }
if !quoteBalance.IsZero() {
buyPrice = trade.Price
s.peakPrice = trade.Price
}
} else if trade.Side == types.SideTypeSell { } else if trade.Side == types.SideTypeSell {
if buyPrice.IsZero() { if quoteBalance.IsZero() {
buyPrice = fixedpoint.Zero
}
if !baseBalance.IsZero() {
sellPrice = trade.Price sellPrice = trade.Price
s.bottomPrice = trade.Price s.bottomPrice = trade.Price
} else {
buyPrice = fixedpoint.Zero
} }
} }
}) })
@ -539,9 +540,6 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
log.Errorf("cannot get last price") log.Errorf("cannot get last price")
return return
} }
balances := session.GetAccount().Balances()
baseBalance := balances[s.Market.BaseCurrency].Available
quoteBalance := balances[s.Market.QuoteCurrency].Available
// cancel non-traded orders // cancel non-traded orders
var toCancel []types.Order var toCancel []types.Order
@ -559,15 +557,21 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
s.tradeCollector.Process() s.tradeCollector.Process()
} }
balances := session.GetAccount().Balances()
baseBalance := balances[s.Market.BaseCurrency].Available
quoteBalance := balances[s.Market.QuoteCurrency].Available
atrx2 := fixedpoint.NewFromFloat(s.atr.Last() * 2)
// well, only track prices on 1m // well, only track prices on 1m
if kline.Interval == types.Interval1m { if kline.Interval == types.Interval1m {
for _, order := range toCancel { for _, order := range toCancel {
if order.Side == types.SideTypeBuy && order.Price.Compare(kline.Low) < 0 { if order.Side == types.SideTypeBuy {
newPrice := lastPrice newPrice := lastPrice
order.Quantity = order.Quantity.Mul(order.Price).Div(newPrice) order.Quantity = order.Quantity.Mul(order.Price).Div(newPrice)
order.Price = newPrice order.Price = newPrice
toRepost = append(toRepost, order.SubmitOrder) toRepost = append(toRepost, order.SubmitOrder)
} else if order.Side == types.SideTypeSell && order.Price.Compare(kline.High) > 0 { } else if order.Side == types.SideTypeSell {
newPrice := lastPrice newPrice := lastPrice
order.Price = newPrice order.Price = newPrice
toRepost = append(toRepost, order.SubmitOrder) toRepost = append(toRepost, order.SubmitOrder)
@ -585,24 +589,18 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
} }
sellall := false sellall := false
buyall := false buyall := false
if !baseBalance.IsZero() { if !buyPrice.IsZero() {
if s.peakPrice.IsZero() && !buyPrice.IsZero() { if s.peakPrice.IsZero() || s.peakPrice.Compare(kline.High) < 0 {
s.peakPrice = kline.High
} else if s.peakPrice.Compare(kline.High) < 0 {
s.peakPrice = kline.High s.peakPrice = kline.High
} }
} }
if !quoteBalance.IsZero() { if !sellPrice.IsZero() {
if s.bottomPrice.IsZero() && !sellPrice.IsZero() { if s.bottomPrice.IsZero() || s.bottomPrice.Compare(kline.Low) > 0 {
s.bottomPrice = kline.Low
} else if s.bottomPrice.Compare(kline.Low) > 0 {
s.bottomPrice = kline.Low s.bottomPrice = kline.Low
} }
} }
atrx2 := fixedpoint.NewFromFloat(s.atr.Last() * 2)
takeProfit := false takeProfit := false
peakBack := s.peakPrice peakBack := s.peakPrice
bottomBack := s.bottomPrice bottomBack := s.bottomPrice
@ -696,7 +694,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
} }
// To get the threshold for ewo // To get the threshold for ewo
mean := types.Mean(types.Abs(s.ewo), 5) mean := types.Mean(s.ewo, 10)
std := types.Stdev(s.ewo, 10)
longSignal := types.CrossOver(s.ewo, s.ewoSignal) longSignal := types.CrossOver(s.ewo, s.ewoSignal)
shortSignal := types.CrossUnder(s.ewo, s.ewoSignal) shortSignal := types.CrossUnder(s.ewo, s.ewoSignal)
@ -712,9 +711,9 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
breakDown = kline.Close.Float64() < s.ma5.Last() breakDown = kline.Close.Float64() < s.ma5.Last()
} }
// kline breakthrough ma5, ma50 trend up, and ewo > threshold // kline breakthrough ma5, ma50 trend up, and ewo > threshold
IsBull := bull && breakThrough && s.ewo.Last() >= mean IsBull := bull && breakThrough && s.ewo.Last() >= mean+2*std
// kline downthrough ma5, ma50 trend down, and ewo < threshold // kline downthrough ma5, ma50 trend down, and ewo < threshold
IsBear := !bull && breakDown && s.ewo.Last() <= -mean IsBear := !bull && breakDown && s.ewo.Last() <= mean-2*std
var orders []types.SubmitOrder var orders []types.SubmitOrder
var price fixedpoint.Value var price fixedpoint.Value
@ -742,7 +741,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
} }
if s.validateOrder(&order) { if s.validateOrder(&order) {
// strong long // strong long
log.Warnf("long at %v, timestamp: %s", price, kline.StartTime) log.Warnf("long at %v, atrx2 %v, timestamp: %s", price, atrx2, kline.StartTime)
orders = append(orders, order) orders = append(orders, order)
} }
@ -764,7 +763,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
TimeInForce: types.TimeInForceGTC, TimeInForce: types.TimeInForceGTC,
} }
if s.validateOrder(&order) { if s.validateOrder(&order) {
log.Warnf("short at %v, timestamp: %s", price, kline.StartTime) log.Warnf("short at %v, atrx2 %v, timestamp: %s", price, atrx2, kline.StartTime)
orders = append(orders, order) orders = append(orders, order)
} }
} }

View File

@ -509,4 +509,14 @@ func Change(a Series, offset ...int) Series {
return &ChangeResult{a, o} return &ChangeResult{a, o}
} }
func Stdev(a Series, length int) float64 {
avg := Mean(a, length)
s := .0
for i := 0; i < length; i++ {
diff := a.Index(i) - avg
s += diff * diff
}
return math.Sqrt(s / float64(length))
}
// TODO: ta.linreg // TODO: ta.linreg