Golden Cross / Death Cross S&P 500 index (^GSPC)¶

1. sma50>sma200, buy
2. sma50<sma200, sell your long position.
In [1]:
import datetime

import matplotlib.pyplot as plt
import pandas as pd

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)

Some global data

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

use_adj=True

Prepare timeseries

In [4]:
# Fetch and select timeseries.
ts = pf.fetch_timeseries(symbol,)
ts = pf.select_tradeperiod(ts, start, end, use_adj=use_adj)

# Add technical indicator: day sma regime filter.
ts['regime'] = \
    pf.CROSSOVER(ts, timeperiod_fast=50, timeperiod_slow=200)

# Finalize the time series before implementing trading strategy.
ts, start = pf.finalize_timeseries(ts, start, dropna=True, drop_columns=['open', 'high', 'low'])

# Create Trade Log (tlog); Create Daily Balance (dbal).
tlog = pf.TradeLog(symbol)
dbal = pf.DailyBal()
In [5]:
# Yahoo S&P 500 timeseries has issues, specifically some of the intraday value are 0.
# drop_columns removes these, then dropna drops the first 200 rows with regime value
# that are NA. pf.find_nan_rows() can help diagnose these issues.
df = pf.find_nan_rows(ts)
df
Out[5]:
close adj_close volume regime
date

Algo: Buy when 50 day ma crosses above 200 day ma. Sell when 50 day ma crosses below 200 day ma.

In [6]:
pf.TradeLog.cash = capital

for i, row in enumerate(ts.itertuples()):

    date = row.Index.to_pydatetime() 
    end_flag = pf.is_last_row(ts, i)

    # Buy
    # Note ts['regime'][i-1] is regime for previous day.
    # We want to buy only on the day of a moving average crossover.
    # i.e. yesteraday regime is negative, today it is positive.
    if tlog.shares == 0:
        if row.regime > 0 and ts['regime'].iloc[i-1] < 0:
            tlog.buy(date, row.close)  
    # Sell
    else:
        if row.regime < 0 or end_flag:
            tlog.sell(date, row.close)

    # Record daily balance.
    dbal.append(date, row.close)       

Retrieve logs

In [7]:
tlog = tlog.get_log()
dbal = dbal.get_log(tlog)

View trade log

