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 >= v0.18.1")
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.2
Pandas v1.2.4
vectorbt >= v0.18.1

Pandas TA v0.2.89b0
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 shares
signal_size_type shares
fees 0.0025
fixed_fees 0.0
slippage 0.0025
reject_prob 0.0
min_size 0.0
max_size inf
allow_partial False
raise_reject False
close_first False
accumulate False
log False
conflict_mode ignore
signal_direction longonly
order_direction all
cash_sharing False
row_wise False
use_numba True
seed None
freq 1D
incl_unrealized False
use_filled_close True
subplots [orders, trade_returns, cum_returns]

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(7149, 7) Saturday June 19, 2021, NYSE: 7:58:24
[+] QQQ(5607, 7) Saturday June 19, 2021, NYSE: 7:58:27
[*] Download Complete

In [7]:
assets = dl(asset_tickers, lc_cols=True)
[i] Downloading: AAPL, TSLA, TWTR
[+] AAPL(10216, 7) Saturday June 19, 2021, NYSE: 7:58:30
[+] TSLA(2763, 7) Saturday June 19, 2021, NYSE: 7:58:32
[+] TWTR(1916, 7) Saturday June 19, 2021, NYSE: 7:58:34
[*] 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 88.048860 88.193728 86.846484 87.136215 55748000 0.0 0
2005-01-04 87.252088 87.310035 85.788956 86.071442 69167600 0.0 0
2005-01-05 86.006247 86.375654 85.470248 85.477493 65667300 0.0 0
2005-01-06 85.788945 86.303215 85.658567 85.912079 47814700 0.0 0
2005-01-07 86.172847 86.361172 85.564411 85.788956 55847700 0.0 0
... ... ... ... ... ... ... ...
2009-12-24 89.876815 90.205268 89.724602 90.109138 39677500 0.0 0
2009-12-28 90.445592 90.517689 89.980945 90.301392 87508500 0.0 0
2009-12-29 90.533684 90.549703 90.165173 90.173180 80572500 0.0 0
2009-12-30 89.908834 90.245300 89.860763 90.141151 73138400 0.0 0
2009-12-31 90.341438 90.365476 89.235905 89.275963 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.994723 0.999791 0.961248 0.971844 691992000 0.0 0.0
2005-01-04 0.979521 1.005318 0.966930 0.981825 1096810400 0.0 0.0
2005-01-05 0.989809 1.001940 0.983514 0.990424 680433600 0.0 0.0
2005-01-06 0.993034 0.996720 0.972458 0.991192 705555200 0.0 0.0
2005-01-07 0.998101 1.069197 0.994263 1.063362 2227450400 0.0 0.0
... ... ... ... ... ... ... ...
2009-12-24 6.251186 6.429309 6.245044 6.419788 500889200 0.0 0.0
2009-12-28 6.502094 6.570578 6.437294 6.498715 644565600 0.0 0.0
2009-12-29 6.530040 6.532804 6.410268 6.421631 445205600 0.0 0.0
2009-12-30 6.413338 6.510692 6.397369 6.499636 412084400 0.0 0.0
2009-12-31 6.545397 6.552154 6.466470 6.471691 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)
Out [15]:
<AxesSubplot:title={'center':'SPY Trends'}, xlabel='date'>
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)
Out [16]:
<AxesSubplot:title={'center':'AAPL Trends'}, xlabel='date'>

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.141912    87.354056   89.275963  0.019501  1.945272   

   entry_fees  exit_fees  
0    0.249377        0.0  

None
/Users/kj/Documents/GitHub/pandas-ta/env/lib/python3.9/site-packages/vectorbt/portfolio/trades.py:252: RuntimeWarning: invalid value encountered in true_divide
  return self.wrapper.wrap_reduced(win_count / total_count, group_by=group_by, **wrap_kwargs)
/Users/kj/Documents/GitHub/pandas-ta/env/lib/python3.9/site-packages/vectorbt/portfolio/trades.py:252: RuntimeWarning: invalid value encountered in true_divide
  return self.wrapper.wrap_reduced(win_count / total_count, group_by=group_by, **wrap_kwargs)
Run Time                  Saturday June 19, 2021, NYSE: 7:58:39
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
Duration                                     1259 days 00:00:00
Init. Cash                                                100.0
Total Profit                                           1.945272
Total Return [%]                                       1.945272
Benchmark Return [%]                                   2.455635
Position Coverage [%]                                     100.0
Max. Drawdown [%]                                     55.189436
Avg. Drawdown [%]                                      2.615867
Max. Drawdown Duration                        562 days 00:00:00
Avg. Drawdown Duration               24 days 01:55:11.999999999
Num. Trades                                                   0
Gross Exposure                                              1.0
Sharpe Ratio                                           0.163325
Sortino Ratio                                          0.231223
Calmar Ratio                                           0.010149
Annual Return [%]                                      0.560106
Annual Volatility [%]                                 28.894472
Omega Ratio                                            1.028442
Skew                                                   0.426804
Kurtosis                                              14.956038
Tail Ratio                                              0.88191
Common Sense Ratio                                      0.88685
Value at Risk                                         -0.022291
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.384595     0.974274    6.471691  5.640079   

         pnl  entry_fees  exit_fees  
