Page 13 of 13
Re: Spread Filters for Scalping: When Not to Enter
Posted: Thu Oct 08, 2026 2:20 pm
by PTScalper
1. The Backend Router (C#)
Inject this second endpoint into your ProcessHttpRequestAsync method, directly below your /panic endpoint block:
Code: Select all
// 2. API ROUTER: Intercept the Parameter Update POST request
if (context.Request.Url.AbsolutePath.Equals("/update-params", StringComparison.OrdinalIgnoreCase))
{
if (context.Request.HttpMethod == "POST")
{
// HttpListener automatically parses URL parameters into a NameValueCollection
string actStr = context.Request.QueryString["act"];
string distStr = context.Request.QueryString["dist"];
// Validate that the incoming data can be parsed into numerical doubles
if (double.TryParse(actStr, out double newAct) && double.TryParse(distStr, out double newDist))
{
// THREAD MARSHALING: Safely assign the new values on the main execution thread
BeginInvokeOnMainThread(() =>
{
ActivationProfit = newAct;
TrailingDistance = newDist;
Print($"[Web Server] Remote Update -> Activation: {newAct:C2} | Trail: {newDist:C2}");
});
response.StatusCode = 200;
byte[] okBytes = Encoding.UTF8.GetBytes("Parameters Updated");
await response.OutputStream.WriteAsync(okBytes, 0, okBytes.Length);
}
else
{
response.StatusCode = 400; // Bad Request (Invalid numbers)
}
}
else
{
response.StatusCode = 405; // Method Not Allowed
}
return; // Exit here so it doesn't try to serve the HTML file
}
Re: Spread Filters for Scalping: When Not to Enter
Posted: Thu Oct 08, 2026 2:21 pm
by PTScalper
2. The Frontend UI (HTML/CSS)
Inside your AppendBatchedTradesToHtmlAsync method, inject the CSS styling and the HTML control panel. Place the HTML just below the header table, but above the chart container.
Code: Select all
// 1. Add this CSS to your existing <style> block
htmlDoc.AppendLine(".control-panel { background-color: #f1f1f1; padding: 15px; border: 1px solid #c0c0c0; margin-bottom: 20px; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; font-weight: bold; }");
htmlDoc.AppendLine(".control-group { display: flex; flex-direction: column; }");
htmlDoc.AppendLine(".control-input { padding: 6px; margin-top: 5px; width: 80px; text-align: right; border: 1px solid #ccc; border-radius: 3px; font-family: Tahoma; }");
htmlDoc.AppendLine(".update-btn { background-color: #0275d8; color: white; border: none; padding: 12px 15px; font-weight: bold; border-radius: 4px; cursor: pointer; }");
htmlDoc.AppendLine(".update-btn:active { background-color: #025aa5; transform: translateY(1px); }");
// 2. Inject the Control Panel UI
// We bind the `value` properties directly to the live C# parameters so the
// page loads with the bot's current exact configuration.
htmlDoc.AppendLine("<div class='control-panel'>");
htmlDoc.AppendLine($" <div class='control-group'><label>Activation PnL ($)</label><input type='number' id='inpAct' class='control-input' value='{ActivationProfit}' step='0.5' /></div>");
htmlDoc.AppendLine($" <div class='control-group'><label>Trail Distance ($)</label><input type='number' id='inpDist' class='control-input' value='{TrailingDistance}' step='0.5' /></div>");
htmlDoc.AppendLine(" <button class='update-btn' onclick='sendParamUpdate()'>💾 UPDATE</button>");
htmlDoc.AppendLine("</div>");
Re: Spread Filters for Scalping: When Not to Enter
Posted: Thu Oct 08, 2026 2:21 pm
by PTScalper
3. The Frontend Logic (JavaScript)
Inject this function at the bottom of your BuildSeamlessDashboardJavaScript() method, right next to your sendPanicSignal() function:
Code: Select all
js.AppendLine("async function sendParamUpdate() {");
js.AppendLine(" const act = document.getElementById('inpAct').value;");
js.AppendLine(" const dist = document.getElementById('inpDist').value;");
js.AppendLine(" try {");
// Append the input values as URL query parameters
js.AppendLine(" const res = await fetch('/update-params?act=' + act + '&dist=' + dist, { method: 'POST' });");
js.AppendLine(" if (res.ok) { ");
js.AppendLine(" alert('Parameters dynamically updated on cTrader!'); ");
js.AppendLine(" } else { ");
js.AppendLine(" alert('Error: Invalid values rejected by server.'); ");
js.AppendLine(" }");
js.AppendLine(" } catch (e) { ");
js.AppendLine(" alert('Network error. Check Wi-Fi connection.'); ");
js.AppendLine(" }");
js.AppendLine("}");
Re: Spread Filters for Scalping: When Not to Enter
Posted: Thu Oct 08, 2026 2:21 pm
by PTScalper
Architectural Mechanics
Query String Parsing (context.Request.QueryString): By passing the data in the URL rather than the HTTP Body, the .NET HttpListener automatically parses the key/value pairs into a native NameValueCollection. This completely eliminates the need to manually read InputStream byte streams and deserialize JSON on the background thread.
Virtual DOM Safety: Notice that the <div class='control-panel'> is not wrapped in an ID like trade-body or summary-container. Because the 5-second seamless auto-refresh script is explicitly programmed to only replace those specific IDs, your control panel is completely immune to the polling cycle. If you are typing a new value into the box, the background table will quietly update without stealing your keyboard focus or erasing what you are typing.
Thread Synchronization (BeginInvokeOnMainThread): While assigning a C# double is technically an atomic operation in memory, modifying algorithmic properties from a background thread while the main loop is simultaneously reading them to calculate trailing stop logic is a concurrency anti-pattern. Funneling the assignment through the main thread guarantees the math engine uses the updated numbers on the exact tick the assignment completes.
Re: Spread Filters for Scalping: When Not to Enter
Posted: Thu Oct 08, 2026 2:22 pm
by PTScalper
To display real-time floating positions alongside your history, we must solve a critical hardware bottleneck: Disk I/O.
Your current architecture only updates the HTML file on your SSD when a trade closes. If we tried to write live floating PnL to the hard drive every 5 seconds, it would hammer your SSD with thousands of unnecessary write cycles and potentially freeze the cTrader engine.
The solution is In-Memory Payload Injection. We keep the HTML file on disk static. When your phone requests the page via the local web server, the C# bot reads the static file, instantly injects the live open positions into the HTML string in memory, and sends the combined result to your phone.
Here is the complete C# architecture to add live position tracking to your dashboard:
Re: Spread Filters for Scalping: When Not to Enter
Posted: Thu Oct 08, 2026 2:22 pm
by PTScalper
1. The C# Real-Time Caching Engine
We will use your existing OnTimer() method to snapshot the active positions and live account equity every 500 milliseconds, storing the HTML strings safely in RAM.
Code: Select all
// Add these class-level variables to hold the live HTML strings in RAM
private string _livePositionsHtml = "";
private string _liveSummaryHtml = "";
protected override void OnTimer()
{
// 1. Snapshot Live Positions
StringBuilder liveBuilder = new StringBuilder();
foreach (var p in Positions)
{
string typeColor = p.TradeType == TradeType.Buy ? "#0000ff" : "#ff0000";
string pnlColor = p.NetProfit >= 0 ? "#0000ff" : "#ff0000";
double lotSize = p.VolumeInUnits / 100000.0;
liveBuilder.AppendLine("<tr align=right>");
liveBuilder.AppendLine($"<td>{p.Id}</td>");
liveBuilder.AppendLine($"<td>{p.EntryTime:yyyy.MM.dd HH:mm:ss}</td>");
liveBuilder.AppendLine($"<td style='color:{typeColor}; font-weight:bold;'>{p.TradeType.ToString().ToUpper()}</td>");
liveBuilder.AppendLine($"<td>{lotSize:F2}</td>");
liveBuilder.AppendLine($"<td align=left>{p.SymbolName}</td>");
liveBuilder.AppendLine($"<td>{p.EntryPrice}</td>");
liveBuilder.AppendLine($"<td style='color:{pnlColor}; font-weight:bold;'>{p.NetProfit:C2}</td>");
liveBuilder.AppendLine("</tr>");
}
if (Positions.Count == 0)
{
liveBuilder.AppendLine("<tr><td colspan='7' align='center' style='padding:10px; color:#888;'>No active positions floating in the market</td></tr>");
}
// Atomic string assignments are 100% thread-safe in C#
_livePositionsHtml = liveBuilder.ToString();
// 2. Snapshot the Live Equity Summary Block
_liveSummaryHtml = BuildDynamicSummaryBlock();
// ... (Your existing Batch Processing logic continues here)
}
Re: Spread Filters for Scalping: When Not to Enter
Posted: Thu Oct 08, 2026 2:22 pm
by PTScalper
2. The HTTP Server Interceptor
Update your ProcessHttpRequestAsync method. Right after it reads the static HTML file from the hard drive, we intercept the string and perform the injection before dispatching it to the phone.
Code: Select all
// ... Inside ProcessHttpRequestAsync ...
string htmlContent;
await _htmlFileLock.WaitAsync();
try
{
htmlContent = await File.ReadAllTextAsync(fullPath);
}
finally
{
_htmlFileLock.Release();
}
// THE INJECTION ENGINE:
// 1. Inject the live table rows into the empty static table body
htmlContent = htmlContent.Replace("<tbody id='live-positions-body'></tbody>", $"<tbody id='live-positions-body'>\n{_livePositionsHtml}\n</tbody>");
// 2. Overwrite the stale disk summary block with the live equity summary block
string regexPattern = @"<!-- SUMMARY START -->.*?<!-- SUMMARY END -->";
htmlContent = Regex.Replace(htmlContent, regexPattern, _liveSummaryHtml, RegexOptions.Singleline);
// Construct and dispatch the HTTP OK Response
byte[] responseBytes = Encoding.UTF8.GetBytes(htmlContent);
response.ContentType = "text/html";
// ...
Re: Spread Filters for Scalping: When Not to Enter
Posted: Thu Oct 08, 2026 2:23 pm
by PTScalper
3. The HTML DOM Structure
Update AppendBatchedTradesToHtmlAsync. When the bot generates a new isNewFile == true document at the start of the month, we must add the UI skeleton for the live table.
Code: Select all
// ... (Inside the isNewFile block, right after the Control Panel HTML) ...
htmlDoc.AppendLine("<h3 style='color:#333; margin-bottom:5px;'>🟢 Live Floating Positions</h3>");
htmlDoc.AppendLine("<table style='margin-bottom: 25px;'><thead><tr>");
htmlDoc.AppendLine("<th>Ticket</th><th>Open Time</th><th>Type</th><th>Size</th><th>Item</th><th>Entry Price</th><th>Current P/L</th>");
// Notice the empty tbody with the specific ID. It stays empty on the hard drive.
htmlDoc.AppendLine("</tr></thead><tbody id='live-positions-body'></tbody></table>");
htmlDoc.AppendLine("<h3 style='color:#333; margin-bottom:5px;'>📓 Trade History</h3>");
htmlDoc.AppendLine("<table id='tradesTable'><thead><tr>");
// ... (Your existing History Table headers and logic continue here)
Re: Spread Filters for Scalping: When Not to Enter
Posted: Thu Oct 08, 2026 2:23 pm
by PTScalper
4. The Frontend JS Poller
Finally, update the JavaScript virtual DOM parser inside BuildSeamlessDashboardJavaScript() to extract and replace the new live table alongside the history table.
Code: Select all
// ... Inside the fetchLiveUpdates() function ...
const parser = new DOMParser();
const virtualDoc = parser.parseFromString(htmlText, 'text/html');
// 1. Extract and update the History Table
const newTbody = virtualDoc.getElementById('trade-body');
if (newTbody) document.getElementById('trade-body').innerHTML = newTbody.innerHTML;
// 2. Extract and update the Live Positions Table
const newLiveTbody = virtualDoc.getElementById('live-positions-body');
if (newLiveTbody) document.getElementById('live-positions-body').innerHTML = newLiveTbody.innerHTML;
// 3. Extract and update the Summary Block
const newSummary = virtualDoc.getElementById('summary-container');
if (newSummary) document.getElementById('summary-container').innerHTML = newSummary.innerHTML;
// Recalculate the chart...
updateChartData(document);
Re: Spread Filters for Scalping: When Not to Enter
Posted: Thu Oct 08, 2026 2:24 pm
by PTScalper
Architectural Mechanics
Graceful Degradation: Because the <tbody id='live-positions-body'></tbody> exists in the raw HTML string when saved to the hard drive, opening the file directly from Windows Explorer (instead of via your local Wi-Fi server) won't break the page. It will simply render the history and show an empty table for live positions, preserving the integrity of your permanent records.
Decoupled Workloads: OnTimer() runs on the main thread and builds the HTML string using lightning-fast C# properties. The HTTP server runs on a background thread and performs a simple string.Replace(). This architecture guarantees that even if 5 phones connect to your local web server simultaneously, the cTrader execution engine will experience exactly zero lag.
Visual Continuity: Because the JavaScript Fetch API performs the updates quietly in the background, you can stare at the dashboard on your phone and literally watch the Current P/L numbers jump up and down in real-time, just like the native cTrader terminal, with absolutely no screen flickering or page reloads.