tradingenv package

Subpackages

Submodules

tradingenv.contracts module

The user is expected to instance these classes when instancing the action observation_space within TradingEnv. For examples, see spaces.py and env.py.

class tradingenv.contracts.AbstractContract[source]

Bases: ABC

Trading contract must be represented by implementation of this abstract class, then passed to the action observation_space within the trading environment.

now

Current time. This is handy for a subset of contracts such as FutureChain or calculate nr days to expiry for derivatives. This is a class attribute shared across all AbstractContract instances.

Type:

datetime

abstract property cash_requirement: float

A number ranging from 0 to 1 defining what (constant) proportion of the notional value should be paid when buying the contract. This is generally 1 for non-derivatives (e.g. ETFs, Indexes, Stocks) and 0 for derivatives (e.g. Future).

make_events() List[IEvent][source]

Returns a list of events (IEvent instances) to be added the Transmitter when initializing TradingEnv. For example, EventContractDiscontinued associated with futures contracts.

abstract property margin_requirement: float

A number ranging from 0 to 1 defining what (constant) proportion of the notional value should be deposited as initial _margin when buying or selling the contract. Note that by design we are making the simplifying assumption that initial _margin == maintenance _margin. This is generally 1 for non-derivatives (e.g. ETFs, Indexes, Stocks) and > 1 for derivatives (e.g. Future).

abstract property multiplier: float

Contract's multiplier. This is generally 1 for non-derivatives (e.g. ETFs, Indexes, Stocks) and > 1 for derivatives (e.g. Future).

now: datetime = datetime.datetime(1, 1, 1, 0, 0)
size(price: float) float[source]
Parameters:

price (float) -- Value of one unit of the underlying asset.

Return type:

Value of the underlying asset multiplier by the future's multiplier.

static_hashing() AbstractContract[source]

Subclasses of AbstractContract are not guaranteed to have static hashing, as it may vary with time. This method returns an instance of AbstractContract with the same hash of the class instance calling this method, which is guaranteed to be static. See FutureChain for more info as it is an example of AbstractContract with dynamic hashing.

abstract property symbol: str

Unique symbol associated with the concrete implementation. Non-unique symbols might lead to silent bugs as symbols are used to hash AbstractClass instances.

property symbol_short: str

Symbol is guaranteed to be unique across all contracts, and in fact it is used to hash the instance. The short symbol is not guaranteed to be unique. Useful e.g. for futures where the same future across different expiry dates share the same short symbol but not the same symbol.

property underlyings: List[AbstractContract]

Returns list of all subcontracts. This is needed when implementing composite contract such as LeadingContract. For example for FutureChain(ES) this would return the list of ES contracts across all expiry dates. Note that this is not to be confused with the underlying index e.g. Index('S&P 500') for eMini S&P 500.

verify(mid_price: float)[source]
class tradingenv.contracts.Asset(symbol: str)[source]

Bases: AbstractContract

An index (e.g. S&P 500).

Examples

>>> index = Index('S&P 500')
>>> index
Index(S&P 500)
cash_requirement = 1.0
margin_requirement = 0.0
multiplier = 1.0
property symbol: str

Unique symbol associated with the concrete implementation. Non-unique symbols might lead to silent bugs as symbols are used to hash AbstractClass instances.

class tradingenv.contracts.Cash(symbol: str = 'USD')[source]

Bases: Asset

A cash contract (e.g. USD).

Examples

>>> cash = Cash('EUR')
>>> cash
Cash(EUR)
class tradingenv.contracts.ES(year: int, month: int)[source]

Bases: Future

Notes

First trading date is 1997-09-07, see [1].

References

[1] https://www.cmegroup.com/media-room/historical-first-trade-dates.html#equityIndices https://www.interactivebrokers.com/en/index.php?f=26662 https://www.cmegroup.com/clearing/risk-management/historical-margins.html https://www.interactivebrokers.com/en/index.php?f=1567&p=physical https://www.tradingacademy.com/lessons/article/futures-contract-rollover

Parameters:
  • year (int) -- Expiry year of the future contract.

  • month (int) -- Expiry month of the future contract.

exists_since = datetime.datetime(1997, 9, 7, 0, 0)
freq = 'QE-DEC'
property margin_requirement: float

A number ranging from 0 to 1 defining what (constant) proportion of the notional value should be deposited as initial _margin when buying or selling the contract. Note that by design we are making the simplifying assumption that initial _margin == maintenance _margin.

multiplier = 50.0
class tradingenv.contracts.ETF(symbol: str)[source]

Bases: Asset

An ETF contract (e.g. SPY).

Examples

>>> etf = ETF('SPY')
>>> etf
ETF(SPY)
class tradingenv.contracts.Future(year: int, month: int)[source]

Bases: AbstractContract

A future contract (e.g. ES, ZN)

Parameters:
  • year (int) -- Expiry year of the future contract.

  • month (int) -- Expiry month of the future contract.

cash_requirement = 0.0
exists_since = datetime.datetime(2000, 1, 1, 0, 0)
exists_until = datetime.date(2026, 8, 26)
abstract property freq: str

Returns frequency of the contracts, e.g. QE-DEC for ES, M for VX.

lifespan() float[source]

Returns the number of trading days left between now and the expiry date.

make_events() List[IEvent][source]

Returns an event run when the contract will expire thus becoming no longer investable.

abstract property margin_requirement: float

A number ranging from 0 to 1 defining what (constant) proportion of the notional value should be deposited as initial _margin when buying or selling the contract. Note that by design we are making the simplifying assumption that initial _margin == maintenance _margin.

month_codes = {1: 'F', 2: 'G', 3: 'H', 4: 'J', 5: 'K', 6: 'M', 7: 'N', 8: 'Q', 9: 'U', 10: 'V', 11: 'X', 12: 'Z'}
property symbol: str