In [8]:
tlog.head(50)
Out[8]:
entry_date entry_price exit_date exit_price pl_points pl_cash qty cumul_total direction symbol
0 1932-09-19 7.34 1933-03-27 6.09 -1.25 -1702.50 1362 -1702.50 LONG ^GSPC
1 1933-05-18 8.89 1934-05-31 9.61 0.72 671.76 933 -1030.74 LONG ^GSPC
2 1935-05-23 10.07 1937-05-21 16.27 6.20 5518.00 890 4487.26 LONG ^GSPC
3 1938-07-27 12.25 1939-03-31 10.98 -1.27 -1501.14 1182 2986.12 LONG ^GSPC
4 1939-09-18 12.47 1940-03-20 12.14 -0.33 -343.53 1041 2642.59 LONG ^GSPC
5 1940-12-13 10.69 1941-02-21 9.76 -0.93 -1099.26 1182 1543.33 LONG ^GSPC
6 1941-08-18 10.13 1941-11-18 9.26 -0.87 -990.93 1139 552.40 LONG ^GSPC
7 1942-08-14 8.58 1943-12-01 11.13 2.55 3133.95 1229 3686.35 LONG ^GSPC
8 1944-03-13 12.24 1946-08-28 16.73 4.49 5019.82 1118 8706.17 LONG ^GSPC
9 1947-07-25 16.08 1948-01-22 14.42 -1.66 -1930.58 1163 6775.59 LONG ^GSPC
10 1948-05-14 16.39 1948-12-01 15.01 -1.38 -1411.74 1023 5363.85 LONG ^GSPC
11 1949-08-30 15.21 1953-05-11 24.91 9.70 9797.00 1010 15160.85 LONG ^GSPC
12 1953-12-21 24.95 1956-10-26 46.27 21.32 21490.56 1008 36651.41 LONG ^GSPC
13 1957-06-03 47.37 1957-09-26 42.57 -4.80 -4723.20 984 31928.21 LONG ^GSPC
14 1958-05-08 43.99 1959-10-30 57.52 13.53 12894.09 953 44822.30 LONG ^GSPC
15 1959-12-30 59.77 1960-02-15 55.17 -4.60 -4218.20 917 40604.10 LONG ^GSPC
16 1961-01-04 58.36 1962-05-07 66.02 7.66 6641.22 867 47245.32 LONG ^GSPC
17 1963-01-03 63.72 1965-07-22 83.85 20.13 18076.74 898 65322.05 LONG ^GSPC
18 1965-09-17 90.05 1966-04-28 91.13 1.08 902.88 836 66224.93 LONG ^GSPC
19 1967-02-03 87.36 1968-02-27 90.53 3.17 2764.24 872 68989.17 LONG ^GSPC
20 1968-05-17 96.90 1969-03-13 98.39 1.49 1214.35 815 70203.51 LONG ^GSPC
21 1969-05-27 103.57 1969-06-23 96.23 -7.34 -5681.16 774 64522.36 LONG ^GSPC
22 1970-10-22 83.38 1971-09-24 98.15 14.77 13189.61 893 77711.97 LONG ^GSPC
23 1972-01-26 102.50 1973-04-18 111.54 9.04 7729.20 855 85441.17 LONG ^GSPC
24 1975-03-06 83.69 1976-12-01 102.49 18.80 21431.99 1140 106873.17 LONG ^GSPC
25 1977-01-04 105.70 1977-03-03 100.88 -4.82 -5326.10 1105 101547.07 LONG ^GSPC
26 1978-05-22 99.09 1978-12-13 96.06 -3.03 -3408.75 1125 98138.32 LONG ^GSPC
27 1979-03-21 101.25 1980-04-22 103.43 2.18 2328.24 1068 100466.56 LONG ^GSPC
28 1980-06-17 116.03 1981-07-02 128.64 12.61 12004.72 952 112471.28 LONG ^GSPC
29 1982-09-28 123.24 1984-02-03 160.91 37.67 37406.32 993 149877.59 LONG ^GSPC
30 1984-09-12 164.68 1986-11-18 236.78 72.10 69937.01 970 219814.60 LONG ^GSPC
31 1986-11-25 248.17 1987-11-05 254.48 6.31 5843.06 926 225657.66 LONG ^GSPC
32 1988-06-28 272.31 1990-02-26 328.67 56.36 48751.41 865 274409.07 LONG ^GSPC
33 1990-05-25 354.58 1990-09-07 323.40 -31.18 -25006.35 802 249402.72 LONG ^GSPC
34 1991-02-15 369.06 1994-04-19 442.54 73.48 51582.97 702 300985.69 LONG ^GSPC
35 1994-09-15 474.81 1998-09-29 1049.02 574.21 375533.35 654 676519.04 LONG ^GSPC
36 1998-12-08 1181.38 1999-11-04 1362.64 181.26 105312.07 581 781831.11 LONG ^GSPC
37 1999-11-11 1381.46 2000-10-30 1398.66 17.20 9855.64 573 791686.75 LONG ^GSPC
38 2003-05-14 939.28 2004-08-18 1095.17 155.89 132974.18 853 924660.93 LONG ^GSPC
39 2004-11-05 1166.17 2006-07-19 1259.81 93.64 75005.65 801 999666.58 LONG ^GSPC
40 2006-09-12 1313.00 2007-12-21 1484.46 171.46 131681.25 768 1131347.83 LONG ^GSPC
41 2009-06-23 895.10 2010-07-02 1022.58 127.48 162537.05 1275 1293884.89 LONG ^GSPC
42 2010-10-22 1183.08 2011-08-12 1178.81 -4.27 -4705.43 1102 1289179.46 LONG ^GSPC
43 2012-01-31 1312.41 2015-08-28 1988.87 676.46 669018.90 989 1958198.36 LONG ^GSPC
44 2015-12-21 2021.15 2016-01-11 1923.67 -97.48 -94848.02 973 1863350.34 LONG ^GSPC
45 2016-04-25 2087.79 2018-12-07 2633.08 545.29 489125.17 897 2352475.50 LONG ^GSPC
46 2019-04-01 2867.19 2020-03-30 2626.65 -240.54 -197964.45 823 2154511.05 LONG ^GSPC
47 2020-07-09 3152.05 2022-03-14 4173.11 1021.06 700447.03 686 2854958.08 LONG ^GSPC
48 2023-02-02 4179.76 2023-12-21 4732.65 552.89 378729.74 685 3233687.83 LONG ^GSPC

Generate strategy stats - display all available stats

