tradingenv.broker package

Submodules

tradingenv.broker.allocation module

Provides an extented python dictionary with handy methods when the dict is mapping contracts to quantities (either nr of contracts of portfolio weight).

class tradingenv.broker.allocation.NrContracts(mapping: Dict[AbstractContract, float] = None, keys: Sequence[AbstractContract] = None, values: Sequence[float] = None)[source]

Bases: _Allocation

A dictionary mapping contracts to number of contracts.

To initialize allocation you need to pass either 'mapping' OR 'keys' and 'values'.

Parameters:
  • mapping -- A dictionary mapping instances of AbstractContract to a float representing the target allocation expressed in a whatever unit of measurement (e.g. weight or number of contracts).

  • keys -- A sequence of AbstractContract instances, representing the keys of the dictionary to be constructed.

  • values -- A sequence of floats, representing the values of the dictionary to be constructed.

class tradingenv.broker.allocation.Weights(mapping: Dict[AbstractContract, float] = None, keys: Sequence[AbstractContract] = None, values: Sequence[float] = None)[source]

Bases: _Allocation

A dictionary mapping contracts to portfolio weight.

To initialize allocation you need to pass either 'mapping' OR 'keys' and 'values'.

Parameters:
  • mapping -- A dictionary mapping instances of AbstractContract to a float representing the target allocation expressed in a whatever unit of measurement (e.g. weight or number of contracts).

  • keys -- A sequence of AbstractContract instances, representing the keys of the dictionary to be constructed.

  • values -- A sequence of floats, representing the values of the dictionary to be constructed.

tradingenv.broker.broker module

This module provides the core logic to perform portfolio _rebalancing. Holdings (cash and contracts) are held in a Brokerage account and historical _rebalancing are keep in TrackRecord.

class tradingenv.broker.broker.Broker(exchange: ~tradingenv.exchange.Exchange, base_currency: ~tradingenv.contracts.Cash = Cash(USD), deposit: float = 100.0, fees: ~tradingenv.broker.fees.IBrokerFees = <tradingenv.broker.fees.BrokerFees object>, epsilon: float = 1e-07)[source]

Bases: object

This class allows to simulate a sequence of _rebalancing requests.

_holdings_margins

A dictionary mapping contracts to margins deposited at the clearing house, e.g. when buying derivatives products such as futures.

Type:

Dict[AbstractContract, float]

_holdings_quantity

A dictionary mapping contracts to number of contracts held now.

Type:

Dict[AbstractContract, float]

track_record

Keeps track of historical _rebalancing request.

Type:

TrackRecord

Parameters:
  • exchange (Exchange) -- An exchanged used to retrieve market prices and conditions to execute _rebalancing requests and trade accordingly.

  • base_currency (Cash) -- Base currency.

  • deposit (float) -- Initial deposit, 100 units of base currency by default.

accrued_interest(now: datetime, accrue: bool = False) float[source]
Parameters:
  • now (datetime) -- Current time, used to calculate how long it has passed since the last time that interest rates were deposited or charged.

  • accrue (bool) -- If True, the interest rate will be accrued, i.e. charged if negative or deposited if positive. The interest rate applied is determined by Broker.fees.interest_rate.

Notes

No interest rate paid on margins. This is a realistic conservative assumption (e.g. Interactive Brokers does not pay interest rates on margins and financial instrument cannot be deposited as _margin).

Returns:

  • Amount of interest rate owed (either positive or negative) since

  • when if was last deposited/charged from the account's holdings.

context() Context[source]
property holdings_margins: Dict[AbstractContract, float]
property holdings_quantity: Dict[AbstractContract, float]
holdings_values(kind: str = 'notional') Dict[AbstractContract, float][source]

Returns np.array with state of all positions within the portfolio (cash included) in base currency. The array has as many entries as traded contracts. Notional values, so transaction_price * multiplier.

holdings_weights() Dict[AbstractContract, float][source]

Relative weights of the notional traded value over NLV. Returns current weights of the portfolio as positions values (including market spread) divided by the net liquidation state of the account (including market spread).

marking_to_market(contract: AbstractContract | Sequence[AbstractContract] = None)[source]
Parameters:

contract (Union[AbstractContract, Sequence[AbstractContract]]) -- An optional contract or sequence of contracts to for which marking-to-market has to be performed. If blank, all contracts will be assumed to be updated.

