Outline

本記事では本ソフトウェアの概要を述べる. とくに,本ソフトウェアを使い始めるにあたり,最初に参照するであろう,マーケット,エージェント,そしてメインの骨格を紹介する.

留意事項,実装ガイドライン

以下は,本ソフトウェアを使用し,実装を進める上でのガイドラインである.

多くのフィールドは変更可能(var)だが. 多くのフィールドは変更可能(var)であるが,基本的には独自作成したクラスのフィールドでない限り,派生クラス側で書き換えること(再代入)は望ましくない. とくにプリミティブ型でない List[T]Map[T,U] などは基本クラスのコンストラクタで初期化し,以後,派生クラス側では書き換えないことが望ましい. たとえば,market.idagent.id なども書き換え可能であるが,書き換えた場合のシミュレーションの挙動は保証されない.

多くのメソッドは public だが. 多くのメソッドは public で定義されているが,だからといって,常識的な範囲を超えてアクセスすることは望ましくない. たとえば,マーケットはオーダーブックを保持し,Market#getOrderBook() を介してオーダーブック本体にアクセスできる. オーダーブックのすべてのメソッドを呼び出せるので,あるエージェントがオーダーブックを全消しするなどできるが,人工市場を用いた取引システムのバグの影響でもテーマにしない限り,そんなことをする理由はないだろう.

