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 [53]:
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 [53]:
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 [54]:
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, timed=True)
[i] Downloading: SPY, QQQ
[+] yf | SPY(7310, 7): 6015.8232 ms (6.0158 s)
[+] SPY(7310, 7) Monday February 7, 2022, NYSE: 5:58:00
[+] yf | QQQ(5768, 7): 4612.5752 ms (4.6126 s)
[+] QQQ(5768, 7) Monday February 7, 2022, NYSE: 5:58:04
[*] Download Complete

In [7]:
assets = dl(asset_tickers, timed=True)
[i] Downloading: AAPL, TSLA, TWTR
[+] yf | AAPL(10377, 7): 4302.5109 ms (4.3025 s)
[+] AAPL(10377, 7) Monday February 7, 2022, NYSE: 5:58:09
[+] yf | TSLA(2924, 7): 4379.0780 ms (4.3791 s)
[+] TSLA(2924, 7) Monday February 7, 2022, NYSE: 5:58:13
[+] yf | TWTR(2077, 7): 4289.1730 ms (4.2892 s)
[+] TWTR(2077, 7) Monday February 7, 2022, NYSE: 5:58:17
[*] Download Complete

Define Testing Dates and Ranges

In [55]:
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 [56]:
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 [57]:
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 [58]:
benchmarkdf
Out [58]:
Open High Low Close Volume Dividends Stock Splits
Date
2005-01-03 87.761711 87.906107 86.563257 86.852043 55748000 0.0 0
2005-01-04 86.967560 87.025318 85.509199 85.790764 69167600 0.0 0
2005-01-05 85.725790 86.093993 85.191540 85.198761 65667300 0.0 0
2005-01-06 85.509188 86.021780 85.379234 85.631920 47814700 0.0 0
2005-01-07 85.891825 86.079536 85.285374 85.509186 55847700 0.0 0
... ... ... ... ... ... ... ...
2009-12-24 89.583749 89.911131 89.432032 89.815315 39677500 0.0 0
2009-12-28 90.150658 90.222520 89.687526 90.006927 87508500 0.0 0
2009-12-29 90.238441 90.254408 89.871132 89.879112 80572500 0.0 0
2009-12-30 89.615662 89.951030 89.567748 89.847221 73138400 0.0 0
2009-12-31 90.046848 90.070809 88.944921 88.984848 90637900 0.0 0

1259 rows × 7 columns

In [59]:
assetdf
Out [59]:
Open High Low Close Volume Dividends Stock Splits
Date
2005-01-03 0.990526 0.995572 0.957192 0.967743 691992000 0.0 0.0
2005-01-04 0.975388 1.001076 0.962849 0.977682 1096810400 0.0 0.0
2005-01-05 0.985632 0.997712 0.979363 0.986244 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.993889 1.064685 0.990067 1.058875 2227450400 0.0 0.0
... ... ... ... ... ... ... ...
2009-12-24 6.224808 6.402179 6.218691 6.392698 500889200 0.0 0.0
2009-12-28 6.474655 6.542851 6.410128 6.471291 644565600 0.0 0.0
2009-12-29 6.502485 6.505238 6.383218 6.394533 445205600 0.0 0.0
2009-12-30 6.386275 6.483218 6.370373 6.472208 412084400 0.0 0.0
2009-12-31 6.517775 6.524503 6.439181 6.444380 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 [60]:
# 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 [61]:
trend_kwargs = {"mamode": "sma", "fast": 50, "slow": 200}
In [62]:
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 [62]:
<matplotlib.lines.Line2D at 0x157858640>
In [63]:
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 [63]:
<matplotlib.lines.Line2D at 0x15797e220>

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 [64]:
# 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 [64]:
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 [65]:
asset_signals = assetdf.ta.tsignals(asset_trends, asbool=True, trade_offset=LIVE, append=True)
asset_signals.tail()
Out [65]:
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 [66]:
# 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 [66]:
Last 1 of 1 Trades
   status  direction         size  entry_price  exit_price    return  \
0       0          0  1145.647991    87.069173   88.984848  0.019502   

           pnl  entry_fees  exit_fees  
0  1945.312385  249.376559        0.0  

