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
data/develop_summary.md
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
Comprehensive Summary: Develop Branch Changes
|
|
2
|
+
Overview
|
|
3
|
+
|
|
4
|
+
The develop branch contains 12 commits spanning from February 2024 to August 2025 (18+ months of development). This represents a major evolution of the SQA project with significant architectural changes and new features.
|
|
5
|
+
|
|
6
|
+
Impact: 104,907 insertions, 2,017 deletions across 47 files
|
|
7
|
+
🎯 Strategic Direction Change
|
|
8
|
+
From Application to Library
|
|
9
|
+
|
|
10
|
+
Commit: 11351a6 (Feb 2024) - "refocused effort on a library rather than library plus application"
|
|
11
|
+
|
|
12
|
+
Major Architectural Shift:
|
|
13
|
+
|
|
14
|
+
Removed entire CLI command structure:
|
|
15
|
+
Deleted lib/sqa/cli.rb (62 lines)
|
|
16
|
+
Deleted lib/sqa/commands.rb (22 lines)
|
|
17
|
+
Deleted lib/sqa/commands/analysis.rb (309 lines)
|
|
18
|
+
Deleted lib/sqa/commands/base.rb (139 lines)
|
|
19
|
+
Deleted lib/sqa/commands/web.rb (199 lines)
|
|
20
|
+
Deleted lib/sqa/constants.rb (23 lines)
|
|
21
|
+
Removed dry-cli patches (228 lines)
|
|
22
|
+
|
|
23
|
+
Removed configuration testing infrastructure:
|
|
24
|
+
All config test files deleted
|
|
25
|
+
CLI override testing removed
|
|
26
|
+
|
|
27
|
+
Rationale: Simplification - focus on providing a reusable library for quantitative analysis rather than a full application with CLI.
|
|
28
|
+
🔧 Core Technical Improvements
|
|
29
|
+
1. DataFrame Refactoring (Major Change)
|
|
30
|
+
|
|
31
|
+
Commit: 9625e31 (Feb 2025) - "Refactor SQA::DataFrame to Integrate with polars-df Gem"
|
|
32
|
+
|
|
33
|
+
Before:
|
|
34
|
+
|
|
35
|
+
# Based on Hashie::Mash wrapper
|
|
36
|
+
class Data < Hashie::Mash
|
|
37
|
+
# Custom wrapper around hash-based data structure
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
After:
|
|
41
|
+
|
|
42
|
+
# Based on Polars (Rust-backed high-performance dataframe)
|
|
43
|
+
def initialize(raw_data = nil, mapping: {}, transformers: {})
|
|
44
|
+
@data = Polars::DataFrame.new(raw_data)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
Benefits:
|
|
48
|
+
|
|
49
|
+
Blazingly fast - Polars is written in Rust
|
|
50
|
+
Industry-standard DataFrame API (similar to Python's pandas/Polars)
|
|
51
|
+
Better memory efficiency
|
|
52
|
+
Native support for complex data operations
|
|
53
|
+
84 insertions, 272 deletions (net reduction + improved functionality)
|
|
54
|
+
|
|
55
|
+
2. Custom Alpha Vantage API Wrapper
|
|
56
|
+
|
|
57
|
+
Commits: 162dbdc, 9fb7727 (Feb 2024)
|
|
58
|
+
|
|
59
|
+
New File: lib/api/alpha_vantage_api.rb (462 lines)
|
|
60
|
+
|
|
61
|
+
Rationale: Replaced the alphavantage gem dependency with custom implementation
|
|
62
|
+
|
|
63
|
+
Features:
|
|
64
|
+
|
|
65
|
+
Direct Faraday-based HTTP client
|
|
66
|
+
Comprehensive API coverage (70+ methods)
|
|
67
|
+
Categories covered:
|
|
68
|
+
Market Data (quotes, intraday, daily, etc.)
|
|
69
|
+
Technical Indicators (AD, ADOSC, ADX, AROON, etc.)
|
|
70
|
+
Economic Indicators (commodities, aluminum, etc.)
|
|
71
|
+
Fundamental Data (balance sheet, earnings, etc.)
|
|
72
|
+
Digital & Forex markets
|
|
73
|
+
Better error handling
|
|
74
|
+
More maintainable and customizable
|
|
75
|
+
|
|
76
|
+
Example Methods:
|
|
77
|
+
|
|
78
|
+
def adx(symbol:, interval:, time_period:)
|
|
79
|
+
def balance_sheet(symbol:)
|
|
80
|
+
def aroon(symbol:, interval:, time_period:)
|
|
81
|
+
|
|
82
|
+
3. Genetic Programming for Strategy Evaluation
|
|
83
|
+
|
|
84
|
+
Commits: 578032b, 84b8ff5 (Oct 2024) - "SQA-144 Add GeneticProgram class for strategy evaluation"
|
|
85
|
+
|
|
86
|
+
New Files:
|
|
87
|
+
|
|
88
|
+
lib/sqa/gp.rb (107 lines) - Genetic Programming implementation
|
|
89
|
+
docs/genetic_programming.md (104 lines) - Comprehensive documentation
|
|
90
|
+
|
|
91
|
+
Purpose: Use evolutionary algorithms to discover optimal trading strategies
|
|
92
|
+
|
|
93
|
+
Key Concepts:
|
|
94
|
+
|
|
95
|
+
# Fitness functions for trading signals
|
|
96
|
+
fitness_sell = gain - delta # more positive = better
|
|
97
|
+
fitness_buy = loss + delta # more negative = better
|
|
98
|
+
fitness_hold = gain.abs < delta && loss.abs < delta
|
|
99
|
+
|
|
100
|
+
# Mutation constraints
|
|
101
|
+
constraint = {
|
|
102
|
+
indicators: [:rsi, :ma, :raw, :whatever],
|
|
103
|
+
pww: (5..15).to_a, # past window width
|
|
104
|
+
fww: (5..15).to_a, # future window width
|
|
105
|
+
x: (-30..-15).to_a # time instance
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
Integration: Uses the darwinning gem for genetic algorithm implementation
|
|
109
|
+
4. Black-Scholes Options Pricing
|
|
110
|
+
|
|
111
|
+
New File: lib/sqa/indicator/black_scholes.rb (61 lines)
|
|
112
|
+
|
|
113
|
+
Implementation:
|
|
114
|
+
|
|
115
|
+
def black_scholes(
|
|
116
|
+
option_type, # 'c' for call, 'p' for put
|
|
117
|
+
current_stock_price,
|
|
118
|
+
strike_price,
|
|
119
|
+
time_to_expiration, # in years
|
|
120
|
+
risk_free_rate, # as decimal
|
|
121
|
+
volatility # as decimal
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
Features:
|
|
125
|
+
|
|
126
|
+
European call and put option pricing
|
|
127
|
+
Normal distribution CDF implementation
|
|
128
|
+
Well-documented with volatility calculation examples
|
|
129
|
+
Meaningful parameter names (refactored from cryptic originals)
|
|
130
|
+
|
|
131
|
+
📚 Documentation Expansion
|
|
132
|
+
|
|
133
|
+
New Documentation Files:
|
|
134
|
+
|
|
135
|
+
docs/genetic_programming.md (104 lines)
|
|
136
|
+
Overview of GP methodology
|
|
137
|
+
Application to stock market predictions
|
|
138
|
+
Ruby implementation examples with darwinning gem
|
|
139
|
+
Fitness function design
|
|
140
|
+
Backtesting considerations
|
|
141
|
+
|
|
142
|
+
docs/factors_that_impact_price.md (26 lines)
|
|
143
|
+
Market factors affecting stock prices
|
|
144
|
+
|
|
145
|
+
docs/options.md (8 lines)
|
|
146
|
+
Options trading basics
|
|
147
|
+
|
|
148
|
+
docs/fx_pro_bit.md (25 lines)
|
|
149
|
+
Foreign exchange and cryptocurrency notes
|
|
150
|
+
|
|
151
|
+
docs/finviz.md (11 lines)
|
|
152
|
+
Note about finviz.com website for market screening
|
|
153
|
+
|
|
154
|
+
docs/i_gotta_an_idea.md (22 lines)
|
|
155
|
+
Project ideas and future directions
|
|
156
|
+
|
|
157
|
+
COMMITS.md (196 lines)
|
|
158
|
+
Conventional Commits specification (latest commit Aug 2025)
|
|
159
|
+
Standardized commit message format
|
|
160
|
+
Semantic versioning alignment
|
|
161
|
+
|
|
162
|
+
🔄 Dependency Updates
|
|
163
|
+
Added Dependencies (Gemfile)
|
|
164
|
+
|
|
165
|
+
gem 'csv' # CSV Reading and Writing
|
|
166
|
+
gem 'eps' # Machine learning for Ruby
|
|
167
|
+
gem 'polars-df' # Blazingly fast DataFrames
|
|
168
|
+
gem 'raix' # Ruby AI eXtensions
|
|
169
|
+
gem 'regent' # AI Agents in Ruby
|
|
170
|
+
gem 'rspec' # Testing framework
|
|
171
|
+
gem 'toml-rb' # TOML parser
|
|
172
|
+
|
|
173
|
+
Gemspec Changes
|
|
174
|
+
|
|
175
|
+
# Removed
|
|
176
|
+
- 'dry-cli' # CLI framework no longer needed
|
|
177
|
+
- 'sem_version' # Replaced with versionaire
|
|
178
|
+
|
|
179
|
+
# Added
|
|
180
|
+
+ 'ruby_llm' # LLM integration
|
|
181
|
+
+ 'ruby_llm-mcp' # LLM MCP protocol
|
|
182
|
+
+ 'shared_tools' # Shared utilities
|
|
183
|
+
|
|
184
|
+
# Updated
|
|
185
|
+
~ 'hashie', '~>4.1.0' => 'hashie' # Version constraint relaxed
|
|
186
|
+
~ spec.required_ruby_version = ">= 2.7" => ">= 3.2" # Ruby 3.2+ required
|
|
187
|
+
|
|
188
|
+
Note: Ruby version requirement increased from 2.7+ to 3.2+
|
|
189
|
+
🗂️ Data & Configuration
|
|
190
|
+
New Data Files
|
|
191
|
+
|
|
192
|
+
data/talk_talk.json (103,284 lines)
|
|
193
|
+
Massive dataset addition
|
|
194
|
+
Likely stock price or trading data
|
|
195
|
+
Represents 98.5% of all line additions
|
|
196
|
+
|
|
197
|
+
hsa_portfolio.csv (11 lines)
|
|
198
|
+
Sample HSA (Health Savings Account) portfolio data
|
|
199
|
+
|
|
200
|
+
.gitmessage.txt (5 lines)
|
|
201
|
+
Git commit message template
|
|
202
|
+
|
|
203
|
+
.semver (6 lines)
|
|
204
|
+
Semantic versioning configuration
|
|
205
|
+
|
|
206
|
+
Build Automation
|
|
207
|
+
|
|
208
|
+
main.just (81 lines)
|
|
209
|
+
|
|
210
|
+
Justfile for task automation
|
|
211
|
+
Modules for repo, gem, version, git operations
|
|
212
|
+
Tasks: install, test, coverage, flay (static analysis)
|
|
213
|
+
Man page generation with kramdown-man
|
|
214
|
+
|
|
215
|
+
🧪 Testing Improvements
|
|
216
|
+
|
|
217
|
+
Commits: ee314d6 (Feb 2024) - "corrected some typos; filled out skipped data_frame_test cases"
|
|
218
|
+
|
|
219
|
+
Changes to test/data_frame_test.rb:
|
|
220
|
+
|
|
221
|
+
46 insertions, 242 deletions (net simplification)
|
|
222
|
+
Previously skipped test cases now implemented
|
|
223
|
+
Found errors in DataFrame requiring research
|
|
224
|
+
Tests updated for Polars-based DataFrame
|
|
225
|
+
|
|
226
|
+
📊 Version History
|
|
227
|
+
|
|
228
|
+
Version Bump: 0.0.25 → 0.0.31 (6 minor versions)
|
|
229
|
+
|
|
230
|
+
Version System Changes:
|
|
231
|
+
|
|
232
|
+
Removed SQA.version class method
|
|
233
|
+
Simplified to constant: SQA::VERSION = '0.0.31'
|
|
234
|
+
Planning to replace semver gem with versionaire
|
|
235
|
+
|
|
236
|
+
🤖 AI/ML Integration Direction
|
|
237
|
+
|
|
238
|
+
New AI-Related Dependencies:
|
|
239
|
+
|
|
240
|
+
eps - Machine learning (regression & classification)
|
|
241
|
+
raix - Ruby AI extensions
|
|
242
|
+
regent - AI Agents library
|
|
243
|
+
ruby_llm - LLM integration
|
|
244
|
+
ruby_llm-mcp - LLM MCP protocol support
|
|
245
|
+
|
|
246
|
+
Genetic Programming:
|
|
247
|
+
|
|
248
|
+
Custom GP implementation for strategy discovery
|
|
249
|
+
Darwinning gem integration
|
|
250
|
+
Automated trading strategy evolution
|
|
251
|
+
|
|
252
|
+
Direction: The project is evolving toward AI-assisted quantitative trading strategy development
|
|
253
|
+
📝 Notable Files Modified
|
|
254
|
+
Stock.rb Refactoring
|
|
255
|
+
|
|
256
|
+
38 insertions, 127 deletions (net simplification)
|
|
257
|
+
Likely adapted to new DataFrame structure
|
|
258
|
+
|
|
259
|
+
Data Source Adapters
|
|
260
|
+
|
|
261
|
+
lib/sqa/data_frame/alpha_vantage.rb - Simplified (18 ins, 52 del)
|
|
262
|
+
lib/sqa/data_frame/yahoo_finance.rb - Refactored (34 ins, 43 del)
|
|
263
|
+
Both adapted to Polars DataFrame
|
|
264
|
+
|
|
265
|
+
🎯 Timeline Summary
|
|
266
|
+
|
|
267
|
+
| Date | Focus Area | Key Achievement | |------|-----------|----------------| | Feb 2024 | Architecture | Removed CLI, created Alpha Vantage API wrapper | | Feb 2024 | Testing | Filled DataFrame test gaps | | May 2024 | Versioning | Planned semver → versionaire migration | | Jun 2024 | Documentation | Added market analysis docs | | Oct 2024 | AI/ML | Added Genetic Programming for strategies | | Dec 2024 | Research | Documented finviz.com resource | | Feb 2025 | Performance | Major: Migrated to Polars DataFrame | | Feb 2025 | Dependencies | Added ML/AI gems (eps, raix, regent, ruby_llm) | | Aug 2025 | Standards | Adopted Conventional Commits |
|
|
268
|
+
⚠️ Breaking Changes
|
|
269
|
+
|
|
270
|
+
Ruby Version: Now requires Ruby 3.2+ (was 2.7+)
|
|
271
|
+
API Changes: DataFrame completely refactored (Hashie → Polars)
|
|
272
|
+
No CLI: Entire command-line interface removed
|
|
273
|
+
Dependencies: Several removed (dry-cli, sem_version)
|
|
274
|
+
Configuration: Test config files removed
|
|
275
|
+
|
|
276
|
+
✅ Merge Readiness Assessment
|
|
277
|
+
Strengths
|
|
278
|
+
|
|
279
|
+
Well-tested DataFrame refactoring
|
|
280
|
+
Comprehensive documentation
|
|
281
|
+
Clear architectural direction
|
|
282
|
+
Modern dependencies
|
|
283
|
+
Performance improvements with Polars
|
|
284
|
+
|
|
285
|
+
Concerns
|
|
286
|
+
|
|
287
|
+
Massive data file (103k line JSON) - may need .gitignore
|
|
288
|
+
Some commits mention finding errors needing research
|
|
289
|
+
Breaking changes will affect existing users
|
|
290
|
+
Ruby 3.2+ requirement may exclude some users
|
|
291
|
+
|
|
292
|
+
Recommendation
|
|
293
|
+
|
|
294
|
+
Review before merging:
|
|
295
|
+
|
|
296
|
+
Verify all DataFrame tests pass
|
|
297
|
+
Consider excluding data/talk_talk.json from version control
|
|
298
|
+
Document breaking changes in CHANGELOG
|
|
299
|
+
Consider semantic versioning: this should be 1.0.0 (major rewrite)
|
|
300
|
+
Ensure AI/ML features are production-ready
|
|
301
|
+
|
|
302
|
+
🚀 Future Direction
|
|
303
|
+
|
|
304
|
+
The develop branch shows SQA evolving from a simple CLI tool into a comprehensive AI-powered quantitative trading library with:
|
|
305
|
+
|
|
306
|
+
High-performance data processing (Polars)
|
|
307
|
+
Genetic algorithm strategy discovery
|
|
308
|
+
Options pricing (Black-Scholes)
|
|
309
|
+
LLM integration capabilities
|
|
310
|
+
Machine learning models
|
|
311
|
+
AI agent support
|
|
312
|
+
|
|
313
|
+
This is a significant and ambitious evolution of the project.
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Backtesting Framework
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Simulate trading strategies on historical data to evaluate performance before risking real capital.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
require 'sqa'
|
|
11
|
+
|
|
12
|
+
# Load stock data
|
|
13
|
+
stock = SQA::Stock.new(ticker: 'AAPL')
|
|
14
|
+
|
|
15
|
+
# Create backtest
|
|
16
|
+
backtest = SQA::Backtest.new(
|
|
17
|
+
stock: stock,
|
|
18
|
+
strategy: SQA::Strategy::RSI,
|
|
19
|
+
initial_cash: 10_000,
|
|
20
|
+
commission: 1.0
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Run simulation
|
|
24
|
+
results = backtest.run
|
|
25
|
+
|
|
26
|
+
# View results
|
|
27
|
+
puts "Total Return: #{results.total_return}%"
|
|
28
|
+
puts "Sharpe Ratio: #{results.sharpe_ratio}"
|
|
29
|
+
puts "Max Drawdown: #{results.max_drawdown}%"
|
|
30
|
+
puts "Win Rate: #{results.win_rate}%"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
### Basic Parameters
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
backtest = SQA::Backtest.new(
|
|
39
|
+
stock: stock, # SQA::Stock object
|
|
40
|
+
strategy: SQA::Strategy::MACD, # Strategy class
|
|
41
|
+
initial_cash: 10_000, # Starting capital
|
|
42
|
+
commission: 1.0, # Per-trade commission
|
|
43
|
+
position_size: 100 # Shares per trade
|
|
44
|
+
)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Results Object
|
|
48
|
+
|
|
49
|
+
The `backtest.run` method returns a results object with:
|
|
50
|
+
|
|
51
|
+
### Return Metrics
|
|
52
|
+
- `total_return` - Total percentage return
|
|
53
|
+
- `annualized_return` - Return per year
|
|
54
|
+
- `benchmark_return` - Buy-and-hold comparison
|
|
55
|
+
|
|
56
|
+
### Risk Metrics
|
|
57
|
+
- `sharpe_ratio` - Risk-adjusted return
|
|
58
|
+
- `sortino_ratio` - Downside risk-adjusted return
|
|
59
|
+
- `max_drawdown` - Largest peak-to-trough decline
|
|
60
|
+
- `volatility` - Standard deviation of returns
|
|
61
|
+
|
|
62
|
+
### Trading Metrics
|
|
63
|
+
- `num_trades` - Total number of trades
|
|
64
|
+
- `win_rate` - Percentage of profitable trades
|
|
65
|
+
- `avg_win` - Average winning trade
|
|
66
|
+
- `avg_loss` - Average losing trade
|
|
67
|
+
- `profit_factor` - Gross profit / gross loss
|
|
68
|
+
|
|
69
|
+
### Portfolio Metrics
|
|
70
|
+
- `final_value` - Ending portfolio value
|
|
71
|
+
- `portfolio_value` - Value over time (array)
|
|
72
|
+
- `trades` - Full trade history
|
|
73
|
+
|
|
74
|
+
## Complete Example
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
# Load data
|
|
78
|
+
stock = SQA::Stock.new(ticker: 'AAPL')
|
|
79
|
+
|
|
80
|
+
# Test multiple strategies
|
|
81
|
+
strategies = [
|
|
82
|
+
SQA::Strategy::RSI,
|
|
83
|
+
SQA::Strategy::MACD,
|
|
84
|
+
SQA::Strategy::BollingerBands
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
strategies.each do |strategy|
|
|
88
|
+
backtest = SQA::Backtest.new(
|
|
89
|
+
stock: stock,
|
|
90
|
+
strategy: strategy,
|
|
91
|
+
initial_cash: 10_000
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
results = backtest.run
|
|
95
|
+
|
|
96
|
+
puts "\n#{strategy.name} Results:"
|
|
97
|
+
puts "-" * 40
|
|
98
|
+
puts "Return: #{results.total_return.round(2)}%"
|
|
99
|
+
puts "Sharpe: #{results.sharpe_ratio.round(2)}"
|
|
100
|
+
puts "Max DD: #{results.max_drawdown.round(2)}%"
|
|
101
|
+
puts "Trades: #{results.num_trades}"
|
|
102
|
+
puts "Win Rate: #{results.win_rate.round(2)}%"
|
|
103
|
+
end
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Advanced Usage
|
|
107
|
+
|
|
108
|
+
### Custom Date Range
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
# Backtest specific period
|
|
112
|
+
stock = SQA::Stock.new(
|
|
113
|
+
ticker: 'AAPL',
|
|
114
|
+
start_date: '2020-01-01',
|
|
115
|
+
end_date: '2023-12-31'
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
backtest = SQA::Backtest.new(stock: stock, strategy: strategy)
|
|
119
|
+
results = backtest.run
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Parameter Optimization
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
# Test different RSI thresholds
|
|
126
|
+
(20..40).step(5).each do |oversold|
|
|
127
|
+
results = test_strategy_with_params(oversold)
|
|
128
|
+
puts "RSI #{oversold}: Return = #{results.total_return}%"
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Walk-Forward Testing
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
# Train on first 70%, test on last 30%
|
|
136
|
+
train_end = (stock.df.height * 0.7).to_i
|
|
137
|
+
train_stock = stock.df[0...train_end]
|
|
138
|
+
test_stock = stock.df[train_end..-1]
|
|
139
|
+
|
|
140
|
+
# Optimize on training data
|
|
141
|
+
best_params = optimize_on(train_stock)
|
|
142
|
+
|
|
143
|
+
# Validate on test data
|
|
144
|
+
results = backtest_with_params(test_stock, best_params)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Visualization
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
# Plot equity curve
|
|
151
|
+
require 'gruff'
|
|
152
|
+
|
|
153
|
+
g = Gruff::Line.new
|
|
154
|
+
g.title = "Portfolio Value Over Time"
|
|
155
|
+
g.data("Portfolio", results.portfolio_value)
|
|
156
|
+
g.write('equity_curve.png')
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Best Practices
|
|
160
|
+
|
|
161
|
+
### 1. Use Enough Data
|
|
162
|
+
- Minimum: 252 trading days (1 year)
|
|
163
|
+
- Recommended: 1260+ days (5 years)
|
|
164
|
+
- Include different market conditions
|
|
165
|
+
|
|
166
|
+
### 2. Account for Costs
|
|
167
|
+
```ruby
|
|
168
|
+
backtest = SQA::Backtest.new(
|
|
169
|
+
commission: 1.0, # Per trade
|
|
170
|
+
slippage: 0.001 # 0.1% slippage
|
|
171
|
+
)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 3. Avoid Overfitting
|
|
175
|
+
- Use walk-forward analysis
|
|
176
|
+
- Test on out-of-sample data
|
|
177
|
+
- Limit parameter optimization
|
|
178
|
+
|
|
179
|
+
### 4. Compare to Benchmark
|
|
180
|
+
```ruby
|
|
181
|
+
if results.total_return < results.benchmark_return
|
|
182
|
+
puts "⚠️ Strategy underperforms buy-and-hold"
|
|
183
|
+
end
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 5. Check Multiple Metrics
|
|
187
|
+
Don't rely on return alone:
|
|
188
|
+
- Sharpe ratio (risk-adjusted)
|
|
189
|
+
- Max drawdown (worst case)
|
|
190
|
+
- Win rate (consistency)
|
|
191
|
+
- Profit factor (quality)
|
|
192
|
+
|
|
193
|
+
## Common Pitfalls
|
|
194
|
+
|
|
195
|
+
❌ **Survivorship Bias**: Only testing stocks that still exist
|
|
196
|
+
❌ **Look-Ahead Bias**: Using future data
|
|
197
|
+
❌ **Curve Fitting**: Over-optimizing parameters
|
|
198
|
+
❌ **Ignoring Costs**: Unrealistic commission/slippage
|
|
199
|
+
❌ **Small Sample**: Too few trades for statistical significance
|
|
200
|
+
|
|
201
|
+
## Related
|
|
202
|
+
|
|
203
|
+
- [Portfolio Management](portfolio.md) - Track positions and P&L
|
|
204
|
+
- [Strategy Generator](strategy-generator.md) - Discover patterns
|
|
205
|
+
- [Risk Management](risk-management.md) - Position sizing and risk metrics
|
|
206
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Ensemble Strategies
|
|
2
|
+
|
|
3
|
+
Combine multiple strategies with voting and meta-learning.
|
|
4
|
+
|
|
5
|
+
## Basic Ensemble
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
ensemble = SQA::Ensemble.new(
|
|
9
|
+
strategies: [
|
|
10
|
+
SQA::Strategy::RSI,
|
|
11
|
+
SQA::Strategy::MACD,
|
|
12
|
+
SQA::Strategy::BollingerBands
|
|
13
|
+
],
|
|
14
|
+
voting_method: :majority
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
signal = ensemble.signal(vector) # => :buy, :sell, or :hold
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Voting Methods
|
|
21
|
+
|
|
22
|
+
### Majority Voting
|
|
23
|
+
```ruby
|
|
24
|
+
voting_method: :majority # Most common signal wins
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Weighted Voting
|
|
28
|
+
```ruby
|
|
29
|
+
voting_method: :weighted
|
|
30
|
+
# Weight strategies by past performance
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Unanimous
|
|
34
|
+
```ruby
|
|
35
|
+
voting_method: :unanimous # All must agree
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Confidence-Based
|
|
39
|
+
```ruby
|
|
40
|
+
voting_method: :confidence # Weight by confidence scores
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Dynamic Weighting
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
# Update weights based on performance
|
|
47
|
+
ensemble.update_weight(SQA::Strategy::RSI, 1.5) # Increase weight
|
|
48
|
+
ensemble.update_weight(SQA::Strategy::MACD, 0.5) # Decrease weight
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Strategy Rotation
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
# Select best strategy for current market
|
|
55
|
+
selected = ensemble.rotate(stock)
|
|
56
|
+
# Automatically switches based on recent performance
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Backtest Comparison
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
comparison = ensemble.backtest_comparison(stock)
|
|
63
|
+
|
|
64
|
+
comparison.each do |name, results|
|
|
65
|
+
puts "#{name}: Return = #{results[:return]}%"
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|