Initial commit after copying files from flawed repository

This commit is contained in:
David Brazda
2024-08-30 20:49:53 +02:00
commit c11ed9d474
47 changed files with 40520 additions and 0 deletions

29
README.md Normal file
View File

@ -0,0 +1,29 @@
# Research for v2realbot
## Overview
Strategy research and development tracker. Serves as a central hub for strategizing, idea generation, and performance tracking of the stragies
## Purpose
This repository is established as an issue tracker to:
- Facilitate the proposal and discussion of new trading strategies.
- Track the progress and refinement of existing strategies.
- Document and share insights, research findings, and performance analyses.
## Getting Started
1. **Proposal of New Strategies**: To propose a new strategy, create a new issue with a clear and descriptive title. Outline your strategy, including its rationale, intended market conditions, and expected outcomes.
2. **Discussion and Feedback**: Use the issues section for ongoing discussions, feedback, and collaborative refinement of strategies.
3. **Strategy Documentation**: Each strategy should be documented in detail on the Wiki pages, including its parameters, implementation guidelines, and any relevant backtesting results. (Note: documentation either here or on [trading.mujdenik.eu](trading.mujdenik.eu) - we will see what's better in practice)
4. **Problem Reporting and Optimization**: Report any issues or suggest optimizations for existing strategies through the Issues section, providing relevant data and analysis to support your observations.
5. **Contribution Guidelines**: Please refer to `CONTRIBUTING.md` for detailed guidelines on how to contribute effectively to this repository.
## Collaboration Tools
- **Issues**: For proposing, discussing, and tracking strategies.
- **Wiki**: For detailed documentation and resource sharing, please se [trading.mujdenik.eu](trading.mujdenik.eu)
- **Research folder**: Notebook dedicated do each strategy.
## Integration with v2realbot
This repository operates in tandem with the `v2realbot` repository. Ensure all strategies are compatible and follow the guidelines for integration into the v2realbot Trading Platform.

24
requirements.txt Normal file
View File

@ -0,0 +1,24 @@
pandas
pywebview>=5.0.5
orjson
v2realbot @ git+https://github.com/drew2323/v2trading.git@master#egg=v2trading
lightweight-charts @ git+https://github.com/drew2323/lightweight-charts-python.git@main#egg=lightweight-charts
pyarrow
matplotlib
seaborn
alpaca-py
rich
tomli
appdirs
python-dotenv
filelock
dill
plotly
dash
dash-bootstrap-components
tinydb
tulipy
ta-lib
pywavelets
/Users/davidbrazda/Desktop/vectorbtpro-2024.2.22-py3-none-any.whl
sqlalchemy

385
research/basic.ipynb Normal file
View File

@ -0,0 +1,385 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from v2realbot.tools.loadbatch import load_batch\n",
"from v2realbot.utils.utils import zoneNY, DATA_DIR\n",
"import pandas as pd\n",
"import numpy as np\n",
"import vectorbtpro as vbt\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"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"batch_id = \"e44a5075\"\n",
"# res, df = load_batch(batch_id=batch_id,\n",
"# space_resolution_evenly=False,\n",
"# indicators_columns=[\"Rsi14\"],\n",
"# main_session_only=True)\n",
"# if res < 0:\n",
"# print(\"Error\" + str(res) + str(df))\n",
"# df = df[\"bars\"]\n",
"# df.info()\n",
"# df.head()\n",
"# #df.ptable()\n",
"# df.to_pickle(DATA_DIR+\"/\"+f'{batch_id}.pickle')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## FILTERING"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df = pd.read_pickle(DATA_DIR+\"/\"+f'{batch_id}.pickle')\n",
"df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"#naloadujeme do vbt symbol as column\n",
"basic_data = vbt.Data.from_data({\"BAC\": df}, tz_convert=zoneNY)\n",
"start_date = pd.Timestamp('2024-03-12 09:30', tz=zoneNY)\n",
"end_date = pd.Timestamp('2024-03-13 16:00', tz=zoneNY)\n",
"\n",
"#filter date\n",
"#basic_data = basic_data.transform(lambda df: df[df.index.date == start_date.date()])\n",
"#filter range\n",
"basic_data = basic_data.transform(lambda df: df[(df.index >= start_date) & (df.index <= end_date)])\n",
"#filtered_data = basic_data.transform(lambda df: df[(df.index >= start_date) & (df.index <= end_date)])\n",
"# #range filtered_data = data[(data.index >= start_date) & (data.index <= end_date)\n",
"#df.between_time('09:30', '16:00')\n",
"#(df.index.time >= pd.Timestamp('09:30').time()) & (df.index.time <= pd.Timestamp('16:00').time())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# basic_data.data[\"BAC\"]\n",
"rsi14"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#b = filtered_data.get().iloc[100:200] #b[[\"Open\",\"High\"]]\n",
"rsi14 = basic_data.data[\"BAC\"][\"Rsi14\"].rename(\"Rsi14\")\n",
"#create subploit\n",
"fig = vbt.make_subplots(rows=2, cols=1, shared_xaxes=True, specs=[[{\"secondary_y\": True}], [{\"secondary_y\": False}]])\n",
"rsi14.vbt.plot(add_trace_kwargs=dict(row=1, col=1, secondary_y=True),fig=fig)\n",
"basic_data.data[\"BAC\"].vbt.ohlcv.plot(add_trace_kwargs=dict(row=1, col=1, secondary_y=False), fig=fig)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"run_rsi = vbt.talib_func(\"rsi\")\n",
"rsi_new = run_rsi(basic_data.vwap, timeperiod=15)\n",
"rsi_new = rsi_new.fillna(0)\n",
"# print(rsi_new)\n",
"# print(dir(rsi_new))\n",
"rsi14 = basic_data.data[\"BAC\"][\"Rsi14\"]\n",
"# print(rsi14)\n",
"\n",
"#zkombinujeme do stejneho dataframe skrz sloupce (axis1)\n",
"# combined_df = pd.concat([rsi_new, rsi14], axis=1)\n",
"# combined_df\n",
"\n",
"#create subplot\n",
"fig = vbt.make_subplots(rows=1, cols=1, shared_xaxes=True)\n",
"\n",
"plot_rsi = vbt.talib_plot_func(\"rsi\")\n",
"plot_rsi(rsi_new, fig=fig)\n",
"plot_rsi(rsi14, fig=fig)\n",
"fig.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"entries = rsi_new.vbt.crossed_below(30)\n",
"entries"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"exits = rsi_new.vbt.crossed_above(70)\n",
"exits "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def plot_rsi(close, rsi, entries, exits):\n",
" fig = vbt.make_subplots(rows=2, cols=1, shared_xaxes=True, specs=[[{\"secondary_y\": True}], [{\"secondary_y\": False}]])\n",
" basic_data.data[\"BAC\"].vbt.ohlcv.plot(add_trace_kwargs=dict(row=1, col=1, secondary_y=True),fig=fig)\n",
" rsi.vbt.plot(fig=fig, add_trace_kwargs=dict(row=1, col=1, secondary_y=False), trace_kwargs=dict(line=dict(color='grey', width=1.5)))\n",
" #close.vbt.plot(fig=fig, add_trace_kwargs=dict(row=1, col=1, secondary_y=True))\n",
" entries.vbt.signals.plot_as_entries(y=close, fig=fig, add_trace_kwargs=dict(row=1, col=1, secondary_y=True))\n",
" exits.vbt.signals.plot_as_exits(y=close, fig=fig, add_trace_kwargs=dict(row=1, col=1, secondary_y=True))\n",
" return fig\n",
"\n",
"close = basic_data.get(\"Close\")\n",
"\n",
"print(entries)\n",
"\n",
"plot_rsi(close, rsi_new, entries, exits).show()\n",
"\n",
"clean_entries, clean_exits = entries.vbt.signals.clean(exits) \n",
"\n",
"plot_rsi(close, rsi_new, clean_entries, clean_exits).show()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"clean_entries.vbt.signals.total() \n",
"clean_exits.vbt.signals.total() "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"index = basic_data.wrapper.index\n",
"\n",
"#minutes from market open\n",
"market_open_time = pd.to_timedelta('09:30:00')\n",
"\n",
"# Calculate the market open datetime for each day\n",
"market_opens = index.normalize() + market_open_time\n",
"\n",
"minutes_from_open = (index - market_opens).total_seconds() / 60\n",
"# Ensuring the result is a Series\n",
"minutes_from_open = pd.Series(minutes_from_open, index=index)\n",
"\n",
"#minutes_from_open.values\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"symbol_wrapper = basic_data.get_symbol_wrapper()\n",
"\n",
"@vbt.njit\n",
"def elapsed_minutes_from_open_nb(time_in_ns):\n",
" market_opens_in_minute = 570 # 9 hours * 60 minutes + 30 minutes\n",
" current_minute = vbt.dt_nb.hour_nb(time_in_ns) * 60 + vbt.dt_nb.minute_nb(time_in_ns)\n",
" #print(\"current_minutes\", current_minutes)\n",
" # Calculate elapsed minutes since market open at 9:30 AM\n",
" minutes_from_open = current_minute - market_opens_in_minute\n",
" print( \"elapsed_from_open\", minutes_from_open)\n",
" return minutes_from_open if minutes_from_open >= 0 else 0\n",
"\n",
"@vbt.njit\n",
"def entry_place_func_nb(c, low, close, time_in_ns, rsi14, window_open, window_close):\n",
" # if c.from_i == 0: # (1)!\n",
" # c.out[0] = True\n",
" # return -1\n",
" # print(\"ted\")\n",
" # print(c.from_i)\n",
" #exit_i = c.from_i - c.wait # (2)!\n",
" #exit_price = close[exit_i, c.col] # (3)!\n",
" #hit_price = exit_price * (1 - th)\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",
"#whether the date changed\n",
"# day_changed_nb(\n",
"# ts1,\n",
"# ts2\n",
"# )\n",
"\n",
"# h_ns(ts1) int\n",
"\n",
"\n",
"@vbt.njit\n",
"def 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",
"\n",
" \n",
"\n",
"\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 high[i, c.col] >= hit_price or close[i, c.col] <= stop_price :\n",
" return out_i\n",
" return -1\n",
"\n",
"index = basic_data.wrapper.index\n",
"\n",
"#minutes from market open\n",
"market_open_time = pd.to_timedelta('09:30:00')\n",
"\n",
"# Calculate the market open datetime for each day\n",
"market_opens = index.normalize() + market_open_time\n",
"\n",
"minutes_from_open = (index - market_opens).total_seconds() / 60\n",
"\n",
"print(minutes_from_open)\n",
"\n",
"\n",
"#index 9:30 az 10:00\n",
"time_entry_window = ((index.time >= pd.Timestamp(\"09:30:00\").time())&\n",
" (index.time <= pd.Timestamp(\"14:00:00\").time()))\n",
"\n",
"\n",
"print(time_entry_window)\n",
"print(rsi_new)\n",
"rsi_entries = rsi_new.vbt.crossed_below(64)\n",
"rsi_entries = rsi_entries < 40\n",
"rsi_entries_array = rsi_entries.vbt.to_1d_array()\n",
"print(rsi_entries_array)\n",
"\n",
"\n",
"entries, exits = vbt.pd_acc.signals.generate_both( # (6)!\n",
" symbol_wrapper.shape,\n",
" entry_place_func_nb=entry_place_func_nb,\n",
" #timeindex to ns to numba\n",
" entry_place_args=(vbt.Rep(\"low\"), vbt.Rep(\"close\"), vbt.dt.to_ns(basic_data.wrapper.index), vbt.Rep(\"rsi14\"), 0, 380), # (7)!\n",
" exit_place_func_nb=exit_place_func_nb,\n",
" exit_place_args=(vbt.Rep(\"high\"), vbt.Rep(\"close\"), vbt.dt.to_ns(basic_data.wrapper.index), 0.001, 0.001),\n",
" wrapper=symbol_wrapper,\n",
" broadcast_named_args=dict( # (8)!\n",
" high=basic_data.get(\"High\"),\n",
" low=basic_data.get(\"Low\"),\n",
" close=basic_data.get(\"Close\"),\n",
" rsi14=basic_data.get(\"Rsi14\"),\n",
" window_open=10,\n",
" window_close=60\n",
" ),\n",
" broadcast_kwargs=dict(post_func=np.asarray) # (9)!\n",
")\n",
"\n",
"\n",
"plot_rsi(close, rsi_new, entries, exits).show()\n",
"\n",
"# fig = basic_data.plot(\n",
"# symbol=\"BAC\", \n",
"# ohlc_trace_kwargs=dict(opacity=0.5), \n",
"# plot_volume=False\n",
"# )\n",
"\n",
"# #rsi_entries.vbt.plot(fig=fig)\n",
"# entries.vbt.signals.plot_as_entries(\n",
"# y=close, fig=fig)\n",
"# exits.vbt.signals.plot_as_exits(\n",
"# y=close, fig=fig)\n",
"# fig.show() # (10)!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"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
}

View File

@ -0,0 +1,95 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import vectorbtpro as vbt\n",
"from lightweight_charts import chart, Panel\n",
"\n",
"# Pulling ETH-USD data\n",
"data = vbt.YFData.pull(\"ETH-USD\")\n",
"close = data.close\n",
"high = data.high\n",
"low = data.low\n",
"\n",
"# Define a simple moving average crossover strategy using EWM\n",
"short_ma = vbt.MA.run(close, window=6, wtype=\"exp\").ma\n",
"long_ma = vbt.MA.run(close, window=73, wtype=\"exp\").ma\n",
"\n",
"# Generate signals\n",
"long_entries = short_ma > long_ma\n",
"long_exits = short_ma < long_ma\n",
"short_entries = short_ma < long_ma\n",
"short_exits = short_ma > long_ma\n",
"\n",
"clean_long_entries, clean_long_exits = long_entries.vbt.signals.clean(long_exits)\n",
"clean_short_entries, clean_short_exits = short_entries.vbt.signals.clean(short_exits)\n",
"\n",
"# ohlcv_df = data.ohlcv.get()\n",
"\n",
"#assume i want to display simple entries or exits on series or ohlcv \n",
"#based on tuple positions it determines entries or exits (and set colors and shape accordingly)\n",
"pane1 = Panel(\n",
" ohlcv=(data.ohlcv.get(), clean_long_entries, clean_short_entries)\n",
")\n",
"ch = chart([pane1], title=\"Chart with Entry/Exit Markers\", session=None, size=\"s\")\n",
"\n",
"#if you want to display more entries or exits, use tuples with their colors\n",
"pane1 = Panel(\n",
" ohlcv=(data.ohlcv.get(),\n",
" [(clean_long_entries, \"yellow\"), (clean_short_entries, \"pink\")], #list of entries tuples with color\n",
" [(clean_long_exits, \"yellow\"), (clean_short_exits, \"pink\")] #list of exits tuples with color\n",
" ), \n",
")\n",
"\n",
"# # Create the chart with the panel\n",
"ch = chart([pane1], title=\"Chart with EntryShort/ExitShort (yellow) and EntryLong/ExitLong markers (pink)\", sync=True, session=None, size=\"s\")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"\n",
"\n",
"\n",
"# # Add the markers to the chart using markers_set method\n",
"# entry_signals = pd.DataFrame({\n",
"# 'time': clean_long_entries.index.astype(str),\n",
"# 'value': clean_long_entries.values\n",
"# }).dropna()\n",
"# entry_signals['value'] = entry_signals['value'].astype(bool)\n",
"\n",
"# ch.markers_set(entry_signals, type='entries')"
]
}
],
"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
}

View File

@ -0,0 +1,348 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Loading trades and vectorized aggregation\n",
"Describes how to fetch trades (remote/cached) and use new vectorized aggregation to aggregate bars of given type (time, volume, dollar) and resolution\n",
"\n",
"`fetch_trades_parallel` enables to fetch trades of given symbol and interval, also can filter conditions and minimum size. return `trades_df`\n",
"`aggregate_trades` acceptss `trades_df` and ressolution and type of bars (VOLUME, TIME, DOLLAR) and return aggregated ohlcv dataframe `ohlcv_df`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"from numba import jit\n",
"from alpaca.data.historical import StockHistoricalDataClient\n",
"from v2realbot.config import ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, DATA_DIR\n",
"from alpaca.data.requests import StockTradesRequest\n",
"from v2realbot.enums.enums import BarType\n",
"import time\n",
"from datetime import datetime\n",
"from v2realbot.utils.utils import parse_alpaca_timestamp, ltp, zoneNY, send_to_telegram, fetch_calendar_data\n",
"import pyarrow\n",
"from v2realbot.loader.aggregator_vectorized import fetch_daily_stock_trades, fetch_trades_parallel, generate_time_bars_nb, aggregate_trades\n",
"import vectorbtpro as vbt\n",
"import v2realbot.utils.config_handler as cfh\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', 20) # Number of rows per page\n",
"# pd.set_option('display.float_format', '{:.9f}'.format)\n",
"\n",
"\n",
"#trade filtering\n",
"exclude_conditions = cfh.config_handler.get_val('AGG_EXCLUDED_TRADES') #standard ['C','O','4','B','7','V','P','W','U','Z','F']\n",
"minsize = 100\n",
"\n",
"symbol = \"SPY\"\n",
"#datetime in zoneNY \n",
"day_start = datetime(2024, 1, 1, 9, 30, 0)\n",
"day_stop = datetime(2024, 1, 14, 16, 00, 0)\n",
"day_start = zoneNY.localize(day_start)\n",
"day_stop = zoneNY.localize(day_stop)\n",
"#filename of trades_df parquet, date are in isoformat but without time zone part\n",
"dir = DATA_DIR + \"/notebooks/\"\n",
"#parquet interval cache contains exclude conditions and minsize filtering\n",
"file_trades = dir + f\"trades_df-{symbol}-{day_start.strftime('%Y-%m-%dT%H:%M:%S')}-{day_stop.strftime('%Y-%m-%dT%H:%M:%S')}-{exclude_conditions}-{minsize}.parquet\"\n",
"#file_trades = dir + f\"trades_df-{symbol}-{day_start.strftime('%Y-%m-%dT%H:%M:%S')}-{day_stop.strftime('%Y-%m-%dT%H:%M:%S')}.parquet\"\n",
"file_ohlcv = dir + f\"ohlcv_df-{symbol}-{day_start.strftime('%Y-%m-%dT%H:%M:%S')}-{day_stop.strftime('%Y-%m-%dT%H:%M:%S')}-{exclude_conditions}-{minsize}.parquet\"\n",
"\n",
"#PRINT all parquet in directory\n",
"import os\n",
"files = [f for f in os.listdir(dir) if f.endswith(\".parquet\")]\n",
"for f in files:\n",
" print(f)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"trades_df = fetch_daily_stock_trades(symbol, day_start, day_stop, exclude_conditions=exclude_conditions, minsize=minsize, force_remote=False, max_retries=5, backoff_factor=1)\n",
"trades_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#Either load trades or ohlcv from parquet if exists\n",
"\n",
"#trades_df = fetch_trades_parallel(symbol, day_start, day_stop, exclude_conditions=exclude_conditions, minsize=50, max_workers=20) #exclude_conditions=['C','O','4','B','7','V','P','W','U','Z','F'])\n",
"# trades_df.to_parquet(file_trades, engine='pyarrow', compression='gzip')\n",
"\n",
"trades_df = pd.read_parquet(file_trades,engine='pyarrow')\n",
"ohlcv_df = aggregate_trades(symbol=symbol, trades_df=trades_df, resolution=1, type=BarType.TIME)\n",
"ohlcv_df.to_parquet(file_ohlcv, engine='pyarrow', compression='gzip')\n",
"\n",
"# ohlcv_df = pd.read_parquet(file_ohlcv,engine='pyarrow')\n",
"# trades_df = pd.read_parquet(file_trades,engine='pyarrow')\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#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",
"file_name = \"\"\n",
"ohlcv_df = pd.read_parquet(file_ohlcv,engine='pyarrow')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ohlcv_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"# Calculate daily returns\n",
"ohlcv_df['returns'] = ohlcv_df['close'].pct_change().dropna()\n",
"#same as above but pct_change is from 3 datapoints back, but only if it is the same date, else na\n",
"\n",
"\n",
"# Plot the probability distribution curve\n",
"plt.figure(figsize=(10, 6))\n",
"sns.histplot(df['returns'].dropna(), kde=True, stat='probability', bins=30)\n",
"plt.title('Probability Distribution of Daily Returns')\n",
"plt.xlabel('Daily Returns')\n",
"plt.ylabel('Probability')\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"from sklearn.model_selection import train_test_split\n",
"from sklearn.preprocessing import StandardScaler\n",
"from sklearn.linear_model import LogisticRegression\n",
"from sklearn.metrics import accuracy_score\n",
"\n",
"# Define the intervals from 5 to 20 s, returns for each interval\n",
"#maybe use rolling window?\n",
"intervals = range(5, 21, 5)\n",
"\n",
"# Create columns for percentage returns\n",
"rolling_window = 50\n",
"\n",
"# Normalize the returns using rolling mean and std\n",
"for N in intervals:\n",
" column_name = f'returns_{N}'\n",
" rolling_mean = ohlcv_df[column_name].rolling(window=rolling_window).mean()\n",
" rolling_std = ohlcv_df[column_name].rolling(window=rolling_window).std()\n",
" ohlcv_df[f'norm_{column_name}'] = (ohlcv_df[column_name] - rolling_mean) / rolling_std\n",
"\n",
"# Display the dataframe with normalized return columns\n",
"ohlcv_df\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Calculate the sum of the normalized return columns for each row\n",
"ohlcv_df['sum_norm_returns'] = ohlcv_df[[f'norm_returns_{N}' for N in intervals]].sum(axis=1)\n",
"\n",
"# Sort the DataFrame based on the sum of normalized returns in descending order\n",
"df_sorted = ohlcv_df.sort_values(by='sum_norm_returns', ascending=False)\n",
"\n",
"# Display the top rows with the highest sum of normalized returns\n",
"df_sorted\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Drop initial rows with NaN values due to pct_change\n",
"ohlcv_df.dropna(inplace=True)\n",
"\n",
"# Plotting the probability distribution curves\n",
"plt.figure(figsize=(14, 8))\n",
"for N in intervals:\n",
" sns.kdeplot(ohlcv_df[f'returns_{N}'].dropna(), label=f'Returns {N}', fill=True)\n",
"\n",
"plt.title('Probability Distribution of Percentage Returns')\n",
"plt.xlabel('Percentage Return')\n",
"plt.ylabel('Density')\n",
"plt.legend()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"# Plot the probability distribution curve\n",
"plt.figure(figsize=(10, 6))\n",
"sns.histplot(ohlcv_df['returns'].dropna(), kde=True, stat='probability', bins=30)\n",
"plt.title('Probability Distribution of Daily Returns')\n",
"plt.xlabel('Daily Returns')\n",
"plt.ylabel('Probability')\n",
"plt.show()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#show only rows from ohlcv_df where returns > 0.005\n",
"ohlcv_df[ohlcv_df['returns'] > 0.0005]\n",
"\n",
"#ohlcv_df[ohlcv_df['returns'] < -0.005]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#ohlcv where index = date 2024-03-13 and between hour 12\n",
"\n",
"a = ohlcv_df.loc['2024-03-13 12:00:00':'2024-03-13 13:00:00']\n",
"a"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ohlcv_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"trades_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ohlcv_df.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"trades_df.to_parquet(\"trades_df-spy-0111-0111.parquett\", engine='pyarrow', compression='gzip')\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"trades_df.to_parquet(\"trades_df-spy-111-0516.parquett\", engine='pyarrow', compression='gzip', allow_truncated_timestamps=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ohlcv_df.to_parquet(\"ohlcv_df-spy-111-0516.parquett\", engine='pyarrow', compression='gzip')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"basic_data = vbt.Data.from_data(vbt.symbol_dict({symbol: ohlcv_df}), tz_convert=zoneNY)\n",
"vbt.settings['plotting']['auto_rangebreaks'] = True\n",
"basic_data.ohlcv.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#access just BCA\n",
"#df_filtered = df.loc[\"BAC\"]"
]
}
],
"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
}

View File

@ -0,0 +1,208 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"celkovy optimalizacni backtest na vetsim oknu 1 - 300\n",
"a možná take to udělat jako parametr\n",
"zkusit CV\n",
"zobrazit nejak robustnost parametru"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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",
"\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",
"res, df = load_batch(batch_id=\"f1ac6651\", #138170bc 0fb5043a bde6d0be f1ac6651\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",
"#basic_data.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vbt.open_api_ref(vbt.base)\n",
"\n",
"vbt"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"##na toto udelat crosssvalidationu nebo alespon na testovacim ci jinem obdobi\n",
"#take udelat long leg - tato je shortovaci\n",
"\n",
"#8\t-0.06\t-0.2\t0.0028\t0.0048\t4.156254\n",
"\n",
"#short combination ok for train(4)/test(0.1) (window 1-90, fe 95-100)\n",
"#2,\t-0.02,\t-0.25,\t0.0018,\t0.0068\n",
"\n",
"#dalsi ok hodnota shortu for train/test 4/1\n",
"#70,\t8,\t-0.06,\t-0.2,\t0.0013,\t0.0053\t\n",
"\n",
"\n",
"#kombinace bez roc_th, train/test 7/-1.5\n",
"#70\t7\t-0.07\t0.0033\t0.0063\n",
"\n",
"#opet bez roc_th, train(5.77)/test 0.9 - spolus tsl_stop + tsl_th\n",
"#29\t7\t-0.09\t0.0033\t0.0068\n",
"\n",
"#bez roc_th a s trailing sl train/test 8.1/-0.8 \n",
"#70\t2\t-0.05\t0.0018\t0.0068\n",
"\n",
"\n",
"# TODO:\n",
"#- vyzkouset zda nejvyhodnejsi kombinace krom train/testu funguje i na nasledujicich dnech po trainu\n",
"# -zkusit najit v short datasetu neco vyhodneho co funguji i na testu\n",
"# - dodelat kombinace pro long signaly\n",
"# - zkusit walk forward\n",
"# - vytvorit vysledkove totoznou na v2realbot\n",
"# - podivat se jak detailne funguji tsl_stop a tsl_th\n",
"\n",
"#70,\t4,\t-0.07,\t0.0048,\t0.0068\t\n",
"\n",
"\n",
"entry_window_closes, mom_timeperiod, mom_th, sl_stop, tp_stop = 8,\t3,\t0.07,\t0.0028,\t0.0033\t\n",
"roc_th = 0\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",
"\n",
"long_signal = momshort.real_crossed_above(mom_th)\n",
"\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",
"# 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",
"\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",
"#short_entries=short_entries, short_exits=short_exits,\n",
"pf = vbt.Portfolio.from_signals(close=basic_data, entries=entries, exits=exits, tsl_stop=sl_stop, tp_stop = tp_stop, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
"\n",
"pf.stats()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.get_drawdowns().records_readable"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.orders.records_readable"
]
}
],
"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
}

View File

@ -0,0 +1,252 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Test persistance\n",
"\n",
"ohlcv and trades persistence with bar type and trade filtering and minsize support\n",
"\n",
"```\n",
"/OHLCV/\n",
" ├── {bar_type}/ (1s)\n",
" │ ├── {resolution}/\n",
" │ │ ├── {filtered_trades}-{min_trade_size}/\n",
" │ │ │ ├── {day}/\n",
" │ │ │ │ └── hashedname.parquet\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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",
"import v2realbot.utils.config_handler as cfh\n",
"init_notebook_mode(all_interactive=True)\n",
"from v2realbot.enums.enums import BarType\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=\"f1ac6651\", #138170bc 0fb5043a bde6d0be f1ac6651\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",
"# 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",
"# #basic_data.info()\n",
"\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-BAC-2023-01-01T09_30_00-2024-05-25T16_00_00-47BCFOPUVWZ-100.parquet\"\n",
"ohlcv_df = pd.read_parquet(dir+file_name,engine='pyarrow')\n",
"\n",
"#filter ohlcv_df to certain date range (assuming datetime index)\n",
"#ohlcv_df = ohlcv_df.loc[\"2024-05-14 09:30\":\"2024-05-15 09:35\"]\n",
"\n",
"#add vwap column to ohlcv_df\n",
"#ohlcv_df[\"hlcc4\"] = (ohlcv_df[\"close\"] + ohlcv_df[\"high\"] + ohlcv_df[\"low\"] + ohlcv_df[\"close\"]) / 4\n",
"\n",
"basic_data = vbt.Data.from_data(vbt.symbol_dict({\"BAC\": ohlcv_df}), tz_convert=zoneNY)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#basic_data.data[\"BAC\"].info()\n",
"#ohlcv_df group by week number of rows\n",
"# ohlcv_df['close'].groupby(pd.Grouper(freq='ME')).mean()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#trade filtering\n",
"exclude_conditions = cfh.config_handler.get_val('AGG_EXCLUDED_TRADES') #standard ['C','O','4','B','7','V','P','W','U','Z','F']\n",
"minsize = 100\n",
"exclude_conditions_str = ''.join(exclude_conditions)\n",
"exclude_conditions_str"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"basic_data.data[\"BAC\"].info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Aim is to store\n",
"OHLCV grouped by symbol, day, resolution\n",
"and \n",
"bar type\n",
"excluded_conditions\n",
"minsize\n",
"main session"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"bartype= BarType.TIME\n",
"resolution = \"1s\"\n",
"trade_filter = exclude_conditions_str+\"-\"+str(minsize)\n",
"dir = \"/OHLCV/\"+bartype+\"/\"+resolution+\"/\"+trade_filter+\"/\"\n",
"#dir = DATA_DIR + dir\n",
"basic_data.to_parquet(partition_by=\"day\", keep_groupby_names=False, path_or_buf=dir, mkdir_kwargs=dict(mkdir=True)) \n",
"#partition_by=\"day\",\n",
"\n",
"#naloaduje partitionvana 1s data skrz 90 dni za 2s\n",
"#day_data = vbt.ParquetData.pull(\"BAC\", paths=dir, filters=[(\"group\", \">\", \"2024-01-02\"),(\"group\", \"<=\", \"2024-01-09\")]) #, \n",
"# day_data[\"2024-05-01\":\"2024-05-14\"].get()\n",
"\n",
"# day_data.data[\"BAC\"].info()\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#naloaduje partitionvana 1s data skrz 90 dni za 2s\n",
"day_data = vbt.ParquetData.pull(\"BAC\", paths=dir, filters=[(\"group\", \">=\", \"2024-01-02\"),(\"group\", \"<=\", \"2024-01-09\")]) #, \n",
"# day_data[\"2024-05-01\":\"2024-05-14\"].get()\n",
"\n",
"day_data.data[\"BAC\"].info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"close = basic_data.close\n",
"#group by close by day, using pandas grouper\n",
"#close.groupby(pd.Grouper(freq='ME')).mean()\n",
"\n",
"#using Grouper of vectorbtpro\n",
"#close.vbt.group_by(pd.Grouper(freq='ME')).mean()\n",
"\n",
"#basic_data.wrapper.get_columns()\n",
"basic_data.wrapper.get_freq()\n",
"# vbt.pdir(basic_data.wrapper)\n",
"# basic_data.wrapper\n",
"basic_data.wrapper.grouper.is_grouped()\n",
"\n",
"vbt.pdir(basic_data.wrapper.grouper)\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"grouper = basic_data.wrapper.index.vbt.get_grouper(\"ME\")\n",
"\n",
"for group, group_idx in grouper:\n",
" print(group, group_idx)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#prevede 1milion dat (6mes 1s) na dict za 10ss\n",
"df = day_data.data[\"BAC\"]\n",
"df_dict = df.to_dict(orient='list')\n",
"\n",
"# Convert the index (which is the time) to a list of float timestamps\n",
"df_dict['time'] = [timestamp.timestamp() for timestamp in df.index]\n",
"\n",
"df_dict"
]
}
],
"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
}

View File

@ -0,0 +1,458 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Loading trades and vectorized aggregation\n",
"This notebook fetches the trades from remote or local cache and aggregates them to bars of given type (time, volume, dollar) and resolution\n",
"\n",
"`fetch_trades_parallel` enables to fetch trades of given symbol and interval, also can filter conditions and minimum size. return `trades_df`\n",
"`aggregate_trades` acceptss `trades_df` and ressolution and type of bars (VOLUME, TIME, DOLLAR) and return aggregated ohlcv dataframe `ohlcv_df`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from dotenv import load_dotenv\n",
"\n",
"#as V2realbot is client , load env variables here\n",
"env_file = \"/Users/davidbrazda/Documents/Development/python/.env\"\n",
"# Load the .env file\n",
"load_dotenv(env_file)\n",
"\n",
"import pandas as pd\n",
"import numpy as np\n",
"from numba import jit\n",
"from alpaca.data.historical import StockHistoricalDataClient\n",
"from v2realbot.config import ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, DATA_DIR\n",
"from alpaca.data.requests import StockTradesRequest\n",
"from v2realbot.enums.enums import BarType\n",
"import time\n",
"from datetime import datetime\n",
"from v2realbot.utils.utils import parse_alpaca_timestamp, ltp, zoneNY, send_to_telegram, fetch_calendar_data\n",
"import pyarrow\n",
"from v2realbot.loader.aggregator_vectorized import fetch_daily_stock_trades, fetch_trades_parallel, generate_time_bars_nb, aggregate_trades\n",
"import vectorbtpro as vbt\n",
"import v2realbot.utils.config_handler as cfh\n",
"from appdirs import user_data_dir\n",
"from pathlib import Path\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', 20) # Number of rows per page\n",
"# pd.set_option('display.float_format', '{:.9f}'.format)\n",
"\n",
"\n",
"#trade filtering\n",
"exclude_conditions = cfh.config_handler.get_val('AGG_EXCLUDED_TRADES') #standard ['C','O','4','B','7','V','P','W','U','Z','F']\n",
"minsize = 100\n",
"\n",
"symbol = \"BAC\"\n",
"#datetime in zoneNY \n",
"day_start = datetime(2023, 1, 1, 9, 30, 0)\n",
"day_stop = datetime(2024, 5, 25, 15, 30, 0)\n",
"day_start = zoneNY.localize(day_start)\n",
"day_stop = zoneNY.localize(day_stop)\n",
"#filename of trades_df parquet, date are in isoformat but without time zone part\n",
"dir = DATA_DIR + \"/notebooks/\"\n",
"#parquet interval cache contains exclude conditions and minsize filtering\n",
"file_trades = dir + f\"trades_df-{symbol}-{day_start.strftime('%Y-%m-%dT%H_%M_%S')}-{day_stop.strftime('%Y-%m-%dT%H_%M_%S')}-{''.join(exclude_conditions)}-{minsize}.parquet\"\n",
"#file_trades = dir + f\"trades_df-{symbol}-{day_start.strftime('%Y-%m-%dT%H:%M:%S')}-{day_stop.strftime('%Y-%m-%dT%H:%M:%S')}.parquet\"\n",
"file_ohlcv = dir + f\"ohlcv_df-{symbol}-{day_start.strftime('%Y-%m-%dT%H_%M_%S')}-{day_stop.strftime('%Y-%m-%dT%H_%M_%S')}-{''.join(exclude_conditions)}-{minsize}.parquet\"\n",
"print(file_trades)\n",
"print(file_ohlcv)\n",
"#PRINT all parquet in directory\n",
"import os\n",
"files = [f for f in os.listdir(dir) if f.endswith(\".parquet\")]\n",
"for f in files:\n",
" print(f)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#Either load trades or ohlcv from parquet if exists\n",
"#trades_df = fetch_trades_parallel(symbol, day_start, day_stop, exclude_conditions=exclude_conditions, minsize=minsize, max_workers=30) #exclude_conditions=['C','O','4','B','7','V','P','W','U','Z','F'])\n",
"#trades_df.to_parquet(file_trades, engine='pyarrow', compression='gzip')\n",
"#trades_df.to_parquet(file_trades, engine='pyarrow', compression='gzip')\n",
"#filenames = [dir+\"trades_df-BAC-2024-01-01T09_30_00-2024-05-14T16_00_00-CO4B7VPWUZF-100.parquet\",dir+\"trades_df-BAC-2024-05-15T09_30_00-2024-05-25T16_00_00-47BCFOPUVWZ-100.parquet\"]\n",
"trades_df = pd.read_parquet(dir+\"trades_df-BAC-2023-01-01T09_30_00-2024-05-25T16_00_00-47BCFOPUVWZ-100.parquet\",engine='pyarrow')\n",
"#focused = trades_df.loc[\"2024-02-16 11:23:11\":\"2024-02-16 11:24:26\"]\n",
"#focused\n",
"ohlcv_df = aggregate_trades(symbol=symbol, trades_df=trades_df, resolution=1, type=BarType.TIME)\n",
"ohlcv_df.to_parquet(file_ohlcv, engine='pyarrow', compression='gzip')\n",
"\n",
"#ohlcv_df = pd.read_parquet(file_ohlcv,engine='pyarrow')\n",
"# trades_df = pd.read_parquet(file_trades,engine='pyarrow')\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"trades_df = None\n",
"ohlcv_df = None"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#ohlcv_df.info()\n",
"#trades_df.info()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"a = trades_df.loc[(\"BAC\", \"2024-02-16 09:30\"):(\"BAC\",\"2024-02-16 09:32:11\")]\n",
"a"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ohlcv_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#trades_df.info()\n",
"focused = trades_df.loc[(\"BAC\", \"2024-02-16 09:30:00\"):(\"BAC\", \"2024-02-16 10:24:26\")]\n",
"focused"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"trades_df.loc[\"2024-02-16 09:30:00\":\"2024-02-16 10:24:26\"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"focohlc = ohlcv_df.loc[\"2024-02-16 09:30:00\":\"2024-02-16 10:24:26\"]\n",
"focohlc\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"focohlc.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#trades_df.to_parquet(dir + \"trades_df-BAC-2024-01-01T09:30:00-2024-05-14T16:00:00-CO4B7VPWUZF-100.parquet\", engine='pyarrow', compression='gzip')\n",
"#trades_df = pd.read_parquet(dir + \"trades_df-BAC-2024-01-01T09:30:00-2024-05-14T16:00:00-CO4B7VPWUZF-100.parquet\",engine='pyarrow')\n",
"\n",
"#trades_df.to_parquet(file_trades, engine='pyarrow', compression='gzip')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"trades_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"file_trades"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#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",
"file_name = \"\"\n",
"ohlcv_df = pd.read_parquet(file_ohlcv,engine='pyarrow')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ohlcv_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"# Calculate daily returns\n",
"ohlcv_df['returns'] = ohlcv_df['close'].pct_change().dropna()\n",
"#same as above but pct_change is from 3 datapoints back, but only if it is the same date, else na\n",
"\n",
"\n",
"# Plot the probability distribution curve\n",
"plt.figure(figsize=(10, 6))\n",
"sns.histplot(df['returns'].dropna(), kde=True, stat='probability', bins=30)\n",
"plt.title('Probability Distribution of Daily Returns')\n",
"plt.xlabel('Daily Returns')\n",
"plt.ylabel('Probability')\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"from sklearn.model_selection import train_test_split\n",
"from sklearn.preprocessing import StandardScaler\n",
"from sklearn.linear_model import LogisticRegression\n",
"from sklearn.metrics import accuracy_score\n",
"\n",
"# Define the intervals from 5 to 20 s, returns for each interval\n",
"#maybe use rolling window?\n",
"intervals = range(5, 21, 5)\n",
"\n",
"# Create columns for percentage returns\n",
"rolling_window = 50\n",
"\n",
"# Normalize the returns using rolling mean and std\n",
"for N in intervals:\n",
" column_name = f'returns_{N}'\n",
" rolling_mean = ohlcv_df[column_name].rolling(window=rolling_window).mean()\n",
" rolling_std = ohlcv_df[column_name].rolling(window=rolling_window).std()\n",
" ohlcv_df[f'norm_{column_name}'] = (ohlcv_df[column_name] - rolling_mean) / rolling_std\n",
"\n",
"# Display the dataframe with normalized return columns\n",
"ohlcv_df\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Calculate the sum of the normalized return columns for each row\n",
"ohlcv_df['sum_norm_returns'] = ohlcv_df[[f'norm_returns_{N}' for N in intervals]].sum(axis=1)\n",
"\n",
"# Sort the DataFrame based on the sum of normalized returns in descending order\n",
"df_sorted = ohlcv_df.sort_values(by='sum_norm_returns', ascending=False)\n",
"\n",
"# Display the top rows with the highest sum of normalized returns\n",
"df_sorted\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Drop initial rows with NaN values due to pct_change\n",
"ohlcv_df.dropna(inplace=True)\n",
"\n",
"# Plotting the probability distribution curves\n",
"plt.figure(figsize=(14, 8))\n",
"for N in intervals:\n",
" sns.kdeplot(ohlcv_df[f'returns_{N}'].dropna(), label=f'Returns {N}', fill=True)\n",
"\n",
"plt.title('Probability Distribution of Percentage Returns')\n",
"plt.xlabel('Percentage Return')\n",
"plt.ylabel('Density')\n",
"plt.legend()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"# Plot the probability distribution curve\n",
"plt.figure(figsize=(10, 6))\n",
"sns.histplot(ohlcv_df['returns'].dropna(), kde=True, stat='probability', bins=30)\n",
"plt.title('Probability Distribution of Daily Returns')\n",
"plt.xlabel('Daily Returns')\n",
"plt.ylabel('Probability')\n",
"plt.show()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#show only rows from ohlcv_df where returns > 0.005\n",
"ohlcv_df[ohlcv_df['returns'] > 0.0005]\n",
"\n",
"#ohlcv_df[ohlcv_df['returns'] < -0.005]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#ohlcv where index = date 2024-03-13 and between hour 12\n",
"\n",
"a = ohlcv_df.loc['2024-03-13 12:00:00':'2024-03-13 13:00:00']\n",
"a"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ohlcv_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"trades_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ohlcv_df.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"trades_df.to_parquet(\"trades_df-spy-0111-0111.parquett\", engine='pyarrow', compression='gzip')\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"trades_df.to_parquet(\"trades_df-spy-111-0516.parquett\", engine='pyarrow', compression='gzip', allow_truncated_timestamps=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ohlcv_df.to_parquet(\"ohlcv_df-spy-111-0516.parquett\", engine='pyarrow', compression='gzip')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"basic_data = vbt.Data.from_data(vbt.symbol_dict({symbol: ohlcv_df}), tz_convert=zoneNY)\n",
"vbt.settings['plotting']['auto_rangebreaks'] = True\n",
"basic_data.ohlcv.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#access just BCA\n",
"#df_filtered = df.loc[\"BAC\"]"
]
}
],
"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
}

467
research/rsi_alpaca.ipynb Normal file
View File

@ -0,0 +1,467 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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",
"from datetime import timedelta, datetime\n",
"import vectorbtpro as vbt\n",
"import os\n",
"from itables import init_notebook_mode, show\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",
"# Alpaca API credentials\n",
"ALPACA_API_KEY = os.environ.get('ACCOUNT1_PAPER_API_KEY')\n",
"ALPACA_API_SECRET = os.environ.get('ACCOUNT1_PAPER_SECRET_KEY')\n",
"\n",
"# Initialize Alpaca data client\n",
"alpaca_data = vbt.AlpacaData.set_custom_settings(client_config=dict(\n",
" api_key=ALPACA_API_KEY,\n",
" secret_key=ALPACA_API_SECRET\n",
" )\n",
")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Fetch Data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Define the symbol, start, and end dates for your data\n",
"symbol = 'BAC'\n",
"start_date = datetime.now() - timedelta(days=10) # Last 30 days\n",
"end_date = datetime.now() - timedelta(days=1) # yesterday\n",
"time_interval = '1T' # 1-minute intervals '1 minute'\n",
"\n",
"basic_data = vbt.AlpacaData.pull([\"BAC\"], start=start_date, end=end_date, timeframe=time_interval, tz=\"America/New_York\")\n",
"basic_data = basic_data.transform(lambda x: x.between_time(\"9:30\",\"16:00\"))\n",
"#basic_data.data[\"BAC\"].vbt.ohlcv.plot()\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', '2024-04-23']).tz_localize('US/Eastern')\n",
"# filtered_df = df.loc[df.index.normalize().isin(dates_of_interest)]\n",
"\n",
"# df = filtered_df\n",
"# df.info()\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_data = m15_data.transform(lambda x: x.between_time(\"9:30\",\"15:59\"))\n",
"\n",
"m15 = m15_data.data[\"BAC\"]\n",
"\n",
"m15.vbt.ohlcv.plot()\n",
"\n",
"m15\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": [
"# Calculate VWAP"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#WWAP\n",
"vbt.phelp(vbt.VWAP.run)\n",
"close = m1_data.close\n",
"high = m1_data.high\n",
"low = m1_data.low\n",
"volume = m1_data.volume\n",
"vwapD = vbt.VWAP.run(high, low, close, volume, anchor=\"D\")\n",
"# vwapT = vbt.VWAP.run(high, low, close, volume, anchor=\"T\")\n",
"\n",
"#vwap.vwap\n",
"\n",
"fig = m1_data.data[\"BAC\"].vbt.ohlcv.plot()\n",
"vwapD.vwap.vbt.plot(fig=fig)\n",
"#vwapT.vwap.vbt.plot(fig=fig)\n",
"fig.show()\n",
"\n",
"vwapD.vwap\n",
"\n",
"#vwap = vbt.VWAP.run()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m1_data.data[\"BAC\"]"
]
},
{
"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 = 1\n",
"entry_window_closes = 350\n",
"\n",
"forced_exit_start = 360\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",
"long_entries = (rsi.rsi.vbt.crossed_below(28) & entry_window_open)\n",
"long_exits = (rsi.rsi.vbt.crossed_above(70) | forced_exit)\n",
"#long_entries.info()\n",
"#number of trues and falses in long_entries\n",
"#long_entries.value_counts()\n",
"long_exits.value_counts()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"close"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def plot_rsi(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=True)) \n",
" exits.vbt.signals.plot_as_exits(close, fig=fig, add_trace_kwargs=dict(secondary_y=True)) \n",
" return fig\n",
"\n",
"plot_rsi(rsi, close, long_entries, long_exits)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"rsi.rsi"
]
},
{
"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.4/100, 0.05/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, entries=long_entries, exits=long_exits, sl_stop=sl_stop, tp_stop = sl_stop, 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": [
"pf[(0.0003,0.0018)].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
}

View File

@ -0,0 +1,949 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"source": [
"# parallel_coordinates method¶\n",
"\n",
"# attach_px_methods.<locals>.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": [],
"source": [
"pf_results"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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
}

View File

@ -0,0 +1,265 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"celkovy optimalizacni backtest na vetsim oknu 1 - 300\n",
"a možná take to udělat jako parametr\n",
"zkusit CV\n",
"zobrazit nejak robustnost parametru"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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",
"vbt.settings.returns.year_freq = \"252 days\" \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=\"f1ac6651\", #138170bc 0fb5043a bde6d0be f1ac6651\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",
"# 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",
"# #basic_data.info()\n",
"\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": [
"# basic_data.stats()\n",
"\n",
"basic_data.data[\"SPY\"].info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vbt.open_api_ref(vbt.base)\n",
"\n",
"vbt"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"##na toto udelat crosssvalidationu nebo alespon na testovacim ci jinem obdobi\n",
"#take udelat long leg - tato je shortovaci\n",
"\n",
"#8\t-0.06\t-0.2\t0.0028\t0.0048\t4.156254\n",
"\n",
"#short combination ok for train(4)/test(0.1) (window 1-90, fe 95-100)\n",
"#2,\t-0.02,\t-0.25,\t0.0018,\t0.0068\n",
"\n",
"#dalsi ok hodnota shortu for train/test 4/1\n",
"#70,\t8,\t-0.06,\t-0.2,\t0.0013,\t0.0053\t\n",
"\n",
"\n",
"#kombinace bez roc_th, train/test 7/-1.5\n",
"#70\t7\t-0.07\t0.0033\t0.0063\n",
"\n",
"#opet bez roc_th, train(5.77)/test 0.9 - spolus tsl_stop + tsl_th\n",
"#29\t7\t-0.09\t0.0033\t0.0068\n",
"\n",
"#bez roc_th a s trailing sl train/test 8.1/-0.8 \n",
"#70\t2\t-0.05\t0.0018\t0.0068\n",
"\n",
"\n",
"#SPY - short\n",
"# entry_window_closes\tmom_timeperiod\tmom_th\tsl_stop\ttp_stop\t\t\t\t\t\n",
"# 10\t6\t-0.13\t0.0022\t0.0057\n",
"\n",
"#5\t7\t-0.19\t0.0037\t0.0042\n",
"\n",
"#SPY - long\n",
"#45\t4\t0.27\t0.0047\t0.0067\n",
"\n",
"# TODO:\n",
"#- vyzkouset zda nejvyhodnejsi kombinace krom train/testu funguje i na nasledujicich dnech po trainu\n",
"# -zkusit najit v short datasetu neco vyhodneho co funguji i na testu\n",
"# - dodelat kombinace pro long signaly\n",
"# - zkusit walk forward\n",
"# - vytvorit vysledkove totoznou na v2realbot\n",
"# - podivat se jak detailne funguji tsl_stop a tsl_th\n",
"\n",
"#70,\t4,\t-0.07,\t0.0048,\t0.0068\t\n",
"\n",
"\n",
"entry_window_closes, mom_timeperiod, mom_th, sl_stop, tp_stop = 8,\t3,\t0.07,\t0.0028,\t0.0033\t\n",
"roc_th = 0\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",
"\n",
"long_signal = momshort.real_crossed_above(mom_th)\n",
"\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",
"# 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",
"\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",
"#short_entries=short_entries, short_exits=short_exits,\n",
"pf = vbt.Portfolio.from_signals(close=basic_data, entries=entries, exits=exits, tsl_stop=sl_stop, tp_stop = tp_stop, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf_B = pf.resample(\"30T\")\n",
"\n",
"pf_B.stats()\n",
"pf_B.orders.records_readable\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.get_drawdowns().records_readable"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.orders.records_readable"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.value.plot().show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"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
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,932 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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 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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"source": [
"# parallel_coordinates method¶\n",
"\n",
"# attach_px_methods.<locals>.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": [],
"source": [
"pf_results"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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
}

View File

@ -0,0 +1,842 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# CANDLEGAPS\n",
"\n",
"* gaps on second based bars indicates short-term up/down move\n",
"\n",
"TODO:\n",
"* dodělat shorty\n",
"* přidat kombinace angle nebo nějaké podobné krátkodobé momentum jako doplňkový indikátor\n",
"* vyzkouset ruzne timeframe (sec a min) + hodnotu gapu a dalsi podminky"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from dotenv import load_dotenv\n",
"\n",
"#as V2realbot is client , load env variables here\n",
"env_file = \"/Users/davidbrazda/Documents/Development/python/.env\"\n",
"# Load the .env file\n",
"load_dotenv(env_file)\n",
"\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 DATA_DIR\n",
"from lightweight_charts import JupyterChart, chart, Panel, PlotAccessor\n",
"from IPython.display import display\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"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 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",
"forced_exit_start = 380\n",
"forced_exit_end = 390\n",
"\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-BAC-2023-01-01T09_30_00-2024-05-25T15_30_00-47BCFOPUVWZ-100.parquet\"\n",
"ohlcv_df = pd.read_parquet(dir+file_name,engine='pyarrow')\n",
"#filter ohlcv_df to certain date range (assuming datetime index)\n",
"#ohlcv_df = ohlcv_df.loc[\"2024-02-12 9:30\":\"2024-02-14 16:00\"]\n",
"\n",
"#add vwap column to ohlcv_df\n",
"#ohlcv_df[\"hlcc4\"] = (ohlcv_df[\"close\"] + ohlcv_df[\"high\"] + ohlcv_df[\"low\"] + ohlcv_df[\"close\"]) / 4\n",
"\n",
"basic_data = vbt.Data.from_data(vbt.symbol_dict({\"BAC\": ohlcv_df}), tz_convert=zoneNY)\n",
"ohlcv_df= None\n",
"basic_data.wrapper.index.normalize().nunique()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"basic_data.data[\"BAC\"].info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Add resample function to custom columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro.utils.config import merge_dicts, Config, HybridConfig\n",
"from vectorbtpro import _typing as tp\n",
"from vectorbtpro.generic import nb as generic_nb\n",
"\n",
"_feature_config: tp.ClassVar[Config] = HybridConfig(\n",
" {\n",
" \"buyvolume\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" ),\n",
" \"sellvolume\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" ),\n",
" \"trades\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" )\n",
" }\n",
")\n",
"\n",
"basic_data._feature_config = _feature_config"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','trades','sellvolume']]\n",
"\n",
"s2data = s1data.resample(\"2s\")\n",
"s2data = s2data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"\n",
"s5data = s1data.resample(\"5s\")\n",
"s5data = s5data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"\n",
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','trades','sellvolume']].resample(\"1T\")\n",
"t1data = t1data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"# t1data.data[\"BAC\"].info()\n",
"\n",
"t30data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','trades','sellvolume']].resample(\"30T\")\n",
"t30data = t30data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"# t30data.data[\"BAC\"].info()\n",
"\n",
"s2close = s2data.close\n",
"s1close = s1data.close\n",
"t1close = t1data.close\n",
"t30close = t30data.close\n",
"t30volume = t30data.volume\n",
"\n",
"#resample on specific index \n",
"resampler = vbt.Resampler(t30data.index, s1data.index, source_freq=\"30T\", target_freq=\"1s\")\n",
"t30close_realigned = t30close.vbt.realign_closing(resampler)\n",
"\n",
"#resample 1min to s\n",
"resampler_s = vbt.Resampler(t1data.index, s1data.index, source_freq=\"1T\", target_freq=\"1s\")\n",
"t1close_realigned = t1close.vbt.realign_closing(resampler_s)\n",
"\n",
"resampler_s = vbt.Resampler(s2data.index, s1data.index, source_freq=\"2s\", target_freq=\"1s\")\n",
"s2close_realigned = s2close.vbt.realign_closing(resampler_s)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vbt.IF.list_indicators(\"*vwap\")\n",
"vbt.phelp(vbt.VWAP.run)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# VWAP"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"t1vwap_h = vbt.VWAP.run(t1data.high, t1data.low, t1data.close, t1data.volume, anchor=\"H\")\n",
"t1vwap_d = vbt.VWAP.run(t1data.high, t1data.low, t1data.close, t1data.volume, anchor=\"D\")\n",
"t1vwap_t = vbt.VWAP.run(t1data.high, t1data.low, t1data.close, t1data.volume, anchor=\"T\")\n",
"\n",
"t1vwap_h_real = t1vwap_h.vwap.vbt.realign_closing(resampler_s)\n",
"t1vwap_d_real = t1vwap_d.vwap.vbt.realign_closing(resampler_s)\n",
"t1vwap_t_real = t1vwap_t.vwap.vbt.realign_closing(resampler_s)\n",
"\n",
"#t1vwap_5t.xloc[\"2024-01-3 09:30:00\":\"2024-01-03 16:00:00\"].plot()\n",
"\n",
"div_rel = (s1data.close.vbt - t1vwap_h_real) - 1\n",
"\n",
"div_rel"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#m30data.close.lw.plot()\n",
"#quick few liner\n",
"pane1 = Panel(\n",
" histogram=[\n",
" #(s1data.volume, \"volume\",None, 0.8),\n",
" #(m30volume, \"m30volume\",None, 1)\n",
" ], # [(series, name, \"rgba(53, 94, 59, 0.6)\", opacity)]\n",
" right=[\n",
" (s1data.close, \"1s close\"),\n",
" (s2data.close, \"1s close\"),\n",
" (t1data.close, \"1min close\"),\n",
" (t1vwap_t, \"1mvwap_t\"),\n",
" (t1vwap_h, \"1mvwap_h\"),\n",
" (t1vwap_d, \"1mvwap_d\"),\n",
" (t1vwap_t_real, \"1mvwap_t_real\"),\n",
" (t1vwap_h_real, \"1mvwap_h_real\"),\n",
" (t1vwap_d_real, \"1mvwap_d_real\")\n",
" # (t1close_realigned, \"1min close realigned\"),\n",
" # (m30data.close, \"30min-close\"),\n",
" # (m30close_realigned, \"30min close realigned\"),\n",
" ],\n",
" left = [\n",
" (div_rel, \"reldiv1s_1Hvwap\",)\n",
" ]\n",
")\n",
"ch = chart([pane1], size=\"s\", xloc=slice(\"2024-05-1 09:30:00\",\"2024-05-5 16:00:00\"))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# SUPERTREND"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"supertrend_s1 = vbt.SUPERTREND.run(s1data.high, s1data.low, s1data.close, period=5, multiplier=3)\n",
"direction_series_s1 = supertrend_s1.direction\n",
"supertrend_t1 = vbt.SUPERTREND.run(t1data.high, t1data.low, t1data.close, period=14, multiplier=3)\n",
"direction_series_t1 = supertrend_t1.direction\n",
"supertrend_t30 = vbt.SUPERTREND.run(t30data.high, t30data.low, t30data.close, period=14, multiplier=3)\n",
"direction_series_t30 = supertrend_t30.direction\n",
"\n",
"resampler_1t_sec = vbt.Resampler(direction_series_t1.index, direction_series_s1.index, source_freq=\"1T\", target_freq=\"1s\")\n",
"resampler_30t_sec = vbt.Resampler(direction_series_t30.index, direction_series_s1.index, source_freq=\"30T\", target_freq=\"1s\")\n",
"direction_series_t1_realigned = direction_series_t1.vbt.realign_closing(resampler_1t_sec)\n",
"direction_series_t30_realigned = direction_series_t30.vbt.realign_closing(resampler_30t_sec)\n",
"\n",
"#supertrend_s1.xloc[\"2024-01-3 09:30:00\":\"2024-01-03 16:00:00\"].plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# aligned_ups= pd.Series(False, index=direction_real.index)\n",
"# aligned_downs= pd.Series(False, index=direction_real.index)\n",
"\n",
"# aligned_ups = direction_real == 1 & supertrend.direction == 1\n",
"# aligned_ups"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s5close = s5data.data[\"BAC\"].close\n",
"s5open = s5data.data[\"BAC\"].open\n",
"s5high = s5data.data[\"BAC\"].high\n",
"s5low = s5data.data[\"BAC\"].low\n",
"s5close_prev = s5close.shift(1)\n",
"s5open_prev = s5open.shift(1)\n",
"s5high_prev = s5high.shift(1)\n",
"s5low_prev = s5low.shift(1)\n",
"#gap nahoru od byci svicky a nevraci se zpet na jeji uroven\n",
"entry_ups = (s5close_prev > s5open_prev) & (s5open > s5high_prev + 0.010) & (s5close > s5close_prev) & (s5close > s5open)\n",
"\n",
"print(entry_ups.value_counts())\n",
"\n",
"entry_downs = (s5close_prev < s5open_prev) & (s5open < s5low_prev - 0.012) & (s5close < s5close_prev)\n",
"\n",
"print(entry_downs.value_counts())\n",
"\n",
"#entry_ups.info()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Entry window"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"market_open = datetime.time(9, 30)\n",
"market_close = datetime.time(16, 0)\n",
"entry_window_opens = 10\n",
"entry_window_closes = 370\n",
"forced_exit_start = 380\n",
"forced_exit_end = 390"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#entry_ups = pd.Series(False, index=s5data.index)\n",
"\n",
"entry_window_open= pd.Series(False, index=entry_ups.index)\n",
"# Calculate the time difference in minutes from market open for each timestamp\n",
"elapsed_min_from_open = (entry_ups.index.hour - market_open.hour) * 60 + (entry_ups.index.minute - market_open.minute)\n",
"entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
"entry_ups = entry_ups & entry_window_open\n",
"# entry_ups\n",
"\n",
"entry_down_window_open= pd.Series(False, index=entry_downs.index)\n",
"entry_down_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
"entry_downs = entry_downs & entry_down_window_open\n",
"\n",
"forced_exits = pd.Series(False, index=s5data.index)\n",
"forced_exits[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n",
"\n",
"# forced_exits\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"entry_ups.value_counts()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s5vwap_h = vbt.VWAP.run(s5data.high, s5data.low, s5data.close, s5data.volume, anchor=\"H\")\n",
"s5vwap_d = vbt.VWAP.run(s5data.high, s5data.low, s5data.close, s5data.volume, anchor=\"D\")\n",
"\n",
"# s5vwap_h_real = s5vwap_h.vwap.vbt.realign_closing(resampler_s)\n",
"# s5vwap_d_real = s5vwap_d.vwap.vbt.realign_closing(resampler_s)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pane1 = Panel(\n",
" ohlcv=(s5data.data[\"BAC\"],), #(series, entries, exits, other_markers)\n",
" histogram=[], # [(series, name, \"rgba(53, 94, 59, 0.6), opacity\")]\n",
" right=[#(bbands,), #[(series, name, entries, exits, other_markers)]\n",
" (s5data.data[\"BAC\"].close, \"close\", entry_ups, entry_downs),\n",
" (s5data.data[\"BAC\"].open, \"open\"),\n",
" (s5vwap_h, \"vwap5s_H\",),\n",
" (s5vwap_d, \"vwap5s_D\",)\n",
" # (t1data.data[\"BAC\"].vwap, \"vwap\"),\n",
" # (t1data.close, \"1min close\"),\n",
" # (supertrend_s1.trend,\"STtrend\"),\n",
" # (supertrend_s1.long,\"STlong\"),\n",
" # (supertrend_s1.short,\"STshort\")\n",
" ],\n",
" left = [\n",
" #(direction_series_s1,\"direction_s1\"),\n",
" # (direction_series_t1,\"direction_t1\"),\n",
" # (direction_series_t30,\"direction_t30\")\n",
" \n",
" ],\n",
" # right=[(bbands.upperband, \"upperband\",),\n",
" # (bbands.lowerband, \"lowerband\",),\n",
" # (bbands.middleband, \"middleband\",)\n",
" # ], #[(series, name, entries, exits, other_markers)]\n",
" middle1=[],\n",
" middle2=[],\n",
")\n",
"\n",
"# pane2 = Panel(\n",
"# ohlcv=(t1data.data[\"BAC\"],uptrend_m30, downtrend_m30), #(series, entries, exits, other_markers)\n",
"# histogram=[], # [(series, name, \"rgba(53, 94, 59, 0.6), opacity\")]\n",
"# left=[#(bbands,), #[(series, name, entries, exits, other_markers)]\n",
"# (direction_real,\"direction30min_real\"),\n",
"# ],\n",
"# # left = [(supertrendm30.direction,\"STdirection30\")],\n",
"# # # right=[(bbands.upperband, \"upperband\",),\n",
"# # # (bbands.lowerband, \"lowerband\",),\n",
"# # # (bbands.middleband, \"middleband\",)\n",
"# # # ], #[(series, name, entries, exits, other_markers)]\n",
"# middle1=[],\n",
"# middle2=[],\n",
"# title = \"1m\")\n",
"\n",
"ch = chart([pane1], sync=True, size=\"s\", xloc=slice(\"2024-05-20 09:30:00\",\"2024-05-25 16:00:00\"), precision=6)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.set_option('display.max_rows', None)\n",
"# data = s5data.xloc[\"2024-01-03 09:30:00\":\"2024-03-10 16:00:00\"]\n",
"# entry = entry_ups.vbt.xloc[\"2024-01-03 09:30:00\":\"2024-03-10 16:00:00\"].obj\n",
"\n",
"pf = vbt.Portfolio.from_signals(close=s5data, entries=entry_ups, exits=forced_exits, direction=\"longonly\", sl_stop=0.05/100, tp_stop = 0.05/100, fees=0.0167/100, freq=\"5s\")\n",
"pf.stats()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.xloc[\"2024-02-20 09:30:00\":\"2024-05-25 16:00:00\"].plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vbt.pdir(pf)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.xloc[\"2024-05-20 09:30:00\":\"2024-05-25 16:00:00\"].asset_value"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hourly_returns = pf.returns.resample(\"h\").get()\n",
"hourly_returns.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.returns"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.value"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.value.vbt.lineplot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf5 = pf.xloc[\"2024-05-20 09:30:00\":\"2024-05-25 16:00:00\"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"monthly_returns = pf5.returns_acc.resample(\"5T\").get()\n",
"monthly_returns = monthly_returns[monthly_returns!=0]\n",
"\n",
"monthly_returns\n",
"#monthly_returns.vbt.heatmap() \n",
"# fig = monthly_returns.vbt.heatmap() \n",
"# fig = monthly_returns.vbt.ts_heatmap() "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.set_option('display.max_rows', None)\n",
"pf.stats()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.plot().save_png()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.trades.records_readable.sort_values(by=\"PnL\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.xloc[\"2024-03-13 09:30:00\":\"2024-03-20 16:00:00\"].plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.xloc[\"2024-01-26 09:30:00\":\"2024-01-28 16:00:00\"].plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.set_option('display.max_rows', None)\n",
"pf.stats()\n",
"# pf.xloc[\"monday\"].stats()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"buyvolume = t1data.data[\"BAC\"].buyvolume\n",
"sellvolume = t1data.data[\"BAC\"].sellvolume\n",
"totalvolume = buyvolume + sellvolume\n",
"\n",
"#adjust to minimal value to avoid division by zero\n",
"sellvolume_adjusted = sellvolume.replace(0, 1e-10)\n",
"oibratio = buyvolume / sellvolume\n",
"\n",
"#cumulative order flow (net difference)\n",
"cof = buyvolume - sellvolume\n",
"\n",
"# Calculate the order imbalance (net differene) normalize the order imbalance by calculating the difference between buy and sell volumes and then scaling it by the total volume.\n",
"order_imbalance = cof / totalvolume\n",
"order_imbalance = order_imbalance.fillna(0) #nan nahradime 0\n",
"\n",
"order_imbalance_allvolume = cof / t1data.data[\"BAC\"].volume\n",
"\n",
"order_imbalance_sma = vbt.indicator(\"talib:EMA\").run(order_imbalance, timeperiod=5)\n",
"short_signals = order_imbalance.vbt < -0.5\n",
"#short_entries = oibratio.vbt < 0.01\n",
"short_signals.value_counts()\n",
"short_signals.name = \"short_entries\"\n",
"#.fillna(False)\n",
"short_exits = short_signals.shift(-2).fillna(False).astype(bool)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pane1 = Panel(\n",
" ohlcv=(t1data.data[\"BAC\"],), #(series, entries, exits, other_markers)\n",
" histogram=[(order_imbalance_allvolume, \"oib_allvolume\", \"rgba(53, 94, 59, 0.6)\",0.5),\n",
" (t1data.data[\"BAC\"].trades, \"trades\",None,0.4),\n",
" ], # [(series, name, \"rgba(53, 94, 59, 0.6)\", opacity)]\n",
" # right=[\n",
" # (supertrend.trend,\"STtrend\"),\n",
" # (supertrend.long,\"STlong\"),\n",
" # (supertrend.short,\"STshort\")\n",
" # ],\n",
" # left = [(supertrend.direction,\"STdirection\")],\n",
" # right=[(bbands.upperband, \"upperband\",),\n",
" # (bbands.lowerband, \"lowerband\",),\n",
" # (bbands.middleband, \"middleband\",)\n",
" # ], #[(series, name, entries, exits, other_markers)]\n",
" middle1=[],\n",
" middle2=[],\n",
")\n",
"\n",
"pane2 = Panel(\n",
" ohlcv=(basic_data.data[\"BAC\"],), #(series, entries, exits, other_markers)\n",
" left=[(basic_data.data[\"BAC\"].trades, \"trades\")],\n",
" histogram=[(basic_data.data[\"BAC\"].trades, \"trades_hist\", \"white\", 0.5)], #\"rgba(53, 94, 59, 0.6)\"\n",
" # ], # [(series, name, \"rgba(53, 94, 59, 0.6)\")]\n",
" # right=[\n",
" # (supertrend.trend,\"STtrend\"),\n",
" # (supertrend.long,\"STlong\"),\n",
" # (supertrend.short,\"STshort\")\n",
" # ],\n",
" # left = [(supertrend.direction,\"STdirection\")],\n",
" # right=[(bbands.upperband, \"upperband\",),\n",
" # (bbands.lowerband, \"lowerband\",),\n",
" # (bbands.middleband, \"middleband\",)\n",
" # ], #[(series, name, entries, exits, other_markers)]\n",
" middle1=[],\n",
" middle2=[],\n",
")\n",
"\n",
"\n",
"ch = chart([pane1, pane2], size=\"m\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#short_signal = t1slope.real_below(t1_th) & t2slope.real_below(t2_th) & t3slope.real_below(t3_th) & t4slope.real_below(t4_th)\n",
"#long_signal = t1slope.real_above(t1_th) & t2slope.real_above(t2_th) & t3slope.real_above(t3_th) & t4slope.real_above(t4_th)\n",
"\n",
"#test na daily s reversem crossed 0\n",
"short_signal = t2slope.vbt < -0.01 & t3slope.vbt < -0.01 #min value of threshold\n",
"long_signal = t2slope.vbt > 0.01 & t3slope.vbt > 0.01 #min\n",
"\n",
"# thirty_up_signal = t3slope.vbt.crossed_above(0.01)\n",
"# thirty_down_signal = t3slope.vbt.crossed_below(-0.01)\n",
"\n",
"fig = plot_2y_close(priminds=[], secinds=[t3slope], close=t1data.close)\n",
"#short_signal.vbt.signals.plot_as_entries(basic_data.close, fig=fig)\n",
"\n",
"short_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"SHORTS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"red\", symbol=\"triangle-down\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ))\n",
"long_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"LONGS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"limegreen\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ))\n",
"\n",
"# thirty_down_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"DOWN30\",\n",
"# line=dict(color=\"#ffe476\"),\n",
"# marker=dict(color=\"yellow\", symbol=\"triangle-down\"),\n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"# thirty_up_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"UP30\",\n",
"# line=dict(color=\"#ffe476\"),\n",
"# marker=dict(color=\"grey\"),\n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"\n",
"# thirtymin_slope_to_compare.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True), trace_kwargs=dict(name=\"30min slope\",\n",
"# line=dict(color=\"yellow\"), \n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"\n",
"fig.show()\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",
"# 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",
"\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",
"#short_entries=short_entries, short_exits=short_exits,\n",
"# pf = vbt.Portfolio.from_signals(close=basic_data, entries=short_entries, exits=exits, tsl_stop=0.005, tp_stop = 0.05, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
"\n",
"# pf.stats()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"forced_exit = t1data.symbol_wrapper.fill(False)\n",
"#entry_window_open = pd.Series(False, index=close.index)\n",
"entry_window_open= t1data.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",
"\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_signals & entry_window_open)\n",
"short_exits = forced_exit\n",
"\n",
"entries = (long_signals & entry_window_open)\n",
"exits = forced_exit\n",
"\n",
"pf = vbt.Portfolio.from_signals(close=t1data, entries=entries, exits=exits, short_entries=short_entries, short_exits=exits,\n",
"td_stop=2, time_delta_format=\"rows\",\n",
"tsl_stop=0.005, tp_stop = 0.005, 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": [
"pf.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.get_drawdowns().records_readable"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.orders.records_readable"
]
}
],
"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
}

View File

@ -0,0 +1,949 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"source": [
"# parallel_coordinates method¶\n",
"\n",
"# attach_px_methods.<locals>.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": [],
"source": [
"pf_results"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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
}

View File

@ -0,0 +1,584 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Multi timeframe momentum\n",
"Cílen je nalézt kombinaci trendu, kdy je velmi pravdě+podobné, že trend bude o určitou hodnootu ještě pokračovat.\n",
"\n",
"jsou počítány linregression úhly pro více timeframů a délku oken\n",
"\n",
"Pro každou kombinaci je daný parametr nad kterým musí být. Pokud je nad všemi pak je entry (short/long).\n",
"\n",
"Zvážit i nějaký kumulativní počítadlo anglů - něco jako trend kummulátor."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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=\"f1ac6651\", #138170bc 0fb5043a bde6d0be f1ac6651\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",
"# 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",
"# #basic_data.info()\n",
"\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-BAC-2023-01-01T09_30_00-2024-05-25T15_30_00-47BCFOPUVWZ-100.parquet\"\n",
"ohlcv_df = pd.read_parquet(dir+file_name,engine='pyarrow')\n",
"#filter ohlcv_df to certain date range (assuming datetime index)\n",
"ohlcv_df = ohlcv_df.loc[\"2024-02-12 10:30\":\"2024-02-14 12:00\"]\n",
"\n",
"#add vwap column to ohlcv_df\n",
"#ohlcv_df[\"hlcc4\"] = (ohlcv_df[\"close\"] + ohlcv_df[\"high\"] + ohlcv_df[\"low\"] + ohlcv_df[\"close\"]) / 4\n",
"\n",
"basic_data = vbt.Data.from_data(vbt.symbol_dict({\"BAC\": ohlcv_df}), tz_convert=zoneNY)\n",
"ohlcv_df= None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Add resample function to custom columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro.utils.config import merge_dicts, Config, HybridConfig\n",
"from vectorbtpro import _typing as tp\n",
"from vectorbtpro.generic import nb as generic_nb\n",
"\n",
"_feature_config: tp.ClassVar[Config] = HybridConfig(\n",
" {\n",
" \"buyvolume\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" ),\n",
" \"sellvolume\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" )\n",
" }\n",
")\n",
"\n",
"basic_data._feature_config = _feature_config"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#asic_data.stats()\n",
"basic_data.wrapper.index.normalize().nunique()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','sellvolume']].resample(\"1T\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t1data.data[\"BAC\"].buyvolume"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t1data.data[\"BAC\"].sellvolume"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"buyvolume = t1data.data[\"BAC\"].buyvolume\n",
"sellvolume = t1data.data[\"BAC\"].sellvolume\n",
"totalvolume = buyvolume + sellvolume\n",
"\n",
"#adjust to minimal value to avoid division by zero\n",
"sellvolume_adjusted = sellvolume.replace(0, 1e-10)\n",
"oibratio = buyvolume / sellvolume\n",
"\n",
"#cumulative order flow (net difference)\n",
"cof = buyvolume - sellvolume\n",
"\n",
"# Calculate the order imbalance (net differene) normalize the order imbalance by calculating the difference between buy and sell volumes and then scaling it by the total volume.\n",
"order_imbalance = cof / totalvolume\n",
"\n",
"order_imbalance_allvolume = cof / t1data.data[\"BAC\"].volume"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"order_imbalance"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#priminds list (cena), secinds list (napr. rsi), close, voluminds (volume based)\n",
"def plot_2y_close(priminds, secinds, close, volume):\n",
" fig = vbt.make_subplots(rows=2, cols=1, shared_xaxes=True, \n",
" specs=[[{\"secondary_y\": True}], [{\"secondary_y\": False}]], \n",
" vertical_spacing=0.02, subplot_titles=(\"Price and Indicators\", \"Volume\"))\n",
"\n",
" # Plotting the close price\n",
" close.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False,row=1, col=1), trace_kwargs=dict(line=dict(color=\"blue\")))\n",
" \n",
" # Plotting primary indicators on the first row\n",
" for ind in priminds:\n",
" if isinstance(ind, pd.Series):\n",
" ind = ind.vbt\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False, row=1, col=1))\n",
" \n",
" # Plotting secondary indicators on the first row\n",
" for ind in secinds:\n",
" #ind = ind.rename(str(ind.name))\n",
" if isinstance(ind, pd.Series):\n",
" ind = ind.vbt\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True, row=1, col=1))\n",
" \n",
" for indvolume in volume:\n",
" # Plotting the volume on the second row\n",
" indvolume.rename(str(indvolume.name)).vbt.barplot(fig=fig, add_trace_kwargs=dict(secondary_y=False, row=2, col=1))\n",
" #vbt.Bar(indvolume, fig=fig, add_trace_kwargs=dict(secondary_y=False, row=2, col=1))\n",
" \n",
" return fig\n",
"\n",
"plot_2y_close([], [cof,oibratio], t1data.close, [t1data.data[\"BAC\"].buyvolume, t1data.data[\"BAC\"].sellvolume, t1data.volume])\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"t0data = basic_data\n",
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap']].resample(\"1T\")\n",
"t2data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap']].resample(\"15T\")\n",
"t3data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap']].resample(\"30T\")\n",
"t4data = basic_data[['open', 'high', 'low', 'close', 'volume', 'vwap']].resample(\"D\").dropna()\n",
"\n",
"t1data = t1data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"t2data = t2data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"t3data = t3data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"\n",
"#30min data to daily\n",
"# t4data = t3data.resample(\"D\").dropna()\n",
"\n",
"#t4data = t4data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"#m1data.data[\"SPY\"].info()\n",
"\n",
"#m1data.data[\"SPY\"].vbt.ohlcv.plot()\n",
"#h2data.data[\"SPY\"].vbt.ohlcv.plot()\n",
"#ddata.data[\"SPY\"]\n",
"t2data.data[\"BAC\"].vbt.ohlcv.plot().show()\n",
"\n",
"\n",
"#t4data.data[\"BAC\"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t2data.close\n",
"\n",
"#in df remove rows with nan\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#realign na 1T = t1data + oriznout main session\n",
"t2data_vwap = t2data.vwap.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"t3data_vwap = t3data.vwap.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"t4data_vwap = t4data.vwap.vbt.realign_closing(\"1T\").dropna()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t2data_vwap"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"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",
" if isinstance(ind, pd.Series):\n",
" ind = ind.vbt\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
" for ind in secinds:\n",
" if isinstance(ind, pd.Series):\n",
" ind = ind.vbt\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True))\n",
" return fig"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t4data.clos.vbt \n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"obvind = vbt.indicator.obv.run(t1data.close, t1data.volume)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t1_lengtgh = 15\n",
"t2_length = 15\n",
"t3_length = 15\n",
"t4_length = 5\n",
"t1_th = 0.1\n",
"t2_th = 0.1\n",
"t3_th = 0.1\n",
"t4_th = 0.1\n",
"\n",
"\n",
"\n",
"#minute\n",
"t1slope = vbt.indicator(\"talib:LINEARREG_SLOPE \").run(t1data.close, timeperiod=t1_lengtgh) # -0.09, 0.09\n",
"t2slope = vbt.indicator(\"talib:LINEARREG_SLOPE \").run(t2data.vwap, timeperiod=t2_length) # -0.08 , 0.079\n",
"t3slope = vbt.indicator(\"talib:LINEARREG_SLOPE \").run(t3data.vwap, timeperiod=t3_length) # -0.08, 0.08\n",
"#daily\n",
"t4slope = vbt.indicator(\"talib:LINEARREG_SLOPE \").run(t4data.vwap, timeperiod=t4_length) # -0.1, 0.09\n",
"\n",
"plot_2y_close(priminds=[], secinds=[t1slope, t2slope, t3slope, t4slope], close=t1data.close).show()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#thirtymin_slope = thirtymin_slope.real.rename(\"30min\") #timto se prejmenuje real na 30min\n",
"t3slope = t3slope.real.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"##filter daily_slope_to_compare to only monday to friday\n",
"t3slope = t3slope[t3slope.index.dayofweek < 5]\n",
"\n",
"#t3slope.info()\n",
"\n",
"t2slope = t2slope.real.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"##filter daily_slope_to_compare to only monday to friday\n",
"t2slope = t2slope[t2slope.index.dayofweek < 5]\n",
"\n",
"t2slope.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"oibratio"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"#\n",
"short_entries = order_imbalance.vbt < 0.0002\n",
"#short_entries = oibratio.vbt < 0.01\n",
"short_entries.value_counts()\n",
"\n",
"entries = order_imbalance.vbt > 0.7\n",
"#entries = oibratio.vbt > 10\n",
"entries.value_counts()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = vbt.make_subplots(rows=3, cols=1, shared_xaxes=True, \n",
" specs=[[{\"secondary_y\": True}], [{\"secondary_y\": True}], [{\"secondary_y\": False}]], \n",
" vertical_spacing=0.02, subplot_titles=(\"Price and Indicators\", \"Volume\"))\n",
"t1data.data[\"BAC\"].vbt.ohlcv.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False, row=1, col=1))\n",
"#oibratio.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True, row=1, col=1))\n",
"order_imbalance.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True, row=1, col=1))\n",
"entries.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"LONGS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"limegreen\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ), add_trace_kwargs=dict(secondary_y=False, row=1, col=1))\n",
"\n",
"short_entries.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"SHORTS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"red\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ), add_trace_kwargs=dict(secondary_y=False, row=1, col=1))\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# thirtymin_slope_to_compare.vbt.xloc[\"04-16-2024\"].get()\n",
"thirty_down_signal.vbt.xloc[\"04-16-2024\"].get()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#short_signal = t1slope.real_below(t1_th) & t2slope.real_below(t2_th) & t3slope.real_below(t3_th) & t4slope.real_below(t4_th)\n",
"#long_signal = t1slope.real_above(t1_th) & t2slope.real_above(t2_th) & t3slope.real_above(t3_th) & t4slope.real_above(t4_th)\n",
"\n",
"#test na daily s reversem crossed 0\n",
"short_signal = t2slope.vbt < -0.01 & t3slope.vbt < -0.01 #min value of threshold\n",
"long_signal = t2slope.vbt > 0.01 & t3slope.vbt > 0.01 #min\n",
"\n",
"# thirty_up_signal = t3slope.vbt.crossed_above(0.01)\n",
"# thirty_down_signal = t3slope.vbt.crossed_below(-0.01)\n",
"\n",
"fig = plot_2y_close(priminds=[], secinds=[t3slope], close=t1data.close)\n",
"#short_signal.vbt.signals.plot_as_entries(basic_data.close, fig=fig)\n",
"\n",
"short_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"SHORTS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"red\", symbol=\"triangle-down\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ))\n",
"long_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"LONGS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"limegreen\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ))\n",
"\n",
"# thirty_down_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"DOWN30\",\n",
"# line=dict(color=\"#ffe476\"),\n",
"# marker=dict(color=\"yellow\", symbol=\"triangle-down\"),\n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"# thirty_up_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"UP30\",\n",
"# line=dict(color=\"#ffe476\"),\n",
"# marker=dict(color=\"grey\"),\n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"\n",
"# thirtymin_slope_to_compare.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True), trace_kwargs=dict(name=\"30min slope\",\n",
"# line=dict(color=\"yellow\"), \n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"\n",
"fig.show()\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",
"# 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",
"\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",
"#short_entries=short_entries, short_exits=short_exits,\n",
"# pf = vbt.Portfolio.from_signals(close=basic_data, entries=short_entries, exits=exits, tsl_stop=0.005, tp_stop = 0.05, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
"\n",
"# pf.stats()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# pf.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.get_drawdowns().records_readable"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.orders.records_readable"
]
}
],
"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
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,932 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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 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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"source": [
"# parallel_coordinates method¶\n",
"\n",
"# attach_px_methods.<locals>.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": [],
"source": [
"pf_results"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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
}

View File

@ -0,0 +1,964 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# ORDER Imbalance\n",
"\n",
"* introduced buyvolume and sellvolume on bar level.\n",
"* calculated order imbalance ratio (buyvolume-sellvolume/totalvolume)\n",
"* calculated on multiple timeframes\n",
"* entry based on confluences imbalances\n",
"\n",
"## Note\n",
"\n",
"Order disbalance nepodminuje zmenu ceny (tzn. muze byt order disbalance na buy stranu, ale cena nemusi jit nahoru a naopak)\n",
"Nicmene pokud je disbalance delsi a nedochazi ke zmene ceny - může to něco indikovat. \n",
"Vytvořit si kumulativní disbalance - kumulátory, které se budou načítat, když se budou silné disbalance, bez změny ceny. Tento akumulátor se bude nabíjet disbalancí a vybíjet příslušnou změnou ceny."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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",
"from lightweight_charts import JupyterChart, chart as chartp, Panel\n",
"from IPython.display import display\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 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-BAC-2023-01-01T09_30_00-2024-05-25T15_30_00-47BCFOPUVWZ-100.parquet\"\n",
"ohlcv_df = pd.read_parquet(dir+file_name,engine='pyarrow')\n",
"#filter ohlcv_df to certain date range (assuming datetime index)\n",
"ohlcv_df = ohlcv_df.loc[\"2024-02-12 9:30\":\"2024-03-14 16:00\"]\n",
"\n",
"#add vwap column to ohlcv_df\n",
"#ohlcv_df[\"hlcc4\"] = (ohlcv_df[\"close\"] + ohlcv_df[\"high\"] + ohlcv_df[\"low\"] + ohlcv_df[\"close\"]) / 4\n",
"\n",
"basic_data = vbt.Data.from_data(vbt.symbol_dict({\"BAC\": ohlcv_df}), tz_convert=zoneNY)\n",
"ohlcv_df= None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Add resample function to custom columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro.utils.config import merge_dicts, Config, HybridConfig\n",
"from vectorbtpro import _typing as tp\n",
"from vectorbtpro.generic import nb as generic_nb\n",
"\n",
"_feature_config: tp.ClassVar[Config] = HybridConfig(\n",
" {\n",
" \"buyvolume\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" ),\n",
" \"sellvolume\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" )\n",
" }\n",
")\n",
"\n",
"basic_data._feature_config = _feature_config"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#asic_data.stats()\n",
"basic_data.wrapper.index.normalize().nunique()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#basic_data.ohlcv.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"second_data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','sellvolume']]\n",
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','sellvolume']].resample(\"1T\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t1data = t1data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"#t1data = t1data.xloc[\"2024-02-12 9:30\":\"2024-02-14 16:00\"]\n",
"basic_data = t1data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"buyvolume = t1data.data[\"BAC\"].buyvolume\n",
"sellvolume = t1data.data[\"BAC\"].sellvolume\n",
"totalvolume = buyvolume + sellvolume\n",
"\n",
"#adjust to minimal value to avoid division by zero\n",
"sellvolume_adjusted = sellvolume.replace(0, 1e-10)\n",
"oibratio = buyvolume / sellvolume\n",
"\n",
"#cumulative order flow (net difference)\n",
"cof = buyvolume - sellvolume\n",
"\n",
"# Calculate the order imbalance (net differene) normalize the order imbalance by calculating the difference between buy and sell volumes and then scaling it by the total volume.\n",
"order_imbalance = cof / totalvolume\n",
"order_imbalance = order_imbalance.fillna(0) #nan nahradime 0\n",
"\n",
"order_imbalance_allvolume = cof / t1data.data[\"BAC\"].volume\n",
"\n",
"order_imbalance_sma = vbt.indicator(\"talib:EMA\").run(order_imbalance, timeperiod=5)\n",
"short_signals = order_imbalance.vbt < -0.5\n",
"#short_entries = oibratio.vbt < 0.01\n",
"short_signals.value_counts()\n",
"short_signals.name = \"short_entries\"\n",
"#.fillna(False)\n",
"short_exits = short_signals.shift(-2).fillna(False).astype(bool)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"##z tohoto si udelat plot funkci (i pro entries,exits)\n",
"#t1data = t1data[[\"open\", \"high\", \"low\", \"close\", \"volume\"]]\n",
"chart = JupyterChart(width=1000, height=600, inner_width=1, inner_height=0.5, leftScale=True)\n",
"#set resolution\n",
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','sellvolume']].resample(\"1T\")\n",
"t1data = t1data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"chart.set(t1data.data[\"BAC\"])\n",
"line_vwap = chart.create_line(name=\"vwap\")#, color=\"blue\")\n",
"line_vwap.set(t1data.vwap)\n",
"\n",
"\n",
"chart.topbar.textbox(\"title\",\"Nadpis\")\n",
"chart2 = chart.create_subchart(position='right', width=1, height=0.5, sync=True, leftScale=True)\n",
"t2data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','sellvolume']].resample(\"5T\")\n",
"t2data = t2data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"\n",
"\n",
"#5min close realigned to 1T\n",
"close_realigned = t2data.close.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"close_realigned = close_realigned[close_realigned.index.dayofweek < 5]\n",
"#close_realigned = close_realigned[close_realigned.index.dayofweek < 5]\n",
"line1 = chart.create_line(name=\"5minclose\")#, color=\"green\")\n",
"line1.set(close_realigned)\n",
"\n",
"#sma z realigned dat\n",
"sma_tp = 20\n",
"sma_t2 = vbt.indicator(\"talib:EMA\").run(close_realigned, timeperiod=sma_tp)\n",
"smaline = chart.create_line(name=f\"sma{sma_tp}\")#, color=\"blue\")\n",
"smaline.set(sma_t2)\n",
"\n",
"\n",
"#sma z puvodnich resamplovanych dat plus navic realign, melo by byt stejne \n",
"sma_real = vbt.indicator(\"talib:EMA\").run(t2data.close, timeperiod=sma_tp)\n",
"sma_real_value = sma_real.real.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"sma_real_value = sma_real_value[sma_real_value.index.dayofweek < 5]\n",
"smaline_real = chart.create_line(name=f\"smareal{sma_tp}\", color=\"yellow\")\n",
"smaline_real.set(sma_real_value)\n",
"\n",
"#resample 15T\n",
"t15data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','sellvolume']].resample(\"15T\")\n",
"t15data = t15data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"\n",
"#5min close realigned to 1T\n",
"close_15realigned = t15data.close.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"close_15realigned = close_15realigned[close_15realigned.index.dayofweek < 5]\n",
"#close_realigned = close_realigned[close_realigned.index.dayofweek < 5]\n",
"line2 = chart.create_line(name=\"15minclose\")#, color=\"pink\")\n",
"line2.set(close_15realigned)\n",
"\n",
"\n",
"chart.legend(True)\n",
"hst = chart2.create_histogram(name=\"buyvolume\", color=\"rgba(53, 94, 59, 0.6)\") #green transparent\n",
"hst1 = chart2.create_histogram(name=\"sellvolume\", color=\"rgba(165, 42, 42, 0.6)\") #red transparent\n",
"hst.set(t1data.data[\"BAC\"])\n",
"hst1.set(t1data.data[\"BAC\"])\n",
"line2 = chart2.create_line(name=\"5minclose\")#, color=\"green\")\n",
"line2.set(close_realigned)\n",
"\n",
"lineoib = chart2.create_line(name=\"oib\", priceScaleId=\"left\") #color=\"violet\", \n",
"#lineoib.scale(0.7,0)\n",
"lineoib.set(order_imbalance_allvolume)\n",
"\n",
"lineoib_sma = chart2.create_line(name=\"oibsma5\", priceScaleId=\"left\") #, color=\"blue\", \n",
"lineoib_sma.set(order_imbalance_sma)\n",
"\n",
"chart.fit()\n",
"chart2.legend(True)\n",
"#\n",
"line2.markers_set(short_signals, \"entries\")\n",
"# TODO jelikoz se davaji do jednoho pole je treba zajistit spravne sortovani\n",
"# domyslet jak to pojmout iterativni doplnovani markeru\n",
"line2.markers_set(short_exits, \"exits\")\n",
"\n",
"\n",
"chart2.fit()\n",
"chart.load()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"short_signals.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#vbt.IF.list_indicators(\"*ma\")\n",
"vbt.phelp(vbt.indicator(\"talib:EMA\").run)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sma = vbt.indicator(\"talib:EMA\").run(t1data.close, timeperiod=20)\n",
"sma.real.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"rr = vbt.RSI.run(t1data.close)\n",
"type(rr)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"buyvolume = t1data.data[\"BAC\"].buyvolume\n",
"sellvolume = t1data.data[\"BAC\"].sellvolume\n",
"totalvolume = buyvolume + sellvolume\n",
"\n",
"#adjust to minimal value to avoid division by zero\n",
"sellvolume_adjusted = sellvolume.replace(0, 1e-10)\n",
"oibratio = buyvolume / sellvolume\n",
"\n",
"#cumulative order flow (net difference)\n",
"cof = buyvolume - sellvolume\n",
"\n",
"# Calculate the order imbalance (net differene) normalize the order imbalance by calculating the difference between buy and sell volumes and then scaling it by the total volume.\n",
"order_imbalance = cof / totalvolume\n",
"order_imbalance = order_imbalance.fillna(0) #nan nahradime 0\n",
"\n",
"order_imbalance_allvolume = cof / t1data.data[\"BAC\"].volume"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"order_imbalance_sma = vbt.indicator(\"talib:EMA\").run(order_imbalance, timeperiod=5)\n",
"short_signals = order_imbalance.vbt < -0.5\n",
"#short_entries = oibratio.vbt < 0.01\n",
"short_signals.value_counts()\n",
"short_signals.name = \"short_entries\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"short_signals.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"short_signals"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"order_imbalance.fillna(0)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"order_imbalance.vbt.plot()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"order_imbalance"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"chartN = JupyterChart(width=500, height=300, inner_width=1, inner_height=0.3, leftScale=True)\n",
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','sellvolume']].resample(\"1T\")\n",
"t1data = t1data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"chartN.set(t1data.data[\"BAC\"])\n",
"line_sma = chartN.create_line(name=\"sma\", priceScaleId=\"right\")#, color=\"blue\")\n",
"line_sma.set(sma)\n",
"# line_sma.markers_set(short_signals, \"entries\")\n",
"# line_sma.markers_set(short_exits, \"exits\")\n",
"# hst = chartN.create_histogram(name=\"oivol\")\n",
"# hst.set(order_imbalance_allvolume)\n",
"# chartN.legend(True)\n",
"# chartN.fit()\n",
"\n",
"# subchart = chartN.create_subchart(position='right', width=1, height=0.5, sync=False, leftScale=True)\n",
"# # subchart.set(t1data.data[\"BAC\"])\n",
"# line_sma1 = subchart.create_line(name=\"smao\", priceScaleId=\"left\")#, color=\"blue\")\n",
"# line_sma1.set(sma)\n",
"# # line_sma1.markers_set(short_signals, \"entries\")\n",
"# # line_sma1.markers_set(short_exits, \"exits\")\n",
"# hsto = subchart.create_histogram(name=\"oivolo\")\n",
"# hsto.set(order_imbalance_sma)\n",
"\n",
"chart2 = chartN.create_subchart(position='left', width=1, height=0.5, sync=True, leftScale=True, toolbox=True)\n",
"# hst = chart2.create_histogram(name=\"buyvolume\", color=\"rgba(53, 94, 59, 0.6)\") #green transparent\n",
"# hst1 = chart2.create_histogram(name=\"sellvolume\", color=\"rgba(165, 42, 42, 0.6)\") #red transparent\n",
"# hst.set(t1data.data[\"BAC\"])\n",
"# hst1.set(t1data.data[\"BAC\"])\n",
"line2 = chart2.create_line(name=\"sma\")#, color=\"green\")\n",
"line2.set(sma)\n",
"chart2.topbar.textbox(\"title\",\"Nadpis\")\n",
"# chartN.topbar.textbox(\"title\",\"NadpisT\")\n",
"\n",
"# subchart.legend(True)\n",
"# subchart.fit()\n",
"chartN.load()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"##z tohoto si udelat plot funkci (i pro entries,exits)\n",
"#t1data = t1data[[\"open\", \"high\", \"low\", \"close\", \"volume\"]]\n",
"chart = JupyterChart(width=1000, height=600, inner_width=1, inner_height=0.5, leftScale=True)\n",
"#set resolution\n",
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','sellvolume']].resample(\"1T\")\n",
"t1data = t1data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"chart.set(t1data.data[\"BAC\"])\n",
"line_vwap = chart.create_line(name=\"vwap\")#, color=\"blue\")\n",
"line_vwap.set(t1data.vwap)\n",
"\n",
"\n",
"chart.topbar.textbox(\"title\",\"Nadpis\")\n",
"chart2 = chart.create_subchart(position='right', width=1, height=0.5, sync=True, leftScale=True)\n",
"t2data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','sellvolume']].resample(\"5T\")\n",
"t2data = t2data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"\n",
"\n",
"#5min close realigned to 1T\n",
"close_realigned = t2data.close.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"close_realigned = close_realigned[close_realigned.index.dayofweek < 5]\n",
"#close_realigned = close_realigned[close_realigned.index.dayofweek < 5]\n",
"line1 = chart.create_line(name=\"5minclose\")#, color=\"green\")\n",
"line1.set(close_realigned)\n",
"\n",
"#sma z realigned dat\n",
"sma_tp = 20\n",
"sma_t2 = vbt.indicator(\"talib:EMA\").run(close_realigned, timeperiod=sma_tp)\n",
"smaline = chart.create_line(name=f\"sma{sma_tp}\")#, color=\"blue\")\n",
"smaline.set(sma_t2)\n",
"\n",
"\n",
"#sma z puvodnich resamplovanych dat plus navic realign, melo by byt stejne \n",
"sma_real = vbt.indicator(\"talib:EMA\").run(t2data.close, timeperiod=sma_tp)\n",
"sma_real_value = sma_real.real.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"sma_real_value = sma_real_value[sma_real_value.index.dayofweek < 5]\n",
"smaline_real = chart.create_line(name=f\"smareal{sma_tp}\", color=\"yellow\")\n",
"smaline_real.set(sma_real_value)\n",
"\n",
"#resample 15T\n",
"t15data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','sellvolume']].resample(\"15T\")\n",
"t15data = t15data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"\n",
"#5min close realigned to 1T\n",
"close_15realigned = t15data.close.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"close_15realigned = close_15realigned[close_15realigned.index.dayofweek < 5]\n",
"#close_realigned = close_realigned[close_realigned.index.dayofweek < 5]\n",
"line2 = chart.create_line(name=\"15minclose\")#, color=\"pink\")\n",
"line2.set(close_15realigned)\n",
"\n",
"\n",
"chart.legend(True)\n",
"hst = chart2.create_histogram(name=\"buyvolume\", color=\"rgba(53, 94, 59, 0.6)\") #green transparent\n",
"hst1 = chart2.create_histogram(name=\"sellvolume\", color=\"rgba(165, 42, 42, 0.6)\") #red transparent\n",
"hst.set(t1data.data[\"BAC\"])\n",
"hst1.set(t1data.data[\"BAC\"])\n",
"line2 = chart2.create_line(name=\"5minclose\")#, color=\"green\")\n",
"line2.set(close_realigned)\n",
"\n",
"lineoib = chart2.create_line(name=\"oib\", priceScaleId=\"left\") #color=\"violet\", \n",
"#lineoib.scale(0.7,0)\n",
"lineoib.set(order_imbalance_allvolume)\n",
"\n",
"lineoib_sma = chart2.create_line(name=\"oibsma5\", priceScaleId=\"left\") #, color=\"blue\", \n",
"lineoib_sma.set(order_imbalance_sma)\n",
"\n",
"chart.fit()\n",
"chart2.legend(True)\n",
"#\n",
"line2.markers_set(short_signals, \"entries\")\n",
"# TODO jelikoz se davaji do jednoho pole je treba zajistit spravne sortovani\n",
"# domyslet jak to pojmout iterativni doplnovani markeru\n",
"line2.markers_set(short_exits, \"exits\")\n",
"\n",
"\n",
"chart2.fit()\n",
"chart.load()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sma.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#priminds list (same Y as price), secinds list (secondary Y napr. rsi), close, voluminds (volume based) list\n",
"def plot_2y_close(priminds, secinds, close, volumeinds, ohlcv=None):\n",
" fig = vbt.make_subplots(rows=2, cols=1, shared_xaxes=True, \n",
" specs=[[{\"secondary_y\": True}], [{\"secondary_y\": False}]], \n",
" vertical_spacing=0.02, subplot_titles=(\"Price and Indicators\", \"Volume\"))\n",
"\n",
" if ohlcv is not None:\n",
" ohlcv.vbt.ohlcv.plot(fig=fig, add_trace_kwargs=dict(row=1, col=1))\n",
"\n",
" # Plotting the close price\n",
" close.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False,row=1, col=1), trace_kwargs=dict(line=dict(color=\"blue\")))\n",
" \n",
" # Plotting primary indicators on the first row\n",
" for ind in priminds:\n",
" if isinstance(ind, pd.Series):\n",
" #if series has no name, make the name same as the variable name\n",
" \n",
" ind = ind.vbt\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False, row=1, col=1))\n",
" \n",
" # Plotting secondary indicators on the first row\n",
" for ind in secinds:\n",
" #ind = ind.rename(str(ind.name))\n",
" if isinstance(ind, pd.Series):\n",
" ind = ind.vbt\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True, row=1, col=1), trace_kwargs=dict(line=dict(color=\"rgba(255, 0, 0, 0.4)\")))\n",
" \n",
" for indvolume in volumeinds:\n",
" # Plotting the volume on the second row\n",
" indvolume.rename(str(indvolume.name)).vbt.barplot(fig=fig, add_trace_kwargs=dict(secondary_y=False, row=2, col=1))\n",
" #vbt.Bar(indvolume, fig=fig, add_trace_kwargs=dict(secondary_y=False, row=2, col=1))\n",
" \n",
" return fig\n",
"\n",
"fig = plot_2y_close([sma], [order_imbalance.rename(\"order_imbalance_norm\"),order_imbalance_sma.real.rename(\"oib_sma\")], t1data.close, [t1data.data[\"BAC\"].buyvolume, t1data.data[\"BAC\"].sellvolume, t1data.volume], t1data.data[\"BAC\"])\n",
"fig.update_yaxes(range=[33,34], secondary_y=False, row=1, col=1) #update y axis range\n",
"fig.update_yaxes(range=[-1,1], secondary_y=True, row=1, col=1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"t0data = basic_data\n",
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap']].resample(\"1T\")\n",
"t2data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap']].resample(\"15T\")\n",
"t3data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap']].resample(\"30T\")\n",
"t4data = basic_data[['open', 'high', 'low', 'close', 'volume', 'vwap']].resample(\"D\").dropna()\n",
"\n",
"t1data = t1data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"t2data = t2data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"t3data = t3data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"\n",
"#30min data to daily\n",
"# t4data = t3data.resample(\"D\").dropna()\n",
"\n",
"#t4data = t4data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"#m1data.data[\"SPY\"].info()\n",
"\n",
"#m1data.data[\"SPY\"].vbt.ohlcv.plot()\n",
"#h2data.data[\"SPY\"].vbt.ohlcv.plot()\n",
"#ddata.data[\"SPY\"]\n",
"t2data.data[\"BAC\"].vbt.ohlcv.plot().show()\n",
"\n",
"\n",
"#t4data.data[\"BAC\"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t2data.close\n",
"\n",
"#in df remove rows with nan\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#realign na 1T = t1data + oriznout main session\n",
"t2data_vwap = t2data.vwap.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"t3data_vwap = t3data.vwap.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"t4data_vwap = t4data.vwap.vbt.realign_closing(\"1T\").dropna()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t2data_vwap"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"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",
" if isinstance(ind, pd.Series):\n",
" ind = ind.vbt\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
" for ind in secinds:\n",
" if isinstance(ind, pd.Series):\n",
" ind = ind.vbt\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True))\n",
" return fig"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t4data.clos.vbt \n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"obvind = vbt.indicator.obv.run(t1data.close, t1data.volume)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t1_lengtgh = 15\n",
"t2_length = 15\n",
"t3_length = 15\n",
"t4_length = 5\n",
"t1_th = 0.1\n",
"t2_th = 0.1\n",
"t3_th = 0.1\n",
"t4_th = 0.1\n",
"\n",
"\n",
"\n",
"#minute\n",
"t1slope = vbt.indicator(\"talib:LINEARREG_SLOPE \").run(t1data.close, timeperiod=t1_lengtgh) # -0.09, 0.09\n",
"t2slope = vbt.indicator(\"talib:LINEARREG_SLOPE \").run(t2data.vwap, timeperiod=t2_length) # -0.08 , 0.079\n",
"t3slope = vbt.indicator(\"talib:LINEARREG_SLOPE \").run(t3data.vwap, timeperiod=t3_length) # -0.08, 0.08\n",
"#daily\n",
"t4slope = vbt.indicator(\"talib:LINEARREG_SLOPE \").run(t4data.vwap, timeperiod=t4_length) # -0.1, 0.09\n",
"\n",
"plot_2y_close(priminds=[], secinds=[t1slope, t2slope, t3slope, t4slope], close=t1data.close).show()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#thirtymin_slope = thirtymin_slope.real.rename(\"30min\") #timto se prejmenuje real na 30min\n",
"t3slope = t3slope.real.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"##filter daily_slope_to_compare to only monday to friday\n",
"t3slope = t3slope[t3slope.index.dayofweek < 5]\n",
"\n",
"#t3slope.info()\n",
"\n",
"t2slope = t2slope.real.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"##filter daily_slope_to_compare to only monday to friday\n",
"t2slope = t2slope[t2slope.index.dayofweek < 5]\n",
"\n",
"t2slope.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"oibratio"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"#\n",
"short_signals = order_imbalance.vbt < -0.3\n",
"#short_entries = oibratio.vbt < 0.01\n",
"short_signals.value_counts()\n",
"\n",
"long_signals = order_imbalance.vbt > 0.3\n",
"#entries = oibratio.vbt > 10\n",
"long_signals.value_counts()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = vbt.make_subplots(rows=3, cols=1, shared_xaxes=True, \n",
" specs=[[{\"secondary_y\": True}], [{\"secondary_y\": True}], [{\"secondary_y\": False}]], \n",
" vertical_spacing=0.02, subplot_titles=(\"Price and Indicators\", \"Volume\"))\n",
"t1data.data[\"BAC\"].vbt.ohlcv.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False, row=1, col=1))\n",
"#oibratio.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True, row=1, col=1))\n",
"order_imbalance.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True, row=1, col=1))\n",
"long_signals.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"LONGS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"limegreen\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ), add_trace_kwargs=dict(secondary_y=False, row=1, col=1))\n",
"\n",
"short_signals.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"SHORTS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"red\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ), add_trace_kwargs=dict(secondary_y=False, row=1, col=1))\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# thirtymin_slope_to_compare.vbt.xloc[\"04-16-2024\"].get()\n",
"thirty_down_signal.vbt.xloc[\"04-16-2024\"].get()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#short_signal = t1slope.real_below(t1_th) & t2slope.real_below(t2_th) & t3slope.real_below(t3_th) & t4slope.real_below(t4_th)\n",
"#long_signal = t1slope.real_above(t1_th) & t2slope.real_above(t2_th) & t3slope.real_above(t3_th) & t4slope.real_above(t4_th)\n",
"\n",
"#test na daily s reversem crossed 0\n",
"short_signal = t2slope.vbt < -0.01 & t3slope.vbt < -0.01 #min value of threshold\n",
"long_signal = t2slope.vbt > 0.01 & t3slope.vbt > 0.01 #min\n",
"\n",
"# thirty_up_signal = t3slope.vbt.crossed_above(0.01)\n",
"# thirty_down_signal = t3slope.vbt.crossed_below(-0.01)\n",
"\n",
"fig = plot_2y_close(priminds=[], secinds=[t3slope], close=t1data.close)\n",
"#short_signal.vbt.signals.plot_as_entries(basic_data.close, fig=fig)\n",
"\n",
"short_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"SHORTS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"red\", symbol=\"triangle-down\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ))\n",
"long_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"LONGS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"limegreen\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ))\n",
"\n",
"# thirty_down_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"DOWN30\",\n",
"# line=dict(color=\"#ffe476\"),\n",
"# marker=dict(color=\"yellow\", symbol=\"triangle-down\"),\n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"# thirty_up_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"UP30\",\n",
"# line=dict(color=\"#ffe476\"),\n",
"# marker=dict(color=\"grey\"),\n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"\n",
"# thirtymin_slope_to_compare.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True), trace_kwargs=dict(name=\"30min slope\",\n",
"# line=dict(color=\"yellow\"), \n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"\n",
"fig.show()\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",
"# 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",
"\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",
"#short_entries=short_entries, short_exits=short_exits,\n",
"# pf = vbt.Portfolio.from_signals(close=basic_data, entries=short_entries, exits=exits, tsl_stop=0.005, tp_stop = 0.05, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
"\n",
"# pf.stats()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"forced_exit = t1data.symbol_wrapper.fill(False)\n",
"#entry_window_open = pd.Series(False, index=close.index)\n",
"entry_window_open= t1data.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",
"\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_signals & entry_window_open)\n",
"short_exits = forced_exit\n",
"\n",
"entries = (long_signals & entry_window_open)\n",
"exits = forced_exit\n",
"\n",
"pf = vbt.Portfolio.from_signals(close=t1data, entries=entries, exits=exits, short_entries=short_entries, short_exits=exits,\n",
"td_stop=2, time_delta_format=\"rows\",\n",
"tsl_stop=0.005, tp_stop = 0.005, 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": [
"pf.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.get_drawdowns().records_readable"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.orders.records_readable"
]
}
],
"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
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,932 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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 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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"source": [
"# parallel_coordinates method¶\n",
"\n",
"# attach_px_methods.<locals>.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": [],
"source": [
"pf_results"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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
}

View File

@ -0,0 +1,679 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# SUPERTREND\n",
"\n",
"* kombinace supertrendu na vice urovnich"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from dotenv import load_dotenv\n",
"\n",
"#as V2realbot is client , load env variables here\n",
"env_file = \"/Users/davidbrazda/Documents/Development/python/.env\"\n",
"# Load the .env file\n",
"load_dotenv(env_file)\n",
"\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 DATA_DIR\n",
"from lightweight_charts import JupyterChart, chart, Panel, PlotAccessor\n",
"from IPython.display import display\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"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 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",
"forced_exit_start = 380\n",
"forced_exit_end = 390\n",
"\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-BAC-2023-01-01T09_30_00-2024-05-25T15_30_00-47BCFOPUVWZ-100.parquet\"\n",
"ohlcv_df = pd.read_parquet(dir+file_name,engine='pyarrow')\n",
"#filter ohlcv_df to certain date range (assuming datetime index)\n",
"#ohlcv_df = ohlcv_df.loc[\"2024-02-12 9:30\":\"2024-02-14 16:00\"]\n",
"\n",
"#add vwap column to ohlcv_df\n",
"#ohlcv_df[\"hlcc4\"] = (ohlcv_df[\"close\"] + ohlcv_df[\"high\"] + ohlcv_df[\"low\"] + ohlcv_df[\"close\"]) / 4\n",
"\n",
"basic_data = vbt.Data.from_data(vbt.symbol_dict({\"BAC\": ohlcv_df}), tz_convert=zoneNY)\n",
"ohlcv_df= None\n",
"basic_data.wrapper.index.normalize().nunique()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"basic_data.data[\"BAC\"].info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Add resample function to custom columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro.utils.config import merge_dicts, Config, HybridConfig\n",
"from vectorbtpro import _typing as tp\n",
"from vectorbtpro.generic import nb as generic_nb\n",
"\n",
"_feature_config: tp.ClassVar[Config] = HybridConfig(\n",
" {\n",
" \"buyvolume\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" ),\n",
" \"sellvolume\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" ),\n",
" \"trades\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" )\n",
" }\n",
")\n",
"\n",
"basic_data._feature_config = _feature_config"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','trades','sellvolume']]\n",
"\n",
"s5data = s1data.resample(\"5s\")\n",
"s5data = s5data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"\n",
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','trades','sellvolume']].resample(\"1T\")\n",
"t1data = t1data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"# t1data.data[\"BAC\"].info()\n",
"\n",
"t30data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','trades','sellvolume']].resample(\"30T\")\n",
"t30data = t30data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"# t30data.data[\"BAC\"].info()\n",
"\n",
"s1close = s1data.close\n",
"t1close = t1data.close\n",
"t30close = t30data.close\n",
"t30volume = t30data.volume\n",
"\n",
"#resample on specific index \n",
"resampler = vbt.Resampler(t30data.index, s1data.index, source_freq=\"30T\", target_freq=\"1s\")\n",
"t30close_realigned = t30close.vbt.realign_closing(resampler)\n",
"\n",
"#resample 1min to s\n",
"resampler_s = vbt.Resampler(t1data.index, s1data.index, source_freq=\"1T\", target_freq=\"1s\")\n",
"t1close_realigned = t1close.vbt.realign_closing(resampler_s)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vbt.IF.list_indicators(\"*vwap\")\n",
"vbt.phelp(vbt.VWAP.run)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# VWAP"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"t1vwap_h = vbt.VWAP.run(t1data.high, t1data.low, t1data.close, t1data.volume, anchor=\"H\")\n",
"t1vwap_d = vbt.VWAP.run(t1data.high, t1data.low, t1data.close, t1data.volume, anchor=\"D\")\n",
"t1vwap_t = vbt.VWAP.run(t1data.high, t1data.low, t1data.close, t1data.volume, anchor=\"T\")\n",
"\n",
"t1vwap_h_real = t1vwap_h.vwap.vbt.realign_closing(resampler_s)\n",
"t1vwap_d_real = t1vwap_d.vwap.vbt.realign_closing(resampler_s)\n",
"t1vwap_t_real = t1vwap_t.vwap.vbt.realign_closing(resampler_s)\n",
"\n",
"#t1vwap_5t.xloc[\"2024-01-3 09:30:00\":\"2024-01-03 16:00:00\"].plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#m30data.close.lw.plot()\n",
"#quick few liner\n",
"pane1 = Panel(\n",
" histogram=[\n",
" #(s1data.volume, \"volume\",None, 0.8),\n",
" #(m30volume, \"m30volume\",None, 1)\n",
" ], # [(series, name, \"rgba(53, 94, 59, 0.6)\", opacity)]\n",
" right=[\n",
" (s1data.close, \"1s close\"),\n",
" (t1data.close, \"1min close\"),\n",
" (t1vwap_t, \"1mvwap_t\"),\n",
" (t1vwap_h, \"1mvwap_h\"),\n",
" (t1vwap_d, \"1mvwap_d\"),\n",
" (t1vwap_t_real, \"1mvwap_t_real\"),\n",
" (t1vwap_h_real, \"1mvwap_h_real\"),\n",
" (t1vwap_d_real, \"1mvwap_d_real\")\n",
" # (t1close_realigned, \"1min close realigned\"),\n",
" # (m30data.close, \"30min-close\"),\n",
" # (m30close_realigned, \"30min close realigned\"),\n",
" ],\n",
")\n",
"ch = chart([pane1], size=\"s\", xloc=slice(\"2024-05-1 09:30:00\",\"2024-05-25 16:00:00\"))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# SUPERTREND"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"supertrend_s1 = vbt.SUPERTREND.run(s1data.high, s1data.low, s1data.close, period=5, multiplier=3)\n",
"direction_series_s1 = supertrend_s1.direction\n",
"supertrend_t1 = vbt.SUPERTREND.run(t1data.high, t1data.low, t1data.close, period=14, multiplier=3)\n",
"direction_series_t1 = supertrend_t1.direction\n",
"supertrend_t30 = vbt.SUPERTREND.run(t30data.high, t30data.low, t30data.close, period=14, multiplier=3)\n",
"direction_series_t30 = supertrend_t30.direction\n",
"\n",
"resampler_1t_sec = vbt.Resampler(direction_series_t1.index, direction_series_s1.index, source_freq=\"1T\", target_freq=\"1s\")\n",
"resampler_30t_sec = vbt.Resampler(direction_series_t30.index, direction_series_s1.index, source_freq=\"30T\", target_freq=\"1s\")\n",
"direction_series_t1_realigned = direction_series_t1.vbt.realign_closing(resampler_1t_sec)\n",
"direction_series_t30_realigned = direction_series_t30.vbt.realign_closing(resampler_30t_sec)\n",
"\n",
"#supertrend_s1.xloc[\"2024-01-3 09:30:00\":\"2024-01-03 16:00:00\"].plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# aligned_ups= pd.Series(False, index=direction_real.index)\n",
"# aligned_downs= pd.Series(False, index=direction_real.index)\n",
"\n",
"# aligned_ups = direction_real == 1 & supertrend.direction == 1\n",
"# aligned_ups"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s5close = s5data.data[\"BAC\"].close\n",
"s5open = s5data.data[\"BAC\"].open\n",
"s5high = s5data.data[\"BAC\"].high\n",
"s5close_prev = s5close.shift(1)\n",
"s5open_prev = s5open.shift(1)\n",
"s5high_prev = s5high.shift(1)\n",
"#gap nahoru od byci svicky a nevraci se zpet na jeji uroven\n",
"entry_ups = (s5close_prev > s5open_prev) & (s5open > s5high_prev + 0.010) & (s5close > s5close_prev)\n",
"\n",
"entry_ups.value_counts()\n",
"\n",
"#entry_ups.info()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Entry window"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"market_open = datetime.time(9, 30)\n",
"market_close = datetime.time(16, 0)\n",
"entry_window_opens = 10\n",
"entry_window_closes = 370"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"entry_window_open= pd.Series(False, index=entry_ups.index)\n",
"# Calculate the time difference in minutes from market open for each timestamp\n",
"elapsed_min_from_open = (entry_ups.index.hour - market_open.hour) * 60 + (entry_ups.index.minute - market_open.minute)\n",
"entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
"#entry_window_open\n",
"\n",
"entry_ups = entry_ups & entry_window_open\n",
"# entry_ups\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s5vwap_h = vbt.VWAP.run(s5data.high, s5data.low, s5data.close, s5data.volume, anchor=\"H\")\n",
"s5vwap_d = vbt.VWAP.run(s5data.high, s5data.low, s5data.close, s5data.volume, anchor=\"D\")\n",
"\n",
"# s5vwap_h_real = s5vwap_h.vwap.vbt.realign_closing(resampler_s)\n",
"# s5vwap_d_real = s5vwap_d.vwap.vbt.realign_closing(resampler_s)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pane1 = Panel(\n",
" ohlcv=(s5data.data[\"BAC\"],), #(series, entries, exits, other_markers)\n",
" histogram=[], # [(series, name, \"rgba(53, 94, 59, 0.6), opacity\")]\n",
" right=[#(bbands,), #[(series, name, entries, exits, other_markers)]\n",
" (s5data.data[\"BAC\"].close, \"close\", entry_ups),\n",
" (s5data.data[\"BAC\"].open, \"open\"),\n",
" (s5vwap_h, \"vwap5s_H\",),\n",
" (s5vwap_d, \"vwap5s_D\",)\n",
" # (t1data.data[\"BAC\"].vwap, \"vwap\"),\n",
" # (t1data.close, \"1min close\"),\n",
" # (supertrend_s1.trend,\"STtrend\"),\n",
" # (supertrend_s1.long,\"STlong\"),\n",
" # (supertrend_s1.short,\"STshort\")\n",
" ],\n",
" left = [\n",
" #(direction_series_s1,\"direction_s1\"),\n",
" # (direction_series_t1,\"direction_t1\"),\n",
" # (direction_series_t30,\"direction_t30\")\n",
" \n",
" ],\n",
" # right=[(bbands.upperband, \"upperband\",),\n",
" # (bbands.lowerband, \"lowerband\",),\n",
" # (bbands.middleband, \"middleband\",)\n",
" # ], #[(series, name, entries, exits, other_markers)]\n",
" middle1=[],\n",
" middle2=[],\n",
")\n",
"\n",
"# pane2 = Panel(\n",
"# ohlcv=(t1data.data[\"BAC\"],uptrend_m30, downtrend_m30), #(series, entries, exits, other_markers)\n",
"# histogram=[], # [(series, name, \"rgba(53, 94, 59, 0.6), opacity\")]\n",
"# left=[#(bbands,), #[(series, name, entries, exits, other_markers)]\n",
"# (direction_real,\"direction30min_real\"),\n",
"# ],\n",
"# # left = [(supertrendm30.direction,\"STdirection30\")],\n",
"# # # right=[(bbands.upperband, \"upperband\",),\n",
"# # # (bbands.lowerband, \"lowerband\",),\n",
"# # # (bbands.middleband, \"middleband\",)\n",
"# # # ], #[(series, name, entries, exits, other_markers)]\n",
"# middle1=[],\n",
"# middle2=[],\n",
"# title = \"1m\")\n",
"\n",
"ch = chart([pane1], sync=True, size=\"s\", xloc=slice(\"2024-02-20 09:30:00\",\"2024-02-22 16:00:00\"), precision=6)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# data = s5data.xloc[\"2024-01-03 09:30:00\":\"2024-03-10 16:00:00\"]\n",
"# entry = entry_ups.vbt.xloc[\"2024-01-03 09:30:00\":\"2024-03-10 16:00:00\"].obj\n",
"\n",
"pf = vbt.Portfolio.from_signals(close=s5data, entries=entry_ups, direction=\"longonly\", sl_stop=0.05/100, tp_stop = 0.05/100, fees=0.0167/100, freq=\"5s\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.xloc[\"2024-01-26 09:30:00\":\"2024-02-28 16:00:00\"].positions.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.xloc[\"2024-01-26 09:30:00\":\"2024-01-28 16:00:00\"].plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.set_option('display.max_rows', None)\n",
"pf.stats()\n",
"# pf.xloc[\"monday\"].stats()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"buyvolume = t1data.data[\"BAC\"].buyvolume\n",
"sellvolume = t1data.data[\"BAC\"].sellvolume\n",
"totalvolume = buyvolume + sellvolume\n",
"\n",
"#adjust to minimal value to avoid division by zero\n",
"sellvolume_adjusted = sellvolume.replace(0, 1e-10)\n",
"oibratio = buyvolume / sellvolume\n",
"\n",
"#cumulative order flow (net difference)\n",
"cof = buyvolume - sellvolume\n",
"\n",
"# Calculate the order imbalance (net differene) normalize the order imbalance by calculating the difference between buy and sell volumes and then scaling it by the total volume.\n",
"order_imbalance = cof / totalvolume\n",
"order_imbalance = order_imbalance.fillna(0) #nan nahradime 0\n",
"\n",
"order_imbalance_allvolume = cof / t1data.data[\"BAC\"].volume\n",
"\n",
"order_imbalance_sma = vbt.indicator(\"talib:EMA\").run(order_imbalance, timeperiod=5)\n",
"short_signals = order_imbalance.vbt < -0.5\n",
"#short_entries = oibratio.vbt < 0.01\n",
"short_signals.value_counts()\n",
"short_signals.name = \"short_entries\"\n",
"#.fillna(False)\n",
"short_exits = short_signals.shift(-2).fillna(False).astype(bool)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pane1 = Panel(\n",
" ohlcv=(t1data.data[\"BAC\"],), #(series, entries, exits, other_markers)\n",
" histogram=[(order_imbalance_allvolume, \"oib_allvolume\", \"rgba(53, 94, 59, 0.6)\",0.5),\n",
" (t1data.data[\"BAC\"].trades, \"trades\",None,0.4),\n",
" ], # [(series, name, \"rgba(53, 94, 59, 0.6)\", opacity)]\n",
" # right=[\n",
" # (supertrend.trend,\"STtrend\"),\n",
" # (supertrend.long,\"STlong\"),\n",
" # (supertrend.short,\"STshort\")\n",
" # ],\n",
" # left = [(supertrend.direction,\"STdirection\")],\n",
" # right=[(bbands.upperband, \"upperband\",),\n",
" # (bbands.lowerband, \"lowerband\",),\n",
" # (bbands.middleband, \"middleband\",)\n",
" # ], #[(series, name, entries, exits, other_markers)]\n",
" middle1=[],\n",
" middle2=[],\n",
")\n",
"\n",
"pane2 = Panel(\n",
" ohlcv=(basic_data.data[\"BAC\"],), #(series, entries, exits, other_markers)\n",
" left=[(basic_data.data[\"BAC\"].trades, \"trades\")],\n",
" histogram=[(basic_data.data[\"BAC\"].trades, \"trades_hist\", \"white\", 0.5)], #\"rgba(53, 94, 59, 0.6)\"\n",
" # ], # [(series, name, \"rgba(53, 94, 59, 0.6)\")]\n",
" # right=[\n",
" # (supertrend.trend,\"STtrend\"),\n",
" # (supertrend.long,\"STlong\"),\n",
" # (supertrend.short,\"STshort\")\n",
" # ],\n",
" # left = [(supertrend.direction,\"STdirection\")],\n",
" # right=[(bbands.upperband, \"upperband\",),\n",
" # (bbands.lowerband, \"lowerband\",),\n",
" # (bbands.middleband, \"middleband\",)\n",
" # ], #[(series, name, entries, exits, other_markers)]\n",
" middle1=[],\n",
" middle2=[],\n",
")\n",
"\n",
"\n",
"ch = chart([pane1, pane2], size=\"m\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#short_signal = t1slope.real_below(t1_th) & t2slope.real_below(t2_th) & t3slope.real_below(t3_th) & t4slope.real_below(t4_th)\n",
"#long_signal = t1slope.real_above(t1_th) & t2slope.real_above(t2_th) & t3slope.real_above(t3_th) & t4slope.real_above(t4_th)\n",
"\n",
"#test na daily s reversem crossed 0\n",
"short_signal = t2slope.vbt < -0.01 & t3slope.vbt < -0.01 #min value of threshold\n",
"long_signal = t2slope.vbt > 0.01 & t3slope.vbt > 0.01 #min\n",
"\n",
"# thirty_up_signal = t3slope.vbt.crossed_above(0.01)\n",
"# thirty_down_signal = t3slope.vbt.crossed_below(-0.01)\n",
"\n",
"fig = plot_2y_close(priminds=[], secinds=[t3slope], close=t1data.close)\n",
"#short_signal.vbt.signals.plot_as_entries(basic_data.close, fig=fig)\n",
"\n",
"short_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"SHORTS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"red\", symbol=\"triangle-down\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ))\n",
"long_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"LONGS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"limegreen\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ))\n",
"\n",
"# thirty_down_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"DOWN30\",\n",
"# line=dict(color=\"#ffe476\"),\n",
"# marker=dict(color=\"yellow\", symbol=\"triangle-down\"),\n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"# thirty_up_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"UP30\",\n",
"# line=dict(color=\"#ffe476\"),\n",
"# marker=dict(color=\"grey\"),\n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"\n",
"# thirtymin_slope_to_compare.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True), trace_kwargs=dict(name=\"30min slope\",\n",
"# line=dict(color=\"yellow\"), \n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"\n",
"fig.show()\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",
"# 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",
"\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",
"#short_entries=short_entries, short_exits=short_exits,\n",
"# pf = vbt.Portfolio.from_signals(close=basic_data, entries=short_entries, exits=exits, tsl_stop=0.005, tp_stop = 0.05, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
"\n",
"# pf.stats()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"forced_exit = t1data.symbol_wrapper.fill(False)\n",
"#entry_window_open = pd.Series(False, index=close.index)\n",
"entry_window_open= t1data.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",
"\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_signals & entry_window_open)\n",
"short_exits = forced_exit\n",
"\n",
"entries = (long_signals & entry_window_open)\n",
"exits = forced_exit\n",
"\n",
"pf = vbt.Portfolio.from_signals(close=t1data, entries=entries, exits=exits, short_entries=short_entries, short_exits=exits,\n",
"td_stop=2, time_delta_format=\"rows\",\n",
"tsl_stop=0.005, tp_stop = 0.005, 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": [
"pf.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.get_drawdowns().records_readable"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.orders.records_readable"
]
}
],
"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
}

View File

@ -0,0 +1,932 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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 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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"source": [
"# parallel_coordinates method¶\n",
"\n",
"# attach_px_methods.<locals>.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": [],
"source": [
"pf_results"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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
}

View File

@ -0,0 +1,595 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# TIME based entries, exits\n",
"\n",
"Recurring time bases entries and exits"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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 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-BAC-2023-01-01T09_30_00-2024-05-25T15_30_00-47BCFOPUVWZ-100.parquet\"\n",
"ohlcv_df = pd.read_parquet(dir+file_name,engine='pyarrow')\n",
"#filter ohlcv_df to certain date range (assuming datetime index)\n",
"ohlcv_df = ohlcv_df.loc[\"2024-02-12 9:30\":\"2024-02-14 16:00\"]\n",
"\n",
"#add vwap column to ohlcv_df\n",
"#ohlcv_df[\"hlcc4\"] = (ohlcv_df[\"close\"] + ohlcv_df[\"high\"] + ohlcv_df[\"low\"] + ohlcv_df[\"close\"]) / 4\n",
"\n",
"basic_data = vbt.Data.from_data(vbt.symbol_dict({\"BAC\": ohlcv_df}), tz_convert=zoneNY)\n",
"ohlcv_df= None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Add resample function to custom columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro.utils.config import merge_dicts, Config, HybridConfig\n",
"from vectorbtpro import _typing as tp\n",
"from vectorbtpro.generic import nb as generic_nb\n",
"\n",
"_feature_config: tp.ClassVar[Config] = HybridConfig(\n",
" {\n",
" \"buyvolume\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" ),\n",
" \"sellvolume\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" )\n",
" }\n",
")\n",
"\n",
"basic_data._feature_config = _feature_config"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#asic_data.stats()\n",
"basic_data.wrapper.index.normalize().nunique()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"basic_data.ohlcv.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','sellvolume']].resample(\"1T\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t1data = t1data.xloc[\"2024-02-12 9:30\":\"2024-02-12 10:20\"]\n",
"#t1data = t1data.transform(lambda df: df.between_time('09:30', '10:00').dropna())\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','sellvolume']]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"buyvolume = t1data.data[\"BAC\"].buyvolume\n",
"sellvolume = t1data.data[\"BAC\"].sellvolume\n",
"totalvolume = buyvolume + sellvolume\n",
"\n",
"#adjust to minimal value to avoid division by zero\n",
"sellvolume_adjusted = sellvolume.replace(0, 1e-10)\n",
"oibratio = buyvolume / sellvolume\n",
"\n",
"#cumulative order flow (net difference)\n",
"cof = buyvolume - sellvolume\n",
"\n",
"# Calculate the order imbalance (net differene) normalize the order imbalance by calculating the difference between buy and sell volumes and then scaling it by the total volume.\n",
"order_imbalance = cof / totalvolume\n",
"order_imbalance.fillna(0) #nan nahradime 0\n",
"\n",
"order_imbalance_allvolume = cof / t1data.data[\"BAC\"].volume"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cof\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"order_imbalance.vbt.plot()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"order_imbalance"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#priminds list (same Y as price), secinds list (secondary Y napr. rsi), close, voluminds (volume based) list\n",
"def plot_2y_close(priminds, secinds, close, volumeinds):\n",
" fig = vbt.make_subplots(rows=2, cols=1, shared_xaxes=True, \n",
" specs=[[{\"secondary_y\": True}], [{\"secondary_y\": False}]], \n",
" vertical_spacing=0.02, subplot_titles=(\"Price and Indicators\", \"Volume\"))\n",
"\n",
" # Plotting the close price\n",
" close.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False,row=1, col=1), trace_kwargs=dict(line=dict(color=\"blue\")))\n",
" \n",
" # Plotting primary indicators on the first row\n",
" for ind in priminds:\n",
" if isinstance(ind, pd.Series):\n",
" #if series has no name, make the name same as the variable name\n",
" \n",
" ind = ind.vbt\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False, row=1, col=1))\n",
" \n",
" # Plotting secondary indicators on the first row\n",
" for ind in secinds:\n",
" #ind = ind.rename(str(ind.name))\n",
" if isinstance(ind, pd.Series):\n",
" ind = ind.vbt\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True, row=1, col=1), trace_kwargs=dict(line=dict(color=\"rgba(255, 0, 0, 0.4)\")))\n",
" \n",
" for indvolume in volumeinds:\n",
" # Plotting the volume on the second row\n",
" indvolume.rename(str(indvolume.name)).vbt.barplot(fig=fig, add_trace_kwargs=dict(secondary_y=False, row=2, col=1))\n",
" #vbt.Bar(indvolume, fig=fig, add_trace_kwargs=dict(secondary_y=False, row=2, col=1))\n",
" \n",
" return fig\n",
"\n",
"plot_2y_close([], [order_imbalance.rename(\"order_imbalance_norm\")], t1data.close, [t1data.data[\"BAC\"].buyvolume, t1data.data[\"BAC\"].sellvolume, t1data.volume])\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"t0data = basic_data\n",
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap']].resample(\"1T\")\n",
"t2data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap']].resample(\"15T\")\n",
"t3data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap']].resample(\"30T\")\n",
"t4data = basic_data[['open', 'high', 'low', 'close', 'volume', 'vwap']].resample(\"D\").dropna()\n",
"\n",
"t1data = t1data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"t2data = t2data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"t3data = t3data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"\n",
"#30min data to daily\n",
"# t4data = t3data.resample(\"D\").dropna()\n",
"\n",
"#t4data = t4data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"#m1data.data[\"SPY\"].info()\n",
"\n",
"#m1data.data[\"SPY\"].vbt.ohlcv.plot()\n",
"#h2data.data[\"SPY\"].vbt.ohlcv.plot()\n",
"#ddata.data[\"SPY\"]\n",
"t2data.data[\"BAC\"].vbt.ohlcv.plot().show()\n",
"\n",
"\n",
"#t4data.data[\"BAC\"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t2data.close\n",
"\n",
"#in df remove rows with nan\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#realign na 1T = t1data + oriznout main session\n",
"t2data_vwap = t2data.vwap.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"t3data_vwap = t3data.vwap.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"t4data_vwap = t4data.vwap.vbt.realign_closing(\"1T\").dropna()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t2data_vwap"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"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",
" if isinstance(ind, pd.Series):\n",
" ind = ind.vbt\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
" for ind in secinds:\n",
" if isinstance(ind, pd.Series):\n",
" ind = ind.vbt\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True))\n",
" return fig"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t4data.clos.vbt \n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"obvind = vbt.indicator.obv.run(t1data.close, t1data.volume)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"t1_lengtgh = 15\n",
"t2_length = 15\n",
"t3_length = 15\n",
"t4_length = 5\n",
"t1_th = 0.1\n",
"t2_th = 0.1\n",
"t3_th = 0.1\n",
"t4_th = 0.1\n",
"\n",
"\n",
"\n",
"#minute\n",
"t1slope = vbt.indicator(\"talib:LINEARREG_SLOPE \").run(t1data.close, timeperiod=t1_lengtgh) # -0.09, 0.09\n",
"t2slope = vbt.indicator(\"talib:LINEARREG_SLOPE \").run(t2data.vwap, timeperiod=t2_length) # -0.08 , 0.079\n",
"t3slope = vbt.indicator(\"talib:LINEARREG_SLOPE \").run(t3data.vwap, timeperiod=t3_length) # -0.08, 0.08\n",
"#daily\n",
"t4slope = vbt.indicator(\"talib:LINEARREG_SLOPE \").run(t4data.vwap, timeperiod=t4_length) # -0.1, 0.09\n",
"\n",
"plot_2y_close(priminds=[], secinds=[t1slope, t2slope, t3slope, t4slope], close=t1data.close).show()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#thirtymin_slope = thirtymin_slope.real.rename(\"30min\") #timto se prejmenuje real na 30min\n",
"t3slope = t3slope.real.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"##filter daily_slope_to_compare to only monday to friday\n",
"t3slope = t3slope[t3slope.index.dayofweek < 5]\n",
"\n",
"#t3slope.info()\n",
"\n",
"t2slope = t2slope.real.vbt.realign_closing(\"1T\").between_time('09:30', '16:00').dropna()\n",
"##filter daily_slope_to_compare to only monday to friday\n",
"t2slope = t2slope[t2slope.index.dayofweek < 5]\n",
"\n",
"t2slope.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"oibratio"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"#\n",
"short_entries = order_imbalance.vbt < 0.0002\n",
"#short_entries = oibratio.vbt < 0.01\n",
"short_entries.value_counts()\n",
"\n",
"entries = order_imbalance.vbt > 0.7\n",
"#entries = oibratio.vbt > 10\n",
"entries.value_counts()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = vbt.make_subplots(rows=3, cols=1, shared_xaxes=True, \n",
" specs=[[{\"secondary_y\": True}], [{\"secondary_y\": True}], [{\"secondary_y\": False}]], \n",
" vertical_spacing=0.02, subplot_titles=(\"Price and Indicators\", \"Volume\"))\n",
"t1data.data[\"BAC\"].vbt.ohlcv.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False, row=1, col=1))\n",
"#oibratio.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True, row=1, col=1))\n",
"order_imbalance.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True, row=1, col=1))\n",
"entries.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"LONGS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"limegreen\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ), add_trace_kwargs=dict(secondary_y=False, row=1, col=1))\n",
"\n",
"short_entries.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"SHORTS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"red\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ), add_trace_kwargs=dict(secondary_y=False, row=1, col=1))\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# thirtymin_slope_to_compare.vbt.xloc[\"04-16-2024\"].get()\n",
"thirty_down_signal.vbt.xloc[\"04-16-2024\"].get()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#short_signal = t1slope.real_below(t1_th) & t2slope.real_below(t2_th) & t3slope.real_below(t3_th) & t4slope.real_below(t4_th)\n",
"#long_signal = t1slope.real_above(t1_th) & t2slope.real_above(t2_th) & t3slope.real_above(t3_th) & t4slope.real_above(t4_th)\n",
"\n",
"#test na daily s reversem crossed 0\n",
"short_signal = t2slope.vbt < -0.01 & t3slope.vbt < -0.01 #min value of threshold\n",
"long_signal = t2slope.vbt > 0.01 & t3slope.vbt > 0.01 #min\n",
"\n",
"# thirty_up_signal = t3slope.vbt.crossed_above(0.01)\n",
"# thirty_down_signal = t3slope.vbt.crossed_below(-0.01)\n",
"\n",
"fig = plot_2y_close(priminds=[], secinds=[t3slope], close=t1data.close)\n",
"#short_signal.vbt.signals.plot_as_entries(basic_data.close, fig=fig)\n",
"\n",
"short_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"SHORTS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"red\", symbol=\"triangle-down\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ))\n",
"long_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"LONGS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"limegreen\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ))\n",
"\n",
"# thirty_down_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"DOWN30\",\n",
"# line=dict(color=\"#ffe476\"),\n",
"# marker=dict(color=\"yellow\", symbol=\"triangle-down\"),\n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"# thirty_up_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"UP30\",\n",
"# line=dict(color=\"#ffe476\"),\n",
"# marker=dict(color=\"grey\"),\n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"\n",
"# thirtymin_slope_to_compare.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True), trace_kwargs=dict(name=\"30min slope\",\n",
"# line=dict(color=\"yellow\"), \n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"\n",
"fig.show()\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",
"# 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",
"\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",
"#short_entries=short_entries, short_exits=short_exits,\n",
"# pf = vbt.Portfolio.from_signals(close=basic_data, entries=short_entries, exits=exits, tsl_stop=0.005, tp_stop = 0.05, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
"\n",
"# pf.stats()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# pf.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.get_drawdowns().records_readable"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.orders.records_readable"
]
}
],
"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
}

161
research/test.ipynb Normal file
View File

@ -0,0 +1,161 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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",
"\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",
"res, df = load_batch(batch_id=\"e44a5075\",\n",
" space_resolution_evenly=False,\n",
" indicators_columns=[\"Rsi14\"],\n",
" main_session_only=True)\n",
"if res < 0:\n",
" print(\"Error\" + str(res) + str(df))\n",
"df = df[\"bars\"]\n",
"\n",
"#df"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# filter dates"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#naloadujeme do vbt symbol as column\n",
"basic_data = vbt.Data.from_data({\"BAC\": df}, tz_convert=zoneNY)\n",
"start_date = pd.Timestamp('2024-03-12 09:30', tz=zoneNY)\n",
"end_date = pd.Timestamp('2024-03-13 16:00', tz=zoneNY)\n",
"\n",
"#basic_data = basic_data.transform(lambda df: df[df.index.date == start_date.date()])\n",
"basic_data = basic_data.transform(lambda df: df[(df.index >= start_date) & (df.index <= end_date)])\n",
"#basic_data.data[\"BAC\"].info()\n",
"\n",
"# fig = basic_data.plot(plot_volume=False)\n",
"# pivot_info = basic_data.run(\"pivotinfo\", up_th=0.003, down_th=0.002)\n",
"# #pivot_info.plot()\n",
"# pivot_info.plot(fig=fig, conf_value_trace_kwargs=dict(visible=True))\n",
"# fig.show()\n",
"\n",
"\n",
"# rsi14 = basic_data.data[\"BAC\"][\"Rsi14\"].rename(\"Rsi14\")\n",
"\n",
"# rsi14.vbt.plot().show()\n",
"# basic_data.data[\"BAC\"].vbt.ohlcv.plot().show()"
]
},
{
"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
}

