Files
pandas-ta/examples/VectorBT_Backtest_with_Pandas_TA.ipynb
T

1.4 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.54b0
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_000
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
vbt.settings.portfolio["signal_direction"] = "both"

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 100000
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 both
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"[+] {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, timed=True)
[i] Downloading: SPY, QQQ
[+] yf | SPY(7343, 7): 3510.6075 ms (3.5106 s)
[+] Friday March 25, 2022, NYSE: 4:33:29
[+] yf | QQQ(5801, 7): 2978.0290 ms (2.9780 s)
[+] Friday March 25, 2022, NYSE: 4:33:32
[*] Download Complete

In [7]:
assets = dl(asset_tickers, timed=True)
[i] Downloading: AAPL, TSLA, TWTR
[+] yf | AAPL(10410, 7): 3208.4925 ms (3.2085 s)
[+] Friday March 25, 2022, NYSE: 4:33:36
[+] yf | TSLA(2957, 7): 2836.7323 ms (2.8367 s)
[+] Friday March 25, 2022, NYSE: 4:33:38
[+] yf | TWTR(2110, 7): 2735.9980 ms (2.7360 s)
[+] Friday March 25, 2022, NYSE: 4:33:41
[*] 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 86.905246 87.048233 85.718488 86.004456 55748000 0.0 0
2005-01-04 86.118826 86.176020 84.674697 84.953514 69167600 0.0 0
2005-01-05 84.889165 85.253774 84.360128 84.367279 65667300 0.0 0
2005-01-06 84.674684 85.182274 84.545999 84.796219 47814700 0.0 0
2005-01-07 85.053626 85.239506 84.453093 84.674721 55847700 0.0 0
... ... ... ... ... ... ... ...
2009-12-24 88.709423 89.033609 88.559186 88.938728 39677500 0.0 0
2009-12-28 89.270837 89.341998 88.812225 89.128510 87508500 0.0 0
2009-12-29 89.357798 89.373609 88.994073 89.001976 80572500 0.0 0
2009-12-30 88.741037 89.073133 88.693591 88.970337 73138400 0.0 0
2009-12-31 89.168030 89.191756 88.076856 88.116394 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.990526 0.995572 0.957193 0.967744 691992000 0.0 0.0
2005-01-04 0.975388 1.001076 0.962850 0.977682 1096810400 0.0 0.0
2005-01-05 0.985632 0.997713 0.979364 0.986245 680433600 0.0 0.0
2005-01-06 0.988843 0.992513 0.968354 0.987009 705555200 0.0 0.0
2005-01-07 0.993890 1.064686 0.990067 1.058875 2227450400 0.0 0.0
... ... ... ... ... ... ... ...
2009-12-24 6.224809 6.402181 6.218693 6.392700 500889200 0.0 0.0
2009-12-28 6.474657 6.542852 6.410130 6.471292 644565600 0.0 0.0
2009-12-29 6.502488 6.505240 6.383221 6.394536 445205600 0.0 0.0
2009-12-30 6.386277 6.483220 6.370375 6.472210 412084400 0.0 0.0
2009-12-31 6.517778 6.524506 6.439184 6.444382 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 that you create 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": "ema", "fast": 20, "slow": 50}
In [15]:
benchmark_trends = trends(benchmarkdf, **trend_kwargs)
benchmark_trends.copy().astype(int).plot(figsize=(16, 1), kind="area", color=["limegreen"], alpha=0.9, title=f"{benchmarkdf.name} Trends", grid=True).axhline(0, color="black")
Out [15]:
<matplotlib.lines.Line2D at 0x157decf40>
In [16]:
asset_trends = trends(assetdf, **trend_kwargs)
asset_trends.copy().astype(int).plot(figsize=(16, 1), kind="area", color=["limegreen"], alpha=0.98, title=f"{assetdf.name} Trends", grid=True).axhline(0, color="black")
Out [16]:
<matplotlib.lines.Line2D at 0x158255ee0>

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  \
0       0          0  1156.938534    86.219467   88.116394  0.019501   

           pnl  entry_fees  exit_fees  
0  1945.251775  249.376559        0.0  

None
Run Time                     Friday March 25, 2022, NYSE: 4:33:45
Mode                                                         TEST
Strategy                                             Buy and Hold
Direction                                                    both
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                                              100000.0
End Value                                           101945.251775
Total Return [%]                                         1.945252
Benchmark Return [%]                                     2.455615
Max Gross Exposure [%]                                      100.0
Total Fees Paid                                        249.376559
Max Drawdown [%]                                        55.189449
Max Drawdown Duration                           562 days 00:00:00
Total Trades                                                    1
Total Closed Trades                                             0
Total Open Trades                                               1
Open Trade PnL                                        1945.251775
Sharpe Ratio                                             0.163325
Calmar Ratio                                             0.010149
Omega Ratio                                              1.028442
Sortino Ratio                                            0.231222
Annualized Return [%]                                    0.560101
Annualized Volatility [%]                               28.894507
Skew                                                     0.426795
Kurtosis                                                14.955978
Tail Ratio                                               0.881912
Common Sense Ratio                                       0.886852
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  102818.423476     0.970163    6.444382  5.640077   

             pnl  entry_fees  exit_fees  
0  562601.217125  249.376559        0.0  

None
Run Time                     Friday March 25, 2022, NYSE: 4:33:46
Mode                                                         TEST
Strategy                                             Buy and Hold
Direction                                                    both
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                                              100000.0
End Value                                           662601.217125
Total Return [%]                                       562.601217
Benchmark Return [%]                                   565.918364
Max Gross Exposure [%]                                      100.0
Total Fees Paid                                        249.376559
Max Drawdown [%]                                         60.86673
Max Drawdown Duration                           456 days 00:00:00
Total Trades                                                    1
Total Closed Trades                                             0
Total Open Trades                                               1
Open Trade PnL                                      562601.217125
Sharpe Ratio                                             1.329387
Calmar Ratio                                             1.199638
Omega Ratio                                              1.209774
Sortino Ratio                                            1.982726
Annualized Return [%]                                   73.018042
Annualized Volatility [%]                               51.088429
Skew                                                    -0.037597
Kurtosis                                                 3.435264
Tail Ratio                                               1.037885
Common Sense Ratio                                       1.795729
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 15 Trades
    status  direction         size  entry_price  exit_price    return  \
10       1          0   797.772784   114.865412  108.679654 -0.058718   
11       1          1   793.793867   108.679654  105.284153  0.026321   
12       1          0   840.908131   105.284153  101.710419 -0.038859   
13       1          1   836.714076   101.710419   63.710985  0.369538   
14       0          0  1828.142319    63.710985   88.116394  0.380564   

             pnl  entry_fees   exit_fees  
10  -5380.674590  229.091248  216.754175  
11   2270.720055  215.673107  208.934787  
12  -3440.340619  221.335750  213.822795  
13  31448.634826  212.756347  133.269695  
14  44325.378834  291.181871    0.000000  

Run Time                      Friday March 25, 2022, NYSE: 4:33:49
Mode                                                          TEST
Strategy                                             Long Strategy
Direction                                                     both
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                                               100000.0
End Value                                            161089.308979
Total Return [%]                                         61.089309
Benchmark Return [%]                                      2.455615
Max Gross Exposure [%]                                       100.0
Total Fees Paid                                        6786.609621
Max Drawdown [%]                                         21.642405
Max Drawdown Duration                            411 days 00:00:00
Total Trades                                                    15
Total Closed Trades                                             14
Total Open Trades                                                1
Open Trade PnL                                        44325.378834
Win Rate [%]                                             35.714286
Best Trade [%]                                           36.953815
Worst Trade [%]                                          -5.871759
Avg Winning Trade [%]                                    10.694648
Avg Losing Trade [%]                                     -3.476746
Avg Winning Trade Duration                       136 days 14:24:00
Avg Losing Trade Duration                         37 days 18:40:00
Profit Factor                                             1.564796
Expectancy                                             1197.423582
Sharpe Ratio                                              0.762857
Calmar Ratio                                              0.684934
Omega Ratio                                               1.124146
Sortino Ratio                                             1.111308
Annualized Return [%]                                    14.823626
Annualized Volatility [%]                                21.015085
Skew                                                      0.013953
Kurtosis                                                  4.710095
Tail Ratio                                                1.023207
Common Sense Ratio                                        1.174883
Value at Risk                                            -0.017307
Alpha                                                     0.191161
Beta                                                     -0.301323
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 19 Trades
    status  direction          size  entry_price  exit_price    return  \
14       1          0  29355.575610     5.320026    4.917980 -0.080383   
15       1          1  29209.163761     4.917980    3.043387  0.377124   
16       1          0  64956.862336     3.043387    2.652390 -0.133153   
17       1          1  64632.887961     2.652390    3.112368 -0.178854   
18       0          0  45253.957674     3.112368    6.444382  1.068072   

              pnl  entry_fees   exit_fees  
14  -12553.649891  390.431047  360.925314  
15   54173.913870  359.125187  222.236997  
16  -26322.878669  494.222229  430.727407  
17  -30661.152430  428.579140  502.903315  
18  150434.715996  352.117414    0.000000  

Run Time                      Friday March 25, 2022, NYSE: 4:33:50
Mode                                                          TEST
Strategy                                             Long Strategy
Direction                                                     both
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                                               100000.0
End Value                                            291633.798892
Total Return [%]                                        191.633799
Benchmark Return [%]                                    565.918364
Max Gross Exposure [%]                                       100.0
Total Fees Paid                                       10631.095277
Max Drawdown [%]                                          46.10598
Max Drawdown Duration                            368 days 00:00:00
Total Trades                                                    19
Total Closed Trades                                             18
Total Open Trades                                                1
Open Trade PnL                                       150434.715996
Win Rate [%]                                             33.333333
Best Trade [%]                                           78.771745
Worst Trade [%]                                          -17.88535
Avg Winning Trade [%]                                    34.908001
Avg Losing Trade [%]                                    -10.451626
Avg Winning Trade Duration                       124 days 12:00:00
Avg Losing Trade Duration                         21 days 20:00:00
Profit Factor                                             1.281501
Expectancy                                             2288.837939
Sharpe Ratio                                              0.926133
Calmar Ratio                                              0.789131
Omega Ratio                                               1.141239
Sortino Ratio                                             1.339789
Annualized Return [%]                                    36.383667
Annualized Volatility [%]                                43.942033
Skew                                                     -0.036485
Kurtosis                                                  1.988366
Tail Ratio                                                0.968342
Common Sense Ratio                                         1.32066
Value at Risk                                            -0.038211
Alpha                                                     0.353839
Beta                                                      0.152645
dtype: object
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()

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.