Advertisement IC Markets

Stop Looking at Your P&L While Trading

Master exponential money management, position sizing calculators, strict daily stop-loss limits, and overcoming FOMO on micro-timeframes.
dreambig
Posts: 19
Joined: Fri Sep 18, 2026 5:10 pm

Stop Looking at Your P&L While Trading

Post by dreambig »

One thing I’m trying to work on lately is something that sounds incredibly simple:

Stop looking at the money.

When I open a trade, I know exactly where my stop loss and take profit are. The trade is planned. The risk is defined.

But then I look at the P&L.

* $80.
* $120.
* $150.

And suddenly my brain starts making decisions that were never part of the plan.

“Maybe I should close it now.”

“What if it goes back?”

“I already made enough today.”

And the worst part is that the chart hasn’t changed. Only the number on my screen has changed.

A trade doesn’t know that I’m currently +$150. The market doesn’t care about my daily target or how much money I need to make.

That’s why I’m starting to believe that watching P&L while trading can actually be a distraction.

The chart should tell me when the trade is wrong or when my target is reached.

Not the money.

I’m not saying it’s easy. Especially with prop firm accounts, where the account balance and drawdown limits are always in the back of your mind.

But maybe the goal is to stop thinking:

“How much am I making?”

And start thinking:

“Am I following my plan?”

Because that’s the only part I can actually control.

DreamBig
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Stop Looking at Your P&L While Trading

Post by PTScalper »

dreambig wrote: Tue Sep 22, 2026 1:08 pm One thing I’m trying to work on lately is something that sounds incredibly simple:

Stop looking at the money.

When I open a trade, I know exactly where my stop loss and take profit are. The trade is planned. The risk is defined.

But then I look at the P&L.

* $80.
* $120.
* $150.

And suddenly my brain starts making decisions that were never part of the plan.

“Maybe I should close it now.”

“What if it goes back?”

“I already made enough today.”

And the worst part is that the chart hasn’t changed. Only the number on my screen has changed.

A trade doesn’t know that I’m currently +$150. The market doesn’t care about my daily target or how much money I need to make.

That’s why I’m starting to believe that watching P&L while trading can actually be a distraction.

The chart should tell me when the trade is wrong or when my target is reached.

Not the money.

I’m not saying it’s easy. Especially with prop firm accounts, where the account balance and drawdown limits are always in the back of your mind.

But maybe the goal is to stop thinking:

“How much am I making?”

And start thinking:

“Am I following my plan?”

Because that’s the only part I can actually control.

DreamBig
Hi DreamBig,

You have just identified the exact boundary between a gambler and a professional. When you look at your P&L, you stop trading the market and start trading your bank account.

The human brain is hardwired for loss aversion. Seeing +$150 triggers a dopamine hit, immediately followed by the fear of losing it. Your brain doesn't see a candlestick approaching a resistance zone; it sees a grocery bill, a car payment, or a prop firm passing threshold. It forces you to manage the emotion instead of managing the trade.

The solution is mechanical: hide the P&L entirely. Change your broker terminal settings to show percentages or points instead of dollars, or minimize the window completely once the order is placed.

To help enforce this, here is a Pine Script designed specifically for your mindset. It is a "Chart-Only Trade Visualizer."
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Stop Looking at Your P&L While Trading

Post by PTScalper »

The goal of this script is to allow you to plan your trade directly on the TradingView chart and track its progress visually without ever needing to look at your broker’s P&L panel. It plots your Entry, Stop Loss, and Take Profit, colors the risk/reward zones, and displays your objective Risk-to-Reward ratio in a corner table. No dollar signs, no floating P&L—just pure structure.

Code: Select all

//@version=5
indicator("Trade Plan Visualizer (Zero P&L)", overlay=true)

