cTraderでEA作成 ~単純論理で利益は出るのか~EA作成編

FXの自動売買をする際に利用されるツールとしてMTシリーズとcTraderが候補として挙がると思う。
ただ実際はユーザ数の多さからMTシリーズばっかりだになっており、
cTraderに関する情報は少ない、、、
今回は簡単な取引を実装し
・cTraderを用いたEAはかなり簡単に作成できる
・MTシリーズには無い自動的にパラメータを調整してくれる機能
を紹介しようと思う。

環境

まず私が取引に利用する環境を下記の通り説明する。
・ブローカ:TradeViewForex
・口座通貨:US Doller
・レバレッジ:400倍
・ロスカット:100%

また今回取引する対象は下記となる。
・通貨ペア:XAUUSD
・証拠金:1,000$
・取引最小ロット:1Oz(0.01lot)

EA仕様

今回は下記インジケータを利用する。
・Bollinger Band(BB) X2 発注タイミングとロスカットタイミング用にそれぞれ用意する。
※以降インジケータ名は( )内の名称に省略し記載する。

発注・決済・損切は下記論理に基づき実施する。
発注
・足の終わり値がBBのtop/bottomの値を一度超過し再度BBの範囲内に戻ってきた時、反対方向のBBのtop/bottomへ向け発注する。

決済
・ポジションの利確方向のBBの値を一度超過し再度BBの範囲内に戻ってきた時

損切
・足の終わり値がロスカット用BBを超過した時

cTraderの前提知識

実際にEAを作成する前にcTraderにてシステムを構築する前提条件を書いていく
・EAをcTraderでは
 ・実際に取引するcbot
 ・インジケータのみをIndicater
    ・2つを相称してcalgoと呼ぶ
・開発言語はC#をベースにした独自仕様(ほとんどC#で出来る事は出来る、EA用にライブラリが追加されている程度)
・リファレンスは下記サイト参照
https://ctrader.com/api/reference/
・visual stadio用プラグインが有る為VSにて開発可能

cbotテンプレート

cbotを新規作成した時のテンプレートは下記となる。

  Τ using System;
  | using System.Linq;
