English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
preview
一からの取引エキスパートアドバイザーの開発(第28部):未来に向かって(III)

一からの取引エキスパートアドバイザーの開発(第28部):未来に向かって(III)

MetaTrader 5 | 28 11月 2022, 13:12
153 0
Daniel Jose
Daniel Jose

はじめに

発注システムの開発を開始したとき、「一からの取引エキスパートアドバイザーの開発(第18部)」稿の後、ここまで到達するのにどれくらいの時間がかかるかわかりませんでした。さまざまな瞬間、変更、修正などを経験してきました。マークを付けたり、システムをより直感的にするなど、特定のことをおこなう方法を示しました。ただ、この段階では道筋が整っていなかったので見せられなかった部分もありました。この旅により、誰もがアイデアを理解し、システムがどのように機能するかを知ることができるような方法で概念を構築することができました。

これまでのすべての記事では、システムのしくみについて同じレベルの理解を持ってこの記事にたどり着くための準備を整えてきました。したがって、この資料が極端にわかりにくかったり複雑になったりしないことを願っています。最初から1つの疑問があり、詳しく分析することは避けました。ただし、経験豊富なトレーダーにとってはこれは非常に重要です。一見するとばかげているように見えるかもしれませんが、取引の時が来れば、EAに何かが欠けていることに気付くでしょう。次に、「ここに何が欠けているか」を自問します。何らかの理由で削除され、チャート上で復元したいテイクプロフィットとストップロスの値を復元する方法について話しています。

試したことがある方は、これはかなり難しくて時間のかかるタスクであることを理解できますでしょう。すべてがうまくいくためには「特定のシナリオに従う」必要があり、そうしないと、間違いを犯してばかりになります。

MetaTrader 5は、注文値の作成と修正を可能にするチケットのシステムを備えています。アイデアは、同じチケットシステムをより高速かつ効率的にするエキスパートアドバイザー(EA)を持つことです。MetaTrader 5システムは完璧ではありません。開発中のEAを使用するよりも遅く、エラーが発生しやすい場合があります。

しかし、今まで、TPとSLレベル(テイクプロフィットとストップロス)の値を生成する方法を説明したことがありません。ストップレベルの削除は明確で直感的だと思います。しかし、それをどのように実装するのでしょうか。つまり、どのようにストップを設定したり、チャート上でそれらを復元したりする必要があるのでしょうか。これは非常に興味深く、いくつかの疑問を投げかけます。もちろん、これがこの記事を作成する理由です。外部リソースに頼ることなく、EAの注文システムを使用するだけで、チャート上でストップ レベルを作成する多くの方法の1つを示すことです。


2.0.はじめに:実装

まず、EAが開発の最初の日から長い間おこなってきたチェックを強制的に停止する必要があります。このチェックを削除するには、取り消し線が引かれたコードをすべて削除し、強調表示されたコードを追加する必要があります。

inline double SecureChannelPosition(void)
{
        double Res = 0, sl, profit, bid, ask;
        ulong ticket;
                                
        bid = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_BID);
        ask = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_ASK);
        for (int i0 = PositionsTotal() - 1; i0 >= 0; i0--) if (PositionGetSymbol(i0) == Terminal.GetSymbol())
        {
                IndicatorAdd(ticket = PositionGetInteger(POSITION_TICKET));
                SetTextValue(ticket, IT_RESULT, PositionGetDouble(POSITION_VOLUME), Res += PositionGetDouble(POSITION_PROFIT), PositionGetDouble(POSITION_PRICE_OPEN));
                SetTextValue(ticket, IT_RESULT, PositionGetDouble(POSITION_VOLUME), profit = PositionGetDouble(POSITION_PROFIT), PositionGetDouble(POSITION_PRICE_OPEN));
                sl = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                        if (ask < sl) ClosePosition(ticket);
                }else
                {
                        if ((bid > sl) && (sl > 0)) ClosePosition(ticket);
                }
                Res += profit;
        }
        return Res;
};

消されたコードは存在しなくなりますが、別の瞬間に復活します。ただし、現時点ではメリットよりもデメリットの方が大きいです。それが完了したら、EA以外のリソースの助けを借りずに、TPおよびSLシステムをチャート上で実装する方法について考え始めることができます。

開発者はそれぞれ、この問題を解決するための独自のアイデアを持っています。トレーダーにとって理解しやすいものもあれば、より難しいものもあります。実践するのが難しいものもあれば、簡単なものもあります。ここで使用および表示する方法が最も適切または最も簡単であると言っているわけではありませんが、私の作業方法およびプラットフォームの使用方法には断然最適です。また、新しい要素を作成する必要はありません。修正するのはコードの一部のみです。


2.0.1.抗力システムのモデリング

現在の開発段階にあるEAコード自体は、作成しようとしているシステムをどのようにモデル化すべきかについてのヒントを提供します。次のコードをご覧ください。

#define macroUpdate(A, B) if (B > 0) {                                                                  \
                if (b0 = (macroGetLinePrice(ticket, A) == 0 ? true : b0)) CreateIndicator(ticket, A);   \
                PositionAxlePrice(ticket, A, B);                                                        \
                SetTextValue(ticket, A, vol, (isBuy ? B - pr : pr - B));                                \
                                     } else RemoveIndicator(ticket, A);
                                                                        
                void UpdateIndicators(ulong ticket, double tp, double sl, double vol, bool isBuy)
                        {
                                double pr;
                                bool b0 = false;
                                
                                if (ticket == def_IndicatorGhost) pr = m_Selection.pr; else
                                {
                                        pr = macroGetLinePrice(ticket, IT_RESULT);
                                        if ((pr == 0) && (macroGetLinePrice(ticket, IT_PENDING) == 0))
                                        {
                                                CreateIndicator(ticket, IT_PENDING);
                                                PositionAxlePrice(ticket, IT_PENDING, m_Selection.pr);
                                                ChartRedraw();
                                        }
                                        pr = (pr > 0 ? pr : macroGetLinePrice(ticket, IT_PENDING));
                                        SetTextValue(ticket, IT_PENDING, vol);
                                }
                                if (m_Selection.tp > 0) macroUpdate(IT_TAKE, tp);
                                if (m_Selection.sl > 0) macroUpdate(IT_STOP, sl);
                                if (b0) ChartRedraw();
                        }
#undef macroUpdate

強調表示された行には、タスクを実行するマクロが含まれています。必要なもの、つまりストップレベル指標を実装するために必要なサポートを提供するように変更する必要があります。マクロコードを詳しく見てみましょう。以下に示します。

#define macroUpdate(A, B){ if (B > 0) {                                                                 \
                if (b0 = (macroGetLinePrice(ticket, A) == 0 ? true : b0)) CreateIndicator(ticket, A);   \
                PositionAxlePrice(ticket, A, B);                                                        \
                SetTextValue(ticket, A, vol, (isBuy ? B - pr : pr - B));                                \
                                        } else RemoveIndicator(ticket, A); }

次のことをおこないます。テイクプロフィットまたはストップロスのB値が0より大きい場合は、指標がチャート上にあるかどうかを確認します。ない場合は、作成して配置し、表示する値を設定します。B値が0に等しい場合、チャートから指標を完全に削除します(マクロコードで強調表示されたポイントでおこないます)。しかし、チャートから指標を完全に削除する代わりに、その要素を保持し、この要素がやりたいことを表示するように構成できまる場合(つまり、注文またはポジションの欠落しているストップを作成する)、これは充分でしょうか。要素は注文またはOCOの位置に戻るでしょうか。はい、それで十分であり、それがアイデアです。要素を残します。この場合、ストップレベルを移動して欠落しているストップレベルを作成するために使用されるオブジェクトです。この要素をドラッグするだけで、制限が作成されます。これは、システムを構築するために使用する理論的な枠組みです。

しかし、これをおこなうだけでは、必要なものをすべて手に入れることはできません。もう1つ変更が必要です。続行する前にこれをおこないます。この変更は、次のコードで強調表示されています。

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{

//... Internal code...

        m_Selection.tp = (valueTp == 0 ? 0 : m_Selection.pr + (bKeyBuy ? valueTp : (-valueTp)));
        m_Selection.sl = (valueSl == 0 ? 0 : m_Selection.pr + (bKeyBuy ? (-valueSl) : valueSl));

// ... The rest of the code ...

}

ChartTradeによって入力された初期値がnullかどうかを確認します。nullの場合、指標は作成されず、チャートにはエントリポイントのみが表示されます。

これにより、システムの残りの部分でより直線的な作業が提供されます。ストップレベルが指定されていない場合、注文モデル全体は、チャートに指値注文を出すときに、最初から値が指定されている場合と同じ動作をします。

したがって、注文やポジションにぶら下がっているこれらの数字は何であるかを疑問に思うトレーダーは1人もいないでしょう。チャート上で移動できる要素を表していることを知っているからです。

ただし、これらすべてにもかかわらず、まだ小さな問題があり、そのために2つのマクロを変更します。以下を参照ください。

#define macroSetLinePrice(ticket, it, price)    ObjectSetDouble(Terminal.Get_ID(), macroMountName(ticket, it, EV_LINE), OBJPROP_PRICE, price)
#define macroGetLinePrice(ticket, it)           ObjectGetDouble(Terminal.Get_ID(), macroMountName(ticket, it, EV_LINE), OBJPROP_PRICE)
#define macroGetPrice(ticket, it, ev)           ObjectGetDouble(Terminal.Get_ID(), macroMountName(ticket, it, ev), OBJPROP_PRICE)

この変更は、これから構築するシステムの残りの部分にとって非常に重要です。でも、なぜカットラインを引っ張っているのでしょうか。その理由は、システムをさらに柔軟にする必要があるということです。これを実現するために、取り消し線が引かれたコードが削除され、代わりに強調表示された行が表示されました。価格に値を割り当てるマクロがないのに、なぜmacroGetPriceが必要なのでしょうか。実際、この価格調整を実際におこなうのは1箇所だけで、チャートオブジェクトに書き込むときです。この点は、次のコードで確認できます。

#define macroSetPrice(ticket, it, ev, price) ObjectSetDouble(Terminal.Get_ID(), macroMountName(ticket, it, ev), OBJPROP_PRICE, price)
//---

// ... Additional code inside the class....

//---
inline void PositionAxlePrice(ulong ticket, eIndicatorTrade it, double price)
                        {
                                int x, y, desl;
                                
                                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y);
                                if (it != IT_RESULT) macroSetPrice(ticket, it, EV_MOVE, price);
                                macroSetPrice(ticket, it, EV_LINE, price);
                                macroSetAxleY(it);
                                switch (it)
                                {
                                        case IT_TAKE: desl = 160; break;
                                        case IT_STOP: desl = 270; break;
                                        default: desl = 0;
                                }
                                macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2));
                        }

このため、オブジェクトの価格をコード内の他のすべての場所から見えるようにマクロで調整する必要はありません。実際、今のところマクロである必要はありませんが、後でこのコードが変更されたときにエラーが発生する可能性を減らすために、そのままにしておきます。

システムが最新の状態になったので、次のトピックに進み、すべてを計画どおりに機能させることができます。


2.0.2.新しいアップデート機能

これについては前のトピックですでに述べました。EAがすべての関連する問題を解決する間、私たちがしなければならないのはUpdate関数を構成することだけです。主な焦点はテイクプロフィットとストップロス指標のみであり、それらはマクロ内で実行されるため、マクロを正しく設定する必要があります。

ただし、まだ解決されていない問題があります。指標の残りの部分から独立して移動ボタンを作成する必要があります。これをおこなうには、ボタン作成コードを分離しましょう。

// ... class code ...

#define def_ColorLineTake       clrDarkGreen
#define def_ColorLineStop       clrMaroon

// ... class code ....

