Prop Firm EA Rules in Code: Daily Loss, Max Drawdown and News Windows

A prop firm Expert Advisor has to enforce the firm’s limits itself, before every order and continuously while positions are open: a daily floor set at the firm’s reset time, a maximum-loss floor that is either static or trailing, a counter for trading days and a block around restricted news. FTMO checks both loss limits against equity, including open positions, swaps and commissions, so a stop-loss alone does not protect the account. As of November 3, 2026, FTMO, FundedNext and The5ers permit EAs under conditions on their own pages, but they calculate the daily limit differently, and the reset clock is not always the MetaTrader server clock.

Which prop firms allow EAs, according to their own pages?

The table summarizes the EA rules three firms publish on their own sites, retrieved on November 3, 2026; it is not a ranking. Rules change, so check the current page for your exact program, account size and platform before any code trades on it.

Firm (as of November 3, 2026)EA position on its own pageConditions that matter for code
FTMOEAs are not restricted if trading is legitimate and avoids its Forbidden Trading PracticesServer limits of 200 orders at a time and 2,000 positions per day; over 2,000 server requests per day counts as hyperactivity; third-party EAs risk the capital allocation rule
FundedNextEAs on MT4 and MT5 only for accounts below $50,000, with a paid EA add-on; none on cTrader or Match-TraderTools that only change SL, TP or lot size count as EAs; EAs designed to pass challenges are prohibited; cap of $300,000 per EA strategy
The5ersAny EA, unless it copies signals, tick-scalps, runs latency, reverse or hedge arbitrage, high-frequency trading or emulatorsThe trader must own the EA’s source code; stop-losses must be visible, so no “stealth” stops

Two consequences follow. Under FundedNext’s definition, the guard itself counts as an EA. And because firms prohibit EAs built to pass the evaluation, this article treats firm limits as risk controls an EA must never cross, not as targets to optimize toward. Martingale and grid logic are not risk control: they add exposure after losses. To have rule guards like these specified, built and tested, see trading bot development for MetaTrader 5 and Interactive Brokers.

How is the daily loss limit calculated: balance, equity or the higher of both?

Every daily rule has four parts a guard needs in writing: the reference (balance, equity or the higher of both), the amount (a percentage of what), the reset clock and what counts as a loss. The published versions differ in exactly these parts:

  • FTMO: the balance recorded at 00:00 CE(S)T minus 5% (2-Step) or 3% (1-Step and Instant) of initial capital. Equity, meaning balance plus open P/L, plus or minus swaps, minus commissions, must not drop below it.
  • FundedNext: a fixed amount, for example 5% of the initial balance on a Stellar 2-Step account, against the day’s closed results plus open positions, including swaps, commissions and fees. Reset at 00:00 server time, GMT+3 during daylight saving time and GMT+2 otherwise.
  • The5ers: the 2-Step plan uses 3% of the previous day’s closing balance or equity, whichever is higher; the daily pause on other programs also starts from the higher of the two, at 00:00 GMT+3.

Worked example: initial capital $100,000, balance $101,000 at the reset and one open position at +$1,500, so equity is $102,500.

Rule as publishedCalculationDaily floor
FTMO 2-Step: balance at reset minus 5% of initial capital101,000 − 5,000$96,000
FundedNext Stellar 2-Step: 5% of initial balance101,000 − 5,000$96,000
The5ers 2-Step: 3% of the higher of balance and equity102,500 × 0.97$99,425

Under a balance reference, open profit at the reset is not protected: if the position falls back to zero, no daily loss is counted. Under the higher-of reference, the same reversal uses $1,500 of a $3,075 allowance. Conversely, FundedNext’s own example shows a trade carried past midnight at a $6,000 floating loss breaching the $5,000 limit of the new day.

Static, end-of-day trailing or intraday trailing: which max drawdown applies?

FTMO’s Trading Objectives page shows three maximum-loss models, each 10% of initial capital and each checked against equity:

