Merge pull request #348 from austin362667/refactor-position

types: refactor Position and related files
This commit is contained in:
Yo-An Lin 2021-12-11 19:25:29 +08:00 committed by GitHub
commit 8fba55707c
11 changed files with 105 additions and 92 deletions

View File

@ -3,7 +3,6 @@ package pnl
import ( import (
"time" "time"
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
@ -33,8 +32,8 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c
var currencyFees = map[string]float64{} var currencyFees = map[string]float64{}
var position = bbgo.NewPositionFromMarket(c.Market) var position = types.NewPositionFromMarket(c.Market)
position.SetFeeRate(bbgo.ExchangeFee{ position.SetFeeRate(types.ExchangeFee{
// binance vip 0 uses 0.075% // binance vip 0 uses 0.075%
MakerFeeRate: fixedpoint.NewFromFloat(0.075 * 0.01), MakerFeeRate: fixedpoint.NewFromFloat(0.075 * 0.01),
TakerFeeRate: fixedpoint.NewFromFloat(0.075 * 0.01), TakerFeeRate: fixedpoint.NewFromFloat(0.075 * 0.01),

View File

@ -199,7 +199,7 @@ type ExchangeSession struct {
// marketDataStores contains the market data store of each market // marketDataStores contains the market data store of each market
marketDataStores map[string]*MarketDataStore marketDataStores map[string]*MarketDataStore
positions map[string]*Position positions map[string]*types.Position
// standard indicators of each market // standard indicators of each market
standardIndicatorSets map[string]*StandardIndicatorSet standardIndicatorSets map[string]*StandardIndicatorSet
@ -236,7 +236,7 @@ func NewExchangeSession(name string, exchange types.Exchange) *ExchangeSession {
markets: make(map[string]types.Market), markets: make(map[string]types.Market),
startPrices: make(map[string]float64), startPrices: make(map[string]float64),
lastPrices: make(map[string]float64), lastPrices: make(map[string]float64),
positions: make(map[string]*Position), positions: make(map[string]*types.Position),
marketDataStores: make(map[string]*MarketDataStore), marketDataStores: make(map[string]*MarketDataStore),
standardIndicatorSets: make(map[string]*StandardIndicatorSet), standardIndicatorSets: make(map[string]*StandardIndicatorSet),
orderStores: make(map[string]*OrderStore), orderStores: make(map[string]*OrderStore),
@ -388,7 +388,7 @@ func (session *ExchangeSession) initSymbol(ctx context.Context, environ *Environ
session.Trades[symbol].Append(trade) session.Trades[symbol].Append(trade)
}) })
position := &Position{ position := &types.Position{
Symbol: symbol, Symbol: symbol,
BaseCurrency: market.BaseCurrency, BaseCurrency: market.BaseCurrency,
QuoteCurrency: market.QuoteCurrency, QuoteCurrency: market.QuoteCurrency,
@ -475,7 +475,7 @@ func (session *ExchangeSession) StandardIndicatorSet(symbol string) (*StandardIn
return set, ok return set, ok
} }
func (session *ExchangeSession) Position(symbol string) (pos *Position, ok bool) { func (session *ExchangeSession) Position(symbol string) (pos *types.Position, ok bool) {
pos, ok = session.positions[symbol] pos, ok = session.positions[symbol]
if ok { if ok {
return pos, ok return pos, ok
@ -486,7 +486,7 @@ func (session *ExchangeSession) Position(symbol string) (pos *Position, ok bool)
return nil, false return nil, false
} }
pos = &Position{ pos = &types.Position{
Symbol: symbol, Symbol: symbol,
BaseCurrency: market.BaseCurrency, BaseCurrency: market.BaseCurrency,
QuoteCurrency: market.QuoteCurrency, QuoteCurrency: market.QuoteCurrency,
@ -496,7 +496,7 @@ func (session *ExchangeSession) Position(symbol string) (pos *Position, ok bool)
return pos, ok return pos, ok
} }
func (session *ExchangeSession) Positions() map[string]*Position { func (session *ExchangeSession) Positions() map[string]*types.Position {
return session.positions return session.positions
} }
@ -712,7 +712,7 @@ func InitExchangeSession(name string, session *ExchangeSession) error {
session.lastPrices = make(map[string]float64) session.lastPrices = make(map[string]float64)
session.startPrices = make(map[string]float64) session.startPrices = make(map[string]float64)
session.marketDataStores = make(map[string]*MarketDataStore) session.marketDataStores = make(map[string]*MarketDataStore)
session.positions = make(map[string]*Position) session.positions = make(map[string]*types.Position)
session.standardIndicatorSets = make(map[string]*StandardIndicatorSet) session.standardIndicatorSets = make(map[string]*StandardIndicatorSet)
session.orderStores = make(map[string]*OrderStore) session.orderStores = make(map[string]*OrderStore)
session.OrderExecutor = &ExchangeOrderExecutor{ session.OrderExecutor = &ExchangeOrderExecutor{

View File

@ -16,15 +16,15 @@ type TradeCollector struct {
tradeStore *TradeStore tradeStore *TradeStore
tradeC chan types.Trade tradeC chan types.Trade
position *Position position *types.Position
orderStore *OrderStore orderStore *OrderStore
tradeCallbacks []func(trade types.Trade) tradeCallbacks []func(trade types.Trade)
positionUpdateCallbacks []func(position *Position) positionUpdateCallbacks []func(position *types.Position)
profitCallbacks []func(trade types.Trade, profit, netProfit fixedpoint.Value) profitCallbacks []func(trade types.Trade, profit, netProfit fixedpoint.Value)
} }
func NewTradeCollector(symbol string, position *Position, orderStore *OrderStore) *TradeCollector { func NewTradeCollector(symbol string, position *types.Position, orderStore *OrderStore) *TradeCollector {
return &TradeCollector{ return &TradeCollector{
Symbol: symbol, Symbol: symbol,
orderSig: sigchan.New(1), orderSig: sigchan.New(1),

View File

@ -17,11 +17,11 @@ func (c *TradeCollector) EmitTrade(trade types.Trade) {
} }
} }
func (c *TradeCollector) OnPositionUpdate(cb func(position *Position)) { func (c *TradeCollector) OnPositionUpdate(cb func(position *types.Position)) {
c.positionUpdateCallbacks = append(c.positionUpdateCallbacks, cb) c.positionUpdateCallbacks = append(c.positionUpdateCallbacks, cb)
} }
func (c *TradeCollector) EmitPositionUpdate(position *Position) { func (c *TradeCollector) EmitPositionUpdate(position *types.Position) {
for _, cb := range c.positionUpdateCallbacks { for _, cb := range c.positionUpdateCallbacks {
cb(position) cb(position)
} }

View File

@ -37,7 +37,7 @@ type TwapExecution struct {
activeMakerOrders *LocalActiveOrderBook activeMakerOrders *LocalActiveOrderBook
orderStore *OrderStore orderStore *OrderStore
position *Position position *types.Position
executionCtx context.Context executionCtx context.Context
cancelExecution context.CancelFunc cancelExecution context.CancelFunc
@ -444,7 +444,7 @@ func (e *TwapExecution) Run(parentCtx context.Context) error {
e.userDataStream = e.Session.Exchange.NewStream() e.userDataStream = e.Session.Exchange.NewStream()
e.userDataStream.OnTradeUpdate(e.handleTradeUpdate) e.userDataStream.OnTradeUpdate(e.handleTradeUpdate)
e.position = &Position{ e.position = &types.Position{
Symbol: e.Symbol, Symbol: e.Symbol,
BaseCurrency: e.market.BaseCurrency, BaseCurrency: e.market.BaseCurrency,
QuoteCurrency: e.market.QuoteCurrency, QuoteCurrency: e.market.QuoteCurrency,

View File

@ -30,7 +30,7 @@ func init() {
} }
type State struct { type State struct {
Position *bbgo.Position `json:"position,omitempty"` Position *types.Position `json:"position,omitempty"`
ProfitStats bbgo.ProfitStats `json:"profitStats,omitempty"` ProfitStats bbgo.ProfitStats `json:"profitStats,omitempty"`
} }
@ -119,7 +119,7 @@ func (s *Strategy) LoadState() error {
// if position is nil, we need to allocate a new position for calculation // if position is nil, we need to allocate a new position for calculation
if s.state.Position == nil { if s.state.Position == nil {
s.state.Position = bbgo.NewPositionFromMarket(s.market) s.state.Position = types.NewPositionFromMarket(s.market)
} }
// init profit states // init profit states
@ -297,7 +297,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
s.state.ProfitStats.AddTrade(trade) s.state.ProfitStats.AddTrade(trade)
}) })
s.tradeCollector.OnPositionUpdate(func(position *bbgo.Position) { s.tradeCollector.OnPositionUpdate(func(position *types.Position) {
log.Infof("position changed: %s", s.state.Position) log.Infof("position changed: %s", s.state.Position)
s.Notify(s.state.Position) s.Notify(s.state.Position)
}) })

View File

@ -31,7 +31,7 @@ type State struct {
Orders []types.SubmitOrder `json:"orders,omitempty"` Orders []types.SubmitOrder `json:"orders,omitempty"`
FilledBuyGrids map[fixedpoint.Value]struct{} `json:"filledBuyGrids"` FilledBuyGrids map[fixedpoint.Value]struct{} `json:"filledBuyGrids"`
FilledSellGrids map[fixedpoint.Value]struct{} `json:"filledSellGrids"` FilledSellGrids map[fixedpoint.Value]struct{} `json:"filledSellGrids"`
Position *bbgo.Position `json:"position,omitempty"` Position *types.Position `json:"position,omitempty"`
AccumulativeArbitrageProfit fixedpoint.Value `json:"accumulativeArbitrageProfit"` AccumulativeArbitrageProfit fixedpoint.Value `json:"accumulativeArbitrageProfit"`
@ -511,7 +511,7 @@ func (s *Strategy) LoadState() error {
FilledBuyGrids: make(map[fixedpoint.Value]struct{}), FilledBuyGrids: make(map[fixedpoint.Value]struct{}),
FilledSellGrids: make(map[fixedpoint.Value]struct{}), FilledSellGrids: make(map[fixedpoint.Value]struct{}),
ArbitrageOrders: make(map[uint64]types.Order), ArbitrageOrders: make(map[uint64]types.Order),
Position: bbgo.NewPositionFromMarket(s.Market), Position: types.NewPositionFromMarket(s.Market),
} }
} else { } else {
s.state = &state s.state = &state
@ -591,16 +591,16 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
}) })
/* /*
if s.TradeService != nil { if s.TradeService != nil {
s.tradeCollector.OnTrade(func(trade types.Trade) { s.tradeCollector.OnTrade(func(trade types.Trade) {
if err := s.TradeService.Mark(ctx, trade.ID, ID); err != nil { if err := s.TradeService.Mark(ctx, trade.ID, ID); err != nil {
log.WithError(err).Error("trade mark error") log.WithError(err).Error("trade mark error")
} }
}) })
} }
*/ */
s.tradeCollector.OnPositionUpdate(func(position *bbgo.Position) { s.tradeCollector.OnPositionUpdate(func(position *types.Position) {
s.Notifiability.Notify(position) s.Notifiability.Notify(position)
}) })
s.tradeCollector.BindStream(session.UserDataStream) s.tradeCollector.BindStream(session.UserDataStream)

View File

@ -27,7 +27,7 @@ func init() {
} }
type State struct { type State struct {
Position *bbgo.Position `json:"position,omitempty"` Position *types.Position `json:"position,omitempty"`
} }
type Target struct { type Target struct {
@ -42,7 +42,7 @@ type PercentageTargetStop struct {
} }
// GenerateOrders generates the orders from the given targets // GenerateOrders generates the orders from the given targets
func (stop *PercentageTargetStop) GenerateOrders(market types.Market, pos *bbgo.Position) []types.SubmitOrder { func (stop *PercentageTargetStop) GenerateOrders(market types.Market, pos *types.Position) []types.SubmitOrder {
var price = pos.AverageCost var price = pos.AverageCost
var quantity = pos.Base var quantity = pos.Base
@ -62,12 +62,12 @@ func (stop *PercentageTargetStop) GenerateOrders(market types.Market, pos *bbgo.
} }
targetOrders = append(targetOrders, types.SubmitOrder{ targetOrders = append(targetOrders, types.SubmitOrder{
Symbol: market.Symbol, Symbol: market.Symbol,
Market: market, Market: market,
Type: types.OrderTypeLimit, Type: types.OrderTypeLimit,
Side: types.SideTypeSell, Side: types.SideTypeSell,
Price: targetPrice, Price: targetPrice,
Quantity: targetQuantity, Quantity: targetQuantity,
MarginSideEffect: target.MarginOrderSideEffect, MarginSideEffect: target.MarginOrderSideEffect,
TimeInForce: "GTC", TimeInForce: "GTC",
}) })
@ -178,7 +178,7 @@ func (s *Strategy) LoadState() error {
} }
if s.state.Position == nil { if s.state.Position == nil {
s.state.Position = bbgo.NewPositionFromMarket(s.Market) s.state.Position = types.NewPositionFromMarket(s.Market)
} }
return nil return nil

View File

@ -41,7 +41,7 @@ func init() {
type State struct { type State struct {
HedgePosition fixedpoint.Value `json:"hedgePosition"` HedgePosition fixedpoint.Value `json:"hedgePosition"`
CoveredPosition fixedpoint.Value `json:"coveredPosition,omitempty"` CoveredPosition fixedpoint.Value `json:"coveredPosition,omitempty"`
Position *bbgo.Position `json:"position,omitempty"` Position *types.Position `json:"position,omitempty"`
ProfitStats ProfitStats `json:"profitStats,omitempty"` ProfitStats ProfitStats `json:"profitStats,omitempty"`
} }
@ -680,7 +680,7 @@ func (s *Strategy) LoadState() error {
// if position is nil, we need to allocate a new position for calculation // if position is nil, we need to allocate a new position for calculation
if s.state.Position == nil { if s.state.Position == nil {
s.state.Position = bbgo.NewPositionFromMarket(s.makerMarket) s.state.Position = types.NewPositionFromMarket(s.makerMarket)
} }
s.state.ProfitStats.Symbol = s.makerMarket.Symbol s.state.ProfitStats.Symbol = s.makerMarket.Symbol
@ -794,14 +794,14 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order
} }
if s.makerSession.MakerFeeRate > 0 || s.makerSession.TakerFeeRate > 0 { if s.makerSession.MakerFeeRate > 0 || s.makerSession.TakerFeeRate > 0 {
s.state.Position.SetExchangeFeeRate(types.ExchangeName(s.MakerExchange), bbgo.ExchangeFee{ s.state.Position.SetExchangeFeeRate(types.ExchangeName(s.MakerExchange), types.ExchangeFee{
MakerFeeRate: s.makerSession.MakerFeeRate, MakerFeeRate: s.makerSession.MakerFeeRate,
TakerFeeRate: s.makerSession.TakerFeeRate, TakerFeeRate: s.makerSession.TakerFeeRate,
}) })
} }
if s.sourceSession.MakerFeeRate > 0 || s.sourceSession.TakerFeeRate > 0 { if s.sourceSession.MakerFeeRate > 0 || s.sourceSession.TakerFeeRate > 0 {
s.state.Position.SetExchangeFeeRate(types.ExchangeName(s.SourceExchange), bbgo.ExchangeFee{ s.state.Position.SetExchangeFeeRate(types.ExchangeName(s.SourceExchange), types.ExchangeFee{
MakerFeeRate: s.sourceSession.MakerFeeRate, MakerFeeRate: s.sourceSession.MakerFeeRate,
TakerFeeRate: s.sourceSession.TakerFeeRate, TakerFeeRate: s.sourceSession.TakerFeeRate,
}) })

View File

@ -1,4 +1,4 @@
package bbgo package types
import ( import (
"fmt" "fmt"
@ -6,7 +6,6 @@ import (
"time" "time"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/c9s/bbgo/pkg/util" "github.com/c9s/bbgo/pkg/util"
"github.com/slack-go/slack" "github.com/slack-go/slack"
) )
@ -21,7 +20,7 @@ type Position struct {
BaseCurrency string `json:"baseCurrency"` BaseCurrency string `json:"baseCurrency"`
QuoteCurrency string `json:"quoteCurrency"` QuoteCurrency string `json:"quoteCurrency"`
Market types.Market `json:"market"` Market Market `json:"market"`
Base fixedpoint.Value `json:"base"` Base fixedpoint.Value `json:"base"`
Quote fixedpoint.Value `json:"quote"` Quote fixedpoint.Value `json:"quote"`
@ -31,13 +30,29 @@ type Position struct {
// This is used for calculating net profit // This is used for calculating net profit
ApproximateAverageCost fixedpoint.Value `json:"approximateAverageCost"` ApproximateAverageCost fixedpoint.Value `json:"approximateAverageCost"`
FeeRate *ExchangeFee `json:"feeRate,omitempty"` FeeRate *ExchangeFee `json:"feeRate,omitempty"`
ExchangeFeeRates map[types.ExchangeName]ExchangeFee `json:"exchangeFeeRates"` ExchangeFeeRates map[ExchangeName]ExchangeFee `json:"exchangeFeeRates"`
// Futures data fields
Isolated bool `json:"isolated"`
Leverage fixedpoint.Value `json:"leverage"`
InitialMargin fixedpoint.Value `json:"initialMargin"`
MaintMargin fixedpoint.Value `json:"maintMargin"`
OpenOrderInitialMargin fixedpoint.Value `json:"openOrderInitialMargin"`
PositionInitialMargin fixedpoint.Value `json:"positionInitialMargin"`
UnrealizedProfit fixedpoint.Value `json:"unrealizedProfit"`
EntryPrice fixedpoint.Value `json:"entryPrice"`
MaxNotional fixedpoint.Value `json:"maxNotional"`
PositionSide string `json:"positionSide"`
PositionAmt fixedpoint.Value `json:"positionAmt"`
Notional fixedpoint.Value `json:"notional"`
IsolatedWallet fixedpoint.Value `json:"isolatedWallet"`
UpdateTime int64 `json:"updateTime"`
sync.Mutex sync.Mutex
} }
func NewPositionFromMarket(market types.Market) *Position { func NewPositionFromMarket(market Market) *Position {
return &Position{ return &Position{
Symbol: market.Symbol, Symbol: market.Symbol,
BaseCurrency: market.BaseCurrency, BaseCurrency: market.BaseCurrency,
@ -64,9 +79,9 @@ func (p *Position) SetFeeRate(exchangeFee ExchangeFee) {
p.FeeRate = &exchangeFee p.FeeRate = &exchangeFee
} }
func (p *Position) SetExchangeFeeRate(ex types.ExchangeName, exchangeFee ExchangeFee) { func (p *Position) SetExchangeFeeRate(ex ExchangeName, exchangeFee ExchangeFee) {
if p.ExchangeFeeRates == nil { if p.ExchangeFeeRates == nil {
p.ExchangeFeeRates = make(map[types.ExchangeName]ExchangeFee) p.ExchangeFeeRates = make(map[ExchangeName]ExchangeFee)
} }
p.ExchangeFeeRates[ex] = exchangeFee p.ExchangeFeeRates[ex] = exchangeFee
@ -127,15 +142,15 @@ func (p *Position) String() string {
) )
} }
func (p *Position) BindStream(stream types.Stream) { func (p *Position) BindStream(stream Stream) {
stream.OnTradeUpdate(func(trade types.Trade) { stream.OnTradeUpdate(func(trade Trade) {
if p.Symbol == trade.Symbol { if p.Symbol == trade.Symbol {
p.AddTrade(trade) p.AddTrade(trade)
} }
}) })
} }
func (p *Position) AddTrades(trades []types.Trade) (fixedpoint.Value, fixedpoint.Value, bool) { func (p *Position) AddTrades(trades []Trade) (fixedpoint.Value, fixedpoint.Value, bool) {
var totalProfitAmount, totalNetProfit fixedpoint.Value var totalProfitAmount, totalNetProfit fixedpoint.Value
for _, trade := range trades { for _, trade := range trades {
if profit, netProfit, madeProfit := p.AddTrade(trade); madeProfit { if profit, netProfit, madeProfit := p.AddTrade(trade); madeProfit {
@ -147,7 +162,7 @@ func (p *Position) AddTrades(trades []types.Trade) (fixedpoint.Value, fixedpoint
return totalProfitAmount, totalNetProfit, totalProfitAmount != 0 return totalProfitAmount, totalNetProfit, totalProfitAmount != 0
} }
func (p *Position) AddTrade(t types.Trade) (profit fixedpoint.Value, netProfit fixedpoint.Value, madeProfit bool) { func (p *Position) AddTrade(t Trade) (profit fixedpoint.Value, netProfit fixedpoint.Value, madeProfit bool) {
price := fixedpoint.NewFromFloat(t.Price) price := fixedpoint.NewFromFloat(t.Price)
quantity := fixedpoint.NewFromFloat(t.Quantity) quantity := fixedpoint.NewFromFloat(t.Quantity)
quoteQuantity := fixedpoint.NewFromFloat(t.QuoteQuantity) quoteQuantity := fixedpoint.NewFromFloat(t.QuoteQuantity)
@ -189,7 +204,7 @@ func (p *Position) AddTrade(t types.Trade) (profit fixedpoint.Value, netProfit f
// Base < 0 means we're in short position // Base < 0 means we're in short position
switch t.Side { switch t.Side {
case types.SideTypeBuy: case SideTypeBuy:
if p.Base < 0 { if p.Base < 0 {
// convert short position to long position // convert short position to long position
if p.Base+quantity > 0 { if p.Base+quantity > 0 {
@ -217,7 +232,7 @@ func (p *Position) AddTrade(t types.Trade) (profit fixedpoint.Value, netProfit f
return 0, 0, false return 0, 0, false
case types.SideTypeSell: case SideTypeSell:
if p.Base > 0 { if p.Base > 0 {
// convert long position to short position // convert long position to short position
if p.Base-quantity < 0 { if p.Base-quantity < 0 {

View File

@ -1,4 +1,4 @@
package bbgo package types
import ( import (
"testing" "testing"
@ -6,7 +6,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
) )
func TestPosition_ExchangeFeeRate_Short(t *testing.T) { func TestPosition_ExchangeFeeRate_Short(t *testing.T) {
@ -17,7 +16,7 @@ func TestPosition_ExchangeFeeRate_Short(t *testing.T) {
} }
feeRate := 0.075 * 0.01 feeRate := 0.075 * 0.01
pos.SetExchangeFeeRate(types.ExchangeBinance, ExchangeFee{ pos.SetExchangeFeeRate(ExchangeBinance, ExchangeFee{
MakerFeeRate: fixedpoint.NewFromFloat(feeRate), MakerFeeRate: fixedpoint.NewFromFloat(feeRate),
TakerFeeRate: fixedpoint.NewFromFloat(feeRate), TakerFeeRate: fixedpoint.NewFromFloat(feeRate),
}) })
@ -27,24 +26,24 @@ func TestPosition_ExchangeFeeRate_Short(t *testing.T) {
fee := quoteQuantity * feeRate fee := quoteQuantity * feeRate
averageCost := (quoteQuantity - fee) / quantity averageCost := (quoteQuantity - fee) / quantity
bnbPrice := 570.0 bnbPrice := 570.0
pos.AddTrade(types.Trade{ pos.AddTrade(Trade{
Exchange: types.ExchangeBinance, Exchange: ExchangeBinance,
Price: 3000.0, Price: 3000.0,
Quantity: quantity, Quantity: quantity,
QuoteQuantity: quoteQuantity, QuoteQuantity: quoteQuantity,
Symbol: "BTCUSDT", Symbol: "BTCUSDT",
Side: types.SideTypeSell, Side: SideTypeSell,
Fee: fee / bnbPrice, Fee: fee / bnbPrice,
FeeCurrency: "BNB", FeeCurrency: "BNB",
}) })
_, netProfit, madeProfit := pos.AddTrade(types.Trade{ _, netProfit, madeProfit := pos.AddTrade(Trade{
Exchange: types.ExchangeBinance, Exchange: ExchangeBinance,
Price: 2000.0, Price: 2000.0,
Quantity: 10.0, Quantity: 10.0,
QuoteQuantity: 2000.0 * 10.0, QuoteQuantity: 2000.0 * 10.0,
Symbol: "BTCUSDT", Symbol: "BTCUSDT",
Side: types.SideTypeBuy, Side: SideTypeBuy,
Fee: 2000.0 * 10.0 * feeRate / bnbPrice, Fee: 2000.0 * 10.0 * feeRate / bnbPrice,
FeeCurrency: "BNB", FeeCurrency: "BNB",
}) })
@ -62,7 +61,7 @@ func TestPosition_ExchangeFeeRate_Long(t *testing.T) {
} }
feeRate := 0.075 * 0.01 feeRate := 0.075 * 0.01
pos.SetExchangeFeeRate(types.ExchangeBinance, ExchangeFee{ pos.SetExchangeFeeRate(ExchangeBinance, ExchangeFee{
MakerFeeRate: fixedpoint.NewFromFloat(feeRate), MakerFeeRate: fixedpoint.NewFromFloat(feeRate),
TakerFeeRate: fixedpoint.NewFromFloat(feeRate), TakerFeeRate: fixedpoint.NewFromFloat(feeRate),
}) })
@ -72,24 +71,24 @@ func TestPosition_ExchangeFeeRate_Long(t *testing.T) {
fee := quoteQuantity * feeRate fee := quoteQuantity * feeRate
averageCost := (quoteQuantity + fee) / quantity averageCost := (quoteQuantity + fee) / quantity
bnbPrice := 570.0 bnbPrice := 570.0
pos.AddTrade(types.Trade{ pos.AddTrade(Trade{
Exchange: types.ExchangeBinance, Exchange: ExchangeBinance,
Price: 3000.0, Price: 3000.0,
Quantity: quantity, Quantity: quantity,
QuoteQuantity: quoteQuantity, QuoteQuantity: quoteQuantity,
Symbol: "BTCUSDT", Symbol: "BTCUSDT",
Side: types.SideTypeBuy, Side: SideTypeBuy,
Fee: fee / bnbPrice, Fee: fee / bnbPrice,
FeeCurrency: "BNB", FeeCurrency: "BNB",
}) })
_, netProfit, madeProfit := pos.AddTrade(types.Trade{ _, netProfit, madeProfit := pos.AddTrade(Trade{
Exchange: types.ExchangeBinance, Exchange: ExchangeBinance,
Price: 4000.0, Price: 4000.0,
Quantity: 10.0, Quantity: 10.0,
QuoteQuantity: 4000.0 * 10.0, QuoteQuantity: 4000.0 * 10.0,
Symbol: "BTCUSDT", Symbol: "BTCUSDT",
Side: types.SideTypeSell, Side: SideTypeSell,
Fee: 4000.0 * 10.0 * feeRate / bnbPrice, Fee: 4000.0 * 10.0 * feeRate / bnbPrice,
FeeCurrency: "BNB", FeeCurrency: "BNB",
}) })
@ -103,7 +102,7 @@ func TestPosition(t *testing.T) {
var feeRate = 0.05 * 0.01 var feeRate = 0.05 * 0.01
var testcases = []struct { var testcases = []struct {
name string name string
trades []types.Trade trades []Trade
expectedAverageCost fixedpoint.Value expectedAverageCost fixedpoint.Value
expectedBase fixedpoint.Value expectedBase fixedpoint.Value
expectedQuote fixedpoint.Value expectedQuote fixedpoint.Value
@ -111,9 +110,9 @@ func TestPosition(t *testing.T) {
}{ }{
{ {
name: "base fee", name: "base fee",
trades: []types.Trade{ trades: []Trade{
{ {
Side: types.SideTypeBuy, Side: SideTypeBuy,
Price: 1000.0, Price: 1000.0,
Quantity: 0.01, Quantity: 0.01,
QuoteQuantity: 1000.0 * 0.01, QuoteQuantity: 1000.0 * 0.01,
@ -128,9 +127,9 @@ func TestPosition(t *testing.T) {
}, },
{ {
name: "quote fee", name: "quote fee",
trades: []types.Trade{ trades: []Trade{
{ {
Side: types.SideTypeSell, Side: SideTypeSell,
Price: 1000.0, Price: 1000.0,
Quantity: 0.01, Quantity: 0.01,
QuoteQuantity: 1000.0 * 0.01, QuoteQuantity: 1000.0 * 0.01,
@ -145,15 +144,15 @@ func TestPosition(t *testing.T) {
}, },
{ {
name: "long", name: "long",
trades: []types.Trade{ trades: []Trade{
{ {
Side: types.SideTypeBuy, Side: SideTypeBuy,
Price: 1000.0, Price: 1000.0,
Quantity: 0.01, Quantity: 0.01,
QuoteQuantity: 1000.0 * 0.01, QuoteQuantity: 1000.0 * 0.01,
}, },
{ {
Side: types.SideTypeBuy, Side: SideTypeBuy,
Price: 2000.0, Price: 2000.0,
Quantity: 0.03, Quantity: 0.03,
QuoteQuantity: 2000.0 * 0.03, QuoteQuantity: 2000.0 * 0.03,
@ -167,21 +166,21 @@ func TestPosition(t *testing.T) {
{ {
name: "long and sell", name: "long and sell",
trades: []types.Trade{ trades: []Trade{
{ {
Side: types.SideTypeBuy, Side: SideTypeBuy,
Price: 1000.0, Price: 1000.0,
Quantity: 0.01, Quantity: 0.01,
QuoteQuantity: 1000.0 * 0.01, QuoteQuantity: 1000.0 * 0.01,
}, },
{ {
Side: types.SideTypeBuy, Side: SideTypeBuy,
Price: 2000.0, Price: 2000.0,
Quantity: 0.03, Quantity: 0.03,
QuoteQuantity: 2000.0 * 0.03, QuoteQuantity: 2000.0 * 0.03,
}, },
{ {
Side: types.SideTypeSell, Side: SideTypeSell,
Price: 3000.0, Price: 3000.0,
Quantity: 0.01, Quantity: 0.01,
QuoteQuantity: 3000.0 * 0.01, QuoteQuantity: 3000.0 * 0.01,
@ -195,21 +194,21 @@ func TestPosition(t *testing.T) {
{ {
name: "long and sell to short", name: "long and sell to short",
trades: []types.Trade{ trades: []Trade{
{ {
Side: types.SideTypeBuy, Side: SideTypeBuy,
Price: 1000.0, Price: 1000.0,
Quantity: 0.01, Quantity: 0.01,
QuoteQuantity: 1000.0 * 0.01, QuoteQuantity: 1000.0 * 0.01,
}, },
{ {
Side: types.SideTypeBuy, Side: SideTypeBuy,
Price: 2000.0, Price: 2000.0,
Quantity: 0.03, Quantity: 0.03,
QuoteQuantity: 2000.0 * 0.03, QuoteQuantity: 2000.0 * 0.03,
}, },
{ {
Side: types.SideTypeSell, Side: SideTypeSell,
Price: 3000.0, Price: 3000.0,
Quantity: 0.10, Quantity: 0.10,
QuoteQuantity: 3000.0 * 0.10, QuoteQuantity: 3000.0 * 0.10,
@ -224,15 +223,15 @@ func TestPosition(t *testing.T) {
{ {
name: "short", name: "short",
trades: []types.Trade{ trades: []Trade{
{ {
Side: types.SideTypeSell, Side: SideTypeSell,
Price: 2000.0, Price: 2000.0,
Quantity: 0.01, Quantity: 0.01,
QuoteQuantity: 2000.0 * 0.01, QuoteQuantity: 2000.0 * 0.01,
}, },
{ {
Side: types.SideTypeSell, Side: SideTypeSell,
Price: 3000.0, Price: 3000.0,
Quantity: 0.03, Quantity: 0.03,
QuoteQuantity: 3000.0 * 0.03, QuoteQuantity: 3000.0 * 0.03,