0  562.60143    0.249377        0.0  

None
/Users/kj/Documents/GitHub/pandas-ta/env/lib/python3.9/site-packages/vectorbt/portfolio/trades.py:252: RuntimeWarning: invalid value encountered in true_divide
  return self.wrapper.wrap_reduced(win_count / total_count, group_by=group_by, **wrap_kwargs)
Run Time                  Saturday June 19, 2021, NYSE: 7:58:52
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
Duration                                     1259 days 00:00:00
Init. Cash                                                100.0
Total Profit                                          562.60143
Total Return [%]                                      562.60143
Benchmark Return [%]                                 565.918578
Position Coverage [%]                                     100.0
Max. Drawdown [%]                                     60.866748
Avg. Drawdown [%]                                      6.076352
Max. Drawdown Duration                        457 days 00:00:00
Avg. Drawdown Duration               22 days 17:32:18.461538461
Num. Trades                                                   0
Gross Exposure                                              1.0
Sharpe Ratio                                           1.329387
Sortino Ratio                                          1.982724
Calmar Ratio                                           1.199638
Annual Return [%]                                     73.018058
Annual Volatility [%]                                 51.088459
Omega Ratio                                            1.209774
Skew                                                    -0.0376
Kurtosis                                               3.435275
Tail Ratio                                             1.037861
Common Sense Ratio                                     1.795687
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.139340    87.551210   93.911126  0.067461   6.729240   
1       1          0  1.094141    97.302877  112.341527  0.149169  15.880955   
2       0          0  1.677572    72.905618   89.275963  0.222042  27.156674   

   entry_fees  exit_fees  
0    0.249377   0.267492  
1    0.266158   0.307294  
2    0.305761   0.000000  

Run Time                  Saturday June 19, 2021, NYSE: 7:58:53
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
Duration                                     1259 days 00:00:00
Init. Cash                                                100.0
Total Profit                                          49.766868
Total Return [%]                                      49.766868
Benchmark Return [%]                                   2.455635
Position Coverage [%]                                 52.819698
Max. Drawdown [%]                                     10.131765
Avg. Drawdown [%]                                      1.723872
Max. Drawdown Duration                        451 days 00:00:00
Avg. Drawdown Duration               17 days 20:34:17.142857143
Num. Trades                                                   2
Win Rate [%]                                              100.0
Best Trade [%]                                        14.916866
Worst Trade [%]                                        6.746063
Avg. Trade [%]                                        10.831464
Max. Trade Duration                           335 days 00:00:00
Avg. Trade Duration                           264 days 00:00:00
Expectancy                                            11.305097
SQN                                                    2.470596
Gross Exposure                                         0.528197
Sharpe Ratio                                           1.009581
Sortino Ratio                                          1.431911
Calmar Ratio                                           1.226146
Annual Return [%]                                     12.423018
Annual Volatility [%]                                 12.357496
Omega Ratio                                            1.224227
Skew                                                  -0.412954
Kurtosis                                               6.272176
Tail Ratio                                             1.032277
Common Sense Ratio                                     1.160517
Value at Risk                                         -0.010296
Alpha                                                  0.122838
Beta                                                   0.182612
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.628049     1.645288    1.772486  0.072117   7.193697   
1       1          0  45.848650     2.332160    3.704574  0.582002  62.231414   
2       1          0  29.257588     5.776368    3.885621 -0.331506 -56.025411   
3       0          0  30.748240     3.678809    6.471691  0.756681  85.593399   

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

Run Time                  Saturday June 19, 2021, NYSE: 7:58:54
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
Duration                                     1259 days 00:00:00
Init. Cash                                                100.0
Total Profit                                          98.993099
Total Return [%]                                      98.993099
Benchmark Return [%]                                 565.918578
Position Coverage [%]                                 62.271644
Max. Drawdown [%]                                     59.898137
Avg. Drawdown [%]                                      6.193394
Max. Drawdown Duration                        506 days 00:00:00
Avg. Drawdown Duration                         32 days 01:30:00
Num. Trades                                                   3
Win Rate [%]                                          66.666667
Best Trade [%]                                        58.200243
Worst Trade [%]                                       -33.15062
Avg. Trade [%]                                        10.753768
Max. Trade Duration                           363 days 00:00:00
Avg. Trade Duration                           207 days 08:00:00
Expectancy                                             4.466566
SQN                                                    0.130735
Gross Exposure                                         0.622716
Sharpe Ratio                                           0.739016
Sortino Ratio                                           1.08289
Calmar Ratio                                            0.36859
Annual Return [%]                                     22.077862
Annual Volatility [%]                                 35.543979
Omega Ratio                                            1.140899
Skew                                                   0.024374
Kurtosis                                               4.672291
Tail Ratio                                             1.095481
Common Sense Ratio                                     1.337339
Value at Risk                                         -0.029217
Alpha                                                 -0.063652
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.active_returns().vbt.plot(title=f"{benchmarkdf.name} | Active 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.active_returns().vbt.plot(title=f"{assetdf.name} | Active 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.active_returns().vbt.plot(title=f"{benchmarkdf.name} | Active 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.active_returns().vbt.plot(title=f"{assetdf.name} | Active 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.