Prop firm challenges are a compelling opportunity for skilled traders: access to $50K–$200K in funded capital for a fraction of the cost. But they come with strict rules — maximum daily loss, maximum total drawdown, minimum trading days, and sometimes news trading restrictions. An algo that doesn't respect these rules will get the account blown or disqualified, even if it's profitable.
Here's how to build a Pine Script strategy that not only performs well but is structurally compliant with standard prop firm challenge rules.
Understanding the Key Rules
Most prop firm challenges (FTMO, E8, The Funded Trader, etc.) share these core constraints:
- Maximum Daily Loss: Typically 5% of the starting day balance. If you breach this, the account is closed immediately.
- Maximum Total Drawdown: Typically 10% from the starting balance (not the peak). Some firms use trailing drawdown from equity peak.
- Minimum Profit Target: Usually 8–10% to pass the challenge phase.
- Minimum Trading Days: Most require at least 10 active trading days.
- No Trading During High-Impact News: Some firms disallow open positions within 2–5 minutes of major news events.
Rules vary significantly between firms and even between challenge tiers. Always verify the exact parameters for your specific challenge before deploying any automated system. The code in this article uses FTMO-style rules as a reference.
Building the Drawdown Guard
The most critical component is a hard daily loss limit that prevents any further entries once the threshold is breached:
//@version=6
strategy("Prop Firm Compliant Strategy",
initial_capital=100000,
default_qty_type=strategy.percent_of_equity,
default_qty_value=2)
// --- Prop Firm Parameters ---
max_daily_loss_pct = input.float(4.5, "Max Daily Loss (%)", step=0.1) / 100 // slightly under 5%
max_total_dd_pct = input.float(9.0, "Max Total Drawdown (%)", step=0.5) / 100 // under 10%
// --- Daily loss tracking ---
var float session_open_equity = na
is_new_session = ta.change(time("D")) != 0
if is_new_session or na(session_open_equity)
session_open_equity := strategy.equity
daily_pnl_pct = (strategy.equity - session_open_equity) / session_open_equity
daily_limit_hit = daily_pnl_pct <= -max_daily_loss_pct
// --- Total drawdown tracking ---
var float peak_equity = strategy.initial_capital
peak_equity := math.max(peak_equity, strategy.equity)
total_dd_pct = (strategy.equity - peak_equity) / peak_equity
total_limit_hit = total_dd_pct <= -max_total_dd_pct
// --- Kill switch ---
trading_allowed = not daily_limit_hit and not total_limit_hit
// Plot warning labels
if daily_limit_hit
label.new(bar_index, high, "DAILY LIMIT HIT — NO NEW TRADES",
color=color.red, textcolor=color.white, size=size.small)
// --- Your signal logic (replace with your own) ---
fast_ma = ta.ema(close, 9)
slow_ma = ta.ema(close, 21)
long_cond = ta.crossover(fast_ma, slow_ma)
short_cond = ta.crossunder(fast_ma, slow_ma)
// Only enter if trading is allowed
if long_cond and trading_allowed
strategy.entry("Long", strategy.long)
if short_cond and trading_allowed
strategy.entry("Short", strategy.short)
Time-Based Session Filter
Prop firms typically want you to trade defined session hours. Avoid holding positions over the weekend or into highly illiquid periods. Add a session time filter:
// Input for session (use your chart's timezone)
session_str = input.session("0800-1700:23456", "Trading Session") // Mon-Fri, 08:00-17:00
// time() returns the bar's time if inside the session, else na
in_session = not na(time(timeframe.period, session_str))
// Close all positions outside session
if not in_session and strategy.position_size != 0
strategy.close_all(comment="Session close")
Consistency Rule Compliance
Some firms require that no single trading day accounts for more than a certain percentage of your total profit (e.g., 50%). This prevents "lucky day" passes. To manage this, track daily PnL and reduce position sizing on days where you've already hit a significant gain:
// Reduce size if daily gain already large (consistency rule protection)
daily_gain_pct = math.max(daily_pnl_pct, 0.0)
size_scalar = daily_gain_pct > 0.03 ? 0.5 : 1.0 // halve size if up >3% today
// Apply scalar to your computed base quantity
base_qty = 1 // replace with your sizing calc
qty_adjusted = math.max(math.floor(base_qty * size_scalar), 1)
Minimum Trading Days Counter
Most challenges require a minimum of 10 trading days. You can track this in Pine Script using a persistent variable that increments each new session where at least one trade was taken:
var int trading_days = 0
var bool traded_today = false
if is_new_session
if traded_today
trading_days := trading_days + 1
traded_today := false
// Mark as traded when a new position is opened
if strategy.position_size != 0 and strategy.position_size[1] == 0
traded_today := true
// Label on chart (only on last bar to avoid spam)
if barstate.islast
label.new(bar_index, high, "Trading Days: " + str.tostring(trading_days),
color=color.blue, textcolor=color.white, style=label.style_label_down)
What BotJockie Builds for Prop Firm Clients
We've built multiple prop-firm-compliant algos for clients across FTMO, E8, The Funded Trader, and FundedNext. Every bot includes hard-coded rule compliance with configurable parameters for each firm's specific thresholds, plus live monitoring so you can see account status at a glance. Book a free call if you want to discuss your specific challenge requirements.