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
}
}
}
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