Long symbol of the future contract, e.g. 'ESU19' for the ES contract expiring in

property symbol_short: str

Symbol is guaranteed to be unique across all contracts, and in fact it is used to hash the instance. The short symbol is not guaranteed to be unique. Useful e.g. for futures where the same future across different expiry dates share the same short symbol but not the same symbol.

class tradingenv.contracts.FutureChain(future_cls: Type[Future] = None, start: str | datetime = None, end: str | datetime = None, contracts: Sequence[Future] = None, month: int = 0)[source]

Bases: AbstractContract

Composition of Future contracts, where the only investable contract is the one associated with the leading month. You must have a very good reason for NOT using this class when running futures trading strategies. Thanks to this class, you don't have to work with continuous futures data, you can just use the chain of uncombined futures contracts across all expires dates of interest.

Parameters:
  • future_cls -- A subclass of Feature (e.g. ES, ZN, VX). Full list of available futures can be found in futures.py or provide your own implementation.

  • start -- Start period of the first available futures contract, passed to pandas.date_range.

  • end -- Last period of the first available futures contract, passed to pandas.date_range.

  • contracts -- A sequence of Future instances which will be represented by this single class. If you pass this argument, there is no need to also pass 'future_cls', 'start' or 'end' as they will be deduced.

  • month -- 0 by default, meaning that the preferred feature in the term structure is the one with the shorted expiry date (i.e. M1). If 1, then the second expiration date will be preferred etc.

Examples

>>> from tradingenv.contracts import ES
>>> future = FutureChain(ES, '2018-03', '2018-12')
>>> future.lead_contract(datetime(2018, 4, 1))
ES(ESM18)
>>> future = FutureChain(ES, '2018-03', '2018-12', month=1)
>>> future.lead_contract(datetime(2018, 4, 1))
ES(ESU18)
cash_requirement = 0.0
lead_contract(now: datetime = None, month: int = 0) Future[source]

Use self.now to identify the lead future contract

lifespan() float[source]

Returns the number of trading days left between now and the expiry date of the leading future contract.

make_events() List[IEvent][source]

Returns list of EventContractDiscontinued associated with every future contract belonging to the specified period when instancing this class.

property margin_requirement: float

A number ranging from 0 to 1 defining what (constant) proportion of the notional value should be deposited as initial _margin when buying or selling the contract. Note that by design we are making the simplifying assumption that initial _margin == maintenance _margin. This is generally 1 for non-derivatives (e.g. ETFs, Indexes, Stocks) and > 1 for derivatives (e.g. Future).

property multiplier: float

Multiplier of the future contract.

static_hashing() AbstractContract[source]

The hash of this class instance varies with the lead contract, which varies with time. This method returns the future instance associated with the current time and static hashing.

property symbol: str

Long symbol of the current leading contract from the current time (i.e. FutureChain.now). For example, in 2019 Oct, the leading ES contract symbol is 'ESZ19'.

property symbol_short: str

Symbol is guaranteed to be unique across all contracts, and in fact it is used to hash the instance. The short symbol is not guaranteed to be unique. Useful e.g. for futures where the same future across different expiry dates share the same short symbol but not the same symbol.

property underlyings: List[AbstractContract]

Returns list of all subcontracts. This is needed when implementing composite contract such as LeadingContract. For example for FutureChain(ES) this would return the list of ES contracts across all expiry dates. Note that this is not to be confused with the underlying index e.g. Index('S&P 500') for eMini S&P 500.

class tradingenv.contracts.Index(symbol: str)[source]

Bases: Asset

An ETF contract (e.g. SPY).

Examples

>>> etf = Index('SPY')
>>> etf
Index(SPY)
class tradingenv.contracts.NK(year: int, month: int)[source]

Bases: Future

References

https://www.cmegroup.com/trading/equity-index/international-index/nikkei-225-dollar_contract_specifications.html https://misc.interactivebrokers.com/cstools/contract_info/v3.10/index.php?action=Conid%20Info&wlId=IB&conid=345561869&lang=en https://www.cmegroup.com/trading/equity-index/nikkei-225-futures-and-options.html https://www.cmegroup.com/trading/equity-index/international-index/nikkei-225-dollar_contract_specifications.html

Parameters:
  • year (int) -- Expiry year of the future contract.

  • month (int) -- Expiry month of the future contract.

exists_since = datetime.datetime(1990, 11, 8, 0, 0)
freq = 'QE-DEC'
property margin_requirement: float

Trading terminates on business day before the 2nd friday of the contract month.

multiplier = 5.0
class tradingenv.contracts.Rate(symbol: str)[source]

Bases: AbstractContract

An rate (e.g. FED Funds rate).

Examples

>>> rate = Rate('FED funds rate')
>>> rate
Rate(FED funds rate)
cash_requirement = 1.0
margin_requirement = 0.0
multiplier = 1.0
property symbol: str

Unique symbol associated with the concrete implementation. Non-unique symbols might lead to silent bugs as symbols are used to hash AbstractClass instances.

verify(mid_price: float)[source]
class tradingenv.contracts.Stock(symbol: str)[source]

Bases: Asset

A stock (e.g. Miscrosoft).

Examples

>>> stock = Stock('MSFT')
>>> stock
Stock(MSFT)
class tradingenv.contracts.VX(year: int, month: int)[source]

Bases: Future

References

