Portfolio#

API documentation for 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>, revalue_failures_as_zero=False, pending_positions=<factory>, expired_positions=<factory>)#
Parameters:
Return type:

None

Methods

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

adjust_reserves(asset, amount[, reason])

Add or remove assets from the cash reserves.

allocate_balance_update_id()

Get a new balance update event id.

check_for_nonce_reuse(nonce)

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

close_position(position, executed_at)

Move a position from open positions to closed ones.

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[, pending])

Find a position that a trade belongs for.

from_dict(kvs, *[, infer_missing])

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

get_all_loan_nav([include_interest, ...])

Get net asset value we can theoretically free from leveraged positions.

get_all_positions([pending])

Get open, closed and frozen, positions.

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_borrowed()

get_cash()

Get how much reserve stablecoins we have.

get_closed_positions_for_pair(pair[, ...])

Get closed position for a trading pair.

get_closed_profit_usd()

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

get_current_cash()

Alias for get_cash()

get_current_credit_positions()

Return currently open credit positions.

get_current_interest_positions()

Get lis of all positions for which we need to sync the on-chain interest

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_cash()

How much we invested at the beginning of a backtest.

get_initial_deposit()

Deprecated.

get_leverage_positions()

Return currently open credit positions.

get_live_position_equity()

Get the value of current trading positions plus unexecuted trades.

get_loan_net_asset_value()

What is our Net Asset Value (NAV) across all open loan positions.

get_net_asset_value([include_interest])

Calculate portfolio value if every position would be closed now.

get_open_and_frozen_positions()

Get open and frozen, positions.

get_open_loans()

Get loans across all 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_pending_position_for_pair(pair)

Get pending position for a trading pair.

get_position_by_id(position_id)

Get any position open/closed/frozen by id.

get_position_by_trading_pair(pair[, pending])

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

get_position_equity_and_loan_nav([...])

Get the equity tied tot the current trading positions.

get_positions_closed_at(ts)

Get positions that were closed at a specific timestamp.

get_realised_profit_in_leveraged_positions()

Get the profit of currently open margiend positions.

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_claimed_interest()

Get the total interest claimed from the positions.

get_total_equity()

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

get_total_repaid_interest()

Get the total interest repaid from the positions.

get_trade_by_id(trade_id)

Look up any trade in all positions.

get_trade_by_tx_hash(tx_hash)

Find a trade that contains a particular transaction.

get_trading_history_duration()

How long this portfolio has trading history.

get_unfrozen_positions()

Get positions that have been repaired.

get_unrealised_profit_in_leveraged_positions()

Get the profit of currently open margiend positions.

get_unrealised_profit_usd()

Get the profit of currently open positions.

has_trading_capital([threshold_usd])

Does this strategy have non-zero deposits and total equity?

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_spot_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 spot sell or collateral returned.

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.

revalue_failures_as_zero

Mark positions that we cannot value as zero

open_positions

Currently open trading positions

reserves

Currently held reserve assets

closed_positions

Trades completed in the past

frozen_positions

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

pending_positions

Positions which have not been opened yet, but are waiting the trade order trigger to happen.

expired_positions

Positions from pending_positions that never triggered

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 -> reserve position mapping.

For migration code, see ReservePosition.

Set by initialise_reserves().

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

revalue_failures_as_zero: bool = False#

Mark positions that we cannot value as zero

This is a backtesting issue workaround flag for disappearing markets. E.g. MKR-USDC liquidity disappears here https://tradingstrategy.ai/trading-view/ethereum/uniswap-v3/mkr-usdc-fee-5#7d

TODO: Not supported yet.

pending_positions: Dict[int, TradingPosition]#

Positions which have not been opened yet, but are waiting the trade order trigger to happen.

  • When a trigger happens, the

Will be pruned when these order expire.

expired_positions: Dict[int, TradingPosition]#

Positions from pending_positions that never triggered

Stored for diagnostics.

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.

Returns:

Found trade or

Parameters:

trade_id (int) –

Return type:

Optional[TradeExecution]

get_trade_by_tx_hash(tx_hash)[source]#

Find a trade that contains a particular transaction.

Parameters:

tx_hash (str) – Ethereum transaction hash

Returns:

None if the portfolio does not contain such a trade

Return type:

tradeexecutor.state.trade.TradeExecution | None

get_all_positions(pending=False)[source]#

Get open, closed and frozen, positions.

Parameters:

pending – Include hypotethical market limit positions.

Return type:

Iterable[TradingPosition]

get_open_loans()[source]#

Get loans across all positions.

Return type:

Iterable[Loan]

get_open_and_frozen_positions()[source]#

Get open and frozen, positions.

These are all the positions where we have capital tied at the moment.

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_pending_position_for_pair(pair)[source]#

Get pending position for a trading pair.

  • Used to check if we already have market limit ready for a pair

Parameters:

pair (TradingPairIdentifier) –

Return type:

