################################################
##
## This module generates an EventDayChain object.
##
## This workhorse object is used to analyse event studies
## and other types of experiments.
##
## See examples/analyse files for more information.
##
################################################
## 
## CHANGELOG
##
## 1.0 - 25 Apr 2013
## * First public version
##
################################################

package Finance::EventDayChain;

use strict;
use warnings;
use Carp;
use PDL;
use PDL::Stats;
use DBI;
use DBD::mysql;
use Statistics::Descriptive;
use Finance::SingleTradingDay;
use Data::Dumper;

################################################

our $AUTHOR  = 'Joachim De Schrijver';
our $VERSION = '1.0';
our $DATE    = '25 Apr 2013';
our $MODULE  = 'Finance::EventDayChain'; 

#################
## Constructor ##
#################

sub new {

	# Set class name
	my $class = shift;
	
	# Init obj
	my $self = {	
		ticker              => undef,
		Xchange             => undef,
		date                => undef,
		chain               => undef,
                window_upstream     => undef,
                window_downstream   => undef,
                normal_return       => undef,
                cum_normal_return   => undef,
                real_cum_normal_return   => undef,
                reference_index     => undef,
                reference_HML       => undef,
                reference_SMB       => undef,                
                ref_sanity_check    => 0,
                ref_intercept       => undef,
                ref_index_slope     => undef,
                ref_HML_slope       => undef,
		ref_SMB_slope       => undef,
		ref_intercept_t     => undef,
		ref_index_slope_t   => undef,
		ref_HML_slope_t     => undef,
		ref_SMB_slope_t     => undef,
                ref_r2              => undef,
                abnormal_return     => undef,
                abnormal_variance   => undef,
                cum_abnormal_return => undef
	};

	# Bless
	bless($self, $class);

	# Return blessed obj
	return $self;
}

#############
## Setters ##
#############

sub set_ticker {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{ticker} = $a;
}

sub set_Xchange {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{Xchange} = $a;
}

sub set_date {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{date} = $a;
}

sub set_chain {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{chain} = $a;
}

sub set_window_upstream {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{window_upstream} = $a;
}

sub set_window_downstream {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{window_downstream} = $a;
}

sub set_normal_return {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{normal_return} = $a;
}

sub set_real_cumulative_normal_return {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{real_cum_normal_return} = $a;
}

sub set_reference_index {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{reference_index} = $a;
}

sub set_reference_HML {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{reference_HML} = $a;
}

sub set_reference_SMB {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{reference_SMB} = $a;
}

sub set_ref_sanity_check {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{ref_sanity_check} = $a;
}

sub set_ref_intercept {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{ref_intercept} = $a;
}

sub set_ref_index_slope {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{ref_index_slope} = $a;
}

sub set_ref_HML_slope {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{ref_HML_slope} = $a;
}

sub set_ref_SMB_slope {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{ref_SMB_slope} = $a;
}

sub set_ref_intercept_t {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{ref_intercept_t} = $a;
}

sub set_ref_index_slope_t {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{ref_index_slope_t} = $a;
}

sub set_ref_HML_slope_t {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{ref_HML_slope_t} = $a;
}

sub set_ref_SMB_slope_t {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{ref_SMB_slope_t} = $a;
}

sub set_ref_r2 {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{ref_r2} = $a;
}

sub set_abnormal_return {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{abnormal_return} = $a;
}

sub set_abnormal_variance {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{abnormal_variance} = $a;
}

sub set_cumulative_abnormal_return {
	
	# Read params
	my ($self, $a) = @_;
	
	# Set
	$self->{cum_abnormal_return} = $a;
}

#############
## Getters ##
#############

sub get_ticker {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{ticker});
}

sub get_Xchange {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{Xchange});
}

sub get_date {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{date});
}

sub get_window_upstream {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{window_upstream});
}

sub get_window_downstream {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{window_downstream});
}

sub get_chain {

	# Read params
	my $self = shift;
	
	# Return
	return($self->{chain});
}

