Skip to content
Browse files

Fixed broken spec (was testing an earlier implementation that used da…

…ily returns and was ignorant of dividends). Adn added more error checking.
  • Loading branch information...
1 parent faeb137 commit 602a6292264200bd6ddd4771724952f5cf0581c5 @jimlindstrom committed
Showing with 56 additions and 50 deletions.
  1. +10 −4 lib/finmodeling/capm.rb
  2. +4 −0 lib/finmodeling/fama_french_cost_of_equity.rb
  3. +42 −46 spec/company_beta_spec.rb
View
14 lib/finmodeling/capm.rb
@@ -30,21 +30,27 @@ class Beta
def self.from_ticker(company_ticker, num_days=6*365, index_ticker="SPY")
index_quotes = FamaFrench::EquityHistoricalData.new(index_ticker, num_days)
company_quotes = FamaFrench::EquityHistoricalData.new(company_ticker, num_days)
-
+
+ raise "no index returns" if !index_quotes || index_quotes.none?
+ raise "no company returns" if !company_quotes || company_quotes.none?
+
common_dates = index_quotes .year_and_month_strings &
company_quotes.year_and_month_strings
index_quotes .filter_by_date!(common_dates)
company_quotes.filter_by_date!(common_dates)
-
+
+ raise "no index returns (after filtering)" if !index_quotes || index_quotes.none?
+ raise "no company returns (after filtering)" if !company_quotes || company_quotes.none?
+
index_div_hist = NasdaqQuery::DividendHistory.for_symbol(index_ticker)
company_div_hist = NasdaqQuery::DividendHistory.for_symbol(company_ticker)
index_monthly_returns = index_quotes .monthly_returns(index_div_hist)
company_monthly_returns = company_quotes.monthly_returns(company_div_hist)
- raise "no index returns" if !index_monthly_returns || index_monthly_returns.none?
- raise "no company returns" if !company_monthly_returns || company_monthly_returns.none?
+ raise "no monthly index returns" if !index_monthly_returns || index_monthly_returns.none?
+ raise "no monthly company returns" if !company_monthly_returns || company_monthly_returns.none?
x = GSL::Vector.alloc(index_monthly_returns)
y = GSL::Vector.alloc(company_monthly_returns)
View
4 lib/finmodeling/fama_french_cost_of_equity.rb
@@ -87,6 +87,10 @@ def monthly_excess_returns(rf, dividends=[])
(pair[1].adjClose - pair[0].adjClose + dividend) / pair[0].adjClose
}
.zip(rf).map{ |pair| pair[0] - pair[1] }
+ end
+
+ def none?; @monthly_quotes.none?; end
+ def any?; @monthly_quotes.any?; end
def count; @monthly_quotes.count; end
end
View
88 spec/company_beta_spec.rb
@@ -1,53 +1,49 @@
require 'spec_helper'
+require 'date' # FIXME: stuff this somewhere else?
describe FinModeling::CAPM::Beta do
describe "#from_ticker" do
- it "returns the right value" do
- index_ticker = "SPY"
- mock_index_quotes = []
- mock_index_quotes << YahooFinance::HistoricalQuote.new("SPY", ["2013-02-05",153.66,154.7,153.64,154.29,121431900, 54.29])
- mock_index_quotes << YahooFinance::HistoricalQuote.new("SPY", ["2013-02-06",153.66,154.7,153.64,154.29,121431900, 52.29])
- mock_index_quotes << YahooFinance::HistoricalQuote.new("SPY", ["2013-02-07",153.66,154.7,153.64,154.29,121431900, 58.29])
- mock_index_quotes << YahooFinance::HistoricalQuote.new("SPY", ["2013-02-08",153.66,154.7,153.64,154.29,121431900, 57.29])
- mock_index_quotes << YahooFinance::HistoricalQuote.new("SPY", ["2013-02-09",153.66,154.7,153.64,154.29,121431900, 84.29])
- mock_index_quotes << YahooFinance::HistoricalQuote.new("SPY", ["2013-02-10",153.66,154.7,153.64,154.29,121431900, 73.29])
- mock_index_quotes << YahooFinance::HistoricalQuote.new("SPY", ["2013-02-11",153.66,154.7,153.64,154.29,121431900, 64.29])
- mock_index_quotes << YahooFinance::HistoricalQuote.new("SPY", ["2013-02-12",153.66,154.7,153.64,154.29,121431900, 54.29])
- mock_index_quotes << YahooFinance::HistoricalQuote.new("SPY", ["2013-02-13",153.66,154.7,153.64,154.29,121431900, 61.29])
- mock_index_quotes << YahooFinance::HistoricalQuote.new("SPY", ["2013-02-14",153.66,154.7,153.64,154.29,121431900, 44.29])
-
- company_ticker = "AAPL"
- mock_company_quotes = []
- mock_company_quotes << YahooFinance::HistoricalQuote.new("AAPL",["2013-02-05",153.66,154.7,153.64,154.29,121431900,154.29])
- mock_company_quotes << YahooFinance::HistoricalQuote.new("AAPL",["2013-02-06",153.66,154.7,153.64,154.29,121431900,152.29])
- mock_company_quotes << YahooFinance::HistoricalQuote.new("AAPL",["2013-02-07",153.66,154.7,153.64,154.29,121431900,158.29])
- mock_company_quotes << YahooFinance::HistoricalQuote.new("AAPL",["2013-02-08",153.66,154.7,153.64,154.29,121431900,157.29])
- mock_company_quotes << YahooFinance::HistoricalQuote.new("AAPL",["2013-02-09",153.66,154.7,153.64,154.29,121431900,184.29])
- mock_company_quotes << YahooFinance::HistoricalQuote.new("AAPL",["2013-02-10",153.66,154.7,153.64,154.29,121431900,173.29])
- mock_company_quotes << YahooFinance::HistoricalQuote.new("AAPL",["2013-02-11",153.66,154.7,153.64,154.29,121431900,164.29])
- mock_company_quotes << YahooFinance::HistoricalQuote.new("AAPL",["2013-02-12",153.66,154.7,153.64,154.29,121431900,154.29])
- mock_company_quotes << YahooFinance::HistoricalQuote.new("AAPL",["2013-02-13",153.66,154.7,153.64,154.29,121431900,161.29])
- mock_company_quotes << YahooFinance::HistoricalQuote.new("AAPL",["2013-02-14",153.66,154.7,153.64,154.29,121431900,144.29])
-
- num_days = 10
-
- YahooFinance.should_receive(:get_HistoricalQuotes_days).with(index_ticker, num_days).and_return(mock_index_quotes)
- YahooFinance.should_receive(:get_HistoricalQuotes_days).with(company_ticker, num_days).and_return(mock_company_quotes)
-
- common_dates = mock_index_quotes.map{ |x| x.date } & mock_company_quotes.map{ |x| x.date }
-
- index_quotes = mock_index_quotes .select{ |x| common_dates.include?(x.date) }.sort{ |x,y| x.date <=> y.date }
- company_quotes = mock_company_quotes.select{ |x| common_dates.include?(x.date) }.sort{ |x,y| x.date <=> y.date }
-
- index_daily_change = index_quotes.each_cons(2).map { |pair| (pair[1].adjClose-pair[0].adjClose)/pair[0].adjClose }
- company_daily_change = company_quotes.each_cons(2).map{ |pair| (pair[1].adjClose-pair[0].adjClose)/pair[0].adjClose }
-
- x = GSL::Vector.alloc(index_daily_change)
- y = GSL::Vector.alloc(company_daily_change)
- intercept, slope = GSL::Fit::linear(x, y)
- expected_beta = slope
-
- FinModeling::CAPM::Beta.from_ticker(company_ticker, num_days).should be_within(0.1).of(expected_beta) # FIXME: this is now ignorant of dividends...
+ context "when num_days >= 31" do
+ it "returns the right value" do
+ index_ticker = "SPY"
+ mock_index_quotes = []
+ company_ticker = "AAPL"
+ mock_company_quotes = []
+
+ num_days = 90 # FIXME: must be greater than 30?
+ 0.upto(num_days-1) do |day|
+ date_str = (DateTime.new(2013, 2, 5) + day).strftime("%Y-%m-%d")
+ mock_index_quotes << YahooFinance::HistoricalQuote.new(index_ticker, [date_str,153.6,154.7,153.6,154.2,121431900, 54.29 + day + rand])
+ mock_company_quotes << YahooFinance::HistoricalQuote.new(company_ticker, [date_str,153.6,154.7,153.6,154.2,121431900,154.29 + day + rand])
+ end
+
+ YahooFinance.should_receive(:get_HistoricalQuotes_days).with(index_ticker, num_days).and_return(mock_index_quotes)
+ YahooFinance.should_receive(:get_HistoricalQuotes_days).with(company_ticker, num_days).and_return(mock_company_quotes)
+
+ common_dates = mock_index_quotes.map{ |x| x.date } & mock_company_quotes.map{ |x| x.date }
+
+ index_daily_quotes = mock_index_quotes .select{ |x| common_dates.include?(x.date) }.sort{ |x,y| x.date <=> y.date }
+ company_daily_quotes = mock_company_quotes.select{ |x| common_dates.include?(x.date) }.sort{ |x,y| x.date <=> y.date }
+
+ index_monthly_quotes = index_daily_quotes .group_by{ |x| x.date.gsub(/-[0-9][0-9]$/, "") }
+ .values
+ .map{ |x| x.sort{ |x,y| x.date <=> y.date }.first }
+ .sort{ |x,y| x.date <=> y.date }[1..-1]
+ company_monthly_quotes = company_daily_quotes.group_by{ |x| x.date.gsub(/-[0-9][0-9]$/, "") }
+ .values
+ .map{ |x| x.sort{ |x,y| x.date <=> y.date }.first }
+ .sort{ |x,y| x.date <=> y.date }[1..-1]
+
+ index_monthly_returns = index_monthly_quotes.each_cons(2).map { |pair| (pair[1].adjClose-pair[0].adjClose)/pair[0].adjClose }
+ company_monthly_returns = company_monthly_quotes.each_cons(2).map{ |pair| (pair[1].adjClose-pair[0].adjClose)/pair[0].adjClose }
+
+ x = GSL::Vector.alloc(index_monthly_returns)
+ y = GSL::Vector.alloc(company_monthly_returns)
+ intercept, slope = GSL::Fit::linear(x, y)
+ expected_beta = slope
+
+ FinModeling::CAPM::Beta.from_ticker(company_ticker, num_days).should be_within(0.1).of(expected_beta) # FIXME: this is now ignorant of dividends...
+ end
end
end
end

0 comments on commit 602a629

Please sign in to comment.
Something went wrong with that request. Please try again.