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,300 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example: Context-Aware Pattern Discovery with KBS
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates the full Pattern Context system:
|
|
7
|
+
# 1. Market regime detection (bull/bear/sideways)
|
|
8
|
+
# 2. Seasonal pattern analysis (monthly/quarterly)
|
|
9
|
+
# 3. Sector analysis with KBS blackboards
|
|
10
|
+
# 4. Context-aware pattern discovery
|
|
11
|
+
# 5. Walk-forward validation to prevent overfitting
|
|
12
|
+
#
|
|
13
|
+
# Key Insight: Patterns aren't universal. They work in specific contexts:
|
|
14
|
+
# - Market regimes (bull vs bear)
|
|
15
|
+
# - Seasons (Q4 holiday shopping)
|
|
16
|
+
# - Sectors (tech stocks move together)
|
|
17
|
+
#
|
|
18
|
+
# This system helps identify WHEN and WHERE patterns are valid.
|
|
19
|
+
|
|
20
|
+
require 'sqa'
|
|
21
|
+
|
|
22
|
+
SQA.init
|
|
23
|
+
|
|
24
|
+
puts "=" * 80
|
|
25
|
+
puts "CONTEXT-AWARE PATTERN DISCOVERY SYSTEM"
|
|
26
|
+
puts "=" * 80
|
|
27
|
+
puts
|
|
28
|
+
|
|
29
|
+
# Only run integration tests if explicitly requested
|
|
30
|
+
if ENV['RUN_INTEGRATION_TESTS']
|
|
31
|
+
|
|
32
|
+
# Example 1: Market Regime Detection
|
|
33
|
+
puts "\n" + "=" * 80
|
|
34
|
+
puts "Example 1: Market Regime Detection"
|
|
35
|
+
puts "=" * 80
|
|
36
|
+
puts
|
|
37
|
+
|
|
38
|
+
stock = SQA::Stock.new(ticker: 'AAPL')
|
|
39
|
+
puts "Loaded #{stock.df.size} days of AAPL history"
|
|
40
|
+
puts
|
|
41
|
+
|
|
42
|
+
# Detect current regime
|
|
43
|
+
regime = SQA::MarketRegime.detect(stock)
|
|
44
|
+
puts "Current Market Regime:"
|
|
45
|
+
puts " Type: #{regime[:type]}"
|
|
46
|
+
puts " Volatility: #{regime[:volatility]}"
|
|
47
|
+
puts " Strength: #{regime[:strength]}"
|
|
48
|
+
puts
|
|
49
|
+
|
|
50
|
+
# Analyze regime history
|
|
51
|
+
regime_history = SQA::MarketRegime.detect_history(stock)
|
|
52
|
+
puts "Regime History (#{regime_history.size} distinct periods):"
|
|
53
|
+
regime_history.last(5).each do |r|
|
|
54
|
+
puts " #{r[:type].to_s.upcase.ljust(10)} - #{r[:duration]} days"
|
|
55
|
+
end
|
|
56
|
+
puts
|
|
57
|
+
|
|
58
|
+
# Example 2: Seasonal Pattern Analysis
|
|
59
|
+
puts "\n" + "=" * 80
|
|
60
|
+
puts "Example 2: Seasonal Pattern Analysis"
|
|
61
|
+
puts "=" * 80
|
|
62
|
+
puts
|
|
63
|
+
|
|
64
|
+
seasonal = SQA::SeasonalAnalyzer.analyze(stock)
|
|
65
|
+
puts "Seasonal Performance:"
|
|
66
|
+
puts " Best performing months: #{seasonal[:best_months].map { |m| Date::MONTHNAMES[m] }.join(', ')}"
|
|
67
|
+
puts " Worst performing months: #{seasonal[:worst_months].map { |m| Date::MONTHNAMES[m] }.join(', ')}"
|
|
68
|
+
puts " Best quarters: Q#{seasonal[:best_quarters].join(', Q')}"
|
|
69
|
+
puts " Has seasonal pattern: #{seasonal[:has_seasonal_pattern] ? 'YES' : 'NO'}"
|
|
70
|
+
puts
|
|
71
|
+
|
|
72
|
+
# Show monthly returns
|
|
73
|
+
puts "Monthly Average Returns:"
|
|
74
|
+
seasonal[:monthly_returns].sort_by { |m, _| m }.each do |month, stats|
|
|
75
|
+
month_name = Date::MONTHNAMES[month].ljust(10)
|
|
76
|
+
avg_return = stats[:avg_return].round(2)
|
|
77
|
+
sign = avg_return >= 0 ? '+' : ''
|
|
78
|
+
puts " #{month_name}: #{sign}#{avg_return}% (#{stats[:count]} samples)"
|
|
79
|
+
end
|
|
80
|
+
puts
|
|
81
|
+
|
|
82
|
+
# Example 3: Sector Analysis with KBS Blackboards
|
|
83
|
+
puts "\n" + "=" * 80
|
|
84
|
+
puts "Example 3: Sector Analysis with KBS Blackboards"
|
|
85
|
+
puts "=" * 80
|
|
86
|
+
puts
|
|
87
|
+
|
|
88
|
+
analyzer = SQA::SectorAnalyzer.new
|
|
89
|
+
|
|
90
|
+
# Add technology stocks
|
|
91
|
+
tech_stocks = ['AAPL', 'MSFT', 'GOOGL'].map { |t| SQA::Stock.new(ticker: t) }
|
|
92
|
+
tech_stocks.each { |s| analyzer.add_stock(s, sector: :technology) }
|
|
93
|
+
|
|
94
|
+
puts "Analyzing Technology sector (#{tech_stocks.size} stocks)"
|
|
95
|
+
puts
|
|
96
|
+
|
|
97
|
+
# Detect sector regime
|
|
98
|
+
sector_regime = analyzer.detect_sector_regime(:technology, tech_stocks)
|
|
99
|
+
puts "Technology Sector Regime:"
|
|
100
|
+
puts " Consensus: #{sector_regime[:consensus_regime]}"
|
|
101
|
+
puts " Sector strength: #{sector_regime[:sector_strength]}% bullish"
|
|
102
|
+
puts
|
|
103
|
+
|
|
104
|
+
# Discover sector-wide patterns
|
|
105
|
+
puts "Discovering sector-wide patterns..."
|
|
106
|
+
sector_patterns = analyzer.discover_sector_patterns(
|
|
107
|
+
:technology,
|
|
108
|
+
tech_stocks,
|
|
109
|
+
min_gain_percent: 8.0,
|
|
110
|
+
fpop: 10,
|
|
111
|
+
max_fpl_risk: 20.0
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
if sector_patterns.any?
|
|
115
|
+
puts "\nSector-Wide Patterns Found: #{sector_patterns.size}"
|
|
116
|
+
sector_patterns.first(3).each_with_index do |sp, i|
|
|
117
|
+
puts "\n Pattern #{i + 1}:"
|
|
118
|
+
puts " Stocks: #{sp[:stocks].join(', ')}"
|
|
119
|
+
puts " Avg Frequency: #{sp[:avg_frequency].round(1)}"
|
|
120
|
+
puts " Avg Gain: #{sp[:avg_gain].round(2)}%"
|
|
121
|
+
puts " Conditions: #{sp[:conditions]}"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
puts
|
|
125
|
+
|
|
126
|
+
# Print sector summary
|
|
127
|
+
analyzer.print_sector_summary(:technology)
|
|
128
|
+
|
|
129
|
+
# Example 4: Context-Aware Pattern Discovery
|
|
130
|
+
puts "\n" + "=" * 80
|
|
131
|
+
puts "Example 4: Context-Aware Pattern Discovery"
|
|
132
|
+
puts "=" * 80
|
|
133
|
+
puts
|
|
134
|
+
|
|
135
|
+
generator = SQA::StrategyGenerator.new(
|
|
136
|
+
stock: stock,
|
|
137
|
+
min_gain_percent: 10.0,
|
|
138
|
+
fpop: 10,
|
|
139
|
+
max_fpl_risk: 20.0,
|
|
140
|
+
required_fpl_directions: [:UP]
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Discover patterns with full context
|
|
144
|
+
context_patterns = generator.discover_context_aware_patterns(
|
|
145
|
+
analyze_regime: true,
|
|
146
|
+
analyze_seasonal: true,
|
|
147
|
+
sector: :technology
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
puts "\nDiscovered Context-Aware Patterns:"
|
|
151
|
+
context_patterns.first(3).each_with_index do |pattern, i|
|
|
152
|
+
puts "\n Pattern #{i + 1}:"
|
|
153
|
+
puts " Frequency: #{pattern.frequency}"
|
|
154
|
+
puts " Avg Gain: #{pattern.avg_gain.round(2)}%"
|
|
155
|
+
puts " Context: #{pattern.context.summary}"
|
|
156
|
+
puts " Market Regime: #{pattern.context.market_regime}"
|
|
157
|
+
puts " Valid Months: #{pattern.context.valid_months.map { |m| Date::MONTHNAMES[m] }.join(', ')}" if pattern.context.valid_months.any?
|
|
158
|
+
puts " Sector: #{pattern.context.sector}"
|
|
159
|
+
puts " Conditions: #{pattern.conditions}"
|
|
160
|
+
end
|
|
161
|
+
puts
|
|
162
|
+
|
|
163
|
+
# Example 5: Walk-Forward Validation
|
|
164
|
+
puts "\n" + "=" * 80
|
|
165
|
+
puts "Example 5: Walk-Forward Validation (Prevent Overfitting)"
|
|
166
|
+
puts "=" * 80
|
|
167
|
+
puts
|
|
168
|
+
|
|
169
|
+
puts "Running time-series cross-validation..."
|
|
170
|
+
puts "(This validates patterns on out-of-sample data)"
|
|
171
|
+
puts
|
|
172
|
+
|
|
173
|
+
validation_results = generator.walk_forward_validate(
|
|
174
|
+
train_size: 250, # 1 year training
|
|
175
|
+
test_size: 60, # ~3 months testing
|
|
176
|
+
step_size: 30 # Step forward 1 month each iteration
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
validated_patterns = validation_results[:validated_patterns]
|
|
180
|
+
all_results = validation_results[:validation_results]
|
|
181
|
+
|
|
182
|
+
puts "\nValidation Summary:"
|
|
183
|
+
puts " Total patterns tested: #{all_results.size}"
|
|
184
|
+
puts " Patterns validated (positive return, Sharpe > 0.5): #{validated_patterns.size}"
|
|
185
|
+
puts
|
|
186
|
+
|
|
187
|
+
if all_results.any?
|
|
188
|
+
# Calculate statistics
|
|
189
|
+
returns = all_results.map { |r| r[:test_return] }
|
|
190
|
+
sharpes = all_results.map { |r| r[:test_sharpe] }.compact
|
|
191
|
+
drawdowns = all_results.map { |r| r[:test_max_drawdown] }.compact
|
|
192
|
+
|
|
193
|
+
avg_return = returns.sum / returns.size
|
|
194
|
+
avg_sharpe = sharpes.any? ? sharpes.sum / sharpes.size : 0
|
|
195
|
+
avg_drawdown = drawdowns.any? ? drawdowns.sum / drawdowns.size : 0
|
|
196
|
+
|
|
197
|
+
puts "Out-of-Sample Performance:"
|
|
198
|
+
puts " Avg Return: #{avg_return.round(2)}%"
|
|
199
|
+
puts " Avg Sharpe: #{avg_sharpe.round(2)}"
|
|
200
|
+
puts " Avg Max Drawdown: #{avg_drawdown.round(2)}%"
|
|
201
|
+
puts
|
|
202
|
+
|
|
203
|
+
# Show best validated patterns
|
|
204
|
+
if validated_patterns.any?
|
|
205
|
+
puts "Best Validated Patterns (worked out-of-sample):"
|
|
206
|
+
validated_patterns.uniq.first(3).each_with_index do |pattern, i|
|
|
207
|
+
puts "\n Pattern #{i + 1}:"
|
|
208
|
+
puts " Frequency: #{pattern.frequency}"
|
|
209
|
+
puts " Avg Gain (in-sample): #{pattern.avg_gain.round(2)}%"
|
|
210
|
+
puts " Conditions: #{pattern.conditions}"
|
|
211
|
+
end
|
|
212
|
+
else
|
|
213
|
+
puts "⚠️ No patterns passed validation!"
|
|
214
|
+
puts "This suggests discovered patterns may be overfit to historical data."
|
|
215
|
+
puts "Try:"
|
|
216
|
+
puts " - Adjusting thresholds"
|
|
217
|
+
puts " - Increasing min_pattern_frequency"
|
|
218
|
+
puts " - Using context filters (regime, seasonal)"
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
puts
|
|
222
|
+
|
|
223
|
+
# Example 6: Pattern Validity Checking
|
|
224
|
+
puts "\n" + "=" * 80
|
|
225
|
+
puts "Example 6: Runtime Pattern Validity Checking"
|
|
226
|
+
puts "=" * 80
|
|
227
|
+
puts
|
|
228
|
+
|
|
229
|
+
if context_patterns.any?
|
|
230
|
+
pattern = context_patterns.first
|
|
231
|
+
|
|
232
|
+
puts "Checking if pattern is valid for different scenarios:"
|
|
233
|
+
puts
|
|
234
|
+
|
|
235
|
+
# Scenario 1: Current date in valid month
|
|
236
|
+
test_date = Date.new(2024, 12, 15) # December
|
|
237
|
+
valid = pattern.context.valid_for?(
|
|
238
|
+
date: test_date,
|
|
239
|
+
regime: :bull,
|
|
240
|
+
sector: :technology
|
|
241
|
+
)
|
|
242
|
+
puts " December 15, 2024 (Bull market, Tech sector): #{valid ? '✓ VALID' : '✗ INVALID'}"
|
|
243
|
+
|
|
244
|
+
# Scenario 2: Wrong month
|
|
245
|
+
test_date = Date.new(2024, 6, 15) # June
|
|
246
|
+
valid = pattern.context.valid_for?(
|
|
247
|
+
date: test_date,
|
|
248
|
+
regime: :bull,
|
|
249
|
+
sector: :technology
|
|
250
|
+
)
|
|
251
|
+
puts " June 15, 2024 (Bull market, Tech sector): #{valid ? '✓ VALID' : '✗ INVALID'}"
|
|
252
|
+
|
|
253
|
+
# Scenario 3: Wrong regime
|
|
254
|
+
valid = pattern.context.valid_for?(
|
|
255
|
+
date: Date.new(2024, 12, 15),
|
|
256
|
+
regime: :bear,
|
|
257
|
+
sector: :technology
|
|
258
|
+
)
|
|
259
|
+
puts " December 15, 2024 (Bear market, Tech sector): #{valid ? '✓ VALID' : '✗ INVALID'}"
|
|
260
|
+
|
|
261
|
+
# Scenario 4: Wrong sector
|
|
262
|
+
valid = pattern.context.valid_for?(
|
|
263
|
+
date: Date.new(2024, 12, 15),
|
|
264
|
+
regime: :bull,
|
|
265
|
+
sector: :finance
|
|
266
|
+
)
|
|
267
|
+
puts " December 15, 2024 (Bull market, Finance sector): #{valid ? '✓ VALID' : '✗ INVALID'}"
|
|
268
|
+
end
|
|
269
|
+
puts
|
|
270
|
+
|
|
271
|
+
puts "=" * 80
|
|
272
|
+
puts "COMPLETE PATTERN CONTEXT SYSTEM DEMONSTRATION FINISHED"
|
|
273
|
+
puts "=" * 80
|
|
274
|
+
puts
|
|
275
|
+
|
|
276
|
+
puts "Key Takeaways:"
|
|
277
|
+
puts " ✓ Patterns have context (regime, season, sector)"
|
|
278
|
+
puts " ✓ Market regimes change (bull/bear/sideways)"
|
|
279
|
+
puts " ✓ Seasonal patterns exist (best months/quarters)"
|
|
280
|
+
puts " ✓ Sector analysis finds patterns across related stocks"
|
|
281
|
+
puts " ✓ Walk-forward validation prevents overfitting"
|
|
282
|
+
puts " ✓ Runtime validation checks if pattern applies NOW"
|
|
283
|
+
puts
|
|
284
|
+
puts "This context-aware approach is much more sophisticated than"
|
|
285
|
+
puts "naive pattern mining that assumes universal applicability."
|
|
286
|
+
puts
|
|
287
|
+
|
|
288
|
+
else
|
|
289
|
+
puts "Skipping integration tests (set RUN_INTEGRATION_TESTS=1 to enable)"
|
|
290
|
+
puts
|
|
291
|
+
puts "This example demonstrates:"
|
|
292
|
+
puts " 1. Market Regime Detection - Identify bull/bear/sideways markets"
|
|
293
|
+
puts " 2. Seasonal Analysis - Find which months/quarters patterns work"
|
|
294
|
+
puts " 3. Sector Analysis - Discover patterns across related stocks using KBS"
|
|
295
|
+
puts " 4. Context-Aware Discovery - Patterns with regime/seasonal metadata"
|
|
296
|
+
puts " 5. Walk-Forward Validation - Prevent overfitting with time-series CV"
|
|
297
|
+
puts " 6. Runtime Validity - Check if pattern applies in current conditions"
|
|
298
|
+
puts
|
|
299
|
+
puts "Run with: RUN_INTEGRATION_TESTS=1 ruby #{__FILE__}"
|
|
300
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
ruby '>= 3.2.0'
|
|
6
|
+
|
|
7
|
+
# Rails framework
|
|
8
|
+
gem 'rails', '~> 7.1'
|
|
9
|
+
|
|
10
|
+
# Database - use SQLite for simplicity
|
|
11
|
+
gem 'sqlite3', '~> 1.7'
|
|
12
|
+
|
|
13
|
+
# Web server
|
|
14
|
+
gem 'puma', '~> 6.0'
|
|
15
|
+
|
|
16
|
+
# Asset pipeline
|
|
17
|
+
gem 'sprockets-rails'
|
|
18
|
+
gem 'importmap-rails'
|
|
19
|
+
gem 'turbo-rails'
|
|
20
|
+
gem 'stimulus-rails'
|
|
21
|
+
|
|
22
|
+
# JSON
|
|
23
|
+
gem 'jbuilder'
|
|
24
|
+
|
|
25
|
+
# SQA library (load from parent directory)
|
|
26
|
+
# In production: gem 'sqa'
|
|
27
|
+
|
|
28
|
+
group :development, :test do
|
|
29
|
+
gem 'debug', platforms: %i[mri windows]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
group :development do
|
|
33
|
+
gem 'web-console'
|
|
34
|
+
end
|