Advertisement IC Markets

Risk-Reward Below 1:1 Needs a Very High Win Rate to Survive

Master exponential money management, position sizing calculators, strict daily stop-loss limits, and overcoming FOMO on micro-timeframes.
Post Reply
Fairman
Posts: 991
Joined: Tue Jul 21, 2026 7:11 am
Location: Abuja

Risk-Reward Below 1:1 Needs a Very High Win Rate to Survive

Post by Fairman »

A lot of scalping setups target 1:1 reward-to-risk, or sometimes even less — grabbing 5 pips while risking 7 or 8, for example.

That's not automatically a bad approach. But it only works under one condition: your win rate has to comfortably clear somewhere around 55-60%, after accounting for spread and commission.

Here's the math intuition. If you're risking exactly what you're targeting, you need to win more than half your trades just to break even, and meaningfully more than half to actually profit once trading costs are subtracted.

This is where a lot of scalpers get quietly destroyed without realizing it. They track their win rate loosely, feel good about "mostly winning," and don't notice that their real, cost-adjusted win rate barely clears breakeven. Weeks go by feeling productive while the account slowly drains.

The fix is simple but requires honesty: track your actual win rate, including cost-adjusted outcomes, over a meaningful sample size — at least 50-100 trades.

If your R:R can't support your win rate, or your win rate can't support your R:R, the strategy is mathematically losing money slowly, even during weeks that feel like "good weeks."

Feelings are not P&L. Only the spreadsheet tells you the truth.
It’s Fairman :geek:
Recommended broker for automated trading & scalping IC Markets
LondonScalper
Posts: 770
Joined: Sat Sep 05, 2026 7:54 am

Re: Risk-Reward Below 1:1 Needs a Very High Win Rate to Survive

Post by LondonScalper »

Fairman wrote:A lot of scalping setups target 1:1 reward-to-risk, or sometimes even less — grabbing 5 pips while risking 7 or 8, for example.
That profile can work — but only if you measure after costs, not on mid prices.

On London majors a 5-pip target with a 1–1.5 pip spread/commission tax is already a different animal from the backtest. I run a simple check: required win rate = (avg loss + costs) / (avg win + avg loss). If my live sample cannot clear that with room to spare, the “high win-rate scalp” is just slow bleed.

Desk preference when R:R is sub-1:1: fewer pairs, stricter session filter, and a hard rule against holding for “a bit more” after the planned exit. Edge lives in repetition and cost control, not in stretching winners you never planned.

Are you tracking realised R including spread, or still grading trades on chart distance alone?
PropScalpDesk
Posts: 364
Joined: Sat Sep 19, 2026 7:50 pm

Re: Risk-Reward Below 1:1 Needs a Very High Win Rate to Survive

Post by PropScalpDesk »

Sub-1:1 only with measured win rate

Grabbing less than you risk can work. It only survives if live win rate after costs clears a high bar — often north of the mid-50s depending on payoff. Many scalpers feel profitable on win rate theatre while the sheet says negative expectancy.

Frankfurt process: I compute required win rate for my actual R:R and costs, then compare to the last 100 tagged tickets. If the sample does not clear the bar, I either improve location quality or stop taking that shape — I do not “try harder” with the same math.

Prop accounts make this stricter; daily loss caps punish grinding negative expectancy faster than a home book does.

I revisit the hurdle after any cost change — new commission, different average spread. Old win-rate memories against new costs are how quiet bleed starts.

Is your sub-1:1 playbook clearing the hurdle on a cost-adjusted 100-trade sample, or surviving on memory of the good weeks?
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Risk-Reward Below 1:1 Needs a Very High Win Rate to Survive

Post by FTtrader »

Fairman wrote: Thu Aug 20, 2026 8:56 pm A lot of scalping setups target 1:1 reward-to-risk, or sometimes even less — grabbing 5 pips while risking 7 or 8, for example.

That's not automatically a bad approach. But it only works under one condition: your win rate has to comfortably clear somewhere around 55-60%, after accounting for spread and commission.

Here's the math intuition. If you're risking exactly what you're targeting, you need to win more than half your trades just to break even, and meaningfully more than half to actually profit once trading costs are subtracted.

