Category Other  Published on 08/05/2023

PSAR Strategy for cTrader

Description

Hi, guys! 

We have just released an innovative Expert Advisor exclusively designed for the cTrader platform. It is a fully automated Forex trading robot with a built-in Parabolic SAR indicator.

PSAR Strategy in Action

PSAR Strategy in Action

This strategy is:

✅ User-friendly.

✅ Configurable.

✅ Equipped with customizable SL, TP, Break-Even, Trailing Stop, and money management features.

✅ Designed with all required functions to manage positions.


Features 

The PSAR strategy can:

  • Track trend pivot points.
  • Check formed bars on which the PSAR pivot point is occurring. The strategy instantly reacts to a pivot opening the order on the current bar. 
  • Show when it is safe to buy, safe to sell or stay out of the market.
  • Identify whether you should close a position by momentum or have several positions of the same type. 

You can: 

  • Swap opening conditions of Buy and Sell orders by applying input parameters.
  • Confirm the signal using an additional PSAR indicator on the higher timeframe. Consider it as an extra filter.

Note! Unformed bar signals do not guarantee the signal validity for bar closing.


Parameters

We have equipped our EA with more than 14 additional parameters. So, you can change them at your will.

 

If you want to familiarize yourself with all the parameters in detail, check this document: 

Note! Try this EA on your demo account first before going live.


Other Products

Ichimoku Cloud System: 

Daily H/L Custom Indicator: 

ADR Custom Indicator: 

 

Contact Info

Contact us via support@4xdev.com

Check out our cozy Telegram blog for traders: https://t.me/Forexdev 

Visit our website to find more tools and programming services: https://bit.ly/44BFRG3

Take a look at our YouTube channel: https://www.youtube.com/channel/UChsDb4Q8X2Vl5DJ7H8PzlHQ 


Feel the miracles of trading — with 4xDev.


using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class PSARStrategy : Robot
    {
        public enum ENUM_TP_TYPE
        {
            Fixed = 0,
            RiskRatio = 1
        }
        public enum ENUM_RISK_SOURCE
        {
            Equity = 0,
            Balance = 1
        }

        public enum ENUM_LOT_TYPE
        {
            Fixed_Lot = 0,
            Percent = 1
            // Fixed_Amount = 2
        }
        public enum ENUM_BAR_CHECK
        {
            Current_Bar = 0,
            Formed_Bar = 1
        }

        #region Input Trade Parameters
        [Parameter("Bar to Check", Group = "Trade Parameters", DefaultValue = ENUM_BAR_CHECK.Formed_Bar)]
        public ENUM_BAR_CHECK barCheck { get; set; }

        [Parameter("Label", Group = "Trade Parameters", DefaultValue = "PSAR Strategy")]
        public string Label { get; set; }

        [Parameter("Stop Loss in pips", Group = "Trade Parameters", DefaultValue = 0)]
        public double SL { get; set; }

        [Parameter("Take Profit type", Group = "Trade Parameters", DefaultValue = ENUM_TP_TYPE.Fixed)]
        public ENUM_TP_TYPE tpType { get; set; }

        [Parameter("Take Profit value", Group = "Trade Parameters", DefaultValue = 0)]
        public double TP { get; set; }

        [Parameter("Close on the opposite signal", Group = "Trade Parameters", DefaultValue = true)]
        public bool oppositeClose { get; set; }

        [Parameter("Only one trade in one direction", Group = "Trade Parameters", DefaultValue = true)]
        public bool onlyOne { get; set; }

        [Parameter("Use Reverse Trade", Group = "Trade Parameters", DefaultValue = true)]
        public bool reverseTrade { get; set; }
        #endregion

        #region Input Main Psar Parameters
        [Parameter("TimeFrame", Group = "Main Parabolic SAR", DefaultValue = 0.02, MinValue = 0)]
        public TimeFrame mainTF { get; set; }
        [Parameter("Min AF", Group = "Main Parabolic SAR", DefaultValue = 0.02, MinValue = 0)]
        public double mainMinAF { get; set; }

        [Parameter("Max AF", Group = "Main Parabolic SAR", DefaultValue = 0.2, MinValue = 0)]
        public double mainMaxAF { get; set; }
        #endregion

        #region Input Confirmation Psar Parameters
        [Parameter("Use Higher TF Confirmation", Group = "Confirmation Parabolic SAR", DefaultValue = 0.02, MinValue = 0)]
        public bool useHigherConfirm { get; set; }

        [Parameter("TimeFrame", Group = "Confirmation Parabolic SAR", DefaultValue = 0.02, MinValue = 0)]
        public TimeFrame confirmTF { get; set; }

        [Parameter("Min AF", Group = "Confirmation Parabolic SAR", DefaultValue = 0.02, MinValue = 0)]
        public double confirmMinAF { get; set; }

        [Parameter("Max AF", Group = "Confirmation Parabolic SAR", DefaultValue = 0.2, MinValue = 0)]
        public double confirmMaxAF { get; set; }
        #endregion


        #region Input Lot Size Parameters
        [Parameter("Lot Type", Group = "Lot Size", DefaultValue = ENUM_LOT_TYPE.Fixed_Lot)]
        public ENUM_LOT_TYPE lotType { get; set; }

        [Parameter("Risk Source", Group = "Lot Size", DefaultValue = ENUM_RISK_SOURCE.Balance)]
        public ENUM_RISK_SOURCE riskSource { get; set; }

        [Parameter("Risk/Lot Value", Group = "Lot Size", DefaultValue = 0.1)]
        public double risk { get; set; }
        #endregion

        #region Input Break Even Parameters
        [Parameter("Use BreakEven", Group = "BreakEven", DefaultValue = false)]
        public bool UseBE { get; set; }
        [Parameter("BreakEven Start(pips)", Group = "BreakEven", DefaultValue = 10)]
        public double BEStart { get; set; }

        [Parameter("BreakEven Profit(pips)", Group = "BreakEven", DefaultValue = 0)]
        public double BEProfit { get; set; }
        #endregion

        private ParabolicSAR parabolicSARMain;
        private ParabolicSAR parabolicSARConfirm;

        public MarketSeries mainSeries;
        public MarketSeries confirmSeries;

        protected override void OnStart()
        {
            mainSeries = MarketData.GetSeries(mainTF);
            confirmSeries = MarketData.GetSeries(confirmTF);
            parabolicSARMain = Indicators.ParabolicSAR(mainSeries, mainMinAF, mainMaxAF);
            parabolicSARConfirm = Indicators.ParabolicSAR(confirmSeries, confirmMinAF, confirmMaxAF);

            // Put your initialization logic here
        }
        DateTime lastTrade;
        double psar1;
        double psar2;
        double psar1Prev;
        // double psar2Prev;

        protected override void OnTick()
        {
            if (UseBE)
                BreakEven();

            if (lastTrade == mainSeries.OpenTime.Last())
            {
                return;
            }
            else
            {
                if (barCheck == ENUM_BAR_CHECK.Formed_Bar)
                    lastTrade = mainSeries.OpenTime.Last();
            }

            psar1 = parabolicSARMain.Result.Last((int)barCheck);
            psar1Prev = parabolicSARMain.Result.Last((int)barCheck + 1);

            if (useHigherConfirm)
            {
                psar2 = parabolicSARConfirm.Result.Last();
                // psar2Prev = parabolicSARConfirm.Result.Last();
            }
            if (CheckPSAR(psar1Prev, mainSeries.High.Last(), TradeType.Sell) && CheckPSAR(psar1, mainSeries.Low.Last(), TradeType.Buy))
            {
                if (oppositeClose)
                {
                    CloseOrders(TradeType.Sell);
                }

                if (!useHigherConfirm || CheckPSAR(psar2, confirmSeries.Low.Last(), TradeType.Buy))
                {
                    if (!onlyOne || CheckOrder(TradeType.Buy))
                    {
                        if (barCheck == 0)
                            lastTrade = mainSeries.OpenTime.Last();
                        OpenOrder(TradeType.Buy);
                    }
                }
            }

            //sell
            if (CheckPSAR(psar1, mainSeries.High.Last(), TradeType.Sell) && CheckPSAR(psar1Prev, mainSeries.Low.Last(), TradeType.Buy))
            {
                if (oppositeClose)
                {
                    CloseOrders(TradeType.Buy);
                }

                if (!useHigherConfirm || CheckPSAR(psar2, confirmSeries.High.Last(), TradeType.Sell))
                {
                    if (!onlyOne || CheckOrder(TradeType.Sell))
                    {
                        if (barCheck == 0)
                            lastTrade = mainSeries.OpenTime.Last();
                        OpenOrder(TradeType.Sell);
                    }
                }
            }

        }

        bool CheckPSAR(double value, double valuePrev, TradeType type)
        {
            if (type == TradeType.Buy)
            {
                if (value < valuePrev)
                    return true;
                return false;
            }
            else
            {
                if (value > valuePrev)
                    return true;
                return false;
            }

            return false;
        }

        void CloseOrders(TradeType type)
        {
            if (reverseTrade)
                type = type == TradeType.Buy ? TradeType.Sell : TradeType.Buy;

            foreach (var pos in Positions.FindAll(Label, Symbol, type))
            {
                ClosePosition(pos);
            }
        }
        bool CheckOrder(TradeType type)
        {
            if (reverseTrade)
                type = type == TradeType.Buy ? TradeType.Sell : TradeType.Buy;

            if (Positions.Find(Label, Symbol, type) != null)
                return false;
            return true;
        }

        void OpenOrder(TradeType type)
        {
            if (reverseTrade)
                type = type == TradeType.Buy ? TradeType.Sell : TradeType.Buy;

            double op;
            double tp = tpType == ENUM_TP_TYPE.Fixed ? TP : SL * TP;
            double sl;

            double source = riskSource == ENUM_RISK_SOURCE.Balance ? Account.Balance : Account.Equity;

            double volumeInUnits = 0;
            if (lotType == ENUM_LOT_TYPE.Fixed_Lot)
                volumeInUnits = Symbol.QuantityToVolumeInUnits(risk);
            else
                volumeInUnits = CalculateVolume(SL, risk, source);

            if (volumeInUnits == -1)
                return;
            ExecuteMarketOrder(type, SymbolName, volumeInUnits, Label, SL, TP);
        }
        private double CalculateVolume(double stopLossPips, double riskSize, double source)
        {
            // source = Account.Balance or Account.Equity
            double riskPerTrade = source * riskSize / 100;
            double totalPips = stopLossPips;

            double _volume;
            double exactVolume = riskPerTrade / (Symbol.PipValue * totalPips);
            if (exactVolume >= Symbol.VolumeInUnitsMin)
            {
                _volume = Symbol.NormalizeVolumeInUnits(exactVolume);
            }
            else
            {
                _volume = -1;
                Print("Not enough Equity to place minimum trade, exactVolume " + exactVolume + " is not >= Symbol.VolumeInUnitsMin " + Symbol.VolumeInUnitsMin);
            }
            return _volume;
        }
        private void BreakEven()
        {
            if (!UseBE)
                return;

            foreach (var pos in Positions.FindAll(Label, SymbolName))
            {
                if (pos.TradeType == TradeType.Buy)
                {
                    if (Symbol.Ask >= pos.EntryPrice + BEStart * Symbol.PipSize && (pos.StopLoss < pos.EntryPrice + BEProfit * Symbol.PipSize || pos.StopLoss == null))
                    {
                        ModifyPosition(pos, pos.EntryPrice + BEProfit * Symbol.PipSize, pos.TakeProfit);
                    }
                }
                if (pos.TradeType == TradeType.Sell)
                {
                    if (Symbol.Bid <= pos.EntryPrice - BEStart * Symbol.PipSize && (pos.StopLoss > pos.EntryPrice - BEProfit * Symbol.PipSize || pos.StopLoss == null))
                    {
                        ModifyPosition(pos, pos.EntryPrice + BEProfit * Symbol.PipSize, pos.TakeProfit);
                    }
                }
            }
        }

        protected override void OnStop()
        {
            // Put your deinitialization logic here
        }
    }
}


4xdev.team's avatar
4xdev.team

Joined on 11.12.2019

  • Distribution: Free
  • Language: C#
  • Trading platform: cTrader Automate
  • File name: PSAR_Strategy (2).algo
  • Rating: 3.89
  • Installs: 2546
  • Modified: 16/11/2021 12:27
Comments
Log in to add a comment.
CO
codey · 2 years ago

Its funny to see all previous comments were posted on the same date of their account creation date :)   and all previous commenters have not commented anything else on ctrader anywhere :)

SH
shamsulzoha5 · 2 years ago

The multiple parameters that can be adjusted in the settings are great. I am trading with a fixed stop loss and take profit and am getting a good win rate. Yeah, I would like to get a custom alert, too

AB
abdrzubair7949 · 2 years ago

What a nice cBot! I made a 15% profit by applying this strategy. if you want a successful trading business then i would recommend using PSAR Strategy and grow your business.

 

NI
nikky · 2 years ago

Awesome! Thanks!
Can you add a small modification to the strategy? I'd like to add a custom alert