Page 2 of 2

Re: Daily Loss Limits Save Careers

Posted: Sat Sep 26, 2026 2:47 pm
by FTtrader
Translating this to TradingView’s Pine Script requires an important shift in how we think about the code.

Unlike cTrader (which manages your actual broker account directly), Pine Script is sandboxed. A Pine Script strategy cannot close manual trades you place yourself via your broker's UI. It can only manage and liquidate the trades that the script itself generates.

Because of this, the most professional way to build this in Pine Script is to use TradingView's built-in strategy.risk engine. I have wrapped this inside a template that includes the Pro On-Chart HUD and Alerts, so you can seamlessly drop your own entry/exit conditions into it.

Re: Daily Loss Limits Save Careers

Posted: Sat Sep 26, 2026 2:47 pm
by FTtrader
Here is the Pine Script v5 version:

Code: Select all

//@version=5
strategy("Pro Daily Risk Manager [HUD]", overlay=true, calc_on_every_tick=true, initial_capital=10000)

// ==============================================================================
// 1. PARAMETERS
// ==============================================================================
maxLossPct = input.float(3.0, title="Max Daily Loss (%)", minval=0.1, step=0.1, group="Risk Limits")
showHud    = input.bool(true, title="Show On-Chart HUD", group="User Interface")

// ==============================================================================
// 2. THE KILL SWITCH (NATIVE PINE SCRIPT ENGINE)
// ==============================================================================
// This single line automatically handles everything. If the script's equity drops 
// by the specified percentage from the start of the day, it instantly liquidates 
// all open positions, cancels pending orders, and locks out new entries until tomorrow.
strategy.risk.max_intraday_loss(maxLossPct, strategy.percent_of_equity)

// ==============================================================================
// 3. TRACKING LOGIC (FOR THE HUD AND ALERTS)
// ==============================================================================
var float startOfDayEquity = na
var bool isLimitHit = false

// Detect when a new daily session begins
isNewDay = ta.change(time("D")) != 0

// Current Equity = Starting Capital + Realized PnL + Floating PnL
currentEquity = strategy.initial_capital + strategy.netprofit + strategy.openprofit

// Reset tracking variables at the start of each day
if isNewDay
    startOfDayEquity := currentEquity
    isLimitHit := false

// Calculate the day's performance
dailyPnL = currentEquity - startOfDayEquity
dailyPnLPct = na(startOfDayEquity) or startOfDayEquity == 0 ? 0 : (dailyPnL / startOfDayEquity) * 100

if dailyPnLPct <= -maxLossPct
    isLimitHit := true

// ==============================================================================
// 4. ALERTS
// ==============================================================================
// Send a webhook/email/app notification the exact moment the limit is breached
if isLimitHit and not isLimitHit[1]
    alert("DAILY LOSS CAP REACHED. All strategy positions closed and locked for the day.", alert.freq_once_per_bar)

// ==============================================================================
// 5. PRO ON-CHART HUD (DASHBOARD)
// ==============================================================================
var table riskTable = table.new(position.top_right, 2, 4, border_width=1, border_color=color.gray)

if showHud and barstate.islast
    // Start Balance Row
    table.cell(riskTable, 0, 0, "Start Balance:", text_color=color.white, bgcolor=color.new(color.black, 20))
    table.cell(riskTable, 1, 0, "$" + str.tostring(startOfDayEquity, "#.##"), text_color=color.white, bgcolor=color.new(color.black, 20))
    
    // Daily PnL Row
    table.cell(riskTable, 0, 1, "Daily PnL:", text_color=color.white, bgcolor=color.new(color.black, 20))
    table.cell(riskTable, 1, 1, "$" + str.tostring(dailyPnL, "#.##"), text_color=dailyPnL < 0 ? color.red : color.green, bgcolor=color.new(color.black, 20))
    
    // Drawdown Row
    table.cell(riskTable, 0, 2, "Drawdown:", text_color=color.white, bgcolor=color.new(color.black, 20))
    table.cell(riskTable, 1, 2, str.tostring(dailyPnLPct, "#.##") + "% / -" + str.tostring(maxLossPct) + "%", text_color=dailyPnLPct <= -maxLossPct ? color.red : color.green, bgcolor=color.new(color.black, 20))
    
    // Status Row
    table.cell(riskTable, 0, 3, "Status:", text_color=color.white, bgcolor=color.new(color.black, 20))
    table.cell(riskTable, 1, 3, isLimitHit ? "LOCKED" : "ACTIVE", text_color=isLimitHit ? color.red : color.green, bgcolor=color.new(color.black, 20))

// ==============================================================================
// 6. YOUR STRATEGY ENTRIES GO HERE
// ==============================================================================
// Example: basic moving average crossover to show how the script executes trades.
// Replace this with your actual edge/scalping logic.

// fastMA = ta.sma(close, 9)
// slowMA = ta.sma(close, 21)
//
// if ta.crossover(fastMA, slowMA)
//     strategy.entry("Long", strategy.long)
//
// if ta.crossunder(fastMA, slowMA)
//     strategy.close("Long")

Re: Daily Loss Limits Save Careers

Posted: Sat Sep 26, 2026 2:48 pm
by FTtrader
Key differences to keep in mind for Pine Script:

The Magic Line: The entire mechanical process of closing positions and stopping trades is handled under the hood by strategy.risk.max_intraday_loss(). You do not need to write strategy.close_all() manually; TradingView's engine takes over the second that percentage is hit.

Calc On Every Tick: I added calc_on_every_tick=true in the strategy declaration. This is critical for scalping. Without it, Pine Script only checks your PnL at the close of a candle. If you are trading a 5-minute chart, your loss could blow way past 3% before the candle closes. This setting ensures the kill switch fires mid-candle if necessary.

Webhook Automation: If you want this to enforce limits on an external exchange (like Bybit or Binance), you will configure the TradingView Alert dialog to send a webhook to your broker/bridge (like PineConnector) using the {{strategy.order.alert_message}} placeholders.

Re: Daily Loss Limits Save Careers

Posted: Sat Sep 26, 2026 2:48 pm
by FTtrader
For MetaTrader 5 (MQL5), we build this as an Expert Advisor (EA).

Unlike TradingView, MT5 can directly manage and close any open positions or pending orders on your account, regardless of whether they were opened manually or by another EA.

Here is a professional-grade MQL5 Risk Manager. It includes mid-day balance recovery (if you attach it after the market has already been open), automatic liquidation of all open positions, cancellation of all pending orders, and a live on-chart dashboard.

Re: Daily Loss Limits Save Careers

Posted: Sat Sep 26, 2026 2:49 pm
by FTtrader
How to use this script in MT5:

1.) Open MT5 and press F4 to open MetaEditor.

2.) Click New -> Expert Advisor (template), name it ProDailyRiskManager, and click finish.

3.) Replace all the generated code with the script below.

4.) Click Compile (the green checkmark icon).

5.) Attach it to any single chart in MT5, ensure Algo Trading is enabled in the platform toolbar, and set your parameters.

Code: Select all

//+------------------------------------------------------------------+
//|                                        ProDailyRiskManager.mq5   |
//|                                  Copyright 2026, Personal AI     |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026"
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#include <Trade\Trade.mqh>
CTrade trade;

//--- Inputs ---
input group "=== Risk Parameters ==="
input double   InpMaxDailyLossPct   = 3.0;      // Max Daily Loss (%)
input bool     InpCancelPending     = true;     // Cancel Pending Orders on Breach

input group "=== User Interface ==="
input bool     InpShowHUD           = true;     // Show On-Chart Dashboard

//--- Global Variables ---
datetime       g_currentDay;
double         g_startOfDayEquity = 0.0;
bool           g_limitHit = false;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   InitializeDay();
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // Clean up chart comment on exit
   Comment("");
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   MqlDateTime dt;
   TimeToStruct(TimeCurrent(), dt);
   datetime todayDate = StringToTime(IntegerToString(dt.year) + "." + IntegerToString(dt.mon) + "." + IntegerToString(dt.day));

   // Check for new trading day roll-over
   if(todayDate != g_currentDay)
   {
      InitializeDay();
   }

   if(g_limitHit) return;

   // Calculate current equity metrics
   double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
   double dailyPnL = currentEquity - g_startOfDayEquity;
   double dailyPnLPct = (g_startOfDayEquity > 0) ? (dailyPnL / g_startOfDayEquity) * 100.0 : 0.0;

   // Update On-Chart HUD
   UpdateHUD(dailyPnL, dailyPnLPct);

   // Check if the loss cap has been breached
   if(dailyPnLPct <= -InpMaxDailyLossPct)
   {
      ExecuteKillSwitch(dailyPnLPct);
   }
}

//+------------------------------------------------------------------+
//| Initialize or Reset Day Tracking (Handles mid-day reloads)       |
//+------------------------------------------------------------------+
void InitializeDay()
{
   MqlDateTime dt;
   TimeToStruct(TimeCurrent(), dt);
   g_currentDay = StringToTime(IntegerToString(dt.year) + "." + IntegerToString(dt.mon) + "." + IntegerToString(dt.day));
   
   // To account for mid-day restarts, we calculate today's realized profit from history
   double todayRealizedPnL = 0.0;
   
   if(HistoryDealSelectByTime(g_currentDay, TimeCurrent()))
   {
      int totalDeals = HistoryDealsTotal();
      for(int i = 0; i < totalDeals; i++)
      {
         ulong ticket = HistoryDealGetTicket(i);
         if(ticket > 0)
         {
            long entryType = HistoryDealGetInteger(ticket, DEAL_ENTRY);
            // Count out deals or simple profit summation for the day
            if(entryType == DEAL_ENTRY_OUT || entryType == DEAL_ENTRY_INOUT)
            {
               todayRealizedPnL += HistoryDealGetDouble(ticket, DEAL_PROFIT) + 
                                   HistoryDealGetDouble(ticket, DEAL_SWAP) + 
                                   HistoryDealGetDouble(ticket, DEAL_COMMISSION);
            }
         }
      }
   }

   // True start of day equity = Current Equity minus what we've already made/lost today
   double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
   g_startOfDayEquity = currentEquity - todayRealizedPnL;
   g_limitHit = false;
   
   Print("Risk Manager Initialized. True Start-of-Day Equity: ", g_startOfDayEquity);
}

//+------------------------------------------------------------------+
//| Execute Kill Switch (Liquidate & Lock)                           |
//+------------------------------------------------------------------+
void ExecuteKillSwitch(double hitPercentage)
{
   g_limitHit = true;
   Print("KILL SWITCH TRIGGERED! Max daily loss hit: ", DoubleToString(hitPercentage, 2), "%");

   // 1. Cancel all pending orders
   if(InpCancelPending)
   {
      int totalOrders = OrdersTotal();
      for(int i = totalOrders - 1; i >= 0; i--)
      {
         ulong orderTicket = OrderGetTicket(i);
         if(orderTicket > 0)
         {
            trade.OrderDelete(orderTicket);
         }
      }
   }

   // 2. Close all open positions across all symbols
   int totalPositions = PositionsTotal();
   for(int i = totalPositions - 1; i >= 0; i--)
   {
      ulong positionTicket = PositionGetTicket(i);
      if(positionTicket > 0)
      {
         string symbol = PositionGetString(POSITION_SYMBOL);
         trade.PositionClose(symbol);
      }
   }

   // 3. Put hard warning message on screen
   Comment("\n\n--------------------------------------------\n",
           "=== DAILY LOSS CAP REACHED! ==Y=\n",
           "TRADING LOCKED FOR THE DAY.\n",
           "--------------------------------------------");
}

//+------------------------------------------------------------------+
//| Update On-Chart HUD                                              |
//+------------------------------------------------------------------+
void UpdateHUD(double dailyPnL, double percentage)
{
   if(!InpShowHUD) return;

   string status = g_limitHit ? "LOCKED" : "ACTIVE";
   string hudText = "=== PRO RISK DASHBOARD ===\n" +
                    "Start Equity: " + DoubleToString(g_startOfDayEquity, 2) + "\n" +
                    "Daily Net PnL: " + DoubleToString(dailyPnL, 2) + "\n" +
                    "Drawdown: " + DoubleToString(percentage, 2) + "% / -" + DoubleToString(InpMaxDailyLossPct, 2) + "%\n" +
                    "Status: " + status;

   Comment(hudText);
}
//+------------------------------------------------------------------+

Re: Daily Loss Limits Save Careers

Posted: Sat Sep 26, 2026 2:49 pm
by FTtrader
Key Differences & Pro Features Built Into This MT5 Version:

Mid-Day Protection: If you open MT5 or restart your terminal at 1:00 PM, a basic script would think your balance at that moment is the start-of-day balance. This script queries MT5's deal history for the current day and mathematically reverse-calculates your true 00:00 equity, preventing you from bypassing your cap by restarting the platform.

Global Account Monitoring: It loops through PositionsTotal() and OrdersTotal(), meaning it manages positions across all active charts and symbols on your account simultaneously.

Live HUD via Comments: It prints a clean metrics panel in the top-left corner of your chart showing your starting equity, live PnL, and current drawdown percentage in real-time.