sqa 0.0.32 → 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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +154 -1
  3. data/README.md +4 -0
  4. data/Rakefile +52 -10
  5. data/docs/advanced/index.md +1 -13
  6. data/docs/api/index.md +547 -61
  7. data/docs/api-reference/alphavantageapi.md +1057 -0
  8. data/docs/api-reference/apierror.md +31 -0
  9. data/docs/api-reference/index.md +221 -0
  10. data/docs/api-reference/notimplemented.md +27 -0
  11. data/docs/api-reference/sqa.md +267 -0
  12. data/docs/api-reference/sqa_backtest.md +171 -0
  13. data/docs/api-reference/sqa_backtest_results.md +530 -0
  14. data/docs/api-reference/sqa_badparametererror.md +13 -0
  15. data/docs/api-reference/sqa_config.md +538 -0
  16. data/docs/api-reference/sqa_configurationerror.md +13 -0
  17. data/docs/api-reference/sqa_datafetcherror.md +56 -0
  18. data/docs/api-reference/sqa_dataframe.md +779 -0
  19. data/docs/api-reference/sqa_dataframe_alphavantage.md +30 -0
  20. data/docs/api-reference/sqa_dataframe_data.md +325 -0
  21. data/docs/api-reference/sqa_dataframe_yahoofinance.md +25 -0
  22. data/docs/api-reference/sqa_ensemble.md +413 -0
  23. data/docs/api-reference/sqa_fpop.md +211 -0
  24. data/docs/api-reference/sqa_geneticprogram.md +325 -0
  25. data/docs/api-reference/sqa_geneticprogram_individual.md +114 -0
  26. data/docs/api-reference/sqa_marketregime.md +212 -0
  27. data/docs/api-reference/sqa_multitimeframe.md +227 -0
  28. data/docs/api-reference/sqa_patternmatcher.md +195 -0
  29. data/docs/api-reference/sqa_pluginmanager.md +55 -0
  30. data/docs/api-reference/sqa_portfolio.md +512 -0
  31. data/docs/api-reference/sqa_portfolio_position.md +220 -0
  32. data/docs/api-reference/sqa_portfolio_trade.md +332 -0
  33. data/docs/api-reference/sqa_portfoliooptimizer.md +248 -0
  34. data/docs/api-reference/sqa_riskmanager.md +388 -0
  35. data/docs/api-reference/sqa_seasonalanalyzer.md +121 -0
  36. data/docs/api-reference/sqa_sectoranalyzer.md +163 -0
  37. data/docs/api-reference/sqa_stock.md +661 -0
  38. data/docs/api-reference/sqa_strategy.md +178 -0
  39. data/docs/api-reference/sqa_strategy_bollingerbands.md +26 -0
  40. data/docs/api-reference/sqa_strategy_common.md +29 -0
  41. data/docs/api-reference/sqa_strategy_consensus.md +129 -0
  42. data/docs/api-reference/sqa_strategy_ema.md +41 -0
  43. data/docs/api-reference/sqa_strategy_kbs.md +154 -0
  44. data/docs/api-reference/sqa_strategy_macd.md +26 -0
  45. data/docs/api-reference/sqa_strategy_mp.md +41 -0
  46. data/docs/api-reference/sqa_strategy_mr.md +41 -0
  47. data/docs/api-reference/sqa_strategy_random.md +41 -0
  48. data/docs/api-reference/sqa_strategy_rsi.md +41 -0
  49. data/docs/api-reference/sqa_strategy_sma.md +41 -0
  50. data/docs/api-reference/sqa_strategy_stochastic.md +26 -0
  51. data/docs/api-reference/sqa_strategy_volumebreakout.md +26 -0
  52. data/docs/api-reference/sqa_strategygenerator.md +298 -0
  53. data/docs/api-reference/sqa_strategygenerator_pattern.md +264 -0
  54. data/docs/api-reference/sqa_strategygenerator_patterncontext.md +326 -0
  55. data/docs/api-reference/sqa_strategygenerator_profitablepoint.md +424 -0
  56. data/docs/api-reference/sqa_stream.md +256 -0
  57. data/docs/api-reference/sqa_ticker.md +175 -0
  58. data/docs/api-reference/string.md +135 -0
  59. data/docs/assets/images/advanced-workflow.svg +89 -0
  60. data/docs/assets/images/architecture.svg +107 -0
  61. data/docs/assets/images/data-flow.svg +138 -0
  62. data/docs/assets/images/getting-started-workflow.svg +88 -0
  63. data/docs/assets/images/strategy-flow.svg +78 -0
  64. data/docs/assets/images/system-architecture.svg +150 -0
  65. data/docs/concepts/index.md +292 -19
  66. data/docs/file_formats.md +250 -0
  67. data/docs/getting-started/index.md +1 -14
  68. data/docs/index.md +26 -23
  69. data/docs/llms.txt +109 -0
  70. data/docs/strategies/kbs.md +15 -14
  71. data/docs/strategy.md +381 -3
  72. data/docs/terms_of_use.md +1 -1
  73. data/examples/README.md +10 -0
  74. data/lib/api/alpha_vantage_api.rb +3 -7
  75. data/lib/sqa/backtest.rb +32 -0
  76. data/lib/sqa/config.rb +109 -28
  77. data/lib/sqa/data_frame/data.rb +13 -1
  78. data/lib/sqa/data_frame.rb +193 -26
  79. data/lib/sqa/errors.rb +79 -17
  80. data/lib/sqa/init.rb +70 -15
  81. data/lib/sqa/pattern_matcher.rb +4 -4
  82. data/lib/sqa/portfolio.rb +55 -1
  83. data/lib/sqa/sector_analyzer.rb +3 -11
  84. data/lib/sqa/stock.rb +180 -15
  85. data/lib/sqa/strategy.rb +62 -4
  86. data/lib/sqa/ticker.rb +106 -48
  87. data/lib/sqa/version.rb +1 -1
  88. data/lib/sqa.rb +4 -4
  89. data/mkdocs.yml +69 -81
  90. metadata +89 -21
  91. data/docs/README.md +0 -43
  92. data/examples/sinatra_app/Gemfile +0 -42
  93. data/examples/sinatra_app/Gemfile.lock +0 -268
  94. data/examples/sinatra_app/QUICKSTART.md +0 -169
  95. data/examples/sinatra_app/README.md +0 -471
  96. data/examples/sinatra_app/RUNNING_WITHOUT_TALIB.md +0 -90
  97. data/examples/sinatra_app/TROUBLESHOOTING.md +0 -95
  98. data/examples/sinatra_app/app.rb +0 -404
  99. data/examples/sinatra_app/config.ru +0 -5
  100. data/examples/sinatra_app/public/css/style.css +0 -723
  101. data/examples/sinatra_app/public/debug_macd.html +0 -82
  102. data/examples/sinatra_app/public/js/app.js +0 -107
  103. data/examples/sinatra_app/start.sh +0 -53
  104. data/examples/sinatra_app/views/analyze.erb +0 -306
  105. data/examples/sinatra_app/views/backtest.erb +0 -325
  106. data/examples/sinatra_app/views/dashboard.erb +0 -831
  107. data/examples/sinatra_app/views/error.erb +0 -58
  108. data/examples/sinatra_app/views/index.erb +0 -118
  109. data/examples/sinatra_app/views/layout.erb +0 -61
  110. data/examples/sinatra_app/views/portfolio.erb +0 -43
