1 //+------------------------------------------------------------------+
\r
2 //| common_functions.mqh |
\r
3 //| Copyright (c) 2010, Bernd Kreuss |
\r
4 //| prof7bit@googlemail.com |
\r
5 //| Version: 2010.11.30.1 |
\r
6 //+------------------------------------------------------------------+
\r
9 This file contains most often used utility functions that are
\r
10 commonly needed in EAs, especially functions for robust order handling,
\r
11 the latest version of this file is available at
\r
12 http://sites.google.com/site/prof7bit/
\r
14 This program is free software: you can redistribute it and/or modify
\r
15 it under the terms of the GNU General Public License as published by
\r
16 the Free Software Foundation, either version 3 of the License, or
\r
17 (at your option) any later version.
\r
19 This program is distributed in the hope that it will be useful,
\r
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
22 GNU General Public License for more details.
\r
24 You should have received a copy of the GNU General Public License
\r
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
\r
29 This documentation is generated by Doxygen. Click on "Files" to see
\r
30 the list of documented files and go to http://sites.google.com/site/prof7bit/
\r
31 for downloading the actual .mqh files and for additional information.
\r
34 #include <stderror.mqh>
\r
35 #include <stdlib.mqh>
\r
37 #import "shell32.dll"
\r
38 int ShellExecuteA(int hWnd, string Verb, string File, string Parameter, string Path, int ShowCommand);
\r
39 #import "kernel32.dll"
\r
40 void OutputDebugStringA(string msg);
\r
43 #define SW_SHOWNORMAL 1
\r
45 static color CLR_BUY_ARROW = Blue;
\r
46 static color CLR_SELL_ARROW = Red;
\r
47 static color CLR_CROSSLINE_ACTIVE = Magenta;
\r
48 static color CLR_CROSSLINE_TRIGGERED = Gray;
\r
49 static int STYLE_CROSSLINE_TRIGGERED = STYLE_SOLID;
\r
50 static int STYLE_CROSSLINE_ACTIVE = STYLE_DASH;
\r
51 static int WIDTH_CROSSLINE_TRIGGERED = 1;
\r
52 static int WIDTH_CROSSLINE_ACTIVE = 1;
\r
53 static bool IS_ECN_BROKER = false;
\r
56 * start an external program but DON'T wait for it to finish
\r
58 void shell(string file, string parameters = "") {
\r
59 ShellExecuteA(0, "open", file, parameters, NULL, SW_SHOWNORMAL);
\r
63 * Replacement for the built-in Print(), output to the debug monitor.
\r
64 * Send information to OutputDebugString() to be viewed and logged
\r
65 * by SysInternals DebugView (free download from microsoft)
\r
66 * This is ideal for debugging as an alternative to Print().
\r
67 * The function will take up to 8 string (or numeric) arguments
\r
68 * to be concatenated into one debug message.
\r
80 string out = StringTrimRight(StringConcatenate(
\r
81 WindowExpertName(), ".mq4 ", Symbol(),
\r
91 OutputDebugStringA(out);
\r
95 * Replacement for the built-in Print(), output to the chart window.
\r
96 * use the Comments() display to simulate the behaviour of
\r
97 * the good old print command, useful for debugging.
\r
98 * text will be appended as a new line on every call
\r
99 * and if it has reached 20 lines it will start to scroll.
\r
100 * if clear is set to True the buffer will be cleared.
\r
102 void print(string text, bool clear = False) {
\r
103 static string print_lines[20];
\r
104 static int print_line_position = 0;
\r
105 if (IsOptimization()) {
\r
108 string output = "\n";
\r
109 string space = " ";
\r
110 int max_lines = 20;
\r
113 for (i = 0; i < max_lines; i++) {
\r
114 print_lines[i] = "";
\r
115 print_line_position = 0;
\r
119 if (print_line_position == max_lines) {
\r
120 for (i = 0; i < max_lines; i++) {
\r
121 print_lines[i] = print_lines[i+1];
\r
123 print_line_position--;
\r
126 print_lines[print_line_position] = text;
\r
127 print_line_position++;
\r
129 for (i = 0; i < print_line_position; i++) {
\r
130 output = output + print_lines[i] + "\n";
\r
133 output = stringReplace(output, "\n", "\n" + space);
\r
138 * search for the string needle in the string haystack and replace all
\r
139 * occurrecnes with replace.
\r
141 string stringReplace(string haystack, string needle, string replace = "") {
\r
142 string left, right;
\r
144 int rlen = StringLen(replace);
\r
145 int nlen = StringLen(needle);
\r
146 while (start > -1) {
\r
147 start = StringFind(haystack, needle, start);
\r
150 left = StringSubstr(haystack, 0, start);
\r
154 right = StringSubstr(haystack, start + nlen);
\r
155 haystack = left + replace + right;
\r
156 start = start + rlen;
\r
164 * parse a string containing delimiters into an array of strings
\r
165 * and return the number of elements in the resulting array.
\r
167 * if compact is set to true (default) then it will trim all
\r
168 * substrigs and only include non-empty strings in the result.
\r
170 * if max is non-zero then this is the maximum number of results
\r
171 * that should be returned. If for example max = 2 and str is
\r
172 * "42 foo bar" and delimiter = " " then it will stop after the
\r
173 * first delimiter and return only two elements: "42" and "foo bar"
\r
174 * max = 0 (default) will search until the end.
\r
176 int stringExplode(string str, string delimiter, string &result[], bool compact = true, int max = 0) {
\r
183 int l_d = StringLen(delimiter);
\r
185 ArrayResize(result, count + 1);
\r
186 pos_next = StringFind(str, delimiter, pos);
\r
187 if (max > 0 && count == max - 1) {
\r
190 if (pos_next > -1) {
\r
192 l_sub = pos_next - pos;
\r
196 sub = StringSubstr(str, pos, l_sub);
\r
198 pos = pos_next + l_d;
\r
200 sub = StringSubstr(str, pos, 0); // until the end
\r
204 sub = StringTrimLeft(StringTrimRight(sub));
\r
206 if (sub != "" || !compact) {
\r
207 result[count] = sub;
\r
216 * convert a 32 bit integer into its hexadecimal representation
\r
218 string intToHex(int x, int digits = 8) {
\r
219 int i, mask, nibble;
\r
222 for (i = 0; i < digits; i++) {
\r
223 nibble = (x & mask) >> (i << 2);
\r
224 if (nibble < 10) n = CharToStr(48 + nibble);
\r
225 if (nibble > 9) n = CharToStr(55 + nibble);
\r
234 * create a positive integer for the use as a magic number.
\r
236 * The function takes a string as argument and calculates
\r
237 * an 31 bit hash value from it. The hash does certainly not
\r
238 * have the strength of a real cryptographic hash function
\r
239 * but it should be more than sufficient for generating a
\r
240 * unique ID from a string and collissions should not occur.
\r
242 * use it in your init() function like this:
\r
243 * magic = makeMagicNumber(WindowExpertName() + Symbol() + Period());
\r
245 * where name would be the name of your EA. Your EA will then
\r
246 * get a unique magic number for each instrument and timeframe
\r
247 * and this number will always be the same, whenever you put
\r
248 * the same EA onto the same chart.
\r
250 * Numbers generated during testing mode will differ from those
\r
251 * numbers generated on a live chart.
\r
253 int makeMagicNumber(string key) {
\r
261 for (i = 0; i < StringLen(key); i++) {
\r
262 k = StringGetChar(key, i);
\r
264 h = bitRotate(h, 5); // rotate 5 bits
\r
267 for (i = 0; i < StringLen(key); i++) {
\r
268 k = StringGetChar(key, i);
\r
270 // rotate depending on character value
\r
271 h = bitRotate(h, k & 0x0000000F);
\r
274 // now we go backwards in our string
\r
275 for (i = StringLen(key); i > 0; i--) {
\r
276 k = StringGetChar(key, i - 1);
\r
278 // rotate depending on the last 4 bits of h
\r
279 h = bitRotate(h, h & 0x0000000F);
\r
282 return(h & 0x7fffffff);
\r
286 * Rotate a 32 bit integer value bit-wise
\r
287 * the specified number of bits to the right.
\r
288 * This function is needed for calculations
\r
289 * in the hash function makeMacicNumber()
\r
291 int bitRotate(int value, int count) {
\r
293 mask = (0x00000001 << count) - 1;
\r
294 tmp = value & mask;
\r
295 value = value >> count;
\r
296 value = value | (tmp << (32 - count));
\r
301 * place a market buy with stop loss, target, magic and Comment
\r
302 * keeps trying in an infinite loop until the position is open.
\r
304 int buy(double lots, double sl, double tp, int magic = 42, string comment = "") {
\r
306 if (!IS_ECN_BROKER) {
\r
307 return(orderSendReliable(Symbol(), OP_BUY, lots, Ask, 100, sl, tp, comment, magic, 0, CLR_BUY_ARROW));
\r
309 ticket = orderSendReliable(Symbol(), OP_BUY, lots, Ask, 100, 0, 0, comment, magic, 0, CLR_BUY_ARROW);
\r
311 orderModifyReliable(ticket, 0, sl, tp, 0);
\r
318 * place a market sell with stop loss, target, magic and comment
\r
319 * keeps trying in an infinite loop until the position is open.
\r
321 int sell(double lots, double sl, double tp, int magic = 42, string comment = "") {
\r
323 if (!IS_ECN_BROKER) {
\r
324 return(orderSendReliable(Symbol(), OP_SELL, lots, Bid, 100, sl, tp, comment, magic, 0, CLR_SELL_ARROW));
\r
326 ticket = orderSendReliable(Symbol(), OP_SELL, lots, Bid, 100, 0, 0, comment, magic, 0, CLR_SELL_ARROW);
\r
328 orderModifyReliable(ticket, 0, sl, tp, 0);
\r
335 * place a buy limit order
\r
337 int buyLimit(double lots, double price, double sl, double tp, int magic = 42, string comment = "") {
\r
338 return(orderSendReliable(Symbol(), OP_BUYLIMIT, lots, price, 1, sl, tp, comment, magic, 0, CLR_NONE));
\r
342 * place a sell limit order
\r
344 int sellLimit(double lots, double price, double sl, double tp, int magic = 42, string comment = "") {
\r
345 return(orderSendReliable(Symbol(), OP_SELLLIMIT, lots, price, 1, sl, tp, comment, magic, 0, CLR_NONE));
\r
349 * place a buy stop order
\r
351 int buyStop(double lots, double price, double sl, double tp, int magic = 42, string comment = "") {
\r
352 return(orderSendReliable(Symbol(), OP_BUYSTOP, lots, price, 1, sl, tp, comment, magic, 0, CLR_NONE));
\r
356 * place a sell stop order
\r
358 int sellStop(double lots, double price, double sl, double tp, int magic = 42, string comment = "") {
\r
359 return(orderSendReliable(Symbol(), OP_SELLSTOP, lots, price, 1, sl, tp, comment, magic, 0, CLR_NONE));
\r
364 * calculate unrealized P&L, belonging to all open trades with this magic number
\r
366 double getProfit(int magic) {
\r
369 int total = OrdersTotal();
\r
370 for (cnt = 0; cnt < total; cnt++) {
\r
371 OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
\r
372 if (OrderMagicNumber() == magic) {
\r
373 profit += OrderProfit() + OrderSwap() + OrderCommission();
\r
380 * calculate realized P&L resulting from all closed trades with this magic number
\r
382 double getProfitRealized(int magic) {
\r
385 int total = OrdersHistoryTotal();
\r
386 for (cnt = 0; cnt < total; cnt++) {
\r
387 OrderSelect(cnt, SELECT_BY_POS, MODE_HISTORY);
\r
388 if (OrderMagicNumber() == magic) {
\r
389 profit += OrderProfit() + OrderSwap() + OrderCommission();
\r
396 * get the number of currently open trades of specified type
\r
398 int getNumOpenOrders(int type, int magic) {
\r
401 int total = OrdersTotal();
\r
402 for (cnt = 0; cnt < total; cnt++) {
\r
403 OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
\r
404 if ((magic == -1 || OrderMagicNumber() == magic) && (type == -1 || OrderType() == type)) {
\r
413 * Trail the take-profit if price moves against the trade.
\r
415 * Loop through all matching open positions and trail the target
\r
416 * towards price if the price moves AGAINST the trade direction (away
\r
417 * from the target). This may even trail the target into negative territory.
\r
419 * If trailtarget is 0 then nothing will be done, a positive number
\r
420 * means the maximum distance between price and target.
\r
422 * If max_negative is set to -1 (default) the there is no limitation
\r
423 * how far into negative territory the target can be trailed, if
\r
424 * max_negative is set to 0 then it will only trail down to break even
\r
425 * and then stop at this point, if it is set to a positive number
\r
426 * then it will use this number as a limit of how far into loss the
\r
427 * target may be trailed.
\r
429 void trailTargets(double trailtarget, int magic, double max_negative = -1) {
\r
430 int total, cnt, type;
\r
431 double op, tp, old_tp, dist;
\r
434 if (trailtarget != 0) {
\r
435 total = OrdersTotal();
\r
436 for (cnt = 0; cnt < total; cnt++) {
\r
437 OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
\r
438 if (OrderMagicNumber() == magic) {
\r
439 op = OrderOpenPrice();
\r
440 old_tp = OrderTakeProfit();
\r
441 type = OrderType();
\r
444 if (type == OP_BUY) {
\r
445 tp = Bid + trailtarget;
\r
447 if (max_negative >= 0 && -dist > max_negative) {
\r
448 tp = op - max_negative;
\r
455 if (type == OP_SELL) {
\r
456 tp = Ask - trailtarget;
\r
458 if (max_negative >= 0 && -dist > max_negative) {
\r
459 tp = op + max_negative;
\r
467 orderModifyReliable(
\r
481 * Trail the stoploss.
\r
483 * Loop through all matching open positions and trail their stops.
\r
485 * If trailstops is 0 then nothing will be done, a positive number is
\r
486 * the maximum distance between price and stop, the distance at which
\r
487 * the stop is trailed behind price.
\r
489 * trailstop_slow is a factor that will increase trailing distance
\r
490 * when the profit grows. default 1 is normal trailing stop behaviour
\r
492 void trailStops(double trailstop, int magic, double trailstop_slow = 1) {
\r
493 int total, cnt, type;
\r
494 double op, sl, old_sl;
\r
497 if (trailstop != 0) {
\r
498 total = OrdersTotal();
\r
499 for (cnt = 0; cnt < total; cnt++) {
\r
500 OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
\r
501 if (OrderMagicNumber() == magic) {
\r
502 op = OrderOpenPrice();
\r
503 old_sl = OrderStopLoss();
\r
504 type = OrderType();
\r
507 if (type == OP_BUY) {
\r
508 sl = op + (Bid - trailstop - op) / trailstop_slow;
\r
509 if (sl > op && sl > old_sl) {
\r
514 if (type == OP_SELL) {
\r
515 sl = op - (op - Ask - trailstop) / trailstop_slow;
\r
516 if (sl < op && (sl < old_sl || old_sl == 0)) {
\r
522 orderModifyReliable(
\r
536 * Lock in pofit after a crtain amount of profit is reached.
\r
538 * This will place (or move) a stoploss min_profit away from
\r
539 * the open price as soon as price has moved distance away from
\r
540 * this point. If distance is 0 or smaller than the minimum
\r
541 * allowed stop distance then it will still behave as if you had
\r
542 * specified the minimum allowed stop distance.
\r
544 void lockProfit(double min_profit, int magic, double distance = 0) {
\r
545 int total, cnt, type;
\r
546 double op, sl, old_sl;
\r
549 total = OrdersTotal();
\r
550 for (cnt = 0; cnt < total; cnt++) {
\r
551 OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
\r
552 if (OrderMagicNumber() == magic) {
\r
553 old_sl = OrderStopLoss();
\r
554 op = OrderOpenPrice();
\r
555 type = OrderType();
\r
558 if (type == OP_BUY) {
\r
559 sl = op + min_profit;
\r
560 if (Bid > sl + distance && sl > old_sl) {
\r
565 if (type == OP_SELL) {
\r
566 sl = op - min_profit;
\r
567 if (Ask < sl - distance && (sl < old_sl || old_sl == 0)) {
\r
573 orderModifyReliable(
\r
586 * Return true if there exists an order of this type at exactly this price.
\r
588 bool isOrder(int type, double price, int magic) {
\r
590 int total = OrdersTotal();
\r
591 for (cnt = 0; cnt < total; cnt++) {
\r
592 OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
\r
593 if (OrderMagicNumber() == magic && (type == -1 || OrderType() == type)) {
\r
594 if (isEqualPrice(OrderOpenPrice(), price)) {
\r
603 * Close all open orders or trades that match type and magic number.
\r
604 * This function won't return until all positions are closed
\r
605 * type = -1 means all types, magic = -1 means all magic numbers
\r
607 void closeOpenOrders(int type, int magic) {
\r
613 Print("closeOpenOrders(" + type + "," + magic + ")");
\r
615 while (getNumOpenOrders(type, magic) > 0) {
\r
616 while (IsTradeContextBusy()) {
\r
617 Print("closeOpenOrders(): waiting for trade context.");
\r
618 Sleep(MathRand() / 10);
\r
620 total = OrdersTotal();
\r
622 if (type == OP_BUY) {
\r
624 clr = CLR_SELL_ARROW;
\r
627 clr = CLR_BUY_ARROW;
\r
629 for (cnt = 0; cnt < total; cnt++) {
\r
630 OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
\r
631 if ((type == -1 || OrderType() == type) && (magic == -1 || OrderMagicNumber() == magic)) {
\r
632 if (IsTradeContextBusy()) {
\r
633 break; // something else is trading too, back to the while loop.
\r
635 order_type = OrderType();
\r
636 if (order_type == OP_BUYSTOP || order_type == OP_SELLSTOP || order_type == OP_BUYLIMIT || order_type == OP_SELLLIMIT) {
\r
637 orderDeleteReliable(OrderTicket());
\r
639 orderCloseReliable(OrderTicket(), OrderLots(), price, 999, clr);
\r
641 break; // restart the loop from 0 (hello FIFO!)
\r
648 * Scale out of a position the specified amounts of lots and return
\r
649 * the number of lots that could *not* be closed.
\r
651 * This function will try to close out the needed lots out of any
\r
652 * combination of open orders, using partial closes or complete closes
\r
653 * until there is nothing left to close or the needed lot size has been
\r
654 * closed. It will start closing the oldest orders first (FIFO).
\r
655 * The return value will be the amount that could not be closed, this
\r
656 * can then be directly used to place a buy or sell into the other
\r
657 * direction. If the requested amount could be closed it will return 0.
\r
659 * This function is not yet fully tested and debugged. It may change
\r
660 * in future releases, it may not work at all. Dont use it (yet)!
\r
662 double reducePosition(int type, double lots, int magic) {
\r
666 bool loop_again = true;
\r
668 Print("reducePosition()");
\r
670 while (loop_again) {
\r
672 if (type == OP_BUY) {
\r
674 clr = CLR_SELL_ARROW;
\r
676 if (type == OP_SELL) {
\r
678 clr = CLR_BUY_ARROW;
\r
680 int total_orders = OrdersTotal();
\r
681 loop_again = false;
\r
682 for (i = 0; i < total_orders; i++) {
\r
683 OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
\r
684 if (OrderMagicNumber() == magic) {
\r
685 if (OrderType() == type) {
\r
686 if (NormalizeDouble(OrderLots() - lots, 2) >= 0) {
\r
687 // found big enough order to do it in one step
\r
688 Print("reducePosition(): now trying to close order");
\r
689 if (orderCloseReliable(OrderTicket(), lots, price, 100, clr) == true) {
\r
690 Print("reducePosition(): success!");
\r
693 Print("reducePosition(): order found but failed to close: " + GetLastError());
\r
694 // permanent error occured
\r
698 // order is smaller. close it comppetely
\r
699 if (orderCloseReliable(OrderTicket(), OrderLots(), price, 100, clr) == true) {
\r
700 lots -= OrderLots();
\r
701 Print("reducePosition(): closed " + OrderLots() + " remaining: " + lots);
\r
703 break; // number of orders has now changed, restart the for loop
\r
705 Print("reducePosition(): order found but failed to close: " + GetLastError());
\r
706 // permanent error occured
\r
711 // are we already done?
\r
712 if (NormalizeDouble(lots, 2) == 0) {
\r
717 }//for (all orders)
\r
719 // whenever the for loop has completed
\r
720 // loop_again would indicate that an order has been closed
\r
721 // and we need to start the for loop again from 0
\r
723 }// while (loop_again)
\r
724 Print("reducePosition(): nothing more to close. " + lots + " could not be closed");
\r
729 * move the stop (stops) to the specified price.
\r
731 void moveStop(int type, int magic, double stoploss = -1) {
\r
733 total = OrdersTotal();
\r
734 for (cnt = 0; cnt < total; cnt++) {
\r
735 OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
\r
736 if (OrderType() == type && OrderMagicNumber() == magic) {
\r
737 if (stoploss == -1) {
\r
738 stoploss = OrderOpenPrice();
\r
740 if (!isEqualPrice(stoploss, OrderStopLoss())) {
\r
741 orderModifyReliable(
\r
754 * Move the pending order to the specified price.
\r
755 * This will also take care of moving associated
\r
756 * stop and target the same amount.
\r
758 void moveOrder(int type, int magic, double price) {
\r
761 total = OrdersTotal();
\r
762 for (cnt = 0; cnt < total; cnt++) {
\r
763 OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
\r
764 if (OrderType() == type && OrderMagicNumber() == magic) {
\r
765 if (!isEqualPrice(d, OrderOpenPrice())) {
\r
766 d = price - OrderOpenPrice();
\r
767 if (OrderStopLoss() == 0) {
\r
770 sl = OrderStopLoss() + d;
\r
772 if (OrderTakeProfit() == 0) {
\r
775 tp = OrderTakeProfit() + d;
\r
777 orderModifyReliable(
\r
790 * Move the take-profit to the specified price.
\r
792 void moveTarget(int type, int magic, double target) {
\r
794 total = OrdersTotal();
\r
795 for (cnt = 0; cnt < total; cnt++) {
\r
796 OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
\r
797 if (OrderType() == type && OrderMagicNumber() == magic) {
\r
798 if (!isEqualPrice(target, OrderTakeProfit())) {
\r
799 orderModifyReliable(
\r
812 * Get the number of (effective) lots that are curretly open.
\r
813 * This will return the effective exposure. Offsetting trades
\r
814 * will be subtracted. The return value will always be positive.
\r
815 * See also getLotsOnTableSigned()
\r
817 double getLotsOnTable(int magic) {
\r
818 double total_lots = 0;
\r
820 int total_orders = OrdersTotal();
\r
821 for (i = 0; i < total_orders; i++) {
\r
822 OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
\r
823 if (OrderMagicNumber() == magic) {
\r
824 if (OrderType() == OP_BUY) {
\r
825 total_lots += OrderLots();
\r
827 if (OrderType() == OP_SELL) {
\r
828 total_lots -= OrderLots();
\r
832 total_lots = MathAbs(total_lots);
\r
833 return(total_lots);
\r
837 * Get the number of (effective) lots that are curretly open.
\r
838 * This will return the effective exposure. Offsetting trades
\r
839 * will be subtracted. Positive means long, negative is short.
\r
840 * See also getLotsOnTable()
\r
842 double getLotsOnTableSigned(int magic) {
\r
843 double total_lots = 0;
\r
845 int total_orders = OrdersTotal();
\r
846 for (i = 0; i < total_orders; i++) {
\r
847 OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
\r
848 if (OrderMagicNumber() == magic) {
\r
849 if (OrderType() == OP_BUY) {
\r
850 total_lots += OrderLots();
\r
852 if (OrderType() == OP_SELL) {
\r
853 total_lots -= OrderLots();
\r
857 return(total_lots);
\r
861 * Get the open price of the average position.
\r
863 double getAveragePositionPrice(int magic) {
\r
864 double total_lots = getLotsOnTable(magic);
\r
865 double average_price = 0;
\r
867 int total_orders = OrdersTotal();
\r
868 for (i = 0; i < total_orders; i++) {
\r
869 OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
\r
870 if (OrderMagicNumber() == magic) {
\r
871 average_price += OrderOpenPrice() * OrderLots() / total_lots;
\r
874 return(average_price);
\r
878 * Get the profit of the last closed order (sl, tp or manually closed).
\r
880 double getLastProfit(int magic) {
\r
881 selectLastClosedTrade(magic);
\r
882 return(OrderProfit());
\r
886 * Select the last closed order (sl, tp or manually closed).
\r
888 void selectLastClosedTrade(int magic) {
\r
889 int total, cnt, type;
\r
890 total = OrdersHistoryTotal();
\r
891 for (cnt = total - 1; cnt >= 0; cnt--) {
\r
892 OrderSelect(cnt, SELECT_BY_POS, MODE_HISTORY);
\r
893 type = OrderType();
\r
894 if (OrderMagicNumber() == magic && (type == OP_BUY || type == OP_SELL)) {
\r
901 * Plot the opening trade arrow.
\r
902 * This is part of a re-implementation of what metatrader does when dragging
\r
903 * a trade from the history to the chart. Metatrader won't do this automatically
\r
904 * for manual trading and for pending order fills so we have to do it ourselves.
\r
905 * See also plotNewOpenTrades() and plotNewClosedTrades() defined below.
\r
907 void plotOpenedTradeArrow(int ticket, bool remove = false) {
\r
908 string name, name_wrong;
\r
910 if (IsOptimization()) {
\r
913 OrderSelect(ticket, SELECT_BY_TICKET);
\r
915 name = "#" + ticket + " ";
\r
916 if (OrderType() == OP_BUY) {
\r
917 name = name + "buy ";
\r
918 clr = CLR_BUY_ARROW;
\r
920 if (OrderType() == OP_SELL) {
\r
921 name = name + "sell ";
\r
922 clr = CLR_SELL_ARROW;
\r
924 name = name + DoubleToStr(OrderLots(), 2) + " ";
\r
925 name = name + OrderSymbol() + " ";
\r
927 // sometimes mt4 will have created an arrow with open price = 0.
\r
928 // this is wrong, we will delete it.
\r
929 name_wrong = name + "at " + DoubleToStr(0, MarketInfo(OrderSymbol(), MODE_DIGITS));
\r
931 name = name + "at " + DoubleToStr(OrderOpenPrice(), MarketInfo(OrderSymbol(), MODE_DIGITS));
\r
933 ObjectDelete(name_wrong);
\r
935 ObjectDelete(name);
\r
937 ObjectCreate(name, OBJ_ARROW, 0, OrderOpenTime(), OrderOpenPrice());
\r
938 ObjectSet(name, OBJPROP_ARROWCODE, 1);
\r
939 ObjectSet(name, OBJPROP_COLOR, clr);
\r
940 ObjectSetText(name, formatOrderArrowInfo());
\r
945 * Plot the closing trade arrow.
\r
946 * This is part of a re-implementation of what metatrader does when dragging
\r
947 * a trade from the history to the chart. Metatrader won't do this automatically
\r
948 * for manual trading and for pending order fills so we have to do it ourselves.
\r
949 * See also plotNewOpenTrades() and plotNewClosedTrades() defined below.
\r
951 void plotClosedTradeArrow(int ticket, bool remove = false) {
\r
954 if (IsOptimization()) {
\r
957 OrderSelect(ticket, SELECT_BY_TICKET);
\r
958 name = "#" + ticket + " ";
\r
959 if (OrderType() == OP_BUY) {
\r
960 name = name + "buy ";
\r
961 clr = CLR_SELL_ARROW; // closing a buy is a sell, so make it red
\r
963 if (OrderType() == OP_SELL) {
\r
964 name = name + "sell ";
\r
965 clr = CLR_BUY_ARROW; // closing a sell is a buy, so make it blue
\r
967 name = name + DoubleToStr(OrderLots(), 2) + " ";
\r
968 name = name + OrderSymbol() + " ";
\r
969 name = name + "at " + DoubleToStr(OrderOpenPrice(), MarketInfo(OrderSymbol(), MODE_DIGITS)) + " ";
\r
970 name = name + "close at " + DoubleToStr(OrderClosePrice(), MarketInfo(OrderSymbol(), MODE_DIGITS));
\r
973 ObjectDelete(name);
\r
975 ObjectCreate(name, OBJ_ARROW, 0, OrderCloseTime(), OrderClosePrice());
\r
976 ObjectSet(name, OBJPROP_ARROWCODE, 3);
\r
977 ObjectSet(name, OBJPROP_COLOR, clr);
\r
978 ObjectSetText(name, formatOrderArrowInfo());
\r
983 * Plot the line connecting open and close of a history trade.
\r
984 * This is part of a re-implementation of what metatrader does when dragging
\r
985 * a trade from the history to the chart. Metatrader won't do this automatically
\r
986 * for manual trading and for pending order fills so we have to do it ourselves.
\r
987 * See also plotNewOpenTrades() and plotNewClosedTrades() defined below.
\r
989 void plotClosedTradeLine(int ticket, bool remove = false) {
\r
992 if (IsOptimization()) {
\r
995 OrderSelect(ticket, SELECT_BY_TICKET);
\r
996 name = "#" + ticket + " ";
\r
997 if (OrderType() == OP_BUY) {
\r
998 clr = CLR_BUY_ARROW;
\r
1000 if (OrderType() == OP_SELL) {
\r
1001 clr = CLR_SELL_ARROW;
\r
1003 name = name + DoubleToStr(OrderOpenPrice(), MarketInfo(OrderSymbol(), MODE_DIGITS));
\r
1004 name = name + " -> ";
\r
1005 name = name + DoubleToStr(OrderClosePrice(), MarketInfo(OrderSymbol(), MODE_DIGITS));
\r
1008 ObjectDelete(name);
\r
1010 ObjectCreate(name, OBJ_TREND, 0, OrderOpenTime(), OrderOpenPrice(), OrderCloseTime(), OrderClosePrice());
\r
1011 ObjectSet(name, OBJPROP_RAY, false);
\r
1012 ObjectSet(name, OBJPROP_STYLE, STYLE_DOT);
\r
1013 ObjectSet(name, OBJPROP_COLOR, clr);
\r
1018 * Create the info-string for opened and closed order arrows.
\r
1020 string formatOrderArrowInfo() {
\r
1021 // order is already selected
\r
1022 return(StringConcatenate(
\r
1025 OrderMagicNumber(),
\r
1027 DoubleToStr(OrderProfit() + OrderSwap() + OrderCommission(), 2),
\r
1029 AccountCurrency(),
\r
1031 DoubleToStr(MathAbs(OrderOpenPrice() - OrderClosePrice()) / (MarketInfo(OrderSymbol(), MODE_POINT) * pointsPerPip()), 2),
\r
1037 * Plot all newly opened trades into the chart.
\r
1038 * Check if the open trade list has changed and plot
\r
1039 * arrows for opened trades into the chart.
\r
1040 * Metatrader won't do this automatically for manual trading.
\r
1041 * Use this function for scanning for new trades and plotting them.
\r
1043 void plotNewOpenTrades(int magic = -1) {
\r
1044 //static int last_ticket=0;
\r
1046 if (IsTesting()) {
\r
1050 total = OrdersTotal();
\r
1051 //OrderSelect(total - 1, SELECT_BY_POS, MODE_TRADES);
\r
1052 //if (OrderTicket() != last_ticket){
\r
1053 // last_ticket = OrderTicket();
\r
1055 // FIXME! find something to detect changes as cheap as possible!
\r
1057 // order list has changed, so plot all arrows
\r
1058 for (i = 0; i < total; i++) {
\r
1059 OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
\r
1060 if ((magic == -1 || OrderMagicNumber() == magic) && OrderSymbol() == Symbol()) {
\r
1061 if (OrderType() == OP_BUY || OrderType() == OP_SELL) {
\r
1062 plotOpenedTradeArrow(OrderTicket());
\r
1070 * Plot all newly closed trades into the chart.
\r
1071 * Check for changes in the trading history and plot the
\r
1072 * trades into the chart with arrows and lines connectimg them.
\r
1073 * Metatrader won't do this automatically for manual trading.
\r
1074 * Use this function for scanning for closed trades and plotting them.
\r
1076 void plotNewClosedTrades(int magic = -1) {
\r
1077 if (IsTesting()) {
\r
1081 static int last_change = 0;
\r
1082 bool max_age_defined = GlobalVariableCheck("ARROW_MAX_AGE");
\r
1083 int max_age = GlobalVariableGet("ARROW_MAX_AGE") * 24 * 60 * 60;
\r
1084 datetime tc = TimeCurrent();
\r
1088 total = OrdersHistoryTotal();
\r
1089 OrderSelect(total - 1, SELECT_BY_POS, MODE_HISTORY);
\r
1090 if (OrderTicket() + max_age + max_age_defined != last_change) {
\r
1091 last_change = OrderTicket() + max_age + max_age_defined;
\r
1093 // order list has changed, so plot all arrows
\r
1094 for (i = 0; i < total; i++) {
\r
1095 OrderSelect(i, SELECT_BY_POS, MODE_HISTORY);
\r
1096 if ((magic == -1 || OrderMagicNumber() == magic) && OrderSymbol() == Symbol()) {
\r
1097 if (OrderType() == OP_BUY || OrderType() == OP_SELL) {
\r
1098 if (max_age_defined && tc - OrderCloseTime() > max_age) {
\r
1103 plotOpenedTradeArrow(OrderTicket(), remove);
\r
1104 plotClosedTradeArrow(OrderTicket(), remove);
\r
1105 plotClosedTradeLine(OrderTicket(), remove);
\r
1113 * Create a line connecting 2 points.
\r
1115 string line(string name, datetime t1, double p1, datetime t2, double p2, color clr = Red, string label = "", bool ray = False) {
\r
1116 if (!IsOptimization()) {
\r
1118 name = "line_" + Time[0];
\r
1120 if (ObjectFind(name) == -1) {
\r
1121 ObjectCreate(name, OBJ_TREND, 0, t1, p1, t2, p2);
\r
1123 ObjectSet(name, OBJPROP_RAY, ray);
\r
1124 ObjectSet(name, OBJPROP_COLOR, clr);
\r
1125 ObjectSet(name, OBJPROP_TIME1, t1);
\r
1126 ObjectSet(name, OBJPROP_TIME2, t2);
\r
1127 ObjectSet(name, OBJPROP_PRICE1, p1);
\r
1128 ObjectSet(name, OBJPROP_PRICE2, p2);
\r
1129 ObjectSetText(name, label);
\r
1135 * Create a horizontal line.
\r
1137 string horizLine(string name, double price, color clr = Red, string label = "") {
\r
1138 if (!IsOptimization()) {
\r
1140 name = "line_" + Time[0];
\r
1142 if (ObjectFind(name) == -1) {
\r
1143 ObjectCreate(name, OBJ_HLINE, 0, 0, price);
\r
1145 ObjectSet(name, OBJPROP_PRICE1, price);
\r
1147 ObjectSet(name, OBJPROP_COLOR, clr);
\r
1148 ObjectSetText(name, label);
\r
1154 * Create a vertical line.
\r
1156 string vertLine(string name, datetime time, color clr = Red, string label = "") {
\r
1157 if (!IsOptimization()) {
\r
1159 name = "line_" + Time[0];
\r
1161 if (ObjectFind(name) == -1) {
\r
1162 ObjectCreate(name, OBJ_VLINE, 0, time, 0);
\r
1164 ObjectSet(name, OBJPROP_TIME1, time);
\r
1166 ObjectSet(name, OBJPROP_COLOR, clr);
\r
1167 ObjectSetText(name, label);
\r
1173 * Create a text object.
\r
1175 string text(string name, string text, datetime time = 0, double price = 0, color clr = Red, int size = 8) {
\r
1180 price = Close[iBarShift(NULL, 0, time)];
\r
1183 name = "text_" + time;
\r
1185 if (ObjectFind(name) == -1) {
\r
1186 ObjectCreate(name, OBJ_TEXT, 0, time, price);
\r
1188 ObjectSet(name, OBJPROP_TIME1, time);
\r
1189 ObjectSet(name, OBJPROP_PRICE1, price);
\r
1190 ObjectSet(name, OBJPROP_COLOR, clr);
\r
1191 ObjectSet(name, OBJPROP_FONTSIZE, size);
\r
1192 ObjectSetText(name, text);
\r
1197 * Create a text label.
\r
1199 string label(string name, int x, int y, int corner, string text, color clr = Gray) {
\r
1200 if (!IsOptimization()) {
\r
1202 name = "label_" + Time[0];
\r
1204 if (ObjectFind(name) == -1) {
\r
1205 ObjectCreate(name, OBJ_LABEL, 0, 0, 0);
\r
1207 ObjectSet(name, OBJPROP_COLOR, clr);
\r
1208 ObjectSet(name, OBJPROP_CORNER, corner);
\r
1209 ObjectSet(name, OBJPROP_XDISTANCE, x);
\r
1210 ObjectSet(name, OBJPROP_YDISTANCE, y);
\r
1211 ObjectSetText(name, text);
\r
1217 * Show a button and check if it has been actuated.
\r
1218 * Emulate a button with a label that must be moved by the user.
\r
1219 * Return true if the label has been moved and move it back.
\r
1220 * create it if it does not already exist.
\r
1222 bool labelButton(string name, int x, int y, int corner, string text, color clr = Gray) {
\r
1223 if (IsOptimization()) {
\r
1226 if (ObjectFind(name) != -1) {
\r
1227 if (ObjectGet(name, OBJPROP_XDISTANCE) != x || ObjectGet(name, OBJPROP_YDISTANCE) != y) {
\r
1228 ObjectDelete(name);
\r
1232 label(name, x, y, corner, "[" + text + "]", clr);
\r
1238 * Check if a line has been crossed, the line may contain a command string.
\r
1240 * return "1" or the the argument if price (Bid) just has crossed
\r
1241 * a line with a decription starting with this command or "" otherwise.
\r
1243 * for example if a line has the description
\r
1245 * then it will return the string "0.1" when price has just crossed it
\r
1246 * and it will return "" if this was not the case.
\r
1248 * If the parameter one_shot is true (default) it will add the word
\r
1249 * "triggered: " to the command so it can't be triggered a second time,
\r
1250 * the line description will then look like
\r
1251 * triggered: buy 0.1
\r
1252 * and it will not be active anymore.
\r
1254 * if there is bo argument behind the command then it will simply
\r
1255 * return the string "1" if it is triggered.
\r
1257 * This function is intentionally returning strings and not doubles
\r
1258 * to enable you to do such funny things like for example define a command
\r
1259 * "delete" and then have lines like "delete buy" or "delete sell" and have
\r
1260 * it delete other lines once price triggers this line or do even more
\r
1261 * complex "visual programming" in the chart.
\r
1263 string crossedLineS(string command, bool one_shot = true, color clr_active = CLR_NONE, color clr_triggered = CLR_NONE) {
\r
1264 double last_bid; // see below!
\r
1268 string command_line;
\r
1269 string command_argument;
\r
1273 if (clr_active == CLR_NONE) {
\r
1274 clr_active = CLR_CROSSLINE_ACTIVE;
\r
1276 if (clr_triggered == CLR_NONE) {
\r
1277 clr_triggered = CLR_CROSSLINE_TRIGGERED;
\r
1280 for (i = 0; i < ObjectsTotal(); i++) {
\r
1281 name = ObjectName(i);
\r
1283 // is this an object without description (newly created by the user)?
\r
1284 if (ObjectDescription(name) == "") {
\r
1285 // Sometimes the user draws a new line and the default color is
\r
1286 // accidentially the color and style of an active line. If we
\r
1287 // simply reset all lines without decription but with the active
\r
1288 // color and style we can almost completely eliminate this problem.
\r
1289 // The color does not influence the functionality in any way but
\r
1290 // we simply don't WANT to confuse the USER with lines that have
\r
1291 // the active color and style that are not active lines.
\r
1292 if (ObjectGet(name, OBJPROP_COLOR) == clr_active
\r
1293 && ObjectGet(name, OBJPROP_WIDTH) == WIDTH_CROSSLINE_ACTIVE
\r
1294 && ObjectGet(name, OBJPROP_STYLE) == STYLE_CROSSLINE_ACTIVE) {
\r
1295 ObjectSet(name, OBJPROP_COLOR, clr_triggered);
\r
1296 ObjectSet(name, OBJPROP_STYLE, STYLE_CROSSLINE_TRIGGERED);
\r
1297 ObjectSet(name, OBJPROP_WIDTH, WIDTH_CROSSLINE_TRIGGERED);
\r
1298 ObjectSet(name, OBJPROP_PRICE3, 0);
\r
1302 // is this an object that contains our command?
\r
1303 if (StringFind(ObjectDescription(name), command) == 0) {
\r
1305 type = ObjectType(name);
\r
1307 // we only care about certain types of objects
\r
1308 if (type == OBJ_HLINE) {
\r
1309 price = ObjectGet(name, OBJPROP_PRICE1);
\r
1311 if (type == OBJ_TREND) {
\r
1312 price = ObjectGetValueByShift(name, 0);
\r
1315 if (price > 0) { // we found a line
\r
1317 // ATTENTION! DIRTY HACK! MAY BREAK IN FUTURE VERSIONS OF MT4
\r
1318 // ==========================================================
\r
1319 // We store the last bid price in the unused PRICE3 field
\r
1320 // of every line, so we can call this function more than once
\r
1321 // per tick for multiple lines. A static variable would not work here
\r
1322 // since we could not call the functon a second time during the same tick
\r
1323 last_bid = ObjectGet(name, OBJPROP_PRICE3);
\r
1325 // visually mark the line as an active line
\r
1326 ObjectSet(name, OBJPROP_COLOR, clr_active);
\r
1327 ObjectSet(name, OBJPROP_STYLE, STYLE_CROSSLINE_ACTIVE);
\r
1328 ObjectSet(name, OBJPROP_WIDTH, WIDTH_CROSSLINE_ACTIVE);
\r
1330 // we have a last_bid value for this line
\r
1331 if (last_bid > 0) {
\r
1333 // did price cross this line since the last time we checked this line?
\r
1334 if ((Close[0] >= price && last_bid <= price) || (Close[0] <= price && last_bid >= price)) {
\r
1336 // extract the argument
\r
1337 command_line = ObjectDescription(name);
\r
1338 command_argument = StringSubstr(command_line, StringLen(command) + 1);
\r
1339 if (command_argument == "") {
\r
1340 command_argument = "1"; // default argument is "1"
\r
1343 // make the line triggered if it is a one shot command
\r
1345 ObjectSetText(name, "triggered: " + command_line);
\r
1346 ObjectSet(name, OBJPROP_COLOR, clr_triggered);
\r
1347 ObjectSet(name, OBJPROP_STYLE, STYLE_CROSSLINE_TRIGGERED);
\r
1348 ObjectSet(name, OBJPROP_WIDTH, WIDTH_CROSSLINE_TRIGGERED);
\r
1349 ObjectSet(name, OBJPROP_PRICE3, 0);
\r
1351 ObjectSet(name, OBJPROP_PRICE3, Close[0]);
\r
1354 return(command_argument);
\r
1358 // store current price in the line itself
\r
1359 ObjectSet(name, OBJPROP_PRICE3, Close[0]);
\r
1365 return(""); // command line not crossed, return empty string (false)
\r
1369 * Check if a line has been crossed, the line may contain a numeric value.
\r
1370 * Call crossedLineS() (see crossedLineS() for more documentation)
\r
1371 * and cast it into a number (will return 1 if the line has no argument)
\r
1373 double crossedLineD(string command, bool one_shot = true, color clr_active = CLR_NONE, color clr_triggered = CLR_NONE) {
\r
1374 string arg = crossedLineS(string command, one_shot, clr_active, clr_triggered);
\r
1375 return(StrToDouble(arg));
\r
1380 * Check if a line has been crossed.
\r
1381 * Call crossedLineS() (see crossedLineS() for more documentation)
\r
1382 * and cast it into a bool (true if crossed, otherwise false)
\r
1384 bool crossedLine(string command, bool one_shot = true, color clr_active = CLR_NONE, color clr_triggered = CLR_NONE) {
\r
1385 string arg = crossedLineS(string command, one_shot, clr_active, clr_triggered);
\r
1386 return(arg != "");
\r
1390 * Drop-in replacement for OrderModify().
\r
1391 * Try to handle all errors and locks and return only if successful
\r
1392 * or if the error can not be handled or waited for.
\r
1394 bool orderModifyReliable(
\r
1398 double takeprofit,
\r
1399 datetime expiration,
\r
1400 color arrow_color = CLR_NONE
\r
1404 Print("OrderModifyReliable(" + ticket + "," + price + "," + stoploss + "," + takeprofit + "," + expiration + "," + arrow_color + ")");
\r
1406 while (IsTradeContextBusy()) {
\r
1407 Print("OrderModifyReliable(): Waiting for trade context.");
\r
1408 Sleep(MathRand() / 10);
\r
1410 success = OrderModify(
\r
1412 NormalizeDouble(price, Digits),
\r
1413 NormalizeDouble(stoploss, Digits),
\r
1414 NormalizeDouble(takeprofit, Digits),
\r
1419 Print("OrderModifyReliable(): Success!");
\r
1423 err = GetLastError();
\r
1424 if (isTemporaryError(err)) {
\r
1425 Print("orderModifyReliable(): Temporary Error: " + err + " " + ErrorDescription(err) + ". waiting.");
\r
1427 Print("orderModifyReliable(): Permanent Error: " + err + " " + ErrorDescription(err) + ". giving up.");
\r
1430 Sleep(MathRand() / 10);
\r
1435 * Drop-in replacement for OrderSend().
\r
1436 * Try to handle all errors and locks and return only if successful
\r
1437 * or if the error can not be handled or waited for.
\r
1439 int orderSendReliable(
\r
1446 double takeprofit,
\r
1447 string comment = "",
\r
1449 datetime expiration = 0,
\r
1450 color arrow_color = CLR_NONE
\r
1454 Print("orderSendReliable("
\r
1461 + takeprofit + ","
\r
1464 + expiration + ","
\r
1465 + arrow_color + ")");
\r
1468 if (IsStopped()) {
\r
1469 Print("orderSendReliable(): Trading is stopped!");
\r
1473 if (cmd == OP_BUY) {
\r
1476 if (cmd == OP_SELL) {
\r
1479 if (!IsTradeContextBusy()) {
\r
1480 ticket = OrderSend(
\r
1484 NormalizeDouble(price, MarketInfo(symbol, MODE_DIGITS)),
\r
1486 NormalizeDouble(stoploss, MarketInfo(symbol, MODE_DIGITS)),
\r
1487 NormalizeDouble(takeprofit, MarketInfo(symbol, MODE_DIGITS)),
\r
1494 Print("orderSendReliable(): Success! Ticket: " + ticket);
\r
1495 return(ticket); // the normal exit
\r
1498 err = GetLastError();
\r
1499 if (isTemporaryError(err)) {
\r
1500 Print("orderSendReliable(): Temporary Error: " + err + " " + ErrorDescription(err) + ". waiting.");
\r
1502 Print("orderSendReliable(): Permanent Error: " + err + " " + ErrorDescription(err) + ". giving up.");
\r
1506 Print("orderSendReliable(): Must wait for trade context");
\r
1508 Sleep(MathRand() / 10);
\r
1513 * Drop-in replacement for OrderClose().
\r
1514 * Try to handle all errors and locks and return only if successful
\r
1515 * or if the error can not be handled or waited for.
\r
1517 bool orderCloseReliable(
\r
1522 color arrow_color = CLR_NONE
\r
1526 Print("orderCloseReliable()");
\r
1527 OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES);
\r
1529 if (IsStopped()) {
\r
1530 Print("orderCloseReliable(): Trading is stopped!");
\r
1534 if (OrderType() == OP_BUY) {
\r
1535 price = Bid; // close long at bid
\r
1537 if (OrderType() == OP_SELL) {
\r
1538 price = Ask; // close short at ask
\r
1540 if (!IsTradeContextBusy()) {
\r
1541 success = OrderClose(
\r
1544 NormalizeDouble(price, MarketInfo(OrderSymbol(), MODE_DIGITS)),
\r
1549 Print("orderCloseReliable(): Success!");
\r
1550 return(true); // the normal exit
\r
1553 err = GetLastError();
\r
1554 if (isTemporaryError(err)) {
\r
1555 Print("orderCloseReliable(): Temporary Error: " + err + " " + ErrorDescription(err) + ". waiting.");
\r
1557 Print("orderCloseReliable(): Permanent Error: " + err + " " + ErrorDescription(err) + ". giving up.");
\r
1561 Print("orderCloseReliable(): Must wait for trade context");
\r
1563 Sleep(MathRand() / 10);
\r
1568 * Drop-in replacement for OrderDelete().
\r
1569 * Try to handle all errors and locks and return only if successful
\r
1570 * or if the error can not be handled or waited for.
\r
1572 bool orderDeleteReliable(int ticket) {
\r
1575 Print("orderDeleteReliable(" + ticket + ")");
\r
1577 while (IsTradeContextBusy()) {
\r
1578 Print("OrderDeleteReliable(): Waiting for trade context.");
\r
1579 Sleep(MathRand() / 10);
\r
1582 success = OrderDelete(ticket);
\r
1585 Print("orderDeleteReliable(): success.");
\r
1589 err = GetLastError();
\r
1590 if (isTemporaryError(err)) {
\r
1591 Print("orderDeleteReliable(): Temporary Error: " + err + " " + ErrorDescription(err) + ". waiting.");
\r
1593 Print("orderDeleteReliable(): Permanent Error: " + err + " " + ErrorDescription(err) + ". giving up.");
\r
1596 Sleep(MathRand() / 10);
\r
1601 * Compare two prices.
\r
1602 * The floating point precision of MT4 can make two seemingly identical values
\r
1603 * still differ from each other. This function will compare two prices after
\r
1604 * rounding them to the precision that is used for prices (value of Digits).
\r
1606 bool isEqualPrice(double a, double b) {
\r
1607 return(NormalizeDouble(a, Digits) == NormalizeDouble(b, Digits));
\r
1611 * Is the error temporary (does it make sense to wait).
\r
1613 bool isTemporaryError(int error) {
\r
1615 error == ERR_NO_ERROR ||
\r
1616 error == ERR_COMMON_ERROR ||
\r
1617 error == ERR_SERVER_BUSY ||
\r
1618 error == ERR_NO_CONNECTION ||
\r
1619 error == ERR_MARKET_CLOSED ||
\r
1620 error == ERR_PRICE_CHANGED ||
\r
1621 error == ERR_INVALID_PRICE || //happens sometimes
\r
1622 error == ERR_OFF_QUOTES ||
\r
1623 error == ERR_BROKER_BUSY ||
\r
1624 error == ERR_REQUOTE ||
\r
1625 error == ERR_TRADE_TIMEOUT ||
\r
1626 error == ERR_TRADE_CONTEXT_BUSY
\r
1632 * current symbol without broker initials.
\r
1633 * Return only the first 6 letters of Symbol()
\r
1634 * Some Brokers add their initials at the end
\r
1635 * of their symbol names and some of my EAs and
\r
1636 * indicators want a 6-character symbol name.
\r
1638 string Symbol6() {
\r
1639 return(StringSubstr(Symbol(), 0, 6));
\r
1644 * Get the list of symbols available on this platform.
\r
1645 * The function will resize and fill the supplied array with
\r
1646 * the symbol names and return the number of symbols.
\r
1648 int getSymbols(string &sym[]) {
\r
1651 int f = FileOpenHistory("symbols.raw", FILE_BIN | FILE_READ);
\r
1652 int count = FileSize(f) / 1936;
\r
1653 ArrayResize(sym, count);
\r
1654 for (i = 0; i < count; i++) {
\r
1655 symbol = FileReadString(f, 12);
\r
1657 FileSeek(f, 1924, SEEK_CUR);
\r
1665 * Determine the pip multiplier.
\r
1666 * Determine the pip multiplier (1 or 10) depending on how many
\r
1667 * digits the EURUSD symbol has. This is done by first
\r
1668 * finding the exact name of this symbol in the symbols.raw
\r
1669 * file (it could be EURUSDm or EURUSDiam or any other stupid name
\r
1670 * the broker comes up with only to break other people's code)
\r
1671 * and then usig MarketInfo() for determining the digits.
\r
1673 double pointsPerPip() {
\r
1678 int f = FileOpenHistory("symbols.raw", FILE_BIN | FILE_READ);
\r
1679 int count = FileSize(f) / 1936;
\r
1680 for (i = 0; i < count; i++) {
\r
1681 symbol = FileReadString(f, 12);
\r
1682 if (StringFind(symbol, "EURUSD") != -1) {
\r
1683 digits = MarketInfo(symbol, MODE_DIGITS);
\r
1684 if (digits == 4) {
\r
1691 FileSeek(f, 1924, SEEK_CUR);
\r
1699 * return false only if this is called the first
\r
1700 * time after a new bar has opened, true otherwise.
\r
1701 * Common usage pattern:
\r
1704 * if (isOldBar()) return(0);
\r
1707 * this will extit start() immediately if
\r
1708 * we are not at the opening of a new bar.
\r
1712 if (time == Time[0]){
\r