FTMO program (as of November 3, 2026)ModelFloorExample on FTMO’s page
2-StepStaticInitial capital minus 10%$100,000 account: $90,000, fixed
1-StepEnd-of-day trailingHighest balance at 00:00 CE(S)T, or initial capital if higher, minus 10% of initial capitalMidnight balances of $104,000, then $103,000: floor $94,000 on both days
InstantIntraday trailing on closed balanceHighest balance after a closed trade, or initial capital, minus 10% of initial capital$50,000 account: $45,000, then $45,600 after a close at $50,600, unchanged after a close at $50,300

Both trailing variants follow balance, so open profit does not move the floor, while ending a day higher (1-Step) or closing a winner (Instant) does. Payouts differ too: the 1-Step limit resets with the new FTMO Account after a Reward withdrawal, while the Instant limit carries over. The peak has to survive a restart: re-initializing it from the current balance after a VPS reboot silently lowers the floor, so rebuild it from the deal history or store it persistently.

Which clock does the reset follow, and what happens to open positions?

FTMO monitors the daily limit by Prague time, while its MetaTrader platform runs on GMT+2 or GMT+3 and, according to its trading updates, follows the US clock change. Normally Prague midnight is 01:00 platform time. Between the US and European clock changes the gap is 2 hours: FTMO’s update of October 22, 2026 named the week from October 26 to November 1, 2026, and its March update the period starting March 8, 2026. A guard that hard-codes “midnight server time” is off by one hour every day, and by two in those weeks.

According to the MQL5 reference, TimeCurrent() returns the time of the last received quote, TimeTradeServer() estimates server time on the client, and TimeGMT() derives GMT from the local computer clock. In the Strategy Tester, TimeGMT() equals simulated server time, so a GMT-based reset that is right live is shifted by the server’s UTC offset in every backtest.

Open positions need a decision at the reset (see the carry-over effect above), before market breaks and before restricted news. On FTMO Accounts and Instant Accounts, positions must be closed before market breaks longer than 2 hours unless the account is a Swing type; Challenge and Verification are exempt. FTMO’s Forbidden Trading Practices also describe opening trades two hours or less before a market closes for at least two hours as gap trading, relevant for late-Friday entries.

How do you turn firm rules into hard limits in an EA?

A rule guard works in layers:

  1. Pre-trade check: the worst-case loss of the new position (stop distance in ticks times tick value times volume, plus costs) and the open risk of all other positions must leave equity above the higher floor plus a buffer. Size the volume from that budget and round it down to the volume step.
  2. Continuous equity check: on a timer as well as on ticks, because OnTick() only fires for the chart’s symbol. Inside the buffer, block new orders; closer to the floor, delete pending orders, close positions and lock trading until the next reset.
  3. Persistent state: the lock, the balance at the reset and any trailing peak must survive restarts. Rebuild them from the deal history where possible; terminal global variables are deleted after four weeks without use.
  4. Request budget: count order sends and modifications per day. A trailing stop modified on every tick can reach FTMO’s threshold of 2,000 server requests per day on its own.
  5. Rounding: calculate in account currency, round the floor up and the volume down, and compare against a buffer, never for equality.

The Python model below implements the formulas and asserts the firms’ published worked examples. It runs with Python 3.9 and the standard library against a stubbed account object: no broker connection, no orders.

import math
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo  # standard library since 3.9; Windows may need the tzdata package

PRAGUE = ZoneInfo("Europe/Prague")      # FTMO resets at 00:00 CE(S)T
NEW_YORK = ZoneInfo("America/New_York")  # only used to model GMT+2/GMT+3 platform time

def reset_utc(now_utc, tz=PRAGUE):
    local = now_utc.astimezone(tz)
    return local.replace(hour=0, minute=0, second=0, microsecond=0).astimezone(timezone.utc)

def platform_time(t_utc):  # assumption: GMT+3 while US daylight saving time applies, else GMT+2
    return t_utc + timedelta(hours=2) + t_utc.astimezone(NEW_YORK).dst()

def daily_floor(model, initial, pct, reset_balance, reset_equity):
    if model == "pct_of_initial":  # balance at reset minus pct of initial capital
        return reset_balance - initial * pct / 100
    if model == "higher_of":       # pct of the higher of balance and equity at reset
        return max(reset_balance, reset_equity) * (1 - pct / 100)
    raise ValueError(model)

def max_loss_floor(model, initial, pct, peak=0.0):  # peak: balance at reset or after closes
    base = initial if model == "static" else max(initial, peak)
    return base - initial * pct / 100

def allowed_lots(equity, floor, buffer, stop_ticks, tick_value, cost_per_lot, step, min_lot):
    budget = equity - floor - buffer
    loss_per_lot = stop_ticks * tick_value + cost_per_lot
    if budget <= 0 or loss_per_lot <= 0:
        return 0.0
    lots = math.floor(budget / loss_per_lot / step + 1e-9) * step  # round down to the step
    return round(lots, 8) if lots >= min_lot else 0.0

class StubAccount:  # replaces the broker connection: no network, no orders
    balance, equity = 101_000.0, 96_400.0

def utc(*a):
    return datetime(*a, tzinfo=timezone.utc)

# Worked examples from the firms' own pages
assert daily_floor("pct_of_initial", 100_000, 5, 102_000, 102_000) == 97_000    # FTMO 2-Step
assert max_loss_floor("eod_trailing", 100_000, 10, peak=104_000) == 94_000      # FTMO 1-Step
assert max_loss_floor("balance_trailing", 50_000, 10, peak=50_600) == 45_600    # FTMO Instant
assert round(daily_floor("higher_of", 10_000, 3, 10_500, 10_400), 2) == 10_185  # The5ers pause
# Reset in platform time: 01:00 normally, 02:00 in the week from October 26, 2026
assert platform_time(reset_utc(utc(2026, 11, 3, 12))).hour == 1
assert platform_time(reset_utc(utc(2026, 10, 28, 12))).hour == 2

acct = StubAccount()
floor = max(daily_floor("pct_of_initial", 100_000, 5, acct.balance, acct.equity),
            max_loss_floor("static", 100_000, 10))
print(floor, allowed_lots(acct.equity, floor, 1_000, 200, 1.0, 7.0, 0.01, 0.01))

The last line prints 96000.0 0.0: equity of $96,400 sits inside the $1,000 buffer above the $96,000 daily floor, so no new volume is allowed.

An MQL5 equity guard skeleton

The same logic in MQL5, reduced to a daily floor with a balance reference and a static maximum loss. It is illustrative and was not compiled for this article; check it against the MQL5 reference before use.

// Illustrative skeleton, not compiled: check against the MQL5 reference before use.
#include <Trade\Trade.mqh>

input double InpInitial   = 100000.0; // initial capital of the program
input double InpDailyPct  = 5.0;      // daily loss amount, % of initial capital
input double InpMaxPct    = 10.0;     // static maximum loss, % of initial capital
input double InpBuffer    = 1000.0;   // own safety margin in account currency
input int    InpResetHour = 1;        // firm reset in platform time (check clock-change weeks)

CTrade trade;

datetime ResetTime()                  // last firm reset, in platform time
{
   datetime t = TimeTradeServer() - InpResetHour * 3600;
   return t - (t % 86400) + InpResetHour * 3600;
}

double BalanceAt(datetime from)       // balance now minus every trading deal since 'from'
{
   double bal = AccountInfoDouble(ACCOUNT_BALANCE);
   if(!HistorySelect(from, TimeTradeServer() + 60)) return -1.0;
   for(int i = HistoryDealsTotal() - 1; i >= 0; i--)
   {
      ulong d = HistoryDealGetTicket(i);
      if(HistoryDealGetInteger(d, DEAL_TYPE) == DEAL_TYPE_BALANCE) continue; // deposit or payout
      bal -= HistoryDealGetDouble(d, DEAL_PROFIT) + HistoryDealGetDouble(d, DEAL_SWAP)
           + HistoryDealGetDouble(d, DEAL_COMMISSION) + HistoryDealGetDouble(d, DEAL_FEE);
   }
   return bal;
}

double Floor()                        // cache this in production; HistorySelect is not free
{
   double day_balance = BalanceAt(ResetTime());
   if(day_balance < 0) return -1.0;    // history unavailable
   double daily = day_balance - InpInitial * InpDailyPct / 100.0;
   double maxl  = InpInitial * (1.0 - InpMaxPct / 100.0);
   return MathMax(daily, maxl);
}

