{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Activating profile profile1\n",
       "
\n" ], "text/plain": [ "Activating profile profile1\n" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Realtime Websocket connection will use FEED: sip and credential of ACCOUNT1\n" ] }, { "data": { "text/html": [ "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", "
\n", "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", "
\n", "
\n", "This is the init_notebook_mode cell from ITables v2.0.1
\n", "(you should not see this message - is your notebook trusted?)\n", "
\n", "
\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from v2realbot.tools.loadbatch import load_batch\n", "from v2realbot.utils.utils import zoneNY\n", "import pandas as pd\n", "import numpy as np\n", "import vectorbtpro as vbt\n", "from itables import init_notebook_mode, show\n", "import datetime\n", "from itertools import product\n", "from v2realbot.config import ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, DATA_DIR\n", "\n", "init_notebook_mode(all_interactive=True)\n", "\n", "vbt.settings.set_theme(\"dark\")\n", "vbt.settings['plotting']['layout']['width'] = 1280\n", "vbt.settings.plotting.auto_rangebreaks = True\n", "# Set the option to display with pagination\n", "pd.set_option('display.notebook_repr_html', True)\n", "pd.set_option('display.max_rows', 10) # Number of rows per page\n", "\n", "# Define the market open and close times\n", "market_open = datetime.time(9, 30)\n", "market_close = datetime.time(16, 0)\n", "entry_window_opens = 1\n", "entry_window_closes = 370\n", "\n", "forced_exit_start = 380\n", "forced_exit_end = 390\n", "\n", "#LOAD FROM BATCH\n", "# res, df = load_batch(batch_id=\"0fb5043a\", #0fb5043a bde6d0be\n", "# space_resolution_evenly=False,\n", "# indicators_columns=[\"Rsi14\"],\n", "# main_session_only=True,\n", "# verbose = False)\n", "# if res < 0:\n", "# print(\"Error\" + str(res) + str(df))\n", "# df = df[\"bars\"]\n", "\n", "# #df\n", "\n", "# basic_data = vbt.Data.from_data(vbt.symbol_dict({\"BAC\": df}), tz_convert=zoneNY)\n", "# #m1_data = basic_data[['Open', 'High', 'Low', 'Close', 'Volume']]\n", "# basic_data = basic_data.transform(lambda df: df.between_time('09:30', '16:00'))\n", "\n", "#LOAD FROM PARQUET\n", "#LOAD FROM PARQUET\n", "#list all files is dir directory with parquet extension\n", "dir = DATA_DIR + \"/notebooks/\"\n", "import os\n", "files = [f for f in os.listdir(dir) if f.endswith(\".parquet\")]\n", "#print('\\n'.join(map(str, files)))\n", "file_name = \"ohlcv_df-SPY-2024-01-01T09:30:00-2024-05-14T16:00:00.parquet\"\n", "ohlcv_df = pd.read_parquet(dir+file_name,engine='pyarrow')\n", "basic_data = vbt.Data.from_data(vbt.symbol_dict({\"SPY\": ohlcv_df}), tz_convert=zoneNY)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#parameters (primary y line, secondary y line, close)\n", "def plot_2y_close(priminds, secinds, close):\n", " fig = vbt.make_subplots(rows=1, cols=1, shared_xaxes=True, specs=[[{\"secondary_y\": True}]], vertical_spacing=0.02, subplot_titles=(\"MOM\", \"Price\" ))\n", " close.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False), trace_kwargs=dict(line=dict(color=\"blue\")))\n", " for ind in priminds:\n", " ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False))\n", " for ind in secinds:\n", " ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True))\n", " return fig\n", "\n", "# close = basic_data.xloc[\"09:30\":\"10:00\"].close" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "9d94a0df1e9a43a9a97a8952296deb13", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/2000 [00:00\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " Total Return [%]\n", " Max Drawdown [%]\n", " Total Trades\n", " Win Rate [%]\n", " Expectancy\n", " \n", " \n", " entry_window_closes\n", " mom_timeperiod\n", " mom_th\n", " sl_stop\n", " tp_stop\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "
\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", "
\n", "
\n", "Loading ITables v2.0.1 from the init_notebook_mode cell...\n", "(need help?)\n", "
\n", "\n", "\n", "\n", "\n" ], "text/plain": [ " Total Return [%] \\\n", "entry_window_closes mom_timeperiod mom_th sl_stop tp_stop \n", "45 6 0.27 0.0062 0.0067 3.969612 \n", " 4 0.27 0.0047 0.0067 3.460137 \n", " 11 0.31 0.0037 0.0057 3.140355 \n", " 0.37 0.0047 0.0067 3.135768 \n", " 7 0.31 0.0047 0.0062 2.973553 \n", "... ... \n", " 8 0.03 0.0002 0.0027 -76.759273 \n", " 9 0.03 0.0002 0.0007 -77.608633 \n", " 8 0.01 0.0002 0.0062 -78.904337 \n", " 4 0.03 0.0002 0.0047 -79.812711 \n", " 3 0.01 0.0002 0.0037 -83.687503 \n", "\n", " Max Drawdown [%] \\\n", "entry_window_closes mom_timeperiod mom_th sl_stop tp_stop \n", "45 6 0.27 0.0062 0.0067 2.187609 \n", " 4 0.27 0.0047 0.0067 1.270021 \n", " 11 0.31 0.0037 0.0057 1.5553 \n", " 0.37 0.0047 0.0067 1.372389 \n", " 7 0.31 0.0047 0.0062 1.324725 \n", "... ... \n", " 8 0.03 0.0002 0.0027 76.759273 \n", " 9 0.03 0.0002 0.0007 77.608633 \n", " 8 0.01 0.0002 0.0062 78.904337 \n", " 4 0.03 0.0002 0.0047 79.81415 \n", " 3 0.01 0.0002 0.0037 83.687503 \n", "\n", " Total Trades \\\n", "entry_window_closes mom_timeperiod mom_th sl_stop tp_stop \n", "45 6 0.27 0.0062 0.0067 29 \n", " 4 0.27 0.0047 0.0067 21 \n", " 11 0.31 0.0037 0.0057 40 \n", " 0.37 0.0047 0.0067 22 \n", " 7 0.31 0.0047 0.0062 24 \n", "... ... \n", " 8 0.03 0.0002 0.0027 4740 \n", " 9 0.03 0.0002 0.0007 4786 \n", " 8 0.01 0.0002 0.0062 5065 \n", " 4 0.03 0.0002 0.0047 5245 \n", " 3 0.01 0.0002 0.0037 5933 \n", "\n", " Win Rate [%] \\\n", "entry_window_closes mom_timeperiod mom_th sl_stop tp_stop \n", "45 6 0.27 0.0062 0.0067 58.62069 \n", " 4 0.27 0.0047 0.0067 61.904762 \n", " 11 0.31 0.0037 0.0057 52.5 \n", " 0.37 0.0047 0.0067 54.545455 \n", " 7 0.31 0.0047 0.0062 54.166667 \n", "... ... \n", " 8 0.03 0.0002 0.0027 11.118143 \n", " 9 0.03 0.0002 0.0007 10.80234 \n", " 8 0.01 0.0002 0.0062 11.273445 \n", " 4 0.03 0.0002 0.0047 11.325071 \n", " 3 0.01 0.0002 0.0037 11.22535 \n", "\n", " Expectancy \n", "entry_window_closes mom_timeperiod mom_th sl_stop tp_stop \n", "45 6 0.27 0.0062 0.0067 0.136883 \n", " 4 0.27 0.0047 0.0067 0.164768 \n", " 11 0.31 0.0037 0.0057 0.078509 \n", " 0.37 0.0047 0.0067 0.142535 \n", " 7 0.31 0.0047 0.0062 0.123898 \n", "... ... \n", " 8 0.03 0.0002 0.0027 -0.016194 \n", " 9 0.03 0.0002 0.0007 -0.016216 \n", " 8 0.01 0.0002 0.0062 -0.015578 \n", " 4 0.03 0.0002 0.0047 -0.015217 \n", " 3 0.01 0.0002 0.0037 -0.014105 \n", "\n", "[2000 rows x 5 columns]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#PIPELINE - FOR - LOOP\n", "\n", "#indicator parameters\n", "mom_timeperiod = list(range(2, 12))\n", "\n", "#uzavreni okna od 1 do 200\n", "#entry_window_closes = list(range(2, 50, 3))\n", "entry_window_closes = [5, 10, 30, 45]\n", "#entry_window_closes = 30\n", "#threshold entries parameters\n", "#long\n", "mom_th = np.round(np.arange(0.01, 0.5 + 0.02, 0.02),4).tolist()#-0.02\n", "# short\n", "#mom_th = np.round(np.arange(-0.01, -0.3 - 0.02, -0.02),4).tolist()#-0.02\n", "roc_th = np.round(np.arange(-0.2, -0.8 - 0.05, -0.05),4).tolist()#-0.2\n", "#print(mom_th, roc_th)\n", "\n", "#portfolio simulation parameters\n", "sl_stop =np.round(np.arange(0.02/100, 0.7/100, 0.05/100),4).tolist()\n", "tp_stop = np.round(np.arange(0.02/100, 0.7/100, 0.05/100),4).tolist()\n", "\n", "combs = list(product(mom_timeperiod, mom_th, roc_th, sl_stop, tp_stop))\n", "\n", "@vbt.parameterized(merge_func = \"concat\", random_subset = 2000, show_progress=True) \n", "def test_strat(entry_window_closes=60,\n", " mom_timeperiod=2,\n", " mom_th=-0.04,\n", " #roc_th=-0.2,\n", " sl_stop=0.19/100,\n", " tp_stop=0.19/100):\n", " # mom_timeperiod=2\n", " # mom_th=-0.06\n", " # roc_th=-0.2\n", " # sl_stop=0.04/100\n", " # tp_stop=0.04/100\n", "\n", " momshort = vbt.indicator(\"talib:MOM\").run(basic_data.close, timeperiod=mom_timeperiod, short_name = \"slope_short\")\n", " rocp = vbt.indicator(\"talib:ROC\").run(basic_data.close, short_name = \"rocp\")\n", " #rate of change + momentum\n", "\n", " #momshort.plot rocp.real_crossed_below(roc_th) & \n", " #short_signal = momshort.real_crossed_below(mom_th)\n", " long_signal = momshort.real_crossed_above(mom_th)\n", " # print(\"short signal\")\n", " # print(short_signal.value_counts())\n", "\n", " #forced_exit = pd.Series(False, index=close.index)\n", " forced_exit = basic_data.symbol_wrapper.fill(False)\n", " #entry_window_open = pd.Series(False, index=close.index)\n", " entry_window_open= basic_data.symbol_wrapper.fill(False)\n", "\n", " #print(entry_window_closes, \"entry window closes\")\n", " # Calculate the time difference in minutes from market open for each timestamp\n", " elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n", "\n", " entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n", "\n", " #print(entry_window_open.value_counts())\n", "\n", " forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n", " #short_entries = (short_signal & entry_window_open)\n", " #short_exits = forced_exit\n", " entries = (long_signal & entry_window_open)\n", " exits = forced_exit\n", " #long_entries.info()\n", " #number of trues and falses in long_entries\n", " #print(short_exits.value_counts())\n", " #print(short_entries.value_counts())\n", "\n", " #fig = plot_2y_close([],[momshort, rocp], close)\n", " #short_signal.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False))\n", " #print(sl_stop)\n", " #tsl_th=sl_stop, \n", " #short_entries=short_entries, short_exits=short_exits,\n", " pf = vbt.Portfolio.from_signals(close=basic_data.close, entries=entries, exits=exits, tsl_stop=sl_stop, tp_stop = tp_stop, fees=0.0167/100, freq=\"1s\", price=\"close\") #sl_stop=sl_stop, tp_stop = sl_stop,\n", " \n", " return pf.stats([\n", " 'total_return',\n", " 'max_dd', \n", " 'total_trades', \n", " 'win_rate', \n", " 'expectancy'\n", " ])\n", "\n", "pf_results = test_strat(vbt.Param(entry_window_closes),\n", " vbt.Param(mom_timeperiod),\n", " vbt.Param(mom_th),\n", " #vbt.Param(roc_th)\n", " vbt.Param(sl_stop),\n", " vbt.Param(tp_stop, condition=\"tp_stop > sl_stop\"))\n", "pf_results = pf_results.unstack(level=-1)\n", "pf_results.sort_values(by=[\"Total Return [%]\", \"Max Drawdown [%]\"], ascending=[False, True])\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "PosixPath('8tiscomb_tsl.pickle')" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#pf_results.load(\"10tiscomb.pickle\")\n", "#pf_results.info()\n", "\n", "vbt.save(pf_results, \"8tiscomb_tsl.pickle\")\n", "\n", "# pf_results = vbt.load(\"8tiscomb_tsl.pickle\")\n", "# pf_results\n" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "ename": "AttributeError", "evalue": "'PXDFAccessor' object has no attribute 'pairplot'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[13], line 12\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# parallel_coordinates method¶\u001b[39;00m\n\u001b[1;32m 2\u001b[0m \n\u001b[1;32m 3\u001b[0m \u001b[38;5;66;03m# attach_px_methods..plot_func(\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 8\u001b[0m \n\u001b[1;32m 9\u001b[0m \u001b[38;5;66;03m# pf_results.vbt.px.parallel_coordinates() #ocdf\u001b[39;00m\n\u001b[1;32m 11\u001b[0m res \u001b[38;5;241m=\u001b[39m pf_results\u001b[38;5;241m.\u001b[39mreset_index()\n\u001b[0;32m---> 12\u001b[0m \u001b[43mres\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvbt\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpx\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpairplot\u001b[49m()\n", "\u001b[0;31mAttributeError\u001b[0m: 'PXDFAccessor' object has no attribute 'pairplot'" ] } ], "source": [ "# parallel_coordinates method¶\n", "\n", "# attach_px_methods..plot_func(\n", "# *args,\n", "# layout=None,\n", "# **kwargs\n", "# )\n", "\n", "# pf_results.vbt.px.parallel_coordinates() #ocdf\n", "\n", "res = pf_results.reset_index()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "
entry_window_closesmom_timeperiodmom_thsl_stoptp_stopTotal Return [%]Max Drawdown [%]Total TradesWin Rate [%]Expectancy
\n", "\n", "
\n", "Loading ITables v2.0.1 from the init_notebook_mode cell...\n", "(need help?)
\n", "\n" ], "text/plain": [ " entry_window_closes mom_timeperiod mom_th sl_stop tp_stop \\\n", "0 3 2 -0.31 0.0003 0.0018 \n", "1 3 2 -0.31 0.0003 0.0068 \n", "2 3 2 -0.31 0.0013 0.0028 \n", "3 3 2 -0.31 0.0013 0.0058 \n", "4 3 2 -0.31 0.0018 0.0038 \n", "... ... ... ... ... ... \n", "7995 70 11 -0.01 0.0003 0.0018 \n", "7996 70 11 -0.01 0.0003 0.0038 \n", "7997 70 11 -0.01 0.0028 0.0038 \n", "7998 70 11 -0.01 0.0033 0.0053 \n", "7999 70 11 -0.01 0.0038 0.0053 \n", "\n", " Total Return [%] Max Drawdown [%] Total Trades Win Rate [%] Expectancy \n", "0 0.0 NaN 0 NaN NaN \n", "1 0.0 NaN 0 NaN NaN \n", "2 0.0 NaN 0 NaN NaN \n", "3 0.0 NaN 0 NaN NaN \n", "4 0.0 NaN 0 NaN NaN \n", "... ... ... ... ... ... \n", "7995 -60.724025 60.747552 4025 23.701863 -0.015087 \n", "7996 -59.557376 59.581603 4017 23.749066 -0.014826 \n", "7997 -13.574229 16.599702 451 33.259424 -0.030098 \n", "7998 -9.970892 14.327723 338 34.023669 -0.0295 \n", "7999 -7.135743 12.027996 280 34.642857 -0.025485 \n", "\n", "[8000 rows x 10 columns]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pf_results" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/8p/dwqnp65s0s77jdbm4_6z4vp80000gn/T/ipykernel_3935/927237224.py:13: FutureWarning: A value is trying to be set on a copy of a DataFrame or Series through chained assignment using an inplace method.\n", "The behavior will change in pandas 3.0. This inplace method will never work because the intermediate object on which we are setting values always behaves as a copy.\n", "\n", "For example, when doing 'df[col].method(value, inplace=True)', try using 'df.method({col: value}, inplace=True)' or df[col] = df[col].method(value) instead, to perform the operation inplace on the original object.\n", "\n", "\n", " pf_results['Max Drawdown [%]'].fillna(pf_results['Max Drawdown [%]'].median(), inplace=True)\n" ] } ], "source": [ "import pandas as pd\n", "from sklearn.decomposition import PCA\n", "from sklearn.preprocessing import StandardScaler\n", "import matplotlib.pyplot as plt\n", "\n", "# Assuming pf_results is your DataFrame\n", "# Convert columns to numeric, assuming NaNs where conversion fails\n", "metrics = ['Total Return [%]', 'Max Drawdown [%]', 'Total Trades']\n", "for metric in metrics:\n", " pf_results[metric] = pd.to_numeric(pf_results[metric], errors='coerce')\n", "\n", "# Handle missing values, for example filling with the median\n", "pf_results['Max Drawdown [%]'].fillna(pf_results['Max Drawdown [%]'].median(), inplace=True)\n", "\n", "# Extract the metrics into a new DataFrame\n", "data_for_pca = pf_results[metrics]\n", "\n", "# Standardize the data before applying PCA\n", "scaler = StandardScaler()\n", "data_scaled = scaler.fit_transform(data_for_pca)\n", "\n", "# Apply PCA\n", "pca = PCA(n_components=2) # Adjust components as needed\n", "principal_components = pca.fit_transform(data_scaled)\n", "\n", "# Create a DataFrame with the principal components\n", "pca_results = pd.DataFrame(data=principal_components, columns=['PC1', 'PC2'])\n", "\n", "# Visualize the results\n", "plt.figure(figsize=(8,6))\n", "plt.scatter(pca_results['PC1'], pca_results['PC2'], alpha=0.5)\n", "plt.xlabel('Principal Component 1')\n", "plt.ylabel('Principal Component 2')\n", "plt.title('PCA of Strategy Optimization Results')\n", "plt.grid(True)\n", "plt.savefig(\"ddd.png\")\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Check if there is any unnamed level and rename it\n", "if None in df.index.names:\n", " # Generate new names list replacing None with 'stat'\n", " new_names = ['stat' if name is None else name for name in df.index.names]\n", " df.index.set_names(new_names, inplace=True)\n", "\n", "rs= df\n", "\n", "rs.info()\n", "\n", "\n", "# # Now, 'stat' is the name of the previously unnamed level\n", "\n", "# # Filter for 'Total Return' assuming it is a correct identifier in the 'stat' level\n", "# total_return_series = df.xs('Total Return [%]', level='stat')\n", "\n", "# # Sort the Series to get the largest 'Total Return' values\n", "# sorted_series = total_return_series.sort_values(ascending=False)\n", "\n", "# # Print the sorted filtered data\n", "# sorted_series.head(20)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sorted_series.vbt.save()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#df.info()\n", "total_return_series = df.xs('Total Return [%]')\n", "sorted_series = total_return_series.sort_values(ascending=False)\n", "\n", "# Display the top N entries, e.g., top 5\n", "sorted_series.head(5)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "comb_stats_df.nlargest(10, 'Total Return [%]')\n", "#stats_df.info()\n", "\n", "\n", "8\t-0.06\t-0.2\t0.0028\t0.0048\t4.156254\n", "4 -0.02 -0.25 0.0028 0.0048 0.84433\n", "3 -0.02 -0.25 0.0033 0.0023 Total Return [%] 0.846753\n", "#2\t-0.04\t-0.2\t0.0019\t0.0019\n", "# 2\t-0.04\t-0.2\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n", "# 2\t-0.04\t-0.25\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n", "# 2\t-0.04\t-0.3\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n", "# 2\t-0.04\t-0.35\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n", "# 2\t-0.04\t-0.4\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n", "# 2\t-0.04\t-0.2\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853\n", "# 2\t-0.04\t-0.25\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853\n", "# 2\t-0.04\t-0.3\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853\n", "# 2\t-0.04\t-0.35\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853\n", "# 2\t-0.04\t-0.4\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pf.plot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "basic_data.symbols" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ ">>> def apply_func(ts, entries, exits, fastw, sloww, minp=None):\n", "... fast_ma = vbt.nb.rolling_mean_nb(ts, fastw, minp=minp)\n", "... slow_ma = vbt.nb.rolling_mean_nb(ts, sloww, minp=minp)\n", "... entries[:] = vbt.nb.crossed_above_nb(fast_ma, slow_ma) \n", "... exits[:] = vbt.nb.crossed_above_nb(slow_ma, fast_ma)\n", "... return (fast_ma, slow_ma) \n", "\n", ">>> CrossSig = vbt.IF(\n", "... class_name=\"CrossSig\",\n", "... input_names=['ts'],\n", "... in_output_names=['entries', 'exits'],\n", "... param_names=['fastw', 'sloww'],\n", "... output_names=['fast_ma', 'slow_ma']\n", "... ).with_apply_func(\n", "... apply_func,\n", "... in_output_settings=dict(\n", "... entries=dict(dtype=np.bool_), #initialize output with bool\n", "... exits=dict(dtype=np.bool_)\n", "... )\n", "... )\n", ">>> cross_sig = CrossSig.run(ts2, 2, 4)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#PIPELINE - parameters in one go\n", "\n", "\n", "#TOTO prepsat do FOR-LOOPu\n", "\n", "\n", "#indicator parameters\n", "mom_timeperiod = list(range(2, 6))\n", "\n", "#threshold entries parameters\n", "mom_th = np.round(np.arange(-0.02, -0.1 - 0.02, -0.02),4).tolist()#-0.02\n", "roc_th = np.round(np.arange(-0.2, -0.4 - 0.05, -0.05),4).tolist()#-0.2\n", "#print(mom_th, roc_th)\n", "#jejich product\n", "# mom_th_prod, roc_th_prod = zip(*product(mom_th, roc_th))\n", "\n", "# #convert threshold to vbt param\n", "# mom_th_index = vbt.Param(mom_th_prod, name='mom_th_th') \n", "# roc_th_index = vbt.Param(roc_th_prod, name='roc_th_th')\n", "\n", "mom_th = vbt.Param(mom_th, name='mom_th')\n", "roc_th = vbt.Param(roc_th, name='roc_th')\n", "\n", "#portfolio simulation parameters\n", "sl_stop = np.arange(0.03/100, 0.2/100, 0.02/100).tolist()\n", "# Using the round function\n", "sl_stop = [round(val, 4) for val in sl_stop]\n", "tp_stop = np.arange(0.03/100, 0.2/100, 0.02/100).tolist()\n", "# Using the round function\n", "tp_stop = [round(val, 4) for val in tp_stop]\n", "sl_stop = vbt.Param(sl_stop) #np.nan mean s no stoploss\n", "tp_stop = vbt.Param(tp_stop) #np.nan mean s no stoploss\n", "\n", "\n", "#def test_mom(window=14, mom_th=0.2, roc_th=0.2, sl_stop=0.03/100, tp_stop=0.03/100):\n", "#close = basic_data.xloc[\"09:30\":\"10:00\"].close\n", "momshort = vbt.indicator(\"talib:MOM\").run(basic_data.get(\"Close\"), timeperiod=mom_timeperiod, short_name = \"slope_short\")\n", "\n", "#ht_trendline = vbt.indicator(\"talib:HT_TRENDLINE\").run(close, short_name = \"httrendline\")\n", "rocp = vbt.indicator(\"talib:ROC\").run(basic_data.get(\"Close\"), short_name = \"rocp\")\n", "#rate of change + momentum\n", "\n", "rocp_signal = rocp.real_crossed_below(mom_th)\n", "mom_signal = momshort.real_crossed_below(roc_th)\n", "\n", "#mom_signal\n", "print(rocp_signal.info())\n", "print(mom_signal.info())\n", "#print(rocp.real)\n", "\n", "\n", "short_signal = (mom_signal.vbt & rocp_signal)\n", "\n", "# #short_signal = (rocp.real_crossed_below(roc_th_index) & momshort.real_crossed_below(mom_th_index))\n", "# forced_exit = m1_data.symbol_wrapper.fill(False)\n", "# entry_window_open= m1_data.symbol_wrapper.fill(False)\n", "\n", "\n", "# # Calculate the time difference in minutes from market open for each timestamp\n", "# elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n", "\n", "# entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n", "# forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n", "# short_entries = (short_signal & entry_window_open)\n", "# short_exits = forced_exit\n", "# #long_entries.info()\n", "# #number of trues and falses in long_entries\n", "# #short_exits.value_counts()\n", "# #short_entries.value_counts()\n", "\n", "\n", "# pf = vbt.Portfolio.from_signals(close=close, short_entries=short_entries, short_exits=short_exits, sl_stop=sl_stop, tp_stop = tp_stop, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# filter dates" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#filter na dny\n", "dates_of_interest = pd.to_datetime(['2024-04-22']).tz_localize('US/Eastern')\n", "filtered_df = df.loc[df.index.normalize().isin(dates_of_interest)]\n", "\n", "df = filtered_df\n", "df.info()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# import plotly.io as pio\n", "# pio.renderers.default = 'notebook'\n", "\n", "#naloadujeme do vbt symbol as column\n", "basic_data = vbt.Data.from_data({\"BAC\": df}, tz_convert=zoneNY)\n", "\n", "vbt.settings.plotting.auto_rangebreaks = True\n", "#basic_data.data[\"BAC\"].vbt.ohlcv.plot()\n", "\n", "#basic_data.data[\"BAC\"]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m1_data = basic_data[['Open', 'High', 'Low', 'Close', 'Volume']]\n", "\n", "m1_data.data[\"BAC\"]\n", "#m5_data = m1_data.resample(\"5T\")\n", "\n", "#m5_data.data[\"BAC\"].head(10)\n", "\n", "# m15_data = m1_data.resample(\"15T\")\n", "\n", "# m15 = m15_data.data[\"BAC\"]\n", "\n", "# m15.vbt.ohlcv.plot()\n", "\n", "# m1_data.wrapper.index\n", "\n", "# m1_resampler = m1_data.wrapper.get_resampler(\"1T\")\n", "# m1_resampler.index_difference(reverse=True)\n", "\n", "\n", "# m5_resampler.prettify()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# MOM indicator" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vbt.phelp(vbt.indicator(\"talib:ROCP\").run)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "vyuzití rychleho klesani na sekundove urovni behem open rush\n", "- MOM + ROC during open rush\n", "- short signal\n", "- pipeline kombinace thresholdu pro vstup mom_th, roc_th + hodnota sl_stop a tp_stop (pripadne trailing) - nalezeni optimalni kombinace atributu" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# fig = plot_2y_close([ht_trendline],[momshort, rocp], close)\n", "# short_signal.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False)) \n", "\n", "#parameters (primary y line, secondary y line, close)\n", "def plot_2y_close(priminds, secinds, close):\n", " fig = vbt.make_subplots(rows=1, cols=1, shared_xaxes=True, specs=[[{\"secondary_y\": True}]], vertical_spacing=0.02, subplot_titles=(\"MOM\", \"Price\" ))\n", " close.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False), trace_kwargs=dict(line=dict(color=\"blue\")))\n", " for ind in priminds:\n", " ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False))\n", " for ind in secinds:\n", " ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True))\n", " return fig\n", "\n", "close = m1_data.xloc[\"09:30\":\"10:00\"].close\n", "momshort = vbt.indicator(\"talib:MOM\").run(close, timeperiod=3, short_name = \"slope_short\")\n", "ht_trendline = vbt.indicator(\"talib:HT_TRENDLINE\").run(close, short_name = \"httrendline\")\n", "rocp = vbt.indicator(\"talib:ROC\").run(close, short_name = \"rocp\")\n", "#rate of change + momentum\n", "short_signal = (rocp.real_crossed_below(-0.2) & momshort.real_crossed_below(-0.02))\n", "#indlong = vbt.indicator(\"talib:MOM\").run(close, timeperiod=10, short_name = \"slope_long\")\n", "fig = plot_2y_close([ht_trendline],[momshort, rocp], close)\n", "short_signal.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False)) " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "close = m1_data.close\n", "#vbt.phelp(vbt.OLS.run)\n", "\n", "#oer steepmnes of regression line\n", "#talib.LINEARREG_SLOPE(close, timeperiod=timeperiod)\n", "#a také ON BALANCE VOLUME - http://5.161.179.223:8000/static/js/vbt/api/indicators/custom/obv/index.html\n", "\n", "\n", "\n", "mom_ind = vbt.indicator(\"talib:MOM\") \n", "#vbt.phelp(mom_ind.run)\n", "\n", "mom = mom_ind.run(close, timeperiod=10)\n", "\n", "plot_2y_close(mom, close)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# defining ENTRY WINDOW and forced EXIT window" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#m1_data.data[\"BAC\"].info()\n", "import datetime\n", "# Define the market open and close times\n", "market_open = datetime.time(9, 30)\n", "market_close = datetime.time(16, 0)\n", "entry_window_opens = 2\n", "entry_window_closes = 30\n", "\n", "forced_exit_start = 380\n", "forced_exit_end = 390\n", "\n", "forced_exit = m1_data.symbol_wrapper.fill(False)\n", "entry_window_open= m1_data.symbol_wrapper.fill(False)\n", "\n", "# Calculate the time difference in minutes from market open for each timestamp\n", "elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n", "\n", "entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n", "forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n", "\n", "#entry_window_open.info()\n", "# forced_exit.tail(100)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "close = m1_data.close\n", "\n", "#rsi = vbt.RSI.run(close, window=14)\n", "\n", "short_entries = (short_signal & entry_window_open)\n", "short_exits = forced_exit\n", "#long_entries.info()\n", "#number of trues and falses in long_entries\n", "#short_exits.value_counts()\n", "short_entries.value_counts()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def plot_rsi(close, entries, exits):\n", " fig = vbt.make_subplots(rows=1, cols=1, shared_xaxes=True, specs=[[{\"secondary_y\": True}]], vertical_spacing=0.02, subplot_titles=(\"RSI\", \"Price\" ))\n", " close.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True))\n", " #rsi.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False))\n", " entries.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False)) \n", " exits.vbt.signals.plot_as_exits(close, fig=fig, add_trace_kwargs=dict(secondary_y=False)) \n", " return fig\n", "\n", "plot_rsi(close, short_entries, short_exits)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vbt.phelp(vbt.Portfolio.from_signals)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sl_stop = np.arange(0.03/100, 0.2/100, 0.02/100).tolist()\n", "# Using the round function\n", "sl_stop = [round(val, 4) for val in sl_stop]\n", "print(sl_stop)\n", "sl_stop = vbt.Param(sl_stop) #np.nan mean s no stoploss\n", "\n", "pf = vbt.Portfolio.from_signals(close=close, short_entries=short_entries, short_exits=short_exits, sl_stop=0.03/100, tp_stop = 0.03/100, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n", "\n", "#pf.stats()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#list of orders\n", "#pf.orders.records_readable\n", "#pf.orders.plots()\n", "#pf.stats()\n", "pf.stats()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pf.plot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pf[(0.0015,0.0013)].plot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pf[0.03].plot_trade_signals()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# pristup k pf jako multi index" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#pf[0.03].plot()\n", "#pf.order_records\n", "pf[(0.03)].stats()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#zgrupovane statistiky\n", "stats_df = pf.stats([\n", " 'total_return',\n", " 'total_trades',\n", " 'win_rate',\n", " 'expectancy'\n", "], agg_func=None)\n", "stats_df\n", "\n", "\n", "stats_df.nlargest(10, 'Total Return [%]')\n", "#stats_df.info()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pf[(0.0011,0.0013000000000000002)].plot()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pandas.tseries.offsets import DateOffset\n", "\n", "temp_data = basic_data['2024-4-22']\n", "temp_data\n", "res1m = temp_data[[\"Open\", \"High\", \"Low\", \"Close\", \"Volume\"]]\n", "\n", "# Define a custom date offset that starts at 9:30 AM and spans 4 hours\n", "custom_offset = DateOffset(hours=4, minutes=30)\n", "\n", "# res1m = res1m.get().resample(\"4H\").agg({ \n", "# \"Open\": \"first\",\n", "# \"High\": \"max\",\n", "# \"Low\": \"min\",\n", "# \"Close\": \"last\",\n", "# \"Volume\": \"sum\"\n", "# })\n", "\n", "res4h = res1m.resample(\"1h\", resample_kwargs=dict(origin=\"start\"))\n", "\n", "res4h.data\n", "\n", "res15m = res1m.resample(\"15T\", resample_kwargs=dict(origin=\"start\"))\n", "\n", "res15m.data[\"BAC\"]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@vbt.njit\n", "def long_entry_place_func_nb(c, low, close, time_in_ns, rsi14, window_open, window_close):\n", " market_open_minutes = 570 # 9 hours * 60 minutes + 30 minutes\n", "\n", " for out_i in range(len(c.out)):\n", " i = c.from_i + out_i\n", "\n", " current_minutes = vbt.dt_nb.hour_nb(time_in_ns[i]) * 60 + vbt.dt_nb.minute_nb(time_in_ns[i])\n", " #print(\"current_minutes\", current_minutes)\n", " # Calculate elapsed minutes since market open at 9:30 AM\n", " elapsed_from_open = current_minutes - market_open_minutes\n", " elapsed_from_open = elapsed_from_open if elapsed_from_open >= 0 else 0\n", " #print( \"elapsed_from_open\", elapsed_from_open)\n", "\n", " #elapsed_from_open = elapsed_minutes_from_open_nb(time_in_ns) \n", " in_window = elapsed_from_open > window_open and elapsed_from_open < window_close\n", " #print(\"in_window\", in_window)\n", " # if in_window:\n", " # print(\"in window\")\n", "\n", " if in_window and rsi14[i] > 60: # and low[i, c.col] <= hit_price: # and hour == 9: # (4)!\n", " return out_i\n", " return -1\n", "\n", "@vbt.njit\n", "def long_exit_place_func_nb(c, high, close, time_index, tp, sl): # (5)!\n", " entry_i = c.from_i - c.wait\n", " entry_price = close[entry_i, c.col]\n", " hit_price = entry_price * (1 + tp)\n", " stop_price = entry_price * (1 - sl)\n", " for out_i in range(len(c.out)):\n", " i = c.from_i + out_i\n", " last_bar_of_day = vbt.dt_nb.day_changed_nb(time_index[i], time_index[i + 1])\n", "\n", " #print(next_day)\n", " if last_bar_of_day: #pokud je dalsi next day, tak zavirame posledni\n", " print(\"ted\",out_i)\n", " return out_i\n", " if close[i, c.col] >= hit_price or close[i, c.col] <= stop_price :\n", " return out_i\n", " return -1\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df = pd.DataFrame(np.random.random(size=(5, 10)), columns=list('abcdefghij'))\n", "\n", "df" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.sum()" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "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.10.11" } }, "nbformat": 4, "nbformat_minor": 2 }