TradeExecution#

tradeexecutor.state.trade.TradeExecution Python class in Trading Strategy framework.

class TradeExecution[source]#

Bases: object

Trade execution tracker.

  • One TradeExecution instance can only represent one swap

Each trade has a reserve currency that we use to trade the token (usually USDC).

Each trade can be

  • Buy: swap quote token -> base token

  • Sell: swap base token -> quote token

When doing a buy planned_reserve (fiat) is the input. This yields to executed_quantity of tokens that may be different from planned_quantity.

When doing a sell planned_quantity (token) is the input. This yields to executed_reserve of fiat that might be different from `planned_reserve.

Trade execution has four states

  • Planning: The execution object is prepared

  • Capital allocation and transaction creation: We move reserve from out portfolio to the trade in internal accounting

  • Transaction broadcast: trade cannot be cancelled in this point

  • Resolving the trade: We check the Ethereum transaction receipt to see how well we succeeded in the trade

There trade state is resolved based on the market variables (usually timestamps).

__init__(trade_id, position_id, trade_type, pair, opened_at, planned_quantity, planned_reserve, planned_price, reserve_currency, reserve_currency_exchange_rate=None, planned_mid_price=None, planned_max_slippage=None, started_at=None, reserve_currency_allocated=None, broadcasted_at=None, executed_at=None, failed_at=None, executed_price=None, executed_quantity=None, executed_reserve=None, slippage_tolerance=None, fee_tier=<property object>, lp_fees_paid=None, lp_fees_estimated=<property object>, lp_fee_exchange_rate=None, native_token_price=None, retry_of=None, blockchain_transactions=<factory>, notes=None, repaired_at=None, repaired_trade_id=None, price_structure=None)#
Parameters:
Return type:

None

Methods

__init__(trade_id, position_id, trade_type, ...)

calculate_asset_deltas()

Get the expected amount fo token balance change in a wallet for this trade.

from_dict(kvs, *[, infer_missing])

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

get_allocated_value()

get_credit_debit()

Returns the token quantity and reserve currency quantity for this trade.

get_decision_lag()

How long it took between strategy decision cycle starting and the strategy to make a decision.

get_equity_for_position()

Get the planned or executed quantity of the base token.

get_equity_for_reserve()

Get the planned or executed quantity of the quote token.

get_executed_value()

Estimate the USD value of this trade.

get_execution_lag()

How long it took between strategy decision cycle starting and the trade executed.

get_execution_sort_position()

When this trade should be executed.

get_failed_transaction()

Get the transaction that failed this trade.

get_fees_paid()

Get total swap fees paid for trade.

get_full_debug_dump_str()

get_human_description()

User friendly description for this trade

get_input_asset()

How we fund this trade.

get_output_asset()

What asset we receive out from this trade

get_planned_max_gas_price()

Get the maximum gas fee set to all transactions in this trade.

get_planned_reserve()

get_planned_value()

get_position_quantity()

Get the planned or executed quantity of the base token.

get_raw_planned_quantity()

Return the amount of USD token for the buy as raw token units.

get_raw_planned_reserve()

Return the amount of USD token for the buy as raw token units.

get_reserve_currency_exchange_rate()

What was the reserve stablecoin exchange trade for this trade.

get_reserve_quantity()

Get the planned or executed quantity of the quote token.

get_revert_reason()

Get the transaction failure reason.

get_status()

Resolve the trade status.

get_value()

Get estimated or realised value of this trade.

is_accounted_for_equity()

Does this trade contribute towards the trading position equity.

is_accounting_correction()

This trade is an accounting correction.

is_buy()

is_executed()

Did this trade ever execute.

is_failed()

This trade was succcessfully completed.

is_pending()

This trade was succcessfully completed.

is_planned()

This trade is still in planning, unallocated.

is_rebalance()

This trade is part of the normal strategy rebalance.

is_repair_needed()

This trade needs repair, but is not repaired yet.

is_repair_trade()

This trade is fixes a frozen position and counters another trade.

is_repaired()

The automatic execution failed and this was later repaired.

is_sell()

is_started()

This trade has a txid allocated.

is_stop_loss()

This trade is made to close stop loss on a position.

is_success()

This trade was succcessfully completed.

is_take_profit()

This trade is made to close take profit on a position.

is_triggered()

Was this trade based on a trigger signal.

is_unfinished()

We could not confirm this trade back from the blockchain after broadcasting.

mark_broadcasted(broadcasted_at)

mark_failed(failed_at)

mark_success(executed_at, executed_price, ...)

Mark trade success.

pretty_print()

Get diagnostics output for the trade.

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

set_blockchain_transactions(txs)

Set the physical transactions needed to perform this trade.

to_dict([encode_json])

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

Attributes

broadcasted_at

When this trade entered mempool

executed_at

Timestamp of the block where the txid was first mined.

executed_price

What was the actual price we received

executed_quantity

How much underlying token we traded, the actual realised amount.

executed_reserve

How much reserves we spend for this traded, the actual realised amount.

failed_at

The trade did not go through.

fee_tier

LP fee % recorded before the execution starts.

lp_fee_exchange_rate

What is the conversation rate between quote token and US dollar used in LP fee conversion.

lp_fees_estimated

LP fees estimated in the USD

lp_fees_paid

LP fees paid, currency convereted to the USD.

native_token_price

USD price per blockchain native currency unit, at the time of execution

notes

Human readable notes about this trade

planned_max_slippage

How much slippage we could initially tolerate, 0.01 is 1% slippage.

planned_mid_price

What we thought was the mid-price when we made the decision to tale this trade

price_structure

Related TradePricing instance

repaired_at

Trade was manually repaired

repaired_trade_id

Which is the trade that this trade is repairing.

reserve_currency_allocated

How much reserves was moved on this trade before execution

reserve_currency_exchange_rate

What is the reserve currency exchange rate used for this trade

retry_of

slippage_tolerance

Slippage tolerance for this trade.

started_at

When this trade was decided

strategy_cycle_at

Alias for oepned_at

trade_id

Trade id is unique among all trades in the same portfolio

position_id

Position id is unique among all trades in the same portfolio

trade_type

Spot, margin, lending, etc.

pair

Which trading pair this trade was for

opened_at

What was the strategy cycle timestamp for it was created.

planned_quantity

Positive for buy, negative for sell.

planned_reserve

How many reserve tokens (USD) we use in this trade

planned_price

What we thought the execution price for this trade would have been at the moment of strategy decision.

reserve_currency

Which reserve currency we are going to take.

blockchain_transactions

Associated blockchain transaction details.

trade_id: int#

Trade id is unique among all trades in the same portfolio

position_id: int#

Position id is unique among all trades in the same portfolio

trade_type: TradeType#

Spot, margin, lending, etc.

pair: TradingPairIdentifier#

Which trading pair this trade was for

opened_at: datetime.datetime | None#

What was the strategy cycle timestamp for it was created.

Naive UTC timestamp.

If the trade was executed by a take profit/stop loss trigger then this is the trigger timestamp (not wall clock time)

Not available if the trade was done outside the strategy execution (manual trades, accounting corrections).

See also

planned_quantity: Decimal#

Positive for buy, negative for sell.

Always accurately known for sells.

planned_reserve: Decimal#

How many reserve tokens (USD) we use in this trade

  • Always a position number (only the sign of planned_quantity changes between buy/sell)

  • For buys, Always known accurately for buys.

  • For sells, an estimation based on planned_price

Expressed in reserve_currency.

planned_price: float#

What we thought the execution price for this trade would have been at the moment of strategy decision.

This price includes any fees we pay for LPs, and should become executed_price if the execution is perfect.

For the market price see planned_mid_price.

reserve_currency: AssetIdentifier#

Which reserve currency we are going to take. Note that pair.quote might be different from reserve currency. This is because we can do three-way trades like BUSD -> BNB -> Cake when our routing model supports this.

reserve_currency_exchange_rate: Optional[float] = None#

What is the reserve currency exchange rate used for this trade

planned_mid_price: Optional[float] = None#

What we thought was the mid-price when we made the decision to tale this trade

This is the market price of the asset at the time of the trade decision.

planned_max_slippage: Optional[float] = None#

How much slippage we could initially tolerate, 0.01 is 1% slippage.

started_at: Optional[datetime] = None#

When this trade was decided

Wall clock time.

For backtested trades, this is always set to opened_at.

reserve_currency_allocated: Optional[Decimal] = None#

How much reserves was moved on this trade before execution

broadcasted_at: Optional[datetime] = None#

When this trade entered mempool

executed_at: Optional[datetime] = None#

Timestamp of the block where the txid was first mined.

For failed trades, this is not set until repaired, but instead we set failed_at.

failed_at: Optional[datetime] = None#

The trade did not go through. The timestamp when we figured this out.

executed_price: Optional[float] = None#

What was the actual price we received

This may be None for even if closed_at is set. This is because invalid trades (execution failed) may be marked close.

executed_quantity: Optional[Decimal] = None#

How much underlying token we traded, the actual realised amount. Positive for buy, negative for sell

executed_reserve: Optional[Decimal] = None#

How much reserves we spend for this traded, the actual realised amount.

slippage_tolerance: Optional[float] = None#

Slippage tolerance for this trade.

Examples

  • 0: no slippage toleranc eallowed at all

  • 0.01: 1% slippage tolerance

  • 1: MEV bots can steal all your money

We estimate executed_quantity = planned_quantity * slippage_tolerance. If any trade outcome exceeds the slippage tolerance the trade fails.

If you are usinga vault-based trading, slippage tolerance must be always set to calculate the asset delta.

See also calculate_asset_deltas().

lp_fees_paid: Optional[float] = None#

LP fees paid, currency convereted to the USD.

The value is read back from the realised trade. LP fee is usually % of the trade. For Uniswap style exchanges fees are always taken from amount in token and directly passed to the LPs as the part of the swap, these is no separate fee information.

lp_fee_exchange_rate: Optional[float] = None#

What is the conversation rate between quote token and US dollar used in LP fee conversion.

We set this exchange rate before the trade is started. Both lp_fees_estimated and lp_fees_paid need to use the same exchange rate, even though it would not be timestamp accurte.

native_token_price: Optional[float] = None#

USD price per blockchain native currency unit, at the time of execution

Used for converting tx fees and gas units to dollars

blockchain_transactions: List[BlockchainTransaction]#

Associated blockchain transaction details. Each trade contains 1 … n blockchain transactions. Typically this is approve() + swap() for Uniswap v2 or just swap() if we have the prior approval and approve does not need to be done for the hot wallet anymore.

notes: Optional[str] = None#

Human readable notes about this trade

Used to mark test trades from command line. Special case; not worth to display unless the field is filled in.

repaired_at: Optional[datetime] = None#

Trade was manually repaired

E.g. failed broadcast issue was fixed. Marked when the repair command is called.

repaired_trade_id: Optional[datetime] = None#

Which is the trade that this trade is repairing.

This trade makes a opposing trade to the trade referred here, making accounting match again and unfreezing the position.

For the repair trade

  • Strategy cycle is set to the original broken trade

price_structure: Optional[TradePricing] = None#

Related TradePricing instance

TradePricing instance can refer to more than one swap

pretty_print()[source]#

Get diagnostics output for the trade.

Use Python pprint module.

Return type:

str

property strategy_cycle_at#

Alias for oepned_at

property fee_tier: float | None#

LP fee % recorded before the execution starts.

Recorded as multiplier

Not available in the case this is ignored in backtesting or not supported by routers/trading pairs.

Used to calculate lp_fees_estimated.

Sourced from Uniswap v2 router or Uniswap v3 pool information.

property lp_fees_estimated: float#

LP fees estimated in the USD

This is set before the execution and is mostly useful for backtesting.

get_human_description()[source]#

User friendly description for this trade

Return type:

str

get_reserve_currency_exchange_rate()[source]#

What was the reserve stablecoin exchange trade for this trade.

Returns:

1.0 if not set

Return type:

float

is_success()[source]#

This trade was succcessfully completed.

Return type:

bool

is_failed()[source]#

This trade was succcessfully completed.

Return type:

bool

is_pending()[source]#

This trade was succcessfully completed.

Return type:

bool

is_planned()[source]#

This trade is still in planning, unallocated.

Return type:

bool

is_started()[source]#

This trade has a txid allocated.

Return type:

bool

is_rebalance()[source]#

This trade is part of the normal strategy rebalance.

Return type:

bool

is_stop_loss()[source]#

This trade is made to close stop loss on a position.

Return type:

bool

is_take_profit()[source]#

This trade is made to close take profit on a position.

Return type:

bool

is_triggered()[source]#

Was this trade based on a trigger signal.

Return type:

bool

is_accounted_for_equity()[source]#

Does this trade contribute towards the trading position equity.

Failed trades are reverted. Only their fees account.

Return type:

bool

is_unfinished()[source]#

We could not confirm this trade back from the blockchain after broadcasting.

Return type:

bool

is_repaired()[source]#

The automatic execution failed and this was later repaired.

A manual repair command was issued and it manaeged to correctly repair this trade and underlying transactions.

See also is_repair_trade().

Return type:

bool

is_executed()[source]#

Did this trade ever execute.

Return type:

bool

is_repair_needed()[source]#

This trade needs repair, but is not repaired yet.

Return type:

bool

is_repair_trade()[source]#

This trade is fixes a frozen position and counters another trade.

Return type:

bool

is_accounting_correction()[source]#

This trade is an accounting correction.

This is a virtual trade made to have the

Return type:

bool

get_input_asset()[source]#

How we fund this trade.

Return type:

AssetIdentifier

get_output_asset()[source]#

What asset we receive out from this trade

Return type:

AssetIdentifier

get_status()[source]#

Resolve the trade status.

Based on the different state variables set on this item, figure out what is the best status for this trade.

Return type:

TradeStatus

get_executed_value()[source]#

Estimate the USD value of this trade.

Based on the

  • Exact quantity and exact crypto price we got executed at

  • USD exchange rate known at the time of the execution

Return type:

float

get_raw_planned_reserve()[source]#

Return the amount of USD token for the buy as raw token units.

Return type:

int

get_raw_planned_quantity()[source]#

Return the amount of USD token for the buy as raw token units.

Return type:

int

get_position_quantity()[source]#

Get the planned or executed quantity of the base token.

Positive for buy, negative for sell.

Return type:

Decimal

get_reserve_quantity()[source]#

Get the planned or executed quantity of the quote token.

Negative for buy, positive for sell.

Return type:

Decimal

get_equity_for_position()[source]#

Get the planned or executed quantity of the base token.

Positive for buy, negative for sell.

Return type:

Decimal

get_equity_for_reserve()[source]#

Get the planned or executed quantity of the quote token.

Negative for buy, positive for sell.

Return type:

Decimal

get_value()[source]#

Get estimated or realised value of this trade.

Value is always a positive number.

Return type:

float

get_credit_debit()[source]#

Returns the token quantity and reserve currency quantity for this trade.

If buy this is (+trading position quantity/-reserve currency quantity).

If sell this is (-trading position quantity/-reserve currency quantity).

Return type:

Tuple[Decimal, Decimal]

get_fees_paid()[source]#

Get total swap fees paid for trade. Returns 0 instead of None

Returns:

total amount of lp fees (swap fees) paid in US dollars

Return type:

float

get_execution_sort_position()[source]#

When this trade should be executed.

Lower, negative, trades should be executed first.

We need to execute sells first because we need to have cash in hand to execute buys.

Returns:

Sortable int

Return type:

int

get_decision_lag()[source]#

How long it took between strategy decision cycle starting and the strategy to make a decision.

Return type:

timedelta

get_execution_lag()[source]#

How long it took between strategy decision cycle starting and the trade executed.

Return type:

Optional[timedelta]

get_failed_transaction()[source]#

Get the transaction that failed this trade.

Extract the EVM revert reason from the underlying transactions.

  • Each trade can consist of multiple transactions

  • Iterate transactions from the last to first and return the first failure reason

Return type:

Optional[BlockchainTransaction]

get_revert_reason()[source]#

Get the transaction failure reason.

Extract the EVM revert reason from the underlying transactions.

  • Each trade can consist of multiple transactions

  • Iterate transactions from the last to first and return the first failure reason

See also

Returns:

Cleaned revert reason message

Return type:

Optional[str]

__init__(trade_id, position_id, trade_type, pair, opened_at, planned_quantity, planned_reserve, planned_price, reserve_currency, reserve_currency_exchange_rate=None, planned_mid_price=None, planned_max_slippage=None, started_at=None, reserve_currency_allocated=None, broadcasted_at=None, executed_at=None, failed_at=None, executed_price=None, executed_quantity=None, executed_reserve=None, slippage_tolerance=None, fee_tier=<property object>, lp_fees_paid=None, lp_fees_estimated=<property object>, lp_fee_exchange_rate=None, native_token_price=None, retry_of=None, blockchain_transactions=<factory>, notes=None, repaired_at=None, repaired_trade_id=None, price_structure=None)#
Parameters:
Return type:

None

mark_success(executed_at, executed_price, executed_quantity, executed_reserve, lp_fees, native_token_price, force=False)[source]#

Mark trade success.

  • Called by execution engine when we get a confirmation from the blockchain our blockchain txs where good

  • Called by repair to force trades to good state

Parameters:
  • force

    Do not check if we are in the correct previous state (broadcasted).

    Used in the accounting corrections.

  • executed_at (datetime) –

  • executed_price (float) –

  • executed_quantity (Decimal) –

  • executed_reserve (Decimal) –

  • lp_fees (float) –

  • native_token_price (float) –

set_blockchain_transactions(txs)[source]#

Set the physical transactions needed to perform this trade.

Parameters:

txs (List[BlockchainTransaction]) –

get_planned_max_gas_price()[source]#

Get the maximum gas fee set to all transactions in this trade.

Return type:

int

calculate_asset_deltas()[source]#

Get the expected amount fo token balance change in a wallet for this trade.

Needed for vault based trading to work, as they run slippage tolerance checks on expectd inputs and outputs.

Each trade has minimum of two asset deltas

  • The token you spent to buu/sell (input)

  • The token you receive (output)

The output will always have slippage_tolerance applied. Input is passed as is.

Returns:

List of asset deltas [input, output]

Return type:

List[AssetDelta]