sqa_demo-sinatra 0.1.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +141 -0
- data/README.md +18 -0
- data/lib/sqa_demo/sinatra/app.rb +18 -549
- data/lib/sqa_demo/sinatra/helpers/api_helpers.rb +221 -0
- data/lib/sqa_demo/sinatra/helpers/filters.rb +50 -0
- data/lib/sqa_demo/sinatra/helpers/formatting.rb +63 -0
- data/lib/sqa_demo/sinatra/helpers/stock_loader.rb +215 -0
- data/lib/sqa_demo/sinatra/public/css/style.css +290 -13
- data/lib/sqa_demo/sinatra/public/js/app.js +55 -8
- data/lib/sqa_demo/sinatra/routes/api.rb +235 -0
- data/lib/sqa_demo/sinatra/routes/pages.rb +137 -0
- data/lib/sqa_demo/sinatra/version.rb +1 -1
- data/lib/sqa_demo/sinatra/views/analyze.erb +83 -8
- data/lib/sqa_demo/sinatra/views/company.erb +682 -0
- data/lib/sqa_demo/sinatra/views/compare.erb +500 -0
- data/lib/sqa_demo/sinatra/views/dashboard.erb +11 -1
- data/lib/sqa_demo/sinatra/views/index.erb +2 -1
- data/lib/sqa_demo/sinatra/views/layout.erb +106 -14
- metadata +10 -1
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
<div class="company-details">
|
|
2
|
+
<div class="company-header">
|
|
3
|
+
<div class="company-title">
|
|
4
|
+
<h1>
|
|
5
|
+
<i class="fas fa-building"></i>
|
|
6
|
+
<%= @company_name || @ticker %>
|
|
7
|
+
</h1>
|
|
8
|
+
<div class="company-meta">
|
|
9
|
+
<span class="ticker-badge"><%= @ticker %></span>
|
|
10
|
+
<% if @exchange %>
|
|
11
|
+
<span class="exchange-badge"><i class="fas fa-exchange-alt"></i> <%= @exchange %></span>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% if @overview[:asset_type] %>
|
|
14
|
+
<span class="type-badge"><%= @overview[:asset_type] %></span>
|
|
15
|
+
<% end %>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="header-actions">
|
|
19
|
+
<button onclick="location.href='/dashboard/<%= @ticker %>'" class="btn btn-primary">
|
|
20
|
+
<i class="fas fa-chart-line"></i> View Dashboard
|
|
21
|
+
</button>
|
|
22
|
+
<button onclick="location.href='/analyze/<%= @ticker %>'" class="btn btn-secondary">
|
|
23
|
+
<i class="fas fa-analytics"></i> Analysis
|
|
24
|
+
</button>
|
|
25
|
+
<button onclick="location.href='/backtest/<%= @ticker %>'" class="btn btn-secondary">
|
|
26
|
+
<i class="fas fa-history"></i> Backtest
|
|
27
|
+
</button>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<% if @overview[:description] %>
|
|
32
|
+
<!-- Company Description -->
|
|
33
|
+
<div class="description-section">
|
|
34
|
+
<div class="section-header">
|
|
35
|
+
<h2><i class="fas fa-align-left"></i> About</h2>
|
|
36
|
+
</div>
|
|
37
|
+
<p class="description-text"><%= @overview[:description] %></p>
|
|
38
|
+
</div>
|
|
39
|
+
<% end %>
|
|
40
|
+
|
|
41
|
+
<!-- Company Overview -->
|
|
42
|
+
<div class="section-header">
|
|
43
|
+
<h2><i class="fas fa-info-circle"></i> Company Overview</h2>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="info-grid">
|
|
46
|
+
<div class="info-card">
|
|
47
|
+
<div class="info-label">Ticker Symbol</div>
|
|
48
|
+
<div class="info-value"><%= @ticker %></div>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="info-card">
|
|
51
|
+
<div class="info-label">Company Name</div>
|
|
52
|
+
<div class="info-value"><%= @company_name || 'N/A' %></div>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="info-card">
|
|
55
|
+
<div class="info-label">Exchange</div>
|
|
56
|
+
<div class="info-value"><%= @exchange || 'N/A' %></div>
|
|
57
|
+
</div>
|
|
58
|
+
<% if @overview[:sector] %>
|
|
59
|
+
<div class="info-card">
|
|
60
|
+
<div class="info-label">Sector</div>
|
|
61
|
+
<div class="info-value"><%= @overview[:sector] %></div>
|
|
62
|
+
</div>
|
|
63
|
+
<% end %>
|
|
64
|
+
<% if @overview[:industry] %>
|
|
65
|
+
<div class="info-card">
|
|
66
|
+
<div class="info-label">Industry</div>
|
|
67
|
+
<div class="info-value"><%= @overview[:industry] %></div>
|
|
68
|
+
</div>
|
|
69
|
+
<% end %>
|
|
70
|
+
<% if @overview[:country] %>
|
|
71
|
+
<div class="info-card">
|
|
72
|
+
<div class="info-label">Country</div>
|
|
73
|
+
<div class="info-value"><%= @overview[:country] %></div>
|
|
74
|
+
</div>
|
|
75
|
+
<% end %>
|
|
76
|
+
<% if @overview[:address] %>
|
|
77
|
+
<div class="info-card wide">
|
|
78
|
+
<div class="info-label">Address</div>
|
|
79
|
+
<div class="info-value small"><%= @overview[:address] %></div>
|
|
80
|
+
</div>
|
|
81
|
+
<% end %>
|
|
82
|
+
<% if @overview[:fiscal_year_end] %>
|
|
83
|
+
<div class="info-card">
|
|
84
|
+
<div class="info-label">Fiscal Year End</div>
|
|
85
|
+
<div class="info-value"><%= @overview[:fiscal_year_end] %></div>
|
|
86
|
+
</div>
|
|
87
|
+
<% end %>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<!-- Price & Valuation -->
|
|
91
|
+
<div class="section-header">
|
|
92
|
+
<h2><i class="fas fa-dollar-sign"></i> Price & Valuation</h2>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="info-grid">
|
|
95
|
+
<div class="info-card highlight">
|
|
96
|
+
<div class="info-label">Current Price</div>
|
|
97
|
+
<div class="info-value price">$<%= sprintf('%.2f', @current_price) %></div>
|
|
98
|
+
</div>
|
|
99
|
+
<% if @overview[:"52_week_high"] %>
|
|
100
|
+
<div class="info-card">
|
|
101
|
+
<div class="info-label">52-Week High</div>
|
|
102
|
+
<div class="info-value positive">$<%= sprintf('%.2f', @overview[:"52_week_high"]) %></div>
|
|
103
|
+
</div>
|
|
104
|
+
<% end %>
|
|
105
|
+
<% if @overview[:"52_week_low"] %>
|
|
106
|
+
<div class="info-card">
|
|
107
|
+
<div class="info-label">52-Week Low</div>
|
|
108
|
+
<div class="info-value negative">$<%= sprintf('%.2f', @overview[:"52_week_low"]) %></div>
|
|
109
|
+
</div>
|
|
110
|
+
<% end %>
|
|
111
|
+
<% if @overview[:"50_day_moving_average"] %>
|
|
112
|
+
<div class="info-card">
|
|
113
|
+
<div class="info-label">50-Day MA</div>
|
|
114
|
+
<div class="info-value">$<%= sprintf('%.2f', @overview[:"50_day_moving_average"]) %></div>
|
|
115
|
+
</div>
|
|
116
|
+
<% end %>
|
|
117
|
+
<% if @overview[:"200_day_moving_average"] %>
|
|
118
|
+
<div class="info-card">
|
|
119
|
+
<div class="info-label">200-Day MA</div>
|
|
120
|
+
<div class="info-value">$<%= sprintf('%.2f', @overview[:"200_day_moving_average"]) %></div>
|
|
121
|
+
</div>
|
|
122
|
+
<% end %>
|
|
123
|
+
<% if @overview[:market_capitalization] %>
|
|
124
|
+
<div class="info-card">
|
|
125
|
+
<div class="info-label">Market Cap</div>
|
|
126
|
+
<div class="info-value">$<%= format_number((@overview[:market_capitalization] / 1_000_000_000).round(2)) %>B</div>
|
|
127
|
+
</div>
|
|
128
|
+
<% end %>
|
|
129
|
+
<% if @overview[:pe_ratio] %>
|
|
130
|
+
<div class="info-card">
|
|
131
|
+
<div class="info-label">P/E Ratio</div>
|
|
132
|
+
<div class="info-value"><%= sprintf('%.2f', @overview[:pe_ratio]) %></div>
|
|
133
|
+
</div>
|
|
134
|
+
<% end %>
|
|
135
|
+
<% if @overview[:forward_pe] %>
|
|
136
|
+
<div class="info-card">
|
|
137
|
+
<div class="info-label">Forward P/E</div>
|
|
138
|
+
<div class="info-value"><%= sprintf('%.2f', @overview[:forward_pe]) %></div>
|
|
139
|
+
</div>
|
|
140
|
+
<% end %>
|
|
141
|
+
<% if @overview[:peg_ratio] %>
|
|
142
|
+
<div class="info-card">
|
|
143
|
+
<div class="info-label">PEG Ratio</div>
|
|
144
|
+
<div class="info-value"><%= sprintf('%.2f', @overview[:peg_ratio]) %></div>
|
|
145
|
+
</div>
|
|
146
|
+
<% end %>
|
|
147
|
+
<% if @overview[:price_to_book_ratio] %>
|
|
148
|
+
<div class="info-card">
|
|
149
|
+
<div class="info-label">Price/Book</div>
|
|
150
|
+
<div class="info-value"><%= sprintf('%.2f', @overview[:price_to_book_ratio]) %></div>
|
|
151
|
+
</div>
|
|
152
|
+
<% end %>
|
|
153
|
+
<% if @overview[:price_to_sales_ratio_ttm] %>
|
|
154
|
+
<div class="info-card">
|
|
155
|
+
<div class="info-label">Price/Sales</div>
|
|
156
|
+
<div class="info-value"><%= sprintf('%.2f', @overview[:price_to_sales_ratio_ttm]) %></div>
|
|
157
|
+
</div>
|
|
158
|
+
<% end %>
|
|
159
|
+
<% if @overview[:ev_to_ebitda] %>
|
|
160
|
+
<div class="info-card">
|
|
161
|
+
<div class="info-label">EV/EBITDA</div>
|
|
162
|
+
<div class="info-value"><%= sprintf('%.2f', @overview[:ev_to_ebitda]) %></div>
|
|
163
|
+
</div>
|
|
164
|
+
<% end %>
|
|
165
|
+
<% if @overview[:beta] %>
|
|
166
|
+
<div class="info-card">
|
|
167
|
+
<div class="info-label">Beta</div>
|
|
168
|
+
<div class="info-value"><%= sprintf('%.3f', @overview[:beta]) %></div>
|
|
169
|
+
</div>
|
|
170
|
+
<% end %>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<!-- Dividends -->
|
|
174
|
+
<% if @overview[:dividend_per_share] || @overview[:dividend_yield] %>
|
|
175
|
+
<div class="section-header">
|
|
176
|
+
<h2><i class="fas fa-hand-holding-usd"></i> Dividends</h2>
|
|
177
|
+
</div>
|
|
178
|
+
<div class="info-grid">
|
|
179
|
+
<% if @overview[:dividend_per_share] %>
|
|
180
|
+
<div class="info-card">
|
|
181
|
+
<div class="info-label">Dividend/Share</div>
|
|
182
|
+
<div class="info-value">$<%= sprintf('%.2f', @overview[:dividend_per_share]) %></div>
|
|
183
|
+
</div>
|
|
184
|
+
<% end %>
|
|
185
|
+
<% if @overview[:dividend_yield] %>
|
|
186
|
+
<div class="info-card">
|
|
187
|
+
<div class="info-label">Dividend Yield</div>
|
|
188
|
+
<div class="info-value positive"><%= sprintf('%.2f', @overview[:dividend_yield] * 100) %>%</div>
|
|
189
|
+
</div>
|
|
190
|
+
<% end %>
|
|
191
|
+
<% if @overview[:ex_dividend_date] %>
|
|
192
|
+
<div class="info-card">
|
|
193
|
+
<div class="info-label">Ex-Dividend Date</div>
|
|
194
|
+
<div class="info-value"><%= @overview[:ex_dividend_date] %></div>
|
|
195
|
+
</div>
|
|
196
|
+
<% end %>
|
|
197
|
+
<% if @overview[:dividend_date] %>
|
|
198
|
+
<div class="info-card">
|
|
199
|
+
<div class="info-label">Dividend Date</div>
|
|
200
|
+
<div class="info-value"><%= @overview[:dividend_date] %></div>
|
|
201
|
+
</div>
|
|
202
|
+
<% end %>
|
|
203
|
+
</div>
|
|
204
|
+
<% end %>
|
|
205
|
+
|
|
206
|
+
<!-- Financials -->
|
|
207
|
+
<div class="section-header">
|
|
208
|
+
<h2><i class="fas fa-chart-pie"></i> Financial Metrics</h2>
|
|
209
|
+
</div>
|
|
210
|
+
<div class="info-grid">
|
|
211
|
+
<% if @overview[:eps] %>
|
|
212
|
+
<div class="info-card">
|
|
213
|
+
<div class="info-label">EPS</div>
|
|
214
|
+
<div class="info-value">$<%= sprintf('%.2f', @overview[:eps]) %></div>
|
|
215
|
+
</div>
|
|
216
|
+
<% end %>
|
|
217
|
+
<% if @overview[:book_value] %>
|
|
218
|
+
<div class="info-card">
|
|
219
|
+
<div class="info-label">Book Value</div>
|
|
220
|
+
<div class="info-value">$<%= sprintf('%.2f', @overview[:book_value]) %></div>
|
|
221
|
+
</div>
|
|
222
|
+
<% end %>
|
|
223
|
+
<% if @overview[:revenue_ttm] %>
|
|
224
|
+
<div class="info-card">
|
|
225
|
+
<div class="info-label">Revenue (TTM)</div>
|
|
226
|
+
<div class="info-value">$<%= format_number((@overview[:revenue_ttm] / 1_000_000_000).round(2)) %>B</div>
|
|
227
|
+
</div>
|
|
228
|
+
<% end %>
|
|
229
|
+
<% if @overview[:gross_profit_ttm] %>
|
|
230
|
+
<div class="info-card">
|
|
231
|
+
<div class="info-label">Gross Profit (TTM)</div>
|
|
232
|
+
<div class="info-value">$<%= format_number((@overview[:gross_profit_ttm] / 1_000_000_000).round(2)) %>B</div>
|
|
233
|
+
</div>
|
|
234
|
+
<% end %>
|
|
235
|
+
<% if @overview[:ebitda] %>
|
|
236
|
+
<div class="info-card">
|
|
237
|
+
<div class="info-label">EBITDA</div>
|
|
238
|
+
<div class="info-value">$<%= format_number((@overview[:ebitda] / 1_000_000_000).round(2)) %>B</div>
|
|
239
|
+
</div>
|
|
240
|
+
<% end %>
|
|
241
|
+
<% if @overview[:profit_margin] %>
|
|
242
|
+
<div class="info-card">
|
|
243
|
+
<div class="info-label">Profit Margin</div>
|
|
244
|
+
<div class="info-value <%= @overview[:profit_margin] >= 0 ? 'positive' : 'negative' %>"><%= sprintf('%.1f', @overview[:profit_margin] * 100) %>%</div>
|
|
245
|
+
</div>
|
|
246
|
+
<% end %>
|
|
247
|
+
<% if @overview[:operating_margin_ttm] %>
|
|
248
|
+
<div class="info-card">
|
|
249
|
+
<div class="info-label">Operating Margin</div>
|
|
250
|
+
<div class="info-value"><%= sprintf('%.1f', @overview[:operating_margin_ttm] * 100) %>%</div>
|
|
251
|
+
</div>
|
|
252
|
+
<% end %>
|
|
253
|
+
<% if @overview[:return_on_equity_ttm] %>
|
|
254
|
+
<div class="info-card">
|
|
255
|
+
<div class="info-label">Return on Equity</div>
|
|
256
|
+
<div class="info-value"><%= sprintf('%.1f', @overview[:return_on_equity_ttm] * 100) %>%</div>
|
|
257
|
+
</div>
|
|
258
|
+
<% end %>
|
|
259
|
+
<% if @overview[:return_on_assets_ttm] %>
|
|
260
|
+
<div class="info-card">
|
|
261
|
+
<div class="info-label">Return on Assets</div>
|
|
262
|
+
<div class="info-value"><%= sprintf('%.1f', @overview[:return_on_assets_ttm] * 100) %>%</div>
|
|
263
|
+
</div>
|
|
264
|
+
<% end %>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<!-- Growth -->
|
|
268
|
+
<% if @overview[:quarterly_earnings_growth_yoy] || @overview[:quarterly_revenue_growth_yoy] %>
|
|
269
|
+
<div class="section-header">
|
|
270
|
+
<h2><i class="fas fa-chart-line"></i> Growth</h2>
|
|
271
|
+
</div>
|
|
272
|
+
<div class="info-grid">
|
|
273
|
+
<% if @overview[:quarterly_earnings_growth_yoy] %>
|
|
274
|
+
<div class="info-card">
|
|
275
|
+
<div class="info-label">Quarterly Earnings Growth (YoY)</div>
|
|
276
|
+
<div class="info-value <%= @overview[:quarterly_earnings_growth_yoy] >= 0 ? 'positive' : 'negative' %>">
|
|
277
|
+
<%= @overview[:quarterly_earnings_growth_yoy] >= 0 ? '+' : '' %><%= sprintf('%.1f', @overview[:quarterly_earnings_growth_yoy] * 100) %>%
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
<% end %>
|
|
281
|
+
<% if @overview[:quarterly_revenue_growth_yoy] %>
|
|
282
|
+
<div class="info-card">
|
|
283
|
+
<div class="info-label">Quarterly Revenue Growth (YoY)</div>
|
|
284
|
+
<div class="info-value <%= @overview[:quarterly_revenue_growth_yoy] >= 0 ? 'positive' : 'negative' %>">
|
|
285
|
+
<%= @overview[:quarterly_revenue_growth_yoy] >= 0 ? '+' : '' %><%= sprintf('%.1f', @overview[:quarterly_revenue_growth_yoy] * 100) %>%
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
<% end %>
|
|
289
|
+
<% if @ytd_return %>
|
|
290
|
+
<div class="info-card">
|
|
291
|
+
<div class="info-label">YTD Return</div>
|
|
292
|
+
<div class="info-value <%= @ytd_return >= 0 ? 'positive' : 'negative' %>">
|
|
293
|
+
<%= @ytd_return >= 0 ? '+' : '' %><%= @ytd_return %>%
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
<% end %>
|
|
297
|
+
</div>
|
|
298
|
+
<% end %>
|
|
299
|
+
|
|
300
|
+
<!-- Analyst Ratings -->
|
|
301
|
+
<% if @overview[:analyst_target_price] || @overview[:analyst_rating_buy] %>
|
|
302
|
+
<div class="section-header">
|
|
303
|
+
<h2><i class="fas fa-users"></i> Analyst Ratings</h2>
|
|
304
|
+
</div>
|
|
305
|
+
<div class="info-grid">
|
|
306
|
+
<% if @overview[:analyst_target_price] %>
|
|
307
|
+
<div class="info-card highlight">
|
|
308
|
+
<div class="info-label">Target Price</div>
|
|
309
|
+
<div class="info-value price">$<%= sprintf('%.2f', @overview[:analyst_target_price]) %></div>
|
|
310
|
+
<% upside = ((@overview[:analyst_target_price] - @current_price) / @current_price * 100).round(1) %>
|
|
311
|
+
<div class="info-subtext <%= upside >= 0 ? 'positive' : 'negative' %>">
|
|
312
|
+
<%= upside >= 0 ? '+' : '' %><%= upside %>% upside
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
<% end %>
|
|
316
|
+
<% if @overview[:analyst_rating_strong_buy] %>
|
|
317
|
+
<div class="info-card">
|
|
318
|
+
<div class="info-label">Strong Buy</div>
|
|
319
|
+
<div class="info-value positive"><%= @overview[:analyst_rating_strong_buy].to_i %></div>
|
|
320
|
+
</div>
|
|
321
|
+
<% end %>
|
|
322
|
+
<% if @overview[:analyst_rating_buy] %>
|
|
323
|
+
<div class="info-card">
|
|
324
|
+
<div class="info-label">Buy</div>
|
|
325
|
+
<div class="info-value positive"><%= @overview[:analyst_rating_buy].to_i %></div>
|
|
326
|
+
</div>
|
|
327
|
+
<% end %>
|
|
328
|
+
<% if @overview[:analyst_rating_hold] %>
|
|
329
|
+
<div class="info-card">
|
|
330
|
+
<div class="info-label">Hold</div>
|
|
331
|
+
<div class="info-value"><%= @overview[:analyst_rating_hold].to_i %></div>
|
|
332
|
+
</div>
|
|
333
|
+
<% end %>
|
|
334
|
+
<% if @overview[:analyst_rating_sell] %>
|
|
335
|
+
<div class="info-card">
|
|
336
|
+
<div class="info-label">Sell</div>
|
|
337
|
+
<div class="info-value negative"><%= @overview[:analyst_rating_sell].to_i %></div>
|
|
338
|
+
</div>
|
|
339
|
+
<% end %>
|
|
340
|
+
<% if @overview[:analyst_rating_strong_sell] %>
|
|
341
|
+
<div class="info-card">
|
|
342
|
+
<div class="info-label">Strong Sell</div>
|
|
343
|
+
<div class="info-value negative"><%= @overview[:analyst_rating_strong_sell].to_i %></div>
|
|
344
|
+
</div>
|
|
345
|
+
<% end %>
|
|
346
|
+
</div>
|
|
347
|
+
<% end %>
|
|
348
|
+
|
|
349
|
+
<!-- Shares -->
|
|
350
|
+
<% if @overview[:shares_outstanding] || @overview[:shares_float] %>
|
|
351
|
+
<div class="section-header">
|
|
352
|
+
<h2><i class="fas fa-layer-group"></i> Share Information</h2>
|
|
353
|
+
</div>
|
|
354
|
+
<div class="info-grid">
|
|
355
|
+
<% if @overview[:shares_outstanding] %>
|
|
356
|
+
<div class="info-card">
|
|
357
|
+
<div class="info-label">Shares Outstanding</div>
|
|
358
|
+
<div class="info-value"><%= format_number((@overview[:shares_outstanding] / 1_000_000).round(1)) %>M</div>
|
|
359
|
+
</div>
|
|
360
|
+
<% end %>
|
|
361
|
+
<% if @overview[:shares_float] %>
|
|
362
|
+
<div class="info-card">
|
|
363
|
+
<div class="info-label">Float</div>
|
|
364
|
+
<div class="info-value"><%= format_number((@overview[:shares_float] / 1_000_000).round(1)) %>M</div>
|
|
365
|
+
</div>
|
|
366
|
+
<% end %>
|
|
367
|
+
<% if @overview[:percent_insiders] %>
|
|
368
|
+
<div class="info-card">
|
|
369
|
+
<div class="info-label">Insider Ownership</div>
|
|
370
|
+
<div class="info-value"><%= sprintf('%.2f', @overview[:percent_insiders]) %>%</div>
|
|
371
|
+
</div>
|
|
372
|
+
<% end %>
|
|
373
|
+
<% if @overview[:percent_institutions] %>
|
|
374
|
+
<div class="info-card">
|
|
375
|
+
<div class="info-label">Institutional Ownership</div>
|
|
376
|
+
<div class="info-value"><%= sprintf('%.2f', @overview[:percent_institutions]) %>%</div>
|
|
377
|
+
</div>
|
|
378
|
+
<% end %>
|
|
379
|
+
</div>
|
|
380
|
+
<% end %>
|
|
381
|
+
|
|
382
|
+
<!-- Historical Data Summary -->
|
|
383
|
+
<div class="section-header">
|
|
384
|
+
<h2><i class="fas fa-database"></i> Historical Data Summary</h2>
|
|
385
|
+
</div>
|
|
386
|
+
<div class="info-grid">
|
|
387
|
+
<div class="info-card">
|
|
388
|
+
<div class="info-label">Data Range Start</div>
|
|
389
|
+
<div class="info-value"><%= @data_start_date %></div>
|
|
390
|
+
</div>
|
|
391
|
+
<div class="info-card">
|
|
392
|
+
<div class="info-label">Data Range End</div>
|
|
393
|
+
<div class="info-value"><%= @data_end_date %></div>
|
|
394
|
+
</div>
|
|
395
|
+
<div class="info-card">
|
|
396
|
+
<div class="info-label">Total Trading Days</div>
|
|
397
|
+
<div class="info-value"><%= format_number(@total_trading_days) %></div>
|
|
398
|
+
</div>
|
|
399
|
+
<div class="info-card">
|
|
400
|
+
<div class="info-label">All-Time High (Data)</div>
|
|
401
|
+
<div class="info-value positive">$<%= sprintf('%.2f', @all_time_high) %></div>
|
|
402
|
+
</div>
|
|
403
|
+
<div class="info-card">
|
|
404
|
+
<div class="info-label">All-Time Low (Data)</div>
|
|
405
|
+
<div class="info-value negative">$<%= sprintf('%.2f', @all_time_low) %></div>
|
|
406
|
+
</div>
|
|
407
|
+
<div class="info-card">
|
|
408
|
+
<div class="info-label">Average Daily Volume</div>
|
|
409
|
+
<div class="info-value"><%= format_number(@avg_volume) %></div>
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
|
|
413
|
+
<!-- Navigation -->
|
|
414
|
+
<div class="navigation-section">
|
|
415
|
+
<h3>Quick Navigation</h3>
|
|
416
|
+
<div class="nav-buttons">
|
|
417
|
+
<a href="/dashboard/<%= @ticker %>" class="nav-card">
|
|
418
|
+
<i class="fas fa-chart-candlestick"></i>
|
|
419
|
+
<span>Dashboard</span>
|
|
420
|
+
<p>View real-time charts and indicators</p>
|
|
421
|
+
</a>
|
|
422
|
+
<a href="/analyze/<%= @ticker %>" class="nav-card">
|
|
423
|
+
<i class="fas fa-microscope"></i>
|
|
424
|
+
<span>Analysis</span>
|
|
425
|
+
<p>Technical and market analysis</p>
|
|
426
|
+
</a>
|
|
427
|
+
<a href="/backtest/<%= @ticker %>" class="nav-card">
|
|
428
|
+
<i class="fas fa-flask"></i>
|
|
429
|
+
<span>Backtest</span>
|
|
430
|
+
<p>Test trading strategies</p>
|
|
431
|
+
</a>
|
|
432
|
+
</div>
|
|
433
|
+
</div>
|
|
434
|
+
</div>
|
|
435
|
+
|
|
436
|
+
<style>
|
|
437
|
+
.company-details {
|
|
438
|
+
max-width: 1200px;
|
|
439
|
+
margin: 0 auto;
|
|
440
|
+
padding: 20px;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.company-header {
|
|
444
|
+
display: flex;
|
|
445
|
+
justify-content: space-between;
|
|
446
|
+
align-items: flex-start;
|
|
447
|
+
margin-bottom: 30px;
|
|
448
|
+
padding-bottom: 20px;
|
|
449
|
+
border-bottom: 1px solid #2a3154;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.company-title h1 {
|
|
453
|
+
color: #e8eaf6;
|
|
454
|
+
font-size: 2rem;
|
|
455
|
+
margin: 0 0 10px 0;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.company-title h1 i {
|
|
459
|
+
color: #00d4ff;
|
|
460
|
+
margin-right: 10px;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.company-meta {
|
|
464
|
+
display: flex;
|
|
465
|
+
gap: 10px;
|
|
466
|
+
flex-wrap: wrap;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.ticker-badge {
|
|
470
|
+
background: linear-gradient(135deg, #00d4ff, #00a0cc);
|
|
471
|
+
color: #fff;
|
|
472
|
+
padding: 6px 14px;
|
|
473
|
+
border-radius: 20px;
|
|
474
|
+
font-weight: bold;
|
|
475
|
+
font-size: 1rem;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.exchange-badge, .type-badge {
|
|
479
|
+
background: #2a3154;
|
|
480
|
+
color: #9fa8da;
|
|
481
|
+
padding: 6px 14px;
|
|
482
|
+
border-radius: 20px;
|
|
483
|
+
font-size: 0.9rem;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.exchange-badge i {
|
|
487
|
+
margin-right: 5px;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.description-section {
|
|
491
|
+
margin-bottom: 20px;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.description-text {
|
|
495
|
+
color: #b0b8d9;
|
|
496
|
+
line-height: 1.7;
|
|
497
|
+
font-size: 0.95rem;
|
|
498
|
+
background: #1a2248;
|
|
499
|
+
padding: 20px;
|
|
500
|
+
border-radius: 10px;
|
|
501
|
+
border: 1px solid #2a3154;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.section-header {
|
|
505
|
+
margin: 30px 0 15px 0;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.section-header h2 {
|
|
509
|
+
color: #e8eaf6;
|
|
510
|
+
font-size: 1.3rem;
|
|
511
|
+
margin: 0;
|
|
512
|
+
padding-bottom: 10px;
|
|
513
|
+
border-bottom: 2px solid #2a3154;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.section-header h2 i {
|
|
517
|
+
color: #00d4ff;
|
|
518
|
+
margin-right: 10px;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.info-grid {
|
|
522
|
+
display: grid;
|
|
523
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
524
|
+
gap: 15px;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.info-card {
|
|
528
|
+
background: #1a2248;
|
|
529
|
+
border-radius: 10px;
|
|
530
|
+
padding: 18px;
|
|
531
|
+
border: 1px solid #2a3154;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.info-card.highlight {
|
|
535
|
+
background: linear-gradient(135deg, #1a2248, #232b52);
|
|
536
|
+
border-color: #00d4ff;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.info-card.wide {
|
|
540
|
+
grid-column: span 2;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.info-label {
|
|
544
|
+
color: #9fa8da;
|
|
545
|
+
font-size: 0.8rem;
|
|
546
|
+
margin-bottom: 6px;
|
|
547
|
+
text-transform: uppercase;
|
|
548
|
+
letter-spacing: 0.5px;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.info-value {
|
|
552
|
+
color: #e8eaf6;
|
|
553
|
+
font-size: 1.2rem;
|
|
554
|
+
font-weight: 600;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.info-value.small {
|
|
558
|
+
font-size: 0.95rem;
|
|
559
|
+
font-weight: normal;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.info-value.price {
|
|
563
|
+
color: #00d4ff;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.info-value.positive {
|
|
567
|
+
color: #00ff88;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
.info-value.negative {
|
|
571
|
+
color: #ff3366;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.info-subtext {
|
|
575
|
+
font-size: 0.85rem;
|
|
576
|
+
margin-top: 5px;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.info-subtext.positive {
|
|
580
|
+
color: #00ff88;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.info-subtext.negative {
|
|
584
|
+
color: #ff3366;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.navigation-section {
|
|
588
|
+
margin-top: 40px;
|
|
589
|
+
padding-top: 30px;
|
|
590
|
+
border-top: 1px solid #2a3154;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.navigation-section h3 {
|
|
594
|
+
color: #e8eaf6;
|
|
595
|
+
margin-bottom: 20px;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.nav-buttons {
|
|
599
|
+
display: grid;
|
|
600
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
601
|
+
gap: 20px;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.nav-card {
|
|
605
|
+
background: #1a2248;
|
|
606
|
+
border-radius: 10px;
|
|
607
|
+
padding: 25px;
|
|
608
|
+
text-decoration: none;
|
|
609
|
+
border: 1px solid #2a3154;
|
|
610
|
+
transition: all 0.3s ease;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.nav-card:hover {
|
|
614
|
+
background: #232b52;
|
|
615
|
+
border-color: #00d4ff;
|
|
616
|
+
transform: translateY(-3px);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.nav-card i {
|
|
620
|
+
font-size: 2rem;
|
|
621
|
+
color: #00d4ff;
|
|
622
|
+
margin-bottom: 10px;
|
|
623
|
+
display: block;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.nav-card span {
|
|
627
|
+
color: #e8eaf6;
|
|
628
|
+
font-size: 1.2rem;
|
|
629
|
+
font-weight: 600;
|
|
630
|
+
display: block;
|
|
631
|
+
margin-bottom: 5px;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.nav-card p {
|
|
635
|
+
color: #9fa8da;
|
|
636
|
+
font-size: 0.9rem;
|
|
637
|
+
margin: 0;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.btn-link {
|
|
641
|
+
background: transparent;
|
|
642
|
+
color: #00d4ff;
|
|
643
|
+
border: 1px solid #00d4ff;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.btn-link:hover {
|
|
647
|
+
background: rgba(0, 212, 255, 0.1);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.company-name {
|
|
651
|
+
font-weight: normal;
|
|
652
|
+
font-size: 0.7em;
|
|
653
|
+
color: #9fa8da;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.company-link {
|
|
657
|
+
margin-top: 5px;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
@media (max-width: 768px) {
|
|
661
|
+
.company-header {
|
|
662
|
+
flex-direction: column;
|
|
663
|
+
gap: 20px;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.header-actions {
|
|
667
|
+
width: 100%;
|
|
668
|
+
display: flex;
|
|
669
|
+
flex-wrap: wrap;
|
|
670
|
+
gap: 10px;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.header-actions .btn {
|
|
674
|
+
flex: 1;
|
|
675
|
+
min-width: 120px;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.info-card.wide {
|
|
679
|
+
grid-column: span 1;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
</style>
|