sqa-tai 0.1.2 → 0.4.0
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/.claude/settings.local.json +33 -0
- data/.envrc +6 -1
- data/.loki +10 -0
- data/.quality/reek_baseline.txt +0 -0
- data/.rubocop.yml +221 -0
- data/CHANGELOG.md +30 -2
- data/Gemfile +0 -8
- data/Gemfile.local +19 -0
- data/README.md +9 -10
- data/Rakefile +163 -7
- data/lib/sqa/tai/cycle_indicators.rb +7 -19
- data/lib/sqa/tai/help/resource.rb +4 -3
- data/lib/sqa/tai/help.rb +16 -11
- data/lib/sqa/tai/momentum_indicators.rb +51 -110
- data/lib/sqa/tai/native.rb +497 -0
- data/lib/sqa/tai/overlap_studies.rb +13 -22
- data/lib/sqa/tai/pattern_recognition.rb +67 -61
- data/lib/sqa/tai/price_transform.rb +10 -4
- data/lib/sqa/tai/statistical_functions.rb +21 -21
- data/lib/sqa/tai/version.rb +1 -1
- data/lib/sqa/tai/volatility_indicators.rb +27 -31
- data/lib/sqa/tai/volume_indicators.rb +9 -3
- data/lib/sqa/tai.rb +38 -29
- data/mkdocs.yml +153 -15
- data/sqa-tai.gemspec +54 -0
- metadata +131 -13
- data/Gemfile.lock +0 -71
- data/lib/extensions/ta_lib_ffi.rb +0 -154
|
@@ -25,11 +25,12 @@ module SQA
|
|
|
25
25
|
# Opens the documentation URL in the default browser
|
|
26
26
|
# @return [Resource] self for chaining
|
|
27
27
|
def open
|
|
28
|
-
|
|
28
|
+
case RbConfig::CONFIG["host_os"]
|
|
29
|
+
when /darwin/
|
|
29
30
|
system("open", @url)
|
|
30
|
-
|
|
31
|
+
when /linux|bsd/
|
|
31
32
|
system("xdg-open", @url)
|
|
32
|
-
|
|
33
|
+
when /mswin|mingw|cygwin/
|
|
33
34
|
system("start", @url)
|
|
34
35
|
else
|
|
35
36
|
warn "Unable to open browser on this platform"
|
data/lib/sqa/tai/help.rb
CHANGED
|
@@ -19,19 +19,24 @@ module SQA
|
|
|
19
19
|
private
|
|
20
20
|
|
|
21
21
|
def load_indicators
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
data = JSON.parse(json_data)
|
|
25
|
-
|
|
26
|
-
# Convert to symbol keys for Ruby idioms
|
|
27
|
-
data.transform_keys(&:to_sym).transform_values do |meta|
|
|
28
|
-
{
|
|
29
|
-
name: meta["name"],
|
|
30
|
-
category: meta["category"].to_sym,
|
|
31
|
-
path: meta["path"]
|
|
32
|
-
}
|
|
22
|
+
read_indicators_json.transform_keys(&:to_sym).transform_values do |meta|
|
|
23
|
+
symbolize_indicator_meta(meta)
|
|
33
24
|
end
|
|
34
25
|
end
|
|
26
|
+
|
|
27
|
+
def read_indicators_json
|
|
28
|
+
json_path = File.join(__dir__, "help", "data.json")
|
|
29
|
+
JSON.parse(File.read(json_path))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Convert a single indicator's metadata hash to symbol keys/values for Ruby idioms
|
|
33
|
+
def symbolize_indicator_meta(meta)
|
|
34
|
+
{
|
|
35
|
+
name: meta["name"],
|
|
36
|
+
category: meta["category"].to_sym,
|
|
37
|
+
path: meta["path"]
|
|
38
|
+
}
|
|
39
|
+
end
|
|
35
40
|
end
|
|
36
41
|
end
|
|
37
42
|
end
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
module SQA
|
|
4
4
|
module TAI
|
|
5
5
|
# Momentum Indicators
|
|
6
|
+
#
|
|
7
|
+
# :reek:DataClump and :reek:LongParameterList -- these methods
|
|
8
|
+
# intentionally mirror TA-Lib's own C function signatures (separate
|
|
9
|
+
# high/low/close/period args, up to 7 for macdext); bundling them into
|
|
10
|
+
# a value object would be a breaking public API change for this gem
|
|
11
|
+
# and its downstream consumers (sqa, sqa-cli, sqa-advisor) and their
|
|
12
|
+
# 133 published indicator doc pages.
|
|
6
13
|
module MomentumIndicators
|
|
7
14
|
# Relative Strength Index
|
|
8
15
|
# @param prices [Array<Float>] Array of prices
|
|
@@ -13,7 +20,7 @@ module SQA
|
|
|
13
20
|
validate_prices!(prices)
|
|
14
21
|
validate_period!(period, prices.size)
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
Native.rsi(prices, time_period: period)
|
|
17
24
|
end
|
|
18
25
|
|
|
19
26
|
# Intraday Momentum Index
|
|
@@ -27,7 +34,7 @@ module SQA
|
|
|
27
34
|
validate_prices!(close_prices)
|
|
28
35
|
validate_period!(period, [open_prices.size, close_prices.size].min)
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
Native.imi([open_prices, close_prices], time_period: period)
|
|
31
38
|
end
|
|
32
39
|
|
|
33
40
|
# Moving Average Convergence/Divergence
|
|
@@ -40,19 +47,7 @@ module SQA
|
|
|
40
47
|
check_available!
|
|
41
48
|
validate_prices!(prices)
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
prices,
|
|
45
|
-
fast_period: fast_period,
|
|
46
|
-
slow_period: slow_period,
|
|
47
|
-
signal_period: signal_period
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
# Handle hash return format from newer ta_lib_ffi versions
|
|
51
|
-
if result.is_a?(Hash)
|
|
52
|
-
[result[:macd], result[:macd_signal], result[:macd_hist]]
|
|
53
|
-
else
|
|
54
|
-
result
|
|
55
|
-
end
|
|
50
|
+
Native.macd(prices, fast_period:, slow_period:, signal_period:).values_at(:macd, :macd_signal, :macd_hist)
|
|
56
51
|
end
|
|
57
52
|
|
|
58
53
|
# Stochastic Oscillator
|
|
@@ -69,21 +64,8 @@ module SQA
|
|
|
69
64
|
validate_prices!(low)
|
|
70
65
|
validate_prices!(close)
|
|
71
66
|
|
|
72
|
-
result =
|
|
73
|
-
|
|
74
|
-
low,
|
|
75
|
-
close,
|
|
76
|
-
fastk_period: fastk_period,
|
|
77
|
-
slowk_period: slowk_period,
|
|
78
|
-
slowd_period: slowd_period
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
# Handle hash return format from newer ta_lib_ffi versions
|
|
82
|
-
if result.is_a?(Hash)
|
|
83
|
-
[result[:slow_k], result[:slow_d]]
|
|
84
|
-
else
|
|
85
|
-
result
|
|
86
|
-
end
|
|
67
|
+
result = Native.stoch([high, low, close], fastk_period:, slowk_period:, slowd_period:)
|
|
68
|
+
[result[:slow_k], result[:slow_d]]
|
|
87
69
|
end
|
|
88
70
|
|
|
89
71
|
# Momentum
|
|
@@ -95,7 +77,7 @@ module SQA
|
|
|
95
77
|
validate_prices!(prices)
|
|
96
78
|
validate_period!(period, prices.size)
|
|
97
79
|
|
|
98
|
-
|
|
80
|
+
Native.mom(prices, time_period: period)
|
|
99
81
|
end
|
|
100
82
|
|
|
101
83
|
# Commodity Channel Index
|
|
@@ -110,7 +92,7 @@ module SQA
|
|
|
110
92
|
validate_prices!(low)
|
|
111
93
|
validate_prices!(close)
|
|
112
94
|
|
|
113
|
-
|
|
95
|
+
Native.cci([high, low, close], time_period: period)
|
|
114
96
|
end
|
|
115
97
|
|
|
116
98
|
# Williams' %R
|
|
@@ -125,7 +107,7 @@ module SQA
|
|
|
125
107
|
validate_prices!(low)
|
|
126
108
|
validate_prices!(close)
|
|
127
109
|
|
|
128
|
-
|
|
110
|
+
Native.willr([high, low, close], time_period: period)
|
|
129
111
|
end
|
|
130
112
|
|
|
131
113
|
# Rate of Change
|
|
@@ -137,7 +119,7 @@ module SQA
|
|
|
137
119
|
validate_prices!(prices)
|
|
138
120
|
validate_period!(period, prices.size)
|
|
139
121
|
|
|
140
|
-
|
|
122
|
+
Native.roc(prices, time_period: period)
|
|
141
123
|
end
|
|
142
124
|
|
|
143
125
|
# Rate of Change Percentage
|
|
@@ -149,7 +131,7 @@ module SQA
|
|
|
149
131
|
validate_prices!(prices)
|
|
150
132
|
validate_period!(period, prices.size)
|
|
151
133
|
|
|
152
|
-
|
|
134
|
+
Native.rocp(prices, time_period: period)
|
|
153
135
|
end
|
|
154
136
|
|
|
155
137
|
# Rate of Change Ratio
|
|
@@ -161,7 +143,7 @@ module SQA
|
|
|
161
143
|
validate_prices!(prices)
|
|
162
144
|
validate_period!(period, prices.size)
|
|
163
145
|
|
|
164
|
-
|
|
146
|
+
Native.rocr(prices, time_period: period)
|
|
165
147
|
end
|
|
166
148
|
|
|
167
149
|
# Percentage Price Oscillator
|
|
@@ -174,7 +156,7 @@ module SQA
|
|
|
174
156
|
check_available!
|
|
175
157
|
validate_prices!(prices)
|
|
176
158
|
|
|
177
|
-
|
|
159
|
+
Native.ppo(prices, fast_period:, slow_period:, ma_type:)
|
|
178
160
|
end
|
|
179
161
|
|
|
180
162
|
# Average Directional Movement Index
|
|
@@ -189,7 +171,7 @@ module SQA
|
|
|
189
171
|
validate_prices!(low)
|
|
190
172
|
validate_prices!(close)
|
|
191
173
|
|
|
192
|
-
|
|
174
|
+
Native.adx([high, low, close], time_period: period)
|
|
193
175
|
end
|
|
194
176
|
|
|
195
177
|
# Average Directional Movement Index Rating
|
|
@@ -204,7 +186,7 @@ module SQA
|
|
|
204
186
|
validate_prices!(low)
|
|
205
187
|
validate_prices!(close)
|
|
206
188
|
|
|
207
|
-
|
|
189
|
+
Native.adxr([high, low, close], time_period: period)
|
|
208
190
|
end
|
|
209
191
|
|
|
210
192
|
# Absolute Price Oscillator
|
|
@@ -217,7 +199,7 @@ module SQA
|
|
|
217
199
|
check_available!
|
|
218
200
|
validate_prices!(prices)
|
|
219
201
|
|
|
220
|
-
|
|
202
|
+
Native.apo(prices, fast_period:, slow_period:, ma_type:)
|
|
221
203
|
end
|
|
222
204
|
|
|
223
205
|
# Aroon
|
|
@@ -230,14 +212,8 @@ module SQA
|
|
|
230
212
|
validate_prices!(high)
|
|
231
213
|
validate_prices!(low)
|
|
232
214
|
|
|
233
|
-
result =
|
|
234
|
-
|
|
235
|
-
# Handle hash return format from newer ta_lib_ffi versions
|
|
236
|
-
if result.is_a?(Hash)
|
|
237
|
-
[result[:aroon_down], result[:aroon_up]]
|
|
238
|
-
else
|
|
239
|
-
result
|
|
240
|
-
end
|
|
215
|
+
result = Native.aroon([high, low], time_period: period)
|
|
216
|
+
[result[:aroon_down], result[:aroon_up]]
|
|
241
217
|
end
|
|
242
218
|
|
|
243
219
|
# Aroon Oscillator
|
|
@@ -250,7 +226,7 @@ module SQA
|
|
|
250
226
|
validate_prices!(high)
|
|
251
227
|
validate_prices!(low)
|
|
252
228
|
|
|
253
|
-
|
|
229
|
+
Native.aroonosc([high, low], time_period: period)
|
|
254
230
|
end
|
|
255
231
|
|
|
256
232
|
# Balance of Power
|
|
@@ -266,7 +242,7 @@ module SQA
|
|
|
266
242
|
validate_prices!(low)
|
|
267
243
|
validate_prices!(close)
|
|
268
244
|
|
|
269
|
-
|
|
245
|
+
Native.bop([open, high, low, close])
|
|
270
246
|
end
|
|
271
247
|
|
|
272
248
|
# Chande Momentum Oscillator
|
|
@@ -278,7 +254,7 @@ module SQA
|
|
|
278
254
|
validate_prices!(prices)
|
|
279
255
|
validate_period!(period, prices.size)
|
|
280
256
|
|
|
281
|
-
|
|
257
|
+
Native.cmo(prices, time_period: period)
|
|
282
258
|
end
|
|
283
259
|
|
|
284
260
|
# Directional Movement Index
|
|
@@ -293,7 +269,7 @@ module SQA
|
|
|
293
269
|
validate_prices!(low)
|
|
294
270
|
validate_prices!(close)
|
|
295
271
|
|
|
296
|
-
|
|
272
|
+
Native.dx([high, low, close], time_period: period)
|
|
297
273
|
end
|
|
298
274
|
|
|
299
275
|
# MACD with Controllable MA Type
|
|
@@ -309,22 +285,15 @@ module SQA
|
|
|
309
285
|
check_available!
|
|
310
286
|
validate_prices!(prices)
|
|
311
287
|
|
|
312
|
-
|
|
288
|
+
Native.macdext(
|
|
313
289
|
prices,
|
|
314
|
-
fast_period
|
|
315
|
-
fast_ma_type
|
|
316
|
-
slow_period
|
|
317
|
-
slow_ma_type
|
|
318
|
-
signal_period
|
|
319
|
-
signal_ma_type:
|
|
320
|
-
)
|
|
321
|
-
|
|
322
|
-
# Handle hash return format from newer ta_lib_ffi versions
|
|
323
|
-
if result.is_a?(Hash)
|
|
324
|
-
[result[:macd], result[:macd_signal], result[:macd_hist]]
|
|
325
|
-
else
|
|
326
|
-
result
|
|
327
|
-
end
|
|
290
|
+
fast_period:,
|
|
291
|
+
fast_ma_type:,
|
|
292
|
+
slow_period:,
|
|
293
|
+
slow_ma_type:,
|
|
294
|
+
signal_period:,
|
|
295
|
+
signal_ma_type:
|
|
296
|
+
).values_at(:macd, :macd_signal, :macd_hist)
|
|
328
297
|
end
|
|
329
298
|
|
|
330
299
|
# MACD Fix 12/26
|
|
@@ -335,14 +304,7 @@ module SQA
|
|
|
335
304
|
check_available!
|
|
336
305
|
validate_prices!(prices)
|
|
337
306
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
# Handle hash return format from newer ta_lib_ffi versions
|
|
341
|
-
if result.is_a?(Hash)
|
|
342
|
-
[result[:macd], result[:macd_signal], result[:macd_hist]]
|
|
343
|
-
else
|
|
344
|
-
result
|
|
345
|
-
end
|
|
307
|
+
Native.macdfix(prices, signal_period:).values_at(:macd, :macd_signal, :macd_hist)
|
|
346
308
|
end
|
|
347
309
|
|
|
348
310
|
# Money Flow Index
|
|
@@ -359,7 +321,7 @@ module SQA
|
|
|
359
321
|
validate_prices!(close)
|
|
360
322
|
validate_prices!(volume)
|
|
361
323
|
|
|
362
|
-
|
|
324
|
+
Native.mfi([high, low, close, volume], time_period: period)
|
|
363
325
|
end
|
|
364
326
|
|
|
365
327
|
# Minus Directional Indicator
|
|
@@ -374,7 +336,7 @@ module SQA
|
|
|
374
336
|
validate_prices!(low)
|
|
375
337
|
validate_prices!(close)
|
|
376
338
|
|
|
377
|
-
|
|
339
|
+
Native.minus_di([high, low, close], time_period: period)
|
|
378
340
|
end
|
|
379
341
|
|
|
380
342
|
# Minus Directional Movement
|
|
@@ -387,7 +349,7 @@ module SQA
|
|
|
387
349
|
validate_prices!(high)
|
|
388
350
|
validate_prices!(low)
|
|
389
351
|
|
|
390
|
-
|
|
352
|
+
Native.minus_dm([high, low], time_period: period)
|
|
391
353
|
end
|
|
392
354
|
|
|
393
355
|
# Plus Directional Indicator
|
|
@@ -402,7 +364,7 @@ module SQA
|
|
|
402
364
|
validate_prices!(low)
|
|
403
365
|
validate_prices!(close)
|
|
404
366
|
|
|
405
|
-
|
|
367
|
+
Native.plus_di([high, low, close], time_period: period)
|
|
406
368
|
end
|
|
407
369
|
|
|
408
370
|
# Plus Directional Movement
|
|
@@ -415,19 +377,21 @@ module SQA
|
|
|
415
377
|
validate_prices!(high)
|
|
416
378
|
validate_prices!(low)
|
|
417
379
|
|
|
418
|
-
|
|
380
|
+
Native.plus_dm([high, low], time_period: period)
|
|
419
381
|
end
|
|
420
382
|
|
|
421
383
|
# Rate of Change Ratio 100 scale
|
|
422
384
|
# @param prices [Array<Float>] Array of prices
|
|
423
385
|
# @param period [Integer] Time period (default: 10)
|
|
424
386
|
# @return [Array<Float>] ROCR100 values
|
|
387
|
+
# :reek:UncommunicativeMethodName -- ROCR100 is TA-Lib's own
|
|
388
|
+
# canonical indicator name; renaming it breaks the public API.
|
|
425
389
|
def rocr100(prices, period: 10)
|
|
426
390
|
check_available!
|
|
427
391
|
validate_prices!(prices)
|
|
428
392
|
validate_period!(period, prices.size)
|
|
429
393
|
|
|
430
|
-
|
|
394
|
+
Native.rocr100(prices, time_period: period)
|
|
431
395
|
end
|
|
432
396
|
|
|
433
397
|
# Stochastic Fast
|
|
@@ -443,20 +407,8 @@ module SQA
|
|
|
443
407
|
validate_prices!(low)
|
|
444
408
|
validate_prices!(close)
|
|
445
409
|
|
|
446
|
-
result =
|
|
447
|
-
|
|
448
|
-
low,
|
|
449
|
-
close,
|
|
450
|
-
fastk_period: fastk_period,
|
|
451
|
-
fastd_period: fastd_period
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
# Handle hash return format from newer ta_lib_ffi versions
|
|
455
|
-
if result.is_a?(Hash)
|
|
456
|
-
[result[:fast_k], result[:fast_d]]
|
|
457
|
-
else
|
|
458
|
-
result
|
|
459
|
-
end
|
|
410
|
+
result = Native.stochf([high, low, close], fastk_period:, fastd_period:)
|
|
411
|
+
[result[:fast_k], result[:fast_d]]
|
|
460
412
|
end
|
|
461
413
|
|
|
462
414
|
# Stochastic RSI
|
|
@@ -469,19 +421,8 @@ module SQA
|
|
|
469
421
|
check_available!
|
|
470
422
|
validate_prices!(prices)
|
|
471
423
|
|
|
472
|
-
result =
|
|
473
|
-
|
|
474
|
-
time_period: period,
|
|
475
|
-
fastk_period: fastk_period,
|
|
476
|
-
fastd_period: fastd_period
|
|
477
|
-
)
|
|
478
|
-
|
|
479
|
-
# Handle hash return format from newer ta_lib_ffi versions
|
|
480
|
-
if result.is_a?(Hash)
|
|
481
|
-
[result[:fast_k], result[:fast_d]]
|
|
482
|
-
else
|
|
483
|
-
result
|
|
484
|
-
end
|
|
424
|
+
result = Native.stochrsi(prices, time_period: period, fastk_period:, fastd_period:)
|
|
425
|
+
[result[:fast_k], result[:fast_d]]
|
|
485
426
|
end
|
|
486
427
|
|
|
487
428
|
# 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA
|
|
@@ -493,7 +434,7 @@ module SQA
|
|
|
493
434
|
validate_prices!(prices)
|
|
494
435
|
validate_period!(period, prices.size)
|
|
495
436
|
|
|
496
|
-
|
|
437
|
+
Native.trix(prices, time_period: period)
|
|
497
438
|
end
|
|
498
439
|
|
|
499
440
|
# Ultimate Oscillator
|
|
@@ -510,7 +451,7 @@ module SQA
|
|
|
510
451
|
validate_prices!(low)
|
|
511
452
|
validate_prices!(close)
|
|
512
453
|
|
|
513
|
-
|
|
454
|
+
Native.ultosc([high, low, close], time_period1: period1, time_period2: period2, time_period3: period3)
|
|
514
455
|
end
|
|
515
456
|
end
|
|
516
457
|
end
|