mirror of
https://github.com/c9s/bbgo.git
synced 2024-09-20 00:01:09 +00:00
strategy: improve harmonic by adding HMM filter to denoise shark signal
strategy: improve harmonic by adding HMM filter to denoise shark signal
This commit is contained in:
parent
b80ac89486
commit
c8aa4ae400
|
@ -16,8 +16,8 @@ exchangeStrategies:
|
||||||
harmonic:
|
harmonic:
|
||||||
symbol: BTCBUSD
|
symbol: BTCBUSD
|
||||||
interval: 1s
|
interval: 1s
|
||||||
window: 500
|
window: 60
|
||||||
quantity: 0.05
|
quantity: 0.005
|
||||||
# Draw pnl
|
# Draw pnl
|
||||||
drawGraph: true
|
drawGraph: true
|
||||||
graphPNLPath: "./pnl.png"
|
graphPNLPath: "./pnl.png"
|
||||||
|
@ -26,12 +26,12 @@ exchangeStrategies:
|
||||||
backtest:
|
backtest:
|
||||||
sessions:
|
sessions:
|
||||||
- binance
|
- binance
|
||||||
startTime: "2022-09-30"
|
startTime: "2022-10-01"
|
||||||
endTime: "2022-10-01"
|
endTime: "2022-10-07"
|
||||||
symbols:
|
symbols:
|
||||||
- BTCBUSD
|
- BTCBUSD
|
||||||
accounts:
|
accounts:
|
||||||
binance:
|
binance:
|
||||||
balances:
|
balances:
|
||||||
BTC: 1.0
|
BTC: 1.0
|
||||||
BUSD: 40_000.0
|
BUSD: 60_000.0
|
|
@ -3,14 +3,17 @@ package harmonic
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/bbgo"
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
"github.com/c9s/bbgo/pkg/data/tsv"
|
"github.com/c9s/bbgo/pkg/data/tsv"
|
||||||
"github.com/c9s/bbgo/pkg/datatype/floats"
|
"github.com/c9s/bbgo/pkg/datatype/floats"
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
"github.com/c9s/bbgo/pkg/indicator"
|
"github.com/c9s/bbgo/pkg/indicator"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
floats2 "gonum.org/v1/gonum/floats"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ID = "harmonic"
|
const ID = "harmonic"
|
||||||
|
@ -317,6 +320,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
s.InitDrawCommands(&profitSlice, &cumProfitSlice)
|
||||||
s.orderExecutor.TradeCollector().OnPositionUpdate(func(position *types.Position) {
|
s.orderExecutor.TradeCollector().OnPositionUpdate(func(position *types.Position) {
|
||||||
bbgo.Sync(ctx, s)
|
bbgo.Sync(ctx, s)
|
||||||
})
|
})
|
||||||
|
@ -332,66 +336,131 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
if klines, ok := kLineStore.KLinesOfInterval(s.shark.Interval); ok {
|
if klines, ok := kLineStore.KLinesOfInterval(s.shark.Interval); ok {
|
||||||
s.shark.LoadK((*klines)[0:])
|
s.shark.LoadK((*klines)[0:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
states := types.NewQueue(s.Window)
|
||||||
|
|
||||||
|
states.Update(0)
|
||||||
s.session.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, s.Interval, func(kline types.KLine) {
|
s.session.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, s.Interval, func(kline types.KLine) {
|
||||||
|
|
||||||
log.Infof("Shark Score: %f, Current Price: %f", s.shark.Last(), kline.Close.Float64())
|
log.Infof("Shark Score: %f, Current Price: %f", s.shark.Last(), kline.Close.Float64())
|
||||||
|
|
||||||
//previousRegime := s.shark.Values.Tail(10).Mean()
|
nextState := alpha(s.shark.Array(s.Window), states.Array(s.Window), s.Window)
|
||||||
//zeroThreshold := 5.
|
states.Update(nextState)
|
||||||
|
log.Infof("Denoised signal via HMM: %f", states.Last())
|
||||||
|
|
||||||
if s.shark.Rank(s.Window).Last()/float64(s.Window) > 0.99 { // && ((previousRegime < zeroThreshold && previousRegime > -zeroThreshold) || s.shark.Index(1) < 0)
|
if states.Length() < s.Window {
|
||||||
if s.Position.IsShort() {
|
return
|
||||||
_ = s.orderExecutor.GracefulCancel(ctx)
|
}
|
||||||
s.orderExecutor.ClosePosition(ctx, fixedpoint.One, "close short position")
|
direction := 0.
|
||||||
}
|
if s.Position.IsLong() {
|
||||||
_, err := s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
direction = 1.
|
||||||
|
} else if s.Position.IsShort() {
|
||||||
|
direction = -1.
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Position.IsOpened(kline.Close) && states.Mean(5) == 0 {
|
||||||
|
s.orderExecutor.ClosePosition(ctx, fixedpoint.One)
|
||||||
|
}
|
||||||
|
if states.Mean(5) == 1 && direction != 1 {
|
||||||
|
_, _ = s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
||||||
Symbol: s.Symbol,
|
Symbol: s.Symbol,
|
||||||
Side: types.SideTypeBuy,
|
Side: types.SideTypeBuy,
|
||||||
Quantity: s.Quantity,
|
Quantity: s.Quantity,
|
||||||
Type: types.OrderTypeMarket,
|
Type: types.OrderTypeMarket,
|
||||||
Tag: "shark long: buy in",
|
Tag: "shark long",
|
||||||
})
|
})
|
||||||
if err == nil {
|
} else if states.Mean(5) == -1 && direction != -1 {
|
||||||
_, err = s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
_, _ = s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
||||||
Symbol: s.Symbol,
|
|
||||||
Side: types.SideTypeSell,
|
|
||||||
Quantity: s.Quantity,
|
|
||||||
Price: fixedpoint.NewFromFloat(s.shark.Highs.Tail(100).Max()),
|
|
||||||
Type: types.OrderTypeLimit,
|
|
||||||
Tag: "shark long: sell back",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if s.shark.Rank(s.Window).Last()/float64(s.Window) < 0.01 { // && ((previousRegime < zeroThreshold && previousRegime > -zeroThreshold) || s.shark.Index(1) > 0)
|
|
||||||
if s.Position.IsLong() {
|
|
||||||
_ = s.orderExecutor.GracefulCancel(ctx)
|
|
||||||
s.orderExecutor.ClosePosition(ctx, fixedpoint.One, "close long position")
|
|
||||||
}
|
|
||||||
_, err := s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
|
||||||
Symbol: s.Symbol,
|
Symbol: s.Symbol,
|
||||||
Side: types.SideTypeSell,
|
Side: types.SideTypeSell,
|
||||||
Quantity: s.Quantity,
|
Quantity: s.Quantity,
|
||||||
Type: types.OrderTypeMarket,
|
Type: types.OrderTypeMarket,
|
||||||
Tag: "shark short: sell in",
|
Tag: "shark short",
|
||||||
})
|
})
|
||||||
if err == nil {
|
|
||||||
_, err = s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
|
||||||
Symbol: s.Symbol,
|
|
||||||
Side: types.SideTypeBuy,
|
|
||||||
Quantity: s.Quantity,
|
|
||||||
Price: fixedpoint.NewFromFloat(s.shark.Lows.Tail(100).Min()),
|
|
||||||
Type: types.OrderTypeLimit,
|
|
||||||
Tag: "shark short: buy back",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
bbgo.OnShutdown(ctx, func(ctx context.Context, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
// Output accumulated profit report
|
||||||
|
if bbgo.IsBackTesting {
|
||||||
|
defer s.AccumulatedProfitReport.Output(s.Symbol)
|
||||||
|
|
||||||
|
if s.DrawGraph {
|
||||||
|
if err := s.Draw(&profitSlice, &cumProfitSlice); err != nil {
|
||||||
|
log.WithError(err).Errorf("cannot draw graph")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = fmt.Fprintln(os.Stderr, s.TradeStats.String())
|
||||||
|
_ = s.orderExecutor.GracefulCancel(ctx)
|
||||||
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: dirichlet distribution is a too naive solution
|
||||||
|
func observationDistribution(y_t, x_t float64) float64 {
|
||||||
|
if x_t == 0. && y_t == 0 {
|
||||||
|
// observed zero value from indicator when in neutral state
|
||||||
|
return 1.
|
||||||
|
} else if x_t > 0. && y_t > 0. {
|
||||||
|
// observed positive value from indicator when in long state
|
||||||
|
return 1.
|
||||||
|
} else if x_t < 0. && y_t < 0. {
|
||||||
|
// observed negative value from indicator when in short state
|
||||||
|
return 1.
|
||||||
|
} else {
|
||||||
|
return 0.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func transitionProbability(x_t0, x_t1 int) float64 {
|
||||||
|
// stick to the same sate
|
||||||
|
if x_t0 == x_t1 {
|
||||||
|
return 0.99
|
||||||
|
}
|
||||||
|
// transit to next new state
|
||||||
|
return 1 - 0.99
|
||||||
|
}
|
||||||
|
|
||||||
|
func alpha(y_t []float64, x_t []float64, l int) float64 {
|
||||||
|
al := make([]float64, l)
|
||||||
|
an := make([]float64, l)
|
||||||
|
as := make([]float64, l)
|
||||||
|
long := 0.
|
||||||
|
neut := 0.
|
||||||
|
short := 0.
|
||||||
|
// n is the incremental time steps
|
||||||
|
for n := 2; n <= len(x_t); n++ {
|
||||||
|
for j := -1; j <= 1; j++ {
|
||||||
|
sil := make([]float64, 3)
|
||||||
|
sin := make([]float64, 3)
|
||||||
|
sis := make([]float64, 3)
|
||||||
|
for i := -1; i <= 1; i++ {
|
||||||
|
sil = append(sil, x_t[n-1-1]*transitionProbability(i, j))
|
||||||
|
sin = append(sin, x_t[n-1-1]*transitionProbability(i, j))
|
||||||
|
sis = append(sis, x_t[n-1-1]*transitionProbability(i, j))
|
||||||
|
}
|
||||||
|
if j > 0 {
|
||||||
|
long = floats2.Max(sil) * observationDistribution(y_t[n-1], float64(j))
|
||||||
|
al = append(al, long)
|
||||||
|
} else if j == 0 {
|
||||||
|
neut = floats2.Max(sin) * observationDistribution(y_t[n-1], float64(j))
|
||||||
|
an = append(an, neut)
|
||||||
|
} else if j < 0 {
|
||||||
|
short = floats2.Max(sis) * observationDistribution(y_t[n-1], float64(j))
|
||||||
|
as = append(as, short)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maximum := floats2.Max([]float64{long, neut, short})
|
||||||
|
if maximum == long {
|
||||||
|
return 1
|
||||||
|
} else if maximum == short {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user