The 100 hour volume moving average divided by the 1000 hour volume moving average shows spikes at around 1.5x <\/figcaption><\/figure><\/div>\n\n\n\nFrom this plot we can see that there are a small number of spikes over 1.5. Let’s try running a simulation in which we get out of the market when the 100 hour moving average is more than 1.5 times the 1000 hour moving average.<\/p>\n\n\n\n
z = pd.read_csv(\"qqq.txt\")\nz['dt'] = pd.to_datetime(z['Date'] + ' ' + z['Time'].astype(str), format = \"%m\/%d\/%Y %H%M\")\n\n\nz.index = pd.to_datetime(z['dt'])\nz = z.sort_index()['2007-01-01':'2022-12-31']\n\ndaily_vol = z.groupby(z.index.date).agg({'Close':'last','Open':'first','Volume':'sum'}).reset_index()\ndaily_vol.index = pd.to_datetime(daily_vol['index'])\n\nz = z.resample('H').last()\nz = z.dropna()\n\nreturns = z['Close'].pct_change().dropna()*100\nz = z.rename({'Close':t},axis=1)\n\nz['vol_sma_100'] = z['Volume'].rolling(100).mean()\nz['vol_sma_1000'] = z['Volume'].rolling(1000).mean()\n\n \nclass TestBuyAlgo(bt.core.Algo):\n \n def __init__(self):\n self.oom_date = None\n \n\n def __call__(self, target): \n\n if target.now in returns.index:\n \n loc = returns.index.get_loc(target.now)\n if loc < len(returns) - 2:\n\n today = returns.index[loc].date()\n\n if self.oom_date is not None:\n if self.oom_date != today:\n if z.index[loc].time().hour == 15:\n if daily_vol.loc[pd.to_datetime(today)]['Volume'] < 50000000:\n self.oom_date = None\n elif z.loc[target.now]['vol_sma_100'] > 1.5 * z.loc[target.now]['vol_sma_1000']:\n target.temp['selected'] = [t]\n target.temp['weights'] = {t:0}\n print(\"out of market \" + str(today))\n self.oom_date = today\n else: \n target.temp['selected'] = t\n target.temp['weights'] = { t:1 }\n \n return True\n \n return False\n \n\nroa = TestBuyAlgo()\nwe = bt.algos.WeighEqually()\nrb = bt.algos.Rebalance()\n\nstart = '2007-02-07'\nspy = bt.Strategy(t, [bt.algos.SelectThese([t]),we,rb])\nbenchmark = bt.Backtest(\n spy,\n z[start:],\n integer_positions=False\n)\n\nstrat = bt.Strategy('Test', [roa,rb])\nbacktest = bt.Backtest(\n strat,\n z[start:],\n integer_positions=False\n)\n\nres = bt.run(backtest,benchmark)\n <\/pre>\n\n\n\nWhen we run this simulation, we get a very good result:<\/p>\n\n\n\nUsing volume data helps us time the market better than without<\/figcaption><\/figure>\n\n\n\nStat Mega QQQ\n------------------- ---------- ----------\nStart 2007-02-06 2007-02-06\nEnd 2021-06-25 2021-06-25\nRisk-free rate 0.00% 0.00%\n\nTotal Return 881.85% 784.29%\nDaily Sharpe 1.01 0.79\nDaily Sortino 1.57 1.25\nCAGR 17.21% 16.36%\nMax Drawdown -25.53% -53.31%\nCalmar Ratio 0.67 0.31\n\nMTD 4.82% 4.82%\n3m 12.38% 12.38%\n6m 9.38% 13.18%\nYTD 7.96% 11.71%\n1Y 37.83% 42.62%\n3Y (ann.) 24.57% 27.70%\n5Y (ann.) 23.78% 28.90%\n10Y (ann.) 17.70% 21.34%\nSince Incep. (ann.) 17.21% 16.36%\n\nDaily Sharpe 1.01 0.79\nDaily Sortino 1.57 1.25\nDaily Mean (ann.) 17.39% 17.65%\nDaily Vol (ann.) 17.24% 22.25%\nDaily Skew -0.36 -0.15\nDaily Kurt 4.25 9.17\nBest Day 6.32% 12.63%\nWorst Day -7.41% -12.58%\n\nMonthly Sharpe 1.11 0.95\nMonthly Sortino 2.14 1.70\nMonthly Mean (ann.) 17.39% 17.07%\nMonthly Vol (ann.) 15.60% 18.02%\nMonthly Skew -0.27 -0.48\nMonthly Kurt 0.75 0.81\nBest Month 13.00% 15.07%\nWorst Month -14.08% -16.00%\n\nYearly Sharpe 0.94 0.76\nYearly Sortino 5.55 1.65\nYearly Mean 18.33% 18.36%\nYearly Vol 19.49% 24.19%\nYearly Skew 0.27 -0.87\nYearly Kurt -0.69 1.92\nBest Year 54.67% 54.67%\nWorst Year -11.44% -41.75%\n\nAvg. Drawdown -2.27% -2.45%\nAvg. Drawdown Days 21.50 21.38\nAvg. Up Month 4.20% 4.45%\nAvg. Down Month -2.76% -3.95%\nWin Year % 78.57% 85.71%\nWin 12m % 85.80% 86.42%<\/code><\/pre>\n\n\n\n<\/p>\n","protected":false},"excerpt":{"rendered":"
In this article we will look at whether it is possible to predict a crash using historical market data. Related reading Before reading this article, it’s useful to understand the basics backtesting in Python. We recommend you read these: Getting started: using Python to find alpha [2021] Do Stocks Exhibit Momentum? A reality check [2021] […]<\/p>\n","protected":false},"author":1,"featured_media":1165,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[23,15],"tags":[],"_links":{"self":[{"href":"https:\/\/firemymoneymanager.com\/wp-json\/wp\/v2\/posts\/1135"}],"collection":[{"href":"https:\/\/firemymoneymanager.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/firemymoneymanager.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/firemymoneymanager.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/firemymoneymanager.com\/wp-json\/wp\/v2\/comments?post=1135"}],"version-history":[{"count":15,"href":"https:\/\/firemymoneymanager.com\/wp-json\/wp\/v2\/posts\/1135\/revisions"}],"predecessor-version":[{"id":1181,"href":"https:\/\/firemymoneymanager.com\/wp-json\/wp\/v2\/posts\/1135\/revisions\/1181"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/firemymoneymanager.com\/wp-json\/wp\/v2\/media\/1165"}],"wp:attachment":[{"href":"https:\/\/firemymoneymanager.com\/wp-json\/wp\/v2\/media?parent=1135"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/firemymoneymanager.com\/wp-json\/wp\/v2\/categories?post=1135"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/firemymoneymanager.com\/wp-json\/wp\/v2\/tags?post=1135"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}