Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: add rsicross strategy #1227

Merged
merged 4 commits into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions config/rsicross.yaml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions pkg/bbgo/order_executor_general.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos
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)
}

Expand Down
7 changes: 1 addition & 6 deletions pkg/bbgo/trader.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ func (trader *Trader) injectFieldsAndSubscribe(ctx context.Context) error {
// load and run Session strategies
for sessionName, strategies := range trader.exchangeStrategies {
var session = trader.environment.sessions[sessionName]
var orderExecutor = trader.getSessionOrderExecutor(sessionName)
for _, strategy := range strategies {
rs := reflect.ValueOf(strategy)

Expand All @@ -237,10 +236,6 @@ func (trader *Trader) injectFieldsAndSubscribe(ctx context.Context) error {
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 err := defaulter.Defaults(); err != nil {
panic(err)
Expand Down Expand Up @@ -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)
}

if err := dynamic.InjectField(elem, "Facade", ps, true); err != nil {
if err := dynamic.InjectField(elem.Interface(), "Facade", ps, true); err != nil {
return err
}

Expand Down
1 change: 1 addition & 0 deletions pkg/cmd/strategy/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
_ "github.com/c9s/bbgo/pkg/strategy/pricealert"
_ "github.com/c9s/bbgo/pkg/strategy/pricedrop"
_ "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/schedule"
_ "github.com/c9s/bbgo/pkg/strategy/scmaker"
Expand Down
102 changes: 3 additions & 99 deletions pkg/dynamic/inject.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,19 @@ package dynamic
import (
"fmt"
"reflect"
"testing"
"time"

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"

"github.com/c9s/bbgo/pkg/service"
"github.com/c9s/bbgo/pkg/types"
)

type testEnvironment struct {
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)

if !field.IsValid() {
return nil
}
Expand Down Expand Up @@ -131,96 +128,3 @@ func ParseStructAndInject(f interface{}, objects ...interface{}) error {

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 changes: 105 additions & 0 deletions pkg/dynamic/inject_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}
4 changes: 2 additions & 2 deletions pkg/strategy/common/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Strategy struct {
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.ctx, s.cancel = context.WithCancel(ctx)

Expand Down Expand Up @@ -53,6 +53,6 @@ func (s *Strategy) Setup(ctx context.Context, environ *bbgo.Environment, session
s.OrderExecutor.BindProfitStats(s.ProfitStats)
s.OrderExecutor.Bind()
s.OrderExecutor.TradeCollector().OnPositionUpdate(func(position *types.Position) {
bbgo.Sync(ctx, s)
// bbgo.Sync(ctx, s)
})
}
11 changes: 6 additions & 5 deletions pkg/strategy/linregmaker/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package linregmaker
import (
"context"
"fmt"
"github.com/c9s/bbgo/pkg/risk/dynamicrisk"
"sync"

"github.com/c9s/bbgo/pkg/risk/dynamicrisk"

"github.com/c9s/bbgo/pkg/indicator"
"github.com/c9s/bbgo/pkg/util"

Expand Down Expand Up @@ -221,20 +222,20 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
})
}

// Setup Exits
// Initialize Exits
s.ExitMethods.SetAndSubscribe(session, s)

// Setup dynamic spread
// Initialize dynamic spread
if s.DynamicSpread.IsEnabled() {
s.DynamicSpread.Initialize(s.Symbol, session)
}

// Setup dynamic exposure
// Initialize dynamic exposure
if s.DynamicExposure.IsEnabled() {
s.DynamicExposure.Initialize(s.Symbol, session)
}

// Setup dynamic quantities
// Initialize dynamic quantities
if len(s.DynamicQuantityIncrease) > 0 {
s.DynamicQuantityIncrease.Initialize(s.Symbol, session)
}
Expand Down
Loading