[1] https://www.cmegroup.com/media-room/historical-first-trade-dates.html#equityIndices https://www.interactivebrokers.com/en/index.php?f=2222&exch=cfe&showcategories=FUTGRP#productbuffer file:///home/federico/Downloads/Trading_VIX_Futures_Options.pdf http://www.cboe.com/micro/vix/pdf/VIX%20fact%20sheet%202019.pdf http://cfe.cboe.com/cfe-products/vx-cboe-volatility-index-vix-futures/contract-specifications https://www.cboe.com/ms/vix-futures-specsheet.pdf

Parameters:
  • year (int) -- Expiry year of the future contract.

  • month (int) -- Expiry month of the future contract.

exists_since = datetime.datetime(2004, 3, 26, 0, 0)
freq = 'M'
property margin_requirement: float

A number ranging from 0 to 1 defining what (constant) proportion of the notional value should be deposited as initial _margin when buying or selling the contract. Note that by design we are making the simplifying assumption that initial _margin == maintenance _margin.

multiplier = 1000.0
class tradingenv.contracts.ZB(year: int, month: int)[source]

Bases: _Treasury

Parameters:
  • year (int) -- Expiry year of the future contract.

  • month (int) -- Expiry month of the future contract.

margin_requirement = 0.05
multiplier = 1000
class tradingenv.contracts.ZF(year: int, month: int)[source]

Bases: _Treasury

Parameters:
  • year (int) -- Expiry year of the future contract.

  • month (int) -- Expiry month of the future contract.

margin_requirement = 0.02
multiplier = 1000
class tradingenv.contracts.ZN(year: int, month: int)[source]

Bases: _Treasury

Parameters:
  • year (int) -- Expiry year of the future contract.

  • month (int) -- Expiry month of the future contract.

margin_requirement = 0.03
multiplier = 1000
class tradingenv.contracts.ZQ(year: int, month: int)[source]

Bases: _Treasury

Parameters:
  • year (int) -- Expiry year of the future contract.

  • month (int) -- Expiry month of the future contract.

margin_requirement = 0.004
multiplier = 4167
class tradingenv.contracts.ZT(year: int, month: int)[source]

Bases: _Treasury

Parameters:
  • year (int) -- Expiry year of the future contract.

  • month (int) -- Expiry month of the future contract.

margin_requirement = 0.02
multiplier = 2000

tradingenv.env module

