Internal development process#

Preface#

In this chapter, we will discuss how to developer trading-strategy and trade-executor Python packages.

Installation for local development#

See respective READMEs in Github repositories.

Run tests#

trading-strategy package#

To run tests you need to have a Trading Strategy API key. Tests use the production server.

poetry shell
export TRADING_STRATEGY_API_KEY="secret-token:tradingstrategy-xxx"
pytest

Tests are very slow.

By default, the test run will cache any downloaded blobs. You can force the redownload with:

CLEAR_CACHES=true pytest --log-cli-level=debug -k test_grouped_liquidity

Or manually:

rm -rf /tmp/trading-strategy-tests

trade-executor package#

To run the full test suite, multiple blockchain node connections are needed.

You will also need to have installed

  • Anvil

  • Ganache

You also need [Trading Strategy API key](https://tradingstrategy.ai/trading-view/backtesting).

To set environment:

# Free and somewhat robust Polygon endpoint
export JSON_RPC_POLYGON="https://polygon-rpc.com"
export TRADING_STRATEGY_API_KEY=...
export BNB_CHAIN_JSON_RPC=...

Then run:

pytest

Some tests take a long time, because they are checking different real-time timings. You can skip the slow tests with

export SKIP_SLOW_TESTS=true

For more information see Github CI pipeline.

Updating documentation notebooks#

The example notebooks must be run using jupyter notebook server before committing to the documentation, as otherwise Plotly figures won’t display.

  • Visual Studio Code notebook output gives you Data type cannot be displayed: application/vnd.plotly.v1+json error if added via nbsphinx

  • PyCharm has internal bugs and fails to finish running notebooks

First set up the development environment:

git clone [email protected]:tradingstrategy-ai/docs.git
co docs
make \
  update-git-submodules \
  poetry-install \
  pip-force-install-deps \
  install-furo \
  rebuild-furo \
  clean-autosummary \
  clean \
  html

This will create build/html/index.html which you can open in your web browser

open build/html/index.html

And now you can browse the documentation locally using file:// protcol.

Then to rerun and rerender a Jupyter notebook locally.

First start the notebook browser locally in Poetry environment:

jupyter notebook

This will spawn a Jupyter server and open a Jupyter notebook user interface in your web browser. You can later close this with CTRL + C.

Find a notebook that you want to rerender.

Choose clear output. Run it.

Manually inspect that the notebook complete and there are no errors

  • All figures are rendered

  • The last cell with print(“Ok”)

= There are no excessive warnings (some warnings are ok)

Then commit new notebook

  • Create a branch

  • Push in refreshed ipynb file

  • Open a PR

Note

Because how Sphinx automsummary works, it may update files under source tree, so be careful when doing a full regeneration of documentation.

Terminal IPython and debugging with ipdb#

You might want to run notebooks in a terminal using ipython command e.g. for better debugging facilities.

You can run example notebooks in a terminal after git checkout and poetry install:

ipython --TerminalIPythonApp.file_to_run=source/programming/strategy-examples/pancakeswap-ema.ipynb

This is especially useful if you want to debug library code with ipdb.

Dataset cache#

The default cache location for the downloaded datasets is ~/.cache/tradingstrategy.

ls -lha ~/.cache/tradingstrategy
total 56M
drwxr-xr-x  5 moo staff  160 Jul 19 23:14 ./
drwx------ 14 moo staff  448 Jul 18 15:49 ../
-rw-r--r--  1 moo staff  49M Jul 19 23:14 candles-24h.feather
-rw-r--r--  1 moo staff  95K Jul 18 15:49 exchange-universe.json
-rw-r--r--  1 moo staff 6.3M Jul 19 21:57 pair-universe.json.zstd

You can clear this out manually from the UNIX shell

rm -rf ~/.cache/tradingstrategy

Making a release#

Release with poetry.

poetry build
poetry publish

Memory profiling#

Use pytest-monitor for profiling memory usage. We need to ensure large datasets do not cause issues on low-memory environments like WebAsssembly in web browsers.

pytest-monitor is installed as a dev dependency.

Example:

pytest --db ./monitor.db

sqlite3 ./monitor.db

Then

select ITEM, MEM_USAGE from TEST_METRICS;

See more examples here.

Leak detection#

For memory leaks see pytest-leaks.

Warning

pytest-leaks crashes with pyarrow package

Note

Python debug build needed. Here are instructions how to install one. Also, this means you need to create another Poetry environment for leak testing.

First create install a Python debug build with pyenv.

Pick a Python version you want to have a debug build. This cannot be the same as your current Python version, as pyenv will overwrite it. The easiest way is to pick a different patch version of your current interpreter.

pyenv install --list
# We have 3.10.13 as main
pyenv install --debug 3.10.12

Then create a new Poetry environment with debug Python:

git clone --recursive [email protected]:tradingstrategy-ai/trade-executor.git executor-debug
cd executor-debug
pyenv local 3.10.12
# Should be 3.10.12
poetry install --all-extras
pip install pytest-leaks

And run a single test to figure out where the leaks are:

poetry shell
#
pytest --leaks : -k test_load_trading_and_lending_data_historical_certain_assets_only

Profiling Python notebooks for code speed bottlenecks#

You can profile a backtesting notebook with Python’s built-in profiler cProf.

In a notebook extract backtest run function to its own cell and add %%prun meta command:

%%prun -s cumulative

state, _, debug_dump = run_backtest_inline(
    name="SLS",
    start_at=start_at,
    end_at=end_at,
    client=client,
    cycle_duration=cycle_duration,
    decide_trades=decide_trades,
    universe=universe,
    initial_deposit=initial_deposit,
    reserve_currency=reserve_currency,
    trade_routing=trade_routing,
    log_level=logging.WARNING,
)

Then run the notebook

ipython notebooks/arbitrum-btc-usd-sls-binance-data-1h.ipynb

The output will be in less paged format in your terminal

      15192529 function calls (14695168 primitive calls) in 4.701 seconds

Ordered by: cumulative time

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   2/1    0.000    0.000    4.706    4.706 {built-in method builtins.exec}
     1    0.000    0.000    4.706    4.706 <string>:1(<module>)
     1    0.000    0.000    4.706    4.706 backtest_runner.py:405(run_backtest_inline)
     1    0.000    0.000    4.706    4.706 backtest_runner.py:297(run_backtest)
     1    0.000    0.000    4.706    4.706 loop.py:1209(run_and_setup_backtest)
     1    0.010    0.010    4.706    4.706 loop.py:669(run_backtest)
  1416    0.007    0.000    4.162    0.003 loop.py:328(tick)
  1416    0.013    0.000    4.138    0.003 runner.py:329(tick)

For more information

Building Docker image locally#

For testing trade-executor command or for writing documentation with an unreleased version.

docker build -t ghcr.io/tradingstrategy-ai/trade-executo/trade-executor:latest .

Then copy-paste the image hash from docker build output and run:

docker run -it cf308d43ad577c5194dd8669316a6a80ba6adc901f461ddf287f14915f206082 --help

Converting backtest notebooks to PDF#

You might want to convert backtest results Jupyter Notebooks for PDF format to share them.

Make sure you initialise notebook charting in static (offline) mode:

from tradeexecutor.backtest.notebook import setup_charting_and_output, OutputMode
# Set Jupyter Notebook output mode parameters
setup_charting_and_output(OutputMode.static)

Run the notebook e.g. using Visual Studio Code.

Then you can use nbconvert to generate a PDF out of the notebook:

# Mactex takes long to install
brew install --cask mactex
eval "$(/usr/libexec/path_helper)"
jupyter nbconvert --to pdf uniswap_v3_1h_arbitrum.ipynb

This will generate PDF file from the notebook.