None
Run Time                     Monday February 7, 2022, NYSE: 6:05:12
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.312385
Total Return [%]                                           1.945312
Benchmark Return [%]                                       2.455676
Max Gross Exposure [%]                                        100.0
Total Fees Paid                                          249.376559
Max Drawdown [%]                                          55.189433
Max Drawdown Duration                             562 days 00:00:00
Total Trades                                                      1
Total Closed Trades                                               0
Total Open Trades                                                 1
Open Trade PnL                                          1945.312385
Sharpe Ratio                                               0.163326
Calmar Ratio                                               0.010149
Omega Ratio                                                1.028442
Sortino Ratio                                              0.231223
Annualized Return [%]                                      0.560118
Annualized Volatility [%]                                 28.894487
Skew                                                       0.426802
Kurtosis                                                  14.956002
Tail Ratio                                                 0.881919
Common Sense Ratio                                         0.886859
Value at Risk                                              -0.02229
Alpha                                                     -0.001443
Beta                                                       1.000002
dtype: object
In [67]:
# 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 [67]:
Last 1 of 1 Trades
   status  direction           size  entry_price  exit_price    return  \
0       0          0  102818.436141     0.970163     6.44438  5.640076   

             pnl  entry_fees  exit_fees  
0  562601.053608  249.376559        0.0  

None
Run Time                     Monday February 7, 2022, NYSE: 6:05:13
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.053608
Total Return [%]                                         562.601054
Benchmark Return [%]                                       565.9182
Max Gross Exposure [%]                                        100.0
Total Fees Paid                                          249.376559
Max Drawdown [%]                                          60.866743
Max Drawdown Duration                             456 days 00:00:00
Total Trades                                                      1
Total Closed Trades                                               0
Total Open Trades                                                 1
Open Trade PnL                                        562601.053608
Sharpe Ratio                                               1.329386
Calmar Ratio                                               1.199638
Omega Ratio                                                1.209774
Sortino Ratio                                              1.982724
Annualized Return [%]                                      73.01803
Annualized Volatility [%]                                 51.088476
Skew                                                      -0.037597
Kurtosis                                                   3.435256
Tail Ratio                                                 1.037879
Common Sense Ratio                                         1.795718
Value at Risk                                             -0.041247
Alpha                                                      -0.00145
Beta                                                        1.00001
dtype: object

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

In [68]:
# 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 [68]:
Last 5 of 5 Trades
   status  direction         size  entry_price  exit_price    return  \
0       1          0  1143.067726    87.265716   93.604879  0.067460   
1       1          1  1137.366640    93.604879   96.985603 -0.041207   
2       1          0  1052.599067    96.985603  111.975205  0.149169   
3       1          1  1047.349196   111.975205   72.667880  0.346914   
4       0          0  2172.358822    72.667880   88.984848  0.222042   

            pnl  entry_fees   exit_fees  
0   6729.224173  249.376559  267.491790  
1  -4387.051303  266.157666  275.770474  
2  15228.160409  255.217389  294.662489  
3  40685.029757  293.192851  190.271616  
4  35051.656621  394.651778    0.000000  

Run Time                      Monday February 7, 2022, NYSE: 6:05:16
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                                              193307.019656
Total Return [%]                                            93.30702
Benchmark Return [%]                                        2.455676
Max Gross Exposure [%]                                         100.0
Total Fees Paid                                          2486.792612
Max Drawdown [%]                                            15.56369
Max Drawdown Duration                              152 days 00:00:00
Total Trades                                                       5
Total Closed Trades                                                4
Total Open Trades                                                  1
Open Trade PnL                                          35051.656621
Win Rate [%]                                                    75.0
Best Trade [%]                                             34.691356
Worst Trade [%]                                            -4.120726
Avg Winning Trade [%]                                      18.784752
Avg Losing Trade [%]                                       -4.120726
Avg Winning Trade Duration                         299 days 08:00:00
Avg Losing Trade Duration                           25 days 00:00:00
Profit Factor                                              14.278934
Expectancy                                              14563.840759
Sharpe Ratio                                                1.128019
Calmar Ratio                                                1.352901
Omega Ratio                                                 1.199806
Sortino Ratio                                               1.653528
Annualized Return [%]                                      21.056134
Annualized Volatility [%]                                  18.452631
Skew                                                       -0.067306
Kurtosis                                                     4.86452
Tail Ratio                                                  1.074084
Common Sense Ratio                                          1.300244
Value at Risk                                              -0.015183
Alpha                                                       0.244574
Beta                                                       -0.220209
dtype: object
In [69]:
# 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 [69]:
Last 5 of 7 Trades
   status  direction          size  entry_price  exit_price    return  \
2       1          0  31274.951777     2.322318    3.688941  0.582002   
3       1          1  31118.966980     3.688941    5.751994 -0.565651   
4       1          0   8696.711793     5.751994    3.869225 -0.331506   
5       1          1   8653.336672     3.869225    3.663285  0.048358   
6       0          0   9580.683873     3.663285    6.444380  0.756681   

            pnl  entry_fees   exit_fees  
2  42271.062627  181.575975  288.428643  
3 -64934.539698  286.990096  447.490244  
4 -16583.080760  125.058576   84.123830  
5   1619.110776   83.704260   79.249099  
6  26557.046830   87.741942    0.000000  

Run Time                      Monday February 7, 2022, NYSE: 6:05:16
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                                               61741.565685
Total Return [%]                                          -38.258434
Benchmark Return [%]                                        565.9182
Max Gross Exposure [%]                                         100.0
Total Fees Paid                                          2801.433663
Max Drawdown [%]                                           83.338227
Max Drawdown Duration                              506 days 00:00:00
Total Trades                                                       7
Total Closed Trades                                                6
Total Open Trades                                                  1
Open Trade PnL                                           26557.04683
Win Rate [%]                                                    50.0
Best Trade [%]                                             58.200242
Worst Trade [%]                                            -56.56514
Avg Winning Trade [%]                                      23.415908
Avg Losing Trade [%]                                      -40.623448
Avg Winning Trade Duration                         231 days 00:00:00
Avg Losing Trade Duration                           68 days 08:00:00
Profit Factor                                               0.440761
Expectancy                                             -10802.580191
Sharpe Ratio                                               -0.071065
Calmar Ratio                                               -0.156552
Omega Ratio                                                 0.988973
Sortino Ratio                                              -0.099881
Annualized Return [%]                                     -13.046751
Annualized Volatility [%]                                  46.262996
Skew                                                        0.150873
Kurtosis                                                    4.766433
Tail Ratio                                                  0.945783
Common Sense Ratio                                          0.822389
Value at Risk                                              -0.039647
Alpha                                                      -0.137603
Beta                                                        0.169161
dtype: object
In [ ]:
In [ ]:

Buy and Hold Plots

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

Benchmark

In [71]:
benchmarkpf_bnh.trades.plot(title=f"{benchmarkdf.name} | Trades", height=cheight, width=cwidth).show_png()
In [72]:
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 [73]:
benchmarkpf_bnh.drawdown().vbt.plot(title=f"{benchmarkdf.name} | Drawdown", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [74]:
benchmarkpf_bnh.trades.plot_pnl(title=f"{benchmarkdf.name} | PnL", height=cheight // 2, width=cwidth).show_png()
In [75]:
benchmarkpf_bnh.asset_returns().vbt.plot(title=f"{benchmarkdf.name} | Asset Returns", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [76]:
benchmarkpf_bnh.cash().vbt.plot(title=f"{benchmarkdf.name} | Cash", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [77]:
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 [78]:
assetpf_bnh.trades.plot(title=f"{assetdf.name} | Trades", height=cheight, width=cwidth).show_png()
In [79]:
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 [80]:
assetpf_bnh.drawdown().vbt.plot(title=f"{assetdf.name} | Drawdown", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [81]:
assetpf_bnh.trades.plot_pnl(title=f"{assetdf.name} | PnL", height=cheight // 2, width=cwidth).show_png()
In [82]:
assetpf_bnh.asset_returns().vbt.plot(title=f"{assetdf.name} | Asset Returns", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [83]:
assetpf_bnh.cash().vbt.plot(title=f"{assetdf.name} | Cash", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [84]:
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 [85]:
vbt.settings.set_theme("dark")

Benchmark

In [86]:
benchmarkpf_signals.trades.plot(title=f"{benchmarkdf.name} | Trades", height=cheight, width=cwidth).show_png()
In [87]:
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 [88]:
benchmarkpf_signals.drawdown().vbt.plot(title=f"{benchmarkdf.name} | Drawdown", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [89]:
benchmarkpf_signals.trades.plot_pnl(title=f"{benchmarkdf.name} | PnL", height=cheight // 2, width=cwidth).show_png()
In [90]:
benchmarkpf_signals.asset_returns().vbt.plot(title=f"{benchmarkdf.name} | Asset Returns", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [91]:
benchmarkpf_signals.cash().vbt.plot(title=f"{benchmarkdf.name} | Cash", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [92]:
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 [93]:
assetpf_signals.trades.plot(title=f"{assetdf.name} | Trades", height=cheight, width=cwidth).show_png()
In [94]:
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 [95]:
assetpf_signals.drawdown().vbt.plot(title=f"{assetdf.name} | Drawdown", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [96]:
assetpf_signals.trades.plot_pnl(title=f"{assetdf.name} | PnL", height=cheight // 2, width=cwidth).show_png()
In [97]:
assetpf_signals.asset_returns().vbt.plot(title=f"{assetdf.name} | Asset Returns", trace_kwargs=dict(name="%"), height=cheight // 2, width=cwidth).show_png()
In [98]:
assetpf_signals.cash().vbt.plot(title=f"{assetdf.name} | Cash", trace_kwargs=dict(name=u"\u00A4"), height=cheight // 2, width=cwidth).show_png()
In [99]:
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.