SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

Java RTSによる金融アプリケーションの作成

リアルタイムアプリケーションによる取引

  • このエントリーをはてなブックマークに追加

Java RTS金融アプリケーションのデモ

 指値/逆指値注文システムは、以下の3つのコンポーネントで構成されます(図1を参照)。

  • データフィードアプリケーション
  • 取引エンジンアプリケーション
  • チャート作成アプリケーション
図1 取引システムのメインコンポーネント
図1 取引システムのメインコンポーネント

 取引エンジン(この記事の重点項目)では、以下の2つのメインスレッドが動作します。

  1. データフィードをリッスンし、監視対象の株の現在価格を更新するスレッド(MarketManager)。
  2. 既存の指値/逆指値を現在の株価と比較するタイムクリティカルなループを実行するスレッド(OrderManager)(図2を参照)。株価が指値/逆指値の水準まで動いた場合、取引エンジンが取引を執行します。
図2 OrderManagerスレッド
図2 OrderManagerスレッド

 リアルタイムではない取引エンジン実装では、外部イベント(ガベージコレクションなど)がタイムクリティカルなループに割り込んできて、余分なレイテンシを発生させる可能性があります。その遅延の間に市場の値動きがあって、システムで1つ以上の無条件注文(オープンオーダー)の範囲内にいったん株価が入ってから範囲外にまた戻すことが起こりえます。事実、この現象は本稿のデモで発生します。

 このシナリオでは、注文の機会を失ったことは運用機関の金銭的損失を意味します。理想的な取引のタイミングを逃したのに、顧客の注文を引き受けなければならないからです(図3を参照)。ガベージコレクションの割り込みが取引に影響を与える可能性は微々たるものですが、リスクは非常に大きなものです。次のデモでは、リスクが現実に起こりうることを示します。

図3 理想的な取引タイミングの逸失
図3 理想的な取引タイミングの逸失

 しかし、同じ取引エンジンを(スレッドモデルにちょっとした変更を加えてから)Java RTSで実行すると、JVMはタイムクリティカルなコード内でデッドラインに対応することができ、取引の機会は失われません。エンジンは、市場のすべてのティックを取得し、すべての取引機会に応じます。その結果、市場の動きに確実に反応し、条件が満たされたときに顧客の注文を正確に取引できるシステムが手に入ります。

取引エンジンのタイムクリティカルな部分

 取引エンジンのオーダーブックは、HashMapで構成されます。HashMapはSubBookクラスのオブジェクトで構成され、このオブジェクトは株価を示す2つのLinkedListで構成されます。1つのLinkedListは買い注文用(上昇時)で、もう1つのLinkedListは売り注文用(下落時)です(図4を参照)。

図4 オーダーブックの取引エンジン
図4 オーダーブックの取引エンジン

 このデータ構造のコードを以下のリストに示します。

class OrderEntry 
{
    private boolean active;
    private double price;
    private long quantity;
    private StringBuffer symbol;
    private int type;
}

class SubBook 
{
    LinkedList<OrderEntry> buy;
    LinkedList<OrderEntry> sell;
}

HashMap<StringBuffer,SubBook> orderBook = 
    new HashMap<StringBuffer,SubBook>(INITIAL_CAPACITY);

 以下のリストは、OrderManagerのタイムクリティカルなループを標準Javaで作成したものです。ここでは、オーダーブックを最新の株価と比較します。

public class OrderManager implements Runnable
{
  ...

  public void run()
  {
// 株価を注文価格と比較するループ
    while ( true ) 
    {
      for ( int i = 0; i < orderBookKeys.length; i++ )
      {
        StringBuffer symbol = orderBookKeys[i];
        StringBuffer sPrice = marketMgr.marketBook.get(symbol);
        if ( sPrice == null )
          continue;

        double marketPrice = 
          Double.parseDouble( sPrice.toString() );

// この銘柄コードのサブブックを取得する
        SubBook sb = orderBook.get(symbol);

// 売り注文リストを検索する
        for ( int x = 0; x < sb.sell.size(); x++ )
        {
          OrderEntry entry = sb.sell.get(x);
          if ( checkForTrade( entry, marketPrice ) == true )
            break;
        }

// 買い注文リストを検索する
        for ( int x = 0; x < sb.buy.size(); x++ )
        {
          OrderEntry entry = sb.buy.get(x);
          if ( checkForTrade( entry, marketPrice ) == true )
            break;
        }
      }
Thread.sleep(1); // 1ミリ秒スリープする
    }
  }
  