This is where a lot of scalpers get quietly destroyed without realizing it. They track their win rate loosely, feel good about "mostly winning," and don't notice that their real, cost-adjusted win rate barely clears breakeven. Weeks go by feeling productive while the account slowly drains.

The fix is simple but requires honesty: track your actual win rate, including cost-adjusted outcomes, over a meaningful sample size — at least 50-100 trades.

If your R:R can't support your win rate, or your win rate can't support your R:R, the strategy is mathematically losing money slowly, even during weeks that feel like "good weeks."

Feelings are not P&L. Only the spreadsheet tells you the truth.
Hello Fairman,

This is exactly right. The math behind a 1:1 or negative risk-to-reward ratio is utterly unforgiving, and the "spread tax" is exactly where unverified intuition destroys trading accounts.

I actually know two other forex scalpers who trade with this exact money management profile—regularly risking 7 or 8 pips to grab 5—and they are highly profitable. The reason it works for them is that their execution is almost surgical. During a good trading series, their win rate sits consistently between 88% and 94%. They aren't just barely clearing the 60% breakeven hurdle; they are completely overwhelming the negative R:R math with a massive volume of consecutive wins.

However, achieving that is incredibly difficult, and this approach absolutely requires a specific psychological wiring. To trade an inverse R:R successfully, you have to be the kind of person who can take a sudden 8-pip loss that instantly wipes out your last two winning trades, and then immediately take the next setup without a drop of hesitation, frustration, or revenge trading.

Most people simply do not have the robotic emotional control for that. It is a highly effective style if your personality naturally suits rapid-fire, high-probability execution. But for anyone who hasn't ruthlessly verified their stats over hundreds of trades like you mentioned, it is just a slow, expensive bleed.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Risk-Reward Below 1:1 Needs a Very High Win Rate to Survive

Post by FTtrader »

This script sets up a complete Pine Script v5 backtesting environment tailored specifically for that inverse R:R scalping model.

Since you focus on raw price action and liquidity sweeps rather than lagging indicators, I built the entry logic around a simple placeholder for a liquidity sweep (sweeping a recent extreme and rejecting).

More importantly, it includes a real-time custom dashboard on the chart that calculates your actual cost-adjusted win rate and compares it live against the mathematical breakeven win rate required for your specific SL/TP inputs.

Code: Select all

//@version=5
strategy("Price Action Scalper [Negative R:R Tracker]", overlay=true, margin_long=100, margin_short=100, commission_type=strategy.commission.cash_per_order, commission_value=3, calc_on_every_tick=true)

// =========================================================================
// INPUTS & RISK MANAGEMENT
// =========================================================================
grp_risk = "Risk & Reward (Pips)"
tp_pips = input.float(5.0, title="Take Profit (Pips)", group=grp_risk)
sl_pips = input.float(8.0, title="Stop Loss (Pips)", group=grp_risk)
// Multiplier to convert pips to ticks (10 for fractional pip forex brokers)
pip_mult = input.int(10, title="Pip to Tick Multiplier", group=grp_risk)

tp_ticks = tp_pips * pip_mult
sl_ticks = sl_pips * pip_mult

// =========================================================================
// ENTRY LOGIC (PRICE ACTION / LIQUIDITY SWEEP PLACEHOLDER)
// =========================================================================
grp_pa = "Price Action Settings"
lookback = input.int(5, title="Sweep Lookback Bars", group=grp_pa)

// Simple Sweep Logic: Price breaks below recent low but closes back above it (rejection)
sweep_low = low < ta.lowest(low[1], lookback) and close > low
sweep_high = high > ta.highest(high[1], lookback) and close < high

// Add your specific structural filters here (e.g., higher timeframe trend, time of day)
valid_long = sweep_low 
valid_short = sweep_high 

// =========================================================================
// EXECUTION
// =========================================================================
if (valid_long and strategy.position_size == 0)
    strategy.entry("Long", strategy.long)
    
if (valid_short and strategy.position_size == 0)
    strategy.entry("Short", strategy.short)