Optional[TradingPosition]

get_closed_positions_for_pair(pair, include_test_position=False)[source]#

Get closed position for a trading pair.

Parameters:
Return type:

list[tradeexecutor.state.position.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, pending=False)[source]#

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

  • Get the first open position for a trading pair

  • Optioonally check

  • Frozen positions not included

See also

Parameters:
  • pending – Check also pending positions that wait market limit open and are not yet triggered

  • 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, leverage=None, closing=False, planned_collateral_consumption=None, planned_collateral_allocation=None, flags=None, pending=False)[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.

  • pending

    Do not generate a new open position.

    Used when adding take profit triggers to market limit position.

  • pair (TradingPairIdentifier) –

  • reserve_currency_price (float) –

  • leverage (Optional[float]) –

  • closing (Optional[bool]) –

  • planned_collateral_consumption (Optional[Decimal]) –

  • planned_collateral_allocation (Optional[Decimal]) –

  • flags (Optional[Set[TradeFlag]]) –

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_cash()[source]#

Get how much reserve stablecoins we have.

Return type:

float

get_current_cash()[source]#

Alias for get_cash()

TODO: Deprecate

get_position_equity_and_loan_nav(include_interest=True)[source]#

Get the equity tied tot the current trading positions.

TODO: Rename this function - also deals with loans not just equity

  • Includes open positions

  • Does not include frozen positions

Return type:

float

get_all_loan_nav(include_interest=True, include_trading_fees=True)[source]#

Get net asset value we can theoretically free from leveraged positions.

Parameters:
  • include_interest – Include accumulated interest in the calculations

  • include_trading_fees – Include trading fees in the calculations

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 includes

  • Equity Value of the positions

  • …and cash in the hand

But not

  • Leverage/loan based positions (equity is in collateral)

See also get_theoretical_value()

Return type:

float

get_net_asset_value(include_interest=True)[source]#

Calculate portfolio value if every position would be closed now.

This includes

  • Cash

  • Equity hold in spot positions

  • Net asset value hold in leveraged positions

TODO: Net asset value calculation does not account for fees paid to close a short position.

Return type:

float

get_unrealised_profit_usd()[source]#

Get the profit of currently open positions.

  • This profit includes spot market equity i.e. holding tokens

See also get_unrealised_profit_in_leveraged_positions().

Return type:

float

get_unrealised_profit_in_leveraged_positions()[source]#

Get the profit of currently open margiend positions.

  • This profit is not included in the portfolio total equity

See also get_unrealised_profit_usd().

Return type:

float

get_realised_profit_in_leveraged_positions()[source]#

Get the profit of currently open margiend positions.

  • This profit is not included in the portfolio total equity

See also get_unrealised_profit_usd().

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, pending=False)[source]#

Find a position that a trade belongs for.

Parameters:

pending – Include pending positions (not yet trading for market limit)

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

close_position(position, executed_at)[source]#

Move a position from open positions to closed ones.

See also TradingPosition.can_be_closed().

Parameters:
  • position (TradingPosition) – Trading position where the trades and balance updates quantity equals to zero

  • executed_at (datetime) – Wall clock time

adjust_reserves(asset, amount, reason=None)[source]#

Add or remove assets from the cash reserves.

For internal accounting of the portfolio state.

Parameters:
  • asset (AssetIdentifier) – Reserve asset

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

  • reason (str) –

:param reason

Human-readable loggable reason why this happened

move_capital_from_reserves_to_spot_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 spot sell or collateral returned.

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) –

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.

See also

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]#

Deprecated.

See get_initial_cash()

Return type:

Optional[float]

get_initial_cash()[source]#

How much we invested at the beginning of a backtest.

Note

Only applicable to the backtest. Will fail for live strategies.

  • 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

allocate_balance_update_id()[source]#

Get a new balance update event id.

Return type:

int

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

get_current_credit_positions()[source]#

Return currently open credit positions.

Return type:

List[TradingPosition]

get_leverage_positions()[source]#

Return currently open credit positions.

Return type:

List[TradingPosition]

get_current_interest_positions()[source]#

Get lis of all positions for which we need to sync the on-chain interest

Return type:

List[TradingPosition]

get_loan_net_asset_value()[source]#

What is our Net Asset Value (NAV) across all open loan positions.

Return type:

float

has_trading_capital(threshold_usd=0.15)[source]#

Does this strategy have non-zero deposits and total equity?

Check the reserves.

  • If we have zero deposits, do not attempt to trade

  • The actual amount is a bit above zero to account for rounding errors

Returns:

If we have any capital to trade

Return type:

bool

get_total_claimed_interest()[source]#

Get the total interest claimed from the positions.

Return type:

float

get_total_repaid_interest()[source]#

Get the total interest repaid from the positions.

Return type:

float

__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>, revalue_failures_as_zero=False, pending_positions=<factory>, expired_positions=<factory>)#
Parameters:
Return type:

None