<jupyter_start><jupyter_text>AssumptionsTransaction cost Total Cost: 0.05% on large portfolioFrazzini, <NAME> Israel, <NAME> Moskowitz, <NAME>., Trading Costs (April 7, 2018). Available at SSRN: https://ssrn.com/abstract=3229719 or http://dx.doi.org/10.2139/ssrn.3229719<jupyter_code>import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# plt.style.use('ggplot')

# # import matplotlib
# # matplotlib.style.use('classic')
# # sns.set_style("whitegrid")
# # %matplotlib inline

pd.set_option('display.max_columns', None)
import sys
positions = pd.read_csv("../../Predictions/Metalearner/df_test_AAPL_predictions.csv")
positions.Date = pd.to_datetime(positions.Date, dayfirst=False)

# Create extra column of long only returns
positions["long_only"] = 1

# Need to read in prices
prices = pd.read_csv("../../Raw Data/Price/price_labels.csv")
prices["Date"] = pd.to_datetime(prices["Date"], dayfirst=False)
# We use adjusted close. This accounts for dividends stock splits (if we're holding and we get dividends, this
# will be reflected in the adj close)
prices = prices.pivot(columns="Ticker", index="Date", values="Adj Close")
returns = np.log(prices).diff() # log returns so that we can add

# Index and columns same as positions 
returns = returns[returns.columns[returns.columns.isin(positions.Ticker.unique())]]
returns = returns.loc[positions.Date.unique()]
col_order = returns.columns
positions = positions[positions.Date < "2019-11-05"]
returns = returns.loc[:"2019-11-04"]
from sklearn.metrics import roc_auc_score, accuracy_score, f1_score, recall_score, precision_score<jupyter_output><empty_output><jupyter_text>Obtain scoring metrics across different predictions<jupyter_code>performance_results = pd.DataFrame()
for pred_type in ['Predictions_8k', 'Predictions_10kq', 'Predictions_tweets', 'Predictions_news', 'Predictions_amxn', 'ensemble_pred', 
              'xgb_pred', 'logreg_pred', 'RF_pred', 'long_only']:
    preds = positions[~(positions[pred_type] == 0)][pred_type]
    y_actual = positions[~(positions[pred_type] == 0)].Label
    performance_results[pred_type] = [
        accuracy_score(y_actual, preds),
        f1_score(y_actual, preds),
        recall_score(y_actual, preds),
        precision_score(y_actual, preds)
    ]
