無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

証券会社アクセスプラグイン作成ガイド

jiji 1.1.0をリリースしました。本バージョンより、証券会社へのアクセスロジックをプラグインで後から追加できるようになっています。

オープン・フリーのFX自動取引システム「jiji (ジジ) 」
Github - unageanu/jiji
アクセスプラグイン自体は、まだ「クリック証券デモトレード」用のものしかありませんが、差し替えの仕組みは実装済みです。ということで、証券会社アクセスプラグインの作り方について解説します。

作成手順の概要

  1. 必要な機能を提供するクラスを書く。
  2. jiji_plugin.rb を用意し、プラグインをjijiに登録するコードを書く。
  3. 作成したファイルを含むgemを作成し、配布する。

1.必要な機能を提供するクラスを書く

証券会社アクセスプラグインが必要とする機能を提供するプラグインクラスを作成します。

  • 必要な機能は以下の4つです。
    • 利用可能な通貨ペア一覧の取得
    • 現在のレート(bid,ask,売スワップ、買スワップ)一覧の取得
    • (成り行きでの)売/買注文
    • 決済
  • ↑とは別に「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。rubyforgegithubでの配布ももちろん可能です。



解説は以上。不具合などありましたら、blogのコメントなりGithubのissuesなりでご報告下さい。