sqa 0.0.24 → 0.0.31
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/.goose/memory/development.txt +3 -0
- data/.semver +6 -0
- data/ARCHITECTURE.md +648 -0
- data/CHANGELOG.md +82 -0
- data/CLAUDE.md +653 -0
- data/COMMITS.md +196 -0
- data/DATAFRAME_ARCHITECTURE_REVIEW.md +421 -0
- data/NEXT-STEPS.md +154 -0
- data/README.md +812 -262
- data/TASKS.md +358 -0
- data/TEST_RESULTS.md +140 -0
- data/TODO.md +42 -0
- data/_notes.txt +25 -0
- data/bin/sqa-console +11 -0
- data/data/talk_talk.json +103284 -0
- data/develop_summary.md +313 -0
- data/docs/advanced/backtesting.md +206 -0
- data/docs/advanced/ensemble.md +68 -0
- data/docs/advanced/fpop.md +153 -0
- data/docs/advanced/index.md +112 -0
- data/docs/advanced/multi-timeframe.md +67 -0
- data/docs/advanced/pattern-matcher.md +75 -0
- data/docs/advanced/portfolio-optimizer.md +79 -0
- data/docs/advanced/portfolio.md +166 -0
- data/docs/advanced/risk-management.md +210 -0
- data/docs/advanced/strategy-generator.md +158 -0
- data/docs/advanced/streaming.md +209 -0
- data/docs/ai_and_ml.md +80 -0
- data/docs/api/dataframe.md +1115 -0
- data/docs/api/index.md +126 -0
- data/docs/assets/css/custom.css +88 -0
- data/docs/assets/js/mathjax.js +18 -0
- data/docs/concepts/index.md +68 -0
- data/docs/contributing/index.md +60 -0
- data/docs/data-sources/index.md +66 -0
- data/docs/data_frame.md +317 -97
- data/docs/factors_that_impact_price.md +26 -0
- data/docs/finviz.md +11 -0
- data/docs/fx_pro_bit.md +25 -0
- data/docs/genetic_programming.md +104 -0
- data/docs/getting-started/index.md +123 -0
- data/docs/getting-started/installation.md +229 -0
- data/docs/getting-started/quick-start.md +244 -0
- data/docs/i_gotta_an_idea.md +22 -0
- data/docs/index.md +163 -0
- data/docs/indicators/index.md +97 -0
- data/docs/indicators.md +110 -24
- data/docs/options.md +8 -0
- data/docs/strategies/bollinger-bands.md +146 -0
- data/docs/strategies/consensus.md +64 -0
- data/docs/strategies/custom.md +310 -0
- data/docs/strategies/ema.md +53 -0
- data/docs/strategies/index.md +92 -0
- data/docs/strategies/kbs.md +164 -0
- data/docs/strategies/macd.md +96 -0
- data/docs/strategies/market-profile.md +54 -0
- data/docs/strategies/mean-reversion.md +58 -0
- data/docs/strategies/rsi.md +95 -0
- data/docs/strategies/sma.md +55 -0
- data/docs/strategies/stochastic.md +63 -0
- data/docs/strategies/volume-breakout.md +54 -0
- data/docs/tags.md +7 -0
- data/docs/true_strength_index.md +46 -0
- data/docs/weighted_moving_average.md +48 -0
- data/examples/README.md +354 -0
- data/examples/advanced_features_example.rb +350 -0
- data/examples/fpop_analysis_example.rb +191 -0
- data/examples/genetic_programming_example.rb +148 -0
- data/examples/kbs_strategy_example.rb +208 -0
- data/examples/pattern_context_example.rb +300 -0
- data/examples/rails_app/Gemfile +34 -0
- data/examples/rails_app/README.md +416 -0
- data/examples/rails_app/app/assets/javascripts/application.js +107 -0
- data/examples/rails_app/app/assets/stylesheets/application.css +659 -0
- data/examples/rails_app/app/controllers/analysis_controller.rb +11 -0
- data/examples/rails_app/app/controllers/api/v1/stocks_controller.rb +227 -0
- data/examples/rails_app/app/controllers/application_controller.rb +22 -0
- data/examples/rails_app/app/controllers/backtest_controller.rb +11 -0
- data/examples/rails_app/app/controllers/dashboard_controller.rb +21 -0
- data/examples/rails_app/app/controllers/portfolio_controller.rb +7 -0
- data/examples/rails_app/app/views/analysis/show.html.erb +209 -0
- data/examples/rails_app/app/views/backtest/show.html.erb +171 -0
- data/examples/rails_app/app/views/dashboard/index.html.erb +118 -0
- data/examples/rails_app/app/views/dashboard/show.html.erb +408 -0
- data/examples/rails_app/app/views/errors/show.html.erb +17 -0
- data/examples/rails_app/app/views/layouts/application.html.erb +60 -0
- data/examples/rails_app/app/views/portfolio/index.html.erb +33 -0
- data/examples/rails_app/bin/rails +6 -0
- data/examples/rails_app/config/application.rb +45 -0
- data/examples/rails_app/config/boot.rb +5 -0
- data/examples/rails_app/config/database.yml +18 -0
- data/examples/rails_app/config/environment.rb +11 -0
- data/examples/rails_app/config/routes.rb +26 -0
- data/examples/rails_app/config.ru +8 -0
- data/examples/realtime_stream_example.rb +274 -0
- data/examples/sinatra_app/Gemfile +22 -0
- data/examples/sinatra_app/QUICKSTART.md +159 -0
- data/examples/sinatra_app/README.md +461 -0
- data/examples/sinatra_app/app.rb +344 -0
- data/examples/sinatra_app/config.ru +5 -0
- data/examples/sinatra_app/public/css/style.css +659 -0
- data/examples/sinatra_app/public/js/app.js +107 -0
- data/examples/sinatra_app/views/analyze.erb +306 -0
- data/examples/sinatra_app/views/backtest.erb +325 -0
- data/examples/sinatra_app/views/dashboard.erb +419 -0
- data/examples/sinatra_app/views/error.erb +58 -0
- data/examples/sinatra_app/views/index.erb +118 -0
- data/examples/sinatra_app/views/layout.erb +61 -0
- data/examples/sinatra_app/views/portfolio.erb +43 -0
- data/examples/strategy_generator_example.rb +346 -0
- data/hsa_portfolio.csv +11 -0
- data/justfile +0 -0
- data/lib/api/alpha_vantage_api.rb +462 -0
- data/lib/sqa/backtest.rb +329 -0
- data/lib/sqa/data_frame/alpha_vantage.rb +43 -65
- data/lib/sqa/data_frame/data.rb +92 -0
- data/lib/sqa/data_frame/yahoo_finance.rb +35 -43
- data/lib/sqa/data_frame.rb +148 -243
- data/lib/sqa/ensemble.rb +359 -0
- data/lib/sqa/fpop.rb +199 -0
- data/lib/sqa/gp.rb +259 -0
- data/lib/sqa/indicator.rb +5 -8
- data/lib/sqa/init.rb +15 -8
- data/lib/sqa/market_regime.rb +240 -0
- data/lib/sqa/multi_timeframe.rb +379 -0
- data/lib/sqa/pattern_matcher.rb +497 -0
- data/lib/sqa/portfolio.rb +260 -6
- data/lib/sqa/portfolio_optimizer.rb +377 -0
- data/lib/sqa/risk_manager.rb +442 -0
- data/lib/sqa/seasonal_analyzer.rb +209 -0
- data/lib/sqa/sector_analyzer.rb +300 -0
- data/lib/sqa/stock.rb +67 -125
- data/lib/sqa/strategy/bollinger_bands.rb +42 -0
- data/lib/sqa/strategy/consensus.rb +5 -2
- data/lib/sqa/strategy/kbs_strategy.rb +470 -0
- data/lib/sqa/strategy/macd.rb +46 -0
- data/lib/sqa/strategy/mp.rb +1 -1
- data/lib/sqa/strategy/stochastic.rb +60 -0
- data/lib/sqa/strategy/volume_breakout.rb +57 -0
- data/lib/sqa/strategy.rb +5 -0
- data/lib/sqa/strategy_generator.rb +947 -0
- data/lib/sqa/stream.rb +361 -0
- data/lib/sqa/version.rb +1 -7
- data/lib/sqa.rb +23 -16
- data/main.just +81 -0
- data/mkdocs.yml +288 -0
- data/trace.log +0 -0
- metadata +261 -51
- data/bin/sqa +0 -6
- data/lib/patches/dry-cli.rb +0 -228
- data/lib/sqa/activity.rb +0 -10
- data/lib/sqa/cli.rb +0 -62
- data/lib/sqa/commands/analysis.rb +0 -309
- data/lib/sqa/commands/base.rb +0 -139
- data/lib/sqa/commands/web.rb +0 -199
- data/lib/sqa/commands.rb +0 -22
- data/lib/sqa/constants.rb +0 -23
- data/lib/sqa/indicator/average_true_range.rb +0 -33
- data/lib/sqa/indicator/bollinger_bands.rb +0 -28
- data/lib/sqa/indicator/candlestick_pattern_recognizer.rb +0 -60
- data/lib/sqa/indicator/donchian_channel.rb +0 -29
- data/lib/sqa/indicator/double_top_bottom_pattern.rb +0 -34
- data/lib/sqa/indicator/elliott_wave_theory.rb +0 -57
- data/lib/sqa/indicator/exponential_moving_average.rb +0 -25
- data/lib/sqa/indicator/exponential_moving_average_trend.rb +0 -36
- data/lib/sqa/indicator/fibonacci_retracement.rb +0 -23
- data/lib/sqa/indicator/head_and_shoulders_pattern.rb +0 -26
- data/lib/sqa/indicator/market_profile.rb +0 -32
- data/lib/sqa/indicator/mean_reversion.rb +0 -37
- data/lib/sqa/indicator/momentum.rb +0 -28
- data/lib/sqa/indicator/moving_average_convergence_divergence.rb +0 -29
- data/lib/sqa/indicator/peaks_and_valleys.rb +0 -29
- data/lib/sqa/indicator/predict_next_value.rb +0 -202
- data/lib/sqa/indicator/relative_strength_index.rb +0 -47
- data/lib/sqa/indicator/simple_moving_average.rb +0 -24
- data/lib/sqa/indicator/simple_moving_average_trend.rb +0 -32
- data/lib/sqa/indicator/stochastic_oscillator.rb +0 -68
- data/lib/sqa/indicator/true_range.rb +0 -39
- data/lib/sqa/trade.rb +0 -26
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Global functions for modal and navigation
|
|
2
|
+
|
|
3
|
+
function showTickerModal() {
|
|
4
|
+
document.getElementById('tickerModal').style.display = 'block';
|
|
5
|
+
document.getElementById('tickerInput').focus();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function closeTickerModal() {
|
|
9
|
+
document.getElementById('tickerModal').style.display = 'none';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function searchTicker(event) {
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
|
|
15
|
+
const input = event.target.querySelector('input[type="text"]');
|
|
16
|
+
const ticker = input.value.trim().toUpperCase();
|
|
17
|
+
|
|
18
|
+
if (!ticker) {
|
|
19
|
+
alert('Please enter a stock ticker symbol');
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Validate ticker format (letters and optional dot)
|
|
24
|
+
if (!/^[A-Z]{1,5}(\.[A-Z]{1,2})?$/.test(ticker)) {
|
|
25
|
+
alert('Please enter a valid ticker symbol (e.g., AAPL, BRK.A)');
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Navigate to dashboard
|
|
30
|
+
window.location.href = `/dashboard/${ticker}`;
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Close modal when clicking outside
|
|
35
|
+
window.onclick = function(event) {
|
|
36
|
+
const modal = document.getElementById('tickerModal');
|
|
37
|
+
if (event.target === modal) {
|
|
38
|
+
closeTickerModal();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Keyboard shortcuts
|
|
43
|
+
document.addEventListener('keydown', function(event) {
|
|
44
|
+
// Ctrl/Cmd + K to open search
|
|
45
|
+
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
showTickerModal();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Escape to close modal
|
|
51
|
+
if (event.key === 'Escape') {
|
|
52
|
+
closeTickerModal();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Utility functions
|
|
57
|
+
function formatCurrency(value) {
|
|
58
|
+
return new Intl.NumberFormat('en-US', {
|
|
59
|
+
style: 'currency',
|
|
60
|
+
currency: 'USD'
|
|
61
|
+
}).format(value);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function formatPercent(value) {
|
|
65
|
+
return `${value >= 0 ? '+' : ''}${value.toFixed(2)}%`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function formatNumber(value) {
|
|
69
|
+
return new Intl.NumberFormat('en-US').format(value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Show loading indicator
|
|
73
|
+
function showLoading(elementId) {
|
|
74
|
+
const element = document.getElementById(elementId);
|
|
75
|
+
if (element) {
|
|
76
|
+
element.innerHTML = '<p class="loading"><i class="fas fa-spinner fa-spin"></i> Loading...</p>';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Show error message
|
|
81
|
+
function showError(elementId, message) {
|
|
82
|
+
const element = document.getElementById(elementId);
|
|
83
|
+
if (element) {
|
|
84
|
+
element.innerHTML = `<p class="error"><i class="fas fa-exclamation-circle"></i> ${message}</p>`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Debounce function for performance
|
|
89
|
+
function debounce(func, wait) {
|
|
90
|
+
let timeout;
|
|
91
|
+
return function executedFunction(...args) {
|
|
92
|
+
const later = () => {
|
|
93
|
+
clearTimeout(timeout);
|
|
94
|
+
func(...args);
|
|
95
|
+
};
|
|
96
|
+
clearTimeout(timeout);
|
|
97
|
+
timeout = setTimeout(later, wait);
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Console welcome message
|
|
102
|
+
console.log('%cSQA Analytics', 'font-size: 24px; font-weight: bold; color: #2196F3;');
|
|
103
|
+
console.log('%cPowered by Ruby & Plotly.js', 'font-size: 14px; color: #666;');
|
|
104
|
+
console.log('');
|
|
105
|
+
console.log('Keyboard shortcuts:');
|
|
106
|
+
console.log(' Ctrl/Cmd + K: Open ticker search');
|
|
107
|
+
console.log(' Escape: Close modal');
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
<div class="dashboard">
|
|
2
|
+
<div class="dashboard-header">
|
|
3
|
+
<div class="ticker-info">
|
|
4
|
+
<h1><%= @ticker %> - Market Analysis</h1>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="header-actions">
|
|
7
|
+
<button onclick="location.href='/dashboard/<%= @ticker %>'" class="btn btn-secondary">
|
|
8
|
+
<i class="fas fa-chart-line"></i> Dashboard
|
|
9
|
+
</button>
|
|
10
|
+
<button onclick="location.href='/backtest/<%= @ticker %>'" class="btn btn-secondary">
|
|
11
|
+
<i class="fas fa-history"></i> Backtest
|
|
12
|
+
</button>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<!-- Market Regime Analysis -->
|
|
17
|
+
<div class="chart-container">
|
|
18
|
+
<div class="chart-header">
|
|
19
|
+
<h2><i class="fas fa-chart-area"></i> Market Regime Analysis</h2>
|
|
20
|
+
</div>
|
|
21
|
+
<div id="regimeAnalysis" class="analysis-content">
|
|
22
|
+
<p class="loading"><i class="fas fa-spinner fa-spin"></i> Loading market regime data...</p>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<!-- Seasonal Pattern Analysis -->
|
|
27
|
+
<div class="chart-container">
|
|
28
|
+
<div class="chart-header">
|
|
29
|
+
<h2><i class="fas fa-calendar-alt"></i> Seasonal Pattern Analysis</h2>
|
|
30
|
+
</div>
|
|
31
|
+
<div id="seasonalAnalysis" class="analysis-content">
|
|
32
|
+
<p class="loading"><i class="fas fa-spinner fa-spin"></i> Loading seasonal data...</p>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<!-- FPOP Analysis -->
|
|
37
|
+
<div class="chart-container">
|
|
38
|
+
<div class="chart-header">
|
|
39
|
+
<h2><i class="fas fa-crystal-ball"></i> Future Period Analysis (FPOP)</h2>
|
|
40
|
+
<p class="chart-subtitle">Potential future price movements based on historical patterns</p>
|
|
41
|
+
</div>
|
|
42
|
+
<div id="fpopAnalysis" class="analysis-content">
|
|
43
|
+
<p class="loading"><i class="fas fa-spinner fa-spin"></i> Loading FPOP analysis...</p>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<!-- Risk Metrics -->
|
|
48
|
+
<div class="chart-container">
|
|
49
|
+
<div class="chart-header">
|
|
50
|
+
<h2><i class="fas fa-shield-alt"></i> Risk Metrics</h2>
|
|
51
|
+
</div>
|
|
52
|
+
<div id="riskMetrics" class="analysis-content">
|
|
53
|
+
<p class="loading"><i class="fas fa-spinner fa-spin"></i> Loading risk metrics...</p>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<style>
|
|
59
|
+
.analysis-content {
|
|
60
|
+
padding: 1rem 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.analysis-grid {
|
|
64
|
+
display: grid;
|
|
65
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
66
|
+
gap: 1.5rem;
|
|
67
|
+
margin-top: 1rem;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.analysis-card {
|
|
71
|
+
background: var(--light-bg);
|
|
72
|
+
padding: 1.5rem;
|
|
73
|
+
border-radius: 8px;
|
|
74
|
+
border-left: 4px solid var(--primary-color);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.analysis-card h3 {
|
|
78
|
+
font-size: 1.25rem;
|
|
79
|
+
margin-bottom: 1rem;
|
|
80
|
+
color: var(--text-primary);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.analysis-stat {
|
|
84
|
+
display: flex;
|
|
85
|
+
justify-content: space-between;
|
|
86
|
+
align-items: center;
|
|
87
|
+
padding: 0.5rem 0;
|
|
88
|
+
border-bottom: 1px solid var(--border-color);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.analysis-stat:last-child {
|
|
92
|
+
border-bottom: none;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.stat-label {
|
|
96
|
+
color: var(--text-secondary);
|
|
97
|
+
font-weight: 500;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.stat-value {
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
color: var(--text-primary);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.fpop-table {
|
|
106
|
+
width: 100%;
|
|
107
|
+
margin-top: 1rem;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.fpop-table th,
|
|
111
|
+
.fpop-table td {
|
|
112
|
+
padding: 0.75rem;
|
|
113
|
+
text-align: left;
|
|
114
|
+
border-bottom: 1px solid var(--border-color);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.fpop-table thead {
|
|
118
|
+
background: var(--light-bg);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.chart-subtitle {
|
|
122
|
+
color: var(--text-secondary);
|
|
123
|
+
font-size: 0.875rem;
|
|
124
|
+
font-weight: normal;
|
|
125
|
+
}
|
|
126
|
+
</style>
|
|
127
|
+
|
|
128
|
+
<script>
|
|
129
|
+
const ticker = '<%= @ticker %>';
|
|
130
|
+
|
|
131
|
+
document.addEventListener('DOMContentLoaded', async function() {
|
|
132
|
+
await loadAnalysisData();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
async function loadAnalysisData() {
|
|
136
|
+
try {
|
|
137
|
+
const response = await fetch(`/api/analyze/${ticker}`);
|
|
138
|
+
const data = await response.json();
|
|
139
|
+
|
|
140
|
+
renderRegimeAnalysis(data.regime);
|
|
141
|
+
renderSeasonalAnalysis(data.seasonal);
|
|
142
|
+
renderFPOPAnalysis(data.fpop);
|
|
143
|
+
renderRiskMetrics(data.risk);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error('Error loading analysis data:', error);
|
|
146
|
+
document.querySelectorAll('.analysis-content').forEach(el => {
|
|
147
|
+
el.innerHTML = '<p class="error">Failed to load analysis data. Please try again.</p>';
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function renderRegimeAnalysis(regime) {
|
|
153
|
+
const regimeType = regime.type.toUpperCase();
|
|
154
|
+
const regimeClass = regimeType === 'BULL' ? 'signal-buy' :
|
|
155
|
+
regimeType === 'BEAR' ? 'signal-sell' : 'signal-neutral';
|
|
156
|
+
|
|
157
|
+
const html = `
|
|
158
|
+
<div class="analysis-grid">
|
|
159
|
+
<div class="analysis-card">
|
|
160
|
+
<h3>Current Market Regime</h3>
|
|
161
|
+
<div class="analysis-stat">
|
|
162
|
+
<span class="stat-label">Type</span>
|
|
163
|
+
<span class="stat-value ${regimeClass}">${regimeType}</span>
|
|
164
|
+
</div>
|
|
165
|
+
<div class="analysis-stat">
|
|
166
|
+
<span class="stat-label">Volatility</span>
|
|
167
|
+
<span class="stat-value">${regime.volatility.toUpperCase()}</span>
|
|
168
|
+
</div>
|
|
169
|
+
<div class="analysis-stat">
|
|
170
|
+
<span class="stat-label">Strength</span>
|
|
171
|
+
<span class="stat-value">${regime.strength.toFixed(2)}</span>
|
|
172
|
+
</div>
|
|
173
|
+
<div class="analysis-stat">
|
|
174
|
+
<span class="stat-label">Trend</span>
|
|
175
|
+
<span class="stat-value">${regime.trend.toFixed(2)}%</span>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
<div class="analysis-card">
|
|
179
|
+
<h3>Interpretation</h3>
|
|
180
|
+
<p style="line-height: 1.8; color: var(--text-secondary);">
|
|
181
|
+
The stock is currently in a <strong class="${regimeClass}">${regimeType}</strong> market regime
|
|
182
|
+
with <strong>${regime.volatility}</strong> volatility. The regime strength of
|
|
183
|
+
<strong>${regime.strength.toFixed(2)}</strong> indicates how pronounced this regime is.
|
|
184
|
+
${regime.type === 'bull' ? 'This is generally favorable for long positions.' :
|
|
185
|
+
regime.type === 'bear' ? 'Consider defensive strategies or short positions.' :
|
|
186
|
+
'The market is consolidating. Wait for clearer signals.'}
|
|
187
|
+
</p>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
`;
|
|
191
|
+
|
|
192
|
+
document.getElementById('regimeAnalysis').innerHTML = html;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function renderSeasonalAnalysis(seasonal) {
|
|
196
|
+
const monthNames = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
197
|
+
const bestMonths = seasonal.best_months.map(m => monthNames[m]).join(', ');
|
|
198
|
+
const worstMonths = seasonal.worst_months.map(m => monthNames[m]).join(', ');
|
|
199
|
+
|
|
200
|
+
const html = `
|
|
201
|
+
<div class="analysis-grid">
|
|
202
|
+
<div class="analysis-card">
|
|
203
|
+
<h3>Monthly Patterns</h3>
|
|
204
|
+
<div class="analysis-stat">
|
|
205
|
+
<span class="stat-label">Best Months</span>
|
|
206
|
+
<span class="stat-value signal-buy">${bestMonths}</span>
|
|
207
|
+
</div>
|
|
208
|
+
<div class="analysis-stat">
|
|
209
|
+
<span class="stat-label">Worst Months</span>
|
|
210
|
+
<span class="stat-value signal-sell">${worstMonths}</span>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
<div class="analysis-card">
|
|
214
|
+
<h3>Quarterly Patterns</h3>
|
|
215
|
+
<div class="analysis-stat">
|
|
216
|
+
<span class="stat-label">Best Quarters</span>
|
|
217
|
+
<span class="stat-value signal-buy">Q${seasonal.best_quarters.join(', Q')}</span>
|
|
218
|
+
</div>
|
|
219
|
+
<div class="analysis-stat">
|
|
220
|
+
<span class="stat-label">Has Seasonal Pattern</span>
|
|
221
|
+
<span class="stat-value">${seasonal.has_pattern ? 'Yes' : 'No'}</span>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
<p style="margin-top: 1.5rem; color: var(--text-secondary); font-style: italic;">
|
|
226
|
+
${seasonal.has_pattern ?
|
|
227
|
+
'This stock shows significant seasonal patterns. Consider timing trades around favorable periods.' :
|
|
228
|
+
'No strong seasonal patterns detected. Seasonal effects may not be a major factor for this stock.'}
|
|
229
|
+
</p>
|
|
230
|
+
`;
|
|
231
|
+
|
|
232
|
+
document.getElementById('seasonalAnalysis').innerHTML = html;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function renderFPOPAnalysis(fpop) {
|
|
236
|
+
if (!fpop || fpop.length === 0) {
|
|
237
|
+
document.getElementById('fpopAnalysis').innerHTML =
|
|
238
|
+
'<p class="hint">No FPOP data available.</p>';
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let tableHTML = `
|
|
243
|
+
<table class="fpop-table">
|
|
244
|
+
<thead>
|
|
245
|
+
<tr>
|
|
246
|
+
<th>Direction</th>
|
|
247
|
+
<th>Magnitude</th>
|
|
248
|
+
<th>Risk</th>
|
|
249
|
+
<th>Interpretation</th>
|
|
250
|
+
</tr>
|
|
251
|
+
</thead>
|
|
252
|
+
<tbody>
|
|
253
|
+
`;
|
|
254
|
+
|
|
255
|
+
fpop.forEach(f => {
|
|
256
|
+
const dirClass = f.direction === 'UP' ? 'signal-buy' :
|
|
257
|
+
f.direction === 'DOWN' ? 'signal-sell' : 'signal-neutral';
|
|
258
|
+
|
|
259
|
+
tableHTML += `
|
|
260
|
+
<tr>
|
|
261
|
+
<td><span class="${dirClass}">${f.direction}</span></td>
|
|
262
|
+
<td>${f.magnitude.toFixed(2)}%</td>
|
|
263
|
+
<td>${f.risk.toFixed(2)}%</td>
|
|
264
|
+
<td>${f.interpretation}</td>
|
|
265
|
+
</tr>
|
|
266
|
+
`;
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
tableHTML += '</tbody></table>';
|
|
270
|
+
|
|
271
|
+
document.getElementById('fpopAnalysis').innerHTML = tableHTML;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function renderRiskMetrics(risk) {
|
|
275
|
+
const html = `
|
|
276
|
+
<div class="analysis-grid">
|
|
277
|
+
<div class="analysis-card">
|
|
278
|
+
<h3>Value at Risk (VaR)</h3>
|
|
279
|
+
<div class="analysis-stat">
|
|
280
|
+
<span class="stat-label">95% Confidence</span>
|
|
281
|
+
<span class="stat-value signal-sell">${(risk.var_95 * 100).toFixed(2)}%</span>
|
|
282
|
+
</div>
|
|
283
|
+
<p style="margin-top: 1rem; color: var(--text-secondary); font-size: 0.875rem;">
|
|
284
|
+
There's a 95% chance your loss will not exceed this amount on any given day.
|
|
285
|
+
</p>
|
|
286
|
+
</div>
|
|
287
|
+
<div class="analysis-card">
|
|
288
|
+
<h3>Performance Metrics</h3>
|
|
289
|
+
<div class="analysis-stat">
|
|
290
|
+
<span class="stat-label">Sharpe Ratio</span>
|
|
291
|
+
<span class="stat-value">${risk.sharpe_ratio.toFixed(2)}</span>
|
|
292
|
+
</div>
|
|
293
|
+
<div class="analysis-stat">
|
|
294
|
+
<span class="stat-label">Max Drawdown</span>
|
|
295
|
+
<span class="stat-value signal-sell">${(risk.max_drawdown * 100).toFixed(2)}%</span>
|
|
296
|
+
</div>
|
|
297
|
+
<p style="margin-top: 1rem; color: var(--text-secondary); font-size: 0.875rem;">
|
|
298
|
+
Sharpe ratio > 1 is good, > 2 is excellent. Lower drawdown indicates less risk.
|
|
299
|
+
</p>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
`;
|
|
303
|
+
|
|
304
|
+
document.getElementById('riskMetrics').innerHTML = html;
|
|
305
|
+
}
|
|
306
|
+
</script>
|