// Fixed Target and Stop based on the exact R:R discussed
strategy.exit("Exit Long", "Long", profit=tp_ticks, loss=sl_ticks)
strategy.exit("Exit Short", "Short", profit=tp_ticks, loss=sl_ticks)

// =========================================================================
// PERFORMANCE DASHBOARD (THE "TRUTH SPREADSHEET")
// =========================================================================
// Calculate mathematical breakeven win rate for the given R:R
req_win_rate = (sl_pips / (tp_pips + sl_pips)) * 100

// Track actual performance
total_trades = strategy.closedtrades
winning_trades = strategy.wintrades
actual_win_rate = total_trades > 0 ? (winning_trades / total_trades) * 100 : 0.0

// Draw Dashboard
var table perf_table = table.new(position.bottom_right, 2, 4, border_width=1, border_color=color.gray, frame_color=color.black, frame_width=1)

if (barstate.islast)
    // Table Headers
    table.cell(perf_table, 0, 0, "Metric", bgcolor=color.new(color.blue, 80), text_color=color.white, text_size=size.small)
    table.cell(perf_table, 1, 0, "Value", bgcolor=color.new(color.blue, 80), text_color=color.white, text_size=size.small)
    
    // Total Trades
    table.cell(perf_table, 0, 1, "Total Trades", bgcolor=color.new(color.gray, 90), text_color=color.white, text_size=size.small)
    table.cell(perf_table, 1, 1, str.tostring(total_trades), bgcolor=color.new(color.gray, 90), text_color=color.white, text_size=size.small)
    
    // Required Breakeven
    table.cell(perf_table, 0, 2, "Req. Breakeven WR", bgcolor=color.new(color.gray, 90), text_color=color.silver, text_size=size.small)
    table.cell(perf_table, 1, 2, str.tostring(req_win_rate, "#.##") + "%", bgcolor=color.new(color.gray, 90), text_color=color.silver, text_size=size.small)
    
    // Actual Win Rate (Colors based on whether it clears breakeven)
    wr_color = actual_win_rate >= req_win_rate ? color.new(color.green, 70) : color.new(color.red, 70)
    table.cell(perf_table, 0, 3, "Actual Win Rate", bgcolor=wr_color, text_color=color.white, text_size=size.small)
    table.cell(perf_table, 1, 3, str.tostring(actual_win_rate, "#.##") + "%", bgcolor=wr_color, text_color=color.white, text_size=size.small)
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Risk-Reward Below 1:1 Needs a Very High Win Rate to Survive

Post by FTtrader »

How to use this for your specific flow:

Commission Setup: The strategy() declaration currently includes a $3 per trade commission (standard for many raw spread accounts). You can adjust this to exactly match your broker's fees so the backtester's net profit reflects reality.

The Entry Trigger: Replace the sweep_low and sweep_high variables with your specific 1-minute or 5-minute price action logic.

The Dashboard: The table in the bottom right will tell you exactly if your current logic clears the ~61.5% minimum threshold required for a 5-pip TP / 8-pip SL setup. If the actual win rate box is red, the logic is slowly bleeding the account regardless of how many individual trades are "winners."
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Risk-Reward Below 1:1 Needs a Very High Win Rate to Survive

Post by FTtrader »

Here is the complete C# cBot for cTrader, translated from the Pine Script logic.

It uses cTrader's native UI elements to build the live performance dashboard directly on the chart. It triggers off completed bars (OnBar) to confirm the price action sweep and hooks into the Positions.Closed event to recalculate your real, cost-adjusted win rate instantly after every trade.

Code: Select all