References

https://www.cmegroup.com/education/courses/introduction-to-futures/mark-to-market.html https://www.cmegroup.com/education/courses/introduction-to-futures/margin-know-what-is-needed.html

net_liquidation_value(raise_if_broke: bool = True) float[source]
Parameters:

raise_if_broke (bool) -- An EndOfEpisodeError will be raised if the notional liquidation value of the account is non-positive.

Returns:

  • The net liquidation value of the account pricing long (short) position

  • using bid (ask) prices.

rebalance(rebalancing: Rebalancing)[source]

Given a _rebalancing object, execute all trades needed to perform the _rebalancing.

Developer notes

The _rebalancing logic is slit between Rebalancing and Broker. Rebalancing is responsible for calculating (but not executing) the trading instructions (i.e. a list of trades), wheres Broker is responsible for anything else (i.e. pre-processing, execution and post-processing). This design allows to optionally compute the trades for a variety of _rebalancing objects and see what the trades would be, without performing any _rebalancing. Also, the separation of concerns between calculating the trades and execution the trades makes it easier to test the code.

transact(trade: Trade)[source]
Parameters:

trade (Trade) -- A trade instance to be executed.

Notes

Transacting in long lasting positions might lead to numerical errors in the order of ~1e-15. It's a design choice not to deal with such errors because for prices --> +inf, _holdings_quantity --> 0 < 1e-15. In other words, we don't deal with such numerical errors to avoid unexpected side effects when asset prices are relatively too high. https://floating-point-gui.de/basic/

class tradingenv.broker.broker.Context(nlv, weights, values, nr_contracts, margins)[source]

Bases: object

exception tradingenv.broker.broker.EndOfEpisodeError[source]

Bases: Exception

Raised when the net liquidation state of the account goes below zero. This exception will trigger an end of episode.

tradingenv.broker.fees module

class tradingenv.broker.fees.BrokerFees(markup: float = 0.0, interest_rate: ~tradingenv.contracts.Rate = Rate(FED funds rate), proportional: float = 0.0, fixed: float = 0.0)[source]

Bases: IBrokerFees

No broker fees of any sort will be applied when using this class.

Parameters:
  • markup (float) -- A non-negative CAGR, zero by default. This mark-up is the profit of the broker when applying the interest rate to idle cash and loans. For example, if interest_rate is 0.03 (3%) and fees=0.01 (1%), CAGR on idle cash will be 2% and cost of loans will be 4%.

  • interest_rate (Rate) -- This interest rate is used to: (1) generate a positive stream of cash flows on idle cash (e.g. when the portfolio is not fully invested) and (2) generate a negative stream of cash flows on loans (e.g. borrowed cash to pay in a leveraged portfolio of ETFs). By default, this is Rate('FED funds rate') and it's expressed in terms of compounded annual growth rate (CAGR).

  • proportional (float) -- Broker fees to be applied to the notional traded value of the security. E.g. if 0.01 (1%) mean that 1 USD is paid for every 100 USD traded.

  • fixed (float) -- Fixed broker fees to be applied to each trade, regardless the notional traded value of trade.

commissions(trade: Trade) float[source]

Zero fees.

class tradingenv.broker.fees.IBrokerFees(markup: float = 0.0, interest_rate: ~tradingenv.contracts.Rate = Rate(FED funds rate), proportional: float = 0.0, fixed: float = 0.0)[source]

Bases: ABC

Parameters:
  • markup (float) -- A non-negative CAGR, zero by default. This mark-up is the profit of the broker when applying the interest rate to idle cash and loans. For example, if interest_rate is 0.03 (3%) and fees=0.01 (1%), CAGR on idle cash will be 2% and cost of loans will be 4%.

  • interest_rate (Rate) -- This interest rate is used to: (1) generate a positive stream of cash flows on idle cash (e.g. when the portfolio is not fully invested) and (2) generate a negative stream of cash flows on loans (e.g. borrowed cash to pay in a leveraged portfolio of ETFs). By default, this is Rate('FED funds rate') and it's expressed in terms of compounded annual growth rate (CAGR).

  • proportional (float) -- Broker fees to be applied to the notional traded value of the security. E.g. if 0.01 (1%) mean that 1 USD is paid for every 100 USD traded.

  • fixed (float) -- Fixed broker fees to be applied to each trade, regardless the notional traded value of trade.

