{
 "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
}