In [9]:
stats = pf.stats(ts, tlog, dbal, capital)
pf.print_full(stats)
start                                                  1928-10-16
end                                                    2023-12-21
beginning_balance                                           10000
ending_balance                                         3243687.83
total_net_profit                                       3233687.83
gross_profit                                           3588549.17
gross_loss                                             -354861.34
profit_factor                                               10.11
return_on_initial_capital                                32336.88
annual_return_rate                                           6.26
trading_period                           95 years 2 months 5 days
pct_time_in_market                                          66.17
margin                                                          1
avg_leverage                                                 1.00
max_leverage                                                 1.00
min_leverage                                                 1.00
total_num_trades                                               49
trades_per_year                                              0.51
num_winning_trades                                             33
num_losing_trades                                              16
num_even_trades                                                 0
pct_profitable_trades                                       67.35
avg_profit_per_trade                                     65993.63
avg_profit_per_winning_trade                            108743.91
avg_loss_per_losing_trade                               -22178.83
ratio_avg_profit_win_loss                                    4.90
largest_profit_winning_trade                            700447.03
largest_loss_losing_trade                              -197964.45
num_winning_points                                        4512.20
num_losing_points                                         -405.75
total_net_points                                          4106.45
avg_points                                                  83.81
largest_points_winning_trade                              1021.06
largest_points_losing_trade                               -240.54
avg_pct_gain_per_trade                                      15.07
largest_pct_winning_trade                                  120.93
largest_pct_losing_trade                                   -17.03
expected_shortfall                                              0
max_consecutive_winning_trades                                  8
max_consecutive_losing_trades                                   4
avg_bars_winning_trades                                    430.85
avg_bars_losing_trades                                     103.19
max_closed_out_drawdown                                    -36.86
max_closed_out_drawdown_peak_date                      1937-03-10
max_closed_out_drawdown_trough_date                    1941-11-13
max_closed_out_drawdown_recovery_date                  1945-05-03
drawdown_loss_period                                         1709
drawdown_recovery_period                                     1267
annualized_return_over_max_drawdown                          0.17
max_intra_day_drawdown                                     -36.86
avg_yearly_closed_out_drawdown                              -9.55
max_yearly_closed_out_drawdown                             -35.09
avg_monthly_closed_out_drawdown                             -2.33
max_monthly_closed_out_drawdown                            -31.47
avg_weekly_closed_out_drawdown                              -0.89
max_weekly_closed_out_drawdown                             -28.51
avg_yearly_closed_out_runup                                 16.79
max_yearly_closed_out_runup                                 61.85
avg_monthly_closed_out_runup                                 2.84
max_monthly_closed_out_runup                                26.55
avg_weekly_closed_out_runup                                  1.03
max_weekly_closed_out_runup                                 23.14
pct_profitable_years                                        61.16
best_year                                                   53.98
worst_year                                                 -28.66
avg_year                                                     7.01
annual_std                                                  13.04
pct_profitable_months                                       43.28
best_month                                                  22.49
worst_month                                                -30.91
avg_month                                                    0.54
monthly_std                                                  3.29
pct_profitable_weeks                                        38.68
best_week                                                   23.14
worst_week                                                 -27.33
avg_week                                                     0.14
weekly_std                                                   1.75
pct_profitable_days                                         35.43
best_day                                                    16.60
worst_day                                                  -20.47
avg_day                                                      0.03
daily_std                                                    0.81
sharpe_ratio                                                 0.54
sharpe_ratio_max                                             0.56
sharpe_ratio_min                                             0.52
sortino_ratio                                                0.54
dtype: object

Benchmark: Run, retrieve logs, generate stats

In [10]:
benchmark = pf.Benchmark(symbol, capital, start, end, use_adj=use_adj)
benchmark.run()

Plot Equity Curves: Strategy vs Benchmark

In [11]:
pf.plot_equity_curve(dbal, benchmark=benchmark.dbal)
No description has been provided for this image

Plot Trades

In [12]:
pf.plot_trades(dbal, benchmark=benchmark.dbal)
No description has been provided for this image

Strategy vs Benchmark

In [13]:
df = pf.summary(stats, benchmark.stats, metrics=pf.currency_metrics)
df
Out[13]:
strategy benchmark
beginning_balance $10,000.00 $10,000.00
ending_balance $3,243,687.83 $2,181,752.85
total_net_profit $3,233,687.83 $2,171,752.85
gross_profit $3,588,549.17 $2,171,752.85
gross_loss -$354,861.34 $0.00
In [14]:
extras = ('avg_month',)

df = pf.plot_bar_graph(stats, benchmark.stats, extras=extras)
df
Out[14]:
strategy benchmark
annual_return_rate 6.26 5.82
max_closed_out_drawdown -36.86 -86.16
annualized_return_over_max_drawdown 0.17 0.07
best_month 22.49 61.49
worst_month -30.91 -42.17
sharpe_ratio 0.54 0.39
sortino_ratio 0.54 0.49
monthly_std 3.29 5.33
annual_std 13.04 19.93
avg_month 0.54 0.59
No description has been provided for this image
In [ ]: