Category Other  Published on 19/08/2024

Ctrader Strategy Tester

Description

Special Announcement: A Major Project is Underway - cTrader Strategy Tester

We are excited to announce the launch of a groundbreaking project set to transform the way you trade: the cTrader Strategy Tester. This new tool is designed to provide traders with in-depth analysis of various strategies, allowing you to assess their performance, advantages, and disadvantages—all based on reliable and accurate data.

We are actively seeking developers, backtesters, and strategy creators to join this exciting journey. You have the opportunity to contribute to an innovative project that could revolutionize your trading experience.

Why Join the cTrader Strategy Tester Project?

  • Master Your Strategies: Test and compare different trading strategies before using them in real-market conditions.
  • Identify Pros and Cons: Gain detailed insights into the strengths and weaknesses of each strategy.
  • Optimize Your Decisions: Leverage accurate data to maximize your chances of success when making market decisions.

Get Involved Now!

If you're passionate about trading and want to develop, test, or create new trading strategies, join us now. Together, we will build a powerful tool that will give you the edge you need.

Be part of the future of trading with the cTrader Strategy Tester!

Click here (Telegram) to participate in this exceptional project or here (Github) to see all you need to know about this exceptional project



Quantitative Analysis: The Key to Mastering Trading Strategies

Project to Develop a CBot for Trading Strategy Analysis

The objective of this project is to develop a CBot for the cTrader platform, capable of analyzing a wide range of trading strategies. This robot is based on the NNFX principle to identify effective trading strategies without relying on complex risk management methods such as grids, hedging, or martingale.

What is NNFX?

NNFX, or No Nonsense Forex, is a trading approach that emphasizes simplicity and efficiency in strategy development. Rather than focusing on common trading indicators or complex risk management methods, NNFX advocates for the use of lesser-known but robust indicators, logically combined to form a coherent and effective strategy. The goal is to minimize market noise and maximize the clarity of trading signals.

Quantitative Analysis

Quantitative analysis is a method of evaluating trading strategies based on numerical data. It involves collecting and analyzing historical market data to assess the performance of strategies in terms of gains, losses, and other key indicators. This approach allows for the identification of the best strategies based on measurable results, rather than assumptions or intuition. It is essential for understanding how a strategy might perform under real market conditions.

Call for Participation

We invite all interested individuals to join this project. Whether you are a 

  • developer/engineer, 
  • back-tester, 
  • strategy designer

Your expertise will be invaluable. Together, we can refine this CBot and develop robust analysis tools for traders.

Robot Foundation

  • Simplified Risk Parameters: The robot integrates simplified risk management parameters, such as fixed volumes or a percentage of the account at risk.
  • Decision-Making According to the NNFX Strategy: The robot will make decisions based on the NNFX approach, ensuring a systematic and proven trading method.
  • Strategy Report: The robot will provide detailed reports including indicators such as Drawdown (DD), Profit Factor, minimum number of consecutive wins, average consecutive wins, etc.

This project aims to create a powerful tool for traders seeking to optimize their strategies through a rigorous analytical approach and simplified risk management.

GitHub Project : https://github.com/YesOrNotCtrader/Ctrader-Stragegy-Tester-Project
Project Group telegram Link :  https://t.me/+Z_YpBsJmznVjMTQ8
 

Previous account here : https://ctrader.com/users/profile/70920users/profile/70920
Contact telegram :  https://t.me/nimi012 

Here is a YouTube playlist that covers the project we are going to create:


https://youtube.com/playlist?list=PLKO_4Dpj9Esz7EliB7HvxVHsADEKT5SfH&si=Nv-3Me0uURzNq40K

 

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class NNFXStrategieTesterv101 : Robot
    {
        // DAYS TO TRADE 
        [Parameter("Monday", DefaultValue = true, Group = "===========================================================\nPERIOD SETTING\n===========================================================\nDay To Trade")]
        public bool Mon { get; set; }
        [Parameter("Tuesday", DefaultValue = true, Group = "===========================================================\nPERIOD SETTING\n===========================================================\nDay To Trade")]
        public bool Tue { get; set; }
        [Parameter("Wednesday", DefaultValue = true, Group = "===========================================================\nPERIOD SETTING\n===========================================================\nDay To Trade")]
        public bool Wed { get; set; }
        [Parameter("Thuesday", DefaultValue = true, Group = "===========================================================\nPERIOD SETTING\n===========================================================\nDay To Trade")]
        public bool Thu { get; set; }
        [Parameter("Friday", DefaultValue = true, Group = "===========================================================\nPERIOD SETTING\n===========================================================\nDay To Trade")]
        public bool Fri { get; set; }
        [Parameter("Saturday", DefaultValue = true, Group = "===========================================================\nPERIOD SETTING\n===========================================================\nDay To Trade")]
        public bool Sat { get; set; }
        [Parameter("Sunday", DefaultValue = true, Group = "===========================================================\nPERIOD SETTING\n===========================================================\nDay To Trade")]
        public bool Sun { get; set; }
        //Time TO TRADE 
        [Parameter("Trade Start Hour", DefaultValue = 0, Group = "Time To Trade")]
        public int StartHour { get; set; }
        [Parameter("Trade End Hour", DefaultValue = 24, Group = "Time To Trade")]
        public int EndHour { get; set; }

        //Risk TO TRADE 
        [Parameter("Type", DefaultValue = EnumSelectionRiskType.Perentage_Risk, Group = "===========================================================\nMONNEY MANNAGEMENT\n===========================================================")]
        public EnumSelectionRiskType SelectionRiskType { get; set; }
        public enum EnumSelectionRiskType
        {
            Fixed_Lot,
            Fixed_Volume,
            Perentage_Risk,
        }
        [Parameter("Value", DefaultValue = 1, Group = "===========================================================\nMONNEY MANNAGEMENT\n===========================================================")]
        public double RiskValue { get; set; }
        [Parameter("Max Spread ", DefaultValue = 2, Group = "===========================================================\nMONNEY MANNAGEMENT\n===========================================================")]
        public double SpreadMax { get; set; }

        //SL Setting -> need add trailing stop, swing sl, fixed sl 
        [Parameter("Atr SL Period ", DefaultValue = 24, Group = "===========================================================\nMONNEY MANNAGEMENT\n===========================================================")]
        public int AtrSLPeriod { get; set; }
        [Parameter("Atr SL Multiplier ", DefaultValue = 1.5, Group = "===========================================================\nMONNEY MANNAGEMENT\n===========================================================")]
        public double AtrSLMultiplier { get; set; }
        [Parameter("AtrSL Ma Type", DefaultValue = MovingAverageType.Exponential, Group = "===========================================================\nMONNEY MANNAGEMENT\n===========================================================")]
        public MovingAverageType AtrSLMaType { get; set; }

        //INDICATOR OUTPUT
        //Baseline Setting
        [Parameter("Time Frame", DefaultValue = "Daily", Group = "###########################################################\n###   STRATEGIE TESTER  #####################################\n###########################################################\n===========================================================\nBASELINE 1\n===========================================================")]
        public TimeFrame Baseline1TimeFrame { get; set; }
        [Parameter("Use it ?", DefaultValue = true, Group = "  => Moving Average                         STRATEGIE 1")]
        public bool Baseline1MovingAverageUse { get; set; }
        [Parameter("     Period", DefaultValue = 34, Group = "        => Moving Average                         STRATEGIE 1")]
        public int Baseline1MovingAveragePeriod { get; set; }
        [Parameter("     Moving Average Type", DefaultValue = MovingAverageType.Simple, Group = "        => Moving Average                         STRATEGIE 1")]
        public MovingAverageType Baseline1MovingAverageMaType { get; set; }
        [Parameter("     Signal Type", DefaultValue = EnumSignalOverUnderType.Over_Positive_Under_Negative, Group = "  => Moving Average                         STRATEGIE 1")]
        public EnumSignalOverUnderType Baseline1MovingAverageSignal { get; set; }
        public enum EnumSignalOverUnderType
        {
            Over_Positive_Under_Negative,
            Over_Negative_Under_Positive,
        }
        //Entry Setting
        [Parameter("Time Frame", DefaultValue = "Hour", Group = "\n===========================================================\nENTRY 1\n===========================================================")]
        public TimeFrame Entry1TimeFrame { get; set; }
        [Parameter("Use it ?", DefaultValue = true, Group = "  => Rsi                         ENTRY 1")]
        public bool Entry1RsiUse { get; set; }
        [Parameter("     Period", DefaultValue = 13, Group = "  => Rsi                         ENTRY 1")]
        public int Entry1RsiPeriod { get; set; }
        [Parameter("     Period Signal", DefaultValue = 9, Group = "  => Rsi                         ENTRY 1")]
        public int Entry1RsiPeriodSignal { get; set; }
        [Parameter("     Moving Average Type", DefaultValue = MovingAverageType.Simple, Group = "  => Rsi                         ENTRY 1")]
        public MovingAverageType Entry1RsiPeriodSignalMaType { get; set; }

        [Parameter("     Signal Type", DefaultValue = EnumSignalCrossType.Cross_On_Level, Group = "  => Rsi                         ENTRY 1")]
        public EnumSignalCrossType Entry1RsiSignal { get; set; }
        public enum EnumSignalCrossType
        {
            Cross_On_Level,
            Cross_On_Signal,
            Cross_On_Signal_And_Level,
            Cross_On_Level_And_Signal,
        }
        [Parameter("     Level Buy", DefaultValue = 30, Group = "  => Rsi                         ENTRY 1")]
        public double Entry1RsiLevelBuy { get; set; }
        [Parameter("     Level Sell", DefaultValue = 70, Group = "  => Rsi                         ENTRY 1")]
        public double Entry1RsiLevelSell { get; set; }
        [Parameter("     LookBack Min", DefaultValue = 1, MinValue = 1, Group = "  => Rsi                         ENTRY 1")]
        public int Entry1RsiLookBackMin { get; set; }

        //Confirmation Setting
        [Parameter("Time Frame", DefaultValue = "Hour", Group = "\n===========================================================\nCONFIRMATION 1\n===========================================================")]
        public TimeFrame Confirmation1TimeFrame { get; set; }
        [Parameter("Use it ?", DefaultValue = false, Group = "   => Macd                         CONFIRMATION 1")]
        public bool Confirmation1MacdUse { get; set; }
        [Parameter("     Period Slow", DefaultValue = 26, Group = "  => Macd                         CONFIRMATION 1")]
        public int Confirmation1MacdPeriodSlow { get; set; }
        [Parameter("     Period Fast", DefaultValue = 12, Group = "  => Macd                         CONFIRMATION 1")]
        public int Confirmation1MacdPeriodFast { get; set; }
        [Parameter("     Period Signal", DefaultValue = 9, Group = "  => Macd                         CONFIRMATION 1")]
        public int Confirmation1MacdPeriodSignal { get; set; }
        [Parameter("     Signal Type", DefaultValue = EnumSignalDoubleOverUnderType.Over_Level_And_Signal_Positive_Or_Under_Negative, Group = "  => Macd                         CONFIRMATION 1")]
        public EnumSignalDoubleOverUnderType Confirmation1MacdSignal { get; set; }
        public enum EnumSignalDoubleOverUnderType
        {
            Over_Level_And_Signal_Positive_Or_Under_Negative,
            Over_Level_And_Signal_Negative_Or_Under_Positive,

            Over_Level_And_Under_Signal_Positive_Or_Under_Level_And_Over_Signal_Negative,
            Over_Level_And_Under_Signal_Negative_Or_Under_Level_And_Over_Signal_Positive,
        }

        [Parameter("     Level Buy", DefaultValue = 0, Group = "  => Macd                         CONFIRMATION 1")]
        public double Confirmation1MacdLevelBuy { get; set; }
        [Parameter("     Level Sell", DefaultValue = 0, Group = "  => Macd                         CONFIRMATION 1")]
        public double Confirmation1MacdLevelSell { get; set; }
        [Parameter("     LookBack Min", DefaultValue = 1, MinValue = 1, Group = "  => Macd                         CONFIRMATION 1")]
        public int Confirmation1MacdLookBackMin { get; set; }

        //Volume Setting
        [Parameter("Time Frame", DefaultValue = "Hour", Group = "\n===========================================================\nVOLUME 1\n===========================================================")]
        public TimeFrame Volume1TimeFrame { get; set; }
        [Parameter("Use it ?", DefaultValue = false, Group = "  => Mfi                         VOLUME 1")]
        public bool Volume1MfiUse { get; set; }
        [Parameter("     Period", DefaultValue = 13, Group = "   => Mfi                         VOLUME 1")]
        public int Volume1MfiPeriod { get; set; }
        [Parameter("     Period Signal", DefaultValue = 9, Group = "  => Mfi                         VOLUME 1")]
        public int Volume1MfiPeriodSignal { get; set; }
        [Parameter("     Moving Average Type", DefaultValue = MovingAverageType.Simple, Group = "  => Mfi                         VOLUME 1")]
        public MovingAverageType Volume1MfiPeriodSignalMaType { get; set; }
        [Parameter("     Signal Type", DefaultValue = EnumSignalOverUnderType.Over_Negative_Under_Positive, Group = "   => Mfi                         VOLUME 1")]
        public EnumSignalOverUnderType Volume1MfiSignal { get; set; }
        [Parameter("     Level Buy", DefaultValue = 30, Group = "  => Mfi                         VOLUME 1")]
        public double Volume1MfiLevelBuy { get; set; }
        [Parameter("     Level Sell", DefaultValue = 70, Group = "  => Mfi                         VOLUME 1")]
        public double Volume1MfiLevelSell { get; set; }
        [Parameter("     LookBack Min", DefaultValue = 1, MinValue = 1, Group = "  => Mfi                         VOLUME 1")]
        public int Volume1MfiLookBackMin { get; set; }

        //Exit Setting
        [Parameter("Time Frame", DefaultValue = "Hour", Group = "\n===========================================================\nEXIT 1\n===========================================================")]
        public TimeFrame Exit1TimeFrame { get; set; }
        [Parameter("Use it ?", DefaultValue = true, Group = "  => SuperTrend                         EXIT 1")]
        public bool Exit1SuperTrendUse { get; set; }
        [Parameter("     Period", DefaultValue = 13, Group = "  => SuperTrend                         EXIT 1")]
        public int Exit1SuperTrendPeriod { get; set; }
        [Parameter("     Period Multi", DefaultValue = 1, Group = "  => SuperTrend                         EXIT 1")]
        public double Exit1SuperTrendPeriodMulti { get; set; }
        [Parameter("     Signal Type", DefaultValue = EnumSignalOverUnderType.Over_Positive_Under_Negative, Group = "  => SuperTrend                         EXIT 1")]
        public EnumSignalOverUnderType Exit1SuperTrendSignal
        { get; set; }

        //Init indicator
        private MovingAverage baseline1MovingAverage;
        private ST_RsiMa entry1Rsi;
        private MacdCrossOver confirmation1Macd;
        private ST_MfiMa volume1Mfi;
        private ST_SuperTrend exit1Supertrend;

        //SL Function
        private AverageTrueRange atr;

        //Base Setting 
        private string botNameLabelBuy;
        private string botNameLabelSell;

        //Statistics Setting 
        private List<List<double>> positionStat;
        private double initialBalance;
        private double drawUpMax;

        protected override void OnStart()
        {
            //Init indicator
            baseline1MovingAverage = Indicators.MovingAverage(MarketData.GetBars(Baseline1TimeFrame).ClosePrices, Baseline1MovingAveragePeriod, Baseline1MovingAverageMaType);
            entry1Rsi = Indicators.GetIndicator<ST_RsiMa>(MarketData.GetBars(Entry1TimeFrame), Entry1RsiPeriod, Entry1RsiPeriodSignal, Entry1RsiPeriodSignalMaType);
            confirmation1Macd = Indicators.MacdCrossOver(MarketData.GetBars(Confirmation1TimeFrame).ClosePrices, Confirmation1MacdPeriodSlow, Confirmation1MacdPeriodFast, Confirmation1MacdPeriodSignal);
            volume1Mfi = Indicators.GetIndicator<ST_MfiMa>(MarketData.GetBars(Volume1TimeFrame), Volume1MfiPeriod, Volume1MfiPeriodSignal, Volume1MfiPeriodSignalMaType);
            exit1Supertrend = Indicators.GetIndicator<ST_SuperTrend>(MarketData.GetBars(Exit1TimeFrame), Exit1SuperTrendPeriod, Exit1SuperTrendPeriodMulti);

            //SL Function
            atr = Indicators.AverageTrueRange(MarketData.GetBars(Exit1TimeFrame), AtrSLPeriod, AtrSLMaType);

            //Base Setting 

            botNameLabelBuy = SymbolName + " " + "ENTER BUY " + TimeFrame + " " + (Bars.OpenTimes.Last(0).ToLocalTime().ToString());
            botNameLabelSell = SymbolName + " " + "ENTER SELL " + TimeFrame + " " + (Bars.OpenTimes.Last(0).ToLocalTime().ToString());
            Positions.Closed += OnPositionsClosed;

            //Statistics Setting 
            initialBalance = Account.Balance;
            positionStat = new List<List<double>>();
            drawUpMax = 0.0;

        }
        protected override void OnTick()
        {
            DrawUp();
        }

        protected override void OnBar()
        {
            //Check For position before enter -> possible to add parametter with multi position enter
            if ((Positions.FindAll(botNameLabelBuy, SymbolName).Length == 0 && Positions.FindAll(botNameLabelSell, SymbolName).Length == 0) && Server.Time.ToLocalTime().Hour >= StartHour && Server.Time.ToLocalTime().Hour < EndHour)
            {
                if (CheckEntryConditions() && Signal(TradeType.Buy) == 1)
                    Open(TradeType.Buy, botNameLabelBuy);
                if (CheckEntryConditions() && Signal(TradeType.Sell) == -1)
                    Open(TradeType.Sell, botNameLabelSell);
            }
            //Check For Buy Position before Exit
            if (Positions.FindAll(botNameLabelBuy, SymbolName, TradeType.Buy).Length > 0)
            {
                if (GetExit1(TradeType.Buy) == -1)
                    Close(TradeType.Buy, botNameLabelBuy);
            }
            //Check For Sell Position before Exit
            if (Positions.FindAll(botNameLabelSell, SymbolName, TradeType.Sell).Length > 0)
            {
                if (GetExit1(TradeType.Sell) == 1)
                    Close(TradeType.Sell, botNameLabelSell);
            }

        }
        private int Signal(TradeType tradeType)
        {
            // all function to get Signal Enter
            if ((GetBasline1(tradeType) == 1) && (GetEntry1(tradeType) == 1) && (GetCondirmation1(tradeType) == 1) && (GetVolume1(tradeType) == 1))
                return 1;

            else if ((GetBasline1(tradeType) == -1) && (GetEntry1(tradeType) == -1) && (GetCondirmation1(tradeType) == -1) && (GetVolume1(tradeType) == -1))
                return -1;

            else
                return 0;
        }
        private int GetBasline1(TradeType tradeType) // all indicator adding on baseline are implemented here
        {
            //int idx1 = Baseline1TimeFrame == Chart.TimeFrame ? index : barsTF.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]); -> need to backtest for knowing if special index is needed for multiTimeframe

            var resultMovingAverage = FunctionSignalUnderOver(Bars.ClosePrices, baseline1MovingAverage.Result, Baseline1MovingAverageSignal); // FunctionSignalUnderOver(DataSeries result, DataSeries Signal, EnumSignalOverUnderType signalOverUnder)

            // without baseline
            if (!Baseline1MovingAverageUse && tradeType == TradeType.Buy)
                return 1;
            else if (!Baseline1MovingAverageUse && tradeType == TradeType.Sell)
                return -1;
            // with baseline
            else if ((!Baseline1MovingAverageUse || resultMovingAverage == 1) && tradeType == TradeType.Buy) // add all variable with this sentence (exemple): (!Volume1MfiUse || resultMfi == 1)
                return 1;
            else if ((!Baseline1MovingAverageUse || resultMovingAverage == -1) && tradeType == TradeType.Sell)
                return -1;
            else
                return 0;
        }
        private int GetEntry1(TradeType tradeType) // all indicator adding on entry are implemented here
        {
            var resultRsi = FunctionSignalCross(entry1Rsi.Result, entry1Rsi.Signal, Entry1RsiSignal, Entry1RsiLevelBuy, Entry1RsiLevelSell, Entry1RsiLookBackMin); //FunctionSignalCross(DataSeries result, DataSeries signal, EnumSignalCrossType signalOverUnder, double levelBuy, double levelSell, int lookBack)

            // without Entry
            if (!Entry1RsiUse && tradeType == TradeType.Buy)
                return 1;
            else if (!Entry1RsiUse && tradeType == TradeType.Sell)
                return -1;
            // with Entry
            else if ((!Entry1RsiUse || resultRsi == 1) && tradeType == TradeType.Buy) // add all variable with this sentence (exemple): (!Entry1RsiUse || resultRsi == 1)
                return 1;
            else if ((!Entry1RsiUse || resultRsi == -1) && tradeType == TradeType.Sell)
                return -1;
            else
                return 0;
        }

        private int GetCondirmation1(TradeType tradeType) // all indicator adding on confirmation are implemented here
        {
            var resultMacd = FunctionSignalDoubleSignalUnderOver(confirmation1Macd.MACD, confirmation1Macd.Signal, Confirmation1MacdSignal, Confirmation1MacdLevelBuy, Confirmation1MacdLevelSell); // FunctionSignalDoubleSignalUnderOver(DataSeries result, DataSeries signal, EnumSignalDoubleOverUnderType signalOverUnder, double levelBuy, double levelSell)

            // without Condirmation
            if (!Confirmation1MacdUse && tradeType == TradeType.Buy)
                return 1;
            else if (!Confirmation1MacdUse && tradeType == TradeType.Sell) // without Condirmation
                return -1;
            // with Condirmation
            else if ((!Confirmation1MacdUse || resultMacd == 1) && tradeType == TradeType.Buy) // add all variable with this sentence : (!Confirmation1MacdUse || resultMacd == 1)
                return 1;
            else if ((!Confirmation1MacdUse || resultMacd == -1) && tradeType == TradeType.Sell)
                return -1;
            else
                return 0;
        }

        private int GetVolume1(TradeType tradeType) // all indicator adding on volume are implemented here
        {
            var resultMfi = FunctionSignalUnderOver(volume1Mfi.Result, volume1Mfi.Signal, Volume1MfiSignal); // FunctionignalUnderOver(DataSeries result, DataSeries Signal, EnumSignalOverUnderType signalOverUnder)

            // without Volume
            if (!Volume1MfiUse && tradeType == TradeType.Buy)
                return 1;
            else if (!Volume1MfiUse && tradeType == TradeType.Sell) // without Volume
                return -1;
            // with Volume
            else if ((!Volume1MfiUse || resultMfi == 1) && tradeType == TradeType.Buy)  // add all variable with this sentence : (!Volume1MfiUse || resultMfi == 1)
                return 1;
            else if ((!Volume1MfiUse || resultMfi == -1) && tradeType == TradeType.Sell)
                return -1;
            else
                return 0;
        }

        private int GetExit1(TradeType tradeType) // all indicator adding on exit are implemented here
        {
            var resultSuperTrend = FunctionCross(Bars.ClosePrices, exit1Supertrend.Result); // FunctionCross(DataSeries result, DataSeries signal)

            // without Exit
            if (!Exit1SuperTrendUse && tradeType == TradeType.Buy) // without Exit
                return 1;
            else if (!Exit1SuperTrendUse && tradeType == TradeType.Sell)
                return -1;
            // with Exit
            else if ((!Exit1SuperTrendUse || resultSuperTrend == 1) && tradeType == TradeType.Sell) // add all variable with this sentence : (!Volume1MfiUse || resultMfi == 1)
                return 1;
            else if ((!Exit1SuperTrendUse || resultSuperTrend == -1) && tradeType == TradeType.Buy)
                return -1;
            else
                return 0;
        }

        // FUNCTION FOR SIGNAL -> need backtest and finding if other conditions need to be add
        // Confirmation Type Function
        private int FunctionSignalUnderOver(DataSeries result, DataSeries Signal, EnumSignalOverUnderType signalOverUnder)
        {
            if (signalOverUnder == EnumSignalOverUnderType.Over_Positive_Under_Negative)
            {
                if (result.Last(1) > Signal.Last(1))
                    return 1;
                else if (result.Last(1) < Signal.Last(1))
                    return -1;
                else
                    return 0;
            }
            else // Over_Negative_Under_Positive
            {
                if (result.Last(1) < Signal.Last(1))
                    return 1;
                else if (result.Last(1) > Signal.Last(1))
                    return -1;
                else
                    return 0;
            }
        }
        // Confirmation Type Function
        private int FunctionSignalDoubleSignalUnderOver(DataSeries result, DataSeries signal, EnumSignalDoubleOverUnderType signalOverUnder, double levelBuy, double levelSell)
        {
            if (signalOverUnder == EnumSignalDoubleOverUnderType.Over_Level_And_Signal_Positive_Or_Under_Negative)
            {
                if (result.Last(1) > levelBuy && result.Last(1) > signal.Last(1))
                    return 1;
                else if (result.Last(1) < levelSell && result.Last(1) < signal.Last(1))
                    return -1;
                else
                    return 0;
            }
            else if (signalOverUnder == EnumSignalDoubleOverUnderType.Over_Level_And_Signal_Positive_Or_Under_Negative)
            {
                if (result.Last(1) < levelBuy && result.Last(1) < signal.Last(1))
                    return 1;
                else if (result.Last(1) > levelSell && result.Last(1) > signal.Last(1))
                    return -1;
                else
                    return 0;
            }
            else if (signalOverUnder == EnumSignalDoubleOverUnderType.Over_Level_And_Under_Signal_Positive_Or_Under_Level_And_Over_Signal_Negative)
            {
                if (result.Last(1) > levelBuy && result.Last(1) < signal.Last(1))
                    return 1;
                else if (result.Last(1) < levelSell && result.Last(1) > signal.Last(1))
                    return -1;
                else
                    return 0;
            }

            else // Over_Level_And_Under_Signal_Negative_Or_Under_Level_And_Over_Signal_Positive
            {
                if (result.Last(1) < levelBuy && result.Last(1) > signal.Last(1))
                    return 1;
                else if (result.Last(1) > levelSell && result.Last(1) < signal.Last(1))
                    return -1;
                else
                    return 0;
            }
        }
        // Real Enter Type Function -> min and max for having possibility to find range period with Multiple signal (Maybe need a special function) it work but it's not good When I think about it. (result.Minimum(lookBack + 1) <= signal.Last(2) && result.Last(1) > signal.Last(1))
        private int FunctionSignalCross(DataSeries result, DataSeries signal, EnumSignalCrossType signalOverUnder, double levelBuy, double levelSell, int lookBack)
        {
            if (signalOverUnder == EnumSignalCrossType.Cross_On_Level)
            {
                if (result.Minimum(lookBack + 1) <= levelBuy && result.Last(1) > levelBuy)
                    return 1;
                else if (result.Maximum(lookBack + 1) >= levelSell && result.Last(1) < levelSell)
                    return -1;
                else
                    return 0;
            }
            else if (signalOverUnder == EnumSignalCrossType.Cross_On_Level_And_Signal)
            {
                if (result.Minimum(lookBack + 1) <= levelBuy && result.Last(1) > levelBuy && signal.Last(1) > signal.Last(1))
                    return 1;
                else if (result.Maximum(lookBack + 1) >= levelSell && result.Last(1) < levelSell && signal.Last(1) < signal.Last(1))
                    return -1;
                else
                    return 0;
            }
            else if (signalOverUnder == EnumSignalCrossType.Cross_On_Signal)
            {
                if (result.Minimum(lookBack + 1) <= signal.Last(2) && result.Last(1) > signal.Last(1))
                    return 1;
                else if (result.Maximum(lookBack + 1) >= signal.Last(2) && result.Last(1) < signal.Last(1))
                    return -1;
                else
                    return 0;
            }

            else // Cross_On_Signal_And_Level
            {
                if (result.Minimum(lookBack + 1) <= signal.Last(2) && result.Last(1) > signal.Last(1) && result.Last(1) < levelBuy)
                    return 1;
                else if (result.Maximum(lookBack + 1) >= signal.Last(2) && result.Last(1) < signal.Last(1) && result.Last(1) > levelSell)
                    return -1;
                else
                    return 0;
            }
        }
        // Exit Type Function
        private int FunctionCross(DataSeries result, DataSeries signal)
        {
            if (result.Last(2) < signal.Last(2) && result.Last(1) >= signal.Last(1))
                return 1;
            else if (result.Last(2) > signal.Last(2) && result.Last(1) <= signal.Last(1))
                return -1;
            else
                return 0;
        }

        // FUNCTION BASE ROBOT
        // Function time to trade, days, and spred
        private bool CheckEntryConditions()
        {
            bool chkDayofWeek = ((Bars.OpenTimes.Last(0).ToLocalTime().DayOfWeek == DayOfWeek.Monday && Mon == true) || (Bars.OpenTimes.Last(0).ToLocalTime().DayOfWeek == DayOfWeek.Tuesday && Tue == true)
            || (Bars.OpenTimes.Last(0).ToLocalTime().DayOfWeek == DayOfWeek.Wednesday && Wed == true) || (Bars.OpenTimes.Last(0).ToLocalTime().DayOfWeek == DayOfWeek.Thursday && Thu == true) || (Bars.OpenTimes.Last(0).ToLocalTime().DayOfWeek == DayOfWeek.Friday && Fri == true)
            || (Bars.OpenTimes.Last(0).ToLocalTime().DayOfWeek == DayOfWeek.Saturday && Sat == true) || (Bars.OpenTimes.Last(0).ToLocalTime().DayOfWeek == DayOfWeek.Sunday && Sun == true));

            if ((Bars.OpenTimes.Last(0).ToLocalTime().Hour >= StartHour) && (Bars.OpenTimes.Last(0).ToLocalTime().Hour <= EndHour) && (chkDayofWeek == true) && (Symbol.Spread < (SpreadMax * Symbol.PipSize)))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        // Function Sl -> need to add swing, fixed sl, 
        private double GetSL(TradeType tradeType)
        {
            if (tradeType == TradeType.Buy)
                return (Bars.ClosePrices.Last(1) - Bars.LowPrices.Last(1) + atr.Result.Last(1)) / Symbol.PipSize;
            else
                return (Bars.HighPrices.Last(1) - Bars.ClosePrices.Last(1) + atr.Result.Last(1)) / Symbol.PipSize;
        }
        private void Open(TradeType tradeType, string label)
        {
            var tradeAmount = 0.0;
            var sl = GetSL(tradeType);

            if (SelectionRiskType == EnumSelectionRiskType.Fixed_Lot)
                tradeAmount = Symbol.QuantityToVolumeInUnits(RiskValue);
            else if (SelectionRiskType == EnumSelectionRiskType.Fixed_Volume)
                tradeAmount = Symbol.NormalizeVolumeInUnits(RiskValue);
            else if (SelectionRiskType == EnumSelectionRiskType.Perentage_Risk)
                tradeAmount = Symbol.VolumeForFixedRisk(Account.Balance * RiskValue / 100, sl, RoundingMode.Down);

            TradeResult result = ExecuteMarketOrder(tradeType, SymbolName, tradeAmount, label, sl, null, null, false /*Trailing stop */ );

            // Possible Pending Order for a waiting retest of signal
            //TradeResult resultPending = PlaceLimitOrder(tradeType, SymbolName, tradeAmount, Bars.ClosePrices.Last(0), label, sl, null);

        }
        //Function Closing trade on Signal SL
        private void Close(TradeType tradeType, string label)
        {
            var lastPosition = Positions.FindAll(label, SymbolName, tradeType).Last();
            foreach (var position in Positions.FindAll(label, SymbolName, tradeType))
                ClosePosition(position);

            AddInfosPositionList(lastPosition);
        }
        //Function Closing trade on Base SL
        private void OnPositionsClosed(PositionClosedEventArgs args)
        {
            AddInfosPositionList(args.Position);
        }
        //Statistics Functions
        private void AddInfosPositionList(Position position)
        {
            double accountBalance = 0.0;          //[positionStat.count-1][0]
            double netProfit = 0.0;               //[positionStat.count-1][1]
            double profitFactor = 0.0;            //[positionStat.count-1][2]
            double commission = 0.0;              //[positionStat.count-1][3]
            double swap = 0.0;                    //[positionStat.count-1][4]
            double drawdown = 0.0;                //[positionStat.count-1][5]
            double drawup = 0.0;                  //[positionStat.count-1][6]
            double consecutiveWin = 0.0;          //[positionStat.count-1][7]
            double consecutiveLoss = 0.0;

            if (positionStat.Count == 0)
            {
                accountBalance = initialBalance;
                netProfit = position.NetProfit;
                commission = position.Commissions;
                profitFactor = position.TradeType == TradeType.Buy ? ((position.CurrentPrice - position.EntryPrice) / (position.EntryPrice - position.StopLoss.Value)) : ((position.EntryPrice - position.CurrentPrice) / (position.StopLoss.Value - position.EntryPrice));
                swap = position.Swap;
                drawdown = initialBalance - netProfit;
                drawup = drawUpMax;
                consecutiveWin = 0;
                consecutiveLoss = 0;

                positionStat.Add(new List<double> { accountBalance, netProfit, profitFactor, commission, swap, drawdown, drawup, consecutiveWin, consecutiveLoss });

                // average of all stat at the end or on close position ?
            }

            else
            {
                var lastPosition = positionStat.Count - 1;

                accountBalance = initialBalance;
                netProfit = position.NetProfit;
                commission = position.Commissions;
                profitFactor = position.TradeType == TradeType.Buy ? ((position.CurrentPrice - position.EntryPrice) / (position.EntryPrice - position.StopLoss.Value)) : ((position.EntryPrice - position.CurrentPrice) / (position.StopLoss.Value - position.EntryPrice));
                swap = position.Swap;
                drawdown = initialBalance - netProfit;
                drawup = drawUpMax;
                consecutiveWin = netProfit > 0 ? positionStat[lastPosition][7] + 1 : 0;
                consecutiveLoss = netProfit < 0 ? positionStat[lastPosition][8] + 1 : 0;

                positionStat.Add(new List<double> { accountBalance, netProfit, profitFactor, commission, swap, drawdown, drawup, consecutiveWin, consecutiveLoss });
            }
            initialBalance = Account.Balance;
            drawUpMax = 0.0;
        }
        private void DrawUp()
        {
            var positionSymbol = Positions.FindAll(botNameLabelBuy).Length + Positions.FindAll(botNameLabelSell).Length;

            if (positionSymbol == 0)
            {
                drawUpMax = 0.0;
            }
            else if (positionSymbol != 0)
            {
                var drawUp = Positions.Where(x => x.TradeType == TradeType.Buy || x.TradeType == TradeType.Sell).Sum(p => p.NetProfit);
                while (drawUp > drawUpMax)
                    drawUpMax = drawUp;
            }
        }

        protected override void OnStop()
        {
            // Print on log
            var averageaccountBalance = 0.0;
            var averagenetProfit = 0.0;
            var averageprofitFactor = 0.0;
            var averagecommission = 0.0;
            var averageswap = 0.0;
            var averagedrawdown = 0.0;
            var averagedrawup = 0.0;
            var averageconsecutiveWin = 0.0;
            var averageconsecutiveLoss = 0.0;

            initialBalance = Account.Balance;

            for (int i = 0; i < positionStat.Count; i++)
            {
                averageaccountBalance += positionStat[i][0];
                averagenetProfit += positionStat[i][1];
                averageprofitFactor += positionStat[i][2];
                averagecommission += positionStat[i][3];
                averageswap += positionStat[i][4];
                averagedrawdown += positionStat[i][5];
                averagedrawup += positionStat[i][6];
                averageconsecutiveWin += positionStat[i][7];
                averageconsecutiveLoss += positionStat[i][8];
            }

            var res0 = averageaccountBalance / positionStat.Count;
            var res1 = averagenetProfit / positionStat.Count;
            var res2 = averageprofitFactor / positionStat.Count;
            var res3 = averagecommission / positionStat.Count;
            var res4 = averageswap / positionStat.Count;
            var res5 = averagedrawdown / positionStat.Count;
            var res6 = averagedrawup / positionStat.Count;
            var res7 = averageconsecutiveWin / positionStat.Count;
            var res8 = averageconsecutiveLoss / positionStat.Count;

            Print("average account Balance :   " + res0 + " || average net Profit :    " + res1 + " || average profit Factor  :  " + res2 + " || average commission :  " + res3 + " || average swap :  " + res4 + " || average drawdown :  " + res5 + " || average drawup :  " + res6 + " || average consecutive Win :  " + res7 + " || average consecutive Loss :  " + res8);

            // Export to csv need Function need idea for good using
        }
    }
}

YE
YesOrNot2

Joined on 17.05.2024

  • Distribution: Free
  • Language: C#
  • Trading platform: cTrader Automate
  • File name: NNFX Strategie Tester v1.02_withSourceCode.algo
  • Rating: 0
  • Installs: 346
  • Modified: 19/08/2024 07:32
Comments
Log in to add a comment.
No comments found.