Skip to content

Commit 432389c

Browse files
authored
Add 10Cap valuation (closes #88) (#90)
* Add 10Cap valuation (closes #88). * Delete the test that is not relevant anymore. I expect the breaking of this test to be fixed by PR #92. * Fix div id. * Make the test test the *API*, not NVDA:) Actually, the purpose of this commit is to bump the CI to run again, because it failed with an error that seems intermittent, and very rare. * Fix precision of 10Cap price. Before: ten_cap("NVDA") = 10.834 After: ten_cap("NVDA") = 10.83
1 parent 090c982 commit 432389c

File tree

10 files changed

+79
-19
lines changed

10 files changed

+79
-19
lines changed

isthisstockgood/DataFetcher.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def fetchDataForTickerSymbol(ticker):
3131
'debt_equity_ratio',
3232
'margin_of_safety_price',
3333
'current_price'
34+
'ten_cap_price'
3435
"""
3536
if not ticker:
3637
return None
@@ -56,7 +57,9 @@ def fetchDataForTickerSymbol(ticker):
5657
margin_of_safety_price, sticker_price = \
5758
_calculateMarginOfSafetyPrice(msn_money.equity_growth_rates[-1], msn_money.pe_low, msn_money.pe_high, msn_money.eps[-1], five_year_growth_rate)
5859
payback_time = _calculatePaybackTime(msn_money.equity_growth_rates[-1], msn_money.last_year_net_income, msn_money.market_cap, five_year_growth_rate)
59-
computed_free_cash_flow = round(float(msn_money.free_cash_flow[-1]) * msn_money.shares_outstanding)
60+
free_cash_flow_per_share = float(msn_money.free_cash_flow[-1])
61+
computed_free_cash_flow = round(free_cash_flow_per_share * msn_money.shares_outstanding)
62+
ten_cap_price = 10 * free_cash_flow_per_share
6063
template_values = {
6164
'ticker' : ticker,
6265
'name' : msn_money.name if msn_money and msn_money.name else 'null',
@@ -68,6 +71,7 @@ def fetchDataForTickerSymbol(ticker):
6871
'cash': msn_money.free_cash_flow_growth_rates if msn_money and msn_money.free_cash_flow_growth_rates else [],
6972
'total_debt' : msn_money.total_debt,
7073
'free_cash_flow' : computed_free_cash_flow,
74+
'ten_cap_price' : round(ten_cap_price, 2),
7175
'debt_payoff_time' : round(float(msn_money.total_debt) / computed_free_cash_flow),
7276
'debt_equity_ratio' : msn_money.debt_equity_ratio if msn_money and msn_money.debt_equity_ratio >= 0 else -1,
7377
'margin_of_safety_price' : margin_of_safety_price if margin_of_safety_price else 'null',

isthisstockgood/templates/home.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
{% include "management.html" %}
88
{% include "margin_of_safety.html" %}
99
{% include "payback_time.html" %}
10+
{% include "ten_cap.html" %}
1011
{% include "market_cap.html" %}
1112
<script>
1213
// Initialize all popover elements for the Bootstrap plugin.
1314
$(document).ready(function(){
1415
$('[data-toggle="popover"]').popover({ trigger: "hover" });
1516
});
1617
</script>
17-
{% endblock %}
18+
{% endblock %}

isthisstockgood/templates/js/search.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,22 @@ $(document).ready(function() {
122122
colorCellWithBackgroundColor('#' + key, Color.red());
123123
}
124124

125+
// Update 10 Cap section
126+
let ten_cap_key = 'ten_cap_price';
127+
let ten_cap_field_id = '#' + ten_cap_key;
128+
let current_price = data['current_price'];
129+
updateHtmlWithValueForKey(data, ten_cap_key, /*commas=*/true);
130+
if (!data[ten_cap_key]) {
131+
colorCellWithBackgroundColor(ten_cap_field_id, Color.red());
132+
}
133+
if (current_price > data[ten_cap_key]) {
134+
colorCellWithBackgroundColor(ten_cap_field_id, Color.red());
135+
}
136+
else {
137+
colorCellWithBackgroundColor(ten_cap_field_id, Color.green());
138+
}
139+
140+
125141
// Update Market Cap numbers
126142
updateHtmlWithValueForKey(data, 'average_volume', /*commas=*/true);
127143
let averageVolume = data['average_volume'];
@@ -212,4 +228,4 @@ function colorCellWithIDForZeroBasedRange(id, range) {
212228
}
213229

214230
colorCellWithBackgroundColor(id, backgroundColor);
215-
}
231+
}

isthisstockgood/templates/json/stock_data.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@
1515
"current_price" : {{ current_price }},
1616
"sticker_price" : {{ sticker_price }},
1717
"payback_time" : {{ payback_time }},
18+
"ten_cap_price" : {{ ten_cap_price }},
1819
"average_volume" : {{ average_volume }}
19-
}
20+
}

isthisstockgood/templates/loading.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090

9191
this.shadowRoot.append(style, wrapper);
9292

93-
this.animationTimeout = None;
93+
this.animationTimeout = null;
9494
}
9595

9696
static get observedAttributes() { return ['data-message']; }

isthisstockgood/templates/searchbox.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
<div class="col-md-3 nopadding">
55
<input
66
type="text"
7+
autofocus
78
class="form-control"
89
id="ticker"
910
placeholder="Ticker Symbol"
10-
maxlength=8
11-
onkeydown="return validateInput(event)"/>
11+
maxlength=8/>
1212
</div>
1313
<div class="col-md-1 nopadding">
1414
<input
@@ -32,4 +32,4 @@
3232

3333
<script>
3434
{% include "js/search.js" %}
35-
</script>
35+
</script>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<div class="container pb-2 pt-2" id="Ten Cap">
2+
<div class="row justify-content-center">
3+
<button
4+
type="button"
5+
class="btn btn-raised btn-lg btn-primary"
6+
data-toggle="popover"
7+
title="10 Cap"
8+
data-content="{% include 'txt/ten_cap_tooltip.txt' %}"
9+
>10 Cap</button>
10+
</div>
11+
12+
<div class="row pt-3 justify-content-center">
13+
<div class="col-sm-5">
14+
<table class="table table-bordered text-center">
15+
<tr id="debt_equity_title_label">
16+
<thead class="thead-light">
17+
<th>10 Cap Price</th>
18+
</thead>
19+
<td id="ten_cap_price">
20+
-
21+
</td>
22+
</tr>
23+
</table>
24+
</div>
25+
</div>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Price at which the company is expected to return to you about 10% of your invested amount via Free Cash Flow (Owner's Earnings).
2+
Discussed in the book 'Invested' by Danielle Town.

tests/test_DataSources.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,6 @@ def test_msn_money():
3232
assert data.last_year_net_income > 0.0
3333
assert data.total_debt >= 0.0
3434

35-
def test_yahoo():
36-
test_ticker = 'MSFT'
37-
test_name = 'Microsoft Corp'
38-
39-
data = get_yahoo_data(test_ticker)
40-
41-
assert data.ticker_symbol == test_ticker
42-
assert float(data.five_year_growth_rate) > 0.0
43-
4435
def get_msn_money_data(ticker):
4536
data_fetcher = DataFetcher()
4637
data_fetcher.ticker_symbol = ticker

tests/test_api.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,27 @@ def test_get_data():
1818

1919
with app.test_client() as test_client:
2020
test_client = app.test_client()
21+
2122
res = test_client.get('/api/ticker/nvda')
22-
data = res.text
23-
assert json.loads(data)['debt_payoff_time'] == 0
2423
assert res.status_code == 200
24+
25+
assert res.json['debt_payoff_time'] >= 0
26+
27+
def test_get_ten_cap_price():
28+
app = create_app(fetchDataForTickerSymbol)
29+
30+
with app.test_client() as test_client:
31+
test_client = app.test_client()
32+
res = test_client.get('/api/ticker/nvda')
33+
assert res.json['ten_cap_price'] > 0
34+
35+
def test_ten_cap_price_has_two_places_precision():
36+
app = create_app(fetchDataForTickerSymbol)
37+
38+
with app.test_client() as test_client:
39+
test_client = app.test_client()
40+
res = test_client.get('/api/ticker/nvda')
41+
42+
price = res.json['ten_cap_price']
43+
44+
assert round(price, 2) == price

0 commit comments

Comments
 (0)