  ...
}

 1ミリ秒ごとにそれぞれの株の売り注文と買い注文が、市場の株価と比較されます。条件を満たす場合は、取引が執行されます。このデモでは、取引に関するメッセージが送信され、指値と約定した価格の差がユーザーインターフェイスコンポーネントによって画面にチャートとして表示されます。ほとんどの場合、差はゼロですが、ガベージコレクションの実行による休止時間が差(損失)を生む可能性があります。

 ループのリアルタイムバージョンは、上記のコードとほとんど同じです。ちょっとした変更として、Thread.sleepの呼び出しをRealtimeThread.waitForNextPeriodに置き換えます(以下のリストを参照)。この変更には、大きく2つのメリットがあります。

  1. RealtimeThreadの作成時に指定したとおりの時間でループが正確に実行されます。つまり、1ミリ秒の時間を指定し、処理に250マイクロ秒かかった場合は、waitForNextPeriodが正確に750マイクロ秒間ブロックします。これにより、コードは確実に1ミリ秒間隔で実行されます。標準Javaでは、この動作を実現できません。
  2. waitForNextPeriodの呼び出しにはJava RTSの高分解能タイマーを利用して、標準Javaに組み込まれているタイマーよりも高い精度で処理を行います。
public class OrderManager implements Runnable
{
  ...

  public void run()
  {
// 株価を注文価格と比較するループ
    while ( true ) 
    {
      for ( int i = 0; i < orderBookKeys.length; i++ )
      {
        StringBuffer symbol = orderBookKeys[i];
        StringBuffer sPrice = marketMgr.marketBook.get(symbol);
        if ( sPrice == null )
          continue;

        double marketPrice = 
          Double.parseDouble( sPrice.toString() );

// この銘柄コードのサブブックを取得する
        SubBook sb = orderBook.get(symbol);

// 売り注文リストを検索する
        for ( int x = 0; x < sb.sell.size(); x++ )
        {
          OrderEntry entry = sb.sell.get(x);
          if ( checkForTrade( entry, marketPrice ) == true )
            break;
        }

// 買い注文リストを検索する
        for ( int x = 0; x < sb.buy.size(); x++ )
        {
          OrderEntry entry = sb.buy.get(x);
          if ( checkForTrade( entry, marketPrice ) == true )
            break;
        }
      }

// 次の処理サイクルの開始まで待機する
      RealtimeThread.waitForNextPeriod();
    }
  }
  
  ...
}

 どちらのバージョンのOrderManagerもRunnableインターフェイスを実装して、スレッドの作成を要求します。リアルタイムバージョンの取引エンジンではjavax.realtime.RealtimeThreadが作成されますが、標準Javaバージョンではjava.lang.Threadです(以下のリストを参照)。RealtimeThreadを作成するときに時間と優先順位を指定しますが、この優先順位はJava RTSに本当に実装されているものです。

// 実行可能コードを作成する
OrderManager orderMgr = new OrderManager();

// スレッドの優先順位を決定する
long maxPriority = 
    PriorityScheduler.instance().getMaxPriority();
PriorityParameters sched = 
    new PriorityParameters( maxPriority );

// スレッドの時間を決定する
RelativeTime one_millisecond = 
    new RelativeTime(1,0);
PeriodicParameters period  = 
    new PeriodicParameters(one_millisecond);

// リアルタイムスレッドを上記のパラメータで作成する
RealtimeThread orderThread = 
    new RealtimeThread(sched,period,null,null,null,orderMgr);

次のページ
デモの実行結果とチャート

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Eric Bruno(Eric Bruno)

ニューヨークを拠点とするコンサルタント。大規模なWebアプリケーション、データベースシステム、リアルタイムトランザクションシステムをJavaとC++で開発した経験を持つ。詳しくは個人ページhttp://www.ericbruno.com/を参照。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/1866 2007/11/22 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング