buy-and-hold (monthly and holding period returns)

buy, then never ever sell, until the end date :)


In [1]:
import pandas as pd
import matplotlib.pyplot as plt
import datetime
from talib.abstract import *

import pinkfish as pf

# format price data
pd.options.display.float_format = '{:0.2f}'.format

%matplotlib inline

In [2]:
# set size of inline plots
'''note: rcParams can't be in same cell as import matplotlib
   or %matplotlib inline
   
   %matplotlib notebook: will lead to interactive plots embedded within
   the notebook, you can zoom and resize the figure
   
   %matplotlib inline: only draw static images in the notebook
'''
plt.rcParams["figure.figsize"] = (10, 7)

In [3]:
pf.DEBUG = True

Some global data


In [4]:
symbol = '^GSPC'
#symbol = 'SPY'
capital = 10000
start = datetime.datetime(1900, 1, 1)
end = datetime.datetime.now()

Define Strategy Class


In [5]:
class Strategy:

    def __init__(self, symbol, capital, start, end):
        self._symbol = symbol
        self._capital = capital
        self._start = start
        self._end = end

    def _algo(self):
        pf.TradeLog.cash = self._capital

        for i, row in enumerate(self._ts.itertuples()):

            date = row.Index.to_pydatetime()
            high = row.high; low = row.low; close = row.close 
            end_flag = pf.is_last_row(self._ts, i)
            shares = 0

            # buy
            if self._tlog.shares == 0:
                shares = self._tlog.buy(date, close)
            # sell
            elif end_flag:
                shares = self._tlog.sell(date, close)

            if shares > 0:
                pf.DBG("{0} BUY  {1} {2} @ {3:.2f}".format(
                       date, shares, self._symbol, close))
            elif shares < 0:
                pf.DBG("{0} SELL {1} {2} @ {3:.2f}".format(
                       date, -shares, self._symbol, close))

            # record daily balance
            self._dbal.append(date, high, low, close)

    def run(self):
        self._ts = pf.fetch_timeseries(self._symbol)
        self._ts = pf.select_tradeperiod(self._ts, self._start, self._end,
                                         use_adj=True)
        self._ts, self._start = pf.finalize_timeseries(self._ts, self._start)
        self._tlog = pf.TradeLog(self._symbol)
        self._dbal = pf.DailyBal()

        self._algo()

    def get_logs(self):
        """ return DataFrames """
        self.tlog = self._tlog.get_log()
        self.dbal = self._dbal.get_log(self.tlog)
        return self.tlog, self.dbal

    def get_stats(self):
        stats = pf.stats(self._ts, self.tlog, self.dbal, self._capital)
        return stats

Run Strategy


In [6]:
s = Strategy(symbol, capital, start, end)
s.run()


1927-12-30 00:00:00 BUY  566 ^GSPC @ 17.66
2020-07-17 00:00:00 SELL 566 ^GSPC @ 3224.73

Retrieve log DataFrames


In [7]:
tlog, dbal = s.get_logs()
stats = s.get_stats()

In [8]:
tlog.tail()


Out[8]:
entry_date entry_price exit_date exit_price pl_points pl_cash qty cumul_total direction symbol
0 1927-12-30 17.66 2020-07-17 3224.73 3207.07 1815201.61 566 1815201.61 LONG ^GSPC

In [9]:
dbal.tail()


Out[9]:
high low close shares cash leverage state
date
2020-07-13 1831195.60 1782581.78 1785858.94 566 4.44 1.00 -
2020-07-14 1811742.11 1770259.95 1809800.77 566 4.44 1.00 -
2020-07-15 1832870.94 1811634.61 1826237.43 566 4.44 1.00 -
2020-07-16 1822745.12 1810406.43 1820017.10 566 4.44 1.00 -
2020-07-17 1825201.61 1825201.61 1825201.61 0 1825201.61 1.00 X

In [10]:
pf.print_full(stats)


start                                                   1927-12-30
end                                                     2020-07-17
beginning_balance                                            10000
ending_balance                                          1825201.61
total_net_profit                                        1815201.61
gross_profit                                            1815201.61
gross_loss                                                    0.00
profit_factor                                                 1000
return_on_initial_capital                                 18152.02
annual_return_rate                                            5.79
trading_period                           92 years 6 months 17 days
pct_time_in_market                                          100.00
margin                                                           1
avg_leverage                                                  1.00
max_leverage                                                  1.00
min_leverage                                                  1.00
total_num_trades                                                 1
trades_per_year                                               0.01
num_winning_trades                                               1
num_losing_trades                                                0
num_even_trades                                                  0
pct_profitable_trades                                       100.00
avg_profit_per_trade                                    1815201.61
avg_profit_per_winning_trade                            1815201.61
avg_loss_per_losing_trade                                        0
ratio_avg_profit_win_loss                                     1000
largest_profit_winning_trade                            1815201.61
largest_loss_losing_trade                                        0
num_winning_points                                         3207.07
num_losing_points                                                0
total_net_points                                           3207.07
avg_points                                                 3207.07
largest_points_winning_trade                               3207.07
largest_points_losing_trade                                      0
avg_pct_gain_per_trade                                    18160.08
largest_pct_winning_trade                                 18160.08
largest_pct_losing_trade                                         0
max_consecutive_winning_trades                                   1
max_consecutive_losing_trades                                    0
avg_bars_winning_trades                                   23246.00
avg_bars_losing_trades                                           0
max_closed_out_drawdown                                     -86.17
max_closed_out_drawdown_start_date                      1929-09-16
max_closed_out_drawdown_end_date                        1932-06-01
max_closed_out_drawdown_recovery_date                   1954-09-22
drawdown_recovery                                            -2.71
drawdown_annualized_return                                  -14.89
max_intra_day_drawdown                                      -86.17
avg_yearly_closed_out_drawdown                              -15.98
max_yearly_closed_out_drawdown                              -71.30
avg_monthly_closed_out_drawdown                              -4.21
max_monthly_closed_out_drawdown                             -42.18
avg_weekly_closed_out_drawdown                               -1.62
max_weekly_closed_out_drawdown                              -28.51
avg_yearly_closed_out_runup                                  26.39
max_yearly_closed_out_runup                                 176.33
avg_monthly_closed_out_runup                                  4.91
max_monthly_closed_out_runup                                 61.42
avg_weekly_closed_out_runup                                   1.79
max_weekly_closed_out_runup                                  26.54
pct_profitable_years                                         68.94
best_year                                                   170.81
worst_year                                                  -71.04
avg_year                                                      7.68
annual_std                                                   20.14
pct_profitable_months                                        59.75
best_month                                                   61.42
worst_month                                                 -42.18
avg_month                                                     0.59
monthly_std                                                   5.35
pct_profitable_weeks                                         56.03
best_week                                                    25.86
worst_week                                                  -27.72
avg_week                                                      0.15
weekly_std                                                    2.62
sharpe_ratio                                                  0.39
sortino_ratio                                                 0.49
dtype: object

Summary


In [11]:
pf.summary(stats)


Out[11]:
strategy
annual_return_rate 5.79
max_closed_out_drawdown -86.17
best_month 61.42
worst_month -42.18
sharpe_ratio 0.39
sortino_ratio 0.49
monthly_std 5.35