82
research/test1.ipynb Normal file
View File

@ -0,0 +1,82 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import statsmodels.api as sm\n",
"\n",
"# Example time series data\n",
"np.random.seed(0)\n",
"dates = pd.date_range('2023-01-01', periods=100)\n",
"data = pd.Series(np.random.randn(100).cumsum(), index=dates)\n",
"\n",
"# Parameters\n",
"window_size = 20\n",
"\n",
"# Function to calculate rolling window linear regression\n",
"def rolling_linreg(series, window):\n",
" intercepts = []\n",
" slopes = []\n",
" for i in range(len(series) - window + 1):\n",
" y = series[i:i + window]\n",
" x = np.arange(window)\n",
" x = sm.add_constant(x)\n",
" model = sm.OLS(y, x).fit()\n",
" intercepts.append(model.params[0])\n",
" slopes.append(model.params[1])\n",
" return intercepts, slopes\n",
"\n",
"# Calculate rolling linear regression parameters\n",
"intercepts, slopes = rolling_linreg(data, window_size)\n",
"\n",
"# Create a DataFrame for plotting\n",
"rolling_dates = dates[window_size - 1:]\n",
"rolling_intercepts = pd.Series(intercepts, index=rolling_dates)\n",
"rolling_slopes = pd.Series(slopes, index=rolling_dates)\n",
"\n",
"# Plot the original data and the rolling linear regression\n",
"plt.figure(figsize=(14, 7))\n",
"plt.plot(data, label='Original Data')\n",
"for i in range(len(rolling_intercepts)):\n",
" start_date = rolling_dates[i] - pd.DateOffset(days=window_size-1)\n",
" end_date = rolling_dates[i]\n",
" plt.plot([start_date, end_date],\n",
" [rolling_intercepts[i], rolling_intercepts[i] + rolling_slopes[i] * (window_size - 1)],\n",
" color='red', alpha=0.5)\n",
"\n",
"plt.legend()\n",
"plt.title('Rolling Window Linear Regression')\n",
"plt.xlabel('Date')\n",
"plt.ylabel('Value')\n",
"plt.show()\n"
]
}
],
"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
}

421
research/test1sbars.ipynb Normal file
View File

@ -0,0 +1,421 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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",
"\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",
"res, df = load_batch(batch_id=\"0fb5043a\", #46 days 1.3 - 6.5.\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"
]
},
{
"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', '2024-04-23']).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",
"start_date = pd.Timestamp('2024-03-12 09:30', tz=zoneNY)\n",
"end_date = pd.Timestamp('2024-03-13 16:00', tz=zoneNY)\n",
"\n",
"#basic_data = basic_data.transform(lambda df: df[df.index.date == start_date.date()])\n",
"#basic_data = basic_data.transform(lambda df: df[(df.index >= start_date) & (df.index <= end_date)])\n",
"#basic_data.data[\"BAC\"].info()\n",
"\n",
"# fig = basic_data.plot(plot_volume=False)\n",
"# pivot_info = basic_data.run(\"pivotinfo\", up_th=0.003, down_th=0.002)\n",
"# #pivot_info.plot()\n",
"# pivot_info.plot(fig=fig, conf_value_trace_kwargs=dict(visible=True))\n",
"# fig.show()\n",
"\n",
"\n",
"# rsi14 = basic_data.data[\"BAC\"][\"Rsi14\"].rename(\"Rsi14\")\n",
"\n",
"# rsi14.vbt.plot().show()\n",
"#basic_data.xloc[\"09:30\":\"10:00\"].data[\"BAC\"].vbt.ohlcv.plot().show()\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": [
"# 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 = 1\n",
"entry_window_closes = 350\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",
"long_entries = (rsi.rsi.vbt.crossed_below(20) & entry_window_open)\n",
"long_exits = (rsi.rsi.vbt.crossed_above(70) | forced_exit)\n",
"#long_entries.info()\n",
"#number of trues and falses in long_entries\n",
"long_entries.value_counts()\n",
"#long_exits.value_counts()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def plot_rsi(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(rsi.rsi, fig=fig, add_trace_kwargs=dict(secondary_y=False)) \n",
" exits.vbt.signals.plot_as_exits(rsi.rsi, fig=fig, add_trace_kwargs=dict(secondary_y=False)) \n",
" return fig\n",
"\n",
"plot_rsi(rsi, close, long_entries, long_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, entries=long_entries, sl_stop=sl_stop, tp_stop = sl_stop, exits=long_exits,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": [
"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(50, 'Total Return [%]')\n",
"#stats_df.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf[(0.0011,0.0013)].plot()\n",
"\n",
"#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
}

View File

@ -0,0 +1,935 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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",
"\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",
"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"
]
},
{
"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": null,
"metadata": {},
"outputs": [],
"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 = [3, 7, 15, 29, 45, 50, 70]\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 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.03/100, 0.7/100, 0.05/100),4).tolist()\n",
"tp_stop = np.round(np.arange(0.03/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 = 1000, 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=\"nextopen\") #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": null,
"metadata": {},
"outputs": [],
"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": null,
"metadata": {},
"outputs": [],
"source": [
"# parallel_coordinates method¶\n",
"\n",
"# attach_px_methods.<locals>.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": [],
"source": [
"pf_results"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"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
}

16
setup.py Normal file
View File

@ -0,0 +1,16 @@
from setuptools import find_packages, setup
setup(name='v2realbot_research',
version='0.1',
description='Research for v2realbot',
author='David Brazda',
author_email='davidbrazda61@gmail.com',
packages=find_packages(),
install_requires=[
'pandas',
'pywebview>=5.0.5',
'orjson',
'v2trading @ git+https://github.com/drew2323/v2trading.git@master#egg=v2trading',
'lightweight-charts-python @ https://github.com/drew2323/lightweight-charts-python.git@main#egg=lightweight-charts-python'
]
)

View File

@ -0,0 +1,499 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "a7f13baa-bf3a-41e2-b4f4-bef957746b6a",
"metadata": {},
"source": [
"# How to backtest chart patterns with VectorBT PRO"
]
},
{
"cell_type": "markdown",
"id": "1e5237a6-cb1e-42b2-8b74-841af2e8859a",
"metadata": {},
"source": [
"VectorBT PRO (https://vectorbt.pro/) is a proprietary Python package designed for backtesting and analyzing quantitative trading strategies. It provides a comprehensive suite of tools for every stage of an algorithmic trading workflow, including data acquisition, signal generation and analysis, portfolio optimization, strategy simulation, hyperparameter tuning, and cross-validation. These modular components empower users to flexibly customize their analysis, setting it apart from monolithic backtesting frameworks."
]
},
{
"cell_type": "markdown",
"id": "51ad2b2b-3ffa-4600-9f03-547f83d8babb",
"metadata": {},
"source": [
"One of these components is a data pattern detector that efficiently scans data using variable-length windows, assessing their similarity to a specified pattern. This process, optimized with Numba (https://numba.pydata.org/), operates on any hardware without the need for machine learning. To showcase the detector's capabilities, we will conduct backtesting on a range of patterns and their combinations on a single dataset."
]
},
{
"cell_type": "markdown",
"id": "36f9e6a9-eedf-4595-b214-2d00f02d9c90",
"metadata": {},
"source": [
"## Imports and set up"
]
},
{
"cell_type": "markdown",
"id": "33459b0c-c21f-4251-b13b-6492c9171f6c",
"metadata": {},
"source": [
"Due to VectorBT PRO's self-contained design, only minimal imports are necessary."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bc8d53d7-0290-4e6c-b760-6c9ba8a6873e",
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro import *\n",
"# whats_imported()\n",
"\n",
"vbt.settings.set_theme(\"dark\")"
]
},
{
"cell_type": "markdown",
"id": "0aec0980-6ee2-41b1-a713-4a062a823fe5",
"metadata": {},
"source": [
"VectorBT PRO features built-in data downloading from sources such as Yahoo Finance, Alpaca, Polygon, TradingView, and many more. We will perform pattern detection on hourly price data pulled from TradingView."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d12bcb35-95ba-424e-8dfc-0e9edff8df99",
"metadata": {},
"outputs": [],
"source": [
"symbols = [\n",
" \"NASDAQ:META\",\n",
" \"NASDAQ:AMZN\",\n",
" \"NASDAQ:AAPL\",\n",
" \"NASDAQ:NFLX\",\n",
" \"NASDAQ:GOOG\",\n",
"]\n",
"\n",
"data = vbt.TVData.pull(symbols, timeframe=\"hourly\")"
]
},
{
"cell_type": "markdown",
"id": "77e48d78-436d-4a52-95d4-8ff8c1e8ff4c",
"metadata": {},
"source": [
"TradingView does not offer the option to specify a date range in advance, so we will need to select it afterward."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b96e37aa-3c00-4373-8030-ca3d97f872b1",
"metadata": {},
"outputs": [],
"source": [
"start_date = \"2020\"\n",
"end_date = None\n",
"\n",
"data = data.xloc[start_date:end_date]"
]
},
{
"cell_type": "markdown",
"id": "3c9c8009-3a78-4799-bc98-2bd191e22851",
"metadata": {},
"source": [
"Ensure that our data spans the correct date period and is free of NaN values."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "76b530eb-f42e-4bdf-b270-20298a66eb6b",
"metadata": {},
"outputs": [],
"source": [
"print(data.stats())"
]
},
{
"cell_type": "markdown",
"id": "4cf31468-ce25-4284-b0c6-dec873e62268",
"metadata": {},
"source": [
"As pattern detection requires only a single time series, we must choose the suitable feature. We'll utilize HLC/3, which effectively captures price fluctuations."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "650c3662-684c-4e57-b7fa-45ba8b2f7f1d",
"metadata": {},
"outputs": [],
"source": [
"price = data.hlc3"
]
},
{
"cell_type": "markdown",
"id": "74dcad43-dd64-435a-9a9d-591681514209",
"metadata": {},
"source": [
"## Define patterns"
]
},
{
"cell_type": "markdown",
"id": "f387c42a-1224-46d9-9397-b6479e6e21e7",
"metadata": {},
"source": [
"Numerous chart patterns can be translated into numerical sequences, like the \"Double Top\" pattern (https://www.investopedia.com/terms/d/doubletop.asp) represented as [1, 3, 2, 3, 1]. It's important to note that while the numbers themselves can be arbitrary, their relative spacing should mirror the relative distance between the pattern's chart points. For instance, in this sequence, 2 aligns with the midpoint between valley point 1 and peak point 3. The same principle applies to temporal distribution: points should be equidistant from one another."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "40827a2a-ee12-4feb-9f6a-4505ed24060d",
"metadata": {},
"outputs": [],
"source": [
"bullish_patterns = {\n",
" \"double_bottom\": [5, 1, 3, 1, 5],\n",
" \"exp_triangle\": [3, 4, 2, 5, 1, 6],\n",
" \"asc_triangle\": [1, 5, 2, 5, 3, 6],\n",
" \"symm_triangle\": [1, 6, 2, 5, 3, 6],\n",
" \"pennant\": [6, 1, 5, 2, 4, 3, 6]\n",
"}\n",
"bearish_patterns = {\n",
" \"head_and_shoulders\": [1, 4, 2, 6, 2, 4, 1],\n",
" \"double_top\": [1, 5, 3, 5, 1],\n",
" \"desc_triangle\": [6, 2, 5, 2, 4, 1],\n",
" \"symm_triangle\": [6, 1, 5, 2, 4, 1],\n",
" \"pennant\": [1, 6, 2, 5, 3, 4, 1]\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "af76a114-d588-443a-8c62-19274c97c416",
"metadata": {},
"source": [
"Confirm the visual representation of a pattern by plotting its corresponding line graph."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0114e669-fff0-48b1-922b-412ad6941914",
"metadata": {},
"outputs": [],
"source": [
"pd.Series(bullish_patterns[\"double_bottom\"]).vbt.plot().show_svg()"
]
},
{
"cell_type": "markdown",
"id": "11172c01-2675-4c12-ab51-ae21137c097a",
"metadata": {},
"source": [
"Each generated sequence serves as a rough approximation of the desired chart pattern, and there's no need for precise adjustments: VectorBT PRO's similarity-based algorithm is flexible and can identify patterns, even if they are not perfectly consistent in their design."
]
},
{
"cell_type": "markdown",
"id": "4292665d-4168-436a-a59d-94b42bfd9482",
"metadata": {},
"source": [
"## Detect patterns in data"
]
},
{
"cell_type": "markdown",
"id": "0a355587-347a-4f4f-9f7a-fa041127f36a",
"metadata": {},
"source": [
"Iterate through each pattern, dataset, and timestamp within the dataset. Search for matches within windows spanning from 1 to 30 days, and create a record for each match that exceeds a pre-defined minimum similarity score, which is set by default to 85%."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d1a9af95-b7d1-4f29-9a6b-b40d57e5f597",
"metadata": {},
"outputs": [],
"source": [
"min_window = 24\n",
"max_window = 24 * 30\n",
"\n",
"def detect_patterns(patterns):\n",
" return vbt.PatternRanges.from_pattern_search(\n",
" price,\n",
" open=data.open, # OHLC for plotting\n",
" high=data.high,\n",
" low=data.low,\n",
" close=data.close,\n",
" pattern=patterns,\n",
" window=min_window,\n",
" max_window=max_window,\n",
" execute_kwargs=dict( # multithreading\n",
" engine=\"threadpool\", \n",
" chunk_len=\"auto\", \n",
" )\n",
" )\n",
"\n",
"bullish_matches = detect_patterns(vbt.Param(bullish_patterns, name=\"bullish_pattern\"))\n",
"bearish_matches = detect_patterns(vbt.Param(bearish_patterns, name=\"bearish_pattern\"))"
]
},
{
"cell_type": "markdown",
"id": "12733006-548c-4c28-a4ac-902aa066f0b3",
"metadata": {},
"source": [
"In just several minutes, VectorBT PRO seamlessly detected matches among all patterns. This process, involving around 230 million unique pattern and window combinations, was executed in parallel."
]
},
{
"cell_type": "markdown",
"id": "714ddd1f-f5a5-420e-9d4d-707e4b5e4685",
"metadata": {},
"source": [
"Get the number of matches for each pattern and dataset."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "77c5957e-a906-4c0c-998a-5b2e92fd652d",
"metadata": {},
"outputs": [],
"source": [
"print(bullish_matches.count())"
]
},
{
"cell_type": "markdown",
"id": "88b7627b-f48c-4d51-986e-cc269abf9604",
"metadata": {},
"source": [
"Plot the pattern and dataset with the most matches."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "708b0d74-8c5e-4921-87e2-4704050ce7ed",
"metadata": {},
"outputs": [],
"source": [
"vbt.settings.plotting.auto_rangebreaks = True # for stocks\n",
"\n",
"display_column = bullish_matches.count().idxmax()\n",
"\n",
"bullish_matches.plot(column=display_column, fit_ranges=True).show_svg()"
]
},
{
"cell_type": "markdown",
"id": "af6a60cf-0d98-49e5-ad48-cc872f6d2ce9",
"metadata": {},
"source": [
"Zoom in on a match."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f22c6c82-cc7c-4d5d-94b9-e14753e82072",
"metadata": {},
"outputs": [],
"source": [
"display_match = 3\n",
"\n",
"bullish_matches.plot(column=display_column, fit_ranges=display_match).show_svg()"
]
},
{
"cell_type": "markdown",
"id": "45f49c51-bd6f-4952-8ff4-76f6ebc00f7f",
"metadata": {},
"source": [
"The window data closely aligns with the pattern. This functionality is highly comprehensive, offering the flexibility to adjust fitness levels, modify rescaling and interpolation algorithms, and more to suit specific requirements."
]
},
{
"cell_type": "markdown",
"id": "16779944-3cae-44e8-a63d-36194479217c",
"metadata": {},
"source": [
"## Transform matches to signals"
]
},
{
"cell_type": "markdown",
"id": "26c9e03d-95ff-44a3-bd56-2a581673aa27",
"metadata": {},
"source": [
"To conduct backtesting on the identified patterns, we will convert them into signals, triggering a signal once a pattern has fully developed."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ce91720c-dd56-496e-aadb-faad71e1a529",
"metadata": {},
"outputs": [],
"source": [
"entries = bullish_matches.last_pd_mask\n",
"exits = bearish_matches.last_pd_mask"
]
},
{
"cell_type": "markdown",
"id": "d049c224-03b7-42fa-8927-51a502812e54",
"metadata": {},
"source": [
"Generate a Cartesian product of bullish and bearish patterns to systematically test each bullish pattern against each bearish pattern."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a5f0a9db-632d-4705-af3f-1c33dfb6f884",
"metadata": {},
"outputs": [],
"source": [
"entries, exits = entries.vbt.x(exits)"
]
},
{
"cell_type": "markdown",
"id": "90a043fe-c990-4358-94be-b8f4b92dec4f",
"metadata": {},
"source": [
"Both arrays have been converted into equally-shaped DataFrames, each comprising 125 columns. Each column represents an individual backtest, encompassing three parameters: bullish pattern, bearish pattern, and symbol."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8b3a1466-246b-42b5-a61d-a0ae1137c54d",
"metadata": {},
"outputs": [],
"source": [
"print(entries.columns)"
]
},
{
"cell_type": "markdown",
"id": "05669332-15a4-4ac5-b376-bdc08006d952",
"metadata": {},
"source": [
"## Backtest signals"
]
},
{
"cell_type": "markdown",
"id": "a44e90d0-f172-445a-9f4b-865444ae0cb3",
"metadata": {},
"source": [
"Establish a portfolio by simulating signals."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5d3ec70d-73e4-407d-8ea7-2a6b0f4436ba",
"metadata": {},
"outputs": [],
"source": [
"pf = vbt.Portfolio.from_signals(data, entries, exits)"
]
},
{
"cell_type": "markdown",
"id": "ff7d821d-f20e-45c3-83d5-ad1aa2ba109b",
"metadata": {},
"source": [
"Get the mean total return for every combination of bullish and bearish patterns."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ad8b789d-c543-4e42-8df7-1a351cceda5f",
"metadata": {},
"outputs": [],
"source": [
"mean_total_return = pf.total_return.groupby([\"bullish_pattern\", \"bearish_pattern\"]).mean()\n",
"\n",
"print(mean_total_return)"
]
},
{
"cell_type": "markdown",
"id": "946aa00a-b183-496e-8a63-7f11485ad3dc",
"metadata": {},
"source": [
"As visual beings, let's represent these values as a heatmap."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d74ac965-0461-4563-813e-56b9cce979c8",
"metadata": {},
"outputs": [],
"source": [
"mean_total_return.vbt.heatmap(x_level=\"bearish_pattern\", y_level=\"bullish_pattern\").show_svg()"
]
},
{
"cell_type": "markdown",
"id": "5d9f9706-d3da-480e-8b65-5eaa47196049",
"metadata": {},
"source": [
"Although the displayed performance of each pattern combination does not guarantee future results, it provides insight into how the market responded to pattern events in the past. For instance, it's noteworthy that the \"Bearish Symmetrical Triangle\" exhibited a notably bullish trend. Cross-validation and robustness testing are next essential steps for a comprehensive assessment."
]
},
{
"cell_type": "markdown",
"id": "2b5b8516-8620-41aa-a11c-96b48798c343",
"metadata": {},
"source": [
"Read more at https://vectorbt.pro/tutorials/patterns-and-projections/"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9efd6597-880f-4769-a486-65e17b1c5475",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -0,0 +1,266 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "9d9b5c91-f3a3-4709-a36f-40ecc86595d6",
"metadata": {},
"source": [
"# Forecasting future price trends by projecting historical price patterns"
]
},
{
"cell_type": "markdown",
"id": "2cffb873-e431-44f3-b243-e35969bbd2c1",
"metadata": {},
"source": [
"In our previous newsletter focusing on VectorBT PRO (VBT), we dived into the pattern detection capabilities of this powerful library. An additional key functionality is VBT's capacity to extrapolate identified price segments into the future and aggregate them for statistical analysis. This feature can be an invaluable tool for real-time decision-making in market analysis."
]
},
{
"cell_type": "markdown",
"id": "c472968b-1863-4d79-a299-ec67c1757455",
"metadata": {},
"source": [
"## Imports and set up"
]
},
{
"cell_type": "markdown",
"id": "ddf68612-622b-4803-87fc-a1ad80341536",
"metadata": {},
"source": [
"Given the self-contained design of VBT, a single import suffices."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a42ccb91-bc73-4ad5-9327-18c7c22af598",
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro import *\n",
"# whats_imported()\n",
"\n",
"vbt.settings.set_theme(\"dark\")"
]
},
{
"cell_type": "markdown",
"id": "15412fda-c27f-4820-9273-17366164b2b3",
"metadata": {},
"source": [
"Let's define a set of variables for our analysis."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fc016fe0-5ae6-416f-bb4d-84a33a91fce8",
"metadata": {},
"outputs": [],
"source": [
"SYMBOL = \"BTCUSDT\"\n",
"TIMEFRAME = \"1 hour\"\n",
"START = \"one year ago\"\n",
"\n",
"LAST_N_BARS = 24\n",
"PRED_N_BARS = 12\n",
"\n",
"GIF_FNAME = \"projections.gif\"\n",
"GIF_N_BARS = 72\n",
"GIF_FPS = 4\n",
"GIF_PAD = 0.01"
]
},
{
"cell_type": "markdown",
"id": "e4667d70-f1d9-4f34-81ff-fdf8320477ae",
"metadata": {},
"source": [
"We will execute the analysis using price data retrieved from BinanceData, based on the parameters we previously defined."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b797e0ff-320d-456b-91df-1e0e369d83a9",
"metadata": {},
"outputs": [],
"source": [
"data = vbt.BinanceData.pull(SYMBOL, timeframe=TIMEFRAME, start=START)"
]
},
{
"cell_type": "markdown",
"id": "43fade8d-2d1f-492b-88bb-95facd21ceda",
"metadata": {},
"source": [
"## Find and plot projections"
]
},
{
"cell_type": "markdown",
"id": "0013fab2-d1fa-4777-99e9-2081a90444e3",
"metadata": {},
"source": [
"Let's write a function that analyzes the most recent price trend and employs it as a pattern to identify similar price movements in historical data. This pattern recognition function will focus exclusively on segments of price history having a comparable percentage change from their respective starting points."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f7f4ead3-c4db-47d5-8a30-3f7dbe4347dc",
"metadata": {},
"outputs": [],
"source": [
"def find_patterns(data):\n",
" price = data.hlc3\n",
" pattern = price.values[-LAST_N_BARS:]\n",
" pattern_ranges = price.vbt.find_pattern(\n",
" pattern=pattern,\n",
" rescale_mode=\"rebase\",\n",
" overlap_mode=\"allow\",\n",
" wrapper_kwargs=dict(freq=TIMEFRAME)\n",
" )\n",
" pattern_ranges = pattern_ranges.status_closed\n",
" return pattern_ranges\n",
"\n",
"pattern_ranges = find_patterns(data)\n",
"print(pattern_ranges.count())"
]
},
{
"cell_type": "markdown",
"id": "6dc1f00c-f0a2-4b74-831f-3043c14f1195",
"metadata": {},
"source": [
"We have identified a number of price segments that closely resemble the latest price trend. Now, we'll write a function that extracts the price data immediately succeeding each identified segment and plots these as extensions of the price trend. These subsequent segments are known as \"projections.\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9fb7b02c-190a-488e-bfa6-843db23c324e",
"metadata": {},
"outputs": [],
"source": [
"def plot_projections(data, pattern_ranges, **kwargs):\n",
" projection_ranges = pattern_ranges.with_delta(\n",
" PRED_N_BARS,\n",
" open=data.open,\n",
" high=data.high,\n",
" low=data.low,\n",
" close=data.close,\n",
" )\n",
" projection_ranges = projection_ranges.status_closed\n",
" return projection_ranges.plot_projections(\n",
" plot_past_period=LAST_N_BARS, \n",
" **kwargs,\n",
" )\n",
"\n",
"plot_projections(data, pattern_ranges, plot_bands=False).show_svg()"
]
},
{
"cell_type": "markdown",
"id": "8df73436-c6ae-411b-8c44-e5764f9c1812",
"metadata": {},
"source": [
"As we can see, similar price movements have historically branched into a diverse set of trajectories. For a visually compelling and statistically robust forecast, we will display the confidence bands encompassing all the projections, with 60% of these projections falling between the upper and lower bands."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b97458a5-7428-4877-80c6-a522aef4b5ce",
"metadata": {},
"outputs": [],
"source": [
"plot_projections(data, pattern_ranges, plot_bands=True).show_svg()"
]
},
{
"cell_type": "markdown",
"id": "9011e2c5-1745-480c-b9da-c031f6ba9ae2",
"metadata": {},
"source": [
"## Generate animation"
]
},
{
"cell_type": "markdown",
"id": "ac05a0ea-6883-4736-a815-619f76607966",
"metadata": {},
"source": [
"Lastly, we will compile a GIF animation that iterates through a specified range of bars, applying the aforementioned procedure to each bar within that range."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6238530e-9d06-4da4-a71d-3ae7489c2c9a",
"metadata": {},
"outputs": [],
"source": [
"def plot_frame(frame_index, **kwargs):\n",
" sub_data = data.loc[:frame_index[-1]]\n",
" pattern_ranges = find_patterns(sub_data)\n",
" if pattern_ranges.count() < 3:\n",
" return None\n",
" return plot_projections(sub_data, pattern_ranges, **kwargs)\n",
"\n",
"vbt.save_animation(\n",
" GIF_FNAME,\n",
" data.index[-GIF_N_BARS:],\n",
" plot_frame,\n",
" plot_projections=False,\n",
" delta=1,\n",
" fps=GIF_FPS,\n",
" writer_kwargs=dict(loop=0),\n",
" yaxis_range=[\n",
" data.low.iloc[-GIF_N_BARS:].min() * (1 - GIF_PAD), \n",
" data.high.iloc[-GIF_N_BARS:].max() * (1 + GIF_PAD)\n",
" ],\n",
")"
]
},
{
"cell_type": "markdown",
"id": "91b825fb-7e4c-4d48-ae73-bffe633a6f52",
"metadata": {},
"source": [
"Bear in mind that while the confidence bands describe past performance, they should not be used as guarantees of future results."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "319a24bb-e210-4d02-ab2c-0ce58b3dc82c",
"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
}

View File

@ -0,0 +1,700 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "be764faf-65cc-408a-8ebd-96b8b4f14b60",
"metadata": {},
"source": [
"# Basic RSI strategy"
]
},
{
"cell_type": "markdown",
"id": "d15aa106-cb66-4fc8-b07d-347c078c634a",
"metadata": {
"tags": []
},
"source": [
"## Single backtest"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dee14aee-a14c-4e54-bf82-dbe9f64cbd62",
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro import *\n",
"# whats_imported()\n",
"\n",
"vbt.settings.set_theme('dark')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "32105542-882d-4403-a86c-fdcd8002e258",
"metadata": {},
"outputs": [],
"source": [
"data = vbt.BinanceData.pull('BTCUSDT')\n",
"data"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "843dc221-4353-47b7-9f3a-68b6b8ba8752",
"metadata": {},
"outputs": [],
"source": [
"data.data['BTCUSDT'].vbt.ohlcv.plot().show_svg()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f14f65d8-a8b8-4cb0-a135-258b01b5ff83",
"metadata": {},
"outputs": [],
"source": [
"data.data['BTCUSDT'].info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b3ef3cc2-c70f-4469-8067-fe5e1c6a06f3",
"metadata": {},
"outputs": [],
"source": [
"open_price = data.get('Open')\n",
"close_price = data.get('Close')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4de98bf9-7f0e-48d8-b532-a917500d1f18",
"metadata": {},
"outputs": [],
"source": [
"vbt.IF.list_indicators(\"RSI*\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "af3df97f-b81e-4186-bf8e-adb50b29c5c0",
"metadata": {},
"outputs": [],
"source": [
"vbt.indicator(\"talib:RSI\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3ce59b4c-7e47-4484-be62-0e26a2dc177e",
"metadata": {},
"outputs": [],
"source": [
"vbt.RSI"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "70589f4f-fa0f-448f-95f6-77c4833beede",
"metadata": {},
"outputs": [],
"source": [
"vbt.talib('RSI')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6ba90109-ca96-49c5-8ea9-2ad90755b43a",
"metadata": {},
"outputs": [],
"source": [
"vbt.ta('RSIIndicator')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cd56e11e-bf9e-4304-842f-6df7b2b0b738",
"metadata": {},
"outputs": [],
"source": [
"vbt.pandas_ta('RSI')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "37d1140d-5ef0-4d22-9100-2b29e0e9f2ed",
"metadata": {},
"outputs": [],
"source": [
"print(vbt.format_func(vbt.RSI.run))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bee38f74-6c29-4cc9-a37e-25fe44c08bc8",
"metadata": {},
"outputs": [],
"source": [
"rsi = vbt.RSI.run(open_price)\n",
"rsi"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "943a0d3c-c8f3-4359-9387-744e5bb3da44",
"metadata": {},
"outputs": [],
"source": [
"rsi.rsi"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "69636dd4-8592-48b8-b4cb-fabf7cbd7b15",
"metadata": {},
"outputs": [],
"source": [
"entries = rsi.rsi.vbt.crossed_below(30)\n",
"entries"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b073280b-69ea-43ba-8694-4c2b506be57e",
"metadata": {},
"outputs": [],
"source": [
"exits = rsi.rsi.vbt.crossed_above(70)\n",
"exits"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "93ea75b8-5b75-4767-8adf-b535c9588147",
"metadata": {},
"outputs": [],
"source": [
"entries = rsi.rsi_crossed_below(30)\n",
"exits = rsi.rsi_crossed_above(70)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c127d5f5-eee4-4d6a-ae26-7592f5d3ab65",
"metadata": {},
"outputs": [],
"source": [
"def plot_rsi(rsi, entries, exits):\n",
" fig = rsi.plot()\n",
" entries.vbt.signals.plot_as_entries(rsi.rsi, fig=fig)\n",
" exits.vbt.signals.plot_as_exits(rsi.rsi, fig=fig)\n",
" return fig"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6743e20c-f2e6-4b64-9934-022fcc10acb7",
"metadata": {},
"outputs": [],
"source": [
"plot_rsi(rsi, entries, exits).show_svg()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3e1ef2e8-e225-4d60-891a-6c3e9cc2821b",
"metadata": {},
"outputs": [],
"source": [
"clean_entries, clean_exits = entries.vbt.signals.clean(exits)\n",
"\n",
"plot_rsi(rsi, clean_entries, clean_exits).show_svg()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1d2d4028-034b-4211-9a24-b82944b264da",
"metadata": {},
"outputs": [],
"source": [
"clean_entries.vbt.signals.total()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aeb5bbf3-1217-4c2f-a53a-3d426ac069c0",
"metadata": {},
"outputs": [],
"source": [
"clean_exits.vbt.signals.total()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f64bd28c-6dc9-476d-9bb1-a2e137a39dfe",
"metadata": {},
"outputs": [],
"source": [
"ranges = clean_entries.vbt.signals.between_ranges(target=clean_exits)\n",
"ranges.duration.mean(wrap_kwargs=dict(to_timedelta=True))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8f55e1c9-9960-43ea-b148-41a6131caa7b",
"metadata": {},
"outputs": [],
"source": [
"pf = vbt.Portfolio.from_signals(\n",
" close=close_price, \n",
" entries=clean_entries, \n",
" exits=clean_exits,\n",
" size=100,\n",
" size_type='value',\n",
" init_cash='auto'\n",
")\n",
"pf"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "02c8de50-5101-4a23-9d58-9c06c1ae4460",
"metadata": {},
"outputs": [],
"source": [
"pf.stats()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a6377f72-4174-49a1-8a9c-ad8095bc4437",
"metadata": {},
"outputs": [],
"source": [
"pf.plot(settings=dict(bm_returns=False)).show_svg()"
]
},
{
"cell_type": "markdown",
"id": "c1f78a0a-7bc9-4b32-9f3c-92d0da44d87b",
"metadata": {},
"source": [
"## Multiple backtests"
]
},
{
"cell_type": "markdown",
"id": "909f506d-8edb-4fa8-8425-5e9c4b6e7b1b",
"metadata": {},
"source": [
"### Using for-loop"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a781ea63-d4df-41c5-820e-195afd3770c9",
"metadata": {},
"outputs": [],
"source": [
"def test_rsi(window=14, wtype=\"wilder\", lower_th=30, upper_th=70):\n",
" rsi = vbt.RSI.run(open_price, window=window, wtype=wtype)\n",
" entries = rsi.rsi_crossed_below(lower_th)\n",
" exits = rsi.rsi_crossed_above(upper_th)\n",
" pf = vbt.Portfolio.from_signals(\n",
" close=close_price, \n",
" entries=entries, \n",
" exits=exits,\n",
" size=100,\n",
" size_type='value',\n",
" init_cash='auto')\n",
" return pf.stats([\n",
" 'total_return', \n",
" 'total_trades', \n",
" 'win_rate', \n",
" 'expectancy'\n",
" ])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d60e7721-a0bb-4f2d-af92-d074ee523582",
"metadata": {},
"outputs": [],
"source": [
"test_rsi()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "14e9e01d-9bcf-4e2c-8959-f5c66e1768c7",
"metadata": {},
"outputs": [],
"source": [
"test_rsi(lower_th=20, upper_th=80)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a7864dbb-5a24-456a-bda2-468e737fa8ea",
"metadata": {},
"outputs": [],
"source": [
"from itertools import product\n",
"\n",
"lower_ths = range(20, 31)\n",
"upper_ths = range(70, 81)\n",
"th_combs = list(product(lower_ths, upper_ths))\n",
"len(th_combs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fbc7c48e-e5e3-4ec7-8f76-7169d58231de",
"metadata": {},
"outputs": [],
"source": [
"comb_stats = [\n",
" test_rsi(lower_th=lower_th, upper_th=upper_th)\n",
" for lower_th, upper_th in th_combs\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8fbae677-56f9-49bc-88f7-325ff8ee00c4",
"metadata": {},
"outputs": [],
"source": [
"comb_stats_df = pd.DataFrame(comb_stats)\n",
"print(comb_stats_df)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a99e9ded-2954-40c8-93c5-ebff4a174036",
"metadata": {},
"outputs": [],
"source": [
"comb_stats_df.index = pd.MultiIndex.from_tuples(\n",
" th_combs, \n",
" names=['lower_th', 'upper_th'])\n",
"print(comb_stats_df)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "69511f79-00cd-40e2-b64f-097137bba69e",
"metadata": {},
"outputs": [],
"source": [
"comb_stats_df['Expectancy'].vbt.heatmap().show_svg()"
]
},
{
"cell_type": "markdown",
"id": "037bfdd0-e3dc-4ac5-9c0e-91c3320fcafd",
"metadata": {},
"source": [
"### Using columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ac5bc6ba-c6e1-4ca8-98fd-19b141a4220a",
"metadata": {},
"outputs": [],
"source": [
"windows = list(range(8, 21))\n",
"wtypes = [\"simple\", \"exp\", \"wilder\"]\n",
"lower_ths = list(range(20, 31))\n",
"upper_ths = list(range(70, 81))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fb6145b0-6ee7-4ac2-a8c3-5bd7127032be",
"metadata": {},
"outputs": [],
"source": [
"rsi = vbt.RSI.run(\n",
" open_price, \n",
" window=windows, \n",
" wtype=wtypes, \n",
" param_product=True)\n",
"rsi.rsi.columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f374086a-bf84-425c-a857-0ce4ea8d6a28",
"metadata": {},
"outputs": [],
"source": [
"lower_ths_prod, upper_ths_prod = zip(*product(lower_ths, upper_ths))\n",
"len(lower_ths_prod)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "069d9ffe-cdda-45af-832d-62bfb47ede42",
"metadata": {},
"outputs": [],
"source": [
"len(upper_ths_prod)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bd96a283-b9bd-4824-9218-86efe06b8cc6",
"metadata": {},
"outputs": [],
"source": [
"lower_th_index = vbt.Param(lower_ths_prod, name='lower_th')\n",
"entries = rsi.rsi_crossed_below(lower_th_index)\n",
"entries.columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c8510247-f1e1-4af7-8b14-0fb6d6f5813b",
"metadata": {},
"outputs": [],
"source": [
"upper_th_index = vbt.Param(upper_ths_prod, name='upper_th')\n",
"exits = rsi.rsi_crossed_above(upper_th_index)\n",
"exits.columns"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "209be8ac-393d-4a43-b752-022f3cfd8698",
"metadata": {},
"outputs": [],
"source": [
"pf = vbt.Portfolio.from_signals(\n",
" close=close_price, \n",
" entries=entries, \n",
" exits=exits,\n",
" size=100,\n",
" size_type='value',\n",
" init_cash='auto'\n",
")\n",
"pf"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "15143c2b-b795-493c-9e8f-5aa0d877d391",
"metadata": {},
"outputs": [],
"source": [
"stats_df = pf.stats([\n",
" 'total_return', \n",
" 'total_trades', \n",
" 'win_rate', \n",
" 'expectancy'\n",
"], agg_func=None)\n",
"print(stats_df)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3d65043d-2081-4179-bc94-d42d1465a898",
"metadata": {},
"outputs": [],
"source": [
">>> print(pf.getsize())"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "862f2e98-dea6-4c7e-b5e9-21c16ca27de2",
"metadata": {},
"outputs": [],
"source": [
">>> np.product(pf.wrapper.shape) * 8 / 1024 / 1024"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "abea668f-5f95-44d3-a158-38440f70433d",
"metadata": {},
"outputs": [],
"source": [
"stats_df['Expectancy'].groupby('rsi_window').mean()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "df815592-9f63-496e-8d88-99b1462d1030",
"metadata": {},
"outputs": [],
"source": [
"print(stats_df.sort_values(by='Expectancy', ascending=False).head())"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1cb39bea-298c-4202-a996-c84b157f1ab9",
"metadata": {},
"outputs": [],
"source": [
"pf[(22, 80, 20, \"wilder\")].plot_value().show_svg()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bced4fa8-f7eb-49e4-980b-f0f4acd20540",
"metadata": {},
"outputs": [],
"source": [
"data = vbt.BinanceData.pull(['BTCUSDT', 'ETHUSDT'])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "55e86189-8244-4955-b736-f4f4f840b80a",
"metadata": {},
"outputs": [],
"source": [
"open_price = data.get('Open')\n",
"close_price = data.get('Close')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bd7aefae-3f83-48ac-8f20-7d0a4760cd4b",
"metadata": {},
"outputs": [],
"source": [
"rsi = vbt.RSI.run(\n",
" open_price, \n",
" window=windows, \n",
" wtype=wtypes, \n",
" param_product=True)\n",
"entries = rsi.rsi_crossed_below(lower_th_index)\n",
"exits = rsi.rsi_crossed_above(upper_th_index)\n",
"pf = vbt.Portfolio.from_signals(\n",
" close=close_price, \n",
" entries=entries, \n",
" exits=exits,\n",
" size=100,\n",
" size_type='value',\n",
" init_cash='auto'\n",
")\n",
"stats_df = pf.stats([\n",
" 'total_return', \n",
" 'total_trades', \n",
" 'win_rate', \n",
" 'expectancy'\n",
"], agg_func=None)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a7007b6a-bf08-40a6-a4be-091fec376290",
"metadata": {},
"outputs": [],
"source": [
"stats_df.index"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a67661e7-d8df-4efe-8479-93d194ca255d",
"metadata": {},
"outputs": [],
"source": [
"eth_mask = stats_df.index.get_level_values('symbol') == 'ETHUSDT'\n",
"btc_mask = stats_df.index.get_level_values('symbol') == 'BTCUSDT'\n",
"pd.DataFrame({\n",
" 'ETHUSDT': stats_df[eth_mask]['Expectancy'].values,\n",
" 'BTCUSDT': stats_df[btc_mask]['Expectancy'].values\n",
"}).vbt.histplot(xaxis=dict(title=\"Expectancy\")).show_svg()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5a64e903-1256-4e4b-9d22-667f2706c9da",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,372 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "2000b27d-9007-44ad-af8a-8a070d3d0e72",
"metadata": {},
"source": [
"# How to backtest a multi-timeframe strategy"
]
},
{
"cell_type": "markdown",
"id": "44334242-c4cd-4656-987b-7e407ab99c76",
"metadata": {},
"source": [
"Multi-timeframe (MTF) analysis is an essential trading approach that involves analyzing an asset's price in different timeframes."
]
},
{
"cell_type": "markdown",
"id": "208ebc3d-f05a-4f28-95cb-3a26c89583cd",
"metadata": {},
"source": [
"Despite its popularity, MTF analysis comes with several pitfalls when working with arrays, including look-ahead bias and information loss."
]
},
{
"cell_type": "markdown",
"id": "0a915599-672d-4609-b800-c3b32c38bcdd",
"metadata": {},
"source": [
"Many native pandas implementations mistakenly assume that events, such as indicator calculations, take place at the same timestamp as the data provided by the exchange, which is typically the opening time of a bar."
]
},
{
"cell_type": "markdown",
"id": "b0a48b90-2dbe-4f1a-8924-6cddb8ba9e8c",
"metadata": {},
"source": [
"VBT operates under the assumption that the exact timing of most events is unknown and occurs at some point between the opening (best-case) and closing (worst-case) times of a bar. Consequently, VBT employs a set of features designed to resample data in the most sensitive way, without looking into the future."
]
},
{
"cell_type": "markdown",
"id": "e964afe5-96c3-4048-96b8-81ed67bdd138",
"metadata": {},
"source": [
"In today's newsletter, we'll use VectorBT PRO to backtest trading on multiple timeframes simultaneously."
]
},
{
"cell_type": "markdown",
"id": "f759738c-4e8e-471b-b668-65962de10b2f",
"metadata": {},
"source": [
"## Imports and set up"
]
},
{
"cell_type": "markdown",
"id": "4b057b37-ca17-4430-bec6-58181442610c",
"metadata": {},
"source": [
"In the newer versions of VBT PRO, the star-import (*) loads all the relevant stuff for us, such as `np` for NumPy."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "369e2d5c-1daa-4c54-ac86-ab91455b6f75",
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro import *"
]
},
{
"cell_type": "markdown",
"id": "efd90f21-1fa5-4c34-b903-73ec2fb5e163",
"metadata": {},
"source": [
"Configure our graphs to be dark and gap-free."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "765bee33-e30a-4dde-ae59-40c0ab5b5b3d",
"metadata": {},
"outputs": [],
"source": [
"vbt.settings.set_theme(\"dark\")\n",
"vbt.settings.plotting.auto_rangebreaks = True"
]
},
{
"cell_type": "markdown",
"id": "dacf0e6a-061c-4c45-9a2d-edb919cce63e",
"metadata": {},
"source": [
"Grab the data of a higher frequency for your favorite asset. We'll use hourly TSLA."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "16bf34cc-5386-44fa-9af1-7a8969d3f4b9",
"metadata": {},
"outputs": [],
"source": [
"data = vbt.YFData.pull(\"TSLA\", start=\"2023\", end=\"2024\", timeframe=\"hourly\")"
]
},
{
"cell_type": "markdown",
"id": "4681de1a-5101-4df7-90cf-ceffa53a6112",
"metadata": {},
"source": [
"## Multi-timeframe indicators"
]
},
{
"cell_type": "markdown",
"id": "8c7c88cb-43dd-459f-ac82-5a5614e26dd2",
"metadata": {},
"source": [
"Instruct VBT to calculate the fast and slow SMA indicators across multiple timeframes."
]
},
{
"cell_type": "markdown",
"id": "ad633ac3-6820-4d64-ba6f-8a480f3eaeb8",
"metadata": {},
"source": [
"Under the hood, data is first resampled to the target timeframe; then, the actual TA-Lib indicator is applied exclusively to non-missing values. Finally, the result is realigned back to the original timeframe in a manner that eliminates the possibility of look-ahead bias."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e6780b81-129f-40b3-8708-679c0fc91f29",
"metadata": {},
"outputs": [],
"source": [
"fast_sma = data.run(\n",
" \"talib:sma\", \n",
" timeframe=[\"1h\", \"4h\", \"1d\"], \n",
" timeperiod=vbt.Default(20),\n",
" skipna=True\n",
")\n",
"slow_sma = data.run(\n",
" \"talib:sma\", \n",
" timeframe=[\"1h\", \"4h\", \"1d\"], \n",
" timeperiod=vbt.Default(50),\n",
" skipna=True\n",
")"
]
},
{
"cell_type": "markdown",
"id": "d6ad5284-3fc3-4340-b925-28ac252bde69",
"metadata": {},
"source": [
"The result of each call is a DataFrame with three columns, one for each timeframe."
]
},
{
"cell_type": "markdown",
"id": "f855ae03-5539-4d4d-8bd3-fe2709a33258",
"metadata": {},
"source": [
"If we plot the DataFrame, we'll observe that the line corresponding to the highest frequency is smooth, whereas the line representing the lowest frequency appears stepped since the indicator values are updated less frequently."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ec9149ca-2c71-4b4a-97e5-6970cd3b0f44",
"metadata": {},
"outputs": [],
"source": [
"fast_sma.real.vbt.plot().show_svg()"
]
},
{
"cell_type": "markdown",
"id": "4ac4ea89-6cc9-48e6-91a6-522fd4922279",
"metadata": {},
"source": [
"## Unified portfolio"
]
},
{
"cell_type": "markdown",
"id": "015840e4-cd91-4fb3-97cc-389d39cf9e9d",
"metadata": {},
"source": [
"Next, we'll set up a portfolio in which we go long whenever the fast SMA crosses above the slow SMA and go short when the opposite occurs, across each timeframe."
]
},
{
"cell_type": "markdown",
"id": "6d998b85-66d7-4149-8160-ee5fff38dc6f",
"metadata": {},
"source": [
"However, since hourly signals occur more frequently than daily signals, we'll allocate less capital to more frequent signals. For instance, we'll allocate 5% of the equity to hourly signals, 10% to 4-hour signals, and 20% to daily signals."
]
},
{
"cell_type": "markdown",
"id": "9aa20269-769d-48ec-ba70-da1c73b508bb",
"metadata": {},
"source": [
"We'll begin with a cash balance of $10,000, shared across all timeframes. Additionally, we'll implement a 20% trailing stop loss (TSL)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8a8eb991-51de-46de-8650-06e1d1c141d2",
"metadata": {},
"outputs": [],
"source": [
"pf = vbt.PF.from_signals(\n",
" data, \n",
" long_entries=fast_sma.real_crossed_above(slow_sma), \n",
" short_entries=fast_sma.real_crossed_below(slow_sma), \n",
" size=[[0.05, 0.1, 0.2]],\n",
" size_type=\"valuepercent\",\n",
" init_cash=10_000,\n",
" group_by=[\"pf\", \"pf\", \"pf\"],\n",
" cash_sharing=True,\n",
" tsl_stop=0.2\n",
")"
]
},
{
"cell_type": "markdown",
"id": "d613367f-2eab-4b8e-8fb0-3ae9b4c278e6",
"metadata": {},
"source": [
"Plot the cumulative return for each timeframe and compare these to the cumulative return of the entire portfolio."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d39b31f1-3f88-458b-ac20-88ed7c383bdb",
"metadata": {},
"outputs": [],
"source": [
"fig = pf.get_cumulative_returns().vbt.plot(trace_kwargs=dict(line_color=\"gray\", line_dash=\"dot\"))\n",
"fig = pf.get_cumulative_returns(group_by=False).vbt.plot(fig=fig)\n",
"fig.show_svg()"
]
},
{
"cell_type": "markdown",
"id": "9ab33146-d1ac-48ad-bd0b-948c67342dd6",
"metadata": {},
"source": [
"To delve deeper into one of the timeframes, we can plot the indicators alongside the executed trade signals."
]
},
{
"cell_type": "markdown",
"id": "5c0f7a7c-8a40-4528-869a-a2ffdfd60735",
"metadata": {},
"source": [
"Here, we can observe that the majority of positions on the daily timeframe were closed out by the TSL."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c0dbd82e-1b70-4373-904f-a023d9983782",
"metadata": {},
"outputs": [],
"source": [
"fig = fast_sma.real.vbt.plot(column=\"1d\", trace_kwargs=dict(name=\"Fast\", line_color=\"limegreen\"))\n",
"fig = slow_sma.real.vbt.plot(column=\"1d\", trace_kwargs=dict(name=\"Slow\", line_color=\"orangered\"), fig=fig)\n",
"fig = pf.plot_trade_signals(column=\"1d\", fig=fig)\n",
"fig.show_svg()"
]
},
{
"cell_type": "markdown",
"id": "9f1fd30d-a43a-4435-a64c-35eb98c40b6c",
"metadata": {},
"source": [
"## Timeframe product"
]
},
{
"cell_type": "markdown",
"id": "989f08bb-14b2-4630-84a7-126920c69918",
"metadata": {},
"source": [
"Since our MTF indicators share the same index, we can combine one timeframe with another. For instance, we can generate signals from the crossover of two timeframes and identify the pair of timeframes that yield the highest expectancy."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "05d70299-4215-49bd-82ec-d52dbc29f678",
"metadata": {},
"outputs": [],
"source": [
"fast_sma_real = fast_sma.real.vbt.rename_levels({\"sma_timeframe\": \"fast_sma_timeframe\"})\n",
"slow_sma_real = slow_sma.real.vbt.rename_levels({\"sma_timeframe\": \"slow_sma_timeframe\"})\n",
"fast_sma_real, slow_sma_real = fast_sma_real.vbt.x(slow_sma_real)\n",
"long_entries = fast_sma_real.vbt.crossed_above(slow_sma_real)\n",
"short_entries = fast_sma_real.vbt.crossed_below(slow_sma_real)\n",
"pf = vbt.PF.from_signals(data, long_entries=long_entries, short_entries=short_entries)\n",
"pf.trades.expectancy.sort_values(ascending=False)"
]
},
{
"cell_type": "markdown",
"id": "2e573717-8f4c-402b-aef6-cab06f9df044",
"metadata": {},
"source": [
"## Next steps"
]
},
{
"cell_type": "markdown",
"id": "9b042ef0-9041-4722-bde7-bea35c9ca2ec",
"metadata": {},
"source": [
"Timeframe is yet another parameter of your strategy that can be tweaked. For example, you can go to uncharted territory and test more unconventional timeframes like \"1h 30min\" to discover potentially novel insights. Similar to other parameters, timeframes should also undergo cross-validation."
]
},
{
"cell_type": "markdown",
"id": "9233c24e-59f5-4947-b1d5-5c4b5c409ebe",
"metadata": {},
"source": [
"However, unlike regular parameters, timeframes should be regarded as a distinct dimension that provides a unique perspective on your strategy."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9395716f-2f53-4e0a-a48b-205bdbe6c1fd",
"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
}

View File

@ -0,0 +1,316 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "b9d33f66-67f4-483f-9613-87b3298a8fc1",
"metadata": {},
"source": [
"# How to cross-validate a parameterized trading strategy"
]
},
{
"cell_type": "markdown",
"id": "23954d08-d7cf-43ec-84b2-cd6dbbfc5c15",
"metadata": {},
"source": [
"Trading strategies often rely on parameters. Enhancing and effectively cross-validating these parameters can provide a competitive advantage in the market. However, creating a reliable cross-validation schema is challenging due to risks like look-ahead bias and other pitfalls that can lead to overestimating a strategy's performance. With [VectorBT PRO](https://vectorbt.pro/), you can easily access and implement a variety of sophisticated cross-validation methods with just a few lines of code."
]
},
{
"cell_type": "markdown",
"id": "d522552e-dd90-46de-a579-6df68154f91b",
"metadata": {},
"source": [
"## Imports and data"
]
},
{
"cell_type": "markdown",
"id": "1d482486-ce8d-4474-8623-7a08dfba157c",
"metadata": {},
"source": [
"Let's import VBT PRO and the few libraries relevant for our analysis."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "039c1b36-bb5a-4272-8887-d2ff7770184e",
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro import *\n",
"# whats_imported()\n",
"\n",
"vbt.settings.set_theme(\"dark\")"
]
},
{
"cell_type": "markdown",
"id": "f0b6912a-09c7-4c72-b381-c35135ca627f",
"metadata": {},
"source": [
"The first step involves acquiring data."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1f432668-8698-4c21-a9b4-d8c4c40972e9",
"metadata": {},
"outputs": [],
"source": [
"SYMBOL = \"AAPL\"\n",
"START = \"2010\"\n",
"END = \"now\"\n",
"TIMEFRAME = \"day\"\n",
"\n",
"data = vbt.YFData.pull(SYMBOL, start=START, end=END, timeframe=TIMEFRAME)"
]
},
{
"cell_type": "markdown",
"id": "9058618c-6395-4bbc-8e7a-b0cdc6394a85",
"metadata": {},
"source": [
"## Cross-validation schema"
]
},
{
"cell_type": "markdown",
"id": "ea50af21-ff28-4d40-870b-7fcdf6e155e1",
"metadata": {},
"source": [
"Next, we'll set up a \"splitter,\" which divides a date range into smaller segments according to a chosen schema. For instance, let's allocate 12 months for training data and another 12 months for testing data, with this cycle repeating every 3 months."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cc405ebe-ae7d-4687-8470-7d1dbf91aad6",
"metadata": {},
"outputs": [],
"source": [
"TRAIN = 12\n",
"TEST = 12\n",
"EVERY = 3\n",
"OFFSET = vbt.offset(\"M\")\n",
"\n",
"splitter = vbt.Splitter.from_ranges(\n",
" data.index, \n",
" every=EVERY * OFFSET, \n",
" lookback_period=(TRAIN + TEST) * OFFSET,\n",
" split=(\n",
" vbt.RepFunc(lambda index: index < index[0] + TRAIN * OFFSET),\n",
" vbt.RepFunc(lambda index: index >= index[0] + TRAIN * OFFSET),\n",
" ),\n",
" set_labels=[\"train\", \"test\"]\n",
")\n",
"splitter.plots().show_svg()"
]
},
{
"cell_type": "markdown",
"id": "7a749de2-6f58-4246-b308-ad64c6b03f90",
"metadata": {},
"source": [
"In the first subplot, we see that each split (or row) contains adjacent training and testing sets, progressively rolling from past to present. The second subplot illustrates the overlap of each data point across different ranges. Tip: For non-overlapping testing sets, use the setting `EVERY = TRAIN`."
]
},
{
"cell_type": "markdown",
"id": "ab6ed776-f682-43c8-a6d7-489101f82c70",
"metadata": {},
"source": [
"## Objective function"
]
},
{
"cell_type": "markdown",
"id": "9ea17ccf-77e0-4d6c-af87-4c2cdb788d4f",
"metadata": {},
"source": [
"Next, we'll create a function to execute a trading strategy within a specified date range using a single parameter set, returning one key metric. Our strategy will be a simple EMA crossover combined with an ATR trailing stop."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b6a0ac65-5336-4600-870d-dd9202ef78ec",
"metadata": {},
"outputs": [],
"source": [
"def objective(data, fast_period=10, slow_period=20, atr_period=14, atr_mult=3):\n",
" fast_ema = data.run(\"talib:ema\", fast_period, short_name=\"fast_ema\", unpack=True)\n",
" slow_ema = data.run(\"talib:ema\", slow_period, short_name=\"slow_ema\", unpack=True)\n",
" atr = data.run(\"talib:atr\", atr_period, unpack=True)\n",
" pf = vbt.PF.from_signals(\n",
" data, \n",
" entries=fast_ema.vbt.crossed_above(slow_ema), \n",
" exits=fast_ema.vbt.crossed_below(slow_ema), \n",
" tsl_stop=atr * atr_mult, \n",
" save_returns=True,\n",
" freq=TIMEFRAME\n",
" )\n",
" return pf.sharpe_ratio\n",
"\n",
"print(objective(data))"
]
},
{
"cell_type": "markdown",
"id": "dc93d52c-a4e5-4122-a8e6-51ef5d52adbb",
"metadata": {},
"source": [
"## Parameter optimization"
]
},
{
"cell_type": "markdown",
"id": "41b0f21c-e99e-416a-b2da-dcdcbf7e7fed",
"metadata": {},
"source": [
"Let's harness the power of VBT PRO! By decorating (or wrapping) our function with `parameterized`, we enable `objective` to accept a list of parameters and execute them across all combinations. We'll then further enhance the function with another decorator, `split`, which runs the strategy on each date range specified by the splitter. This approach allows us to apply our strategy across every possible date range and parameter combination, compiling the outcomes into a single Pandas Series."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e150f68a-9ce3-432f-81ee-566acdfdd2dc",
"metadata": {},
"outputs": [],
"source": [
"param_objective = vbt.parameterized(\n",
" objective,\n",
" merge_func=\"concat\",\n",
" mono_n_chunks=\"auto\", # merge parameter combinations into chunks\n",
" execute_kwargs=dict(warmup=True, engine=\"pathos\") # run chunks in parallel using Pathos\n",
")\n",
"cv_objective = vbt.split(\n",
" param_objective,\n",
" splitter=splitter, \n",
" takeable_args=[\"data\"], # select date range from data\n",
" merge_func=\"concat\", \n",
")\n",
"\n",
"sharpe_ratio = cv_objective(\n",
" data,\n",
" vbt.Param(np.arange(10, 50), condition=\"slow_period - fast_period >= 5\"),\n",
" vbt.Param(np.arange(10, 50)),\n",
" vbt.Param(np.arange(10, 50), condition=\"fast_period <= atr_period <= slow_period\"),\n",
" vbt.Param(np.arange(2, 5))\n",
")\n",
"print(sharpe_ratio)"
]
},
{
"cell_type": "markdown",
"id": "a5558952-d14d-49a3-9a2d-21152eb3bcdd",
"metadata": {},
"source": [
"We tested over 3 million combinations of date ranges and parameters in just a few minutes."
]
},
{
"cell_type": "markdown",
"id": "dbb436c2-6428-48d3-9c58-67e9293c7333",
"metadata": {},
"source": [
"## Analysis"
]
},
{
"cell_type": "markdown",
"id": "d92c269d-9641-4bf4-a86d-c415343615d5",
"metadata": {},
"source": [
"Let's find out if there's a correlation between the results of the training and testing sets."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9d52a439-cee9-4e6d-9ef6-48dd050e8c7a",
"metadata": {},
"outputs": [],
"source": [
"train_sharpe_ratio = sharpe_ratio.xs(\"train\", level=\"set\")\n",
"test_sharpe_ratio = sharpe_ratio.xs(\"test\", level=\"set\")\n",
"print(train_sharpe_ratio.corr(test_sharpe_ratio))"
]
},
{
"cell_type": "markdown",
"id": "2c2950f5-bd78-4bbc-b78d-bf3bb7f31f02",
"metadata": {},
"source": [
"The analysis indicates a weak negative correlation or no substantial correlation. This suggests that the strategy tends to perform oppositely compared to its results in previous months."
]
},
{
"cell_type": "markdown",
"id": "f565e4ac-2cac-4d81-8b82-8f375333e1ad",
"metadata": {},
"source": [
"And here's an analysis segmented by fast and slow EMA periods. It highlights the minimal variation in the Sharpe ratio from the training to the testing set across at least 50% of the splits, where blue indicates a positive change."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "58be0727-3e27-45bf-bca9-de24674eb5ea",
"metadata": {},
"outputs": [],
"source": [
"sharpe_ratio_diff = test_sharpe_ratio - train_sharpe_ratio\n",
"sharpe_ratio_diff_median = sharpe_ratio_diff.groupby([\"fast_period\", \"slow_period\"]).median()\n",
"sharpe_ratio_diff_median.vbt.heatmap(trace_kwargs=dict(colorscale=\"RdBu\")).show_svg()"
]
},
{
"cell_type": "markdown",
"id": "c5423a9e-289e-421f-886e-753577b4ba13",
"metadata": {},
"source": [
"## Conclusion"
]
},
{
"cell_type": "markdown",
"id": "cfdc909c-6559-4eba-801c-d0792068aba2",
"metadata": {},
"source": [
"Although you might have developed a promising strategy on paper, cross-validating it is essential to confirm its consistent performance over time and to ensure it's not merely a result of random fluctuations."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c1eb4e7a-4698-4c2c-896f-1c8d18a4c3ee",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -0,0 +1,499 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "a7f13baa-bf3a-41e2-b4f4-bef957746b6a",
"metadata": {},
"source": [
"# How to backtest chart patterns with VectorBT PRO"
]
},
{
"cell_type": "markdown",
"id": "1e5237a6-cb1e-42b2-8b74-841af2e8859a",
"metadata": {},
"source": [
"VectorBT PRO (https://vectorbt.pro/) is a proprietary Python package designed for backtesting and analyzing quantitative trading strategies. It provides a comprehensive suite of tools for every stage of an algorithmic trading workflow, including data acquisition, signal generation and analysis, portfolio optimization, strategy simulation, hyperparameter tuning, and cross-validation. These modular components empower users to flexibly customize their analysis, setting it apart from monolithic backtesting frameworks."
]
},
{
"cell_type": "markdown",
"id": "51ad2b2b-3ffa-4600-9f03-547f83d8babb",
"metadata": {},
"source": [
"One of these components is a data pattern detector that efficiently scans data using variable-length windows, assessing their similarity to a specified pattern. This process, optimized with Numba (https://numba.pydata.org/), operates on any hardware without the need for machine learning. To showcase the detector's capabilities, we will conduct backtesting on a range of patterns and their combinations on a single dataset."
]
},
{
"cell_type": "markdown",
"id": "36f9e6a9-eedf-4595-b214-2d00f02d9c90",
"metadata": {},
"source": [
"## Imports and set up"
]
},
{
"cell_type": "markdown",
"id": "33459b0c-c21f-4251-b13b-6492c9171f6c",
"metadata": {},
"source": [
"Due to VectorBT PRO's self-contained design, only minimal imports are necessary."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bc8d53d7-0290-4e6c-b760-6c9ba8a6873e",
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro import *\n",
"# whats_imported()\n",
"\n",
"vbt.settings.set_theme(\"dark\")"
]
},
{
"cell_type": "markdown",
"id": "0aec0980-6ee2-41b1-a713-4a062a823fe5",
"metadata": {},
"source": [
"VectorBT PRO features built-in data downloading from sources such as Yahoo Finance, Alpaca, Polygon, TradingView, and many more. We will perform pattern detection on hourly price data pulled from TradingView."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d12bcb35-95ba-424e-8dfc-0e9edff8df99",
"metadata": {},
"outputs": [],
"source": [
"symbols = [\n",
" \"NASDAQ:META\",\n",
" \"NASDAQ:AMZN\",\n",
" \"NASDAQ:AAPL\",\n",
" \"NASDAQ:NFLX\",\n",
" \"NASDAQ:GOOG\",\n",
"]\n",
"\n",
"data = vbt.TVData.pull(symbols, timeframe=\"hourly\")"
]
},
{
"cell_type": "markdown",
"id": "77e48d78-436d-4a52-95d4-8ff8c1e8ff4c",
"metadata": {},
"source": [
"TradingView does not offer the option to specify a date range in advance, so we will need to select it afterward."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b96e37aa-3c00-4373-8030-ca3d97f872b1",
"metadata": {},
"outputs": [],
"source": [
"start_date = \"2020\"\n",
"end_date = None\n",
"\n",
"data = data.xloc[start_date:end_date]"
]
},
{
"cell_type": "markdown",
"id": "3c9c8009-3a78-4799-bc98-2bd191e22851",
"metadata": {},
"source": [
"Ensure that our data spans the correct date period and is free of NaN values."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "76b530eb-f42e-4bdf-b270-20298a66eb6b",
"metadata": {},
"outputs": [],
"source": [
"print(data.stats())"
]
},
{
"cell_type": "markdown",
"id": "4cf31468-ce25-4284-b0c6-dec873e62268",
"metadata": {},
"source": [
"As pattern detection requires only a single time series, we must choose the suitable feature. We'll utilize HLC/3, which effectively captures price fluctuations."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "650c3662-684c-4e57-b7fa-45ba8b2f7f1d",
"metadata": {},
"outputs": [],
"source": [
"price = data.hlc3"
]
},
{
"cell_type": "markdown",
"id": "74dcad43-dd64-435a-9a9d-591681514209",
"metadata": {},
"source": [
"## Define patterns"
]
},
{
"cell_type": "markdown",
"id": "f387c42a-1224-46d9-9397-b6479e6e21e7",
"metadata": {},
"source": [
"Numerous chart patterns can be translated into numerical sequences, like the \"Double Top\" pattern (https://www.investopedia.com/terms/d/doubletop.asp) represented as [1, 3, 2, 3, 1]. It's important to note that while the numbers themselves can be arbitrary, their relative spacing should mirror the relative distance between the pattern's chart points. For instance, in this sequence, 2 aligns with the midpoint between valley point 1 and peak point 3. The same principle applies to temporal distribution: points should be equidistant from one another."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "40827a2a-ee12-4feb-9f6a-4505ed24060d",
"metadata": {},
"outputs": [],
"source": [
"bullish_patterns = {\n",
" \"double_bottom\": [5, 1, 3, 1, 5],\n",
" \"exp_triangle\": [3, 4, 2, 5, 1, 6],\n",
" \"asc_triangle\": [1, 5, 2, 5, 3, 6],\n",
" \"symm_triangle\": [1, 6, 2, 5, 3, 6],\n",
" \"pennant\": [6, 1, 5, 2, 4, 3, 6]\n",
"}\n",
"bearish_patterns = {\n",
" \"head_and_shoulders\": [1, 4, 2, 6, 2, 4, 1],\n",
" \"double_top\": [1, 5, 3, 5, 1],\n",
" \"desc_triangle\": [6, 2, 5, 2, 4, 1],\n",
" \"symm_triangle\": [6, 1, 5, 2, 4, 1],\n",
" \"pennant\": [1, 6, 2, 5, 3, 4, 1]\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "af76a114-d588-443a-8c62-19274c97c416",
"metadata": {},
"source": [
"Confirm the visual representation of a pattern by plotting its corresponding line graph."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0114e669-fff0-48b1-922b-412ad6941914",
"metadata": {},
"outputs": [],
"source": [
"pd.Series(bullish_patterns[\"double_bottom\"]).vbt.plot().show_svg()"
]
},
{
"cell_type": "markdown",
"id": "11172c01-2675-4c12-ab51-ae21137c097a",
"metadata": {},
"source": [
"Each generated sequence serves as a rough approximation of the desired chart pattern, and there's no need for precise adjustments: VectorBT PRO's similarity-based algorithm is flexible and can identify patterns, even if they are not perfectly consistent in their design."
]
},
{
"cell_type": "markdown",
"id": "4292665d-4168-436a-a59d-94b42bfd9482",
"metadata": {},
"source": [
"## Detect patterns in data"
]
},
{
"cell_type": "markdown",
"id": "0a355587-347a-4f4f-9f7a-fa041127f36a",
"metadata": {},
"source": [
"Iterate through each pattern, dataset, and timestamp within the dataset. Search for matches within windows spanning from 1 to 30 days, and create a record for each match that exceeds a pre-defined minimum similarity score, which is set by default to 85%."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d1a9af95-b7d1-4f29-9a6b-b40d57e5f597",
"metadata": {},
"outputs": [],
"source": [
"min_window = 24\n",
"max_window = 24 * 30\n",
"\n",
"def detect_patterns(patterns):\n",
" return vbt.PatternRanges.from_pattern_search(\n",
" price,\n",
" open=data.open, # OHLC for plotting\n",
" high=data.high,\n",
" low=data.low,\n",
" close=data.close,\n",
" pattern=patterns,\n",
" window=min_window,\n",
" max_window=max_window,\n",
" execute_kwargs=dict( # multithreading\n",
" engine=\"threadpool\", \n",
" chunk_len=\"auto\", \n",
" )\n",
" )\n",
"\n",
"bullish_matches = detect_patterns(vbt.Param(bullish_patterns, name=\"bullish_pattern\"))\n",
"bearish_matches = detect_patterns(vbt.Param(bearish_patterns, name=\"bearish_pattern\"))"
]
},
{
"cell_type": "markdown",
"id": "12733006-548c-4c28-a4ac-902aa066f0b3",
"metadata": {},
"source": [
"In just several minutes, VectorBT PRO seamlessly detected matches among all patterns. This process, involving around 230 million unique pattern and window combinations, was executed in parallel."
]
},
{
"cell_type": "markdown",
"id": "714ddd1f-f5a5-420e-9d4d-707e4b5e4685",
"metadata": {},
"source": [
"Get the number of matches for each pattern and dataset."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "77c5957e-a906-4c0c-998a-5b2e92fd652d",
"metadata": {},
"outputs": [],
"source": [
"print(bullish_matches.count())"
]
},
{
"cell_type": "markdown",
"id": "88b7627b-f48c-4d51-986e-cc269abf9604",
"metadata": {},
"source": [
"Plot the pattern and dataset with the most matches."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "708b0d74-8c5e-4921-87e2-4704050ce7ed",
"metadata": {},
"outputs": [],
"source": [
"vbt.settings.plotting.auto_rangebreaks = True # for stocks\n",
"\n",
"display_column = bullish_matches.count().idxmax()\n",
"\n",
"bullish_matches.plot(column=display_column, fit_ranges=True).show_svg()"
]
},
{
"cell_type": "markdown",
"id": "af6a60cf-0d98-49e5-ad48-cc872f6d2ce9",
"metadata": {},
"source": [
"Zoom in on a match."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f22c6c82-cc7c-4d5d-94b9-e14753e82072",
"metadata": {},
"outputs": [],
"source": [
"display_match = 3\n",
"\n",
"bullish_matches.plot(column=display_column, fit_ranges=display_match).show_svg()"
]
},
{
"cell_type": "markdown",
"id": "45f49c51-bd6f-4952-8ff4-76f6ebc00f7f",
"metadata": {},
"source": [
"The window data closely aligns with the pattern. This functionality is highly comprehensive, offering the flexibility to adjust fitness levels, modify rescaling and interpolation algorithms, and more to suit specific requirements."
]
},
{
"cell_type": "markdown",
"id": "16779944-3cae-44e8-a63d-36194479217c",
"metadata": {},
"source": [
"## Transform matches to signals"
]
},
{
"cell_type": "markdown",
"id": "26c9e03d-95ff-44a3-bd56-2a581673aa27",
"metadata": {},
"source": [
"To conduct backtesting on the identified patterns, we will convert them into signals, triggering a signal once a pattern has fully developed."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ce91720c-dd56-496e-aadb-faad71e1a529",
"metadata": {},
"outputs": [],
"source": [
"entries = bullish_matches.last_pd_mask\n",
"exits = bearish_matches.last_pd_mask"
]
},
{
"cell_type": "markdown",
"id": "d049c224-03b7-42fa-8927-51a502812e54",
"metadata": {},
"source": [
"Generate a Cartesian product of bullish and bearish patterns to systematically test each bullish pattern against each bearish pattern."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a5f0a9db-632d-4705-af3f-1c33dfb6f884",
"metadata": {},
"outputs": [],
"source": [
"entries, exits = entries.vbt.x(exits)"
]
},
{
"cell_type": "markdown",
"id": "90a043fe-c990-4358-94be-b8f4b92dec4f",
"metadata": {},
"source": [
"Both arrays have been converted into equally-shaped DataFrames, each comprising 125 columns. Each column represents an individual backtest, encompassing three parameters: bullish pattern, bearish pattern, and symbol."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8b3a1466-246b-42b5-a61d-a0ae1137c54d",
"metadata": {},
"outputs": [],
"source": [
"print(entries.columns)"
]
},
{
"cell_type": "markdown",
"id": "05669332-15a4-4ac5-b376-bdc08006d952",
"metadata": {},
"source": [
"## Backtest signals"
]
},
{
"cell_type": "markdown",
"id": "a44e90d0-f172-445a-9f4b-865444ae0cb3",
"metadata": {},
"source": [
"Establish a portfolio by simulating signals."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5d3ec70d-73e4-407d-8ea7-2a6b0f4436ba",
"metadata": {},
"outputs": [],
"source": [
"pf = vbt.Portfolio.from_signals(data, entries, exits)"
]
},
{
"cell_type": "markdown",
"id": "ff7d821d-f20e-45c3-83d5-ad1aa2ba109b",
"metadata": {},
"source": [
"Get the mean total return for every combination of bullish and bearish patterns."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ad8b789d-c543-4e42-8df7-1a351cceda5f",
"metadata": {},
"outputs": [],
"source": [
"mean_total_return = pf.total_return.groupby([\"bullish_pattern\", \"bearish_pattern\"]).mean()\n",
"\n",
"print(mean_total_return)"
]
},
{
"cell_type": "markdown",
"id": "946aa00a-b183-496e-8a63-7f11485ad3dc",
"metadata": {},
"source": [
"As visual beings, let's represent these values as a heatmap."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d74ac965-0461-4563-813e-56b9cce979c8",
"metadata": {},
"outputs": [],
"source": [
"mean_total_return.vbt.heatmap(x_level=\"bearish_pattern\", y_level=\"bullish_pattern\").show_svg()"
]
},
{
"cell_type": "markdown",
"id": "5d9f9706-d3da-480e-8b65-5eaa47196049",
"metadata": {},
"source": [
"Although the displayed performance of each pattern combination does not guarantee future results, it provides insight into how the market responded to pattern events in the past. For instance, it's noteworthy that the \"Bearish Symmetrical Triangle\" exhibited a notably bullish trend. Cross-validation and robustness testing are next essential steps for a comprehensive assessment."
]
},
{
"cell_type": "markdown",
"id": "2b5b8516-8620-41aa-a11c-96b48798c343",
"metadata": {},
"source": [
"Read more at https://vectorbt.pro/tutorials/patterns-and-projections/"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9efd6597-880f-4769-a486-65e17b1c5475",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -0,0 +1,266 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "9d9b5c91-f3a3-4709-a36f-40ecc86595d6",
"metadata": {},
"source": [
"# Forecasting future price trends by projecting historical price patterns"
]
},
{
"cell_type": "markdown",
"id": "2cffb873-e431-44f3-b243-e35969bbd2c1",
"metadata": {},
"source": [
"In our previous newsletter focusing on VectorBT PRO (VBT), we dived into the pattern detection capabilities of this powerful library. An additional key functionality is VBT's capacity to extrapolate identified price segments into the future and aggregate them for statistical analysis. This feature can be an invaluable tool for real-time decision-making in market analysis."
]
},
{
"cell_type": "markdown",
"id": "c472968b-1863-4d79-a299-ec67c1757455",
"metadata": {},
"source": [
"## Imports and set up"
]
},
{
"cell_type": "markdown",
"id": "ddf68612-622b-4803-87fc-a1ad80341536",
"metadata": {},
"source": [
"Given the self-contained design of VBT, a single import suffices."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a42ccb91-bc73-4ad5-9327-18c7c22af598",
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro import *\n",
"# whats_imported()\n",
"\n",
"vbt.settings.set_theme(\"dark\")"
]
},
{
"cell_type": "markdown",
"id": "15412fda-c27f-4820-9273-17366164b2b3",
"metadata": {},
"source": [
"Let's define a set of variables for our analysis."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fc016fe0-5ae6-416f-bb4d-84a33a91fce8",
"metadata": {},
"outputs": [],
"source": [
"SYMBOL = \"BTCUSDT\"\n",
"TIMEFRAME = \"1 hour\"\n",
"START = \"one year ago\"\n",
"\n",
"LAST_N_BARS = 24\n",
"PRED_N_BARS = 12\n",
"\n",
"GIF_FNAME = \"projections.gif\"\n",
"GIF_N_BARS = 72\n",
"GIF_FPS = 4\n",
"GIF_PAD = 0.01"
]
},
{
"cell_type": "markdown",
"id": "e4667d70-f1d9-4f34-81ff-fdf8320477ae",
"metadata": {},
"source": [
"We will execute the analysis using price data retrieved from BinanceData, based on the parameters we previously defined."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b797e0ff-320d-456b-91df-1e0e369d83a9",
"metadata": {},
"outputs": [],
"source": [
"data = vbt.BinanceData.pull(SYMBOL, timeframe=TIMEFRAME, start=START)"
]
},
{
"cell_type": "markdown",
"id": "43fade8d-2d1f-492b-88bb-95facd21ceda",
"metadata": {},
"source": [
"## Find and plot projections"
]
},
{
"cell_type": "markdown",
"id": "0013fab2-d1fa-4777-99e9-2081a90444e3",
"metadata": {},
"source": [
"Let's write a function that analyzes the most recent price trend and employs it as a pattern to identify similar price movements in historical data. This pattern recognition function will focus exclusively on segments of price history having a comparable percentage change from their respective starting points."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f7f4ead3-c4db-47d5-8a30-3f7dbe4347dc",
"metadata": {},
"outputs": [],
"source": [
"def find_patterns(data):\n",
" price = data.hlc3\n",
" pattern = price.values[-LAST_N_BARS:]\n",
" pattern_ranges = price.vbt.find_pattern(\n",
" pattern=pattern,\n",
" rescale_mode=\"rebase\",\n",
" overlap_mode=\"allow\",\n",
" wrapper_kwargs=dict(freq=TIMEFRAME)\n",
" )\n",
" pattern_ranges = pattern_ranges.status_closed\n",
" return pattern_ranges\n",
"\n",
"pattern_ranges = find_patterns(data)\n",
"print(pattern_ranges.count())"
]
},
{
"cell_type": "markdown",
"id": "6dc1f00c-f0a2-4b74-831f-3043c14f1195",
"metadata": {},
"source": [
"We have identified a number of price segments that closely resemble the latest price trend. Now, we'll write a function that extracts the price data immediately succeeding each identified segment and plots these as extensions of the price trend. These subsequent segments are known as \"projections.\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9fb7b02c-190a-488e-bfa6-843db23c324e",
"metadata": {},
"outputs": [],
"source": [
"def plot_projections(data, pattern_ranges, **kwargs):\n",
" projection_ranges = pattern_ranges.with_delta(\n",
" PRED_N_BARS,\n",
" open=data.open,\n",
" high=data.high,\n",
" low=data.low,\n",
" close=data.close,\n",
" )\n",
" projection_ranges = projection_ranges.status_closed\n",
" return projection_ranges.plot_projections(\n",
" plot_past_period=LAST_N_BARS, \n",
" **kwargs,\n",
" )\n",
"\n",
"plot_projections(data, pattern_ranges, plot_bands=False).show_svg()"
]
},
{
"cell_type": "markdown",
"id": "8df73436-c6ae-411b-8c44-e5764f9c1812",
"metadata": {},
"source": [
"As we can see, similar price movements have historically branched into a diverse set of trajectories. For a visually compelling and statistically robust forecast, we will display the confidence bands encompassing all the projections, with 60% of these projections falling between the upper and lower bands."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b97458a5-7428-4877-80c6-a522aef4b5ce",
"metadata": {},
"outputs": [],
"source": [
"plot_projections(data, pattern_ranges, plot_bands=True).show_svg()"
]
},
{
"cell_type": "markdown",
"id": "9011e2c5-1745-480c-b9da-c031f6ba9ae2",
"metadata": {},
"source": [
"## Generate animation"
]
},
{
"cell_type": "markdown",
"id": "ac05a0ea-6883-4736-a815-619f76607966",
"metadata": {},
"source": [
"Lastly, we will compile a GIF animation that iterates through a specified range of bars, applying the aforementioned procedure to each bar within that range."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6238530e-9d06-4da4-a71d-3ae7489c2c9a",
"metadata": {},
"outputs": [],
"source": [
"def plot_frame(frame_index, **kwargs):\n",
" sub_data = data.loc[:frame_index[-1]]\n",
" pattern_ranges = find_patterns(sub_data)\n",
" if pattern_ranges.count() < 3:\n",
" return None\n",
" return plot_projections(sub_data, pattern_ranges, **kwargs)\n",
"\n",
"vbt.save_animation(\n",
" GIF_FNAME,\n",
" data.index[-GIF_N_BARS:],\n",
" plot_frame,\n",
" plot_projections=False,\n",
" delta=1,\n",
" fps=GIF_FPS,\n",
" writer_kwargs=dict(loop=0),\n",
" yaxis_range=[\n",
" data.low.iloc[-GIF_N_BARS:].min() * (1 - GIF_PAD), \n",
" data.high.iloc[-GIF_N_BARS:].max() * (1 + GIF_PAD)\n",
" ],\n",
")"
]
},
{
"cell_type": "markdown",
"id": "91b825fb-7e4c-4d48-ae73-bffe633a6f52",
"metadata": {},
"source": [
"Bear in mind that while the confidence bands describe past performance, they should not be used as guarantees of future results."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "319a24bb-e210-4d02-ab2c-0ce58b3dc82c",
"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
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,771 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notebook for the article [Stop Loss, Trailing Stop, or Take Profit? 2 Million Backtests Shed Light](https://polakowo.medium.com/stop-loss-trailing-stop-or-take-profit-2-million-backtests-shed-light-dde23bda40be)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro import *\n",
"# whats_imported()\n",
"\n",
"import ipywidgets\n",
"\n",
"vbt.settings.set_theme('dark')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"seed = 42\n",
"symbols = [\n",
" \"BTC-USD\", \"ETH-USD\", \"XRP-USD\", \"BCH-USD\", \"LTC-USD\", \n",
" \"BNB-USD\", \"EOS-USD\", \"XLM-USD\", \"XMR-USD\", \"ADA-USD\"\n",
"]\n",
"start_date = vbt.utc_timestamp(\"2018-01-01\")\n",
"end_date = vbt.utc_timestamp(\"2021-01-01\")\n",
"time_delta = end_date - start_date\n",
"window_len = vbt.timedelta(\"180d\")\n",
"window_cnt = 400\n",
"exit_types = [\"SL\", \"TS\", \"TP\", \"Random\", \"Holding\"]\n",
"step = 0.01\n",
"stops = np.arange(step, 1 + step, step)\n",
"\n",
"vbt.settings.wrapping[\"freq\"] = \"d\"\n",
"vbt.settings.plotting[\"layout\"][\"template\"] = \"vbt_dark\"\n",
"vbt.settings.portfolio[\"init_cash\"] = 100.\n",
"\n",
"print(pd.Series({\n",
" \"Start date\": start_date,\n",
" \"End date\": end_date,\n",
" \"Time period (days)\": time_delta.days,\n",
" \"Assets\": len(symbols),\n",
" \"Window length\": window_len,\n",
" \"Windows\": window_cnt,\n",
" \"Exit types\": len(exit_types),\n",
" \"Stop values\": len(stops),\n",
" \"Tests per asset\": window_cnt * len(stops) * len(exit_types),\n",
" \"Tests per window\": len(symbols) * len(stops) * len(exit_types),\n",
" \"Tests per exit type\": len(symbols) * window_cnt * len(stops),\n",
" \"Tests per stop type and value\": len(symbols) * window_cnt,\n",
" \"Tests total\": len(symbols) * window_cnt * len(stops) * len(exit_types)\n",
"}))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cols = [\"Open\", \"Low\", \"High\", \"Close\", \"Volume\"]\n",
"yfdata = vbt.YFData.pull(symbols, start=start_date, end=end_date)\n",
"\n",
"print(yfdata.data.keys())\n",
"print(yfdata.data[\"BTC-USD\"].shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"yfdata.plot(symbol=\"BTC-USD\").show_svg()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ohlcv = yfdata.concat()\n",
"\n",
"print(ohlcv.keys())\n",
"print(ohlcv[\"Open\"].shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"splitter = vbt.Splitter.from_n_rolling(\n",
" ohlcv[\"Open\"].index, \n",
" n=window_cnt,\n",
" length=window_len.days\n",
")\n",
"\n",
"split_ohlcv = {}\n",
"for k, v in ohlcv.items():\n",
" split_ohlcv[k] = splitter.take(v, into=\"reset_stacked\")\n",
"print(split_ohlcv[\"Open\"].shape)\n",
"\n",
"split_indexes = splitter.take(ohlcv[\"Open\"].index)\n",
"print(split_indexes)\n",
"print(split_indexes[10])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(split_ohlcv[\"Open\"].columns)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"entries = pd.DataFrame.vbt.signals.empty_like(split_ohlcv[\"Open\"])\n",
"entries.iloc[0, :] = True\n",
"\n",
"print(entries.shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# We use OHLCSTX instead of built-in stop-loss in Portfolio.from_signals\n",
"# because we want to analyze signals before simulation + it's easier to construct param grids\n",
"# For reality check, run the same setup using Portfolio.from_signals alone\n",
"\n",
"sl_ohlcstx = vbt.OHLCSTX.run(\n",
" entries, \n",
" entry_price=split_ohlcv[\"Close\"], \n",
" open=split_ohlcv[\"Open\"], \n",
" high=split_ohlcv[\"High\"], \n",
" low=split_ohlcv[\"Low\"], \n",
" close=split_ohlcv[\"Close\"], \n",
" sl_stop=list(stops),\n",
" stop_type=None\n",
")\n",
"sl_exits = sl_ohlcstx.exits.copy()\n",
"sl_price = sl_ohlcstx.close.copy()\n",
"sl_price[sl_exits] = sl_ohlcstx.stop_price\n",
"del sl_ohlcstx\n",
"\n",
"print(sl_exits.shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tsl_ohlcstx = vbt.OHLCSTX.run(\n",
" entries, \n",
" entry_price=split_ohlcv[\"Close\"], \n",
" open=split_ohlcv[\"Open\"], \n",
" high=split_ohlcv[\"High\"], \n",
" low=split_ohlcv[\"Low\"], \n",
" close=split_ohlcv[\"Close\"], \n",
" tsl_stop=list(stops),\n",
" stop_type=None\n",
")\n",
"tsl_exits = tsl_ohlcstx.exits.copy()\n",
"tsl_price = tsl_ohlcstx.close.copy()\n",
"tsl_price[tsl_exits] = tsl_ohlcstx.stop_price\n",
"del tsl_ohlcstx\n",
"\n",
"print(tsl_exits.shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tp_ohlcstx = vbt.OHLCSTX.run(\n",
" entries, \n",
" entry_price=split_ohlcv[\"Close\"], \n",
" open=split_ohlcv[\"Open\"], \n",
" high=split_ohlcv[\"High\"], \n",
" low=split_ohlcv[\"Low\"], \n",
" close=split_ohlcv[\"Close\"], \n",
" tp_stop=list(stops),\n",
" stop_type=None\n",
")\n",
"tp_exits = tp_ohlcstx.exits.copy()\n",
"tp_price = tp_ohlcstx.close.copy()\n",
"tp_price[tp_exits] = tp_ohlcstx.stop_price\n",
"del tp_ohlcstx\n",
"\n",
"print(tp_exits.shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def rename_stop_level(df):\n",
" return df.vbt.rename_levels({\n",
" \"ohlcstx_sl_stop\": \"stop_value\",\n",
" \"ohlcstx_tsl_stop\": \"stop_value\",\n",
" \"ohlcstx_tp_stop\": \"stop_value\"\n",
" }, strict=False)\n",
"\n",
"sl_exits = rename_stop_level(sl_exits)\n",
"tsl_exits = rename_stop_level(tsl_exits)\n",
"tp_exits = rename_stop_level(tp_exits)\n",
"\n",
"sl_price = rename_stop_level(sl_price)\n",
"tsl_price = rename_stop_level(tsl_price)\n",
"tp_price = rename_stop_level(tp_price)\n",
"\n",
"print(sl_exits.columns)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(pd.Series({\n",
" \"SL\": sl_exits.vbt.signals.total().mean(),\n",
" \"TS\": tsl_exits.vbt.signals.total().mean(),\n",
" \"TP\": tp_exits.vbt.signals.total().mean()\n",
"}, name=\"avg_num_signals\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def groupby_stop_value(df):\n",
" return df.vbt.signals.total().groupby(\"stop_value\").mean()\n",
"\n",
"pd.DataFrame({\n",
" \"Stop Loss\": groupby_stop_value(sl_exits),\n",
" \"Trailing Stop\": groupby_stop_value(tsl_exits),\n",
" \"Take Profit\": groupby_stop_value(tp_exits)\n",
"}).vbt.plot(\n",
" xaxis_title=\"Stop value\", \n",
" yaxis_title=\"Avg number of signals\"\n",
").show_svg()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sl_exits.iloc[-1, :] = True\n",
"tsl_exits.iloc[-1, :] = True\n",
"tp_exits.iloc[-1, :] = True\n",
"\n",
"sl_exits = sl_exits.vbt.signals.first_after(entries)\n",
"tsl_exits = tsl_exits.vbt.signals.first_after(entries)\n",
"tp_exits = tp_exits.vbt.signals.first_after(entries)\n",
"\n",
"print(pd.Series({\n",
" \"SL\": sl_exits.vbt.signals.total().mean(),\n",
" \"TS\": tsl_exits.vbt.signals.total().mean(),\n",
" \"TP\": tp_exits.vbt.signals.total().mean()\n",
"}, name=\"avg_num_signals\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hold_exits = pd.DataFrame.vbt.signals.empty_like(sl_exits)\n",
"hold_exits.iloc[-1, :] = True\n",
"hold_price = vbt.broadcast_to(split_ohlcv[\"Close\"], sl_price)\n",
"\n",
"print(hold_exits.shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"rand_exits = hold_exits.vbt.shuffle(seed=seed)\n",
"rand_price = hold_price\n",
"\n",
"print(rand_exits.shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"exits = pd.DataFrame.vbt.concat(\n",
" sl_exits, \n",
" tsl_exits, \n",
" tp_exits, \n",
" rand_exits, \n",
" hold_exits, \n",
" keys=pd.Index(exit_types, name=\"exit_type\")\n",
")\n",
"del sl_exits\n",
"del tsl_exits\n",
"del tp_exits\n",
"del rand_exits\n",
"del hold_exits\n",
"\n",
"print(exits.shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"price = pd.DataFrame.vbt.concat(\n",
" sl_price, \n",
" tsl_price, \n",
" tp_price, \n",
" rand_price, \n",
" hold_price, \n",
" keys=pd.Index(exit_types, name=\"exit_type\")\n",
")\n",
"del sl_price\n",
"del tsl_price\n",
"del tp_price\n",
"del rand_price\n",
"del hold_price\n",
"\n",
"print(price.shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(exits.columns)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(exits.vbt.getsize())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(price.vbt.getsize())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"avg_distance = entries.vbt.signals.between_ranges(target=exits)\\\n",
" .duration.mean()\\\n",
" .groupby([\"exit_type\", \"stop_value\"])\\\n",
" .mean()\\\n",
" .unstack(level=\"exit_type\")\n",
"\n",
"print(avg_distance.mean())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"avg_distance[exit_types].vbt.plot(\n",
" xaxis_title=\"Stop value\", \n",
" yaxis_title=\"Avg distance to entry\"\n",
").show_svg()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"pf = vbt.Portfolio.from_signals(\n",
" split_ohlcv[\"Close\"], \n",
" entries, \n",
" exits, \n",
" price=price\n",
")\n",
"\n",
"print(len(pf.orders))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"total_return = pf.total_return\n",
"del pf\n",
"\n",
"print(total_return.shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import gc\n",
"\n",
"total_returns = []\n",
"for i in vbt.ProgressBar(range(len(exit_types))):\n",
" chunk_mask = exits.columns.get_level_values(\"exit_type\") == exit_types[i]\n",
" chunk_pf = vbt.Portfolio.from_signals(\n",
" split_ohlcv[\"Close\"], \n",
" entries, \n",
" exits.loc[:, chunk_mask],\n",
" price=price.loc[:, chunk_mask]\n",
" )\n",
" total_returns.append(chunk_pf.total_return)\n",
" \n",
" del chunk_pf\n",
" gc.collect()\n",
" \n",
"total_return = pd.concat(total_returns)\n",
"\n",
"print(total_return.shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"return_by_type = total_return.unstack(level=\"exit_type\")[exit_types]\n",
"\n",
"print(return_by_type[\"Holding\"].describe(percentiles=[]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"purple_color = vbt.settings[\"plotting\"][\"color_schema\"][\"purple\"]\n",
"return_by_type[\"Holding\"].vbt.histplot(\n",
" xaxis_title=\"Total return\",\n",
" xaxis_tickformat=\".2%\",\n",
" yaxis_title=\"Count\",\n",
" trace_kwargs=dict(marker_color=purple_color)\n",
").show_svg()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(pd.DataFrame({\n",
" \"Mean\": return_by_type.mean(),\n",
" \"Median\": return_by_type.median(),\n",
" \"Std\": return_by_type.std(),\n",
"}))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"return_by_type.vbt.boxplot(\n",
" trace_kwargs=dict(boxpoints=False),\n",
" yaxis_title=\"Total return\",\n",
" yaxis_tickformat=\".2%\"\n",
").show_svg()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print((return_by_type > 0).mean().rename(\"win_rate\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"init_cash = vbt.settings.portfolio[\"init_cash\"]\n",
"\n",
"def get_expectancy(return_by_type, level_name):\n",
" grouped = return_by_type.groupby(level_name, axis=0)\n",
" win_rate = grouped.apply(lambda x: (x > 0).mean())\n",
" avg_win = grouped.apply(lambda x: init_cash * x[x > 0].mean())\n",
" avg_win = avg_win.fillna(0)\n",
" avg_loss = grouped.apply(lambda x: init_cash * x[x < 0].mean())\n",
" avg_loss = avg_loss.fillna(0)\n",
" return win_rate * avg_win - (1 - win_rate) * np.abs(avg_loss)\n",
" \n",
"expectancy_by_stop = get_expectancy(return_by_type, \"stop_value\")\n",
"\n",
"print(expectancy_by_stop.mean())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"expectancy_by_stop.vbt.plot(\n",
" xaxis_title=\"Stop value\", \n",
" yaxis_title=\"Expectancy\"\n",
").show_svg()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"return_values = np.sort(return_by_type[\"Holding\"].values)\n",
"idxs = np.ceil(np.linspace(0, len(return_values) - 1, 21)).astype(int)\n",
"bins = return_values[idxs][:-1]\n",
"\n",
"def bin_return(return_by_type):\n",
" classes = pd.cut(return_by_type[\"Holding\"], bins=bins, right=True)\n",
" new_level = np.array(classes.apply(lambda x: x.right))\n",
" new_level = pd.Index(new_level, name=\"bin_right\")\n",
" return return_by_type.vbt.add_levels(new_level, axis=0)\n",
"\n",
"binned_return_by_type = bin_return(return_by_type)\n",
"\n",
"expectancy_by_bin = get_expectancy(binned_return_by_type, \"bin_right\")\n",
"\n",
"expectancy_by_bin.vbt.plot(\n",
" trace_kwargs=dict(mode=\"lines\"),\n",
" xaxis_title=\"Total return of holding\",\n",
" xaxis_tickformat=\".2%\",\n",
" yaxis_title=\"Expectancy\"\n",
").show_svg()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"range_starts = pd.DatetimeIndex(list(map(lambda x: x[0], split_indexes)))\n",
"range_ends = pd.DatetimeIndex(list(map(lambda x: x[-1], split_indexes)))\n",
"\n",
"symbol_lvl = return_by_type.index.get_level_values(\"symbol\")\n",
"split_lvl = return_by_type.index.get_level_values(\"split\")\n",
"range_start_lvl = range_starts[split_lvl]\n",
"range_end_lvl = range_ends[split_lvl]\n",
"\n",
"asset_multi_select = ipywidgets.SelectMultiple(\n",
" options=symbols,\n",
" value=symbols,\n",
" rows=len(symbols),\n",
" description=\"Symbols\"\n",
")\n",
"dates = np.unique(yfdata.wrapper.index)\n",
"date_range_slider = ipywidgets.SelectionRangeSlider(\n",
" options=dates,\n",
" index=(0, len(dates)-1),\n",
" orientation=\"horizontal\",\n",
" readout=False,\n",
" continuous_update=False\n",
")\n",
"range_start_label = ipywidgets.Label()\n",
"range_end_label = ipywidgets.Label()\n",
"metric_dropdown = ipywidgets.Dropdown(\n",
" options=[\"Mean\", \"Median\", \"Win Rate\", \"Expectancy\"],\n",
" value=\"Expectancy\"\n",
")\n",
"stop_scatter = vbt.Scatter(\n",
" trace_names=exit_types,\n",
" x_labels=stops, \n",
" xaxis_title=\"Stop value\", \n",
" yaxis_title=\"Expectancy\"\n",
")\n",
"stop_scatter_img = ipywidgets.Image(\n",
" format=\"png\",\n",
" width=stop_scatter.fig.layout.width,\n",
" height=stop_scatter.fig.layout.height\n",
")\n",
"bin_scatter = vbt.Scatter(\n",
" trace_names=exit_types,\n",
" x_labels=expectancy_by_bin.index, \n",
" trace_kwargs=dict(mode=\"lines\"),\n",
" xaxis_title=\"Total return of holding\",\n",
" xaxis_tickformat=\"%\",\n",
" yaxis_title=\"Expectancy\"\n",
")\n",
"bin_scatter_img = ipywidgets.Image(\n",
" format=\"png\",\n",
" width=bin_scatter.fig.layout.width,\n",
" height=bin_scatter.fig.layout.height\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def update_scatter(*args, **kwargs):\n",
" _symbols = asset_multi_select.value\n",
" _from = date_range_slider.value[0]\n",
" _to = date_range_slider.value[1]\n",
" _metric_name = metric_dropdown.value\n",
" \n",
" range_mask = (range_start_lvl >= _from) & (range_end_lvl <= _to)\n",
" asset_mask = symbol_lvl.isin(_symbols)\n",
" filt = return_by_type[range_mask & asset_mask]\n",
" \n",
" filt_binned = bin_return(filt)\n",
" if _metric_name == \"Mean\":\n",
" filt_metric = filt.groupby(\"stop_value\").mean()\n",
" filt_bin_metric = filt_binned.groupby(\"bin_right\").mean()\n",
" elif _metric_name == \"Median\":\n",
" filt_metric = filt.groupby(\"stop_value\").median()\n",
" filt_bin_metric = filt_binned.groupby(\"bin_right\").median()\n",
" elif _metric_name == \"Win Rate\":\n",
" filt_metric = (filt > 0).groupby(\"stop_value\").mean()\n",
" filt_bin_metric = (filt_binned > 0).groupby(\"bin_right\").mean()\n",
" elif _metric_name == \"Expectancy\":\n",
" filt_metric = get_expectancy(filt, \"stop_value\")\n",
" filt_bin_metric = get_expectancy(filt_binned, \"bin_right\")\n",
" \n",
" stop_scatter.fig.update_layout(yaxis_title=_metric_name)\n",
" stop_scatter.update(filt_metric)\n",
" stop_scatter_img.value = stop_scatter.fig.to_image(format=\"png\")\n",
" \n",
" bin_scatter.fig.update_layout(yaxis_title=_metric_name)\n",
" bin_scatter.update(filt_bin_metric)\n",
" bin_scatter_img.value = bin_scatter.fig.to_image(format=\"png\")\n",
" \n",
" range_start_label.value = np.datetime_as_string(_from.to_datetime64(), unit=\"D\")\n",
" range_end_label.value = np.datetime_as_string(_to.to_datetime64(), unit=\"D\")\n",
" \n",
"asset_multi_select.observe(update_scatter, names=\"value\")\n",
"date_range_slider.observe(update_scatter, names=\"value\")\n",
"metric_dropdown.observe(update_scatter, names=\"value\")\n",
"update_scatter()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"dashboard = ipywidgets.VBox([\n",
" asset_multi_select,\n",
" ipywidgets.HBox([\n",
" range_start_label,\n",
" date_range_slider,\n",
" range_end_label\n",
" ]),\n",
" metric_dropdown,\n",
" stop_scatter_img,\n",
" bin_scatter_img\n",
"])\n",
"dashboard"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"dashboard.close()"
]
},
{
"cell_type": "code",
"execution_count": null,
"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"
},
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"state": {},
"version_major": 2,
"version_minor": 0
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,285 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this example, we will build a Telegram bot that sends a signal once any Bollinger Band has been crossed. We will periodically query for the latest OHLCV data of the selected cryptocurrencies and append this data to our data pool. Additionally to receiving signals, any Telegram user can join the group and ask the bot to provide him with the current information. If the price change is higher than some number of standard deviations from the mean, while crossing the band, the bot sends a funny GIF."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from telegram import __version__ as TG_VER\n",
"\n",
"try:\n",
" from telegram import __version_info__\n",
"except ImportError:\n",
" __version_info__ = (0, 0, 0, 0, 0)\n",
"\n",
"if __version_info__ >= (20, 0, 0, \"alpha\", 1):\n",
" raise RuntimeError(f\"This example is not compatible with your current PTB version {TG_VER}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro import *\n",
"# whats_imported()\n",
"\n",
"import logging"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)\n",
"logger = logging.getLogger(__name__)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Telegram\n",
"vbt.settings.messaging['telegram']['token'] = \"YOUR_TOKEN\"\n",
"\n",
"# Giphy\n",
"vbt.settings.messaging['giphy']['api_key'] = \"YOUR_API_KEY\"\n",
"\n",
"# Data\n",
"SYMBOLS = ['BTC/USDT', 'ETH/USDT', 'ADA/USDT']\n",
"START = '1 hour ago UTC'\n",
"TIMEFRAME = '1m'\n",
"UPDATE_EVERY = vbt.utils.datetime_.interval_to_ms(TIMEFRAME) // 1000 # in seconds\n",
"DT_FORMAT = '%d %b %Y %H:%M:%S %z'\n",
"IND_PARAMS = dict(\n",
" timeperiod=20, \n",
" nbdevup=2, \n",
" nbdevdn=2\n",
")\n",
"CHANGE_NBDEV = 2"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"data = vbt.CCXTData.pull(SYMBOLS, start=START, timeframe=TIMEFRAME)\n",
"\n",
"print(data.wrapper.index)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_bbands(data):\n",
" return vbt.IndicatorFactory.from_talib('BBANDS').run(\n",
" data.get('Close'), **IND_PARAMS, hide_params=list(IND_PARAMS.keys()))\n",
"\n",
"\n",
"def get_info(bbands):\n",
" info = dict()\n",
" info['last_price'] = bbands.close.iloc[-1]\n",
" info['last_change'] = (bbands.close.iloc[-1] - bbands.close.iloc[-2]) / bbands.close.iloc[-1]\n",
" info['last_crossed_above_upper'] = bbands.close_crossed_above(bbands.upperband).iloc[-1]\n",
" info['last_crossed_below_upper'] = bbands.close_crossed_below(bbands.upperband).iloc[-1]\n",
" info['last_crossed_below_lower'] = bbands.close_crossed_below(bbands.lowerband).iloc[-1]\n",
" info['last_crossed_above_lower'] = bbands.close_crossed_above(bbands.lowerband).iloc[-1]\n",
" info['bw'] = (bbands.upperband - bbands.lowerband) / bbands.middleband\n",
" info['last_bw_zscore'] = info['bw'].vbt.zscore().iloc[-1]\n",
" info['last_change_zscore'] = bbands.close.vbt.pct_change().vbt.zscore().iloc[-1]\n",
" info['last_change_pos'] = info['last_change_zscore'] >= CHANGE_NBDEV\n",
" info['last_change_neg'] = info['last_change_zscore'] <= -CHANGE_NBDEV\n",
" return info\n",
"\n",
"\n",
"def format_symbol_info(symbol, info):\n",
" last_change = info['last_change'][symbol]\n",
" last_price = info['last_price'][symbol]\n",
" last_bw_zscore = info['last_bw_zscore'][symbol]\n",
" return \"{} ({:.2%}, {}, {:.2f})\".format(symbol, last_change, last_price, last_bw_zscore)\n",
"\n",
"\n",
"def format_signals_info(emoji, signals, info):\n",
" symbols = signals.index[signals]\n",
" symbol_msgs = []\n",
" for symbol in symbols:\n",
" symbol_msgs.append(format_symbol_info(symbol, info))\n",
" return \"{} {}\".format(emoji, ', '.join(symbol_msgs))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from telegram.ext import CommandHandler\n",
"\n",
"class MyTelegramBot(vbt.TelegramBot):\n",
" def __init__(self, data, **kwargs):\n",
" super().__init__(data=data, **kwargs)\n",
" \n",
" self.data = data\n",
" self.update_ts = data.wrapper.index[-1]\n",
" \n",
" @property\n",
" def custom_handlers(self):\n",
" return (CommandHandler('info', self.info_callback),)\n",
" \n",
" def info_callback(self, update, context):\n",
" chat_id = update.effective_chat.id\n",
" if len(context.args) != 1:\n",
" await self.send_message(chat_id, \"Please provide one symbol.\")\n",
" return\n",
" symbol = context.args[0]\n",
" if symbol not in SYMBOLS:\n",
" await self.send_message(chat_id, f\"There is no such symbol as \\\"{symbol}\\\".\")\n",
" return\n",
" \n",
" bbands = get_bbands(self.data)\n",
" info = get_info(bbands)\n",
" messages = [format_symbol_info(symbol, info)]\n",
" message = '\\n'.join([\"{}:\".format(self.update_ts.strftime(DT_FORMAT))] + messages)\n",
" await self.send_message(chat_id, message)\n",
" \n",
" @property\n",
" def start_message(self):\n",
" index = self.data.wrapper.index\n",
" return f\"\"\"Hello! \n",
"\n",
"Starting with {len(index)} rows from {index[0].strftime(DT_FORMAT)} to {index[-1].strftime(DT_FORMAT)}.\"\"\"\n",
" \n",
" @property\n",
" def help_message(self):\n",
" return \"\"\"Message format:\n",
"[event] [symbol] ([price change], [new price], [bandwidth z-score])\n",
" \n",
"Event legend:\n",
"⬆️ - Price went above upper band\n",
"⤵️ - Price retraced below upper band\n",
"⬇️ - Price went below lower band\n",
"⤴️ - Price retraced above lower band\n",
"\n",
"GIF is sent once a band is crossed and the price change is 2 stds from the mean.\"\"\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"telegram_bot = MyTelegramBot(data)\n",
"telegram_bot.start(in_background=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class MyDataUpdater(vbt.DataUpdater):\n",
" _expected_keys=None\n",
" \n",
" def __init__(self, data, telegram_bot, **kwargs):\n",
" super().__init__(data, telegram_bot=telegram_bot, **kwargs)\n",
" \n",
" self.telegram_bot = telegram_bot\n",
" self.update_ts = data.wrapper.index[-1]\n",
" \n",
" def update(self):\n",
" super().update()\n",
" self.update_ts = vbt.timestamp(tz=self.update_ts.tz)\n",
" self.telegram_bot.data = self.data\n",
" self.telegram_bot.update_ts = self.update_ts\n",
" \n",
" bbands = get_bbands(self.data)\n",
" info = get_info(bbands)\n",
" \n",
" messages = []\n",
" if info['last_crossed_above_upper'].any():\n",
" messages.append(format_signals_info('⬆️', info['last_crossed_above_upper'], info))\n",
" if info['last_crossed_below_upper'].any():\n",
" messages.append(format_signals_info('⤵️', info['last_crossed_below_upper'], info))\n",
" if info['last_crossed_below_lower'].any():\n",
" messages.append(format_signals_info('⬇️', info['last_crossed_below_lower'], info))\n",
" if info['last_crossed_above_lower'].any():\n",
" messages.append(format_signals_info('⤴️', info['last_crossed_above_lower'], info))\n",
" \n",
" if len(messages) > 0:\n",
" message = '\\n'.join([\"{}:\".format(self.update_ts.strftime(DT_FORMAT))] + messages)\n",
" self.telegram_bot.send_message_to_all(message)\n",
" if (info['last_crossed_above_upper'] & info['last_change_pos']).any():\n",
" self.telegram_bot.send_giphy_to_all(\"launch\")\n",
" if (info['last_crossed_below_lower'] & info['last_change_neg']).any():\n",
" self.telegram_bot.send_giphy_to_all(\"fall\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"data_updater = MyDataUpdater(data, telegram_bot)\n",
"data_updater.update_every(UPDATE_EVERY)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"telegram_bot.stop()"
]
},
{
"cell_type": "code",
"execution_count": null,
"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": 4
}