Start trading

Price Action Trading Bot

Created at 21 Sep 2022
cTrader Discord
SA

samachua2011

Joined 18.04.2022

Status

Open


Budget

50.00 USD


Payment Method

via  Freelancer

Job Description

Greetings,

We want a custom bot that utilizes only one indicator (the Smoothed Heikenashi Indicator). However, while using this indicator, we need to have the bot structured on a known an readily available bot (the PSAR bot). 

We want to retain all of the PSAR bots' functionalities. However, instead of using PSAR indicator to trigger the Buy/Sell signals, we want to have the Smoothed Heikenashi Indicator trigger the trading positions as explained below:

 

  • Short Position - When the Smoothed Heiken-Ashi indicator's bars turn RED on the chart for a symbol e.g. EURJPY, the bot should automatically open a SHORT position not more than 5 pips from the position of the color change.

 

  • Long Position - When the Smoothed Heiken-Ashi indicator's bars turn GREEN on the chart for a symbol e.g. EURJPY, the bot should automatically open a LONG position not more than 5 pips from the position of the color change.

           

I have outlined the Source Code for the Indicator and the Bot that will serve as a base/ guide to developing this bot below.

You can find the link to the bots on the Freelance website:

 

____________________________________________________________________________________________________________

Heiken-Ashi Smoothed Indicator Source Code

using System;
using cAlgo.API;
using cAlgo.API.Indicators;

namespace cAlgo.Indicators
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.FullAccess)]
    public class HeikenAshiSmoothed : Indicator
    {
        [Parameter(DefaultValue = 5, MinValue = 1)]
        public int Periods { get; set; }

        [Parameter("MA Type", DefaultValue = MovingAverageType.Exponential)]
        public MovingAverageType MAType { get; set; }

        private MovingAverage maOpen;
        private MovingAverage maClose;
        private MovingAverage maHigh;
        private MovingAverage maLow;
        private IndicatorDataSeries haClose;
        private IndicatorDataSeries haOpen;

        protected override void Initialize()
        {
            maOpen = Indicators.MovingAverage(MarketSeries.Open, Periods, MAType);
            maClose = Indicators.MovingAverage(MarketSeries.Close, Periods, MAType);
            maHigh = Indicators.MovingAverage(MarketSeries.High, Periods, MAType);
            maLow = Indicators.MovingAverage(MarketSeries.Low, Periods, MAType);
            haOpen = CreateDataSeries();
            haClose = CreateDataSeries();
        }

        public override void Calculate(int index)
        {
            double haHigh;
            double haLow;
            Colors Color;

            if (index > 0 && !double.IsNaN(maOpen.Result[index - 1]))
            {
                haOpen[index] = (haOpen[index - 1] + haClose[index - 1]) / 2;
                haClose[index] = (maOpen.Result[index] + maClose.Result[index] + maHigh.Result[index] + maLow.Result[index]) / 4;
                haHigh = Math.Max(maHigh.Result[index], Math.Max(haOpen[index], haClose[index]));
                haLow = Math.Min(maLow.Result[index], Math.Min(haOpen[index], haClose[index]));
                Color = (haOpen[index] > haClose[index]) ? Colors.Red : Colors.LimeGreen;
                ChartObjects.DrawLine("BarHA" + index, index, haOpen[index], index, haClose[index], Color, 5, LineStyle.Solid);
                ChartObjects.DrawLine("LineHA" + index, index, haHigh, index, haLow, Color, 1, LineStyle.Solid);
            }
            else if (!double.IsNaN(maOpen.Result[index]))
            {
                haOpen[index] = (maOpen.Result[index] + maClose.Result[index]) / 2;
                haClose[index] = (maOpen.Result[index] + maClose.Result[index] + maHigh.Result[index] + maLow.Result[index]) / 4;
                haHigh = maHigh.Result[index];
                haLow = maLow.Result[index];
            }
        }
    }
}

____________________________________________________________________________________________________________

PSAR Bot Code

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    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
        }
    }
}

 

Comments
Log in to add a comment.
PanagiotisChar's avatar
PanagiotisChar · 3 years ago

Hi Sam,

We can help you with your project. Feel free to reach out to me at development@clickalgo.com

Aieden Technologies

Need Help? Join us on Telegram