<reponame>wthamisupposedtowritethere/Simple-Backtest-Environment<filename>src/btengine/financefunctions.py
# -*- coding: utf-8 -*-
"""
Created on Wed Oct  7 23:12:15 2020

@author: Antho
"""

import numpy as np
import statsmodels.api as sm
from statsmodels import regression
from scipy.stats import linregress
from scipy.stats import norm

def skewness(r):
    '''
        ARGS:
            Series or Dataframe
        RETURNS: 
            Float or a series data with the calculated skewness
    '''
    
    # Calculate the demeaned returns 
    demeaned_r = r - r.mean()
    
    # Use the population standard deviation, ddof=0
    sigma_r = r.std(ddof=0)
    
    # Calculate the expectation of the demeaned returns raised to the third power
    exp = (demeaned_r**3).mean()
    
    # Calcualte the skew
    if(sigma_r != 0):
        skew = exp/sigma_r**3
        return skew
    return exp

# Make a kurtosis function 
def kurtosis(r):
    '''
        ARGS:
            Series or Dataframe
        RETURNS: 
            Float or a series data with the calculated kurtosis
    '''
    
    # Calculate the demeaned returns 
    demeaned_r = r - r.mean()
    
    # Use the population standard deviation, ddof=0
    sigma_r = r.std(ddof=0)
    
    # Calculate the expectation of the demeaned returns raised to the fourth power
    exp = (demeaned_r**4).mean()
    
    # Calcualte the skew
    if(sigma_r != 0):
        kurt = exp/sigma_r**4
        return kurt
    else:
        return exp
    
def modVaR(returns):
    k = kurtosis(returns)
    s = skewness(returns)
    z = norm.ppf(0.05)
    
    z = (z + (z**2 - 1)*s/6 + (z**3 - 3*z)*(k-3)/24 - (2 * z**3 - 5*z)*(s**2)/36)
    
    mcf_var = - (returns.mean() + z * returns.std(ddof=0))
    return(mcf_var)
    
def sd_pos(returns):
    average = returns.mean()
    returns_negative = returns[returns < average]
    if(len(returns_negative) > 0):
        return np.sqrt( (1/len(returns_negative)) * np.power((average - returns_negative), 2).sum() )
    else:
        return np.sqrt( (1) * np.power(average, 2).sum() )
        
def momentum(closes):
    # MAJOR UPDATE: RETURNS ARE NOW IN THE FILE
    returns = np.log(closes)
    x = np.arange(len(returns))
    slope, _, rvalue, _, _ = linregress(x, returns)
    return ((1 + slope) ** 252) * (rvalue ** 2)  # annualize slope and multiply by R^2

def alpha_beta(x, y):
    """
    list(float)*2 ->  float, float
    
    Performs a linear regression y = alpha + beta * x and returns its coefficients.
    """
    # Fit regression model

    x = sm.add_constant(x)
    model = regression.linear_model.OLS(x, y).fit()
    
    x = x[:, 1]
    
    # Returns alpha, beta
    return model.params[0][0], model.params[0][1]
    
def information_coefficient(returns, benchmark_returns):
    if(returns.isnull().mean() > 0.1):
        return np.NaN
    
    returns = returns[~returns.isnull()]
    benchmark_returns = benchmark_returns.loc[returns.index]
    
    exces_rendement = (returns - benchmark_returns)
    
    _, coeff_correlation = alpha_beta(list(exces_rendement)[:-7], list(exces_rendement.shift(-7))[:-7])
    
    IC = (coeff_correlation * exces_rendement.std() * (exces_rendement -  exces_rendement.mean())/exces_rendement.std())
    return IC.sum()
# -*- coding: utf-8 -*-
"""
Created on Wed Oct  7 23:12:15 2020

@author: Antho
"""

import numpy as np
import statsmodels.api as sm
from statsmodels import regression
from scipy.stats import linregress
from scipy.stats import norm

def skewness(r):
    '''
        ARGS:
            Series or Dataframe
        RETURNS: 
            Float or a series data with the calculated skewness
    '''
    
    # Calculate the demeaned returns 
    demeaned_r = r - r.mean()
    
    # Use the population standard deviation, ddof=0
    sigma_r = r.std(ddof=0)
    
    # Calculate the expectation of the demeaned returns raised to the third power
    exp = (demeaned_r**3).mean()
    
    # Calcualte the skew
    if(sigma_r != 0):
        skew = exp/sigma_r**3
        return skew
    return exp

# Make a kurtosis function 
def kurtosis(r):
    '''
        ARGS:
            Series or Dataframe
        RETURNS: 
            Float or a series data with the calculated kurtosis
    '''
    
    # Calculate the demeaned returns 
    demeaned_r = r - r.mean()
    
    # Use the population standard deviation, ddof=0
    sigma_r = r.std(ddof=0)
    
    # Calculate the expectation of the demeaned returns raised to the fourth power
    exp = (demeaned_r**4).mean()
    
    # Calcualte the skew
    if(sigma_r != 0):
        kurt = exp/sigma_r**4
        return kurt
    else:
        return exp
    
def modVaR(returns):
    k = kurtosis(returns)
    s = skewness(returns)
    z = norm.ppf(0.05)
    
    z = (z + (z**2 - 1)*s/6 + (z**3 - 3*z)*(k-3)/24 - (2 * z**3 - 5*z)*(s**2)/36)
    
    mcf_var = - (returns.mean() + z * returns.std(ddof=0))
    return(mcf_var)
    
def sd_pos(returns):
    average = returns.mean()
    returns_negative = returns[returns < average]
    if(len(returns_negative) > 0):
        return np.sqrt( (1/len(returns_negative)) * np.power((average - returns_negative), 2).sum() )
    else:
        return np.sqrt( (1) * np.power(average, 2).sum() )
        
def momentum(closes):
    # MAJOR UPDATE: RETURNS ARE NOW IN THE FILE
    returns = np.log(closes)
    x = np.arange(len(returns))
    slope, _, rvalue, _, _ = linregress(x, returns)
    return ((1 + slope) ** 252) * (rvalue ** 2)  # annualize slope and multiply by R^2

def alpha_beta(x, y):
    """
    list(float)*2 ->  float, float
    
    Performs a linear regression y = alpha + beta * x and returns its coefficients.
    """
    # Fit regression model

    x = sm.add_constant(x)
    model = regression.linear_model.OLS(x, y).fit()
    
    x = x[:, 1]
    
    # Returns alpha, beta
    return model.params[0][0], model.params[0][1]
    
def information_coefficient(returns, benchmark_returns):
    if(returns.isnull().mean() > 0.1):
        return np.NaN
    
    returns = returns[~returns.isnull()]
    benchmark_returns = benchmark_returns.loc[returns.index]
    
    exces_rendement = (returns - benchmark_returns)
    
    _, coeff_correlation = alpha_beta(list(exces_rendement)[:-7], list(exces_rendement.shift(-7))[:-7])
    
    IC = (coeff_correlation * exces_rendement.std() * (exces_rendement -  exces_rendement.mean())/exces_rendement.std())
    return IC.sum()
# -*- coding: utf-8 -*- Created on Wed Oct  7 23:12:15 2020
 
 @author: Antho ARGS:
             Series or Dataframe
         RETURNS: 
             Float or a series data with the calculated skewness # Calculate the demeaned returns # Use the population standard deviation, ddof=0 # Calculate the expectation of the demeaned returns raised to the third power # Calcualte the skew # Make a kurtosis function ARGS:
             Series or Dataframe
         RETURNS: 
             Float or a series data with the calculated kurtosis # Calculate the demeaned returns # Use the population standard deviation, ddof=0 # Calculate the expectation of the demeaned returns raised to the fourth power # Calcualte the skew # MAJOR UPDATE: RETURNS ARE NOW IN THE FILE # annualize slope and multiply by R^2 list(float)*2 ->  float, float
     
     Performs a linear regression y = alpha + beta * x and returns its coefficients. # Fit regression model # Returns alpha, beta