Because cTrader’s native language (cAlgo) is pure C#, you can bypass proprietary workarounds and leverage the standard System.IO namespace for file handling.
The biggest gotcha when porting to cTrader is permissions. You must explicitly declare AccessRights = AccessRights.FileSystem in the [Robot] attribute; otherwise, cTrader’s sandbox will throw a security exception the moment you try to create the CSV.
Here is the complete cBot logic, utilizing standard C# string interpolation and stream writers. It uses the Bars.OpenTimes.LastValue property to ensure it only evaluates the spread and writes to the log once per bar, preventing redundant tick-by-tick spam.
Avoiding EURJPY during Friday afternoon when spreads exceed my filter
Re: Avoiding EURJPY during Friday afternoon when spreads exceed my filter
Ctrader code:
Code: Select all
using System;
using System.IO;
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FileSystem)]
public class FridaySpreadFilterLog : Robot
{
[Parameter("Max Spread Cap (Pips)", DefaultValue = 1.8, MinValue = 0.1, Step = 0.1)]
public double MaxSpreadPips { get; set; }
[Parameter("Friday Cutoff Hour", DefaultValue = 14, MinValue = 0, MaxValue = 23)]
public int FridayCutoffHour { get; set; }
private DateTime _lastBarTime;
private string _filePath;
protected override void OnStart()
{
// Safely route the CSV to the user's Documents/cAlgo folder
string docsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
_filePath = Path.Combine(docsPath, "cAlgo", "SkippedTradesLog.csv");
}
protected override void OnTick()
{
// 1. Evaluate once per bar to prevent CSV log spam
var currentBarTime = Bars.OpenTimes.LastValue;
if (_lastBarTime == currentBarTime)
return;
// 2. Time Logic: Broker Time
bool isFriday = Server.Time.DayOfWeek == DayOfWeek.Friday;
bool isAfterCutoff = Server.Time.Hour >= FridayCutoffHour;
bool killzoneActive = isFriday && isAfterCutoff;
// 3. Spread Logic
// cTrader natively handles pricing digits; Symbol.PipSize safely calculates true pips
double currentSpreadPips = Symbol.Spread / Symbol.PipSize;
bool spreadExceeded = currentSpreadPips > MaxSpreadPips;
// 4. Execution Logging
if (spreadExceeded && !killzoneActive)
{
LogSkippedTrade("Spread Cap Exceeded", currentSpreadPips);
_lastBarTime = currentBarTime;
}
else if (killzoneActive)
{
LogSkippedTrade("Friday Cutoff Time Reached", currentSpreadPips);
_lastBarTime = currentBarTime;
}
}
private void LogSkippedTrade(string reason, double spreadPips)
{
try
{
// Check if file is new to write the header row
bool writeHeader = !File.Exists(_filePath) || new FileInfo(_filePath).Length == 0;
using (StreamWriter sw = new StreamWriter(_filePath, append: true))
{
if (writeHeader)
{
sw.WriteLine("Time,Symbol,Action,Reason,SpreadPips,Price");
}
sw.WriteLine($"{Server.Time:yyyy-MM-dd HH:mm:ss},{SymbolName},SKIP,{reason},{Math.Round(spreadPips, 1)},{Symbol.Ask}");
}
Print($"Logged Skip: {reason} | Spread: {Math.Round(spreadPips, 1)}");
}
catch (Exception ex)
{
Print($"Failed to write to CSV. Error: {ex.Message}");
}
}
}
}Re: Avoiding EURJPY during Friday afternoon when spreads exceed my filter
Accessing the Log Data
When you build and attach this cBot to your chart, it will automatically route the SkippedTradesLog.csv to your Documents\cAlgo folder. Since it leaves the file handle open only during the split-second write operation inside the using block, you can safely open the CSV in Excel or a custom data pipeline in real-time while the cBot continues running.
When you build and attach this cBot to your chart, it will automatically route the SkippedTradesLog.csv to your Documents\cAlgo folder. Since it leaves the file handle open only during the split-second write operation inside the using block, you can safely open the CSV in Excel or a custom data pipeline in real-time while the cBot continues running.