inline void CreateBtnMoveIndicator(ulong ticket, eIndicatorTrade it, color C = clrNONE)
                        {
                                string sz0 = macroMountName(ticket, it, EV_MOVE);

                                ObjectDelete(Terminal.Get_ID(), macroMountName(ticket, it, EV_MOVE));
                                m_BtnMove.Create(ticket, sz0, "Wingdings", "u", 17, (C == clrNONE ? (it == IT_TAKE ? def_ColorLineTake : def_ColorLineStop) : C));
                                m_BtnMove.Size(sz0, 21, 23);
                        }

// ... the rest of the class code ...

このコードは、移動ボタンのみを作成し、他には何も作成しません。とてもシンプルで簡単です。このコードはマクロとして残しておくことも考えましたが、関数にすることにしました。組み込み関数として宣言されているため、コンパイラはマクロを扱うのと同じように扱います。作業を簡単にするために、同じコード部分に2つの新しい定義を追加しました。これは、ある時点で移動ボタンのみを作成し、システム全体で使用されるのと同じ色にする必要があるためです。同様のケースでシステムの動作や外観が異なることは望ましくありません。問題を減らすために、色は上記のようにしています。

これでUpdate関数に移動できます。その完全なコードは上に示されています。以下のバージョンとこの記事の前半で示したバージョンとの唯一の違いは、強調表示されたコードです。このコードは、Update関数自体で使用されるマクロです。

#define macroUpdate(A, B){                                                                                                      \
                if (B == 0) {   if (macroGetPrice(ticket, A, EV_LINE) > 0) RemoveIndicator(ticket, A);                          \
                                if (macroGetPrice(ticket, A, EV_MOVE) == 0) CreateBtnMoveIndicator(ticket, A);                  \
                            } else if (b0 = (macroGetPrice(ticket, A, EV_LINE) == 0 ? true : b0)) CreateIndicator(ticket, A);   \
                PositionAxlePrice(ticket, A, (B == 0 ? pr : B));                                                                \
                SetTextValue(ticket, A, vol, (isBuy ? B - pr : pr - B));                                                        \
                        }
                                                                        
                void UpdateIndicators(ulong ticket, double tp, double sl, double vol, bool isBuy)
                        {
                                double pr;
                                bool b0 = false;
                                
                                if (ticket == def_IndicatorGhost) pr = m_Selection.pr; else
                                {
                                        pr = macroGetPrice(ticket, IT_RESULT, EV_LINE);
                                        if ((pr == 0) && (macroGetPrice(ticket, IT_PENDING, EV_MOVE) == 0))
                                        {
                                                CreateIndicator(ticket, IT_PENDING);
                                                PositionAxlePrice(ticket, IT_PENDING, m_Selection.pr);
                                                ChartRedraw();
                                        }
                                        pr = (pr > 0 ? pr : macroGetPrice(ticket, IT_PENDING, EV_MOVE));
                                        SetTextValue(ticket, IT_PENDING, vol);
                                }
                                macroUpdate(IT_TAKE, tp);
                                macroUpdate(IT_STOP, sl);
                                if (b0) ChartRedraw();
                        }
#undef macroUpdate

実際に何が起こっているのかを理解するために、このマクロをさらに詳しく見てみましょう。システムを使用するときにまだ発生する問題のいくつかを解決できるようにするためにはこれを理解することが必要です。準備はまだ整っておらず、さらに変更を加える必要があります。

最初のステップにある動作は、指値注文(テイクプロフィットまたはストップロス)の1つを削除すると、銘柄チャートから関連する指標をすぐに削除することです。これをおこなうには、指標の存在を示すポイントの1つである線があるかどうかを確認します。次に、この指標の移動オブジェクトがあるかどうかを確認します。存在しない場合は作成します。したがって、チャートに指標はありませんが、残りの指標はまだチャートに存在します。これが動きのオブジェクトです。

2番目のステップは、ストップレベルを作成する場合に発生します。サーバー上の注文には、チャートに表示されるべきストップレベルがあります。この場合、動きを表すオブジェクトは削除され、完全な指標が作成され、適切な場所に配置され、現在のストップレベル(テイクプロフィットまたはストップロス)がどこにあるかを示します。

