This script is designed to automatically trade on the currency market using a "grid" strategy, combined with a technical indicator called MACD. Here's a breakdown of how it works:
1. Initial Setup and Customization:
- The script starts by defining several settings that you, as the user, can customize. These settings control how the trading strategy is implemented.
- EAName: A name to identify the script.
- Magic Number: A unique code to identify orders placed by this specific script, helping to distinguish them from other trades you might have.
- Grid Intervals: These determine how far apart the script places orders, creating a grid-like structure. There's an initial interval and then a subsequent interval for following orders.
- Maximum Orders: The maximum number of orders the script will open at any one time.
- Maximum Profit: A profit target, in points. Once this level of profit is reached, the script will close all trades.
- Lots: The size of each trade the script will place.
- Slippage: A buffer (in points) to allow for price fluctuations when placing orders, to help ensure they are filled.
- Minimum/Maximum Opposite Orders: These settings determine when the script should consider closing all trades based on the balance of buy and sell orders. If there are enough orders in the opposite direction with a minimum profit, the script may close everything. Or, if there's simply a large number of orders in the opposite direction (regardless of profit), the script may also close.
- Shutdown Grid: A simple on/off switch to completely stop the script from opening new orders and optionally closing all existing ones.
- Trailing Stop: How far the stop loss will follow the price when it is going in profit.
2. Initial Calculations:
- When the script starts, it checks to see if existing buy and sell entry points have been established
- It does that by going through each open order and checks its price, then sets the buy and sell entry points.
3. Trading Logic:
- MACD Indicator: The script uses the MACD (Moving Average Convergence Divergence) indicator to identify potential trading opportunities. The MACD is a trend-following momentum indicator that shows the relationship between two moving averages of a price.
- Initial Order Placement: If the MACD indicator signals a potential trend change (a "cross"), and there are no existing open orders, the script will place two "pending" orders:
- A "buy stop" order above the current market price. This is an order to buy if the price rises to that level.
- A "sell stop" order below the current market price. This is an order to sell if the price falls to that level.
- Grid Creation: If a buy or sell order is already open, and the total number of orders is less than the maximum allowed, the script will place another pending order. These additional orders are placed at intervals defined by the "subsequent grid interval" setting, creating a grid of potential entry points.
- Trailing stop loss: The script will follow the price in profit with the stop loss, a determined number of points.
4. Order Management and Exit Strategies:
- Profit Monitoring: The script constantly monitors the total profit of all open trades.
- Opposite Order Balance: The script looks at the number of buy and sell orders it has open.
- Automated Closure: The script will close all pending orders and existing positions if:
- The "shutdown grid" setting is turned on.
- The total profit reaches the "maximum profit" target.
- The number of orders in the opposite direction is high enough, and the overall profit is at least break even.
- The number of orders in the opposite direction exceeds the maximum limit.
- The maximum number of orders has been reached.
5. Important Notes:
- Risk: Grid trading strategies can be risky. If the price moves strongly against the grid, it can lead to significant losses.
- Customization: The script's settings are critical to its performance. You need to carefully consider the currency pair, timeframe, and market conditions when setting the parameters.
- Monitoring: While the script automates trading, it's still important to monitor its activity and performance regularly.
In essence, this script tries to profit from both upward and downward price movements by creating a grid of orders around the current price. It uses the MACD indicator to try to identify when to start this grid and has several built-in mechanisms to close the grid when certain conditions are met, in an attempt to limit losses or secure profits.
#property copyright "pengie"
#include <stdlib.mqh>
extern string EAName = "GridMACD";
extern int magic = 1012;
extern int initialGridInterval = 20;
extern int subsequentGridInterval = 15;
extern int maxOrders = 25;
extern int maxProfit = 250;
extern double lots = 0.1;
extern int slippage = 3;
extern int minOppOrders = 3; // Close if breakeven or slight profit if there is opposite orders that are more than or equal to minOppOrders.
extern int maxOppOrders = 10; // Close if there are opposite orders that are more than or equal to maxOppOrders.
extern bool shutdownGrid = false; // If true, will close all orders and not open any new orders.
extern int traling = 30;
double buyEntry = 0.0;
double sellEntry = 0.0;
int curBuy = 0;
int curSell = 0;
int init()
{
magic = GenerateMagicNumber(magic, Symbol(), Period());
EAName = GenerateComment(EAName, magic, Period());
curBuy = CountOrders(Symbol(), magic, OP_BUY);
curSell = CountOrders(Symbol(), magic, OP_SELL);
return (0);
}
int deinit()
{
return (0);
}
int start()
{
int ticket;
if (buyEntry==0.0 || sellEntry==0.0 || buyEntry==10000.0)
{
double minBuyEntry = 10000;
double maxSellEntry = 0;
for (ticket=GetFirstTicketByMagic(magic); ticket!=0; ticket=GetNextTicket())
{
OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES);
switch (OrderType())
{
case OP_BUY:{
if(OrderStopLoss() < Bid - (traling*Point))
moveSL(OrderTicket(),Bid - traling*Point);
if (OrderOpenPrice()<minBuyEntry) minBuyEntry = OrderOpenPrice();
break;
}
case OP_SELL:
{
if(OrderStopLoss() > Bid + (traling*Point))
moveSL(OrderTicket(),Bid + traling*Point);
if (OrderOpenPrice()>maxSellEntry) maxSellEntry = OrderOpenPrice();
break;
}
}
}
buyEntry = minBuyEntry - initialGridInterval*Point;
sellEntry = maxSellEntry + initialGridInterval*Point;
}
//===these are the original MACD settings for use as default on suggested 1hr timeframe charts:
// double prevMACD = iMACD(Symbol(), 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 2);
// double curMACD = iMACD(Symbol(), 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 1);
//===end of comment.
double prevMACD = iMACD(Symbol(), 0, 720, 1560, 9, PRICE_CLOSE, MODE_MAIN, 2);
double curMACD = iMACD(Symbol(), 0, 720, 1560, 9, PRICE_CLOSE, MODE_MAIN, 1);
// double prevMACD = iMACD(Symbol(), 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 2);
// double curMACD = iMACD(Symbol(), 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 1);
int totalOrders = CountOrdersIfMagic(magic);
if (((prevMACD<0 && curMACD>0) || (prevMACD>0 && curMACD<0)) && totalOrders==0 && !shutdownGrid)
{
buyEntry = Ask;
curBuy = 1;
OrderSend(Symbol(), OP_BUYSTOP, lots, buyEntry+initialGridInterval*Point, slippage, 0, 0, EAName, magic , 0, Green);
sellEntry = Bid;
curSell = 1;
OrderSend(Symbol(), OP_SELLSTOP, lots, sellEntry-initialGridInterval*Point, slippage, 0, 0, EAName, magic , 0, Red);
}
if (CountOrders(Symbol(), magic, OP_BUY)>0 && CountOrders(Symbol(), magic, OP_BUYSTOP)==0 && totalOrders<maxOrders)
{
if (OrderSend(Symbol(), OP_BUYSTOP, lots, buyEntry+initialGridInterval*Point+curBuy*subsequentGridInterval*Point, slippage, 0, 0, EAName, magic , 0, Green)==-1)
{
Print(Symbol(), ", Buystop=", buyEntry+initialGridInterval*Point+curBuy*subsequentGridInterval*Point);
}
curBuy++;
}
if (CountOrders(Symbol(), magic, OP_SELL)>0 && CountOrders(Symbol(), magic, OP_SELLSTOP)==0 && totalOrders<maxOrders)
{
if (OrderSend(Symbol(), OP_SELLSTOP, lots, sellEntry-initialGridInterval*Point-curSell*subsequentGridInterval*Point, slippage, 0, 0, EAName, magic , 0, Red)==-1)
{
Print(Symbol(), ", Sellstop=", sellEntry-initialGridInterval*Point-curSell*subsequentGridInterval*Point);
}
curSell++;
}
int totalPips = 0;
double totalProfits = 0.0;
for (ticket=GetFirstTicketByMagic(magic); ticket!=0; ticket=GetNextTicket())
{
OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES);
switch (OrderType())
{
case OP_BUY:
totalPips += (Bid-OrderOpenPrice())/Point;
totalProfits += OrderProfit();
break;
case OP_SELL:
totalPips += (OrderOpenPrice()-Ask)/Point;
totalProfits += OrderProfit();
break;
}
}
Comment(StringConcatenate("Total pips=",totalPips,"\nTotal profits=",totalProfits,"\n"));
int buyOrders = CountOrders(Symbol(), magic, OP_BUY);
int sellOrders = CountOrders(Symbol(), magic, OP_SELL);
int oppOrders = 0;
if (buyOrders > sellOrders) oppOrders = sellOrders;
else oppOrders = buyOrders;
int stopGrid = 0;
if ((oppOrders>=minOppOrders && totalPips>=0) || oppOrders>=maxOppOrders) stopGrid = 1;
if (shutdownGrid || totalPips>=maxProfit || stopGrid || totalOrders==maxOrders)
{
DeleteAllPendingOrders(magic);
CloseAllOrders(magic);
curBuy = 0;
curSell = 0;
buyEntry = 0.0;
sellEntry = 0.0;
}
return (0);
}
int DeleteAllPendingOrders(int magic)
{
for (int ticket=GetFirstTicketByMagic(magic); ticket!=0; ticket=GetNextTicket())
{
OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES);
switch (OrderType())
{
case OP_BUYSTOP:
case OP_SELLSTOP:
OrderDelete(ticket);
break;
}
}
return (0);
}
int CloseAllOrders(int magic)
{
for (int ticket=GetFirstTicketByMagic(magic); ticket!=0; ticket=GetNextTicket())
{
OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES);
switch (OrderType())
{
case OP_BUY:
OrderClose(ticket, OrderLots(), Bid, slippage, CLR_NONE);
break;
case OP_SELL:
OrderClose(ticket, OrderLots(), Ask, slippage, CLR_NONE);
break;
}
}
return (0);
}
int GenerateMagicNumber(int seed, string symbol, int timeFrame)
{
int isymbol = 0;
if (symbol == "EURUSD") isymbol = 1;
else if (symbol == "GBPUSD") isymbol = 2;
else if (symbol == "USDJPY") isymbol = 3;
else if (symbol == "USDCHF") isymbol = 4;
else if (symbol == "AUDUSD") isymbol = 5;
else if (symbol == "USDCAD") isymbol = 6;
else if (symbol == "EURGBP") isymbol = 7;
else if (symbol == "EURJPY") isymbol = 8;
else if (symbol == "EURCHF") isymbol = 9;
else if (symbol == "EURAUD") isymbol = 10;
else if (symbol == "EURCAD") isymbol = 11;
else if (symbol == "GBPUSD") isymbol = 12;
else if (symbol == "GBPJPY") isymbol = 13;
else if (symbol == "GBPCHF") isymbol = 14;
else if (symbol == "GBPAUD") isymbol = 15;
else if (symbol == "GBPCAD") isymbol = 16;
return (StrToInteger(StringConcatenate(seed, isymbol, timeFrame)));
}
string GenerateComment(string EAName, int magic, int timeFrame)
{
return (StringConcatenate(EAName, "-", magic, "-", timeFrame));
}
int CountOrders(string symbol="", int magicNumber=-1, int cmd=-1)
{
int totalOrders = 0;
int maxOrders = OrdersTotal();
for (int i=0; i<maxOrders; i++)
{
OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
if ((symbol=="" || OrderSymbol()==symbol) &&
(magicNumber==-1 || OrderMagicNumber()==magicNumber) &&
(cmd==-1 || OrderType()==cmd))
{
totalOrders++;
}
}
return (totalOrders);
}
int CountOrdersIfMagic(int magicNumber)
{
return (CountOrders("", magicNumber, -1));
}
int t_index = 0;
string t_symbol = "";
int t_magicNumber = -1;
int t_cmd = -1;
int GetFirstTicketByMagic(int magicNumber)
{
return (GetFirstTicket("", magicNumber, -1));
}
int GetFirstTicket(string symbol="", int magicNumber=-1, int cmd=-1)
{
t_symbol = symbol;
t_magicNumber = magicNumber;
t_cmd = cmd;
int maxOrders = OrdersTotal();
for (t_index=maxOrders-1; t_index>=0; t_index--)
{
OrderSelect(t_index, SELECT_BY_POS, MODE_TRADES);
if ((t_symbol=="" || OrderSymbol()==t_symbol) &&
(t_magicNumber==-1 || OrderMagicNumber()==t_magicNumber) &&
(t_cmd==-1 || OrderType()==t_cmd))
{
return (OrderTicket());
}
}
return (0);
}
int GetNextTicket()
{
for (t_index--; t_index>=0; t_index--)
{
OrderSelect(t_index, SELECT_BY_POS, MODE_TRADES);
if ((t_symbol=="" || OrderSymbol()==t_symbol) &&
(t_magicNumber==-1 || OrderMagicNumber()==t_magicNumber) &&
(t_cmd==-1 || OrderType()==t_cmd))
{
return (OrderTicket());
}
}
return (0);
}
bool moveSL(int ticket,double stoploss)
{
if(!IsTradeAllowed())
return (false);
if(MathAbs(Ask-stoploss)/Point < MarketInfo(Symbol(),MODE_STOPLEVEL))
{
Print("STOP LOSS too close ",Bid," SL ",stoploss);
return(false);
}
int error;
int MAXRETRIES = 5;
int retries = 0;
while(!OrderModify(ticket,OrderOpenPrice(), stoploss, OrderTakeProfit(), 0,CLR_NONE))
{
error = GetLastError();
if(error>1)
Print("MoveSL failed with error #",ErrorDescription(error)," CurrentSL ",OrderStopLoss()," NewSL ",stoploss);
Sleep(1000);
RefreshRates();
if(retries >= MAXRETRIES)
return(false);
else
retries++;
}
}
Comments