見出し画像

【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の適用画面
赤い点が高値、青い点が安値

 おまけでシグナルの表示もあるが当てにしない方が良い。
(シグナルの表示は、買いシグナルが水色ドット、売りシグナルがピンクドット、イグジットが黄色ドット)

■ 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);
                    }
            }
        }


    }
}

自己責任でのご利用をお願いします。このコードを使用することによって発生した如何なる損害もご自身でご負担下さい。当方は全ての発生事象や損害について免責とし、賠償請求には一切応じません。

おわり

この記事が気に入ったらサポートをしてみませんか?