{ "cells": [ { "cell_type": "code", "execution_count": 60, "id": "00769472-1fe0-4010-8a2f-248e5154fc92", "metadata": {}, "outputs": [], "source": [ "import requests\n", "import json\n", "import plotly.graph_objs as go\n", "import plotly.express as px\n", "import pandas as pd\n", "import numpy as np\n", "import time\n", "from datetime import date, timedelta, datetime\n", "from pypfopt import EfficientFrontier # nb pypfopt is shorthand for the package pyportfolioopt\n", "from pypfopt import risk_models\n", "from pypfopt import expected_returns" ] }, { "cell_type": "code", "execution_count": 56, "id": "ca4202a4-9a1b-4046-9bc6-d03568b26e5b", "metadata": {}, "outputs": [], "source": [ "def create_assets(total_coins=100):\n", " '''\n", " A function to retrieve info about the largest total_coins number of\n", " cryptocurrencies, ranked by market cap, generated by a call to coincap assets\n", " api.\n", " '''\n", " url = \"https://api.coincap.io/v2/assets\"\n", "\n", " # N.B. here adampt the params dict to only request what you need\n", " payload={'limit': total_coins}\n", " headers = {}\n", "\n", " assets_json = requests.request(\"GET\", url, params=payload, headers=headers).json()\n", " return assets_json\n", "\n", "def gen_symbols(assets_json):\n", " '''\n", " Function to generate three lists: symbols, names and ids, from the result of\n", " a call to the coincap assets api, assets_json.\n", " '''\n", " symbols_list = []\n", " names_list = []\n", " ids_list =[]\n", " for dict in assets_json['data']:\n", " symbols_list.append(dict['symbol'])\n", " names_list.append(dict['name'])\n", " ids_list.append(dict['id'])\n", " return symbols_list, names_list, ids_list\n", "\n", "def load_histories(coin_ids, start, end):\n", " '''\n", " Function to load daily historic prices for all crypto currencies in the\n", " coin_ids list within the time period defined by the interval [start, end].\n", " '''\n", " url = \"http://api.coincap.io/v2/assets/{}/history\"\n", "\n", " payload={'interval':'d1', 'start':start, 'end':end}\n", " headers = {}\n", "\n", " histories_dict = {}\n", " for id in coin_ids:\n", " response_histories = requests.request(\"GET\", url.format(id), headers=headers, params=payload)\n", " histories_json = response_histories.json()\n", " histories_dict[id] = histories_json['data']\n", " return histories_dict\n", "\n", "def create_unix_dates(today=date.today(), lookback_years = 5):\n", " '''\n", " A function to create start_unix and end_unix times in UNIX time in milliseconds\n", " '''\n", " start_datetime = today-timedelta(365*lookback_years)\n", " start_unix = int(time.mktime(start_datetime.timetuple()) * 1000)\n", " end_unix = int(time.mktime(date.today().timetuple()) * 1000)\n", " return start_unix, end_unix" ] }, { "cell_type": "code", "execution_count": 63, "id": "8f806ff9-d219-4935-b2fa-142409796951", "metadata": {}, "outputs": [], "source": [ "assets_json = create_assets(total_coins=100)" ] }, { "cell_type": "code", "execution_count": 64, "id": "eb3ac8a0-24ee-4cfb-b410-2bc4792e7485", "metadata": {}, "outputs": [], "source": [ "symbols, names, coin_ids = gen_symbols(assets_json)" ] }, { "cell_type": "code", "execution_count": 65, "id": "9b4f9e03-5f5d-4c35-b515-3ed956191603", "metadata": {}, "outputs": [], "source": [ "start_unix, end_unix = create_unix_dates(today=date.today(), lookback_years=5)" ] }, { "cell_type": "code", "execution_count": 67, "id": "c4c15661-17fb-420c-bce1-0850066ab683", "metadata": {}, "outputs": [], "source": [ "url = \"http://api.coincap.io/v2/assets/{}/history\"\n", "\n", "payload={'interval':'d1', 'start':start_unix, 'end':end_unix}\n", "headers = {}\n", "\n", "histories_dict = {}\n", "for id in coin_ids:\n", " response_histories = requests.request(\"GET\", url.format(id), headers=headers, params=payload)\n", " histories_json = response_histories.json()\n", " histories_dict[id] = histories_json['data']" ] }, { "cell_type": "code", "execution_count": 66, "id": "de780763-c0ad-4c82-acd0-f279d46e54d3", "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true }, "tags": [] }, "outputs": [ { "ename": "JSONDecodeError", "evalue": "Expecting value: line 1 column 1 (char 0)", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mJSONDecodeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/ff/pmf9d5156jz_pr_s8ybs3x780000gn/T/ipykernel_63357/1421263752.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mhistories_dict\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mload_histories\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcoin_ids\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstart_unix\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mend_unix\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m/var/folders/ff/pmf9d5156jz_pr_s8ybs3x780000gn/T/ipykernel_63357/437834857.py\u001b[0m in \u001b[0;36mload_histories\u001b[0;34m(coin_ids, start, end)\u001b[0m\n\u001b[1;32m 41\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mid\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mcoin_ids\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 42\u001b[0m \u001b[0mresponse_histories\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrequests\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"GET\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheaders\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheaders\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpayload\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 43\u001b[0;31m \u001b[0mhistories_json\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mresponse_histories\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjson\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 44\u001b[0m \u001b[0mhistories_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mhistories_json\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'data'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mhistories_dict\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/usr/local/lib/python3.9/site-packages/requests/models.py\u001b[0m in \u001b[0;36mjson\u001b[0;34m(self, **kwargs)\u001b[0m\n\u001b[1;32m 864\u001b[0m \u001b[0;31m# used.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 865\u001b[0m \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 866\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mcomplexjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloads\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtext\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 867\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 868\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mproperty\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/usr/local/Cellar/python@3.9/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/json/__init__.py\u001b[0m in \u001b[0;36mloads\u001b[0;34m(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)\u001b[0m\n\u001b[1;32m 344\u001b[0m \u001b[0mparse_int\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mparse_float\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 345\u001b[0m parse_constant is None and object_pairs_hook is None and not kw):\n\u001b[0;32m--> 346\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_default_decoder\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 347\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcls\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 348\u001b[0m \u001b[0mcls\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mJSONDecoder\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/usr/local/Cellar/python@3.9/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/json/decoder.py\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(self, s, _w)\u001b[0m\n\u001b[1;32m 335\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 336\u001b[0m \"\"\"\n\u001b[0;32m--> 337\u001b[0;31m \u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mend\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mraw_decode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0midx\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0m_w\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 338\u001b[0m \u001b[0mend\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_w\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mend\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 339\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mend\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/usr/local/Cellar/python@3.9/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/json/decoder.py\u001b[0m in \u001b[0;36mraw_decode\u001b[0;34m(self, s, idx)\u001b[0m\n\u001b[1;32m 353\u001b[0m \u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mend\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mscan_once\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0midx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 354\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mStopIteration\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 355\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mJSONDecodeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Expecting value\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 356\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mend\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mJSONDecodeError\u001b[0m: Expecting value: line 1 column 1 (char 0)" ] } ], "source": [ "histories_dict = load_histories(coin_ids, start_unix, end_unix)" ] }, { "cell_type": "code", "execution_count": 2, "id": "c687db6d-bfd8-4e43-9e7a-b03923eed314", "metadata": {}, "outputs": [], "source": [ "def date_conv(date):\n", " return datetime.strptime(date, '%Y-%m-%d')" ] }, { "cell_type": "code", "execution_count": 3, "id": "8a6a3771-f0f0-41dc-b34b-e9f5cd403dc4", "metadata": {}, "outputs": [], "source": [ "histories_df= pd.read_csv('histories.csv')\n", "histories_df['date'] = list(map(date_conv,histories_df['date']))\n", "histories_df = histories_df.set_index('date')" ] }, { "cell_type": "code", "execution_count": 4, "id": "93ccd49c-55d4-4984-a768-2d1d961adf7c", "metadata": {}, "outputs": [], "source": [ "def ids_with_histories(histories_df, start_date, end_date):\n", " investment_df = histories_df[start_date:end_date]\n", " investment_df.dropna(axis=1, inplace=True) # drop cols with any NaN values\n", " return investment_df.columns\n", "\n", "def uniform_weights_dict(ids_with_histories):\n", " weight = 1/len(ids_with_histories)\n", " uniform_weights_dict = {}\n", " for id in ids_with_histories:\n", " uniform_weights_dict[id] = weight\n", " return uniform_weights_dict\n", "\n", "\n", "def markowitz_weights_dict(histories_df,start_port_date,ids_with_histories, analysis_days=365):\n", " start_analysis_date = start_port_date - timedelta(analysis_days)\n", " analysis_df = histories_df[start_analysis_date:start_port_date][ids_with_histories]\n", "\n", " # Calculate expected returns and sample covariance\n", " mu = expected_returns.mean_historical_return(analysis_df)\n", " S = risk_models.sample_cov(analysis_df)\n", " # Optimize for maximal Sharpe ratio\n", " attempts=0\n", " while attempts < 10:\n", " try:\n", " ef = EfficientFrontier(mu, S, weight_bounds=(0, 1))\n", " ef.max_sharpe()\n", " break\n", " except Exception as e:\n", " attempts += 1\n", " try:\n", " cleaned_weights = ef.clean_weights()\n", " except Exception as e:\n", " print(\"Could not find optimal solution, try changing optimisation constraints or investment set\")\n", " return cleaned_weights\n", "\n", "\n", "def gen_port_rtns(rebased_df, weights_dict):\n", " return rebased_df[list(weights_dict.keys())].dot(list(weights_dict.values()))\n", "\n", "def gen_rebased_df(histories_df, ids_with_histories, start_date, end_date):\n", " returns_df = histories_df[ids_with_histories].pct_change(1)\n", " returns_df[start_date:start_date]=0\n", " return (1 + returns_df[start_date:end_date]).cumprod()" ] }, { "cell_type": "code", "execution_count": 5, "id": "6092ed85-0472-447d-90e5-ab0e357d1a5f", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/ff/pmf9d5156jz_pr_s8ybs3x780000gn/T/ipykernel_63357/2151836904.py:3: SettingWithCopyWarning: \n", "A value is trying to be set on a copy of a slice from a DataFrame\n", "\n", "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", " investment_df.dropna(axis=1, inplace=True) # drop cols with any NaN values\n" ] } ], "source": [ "lookback_years = 5\n", "start_date = date.today() - timedelta(365)\n", "end_date = date.today()\n", "ids_with_histories = ids_with_histories(histories_df,\n", " start_date, end_date)" ] }, { "cell_type": "code", "execution_count": 6, "id": "6044aecd-752f-4097-8598-2ca9b1693c2f", "metadata": {}, "outputs": [], "source": [ "uniform_weights_dict = uniform_weights_dict(ids_with_histories[:10])\n", "markowitz_weights_dict = markowitz_weights_dict(histories_df,\n", " start_date ,ids_with_histories[:10], analysis_days=365)\n", "rebased_df = gen_rebased_df(histories_df, ids_with_histories,\n", " start_date, end_date)" ] }, { "cell_type": "code", "execution_count": 9, "id": "0741b8f0-32f5-411a-a412-f40320297ef2", "metadata": {}, "outputs": [], "source": [ "def gen_all_returns(rebased_df, ids_with_histories,uniform_weights_dict,\n", " markowitz_weights_dict):\n", " '''\n", " A function to generate returns for all portfolios and all coins with full\n", " histories over the backtest period, rebased to the start of the backtest\n", " period.\n", " '''\n", " uniform_returns = gen_port_rtns(rebased_df, uniform_weights_dict)\n", " uniform_returns.name = \"Uniform\"\n", " markowitz_returns = gen_port_rtns(rebased_df, markowitz_weights_dict)\n", " markowitz_returns.name = \"Markowitz\"\n", " port_returns = uniform_returns.to_frame().join(markowitz_returns)\n", " return port_returns.join(rebased_df[ids_with_histories])" ] }, { "cell_type": "code", "execution_count": 10, "id": "0b67f3d3-ae6f-4d8e-b52c-1c4c7c18d662", "metadata": {}, "outputs": [], "source": [ "all_returns_df = gen_all_returns(rebased_df, ids_with_histories,uniform_weights_dict,\n", " markowitz_weights_dict)" ] }, { "cell_type": "code", "execution_count": 11, "id": "7dd39fac-3015-4024-a045-04f8a273ffec", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
UniformMarkowitzbitcoinethereumtetherusd-coinbinance-coinxrpbinance-usdcardano...arweavecompoundkavahologatetokenfei-protocolkyber-networkqtumbancor1inch
date
2021-05-241.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000...1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
2021-05-251.0571651.0157921.0353961.1040841.0043461.0083321.1177361.1418061.0042411.067167...1.0154321.0512281.0841181.2318731.1069471.0045241.0379041.2646761.0781221.064602
2021-05-261.1036991.0184141.0592621.1821780.9987640.9980871.2111521.1724560.9988921.180429...1.1572931.1429801.2167421.5082021.1674141.0018741.1536831.2885061.1730151.220068
2021-05-271.0940571.0163291.0470551.1713370.9987210.9977851.2098191.1516040.9988991.166563...1.1599351.1143191.2532481.4327461.1556250.9994511.1784371.4249981.1922241.226486
2021-05-281.0352211.0054360.9881501.0813100.9984000.9979461.1185471.0550650.9989321.070248...1.0117611.0078711.2185211.2691491.0531980.9947761.0728101.3362661.0951051.125980
\n", "

5 rows × 87 columns

\n", "
" ], "text/plain": [ " Uniform Markowitz bitcoin ethereum tether usd-coin \\\n", "date \n", "2021-05-24 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 \n", "2021-05-25 1.057165 1.015792 1.035396 1.104084 1.004346 1.008332 \n", "2021-05-26 1.103699 1.018414 1.059262 1.182178 0.998764 0.998087 \n", "2021-05-27 1.094057 1.016329 1.047055 1.171337 0.998721 0.997785 \n", "2021-05-28 1.035221 1.005436 0.988150 1.081310 0.998400 0.997946 \n", "\n", " binance-coin xrp binance-usd cardano ... arweave \\\n", "date ... \n", "2021-05-24 1.000000 1.000000 1.000000 1.000000 ... 1.000000 \n", "2021-05-25 1.117736 1.141806 1.004241 1.067167 ... 1.015432 \n", "2021-05-26 1.211152 1.172456 0.998892 1.180429 ... 1.157293 \n", "2021-05-27 1.209819 1.151604 0.998899 1.166563 ... 1.159935 \n", "2021-05-28 1.118547 1.055065 0.998932 1.070248 ... 1.011761 \n", "\n", " compound kava holo gatetoken fei-protocol \\\n", "date \n", "2021-05-24 1.000000 1.000000 1.000000 1.000000 1.000000 \n", "2021-05-25 1.051228 1.084118 1.231873 1.106947 1.004524 \n", "2021-05-26 1.142980 1.216742 1.508202 1.167414 1.001874 \n", "2021-05-27 1.114319 1.253248 1.432746 1.155625 0.999451 \n", "2021-05-28 1.007871 1.218521 1.269149 1.053198 0.994776 \n", "\n", " kyber-network qtum bancor 1inch \n", "date \n", "2021-05-24 1.000000 1.000000 1.000000 1.000000 \n", "2021-05-25 1.037904 1.264676 1.078122 1.064602 \n", "2021-05-26 1.153683 1.288506 1.173015 1.220068 \n", "2021-05-27 1.178437 1.424998 1.192224 1.226486 \n", "2021-05-28 1.072810 1.336266 1.095105 1.125980 \n", "\n", "[5 rows x 87 columns]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "all_returns_df.head()" ] }, { "cell_type": "code", "execution_count": 12, "id": "ebe9263f-1a3f-4dc8-ba19-732b83d30e50", "metadata": {}, "outputs": [], "source": [ "def absolute_return(prices):\n", " 'a function to calculate the absolute return given a daily price series'\n", " abs_rtn = ((prices.iloc[-1]/prices[0])-1)\n", " return abs_rtn\n", "\n", "def annual_return(prices):\n", " 'a function to calculate the annualised return given a daily price series'\n", " abs_rtn = absolute_return(prices)\n", " annual_rnt = (pow((abs_rtn/100)+1, 365/len(prices))-1)*100\n", " return annual_rnt\n", "\n", "def max_drawdown(prices):\n", " '''\n", " A function to calculate the max drawdown for a given price series \"prices\"\n", " as well as the index of the start of the max drawdown period, \"start_idx\"\n", " and the index of end of the max drawdwon period, \"end index\"\n", " '''\n", " if type(prices)==type(pd.Series(dtype='object')):\n", " prices = prices.values\n", " end_idx = np.argmax(np.maximum.accumulate(prices) - prices) # end of the period\n", " start_idx = np.argmax(prices[:end_idx]) # start of period\n", " max_dd = (prices[start_idx]-prices[end_idx])/prices[start_idx]\n", " return max_dd, start_idx, end_idx\n", "\n", "def annual_vol(prices):\n", " '''\n", " A function to calculate the annuaised volatility of a price series assuming\n", " cryptos trade 365 days a year\n", " '''\n", " return prices.pct_change().std()*(365**0.5)" ] }, { "cell_type": "code", "execution_count": 26, "id": "c7cfd009-dbe6-4afe-86a8-9706beb1f351", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 38.2 ms, sys: 22.1 ms, total: 60.3 ms\n", "Wall time: 677 ms\n" ] } ], "source": [ "%%time \n", "\n", "url = \"https://api.coincap.io/v2/assets\"\n", "\n", "# N.B. here adampt the params dict to only request what you need\n", "payload={'limit': '100'}\n", "headers = {}\n", "\n", "response_assets = requests.request(\"GET\", url, params=payload, headers=headers)\n", "assets_json = response_assets.json()" ] }, { "cell_type": "code", "execution_count": 51, "id": "9f8abf03-8214-498d-8cd1-778f77ff308e", "metadata": {}, "outputs": [], "source": [ "market_cap_dict = {}\n", "for asset_dict in assets_json['data']:\n", " market_cap_dict[asset_dict['id']] = int(float(asset_dict['marketCapUsd']))" ] }, { "cell_type": "code", "execution_count": 53, "id": "68a777da-401f-41cb-8485-688bd3d6df70", "metadata": {}, "outputs": [], "source": [ "assets = all_returns_df.columns\n", "performance_df = pd.DataFrame(index = assets)\n", "performance_df['Type'] = [\"Portfolio\" if x in ['Uniform','Markowitz'] else \"Coin\" for x in assets]\n", "abs_return = all_returns_df.apply(absolute_return)\n", "ann_vol = all_returns_df.apply(annual_vol)\n", "drawdown_triples = all_returns_df.apply(max_drawdown)\n", "sharpe = abs_return.divide(ann_vol)\n", "market_caps=[]\n", "for asset in assets:\n", " try:\n", " market_caps.append(int(market_cap_dict[asset]))\n", " except:\n", " market_caps.append(0)\n", "performance_df['Risk adjusted return'] = sharpe *100\n", "performance_df['Return over period'] = abs_return * 100\n", "performance_df['Annual volatility'] = ann_vol *100\n", "performance_df['Max loss'] = drawdown_triples.iloc[0] *100\n", "performance_df['Market cap'] = market_caps" ] }, { "cell_type": "code", "execution_count": 55, "id": "3bf84fd6-795b-4e06-b82b-bdf4e0714224", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
TypeRisk adjusted returnReturn over periodAnnual volatilityMax lossMarket cap
UniformPortfolio-22.24-12.2054.8462.390
MarkowitzPortfolio-32.44-4.1012.6520.490
bitcoinCoin-32.53-18.4256.6157.98564237420636
ethereumCoin-18.21-12.5969.1159.35238817158476
tetherCoin-20.42-0.351.741.3873268815333
\n", "
" ], "text/plain": [ " Type Risk adjusted return Return over period \\\n", "Uniform Portfolio -22.24 -12.20 \n", "Markowitz Portfolio -32.44 -4.10 \n", "bitcoin Coin -32.53 -18.42 \n", "ethereum Coin -18.21 -12.59 \n", "tether Coin -20.42 -0.35 \n", "\n", " Annual volatility Max loss Market cap \n", "Uniform 54.84 62.39 0 \n", "Markowitz 12.65 20.49 0 \n", "bitcoin 56.61 57.98 564237420636 \n", "ethereum 69.11 59.35 238817158476 \n", "tether 1.74 1.38 73268815333 " ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "performance_df.round(2).head()\n", " " ] }, { "cell_type": "code", "execution_count": 48, "id": "62360da2-742f-418d-b071-2f9256855341", "metadata": {}, "outputs": [], "source": [ "market_caps=[]\n", "for asset in assets:\n", " try:\n", " market_caps.append(int(market_cap_dict[asset]))\n", " except:\n", " market_caps.append(0)" ] }, { "cell_type": "code", "execution_count": 81, "id": "8c4e6c8b-5496-4ea7-b599-370fab33f4e1", "metadata": { "tags": [] }, "outputs": [ { "ename": "ValueError", "evalue": "['Uniform', 'bitcoin'] is not in list", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/ff/pmf9d5156jz_pr_s8ybs3x780000gn/T/ipykernel_63357/545937437.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mall_returns_df\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcolumns\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'Uniform'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'bitcoin'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: ['Uniform', 'bitcoin'] is not in list" ] } ], "source": [ "list(all_returns_df.columns).index(['Uniform','bitcoin'])" ] }, { "cell_type": "code", "execution_count": 82, "id": "4c8bdb96-620f-418d-8d76-3cb7d633168c", "metadata": {}, "outputs": [], "source": [ "N = [i for i in range(len(all_returns_df.columns)) if all_returns_df.columns[i] in ['Uniform','bitcoin']]" ] }, { "cell_type": "code", "execution_count": 83, "id": "f21e4471-a78d-42d9-8d44-782a36b80931", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 2]" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "N" ] }, { "cell_type": "code", "execution_count": 95, "id": "6cce98be-4c73-4b2b-98d2-4f995e3be60f", "metadata": {}, "outputs": [], "source": [ "dic={'a':1, 'b':2}" ] }, { "cell_type": "code", "execution_count": 98, "id": "37c4c19f-21ec-4817-a142-cde2b767a179", "metadata": {}, "outputs": [ { "ename": "SyntaxError", "evalue": "invalid syntax (1228105859.py, line 1)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"/var/folders/ff/pmf9d5156jz_pr_s8ybs3x780000gn/T/ipykernel_63357/1228105859.py\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m for temp in del dic['a']:\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], "source": [ "for temp in del dic['a']:\n", " print('yes')" ] }, { "cell_type": "code", "execution_count": 101, "id": "72199468-c241-4550-9686-c4349c7c0734", "metadata": {}, "outputs": [], "source": [ "del dic['b']" ] }, { "cell_type": "code", "execution_count": 104, "id": "09e95fe9-5000-419e-be53-686ca0e88a12", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(dic)!=0" ] }, { "cell_type": "code", "execution_count": 108, "id": "46b8e046-810c-4284-be8b-fb24fcf46588", "metadata": {}, "outputs": [], "source": [ "strategy_dict = {'Uniform': {'a':2}, 'Markowitz':{'b':3}}" ] }, { "cell_type": "code", "execution_count": 113, "id": "770e265a-d31d-43bd-a7f6-053a1f9cdcf3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Uniform\n", "Markowitz\n" ] } ], "source": [ "for name, weights in strategy_dict.items():\n", " print(name)" ] }, { "cell_type": "code", "execution_count": 114, "id": "ed0aea65-24c0-48a2-a957-0b1a1a4b518d", "metadata": {}, "outputs": [], "source": [ "port_returns = gen_port_rtns(rebased_df, uniform_weights_dict)" ] }, { "cell_type": "code", "execution_count": 115, "id": "72ec929c-629e-4e3c-841a-9fcae6610d2c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "date\n", "2021-05-24 1.000000\n", "2021-05-25 1.057165\n", "2021-05-26 1.103699\n", "2021-05-27 1.094057\n", "2021-05-28 1.035221\n", " ... \n", "2022-05-13 0.858603\n", "2022-05-14 0.842769\n", "2022-05-15 0.863536\n", "2022-05-16 0.868936\n", "2022-05-17 0.878021\n", "Length: 359, dtype: float64" ] }, "execution_count": 115, "metadata": {}, "output_type": "execute_result" } ], "source": [ "port_returns" ] }, { "cell_type": "code", "execution_count": 116, "id": "b0694bbc-9941-41a4-bf49-96d581f82907", "metadata": {}, "outputs": [], "source": [ "port_returns = pd.DataFrame({'Uniform': port_returns})" ] }, { "cell_type": "code", "execution_count": 117, "id": "02b1c5fd-0b51-4b4d-9bf1-f5dcf46477a1", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Uniform
date
2021-05-241.000000
2021-05-251.057165
2021-05-261.103699
2021-05-271.094057
2021-05-281.035221
......
2022-05-130.858603
2022-05-140.842769
2022-05-150.863536
2022-05-160.868936
2022-05-170.878021
\n", "

359 rows × 1 columns

\n", "
" ], "text/plain": [ " Uniform\n", "date \n", "2021-05-24 1.000000\n", "2021-05-25 1.057165\n", "2021-05-26 1.103699\n", "2021-05-27 1.094057\n", "2021-05-28 1.035221\n", "... ...\n", "2022-05-13 0.858603\n", "2022-05-14 0.842769\n", "2022-05-15 0.863536\n", "2022-05-16 0.868936\n", "2022-05-17 0.878021\n", "\n", "[359 rows x 1 columns]" ] }, "execution_count": 117, "metadata": {}, "output_type": "execute_result" } ], "source": [ "port_returns" ] }, { "cell_type": "code", "execution_count": 118, "id": "9f276fcd-b0aa-4faf-980d-3766737d4e35", "metadata": {}, "outputs": [], "source": [ "list1 = ['a','b', 'c']\n", "list2=['a','b']" ] }, { "cell_type": "code", "execution_count": 121, "id": "9d95a028-39f4-4bc4-b15e-1ac98c6c8242", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['a', 'b']" ] }, "execution_count": 121, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x for x in list1 if x in list2]" ] }, { "cell_type": "code", "execution_count": 122, "id": "ef331045-44ad-4ff5-8ba7-3b4584a818c5", "metadata": {}, "outputs": [], "source": [ "dic = {'a':1, 'b':2}" ] }, { "cell_type": "code", "execution_count": 123, "id": "087dc4d9-b6b5-4cd5-9c76-b227fc56e31c", "metadata": {}, "outputs": [], "source": [ "dic['a'] =3" ] }, { "cell_type": "code", "execution_count": 124, "id": "688edc89-974a-4562-81db-0bc30798c30c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'a': 3, 'b': 2}" ] }, "execution_count": 124, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dic" ] }, { "cell_type": "code", "execution_count": null, "id": "6ef4a1cb-d075-4d09-8038-caf04767565b", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.5" } }, "nbformat": 4, "nbformat_minor": 5 }