多くのフィールドは public だが. 多くのフィールドおよびメソッドは public で定義されているが,フィールドへの直接アクセスは可能であれば避けるのが望ましい. とくに plham 直下に配置される本ソフトウェアの核をなすクラス群(plham/*.x10)は仕様変更される可能性が高いので,メソッドを使用することを推奨する. たとえば,submitOrders() が呼び出される度にあるエージェントがマーケットの市場価格を書き換えたりできるが,人工市場を用いた金融サイバーテロのシミュレーションでもテーマにしない限り,そんなことをする理由はないだろう.

シミュレーションの流れ

Plham を用いた人工市場シミュレーションは基本的には2つのメソッド

を特定のスケジュールで呼び出すことで進行する.

Agent#submitOrders(List[Market]) では,エージェントはマーケットの価格情報などから注文を決定し,このメソッドの戻り値として注文のリストを返す. このメソッドがエージェントが注文をだす唯一の方法となるべきである. このメソッドの戻り値 List[Order] のなかにキャンセル要求 Cancel を含めることが可能である.

Market#handleOrders(List[Order]) では,マーケットはエージェントから集められた注文を処理し,約定が成立した場合にはエージェントの資産情報を更新する. このメソッドがマーケットが注文を受け付ける唯一の方法となるべきである. これらのメソッドはスケジューラによって自動的に呼び出されるので,ユーザが明示的に呼び出すことはない.

上記の例からもわかる通り,各クラスに定義されているメソッドには「暗黙のアクセス制限」がある. 暗黙のアクセス制限はクラス間の関係で想定される. たとえば,「エージェントはマーケットの何を参照許可されるか」などである. 本記事では,Plham で想定されている「エージェント」,「マーケット」,「システム」の間のアクセス制限について述べる(サンプルコードも活用してほしい). まずは各クラスを紹介する.

マーケット

マーケットは plham/Market.x10 に実装されている.

マーケットの基本的な機能は以下である.

各機能に対応したメソッドを以下に示す.

// plham/Market.x10
public class Market {

    public def handleOrders(orders:List[Order]) {}       // 注文の処理(約定)

    public def getTime():Long {}                         // ステップ時間の取得

    public def getPrice(t:Long):Double {}                // ステップ t の市場価格
    public def getFundamentalPrice(t:Long):Double {}     // ステップ t の理論価格
}

注文の処理は handleOrders() メソッドに実装する. これがユーザがマーケットを作成するときの主要な作業内容である.

留意点として,モデルによってはマーケットごとにステップ時間が異なる可能性がある. そのため,すべてのマーケットで getTime() が一致しているという前提で意思決定 を実装することは望ましくない.

エージェント

エージェントは plham/Agent.x10 に実装されている.

エージェントの基本的な役割は以下である.

以下にエージェントクラスの概要を示す.

// plham/Agent.x10
public class Agent {

    public def submitOrders(markets:List[Market]):List[Order] {}    // 注文の決定(複数銘柄)

    public def isMarketAccessible(market:Market) {}                // そのマーケットで取引するか
    public def setMarketAccessible(market:Market) {}               // そのマーケットで取引するか

    public def getCashAmount():Double {}                           // 現金額の取得
    public def getAssetVolume(market:Market):Long {}               // 株式数の取得
}

注文の決定は submitOrders() メソッドに実装する. これがユーザがエージェントを作成するときの主要な作業内容である.

エージェントの保有する現金額,株式数はマーケットが注文を処理する過程で自動的に更新される.

エージェントの実装例は plham.agent.FCNAgent を参考にしてほしい.

システム

システムは,Agent#submitOrders() および Market#handleOrders() の呼び出しを含む,シミュレーションの進行に伴うスケジューリングを行う. また,システムは下記のメイン(Main)を含み,そこではユーザはシミュレーションに必要なインスタンスを生成したり,シミュレーションの初期状態を設定できる.

メイン

通常,ユーザは plham.Main を継承した独自のメインクラスを作成する. その際,以下のメソッドをオーバーライドし,JSON ファイルと連携しつつ,シミュレーションで使用したいインスタンスを生成すればよい(JSON との連携方法はここでは説明しないので,チュートリアルを参考にしてほしい).

// plham/Main.x10
public class Main extends Simulator {

    public def createMarkets(json:JSON.Value):List[Market] {}
    public def createAgents(json:JSON.Value):List[Agent] {}
    public def createEvents(json:JSON.Value):List[Event] {}
}

エージェントを実装するためのマーケットの API

エージェントは,マーケットから価格時系列や気配値の情報などを読み取る. 現実ではエージェントはマーケットの内部変数を書き換えることはできないので,シミュレーション上でもそうすべきではない.

マーケットがもつメソッドのうち,エージェントの submitOrders() で利用推奨なメソッドを示す.

// plham/Market.x10
public class Market {
	id:Long;                                // ID 番号
	name:String;                            // 名前(JSON で定義された)
	getTime():Long;                         // 時間ステップ
	getPrice(t:Long):Double;                // 市場価格
	getFundamentalPrice(t:Long):Double;     // ファンダメンタル価格
	getBestBuyPrice():Double;               // 買い気配値
	getBestSellPrice():Double;              // 売り気配値
	getMidPrice():Double;                   // 仲値
    getTradeVolume(t:Long):Long;            // 出来高(約定単位数)
	isRunning():Boolean;                    // マーケットが開いているか?
	getOutstandingShares():Long;            // 時価総額シェア(時価総額荷重指数にのみ使用)
	getTickSize():Double;                   // ティックサイズ
	roundBuyPrice(price:Double):Double;     // 価格をティックサイズに丸める
	roundSellPrice(price:Double):Double;    // 価格をティックサイズに丸める
}

次に,利用非推奨なメソッド(類似の名をもつメソッドも非推奨)の一部を示す.

// plham/Market.x10
public class Market {
	handleOrders(orders:List[Order]);    // 非推奨
	handleOrder(order:Order);            // 非推奨
	cancelOrder(order:Order);            // 非推奨
}

マーケットを実装するためのエージェントの API

マーケットはシステムから与えられた注文を処理し,その途中でエージェントの資産を更新する.

エージェントがもつメソッドのうち,マーケットの submitOrders() で利用推奨なメソッドを示す.

// plham/Agent.x10
public class Agent {
	id:Long;                               // ID 番号
	name:String;                           // 名前(JSON で定義された)
	getCashAmount():Double;                // 現金量
	getAssetVolume(market:Market):Long;    // 株式数
	updateCashAmount(delta:Double);                    // 現金量を更新
	updateAssetVolume(market:Market, delta:Double);    // 株式数を更新
	orderExecuted(market:Market, orderId:Long, price:Double, cashAmountDelta:Double, assetVolumeDelta:Long);
}

次に,利用非推奨なメソッド(類似の名をもつメソッドも非推奨)の一部を示す.

// plham/Agent.x10
public class Agent {
	submitOrders(markets:List[Market]):List[Order];    // 非推奨
}

システムを実装するためのマーケットとエージェントの API

一部のみ示すが,繰り返し述べているとおり,Agent#submitOrders() および Market#handleOrders() を確実に呼び出すことが重要である.

// plham/Market.x10
public class Market {
	handleOrders(orders:List[Order]);
	handleOrder(order:Order);
}
// plham/Agent.x10
public class Agent {
	submitOrders(markets:List[Market]):List[Order];
}