// Interactive inputs - you can click the chart to set these levels
entryPrice = input.price(title="1. Click Entry Price", defval=0.0, confirm=true)
stopLoss   = input.price(title="2. Click Stop Loss", defval=0.0, confirm=true)
takeProfit = input.price(title="3. Click Take Profit", defval=0.0, confirm=true)

// Calculate Risk and Reward
risk = math.abs(entryPrice - stopLoss)
reward = math.abs(takeProfit - entryPrice)
rrRatio = risk > 0 ? reward / risk : 0

// Plotting the levels invisible to standard plots so we can use fill()
plotEntry = plot(entryPrice != 0 ? entryPrice : na, color=color.new(color.gray, 0), style=plot.style_linebr, linewidth=2, title="Entry")
plotSL    = plot(stopLoss != 0 ? stopLoss : na, color=color.new(color.red, 0), style=plot.style_linebr, linewidth=2, title="Stop Loss")
plotTP    = plot(takeProfit != 0 ? takeProfit : na, color=color.new(color.green, 0), style=plot.style_linebr, linewidth=2, title="Take Profit")

// Fill the zones to visualize Risk (Red) and Reward (Green)
fill(plotEntry, plotSL, color=color.new(color.red, 85), title="Risk Zone")
fill(plotEntry, plotTP, color=color.new(color.green, 85), title="Reward Zone")

// Display an objective stat table - completely free of dollar amounts
var table planTable = table.new(position.bottom_right, 2, 2, border_width = 1, border_color = color.gray, frame_color = color.gray, frame_width = 1)

if barstate.islast and entryPrice != 0
    table.cell(planTable, 0, 0, "Risk : Reward", text_color=color.white, bgcolor=color.new(color.black, 50))
    table.cell(planTable, 1, 0, "1 : " + str.tostring(rrRatio, "#.##"), text_color=color.white, bgcolor=color.new(color.black, 50))
    table.cell(planTable, 0, 1, "Status", text_color=color.white, bgcolor=color.new(color.black, 50))
    table.cell(planTable, 1, 1, "Trade The Chart", text_color=color.yellow, bgcolor=color.new(color.black, 50))
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Stop Looking at Your P&L While Trading

Post by PTScalper »

How to use this to fix the habit:

Set your broker aside: Place your order in your terminal with the hard SL and TP attached.

Minimize the broker window: Completely remove it from your field of vision.

Use the script: Add this script to your TradingView chart. When prompted, click the chart to drop your Entry, SL, and TP lines where your real orders are.

Watch the zones, not the numbers: Your screen will now show a shaded green box (your target) and a shaded red box (your invalidation).

If the price is inside the zones, there is nothing for you to do. You only act if the price structurally breaks your thesis. The money is just a side effect of following the lines.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Stop Looking at Your P&L While Trading

Post by PTScalper »

MetaTrader requires a different mechanical approach than Pine Script, but it actually handles this workflow better. We can leverage OnChartEvent to create a fluid, interactive overlay—perfect for plotting out structural invalidations and liquidity sweeps on a 15-minute or daily chart without ever looking at the terminal balance.

This single file compiles natively in both MetaTrader 4 (.mq4) and MetaTrader 5 (.mq5) as a Custom Indicator.

When you attach it to a chart, it generates three interactive horizontal lines (Entry, SL, TP). You simply double-click and drag the lines to frame your setup. The script automatically redraws the Risk/Reward background zones behind your candlesticks and updates the objective ratio in the corner as you move them.

Code: Select all

//+------------------------------------------------------------------+
//|                                        TradePlanVisualizer.mq4/5 |
//|                                        Zero P&L Mindset Tracker  |
//+------------------------------------------------------------------+
#property copyright "Zero P&L"
#property indicator_chart_window
#property indicator_plots 0

//--- Input parameters
input color InpEntryColor  = clrGray;          // Entry Line Color
input color InpSLColor     = clrRed;           // Stop Loss Line Color
input color InpTPColor     = clrLimeGreen;     // Take Profit Line Color
input color InpRiskColor   = clrCrimson;       // Risk Zone Color
input color InpRewardColor = clrMediumSeaGreen;// Reward Zone Color

//--- Global variables
string prefix = "ZeroPnL_";
string lineEntry = prefix + "Entry";
string lineSL    = prefix + "SL";
string lineTP    = prefix + "TP";
string boxRisk   = prefix + "RiskBox";
string boxReward = prefix + "RewardBox";
string lblStats  = prefix + "Stats";
string lblStatus = prefix + "Status";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   
   // Default separation of 200 points (20 pips on 5-digit brokers)
   CreateHLine(lineTP, ask + 200 * point, InpTPColor);
   CreateHLine(lineEntry, ask, InpEntryColor);
   CreateHLine(lineSL, ask - 200 * point, InpSLColor);
   
   CreateRectangle(boxRisk, InpRiskColor);
   CreateRectangle(boxReward, InpRewardColor);
   
   CreateLabel(lblStats, 10, 40, clrWhite, 12);
   CreateLabel(lblStatus, 10, 20, clrYellow, 10);
   
   UpdateVisuals();
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0, prefix);
   ChartRedraw(0);
  }

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   // If any of our lines are dragged, recalculate and redraw the zones
   if(id == CHARTEVENT_OBJECT_DRAG)
     {
      if(sparam == lineEntry || sparam == lineSL || sparam == lineTP)
        {
         UpdateVisuals();
         ChartRedraw(0);
        }
     }
  }

//+------------------------------------------------------------------+
//| 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[])
  {
   // Keeps the rectangles anchored to the far right as live ticks come in
   UpdateVisuals();
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| Helper Functions                                                 |
//+------------------------------------------------------------------+
void CreateHLine(string name, double price, color clr)
  {
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, true);
      ObjectSetInteger(0, name, OBJPROP_SELECTED, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, false);
     }
  }

void CreateRectangle(string name, color clr)
  {
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_RECTANGLE, 0, 0, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_BACK, true); // Keep behind candles
      ObjectSetInteger(0, name, OBJPROP_FILL, true); // Solid fill
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
     }
  }

void CreateLabel(string name, int x, int y, color clr, int fontSize)
  {
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetString(0, name, OBJPROP_FONT, "Arial");
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
     }
  }

void UpdateVisuals()
  {
   double entry = ObjectGetDouble(0, lineEntry, OBJPROP_PRICE);
   double sl    = ObjectGetDouble(0, lineSL, OBJPROP_PRICE);
   double tp    = ObjectGetDouble(0, lineTP, OBJPROP_PRICE);
   
   double risk   = MathAbs(entry - sl);
   double reward = MathAbs(tp - entry);
   double rrRatio = (risk > 0) ? (reward / risk) : 0;
   
   // Stretch rectangles across the entire timeline
   datetime t1 = 0; 
   datetime t2 = TimeCurrent() + (PeriodSeconds() * 1000); 
   
   // Risk Box Mapping
   ObjectSetDouble(0, boxRisk, OBJPROP_PRICE1, entry);
   ObjectSetInteger(0, boxRisk, OBJPROP_TIME1, t1);
   ObjectSetDouble(0, boxRisk, OBJPROP_PRICE2, sl);
   ObjectSetInteger(0, boxRisk, OBJPROP_TIME2, t2);
   
   // Reward Box Mapping
   ObjectSetDouble(0, boxReward, OBJPROP_PRICE1, entry);
   ObjectSetInteger(0, boxReward, OBJPROP_TIME1, t1);
   ObjectSetDouble(0, boxReward, OBJPROP_PRICE2, tp);
   ObjectSetInteger(0, boxReward, OBJPROP_TIME2, t2);
   
   // Text Updates
   string rrText = "Risk : Reward = 1 : " + DoubleToString(rrRatio, 2);
   ObjectSetString(0, lblStats, OBJPROP_TEXT, rrText);
   ObjectSetString(0, lblStatus, OBJPROP_TEXT, "Trade The Chart");
  }