@@ -0,0 +1,779 @@
1
+ # 📦 SQA::DataFrame
2
+
3
+ !!! note "Description"
4
+ High-performance DataFrame wrapper around Polars for time series data manipulation.
5
+ Provides convenience methods for stock market data while leveraging Polars' Rust-backed
6
+ performance for vectorized operations.
7
+
8
+ !!! abstract "Source Information"
9
+ **Defined in:** [`lib/sqa/data_frame.rb:28`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L28)
10
+
11
+ **Inherits from:** `Object`
12
+
13
+ ## 🏭 Class Methods
14
+
15
+ ### `.is_date?(value)`
16
+
17
+ Checks if a value appears to be a date string.
18
+
19
+ !!! info "Parameters"
20
+
21
+ | Name | Type | Description |
22
+ |------|------|-------------|
23
+ | `value` | `Object` | Value to check |
24
+ !!! success "Returns"
25
+
26
+ **Type:** `Boolean`
27
+
28
+
29
+
30
+ true if value matches YYYY-MM-DD format
31
+
32
+ ??? info "Source Location"
33
+ [`lib/sqa/data_frame.rb:246`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L246)
34
+
35
+ ---
36
+
37
+ ### `.load(source:, transformers: = {}, mapping: = {})`
38
+
39
+ Load a DataFrame from a file source
40
+ This is the primary method for loading persisted DataFrames
41
+
42
+ Note: For cached CSV files, transformers and mapping should typically be empty
43
+ since transformations were already applied when the data was first fetched.
44
+ We only apply them if the CSV has old-format column names that need migration.
45
+
46
+ !!! info "Parameters"
47
+
48
+ | Name | Type | Description |
49
+ |------|------|-------------|
50
+ | `source` | `String, Pathname` | Path to CSV file |
51
+ | `transformers` | `Hash` | Column transformations to apply (usually not needed for cached data) |
52
+ | `mapping` | `Hash` | Column name mappings (usually not needed for cached data) |
53
+ !!! success "Returns"
54
+
55
+ **Type:** `SQA::DataFrame`
56
+
57
+
58
+
59
+ Loaded DataFrame
60
+
61
+ ??? info "Source Location"
62
+ [`lib/sqa/data_frame.rb:283`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L283)
63
+
64
+ ---
65
+
66
+ ### `.from_aofh(aofh, mapping: = {}, transformers: = {})`
67
+
68
+ Creates a DataFrame from an array of hashes.
69
+
70
+ !!! info "Parameters"
71
+
72
+ | Name | Type | Description |
73
+ |------|------|-------------|
74
+ | `aofh` | `Array<Hash>` | Array of hash records |
75
+ | `mapping` | `Hash` | Column name mappings to apply |
76
+ | `transformers` | `Hash` | Column transformers to apply |
77
+ !!! success "Returns"
78
+
79
+ **Type:** `SQA::DataFrame`
80
+
81
+
82
+
83
+ New DataFrame instance
84
+ !!! example "Usage Examples"
85
+
86
+ ```ruby
87
+ data = [{ "date" => "2024-01-01", "price" => 100.0 }]
88
+ df = SQA::DataFrame.from_aofh(data)
89
+ ```
90
+ ??? info "Source Location"
91
+ [`lib/sqa/data_frame.rb:302`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L302)
92
+
93
+ ---
94
+
95
+ ### `.from_csv_file(source, mapping: = {}, transformers: = {})`
96
+
97
+ Creates a DataFrame from a CSV file.
98
+
99
+ !!! info "Parameters"
100
+
101
+ | Name | Type | Description |
102
+ |------|------|-------------|
103
+ | `source` | `String, Pathname` | Path to CSV file |
104
+ | `mapping` | `Hash` | Column name mappings to apply |
105
+ | `transformers` | `Hash` | Column transformers to apply |
106
+ !!! success "Returns"
107
+
108
+ **Type:** `SQA::DataFrame`
109
+
110
+
111
+
112
+ New DataFrame instance
113
+
114
+ ??? info "Source Location"
115
+ [`lib/sqa/data_frame.rb:324`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L324)
116
+
117
+ ---
118
+
119
+ ### `.from_json_file(source, mapping: = {}, transformers: = {})`
120
+
121
+ Creates a DataFrame from a JSON file.
122
+
123
+ !!! info "Parameters"
124
+
125
+ | Name | Type | Description |
126
+ |------|------|-------------|
127
+ | `source` | `String, Pathname` | Path to JSON file containing array of objects |
128
+ | `mapping` | `Hash` | Column name mappings to apply |
129
+ | `transformers` | `Hash` | Column transformers to apply |
130
+ !!! success "Returns"
131
+
132
+ **Type:** `SQA::DataFrame`
133
+
134
+
135
+
136
+ New DataFrame instance
137
+
138
+ ??? info "Source Location"
139
+ [`lib/sqa/data_frame.rb:335`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L335)
140
+
141
+ ---
142
+
143
+ ### `.generate_mapping(keys)`
144
+
145
+ Generates a mapping of original keys to underscored keys.
146
+
147
+ !!! info "Parameters"
148
+
149
+ | Name | Type | Description |
150
+ |------|------|-------------|
151
+ | `keys` | `Array<String>` | Original key names |
152
+ !!! success "Returns"
153
+
154
+ **Type:** `Hash{String => Symbol}`
155
+
156
+
157
+
158
+ Mapping from original to underscored keys
159
+
160
+ ??? info "Source Location"
161
+ [`lib/sqa/data_frame.rb:344`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L344)
162
+
163
+ ---
164
+
165
+ ### `.underscore_key(key)`
166
+
167
+ Converts a key string to underscored snake_case format.
168
+
169
+ !!! info "Parameters"
170
+
171
+ | Name | Type | Description |
172
+ |------|------|-------------|
173
+ | `key` | `String` | Key to convert |
174
+ !!! success "Returns"
175
+
176
+ **Type:** `Symbol`
177
+
178
+
179
+
180
+ Underscored key as symbol
181
+ !!! example "Usage Examples"
182
+
183
+ ```ruby
184
+ underscore_key("closePrice") # => :close_price
185
+ underscore_key("Close Price") # => :close_price
186
+ ```
187
+ ??? info "Source Location"
188
+ [`lib/sqa/data_frame.rb:359`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L359)
189
+
190
+ ---
191
+
192
+ ### `.sanitize_key()`
193
+
194
+ Converts a key string to underscored snake_case format.
195
+
196
+ !!! info "Parameters"
197
+
198
+ | Name | Type | Description |
199
+ |------|------|-------------|
200
+ | `key` | `String` | Key to convert |
201
+ !!! success "Returns"
202
+
203
+ **Type:** `Symbol`
204
+
205
+
206
+
207
+ Underscored key as symbol
208
+ !!! example "Usage Examples"
209
+
210
+ ```ruby
211
+ underscore_key("closePrice") # => :close_price
212
+ underscore_key("Close Price") # => :close_price
213
+ ```
214
+ ??? info "Source Location"
215
+ [`lib/sqa/data_frame.rb:371`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L371)
216
+
217
+ ---
218
+
219
+ ### `.normalize_keys(hash, adapter_mapping: = {})`
220
+
221
+ Normalizes all keys in a hash to snake_case format.
222
+
223
+ !!! info "Parameters"
224
+
225
+ | Name | Type | Description |
226
+ |------|------|-------------|
227
+ | `hash` | `Hash` | Hash with keys to normalize |
228
+ | `adapter_mapping` | `Hash` | Optional pre-mapping to apply first |
229
+ !!! success "Returns"
230
+
231
+ **Type:** `Hash`
232
+
233
+
234
+
235
+ Hash with normalized keys
236
+
237
+ ??? info "Source Location"
238
+ [`lib/sqa/data_frame.rb:378`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L378)
239
+
240
+ ---
241
+
242
+ ### `.rename(hash, mapping)`
243
+
244
+ Renames keys in a hash according to a mapping.
245
+
246
+ !!! info "Parameters"
247
+
248
+ | Name | Type | Description |
249
+ |------|------|-------------|
250
+ | `hash` | `Hash` | Hash to modify |
251
+ | `mapping` | `Hash` | Old key to new key mapping |
252
+ !!! success "Returns"
253
+
254
+ **Type:** `Hash`
255
+
256
+
257
+
258
+ Modified hash
259
+
260
+ ??? info "Source Location"
261
+ [`lib/sqa/data_frame.rb:389`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L389)
262
+
263
+ ---
264
+
265
+ ### `.aofh_to_hofa(aofh, mapping: = {}, transformers: = {})`
266
+
267
+ Converts array of hashes to hash of arrays format.
268
+
269
+ !!! info "Parameters"
270
+
271
+ | Name | Type | Description |
272
+ |------|------|-------------|
273
+ | `aofh` | `Array<Hash>` | Array of hash records |
274
+ | `mapping` | `Hash` | Column name mappings (unused, for API compatibility) |
275
+ | `transformers` | `Hash` | Column transformers (unused, for API compatibility) |
276
+ !!! success "Returns"
277
+
278
+ **Type:** `Hash{String => Array}`
279
+
280
+
281
+
282
+ Hash with column names as keys and arrays as values
283
+
284
+ ??? info "Source Location"
285
+ [`lib/sqa/data_frame.rb:400`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L400)
286
+
287
+ ---
288
+
289
+ ## 🔨 Instance Methods
290
+
291
+ ### `#data()`
292
+
293
+
294
+ !!! success "Returns"
295
+
296
+ **Type:** `Polars::DataFrame`
297
+
298
+
299
+
300
+ The underlying Polars DataFrame
301
+
302
+ ??? info "Source Location"
303
+ [`lib/sqa/data_frame.rb:33`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L33)
304
+
305
+ ---
306
+
307
+ ### `#data=(value)`
308
+
309
+ Sets the attribute data
310
+
311
+ !!! info "Parameters"
312
+
313
+ | Name | Type | Description |
314
+ |------|------|-------------|
315
+ | `value` | `Any` | the value to set the attribute data to. |
316
+
317
+
318
+ ??? info "Source Location"
319
+ [`lib/sqa/data_frame.rb:33`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L33)
320
+
321
+ ---
322
+
323
+ ### `#initialize(raw_data = nil, mapping: = {}, transformers: = {})`
324
+
325
+ Creates a new DataFrame instance.
326
+
327
+ !!! info "Parameters"
328
+
329
+ | Name | Type | Description |
330
+ |------|------|-------------|
331
+ | `raw_data` | `Hash, Array, Polars::DataFrame, nil` | Initial data for the DataFrame |
332
+ | `mapping` | `Hash` | Column name mappings to apply (old_name => new_name) |
333
+ | `transformers` | `Hash` | Column transformers to apply (column => lambda) |
334
+ !!! success "Returns"
335
+
336
+ **Type:** `DataFrame`
337
+
338
+
339
+
340
+ a new instance of DataFrame
341
+ !!! example "Usage Examples"
342
+
343
+ ```ruby
344
+ df = SQA::DataFrame.new(data, mapping: { "Close" => "close_price" })
345
+ ```
346
+
347
+ ```ruby
348
+ df = SQA::DataFrame.new(data, transformers: { "price" => ->(v) { v.to_f } })
349
+ ```
350
+ ??? info "Source Location"
351
+ [`lib/sqa/data_frame.rb:47`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L47)
352
+
353
+ ---
354
+
355
+ ### `#apply_transformers!(transformers)`
356
+
357
+ Applies transformer functions to specified columns in place.
358
+
359
+ !!! info "Parameters"
360
+
361
+ | Name | Type | Description |
362
+ |------|------|-------------|
363
+ | `transformers` | `Hash{String, Symbol => Proc}` | Column name to transformer mapping |
364
+ !!! success "Returns"
365
+
366
+ **Type:** `void`
367
+
368
+
369
+ !!! example "Usage Examples"
370
+
371
+ ```ruby
372
+ df.apply_transformers!({ "price" => ->(v) { v.to_f }, "volume" => ->(v) { v.to_i } })
373
+ ```
374
+ ??? info "Source Location"
375
+ [`lib/sqa/data_frame.rb:65`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L65)
376
+
377
+ ---
378
+
379
+ ### `#rename_columns!(mapping)`
380
+
381
+ Renames columns according to the provided mapping in place.
382
+
383
+ !!! info "Parameters"
384
+
385
+ | Name | Type | Description |
386
+ |------|------|-------------|
387
+ | `mapping` | `Hash{String, Symbol => String}` | Old column name to new column name mapping |
388
+ !!! success "Returns"
389
+
390
+ **Type:** `void`
391
+
392
+
393
+ !!! example "Usage Examples"
394
+
395
+ ```ruby
396
+ df.rename_columns!({ "open" => "open_price", "close" => "close_price" })
397
+ ```
398
+ ??? info "Source Location"
399
+ [`lib/sqa/data_frame.rb:82`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L82)
400
+
401
+ ---
402
+
403
+ ### `#append!(other_df)`
404
+
405
+ Appends another DataFrame to this one in place.
406
+
407
+ !!! info "Parameters"
408
+
409
+ | Name | Type | Description |
410
+ |------|------|-------------|
411
+ | `other_df` | `SQA::DataFrame` | DataFrame to append |
412
+ !!! success "Returns"
413
+
414
+ **Type:** `void`
415
+
416
+
417
+ !!! example "Usage Examples"
418
+
419
+ ```ruby
420
+ df1.append!(df2)
421
+ ```
422
+ ??? info "Source Location"
423
+ [`lib/sqa/data_frame.rb:107`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L107)
424
+
425
+ ---
426
+
427
+ ### `#concat!()`
428
+
429
+ Appends another DataFrame to this one in place.
430
+
431
+ !!! info "Parameters"
432
+
433
+ | Name | Type | Description |
434
+ |------|------|-------------|
435
+ | `other_df` | `SQA::DataFrame` | DataFrame to append |
436
+ !!! success "Returns"
437
+
438
+ **Type:** `void`
439
+
440
+
441
+ !!! example "Usage Examples"
442
+
443
+ ```ruby
444
+ df1.append!(df2)
445
+ ```
446
+ ??? info "Source Location"
447
+ [`lib/sqa/data_frame.rb:124`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L124)
448
+
449
+ ---
450
+
451
+ ### `#concat_and_deduplicate!(other_df, sort_column: = "timestamp", descending: = false)`
452
+
453
+ Concatenate another DataFrame, remove duplicates, and sort
454
+ This is the preferred method for updating CSV data to prevent duplicates
455
+
456
+ NOTE: TA-Lib requires data in ascending (oldest-first) order. Using descending: true
457
+ will produce a warning and force ascending order to prevent silent calculation errors.
458
+
459
+ !!! info "Parameters"
460
+
461
+ | Name | Type | Description |
462
+ |------|------|-------------|
463
+ | `other_df` | `SQA::DataFrame` | DataFrame to append |
464
+ | `sort_column` | `String` | Column to use for deduplication and sorting (default: "timestamp") |
465
+ | `descending` | `Boolean` | Sort order - false for ascending (oldest first, TA-Lib compatible), true for descending |
466
+
467
+ !!! example "Usage Examples"
468
+
469
+ ```ruby
470
+ stock = SQA::Stock.new(ticker: 'AAPL')
471
+ df = stock.df
472
+ df.size # => 252
473
+
474
+ # Fetch recent data (may have overlapping dates)
475
+ new_df = SQA::DataFrame::AlphaVantage.recent('AAPL', from_date: Date.today - 7)
476
+ df.concat_and_deduplicate!(new_df)
477
+ # Duplicates removed, data sorted ascending (oldest first)
478
+ df.size # => 255 (only 3 new unique dates added)
479
+ ```
480
+
481
+ ```ruby
482
+ df.concat_and_deduplicate!(new_df) # Sorted ascending automatically
483
+ prices = df["adj_close_price"].to_a
484
+ rsi = SQAI.rsi(prices, period: 14) # Works correctly with ascending data
485
+ ```
486
+ ??? info "Source Location"
487
+ [`lib/sqa/data_frame.rb:135`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L135)
488
+
489
+ ---
490
+
491
+ ### `#columns()`
492
+
493
+ Returns the column names of the DataFrame.
494
+
495
+
496
+ !!! success "Returns"
497
+
498
+ **Type:** `Array<String>`
499
+
500
+
501
+
502
+ List of column names
503
+
504
+ ??? info "Source Location"
505
+ [`lib/sqa/data_frame.rb:159`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L159)
506
+
507
+ ---
508
+
509
+ ### `#keys()`
510
+
511
+ Returns the column names of the DataFrame.
512
+ Alias for {#columns}.
513
+
514
+
515
+ !!! success "Returns"
516
+
517
+ **Type:** `Array<String>`
518
+
519
+
520
+
521
+ List of column names
522
+
523
+ ??? info "Source Location"
524
+ [`lib/sqa/data_frame.rb:167`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L167)
525
+
526
+ ---
527
+
528
+ ### `#vectors()`
529
+
530
+ Returns the column names of the DataFrame.
531
+ Alias for {#columns}.
532
+
533
+
534
+ !!! success "Returns"
535
+
536
+ **Type:** `Array<String>`
537
+
538
+
539
+
540
+ List of column names
541
+
542
+ ??? info "Source Location"
543
+ [`lib/sqa/data_frame.rb:170`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L170)
544
+
545
+ ---
546
+
547
+ ### `#to_h()`
548
+
549
+ Converts the DataFrame to a Ruby Hash.
550
+
551
+
552
+ !!! success "Returns"
553
+
554
+ **Type:** `Hash{Symbol => Array}`
555
+
556
+
557
+
558
+ Hash with column names as keys and column data as arrays
559
+ !!! example "Usage Examples"
560
+
561
+ ```ruby
562
+ df.to_h # => { timestamp: ["2024-01-01", ...], close_price: [100.0, ...] }
563
+ ```
564
+ ??? info "Source Location"
565
+ [`lib/sqa/data_frame.rb:179`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L179)
566
+
567
+ ---
568
+
569
+ ### `#to_csv(path_to_file)`
570
+
571
+ Writes the DataFrame to a CSV file.
572
+
573
+ !!! info "Parameters"
574
+
575
+ | Name | Type | Description |
576
+ |------|------|-------------|
577
+ | `path_to_file` | `String, Pathname` | Path to output CSV file |
578
+ !!! success "Returns"
579
+
580
+ **Type:** `void`
581
+
582
+
583
+ !!! example "Usage Examples"
584
+
585
+ ```ruby
586
+ stock = SQA::Stock.new(ticker: 'AAPL')
587
+ stock.df.to_csv('aapl_prices.csv')
588
+ ```
589
+
590
+ ```ruby
591
+ df.to_csv(Pathname.new('data/exports/prices.csv'))
592
+ ```
593
+ ??? info "Source Location"
594
+ [`lib/sqa/data_frame.rb:187`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L187)
595
+
596
+ ---
597
+
598
+ ### `#size()`
599
+
600
+ Returns the number of rows in the DataFrame.
601
+
602
+
603
+ !!! success "Returns"
604
+
605
+ **Type:** `Integer`
606
+
607
+
608
+
609
+ Row count
610
+
611
+ ??? info "Source Location"
612
+ [`lib/sqa/data_frame.rb:194`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L194)
613
+
614
+ ---
615
+
616
+ ### `#nrows()`
617
+
618
+ Returns the number of rows in the DataFrame.
619
+
620
+
621
+ !!! success "Returns"
622
+
623
+ **Type:** `Integer`
624
+
625
+
626
+
627
+ Row count
628
+
629
+ ??? info "Source Location"
630
+ [`lib/sqa/data_frame.rb:197`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L197)
631
+
632
+ ---
633
+
634
+ ### `#length()`
635
+
636
+ Returns the number of rows in the DataFrame.
637
+
638
+
639
+ !!! success "Returns"
640
+
641
+ **Type:** `Integer`
642
+
643
+
644
+
645
+ Row count
646
+
647
+ ??? info "Source Location"
648
+ [`lib/sqa/data_frame.rb:198`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L198)
649
+
650
+ ---
651
+
652
+ ### `#ncols()`
653
+
654
+ Returns the number of columns in the DataFrame.
655
+
656
+
657
+ !!! success "Returns"
658
+
659
+ **Type:** `Integer`
660
+
661
+
662
+
663
+ Column count
664
+
665
+ ??? info "Source Location"
666
+ [`lib/sqa/data_frame.rb:203`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L203)
667
+
668
+ ---
669
+
670
+ ### `#fpl(column: = 'adj_close_price', fpop: = 14)`
671
+
672
+ FPL Analysis - Calculate Future Period Loss/Profit
673
+
674
+ !!! info "Parameters"
675
+
676
+ | Name | Type | Description |
677
+ |------|------|-------------|
678
+ | `column` | `String, Symbol` | Column name containing prices (default: "adj_close_price") |
679
+ | `fpop` | `Integer` | Future Period of Performance (days to look ahead) |
680
+ !!! success "Returns"
681
+
682
+ **Type:** `Array<Array<Float, Float>>`
683
+
684
+
685
+
686
+ Array of [min_delta, max_delta] pairs
687
+ !!! example "Usage Examples"
688
+
689
+ ```ruby
690
+ stock = SQA::Stock.new(ticker: 'AAPL')
691
+ fpl_data = stock.df.fpl(fpop: 10)
692
+ ```
693
+ ??? info "Source Location"
694
+ [`lib/sqa/data_frame.rb:218`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L218)
695
+
696
+ ---
697
+
698
+ ### `#fpl_analysis(column: = 'adj_close_price', fpop: = 14)`
699
+
700
+ FPL Analysis with risk metrics and classification
701
+
702
+ !!! info "Parameters"
703
+
704
+ | Name | Type | Description |
705
+ |------|------|-------------|
706
+ | `column` | `String, Symbol` | Column name containing prices (default: "adj_close_price") |
707
+ | `fpop` | `Integer` | Future Period of Performance |
708
+ !!! success "Returns"
709
+
710
+ **Type:** `Array<Hash>`
711
+
712
+
713
+
714
+ Array of analysis hashes
715
+ !!! example "Usage Examples"
716
+
717
+ ```ruby
718
+ analysis = stock.df.fpl_analysis(fpop: 10)
719
+ analysis.first[:direction] # => :UP, :DOWN, :UNCERTAIN, or :FLAT
720
+ analysis.first[:magnitude] # => Average expected movement percentage
721
+ analysis.first[:risk] # => Volatility range
722
+ ```
723
+ ??? info "Source Location"
724
+ [`lib/sqa/data_frame.rb:236`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L236)
725
+
726
+ ---
727
+
728
+ ### `#method_missing(method_name, *args, &block)`
729
+
730
+ Delegates unknown methods to the underlying Polars DataFrame.
731
+ This allows direct access to Polars methods like filter, select, etc.
732
+
733
+ !!! info "Parameters"
734
+
735
+ | Name | Type | Description |
736
+ |------|------|-------------|
737
+ | `method_name` | `Symbol` | Method name being called |
738
+ | `args` | `Array` | Method arguments |
739
+ | `block` | `Proc` | Optional block |
740
+ !!! success "Returns"
741
+
742
+ **Type:** `Object`
743
+
744
+
745
+
746
+ Result from Polars DataFrame method
747
+
748
+ ??? info "Source Location"
749
+ [`lib/sqa/data_frame.rb:257`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L257)
750
+
751
+ ---
752
+
753
+ ### `#respond_to_missing?(method_name, include_private = false)`
754
+
755
+ Checks if the DataFrame responds to a method.
756
+
757
+ !!! info "Parameters"
758
+
759
+ | Name | Type | Description |
760
+ |------|------|-------------|
761
+ | `method_name` | `Symbol` | Method name to check |
762
+ | `include_private` | `Boolean` | Include private methods |
763
+ !!! success "Returns"
764
+
765
+ **Type:** `Boolean`
766
+
767
+
768
+
769
+ true if method is available
770
+
771
+ ??? info "Source Location"
772
+ [`lib/sqa/data_frame.rb:267`](https://github.com/madbomber/sqa/blob/main/lib/sqa/data_frame.rb#L267)
773
+
774
+ ---
775
+
776
+ ## 📝 Attributes
777
+
778
+ ### 🔄 `data` <small>read/write</small>
779
+