Files
pandas-ta/examples/VectorBT_Backtest_with_Pandas_TA.ipynb
T

1.3 MiB
Raw Blame History

Backtesting with vectorbt

  • Data Acquisition (yfinance)
  • Statistical and Technical Analysis (pandas_ta)
  • Backtesting Analysis and Results (vectorbt)

Initializations

In [1]:
import asyncio
import itertools
from datetime import datetime

from IPython import display

import numpy as np
import pandas as pd
import pandas_ta as ta
import vectorbt as vbt

import plotly.graph_objects as go

print("Package Versions:")
print(f"Numpy v{np.__version__}")
print(f"Pandas v{pd.__version__}")
print(f"vectorbt v{vbt.__version__}")
print(f"\nPandas TA v{ta.version}\nTo install the Latest Version:\n$ pip install -U git+https://github.com/twopirllc/pandas-ta\n")

%matplotlib inline
Package Versions:
Numpy v1.20.3
Pandas v1.3.0
vectorbt v0.23.1

Pandas TA v0.3.32b0
To install the Latest Version:
$ pip install -U git+https://github.com/twopirllc/pandas-ta

vectorbt Theme and Portfolio Settings

In [2]:
cheight, cwidth = 500, 1000 # Adjust as needed for Chart Height and Width
vbt.settings.set_theme("dark") # Options: "light" (Default), "dark" (my fav), "seaborn"

# Must be set
vbt.settings.portfolio["freq"] = "1D" # Daily

# Predefine vectorbt Portfolio settings
# vbt.settings.portfolio["init_cash"] = 100
vbt.settings.portfolio["fees"] = 0.0025 # 0.25%
vbt.settings.portfolio["slippage"] = 0.0025 # 0.25%
# vbt.settings.portfolio["size"] = 100
# vbt.settings.portfolio["accumulate"] = False
vbt.settings.portfolio["allow_partial"] = False

pf_settings = pd.DataFrame(vbt.settings.portfolio.items(), columns=["Option", "Value"])
pf_settings.set_index("Option", inplace=True)