sub get_chain_item {
	
	# Read params
	my @params = @_;
	my $self = $params[0];
	my $nr   = $params[1];
	
	# Check if defined
	if (defined $self->get_chain()->{$nr}){
		return $self->get_chain()->{$nr}
	}
	else{
		croak "Cannot get chain item: undefined item number!\nMaybe you need to retrieve data using retrieve_SingleTradingDays_around_window first?\n";
	}
}

sub get_normal_return {

	# Read params
	my $self = shift;
	
	# Return
	return($self->{normal_return});	
}

sub get_real_cumulative_normal_return {

	# Read params
	my $self = shift;
	
	# Return
	return($self->{real_cum_normal_return});	
}

sub get_reference_index {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{reference_index});
}

sub get_reference_HML {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{reference_HML});
}

sub get_reference_SMB {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{reference_SMB});
}

sub get_ref_sanity_check {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{ref_sanity_check});
}

sub get_ref_intercept {
	
	# Read params
	my $self = shift; 
	
	# Return
	return($self->{ref_intercept});
}

sub get_ref_index_slope {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{ref_index_slope});
}

sub get_ref_HML_slope {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{ref_HML_slope});
}

sub get_ref_SMB_slope {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{ref_SMB_slope});
}

sub get_ref_intercept_t {
	
	# Read params
	my $self = shift; 
	
	# Return
	return($self->{ref_intercept_t});
}

sub get_ref_index_slope_t {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{ref_index_slope_t});
}

sub get_ref_HML_slope_t {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{ref_HML_slope_t});
}

sub get_ref_SMB_slope_t {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{ref_SMB_slope_t});
}

sub get_ref_r2 {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{ref_r2});
}

sub get_abnormal_return {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{abnormal_return});
}

sub get_abnormal_variance {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{abnormal_variance});
}

sub get_cumulative_abnormal_return {
	
	# Read params
	my $self = shift;
	
	# Return
	return($self->{cum_abnormal_return});
}

##############
## Changers ##
##############

sub add_chain_item {

	# Read params
	my @params    = @_;
	my $self      = $params[0];
	my $position  = $params[1];
	my $chain_item= $params[2];

	# Get chain
	my $chain = $self->get_chain();
	
	# Add to chain
	$chain->{$position} = $chain_item;
	
	# Set chain
	$self->set_chain($chain);
}

######################
## Object functions ##
######################

###########
# Retrieves SingleTradingDay objects around event date.
# Needs to be called on an empty Finance::EventDayChain object.
#
# Input [array]:
#   1) date (yyyy-mm-dd)
#   2) bourse (e.g. NASDAQ, LSE...)
#   3) ticker (e.g. AAPL, GOOG...)
#   4) window size downstream
#   5) window size upstream
#   6) database handler
# 
# Output [no return]:
#   1) Finance::EventDayChain object [modified]
#
###########

sub retrieve_SingleTradingDays_around_window {
	
	# Catch params
	my @params = @_;
	my $self   = $params[0];
	my $date   = $params[1];
	my $bourse = $params[2];
	my $ticker = $params[3];
	my $window1= $params[4];
	my $window2= $params[5];
	my $dbh    = $params[6];
	
	# Set 
	$self->set_ticker($ticker);
	$self->set_Xchange($bourse);
	$self->set_date($date);
	$self->set_window_downstream($window2);
	$self->set_window_upstream($window1);
		
	# Fetch data upstream
	my $window2_fix = 0;
	if ($window2 < 0){
		$window2_fix = -$window2;
	}
	if ($window1 >= 0){
		my $query = "SELECT * FROM _prices_".$bourse."_".$ticker." WHERE date <= \'".$date."\' ORDER BY date DESC LIMIT ".($window2_fix).",".($window1 + 2 - $window2_fix);	# +2: Need an additional day back to be able to calculate first day return (T - T-1 / T-1)
		my $sth = $dbh->prepare($query);
		$sth->execute();
		my $window_pos1 = 0 - $window2_fix;
		while (my $ref = $sth->fetchrow_hashref){
			bless($ref,"Finance::SingleTradingDay");
			$self->add_chain_item($window_pos1,$ref);
			$window_pos1--;
		}
		$sth->finish();
	}
	
	# Fetch data downstream
	my $window1_fix = 0;
	if ($window1 < 0){
		$window1_fix = -$window1 - 1;
	}
	
	if ($window2 >= 0){
		my $query2 = "SELECT * FROM _prices_".$bourse."_".$ticker." WHERE date > \'".$date."\' ORDER BY date ASC LIMIT ".($window1_fix).",".($window2 - $window1_fix);		
		my $sth2 = $dbh->prepare($query2);
		$sth2->execute();
		my $window_pos2 = 1 + $window1_fix;
		while (my $ref = $sth2->fetchrow_hashref){
			bless($ref,"Finance::SingleTradingDay");
			$self->add_chain_item($window_pos2,$ref);
			$window_pos2++;
		}	
		$sth2->finish();
	}
}

###########
# Calculates normal returns in a Finance::EventDayChain object.
# Normal return = [price(t)-price(t-1)] / price (t-1)
# Needs to be called on a non-empty Finance::EventDayChain object.
#
# Input:
#   None
# 
# Output [no return]:
#   1) Finance::SingleTradingDay object [modified]
#
###########

sub calculate_chain_normal_return {

	# Read params
	my @params = @_;
	my $self = $params[0];
	
	# Retrieve working space
	my $window_start = -($self->get_window_upstream());	
	my $window_stop  = $self->get_window_downstream();
	
	if ($window_start > 0){
		$window_start++;
	}
	
	# Calculate normal returns
	my $hash = {};
	for (my $day = $window_start; $day <= $window_stop; $day++){
		my $return = $self->calculate_return($day);
		$hash->{$day} = $return;
	}

	# Send to obj
	$self->set_normal_return($hash);
}

###########
# Calculates cumulative normal returns in a Finance::EventDayChain object.
# Cumulative normal return = ([1 + cumulative_return(t-1)] * [1 + normal_return(t)]) - 1
# Needs to be called on a non-empty Finance::EventDayChain object.
#
# Input:
#   None
# 
# Output:
#   1) Finance::SingleTradingDay object [modified]
#
###########

sub calculate_chain_real_cumulative_normal_returns{
	
	# Read params
	my @params = @_;
	my $self = $params[0];
	
	# Retrieve working space
	my $window_start = -($self->get_window_upstream());
	my $window_stop  = $self->get_window_downstream();
	
	if ($window_start > 0){
		$window_start++;
	}
	
	# Calculate cumulative normal returns
	my $hash = {};
	$hash->{($window_start - 1)} = 0;
	for (my $day = $window_start; $day <= $window_stop; $day++){
		my $return = $self->calculate_return($day);
		$hash->{$day} = ((1+$hash->{($day -1)}) * (1+$return)) - 1;
	}

	# Send to obj
	$self->set_real_cumulative_normal_return($hash);
}

###########
# Attaches a reference index to an existing Finance::EventDayChain object
#
# Input:
#   1) Finance::EventDayChain object (sanity check in place)
# 
# Output [no return]:
#   1) Finance::EventDayChain object [modified]
#
###########

sub attach_index_reference {
	
	# Read params
	my @params = @_;
	my $self = $params[0];
	my $ref  = $params[1];
	
	# Check object type
	if (ref($ref) ne $MODULE){
		
		# Croak if wrong object type
		croak "Cannot attach reference: The reference you tried to attach is not an EventDayChain object!\n";
	}
	else{
		# Set index if correct object type
		$self->set_reference_index($ref);
	}
}

###########
# Attaches a FamaFrench HML index to an existing Finance::EventDayChain object
#
# Input:
#   1) Finance::EventDayChain object (sanity check in place)
# 
# Output [no return]:
#   1) Finance::EventDayChain object [modified]
#
###########

