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.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqa-tai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dewayne VanHoozer
@@ -10,19 +10,33 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: ta_lib_ffi
13
+ name: fiddle
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '0.3'
18
+ version: '0'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - "~>"
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: myway_config
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
24
38
  - !ruby/object:Gem::Version
25
- version: '0.3'
39
+ version: '0'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: bundler
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -93,6 +107,20 @@ dependencies:
93
107
  - - ">="
94
108
  - !ruby/object:Gem::Version
95
109
  version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: debug
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
96
124
  - !ruby/object:Gem::Dependency
97
125
  name: debug_me
98
126
  requirement: !ruby/object:Gem::Requirement
@@ -107,30 +135,118 @@ dependencies:
107
135
  - - ">="
108
136
  - !ruby/object:Gem::Version
109
137
  version: '0'
110
- description: An SQA-specific wrapper around the ta_lib_ffi gem which access to TA-Lib
111
- providing many technical analysis indicators for stock analysis. Part of the SQA
112
- (Simple Qualitative Analysis) ecosystem.
138
+ - !ruby/object:Gem::Dependency
139
+ name: flog
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ - !ruby/object:Gem::Dependency
153
+ name: flay
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ type: :development
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ - !ruby/object:Gem::Dependency
167
+ name: racc
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ type: :development
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ - !ruby/object:Gem::Dependency
181
+ name: reek
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: '0'
187
+ type: :development
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ - !ruby/object:Gem::Dependency
195
+ name: rubocop
196
+ requirement: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ type: :development
202
+ prerelease: false
203
+ version_requirements: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: '0'
208
+ - !ruby/object:Gem::Dependency
209
+ name: fasterer
210
+ requirement: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - ">="
213
+ - !ruby/object:Gem::Version
214
+ version: '0'
215
+ type: :development
216
+ prerelease: false
217
+ version_requirements: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - ">="
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
222
+ description: An SQA-specific Fiddle binding directly against the installed TA-Lib
223
+ C library, providing many technical analysis indicators for stock analysis. Part
224
+ of the SQA (Simple Qualitative Analysis) ecosystem.
113
225
  email:
114
226
  - dvanhoozer@gmail.com
115
227
  executables: []
116
228
  extensions: []
117
229
  extra_rdoc_files: []
118
230
  files:
231
+ - ".claude/settings.local.json"
119
232
  - ".envrc"
233
+ - ".loki"
234
+ - ".quality/reek_baseline.txt"
235
+ - ".rubocop.yml"
120
236
  - CHANGELOG.md
121
237
  - COMMITS.md
122
238
  - Gemfile
123
- - Gemfile.lock
239
+ - Gemfile.local
124
240
  - LICENSE
125
241
  - README.md
126
242
  - Rakefile
127
- - lib/extensions/ta_lib_ffi.rb
128
243
  - lib/sqa/tai.rb
129
244
  - lib/sqa/tai/cycle_indicators.rb
130
245
  - lib/sqa/tai/help.rb
131
246
  - lib/sqa/tai/help/data.json
132
247
  - lib/sqa/tai/help/resource.rb
133
248
  - lib/sqa/tai/momentum_indicators.rb
249
+ - lib/sqa/tai/native.rb
134
250
  - lib/sqa/tai/overlap_studies.rb
135
251
  - lib/sqa/tai/pattern_recognition.rb
136
252
  - lib/sqa/tai/price_transform.rb
@@ -139,6 +255,7 @@ files:
139
255
  - lib/sqa/tai/volatility_indicators.rb
140
256
  - lib/sqa/tai/volume_indicators.rb
141
257
  - mkdocs.yml
258
+ - sqa-tai.gemspec
142
259
  - ta-lib-license.md
143
260
  homepage: https://github.com/MadBomber/sqa-tai
144
261
  licenses:
@@ -149,6 +266,7 @@ metadata:
149
266
  source_code_uri: https://github.com/MadBomber/sqa-tai
150
267
  changelog_uri: https://github.com/MadBomber/sqa-tai/blob/main/CHANGELOG.md
151
268
  documentation_uri: https://madbomber.github.io/sqa-tai
269
+ rubygems_mfa_required: 'true'
152
270
  rdoc_options: []
153
271
  require_paths:
154
272
  - lib
@@ -156,14 +274,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
156
274
  requirements:
157
275
  - - ">="
158
276
  - !ruby/object:Gem::Version
159
- version: 3.1.0
277
+ version: 3.4.0
160
278
  required_rubygems_version: !ruby/object:Gem::Requirement
161
279
  requirements:
162
280
  - - ">="
163
281
  - !ruby/object:Gem::Version
164
282
  version: '0'
