mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-23 23:35:14 +00:00
bbgo: refactor trade collector using position interface
This commit is contained in:
parent
8652b4e043
commit
a5e1ae4867
|
@ -18,17 +18,17 @@ type TradeCollector struct {
|
||||||
|
|
||||||
tradeStore *TradeStore
|
tradeStore *TradeStore
|
||||||
tradeC chan types.Trade
|
tradeC chan types.Trade
|
||||||
position *types.Position
|
position types.PositionInterface
|
||||||
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.Position)
|
positionUpdateCallbacks []func(position types.PositionInterface)
|
||||||
profitCallbacks []func(trade types.Trade, profit, netProfit fixedpoint.Value)
|
profitCallbacks []func(trade types.Trade, profit, netProfit fixedpoint.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTradeCollector(symbol string, position *types.Position, orderStore *OrderStore) *TradeCollector {
|
func NewTradeCollector(symbol string, position types.PositionInterface, 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.Position {
|
func (c *TradeCollector) Position() types.PositionInterface {
|
||||||
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.Position)) {
|
func (c *TradeCollector) OnPositionUpdate(cb func(position types.PositionInterface)) {
|
||||||
c.positionUpdateCallbacks = append(c.positionUpdateCallbacks, cb)
|
c.positionUpdateCallbacks = append(c.positionUpdateCallbacks, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TradeCollector) EmitPositionUpdate(position *types.Position) {
|
func (c *TradeCollector) EmitPositionUpdate(position types.PositionInterface) {
|
||||||
for _, cb := range c.positionUpdateCallbacks {
|
for _, cb := range c.positionUpdateCallbacks {
|
||||||
cb(position)
|
cb(position)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,10 @@ type PositionRisk struct {
|
||||||
LiquidationPrice fixedpoint.Value `json:"liquidationPrice"`
|
LiquidationPrice fixedpoint.Value `json:"liquidationPrice"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PositionInterface interface {
|
||||||
|
AddTrade(td Trade) (profit fixedpoint.Value, netProfit fixedpoint.Value, madeProfit bool)
|
||||||
|
}
|
||||||
|
|
||||||
type Position struct {
|
type Position struct {
|
||||||
Symbol string `json:"symbol" db:"symbol"`
|
Symbol string `json:"symbol" db:"symbol"`
|
||||||
BaseCurrency string `json:"baseCurrency" db:"base"`
|
BaseCurrency string `json:"baseCurrency" db:"base"`
|
||||||
|
@ -453,3 +457,163 @@ func (p *Position) AddTrade(td Trade) (profit fixedpoint.Value, netProfit fixedp
|
||||||
|
|
||||||
return fixedpoint.Zero, fixedpoint.Zero, false
|
return fixedpoint.Zero, fixedpoint.Zero, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PositionStack struct {
|
||||||
|
*Position
|
||||||
|
Stack []*Position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stack *PositionStack) Push(pos *Position) *PositionStack {
|
||||||
|
stack.Position = pos
|
||||||
|
stack.Stack = append(stack.Stack, pos)
|
||||||
|
return stack
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stack *PositionStack) Pop() *PositionStack {
|
||||||
|
if len(stack.Stack) < 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
stack.Position = stack.Stack[len(stack.Stack)-1]
|
||||||
|
stack.Stack = stack.Stack[:len(stack.Stack)-1]
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPositionStackFromMarket(market Market) *PositionStack {
|
||||||
|
pos := &Position{
|
||||||
|
Symbol: market.Symbol,
|
||||||
|
BaseCurrency: market.BaseCurrency,
|
||||||
|
QuoteCurrency: market.QuoteCurrency,
|
||||||
|
Market: market,
|
||||||
|
TotalFee: make(map[string]fixedpoint.Value),
|
||||||
|
}
|
||||||
|
return &PositionStack{
|
||||||
|
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