sub attach_HML_reference {
	
	# Read params
	my @params = @_;
	my $self = $params[0];
	my $ref  = $params[1];
	
	# Check object type
	if (ref($ref) ne $MODULE){
		
		# Croak if wrong object type
		croak "Cannot attach reference: The reference you tried to attach is not an EventDayChain object!\n";
	}
	else{
		# Set index if correct object type
		$self->set_reference_HML($ref);
	}
}

###########
# Attaches a FamaFrench SMB index to an existing Finance::EventDayChain object
#
# Input:
#   1) Finance::EventDayChain object (sanity check in place)
# 
# Output [no return]:
#   1) Finance::EventDayChain object [modified]
#
###########

sub attach_SMB_reference {
	
	# Read params
	my @params = @_;
	my $self = $params[0];
	my $ref  = $params[1];
	
	# Check object type
	if (ref($ref) ne $MODULE){
		
		# Croak if wrong object type
		croak "Cannot attach reference: The reference you tried to attach is not an EventDayChain object!\n";
	}
	else{
		# Set index if correct object type
		$self->set_reference_SMB($ref);
	}
}

###########
# Checks references and chain of stock returns for consistency.
# -Checks whether a reference is attached
# -Checks whether windows have same length
# -Checks whether windows have same days
# =>If checks ok, then sanity boolean is set to 1
# Sanity boolean is used in other reference functions
#
# Input [array]
#   1) FamaFrench_check: boolean to indicate whether FamaFrench of single factor
#      consistency need to be checked (i.e. ignore SMB/MHL if allowed)
# 
# Output [no return]:
#   1) Finance::EventDayChain object [modified]
#
###########

sub reference_sanity_check {

	# Read params
	my @params = @_;
	my $self = $params[0];
	my $FamaFrench_check = $params[1];
	
	# Initiate error bool;
	my $error = 0;
	
	# Check whether a reference is defined
	if (not defined($self->get_reference_index())){
		$error = 1;
		print "Warning: No reference index is defined!";
		goto EXIT;
	}
	
	elsif (($FamaFrench_check) && (not defined($self->get_reference_HML()))){
		$error = 1;
		print "Warning: No reference HML is defined in the Fama French model!";
		goto EXIT;
	}
	
	elsif (($FamaFrench_check) && (not defined($self->get_reference_SMB()))){
		$error = 1;
		print "Warning: No reference SMB is defined in the Fama French model!";
		goto EXIT;
	}	
		
	# Check whether reference and security upstream window size are the same
	elsif ($self->get_reference_index->get_window_upstream() != $self->get_window_upstream()){
		$error = 1;
		print "Warning: Reference chain and security chain have different upstream window sizes!";
		goto EXIT;	
	}

	elsif (($FamaFrench_check) && ($self->get_reference_HML->get_window_upstream() != $self->get_window_upstream())){
		$error = 1;
		print "Warning: Reference HML chain and security chain have different upstream window sizes!";
		goto EXIT;	
	}	

	elsif (($FamaFrench_check) && ($self->get_reference_SMB->get_window_upstream() != $self->get_window_upstream())){
		$error = 1;
		print "Warning: Reference SMB chain and security chain have different upstream window sizes!";
		goto EXIT;	
	}
	
	# Check whether reference and security downstream window size are the same
	elsif ($self->get_reference_index->get_window_downstream() != $self->get_window_downstream()){
		$error = 1;
		print "Warning: Reference chain and security chain have different downstream window sizes!";
		goto EXIT;	
	}
	
	elsif (($FamaFrench_check) && ($self->get_reference_HML->get_window_downstream() != $self->get_window_downstream())){
		$error = 1;
		print "Warning: HML chain and security chain have different downstream window sizes!";
		goto EXIT;	
	}
	
	elsif (($FamaFrench_check) && ($self->get_reference_SMB->get_window_downstream() != $self->get_window_downstream())){
		$error = 1;
		print "Warning: SMB chain and security chain have different downstream window sizes!";
		goto EXIT;	
	}
	
	# Check whether reference and security have same date at [upstream_window]
	elsif ($self->get_reference_index->get_chain_item(-($self->get_reference_index->get_window_upstream()))->get_date() ne $self->get_chain_item(-($self->get_window_upstream()))->get_date()){
		$error = 1;
		print "Warning: Reference chain and security chain have different date at [upstream window]!";
		goto EXIT;	
	}

	elsif (($FamaFrench_check) && ($self->get_reference_HML->get_chain_item(-($self->get_reference_HML->get_window_upstream()))->get_date() ne $self->get_chain_item(-($self->get_window_upstream()))->get_date())){
		$error = 1;
		print "Warning: HML chain and security chain have different date at [upstream window]!";
		goto EXIT;	
	}
	
	elsif (($FamaFrench_check) && ($self->get_reference_SMB->get_chain_item(-($self->get_reference_SMB->get_window_upstream()))->get_date() ne $self->get_chain_item(-($self->get_window_upstream()))->get_date())){
		$error = 1;
		print "Warning: SMB chain and security chain have different date at [upstream window]!";
		goto EXIT;	
	}
	
	# Check whether reference and security have same date at [downstream_window]
	elsif ($self->get_reference_index->get_chain_item($self->get_reference_index->get_window_downstream())->get_date() ne $self->get_chain_item($self->get_window_downstream())->get_date()){
		$error = 1;
		print "Warning: Reference chain and security chain have different date at [downstream window]!";
		goto EXIT;	
	}
	
	elsif (($FamaFrench_check) && ($self->get_reference_HML->get_chain_item($self->get_reference_HML->get_window_downstream())->get_date() ne $self->get_chain_item($self->get_window_downstream())->get_date())){
		$error = 1;
		print "Warning: HML chain and security chain have different date at [downstream window]!";
		goto EXIT;	
	}
	
	elsif (($FamaFrench_check) && ($self->get_reference_SMB->get_chain_item($self->get_reference_SMB->get_window_downstream())->get_date() ne $self->get_chain_item($self->get_window_downstream())->get_date())){
		$error = 1;
		print "Warning: SMB chain and security chain have different date at [downstream window]!";
		goto EXIT;	
	}

	# Send to obj
EXIT:	if ($error > 0){
		$self->set_ref_sanity_check(0);
	}
	else{
		$self->set_ref_sanity_check(1);
	}
}