plt.figure(figsize=(20,6))
sns.heatmap(performance_results, cmap="Blues", annot=True, cbar_kws = dict(use_gridspec=False,location="bottom"))
plt.title("Performance Metrics")<jupyter_output><empty_output><jupyter_text>Preprocess Data to obtain model returns  Pivot positions to get time series of positions of companies by date<jupyter_code>sec_8k        = positions.pivot(columns="Ticker", index="Date", values="Predictions_8k")[col_order]
sec_10k10q    = positions.pivot(columns="Ticker", index="Date", values="Predictions_10kq")[col_order]
tweets        = positions.pivot(columns="Ticker", index="Date", values="Predictions_tweets")[col_order]
news          = positions.pivot(columns="Ticker", index="Date", values="Predictions_news")[col_order]
amzn          = positions.pivot(columns="Ticker", index="Date", values="Predictions_amxn")[col_order]
meta_ensemble = positions.pivot(columns="Ticker", index="Date", values="ensemble_pred")[col_order]
meta_xgb      = positions.pivot(columns="Ticker", index="Date", values="xgb_pred")[col_order]
meta_logreg   = positions.pivot(columns="Ticker", index="Date", values="logreg_pred")[col_order]
meta_rf       = positions.pivot(columns="Ticker", index="Date", values="RF_pred")[col_order]
long_only     = positions.pivot(columns="Ticker", index="Date", values="long_only")[col_order]
perfect_pred  = positions.pivot(columns="Ticker", index="Date", values="Label")[col_order]
sec_8k.head()<jupyter_output><empty_output><jupyter_text>Scale all values, absolute sum of all values should = 1<jupyter_code>epsilon = sys.float_info.epsilon
epsilon # for adding small values
sec_8k        = sec_8k.apply(lambda x: x/sum(abs(x)+epsilon), axis = 1)
sec_10k10q    = sec_10k10q.apply(lambda x: x/sum(abs(x)+epsilon), axis = 1)
tweets        = tweets.apply(lambda x: x/sum(abs(x)+epsilon), axis = 1)
news          = news.apply(lambda x: x/sum(abs(x)+epsilon), axis = 1)
amzn          = amzn.apply(lambda x: x/sum(abs(x)+epsilon), axis = 1)
meta_ensemble = meta_ensemble.apply(lambda x: x/sum(abs(x)+epsilon), axis = 1)
meta_xgb      = meta_xgb.apply(lambda x: x/sum(abs(x)+epsilon), axis = 1)
meta_logreg   = meta_logreg.apply(lambda x: x/sum(abs(x)+epsilon), axis = 1)
meta_rf       = meta_rf.apply(lambda x: x/sum(abs(x)+epsilon), axis = 1)
long_only     = long_only.apply(lambda x: x/sum(abs(x)+epsilon), axis = 1)
perfect_pred  = perfect_pred.apply(lambda x: x/sum(abs(x)+epsilon), axis = 1)
sec_8k.head()<jupyter_output><empty_output><jupyter_text>Pre-TC Model Returns Calculation<jupyter_code>sec_8k_pretc        = sec_8k * returns # no tc subtracted
sec_10k10q_pretc    = sec_10k10q * returns
tweets_pretc        = tweets * returns
news_pretc          = news * returns
amzn_pretc          = amzn * returns
meta_ensemble_pretc = meta_ensemble * returns
meta_xgb_pretc      = meta_xgb * returns
meta_logreg_pretc   = meta_logreg * returns
meta_rf_pretc       = meta_rf * returns
long_only_pretc     = long_only * returns
perfect_pred_pretc  = perfect_pred * returns<jupyter_output><empty_output><jupyter_text>Sum up row-wise to obtain model returns<jupyter_code>sec_8k_pretc        = sec_8k_pretc.apply(lambda x: sum(x), axis = 1)
sec_10k10q_pretc    = sec_10k10q_pretc.apply(lambda x: sum(x), axis = 1)
tweets_pretc        = tweets_pretc.apply(lambda x: sum(x), axis = 1)
news_pretc          = news_pretc.apply(lambda x: sum(x), axis = 1)
amzn_pretc          = amzn_pretc.apply(lambda x: sum(x), axis = 1)
meta_ensemble_pretc = meta_ensemble_pretc.apply(lambda x: sum(x), axis = 1)
meta_xgb_pretc      = meta_xgb_pretc.apply(lambda x: sum(x), axis = 1)
meta_logreg_pretc   = meta_logreg_pretc.apply(lambda x: sum(x), axis = 1)
meta_rf_pretc       = meta_rf_pretc.apply(lambda x: sum(x), axis = 1)
long_only_pretc     = long_only_pretc.apply(lambda x: sum(x), axis = 1)
perfect_pred_pretc  = perfect_pred_pretc.apply(lambda x: sum(x), axis = 1)
data = {"sec_8k":sec_8k_pretc,
"sec_10k10q":sec_10k10q_pretc,
"tweets":tweets_pretc,
"news":news_pretc,
"amzn": amzn_pretc,
"meta_ensemble":meta_ensemble_pretc,
"meta_xgb":meta_xgb_pretc,
"meta_logreg":meta_logreg_pretc,
"meta_rf":meta_rf_pretc,
"long_only":long_only_pretc,
"perfect_pred":perfect_pred_pretc}
model_returns_pretc = pd.DataFrame(data).iloc[1:]
model_returns_pretc.head()<jupyter_output><empty_output><jupyter_text>Post-TC Model Returns Calculation  Get transaction cost<jupyter_code>sec_8k_tc        = abs(sec_8k.diff())        * 0.05 / 100
sec_10k10q_tc    = abs(sec_10k10q.diff())    * 0.05 / 100
tweets_tc        = abs(tweets.diff())        * 0.05 / 100
news_tc          = abs(news.diff())          * 0.05 / 100
amzn_tc          = abs(amzn.diff())          * 0.05 / 100
meta_ensemble_tc = abs(meta_ensemble.diff()) * 0.05 / 100
meta_xgb_tc      = abs(meta_xgb.diff())      * 0.05 / 100
meta_logreg_tc   = abs(meta_logreg.diff())   * 0.05 / 100
meta_rf_tc       = abs(meta_rf.diff())       * 0.05 / 100
long_only_tc     = abs(long_only.diff())     * 0.05 / 100
perfect_pred_tc  = abs(perfect_pred.diff())  * 0.05 / 100
sec_8k        = sec_8k * returns - sec_8k_tc
sec_10k10q    = sec_10k10q * returns - sec_10k10q_tc
tweets        = tweets * returns - tweets_tc
news          = news * returns - news_tc
amzn          = amzn * returns - amzn_tc
meta_ensemble = meta_ensemble * returns - meta_ensemble_tc
meta_xgb      = meta_xgb * returns - meta_xgb_tc
meta_logreg   = meta_logreg * returns - meta_logreg_tc
meta_rf       = meta_rf * returns - meta_rf_tc
long_only     = long_only * returns - long_only_tc
perfect_pred  = perfect_pred * returns - perfect_pred_tc
sec_8k.head()<jupyter_output><empty_output><jupyter_text>Lastly, sum up all the returns from different assets to obtain model returns<jupyter_code>sec_8k        = sec_8k.apply(lambda x: sum(x), axis = 1)
sec_10k10q    = sec_10k10q.apply(lambda x: sum(x), axis = 1)
tweets        = tweets.apply(lambda x: sum(x), axis = 1)
news          = news.apply(lambda x: sum(x), axis = 1)
amzn          = amzn.apply(lambda x: sum(x), axis = 1)
meta_ensemble = meta_ensemble.apply(lambda x: sum(x), axis = 1)
meta_xgb      = meta_xgb.apply(lambda x: sum(x), axis = 1)
meta_logreg   = meta_logreg.apply(lambda x: sum(x), axis = 1)
meta_rf       = meta_rf.apply(lambda x: sum(x), axis = 1)
long_only     = long_only.apply(lambda x: sum(x), axis = 1)
perfect_pred  = perfect_pred.apply(lambda x: sum(x), axis = 1)
sec_8k.head()
data = {"sec_8k":sec_8k,
"sec_10k10q":sec_10k10q,
"tweets":tweets,
"news":news,
"amzn":amzn,
"meta_ensemble":meta_ensemble,
"meta_xgb":meta_xgb,
"meta_logreg":meta_logreg,
"meta_rf":meta_rf,
"long_only":long_only,
"perfect_pred":perfect_pred}
model_returns = pd.DataFrame(data).iloc[1:]
model_returns.head()<jupyter_output><empty_output><jupyter_text>Visualisations with Perfect Prediction<jupyter_code>sharpe_ratios = round(model_returns_pretc.mean() / model_returns_pretc.std() * 252 ** 0.5, 2)
ax = sharpe_ratios.plot(kind="bar", rot=45, figsize=(15,5))

