クリック証券アクセスプラグインをαリリース
スクレイピング版ClickClient ver 0.1.3をリリースしました。
→Github - unageanu / clickclient_scrap
このバージョンから、jiji用のクリック証券アクセスプラグインをリリース物に追加しています。jijiに加えてclickclient_scrapを環境にインストールして設定することで、クリック証券にアクセスして取引を行うことが可能となります。
注意事項
jijiへの導入手順
前提条件
クリック証券のアカウントが必要です。インストールの前にご用意ください。
導入
以下の操作を実行します。
$ gem sources -a http://gems.github.com $ gem install unageanu-clickclient_scrap
導入後、「jiji setting」を実行すればプラグインが認識されているはずです。あとは設定を行ってjijiを再起動すればOK。
$ jiji setting > Please select a securities. 1 : CLICK Securities DEMO Trade 2 : CLICK Securities 2 ←「クリック証券」を選択 > Please input a user name of CLICK Securities. unageanu ←ユーザー名を入力 > Please input a password of CLICK Securities. ************ ←パスワードを入力 > Please input a proxy. example: https://example.com:80 (default: nil ) ←(必要なら)プロキシを入力 > Please input a data directory of jiji. (default: ~/.jiji ) ...略
不具合などありましたら、例によってブログのコメント、もしくは GitHubのIssuesで報告いただければ幸いです。
加重移動平均を算出する
移動平均もいろいろあるなー。
普通の移動平均は作成済みなので、その次の加重移動平均を実装。
# 一定期間の加重移動平均を得る class WeightedMovingAverage def initialize( range=25 ) @rates = [] # レートを記録するバッファ @range = range end def next_rate( rate ) # バッファのデータを更新 @rates.push rate @rates.shift if @rates.length > @range # バッファサイズが十分でなければ、nilを返す。 return nil if @rates.length != @range return WeightedMovingAverage.get_weighted_moving_average(@rates) end private # 加重移動平均値を計算する。 # see http://ja.wikipedia.org/wiki/%E7%A7%BB%E5%8B%95%E5%B9%B3%E5%9D%87 def self.get_weighted_moving_average( rates ) weight = 1 total = rates.inject(0.0) {|t,s| t += s.end * weight weight += 1 t } return total / ( rates.length * (rates.length+1) / 2 ) end end
利用側のエージェントは以下。普通の移動平均との違いがわかるように、両者を表示してみました。実行してみると加重移動平均(緑)のほうが感度が高いのがわかるかと思います。
# # 加重移動平均を使うエージェントのサンプル # class MovingAverageAgent < JIJI::PeriodicallyAgent # エージェントの説明 def description "加重移動平均を使うエージェントのサンプル。" end # エージェントを初期化する。 def init # 移動平均の算出クラス @mvs = [ JIJI::Agent::Shared::WeightedMovingAverage.new(@range), JIJI::Agent::Shared::MovingAverage.new(@range) ] @out = output.get( "加重移動平均と移動平均", :graph, { :column_count=>2, :graph_type=>:rate, :colors=>["#99aaaa","#aa99aa"] # デフォルトのグラフの色 } ) end # 次のレートを受け取る def next_period_rates( rates ) res = @mvs.map{|mv| mv.next_rate( rates[:EURJPY].bid ) } return if ( !res[0] || !res[1]) @out.put( *res ) end # UIから設定可能なプロパティの一覧を返す。 def property_infos super().concat [ Property.new( "range", "集計期間", 25, :number ) ] end end
例によって、デモサイトにも置いています。
ボリンジャーバンドを算出する
クリック証券のスクレイピング版プラグインもだいぶできつつある(現在稼動テスト&デバッグ中)ので、ぼちぼち組み込みのライブラリを充実させて簡単にエージェントを作れるようにしていきたい。ということで、とりあえずボリンジャーバンドを算出するライブラリを書いてみました。jiji添付の「MovingAverage」と同じような形で使えます。
# ボリンジャーバンドを得る class BollingerBands #コンストラクタ #range:: 集計期間 def initialize( range=25, pivot=[0,1,2] ) @rates = [] # レートを記録するバッファ @range = range @pivot = pivot end def next_rate( rate ) # バッファのデータを更新 @rates.push rate @rates.shift if @rates.length > @range # バッファサイズが十分でなければ、nilを返す。 return nil if @rates.length != @range # ボリンジャーバンドを算出 return BollingerBands.get_bollinger_bands(@rates, @pivot) end private # 移動平均値を計算する。 def self.get_moving_average( rates ) total = 0 rates.each {|s| total += s.end total += s.max total += s.min } return total / ( rates.length * 3 ) end #===ボリンジャーバンド値を得る。 # # TP(ティピカルプライス)=(高値+安値+終値)÷3 # +2σ=TPの移動平均+標準偏差×2 # +σ=TPの移動平均+標準偏差 # -σ=TPの移動平均-標準偏差 # -2σ=TPの移動平均-標準偏差×2 # 標準偏差=√{(各TP−TPの期間中平均値)の2乗を期間分全部加えたもの}÷期間 # (√は式全体にかかる) # #rates:: 値を取得する範囲の株価の配列 25が一般的。 #range:: 標準偏差の倍数。初期値[0,1,2] #戻り値:: ボリンジャーバンドの各値の配列。例) [+2σ, +1σ, TP, -1σ, -2σ] # def self.get_bollinger_bands( rates, range=[0,1,2] ) ma = get_moving_average( rates ) total = rates.inject(0.0) {|t,s| t+= (( (s.end + s.max + s.min ) / 3 ) - ma ) ** 2 t } sd = Math.sqrt((total / rates.length)) res = [] range.each { |r| res.unshift( ma + sd * r ) res.push( ma + sd * r * -1 ) if r != 0 } return res end end
利用側のエージェントは以下。取引はせず、グラフデータの出力のみ行っています。
#ボリンジャーバンドを使うエージェントのサンプル class BollingerBandSampleAgent < JIJI::PeriodicallyAgent def description <<-STR ボリンジャーバンドを使うエージェントのサンプルです。 STR end # エージェントを初期化する。 def init @bollinger_band = JIJI::Agent::Shared::BollingerBands.new(@range) # 移動平均をグラフで表示するためのOutput @out = output.get( "ボリンジャーバンド", :graph, { :column_count=>5, :graph_type=>:rate, :colors=>["#779999","#557777","#335555","#557777", "#779999"] } ) end # 次のレートを受け取る def next_period_rates( rates ) res =@bollinger_band.next_rate( rates[:EURJPY].bid ) return unless res @out.put( *res ) end # UIから設定可能なプロパティの一覧を返す。 def property_infos super().concat [ Property.new( "range", "集計期間", 25, :number ) ] end end
デモサイトにも置いたので遊びたい方はどうぞ。
週末になるとjijiのデモサイトが落ちる
週末になるとjijiのデモサイトが落ちる問題があって、Ubuntuのアップデートとかでシステムが再起動されてるのかなー?とか思っていたんだけど、ログをちゃんとみてみると、OOM Killerが発動して殺されてるじゃないか!!!名前は聞いたことあったけど動いているのははじめてみたよ > OOM Killer。
原因は、まず間違いなくjijiなんだろうけど(申し訳ない)なんで週末にだけメモリ消費が増大するのかがわからん。考えられるのは、クリック証券Webサービスのシステムメンテナンスくらいかなー。メンテナンスに移行するタイミングでなんか変なデータ返してきてんじゃねーの?と言いがかりをつけてみる。 さて、どう調査したものか。
デモサイトに繋がらなくなっています。
申し訳ありません。現在、jijiのデモサイトに接続できなくなっているようです。
連休中に自宅サーバーを変更したので、どこかで設定をミスっているものと思われます。orz.しばらくお待ちください。
証券会社アクセスプラグイン作成ガイド
jiji 1.1.0をリリースしました。本バージョンより、証券会社へのアクセスロジックをプラグインで後から追加できるようになっています。
→オープン・フリーのFX自動取引システム「jiji (ジジ) 」
→Github - unageanu/jiji
アクセスプラグイン自体は、まだ「クリック証券デモトレード」用のものしかありませんが、差し替えの仕組みは実装済みです。ということで、証券会社アクセスプラグインの作り方について解説します。
作成手順の概要
- 必要な機能を提供するクラスを書く。
- jiji_plugin.rb を用意し、プラグインをjijiに登録するコードを書く。
- 作成したファイルを含むgemを作成し、配布する。
1.必要な機能を提供するクラスを書く
証券会社アクセスプラグインが必要とする機能を提供するプラグインクラスを作成します。
- 必要な機能は以下の4つです。
- ↑とは別に「jiji setting」で入力が必要な情報(ユーザー名/パスワード)を返すAPIなども実装する必要があります。
APIの詳細は、以下のモジュールの説明を参照ください。プラグインクラスを作成する場合、このモジュールをincludeして必要なAPIをオーバーライドするのが簡単です。
module JIJI module Plugin # #===証券会社アクセスプラグイン # #証券会社へのアクセスを提供するプラグインのインターフェイスを示すモジュール。 #証券会社アクセスプラグインはこのモジュールが示すメソッドを実装する必要があります。 # module SecuritiesPlugin #プラグイン識別子 FUTURE_NAME = :securities #プラグインの識別子を返します。 def plugin_id end #プラグインの表示名を返します。 #「jiji setting」での証券会社選択時に使用します。 def display_name end #「jiji setting」でユーザーに入力を要求するデータの情報を返します。 #return:: JIJI::Plugin::Securities::Inputの配列 def input_infos end #プラグインを初期化します。プラグインの利用が開始される前に1度だけ呼び出されます。 #引数として、ユーザーが入力したパラメータが渡されます。 #props:: ユーザーが入力したパラメータ(JIJI::Plugin::Securities::Inputのkeyをキーとする設定値の配列) #logger:: ロガー def init_plugin( props, logger ) end #プラグインを破棄します。jijiの停止時に1度だけ呼び出されます。 def destroy_plugin end #利用可能な通貨ペア一覧を取得します。 #return:: JIJI::Plugin::Securities::Pairの配列 def list_pairs end #現在のレートを取得します。 #return:: 通貨ペア名をキーとするJIJI::Plugin::Securities::Rateのハッシュ def list_rates end #成り行きで発注を行います。 #pair:: 通貨ペア名 #sell_or_buy:: 売(:sell)または買い(:buy) #count:: 取引数量 #return:: JIJI::Plugin::Securities::Position def order( pair, sell_or_buy, count ) end #建玉を決済します。 #position_id:: 建玉ID #count:: 取引数量 def commit( position_id, count ) end #===ユーザーに入力を要求するデータの情報 #key:: データのキー #description:: 入力時に表示する説明 #secure:: UIでの入力値の表示を行うかどうか。trueにするとUI上では「*」で表示されます。 #validator:: UIでの入力値のチェックを行うProc。引数として文字列を受け取り、 # エラーがあった場合はエラーメッセージ、問題ない場合はnilを返すこと。 # nilを指定すると、入力値のチェックを行わない。 Input = Struct.new( :key, :description, :secure, :validator ) #===取引可能な通貨ペア #name:: 通貨ペア名 例) :EURJPY #tade_unit:: 取引単位 Pair = Struct.new( :name, :trade_unit ) #===レート #bid:: bidレート #ask:: askレート #sell_swap:: 売りスワップ #buy_swap:: 買いスワップ Rate = Struct.new( :bid, :ask, :sell_swap, :buy_swap ) #===建玉 #position_id:: 建玉の識別子 Position = Struct.new( :position_id ) end end
参考までに、jiji添付の「クリック証券デモトレードアクセスプラグイン」のコードも載せておきます。依存しているクラスなどはGithub - unageanu/jiji を参照ください。
require 'jiji/plugin/securities_plugin' require 'jiji/plugin/embedded/single_click_client' module JIJI module Plugin # クリック証券デモトレードアクセスプラグイン class ClickSecuritiesDemoPlugin include JIJI::Plugin::SecuritiesPlugin #プラグインの識別子を返します。 def plugin_id :click_securities_demo end #プラグインの表示名を返します。 def display_name "CLICK Securities DEMO Trade" end #「jiji setting」でユーザーに入力を要求するデータの情報を返します。 def input_infos [ Input.new( :user, "Please input a user name of CLICK Securities DEMO Trade.", false, nil ), Input.new( :password, "Please input a password of CLICK Securities DEMO Trade.", true, nil )] end #プラグインを初期化します。 def init_plugin( props, logger ) @client = JIJI::Plugin::SingleClickClient.new( props, logger ) end #プラグインを破棄します。 def destroy_plugin @client.close if @client end #利用可能な通貨ペア一覧を取得します。 def list_pairs pairs = @client.request {|fx| fx.list_currency_pairs } return pairs.map {|i| name = convert_currency_pair_code(i[0]) Pair.new( name, i[1].trade_unit ) } end #現在のレートを取得します。 def list_rates @client.request {|fx| fx.list_rates.inject({}) {|r,p| code = convert_currency_pair_code(p[0]) r[code] = Rate.new( p[1].bid, p[1].ask, p[1].sell_swap, p[1].buy_swap ) r } } end #成り行きで発注を行います。 def order( pair, sell_or_buy, count ) result = @client.request{ |fx| fx.order( convert_currency_pair_code_r(pair), sell_or_buy == :buy ? ClickClient::FX::BUY : ClickClient::FX::SELL, count ) } return JIJI::Plugin::SecuritiesPlugin::Position.new( result.open_interest_no ) end #建玉を決済します。 def commit( position_id, count ) @client.request {|fx| fx.settle( position_id, count ) } end # 通貨ペアコードをシンボルに変換する def convert_currency_pair_code(code) case code when ClickClient::FX::USDJPY return :USDJPY when ClickClient::FX::EURJPY return :EURJPY when ClickClient::FX::GBPJPY return :GBPJPY when ClickClient::FX::AUDJPY return :AUDJPY when ClickClient::FX::NZDJPY return :NZDJPY when ClickClient::FX::CADJPY return :CADJPY when ClickClient::FX::CHFJPY return :CHFJPY when ClickClient::FX::ZARJPY return :ZARJPY when ClickClient::FX::EURUSD return :EURUSD when ClickClient::FX::GBPUSD return :GBPUSD when ClickClient::FX::AUDUSD return :AUDUSD when ClickClient::FX::EURCHF return :EURCHF when ClickClient::FX::GBPCHF return :GBPCHF when ClickClient::FX::USDCHF return :USDCHF end end # シンボルを通貨ペアコードに変換する def convert_currency_pair_code_r(code) case code when :USDJPY return ClickClient::FX::USDJPY when :EURJPY return ClickClient::FX::EURJPY when :GBPJPY return ClickClient::FX::GBPJPY when :AUDJPY return ClickClient::FX::AUDJPY when :NZDJPY return ClickClient::FX::NZDJPY when :CADJPY return ClickClient::FX::CADJPY when :CHFJPY return ClickClient::FX::CHFJPY when :ZARJPY return ClickClient::FX::ZARJPY when :EURUSD return ClickClient::FX::EURUSD when :GBPUSD return ClickClient::FX::GBPUSD when :AUDUSD return ClickClient::FX::AUDUSD when :EURCHF return ClickClient::FX::EURCHF when :GBPCHF return ClickClient::FX::GBPCHF when :USDCHF return ClickClient::FX::USDCHF end end end end end
2. jiji_plugin.rb を用意し、プラグインをjijiに登録するコードを書く。
プラグインクラスができたら、jijiにプラグインを認識させるためのRubyスクリプト「jiji_plugin.rb」を書きます。
- jijiは、起動時にすべてのgemに含まれる「lib/jiji_plugin.rb」ファイルを探索し、ロードします。(詳細はプラグインローダーの解説を参照ください。)
- 「jiji_plugin.rb」内にプラグイン登録コードを書いておくことで、プラグインがjijiに認識されるようになります。
詳細は以下のコードを参照ください。
# 必要なモジュール(プラグインクラスなど)を読み込む require 'jiji/plugin/embedded/click_securities_demo_plugin' # 「JIJI::Plugin.register( <機能名>, <プラグインインスタンス>)」でjijiに機能を登録する。 # 以下は、証券会社アクセスプラグインとして、「JIJI::Plugin::ClickSecuritiesDemoPlugin」を登録する例 JIJI::Plugin.register( JIJI::Plugin::SecuritiesPlugin::FUTURE_NAME, JIJI::Plugin::ClickSecuritiesDemoPlugin.new )
3.作成したファイルを含むgemを作成し、配布する。
プラグインクラス、および「jiji_plugin.rb」を含むgemを作成します。
- プラグインクラスは任意の場所に、
- 「jiji_plugin.rb」は「lib」直下に配置する必要があります。
あとは、gemをインストールしてjijiを再起動すれば、プラグインが認識されるはずです。正しく認識されていれば、「jiji setting」で利用可能な証券会社の選択肢が追加されます。プラグインを公開したい場合は、gemを公開すればOK。rubyforgeやgithubでの配布ももちろん可能です。
解説は以上。不具合などありましたら、blogのコメントなりGithubのissuesなりでご報告下さい。