using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class PriceActionScalper : Robot
    {
        // =========================================================================
        // INPUTS & RISK MANAGEMENT
        // =========================================================================
        [Parameter("Volume (Units)", Group = "Risk & Reward", DefaultValue = 100000, MinValue = 1000, Step = 1000)]
        public double Volume { get; set; }

        [Parameter("Take Profit (Pips)", Group = "Risk & Reward", DefaultValue = 5.0)]
        public double TakeProfitPips { get; set; }

        [Parameter("Stop Loss (Pips)", Group = "Risk & Reward", DefaultValue = 8.0)]
        public double StopLossPips { get; set; }

        [Parameter("Sweep Lookback Bars", Group = "Price Action Settings", DefaultValue = 5, MinValue = 2)]
        public int Lookback { get; set; }

        private const string BotLabel = "NegRR_Scalper";
        
        // UI Elements
        private TextBlock _actualWinRateText;
        private TextBlock _totalTradesText;
        private Border _winRateBorder;
        private double _reqWinRate;

        protected override void OnStart()
        {
            // Calculate mathematical breakeven win rate
            _reqWinRate = (StopLossPips / (TakeProfitPips + StopLossPips)) * 100;

            DrawDashboard();

            // Hook into position closure to update stats instantly
            Positions.Closed += OnPositionClosed;
        }

        // =========================================================================
        // ENTRY LOGIC (PRICE ACTION / LIQUIDITY SWEEP)
        // =========================================================================
        protected override void OnBar()
        {
            // Only execute if we have no open positions for this bot
            if (Positions.Count(p => p.Label == BotLabel) > 0)
                return;

            // Evaluate on the most recently closed bar (index 1) to avoid mid-candle repainting
            var lastBarIndex = Bars.Count - 2;
            var lastBar = Bars[lastBarIndex];

            // Get the lowest low and highest high of the lookback period BEFORE the last closed bar
            double lowestLow = GetLowestLow(lastBarIndex - 1, Lookback);
            double highestHigh = GetHighestHigh(lastBarIndex - 1, Lookback);

            // Simple Sweep Logic: Price breaks recent extreme but closes back inside
            bool sweepLow = lastBar.Low < lowestLow && lastBar.Close > lastBar.Low;
            bool sweepHigh = lastBar.High > highestHigh && lastBar.Close < lastBar.High;

            if (sweepLow)
            {
                ExecuteMarketOrder(TradeType.Buy, SymbolName, Volume, BotLabel, StopLossPips, TakeProfitPips);
            }
            else if (sweepHigh)
            {
                ExecuteMarketOrder(TradeType.Sell, SymbolName, Volume, BotLabel, StopLossPips, TakeProfitPips);
            }
        }

        // =========================================================================
        // PERFORMANCE DASHBOARD (THE "TRUTH SPREADSHEET")
        // =========================================================================
        private void OnPositionClosed(PositionClosedEventArgs args)
        {
            if (args.Position.Label != BotLabel) return;
            UpdateDashboard();
        }

        private void DrawDashboard()
        {
            var panel = new StackPanel
            {
                Orientation = Orientation.Vertical,
                BackgroundColor = Color.FromHex("CC1E1E1E"),
                HorizontalAlignment = HorizontalAlignment.Right,
                VerticalAlignment = VerticalAlignment.Bottom,
                Margin = new Thickness(10)
            };

            // Title
            panel.AddChild(CreateRow("Performance Tracker", "", Color.DodgerBlue, Color.Transparent));

            // Total Trades
            _totalTradesText = new TextBlock { Text = "0", Foreground = Color.White, Margin = new Thickness(5, 0, 0, 0) };
            panel.AddChild(CreateDataRow("Total Trades:", _totalTradesText));

            // Required Breakeven
            var reqRateText = new TextBlock { Text = $"{Math.Round(_reqWinRate, 2)}%", Foreground = Color.Silver, Margin = new Thickness(5, 0, 0, 0) };
            panel.AddChild(CreateDataRow("Req. Breakeven:", reqRateText));

            // Actual Win Rate
            _actualWinRateText = new TextBlock { Text = "0.00%", Foreground = Color.White, Margin = new Thickness(5, 0, 0, 0), FontWeight = FontWeight.ExtraBold };
            
            _winRateBorder = new Border { Child = _actualWinRateText, BackgroundColor = Color.Red, Padding = new Thickness(3), CornerRadius = 3 };
            
            var wrRow = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(5) };
            wrRow.AddChild(new TextBlock { Text = "Actual Win Rate:", Foreground = Color.White, Width = 100 });
            wrRow.AddChild(_winRateBorder);
            
            panel.AddChild(wrRow);

            Chart.AddControl(panel);
            
            // Initial render
            UpdateDashboard();
        }

        private void UpdateDashboard()
        {
            // Calculate stats based on closed trades in History
            var botTrades = History.Where(t => t.Label == BotLabel).ToList();
            int totalTrades = botTrades.Count;
            
            if (totalTrades == 0) return;

            // Using NetProfit > 0 ensures commission and swap are fully accounted for in the win rate
            int winningTrades = botTrades.Count(t => t.NetProfit > 0);
            
            double actualWinRate = ((double)winningTrades / totalTrades) * 100;

            _totalTradesText.Text = totalTrades.ToString();
            _actualWinRateText.Text = $"{Math.Round(actualWinRate, 2)}%";

            // Green if profitable, Red if mathematically bleeding
            _winRateBorder.BackgroundColor = actualWinRate >= _reqWinRate ? Color.SeaGreen : Color.Crimson;
        }

        // =========================================================================
        // UTILITY METHODS
        // =========================================================================
        private double GetLowestLow(int startIndex, int periods)
        {
            double min = double.MaxValue;
            for (int i = 0; i < periods; i++)
            {
                min = Math.Min(min, Bars[startIndex - i].Low);
            }
            return min;
        }

        private double GetHighestHigh(int startIndex, int periods)
        {
            double max = double.MinValue;
            for (int i = 0; i < periods; i++)
            {
                max = Math.Max(max, Bars[startIndex - i].High);
            }
            return max;
        }
        
        private StackPanel CreateDataRow(string label, TextBlock valueControl)
        {
            var row = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(5) };
            row.AddChild(new TextBlock { Text = label, Foreground = Color.White, Width = 100 });
            row.AddChild(valueControl);
            return row;
        }

        private StackPanel CreateRow(string left, string right, Color bgColor, Color fgColor)
        {
            var row = new StackPanel { Orientation = Orientation.Horizontal, BackgroundColor = bgColor, Margin = new Thickness(0, 0, 0, 2), Padding = new Thickness(5) };
            row.AddChild(new TextBlock { Text = left, Foreground = Color.White, FontWeight = FontWeight.Bold, Width = 150 });
            return row;
        }
    }
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Risk-Reward Below 1:1 Needs a Very High Win Rate to Survive

