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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -5
- data/README.md +21 -8
- data/Rakefile +12 -0
- data/lib/ta_lib_ffi/doc.rb +138 -0
- data/lib/ta_lib_ffi.rb +2082 -0
- data/ta_lib_ffi.gemspec +9 -7
- metadata +37 -8
- data/lib/ta_lib.rb +0 -647
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
|