mirror of
https://github.com/c9s/bbgo.git
synced 2024-09-20 08:11:08 +00:00
add rsicross strategy
This commit is contained in:
parent
7c2de46273
commit
5c88abe72f
53
config/rsicross.yaml
Normal file
53
config/rsicross.yaml
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
persistence:
|
||||||
|
json:
|
||||||
|
directory: var/data
|
||||||
|
redis:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 6379
|
||||||
|
db: 0
|
||||||
|
|
||||||
|
sessions:
|
||||||
|
binance:
|
||||||
|
exchange: binance
|
||||||
|
envVarPrefix: binance
|
||||||
|
|
||||||
|
exchangeStrategies:
|
||||||
|
- on: binance
|
||||||
|
rsicross:
|
||||||
|
symbol: BTCUSDT
|
||||||
|
interval: 5m
|
||||||
|
fastWindow: 7
|
||||||
|
slowWindow: 12
|
||||||
|
|
||||||
|
quantity: 0.1
|
||||||
|
|
||||||
|
### RISK CONTROLS
|
||||||
|
## circuitBreakEMA is used for calculating the price for circuitBreak
|
||||||
|
# circuitBreakEMA:
|
||||||
|
# interval: 1m
|
||||||
|
# window: 14
|
||||||
|
|
||||||
|
## circuitBreakLossThreshold is the maximum loss threshold for realized+unrealized PnL
|
||||||
|
# circuitBreakLossThreshold: -10.0
|
||||||
|
|
||||||
|
## positionHardLimit is the maximum position limit
|
||||||
|
# positionHardLimit: 500.0
|
||||||
|
|
||||||
|
## maxPositionQuantity is the maximum quantity per order that could be controlled in positionHardLimit,
|
||||||
|
## this parameter is used with positionHardLimit togerther
|
||||||
|
# maxPositionQuantity: 10.0
|
||||||
|
|
||||||
|
backtest:
|
||||||
|
startTime: "2022-01-01"
|
||||||
|
endTime: "2022-02-01"
|
||||||
|
symbols:
|
||||||
|
- BTCUSDT
|
||||||
|
sessions: [binance]
|
||||||
|
# syncSecKLines: true
|
||||||
|
accounts:
|
||||||
|
binance:
|
||||||
|
makerFeeRate: 0.0%
|
||||||
|
takerFeeRate: 0.075%
|
||||||
|
balances:
|
||||||
|
BTC: 0.0
|
||||||
|
USDT: 10000.0
|
|
@ -417,6 +417,8 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos
|
||||||
return createdOrder, nil
|
return createdOrder, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.WithError(err).Errorf("unable to submit order: %v", err)
|
||||||
|
log.Infof("reduce quantity and retry order")
|
||||||
return e.reduceQuantityAndSubmitOrder(ctx, price, *submitOrder)
|
return e.reduceQuantityAndSubmitOrder(ctx, price, *submitOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -222,7 +222,6 @@ func (trader *Trader) injectFieldsAndSubscribe(ctx context.Context) error {
|
||||||
// load and run Session strategies
|
// load and run Session strategies
|
||||||
for sessionName, strategies := range trader.exchangeStrategies {
|
for sessionName, strategies := range trader.exchangeStrategies {
|
||||||
var session = trader.environment.sessions[sessionName]
|
var session = trader.environment.sessions[sessionName]
|
||||||
var orderExecutor = trader.getSessionOrderExecutor(sessionName)
|
|
||||||
for _, strategy := range strategies {
|
for _, strategy := range strategies {
|
||||||
rs := reflect.ValueOf(strategy)
|
rs := reflect.ValueOf(strategy)
|
||||||
|
|
||||||
|
@ -237,10 +236,6 @@ func (trader *Trader) injectFieldsAndSubscribe(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dynamic.InjectField(rs, "OrderExecutor", orderExecutor, false); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to inject OrderExecutor on %T", strategy)
|
|
||||||
}
|
|
||||||
|
|
||||||
if defaulter, ok := strategy.(StrategyDefaulter); ok {
|
if defaulter, ok := strategy.(StrategyDefaulter); ok {
|
||||||
if err := defaulter.Defaults(); err != nil {
|
if err := defaulter.Defaults(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -441,7 +436,7 @@ func (trader *Trader) injectCommonServices(ctx context.Context, s interface{}) e
|
||||||
return fmt.Errorf("field Persistence is not a struct element, %s given", field)
|
return fmt.Errorf("field Persistence is not a struct element, %s given", field)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dynamic.InjectField(elem, "Facade", ps, true); err != nil {
|
if err := dynamic.InjectField(elem.Interface(), "Facade", ps, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/pricealert"
|
_ "github.com/c9s/bbgo/pkg/strategy/pricealert"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/pricedrop"
|
_ "github.com/c9s/bbgo/pkg/strategy/pricedrop"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/rebalance"
|
_ "github.com/c9s/bbgo/pkg/strategy/rebalance"
|
||||||
|
_ "github.com/c9s/bbgo/pkg/strategy/rsicross"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/rsmaker"
|
_ "github.com/c9s/bbgo/pkg/strategy/rsmaker"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/schedule"
|
_ "github.com/c9s/bbgo/pkg/strategy/schedule"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/scmaker"
|
_ "github.com/c9s/bbgo/pkg/strategy/scmaker"
|
||||||
|
|
|
@ -3,22 +3,19 @@ package dynamic
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/service"
|
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type testEnvironment struct {
|
type testEnvironment struct {
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func InjectField(rs reflect.Value, fieldName string, obj interface{}, pointerOnly bool) error {
|
func InjectField(target interface{}, fieldName string, obj interface{}, pointerOnly bool) error {
|
||||||
|
rs := reflect.ValueOf(target)
|
||||||
field := rs.FieldByName(fieldName)
|
field := rs.FieldByName(fieldName)
|
||||||
|
|
||||||
if !field.IsValid() {
|
if !field.IsValid() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -131,96 +128,3 @@ func ParseStructAndInject(f interface{}, objects ...interface{}) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_injectField(t *testing.T) {
|
|
||||||
type TT struct {
|
|
||||||
TradeService *service.TradeService
|
|
||||||
}
|
|
||||||
|
|
||||||
// only pointer object can be set.
|
|
||||||
var tt = &TT{}
|
|
||||||
|
|
||||||
// get the value of the pointer, or it can not be set.
|
|
||||||
var rv = reflect.ValueOf(tt).Elem()
|
|
||||||
|
|
||||||
_, ret := HasField(rv, "TradeService")
|
|
||||||
assert.True(t, ret)
|
|
||||||
|
|
||||||
ts := &service.TradeService{}
|
|
||||||
|
|
||||||
err := InjectField(rv, "TradeService", ts, true)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_parseStructAndInject(t *testing.T) {
|
|
||||||
t.Run("skip nil", func(t *testing.T) {
|
|
||||||
ss := struct {
|
|
||||||
a int
|
|
||||||
Env *testEnvironment
|
|
||||||
}{
|
|
||||||
a: 1,
|
|
||||||
Env: nil,
|
|
||||||
}
|
|
||||||
err := ParseStructAndInject(&ss, nil)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Nil(t, ss.Env)
|
|
||||||
})
|
|
||||||
t.Run("pointer", func(t *testing.T) {
|
|
||||||
ss := struct {
|
|
||||||
a int
|
|
||||||
Env *testEnvironment
|
|
||||||
}{
|
|
||||||
a: 1,
|
|
||||||
Env: nil,
|
|
||||||
}
|
|
||||||
err := ParseStructAndInject(&ss, &testEnvironment{})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, ss.Env)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("composition", func(t *testing.T) {
|
|
||||||
type TT struct {
|
|
||||||
*service.TradeService
|
|
||||||
}
|
|
||||||
ss := TT{}
|
|
||||||
err := ParseStructAndInject(&ss, &service.TradeService{})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, ss.TradeService)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("struct", func(t *testing.T) {
|
|
||||||
ss := struct {
|
|
||||||
a int
|
|
||||||
Env testEnvironment
|
|
||||||
}{
|
|
||||||
a: 1,
|
|
||||||
}
|
|
||||||
err := ParseStructAndInject(&ss, testEnvironment{
|
|
||||||
startTime: time.Now(),
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotEqual(t, time.Time{}, ss.Env.startTime)
|
|
||||||
})
|
|
||||||
t.Run("interface/any", func(t *testing.T) {
|
|
||||||
ss := struct {
|
|
||||||
Any interface{} // anything
|
|
||||||
}{
|
|
||||||
Any: nil,
|
|
||||||
}
|
|
||||||
err := ParseStructAndInject(&ss, &testEnvironment{
|
|
||||||
startTime: time.Now(),
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, ss.Any)
|
|
||||||
})
|
|
||||||
t.Run("interface/stringer", func(t *testing.T) {
|
|
||||||
ss := struct {
|
|
||||||
Stringer types.Stringer // stringer interface
|
|
||||||
}{
|
|
||||||
Stringer: nil,
|
|
||||||
}
|
|
||||||
err := ParseStructAndInject(&ss, &types.Trade{})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, ss.Stringer)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
105
pkg/dynamic/inject_test.go
Normal file
105
pkg/dynamic/inject_test.go
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
package dynamic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/service"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_injectField(t *testing.T) {
|
||||||
|
type TT struct {
|
||||||
|
TradeService *service.TradeService
|
||||||
|
}
|
||||||
|
|
||||||
|
// only pointer object can be set.
|
||||||
|
var tt = &TT{}
|
||||||
|
|
||||||
|
// get the value of the pointer, or it can not be set.
|
||||||
|
var rv = reflect.ValueOf(tt).Elem()
|
||||||
|
|
||||||
|
_, ret := HasField(rv, "TradeService")
|
||||||
|
assert.True(t, ret)
|
||||||
|
|
||||||
|
ts := &service.TradeService{}
|
||||||
|
|
||||||
|
err := InjectField(rv, "TradeService", ts, true)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_parseStructAndInject(t *testing.T) {
|
||||||
|
t.Run("skip nil", func(t *testing.T) {
|
||||||
|
ss := struct {
|
||||||
|
a int
|
||||||
|
Env *testEnvironment
|
||||||
|
}{
|
||||||
|
a: 1,
|
||||||
|
Env: nil,
|
||||||
|
}
|
||||||
|
err := ParseStructAndInject(&ss, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Nil(t, ss.Env)
|
||||||
|
})
|
||||||
|
t.Run("pointer", func(t *testing.T) {
|
||||||
|
ss := struct {
|
||||||
|
a int
|
||||||
|
Env *testEnvironment
|
||||||
|
}{
|
||||||
|
a: 1,
|
||||||
|
Env: nil,
|
||||||
|
}
|
||||||
|
err := ParseStructAndInject(&ss, &testEnvironment{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, ss.Env)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("composition", func(t *testing.T) {
|
||||||
|
type TT struct {
|
||||||
|
*service.TradeService
|
||||||
|
}
|
||||||
|
ss := TT{}
|
||||||
|
err := ParseStructAndInject(&ss, &service.TradeService{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, ss.TradeService)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("struct", func(t *testing.T) {
|
||||||
|
ss := struct {
|
||||||
|
a int
|
||||||
|
Env testEnvironment
|
||||||
|
}{
|
||||||
|
a: 1,
|
||||||
|
}
|
||||||
|
err := ParseStructAndInject(&ss, testEnvironment{
|
||||||
|
startTime: time.Now(),
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEqual(t, time.Time{}, ss.Env.startTime)
|
||||||
|
})
|
||||||
|
t.Run("interface/any", func(t *testing.T) {
|
||||||
|
ss := struct {
|
||||||
|
Any interface{} // anything
|
||||||
|
}{
|
||||||
|
Any: nil,
|
||||||
|
}
|
||||||
|
err := ParseStructAndInject(&ss, &testEnvironment{
|
||||||
|
startTime: time.Now(),
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, ss.Any)
|
||||||
|
})
|
||||||
|
t.Run("interface/stringer", func(t *testing.T) {
|
||||||
|
ss := struct {
|
||||||
|
Stringer types.Stringer // stringer interface
|
||||||
|
}{
|
||||||
|
Stringer: nil,
|
||||||
|
}
|
||||||
|
err := ParseStructAndInject(&ss, &types.Trade{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, ss.Stringer)
|
||||||
|
})
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ type Strategy struct {
|
||||||
OrderExecutor *bbgo.GeneralOrderExecutor
|
OrderExecutor *bbgo.GeneralOrderExecutor
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) Setup(ctx context.Context, environ *bbgo.Environment, session *bbgo.ExchangeSession, market types.Market, strategyID, instanceID string) {
|
func (s *Strategy) Initialize(ctx context.Context, environ *bbgo.Environment, session *bbgo.ExchangeSession, market types.Market, strategyID, instanceID string) {
|
||||||
s.parent = ctx
|
s.parent = ctx
|
||||||
s.ctx, s.cancel = context.WithCancel(ctx)
|
s.ctx, s.cancel = context.WithCancel(ctx)
|
||||||
|
|
||||||
|
@ -53,6 +53,6 @@ func (s *Strategy) Setup(ctx context.Context, environ *bbgo.Environment, session
|
||||||
s.OrderExecutor.BindProfitStats(s.ProfitStats)
|
s.OrderExecutor.BindProfitStats(s.ProfitStats)
|
||||||
s.OrderExecutor.Bind()
|
s.OrderExecutor.Bind()
|
||||||
s.OrderExecutor.TradeCollector().OnPositionUpdate(func(position *types.Position) {
|
s.OrderExecutor.TradeCollector().OnPositionUpdate(func(position *types.Position) {
|
||||||
bbgo.Sync(ctx, s)
|
// bbgo.Sync(ctx, s)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,10 @@ package linregmaker
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/c9s/bbgo/pkg/risk/dynamicrisk"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/risk/dynamicrisk"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/indicator"
|
"github.com/c9s/bbgo/pkg/indicator"
|
||||||
"github.com/c9s/bbgo/pkg/util"
|
"github.com/c9s/bbgo/pkg/util"
|
||||||
|
|
||||||
|
@ -221,20 +222,20 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup Exits
|
// Initialize Exits
|
||||||
s.ExitMethods.SetAndSubscribe(session, s)
|
s.ExitMethods.SetAndSubscribe(session, s)
|
||||||
|
|
||||||
// Setup dynamic spread
|
// Initialize dynamic spread
|
||||||
if s.DynamicSpread.IsEnabled() {
|
if s.DynamicSpread.IsEnabled() {
|
||||||
s.DynamicSpread.Initialize(s.Symbol, session)
|
s.DynamicSpread.Initialize(s.Symbol, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup dynamic exposure
|
// Initialize dynamic exposure
|
||||||
if s.DynamicExposure.IsEnabled() {
|
if s.DynamicExposure.IsEnabled() {
|
||||||
s.DynamicExposure.Initialize(s.Symbol, session)
|
s.DynamicExposure.Initialize(s.Symbol, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup dynamic quantities
|
// Initialize dynamic quantities
|
||||||
if len(s.DynamicQuantityIncrease) > 0 {
|
if len(s.DynamicQuantityIncrease) > 0 {
|
||||||
s.DynamicQuantityIncrease.Initialize(s.Symbol, session)
|
s.DynamicQuantityIncrease.Initialize(s.Symbol, session)
|
||||||
}
|
}
|
||||||
|
|
137
pkg/strategy/rsicross/strategy.go
Normal file
137
pkg/strategy/rsicross/strategy.go
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
package rsicross
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/indicator"
|
||||||
|
"github.com/c9s/bbgo/pkg/risk/riskcontrol"
|
||||||
|
"github.com/c9s/bbgo/pkg/strategy/common"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ID = "rsicross"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bbgo.RegisterStrategy(ID, &Strategy{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Strategy struct {
|
||||||
|
*common.Strategy
|
||||||
|
|
||||||
|
Environment *bbgo.Environment
|
||||||
|
Market types.Market
|
||||||
|
|
||||||
|
Symbol string `json:"symbol"`
|
||||||
|
Interval types.Interval `json:"interval"`
|
||||||
|
SlowWindow int `json:"slowWindow"`
|
||||||
|
FastWindow int `json:"fastWindow"`
|
||||||
|
|
||||||
|
bbgo.OpenPositionOptions
|
||||||
|
|
||||||
|
// risk related parameters
|
||||||
|
PositionHardLimit fixedpoint.Value `json:"positionHardLimit"`
|
||||||
|
MaxPositionQuantity fixedpoint.Value `json:"maxPositionQuantity"`
|
||||||
|
CircuitBreakLossThreshold fixedpoint.Value `json:"circuitBreakLossThreshold"`
|
||||||
|
CircuitBreakEMA types.IntervalWindow `json:"circuitBreakEMA"`
|
||||||
|
|
||||||
|
positionRiskControl *riskcontrol.PositionRiskControl
|
||||||
|
circuitBreakRiskControl *riskcontrol.CircuitBreakRiskControl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) ID() string {
|
||||||
|
return ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) InstanceID() string {
|
||||||
|
return fmt.Sprintf("%s:%s:%s:%d-%d", ID, s.Symbol, s.Interval, s.FastWindow, s.SlowWindow)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
||||||
|
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
||||||
|
s.Strategy = &common.Strategy{}
|
||||||
|
s.Strategy.Initialize(ctx, s.Environment, session, s.Market, ID, s.InstanceID())
|
||||||
|
|
||||||
|
if !s.PositionHardLimit.IsZero() && !s.MaxPositionQuantity.IsZero() {
|
||||||
|
log.Infof("positionHardLimit and maxPositionQuantity are configured, setting up PositionRiskControl...")
|
||||||
|
s.positionRiskControl = riskcontrol.NewPositionRiskControl(s.OrderExecutor, s.PositionHardLimit, s.MaxPositionQuantity)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.CircuitBreakLossThreshold.IsZero() {
|
||||||
|
log.Infof("circuitBreakLossThreshold is configured, setting up CircuitBreakRiskControl...")
|
||||||
|
s.circuitBreakRiskControl = riskcontrol.NewCircuitBreakRiskControl(
|
||||||
|
s.Position,
|
||||||
|
session.Indicators(s.Symbol).EWMA(s.CircuitBreakEMA),
|
||||||
|
s.CircuitBreakLossThreshold,
|
||||||
|
s.ProfitStats)
|
||||||
|
}
|
||||||
|
|
||||||
|
fastRsi := session.Indicators(s.Symbol).RSI(types.IntervalWindow{Interval: s.Interval, Window: s.FastWindow})
|
||||||
|
slowRsi := session.Indicators(s.Symbol).RSI(types.IntervalWindow{Interval: s.Interval, Window: s.SlowWindow})
|
||||||
|
rsiCross := indicator.Cross(fastRsi, slowRsi)
|
||||||
|
rsiCross.OnUpdate(func(v float64) {
|
||||||
|
switch indicator.CrossType(v) {
|
||||||
|
case indicator.CrossOver:
|
||||||
|
opts := s.OpenPositionOptions
|
||||||
|
opts.Long = true
|
||||||
|
|
||||||
|
if price, ok := session.LastPrice(s.Symbol); ok {
|
||||||
|
opts.Price = price
|
||||||
|
}
|
||||||
|
|
||||||
|
// opts.Price = closePrice
|
||||||
|
opts.Tags = []string{"rsiCrossOver"}
|
||||||
|
if _, err := s.OrderExecutor.OpenPosition(ctx, opts); err != nil {
|
||||||
|
logErr(err, "unable to open position")
|
||||||
|
}
|
||||||
|
|
||||||
|
case indicator.CrossUnder:
|
||||||
|
if err := s.OrderExecutor.ClosePosition(ctx, fixedpoint.One); err != nil {
|
||||||
|
logErr(err, "failed to close position")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
bbgo.OnShutdown(ctx, func(ctx context.Context, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) preloadKLines(inc *indicator.KLineStream, session *bbgo.ExchangeSession, symbol string, interval types.Interval) {
|
||||||
|
if store, ok := session.MarketDataStore(symbol); ok {
|
||||||
|
if kLinesData, ok := store.KLinesOfInterval(interval); ok {
|
||||||
|
for _, k := range *kLinesData {
|
||||||
|
inc.EmitUpdate(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func logErr(err error, msgAndArgs ...interface{}) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(msgAndArgs) == 0 {
|
||||||
|
log.WithError(err).Error(err.Error())
|
||||||
|
} else if len(msgAndArgs) == 1 {
|
||||||
|
msg := msgAndArgs[0].(string)
|
||||||
|
log.WithError(err).Error(msg)
|
||||||
|
} else if len(msgAndArgs) > 1 {
|
||||||
|
msg := msgAndArgs[0].(string)
|
||||||
|
log.WithError(err).Errorf(msg, msgAndArgs[1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -101,7 +101,7 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
||||||
|
|
||||||
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
||||||
s.Strategy = &common.Strategy{}
|
s.Strategy = &common.Strategy{}
|
||||||
s.Strategy.Setup(ctx, s.Environment, session, s.Market, ID, s.InstanceID())
|
s.Strategy.Initialize(ctx, s.Environment, session, s.Market, ID, s.InstanceID())
|
||||||
|
|
||||||
s.book = types.NewStreamBook(s.Symbol)
|
s.book = types.NewStreamBook(s.Symbol)
|
||||||
s.book.BindStream(session.UserDataStream)
|
s.book.BindStream(session.UserDataStream)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user