Post by FTtrader »

Key Differences from the Pine Version:

Spread & Commission Calculation: Because cTrader accounts for exact cash values natively, the dashboard checks NetProfit > 0 for the winningTrades count. This ensures that a trade that technically hits its 5-pip TP, but loses money due to high slippage, swap, or commission, is accurately classified as a loss in the tracker.

Tick vs Bar Execution: cBots default to OnTick, but I explicitly routed the logic through OnBar. Since you are scalping liquidity sweeps based on candlestick structure, triggering on the bar close completely prevents mid-candle repainting.

Event-Driven Tracking: Rather than calculating stats on every tick, it uses the Positions.Closed event handler to update the UI only when a trade finalizes, keeping the C# execution perfectly optimized.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Risk-Reward Below 1:1 Needs a Very High Win Rate to Survive

Post by FTtrader »

Here are the complete Expert Advisors for both MetaTrader platforms. Both versions use the Comment() function to output the real-time, cost-adjusted metrics directly to the chart, keeping the codebase lean and entirely focused on execution logic.

The entry logic is evaluated exactly on the bar close (IsNewBar() check) to confirm the liquidity sweep and completely eliminate mid-candle repainting.

MetaTrader 4 (MQL4)
In MT4, the dashboard calculates the exact net result by summing OrderProfit(), OrderCommission(), and OrderSwap()

Code: Select all

//+------------------------------------------------------------------+
//|                                     PriceActionScalper_MT4.mq4   |
//+------------------------------------------------------------------+
#property strict

input double InpLots = 0.1;
input double InpTakeProfitPips = 5.0;
input double InpStopLossPips = 8.0;
input int    InpLookback = 5;
input int    InpMagicNumber = 123456;

double _reqWinRate;
double _pipMultiplier;

int OnInit()
{
    _reqWinRate = (InpStopLossPips / (InpTakeProfitPips + InpStopLossPips)) * 100.0;
    _pipMultiplier = (Digits == 3 || Digits == 5) ? 10.0 : 1.0;
    return(INIT_SUCCEEDED);
}

