//+------------------------------------------------------------------+ //| common_functions.mq4 | //| Copyright © 2010, Bernd Kreuss | //| Version: 2010.6.11.1 | //+------------------------------------------------------------------+ /** This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #import "shell32.dll" int ShellExecuteA(int hWnd, string Verb, string File, string Parameter, string Path, int ShowCommand); #import "kernel32.dll" void OutputDebugStringA(string msg); #import #define SW_SHOWNORMAL 1 static color CLR_BUY_ARROW = Blue; static color CLR_SELL_ARROW = Red; static color CLR_CROSSLINE_ACTIVE = Magenta; static color CLR_CROSSLINE_TRIGGERED = Aqua; static bool IS_ECN_BROKER = false; /** * start an external program but DON'T wait for it to finish */ void shell(string file, string parameters=""){ ShellExecuteA(0, "open", file, parameters, NULL, SW_SHOWNORMAL); } /** * send information to OutputDebugString() to be viewed and logged * by SysInternals DebugView (free download from microsoft) * This is ideal for debugging as an alternative to Print(). * The function will take up to 8 string (or numeric) arguments * to be concatenated into one debug message. */ void log( string s1, string s2="", string s3="", string s4="", string s5="", string s6="", string s7="", string s8="" ){ string out = StringTrimRight(StringConcatenate( WindowExpertName(), ".mq4 ", Symbol(), " ", s1, " ", s2, " ", s3, " ", s4, " ", s5, " ", s6, " ", s7, " ", s8 )); OutputDebugStringA(out); } /** * use the Comments() display to simulate the behaviour of * the good old print command, useful for debugging. * text will be appended as a new line on every call * and if it has reached 20 lines it will start to scroll. * if clear is set to True the buffer will be cleared. */ void print(string text, bool clear=False){ static string print_lines[20]; static int print_line_position = 0; if (IsOptimization()){ return(0); } string output="\n"; string space = " "; int max_lines = 20; int i; if (clear){ for (i=0; i -1){ start = StringFind(haystack, needle, start); if (start > -1){ if(start > 0){ left = StringSubstr(haystack, 0, start); }else{ left=""; } right = StringSubstr(haystack, start + nlen); haystack = left + replace + right; start = start + rlen; } } return (haystack); } /** * create a positive integer for the use as a magic number. * * The function takes a string as argument and calculates * an 31 bit hash value from it. The hash does certainly not * have the strength of a real cryptographic hash function * but it should be more than sufficient for generating a * unique ID from a string and collissions should not occur. * * use it in your init() function like this: * magic = makeMagicNumber(WindowExpertName() + Symbol() + Period()); * * where name would be the name of your EA. Your EA will then * get a unique magic number for each instrument and timeframe * and this number will always be the same, whenever you put * the same EA onto the same chart. * * Numbers generated during testing mode will differ from those * numbers generated on a live chart. */ int makeMagicNumber(string key){ int i, k; int h = 0; if (IsTesting()){ key = "_" + key; } for (i=0; i0; i--){ k = StringGetChar(key, i - 1); h = h + k; // rotate depending on the last 4 bits of h h = bitRotate(h, h & 0x0000000F); } return(h & 0x7fffffff); } /** * Rotate a 32 bit integer value bit-wise * the specified number of bits to the right. * This function is needed for calculations * in the hash function makeMacicNumber() */ int bitRotate(int value, int count){ int i, tmp, mask; mask = (0x00000001 << count) - 1; tmp = value & mask; value = value >> count; value = value | (tmp << (32 - count)); return(value); } /** * place a market buy with stop loss, target, magic and Comment * keeps trying in an infinite loop until the position is open. */ int buy(double lots, double sl, double tp, int magic=42, string comment=""){ int ticket; if (!IS_ECN_BROKER){ return(orderSendReliable(Symbol(), OP_BUY, lots, Ask, 100, sl, tp, comment, magic, 0, CLR_BUY_ARROW)); }else{ ticket = orderSendReliable(Symbol(), OP_BUY, lots, Ask, 100, 0, 0, comment, magic, 0, CLR_BUY_ARROW); if (sl + tp > 0){ orderModifyReliable(ticket, 0, sl, tp, 0); } return(ticket); } } /** * place a market sell with stop loss, target, magic and comment * keeps trying in an infinite loop until the position is open. */ int sell(double lots, double sl, double tp, int magic=42, string comment=""){ int ticket; if (!IS_ECN_BROKER){ return(orderSendReliable(Symbol(), OP_SELL, lots, Bid, 100, sl, tp, comment, magic, 0, CLR_SELL_ARROW)); }else{ ticket = orderSendReliable(Symbol(), OP_SELL, lots, Bid, 100, 0, 0, comment, magic, 0, CLR_SELL_ARROW); if (sl + tp > 0){ orderModifyReliable(ticket, 0, sl, tp, 0); } return(ticket); } } /** * place a buy limit order */ int buyLimit(double lots, double price, double sl, double tp, int magic=42, string comment=""){ return(orderSendReliable(Symbol(), OP_BUYLIMIT, lots, price, 1, sl, tp, comment, magic, 0, CLR_NONE)); } /** * place a sell limit order */ int sellLimit(double lots, double price, double sl, double tp, int magic=42, string comment=""){ return(orderSendReliable(Symbol(), OP_SELLLIMIT, lots, price, 1, sl, tp, comment, magic, 0, CLR_NONE)); } /** * place a buy stop order */ int buyStop(double lots, double price, double sl, double tp, int magic=42, string comment=""){ return(orderSendReliable(Symbol(), OP_BUYSTOP, lots, price, 1, sl, tp, comment, magic, 0, CLR_NONE)); } /** * place a sell stop order */ int sellStop(double lots, double price, double sl, double tp, int magic=42, string comment=""){ return(orderSendReliable(Symbol(), OP_SELLSTOP, lots, price, 1, sl, tp, comment, magic, 0, CLR_NONE)); } /** * calculate unrealized P&L, belonging to all open trades with this magic number */ double getProfit(int magic){ int cnt; double profit = 0; int total=OrdersTotal(); for(cnt=0; cnt= 0 && -dist > max_negative){ tp = op - max_negative; } if (tp < old_tp){ change = True; } } if (type == OP_SELL){ tp = Ask - trailtarget; dist = op - tp; if (max_negative >= 0 && -dist > max_negative){ tp = op + max_negative; } if (tp > old_tp){ change = True; } } if (change){ orderModifyReliable( OrderTicket(), op, OrderStopLoss(), tp, OrderExpiration() ); } } } } } /** * trailStops() * will loop through all matching open positions and trail their stops * * if trailstops is 0 then nothing will be done, a positive number is * the maximum distance between price and stop, the distance at which * the stop is trailed behind price. * * trailstop_slow is a factor that will increase trailing distance * when the profit grows. default 1 is normal trailing stop behaviour */ void trailStops(double trailstop, int magic, double trailstop_slow=1){ int total, cnt, type; double op, sl, old_sl; bool change; if (trailstop != 0){ total=OrdersTotal(); for(cnt=0; cnt op && sl > old_sl){ change = True; } } if (type == OP_SELL){ sl = op - (op - Ask - trailstop) / trailstop_slow; if (sl < op && (sl < old_sl || old_sl == 0)){ change = True; } } if (change){ orderModifyReliable( OrderTicket(), op, sl, OrderTakeProfit(), OrderExpiration() ); } } } } } void lockProfit(double min_profit, int magic, double distance=0){ int total, cnt, type; double op, sl, old_sl; bool change; total=OrdersTotal(); for(cnt=0; cnt sl + distance && sl > old_sl){ change = True; } } if (type==OP_SELL){ sl = op - min_profit; if (Ask < sl - distance && (sl < old_sl || old_sl == 0)){ change = True; } } if (change){ orderModifyReliable( OrderTicket(), op, sl, OrderTakeProfit(), OrderExpiration() ); } } } } bool isOrder(int type, double price, int magic){ int cnt, num; int total=OrdersTotal(); for(cnt=0; cnt 0); } /** * will close all open orders or positions of specified type with our magic number * this function won't return until all positions are closed * type = -1 means all types, magic = -1 means all magic numbers */ void closeOpenOrders(int type, int magic){ int total, cnt; double price; color clr; int order_type; Print ("closeOpenOrders(" + type + "," + magic + ")"); while (getNumOpenOrders(type, magic) > 0){ while (IsTradeContextBusy()){ Print("closeOpenOrders(): waiting for trade context."); Sleep(MathRand()/10); } total=OrdersTotal(); RefreshRates(); if (type == OP_BUY){ price = Bid; clr = CLR_SELL_ARROW; }else{ price = Ask; clr = CLR_BUY_ARROW; } for(cnt=0; cnt= 0){ // found big enough order to do it in one step Print("reducePosition(): now trying to close order"); if(orderCloseReliable(OrderTicket(), lots, price, 100, clr) == true){ Print("reducePosition(): success!"); return(0); }else{ Print("reducePosition(): order found but failed to close: " + GetLastError()); // permanent error occured return(lots); } }else{ // order is smaller. close it comppetely if(orderCloseReliable(OrderTicket(), OrderLots(), price, 100, clr) == true){ lots -= OrderLots(); Print("reducePosition(): closed " + OrderLots() + " remaining: " + lots); loop_again = True; break; // number of orders has now changed, restart the for loop }else{ Print("reducePosition(): order found but failed to close: " + GetLastError()); // permanent error occured return(lots); } } // are we already done? if(NormalizeDouble(lots, 2) == 0){ return(0); } } } }//for (all orders) // whenever the for loop has completed // loop_again would indicate that an order has been closed // and we need to start the for loop again from 0 }// while (loop_again) Print("reducePosition(): nothing more to close. " + lots + " could not be closed"); return(lots); } void moveStop(int type, int magic, double stoploss=-1){ int total, cnt; total=OrdersTotal(); for(cnt=0; cnt=0; cnt--){ OrderSelect(cnt, SELECT_BY_POS, MODE_HISTORY); type = OrderType(); if(OrderMagicNumber() == magic && (type == OP_BUY || type == OP_SELL)){ return(0); } } } /** * plot the opening trade arrow * This is part of a re-implementation of what metatrader does when dragging * a trade from the history to the chart. Metatrader won't do this automatically * for manual trading and for pending order fills so we have to do it ourselves. * See also plotNewOpenTrades() and plotNewClosedTrades() defined below. */ void plotOpenedTradeArrow(int ticket){ string name; color clr; if (IsOptimization()){ return(0); } OrderSelect(ticket, SELECT_BY_TICKET); name = "#" + ticket + " "; if (OrderType() == OP_BUY){ name = name + "buy "; clr = CLR_BUY_ARROW; } if (OrderType() == OP_SELL){ name = name + "sell "; clr = CLR_SELL_ARROW; } name = name + DoubleToStr(OrderLots(), 2) + " "; name = name + OrderSymbol() + " "; name = name + "at " + DoubleToStr(OrderOpenPrice(), MarketInfo(OrderSymbol(), MODE_DIGITS)); ObjectCreate(name, OBJ_ARROW, 0, OrderOpenTime(), OrderOpenPrice()); ObjectSet(name, OBJPROP_ARROWCODE, 1); ObjectSet(name, OBJPROP_COLOR, clr); } /** * plot the closing trade arrow (needed for filled stoplosses and takeprofits) * This is part of a re-implementation of what metatrader does when dragging * a trade from the history to the chart. Metatrader won't do this automatically * for manual trading and for pending order fills so we have to do it ourselves. * See also plotNewOpenTrades() and plotNewClosedTrades() defined below. */ void plotClosedTradeArrow(int ticket){ string name; color clr; if (IsOptimization()){ return(0); } OrderSelect(ticket, SELECT_BY_TICKET); name = "#" + ticket + " "; if (OrderType() == OP_BUY){ name = name + "buy "; clr = CLR_SELL_ARROW; // closing a buy is a sell, so make it red } if (OrderType() == OP_SELL){ name = name + "sell "; clr = CLR_BUY_ARROW; // closing a sell is a buy, so make it blue } name = name + DoubleToStr(OrderLots(), 2) + " "; name = name + OrderSymbol() + " "; name = name + "at " + DoubleToStr(OrderOpenPrice(), MarketInfo(OrderSymbol(), MODE_DIGITS)) + " "; name = name + "close at " + DoubleToStr(OrderClosePrice(), MarketInfo(OrderSymbol(), MODE_DIGITS)); ObjectCreate(name, OBJ_ARROW, 0, OrderCloseTime(), OrderClosePrice()); ObjectSet(name, OBJPROP_ARROWCODE, 3); ObjectSet(name, OBJPROP_COLOR, clr); } /** * plot the line connecting open and close of a history trade * This is part of a re-implementation of what metatrader does when dragging * a trade from the history to the chart. Metatrader won't do this automatically * for manual trading and for pending order fills so we have to do it ourselves. * See also plotNewOpenTrades() and plotNewClosedTrades() defined below. */ void plotClosedTradeLine(int ticket){ string name; color clr; if (IsOptimization()){ return(0); } OrderSelect(ticket, SELECT_BY_TICKET); name = "#" + ticket + " "; if (OrderType() == OP_BUY){ clr = CLR_BUY_ARROW; } if (OrderType() == OP_SELL){ clr = CLR_SELL_ARROW; } name = name + DoubleToStr(OrderOpenPrice(), MarketInfo(OrderSymbol(), MODE_DIGITS)); name = name + " -> "; name = name + DoubleToStr(OrderClosePrice(), MarketInfo(OrderSymbol(), MODE_DIGITS)); ObjectCreate(name, OBJ_TREND, 0, OrderOpenTime(), OrderOpenPrice(), OrderCloseTime(), OrderClosePrice()); ObjectSet(name, OBJPROP_RAY, false); ObjectSet(name, OBJPROP_STYLE, STYLE_DOT); ObjectSet(name, OBJPROP_COLOR, clr); } /** * check if the open trade list has changed and plot * arrows for opened trades into the chart. * Metatrader won't do this automatically for manual trading. * Use this function for scanning for new trades and plotting them. */ void plotNewOpenTrades(int magic=-1){ //static int last_ticket=0; int total, i; if (IsOptimization()){ return(0); } total = OrdersTotal(); //OrderSelect(total - 1, SELECT_BY_POS, MODE_TRADES); //if (OrderTicket() != last_ticket){ // last_ticket = OrderTicket(); // FIXME! find something to detect changes as cheap as possible! // order list has changed, so plot all arrows for (i=0; i 0){ // we found a line // ATTENTION! DIRTY HACK! MAY BREAK IN FUTURE VERSIONS OF MT4 // ========================================================== // We store the last bid price in the unused PRICE3 field // of every line, so we can call this function more than once // per tick for multiple lines. A static variable would not work here // since we could not call the functon a second time during the same tick last_bid = ObjectGet(name, OBJPROP_PRICE3); // visually mark the line as an active line ObjectSet(name, OBJPROP_COLOR, clr_active); ObjectSet(name, OBJPROP_STYLE, STYLE_DASH); // we have a last_bid value for this line if (last_bid > 0){ // did price cross this line since the last time we checked this line? if ((Close[0] >= price && last_bid <= price) || (Close[0] <= price && last_bid >= price)){ if (one_shot){ ObjectSetText(name, comment + " triggered"); ObjectSet(name, OBJPROP_COLOR, clr_triggered); ObjectSet(name, OBJPROP_STYLE, STYLE_SOLID); ObjectSet(name, OBJPROP_PRICE3, 0); }else{ ObjectSet(name, OBJPROP_PRICE3, Close[0]); } return(true); } } // store current price in the line itself ObjectSet(name, OBJPROP_PRICE3, Close[0]); } } } return(false); } /** * orderModifyReliable() improved OrderModify() */ bool orderModifyReliable( int ticket, double price, double stoploss, double takeprofit, datetime expiration, color arrow_color=CLR_NONE ){ bool success; int err; Print("OrderModifyReliable(" + ticket + "," + price + "," + stoploss + "," + takeprofit + "," + expiration + "," + arrow_color + ")"); while (True){ while (IsTradeContextBusy()){ Print("OrderModifyReliable(): Waiting for trade context."); Sleep(MathRand()/10); } success = OrderModify( ticket, NormalizeDouble(price, Digits), NormalizeDouble(stoploss, Digits), NormalizeDouble(takeprofit, Digits), expiration, arrow_color); if (success){ Print("OrderModifyReliable(): Success!"); return(True); } err = GetLastError(); if (isTemporaryError(err)){ Print("orderModifyReliable(): Temporary Error: " + err + " " + ErrorDescription(err) + ". waiting."); }else{ Print("orderModifyReliable(): Permanent Error: " + err + " " + ErrorDescription(err) + ". giving up."); return(false); } Sleep(MathRand()/10); } } int orderSendReliable( string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment="", int magic=0, datetime expiration=0, color arrow_color=CLR_NONE ){ int ticket; int err; Print("orderSendReliable(" + symbol + "," + cmd + "," + volume + "," + price + "," + slippage + "," + stoploss + "," + takeprofit + "," + comment + "," + magic + "," + expiration + "," + arrow_color + ")"); while(true){ if (IsStopped()){ Print("orderSendReliable(): Trading is stopped!"); return(-1); } RefreshRates(); if (cmd == OP_BUY){ price = Ask; } if (cmd == OP_SELL){ price = Bid; } if (!IsTradeContextBusy()){ ticket = OrderSend( symbol, cmd, volume, NormalizeDouble(price, MarketInfo(symbol, MODE_DIGITS)), slippage, NormalizeDouble(stoploss, MarketInfo(symbol, MODE_DIGITS)), NormalizeDouble(takeprofit, MarketInfo(symbol, MODE_DIGITS)), comment, magic, expiration, arrow_color ); if (ticket > 0){ Print("orderSendReliable(): Success! Ticket: " + ticket); return(ticket); // the normal exit } err = GetLastError(); if (isTemporaryError(err)){ Print("orderSendReliable(): Temporary Error: " + err + " " + ErrorDescription(err) + ". waiting."); }else{ Print("orderSendReliable(): Permanent Error: " + err + " " + ErrorDescription(err) + ". giving up."); return(-1); } }else{ Print("orderSendReliable(): Must wait for trade context"); } Sleep(MathRand()/10); } } bool orderCloseReliable( int ticket, double lots, double price, int slippage, color arrow_color=CLR_NONE ){ bool success; int err; Print("orderCloseReliable()"); OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES); while(true){ if (IsStopped()){ Print("orderCloseReliable(): Trading is stopped!"); return(false); } RefreshRates(); if (OrderType() == OP_BUY){ price = Bid; // close long at bid } if (OrderType() == OP_SELL){ price = Ask; // close short at ask } if (!IsTradeContextBusy()){ success = OrderClose( ticket, lots, NormalizeDouble(price, MarketInfo(OrderSymbol(), MODE_DIGITS)), slippage, arrow_color ); if (success){ Print("orderCloseReliable(): Success!"); return(true); // the normal exit } err = GetLastError(); if (isTemporaryError(err)){ Print("orderCloseReliable(): Temporary Error: " + err + " " + ErrorDescription(err) + ". waiting."); }else{ Print("orderCloseReliable(): Permanent Error: " + err + " " + ErrorDescription(err) + ". giving up."); return(false); } }else{ Print("orderCloseReliable(): Must wait for trade context"); } Sleep(MathRand()/10); } } bool orderDeleteReliable(int ticket){ bool success; int err; Print("orderDeleteReliable(" + ticket + ")"); while(true){ while (IsTradeContextBusy()){ Print("OrderDeleteReliable(): Waiting for trade context."); Sleep(MathRand()/10); } success = OrderDelete(ticket); if (success){ Print("orderDeleteReliable(): success."); return(true); } err = GetLastError(); if (isTemporaryError(err)){ Print("orderDeleteReliable(): Temporary Error: " + err + " " + ErrorDescription(err) + ". waiting."); }else{ Print("orderDeleteReliable(): Permanent Error: " + err + " " + ErrorDescription(err) + ". giving up."); return(false); } Sleep(MathRand()/10); } } bool isEqualPrice(double a, double b){ return(NormalizeDouble(a, Digits) == NormalizeDouble(b, Digits)); } bool isTemporaryError(int error){ return( error == ERR_NO_ERROR || error == ERR_COMMON_ERROR || error == ERR_SERVER_BUSY || error == ERR_NO_CONNECTION || error == ERR_MARKET_CLOSED || error == ERR_PRICE_CHANGED || error == ERR_INVALID_PRICE || //happens sometimes error == ERR_OFF_QUOTES || error == ERR_BROKER_BUSY || error == ERR_REQUOTE || error == ERR_TRADE_TIMEOUT || error == ERR_TRADE_CONTEXT_BUSY ); } /** * return only the first 6 letters of Symbol() * Some Brokers add their initials at the end * of their symbol names and some of my EAs and * indicators want a 6-character symbol name. */ string Symbol6(){ return(StringSubstr(Symbol(), 0, 6)); } /** * determine the pip multiplier (1 or 10) depending on how many * digits the EURUSD symbol has. This is done by first * finding the exact name of this symbol in the symbols.raw * file (it could be EURUSDm or EURUSDiam or any other stupid name * the broker comes up with only to break other people's code) * and then usig MarketInfo() for determining the digits. */ double pointsPerPip(){ int i; int digits; double ppp = 1; string symbol; int f = FileOpenHistory("symbols.raw", FILE_BIN | FILE_READ); int count = FileSize(f) / 1936; for (i=0; i