cAlgoの基礎知識
この記事ではcAlgoを用いたプログラミングの基礎知識をご紹介します。
cTrader 3.6に対応しています。
目次
プログラミング言語はC#
MT4の独自言語であるMQL4と違い、cTraderではC#を使いインジケーターと自動売買プログラムを作成します。
cAlgoはcTraderの開発元であるSpotwareが提供しているC#のAPIの一種です。
このAPIを使い、cTrader上で動くプログラムを作成します。
MT4上でのみにしか使われないMQL4と違い、C#は様々な場面で使われている言語な為、情報の収集が比較的容易です。
また、通常のC#でのプログラミングで使える機能は全て使用できます。
開発環境
各プロジェクトのビルドはcTraderのAutomateタブか、Visual Studioより行います。
cTraderからのビルド
cTraderの左側のメニューよりAutomateを選択し、ビルドしたいプロジェクトを選択します。
+ボタン左隣のビルドボタンをクリックするか、右クリックしてビルドをクリックするとビルドされます。
Ctrl+Bのホットキーでもビルドを行えます。
ソースコード書き込みとビルドの違い
右クリックして表示されるメニューにあるビルドは二種類あります。
通常のビルドの場合はソースコードは平文でファイルに書き込まれず、cTrader上でのみ動作する様にコードが暗号化されます。
ソースコード書き込みを選択した場合はファイルにソースコードが書き込まれ、利用するユーザーに中身が丸見えの状態になります。
ビルドした環境に限り、通常のビルドをした場合でもコードは常に表示されます。
Visual Studioからのビルド
統合開発環境(IDE)であるVisual Studioをコンパイラとして使う事ができます。
cTraderに搭載されているエディタはお世辞にも使い勝手が良いとは言えず、特に理由が無ければVisual Studioのご利用をおすすめします。
Visual Studioがインストールされた状態で、プロジェクトを右クリック→”Visual Studioを使用しないでください”を選択します。
原文は”Edit in Visual Studio”なので、これは誤訳です。
VSIXのインストーラーが実行されるので、指示に従いインストールします。
以上で下準備は完了し、以後同じ動作をするとVisual Studioが開かれます。
Visual Studioの上部にあるビルド→ソリューションのビルドを選択するとビルドが実行されます。
.algoファイルが生成されていれば問題なくビルドに成功しており、cTraderは自動的にそのファイルを読み込みます。
定義へ移動
Visual Studioでは各APIを選択してF12、もしくは右クリックして表示されるメニューから定義へ移動をクリックするとそのAPIの定義を確認する事ができます。
地味な機能ですが、この機能のおかげでいちいちオンライン上でドキュメントを漁る手間が省け、時間の節約になります。
クラスのプロパティ
クラス宣言の前に[Indicator]もしくは[Robot]の属性を付加する事により、主に五つのプロパティを設定できます。
1 2 3 4 5 |
namespace cAlgo { [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)] public class NewIndicator : Indicator { |
1 2 3 4 5 |
namespace cAlgo.Robots { [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)] public class NewcBot : Robot { |
ScalePrecision
インジケーターでのみ設定できます。
intを取ります。
チャートの右側に表示される価格の桁を指定します。
IsOverlayがtrueの場合は反映されません。
IsOverlay
インジケーターでのみ設定できます。
boolを取ります。
trueに設定すると、チャート画面にオーバーレイとして表示され、falseの場合はチャートの下画面に個別のエリアが与えられます。
AutoRescale
インジケーターでのみ設定できます。
boolを取ります。
チャート画面のスケール変更を許可するかどうかを指定します。
TimeZone
インジケーターとcBotで設定できます。
stringを取ります。
TimeZonesクラスに対応しています。
DataTimeの時間がここで指定したタイムゾーンになります。
TimeZones.TokyoStandardTimeを指定すると、DataTimeは全て日本時間に変換されますが、特定のタイムゾーンのDateTimeを取りたい時以外は特に設定する必要はありません。
DataTimeのToLocalTime()を必要な時に使えば事足ります。
AccessRights
インジケーターとcBotで設定できます。
AccessRightsを取ります。
ここで指定したAccessRightsによって、出来る事が限られます。
権限が足らない場合、SecurityExceptionの例外がスローされます。
cTraderのAutomateタブより実行している時に限り、例外が発生した場合は下のログタブから確認できます。
None以外のAccessRightsが設定されている場合、チャートへ追加時に確認のダイアログが表示されます。
AccessRights.None
何の権限も指定しません。
cAlgoのみで完結する場合はこちらの権限で問題なく動作します。
AccessRights.FileSystem
ファイルへの干渉を可能にします。
AccessRights.Internet
インターネット接続を可能にします。
AccessRights.Registry
レジストリの操作を可能にします。
AccessRights.FullAccess
何の制限も無い最も強い権限です。
winapiのインポートやウィンドウの作成等、.Net Frameworkの機能を際限なく使用する事が可能になります。
パラメーターの入力
各パラメーターはクラスのメンバとして宣言され、get,setアクセサを持っている必要があります。
[Parameter]の属性も必須です。
六つのプロパティが用意されています。
クラスのプロパティと違い、パラメーターのプロパティはcBotでもIndicatorでも同じです。
1 2 |
[Parameter("移動平均線の期間", DefaultValue = 10, MinValue = 0, MaxValue = 120)] public int MAPeriod { get; set; } |
パラメーターの名前
一番最初にstringを入れると表示されるパラメーター名を変更できます。
指定しない場合、変数名がそのまま表示されます。
DefaultValue
デフォルトの値を指定します。
MinValue
最小値を指定します。
この数値以下に設定する事はできなくなります。
MaxValue
最大値を指定します。
この数値以上に設定する事はできなくなります。
Step
矢印ボタンをクリックした時の変動幅を指定します。
Group
同じstringをGroupに指定し、パラメーターのグループ分けをする事ができます。
1 2 3 4 5 |
[Parameter("一本目の期間", DefaultValue = 10, MinValue = 0, MaxValue = 120, Group = "移動平均線の期間")] public int MAPeriod1 { get; set; } [Parameter("二本目の期間", DefaultValue = 20, MinValue = 0, MaxValue = 120, Group = "移動平均線の期間")] public int MAPeriod2 { get; set; } |
この様に書くと、パラメーターの入力画面ではグループ毎にまとめられて表示されます。
チャートに計算結果を表示する
パラメーターの入力と同じように、IndicatorDataSeriesの前に[Output]属性を付与する事で、チャート上に計算結果を表示させる事ができるようになります。
[Output]属性はインジケーターでのみ有効です。
設定可能なプロパティの数も同じく六つあります。
LineColor,LineStyle,Thicknessの三つはパラメーター入力画面でユーザーが設定できます。
1 2 |
[Output("MA1", PlotType = PlotType.Points, LineColor = "Red", Thickness = 3)] public IndicatorDataSeries MA1Result { get; set; } |
Outputの名前
stringを取ります。
一番最初にstringを入れるとパラメーター入力画面で表示されるOutput名を変更できます。
パラメーターの名前と違い、設定は必須です。
LineStyle
LineStyleというenumを取ります。
ラインの表示の仕方を設定できます。
ユーザーが後から設定を変更する事もできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public enum LineStyle { // // 概要: // A solid line: ----- Solid = 0, // // 概要: // A dotted line: ..... Dots = 1, // // 概要: // A dotted line, large gap between dots: . . . . DotsRare = 2, // // 概要: // A dotted line, extra large gap between dots: . . . . DotsVeryRare = 3, // // 概要: // A mixed line / dot style is used to render the line: - . - . - . LinesDots = 4, // // 概要: // Lines with gaps are used to render the line: - - - - Lines = 5 } |
LineColor
stringを取ります。
ラインの色を指定します。
ここで指定する色はデフォルトの色なだけで、ユーザーはパラメーター入力画面より好きな色に変更できます。
cAlgoにはColorクラスが用意されていますが、これをそのまま指定する事はできません。
“Red”,”Blue”等、stringとして色を直接指定する必要があります。
Thickness
floatを取ります。
ラインの太さを指定します。
IsHistogram
boolを取ります。
ヒストグラムとして表示するかどうかを指定します。
PlotType
PlotTypeというenumを取ります。
ラインの見た目を変更します。
ユーザーが設定を変更する事はできません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public enum PlotType { // // 概要: // Plot Indicator result as a line. Line = 0, // // 概要: // Plot Indicator result as a histogram. Histogram = 1, // // 概要: // Plot Indicator result as a sequence of points. Points = 2, // // 概要: // Plot Indicator result as a line with breaks where there are no values in the // IndicatorDataSeries. DiscontinuousLine = 3 } |
IndicatorDataSeriesに計算結果を入れる
Calculateメソッド内でindexを元に計算結果を入れればチャートへ表示されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[Output("MA1", LineColor = "White")] public IndicatorDataSeries MA1Result { get; set; } [Output("MA2", LineColor = "Yellow")] public IndicatorDataSeries MA2Result { get; set; } private MovingAverage MA1, MA2; protected override void Initialize() { //MA1とMA2を初期化 計算の元となるデータを終値とし、期間と移動平均線の種類を指定 MA1 = Indicators.MovingAverage(MarketSeries.Close, 10, MovingAverageType.Simple); MA2 = Indicators.MovingAverage(MarketSeries.Close, 20, MovingAverageType.Simple); } public override void Calculate(int index) { //チャートに移動平均線を表示する為のIndicatorDataSeriesにそれぞれの移動平均線の計算結果を代入 MA1Result[index] = MA1.Result[index]; MA2Result[index] = MA2.Result[index]; } |
主なイベントハンドラ
cAlgoのプログラミングは、ある一定のイベントが発生した時に呼び出されるイベントハンドラを定義して行います。
様々なイベントハンドラが用意されていますが、ここではcTraderのAutomateタブより追加ボタンを押した時に生成されるコードを参考に基礎的なハンドラの解説を行います。
OnStart()
cBotのイベントハンドラです。
チャートに追加された時に一度だけ呼び出されます。
各インジケーターの初期化や、ユーザーから入力されたパラメーターを元に何かの計算をここで行います。
1 2 3 4 5 6 7 8 |
//移動平均線のオブジェクトを宣言 private MovingAverage sma; protected override void OnStart() { //移動平均線の初期化 左から順番に、計算の元となるデータ、期間、移動平均線の種類 sma = Indicators.MovingAverage(MarketSeries.High, 2, MovingAverageType.Simple); } |
OnTick()
cBotのイベントハンドラです。
ティック(値動き)毎に呼び出されます。
通常OnTick()かOnBar()にロジックを書きます。
1 2 3 4 5 6 7 |
protected override void OnTick() { //値動きがある度に売値を表示 Print(Symbol.Bid); //値動きがある度に買値を表示 Print(Symbol.Ask); } |
OnBar()
cBotのイベントハンドラです。
新しい足が生成される度に呼び出されます。
OnTick()と同じ用途ですが、OnTick()を使ったcBotは大抵、スプレッドやスリッページの僅かな差によって結果が大きく変わるロジックばかりで、そういったcBotはバックテストでは成績が良くとも実際のトレードでは成績が振るわないという事になりやすいです。
そういったロジックを避けて、なるだけこちらのOnBar()を使ったcBotの作成をおすすめします。
1 2 3 4 5 |
protected override void OnBar() { //新しい足が表示される度に現在時刻を表示 Print(Server.Time.ToLocalTime()); } |
OnStop()
cBotのイベントハンドラです。
cBotが停止される時に一度だけ呼び出されます。
cTraderを直接終了した場合には呼び出されず、停止ボタンをクリックした時のみ呼び出されます。
1 2 3 4 5 |
protected override void OnStop() { //cBot の停止を通知 Print("cBot stopped"); } |
OnError()
cBotのイベントハンドラです。
名前からはわかりにくいですが、注文時にエラーが発生した時にのみ呼び出されます。
プログラムのクラッシュ時やアクセス権限が足らなかった時には呼び出されません。
1 2 3 4 5 6 7 8 9 |
protected override void OnError(Error error) { //エラーメッセージを表示 Print(error.Code); //資金不足により注文に失敗した場合はcBotを停止させる if (error.Code == ErrorCode.NoMoney) Stop(); } |
Initialize()
インジケーターのイベントハンドラです。
cBotのOnStart()と同じく、チャートに追加された時に一度だけ呼び出されます。
使用するインジケーターの初期化処理等を行います。
1 2 3 4 5 6 7 8 9 |
//移動平均線のオブジェクトを宣言 private MovingAverage MA1, MA2; protected override void Initialize() { //MA1とMA2を初期化 計算するデータを終値とし、パラメーターより期間と移動平均線の種類を指定 MA1 = Indicators.MovingAverage(MarketSeries.Close, MAPeriod1, MA1Type); MA2 = Indicators.MovingAverage(MarketSeries.Close, MAPeriod2, MA2Type); } |
Calculate(int index)
インジケーターのイベントハンドラです。
cBotのOnTick()と同じく、値動きがあった時に呼び出されます。
インジケーターのロジックをここに書きます。
1 2 3 4 5 6 |
public override void Calculate(int index) { //チャートに移動平均線を表示する為のIndicatorDataSeriesにそれぞれの移動平均線の計算結果を代入 MA1Result[index] = MA1.Result[index]; MA2Result[index] = MA2.Result[index]; } |
cBotのコメント付きサンプルコード
cTraderに元から入っているSample RSI cBotはOnTick()を使い、値動きがある度に評価します。
Relative Strength Indexの値が30と70になった時に買いか売りのポジションを閉じ、それと同時にその閉じたポジションの逆のポジションを取ります。
ソースコードにコメントをつけたものがこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
//cAlgoを利用する為のネームスペース using cAlgo.API; //インジケーターを使う場合に必要なネームスペース using cAlgo.API.Indicators; //cAlgoのネームスペース namespace cAlgo { //次の宣言がRobot(cBot)である事を伝えるRobot属性 TimeZoneとAccesRightsはデフォルトのまま [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)] //Robot(cBot)クラスの宣言 public class SampleRSIcBot : Robot { //RSIの初期化に使用するパラメーター //計算の元となるデータ DataSeriesは、Open(始値),High(高値),Low(安値),Close(終値)の四つが選択可能 [Parameter("Source")] //get,setアクセサにより、ユーザーからの参照と入力を可能にする public DataSeries Source { get; set; } //RSIの初期化に使用するパラメーター //期間 型はintで、デフォルトの値は14 [Parameter("Periods", DefaultValue = 14)] public int Periods { get; set; } //トレードするロット数 最低値は0.01、パラメーター入力画面の矢印をクリックすると0.01ずつ変動 [Parameter("Quantity (Lots)", DefaultValue = 1, MinValue = 0.01, Step = 0.01)] public double Quantity { get; set; } //RSIのオブジェクト宣言 private RelativeStrengthIndex rsi; // cBotの初期化時に呼び出されるOnStart // overrideと書く事で、Robotクラスの実装を上書きする protected override void OnStart() { //RSIの初期化 計算の元となるデータと期間を引数に取る rsi = Indicators.RelativeStrengthIndex(Source, Periods); } //ティック(値動き)がある度に呼び出される protected override void OnTick() { //値動きがある度にチェックされる //rsiの値が30以下の場合 if (rsi.Result.LastValue < 30) { //この条件文では、rsiの値が30以下になったら、売りのポジションを閉じて買いのポジションを取る //引数に決済したいトレードのタイプ(TradeType.BuyかTradeType.Sellか)を入れる Close(TradeType.Sell); //引数に注文したいトレードのタイプ(TradeType.BuyかTradeType.Sellか)を入れる Open(TradeType.Buy); } //値動きがある度にチェックされる //rsiの値が70以上の場合 else if (rsi.Result.LastValue > 70) { //この条件文では、rsiの値が70以上になったら、買いのポジションを閉じて売りのポジションを取る //引数に決済したいトレードのタイプ(TradeType.BuyかTradeType.Sellか)を入れる Close(TradeType.Buy); //引数に注文したいトレードのタイプ(TradeType.BuyかTradeType.Sellか)を入れる Open(TradeType.Sell); } } //引数にTradeTypeを取り、そのTradeTypeを元に、このcBotが注文したポジションを閉じる private void Close(TradeType tradeType) { //foreach文を使い、Positions.FindAllを元に閉じたいポジションを全て取得する //ここでのPositions.FindAllの引数は、最初がこのcBotが注文する時に付与するポジションのラベル,次にSymbol(通貨)の名前,そして最後にCloseの引数であるtradeTypeとなる foreach (Position position in Positions.FindAll("SampleRSI", Symbol.Name, tradeType)) //取得したpositionをClosePositionにより閉じる ClosePosition(position); } //引数にTradeTypeを取り、そのTradeTypeを元に、ポジションを取る private void Open(TradeType tradeType) { //既にtradeTypeの方向にこのcBotが取ったポジションがあるかどうかを確認する為に、ポジションを探す Position position = Positions.Find("SampleRSI", Symbol.Name, tradeType); //パラメーターで入力した数値を元に、注文をするExecuteMarketOrderの引数の合う数値に変換する double volumeInUnits = Symbol.QuantityToVolumeInUnits(Quantity); //Positions.Findはポジションが見つからなかった時はnullを返すので、もしpositionがnullだった場合、ポジションを取る if (position == null) //注文を出す為のAPI ここでの引数は初めにトレードの方向(買いか売りか),Symbol(通貨の名前),注文の数量,後にポジションを探す為のラベル("SampleRSI")となる ExecuteMarketOrder(tradeType, Symbol.Name, volumeInUnits, "SampleRSI"); } } } |