void OnTick()
{
    if(!IsNewBar()) return;
    
    UpdateDashboard();
    
    // Prevent multiple open positions
    if(OrdersTotal() > 0)
    {
        for(int i = 0; i < OrdersTotal(); i++)
        {
            if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
            {
                if(OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagicNumber && OrderType() <= OP_SELL)
                    return; 
            }
        }
    }
    
    int lowestIndex = iLowest(_Symbol, _Period, MODE_LOW, InpLookback, 2);
    int highestIndex = iHighest(_Symbol, _Period, MODE_HIGH, InpLookback, 2);
    
    double lowestLow = Low[lowestIndex];
    double highestHigh = High[highestIndex];
    
    bool sweepLow = Low[1] < lowestLow && Close[1] > Low[1];
    bool sweepHigh = High[1] > highestHigh && Close[1] < High[1];
    
    double tpPoints = InpTakeProfitPips * _pipMultiplier * Point;
    double slPoints = InpStopLossPips * _pipMultiplier * Point;
    
    if(sweepLow)
    {
        OrderSend(_Symbol, OP_BUY, InpLots, Ask, 3, Ask - slPoints, Ask + tpPoints, "NegRR_Scalper", InpMagicNumber, 0, clrGreen);
    }
    else if(sweepHigh)
    {
        OrderSend(_Symbol, OP_SELL, InpLots, Bid, 3, Bid + slPoints, Bid - tpPoints, "NegRR_Scalper", InpMagicNumber, 0, clrRed);
    }
}

bool IsNewBar()
{
    static datetime lastBarTime;
    datetime currentBarTime = Time[0];
    if(lastBarTime != currentBarTime)
    {
        lastBarTime = currentBarTime;
        return true;
    }
    return false;
}

void UpdateDashboard()
{
    int totalTrades = 0;
    int winningTrades = 0;
    
    int historyTotal = OrdersHistoryTotal();
    for(int i = 0; i < historyTotal; i++)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
        {
            if(OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagicNumber)
            {
                if(OrderType() == OP_BUY || OrderType() == OP_SELL)
                {
                    totalTrades++;
                    double netProfit = OrderProfit() + OrderCommission() + OrderSwap();
                    if(netProfit > 0) winningTrades++;
                }
            }
        }
    }
    
    double actualWR = totalTrades > 0 ? ((double)winningTrades / totalTrades) * 100.0 : 0;
    string status = actualWR >= _reqWinRate ? "PROFITABLE [OK]" : "BLEEDING [WARNING]";
    
    string text = "--- NEGATIVE R:R SCALPER ---\n";
    text += "Total Trades: " + IntegerToString(totalTrades) + "\n";
    text += "Req. Breakeven: " + DoubleToString(_reqWinRate, 2) + "%\n";
    text += "Actual Win Rate: " + DoubleToString(actualWR, 2) + "%\n";
    text += "Status: " + status;
    
    Comment(text);
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Risk-Reward Below 1:1 Needs a Very High Win Rate to Survive

Post by FTtrader »

MetaTrader 5 (MQL5)

In MT5, the exact cost basis is derived by isolating closed trades (DEAL_ENTRY_OUT) and summing DEAL_PROFIT, DEAL_COMMISSION, and DEAL_SWAP.

Code: Select all

//+------------------------------------------------------------------+
//|                                     PriceActionScalper_MT5.mq5   |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\DealInfo.mqh>

input double InpLots = 0.1;
input double InpTakeProfitPips = 5.0;
input double InpStopLossPips = 8.0;
input int    InpLookback = 5;
input ulong  InpMagicNumber = 123456;

CTrade trade;
CPositionInfo pos;

double _reqWinRate;
double _pipMultiplier;

int OnInit()
{
    trade.SetExpertMagicNumber(InpMagicNumber);
    _reqWinRate = (InpStopLossPips / (InpTakeProfitPips + InpStopLossPips)) * 100.0;
    _pipMultiplier = (_Digits == 3 || _Digits == 5) ? 10.0 : 1.0;
    return(INIT_SUCCEEDED);
}

