The Problem
Trading‑strategy research often stalls because data pipelines, environment simulation, and RL agent wiring are each built from scratch. The result is duplicated code, fragile imports, and a high cost to prototype or move from notebook to production.
What This Does
tensortrade supplies a modular RL‑driven trading stack. Core pieces live under tensortrade/ – e.g. environments/trading_environment.py builds a TradingEnvironment that glues together an exchange (exchanges/*_exchange.py), an action strategy (actions/*_strategy.py), a reward strategy (rewards/*_strategy.py), and a feature pipeline (features/feature_pipeline.py).
The library is deliberately thin on runtime scaffolding; the entry point for most users is the TradingEnvironment class and the strategy classes in strategies/ (e.g. tensorforce_trading_strategy.py). Example notebooks in examples/ show end‑to‑end usage without needing a separate CLI.
How It Is Wired
Execution begins when a client instantiates tensortrade.environments.trading_environment.TradingEnvironment. Its __init__ (17 functions total) stores references to:
- Exchange – concrete classes such as
exchanges/simulated/simulated_exchange.py(20 functions) orexchanges/live/ccxt_exchange.py. They exposereset,step, and market‑data accessors. - ActionStrategy – abstract base in
actions/action_strategy.py(9 functions) with concreteContinuousActionStrategy,DiscreteActionStrategy, etc. These exposeaction_spaceandaction_type. - RewardStrategy – defined in
rewards/reward_strategy.pyand specialized insimple_profit_strategy.pyorrisk_adjusted_return_strategy.py. - FeaturePipeline –
features/feature_pipeline.py(9 functions) orchestrates a list ofFeatureTransformerobjects fromfeatures/feature_transformer.py.
During a training episode, TradingEnvironment.step() calls the exchange’s step(), passes the observation through the FeaturePipeline, asks the strategy for an action, and finally computes a reward via the reward strategy. The resulting Trade objects are created by trades/trade.py (13 functions) and aggregated by the hub module trades/__init__.py, which is imported by 17 other modules – the largest blast radius in the import graph.
The import graph contains 12 circular dependencies (e.g. actions/__init__.py, rewards/__init__.py, slippage/__init__.py). These cycles increase cognitive load and make refactoring risky because a change in any cycle member can trigger import‑time side effects across the stack.
Deep nesting (max depth 16) appears in several exchange and action‑strategy files, making the control flow hard to follow. Duplicated 6‑line blocks are scattered across setup.py and the three action‑strategy implementations, suggesting a missed utility layer.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/tensortrade
cd tensortrade
# Install Python deps (exact versions are pinned in requirements.txt)
pip install -r requirements.txt
Optional: build the provided Docker image for an isolated environment.
docker build -t tensortrade .
docker run -it --rm tensortrade /bin/bash
Run an example notebook, e.g. examples/TensorTrade_Tutorial.ipynb, which creates a TradingEnvironment, attaches a TensorForceTradingStrategy, and calls environment.run() inside a training loop.
No additional config files are required; the examples construct all components programmatically. If you need a live broker, import the classes under exchanges/live/ and supply API keys directly to the constructor (the code does not read environment variables).
Real‑World Use
A quant team can replace a bespoke back‑test harness with:
from tensortrade.environments import TradingEnvironment
from tensortrade.exchanges import SimulatedExchange
from tensortrade.actions import DiscreteActionStrategy
from tensortrade.rewards import SimpleProfitStrategy
from tensortrade.features import FeaturePipeline
from tensortrade.strategies import TensorForceTradingStrategy
exchange = SimulatedExchange(data_frame=my_prices)
pipeline = FeaturePipeline([MyIndicator()])
env = TradingEnvironment(exchange=exchange,
action_strategy=DiscreteActionStrategy(),
reward_strategy=SimpleProfitStrategy(),
feature_pipeline=pipeline)
agent = TensorForceTradingStrategy(environment=env)
agent.train(episodes=500)
The agent can then be exported and deployed in a production service that streams live market data to the same SimulatedExchange subclass adapted for a real broker.
Code Health & Issues
- High – Import cycles –
actions/__init__.py,rewards/__init__.py,slippage/__init__.py(12 circular edges). - High – Duplicated code – repeated 6‑line blocks across
setup.pyand three action‑strategy files. - High – Deep nesting – max indentation depth 16 in
exchanges/simulated/simulated_exchange.pyand related files. - Medium – Hub module –
trades/__init__.pyis imported by 17 modules; changes here have wide impact. - Medium – Broad exception handling –
exchanges/simulated/fbm_exchange.pyuses bareexcept. - Low – Stale TODO/FIXME – 14 markers in live‑exchange modules.
- Dependency drift –
pandas==0.25.0(current 3.x) andnumpy==1.16.4(current 2.x) are pinned far behind upstream releases, risking incompatibility with modern tooling. - No lockfile; reproducible builds rely on
requirements.txtalone.
CI runs on Travis (.travis.yml) and a Dockerfile are present; tests exist (tests/), but coverage is modest (8 test files).
The Bottom Line
tensortrade offers a well‑documented, component‑driven RL trading framework that can accelerate prototyping and provide a clear path to production. However, the import cycles, deep nesting, and outdated pinned dependencies make the codebase fragile to change and potentially hard to maintain. It is suited for teams comfortable with Python refactoring and willing to modernize the dependency stack before extending the library.