
一からの取引エキスパートアドバイザーの開発(第9部):概念的な飛躍(II)
はじめに
前稿では、フローティングウィンドウ内でテンプレートを使用できるようにする基本的なシステムを作成しました。変更を多く加えましたが、コードはまだ完成していません。これは意図的で、説明を単純にするためににおこなわれたものです。フローティングウィンドウでテンプレートを使用するのはかなり単純ですが、オブジェクトを使用するのははるかに複雑だからです。完全に新しい仕事にお備えください。
実際、フローティングウィンドウでChart Tradeインターフェイスを作成するために利用するオブジェクトの使用に関連する最大の問題は、MetaTrader5が実際にはこの目的を意図していないことです。Chart Tradeウィンドウの作成に標準ライブラリを使用できるとおっしゃる読者の方もいらっしゃるでしょうが、私は物事を複雑にするのが好きで、いくつか前の記事で説明したのと同じ方法で、すべての人が独自のインターフェイスを作成できるようにしたいと思います。ただし、その記事ではすべてが単純でした。次に、MetaTrader 5の制限を回避するために、それらを理解する必要があります。
計画
基本から始めましょう。次のコードは、期待どおりに動作します。
#property copyright "Daniel Jose" #property indicator_chart_window #property indicator_plots 0 //+------------------------------------------------------------------+ int OnInit() { long id = ChartID(); string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(); ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0); ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10); ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10); ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300); ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300); ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false); ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { return rates_total; } //+------------------------------------------------------------------+
効果は期待通りで、ここでは複雑なことは何もありません。MQL5を使用すると、もう少し先に進むことができますが、システムの当初の設計を超えて何かをおこなおうとすると、問題が発生する可能性があります。上記のコードを以下のようなものに変更すると面白くなります。
int OnInit() { long id = ChartID(), handle; string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(), sz1 = (string)MathRand(); ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0); ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10); ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10); ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300); ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300); ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false); ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false); handle = ObjectGetInteger(id, sz0, OBJPROP_CHART_ID); ObjectCreate(handle, sz1, OBJ_CHART, 0, 0, 0); ObjectSetInteger(handle, sz1, OBJPROP_XDISTANCE, 50); ObjectSetInteger(handle, sz1, OBJPROP_YDISTANCE, 50); ObjectSetInteger(handle, sz1, OBJPROP_XSIZE, 300); ObjectSetInteger(handle, sz1, OBJPROP_YSIZE, 300); ObjectSetInteger(handle, sz1, OBJPROP_PRICE_SCALE, false); ObjectSetInteger(handle, sz1, OBJPROP_DATE_SCALE, false); ChartRedraw(handle); return INIT_SUCCEEDED; }
追加したコード行を強調表示しています。チャート上で実行した結果は次のようになります。
何が起こったのでしょうか。チャートをチャートの上に配置しました。MQL5で許可されているため、ここには任意のオブジェクトを配置できますが、これにはメリットとデメリットの両方があります。この手順の利点を理解するために、コードの次の変更を見てください。
int OnInit() { long id = ChartID(), handle; string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(), sz1 = (string)MathRand(); ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0); ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10); ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10); ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300); ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300); ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false); ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false); ObjectSetInteger(id, sz0, OBJPROP_SELECTABLE, true); ObjectSetInteger(id, sz0, OBJPROP_SELECTED, true); handle = ObjectGetInteger(id, sz0, OBJPROP_CHART_ID); ObjectCreate(handle, sz1, OBJ_CHART, 0, 0, 0); ObjectSetInteger(handle, sz1, OBJPROP_XDISTANCE, 50); ObjectSetInteger(handle, sz1, OBJPROP_YDISTANCE, 50); ObjectSetInteger(handle, sz1, OBJPROP_XSIZE, 300); ObjectSetInteger(handle, sz1, OBJPROP_YSIZE, 300); ObjectSetInteger(handle, sz1, OBJPROP_PRICE_SCALE, false); ObjectSetInteger(handle, sz1, OBJPROP_DATE_SCALE, false); ChartRedraw(handle); return INIT_SUCCEEDED; }
強調表示された行の追加によって、次の結果が生成されます。
オブジェクト内にあるすべてのものがオブジェクト内に残っています。制御ロジックが大幅に簡素化されるため、これは、フローティングウィンドウを使用する場合に必要です。ただし、ここではすべてが完璧というわけではありません。MetaTrader5はもともとこのためには設計されてないので、オブジェクトが別のオブジェクトの内部にある場合、内部オブジェクトにイベントを送信できないという問題が発生します。これを理解するために、コードにさらに変更を実装しましょう。最終的なコードは次のようになります。
#property copyright "Daniel Jose" #property indicator_chart_window #property indicator_plots 0 //+------------------------------------------------------------------+ int OnInit() { long id = ChartID(), handle; string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(), sz1 = (string)MathRand(); ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0); ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10); ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10); ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300); ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300); ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false); ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false); ObjectSetInteger(id, sz0, OBJPROP_SELECTABLE, true); ObjectSetInteger(id, sz0, OBJPROP_SELECTED, true); handle = ObjectGetInteger(id, sz0, OBJPROP_CHART_ID); ObjectCreate(handle, sz1, OBJ_CHART, 0, 0, 0); ObjectSetInteger(handle, sz1, OBJPROP_XDISTANCE, 50); ObjectSetInteger(handle, sz1, OBJPROP_YDISTANCE, 50); ObjectSetInteger(handle, sz1, OBJPROP_XSIZE, 300); ObjectSetInteger(handle, sz1, OBJPROP_YSIZE, 300); ObjectSetInteger(handle, sz1, OBJPROP_PRICE_SCALE, false); ObjectSetInteger(handle, sz1, OBJPROP_DATE_SCALE, false); ObjectSetInteger(handle, sz1, OBJPROP_SELECTABLE, true); ObjectSetInteger(handle, sz1, OBJPROP_SELECTED, true); ChartRedraw(handle); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { return rates_total; } //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (id == CHARTEVENT_OBJECT_CLICK) Print(sparam); } //+------------------------------------------------------------------+
コードがプラットフォームで実行されると、結果は次のとおりになります。
内側のオブジェクトをクリックすると、実際には外側のオブジェクトをクリックすることになります。ここで物事がややっこしくなります。しかし、プログラマーとは常に問題解決の専門家になるよう努めるのもので、目的の結果を得るには、問題を解決する必要があります。このような知識を活かして、フローティングウィンドウでChart Tradeを作成し、機能的で個性的な外観を実現するシステムを構築していきます。
この計画プロセスには最後のフェーズがあります。この部分は最近のコンピュータにとってそれほど深刻ではありませんが、それでも考慮する必要があります。処理時間の最適化です。この問題は、情報の処理にかかる時間ではなく、プロセッサが実行する必要のある操作の数に関連しています。提案されたフローティングウィンドウシステムには、アクションに反応して動くことができる4つのオブジェクトが含まれています。したがって、ビューポートに配置された情報は、ウィンドウ自体の変更の対象となります。少なくともChart Tradeでオブジェクトの数が増えます。また、対応する計算コストはありませんが、コードが不快になり、うまく最適化されていないように見えます。問題は制御システムを追加するだけで解決しますが、もっとエレガントな提案があります。時間と労力がかかるように思えますが、実際には、保守と操作が必要なオブジェクトの数が減ります。
実装
まず、コードの再利用をサポートするために、フローティングウィンドウの作成をいくつかの手順に分割します。次に、C_ChartFloatingオブジェクトクラスに新しい関数を2つ作成します。
//+------------------------------------------------------------------+ bool StageLocal01(string sz0, ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT, int Scale = -1) { m_LimitX = (int)ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS); m_LimitY = (int)ChartGetInteger(Terminal.Get_ID(), CHART_HEIGHT_IN_PIXELS); if (m_MaxCounter >= def_MaxFloating) return false; CreateBarTitle(); CreateCaption(sz0); CreateBtnMaxMin(); CreateRegion(TimeFrame, Scale); m_Win[m_MaxCounter].handle = ObjectGetInteger(Terminal.Get_ID(), m_Win[m_MaxCounter].szRegionChart, OBJPROP_CHART_ID); return true; } //+------------------------------------------------------------------+ void StageLocal02(int x, int y, int w, int h) { y = (y < 0 ? m_MaxCounter * def_SizeBarCaption : y); m_Win[m_MaxCounter].PosX = -1; m_Win[m_MaxCounter].PosY = -1; m_Win[m_MaxCounter].PosX_Minimized = m_Win[m_MaxCounter].PosX_Maximized = x; m_Win[m_MaxCounter].PosY_Minimized = m_Win[m_MaxCounter].PosY_Maximized = y; SetDimension(w, h, true, m_MaxCounter); SetPosition(x, y, m_MaxCounter); ChartRedraw(m_Win[m_MaxCounter].handle); m_MaxCounter++; } //+------------------------------------------------------------------+
次は、フローティングウィンドウを追加する新しいコードです。
bool AddIndicator(string sz0, int x = 0, int y = -1, int w = 300, int h = 200, ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT, int Scale = -1) { if (!StageLocal01(sz0, TimeFrame, Scale)) return false; ChartApplyTemplate(m_Win[m_MaxCounter].handle, sz0 + ".tpl"); m_Win[m_MaxCounter].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand(); ObjectCreate(m_Win[m_MaxCounter].handle, m_Win[m_MaxCounter].szVLine, OBJ_VLINE, 0, 0, 0); ObjectSetInteger(m_Win[m_MaxCounter].handle, m_Win[m_MaxCounter].szVLine, OBJPROP_COLOR, clrBlack); StageLocal02(x, y, w, h); return true; }
すでに組み立てられているシステムには影響しませんが、それをより良く使用できるようになります。強調表示された行に着目してください。次に、IDEを使用する関数を作成します。その始まりを以下に示します。
bool Add_RAD_IDE(string sz0, int x, int y, int w, int h) { if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false; StageLocal02(x, y, w, h); return true; }
強調表示された行は、前のコードで使用した行と同じです。つまり、コードが再利用されていて、調整する必要があるものだけが異なります。次に、IDEを管理するためのツールがあることをシステムに通知できます。これをおこなうには、C_TemplateChartオブジェクトクラスを変更します。以下のコードは、関数で何が変更されるかを正確に示しています。必要なすべてのサポートがすでに正しく機能するようになるため、今後はIDEを使用したフローティングウィンドウの実装に集中できます。
void AddTemplate(void) { // .... Function code.... if (h == 0) { SetBase(m_Params.Param[TEMPLATE], (bIsSymbol ? m_Params.Param[TEMPLATE] : _Symbol), timeframe, i, w); if (!ChartApplyTemplate(m_Info[m_Counter - 1].handle, m_Params.Param[TEMPLATE] + ".tpl")) if (bIsSymbol) ChartApplyTemplate(m_Info[m_Counter - 1].handle, "Default.tpl"); } if (m_Params.Param[TEMPLATE] == def_NameTemplateRAD) { if ((h > 0) && (w > 0)) Add_RAD_IDE(m_Params.Param[TEMPLATE], 0, -1, w, h); else { C_Chart_IDE::Create(GetIdSubWinEA()); m_Info[m_Counter - 1].szVLine = ""; } }else { if ((w > 0) && (h > 0)) AddIndicator(m_Params.Param[TEMPLATE], 0, -1, w, h, timeframe, i); else { m_Info[m_Counter - 1].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand(); ObjectCreate(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJ_VLINE, 0, 0, 0); ObjectSetInteger(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJPROP_COLOR, clrBlack); } } }
可能な限り柔軟にするためにコードがどのように構成されているかを見てみましょう。これによって、システムがつぎはぎだらけになるのを防ぐことができます。コードを変更するときは、常に、コードを最初から書き直したり同じことを何度もチェックしたりする必要がないということを念頭に置いてください。チェックは常に一度だけにするようにします。そうすれば、新しいテストをおこなう前に、可能な限り物事を使用して探索することができます。したがって、システムは良好な前提で成長しますが、コードは長期にわたって持続可能かつ拡張可能になります。
ここでシステムを実行すると、チャートに何かが表示されますが、少なくとも以前に作成したインターフェイスが表示される必要があるので、コードに追加の変更を加える必要があります。次のコードができます。
bool Add_RAD_IDE(string sz0, int x, int y, int w, int h) { if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false; ChartApplyTemplate(m_Win[m_MaxCounter].handle, "\\Files\\Chart Trade\\IDE.tpl"); StageLocal02(x, y, w, h); return true; }
アプリを起動すると、結果は次のとおりになります。
テンプレート内にあるオブジェクトにアクセスできれば、非常によいのでしょう(テンプレートは、上記のコードで強調表示されている行でロードされます)が、それは不可能です。そして、ここに重要な詳細があります。以前に考えた方法でオブジェクトを作成する代わりに、操作する必要のあるオブジェクトのみを作成するということです。これにより、ウィンドウを移動する必要がある場合に処理時間を大幅に節約できます。まだ別の問題もありますが、まず処理の問題を解決して、システムを機能させましょう。実際、この部分はすでに完了しており、動作させるには、調整が必要です。
クラス間の継承シーケンスを変更することから始めましょう。これをおこなう必要があるのは、多重継承がないためです。新しい構造は次のようになります。
ただし、この変更は問題ではありません。継承シーケンスを変更しても、コードはまったく変更されません。ほぼ準備が整います。変更した部分は以下のコードで強調表示されています。
bool Add_RAD_IDE(string sz0, int x, int y, int w, int h) { if ((w <= 0) || (h <= 0)) return false; if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false; ChartApplyTemplate(m_Win[m_MaxCounter].handle, "\\Files\\Chart Trade\\IDE.tpl"); StageLocal02(x, y, w, h); return true; } void AddTemplate(void) { // ..... Código .... if (h == 0) { SetBase(m_Params.Param[TEMPLATE], (bIsSymbol ? m_Params.Param[TEMPLATE] : _Symbol), timeframe, i, w); if (!ChartApplyTemplate(m_Info[m_Counter - 1].handle, m_Params.Param[TEMPLATE] + ".tpl")) if (bIsSymbol) ChartApplyTemplate(m_Info[m_Counter - 1].handle, "Default.tpl"); } if (m_Params.Param[TEMPLATE] == def_NameTemplateRAD) { C_Chart_IDE::Create(Add_RAD_IDE(m_Params.Param[TEMPLATE], 0, -1, w, h)); m_Info[m_Counter - 1].szVLine = ""; }else { if ((w > 0) && (h > 0)) AddIndicator(m_Params.Param[TEMPLATE], 0, -1, w, h, timeframe, i); else { m_Info[m_Counter - 1].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand(); ObjectCreate(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJ_VLINE, 0, 0, 0); ObjectSetInteger(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJPROP_COLOR, clrBlack); } } } bool Create(bool bFloat) { m_CountObject = 0; if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false; FileReadInteger(m_fp, SHORT_VALUE); for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = ""; m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA()); m_szLine = ""; while (m_szLine != "</chart>") { if (!FileReadLine()) return false; if (m_szLine == "<object>") { if (!FileReadLine()) return false; if (m_szLine == "type") { if (m_szValue == "102") if (!LoopCreating(OBJ_LABEL)) return false; if (m_szValue == "103") if (!LoopCreating(OBJ_BUTTON)) return false; if (m_szValue == "106") if (!LoopCreating(OBJ_BITMAP_LABEL)) return false; if (m_szValue == "107") if (!LoopCreating(OBJ_EDIT)) return false; if (m_szValue == "110") if (!LoopCreating(OBJ_RECTANGLE_LABEL)) return false; } } } FileClose(m_fp); DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, 0, szMsgIDE[eLABEL_SYMBOL]); return true; } bool LoopCreating(ENUM_OBJECT type) { #define macro_SetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B) #define macro_SetString(A, B) ObjectSetString(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B) int c0; bool b0; string sz0 = m_szValue; while (m_szLine != "</object>") if (!FileReadLine()) return false; else { if (m_szLine == "name") { b0 = false; StringToUpper(m_szValue); for(c0 = eRESULT; (c0 <= eEDIT_STOP) && (!(b0 = (m_szValue == szMsgIDE[c0]))); c0++); if (!b0 && m_IsFloating) return true; else c0 = (b0 ? c0 : m_CountObject); m_ArrObject[c0].szName = StringFormat("%s%04s>%s", def_HeaderMSG, sz0, m_szValue); //... The rest of the function... }
奇妙に思えるかもしれませんが、ここで複雑なことは何もありません。フローティングウィンドウを使用しなければ、システムはIDEで実行されているイベントをすでに処理できるのですが、そのフローティングウィンドウのために、すべてを再構築する必要があります。ただし、これを最初からおこなう必要はありません。既存のコードを変更して、適切な場所にIDEを追加します。イベントを受け取るオブジェクトを追加するだけです。これらの変更により、すべての要素を作成する必要があるのか、一部の要素のみを作成する必要があるのかがわかるようになります。
変更後、チャートにIDE情報が表示されます。ただし、オブジェクトがフローティングウィンドウに関連付けられていないため、IDEオブジェクトは実際の混乱を引き起こします。次に、これを修正する必要があります。
まず、チャート上でフローティングウィンドウが配置されている点を取得する必要があります。ウィンドウを表すオブジェクトの点が分かっていれば十分ですが、オブジェクト指向プログラミングに固執するため、いくつかの変更を加える必要があります。主な結果は以下のコードです。
//+------------------------------------------------------------------+ struct IDE_Struct { int X, Y, Index; bool IsMaximized; }; //+------------------------------------------------------------------+ class C_ChartFloating { // ... Class code ... //+------------------------------------------------------------------+ void SetPosition(const int X, const int Y, const int c0) { // ... Function code ... if (c0 == m_IDEStruct.Index) { m_IDEStruct.X = m_Win[c0].PosX + 3; m_IDEStruct.Y = m_Win[c0].PosY + def_SizeBarCaption + 3; m_IDEStruct.IsMaximized = m_Win[c0].IsMaximized; } } //+------------------------------------------------------------------+ // ... Class code ... //+------------------------------------------------------------------+ inline IDE_Struct GetIDE_Struct(void) const { return m_IDEStruct; } //+------------------------------------------------------------------+ bool Add_RAD_IDE(string sz0, int x, int y, int w, int h) { if ((w <= 0) || (h <= 0)) return false; if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false; ChartApplyTemplate(m_Win[m_MaxCounter].handle, "\\Files\\Chart Trade\\IDE.tpl"); m_IDEStruct.Index = m_MaxCounter; StageLocal02(x, y, w, h); return true; } //+------------------------------------------------------------------+ //... The rest of the class code }
この解決策では1つのウィンドウ(Chart Trade)に焦点を合わせているため、この時点以降、物事をより複雑にする意味はありません。これで、少なくとも最初は、ウィンドウ内にオブジェクトを正しく配置できます。これは、以下の関数でおこなわれます。
void Resize(int x) { for (int c0 = 0; c0 < m_CountObject; c0++) if (m_IsFloating) { ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, GetIDE_Struct().X + m_ArrObject[c0].iPosX); ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_YDISTANCE, GetIDE_Struct().Y + m_ArrObject[c0].iPosY); }else ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, x + m_ArrObject[c0].iPosX); };
上記の関数はオブジェクトの正しい初期位置を提供します。結果を以下に示します。
初期配置が正しいことに加えて、オブジェクトはすでにイベントに反応します。ただし、システムにはまだ不具合があり、修正する必要があります。一番初めに変えるのはこれです。
ご覧のとおり、ウィンドウは最小化されていますが、オブジェクトは画面に残っています。これは、オブジェクトがウィンドウ領域内にないためです(理由は前に説明しました)。先に進む前に修正します。これは、以下に示す簡単なコード変更によっておこなうことができます。
void Resize(int x) { for (int c0 = 0; c0 < m_CountObject; c0++) if (m_IsFloating) { ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, GetIDE_Struct().X + m_ArrObject[c0].iPosX + (GetIDE_Struct().IsMaximized ? 0 : Terminal.GetWidth())); ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_YDISTANCE, GetIDE_Struct().Y + m_ArrObject[c0].iPosY + (GetIDE_Struct().IsMaximized ? 0 : Terminal.GetHeight())); }else ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, x + m_ArrObject[c0].iPosX); };
結果は以下のとおりです。
次に、移動の問題を修正しましょう。以下のコードを使用して解決できます。
void DispatchMessage(int id, long lparam, double dparam, string sparam) { static double AccumulatedRoof = 0.0; bool b0; double d0; static int px = -1, py = -1; C_ChartFloating::DispatchMessage(id, lparam, dparam, sparam); if (m_CountObject < eEDIT_STOP) return; switch (id) { case CHARTEVENT_MOUSE_MOVE: if ((GetIDE_Struct().X != px) || (GetIDE_Struct().Y != py)) { px = GetIDE_Struct().X; py = GetIDE_Struct().Y; Resize(-1); } break; //... The rest of the function ...
最終結果は次のとおりです。
結論
プログラミングの素晴らしさをご覧ください。問題から始め、コードに大きな変更を加えることなく問題を1つずつ解決し、最終的には可能な限り最小限のコードで作業するコードを作成します。
重要事項:Chart Tradeの背景に奇妙な情報が表示される場合、更新3228が原因でclrNONEの色のオブジェクトが不透明になっています。添付のIDEファイルを使用して問題を修正してください。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/10363




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