# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: py37
#     language: python
#     name: py37
# ---

# ## Imports

# +
# #Delete me
# # Settings for notebook visualization
# from IPython.core.interactiveshell import InteractiveShell
# InteractiveShell.ast_node_interactivity = 'all'
# # %matplotlib inline
# from IPython.core.display import HTML
# HTML("""<style>.output_png img {display: block;margin-left: auto;margin-right: auto;text-align: center;vertical-align: middle;} </style>""")

# + code_folding=[]
# Necessary imports
#import yfinance as yf
import numpy as np
import pandas as pd

import quantstats as qs
#import statistics as st
import os
#from datetime import datetime, timedelta

# + code_folding=[]
# Other settings
qs.extend_pandas()

np.set_printoptions(edgeitems=40, linewidth=1000)

pd.set_option("display.precision", 6)
pd.set_option('display.max_columns', 500)
pd.set_option('display.width', 1000)
# -

os.chdir("/Users/Sergio/Documents/Master_QF/Thesis/Code/Algorithmic Strategies/")

# ## Data

# +
# ini_equity_default = 100
# commision_default = 0.000111538462 # 2/130000 + 12.5/130000
# #commision_default = 0.0005 # Slightly Bbgger commision, for better visualization
# # 0.01 = 1% of the cummulative return (equity)

# + code_folding=[]
# # Load DF with SP500 data
# def get_sp500_data(start_date="1928-01-01", from_local_file=True, save_to_file=False):
#     if from_local_file == True:
#         data = pd.read_pickle('data/SP500_hist_data.pkl')
#     else:
#         # Download data from yfinance
#         data = yf.download("^GSPC", auto_adjust=True, start=start_date)
#         if save_to_file == True:
#             data.to_pickle("data/SP500_hist_data.pkl")
#     return data

# +
# full_df = get_sp500_data()
# full_df['Market_daily_ret'] = full_df['Close'].pct_change().fillna((full_df['Close']-full_df['Open'])/full_df['Open'])
# full_df = full_df[['Close', 'Open', 'Market_daily_ret']]
# -

# ## Performance statistics

# + code_folding=[]
import calendar

def print_backtest_stats(df, strat_pnl, strat_ir, market_pnl, market_ir, strat_name="sell_in_may", strat_params=(0,0)):
    if "ma_crossover" in strat_name: 
        if strat_params[0] >= strat_params[1]: # fast_ma >= slow_ma
            strat_name="buy_and_hold"
        else:
            print("Strategy: ma_crossover({}-{})".format(strat_params[0], strat_params[1]))
    
    elif "sell_in_may" in strat_name:
        # strat_params[0] = Sell month    # strat_params[1] = sell duration
        #print(f'strat_params[0] = {strat_params[0]} \tstrat_params[1] = {strat_params[1]}')
        if (strat_params[0] == 0) | (strat_params[1] == 0) | (strat_params[0] > 12) | (strat_params[1] > 12): # Buy and hold 
            strat_name="buy_and_hold"
        else:
            month_sell = calendar.month_name[strat_params[0]] # calendar.month_abbr
            month_buy = calendar.month_name[((strat_params[0] + strat_params[1] - 1) % 12) + 1 ]
            print(f'Strategy: sell_in_may_and_go_away(short from {month_sell} - long from {month_buy})')
            
    if "buy_and_hold" in strat_name:
        print("Strategy: buy_and_hold()")
        strat_pnl = market_pnl
        strat_ir = market_ir
    
    print("Period: {:%Y-%m-%d} to {:%Y-%m-%d}".format(df.index[0], df.index[-1]))

    print("\tOverall return of SP500: {:+.2f} %. IR of SP500: {:.2f}".format(market_pnl, market_ir))
    print("\tOverall return of strategy: {:+.2f} %. IR strategy: {:.2f}".format(strat_pnl, strat_ir))

    return


# + code_folding=[]
def calculate_annualized_return_compounded(daily_ret):
    n = len(daily_ret) # This one excludes the first row

    ARC = ((daily_ret.add(1).cumprod()[-1])**(252/n) - 1) * 100
    
    return ARC


# + code_folding=[]
def calculate_information_ratio(daily_ret):
    ARC = calculate_annualized_return_compounded(daily_ret)
    aSD = calculate_annualized_st_dev(daily_ret)
    
    if aSD == 0:
        return 0.00
    
    IR = ARC/aSD
    
    return np.round(IR, 4)


# + code_folding=[]
def calculate_annualized_st_dev(daily_ret):
    aSD = (daily_ret.std()) * (252**0.5) * 100 # Same as df_1['Strat_daily_ret'].std() * (252**0.5) * 100
    
    return aSD


# + code_folding=[]
def calculate_performance_metrics(df, strat_name='MA Crossover'):    
    metrics = ['AbsRet', 'ARC', 'IR', 'aSD', 'MD', 'AMD', 'MLD', 'IR**', 'All Risk', 'ARCMD', 'ARCAMD', 'Num Trades', 'Out of market']
    #metrics = ['AbsRet', 'ARC', 'IR', 'aSD', 'MD']

    n = len(df['Market_daily_ret']) # This one excludes the first row
    
    # Strategy
    AbsRet = (df['Strat_daily_ret'].add(1).cumprod().sub(1)[-1]) * 100
    ARC = calculate_annualized_return_compounded(df['Strat_daily_ret'])
    aSD = calculate_annualized_st_dev(df['Strat_daily_ret'])
    IR = ARC/aSD
    MD = abs(qs.stats.max_drawdown(df['Strat_daily_ret'])) * 100
    AMD = abs(df['Strat_daily_ret'].groupby(by=df.index.year).apply(qs.stats.max_drawdown).mean()*100)
    MLD = qs.stats.drawdown_details(qs.stats.to_drawdown_series(df['Strat_daily_ret']))['days'].max()/365.25
    IR_2 = (ARC*ARC)/(aSD*MD)
    all_risk = aSD*MD*AMD*MLD / 10000
    ARCMD = ARC/MD
    ARCAMD = ARC/AMD
    num_trades = int(df['Strat_position'].sub(df['Strat_position'].shift(fill_value=0)).abs().sum().round())
    no_signal = len(df[df['Strat_position'] == 0.0])

    
    metrics_data = [[AbsRet, ARC, IR, aSD, MD, AMD, MLD, IR_2, all_risk, ARCMD, ARCAMD, num_trades, no_signal]]
    performance_metrics = pd.DataFrame(data=metrics_data, index=[strat_name], columns=metrics)
    
    # Buy & Hold
    AbsRet_bh = (df['Market_daily_ret'].add(1).cumprod().sub(1)[-1]) * 100
    ARC_bh = calculate_annualized_return_compounded(df['Market_daily_ret'])
    aSD_bh = calculate_annualized_st_dev(df['Market_daily_ret'])
    IR_bh = ARC_bh/aSD_bh
    MD_bh = abs(qs.stats.max_drawdown(df['Market_daily_ret'])) * 100
    AMD_bh = abs(df['Market_daily_ret'].groupby(by=df.index.year).apply(qs.stats.max_drawdown).mean()*100)
    MLD_bh = qs.stats.drawdown_details(qs.stats.to_drawdown_series(df['Market_daily_ret']))['days'].max()/365.25
    IR_2_bh = (ARC_bh*ARC_bh)/(aSD_bh*MD_bh)
    all_risk_bh = aSD_bh*MD_bh*AMD_bh*MLD_bh / 10000
    ARCMD_bh = ARC_bh/MD_bh
    ARCAMD_bh = ARC_bh/AMD_bh
    num_trades_bh = 1
    no_signal_bh = 0


    buy_and_hold_data = [[AbsRet_bh, ARC_bh, IR_bh, aSD_bh, MD_bh, AMD_bh, MLD_bh, IR_2_bh,
                          all_risk_bh, ARCMD_bh, ARCAMD_bh, num_trades_bh, no_signal_bh]]
    buy_and_hold_row = pd.DataFrame(data=buy_and_hold_data, index=['Buy and Hold'], columns=metrics)
    
    performance_metrics = pd.concat([performance_metrics, buy_and_hold_row], axis='index')
    
    return performance_metrics.round(4)

StarcoderdataJupyterScriptsDedupFiltered