Prop Firm EA Requirements: Building Compliant Trading Bots
Prop firm trading has exploded in the last three years. Firms like FTMO, MyForexFunds (before its closure), The Funded Trader, and Apex Trader Funding.

Prop Firm EA Requirements: How to Build a Compliant Expert Advisor
Prop firm trading has exploded in the last three years. Firms like FTMO, MyForexFunds (before its closure), The Funded Trader, and Apex Trader Funding collectively manage tens of thousands of active traders. For EA developers, the prop firm market represents a massive opportunity โ and a distinct set of technical constraints that differ completely from retail trading.
This guide covers everything you need to build an Expert Advisor that will pass prop firm evaluation and continue to perform in funded account conditions.
Understanding Prop Firm Rules
Every prop firm has a slightly different ruleset, but the core constraints are universal:
The Inviolable Rules (Instant Fail on Violation)
Maximum Daily Drawdown The most common hard limit. Typically 4-5% of account balance (not equity). If your account is $100K and you lose $5K in a single trading day, the account is closed. Your EA must track this in real-time and close all positions if approaching the limit.
Maximum Overall Drawdown Usually 8-12% of initial balance. FTMO uses 10%. This is calculated from the initial funded amount, not the current balance.
Minimum Trading Days Most firms require 4-10 minimum trading days before passing evaluation. Your EA must spread trades across calendar days, not bunch them into one session.
Profit Target 8-10% profit required to pass Phase 1. 5% for Phase 2. Your EA's position sizing must be calibrated to reach this target while staying within drawdown limits.
Rules That Vary by Firm
News Trading Restriction: Some firms prohibit trading within 2 minutes of high-impact news events (NFP, FOMC, CPI). FTMO allows it; others ban it outright.
Weekend Holding: Most firms allow holding positions over weekends, but some charge additional fees or prohibit it entirely.
Overnight Holding: Similar variation โ check the specific firm's rules.
EA/Automation Disclosure: Most top-tier firms allow EAs and require disclosure. Some lower-tier firms ban automated trading entirely. Verify before building.
Copy Trading: Prohibited at virtually all firms. Your EA must not be copying signals from another account.
High-Frequency Trading: Firms have varying definitions. Some ban scalping under 2-minute holds. Others are fine with it. Check the minimum holding time rule carefully.
Implementing Compliance Logic
This is where most EA developers get it wrong. Compliance logic isn't an afterthought โ it's the architecture.
Drawdown Manager (Critical)
// In your EA's global variables
double g_InitialBalance; // Account balance at EA start
double g_DailyStartBalance; // Balance at start of trading day
double g_MaxDailyDrawdown; // Absolute max daily loss (e.g. 5%)
double g_MaxTotalDrawdown; // Absolute max total loss (e.g. 10%)
// In OnInit()
void InitDrawdownManager() {
g_InitialBalance = AccountInfoDouble(ACCOUNT_BALANCE);
g_DailyStartBalance = g_InitialBalance;
g_MaxDailyDrawdown = g_InitialBalance * 0.04; // 4% daily limit
g_MaxTotalDrawdown = g_InitialBalance * 0.09; // 9% total (1% buffer from 10%)
Print("Drawdown Manager initialized. Balance: ", g_InitialBalance,
" Daily limit: -", g_MaxDailyDrawdown,
" Total limit: -", g_MaxTotalDrawdown);
}
// Call this on every tick
bool IsDrawdownBreached() {
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
// Check daily drawdown
double dailyLoss = g_DailyStartBalance - currentEquity;
if (dailyLoss >= g_MaxDailyDrawdown) {
Print("DAILY DRAWDOWN LIMIT HIT. Closing all positions.");
CloseAllPositions("Daily DD limit");
return true;
}
// Check total drawdown from initial balance
double totalLoss = g_InitialBalance - currentEquity;
if (totalLoss >= g_MaxTotalDrawdown) {
Print("TOTAL DRAWDOWN LIMIT HIT. EA stopping.");
CloseAllPositions("Total DD limit");
ExpertRemove(); // Stop the EA
return true;
}
return false;
}
// Reset daily balance at midnight
void OnTimer() {
MqlDateTime currentTime;
TimeToStruct(TimeCurrent(), currentTime);
if (currentTime.hour == 0 && currentTime.min == 0) {
g_DailyStartBalance = AccountInfoDouble(ACCOUNT_BALANCE);
Print("Day reset. New daily start balance: ", g_DailyStartBalance);
}
}
News Filter
For firms that restrict news trading:
// Check if current time is within N minutes of high-impact news
// In production, pull from a news API. Simpler: hardcode major releases
bool IsNewsWindow(int minutesBefore = 2, int minutesAfter = 2) {
// High-impact news times (UTC) - update weekly
// NFP: First Friday of month, 13:30 UTC
// FOMC: 8x per year, 19:00 UTC
// CPI: Monthly, 13:30 UTC
datetime current = TimeCurrent();
// Check against your news calendar array
for (int i = 0; i < ArraySize(g_NewsEvents); i++) {
long diffSeconds = (long)(current - g_NewsEvents[i]);
if (diffSeconds >= -(minutesBefore * 60) &&
diffSeconds <= (minutesAfter * 60)) {
return true; // In news window
}
}
return false;
}
Minimum Holding Time Enforcement
// Track when each position was opened
datetime g_PositionOpenTime[];
bool CanClosePosition(ulong positionTicket) {
int minHoldingMinutes = 2; // Per firm rules
datetime openTime = PositionGetInteger(POSITION_TIME);
long holdingSeconds = TimeCurrent() - openTime;
if (holdingSeconds < minHoldingMinutes * 60) {
return false; // Not held long enough
}
return true;
}
Minimum Trading Days Spread
// Ensure trades are spread across required minimum days
int g_TradingDaysActive = 0;
datetime g_LastTradingDate = 0;
void TrackTradingDay() {
MqlDateTime today;
TimeToStruct(TimeCurrent(), today);
MqlDateTime lastDate;
TimeToStruct(g_LastTradingDate, lastDate);
if (today.day != lastDate.day || today.mon != lastDate.mon) {
if (HasTradedToday()) {
g_TradingDaysActive++;
g_LastTradingDate = TimeCurrent();
}
}
}
bool HasMetMinimumTradingDays() {
int requiredDays = 4; // Most firms require 4 minimum
return g_TradingDaysActive >= requiredDays;
}
๐ค Can This Strategy Be Automated?
In 2026, top traders run custom EAs โ not manual charts. We build MT4/MT5 Expert Advisors that execute your exact strategy 24/7, pass prop firm challenges, and eliminate emotional decisions.
- Runs 24/7 โ no screen time, no missed entries
- Prop-firm compliant (FTMO, MFF, TFT drawdown rules)
- MyFXBook-verified backtest results included
- From strategy brief to live EA in 2โ4 weeks
Position Sizing for Prop Firms
Standard fixed-lot sizing is dangerous on prop accounts. Use Kelly-based or fixed-risk sizing:
double CalculateProportionalLotSize(
string symbol,
double stopLossPips,
double riskPercent = 0.5 // Conservative: 0.5% per trade
) {
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = accountBalance * (riskPercent / 100.0);
double pipValue = GetPipValue(symbol);
double lotSize = riskAmount / (stopLossPips * pipValue);
// Hard cap at 2% of balance per trade for prop firms
double maxRisk = accountBalance * 0.02;
double maxLots = maxRisk / (stopLossPips * pipValue);
lotSize = MathMin(lotSize, maxLots);
// Round to broker's lot step
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
lotSize = MathFloor(lotSize / lotStep) * lotStep;
return MathMax(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN));
}
Testing Your EA on Prop Firm Conditions
Before submitting to evaluation, run simulated prop conditions:
1. Stress Test for Drawdown Triggers Deliberately set the drawdown limit artificially low in testing and verify the EA closes positions correctly.
2. Test Day Counting Logic
Run the EA across a date change at midnight and verify g_TradingDaysActive increments correctly.
3. Test News Window Set a test news event 1 minute in the future, verify the EA stops opening positions.
4. Backtest with Realistic Spreads Prop firm environment: use "every tick" backtest quality, set spread to 2x normal (prop firm spread can vary), enable weekend gaps.
5. Forward Test on Demo Run on a demo account sized identically to the prop account for at least 2-3 weeks before paying for evaluation.
๐ Stop Trading Manually โ Let AI Do It
While you sleep, your EA keeps working. Viprasol builds prop-firm-compliant Expert Advisors with strict risk management, real backtests, and live deployment support.
- No rule violations โ daily drawdown, max drawdown, consistency rules built in
- Covers MT4, MT5, cTrader, and Python-based algos
- 5.0โ Upwork record โ 100% job success rate
- Free strategy consultation before we write a single line
Prop Firm-Specific Optimizations
FTMO: Uses balance-based drawdown (not equity). Your EA must monitor ACCOUNT_BALANCE for the daily DD calculation, not ACCOUNT_EQUITY. Easy to get this wrong.
Apex Trader Funding: Has a consistency rule on some account types โ no single day's profit can exceed 30% of total profits. Requires tracking daily P&L.
The Funded Trader: Requires minimum 5 trading days in Phase 1, 5 in Phase 2. Verify your day-counting logic.
E8 Funding: 5% max daily drawdown, 8% max overall. Relatively lenient for getting through evaluation.
Red Lines: What Will Get You Flagged
- Same IP, multiple accounts: Firms actively monitor this. Each account on a separate connection.
- Overnight position with weekend gap risk: Some firms scan for this. Add a Friday close before market close if targeting these firms.
- Martingale / grid without hard stop: Any system that can theoretically grow positions infinitely will eventually hit the drawdown limit catastrophically. Hard-code a maximum position count and maximum cumulative exposure.
- Latency arbitrage / tick scalping: Firms with < 10ms tick resolution in their feed can detect this. Minimum 30-second hold time is safer.
Getting Your EA Verified
After passing evaluation, get your performance independently verified:
- MyFXBook: Industry standard. Live connect your MT4/MT5 account.
- FXBlue: Good alternative, more detailed statistics.
- Verified trading history builds trust if you plan to sell or market your EA.
At Viprasol Tech, we build custom prop-firm-compliant Expert Advisors with hardcoded drawdown management, news filters, day counting, and full verification setup. Our EAs are built to pass evaluation โ and to keep funded accounts alive.
Build a Prop Firm EA with Viprasol โ
class CPropFirmSafety {
private:
double m_initial_balance;
double m_daily_loss_limit; // e.g., 0.05 for 5%
double m_max_drawdown_limit; // e.g., 0.10 for 10%
double m_highest_equity;
double m_day_start_equity;
public:
CPropFirmSafety(double daily_limit, double dd_limit) {
m_initial_balance = AccountInfoDouble(ACCOUNT_BALANCE);
m_daily_loss_limit = daily_limit;
m_max_drawdown_limit = dd_limit;
m_highest_equity = AccountInfoDouble(ACCOUNT_EQUITY);
m_day_start_equity = m_highest_equity;
}
void OnNewDay() {
m_day_start_equity = AccountInfoDouble(ACCOUNT_EQUITY);
}
void UpdateHighestEquity() {
double current_equity = AccountInfoDouble(ACCOUNT_EQUITY);
if(current_equity > m_highest_equity) {
m_highest_equity = current_equity;
}
}
bool IsDailyLossExceeded() {
double current_equity = AccountInfoDouble(ACCOUNT_EQUITY);
double daily_loss = (m_day_start_equity - current_equity) / m_day_start_equity;
// Use 80% of limit as safety buffer
return daily_loss >= m_daily_loss_limit * 0.8;
}
bool IsDrawdownExceeded() {
double current_equity = AccountInfoDouble(ACCOUNT_EQUITY);
double drawdown = (m_highest_equity - current_equity) / m_initial_balance;
// Use 80% of limit as safety buffer
return drawdown >= m_max_drawdown_limit * 0.8;
}
bool CanOpenNewTrade() {
if(IsDailyLossExceeded()) {
Print("Daily loss limit approaching - no new trades");
return false;
}
if(IsDrawdownExceeded()) {
Print("Drawdown limit approaching - no new trades");
return false;
}
return true;
}
double GetRemainingDailyRisk() {
double current_equity = AccountInfoDouble(ACCOUNT_EQUITY);
double used_loss = m_day_start_equity - current_equity;
double max_loss = m_day_start_equity * m_daily_loss_limit;
return MathMax(0, (max_loss - used_loss) / current_equity);
}
void EmergencyCloseAll() {
for(int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if(ticket > 0) {
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.position = ticket;
request.symbol = PositionGetString(POSITION_SYMBOL);
request.volume = PositionGetDouble(POSITION_VOLUME);
request.type = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY ?
ORDER_TYPE_SELL : ORDER_TYPE_BUY;
request.price = request.type == ORDER_TYPE_BUY ?
SymbolInfoDouble(request.symbol, SYMBOL_ASK) :
SymbolInfoDouble(request.symbol, SYMBOL_BID);
OrderSend(request, result);
}
}
}
};
Dynamic Position Sizing:
class CPropFirmSizing {
private:
CPropFirmSafety* m_safety;
double m_base_risk_percent;
public:
CPropFirmSizing(CPropFirmSafety* safety, double base_risk = 0.5) {
m_safety = safety;
m_base_risk_percent = base_risk;
}
double CalculateLots(string symbol, double stop_loss_pips) {
// Get remaining daily risk
double remaining_risk = m_safety.GetRemainingDailyRisk();
// Use smaller of base risk or remaining risk
double actual_risk = MathMin(m_base_risk_percent / 100, remaining_risk * 0.3);
// Calculate lots
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double risk_amount = equity * actual_risk;
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double pip_value = tick_value * (point / tick_size);
double lots = risk_amount / (stop_loss_pips * pip_value);
// Normalize to broker requirements
double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
lots = MathFloor(lots / lot_step) * lot_step;
lots = MathMax(min_lot, MathMin(max_lot, lots));
return lots;
}
};
Trailing Drawdown Handler
For Firms with Trailing Max Loss:
class CTrailingDrawdown {
private:
double m_starting_balance;
double m_current_dd_limit;
double m_max_dd_percent;
bool m_dd_is_trailing;
public:
CTrailingDrawdown(double max_dd = 0.10, bool trailing = true) {
m_starting_balance = AccountInfoDouble(ACCOUNT_BALANCE);
m_max_dd_percent = max_dd;
m_dd_is_trailing = trailing;
m_current_dd_limit = m_starting_balance * (1 - m_max_dd_percent);
}
void UpdateDrawdownLimit() {
if(!m_dd_is_trailing) return;
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double new_limit = equity - (m_starting_balance * m_max_dd_percent);
// Drawdown limit only moves up, never down
if(new_limit > m_current_dd_limit) {
m_current_dd_limit = new_limit;
Print("Drawdown limit updated to: ", m_current_dd_limit);
}
}
double GetDistanceToLimit() {
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
return equity - m_current_dd_limit;
}
bool IsApproachingLimit(double buffer_percent = 0.2) {
double buffer = m_starting_balance * m_max_dd_percent * buffer_percent;
return GetDistanceToLimit() < buffer;
}
};
Progress Tracking
Challenge Progress Monitor:
class CChallengeProgress {
private:
double m_starting_balance;
double m_profit_target;
int m_min_trading_days;
datetime m_start_date;
int m_trading_days_count;
public:
double GetProgress() {
double current_profit = AccountInfoDouble(ACCOUNT_BALANCE) - m_starting_balance;
return (current_profit / (m_starting_balance * m_profit_target)) * 100;
}
string GetStatus() {
double progress = GetProgress();
int days_remaining = m_min_trading_days - m_trading_days_count;
string status = StringFormat(
"Progress: %.1f%% | Days: %d/%d | Target: $%.2f",
progress,
m_trading_days_count,
m_min_trading_days,
m_starting_balance * m_profit_target
);
return status;
}
bool IsTargetReached() {
return GetProgress() >= 100.0 && m_trading_days_count >= m_min_trading_days;
}
void UpdateTradingDays() {
// Call daily - checks if any trades were made
if(HasTradedToday()) {
m_trading_days_count++;
}
}
};
Strategy Guidelines for Prop Firms
Recommended Approach:
- Conservative Risk: 0.5-1% per trade maximum
- Wider Stops: Give trades room to breathe
- Quality Over Quantity: Fewer, higher-probability trades
- No Averaging Down: Never add to losers
- Close Before Limits: Don't cut it close
Strategy Types That Work:
โ Swing trading (H4/Daily) โ Trend following โ Breakout with confirmation โ Supply/demand zones
Strategies to Avoid:
โ Martingale โ Grid without stops โ High-frequency scalping โ News straddles (if restricted)
Our Prop Firm EA Services
Viprasol develops prop-firm optimized EAs with:
- Built-in rule compliance
- Real-time progress tracking
- Automatic risk reduction
- Emergency shutdown systems
Need a prop firm EA? Contact us to get funded faster.
About the Author
Viprasol Tech Team
Custom Software Development Specialists
The Viprasol Tech team specialises in algorithmic trading software, AI agent systems, and SaaS development. With 100+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement. Based in India, serving clients globally.
Ready to Automate Your Trading?
Get a custom Expert Advisor built by professionals with verified MyFXBook results.
Free consultation โข No commitment โข Response within 24 hours
Need a custom EA or trading bot built?
We specialise in MT4/MT5 Expert Advisor development โ prop-firm compliant, forward-tested before live, MyFXBook verifiable. 5.0โ Upwork, 100% Job Success, 100+ projects shipped.