{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d2ac023c-531a-487a-81f0-fcce3c9925b6",
   "metadata": {},
   "source": [
    "# SuperFast SuperTrend"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e052b3c7-c1ec-4245-8025-fbacbc71e1a6",
   "metadata": {},
   "source": [
    "## Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ea9cb06-d59c-4c2c-807a-23854fa16676",
   "metadata": {},
   "outputs": [],
   "source": [
    "from vectorbtpro import *\n",
    "# whats_imported()\n",
    "\n",
    "vbt.settings.set_theme('dark')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f8c4e61-3d13-4659-b559-3cef404adfb4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# data = vbt.BinanceData.pull(\n",
    "#     ['BTCUSDT', 'ETHUSDT'], \n",
    "#     start='2020-01-01 UTC',\n",
    "#     end='2022-01-01 UTC',\n",
    "#     timeframe='1h'\n",
    "# )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "508e2929-eab9-4db9-b581-d3a78959e173",
   "metadata": {},
   "outputs": [],
   "source": [
    "# data.to_hdf('my_data.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f4587b7-a9d2-4765-a52d-1ea9fa32bdbf",
   "metadata": {},
   "outputs": [],
   "source": [
    "data = vbt.HDFData.pull('my_data.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87de3e25-9836-45a4-9452-a3a0976a1d5d",
   "metadata": {},
   "outputs": [],
   "source": [
    "data.data['BTCUSDT'].info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9389c053-5429-4fe7-a05f-cd2961ebaf94",
   "metadata": {},
   "outputs": [],
   "source": [
    "data.stats()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b10cf7a1-88a5-42d4-820b-ae7752ab85f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "high = data.get('High')\n",
    "low = data.get('Low')\n",
    "close = data.get('Close')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d086a72b-de2e-4b0f-875d-54de8753b1ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(close)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc9fb395-725a-4376-8d89-f3cfbc0fb9dd",
   "metadata": {},
   "source": [
    "## Design"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94ad1367-4ca1-4e8a-8628-39242e93ceea",
   "metadata": {},
   "source": [
    "### Pandas"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b94872b9-23db-42d4-b5d5-ec15176c4f6e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_med_price(high, low):\n",
    "    return (high + low) / 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "45fdd6c7-4566-4468-8872-6f1f6c62991e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_atr(high, low, close, period):\n",
    "    tr0 = abs(high - low)\n",
    "    tr1 = abs(high - close.shift())\n",
    "    tr2 = abs(low - close.shift())\n",
    "    tr = pd.concat((tr0, tr1, tr2), axis=1).max(axis=1)\n",
    "    atr = tr.ewm(alpha=1 / period, adjust=False, min_periods=period).mean()\n",
    "    return atr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "37baaa9e-901d-4f49-a143-a9e1a24a20e7",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_basic_bands(med_price, atr, multiplier):\n",
    "    matr = multiplier * atr\n",
    "    upper = med_price + matr\n",
    "    lower = med_price - matr\n",
    "    return upper, lower"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b67ade24-1bec-411e-b570-e54ab89a47c5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_final_bands(close, upper, lower):\n",
    "    trend = pd.Series(np.full(close.shape, np.nan), index=close.index)\n",
    "    dir_ = pd.Series(np.full(close.shape, 1), index=close.index)\n",
    "    long = pd.Series(np.full(close.shape, np.nan), index=close.index)\n",
    "    short = pd.Series(np.full(close.shape, np.nan), index=close.index)\n",
    "\n",
    "    for i in range(1, close.shape[0]):\n",
    "        if close.iloc[i] > upper.iloc[i - 1]:\n",
    "            dir_.iloc[i] = 1\n",
    "        elif close.iloc[i] < lower.iloc[i - 1]:\n",
    "            dir_.iloc[i] = -1\n",
    "        else:\n",
    "            dir_.iloc[i] = dir_.iloc[i - 1]\n",
    "            if dir_.iloc[i] > 0 and lower.iloc[i] < lower.iloc[i - 1]:\n",
    "                lower.iloc[i] = lower.iloc[i - 1]\n",
    "            if dir_.iloc[i] < 0 and upper.iloc[i] > upper.iloc[i - 1]:\n",
    "                upper.iloc[i] = upper.iloc[i - 1]\n",
    "\n",
    "        if dir_.iloc[i] > 0:\n",
    "            trend.iloc[i] = long.iloc[i] = lower.iloc[i]\n",
    "        else:\n",
    "            trend.iloc[i] = short.iloc[i] = upper.iloc[i]\n",
    "            \n",
    "    return trend, dir_, long, short"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb3af20c-d59e-4bef-854e-b59bad25acce",
   "metadata": {},
   "outputs": [],
   "source": [
    "def supertrend(high, low, close, period=7, multiplier=3):\n",
    "    med_price = get_med_price(high, low)\n",
    "    atr = get_atr(high, low, close, period)\n",
    "    upper, lower = get_basic_bands(med_price, atr, multiplier)\n",
    "    return get_final_bands(close, upper, lower)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ac2ba411-f0e2-49ba-87b0-c2510e0d35f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "supert, superd, superl, supers = supertrend(\n",
    "    high['BTCUSDT'], \n",
    "    low['BTCUSDT'], \n",
    "    close['BTCUSDT']\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7baab77d-8681-49ac-af7e-7ca5671adbb2",
   "metadata": {},
   "outputs": [],
   "source": [
    "supert"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6d8c9e67-84e0-4102-8c33-1b3b6fc3ad0e",
   "metadata": {},
   "outputs": [],
   "source": [
    "superd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bde7f7ec-7564-447a-bf48-27873ff72a8a",
   "metadata": {},
   "outputs": [],
   "source": [
    "superl"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f21db639-c324-4325-b9b9-0def69c37497",
   "metadata": {},
   "outputs": [],
   "source": [
    "supers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "44cdaed3-f1a3-4202-8d74-fb9509aca2c8",
   "metadata": {},
   "outputs": [],
   "source": [
    "date_range = slice('2020-01-01', '2020-02-01')\n",
    "fig = close.loc[date_range, 'BTCUSDT'].rename('Close').vbt.plot()\n",
    "supers.loc[date_range].rename('Short').vbt.plot(fig=fig)\n",
    "superl.loc[date_range].rename('Long').vbt.plot(fig=fig).show_svg()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5fc5caf2-d308-4401-b36a-dcfdc6c16b55",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "supertrend(high['BTCUSDT'], low['BTCUSDT'], close['BTCUSDT'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a73d8c09-378b-4c27-b306-8ea61153207b",
   "metadata": {},
   "outputs": [],
   "source": [
    "SUPERTREND = vbt.pandas_ta('SUPERTREND')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ed795d82-9324-413a-a343-9d2ad3b06750",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "SUPERTREND.run(high['BTCUSDT'], low['BTCUSDT'], close['BTCUSDT'])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42175d89-0ee6-42c4-9ce0-4124603cf90c",
   "metadata": {},
   "source": [
    "### NumPy + Numba"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c401b752-fb8f-4e4f-a626-5b1bc1b00abf",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_atr_np(high, low, close, period):\n",
    "    shifted_close = vbt.nb.fshift_1d_nb(close)\n",
    "    tr0 = np.abs(high - low)\n",
    "    tr1 = np.abs(high - shifted_close)\n",
    "    tr2 = np.abs(low - shifted_close)\n",
    "    tr = np.column_stack((tr0, tr1, tr2)).max(axis=1)\n",
    "    atr = vbt.nb.wwm_mean_1d_nb(tr, period)\n",
    "    return atr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5020dffd-48cb-4805-b258-5b6987792d1d",
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit\n",
    "def get_final_bands_nb(close, upper, lower):\n",
    "    trend = np.full(close.shape, np.nan)\n",
    "    dir_ = np.full(close.shape, 1)\n",
    "    long = np.full(close.shape, np.nan)\n",
    "    short = np.full(close.shape, np.nan)\n",
    "\n",
    "    for i in range(1, close.shape[0]):\n",
    "        if close[i] > upper[i - 1]:\n",
    "            dir_[i] = 1\n",
    "        elif close[i] < lower[i - 1]:\n",
    "            dir_[i] = -1\n",
    "        else:\n",
    "            dir_[i] = dir_[i - 1]\n",
    "            if dir_[i] > 0 and lower[i] < lower[i - 1]:\n",
    "                lower[i] = lower[i - 1]\n",
    "            if dir_[i] < 0 and upper[i] > upper[i - 1]:\n",
    "                upper[i] = upper[i - 1]\n",
    "\n",
    "        if dir_[i] > 0:\n",
    "            trend[i] = long[i] = lower[i]\n",
    "        else:\n",
    "            trend[i] = short[i] = upper[i]\n",
    "            \n",
    "    return trend, dir_, long, short"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0db7eb3a-c3ac-4495-a91e-ba056c09b7af",
   "metadata": {},
   "outputs": [],
   "source": [
    "def faster_supertrend(high, low, close, period=7, multiplier=3):\n",
    "    med_price = get_med_price(high, low)\n",
    "    atr = get_atr_np(high, low, close, period)\n",
    "    upper, lower = get_basic_bands(med_price, atr, multiplier)\n",
    "    return get_final_bands_nb(close, upper, lower)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1faaaafb-871c-46c9-a03e-2a81ec075e8f",
   "metadata": {},
   "outputs": [],
   "source": [
    "supert, superd, superl, supers = faster_supertrend(\n",
    "    high['BTCUSDT'].values, \n",
    "    low['BTCUSDT'].values, \n",
    "    close['BTCUSDT'].values\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1337f05f-b7e5-405a-abc0-7bab2099cabc",
   "metadata": {},
   "outputs": [],
   "source": [
    "supert"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30a8e2dd-c24c-4b25-8854-89ab4f256911",
   "metadata": {},
   "outputs": [],
   "source": [
    "superd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cd5eb5ad-cf92-47e8-9eba-3b222ce8a3c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "superl"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f87b9091-a8b8-49a5-a48d-d35f21d46461",
   "metadata": {},
   "outputs": [],
   "source": [
    "supers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "08d97bbe-5c1b-44fe-94bb-0ce136de5c1c",
   "metadata": {},
   "outputs": [],
   "source": [
    "pd.Series(supert, index=close.index)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1d9e597d-0cf8-415e-a199-eb55a4a513d5",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "faster_supertrend(\n",
    "    high['BTCUSDT'].values, \n",
    "    low['BTCUSDT'].values,\n",
    "    close['BTCUSDT'].values\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e52b679-5838-46ad-9e91-7674021261e0",
   "metadata": {},
   "source": [
    "### NumPy + Numba + TA-Lib"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f2de5960-18c0-4b21-a0a8-856738c14bc9",
   "metadata": {},
   "outputs": [],
   "source": [
    "import talib\n",
    "\n",
    "def faster_supertrend_talib(high, low, close, period=7, multiplier=3):\n",
    "    avg_price = talib.MEDPRICE(high, low)\n",
    "    atr = talib.ATR(high, low, close, period)\n",
    "    upper, lower = get_basic_bands(avg_price, atr, multiplier)\n",
    "    return get_final_bands_nb(close, upper, lower)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ee2ed2c-2feb-4ef4-a8b7-914cd837610d",
   "metadata": {},
   "outputs": [],
   "source": [
    "faster_supertrend_talib(\n",
    "    high['BTCUSDT'].values, \n",
    "    low['BTCUSDT'].values, \n",
    "    close['BTCUSDT'].values\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "92ff0625-ad73-44d7-8984-8103a702a03b",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "faster_supertrend_talib(\n",
    "    high['BTCUSDT'].values, \n",
    "    low['BTCUSDT'].values, \n",
    "    close['BTCUSDT'].values\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f2d1d3c-80f6-4bdf-972b-af349ec3e1ea",
   "metadata": {},
   "source": [
    "## Indicator factory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fcd4ca20-aa82-42f9-916b-7b9024890b14",
   "metadata": {},
   "outputs": [],
   "source": [
    "SuperTrend = vbt.IF(\n",
    "    class_name='SuperTrend',\n",
    "    short_name='st',\n",
    "    input_names=['high', 'low', 'close'],\n",
    "    param_names=['period', 'multiplier'],\n",
    "    output_names=['supert', 'superd', 'superl', 'supers']\n",
    ").with_apply_func(\n",
    "    faster_supertrend_talib, \n",
    "    takes_1d=True,\n",
    "    period=7, \n",
    "    multiplier=3\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c9b84719-41b9-4622-87a4-1afc4da1cafd",
   "metadata": {},
   "outputs": [],
   "source": [
    "help(SuperTrend.run)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98812dfa-3e5b-487f-9bb0-738ff2e9ffdf",
   "metadata": {},
   "outputs": [],
   "source": [
    "st = SuperTrend.run(high, low, close)\n",
    "print(st.supert)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "200ef4a9-cd6e-4f04-a279-0e4941933a8c",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "SuperTrend.run(high, low, close)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a038fda3-4789-4756-a298-f57a48cd2e99",
   "metadata": {},
   "source": [
    "### Using expressions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffcfb664-d323-49d2-8544-9696dd74a316",
   "metadata": {},
   "outputs": [],
   "source": [
    "expr = \"\"\"\n",
    "SuperTrend[st]:\n",
    "medprice = @talib_medprice(high, low)\n",
    "atr = @talib_atr(high, low, close, @p_period)\n",
    "upper, lower = get_basic_bands(medprice, atr, @p_multiplier)\n",
    "supert, superd, superl, supers = get_final_bands(close, upper, lower)\n",
    "supert, superd, superl, supers\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "522edc62-b6ad-4f1e-95f2-d8b871609b09",
   "metadata": {},
   "outputs": [],
   "source": [
    "SuperTrend = vbt.IF.from_expr(\n",
    "    expr, \n",
    "    takes_1d=True,\n",
    "    get_basic_bands=get_basic_bands,\n",
    "    get_final_bands=get_final_bands_nb,\n",
    "    period=7, \n",
    "    multiplier=3\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dcd6acd6-ee0e-4953-9bb0-223fbb0158f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "st = SuperTrend.run(high, low, close)\n",
    "print(st.supert)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3fcb34c2-d1cc-48e7-8eb4-fdd0a1a9b1bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "SuperTrend.run(high, low, close)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b834219-4c05-478a-b581-9bb6ec0db8fb",
   "metadata": {},
   "source": [
    "## Plot indicator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21bb38bf-b6da-42b1-ab48-039d9e75a16d",
   "metadata": {},
   "outputs": [],
   "source": [
    "class SuperTrend(SuperTrend):\n",
    "    def plot(self, \n",
    "             column=None, \n",
    "             close_kwargs=None,\n",
    "             superl_kwargs=None,\n",
    "             supers_kwargs=None,\n",
    "             fig=None, \n",
    "             **layout_kwargs):\n",
    "        close_kwargs = close_kwargs if close_kwargs else {}\n",
    "        superl_kwargs = superl_kwargs if superl_kwargs else {}\n",
    "        supers_kwargs = supers_kwargs if supers_kwargs else {}\n",
    "        \n",
    "        close = self.select_col_from_obj(self.close, column).rename('Close')\n",
    "        supers = self.select_col_from_obj(self.supers, column).rename('Short')\n",
    "        superl = self.select_col_from_obj(self.superl, column).rename('Long')\n",
    "        \n",
    "        fig = close.vbt.plot(fig=fig, **close_kwargs, **layout_kwargs)\n",
    "        supers.vbt.plot(fig=fig, **supers_kwargs)\n",
    "        superl.vbt.plot(fig=fig, **superl_kwargs)\n",
    "        \n",
    "        return fig"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99b0be24-8256-468a-bdaf-bd82467e4819",
   "metadata": {},
   "outputs": [],
   "source": [
    "st = SuperTrend.run(high, low, close)\n",
    "st.loc[date_range, 'BTCUSDT'].plot(\n",
    "    superl_kwargs=dict(trace_kwargs=dict(line_color='limegreen')),\n",
    "    supers_kwargs=dict(trace_kwargs=dict(line_color='red'))\n",
    ").show_svg()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d7956a9-a24c-4db1-871e-ef5f938e193e",
   "metadata": {},
   "source": [
    "## Test indicator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "df6dfa41-4206-4d61-a270-6d5f7fc77c2b",
   "metadata": {},
   "outputs": [],
   "source": [
    "entries = (~st.superl.isnull()).vbt.signals.fshift()\n",
    "exits = (~st.supers.isnull()).vbt.signals.fshift()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1cd2f65d-2682-4cfd-bbcf-54702bc115b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "pf = vbt.Portfolio.from_signals(\n",
    "    close=close, \n",
    "    entries=entries, \n",
    "    exits=exits, \n",
    "    fees=0.001, \n",
    "    freq='1h'\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "757609ba-f3ed-4c70-8e3c-9c6f0cdca05c",
   "metadata": {},
   "outputs": [],
   "source": [
    "pf['ETHUSDT'].stats()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "961fa4a3-74c3-4b90-9678-aaee56cf60af",
   "metadata": {},
   "source": [
    "### Optimization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a1b757f6-0c30-4102-8580-d6d82bf249f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "periods = np.arange(4, 20)\n",
    "multipliers = np.arange(20, 41) / 10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "65b4d9ed-2866-4044-8023-829d5e9b0f30",
   "metadata": {},
   "outputs": [],
   "source": [
    "st = SuperTrend.run(\n",
    "    high, low, close, \n",
    "    period=periods, \n",
    "    multiplier=multipliers,\n",
    "    param_product=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "00adab7e-1ac1-4aca-a2b5-e00926381009",
   "metadata": {},
   "outputs": [],
   "source": [
    "st.wrapper.columns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bcea7660-bf82-4ea2-ae3d-6407914167bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "st.loc[date_range, (19, 4, 'ETHUSDT')].plot().show_svg()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c1ed2c21-e61c-49e1-8b08-1324380ca566",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(st.getsize())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4cc44d67-cabe-41e5-9d4b-c37527ff6dbb",
   "metadata": {},
   "outputs": [],
   "source": [
    "input_size = st.wrapper.shape[0] * st.wrapper.shape[1]\n",
    "n_outputs = 4\n",
    "data_type_size = 8\n",
    "input_size * n_outputs * data_type_size / 1024 / 1024"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5b150641-4aa6-4634-98a9-8bd698448f4c",
   "metadata": {},
   "outputs": [],
   "source": [
    "entries = (~st.superl.isnull()).vbt.signals.fshift()\n",
    "exits = (~st.supers.isnull()).vbt.signals.fshift()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3f5a43d0-9b85-4347-9bb9-f0e9b096d143",
   "metadata": {},
   "outputs": [],
   "source": [
    "pf = vbt.Portfolio.from_signals(close, entries, exits, fees=0.001, freq='1h')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4a9155ae-4971-4009-b1dc-607dc4116fed",
   "metadata": {},
   "outputs": [],
   "source": [
    "pf.sharpe_ratio.vbt.heatmap(\n",
    "    x_level='st_period', \n",
    "    y_level='st_multiplier',\n",
    "    slider_level='symbol'\n",
    ").show_svg()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "855fe67d-6c43-4a9a-9e03-df18d4d2e77c",
   "metadata": {},
   "outputs": [],
   "source": [
    "vbt.Portfolio.from_holding(close, freq='1h').sharpe_ratio"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25a4b932-81a0-42b5-98b5-f58988319952",
   "metadata": {},
   "source": [
    "## Streaming"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3a132681-34a2-4a7d-ab67-5cf72645fd7c",
   "metadata": {},
   "outputs": [],
   "source": [
    "class SuperTrendAIS(tp.NamedTuple):\n",
    "    i: int\n",
    "    high: float\n",
    "    low: float\n",
    "    close: float\n",
    "    prev_close: float\n",
    "    prev_upper: float\n",
    "    prev_lower: float\n",
    "    prev_dir_: float\n",
    "    nobs: int\n",
    "    weighted_avg: float\n",
    "    old_wt: float\n",
    "    period: int\n",
    "    multiplier: float\n",
    "    \n",
    "class SuperTrendAOS(tp.NamedTuple):\n",
    "    nobs: int\n",
    "    weighted_avg: float\n",
    "    old_wt: float\n",
    "    upper: float\n",
    "    lower: float\n",
    "    trend: float\n",
    "    dir_: float\n",
    "    long: float\n",
    "    short: float"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6dca1372-1c55-48f3-b569-7a193142fff6",
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit(nogil=True)\n",
    "def get_tr_one_nb(high, low, prev_close):\n",
    "    tr0 = abs(high - low)\n",
    "    tr1 = abs(high - prev_close)\n",
    "    tr2 = abs(low - prev_close)\n",
    "    if np.isnan(tr0) or np.isnan(tr1) or np.isnan(tr2):\n",
    "        tr = np.nan\n",
    "    else:\n",
    "        tr = max(tr0, tr1, tr2)\n",
    "    return tr\n",
    "\n",
    "@njit(nogil=True)\n",
    "def get_med_price_one_nb(high, low):\n",
    "    return (high + low) / 2\n",
    "\n",
    "@njit(nogil=True)\n",
    "def get_basic_bands_one_nb(high, low, atr, multiplier):\n",
    "    med_price = get_med_price_one_nb(high, low)\n",
    "    matr = multiplier * atr\n",
    "    upper = med_price + matr\n",
    "    lower = med_price - matr\n",
    "    return upper, lower\n",
    "    \n",
    "@njit(nogil=True)\n",
    "def get_final_bands_one_nb(close, upper, lower, prev_upper, prev_lower, prev_dir_):\n",
    "    if close > prev_upper:\n",
    "        dir_ = 1\n",
    "    elif close < prev_lower:\n",
    "        dir_ = -1\n",
    "    else:\n",
    "        dir_ = prev_dir_\n",
    "        if dir_ > 0 and lower < prev_lower:\n",
    "            lower = prev_lower\n",
    "        if dir_ < 0 and upper > prev_upper:\n",
    "            upper = prev_upper\n",
    "\n",
    "    if dir_ > 0:\n",
    "        trend = long = lower\n",
    "        short = np.nan\n",
    "    else:\n",
    "        trend = short = upper\n",
    "        long = np.nan\n",
    "    return upper, lower, trend, dir_, long, short"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e787868d-99b9-41ea-ad97-e70480e021e8",
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit(nogil=True)\n",
    "def superfast_supertrend_acc_nb(in_state):\n",
    "    i = in_state.i\n",
    "    high = in_state.high\n",
    "    low = in_state.low\n",
    "    close = in_state.close\n",
    "    prev_close = in_state.prev_close\n",
    "    prev_upper = in_state.prev_upper\n",
    "    prev_lower = in_state.prev_lower\n",
    "    prev_dir_ = in_state.prev_dir_\n",
    "    nobs = in_state.nobs\n",
    "    weighted_avg = in_state.weighted_avg\n",
    "    old_wt = in_state.old_wt\n",
    "    period = in_state.period\n",
    "    multiplier = in_state.multiplier\n",
    "    \n",
    "    tr = get_tr_one_nb(high, low, prev_close)\n",
    "\n",
    "    alpha = vbt.nb.alpha_from_wilder_nb(period)\n",
    "    ewm_mean_in_state = vbt.nb.EWMMeanAIS(\n",
    "        i=i,\n",
    "        value=tr,\n",
    "        old_wt=old_wt,\n",
    "        weighted_avg=weighted_avg,\n",
    "        nobs=nobs,\n",
    "        alpha=alpha,\n",
    "        minp=period,\n",
    "        adjust=False\n",
    "    )\n",
    "    ewm_mean_out_state = vbt.nb.ewm_mean_acc_nb(ewm_mean_in_state)\n",
    "    atr = ewm_mean_out_state.value\n",
    "    \n",
    "    upper, lower = get_basic_bands_one_nb(high, low, atr, multiplier)\n",
    "    \n",
    "    if i == 0:\n",
    "        trend, dir_, long, short = np.nan, 1, np.nan, np.nan\n",
    "    else:\n",
    "        upper, lower, trend, dir_, long, short = get_final_bands_one_nb(\n",
    "            close, upper, lower, prev_upper, prev_lower, prev_dir_)\n",
    "            \n",
    "    return SuperTrendAOS(\n",
    "        nobs=ewm_mean_out_state.nobs,\n",
    "        weighted_avg=ewm_mean_out_state.weighted_avg,\n",
    "        old_wt=ewm_mean_out_state.old_wt,\n",
    "        upper=upper,\n",
    "        lower=lower,\n",
    "        trend=trend,\n",
    "        dir_=dir_,\n",
    "        long=long,\n",
    "        short=short\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1d5a76d7-f981-4e64-bf5f-9cd69046cfb2",
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit(nogil=True)\n",
    "def superfast_supertrend_nb(high, low, close, period=7, multiplier=3):\n",
    "    trend = np.empty(close.shape, dtype=np.float_)\n",
    "    dir_ = np.empty(close.shape, dtype=np.int_)\n",
    "    long = np.empty(close.shape, dtype=np.float_)\n",
    "    short = np.empty(close.shape, dtype=np.float_)\n",
    "    \n",
    "    if close.shape[0] == 0:\n",
    "        return trend, dir_, long, short\n",
    "\n",
    "    nobs = 0\n",
    "    old_wt = 1.\n",
    "    weighted_avg = np.nan\n",
    "    prev_upper = np.nan\n",
    "    prev_lower = np.nan\n",
    "\n",
    "    for i in range(close.shape[0]):\n",
    "        in_state = SuperTrendAIS(\n",
    "            i=i,\n",
    "            high=high[i],\n",
    "            low=low[i],\n",
    "            close=close[i],\n",
    "            prev_close=close[i - 1] if i > 0 else np.nan,\n",
    "            prev_upper=prev_upper,\n",
    "            prev_lower=prev_lower,\n",
    "            prev_dir_=dir_[i - 1] if i > 0 else 1,\n",
    "            nobs=nobs,\n",
    "            weighted_avg=weighted_avg,\n",
    "            old_wt=old_wt,\n",
    "            period=period,\n",
    "            multiplier=multiplier\n",
    "        )\n",
    "        \n",
    "        out_state = superfast_supertrend_acc_nb(in_state)\n",
    "        \n",
    "        nobs = out_state.nobs\n",
    "        weighted_avg = out_state.weighted_avg\n",
    "        old_wt = out_state.old_wt\n",
    "        prev_upper = out_state.upper\n",
    "        prev_lower = out_state.lower\n",
    "        trend[i] = out_state.trend\n",
    "        dir_[i] = out_state.dir_\n",
    "        long[i] = out_state.long\n",
    "        short[i] = out_state.short\n",
    "        \n",
    "    return trend, dir_, long, short"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a16e5ed-f62c-4523-a5e1-6b75e6af9a7b",
   "metadata": {},
   "outputs": [],
   "source": [
    "superfast_out = superfast_supertrend_nb(\n",
    "    high['BTCUSDT'].values,\n",
    "    low['BTCUSDT'].values,\n",
    "    close['BTCUSDT'].values\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "94757fa3-9008-4320-b66f-c72c94ab19b6",
   "metadata": {},
   "outputs": [],
   "source": [
    "faster_out = faster_supertrend(\n",
    "    high['BTCUSDT'].values,\n",
    "    low['BTCUSDT'].values,\n",
    "    close['BTCUSDT'].values\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f0733554-9a70-4ebb-9962-7ac87ddb63b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "np.testing.assert_array_equal(superfast_out[0], faster_out[0])\n",
    "np.testing.assert_array_equal(superfast_out[1], faster_out[1])\n",
    "np.testing.assert_array_equal(superfast_out[2], faster_out[2])\n",
    "np.testing.assert_array_equal(superfast_out[3], faster_out[3])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1cc3b596-f88e-4222-947f-2fb1ca0e879c",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "superfast_supertrend_nb(\n",
    "    high['BTCUSDT'].values, \n",
    "    low['BTCUSDT'].values, \n",
    "    close['BTCUSDT'].values\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "08682671-abae-4f68-98d6-da623ed67c7d",
   "metadata": {},
   "source": [
    "## Multithreading"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "da0c1330-ee15-409c-a57a-a2d95facaa3c",
   "metadata": {},
   "outputs": [],
   "source": [
    "SuperTrend = vbt.IF(\n",
    "    class_name='SuperTrend',\n",
    "    short_name='st',\n",
    "    input_names=['high', 'low', 'close'],\n",
    "    param_names=['period', 'multiplier'],\n",
    "    output_names=['supert', 'superd', 'superl', 'supers']\n",
    ").with_apply_func(\n",
    "    superfast_supertrend_nb, \n",
    "    takes_1d=True,\n",
    "    period=7, \n",
    "    multiplier=3\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "72fbbf4b-107f-4efd-9a13-01b3230b8ec0",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "SuperTrend.run(high, low, close)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "01592b3c-cba0-4007-82d5-0026ade663c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "SuperTrend.run(\n",
    "    high, low, close, \n",
    "    period=periods, \n",
    "    multiplier=multipliers,\n",
    "    param_product=True,\n",
    "    execute_kwargs=dict(show_progress=False)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ee578ba8-c32c-4c4e-9da7-e39a92bfcb7e",
   "metadata": {},
   "outputs": [],
   "source": [
    "270 / 336 / 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "527a2a56-f5e8-4897-8288-40edeb023639",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "SuperTrend.run(\n",
    "    high, low, close, \n",
    "    period=periods, \n",
    "    multiplier=multipliers,\n",
    "    param_product=True,\n",
    "    execute_kwargs=dict(\n",
    "        engine='dask', \n",
    "        chunk_len='auto', \n",
    "        show_progress=False\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac158ed3-1c7c-498f-843d-09a6d02439c4",
   "metadata": {},
   "source": [
    "## Pipelines"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c5bcfaed-2d4c-4929-8eae-3505d682577d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def pipeline(data, period=7, multiplier=3):\n",
    "    high = data.get('High')\n",
    "    low = data.get('Low')\n",
    "    close = data.get('Close')\n",
    "    st = SuperTrend.run(\n",
    "        high, \n",
    "        low, \n",
    "        close, \n",
    "        period=period, \n",
    "        multiplier=multiplier\n",
    "    )\n",
    "    entries = (~st.superl.isnull()).vbt.signals.fshift()\n",
    "    exits = (~st.supers.isnull()).vbt.signals.fshift()\n",
    "    pf = vbt.Portfolio.from_signals(\n",
    "        close, \n",
    "        entries=entries, \n",
    "        exits=exits, \n",
    "        fees=0.001,\n",
    "        save_returns=True,\n",
    "        max_order_records=0,\n",
    "        freq='1h'\n",
    "    )\n",
    "    return pf.sharpe_ratio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e57ec5e5-71a3-46fa-a8d1-edc9d77876f3",
   "metadata": {},
   "outputs": [],
   "source": [
    "pipeline(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c87e3133-9238-4cf6-8fb2-518b36e1962d",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "pipeline(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3c94c2a-7c02-484f-9b5b-83892cc0d2e0",
   "metadata": {},
   "outputs": [],
   "source": [
    "336 * 32"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b23cfc27-81f4-43d6-9a3c-3e2136db980c",
   "metadata": {},
   "outputs": [],
   "source": [
    "op_tree = (product, periods, multipliers)\n",
    "period_product, multiplier_product = vbt.generate_param_combs(op_tree)\n",
    "period_product = np.asarray(period_product)\n",
    "multiplier_product = np.asarray(multiplier_product)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0c220d9-bd6f-48c5-a794-f02d6a6e3eeb",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "pipeline(data, period_product, multiplier_product)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52529de0-d7a9-4e4e-9a91-4c2449f3405f",
   "metadata": {},
   "source": [
    "### Chunked pipeline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d8413e9c-5fe1-4339-a289-f9d1f7595ce6",
   "metadata": {},
   "outputs": [],
   "source": [
    "chunked_pipeline = vbt.chunked(\n",
    "    size=vbt.LenSizer(arg_query='period', single_type=int),\n",
    "    arg_take_spec=dict(\n",
    "        data=None,\n",
    "        period=vbt.ChunkSlicer(),\n",
    "        multiplier=vbt.ChunkSlicer()\n",
    "    ),\n",
    "    merge_func=lambda x: pd.concat(x).sort_index()\n",
    ")(pipeline)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb15c6bc-d73e-4449-bb98-d43432dc9dad",
   "metadata": {},
   "outputs": [],
   "source": [
    "chunked_pipeline(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "728cadd0-a62f-49bb-8c86-c24df3642ad2",
   "metadata": {},
   "outputs": [],
   "source": [
    "chunked_pipeline(\n",
    "    data, \n",
    "    period_product[:4], \n",
    "    multiplier_product[:4],\n",
    "    _n_chunks=2,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bc3206ff-4b53-459c-91cd-7183193d76f2",
   "metadata": {},
   "outputs": [],
   "source": [
    "chunk_meta, tasks = chunked_pipeline(\n",
    "    data, \n",
    "    period_product[:4], \n",
    "    multiplier_product[:4],\n",
    "    _n_chunks=2,\n",
    "    _return_raw_chunks=True\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fc340146-43ee-40a2-8bb2-60494469d39e",
   "metadata": {},
   "outputs": [],
   "source": [
    "chunk_meta"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f734466d-59f9-49c6-ae2b-b3ff038833df",
   "metadata": {},
   "outputs": [],
   "source": [
    "list(tasks)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d279086d-743e-4044-9fe7-1d46e74d04e4",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "chunked_pipeline(data, period_product, multiplier_product)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5125c3af-b330-47a2-a24d-49e58d37e471",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "chunked_pipeline(data, period_product, multiplier_product, _chunk_len=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e6c55bd-6e54-4ebc-bb9a-9f2a2805403d",
   "metadata": {},
   "source": [
    "### Numba pipeline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d8830575-46de-4884-a278-214c258ecc00",
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit(nogil=True)\n",
    "def pipeline_nb(high, low, close, periods=np.array([7]), multipliers=np.array([3]), ann_factor=365):\n",
    "    sharpe = np.empty(periods.size * close.shape[1], dtype=np.float_)\n",
    "    long_entries = np.empty(close.shape, dtype=np.bool_)\n",
    "    long_exits = np.empty(close.shape, dtype=np.bool_)\n",
    "    group_lens = np.full(close.shape[1], 1)\n",
    "    init_cash = 100.\n",
    "    fees = 0.001\n",
    "    k = 0\n",
    "    \n",
    "    for i in range(periods.size):\n",
    "        for col in range(close.shape[1]):\n",
    "            _, _, superl, supers = superfast_supertrend_nb(\n",
    "                high[:, col], \n",
    "                low[:, col], \n",
    "                close[:, col], \n",
    "                periods[i], \n",
    "                multipliers[i]\n",
    "            )\n",
    "            long_entries[:, col] = vbt.nb.fshift_1d_nb(~np.isnan(superl), fill_value=False)\n",
    "            long_exits[:, col] = vbt.nb.fshift_1d_nb(~np.isnan(supers), fill_value=False)\n",
    "            \n",
    "        sim_out = vbt.pf_nb.from_signals_nb(\n",
    "            target_shape=close.shape,\n",
    "            group_lens=group_lens,\n",
    "            init_cash=init_cash,\n",
    "            high=high,\n",
    "            low=low,\n",
    "            close=close,\n",
    "            long_entries=long_entries,\n",
    "            long_exits=long_exits,\n",
    "            fees=fees,\n",
    "            save_returns=True\n",
    "        )\n",
    "        returns = sim_out.in_outputs.returns\n",
    "        sharpe[k:k + close.shape[1]] = vbt.ret_nb.sharpe_ratio_nb(returns, ann_factor, ddof=1)\n",
    "        k += close.shape[1]\n",
    "        \n",
    "    return sharpe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2eabd3cf-5f03-4cbb-8b1d-6bba6bcbbd3d",
   "metadata": {},
   "outputs": [],
   "source": [
    "ann_factor = vbt.pd_acc.returns.get_ann_factor(freq='1h')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d5a82762-332a-4b38-9739-0da01d06a02d",
   "metadata": {},
   "outputs": [],
   "source": [
    "pipeline_nb(\n",
    "    high.values, \n",
    "    low.values, \n",
    "    close.values,\n",
    "    ann_factor=ann_factor\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cd786362-5c17-4d9a-a8b7-77604c47942f",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "pipeline_nb(\n",
    "    high.values, \n",
    "    low.values, \n",
    "    close.values,\n",
    "    ann_factor=ann_factor\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1796f5ea-c0de-4249-98e5-ce14c550108f",
   "metadata": {},
   "outputs": [],
   "source": [
    "def merge_func(arrs, ann_args, input_columns):\n",
    "    arr = np.concatenate(arrs)\n",
    "    param_index = vbt.stack_indexes((\n",
    "        pd.Index(ann_args['periods']['value'], name='st_period'),\n",
    "        pd.Index(ann_args['multipliers']['value'], name='st_multiplier')\n",
    "    ))\n",
    "    index = vbt.combine_indexes((\n",
    "        param_index,\n",
    "        input_columns\n",
    "    ))\n",
    "    return pd.Series(arr, index=index)\n",
    "\n",
    "nb_chunked = vbt.chunked(\n",
    "    size=vbt.ArraySizer(arg_query='periods', axis=0),\n",
    "    arg_take_spec=dict(\n",
    "        high=None,\n",
    "        low=None,\n",
    "        close=None,\n",
    "        periods=vbt.ArraySlicer(axis=0),\n",
    "        multipliers=vbt.ArraySlicer(axis=0),\n",
    "        ann_factor=None\n",
    "    ),\n",
    "    merge_func=merge_func,\n",
    "    merge_kwargs=dict(\n",
    "        ann_args=vbt.Rep(\"ann_args\")\n",
    "    )\n",
    ")\n",
    "chunked_pipeline_nb = nb_chunked(pipeline_nb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f0a9058-bc57-4331-b9e5-31fd511c8862",
   "metadata": {},
   "outputs": [],
   "source": [
    "chunked_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values,\n",
    "    close.values,\n",
    "    periods=period_product[:4], \n",
    "    multipliers=multiplier_product[:4],\n",
    "    ann_factor=ann_factor,\n",
    "    _n_chunks=2,\n",
    "    _merge_kwargs=dict(input_columns=close.columns)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "badba6cb-3405-498d-b366-cd9205721541",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "chunked_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values, \n",
    "    close.values,\n",
    "    periods=period_product, \n",
    "    multipliers=multiplier_product,\n",
    "    ann_factor=ann_factor,\n",
    "    _merge_kwargs=dict(input_columns=close.columns)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c5208092-e88f-428a-a2da-f43d325ab20e",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "chunked_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values, \n",
    "    close.values,\n",
    "    periods=period_product, \n",
    "    multipliers=multiplier_product,\n",
    "    ann_factor=ann_factor,\n",
    "    _execute_kwargs=dict(engine='dask'),\n",
    "    _merge_kwargs=dict(input_columns=close.columns)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b03f9632-ebfd-4073-a80b-a1671a3d3361",
   "metadata": {},
   "source": [
    "### Contextualized pipeline"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3eded1d5-1a4a-410c-96ab-3f715be479a1",
   "metadata": {},
   "source": [
    "#### Streaming Sharpe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "03f8b212-4ae3-404e-90fe-4c44a867e556",
   "metadata": {},
   "outputs": [],
   "source": [
    "class RollSharpeAIS(tp.NamedTuple):\n",
    "    i: int\n",
    "    ret: float\n",
    "    pre_window_ret: float\n",
    "    cumsum: float\n",
    "    cumsum_sq: float\n",
    "    nancnt: int\n",
    "    window: int\n",
    "    minp: tp.Optional[int]\n",
    "    ddof: int\n",
    "    ann_factor: float\n",
    "    \n",
    "class RollSharpeAOS(tp.NamedTuple):\n",
    "    cumsum: float\n",
    "    cumsum_sq: float\n",
    "    nancnt: int\n",
    "    value: float\n",
    "\n",
    "@njit(nogil=True)\n",
    "def rolling_sharpe_acc_nb(in_state):\n",
    "    mean_in_state = vbt.nb.RollMeanAIS(\n",
    "        i=in_state.i,\n",
    "        value=in_state.ret,\n",
    "        pre_window_value=in_state.pre_window_ret,\n",
    "        cumsum=in_state.cumsum,\n",
    "        nancnt=in_state.nancnt,\n",
    "        window=in_state.window,\n",
    "        minp=in_state.minp\n",
    "    )\n",
    "    mean_out_state = vbt.nb.rolling_mean_acc_nb(mean_in_state)\n",
    "    \n",
    "    std_in_state = vbt.nb.RollStdAIS(\n",
    "        i=in_state.i,\n",
    "        value=in_state.ret,\n",
    "        pre_window_value=in_state.pre_window_ret,\n",
    "        cumsum=in_state.cumsum,\n",
    "        cumsum_sq=in_state.cumsum_sq,\n",
    "        nancnt=in_state.nancnt,\n",
    "        window=in_state.window,\n",
    "        minp=in_state.minp,\n",
    "        ddof=in_state.ddof\n",
    "    )\n",
    "    std_out_state = vbt.nb.rolling_std_acc_nb(std_in_state)\n",
    "    \n",
    "    mean = mean_out_state.value\n",
    "    std = std_out_state.value\n",
    "    if std == 0:\n",
    "        sharpe = np.nan\n",
    "    else:\n",
    "        sharpe = mean / std * np.sqrt(in_state.ann_factor)\n",
    "    return RollSharpeAOS(\n",
    "        cumsum=std_out_state.cumsum,\n",
    "        cumsum_sq=std_out_state.cumsum_sq,\n",
    "        nancnt=std_out_state.nancnt,\n",
    "        value=sharpe\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1339d8a8-c186-4fea-abc3-fd926b17149d",
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit(nogil=True)\n",
    "def rolling_sharpe_ratio_nb(returns, window, minp=None, ddof=0, ann_factor=365):\n",
    "    if window is None:\n",
    "        window = returns.shape[0]\n",
    "    if minp is None:\n",
    "        minp = window\n",
    "    out = np.empty(returns.shape, dtype=np.float_)\n",
    "    \n",
    "    if returns.shape[0] == 0:\n",
    "        return out\n",
    "\n",
    "    cumsum = 0.\n",
    "    cumsum_sq = 0.\n",
    "    nancnt = 0\n",
    "\n",
    "    for i in range(returns.shape[0]):\n",
    "        in_state = RollSharpeAIS(\n",
    "            i=i,\n",
    "            ret=returns[i],\n",
    "            pre_window_ret=returns[i - window] if i - window >= 0 else np.nan,\n",
    "            cumsum=cumsum,\n",
    "            cumsum_sq=cumsum_sq,\n",
    "            nancnt=nancnt,\n",
    "            window=window,\n",
    "            minp=minp,\n",
    "            ddof=ddof,\n",
    "            ann_factor=ann_factor\n",
    "        )\n",
    "        \n",
    "        out_state = rolling_sharpe_acc_nb(in_state)\n",
    "        \n",
    "        cumsum = out_state.cumsum\n",
    "        cumsum_sq = out_state.cumsum_sq\n",
    "        nancnt = out_state.nancnt\n",
    "        out[i] = out_state.value\n",
    "        \n",
    "    return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "66ff280b-0057-4095-b473-ceee2ee1e0f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "returns = close['BTCUSDT'].vbt.to_returns()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "084c24fc-53b0-4d85-a184-0d3a8503cd00",
   "metadata": {},
   "outputs": [],
   "source": [
    "np.testing.assert_allclose(\n",
    "    rolling_sharpe_ratio_nb(\n",
    "        returns=returns.values, \n",
    "        window=10, \n",
    "        ddof=1, \n",
    "        ann_factor=ann_factor),\n",
    "    returns.vbt.returns(freq='1h').rolling_sharpe_ratio(10).values\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47c7edb2-f5de-4d7f-99a7-a35e5dcdec46",
   "metadata": {},
   "source": [
    "#### Callbacks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "28fba511-1030-4c6b-8592-5a965b8b6b1e",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Memory(tp.NamedTuple):\n",
    "    nobs: tp.Array1d\n",
    "    old_wt: tp.Array1d\n",
    "    weighted_avg: tp.Array1d\n",
    "    prev_upper: tp.Array1d\n",
    "    prev_lower: tp.Array1d\n",
    "    prev_dir_: tp.Array1d\n",
    "    cumsum: tp.Array1d\n",
    "    cumsum_sq: tp.Array1d\n",
    "    nancnt: tp.Array1d\n",
    "    was_entry: tp.Array1d\n",
    "    was_exit: tp.Array1d\n",
    "\n",
    "@njit(nogil=True)\n",
    "def pre_sim_func_nb(c):\n",
    "    memory = Memory(\n",
    "        nobs=np.full(c.target_shape[1], 0, dtype=np.int_),\n",
    "        old_wt=np.full(c.target_shape[1], 1., dtype=np.float_),\n",
    "        weighted_avg=np.full(c.target_shape[1], np.nan, dtype=np.float_),\n",
    "        prev_upper=np.full(c.target_shape[1], np.nan, dtype=np.float_),\n",
    "        prev_lower=np.full(c.target_shape[1], np.nan, dtype=np.float_),\n",
    "        prev_dir_=np.full(c.target_shape[1], np.nan, dtype=np.float_),\n",
    "        cumsum=np.full(c.target_shape[1], 0., dtype=np.float_),\n",
    "        cumsum_sq=np.full(c.target_shape[1], 0., dtype=np.float_),\n",
    "        nancnt=np.full(c.target_shape[1], 0, dtype=np.int_),\n",
    "        was_entry=np.full(c.target_shape[1], False, dtype=np.bool_),\n",
    "        was_exit=np.full(c.target_shape[1], False, dtype=np.bool_)\n",
    "    )\n",
    "    return (memory,)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5a3f8ffd-82d1-4b86-80c5-0d9d4e9a5680",
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit(nogil=True)\n",
    "def order_func_nb(c, memory, period, multiplier):\n",
    "    is_entry = memory.was_entry[c.col]\n",
    "    is_exit = memory.was_exit[c.col]\n",
    "    \n",
    "    in_state = SuperTrendAIS(\n",
    "        i=c.i,\n",
    "        high=c.high[c.i, c.col],\n",
    "        low=c.low[c.i, c.col],\n",
    "        close=c.close[c.i, c.col],\n",
    "        prev_close=c.close[c.i - 1, c.col] if c.i > 0 else np.nan,\n",
    "        prev_upper=memory.prev_upper[c.col],\n",
    "        prev_lower=memory.prev_lower[c.col],\n",
    "        prev_dir_=memory.prev_dir_[c.col],\n",
    "        nobs=memory.nobs[c.col],\n",
    "        weighted_avg=memory.weighted_avg[c.col],\n",
    "        old_wt=memory.old_wt[c.col],\n",
    "        period=period,\n",
    "        multiplier=multiplier\n",
    "    )\n",
    "\n",
    "    out_state = superfast_supertrend_acc_nb(in_state)\n",
    "\n",
    "    memory.nobs[c.col] = out_state.nobs\n",
    "    memory.weighted_avg[c.col] = out_state.weighted_avg\n",
    "    memory.old_wt[c.col] = out_state.old_wt\n",
    "    memory.prev_upper[c.col] = out_state.upper\n",
    "    memory.prev_lower[c.col] = out_state.lower\n",
    "    memory.prev_dir_[c.col] = out_state.dir_\n",
    "    memory.was_entry[c.col] = not np.isnan(out_state.long)\n",
    "    memory.was_exit[c.col] = not np.isnan(out_state.short)\n",
    "    \n",
    "    in_position = c.position_now > 0\n",
    "    if is_entry and not in_position:\n",
    "        size = np.inf\n",
    "    elif is_exit and in_position:\n",
    "        size = -np.inf\n",
    "    else:\n",
    "        size = 0.\n",
    "    return vbt.pf_nb.order_nb(\n",
    "        size=size, \n",
    "        direction=vbt.pf_enums.Direction.LongOnly,\n",
    "        fees=0.001\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73e93b02-3a10-4143-abf4-24ec71afb42f",
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit(nogil=True)\n",
    "def post_segment_func_nb(c, memory, ann_factor):\n",
    "    for col in range(c.from_col, c.to_col):\n",
    "        in_state = RollSharpeAIS(\n",
    "            i=c.i,\n",
    "            ret=c.last_return[col],\n",
    "            pre_window_ret=np.nan,\n",
    "            cumsum=memory.cumsum[col],\n",
    "            cumsum_sq=memory.cumsum_sq[col],\n",
    "            nancnt=memory.nancnt[col],\n",
    "            window=c.i + 1,\n",
    "            minp=0,\n",
    "            ddof=1,\n",
    "            ann_factor=ann_factor\n",
    "        )\n",
    "        out_state = rolling_sharpe_acc_nb(in_state)\n",
    "        memory.cumsum[col] = out_state.cumsum\n",
    "        memory.cumsum_sq[col] = out_state.cumsum_sq\n",
    "        memory.nancnt[col] = out_state.nancnt\n",
    "        c.in_outputs.sharpe[col] = out_state.value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fe428781-32c6-4be7-a65a-97827372a31f",
   "metadata": {},
   "outputs": [],
   "source": [
    "class InOutputs(tp.NamedTuple):\n",
    "    sharpe: tp.Array1d\n",
    "\n",
    "@njit(nogil=True)\n",
    "def ctx_pipeline_nb(high, low, close, periods=np.array([7]), multipliers=np.array([3]), ann_factor=365):\n",
    "    in_outputs = InOutputs(sharpe=np.empty(close.shape[1], dtype=np.float_))\n",
    "    sharpe = np.empty(periods.size * close.shape[1], dtype=np.float_)\n",
    "    group_lens = np.full(close.shape[1], 1)\n",
    "    init_cash = 100.\n",
    "    k = 0\n",
    "    \n",
    "    for i in range(periods.size):\n",
    "        sim_out = vbt.pf_nb.from_order_func_nb(\n",
    "            target_shape=close.shape,\n",
    "            group_lens=group_lens,\n",
    "            cash_sharing=False,\n",
    "            init_cash=init_cash,\n",
    "            pre_sim_func_nb=pre_sim_func_nb,\n",
    "            order_func_nb=order_func_nb,\n",
    "            order_args=(periods[i], multipliers[i]),\n",
    "            post_segment_func_nb=post_segment_func_nb,\n",
    "            post_segment_args=(ann_factor,),\n",
    "            high=high,\n",
    "            low=low,\n",
    "            close=close,\n",
    "            in_outputs=in_outputs,\n",
    "            fill_pos_info=False,\n",
    "            max_order_records=0\n",
    "        )\n",
    "        sharpe[k:k + close.shape[1]] = in_outputs.sharpe\n",
    "        k += close.shape[1]\n",
    "        \n",
    "    return sharpe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f3a0e59c-b063-442b-bf9c-9007cabac426",
   "metadata": {},
   "outputs": [],
   "source": [
    "ctx_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values, \n",
    "    close.values,\n",
    "    ann_factor=ann_factor\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2876b2d3-c306-4340-80ea-1106d9953f95",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "ctx_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values, \n",
    "    close.values,\n",
    "    ann_factor=ann_factor\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3c85d1df-5094-4664-888e-90f31e0727b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "chunked_ctx_pipeline_nb = nb_chunked(ctx_pipeline_nb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3d4ed0b0-975e-4367-b17c-d233142dddb1",
   "metadata": {},
   "outputs": [],
   "source": [
    "chunked_ctx_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values,\n",
    "    close.values,\n",
    "    periods=period_product[:4], \n",
    "    multipliers=multiplier_product[:4],\n",
    "    ann_factor=ann_factor,\n",
    "    _n_chunks=2,\n",
    "    _merge_kwargs=dict(input_columns=close.columns)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5e837057-bfbf-404f-916a-fb1826cc911a",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "chunked_ctx_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values, \n",
    "    close.values,\n",
    "    periods=period_product, \n",
    "    multipliers=multiplier_product,\n",
    "    ann_factor=ann_factor,\n",
    "    _merge_kwargs=dict(input_columns=close.columns)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "03d97ac5-d061-4b8c-a45e-857c21e7b724",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "chunked_ctx_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values, \n",
    "    close.values,\n",
    "    periods=period_product, \n",
    "    multipliers=multiplier_product,\n",
    "    ann_factor=ann_factor,\n",
    "    _execute_kwargs=dict(engine='dask'),\n",
    "    _merge_kwargs=dict(input_columns=close.columns)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0d07ec5-9650-4db1-a3cd-def9a0950af2",
   "metadata": {},
   "source": [
    "### Bonus: Own simulator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "56dcbce3-1df4-4577-8a49-122c231f309f",
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit(nogil=True)\n",
    "def raw_pipeline_nb(high, low, close, periods=np.array([7]), multipliers=np.array([3]), ann_factor=365):\n",
    "    out = np.empty(periods.size * close.shape[1], dtype=np.float_)\n",
    "    \n",
    "    if close.shape[0] == 0:\n",
    "        return out\n",
    "\n",
    "    for k in range(len(periods)):\n",
    "        \n",
    "        for col in range(close.shape[1]):\n",
    "            nobs = 0\n",
    "            old_wt = 1.\n",
    "            weighted_avg = np.nan\n",
    "            prev_close_ = np.nan\n",
    "            prev_upper = np.nan\n",
    "            prev_lower = np.nan\n",
    "            prev_dir_ = 1\n",
    "            cumsum = 0.\n",
    "            cumsum_sq = 0.\n",
    "            nancnt = 0\n",
    "            was_entry = False\n",
    "            was_exit = False\n",
    "\n",
    "            init_cash = 100.\n",
    "            cash = init_cash\n",
    "            position = 0.\n",
    "            debt = 0.\n",
    "            locked_cash = 0.\n",
    "            free_cash = init_cash\n",
    "            val_price = np.nan\n",
    "            value = init_cash\n",
    "            prev_value = init_cash\n",
    "            return_ = 0.\n",
    "\n",
    "            for i in range(close.shape[0]):\n",
    "                is_entry = was_entry\n",
    "                is_exit = was_exit\n",
    "\n",
    "                st_in_state = SuperTrendAIS(\n",
    "                    i=i,\n",
    "                    high=high[i, col],\n",
    "                    low=low[i, col],\n",
    "                    close=close[i, col],\n",
    "                    prev_close=prev_close_,\n",
    "                    prev_upper=prev_upper,\n",
    "                    prev_lower=prev_lower,\n",
    "                    prev_dir_=prev_dir_,\n",
    "                    nobs=nobs,\n",
    "                    weighted_avg=weighted_avg,\n",
    "                    old_wt=old_wt,\n",
    "                    period=periods[k],\n",
    "                    multiplier=multipliers[k]\n",
    "                )\n",
    "\n",
    "                st_out_state = superfast_supertrend_acc_nb(st_in_state)\n",
    "\n",
    "                nobs = st_out_state.nobs\n",
    "                weighted_avg = st_out_state.weighted_avg\n",
    "                old_wt = st_out_state.old_wt\n",
    "                prev_close_ = close[i, col]\n",
    "                prev_upper = st_out_state.upper\n",
    "                prev_lower = st_out_state.lower\n",
    "                prev_dir_ = st_out_state.dir_\n",
    "                was_entry = not np.isnan(st_out_state.long)\n",
    "                was_exit = not np.isnan(st_out_state.short)\n",
    "\n",
    "                if is_entry and position == 0:\n",
    "                    size = np.inf\n",
    "                elif is_exit and position > 0:\n",
    "                    size = -np.inf\n",
    "                else:\n",
    "                    size = np.nan\n",
    "\n",
    "                val_price = close[i, col]\n",
    "                value = cash + position * val_price\n",
    "                if not np.isnan(size):\n",
    "                    exec_state = vbt.pf_enums.ExecState(\n",
    "                        cash=cash,\n",
    "                        position=position,\n",
    "                        debt=debt,\n",
    "                        locked_cash=locked_cash,\n",
    "                        free_cash=free_cash,\n",
    "                        val_price=val_price,\n",
    "                        value=value\n",
    "                    )\n",
    "                    price_area = vbt.pf_enums.PriceArea(\n",
    "                        open=np.nan,\n",
    "                        high=high[i, col],\n",
    "                        low=low[i, col],\n",
    "                        close=close[i, col]\n",
    "                    )\n",
    "                    order = vbt.pf_nb.order_nb(\n",
    "                        size=size, \n",
    "                        direction=vbt.pf_enums.Direction.LongOnly,\n",
    "                        fees=0.001\n",
    "                    )\n",
    "                    _, new_exec_state = vbt.pf_nb.execute_order_nb(exec_state, order, price_area)\n",
    "                    cash, position, debt, locked_cash, free_cash, val_price, value = new_exec_state\n",
    "\n",
    "                value = cash + position * val_price\n",
    "                return_ = vbt.ret_nb.get_return_nb(prev_value, value)\n",
    "                prev_value = value\n",
    "\n",
    "                sharpe_in_state = RollSharpeAIS(\n",
    "                    i=i,\n",
    "                    ret=return_,\n",
    "                    pre_window_ret=np.nan,\n",
    "                    cumsum=cumsum,\n",
    "                    cumsum_sq=cumsum_sq,\n",
    "                    nancnt=nancnt,\n",
    "                    window=i + 1,\n",
    "                    minp=0,\n",
    "                    ddof=1,\n",
    "                    ann_factor=ann_factor\n",
    "                )\n",
    "                sharpe_out_state = rolling_sharpe_acc_nb(sharpe_in_state)\n",
    "                cumsum = sharpe_out_state.cumsum\n",
    "                cumsum_sq = sharpe_out_state.cumsum_sq\n",
    "                nancnt = sharpe_out_state.nancnt\n",
    "                sharpe = sharpe_out_state.value\n",
    "\n",
    "            out[k * close.shape[1] + col] = sharpe\n",
    "        \n",
    "    return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99f0e6a6-68ca-4d6e-8160-a9dfa5fc6153",
   "metadata": {},
   "outputs": [],
   "source": [
    "raw_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values, \n",
    "    close.values,\n",
    "    ann_factor=ann_factor\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6e16670e-ecd3-4d94-b926-e7773eb088dd",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "raw_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values, \n",
    "    close.values,\n",
    "    ann_factor=ann_factor\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14812b10-2e4f-4171-ba91-eecd5c329394",
   "metadata": {},
   "outputs": [],
   "source": [
    "chunked_raw_pipeline_nb = nb_chunked(raw_pipeline_nb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8339de75-6c3c-4e59-9df0-17f969e8d14c",
   "metadata": {},
   "outputs": [],
   "source": [
    "chunked_raw_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values,\n",
    "    close.values,\n",
    "    periods=period_product[:4], \n",
    "    multipliers=multiplier_product[:4],\n",
    "    ann_factor=ann_factor,\n",
    "    _n_chunks=2,\n",
    "    _merge_kwargs=dict(input_columns=close.columns)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1fa1ac51-3cea-4ae8-86dc-d2b5068994b0",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "chunked_raw_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values, \n",
    "    close.values,\n",
    "    periods=period_product, \n",
    "    multipliers=multiplier_product,\n",
    "    ann_factor=ann_factor,\n",
    "    _merge_kwargs=dict(input_columns=close.columns)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d8115735-80ca-463e-ba6d-f0892e413b17",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%timeit\n",
    "chunked_raw_pipeline_nb(\n",
    "    high.values, \n",
    "    low.values, \n",
    "    close.values,\n",
    "    periods=period_product, \n",
    "    multipliers=multiplier_product,\n",
    "    ann_factor=ann_factor,\n",
    "    _execute_kwargs=dict(engine=\"dask\"),\n",
    "    _merge_kwargs=dict(input_columns=close.columns)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "382e71b5-5cbc-46fa-b2de-ce031ad902a0",
   "metadata": {},
   "outputs": [],
   "source": [
    "range_len = int(vbt.timedelta('365d') / vbt.timedelta('1h'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dabf85c8-62ac-4a93-8903-2255e66b2d09",
   "metadata": {},
   "outputs": [],
   "source": [
    "splitter = vbt.Splitter.from_n_rolling(high.index, n=100, length=range_len)\n",
    "\n",
    "roll_high = splitter.take(high, into=\"reset_stacked\")\n",
    "roll_low = splitter.take(low, into=\"reset_stacked\")\n",
    "roll_close = splitter.take(close, into=\"reset_stacked\")\n",
    "\n",
    "range_indexes = splitter.take(high.index)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "722b1a84-5307-42fa-97f1-0027301e4367",
   "metadata": {},
   "outputs": [],
   "source": [
    "roll_close.columns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5832339d-885a-4daa-982e-22b3c665d45c",
   "metadata": {},
   "outputs": [],
   "source": [
    "range_indexes[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5232615f-9745-48d4-8cea-3e32d19d4c77",
   "metadata": {},
   "outputs": [],
   "source": [
    "sharpe_ratios = chunked_raw_pipeline_nb(\n",
    "    roll_high.values, \n",
    "    roll_low.values,\n",
    "    roll_close.values,\n",
    "    periods=period_product, \n",
    "    multipliers=multiplier_product,\n",
    "    ann_factor=ann_factor,\n",
    "    _execute_kwargs=dict(engine=\"dask\"),\n",
    "    _merge_kwargs=dict(input_columns=roll_close.columns)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "190dd192-7fc0-48e1-8ad9-c9653c9c01ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "sharpe_ratios"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "077421ca-b5d6-4e22-b53b-258c4033a8c0",
   "metadata": {},
   "outputs": [],
   "source": [
    "pf_hold = vbt.Portfolio.from_holding(roll_close, freq='1h')\n",
    "sharpe_ratios_hold = pf_hold.sharpe_ratio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "838776aa-3700-4d86-921b-d0bbabbdb47e",
   "metadata": {},
   "outputs": [],
   "source": [
    "sharpe_ratios_hold"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7e8a6477-3235-4243-bb93-662d17daf28d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_subperiod_sharpe(index, sharpe_ratios, sharpe_ratios_hold, range_indexes, symbol):\n",
    "    split = index[0]\n",
    "    sharpe_ratios = sharpe_ratios.xs(\n",
    "        symbol, \n",
    "        level='symbol', \n",
    "        drop_level=True)\n",
    "    sharpe_ratios = sharpe_ratios.xs(\n",
    "        split, \n",
    "        level='split', \n",
    "        drop_level=True)\n",
    "    start_date = range_indexes[split][0]\n",
    "    end_date = range_indexes[split][-1]\n",
    "    return sharpe_ratios.vbt.heatmap(\n",
    "        x_level='st_period', \n",
    "        y_level='st_multiplier',\n",
    "        title=\"{} - {}\".format(\n",
    "            start_date.strftime(\"%d %b, %Y %H:%M:%S\"),\n",
    "            end_date.strftime(\"%d %b, %Y %H:%M:%S\")\n",
    "        ),\n",
    "        trace_kwargs=dict(\n",
    "            zmin=sharpe_ratios.min(),\n",
    "            zmid=sharpe_ratios_hold[(split, symbol)],\n",
    "            zmax=sharpe_ratios.max(),\n",
    "            colorscale='Spectral'\n",
    "        )\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5306f36f-765f-4d52-b8a8-b4c3a4ba7b6d",
   "metadata": {},
   "outputs": [],
   "source": [
    "fname = 'raw_pipeline.gif'\n",
    "level_idx = sharpe_ratios.index.names.index('split')\n",
    "split_indices = sharpe_ratios.index.levels[level_idx]\n",
    "\n",
    "vbt.save_animation(\n",
    "    fname,\n",
    "    split_indices, \n",
    "    plot_subperiod_sharpe,\n",
    "    sharpe_ratios,\n",
    "    sharpe_ratios_hold,\n",
    "    range_indexes,\n",
    "    'BTCUSDT',\n",
    "    delta=1,\n",
    "    fps=7,\n",
    "    writer_kwargs=dict(loop=0)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dd826ac3-a4da-4065-a5d0-9c77ea5ad6f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import Image, display\n",
    "    \n",
    "with open(fname,'rb') as f:\n",
    "    display(Image(data=f.read(), format='png'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4743b6d1-c6a1-4fa7-85f0-0c1e7366a44c",
   "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.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
