–
前回は、前々回である93回のシンプルな形のトレーリングストップに「ミニマムプロフィット」という概念を加え、トレーリングストップを発動させるタイミングをある程度柔軟に調整できるようにしました。
※前回解説した、ミニマムプロフィットを取り込んだコード全体記述はコチラ
今回も引き続き、トレーリングストップについてです。
今回は、トレーリングストップに「ステップ幅」という概念を落とし込んでいきます。
今回の記事を読んでトレーリングストップに「ステップ幅」を実装できるようになれば、トレーリングストップもかなり実践投入に耐えうるものになるのではないかと思います。
ステップ幅とは?
前回解説した「ミニマムプロフィット」の概念を取り入れたことによって、ポジションが一定の利益を達成するような動きになってから、トレーリングストップを発動する事ができるようになりました。
ただ、これだけだとまだ実戦に投入するには十分とは言えません。
94回までの記述だと、一旦ポジションが一定の利益を達成し、トレーリングストップが発動すると、その後は1ポイント分でもトレーリングストップの条件を満たせば、その時点で気ぜわしく再びトレーリングストップが発動してしまいます。
トレーリングストップの発動→その都度OrderSend関数による発注命令をトレードサーバーに送る、という事に他なりませんから、これはトレードサーバーに過剰な負荷をかける可能性があります。
なにより、トレード戦略的に見ても、このままの記述だと頻繁過ぎるストップロスの移動が災いとなり、相場の一時的な逆行に絡めとられる形での不本意な決済になりかねません。
「ステップ幅」とはそういった事態を避けるために、一旦トレーリングストップが発動した後に、再度発動するタイミングを任意に調整できるようにしようという仕組み(MQL5においてはそのコード記述)になります。
言い換えれば、一旦トレーリングストップが発動した後、さらに相場が想定通りの方向(=より利益を生む方向)に向かっても、指定した価格(input変数などで指定する)に達するまではトレーリングストップを発動させない仕組みです。
トレーリングストップにステップ幅を導入する為のロードマップ
では、その「ステップ幅」をどう記述に落とし込んでいくか?それをこれから解説していきます。
ステップ幅を算出する為の値をinput変数でパラメータ化する
//パラメータ
input int TrailingStop = 500;//トレイリンストップを設定する
input int MinimumProfit = 200;//ミニマムプロフィットを設定する
input int StepRange=10;//ステップ幅を設定する
まずはステップ幅を算出する為の値を、input変数でパラメータ化していきます。
ミニマムプロフィットを設定する為のinput変数「MinimumProfit」の下に、input変数「StepRange」を宣言します。初期値は仮に「10」としておきます。
※input変数については以下↓の記事をご参照ください
ポジション選択とトレーリングストップ値の設定有無を確認する
ここは93回、94回と変更がない部分ですが、復習を兼ねて改めて見てみましょう。↓
関数内の記述文、一番最初の
if(PositionSelectByTicket(pTicket) == true && TrailingStop > 0)
は、「ポジションの選択ができて、トレーリングストップの値が指定されている」という意味です。
このif文がtrueであれば、トレーリングストップの設定を執行する{}内の記述に移ります。
<参照>
トレーリングストップ算出に必要な値を取得する その1
トレーリングストップ算出に必要な値を取得する記述は93–94回で解説したものと変更はありません。
{
request.action = TRADE_ACTION_SLTP;
request.position = ポジション番号;
long posType = PositionGetInteger(POSITION_TYPE);//ポジションタイプ
double currentStop = PositionGetDouble(POSITION_SL);//ストップロス価格
double currentLimit = PositionGetDouble(POSITION_TP);//テイクプロフィット価格
MqlTradeRequest構造体の各メンバ変数に値を設定していきます
ストップロスに修正を加えるので.actionには「TRADE_ACTION_SLTP」が入ります。
メンバ変数.position には ポジション番号が代入されます(ポジション番号を格納した変数が記述されるのが一般的でしょう)
ポジション種類が買いか、売りかを取得するにはPositionGetInteger関数の引数に定数値「POSITION_TYPE」を記述します。取得出来た値を変数「posType」に格納します。
現在のストップロス価格を取得するにはPositionGetDouble関数の引数に定数値「POSITION_SL」を記述します。取得出来た値を変数「currentStop」に格納します。
現在のテイクプロフィット価格を取得するにはPositionGetDouble関数の引数に定数値「POSITION_TP」を記述します。取得出来た値を変数「currentLimit」に格納します。
※MqlTradeRequest構造体については↓
第60回「OrderSend関数とMqlTradeRequest構造体」 をご覧ください
トレーリングストップ算出に必要な値を取得する その2
ここで前回からの変更が少し入ります。
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);//ポジションの約定価格
string symbol = PositionGetString(POSITION_SYMBOL);//取引銘柄
double point = SymbolInfoDouble(symbol,SYMBOL_POINT);//ポイント
double trailStop = TrailingStop* point;//トレーリングストップポイント
double minProfit = MinimumProfit* point;//ミニマムプロフィットポイント(この変数を93回の記述に追加している)
if(StepRange<10)StepRange=10;//ステップ幅が10ポイントより小さければステップ幅を10ポイントに設定する。(95回の追加ポイント)
double step=StepRange*_Point;//ステップ幅を価格ベースに変換する(95回の追加ポイント)
double trailStopPrice;//トレーリングストップ価格
double currentProfit;//ミニマムプロフィット価格(この変数を93回の記述に追加している)
前回、宣言したローカル変数で最終的なミニマムプロフィットポイントを格納するローカル変数「minProfit」の下に以下の記述を追加しています。
if(StepRange<10)StepRange=10;//ステップ幅が10ポイントより小さければステップ幅を10ポイントに設定する。(95回の追加ポイント)
double step=StepRange*_Point;//ステップ幅を価格ベースに変換する(95回の追加ポイント)
if(StepRange<10)StepRange=10;
という記述はステップ幅が極端に小さくなると、ステップ幅の機能が意味を為さなくなってしまうので、、input変数「StepRange」に極端に小さい数値が入っている場合、それを修正する(今回のサンプルコードで言えば10ポイント)為の記述です。和訳すると、
「ステップ幅が10ポイントより小さければステップ幅を10ポイントに設定する」
↑という意味の記述となります。
10ポイントより小さい値がinput変数「StepRange」に入っていた場合は、強制的にinput変数「StepRange」の値を10ポイントに設定します。
その下に宣言した、double型のローカル変数「step」には、input変数「StepRange」に定義済み変数「_Point」をかけ合わせた値が入ります。
これによりステップ幅を価格ベースに変換する情報をローカル変数「step」に格納する事ができました。
トレーリングストップの注文を出す(買いの場合)
ステップ幅を加味した、トレーリングストップの値取得が終わったので、次はトレーリングストップの注文を出す記述に移ります。まずは買いの場合です。
if(posType == POSITION_TYPE_BUY)
{ //トレーリングストップ価格を算出
trailStopPrice = SymbolInfoDouble(symbol,SYMBOL_BID) - trailStop;
//ミニマムプロフィット価格を算出(追加箇所)
currentProfit= SymbolInfoDouble(symbol,SYMBOL_BID) - openPrice;
//ミニマムプロフィットの条件・ステップ幅の条件を追加
if(trailStopPrice > currentStop+step && currentProfit>=minProfit)
{
request.sl = trailStopPrice;//
OrderSend(request,result);
}//if(trailStopPrice > currentStop)
}//if(posType == POSITION_TYPE_BUY)
まず、買いの場合のトレーリングストップ価格はBid-変数「trailStop」の値で求まります。
Bidの価格はSymbolInfoDouble関数の第2引数に定数値「SYMBOL_BID」を記述することで取得出来ます。
SymbolInfoDouble(symbol,SYMBOL_BID) – trailStopの計算結果を最終的なトレーリングストップ価格を格納する、double型の変数「trailStopPrice」に格納します。ここの記述は93–94回と同じです。
さらにここにミニマムプロフィットの条件を付け加えたif文が、前回94回時点の↓
if(trailStopPrice > currentStop && currentProfit>=minProfit)
という記述でした。これを以下のように変更します↓
//ミニマムプロフィットの条件・ステップ幅の条件を追加
if(trailStopPrice > currentStop+step && currentProfit>=minProfit)
変数「currentStop」に変数「step」をプラスさせる計算式を加えた形です。
この変更により、この条件式は
「指定したトレーリングストップ値が、現在のストップロス価格+指定のステップ幅より大きく、かつ現在のポジション利益が指定利益(ミニマムプロフィット)より大きい」
と読み解く事ができます。
仮に、指定したトレーリングストップ値が、現在のストップロス価格より大きくなっても、それに指定したステップ幅(サンプルコードの例で言えば10ポイント、価格ベースに変換した値は3桁通貨=0.01、5桁通貨=0.0001)をプラスした価格を越えない限り、再度のトレーリングストップが発動しなくなります。
前回のミニマムプロフィット、今回のステップ幅をトレーリングストップに落とし込んだことで、さらに柔軟性が増しました。
※論理積条件については下記の記事をご参照ください↓
request.sl に変数「trailStopPrice」の値を代入し、OrderSend関数の引数にMqlTradeRequest構造体、MqlTradeResult構造体のインスタンスrequest,resultをそれぞれ設定する所は93回、94回と変わりません。
買いポジションの記述が終わったので、ポジションタイプが「売り」の場合の記述に移ります。
※インスタンスについては↓の記事をご参照ください。
※OrderSend関数やMqlTradeRequest構造体については↓の記事をご参照ください。
・MQL5 EA講座 第60回「OrderSend関数とMqlTradeRequest構造体」
※MqlTradeResult構造体については↓の記事をご参照ください。
・MQL5 EA講座 第61回「MqlTradeResult構造体について」
トレーリングストップの注文を出す(売りの場合)
ポジションタイプが売りだった場合の記述は以下のようになります。
else if(posType == POSITION_TYPE_SELL)
{
trailStopPrice = SymbolInfoDouble(symbol,SYMBOL_ASK) + trailStop;
//ミニマムプロフィット価格を算出(追加箇所)
currentProfit= openPrice -SymbolInfoDouble(symbol,SYMBOL_ASK);
//ミニマムプロフィットの条件・ステップ幅の条件を追加
if(trailStopPrice < currentStop-step && currentProfit>=minProfit)
{
request.sl = trailStopPrice;
OrderSend(request,result);
}//if(trailStopPrice < currentStop && currentProfit>=minProfit)
}//else if(posType == POSITION_TYPE_SELL)
}//if(PositionSelectByTicket(pTicket) == true && TrailingStop > 0)
}//OnTick
売りの場合は、要所要所の記述を買いの時と逆に考えればよいです。
売りの場合のトレーリングストップ価格はAsk+変数「trailStop」の値で求まります。
SymbolInfoDouble(symbol,SYMBOL_ASK) + trailStopの計算結果を最終的なトレーリングストップ価格を格納する、double型の変数「trailStopPrice」に格納します。
変数「currentProfit」に代入する値についてですが、「売り」の場合、価格が下がるほど利益が伸びていくわけですから、計算式は
約定価格-現在値
となります。すなわち、
openPrice –SymbolInfoDouble(symbol,SYMBOL_ASK)の計算結果を価格を変数「currentProfit」に代入すればOKです。
前回94回時点での、ミニマムプロフィットの条件を付け加えたif文の記述は以下のようなものでした。
if(trailStopPrice < currentStop && currentProfit>=minProfit)
これを以下のような記述に変更します。
if(trailStopPrice < currentStop-step && currentProfit>=minProfit)
変数「currentStop」に変数「step」をマイナスさせる計算式を加えた形です。
この変更により、この条件式は
「指定したトレーリングストップ値が、現在のストップロス価格–指定のステップ幅より小さく、かつ現在のポジション利益が指定利益(ミニマムプロフィット)より大きい」
と読み解く事ができます。
仮に、指定したトレーリングストップ値が、現在のストップロス価格より小さくなっても、それに指定したステップ幅(サンプルコードの例で言えば10ポイント、価格ベースに変換した値は3桁通貨=0.01、5桁通貨=0.0001)をマイナスした価格にならない限り、再度のトレーリングストップが発動しなくなります。
買いの時に実装した、ステップ幅の回路が売りの場合にもこれで実装できたことになります。
まとめ
今回は、トレーリングストップに「ステップ幅」の概念を落とし込みました。
もう一度、簡単に「ステップ幅」について説明しておくと、
「ステップ幅」は、「一旦トレーリングストップが発動した後に、再度発動するタイミングを任意に調整できるようにしようという仕組み(MQL5においてはそのコード記述)になります。
一旦トレーリングストップが発動した後、さらに相場が想定通りの方向(=より利益を生む方向)に向かっても、input変数などで設定・調整した指定価格に到達するまではトレーリングストップを発動させない仕組みです。
今回解説したコードの全体図は以下の通りになっています。
※ポジション番号に関する適切な記述をしていないので、コンパイルエラーになります。
あくまで全体的な流れの参考程度にご覧ください。
//パラメータ
input int TrailingStop = 500;//トレイリンストップを設定する
input int MinimumProfit = 200;//ミニマムプロフィットを設定する
input int StepRange=10;//ステップ幅を設定する
-----------------------------------------------------------
void OnTick()
{
if(PositionSelectByTicket(pTicket) == true && TrailingStop > 0)
{
request.action = TRADE_ACTION_SLTP;
request.position = ポジション番号;
long posType = PositionGetInteger(POSITION_TYPE);//ポジションタイプ
double currentStop = PositionGetDouble(POSITION_SL);//ストップロス価格
double currentLimit = PositionGetDouble(POSITION_TP);//テイクプロフィット価格
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);//ポジションの約定価格
string symbol = PositionGetString(POSITION_SYMBOL);//取引銘柄
double point = SymbolInfoDouble(symbol,SYMBOL_POINT);//ポイント
double trailStop = TrailingStop* point;//トレーリングストップポイント
double minProfit = MinimumProfit* point;//ミニマムプロフィットポイント(この変数を93回の記述に追加している)
if(StepRange<10)StepRange=10;//ステップ幅が10ポイントより小さければステップ幅を10ポイントに設定する。(95回の追加ポイント)
double step=StepRange*_Point;//ステップ幅を価格ベースに変換する(95回の追加ポイント)
double trailStopPrice;//トレーリングストップ価格
double currentProfit;//ミニマムプロフィット価格(この変数を93回の記述に追加している)
if(posType == POSITION_TYPE_BUY)
{ //トレーリングストップ価格を算出
trailStopPrice = SymbolInfoDouble(symbol,SYMBOL_BID) - trailStop;
//ミニマムプロフィット価格を算出(追加箇所)
currentProfit= SymbolInfoDouble(symbol,SYMBOL_BID) - openPrice;
//ミニマムプロフィットの条件・ステップ幅の条件を追加
if(trailStopPrice > currentStop+step && currentProfit>=minProfit)
{
request.sl = trailStopPrice;//
OrderSend(request,result);
}//if(trailStopPrice > currentStop)
}//if(posType == POSITION_TYPE_BUY)
else if(posType == POSITION_TYPE_SELL)
{
trailStopPrice = SymbolInfoDouble(symbol,SYMBOL_ASK) + trailStop;
//ミニマムプロフィット価格を算出(追加箇所)
currentProfit= openPrice -SymbolInfoDouble(symbol,SYMBOL_ASK);
//ミニマムプロフィットの条件・ステップ幅の条件を追加
if(trailStopPrice < currentStop-step && currentProfit>=minProfit)
{
request.sl = trailStopPrice;
OrderSend(request,result);
}//if(trailStopPrice < currentStop && currentProfit>=minProfit)
}//else if(posType == POSITION_TYPE_SELL)
}//if(PositionSelectByTicket(pTicket) == true && TrailingStop > 0)
}//OnTick
今回は以上となります。
最後までお読みいただきありがとうございました。
※↓の記事でもトレーリングストップを実装している様子を解説しているので、よろしければ参考にしていただければと思います。
コメント