//+------------------------------------------------------------------+
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Stop Looking at Your P&L While Trading

Post by PTScalper »

Hide your MetaTrader terminal entirely. Place your execution orders, load this onto the chart, and let the geometry dictate your decisions instead of the equity curve.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Stop Looking at Your P&L While Trading

Post by PTScalper »

Strict MQL4 requires a few specific compiler adaptations, particularly with how it handles timeline extensions and object properties under the #property strict directive.

This version is written natively for .mq4. When you drop this onto a 15-minute or 1-minute chart, it anchors the risk/reward boxes to the live price action, allowing you to drag your structural invalidation and target levels manually while keeping the math entirely detached from your MT4 terminal balance.

Code: Select all

//+------------------------------------------------------------------+
//|                                        TradePlanVisualizer.mq4   |
//|                                        Zero P&L Mindset Tracker  |
//+------------------------------------------------------------------+
#property copyright "Zero P&L"
#property strict
#property indicator_chart_window
#property indicator_plots 0

//--- Input parameters
input color InpEntryColor  = clrGray;          // Entry Line Color
input color InpSLColor     = clrRed;           // Stop Loss Line Color
input color InpTPColor     = clrLimeGreen;     // Take Profit Line Color
input color InpRiskColor   = clrCrimson;       // Risk Zone Color
input color InpRewardColor = clrMediumSeaGreen;// Reward Zone Color

//--- Global variables
string prefix = "ZeroPnL_";
string lineEntry = prefix + "Entry";
string lineSL    = prefix + "SL";
string lineTP    = prefix + "TP";
string boxRisk   = prefix + "RiskBox";
string boxReward = prefix + "RewardBox";
string lblStats  = prefix + "Stats";
string lblStatus = prefix + "Status";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   
   // Default separation of 200 points
   CreateHLine(lineTP, ask + 200 * point, InpTPColor);
   CreateHLine(lineEntry, ask, InpEntryColor);
   CreateHLine(lineSL, ask - 200 * point, InpSLColor);
   
   CreateRectangle(boxRisk, InpRiskColor);
   CreateRectangle(boxReward, InpRewardColor);
   
   CreateLabel(lblStats, 10, 40, clrWhite, 12);
   CreateLabel(lblStatus, 10, 20, clrYellow, 10);
   
   UpdateVisuals();
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0, prefix);
   ChartRedraw(0);
  }

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   // Recalculate and redraw the zones if any line is dragged
   if(id == CHARTEVENT_OBJECT_DRAG)
     {
      if(sparam == lineEntry || sparam == lineSL || sparam == lineTP)
        {
         UpdateVisuals();
         ChartRedraw(0);
        }
     }
  }

//+------------------------------------------------------------------+
//| 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[])
  {
   // Keeps the rectangles anchored to the far right as live ticks come in
   UpdateVisuals();
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| Helper Functions                                                 |
//+------------------------------------------------------------------+
void CreateHLine(string name, double price, color clr)
  {
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, true);
      ObjectSetInteger(0, name, OBJPROP_SELECTED, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, false);
     }
  }

void CreateRectangle(string name, color clr)
  {
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_RECTANGLE, 0, 0, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_BACK, true); // Keep behind candles
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
     }
  }

void CreateLabel(string name, int x, int y, color clr, int fontSize)
  {
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetString(0, name, OBJPROP_FONT, "Arial");
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
     }
  }

