diff --git a/Cole Wins Above Replacement Portfolio.ipynb b/Cole Wins Above Replacement Portfolio.ipynb
index f8fae6a..334a26c 100644
--- a/Cole Wins Above Replacement Portfolio.ipynb
+++ b/Cole Wins Above Replacement Portfolio.ipynb
@@ -460,7 +460,7 @@
"CWARP_W_String=str('CWARP_'+percentage_weight_asset+'_asset')\n",
"\n",
"risk_ret_df=pd.DataFrame(index=['Start_Date','End_Date','CWARP','+Sortino','+Ret_To_MaxDD','Sharpe','Sortino','Max_DD'],columns=ticker_list)\n",
- "new_risk_ret_df=pd.DataFrame(index=['Return','Vol','Sharpe','Sortino','Max_DD','Ret_To_MaxDD','CWARP_25%_asset'],columns=ticker_list)\n",
+ "new_risk_ret_df=pd.DataFrame(index=['Return','Vol','Sharpe','Sortino','Max_DD','Ret_To_MaxDD',CWARP_W_String],columns=ticker_list)\n",
"new_risk_ret_df=new_risk_ret_df.add_suffix('@'+percentage_weight_asset+' | '+replacement_port.name+'@'+percentage_weight_replace_port)\n",
"new_risk_ret_df[replacement_port.name]=np.nan\n",
"\n",
@@ -468,12 +468,12 @@
"\n",
"# evaluate metrics for all tickers in list above\n",
"for i in range(0,len(ticker_list)):\n",
- " temp_data=retrieve_yhoo_data(ticker_list[i])\n",
+ " temp_data=retrieve_yhoo_data(ticker_list[i], start_date, end_date)\n",
" new_port = cwarp_new_port_data(new_asset=temp_data,replace_port=replacement_port,financing_rate=financing_rate,\n",
" risk_free_rate=risk_free_rate,weight_asset=weight_asset,weight_replace_port=weight_replace_port,periodicity=periodicity)\n",
" prices_df=pd.merge(prices_df,temp_data, left_index=True, right_index=True)\n",
- " risk_ret_df.loc['Start_Date',ticker_list[i]]=min(temp_data.index)\n",
- " risk_ret_df.loc['End_Date',ticker_list[i]]=max(temp_data.index)\n",
+ " risk_ret_df.loc['Start_Date',ticker_list[i]]=min(temp_data.index).date()\n",
+ " risk_ret_df.loc['End_Date',ticker_list[i]]=max(temp_data.index).date()\n",
" \n",
" risk_ret_df.loc['CWARP',ticker_list[i]]=cole_win_above_replace_port(new_asset=temp_data,replace_port=replacement_port,financing_rate=financing_rate,\n",
" risk_free_rate=risk_free_rate,weight_asset=weight_asset,weight_replace_port=weight_replace_port,periodicity=periodicity)\n",
@@ -532,231 +532,148 @@
{
"data": {
"text/html": [
- "
\n",
- "\n",
- "
\n",
- " \n",
- " \n",
- " | \n",
- " tlt | \n",
- " ief | \n",
- " gld | \n",
- " lqd | \n",
- " shy | \n",
- " qqq | \n",
- " xlk | \n",
- " slv | \n",
- " hyg | \n",
- " iyr | \n",
- " eem | \n",
- " efa | \n",
- " xle | \n",
- " xlf | \n",
- "
\n",
- " \n",
- " \n",
- " \n",
- " Start_Date | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- " 2007-07-02 00:00:00 | \n",
- "
\n",
- " \n",
- " End_Date | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- " 2020-12-30 00:00:00 | \n",
- "
\n",
- " \n",
- " CWARP | \n",
- " 35.075059 | \n",
- " 21.355028 | \n",
- " 19.86268 | \n",
- " 10.370056 | \n",
- " 4.964182 | \n",
- " 3.054918 | \n",
- " 0.176191 | \n",
- " -3.105972 | \n",
- " -5.412795 | \n",
- " -23.615039 | \n",
- " -24.563182 | \n",
- " -27.322877 | \n",
- " -33.078864 | \n",
- " -33.273558 | \n",
- "
\n",
- " \n",
- " +Sortino | \n",
- " 29.874918 | \n",
- " 18.216353 | \n",
- " 13.873923 | \n",
- " 9.440441 | \n",
- " 3.734666 | \n",
- " 2.767763 | \n",
- " -0.26814 | \n",
- " -6.900377 | \n",
- " -3.632828 | \n",
- " -21.710574 | \n",
- " -24.392485 | \n",
- " -24.57957 | \n",
- " -34.119457 | \n",
- " -27.888017 | \n",
- "
\n",
- " \n",
- " +Ret_To_MaxDD | \n",
- " 40.483412 | \n",
- " 24.577036 | \n",
- " 26.166392 | \n",
- " 11.307567 | \n",
- " 6.208272 | \n",
- " 3.342876 | \n",
- " 0.622501 | \n",
- " 0.84308 | \n",
- " -7.159884 | \n",
- " -25.473176 | \n",
- " -24.733494 | \n",
- " -29.9664 | \n",
- " -32.021834 | \n",
- " -38.256891 | \n",
- "
\n",
- " \n",
- " Sharpe | \n",
- " 0.544866 | \n",
- " 0.746821 | \n",
- " 0.473668 | \n",
- " 0.663613 | \n",
- " 1.049695 | \n",
- " 0.751663 | \n",
- " 0.684662 | \n",
- " 0.302802 | \n",
- " 0.464621 | \n",
- " 0.28593 | \n",
- " 0.239333 | \n",
- " 0.192215 | \n",
- " 0.091022 | \n",
- " 0.211968 | \n",
- "
\n",
- " \n",
- " Sortino | \n",
- " 0.790751 | \n",
- " 1.100768 | \n",
- " 0.672288 | \n",
- " 0.942979 | \n",
- " 1.615862 | \n",
- " 1.065266 | \n",
- " 0.973925 | \n",
- " 0.414236 | \n",
- " 0.669127 | \n",
- " 0.405208 | \n",
- " 0.344397 | \n",
- " 0.266824 | \n",
- " 0.126671 | \n",
- " 0.306036 | \n",
- "
\n",
- " \n",
- " Max_DD | \n",
- " 0.265854 | \n",
- " 0.104014 | \n",
- " 0.45555 | \n",
- " 0.217621 | \n",
- " 0.022314 | \n",
- " 0.53404 | \n",
- " 0.530358 | \n",
- " 0.762802 | \n",
- " 0.342465 | \n",
- " 0.705002 | \n",
- " 0.664343 | \n",
- " 0.610373 | \n",
- " 0.712628 | \n",
- " 0.822147 | \n",
- "
\n",
- " \n",
- "
\n",
- "
"
+ " | tlt | ief | gld | lqd | shy | qqq | xlk | slv | hyg | iyr | eem | efa | xle | xlf |
\n",
+ " \n",
+ " Start_Date | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ " 2007-07-02 | \n",
+ "
\n",
+ " \n",
+ " End_Date | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ " 2020-12-30 | \n",
+ "
\n",
+ " \n",
+ " CWARP | \n",
+ " 35.075 | \n",
+ " 21.355 | \n",
+ " 19.863 | \n",
+ " 10.370 | \n",
+ " 4.964 | \n",
+ " 3.055 | \n",
+ " 0.176 | \n",
+ " -3.106 | \n",
+ " -5.413 | \n",
+ " -23.615 | \n",
+ " -24.563 | \n",
+ " -27.323 | \n",
+ " -33.079 | \n",
+ " -33.274 | \n",
+ "
\n",
+ " \n",
+ " +Sortino | \n",
+ " 29.875 | \n",
+ " 18.216 | \n",
+ " 13.874 | \n",
+ " 9.440 | \n",
+ " 3.735 | \n",
+ " 2.768 | \n",
+ " -0.268 | \n",
+ " -6.900 | \n",
+ " -3.633 | \n",
+ " -21.711 | \n",
+ " -24.392 | \n",
+ " -24.580 | \n",
+ " -34.119 | \n",
+ " -27.888 | \n",
+ "
\n",
+ " \n",
+ " +Ret_To_MaxDD | \n",
+ " 40.483 | \n",
+ " 24.577 | \n",
+ " 26.166 | \n",
+ " 11.308 | \n",
+ " 6.208 | \n",
+ " 3.343 | \n",
+ " 0.623 | \n",
+ " 0.843 | \n",
+ " -7.160 | \n",
+ " -25.473 | \n",
+ " -24.733 | \n",
+ " -29.966 | \n",
+ " -32.022 | \n",
+ " -38.257 | \n",
+ "
\n",
+ " \n",
+ " Sharpe | \n",
+ " 0.545 | \n",
+ " 0.747 | \n",
+ " 0.474 | \n",
+ " 0.664 | \n",
+ " 1.050 | \n",
+ " 0.752 | \n",
+ " 0.685 | \n",
+ " 0.303 | \n",
+ " 0.465 | \n",
+ " 0.286 | \n",
+ " 0.239 | \n",
+ " 0.192 | \n",
+ " 0.091 | \n",
+ " 0.212 | \n",
+ "
\n",
+ " \n",
+ " Sortino | \n",
+ " 0.791 | \n",
+ " 1.101 | \n",
+ " 0.672 | \n",
+ " 0.943 | \n",
+ " 1.616 | \n",
+ " 1.065 | \n",
+ " 0.974 | \n",
+ " 0.414 | \n",
+ " 0.669 | \n",
+ " 0.405 | \n",
+ " 0.344 | \n",
+ " 0.267 | \n",
+ " 0.127 | \n",
+ " 0.306 | \n",
+ "
\n",
+ " \n",
+ " Max_DD | \n",
+ " 0.266 | \n",
+ " 0.104 | \n",
+ " 0.456 | \n",
+ " 0.218 | \n",
+ " 0.022 | \n",
+ " 0.534 | \n",
+ " 0.530 | \n",
+ " 0.763 | \n",
+ " 0.342 | \n",
+ " 0.705 | \n",
+ " 0.664 | \n",
+ " 0.610 | \n",
+ " 0.713 | \n",
+ " 0.822 | \n",
+ "
\n",
+ "
"
],
"text/plain": [
- " tlt ief gld \\\n",
- "Start_Date 2007-07-02 00:00:00 2007-07-02 00:00:00 2007-07-02 00:00:00 \n",
- "End_Date 2020-12-30 00:00:00 2020-12-30 00:00:00 2020-12-30 00:00:00 \n",
- "CWARP 35.075059 21.355028 19.86268 \n",
- "+Sortino 29.874918 18.216353 13.873923 \n",
- "+Ret_To_MaxDD 40.483412 24.577036 26.166392 \n",
- "Sharpe 0.544866 0.746821 0.473668 \n",
- "Sortino 0.790751 1.100768 0.672288 \n",
- "Max_DD 0.265854 0.104014 0.45555 \n",
- "\n",
- " lqd shy qqq \\\n",
- "Start_Date 2007-07-02 00:00:00 2007-07-02 00:00:00 2007-07-02 00:00:00 \n",
- "End_Date 2020-12-30 00:00:00 2020-12-30 00:00:00 2020-12-30 00:00:00 \n",
- "CWARP 10.370056 4.964182 3.054918 \n",
- "+Sortino 9.440441 3.734666 2.767763 \n",
- "+Ret_To_MaxDD 11.307567 6.208272 3.342876 \n",
- "Sharpe 0.663613 1.049695 0.751663 \n",
- "Sortino 0.942979 1.615862 1.065266 \n",
- "Max_DD 0.217621 0.022314 0.53404 \n",
- "\n",
- " xlk slv hyg \\\n",
- "Start_Date 2007-07-02 00:00:00 2007-07-02 00:00:00 2007-07-02 00:00:00 \n",
- "End_Date 2020-12-30 00:00:00 2020-12-30 00:00:00 2020-12-30 00:00:00 \n",
- "CWARP 0.176191 -3.105972 -5.412795 \n",
- "+Sortino -0.26814 -6.900377 -3.632828 \n",
- "+Ret_To_MaxDD 0.622501 0.84308 -7.159884 \n",
- "Sharpe 0.684662 0.302802 0.464621 \n",
- "Sortino 0.973925 0.414236 0.669127 \n",
- "Max_DD 0.530358 0.762802 0.342465 \n",
- "\n",
- " iyr eem efa \\\n",
- "Start_Date 2007-07-02 00:00:00 2007-07-02 00:00:00 2007-07-02 00:00:00 \n",
- "End_Date 2020-12-30 00:00:00 2020-12-30 00:00:00 2020-12-30 00:00:00 \n",
- "CWARP -23.615039 -24.563182 -27.322877 \n",
- "+Sortino -21.710574 -24.392485 -24.57957 \n",
- "+Ret_To_MaxDD -25.473176 -24.733494 -29.9664 \n",
- "Sharpe 0.28593 0.239333 0.192215 \n",
- "Sortino 0.405208 0.344397 0.266824 \n",
- "Max_DD 0.705002 0.664343 0.610373 \n",
- "\n",
- " xle xlf \n",
- "Start_Date 2007-07-02 00:00:00 2007-07-02 00:00:00 \n",
- "End_Date 2020-12-30 00:00:00 2020-12-30 00:00:00 \n",
- "CWARP -33.078864 -33.273558 \n",
- "+Sortino -34.119457 -27.888017 \n",
- "+Ret_To_MaxDD -32.021834 -38.256891 \n",
- "Sharpe 0.091022 0.211968 \n",
- "Sortino 0.126671 0.306036 \n",
- "Max_DD 0.712628 0.822147 "
+ ""
]
},
"execution_count": 8,
@@ -765,7 +682,7 @@
}
],
"source": [
- "risk_ret_df.sort_values(by='CWARP', axis=1, ascending=False)"
+ "risk_ret_df.sort_values(by='CWARP', axis=1, ascending=False).style.set_precision(3)"
]
},
{
@@ -778,249 +695,145 @@
{
"cell_type": "code",
"execution_count": 9,
- "metadata": {},
+ "metadata": {
+ "scrolled": true
+ },
"outputs": [
{
"data": {
"text/html": [
- "\n",
- "\n",
- "
\n",
- " \n",
- " \n",
- " | \n",
- " tlt@25% | Classic 60/40@100% | \n",
- " ief@25% | Classic 60/40@100% | \n",
- " gld@25% | Classic 60/40@100% | \n",
- " lqd@25% | Classic 60/40@100% | \n",
- " shy@25% | Classic 60/40@100% | \n",
- " qqq@25% | Classic 60/40@100% | \n",
- " Classic 60/40 | \n",
- " xlk@25% | Classic 60/40@100% | \n",
- " hyg@25% | Classic 60/40@100% | \n",
- " slv@25% | Classic 60/40@100% | \n",
- " iyr@25% | Classic 60/40@100% | \n",
- " efa@25% | Classic 60/40@100% | \n",
- " eem@25% | Classic 60/40@100% | \n",
- " xlf@25% | Classic 60/40@100% | \n",
- " xle@25% | Classic 60/40@100% | \n",
- "
\n",
- " \n",
- " \n",
- " \n",
- " Return | \n",
- " 0.100618 | \n",
- " 0.091829 | \n",
- " 0.099673 | \n",
- " 0.093002 | \n",
- " 0.081617 | \n",
- " 0.115787 | \n",
- " 0.084037 | \n",
- " 0.112338 | \n",
- " 0.089942 | \n",
- " 0.098068 | \n",
- " 0.091202 | \n",
- " 0.081532 | \n",
- " 0.086401 | \n",
- " 0.084476 | \n",
- " 0.074215 | \n",
- "
\n",
- " \n",
- " Vol | \n",
- " 0.077024 | \n",
- " 0.078078 | \n",
- " 0.088729 | \n",
- " 0.086457 | \n",
- " 0.080336 | \n",
- " 0.117915 | \n",
- " 0.081088 | \n",
- " 0.118684 | \n",
- " 0.097239 | \n",
- " 0.111243 | \n",
- " 0.130806 | \n",
- " 0.120742 | \n",
- " 0.129702 | \n",
- " 0.135409 | \n",
- " 0.132069 | \n",
- "
\n",
- " \n",
- " Sharpe | \n",
- " 0.909868 | \n",
- " 0.831409 | \n",
- " 0.806098 | \n",
- " 0.777532 | \n",
- " 0.73672 | \n",
- " 0.733642 | \n",
- " 0.711829 | \n",
- " 0.710859 | \n",
- " 0.690471 | \n",
- " 0.676461 | \n",
- " 0.561602 | \n",
- " 0.542909 | \n",
- " 0.537379 | \n",
- " 0.515902 | \n",
- " 0.477671 | \n",
- "
\n",
- " \n",
- " Sortino | \n",
- " 1.32049 | \n",
- " 1.201953 | \n",
- " 1.157802 | \n",
- " 1.112725 | \n",
- " 1.054712 | \n",
- " 1.044881 | \n",
- " 1.016740 | \n",
- " 1.014014 | \n",
- " 0.979803 | \n",
- " 0.946581 | \n",
- " 0.796 | \n",
- " 0.76683 | \n",
- " 0.768732 | \n",
- " 0.733191 | \n",
- " 0.669834 | \n",
- "
\n",
- " \n",
- " Max_DD | \n",
- " 0.280871 | \n",
- " 0.290371 | \n",
- " 0.309944 | \n",
- " 0.328926 | \n",
- " 0.304665 | \n",
- " 0.436661 | \n",
- " 0.313935 | \n",
- " 0.435658 | \n",
- " 0.382038 | \n",
- " 0.38183 | \n",
- " 0.482234 | \n",
- " 0.46158 | \n",
- " 0.453658 | \n",
- " 0.541374 | \n",
- " 0.435315 | \n",
- "
\n",
- " \n",
- " Ret_To_MaxDD | \n",
- " 0.375968 | \n",
- " 0.333399 | \n",
- " 0.337652 | \n",
- " 0.297886 | \n",
- " 0.284239 | \n",
- " 0.276571 | \n",
- " 0.267624 | \n",
- " 0.26929 | \n",
- " 0.248463 | \n",
- " 0.269881 | \n",
- " 0.199452 | \n",
- " 0.187427 | \n",
- " 0.201432 | \n",
- " 0.16524 | \n",
- " 0.181926 | \n",
- "
\n",
- " \n",
- " CWARP_25%_asset | \n",
- " 35.075059 | \n",
- " 21.355028 | \n",
- " 19.86268 | \n",
- " 10.370056 | \n",
- " 4.964182 | \n",
- " 3.054918 | \n",
- " -1.407647 | \n",
- " 0.176191 | \n",
- " -5.412795 | \n",
- " -3.105972 | \n",
- " -23.615039 | \n",
- " -27.322877 | \n",
- " -24.563182 | \n",
- " -33.273558 | \n",
- " -33.078864 | \n",
- "
\n",
- " \n",
- "
\n",
- "
"
+ " | tlt@25% | Classic 60/40@100% | ief@25% | Classic 60/40@100% | gld@25% | Classic 60/40@100% | lqd@25% | Classic 60/40@100% | shy@25% | Classic 60/40@100% | qqq@25% | Classic 60/40@100% | Classic 60/40 | xlk@25% | Classic 60/40@100% | hyg@25% | Classic 60/40@100% | slv@25% | Classic 60/40@100% | iyr@25% | Classic 60/40@100% | efa@25% | Classic 60/40@100% | eem@25% | Classic 60/40@100% | xlf@25% | Classic 60/40@100% | xle@25% | Classic 60/40@100% |
\n",
+ " \n",
+ " Return | \n",
+ " 0.101 | \n",
+ " 0.092 | \n",
+ " 0.100 | \n",
+ " 0.093 | \n",
+ " 0.082 | \n",
+ " 0.116 | \n",
+ " 0.084 | \n",
+ " 0.112 | \n",
+ " 0.090 | \n",
+ " 0.098 | \n",
+ " 0.091 | \n",
+ " 0.082 | \n",
+ " 0.086 | \n",
+ " 0.084 | \n",
+ " 0.074 | \n",
+ "
\n",
+ " \n",
+ " Vol | \n",
+ " 0.077 | \n",
+ " 0.078 | \n",
+ " 0.089 | \n",
+ " 0.086 | \n",
+ " 0.080 | \n",
+ " 0.118 | \n",
+ " 0.081 | \n",
+ " 0.119 | \n",
+ " 0.097 | \n",
+ " 0.111 | \n",
+ " 0.131 | \n",
+ " 0.121 | \n",
+ " 0.130 | \n",
+ " 0.135 | \n",
+ " 0.132 | \n",
+ "
\n",
+ " \n",
+ " Sharpe | \n",
+ " 0.910 | \n",
+ " 0.831 | \n",
+ " 0.806 | \n",
+ " 0.778 | \n",
+ " 0.737 | \n",
+ " 0.734 | \n",
+ " 0.712 | \n",
+ " 0.711 | \n",
+ " 0.690 | \n",
+ " 0.676 | \n",
+ " 0.562 | \n",
+ " 0.543 | \n",
+ " 0.537 | \n",
+ " 0.516 | \n",
+ " 0.478 | \n",
+ "
\n",
+ " \n",
+ " Sortino | \n",
+ " 1.320 | \n",
+ " 1.202 | \n",
+ " 1.158 | \n",
+ " 1.113 | \n",
+ " 1.055 | \n",
+ " 1.045 | \n",
+ " 1.017 | \n",
+ " 1.014 | \n",
+ " 0.980 | \n",
+ " 0.947 | \n",
+ " 0.796 | \n",
+ " 0.767 | \n",
+ " 0.769 | \n",
+ " 0.733 | \n",
+ " 0.670 | \n",
+ "
\n",
+ " \n",
+ " Max_DD | \n",
+ " 0.281 | \n",
+ " 0.290 | \n",
+ " 0.310 | \n",
+ " 0.329 | \n",
+ " 0.305 | \n",
+ " 0.437 | \n",
+ " 0.314 | \n",
+ " 0.436 | \n",
+ " 0.382 | \n",
+ " 0.382 | \n",
+ " 0.482 | \n",
+ " 0.462 | \n",
+ " 0.454 | \n",
+ " 0.541 | \n",
+ " 0.435 | \n",
+ "
\n",
+ " \n",
+ " Ret_To_MaxDD | \n",
+ " 0.376 | \n",
+ " 0.333 | \n",
+ " 0.338 | \n",
+ " 0.298 | \n",
+ " 0.284 | \n",
+ " 0.277 | \n",
+ " 0.268 | \n",
+ " 0.269 | \n",
+ " 0.248 | \n",
+ " 0.270 | \n",
+ " 0.199 | \n",
+ " 0.187 | \n",
+ " 0.201 | \n",
+ " 0.165 | \n",
+ " 0.182 | \n",
+ "
\n",
+ " \n",
+ " CWARP_25%_asset | \n",
+ " 35.075 | \n",
+ " 21.355 | \n",
+ " 19.863 | \n",
+ " 10.370 | \n",
+ " 4.964 | \n",
+ " 3.055 | \n",
+ " -1.408 | \n",
+ " 0.176 | \n",
+ " -5.413 | \n",
+ " -3.106 | \n",
+ " -23.615 | \n",
+ " -27.323 | \n",
+ " -24.563 | \n",
+ " -33.274 | \n",
+ " -33.079 | \n",
+ "
\n",
+ "
"
],
"text/plain": [
- " tlt@25% | Classic 60/40@100% ief@25% | Classic 60/40@100% \\\n",
- "Return 0.100618 0.091829 \n",
- "Vol 0.077024 0.078078 \n",
- "Sharpe 0.909868 0.831409 \n",
- "Sortino 1.32049 1.201953 \n",
- "Max_DD 0.280871 0.290371 \n",
- "Ret_To_MaxDD 0.375968 0.333399 \n",
- "CWARP_25%_asset 35.075059 21.355028 \n",
- "\n",
- " gld@25% | Classic 60/40@100% lqd@25% | Classic 60/40@100% \\\n",
- "Return 0.099673 0.093002 \n",
- "Vol 0.088729 0.086457 \n",
- "Sharpe 0.806098 0.777532 \n",
- "Sortino 1.157802 1.112725 \n",
- "Max_DD 0.309944 0.328926 \n",
- "Ret_To_MaxDD 0.337652 0.297886 \n",
- "CWARP_25%_asset 19.86268 10.370056 \n",
- "\n",
- " shy@25% | Classic 60/40@100% qqq@25% | Classic 60/40@100% \\\n",
- "Return 0.081617 0.115787 \n",
- "Vol 0.080336 0.117915 \n",
- "Sharpe 0.73672 0.733642 \n",
- "Sortino 1.054712 1.044881 \n",
- "Max_DD 0.304665 0.436661 \n",
- "Ret_To_MaxDD 0.284239 0.276571 \n",
- "CWARP_25%_asset 4.964182 3.054918 \n",
- "\n",
- " Classic 60/40 xlk@25% | Classic 60/40@100% \\\n",
- "Return 0.084037 0.112338 \n",
- "Vol 0.081088 0.118684 \n",
- "Sharpe 0.711829 0.710859 \n",
- "Sortino 1.016740 1.014014 \n",
- "Max_DD 0.313935 0.435658 \n",
- "Ret_To_MaxDD 0.267624 0.26929 \n",
- "CWARP_25%_asset -1.407647 0.176191 \n",
- "\n",
- " hyg@25% | Classic 60/40@100% slv@25% | Classic 60/40@100% \\\n",
- "Return 0.089942 0.098068 \n",
- "Vol 0.097239 0.111243 \n",
- "Sharpe 0.690471 0.676461 \n",
- "Sortino 0.979803 0.946581 \n",
- "Max_DD 0.382038 0.38183 \n",
- "Ret_To_MaxDD 0.248463 0.269881 \n",
- "CWARP_25%_asset -5.412795 -3.105972 \n",
- "\n",
- " iyr@25% | Classic 60/40@100% efa@25% | Classic 60/40@100% \\\n",
- "Return 0.091202 0.081532 \n",
- "Vol 0.130806 0.120742 \n",
- "Sharpe 0.561602 0.542909 \n",
- "Sortino 0.796 0.76683 \n",
- "Max_DD 0.482234 0.46158 \n",
- "Ret_To_MaxDD 0.199452 0.187427 \n",
- "CWARP_25%_asset -23.615039 -27.322877 \n",
- "\n",
- " eem@25% | Classic 60/40@100% xlf@25% | Classic 60/40@100% \\\n",
- "Return 0.086401 0.084476 \n",
- "Vol 0.129702 0.135409 \n",
- "Sharpe 0.537379 0.515902 \n",
- "Sortino 0.768732 0.733191 \n",
- "Max_DD 0.453658 0.541374 \n",
- "Ret_To_MaxDD 0.201432 0.16524 \n",
- "CWARP_25%_asset -24.563182 -33.273558 \n",
- "\n",
- " xle@25% | Classic 60/40@100% \n",
- "Return 0.074215 \n",
- "Vol 0.132069 \n",
- "Sharpe 0.477671 \n",
- "Sortino 0.669834 \n",
- "Max_DD 0.435315 \n",
- "Ret_To_MaxDD 0.181926 \n",
- "CWARP_25%_asset -33.078864 "
+ ""
]
},
"execution_count": 9,
@@ -1029,7 +842,7 @@
}
],
"source": [
- "new_risk_ret_df.sort_values(by='Sharpe', axis=1, ascending=False)"
+ "new_risk_ret_df.sort_values(by='Sharpe', axis=1, ascending=False).style.set_precision(3)"
]
},
{
@@ -1042,7 +855,9 @@
{
"cell_type": "code",
"execution_count": 10,
- "metadata": {},
+ "metadata": {
+ "scrolled": false
+ },
"outputs": [
{
"data": {
diff --git a/cwarp_app.py b/cwarp_app.py
index 11c9160..f3547aa 100644
--- a/cwarp_app.py
+++ b/cwarp_app.py
@@ -66,12 +66,13 @@ def main():
try:
start_date = st.sidebar.date_input("Start Date", datetime.date(2007,7,1)).strftime("%Y-%m-%d")
end_date = st.sidebar.date_input("End Date", datetime.date(2020,12,31)).strftime("%Y-%m-%d")
- weight_asset = st.sidebar.slider('Diversifier Weight', min_value=0.0001, max_value=0.9999, value=.25)
- risk_free_rate = st.sidebar.slider('Risk-Free Rate (annualized)', min_value=0.0, max_value=0.2, value=0.027)
- financing_rate = st.sidebar.slider('Financing Rate (annualized)', min_value=0.0, max_value=0.2, value=0.0)
+ weight_asset = st.sidebar.slider('Diversifier Weight', min_value=0.0001, max_value=0.9999, value=.25, step=0.01)
+ weight_replace_port = st.sidebar.slider('Replacement Portfolio Weight', min_value=0.0001, max_value=1.0000, value=1.00, step=0.01)
+ risk_free_rate = st.sidebar.slider('Risk-Free Rate (annualized)', min_value=0.0, max_value=0.2, value=0.005, step=0.001, format='%.3f')
+ financing_rate = st.sidebar.slider('Financing Rate (annualized)', min_value=0.0, max_value=0.2, value=0.01)
replacement_port_name = st.sidebar.text_input("Replacement Portfolio Name", "Plain 60/40")
- ticker_string_ = "qqq, lqd, hyg, tlt, ief, shy, gld, efa, eem, iyr, xle, xlf"
+ ticker_string_ = "qqq, lqd, hyg, tlt, ief, shy, gld, slv, efa, eem, iyr, xle, xlk, xlf"
ticker_string = st.text_input("Prospective Portfolio Diversifiers (comma separated)", ticker_string_)
ticker_list=ticker_string.replace(' ','').split(',')
@@ -88,14 +89,16 @@ def main():
D = retrieve_yhoo_data(replacement_port_tik[-1], start_date, end_date)
replacement_port_list.append(D)
- replacement_port = replacement_port_list[0]
+ replacement_port = replacement_port_list[0]*(replacement_port_w[0]/sum(replacement_port_w))
for k in range(1,len(replacement_port_list)):
replacement_port += replacement_port_list[k]*(replacement_port_w[k]/sum(replacement_port_w))
+ # with st.spinner("Pulling data..."):
+ # replacement_port=sum([retrieve_yhoo_data(sym, start_date, end_date)*replacement_port_w[i] for i, sym in enumerate(replacement_port_tik)])
replacement_port.name=replacement_port_name
risk_ret_df=pd.DataFrame(index=['Start_Date','End_Date','CWARP','+Sortino','+Ret_To_MaxDD','Sharpe','Sortino','Max_DD'],columns=ticker_list)
new_risk_ret_df=pd.DataFrame(index=['Return','Vol','Sharpe','Sortino','Max_DD','Ret_To_MaxDD',f'CWARP_{round(100*weight_asset)}%_asset'],columns=ticker_list)
- new_risk_ret_df=new_risk_ret_df.add_suffix(f'@{round(100*weight_asset)}% | '+replacement_port.name+'@100%')
+ new_risk_ret_df=new_risk_ret_df.add_suffix(f'@{round(100*weight_asset)}% | '+replacement_port.name+f'{round(100*weight_replace_port)}%')
new_risk_ret_df[replacement_port.name]=np.nan
prices_df=pd.DataFrame(index=retrieve_yhoo_data(ticker_list[0], start_date, end_date).index)
@@ -104,52 +107,66 @@ def main():
for i in range(0,len(ticker_list)):
temp_data=retrieve_yhoo_data(ticker_list[i], start_date, end_date)
prices_df=pd.merge(prices_df,temp_data, left_index=True, right_index=True)
- risk_ret_df.loc['Start_Date',ticker_list[i]]=min(temp_data.index)
- risk_ret_df.loc['End_Date',ticker_list[i]]=max(temp_data.index)
+ risk_ret_df.loc['Start_Date',ticker_list[i]]=min(temp_data.index).date()
+ risk_ret_df.loc['End_Date',ticker_list[i]]=max(temp_data.index).date()
risk_ret_df.loc['CWARP',ticker_list[i]]=cole_win_above_replace_port(new_asset=temp_data, replace_port=replacement_port,
risk_free_rate = risk_free_rate,
financing_rate = financing_rate,
- weight_asset = weight_asset
- )
+ weight_asset = weight_asset,
+ weight_replace_port = weight_replace_port,
+ periodicity=252)
risk_ret_df.loc['+Sortino',ticker_list[i]]=cwarp_additive_sortino(new_asset=temp_data, replace_port=replacement_port,
risk_free_rate = risk_free_rate,
financing_rate = financing_rate,
- weight_asset = weight_asset)
+ weight_asset = weight_asset,
+ weight_replace_port = weight_replace_port,
+ periodicity=252)
risk_ret_df.loc['+Ret_To_MaxDD',ticker_list[i]]=cwarp_additive_ret_maxdd(new_asset=temp_data, replace_port=replacement_port,
risk_free_rate = risk_free_rate,
financing_rate = financing_rate,
- weight_asset = weight_asset)
- risk_ret_df.loc['Sharpe',ticker_list[i]]=sharpe_ratio(temp_data, risk_free_rate = risk_free_rate)
- risk_ret_df.loc['Sortino',ticker_list[i]]=sortino_ratio(temp_data, risk_free_rate = risk_free_rate)
+ weight_asset = weight_asset,
+ weight_replace_port = weight_replace_port,
+ periodicity=252)
+ risk_ret_df.loc['Sharpe',ticker_list[i]]=sharpe_ratio(temp_data, risk_free = risk_free_rate, periodicity=252)
+ risk_ret_df.loc['Sortino',ticker_list[i]]=sortino_ratio(temp_data, risk_free = risk_free_rate, periodicity=252)
risk_ret_df.loc['Max_DD',ticker_list[i]]=max_dd(temp_data)
new_risk_ret_df.loc['Return',new_risk_ret_df.columns[i]]=cwarp_port_return(new_asset=temp_data,replace_port=replacement_port,
risk_free_rate = risk_free_rate,
financing_rate = financing_rate,
- weight_asset = weight_asset)
+ weight_asset = weight_asset,
+ weight_replace_port = weight_replace_port,
+ periodicity = 252)
new_risk_ret_df.loc['Vol',new_risk_ret_df.columns[i]]=cwarp_port_risk(new_asset=temp_data,replace_port=replacement_port,
risk_free_rate = risk_free_rate,
financing_rate = financing_rate,
- weight_asset = weight_asset)
+ weight_asset = weight_asset,
+ weight_replace_port = weight_replace_port,
+ periodicity=252)
cnpd = cwarp_new_port_data(new_asset=temp_data,replace_port=replacement_port, risk_free_rate = risk_free_rate,
financing_rate = financing_rate,
- weight_asset = weight_asset)
+ weight_asset = weight_asset,
+ weight_replace_port = weight_replace_port,
+ periodicity = 252)
new_ports[ticker_list[i]] = cnpd
- new_risk_ret_df.loc['Sharpe',new_risk_ret_df.columns[i]]=sharpe_ratio(cnpd.copy(), risk_free_rate = risk_free_rate)
- new_risk_ret_df.loc['Sortino',new_risk_ret_df.columns[i]]=sortino_ratio(cnpd.copy(), risk_free_rate = risk_free_rate)
+ new_risk_ret_df.loc['Sharpe',new_risk_ret_df.columns[i]]=sharpe_ratio(cnpd.copy(), risk_free = risk_free_rate, periodicity=252)
+ new_risk_ret_df.loc['Sortino',new_risk_ret_df.columns[i]]=sortino_ratio(cnpd.copy(), risk_free = risk_free_rate, periodicity=252)
new_risk_ret_df.loc['Max_DD',new_risk_ret_df.columns[i]]=max_dd(cnpd.copy())
- new_risk_ret_df.loc['Ret_To_MaxDD',new_risk_ret_df.columns[i]]=return_maxdd_ratio(cnpd.copy(), risk_free_rate = risk_free_rate)
+ new_risk_ret_df.loc['Ret_To_MaxDD',new_risk_ret_df.columns[i]]=return_maxdd_ratio(cnpd.copy(), risk_free = risk_free_rate, periodicity=252)
new_risk_ret_df.loc[f'CWARP_{round(100*weight_asset)}%_asset',new_risk_ret_df.columns[i]]=risk_ret_df.loc['CWARP',ticker_list[i]]
- new_risk_ret_df.loc['Return',[replacement_port_name]]=(replacement_port.mean()+1)**252-1
- new_risk_ret_df.loc['Vol',[replacement_port_name]]=replacement_port.std()*np.sqrt(252)
- new_risk_ret_df.loc['Sharpe',[replacement_port_name]]=sharpe_ratio(replacement_port, risk_free_rate = risk_free_rate)
- new_risk_ret_df.loc['Sortino',[replacement_port_name]]=sortino_ratio(replacement_port, risk_free_rate = risk_free_rate)
+ new_risk_ret_df.loc['Return',[replacement_port_name]]=annualized_return(replacement_port, periodicity=252)
+ new_risk_ret_df.loc['Vol',[replacement_port_name]]=target_downside_deviation(replacement_port, MAR=0)*np.sqrt(252)
+ new_risk_ret_df.loc['Sharpe',[replacement_port_name]]=sharpe_ratio(replacement_port, risk_free = risk_free_rate, periodicity=252)
+ new_risk_ret_df.loc['Sortino',[replacement_port_name]]=sortino_ratio(replacement_port, risk_free = risk_free_rate, periodicity=252)
new_risk_ret_df.loc['Max_DD',[replacement_port_name]]=max_dd(replacement_port)
- new_risk_ret_df.loc['Ret_To_MaxDD',[replacement_port_name]]=return_maxdd_ratio(replacement_port, risk_free_rate = risk_free_rate)
+ new_risk_ret_df.loc['Ret_To_MaxDD',[replacement_port_name]]=return_maxdd_ratio(replacement_port, risk_free = risk_free_rate, periodicity=252)
+ new_risk_ret_df.loc[f'CWARP_{round(100*weight_asset)}%_asset',[replacement_port_name]] = cole_win_above_replace_port(new_asset=replacement_port, replace_port=replacement_port, risk_free_rate=risk_free_rate, financing_rate=financing_rate,
+ weight_asset=weight_asset, weight_replace_port=weight_replace_port, periodicity=252)
first_col = new_risk_ret_df.pop(replacement_port_name)
new_risk_ret_df.insert(0, replacement_port_name, first_col)
- st.write(risk_ret_df)
- st.write(new_risk_ret_df)
+ # display dataframes
+ st.write(risk_ret_df.sort_values(by='CWARP', axis=1, ascending=False).style.set_precision(3))
+ st.write(new_risk_ret_df.sort_values(by='Sharpe', axis=1, ascending=False).style.set_precision(3))
vol_arr=new_risk_ret_df.loc['Vol',new_risk_ret_df.columns[1:]]
ret_arr=new_risk_ret_df.loc['Return',new_risk_ret_df.columns[1:]]
sharpe_arr=new_risk_ret_df.loc['Sharpe',new_risk_ret_df.columns[1:]]
diff --git a/cwarp_defs.py b/cwarp_defs.py
index ca60d72..49bc14c 100644
--- a/cwarp_defs.py
+++ b/cwarp_defs.py
@@ -10,40 +10,115 @@
import pandas as pd
#Risk and Reward Functions##################################################################
-def sharpe_ratio(df,risk_free_rate=0,periodicity=252):
- risk_free_rate=(1+risk_free_rate)**(1/periodicity)-1
- dfMean=np.mean(df)-risk_free_rate
- dfSTD=np.std(df)
+def sharpe_ratio(df,risk_free=0,periodicity=252):
+ """df - asset return series, e.g. daily returns based on daily close prices of asset
+ risk_free - annualized risk free rate (default is assumed to be 0)
+ periodicity - number of periods at desired frequency in one year
+ e.g. 252 business days in 1 year (default),
+ 12 months in 1 year,
+ 52 weeks in 1 year etc."""
+ # convert return series to numpy array (in case Pandas series is provided)
+ df = np.asarray(df)
+ # convert annualized risk free rate into appropriate value for provided frequency of asset return series (df)
+ risk_free=(1+risk_free)**(1/periodicity)-1
+ # calculate mean excess return based on return series provided
+ dfMean=np.nanmean(df)-risk_free
+ # calculate standard deviation of return series
+ dfSTD=np.nanstd(df)
+ # calculate Sharpe Ratio = Mean excess return / Std of returns * sqrt(periodicity)
dfSharpe=dfMean/dfSTD*np.sqrt(periodicity)
return dfSharpe
-def sortino_ratio(df,risk_free_rate=0,periodicity=252):
- neg_ndx = np.where(df<0)[0]
- dfSTD= np.std(df[neg_ndx])
- risk_free_rate=(1+risk_free_rate)**(1/periodicity)-1
- dfMean=np.mean(df)-risk_free_rate
- dfSortino=(dfMean/dfSTD)*np.sqrt(periodicity)
+def target_downside_deviation(df, MAR=0, periodicity=252):
+ """df - asset return series, e.g. daily returns based on daily close prices of asset
+ minimum acceptable return (MAR) - value is subtracted from returns before root-mean-square calculation to obtain target downside deviation (TDD)"""
+ # convert return series to numpy array (in case Pandas series is provided)
+ df = np.asarray(df)
+ # TDD step 1: subtract mininum acceptable return (MAR) from period returns provided in df
+ df_ = df - MAR
+ # TDD step 2: zero out positive excess returns and calculate root-mean-square for resulting values
+ df2 = np.where(df_<0, df_, 0)
+ tdd = np.sqrt(np.nanmean(df2**2))
+ return tdd
+
+def sortino_ratio(df,risk_free=0, periodicity=252, include_risk_free_in_vol=False):
+ """df - asset return series, e.g. daily returns based on daily close prices of asset
+ risk_free - annualized risk free rate (default is assumed to be 0). Note: risk free rate is assumed to be the target return/minimum acceptable return (MAR)
+ used in calculating both the mean excess return (numerator of Sortino ratio) and determining target downside deviation (TDD, the denominator of Sortino)
+ periodicity - number of periods at desired frequency in one year
+ e.g. 252 business days in 1 year (default),
+ 12 months in 1 year,
+ 52 weeks in 1 year etc."""
+ # convert return series to numpy array (in case Pandas series is provided)
+ df = np.asarray(df)
+ # convert annualized risk free rate into appropriate value for provided frequency of asset return series (df)
+ risk_free=(1+risk_free)**(1/periodicity)-1
+ # calculate mean excess return based on return series provided
+ dfMean=np.nanmean(df)-risk_free
+ # calculate target downside deviation (TDD)
+ # assume risk free rate is MAR
+ if include_risk_free_in_vol==True: MAR=risk_free
+ else: MAR=0
+ tdd = target_downside_deviation(df, MAR=MAR)
+ # calculate Sortino Ratio = Mean excess return / TDD * sqrt(periodicity)
+ dfSortino=(dfMean/tdd)*np.sqrt(periodicity)
return dfSortino
-def return_maxdd_ratio(df,risk_free_rate=0,periodicity=252):
- #drop zero means you will drop all zero values from the calculation
- risk_free_rate=(1+risk_free_rate)**(1/periodicity)-1
- dfMean=(1+np.mean(df)-risk_free_rate)**(periodicity)-1
- maxDD=max_dd(df,use_window=False,window=252,return_data=False)
- return dfMean/abs(maxDD)
-
-def annualized_return(returns,periodicity=252):
- """Assumes returns is a pandas Series"""
- #start_date=returns.index[0]
- #end_date=returns.index[-1]
- #difference_in_years = (end_date-start_date).days/days_in_year
- difference_in_years = len(returns)/periodicity
- r = np.cumprod(returns+1.)
- start_NAV=1
+def annualized_return(df,periodicity=252):
+ """df - asset return series, e.g. returns based on daily close prices of asset
+ periodicity - number of periods at desired frequency in one year
+ e.g. 252 business days in 1 year (default),
+ 12 months in 1 year,
+ 52 weeks in 1 year etc."""
+ # convert return series to numpy array (in case Pandas series is provided)
+ df = np.asarray(df)
+ # how many years of returns data is provided in df
+ difference_in_years = len(df)/periodicity
+ # starting net asset value / NAV (assumed to be 1) and cumulative returns (r) over time period provided in returns data
+ start_NAV=1.0
+ r = np.nancumprod(df+start_NAV)
+ # end NAV based on final cumulative return
end_NAV=r[-1]
- AnnualReturn = (1 + (end_NAV - 1) / 1)** (1 / difference_in_years) - 1
+ # determine annualized return
+ AnnualReturn = end_NAV**(1 / difference_in_years) - 1
return AnnualReturn
+def max_dd(df, return_data=False):
+ """df - asset return series, e.g. returns based on daily close prices of asset
+ return_data - boolean value to determine if drawdown values over the return data time period should be return, instead of max DD"""
+ # convert return series to numpy array (in case Pandas series is provided)
+ df = np.asarray(df)
+ # calculate cumulative returns
+ start_NAV = 1
+ r = np.nancumprod(df+start_NAV)
+ # calculate cumulative max returns (i.e. keep track of peak cumulative return up to that point in time, despite actual cumulative return at that point in time)
+ peak_r = np.maximum.accumulate(r)
+ # determine drawdowns relative to peak cumulative return achieved up to each point in time
+ dd = (r - peak_r) / peak_r
+ # return drawdown values over time period if return_data is set to True, otherwise return max drawdown which will be a positive number
+ if return_data==True:
+ out = dd
+ else:
+ out = np.abs(np.nanmin(dd))
+ return out
+
+def return_maxdd_ratio(df,risk_free=0,periodicity=252):
+ """df - asset return series, e.g. returns based on daily close prices of asset
+ risk_free - annualized risk free rate (default is assumed to be 0)
+ periodicity - number of periods at desired frequency in one year
+ e.g. 252 business days in 1 year (default),
+ 12 months in 1 year,
+ 52 weeks in 1 year etc."""
+ # convert return series to numpy array (in case Pandas series is provided)
+ df = np.asarray(df)
+ # convert annualized risk free rate into appropriate value for provided frequency of asset return series (df)
+ risk_free=(1+risk_free)**(1/periodicity)-1
+ # determine annualized return to be used in numerator of return to max drawdown (RMDD) calculation
+ AnnualReturn = annualized_return(df, periodicity=periodicity)
+ # determine max drawdown to be used in the denominator of RMDD calculation
+ maxDD=max_dd(df,return_data=False)
+ return (AnnualReturn-risk_free)/abs(maxDD)
+
def avg_positive(ret,dropzero=1):
if dropzero>0:
positives = ret > 0
@@ -70,20 +145,6 @@ def win_pct(ret,dropzero=1):
total=len(ret)
return (win/total)
-def max_dd(pct_df,use_window=False,window=252,return_data=False):
- #calculates the maximum drawdown for the strategy cumulatively or over a rolling window period
- #use_window initiates rolling period, otherwise is cumulative
- #return data provides the raw datastream rather than the min
- if use_window==False:
- out=((((pct_df + 1).cumprod()-(pct_df + 1).cumprod().cummax())/(pct_df + 1).cumprod().cummax()).dropna())
- else:
- out=(((pct_df + 1).cumprod()-(pct_df + 1).cumprod().rolling(window).max())/(pct_df + 1).cumprod().rolling(window).max()).dropna()
- if return_data==True:
- out = out
- else:
- out = min(out)
- return out
-
def kelly(df,dropzero=0):
if dropzero==1: df = df[(df!= 0)]
avg_pos=df[df>=0].mean()
@@ -133,84 +194,56 @@ def ReturnTable(daily_nav_df,freq='1M'):
return_df
-def cole_win_above_replace_port(new_asset, replace_port,
- risk_free_rate=0,
- financing_rate=0,
- weight_asset=0.25,
- weight_replace_port=1,
- periodicity=252):
- # Cole Win Above Replacement Portolio: Calculate additive return to unit of risk for a new asset on an existing portfolio
- # new_asset = returns of the asset you are thinking of adding to your portfolio
- # replace_port = returns of your pre-existing portfolio (e.g. S&P 500 Index, 60/40 Stock-Bond Portfolio)
- # risk_free_rate = Tbill rate
- # financing_rate = portfolio margin/borrowing cost to layer new asset on top of prevailing portfolio
- # weight_asset = % weight you wish to overlay for the new asset on top of the previous portfolio, 25% overlay allocation is standard
- # weight_replace_port = % weight of the replacement portfolio, 100% pre-existing portfolio value is standard
- # periodicity = the frequency of the data you are sampling, typically 12 for monthly or 252 for trading day count
-
- risk_free_rate=(1+risk_free_rate)**(1/periodicity)-1
+def cole_win_above_replace_port(new_asset,replace_port,risk_free_rate=0,financing_rate=0,weight_asset=0.25,weight_replace_port=1,periodicity=252):
+ """Cole Win Above Replacement Portolio (CWARP): Total score to evaluate whether any new investment improves or hurts the return to risk of your total portfolio.
+ new_asset = returns of the asset you are thinking of adding to your portfolio
+ replace_port = returns of your pre-existing portfolio (e.g. S&P 500 Index, 60/40 Stock-Bond Portfolio)
+ risk_free_rate = Tbill rate (annualized)
+ financing_rate = portfolio margin/borrowing cost (annualized) to layer new asset on top of prevailing portfolio (e.g. LIBOR + 60bps). No financing rate is reasonable for derivate overlay products.
+ weight_asset = % weight you wish to overlay for the new asset on top of the previous portfolio, 25% overlay allocation is standard
+ weight_replace_port = % weight of the replacement portfolio, 100% pre-existing portfolio value is standard
+ periodicity = the frequency of the data you are sampling, typically 12 for monthly or 252 for trading day count"""
+ # convert annualized financing rate into appropriate value for provided periodicity
+ # risk_free_rate will be converted appropriately in respective Sortino and RMDD calcs
financing_rate=(1+financing_rate)**(1/periodicity)-1
- replace_port_ex_rf=replace_port-risk_free_rate
- new_port_returns_ex_rf=(new_asset-financing_rate)*weight_asset+(replace_port*weight_replace_port)-risk_free_rate
- new_port=(new_asset-financing_rate)*weight_asset+(replace_port*weight_replace_port)
-
#Calculate Replacement Portfolio Sortino Ratio
- neg_ndx = np.where(replace_port<0)[0]
- replace_port_sortino=np.mean(replace_port_ex_rf)/(np.std(replace_port[neg_ndx]))*np.sqrt(periodicity)
+ replace_port_sortino = sortino_ratio(replace_port, risk_free=risk_free_rate, periodicity=periodicity)
#Calculate Replacement Portfolio Return to Max Drawdown
- maxdd_replace_port=min((((replace_port + 1).cumprod()-(replace_port + 1).cumprod().cummax())/(replace_port + 1).cumprod().cummax()).dropna())
- replace_port_return_maxdd= ((1+np.mean(replace_port_ex_rf))**(periodicity)-1)/abs(maxdd_replace_port)
+ replace_port_return_maxdd = return_maxdd_ratio(replace_port, risk_free=risk_free_rate, periodicity=periodicity)
#Calculate New Portfolio Sortino Ratio
- neg_ndx_new = np.where(new_port<0)[0]
- new_port_sortino=np.mean(new_port_returns_ex_rf)/(np.std(new_port[neg_ndx_new]))*np.sqrt(periodicity)
+ new_port = (new_asset-financing_rate)*weight_asset+replace_port*weight_replace_port
+ new_port_sortino = sortino_ratio(new_port, risk_free=risk_free_rate, periodicity=periodicity)
#Calculate New Portfolio Return to Max Drawdown
- maxdd_new_port=min((((new_port + 1).cumprod()-(new_port + 1).cumprod().cummax())/(new_port + 1).cumprod().cummax()).dropna())
- new_port_return_maxdd= ((1+np.mean(new_port_returns_ex_rf))**(periodicity)-1)/abs(maxdd_new_port)
+ new_port_return_maxdd = return_maxdd_ratio(new_port, risk_free=risk_free_rate, periodicity=periodicity)
#Final CWARP calculation
- CWARP=(((new_port_return_maxdd/replace_port_return_maxdd)*(new_port_sortino/replace_port_sortino))**(1/2)-1)*100
+ CWARP = ((new_port_return_maxdd/replace_port_return_maxdd*new_port_sortino/replace_port_sortino)**(1/2)-1)*100
+
return CWARP
-def cwarp_additive_sortino(new_asset,replace_port,
- risk_free_rate=0,
- financing_rate=0,
- weight_asset=0.25,
- weight_replace_port=1,
- periodicity=252):
- # new_asset = returns of the asset you are thinking of adding to your portfolio
- # replace_port = returns of your pre-existing portfolio (e.g. S&P 500 Index, 60/40 Stock-Bond Portfolio)
- # risk_free_rate = Tbill rate
- # financing_rate = portfolio margin/borrowing cost to layer new asset on top of prevailing portfolio
- # weight_asset = % weight you wish to overlay for the new asset on top of the previous portfolio, 25% overlay allocation is standard
- # weight_replace_port = % weight of the replacement portfolio, 100% pre-existing portfolio value is standard
- # periodicity = the frequency of the data you are sampling, typically 12 for monthly or 252 for trading day count
-
- risk_free_rate=(1+risk_free_rate)**(1/periodicity)-1
+def cwarp_additive_sortino(new_asset,replace_port,risk_free_rate=0,financing_rate=0,weight_asset=0.25,weight_replace_port=1,periodicity=252):
+ """Cole Win Above Replacement Portolio (CWARP) Sortino +: Isolates new investment effect on total portfolio Sortino Ratio, which is a portion of the holistic CWARP score.
+ new_asset = returns of the asset you are thinking of adding to your portfolio
+ replace_port = returns of your pre-existing portfolio (e.g. S&P 500 Index, 60/40 Stock-Bond Portfolio)
+ risk_free_rate = Tbill rate (annualized)
+ financing_rate = portfolio margin/borrowing cost (annualized) to layer new asset on top of prevailing portfolio (e.g. LIBOR + 60bps). No financing rate is reasonable for derivate overlay products.
+ weight_asset = % weight you wish to overlay for the new asset on top of the previous portfolio, 25% overlay allocation is standard
+ weight_replace_port = % weight of the replacement portfolio, 100% pre-existing portfolio value is standard
+ periodicity = the frequency of the data you are sampling, typically 12 for monthly or 252 for trading day count"""
+ # convert annualized financing rate into appropriate value for provided periodicity
+ # risk_free_rate will be converted appropriately in respective Sortino and RMDD calcs
financing_rate=(1+financing_rate)**(1/periodicity)-1
- replace_port_ex_rf=replace_port-risk_free_rate
- new_port_returns_ex_rf=(new_asset-financing_rate)*weight_asset+(replace_port*weight_replace_port)-risk_free_rate
- new_port=(new_asset-financing_rate)*weight_asset+(replace_port*weight_replace_port)
-
#Calculate Replacement Portfolio Sortino Ratio
- neg_ndx = np.where(replace_port<0)[0]
- replace_port_sortino=np.mean(replace_port_ex_rf)/(np.std(replace_port[neg_ndx]))*np.sqrt(periodicity)
-
- #Calculate Replacement Portfolio Return to Max Drawdown
- maxdd_replace_port=min((((replace_port + 1).cumprod()-(replace_port + 1).cumprod().cummax())/(replace_port + 1).cumprod().cummax()).dropna())
- replace_port_return_maxdd= ((1+np.mean(replace_port_ex_rf))**(periodicity)-1)/abs(maxdd_replace_port)
+ replace_port_sortino = sortino_ratio(replace_port, risk_free=risk_free_rate, periodicity=periodicity)
#Calculate New Portfolio Sortino Ratio
- neg_ndx_new = np.where(new_port<0)[0]
- new_port_sortino=np.mean(new_port_returns_ex_rf)/(np.std(new_port[neg_ndx_new]))*np.sqrt(periodicity)
-
- #Calculate New Portfolio Return to Max Drawdown
- maxdd_new_port=min((((new_port + 1).cumprod()-(new_port + 1).cumprod().cummax())/(new_port + 1).cumprod().cummax()).dropna())
- new_port_return_maxdd= ((1+np.mean(new_port_returns_ex_rf))**(periodicity)-1)/abs(maxdd_new_port)
+ new_port = (new_asset-financing_rate)*weight_asset+replace_port*weight_replace_port
+ new_port_sortino = sortino_ratio(new_port, risk_free=risk_free_rate, periodicity=periodicity)
#Final calculation
CWARP_add_sortino=((new_port_sortino/replace_port_sortino)-1)*100
@@ -218,58 +251,77 @@ def cwarp_additive_sortino(new_asset,replace_port,
return CWARP_add_sortino
def cwarp_additive_ret_maxdd(new_asset,replace_port,risk_free_rate=0,financing_rate=0,weight_asset=0.25,weight_replace_port=1,periodicity=252):
- # new_asset = returns of the asset you are thinking of adding to your portfolio
- # replace_port = returns of your pre-existing portfolio (e.g. S&P 500 Index, 60/40 Stock-Bond Portfolio)
- # risk_free_rate = Tbill rate
- # financing_rate = portfolio margin/borrowing cost to layer new asset on top of prevailing portfolio
- # weight_asset = % weight you wish to overlay for the new asset on top of the previous portfolio, 25% overlay allocation is standard
- # weight_replace_port = % weight of the replacement portfolio, 100% pre-existing portfolio value is standard
- # periodicity = the frequency of the data you are sampling, typically 12 for monthly or 252 for trading day count
-
- risk_free_rate=(1+risk_free_rate)**(1/periodicity)-1
+ """Cole Win Above Replacement Portolio (CWARP) Ret to Max DD +: Isolates new investment effect on total portfolio Return to MAXDD, which is a portion of the holistic CWARP score.
+ new_asset = returns of the asset you are thinking of adding to your portfolio
+ replace_port = returns of your pre-existing portfolio (e.g. S&P 500 Index, 60/40 Stock-Bond Portfolio)
+ risk_free_rate = Tbill rate (annualized)
+ financing_rate = portfolio margin/borrowing cost (annualized) to layer new asset on top of prevailing portfolio (e.g. LIBOR + 60bps). No financing rate is reasonable for derivate overlay products.
+ weight_asset = % weight you wish to overlay for the new asset on top of the previous portfolio, 25% overlay allocation is standard
+ weight_replace_port = % weight of the replacement portfolio, 100% pre-existing portfolio value is standard
+ periodicity = the frequency of the data you are sampling, typically 12 for monthly or 252 for trading day count"""
+ # convert annualized financing rate into appropriate value for provided periodicity
+ # risk_free_rate will be converted appropriately in respective Sortino and RMDD calcs
financing_rate=(1+financing_rate)**(1/periodicity)-1
- replace_port_ex_rf=replace_port-risk_free_rate
- new_port_returns_ex_rf=(new_asset-financing_rate)*weight_asset+(replace_port*weight_replace_port)-risk_free_rate
- new_port=(new_asset-financing_rate)*weight_asset+(replace_port*weight_replace_port)
-
- #Calculate Replacement Portfolio Sortino Ratio
- neg_ndx = np.where(replace_port<0)[0]
- replace_port_sortino=np.mean(replace_port_ex_rf)/(np.std(replace_port[neg_ndx]))*np.sqrt(periodicity)
-
#Calculate Replacement Portfolio Return to Max Drawdown
- maxdd_replace_port=min((((replace_port + 1).cumprod()-(replace_port + 1).cumprod().cummax())/(replace_port + 1).cumprod().cummax()).dropna())
- replace_port_return_maxdd= ((1+np.mean(replace_port_ex_rf))**(periodicity)-1)/abs(maxdd_replace_port)
-
- #Calculate New Portfolio Sortino Ratio
- neg_ndx_new = np.where(new_port<0)[0]
- new_port_sortino=np.mean(new_port_returns_ex_rf)/(np.std(new_port[neg_ndx_new]))*np.sqrt(periodicity)
+ replace_port_return_maxdd = return_maxdd_ratio(replace_port, risk_free=risk_free_rate, periodicity=periodicity)
#Calculate New Portfolio Return to Max Drawdown
- maxdd_new_port=min((((new_port + 1).cumprod()-(new_port + 1).cumprod().cummax())/(new_port + 1).cumprod().cummax()).dropna())
- new_port_return_maxdd= ((1+np.mean(new_port_returns_ex_rf))**(periodicity)-1)/abs(maxdd_new_port)
+ new_port = (new_asset-financing_rate)*weight_asset+replace_port*weight_replace_port
+ new_port_return_maxdd = return_maxdd_ratio(new_port, risk_free=risk_free_rate, periodicity=periodicity)
#Final calculation
CWARP_add_ret_maxdd=((new_port_return_maxdd/replace_port_return_maxdd)-1)*100
+
return CWARP_add_ret_maxdd
def cwarp_port_return(new_asset,replace_port,risk_free_rate=0,financing_rate=0,weight_asset=0.25,weight_replace_port=1,periodicity=252):
+ """Cole Win Above Replacement Portolio (CWARP) Portfolio Return: Returns of the aggregate portfolio after a new asset is financed and layered on top of the replacement portfolio.
+ new_asset = returns of the asset you are thinking of adding to your portfolio
+ replace_port = returns of your pre-existing portfolio (e.g. S&P 500 Index, 60/40 Stock-Bond Portfolio)
+ risk_free_rate = Tbill rate (annualized)
+ financing_rate = portfolio margin/borrowing cost (annualized) to layer new asset on top of prevailing portfolio (e.g. LIBOR + 60bps). No financing rate is reasonable for derivate overlay products.
+ weight_asset = % weight you wish to overlay for the new asset on top of the previous portfolio, 25% overlay allocation is standard
+ weight_replace_port = % weight of the replacement portfolio, 100% pre-existing portfolio value is standard
+ periodicity = the frequency of the data you are sampling, typically 12 for monthly or 252 for trading day count"""
+ # convert annual financing based on periodicity
financing_rate=((financing_rate+1)**(1/periodicity)-1)
- risk_free_rate=((risk_free_rate+1)**(1/periodicity)-1)
- new_port=(new_asset-financing_rate)*weight_asset+replace_port*weight_replace_port-risk_free_rate
- out=(np.mean(new_port)+1)**(periodicity)-1
+
+ # compose new portfolio
+ new_port=(new_asset-financing_rate)*weight_asset+replace_port*weight_replace_port
+
+ # calculate annualized return of new portfolio and subtract risk-free rate
+ out = annualized_return(new_port, periodicity=periodicity) - risk_free_rate
return out
def cwarp_port_risk(new_asset,replace_port,risk_free_rate=0,financing_rate=0,weight_asset=0.25,weight_replace_port=1,periodicity=252):
+ """Cole Win Above Replacement Portolio (CWARP) Portfolio Risk: Volatility of the aggregate portfolio after a new asset is financed and layered on top of the replacement portfolio.
+ new_asset = returns of the asset you are thinking of adding to your portfolio
+ replace_port = returns of your pre-existing portfolio (e.g. S&P 500 Index, 60/40 Stock-Bond Portfolio)
+ risk_free_rate = Tbill rate (annualized)
+ financing_rate = portfolio margin/borrowing cost (annualized) to layer new asset on top of prevailing portfolio (e.g. LIBOR + 60bps). No financing rate is reasonable for derivate overlay products.
+ weight_asset = % weight you wish to overlay for the new asset on top of the previous portfolio, 25% overlay allocation is standard
+ weight_replace_port = % weight of the replacement portfolio, 100% pre-existing portfolio value is standard
+ periodicity = the frequency of the data you are sampling, typically 12 for monthly or 252 for trading day count"""
+ # convert annual financing and risk free rates based on periodicity
financing_rate=((financing_rate+1)**(1/periodicity)-1)
risk_free_rate=((risk_free_rate+1)**(1/periodicity)-1)
+ # compose new portfolio
new_port=(new_asset-financing_rate)*weight_asset+replace_port*weight_replace_port
- neg_ndx = np.where(new_port<0)[0]
- out=np.std(new_port[neg_ndx])*np.sqrt(periodicity)
- return out
+ # calculated target downside deviation (TDD)
+ tdd = target_downside_deviation(new_port, MAR=0)*np.sqrt(periodicity)
+ return tdd
def cwarp_new_port_data(new_asset,replace_port,risk_free_rate=0,financing_rate=0,weight_asset=0.25,weight_replace_port=1,periodicity=252):
+ """Cole Win Above Replacement Portolio (CWARP) return stream: Return series after a new asset is financed and layered on top of the replacement portfolio.
+ new_asset = returns of the asset you are thinking of adding to your portfolio
+ replace_port = returns of your pre-existing portfolio (e.g. S&P 500 Index, 60/40 Stock-Bond Portfolio)
+ risk_free_rate = Tbill rate
+ financing_rate = portfolio margin/borrowing cost to layer new asset on top of prevailing portfolio (e.g. LIBOR + 60bps). No financing rate is reasonable for derivate overlay products.
+ weight_asset = % weight you wish to overlay for the new asset on top of the previous portfolio, 25% overlay allocation is standard
+ weight_replace_port = % weight of the replacement portfolio, 100% pre-existing portfolio value is standard
+ periodicity = the frequency of the data you are sampling, typically 12 for monthly or 252 for trading day count"""
+ # convert annual financing based on periodicity
financing_rate=((financing_rate+1)**(1/periodicity)-1)
- risk_free_rate=((risk_free_rate+1)**(1/periodicity)-1)
new_port=(new_asset-financing_rate)*weight_asset+replace_port*weight_replace_port
return new_port