using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
// Description:
// ===========
// Using cTrader and only trading using either a set time (GMT) or take the high and low of the previous configurable time-frame candle then calculate and enter a pending buy stop entry and a pending sell stop entry.
// Customer: Louis Fernandes
// LFStrategy B version 0.1 on 14.12.2016
//
// Additional Versions - bug fixes and enhancements
// ===================
namespace cAlgo
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class LFStrategyB : Robot
{
#region user defined parameters
[Parameter("Instance Name", DefaultValue = "001")]
public string InstanceName { get; set; }
[Parameter("Include Time Trading?", DefaultValue = true)]
public bool IncludeTradeStartTime { get; set; }
[Parameter("Trade Start Time (GMT)", DefaultValue = "05:45")]
public string TradeStartTime { get; set; }
[Parameter("Include Time-Frame Trading?", DefaultValue = true)]
public bool IncludeTradeOnTimeFrame { get; set; }
[Parameter("Risk %", DefaultValue = 5.0, MaxValue = 10.0)]
public double RiskPercent { get; set; }
[Parameter("Use Fixed Lot Size?", DefaultValue = true)]
public bool UseFixedLotSize { get; set; }
[Parameter("Lot Size", DefaultValue = 0.1)]
public double LotSize { get; set; }
[Parameter("Stop Loss (pips)", DefaultValue = 25)]
public double StopLoss { get; set; }
[Parameter("Take Profit (pips)", DefaultValue = 50)]
public double TakeProfit { get; set; }
[Parameter("Include Long Trades?", DefaultValue = true)]
public bool IncludeLong { get; set; }
[Parameter("Include Short Trades?", DefaultValue = true)]
public bool IncludeShort { get; set; }
[Parameter("Allow Multiple Orders", DefaultValue = false)]
public bool IncludeMultipleOrders { get; set; }
[Parameter("Pip Offset?", DefaultValue = 3)]
public int PipOffset { get; set; }
[Parameter("Include Trailing Stop", DefaultValue = false)]
public bool IncludeTrailingStop { get; set; }
[Parameter("Trailing Stop Trigger (pips)", DefaultValue = 40, MinValue = 1)]
public int TrailingStopTrigger { get; set; }
[Parameter("Trailing Stop Step (pips)", DefaultValue = 20, MinValue = 1)]
public int TrailingStopStep { get; set; }
#endregion
#region private fields
private string version = "v0.1";
private int tradeStartHour = 0;
private int tradeStartMin = 0;
private int tradeStopHour = 0;
private int tradeStopMin = 0;
#endregion
#region timezones
TimeZoneInfo timeZoneGMT = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
#endregion
#region cAlgo events
///
/// called once when robot starts
///
protected override void OnStart()
{
// print version to log file
Print(version);
DateTime dtGMT = ExchangeTime(timeZoneGMT);
// get start time
string[] startTime = TradeStartTime.Split(':');
tradeStartHour = Convert.ToInt16(startTime[0]);
tradeStartMin = Convert.ToInt16(startTime[1]);
Print("Current GMT time: " + dtGMT.ToShortTimeString());
// event handler delegate to capture opened positions and close other order
Positions.Opened += OnPositionOpened;
}
///
/// called every incoming tick of data
///
protected override void OnTick()
{
DateTime dtGMT = ExchangeTime(timeZoneGMT);
// adjust trailing stop
if (IncludeTrailingStop)
{
foreach (var p in this.Positions.Where(x => (x.Label == InstanceName)).Where(x => x.SymbolCode == Symbol.Code))
{
SetTrailingStopAdjustment(p, TrailingStopTrigger, TrailingStopStep);
}
}
// Open a buy and sell stop pending order daily at 05:45AM (GMT)
if (IncludeTradeStartTime)
{
if (dtGMT.Hour == tradeStartHour && dtGMT.Minute == tradeStartMin)
{
SubmitPendingOrders();
}
}
}
///
/// called when candle closes and new one opens
///
protected override void OnBar()
{
// if multiple orders are not allowed, do not cancel un-filled pending orders
if (!IncludeMultipleOrders)
{
// All non-triggered orders should be canceled at the start of the next candle
CancelPendingOrders();
}
// Open a buy and sell stop pending order at the start of a new candle
if (IncludeTradeOnTimeFrame)
{
SubmitPendingOrders();
}
}
private void OnPositionOpened(PositionOpenedEventArgs args)
{
var position = args.Position;
if (position.Label == InstanceName && position.SymbolCode == this.Symbol.Code)
{
Print("Pending order has triggered to open a " + args.Position.TradeType.ToString() + " position.");
}
}
#endregion
#region Trade logic
///
/// submit pending orders only is target price has been reached.
///
private void SubmitPendingOrders()
{
long volume = Symbol.QuantityToVolume(LotSize);
volume = Symbol.NormalizeVolume(volume);
// calculate long entry price last candle high
double targetPriceLong = GetLongEntryPrice();
double targetPriceShort = GetShortEntryPrice();
if (!UseFixedLotSize)
{
// get risk based volume
volume = GetVolumeFromPercent();
}
if (IncludeLong)
{
// Buy Pending order should only be placed if current price is (default) 5 pips below the calculated entry price
if (Symbol.Bid <= targetPriceLong)
{
PlaceStopOrder(TradeType.Buy, Symbol, volume, targetPriceLong, InstanceName, StopLoss, TakeProfit);
Print("Buy Stop Order placed with entry price at " + targetPriceLong.ToString() + " with a volume of " + volume.ToString());
}
}
if (IncludeShort)
{
if (Symbol.Ask >= targetPriceShort)
{
// Sell Pending order should only be placed if current price is (default) 5 pips above the calculated entry price
PlaceStopOrder(TradeType.Sell, Symbol, volume, targetPriceShort, InstanceName, StopLoss, TakeProfit);
Print("Sell Stop Order placed with entry price at " + targetPriceShort.ToString() + " with a volume of " + volume.ToString());
}
}
}
///
/// cancels all open pending orders
///
private void CancelPendingOrders()
{
foreach (var po in this.PendingOrders)
{
if (po != null)
{
TradeResult result = CancelPendingOrder(po);
if (result.IsSuccessful)
{
Print(result.PendingOrder.TradeType.ToString() + " Pending order canceled.");
}
else
{
Print("Error occurred canceling a pending order");
}
}
}
}
///
/// adjusts all open positions with a trailing stop
///
///
///
///
///
private bool SetTrailingStopAdjustment(Position p, double trigger, double trailing)
{
try
{
bool TradeTypeBuy = true;
if (p.TradeType == TradeType.Sell)
{
TradeTypeBuy = false;
}
double profit = TradeTypeBuy ? Symbol.Bid - p.EntryPrice : p.EntryPrice - Symbol.Ask;
if (profit >= trigger * Symbol.PipSize)
{
double newStopLossPrice = TradeTypeBuy ? Math.Round(Symbol.Bid - trailing * Symbol.PipSize, Symbol.Digits) : Math.Round(Symbol.Ask + trailing * Symbol.PipSize, Symbol.Digits);
bool modify = TradeTypeBuy ? newStopLossPrice > p.StopLoss : newStopLossPrice < p.StopLoss;
if (modify)
{
ModifyPosition(p, newStopLossPrice, p.TakeProfit);
Print("Trailing stop was modified.");
return true;
}
}
} catch
{
}
return false;
}
#endregion
#region Risk percent per trade
///
///
///
///
///
private int GetVolumeFromPercent()
{
double risk = RiskPercent / 100.0;
double volume;
volume = Account.Equity * risk / (StopLoss * Symbol.PipValue);
return (int)Symbol.NormalizeVolume(volume);
}
#endregion
#region calculations
// return the exact date and time for the exchange locality.
public DateTime ExchangeTime(TimeZoneInfo timeZone)
{
return TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.Local, timeZone);
}
///
/// calculates the entry price for a long position
///
///
private double GetLongEntryPrice()
{
double entryPrice = 0;
// get previous candle high
double lastHigh = this.MarketSeries.High.Last(1);
// get pip distance from offset
double pipDistance = PipOffset * this.Symbol.PipSize;
// High of previous candle + 3 pips
entryPrice = lastHigh + pipDistance;
// round decimal places
entryPrice = Math.Round(entryPrice, Symbol.Digits);
return entryPrice;
}
///
/// calculates the entry price for a short position
///
///
private double GetShortEntryPrice()
{
double entryPrice = 0;
// get previous candle high
double lastLow = this.MarketSeries.Low.Last(1);
// get pip distance from offset
double pipDistance = PipOffset * this.Symbol.PipSize;
// High of previous candle - 3 pips
entryPrice = lastLow - pipDistance;
// round decimal places
entryPrice = Math.Round(entryPrice, Symbol.Digits);
return entryPrice;
}
///
///
///
///
///
private bool IsPositionOpenByType(TradeType type)
{
var p = Positions.FindAll(InstanceName, Symbol, type);
if (p.Count() >= 1)
{
return true;
}
return false;
}
#endregion
}
}