Portfolio#

tradeexecutor.state.portfolio.Portfolio Python class in Trading Strategy framework.

class Portfolio[source]#

Bases: object

Represents a trading portfolio on DEX markets.

Multiple trading pair issue: We do not identify the actual assets we have purchased, but the trading pairs that we used to purchase them. Thus, each position marks “openness” in a certain trading pair. For example open position of WBNB-BUSD means we have bought X BNB tokens through WBNB-BUSD trading pair. But because this is DEX market, we could enter and exit through WBNB-USDT, WBNB-ETH, etc. and our position is not really tied to a trading pair. However, because all TradFi trading view the world through trading pairs, we keep the tradition here.

__init__(next_position_id=1, next_trade_id=1, next_balance_update_id=1, open_positions=<factory>, reserves=<factory>, closed_positions=<factory>, frozen_positions=<factory>)#
Parameters:
Return type:

None

Methods

__init__([next_position_id, next_trade_id, ...])

adjust_reserves(asset, amount)

Remove currency from reserved.

check_for_nonce_reuse(nonce)

A helper assert to see we are not generating invalid transactions somewhere.

correct_open_position_balance(position, ...)

Create an accounting entry trade that correct the balance.

create_trade(strategy_cycle_at, pair, ...[, ...])

Create a trade.

find_position_for_trade(trade)

Find a position tha trade belongs for.

from_dict(kvs, *[, infer_missing])

from_json(s, *[, parse_float, parse_int, ...])

get_all_positions()

Get open, closed and frozen, positions.

get_all_positions_filtered()

Get open, closed and frozen, positions filtered to remove repaired or failed trades.

get_all_traded_pairs()

Get all pairs for which we have or had positions.

get_all_trades()

Iterate through all trades: completed, failed and in progress

get_closed_profit_usd()

Get the value of the portfolio based on the latest pricing.

get_current_cash()

Get how much reserve stablecoins we have.

get_default_reserve_asset()

Gets the default reserve currency associated with this state.

get_default_reserve_position()

Get the default reserve position.

get_equity_for_pair(pair)

Return how much equity allocation we have in a certain trading pair.

get_executed_positions()

Get all positions with already executed trades.

get_existing_open_position_by_trading_pair(pair)

Get a position by a trading pair smart contract address identifier.

get_first_and_last_executed_trade()

Get first and last trades overall.

get_frozen_position_equity()

Get the value of trading positions that are frozen currently.

get_initial_deposit()

How much we invested at the beginning of a backtest.

get_live_position_equity()

Get the value of current trading positions plus unexecuted trades.

get_open_position_equity()

Get the value of current trading positions.

get_open_position_for_asset(asset)

Get open position for a trading pair.

get_open_position_for_pair(pair)

Get Open position for a trading pair.

get_open_positions()

Get currently open positions.

get_open_quantities_by_internal_id()

Return the current holdings in different trading pairs.

get_open_quantities_by_position_id()

Return the current ownerships.

get_position_by_id(position_id)

Get any position open/closed/frozen by id.

get_position_by_trading_pair(pair)

Get open position by a trading pair smart contract address identifier.

get_positions_closed_at(ts)

Get positions that were closed at a specific timestamp.

get_reserve_assets()

Get reserves assets.

get_reserve_position(asset)

Get reserves for a certain reserve asset.

get_single_pair()

Return the only trading pair a single pair strategy has been trading.

get_total_equity()

Get the value of the portfolio based on the latest pricing.

get_trade_by_id(trade_id)

Look up any trade in all positions.

get_trading_history_duration()

How long this portfolio has trading history.

get_unfrozen_positions()

Get positions that have been repaired.

get_unrealised_profit_usd()

Get the profit of currently open positions.

has_unexecuted_trades()

Do we have any trades that have capital allocated, but not executed yet.

initialise_reserves(asset)

Create the initial reserve currency list.

is_empty()

This portfolio has no open or past trades or any reserves.

move_capital_from_reserves_to_trade(trade[, ...])

Allocate capital from reserves to trade instance.

open_new_position(ts, pair, assumed_price, ...)

Opens a new trading position.

