mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
Merge pull request #543 from austin362667/strategy/factorzoo
strategy: factorzoo
This commit is contained in:
commit
1dea293fca
30
config/factorzoo.yaml
Normal file
30
config/factorzoo.yaml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
sessions:
|
||||||
|
binance:
|
||||||
|
exchange: binance
|
||||||
|
envVarPrefix: binance
|
||||||
|
# futures: true
|
||||||
|
|
||||||
|
|
||||||
|
exchangeStrategies:
|
||||||
|
- on: binance
|
||||||
|
factorzoo:
|
||||||
|
symbol: BTCUSDT
|
||||||
|
interval: 12h # T:20/12h
|
||||||
|
quantity: 0.95
|
||||||
|
|
||||||
|
|
||||||
|
backtest:
|
||||||
|
sessions:
|
||||||
|
- binance
|
||||||
|
# for testing max draw down (MDD) at 03-12
|
||||||
|
# see here for more details
|
||||||
|
# https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp
|
||||||
|
startTime: "2022-03-15"
|
||||||
|
endTime: "2022-04-13"
|
||||||
|
symbols:
|
||||||
|
- BTCUSDT
|
||||||
|
account:
|
||||||
|
binance:
|
||||||
|
balances:
|
||||||
|
BTC: 1.0
|
||||||
|
USDT: 45_000.0
|
1
go.mod
1
go.mod
|
@ -88,6 +88,7 @@ require (
|
||||||
github.com/prometheus/common v0.32.1 // indirect
|
github.com/prometheus/common v0.32.1 // indirect
|
||||||
github.com/prometheus/procfs v0.7.3 // indirect
|
github.com/prometheus/procfs v0.7.3 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||||
|
github.com/sajari/regression v1.0.1 // indirect
|
||||||
github.com/shopspring/decimal v1.2.0 // indirect
|
github.com/shopspring/decimal v1.2.0 // indirect
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||||
github.com/spf13/afero v1.5.1 // indirect
|
github.com/spf13/afero v1.5.1 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -412,6 +412,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/sajari/regression v1.0.1 h1:iTVc6ZACGCkoXC+8NdqH5tIreslDTT/bXxT6OmHR5PE=
|
||||||
|
github.com/sajari/regression v1.0.1/go.mod h1:NeG/XTW1lYfGY7YV/Z0nYDV/RGh3wxwd1yW46835flM=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/bollmaker"
|
_ "github.com/c9s/bbgo/pkg/strategy/bollmaker"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/emastop"
|
_ "github.com/c9s/bbgo/pkg/strategy/emastop"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/etf"
|
_ "github.com/c9s/bbgo/pkg/strategy/etf"
|
||||||
|
_ "github.com/c9s/bbgo/pkg/strategy/ewoDgtrd"
|
||||||
|
_ "github.com/c9s/bbgo/pkg/strategy/factorzoo"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/flashcrash"
|
_ "github.com/c9s/bbgo/pkg/strategy/flashcrash"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/funding"
|
_ "github.com/c9s/bbgo/pkg/strategy/funding"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/grid"
|
_ "github.com/c9s/bbgo/pkg/strategy/grid"
|
||||||
|
@ -23,5 +25,4 @@ import (
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/xmaker"
|
_ "github.com/c9s/bbgo/pkg/strategy/xmaker"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/xnav"
|
_ "github.com/c9s/bbgo/pkg/strategy/xnav"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/xpuremaker"
|
_ "github.com/c9s/bbgo/pkg/strategy/xpuremaker"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/ewoDgtrd"
|
|
||||||
)
|
)
|
||||||
|
|
103
pkg/strategy/factorzoo/correlation.go
Normal file
103
pkg/strategy/factorzoo/correlation.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package factorzoo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/indicator"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var zeroTime time.Time
|
||||||
|
|
||||||
|
type KLineValueMapper func(k types.KLine) float64
|
||||||
|
|
||||||
|
//go:generate callbackgen -type Correlation
|
||||||
|
type Correlation struct {
|
||||||
|
types.IntervalWindow
|
||||||
|
Values types.Float64Slice
|
||||||
|
EndTime time.Time
|
||||||
|
|
||||||
|
UpdateCallbacks []func(value float64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inc *Correlation) Last() float64 {
|
||||||
|
if len(inc.Values) == 0 {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
return inc.Values[len(inc.Values)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inc *Correlation) calculateAndUpdate(klines []types.KLine) {
|
||||||
|
if len(klines) < inc.Window {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = len(klines) - 1
|
||||||
|
var lastKLine = klines[end]
|
||||||
|
|
||||||
|
if inc.EndTime != zeroTime && lastKLine.GetEndTime().Before(inc.EndTime) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var recentT = klines[end-(inc.Window-1) : end+1]
|
||||||
|
|
||||||
|
correlation, err := calculateCORRELATION(recentT, inc.Window, KLineAmplitudeMapper, indicator.KLineVolumeMapper)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("can not calculate correlation")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
inc.Values.Push(correlation)
|
||||||
|
|
||||||
|
if len(inc.Values) > indicator.MaxNumOfVOL {
|
||||||
|
inc.Values = inc.Values[indicator.MaxNumOfVOLTruncateSize-1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
inc.EndTime = klines[end].GetEndTime().Time()
|
||||||
|
|
||||||
|
inc.EmitUpdate(correlation)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inc *Correlation) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||||
|
if inc.Interval != interval {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
inc.calculateAndUpdate(window)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inc *Correlation) Bind(updater indicator.KLineWindowUpdater) {
|
||||||
|
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateCORRELATION(klines []types.KLine, window int, valA KLineValueMapper, valB 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
sumA, sumB, sumAB, squareSumA, squareSumB := 0., 0., 0., 0., 0.
|
||||||
|
for _, k := range klines {
|
||||||
|
// sum of elements of array A
|
||||||
|
sumA += valA(k)
|
||||||
|
// sum of elements of array B
|
||||||
|
sumB += valB(k)
|
||||||
|
|
||||||
|
// sum of A[i] * B[i].
|
||||||
|
sumAB = sumAB + valA(k)*valB(k)
|
||||||
|
|
||||||
|
// sum of square of array elements.
|
||||||
|
squareSumA = squareSumA + valA(k)*valA(k)
|
||||||
|
squareSumB = squareSumB + valB(k)*valB(k)
|
||||||
|
}
|
||||||
|
// use formula for calculating correlation coefficient.
|
||||||
|
corr := (float64(window)*sumAB - sumA*sumB) /
|
||||||
|
math.Sqrt((float64(window)*squareSumA-sumA*sumA)*(float64(window)*squareSumB-sumB*sumB))
|
||||||
|
|
||||||
|
return corr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func KLineAmplitudeMapper(k types.KLine) float64 {
|
||||||
|
return k.High.Div(k.Low).Float64()
|
||||||
|
}
|
15
pkg/strategy/factorzoo/correlation_callbacks.go
Normal file
15
pkg/strategy/factorzoo/correlation_callbacks.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Code generated by "callbackgen -type Correlation"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package factorzoo
|
||||||
|
|
||||||
|
import ()
|
||||||
|
|
||||||
|
func (inc *Correlation) OnUpdate(cb func(value float64)) {
|
||||||
|
inc.UpdateCallbacks = append(inc.UpdateCallbacks, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inc *Correlation) EmitUpdate(value float64) {
|
||||||
|
for _, cb := range inc.UpdateCallbacks {
|
||||||
|
cb(value)
|
||||||
|
}
|
||||||
|
}
|
247
pkg/strategy/factorzoo/strategy.go
Normal file
247
pkg/strategy/factorzoo/strategy.go
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
package factorzoo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
"github.com/sajari/regression"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ID = "factorzoo"
|
||||||
|
|
||||||
|
var three = fixedpoint.NewFromInt(3)
|
||||||
|
|
||||||
|
var log = logrus.WithField("strategy", ID)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bbgo.RegisterStrategy(ID, &Strategy{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntervalWindowSetting struct {
|
||||||
|
types.IntervalWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
type Strategy struct {
|
||||||
|
Symbol string `json:"symbol"`
|
||||||
|
Market types.Market
|
||||||
|
Interval types.Interval `json:"interval"`
|
||||||
|
Quantity fixedpoint.Value `json:"quantity"`
|
||||||
|
|
||||||
|
Position *types.Position `json:"position,omitempty"`
|
||||||
|
|
||||||
|
activeMakerOrders *bbgo.LocalActiveOrderBook
|
||||||
|
orderStore *bbgo.OrderStore
|
||||||
|
tradeCollector *bbgo.TradeCollector
|
||||||
|
|
||||||
|
session *bbgo.ExchangeSession
|
||||||
|
book *types.StreamOrderBook
|
||||||
|
|
||||||
|
prevClose fixedpoint.Value
|
||||||
|
|
||||||
|
pvDivergenceSetting *IntervalWindowSetting `json:"pvDivergence"`
|
||||||
|
pvDivergence *Correlation
|
||||||
|
|
||||||
|
Ret []float64
|
||||||
|
Alpha [][]float64
|
||||||
|
|
||||||
|
T int64
|
||||||
|
prevER fixedpoint.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) ID() string {
|
||||||
|
return ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
||||||
|
log.Infof("subscribe %s", s.Symbol)
|
||||||
|
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval.String()})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) ClosePosition(ctx context.Context, percentage fixedpoint.Value) error {
|
||||||
|
base := s.Position.GetBase()
|
||||||
|
if base.IsZero() {
|
||||||
|
return fmt.Errorf("no opened %s position", s.Position.Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make it negative
|
||||||
|
quantity := base.Mul(percentage).Abs()
|
||||||
|
side := types.SideTypeBuy
|
||||||
|
if base.Sign() > 0 {
|
||||||
|
side = types.SideTypeSell
|
||||||
|
}
|
||||||
|
|
||||||
|
if quantity.Compare(s.Market.MinQuantity) < 0 {
|
||||||
|
return fmt.Errorf("order quantity %v is too small, less than %v", quantity, s.Market.MinQuantity)
|
||||||
|
}
|
||||||
|
|
||||||
|
submitOrder := types.SubmitOrder{
|
||||||
|
Symbol: s.Symbol,
|
||||||
|
Side: side,
|
||||||
|
Type: types.OrderTypeMarket,
|
||||||
|
Quantity: quantity,
|
||||||
|
Market: s.Market,
|
||||||
|
}
|
||||||
|
|
||||||
|
//s.Notify("Submitting %s %s order to close position by %v", s.Symbol, side.String(), percentage, submitOrder)
|
||||||
|
|
||||||
|
createdOrders, err := s.session.Exchange.SubmitOrders(ctx, submitOrder)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Errorf("can not place position close order")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.orderStore.Add(createdOrders...)
|
||||||
|
s.activeMakerOrders.Add(createdOrders...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) placeOrders(ctx context.Context, orderExecutor bbgo.OrderExecutor, er fixedpoint.Value) {
|
||||||
|
|
||||||
|
//if s.prevER.Sign() < 0 && er.Sign() > 0 {
|
||||||
|
if er.Sign() >= 0 {
|
||||||
|
submitOrder := types.SubmitOrder{
|
||||||
|
Symbol: s.Symbol,
|
||||||
|
Side: types.SideTypeBuy,
|
||||||
|
Type: types.OrderTypeMarket,
|
||||||
|
Quantity: s.Quantity, //er.Abs().Mul(fixedpoint.NewFromInt(20)),
|
||||||
|
}
|
||||||
|
createdOrders, err := orderExecutor.SubmitOrders(ctx, submitOrder)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Errorf("can not place orders")
|
||||||
|
}
|
||||||
|
s.orderStore.Add(createdOrders...)
|
||||||
|
s.activeMakerOrders.Add(createdOrders...)
|
||||||
|
//} else if s.prevER.Sign() > 0 && er.Sign() < 0 {
|
||||||
|
} else {
|
||||||
|
submitOrder := types.SubmitOrder{
|
||||||
|
Symbol: s.Symbol,
|
||||||
|
Side: types.SideTypeSell,
|
||||||
|
Type: types.OrderTypeMarket,
|
||||||
|
Quantity: s.Quantity, //er.Abs().Mul(fixedpoint.NewFromInt(20)),
|
||||||
|
}
|
||||||
|
createdOrders, err := orderExecutor.SubmitOrders(ctx, submitOrder)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Errorf("can not place orders")
|
||||||
|
}
|
||||||
|
s.orderStore.Add(createdOrders...)
|
||||||
|
s.activeMakerOrders.Add(createdOrders...)
|
||||||
|
}
|
||||||
|
s.prevER = er
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
||||||
|
// initial required information
|
||||||
|
s.session = session
|
||||||
|
s.prevClose = fixedpoint.Zero
|
||||||
|
|
||||||
|
// first we need to get market data store(cached market data) from the exchange session
|
||||||
|
st, _ := session.MarketDataStore(s.Symbol)
|
||||||
|
// setup the time frame size
|
||||||
|
iw := types.IntervalWindow{Window: 50, Interval: s.Interval}
|
||||||
|
// construct CORR indicator
|
||||||
|
s.pvDivergence = &Correlation{IntervalWindow: iw}
|
||||||
|
// bind indicator to the data store, so that our callback could be triggered
|
||||||
|
s.pvDivergence.Bind(st)
|
||||||
|
//s.pvDivergence.OnUpdate(func(corr float64) {
|
||||||
|
// //fmt.Printf("now we've got corr: %f\n", corr)
|
||||||
|
//})
|
||||||
|
|
||||||
|
s.Alpha = [][]float64{{}, {}, {}, {}, {}}
|
||||||
|
s.Ret = []float64{}
|
||||||
|
//thetas := []float64{0, 0, 0, 0}
|
||||||
|
preCompute := 0
|
||||||
|
|
||||||
|
s.activeMakerOrders = bbgo.NewLocalActiveOrderBook(s.Symbol)
|
||||||
|
s.activeMakerOrders.BindStream(session.UserDataStream)
|
||||||
|
|
||||||
|
s.orderStore = bbgo.NewOrderStore(s.Symbol)
|
||||||
|
s.orderStore.BindStream(session.UserDataStream)
|
||||||
|
|
||||||
|
if s.Position == nil {
|
||||||
|
s.Position = types.NewPositionFromMarket(s.Market)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.tradeCollector = bbgo.NewTradeCollector(s.Symbol, s.Position, s.orderStore)
|
||||||
|
s.tradeCollector.BindStream(session.UserDataStream)
|
||||||
|
|
||||||
|
session.UserDataStream.OnStart(func() {
|
||||||
|
log.Infof("connected")
|
||||||
|
})
|
||||||
|
|
||||||
|
session.MarketDataStream.OnKLineClosed(func(kline types.KLine) {
|
||||||
|
|
||||||
|
if kline.Symbol != s.Symbol || kline.Interval != s.Interval {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.activeMakerOrders.GracefulCancel(ctx, s.session.Exchange); err != nil {
|
||||||
|
log.WithError(err).Errorf("graceful cancel order error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// amplitude volume divergence
|
||||||
|
corr := fixedpoint.NewFromFloat(s.pvDivergence.Last()).Neg()
|
||||||
|
// price mean reversion
|
||||||
|
rev := fixedpoint.NewFromInt(1).Div(kline.Close)
|
||||||
|
// alpha150 from GTJA's 191 paper
|
||||||
|
a150 := kline.High.Add(kline.Low).Add(kline.Close).Div(three).Mul(kline.Volume)
|
||||||
|
// momentum from WQ's 101 paper
|
||||||
|
mom := fixedpoint.One.Sub(kline.Open.Div(kline.Close)).Mul(fixedpoint.NegOne)
|
||||||
|
// opening gap
|
||||||
|
ogap := kline.Open.Div(s.prevClose)
|
||||||
|
|
||||||
|
log.Infof("corr: %f, rev: %f, a150: %f, mom: %f, ogap: %f", corr.Float64(), rev.Float64(), a150.Float64(), mom.Float64(), ogap.Float64())
|
||||||
|
s.Alpha[0] = append(s.Alpha[0], corr.Float64())
|
||||||
|
s.Alpha[1] = append(s.Alpha[1], rev.Float64())
|
||||||
|
s.Alpha[2] = append(s.Alpha[2], a150.Float64())
|
||||||
|
s.Alpha[3] = append(s.Alpha[3], mom.Float64())
|
||||||
|
s.Alpha[4] = append(s.Alpha[4], ogap.Float64())
|
||||||
|
|
||||||
|
//s.Alpha[5] = append(s.Alpha[4], 1.0) // constant
|
||||||
|
|
||||||
|
ret := kline.Close.Sub(s.prevClose).Div(s.prevClose).Float64()
|
||||||
|
s.Ret = append(s.Ret, ret)
|
||||||
|
log.Infof("Current Return: %f", s.Ret[len(s.Ret)-1])
|
||||||
|
|
||||||
|
// accumulate enough data for cross-sectional regression, not time-series regression
|
||||||
|
s.T = 20
|
||||||
|
if preCompute < int(s.T)+1 {
|
||||||
|
preCompute++
|
||||||
|
} else {
|
||||||
|
s.ClosePosition(ctx, fixedpoint.One)
|
||||||
|
s.tradeCollector.Process()
|
||||||
|
// rolling regression for last 20 interval alphas
|
||||||
|
r := new(regression.Regression)
|
||||||
|
r.SetObserved("Return Rate Per Timeframe")
|
||||||
|
r.SetVar(0, "Corr")
|
||||||
|
r.SetVar(1, "Rev")
|
||||||
|
r.SetVar(2, "A150")
|
||||||
|
r.SetVar(3, "Mom")
|
||||||
|
r.SetVar(4, "OGap")
|
||||||
|
var rdp regression.DataPoints
|
||||||
|
for i := 1; i <= int(s.T); i++ {
|
||||||
|
// alphas[t-1], previous alphas, dot not take current alpha into account, will cause look-ahead bias
|
||||||
|
as := []float64{s.Alpha[0][len(s.Alpha[0])-(i+2)], s.Alpha[1][len(s.Alpha[1])-(i+2)], s.Alpha[2][len(s.Alpha[2])-(i+2)], s.Alpha[3][len(s.Alpha[3])-(i+2)], s.Alpha[4][len(s.Alpha[4])-(i+2)]}
|
||||||
|
// alphas[t], current return rate
|
||||||
|
rt := s.Ret[len(s.Ret)-(i+1)]
|
||||||
|
rdp = append(rdp, regression.DataPoint(rt, as))
|
||||||
|
|
||||||
|
}
|
||||||
|
r.Train(rdp...)
|
||||||
|
r.Run()
|
||||||
|
fmt.Printf("Regression formula:\n%v\n", r.Formula)
|
||||||
|
//prediction := r.Coeff(0)*corr.Float64() + r.Coeff(1)*rev.Float64() + r.Coeff(2)*factorzoo.Float64() + r.Coeff(3)*mom.Float64() + r.Coeff(4)
|
||||||
|
prediction, _ := r.Predict([]float64{corr.Float64(), rev.Float64(), a150.Float64(), mom.Float64(), ogap.Float64()})
|
||||||
|
log.Infof("Predicted Return: %f", prediction)
|
||||||
|
|
||||||
|
s.placeOrders(ctx, orderExecutor, fixedpoint.NewFromFloat(prediction))
|
||||||
|
s.tradeCollector.Process()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.prevClose = kline.Close
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user