for p in ax.patches:
    if p.get_height() <= 0:
        ax.annotate(str(p.get_height()), (p.get_x()+p.get_width()/2-0.05, 0.01))
    else:
        ax.annotate(str(p.get_height()), (p.get_x()+p.get_width()/2-0.05, p.get_height() * 1.005))
plt.title("Sharpe Ratios by NLP (Pre TC)")
plt.ylabel("Annualised Sharpe Ratio")
sharpe_ratios = round(model_returns.mean() / model_returns.std() * 252 ** 0.5, 2)
ax = sharpe_ratios.plot(kind="bar", rot=45, figsize=(15,5))

for p in ax.patches:
    if p.get_height() <= 0:
        ax.annotate(str(p.get_height()), (p.get_x()+p.get_width()/2-0.05, 0.01))
    else:
        ax.annotate(str(p.get_height()), (p.get_x()+p.get_width()/2-0.05, p.get_height() * 1.005))
plt.title("Sharpe Ratios by NLP (Post-TC)")
plt.ylabel("Annualised Sharpe Ratio")
model_returns_pretc.expanding(2).sum().plot(figsize=(15,5))
plt.title("Model Returns (Pre-TC)")
model_returns.expanding(2).sum().plot(figsize=(15,5))
plt.title("Model Returns (Post-TC)")
transaction_cost = round((model_returns_pretc.expanding(2).sum().iloc[-1] - model_returns.expanding(2).sum().iloc[-1]) * 100, 2)
ax = transaction_cost.plot(kind="bar", figsize=(15, 5), rot=45)