return_capital_to_reserves(trade[, ...])

Return capital to reserves after a sell.

revalue_positions(ts, valuation_method[, ...])

Revalue all open positions in the portfolio.

schema(*[, infer_missing, only, exclude, ...])

to_dict([encode_json])

to_json(*[, skipkeys, ensure_ascii, ...])

update_reserves(new_reserves)

Update current reserves.

Attributes

next_balance_update_id

Each balance update event gets it unique id as a running counter.

next_position_id

Each position gets it unique running counter id.

next_trade_id

Each trade gets it unique id as a running counter.

open_positions

Currently open trading positions

reserves

Currently held reserve assets Token address -> reserve position mapping.

closed_positions

Trades completed in the past

frozen_positions

Positions that have failed sells, or otherwise immovable and need manual clean up.

next_position_id: int = 1#

Each position gets it unique running counter id.

next_trade_id: int = 1#

Each trade gets it unique id as a running counter. Trade ids are unique across different positions.

next_balance_update_id: int = 1#

Each balance update event gets it unique id as a running counter.

open_positions: Dict[int, TradingPosition]#

Currently open trading positions

reserves: Dict[str, ReservePosition]#

Currently held reserve assets Token address -> reserve position mapping.

closed_positions: Dict[int, TradingPosition]#

Trades completed in the past

frozen_positions: Dict[int, TradingPosition]#

Positions that have failed sells, or otherwise immovable and need manual clean up. Failure reasons could include - blockchain halted - ERC-20 token tax fees - rug pull token - transfer disabled

is_empty()[source]#

This portfolio has no open or past trades or any reserves.

get_position_by_id(position_id)[source]#

Get any position open/closed/frozen by id.

Always assume the position for a position_id exists.

Parameters:

position_id (int) – Internal running counter id for the position inside this portfolio.

Returns:

Always returns

Throw:

Fails with py:class:AssertionError if there is no such position.

Return type:

TradingPosition

get_trade_by_id(trade_id)[source]#

Look up any trade in all positions.

Note

Slow lookup. Only designed for testing.

Returns:

Found trade or

Parameters:

trade_id (int) –

Return type:

Optional[TradeExecution]

get_all_positions()[source]#

Get open, closed and frozen, positions.

Return type:

Iterable[TradingPosition]

get_all_positions_filtered()[source]#

Get open, closed and frozen, positions filtered to remove repaired or failed trades.

Return type:

Iterable[TradingPosition]

get_open_positions()[source]#

Get currently open positions.

Return type:

Iterable[TradingPosition]

get_unfrozen_positions()[source]#

Get positions that have been repaired.

Return type:

Iterable[TradingPosition]

get_executed_positions()[source]#

Get all positions with already executed trades.

Ignore positions that are still pending - they have only planned trades.

Return type:

Iterable[TradingPosition]

get_open_position_for_pair(pair)[source]#

Get Open position for a trading pair.

Parameters:

pair (TradingPairIdentifier) –

Return type:

Optional[TradingPosition]

get_open_position_for_asset(asset)[source]#

Get open position for a trading pair.

  • Check all open positions where asset is a base token

Returns:

Tingle open position or None

Raises:

MultipleOpenPositionsWithAsset – If more than one position is open

Parameters:

asset (AssetIdentifier) –

Return type:

Optional[TradingPosition]

get_open_quantities_by_position_id()[source]#

Return the current ownerships.

Keyed by position id -> quantity.

Return type:

Dict[str, Decimal]

get_open_quantities_by_internal_id()[source]#

Return the current holdings in different trading pairs.

Keyed by trading pair internal id -> quantity.

Return type:

Dict[int, Decimal]

open_new_position(ts, pair, assumed_price, reserve_currency, reserve_currency_price)[source]#

Opens a new trading position.

  • Marks the position opened.

  • Does not add any trades yet

  • Marks the current value of the portfolio at the trade opening time, as we need to use this for the risk calculations

Parameters:
Return type:

TradingPosition

get_position_by_trading_pair(pair)[source]#

Get open position by a trading pair smart contract address identifier.

For Uniswap-likes we use the pool address as the persistent identifier for each trading pair.

Parameters:

pair (TradingPairIdentifier) –

Return type:

Optional[TradingPosition]

get_existing_open_position_by_trading_pair(pair)[source]#

Get a position by a trading pair smart contract address identifier.

The position must have already executed trades (cannot be planned position(.

Parameters:

pair (TradingPairIdentifier) –

Return type:

Optional[TradingPosition]

get_positions_closed_at(ts)[source]#

Get positions that were closed at a specific timestamp.

Useful to display closed positions after the rebalance.

Parameters:

ts (datetime) –

Return type:

Iterable[TradingPosition]

create_trade(strategy_cycle_at, pair, quantity, reserve, assumed_price, trade_type, reserve_currency, reserve_currency_price, notes=None, pair_fee=None, lp_fees_estimated=None, planned_mid_price=None, price_structure=None, position=None, slippage_tolerance=None)[source]#

Create a trade.

Trade can be opened by knowing how much you want to buy (quantity) or how much cash you have to buy (reserve).

Parameters:
  • strategy_cycle_at (datetime) – The strategy cycle timestamp for which this trade was executed.

  • trade_id – Trade id allocated by the portfolio

  • quantity (Optional[Decimal]) –

    How many units this trade does.

    Positive for buys, negative for sells in the spot market.

  • assumed_price (float) –

    The planned execution price.

    This is the price we expect to pay per quantity unit after the execution. This is the mid price + any LP fees included.

  • trade_type (TradeType) – What kind of a trade is this.

  • reserve_currency (AssetIdentifier) –

    Which portfolio reserve we use for this trade.

    param reserve_currency_price:

    If the quote token is not USD, then the exchange rate between USD and quote token we assume we have.

    Actual exchange rate may depend on the execution.

  • notes (Optional[str]) – Any human-readable remarks we want to tell about this trade.

  • pair_fee (Optional[float]) – The fee tier from the trading pair / overriden fee.

  • lp_fees_estimated (Optional[float]) – HOw much we estimate to pay in LP fees (dollar)

  • planned_mid_price (Optional[float]) – What was the mid-price of the trading pair when we started to plan this trade.

  • reserve (Optional[Decimal]) –

    How many reserve units this trade produces/consumes.

    I.e. dollar amount for buys/sells.

  • price_structure (Optional[TradePricing]) –

    The full planned price structure for this trade.

    The state of the market at the time of planning the trade, and what fees we assumed we are going to get.

  • position (Optional[TradingPosition]) –

    Override the position for the trade.

    Use for repair trades.

  • slippage_tolerance (Optional[float]) –

    Slippage tolerance for this trade.

    See tradeexecutor.state.trade.TradeExecution.slippage_tolerance for details.

  • pair (TradingPairIdentifier) –

  • reserve_currency_price (float) –

Returns:

Tuple of entries

  • Trade position (old/new)

  • New trade

  • True if a a new position was opened

Return type:

Tuple[TradingPosition, TradeExecution, bool]

get_current_cash()[source]#

Get how much reserve stablecoins we have.

Return type:

float

get_open_position_equity()[source]#

Get the value of current trading positions.

Return type:

float

get_frozen_position_equity()[source]#

Get the value of trading positions that are frozen currently.

Return type:

float

get_live_position_equity()[source]#

Get the value of current trading positions plus unexecuted trades.

Return type:

float

get_total_equity()[source]#

Get the value of the portfolio based on the latest pricing.

This is

  • Value of the positions

plus

  • Cash in the hand

Return type:

float

get_unrealised_profit_usd()[source]#

Get the profit of currently open positions.

Return type:

float

get_closed_profit_usd()[source]#

Get the value of the portfolio based on the latest pricing.

Return type:

float

find_position_for_trade(trade)[source]#

Find a position tha trade belongs for.

Return type:

Optional[TradingPosition]

get_reserve_position(asset)[source]#

Get reserves for a certain reserve asset.

Raises:

KeyError – If we do not have reserves for the asset

Parameters:

asset (AssetIdentifier) –

Return type:

ReservePosition

get_default_reserve_position()[source]#

Get the default reserve position.

Assume portfolio has only one reserve asset.

Raises:

AssertionError – If there is not exactly one reserve position

Return type:

ReservePosition

get_reserve_assets()[source]#

Get reserves assets.

Reserve assets are registered with the state when it is initialised.

Returns:

If the state is not properly initialised, the reserve asset list is empty.

Return type:

List[AssetIdentifier]

get_equity_for_pair(pair)[source]#

Return how much equity allocation we have in a certain trading pair.

Parameters:

pair (TradingPairIdentifier) –

Return type:

Decimal

adjust_reserves(asset, amount)[source]#

Remove currency from reserved.

For internal accounting of the portfolio state.

Parameters:
  • asset (AssetIdentifier) – Reserve asset

  • amount (Decimal) – Negative to reduce portfolio reserves, positive to increase

move_capital_from_reserves_to_trade(trade, underflow_check=True)[source]#

Allocate capital from reserves to trade instance.

Total equity of the porfolio stays the same.

Parameters:

trade (TradeExecution) –

return_capital_to_reserves(trade, underflow_check=True)[source]#

Return capital to reserves after a sell.

Parameters:

trade (TradeExecution) –

has_unexecuted_trades()[source]#

Do we have any trades that have capital allocated, but not executed yet.

Return type:

bool

update_reserves(new_reserves)[source]#

Update current reserves.

Overrides current amounts of reserves.

E.g. in the case users have deposited more capital.

Parameters:

new_reserves (List[ReservePosition]) –

check_for_nonce_reuse(nonce)[source]#

A helper assert to see we are not generating invalid transactions somewhere.

Raise:

AssertionError

Parameters:

nonce (int) –

revalue_positions(ts, valuation_method, revalue_frozen=True)[source]#

Revalue all open positions in the portfolio.

Reserves are not revalued.

Parameters:
  • revalue_frozen – Revalue frozen positions as well

  • ts (datetime) –

  • valuation_method (Callable) –

get_default_reserve_asset()[source]#

Gets the default reserve currency associated with this state.

For strategies that use only one reserve currency. This is the first in the reserve currency list.

Returns:

Tuple (Reserve currency asset, its latest US dollar exchanage rate)

Return (None, 1.0) if the strategy has not seen any deposits yet.

Return type:

Tuple[Optional[AssetIdentifier], Optional[float]]

get_all_trades()[source]#

Iterate through all trades: completed, failed and in progress

Return type:

Iterable[TradeExecution]

get_first_and_last_executed_trade()[source]#

Get first and last trades overall.

Return type:

Tuple[Optional[TradeExecution], Optional[TradeExecution]]

get_trading_history_duration()[source]#

How long this portfolio has trading history.

Calculated as the difference between first and last executed trade.

Returns:

None if there has been any trades.

Zero seconds period if there is only a single trade.

Return type:

Optional[timedelta]

get_initial_deposit()[source]#

How much we invested at the beginning of a backtest.

  • Assumes we track the performance against the US dollar

  • Assume there has been only one deposit event

  • This deposit happened at the start of the backtest

TODO: Shoud not be used, as we have new SyncModel instance for backtesters. Code will be removed.

Return type:

Optional[float]

get_all_traded_pairs()[source]#

Get all pairs for which we have or had positions.

Return type:

Iterable[TradingPairIdentifier]

initialise_reserves(asset)[source]#

Create the initial reserve currency list.

Currently we assume there can be only one reserve currency.

Parameters:

asset (AssetIdentifier) –

get_single_pair()[source]#

Return the only trading pair a single pair strategy has been trading.

Raises:

NotSinglePair

This may happen when

  • Strategy has not traded yet

  • Strategy has traded multiple pairs

Return type:

TradingPairIdentifier

correct_open_position_balance(position, expected_amount, actual_amount, strategy_cycle_ts, block_number, balance_update_id)[source]#

Create an accounting entry trade that correct the balance.

Parameters:
Return type:

TradeExecution

__init__(next_position_id=1, next_trade_id=1, next_balance_update_id=1, open_positions=<factory>, reserves=<factory>, closed_positions=<factory>, frozen_positions=<factory>)#
Parameters:
Return type:

None