void UpdateVisuals()
  {
   double entry = ObjectGetDouble(0, lineEntry, OBJPROP_PRICE1);
   double sl    = ObjectGetDouble(0, lineSL, OBJPROP_PRICE1);
   double tp    = ObjectGetDouble(0, lineTP, OBJPROP_PRICE1);
   
   double risk   = MathAbs(entry - sl);
   double reward = MathAbs(tp - entry);
   double rrRatio = (risk > 0) ? (reward / risk) : 0;
   
   // Stretch rectangles across the timeline
   datetime t1 = 0; 
   // Extend right side into the future using current chart period
   datetime t2 = TimeCurrent() + (Period() * 60 * 100); 
   
   // Risk Box Mapping
   ObjectSetDouble(0, boxRisk, OBJPROP_PRICE1, entry);
   ObjectSetInteger(0, boxRisk, OBJPROP_TIME1, t1);
   ObjectSetDouble(0, boxRisk, OBJPROP_PRICE2, sl);
   ObjectSetInteger(0, boxRisk, OBJPROP_TIME2, t2);
   
   // Reward Box Mapping
   ObjectSetDouble(0, boxReward, OBJPROP_PRICE1, entry);
   ObjectSetInteger(0, boxReward, OBJPROP_TIME1, t1);
   ObjectSetDouble(0, boxReward, OBJPROP_PRICE2, tp);
   ObjectSetInteger(0, boxReward, OBJPROP_TIME2, t2);
   
   // Text Updates
   string rrText = "Risk : Reward = 1 : " + DoubleToString(rrRatio, 2);
   ObjectSetString(0, lblStats, OBJPROP_TEXT, rrText);
   ObjectSetString(0, lblStatus, OBJPROP_TEXT, "Trade The Chart");
  }
//+------------------------------------------------------------------+
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Stop Looking at Your P&L While Trading

Post by PTScalper »

To use it in MT4, just drop it onto your chart, double-click the horizontal lines to unlock them, and drag them to your price action setups. As long as you keep your terminal hidden (Ctrl+T), you'll only see the geometry of the trade.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Stop Looking at Your P&L While Trading

Post by PTScalper »

For a native MQL5 environment, the object property handling is stricter than MQL4, particularly regarding how background fills are rendered and how object coordinate indexes (anchor points) are assigned.

This version is written strictly for .mq5. It utilizes MQL5’s OBJPROP_FILL to properly shade the risk/reward zones and maps the time/price coordinates using precise anchor indexing (0 and 1).

Code: Select all

//+------------------------------------------------------------------+
//|                                        TradePlanVisualizer.mq5   |
//|                                        Zero P&L Mindset Tracker  |
//+------------------------------------------------------------------+
#property copyright "Zero P&L"
#property indicator_chart_window
#property indicator_plots 0

//--- Input parameters
input color InpEntryColor  = clrGray;          // Entry Line Color
input color InpSLColor     = clrRed;           // Stop Loss Line Color
input color InpTPColor     = clrLimeGreen;     // Take Profit Line Color
input color InpRiskColor   = clrCrimson;       // Risk Zone Color
input color InpRewardColor = clrMediumSeaGreen;// Reward Zone Color

//--- Global variables
string prefix = "ZeroPnL_";
string lineEntry = prefix + "Entry";
string lineSL    = prefix + "SL";
string lineTP    = prefix + "TP";
string boxRisk   = prefix + "RiskBox";
string boxReward = prefix + "RewardBox";
string lblStats  = prefix + "Stats";
string lblStatus = prefix + "Status";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   
   // Default separation of 200 points to make the lines visible on load
   CreateHLine(lineTP, ask + 200 * point, InpTPColor);
   CreateHLine(lineEntry, ask, InpEntryColor);
   CreateHLine(lineSL, ask - 200 * point, InpSLColor);
   
   CreateRectangle(boxRisk, InpRiskColor);
   CreateRectangle(boxReward, InpRewardColor);
   
   CreateLabel(lblStats, 10, 40, clrWhite, 12);
   CreateLabel(lblStatus, 10, 20, clrYellow, 10);
   
   UpdateVisuals();
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0, prefix);
   ChartRedraw(0);
  }

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   // Recalculate and redraw the zones if any line is dragged by the user
   if(id == CHARTEVENT_OBJECT_DRAG)
     {
      if(sparam == lineEntry || sparam == lineSL || sparam == lineTP)
        {
         UpdateVisuals();
         ChartRedraw(0);
        }
     }
  }

