
リプレイシステムの開発(第32回):受注システム(I)
はじめに
前回の「リプレイシステムの開発(第31回):エキスパートアドバイザープロジェクト - C_Mouseクラス(V)」稿では、リプレイ/シミュレーションシステムでマウスを使用するための基本部分を開発しました。マウスのスクロールホイールの問題は、当初この機能を使用する必要性を感じていなかったので、お見せしませんでした。これでもう1つの部分に取りかかることができます。それは間違いなくはるかに困難です。実装しなければならないのは、コード上でも、その他の関連することでも、間違いなく、リプレイ/モデリングシステム全体で最も難しいことです。この部分がなければ、実用的でシンプルな分析をおこなうことはできません。受注システムについて話しています。
これまで開発してきたものの中で、このシステムが最も複雑であることは、おそらく皆さんもお気づきでしょうし、最終的にはご納得いただけると思います。あとは非常に単純なことですが、取引サーバーの動作をシミュレーションするシステムを作る必要があります。取引サーバーの操作方法を正確に実装する必要性は、当然のことのように思えます。少なくとも言葉ではです。ただし、リプレイ/シミュレーションシステムのユーザーにとって、すべてがシームレスで透明なものとなるようにする必要があります。システムを使用する際、ユーザーは実際のシステムとシミュレーションされたシステムの区別がつきません。しかし、一番難しいのはそこではありません。一番難しいのは、どんな状況でもEAが同じであることを許可することです。
EAが同じでなければならないと言うことは、テスターで使用するためにコンパイルし、実際の市場で使用するために再度コンパイルしないことです。プログラミングの観点からは、この方がはるかにシンプルで簡単ですが、ユーザーにとっては、EAの再コンパイルが常に必要になるという問題が生じます。そこで、最高のユーザーエクスペリエンスを保証するために、コンパイルが1回で済むEAを作成する必要があります。これが終われば、実際の市場でもテスターでも使用することができます。
デモ口座であっても、実際のマーケットでEAを使用する上で重要なのは、最も簡単に実装できることです。まずはそこから始めましょう。非常に基本的なモデルから始めます。徐々にEAを複雑化し、その機能を強化することで、最終的にはリプレイ/シミュレーションとデモ口座またはライブ口座の両方で使用できるEAを作成する予定です。まず最初にすることは、このコミュニティで公開されている他の記事で以前に説明されているコードのほとんどを拝借することです。これらの記事は私のものだから、この情報を使用することに問題はありません。システムを柔軟でモジュール化し、信頼性と堅牢性を高めるために、いくつかの変更を加えるつもりです。そうでなければ、ある時点で行き詰まりを感じ、あらゆる面でシステムのさらなる発展が不可能になるかもしれません。
この仕事は非常に複雑なので、これはほんの始まりに過ぎません。まず第1に、EAでクラス継承システムが現在どのように実装されているかを知る必要があります。以前、この連載の他の記事でも紹介しました。現在の継承図を図01に示します。
図01:現在のEAクラス継承図
この図は、これまでおこなわれてきたことに関しては非常にうまく機能していますが、本当に必要としているものにはまだほど遠いです。C_Ordersクラスをこの継承スキームに加えるのが難しいからではありません。それは、実際には、C_StudyクラスからC_Ordersクラスを派生させることで実現できますが、そのようなことはしたくありません。その理由は、OOP(オブジェクト指向プログラミング)に携わるほとんどのプログラマーが無視しがちな、非常に現実的な問題にあります。この問題はカプセル化と呼ばれています。つまり、自分の仕事に必要なことだけを知ることです。クラスの階層を作るとき、あるクラスには本当に必要な以上のことを知らせるべきではありません。私たちは常に、各クラスがそのタスクを実行するために本当に必要なことだけを知っているようなプログラミングを好むべきです。したがって、図01のダイアグラムにC_Ordersクラスを追加しながらカプセル化を維持することは、現実的に不可能です。この問題に対する最善の解決策は、図01に示すように継承ブロックからC_Terminalクラスを削除し、同じブロックに引数またはパラメータとして渡すことです。つまり、誰がどの情報を受け取るかをコントロールするのはEAのコードであり、これは情報のカプセル化を維持するのに役立ちます。
そのため、この記事で使用する新しいクラス図は図02のようになります。
図02:新しい継承図
新しいダイアグラムでは、個々のクラスはEAコードが許可した場合にのみアクセス可能になります。お察しの通り、既存のコードに少し変更を加えなければなりません。しかし、これらの変更はシステム全体に大きな影響を与えるものではありません。何が新しくなったかを確認するために、これらの変更をすぐに検討することができます。
地固めをする
最初にすべきことは、C_Terminalクラスに列挙型を作ることです。こちらです。
class C_Terminal { protected: enum eErrUser {ERR_Unknown, ERR_PointerInvalid}; // ... Internal code ... };
この列挙によって、_LastError変数を使用して、何らかの理由でシステムにエラーが発生した場合に通知できるようになります。ここでは、この2種類のエラーだけを定義します。
この時点で、C_Mouseクラスを変更します。この変更はクラスの機能には影響しないので、詳細は省きます。継承システムを使用する場合とは、メッセージの流れが若干異なるだけです。変更点を以下に示します。
#define def_AcessTerminal (*Terminal) #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal() //+------------------------------------------------------------------+ class C_Mouse : public C_Terminal { protected: //+------------------------------------------------------------------+ // ... Internal fragment .... //+------------------------------------------------------------------+ private : //+------------------------------------------------------------------+ // ... Internal fragment ... C_Terminal *Terminal; //+------------------------------------------------------------------+
常にコードの繰り返しを避けるため、新たに2つの定義を追加しました。これにより、幅広い構成オプションが可能になります。さらに、privateグローバル変数が追加され、C_Terminalクラスに正しくアクセスできるようになりました。また、上のコードでわかるように、C_Terminalクラスの継承はもう使用しません。
継承は使用しないので、さらに2つの変更点を議論する必要があります。最初のものは、C_Mouseクラスのコンストラクタにあります。
C_Mouse(C_Terminal *arg, color corH, color corP, color corN) :C_Terminal() { Terminal = arg; if (CheckPointer(Terminal) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid); if (_LastError != ERR_SUCCESS) return; ZeroMemory(m_Info); m_Info.corLineH = corH; m_Info.corTrendP = corP; m_Info.corTrendN = corN; m_Info.Study = eStudyNull; m_Mem.CrossHair = (bool)ChartGetInteger(def_InfoTerminal.ID, CHART_CROSSHAIR_TOOL); ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_MOUSE_MOVE, true); ChartSetInteger(def_InfoTerminal.ID, CHART_CROSSHAIR_TOOL, false); def_AcessTerminal.CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); }
ここでは、C_MouseクラスのコンストラクタからC_Terminalクラスのコンストラクタの呼び出しを削除しています。ここで、クラスへのポインタを初期化するための新しいパラメータを取得する必要があります。セキュリティ上の理由から、不適切な状況でコードが壊れることを避けたいので、C_Terminalクラスを使用するためのポインタが正しく初期化されたことを確認するテストを実行します。
これにはCheckPointer関数を使用しますが、コンストラクタと同様、エラー情報を返すことはできません。C_Terminalクラスに存在する列挙型の定義済みの値を使用して、エラー状態を示します。ただし、_LastError変数の値を直接変更することはできないので、SetUserErrorの呼び出しを使用する必要があります。その後、_LastErrorを確認して、何が起こったかを知ることができます。
ただし、C_Terminalクラスが正しく初期化されていない場合は注意が必要です。C_Mouseクラスのコンストラクタは初期化されていないC_Terminalクラスを使用することができないため、何もせずに戻ってきます。
もう1つの変更は、以下の関数に関するものです。
virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) { int w = 0; static double memPrice = 0; C_Terminal::DispatchMessage(id, lparam, dparam, sparam); switch (id) { //....
MetaTrader 5プラットフォームから報告されるイベントを処理するために、指定されたコードをEAに追加する必要があります。ご覧のように、これをおこなわないと、いくつかのイベントで問題が発生し、チャート上の要素の位置の違反につながる可能性があります。とりあえず、この場所からコードを削除します。C_MouseクラスがC_Terminalクラスのメッセージングシステムを呼び出すこともできます。しかし、継承を使用していないので、このコードにはちょっと変わった依存関係が残ることになります。
C_Mouseクラスでやったように、C_Studyクラスでもやってみましょう。クラスのコンストラクタにご注目ください。
C_Study(C_Terminal *arg, color corH, color corP, color corN) :C_Mouse(arg, corH, corP, corN) { Terminal = arg; if (CheckPointer(Terminal) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid); if (_LastError != ERR_SUCCESS) return; ZeroMemory(m_Info); m_Info.Status = eCloseMarket; m_Info.Rate.close = iClose(def_InfoTerminal.szSymbol, PERIOD_D1, ((def_InfoTerminal.szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(def_InfoTerminal.szSymbol, PERIOD_D1, 0))) ? 0 : 1)); m_Info.corP = corP; m_Info.corN = corN; CreateObjectInfo(2, 110, def_ExpansionBtn1, clrPaleTurquoise); CreateObjectInfo(2, 53, def_ExpansionBtn2); CreateObjectInfo(58, 53, def_ExpansionBtn3); }
C_Terminalクラスのポインタを指すパラメータを受け取り、それをC_Mouseクラスに渡します。継承した以上、正しく初期化しなければなりませんが、いずれにせよ、C_Mouseクラスのコンストラクタでおこなったのと同じ確認をおこなって、正しいポインタを使用していることを確認します。ここで、1つのことに注意する必要があります。C_MouseとC_Studyのコンストラクタでは、_LastErrorの値を確認して、何かが期待通りでないかどうかを判断しているということです。しかし、使用する資産によっては、EAが現在どの資産がチャート上にあるかを知るために、C_Terminalクラスはその名前を初期化する必要があるかもしれません。
これが発生したとすると、_LastError変数には4301(ERR_MARKET_UNKNOWN_SYMBOL)という値が格納され、資産が正しく検出されなかったことを示します。しかし、C_Terminalクラスは現在プログラムされている状態では、目的の資産にアクセスできるため、これは真実ではありません。このエラーによってEAがチャートから除外されるのを避けるためには、C_Terminalクラスのコンストラクタにちょっとした変更を加える必要があります。こちらです。
C_Terminal() { m_Infos.ID = ChartID(); CurrentSymbol(); m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR); m_Mem.Show_Date = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE); ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false); ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, true); ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false); m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS); m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); m_Infos.PointPerTick = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE); m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE); m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP); m_Infos.AdjustToTrade = m_Infos.PointPerTick / m_Infos.ValuePerPoint; ResetLastError(); }
このコードを追加することで、初期エラーがないことを示します。コンストラクタシステムが、EAコードの中でクラスを初期化するために使用されます。この行を実際に追加する必要はありません。というのも、場合によっては追加するのを忘れてしまうかもしれないし、最悪の場合、間違ったタイミングで追加してしまうかもしれません。
C_Ordersクラス
ここまで見てきたことが、次の手順につながります。C_Terminalクラスには、まだいくつかの変更を加える必要があります。この記事の後半で、これらの変更のいくつかをおこないます。取引サーバーとのやり取りを可能にするC_Ordersクラスの作成に移りましょう。この場合、証券会社が提供するリアルサーバーにアクセスすることになりますが、システムをテストするためにはデモ口座を使用することができます。実際、リアル口座でシステムを直接使用することはお勧めできません。
このクラスのコードは次のように始まります。
#property copyright "Daniel Jose" //+------------------------------------------------------------------+ #include "..\C_Terminal.mqh" //+------------------------------------------------------------------+ #define def_AcessTerminal (*Terminal) #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal() //+------------------------------------------------------------------+ class C_Orders {
ここでは、コード化を容易にするために、C_Terminalクラスにアクセスするためのいくつかの物を定義します。これで、これらの定義はクラスファイルの最後ではなく、クラスコードの中に配置されることになります。これがC_Terminalクラスへのアクセス方法となります。これで、将来何か変更があっても、クラスコードを変更する必要はなく、この定義だけを変更すれば済みます。このクラスは何も継承していません。このクラスをプログラミングするときや、後で出てくる他のクラスを書くときに混乱しないように、このことを覚えておくことが重要です。
次に、最初のグローバル変数と内部クラス変数を宣言します。クラスコードの外部からはアクセスできません。こちらです。
private : //+------------------------------------------------------------------+ MqlTradeRequest m_TradeRequest; ulong m_MagicNumber; C_Terminal *Terminal;
これらのグローバル変数はprivateと宣言されています。クラスコードの外部からはアクセスできません。C_Terminalクラスへのアクセスを提供する変数がどのように宣言されているかにご注意ください。MQL5でのポインタの使い方はC/C++とは異なりますが、実際にはポインタとして宣言されています。
ulong ToServer(void) { MqlTradeCheckResult TradeCheck; MqlTradeResult TradeResult; bool bTmp; ResetLastError(); ZeroMemory(TradeCheck); ZeroMemory(TradeResult); bTmp = OrderCheck(m_TradeRequest, TradeCheck); if (_LastError == ERR_SUCCESS) bTmp = OrderSend(m_TradeRequest, TradeResult); if (_LastError != ERR_SUCCESS) PrintFormat("Order System - Error Number: %d", _LastError); return (_LastError == ERR_SUCCESS ? TradeResult.order : 0); }
上記の関数はprivateで、呼び出しを「一元化」する役割を果たします。呼び出しを一元化することにしたのは、将来的にシステムを適応させるのが容易になるからです。これは、実際のサーバーとシミュレーションしたサーバーの両方で同じ図を使用できるようにするために必要です。以前の関数は、「自動で動くEAを作る(第15回):自動化(VII)」稿で取り上げた他の関数とともに削除されました。その記事では、手動EAから自動EAを作成する方法を説明しました。この記事で紹介されている関数をいくつか使用して、ここでの作業を少し簡略化しましょう。これにより、同じ概念を使用したい場合、MetaTrader 5のストラテジーテスターを使用することなく、リプレイ/シミュレーションシステムを使用して自動EAをテストすることができます。
基本的に、上記の関数は証券会社のサーバー上でいくつかのことを確認します。問題がなければ、取引サーバーにリクエストを送り、ユーザーまたはEA(自動モードで動作するため)のリクエストを満たします。
inline void CommonData(const ENUM_ORDER_TYPE type, const double Price, const double FinanceStop, const double FinanceTake, const uint Leverage, const bool IsDayTrade) { double Desloc; ZeroMemory(m_TradeRequest); m_TradeRequest.magic = m_MagicNumber; m_TradeRequest.symbol = def_InfoTerminal.szSymbol; m_TradeRequest.volume = NormalizeDouble(def_InfoTerminal.VolumeMinimal + (def_InfoTerminal.VolumeMinimal * (Leverage - 1)), def_InfoTerminal.nDigits); m_TradeRequest.price = NormalizeDouble(Price, def_InfoTerminal.nDigits); Desloc = def_AcessTerminal.FinanceToPoints(FinanceStop, Leverage); m_TradeRequest.sl = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? -1 : 1)), def_InfoTerminal.nDigits); Desloc = def_AcessTerminal.FinanceToPoints(FinanceTake, Leverage); m_TradeRequest.tp = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? 1 : -1)), def_InfoTerminal.nDigits); m_TradeRequest.type_time = (IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC); m_TradeRequest.stoplimit = 0; m_TradeRequest.expiration = 0; m_TradeRequest.type_filling = ORDER_FILLING_RETURN; m_TradeRequest.deviation = 1000; m_TradeRequest.comment = "Order Generated by Experts Advisor."; }
上記の関数も同連載の記事から取り込んだものです。しかし、ここでは導入される新システムに合わせなければなりませんでした。作動原理は基本的に自動化の連載と同じです。しかし、その連載を読んでいない方のために、この関数を簡単に見てみましょう。まず、財務的価値をポイントに変換するコードがあります。これは、私たちユーザーが、与えられたレバレッジのポイント数の設定に悩む必要がないようにするためです。こうして、私たちは必要以上に金融と向き合う必要がなくなります。この操作を手作業でおこなうと、エラーや失敗の原因となるが、この関数を使用すれば、非常に簡単に値を変換することができます。この関数は資産に関係なく機能します。資産タイプが何であれ、変換は常に正確かつ効率的におこなわれます。
C_Terminalクラスにある次の関数を見てみましょう。そのコードを以下に示します。
inline double FinanceToPoints(const double Finance, const uint Leverage) { double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1)); return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade))); };
この関数の主な秘密は、以下の断片に示すように計算される値にあります。
m_Infos.PointPerTick = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE); m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE); m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP); m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
FinanceToPointsで使用される上記の値はすべて、当社が管理し、取引に使用する資産によって異なります。そのため、FinanceToPointsが変換をおこなう際、実際にチャート上で使用している資産に適応します。したがって、EAはどの資産で、どの市場で発売されたかを気にしません。同様に、どんなユーザーにも対応できます。クラスのprivateの部分を見てきたので、publicの部分を見てみましょう。コンストラクタから始めましょう。
C_Orders(C_Terminal *arg, const ulong magic) :m_MagicNumber(magic) { if (CheckPointer(Terminal = arg) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid); }
簡単で効果的な方法で、クラスのコンストラクタがC_Terminalクラスにアクセスできるようにします。EAがクラスが使用するC_Terminalオブジェクトを作成するとき、そのオブジェクトを必要とする他のすべてのクラスに渡されるオブジェクトも作成されます。クラスは、すでに初期化されたクラスにアクセスするために、EAによって作成されたポインタを受け取ります。そして、この値をprivateグローバル変数に保存し、必要なときにC_Terminalクラスのデータや関数にアクセスできるようにします。実際、このようなオブジェクト(この場合はクラス)が何か有用なものを指していなければ、エラーとして報告されます。コンストラクタは値を返せないので、_LastError変数に適切な値を設定するこのメソッドを使用します。そうすることで、その理由が見えてきます。
では、現段階でクラスに存在する最後の2つの機能に話を進めましょう。最初のものを以下に示します。
ulong ToMarket(const ENUM_ORDER_TYPE type, const double FinanceStop, const double FinanceTake, const uint Leverage, const bool IsDayTrade) { CommonData(type, SymbolInfoDouble(def_InfoTerminal.szSymbol, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID)), FinanceStop, FinanceTake, Leverage, IsDayTrade); m_TradeRequest.action = TRADE_ACTION_DEAL; m_TradeRequest.type = type; return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? ToServer() : 0); };
この関数は、市場価格での約定要求を送信する役割を担います。ここでは、先に検討したコード全体を実質的に使用します。これは再利用の良いケースです。このような再利用は、長期にわたってより高い安全性とパフォーマンスを促進します。再利用されるシステムのどの部分を改善しても、コード全体が改善されます。上記のコードについて、以下の詳細にご注意ください。
- まず、ストップレベル(利食いと損切り)をポイントではなく、財務的価値で示します。
- 次に、注文が執行された時点で利用可能な最良の価格で、即座に注文を執行するようサーバーに指示します。
- 第3に、より多くの注文タイプを利用できますが、ここでは買いか売りかを示すこの2つのタイプしか使用できません。この表示がない場合、注文は送信されません。
これらの詳細は重要です。そうでなければ、このシステムを使用することができません。これらの点を知らなかったり、無視したりすると、次の開発段階で多くの頭痛や疑念を抱えることになります。
ここでC_Ordersクラスの最後の関数が登場します。以下に現在の開発段階を示します。
ulong CreateOrder(const ENUM_ORDER_TYPE type, const double Price, const double FinanceStop, const double FinanceTake, const uint Leverage, const bool IsDayTrade) { double bid, ask; bid = SymbolInfoDouble(def_InfoTerminal.szSymbol, (def_InfoTerminal.ChartMode == SYMBOL_CHART_MODE_LAST ? SYMBOL_LAST : SYMBOL_BID)); ask = (def_InfoTerminal.ChartMode == SYMBOL_CHART_MODE_LAST ? bid : SymbolInfoDouble(def_InfoTerminal.szSymbol, SYMBOL_ASK)); CommonData(type, def_AcessTerminal.AdjustPrice(Price), FinanceStop, FinanceTake, Leverage, IsDayTrade); m_TradeRequest.action = TRADE_ACTION_PENDING; m_TradeRequest.type = (type == ORDER_TYPE_BUY ? (ask >= Price ? ORDER_TYPE_BUY_LIMIT : ORDER_TYPE_BUY_STOP) : (bid < Price ? ORDER_TYPE_SELL_LIMIT : ORDER_TYPE_SELL_STOP)); return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? ToServer() : 0); };
市場実行関数とよく似たものをいくつか紹介しましょう。例えば、ストップレベルは財務的価値で設定され、この2つの数値のうち1つだけを使用して買いか売りかを示さなければなりません。しかし、EAに存在する大半のコードとは異なるものがあります。通常、エキスパートアドバイザー(EA)を作成するときは、FX市場や証券取引所など、非常に特定の種類の市場で使用することを目的としています。MetaTrader 5は両方のタイプの市場をサポートしているので、より簡単にするためにいくつかの標準化をおこなう必要があります。FXや証券取引所で働くのも同じことではないでしょうか。ユーザーの視点からはイエスですが、プログラミングの視点からはノーです。よく見ると、現在使用されているチャートの種類を確認していることがわかります。これに基づいて、システムが最終価格で動くのか、買値と売値で動くのかを結論づけることができます。このことを知ることは、注文を出すためではなく、どのような注文の仕方をするかを知るために重要です。後で、取引サーバーの動作をシミュレーションするために、このような注文をシステムに実装する必要があります。しかし、この段階で知っておくべきことは、注文の種類は、それが執行される価格と同じくらい重要だということです。価格を正しい場所に配置したとしても、注文の種類を間違えれば、注文はサーバーによって実行されると思っていた時間とは異なる時間に実行されることになり、問題が発生します。
MetaTrader 5の初心者ユーザーは、未決済注文を入力する際にミスを犯すことがよくあります。市場によります。時間が経てば、ユーザーは市場に慣れ、そう簡単には間違えなくなるからです。しかし、ある市場から別の市場に移ると、事態はより複雑になります。チャートシステムがBID-ASKベースの場合、注文タイプの設定方法はLASTベースのチャートシステムとは異なります。この違いは微妙ですが、そこにあり、注文が保留されたままではなく、市場価格で実行されるという事実につながります。
結論
説明された内容にもかかわらず、この記事にはコードは添付しません。その理由は、受注システムを実装するのではなく、単にそのようなシステムを実装するためのBASICクラスを作成するだけだからです。ここに示したC_Ordersクラスのコードと比べて、いくつかの関数やメソッドが欠けていることにお気づきでしょうか。つまり、以前の記事で受注システムについて考察したコードと比較した場合にです。
このようなことが起きているのは、この受注システムをいくつかの部分に分けるという私の決断によるものです。そうすることで、システムがリプレイ/シミュレーションサービスとどのように統合されるかを、明確かつシンプルに説明することができます。信じてください。これは最も簡単な作業ではなく、むしろ非常に複雑で、まだ馴染みのない多くの概念が含まれています。そのため、記事が理解しやすく、内容が完全にカオスにならないように、少しずつ説明を示していかなければなりません。
次回は、この受注システムを取引サーバーとやりとりさせる方法について見ていきましょう。少なくとも物理的なレベルでは、デモ口座やリアル口座でEAを使用することができます。そこで、注文タイプがどのように機能するのかを理解し、シミュレーションされたシステムから始めることができるようにします。その逆をおこなったり、シミュレーションされたシステムと実際のシステムを一緒にしたりすれば、結果は完全に混乱します。では、また次の記事で!
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/11393





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索