Spread Filters for Scalping: When Not to Enter
Re: Spread Filters for Scalping: When Not to Enter
To integrate an external REST API like Telegram, you must break the cBot out of its native security sandbox. By default, cTrader prevents network calls to protect users from malicious code.
As a .NET environment, cTrader fully supports System.Net.Http.HttpClient, provided you grant the bot the necessary permissions. The most critical architectural requirement here is thread offloading: the HTTP request must be completely decoupled from the main execution thread so network latency to Telegram's servers does not delay your market liquidation orders.
As a .NET environment, cTrader fully supports System.Net.Http.HttpClient, provided you grant the bot the necessary permissions. The most critical architectural requirement here is thread offloading: the HTTP request must be completely decoupled from the main execution thread so network latency to Telegram's servers does not delay your market liquidation orders.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Spread Filters for Scalping: When Not to Enter
Here is the isolated architecture to inject the Telegram API into your panic sequence:
Code: Select all
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using cAlgo.API;
namespace cAlgo.Robots
{
// CRITICAL: You must change AccessRights from None to FullAccess to allow outbound HTTP requests
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
public partial class InteractiveFiatHUD : Robot
{
[Parameter("Telegram Bot Token", Group = "Telegram API", DefaultValue = "YOUR_BOT_TOKEN_HERE")]
public string TelegramToken { get; set; }
[Parameter("Telegram Chat ID", Group = "Telegram API", DefaultValue = "YOUR_CHAT_ID_HERE")]
public string TelegramChatId { get; set; }
[Parameter("Send Telegram on Panic", Group = "Telegram API", DefaultValue = true)]
public bool EnableTelegramAlerts { get; set; }
// Reuse a single static HttpClient instance across the lifetime of the bot to prevent socket exhaustion
private static readonly HttpClient _httpClient = new HttpClient();
private bool _isPanicProcessing = false;
private void ExecuteGlobalPanicLiquidation()
{
if (_isPanicProcessing) return;
_isPanicProcessing = true;
Print("[PANIC] Global Account Liquidation Initiated.");
// 1. Fire-and-Forget the Telegram Alert
// We push this to the thread pool so the main execution thread instantly proceeds to closing trades
if (EnableTelegramAlerts && !string.IsNullOrWhiteSpace(TelegramToken) && !string.IsNullOrWhiteSpace(TelegramChatId))
{
Task.Run(() => SendTelegramAlertAsync("⚠️ <b>PANIC BUTTON PRESSED</b>\nLiquidating all active positions on cTrader."));
}
// 2. Execute Market Closures (Your existing logic)
foreach (var pendingOrder in PendingOrders)
{
CancelPendingOrderAsync(pendingOrder, OnPanicOrderCancelled);
}
foreach (var position in Positions)
{
ClosePositionAsync(position, OnPanicPositionClosed);
}
}
/// <summary>
/// Asynchronously formats and transmits the payload to the Telegram Bot API.
/// </summary>
private async Task SendTelegramAlertAsync(string messageText)
{
try
{
string url = $"https://api.telegram.org/bot{TelegramToken}/sendMessage";
// Construct the JSON payload with HTML parse mode enabled for bolding/formatting
string jsonPayload = $@"{{
""chat_id"": ""{TelegramChatId}"",
""text"": ""{messageText}"",
""parse_mode"": ""HTML""
}}";
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
// Execute the POST request
HttpResponseMessage response = await _httpClient.PostAsync(url, content);
if (!response.IsSuccessStatusCode)
{
string errorResponse = await response.Content.ReadAsStringAsync();
Print($"[Telegram Error] HTTP {response.StatusCode}: {errorResponse}");
}
}
catch (Exception ex)
{
// Isolate exceptions so DNS failures or timeout errors do not crash the cBot
Print($"[Telegram Exception] {ex.Message}");
}
}
// ... (Remaining methods stay the same)
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Spread Filters for Scalping: When Not to Enter
Architectural Mechanics
AccessRights.FullAccess: If you leave the attribute as AccessRights.None, cTrader will instantly throw a fatal System.Security.SecurityException the moment the bot attempts to instantiate the HttpClient, terminating the cBot entirely. When you attach a FullAccess bot to a chart, cTrader will prompt you with a UAC-style confirmation popup verifying that you trust the algorithm.
Thread Decoupling (Task.Run): While SendTelegramAlertAsync uses async/await, calling it directly from the main thread could still introduce a few milliseconds of synchronous overhead (DNS resolution, socket initialization). Wrapping it in Task.Run() pushes the entire operation onto a background worker thread. The main loop completely abandons it and instantly moves to the CancelPendingOrderAsync loops.
Static HttpClient: C# developers frequently wrap HttpClient in a using block, but doing so under heavy load leads to socket exhaustion (TIME_WAIT states). Declaring it as private static readonly HttpClient ensures the socket pool is efficiently reused for the lifetime of the cTrader terminal session.
JSON String Interpolation ($@"{...}"): Because the Telegram payload is lightweight, raw string interpolation is used instead of importing Newtonsoft.Json. The @ symbol allows for multi-line formatting, while double braces {{ and }} escape the brackets so they render correctly in the JSON body.
AccessRights.FullAccess: If you leave the attribute as AccessRights.None, cTrader will instantly throw a fatal System.Security.SecurityException the moment the bot attempts to instantiate the HttpClient, terminating the cBot entirely. When you attach a FullAccess bot to a chart, cTrader will prompt you with a UAC-style confirmation popup verifying that you trust the algorithm.
Thread Decoupling (Task.Run): While SendTelegramAlertAsync uses async/await, calling it directly from the main thread could still introduce a few milliseconds of synchronous overhead (DNS resolution, socket initialization). Wrapping it in Task.Run() pushes the entire operation onto a background worker thread. The main loop completely abandons it and instantly moves to the CancelPendingOrderAsync loops.
Static HttpClient: C# developers frequently wrap HttpClient in a using block, but doing so under heavy load leads to socket exhaustion (TIME_WAIT states). Declaring it as private static readonly HttpClient ensures the socket pool is efficiently reused for the lifetime of the cTrader terminal session.
JSON String Interpolation ($@"{...}"): Because the Telegram payload is lightweight, raw string interpolation is used instead of importing Newtonsoft.Json. The @ symbol allows for multi-line formatting, while double braces {{ and }} escape the brackets so they render correctly in the JSON body.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Spread Filters for Scalping: When Not to Enter
Unlike MetaTrader 5, the cTrader API does not feature a native Chart.TakeScreenshot() method for cBots. However, because cBots are fully-fledged C# applications, we can bypass the sandbox and tap directly into native Windows libraries (System.Drawing and System.Windows.Forms) to capture the screen monitor.
To do this, we must change the Telegram request from a lightweight JSON payload (used for text) to a Multipart Form Data payload (used for file uploads) targeting Telegram's sendPhoto endpoint.
To do this, we must change the Telegram request from a lightweight JSON payload (used for text) to a Multipart Form Data payload (used for file uploads) targeting Telegram's sendPhoto endpoint.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Spread Filters for Scalping: When Not to Enter
Here is the complete architecture to capture the screen and transmit it to Telegram without delaying your panic liquidations:
Code: Select all
using System;
using System.IO;
using System.Net.Http;
using System.Drawing; // Requires Assembly Reference in cTrader
using System.Drawing.Imaging; // Requires Assembly Reference in cTrader
using System.Windows.Forms; // Requires Assembly Reference in cTrader
using System.Threading.Tasks;
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
public partial class InteractiveFiatHUD : Robot
{
[Parameter("Telegram Bot Token", Group = "Telegram API", DefaultValue = "YOUR_TOKEN")]
public string TelegramToken { get; set; }
[Parameter("Telegram Chat ID", Group = "Telegram API", DefaultValue = "YOUR_CHAT_ID")]
public string TelegramChatId { get; set; }
private static readonly HttpClient _httpClient = new HttpClient();
private bool _isPanicProcessing = false;
private void ExecuteGlobalPanicLiquidation()
{
if (_isPanicProcessing) return;
_isPanicProcessing = true;
Print("[PANIC] Global Account Liquidation Initiated.");
// 1. SYNCHRONOUS CAPTURE: Take the screenshot immediately before closing trades
// so the image actually shows your open positions on the chart.
byte[] screenshotBytes = CaptureScreenToMemory();
// 2. ASYNCHRONOUS UPLOAD: Push the heavy upload process to a background thread
// so the bot can instantly proceed to liquidating the account.
if (screenshotBytes != null && !string.IsNullOrWhiteSpace(TelegramToken))
{
Task.Run(() => SendTelegramScreenshotAsync(screenshotBytes, "⚠️ <b>PANIC TRIGGERED</b>\nLiquidating all positions."));
}
// 3. Execute Market Closures (Your existing logic)
foreach (var pendingOrder in PendingOrders)
{
CancelPendingOrderAsync(pendingOrder, OnPanicOrderCancelled);
}
foreach (var position in Positions)
{
ClosePositionAsync(position, OnPanicPositionClosed);
}
}
/// <summary>
/// Captures all active Windows monitors and compresses them into a JPEG byte array.
/// </summary>
private byte[] CaptureScreenToMemory()
{
try
{
// SystemInformation.VirtualScreen captures all monitors, ensuring the chart is
// photographed even if cTrader is running on a secondary display.
Rectangle bounds = SystemInformation.VirtualScreen;
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
}
using (MemoryStream ms = new MemoryStream())
{
// JPEG compression is critical. A raw BMP of a 4K monitor is too large
// and will cause the Telegram API to time out or reject the payload.
bitmap.Save(ms, ImageFormat.Jpeg);
return ms.ToArray();
}
}
}
catch (Exception ex)
{
Print($"[Screenshot Error] Failed to capture screen: {ex.Message}");
return null;
}
}
/// <summary>
/// Packages the JPEG bytes into a Multipart Form Data payload and transmits to Telegram.
/// </summary>
private async Task SendTelegramScreenshotAsync(byte[] imageBytes, string captionText)
{
try
{
string url = $"https://api.telegram.org/bot{TelegramToken}/sendPhoto";
// Construct a multipart form payload (required for binary file uploads)
using (var form = new MultipartFormDataContent())
{
form.Add(new StringContent(TelegramChatId), "chat_id");
form.Add(new StringContent(captionText), "caption");
form.Add(new StringContent("HTML"), "parse_mode");
// Attach the image bytes
var imageContent = new ByteArrayContent(imageBytes);
imageContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
// The filename parameter ("panic_chart.jpg") is required by Telegram
form.Add(imageContent, "photo", "panic_chart.jpg");
HttpResponseMessage response = await _httpClient.PostAsync(url, form);
if (!response.IsSuccessStatusCode)
{
string errorResponse = await response.Content.ReadAsStringAsync();
Print($"[Telegram Error] HTTP {response.StatusCode}: {errorResponse}");
}
else
{
Print("[Telegram] Screenshot successfully delivered.");
}
}
}
catch (Exception ex)
{
Print($"[Telegram Exception] {ex.Message}");
}
}
// ... (Remaining methods stay the same)
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Spread Filters for Scalping: When Not to Enter
Critical Setup: Adding Assembly References
1.) Because System.Drawing and System.Windows.Forms are not default algorithmic trading libraries, cTrader's compiler will throw an error until you manually link them.
2.) Open the Automate tab in cTrader.
3.) Look at the left-hand panel where your cBot files are listed. Right-click your cBot and select Manage References.
4.) Search for and check the boxes for System.Drawing (or System.Drawing.Common in newer .NET 6+ versions of cTrader) and System.Windows.Forms.
5.) Click Apply and Build.
1.) Because System.Drawing and System.Windows.Forms are not default algorithmic trading libraries, cTrader's compiler will throw an error until you manually link them.
2.) Open the Automate tab in cTrader.
3.) Look at the left-hand panel where your cBot files are listed. Right-click your cBot and select Manage References.
4.) Search for and check the boxes for System.Drawing (or System.Drawing.Common in newer .NET 6+ versions of cTrader) and System.Windows.Forms.
5.) Click Apply and Build.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Spread Filters for Scalping: When Not to Enter
Architectural Mechanics
Synchronous Capture / Asynchronous Upload: Notice that CaptureScreenToMemory() is called directly on the main thread before Task.Run(). If you captured the screen inside the background thread, the broker's API might close your positions before the Windows graphics engine snaps the picture, resulting in an empty chart. The upload, which takes roughly 500ms to 2 seconds, is offloaded so your closure requests execute instantly.
Virtual Screen Dimensions: SystemInformation.VirtualScreen creates a bounding box around every monitor plugged into your machine. If you have two 1080p monitors side-by-side, it will capture a 3840x1080 image. This guarantees the cTrader window is captured regardless of which monitor it is currently dragged to.
JPEG Memory Stream: The Windows screen is captured as a raw, uncompressed Bitmap. If you send an uncompressed 4K Bitmap to a REST API, the payload could easily exceed 20MB, causing severe latency or a 413 Payload Too Large rejection from Telegram. Streaming the Bitmap through an ImageFormat.Jpeg encoder drastically shrinks the file size down to a few hundred kilobytes for rapid transmission.
Synchronous Capture / Asynchronous Upload: Notice that CaptureScreenToMemory() is called directly on the main thread before Task.Run(). If you captured the screen inside the background thread, the broker's API might close your positions before the Windows graphics engine snaps the picture, resulting in an empty chart. The upload, which takes roughly 500ms to 2 seconds, is offloaded so your closure requests execute instantly.
Virtual Screen Dimensions: SystemInformation.VirtualScreen creates a bounding box around every monitor plugged into your machine. If you have two 1080p monitors side-by-side, it will capture a 3840x1080 image. This guarantees the cTrader window is captured regardless of which monitor it is currently dragged to.
JPEG Memory Stream: The Windows screen is captured as a raw, uncompressed Bitmap. If you send an uncompressed 4K Bitmap to a REST API, the payload could easily exceed 20MB, causing severe latency or a 413 Payload Too Large rejection from Telegram. Streaming the Bitmap through an ImageFormat.Jpeg encoder drastically shrinks the file size down to a few hundred kilobytes for rapid transmission.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Spread Filters for Scalping: When Not to Enter
To automate this, you must intercept the Positions.Closed event and filter the closure reason. cTrader's API provides the PositionCloseReason enumeration, which explicitly tells you why the trade was liquidated (e.g., Stop Loss, Take Profit, Stop Out, or manually closed).
Because you already built the CaptureScreenToMemory and SendTelegramScreenshotAsync methods, you only need to wire them into the event handler and construct a dynamic caption that details the loss.
Because you already built the CaptureScreenToMemory and SendTelegramScreenshotAsync methods, you only need to wire them into the event handler and construct a dynamic caption that details the loss.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Spread Filters for Scalping: When Not to Enter
Here is the isolated code to inject into your existing class:
Code: Select all
using System;
using System.Threading.Tasks;
using cAlgo.API;
namespace cAlgo.Robots
{
public partial class InteractiveFiatHUD : Robot
{
[Parameter("Send SL Screenshots", Group = "Telegram API", DefaultValue = true)]
public bool EnableSlScreenshots { get; set; }
protected override void OnStart()
{
// Subscribe to the global position closed event
Positions.Closed += OnPositionClosed;
}
private void OnPositionClosed(PositionClosedEventArgs args)
{
// 1. Check if the trade was closed specifically by hitting the Stop Loss
if (EnableSlScreenshots && args.Reason == PositionCloseReason.StopLoss)
{
TriggerStopLossScreenshot(args.Position);
}
// ... (Your existing HUD cleanup logic for closed positions goes here)
}
/// <summary>
/// Captures the chart and dispatches the Telegram payload with trade details.
/// </summary>
private void TriggerStopLossScreenshot(Position closedPosition)
{
Print($"[SL Hit] Position {closedPosition.Id} stopped out. Capturing chart...");
// 1. SYNCHRONOUS CAPTURE: Take the picture immediately while the chart
// still reflects the exact price action that caused the stop-out.
byte[] screenshotBytes = CaptureScreenToMemory();
if (screenshotBytes != null && !string.IsNullOrWhiteSpace(TelegramToken))
{
// 2. Format a detailed caption using HTML parsing
string caption = $"🛑 <b>STOP LOSS HIT</b>\n" +
$"<b>Pair:</b> {closedPosition.SymbolName}\n" +
$"<b>Type:</b> {closedPosition.TradeType}\n" +
$"<b>Loss:</b> {closedPosition.NetProfit:C2}\n" +
$"<b>Duration:</b> {DateTime.UtcNow - closedPosition.EntryTime:hh\\:mm\\:ss}";
// 3. ASYNCHRONOUS UPLOAD: Push the upload to a background thread
// so the OnPositionClosed event finishes instantly and doesn't freeze the bot.
Task.Run(() => SendTelegramScreenshotAsync(screenshotBytes, caption));
}
}
// ... (CaptureScreenToMemory and SendTelegramScreenshotAsync remain exactly the same)
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Spread Filters for Scalping: When Not to Enter
Architectural Mechanics
PositionCloseReason Filtering: Checking args.Reason == PositionCloseReason.StopLoss guarantees the bot ignores Take Profits (TakeProfit), manual closures via your HUD (Closed), or margin calls (StopOut). It only fires when the hard server-side stop is triggered.
NetProfit Formatting (:C2): Because closedPosition.NetProfit automatically factors in commissions and swap fees, the string format {closedPosition.NetProfit:C2} will render the exact, true fiat loss (e.g., -$104.50), removing the need for you to calculate gross vs. net manually.
Time Delta Calculation: The subtraction DateTime.UtcNow - closedPosition.EntryTime generates a TimeSpan object. Formatting it with :hh\\:mm\\:ss tells you exactly how long you were exposed to the market before the trade failed (e.g., 01:45:12), which is highly useful for post-trade journaling from your phone.
Telegram Rate Limiting Danger: If you are running a grid or basket strategy where 10 trades hit their Stop Loss in the exact same millisecond, this code will attempt to upload 10 separate images to Telegram instantly. Telegram strictly enforces a limit of roughly 30 messages per second, but for heavy file uploads like images, spamming concurrent POST requests will result in an HTTP 429 Too Many Requests error. If you trade large baskets, you would need to implement an upload queue to stagger the screenshots by a few seconds.
PositionCloseReason Filtering: Checking args.Reason == PositionCloseReason.StopLoss guarantees the bot ignores Take Profits (TakeProfit), manual closures via your HUD (Closed), or margin calls (StopOut). It only fires when the hard server-side stop is triggered.
NetProfit Formatting (:C2): Because closedPosition.NetProfit automatically factors in commissions and swap fees, the string format {closedPosition.NetProfit:C2} will render the exact, true fiat loss (e.g., -$104.50), removing the need for you to calculate gross vs. net manually.
Time Delta Calculation: The subtraction DateTime.UtcNow - closedPosition.EntryTime generates a TimeSpan object. Formatting it with :hh\\:mm\\:ss tells you exactly how long you were exposed to the market before the trade failed (e.g., 01:45:12), which is highly useful for post-trade journaling from your phone.
Telegram Rate Limiting Danger: If you are running a grid or basket strategy where 10 trades hit their Stop Loss in the exact same millisecond, this code will attempt to upload 10 separate images to Telegram instantly. Telegram strictly enforces a limit of roughly 30 messages per second, but for heavy file uploads like images, spamming concurrent POST requests will result in an HTTP 429 Too Many Requests error. If you trade large baskets, you would need to implement an upload queue to stagger the screenshots by a few seconds.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.