mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-23 15:25:14 +00:00
bollmaker: clean up position stack
This commit is contained in:
parent
54d60b9890
commit
3f939461cf
|
@ -16,7 +16,7 @@ backtest:
|
||||||
# for testing max draw down (MDD) at 03-12
|
# for testing max draw down (MDD) at 03-12
|
||||||
# see here for more details
|
# see here for more details
|
||||||
# https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp
|
# https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp
|
||||||
startTime: "2022-05-01"
|
startTime: "2022-01-01"
|
||||||
endTime: "2022-05-31"
|
endTime: "2022-05-31"
|
||||||
sessions:
|
sessions:
|
||||||
- binance
|
- binance
|
||||||
|
@ -26,7 +26,7 @@ backtest:
|
||||||
binance:
|
binance:
|
||||||
balances:
|
balances:
|
||||||
ETH: 0.0
|
ETH: 0.0
|
||||||
USDT: 100_000.0
|
USDT: 10_000.0
|
||||||
|
|
||||||
exchangeStrategies:
|
exchangeStrategies:
|
||||||
|
|
||||||
|
@ -44,13 +44,15 @@ exchangeStrategies:
|
||||||
# Position Stack, with longer stack length, may need more capital.
|
# Position Stack, with longer stack length, may need more capital.
|
||||||
# Push position in stack is initiating a position to calculate base, average cost, etc.
|
# Push position in stack is initiating a position to calculate base, average cost, etc.
|
||||||
# Pop position in stack is loading a previous position back.
|
# Pop position in stack is loading a previous position back.
|
||||||
pushThreshold: 10%
|
positionStack:
|
||||||
# popThreshold : 1%
|
enabled: true
|
||||||
|
pushThreshold: 25%
|
||||||
|
popThreshold: 5%
|
||||||
|
|
||||||
# useTickerPrice use the ticker api to get the mid price instead of the closed kline price.
|
# useTickerPrice use the ticker api to get the mid price instead of the closed kline price.
|
||||||
# The back-test engine is kline-based, so the ticker price api is not supported.
|
# The back-test engine is kline-based, so the ticker price api is not supported.
|
||||||
# Turn this on if you want to do real trading.
|
# Turn this on if you want to do real trading.
|
||||||
useTickerPrice: true
|
useTickerPrice: false
|
||||||
|
|
||||||
# spread is the price spread from the middle price.
|
# spread is the price spread from the middle price.
|
||||||
# For ask orders, the ask price is ((bestAsk + bestBid) / 2 * (1.0 + spread))
|
# For ask orders, the ask price is ((bestAsk + bestBid) / 2 * (1.0 + spread))
|
||||||
|
@ -110,7 +112,7 @@ exchangeStrategies:
|
||||||
domain: [ -1, 1 ]
|
domain: [ -1, 1 ]
|
||||||
# when in down band, holds 1.0 by maximum
|
# when in down band, holds 1.0 by maximum
|
||||||
# when in up band, holds 0.05 by maximum
|
# when in up band, holds 0.05 by maximum
|
||||||
range: [10.0, 1.0 ]
|
range: [ 3.0, 0.5]
|
||||||
|
|
||||||
# DisableShort means you can don't want short position during the market making
|
# DisableShort means you can don't want short position during the market making
|
||||||
# THe short here means you might sell some of your existing inventory.
|
# THe short here means you might sell some of your existing inventory.
|
||||||
|
|
|
@ -62,7 +62,7 @@ func (c *TrailingStopController) Run(ctx context.Context, session *ExchangeSessi
|
||||||
c.averageCost = c.position.AverageCost
|
c.averageCost = c.position.AverageCost
|
||||||
|
|
||||||
// Use trade collector to get the position update event
|
// Use trade collector to get the position update event
|
||||||
tradeCollector.OnPositionUpdate(func(position types.PositionInterface) {
|
tradeCollector.OnPositionUpdate(func(position types.AnyPosition) {
|
||||||
// update average cost if we have it.
|
// update average cost if we have it.
|
||||||
c.averageCost = position.(*types.Position).AverageCost
|
c.averageCost = position.(*types.Position).AverageCost
|
||||||
})
|
})
|
||||||
|
|
|
@ -18,17 +18,17 @@ type TradeCollector struct {
|
||||||
|
|
||||||
tradeStore *TradeStore
|
tradeStore *TradeStore
|
||||||
tradeC chan types.Trade
|
tradeC chan types.Trade
|
||||||
position types.PositionInterface
|
position types.AnyPosition
|
||||||
orderStore *OrderStore
|
orderStore *OrderStore
|
||||||
doneTrades map[types.TradeKey]struct{}
|
doneTrades map[types.TradeKey]struct{}
|
||||||
|
|
||||||
recoverCallbacks []func(trade types.Trade)
|
recoverCallbacks []func(trade types.Trade)
|
||||||
tradeCallbacks []func(trade types.Trade, profit, netProfit fixedpoint.Value)
|
tradeCallbacks []func(trade types.Trade, profit, netProfit fixedpoint.Value)
|
||||||
positionUpdateCallbacks []func(position types.PositionInterface)
|
positionUpdateCallbacks []func(position types.AnyPosition)
|
||||||
profitCallbacks []func(trade types.Trade, profit, netProfit fixedpoint.Value)
|
profitCallbacks []func(trade types.Trade, profit, netProfit fixedpoint.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTradeCollector(symbol string, position types.PositionInterface, orderStore *OrderStore) *TradeCollector {
|
func NewTradeCollector(symbol string, position types.AnyPosition, orderStore *OrderStore) *TradeCollector {
|
||||||
return &TradeCollector{
|
return &TradeCollector{
|
||||||
Symbol: symbol,
|
Symbol: symbol,
|
||||||
orderSig: sigchan.New(1),
|
orderSig: sigchan.New(1),
|
||||||
|
@ -47,7 +47,7 @@ func (c *TradeCollector) OrderStore() *OrderStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position returns the position used by the trade collector
|
// Position returns the position used by the trade collector
|
||||||
func (c *TradeCollector) Position() types.PositionInterface {
|
func (c *TradeCollector) Position() types.AnyPosition {
|
||||||
return c.position
|
return c.position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,11 +27,11 @@ func (c *TradeCollector) EmitTrade(trade types.Trade, profit fixedpoint.Value, n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TradeCollector) OnPositionUpdate(cb func(position types.PositionInterface)) {
|
func (c *TradeCollector) OnPositionUpdate(cb func(position types.AnyPosition)) {
|
||||||
c.positionUpdateCallbacks = append(c.positionUpdateCallbacks, cb)
|
c.positionUpdateCallbacks = append(c.positionUpdateCallbacks, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TradeCollector) EmitPositionUpdate(position types.PositionInterface) {
|
func (c *TradeCollector) EmitPositionUpdate(position types.AnyPosition) {
|
||||||
for _, cb := range c.positionUpdateCallbacks {
|
for _, cb := range c.positionUpdateCallbacks {
|
||||||
cb(position)
|
cb(position)
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,12 @@ type State struct {
|
||||||
ProfitStats types.ProfitStats `json:"profitStats,omitempty"`
|
ProfitStats types.ProfitStats `json:"profitStats,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PositionStack struct {
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
PushThreshold fixedpoint.Value `json:"pushThreshold,omitempty"`
|
||||||
|
PopThreshold fixedpoint.Value `json:"popThreshold,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type BollingerSetting struct {
|
type BollingerSetting struct {
|
||||||
types.IntervalWindow
|
types.IntervalWindow
|
||||||
BandWidth float64 `json:"bandWidth"`
|
BandWidth float64 `json:"bandWidth"`
|
||||||
|
@ -226,14 +232,12 @@ type Strategy struct {
|
||||||
book *types.StreamOrderBook
|
book *types.StreamOrderBook
|
||||||
|
|
||||||
state *State
|
state *State
|
||||||
|
PositionStack PositionStack
|
||||||
|
|
||||||
// persistence fields
|
// persistence fields
|
||||||
Position *types.PositionStack `json:"position,omitempty" persistence:"position"`
|
Position *types.PositionStack `json:"position,omitempty" persistence:"position"`
|
||||||
ProfitStats *types.ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"`
|
ProfitStats *types.ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"`
|
||||||
|
|
||||||
PushThreshold fixedpoint.Value `json:"pushThreshold,omitempty"`
|
|
||||||
PopThreshold fixedpoint.Value `json:"popThreshold,omitempty"`
|
|
||||||
|
|
||||||
activeMakerOrders *bbgo.LocalActiveOrderBook
|
activeMakerOrders *bbgo.LocalActiveOrderBook
|
||||||
orderStore *bbgo.OrderStore
|
orderStore *bbgo.OrderStore
|
||||||
tradeCollector *bbgo.TradeCollector
|
tradeCollector *bbgo.TradeCollector
|
||||||
|
@ -717,7 +721,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
s.tradeCollector.OnPositionUpdate(func(position types.PositionInterface) {
|
s.tradeCollector.OnPositionUpdate(func(position types.AnyPosition) {
|
||||||
log.Infof("position changed: %s", s.Position)
|
log.Infof("position changed: %s", s.Position)
|
||||||
s.Notify(s.Position)
|
s.Notify(s.Position)
|
||||||
})
|
})
|
||||||
|
@ -775,19 +779,27 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
if err := s.activeMakerOrders.GracefulCancel(ctx, s.session.Exchange); err != nil {
|
if err := s.activeMakerOrders.GracefulCancel(ctx, s.session.Exchange); err != nil {
|
||||||
log.WithError(err).Errorf("graceful cancel order error")
|
log.WithError(err).Errorf("graceful cancel order error")
|
||||||
}
|
}
|
||||||
//log.Error(len(s.Position.Stack), s.Position.AverageCost, kline.Close)
|
|
||||||
|
|
||||||
if s.Position.Position.AverageCost.Div(kline.Close).Compare(fixedpoint.One.Add(s.PushThreshold)) > 0 {
|
//log.Error(len(s.Position.Stack), s.Position.AverageCost, kline.Close)
|
||||||
log.Errorf("push")
|
if s.Position.Position.AverageCost.Div(kline.Close).Compare(fixedpoint.One.Add(s.PositionStack.PushThreshold)) > 0 {
|
||||||
log.Error(s.Position)
|
log.Infof("push position %s", s.Position)
|
||||||
s.Position = s.Position.Push(types.NewPositionFromMarket(s.Market))
|
s.Position = s.Position.Push(types.NewPositionFromMarket(s.Market))
|
||||||
}
|
}
|
||||||
// &&
|
// make it dust naturally by bollmaker
|
||||||
if len(s.Position.Stack) > 1 && s.Position.Stack[len(s.Position.Stack)-2].AverageCost.Compare(kline.Close) < 0 && s.Market.IsDustQuantity(s.Position.Position.GetBase(), kline.Close) {
|
if len(s.Position.Stack) > 1 && s.Position.Stack[len(s.Position.Stack)-2].AverageCost.Compare(kline.Close) < 0 && s.Market.IsDustQuantity(s.Position.GetBase(), kline.Close) {
|
||||||
log.Errorf("pop")
|
log.Infof("pop position %s", s.Position)
|
||||||
log.Error(s.Position)
|
|
||||||
s.Position = s.Position.Pop()
|
s.Position = s.Position.Pop()
|
||||||
}
|
}
|
||||||
|
// make it dust by TP
|
||||||
|
if !s.PositionStack.PopThreshold.IsZero() {
|
||||||
|
if len(s.Position.Stack) > 1 && s.Position.Stack[len(s.Position.Stack)-2].AverageCost.Compare(kline.Close) < 0 && s.Position.AverageCost.Div(kline.Close).Compare(fixedpoint.One.Sub(s.PositionStack.PopThreshold)) < 0 {
|
||||||
|
s.ClosePosition(ctx, fixedpoint.One)
|
||||||
|
log.Infof("pop position %s", s.Position)
|
||||||
|
log.Error("pop position")
|
||||||
|
s.Position = s.Position.Pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//if s.Position.AverageCost.Div(kline.Close).Compare(fixedpoint.One.Sub(s.PopThreshold)) < 0 && && !s.Position.AverageCost.IsZero() {
|
//if s.Position.AverageCost.Div(kline.Close).Compare(fixedpoint.One.Sub(s.PopThreshold)) < 0 && && !s.Position.AverageCost.IsZero() {
|
||||||
// //log.Error(len(s.Position.Stack), s.Position.AverageCost, kline.Close)
|
// //log.Error(len(s.Position.Stack), s.Position.AverageCost, kline.Close)
|
||||||
// log.Errorf("pop")
|
// log.Errorf("pop")
|
||||||
|
|
|
@ -834,7 +834,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
s.tradeCollector.OnPositionUpdate(func(position types.PositionInterface) {
|
s.tradeCollector.OnPositionUpdate(func(position types.AnyPosition) {
|
||||||
log.Infof("position changed: %s", position)
|
log.Infof("position changed: %s", position)
|
||||||
s.Notify(s.Position)
|
s.Notify(s.Position)
|
||||||
})
|
})
|
||||||
|
|
|
@ -620,7 +620,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
s.tradeCollector.OnPositionUpdate(func(position types.PositionInterface) {
|
s.tradeCollector.OnPositionUpdate(func(position types.AnyPosition) {
|
||||||
s.Notifiability.Notify(position)
|
s.Notifiability.Notify(position)
|
||||||
})
|
})
|
||||||
s.tradeCollector.BindStream(session.UserDataStream)
|
s.tradeCollector.BindStream(session.UserDataStream)
|
||||||
|
|
|
@ -179,7 +179,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
s.tradeCollector.OnPositionUpdate(func(position types.PositionInterface) {
|
s.tradeCollector.OnPositionUpdate(func(position types.AnyPosition) {
|
||||||
log.Infof("position changed: %s", s.Position)
|
log.Infof("position changed: %s", s.Position)
|
||||||
s.Notify(s.Position)
|
s.Notify(s.Position)
|
||||||
})
|
})
|
||||||
|
|
|
@ -499,7 +499,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
|
|
||||||
if !s.TrailingStopTarget.TrailingStopCallbackRatio.IsZero() {
|
if !s.TrailingStopTarget.TrailingStopCallbackRatio.IsZero() {
|
||||||
// Update trailing stop when the position changes
|
// Update trailing stop when the position changes
|
||||||
s.tradeCollector.OnPositionUpdate(func(position types.PositionInterface) {
|
s.tradeCollector.OnPositionUpdate(func(position types.AnyPosition) {
|
||||||
// StrategyController
|
// StrategyController
|
||||||
if s.Status != types.StrategyStatusRunning {
|
if s.Status != types.StrategyStatusRunning {
|
||||||
return
|
return
|
||||||
|
|
|
@ -302,7 +302,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
s.tradeCollector.OnPositionUpdate(func(position types.PositionInterface) {
|
s.tradeCollector.OnPositionUpdate(func(position types.AnyPosition) {
|
||||||
log.Infof("position changed: %s", s.Position)
|
log.Infof("position changed: %s", s.Position)
|
||||||
s.Notify(s.Position)
|
s.Notify(s.Position)
|
||||||
})
|
})
|
||||||
|
|
|
@ -781,7 +781,7 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
s.tradeCollector.OnPositionUpdate(func(position types.PositionInterface) {
|
s.tradeCollector.OnPositionUpdate(func(position types.AnyPosition) {
|
||||||
s.Notifiability.Notify(position)
|
s.Notifiability.Notify(position)
|
||||||
})
|
})
|
||||||
s.tradeCollector.OnRecover(func(trade types.Trade) {
|
s.tradeCollector.OnRecover(func(trade types.Trade) {
|
||||||
|
|
|
@ -29,8 +29,9 @@ type PositionRisk struct {
|
||||||
LiquidationPrice fixedpoint.Value `json:"liquidationPrice"`
|
LiquidationPrice fixedpoint.Value `json:"liquidationPrice"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PositionInterface interface {
|
type AnyPosition interface {
|
||||||
AddTrade(td Trade) (profit fixedpoint.Value, netProfit fixedpoint.Value, madeProfit bool)
|
AddTrade(td Trade) (profit fixedpoint.Value, netProfit fixedpoint.Value, madeProfit bool)
|
||||||
|
GetBase() (base fixedpoint.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Position struct {
|
type Position struct {
|
||||||
|
@ -480,140 +481,9 @@ func (stack *PositionStack) Pop() *PositionStack {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPositionStackFromMarket(market Market) *PositionStack {
|
func NewPositionStackFromMarket(market Market) *PositionStack {
|
||||||
pos := &Position{
|
pos := NewPositionFromMarket(market)
|
||||||
Symbol: market.Symbol,
|
|
||||||
BaseCurrency: market.BaseCurrency,
|
|
||||||
QuoteCurrency: market.QuoteCurrency,
|
|
||||||
Market: market,
|
|
||||||
TotalFee: make(map[string]fixedpoint.Value),
|
|
||||||
}
|
|
||||||
return &PositionStack{
|
return &PositionStack{
|
||||||
Position: pos,
|
Position: pos,
|
||||||
Stack: []*Position{pos},
|
Stack: []*Position{pos},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PositionStack) AddTrade(td Trade) (profit fixedpoint.Value, netProfit fixedpoint.Value, madeProfit bool) {
|
|
||||||
price := td.Price
|
|
||||||
quantity := td.Quantity
|
|
||||||
quoteQuantity := td.QuoteQuantity
|
|
||||||
fee := td.Fee
|
|
||||||
|
|
||||||
// calculated fee in quote (some exchange accounts may enable platform currency fee discount, like BNB)
|
|
||||||
// convert platform fee token into USD values
|
|
||||||
var feeInQuote fixedpoint.Value = fixedpoint.Zero
|
|
||||||
|
|
||||||
switch td.FeeCurrency {
|
|
||||||
|
|
||||||
case p.BaseCurrency:
|
|
||||||
quantity = quantity.Sub(fee)
|
|
||||||
|
|
||||||
case p.QuoteCurrency:
|
|
||||||
quoteQuantity = quoteQuantity.Sub(fee)
|
|
||||||
|
|
||||||
default:
|
|
||||||
if p.ExchangeFeeRates != nil {
|
|
||||||
if exchangeFee, ok := p.ExchangeFeeRates[td.Exchange]; ok {
|
|
||||||
if td.IsMaker {
|
|
||||||
feeInQuote = feeInQuote.Add(exchangeFee.MakerFeeRate.Mul(quoteQuantity))
|
|
||||||
} else {
|
|
||||||
feeInQuote = feeInQuote.Add(exchangeFee.TakerFeeRate.Mul(quoteQuantity))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if p.FeeRate != nil {
|
|
||||||
if td.IsMaker {
|
|
||||||
feeInQuote = feeInQuote.Add(p.FeeRate.MakerFeeRate.Mul(quoteQuantity))
|
|
||||||
} else {
|
|
||||||
feeInQuote = feeInQuote.Add(p.FeeRate.TakerFeeRate.Mul(quoteQuantity))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.Lock()
|
|
||||||
defer p.Unlock()
|
|
||||||
|
|
||||||
// update changedAt field before we unlock in the defer func
|
|
||||||
defer func() {
|
|
||||||
p.ChangedAt = td.Time.Time()
|
|
||||||
}()
|
|
||||||
|
|
||||||
p.addTradeFee(td)
|
|
||||||
|
|
||||||
// Base > 0 means we're in long position
|
|
||||||
// Base < 0 means we're in short position
|
|
||||||
switch td.Side {
|
|
||||||
|
|
||||||
case SideTypeBuy:
|
|
||||||
if p.Base.Sign() < 0 {
|
|
||||||
// convert short position to long position
|
|
||||||
if p.Base.Add(quantity).Sign() > 0 {
|
|
||||||
profit = p.AverageCost.Sub(price).Mul(p.Base.Neg())
|
|
||||||
netProfit = p.ApproximateAverageCost.Sub(price).Mul(p.Base.Neg()).Sub(feeInQuote)
|
|
||||||
p.Base = p.Base.Add(quantity)
|
|
||||||
p.Quote = p.Quote.Sub(quoteQuantity)
|
|
||||||
p.AverageCost = price
|
|
||||||
p.ApproximateAverageCost = price
|
|
||||||
p.AccumulatedProfit = p.AccumulatedProfit.Add(profit)
|
|
||||||
return profit, netProfit, true
|
|
||||||
} else {
|
|
||||||
// covering short position
|
|
||||||
p.Base = p.Base.Add(quantity)
|
|
||||||
p.Quote = p.Quote.Sub(quoteQuantity)
|
|
||||||
profit = p.AverageCost.Sub(price).Mul(quantity)
|
|
||||||
netProfit = p.ApproximateAverageCost.Sub(price).Mul(quantity).Sub(feeInQuote)
|
|
||||||
p.AccumulatedProfit = p.AccumulatedProfit.Add(profit)
|
|
||||||
return profit, netProfit, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
divisor := p.Base.Add(quantity)
|
|
||||||
p.ApproximateAverageCost = p.ApproximateAverageCost.Mul(p.Base).
|
|
||||||
Add(quoteQuantity).
|
|
||||||
Add(feeInQuote).
|
|
||||||
Div(divisor)
|
|
||||||
p.AverageCost = p.AverageCost.Mul(p.Base).Add(quoteQuantity).Div(divisor)
|
|
||||||
p.Base = p.Base.Add(quantity)
|
|
||||||
p.Quote = p.Quote.Sub(quoteQuantity)
|
|
||||||
|
|
||||||
return fixedpoint.Zero, fixedpoint.Zero, false
|
|
||||||
|
|
||||||
case SideTypeSell:
|
|
||||||
if p.Base.Sign() > 0 {
|
|
||||||
// convert long position to short position
|
|
||||||
if p.Base.Compare(quantity) < 0 {
|
|
||||||
profit = price.Sub(p.AverageCost).Mul(p.Base)
|
|
||||||
netProfit = price.Sub(p.ApproximateAverageCost).Mul(p.Base).Sub(feeInQuote)
|
|
||||||
p.Base = p.Base.Sub(quantity)
|
|
||||||
p.Quote = p.Quote.Add(quoteQuantity)
|
|
||||||
p.AverageCost = price
|
|
||||||
p.ApproximateAverageCost = price
|
|
||||||
p.AccumulatedProfit = p.AccumulatedProfit.Add(profit)
|
|
||||||
return profit, netProfit, true
|
|
||||||
} else {
|
|
||||||
p.Base = p.Base.Sub(quantity)
|
|
||||||
p.Quote = p.Quote.Add(quoteQuantity)
|
|
||||||
profit = price.Sub(p.AverageCost).Mul(quantity)
|
|
||||||
netProfit = price.Sub(p.ApproximateAverageCost).Mul(quantity).Sub(feeInQuote)
|
|
||||||
p.AccumulatedProfit = p.AccumulatedProfit.Add(profit)
|
|
||||||
return profit, netProfit, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handling short position, since Base here is negative we need to reverse the sign
|
|
||||||
divisor := quantity.Sub(p.Base)
|
|
||||||
p.ApproximateAverageCost = p.ApproximateAverageCost.Mul(p.Base.Neg()).
|
|
||||||
Add(quoteQuantity).
|
|
||||||
Sub(feeInQuote).
|
|
||||||
Div(divisor)
|
|
||||||
|
|
||||||
p.AverageCost = p.AverageCost.Mul(p.Base.Neg()).
|
|
||||||
Add(quoteQuantity).
|
|
||||||
Div(divisor)
|
|
||||||
p.Base = p.Base.Sub(quantity)
|
|
||||||
p.Quote = p.Quote.Add(quoteQuantity)
|
|
||||||
|
|
||||||
return fixedpoint.Zero, fixedpoint.Zero, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return fixedpoint.Zero, fixedpoint.Zero, false
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user