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,43 @@
|
|
|
1
|
+
<div class="dashboard">
|
|
2
|
+
<div class="dashboard-header">
|
|
3
|
+
<div class="ticker-info">
|
|
4
|
+
<h1><i class="fas fa-briefcase"></i> Portfolio Optimization</h1>
|
|
5
|
+
<p style="color: var(--text-secondary); margin-top: 0.5rem;">
|
|
6
|
+
Optimize allocation across multiple stocks for maximum risk-adjusted returns
|
|
7
|
+
</p>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div class="chart-container">
|
|
12
|
+
<div class="chart-header">
|
|
13
|
+
<h2><i class="fas fa-layer-group"></i> Coming Soon</h2>
|
|
14
|
+
</div>
|
|
15
|
+
<div style="padding: 3rem 2rem; text-align: center;">
|
|
16
|
+
<div style="font-size: 5rem; color: var(--primary-color); margin-bottom: 2rem;">
|
|
17
|
+
<i class="fas fa-tools"></i>
|
|
18
|
+
</div>
|
|
19
|
+
<h3 style="font-size: 2rem; margin-bottom: 1rem; color: var(--text-primary);">
|
|
20
|
+
Portfolio Optimization Feature
|
|
21
|
+
</h3>
|
|
22
|
+
<p style="font-size: 1.1rem; color: var(--text-secondary); max-width: 600px; margin: 0 auto 2rem; line-height: 1.8;">
|
|
23
|
+
This feature will allow you to optimize portfolio allocation across multiple stocks
|
|
24
|
+
using various methods including Maximum Sharpe ratio, Minimum Variance, and Risk Parity.
|
|
25
|
+
</p>
|
|
26
|
+
<div style="background: var(--light-bg); padding: 2rem; border-radius: 12px; max-width: 800px; margin: 0 auto;">
|
|
27
|
+
<h4 style="margin-bottom: 1rem; color: var(--text-primary);">Planned Features:</h4>
|
|
28
|
+
<ul style="text-align: left; max-width: 500px; margin: 0 auto; line-height: 2;">
|
|
29
|
+
<li><i class="fas fa-check-circle" style="color: var(--success-color);"></i> Maximum Sharpe Ratio Optimization</li>
|
|
30
|
+
<li><i class="fas fa-check-circle" style="color: var(--success-color);"></i> Minimum Variance Portfolio</li>
|
|
31
|
+
<li><i class="fas fa-check-circle" style="color: var(--success-color);"></i> Risk Parity Allocation</li>
|
|
32
|
+
<li><i class="fas fa-check-circle" style="color: var(--success-color);"></i> Efficient Frontier Visualization</li>
|
|
33
|
+
<li><i class="fas fa-check-circle" style="color: var(--success-color);"></i> Multi-stock Correlation Analysis</li>
|
|
34
|
+
</ul>
|
|
35
|
+
</div>
|
|
36
|
+
<div style="margin-top: 2rem;">
|
|
37
|
+
<a href="/" class="btn btn-primary">
|
|
38
|
+
<i class="fas fa-home"></i> Back to Home
|
|
39
|
+
</a>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example: Strategy Generator - Reverse Engineering Profitable Trades
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates how to use SQA::StrategyGenerator to:
|
|
7
|
+
# 1. Detect inflection points (turning points) in price history
|
|
8
|
+
# 2. Identify which inflection points preceded profitable moves
|
|
9
|
+
# 3. Discover which indicators were active at those points
|
|
10
|
+
# 4. Generate trading strategies from discovered patterns
|
|
11
|
+
# 5. Backtest the generated strategies
|
|
12
|
+
#
|
|
13
|
+
# FPOP (Future Period of Performance): The number of days to look ahead
|
|
14
|
+
# from an inflection point to measure if price change exceeds threshold.
|
|
15
|
+
|
|
16
|
+
require 'sqa'
|
|
17
|
+
|
|
18
|
+
SQA.init
|
|
19
|
+
|
|
20
|
+
puts "=" * 70
|
|
21
|
+
puts "Strategy Generator: Mining Profitable Patterns"
|
|
22
|
+
puts "=" * 70
|
|
23
|
+
puts
|
|
24
|
+
|
|
25
|
+
# Load stock data
|
|
26
|
+
puts "Loading stock data for AAPL..."
|
|
27
|
+
stock = SQA::Stock.new(ticker: 'AAPL')
|
|
28
|
+
puts "Loaded #{stock.df.data.height} days of price history"
|
|
29
|
+
puts
|
|
30
|
+
|
|
31
|
+
# Example 1: Basic Pattern Discovery with FPOP
|
|
32
|
+
puts "\n" + "=" * 70
|
|
33
|
+
puts "Example 1: Discovering Patterns with FPOP (Future Period of Performance)"
|
|
34
|
+
puts "=" * 70
|
|
35
|
+
puts
|
|
36
|
+
|
|
37
|
+
generator = SQA::StrategyGenerator.new(
|
|
38
|
+
stock: stock,
|
|
39
|
+
min_gain_percent: 10.0, # Find inflection points that lead to ≥10% gain
|
|
40
|
+
fpop: 10, # Look 10 days ahead from inflection point
|
|
41
|
+
inflection_window: 3 # 3-day window for detecting local min/max
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Discover patterns
|
|
45
|
+
patterns = generator.discover_patterns(min_pattern_frequency: 3)
|
|
46
|
+
|
|
47
|
+
# Print discovered patterns
|
|
48
|
+
generator.print_patterns(max_patterns: 10)
|
|
49
|
+
|
|
50
|
+
# Example 2: Generate Strategies from Patterns
|
|
51
|
+
puts "\n" + "=" * 70
|
|
52
|
+
puts "Example 2: Generating Strategies from Top Patterns"
|
|
53
|
+
puts "=" * 70
|
|
54
|
+
puts
|
|
55
|
+
|
|
56
|
+
if patterns.any?
|
|
57
|
+
# Generate top 3 strategies
|
|
58
|
+
strategies = generator.generate_strategies(top_n: 3, strategy_type: :class)
|
|
59
|
+
|
|
60
|
+
puts "Generated #{strategies.size} strategies from top patterns"
|
|
61
|
+
puts
|
|
62
|
+
|
|
63
|
+
# Test each generated strategy
|
|
64
|
+
strategies.each_with_index do |strategy, i|
|
|
65
|
+
puts "-" * 70
|
|
66
|
+
puts "Testing Generated Strategy ##{i + 1}"
|
|
67
|
+
puts "Pattern: #{strategy.pattern}"
|
|
68
|
+
puts
|
|
69
|
+
|
|
70
|
+
backtest = SQA::Backtest.new(
|
|
71
|
+
stock: stock,
|
|
72
|
+
strategy: strategy,
|
|
73
|
+
initial_capital: 10_000.0,
|
|
74
|
+
commission: 1.0
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
results = backtest.run
|
|
78
|
+
|
|
79
|
+
puts "Backtest Results:"
|
|
80
|
+
puts " Total Return: #{results.total_return.round(2)}%"
|
|
81
|
+
puts " Sharpe Ratio: #{results.sharpe_ratio.round(2)}"
|
|
82
|
+
puts " Max Drawdown: #{results.max_drawdown.round(2)}%"
|
|
83
|
+
puts " Win Rate: #{results.win_rate.round(2)}%"
|
|
84
|
+
puts " Total Trades: #{results.total_trades}"
|
|
85
|
+
puts
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Example 3: Different Gain Thresholds
|
|
90
|
+
puts "\n" + "=" * 70
|
|
91
|
+
puts "Example 3: Comparing Different Gain Thresholds"
|
|
92
|
+
puts "=" * 70
|
|
93
|
+
puts
|
|
94
|
+
|
|
95
|
+
gain_thresholds = [5.0, 10.0, 15.0, 20.0]
|
|
96
|
+
|
|
97
|
+
gain_thresholds.each do |threshold|
|
|
98
|
+
puts "-" * 70
|
|
99
|
+
puts "Target Gain: #{threshold}%"
|
|
100
|
+
puts
|
|
101
|
+
|
|
102
|
+
gen = SQA::StrategyGenerator.new(
|
|
103
|
+
stock: stock,
|
|
104
|
+
min_gain_percent: threshold,
|
|
105
|
+
fpop: 10
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
patterns = gen.discover_patterns(min_pattern_frequency: 2)
|
|
109
|
+
|
|
110
|
+
if patterns.any?
|
|
111
|
+
top_pattern = patterns.first
|
|
112
|
+
puts "Top Pattern Found:"
|
|
113
|
+
puts " Frequency: #{top_pattern.frequency} occurrences"
|
|
114
|
+
puts " Average Gain: #{top_pattern.avg_gain.round(2)}%"
|
|
115
|
+
puts " Average Holding: #{top_pattern.avg_holding_days.round(1)} days"
|
|
116
|
+
puts " Conditions: #{top_pattern.conditions}"
|
|
117
|
+
else
|
|
118
|
+
puts "No patterns found for #{threshold}% gain threshold"
|
|
119
|
+
end
|
|
120
|
+
puts
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Example 4: Different FPOP Periods
|
|
124
|
+
puts "\n" + "=" * 70
|
|
125
|
+
puts "Example 4: Short-term vs Long-term FPOP"
|
|
126
|
+
puts "=" * 70
|
|
127
|
+
puts
|
|
128
|
+
|
|
129
|
+
fpop_configs = [
|
|
130
|
+
{ name: "Short-term", fpop: 5 },
|
|
131
|
+
{ name: "Medium-term", fpop: 15 },
|
|
132
|
+
{ name: "Long-term", fpop: 30 }
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
fpop_configs.each do |config|
|
|
136
|
+
puts "-" * 70
|
|
137
|
+
puts "#{config[:name]} FPOP (#{config[:fpop]} days)"
|
|
138
|
+
puts
|
|
139
|
+
|
|
140
|
+
gen = SQA::StrategyGenerator.new(
|
|
141
|
+
stock: stock,
|
|
142
|
+
min_gain_percent: 10.0,
|
|
143
|
+
fpop: config[:fpop]
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
patterns = gen.discover_patterns(min_pattern_frequency: 2)
|
|
147
|
+
|
|
148
|
+
if patterns.any?
|
|
149
|
+
puts "Discovered #{patterns.size} patterns"
|
|
150
|
+
puts "Top Pattern:"
|
|
151
|
+
top = patterns.first
|
|
152
|
+
puts " Frequency: #{top.frequency}"
|
|
153
|
+
puts " Avg Gain: #{top.avg_gain.round(2)}%"
|
|
154
|
+
puts " Avg Holding: #{top.avg_holding_days.round(1)} days"
|
|
155
|
+
puts " Conditions: #{top.conditions.keys.join(', ')}"
|
|
156
|
+
else
|
|
157
|
+
puts "No patterns found"
|
|
158
|
+
end
|
|
159
|
+
puts
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Example 5: Export Patterns to CSV
|
|
163
|
+
puts "\n" + "=" * 70
|
|
164
|
+
puts "Example 5: Exporting Patterns"
|
|
165
|
+
puts "=" * 70
|
|
166
|
+
puts
|
|
167
|
+
|
|
168
|
+
generator = SQA::StrategyGenerator.new(
|
|
169
|
+
stock: stock,
|
|
170
|
+
min_gain_percent: 10.0,
|
|
171
|
+
fpop: 10
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
patterns = generator.discover_patterns(min_pattern_frequency: 2)
|
|
175
|
+
|
|
176
|
+
if patterns.any?
|
|
177
|
+
output_file = "/tmp/sqa_discovered_patterns.csv"
|
|
178
|
+
generator.export_patterns(output_file)
|
|
179
|
+
puts "Patterns exported successfully!"
|
|
180
|
+
puts "View with: cat #{output_file}"
|
|
181
|
+
puts
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Example 6: Generate and Compare Multiple Strategies
|
|
185
|
+
puts "\n" + "=" * 70
|
|
186
|
+
puts "Example 6: Strategy Performance Comparison"
|
|
187
|
+
puts "=" * 70
|
|
188
|
+
puts
|
|
189
|
+
|
|
190
|
+
generator = SQA::StrategyGenerator.new(
|
|
191
|
+
stock: stock,
|
|
192
|
+
min_gain_percent: 10.0,
|
|
193
|
+
fpop: 10
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
patterns = generator.discover_patterns(min_pattern_frequency: 3)
|
|
197
|
+
|
|
198
|
+
if patterns.size >= 3
|
|
199
|
+
puts "Comparing Top 3 Discovered Strategies vs Traditional Strategies"
|
|
200
|
+
puts "-" * 70
|
|
201
|
+
puts
|
|
202
|
+
|
|
203
|
+
comparison = []
|
|
204
|
+
|
|
205
|
+
# Test discovered strategies
|
|
206
|
+
[0, 1, 2].each do |i|
|
|
207
|
+
strategy = generator.generate_strategy(pattern_index: i, strategy_type: :class)
|
|
208
|
+
|
|
209
|
+
backtest = SQA::Backtest.new(
|
|
210
|
+
stock: stock,
|
|
211
|
+
strategy: strategy,
|
|
212
|
+
initial_capital: 10_000.0,
|
|
213
|
+
commission: 1.0
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
results = backtest.run
|
|
217
|
+
|
|
218
|
+
comparison << {
|
|
219
|
+
name: "Discovered ##{i + 1}",
|
|
220
|
+
return: results.total_return,
|
|
221
|
+
sharpe: results.sharpe_ratio,
|
|
222
|
+
drawdown: results.max_drawdown,
|
|
223
|
+
win_rate: results.win_rate,
|
|
224
|
+
trades: results.total_trades
|
|
225
|
+
}
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Test traditional strategies for comparison
|
|
229
|
+
traditional_strategies = [
|
|
230
|
+
{ name: "RSI", class: SQA::Strategy::RSI },
|
|
231
|
+
{ name: "MACD", class: SQA::Strategy::MACD },
|
|
232
|
+
{ name: "Bollinger Bands", class: SQA::Strategy::BollingerBands }
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
traditional_strategies.each do |strat|
|
|
236
|
+
backtest = SQA::Backtest.new(
|
|
237
|
+
stock: stock,
|
|
238
|
+
strategy: strat[:class],
|
|
239
|
+
initial_capital: 10_000.0,
|
|
240
|
+
commission: 1.0
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
results = backtest.run
|
|
244
|
+
|
|
245
|
+
comparison << {
|
|
246
|
+
name: strat[:name],
|
|
247
|
+
return: results.total_return,
|
|
248
|
+
sharpe: results.sharpe_ratio,
|
|
249
|
+
drawdown: results.max_drawdown,
|
|
250
|
+
win_rate: results.win_rate,
|
|
251
|
+
trades: results.total_trades
|
|
252
|
+
}
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Print comparison table
|
|
256
|
+
puts "Strategy Performance Comparison:"
|
|
257
|
+
puts "-" * 70
|
|
258
|
+
printf("%-20s %10s %10s %12s %10s %8s\n",
|
|
259
|
+
"Strategy", "Return %", "Sharpe", "Drawdown %", "Win Rate", "Trades")
|
|
260
|
+
puts "-" * 70
|
|
261
|
+
|
|
262
|
+
comparison.sort_by { |s| -s[:return] }.each do |s|
|
|
263
|
+
printf("%-20s %10.2f %10.2f %12.2f %9.1f%% %8d\n",
|
|
264
|
+
s[:name],
|
|
265
|
+
s[:return],
|
|
266
|
+
s[:sharpe],
|
|
267
|
+
s[:drawdown],
|
|
268
|
+
s[:win_rate],
|
|
269
|
+
s[:trades])
|
|
270
|
+
end
|
|
271
|
+
puts "-" * 70
|
|
272
|
+
puts
|
|
273
|
+
|
|
274
|
+
# Find best performer
|
|
275
|
+
best = comparison.max_by { |s| s[:return] }
|
|
276
|
+
puts "🏆 Best Performer: #{best[:name]}"
|
|
277
|
+
puts " Total Return: #{best[:return].round(2)}%"
|
|
278
|
+
puts " Sharpe Ratio: #{best[:sharpe].round(2)}"
|
|
279
|
+
puts
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Example 7: Aggressive Pattern Mining
|
|
283
|
+
puts "\n" + "=" * 70
|
|
284
|
+
puts "Example 7: Aggressive Pattern Discovery (High Gains)"
|
|
285
|
+
puts "=" * 70
|
|
286
|
+
puts
|
|
287
|
+
|
|
288
|
+
aggressive_gen = SQA::StrategyGenerator.new(
|
|
289
|
+
stock: stock,
|
|
290
|
+
min_gain_percent: 20.0, # Very high gain target
|
|
291
|
+
fpop: 15
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
aggressive_patterns = aggressive_gen.discover_patterns(min_pattern_frequency: 1)
|
|
295
|
+
|
|
296
|
+
if aggressive_patterns.any?
|
|
297
|
+
puts "Aggressive patterns found for 20%+ gains:"
|
|
298
|
+
aggressive_gen.print_patterns(max_patterns: 5)
|
|
299
|
+
|
|
300
|
+
# Test the most aggressive pattern
|
|
301
|
+
puts "Testing Most Aggressive Pattern:"
|
|
302
|
+
puts "-" * 70
|
|
303
|
+
|
|
304
|
+
aggressive_strategy = aggressive_gen.generate_strategy(
|
|
305
|
+
pattern_index: 0,
|
|
306
|
+
strategy_type: :class
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
backtest = SQA::Backtest.new(
|
|
310
|
+
stock: stock,
|
|
311
|
+
strategy: aggressive_strategy,
|
|
312
|
+
initial_capital: 10_000.0,
|
|
313
|
+
commission: 1.0
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
results = backtest.run
|
|
317
|
+
|
|
318
|
+
puts "Results:"
|
|
319
|
+
puts " Total Return: #{results.total_return.round(2)}%"
|
|
320
|
+
puts " Sharpe Ratio: #{results.sharpe_ratio.round(2)}"
|
|
321
|
+
puts " Max Drawdown: #{results.max_drawdown.round(2)}%"
|
|
322
|
+
puts " Win Rate: #{results.win_rate.round(2)}%"
|
|
323
|
+
puts " Total Trades: #{results.total_trades}"
|
|
324
|
+
puts
|
|
325
|
+
puts "⚠️ Note: High gain patterns often have lower frequency and higher risk"
|
|
326
|
+
else
|
|
327
|
+
puts "No patterns found for 20%+ gains (criteria too strict)"
|
|
328
|
+
end
|
|
329
|
+
puts
|
|
330
|
+
|
|
331
|
+
puts "=" * 70
|
|
332
|
+
puts "Strategy Generation Complete!"
|
|
333
|
+
puts "=" * 70
|
|
334
|
+
puts
|
|
335
|
+
puts "Key Takeaways:"
|
|
336
|
+
puts "1. Pattern discovery identifies what indicators were active at profitable points"
|
|
337
|
+
puts "2. Higher gain thresholds yield fewer but potentially more profitable patterns"
|
|
338
|
+
puts "3. Different holding periods reveal different trading styles"
|
|
339
|
+
puts "4. Generated strategies can outperform traditional single-indicator strategies"
|
|
340
|
+
puts "5. Always backtest discovered patterns before live trading"
|
|
341
|
+
puts
|
|
342
|
+
puts "Next Steps:"
|
|
343
|
+
puts "- Combine with Genetic Programming to optimize pattern parameters"
|
|
344
|
+
puts "- Use KBS to create rule-based strategies from complex patterns"
|
|
345
|
+
puts "- Test on multiple stocks to find universal patterns"
|
|
346
|
+
puts "- Walk-forward validate patterns on out-of-sample data"
|
data/hsa_portfolio.csv
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# hsa_portfolio.csv
|
|
2
|
+
# health savings account hationalhr.com via oddball
|
|
3
|
+
# Fund %
|
|
4
|
+
#
|
|
5
|
+
# Ticker, Name, Shares (actually %)
|
|
6
|
+
#
|
|
7
|
+
HCB1B,HCB INTEREST BEARING ACCOUNT,10
|
|
8
|
+
VFIAX,VANGUARD 500 INDEX ADMIRAL,10
|
|
9
|
+
VSCIX,VANGUARD SMALL CAP INDEX INST,10
|
|
10
|
+
VSGIX,VANGUARD SMALL CP GROWTH IDX I,30
|
|
11
|
+
VIPIX,VANGUARD INFLATION PROTECTED I,40
|
data/justfile
ADDED
|
File without changes
|