for p in ax.patches:
    if p.get_height() <= 0:
        ax.annotate(str(p.get_height()), (p.get_x()+p.get_width()/2-0.1, 0.01))
    else:
        ax.annotate(str(p.get_height()), (p.get_x()+p.get_width()/2-0.1, p.get_height() * 1.005))
plt.title("Total Transaction Cost")
plt.ylabel("Transaction Cost (%)")
plt.xlabel("Prediction Type")<jupyter_output><empty_output><jupyter_text>Visualisations<jupyter_code>plot_cols = ['amzn', 'meta_ensemble', 'meta_xgb', 'meta_logreg', 'meta_rf', 'long_only']
model_returns_pretc[plot_cols].expanding(2).sum().plot(figsize=(15,8))
plt.title("Model Returns (Pre-TC)")
model_returns[plot_cols].expanding(2).sum().plot(figsize=(15,8))
plt.title("Model Returns (Post-TC)")
model_returns[["amzn", "long_only"]].expanding(2).sum().plot(figsize=(15,8))
plt.title("Model Returns (Post-TC)")
plt.axhline(0, color='blue', ls="--")
sharpe_ratios = round(model_returns_pretc[plot_cols].mean() / model_returns_pretc[plot_cols].std() * 252 ** 0.5, 2)
ax = sharpe_ratios.plot(kind="bar", rot=45, figsize=(15,5))

for p in ax.patches:
    if p.get_height() <= 0:
        ax.annotate(str(p.get_height()), (p.get_x()+p.get_width()/2-0.05, 0.01))
    else:
        ax.annotate(str(p.get_height()), (p.get_x()+p.get_width()/2-0.05, p.get_height() * 1.005))
plt.title("Sharpe Ratios by NLP (Pre TC)")
plt.ylabel("Annualised Sharpe Ratio")
plt.xlabel("Prediction Type")
sharpe_ratios = round(model_returns[plot_cols].mean() / model_returns[plot_cols].std() * 252 ** 0.5, 2)
ax = sharpe_ratios.plot(kind="bar", rot=45, figsize=(15,7))

for p in ax.patches:
    if p.get_height() <= 0:
        ax.annotate(str(p.get_height()), (p.get_x()+p.get_width()/2-0.1, 0.01))
    else:
        ax.annotate(str(p.get_height()), (p.get_x()+p.get_width()/2-0.1, p.get_height() * 1.005))
