sqa 0.0.37 → 0.0.38

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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -0
  3. data/docs/api-reference/alphavantageapi.md +105 -105
  4. data/docs/api-reference/apierror.md +2 -2
  5. data/docs/api-reference/index.md +2 -2
  6. data/docs/api-reference/notimplemented.md +2 -2
  7. data/docs/api-reference/sqa.md +12 -12
  8. data/docs/api-reference/sqa_backtest.md +43 -9
  9. data/docs/api-reference/sqa_backtest_results.md +34 -34
  10. data/docs/api-reference/sqa_badparametererror.md +1 -1
  11. data/docs/api-reference/sqa_config.md +31 -31
  12. data/docs/api-reference/sqa_configurationerror.md +1 -1
  13. data/docs/api-reference/sqa_datafetcherror.md +3 -3
  14. data/docs/api-reference/sqa_dataframe.md +63 -36
  15. data/docs/api-reference/sqa_dataframe_alphavantage.md +2 -2
  16. data/docs/api-reference/sqa_dataframe_data.md +18 -18
  17. data/docs/api-reference/sqa_dataframe_yahoofinance.md +2 -2
  18. data/docs/api-reference/sqa_ensemble.md +21 -21
  19. data/docs/api-reference/sqa_fpop.md +8 -8
  20. data/docs/api-reference/sqa_geneticprogram.md +20 -20
  21. data/docs/api-reference/sqa_geneticprogram_individual.md +8 -8
  22. data/docs/api-reference/sqa_marketregime.md +10 -10
  23. data/docs/api-reference/sqa_multitimeframe.md +11 -11
  24. data/docs/api-reference/sqa_patternmatcher.md +9 -9
  25. data/docs/api-reference/sqa_pluginmanager.md +4 -4
  26. data/docs/api-reference/sqa_portfolio.md +84 -27
  27. data/docs/api-reference/sqa_portfolio_position.md +12 -12
  28. data/docs/api-reference/sqa_portfolio_trade.md +16 -16
  29. data/docs/api-reference/sqa_portfoliooptimizer.md +10 -10
  30. data/docs/api-reference/sqa_riskmanager.md +12 -12
  31. data/docs/api-reference/sqa_seasonalanalyzer.md +6 -6
  32. data/docs/api-reference/sqa_sectoranalyzer.md +9 -9
  33. data/docs/api-reference/sqa_stock.md +48 -36
  34. data/docs/api-reference/sqa_strategy.md +8 -8
  35. data/docs/api-reference/sqa_strategy_bollingerbands.md +2 -2
  36. data/docs/api-reference/sqa_strategy_common.md +3 -3
  37. data/docs/api-reference/sqa_strategy_consensus.md +12 -12
  38. data/docs/api-reference/sqa_strategy_ema.md +4 -4
  39. data/docs/api-reference/sqa_strategy_kbs.md +11 -11
  40. data/docs/api-reference/sqa_strategy_macd.md +2 -2
  41. data/docs/api-reference/sqa_strategy_mp.md +4 -4
  42. data/docs/api-reference/sqa_strategy_mr.md +4 -4
  43. data/docs/api-reference/sqa_strategy_random.md +4 -4
  44. data/docs/api-reference/sqa_strategy_rsi.md +4 -4
  45. data/docs/api-reference/sqa_strategy_sma.md +4 -4
  46. data/docs/api-reference/sqa_strategy_stochastic.md +2 -2
  47. data/docs/api-reference/sqa_strategy_volumebreakout.md +2 -2
  48. data/docs/api-reference/sqa_strategygenerator.md +19 -19
  49. data/docs/api-reference/sqa_strategygenerator_pattern.md +17 -17
  50. data/docs/api-reference/sqa_strategygenerator_patterncontext.md +21 -21
  51. data/docs/api-reference/sqa_strategygenerator_profitablepoint.md +27 -27
  52. data/docs/api-reference/sqa_stream.md +18 -18
  53. data/docs/api-reference/sqa_ticker.md +8 -8
  54. data/docs/api-reference/string.md +11 -11
  55. data/docs/file_formats.md +250 -0
  56. data/lib/sqa/backtest.rb +32 -0
  57. data/lib/sqa/data_frame.rb +25 -0
  58. data/lib/sqa/portfolio.rb +54 -0
  59. data/lib/sqa/stock.rb +11 -0
  60. data/lib/sqa/version.rb +1 -1
  61. data/mkdocs.yml +1 -0
  62. metadata +2 -2
  63. data/docs/IMPROVEMENT_PLAN.md +0 -531
