# Detecting Future Functions in Indicators: The First Step Before EA Integration
Why Future Functions Destroy Your EA
Before converting any indicator into an EA strategy, you must verify it contains no future function (also known as look-ahead bias). A future function means the indicator recalculates historical values based on current price data — effectively "cheating" by using tomorrow's news to trade yesterday.
In live trading, this results in catastrophic failure. An EA that looks perfect in backtesting will fail miserably forward because those perfect historical signals disappear in real-time.
Two Methods to Detect Future Functions
Method 1: White-Box (Source Code Inspection)
If you have access to the indicator's MQL4 source code, inspect the loop sections carefully. Look for any code that modifies historical time series arrays after they have been calculated.
Red flags include:
Method 2: Black-Box (Real-Time Observation)
When source code isn't available, load the indicator on a 1-minute chart and observe it continuously.
Watch for historical signal changes as new candles form. For example, a sell arrow that appeared two candles ago might suddenly change to a buy arrow after the current candle closes. This is definitive proof of a future function.
The iCustom Function: Reading Indicator Values in Your EA
Once you've verified the indicator is safe, use the `iCustom()` function to read its values in your EA:
```cpp
double iCustom(
string symbol, // symbol (NULL = current)
int timeframe, // timeframe (0 = current chart)
string name, // indicator file name
... , // indicator parameters
int mode, // line index (0-7)
int shift // shift (0 = current bar)
);
```
Practical Example: Arrow-Based Signal
For arrow indicators, the value is `EMPTY_VALUE` (empty) when no arrow exists, and the price value when an arrow appears:
```cpp
// Check for buy arrow on previous bar (safer than current bar)
double buySignal = iCustom(NULL, 0, "MyArrowIndicator", 0, 1);
double sellSignal = iCustom(NULL, 0, "MyArrowIndicator", 1, 1);
if(buySignal != EMPTY_VALUE && buySignal > 0) {
// Buy signal detected
OrderSend(Symbol(), OP_BUY, lotSize, Ask, 3, 0, 0, "Buy Signal", magic, 0, clrGreen);
}
```
Handling Line-Based Indicators
For line indicators (e.g., oscillators), each color/line is stored in a separate buffer:
```cpp
// For a dual-color line indicator (e.g., red=overbought, green=oversold)
double redLine = iCustom(NULL, 0, "DualColorIndicator", 0, 0);
double greenLine = iCustom(NULL, 0, "DualColorIndicator", 1, 0);
if(redLine > 70) { /* Overbought - consider sell */ }
if(greenLine < 30) { /* Oversold - consider buy */ }
```
Critical Backtesting Warning: The Shift 0 Trap
A common mistake that ruins backtesting accuracy is using `shift=0` (current bar) in historical tests.
The Problem: During backtesting, MT4 can see the `close` price of the current bar before the bar actually closes in real trading. This creates an unrealistic advantage — your EA knows the future.
The Solution: Always use `shift=1` (previous completed bar) for entry conditions during backtesting:
```cpp
// WRONG - Uses future data in backtest
double signal = iCustom(NULL, 0, "Indicator", 0, 0);
// CORRECT - Only uses confirmed closed bar data
double signal = iCustom(NULL, 0, "Indicator", 0, 1);
```
EA Logic Structure for Indicator-Based Strategies
A robust EA follows this state-check flow:
```cpp
void OnTick() {
// Step 1: Check current positions
int totalOrders = OrdersTotal();
bool hasBuy = false, hasSell = false;
for(int i = 0; i < totalOrders; i++) {
if(OrderSelect(i, SELECT_BY_POS)) {
if(OrderMagicNumber() == magic) {
if(OrderType() == OP_BUY) hasBuy = true;
if(OrderType() == OP_SELL) hasSell = true;
}
}
}
// Step 2: Get indicator signals (use shift=1 for reliability)
double buySignal = iCustom(NULL, 0, "Indicator", 0, 1);
double sellSignal = iCustom(NULL, 0, "Indicator", 1, 1);
// Step 3: Position management
if(hasBuy && !hasSell && sellSignal != EMPTY_VALUE) {
// Close buy, open sell (reverse)
OrderClose(...);
OrderSend(Symbol(), OP_SELL, lotSize, Bid, ...);
}
else if(!hasBuy && !hasSell) {
// No position - check both directions
if(buySignal != EMPTY_VALUE) {
OrderSend(Symbol(), OP_BUY, lotSize, Ask, ...);
}
else if(sellSignal != EMPTY_VALUE) {
OrderSend(Symbol(), OP_SELL, lotSize, Bid, ...);
}
}
}
```
The Overfitting Trap: Why "Perfect" Backtests Fail
A backtest showing 85% win rate and 4% drawdown turning $10,000 into $10,000,000 in one year is almost certainly overfit or using future functions.
Prevention Measures:
1. Use out-of-sample data - Reserve 30% of historical data for final validation only
2. Limit optimization parameters - Test no more than 5 variables simultaneously
3. Verify no `shift=0` - Ensure all indicator reads use completed bars
4. Forward test on demo - Run 3 months live demo before any real money
---
References:
1. MQL4 Documentation - Variables (docs.mql4.com)
2. FxGecko - MT4 Programming: How to Apply Indicators to EAs (2023)
3. MetaTrader 4 Help - Expert Optimization Setup
4. MQL4 Documentation - OrderSend Function
5. Forex Factory - Backtesting Reliability Discussion (2007)
6. MQL4 Documentation - Other Operations (Indexing)