string LockName() { return "guard_lock_" + IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN)); }
bool   Locked()   { return GlobalVariableGet(LockName()) >= (double)ResetTime(); }

void FlattenAndLock()                 // whole account, not only this EA's positions
{
   GlobalVariableSet(LockName(), (double)ResetTime());
   for(int i = OrdersTotal() - 1; i >= 0; i--)    trade.OrderDelete(OrderGetTicket(i));
   for(int i = PositionsTotal() - 1; i >= 0; i--) trade.PositionClose(PositionGetTicket(i));
}

bool MayOpen(double worst_case_loss)  // call before every order; fails closed
{
   double f = Floor();
   return f > 0 && !Locked() && AccountInfoDouble(ACCOUNT_EQUITY) - worst_case_loss > f + InpBuffer;
}

void CheckGuard()
{
   double f = Floor();
   if(f < 0) return;                  // no history: MayOpen() already blocks new orders
   // while locked, this retries every second if anything is open: throttle and count these requests
   if(Locked() || AccountInfoDouble(ACCOUNT_EQUITY) <= f + InpBuffer / 2.0)
      FlattenAndLock();
}

int  OnInit()                  { EventSetTimer(1); return INIT_SUCCEEDED; }
void OnDeinit(const int reason) { EventKillTimer(); }
void OnTimer()                 { CheckGuard(); }
void OnTick()                  { CheckGuard(); }

FlattenAndLock() acts on every position, because the firm’s limit applies to the whole account. Rebuilding the reset balance from the deal history keeps the floor stable across restarts; skipping balance operations makes the first-day reference the initial capital, as FTMO specifies. The lock is keyed to the reset time and releases at the next reset. If the history cannot be read, the guard blocks new orders without closing anything.

How do you code news windows and minimum trading days?

FTMO’s news restriction covers FTMO Accounts and Instant Accounts, not Challenge, Verification or Swing accounts, although its Forbidden Trading Practices still count opening trades when major news is scheduled as gap trading. From 2 minutes before to 2 minutes after a restricted release, no order may execute on affected instruments, explicitly including stop-loss, take-profit and pending orders; positions opened more than 2 minutes earlier may stay open. Instruments are mapped by currency: US releases affect forex pairs with USD, gold, the US indices and DXY. A filter on the string “USD” therefore misses US500.cash; the mapping belongs in configuration. The5ers’ High Stakes page states a similar 2-minute rule for high-impact news.

MetaTrader 5’s built-in economic calendar works in trade server time, but according to the MQL5 book its functions cannot be used in the Strategy Tester (error 4014), so backtests need a recorded calendar file. Per the same chapter, recorded times need correcting for periods with a different DST mode. The firm’s list stays the source of truth, not a calendar importance label. One conflict needs a written decision: a guard that force-closes at the floor during a restricted window would itself execute inside it. Reducing exposure before the window avoids that; how the firm treats a guard close is a question for its support.

Minimum trading days are a counter, not a hard stop. FTMO’s 2-Step requires at least 4 trading days, each a Prague calendar day on which at least one position was opened. The5ers’ High Stakes counts profitable days of at least 0.5% of the initial balance, measured as the lower of midnight balance and midnight equity minus the previous day’s balance. Code should count days on the firm’s clock and report them, not open trades just to fill a day.

Limits and risks of a rule guard

  • The firm measures, not the terminal. According to FTMO, its daily loss check tracks the lowest point equity reaches, even for a split second. A terminal guard acts only after the ticks it receives, so slippage and gaps can carry equity through the floor before a close fills; hence the buffer.
  • No terminal, no guard. If the VPS, terminal or connection is down, nothing in the EA enforces the limit; stop-loss orders held on the server remain the first line of protection.
  • Rules change. FTMO replaced its Best Day Rule with a Consistency rule for new 1-Step orders from October 12, 2026. Keep the parameters outside the code, with the date they were checked.
  • A guard limits losses; it does not create an edge. No code makes an evaluation passable, and leveraged trading can lose money even when every firm rule is respected.

Checklist before an EA trades on a prop firm account

Status: November 2026. Answer each point in writing before the first order:

  1. EA permission for the exact program, account size and platform, from the firm’s page, saved with the date.
  2. Daily rule: reference, amount, reset time in platform time, and whether swaps, commissions and fees count.
  3. Maximum loss: static or trailing, on balance or equity, end-of-day or intraday, and the effect of a payout.
  4. News, weekend and market-break rules for the account type, with the instrument mapping.
  5. Minimum or profitable days and the firm’s definition of a day.
  6. Demo test: the guard’s floor matches the firm’s dashboard on a day with a position open across the reset.
  7. Tests for a restart mid-day, a clock-change week, missing calendar data and the daily request count.

See also the pre-live checklist for AI-built trading bots and, if the EA stops trading unexpectedly, the checklist for an EA that is not placing trades.

Frequently asked questions

Which prop firms allow EAs?

The three firms reviewed here allow Expert Advisors with conditions, and the conditions decide whether a specific EA fits. As of November 3, 2026, FTMO does not restrict EAs that avoid its forbidden practices, FundedNext allows EAs on MT4 and MT5 only for accounts below $50,000 with a paid add-on, and The5ers allows EAs except listed strategies such as arbitrage and high-frequency trading. Check the firm’s current page for the exact program before trading.

How does FTMO calculate the daily loss limit?

FTMO recalculates the Maximum Daily Loss Limit at 00:00 CE(S)T as the balance at that moment minus 5% of the initial capital on the 2-Step, or 3% on the 1-Step and Instant accounts. Equity, meaning balance plus open profit or loss, swaps and commissions, must not fall below that limit at any time during the day. On the first day, the initial capital is the reference balance.

Does a prop firm daily loss limit use balance or equity?

Both, in different roles: the reference is set at the reset, and the check runs against live equity. FTMO and FundedNext use the balance at the reset and a fixed amount based on initial capital. The5ers’ 2-Step plan uses the higher of the previous day’s closing balance or equity, so open profit at the reset raises the floor. An EA has to implement the exact variant of its firm.

What is the difference between static and trailing drawdown?

A static maximum loss stays at a fixed level, for example initial capital minus 10%. A trailing limit moves up with a peak and never down. At FTMO, the 1-Step trails the highest balance recorded at midnight, and the Instant account trails the highest balance after closed trades. Open profit moves neither of them, but ending a day higher or closing a winning trade does.

What time does the FTMO daily loss reset in MT5?

FTMO resets the daily limit at midnight Prague time. Its MetaTrader platform runs on GMT+2 or GMT+3, so the reset is normally at 01:00 platform time. In the weeks between the US and European clock changes, the gap is 2 hours; FTMO named October 26 to November 1, 2026 as such a week. The countdown in Account MetriX shows the exact reset.

Can an EA trade during news on FTMO?

In the Challenge, Verification and Swing accounts, FTMO does not restrict news trading, provided its Forbidden Trading Practices are avoided. On FTMO Accounts and Instant Accounts, no order may execute on affected instruments from 2 minutes before to 2 minutes after a restricted release, including stop-loss, take-profit and pending orders. Positions opened more than 2 minutes earlier may stay open. FTMO lists the restricted events and instruments in its FAQ and economic calendar.

Is an equity protector EA allowed on a prop firm account?

It depends on the firm, because a protector is itself an EA. FundedNext classifies tools that only change stop-loss, take-profit or lot size as EAs and prohibits them on accounts of $50,000 and above. Where EAs are allowed, a protector still counts toward rules such as server request limits. It reduces risk, but gaps, slippage or an outage can still push equity through the limit.

Sources and status

As of November 2026. Third-party rules, prices and APIs may have changed since; the providers' current documentation and the official texts are authoritative.

Need a prop firm rule guard built or reviewed for MetaTrader 5? Get in touch — a free intro call covers the firm’s published rules, the guard design and a test plan for a demo account before any live order. More: Trading bot development for Interactive Brokers and MetaTrader 5.