最後のステップでは、指標を正しいポイントに配置します。1つの興味深い詳細:ストップレベル指標が動きの可能性を表すオブジェクトにすぎない場合、それが配置されるポイントは、注文またはポジションの価格になります。つまり、指値と逆指値を作成できる移動オブジェクトは、それが属する注文またはポジションの価格ラインにリンクされます。したがって、注文またはポジションにストップレベルの1つがない場合は、簡単に気付くことができます。

基本的に、動きを示すオブジェクトをクリックすると、通常どおりゴーストが作成され、同時に完全な指標の表現も作成されるようにすることが必要です。これは、コードを追加または変更することなくおこなわれます。これからは、以前と同じように、通常の方法でストップレベルを移動および調整できます。しかし、特定の点をクリックするまで、ストップ注文は存在しません。これは、記事の最後にあるデモビデオで明確に示されています。ここでは、システムが実際の口座でどのように機能するかを示しています。

すべてがうまくいっているように見えますが、いくつかの点でコードを作成または変更せざるを得ない不整合があります。このステップはもう終りなので、次のトピックで説明します。


2.0.3.フローティング指標の不便さを解決すうr

最初の不都合は、フローティング指標モードにあるときに発生します。これがチャート上にあるがサーバー上にないということです。詳細については一からの取引エキスパートアドバイザーの開発(第26部)および(第27部)の記事を参照してください。ここでは、フローティング指標がどのように機能し、どのように実装されたかを示しています。これらの指標は有用であるため、EAから削除されることはありません。ただし、それらの操作は、取引サーバー上に存在する注文とポジションを実際に表す指標とは異なるため、上記のシステムには適していません。フローティング指標を使用するときに発生する問題を解決するには、以下に示すようにDispatchMessage関数にアクセスして調整する必要があります。

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   ticket;
        double  price;

// ... Internal code...
                        
        switch (id)
        {

// ... Internal code...

                case CHARTEVENT_OBJECT_CLICK:
                        if (GetIndicatorInfos(sparam, ticket, it, ev)) switch (ev)
                        {
                                case EV_TYPE:
                                        if (ticket == def_IndicatorFloat)
                                        {
                                                macroGetDataIndicatorFloat;
                                                m_Selection.tp = (m_Selection.tp == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.tp - m_Selection.pr) * (m_Selection.bIsBuy ? 1 : -1)));
                                                m_Selection.sl = (m_Selection.sl == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.sl - m_Selection.pr) * (m_Selection.bIsBuy ? -1 : 1)));
                                                m_Selection.ticket = 0;
                                                UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                        } else m_BtnInfoType.SetStateButton(sparam, !m_BtnInfoType.GetStateButton(sparam));
                                        break;
                                case EV_DS:
                                        if (ticket != def_IndicatorFloat) m_BtnInfo_DS.SetStateButton(sparam, !m_BtnInfo_DS.GetStateButton(sparam));
                                        break;
                                case EV_CLOSE:
                                        if (ticket == def_IndicatorFloat)
                                        {
                                                macroGetDataIndicatorFloat;
                                                RemoveIndicator(def_IndicatorFloat, it);
                                                if (it != IT_PENDING) UpdateIndicators(def_IndicatorFloat, (it == IT_TAKE ? 0 : m_Selection.tp), (it == IT_STOP ? 0 : m_Selection.sl), m_Selection.vol, m_Selection.bIsBuy);
                                        }else if ((cRet = GetInfosTradeServer(ticket)) != 0) switch (it)

// ... The rest of the code...

上記で強調表示された変更をおこなうことにより、フローティング指標データと取引サーバーに存在するデータを表すものを同じ方法で調整できるようになったため、フローティング指標のセットアップに関連する他の問題が実質的に排除されます。ただし、これで私たちの問題が完全に解決されるわけではありません。長い間続いていた別の不都合を解決しなければなりません。これについては、別の議論に値するので、次のトピックに移りましょう。


