mirror of
https://github.com/c9s/bbgo.git
synced 2024-09-20 08:11:08 +00:00
Merge pull request #862 from andycheng123/improve/supertrend-strategy
Improve: supertrend strategy
This commit is contained in:
commit
1cd48177ae
|
@ -43,8 +43,10 @@ exchangeStrategies:
|
|||
# ATR Multiplier for calculating super trend prices, the higher, the stronger the trends are
|
||||
supertrendMultiplier: 4.1
|
||||
|
||||
# leverage is the leverage of the orders
|
||||
# leverage uses the account net value to calculate the order qty
|
||||
leverage: 1.0
|
||||
# quantity sets the fixed order qty, takes precedence over Leverage
|
||||
#quantity: 0.5
|
||||
|
||||
# fastDEMAWindow and slowDEMAWindow are for filtering super trend noise
|
||||
fastDEMAWindow: 144
|
||||
|
|
|
@ -130,7 +130,24 @@ func (inc *Supertrend) GetSignal() types.Direction {
|
|||
var _ types.SeriesExtend = &Supertrend{}
|
||||
|
||||
func (inc *Supertrend) PushK(k types.KLine) {
|
||||
if inc.EndTime != zeroTime && k.EndTime.Before(inc.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
inc.Update(k.GetHigh().Float64(), k.GetLow().Float64(), k.GetClose().Float64())
|
||||
inc.EndTime = k.EndTime.Time()
|
||||
inc.EmitUpdate(inc.Last())
|
||||
|
||||
}
|
||||
|
||||
func (inc *Supertrend) BindK(target KLineClosedEmitter, symbol string, interval types.Interval) {
|
||||
target.OnKLineClosed(types.KLineWith(symbol, interval, inc.PushK))
|
||||
}
|
||||
|
||||
func (inc *Supertrend) LoadK(allKLines []types.KLine) {
|
||||
for _, k := range allKLines {
|
||||
inc.PushK(k)
|
||||
}
|
||||
}
|
||||
|
||||
func (inc *Supertrend) CalculateAndUpdate(kLines []types.KLine) {
|
||||
|
|
|
@ -12,6 +12,8 @@ import (
|
|||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
var log = logrus.WithField("risk", "AccountValueCalculator")
|
||||
|
||||
var one = fixedpoint.One
|
||||
|
||||
var maxLeverage = fixedpoint.NewFromInt(10)
|
||||
|
@ -140,6 +142,34 @@ func (c *AccountValueCalculator) NetValue(ctx context.Context) (fixedpoint.Value
|
|||
return accountValue, nil
|
||||
}
|
||||
|
||||
func (c *AccountValueCalculator) AvailableQuote(ctx context.Context) (fixedpoint.Value, error) {
|
||||
accountValue := fixedpoint.Zero
|
||||
|
||||
if len(c.prices) == 0 {
|
||||
if err := c.UpdatePrices(ctx); err != nil {
|
||||
return accountValue, err
|
||||
}
|
||||
}
|
||||
|
||||
balances := c.session.Account.Balances()
|
||||
for _, b := range balances {
|
||||
if b.Currency == c.quoteCurrency {
|
||||
accountValue = accountValue.Add(b.Available)
|
||||
continue
|
||||
}
|
||||
|
||||
symbol := b.Currency + c.quoteCurrency
|
||||
price, ok := c.prices[symbol]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
accountValue = accountValue.Add(b.Available.Mul(price))
|
||||
}
|
||||
|
||||
return accountValue, nil
|
||||
}
|
||||
|
||||
// MarginLevel calculates the margin level from the asset market value and the debt value
|
||||
// See https://www.binance.com/en/support/faq/360030493931
|
||||
func (c *AccountValueCalculator) MarginLevel(ctx context.Context) (fixedpoint.Value, error) {
|
||||
|
@ -164,7 +194,6 @@ func CalculateBaseQuantity(session *bbgo.ExchangeSession, market types.Market, p
|
|||
leverage = fixedpoint.NewFromInt(3)
|
||||
}
|
||||
|
||||
|
||||
baseBalance, _ := session.Account.Balance(market.BaseCurrency)
|
||||
quoteBalance, _ := session.Account.Balance(market.QuoteCurrency)
|
||||
|
||||
|
@ -241,3 +270,29 @@ func CalculateBaseQuantity(session *bbgo.ExchangeSession, market types.Market, p
|
|||
|
||||
return quantity, fmt.Errorf("quantity is zero, can not submit sell order, please check your settings")
|
||||
}
|
||||
|
||||
func CalculateQuoteQuantity(session *bbgo.ExchangeSession, ctx context.Context, quoteCurrency string, leverage fixedpoint.Value) (fixedpoint.Value, error) {
|
||||
// default leverage guard
|
||||
if leverage.IsZero() {
|
||||
leverage = fixedpoint.NewFromInt(3)
|
||||
}
|
||||
|
||||
quoteBalance, _ := session.Account.Balance(quoteCurrency)
|
||||
accountValue := NewAccountValueCalculator(session, quoteCurrency)
|
||||
|
||||
usingLeverage := session.Margin || session.IsolatedMargin || session.Futures || session.IsolatedFutures
|
||||
if !usingLeverage {
|
||||
// For spot, we simply return the quote balance
|
||||
return quoteBalance.Available.Mul(fixedpoint.Min(leverage, fixedpoint.One)), nil
|
||||
}
|
||||
|
||||
// using leverage -- starts from here
|
||||
availableQuote, err := accountValue.AvailableQuote(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("can not update available quote")
|
||||
return fixedpoint.Zero, err
|
||||
}
|
||||
logrus.Infof("calculating available leveraged quote quantity: account available quote = %+v", availableQuote)
|
||||
|
||||
return availableQuote.Mul(leverage), nil
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
package supertrend
|
||||
|
||||
import (
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
// LinGre is Linear Regression baseline
|
||||
type LinGre struct {
|
||||
types.IntervalWindow
|
||||
baseLineSlope float64
|
||||
}
|
||||
|
||||
// Update Linear Regression baseline slope
|
||||
func (lg *LinGre) Update(klines []types.KLine) {
|
||||
if len(klines) < lg.Window {
|
||||
lg.baseLineSlope = 0
|
||||
return
|
||||
}
|
||||
|
||||
var sumX, sumY, sumXSqr, sumXY float64 = 0, 0, 0, 0
|
||||
end := len(klines) - 1 // The last kline
|
||||
for i := end; i >= end-lg.Window+1; i-- {
|
||||
val := klines[i].GetClose().Float64()
|
||||
per := float64(end - i + 1)
|
||||
sumX += per
|
||||
sumY += val
|
||||
sumXSqr += per * per
|
||||
sumXY += val * per
|
||||
}
|
||||
length := float64(lg.Window)
|
||||
slope := (length*sumXY - sumX*sumY) / (length*sumXSqr - sumX*sumX)
|
||||
average := sumY / length
|
||||
endPrice := average - slope*sumX/length + slope
|
||||
startPrice := endPrice + slope*(length-1)
|
||||
lg.baseLineSlope = (length - 1) / (endPrice - startPrice)
|
||||
|
||||
log.Debugf("linear regression baseline slope: %f", lg.baseLineSlope)
|
||||
}
|
||||
|
||||
func (lg *LinGre) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||
if lg.Interval != interval {
|
||||
return
|
||||
}
|
||||
|
||||
lg.Update(window)
|
||||
}
|
||||
|
||||
func (lg *LinGre) Bind(updater indicator.KLineWindowUpdater) {
|
||||
updater.OnKLineWindowUpdate(lg.handleKLineWindowUpdate)
|
||||
}
|
||||
|
||||
// GetSignal get linear regression signal
|
||||
func (lg *LinGre) GetSignal() types.Direction {
|
||||
var lgSignal types.Direction = types.DirectionNone
|
||||
|
||||
switch {
|
||||
case lg.baseLineSlope > 0:
|
||||
lgSignal = types.DirectionUp
|
||||
case lg.baseLineSlope < 0:
|
||||
lgSignal = types.DirectionDown
|
||||
}
|
||||
|
||||
return lgSignal
|
||||
}
|
||||
|
||||
// preloadLinGre preloads linear regression indicator
|
||||
func (lg *LinGre) preload(kLineStore *bbgo.MarketDataStore) {
|
||||
if klines, ok := kLineStore.KLinesOfInterval(lg.Interval); ok {
|
||||
lg.Update((*klines)[0:])
|
||||
}
|
||||
}
|
104
pkg/strategy/supertrend/linreg.go
Normal file
104
pkg/strategy/supertrend/linreg.go
Normal file
|
@ -0,0 +1,104 @@
|
|||
package supertrend
|
||||
|
||||
import (
|
||||
"github.com/c9s/bbgo/pkg/indicator"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LinReg is Linear Regression baseline
|
||||
type LinReg struct {
|
||||
types.SeriesBase
|
||||
types.IntervalWindow
|
||||
// Values are the slopes of linear regression baseline
|
||||
Values types.Float64Slice
|
||||
klines types.KLineWindow
|
||||
EndTime time.Time
|
||||
}
|
||||
|
||||
// Last slope of linear regression baseline
|
||||
func (lr *LinReg) Last() float64 {
|
||||
if lr.Values.Length() == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return lr.Values.Last()
|
||||
}
|
||||
|
||||
// Index returns the slope of specified index
|
||||
func (lr *LinReg) Index(i int) float64 {
|
||||
if i >= lr.Values.Length() {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
return lr.Values.Index(i)
|
||||
}
|
||||
|
||||
// Length of the slope values
|
||||
func (lr *LinReg) Length() int {
|
||||
return lr.Values.Length()
|
||||
}
|
||||
|
||||
var _ types.SeriesExtend = &LinReg{}
|
||||
|
||||
// Update Linear Regression baseline slope
|
||||
func (lr *LinReg) Update(kline types.KLine) {
|
||||
lr.klines.Add(kline)
|
||||
lr.klines.Truncate(lr.Window)
|
||||
if len(lr.klines) < lr.Window {
|
||||
lr.Values.Push(0)
|
||||
return
|
||||
}
|
||||
|
||||
var sumX, sumY, sumXSqr, sumXY float64 = 0, 0, 0, 0
|
||||
end := len(lr.klines) - 1 // The last kline
|
||||
for i := end; i >= end-lr.Window+1; i-- {
|
||||
val := lr.klines[i].GetClose().Float64()
|
||||
per := float64(end - i + 1)
|
||||
sumX += per
|
||||
sumY += val
|
||||
sumXSqr += per * per
|
||||
sumXY += val * per
|
||||
}
|
||||
length := float64(lr.Window)
|
||||
slope := (length*sumXY - sumX*sumY) / (length*sumXSqr - sumX*sumX)
|
||||
average := sumY / length
|
||||
endPrice := average - slope*sumX/length + slope
|
||||
startPrice := endPrice + slope*(length-1)
|
||||
lr.Values.Push((length - 1) / (endPrice - startPrice))
|
||||
|
||||
log.Debugf("linear regression baseline slope: %f", lr.Last())
|
||||
}
|
||||
|
||||
func (lr *LinReg) BindK(target indicator.KLineClosedEmitter, symbol string, interval types.Interval) {
|
||||
target.OnKLineClosed(types.KLineWith(symbol, interval, lr.PushK))
|
||||
}
|
||||
|
||||
func (lr *LinReg) PushK(k types.KLine) {
|
||||
var zeroTime = time.Time{}
|
||||
if lr.EndTime != zeroTime && k.EndTime.Before(lr.EndTime) {
|
||||
return
|
||||
}
|
||||
|
||||
lr.Update(k)
|
||||
lr.EndTime = k.EndTime.Time()
|
||||
}
|
||||
|
||||
func (lr *LinReg) LoadK(allKLines []types.KLine) {
|
||||
for _, k := range allKLines {
|
||||
lr.PushK(k)
|
||||
}
|
||||
}
|
||||
|
||||
// GetSignal get linear regression signal
|
||||
func (lr *LinReg) GetSignal() types.Direction {
|
||||
var lrSignal types.Direction = types.DirectionNone
|
||||
|
||||
switch {
|
||||
case lr.Last() > 0:
|
||||
lrSignal = types.DirectionUp
|
||||
case lr.Last() < 0:
|
||||
lrSignal = types.DirectionDown
|
||||
}
|
||||
|
||||
return lrSignal
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
package supertrend
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/risk"
|
||||
"github.com/fatih/color"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
|
@ -54,10 +57,13 @@ type Strategy struct {
|
|||
SupertrendMultiplier float64 `json:"supertrendMultiplier"`
|
||||
|
||||
// LinearRegression Use linear regression as trend confirmation
|
||||
LinearRegression *LinGre `json:"linearRegression,omitempty"`
|
||||
LinearRegression *LinReg `json:"linearRegression,omitempty"`
|
||||
|
||||
// Leverage
|
||||
Leverage float64 `json:"leverage"`
|
||||
// Leverage uses the account net value to calculate the order qty
|
||||
Leverage fixedpoint.Value `json:"leverage"`
|
||||
// Quantity sets the fixed order qty, takes precedence over Leverage
|
||||
Quantity fixedpoint.Value `json:"quantity"`
|
||||
AccountValueCalculator *risk.AccountValueCalculator
|
||||
|
||||
// TakeProfitAtrMultiplier TP according to ATR multiple, 0 to disable this
|
||||
TakeProfitAtrMultiplier float64 `json:"takeProfitAtrMultiplier"`
|
||||
|
@ -84,6 +90,16 @@ type Strategy struct {
|
|||
|
||||
// StrategyController
|
||||
bbgo.StrategyController
|
||||
|
||||
// Accumulated profit report
|
||||
accumulatedProfit fixedpoint.Value
|
||||
accumulatedProfitMA *indicator.SMA
|
||||
// AccumulatedProfitMAWindow Accumulated profit SMA window
|
||||
AccumulatedProfitMAWindow int `json:"accumulatedProfitMAWindow"`
|
||||
dailyAccumulatedProfits types.Float64Slice
|
||||
lastDayAccumulatedProfit fixedpoint.Value
|
||||
// AccumulatedProfitLastPeriodWindow Last period window of accumulated profit
|
||||
AccumulatedProfitLastPeriodWindow int `json:"accumulatedProfitLastPeriodWindow"`
|
||||
}
|
||||
|
||||
func (s *Strategy) ID() string {
|
||||
|
@ -103,10 +119,6 @@ func (s *Strategy) Validate() error {
|
|||
return errors.New("interval is required")
|
||||
}
|
||||
|
||||
if s.Leverage <= 0.0 {
|
||||
return errors.New("leverage is required")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -115,6 +127,9 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
|||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.LinearRegression.Interval})
|
||||
|
||||
s.ExitMethods.SetAndSubscribe(session, s)
|
||||
|
||||
// Accumulated profit report
|
||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: types.Interval1d})
|
||||
}
|
||||
|
||||
// Position control
|
||||
|
@ -153,15 +168,6 @@ func (s *Strategy) ClosePosition(ctx context.Context, percentage fixedpoint.Valu
|
|||
return err
|
||||
}
|
||||
|
||||
// preloadSupertrend preloads supertrend indicator
|
||||
func preloadSupertrend(supertrend *indicator.Supertrend, kLineStore *bbgo.MarketDataStore) {
|
||||
if klines, ok := kLineStore.KLinesOfInterval(supertrend.Interval); ok {
|
||||
for i := 0; i < len(*klines); i++ {
|
||||
supertrend.Update((*klines)[i].GetHigh().Float64(), (*klines)[i].GetLow().Float64(), (*klines)[i].GetClose().Float64())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setupIndicators initializes indicators
|
||||
func (s *Strategy) setupIndicators() {
|
||||
// K-line store for indicators
|
||||
|
@ -179,8 +185,10 @@ func (s *Strategy) setupIndicators() {
|
|||
}
|
||||
s.Supertrend = &indicator.Supertrend{IntervalWindow: types.IntervalWindow{Window: s.Window, Interval: s.Interval}, ATRMultiplier: s.SupertrendMultiplier}
|
||||
s.Supertrend.AverageTrueRange = &indicator.ATR{IntervalWindow: types.IntervalWindow{Window: s.Window, Interval: s.Interval}}
|
||||
s.Supertrend.Bind(kLineStore)
|
||||
preloadSupertrend(s.Supertrend, kLineStore)
|
||||
s.Supertrend.BindK(s.session.MarketDataStream, s.Symbol, s.Supertrend.Interval)
|
||||
if klines, ok := kLineStore.KLinesOfInterval(s.Supertrend.Interval); ok {
|
||||
s.Supertrend.LoadK((*klines)[0:])
|
||||
}
|
||||
|
||||
// Linear Regression
|
||||
if s.LinearRegression != nil {
|
||||
|
@ -189,8 +197,10 @@ func (s *Strategy) setupIndicators() {
|
|||
} else if s.LinearRegression.Interval == "" {
|
||||
s.LinearRegression = nil
|
||||
} else {
|
||||
s.LinearRegression.Bind(kLineStore)
|
||||
s.LinearRegression.preload(kLineStore)
|
||||
s.LinearRegression.BindK(s.session.MarketDataStream, s.Symbol, s.LinearRegression.Interval)
|
||||
if klines, ok := kLineStore.KLinesOfInterval(s.LinearRegression.Interval); ok {
|
||||
s.LinearRegression.LoadK((*klines)[0:])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -251,17 +261,52 @@ func (s *Strategy) generateOrderForm(side types.SideType, quantity fixedpoint.Va
|
|||
}
|
||||
|
||||
// calculateQuantity returns leveraged quantity
|
||||
func (s *Strategy) calculateQuantity(currentPrice fixedpoint.Value) fixedpoint.Value {
|
||||
balance, ok := s.session.GetAccount().Balance(s.Market.QuoteCurrency)
|
||||
if !ok {
|
||||
log.Errorf("can not update %s balance from exchange", s.Symbol)
|
||||
return fixedpoint.Zero
|
||||
func (s *Strategy) calculateQuantity(ctx context.Context, currentPrice fixedpoint.Value, side types.SideType) fixedpoint.Value {
|
||||
// Quantity takes precedence
|
||||
if !s.Quantity.IsZero() {
|
||||
return s.Quantity
|
||||
}
|
||||
|
||||
amountAvailable := balance.Available.Mul(fixedpoint.NewFromFloat(s.Leverage))
|
||||
quantity := amountAvailable.Div(currentPrice)
|
||||
usingLeverage := s.session.Margin || s.session.IsolatedMargin || s.session.Futures || s.session.IsolatedFutures
|
||||
|
||||
return quantity
|
||||
if bbgo.IsBackTesting { // Backtesting
|
||||
balance, ok := s.session.GetAccount().Balance(s.Market.QuoteCurrency)
|
||||
if !ok {
|
||||
log.Errorf("can not update %s quote balance from exchange", s.Symbol)
|
||||
return fixedpoint.Zero
|
||||
}
|
||||
|
||||
return balance.Available.Mul(fixedpoint.Min(s.Leverage, fixedpoint.One)).Div(currentPrice)
|
||||
} else if !usingLeverage && side == types.SideTypeSell { // Spot sell
|
||||
balance, ok := s.session.GetAccount().Balance(s.Market.BaseCurrency)
|
||||
if !ok {
|
||||
log.Errorf("can not update %s base balance from exchange", s.Symbol)
|
||||
return fixedpoint.Zero
|
||||
}
|
||||
|
||||
return balance.Available.Mul(fixedpoint.Min(s.Leverage, fixedpoint.One))
|
||||
} else { // Using leverage or spot buy
|
||||
quoteQty, err := risk.CalculateQuoteQuantity(s.session, ctx, s.Market.QuoteCurrency, s.Leverage)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("can not update %s quote balance from exchange", s.Symbol)
|
||||
return fixedpoint.Zero
|
||||
}
|
||||
|
||||
return quoteQty.Div(currentPrice)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintResult prints accumulated profit status
|
||||
func (s *Strategy) PrintResult(o *os.File) {
|
||||
f := bufio.NewWriter(o)
|
||||
defer f.Flush()
|
||||
hiyellow := color.New(color.FgHiYellow).FprintfFunc()
|
||||
hiyellow(f, "------ %s Accumulated Profit Results ------\n", s.InstanceID())
|
||||
hiyellow(f, "Symbol: %v\n", s.Symbol)
|
||||
hiyellow(f, "Accumulated Profit: %v\n", s.accumulatedProfit)
|
||||
hiyellow(f, "Accumulated Profit %dMA: %f\n", s.AccumulatedProfitMAWindow, s.accumulatedProfitMA.Last())
|
||||
hiyellow(f, "Last %d day(s) Accumulated Profit: %f\n", s.AccumulatedProfitLastPeriodWindow, s.dailyAccumulatedProfits.Sum())
|
||||
hiyellow(f, "\n")
|
||||
}
|
||||
|
||||
func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
|
||||
|
@ -289,6 +334,10 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
if s.TradeStats == nil {
|
||||
s.TradeStats = types.NewTradeStats(s.Symbol)
|
||||
}
|
||||
startTime := s.Environment.StartTime()
|
||||
s.TradeStats.SetIntervalProfitCollector(types.NewIntervalProfitCollector(types.Interval1d, startTime))
|
||||
s.TradeStats.SetIntervalProfitCollector(types.NewIntervalProfitCollector(types.Interval1w, startTime))
|
||||
s.TradeStats.SetIntervalProfitCollector(types.NewIntervalProfitCollector(types.Interval1mo, startTime))
|
||||
|
||||
// Set fee rate
|
||||
if s.session.MakerFeeRate.Sign() > 0 || s.session.TakerFeeRate.Sign() > 0 {
|
||||
|
@ -305,6 +354,31 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
s.orderExecutor.BindTradeStats(s.TradeStats)
|
||||
s.orderExecutor.Bind()
|
||||
|
||||
// AccountValueCalculator
|
||||
s.AccountValueCalculator = risk.NewAccountValueCalculator(s.session, s.Market.QuoteCurrency)
|
||||
|
||||
// Accumulated profit report
|
||||
if s.AccumulatedProfitMAWindow <= 0 {
|
||||
s.AccumulatedProfitMAWindow = 60
|
||||
}
|
||||
s.accumulatedProfitMA = &indicator.SMA{IntervalWindow: types.IntervalWindow{Interval: s.Interval, Window: s.AccumulatedProfitMAWindow}}
|
||||
s.orderExecutor.TradeCollector().OnProfit(func(trade types.Trade, profit *types.Profit) {
|
||||
if profit == nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.accumulatedProfit = s.accumulatedProfit.Add(profit.Profit)
|
||||
s.accumulatedProfitMA.Update(s.accumulatedProfit.Float64())
|
||||
})
|
||||
if s.AccumulatedProfitLastPeriodWindow <= 0 {
|
||||
s.AccumulatedProfitLastPeriodWindow = 7
|
||||
}
|
||||
session.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, types.Interval1d, func(kline types.KLine) {
|
||||
s.dailyAccumulatedProfits.Update(s.accumulatedProfit.Sub(s.lastDayAccumulatedProfit).Float64())
|
||||
s.dailyAccumulatedProfits = s.dailyAccumulatedProfits.Tail(s.AccumulatedProfitLastPeriodWindow)
|
||||
s.lastDayAccumulatedProfit = s.accumulatedProfit
|
||||
}))
|
||||
|
||||
// Sync position to redis on trade
|
||||
s.orderExecutor.TradeCollector().OnPositionUpdate(func(position *types.Position) {
|
||||
bbgo.Sync(s)
|
||||
|
@ -395,7 +469,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
}
|
||||
}
|
||||
|
||||
orderForm := s.generateOrderForm(side, s.calculateQuantity(closePrice), types.SideEffectTypeMarginBuy)
|
||||
orderForm := s.generateOrderForm(side, s.calculateQuantity(ctx, closePrice, side), types.SideEffectTypeMarginBuy)
|
||||
log.Infof("submit open position order %v", orderForm)
|
||||
_, err := s.orderExecutor.SubmitOrders(ctx, orderForm)
|
||||
if err != nil {
|
||||
|
@ -409,6 +483,9 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
bbgo.OnShutdown(func(ctx context.Context, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
// Print accumulated profit report
|
||||
defer s.PrintResult(os.Stdout)
|
||||
|
||||
_ = s.orderExecutor.GracefulCancel(ctx)
|
||||
_, _ = fmt.Fprintln(os.Stderr, s.TradeStats.String())
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue
Block a user