In [12]:
returns = dbal['close']
pf.monthly_returns_map(returns['1990':])


Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Year
1990 -8.5 0.9 2.4 -2.7 9.2 -0.9 -0.5 -9.4 -5.1 -0.7 6.0 2.5 -8.2
1991 4.2 6.7 2.2 0.0 3.9 -4.8 4.5 2.0 -1.9 1.2 -4.4 11.2 26.3
1992 -2.0 1.0 -2.2 2.8 0.1 -1.7 3.9 -2.4 0.9 0.2 3.0 1.0 4.5
1993 0.7 1.0 1.9 -2.5 2.3 0.1 -0.5 3.4 -1.0 1.9 -1.3 1.0 7.1
1994 3.3 -3.0 -4.6 1.2 1.2 -2.7 3.1 3.8 -2.7 2.1 -4.0 1.2 -1.5
1995 2.4 3.6 2.7 2.8 3.6 2.1 3.2 -0.0 4.0 -0.5 4.1 1.7 34.1
1996 3.3 0.7 0.8 1.3 2.3 0.2 -4.6 1.9 5.4 2.6 7.3 -2.2 20.3
1997 6.1 0.6 -4.3 5.8 5.9 4.3 7.8 -5.7 5.3 -3.4 4.5 1.6 31.0
1998 1.0 7.0 5.0 0.9 -1.9 3.9 -1.2 -14.6 6.2 8.0 5.9 5.6 26.7
1999 4.1 -3.2 3.9 3.8 -2.5 5.4 -3.2 -0.6 -2.9 6.3 1.9 5.8 19.5
2000 -5.1 -2.0 9.7 -3.1 -2.2 2.4 -1.6 6.1 -5.3 -0.5 -8.0 0.4 -10.1
2001 3.5 -9.2 -6.4 7.7 0.5 -2.5 -1.1 -6.4 -8.2 1.8 7.5 0.8 -13.0
2002 -1.6 -2.1 3.7 -6.1 -0.9 -7.2 -7.9 0.5 -11.0 8.6 5.7 -6.0 -23.4
2003 -2.7 -1.7 0.8 8.1 5.1 1.1 1.6 1.8 -1.2 5.5 0.7 5.1 26.4
2004 1.7 1.2 -1.6 -1.7 1.2 1.8 -3.4 0.2 0.9 1.4 3.9 3.2 9.0
Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Year
2005 -2.5 1.9 -1.9 -2.0 3.0 -0.0 3.6 -1.1 0.7 -1.8 3.5 -0.1 3.0
2006 2.5 0.0 1.1 1.2 -3.1 0.0 0.5 2.1 2.5 3.2 1.6 1.3 13.6
2007 1.4 -2.2 1.0 4.3 3.3 -1.8 -3.2 1.3 3.6 1.5 -4.4 -0.9 3.5
2008 -6.1 -3.5 -0.6 4.8 1.1 -8.6 -1.0 1.2 -9.1 -16.9 -7.5 0.8 -38.5
2009 -8.6 -11.0 8.5 9.4 5.3 0.0 7.4 3.4 3.6 -2.0 5.7 1.8 23.5
2010 -3.7 2.9 5.9 1.5 -8.2 -5.4 6.9 -4.7 8.8 3.7 -0.2 6.5 12.8
2011 2.3 3.2 -0.1 2.8 -1.4 -1.8 -2.1 -5.7 -7.2 10.8 -0.5 0.9 -0.0
2012 4.4 4.1 3.1 -0.7 -6.3 4.0 1.3 2.0 2.4 -2.0 0.3 0.7 13.4
2013 5.0 1.1 3.6 1.8 2.1 -1.5 4.9 -3.1 3.0 4.5 2.8 2.4 29.6
2014 -3.6 4.3 0.7 0.6 2.1 1.9 -1.5 3.8 -1.6 2.3 2.5 -0.4 11.4
2015 -3.1 5.5 -1.7 0.9 1.0 -2.1 2.0 -6.3 -2.6 8.3 0.1 -1.8 -0.7
2016 -5.1 -0.4 6.6 0.3 1.5 0.1 3.6 -0.1 -0.1 -1.9 3.4 1.8 9.5
2017 1.8 3.7 -0.0 0.9 1.2 0.5 1.9 0.1 1.9 2.2 2.8 1.0 19.4
2018 5.6 -3.9 -2.7 0.3 2.2 0.5 3.6 3.0 0.4 -6.9 1.8 -9.2 -6.2
2019 7.9 3.0 1.8 3.9 -6.6 6.9 1.3 -1.8 1.7 2.0 3.4 2.9 28.9
Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Year
2020 -0.2 -8.4 -12.5 12.7 4.5 1.8 4.0 - - - - - -0.2

In [13]:
returns = dbal['close']
pf.holding_period_map(returns['1990':])


Years12345678910111213141516171819202122232425262728293031
1990-8877591113151513107888885666677777787
19912615129131417181815129101091096777788888888
1992463101215171714117998985666677777787
199373121417191915117998985666677778787
1994-2151720212116127999995666677778787
19953427282826191481010101095666688778788
19962026262416115887883455567667677
19973129261693666762344466566676
19982723114-223344-1122345445565
1999204-2-8-2-0022-3-100133334454
2000-10-12-16-7-4-3-1-0-5-3-1-1-022233344
2001-13-18-6-2-112-5-2-0-0133334455
2002-23-22244-3-011245455566
200326171213110354577778787
20049687-4022356566676
2005387-7-211255556576
2006148-10-300256557677
20074-20-8-3-2045456576
2008-38-13-5-4-145456576
2009231812121515121213111211
20101369131311101291110
2011-06141310101191110
2012132118131213101311
20133020131213101211
20141157106108
2015-149598
2016101471210
20171961310
2018-6106
20192913
2020-0