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:

  1. Exchange – concrete classes such as exchanges/simulated/simulated_exchange.py (20 functions) or exchanges/live/ccxt_exchange.py. They expose reset, step, and market‑data accessors.
  2. ActionStrategy – abstract base in actions/action_strategy.py (9 functions) with concrete ContinuousActionStrategy, DiscreteActionStrategy, etc. These expose action_space and action_type.
  3. RewardStrategy – defined in rewards/reward_strategy.py and specialized in simple_profit_strategy.py or risk_adjusted_return_strategy.py.
  4. FeaturePipelinefeatures/feature_pipeline.py (9 functions) orchestrates a list of FeatureTransformer objects from features/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 cyclesactions/__init__.py, rewards/__init__.py, slippage/__init__.py (12 circular edges).
  • High – Duplicated code – repeated 6‑line blocks across setup.py and three action‑strategy files.
  • High – Deep nesting – max indentation depth 16 in exchanges/simulated/simulated_exchange.py and related files.
  • Medium – Hub moduletrades/__init__.py is imported by 17 modules; changes here have wide impact.
  • Medium – Broad exception handlingexchanges/simulated/fbm_exchange.py uses bare except.
  • Low – Stale TODO/FIXME – 14 markers in live‑exchange modules.
  • Dependency driftpandas==0.25.0 (current 3.x) and numpy==1.16.4 (current 2.x) are pinned far behind upstream releases, risking incompatibility with modern tooling.
  • No lockfile; reproducible builds rely on requirements.txt alone.

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.