plt.title("Sharpe Ratios by NLP (After Accouting for Transaction Cost)")
plt.ylabel("Annualised Sharpe Ratio")
plt.xlabel("Prediction Type")
plt.axhline(0, color='black')
plt.axhline(0.82, color='orange', ls="--")
a_returns = model_returns[['amzn', 'long_only']].expanding(2).sum().iloc[-1] / 501 * 252
annual_rtns = round(pd.DataFrame(a_returns) * 100, 2)
annual_rtns.columns = ["Annual Returns"]
plt.figure(figsize=(1.5,2))
ax = sns.heatmap(annual_rtns, cmap = sns.diverging_palette(10, 150, n=9), annot=True)
ax.xaxis.set_ticks_position('top')
ax.set_yticklabels(ax.get_yticklabels(), rotation = 0, fontsize = 8)
vol = pd.DataFrame(round(model_returns[['amzn', 'long_only']].std() * 252 ** 0.5 * 100, 2))
vol.columns = ["Annual Volatility"]
plt.figure(figsize=(1.5,2))
ax = sns.heatmap(vol, cmap = sns.diverging_palette(150, 10, n=9), annot=True)
ax.xaxis.set_ticks_position('top')
ax.set_yticklabels(ax.get_yticklabels(), rotation = 0, fontsize = 8)
cumulative_returns = model_returns[['amzn', 'long_only']].expanding(2).sum()
drawdowns = dict()
for col in cumulative_returns.columns:
    maximum_drawdown = []
    highest = cumulative_returns[col].values[1]
    lowest = cumulative_returns[col].values[1]
    for cum_rtn in cumulative_returns[col].values:
        if cum_rtn >= highest:
            maximum_drawdown.append(highest - lowest)
            highest = cum_rtn
            lowest = cum_rtn
            continue
        if cum_rtn <= lowest:
            lowest = cum_rtn
    drawdowns[col] = [max(maximum_drawdown)]
drawdowns
plt.figure(figsize=(1.5,2))
dd_df = round(pd.DataFrame.from_dict(drawdowns).T * 100, 2)
dd_df.columns = ["Max Drawdown"]
ax = sns.heatmap(dd_df, cmap = sns.diverging_palette(150, 10, n=9), annot=True)
ax.xaxis.set_ticks_position('top')
ax.set_yticklabels(ax.get_yticklabels(), rotation = 0, fontsize = 8)
no_amzn = pd.read_csv("../../Predictions/Metalearner/appl_no_amzn.csv")[["Date", "meta_ensemble", "meta_xgb", "meta_logreg","meta_rf"]]
no_amzn.Date = pd.to_datetime(no_amzn.Date)
no_amzn.set_index("Date", inplace=True)
no_amzn = pd.DataFrame(round(no_amzn.mean() / no_amzn.std() * 252 ** 0.5, 2))
no_amzn = no_amzn.reset_index()
no_amzn["type"] = "No Amzn"
no_amzn.columns = ["Model", "Sharpe", "type"]
no_amzn
sharpe_ratios = round(model_returns[["meta_ensemble","meta_xgb", "meta_logreg", "meta_rf"]].mean() / model_returns[["meta_ensemble","meta_xgb", "meta_logreg", "meta_rf"]].std() * 252 ** 0.5, 2)
sharpe_ratios = pd.DataFrame(sharpe_ratios).reset_index()
sharpe_ratios["type"] = "Amzn"
sharpe_ratios.columns = ["Model", "Sharpe", "type"]
sharpe_ratios
plt_sharpes = pd.concat([sharpe_ratios, no_amzn])
plt.style.use('ggplot')

import matplotlib
matplotlib.style.use('classic')
sns.set_style("whitegrid")
%matplotlib inline
plt.figure(figsize=(15, 7))
ax = sns.barplot(x="Model", y="Sharpe", hue="type", data=plt_sharpes, color="blue")

for p in ax.patches:
    if p.get_height() <= 0:
        ax.annotate(str(p.get_height()), (p.get_x()+p.get_width()/2-0.05, 0.01))
    else:
        ax.annotate(str(p.get_height()), (p.get_x()+p.get_width()/2-0.05, p.get_height() * 1.005))
plt.ylabel("Annualised Sharpe Ratio")
plt.xlabel("Prediction Type")
plt.ylim([0, 1.65])
plt.title("Sharpe Ratio with and Without Amazon")<jupyter_output><empty_output>