Page 2 of 2
Re: Plotting win rate against reward to risk for each setup, and what the picture showed
Posted: Tue Oct 13, 2026 11:23 am
by PTScalper
This Pine Script v5 indicator provides a foundational trend-following scalping setup. It uses a 200-period Exponential Moving Average (EMA) to determine the baseline trend direction and the Relative Strength Index (RSI) to pinpoint pullback entries when momentum shifts.
Code: Select all
//@version=5
indicator("Scalping Momentum Filter", overlay=true)
// --- Input Parameters ---
emaPeriod = input.int(200, title="Trend EMA Period", group="Trend Settings")
rsiPeriod = input.int(14, title="RSI Period", group="Momentum Settings")
rsiOverbought = input.int(70, title="RSI Overbought Level", group="Momentum Settings")
rsiOversold = input.int(30, title="RSI Oversold Level", group="Momentum Settings")
// --- Calculate Indicators ---
trendEma = ta.ema(close, emaPeriod)
currentRsi = ta.rsi(close, rsiPeriod)
// --- Define Entry Logic ---
// Long: Price is above the 200 EMA (Uptrend) AND RSI crosses up from the oversold level
longCondition = close > trendEma and ta.crossover(currentRsi, rsiOversold)
// Short: Price is below the 200 EMA (Downtrend) AND RSI crosses down from the overbought level
shortCondition = close < trendEma and ta.crossunder(currentRsi, rsiOverbought)
// --- Plotting ---
// Plot the 200 EMA on the chart
plot(trendEma, color=color.new(color.blue, 0), linewidth=2, title="Trend EMA")
// Plot Buy/Sell shapes directly on the candlesticks
plotshape(series=longCondition, title="Long Signal", location=location.belowbar, color=color.new(color.green, 0), style=shape.triangleup, size=size.small, text="BUY")
plotshape(series=shortCondition, title="Short Signal", location=location.abovebar, color=color.new(color.red, 0), style=shape.triangledown, size=size.small, text="SELL")
// --- Optional Alerts ---
if longCondition
alert("Long Scalp Signal Triggered", alert.freq_once_per_bar_close)
if shortCondition
alert("Short Scalp Signal Triggered", alert.freq_once_per_bar_close)
Re: Plotting win rate against reward to risk for each setup, and what the picture showed
Posted: Tue Oct 13, 2026 11:23 am
by PTScalper
How the Logic Works
Trend Filter: The script only looks for long setups when the price is trading above the emaPeriod (default 200), and short setups when below it. This keeps you on the right side of the macroeconomic flow.
Momentum Trigger: Instead of buying the breakout, it waits for a localized pullback within that trend. A long signal fires exactly when the RSI dips below 30 and crosses back up, signaling that the short-term selling pressure is exhausted.
Alert Ready: The alert() function is pre-configured, meaning you can link this directly to external webhooks or trading terminals for automated execution once applied to a chart.
What specific mathematical logic, indicators, or risk management parameters are you looking to build into your custom script?
Re: Plotting win rate against reward to risk for each setup, and what the picture showed
Posted: Tue Oct 13, 2026 11:24 am
by PTScalper
These native implementations recreate the exact logic of the Pine Script indicator for both MetaTrader platforms. Both scripts output EA-ready data through Buffer 0 (Buy signals) and Buffer 1 (Sell signals) so they can be easily integrated into algorithmic execution systems using iCustom().
They also execute the exact same alert logic: firing a pop-up alert only once when the candle closes to prevent intra-bar repainting.
Re: Plotting win rate against reward to risk for each setup, and what the picture showed
Posted: Tue Oct 13, 2026 11:25 am
by PTScalper
MQL5 Implementation (ScalpingMomentum.mq5)
The MQL5 version utilizes indicator handles (iMA, iRSI) within OnInit() and dynamically copies buffer arrays into memory using CopyBuffer during the calculation loop to maximize execution speed.
Code: Select all
//+------------------------------------------------------------------+
//| ScalpingMomentumFilter.mq5 |
//+------------------------------------------------------------------+
#property copyright "Custom Indicator"
#property version "1.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2
//--- Plot 1: Buy Signal
#property indicator_label1 "Buy Signal"
#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrLime
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
//--- Plot 2: Sell Signal
#property indicator_label2 "Sell Signal"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrRed
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
//--- Input Parameters
input int InpEmaPeriod = 200; // Trend EMA Period
input int InpRsiPeriod = 14; // RSI Period
input int InpRsiOverbought = 70; // RSI Overbought Level
input int InpRsiOversold = 30; // RSI Oversold Level
//--- Indicator Buffers
double BuyBuffer[];
double SellBuffer[];
//--- Core Handles and Arrays
int emaHandle;
int rsiHandle;
double emaArr[];
double rsiArr[];
//--- Alert Timer
datetime lastAlertTime = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Bind buffers
SetIndexBuffer(0, BuyBuffer, INDICATOR_DATA);
PlotIndexSetInteger(0, PLOT_ARROW, 233); // Up Arrow
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
SetIndexBuffer(1, SellBuffer, INDICATOR_DATA);
PlotIndexSetInteger(1, PLOT_ARROW, 234); // Down Arrow
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
// Initialize Indicator Handles
emaHandle = iMA(_Symbol, _Period, InpEmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(emaHandle == INVALID_HANDLE) { Print("EMA handle error"); return(INIT_FAILED); }
rsiHandle = iRSI(_Symbol, _Period, InpRsiPeriod, PRICE_CLOSE);
if(rsiHandle == INVALID_HANDLE) { Print("RSI handle error"); return(INIT_FAILED); }
// Set array indexing (0 = current forming candle)
ArraySetAsSeries(BuyBuffer, true);
ArraySetAsSeries(SellBuffer, true);
ArraySetAsSeries(emaArr, true);
ArraySetAsSeries(rsiArr, true);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
// Wait for sufficient data
if(rates_total < InpEmaPeriod) return 0;
ArraySetAsSeries(time, true);
ArraySetAsSeries(low, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(close, true);
// Determine how much data needs updating
int to_copy = (prev_calculated == 0) ? rates_total : rates_total - prev_calculated + 1;
// Copy indicator data
if(CopyBuffer(emaHandle, 0, 0, to_copy, emaArr) <= 0) return 0;
if(CopyBuffer(rsiHandle, 0, 0, to_copy + 1, rsiArr) <= 0) return 0; // Request +1 to check the previous bar
int limit = to_copy - 1;
if(prev_calculated == 0) limit = rates_total - 2;
for(int i = limit; i >= 0; i--)
{
// Reset buffers
BuyBuffer[i] = 0.0;
SellBuffer[i] = 0.0;
double ema = emaArr[i];
double rsi = rsiArr[i];
double rsi_prev = rsiArr[i+1];
// Buy Entry Logic
if(close[i] > ema && rsi_prev < InpRsiOversold && rsi >= InpRsiOversold)
{
BuyBuffer[i] = low[i] - 10 * _Point;
}
// Sell Entry Logic
if(close[i] < ema && rsi_prev > InpRsiOverbought && rsi <= InpRsiOverbought)
{
SellBuffer[i] = high[i] + 10 * _Point;
}
// Fire alerts on closed candles (index 1)
if(i == 1)
{
if(BuyBuffer[1] != 0.0 && time[1] != lastAlertTime)
{
Alert(_Symbol, " [", EnumToString(_Period), "] Long Scalp Triggered");
lastAlertTime = time[1];
}
if(SellBuffer[1] != 0.0 && time[1] != lastAlertTime)
{
Alert(_Symbol, " [", EnumToString(_Period), "] Short Scalp Triggered");
lastAlertTime = time[1];
}
}
}
return(rates_total);
}
Re: Plotting win rate against reward to risk for each setup, and what the picture showed
Posted: Tue Oct 13, 2026 11:25 am
by PTScalper
MQL4 Implementation (ScalpingMomentum.mq4)
MQL4 handles indicators natively within the main calculation loop, calling iMA() and iRSI() directly bar-by-bar.
Code: Select all
//+------------------------------------------------------------------+
//| ScalpingMomentumFilter.mq4 |
//+------------------------------------------------------------------+
#property copyright "Custom Indicator"
#property version "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 clrLime
#property indicator_color2 clrRed
//--- Input Parameters
input int InpEmaPeriod = 200; // Trend EMA Period
input int InpRsiPeriod = 14; // RSI Period
input int InpRsiOverbought = 70; // RSI Overbought Level
input int InpRsiOversold = 30; // RSI Oversold Level
//--- Indicator Buffers
double BuyBuffer[];
double SellBuffer[];
//--- Alert Timer
datetime lastAlertTime = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Bind Buffers
SetIndexBuffer(0, BuyBuffer);
SetIndexStyle(0, DRAW_ARROW);
SetIndexArrow(0, 233); // Up Arrow
SetIndexLabel(0, "Buy Signal");
SetIndexBuffer(1, SellBuffer);
SetIndexStyle(1, DRAW_ARROW);
SetIndexArrow(1, 234); // Down Arrow
SetIndexLabel(1, "Sell Signal");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
// Determine uncalculated bars
int limit = rates_total - prev_calculated;
// Skip the earliest bar to prevent out-of-bounds array errors on i+1
if(prev_calculated == 0) limit = rates_total - 2;
for(int i = limit; i >= 0; i--)
{
// Calculate current EMA and RSI values
double ema = iMA(NULL, 0, InpEmaPeriod, 0, MODE_EMA, PRICE_CLOSE, i);
double rsi = iRSI(NULL, 0, InpRsiPeriod, PRICE_CLOSE, i);
double rsi_prev = iRSI(NULL, 0, InpRsiPeriod, PRICE_CLOSE, i+1);
// Reset buffers
BuyBuffer[i] = EMPTY_VALUE;
SellBuffer[i] = EMPTY_VALUE;
// Buy Entry Logic
if(close[i] > ema && rsi_prev < InpRsiOversold && rsi >= InpRsiOversold)
{
BuyBuffer[i] = low[i] - 10 * Point;
}
// Sell Entry Logic
if(close[i] < ema && rsi_prev > InpRsiOverbought && rsi <= InpRsiOverbought)
{
SellBuffer[i] = high[i] + 10 * Point;
}
// Fire alerts on closed candles (index 1)
if(i == 1)
{
if(BuyBuffer[1] != EMPTY_VALUE && time[1] != lastAlertTime)
{
Alert(Symbol(), " [M", Period(), "] Long Scalp Triggered");
lastAlertTime = time[1];
}
if(SellBuffer[1] != EMPTY_VALUE && time[1] != lastAlertTime)
{
Alert(Symbol(), " [M", Period(), "] Short Scalp Triggered");
lastAlertTime = time[1];
}
}
}
return(rates_total);
}
Re: Plotting win rate against reward to risk for each setup, and what the picture showed
Posted: Tue Oct 13, 2026 11:26 am
by PTScalper
Ctrader version:
Code: Select all
using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class ScalpingMomentumFilter : Indicator
{
// --- Input Parameters ---
[Parameter("Trend EMA Period", DefaultValue = 200, Group = "Trend Settings")]
public int EmaPeriod { get; set; }
[Parameter("RSI Period", DefaultValue = 14, Group = "Momentum Settings")]
public int RsiPeriod { get; set; }
[Parameter("RSI Overbought Level", DefaultValue = 70, Group = "Momentum Settings")]
public int RsiOverbought { get; set; }
[Parameter("RSI Oversold Level", DefaultValue = 30, Group = "Momentum Settings")]
public int RsiOversold { get; set; }
// --- Indicator Outputs (Exposed for cBots) ---
[Output("Trend EMA", LineColor = "Blue", Thickness = 2)]
public IndicatorDataSeries TrendEma { get; set; }
[Output("Buy Signal", LineColor = "Lime", PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries BuyBuffer { get; set; }
[Output("Sell Signal", LineColor = "Red", PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries SellBuffer { get; set; }
// --- Internal Variables ---
private ExponentialMovingAverage _ema;
private RelativeStrengthIndex _rsi;
private int _lastAlertBarIndex = -1;
protected override void Initialize()
{
// Initialize built-in indicators
_ema = Indicators.ExponentialMovingAverage(Bars.ClosePrices, EmaPeriod);
_rsi = Indicators.RelativeStrengthIndex(Bars.ClosePrices, RsiPeriod);
}
public override void Calculate(int index)
{
// Wait for sufficient data
if (index < EmaPeriod || index < 1)
return;
// Assign EMA for chart plotting
TrendEma[index] = _ema.Result[index];
// Initialize buffers to NaN (Empty Value) to ensure no rogue dots are drawn
BuyBuffer[index] = double.NaN;
SellBuffer[index] = double.NaN;
double close = Bars.ClosePrices[index];
double ema = _ema.Result[index];
double rsi = _rsi.Result[index];
double rsiPrev = _rsi.Result[index - 1];
// Buy Entry Logic
if (close > ema && rsiPrev < RsiOversold && rsi >= RsiOversold)
{
// Offset the visual dot 2 pips below the low
BuyBuffer[index] = Bars.LowPrices[index] - (2 * Symbol.PipSize);
}
// Sell Entry Logic
if (close < ema && rsiPrev > RsiOverbought && rsi <= RsiOverbought)
{
// Offset the visual dot 2 pips above the high
SellBuffer[index] = Bars.HighPrices[index] + (2 * Symbol.PipSize);
}
// --- Alert Logic on Bar Close ---
// cTrader processes 'Calculate' on every tick. This logic ensures alerts
// only fire once upon the formation of a new bar, preventing repainting spam.
if (IsRealTime && !IsBacktesting)
{
if (index > _lastAlertBarIndex && _lastAlertBarIndex != -1)
{
int closedBarIndex = index - 1;
if (!double.IsNaN(BuyBuffer[closedBarIndex]))
{
Print("{0} [{1}] Long Scalp Signal Triggered", Symbol.Name, TimeFrame.ToString());
Notifications.PlaySound(SoundType.DoorBell);
}
if (!double.IsNaN(SellBuffer[closedBarIndex]))
{
Print("{0} [{1}] Short Scalp Signal Triggered", Symbol.Name, TimeFrame.ToString());
Notifications.PlaySound(SoundType.DoorBell);
}
}
_lastAlertBarIndex = index; // Update index to prevent duplicate alerts
}
}
}
}
Re: Plotting win rate against reward to risk for each setup, and what the picture showed
Posted: Tue Oct 13, 2026 11:26 am
by PTScalper
Key cTrader (C#) Implementation Details
Buffer Exposure for cBots: The [Output] properties (BuyBuffer and SellBuffer) utilize PlotType.Points. Setting them to double.NaN when there is no signal ensures the chart remains clean. If you reference this indicator inside a custom cBot via Indicators.GetIndicator<ScalpingMomentumFilter>(), the bot can natively read these public properties rather than scraping chart objects.
Alert Synchronization: cAlgo does not feature a native pop-up UI box for custom indicator alerts like MetaTrader. Instead, the logic utilizes Print() to log the exact trigger to the Automate log, and Notifications.PlaySound() for an audible alert.
State Management (IsRealTime): To prevent historical backtesting data from flooding the log or firing thousands of doorbell sounds when the indicator first initializes on the chart, the alert logic is wrapped in if (IsRealTime && !IsBacktesting).