abstractmethod commissions(trade: Trade) float[source]

Returns total broker fees.

class tradingenv.broker.fees.InteractiveBrokersFees(markup: float = 0.0, interest_rate: ~tradingenv.contracts.Rate = Rate(FED funds rate), proportional: float = 0.0, fixed: float = 0.0)[source]

Bases: IBrokerFees

Replicate Interactive Broker commisions.

References

https://www.interactivebrokers.com/en/index.php?f=1590

Parameters:
  • markup (float) -- A non-negative CAGR, zero by default. This mark-up is the profit of the broker when applying the interest rate to idle cash and loans. For example, if interest_rate is 0.03 (3%) and fees=0.01 (1%), CAGR on idle cash will be 2% and cost of loans will be 4%.

  • interest_rate (Rate) -- This interest rate is used to: (1) generate a positive stream of cash flows on idle cash (e.g. when the portfolio is not fully invested) and (2) generate a negative stream of cash flows on loans (e.g. borrowed cash to pay in a leveraged portfolio of ETFs). By default, this is Rate('FED funds rate') and it's expressed in terms of compounded annual growth rate (CAGR).

  • proportional (float) -- Broker fees to be applied to the notional traded value of the security. E.g. if 0.01 (1%) mean that 1 USD is paid for every 100 USD traded.

  • fixed (float) -- Fixed broker fees to be applied to each trade, regardless the notional traded value of trade.

commissions(trade: Trade) float[source]

Returns total broker fees.

tradingenv.broker.rebalancing module

class tradingenv.broker.rebalancing.Rebalancing(contracts: Sequence[AbstractContract] = None, allocation: Sequence[float] = None, measure: str = 'weight', absolute: bool = True, fractional: bool = True, margin: float = 0.0, time: datetime = None)[source]

Bases: object

Private class returned by PortfolioSpace._make_rebalancing_request and passed to RebalancingResponse during its initialization.

Parameters:
  • contracts -- A sequence of contracts. The i-th contract is associated with the i-th element in 'allocation'.

  • allocation -- A sequence of target portfolio allocations of the _rebalancing. The i-th allocation is associated with the i-th element in 'contracts'. The representation of the values in the sequence depends on the parameter 'kind'.

  • measure -- The unit of measurement of the parameter 'allocation'. If: - 'weight': a 0.2 would mean 20% of the net liquidation value of the current portfolio. - 'nr-contracts': a 21 would mean 21 contracts.

  • absolute -- If True (default), 'allocation' is assumed to represents the desired target allocation. For example, if allocation={ETF(SPY): 0.03} and measure='weight', then the desired _rebalancing will result in an allocation in ETF(SPY) corresponding to 3% of the net liquidation value and the remaining 97% in cash. If False, 'allocation' is assumed to represent the desired change from the current allocation. or example, if allocation={ETF(SPY): 0.03} and measure='weight', then the desired _rebalancing will result in the previously held portfolio with 3% less of cash and 3% more of ETF(SPY).

  • fractional -- True by default. If false, decimals will be ignored from every element of 'allocation'.

  • margin -- A non-negative float. Rebalancing of positions which are not at least different by '_margin' in absolute value, will not be executed. Setting a small positive value (e.g. 0.02==2%) might be a good practice to reduce transaction costs. Skip trade if the absolute weight imbalance is below the margin AND the target weight is different from zero. The latter condition allows to liquidate a position in the situation where we hold in the portfolio a contract with a tiny weight below the margin.

  • time -- Time of the _rebalancing request. Current time by default.

Notes

Target weights are applied to the net liquidation value of the account before paying transaction costs to make the problem more trackable). Therefore, in presence transaction costs, the amount of cash in the broker account will be -x where x are the broker commissions paid.

make_trades(broker: Broker) List[Trade][source]
Parameters:

broker -- A broker instance that will be used e.g. to calculate the offset between target _rebalancing and current holdings.

Returns:

  • A list of trade objects, recommended to perform the portfolio

  • _rebalancing.

tradingenv.broker.track_record module

TrackRecord stores historical _rebalancing execution performed by Broker.

class tradingenv.broker.track_record.TrackRecord[source]

