Skip to content

Commit

Permalink
Merge pull request #1864 from c9s/c9s/session-price-solver
Browse files Browse the repository at this point in the history
FEATURE: [core] integrate priceSolver into the session struct
  • Loading branch information
c9s authored Dec 14, 2024
2 parents 7d94e46 + 8a46033 commit b16a4d6
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 96 deletions.
58 changes: 11 additions & 47 deletions pkg/bbgo/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,25 +75,16 @@ func TestLoadConfig(t *testing.T) {
assert.Equal(t, map[string]interface{}{
"sessions": map[string]interface{}{
"max": map[string]interface{}{
"exchange": "max",
"envVarPrefix": "MAX",
"takerFeeRate": 0.,
"makerFeeRate": 0.,
"modifyOrderAmountForFee": false,
"exchange": "max",
"envVarPrefix": "MAX",
"takerFeeRate": 0.,
"makerFeeRate": 0.,
},
"binance": map[string]interface{}{
"exchange": "binance",
"envVarPrefix": "BINANCE",
"takerFeeRate": 0.,
"makerFeeRate": 0.,
"modifyOrderAmountForFee": false,
},
"ftx": map[string]interface{}{
"exchange": "ftx",
"envVarPrefix": "FTX",
"takerFeeRate": 0.,
"makerFeeRate": 0.,
"modifyOrderAmountForFee": true,
"exchange": "binance",
"envVarPrefix": "BINANCE",
"takerFeeRate": 0.,
"makerFeeRate": 0.,
},
},
"build": map[string]interface{}{
Expand Down Expand Up @@ -155,43 +146,16 @@ func TestLoadConfig(t *testing.T) {
assert.NotNil(t, config.Persistence.Json)
},
},

{
name: "order_executor",
args: args{configFile: "testdata/order_executor.yaml"},
wantErr: false,
f: func(t *testing.T, config *Config) {
assert.Len(t, config.Sessions, 2)

session, ok := config.Sessions["max"]
assert.True(t, ok)
assert.NotNil(t, session)

riskControls := config.RiskControls
assert.NotNil(t, riskControls)
assert.NotNil(t, riskControls.SessionBasedRiskControl)

conf, ok := riskControls.SessionBasedRiskControl["max"]
assert.True(t, ok)
assert.NotNil(t, conf)
assert.NotNil(t, conf.OrderExecutor)
assert.NotNil(t, conf.OrderExecutor.BySymbol)

executorConf, ok := conf.OrderExecutor.BySymbol["BTCUSDT"]
assert.True(t, ok)
assert.NotNil(t, executorConf)
},
},
{
name: "backtest",
args: args{configFile: "testdata/backtest.yaml"},
wantErr: false,
f: func(t *testing.T, config *Config) {
assert.Len(t, config.ExchangeStrategies, 1)
assert.NotNil(t, config.Backtest)
assert.NotNil(t, config.Backtest.Account)
assert.NotNil(t, config.Backtest.Account["binance"].Balances)
assert.Len(t, config.Backtest.Account["binance"].Balances, 2)
assert.NotNil(t, config.Backtest.Accounts)
assert.NotNil(t, config.Backtest.Accounts["binance"].Balances)
assert.Len(t, config.Backtest.Accounts["binance"].Balances, 2)
},
},
}
Expand Down
54 changes: 21 additions & 33 deletions pkg/bbgo/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/c9s/bbgo/pkg/envvar"
"github.com/c9s/bbgo/pkg/exchange/retry"
"github.com/c9s/bbgo/pkg/metrics"
"github.com/c9s/bbgo/pkg/pricesolver"
"github.com/c9s/bbgo/pkg/util/templateutil"

exchange2 "github.com/c9s/bbgo/pkg/exchange"
Expand Down Expand Up @@ -47,10 +48,9 @@ type ExchangeSession struct {
SubAccount string `json:"subAccount,omitempty" yaml:"subAccount,omitempty"`

// Withdrawal is used for enabling withdrawal functions
Withdrawal bool `json:"withdrawal,omitempty" yaml:"withdrawal,omitempty"`
MakerFeeRate fixedpoint.Value `json:"makerFeeRate" yaml:"makerFeeRate"`
TakerFeeRate fixedpoint.Value `json:"takerFeeRate" yaml:"takerFeeRate"`
ModifyOrderAmountForFee bool `json:"modifyOrderAmountForFee" yaml:"modifyOrderAmountForFee"`
Withdrawal bool `json:"withdrawal,omitempty" yaml:"withdrawal,omitempty"`
MakerFeeRate fixedpoint.Value `json:"makerFeeRate" yaml:"makerFeeRate"`
TakerFeeRate fixedpoint.Value `json:"takerFeeRate" yaml:"takerFeeRate"`

// PublicOnly is used for setting the session to public only (without authentication, no private user data)
PublicOnly bool `json:"publicOnly,omitempty" yaml:"publicOnly"`
Expand Down Expand Up @@ -126,9 +126,6 @@ type ExchangeSession struct {
// markets defines market configuration of a symbol
markets map[string]types.Market

// orderBooks stores the streaming order book
orderBooks map[string]*types.StreamOrderBook

// startPrices is used for backtest
startPrices map[string]fixedpoint.Value

Expand All @@ -150,6 +147,8 @@ type ExchangeSession struct {
initializedSymbols map[string]struct{}

logger log.FieldLogger

priceSolver *pricesolver.SimplePriceSolver
}

func NewExchangeSession(name string, exchange types.Exchange) *ExchangeSession {
Expand Down Expand Up @@ -182,17 +181,17 @@ func NewExchangeSession(name string, exchange types.Exchange) *ExchangeSession {
Account: &types.Account{},
Trades: make(map[string]*types.TradeSlice),

orderBooks: make(map[string]*types.StreamOrderBook),
markets: make(map[string]types.Market),
startPrices: make(map[string]fixedpoint.Value),
lastPrices: make(map[string]fixedpoint.Value),
markets: make(map[string]types.Market, 100),
startPrices: make(map[string]fixedpoint.Value, 30),
lastPrices: make(map[string]fixedpoint.Value, 30),
positions: make(map[string]*types.Position),
marketDataStores: make(map[string]*MarketDataStore),
standardIndicatorSets: make(map[string]*StandardIndicatorSet),
indicators: make(map[string]*IndicatorSet),
usedSymbols: make(map[string]struct{}),
initializedSymbols: make(map[string]struct{}),
logger: log.WithField("session", name),
priceSolver: pricesolver.NewSimplePriceResolver(nil),
}

session.OrderExecutor = &ExchangeOrderExecutor{
Expand Down Expand Up @@ -304,8 +303,11 @@ func (session *ExchangeSession) Init(ctx context.Context, environ *Environment)
return ErrEmptyMarketInfo
}

logger.Infof("%d markets loaded", len(markets))
session.markets = markets

session.priceSolver = pricesolver.NewSimplePriceResolver(markets)

if feeRateProvider, ok := session.Exchange.(types.ExchangeDefaultFeeRates); ok {
defaultFeeRates := feeRateProvider.DefaultFeeRates()
if session.MakerFeeRate.IsZero() {
Expand All @@ -316,22 +318,16 @@ func (session *ExchangeSession) Init(ctx context.Context, environ *Environment)
}
}

if session.ModifyOrderAmountForFee {
amountProtectExchange, ok := session.Exchange.(types.ExchangeAmountFeeProtect)
if !ok {
return fmt.Errorf("exchange %s does not support order amount protection", session.ExchangeName.String())
}

fees := types.ExchangeFee{MakerFeeRate: session.MakerFeeRate, TakerFeeRate: session.TakerFeeRate}
amountProtectExchange.SetModifyOrderAmountForFee(fees)
}

if session.UseHeikinAshi {
// replace the existing market data stream
session.MarketDataStream = &types.HeikinAshiStream{
StandardStreamEmitter: session.MarketDataStream.(types.StandardStreamEmitter),
}
}

session.priceSolver.BindStream(session.UserDataStream)
session.priceSolver.BindStream(session.MarketDataStream)

// query and initialize the balances
if !session.PublicOnly {
if len(session.PrivateChannels) > 0 {
Expand All @@ -358,8 +354,10 @@ func (session *ExchangeSession) Init(ctx context.Context, environ *Environment)
}

session.setAccount(account)
session.metricsBalancesUpdater(account.Balances())
logger.Infof("account %s balances:\n%s", session.Name, account.Balances().String())

balances := account.Balances()
session.metricsBalancesUpdater(balances)
logger.Infof("session %s account balances:\n%s", session.Name, balances.NotZero().String())
}

// forward trade updates and order updates to the order executor
Expand Down Expand Up @@ -545,9 +543,6 @@ func (session *ExchangeSession) initSymbol(ctx context.Context, environ *Environ
for _, sub := range session.Subscriptions {
switch sub.Channel {
case types.BookChannel:
book := types.NewStreamBook(sub.Symbol, session.ExchangeName)
book.BindStream(session.MarketDataStream)
session.orderBooks[sub.Symbol] = book

case types.KLineChannel:
if sub.Options.Interval == "" {
Expand Down Expand Up @@ -707,12 +702,6 @@ func (session *ExchangeSession) SerialMarketDataStore(
return store, true
}

// OrderBook returns the personal orderbook of a symbol
func (session *ExchangeSession) OrderBook(symbol string) (s *types.StreamOrderBook, ok bool) {
s, ok = session.orderBooks[symbol]
return s, ok
}

func (session *ExchangeSession) StartPrice(symbol string) (price fixedpoint.Value, ok bool) {
price, ok = session.startPrices[symbol]
return price, ok
Expand Down Expand Up @@ -946,7 +935,6 @@ func (session *ExchangeSession) InitExchange(name string, ex types.Exchange) err
session.Account = &types.Account{}
session.Trades = make(map[string]*types.TradeSlice)

session.orderBooks = make(map[string]*types.StreamOrderBook)
session.markets = make(map[string]types.Market)
session.lastPrices = make(map[string]fixedpoint.Value)
session.startPrices = make(map[string]fixedpoint.Value)
Expand Down
2 changes: 1 addition & 1 deletion pkg/bbgo/testdata/backtest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ backtest:
# see here for more details
# https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp
startTime: "2020-01-01"
account:
accounts:
binance:
makerFeeRate: 15
takerFeeRate: 15
Expand Down
8 changes: 0 additions & 8 deletions pkg/bbgo/testdata/strategy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,11 @@ sessions:
envVarPrefix: MAX
takerFeeRate: 0
makerFeeRate: 0
modifyOrderAmountForFee: false
binance:
exchange: binance
envVarPrefix: BINANCE
takerFeeRate: 0
makerFeeRate: 0
modifyOrderAmountForFee: false
ftx:
exchange: ftx
envVarPrefix: FTX
takerFeeRate: 0
makerFeeRate: 0
modifyOrderAmountForFee: true

exchangeStrategies:
- on: ["binance"]
Expand Down
4 changes: 4 additions & 0 deletions pkg/pricesolver/simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ type SimplePriceSolver struct {
}

func NewSimplePriceResolver(markets types.MarketMap) *SimplePriceSolver {
if markets == nil {
markets = make(types.MarketMap)
}

return &SimplePriceSolver{
markets: markets,
symbolPrices: make(map[string]fixedpoint.Value),
Expand Down
4 changes: 3 additions & 1 deletion pkg/strategy/tri/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,9 @@ func (s *Strategy) buildArbMarkets(
m.book = book
m.stream = stream
} else {
book, _ := session.OrderBook(symbol)

book := types.NewStreamBook(symbol, session.ExchangeName)
book.BindStream(session.MarketDataStream)
priceUpdater := func(_ types.SliceOrderBook) {
bestAsk, bestBid, _ := book.BestBidAndAsk()
if bestAsk.Equals(m.bestAsk) && bestBid.Equals(m.bestBid) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/strategy/xnav/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (s *Strategy) Validate() error {
return nil
}

var Ten = fixedpoint.NewFromInt(10)
var ten = fixedpoint.NewFromInt(10)

func (s *Strategy) CrossSubscribe(sessions map[string]*bbgo.ExchangeSession) {}

Expand Down Expand Up @@ -134,7 +134,7 @@ func (s *Strategy) recordNetAssetValue(ctx context.Context, sessions map[string]

for currency, asset := range totalAssets {
// calculated if it's dust only when InUSD (usd value) is defined.
if s.IgnoreDusts && !asset.InUSD.IsZero() && asset.InUSD.Compare(Ten) < 0 && asset.InUSD.Compare(Ten.Neg()) > 0 {
if s.IgnoreDusts && !asset.InUSD.IsZero() && asset.InUSD.Compare(ten) < 0 && asset.InUSD.Compare(ten.Neg()) > 0 {
continue
}

Expand Down
4 changes: 0 additions & 4 deletions pkg/types/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,6 @@ type ExchangeDefaultFeeRates interface {
DefaultFeeRates() ExchangeFee
}

type ExchangeAmountFeeProtect interface {
SetModifyOrderAmountForFee(ExchangeFee)
}

//go:generate mockgen -destination=mocks/mock_exchange_trade_history.go -package=mocks . ExchangeTradeHistoryService
type ExchangeTradeHistoryService interface {
QueryTrades(ctx context.Context, symbol string, options *TradeQueryOptions) ([]Trade, error)
Expand Down

0 comments on commit b16a4d6

Please sign in to comment.