Firebird System Logic Overview
This document provides a non-technical explanation of the logic used by the Firebird trading script. The system is designed as an "exhaustion" strategy, meaning it looks for moments when price movement becomes overextended and attempts to trade in the direction of a reversal.
1. Core Strategy: The Moving Average Envelope
The foundation of this system is a specific type of price channel called an Envelope.
- The Base: The script calculates a 10-day Simple Moving Average (SMA). Depending on your settings, it looks at either the "Closing" prices or the "High/Low" range of the candles.
- The Channel: It then creates a boundary (an envelope) by shifting that moving average up and down by a small percentage (defaulting to 0.1%).
- The Trigger: The system watches for the price to break outside of these boundaries. When the price "exhausts" itself by pushing too far beyond the normal range, the script enters a trade in the opposite direction of the current trend.
2. Trade Management & Averaging
Unlike simple systems that exit at the first sign of trouble, Firebird uses a proactive management style:
- Averaging Down: If a trade moves against the desired direction, the system does not immediately close it. Instead, it opens additional positions to "average" the entry price.
- Dynamic Spacing: The distance between these extra trades (the "Pip Step") can be adjusted. In some configurations, the script can automatically increase the gap between new orders as the number of open positions grows.
- Unified Targets: When multiple positions are held, the script calculates a new Average Opening Price. It then recalculates a single unified Profit Target and Stop Loss for the entire group of trades based on that average.
3. Safety Mechanisms & Filters
The script includes several layers of logic to protect the account and ensure smooth execution:
- Frequency Filter: To prevent "over-trading" or entering a trade immediately after a loss, the script includes a buffer. It will not open a new position if an order was closed within a very short recent timeframe.
- Trade Direction Lock: The system tracks the current market direction. It avoids opening redundant trades in the same direction to keep the portfolio balanced.
- Stop Loss Protection: If a Stop Loss is hit, the script monitors the price action. If it detects that a stop loss was triggered because the market moved violently in one direction, it may temporarily block new trades in that specific direction until the market stabilizes. This prevents the system from being "trapped" repeatedly by an extreme trending move.
- Friday Filter: There is an option to toggle trading on Fridays, allowing for manual control over weekend risk.
4. Summary of Behavior
In plain terms: Firebird waits for the market to become over-extended relative to its recent average. Once it hits that "exhaustion" point, it bets on a reversal. If the price continues to move against the bet, it adds more positions to lower the average cost and then manages all those positions as a single unit with a shared target.
/*-----------------------------+
| |
| Shared by www.Aptrafx.com |
| |
+------------------------------*/
//+-----------------------------------------------------------------------------+
//| Firebird v0.63 - MA envelope exhaustion system |
//+-----------------------------------------------------------------------------+
#property copyright "Copyright © 2005, TraderSeven"
#property link "TraderSeven@gmx.net"
// \\|// +-+-+-+-+-+-+-+-+-+-+-+ \\|//
// ( o o ) |T|r|a|d|e|r|S|e|v|e|n| ( o o )
// ~~~~oOOo~(_)~oOOo~~~~ +-+-+-+-+-+-+-+-+-+-+-+ ~~~~oOOo~(_)~oOOo~~~~
// Firebird calculates a 10 day SMA and then shifts it up and down 2% to for a channel.
// For the calculation of this SMA either close (more trades) or H+L (safer trades) is used.
// When the price breaks a band a postion in the opposite of the current trend is taken.
// If the position goes against us we simply open an extra position to average.
// 50% of the trades last a day. 45% 2-6 days 5% longer or just fail.
//
//01010100 01110010 01100001 01100100 01100101 01110010 01010011 01100101 01110110 01100101 01101110
// Credits fly to:
// Vooch for the backtesting fix.
// Hugues Du Bois for the multi currency code.
// Jackie Griffin for some debugging.
// Many people in the MT forum for testing and feedback
//----------------------- USER INPUT
extern int MA_length = 10;
extern int MA_timeframe = 30; // hdb did I add this ? lol
extern int MAtype=0;//0=close, 1=HL
extern double Percent = 0.1;
extern int TradeOnFriday =1; // >0 trades on friday
extern int slip = 100;//exits only
extern double Lots = 0.3;
extern int TakeProfit = 30;
extern int Stoploss = 2000;// total loss on all open positions in pips
//extern double TrailingStop = 5;
extern int PipStep = 30;//if position goes this amount of pips against you add another.
extern double IncreasementType =0;//0=just add every PipStep, >0 =OrdersToal()^x *Pipstep
extern int MagicNumber=12345;
double Stopper=0;
double KeepStopLoss=0;
double KeepAverage;
double dummy;
double spread=0;
double CurrentPipStep;
int OrderWatcher=0;
//----------------------- MAIN PROGRAM LOOP
int start()
{
double PriceTarget;
double AveragePrice;
int OpeningDay;
//----------------------- CALCULATE THE NEW PIPSTEP
CurrentPipStep=PipStep;
if(IncreasementType>0)
{
CurrentPipStep=MathSqrt(OrdersTotal())*PipStep;
CurrentPipStep=MathPow(OrdersTotal(),IncreasementType)*PipStep;
}
//-----------------------
int Direction=0;//1=long, 11=avoid long, 2=short, 22=avoid short
if (Day()!=5 || TradeOnFriday >0)
{
int cnt=0, total;
int myTotal =0; // hdb
total=OrdersTotal();
if(total==0) OpeningDay=DayOfYear();
for(cnt=0;cnt<total;cnt++)
{
if ( (OrderSymbol()==Symbol()) && (OrderMagicNumber()==MagicNumber) ) // hdb - only symbol and magic
{
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
double LastPrice=OrderOpenPrice();
myTotal = myTotal +1; // hdb - count of relevant trades
}
}
// OrderSelect(total, SELECT_BY_POS, MODE_TRADES); // removed hdb
/////////////////////////////////////////////////////////////////////////////////////////
// BACKTESTER FIX: DO NOT PLACE AN ORDER IF WE JUST CLOSED
// AN ORDER WITHIN Period() MINUTES AGO
/////////////////////////////////////////////////////////////////////////////////////////
datetime orderclosetime;
string rightnow;
int rightnow2;
int TheHistoryTotal=HistoryTotal();
int difference;
int flag=0;
for(cnt=0;cnt<TheHistoryTotal;cnt++)
{
if(OrderSelect(cnt,SELECT_BY_POS,MODE_HISTORY)==true)
{
if ( (OrderSymbol()==Symbol()) && (OrderMagicNumber()==MagicNumber) ) // hdb - only symbol and magic
{
orderclosetime=OrderCloseTime();
rightnow=Year()+"-"+Month()+"-"+Day()+" "+Hour()+":"+Minute()+":"+Seconds();
rightnow2=StrToTime(rightnow);
difference=rightnow2-orderclosetime;
if(Period()*60*2>difference)
{ // At least 2 periods away!
flag=1; // Throw a flag
break;
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
if(flag!=1)
{
//----------------------- PREVIOUS OPEN PRICE
OrderWatcher=0;
total=OrdersTotal();
for(cnt=(total-1);cnt>=0;cnt--){
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
// Print("ordersymbol = ", OrderSymbol(), " OrderOpenPrice= ", DoubleToStr(OrderOpenPrice(), 10));
if ( OrderSymbol()==Symbol()) // && (OrderMagicNumber()==MagicNumber) ) // hdb - only symbol and magic
{
LastPrice=OrderOpenPrice();
Comment("LastPrice= ",DoubleToStr(LastPrice, 10));
// Print("cnt= ", cnt, " ordersymbol = ", OrderSymbol(), " OrderOpenPrice= ", DoubleToStr(OrderOpenPrice(), 10), " lastprice= ",DoubleToStr(LastPrice, 10 ));
break;
}
}
//Print("ordersymbol = ", OrderSymbol(), " OrderOpenPrice= ", DoubleToStr(OrderOpenPrice(), 10), " lastprice= ",DoubleToStr(LastPrice, 10 ));
//----------------------- ENTER POSITION BASED ON OPEN
if(MAtype==0)
{
double myMA =iMA(NULL,MA_timeframe,MA_length,0,MODE_SMA,PRICE_OPEN,0);
// Print(" Top, Bid ",myMA*(1+Percent/100)," ",Bid);
// if((myMA*(1+Percent/100))<Bid) Print(" Top, Bid ",myMA*(1+Percent/100)," ",Bid);
if((myMA*(1+Percent/100))<Bid && Direction!=22 && (Bid>=(LastPrice+(CurrentPipStep*Point))||total==0)) // Go SHORT -> Only sell if >= 30 pips above previous position entry
{
OrderSend(Symbol(),OP_SELL,Lots,Bid,slip,Bid+(Stoploss*Point),Bid-(TakeProfit*Point),0,0,Red);
OrderWatcher=1;
Direction=2;
}
if((myMA*(1-Percent/100))>Ask && Direction!=11 && (Ask<=(LastPrice-(CurrentPipStep*Point))||total==0)) // Go LONG -> Only buy if >= 30 pips below previous position entry
{
OrderSend(Symbol(),OP_BUY,Lots,Ask,slip,Ask-(Stoploss*Point),Ask+(TakeProfit*Point),0,0,Blue);
OrderWatcher=1;
Direction=1;
}
}
//----------------------- ENTER POSITION BASED ON HIGH/LOW
if(MAtype==1)
{
if((iMA(NULL,MA_timeframe,MA_length,0,MODE_SMA,PRICE_HIGH,0)*(1+Percent/100))<Bid && Direction!=22 && (Bid>=(LastPrice+(CurrentPipStep*Point))||total==0)) // Go SHORT -> Only sell if >= 30 pips above previous position entry
{
OrderSend(Symbol(),OP_SELL,Lots,Bid,slip,Bid+(Stoploss*Point),Bid-(TakeProfit*Point),0,0,Red);
OrderWatcher=1;
Direction=2;
}
if((iMA(NULL,MA_timeframe,MA_length,0,MODE_SMA,PRICE_LOW,0)*(1-Percent/100))>Ask && Direction!=11 && (Ask<=(LastPrice-(CurrentPipStep*Point))||total==0)) // Go LONG -> Only buy if >= 30 pips below previous position entry
{
OrderSend(Symbol(),OP_BUY,Lots,Ask,slip,Ask-(Stoploss*Point),Ask+(TakeProfit*Point),0,0,Blue);
OrderWatcher=1;
Direction=1;
}
}
} // end of flag test
//----------------------- CALCULATE AVERAGE OPENING PRICE
total=OrdersTotal();
AveragePrice=0;
int myOrderType = -1; // hdb
myTotal = 0; // hdb - count of relevant trades
if(total>1 && OrderWatcher==1)
{
for(cnt=0;cnt<total;cnt++)
{
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
if ( (OrderSymbol()==Symbol()) && (OrderMagicNumber()==MagicNumber) ) // hdb - only symbol and magic
{
AveragePrice=AveragePrice+OrderOpenPrice();
myOrderType = OrderType(); // hdb - keep order type
myTotal = myTotal +1; // hdb - count of relevant trades
}
}
AveragePrice=AveragePrice/MathMax(myTotal,1); // hdb myTotal
}
//----------------------- RECALCULATE STOPLOSS & PROFIT TARGET BASED ON AVERAGE OPENING PRICE
// OrderSelect(0, SELECT_BY_POS, MODE_TRADES); // hdb removed
if(myOrderType==OP_BUY && OrderWatcher==1 && myTotal>1) // Calculate profit/stop target for long
{
PriceTarget=AveragePrice+(TakeProfit*Point);
Stopper=AveragePrice-(((Stoploss*Point)/myTotal));
}
if(myOrderType==OP_SELL && OrderWatcher==1 && myTotal>1) // Calculate profit/stop target for short
{
PriceTarget=AveragePrice-(TakeProfit*Point);
Stopper=AveragePrice+(((Stoploss*Point)/myTotal));
}
Comment("AveragePrice", AveragePrice);
//----------------------- IF NEEDED CHANGE ALL OPEN ORDERS TO THE NEWLY CALCULATED PROFIT TARGET
//if(OrderWatcher==1 && myTotal>1)// check if average has really changed
//if(OrderWatcher==1 && myTotal>1)// check if average has really changed
// {
total=OrdersTotal();
for(cnt=0;cnt<total;cnt++)
{
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
if ( (OrderSymbol()==Symbol()) &&
(OrderMagicNumber()==MagicNumber) ) // hdb - only symbol and magic
{
OrderModify(OrderTicket(),0,Stopper,PriceTarget,0,Yellow);//set all positions to averaged levels
}
}
// }
//Comment("PriceTarget: ",PriceTarget," AveragePrice: ",AveragePrice," Total: ",total);
//----------------------- KEEP TRACK OF STOPLOSS TO AVOID RUNAWAY MARKETS
// Sometimes the market keeps trending so strongly the system never reaches it's target.
// This means huge drawdown. After stopping out it falls in the same trap over and over.
// The code below avoids this by only accepting a signal in teh opposite direction after a SL was hit.
// After that all signals are taken again. Luckily this seems to happen rarely.
if (OrdersTotal()>0)
{
myOrderType = -1; // hdb
myTotal = 0; // hdb - count of relevant trades
total=OrdersTotal();
for(cnt=0;cnt<total;cnt++)
{
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
if ( (OrderSymbol()==Symbol()) && (OrderMagicNumber()==MagicNumber) ) // hdb - only symbol and magic
{
KeepStopLoss=OrderStopLoss();
AveragePrice=AveragePrice+OrderOpenPrice();
myTotal = myTotal +1; // hdb - count of relevant trades
myOrderType = OrderType(); // hdb - keep order type
}
}
AveragePrice=AveragePrice/MathMax(myTotal,1); // hdb myTotal
KeepAverage=AveragePrice;
Direction =0;
if(myOrderType==OP_BUY)
{ Direction=1; } //long
else
{ if (myOrderType==OP_SELL) Direction=2; }//short
}
if(KeepStopLoss!=0)
{
spread=MathAbs(KeepAverage-KeepStopLoss)/2;
dummy=(Bid+Ask)/2;
if (KeepStopLoss<(dummy+spread) && KeepStopLoss>(dummy-spread))
{
// a stoploss was hit
if(Direction==1) Direction=11;// no more longs
if(Direction==2) Direction=22;// no more shorts
}
KeepStopLoss=0;
}
}
}
//----------------------- TO DO LIST
// 1st days profit target is the 30 pip line *not* 30 pips below average as usually. -----> Day()
// Trailing stop -> trailing or S/R or pivot target
// Realistic stop loss
// Avoid overly big positions
// EUR/USD 30 pips / use same value as CurrentPipStep
// GBP/CHF 50 pips / use same value as CurrentPipStep
// USD/CAD 35 pips / use same value as CurrentPipStep
//----------------------- OBSERVATIONS
// GBPUSD not suited for this system due to not reversing exhaustions. Maybe use other types of MA
// EURGBP often sharp reversals-> good for trailing stops?
// EURJPY deep pockets needed.
Comments