void OnTick()
{
    if(!IsNewBar()) return;
    
    UpdateDashboard();
    
    // Prevent multiple open positions
    if(PositionsTotal() > 0)
    {
        for(int i = PositionsTotal()-1; i>=0; i--)
        {
            if(pos.SelectByIndex(i))
            {
                if(pos.Symbol() == _Symbol && pos.Magic() == InpMagicNumber) 
                    return; 
            }
        }
    }
    
    double lowestLow = iLowestCustom(_Symbol, _Period, InpLookback, 2);
    double highestHigh = iHighestCustom(_Symbol, _Period, InpLookback, 2);
    
    double low1 = iLow(_Symbol, _Period, 1);
    double high1 = iHigh(_Symbol, _Period, 1);
    double close1 = iClose(_Symbol, _Period, 1);
    
    bool sweepLow = low1 < lowestLow && close1 > low1;
    bool sweepHigh = high1 > highestHigh && close1 < high1;
    
    double tpPoints = InpTakeProfitPips * _pipMultiplier * _Point;
    double slPoints = InpStopLossPips * _pipMultiplier * _Point;
    
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    
    if(sweepLow)
    {
        trade.Buy(InpLots, _Symbol, ask, ask - slPoints, ask + tpPoints, "NegRR_Scalper");
    }
    else if(sweepHigh)
    {
        trade.Sell(InpLots, _Symbol, bid, bid + slPoints, bid - tpPoints, "NegRR_Scalper");
    }
}

bool IsNewBar()
{
    static datetime lastBarTime = 0;
    datetime currentBarTime = iTime(_Symbol, _Period, 0);
    if(lastBarTime != currentBarTime)
    {
        lastBarTime = currentBarTime;
        return true;
    }
    return false;
}

double iLowestCustom(string symbol, ENUM_TIMEFRAMES tf, int count, int start)
{
    double arr[];
    ArraySetAsSeries(arr, true);
    if(CopyLow(symbol, tf, start, count, arr) > 0)
    {
        int min_idx = ArrayMinimum(arr);
        return arr[min_idx];
    }
    return 0;
}

double iHighestCustom(string symbol, ENUM_TIMEFRAMES tf, int count, int start)
{
    double arr[];
    ArraySetAsSeries(arr, true);
    if(CopyHigh(symbol, tf, start, count, arr) > 0)
    {
        int max_idx = ArrayMaximum(arr);
        return arr[max_idx];
    }
    return 0;
}

void UpdateDashboard()
{
    int totalTrades = 0;
    int winningTrades = 0;
    
    if(HistorySelect(0, TimeCurrent()))
    {
        int dealsTotal = HistoryDealsTotal();
        for(int i = 0; i < dealsTotal; i++)
        {
            ulong ticket = HistoryDealGetTicket(i);
            if(ticket > 0)
            {
                if(HistoryDealGetString(ticket, DEAL_SYMBOL) == _Symbol && 
                   HistoryDealGetInteger(ticket, DEAL_MAGIC) == InpMagicNumber &&
                   HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_OUT)
                {
                    totalTrades++;
                    double netProfit = HistoryDealGetDouble(ticket, DEAL_PROFIT) + 
                                       HistoryDealGetDouble(ticket, DEAL_COMMISSION) + 
                                       HistoryDealGetDouble(ticket, DEAL_SWAP);
                    if(netProfit > 0) winningTrades++;
                }
            }
        }
    }
    
    double actualWR = totalTrades > 0 ? ((double)winningTrades / totalTrades) * 100.0 : 0;
    string status = actualWR >= _reqWinRate ? "PROFITABLE [OK]" : "BLEEDING [WARNING]";
    
    string text = "--- NEGATIVE R:R SCALPER ---\n";
    text += "Total Trades: " + IntegerToString(totalTrades) + "\n";
    text += "Req. Breakeven: " + DoubleToString(_reqWinRate, 2) + "%\n";
    text += "Actual Win Rate: " + DoubleToString(actualWR, 2) + "%\n";
    text += "Status: " + status;
    
    Comment(text);
}
Post Reply