Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions lib/yahoo_finance_client/stock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,34 @@ def parse_response(body, symbol)
end

def format_quote(quote)
{ symbol: quote["symbol"], price: quote["regularMarketPrice"], change: quote["regularMarketChange"],
percent_change: quote["regularMarketChangePercent"], volume: quote["regularMarketVolume"] }
price = quote["regularMarketPrice"]
dividend = quote["dividendRate"]
eps = quote["epsTrailingTwelveMonths"]

build_quote_hash(quote, price, dividend, eps)
end

def build_quote_hash(quote, price, dividend, eps)
{
symbol: quote["symbol"], name: quote["shortName"], price: price,
change: quote["regularMarketChange"], percent_change: quote["regularMarketChangePercent"],
volume: quote["regularMarketVolume"], pe_ratio: quote["trailingPE"], eps: eps,
dividend: dividend, dividend_yield: calculate_yield(dividend, price),
payout_ratio: calculate_payout(dividend, eps),
ma50: quote["fiftyDayAverage"], ma200: quote["twoHundredDayAverage"]
}
end

def calculate_yield(dividend, price)
return nil unless dividend && price&.positive?

(dividend / price * 100).round(2)
end

def calculate_payout(dividend, eps)
return nil unless dividend && eps&.positive?

(dividend / eps * 100).round(2)
end

def fetch_from_cache(key)
Expand Down
237 changes: 200 additions & 37 deletions spec/yahoo_finance_client/stock_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,42 +32,54 @@
"result" => [
{
"symbol" => "AAPL",
"shortName" => "Apple Inc.",
"regularMarketPrice" => 150.0,
"regularMarketChange" => 1.5,
"regularMarketChangePercent" => 1.0,
"regularMarketVolume" => 100_000
"regularMarketVolume" => 100_000,
"trailingPE" => 25.5,
"epsTrailingTwelveMonths" => 5.88,
"dividendRate" => 0.96,
"fiftyDayAverage" => 148.5,
"twoHundredDayAverage" => 145.0
}
]
}
}.to_json
end

let(:expected_quote) do
{
symbol: "AAPL",
name: "Apple Inc.",
price: 150.0,
change: 1.5,
percent_change: 1.0,
volume: 100_000,
pe_ratio: 25.5,
eps: 5.88,
dividend: 0.96,
dividend_yield: 0.64,
payout_ratio: 16.33,
ma50: 148.5,
ma200: 145.0
}
end

before do
stub_request(:get, quote_url)
.to_return(status: 200, body: response_body)
end

it "returns the quote data" do
result = described_class.get_quote(symbol)
expect(result).to eq(
symbol: "AAPL",
price: 150.0,
change: 1.5,
percent_change: 1.0,
volume: 100_000
)
expect(result).to eq(expected_quote)
end

it "caches the quote data" do
described_class.get_quote(symbol)
cache = described_class.instance_variable_get(:@cache)
expect(cache[cache_key][:data]).to eq(
symbol: "AAPL",
price: 150.0,
change: 1.5,
percent_change: 1.0,
volume: 100_000
)
expect(cache[cache_key][:data]).to eq(expected_quote)
end
end

Expand Down Expand Up @@ -107,10 +119,18 @@
let(:cached_data) do
{
symbol: "AAPL",
name: "Apple Inc.",
price: 150.0,
change: 1.5,
percent_change: 1.0,
volume: 100_000
volume: 100_000,
pe_ratio: 25.5,
eps: 5.88,
dividend: 0.96,
dividend_yield: 0.64,
payout_ratio: 16.33,
ma50: 148.5,
ma200: 145.0
}
end

Expand All @@ -133,10 +153,18 @@
let(:cached_data) do
{
symbol: "AAPL",
name: "Apple Inc.",
price: 150.0,
change: 1.5,
percent_change: 1.0,
volume: 100_000
volume: 100_000,
pe_ratio: 25.5,
eps: 5.88,
dividend: 0.96,
dividend_yield: 0.64,
payout_ratio: 16.33,
ma50: 148.5,
ma200: 145.0
}
end

Expand All @@ -146,16 +174,40 @@
"result" => [
{
"symbol" => "AAPL",
"regularMarketPrice" => 150.0,
"regularMarketChange" => 1.5,
"regularMarketChangePercent" => 1.0,
"regularMarketVolume" => 100_000
"shortName" => "Apple Inc.",
"regularMarketPrice" => 155.0,
"regularMarketChange" => 2.0,
"regularMarketChangePercent" => 1.3,
"regularMarketVolume" => 120_000,
"trailingPE" => 26.0,
"epsTrailingTwelveMonths" => 5.96,
"dividendRate" => 0.96,
"fiftyDayAverage" => 150.0,
"twoHundredDayAverage" => 147.0
}
]
}
}.to_json
end

let(:expected_new_quote) do
{
symbol: "AAPL",
name: "Apple Inc.",
price: 155.0,
change: 2.0,
percent_change: 1.3,
volume: 120_000,
pe_ratio: 26.0,
eps: 5.96,
dividend: 0.96,
dividend_yield: 0.62,
payout_ratio: 16.11,
ma50: 150.0,
ma200: 147.0
}
end

