{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "257110ac-fb97-4051-adfe-81c1130b8cd2",
   "metadata": {},
   "source": [
    "# MTF analysis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "957fc6b1-f3b9-4778-ab72-28921aed1449",
   "metadata": {},
   "outputs": [],
   "source": [
    "from vectorbtpro import *\n",
    "# whats_imported()\n",
    "\n",
    "vbt.settings.set_theme(\"dark\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c705f5df-b3ab-4a8f-8ea8-952d0cc6cd81",
   "metadata": {},
   "source": [
    "## Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8356ce73-60e4-469e-b3b3-2e414b82eb5e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# h1_data = vbt.BinanceData.pull(\n",
    "#     \"BTCUSDT\", \n",
    "#     start=\"2020-01-01 UTC\", \n",
    "#     end=\"2021-01-01 UTC\",\n",
    "#     timeframe=\"1h\"\n",
    "# )\n",
    "\n",
    "# h1_data.to_hdf()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "06507a95-fc57-49db-9b97-62ccc4184055",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_data = vbt.HDFData.pull('BinanceData.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2fab718a-09f2-4640-b477-dd681753ac53",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_data.wrapper.index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2a930576-729d-4c06-a9a3-6a8ad3de34e6",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_resampler = h1_data.wrapper.get_resampler(\"1h\")\n",
    "h1_resampler.index_difference(reverse=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d4124583-e303-4eda-bfb0-f97e3908ef91",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_data.wrapper.columns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f39bc0ee-6e5c-4c87-93a3-1b5a31fe86f4",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_ohlcv_data = h1_data[[\"Open\", \"High\", \"Low\", \"Close\", \"Volume\"]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "461c8632-4e29-4b0d-8e85-72ea3b1754ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_ohlcv = h1_ohlcv_data.get().resample(\"4h\").agg({\n",
    "    \"Open\": \"first\",\n",
    "    \"High\": \"max\",\n",
    "    \"Low\": \"min\",\n",
    "    \"Close\": \"last\",\n",
    "    \"Volume\": \"sum\"\n",
    "})\n",
    "h4_ohlcv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b84728de-a16b-417f-9ee8-ddafce9b24c3",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(h1_ohlcv_data.get().iloc[:4])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "def713aa-9e46-488a-b100-83a563c5c30f",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(h4_ohlcv.iloc[[0]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8e0d8b54-f1b6-4ff0-851e-c0cb8b8c415a",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(vbt.prettify(vbt.BinanceData.feature_config))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5fcdcdbd-4274-4078-9e8f-5b64c4cd1e46",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_data.use_feature_config_of(vbt.BinanceData)\n",
    "\n",
    "h4_data = h1_data.resample(\"4h\")\n",
    "d1_data = h1_data.resample(\"1d\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5980b577-d6ee-484d-8faf-9db6647dd29b",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(d1_data.get().iloc[[0, -1]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "000ebb64-4228-4967-aaac-2dc88975da61",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(vbt.BinanceData.pull(\n",
    "    \"BTCUSDT\", \n",
    "    start=\"2020-01-01 UTC\", \n",
    "    end=\"2021-01-01 UTC\",\n",
    "    timeframe=\"1d\"\n",
    ").get().iloc[[0, -1]])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f47840ab-b31d-49b3-bf24-d6406a2b542c",
   "metadata": {},
   "source": [
    "## Alignment"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c0ebc21-981a-4c09-8b38-2339e204cf5d",
   "metadata": {},
   "source": [
    "### Pandas"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73b897e3-4f35-41b2-a5c1-8f1c4010c0c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_close = h1_data.get(\"Close\")\n",
    "h4_close = h4_data.get(\"Close\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9dccb01e-8056-4dc0-b5f6-0be44e3f5099",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_close.iloc[:4]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0a3a3c1c-0747-4a18-9462-863162dcdddb",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_close.iloc[:1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1c22394-c1fc-4abc-8036-788848acbe92",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_h4_ratio = h1_close / h4_close\n",
    "h1_h4_ratio.iloc[:4]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "acc7216c-0457-46a0-819d-a4675780f242",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_close_shifted = h4_close.shift()\n",
    "h1_h4_ratio = h1_close / h4_close_shifted\n",
    "h1_h4_ratio.iloc[:8]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8dbbb29-8b79-4c8c-8ce8-5c2582434c95",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_h4_ratio.shift(-1).iloc[:8]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fd546cfa-559d-45a3-8e35-b2af75bd9df6",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_h1_close = h4_close.shift(1).resample(\"1h\").last().shift(-1).ffill()\n",
    "h4_h1_close.iloc[:8]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ba830c23-074b-4ab9-ab4d-3b738a14be56",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = h1_close.rename(\"H1\").iloc[:16].vbt.plot()\n",
    "h4_h1_close.rename(\"H4_H1\").iloc[:16].vbt.plot(fig=fig).show_svg()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6e8c73ee-5c05-4e95-89d0-ad195e138a96",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_h4_ratio = h1_close / h4_h1_close\n",
    "h1_h4_ratio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "023c506a-8e48-43e6-9244-9093c876edb0",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_open = h1_data.get(\"Open\")\n",
    "h4_open  = h4_data.get(\"Open\")\n",
    "\n",
    "h1_open.iloc[:8]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca1bd16a-4157-43ba-be2f-61749ba83a3a",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_h1_open = h4_open.resample(\"1h\").first().ffill()\n",
    "h4_h1_open.iloc[:8]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ffcce240-8595-470d-b743-9b9f743f4543",
   "metadata": {},
   "source": [
    "### VBT"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8e90d2b3-d4b0-4b4f-ae19-fc6e0260ac16",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_close.vbt.realign_closing(\"1h\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "328daf52-40c4-40a8-9614-ea83c702f429",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_open.vbt.realign_opening(\"1h\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fceafe15-dd02-495f-bbcb-7978981dbfc8",
   "metadata": {},
   "source": [
    "#### Resampler"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d9a79ae2-812a-4693-b68f-4b20246f86d4",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_h1_resampler = h4_close.vbt.wrapper.get_resampler(\"1h\")\n",
    "h4_h1_resampler.source_index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75260aef-922a-46d9-b2c2-268247e57c20",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_h1_resampler.target_index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1bcb75a6-1808-4738-b746-80805817bd97",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_h1_resampler.source_freq"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb51ba6c-2d10-4f4e-9d5e-432d515d72c4",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_h1_resampler.target_freq"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ef759223-074b-4961-87f4-47d9ab48e31a",
   "metadata": {},
   "outputs": [],
   "source": [
    "pd_resampler = h4_close.resample(\"1h\")\n",
    "vbt.Resampler.from_pd_resampler(pd_resampler)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "402d9361-3657-46de-bf30-5a57d3006c50",
   "metadata": {},
   "outputs": [],
   "source": [
    "resampler = vbt.Resampler.from_date_range(\n",
    "    source_index=h4_close.index,\n",
    "    source_freq=\"4h\",\n",
    "    start=\"2020-01-01 10:00:00\",\n",
    "    end=\"2020-01-01 22:00:00\",\n",
    "    freq=\"1h\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "52df63ec-3951-4ed7-a1ea-18628dc9a0c0",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_close.vbt.realign_closing(resampler)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "beef78cd-9046-456f-8484-14e1e04b3c2f",
   "metadata": {},
   "source": [
    "#### Custom index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63ff2b4e-46b9-4ad3-8ef6-2f751ad7a9d8",
   "metadata": {},
   "outputs": [],
   "source": [
    "target_index = pd.Index([\n",
    "    \"2020-01-01\",\n",
    "    \"2020-02-01\",\n",
    "    \"2020-03-01\",\n",
    "    \"2020-04-01\",\n",
    "    \"2020-05-01\",\n",
    "    \"2020-06-01\",\n",
    "    \"2020-07-01\",\n",
    "    \"2020-08-01\",\n",
    "    \"2020-09-01\",\n",
    "    \"2020-10-01\",\n",
    "    \"2020-11-01\",\n",
    "    \"2020-12-01\",\n",
    "    \"2021-01-01\"\n",
    "])\n",
    "resampler = vbt.Resampler(h4_close.index, target_index, target_freq=False)\n",
    "h4_close.vbt.realign_closing(resampler)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e5b6de84-d1a8-4d16-9677-fd8e7d8e5b1f",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_close[h4_close.index < \"2020-09-01\"].iloc[-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3612f6b6-c960-4a10-b581-ccfbaf583cf8",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_open.vbt.realign_opening(resampler)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4fed1a9d-0867-49a1-bd20-4acf2d974e91",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_open[h4_open.index <= \"2020-08-01\"].iloc[-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "063cfaaf-045c-40f4-be79-c177196c884b",
   "metadata": {},
   "outputs": [],
   "source": [
    "target_index = pd.Index([\n",
    "    \"2020-01-01\",\n",
    "    \"2020-02-01\",\n",
    "])\n",
    "resampler = vbt.Resampler(h4_close.index, target_index, target_freq=False)\n",
    "h4_close.vbt.realign_closing(resampler)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f3fb1cf-8646-4c71-b38a-b7631d94907e",
   "metadata": {},
   "outputs": [],
   "source": [
    "resampler = vbt.Resampler(h4_close.index, target_index, target_freq=\"30d\")\n",
    "h4_close.vbt.realign_closing(resampler)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "07f61c2b-ea6a-4b6f-9633-760ce35901c1",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_open.vbt.realign(\"2020-06-07 12:15:00\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "42601b68-e4ea-4b70-b302-4f6bb2025969",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_close.vbt.realign(\n",
    "    \"2020-06-07 12:15:00\", \n",
    "    source_rbound=True\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8a5685b5-4847-4e59-8463-e7690bcf84f7",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_high = h4_data.get(\"High\")\n",
    "h4_high.vbt.realign(\n",
    "    target_index, \n",
    "    source_rbound=True\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "65eb1d9f-a7a6-4c03-89ec-68db9685c1d1",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_high.index[h4_high.index < \"2020-02-01\"][-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a9873388-768a-4ff5-a034-081779b3f879",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_high.vbt.realign(\n",
    "    target_index, \n",
    "    source_rbound=True,\n",
    "    target_rbound=True\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3be78c1c-1424-406c-b959-64bdc8cbd826",
   "metadata": {},
   "outputs": [],
   "source": [
    "resampler = vbt.Resampler(h4_high.index, target_index)\n",
    "resampler.target_rbound_index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0631eb39-d180-461f-93c6-3b9c4b5efce2",
   "metadata": {},
   "outputs": [],
   "source": [
    "resampler = vbt.Resampler(\n",
    "    h4_high.index, \n",
    "    target_index, \n",
    "    target_freq=pd.offsets.MonthBegin(1))\n",
    "resampler.target_rbound_index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b96efd81-4a14-4f65-8663-5d049b01a42b",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_high.vbt.realign(\n",
    "    resampler.replace(\n",
    "        target_index=resampler.target_rbound_index, \n",
    "        target_freq=False\n",
    "    ), \n",
    "    wrap_kwargs=dict(index=target_index)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e3f96e1-8161-4417-8201-5084aaec43ee",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_high.vbt.realign(\n",
    "    target_index, \n",
    "    freq=pd.offsets.MonthBegin(1),\n",
    "    target_rbound=\"pandas\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bac8e960-94ac-420a-81db-58db70a623d3",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_high[h4_high.index < \"2020-03-01\"].resample(vbt.offset(\"M\")).last()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa904b11-4aa9-4a3c-9990-4dfb17e5092f",
   "metadata": {},
   "source": [
    "#### Numeric index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "acfcad7a-b094-4378-a380-8dd5dc8bc4ea",
   "metadata": {},
   "outputs": [],
   "source": [
    "resampler = vbt.Resampler(\n",
    "    source_index=np.arange(len(h4_high)),\n",
    "    target_index=np.arange(len(h4_high))[::6],\n",
    "    source_freq=1,\n",
    "    target_freq=6\n",
    ")\n",
    "h4_high.vbt.realign(\n",
    "    resampler, \n",
    "    source_rbound=True,\n",
    "    target_rbound=True\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d3ec6678-66b8-4a9d-92cf-a41ca96a9adb",
   "metadata": {},
   "source": [
    "#### Forward filling"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3f27ab10-cba8-4c5f-97e6-e0533943795a",
   "metadata": {},
   "outputs": [],
   "source": [
    "min5_index = vbt.date_range(start=\"2020\", freq=\"5min\", periods=3)\n",
    "min1_index = vbt.date_range(start=\"2020\", freq=\"1min\", periods=15)\n",
    "min5_mask = pd.Series(False, index=min5_index)\n",
    "min5_mask.iloc[0] = True\n",
    "min5_mask.iloc[2] = True\n",
    "\n",
    "resampler = vbt.Resampler(min5_index, min1_index)\n",
    "min1_mask = min5_mask.vbt.realign_closing(resampler)\n",
    "min1_mask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ab594857-24ea-4316-83e9-aa65c0be621c",
   "metadata": {},
   "outputs": [],
   "source": [
    "min1_mask = min5_mask.vbt.realign_closing(resampler, ffill=False)\n",
    "min1_mask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "053aeb5f-b8c1-46d5-849a-ec431e64ce8c",
   "metadata": {},
   "outputs": [],
   "source": [
    "min1_mask = min1_mask.fillna(False).astype(bool)\n",
    "min1_mask"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a220a77-0bd8-43f3-92ea-d532b883a793",
   "metadata": {},
   "source": [
    "### Indicators"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f1b6073-9e03-49cf-a212-83953d9910e3",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_sma = vbt.talib(\"SMA\").run(h4_data.get(\"Close\"), skipna=True).real\n",
    "d1_sma = vbt.talib(\"SMA\").run(d1_data.get(\"Close\"), skipna=True).real\n",
    "\n",
    "h4_sma = h4_sma.ffill()\n",
    "d1_sma = d1_sma.ffill()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13ace4e8-4647-4f29-a516-142a94460a76",
   "metadata": {},
   "outputs": [],
   "source": [
    "resampler = vbt.Resampler(\n",
    "    d1_sma.index,\n",
    "    h4_sma.index,\n",
    "    source_freq=\"1d\",\n",
    "    target_freq=\"4h\"\n",
    ")\n",
    "d1_h4_sma = d1_sma.vbt.realign_closing(resampler)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "837efa98-5a41-411f-91e4-7e07db205703",
   "metadata": {},
   "outputs": [],
   "source": [
    "d1_sma[\"2020-12-30\":]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f4fab4d-05ec-4c96-be22-7d383ad493bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "d1_h4_sma[\"2020-12-30\":]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e4de9a1-e7e4-49f4-b2d4-f5afe1895cae",
   "metadata": {},
   "outputs": [],
   "source": [
    "entries = h4_sma.vbt.crossed_above(d1_h4_sma)\n",
    "exits = h4_sma.vbt.crossed_below(d1_h4_sma)\n",
    "\n",
    "def plot_date_range(date_range):\n",
    "    fig = h4_sma[date_range].rename(\"H4\").vbt.plot()\n",
    "    d1_h4_sma[date_range].rename(\"D1_H4\").vbt.plot(fig=fig)\n",
    "    entries[date_range].rename(\"Entry\").vbt.signals.plot_as_entries(\n",
    "        y=h4_sma[date_range], fig=fig)\n",
    "    exits[date_range].rename(\"Exit\").vbt.signals.plot_as_exits(\n",
    "        y=h4_sma[date_range], fig=fig)\n",
    "    return fig\n",
    "\n",
    "plot_date_range(slice(\"2020-02-01\", \"2020-03-01\")).show_svg()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dfa7eae9-caff-49c9-b6cd-49216374cda5",
   "metadata": {},
   "outputs": [],
   "source": [
    "d1_open_sma = vbt.talib(\"SMA\").run(\n",
    "    d1_data.get(\"Open\"), \n",
    "    skipna=True\n",
    ").real\n",
    "d1_open_sma = d1_open_sma.ffill()\n",
    "\n",
    "d1_h4_open_sma = d1_open_sma.vbt.realign(\n",
    "    resampler, \n",
    "    source_rbound=False,\n",
    "    target_rbound=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ab960235-1f29-4558-9e3b-c8f1d4ade177",
   "metadata": {},
   "outputs": [],
   "source": [
    "d1_open_sma[\"2020-12-30\":]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5cd93da8-2e9e-46a3-b2cc-576561baca66",
   "metadata": {},
   "outputs": [],
   "source": [
    "d1_h4_open_sma[\"2020-12-30\":]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9b7e7a74-9d26-486d-b1e9-47844c4c0d26",
   "metadata": {},
   "outputs": [],
   "source": [
    "def generate_bandwidths(freqs):\n",
    "    bandwidths = []\n",
    "    for freq in freqs:\n",
    "        close = h1_data.resample(freq).get(\"Close\")\n",
    "        bbands = vbt.talib(\"BBANDS\").run(close, skipna=True)\n",
    "        upperband = bbands.upperband.ffill()\n",
    "        middleband = bbands.middleband.ffill()\n",
    "        lowerband = bbands.lowerband.ffill()\n",
    "        bandwidth = (upperband - lowerband) / middleband\n",
    "        bandwidths.append(bandwidth.vbt.realign_closing(\"1h\"))\n",
    "    df = pd.concat(bandwidths, axis=1, keys=pd.Index(freqs, name=\"timeframe\"))\n",
    "    return df.ffill()\n",
    "\n",
    "bandwidths = generate_bandwidths([\"1h\", \"4h\", \"1d\", \"7d\"])\n",
    "print(bandwidths)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5349999-74d9-4106-924d-b8bfa44557b3",
   "metadata": {},
   "outputs": [],
   "source": [
    "bandwidths.loc[:, ::-1].vbt.ts_heatmap().show_svg()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5474bcdd-1096-46a4-97da-cb16ea310c26",
   "metadata": {},
   "outputs": [],
   "source": [
    ">>> bbands = vbt.talib(\"BBANDS\").run(\n",
    "...     h1_data.get(\"Close\"), \n",
    "...     skipna=True, \n",
    "...     timeframe=[\"1h\", \"4h\", \"1d\", \"7d\"],\n",
    "...     broadcast_kwargs=dict(wrapper_kwargs=dict(freq=\"1h\"))\n",
    "... )\n",
    ">>> bandwidth = (bbands.upperband - bbands.lowerband) / bbands.middleband\n",
    ">>> print(bandwidths)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0ab34e7-6b65-420a-b7ad-41626267a852",
   "metadata": {},
   "source": [
    "### Testing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3d012722-e263-4361-a2be-f77fd57ee53c",
   "metadata": {},
   "outputs": [],
   "source": [
    "def generate_signals(data, freq, fast_window, slow_window):\n",
    "    open_price = data.get(\"Open\").resample(freq).first()\n",
    "    fast_sma = vbt.talib(\"SMA\")\\\n",
    "        .run(\n",
    "            open_price, \n",
    "            fast_window, \n",
    "            skipna=True, \n",
    "            short_name=\"fast_sma\"\n",
    "        )\\\n",
    "        .real.ffill()\\\n",
    "        .vbt.realign(data.wrapper.index)\n",
    "    slow_sma = vbt.talib(\"SMA\")\\\n",
    "        .run(\n",
    "            open_price, \n",
    "            slow_window, \n",
    "            skipna=True, \n",
    "            short_name=\"slow_sma\"\n",
    "        )\\\n",
    "        .real.ffill()\\\n",
    "        .vbt.realign(data.wrapper.index)\n",
    "    entries = fast_sma.vbt.crossed_above(slow_sma)\n",
    "    exits = fast_sma.vbt.crossed_below(slow_sma)\n",
    "    return entries, exits\n",
    "\n",
    "fast_window = [10, 20]\n",
    "slow_window = [20, 30]\n",
    "h1_entries, h1_exits = generate_signals(h1_data, \"1h\", fast_window, slow_window)\n",
    "h4_entries, h4_exits = generate_signals(h1_data, \"4h\", fast_window, slow_window)\n",
    "d1_entries, d1_exits = generate_signals(h1_data, \"1d\", fast_window, slow_window)\n",
    "\n",
    "entries = pd.concat(\n",
    "    (h1_entries, h4_entries, d1_entries), \n",
    "    axis=1, \n",
    "    keys=pd.Index([\"1h\", \"4h\", \"1d\"], name=\"timeframe\")\n",
    ")\n",
    "exits = pd.concat(\n",
    "    (h1_exits, h4_exits, d1_exits), \n",
    "    axis=1, \n",
    "    keys=pd.Index([\"1h\", \"4h\", \"1d\"], name=\"timeframe\")\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be4a1fb7-231e-42a3-b113-a1e71173c96d",
   "metadata": {},
   "outputs": [],
   "source": [
    "(entries.astype(int) - exits.astype(int))\\\n",
    "    .resample(\"1d\").sum()\\\n",
    "    .vbt.ts_heatmap(\n",
    "        trace_kwargs=dict(\n",
    "            colorscale=[\"#ef553b\", \"rgba(0, 0, 0, 0)\", \"#17becf\"],\n",
    "            colorbar=dict(\n",
    "                tickvals=[-1, 0, 1], \n",
    "                ticktext=[\"Exit\", \"\", \"Entry\"]\n",
    "            )\n",
    "        )\n",
    "    ).show_svg()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8cbd506a-8911-400e-bbe3-fc439ae5e31d",
   "metadata": {},
   "outputs": [],
   "source": [
    "pf = vbt.Portfolio.from_signals(\n",
    "    h1_data,\n",
    "    entries,\n",
    "    exits,\n",
    "    sl_stop=0.1,\n",
    "    freq=\"1h\"\n",
    ")\n",
    "\n",
    "pf.orders.count()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "79f8734e-cfd3-4bb3-836f-5e4c96af1078",
   "metadata": {},
   "outputs": [],
   "source": [
    "pf.sharpe_ratio"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "594dd6b4-814a-459a-9a68-a004471cbf55",
   "metadata": {},
   "source": [
    "## Aggregation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e1a727a9-be21-4859-81c7-f660932dbfef",
   "metadata": {},
   "outputs": [],
   "source": [
    "ms_data = h1_data.resample(\"M\")\n",
    "ms_data.get(\"Low\") / ms_data.get(\"High\") - 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ba41908f-77bb-41fd-92dc-3ddfb66660c7",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_high = h1_data.get(\"High\")\n",
    "h1_low = h1_data.get(\"Low\")\n",
    "ms_high = h1_high.resample(vbt.offset(\"M\")).max()\n",
    "ms_low = h1_low.resample(vbt.offset(\"M\")).min()\n",
    "ms_low / ms_high - 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4054b7a6-002d-40f7-8d81-906491355f2c",
   "metadata": {},
   "outputs": [],
   "source": [
    "ms_high = h1_high.vbt.resample_apply(\"M\", vbt.nb.max_reduce_nb)\n",
    "ms_low = h1_low.vbt.resample_apply(\"M\", vbt.nb.min_reduce_nb)\n",
    "ms_low / ms_high - 1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b8bc23f-95a4-4dd9-92cd-87e997979eb9",
   "metadata": {},
   "source": [
    "### Custom index"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "49646098-0314-4c0d-8ed2-361dc5316346",
   "metadata": {},
   "source": [
    "#### Using target index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "286732d7-1ae7-4bd9-9a8e-8a7c21e7caab",
   "metadata": {},
   "outputs": [],
   "source": [
    "target_index = pd.Index([\n",
    "    \"2020-01-01\",\n",
    "    \"2020-02-01\",\n",
    "])\n",
    "h1_high.vbt.resample_to_index(\n",
    "    target_index, \n",
    "    vbt.nb.max_reduce_nb\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6c8f7530-3669-4b50-9cd8-933c638b47f9",
   "metadata": {},
   "outputs": [],
   "source": [
    "target_rbound_index = vbt.Resampler.get_rbound_index(\n",
    "    target_index, \n",
    "    pd.offsets.MonthBegin(1)\n",
    ")\n",
    "h1_high.vbt.resample_to_index(\n",
    "    target_index.append(target_rbound_index[[-1]]), \n",
    "    vbt.nb.max_reduce_nb\n",
    ").iloc[:-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "41025b95-36f3-40cb-ac84-8fd0168ef481",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_high[:\"2020-03-01\"].resample(vbt.offset(\"M\")).max().iloc[:-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2f6f226-2cdd-48b6-9a5c-9379dd2c6ec9",
   "metadata": {},
   "source": [
    "#### Using group-by"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6ae265d2-195c-4045-9dcd-4180bb7ddb7b",
   "metadata": {},
   "outputs": [],
   "source": [
    "pd_resampler = h1_high.resample(vbt.offset(\"M\"))\n",
    "ms_high = h1_high.vbt.groupby_apply(pd_resampler, vbt.nb.max_reduce_nb)\n",
    "ms_low = h1_low.vbt.groupby_apply(pd_resampler, vbt.nb.min_reduce_nb)\n",
    "ms_low / ms_high - 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9a38ab9-18a0-4716-84cf-d276c945f2fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "target_lbound_index = pd.Index([\n",
    "    \"2020-01-01\",\n",
    "    \"2020-02-01\",\n",
    "])\n",
    "target_rbound_index = pd.Index([\n",
    "    \"2020-02-01\",\n",
    "    \"2020-03-01\",\n",
    "])\n",
    "h1_high.vbt.resample_between_bounds(\n",
    "    target_lbound_index, \n",
    "    target_rbound_index,\n",
    "    vbt.nb.max_reduce_nb\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1b315822-a70c-4385-b0c4-2bc5c2b488eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_high.vbt.resample_between_bounds(\n",
    "    \"2020-01-01\", \n",
    "    vbt.date_range(\"2020-01-02\", \"2021-01-01\", freq=\"M\", inclusive=\"both\"),\n",
    "    vbt.nb.max_reduce_nb\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "170017dd-fb6d-47ca-b819-6498d018e9e8",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_high.expanding().max().resample(vbt.offset(\"M\")).max()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24e72852-be2b-4b5b-bc60-d921491d1ab3",
   "metadata": {},
   "source": [
    "### Meta methods"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "986fe4e4-f9f6-47d2-b415-a22e49723af4",
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit\n",
    "def mdd_nb(from_i, to_i, col, high, low):\n",
    "    highest = np.nanmax(high[from_i:to_i, col])\n",
    "    lowest = np.nanmin(low[from_i:to_i, col])\n",
    "    return lowest / highest - 1\n",
    "\n",
    "vbt.pd_acc.resample_apply(\n",
    "    \"M\",\n",
    "    mdd_nb,\n",
    "    vbt.Rep(\"high\"),\n",
    "    vbt.Rep(\"low\"),\n",
    "    broadcast_named_args=dict(\n",
    "        high=h1_high,\n",
    "        low=h1_low\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a83cb89f-90b9-4563-8124-79ea7938da70",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_high.iloc[0:744]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "41414c25-d863-4f83-a140-c1dfef1e4226",
   "metadata": {},
   "outputs": [],
   "source": [
    "h1_low.iloc[0:744].min() / h1_high.iloc[0:744].max() - 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98949b71-5900-407f-b117-de2b7b50cd2e",
   "metadata": {},
   "outputs": [],
   "source": [
    ">>> target_lbound_index = vbt.date_range(\"2020-01-01\", \"2020-12-01\", freq=\"M\", tz=\"UTC\", inclusive=\"both\")\n",
    ">>> target_rbound_index = vbt.date_range(\"2020-02-01\", \"2021-01-01\", freq=\"M\", tz=\"UTC\", inclusive=\"both\")\n",
    ">>> vbt.pd_acc.resample_between_bounds(\n",
    "...     target_lbound_index,\n",
    "...     target_rbound_index,\n",
    "...     mdd_nb,\n",
    "...     vbt.Rep(\"high\"),\n",
    "...     vbt.Rep(\"low\"),\n",
    "...     broadcast_named_args=dict(\n",
    "...         high=h1_high,\n",
    "...         low=h1_low\n",
    "...     )\n",
    "... )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d039d5e0-2562-44e1-b572-0345064bacfe",
   "metadata": {},
   "source": [
    "### Numba"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cf993314-d0dc-42ae-a97c-3f38c237d67a",
   "metadata": {},
   "outputs": [],
   "source": [
    ">>> from vectorbtpro.base.resampling.nb import map_bounds_to_source_ranges_nb\n",
    "\n",
    ">>> range_starts, range_ends = map_bounds_to_source_ranges_nb(\n",
    "...     source_index=h1_high.index.values,\n",
    "...     target_lbound_index=target_lbound_index.values,\n",
    "...     target_rbound_index=target_rbound_index.values,\n",
    "...     closed_lbound=True,\n",
    "...     closed_rbound=False,\n",
    "... )\n",
    ">>> np.column_stack((range_starts, range_ends))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ad3643dd-c3a4-4bc4-92b8-0c976fea9636",
   "metadata": {},
   "outputs": [],
   "source": [
    ">>> ms_mdd_arr = vbt.nb.reduce_index_ranges_meta_nb(\n",
    "...     1,\n",
    "...     range_starts,\n",
    "...     range_ends,\n",
    "...     mdd_nb,\n",
    "...     vbt.to_2d_array(h1_high),\n",
    "...     vbt.to_2d_array(h1_low)\n",
    "... )\n",
    ">>> ms_mdd_arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "00aeb4d1-6f86-4470-9fe0-695823118b31",
   "metadata": {},
   "outputs": [],
   "source": [
    ">>> pd.Series(ms_mdd_arr[:, 0], index=target_lbound_index)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8df7bde-fef1-421d-b09e-b454eb2286d9",
   "metadata": {},
   "source": [
    "### Caveats"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "911d934a-5e38-4a62-a7f1-9a538f7b09a2",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_close_2d = h4_close.iloc[:12]\n",
    "h4_close_2d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "57f8392d-8fa2-4927-923f-ceb3bf1e9349",
   "metadata": {},
   "outputs": [],
   "source": [
    "h4_close_2d.resample(\"1d\").last()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e7e3b5d-e6e7-463c-ae65-3b0b8915afa0",
   "metadata": {},
   "outputs": [],
   "source": [
    "h5_close = h1_close.resample(\"5h\").last()\n",
    "h5_close_2d = h5_close.iloc[:10]\n",
    "h5_close_2d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f5fafb16-c6c3-4a6a-ab92-6f1d0122b603",
   "metadata": {},
   "outputs": [],
   "source": [
    "h5_close_2d.resample(\"1d\").last()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1a45bfe1-fdcf-4e0b-b208-78aa32cc88b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "vbt.timedelta(\"1d\") % vbt.timedelta(\"1h\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ab874a51-5c3f-442d-9b2f-238d3fc810ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "vbt.timedelta(\"1d\") % vbt.timedelta(\"4h\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f8129ea-1f41-412c-a86b-bde7bc42af2b",
   "metadata": {},
   "outputs": [],
   "source": [
    "vbt.timedelta(\"1d\") % vbt.timedelta(\"5h\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fcd15b36-44f9-4642-a976-9a341d12f2f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "h5_close_time = h5_close_2d.index.shift() - pd.Timedelta(nanoseconds=1)\n",
    "h5_close_time.name = \"Close time\"\n",
    "h5_close_2d.index = h5_close_time\n",
    "h5_close_2d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "62befe68-5743-4901-b6eb-b654be33148b",
   "metadata": {},
   "outputs": [],
   "source": [
    "h5_close_2d.resample(\"1d\").last()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f5ad0b0-fe77-44c1-bcc2-4364c7ca15a3",
   "metadata": {},
   "source": [
    "### Portfolio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1c8b2cf1-a1e2-4acf-8eee-5568d93005eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "fast_sma = vbt.talib(\"SMA\").run(h1_close, timeperiod=vbt.Default(10))\n",
    "slow_sma = vbt.talib(\"SMA\").run(h1_close, timeperiod=vbt.Default(20))\n",
    "entries = fast_sma.real_crossed_above(slow_sma.real)\n",
    "exits = fast_sma.real_crossed_below(slow_sma.real)\n",
    "\n",
    "pf = vbt.Portfolio.from_signals(h1_close, entries, exits)\n",
    "pf.plot().show_svg()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8e50332b-8357-4b8d-9236-b2099eb13b62",
   "metadata": {},
   "outputs": [],
   "source": [
    "ms_pf = pf.resample(\"M\")\n",
    "ms_pf.plot().show_svg()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "695b0602-710b-49ac-bf01-8eb981481dc7",
   "metadata": {},
   "outputs": [],
   "source": [
    "pf.total_return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3c10ebcb-64ab-439d-b71e-0ab9e2645c6e",
   "metadata": {},
   "outputs": [],
   "source": [
    "ms_pf.total_return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9821141f-89ad-465d-980c-632f0e8a4c17",
   "metadata": {},
   "outputs": [],
   "source": [
    "(1 + pf.returns).resample(vbt.offset(\"M\")).apply(lambda x: x.prod() - 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ac16b57c-2148-4e85-a2a9-21909209c463",
   "metadata": {},
   "outputs": [],
   "source": [
    "ms_pf.returns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d5725b28-494d-4748-8959-27906374a239",
   "metadata": {},
   "outputs": [],
   "source": [
    "ms_pf.trades.pnl.to_pd(reduce_func_nb=\"sum\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a175390c-359d-49d9-8892-cfcce10e464b",
   "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
}