165
283
  requirements: []
166
- rubygems_version: 3.7.2
284
+ rubygems_version: 4.0.21
167
285
  specification_version: 4
168
286
  summary: SQA::TAI - Technical Analysis Indicators
169
287
  test_files: []
data/Gemfile.lock DELETED
@@ -1,71 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- sqa-tai (0.1.2)
5
- ta_lib_ffi (~> 0.3)
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- ansi (1.5.0)
11
- builder (3.3.0)
12
- date (3.5.0)
13
- debug (1.11.0)
14
- irb (~> 1.10)
15
- reline (>= 0.3.8)
16
- debug_me (1.1.1)
17
- docile (1.4.1)
18
- erb (5.1.3)
19
- fiddle (1.1.8)
20
- io-console (0.8.1)
21
- irb (1.15.3)
22
- pp (>= 0.6.0)
23
- rdoc (>= 4.0.0)
24
- reline (>= 0.4.2)
25
- minitest (5.26.0)
26
- minitest-reporters (1.7.1)
27
- ansi
28
- builder
29
- minitest (>= 5.0)
30
- ruby-progressbar
31
- pp (0.6.3)
32
- prettyprint
33
- prettyprint (0.2.0)
34
- psych (5.2.6)
35
- date
36
- stringio
37
- rake (13.3.1)
38
- rdoc (6.15.1)
39
- erb
40
- psych (>= 4.0.0)
41
- tsort
42
- reline (0.6.2)
43
- io-console (~> 0.5)
44
- ruby-progressbar (1.13.0)
45
- simplecov (0.22.0)
46
- docile (~> 1.1)
47
- simplecov-html (~> 0.11)
48
- simplecov_json_formatter (~> 0.1)
49
- simplecov-html (0.13.2)
50
- simplecov_json_formatter (0.1.4)
51
- stringio (3.1.7)
52
- ta_lib_ffi (0.3.0)
53
- fiddle (~> 1.1)
54
- tsort (0.2.0)
55
-
56
- PLATFORMS
57
- arm64-darwin-25
58
- ruby
59
-
60
- DEPENDENCIES
61
- bundler
62
- debug
63
- debug_me
64
- minitest (~> 5.0)
65
- minitest-reporters
66
- rake (~> 13.0, >= 0)
67
- simplecov
68
- sqa-tai!
69
-
70
- BUNDLED WITH
71
- 2.7.2
@@ -1,154 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Monkey patch for ta_lib_ffi 0.3.0 to fix multi-array parameter bug
4
- #
5
- # This patch fixes a critical bug in ta_lib_ffi 0.3.0 where functions requiring
6
- # multiple array parameters (high, low, close) fail with NoMethodError.
7
- #
8
- # Affected functions:
9
- # - All volatility indicators (ATR, NATR, SAR, TRANGE)
10
- # - All momentum indicators requiring OHLC (CCI, WILLR, ADX, STOCH)
11
- # - All volume indicators (OBV, AD, ADOSC)
12
- # - All candlestick pattern recognition functions (60+ patterns)
13
- #
14
- # This patch can be removed once ta_lib_ffi fixes the upstream bug.
15
-
16
- module TALibFFI
17
- class << self
18
- # Store original method to call it for non-price parameters
19
- alias_method :original_setup_input_parameters, :setup_input_parameters
20
-
21
- # Fixed version that properly handles multi-array Price inputs
22
- #
23
- # The original code assumes a 1-to-1 mapping between input_arrays and function
24
- # parameters, but TA_Input_Price parameters can consume multiple arrays.
25
- #
26
- # This fix:
27
- # 1. Tracks position in input_arrays with array_index
28
- # 2. For each function parameter, checks its type
29
- # 3. For TA_Input_Price, determines how many arrays are needed from flags
30
- # 4. Bundles those arrays together: [[high], [low], [close]]
31
- # 5. Passes the bundle to setup_price_inputs
32
- # 6. Advances array_index by the number of arrays consumed
33
- def setup_input_parameters(params_ptr, input_arrays, func_name)
34
- func_info = function_info_map[func_name]
35
- array_index = 0 # Track position in input_arrays
36
-
37
- func_info[:inputs].each_with_index do |input_info, param_index|
38
- case input_info["type"]
39
- when TA_PARAM_TYPE[:TA_Input_Price]
40
- # Price inputs consume multiple arrays based on required flags
41
- required_flags = extract_flags(input_info["flags"], :TA_InputFlags)
42
- num_arrays = required_flags.length
43
-
44
- # Collect the next num_arrays from input_arrays
45
- if array_index + num_arrays > input_arrays.length
46
- raise ArgumentError, "Function #{func_name} requires #{num_arrays} price arrays " \
47
- "(#{required_flags.join(', ')}), but only #{input_arrays.length - array_index} " \
48
- "provided at position #{array_index}"
49
- end
50
-
51
- price_arrays = input_arrays[array_index, num_arrays]
52
-
53
- # Pass the bundled arrays to set_input_parameter
54
- ret_code = set_input_parameter(params_ptr, param_index, price_arrays, input_info)
55
- check_ta_return_code(ret_code)
56
-
57
- array_index += num_arrays # Advance by number of arrays consumed
58
-
59
- when TA_PARAM_TYPE[:TA_Input_Real], TA_PARAM_TYPE[:TA_Input_Integer]
60
- # Single array inputs
61
- if array_index >= input_arrays.length
62
- raise ArgumentError, "Not enough input arrays for function #{func_name} " \
63
- "(expected array at index #{array_index}, but only #{input_arrays.length} provided)"
64
- end
65
-
66
- ret_code = set_input_parameter(params_ptr, param_index, input_arrays[array_index], input_info)
67
- check_ta_return_code(ret_code)
68
-
69
- array_index += 1 # Advance by one
70
-
71
- else
72
- # Unknown type - should not happen, but handle gracefully
73
- raise ArgumentError, "Unknown input type #{input_info['type']} for function #{func_name}"
74
- end
75
- end
76
-
77
- # Verify all arrays were consumed
78
- if array_index != input_arrays.length
79
- raise ArgumentError, "Function #{func_name} expected #{array_index} input arrays " \
80
- "but received #{input_arrays.length}"
81
- end
82
- end
83
-
84
- # Store original method
85
- alias_method :original_setup_price_inputs, :setup_price_inputs
86
-
87
- # Fixed version that handles bundled price arrays correctly
88
- #
89
- # After the setup_input_parameters fix, price_data is now guaranteed to be
90
- # an array of arrays: [[high_array], [low_array], [close_array]]
91
- def setup_price_inputs(params_ptr, index, price_data, flags)
92
- required_flags = extract_flags(flags, :TA_InputFlags)
93
- data_pointers = Array.new(6) { Fiddle::Pointer.malloc(0) }
94
-
95
- # price_data is now an array of arrays: [[high], [low], [close]]
96
- # Each element corresponds to one required flag
97
- required_flags.each_with_index do |flag, i|
98
- flag_index = TA_FLAGS[:TA_InputFlags].keys.index(flag)
99
-
100
- # price_data[i] now correctly returns an array (e.g., high, low, or close)
101
- if price_data[i].nil?
102
- raise ArgumentError, "Missing price array for flag #{flag} at index #{i}"
103
- end
104
-
105
- # Validate it's an array
106
- unless price_data[i].is_a?(Array)
107
- raise ArgumentError, "Expected array for flag #{flag} at index #{i}, " \
108
- "got #{price_data[i].class}"
109
- end
110
-
111
- data_pointers[flag_index] = prepare_double_array(price_data[i]) if required_flags.include?(flag)
112
- end
113
-
114
- TA_SetInputParamPricePtr(params_ptr, index, *data_pointers)
115
- end
116
-
117
- # Store original method
118
- alias_method :original_calculate_results, :calculate_results
119
-
120
- # Fixed version that correctly determines input_size for Price inputs
121
- #
122
- # The original code expects input_arrays[0][0].length for Price inputs,
123
- # which assumes format [[high], [low], [close]]. Our fix passes [high, low, close],
124
- # so we need to adjust this check.
125
- def calculate_results(params_ptr, input_arrays, func_name)
126
- func_info = function_info_map[func_name]
127
-
128
- # Determine input size based on input type
129
- input_size = if func_info[:inputs].first["type"] == TA_PARAM_TYPE[:TA_Input_Price]
130
- # For Price inputs, input_arrays is [high, low, close]
131
- # Get length from the first array
132
- input_arrays[0].length
133
- else
134
- # For other inputs, get length normally
135
- input_arrays[0].length
136
- end
137
-
138
- out_begin = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT)
139
- out_size = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT)
140
- output_arrays = setup_output_buffers(params_ptr, input_size, func_name)
141
-
142
- begin
143
- ret_code = TA_CallFunc(params_ptr, 0, input_size - 1, out_begin, out_size)
144
- check_ta_return_code(ret_code)
145
-
146
- actual_size = out_size[0, Fiddle::SIZEOF_INT].unpack1("l")
147
- format_output_results(output_arrays, actual_size, func_name)
148
- ensure
149
- out_begin.free
150
- out_size.free
151
- end
152
- end
153
- end
154
- end