Okay, here's a breakdown of what this MetaTrader script does, explained in a way that someone who isn't a programmer can understand:
Overall Goal:
The script, named "Firebird," aims to automatically trade on the Forex market (the market for exchanging currencies) by identifying moments when the price of a currency pair may be exhausted and ready to reverse direction. It tries to profit from these small reversals.
Core Strategy: Following a Channel:
-
Calculating a Moving Average: The script calculates a "moving average" of the price over a certain period (default is 10 days). Think of a moving average as a line that smooths out the price fluctuations, giving you an idea of the general trend. It's like averaging the closing price of the last 10 days to see where the price has generally been.
-
Creating a Channel: The script then creates a "channel" around this moving average by shifting the moving average line up and down by a small percentage (default is 5%). Imagine drawing two lines parallel to the moving average, one above it and one below it. This forms a channel.
-
Trading on Breakouts: The core idea is to trade when the price breaks out of this channel.
- If the price goes above the upper line of the channel, the script assumes the price might be too high and will open a "sell" order (betting that the price will go down).
- If the price goes below the lower line of the channel, the script assumes the price might be too low and will open a "buy" order (betting that the price will go up).
Managing Risk:
- Averaging Down: If the trade goes against the script (e.g., it opens a "buy" order, but the price keeps falling), it will open another "buy" order. This is called "averaging down." The goal is to lower the average entry price so that a smaller price increase will make the trade profitable.
- Stop Loss and Take Profit:
- The script sets a "stop loss" level for each trade. This is a price point at which the trade will automatically close to prevent further losses if the market moves too far in the wrong direction.
- It also sets a "take profit" level. This is a price point at which the trade will automatically close to secure a profit if the market moves in the desired direction.
- Divergence Filter: The script uses an indicator called "divergence" to help avoid trading during strong trends. Divergence looks at the relationship between price and other indicators. If the price is making new highs, but the other indicator isn't, that's a divergence, which can signal a possible trend reversal. The script only trades if the divergence is within a certain limit.
- Maximum Open Orders: The script limits the total number of trades that can be open at the same time.
Other Key Features:
- Time Restrictions: The script can be configured to only trade during certain hours of the day.
- Minimum Margin Level: It stops trading if the account's margin level (a measure of available funds) falls below a certain percentage.
- Safe Area: The script uses another indicator called "Safe Area" and only trades if the market is considered to be within a safe range.
- Loss Checking: The script keeps track of the largest loss experienced and if it exceeds a certain amount, it stops trading and alerts the user.
- Increasing position size: The script increases the lot size based on the number of open orders.
- Time Zone Management: The script adjust the hours using GMT and DST
In Summary:
Firebird is an automated trading system that aims to profit from small price reversals by trading within a channel. It tries to manage risk by averaging down, setting stop losses, and using divergence and other filters to avoid trading during strong trends.
Profitability Reports
//+-----------------------------------------------------------------------------+
//| Firebird v0.63H03 - 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
// Ron added [2006 03 08 (Mar 08)]
// maxDrawDown and maxOrders to track DD and number of open orders
// Divergence to protect from trends
// Swissly modified Timeframe for SafeArea to originally planned 15M
//----------------------- USER INPUT
extern int MA_length = 10;
extern int MA_timeframe = 15; // hdb did I add this ? lol
extern int MAtype = 0; //0=close, 1=HL
extern double Percent = 0.05;
extern int TradeOnFriday = 1; // >0 trades on friday
extern int slip = 100; //exits only
extern double Lots = 0.2; // modified by Renato
extern int TakeProfit = 18;
extern int Stoploss = 42; // total loss on all open positions in pips // modified by Renato
extern int MaxOpenOrders = 10;
extern double MinMarginLevel = 250; // Below this Minimum Margin Level percent % trading stops
extern int TradeFrom1 = 0; // Place trades "From" - "Until"
extern int TradeUntil1 = 24;
extern int TradeFrom2 = 0;
extern int TradeUntil2 = 0;
extern int TradeFrom3 = 0;
extern int TradeUntil3 = 10;
extern int TradeFrom4 = 0;
extern int TradeUntil4 = 0;
extern double SafeArea = 40;
// Ron added for iFXAnalyzer
extern int Fast_Period=23;
extern int Fast_Price = PRICE_OPEN;
extern int Slow_Period=84;
extern int Slow_Price = PRICE_OPEN;
extern double DivergenceLimit=0.002;
extern bool Use_V63D_Divergence = false; // 0 - Use original method for divergence, 1 - use in iFXAnalyzer
extern int PipStep = 40; //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 DVLimit = 10; // included by Renato
extern int PipsGoal = 500; // included by Renato
extern int PipsLoss = 500; // included by Renato
extern int GMT = 0; // InterbankFX // included by Renato Changed back to 0 by MrPip
extern int DST = 0; // 0=Standard 1=Daylight Saving // included by Renato
extern int OpeningHour = 0; // included by Renato
extern int ClosingHour = 24; // included by Renato
extern int writelog = 0;
double Stopper=0;
double KeepStopLoss=0;
double KeepAverage;
double dummy;
double spread=0;
double CurrentPipStep;
int OrderWatcher=0;
// Ron Adds
int maxDD=0;
int maxOO=0;
color clOpenBuy = DodgerBlue; // included by Renato
color clModiBuy = DodgerBlue; // included by Renato
color clCloseBuy = DodgerBlue; // included by Renato
color clOpenSell = Red; // included by Renato
color clModiSell = Red; // included by Renato
color clCloseSell = Red; // included by Renato
color clDelete = White; // included by Renato
string Name_Expert = "Firebird v63H03"; // included by Renato
string NameFileSound = "expert.wav"; // included by Renato
int MODE_DIV=0; // included by Renato
int MODE_SLOPE=1; // included by Renato
int MODE_ACEL=2; // included by Renato
// MrPip adds
int MagicNumber; // Made a global variable to aid in modularizing expert code
int Direction; //1=long, 11=avoid long, 2=short, 22=avoid short
double LastPrice;
double PriceTarget;
double AveragePrice;
//----------------------- INITIALISATION ROUTINE
int init()
{
LogWrite(Symbol()+",M"+Period());
}
//----------------------- MAIN PROGRAM LOOP
int start()
{
int flag, retval, total, myTotal;
LogWrite(TimeToStr(CurTime())+" - "+"Bid="+Bid);
MagicNumber=MagicfromSymbol();
int OpeningDay;
if ( DayOfWeek()==6 && Hour()>=20 ) { Comment("weekend"); return(0); } // included by Renato
if ( !(IsTesting() || IsDemo()) ) { if (LossCheck()) { Alert("excessive loss!"); PlaySound("alert.wav"); return(0); }} // included by Renato
Print("Account equity = ",AccountEquity());
//Ron Adds
double diverge;
if(AccountBalance()-AccountEquity() > maxDD) maxDD=AccountBalance()-AccountEquity();
if(MyOrdersTotal()>maxOO) maxOO=OrdersTotal(); //modified by Renato
diverge=divergence(Fast_Period,Slow_Period,Fast_Price,Slow_Price,0);
//----------------------- CALCULATE THE NEW PIPSTEP
CurrentPipStep=PipStep;
if(IncreasementType>0)
{
CurrentPipStep=MathSqrt(MyOrdersTotal())*PipStep; // modified by Renato
CurrentPipStep=MathPow(MyOrdersTotal(),IncreasementType)*PipStep; // modified by Renato
}
LogWrite("CurrentPipStep="+CurrentPipStep);
//-----------------------
Direction=0; //1=long, 11=avoid long, 2=short, 22=avoid short
if (Day()!=5 || TradeOnFriday >0)
{
total=OrdersTotal();
myTotal = MyOrdersTotal();
LogWrite("OrdersTotal="+total);
LogWrite("MyOrdersTotal="+myTotal);
if(myTotal==0) OpeningDay=DayOfYear(); // modified by Renato
if (myTotal > 0)
LastPrice = GetPreviousOpenPrice();
else
LogWrite("LastPrice="+LastPrice);
flag = CheckJustClosedOrder();
if(flag!=1)
{
//----------------------- PREVIOUS OPEN PRICE
OrderWatcher=0;
LastPrice = GetPreviousOpenPrice();
LogWrite("LastPrice="+LastPrice);
// Ron added divergence check
if(MathAbs(diverge)<=DivergenceLimit)
{
if ( Hour()<OpeningHour+GMT+DST || Hour()>ClosingHour+GMT+DST )
Comment("bad hours.");
else
{ // included by Renato
//----------------------- ENTER POSITION BASED ON OPEN
if(MAtype==0)
{
retval = EnterPositionBasedOnOpen();
if (retval == 1) // Opened Short position
{
OrderWatcher=1;
Direction=2;
}
if (retval == 2) // Opened Long Position
{
OrderWatcher=1;
Direction=1;
}
} // end of MAtype==0
//----------------------- ENTER POSITION BASED ON HIGH/LOW
if(MAtype==1)
{
retval = EnterPositionBasedOnHL();
if (retval == 1)
{
OrderWatcher=1;
Direction=2;
}
if (retval == 2)
{
OrderWatcher=1;
Direction=1;
}
} // end of MAtype==1
} // end of Trading hours ok included by Renato
} // end of Divergence < limit included by Ron
} // end of flag test
//----------------------- CALCULATE AVERAGE OPENING PRICE
myTotal = MyOrdersTotal();
if (myTotal>0 && OrderWatcher==1)
{
AveragePrice = CalculateAverageOpeningPrice(myTotal);
Comment("AveragePrice: ",AveragePrice," myTotal: ",myTotal); // modified by Renato
}
if(OrderWatcher==1 && myTotal>1) // check if average has really changed
{
ChangeOpenOrders(false, myTotal, AveragePrice);
}
//----------------------- KEEP TRACK OF STOPLOSS TO AVOID RUNAWAY MARKETS
if (myTotal > 0) KeepTrackOfStopLoss(AveragePrice);
} // end of Trading days
} // end of main program loop
//----------------------- SUB PROGRAMS AND FUNCTIONS
double GetPreviousOpenPrice()
{
int cnt;
double LstPrice;
for(cnt=OrdersTotal()-1;cnt>=0;cnt--){
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
if ( (OrderSymbol()==Symbol()) && (OrderMagicNumber()==MagicNumber) && (OrderComment()==GetCommentForOrder()) ) // hdb - only symbol and magic // modified by Renato
{
LstPrice=OrderOpenPrice();
break;
}
}
return(LstPrice);
} // end of GetPreviousOpenPrice
/////////////////////////////////////////////////////////////////////////////////////////
// BACKTESTER FIX: DO NOT PLACE AN ORDER IF WE JUST CLOSED
// AN ORDER WITHIN Period() MINUTES AGO
/////////////////////////////////////////////////////////////////////////////////////////
int CheckJustClosedOrder()
{
int cnt;
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) && (OrderComment()==GetCommentForOrder()) ) // hdb - only symbol and magic // modified by Renato
{
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;
}
}
}
}
return(flag);
} // end of CheckJustClosedOrder
//----------------------- ENTER POSITION BASED ON OPEN
int EnterPositionBasedOnOpen()
{
int ret;
bool dealok=false;
bool dealsave;
int h=TimeHour(LocalTime());
int m=TimeMinute(LocalTime());
double myMA = iMA(NULL,MA_timeframe,MA_length,0,MODE_SMA,PRICE_OPEN,0);
double RVI = iRVI(NULL,0,10,MODE_MAIN,0)-iRVI(NULL,0,10,MODE_MAIN,1); // included by Renato
double Safe1 = iBullsPower(NULL,PERIOD_M15,20,6,0)+iBearsPower(NULL,PERIOD_M15,20,6,0); // modified by Swissly
if(Point==0.01) Safe1=Safe1*100;
if(Point==0.0001) Safe1=Safe1*10000;
dealsave = ((Safe1>-SafeArea) && (Safe1<SafeArea));
if(MyOrdersTotal()<MaxOpenOrders && AccountEquity()/(AccountMargin()+0.0001)>(MinMarginLevel/100))
{
if(((h >= TradeFrom1) && (h <= (TradeUntil1-1)))||((h >= TradeFrom2) && (h <= (TradeUntil2-1)))||((h >= TradeFrom3) && (h <= (TradeUntil3-1)))||((h >= TradeFrom4) && (h <= (TradeUntil4-1))))
{
if(dealsave)
{
Comment("== Trades enabled ==","== Safe to trade ==");
dealok=true;
}
else
Comment("== Trades enabled ==","== Not safe to trade ==");
}
else
Comment("== Trades disabled ==");
// Go SHORT -> Only sell if >= 30 pips above previous position entry
if( (myMA*(1+Percent/100))<Bid && Direction!=22 && (Bid>=(LastPrice+(CurrentPipStep*Point)) || MyOrdersTotal()==0) && RVI<0 && dealok) // modified by Renato
{
OrderSend(Symbol(),OP_SELL,Lots,Bid,slip,Bid+(Stoploss*Point),Bid-(TakeProfit*Point),GetCommentForOrder(),MagicNumber,0,clOpenSell); // modified by Renato
ret = 1;
}
// Go LONG -> Only buy if >= 30 pips below previous position entry
if((myMA*(1-Percent/100))>Ask && Direction!=11 && (Ask<=(LastPrice-(CurrentPipStep*Point)) || MyOrdersTotal()==0) && RVI>0 && dealok) // modified by Renato
{
OrderSend(Symbol(),OP_BUY,Lots,Ask,slip,Ask-(Stoploss*Point),Ask+(TakeProfit*Point),GetCommentForOrder(),MagicNumber,0,clOpenBuy); // modified by Renato
ret = 2;
}
return(ret);
}
} // end of EnterPositionBasedOnOpen
//----------------------- ENTER POSITION BASED ON HIGH/LOW
int EnterPositionBasedOnHL()
{
if(MyOrdersTotal()<MaxOpenOrders && AccountEquity()/(AccountMargin()+0.0001)>(MinMarginLevel/100))
{
int ret;
if((iMA(NULL,MA_timeframe,MA_length,0,MODE_SMA,PRICE_HIGH,0)*(1+Percent/100))<Bid && Direction!=22 && (Bid>=(LastPrice+(CurrentPipStep*Point)) || MyOrdersTotal()==0)) // Go SHORT -> Only sell if >= 30 pips above previous position entry // modified by Renato
{
OrderSend(Symbol(),OP_SELL,Lots,Bid,slip,Bid+(Stoploss*Point),Bid-(TakeProfit*Point),GetCommentForOrder(),MagicNumber,0,clOpenSell); // modified by Renato
ret = 1;
}
if((iMA(NULL,MA_timeframe,MA_length,0,MODE_SMA,PRICE_LOW,0)*(1-Percent/100))>Ask && Direction!=11 && (Ask<=(LastPrice-(CurrentPipStep*Point)) || MyOrdersTotal()==0)) // Go LONG -> Only buy if >= 30 pips below previous position entry // modified by Renato
{
OrderSend(Symbol(),OP_BUY,Lots,Ask,slip,Ask-(Stoploss*Point),Ask+(TakeProfit*Point),GetCommentForOrder(),MagicNumber,0,clOpenBuy); // modified by Renato
ret = 2;
}
return(ret);
}
} // end of EnterPositionBasedOnHL
//----------------------- CALCULATE AVERAGE OPENING PRICE
double CalculateAverageOpeningPrice(int myTot)
{
int cnt;
double AvePrice;
AvePrice=0;
for(cnt=OrdersTotal() - 1;cnt>=0;cnt--)
{
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
if ( (OrderSymbol()==Symbol()) && (OrderMagicNumber()==MagicNumber) && (OrderComment()==GetCommentForOrder()) ) // hdb - only symbol and magic // modified by Renato
AvePrice=AvePrice+OrderOpenPrice();
}
AvePrice=AvePrice/MathMax(myTot,1); // hdb myTotal
return(AvePrice);
} // end of CalculateAverageOpeningPrice
//----------------------- RECALCULATE STOPLOSS & PROFIT TARGET BASED ON AVERAGE OPENING PRICE
//----------------------- IF NEEDED CHANGE ALL OPEN ORDERS TO THE NEWLY CALCULATED PROFIT TARGET
void ChangeOpenOrders(bool ChangeIt, int myTot, double AvePrice)
{
int cnt, total;
total=OrdersTotal();
for(cnt=0;cnt<total;cnt++)
{
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
if ( (OrderSymbol()==Symbol()) && (OrderMagicNumber()==MagicNumber) && (OrderComment()==GetCommentForOrder()) ) // hdb - only symbol and magic // modified by Renato
{
if(OrderType()==OP_BUY ) // Calculate profit/stop target for long // modified by Renato
{
PriceTarget=AvePrice+(TakeProfit*Point);
Stopper=AvePrice-(((Stoploss*Point)/myTot));
}
if(OrderType()==OP_SELL ) // Calculate profit/stop target for short // modified by Renato
{
PriceTarget=AvePrice-(TakeProfit*Point);
Stopper=AvePrice+(((Stoploss*Point)/myTot));
}
if (ChangeIt) OrderModify(OrderTicket(),0,Stopper,PriceTarget,0,Yellow);//set all positions to averaged levels
}
}
} // end of ChangeOpenOrders
//----------------------- 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.
void KeepTrackOfStopLoss(double AvePrice)
{
int myOrderType, total, cnt;
myOrderType = -1; // hdb
total=OrdersTotal();
for(cnt=0;cnt<total;cnt++)
{
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
if ( (OrderSymbol()==Symbol()) && (OrderMagicNumber()==MagicNumber) && (OrderComment()==GetCommentForOrder()) ) // hdb - only symbol and magic // modified by Renato
{
KeepStopLoss=OrderStopLoss();
myOrderType = OrderType(); // hdb - keep order type
}
}
KeepAverage=AvePrice;
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;
}
} // end of KeepTrackOfStopLoss
int MagicfromSymbol() { // included by Renato
int MagicNumber=0;
for (int i=0; i<5; i++)
{
MagicNumber=MagicNumber*3+StringGetChar(Symbol(),i);
}
MagicNumber=MagicNumber*3+Period();
return(MagicNumber);
} // end of MagicfromSymbol
// Ron added for divergence filter
double divergence(int F_Period, int S_Period, int F_Price, int S_Price, int mypos)
{
int i;
double maF1, maF2, maS1, maS2;
double dv1, dv2;
maF1=iMA(Symbol(),0,F_Period,0,MODE_SMA,F_Price,mypos);
maS1=iMA(Symbol(),0,S_Period,0,MODE_SMA,S_Price,mypos);
dv1=maF1-maS1;
maF2=iMA(Symbol(),0,F_Period,0,MODE_SMA,F_Price,mypos+1);
maS2=iMA(Symbol(),0,S_Period,0,MODE_SMA,S_Price,mypos+1);
if (Use_V63D_Divergence)
{
dv2=((maF1-maS1)-(maF2-maS2));
}
else
{
dv2=maF2-maS2;
}
return(dv1-dv2);
} // end of divergence
bool LossCheck()
{ // included by Renato
int handle = FileOpen(LogFileName(),FILE_CSV|FILE_READ,";");
if (handle>0)
{
int lsteqty = FileReadNumber(handle);
FileClose(handle);
}
else
lsteqty = 0;
if (lsteqty==0)
{
handle = FileOpen(LogFileName(),FILE_CSV|FILE_WRITE,";");
FileWrite(handle,AccountEquity());
FileClose(handle);
}
if (lsteqty-AccountEquity()>=PipsLoss*GetSizeLot())
return(True);
else
return(False);
} // end of LossCheck
string LogFileName()
{ // included by Renato
string stryear = DoubleToStr(Year(),0);
string strmonth = DoubleToStr(Month(),0);
if (StringLen(strmonth)<2) strmonth = "0"+strmonth;
string strday = DoubleToStr(Day(),0);
if (StringLen(strday)<2) strday = "0"+strday;
return(stryear+strmonth+strday+".log");
} // end of LogFileName
void LogWrite(string content)
{
if (writelog==1)
{
int handle = FileOpen(Name_Expert+".log",FILE_CSV|FILE_WRITE,";");
FileSeek(handle,0,SEEK_END);
FileWrite(handle,content);
FileFlush(handle);
FileClose(handle);
}
} // end of LogWrite
int MyOrdersTotal()
{ // included by Renato
int Mytotal=0;
for (int i=0; i<OrdersTotal(); i++)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
if ( (OrderSymbol()==Symbol()) && (OrderMagicNumber()==MagicfromSymbol()) && (OrderComment()==GetCommentForOrder()) )
Mytotal++;
}
return(Mytotal);
} // end of MyOrdersTotal
string GetCommentForOrder() { return(Name_Expert); } // included by Renato
double GetSizeLot() { return(Lots);} // included by Renato
//----------------------- 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
Markdown Formatting Guide
# H1
## H2
### H3
**bold text**
*italicized text*
[title](https://www.example.com)

`code`
```
code block
```
> blockquote
- Item 1
- Item 2
1. First item
2. Second item
---