· 11 min read

Automating Prop Firm Challenges with Pine Script

How to build rule-compliant algos for FTMO, My Forex Funds, and other prop firm challenges — with daily drawdown guards, consistency rules, and news filters baked in.

Bhavin Javia
Bhavin Javia
Founder, BotJockie · 22+ yrs software · 5+ yrs algo dev

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:

⚠️ Read Your Specific Rules

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.