Create a new Mock object. Mock takes several optional arguments that specify the behaviour of the Mock object:

  • spec: This can be either a list of strings or an existing object (a class or instance) that acts as the specification for the mock object. If you pass in an object then a list of strings is formed by calling dir on the object (excluding unsupported magic attributes and methods). Accessing any attribute not in this list will raise an AttributeError.

    If spec is an object (rather than a list of strings) then mock.__class__ returns the class of the spec object. This allows mocks to pass isinstance tests.

  • spec_set: A stricter variant of spec. If used, attempting to set or get an attribute on the mock that isn't on the object passed as spec_set will raise an AttributeError.

  • side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT, the return value of this function is used as the return value.

    If side_effect is an iterable then each call to the mock will return the next value from the iterable. If any of the members of the iterable are exceptions they will be raised instead of returned.

  • return_value: The value returned when the mock is called. By default this is a new Mock (created on first access). See the return_value attribute.

  • unsafe: By default, accessing any attribute whose name starts with assert, assret, asert, aseert, or assrt raises an AttributeError. Additionally, an AttributeError is raised when accessing attributes that match the name of an assertion method without the prefix assert_, e.g. accessing called_once instead of assert_called_once. Passing unsafe=True will allow access to these attributes.

  • wraps: Item for the mock object to wrap. If wraps is not None then calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the wrapped object (so attempting to access an attribute that doesn't exist will raise an AttributeError).

    If the mock has an explicit return_value set then calls are not passed to the wrapped object and the return_value is returned instead.

  • name: If the mock has a name then it will be used in the repr of the mock. This can be useful for debugging. The name is propagated to child mocks.

Mocks can also be called with arbitrary keyword arguments. These will be used to set attributes on the mock after it is created.

tradingenv.events module

TradingEnv is an even-driven market simulator. All custom events must inherit the class IEvent.

class tradingenv.events.EventContractDiscontinued(time: datetime, contract: AbstractContract)[source]

Bases: IEvent

class tradingenv.events.EventDone(time: datetime, broker: Broker)[source]

Bases: IEvent

IEvent signaling that TradingEnv.step has finished to run.

class tradingenv.events.EventNBBO(time: ~datetime.datetime, contract: ~tradingenv.contracts.AbstractContract, bid_price: float, ask_price: float, bid_size: float = <Mock name='mock.inf' id='136273439566656'>, ask_size: float = <Mock name='mock.inf' id='136273439566656'>)[source]

Bases: IEvent

(Synchronous) National Best Bid and Offer.

Parameters:
  • contract ('tradingenv.contracts.AbstractContract') -- Contract ID. Generally a string (e.g. 'S&P 500') but could be a whatever hashable object.

  • bid_price (float) -- Bid transaction_price now.

  • ask_price (float) -- Ask transaction_price now.

  • bid_size (float) -- Bid size now (top of the book).

  • ask_size (float) -- Ask size now (top of the book).

  • time (datetime) -- Timestamp associated with the NBBO. If not provided, the time will be assumed to be the current UTC time.

ask_price
ask_size
bid_price
bid_size
contract
time: datetime
class tradingenv.events.EventNewDate(time: datetime, broker: Broker)[source]

Bases: IEvent

Triggered just before the first event of the date is processed.

class tradingenv.events.EventNewObservation(time: datetime, data: Mapping)[source]

Bases: IEvent

Stores a snapshot of exogenous variables at a given time.

to_list() <Mock name='mock.array' id='136273439567424'>[source]
class tradingenv.events.EventReset(time: datetime, track_record: TrackRecord)[source]

Bases: IEvent

IEvent signaling that TradingEnv.reset has finished to run.

class tradingenv.events.EventStep(time: datetime, track_record: TrackRecord, action)[source]

Bases: IEvent

IEvent signaling that TradingEnv.step has finished to run.

class tradingenv.events.IEvent[source]

Bases: object

TradingEnv is an even-driven simulator where events are passed using TradingEnv.add_event. All events must inherit this class.

time

Timestamp associated with the event, i.e. when the event occurred.

Type:

datetime

notify(observers: Sequence[Observer])[source]
time: datetime = None
class tradingenv.events.Observer(*args, **kwargs)[source]

Bases: ABC

_observed_events

A sequence of class Events. The state of a Feature is updated automatically by TradingEnv whenever an observed event occurs (i.e. it is passed to TradingEnv.add_event). By default, no event are observed.

Type:

Sequence[Type['tradingenv.events.IEvent']]

Notes

Generally in the observer patterns we store the functions to be run whenever an observed event is processed. Here we register the method id instead, which is slightly slower and certainly less elegant. The reason is that deepcopies of the env will introduce bucks because features will be copied but not their callback methods from the observed_events, which might introduce nasty bugs (e.g. when using ray).

Ideas for alternative implementations of the observer pattern:

https://stackoverflow.com/questions/1092531/event-system-in-python

reset()[source]

Re-initialise from scratch, discard attributes that don't come with the initialisation.

tradingenv.exchange module

Classes to store the state of the exchange, used to check buy and sell prices. Exchange is an aggregation of LimitOrderBook.

class tradingenv.exchange.Exchange(*args, **kwargs)[source]

Bases: Observer

An aggregation of limit order books.

_books

A dictionary mapping contract IDs to the corresponding instance of LimitOrderBook.

Type:

Dict[AbstractContract, LimitOrderBook]

Examples

>>> from tradingenv.exchange import Exchange
>>> from tradingenv.events import EventNBBO
>>> from tradingenv.contracts import ETF
>>> exchange = Exchange()
>>>
>>> # Assume that there is a NBBO update for S&P 500 and NASDAQ.
>>> nbbo_sp500 = EventNBBO(
...     time=datetime.now(),
...     bid_price=9,
...     ask_price=11,
...     bid_size=100,
...     ask_size=200,
...     contract=ETF('SPY'),
... )
>>> nbbo_nasdaq = EventNBBO(
...     time=datetime.now(),
...     bid_price=99,
...     ask_price=101,
...     bid_size=10,
...     ask_size=20,
...     contract=ETF('IEF'),
... )
>>> exchange.process_EventNBBO(nbbo_sp500)
>>> exchange.process_EventNBBO(nbbo_nasdaq)
>>> exchange
{ETF(SPY): '9 : 11', ETF(IEF): '99 : 101'}
acq_prices(keys: ~typing.Sequence[~tradingenv.contracts.AbstractContract], signs: <Mock name='mock.ndarray' id='136273437613456'>) <Mock name='mock.ndarray' id='136273437613456'>[source]
ask_prices(keys: Sequence[AbstractContract]) <Mock name='mock.array' id='136273439567424'>[source]

Return a np.array with mid prices of the provided contract IDs.

bid_prices(keys: Sequence[AbstractContract]) <Mock name='mock.array' id='136273439567424'>[source]

Return a np.array with mid prices of the provided contract IDs.

liq_prices(keys: ~typing.Sequence[~tradingenv.contracts.AbstractContract], signs: <Mock name='mock.ndarray' id='136273437613456'>) <Mock name='mock.ndarray' id='136273437613456'>[source]
mid_prices(keys: Sequence[AbstractContract]) <Mock name='mock.array' id='136273439567424'>[source]

Return a np.array with mid prices of the provided contract IDs.

process_EventContractDiscontinued(event: EventContractDiscontinued)[source]
process_EventNBBO(event: EventNBBO)[source]

Process an EventNBBO, representing an update of market prices and sizes in the limit order book.

Parameters:

event (EventNBBO) -- An event instance storing prices, sizes and contract_id to be updated.

spreads(keys: Sequence[AbstractContract]) <Mock name='mock.array' id='136273439567424'>[source]

Return a np.array with spreads of the provided contract IDs.

to_frame(contracts: Sequence[AbstractContract], join: str = 'outer')[source]
class tradingenv.exchange.LimitOrderBook(bid_price: float = <Mock name='mock.nan' id='136273437613024'>, ask_price: float = <Mock name='mock.nan' id='136273437613024'>, bid_size: float = <Mock name='mock.nan' id='136273437613024'>, ask_size: float = <Mock name='mock.nan' id='136273437613024'>, time: ~datetime.datetime = None)[source]

Bases: object

Limit order book of a single asset.

Parameters:
  • bid_price (float) -- Latest on-screen bid transaction_price.

  • ask_price (float) -- Latest on-screen ask transaction_price.

  • bid_size (float) -- Latest on-screen bid size.

  • ask_size (float) -- Latest on-screen ask size.

Examples

>>> from tradingenv.exchange import LimitOrderBook
>>> lob = LimitOrderBook(
...     bid_price=99.9,
...     ask_price=100.1,
...     bid_size=100,
...     ask_size=200,
... )
>>> lob
99.9 : 100.1
acq_price(quantity: float) float[source]

Returns the execution price of buying 'quantity' now.

liq_price(quantity: float) float[source]

Returns the execution price of selling 'quantity' now.

property mid_price: float

Returns the middle transaction_price between the bid and the ask.

property spread: float

Returns bid-ask spread.

terminate(event: EventContractDiscontinued)[source]

Reset and stop quotes in the lob but keep history.

to_frame(what: str = 'mid_price') <Mock name='mock.Series' id='136273437613120'>[source]
update(event: EventNBBO)[source]

tradingenv.features module

class tradingenv.features.Feature(*args, **kwargs)[source]

Bases: Observer

This class gives you the option to instance State as a collection of features. See docstring of State for further details.

exchange

Instance of tradingenv.exchange.Exchange, storing current and historical asset prices.

Type:

tradingenv.exchange.Exchange

action_space

Action space passed when instancing the environemtn.

Type:

tradingenv.spaces.PortfolioSpace

broker

Instance of tradingenv.broker.Broker storing current holdings, net liquidation value, pnl, past track record, past rebalancing requests, past trades, past commissions and more.

Type:

tradingenv.broker.Broker

Parameters:
  • space -- An optional Space needed for OpenAI-gym compatibility. Note: only the feature before transformations (if any) will be validated against the space.

  • name -- An optional name of the feature, class name by default. It is useful to provide a custom name to differentiate the same feature in the state if provided more than once e.g. with different parameters.

  • save -- If True (default), the output of Feature.parse() will be automatically saved to feature.history whenever (1) an observed event is processed by the feature or (2) feature() is called.

  • transformer -- A sklearn preprocessing transformer, defaults to None. If provided, by default the features returned by parse will be transformed. The easiest way to fitting transformers is to pass fit_transformer=True when instancing TradingEnv. You can set custom routines either by passing extending transformers or by implementing Feature._manual_fit_transformer.

action_space: tradingenv.spaces.PortfolioSpace = None
broker: tradingenv.broker.Broker = None
exchange: tradingenv.exchange.Exchange = None
fit_transformer()[source]

Fit transformed using all historical observations (batch) by default. The user can optionally implement _manual_fit_transformer to override the procedure.

parse()[source]

Returns any data structure representing the current value assumed by the feature. If a 'space' is provided when instancing this Feature, the returned value will be validated against the space if verify=True. This method is also required if you desire to store historical values of the feature in Feature.history whenever an observed event is received.

reset(exchange: tradingenv.exchange.Exchange = None, action_space: tradingenv.spaces.PortfolioSpace = None, broker: tradingenv.broker.Broker = None)[source]

Reset the feature to its original state. We pass exchange, action_space and broker as they could be needed to compute some features such as current positions, portfolio weights, market prices etc.

tradingenv.library module

Note: all transformers expect 2D arrays in feature.transformer.transform.

ValueError: Expected 2D array, got 1D array instead

Therefore, all space shapes should really be 2D and parsed history 3D. However,

'vx-term-structure-basis': mid_prices[1:] - self.exchange[Contracts.vix].mid_price, 'vx-term-structure-roll-yield': np.array([term_structure.roll_yield(0, t) for t in lifespan[:2]]), 'vx-roll-yield-30days': roll_yield_30d, 'vx-implied-sharpe-30days': implied_sharpe, 'time-since-vix-update': np.array([hours_since_vix_update]),

class tradingenv.library.FeatureIsRTH(*args, **kwargs)[source]

Bases: Feature

Boolean flag indicating weather markets is trading during Regular Trading Hours.

Parameters:
  • space -- An optional Space needed for OpenAI-gym compatibility. Note: only the feature before transformations (if any) will be validated against the space.

  • name -- An optional name of the feature, class name by default. It is useful to provide a custom name to differentiate the same feature in the state if provided more than once e.g. with different parameters.

  • save -- If True (default), the output of Feature.parse() will be automatically saved to feature.history whenever (1) an observed event is processed by the feature or (2) feature() is called.

  • transformer -- A sklearn preprocessing transformer, defaults to None. If provided, by default the features returned by parse will be transformed. The easiest way to fitting transformers is to pass fit_transformer=True when instancing TradingEnv. You can set custom routines either by passing extending transformers or by implementing Feature._manual_fit_transformer.

parse()[source]

Parse progress bar during RTH, ETH or just a dummy indicating weather

class tradingenv.library.FeaturePortfolioWeight(*args, **kwargs)[source]

Bases: Feature

Portfolio weights of holdings.

Parameters:
  • contracts -- A contract or sequence of contracts.

  • low -- Minimum possible portfolio allocation to the contract. E.g. -1.5 for -150% (i.e. 150% short).

  • high -- Minimum possible portfolio allocation to the contract. E.g. -1.5 for -150% (i.e. 150% short).

  • name -- Feature name. Default is class name if omitted.

  • transformer -- A class from sklearn.preprocessing.

Notes

Because gymnasium.spaces.Box forces the same low and high bounds on all items, you'll have to instance a different PortfolioWeight for each contract with different 'low' and 'high' allocation.

parse()[source]

Returns array of currently held weights of self.contracts in the portfolio.

class tradingenv.library.FeaturePrices(*args, **kwargs)[source]

Bases: Feature

Feature representing price data.

Parameters:
  • contracts -- A contract or sequence of contracts.

  • name -- Feature name. Default is class name if omitted.

  • transformer -- A class from sklearn.preprocessing.

parse()[source]

Parse sequence of prices.

class tradingenv.library.FeatureSpread(*args, **kwargs)[source]

Bases: Feature

Feature representing prices spreads.

Parameters:
  • contracts -- A contract or sequence of contracts.

  • clip -- Spread (%) greater than this are clipped. By default this is 0.01 (i.e. 1%).

  • name -- Feature name. Default is class name if omitted.

  • transformer -- A class from sklearn.preprocessing. Note that spread can be highly positively skewed unless clipped or log transformed if the market data include overnight sessions, illiquid or emerging assets, in which case you might want to log the spread or use a more sophisticated transformer to normalise the data.

parse()[source]

Parse sequence of spreads.

tradingenv.library.listify(item)[source]

If the item is not iterable, insert the item in a list.

tradingenv.metrics module

It is harder to convert returns to levels than viceversa because the former requires to know what was the base date.

tradingenv.metrics.to_pandas(method: ~typing.Callable = None, target: ~typing.Type = <Mock name='mock.NDFrame' id='136273437619168'>)[source]

This decorator simply adds method to the signature of cls, which is NDFrame by default.

Parameters:
  • method (Callable) -- A function definition whose first argument is an instance of cls.

  • target (Type) -- A class type. Input 'method' will be set as attribute of this 'target' class.

Notes

When optional arguments are passed to properties, then the first argument ('method' in this case) is None. This requires special handling, managed by the big if-else below. The implementation has been inspired by: https://stackoverflow.com/a/24617244/7677636

Examples

>>> df = pd.DataFrame()
>>> series = pd.Series(dtype=float)
>>> # df.method()  # raises
>>> # series.method()  # raises
...
>>> @to_pandas(target=pd.DataFrame)
... def method(frame: NDFrame):
...     return 'Hello!'
>>> df.method()
'Hello!'
>>> # series.method()  # AttributeError: 'Series' object has no attribute 'method'
...
>>> @to_pandas(target=pd.Series)
... def method(frame: NDFrame):
...     return 'Hello!'
>>> df.method()
'Hello!'
>>> series.method()
'Hello!'

tradingenv.policy module

Logic to wrap policy passed in TradingEnv.sample_episode(policy).

class tradingenv.policy.AbstractPolicy[source]

Bases: ABC

abstractmethod act(state)[source]

Returns an action which belongs to the action observation_space, so action_space.contains(action) must return True.

action_space: <Mock name='mock.PortfolioSpace' id='136273439325168'> = None
observation_space: <Mock name='mock.Space' id='136273437613840'> = None
class tradingenv.policy.RandomPolicy[source]

Bases: AbstractPolicy

act(state=None)[source]

Returns an action which belongs to the action observation_space, so action_space.contains(action) must return True.

tradingenv.policy.make_policy(policy, action_space, observation_space) AbstractPolicy[source]

tradingenv.rewards module

The reward is a parameter of TradingEnv. Valid rewards must implement the abstract class AbstractReward.

class tradingenv.rewards.AbstractReward[source]

Bases: ABC

All custom rewards must implement this interface.

abstractmethod calculate(env: tradingenv.env.TradingEnv) float[source]

Return float associated with the last reward of the agent, generally manipulating stuff from env.broker.track_record. See implementations for examples.

reset() None[source]

Reset values of this class, if any.

class tradingenv.rewards.LogReturn(scale: float = 1.0, clip: float = 2.0, risk_aversion: float = 0.0)[source]

Bases: AbstractReward

Parameters:
  • scale -- Reward is divided by this number before being returned. This is a helper to rescale the reward closer to a [-1, +1] range.

  • clip -- Rewards larger than this clip value are truncated.

  • risk_aversion -- Negative rewards are multiplied by (1 + risk_aversion). Zero by default. Risk aversion is computed after clipping the reward. Empirically, values around 0.1 seem to work well but optimal values strongly depend on the use case.

Notes

For a good reference on why reward shaping is important see [1].

References

[1] van Hasselt, Hado P., et al. "Learning values across many orders of magnitude." Advances in neural information processing systems 29 (2016).

calculate(env: tradingenv.env.TradingEnv) float[source]

Return float associated with the last reward of the agent, generally manipulating stuff from env.broker.track_record. See implementations for examples.

class tradingenv.rewards.RewardDifferentialSharpeRatio[source]

Bases: object

An elegant online Sharpe ratio which uses the second order Taylor expansion. I still wonder how problematic this approximation might be?

References

http://papers.nips.cc/paper/1551-reinforcement-learning-for-trading.pdf https://quant.stackexchange.com/questions/37969/what-s-the-derivative-of-the-sharpe-ratio-for-one-asset-trying-to-optimize-on-i https://www.reddit.com/r/algotrading/comments/9xkvby/how_to_calculate_differential_sharpe_ratio/

calculate(env: tradingenv.env.TradingEnv) float[source]
class tradingenv.rewards.RewardLogReturn[source]

Bases: AbstractReward

Log change of the net liquidation value of the account at each step.

calculate(env: tradingenv.env.TradingEnv) float[source]

Return float associated with the last reward of the agent, generally manipulating stuff from env.broker.track_record. See implementations for examples.

class tradingenv.rewards.RewardPnL[source]

Bases: AbstractReward

Profit and Loss reward.

calculate(env: tradingenv.env.TradingEnv) float[source]

Return float associated with the last reward of the agent, generally manipulating stuff from env.broker.track_record. See implementations for examples.

class tradingenv.rewards.RewardSimpleReturn[source]

Bases: AbstractReward

Simple change of the net liquidation value of the account at each step.

calculate(env: tradingenv.env.TradingEnv) float[source]

Return float associated with the last reward of the agent, generally manipulating stuff from env.broker.track_record. See implementations for examples.

tradingenv.rewards.make_reward(reward: AbstractReward | str)[source]

Valid reward are implementation of the interface AbstractReward. However, this method allows the user to specify the reward as a string and the corresponding id will be retrieved from tradingenv.rewards.

tradingenv.spaces module

Create a new Mock object. Mock takes several optional arguments that specify the behaviour of the Mock object:

  • spec: This can be either a list of strings or an existing object (a class or instance) that acts as the specification for the mock object. If you pass in an object then a list of strings is formed by calling dir on the object (excluding unsupported magic attributes and methods). Accessing any attribute not in this list will raise an AttributeError.

    If spec is an object (rather than a list of strings) then mock.__class__ returns the class of the spec object. This allows mocks to pass isinstance tests.

  • spec_set: A stricter variant of spec. If used, attempting to set or get an attribute on the mock that isn't on the object passed as spec_set will raise an AttributeError.

  • side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT, the return value of this function is used as the return value.

    If side_effect is an iterable then each call to the mock will return the next value from the iterable. If any of the members of the iterable are exceptions they will be raised instead of returned.

  • return_value: The value returned when the mock is called. By default this is a new Mock (created on first access). See the return_value attribute.

  • unsafe: By default, accessing any attribute whose name starts with assert, assret, asert, aseert, or assrt raises an AttributeError. Additionally, an AttributeError is raised when accessing attributes that match the name of an assertion method without the prefix assert_, e.g. accessing called_once instead of assert_called_once. Passing unsafe=True will allow access to these attributes.

  • wraps: Item for the mock object to wrap. If wraps is not None then calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the wrapped object (so attempting to access an attribute that doesn't exist will raise an AttributeError).

    If the mock has an explicit return_value set then calls are not passed to the wrapped object and the return_value is returned instead.

  • name: If the mock has a name then it will be used in the repr of the mock. This can be useful for debugging. The name is propagated to child mocks.

Mocks can also be called with arbitrary keyword arguments. These will be used to set attributes on the mock after it is created.

tradingenv.state module

class tradingenv.state.IState(*args, **kwargs)[source]

Bases: Observer

An object storing the state of the environment. If you need OpenAI-gym compatibility, you are expected to implement IState.space and IState.parse in order to define the appropriate data structure to store the state.

Parameters:

features -- A sequence of classes implementing Feature. For small states, you probably don't need this. If your state class is becoming too big or if you need features transformation, a common requirement in machine learning applications, implementing Feature allows logic decoupling as IState will be a collection of features. Each feature is an observer of events and can define its own logic decoupled from the rest of the state or other features. Note: if you implement features, you don't have to implement any other method of this class. You are expected to implement only feature and their abstract methods and attributed. See tests for examples.

parse()[source]

Returns an object compatible with IState.space. If IState.space is None, you don't have to worry about implementing this method as IState() will simply return self. If features are provided, no need to implement IState.parse.

reset(exchange: tradingenv.exchange.Exchange = None, action_space: tradingenv.spaces.PortfolioSpace = None, broker: tradingenv.broker.Broker = None)[source]

Reset state and features for next episode.

space: <Mock name='mock.Space' id='136273437621376'> = None
class tradingenv.state.State(*args, **kwargs)[source]

Bases: IState

This State implementation allows to specify a window of past data. Useful when the observation is not Markovian.

Parameters:
  • features -- Number of features or list of feature names. If int, feature names will be 0, 1, ..., n-1.

  • window -- Window size of the observations. In other words, the observation returned by the environment in a given timestep is given by the concatenation of as many past observations. Using a window larger than 1 can be useful when past observation may have informational value that is observations are not Markovian.

  • max -- Maximum value of each feature. This is used to scale the state. The state is scaled to [-max_, +max_]. The default value is 100, arguably a large value. This is because we defer sanity checks to input features to data preprocessing.

flatten() dict[source]

Flatten the history of past states. Can be useful to parse complex states in tabular form for further analysis or visualisations.

parse() <Mock name='mock.array' id='136273439567424'>[source]

Returns a numpy array of shape (window, n_features).

process_EventNewObservation(event: EventNewObservation)[source]
tradingenv.state.cache(callback=None, check_every: int = <Mock name='mock.inf' id='136273439566656'>, check_on_start: bool = True)[source]

Decorator which allows to use cached results from previous episodes/backtests of Feature.process_<Event> methods to save computations.

Parameters:
  • callback -- A convenience arg all allows cache being either callable or not.

  • check_every (bool) -- If True, every 'check_every' the cached result will not be used and compared with the cached result. If there is basis_naive_tm1 difference, an error will be raised, meaning that your features should probably not used cached results. See Notes.

  • check_on_start (bool) -- If True, cached and non-cached results will be compared when the feature will receive its first observed event. If they are different, an error will be raised, meaning that your features should probably not used cached results. See Notes.

Notes

By cached result we mean the state of the feature calculated at the same timestep from basis_naive_tm1 previous episode. By the nature of this caching mechanism, there is no way to use cached results when your feature is not deterministic and in such case an error will be raised presumably at the first check, assuming that some form of checking is performed as indicated by the arguments of this function.

tradingenv.transmitter module

TradingEnv must receive events in order to update its state during a simulation. This could either be done by the client with TradingEnv.add_event (low-level) OR by providing a AbstractTransmitter when instancing TradingEnv ( high-level, recommended).

class tradingenv.transmitter.AbstractTransmitter[source]

Bases: ABC

Implementations of this interface take care of delivering events to the trading environment during the interaction of the agent with the environment at pre-specified timesteps.

abstractmethod add_events(events: List[IEvent])[source]
class tradingenv.transmitter.AsynchronousTransmitter[source]

Bases: AbstractTransmitter

This AbstractTransmitter does not schedule any event to the TradingEnv. Suitable for when deploying to production your code.

add_events(events: List[IEvent])[source]
class tradingenv.transmitter.Folds(timesteps: Sequence[datetime], train_start: Sequence, train_end: Sequence, test_start: Sequence, test_end: Sequence)[source]

Bases: object

as_time()[source]

Returns Folds instance storing timesteps associated with the end and start of each fold.

class tradingenv.transmitter.PartitionTimeRanges(folds: dict = None)[source]

Bases: object

verify_overlaps()[source]

Verify absence of any overlap across all time windows.

verify_start_before_end()[source]

Verify that start < end across all datasets.

class tradingenv.transmitter.Transmitter(timesteps: Sequence, folds: Dict[str, Sequence[datetime]] = None, markov_reset: bool = False, warmup: timedelta = None)[source]

Bases: AbstractTransmitter

AbstractTransmitter takes care of sending Event(s) to TradingEnv while interacting with the environment at specified timesteps. You want to use this class to run a backtest in order to schedule at what time the Event(s) (e.g. market prices or alternative data) should trigger.

events

A list accumulating events to be delivered to TradingEnv during the interaction.

Type:

List[Event]

timesteps

A list accumulating timesteps at which to deliver events to TradingEnv during the interaction.

Type:

List[IEvent]

Parameters:
  • timesteps (Sequence) -- A sequence of timestamps (e.g. datetime, date, timestamp). Starting from the oldest timestamp, Transmitter next will send events in batches stopping at the next timestamp (chronological order) until when the last timestep is reached.

  • folds (Dict[str, Sequence[datetime]]) -- A dictionary mapping fold _names (keys) to a pair (Sequence) of timesteps defining the start and end time of the fold. If not provided, all timesteps will belong to a single fold named 'training-set'.

  • markov_reset (bool) -- If False (default), all events occurring before the date of the reset will be processed to allow calculating derived data from past information. If True, past events will not be processed. Setting this to True will speed up the time required to reset the environment.

Examples

>>> from tradingenv.contracts import Index
>>> returns = pd.DataFrame(
...     data=np.random.normal(0, 0.001, (100, 2)),
...     columns=[Index('S&P 500'), Index('T-Bond')],
...     index=pd.date_range('2019-01-01', periods=100, freq='B'),
... )
>>> prices = (1 + returns).cumprod()
>>>
>>> # All prices belong to the training-set.
>>> transmitter = Transmitter(timesteps=prices.index)
>>> transmitter.add_prices(prices)
>>> # Partition prices between training and test set.
>>> transmitter = Transmitter(
...     timesteps=prices.index,
...     folds={
...         'training-set': [datetime(2019, 1, 1), datetime(2019, 3, 1)],
...         'test-set': [datetime(2019, 3, 2), datetime(2019, 5, 1)],
...     },
... )
>>> transmitter.add_prices(prices)
>>> transmitter._create_partitions()
>>> transmitter._reset(fold_name='test-set')
>>> # Retrieve batches of events, then processed by TradingEnv.add_event
>>> events_batch_0 = transmitter._next()
>>> events_batch_1 = transmitter._next()
add_custom_events(data: <Mock name='mock.DataFrame' id='136273440799872'>, event_class: ~typing.Type[~tradingenv.events.IEvent], mapping: ~typing.Dict[str, str] = None)[source]
Parameters:
  • data (pandas.DataFrame) -- Every row of this pandas.DataFrame will be used to create instance of EventType passing columns-state pairs as kwargs. The index of the dataframe must be of type Datetime and will be used to set Event.time.

  • event_class (Type[Event]) -- Arguments in EventType.__init__ will be searched from the columns of the dataframe and set accordingly.

  • mapping (Dict[str, str]) -- A dictionary mapping df column _names to attribute _names of EventType. There is no need to provide a mapping if the columns _names are exactly the same as the attribute _names of EventType.

Examples

>>> from tradingenv.events import IEvent
>>> from tradingenv.transmitter import Transmitter
>>> from datetime import datetime
>>>
>>> class NewsSentiment(IEvent):
...     def __init__(self, headline: str, sentiment: float):
...         self.headline = headline
...         self.sentiment = sentiment
>>>
>>> dataset = pd.DataFrame(
...     data=[
...         ['This is a very good news!', +0.7],
...         ['A kind of bad stuff has happened', -0.5],
...         ['Neutral news', 0],
...     ],
...     columns=['headline', 'sentiment'],
...     index=[
...         datetime(2019, 1, 1),
...         datetime(2019, 1, 1),
...         datetime(2019, 1, 2),
... ])
>>> transmitter = Transmitter(timesteps=dataset.index)
>>> transmitter.add_custom_events(dataset, NewsSentiment)
add_events(events: List[IEvent]) None[source]
Parameters:

events (Iterable[IEvent]) -- Events to be send to the environment. The time at which the Event will trigger corresponds to the first timestep greater or equal to Event.time.

add_prices(prices: <Mock name='mock.DataFrame' id='136273440799872'>, spread: float = 0) None[source]
Parameters:
  • prices (pandas.DataFrame) -- A pandas.DataFrame with the following characteristics: - index: datetime objects - columns: contract _names - values: mid prices. All mid prices will be mapped to EventNBBOs to be delivered while interacting with TradingEnv. No timesteps are added.

  • spread (float) -- Spread to be applied to mid_prices (zero by default). For example, if spread=0.001, then there will be a 0.1% bid-ask spread for all prices.

add_timesteps(timesteps: List[datetime]) None[source]
Parameters:

timesteps (Iterable[datetime]) -- Events will be sent at the intervals specified here.

walk_forward(train_size: int = None, test_size: int = None, sliding_window: bool = True) Folds[source]
Parameters:
  • train_size (int) -- Number of observations in the training folds.

  • test_size (int) -- Number of observation in the validation folds.

  • sliding_window (bool) -- If True (default), training folds will be computed using a sliding window with the same number of observations. If False, an expanding window will be used instead, so each training fold will start from the first timestep which a monotonically increasing number of observations.

Notes

By design, the step size of the window is set to be 'test_size'. Reasons are: - if 'step'<'test_size': your validation set is not using all available information. - if 'step'>'test_size': there is the risk of look-ahead bias because the next training fold will be validated on a portion of previously seen validation data.

Return type:

A Folds object.

Module contents