mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 23:05:15 +00:00
Merge pull request #833 from c9s/fix/pivotshort-quantity-cal
fix: strategy/pivotshort: fix margin quantity calculation
This commit is contained in:
commit
8c61f68d1d
|
@ -4,8 +4,8 @@ sessions:
|
|||
exchange: binance
|
||||
envVarPrefix: binance
|
||||
margin: true
|
||||
# isolatedMargin: true
|
||||
# isolatedMarginSymbol: ETHUSDT
|
||||
isolatedMargin: true
|
||||
isolatedMarginSymbol: ETHUSDT
|
||||
|
||||
exchangeStrategies:
|
||||
- on: binance
|
||||
|
@ -20,6 +20,9 @@ exchangeStrategies:
|
|||
|
||||
quantity: 10.0
|
||||
|
||||
# when quantity is not given, leverage will be used.
|
||||
# leverage: 10.0
|
||||
|
||||
# breakLow settings are used for shorting when the current price break the previous low
|
||||
breakLow:
|
||||
# ratio is how much the price breaks the previous low to trigger the short.
|
||||
|
|
|
@ -365,12 +365,18 @@ func (session *ExchangeSession) initSymbol(ctx context.Context, environ *Environ
|
|||
orderStore.BindStream(session.UserDataStream)
|
||||
session.orderStores[symbol] = orderStore
|
||||
|
||||
marketDataStore := NewMarketDataStore(symbol)
|
||||
marketDataStore.BindStream(session.MarketDataStream)
|
||||
session.marketDataStores[symbol] = marketDataStore
|
||||
if _, ok := session.marketDataStores[symbol]; !ok {
|
||||
marketDataStore := NewMarketDataStore(symbol)
|
||||
marketDataStore.BindStream(session.MarketDataStream)
|
||||
session.marketDataStores[symbol] = marketDataStore
|
||||
}
|
||||
|
||||
standardIndicatorSet := NewStandardIndicatorSet(symbol, session.MarketDataStream, marketDataStore)
|
||||
session.standardIndicatorSets[symbol] = standardIndicatorSet
|
||||
marketDataStore := session.marketDataStores[symbol]
|
||||
|
||||
if _, ok := session.standardIndicatorSets[symbol]; !ok {
|
||||
standardIndicatorSet := NewStandardIndicatorSet(symbol, session.MarketDataStream, marketDataStore)
|
||||
session.standardIndicatorSets[symbol] = standardIndicatorSet
|
||||
}
|
||||
|
||||
// used kline intervals by the given symbol
|
||||
var klineSubscriptions = map[types.Interval]struct{}{}
|
||||
|
@ -434,6 +440,14 @@ func (session *ExchangeSession) initSymbol(ctx context.Context, environ *Environ
|
|||
|
||||
func (session *ExchangeSession) StandardIndicatorSet(symbol string) (*StandardIndicatorSet, bool) {
|
||||
set, ok := session.standardIndicatorSets[symbol]
|
||||
if !ok {
|
||||
if store, ok2 := session.MarketDataStore(symbol); ok2 {
|
||||
set = NewStandardIndicatorSet(symbol, session.MarketDataStream, store)
|
||||
session.standardIndicatorSets[symbol] = set
|
||||
return set, true
|
||||
}
|
||||
}
|
||||
|
||||
return set, ok
|
||||
}
|
||||
|
||||
|
@ -465,6 +479,12 @@ func (session *ExchangeSession) Positions() map[string]*types.Position {
|
|||
// MarketDataStore returns the market data store of a symbol
|
||||
func (session *ExchangeSession) MarketDataStore(symbol string) (s *MarketDataStore, ok bool) {
|
||||
s, ok = session.marketDataStores[symbol]
|
||||
if !ok {
|
||||
s = NewMarketDataStore(symbol)
|
||||
s.BindStream(session.MarketDataStream)
|
||||
session.marketDataStores[symbol] = s
|
||||
return s, true
|
||||
}
|
||||
return s, ok
|
||||
}
|
||||
|
||||
|
|
|
@ -211,52 +211,6 @@ func (trader *Trader) Subscribe() {
|
|||
}
|
||||
|
||||
func (trader *Trader) RunSingleExchangeStrategy(ctx context.Context, strategy SingleExchangeStrategy, session *ExchangeSession, orderExecutor OrderExecutor) error {
|
||||
rs := reflect.ValueOf(strategy)
|
||||
|
||||
// get the struct element
|
||||
rs = rs.Elem()
|
||||
|
||||
if rs.Kind() != reflect.Struct {
|
||||
return errors.New("strategy object is not a struct")
|
||||
}
|
||||
|
||||
if err := trader.injectCommonServices(strategy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := dynamic.InjectField(rs, "OrderExecutor", orderExecutor, false); err != nil {
|
||||
return errors.Wrapf(err, "failed to inject OrderExecutor on %T", strategy)
|
||||
}
|
||||
|
||||
if symbol, ok := dynamic.LookupSymbolField(rs); ok {
|
||||
log.Infof("found symbol based strategy from %s", rs.Type())
|
||||
|
||||
market, ok := session.Market(symbol)
|
||||
if !ok {
|
||||
return fmt.Errorf("market of symbol %s not found", symbol)
|
||||
}
|
||||
|
||||
indicatorSet, ok := session.StandardIndicatorSet(symbol)
|
||||
if !ok {
|
||||
return fmt.Errorf("standardIndicatorSet of symbol %s not found", symbol)
|
||||
}
|
||||
|
||||
store, ok := session.MarketDataStore(symbol)
|
||||
if !ok {
|
||||
return fmt.Errorf("marketDataStore of symbol %s not found", symbol)
|
||||
}
|
||||
|
||||
if err := dynamic.ParseStructAndInject(strategy,
|
||||
market,
|
||||
indicatorSet,
|
||||
store,
|
||||
session,
|
||||
session.OrderExecutor,
|
||||
); err != nil {
|
||||
return errors.Wrapf(err, "failed to inject object into %T", strategy)
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := strategy.(StrategyValidator); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return fmt.Errorf("failed to validate the config: %w", err)
|
||||
|
@ -307,12 +261,87 @@ func (trader *Trader) RunAllSingleExchangeStrategy(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (trader *Trader) injectFields() 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)
|
||||
|
||||
// get the struct element
|
||||
rs = rs.Elem()
|
||||
|
||||
if rs.Kind() != reflect.Struct {
|
||||
return errors.New("strategy object is not a struct")
|
||||
}
|
||||
|
||||
if err := trader.injectCommonServices(strategy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := dynamic.InjectField(rs, "OrderExecutor", orderExecutor, false); err != nil {
|
||||
return errors.Wrapf(err, "failed to inject OrderExecutor on %T", strategy)
|
||||
}
|
||||
|
||||
if symbol, ok := dynamic.LookupSymbolField(rs); ok {
|
||||
log.Infof("found symbol based strategy from %s", rs.Type())
|
||||
|
||||
market, ok := session.Market(symbol)
|
||||
if !ok {
|
||||
return fmt.Errorf("market of symbol %s not found", symbol)
|
||||
}
|
||||
|
||||
indicatorSet, ok := session.StandardIndicatorSet(symbol)
|
||||
if !ok {
|
||||
return fmt.Errorf("standardIndicatorSet of symbol %s not found", symbol)
|
||||
}
|
||||
|
||||
store, ok := session.MarketDataStore(symbol)
|
||||
if !ok {
|
||||
return fmt.Errorf("marketDataStore of symbol %s not found", symbol)
|
||||
}
|
||||
|
||||
if err := dynamic.ParseStructAndInject(strategy,
|
||||
market,
|
||||
session,
|
||||
session.OrderExecutor,
|
||||
indicatorSet,
|
||||
store,
|
||||
); err != nil {
|
||||
return errors.Wrapf(err, "failed to inject object into %T", strategy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, strategy := range trader.crossExchangeStrategies {
|
||||
rs := reflect.ValueOf(strategy)
|
||||
|
||||
// get the struct element from the struct pointer
|
||||
rs = rs.Elem()
|
||||
if rs.Kind() != reflect.Struct {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := trader.injectCommonServices(strategy); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (trader *Trader) Run(ctx context.Context) error {
|
||||
// before we start the interaction,
|
||||
// register the core interaction, because we can only get the strategies in this scope
|
||||
// trader.environment.Connect will call interact.Start
|
||||
interact.AddCustomInteraction(NewCoreInteraction(trader.environment, trader))
|
||||
|
||||
if err := trader.injectFields(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trader.Subscribe()
|
||||
|
||||
if err := trader.environment.Start(ctx); err != nil {
|
||||
|
@ -333,18 +362,6 @@ func (trader *Trader) Run(ctx context.Context) error {
|
|||
}
|
||||
|
||||
for _, strategy := range trader.crossExchangeStrategies {
|
||||
rs := reflect.ValueOf(strategy)
|
||||
|
||||
// get the struct element from the struct pointer
|
||||
rs = rs.Elem()
|
||||
if rs.Kind() != reflect.Struct {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := trader.injectCommonServices(strategy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := strategy.CrossRun(ctx, router, trader.environment.sessions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -96,11 +96,28 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
|
|||
}
|
||||
|
||||
if lastLow.Compare(s.lastLow) != 0 {
|
||||
bbgo.Notify("%s new pivot low detected: %f", s.Symbol, s.pivot.LastLow())
|
||||
bbgo.Notify("%s found new pivot low: %f", s.Symbol, s.pivot.LastLow())
|
||||
}
|
||||
|
||||
s.lastLow = lastLow
|
||||
s.pivotLowPrices = append(s.pivotLowPrices, s.lastLow)
|
||||
|
||||
log.Infof("pilot calculation for max position: last low = %f, quantity = %f, leverage = %f",
|
||||
s.lastLow.Float64(),
|
||||
s.Quantity.Float64(),
|
||||
s.Leverage.Float64())
|
||||
|
||||
quantity, err := useQuantityOrBaseBalance(s.session, s.Market, s.lastLow, s.Quantity, s.Leverage)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("quantity calculation error")
|
||||
}
|
||||
|
||||
if quantity.IsZero() {
|
||||
log.WithError(err).Errorf("quantity is zero, can not submit order")
|
||||
return
|
||||
}
|
||||
|
||||
bbgo.Notify("%s %f quantity will be used for shorting", s.Symbol, quantity.Float64())
|
||||
})
|
||||
|
||||
session.MarketDataStream.OnKLineClosed(types.KLineWith(symbol, s.Interval, func(kline types.KLine) {
|
||||
|
@ -110,7 +127,7 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
|
|||
}
|
||||
|
||||
if lastLow.Compare(s.lastLow) != 0 {
|
||||
bbgo.Notify("%s new pivot low detected: %f %s", s.Symbol, s.pivot.LastLow())
|
||||
bbgo.Notify("%s new pivot low: %f", s.Symbol, s.pivot.LastLow())
|
||||
}
|
||||
|
||||
s.lastLow = lastLow
|
||||
|
@ -237,10 +254,14 @@ func useQuantityOrBaseBalance(session *bbgo.ExchangeSession, market types.Market
|
|||
baseBalance, _ := session.Account.Balance(market.BaseCurrency)
|
||||
quoteBalance, _ := session.Account.Balance(market.QuoteCurrency)
|
||||
|
||||
log.Infof("calculating quantity: base balance = %+v, quote balance = %+v", baseBalance, quoteBalance)
|
||||
|
||||
// calculate the quantity automatically
|
||||
if session.Margin || session.IsolatedMargin {
|
||||
baseBalanceValue := baseBalance.Total().Mul(price)
|
||||
accountValue := baseBalanceValue.Add(quoteBalance.Total())
|
||||
baseBalanceValue := baseBalance.Net().Mul(price)
|
||||
accountValue := baseBalanceValue.Add(quoteBalance.Net())
|
||||
|
||||
log.Infof("calculated account value %f %s", accountValue.Float64(), market.QuoteCurrency)
|
||||
|
||||
if session.IsolatedMargin {
|
||||
originLeverage := leverage
|
||||
|
|
Loading…
Reference in New Issue
Block a user