2.0.4.負のテイクプロフィット値に関する欠点

この記事で議論している最後の欠点は、テイクプロフィットの値がしばしば負に設定される可能性があることであり、これはかなり長い間発生しています。これは取引システムにとって意味がありません。値をサーバーに送信しようとすると、エラーメッセージが返されます。したがって、これを修正し、未決注文の逆指値がプラスに変更される可能性があるという別の問題も解決する必要があります。

EAは現在それを許可しており、さらに悪いことに、発注システムはこの値がサーバー上にあることを示しますが、実際にはサーバーはエラーを返し、EAは単にそれを無視します。指値注文の場合、問題はより複雑になります。ポジションの場合は動作が異なるはずであり、このバグはまだ修正されていないからです。チャート上でストップレベルを直接定義できるようになれば、この欠点はなくなります。

ここで、ポジションの場合、正の値のストップロスを設定できることに留意してください。これは、ストップロスがトリガーされた場合、関連する値が口座に入金されることを示しています。ただし、未決注文の場合、これはエラーになり、サーバーが正しい注文を作成できなくなります。この問題を解決するには、テイクプロフィットの値を確認して、値が0以下になった場合は、値が小さくなるのを防ぐ必要があります。また、指値注文の場合、ストップロスを0より大きくすることはできません。実際、条件0が満たされた場合、EAに最小許容値を使用するように強制します。この場合、注文またはポジションは取引システムにとってある程度意味がありますが、ストップロスでポジションを建てたり、始値に等しいテイクプロフィットを設定したりすることは意味がありません。

これをできるだけ簡単にするために、システムに変数を作成する必要があります。これは以下で確認できます。

struct st00
{
        eIndicatorTrade it;
        bool            bIsBuy,
                        bIsDayTrade;
        ulong           ticket;
        double          vol,
                        pr,
                        tp,
                        sl,
                        MousePrice;
}m_Selection;

なぜ単にマウスラインの価格ポイントを変更しないのでしょうか。理由は、マウスを正しく操作するには、システムコールを使用する必要があるということです。つまり、WINDOWSAPIを介してマウス位置の値を操作する必要があり、これにより、外部dllの使用を有効にする必要があります。これはしたくありません。このようにして、EA内でローカル値を組み立てるのがより簡単になり、強調表示されたデータがこの値を保存します。

この値は、3つの異なる場所で使用されます。1番目の場所は移動関数です。以下のコードは、これがどこで起こっているかを正確に示しています。

void MoveSelection(double price)
{
        if (m_Selection.ticket == 0) return;
        switch (m_Selection.it)
        {
                case IT_TAKE:
                        UpdateIndicators(m_Selection.ticket, price, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                        break;
                case IT_STOP:
                        UpdateIndicators(m_Selection.ticket, m_Selection.tp, price, m_Selection.vol, m_Selection.bIsBuy);
                        break;
                case IT_PENDING:
                        PositionAxlePrice(m_Selection.ticket, IT_PENDING, price);
                        UpdateIndicators(m_Selection.ticket, (m_Selection.tp == 0 ? 0 : price + m_Selection.tp - m_Selection.pr), (m_Selection.sl == 0 ? 0 : price + m_Selection.sl - m_Selection.pr), m_Selection.vol, m_Selection.bIsBuy);
                        m_Selection.MousePrice = price;
                        break;
        }
        if (Mouse.IsVisible())
        {
                m_TradeLine.SpotLight(macroMountName(m_Selection.ticket, m_Selection.it, EV_LINE));
                Mouse.Hide();
        }
}

上記の関数はストップ注文点の移動を担当するので、何故すべてを上記の関数に入れないのでしょうか。理由は、テイクプロフィットまたはストップロスのレベルを正しく設定するためにいくつかの計算をおこなう必要があるためです。これは別の時点でおこなう方がはるかに簡単です。この値は別の場所でも変更する必要があります。値が参照される2番目の場所をここに示します。以下のコードをご覧ください。

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   ticket;
        double  price;
        bool    bKeyBuy,

// ... Internal code ....      
                                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Mouse.GetPositionDP(dt, price);
                        mKeys   = Mouse.GetButtonStatus();
                        bEClick  = (mKeys & 0x01) == 0x01;    //Left mouse button click
                        bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT pressed
                        bKeySell = (mKeys & 0x08) == 0x08;    //CTRL pressed
                        if (bKeyBuy != bKeySell)
                        {
                                if (!bMounting)
                                {
                                        m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl);
                                        valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / m_Selection.vol);
                                        valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / m_Selection.vol);
                                        m_Selection.it = IT_PENDING;
                                        m_Selection.pr = price;
                                }
                                m_Selection.tp = (valueTp == 0 ? 0 : m_Selection.pr + (bKeyBuy ? valueTp : (-valueTp)));
                                m_Selection.sl = (valueSl == 0 ? 0 : m_Selection.pr + (bKeyBuy ? (-valueSl) : valueSl));
                                m_Selection.bIsBuy = bKeyBuy;
                                m_BtnInfoType.SetStateButton(macroMountName(def_IndicatorTicket0, IT_PENDING, EV_TYPE), bKeyBuy);
                                if (!bMounting)
                                {
                                        IndicatorAdd(m_Selection.ticket = def_IndicatorTicket0);
                                        bMounting = true;
                                }
                                MoveSelection(price);
                                if ((bEClick) && (memLocal == 0)) SetPriceSelection(memLocal = price);
                        }else if (bMounting)
                        {
                                RemoveIndicator(def_IndicatorTicket0);
                                memLocal = 0;
                                bMounting = false;
                        }else if ((!bMounting) && (bKeyBuy == bKeySell) && (m_Selection.ticket > def_IndicatorGhost))
                        {
                                if (bEClick) SetPriceSelection(m_Selection.MousePrice); else MoveSelection(price);
                        }
                        break;

// ... Rest of the code...

これにより、システム内で予測可能な動作が得られます。値が参照される別の場所がありますが、複雑なため、マクロが存在しないように全体を変更することにしました。関数になります。新しいUpdate関数を以下に示します。

void UpdateIndicators(ulong ticket, double tp, double sl, double vol, bool isBuy)
{
        double pr;
        bool b0, bPen = true;
                                
        if (ticket == def_IndicatorGhost) pr = m_Selection.pr; else
        {
                bPen = (pr = macroGetPrice(ticket, IT_RESULT, EV_LINE)) == 0;
                if (bPen && (macroGetPrice(ticket, IT_PENDING, EV_MOVE) == 0))
                {
                        CreateIndicator(ticket, IT_PENDING);
                        PositionAxlePrice(ticket, IT_PENDING, m_Selection.pr);
                        ChartRedraw();
                }
                pr = (pr > 0 ? pr : macroGetPrice(ticket, IT_PENDING, EV_MOVE));
                SetTextValue(ticket, IT_PENDING, vol);
        }
        b0 = UpdateIndicatorsLimits(ticket, IT_TAKE, tp, vol, pr, isBuy, bPen);
        b0 = (UpdateIndicatorsLimits(ticket, IT_STOP, sl, vol, pr, isBuy, bPen) ? true : b0);
        if (b0) ChartRedraw();
}

強調表示された場所は前のマクロを置き換えますが、必要なコードははるかに複雑であると述べたので、3番目と最後の場所が実際にどこで参照されているかを見てみましょう。上記のコードでは、テイクプロフィット指標とストップロス指標に違いはありません。どちらも同じように処理されます。それではコードを見ていきましょう。

