株価分析ソフトを作る際に利用されるTA-LibをRubyから活用する方法を説明する。この記事では移動平均を算出するTA_MA関数を例にまずはC言語から呼出すコードを掲載し、更にRubyからも呼出すコードも示す。
C/C++ API - TA-Lib - Technical Analysis Library
C言語から読み出すコード。
#include "ta-lib/ta_func"
TA_Initialize()
TA_Real closePrice[400];
TA_Real out[400];
TA_Integer outBeg;
TA_Integer outNbElement;
/* ... initialize your closing price here... */
retCode = TA_MA( 0, 399,
&closePrice[0],
30,TA_MAType_SMA,
&outBeg, &outNbElement, &out[0] );
/* The output is displayed here */
for( i=0; i < outNbElement; i++ )
printf( "Day %d = %f\n", outBeg+i, out[i] );
同じ事をRubyで行うコード
配列へのPointerを受け取るC言語関数(ta-lib)をRubyから呼出す方法の要点
- PointerはRubyのオブジェクトFiddle::Pointer.mallocメソッドにサイズを与えて取得
- PointerはRubyではバイト列(String)のオブジェクト
- String.unpackメソッドで配列が取得できる
require 'fiddle'
require 'fiddle/import'
# TA-Libの関数を扱うモジュールを定義
module TALib
extend Fiddle::Importer
# 共有ライブラリをロード(環境に応じてパスを調整)
dlload 'libta_lib.dylib' # Linux/macOSの場合。Windowsなら 'ta_lib.dll'
# TA-Libの型定義
TA_Real = Fiddle::TYPE_DOUBLE
TA_Integer = Fiddle::TYPE_INT
# TA_MA関数のシグネチャを定義
extern 'int TA_Initialize()'
extern 'int TA_MA(int startIdx, int endIdx, double *inReal, int optInTimePeriod, int optInMAType, int *outBegIdx, int *outNbElement, double *outReal)'
end
# サンプルデータと計算
def calculate_moving_average
# TA-Libの初期化
ret = TALib.TA_Initialize()
raise "TA_Initialize failed with code #{ret}" unless ret == 0
# クローズ価格データの準備(仮のデータ)
# close_prices = Array.new(10) { |i| rand(100.0..200.0) } # 400個のダミーデータ
close_prices = Array.new(10) { |i| i * 10.0 } # 10個のダミーデータ
in_real = Fiddle::Pointer[close_prices.pack('d*')] # double型の配列に変換
# 出力用の配列と変数を準備
out_real = Fiddle::Pointer.malloc(10 * Fiddle::SIZEOF_DOUBLE) # 出力配列
out_beg_idx = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT) # 開始インデックス
out_nb_element = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT) # 出力要素数
# TA_MA関数の呼び出し
ret = TALib.TA_MA(
0, # startIdx
9, # endIdx
in_real, # inReal (入力データ)
4, # optInTimePeriod (期間)
0, # optInMAType (0 = SMA)
out_beg_idx, # outBegIdx (出力開始インデックス)
out_nb_element, # outNbElement (出力要素数)
out_real # outReal (出力データ)
)
raise "TA_MA failed with code #{ret}" unless ret == 0
# 結果の取得
out_beg = out_beg_idx[0, Fiddle::SIZEOF_INT].unpack('i')[0]
nb_element = out_nb_element[0, Fiddle::SIZEOF_INT].unpack('i')[0]
out = out_real[0, nb_element * Fiddle::SIZEOF_DOUBLE].unpack('d*')
# unpackメソッドはバイト列をオブジェクト配列へ変換する。out_beg_idx, out_nb_elementは
# Fiddle::Ponterオブジェクト
# 結果を表示
nb_element.times do |i|
puts "Day #{out_beg + i} = #{out[i]}"
end
end
# メモリ解放
out_real.free
out_beg_idx.free
out_nb_element.free
# 実行
begin
calculate_moving_average
rescue => e
puts "エラー: #{e.message}"
end
see source of import.rb and struct.rb, then search Example. fiddle/lib/fiddle at master · ruby/fiddle