//+------------------------------------------------------------------+
//| 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[])
  {
   // Keeps the visual rectangles stretched to the far right as live ticks come in
   UpdateVisuals();
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| Helper Functions                                                 |
//+------------------------------------------------------------------+
void CreateHLine(string name, double price, color clr)
  {
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, true);
      ObjectSetInteger(0, name, OBJPROP_SELECTED, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, false);
     }
  }

void CreateRectangle(string name, color clr)
  {
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_RECTANGLE, 0, 0, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_BACK, true); // Render behind candles
      ObjectSetInteger(0, name, OBJPROP_FILL, true); // Required in MQL5 for solid coloring
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
     }
  }

void CreateLabel(string name, int x, int y, color clr, int fontSize)
  {
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetString(0, name, OBJPROP_FONT, "Arial");
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
     }
  }

void UpdateVisuals()
  {
   // MQL5 requires property modifiers (0) to access the specific anchor point prices
   double entry = ObjectGetDouble(0, lineEntry, OBJPROP_PRICE, 0);
   double sl    = ObjectGetDouble(0, lineSL, OBJPROP_PRICE, 0);
   double tp    = ObjectGetDouble(0, lineTP, OBJPROP_PRICE, 0);
   
   double risk   = MathAbs(entry - sl);
   double reward = MathAbs(tp - entry);
   double rrRatio = (risk > 0) ? (reward / risk) : 0;
   
   // Stretch rectangles across the timeline
   datetime t1 = 0; 
   // Extend right side into the future using the current chart's period in seconds
   datetime t2 = TimeCurrent() + (PeriodSeconds(_Period) * 100); 
   
   // Risk Box Mapping (Anchor 0 and Anchor 1)
   ObjectSetDouble(0, boxRisk, OBJPROP_PRICE, 0, entry);
   ObjectSetInteger(0, boxRisk, OBJPROP_TIME, 0, t1);
   ObjectSetDouble(0, boxRisk, OBJPROP_PRICE, 1, sl);
   ObjectSetInteger(0, boxRisk, OBJPROP_TIME, 1, t2);
   
   // Reward Box Mapping (Anchor 0 and Anchor 1)
   ObjectSetDouble(0, boxReward, OBJPROP_PRICE, 0, entry);
   ObjectSetInteger(0, boxReward, OBJPROP_TIME, 0, t1);
   ObjectSetDouble(0, boxReward, OBJPROP_PRICE, 1, tp);
   ObjectSetInteger(0, boxReward, OBJPROP_TIME, 1, t2);
   
   // Text Updates
   string rrText = "Risk : Reward = 1 : " + DoubleToString(rrRatio, 2);
   ObjectSetString(0, lblStats, OBJPROP_TEXT, rrText);
   ObjectSetString(0, lblStatus, OBJPROP_TEXT, "Trade The Chart");
  }
//+------------------------------------------------------------------+
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Stop Looking at Your P&L While Trading

Post by PTScalper »

Since you have an extensive background in C# and .NET engineering, you’ll appreciate how elegantly cTrader handles this compared to MetaTrader.

cTrader’s cAlgo.API provides a much cleaner, event-driven object model. Instead of constantly redrawing objects on every tick like in MQL, we can simply subscribe to Chart.ObjectUpdated to listen for user drag events and handle the rendering mathematically.

Here is the native C# custom indicator for cTrader.

Code: Select all

