Skip to content

Commit

Permalink
FTX: Funding rates, payments & stats + order manager tracking (thrash…
Browse files Browse the repository at this point in the history
…er-corp#976)

* Adds basic PoC for calculating/retrieving position data

* A very unfortunate day of miscalculations

* Adds position summary and funding rate details to RPC

* Offline funding rate calculations

* More helpers, more stats, refining data, automated retrieval

* Adds new rpc server commands and attempts some organisation

* lower string, lower stress

* Adds ordermanager config. Fleshes outcli. Tracks positions automatically

* Adds new separation for funding payments/rates

* Combines funding rates and payments

* Fun test coverage

* ALL THE TESTS... I hope

* Fixes

* polishes ftx tests. improves perp check. Loops rates

* Final touches before nit attax

* buff 💪

* Stops NotYetImplemented spam with one simple trick!

* Some lovely little niteroos

* linteroo

* Clarifies a couple of errors to help narrow likely end user problems

* Fixes asset type bug, fixes closed position order return, fixes unset status bug

* Fixes order manager handling when no rates are available yet

* Continues on no funding rates instead. Removes err

* Don't show predicted rate if the time is zero

* Addresses scenario with no funding rate payments

* Bug fixes and commentary before updating maps to use *currency.Item

* Adds a pair key type

* Polishes pKey, fixes map order bug

* key is not a property in the event someone changes the base/quote

* Adds improvements to order processing...Breaks it all

* Shakes up the design of things by removing a function

* Fixes issues with order manager positions. Limits update range

* Fixes build issues. Identification of bad tests.

* Merges and fixes features from master and this branch

* buff linter 💪

* re-gen

* proto regen

* Addresses some nits. But not all of them.

* Fixes issue where funding rates weren't returned 🎉

* completes transition futures tracking to map[*currency.Item]map[*currency.Item]

* who did that? not me

* removes redundant check on account of being redundant and unnecessary

* so buf

* addresses nits: duplications, startTime, loops, go tidy, typos

* fixes minor mistakes

* fixes 🍣 🐻 changes to int64
  • Loading branch information
gloriousCode authored Aug 23, 2022
1 parent e93ee83 commit 46cadd6
Show file tree
Hide file tree
Showing 50 changed files with 9,212 additions and 3,693 deletions.
2 changes: 1 addition & 1 deletion backtester/engine/backtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu

err = bt.Portfolio.UpdatePNL(ev, ev.GetClosePrice())
if err != nil {
if errors.Is(err, gctorder.ErrPositionsNotLoadedForPair) {
if errors.Is(err, gctorder.ErrPositionNotFound) {
// if there is no position yet, there's nothing to update
return nil
}
Expand Down
2 changes: 2 additions & 0 deletions backtester/engine/backtest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,7 @@ func TestProcessFuturesFillEvent(t *testing.T) {
if err != nil {
t.Fatal(err)
}

em.Add(exch)
f, err := funding.SetupFundingManager(em, false, true)
if !errors.Is(err, expectedError) {
Expand Down Expand Up @@ -1300,6 +1301,7 @@ func TestProcessFuturesFillEvent(t *testing.T) {
}
bt.exchangeManager = em
bt.Funding = f

err = pt.SetupCurrencySettingsMap(&exchange.Settings{
Exchange: exch,
Pair: cp,
Expand Down
2 changes: 1 addition & 1 deletion backtester/engine/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool
var err error
bt := New()
bt.exchangeManager = engine.SetupExchangeManager()
bt.orderManager, err = engine.SetupOrderManager(bt.exchangeManager, &engine.CommunicationManager{}, &sync.WaitGroup{}, false, false)
bt.orderManager, err = engine.SetupOrderManager(bt.exchangeManager, &engine.CommunicationManager{}, &sync.WaitGroup{}, false, false, 0)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion backtester/eventhandlers/exchange/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ func applySlippageToPrice(direction gctorder.Side, price, slippageRate decimal.D
case gctorder.Sell, gctorder.Ask, gctorder.Short:
adjustedPrice = price.Mul(slippageRate)
default:
return decimal.Decimal{}, fmt.Errorf("%v %w", direction, gctorder.ErrSideIsInvalid)
return decimal.Zero, fmt.Errorf("%v %w", direction, gctorder.ErrSideIsInvalid)
}
if adjustedPrice.IsZero() {
adjustedPrice = price
Expand Down
6 changes: 3 additions & 3 deletions backtester/eventhandlers/exchange/exchange_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ func TestPlaceOrder(t *testing.T) {
}
em.Add(exch)
bot.ExchangeManager = em
bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false, false)
bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false, false, 0)
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -218,7 +218,7 @@ func TestExecuteOrder(t *testing.T) {
}
em.Add(exch)
bot.ExchangeManager = em
bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false, false)
bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false, false, 0)
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -318,7 +318,7 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) {

em.Add(exch)
bot.ExchangeManager = em
bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false, false)
bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false, false, 0)
if err != nil {
t.Error(err)
}
Expand Down
14 changes: 7 additions & 7 deletions backtester/eventhandlers/portfolio/portfolio.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi
// cannot close a non existent position
return nil, errNoHoldings
}
sizingFunds = positions[len(positions)-1].Exposure
sizingFunds = positions[len(positions)-1].LatestSize
d := positions[len(positions)-1].OpeningDirection
switch d {
case gctorder.Short:
Expand Down Expand Up @@ -452,7 +452,7 @@ func (s *Settings) GetHoldingsForTime(t time.Time) holdings.Holding {
}

// GetPositions returns all futures positions for an event's exchange, asset, pair
func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStats, error) {
func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.Position, error) {
settings, err := p.getFuturesSettingsFromEvent(e)
if err != nil {
return nil, err
Expand All @@ -461,14 +461,14 @@ func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStat
}

// GetLatestPosition returns all futures positions for an event's exchange, asset, pair
func (p *Portfolio) GetLatestPosition(e common.EventHandler) (*gctorder.PositionStats, error) {
func (p *Portfolio) GetLatestPosition(e common.EventHandler) (*gctorder.Position, error) {
settings, err := p.getFuturesSettingsFromEvent(e)
if err != nil {
return nil, err
}
positions := settings.FuturesTracker.GetPositions()
if len(positions) == 0 {
return nil, fmt.Errorf("%w %v %v %v", gctorder.ErrPositionsNotLoadedForPair, e.GetExchange(), e.GetAssetType(), e.Pair())
return nil, fmt.Errorf("%w %v %v %v", gctorder.ErrPositionNotFound, e.GetExchange(), e.GetAssetType(), e.Pair())
}
return &positions[len(positions)-1], nil
}
Expand Down Expand Up @@ -623,7 +623,7 @@ func (p *Portfolio) CreateLiquidationOrdersForExchange(ev common.DataEventHandle
continue
}
pos := positions[len(positions)-1]
if !pos.Exposure.IsPositive() {
if !pos.LatestSize.IsPositive() {
continue
}
direction := gctorder.Short
Expand All @@ -644,8 +644,8 @@ func (p *Portfolio) CreateLiquidationOrdersForExchange(ev common.DataEventHandle
Direction: direction,
Status: gctorder.Liquidated,
ClosePrice: ev.GetClosePrice(),
Amount: pos.Exposure,
AllocatedFunds: pos.Exposure,
Amount: pos.LatestSize,
AllocatedFunds: pos.LatestSize,
OrderType: gctorder.Market,
LiquidatingPosition: true,
})
Expand Down
6 changes: 3 additions & 3 deletions backtester/eventhandlers/portfolio/portfolio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -730,8 +730,8 @@ func TestCalculatePNL(t *testing.T) {
ev.Time = tt0

err = p.UpdatePNL(ev, decimal.Zero)
if !errors.Is(err, gctorder.ErrPositionsNotLoadedForPair) {
t.Errorf("received: %v, expected: %v", err, gctorder.ErrPositionsNotLoadedForPair)
if !errors.Is(err, gctorder.ErrPositionNotFound) {
t.Errorf("received: %v, expected: %v", err, gctorder.ErrPositionNotFound)
}

od := &gctorder.Detail{
Expand Down Expand Up @@ -964,7 +964,7 @@ func TestGetLatestPNLForEvent(t *testing.T) {
if !errors.Is(err, nil) {
t.Errorf("received: %v, expected: %v", err, nil)
}
expectedError = gctorder.ErrPositionsNotLoadedForPair
expectedError = gctorder.ErrPositionNotFound
_, err = p.GetLatestPNLForEvent(ev)
if !errors.Is(err, expectedError) {
t.Fatalf("received '%v' expected '%v'", err, expectedError)
Expand Down
2 changes: 1 addition & 1 deletion backtester/eventhandlers/portfolio/portfolio_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ type Handler interface {
setHoldingsForOffset(*holdings.Holding, bool) error
UpdateHoldings(common.DataEventHandler, funding.IFundReleaser) error
GetComplianceManager(string, asset.Item, currency.Pair) (*compliance.Manager, error)
GetPositions(common.EventHandler) ([]gctorder.PositionStats, error)
GetPositions(common.EventHandler) ([]gctorder.Position, error)
TrackFuturesOrder(fill.Event, funding.IFundReleaser) (*PNLSummary, error)
UpdatePNL(common.EventHandler, decimal.Decimal) error
GetLatestPNLForEvent(common.EventHandler) (*PNLSummary, error)
Expand Down
38 changes: 19 additions & 19 deletions backtester/eventhandlers/portfolio/size/size.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import (
// SizeOrder is responsible for ensuring that the order size is within config limits
func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exchange.Settings) (*order.Order, decimal.Decimal, error) {
if o == nil || cs == nil {
return nil, decimal.Decimal{}, common.ErrNilArguments
return nil, decimal.Zero, common.ErrNilArguments
}
if amountAvailable.LessThanOrEqual(decimal.Zero) {
return nil, decimal.Decimal{}, errNoFunds
return nil, decimal.Zero, errNoFunds
}
retOrder, ok := o.(*order.Order)
if !ok {
return nil, decimal.Decimal{}, fmt.Errorf("%w expected order event", common.ErrInvalidDataType)
return nil, decimal.Zero, fmt.Errorf("%w expected order event", common.ErrInvalidDataType)
}

if fde := o.GetFillDependentEvent(); fde != nil && fde.MatchOrderAmount() {
Expand All @@ -35,18 +35,18 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc
FreeCollateral: amountAvailable,
})
if err != nil {
return nil, decimal.Decimal{}, err
return nil, decimal.Zero, err
}
initialAmount := amountAvailable.Mul(scalingInfo.Weighting).Div(fde.GetClosePrice())
oNotionalPosition := initialAmount.Mul(o.GetClosePrice())
sizedAmount, estFee, err := s.calculateAmount(o.GetDirection(), o.GetClosePrice(), oNotionalPosition, cs, o)
if err != nil {
return nil, decimal.Decimal{}, err
return nil, decimal.Zero, err
}
scaledCollateralFromAmount := sizedAmount.Mul(scalingInfo.Weighting)
excess := amountAvailable.Sub(sizedAmount).Add(scaledCollateralFromAmount)
if excess.IsNegative() {
return nil, decimal.Decimal{}, fmt.Errorf("%w not enough funding for position", errCannotAllocate)
return nil, decimal.Zero, fmt.Errorf("%w not enough funding for position", errCannotAllocate)
}
retOrder.SetAmount(sizedAmount)
fde.SetAmount(sizedAmount)
Expand All @@ -56,7 +56,7 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc

amount, estFee, err := s.calculateAmount(retOrder.Direction, retOrder.ClosePrice, amountAvailable, cs, o)
if err != nil {
return nil, decimal.Decimal{}, err
return nil, decimal.Zero, err
}
retOrder.SetAmount(amount)

Expand All @@ -73,12 +73,12 @@ func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable d
// check size against currency specific settings
amount, fee, err = s.calculateBuySize(price, amountAvailable, cs.TakerFee, o.GetBuyLimit(), cs.BuySide)
if err != nil {
return decimal.Decimal{}, decimal.Decimal{}, err
return decimal.Zero, decimal.Zero, err
}
// check size against portfolio specific settings
portfolioAmount, portfolioFee, err = s.calculateBuySize(price, amountAvailable, cs.TakerFee, o.GetBuyLimit(), s.BuySide)
if err != nil {
return decimal.Decimal{}, decimal.Decimal{}, err
return decimal.Zero, decimal.Zero, err
}
// global settings overrule individual currency settings
if amount.GreaterThan(portfolioAmount) {
Expand All @@ -89,24 +89,24 @@ func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable d
// check size against currency specific settings
amount, fee, err = s.calculateSellSize(price, amountAvailable, cs.TakerFee, o.GetSellLimit(), cs.SellSide)
if err != nil {
return decimal.Decimal{}, decimal.Decimal{}, err
return decimal.Zero, decimal.Zero, err
}
// check size against portfolio specific settings
portfolioAmount, portfolioFee, err = s.calculateSellSize(price, amountAvailable, cs.TakerFee, o.GetSellLimit(), s.SellSide)
if err != nil {
return decimal.Decimal{}, decimal.Decimal{}, err
return decimal.Zero, decimal.Zero, err
}
// global settings overrule individual currency settings
if amount.GreaterThan(portfolioAmount) {
amount = portfolioAmount
fee = portfolioFee
}
default:
return decimal.Decimal{}, decimal.Decimal{}, fmt.Errorf("%w at %v for %v %v %v", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair())
return decimal.Zero, decimal.Zero, fmt.Errorf("%w at %v for %v %v %v", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair())
}

if amount.LessThanOrEqual(decimal.Zero) {
return decimal.Decimal{}, decimal.Decimal{}, fmt.Errorf("%w at %v for %v %v %v, no amount sized", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair())
return decimal.Zero, decimal.Zero, fmt.Errorf("%w at %v for %v %v %v, no amount sized", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair())
}

if o.GetAmount().IsPositive() && o.GetAmount().LessThanOrEqual(amount) {
Expand All @@ -125,10 +125,10 @@ func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable d
// this can only attempt to factor the potential fee to remain under the max rules
func (s *Size) calculateBuySize(price, availableFunds, feeRate, buyLimit decimal.Decimal, minMaxSettings exchange.MinMax) (amount, fee decimal.Decimal, err error) {
if availableFunds.LessThanOrEqual(decimal.Zero) {
return decimal.Decimal{}, decimal.Decimal{}, errNoFunds
return decimal.Zero, decimal.Zero, errNoFunds
}
if price.IsZero() {
return decimal.Decimal{}, decimal.Decimal{}, nil
return decimal.Zero, decimal.Zero, nil
}
amount = availableFunds.Mul(decimal.NewFromInt(1).Sub(feeRate)).Div(price)
if !buyLimit.IsZero() &&
Expand All @@ -144,7 +144,7 @@ func (s *Size) calculateBuySize(price, availableFunds, feeRate, buyLimit decimal
amount = minMaxSettings.MaximumTotal.Mul(decimal.NewFromInt(1).Sub(feeRate)).Div(price)
}
if amount.LessThan(minMaxSettings.MinimumSize) && minMaxSettings.MinimumSize.GreaterThan(decimal.Zero) {
return decimal.Decimal{}, decimal.Decimal{}, fmt.Errorf("%w. Sized: '%v' Minimum: '%v'", errLessThanMinimum, amount, minMaxSettings.MinimumSize)
return decimal.Zero, decimal.Zero, fmt.Errorf("%w. Sized: '%v' Minimum: '%v'", errLessThanMinimum, amount, minMaxSettings.MinimumSize)
}
fee = amount.Mul(price).Mul(feeRate)
return amount, fee, nil
Expand All @@ -158,10 +158,10 @@ func (s *Size) calculateBuySize(price, availableFunds, feeRate, buyLimit decimal
// this can only attempt to factor the potential fee to remain under the max rules
func (s *Size) calculateSellSize(price, baseAmount, feeRate, sellLimit decimal.Decimal, minMaxSettings exchange.MinMax) (amount, fee decimal.Decimal, err error) {
if baseAmount.LessThanOrEqual(decimal.Zero) {
return decimal.Decimal{}, decimal.Decimal{}, errNoFunds
return decimal.Zero, decimal.Zero, errNoFunds
}
if price.IsZero() {
return decimal.Decimal{}, decimal.Decimal{}, nil
return decimal.Zero, decimal.Zero, nil
}
oneMFeeRate := decimal.NewFromInt(1).Sub(feeRate)
amount = baseAmount.Mul(oneMFeeRate)
Expand All @@ -178,7 +178,7 @@ func (s *Size) calculateSellSize(price, baseAmount, feeRate, sellLimit decimal.D
amount = minMaxSettings.MaximumTotal.Mul(oneMFeeRate).Div(price)
}
if amount.LessThan(minMaxSettings.MinimumSize) && minMaxSettings.MinimumSize.GreaterThan(decimal.Zero) {
return decimal.Decimal{}, decimal.Decimal{}, fmt.Errorf("%w. Sized: '%v' Minimum: '%v'", errLessThanMinimum, amount, minMaxSettings.MinimumSize)
return decimal.Zero, decimal.Zero, fmt.Errorf("%w. Sized: '%v' Minimum: '%v'", errLessThanMinimum, amount, minMaxSettings.MinimumSize)
}
fee = amount.Mul(price).Mul(feeRate)
return amount, fee, nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTra

// createSignals creates signals based on the relationships between
// futures and spot signals
func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresSignal *signal.Signal, diffBetweenFuturesSpot decimal.Decimal, isLastEvent bool) ([]signal.Event, error) {
func (s *Strategy) createSignals(pos []order.Position, spotSignal, futuresSignal *signal.Signal, diffBetweenFuturesSpot decimal.Decimal, isLastEvent bool) ([]signal.Event, error) {
if spotSignal == nil {
return nil, fmt.Errorf("%w missing spot signal", common.ErrNilArguments)
}
Expand Down Expand Up @@ -135,14 +135,6 @@ func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresS
spotSignal.FillDependentEvent = futuresSignal
// only appending spotSignal as futuresSignal will be raised later
response = append(response, spotSignal)
case pos[len(pos)-1].Status == order.Open &&
isLastEvent:
// closing positions on last event
spotSignal.SetDirection(order.ClosePosition)
spotSignal.AppendReason("Selling asset on last event")
futuresSignal.SetDirection(order.ClosePosition)
futuresSignal.AppendReason("Closing position on last event")
response = append(response, futuresSignal, spotSignal)
case pos[len(pos)-1].Status == order.Open &&
diffBetweenFuturesSpot.LessThanOrEqual(s.closeShortDistancePercentage):
// closing positions when custom threshold met
Expand All @@ -151,6 +143,14 @@ func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresS
futuresSignal.SetDirection(order.ClosePosition)
futuresSignal.AppendReasonf("Closing position. Met threshold %v", s.closeShortDistancePercentage)
response = append(response, futuresSignal, spotSignal)
case pos[len(pos)-1].Status == order.Open &&
isLastEvent:
// closing positions on last event
spotSignal.SetDirection(order.ClosePosition)
spotSignal.AppendReason("Selling asset on last event")
futuresSignal.SetDirection(order.ClosePosition)
futuresSignal.AppendReason("Closing position on last event")
response = append(response, futuresSignal, spotSignal)
default:
response = append(response, spotSignal, futuresSignal)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ func TestCreateSignals(t *testing.T) {
t.Errorf("received '%v' expected '%v", err, expectedError)
}

// case len(pos) == 0:
// targeting first case
expectedError = nil
futuresSignal := &signal.Signal{
Base: &event.Base{AssetType: asset.Futures},
Expand All @@ -199,9 +199,8 @@ func TestCreateSignals(t *testing.T) {
t.Errorf("received '%v' expected '%v", resp[0].GetAssetType(), asset.Spot)
}

// case len(pos) > 0 && pos[len(pos)-1].Status == order.Open &&
// diffBetweenFuturesSpot.LessThanOrEqual(s.closeShortDistancePercentage):
pos := []gctorder.PositionStats{
// targeting second case:
pos := []gctorder.Position{
{
Status: gctorder.Open,
},
Expand All @@ -226,9 +225,7 @@ func TestCreateSignals(t *testing.T) {
t.Fatal("unhandled issue in test scenario")
}

// case len(pos) > 0 &&
// pos[len(pos)-1].Status == order.Open &&
// isLastEvent:
// targeting third case
resp, err = s.createSignals(pos, spotSignal, futuresSignal, decimal.Zero, true)
if !errors.Is(err, expectedError) {
t.Errorf("received '%v' expected '%v", err, expectedError)
Expand All @@ -248,9 +245,8 @@ func TestCreateSignals(t *testing.T) {
if !caseTested {
t.Fatal("unhandled issue in test scenario")
}
// case len(pos) > 0 &&
// pos[len(pos)-1].Status == order.Closed &&
// diffBetweenFuturesSpot.GreaterThan(s.openShortDistancePercentage):

// targeting first case after a cash and carry is completed, have a new one opened
pos[0].Status = gctorder.Closed
resp, err = s.createSignals(pos, spotSignal, futuresSignal, decimal.NewFromInt(1337), true)
if !errors.Is(err, expectedError) {
Expand All @@ -275,7 +271,7 @@ func TestCreateSignals(t *testing.T) {
t.Fatal("unhandled issue in test scenario")
}

// default:
// targeting default case
pos[0].Status = gctorder.UnknownStatus
resp, err = s.createSignals(pos, spotSignal, futuresSignal, decimal.NewFromInt(1337), true)
if !errors.Is(err, expectedError) {
Expand Down Expand Up @@ -303,8 +299,8 @@ type portfolerino struct {
}

// GetPositions overrides default implementation
func (p portfolerino) GetPositions(common.EventHandler) ([]gctorder.PositionStats, error) {
return []gctorder.PositionStats{
func (p portfolerino) GetPositions(common.EventHandler) ([]gctorder.Position, error) {
return []gctorder.Position{
{
Exchange: exchangeName,
Asset: asset.Spot,
Expand Down
Loading

0 comments on commit 46cadd6

Please sign in to comment.