ta_lib_ffi 0.1.0 → 0.3.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.
data/lib/ta_lib.rb DELETED
@@ -1,647 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fiddle"
4
- require "fiddle/import"
5
-
6
- # Ruby FFI wrapper for TA-Lib (Technical Analysis Library)
7
- module TALib
8
- VERSION = "0.1.0"
9
-
10
- extend Fiddle::Importer
11
-
12
- lib_path = case RUBY_PLATFORM
13
- when /darwin/
14
- brew_prefix = `brew --prefix`.chomp
15
- "#{brew_prefix}/lib/libta-lib.dylib"
16
- when /linux/
17
- "libta-lib.so"
18
- when /cygwin|mswin|mingw|bccwin|wince|emx/
19
- "C:/Program Files/TA-Lib/bin/ta-lib.dll"
20
- else
21
- raise "Unsupported platform"
22
- end
23
-
24
- dlload lib_path
25
-
26
- class TALibError < StandardError; end
27
-
28
- TA_SUCCESS = 0
29
- TA_LIB_NOT_INITIALIZE = 1
30
- TA_BAD_PARAM = 2
31
- TA_ALLOC_ERR = 3
32
- TA_GROUP_NOT_FOUND = 4
33
- TA_FUNC_NOT_FOUND = 5
34
- TA_INVALID_HANDLE = 6
35
- TA_INVALID_PARAM_HOLDER = 7
36
- TA_INVALID_PARAM_HOLDER_TYPE = 8
37
- TA_INVALID_PARAM_FUNCTION = 9
38
- TA_INPUT_NOT_ALL_INITIALIZE = 10
39
- TA_OUTPUT_NOT_ALL_INITIALIZE = 11
40
- TA_OUT_OF_RANGE_START_INDEX = 12
41
- TA_OUT_OF_RANGE_END_INDEX = 13
42
- TA_INVALID_LIST_TYPE = 14
43
- TA_BAD_OBJECT = 15
44
- TA_NOT_SUPPORTED = 16
45
- TA_INTERNAL_ERROR = 5000
46
- TA_UNKNOWN_ERR = 0xFFFF
47
-
48
- # {0,"SMA"},
49
- # {1,"EMA"},
50
- # {2,"WMA"},
51
- # {3,"DEMA" },
52
- # {4,"TEMA" },
53
- # {5,"TRIMA"},
54
- # {6,"KAMA" },
55
- # {7,"MAMA" },
56
- # {8,"T3"}
57
-
58
- typealias "TA_Real", "double"
59
- typealias "TA_Integer", "int"
60
- typealias "TA_RetCode", "int"
61
- typealias "TA_FuncHandle", "unsigned int"
62
- typealias "TA_FuncFlags", "int"
63
- typealias "TA_CallForEachFunc", "void *"
64
- typealias "TA_InputParameterType", "int"
65
- typealias "TA_InputFlags", "int"
66
- typealias "TA_OptInputParameterType", "int"
67
- typealias "TA_OptInputFlags", "int"
68
- typealias "TA_OutputParameterType", "int"
69
- typealias "TA_OutputFlags", "int"
70
-
71
- TA_StringTable = struct [
72
- "unsigned int size",
73
- "const char **string",
74
- "void *hiddenData"
75
- ]
76
-
77
- TA_FuncInfo = struct [
78
- "const char *name",
79
- "const char *group",
80
- "const char *hint",
81
- "const char *camelCaseName",
82
- "TA_FuncFlags flags",
83
- "unsigned int nbInput",
84
- "unsigned int nbOptInput",
85
- "unsigned int nbOutput",
86
- "const TA_FuncHandle *handle"
87
- ]
88
-
89
- TA_ParamHolder = struct [
90
- "void *hiddenData"
91
- ]
92
-
93
- TA_InputParameterInfo = struct [
94
- "TA_InputParameterType type",
95
- "const char *paramName",
96
- "TA_InputFlags flags"
97
- ]
98
-
99
- TA_OptInputParameterInfo = struct [
100
- "TA_OptInputParameterType type",
101
- "const char *paramName",
102
- "TA_OptInputFlags flags",
103
- "const char *displayName",
104
- "const void *dataSet",
105
- "TA_Real defaultValue",
106
- "const char *hint",
107
- "const char *helpFile"
108
- ]
109
-
110
- TA_OutputParameterInfo = struct [
111
- "TA_OutputParameterType type",
112
- "const char *paramName",
113
- "TA_OutputFlags flags"
114
- ]
115
-
116
- TA_PARAM_TYPE = {
117
- TA_Input_Price: 0,
118
- TA_Input_Real: 1,
119
- TA_Input_Integer: 2,
120
- TA_OptInput_RealRange: 0,
121
- TA_OptInput_RealList: 1,
122
- TA_OptInput_IntegerRange: 2,
123
- TA_OptInput_IntegerList: 3,
124
- TA_Output_Real: 0,
125
- TA_Output_Integer: 1
126
- }.freeze
127
-
128
- TA_FLAGS = {
129
- TA_InputFlags: {
130
- TA_IN_PRICE_OPEN: 0x00000001,
131
- TA_IN_PRICE_HIGH: 0x00000002,
132
- TA_IN_PRICE_LOW: 0x00000004,
133
- TA_IN_PRICE_CLOSE: 0x00000008,
134
- TA_IN_PRICE_VOLUME: 0x00000010,
135
- TA_IN_PRICE_OPENINTEREST: 0x00000020,
136
- TA_IN_PRICE_TIMESTAMP: 0x00000040
137
- },
138
- TA_OptInputFlags: {
139
- TA_OPTIN_IS_PERCENT: 0x00100000,
140
- TA_OPTIN_IS_DEGREE: 0x00200000,
141
- TA_OPTIN_IS_CURRENCY: 0x00400000,
142
- TA_OPTIN_ADVANCED: 0x01000000
143
- },
144
- TA_OutputFlags: {
145
- TA_OUT_LINE: 0x00000001,
146
- TA_OUT_DOT_LINE: 0x00000002,
147
- TA_OUT_DASH_LINE: 0x00000004,
148
- TA_OUT_DOT: 0x00000008,
149
- TA_OUT_HISTO: 0x00000010,
150
- TA_OUT_PATTERN_BOOL: 0x00000020,
151
- TA_OUT_PATTERN_BULL_BEAR: 0x00000040,
152
- TA_OUT_PATTERN_STRENGTH: 0x00000080,
153
- TA_OUT_POSITIVE: 0x00000100,
154
- TA_OUT_NEGATIVE: 0x00000200,
155
- TA_OUT_ZERO: 0x00000400,
156
- TA_OUT_UPPER_LIMIT: 0x00000800,
157
- TA_OUT_LOWER_LIMIT: 0x00001000
158
- }
159
- }.freeze
160
-
161
- extern "int TA_Initialize()"
162
- extern "int TA_Shutdown()"
163
- extern "int TA_GroupTableAlloc(TA_StringTable**)"
164
- extern "int TA_GroupTableFree(TA_StringTable*)"
165
- extern "TA_RetCode TA_FuncTableAlloc(const char *group, TA_StringTable **table)"
166
- extern "TA_RetCode TA_FuncTableFree(TA_StringTable *table)"
167
- extern "TA_FuncHandle TA_GetFuncHandle(const char *name, const TA_FuncHandle **handle)"
168
- extern "TA_RetCode TA_GetFuncInfo(const TA_FuncHandle *handle, const TA_FuncInfo **funcInfo)"
169
- extern "TA_RetCode TA_ForEachFunc(TA_CallForEachFunc functionToCall, void *opaqueData)"
170
- extern "TA_RetCode TA_GetInputParameterInfo(const TA_FuncHandle *handle, unsigned int paramIndex, const TA_InputParameterInfo **info)"
171
- extern "TA_RetCode TA_GetOptInputParameterInfo(const TA_FuncHandle *handle, unsigned int paramIndex, const TA_OptInputParameterInfo **info)"
172
- extern "TA_RetCode TA_GetOutputParameterInfo(const TA_FuncHandle *handle, unsigned int paramIndex, const TA_OutputParameterInfo **info)"
173
- extern "TA_RetCode TA_ParamHolderAlloc(const TA_FuncHandle *handle, TA_ParamHolder **allocatedParams)"
174
- extern "TA_RetCode TA_ParamHolderFree(TA_ParamHolder *params)"
175
- extern "TA_RetCode TA_SetInputParamIntegerPtr(TA_ParamHolder *params, unsigned int paramIndex, const TA_Integer *value)"
176
- extern "TA_RetCode TA_SetInputParamRealPtr(TA_ParamHolder *params, unsigned int paramIndex, const TA_Real *value)"
177
- extern "TA_RetCode TA_SetInputParamPricePtr(TA_ParamHolder *params,
178
- unsigned int paramIndex,
179
- const TA_Real *open,
180
- const TA_Real *high,
181
- const TA_Real *low,
182
- const TA_Real *close,
183
- const TA_Real *volume,
184
- const TA_Real *openInterest)"
185
- extern "TA_RetCode TA_SetOptInputParamInteger(TA_ParamHolder *params, unsigned int paramIndex, TA_Integer optInValue)"
186
- extern "TA_RetCode TA_SetOptInputParamReal(TA_ParamHolder *params, unsigned int paramIndex, TA_Real optInValue)"
187
- extern "TA_RetCode TA_SetOutputParamIntegerPtr(TA_ParamHolder *params, unsigned int paramIndex, TA_Integer *out)"
188
- extern "TA_RetCode TA_SetOutputParamRealPtr(TA_ParamHolder *params, unsigned int paramIndex, TA_Real *out)"
189
- extern "TA_RetCode TA_GetLookback(const TA_ParamHolder *params, TA_Integer *lookback)"
190
- extern "TA_RetCode TA_CallFunc(const TA_ParamHolder *params,
191
- TA_Integer startIdx,
192
- TA_Integer endIdx,
193
- TA_Integer *outBegIdx,
194
- TA_Integer *outNbElement)"
195
- extern "const char *TA_FunctionDescriptionXML(void)"
196
-
197
- module_function
198
-
199
- def extract_flags(value, type)
200
- flags_set = []
201
- TA_FLAGS[type].each do |k, v|
202
- flags_set << k if (value & v) != 0
203
- end
204
- flags_set
205
- end
206
-
207
- def group_table
208
- string_table_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
209
- ret_code = TA_GroupTableAlloc(string_table_ptr.ref)
210
- check_ta_return_code(ret_code)
211
-
212
- string_table = TA_StringTable.new(string_table_ptr)
213
- group_names = Fiddle::Pointer.new(string_table["string"])[0, Fiddle::SIZEOF_VOIDP * string_table["size"]].unpack("Q*").collect { |ptr| Fiddle::Pointer.new(ptr).to_s }
214
- TA_GroupTableFree(string_table_ptr)
215
-
216
- group_names
217
- end
218
-
219
- def function_table(group)
220
- string_table_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
221
- ret_code = TA_FuncTableAlloc(group, string_table_ptr.ref)
222
- check_ta_return_code(ret_code)
223
-
224
- string_table = TA_StringTable.new(string_table_ptr)
225
- func_names = Fiddle::Pointer.new(string_table["string"])[0, Fiddle::SIZEOF_VOIDP * string_table["size"]].unpack("Q*").collect { |ptr| Fiddle::Pointer.new(ptr).to_s }
226
-
227
- TA_FuncTableFree(string_table)
228
-
229
- func_names
230
- end
231
-
232
- def function_info(name)
233
- handle_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
234
- ret_code = TA_GetFuncHandle(name, handle_ptr.ref)
235
- check_ta_return_code(ret_code)
236
-
237
- info_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
238
- ret_code = TA_GetFuncInfo(handle_ptr, info_ptr.ref)
239
- check_ta_return_code(ret_code)
240
-
241
- TA_FuncInfo.new(info_ptr)
242
- end
243
-
244
- def each_function(&block)
245
- callback = Fiddle::Closure::BlockCaller.new(
246
- Fiddle::TYPE_VOID,
247
- [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP],
248
- Fiddle::Function::DEFAULT
249
- ) do |func_info_ptr, _|
250
- block.call TA_FuncInfo.new(func_info_ptr)
251
- end
252
-
253
- ret_code = TA_ForEachFunc(callback, nil)
254
- check_ta_return_code(ret_code)
255
- end
256
-
257
- # rubocop:disable Metrics/MethodLength
258
- # rubocop:disable Metrics/AbcSize
259
- def print_function_info(func_info)
260
- puts "Function Name: #{func_info["name"]}"
261
- puts "Function Group: #{func_info["group"]}"
262
- puts "Function Hint: #{func_info["hint"]}"
263
- puts "Camel Case Name: #{func_info["camelCaseName"]}"
264
- puts "Flags: #{func_info["flags"]}"
265
- puts "Number of Inputs: #{func_info["nbInput"]}"
266
- puts "Number of Optional Inputs: #{func_info["nbOptInput"]}"
267
- puts "Number of Outputs: #{func_info["nbOutput"]}"
268
- puts "Function Handle: #{func_info["handle"].to_i}"
269
-
270
- puts "\nInput Parameter Info:"
271
- func_info["nbInput"].times do |i|
272
- param_info_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
273
- ret_code = TA_GetInputParameterInfo(func_info["handle"], i, param_info_ptr.ref)
274
- check_ta_return_code(ret_code)
275
- param_info = TA_InputParameterInfo.new(param_info_ptr)
276
- puts " Parameter #{i + 1}:"
277
- puts " Name: #{param_info["paramName"]}"
278
- puts " Type: #{param_info["type"]}"
279
- puts " Flags: #{extract_flags(param_info["flags"], :TA_InputFlags)}"
280
- end
281
-
282
- puts "\nOptional Input Parameter Info:"
283
- func_info["nbOptInput"].times do |i|
284
- param_info_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
285
- ret_code = TA_GetOptInputParameterInfo(func_info["handle"], i, param_info_ptr.ref)
286
- check_ta_return_code(ret_code)
287
- param_info = TA_OptInputParameterInfo.new(param_info_ptr)
288
- puts " Parameter #{i + 1}:"
289
- puts " Name: #{param_info["paramName"]}"
290
- puts " Type: #{param_info["type"]}"
291
- puts " Flags: #{extract_flags(param_info["flags"], :TA_OptInputFlags)}"
292
- puts " Display Name: #{param_info["displayName"]}"
293
- puts " Default Value: #{param_info["defaultValue"]}"
294
- puts " Hint: #{param_info["hint"]}"
295
- end
296
-
297
- puts "\nOutput Parameter Info:"
298
- func_info["nbOutput"].times do |i|
299
- param_info_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
300
- ret_code = TA_GetOutputParameterInfo(func_info["handle"], i, param_info_ptr.ref)
301
- check_ta_return_code(ret_code)
302
- param_info = TA_OutputParameterInfo.new(param_info_ptr)
303
- puts " Parameter #{i + 1}:"
304
- puts " Name: #{param_info["paramName"]}"
305
- puts " Type: #{param_info["type"]}"
306
- puts " Flags: #{extract_flags(param_info["flags"], :TA_OutputFlags)}"
307
- end
308
- end
309
- # rubocop:enable Metrics/MethodLength
310
- # rubocop:enable Metrics/AbcSize
311
-
312
- # rubocop:disable Metrics/MethodLength
313
- def call_func(func_name, args)
314
- options = args.last.is_a?(Hash) ? args.pop : {}
315
- input_arrays = args
316
-
317
- validate_inputs!(input_arrays)
318
-
319
- handle_ptr = get_function_handle(func_name)
320
- params_ptr = create_parameter_holder(handle_ptr)
321
-
322
- begin
323
- setup_input_parameters(params_ptr, input_arrays, func_name)
324
- setup_optional_parameters(params_ptr, options, func_name)
325
- _lookback = calculate_lookback(params_ptr)
326
- calculate_results(params_ptr, input_arrays.first.length, func_name)
327
- ensure
328
- TA_ParamHolderFree(params_ptr)
329
- end
330
- end
331
- # rubocop:enable Metrics/MethodLength
332
-
333
- def calculate_lookback(params_ptr)
334
- lookback_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT)
335
- ret_code = TA_GetLookback(params_ptr, lookback_ptr)
336
- check_ta_return_code(ret_code)
337
- lookback_ptr[0, Fiddle::SIZEOF_INT].unpack1("l")
338
- end
339
-
340
- # rubocop:disable Metrics/CyclomaticComplexity
341
- # rubocop:disable Metrics/PerceivedComplexity
342
- def validate_inputs!(arrays)
343
- raise TALibError, "Input arrays cannot be empty" if arrays.empty?
344
-
345
- arrays.each do |arr|
346
- raise TALibError, "Input must be arrays" unless arr.is_a?(Array)
347
- end
348
-
349
- sizes = arrays.map(&:length)
350
- raise TALibError, "Input arrays cannot be empty" if sizes.any?(&:zero?)
351
-
352
- arrays.each do |arr|
353
- raise TALibError, "Input arrays must contain only numbers" unless arr.flatten.all? { |x| x.is_a?(Numeric) }
354
- end
355
- end
356
- # rubocop:enable Metrics/CyclomaticComplexity
357
- # rubocop:enable Metrics/PerceivedComplexity
358
-
359
- def get_function_handle(func_name)
360
- handle_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
361
- ret_code = TA_GetFuncHandle(func_name, handle_ptr.ref)
362
- check_ta_return_code(ret_code)
363
- handle_ptr
364
- end
365
-
366
- def create_parameter_holder(handle_ptr)
367
- params_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
368
- ret_code = TA_ParamHolderAlloc(handle_ptr, params_ptr.ref)
369
- check_ta_return_code(ret_code)
370
- params_ptr
371
- end
372
-
373
- def setup_input_parameters(params_ptr, input_arrays, func_name)
374
- func_info = function_info_map[func_name]
375
- input_arrays.each_with_index do |array, index|
376
- input_info = func_info[:inputs][index]
377
- ret_code = set_input_parameter(params_ptr, index, array, input_info)
378
- check_ta_return_code(ret_code)
379
- end
380
- end
381
-
382
- def set_input_parameter(params_ptr, index, array, input_info)
383
- case input_info["type"]
384
- when TA_PARAM_TYPE[:TA_Input_Real]
385
- input_ptr = prepare_double_array(array)
386
- TA_SetInputParamRealPtr(params_ptr, index, input_ptr)
387
- when TA_PARAM_TYPE[:TA_Input_Integer]
388
- input_ptr = prepare_integer_array(array)
389
- TA_SetInputParamIntegerPtr(params_ptr, index, input_ptr)
390
- when TA_PARAM_TYPE[:TA_Input_Price]
391
- setup_price_inputs(params_ptr, index, array, input_info["flags"])
392
- end
393
- end
394
-
395
- def prepare_double_array(array)
396
- array_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_DOUBLE * array.length)
397
- array.each_with_index do |value, i|
398
- array_ptr[i * Fiddle::SIZEOF_DOUBLE, Fiddle::SIZEOF_DOUBLE] = [value.to_f].pack("d")
399
- end
400
- array_ptr
401
- end
402
-
403
- def prepare_integer_array(array)
404
- array_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT * array.length)
405
- array.each_with_index do |value, i|
406
- array_ptr[i * Fiddle::SIZEOF_INT, Fiddle::SIZEOF_INT] = [value.to_i].pack("l")
407
- end
408
- array_ptr
409
- end
410
-
411
- def setup_optional_parameters(params_ptr, options, func_name)
412
- func_info = function_info_map[func_name]
413
- func_info[:opt_inputs]&.each_with_index do |opt_input, index|
414
- param_name = normalize_parameter_name(opt_input["paramName"].to_s)
415
- set_optional_parameter(params_ptr, index, options[param_name.to_sym], opt_input["type"]) if options.key?(param_name.to_sym)
416
- end
417
- end
418
-
419
- def set_optional_parameter(params_ptr, index, value, type)
420
- case type
421
- when TA_PARAM_TYPE[:TA_OptInput_RealRange], TA_PARAM_TYPE[:TA_OptInput_RealList]
422
- ret_code = TA_SetOptInputParamReal(params_ptr, index, value)
423
- when TA_PARAM_TYPE[:TA_OptInput_IntegerRange], TA_PARAM_TYPE[:TA_OptInput_IntegerList]
424
- ret_code = TA_SetOptInputParamInteger(params_ptr, index, value)
425
- end
426
- check_ta_return_code(ret_code)
427
- end
428
-
429
- # rubocop:disable Metrics/MethodLength
430
- def calculate_results(params_ptr, input_size, func_name)
431
- out_begin = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT)
432
- out_size = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT)
433
- output_arrays = setup_output_buffers(params_ptr, input_size, func_name)
434
-
435
- begin
436
- ret_code = TA_CallFunc(params_ptr, 0, input_size - 1, out_begin, out_size)
437
- check_ta_return_code(ret_code)
438
-
439
- actual_size = out_size[0, Fiddle::SIZEOF_INT].unpack1("l")
440
- format_output_results(output_arrays, actual_size, func_name)
441
- ensure
442
- out_begin.free
443
- out_size.free
444
- output_arrays.each(&:free)
445
- end
446
- end
447
- # rubocop:enable Metrics/MethodLength
448
-
449
- # rubocop:disable Metrics/MethodLength
450
- # rubocop:disable Metrics/AbcSize
451
- def setup_output_buffers(params_ptr, size, func_name)
452
- func_info = function_info_map[func_name]
453
- output_ptrs = []
454
-
455
- func_info[:outputs].each_with_index do |output, index|
456
- ptr = case output["type"]
457
- when TA_PARAM_TYPE[:TA_Output_Real]
458
- Fiddle::Pointer.malloc(Fiddle::SIZEOF_DOUBLE * size)
459
- when TA_PARAM_TYPE[:TA_Output_Integer]
460
- Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT * size)
461
- end
462
-
463
- output_ptrs << ptr
464
-
465
- ret_code = case output["type"]
466
- when TA_PARAM_TYPE[:TA_Output_Real]
467
- TA_SetOutputParamRealPtr(params_ptr, index, ptr)
468
- when TA_PARAM_TYPE[:TA_Output_Integer]
469
- TA_SetOutputParamIntegerPtr(params_ptr, index, ptr)
470
- end
471
-
472
- check_ta_return_code(ret_code)
473
- end
474
-
475
- output_ptrs
476
- end
477
- # rubocop:enable Metrics/MethodLength
478
- # rubocop:enable Metrics/AbcSize
479
-
480
- # rubocop:disable Metrics/MethodLength
481
- # rubocop:disable Metrics/AbcSize
482
- def format_output_results(output_ptrs, size, func_name)
483
- func_info = function_info_map[func_name]
484
- results = output_ptrs.zip(func_info[:outputs]).map do |ptr, output|
485
- case output["type"]
486
- when TA_PARAM_TYPE[:TA_Output_Real]
487
- ptr[0, Fiddle::SIZEOF_DOUBLE * size].unpack("d#{size}")
488
- when TA_PARAM_TYPE[:TA_Output_Integer]
489
- ptr[0, Fiddle::SIZEOF_INT * size].unpack("l#{size}")
490
- end
491
- end
492
-
493
- return results.first if results.length == 1
494
-
495
- output_names = func_info[:outputs].map do |output|
496
- normalize_parameter_name(output["paramName"].to_s).to_sym
497
- end
498
- output_names.zip(results).to_h
499
- end
500
- # rubocop:enable Metrics/AbcSize
501
- # rubocop:enable Metrics/MethodLength
502
-
503
- def function_description_xml
504
- TA_FunctionDescriptionXML().to_s
505
- end
506
-
507
- def function_info_map
508
- @function_info_map ||= build_function_info_map
509
- end
510
-
511
- def build_function_info_map
512
- info_map = {}
513
- each_function do |func_info|
514
- info_map[func_info["name"].to_s] = {
515
- info: func_info,
516
- inputs: collect_input_info(func_info),
517
- outputs: collect_output_info(func_info),
518
- opt_inputs: collect_opt_input_info(func_info)
519
- }
520
- end
521
- info_map
522
- end
523
-
524
- def collect_input_info(func_info)
525
- func_info["nbInput"].times.map do |i|
526
- param_info_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
527
- TA_GetInputParameterInfo(func_info["handle"], i, param_info_ptr.ref)
528
- TA_InputParameterInfo.new(param_info_ptr)
529
- end
530
- end
531
-
532
- def collect_opt_input_info(func_info)
533
- func_info["nbOptInput"].times.map do |i|
534
- param_info_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
535
- TA_GetOptInputParameterInfo(func_info["handle"], i, param_info_ptr.ref)
536
- TA_OptInputParameterInfo.new(param_info_ptr)
537
- end
538
- end
539
-
540
- def collect_output_info(func_info)
541
- func_info["nbOutput"].times.map do |i|
542
- param_info_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
543
- TA_GetOutputParameterInfo(func_info["handle"], i, param_info_ptr.ref)
544
- TA_OutputParameterInfo.new(param_info_ptr)
545
- end
546
- end
547
-
548
- def generate_ta_functions
549
- each_function do |func_info|
550
- define_ta_function(func_info["name"].to_s.downcase, func_info["name"].to_s)
551
- end
552
- end
553
-
554
- def normalize_parameter_name(name)
555
- name.sub(/^(optIn|outReal|outInteger|out)/, "")
556
- .gsub(/::/, "/")
557
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
558
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
559
- .tr("-", "_")
560
- .downcase
561
- end
562
-
563
- # rubocop:disable Metrics/MethodLength
564
- # rubocop:disable Metrics/AbcSize
565
- # rubocop:disable Metrics/CyclomaticComplexity
566
- def check_ta_return_code(code)
567
- return if code == TA_SUCCESS
568
-
569
- error_message = case code
570
- when TA_LIB_NOT_INITIALIZE
571
- "TA-Lib not initialized, please call TA_Initialize first"
572
- when TA_BAD_PARAM
573
- "Bad parameter, please check input parameters"
574
- when TA_ALLOC_ERR
575
- "Memory allocation error, possibly insufficient memory"
576
- when TA_GROUP_NOT_FOUND
577
- "Function group not found"
578
- when TA_FUNC_NOT_FOUND
579
- "Function not found"
580
- when TA_INVALID_HANDLE
581
- "Invalid handle"
582
- when TA_INVALID_PARAM_HOLDER
583
- "Invalid parameter holder"
584
- when TA_INVALID_PARAM_HOLDER_TYPE
585
- "Invalid parameter holder type"
586
- when TA_INVALID_PARAM_FUNCTION
587
- "Invalid parameter function"
588
- when TA_INPUT_NOT_ALL_INITIALIZE
589
- "Input parameters not fully initialized"
590
- when TA_OUTPUT_NOT_ALL_INITIALIZE
591
- "Output parameters not fully initialized"
592
- when TA_OUT_OF_RANGE_START_INDEX
593
- "Start index out of range"
594
- when TA_OUT_OF_RANGE_END_INDEX
595
- "End index out of range"
596
- when TA_INVALID_LIST_TYPE
597
- "Invalid list type"
598
- when TA_BAD_OBJECT
599
- "Invalid object"
600
- when TA_NOT_SUPPORTED
601
- "Operation not supported"
602
- when TA_INTERNAL_ERROR
603
- "TA-Lib internal error"
604
- when TA_UNKNOWN_ERR
605
- "Unknown error"
606
- else
607
- "Undefined TA-Lib error (Error code: #{code})"
608
- end
609
-
610
- raise TALibError, error_message
611
- end
612
- # rubocop:enable Metrics/CyclomaticComplexity
613
- # rubocop:enable Metrics/MethodLength
614
- # rubocop:enable Metrics/AbcSize
615
-
616
- def initialize_ta_lib
617
- return if @initialized
618
-
619
- ret_code = TA_Initialize()
620
- check_ta_return_code(ret_code)
621
- at_exit { TA_Shutdown() }
622
- @initialized = true
623
- end
624
-
625
- def define_ta_function(method_name, func_name)
626
- define_singleton_method(method_name) do |*args|
627
- call_func(func_name, args)
628
- end
629
- end
630
-
631
- def setup_price_inputs(params_ptr, index, price_data, flags)
632
- required_flags = extract_flags(flags, :TA_InputFlags)
633
- data_pointers = Array.new(6) { nil }
634
- TA_FLAGS[:TA_InputFlags].keys[0..5].each_with_index do |flag, i|
635
- data_pointers[i] = if required_flags.include?(flag)
636
- prepare_double_array(price_data[required_flags.index(flag)])
637
- else
638
- Fiddle::Pointer.malloc(0)
639
- end
640
- end
641
-
642
- TA_SetInputParamPricePtr(params_ptr, index, *data_pointers)
643
- end
644
-
645
- initialize_ta_lib
646
- generate_ta_functions
647
- end