###########
# Calculates single factor model regression.
#
# Input:
#   None
# 
# Output [no return]:
#   1) Finance::EventDayChain object [modified]
#
###########

sub calculate_alfa_beta {
	
	# Read params
	my @params = @_;
	my $self = $params[0];
	
	# Sanity check
	if ($self->get_ref_sanity_check() == 0){
		croak "Error in $MODULE object: tried to regress single factor model but sanity check was not set to 1.\n Maybe you forgot to run reference_sanity_check or you did and it failed?\n";
	}
	
	# Fetch indexes
	my $ref  = $self->get_reference_index();
	
	# Calculate returns security
	$self->calculate_chain_normal_return();
	
	# Calculate returns reference
	$ref->calculate_chain_normal_return();
	
	# Get returns
	my $asset_returns = $self->get_normal_return();
	my $ref_returns   = $ref->get_normal_return();
	
	# Convert to array
	my @asset;
	my @ref;
	foreach my $day (keys %$asset_returns){
		push (@asset,$asset_returns->{$day});
		push (@ref,$ref_returns->{$day});
	}
	
	# Convert to PDL
	my $asset_pdl = pdl @asset;
	my $ref_pdl   = pdl @ref;
	
	# Calculate OLS coefficients
	my %coeffs = $asset_pdl->ols($ref_pdl, {plot=>0} );
	
	# Catch
	my $alfbet = $coeffs{b};
	my $R      = $coeffs{R2};
	
	# Store in object
	$self->set_ref_intercept($alfbet->index(1));
	$self->set_ref_index_slope($alfbet->index(0));
	$self->set_ref_r2($R);
}

###########
# Calculates Fama French regression.
#
# Input:
#   None
# 
# Output [no return]:
#   1) Finance::EventDayChain object [modified]
#
###########

sub calculate_Fama_French {

	# Initialize packages
	use Statistics::Regression;

	# Read params
	my @params = @_;
	my $self = $params[0];
	
	# Sanity check
	if ($self->get_ref_sanity_check() == 0){
		croak "Error in $MODULE object: tried to regress Fama French model but sanity check was not set to 1.\n Maybe you forgot to run reference_sanity_check or you did and it failed?\n";
	}
	
	# Fetch indexes
	my $index  = $self->get_reference_index();
	my $HML    = $self->get_reference_HML();
	my $SMB    = $self->get_reference_SMB();
	
	# Calculate returns security
	$self->calculate_chain_normal_return();
	
	# Calculate reference returns
	$index->calculate_chain_normal_return();
	$HML->calculate_chain_normal_return();
	$SMB->calculate_chain_normal_return();
	
	# Get returns
	my $asset_returns = $self->get_normal_return();
	my $index_returns = $index->get_normal_return();
	my $HML_returns   = $HML->get_normal_return();
	my $SMB_returns   = $SMB->get_normal_return();
	
	# Convert to array
	my @asset;
	my @index;
	my @HML;
	my @SMB;
	
	foreach my $day (keys %$asset_returns){
		push (@asset,$asset_returns->{$day});
		push (@index,$index_returns->{$day});
		push (@HML,$HML_returns->{$day});
		push (@SMB,$SMB_returns->{$day});
	}
	
	# Create regression object
	my $reg = Statistics::Regression->new("sample regression", ["const", "Rm", "HML", "SMB"]);
	
	# Generate data frame
	for (my $i=0; $i< scalar(@index); $i++){
		$reg->include($asset[$i], [1.0, $index[$i], $HML[$i], $SMB[$i]]);
	}
	
	# Store regression coefficients and t stats
	my @standard_errors = $reg->standarderrors();
	my @thetas = $reg->theta();
	for (my $i=0; $i < $reg->k();$i++){
		my $error = $standard_errors[$i];
		my $theta = $thetas[$i];
		my $tStat = $theta/$error;
	
		if ($i == 0){
			$self->set_ref_intercept($theta);
			$self->set_ref_intercept_t($tStat);
		}
		elsif($i == 1){
			$self->set_ref_index_slope($theta);
			$self->set_ref_index_slope_t($tStat);		
		}
		elsif($i == 2){
			$self->set_ref_HML_slope($theta);
			$self->set_ref_HML_slope_t($tStat);		
		}		
		elsif($i == 3){
			$self->set_ref_SMB_slope($theta);
			$self->set_ref_SMB_slope_t($tStat);		
		}			
	}
	
	# Store R2
	$self->set_ref_r2($reg->rsq());
}

###########
# Calculates abnormal returns.
#
# Input:
#   None
# 
# Output [no return]:
#   1) Finance::EventDayChain object [modified]
#
###########

sub calculate_chain_abnormal_return {

	# Read params
	my @params = @_;
	my $self = $params[0];
	my $type = $params[1];
	
	# Sanity checks
	if (not defined $type){
		croak "Error in $MODULE object: forgot to define model type to calculate abnormal returns.\n";
	}
	
	if (not defined $self->get_ref_index_slope() || not defined $self->get_ref_intercept()){
		croak: "Error in $MODULE object: tried to calculate abnormal returns but no coefficients are defined.\nPerhaps you forgot to run calculate_Fama_French or calculate_alfa_beta?\n";
	}
	
	# Factor model
	if ($type == 0){
	
		# Fetch references
		my $ref = $self->get_reference_index();

		# Init
		my $abnormal = {};
		my @values;

		# Calculate abnormal returns
		foreach my $day (keys %{$self->get_normal_return()}){
			my $abn_return = $self->get_normal_return()->{$day} - $ref->get_normal_return()->{$day}*$self->get_ref_index_slope() - $self->get_ref_intercept();	
			$abnormal->{$day} = $abn_return;
			push(@values,$abn_return)
		}

		# Calculate variance of abnormal returns
		my $stat_obj = Statistics::Descriptive::Full->new();
		$stat_obj->add_data(@values);
		my $variance = $stat_obj->variance();

		# Send to obj
		$self->set_abnormal_return($abnormal);
		$self->set_abnormal_variance($variance);
	}
	
	# Fama French
	elsif($type == 1){
	
		# Fetch references
		my $ref = $self->get_reference_index();
		my $SMB = $self->get_reference_HML();
		my $HML = $self->get_reference_SMB();

		# Init
		my $abnormal = {};
		my @values;

		# Calculate abnormal returns
		foreach my $day (keys %{$self->get_normal_return()}){
			my $abn_return = $self->get_normal_return()->{$day} - $ref->get_normal_return()->{$day}*$self->get_ref_index_slope() - $SMB->get_normal_return()->{$day}*$self->get_ref_SMB_slope - $HML->get_normal_return()->{$day}*$self->get_ref_HML_slope - $self->get_ref_intercept();	
			$abnormal->{$day} = $abn_return;
			push(@values,$abn_return)
		}

		# Calculate variance of abnormal returns
		my $stat_obj = Statistics::Descriptive::Full->new();
		$stat_obj->add_data(@values);
		my $variance = $stat_obj->variance();

		# Send to obj
		$self->set_abnormal_return($abnormal);
		$self->set_abnormal_variance($variance);	
	}
	# Unspecified type
	else{
		croak("Error in $MODULE object: cannot calculate abnormal returns using an unknown model.\n Can only be 0 (single factor) or 1 (Fama French)");
	}
}

###########
# Normalizes a chain of abnormal returns.
#
# Input [array]:
#   1) Variance
# 
# Output [no return]:
#   1) Finance::EventDayChain object [modified]
#
###########

sub normalize_chain_abnormal_return {

	# Read params
	my $self = $_[0];
	my $variance = $_[1];

	# Get abnormal returns
	my $chain = $self->get_abnormal_return();

	# Calculate standard error
	my $stdev = sqrt($variance);

	# Normalize	
	foreach (keys %$chain){
		$chain->{$_} /=$stdev;
	}

	# Send to obj
	$self->set_abnormal_return($chain);
	$self->set_abnormal_variance(1);
}

###########
# Calculate cumulative abnormal returns
#
# Input:
#   None
# 
# Output [no return]:
#   1) Finance::EventDayChain object [modified]
#
###########

sub calculate_chain_abnormal_cumulative_return {

	# Read params
	my @params = @_;
	my $self = $params[0];
	
	# Get normal abnormal returns
	my $returns = $self->get_abnormal_return();
	
	# Order dates
	my @days = sort {$a <=> $b} keys %$returns;
	
	# Calculate cumulative returns
	my $hash = {};
	$hash->{$days[0]} = $returns->{$days[0]};
	for (my $i=1; $i < scalar(@days); $i++){
		$hash->{$days[$i]} = $hash->{$days[($i - 1)]} + $returns->{$days[$i]};
	}
	
	# Send to obj
	$self->set_cumulative_abnormal_return($hash);
}

#################
## Subroutines ##
#################

###########
# Calculates a standard daily return of a given day in a Finance::EventDayChain object.
# Standard daily return = [daily_price(t) - daily_price(t-1)] / daily_price(t-1)
# Needs to be called on a non-empty Finance::EventDayChain object.
#
# Input:
#   1) Day number [need to add sanity check ~ window]
# 
# Output:
#   1) daily return [float]
#
###########

sub calculate_return {

	# Read params
	my @params = @_;
	my $self = $params[0];
	my $day = $params[1];
	
	# Calculate return
	my $return = ($self->get_chain_item($day)->get_price_closing_adj() - $self->get_chain_item($day - 1)->get_price_closing_adj())/$self->get_chain_item($day - 1)->get_price_closing_adj();
	
	# Return
	return($return);
}

1;