{ "cells": [ { "cell_type": "markdown", "id": "be764faf-65cc-408a-8ebd-96b8b4f14b60", "metadata": {}, "source": [ "# Basic RSI strategy" ] }, { "cell_type": "markdown", "id": "d15aa106-cb66-4fc8-b07d-347c078c634a", "metadata": { "tags": [] }, "source": [ "## Single backtest" ] }, { "cell_type": "code", "execution_count": null, "id": "dee14aee-a14c-4e54-bf82-dbe9f64cbd62", "metadata": {}, "outputs": [], "source": [ "from vectorbtpro import *\n", "# whats_imported()\n", "\n", "vbt.settings.set_theme('dark')" ] }, { "cell_type": "code", "execution_count": null, "id": "32105542-882d-4403-a86c-fdcd8002e258", "metadata": {}, "outputs": [], "source": [ "data = vbt.BinanceData.pull('BTCUSDT')\n", "data" ] }, { "cell_type": "code", "execution_count": null, "id": "843dc221-4353-47b7-9f3a-68b6b8ba8752", "metadata": {}, "outputs": [], "source": [ "data.data['BTCUSDT'].vbt.ohlcv.plot().show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "f14f65d8-a8b8-4cb0-a135-258b01b5ff83", "metadata": {}, "outputs": [], "source": [ "data.data['BTCUSDT'].info()" ] }, { "cell_type": "code", "execution_count": null, "id": "b3ef3cc2-c70f-4469-8067-fe5e1c6a06f3", "metadata": {}, "outputs": [], "source": [ "open_price = data.get('Open')\n", "close_price = data.get('Close')" ] }, { "cell_type": "code", "execution_count": null, "id": "4de98bf9-7f0e-48d8-b532-a917500d1f18", "metadata": {}, "outputs": [], "source": [ "vbt.IF.list_indicators(\"RSI*\")" ] }, { "cell_type": "code", "execution_count": null, "id": "af3df97f-b81e-4186-bf8e-adb50b29c5c0", "metadata": {}, "outputs": [], "source": [ "vbt.indicator(\"talib:RSI\")" ] }, { "cell_type": "code", "execution_count": null, "id": "3ce59b4c-7e47-4484-be62-0e26a2dc177e", "metadata": {}, "outputs": [], "source": [ "vbt.RSI" ] }, { "cell_type": "code", "execution_count": null, "id": "70589f4f-fa0f-448f-95f6-77c4833beede", "metadata": {}, "outputs": [], "source": [ "vbt.talib('RSI')" ] }, { "cell_type": "code", "execution_count": null, "id": "6ba90109-ca96-49c5-8ea9-2ad90755b43a", "metadata": {}, "outputs": [], "source": [ "vbt.ta('RSIIndicator')" ] }, { "cell_type": "code", "execution_count": null, "id": "cd56e11e-bf9e-4304-842f-6df7b2b0b738", "metadata": {}, "outputs": [], "source": [ "vbt.pandas_ta('RSI')" ] }, { "cell_type": "code", "execution_count": null, "id": "37d1140d-5ef0-4d22-9100-2b29e0e9f2ed", "metadata": {}, "outputs": [], "source": [ "print(vbt.format_func(vbt.RSI.run))" ] }, { "cell_type": "code", "execution_count": null, "id": "bee38f74-6c29-4cc9-a37e-25fe44c08bc8", "metadata": {}, "outputs": [], "source": [ "rsi = vbt.RSI.run(open_price)\n", "rsi" ] }, { "cell_type": "code", "execution_count": null, "id": "943a0d3c-c8f3-4359-9387-744e5bb3da44", "metadata": {}, "outputs": [], "source": [ "rsi.rsi" ] }, { "cell_type": "code", "execution_count": null, "id": "69636dd4-8592-48b8-b4cb-fabf7cbd7b15", "metadata": {}, "outputs": [], "source": [ "entries = rsi.rsi.vbt.crossed_below(30)\n", "entries" ] }, { "cell_type": "code", "execution_count": null, "id": "b073280b-69ea-43ba-8694-4c2b506be57e", "metadata": {}, "outputs": [], "source": [ "exits = rsi.rsi.vbt.crossed_above(70)\n", "exits" ] }, { "cell_type": "code", "execution_count": null, "id": "93ea75b8-5b75-4767-8adf-b535c9588147", "metadata": {}, "outputs": [], "source": [ "entries = rsi.rsi_crossed_below(30)\n", "exits = rsi.rsi_crossed_above(70)" ] }, { "cell_type": "code", "execution_count": null, "id": "c127d5f5-eee4-4d6a-ae26-7592f5d3ab65", "metadata": {}, "outputs": [], "source": [ "def plot_rsi(rsi, entries, exits):\n", " fig = rsi.plot()\n", " entries.vbt.signals.plot_as_entries(rsi.rsi, fig=fig)\n", " exits.vbt.signals.plot_as_exits(rsi.rsi, fig=fig)\n", " return fig" ] }, { "cell_type": "code", "execution_count": null, "id": "6743e20c-f2e6-4b64-9934-022fcc10acb7", "metadata": {}, "outputs": [], "source": [ "plot_rsi(rsi, entries, exits).show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "3e1ef2e8-e225-4d60-891a-6c3e9cc2821b", "metadata": {}, "outputs": [], "source": [ "clean_entries, clean_exits = entries.vbt.signals.clean(exits)\n", "\n", "plot_rsi(rsi, clean_entries, clean_exits).show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "1d2d4028-034b-4211-9a24-b82944b264da", "metadata": {}, "outputs": [], "source": [ "clean_entries.vbt.signals.total()" ] }, { "cell_type": "code", "execution_count": null, "id": "aeb5bbf3-1217-4c2f-a53a-3d426ac069c0", "metadata": {}, "outputs": [], "source": [ "clean_exits.vbt.signals.total()" ] }, { "cell_type": "code", "execution_count": null, "id": "f64bd28c-6dc9-476d-9bb1-a2e137a39dfe", "metadata": {}, "outputs": [], "source": [ "ranges = clean_entries.vbt.signals.between_ranges(target=clean_exits)\n", "ranges.duration.mean(wrap_kwargs=dict(to_timedelta=True))" ] }, { "cell_type": "code", "execution_count": null, "id": "8f55e1c9-9960-43ea-b148-41a6131caa7b", "metadata": {}, "outputs": [], "source": [ "pf = vbt.Portfolio.from_signals(\n", " close=close_price, \n", " entries=clean_entries, \n", " exits=clean_exits,\n", " size=100,\n", " size_type='value',\n", " init_cash='auto'\n", ")\n", "pf" ] }, { "cell_type": "code", "execution_count": null, "id": "02c8de50-5101-4a23-9d58-9c06c1ae4460", "metadata": {}, "outputs": [], "source": [ "pf.stats()" ] }, { "cell_type": "code", "execution_count": null, "id": "a6377f72-4174-49a1-8a9c-ad8095bc4437", "metadata": {}, "outputs": [], "source": [ "pf.plot(settings=dict(bm_returns=False)).show_svg()" ] }, { "cell_type": "markdown", "id": "c1f78a0a-7bc9-4b32-9f3c-92d0da44d87b", "metadata": {}, "source": [ "## Multiple backtests" ] }, { "cell_type": "markdown", "id": "909f506d-8edb-4fa8-8425-5e9c4b6e7b1b", "metadata": {}, "source": [ "### Using for-loop" ] }, { "cell_type": "code", "execution_count": null, "id": "a781ea63-d4df-41c5-820e-195afd3770c9", "metadata": {}, "outputs": [], "source": [ "def test_rsi(window=14, wtype=\"wilder\", lower_th=30, upper_th=70):\n", " rsi = vbt.RSI.run(open_price, window=window, wtype=wtype)\n", " entries = rsi.rsi_crossed_below(lower_th)\n", " exits = rsi.rsi_crossed_above(upper_th)\n", " pf = vbt.Portfolio.from_signals(\n", " close=close_price, \n", " entries=entries, \n", " exits=exits,\n", " size=100,\n", " size_type='value',\n", " init_cash='auto')\n", " return pf.stats([\n", " 'total_return', \n", " 'total_trades', \n", " 'win_rate', \n", " 'expectancy'\n", " ])" ] }, { "cell_type": "code", "execution_count": null, "id": "d60e7721-a0bb-4f2d-af92-d074ee523582", "metadata": {}, "outputs": [], "source": [ "test_rsi()" ] }, { "cell_type": "code", "execution_count": null, "id": "14e9e01d-9bcf-4e2c-8959-f5c66e1768c7", "metadata": {}, "outputs": [], "source": [ "test_rsi(lower_th=20, upper_th=80)" ] }, { "cell_type": "code", "execution_count": null, "id": "a7864dbb-5a24-456a-bda2-468e737fa8ea", "metadata": {}, "outputs": [], "source": [ "from itertools import product\n", "\n", "lower_ths = range(20, 31)\n", "upper_ths = range(70, 81)\n", "th_combs = list(product(lower_ths, upper_ths))\n", "len(th_combs)" ] }, { "cell_type": "code", "execution_count": null, "id": "fbc7c48e-e5e3-4ec7-8f76-7169d58231de", "metadata": {}, "outputs": [], "source": [ "comb_stats = [\n", " test_rsi(lower_th=lower_th, upper_th=upper_th)\n", " for lower_th, upper_th in th_combs\n", "]" ] }, { "cell_type": "code", "execution_count": null, "id": "8fbae677-56f9-49bc-88f7-325ff8ee00c4", "metadata": {}, "outputs": [], "source": [ "comb_stats_df = pd.DataFrame(comb_stats)\n", "print(comb_stats_df)" ] }, { "cell_type": "code", "execution_count": null, "id": "a99e9ded-2954-40c8-93c5-ebff4a174036", "metadata": {}, "outputs": [], "source": [ "comb_stats_df.index = pd.MultiIndex.from_tuples(\n", " th_combs, \n", " names=['lower_th', 'upper_th'])\n", "print(comb_stats_df)" ] }, { "cell_type": "code", "execution_count": null, "id": "69511f79-00cd-40e2-b64f-097137bba69e", "metadata": {}, "outputs": [], "source": [ "comb_stats_df['Expectancy'].vbt.heatmap().show_svg()" ] }, { "cell_type": "markdown", "id": "037bfdd0-e3dc-4ac5-9c0e-91c3320fcafd", "metadata": {}, "source": [ "### Using columns" ] }, { "cell_type": "code", "execution_count": null, "id": "ac5bc6ba-c6e1-4ca8-98fd-19b141a4220a", "metadata": {}, "outputs": [], "source": [ "windows = list(range(8, 21))\n", "wtypes = [\"simple\", \"exp\", \"wilder\"]\n", "lower_ths = list(range(20, 31))\n", "upper_ths = list(range(70, 81))" ] }, { "cell_type": "code", "execution_count": null, "id": "fb6145b0-6ee7-4ac2-a8c3-5bd7127032be", "metadata": {}, "outputs": [], "source": [ "rsi = vbt.RSI.run(\n", " open_price, \n", " window=windows, \n", " wtype=wtypes, \n", " param_product=True)\n", "rsi.rsi.columns" ] }, { "cell_type": "code", "execution_count": null, "id": "f374086a-bf84-425c-a857-0ce4ea8d6a28", "metadata": {}, "outputs": [], "source": [ "lower_ths_prod, upper_ths_prod = zip(*product(lower_ths, upper_ths))\n", "len(lower_ths_prod)" ] }, { "cell_type": "code", "execution_count": null, "id": "069d9ffe-cdda-45af-832d-62bfb47ede42", "metadata": {}, "outputs": [], "source": [ "len(upper_ths_prod)" ] }, { "cell_type": "code", "execution_count": null, "id": "bd96a283-b9bd-4824-9218-86efe06b8cc6", "metadata": {}, "outputs": [], "source": [ "lower_th_index = vbt.Param(lower_ths_prod, name='lower_th')\n", "entries = rsi.rsi_crossed_below(lower_th_index)\n", "entries.columns" ] }, { "cell_type": "code", "execution_count": null, "id": "c8510247-f1e1-4af7-8b14-0fb6d6f5813b", "metadata": {}, "outputs": [], "source": [ "upper_th_index = vbt.Param(upper_ths_prod, name='upper_th')\n", "exits = rsi.rsi_crossed_above(upper_th_index)\n", "exits.columns" ] }, { "cell_type": "code", "execution_count": null, "id": "209be8ac-393d-4a43-b752-022f3cfd8698", "metadata": {}, "outputs": [], "source": [ "pf = vbt.Portfolio.from_signals(\n", " close=close_price, \n", " entries=entries, \n", " exits=exits,\n", " size=100,\n", " size_type='value',\n", " init_cash='auto'\n", ")\n", "pf" ] }, { "cell_type": "code", "execution_count": null, "id": "15143c2b-b795-493c-9e8f-5aa0d877d391", "metadata": {}, "outputs": [], "source": [ "stats_df = pf.stats([\n", " 'total_return', \n", " 'total_trades', \n", " 'win_rate', \n", " 'expectancy'\n", "], agg_func=None)\n", "print(stats_df)" ] }, { "cell_type": "code", "execution_count": null, "id": "3d65043d-2081-4179-bc94-d42d1465a898", "metadata": {}, "outputs": [], "source": [ ">>> print(pf.getsize())" ] }, { "cell_type": "code", "execution_count": null, "id": "862f2e98-dea6-4c7e-b5e9-21c16ca27de2", "metadata": {}, "outputs": [], "source": [ ">>> np.product(pf.wrapper.shape) * 8 / 1024 / 1024" ] }, { "cell_type": "code", "execution_count": null, "id": "abea668f-5f95-44d3-a158-38440f70433d", "metadata": {}, "outputs": [], "source": [ "stats_df['Expectancy'].groupby('rsi_window').mean()" ] }, { "cell_type": "code", "execution_count": null, "id": "df815592-9f63-496e-8d88-99b1462d1030", "metadata": {}, "outputs": [], "source": [ "print(stats_df.sort_values(by='Expectancy', ascending=False).head())" ] }, { "cell_type": "code", "execution_count": null, "id": "1cb39bea-298c-4202-a996-c84b157f1ab9", "metadata": {}, "outputs": [], "source": [ "pf[(22, 80, 20, \"wilder\")].plot_value().show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "bced4fa8-f7eb-49e4-980b-f0f4acd20540", "metadata": {}, "outputs": [], "source": [ "data = vbt.BinanceData.pull(['BTCUSDT', 'ETHUSDT'])" ] }, { "cell_type": "code", "execution_count": null, "id": "55e86189-8244-4955-b736-f4f4f840b80a", "metadata": {}, "outputs": [], "source": [ "open_price = data.get('Open')\n", "close_price = data.get('Close')" ] }, { "cell_type": "code", "execution_count": null, "id": "bd7aefae-3f83-48ac-8f20-7d0a4760cd4b", "metadata": {}, "outputs": [], "source": [ "rsi = vbt.RSI.run(\n", " open_price, \n", " window=windows, \n", " wtype=wtypes, \n", " param_product=True)\n", "entries = rsi.rsi_crossed_below(lower_th_index)\n", "exits = rsi.rsi_crossed_above(upper_th_index)\n", "pf = vbt.Portfolio.from_signals(\n", " close=close_price, \n", " entries=entries, \n", " exits=exits,\n", " size=100,\n", " size_type='value',\n", " init_cash='auto'\n", ")\n", "stats_df = pf.stats([\n", " 'total_return', \n", " 'total_trades', \n", " 'win_rate', \n", " 'expectancy'\n", "], agg_func=None)" ] }, { "cell_type": "code", "execution_count": null, "id": "a7007b6a-bf08-40a6-a4be-091fec376290", "metadata": {}, "outputs": [], "source": [ "stats_df.index" ] }, { "cell_type": "code", "execution_count": null, "id": "a67661e7-d8df-4efe-8479-93d194ca255d", "metadata": {}, "outputs": [], "source": [ "eth_mask = stats_df.index.get_level_values('symbol') == 'ETHUSDT'\n", "btc_mask = stats_df.index.get_level_values('symbol') == 'BTCUSDT'\n", "pd.DataFrame({\n", " 'ETHUSDT': stats_df[eth_mask]['Expectancy'].values,\n", " 'BTCUSDT': stats_df[btc_mask]['Expectancy'].values\n", "}).vbt.histplot(xaxis=dict(title=\"Expectancy\")).show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "5a64e903-1256-4e4b-9d22-667f2706c9da", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.8" } }, "nbformat": 4, "nbformat_minor": 5 }