{ "cells": [ { "cell_type": "markdown", "id": "906f22ee-4fda-4b00-86b5-666eeba85526", "metadata": {}, "source": [ "# Signal development" ] }, { "cell_type": "markdown", "id": "ea0a17e8-dede-43b7-8d69-ae44fa9eeef0", "metadata": {}, "source": [ "## Generation" ] }, { "cell_type": "markdown", "id": "c92e74cd-bef9-43e1-a78e-f39d308798df", "metadata": {}, "source": [ "### Comparison" ] }, { "cell_type": "code", "execution_count": null, "id": "bc046d2c-54c8-4363-aa50-9535db8c1fa7", "metadata": {}, "outputs": [], "source": [ "from vectorbtpro import *\n", "# whats_imported()\n", "\n", "vbt.settings.set_theme(\"dark\")" ] }, { "cell_type": "code", "execution_count": null, "id": "3a5fa071-d8c4-4443-8528-88c5cab650cf", "metadata": {}, "outputs": [], "source": [ "data = vbt.BinanceData.pull(\n", " [\"BTCUSDT\", \"ETHUSDT\"], \n", " start=\"2021-01-01\",\n", " end=\"2022-01-01\"\n", ")\n", "print(data.get(\"Low\"))" ] }, { "cell_type": "code", "execution_count": null, "id": "b43d9cc2-631e-44a4-9a0b-83aa08c3a722", "metadata": {}, "outputs": [], "source": [ "bb = vbt.talib(\"BBANDS\").run(\n", " data.get(\"Close\"),\n", " timeperiod=vbt.Default(14),\n", " nbdevup=vbt.Default(2),\n", " nbdevdn=vbt.Default(2)\n", ")\n", "print(bb.lowerband)" ] }, { "cell_type": "code", "execution_count": null, "id": "e3ee3576-aa6f-4652-83de-fbaa47eb7137", "metadata": {}, "outputs": [], "source": [ "mask = data.get(\"Low\") < bb.lowerband\n", "print(mask)" ] }, { "cell_type": "code", "execution_count": null, "id": "73b16f6b-cd9d-4ccb-92b8-d374f59dfe4b", "metadata": {}, "outputs": [], "source": [ "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "f1032773-752b-435f-8a96-3aea5e7fec6e", "metadata": {}, "outputs": [], "source": [ "bb_mult = vbt.talib(\"BBANDS\").run(\n", " data.get(\"Close\"),\n", " timeperiod=vbt.Default(14),\n", " nbdevup=[2, 3],\n", " nbdevdn=[2, 3]\n", ")\n", "# mask = data.get(\"Low\") < bb_mult.lowerband" ] }, { "cell_type": "code", "execution_count": null, "id": "6aafc576-9b8a-4475-ad4c-1aaa7f71d4b1", "metadata": {}, "outputs": [], "source": [ "mask = data.get(\"Low\").vbt < bb_mult.lowerband\n", "print(mask)" ] }, { "cell_type": "code", "execution_count": null, "id": "dbe069e6-47c0-4760-b6d7-ebc2e8bf9e62", "metadata": {}, "outputs": [], "source": [ "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "e6db65b8-b971-433f-b4bc-fe97263baa30", "metadata": {}, "outputs": [], "source": [ "mask = bb_mult.lowerband_above(data.get(\"Low\"))\n", "mask.sum()" ] }, { "cell_type": "markdown", "id": "7301f0a1-6e79-4da1-9daf-8345c35da38d", "metadata": {}, "source": [ "#### Thresholds" ] }, { "cell_type": "code", "execution_count": null, "id": "2487f5f5-2277-4608-a6ee-bd26a88b3d48", "metadata": {}, "outputs": [], "source": [ "bandwidth = (bb.upperband - bb.lowerband) / bb.middleband\n", "\n", "mask = bandwidth.vbt > vbt.Param([0.15, 0.3], name=\"threshold\")\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "4f634615-3e6f-4ba8-a3e4-843748fb6663", "metadata": {}, "outputs": [], "source": [ "mask = bandwidth.vbt.combine(\n", " [0.15, 0.3],\n", " combine_func=np.greater, \n", " keys=pd.Index([0.15, 0.3], name=\"threshold\")\n", ")\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "6bda89d4-6381-4ec1-b1bc-ffe302454181", "metadata": {}, "outputs": [], "source": [ "mask = pd.concat(\n", " (bandwidth > 0.15, bandwidth > 0.3), \n", " keys=pd.Index([0.15, 0.3], name=\"threshold\"), \n", " axis=1\n", ")\n", "mask.sum()" ] }, { "cell_type": "markdown", "id": "66ef2746-1d4f-48bd-887e-da3ce86aa2d9", "metadata": {}, "source": [ "#### Crossovers" ] }, { "cell_type": "code", "execution_count": null, "id": "112ffe71-78da-4bba-a9d4-7f5ffeb5cb3e", "metadata": {}, "outputs": [], "source": [ "low_below_lband = data.get(\"Low\") < bb.lowerband\n", "mask = low_below_lband.vbt.signals.first()\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "e92bbb10-724a-4f28-9a54-6d41f36f7bf4", "metadata": {}, "outputs": [], "source": [ "btc_low = data.get(\"Low\", \"BTCUSDT\").rename(\"Low\")\n", "btc_lowerband = bb.lowerband[\"BTCUSDT\"].rename(\"Lower Band\")\n", "btc_mask = mask[\"BTCUSDT\"].rename(\"Signals\")\n", "\n", "fig = btc_low.vbt.plot()\n", "btc_lowerband.vbt.plot(fig=fig)\n", "btc_mask.vbt.signals.plot_as_markers(\n", " y=btc_low, \n", " trace_kwargs=dict(\n", " marker=dict(\n", " color=\"#DFFF00\"\n", " )\n", " ),\n", " fig=fig\n", ").show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "9caf1ea8-18b8-42d6-b759-e3ef87ea23ab", "metadata": {}, "outputs": [], "source": [ "mask = low_below_lband.vbt.signals.first(after_false=True)\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "d22b9fb1-2757-4fe9-b257-233e8a6428b9", "metadata": {}, "outputs": [], "source": [ "sample_low = pd.Series([10, 9, 8, 9, 8])\n", "sample_lband = pd.Series([np.nan, np.nan, 9, 8, 9])\n", "sample_mask = sample_low < sample_lband\n", "sample_mask.vbt.signals.first(after_false=True)" ] }, { "cell_type": "code", "execution_count": null, "id": "cd63d8f4-f71e-4b77-ba9e-f3aa5fab70d3", "metadata": {}, "outputs": [], "source": [ "sample_mask[sample_lband.ffill().isnull()] = True\n", "sample_mask.vbt.signals.first(after_false=True)" ] }, { "cell_type": "code", "execution_count": null, "id": "48ec8914-6279-4e04-9577-7a61123e8f9b", "metadata": {}, "outputs": [], "source": [ "buffer = sample_lband.ffill().isnull().sum(axis=0).max()\n", "buffer" ] }, { "cell_type": "code", "execution_count": null, "id": "9e7b37d6-435b-4d05-b813-c71e3533d7f6", "metadata": {}, "outputs": [], "source": [ "sample_buf_mask = sample_low.iloc[buffer:] < sample_lband.iloc[buffer:]\n", "sample_buf_mask = sample_buf_mask.vbt.signals.first(after_false=True)\n", "sample_mask = sample_low.vbt.wrapper.fill(False)\n", "sample_mask.loc[sample_buf_mask.index] = sample_buf_mask\n", "sample_mask" ] }, { "cell_type": "code", "execution_count": null, "id": "497a1101-5e73-4bd3-9d6d-ceb51bd6ac58", "metadata": {}, "outputs": [], "source": [ "mask = data.get(\"Low\").vbt.crossed_below(bb.lowerband, wait=1)\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "e385bd31-4be5-40c8-95eb-23ec488dcff2", "metadata": {}, "outputs": [], "source": [ "mask = bb.lowerband_crossed_above(data.get(\"Low\"), wait=1)\n", "mask.sum()" ] }, { "cell_type": "markdown", "id": "67ac0af5-b8bc-4e4f-8351-e33661778165", "metadata": {}, "source": [ "### Logical operators" ] }, { "cell_type": "code", "execution_count": null, "id": "931d10e3-7259-49f7-bde3-3c6b11e356d0", "metadata": {}, "outputs": [], "source": [ "cond1 = data.get(\"Low\") < bb.lowerband\n", "cond2 = bandwidth > 0.3\n", "cond3 = data.get(\"High\") > bb.upperband\n", "cond4 = bandwidth < 0.15\n", "\n", "mask = (cond1 & cond2) | (cond3 & cond4)\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "7a8e5bb5-3a3f-4e95-8805-78dbeb73e119", "metadata": {}, "outputs": [], "source": [ "cond1 = data.get(\"Low\").vbt < bb.lowerband\n", "cond2 = bandwidth.vbt > vbt.Param([0.3, 0.3, 0.4, 0.4], name=\"cond2_th\")\n", "cond3 = data.get(\"High\").vbt > bb.upperband\n", "cond4 = bandwidth.vbt < vbt.Param([0.1, 0.2, 0.1, 0.2], name=\"cond4_th\")\n", "\n", "mask = (cond1.vbt & cond2).vbt | (cond3.vbt & cond4)\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "96fec8a9-27fe-4ec0-9745-06b612a8beeb", "metadata": {}, "outputs": [], "source": [ "cond1 = data.get(\"Low\").vbt < bb.lowerband\n", "cond2 = bandwidth.vbt > vbt.Param([0.3, 0.4], name=\"cond2_th\")\n", "cond3 = data.get(\"High\").vbt > bb.upperband\n", "cond4 = bandwidth.vbt < vbt.Param([0.1, 0.2], name=\"cond4_th\")" ] }, { "cell_type": "code", "execution_count": null, "id": "553a64f0-a358-4e98-a0f6-e05d44ba84be", "metadata": {}, "outputs": [], "source": [ "i1 = np.split(np.arange(len(cond1.columns)), len(cond1.columns) // 2)\n", "i2 = np.split(np.arange(len(cond2.columns)), len(cond2.columns) // 2)\n", "i3 = np.split(np.arange(len(cond3.columns)), len(cond3.columns) // 2)\n", "i4 = np.split(np.arange(len(cond4.columns)), len(cond4.columns) // 2)" ] }, { "cell_type": "code", "execution_count": null, "id": "55799254-e8c1-4b67-aba1-a7015ebe5b92", "metadata": {}, "outputs": [], "source": [ "print(i1)\n", "print(i2)\n", "print(i3)\n", "print(i4)" ] }, { "cell_type": "code", "execution_count": null, "id": "6cef9c96-52bf-42be-8c70-750c39bea02a", "metadata": {}, "outputs": [], "source": [ "i1, i2, i3, i4 = zip(*product(i1, i2, i3, i4))" ] }, { "cell_type": "code", "execution_count": null, "id": "7348da68-f874-4334-88ac-be80db0ec4e7", "metadata": {}, "outputs": [], "source": [ "print(i1)\n", "print(i2)\n", "print(i3)\n", "print(i4)" ] }, { "cell_type": "code", "execution_count": null, "id": "f055cbef-62ef-4958-a6c2-9c253e023e9d", "metadata": {}, "outputs": [], "source": [ "i1 = np.asarray(i1).flatten()\n", "i2 = np.asarray(i2).flatten()\n", "i3 = np.asarray(i3).flatten()\n", "i4 = np.asarray(i4).flatten()" ] }, { "cell_type": "code", "execution_count": null, "id": "f7941975-2907-4a19-811f-e98848822d66", "metadata": {}, "outputs": [], "source": [ "print(i1)\n", "print(i2)\n", "print(i3)\n", "print(i4)" ] }, { "cell_type": "code", "execution_count": null, "id": "8ed4ad6f-3182-4da6-9bfa-8ecb705af57e", "metadata": {}, "outputs": [], "source": [ "cond1 = cond1.iloc[:, i1]\n", "cond2 = cond2.iloc[:, i2]\n", "cond3 = cond3.iloc[:, i3]\n", "cond4 = cond4.iloc[:, i4]" ] }, { "cell_type": "code", "execution_count": null, "id": "38fb824d-8aa5-494d-b2f8-bea49e2d584e", "metadata": {}, "outputs": [], "source": [ "mask = (cond1.vbt & cond2).vbt | (cond3.vbt & cond4)\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "0e5545a3-b347-41de-bee5-9954ff2b0698", "metadata": {}, "outputs": [], "source": [ "MaskGenerator = vbt.IF.from_expr(\"\"\"\n", "upperband, middleband, lowerband = @res_talib_bbands\n", "bandwidth = (upperband - lowerband) / middleband\n", "cond1 = low < lowerband\n", "cond2 = bandwidth > @p_cond2_th\n", "cond3 = high > upperband\n", "cond4 = bandwidth < @p_cond4_th\n", "@out_mask:(cond1 & cond2) | (cond3 & cond4)\n", "\"\"\")\n", "\n", "print(vbt.format_func(MaskGenerator.run, incl_doc=False))" ] }, { "cell_type": "code", "execution_count": null, "id": "3bbbab02-1e9e-497c-8616-cbfaa19cd39e", "metadata": {}, "outputs": [], "source": [ "mask_generator = MaskGenerator.run(\n", " high=data.get(\"High\"),\n", " low=data.get(\"Low\"),\n", " close=data.get(\"Close\"),\n", " cond2_th=[0.3, 0.4],\n", " cond4_th=[0.1, 0.2],\n", " bbands_timeperiod=vbt.Default(14),\n", " param_product=True\n", ")\n", "mask_generator.mask.sum()" ] }, { "cell_type": "markdown", "id": "8d6deb30-8e48-48e1-b06e-699454a28098", "metadata": {}, "source": [ "### Shifting" ] }, { "cell_type": "code", "execution_count": null, "id": "58f200ec-96b8-450b-afc4-76307bf59549", "metadata": {}, "outputs": [], "source": [ "cond1 = data.get(\"Low\") < bb.lowerband\n", "cond2 = bandwidth > bandwidth.shift(1)\n", "\n", "mask = cond1 & cond2\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "efef3852-ba5a-4670-8ccf-cb51e5d41432", "metadata": {}, "outputs": [], "source": [ "cond2 = bandwidth > bandwidth.rolling(\"7d\").apply(lambda x: x[0])\n", "\n", "mask = cond1 & cond2\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "5b308a3e-f8e8-46b0-b016-2aa1933dc44d", "metadata": {}, "outputs": [], "source": [ "def exactly_ago(sr):\n", " if sr.index[0] == sr.index[-1] - vbt.timedelta(\"7d\"):\n", " return sr.iloc[0]\n", " return np.nan\n", "\n", "cond_7d_ago = bandwidth.rolling(\"8d\").apply(exactly_ago, raw=False)\n", "cond2 = bandwidth > cond_7d_ago\n", "\n", "mask = cond1 & cond2\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "a41f9fe9-51d8-420b-8352-2238cf58208f", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def exactly_ago_meta_nb(from_i, to_i, col, index, freq, arr):\n", " if index[from_i] == index[to_i - 1] - freq:\n", " return arr[from_i, col]\n", " return np.nan\n", "\n", "cond_7d_ago = vbt.pd_acc.rolling_apply(\n", " \"8d\",\n", " exactly_ago_meta_nb,\n", " bandwidth.index.values,\n", " vbt.timedelta(\"7d\").to_timedelta64(),\n", " vbt.to_2d_array(bandwidth),\n", " wrapper=bandwidth.vbt.wrapper\n", ")\n", "cond2 = bandwidth > cond_7d_ago\n", "\n", "mask = cond1 & cond2\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "fa58a149-9754-41da-ab53-e7642d0c3410", "metadata": {}, "outputs": [], "source": [ "cond2 = bandwidth > bandwidth.vbt.ago(\"7d\")\n", "\n", "mask = cond1 & cond2\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "ae73e445-ca38-4b64-acee-daed9f4ba6fe", "metadata": {}, "outputs": [], "source": [ "bandwidth.iloc[-8]" ] }, { "cell_type": "code", "execution_count": null, "id": "bba2e968-9d6a-422a-a88f-abad435da438", "metadata": {}, "outputs": [], "source": [ "bandwidth.vbt.ago(\"7d\").iloc[-1]" ] }, { "cell_type": "markdown", "id": "50937a93-0f50-47c2-a28f-578baa33609a", "metadata": {}, "source": [ "### Truth value testing" ] }, { "cell_type": "code", "execution_count": null, "id": "f1e5ab80-5cda-46cf-8a30-d20ab4ab9aab", "metadata": {}, "outputs": [], "source": [ "cond2 = data.get(\"Close\").vbt.crossed_below(bb.middleband)\n", "cond2 = cond2.rolling(5, min_periods=1).max().astype(bool)\n", "\n", "mask = cond1 & cond2\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "4a4231aa-0152-4aba-b1ff-b40c98f2f196", "metadata": {}, "outputs": [], "source": [ "cond2 = data.get(\"Close\").vbt.crossed_below(bb.middleband)\n", "cond2 = cond2.vbt.rolling_any(5)\n", "\n", "mask = cond1 & cond2\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "df8a2ad1-3ba1-4b81-8d63-ebca8e656a2b", "metadata": {}, "outputs": [], "source": [ "cond2 = data.get(\"Close\").vbt.crossed_below(bb.middleband)\n", "cond2 = cond2.vbt.rolling_apply(\n", " \"W\", \"any\", \n", " minp=1, \n", " wrap_kwargs=dict(fillna=0, dtype=bool)\n", ")\n", "\n", "mask = cond1 & cond2\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "39b1b59c-d0ad-4757-bfe6-789a12e11026", "metadata": {}, "outputs": [], "source": [ "anchor_points = data.wrapper.get_index_points(\n", " every=\"M\", \n", " start=0, \n", " exact_start=True\n", ")\n", "anchor_points" ] }, { "cell_type": "code", "execution_count": null, "id": "d6b79fd8-b86e-486b-8e87-663e2ddff2c9", "metadata": {}, "outputs": [], "source": [ "left_bound = np.full(len(data.wrapper.index), np.nan)\n", "left_bound[anchor_points] = anchor_points\n", "left_bound = vbt.dt.to_ns(vbt.nb.ffill_1d_nb(left_bound))\n", "left_bound = bandwidth.index[left_bound]\n", "left_bound" ] }, { "cell_type": "code", "execution_count": null, "id": "86481972-0ab1-4f65-9fa3-4c3a7f38d3a1", "metadata": {}, "outputs": [], "source": [ "right_bound = data.wrapper.index\n", "right_bound" ] }, { "cell_type": "code", "execution_count": null, "id": "5177843a-f65a-42ba-9e1d-c1744aee9e7b", "metadata": {}, "outputs": [], "source": [ "mask = (bandwidth <= 0.1).vbt.resample_between_bounds(\n", " left_bound, \n", " right_bound,\n", " \"any\",\n", " closed_lbound=True,\n", " closed_rbound=True,\n", " wrap_with_lbound=False,\n", " wrap_kwargs=dict(fillna=0, dtype=bool)\n", ")\n", "mask.index = right_bound\n", "mask.astype(int).vbt.ts_heatmap().show_svg()" ] }, { "cell_type": "markdown", "id": "73d0ca33-a66b-4fe6-8e7d-144df6bd0882", "metadata": {}, "source": [ "### Periodically" ] }, { "cell_type": "code", "execution_count": null, "id": "a27fa7cc-6c65-4ff4-aebf-aacdaf160ca1", "metadata": {}, "outputs": [], "source": [ "min_data = vbt.BinanceData.pull(\n", " [\"BTCUSDT\", \"ETHUSDT\"], \n", " start=\"2021-01-01 UTC\",\n", " end=\"2021-02-01 UTC\",\n", " timeframe=\"1h\"\n", ")\n", "index = min_data.wrapper.index\n", "tuesday_index = index[index.weekday == 1]\n", "tuesday_index" ] }, { "cell_type": "code", "execution_count": null, "id": "61283286-8bc4-4a67-8385-54b88926b078", "metadata": {}, "outputs": [], "source": [ "tuesday_1800_index = tuesday_index[tuesday_index.hour == 18]\n", "tuesday_1800_index" ] }, { "cell_type": "code", "execution_count": null, "id": "8c1f8c57-df34-4cd0-99b3-f9511f987f99", "metadata": {}, "outputs": [], "source": [ "tuesday_1730_index = index[\n", " (index.weekday == 1) & \n", " (index.hour == 17) & \n", " (index.minute == 30)\n", "]\n", "tuesday_1730_index" ] }, { "cell_type": "code", "execution_count": null, "id": "52657759-5799-4aae-ac79-cd588037fcea", "metadata": {}, "outputs": [], "source": [ "index.get_indexer([vbt.timestamp(\"2021-01-07\", tz=index.tz)])" ] }, { "cell_type": "code", "execution_count": null, "id": "7e13344f-4c60-4e46-a2ff-9dcecc989475", "metadata": {}, "outputs": [], "source": [ "index.get_indexer([vbt.timestamp(\"2021-01-07 17:30:00\", tz=index.tz)]) " ] }, { "cell_type": "code", "execution_count": null, "id": "bbdd67e5-4b86-4e5a-86f3-d2c123f3bf01", "metadata": {}, "outputs": [], "source": [ "index[index.get_indexer(\n", " [vbt.timestamp(\"2021-01-07 17:30:00\", tz=index.tz)],\n", " method=\"ffill\"\n", ")]" ] }, { "cell_type": "code", "execution_count": null, "id": "241ed072-2873-4f0b-9cba-7fa5d8bd5401", "metadata": {}, "outputs": [], "source": [ "index[index.get_indexer(\n", " [vbt.timestamp(\"2021-01-07 17:30:00\", tz=index.tz)],\n", " method=\"bfill\"\n", ")]" ] }, { "cell_type": "code", "execution_count": null, "id": "e31841d0-3c4a-4967-a0a1-62b5c680cd85", "metadata": {}, "outputs": [], "source": [ "each_tuesday = vbt.date_range(index[0], index[-1], freq=\"tuesday\")\n", "each_tuesday_1730 = each_tuesday + pd.Timedelta(hours=17, minutes=30)\n", "each_tuesday_1730" ] }, { "cell_type": "code", "execution_count": null, "id": "21d5f7eb-f18e-4327-8d20-9cdc72cad6d8", "metadata": {}, "outputs": [], "source": [ "positions = index.get_indexer(each_tuesday_1730, method=\"bfill\")\n", "\n", "min_symbol_wrapper = min_data.get_symbol_wrapper()\n", "mask = min_symbol_wrapper.fill(False)\n", "mask.iloc[positions] = True\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "bfadfca9-23df-4372-aab6-ecfb9e5c4046", "metadata": {}, "outputs": [], "source": [ "mask[mask.any(axis=1)].index.strftime(\"%A %T\")" ] }, { "cell_type": "code", "execution_count": null, "id": "1206dc67-91f7-4207-ae3e-05e579576fe3", "metadata": {}, "outputs": [], "source": [ "tuesday_after_1700 = (index.weekday == 1) & (index.hour >= 17)\n", "wednesday_before_1700 = (index.weekday == 2) & (index.hour < 17)\n", "main_cond = tuesday_after_1700 | wednesday_before_1700\n", "mask = min_symbol_wrapper.fill(False)\n", "mask[main_cond] = True\n", "mask = mask.vbt.signals.first()\n", "mask[mask.any(axis=1)].index.strftime(\"%A %T\")" ] }, { "cell_type": "code", "execution_count": null, "id": "e011aa6b-94a2-4a76-a3e6-f2998e4bdc5e", "metadata": {}, "outputs": [], "source": [ "mask = min_symbol_wrapper.fill(False)\n", "mask.vbt.set(\n", " True, \n", " every=\"tuesday\", \n", " at_time=\"17:30\", \n", " inplace=True\n", ")\n", "mask[mask.any(axis=1)].index.strftime(\"%A %T\")" ] }, { "cell_type": "code", "execution_count": null, "id": "3849cd8e-63ab-4603-b84a-ea2f0e0cb282", "metadata": {}, "outputs": [], "source": [ "mask = min_symbol_wrapper.fill(False)\n", "mask.vbt.set(\n", " True, \n", " every=\"tuesday\", \n", " at_time=\"18:00\", \n", " add_delta=pd.Timedelta(nanoseconds=1),\n", " inplace=True\n", ")\n", "mask[mask.any(axis=1)].index.strftime(\"%A %T\")" ] }, { "cell_type": "code", "execution_count": null, "id": "0bcd77bd-b5f9-4e03-8a1b-a2f1646d2887", "metadata": {}, "outputs": [], "source": [ "mask = min_symbol_wrapper.fill(False)\n", "mask.vbt.set_between(\n", " True, \n", " every=\"monday\", \n", " start_time=\"12:00\", \n", " end_time=\"17:00\", \n", " add_end_delta=pd.Timedelta(days=1),\n", " inplace=True\n", ")\n", "mask[mask.any(axis=1)].index.strftime(\"%A %T\")" ] }, { "cell_type": "code", "execution_count": null, "id": "0335ab31-4ae0-4e2d-be6e-412949468b6f", "metadata": {}, "outputs": [], "source": [ "mask = min_symbol_wrapper.fill(False)\n", "mask.vbt.set(\n", " True, \n", " on=\"January 7th 2021 UTC\",\n", " indexer_method=None,\n", " inplace=True\n", ")\n", "mask[mask.any(axis=1)].index" ] }, { "cell_type": "code", "execution_count": null, "id": "8383ce7c-dda3-472c-af61-5b1f33b67b3f", "metadata": {}, "outputs": [], "source": [ "mask = min_symbol_wrapper.fill(False)\n", "mask.vbt.set_between(\n", " True, \n", " start=[\"2021-01-01 12:00:00\", \"2021-01-07 12:00:00\"],\n", " end=[\"2021-01-02 12:00:00\", \"2021-01-08 12:00:00\"],\n", " inplace=True\n", ")\n", "mask[mask.any(axis=1)].index" ] }, { "cell_type": "code", "execution_count": null, "id": "1be65183-69dc-4576-925f-ba51c2d9b16d", "metadata": {}, "outputs": [], "source": [ "mask = min_symbol_wrapper.fill(False)\n", "mask.vbt.set_between(\n", " True, \n", " every=\"monday\",\n", " split_every=False,\n", " add_end_delta=\"2h\",\n", " inplace=True\n", ")\n", "mask[mask.any(axis=1)].index" ] }, { "cell_type": "markdown", "id": "0942b710-9089-4203-812c-087c90458e50", "metadata": {}, "source": [ "### Iteratively" ] }, { "cell_type": "code", "execution_count": null, "id": "9da35717-67da-421a-acf7-e3e1a81d34f1", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def generate_mask_1d_nb(\n", " high, low,\n", " uband, mband, lband,\n", " cond2_th, cond4_th\n", "):\n", " out = np.full(high.shape, False)\n", " \n", " for i in range(high.shape[0]):\n", "\n", " \n", " bandwidth = (uband[i] - lband[i]) / mband[i]\n", " cond1 = low[i] < lband[i]\n", " cond2 = bandwidth > cond2_th\n", " cond3 = high[i] > uband[i]\n", " cond4 = bandwidth < cond4_th\n", " signal = (cond1 and cond2) or (cond3 and cond4)\n", " \n", " out[i] = signal\n", " \n", " return out\n", "\n", "mask = generate_mask_1d_nb(\n", " data.get(\"High\")[\"BTCUSDT\"].values,\n", " data.get(\"Low\")[\"BTCUSDT\"].values,\n", " bb.upperband[\"BTCUSDT\"].values,\n", " bb.middleband[\"BTCUSDT\"].values,\n", " bb.lowerband[\"BTCUSDT\"].values,\n", " 0.30,\n", " 0.15\n", ")\n", "symbol_wrapper = data.get_symbol_wrapper()\n", "mask = symbol_wrapper[\"BTCUSDT\"].wrap(mask)\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "ff27246a-d96c-428a-a391-56decc8a457c", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def generate_mask_nb(\n", " high, low,\n", " uband, mband, lband,\n", " cond2_th, cond4_th\n", "):\n", " out = np.empty(high.shape, dtype=np.bool_)\n", " \n", " for col in range(high.shape[1]):\n", " out[:, col] = generate_mask_1d_nb(\n", " high[:, col], low[:, col],\n", " uband[:, col], mband[:, col], lband[:, col],\n", " cond2_th, cond4_th\n", " )\n", " \n", " return out\n", "\n", "mask = generate_mask_nb(\n", " vbt.to_2d_array(data.get(\"High\")),\n", " vbt.to_2d_array(data.get(\"Low\")),\n", " vbt.to_2d_array(bb.upperband),\n", " vbt.to_2d_array(bb.middleband),\n", " vbt.to_2d_array(bb.lowerband),\n", " 0.30,\n", " 0.15\n", ")\n", "mask = symbol_wrapper.wrap(mask)\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "7e38705d-863f-40c7-bfad-127b7551999d", "metadata": {}, "outputs": [], "source": [ "MaskGenerator = vbt.IF(\n", " input_names=[\"high\", \"low\", \"uband\", \"mband\", \"lband\"],\n", " param_names=[\"cond2_th\", \"cond4_th\"],\n", " output_names=[\"mask\"]\n", ").with_apply_func(generate_mask_1d_nb, takes_1d=True)\n", "mask_generator = MaskGenerator.run(\n", " data.get(\"High\"),\n", " data.get(\"Low\"),\n", " bb.upperband,\n", " bb.middleband,\n", " bb.lowerband,\n", " [0.3, 0.4],\n", " [0.1, 0.2],\n", " param_product=True\n", ")\n", "mask_generator.mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "d2989ca1-0925-4d8f-bfb1-82606aed0d80", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def value_ago_1d_nb(arr, ago):\n", " out = np.empty(arr.shape, dtype=np.float_)\n", " for i in range(out.shape[0]):\n", " if i - ago >= 0:\n", " out[i] = arr[i - ago]\n", " else:\n", " out[i] = np.nan\n", " return out\n", "\n", "arr = np.array([1, 2, 3])\n", "value_ago_1d_nb(arr, 1)" ] }, { "cell_type": "code", "execution_count": null, "id": "24455bb7-98c0-45c1-9333-78c9424838ef", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def any_in_window_1d_nb(arr, window):\n", " out = np.empty(arr.shape, dtype=np.bool_)\n", " for i in range(out.shape[0]):\n", " from_i = max(0, i + 1 - window)\n", " to_i = i + 1\n", " out[i] = np.any(arr[from_i:to_i])\n", " return out\n", "\n", "arr = np.array([False, True, True, False, False])\n", "any_in_window_1d_nb(arr, 2)" ] }, { "cell_type": "code", "execution_count": null, "id": "a4cf9d70-b97c-4c47-b65a-8a4c13f4ab9f", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def any_in_var_window_1d_nb(arr, index, freq):\n", " out = np.empty(arr.shape, dtype=np.bool_)\n", " from_i = 0\n", " for i in range(out.shape[0]):\n", " if index[from_i] <= index[i] - freq:\n", " for j in range(from_i + 1, index.shape[0]):\n", " if index[j] > index[i] - freq:\n", " from_i = j\n", " break\n", " to_i = i + 1\n", " out[i] = np.any(arr[from_i:to_i])\n", " return out\n", "\n", "arr = np.array([False, True, True, False, False])\n", "index = vbt.date_range(\"2020\", freq=\"5min\", periods=len(arr)).values\n", "freq = vbt.timedelta(\"10min\").to_timedelta64()\n", "any_in_var_window_1d_nb(arr, index, freq)" ] }, { "cell_type": "code", "execution_count": null, "id": "3fd6c643-a018-4f56-a52d-2352a5ecb430", "metadata": {}, "outputs": [], "source": [ "any_in_var_window_1d_nb(arr, vbt.dt.to_ns(index), vbt.dt.to_ns(freq))" ] }, { "cell_type": "code", "execution_count": null, "id": "2a375903-eea0-4bd1-b46f-d71c0dbc719c", "metadata": {}, "outputs": [], "source": [ "vbt.dt.to_ns(index)" ] }, { "cell_type": "code", "execution_count": null, "id": "e32610d0-01e8-4d4d-a3e6-b83218a8e877", "metadata": {}, "outputs": [], "source": [ "vbt.dt.to_ns(index - np.datetime64(0, \"ns\"))" ] }, { "cell_type": "code", "execution_count": null, "id": "b775b6a0-2e64-4027-b84a-9aed88e0ae66", "metadata": {}, "outputs": [], "source": [ "vbt.dt.to_ns(freq)" ] }, { "cell_type": "code", "execution_count": null, "id": "20a5f083-2f52-436e-9d23-3a0ef67d929d", "metadata": {}, "outputs": [], "source": [ "vbt.dt.to_ns(freq) / 1000 / 1000 / 1000 / 60" ] }, { "cell_type": "markdown", "id": "d1dc72ab-3858-49a0-93c2-a7d37c8073c7", "metadata": {}, "source": [ "### Generators" ] }, { "cell_type": "code", "execution_count": null, "id": "b6bb839a-61f5-44be-8643-495078dbd208", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def place_func_nb(c, index):\n", " for out_i in range(len(c.out)):\n", " i = c.from_i + out_i\n", " weekday = vbt.dt_nb.weekday_nb(index[i])\n", " hour = vbt.dt_nb.hour_nb(index[i])\n", " if weekday == 2 and hour == 17:\n", " c.out[out_i] = True\n", " return out_i\n", " return -1\n", "\n", "mask = vbt.pd_acc.signals.generate(\n", " symbol_wrapper.shape,\n", " place_func_nb,\n", " vbt.dt.to_ns(symbol_wrapper.index),\n", " wrapper=symbol_wrapper\n", ")\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "b3124c4b-c02c-4e0e-9362-5c5b8114dd1b", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def place_func_nb(c, index):\n", " last_i = -1\n", " for out_i in range(len(c.out)):\n", " i = c.from_i + out_i\n", " weekday = vbt.dt_nb.weekday_nb(index[i])\n", " hour = vbt.dt_nb.hour_nb(index[i])\n", " if weekday == 2 and hour == 17:\n", " c.out[out_i] = True\n", " last_i = out_i\n", " else:\n", " past_target_midnight = vbt.dt_nb.past_weekday_nb(index[i], 2)\n", " past_target = past_target_midnight + 17 * vbt.dt_nb.h_ns\n", " if (i > 0 and index[i - 1] < past_target) and \\\n", " index[i] > past_target:\n", " c.out[out_i] = True\n", " last_i = out_i\n", " return last_i\n", "\n", "mask = vbt.pd_acc.signals.generate(\n", " symbol_wrapper.shape,\n", " place_func_nb,\n", " vbt.dt.to_ns(symbol_wrapper.index),\n", " wrapper=symbol_wrapper\n", ")\n", "mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "dce0bdc1-7956-4760-9b29-05cb8a7d13b6", "metadata": {}, "outputs": [], "source": [ "mask.index[mask.any(axis=1)].strftime('%A %m/%d/%Y')" ] }, { "cell_type": "code", "execution_count": null, "id": "c1a5a0e2-23cd-48d3-ab36-2f94d8a03adc", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def place_func_nb(c, weekday, hour, index):\n", " last_i = -1\n", " for out_i in range(len(c.out)):\n", " i = c.from_i + out_i\n", " weekday_now = vbt.dt_nb.weekday_nb(index[i])\n", " hour_now = vbt.dt_nb.hour_nb(index[i])\n", " if weekday_now == weekday and hour_now == hour:\n", " c.out[out_i] = True\n", " last_i = out_i\n", " return last_i\n", "\n", "EntryGenerator = vbt.SignalFactory(\n", " mode=\"entries\",\n", " param_names=[\"weekday\", \"hour\"]\n", ").with_place_func(\n", " entry_place_func_nb=place_func_nb,\n", " entry_settings=dict(\n", " pass_params=[\"weekday\", \"hour\"],\n", " ),\n", " var_args=True\n", ")\n", "entry_generator = EntryGenerator.run(\n", " symbol_wrapper.shape,\n", " 2, \n", " [0, 17],\n", " vbt.dt.to_ns(symbol_wrapper.index),\n", " input_index=symbol_wrapper.index,\n", " input_columns=symbol_wrapper.columns\n", ")\n", "entry_generator.entries.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "f29a5bde-9025-4946-932d-94d23e4b5ac0", "metadata": {}, "outputs": [], "source": [ "entry_generator.plot(column=(2, 0, \"BTCUSDT\")).show_svg()" ] }, { "cell_type": "markdown", "id": "21f7b441-5d5a-4ac9-93bb-e0289eefa5d6", "metadata": {}, "source": [ "#### Exits" ] }, { "cell_type": "code", "execution_count": null, "id": "1a58d3a9-e3fa-4839-908d-e6d8a0009e04", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def exit_place_func_nb(c):\n", " c.out[0] = True\n", " return 0\n", "\n", "entries = symbol_wrapper.fill(False)\n", "entries.vbt.set(True, every=\"Q\", inplace=True)\n", "entries.index[entries.any(axis=1)]" ] }, { "cell_type": "code", "execution_count": null, "id": "03872da1-6712-4f1e-809d-92303661364e", "metadata": {}, "outputs": [], "source": [ "exits = entries.vbt.signals.generate_exits(exit_place_func_nb)\n", "exits.index[exits.any(axis=1)]" ] }, { "cell_type": "code", "execution_count": null, "id": "c80edcb4-602b-48e8-bf7b-98e805259758", "metadata": {}, "outputs": [], "source": [ "exits = entries.vbt.signals.generate_exits(\n", " exit_place_func_nb,\n", " wait=0\n", ")\n", "exits.index[exits.any(axis=1)]" ] }, { "cell_type": "code", "execution_count": null, "id": "02c6a9cc-13f0-4998-8992-61110ea2d66f", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def exit_place_func_nb(c, index, wait_td):\n", " last_i = -1\n", " for out_i in range(len(c.out)):\n", " i = c.from_i + out_i\n", " if index[i] >= index[c.from_i] + wait_td:\n", " c.out[out_i] = True\n", " last_i = out_i\n", " break\n", " return last_i\n", "\n", "exits = entries.vbt.signals.generate_exits(\n", " exit_place_func_nb,\n", " vbt.dt.to_ns(entries.index),\n", " vbt.dt.to_ns(vbt.timedelta(\"7d\")),\n", " wait=0\n", ")\n", "exits.index[exits.any(axis=1)]" ] }, { "cell_type": "code", "execution_count": null, "id": "da4bdca3-d5e6-47f4-bf53-9ac89228df0a", "metadata": {}, "outputs": [], "source": [ "entries = symbol_wrapper.fill(False)\n", "entries.vbt.set(True, every=\"5d\", inplace=True)\n", "exits = entries.vbt.signals.generate_exits(\n", " exit_place_func_nb,\n", " vbt.dt.to_ns(entries.index),\n", " vbt.dt.to_ns(vbt.timedelta(\"7d\")),\n", " wait=0\n", ")\n", "exits.index[exits.any(axis=1)]" ] }, { "cell_type": "code", "execution_count": null, "id": "6690a670-24a5-4eb9-8552-16f845ab3c50", "metadata": {}, "outputs": [], "source": [ "exits = entries.vbt.signals.generate_exits(\n", " exit_place_func_nb,\n", " vbt.dt.to_ns(entries.index),\n", " vbt.dt.to_ns(vbt.timedelta(\"7d\")),\n", " wait=0,\n", " until_next=False\n", ")\n", "exits.index[exits.any(axis=1)]" ] }, { "cell_type": "code", "execution_count": null, "id": "b3729138-f008-4371-883f-31240e1aadb3", "metadata": {}, "outputs": [], "source": [ "exits = entries.vbt.signals.generate_exits(\n", " exit_place_func_nb,\n", " vbt.dt.to_ns(entries.index),\n", " vbt.dt.to_ns(vbt.timedelta(\"7d\")),\n", " wait=0,\n", " until_next=False,\n", " skip_until_exit=True\n", ")\n", "exits.index[exits.any(axis=1)]" ] }, { "cell_type": "code", "execution_count": null, "id": "939bb536-1973-4798-9f9e-bb65314a6f31", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def exit_place_func_nb(c, wait_td, index):\n", " last_i = -1\n", " for out_i in range(len(c.out)):\n", " i = c.from_i + out_i\n", " if index[i] >= index[c.from_i] + wait_td:\n", " c.out[out_i] = True\n", " last_i = out_i\n", " break\n", " return last_i\n", "\n", "ExitGenerator = vbt.SignalFactory(\n", " mode=\"exits\",\n", " param_names=[\"wait_td\"]\n", ").with_place_func(\n", " exit_place_func_nb=exit_place_func_nb,\n", " exit_settings=dict(\n", " pass_params=[\"wait_td\"],\n", " ),\n", " var_args=True,\n", " wait=0,\n", " until_next=False,\n", " skip_until_exit=True,\n", " param_settings=dict(\n", " wait_td=dict(\n", " post_index_func=lambda x: x.map(lambda y: str(vbt.timedelta(y)))\n", " )\n", " ),\n", ")\n", "exit_generator = ExitGenerator.run(\n", " entries,\n", " [\n", " vbt.timedelta(\"3d\").to_timedelta64(),\n", " vbt.timedelta(\"7d\").to_timedelta64()\n", " ],\n", " symbol_wrapper.index.values\n", ")\n", "exit_generator.exits.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "de964714-e321-4d79-bfe1-b466a087b293", "metadata": {}, "outputs": [], "source": [ "new_entries = exit_generator.entries.vbt.signals.first(\n", " reset_by=exit_generator.exits,\n", " allow_gaps=True, \n", ")\n", "new_entries.index[new_entries[(\"7 days 00:00:00\", \"BTCUSDT\")]]" ] }, { "cell_type": "markdown", "id": "5b97e203-9f56-4390-a5cd-30ee65beb85f", "metadata": {}, "source": [ "#### Both" ] }, { "cell_type": "code", "execution_count": null, "id": "d838e1e5-0060-4a04-8c33-4c1f96e701a4", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def entry_place_func_nb(c, low, close, th):\n", " if c.from_i == 0:\n", " c.out[0] = True\n", " return 0\n", " exit_price = close[c.from_i - c.wait, c.col]\n", " hit_price = exit_price * (1 - th)\n", " last_i = -1\n", " for out_i in range(len(c.out)):\n", " i = c.from_i + out_i\n", " if low[i, c.col] <= hit_price:\n", " c.out[out_i] = True\n", " last_i = out_i\n", " break\n", " return last_i\n", "\n", "@njit\n", "def exit_place_func_nb(c, high, close, th):\n", " entry_price = close[c.from_i - c.wait, c.col]\n", " hit_price = entry_price * (1 + th)\n", " last_i = -1\n", " for out_i in range(len(c.out)):\n", " i = c.from_i + out_i\n", " if high[i, c.col] >= hit_price:\n", " c.out[out_i] = True\n", " last_i = out_i\n", " break\n", " return last_i\n", "\n", "entries, exits = vbt.pd_acc.signals.generate_both(\n", " symbol_wrapper.shape,\n", " entry_place_func_nb=entry_place_func_nb,\n", " entry_place_args=(vbt.Rep(\"low\"), vbt.Rep(\"close\"), 0.1),\n", " exit_place_func_nb=exit_place_func_nb,\n", " exit_place_args=(vbt.Rep(\"high\"), vbt.Rep(\"close\"), 0.2),\n", " wrapper=symbol_wrapper,\n", " broadcast_named_args=dict(\n", " high=data.get(\"High\"),\n", " low=data.get(\"Low\"),\n", " close=data.get(\"Close\")\n", " ),\n", " broadcast_kwargs=dict(\n", " post_func=vbt.to_2d_array\n", " )\n", ")\n", "\n", "fig = data.plot(\n", " symbol=\"BTCUSDT\", \n", " ohlc_trace_kwargs=dict(opacity=0.5), \n", " plot_volume=False\n", ")\n", "entries[\"BTCUSDT\"].vbt.signals.plot_as_entries(\n", " y=data.get(\"Close\", \"BTCUSDT\"), fig=fig)\n", "exits[\"BTCUSDT\"].vbt.signals.plot_as_exits(\n", " y=data.get(\"Close\", \"BTCUSDT\"), fig=fig)\n", "fig.show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "1241ad12-8874-43f9-868f-337dba360343", "metadata": {}, "outputs": [], "source": [ "BothGenerator = vbt.SignalFactory(\n", " mode=\"both\",\n", " input_names=[\"high\", \"low\", \"close\"],\n", " param_names=[\"entry_th\", \"exit_th\"]\n", ").with_place_func(\n", " entry_place_func_nb=entry_place_func_nb,\n", " entry_settings=dict(\n", " pass_inputs=[\"low\", \"close\"],\n", " pass_params=[\"entry_th\"],\n", " ),\n", " exit_place_func_nb=exit_place_func_nb,\n", " exit_settings=dict(\n", " pass_inputs=[\"high\", \"close\"],\n", " pass_params=[\"exit_th\"],\n", " )\n", ")\n", "both_generator = BothGenerator.run(\n", " data.get(\"High\"),\n", " data.get(\"Low\"),\n", " data.get(\"Close\"),\n", " [0.1, 0.2],\n", " [0.2, 0.3],\n", " param_product=True\n", ")\n", "fig = data.plot(\n", " symbol=\"BTCUSDT\", \n", " ohlc_trace_kwargs=dict(opacity=0.5), \n", " plot_volume=False\n", ")\n", "both_generator.plot(\n", " column=(0.1, 0.3, \"BTCUSDT\"), \n", " entry_y=data.get(\"Close\", \"BTCUSDT\"), \n", " exit_y=data.get(\"Close\", \"BTCUSDT\"), \n", " fig=fig\n", ").show_svg()" ] }, { "cell_type": "markdown", "id": "b3e76f00-2927-4af1-90e3-2c288f81225a", "metadata": {}, "source": [ "#### Chained exits" ] }, { "cell_type": "code", "execution_count": null, "id": "92d515cf-394e-4b2a-b5c9-8a7c702e9f45", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def exit_place_func_nb(c, low, request_price, fill_price_out):\n", " _request_price = request_price[c.from_i - c.wait, c.col]\n", " last_i = -1\n", " for out_i in range(len(c.out)):\n", " i = c.from_i + out_i\n", " if low[i, c.col] <= _request_price:\n", " fill_price_out[i, c.col] = _request_price\n", " c.out[out_i] = True\n", " last_i = out_i\n", " break\n", " return last_i\n", "\n", "ChainGenerator = vbt.SignalFactory(\n", " mode=\"chain\",\n", " input_names=[\"low\", \"request_price\"],\n", " in_output_names=[\"fill_price_out\"]\n", ").with_place_func(\n", " exit_place_func_nb=exit_place_func_nb,\n", " exit_settings=dict(\n", " pass_inputs=[\"low\", \"request_price\"],\n", " pass_in_outputs=[\"fill_price_out\"],\n", " ),\n", " fill_price_out=np.nan\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "66e50dd1-4984-432a-8a3e-4ca8fa6a6a29", "metadata": {}, "outputs": [], "source": [ "fast_ma = vbt.talib(\"SMA\").run(\n", " data.get(\"Close\"), \n", " vbt.Default(10), \n", " short_name=\"fast_ma\"\n", ")\n", "slow_ma = vbt.talib(\"SMA\").run(\n", " data.get(\"Close\"), \n", " vbt.Default(20), \n", " short_name=\"slow_ma\"\n", ")\n", "entries = fast_ma.real_crossed_above(slow_ma)\n", "entries.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "45ad9fbd-9250-463a-b599-7c69eeb1247d", "metadata": {}, "outputs": [], "source": [ "chain_generator = ChainGenerator.run(\n", " entries,\n", " data.get(\"Low\"),\n", " data.get(\"Close\") * (1 - 0.1)\n", ")\n", "request_mask = chain_generator.new_entries\n", "request_mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "f7a80bb3-b778-45df-a375-0df6672cf36b", "metadata": {}, "outputs": [], "source": [ "request_price = chain_generator.request_price\n", "print(request_price[request_mask.any(axis=1)])" ] }, { "cell_type": "code", "execution_count": null, "id": "bd9c6dd2-5da2-437f-b750-050b031160a1", "metadata": {}, "outputs": [], "source": [ "fill_mask = chain_generator.exits\n", "fill_mask.sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "0691fbf4-4b08-43c0-a8ae-6075bdf0af12", "metadata": {}, "outputs": [], "source": [ "fill_price = chain_generator.fill_price_out\n", "print(fill_price[fill_mask.any(axis=1)])" ] }, { "cell_type": "markdown", "id": "0566319a-0cb4-4dd1-81f8-3ed7d2d5f9d6", "metadata": {}, "source": [ "### Preset generators" ] }, { "cell_type": "markdown", "id": "07b9bd34-8bc7-4383-9b3b-13d8fbb7ae99", "metadata": {}, "source": [ "#### Random" ] }, { "cell_type": "code", "execution_count": null, "id": "0081e390-e918-4edb-ac65-297da562a4a2", "metadata": {}, "outputs": [], "source": [ "btcusdt_wrapper = symbol_wrapper[\"BTCUSDT\"]\n", "mask = vbt.pd_acc.signals.generate_random(\n", " btcusdt_wrapper.shape,\n", " prob=1 / 10,\n", " wrapper=btcusdt_wrapper,\n", " seed=42\n", ")\n", "mask_index = mask.index[mask]\n", "(mask_index[1:] - mask_index[:-1]).mean()" ] }, { "cell_type": "code", "execution_count": null, "id": "9cca388d-e1d0-4c83-a86b-ec7087d34e05", "metadata": {}, "outputs": [], "source": [ "monday_mask = btcusdt_wrapper.fill(False)\n", "monday_mask.vbt.set(True, every=\"monday\", inplace=True)\n", "mask = monday_mask.vbt.signals.generate_random_exits(wait=0)\n", "mask_index = mask.index[mask]\n", "mask_index.strftime(\"%W %A\")" ] }, { "cell_type": "code", "execution_count": null, "id": "5df8383a-7c16-4d54-8b7a-6695d9b24546", "metadata": {}, "outputs": [], "source": [ "prob = np.linspace(0, 1, len(symbol_wrapper.index))\n", "rprob = vbt.RPROB.run(\n", " symbol_wrapper.shape,\n", " vbt.Default(vbt.to_2d_pr_array(prob)),\n", " seed=42,\n", " input_index=symbol_wrapper.index,\n", " input_columns=symbol_wrapper.columns\n", ")\n", "rprob.entries.astype(int).vbt.ts_heatmap().show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "2ecf3d7f-ecb2-4cda-bd89-756d0a278c4c", "metadata": {}, "outputs": [], "source": [ "rprob = vbt.RPROB.run(\n", " symbol_wrapper.shape,\n", " [0.5, vbt.to_2d_pr_array(prob)],\n", " seed=42,\n", " input_index=symbol_wrapper.index,\n", " input_columns=symbol_wrapper.columns\n", ")\n", "rprob.entries.sum()" ] }, { "cell_type": "markdown", "id": "1646593e-9669-4757-8df9-3713a8e46ad6", "metadata": {}, "source": [ "#### Stops" ] }, { "cell_type": "code", "execution_count": null, "id": "ecf076e4-697a-4da7-8afa-287820c2c1da", "metadata": {}, "outputs": [], "source": [ "new_entries, exits = entries.vbt.signals.generate_stop_exits(\n", " data.get(\"Close\"),\n", " data.get(\"High\"),\n", " stop=0.1,\n", " chain=True\n", ")\n", "print(new_entries[new_entries.any(axis=1)])" ] }, { "cell_type": "code", "execution_count": null, "id": "fe64925a-0fc3-42fa-b128-7f92cb650456", "metadata": {}, "outputs": [], "source": [ "print(exits[exits.any(axis=1)])" ] }, { "cell_type": "code", "execution_count": null, "id": "e84b01df-1e52-4b51-8d91-5a057bed798a", "metadata": {}, "outputs": [], "source": [ "out_dict = {}\n", "new_entries, exits = entries.vbt.signals.generate_stop_exits(\n", " data.get(\"Close\"),\n", " data.get(\"High\"),\n", " stop=0.1,\n", " chain=True,\n", " out_dict=out_dict\n", ")\n", "print(out_dict[\"stop_ts\"][exits.any(axis=1)])" ] }, { "cell_type": "code", "execution_count": null, "id": "92b3b055-6b0d-4968-8042-943ad3612b38", "metadata": {}, "outputs": [], "source": [ "stcx = vbt.STCX.run(\n", " entries,\n", " data.get(\"Open\"),\n", " ts=data.get(\"Low\"),\n", " follow_ts=data.get(\"High\"),\n", " stop=-0.1,\n", " trailing=[False, True],\n", " wait=0\n", ")\n", "fig = data.plot(\n", " symbol=\"BTCUSDT\", \n", " ohlc_trace_kwargs=dict(opacity=0.5), \n", " plot_volume=False\n", ")\n", "stcx.plot(\n", " column=(-0.1, True, \"BTCUSDT\"), \n", " entry_y=\"entry_ts\",\n", " exit_y=\"stop_ts\", \n", " fig=fig\n", ").show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "ef68d113-7f90-4b68-810b-d31f6f13e1ea", "metadata": {}, "outputs": [], "source": [ "ohlcstcx = vbt.OHLCSTCX.run(\n", " entries,\n", " data.get(\"Close\"),\n", " data.get(\"Open\"),\n", " data.get(\"High\"),\n", " data.get(\"Low\"),\n", " data.get(\"Close\"),\n", " sl_stop=vbt.Default(0.1),\n", " tsl_stop=vbt.Default(0.15),\n", " is_entry_open=False\n", ")\n", "ohlcstcx.plot(column=(\"BTCUSDT\")).show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "222b01c5-3d86-4c5a-9c1e-dbfdd568407f", "metadata": {}, "outputs": [], "source": [ "print(ohlcstcx.stop_type_readable[ohlcstcx.exits.any(axis=1)])" ] }, { "cell_type": "code", "execution_count": null, "id": "7b7d0fdc-e793-46be-8559-398c418ccffc", "metadata": {}, "outputs": [], "source": [ "ohlcstcx = vbt.OHLCSTCX.run(\n", " entries,\n", " data.get(\"Close\"),\n", " sl_stop=vbt.Default(0.1),\n", " tsl_stop=vbt.Default(0.15),\n", " is_entry_open=False\n", ")\n", "ohlcstcx.plot(column=(\"BTCUSDT\")).show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "04992035-5ab7-47c5-b4c1-468bc22221ce", "metadata": {}, "outputs": [], "source": [ "entry_pos_rank = entries.vbt.signals.pos_rank(allow_gaps=True)\n", "short_entries = (entry_pos_rank >= 0) & (entry_pos_rank % 2 == 1)\n", "\n", "ohlcstcx = vbt.OHLCSTCX.run(\n", " entries,\n", " data.get(\"Close\"),\n", " data.get(\"Open\"),\n", " data.get(\"High\"),\n", " data.get(\"Low\"),\n", " data.get(\"Close\"),\n", " tsl_th=vbt.Default(0.2),\n", " tsl_stop=vbt.Default(0.1),\n", " reverse=vbt.Default(short_entries),\n", " is_entry_open=False\n", ")\n", "ohlcstcx.plot(column=(\"BTCUSDT\")).show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "9d5b66ff-5a7f-46da-b515-a8d84c1c2334", "metadata": {}, "outputs": [], "source": [ "long_entries = ohlcstcx.new_entries.vbt & (~short_entries)\n", "long_exits = ohlcstcx.exits.vbt.signals.first_after(long_entries)\n", "short_entries = ohlcstcx.new_entries.vbt & short_entries\n", "short_exits = ohlcstcx.exits.vbt.signals.first_after(short_entries)" ] }, { "cell_type": "code", "execution_count": null, "id": "ad0ad046-3df7-4872-b943-03049982ce10", "metadata": {}, "outputs": [], "source": [ "fig = data.plot(\n", " symbol=\"BTCUSDT\", \n", " ohlc_trace_kwargs=dict(opacity=0.5), \n", " plot_volume=False\n", ")\n", "long_entries[\"BTCUSDT\"].vbt.signals.plot_as_entries(\n", " ohlcstcx.entry_price[\"BTCUSDT\"],\n", " trace_kwargs=dict(marker=dict(color=\"limegreen\"), name=\"Long entries\"), \n", " fig=fig\n", ")\n", "long_exits[\"BTCUSDT\"].vbt.signals.plot_as_exits(\n", " ohlcstcx.stop_price[\"BTCUSDT\"],\n", " trace_kwargs=dict(marker=dict(color=\"orange\"), name=\"Long exits\"),\n", " fig=fig\n", ")\n", "short_entries[\"BTCUSDT\"].vbt.signals.plot_as_entries(\n", " ohlcstcx.entry_price[\"BTCUSDT\"],\n", " trace_kwargs=dict(marker=dict(color=\"magenta\"), name=\"Short entries\"),\n", " fig=fig\n", ")\n", "short_exits[\"BTCUSDT\"].vbt.signals.plot_as_exits(\n", " ohlcstcx.stop_price[\"BTCUSDT\"],\n", " trace_kwargs=dict(marker=dict(color=\"red\"), name=\"Short exits\"),\n", " fig=fig\n", ").show_svg()" ] }, { "cell_type": "markdown", "id": "bf356e79-4c3b-4819-ad6e-7f5c556be5fd", "metadata": {}, "source": [ "## Pre-analysis" ] }, { "cell_type": "markdown", "id": "30097f36-14d4-4a9b-bad9-d0cf8b6668f1", "metadata": {}, "source": [ "### Ranking" ] }, { "cell_type": "code", "execution_count": null, "id": "13ca8377-310f-4240-babf-5c99023e3d9f", "metadata": {}, "outputs": [], "source": [ "@njit\n", "def rank_func_nb(c):\n", " if c.sig_in_part_cnt == 1:\n", " return 1\n", " return 0\n", "\n", "sample_mask = pd.Series([True, True, False, True, True])\n", "ranked = sample_mask.vbt.signals.rank(rank_func_nb)\n", "ranked" ] }, { "cell_type": "code", "execution_count": null, "id": "7c44fcd0-d678-4ad8-8ef1-99d0f91aad40", "metadata": {}, "outputs": [], "source": [ "ranked == 1" ] }, { "cell_type": "code", "execution_count": null, "id": "693bd2d7-24bf-4c0c-80e4-8665b1649d2d", "metadata": {}, "outputs": [], "source": [ "ranked = sample_mask.vbt.signals.rank(rank_func_nb, after_false=True)\n", "ranked == 1" ] }, { "cell_type": "code", "execution_count": null, "id": "2ba16d6f-f5fa-4bca-b8a5-fa4891c10587", "metadata": {}, "outputs": [], "source": [ "sample_entries = pd.Series([True, True, True, True, True])\n", "sample_exits = pd.Series([False, False, True, False, False])\n", "ranked = sample_entries.vbt.signals.rank(\n", " rank_func_nb, \n", " reset_by=sample_exits\n", ")\n", "ranked == 1" ] }, { "cell_type": "code", "execution_count": null, "id": "caff048c-a2ae-4e1f-ab1c-15c60a87a7ec", "metadata": {}, "outputs": [], "source": [ "ranked = sample_entries.vbt.signals.rank(\n", " rank_func_nb, \n", " reset_by=sample_exits,\n", " after_reset=True\n", ")\n", "ranked == 1" ] }, { "cell_type": "markdown", "id": "a973bf32-e451-4786-867e-5c90c82bc6f5", "metadata": {}, "source": [ "#### Preset rankers" ] }, { "cell_type": "code", "execution_count": null, "id": "b0e2b978-480f-4149-8b31-63004f9ef9b9", "metadata": {}, "outputs": [], "source": [ "sample_mask = pd.Series([True, True, False, True, True])\n", "ranked = sample_mask.vbt.signals.pos_rank()\n", "ranked" ] }, { "cell_type": "code", "execution_count": null, "id": "6415bbd4-2e5d-4484-ab21-0478566513c8", "metadata": {}, "outputs": [], "source": [ "ranked == 1" ] }, { "cell_type": "code", "execution_count": null, "id": "d4b19ce3-2a0e-47ff-bc1b-c1a3695cbcaa", "metadata": {}, "outputs": [], "source": [ "ranked = sample_mask.vbt.signals.pos_rank(allow_gaps=True)\n", "ranked" ] }, { "cell_type": "code", "execution_count": null, "id": "369d7a73-c8cb-4579-8f42-28510b948f63", "metadata": {}, "outputs": [], "source": [ "(ranked > -1) & (ranked % 2 == 1)" ] }, { "cell_type": "code", "execution_count": null, "id": "50d51cf9-c42b-4468-a54f-be011327e474", "metadata": {}, "outputs": [], "source": [ "ranked = sample_mask.vbt.signals.partition_pos_rank(allow_gaps=True)\n", "ranked" ] }, { "cell_type": "code", "execution_count": null, "id": "7d4864bb-e549-4016-bced-cd39ca4b5b70", "metadata": {}, "outputs": [], "source": [ "ranked == 1" ] }, { "cell_type": "code", "execution_count": null, "id": "c624d629-f1fc-4c33-ad43-5c85496cb483", "metadata": {}, "outputs": [], "source": [ "entry_cond1 = data.get(\"Low\") < bb.lowerband\n", "entry_cond2 = bandwidth > 0.3\n", "entry_cond3 = data.get(\"High\") > bb.upperband\n", "entry_cond4 = bandwidth < 0.15\n", "entries = (entry_cond1 & entry_cond2) | (entry_cond3 & entry_cond4)\n", "\n", "entries.vbt.signals.from_nth(0).sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "a8b250d1-ba86-44ba-b532-cd42d261545a", "metadata": {}, "outputs": [], "source": [ "entries.vbt.signals.from_nth(1).sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "047fbe11-5da9-439f-b5c5-b1109f32fcbf", "metadata": {}, "outputs": [], "source": [ "entries.vbt.signals.from_nth(2).sum()" ] }, { "cell_type": "code", "execution_count": null, "id": "80f7a1d3-16eb-4aee-bb06-91cc09556a8e", "metadata": {}, "outputs": [], "source": [ "exit_cond1 = data.get(\"High\") > bb.upperband\n", "exit_cond2 = bandwidth > 0.3\n", "exit_cond3 = data.get(\"Low\") < bb.lowerband\n", "exit_cond4 = bandwidth < 0.15\n", "exits = (exit_cond1 & exit_cond2) | (exit_cond3 & exit_cond4)" ] }, { "cell_type": "code", "execution_count": null, "id": "24028cc0-e4f5-4025-a107-38d92c01c4c1", "metadata": {}, "outputs": [], "source": [ "exits.vbt.signals.pos_rank_after(entries, reset_wait=0).max() + 1" ] }, { "cell_type": "code", "execution_count": null, "id": "3a4b2be2-8e5c-463a-9655-1f420b0017c9", "metadata": {}, "outputs": [], "source": [ "entries.vbt.signals.pos_rank_after(exits).max() + 1" ] }, { "cell_type": "code", "execution_count": null, "id": "c0beeb7b-b5c4-4f48-9791-8a0828e956ec", "metadata": {}, "outputs": [], "source": [ "ranked = exits.vbt.signals.pos_rank_after(entries, reset_wait=0)\n", "highest_ranked = ranked == ranked.max()\n", "print(ranked[highest_ranked.any(axis=1)])" ] }, { "cell_type": "code", "execution_count": null, "id": "805ef372-1748-4dc4-825a-46e3f55fb3f7", "metadata": {}, "outputs": [], "source": [ "exits_after = exits.vbt.signals.from_nth_after(0, entries, reset_wait=0)\n", "(exits ^ exits_after).sum()" ] }, { "cell_type": "markdown", "id": "0db70553-be70-4a1c-9ce4-92f906ba291b", "metadata": {}, "source": [ "#### Mapped ranks" ] }, { "cell_type": "code", "execution_count": null, "id": "9aeaa467-6a09-4a45-8c4a-abd587c7148d", "metadata": {}, "outputs": [], "source": [ "mask = bandwidth.vbt > vbt.Param(np.arange(1, 10) / 10, name=\"bw_th\")\n", "mapped_ranks = mask.vbt.signals.pos_rank(as_mapped=True)\n", "mapped_ranks.max(group_by=vbt.ExceptLevel(\"symbol\"))" ] }, { "cell_type": "markdown", "id": "5ef8deef-a5d9-40b0-9819-78dbac8f5b27", "metadata": {}, "source": [ "### Cleaning" ] }, { "cell_type": "code", "execution_count": null, "id": "402c23ed-0a63-4b9c-b215-ac7dacf083e5", "metadata": {}, "outputs": [], "source": [ "new_exits = exits.vbt.signals.first_after(entries, reset_wait=0)\n", "new_entries = entries.vbt.signals.first_after(exits)" ] }, { "cell_type": "code", "execution_count": null, "id": "b79491ce-3291-46ee-a2a6-5aa3ac9a2de3", "metadata": {}, "outputs": [], "source": [ "symbol = \"ETHUSDT\"\n", "fig = data.plot(\n", " symbol=symbol, \n", " ohlc_trace_kwargs=dict(opacity=0.5), \n", " plot_volume=False\n", ")\n", "entries[symbol].vbt.signals.plot_as_entries(\n", " y=data.get(\"Close\", symbol), fig=fig)\n", "exits[symbol].vbt.signals.plot_as_exits(\n", " y=data.get(\"Close\", symbol), fig=fig)\n", "new_entries[symbol].vbt.signals.plot_as_entry_marks(\n", " y=data.get(\"Close\", symbol), fig=fig, \n", " trace_kwargs=dict(name=\"New entries\"))\n", "new_exits[symbol].vbt.signals.plot_as_exit_marks(\n", " y=data.get(\"Close\", symbol), fig=fig, \n", " trace_kwargs=dict(name=\"New exits\")).show_svg()" ] }, { "cell_type": "code", "execution_count": null, "id": "883ff6e0-7777-487e-b58b-80bc38f2634c", "metadata": {}, "outputs": [], "source": [ "new_entries, new_exits = entries.vbt.signals.clean(exits)" ] }, { "cell_type": "markdown", "id": "82b4e080-808a-4ab3-a700-81ecf0b75d98", "metadata": {}, "source": [ "### Duration" ] }, { "cell_type": "code", "execution_count": null, "id": "1b9ac018-2643-4ef5-aded-a617062e726a", "metadata": {}, "outputs": [], "source": [ "ranges = entries.vbt.signals.between_ranges()\n", "print(ranges.records)" ] }, { "cell_type": "code", "execution_count": null, "id": "aa971658-1359-403b-9e65-a4a3ce390751", "metadata": {}, "outputs": [], "source": [ "ranges.start_idx.min(wrap_kwargs=dict(to_index=True))" ] }, { "cell_type": "code", "execution_count": null, "id": "35efd36c-c2f2-43c0-af21-9df040ec7cc8", "metadata": {}, "outputs": [], "source": [ "print(ranges.duration.describe(wrap_kwargs=dict(to_timedelta=True)))" ] }, { "cell_type": "code", "execution_count": null, "id": "8b944e41-0a8f-498e-82ce-30c46d4b2db6", "metadata": {}, "outputs": [], "source": [ "ranges = entries.vbt.signals.between_ranges(target=exits)\n", "ranges.avg_duration" ] }, { "cell_type": "code", "execution_count": null, "id": "7b920e56-d16d-400a-9382-a23d63162fda", "metadata": {}, "outputs": [], "source": [ "new_ranges = new_entries.vbt.signals.between_ranges(target=new_exits)\n", "new_ranges.avg_duration" ] }, { "cell_type": "code", "execution_count": null, "id": "8528ab4c-22b6-4af0-87f5-5c6063bf68c4", "metadata": {}, "outputs": [], "source": [ "ranges = entries.vbt.signals.between_ranges(target=exits, relation=\"manyone\")\n", "ranges.avg_duration" ] }, { "cell_type": "code", "execution_count": null, "id": "a047b2b8-a27c-4ac0-b124-9f6cfbe513b4", "metadata": {}, "outputs": [], "source": [ "new_ranges = new_entries.vbt.signals.between_ranges(target=new_exits, relation=\"manyone\")\n", "new_ranges.avg_duration" ] }, { "cell_type": "code", "execution_count": null, "id": "3ab9a53b-2f2e-4601-8e58-c04d86892ee5", "metadata": {}, "outputs": [], "source": [ "ranges = entries.vbt.signals.partition_ranges()\n", "print(ranges.duration.describe())" ] }, { "cell_type": "code", "execution_count": null, "id": "c35fb94f-04e8-4e6f-a2a4-1f1542d02edb", "metadata": {}, "outputs": [], "source": [ "new_ranges = new_entries.vbt.signals.partition_ranges()\n", "print(new_ranges.duration.describe())" ] }, { "cell_type": "code", "execution_count": null, "id": "5cdef2ce-f8ef-44c6-8eac-42172560d93c", "metadata": {}, "outputs": [], "source": [ "ranges = entries.vbt.signals.between_partition_ranges()\n", "print(ranges.duration.describe(wrap_kwargs=dict(to_timedelta=True)))" ] }, { "cell_type": "markdown", "id": "541e5a28-aa4f-4056-b4de-6ea7ecdb9064", "metadata": {}, "source": [ "### Overview" ] }, { "cell_type": "code", "execution_count": null, "id": "fe7bcbc0-4300-4c7b-a4d6-17e903bb18bb", "metadata": {}, "outputs": [], "source": [ "entries.vbt.signals.stats(column=\"BTCUSDT\")" ] }, { "cell_type": "code", "execution_count": null, "id": "a650256c-a112-4ef2-9480-b867ae5b1892", "metadata": {}, "outputs": [], "source": [ "entries.vbt.signals.stats(column=\"BTCUSDT\", settings=dict(target=exits))" ] }, { "cell_type": "code", "execution_count": null, "id": "1cf7ad48-e516-4f88-897b-29cc5a0f7d64", "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 }