【cTrader用インジケータ】直近高値と直近安値に水平線を引く
FXの自動売買プログラミングに興味を持った時期があって、プログラミングが出来るFXトレーディングソフトを使っていたことがある。
日本のFX取り扱い業者で採用されているソフトのうち、プログラミング機能が付いているものにMetaQuotes社の開発したMetaTrader 4がある。省略してMT4(エム・ティー・フォー)と呼ばれることが多い。
このMT4の他に、Spotware Systems社のcTraderというソフトウェアがある。このcTraderは、日本のFXブローカーでの採用例は無いと思うが、個人的には気に入っていて今でも使っている。使いづらいという人もいるので万人にお勧め出来ないが、MT4のプログラミング言語がMT4独自のものであるのに対し、cTraderで使えるプログラミング言語はC#なので、多少は使いやすいかなと思っている。
これまで、自動売買のソフトはMT4用もcTrader用も沢山作ったが、プログラミング趣味欲を満たしてくれはすれど、結果は惨憺たるものですっかり諦めの境地だ。プログラムが暴走して大損したこともあって、通算成績は大きくマイナスになっているので安心して欲しい。私がFX長者になる可能性は微塵も見えていない。
自動売買が駄目なら手動取引の際の判断をサポートしてくれるインジケータならいいだろうということで、インジケータも沢山作った。
しかしどれも役立たずばかりで、作っては捨てるを繰り返してきた。
ところが、最近ようやく気づいたことがある。
役立たずだったのはインジケータではなくて、私自身だったということだ。
インジケータは売買の判断材料として機能するものだが、最終的な決断は人間、つまり私が行う。その私の判断がポンコツ過ぎたがためにインジケータが使えないものと思いこんでいたのではないかということだ。
実際の相場では、ひとつのインジケータのみで正しい判断が出来ることは無い。そもそも正しい判断だったかどうかはエントリーする時点では分からないものだ。私はこのことに気づいていなかった。インジケータによって最適なエントリータイミングを知ろうとしていた。これが間違いのもとだ。
インジケータは相場状況の、ある局面を数値やグラフやサインで示してくれる。例えば、あるインジケータが80を超えたら買いサインだとする。この買いサインというのがエントリータイミングそのものと私は思っていたのだが、そうではないことに気づいた。
インジケータのシグナルは、例えて言えば天気予報の降水確率みたいなものだ。降水確率が何パーセントを超えたら雨が降ると思うだろうか。降水確率が50%だとどれ位の人が傘を持って家を出るだろうか。それは人によって違うのではないか。なぜなら、降水確率による判断は人それぞれだし、状況によっても判断は変わるからだ。
買いサインが点灯しても、それだけで「買い」なわけではない。もちろん買っても良いが、買ったあとにどうするかが重要だったのだ。あるいは買いサインで買わなかったとしたら、その後どうするかが重要だったのだ。
ここで言う「どうするか」というのは利益確定のイグジットポイントを予め決めておくとか、損切りポイントを決めておくといったことだけではなく、何がどうなったらどうするという具体的なシミュレーションをしておくということだ。
前置きが長くなったが、今回紹介するのは私が使用している自作インジケータのうち、直近高値と直近安値に水平のラインを引いてくれる K-Peaks というものだ。cTraderをご利用の方で使って見たいという方は下に載せたコードをご使用下さい。
おまけでシグナルの表示もあるが当てにしない方が良い。
(シグナルの表示は、買いシグナルが水色ドット、売りシグナルがピンクドット、イグジットが黄色ドット)
■ K-Peaks for cTrader ■
using System;
using cAlgo.API;
namespace cAlgo.Indicators
{
[Indicator(IsOverlay = true, AccessRights = AccessRights.None)]
public class KPeaks : Indicator
{
[Parameter(DefaultValue = 5, MinValue = 2)]
public int PastPeriod { get; set; }
[Parameter(DefaultValue = 3, MinValue = 1)]
public int ForePeriod { get; set; }
[Output("Up Peaks", LineColor = "Red", PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries UpPeaks { get; set; }
[Output("Down Peaks", LineColor = "Blue", PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries DownPeaks { get; set; }
[Output("Short Entry", LineColor = "HotPink", PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries Shorters { get; set; }
[Output("Long Entry", LineColor = "Turquoise", PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries Longers { get; set; }
[Output("Short Exit", LineColor = "Yellow", PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries ShortersExit { get; set; }
[Output("Long Exit", LineColor = "Yellow", PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries LongersExit { get; set; }
public double UpPeak_1 = 0;
public double UpPeak_2 = 0;
private double UpPeak_3 = 0;
private double UpPeak_4 = 0;
public double DownPeak_1 = 0;
public double DownPeak_2 = 0;
private double DownPeak_3 = 0;
private double DownPeak_4 = 0;
private int foreTrend = 0;
private double sign_distance = 0.05;
private string signal_comment = "";
private string fore_comment = "";
public string position_status = "SQURE";
private string str_signal = "";
public string signal_out = "";
private int lastIndex = 0;
private double ave_up_peakchange = 0;
private double ave_dn_peakchange = 0;
public override void Calculate(int index)
{
if (index > lastIndex)
{
OnBarClosed(lastIndex);
lastIndex = index;
}
}
private void OnBarClosed(int index)
{
bool status_changed = false;
if (index < PastPeriod)
return;
DrawUpPeaks(index);
DrawDownPeaks(index);
//**************************************************************
// Make Comment
//**************************************************************
int trend = 0;
Color SignalColor = Color.White;
signal_comment = "Changing";
if (UpPeak_1 > UpPeak_2 && Math.Max(UpPeak_2, Math.Max(UpPeak_3, UpPeak_4)) < UpPeak_1)
trend++;
if (UpPeak_1 < UpPeak_2 && Math.Min(UpPeak_2, Math.Min(UpPeak_3, UpPeak_4)) > UpPeak_1)
trend--;
if (DownPeak_1 > DownPeak_2 && Math.Max(DownPeak_2, Math.Max(DownPeak_3, DownPeak_4)) < DownPeak_1)
trend++;
if (DownPeak_1 < DownPeak_2 && Math.Min(DownPeak_2, Math.Min(DownPeak_3, DownPeak_4)) > DownPeak_1)
trend--;
// if (Math.Abs(UpPeak_1 - UpPeak_2) > ave_up_peakchange && Math.Abs(DownPeak_1 - DownPeak_2) > ave_dn_peakchange)
// {
if (trend == 2 && Bars.LowPrices[index] > DownPeak_1)
{
signal_comment = "UP";
SignalColor = Color.Turquoise;
}
if (trend == -2 && Bars.HighPrices[index] < UpPeak_1)
{
signal_comment = "DOWN";
SignalColor = Color.HotPink;
}
if (Math.Abs(trend) == Math.Abs(foreTrend) && signal_comment != "Changing")
signal_comment = "STRONG " + signal_comment;
// }
//**************************************************************
// Entry Signal
//**************************************************************
if (position_status == "SQURE" && fore_comment == "Changing" && signal_comment != "Changing")
{
// 買いエントリーは上昇トレンド下で直近安値に達して上昇したとき
if (signal_comment == "UP" || signal_comment == "STRONG UP")
{
if (Bars.LowPrices[index] > DownPeak_1)
{
position_status = "LONG";
str_signal = "BUY ENTRY";
status_changed = true;
}
}
if (signal_comment == "DOWN" || signal_comment == "STRONG DOWN")
{
if (Bars.HighPrices[index] < UpPeak_1)
{
position_status = "SHORT";
str_signal = "SELL ENTRY";
status_changed = true;
}
}
}
//**************************************************************
// Entry Signal Added
//**************************************************************
string Added = "";
if (!status_changed)
{
if (position_status != "SQURE" && fore_comment == "Changing" && signal_comment != "Changing")
{
// 買いエントリーは上昇トレンド下で直近安値に達して上昇したとき
if (position_status == "LONG" && (signal_comment == "UP" || signal_comment == "STRONG UP"))
{
if (Bars.LowPrices[index] <= UpPeak_1)
{
position_status = "LONG";
str_signal = "BUY ENTRY";
status_changed = true;
Added = " <Added>";
}
}
if (position_status == "SHORT" && (signal_comment == "DOWN" || signal_comment == "STRONG DOWN"))
{
if (Bars.HighPrices[index] >= DownPeak_1)
{
position_status = "SHORT";
str_signal = "SELL ENTRY";
status_changed = true;
Added = " <Added>";
}
}
}
}
//**************************************************************
// Exit Signal
//**************************************************************
if (position_status != "SQURE")
{
// 買いポジション場合、直近安値を下回ったらEXIT
if (position_status == "LONG")
{
//
////|| Bars.ClosePrices[index] < UpPeak_2
// You should be Bid on your BOT.
if (Bars.ClosePrices[index - 1] < DownPeak_1)
{
// Entry Cancel on this bar
if (str_signal == "BUY ENTRY")
{
position_status = "SQURE";
str_signal = "";
status_changed = false;
}
else
{
// Exit
position_status = "SQURE";
str_signal = "BUY Exit";
status_changed = true;
}
}
}
if (position_status == "SHORT")
{
//
////|| Bars.ClosePrices[index] > DownPeak_2
if (Bars.ClosePrices[index - 1] > UpPeak_1)
{
// Entry Cancel on this bar
if (str_signal == "SELL ENTRY")
{
position_status = "SQURE";
str_signal = "";
status_changed = false;
}
else
{
// Exit Short Position
position_status = "SQURE";
str_signal = "SELL Exit";
status_changed = true;
}
}
}
}
//**************************************************************
// Signal Output
//**************************************************************
switch (str_signal)
{
case "BUY ENTRY":
Longers[index] = Bars.LowPrices[index] * (100 - sign_distance) / 100;
break;
case "SELL ENTRY":
Shorters[index] = Bars.HighPrices[index] * (100 + sign_distance) / 100;
break;
case "BUY Exit":
LongersExit[index] = Bars.HighPrices[index] * (100 + sign_distance) / 100;
break;
case "SELL Exit":
ShortersExit[index] = Bars.LowPrices[index] * (100 - sign_distance) / 100;
break;
}
//**************************************************************
// set to fore variables
//**************************************************************
foreTrend = trend;
fore_comment = signal_comment;
//**************************************************************
// Log & OUTPUT
//**************************************************************
if (status_changed)
{
// For OUTPUT
signal_out = str_signal;
// For LOG
str_signal = str_signal == "" ? "" : ", Signal = " + str_signal;
Print("K-Peaks LOG on " + Bars.OpenTimes + " >> Trend value = " + trend + ", Comment = " + signal_comment + ", Position = " + position_status + str_signal + Added);
str_signal = "";
}
else
{
// For OUTPUT
signal_out = "";
}
//**************************************************************
// Object Drawing
//**************************************************************
var TXT = Chart.DrawStaticText("Trend", "Trend Status: " + signal_comment, VerticalAlignment.Top, HorizontalAlignment.Right, SignalColor);
Chart.RemoveObject("Hline1");
Chart.RemoveObject("Lline1");
Chart.RemoveObject("Hline2");
Chart.RemoveObject("Lline2");
var HL1 = Chart.DrawHorizontalLine("Hline1", UpPeak_1, Color.Red, 1, LineStyle.Solid);
var HL2 = Chart.DrawHorizontalLine("Hline2", UpPeak_2, Color.HotPink, 1, LineStyle.Solid);
var LL1 = Chart.DrawHorizontalLine("Lline1", DownPeak_1, Color.Blue, 1, LineStyle.Solid);
var LL2 = Chart.DrawHorizontalLine("Lline2", DownPeak_2, Color.Turquoise, 1, LineStyle.Solid);
}
private void DrawUpPeaks(int index)
{
int period = PastPeriod + ForePeriod;
int middleIndex = index - ForePeriod;
double middleValue = Bars.HighPrices[middleIndex];
bool up = true;
for (int i = 0; i < period; i++)
{
if (middleValue < Bars.HighPrices[index - i])
{
up = false;
break;
}
}
if (up)
{
UpPeaks[middleIndex] = middleValue;
UpPeak_4 = UpPeak_3;
UpPeak_3 = UpPeak_2;
UpPeak_2 = UpPeak_1;
UpPeak_1 = middleValue;
if (UpPeak_2 != 0)
{
if (ave_up_peakchange != 0)
{
ave_up_peakchange = (ave_up_peakchange + Math.Abs(UpPeak_1 - UpPeak_2)) / 2;
}
else
{
ave_up_peakchange = Math.Abs(UpPeak_1 - UpPeak_2);
}
}
}
}
private void DrawDownPeaks(int index)
{
int period = PastPeriod + ForePeriod;
//Period % 2 == 0 ? Period - 1 : Period;
int middleIndex = index - ForePeriod;
//index - period / 2;
double middleValue = Bars.LowPrices[middleIndex];
bool down = true;
for (int i = 0; i < period; i++)
{
if (middleValue > Bars.LowPrices[index - i])
{
down = false;
break;
}
}
if (down)
{
DownPeaks[middleIndex] = middleValue;
DownPeak_4 = DownPeak_3;
DownPeak_3 = DownPeak_2;
DownPeak_2 = DownPeak_1;
DownPeak_1 = middleValue;
if (DownPeak_2 != 0)
if (ave_dn_peakchange != 0)
{
ave_dn_peakchange = (ave_dn_peakchange + Math.Abs(DownPeak_1 - DownPeak_2)) / 2;
}
else
{
ave_dn_peakchange = Math.Abs(DownPeak_1 - DownPeak_2);
}
}
}
}
}
おわり
この記事が気に入ったらサポートをしてみませんか?