using System;
using cAlgo.API;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ZeroPnLVisualizer : Indicator
    {
        // Parameter definitions using standard Color names
        [Parameter("Entry Color", DefaultValue = "Gray")]
        public string EntryColor { get; set; }

        [Parameter("Stop Loss Color", DefaultValue = "Red")]
        public string SlColor { get; set; }

        [Parameter("Take Profit Color", DefaultValue = "LimeGreen")]
        public string TpColor { get; set; }

        [Parameter("Risk Color", DefaultValue = "Crimson")]
        public string RiskColor { get; set; }

        [Parameter("Reward Color", DefaultValue = "MediumSeaGreen")]
        public string RewardColor { get; set; }

        [Parameter("Zone Opacity (0-255)", DefaultValue = 60)]
        public int ZoneOpacity { get; set; }

        // Object references
        private ChartHorizontalLine _entryLine;
        private ChartHorizontalLine _slLine;
        private ChartHorizontalLine _tpLine;

        protected override void Initialize()
        {
            double ask = Symbol.Ask;
            double defaultDistance = Symbol.PipSize * 20;

            // Parse colors
            Color clrEntry = Color.FromName(EntryColor);
            Color clrSl = Color.FromName(SlColor);
            Color clrTp = Color.FromName(TpColor);

            // 1. Create the interactive horizontal lines
            _entryLine = Chart.DrawHorizontalLine("ZeroPnL_Entry", ask, clrEntry, 2, LineStyle.Solid);
            _entryLine.IsInteractive = true;

            _slLine = Chart.DrawHorizontalLine("ZeroPnL_SL", ask - defaultDistance, clrSl, 2, LineStyle.Solid);
            _slLine.IsInteractive = true;

            _tpLine = Chart.DrawHorizontalLine("ZeroPnL_TP", ask + defaultDistance, clrTp, 2, LineStyle.Solid);
            _tpLine.IsInteractive = true;

            // 2. Subscribe to the native dragging event
            Chart.ObjectUpdated += OnChartObjectUpdated;
            
            // 3. Initial draw
            UpdateVisuals();
        }

        public override void Calculate(int index)
        {
            // Keep zones stretched seamlessly into the future as new live bars arrive
            if (IsLastBar)
            {
                UpdateVisuals();
            }
        }

        private void OnChartObjectUpdated(ChartObjectUpdatedEventArgs args)
        {
            // Recalculate geometries only when one of our specific lines is moved
            if (args.ChartObject.Name.StartsWith("ZeroPnL_"))
            {
                UpdateVisuals();
            }
        }

        private void UpdateVisuals()
        {
            if (_entryLine == null || _slLine == null || _tpLine == null)
                return;

            double entry = _entryLine.Y;
            double sl = _slLine.Y;
            double tp = _tpLine.Y;

            double risk = Math.Abs(entry - sl);
            double reward = Math.Abs(tp - entry);
            double rrRatio = risk > 0 ? Math.Round(reward / risk, 2) : 0;

            // Compute background fills using ARGB transparency
            Color riskZoneClr = Color.FromArgb(ZoneOpacity, Color.FromName(RiskColor));
            Color rewardZoneClr = Color.FromArgb(ZoneOpacity, Color.FromName(RewardColor));

            // Use Bar Indices rather than DateTime to seamlessly cover weekend gaps
            int startIndex = 0;
            int endIndex = Bars.Count + 500;

            // Map Risk Rectangle
            var riskBox = Chart.DrawRectangle("ZeroPnL_RiskBox", startIndex, entry, endIndex, sl, riskZoneClr);
            riskBox.IsFilled = true;
            riskBox.IsInteractive = false; // Prevent user from accidentally dragging the background

            // Map Reward Rectangle
            var rewardBox = Chart.DrawRectangle("ZeroPnL_RewardBox", startIndex, entry, endIndex, tp, rewardZoneClr);
            rewardBox.IsFilled = true;
            rewardBox.IsInteractive = false;

            // Draw clean HUD anchored to the UI layout, ignoring chart scrolling
            string statsText = $"Risk : Reward = 1 : {rrRatio:F2}\nTrade The Chart";
            Chart.DrawStaticText("ZeroPnL_Stats", statsText, VerticalAlignment.Bottom, HorizontalAlignment.Right, Color.White);
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply