Price Action Trading Bot

Created at 21 Sep 2022
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 · 1 year 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