print(f"Portfolio Settings [Initial]")
pf_settings
Out [2]:
Portfolio Settings [Initial]
Value
Option
call_seq default
init_cash 100.0
size inf
size_type amount
fees 0.0025
fixed_fees 0.0
slippage 0.0025
reject_prob 0.0
min_size 0.0
max_size inf
size_granularity NaN
lock_cash False
allow_partial False
raise_reject False
val_price inf
accumulate False
sl_stop NaN
sl_trail False
tp_stop NaN
stop_entry_price close
stop_exit_price stoplimit
stop_conflict_mode exit
upon_stop_exit close
upon_stop_update override
use_stops None
log False
upon_long_conflict ignore
upon_short_conflict ignore
upon_dir_conflict ignore
upon_opposite_entry reversereduce
signal_direction longonly
order_direction both
cash_sharing False
call_pre_segment False
call_post_segment False
ffill_val_price True
update_value False
fill_pos_record True
row_wise False
flexible False
use_numba True
seed None
freq 1D
attach_call_seq False
fillna_close True
trades_type exittrades
stats {'filters': {'has_year_freq': {'filter_func': ...
plots {'subplots': ['orders', 'trade_pnl', 'cum_retu...

Helper Methods

In [3]:
def combine_stats(pf: vbt.portfolio.base.Portfolio, ticker: str, strategy: str, mode: int = 0):
    header = pd.Series({
        "Run Time": ta.get_time(full=False, to_string=True),
        "Mode": "LIVE" if mode else "TEST",
        "Strategy": strategy,
        "Direction": vbt.settings.portfolio["signal_direction"],
        "Symbol": ticker.upper(),
        "Fees [%]": 100 * vbt.settings.portfolio["fees"],
        "Slippage [%]": 100 * vbt.settings.portfolio["slippage"],
        "Accumulate": vbt.settings.portfolio["accumulate"],
    })
    rstats = pf.returns_stats().dropna(axis=0).T
    stats = pf.stats().dropna(axis=0).T
    joint = pd.concat([header, stats, rstats])
    return joint[~joint.index.duplicated(keep="first")]

def earliest_common_index(d: dict):
    """Returns index of the earliest common index of all DataFrames in the dict"""
    min_date = None
    for df in d.values():
        if min_date is None:
            min_date = df.index[0]
        elif min_date < df.index[0]:
            min_date = df.index[0]
    return min_date

def dl(tickers: list, same_start: bool = False, **kwargs):
    if isinstance(tickers, str):
        tickers = [tickers]
    
    if not isinstance(tickers, list) or len(tickers) == 0:
        print("Must be a non-empty list of tickers or symbols")
        return

    if "limit" in kwargs and kwargs["limit"] and len(tickers) > kwargs["limit"]:
        from itertools import islice            
        tickers = list(islice(tickers, kwargs["limit"]))
        print(f"[!] Too many assets to compare. Using the first {kwargs['limit']}: {', '.join(tickers)}")

    print(f"[i] Downloading: {', '.join(tickers)}")

    received = {}
    if len(tickers):
        _df = pd.DataFrame()
        for ticker in tickers:
            received[ticker] = _df.ta.ticker(ticker, **kwargs)
            print(f"[+] {ticker}{received[ticker].shape} {ta.get_time(full=False, to_string=True)}")
    
    if same_start and len(tickers) > 1:
        earliestci = earliest_common_index(received)
        print(f"[i] Earliest Common Date: {earliestci}")
        result = {ticker:df[df.index > earliestci].copy() for ticker,df in received.items()}
    else:
        result = received
    print(f"[*] Download Complete\n")
    return result

def dtmask(df: pd.DataFrame, start: datetime, end: datetime):
    return df.loc[(df.index >= start) & (df.index <= end), :].copy()

def show_data(d: dict):
    [print(f"{t}[{df.index[0]} - {df.index[-1]}]: {df.shape} {df.ta.time_range:.2f} years") for t,df in d.items()]
    
def trade_table(pf: vbt.portfolio.base.Portfolio, k: int = 1, total_fees: bool = False):
    if not isinstance(pf, vbt.portfolio.base.Portfolio): return
    k = int(k) if isinstance(k, int) and k > 0 else 1

    df = pf.trades.records[["status", "direction", "size", "entry_price", "exit_price", "return", "pnl", "entry_fees", "exit_fees"]]
    if total_fees:
        df["total_fees"] = df["entry_fees"] + df["exit_fees"]

    print(f"\nLast {k} of {df.shape[0]} Trades\n{df.tail(k)}\n")

Data Acquisition

Specify Symbols for Benchmarks and Assets

In [4]:
benchmark_tickers = ["SPY", "QQQ"]
asset_tickers = ["AAPL", "TSLA", "TWTR"]
all_tickers = benchmark_tickers + asset_tickers

print("Tickers by index #")
print("="*100)
print(f"Benchmarks: {', '.join([f'{k}: {v}' for k,v in enumerate(benchmark_tickers)])}")
print(f"    Assets: {', '.join([f'{k}: {v}' for k,v in enumerate(asset_tickers)])}")
print(f"       All: {', '.join([f'{k}: {v}' for k,v in enumerate(all_tickers)])}")
Tickers by index #
====================================================================================================
Benchmarks: 0: SPY, 1: QQQ
    Assets: 0: AAPL, 1: TSLA, 2: TWTR
       All: 0: SPY, 1: QQQ, 2: AAPL, 3: TSLA, 4: TWTR
In [5]:
benchmark = benchmark_tickers[0] # Change index for different benchmark
asset = asset_tickers[2] # Change index for different symbol
print(f"Selected Benchmark | Asset: {benchmark} | {asset}")
Selected Benchmark | Asset: SPY | TWTR
In [6]:
benchmarks = dl(benchmark_tickers, lc_cols=True)
[i] Downloading: SPY, QQQ
[+] SPY(7299, 7) Sunday January 23, 2022, NYSE: 11:30:14
[+] QQQ(5757, 7) Sunday January 23, 2022, NYSE: 11:30:17
[*] Download Complete

In [7]:
assets = dl(asset_tickers, lc_cols=True)
[i] Downloading: AAPL, TSLA, TWTR
[+] AAPL(10366, 7) Sunday January 23, 2022, NYSE: 11:30:21
[+] TSLA(2913, 7) Sunday January 23, 2022, NYSE: 11:30:23
[+] TWTR(2066, 7) Sunday January 23, 2022, NYSE: 11:30:26
[*] Download Complete

Define Testing Dates and Ranges

In [8]:
start_date = datetime(2005, 1, 1) # Adjust as needed
end_date = datetime(2010, 1, 1)   # Adjust as needed

Select and Benchmark and Asset to Backtest

In [9]:
print("Available Data:")
print("="*100)
print(f"Benchmarks: {', '.join(benchmarks.keys())}")
print(f"Assets: {', '.join(assets.keys())}")
Available Data:
====================================================================================================
Benchmarks: SPY, QQQ
Assets: AAPL, TSLA, TWTR
In [10]:
benchmark_name = "SPY" # Select a Benchmark
asset_name = "AAPL" # Select an Asset

benchmarkdf = benchmarks[benchmark_name]
assetdf     = assets[asset_name]

# Set True if you want to constrain Data between start_date & end_date
common_range = True
if common_range:
    crs = f" from {start_date} to {end_date}"
    benchmarkdf = dtmask(benchmarkdf, start_date, end_date)
    assetdf = dtmask(assetdf, start_date, end_date)

# Update DataFrame names
benchmarkdf.name = benchmark_name
assetdf.name = asset_name
print(f"Analysis of: {benchmarkdf.name} and {assetdf.name}{crs if common_range else ''}")
Analysis of: SPY and AAPL from 2005-01-01 00:00:00 to 2010-01-01 00:00:00

Sanity Check

In [11]:
benchmarkdf
Out [11]:
open high low close volume dividends stock splits
date
2005-01-03 87.175188 87.318618 85.984743 86.271599 55748000 0.0 0
2005-01-04 86.386354 86.443727 84.937739 85.217422 69167600 0.0 0
2005-01-05 85.152886 85.518628 84.622206 84.629379 65667300 0.0 0
2005-01-06 84.937742 85.446909 84.808657 85.059654 47814700 0.0 0
2005-01-07 85.317811 85.504268 84.715413 84.937729 55847700 0.0 0
... ... ... ... ... ... ... ...
2009-12-24 88.985016 89.310210 88.834313 89.215034 39677500 0.0 0
2009-12-28 89.548158 89.619539 89.088121 89.405388 87508500 0.0 0
2009-12-29 89.635392 89.651253 89.270538 89.278465 80572500 0.0 0
2009-12-30 89.016723 89.349850 88.969129 89.246735 73138400 0.0 0
2009-12-31 89.445047 89.468847 88.350484 88.390144 90637900 0.0 0

1259 rows × 7 columns

In [12]:
assetdf
Out [12]:
open high low close volume dividends stock splits
date
2005-01-03 0.991788 0.996840 0.958411 0.968976 691992000 0.0 0.0
2005-01-04 0.976631 1.002352 0.964076 0.978928 1096810400 0.0 0.0
2005-01-05 0.986888 0.998984 0.980611 0.987501 680433600 0.0 0.0
2005-01-06 0.990103 0.993778 0.969588 0.988266 705555200 0.0 0.0
2005-01-07 0.995156 1.066042 0.991329 1.060224 2227450400 0.0 0.0
... ... ... ... ... ... ... ...
2009-12-24 6.232738 6.410336 6.226614 6.400843 500889200 0.0 0.0
2009-12-28 6.482905 6.551187 6.418296 6.479537 644565600 0.0 0.0
2009-12-29 6.510768 6.513524 6.391349 6.402678 445205600 0.0 0.0
2009-12-30 6.394412 6.491478 6.378490 6.480454 412084400 0.0 0.0
2009-12-31 6.526080 6.532817 6.447386 6.452591 352410800 0.0 0.0

1259 rows × 7 columns

Creating Trading Signals for vectorbt

vectorbt can create a Backtest using vbt.Portfolio.from_signals(*args, **kwargs) based on trends can you define with Pandas TA.

Trend Creation

A Trend is the result of some calculation or condition of one or more indicators. For simplicity, a Trend is either True or 1 and No Trend is False or 0. Using the Hello World of Trends, the Golden/Death Cross, it's Trend is Long when long = ma(close, 50) > ma(close, 200) and Short when short = ma(close, 50) < ma(close, 200) .

In [13]:
# Example Long Trends for the selected Asset
# * Uncomment others for exploration or replace them with your own TA Trend Strategy
def trends(df: pd.DataFrame, mamode: str = "sma", fast: int = 50, slow: int = 200):
    return ta.ma(mamode, df.close, length=fast) > ta.ma(mamode, df.close, length=slow) # SMA(fast) > SMA(slow) "Golden/Death Cross"
#     return ta.increasing(ta.ma(mamode, df.close, length=fast)) # Increasing MA(fast)
#     return ta.macd(df.close, fast, slow).iloc[:,1] > 0 # MACD Histogram is positive
In [14]:
trend_kwargs = {"mamode": "sma", "fast": 50, "slow": 200}
In [15]:
benchmark_trends = trends(benchmarkdf, **trend_kwargs)
benchmark_trends.copy().astype(int).plot(figsize=(16, 1), kind="area", color=["green"], alpha=0.45, title=f"{benchmarkdf.name} Trends", grid=True).axhline(0, color="black")
Out [15]:
<matplotlib.lines.Line2D at 0x155b155e0>
In [16]:
asset_trends = trends(assetdf, **trend_kwargs)
asset_trends.copy().astype(int).plot(figsize=(16, 1), kind="area", color=["green"], alpha=0.45, title=f"{assetdf.name} Trends", grid=True).axhline(0, color="black")
Out [16]:
<matplotlib.lines.Line2D at 0x155a0d7c0>

Trend Signals

Given a Trend, Trend Signals returns the Trend, Trades, Entries and Exits as boolean integers. When asbool=True, it returns Trends, Entries and Exits as boolean values which is helpful when combined with the vectorbt backtesting package.

In [17]:
# trade_offset = 0 for Live Signals (close is last price)
# trade_offset = 1 for Backtesting
LIVE = 0

benchmark_signals = assetdf.ta.tsignals(benchmark_trends, asbool=True, trade_offset=LIVE, append=True)
benchmark_signals.tail()
Out [17]:
TS_Trends TS_Trades TS_Entries TS_Exits
date
2009-12-24 True 0 False False
2009-12-28 True 0 False False
2009-12-29 True 0 False False
2009-12-30 True 0 False False
2009-12-31 True 0 False False
In [18]:
asset_signals = assetdf.ta.tsignals(asset_trends, asbool=True, trade_offset=LIVE, append=True)
asset_signals.tail()
Out [18]:
TS_Trends TS_Trades TS_Entries TS_Exits
date
2009-12-24 True 0 False False
2009-12-28 True 0 False False
2009-12-29 True 0 False False
2009-12-30 True 0 False False
2009-12-31 True 0 False False
In [ ]:

Creating vectorbt Portfolios

Buy 'N Hold Portfolios with their Single Trade and Performance Statistics

In [19]:
# Benchmark Buy and Hold (BnH) Strategy
benchmarkpf_bnh = vbt.Portfolio.from_holding(benchmarkdf.close)
print(trade_table(benchmarkpf_bnh))
combine_stats(benchmarkpf_bnh, benchmarkdf.name, "Buy and Hold", LIVE)
Out [19]:
Last 1 of 1 Trades
   status  direction      size  entry_price  exit_price    return       pnl  \
0       0          0  1.153356    86.487278   88.390144  0.019502  1.945306   

   entry_fees  exit_fees  
0    0.249377        0.0  

None
Run Time                     Sunday January 23, 2022, NYSE: 11:30:30
Mode                                                            TEST
Strategy                                                Buy and Hold
Direction                                                   longonly
Symbol                                                           SPY
Fees [%]                                                        0.25
Slippage [%]                                                    0.25
Accumulate                                                     False
Start                                            2005-01-03 00:00:00
End                                              2009-12-31 00:00:00
Period                                            1259 days 00:00:00
Start Value                                                    100.0
End Value                                                 101.945306
Total Return [%]                                            1.945306
Benchmark Return [%]                                         2.45567
Max Gross Exposure [%]                                         100.0
Total Fees Paid                                             0.249377
Max Drawdown [%]                                           55.189424
Max Drawdown Duration                              562 days 00:00:00
Total Trades                                                       1
Total Closed Trades                                                0
Total Open Trades                                                  1
Open Trade PnL                                              1.945306
Sharpe Ratio                                                0.163326
Calmar Ratio                                                0.010149
Omega Ratio                                                 1.028442
Sortino Ratio                                               0.231223
Annualized Return [%]                                       0.560116
Annualized Volatility [%]                                  28.894483
Skew                                                        0.426799
Kurtosis                                                   14.956044
Tail Ratio                                                   0.88193
Common Sense Ratio                                           0.88687
Value at Risk                                               -0.02229
Alpha                                                      -0.001443
Beta                                                        1.000002
dtype: object
In [20]:
# Asset Buy and Hold (BnH) Strategy
assetpf_bnh = vbt.Portfolio.from_holding(assetdf.close)
print(trade_table(assetpf_bnh))
combine_stats(assetpf_bnh, assetdf.name, "Buy and Hold", LIVE)
Out [20]:
Last 1 of 1 Trades
   status  direction        size  entry_price  exit_price    return  \
0       0          0  102.687667     0.971398    6.452591  5.640081   

         pnl  entry_fees  exit_fees  
0  562.60156    0.249377        0.0  

None
Run Time                     Sunday January 23, 2022, NYSE: 11:30:31
Mode                                                            TEST
Strategy                                                Buy and Hold
Direction                                                   longonly
Symbol                                                          AAPL
Fees [%]                                                        0.25
Slippage [%]                                                    0.25
Accumulate                                                     False
Start                                            2005-01-03 00:00:00
End                                              2009-12-31 00:00:00
Period                                            1259 days 00:00:00
Start Value                                                    100.0
End Value                                                  662.60156
Total Return [%]                                           562.60156
Benchmark Return [%]                                      565.918709
Max Gross Exposure [%]                                         100.0
Total Fees Paid                                             0.249377
Max Drawdown [%]                                           60.866739
Max Drawdown Duration                              456 days 00:00:00
Total Trades                                                       1
Total Closed Trades                                                0
Total Open Trades                                                  1
Open Trade PnL                                             562.60156
Sharpe Ratio                                                1.329387
Calmar Ratio                                                1.199638
Omega Ratio                                                 1.209774
Sortino Ratio                                               1.982724
Annualized Return [%]                                      73.018068
Annualized Volatility [%]                                  51.088482
Skew                                                       -0.037601
Kurtosis                                                    3.435272
Tail Ratio                                                  1.037874
Common Sense Ratio                                          1.795709
Value at Risk                                              -0.041247
Alpha                                                       -0.00145
Beta                                                         1.00001
dtype: object

Signal Portfolios with their Last 'k' Trades and Performance Statistics

In [21]:
# Benchmark Portfolio from Trade Signals
benchmarkpf_signals = vbt.Portfolio.from_signals(
    benchmarkdf.close,
    entries=benchmark_signals.TS_Entries,
    exits=benchmark_signals.TS_Exits,
)
trade_table(benchmarkpf_signals, k=5)
combine_stats(benchmarkpf_signals, benchmarkdf.name, "Long Strategy", LIVE)
Out [21]:
Last 5 of 3 Trades
   status  direction      size  entry_price  exit_price    return        pnl  \
0       1          0  1.150759    86.682497   92.979310  0.067461   6.729243   
1       1          0  1.105106    96.337434  111.226859  0.149169  15.880943   
2       0          0  1.694384    72.182249   88.390144  0.222041  27.156633   

   entry_fees  exit_fees  
0    0.249377   0.267492  
1    0.266158   0.307294  
2    0.305761   0.000000  

Run Time                      Sunday January 23, 2022, NYSE: 11:30:34
Mode                                                             TEST
Strategy                                                Long Strategy
Direction                                                    longonly
Symbol                                                            SPY
Fees [%]                                                         0.25
Slippage [%]                                                     0.25
Accumulate                                                      False
Start                                             2005-01-03 00:00:00
End                                               2009-12-31 00:00:00
Period                                             1259 days 00:00:00
Start Value                                                     100.0
End Value                                                  149.766819
Total Return [%]                                            49.766819
Benchmark Return [%]                                          2.45567
Max Gross Exposure [%]                                          100.0
Total Fees Paid                                              1.396081
Max Drawdown [%]                                            10.131737
Max Drawdown Duration                               450 days 00:00:00
Total Trades                                                        3
Total Closed Trades                                                 2
Total Open Trades                                                   1
Open Trade PnL                                              27.156633
Win Rate [%]                                                    100.0
Best Trade [%]                                              14.916854
Worst Trade [%]                                              6.746066
Avg Winning Trade [%]                                        10.83146
Avg Winning Trade Duration                          264 days 00:00:00
Profit Factor                                                     inf
Expectancy                                                  11.305093
Sharpe Ratio                                                 1.009582
Calmar Ratio                                                 1.226148
Omega Ratio                                                  1.224227
Sortino Ratio                                                1.431912
Annualized Return [%]                                       12.423008
Annualized Volatility [%]                                   12.357481
Skew                                                        -0.412948
Kurtosis                                                     6.272119
Tail Ratio                                                   1.032283
Common Sense Ratio                                           1.160524
Value at Risk                                               -0.010296
Alpha                                                        0.122837
Beta                                                         0.182611
dtype: object
In [22]:
# Asset Portfolio from Trade Signals
assetpf_signals = vbt.Portfolio.from_signals(
    assetdf.close,
    entries=asset_signals.TS_Entries,
    exits=asset_signals.TS_Exits,
)
trade_table(assetpf_signals, k=5)
combine_stats(assetpf_signals, assetdf.name, "Long Strategy", LIVE)
Out [22]:
Last 5 of 4 Trades
   status  direction       size  entry_price  exit_price    return        pnl  \
0       1          0  60.807500     1.640433    1.767254  0.072116   7.193656   
1       1          0  45.984332     2.325278    3.693641  0.582002  62.231344   
2       1          0  29.344173     5.759320    3.874155 -0.331506 -56.025321   
3       0          0  30.839253     3.667952    6.452591  0.756681  85.593416   

   entry_fees  exit_fees  
0    0.249377   0.268656  
1    0.267316   0.424624  
2    0.422506   0.284210  
3    0.282792   0.000000  

Run Time                      Sunday January 23, 2022, NYSE: 11:30:34
Mode                                                             TEST
Strategy                                                Long Strategy
Direction                                                    longonly
Symbol                                                           AAPL
Fees [%]                                                         0.25
Slippage [%]                                                     0.25
Accumulate                                                      False
Start                                             2005-01-03 00:00:00
End                                               2009-12-31 00:00:00
Period                                             1259 days 00:00:00
Start Value                                                     100.0
End Value                                                  198.993096
Total Return [%]                                            98.993096
Benchmark Return [%]                                       565.918709
Max Gross Exposure [%]                                          100.0
Total Fees Paid                                               2.19948
Max Drawdown [%]                                            59.898125
Max Drawdown Duration                               506 days 00:00:00
Total Trades                                                        4
Total Closed Trades                                                 3
Total Open Trades                                                   1
Open Trade PnL                                              85.593416
Win Rate [%]                                                66.666667
Best Trade [%]                                                58.2002
Worst Trade [%]                                            -33.150588
Avg Winning Trade [%]                                        32.70592
Avg Losing Trade [%]                                       -33.150588
Avg Winning Trade Duration                          266 days 12:00:00
Avg Losing Trade Duration                            89 days 00:00:00
Profit Factor                                                1.239172
Expectancy                                                    4.46656
Sharpe Ratio                                                 0.739016
Calmar Ratio                                                  0.36859
Omega Ratio                                                  1.140899
Sortino Ratio                                                 1.08289
Annualized Return [%]                                       22.077862
Annualized Volatility [%]                                   35.544005
Skew                                                         0.024376
Kurtosis                                                     4.672283
Tail Ratio                                                   1.095488
Common Sense Ratio                                           1.337348
Value at Risk                                               -0.029216
Alpha                                                       -0.063653
Beta                                                         0.482566
dtype: object
In [ ]:
In [ ]:
In [ ]:

Buy and Hold Plots

In [23]:
vbt.settings.set_theme("seaborn")

Benchmark

In [24]:
benchmarkpf_bnh.trades.plot(title=f"{benchmarkdf.name} | Trades", height=cheight, width=cwidth).show_png()
In [25]:
benchmarkpf_bnh.value().vbt.plot(title=f"{benchmarkdf.name} | Equity Curve", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [26]:
benchmarkpf_bnh.drawdown().vbt.plot(title=f"{benchmarkdf.name} | Drawdown", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [27]:
benchmarkpf_bnh.trades.plot_pnl(title=f"{benchmarkdf.name} | PnL", height=cheight // 2, width=cwidth).show_png()
In [28]:
benchmarkpf_bnh.asset_returns().vbt.plot(title=f"{benchmarkdf.name} | Asset Returns", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [29]:
benchmarkpf_bnh.cash().vbt.plot(title=f"{benchmarkdf.name} | Cash", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [30]:
total_assetfees = benchmarkpf_bnh.trades.records_readable["Entry Fees"] + benchmarkpf_bnh.trades.records_readable["Exit Fees"]
total_assetfees.vbt.plot(title=f"{benchmarkdf.name} | Total Fees", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [ ]:

Asset

In [31]:
assetpf_bnh.trades.plot(title=f"{assetdf.name} | Trades", height=cheight, width=cwidth).show_png()
In [32]:
assetpf_bnh.value().vbt.plot(title=f"{assetdf.name} | Equity Curve", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [33]:
assetpf_bnh.drawdown().vbt.plot(title=f"{assetdf.name} | Drawdown", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [34]:
assetpf_bnh.trades.plot_pnl(title=f"{assetdf.name} | PnL", height=cheight // 2, width=cwidth).show_png()
In [35]:
assetpf_bnh.asset_returns().vbt.plot(title=f"{assetdf.name} | Asset Returns", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [36]:
assetpf_bnh.cash().vbt.plot(title=f"{assetdf.name} | Cash", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [37]:
total_assetfees = assetpf_bnh.trades.records_readable["Entry Fees"] + assetpf_bnh.trades.records_readable["Exit Fees"]
total_assetfees.vbt.plot(title=f"{assetdf.name} | Total Fees", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [ ]:
In [ ]:

Signal Plots

In [38]:
vbt.settings.set_theme("dark")

Benchmark

In [39]:
benchmarkpf_signals.trades.plot(title=f"{benchmarkdf.name} | Trades", height=cheight, width=cwidth).show_png()
In [40]:
benchmarkpf_signals.value().vbt.plot(title=f"{benchmarkdf.name} | Equity Curve", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [41]:
benchmarkpf_signals.drawdown().vbt.plot(title=f"{benchmarkdf.name} | Drawdown", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [42]:
benchmarkpf_signals.trades.plot_pnl(title=f"{benchmarkdf.name} | PnL", height=cheight // 2, width=cwidth).show_png()
In [43]:
benchmarkpf_signals.asset_returns().vbt.plot(title=f"{benchmarkdf.name} | Asset Returns", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [44]:
benchmarkpf_signals.cash().vbt.plot(title=f"{benchmarkdf.name} | Cash", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [45]:
total_assetfees = benchmarkpf_signals.trades.records_readable["Entry Fees"] + benchmarkpf_signals.trades.records_readable["Exit Fees"]
total_assetfees.vbt.plot(title=f"{benchmarkdf.name} | Total Fees", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [ ]:

Asset

In [46]:
assetpf_signals.trades.plot(title=f"{assetdf.name} | Trades", height=cheight, width=cwidth).show_png()
In [47]:
assetpf_signals.value().vbt.plot(title=f"{assetdf.name} | Equity Curve", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [48]:
assetpf_signals.drawdown().vbt.plot(title=f"{assetdf.name} | Drawdown", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [49]:
assetpf_signals.trades.plot_pnl(title=f"{assetdf.name} | PnL", height=cheight // 2, width=cwidth).show_png()
In [50]:
assetpf_signals.asset_returns().vbt.plot(title=f"{assetdf.name} | Asset Returns", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [51]:
assetpf_signals.cash().vbt.plot(title=f"{assetdf.name} | Cash", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [52]:
total_assetfees = assetpf_signals.trades.records_readable["Entry Fees"] + assetpf_signals.trades.records_readable["Exit Fees"]
total_assetfees.vbt.plot(title=f"{assetdf.name} | Total Fees", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [ ]:

Disclaimer

  • All investments involve risk, and the past performance of a security, industry, sector, market, financial product, trading strategy, or individuals trading does not guarantee future results or returns. Investors are fully responsible for any investment decisions they make. Such decisions should be based solely on an evaluation of their financial circumstances, investment objectives, risk tolerance, and liquidity needs.

  • Any opinions, news, research, analyses, prices, or other information offered is provided as general market commentary, and does not constitute investment advice. I will not accept liability for any loss or damage, including without limitation any loss of profit, which may arise directly or indirectly from use of or reliance on such information.