{
"cells": [
{
"cell_type": "markdown",
"source": [
"# \"Beating the market with the simple possible predictive metric.\"\n",
"\n",
"This is an implementation of algorithm from https://www.reddit.com/r/algotrading/comments/mtp8b5/beating_the_market_with_the_simple_possible/ using [universal-portfolios](https://github.com/Marigold/universal-portfolios) package.\n",
"\n",
"Note that this is just a demonstration how to use the package and replicate the results, it's not meant as a real analysis and it's likely wrong."
],
"metadata": {}
},
{
"cell_type": "code",
"execution_count": 1,
"source": [
"# some init stuff\n",
"%matplotlib inline\n",
"%load_ext autoreload\n",
"%autoreload 2\n",
"%config InlineBackend.figure_format = 'svg'\n",
"\n",
"import numpy as np\n",
"import pandas as pd\n",
"import seaborn as sns\n",
"import datetime as dt\n",
"import matplotlib.pyplot as plt\n",
"\n",
"sns.set_context(\"notebook\")\n",
"plt.rcParams[\"figure.figsize\"] = (16, 8)"
],
"outputs": [],
"metadata": {}
},
{
"cell_type": "markdown",
"source": [
"# Get data"
],
"metadata": {}
},
{
"cell_type": "code",
"execution_count": 2,
"source": [
"# nasdaq 100 as of 2021-08-18\n",
"nasdaq100 = [\n",
"\"AAPL\",\n",
"\"MSFT\",\n",
"\"AMZN\",\n",
"\"GOOG\",\n",
"\"FB\",\n",
"\"TSLA\",\n",
"\"NVDA\",\n",
"\"PYPL\",\n",
"\"ADBE\",\n",
"\"CMCSA\",\n",
"\"CSCO\",\n",
"\"NFLX\",\n",
"\"PEP\",\n",
"\"INTC\",\n",
"\"COST\",\n",
"\"AVGO\",\n",
"\"TMUS\",\n",
"\"TXN\",\n",
"\"QCOM\",\n",
"\"MRNA\",\n",
"\"HON\",\n",
"\"CHTR\",\n",
"\"INTU\",\n",
"\"SBUX\",\n",
"\"AMGN\",\n",
"\"AMD\",\n",
"\"ISRG\",\n",
"\"AMAT\",\n",
"\"GILD\",\n",
"\"ADP\",\n",
"\"MDLZ\",\n",
"\"MELI\",\n",
"\"BKNG\",\n",
"\"LRCX\",\n",
"\"ZM\",\n",
"\"MU\",\n",
"\"CSX\",\n",
"\"ILMN\",\n",
"\"FISV\",\n",
"\"ADSK\",\n",
"\"REGN\",\n",
"\"ATVI\",\n",
"\"ASML\",\n",
"\"ADI\",\n",
"\"IDXX\",\n",
"\"NXPI\",\n",
"\"DOCU\",\n",
"\"ALGN\",\n",
"\"BIIB\",\n",
"\"JD\",\n",
"\"MNST\",\n",
"\"VRTX\",\n",
"\"EBAY\",\n",
"\"KLAC\",\n",
"\"DXCM\",\n",
"\"KDP\",\n",
"\"LULU\",\n",
"\"MRVL\",\n",
"\"EXC\",\n",
"\"KHC\",\n",
"\"TEAM\",\n",
"\"AEP\",\n",
"\"SNPS\",\n",
"\"WDAY\",\n",
"\"ROST\",\n",
"\"WBA\",\n",
"\"MAR\",\n",
"\"PAYX\",\n",
"\"ORLY\",\n",
"\"CDNS\",\n",
"\"CTAS\",\n",
"\"CTSH\",\n",
"\"EA\",\n",
"\"MCHP\",\n",
"\"XEL\",\n",
"\"BIDU\",\n",
"\"MTCH\",\n",
"\"XLNX\",\n",
"\"CPRT\",\n",
"\"FAST\",\n",
"\"VRSK\",\n",
"\"ANSS\",\n",
"\"PTON\",\n",
"\"PDD\",\n",
"\"SWKS\",\n",
"\"SGEN\",\n",
"\"OKTA\",\n",
"\"PCAR\",\n",
"\"CDW\",\n",
"\"MXIM\",\n",
"\"NTES\",\n",
"\"SIRI\",\n",
"\"CERN\",\n",
"\"VRSN\",\n",
"\"SPLK\",\n",
"\"DLTR\",\n",
"\"INCY\",\n",
"\"CHKP\",\n",
"\"TCOM\",\n",
"\"FOX\",\n",
"]\n",
"\n",
"import pandas_datareader.data as web\n",
"S = []\n",
"# loading data in chunks from yahoo is more robust than loading everything at once\n",
"for i, chunk in enumerate(np.array_split(nasdaq100, 10)):\n",
" print(f'Loading chunk {i}')\n",
" # 2010-01-01 is arbitrary\n",
" S.append(web.DataReader(chunk, 'yahoo', start='2010-01-01', end='2021-08-17')['Adj Close'])\n",
" \n",
"S = pd.concat(S, axis=1)"
],
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Loading chunk 0\n",
"Loading chunk 1\n",
"Loading chunk 2\n",
"Loading chunk 3\n",
"Loading chunk 4\n",
"Loading chunk 5\n",
"Loading chunk 6\n",
"Loading chunk 7\n",
"Loading chunk 8\n",
"Loading chunk 9\n"
]
}
],
"metadata": {}
},
{
"cell_type": "markdown",
"source": [
"# Construct weights for momentum & reversal strategies\n",
"\n",
"Momentum strategy goes long stock with the highest yesterday return, reversal goes long stock with the lowest yesterday return.\n",
"\n",
"Construct weight matrices for CRP \"algorithm\" that just allocates portfolio based on given weights."
],
"metadata": {}
},
{
"cell_type": "code",
"execution_count": 11,
"source": [
"from universal.algos import CRP\n",
"\n",
"# find best performing and worst performing stock\n",
"R = S / S.shift(1)\n",
"highest_return_symbol = R.idxmax(axis=1).shift(1)\n",
"lower_return_symbol = R.idxmin(axis=1).shift(1)\n",
"\n",
"# construct weights\n",
"W_mom = S * 0\n",
"for col in R.columns:\n",
" W_mom.loc[highest_return_symbol == col, col] = 1\n",
"\n",
"W_rev = S * 0\n",
"for col in R.columns:\n",
" W_rev.loc[lower_return_symbol == col, col] = 1"
],
"outputs": [],
"metadata": {}
},
{
"cell_type": "code",
"execution_count": 12,
"source": [
"# keep graphs simple\n",
"plot_kwargs = {\n",
" \"logy\": True,\n",
" \"assets\": False,\n",
" \"weights\": False,\n",
" \"ucrp\": True,\n",
"}\n",
"\n",
"algo = CRP(W_mom)\n",
"result = algo.run(S)\n",
"print(result.summary())\n",
"result.plot(**plot_kwargs, title='Momentum strategy')\n",
"r_mom = result.r_log"
],
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Summary:\n",
" Profit factor: 0.97\n",
" Sharpe ratio: 0.06 ± 0.29\n",
" Ulcer index: 0.05\n",
" Information ratio (wrt UCRP): -0.49\n",
" UCRP sharpe: 1.36 ± 0.41\n",
" Appraisal ratio (wrt UCRP): -0.48 ± 0.29\n",
" Beta / Alpha: 0.98 / -22.686%\n",
" Annualized return: 3.12%\n",
" Annualized volatility: 50.97%\n",
" Longest drawdown: 2484 days\n",
" Max drawdown: 89.01%\n",
" Winning days: 49.0%\n",
" Annual turnover: 466.4\n",
" \n"
]
},
{
"output_type": "display_data",
"data": {
"image/svg+xml": "\n\n\n\n",
"text/plain": [
"