before do
described_class.instance_variable_set(
:@cache,
Expand All @@ -169,20 +221,103 @@

it "fetches new data and updates the cache" do
result = described_class.get_quote(symbol)
expect(result).to eq(expected_new_quote)
cache = described_class.instance_variable_get(:@cache)
expect(cache[cache_key][:data]).to eq(expected_new_quote)
end
end

context "when stock has no dividend data" do
let(:response_body) do
{
"quoteResponse" => {
"result" => [
{
"symbol" => "GOOG",
"shortName" => "Alphabet Inc.",
"regularMarketPrice" => 140.0,
"regularMarketChange" => -0.5,
"regularMarketChangePercent" => -0.36,
"regularMarketVolume" => 50_000,
"trailingPE" => 22.0,
"epsTrailingTwelveMonths" => 6.36,
"fiftyDayAverage" => 138.0,
"twoHundredDayAverage" => 135.0
}
]
}
}.to_json
end

before do
stub_request(:get, "#{base_url}/v7/finance/quote?symbols=GOOG&crumb=#{crumb}")
.to_return(status: 200, body: response_body)
end

it "returns nil for dividend-related fields" do
result = described_class.get_quote("GOOG")
expect(result).to eq(
symbol: "AAPL",
price: 150.0,
change: 1.5,
percent_change: 1.0,
volume: 100_000
symbol: "GOOG",
name: "Alphabet Inc.",
price: 140.0,
change: -0.5,
percent_change: -0.36,
volume: 50_000,
pe_ratio: 22.0,
eps: 6.36,
dividend: nil,
dividend_yield: nil,
payout_ratio: nil,
ma50: 138.0,
ma200: 135.0
)
cache = described_class.instance_variable_get(:@cache)
expect(cache[cache_key][:data]).to eq(
symbol: "AAPL",
price: 150.0,
change: 1.5,
percent_change: 1.0,
volume: 100_000
end
end

context "when stock has negative EPS" do
let(:response_body) do
{
"quoteResponse" => {
"result" => [
{
"symbol" => "TSLA",
"shortName" => "Tesla Inc.",
"regularMarketPrice" => 200.0,
"regularMarketChange" => 5.0,
"regularMarketChangePercent" => 2.56,
"regularMarketVolume" => 80_000,
"trailingPE" => nil,
"epsTrailingTwelveMonths" => -1.5,
"dividendRate" => 0.0,
"fiftyDayAverage" => 195.0,
"twoHundredDayAverage" => 180.0
}
]
}
}.to_json
end

before do
stub_request(:get, "#{base_url}/v7/finance/quote?symbols=TSLA&crumb=#{crumb}")
.to_return(status: 200, body: response_body)
end

it "returns nil for payout ratio when EPS is negative" do
result = described_class.get_quote("TSLA")
expect(result).to eq(
symbol: "TSLA",
name: "Tesla Inc.",
price: 200.0,
change: 5.0,
percent_change: 2.56,
volume: 80_000,
pe_ratio: nil,
eps: -1.5,
dividend: 0.0,
dividend_yield: 0.0,
payout_ratio: nil,
ma50: 195.0,
ma200: 180.0
)
end
end
Expand All @@ -194,10 +329,16 @@
"result" => [
{
"symbol" => "AAPL",
"shortName" => "Apple Inc.",
"regularMarketPrice" => 150.0,
"regularMarketChange" => 1.5,
"regularMarketChangePercent" => 1.0,
"regularMarketVolume" => 100_000
"regularMarketVolume" => 100_000,
"trailingPE" => 25.5,
"epsTrailingTwelveMonths" => 5.88,
"dividendRate" => 0.96,
"fiftyDayAverage" => 148.5,
"twoHundredDayAverage" => 145.0
}
]
}
Expand All @@ -217,10 +358,18 @@
result = described_class.get_quote(symbol)
expect(result).to eq(
symbol: "AAPL",
name: "Apple Inc.",
price: 150.0,
change: 1.5,
percent_change: 1.0,
volume: 100_000
volume: 100_000,
pe_ratio: 25.5,
eps: 5.88,
dividend: 0.96,
dividend_yield: 0.64,
payout_ratio: 16.33,
ma50: 148.5,
ma200: 145.0
)
end
end
Expand All @@ -244,10 +393,16 @@
"result" => [
{
"symbol" => "AAPL",
"shortName" => "Apple Inc.",
"regularMarketPrice" => 150.0,
"regularMarketChange" => 1.5,
"regularMarketChangePercent" => 1.0,
"regularMarketVolume" => 100_000
"regularMarketVolume" => 100_000,
"trailingPE" => 25.5,
"epsTrailingTwelveMonths" => 5.88,
"dividendRate" => 0.96,
"fiftyDayAverage" => 148.5,
"twoHundredDayAverage" => 145.0
}
]
}
Expand All @@ -268,10 +423,18 @@
result = described_class.get_quote(symbol)
expect(result).to eq(
symbol: "AAPL",
name: "Apple Inc.",
price: 150.0,
change: 1.5,
percent_change: 1.0,
volume: 100_000
volume: 100_000,
pe_ratio: 25.5,
eps: 5.88,
dividend: 0.96,
dividend_yield: 0.64,
payout_ratio: 16.33,
ma50: 148.5,
ma200: 145.0
)
end
end
Expand Down