//:adic: double standardLot = 1; // Value of the take profit, on a chart: // dTp = Bid - dTakeProfit; // or // dTp = Ask + dTakeProfit; double dTp = 0; // Used to deside, if this is the beginning of a new bar. int nBars; // An expert runs 10 * nMagic seconds after the bar end // This way we reduce competition between experts and // avoid deadlocks int nDelaySeconds = 10; // Slippage int nSlip = 5; // Used to calculate the size of a lot, if bUseMm is // set to 0, the lot size is always 0.1 // We strongly recommend that you use your own // money management system, this is just a simple // placeholder double dProfit = 0; // Used to figure out if we have enough money to trade // We strongly recommend that you use your own // money management system, this is just a simple // placeholder double dInitAmount = 1000; // A standart lot size double dLotSize = 1; //lotSize; //0.1; // Magic number. Unique for a combination of expert - // currency - timeframe int nMagic = 0; // Report is not part of the trading, but only a // function to write transactions to a file. We // do it once a day bool bReportDone = false; // This variable contains 0, or the magic number // of currently trading expert string strTradeSemaphore = "TradeSemaphore"; // ------ /* This function creates a file with the name, that is unique for an expert-currency-timeframe, and writes trading history in it. Note, that we only same the profit of operation(s) here. If you want to save additional information, like order open dates or swaps, you need to modify the code below. The strFileName contains the file name prefix, usually it is the name of an expert. */ void Report(string strFileName, int nMagic, bool& bReportDone) { // Do it only during run time trading if(IsTesting()) return; // Save once a day, at 0 hours nMagic / 2 minutes if(Hour() == 0 && Minute() >= nMagic / 2) { // Make sure it only happens once, even // if we have 2 or more ticks satisfying // the condition above if(bReportDone == false) { int hFile = FileOpen(strFileName + "_" + Symbol() + "_" + Period() + ".rpt", FILE_BIN | FILE_WRITE, ','); string str = "CloseDateTime,Buy,Sell\r\n"; FileWriteString(hFile, str, StringLen(str)); // Write the entire trading history to the // file. // Note: make sure all trading history is // available (MT4-Terminal-Account History) for(int nCnt = 0; nCnt < HistoryTotal(); nCnt++) { OrderSelect(nCnt, SELECT_BY_POS, MODE_HISTORY); if(OrderMagicNumber() == nMagic && OrderType() <= OP_SELL && OrderSymbol() == Symbol()) { str = TimeToStr(OrderCloseTime(), TIME_DATE|TIME_MINUTES); if(OrderType() == OP_BUY) str = str + "," + OrderProfit() + ",0"; else str = str + ",0," + OrderProfit(); str = str + " \r\n "; FileWriteString(hFile, str, StringLen(str)); } } FileFlush(hFile); FileClose(hFile); bReportDone = true; } } else if(Hour() != 0) // Reset the flag bReportDone = false; } // ------ // A simple placeholder for a real money management // algorythm. In expert, set bUseMm to false or // true. A default lot size is 0.1. double GetLotSize(double dInitFraction = 0.1, double dProfitFraction = 0.1) { double dLot = standardLot; //0.1; if(bUseMm) { dLot = (dInitFraction * dInitAmount + dProfitFraction * dProfit) / 1000; dLot = MathFloor(dLot * 10) / 10; if(dLot < standardLot) dLot = standardLot; } return(dLot); } // ------ /* Buy() and Sell() functions use dStopLoss and dTakeProfit, that are assigned in the expert. We are going to try to open an order for 10 times, because sometimes the server or the Internet connection fails. Note the call to SaveComment() function. If the order was not opened, when you think it should be, you may check the corresponding file in experts/files directory. It will give you the error number and some additional hints on what happened. Note, that it is possible to use more than one magic number with the same expert-currency-timeframe, in which case you need to explicitely pass it as a nMagicNumber parameter. dLot is a coefficient (a multiplier) used to increase the lot size, usually, you will use it for trading contests, or if you are overconfident in the performance (drawdowns) of your expert. */ int Sell(string strExpertName, double dLot = 0, int nMagicNumber = -1) { int nResult = -1; if(nMagicNumber == -1) nMagicNumber = nMagic; if(dLot == 0) dLotSize = GetLotSize(); else dLotSize = dLot * GetLotSize(); // Note, that this is a simple placeholder for a // real money management algorythm. We will discuss // money management in a separate article. if(AccountFreeMargin() < dLotSize * dInitAmount || AccountFreeMargin() < 500) return(nResult); double dTp; if(dTakeProfit == 0) dTp = 0; else dTp = Bid - dTakeProfit; // We try 10 times for(int nTry = 0; nTry < 10; nTry++) { SaveComment(" \r\n " + Day() + "." + Month() + "." + Year() + " " + Hour() + ":" + Minute() + ":" + Seconds()); SaveComment(" Trying to sell, attempt " + nTry + " \r\n "); SaveComment("\r\nAsk: " + Ask + ", StopLoss: " + dStopLoss + ", TakeProfit: " + dTakeProfit + " \r\n "); double vAdicStopLoss; if (dStopLoss == 0) vAdicStopLoss = 0; else { if (dStopLoss >= 1) { vAdicStopLoss = Bid + dStopLoss*Point; } else vAdicStopLoss = Bid + dStopLoss; } nResult = OrderSend(Symbol(), OP_SELL, dLotSize, Ask, nSlip, vAdicStopLoss, dTp, strExpertName, nMagicNumber, 0, Aqua); //nResult = OrderSend(Symbol(), OP_SELL, dLotSize, Bid, nSlip, Bid + dStopLoss*Point, dTp, strExpertName, nMagicNumber, 0, OrangeRed); // Wait 10 seconds before the next try. Sleep(10000); // If trade failed, refresh rates and try again if(nResult != -1) { SaveComment(" successfull\r\n"); break; } else { SaveComment(" failed, error " + GetLastError() + " \r\n "); RefreshRates(); } } // If all 10 attempts failed, write additional info // to the report file if(nResult == -1) { int nError = GetLastError(); Alert(strExpertName + " sell error: " + nError + " \r\n " + Bid + ", " + vAdicStopLoss + ", " + dTp); SaveComment(strExpertName + " sell error: " + nError + " \r\n " + Bid + ", " + vAdicStopLoss + ", " + dTp); } return(nResult); } // ------ // The structure of Buy() is similar to Sell(), // see Sell() for comments int Buy(string strExpertName, double dLot = 0, int nMagicNumber = -1) { int nResult = -1; if(nMagicNumber == -1) nMagicNumber = nMagic; if(dLot == 0) dLotSize = GetLotSize(); else dLotSize = dLot * GetLotSize(); if(AccountFreeMargin() < dLotSize * dInitAmount || AccountFreeMargin() < 500) return(nResult); double dTp; if(dTakeProfit == 0) dTp = 0; else dTp = Ask + dTakeProfit; for(int nTry = 0; nTry < 10; nTry++) { SaveComment(" \r\n " + Day() + "." + Month() + "." + Year() + " " + Hour() + ":" + Minute() + ":" + Seconds()); SaveComment(" Trying to buy, attempt " + nTry + " \r\n "); SaveComment("\r\nBid: " + Bid + ", StopLoss: " + dStopLoss + ", TakeProfit: " + dTakeProfit); double vAdicStopLoss; if (dStopLoss == 0) vAdicStopLoss = 0; else { if (dStopLoss >= 1) { vAdicStopLoss = Ask - dStopLoss*Point; } else vAdicStopLoss = Ask - dStopLoss; } nResult = OrderSend(Symbol(), OP_BUY, dLotSize, Ask, nSlip, vAdicStopLoss, dTp, strExpertName, nMagicNumber, 0, Aqua); //:adic:nResult = OrderSend(Symbol(), OP_BUY, dLotSize, Ask, nSlip, Ask - dStopLoss*Point, dTp, strExpertName, nMagicNumber, 0, Aqua); Sleep(10000); if(nResult != -1) { SaveComment(" successfull\r\n"); break; } else { SaveComment(" failed, error " + GetLastError() + " \r\n "); RefreshRates(); } } if(nResult == -1) { int nError = GetLastError(); Alert(strExpertName + " buy error: " + nError + " \r\n " + Ask + ", " + vAdicStopLoss + ", " + dTp); SaveComment(strExpertName + " buy error: " + nError + " \r\n " + Ask + ", " + vAdicStopLoss + ", " + dTp); } return(nResult); } // ------ /* We use this function to modify orders. It uses dTrailingStop, assigned in the expert. When the price goes away from the stop loss more than 5 points (from the stop loss to stop loss + 5 points), we move the stop closer to the price. */ void ModifyOrders() { if(dTrailingStop == 0) return; // Scan all opened orders, looking for an // order with "our" magic number for(int nCnt = 0; nCnt < OrdersTotal(); nCnt++) { OrderSelect(nCnt, SELECT_BY_POS, MODE_TRADES); if(OrderMagicNumber() == nMagic) { if(OrderType() == OP_BUY) { if(OrderStopLoss() < Bid - dTrailingStop - 5 * Point) { OrderModify(OrderTicket(), OrderOpenPrice(), Bid - dTrailingStop, OrderTakeProfit(), 0, Aqua); // Note: we assume, that there // is only one opened order with // this magic number in a time. // If it is not so, comment the // "break". break; } } if(OrderType() == OP_SELL) { if(OrderStopLoss() > Ask + dTrailingStop + 5 * Point) { OrderModify(OrderTicket(), OrderOpenPrice(), Ask + dTrailingStop, OrderTakeProfit(), 0, OrangeRed); break; } } } } } // ------ // This function determines if this is the beginning // of a new bar. Note, that in order to avoid conflicts // between experts, we wait few seconds after the bar // start, the delay is unique for each expert-currency- // timeframe bool IsBarEnd() { bool bIsBarEnd = false; if(nBars != Bars) { if(IsTesting() || (!IsTesting() && CurTime() > Time[0] + nMagic * nDelaySeconds)) { bIsBarEnd = true; nBars = Bars; } } return(bIsBarEnd); } // ------ /* The magic number of the currently trading expert (or 0) is stored in the global variable. In a cycle, our expert tries to set its magic number to this global variable, and when successfull, exits the cycle. */ void CheckTradeSemaphore() { if(!IsTesting()) { while(!IsStopped()) { GlobalVariableSetOnCondition( strTradeSemaphore, nMagic, 0.0); if(GlobalVariableGet(strTradeSemaphore) == nMagic) break; Sleep(1000); } RefreshRates(); } } // ------ /* Attempts to close an order, for 10 times. Comments written to the file with SaveComment() will help you to debug the expert, if it fails to close an order. */ void CloseBuy(string strExpertName) { int nTicket = OrderTicket(); SaveComment(" \r\n \tAttempting to close long position, ticket: " + nTicket + " \r\n "); for(int nTry = 0; nTry < 10; nTry++) { SaveComment(Day() + "." + Month() + "." + Year() + " " + Hour() + ":" + Minute() + ":" + Seconds()); int nResult = OrderClose(OrderTicket(), OrderLots(), Bid, nSlip, Aqua); Sleep(10000); if(nResult == -1) { int nError = GetLastError(); Alert(strExpertName + ", error: " + nError); SaveComment(strExpertName + ", error: " + nError); } bool bClosed = true; for(int nOrderNo = OrdersTotal() - 1; nOrderNo >= 0; nOrderNo--) { OrderSelect(nOrderNo, SELECT_BY_POS, MODE_TRADES); if(OrderTicket() == nTicket) { bClosed = false; break; } } if(bClosed == true) { SaveComment( " \r\n \tNo more orders with this ticket No"); break; } else { SaveComment(" \r\n \tOrder with this ticket still present, trying again"); RefreshRates(); } } } // ------ // Same logic as in CloseBuy() void CloseSell(string strExpertName) { int nTicket = OrderTicket(); SaveComment(" \r\n \tAttempting to close short position, ticket: " + nTicket + " \r\n "); for(int nTry = 0; nTry < 10; nTry++) { SaveComment(Day() + "." + Month() + "." + Year() + " " + Hour() + ":" + Minute() + ":" + Seconds()); int nResult = OrderClose(OrderTicket(), OrderLots(), Ask, nSlip, OrangeRed); Sleep(10000); if(nResult == -1) { int nError = GetLastError(); Alert(strExpertName + ", error: " + nError); SaveComment(strExpertName + ", error: " + nError); } bool bClosed = true; for(int nOrderNo = OrdersTotal() - 1; nOrderNo >= 0; nOrderNo--) { OrderSelect(nOrderNo, SELECT_BY_POS, MODE_TRADES); if(OrderTicket() == nTicket) { bClosed = false; break; } } if(bClosed == true) { SaveComment(" \r\n \tNo more orders with this ticket No"); break; } else { SaveComment(" \r\n \tOrder with this ticket still present, trying again"); RefreshRates(); } } } // ------ // Open a file with unique file name, add a string // to the end of this file void SaveComment(string strComment) { if(!IsTesting()) { int hFile = FileOpen("__test_" + strExpert + "_" + Symbol() + ".txt", FILE_BIN | FILE_READ | FILE_WRITE, '\t'); FileSeek(hFile, 0, SEEK_END); FileWriteString(hFile, strComment, StringLen(strComment)); FileClose(hFile); } } // ------ // Delete pending order, logic is the same as in // CloseBuy() int DeletePending() { int nResult = -1; for(int nTry = 0; nTry < 10; nTry++) { SaveComment(" \r\n " + Day() + "." + Month() + "." + Year() + " " + Hour() + ":" + Minute() + ":" + Seconds()); SaveComment(" Trying to delete pending " + OrderTicket() + ", attempt " + nTry + " \r\n "); nResult = OrderDelete(OrderTicket()); Sleep(10000); if(nResult != -1) { SaveComment(" successfull\r\n"); break; } else SaveComment(" failed, error " + GetLastError() + " \r\n "); } if(nResult == -1) { int nError = GetLastError(); Alert(strExpert + " delete pending, error: " + nError + " \r\n "); SaveComment(strExpert + " delete pending, error: " + nError + " \r\n "); } return(nResult); }