Bases: object

Stores historical _rebalancing requests and _rebalancing.

cost_of_commissions()[source]
cost_of_spread()[source]
fig_annual_returns() <Mock name='mock.graph_objs.Figure' id='136273437610528'>[source]
fig_capital_asset_pricing_model() <Mock name='mock.graph_objs.Figure' id='136273437610528'>[source]
fig_drawdown() <Mock name='mock.graph_objs.Figure' id='136273437610528'>[source]
fig_historical_portfolio_weights_actual(group_futures: bool = True, post_rebalancing: bool = True) <Mock name='mock.graph_objs.Figure' id='136273437610528'>[source]
fig_historical_portfolio_weights_diff(group_futures: bool = True, post_rebalancing: bool = True) <Mock name='mock.graph_objs.Figure' id='136273437610528'>[source]
fig_historical_portfolio_weights_target(group_futures: bool = True)[source]
fig_net_liquidation_value() <Mock name='mock.graph_objs.Figure' id='136273437610528'>[source]
fig_returns_distribution()[source]
fig_transaction_costs() <Mock name='mock.graph_objs.Figure' id='136273437610528'>[source]
classmethod load(path: str) TrackRecord[source]
net_liquidation_value(before_rebalancing=True, burn: bool = False, name: str = 'Net liquidation value', index_name: str = 'Date') <Mock name='mock.DataFrame' id='136273440799872'>[source]
Parameters:
  • before_rebalancing

  • burn (bool) -- Default is False (least surprise principle). If True, initial checkpoints with no trades (i.e. when the portfolio is cash only) will be discarded when calling TrackRecord.to_frame, unless a burn argument is explicitly passed to TrackRecord.to_frame.

  • name

  • index_name

save(directory: str, name: str = None)[source]
tearsheet()[source]
to_excel(path)[source]

Export track record to excel.

transaction_costs(burn: bool = False, index_name: str = 'Date', cumulative: bool = True) <Mock name='mock.DataFrame' id='136273440799872'>[source]
weights_actual(before_rebalancing=True, burn: bool = False, name: str = 'Portfolio weights actual', index_name: str = 'Date', aggregate_future_chain: bool = False) <Mock name='mock.DataFrame' id='136273440799872'>[source]
weights_target(burn: bool = False, name: str = 'Portfolio weights target', index_name: str = 'Date', aggregate_future_chain: bool = False) <Mock name='mock.DataFrame' id='136273440799872'>[source]
class tradingenv.broker.track_record.TrackRecordComparison(track_records: Sequence[TrackRecord])[source]

Bases: object

fig_net_liquidation_value(logy: bool = False) <Mock name='mock.graph_objs.Figure' id='136273437610528'>[source]

tradingenv.broker.trade module

Execution details of a single trade.

class tradingenv.broker.trade.Trade(time: ~datetime.datetime, contract: ~tradingenv.contracts.AbstractContract, quantity: float, bid_price: float, ask_price: float, broker_fees: ~tradingenv.broker.fees.IBrokerFees = <tradingenv.broker.fees.BrokerFees object>)[source]

Bases: object

Execution details of a single trade. This class is instanced by RebalancingResponse.

acq_price

Average acquisition or liquidation price across all lots to execute the trade. By default, there is no market impact nor slippage. Therefore, avg_price corresponds to the bid price (ask price) in case of buy (sell).

Type:

float

cost_of_cash

Cash to be paid (earned) upfront to buy (sell) the contract.

Type:

float

cost_of_commissions

Broker commissions paid when transacting.

Type:

float

Parameters:
  • time (datetime) -- Execution time of the trade.

  • contract (AbstractContract) -- Traded contract.

  • quantity (float) -- Traded quantity. A negative number denotes a sell, liquidation or short-selling.

  • bid_price (float) -- Bid price in the limit order book of the contract. Bid price must be non-greater then the ask price.

  • ask_price (float) -- Ask price in the limit order book of the contract. Ask price must be non.smaller than the bid price.

  • broker_fees (IBrokerFees) -- An concrete implementation of AbstractBrokerFees, responsible to calculate the total broker fees to be paid for the trade.

acq_price
ask_price
bid_price
contract
cost_of_cash
cost_of_commissions
cost_of_spread
notional
quantity
time

Module contents