This script is designed to automatically trade on the Forex market using the MetaTrader platform. Here's a breakdown of what it does:
Overall Strategy:
The script uses a trading strategy based on the "Least Squares Moving Average" (LSMA). Imagine the LSMA as a line that tries to predict the future price based on past price data. The script looks at how steeply this line is angled to make decisions about buying or selling. If the angle is steep enough, it signals a potential trade.
Initialization (init):
- When the script starts, it sets up a special identification number (Magic Number) to keep track of its own trades. It also creates a unique name (setup) for itself, combining the script's name, the currency pair it's trading on, and the timeframe it's analyzing.
Trading Logic (start):
- Check for New Bar: It makes sure it only runs its main logic once for each new period (e.g., every 5 minutes, every hour). This is controlled by the "UseOncePerBar" setting.
- Manage Open Positions: It checks if there are any existing trades that need to be closed (exited). It uses a function to determine if an open position should be closed.
- Check Open Positions: It counts how many trades are currently open for the specific currency pair.
- One Trade at a Time: It's designed to only have one trade open at a time for each currency pair. If a trade is already open, it skips the rest of the process until the next period/bar.
- Determine Trade Size: It decides how many "lots" (units of currency) to trade. This is based on either a fixed amount set by the user or a percentage of the account balance.
- Check for Buy Signal: It checks if the LSMA angle is steep enough to trigger a "buy" order, using a set threshold.
- Open Buy Order: If the buy signal is triggered, the script attempts to open a buy order with a specified trade size, stop-loss (a price at which to automatically close the trade if it goes against you), and take-profit (a price at which to automatically close the trade when it hits a profit target).
- Check for Sell Signal: Similarly, it checks for a sell signal based on the LSMA angle.
- Open Sell Order: If the sell signal is triggered, it opens a sell order.
LSMA Calculation (LSMA):
- This is the core of the strategy. It calculates the Least Squares Moving Average for a given period (number of past data points). Essentially, it draws a line that best fits the price data over that period. The final point of this line is returned.
Angle/Difference Calculation (MA_Dif):
- This function calculates the steepness of the LSMA line. It compares the LSMA value at two different points and figures out the difference between them and is then used to determine how steep this difference is.
Entry/Exit Conditions (CheckEntryCondition, CheckExitCondition):
- These functions determine when to enter a trade (buy or sell) and when to exit a trade. The "EntryDif_Threshold" parameter controls how steep the LSMA angle needs to be to enter a trade. The "ExitDif_Threshold" parameter controls how shallow the LSMA angle needs to be to exit a trade.
Money Management (GetLots, LotsOptimized):
- The script has money management features to automatically adjust the trade size based on the account balance or free margin (available money in the account). This aims to control risk.
Order Management (OpenBuyOrder, OpenSellOrder, HandleOpenPositions, CloseOrder):
- These functions handle the actual opening and closing of trades. They communicate with the MetaTrader platform to place orders, set stop-loss and take-profit levels, and close trades when the exit conditions are met.
Utility Functions:
- There are also utility functions to convert currency pair symbols and timeframes into numerical values for internal calculations.
//+------------------------------------------------------------------+
//| |
//| LSMA_EA |
//| |
//| Written by Robert Hill aka MrPip for StrategyBuilder FX group |
//| |
//+------------------------------------------------------------------+
#property copyright "Robert Hill"
#property link "None"
#include <stdlib.mqh>
extern string Expert_Name = "LSMA_EA";
extern int MagicNumberBase = 200000;
extern string UserComment = "LSMA_EA";
extern string in="---Indicator settings---";
extern string p = "--Applied Price Types--";
extern string p0 = " 0 = close";
extern string p1 = " 1 = open";
extern string p2 = " 2 = high";
extern string p3 = " 3 = low";
extern string p4 = " 4 = median(high+low)/2";
extern string p5 = " 5 = typical(high+low+close)/3";
extern string p6 = " 6 = weighted(high+low+close+close)/4";
extern int LSMA_Period=7;
extern int LSMA_AppliedPrice = 0;
extern double EntryDif_Threshold=10; // What size angle to trigger a trade
extern double ExitDif_Threshold=2;
extern int PrevMAShift=3;
extern int CurMAShift=1;
//+---------------------------------------------------+
//|Money Management |
//+---------------------------------------------------+
extern string mm = "---Money Management---";
extern double Lots=1.0;
extern double MaxLots = 100;
extern bool UseMoneyManagement = true; // Change to false to shutdown money management controls.
extern bool BrokerIsIBFX = false;
extern string m1="Set mini and micro to false for standard account";
extern bool AccountIsMini = false;
extern bool AccountIsMicro = false;
extern string fm="UseFreeMargin = false to use Account Balance";
extern bool UseFreeMargin = false;
extern double TradeSizePercent = 10; // Change to whatever percent of equity you wish to risk.
extern bool BrokerPermitsFractionalLots = true;
//+---------------------------------------------------+
//|Profit controls |
//+---------------------------------------------------+
extern string st6 = "--Profit Controls--";
extern double StopLoss=0;
extern double TakeProfit=0;
extern int Slippage=3;
extern string db0="---Flag to run start once per bar---";
extern string db1=" Set to false when using stoploss";
extern string db2=" takeprofit or trailing stop";
extern bool UseOncePerBar = true;
//+---------------------------------------------------+
//|General controls |
//+---------------------------------------------------+
int MagicNumber=0;
string setup;
int TradesInThisSymbol = 0;
double mLots=0;
datetime timeprev = 0;
//+---------------------------------------------------+
//| Indicator values for signals and filters |
//| Add or Change to test your system |
//+---------------------------------------------------+
//+------------------------------------------------------------------+
//| Calculate MagicNumber, setup comment and assign RSI Period |
//| |
//+------------------------------------------------------------------+
int init()
{
MagicNumber = MagicNumberBase + func_Symbol2Val(Symbol())*100 + func_TimeFrame_Const2Val(Period());
setup=Expert_Name + Symbol() + "_" + func_TimeFrame_Val2String(func_TimeFrame_Const2Val(Period()));
setup = UserComment;
return(0);
}
int deinit()
{
return(0);
}
//+------------------------------------------------------------------------+
//| LSMA - Least Squares Moving Average function calculation |
//| LSMA_In_Color Indicator plots the end of the linear regression line |
//+------------------------------------------------------------------------+
double LSMA(int Rperiod, int prMode, int shift)
{
int i, myShift;
double sum, pr;
int length;
double lengthvar;
double tmp;
double wt;
length = Rperiod;
sum = 0;
for(i = length; i >= 1 ; i--)
{
lengthvar = length + 1;
lengthvar /= 3;
tmp = 0;
myShift = length - i + shift;
switch (prMode)
{
case 0: pr = iClose(NULL,0,myShift);break;
case 1: pr = iOpen(NULL,0,myShift);break;
case 2: pr = iHigh(NULL,0,myShift);break;
case 3: pr = iLow(NULL,0,myShift);break;
case 4: pr = (iHigh(NULL,0,myShift) + iLow(NULL,0,myShift))/2;break;
case 5: pr = (iHigh(NULL,0,myShift) + iLow(NULL,0,myShift) + iClose(NULL,0,myShift))/3;break;
case 6: pr = (iHigh(NULL,0,myShift) + iLow(NULL,0,myShift) + iClose(NULL,0,myShift) + iClose(NULL,0,myShift))/4;break;
}
tmp = ( i - lengthvar)*pr;
sum+=tmp;
}
wt = sum*6/(length*(length+1));
wt = MathFloor(wt/Point)*Point;
return(wt);
}
double MA_Dif( int PrevShift, int CurShift)
{
double fDif, mFactor;
double fCurMA, fPrevMA;
string Sym;
int ShiftDif;
mFactor = 100000.0;
Sym = StringSubstr(Symbol(),3,3);
if (Sym == "JPY") mFactor = 1000.0;
ShiftDif = PrevShift-CurShift;
mFactor /= ShiftDif;
fCurMA=LSMA(LSMA_Period,LSMA_AppliedPrice,CurShift);
fPrevMA=LSMA(LSMA_Period,LSMA_AppliedPrice,PrevShift);
fDif = mFactor * (fCurMA - fPrevMA)/2.0;
return(fDif);
}
//+------------------------------------------------------------------+
//| CheckExitCondition |
//| Check if AngleSep cross 0 line |
//+------------------------------------------------------------------+
bool CheckExitCondition(int cmd)
{
double maDif;
maDif = MA_Dif(PrevMAShift, CurMAShift);
switch (cmd)
{
case OP_BUY : if (maDif < ExitDif_Threshold) return(true);
break;
case OP_SELL : if (maDif > -ExitDif_Threshold) return(true);
}
return(false);
}
//+------------------------------------------------------------------+
//| CheckEntryCondition |
//| Check if separation on LSMA pair |
//+------------------------------------------------------------------+
bool CheckEntryCondition(int cmd)
{
double maDif;
maDif = MA_Dif(PrevMAShift, CurMAShift);
switch (cmd)
{
case OP_BUY : if (maDif > EntryDif_Threshold) return(true);
break;
case OP_SELL : if (maDif < -EntryDif_Threshold) return(true);
}
return(false);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Start |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
int start()
{
// Only run once per completed bar
if (UseOncePerBar == true)
{
if(timeprev==Time[0]) return(0);
timeprev = Time[0];
}
//+------------------------------------------------------------------+
//| Check for Open Position |
//+------------------------------------------------------------------+
HandleOpenPositions();
// Check if any open positions were not closed
TradesInThisSymbol = CheckOpenPositions();
// Only allow 1 trade per Symbol
if(TradesInThisSymbol > 0) {
return(0);}
mLots = GetLots();
if(CheckEntryCondition(OP_BUY) == true)
{
OpenBuyOrder(mLots, StopLoss,TakeProfit, Slippage, setup, MagicNumber, Green);
return(0);
}
if(CheckEntryCondition(OP_SELL) == true)
{
OpenSellOrder(mLots, StopLoss,TakeProfit, Slippage, setup, MagicNumber, Red);
}
return(0);
}
//+------------------------------------------------------------------+
//| Get number of lots for this trade |
//+------------------------------------------------------------------+
double GetLots()
{
double lot;
if(UseMoneyManagement == true)
{
lot = LotsOptimized();
}
else
{
lot = Lots;
}
// Use at least 1 micro lot
if (AccountIsMicro == true)
{
if (lot < 0.01) lot = 0.01;
if (lot > MaxLots) lot = MaxLots * 100;
if (BrokerIsIBFX == true) lot = lot * 10;
return(lot);
}
// Use at least 1 mini lot
if(AccountIsMini == true)
{
if (lot < 0.1) lot = 0.1;
if (lot > MaxLots) lot = MaxLots;
if (BrokerIsIBFX == true) lot = lot * 10;
return(lot);
}
// Standard account
if( BrokerPermitsFractionalLots == false)
{
if (lot >= 1.0) lot = MathFloor(lot); else lot = 1.0;
}
if (lot > MaxLots) lot = MaxLots;
return(lot);
}
//+------------------------------------------------------------------+
//| Calculate optimal lot size |
//+------------------------------------------------------------------+
double LotsOptimized()
{
double lot=Lots;
//---- select lot size
if (UseFreeMargin == true)
lot=NormalizeDouble(MathFloor(AccountFreeMargin()*TradeSizePercent/10000)/10,1);
else
lot=NormalizeDouble(MathFloor(AccountBalance()*TradeSizePercent/10000)/10,1);
// Check if mini or standard Account
if(AccountIsMini == true)
{
lot = MathFloor(lot*10)/10;
}
return(lot);
}
//+------------------------------------------------------------------+
//| OpenBuyOrder |
//| If Stop Loss or TakeProfit are used the values are calculated |
//| for each trade |
//+------------------------------------------------------------------+
int OpenBuyOrder(double mLots, double mStopLoss, double mTakeProfit, int mSlippage, string mComment, int mMagic, color mColor)
{
int err,ticket;
double myPrice, myStopLoss = 0, myTakeProfit = 0;
RefreshRates();
myStopLoss = StopLong(Bid,mStopLoss);
myTakeProfit = TakeLong(Bid,mTakeProfit);
// Normalize all price / stoploss / takeprofit to the proper # of digits.
if (Digits > 0)
{
myPrice = NormalizeDouble( Ask, Digits);
myStopLoss = NormalizeDouble( myStopLoss, Digits);
myTakeProfit = NormalizeDouble( myTakeProfit, Digits);
}
ticket=OrderSend(Symbol(),OP_BUY,mLots,myPrice,mSlippage,myStopLoss,myTakeProfit,mComment,mMagic,0,mColor);
if (ticket > 0)
{
if (OrderSelect( ticket,SELECT_BY_TICKET, MODE_TRADES) )
{
Print("BUY order opened : ", OrderOpenPrice( ));
// ModifyOrder(ticket,OrderOpenPrice( ), OrderStopLoss(), myTakeProfit);
}
}
else
{
err = GetLastError();
if(err==0)
{
return(ticket);
}
else
{
if(err==4 || err==137 ||err==146 || err==136) //Busy errors
{
Sleep(5000);
}
else //normal error
{
Print("Error opening BUY order [" + setup + "]: (" + err + ") " + ErrorDescription(err));
}
}
}
return(ticket);
}
//+------------------------------------------------------------------+
//| OpenSellOrder |
//| If Stop Loss or TakeProfit are used the values are calculated |
//| for each trade |
//+------------------------------------------------------------------+
void OpenSellOrder(double mLots, double mStopLoss, double mTakeProfit, int mSlippage, string mComment, int mMagic, color mColor)
{
int err, ticket;
double myPrice, myStopLoss = 0, myTakeProfit = 0;
RefreshRates();
myStopLoss = StopShort(Ask,mStopLoss);
myTakeProfit = TakeShort(Ask,mTakeProfit);
// Normalize all price / stoploss / takeprofit to the proper # of digits.
if (Digits > 0)
{
myPrice = NormalizeDouble( Bid, Digits);
myStopLoss = NormalizeDouble( myStopLoss, Digits);
myTakeProfit = NormalizeDouble( myTakeProfit, Digits);
}
ticket=OrderSend(Symbol(),OP_SELL,mLots,myPrice,mSlippage,myStopLoss,myTakeProfit,mComment,mMagic,0,mColor);
if (ticket > 0)
{
if (OrderSelect( ticket,SELECT_BY_TICKET, MODE_TRADES) )
{
Print("Sell order opened : ", OrderOpenPrice());
// ModifyOrder(ticket,OrderOpenPrice( ), OrderStopLoss(), myTakeProfit);
}
}
else
{
err = GetLastError();
if(err==0)
{
return(ticket);
}
else
{
if(err==4 || err==137 ||err==146 || err==136) //Busy errors
{
Sleep(5000);
}
else //normal error
{
Print("Error opening Sell order [" + mComment + "]: (" + err + ") " + ErrorDescription(err));
}
}
}
return(ticket);
}
double StopLong(double price,int stop)
{
if(stop==0)
return(0);
else
return(price-(stop*Point));
}
double StopShort(double price,int stop)
{
if(stop==0)
return(0);
else
return(price+(stop*Point));
}
double TakeLong(double price,int take)
{
if(take==0)
return(0);
else
return(price+(take*Point));
}
double TakeShort(double price,int take)
{
if(take==0)
return(0);
else
return(price-(take*Point));
}
//+------------------------------------------------------------------+
//| Handle Open Positions |
//| Check if any open positions need to be closed or modified |
//+------------------------------------------------------------------+
int HandleOpenPositions()
{
int cnt;
for(cnt=OrdersTotal()-1;cnt>=0;cnt--)
{
OrderSelect (cnt, SELECT_BY_POS, MODE_TRADES);
if ( OrderSymbol() != Symbol()) continue;
if ( OrderMagicNumber() != MagicNumber) continue;
if(OrderType() == OP_BUY)
{
if (CheckExitCondition(OP_BUY) == true)
{
CloseOrder(OrderTicket(),OrderOpenPrice(),OrderLots(),OP_BUY);
}
}
if(OrderType() == OP_SELL)
{
if (CheckExitCondition(OP_SELL) == true)
{
CloseOrder(OrderTicket(),OrderOpenPrice(), OrderLots(),OP_SELL);
}
}
}
}
//+------------------------------------------------------------------+
//| Check Open Position Controls |
//+------------------------------------------------------------------+
int CheckOpenPositions()
{
int cnt, total;
int NumTrades;
NumTrades = 0;
total=OrdersTotal();
for(cnt=OrdersTotal()-1;cnt>=0;cnt--)
{
OrderSelect (cnt, SELECT_BY_POS, MODE_TRADES);
if ( OrderSymbol() != Symbol()) continue;
if ( OrderMagicNumber() != MagicNumber) continue;
if(OrderType() == OP_BUY ) NumTrades++;
if(OrderType() == OP_SELL ) NumTrades++;
}
return (NumTrades);
}
int CloseOrder(int ticket, double op, double numLots,int cmd)
{
int CloseCnt, err, digits;
double myPrice;
string olStr, bidStr, askStr;
RefreshRates();
if (cmd == OP_BUY) myPrice = Bid;
if (cmd == OP_SELL) myPrice = Ask;
if (Digits > 0) myPrice = NormalizeDouble( myPrice, Digits);
olStr = DoubleToStr(numLots,2);
bidStr = DoubleToStr(Bid, Digits);
askStr = DoubleToStr(Ask, Digits);
// try to close 3 Times
CloseCnt = 0;
while (CloseCnt < 3)
{
if (OrderClose(ticket,numLots,myPrice,Slippage,Violet) == false)
{
err=GetLastError();
Print(CloseCnt," Error closing order : (", err , ") " + ErrorDescription(err));
if (err > 0) CloseCnt++;
}
else
{
CloseCnt = 3;
}
}
}
int func_Symbol2Val(string symbol)
{
string mySymbol = StringSubstr(symbol,0,6);
if(mySymbol=="AUDCAD") return(1);
if(mySymbol=="AUDJPY") return(2);
if(mySymbol=="AUDNZD") return(3);
if(mySymbol=="AUDUSD") return(4);
if(mySymbol=="CHFJPY") return(5);
if(mySymbol=="EURAUD") return(6);
if(mySymbol=="EURCAD") return(7);
if(mySymbol=="EURCHF") return(8);
if(mySymbol=="EURGBP") return(9);
if(mySymbol=="EURJPY") return(10);
if(mySymbol=="EURUSD") return(11);
if(mySymbol=="GBPCHF") return(12);
if(mySymbol=="GBPJPY") return(13);
if(mySymbol=="GBPUSD") return(14);
if(mySymbol=="NZDJPY") return(15);
if(mySymbol=="NZDUSD") return(16);
if(mySymbol=="USDCAD") return(17);
if(mySymbol=="USDCHF") return(18);
if(mySymbol=="USDJPY") return(19);
Comment("unexpected Symbol");
return(999);
}
//+------------------------------------------------------------------+
//| Time frame interval appropriation function |
//+------------------------------------------------------------------+
int func_TimeFrame_Const2Val(int Constant ) {
switch(Constant) {
case 1: // M1
return(1);
case 5: // M5
return(2);
case 15:
return(3);
case 30:
return(4);
case 60:
return(5);
case 240:
return(6);
case 1440:
return(7);
case 10080:
return(8);
case 43200:
return(9);
}
}
//+------------------------------------------------------------------+
//| Time frame string appropriation function |
//+------------------------------------------------------------------+
string func_TimeFrame_Val2String(int Value ) {
switch(Value) {
case 1: // M1
return("PERIOD_M1");
case 2: // M1
return("PERIOD_M5");
case 3:
return("PERIOD_M15");
case 4:
return("PERIOD_M30");
case 5:
return("PERIOD_H1");
case 6:
return("PERIOD_H4");
case 7:
return("PERIOD_D1");
case 8:
return("PERIOD_W1");
case 9:
return("PERIOD_MN1");
default:
return("undefined " + Value);
}
}
//+------------------------------------------------------------------+
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
---