① | using cAlgo.API;
  | using cAlgo.API.Indicators;
  | using cAlgo.API.Internals;
  ⊥ using cAlgo.Indicators;
   namespace cAlgo.Robots
    {
②       [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
③       public class NewcBot : Robot
        {
④ Τ       [Parameter(DefaultValue = 0.0)]
  ⊥       public double Parameter { get; set; }
⑤ Τ       protected override void OnStart()
  |       {
  |        // Put your initialization logic here
  |       }
  |      protected override void OnTick()
  |       {
  |        // Put your core logic here
  |       }
  |       protected override void OnStop()
  |       {
  |        // Put your deinitialization logic here
  ⊥       }
         }
     }

①インポート部分 cAlgoで利用するライブラリ・パッケージを呼び出している。
基本的に変更は不要
自作ライブラリやインジケータを呼び出したい時に追記する。

②cbot自体のパラメータ設定
 ・システムが利用するタイムゾーン ※デフォルトでは世界標準時を指定
 ・cbotに許可する権限を記載  ※デフォルトではアクセスのみ許可されている。

③cbotのメインクラス
 この中で各イベント時の処理及び基本的な処理を記載していく

④パラメータ定義
 コンパイル後適宜修正したパラメータとかを定義する。

⑤イベント事の処理を記載
 テンプレートでは上から
 ・OnStart :cbot起動時に処理させる内容を記載する。
 ・OnTick:tickをcTraderにて取得出来たタイミングで実施させたい処理を記載する。
 ・OnStop:cbot停止時に処理させる内容を記載する。

また上記以外に
 ・Onbar:グラフで新しい駒に移動した時に処理させたい内容を記載
とか在る。それ以外にも使うかもだけど他は適宜説明する。
また当領域に独自イベントを定義し処理を実施する事が出来る
またまたMTシリーズではポジション生成・決済時のイベントが定義されていたがcalgoではそれらはイベントではなく関数で定義されている。

作成するEAの構造

上記テンプレートを基にし今回作成するEAの構造は下記となる。

  Τ using System;
  | using System.Linq;
  | using cAlgo.API;
① | using cAlgo.API.Indicators;
  | using cAlgo.API.Internals;
  ⊥ using cAlgo.Indicators;
  namespace cAlgo.Robots
   {
②     [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
③      public class SimpleTrade : Robot
      {
④ Τ    [Parameter(DefaultValue = 0.0)]
  ⊥    public double Parameter { get; set; }
⑤ Τ    protected override void OnStart()
  |    {
  |        // Put your initialization logic here
  |    }
  |    protected override void OnTick()
  |    {
  |        // Put your core logic here
  |    }
  |    protected override void OnBar()
  |    { }
  |    protected override void OnStop()
  |    {
  |        // Put your deinitialization logic here
  |    }
  |    private void Open()
  |    { }
  |    private void Close()
  ⊥    { }
   }
}


①インポート部分 :テンプレートから変更無し
②cbot自体のパラメータ設定:テンプレートから変更無し
③cbotのメインクラス:都度変更する。
⑤パラメータ定義:今回BB用のパラメータ等はパラメータとして定義する為修正する
⑥イベント事の処理を記載
 テンプレートから下記を追加
 ・OnBar:グラフ生成事に処理を追加する。
 ・Open:発注処理を関数として定義する。 
 ・Close:決済処理を関数として定義する。


cbot作成 ⑤パラメータ設定

       [Parameter("Periods", DefaultValue = 20, Group = "BollingerBands Atack")]
       public int BB_A_Periods { get; set; }
       [Parameter("Standerd Dev", DefaultValue = 1.8, Group = "BollingerBands Atack")]
       public double BB_A_Dev { get; set; }
       [Parameter("Souese", Group = "BollingerBands Atack")]
       public DataSeries BB_A_Sourse { get; set; }

       [Parameter("MA Type", Group = "BollingerBands Atack")]
       public MovingAverageType BB_A_MAType { get; set; }
       // 
       [Parameter("Periods", DefaultValue = 20, Group = "BollingerBands loss")]
       public int BB_L_Periods { get; set; }
       [Parameter("Standerd Dev", DefaultValue = 1.8, Group = "BollingerBands loss")]
       public double BB_L_Dev { get; set; }
       [Parameter("Souese", Group = "BollingerBands loss")]
       public DataSeries BB_L_Sourse { get; set; }

       [Parameter("MA Type", Group = "BollingerBands loss")]
       public MovingAverageType BB_L_MAType { get; set; }

パラメータの構文は下記となる。

       [Parameter(名称, DefaultValue =初期値 ,Group = グループ名)]
       public 変数の型 変数名 { get; set; }

・名称はGUI上に表示させる時の名前
・初期値は、cTraderにインポートした際のデフォルトの値が与えられる
・グループはパラメータを複数のグループに分類する際に利用する。
 今回のスクリプトでは
  ・BollingerBands Atack
    ・BollingerBands loss
 でそれぞれ発注・決済用のBBとロスカット用BBで分類している。

またパラメータを設定した後にそのパラメータをプログラム内で認識する為の変数を準備している。

また今回はパラメータ設定後に今回利用する変数を定義している。

cbot作成 ⑥OnStart設定

        protected override void OnStart()
       {
①           base.OnStart();
           // BBの呼び出し
②Τ         BB_A = Indicators.BollingerBands(BB_A_Sourse,BB_A_Periods,BB_A_Dev,BB_A_MAType);
 ⊥         BB_L = Indicators.BollingerBands(BB_L_Sourse, BB_L_Periods, BB_L_Dev, BB_L_MAType) ;
           // BBの値から超過しているかの判定
③Τ         Flag_Top = ((BB_A.Top.LastValue - Bars.ClosePrices.LastValue) > 0);
 ⊥         Flag_bottom = ((BB_A.Bottom.LastValue - Bars.ClosePrices.LastValue) < 0);

           // 取引通貨ペア名の格納
④          SymbleName = Bars.SymbolName;

       }

①は定型文ではあるが OnStartのイベントを呼びだしている。
②では今回利用するBBを呼びだしている。
 calgoでは主要なインジケータは関数として定義されている為、予備だすだけで利用できる。
③では今回の発注、決済判定用に利用するフラグを定義している。
 また為替にて利用する値等は簡単に取得できる様になっている。
④今回取引する通貨ペアをcbot実行時に表示されているグラフの通貨ペアを取得する事で定義している。 

cbot作成 ⑥OnBar作成

        protected override void OnBar()
       {
     ①Τ    // 決済判定
      |    // 買い判定
      |    if ((Flag_Top) && ((BB_A.Top.LastValue - Bars.ClosePrices.LastValue) <= 0))
      |    {
      |        Close(TradeType.Buy);
      |    }
      |    // 売り判定
      |    if ((Flag_bottom) && ((BB_A.Bottom.LastValue - Bars.ClosePrices.LastValue) >= 0))
      |    {
      |        Close(TradeType.Sell);
      ⊥   
          }
     ②Τ    // 発注判定
      |    //売り判定
      |    if ((Flag_Top) && ((BB_A.Top.LastValue - Bars.ClosePrices.LastValue) <= 0))
      |    {
      |        Open(TradeType.Sell);
      |    }
      |    //買い判定
      |    if ((Flag_bottom) && ((BB_A.Bottom.LastValue - Bars.ClosePrices.LastValue) >= 0))
      |    {
      |        Open(TradeType.Buy);
      ⊥  }
                  // BBの値から超過しているかの判定
     ③Τ  Flag_Top = ((BB_A.Top.LastValue - Bars.ClosePrices.LastValue) > 0);
      ⊥  Flag_bottom = ((BB_A.Bottom.LastValue - Bars.ClosePrices.LastValue) < 0);

①決済判定を実施する。
 今回はBBの値を超過した後にBB範囲内に戻ってきた時に決算する為
 ・BB超過フラグが正である
 かつ
 ・直近の終わり値がBBの範囲内に在る
事を判定し、正ならば 独自に作成したClose関数を用いてポジションを決済する。

②発注処理の判定を実施
①と同様の処理にて今度は発注処理を実施する。

③BBの超過フラグの判定
OnStartイベントにて実装した内容と同一の処理をグラフ更新事に実施するべくOnBarイベントにて実装する。

cbot作成 ⑥Ontick作成

        protected override void OnTick()
       {
           // 買いポジションのロスカット判定 
①Τ      if ((BB_L.Bottom.LastValue - Ask) < 0)
 |         {
 |             Close(TradeType.Buy);
 |         }
 |         // 売りポジションのロスカット判定 
 |         if ((BB_L.Top.LastValue - Bid) > 0)
 |         {
 |             Close(TradeType.Sell);
 ⊥       }

           // テスト用 疑似ロスカット
②Τ       if (Account.MarginLevel < 100)
 |        {
 |             Close(TradeType.Buy);
 |             Close(TradeType.Sell);
 ⊥      }

       }

①ロスカットの判定
  今回はロスカット用BBを買い・売りそれぞれの値が接触した際にロスカットする。

②バックテスト用ロスカットの設定
 cTraderのバックテストでは各ブローカーが設定しているロスカット率が認識されていない。
 ブローカにより処理方法は違うが単純なcbotとする為今回はブローカ指定ロスカット率に接触したら全てのポジションを決済する様にしている。

cbot作成 ⑥Open作成

        private void Open(TradeType t)
       {
           ExecuteMarketOrder(t, SymbleName, 1, BotName);
       }

発注処理用の関数を自作している。
関数自体はC#と同じ書式で作成できる。
発注処理自体はcalgoでは色々と変数が用意されているが今回は成行注文である為、”ExecuteMarketOrder"を利用する。

cbot作成 ⑥Close作成

        private void Close(TradeType t)
       {
           foreach (Position p in Positions.FindAll(BotName, SymbleName, t))
           {
               ClosePosition(p);
           }
       }
        

発注処理と同様決済処理に関しても関数を今回作成する。
全てのポジションの中でcbotで発注されたポジションのみを抽出し、
1ポジション毎に変数を用いて決済処理を実施する様にしている。

纏め

今回作成したcbotは下記となる。

using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
namespace cAlgo.Robots
{
   [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
   public class SimpleTrade : Robot
   {
       [Parameter("Periods", DefaultValue = 20, Group = "BollingerBands Atack")]
       public int BB_A_Periods { get; set; }
       [Parameter("Standerd Dev", DefaultValue = 1.8, Group = "BollingerBands Atack")]
       public double BB_A_Dev { get; set; }
       [Parameter("Souese", Group = "BollingerBands Atack")]
       public DataSeries BB_A_Sourse { get; set; }

       [Parameter("MA Type", Group = "BollingerBands Atack")]
       public MovingAverageType BB_A_MAType { get; set; }
       // 
       [Parameter("Periods", DefaultValue = 20, Group = "BollingerBands loss")]
       public int BB_L_Periods { get; set; }
       [Parameter("Standerd Dev", DefaultValue = 1.8, Group = "BollingerBands loss")]
       public double BB_L_Dev { get; set; }
       [Parameter("Souese", Group = "BollingerBands loss")]
       public DataSeries BB_L_Sourse { get; set; }

       [Parameter("MA Type", Group = "BollingerBands loss")]
       public MovingAverageType BB_L_MAType { get; set; }

       private BollingerBands BB_A; // 基本処理用BB
       private BollingerBands BB_L; // ロスカット判断用BB
       private bool Flag_Top = false; // BBのトップを超過しているかのフラグ (true: 超過,False:未超過)
       private bool Flag_bottom = false; // BBのbottomを超過しているかのフラグ (true: 超過,False:未超過)

       private const string BotName = "SimpleTrade";
       private string SymbleName;
       protected override void OnStart()
       {
           base.OnStart();
           // BBの呼び出し
           BB_A = Indicators.BollingerBands(BB_A_Sourse,BB_A_Periods,BB_A_Dev,BB_A_MAType);
           BB_L = Indicators.BollingerBands(BB_L_Sourse, BB_L_Periods, BB_L_Dev, BB_L_MAType) ;
           // BBの値から超過しているかの判定
           Flag_Top = ((BB_A.Top.LastValue - Bars.ClosePrices.LastValue) > 0);
           Flag_bottom = ((BB_A.Bottom.LastValue - Bars.ClosePrices.LastValue) < 0);

           // 取引通貨ペア名の格納
           SymbleName = Bars.SymbolName;

       }
       protected override void OnTick()
       {
           // 買いポジションのロスカット判定 
           if ((BB_L.Bottom.LastValue - Ask) < 0)
           {
               Close(TradeType.Buy);
           }
           // 売りポジションのロスカット判定 
           if ((BB_L.Top.LastValue - Bid) > 0)
           {
               Close(TradeType.Sell);
           }

           // テスト用 疑似ロスカット
           if (Account.MarginLevel < 100)
           {
               Close(TradeType.Buy);
               Close(TradeType.Sell);
           }

       }
       protected override void OnBar()
       {
           // 決済判定
           // 買い判定
           if ((Flag_Top) && ((BB_A.Top.LastValue - Bars.ClosePrices.LastValue) <= 0))
           {
               Close(TradeType.Buy);
           }
           // 売り判定
           if ((Flag_bottom) && ((BB_A.Bottom.LastValue - Bars.ClosePrices.LastValue) >= 0))
           {
               Close(TradeType.Sell);
           }

           // 発注判定
           //売り判定
           if ((Flag_Top) && ((BB_A.Top.LastValue - Bars.ClosePrices.LastValue) <= 0))
           {
               Open(TradeType.Sell);
           }
           //買い判定
           if ((Flag_bottom) && ((BB_A.Bottom.LastValue - Bars.ClosePrices.LastValue) >= 0))
           {
               Open(TradeType.Buy);
           }

           // BBの値から超過しているかの判定
           Flag_Top = ((BB_A.Top.LastValue - Bars.ClosePrices.LastValue) > 0);
           Flag_bottom = ((BB_A.Bottom.LastValue - Bars.ClosePrices.LastValue) < 0);

       }
       protected override void OnStop()
       {
           // Put your deinitialization logic here
       }
       private void Open(TradeType t)
       {
           ExecuteMarketOrder(t, SymbleName, 1, BotName);
       }

       private void Close(TradeType t)
       {
           foreach (Position p in Positions.FindAll(BotName, SymbleName, t))
           {
               ClosePosition(p);
           }
       }
   }
}

行数として110行程度でFXのEAが駆逐出来ました。
本当はこの後実際にcTraderで最適化及びバックテストを実施し損益出るのか検証したいと思ったのですが、記事が長くなったので次回実施していきたいたいと思います。

この記事が気に入ったらサポートをしてみませんか?