{ "cells": [ { "cell_type": "markdown", "id": "e6959247", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "id": "3cd2f51a", "metadata": {}, "source": [ "This notebook demonstrates how to backtest a multi-timeframe (MTF) trading strategy using VectorBT PRO. The code involves fetching asset price data, calculating technical indicators across multiple timeframes, and setting up a unified trading portfolio. It addresses common pitfalls in MTF analysis, such as look-ahead bias and information loss, by resampling data effectively. The final output includes visualizations of the cumulative returns and trade signals for each timeframe, aiding in financial modeling and strategy optimization." ] }, { "cell_type": "code", "execution_count": null, "id": "debf173e", "metadata": {}, "outputs": [], "source": [ "from vectorbtpro import *" ] }, { "cell_type": "markdown", "id": "db7d0fe5", "metadata": {}, "source": [ "Configure our graphs to be dark and gap-free." ] }, { "cell_type": "code", "execution_count": null, "id": "9e8cc500", "metadata": {}, "outputs": [], "source": [ "vbt.settings.set_theme(\"dark\")\n", "vbt.settings.plotting.auto_rangebreaks = True" ] }, { "cell_type": "markdown", "id": "ae287211", "metadata": {}, "source": [ "Fetch hourly price data for TSLA from 2023 to 2024." ] }, { "cell_type": "code", "execution_count": null, "id": "9443b439", "metadata": {}, "outputs": [], "source": [ "data = vbt.YFData.pull(\"TSLA\", start=\"2023\", end=\"2024\", timeframe=\"hourly\")" ] }, { "cell_type": "markdown", "id": "c8a28ff6", "metadata": {}, "source": [ "Calculate the fast and slow SMA indicators across different timeframes." ] }, { "cell_type": "code", "execution_count": null, "id": "1c64d052", "metadata": {}, "outputs": [], "source": [ "fast_sma = data.run(\n", " \"talib:sma\", \n", " timeframe=[\"1h\", \"4h\", \"1d\"], \n", " timeperiod=vbt.Default(20),\n", " skipna=True\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "ce377e0d", "metadata": {}, "outputs": [], "source": [ "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": "bf5600f1", "metadata": {}, "source": [ "Plot the fast SMA indicators to observe their behavior across timeframes." ] }, { "cell_type": "code", "execution_count": null, "id": "9fd489d4", "metadata": {}, "outputs": [], "source": [ "fast_sma.real.vbt.plot().show_png()" ] }, { "cell_type": "markdown", "id": "12c9d08d", "metadata": {}, "source": [ "Set up a trading portfolio based on SMA crossover signals and allocate capital." ] }, { "cell_type": "code", "execution_count": null, "id": "3d4ae2ac", "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": "e36ba1b5", "metadata": {}, "source": [ "Plot cumulative returns for each timeframe and the entire portfolio." ] }, { "cell_type": "code", "execution_count": null, "id": "0427faea", "metadata": {}, "outputs": [], "source": [ "fig = (\n", " pf\n", " .get_cumulative_returns()\n", " .vbt\n", " .plot(trace_kwargs=dict(line_color=\"gray\", line_dash=\"dot\"))\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "d01990a8", "metadata": {}, "outputs": [], "source": [ "fig = pf.get_cumulative_returns(group_by=False).vbt.plot(fig=fig)\n", "fig.show_png()" ] }, { "cell_type": "markdown", "id": "1952ee00", "metadata": {}, "source": [ "Plot indicators and trade signals for the daily timeframe." ] }, { "cell_type": "code", "execution_count": null, "id": "021534ea", "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_png()" ] }, { "cell_type": "markdown", "id": "5dedb6a8", "metadata": {}, "source": [ "Combine indicators from different timeframes and identify the best performing pairs." ] }, { "cell_type": "code", "execution_count": null, "id": "f73b207b", "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": "3d5c13f3", "metadata": {}, "source": [ "Timeframe is another strategy parameter to tweak for novel insights and cross-validation." ] }, { "cell_type": "markdown", "id": "20613667", "metadata": {}, "source": [ "Timeframes offer a unique dimension for strategy optimization and should be cross-validated like other parameters." ] }, { "cell_type": "markdown", "id": "7d7aa76b", "metadata": {}, "source": [ "PyQuant News is where finance practitioners level up with Python for quant finance, algorithmic trading, and market data analysis. Looking to get started? Check out the fastest growing, top-selling course to get started with Python for quant finance. For educational purposes. Not investment advise. Use at your own risk." ] } ], "metadata": { "jupytext": { "cell_metadata_filter": "-all", "main_language": "python", "notebook_metadata_filter": "-all" } }, "nbformat": 4, "nbformat_minor": 5 }