inline bool UpdateIndicatorsLimits(ulong ticket, eIndicatorTrade it, double price, double vol, double pr, bool isBuy, bool isPen)
{
        bool b0 = false;
        double d1 = Terminal.GetPointPerTick();
                
        if (
    price  == 0)
        {
                if (macroGetPrice(ticket, it, EV_LINE) > 0) RemoveIndicator(ticket, it);
                if (macroGetPrice(ticket, it, EV_MOVE) == 0) CreateBtnMoveIndicator(ticket, it);
        } else if (b0 = (macroGetPrice(ticket, it, EV_LINE) == 0 ? true : b0)) CreateIndicator(ticket, it);
        switch (it)
        {
                case IT_TAKE:
                        price = (price == 0 ? 0 : (((isBuy ? price - pr : pr - price) > 0) ? price : (isBuy ? pr + d1 : pr - d1)));
                        break;
                case IT_STOP:
                        price = (price == 0 ? 0 : (isPen ? (((isBuy ? price - pr : pr - price) < 0) ? price : (isBuy ? pr - d1 : pr + d1)) : price));
                        break;
        }
        if (m_Selection.it == it) m_Selection.MousePrice = price;
        PositionAxlePrice(ticket, it, (price == 0 ? pr : price));
        SetTextValue(ticket, it, vol, (isBuy ? price - pr : pr - price));
                        
        return b0;
}

今後は、許容される値に制限があるため、未決注文のテイクプロフィット値が間違っていることはありません。買い指値注文を出してから、テイクプロフィットを負の値(つまり、エントリポイントより下)に移動することはできなくなりました。テイクプロフィット指標の計算がこれを防止するためです。このようにコードを書く利点は、注文であろうとポジションであろうと、EA自体がそれを許可しないため、テイクプロフィット値が負になることは決してないということです。

ここで、ストップロスについていえば、管理しているもの(注文またはポジション)を確認する少し異なる計算があります。注文の場合、ストップ値が正になることはありません。ポジションの場合、EAは他の条件を単に無視します。この場合、EAはトレーダーによって指定された値を受け入れる必要があります。これで、発注システムコードの残りの部分に害を与えることなく、ポジションの場合のみ、正のストップロスを設定できます。このようにして、EAは送信されたデータが拒否されることなく、最終的に取引サーバーと対話します。


結論

ついに、いくつかの記事を経て最高潮に達しました。発注システムはほぼ完全で、さまざまな状況や市場の状況に非常に適応できるようになりました。これからは、マウスとキーボードを使用して完全にグラフィカルなシステムで取引をおこなうことができ、市場分析とともに、取引を開始または終了できます。

この記事に到着したばかりで、現在の開発段階でシステムがどのように動作するかまたはどのように見えるかを知りたい方は、以下のビデオをご覧ください。そして、これまでこの一連の記事をフォローしてくださったすべての方々に感謝します。作業はまだ終わっていません。このEAが記憶に残るものになるまで、やるべきことがたくさんあります。次の記事でお会いしましょう。👍



MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/10635

添付されたファイル |
ニューラルネットワークが簡単に(第25部):転移学習の実践 ニューラルネットワークが簡単に(第25部):転移学習の実践
前々回、前回と、ニューラルネットワークのモデルを作成・編集するためのツールを開発しました。いよいよ転移学習技術の利用可能性を実例で評価することになります。
MQL5での行列およびベクトル演算 MQL5での行列およびベクトル演算
行列とベクトルがMQL5に導入され、数学的な解決策による効率的な操作が可能になりました。これらの新しい型は、数学表記に近い簡潔でわかりやすいコードを作成するための組み込みメソッドを提供します。配列は広範な機能を提供しますが、行列の方がはるかに効率的である場合が多くあります。
市場の数学:利益、損失、コスト 市場の数学:利益、損失、コスト
この記事では、手数料やスワップなど、あらゆる取引の総損益を計算する方法を紹介します。最も正確な数学的モデルを提供し、それを使ってコードを書き、標準と比較するつもりです。そのほか、利益を計算するMQL5のメイン関数の内部にも入り込み、仕様から必要な値をすべて突き詰めてみます。
Bulls Powerによる取引システムの設計方法を学ぶ Bulls Powerによる取引システムの設計方法を学ぶ
最も人気のあるテクニカル指標によって取引システムを設計する方法を学ぶ連載の新しい記事へようこそ。この新しい記事では、Bulls Power(ブルパワー )テクニカル指標によって取引システムを設計する方法を学びます。