Get started¶
Requirements¶
Cryptrality is wrapped in a python package. So Python is required to use the software. Only Python3 (Python >= 3.4) is supported.
The library also depends on the talib C++ headers to be installed in the system
Installation¶
Note
It is strongly advised to use virtualenv to install the module locally.
From git with pip:¶
pip install git+https://git@github.com/Cryptrality/backtester.git
Conda environment:¶
name: cryptrality
channels:
- conda-forge
- defaults
dependencies:
- python=3.8
- ta-lib
- sphinxcontrib-programoutput
- sphinx_rtd_theme
- pip:
- "--editable=git+https://git@github.com/Cryptrality/backtester.git@main#egg=cryptrality"
After installation the cryptrality command line allows access to various modules:
$ cryptrality
Matplotlib is building the font cache; this may take a moment.
WARNING: proceeding without Binance client auth
usage: cryptrality [-h] {backtest,download_year,live} ...
Cryptrality backtest tool
positional arguments:
backtest Test a strategy code with historical data
download_year Download yearly historical data
live Run a strategy live on the exchangerm
optional arguments:
-h, --help show this help message and exit
This is version 0.01 - Francesco - 12/12/2021
Using the backtest sub-command
$ cryptrality backtest
error: the following arguments are required: strategy, -s/--start, -e/--end
WARNING: proceeding without Binance client auth
usage: cryptrality backtest -s START -e END [-o OUT] [--stats] [--plots]
[--chart_window {6h,24h,2d,7d,1M}]
[--exchange {binance_futures,binance_spot}]
[--hold_asset HOLD_ASSET]
strategy
positional arguments:
strategy strategy python source file
optional arguments:
-s START, --start START
Start date in the format of dd-mm-yy
-e END, --end END End date in the format of dd-mm-yy
-o OUT, --out OUT Output folder
--stats Toggle on the report generation
--plots Toggle on the candlestick chart generation
--chart_window {6h,24h,2d,7d,1M}
If plot is enabled, define the time window for the
candlestick data to be displayed
--exchange {binance_futures,binance_spot}
Define the exchange to run the backtest
--hold_asset HOLD_ASSET
Define the asset for the buy and hold comparison.
Default BTC-USD
Using the download_year sub-command
$ cryptrality download_year
error: the following arguments are required: symbol, -k/--kline, -y/--year
WARNING: proceeding without Binance client auth
usage: cryptrality download_year -k K -y [YEAR [YEAR ...]]
[-e {binance_futures,binance_spot}]
symbol
positional arguments:
symbol Symbol to download, eg ETHUSDT
optional arguments:
-k K, --kline K Granularity string, eg 1m 5m 1h.
-y [YEAR [YEAR ...]], --year [YEAR [YEAR ...]]
Year of the data to download. full year needed eg 2020
-e {binance_futures,binance_spot}, --exchange {binance_futures,binance_spot}
Supported exchanges
Example Run¶
This is an Test run with a simple EMA cross RSI example strategy:
import talib
from cryptrality.core import plot, plot_config
from cryptrality.misc import get_default_params
MARGIN_AMOUNT = 120
FUTURES_LEVERAGE = 2
BUY_VALUE = float(MARGIN_AMOUNT) * float(FUTURES_LEVERAGE)
TRADE_SYMBOLS = ["ETHUSDT", "BTCUSDT"]
CANDLE_PERIOD1 = "15m"
CANDLE_PERIOD2 = "1m"
MAX_DATA = 500
EMA_LONG = 40
EMA_SHORT = 10
RSI = 6
def initialize(state):
plot_config(
{
"ema_long": {"plot": "root", "type": "line", "color": "black"},
"ema_short": {"plot": "root", "type": "line", "color": "red"},
"rsi": {"plot": "rsi", "type": "line", "color": "black"},
"oversold": {"plot": "rsi", "type": "line", "color": "red"},
"overbought": {"plot": "rsi", "type": "line", "color": "red"},
}
)
state.params = {}
state.balance_quoted = 0
state.ema_values = {}
state.params["DEFAULT"] = {
"ema_long": EMA_LONG,
"ema_short": EMA_SHORT,
"rsi": RSI,
}
@schedule(interval=CANDLE_PERIOD1, symbols=TRADE_SYMBOLS, window_size=MAX_DATA)
def ema_5m(state, dataMap):
for symbol, data in dataMap.items():
strategy_logic_5m(state, data, symbol)
def strategy_logic_5m(state, data, symbol):
if data is None:
return
params = get_default_params(state, symbol)
ema_long_period = params["ema_long"]
ema_short_period = params["ema_short"]
ema_long = talib.EMA(data["close"], timeperiod=ema_long_period)
ema_short = talib.EMA(data["close"], timeperiod=ema_short_period)
state.ema_values[symbol] = [ema_long[-1], ema_short[-1]]
@schedule(interval=CANDLE_PERIOD2, symbols=TRADE_SYMBOLS, window_size=MAX_DATA)
def rsi_1m(state, dataMap):
for symbol, data in dataMap.items():
strategy_logic_rsi_1m(state, data, symbol)
def strategy_logic_rsi_1m(state, data, symbol):
if data is None:
return
params = get_default_params(state, symbol)
try:
ema_long, ema_short = state.ema_values[symbol]
except KeyError:
ema_long, ema_short = [None, None]
rsi_period = params["rsi"]
rsi = talib.RSI(data["close"], timeperiod=rsi_period)
data_plot = {key: values[-1] for key, values in data.items()}
data_plot["rsi"] = rsi[-1]
data_plot["ema_long"] = ema_long
data_plot["ema_short"] = ema_short
data_plot["oversold"] = 10
data_plot["overbought"] = 70
if len(data["close"]) < rsi_period * 2:
return
position = get_open_position(symbol, side="LONG")
has_position = position is not None
buy = False
sell = False
if has_position:
sell_quantity = position.quantity
if (
ema_long
and not has_position
and ema_long <= ema_short
and rsi[-1] < 10
):
buy = True
elif ema_long and has_position and ema_long <= ema_short and rsi[-1] >= 70:
sell = True
if buy:
order_market_value(symbol, value=BUY_VALUE, leverage=FUTURES_LEVERAGE)
logger.info("buy %s %s" % (symbol, data["close"][-1]))
elif sell:
order_market_amount(
symbol, quantity=-1 * sell_quantity, leverage=FUTURES_LEVERAGE
)
logger.info("sell %s %s" % (symbol, data["close"][-1]))
plot(symbol, data_plot)
The source code is used with the following command line:
$ cryptrality backtest -s 22-1-22 -e 25-1-22 ../example_strategies/multi_symbols_ema_rsi.py
INFO : 2023-07-25 22:43:46,659 : Writing bot logs to folder summary_reports
INFO : 2023-07-25 22:43:46,659 : Writing strategy logs to folder summary_reports
INFO : 2023-07-25 22:43:46,659 : Writing execution logs to folder summary_reports
INFO : 2023-07-25 22:43:46,665 : Caching 15m klines for ETHUSDT
INFO : 2023-07-25 22:43:46,732 : Caching 15m klines for BTCUSDT
INFO : 2023-07-25 22:43:46,795 : Caching 1m klines for ETHUSDT
INFO : 2023-07-25 22:43:48,222 : Caching 1m klines for BTCUSDT
INFO : 2023-07-25 22:43:49,893 : buy BTCUSDT 35291.3
INFO : 2023-07-25 22:43:49,898 : sell BTCUSDT 35211.95
INFO : 2023-07-25 22:43:49,925 : buy BTCUSDT 35038.12
INFO : 2023-07-25 22:43:49,957 : buy ETHUSDT 2414.4
INFO : 2023-07-25 22:43:49,961 : sell ETHUSDT 2431.47
INFO : 2023-07-25 22:43:49,966 : sell BTCUSDT 35513.46
INFO : 2023-07-25 22:43:50,018 : buy ETHUSDT 2515.69
INFO : 2023-07-25 22:43:50,023 : sell ETHUSDT 2505.56
INFO : 2023-07-25 22:43:50,047 : buy ETHUSDT 2481.03
INFO : 2023-07-25 22:43:50,137 : sell ETHUSDT 2537.94
INFO : 2023-07-25 22:43:50,160 : buy ETHUSDT 2465.6
INFO : 2023-07-25 22:43:50,160 : buy BTCUSDT 35678.89
INFO : 2023-07-25 22:43:50,171 : sell ETHUSDT 2456.92
INFO : 2023-07-25 22:43:50,171 : sell BTCUSDT 35637.71
INFO : 2023-07-25 22:43:50,173 : buy BTCUSDT 35381.06
INFO : 2023-07-25 22:43:50,176 : buy ETHUSDT 2432.21
INFO : 2023-07-25 22:43:50,326 : sell BTCUSDT 34840.88
INFO : 2023-07-25 22:43:50,334 : sell ETHUSDT 2388.62
INFO : 2023-07-25 22:43:50,344 : buy BTCUSDT 35808.05
INFO : 2023-07-25 22:43:50,350 : sell BTCUSDT 35937.67
INFO : 2023-07-25 22:43:50,376 : buy BTCUSDT 36551.21
INFO : 2023-07-25 22:43:50,377 : buy ETHUSDT 2427.0
INFO : 2023-07-25 22:43:50,380 : sell ETHUSDT 2425.56
INFO : 2023-07-25 22:43:50,383 : sell BTCUSDT 36467.53
WARNING: proceeding without Binance client auth
Creating The Singleton Runner
Total PnL: -5.715964
Number of winning trades 4 / 12
This is another example run with a more complex strategy provided in the examples
$ cryptrality backtest -s 22-1-22 -e 25-1-22 ../example_strategies/bayes_bollinger_multicoins_cooldown.py
INFO : 2023-07-25 22:43:52,450 : Writing bot logs to folder summary_reports
INFO : 2023-07-25 22:43:52,450 : Writing strategy logs to folder summary_reports
INFO : 2023-07-25 22:43:52,450 : Writing execution logs to folder summary_reports
INFO : 2023-07-25 22:43:52,464 : Caching 1d klines for MATICUSDT
INFO : 2023-07-25 22:43:52,504 : Caching 1d klines for MANAUSDT
INFO : 2023-07-25 22:43:52,540 : Caching 1d klines for ZILUSDT
INFO : 2023-07-25 22:43:52,558 : Caching 1d klines for 1INCHUSDT
INFO : 2023-07-25 22:43:52,596 : Caching 1d klines for ZRXUSDT
INFO : 2023-07-25 22:43:52,633 : Caching 1h klines for MATICUSDT
INFO : 2023-07-25 22:43:52,673 : Caching 1h klines for MANAUSDT
INFO : 2023-07-25 22:43:52,713 : Caching 1h klines for ZILUSDT
INFO : 2023-07-25 22:43:52,733 : Caching 1h klines for 1INCHUSDT
INFO : 2023-07-25 22:43:52,774 : Caching 1h klines for ZRXUSDT
INFO : 2023-07-25 22:43:52,814 : Caching 15m klines for MATICUSDT
INFO : 2023-07-25 22:43:52,868 : Caching 15m klines for MANAUSDT
INFO : 2023-07-25 22:43:52,924 : Caching 15m klines for ZILUSDT
INFO : 2023-07-25 22:43:52,943 : Caching 15m klines for 1INCHUSDT
INFO : 2023-07-25 22:43:52,993 : Caching 15m klines for ZRXUSDT
WARNING: proceeding without Binance client auth
Creating The Singleton Runner
Traceback (most recent call last):
File "/home/docs/checkouts/readthedocs.org/user_builds/cryptrality/checkouts/latest/src/cryptrality/cryptrality/exchanges/backtest_binance_spot.py", line 804, in sync_klines
buffer_klines[buffer_key] = next(
StopIteration
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/docs/checkouts/readthedocs.org/user_builds/cryptrality/conda/latest/bin/cryptrality", line 33, in <module>
sys.exit(load_entry_point('cryptrality', 'console_scripts', 'cryptrality')())
File "/home/docs/checkouts/readthedocs.org/user_builds/cryptrality/checkouts/latest/src/cryptrality/cryptrality/commands.py", line 34, in main
modules[args.module](subparsers, args.module, extra)
File "/home/docs/checkouts/readthedocs.org/user_builds/cryptrality/checkouts/latest/src/cryptrality/cryptrality/subcommands/backtest.py", line 166, in backtest
runner.run_forever()
File "/home/docs/checkouts/readthedocs.org/user_builds/cryptrality/checkouts/latest/src/cryptrality/cryptrality/exchanges/backtest_binance_spot.py", line 606, in run_forever
for k in self.klines:
RuntimeError: generator raised StopIteration