@@ -1,531 +0,0 @@
1
- # SQA Gem Improvement Plan
2
-
3
- **Created:** 2025-11-23
4
- **Status:** Pending Review
5
- **Scope:** 17 files, 31 discrete changes across 4 phases
6
- **Impact Analysis:** Verified against sqa_demo-sinatra and sqa-cli
7
-
8
- ---
9
-
10
- ## Overview
11
-
12
- This plan addresses code quality issues identified in a comprehensive review of the SQA codebase. All changes have been verified for backward compatibility with dependent gems.
13
-
14
- ### Dependent Gems Analyzed
15
-
16
- | Gem | Impact | Notes |
17
- |-----|--------|-------|
18
- | sqa_demo-sinatra | ✅ All changes safe | Explicitly calls `SQA.init` |
19
- | sqa-cli | ✅ All changes safe | Requires coordinated update (see separate plan) |
20
-
21
- ---
22
-
23
- ## Phase 1: Critical Security & Correctness Fixes
24
-
25
- **Priority:** Immediate
26
- **Dependencies:** None
27
- **Files:** 4
28
- **Breaking Changes:** None
29
-
30
- ### Task 1.1: Fix Shell Injection Vulnerability
31
-
32
- **File:** `lib/sqa/config.rb`
33
- **Line:** 123
34
-
35
- ```ruby
36
- # FROM:
37
- `touch #{config_file}`
38
-
39
- # TO:
40
- FileUtils.touch(config_file)
41
- ```
42
-
43
- **Requires:** Verify `require 'fileutils'` is present
44
- **Risk:** None
45
- **Test:** Verify config file creation works
46
-
47
- ---
48
-
49
- ### Task 1.2: Replace Deprecated `has_key?` with `key?`
50
-
51
- | File | Line | Change |
52
- |------|------|--------|
53
- | `lib/sqa/stock.rb` | 176 | `temp.has_key? "Information"` → `temp.key?("Information")` |
54
- | `lib/sqa/ticker.rb` | 74 | `has_key?` → `key?` |
55
- | `lib/sqa/config.rb` | 111 | `has_key?` → `key?` |
56
-
57
- **Risk:** None
58
- **Test:** Existing tests pass
59
-
60
- ---
61
-
62
- ### Task 1.3: Fix String Raises to Proper Exception Classes
63
-
64
- **File:** `lib/sqa/errors.rb` - Add new classes:
65
-
66
- ```ruby
67
- class DataFetchError < StandardError
68
- attr_reader :original_error
69
- def initialize(message, original: nil)
70
- @original_error = original
71
- super(message)
72
- end
73
- end
74
-
75
- class ConfigurationError < StandardError; end
76
- ```
77
-
78
- **File:** `lib/sqa/stock.rb` (Lines 106-108):
79
-
80
- ```ruby
81
- # FROM:
82
- raise "Unable to fetch data for #{@ticker}...Error: #{e.message}"
83
-
84
- # TO:
85
- raise SQA::DataFetchError.new("Unable to fetch data for #{@ticker}: #{e.message}", original: e)
86
- ```
87
-
88
- **File:** `lib/sqa/init.rb` (Line 34):
89
-
90
- ```ruby
91
- # FROM:
92
- raise('Alpha Vantage API key not set...')
93
-
94
- # TO:
95
- raise SQA::ConfigurationError, 'Alpha Vantage API key not set...'
96
- ```
97
-
98
- **Risk:** Low - More informative errors, generic rescue still catches them
99
- **Test:** Add tests for new exception classes
100
-
101
- ---
102
-
103
- ### Task 1.4: Replace Bare Rescues with Specific Exception Types
104
-
105
- **File:** `lib/sqa/stock.rb`
106
-
107
- | Line | From | To |
108
- |------|------|-----|
109
- | 54 | `rescue => e` | `rescue StandardError => e` |
110
- | 106 | `rescue => e` | `rescue Faraday::Error, StandardError => e` |
111
- | 128 | `rescue => e` | `rescue StandardError => e` |
112
- | 143 | `rescue` (bare) | `rescue ArgumentError, Date::Error => e` |
113
- | 154 | `rescue => e` | `rescue StandardError => e` |
114
-
115
- **Risk:** Low - Won't catch `Interrupt`/`SystemExit` (correct behavior)
116
- **Test:** Verify error handling for expected failures
117
-
118
- ---
119
-
120
- ## Phase 2: Code Quality & Best Practices
121
-
122
- **Priority:** High
123
- **Dependencies:** Phase 1 complete
124
- **Files:** 7
125
- **Breaking Changes:** None
126
-
127
- ### Task 2.1: Replace Class Variables with Thread-Safe Alternatives
128
-
129
- **File:** `lib/sqa/init.rb` (Lines 5-6, 34, 47-48, 52, 55)
130
-
131
- ```ruby
132
- # FROM:
133
- @@data_dir = nil
134
- @@av_api_key = nil
135
-
136
- # TO:
137
- class << self
138
- attr_accessor :data_dir, :av_api_key
139
- end
140
- @data_dir = nil
141
- @av_api_key = nil
142
- ```
143
-
144
- **File:** `lib/sqa/stock.rb` (Lines 196, 219)
145
-
146
- ```ruby
147
- # FROM:
148
- @@top = nil
149
- @@bottom = nil
150
-
151
- # TO:
152
- class << self
153
- def top
154
- @top ||= fetch_top_data
155
- end
156
-
157
- def bottom
158
- @bottom ||= fetch_bottom_data
159
- end
160
-
161
- def reset_cache!
162
- @top = nil
163
- @bottom = nil
164
- end
165
- end
166
- ```
167
-
168
- **File:** `lib/sqa/ticker.rb` (Lines 16, 61, 67, 72) - Similar pattern
169
-
170
- **Risk:** Medium - Changes shared state behavior
171
- **Test:** Add thread-safety tests, verify existing tests pass
172
-
173
- ---
174
-
175
- ### Task 2.2: Remove `puts` from Error Classes
176
-
177
- **File:** `lib/sqa/errors.rb` (Lines 5-14, 18-26)
178
-
179
- ```ruby
180
- # FROM:
181
- class ApiError < RuntimeError
182
- def self.raise(why)
183
- puts "="*64
184
- puts "== API Error"
185
- puts why
186
- puts "="*64
187
- super
188
- end
189
- end
190
-
191
- # TO:
192
- class ApiError < RuntimeError
193
- def self.raise(why)
194
- debug_me {"API Error: #{why}"}
195
- super
196
- end
197
- end
198
- ```
199
-
200
- **Requires:** `debug_me` gem available (per CLAUDE.md guidelines)
201
- **Risk:** Low
202
- **Test:** Verify errors still raise correctly
203
-
204
- ---
205
-
206
- ### Task 2.3: Remove `puts` from Production Code
207
-
208
- **File:** `lib/sqa/sector_analyzer.rb` (Lines 145-147)
209
-
210
- ```ruby
211
- # FROM:
212
- puts "=" * 70
213
- puts "Discovering patterns for #{sector.to_s.upcase} sector"
214
- puts "=" * 70
215
-
216
- # TO:
217
- debug_me {"Discovering patterns for #{sector.to_s.upcase} sector"}
218
- ```
219
-
220
- **Risk:** None
221
- **Test:** Verify sector analysis works
222
-
223
- ---
224
-
225
- ### Task 2.4: Simplify `method_missing` in DataFrame
226
-
227
- **File:** `lib/sqa/data_frame.rb` (Lines 164-173)
228
-
229
- **Option A - Simplified Dynamic (Recommended):**
230
-
231
- ```ruby
232
- # FROM:
233
- def method_missing(method_name, *args, &block)
234
- if @data.respond_to?(method_name)
235
- self.class.send(:define_method, method_name) do |*method_args, &method_block|
236
- @data.send(method_name, *method_args, &method_block)
237
- end
238
- send(method_name, *args, &block)
239
- else
240
- super
241
- end
242
- end
243
-
244
- # TO:
245
- def method_missing(method_name, *args, &block)
246
- return super unless @data.respond_to?(method_name)
247
- @data.send(method_name, *args, &block)
248
- end
249
- ```
250
-
251
- **Risk:** Low - Removes dynamic method definition, keeps delegation
252
- **Test:** Run all DataFrame tests
253
-
254
- ---
255
-
256
- ### Task 2.5: Remove Unused Parameters
257
-
258
- **File:** `lib/sqa/data_frame.rb` (Lines 200-209)
259
-
260
- ```ruby
261
- # FROM:
262
- def from_aofh(aofh, mapping: {}, transformers: {})
263
-
264
- # TO:
265
- def from_aofh(aofh)
266
- ```
267
-
268
- **Risk:** None - Parameters were never used
269
- **Test:** Verify `from_aofh` works
270
-
271
- ---
272
-
273
- ### Task 2.6: Fix Type Checking Pattern
274
-
275
- **File:** `lib/sqa/strategy.rb` (Line 11)
276
-
277
- ```ruby
278
- # FROM:
279
- raise BadParameterError unless [Class, Method].include? a_strategy.class
280
-
281
- # TO:
282
- raise BadParameterError unless a_strategy.is_a?(Class) || a_strategy.is_a?(Method)
283
- ```
284
-
285
- **Risk:** None
286
- **Test:** Existing tests pass
287
-
288
- ---
289
-
290
- ### Task 2.7: Remove Magic Placeholder Code
291
-
292
- **File:** `lib/sqa/stock.rb` (Line 48)
293
-
294
- ```ruby
295
- # FROM:
296
- @data = SQA::DataFrame::Data.new(ticker: @ticker, source: @source, indicators: { xyzzy: "Magic" })
297
-
298
- # TO:
299
- @data = SQA::DataFrame::Data.new(ticker: @ticker, source: @source, indicators: {})
300
- ```
301
-
302
- **Risk:** None
303
- **Test:** Verify stock initialization
304
-
305
- ---
306
-
307
- ## Phase 3: Architecture & Design Improvements
308
-
309
- **Priority:** Medium
310
- **Dependencies:** Phases 1-2 complete
311
- **Files:** 3
312
- **Breaking Changes:** None (Task 3.1 revised)
313
-
314
- ### Task 3.1: Add Deprecation Warning for Auto-Initialization (REVISED)
315
-
316
- **Original Plan:** Remove `SQA::Config.reset` at require time
317
- **Revision:** Keep auto-init but add deprecation warning for future removal
318
-
319
- **File:** `lib/sqa/config.rb` (Line 195)
320
-
321
- ```ruby
322
- # FROM:
323
- SQA::Config.reset
324
-
325
- # TO:
326
- unless SQA::Config.instance_variable_get(:@initialized)
327
- warn "[SQA DEPRECATION] Auto-initialization at require time will be removed in v1.0. " \
328
- "Please call SQA.init explicitly in your application startup."
329
- SQA::Config.reset
330
- end
331
- ```
332
-
333
- **Rationale:** sqa-cli does not explicitly call `SQA.init` and would break. This gives dependent gems time to update.
334
-
335
- **Coordination Required:**
336
- - Update sqa-cli to call `SQA.init` explicitly (see separate plan)
337
- - Remove auto-init in SQA v1.0
338
-
339
- **Risk:** None - Backward compatible with warning
340
- **Test:** Verify warning appears, initialization still works
341
-
342
- ---
343
-
344
- ### Task 3.2: Add Ascending Order Enforcement
345
-
346
- **File:** `lib/sqa/data_frame.rb` (around line 85)
347
-
348
- ```ruby
349
- def concat_and_deduplicate!(other_df, sort_column: "timestamp", descending: false)
350
- if descending
351
- warn "[SQA WARNING] TA-Lib requires ascending (oldest-first) order. Forcing descending: false"
352
- descending = false
353
- end
354
- # ... rest of method
355
- end
356
- ```
357
-
358
- **Risk:** Low - Prevents silent data ordering bugs
359
- **Test:** Add test for descending=true warning
360
-
361
- ---
362
-
363
- ### Task 3.3: Extract Faraday Connection to Configurable Dependency
364
-
365
- **File:** `lib/sqa/stock.rb` (Line 6)
366
-
367
- ```ruby
368
- # FROM:
369
- CONNECTION = Faraday.new(url: "https://www.alphavantage.co")
370
-
371
- # TO:
372
- class << self
373
- def connection
374
- @connection ||= default_connection
375
- end
376
-
377
- def connection=(conn)
378
- @connection = conn
379
- end
380
-
381
- def default_connection
382
- Faraday.new(url: "https://www.alphavantage.co")
383
- end
384
-
385
- def reset_connection!
386
- @connection = nil
387
- end
388
- end
389
- ```
390
-
391
- **Benefit:** Allows mocking in tests, custom configurations
392
- **Risk:** Low - Default behavior unchanged
393
- **Test:** Add tests for connection injection
394
-
395
- ---
396
-
397
- ## Phase 4: Documentation & Cleanup
398
-
399
- **Priority:** Low
400
- **Dependencies:** Phases 1-3 complete
401
- **Files:** Multiple
402
- **Breaking Changes:** None
403
-
404
- ### Task 4.1: Resolve TODO Comments
405
-
406
- Create GitHub issues for each TODO, update code with issue references:
407
-
408
- | File | Line | TODO |
409
- |------|------|------|
410
- | `lib/sqa.rb` | 4, 16-17 | Investigate |
411
- | `lib/sqa/config.rb` | 23 (FIXME), 36, 43, 47, 96 | Create issues |
412
- | `lib/api/alpha_vantage_api.rb` | 6 | Investigate |
413
- | `lib/sqa/strategy.rb` | 24 | Investigate |
414
- | `lib/sqa/init.rb` | 21 | Investigate |
415
-
416
- **Deliverable:** GitHub issues created, TODOs updated
417
-
418
- ---
419
-
420
- ### Task 4.2: Fix Error Class Documentation
421
-
422
- **File:** `lib/sqa/errors.rb`
423
-
424
- Fix duplicated/incorrect docstring for `NotImplemented` class.
425
-
426
- ---
427
-
428
- ### Task 4.3: Standardize Method Naming (Optional)
429
-
430
- **File:** `lib/sqa/stock.rb`
431
-
432
- | Current | Proposed |
433
- |---------|----------|
434
- | `update_the_dataframe` | `update_dataframe` |
435
-
436
- **Decision:** Add deprecation alias or rename directly?
437
- **Risk:** Medium if renaming without deprecation
438
-
439
- ---
440
-
441
- ## Implementation Order
442
-
443
- ```
444
- Week 1: Phase 1 (Critical)
445
- ├── Task 1.1: Shell injection fix
446
- ├── Task 1.2: has_key? → key?
447
- ├── Task 1.3: String raises → exceptions
448
- └── Task 1.4: Bare rescues → specific types
449
-
450
- Week 2: Phase 2 (Quality) - Part 1
451
- ├── Task 2.1: Class variables → instance variables
452
- ├── Task 2.2: Remove puts from errors
453
- └── Task 2.3: Remove puts from production
454
-
455
- Week 3: Phase 2 (Quality) - Part 2
456
- ├── Task 2.4: Simplify method_missing
457
- ├── Task 2.5: Remove unused parameters
458
- ├── Task 2.6: Fix type checking
459
- └── Task 2.7: Remove magic placeholder
460
-
461
- Week 4: Phase 3 (Architecture)
462
- ├── Task 3.1: Add deprecation warning (coordinate with sqa-cli)
463
- ├── Task 3.2: Ascending order enforcement
464
- └── Task 3.3: Configurable connection
465
-
466
- Ongoing: Phase 4 (Docs)
467
- ├── Task 4.1-4.3: Documentation cleanup
468
- ```
469
-
470
- ---
471
-
472
- ## Testing Strategy
473
-
474
- ### Per-Phase Testing
475
- - Run `rake test` after each task
476
- - Verify no regressions
477
-
478
- ### Integration Testing
479
- After all phases:
480
- 1. Install updated SQA locally
481
- 2. Run sqa_demo-sinatra test suite
482
- 3. Run sqa-cli test suite
483
- 4. Manual smoke testing of key features
484
-
485
- ### Test Coverage Additions
486
- - New exception classes (Task 1.3)
487
- - Thread-safety for class instance variables (Task 2.1)
488
- - Connection injection (Task 3.3)
489
- - Ascending order warning (Task 3.2)
490
-
491
- ---
492
-
493
- ## Rollback Strategy
494
-
495
- Each phase creates separate commits allowing:
496
- - Independent review per phase
497
- - Easy rollback if issues discovered
498
- - Incremental release if needed
499
-
500
- ---
501
-
502
- ## Version Strategy
503
-
504
- | Release | Changes |
505
- |---------|---------|
506
- | v0.0.33 | Current release |
507
- | v0.0.34 | Phases 1-2 (non-breaking fixes) |
508
- | v0.0.35 | Phase 3 (with deprecation warning) |
509
- | v1.0.0 | Remove auto-initialization (breaking) |
510
-
511
- ---
512
-
513
- ## Coordination with Dependent Gems
514
-
515
- ### sqa-cli
516
- - **Before SQA v0.0.35:** Update sqa-cli to call `SQA.init` explicitly
517
- - See: `sqa-cli/docs/SQA_COMPATIBILITY_UPDATE.md`
518
-
519
- ### sqa_demo-sinatra
520
- - **No changes required** - Already calls `SQA.init` explicitly
521
-
522
- ---
523
-
524
- ## Approval Checklist
525
-
526
- - [ ] User approves Phase 1 tasks
527
- - [ ] User approves Phase 2 tasks
528
- - [ ] User approves Phase 3 tasks (revised)
529
- - [ ] User approves Phase 4 tasks
530
- - [ ] sqa-cli update plan approved
531
- - [ ] Ready to begin implementation