//+--------------------------------------------------------------------+ //| Copyright: (C) 2014 Forex Software Ltd. | //| Website: http://forexsb.com/ | //| Support: http://forexsb.com/forum/ | //| License: Proprietary under the following circumstances: | //| | //| This code is a part of Forex Strategy Builder. It is free for | //| use as an integral part of Forex Strategy Builder. | //| One can modify it in order to improve the code or to fit it for | //| personal use. This code or any part of it cannot be used in | //| other applications without a permission. | //| The contact information cannot be changed. | //| | //| NO LIABILITY FOR CONSEQUENTIAL DAMAGES | //| | //| In no event shall the author be liable for any damages whatsoever | //| (including, without limitation, incidental, direct, indirect and | //| consequential damages, damages for loss of business profits, | //| business interruption, loss of business information, or other | //| pecuniary loss) arising out of the use or inability to use this | //| product, even if advised of the possibility of such damages. | //+--------------------------------------------------------------------+ #property copyright "Copyright (C) 2014 Forex Software Ltd." #property link "http://forexsb.com" #property version "33.0" #property strict // ----------------------- External variables ----------------------- // static input string StrategyProperties__ = "------------"; // ------ Strategy Properties ------ static input double Entry_Amount = 1; // Amount for a new position [lot] static input double Maximum_Amount = 20; // Maximum position amount [lot] static input double Adding_Amount = 1; // Amount to add on addition [lot] static input double Reducing_Amount = 1; // Amount to close on reduction [lot] input int Stop_Loss = 0; // Stop Loss [point] input int Take_Profit = 0; // Take Profit [point] input int Break_Even = 0; // Break Even [point] static input double Martingale_Multiplier = 0; // Martingale Multiplier static input string IndicatorName1 = "Candle Color"; // ------ Indicator parameters ------ input int Slot1IndParam0 = 30; // Min body height [pips] input int Slot1IndParam1 = 1; // Consecutive candles static input string IndicatorName2 = "Rising Volume"; // ------ Indicator parameters ------ input int Slot2IndParam0 = 80; // Volume period input double Slot2IndParam1 = 2; // Volume threshold static input string IndicatorName3 = "Trailing Stop"; // ------ Indicator parameters ------ input int Slot3IndParam0 = 1000; // Trailing Stop static input string ExpertSettings__ = "------------"; // ------ Expert Settings ------ // A unique number of the expert's orders. static input int Expert_Magic = 16160832; // Expert Magic Number // If account equity drops below this value, the expert will close out all positions and stop automatic trade. // The value must be set in account currency. Example: // Protection_Min_Account = 700 will close positions if the equity drops below 700 USD (EUR if you account is in EUR). static input int Protection_Min_Account = 0; // Stop trading at min account // The expert checks the open positions at every tick and if found no SL or SL lower (higher for short) than selected, // It sets SL to the defined value. The value is in points. Example: // Protection_Max_StopLoss = 200 means 200 pips for 4 digit broker and 20 pips for 5 digit broker. static input int Protection_Max_StopLoss = 0; // Ensure maximum Stop Loss [point] // How many seconds before the expected bar closing to rise a Bar Closing event. static input int Bar_Close_Advance = 15; // Bar closing advance [sec] // Expert writes a log file when Write_Log_File = true. static input bool Write_Log_File = false; // Write a log file // Custom comment. It can be used for setting a binnary option epxiration perod static input string Order_Comment = ""; // Custom order comment // ---------------------------- Options ---------------------------- // // Data bars for calculating the indicator values with the necessary precission. // If set to 0, the expert calculates them automatically. int Min_Data_Bars = 0; // Have to be set to true for STP brokers that cannot set SL and TP together with the position (with OrderSend()). // When Separate_SL_TP = true, the expert first opens the position and after that sets StopLoss and TakeProfit. bool Separate_SL_TP = false; // Separate SL and TP orders // TrailingStop_Moving_Step determines the step of changing the Trailing Stop. // 0 <= TrailingStop_Moving_Step <= 2000 // If TrailingStop_Moving_Step = 0, the Trailing Stop trails at every new extreme price in the position's direction. // If TrailingStop_Moving_Step > 0, the Trailing Stop moves at steps equal to the number of pips chosen. // This prevents sending multiple order modifications. int TrailingStop_Moving_Step = 10; // FIFO (First In First Out) forces the expert to close positions starting from // the oldest one. This rule complies with the new NFA regulations. // If you want to close the positions from the newest one (FILO), change the variable to "false". // This doesn't change the normal work of Forex Strategy Builder. bool FIFO_order = true; // When the log file reaches the preset number of lines, // the expert starts a new log file. int Max_Log_Lines_in_File = 2000; // Used to detect a chart change string __symbol = ""; int __period = -1; //## Import Start enum DataPeriod { DataPeriod_M1 = 1, DataPeriod_M5 = 5, DataPeriod_M15 = 15, DataPeriod_M30 = 30, DataPeriod_H1 = 60, DataPeriod_H4 = 240, DataPeriod_D1 = 1440, DataPeriod_W1 = 10080, DataPeriod_MN1 = 43200 }; enum PosDirection { PosDirection_None, PosDirection_Long, PosDirection_Short, PosDirection_Closed }; enum OrderDirection { OrderDirection_None, OrderDirection_Buy, OrderDirection_Sell }; enum StrategyPriceType { StrategyPriceType_Open, StrategyPriceType_Close, StrategyPriceType_Indicator, StrategyPriceType_CloseAndReverse, StrategyPriceType_Unknown }; enum ExecutionTime { ExecutionTime_DuringTheBar, ExecutionTime_AtBarOpening, ExecutionTime_AtBarClosing, ExecutionTime_CloseAndReverse }; enum TraderOrderType { TraderOrderType_Buy = 0, TraderOrderType_Sell = 1, TraderOrderType_BuyLimit = 2, TraderOrderType_SellLimit = 3, TraderOrderType_BuyStop = 4, TraderOrderType_SellStop = 5 }; enum TradeDirection { TradeDirection_None, TradeDirection_Long, TradeDirection_Short, TradeDirection_Both }; enum LongTradeEntryPrice { LongTradeEntryPrice_Bid, LongTradeEntryPrice_Ask, LongTradeEntryPrice_Chart }; enum OperationType { OperationType_Buy, OperationType_Sell, OperationType_Close, OperationType_Modify }; enum TickType { TickType_Open = 0, TickType_OpenClose = 1, TickType_Regular = 2, TickType_Close = 3, TickType_AfterClose = 4 }; enum InstrumentType { InstrumentType_Forex, InstrumentType_CFD, InstrumentType_Index }; enum SlotTypes { SlotTypes_NotDefined = 0, SlotTypes_Open = 1, SlotTypes_OpenFilter = 2, SlotTypes_Close = 4, SlotTypes_CloseFilter = 8 }; enum IndComponentType { IndComponentType_NotDefined, IndComponentType_OpenLongPrice, IndComponentType_OpenShortPrice, IndComponentType_OpenPrice, IndComponentType_CloseLongPrice, IndComponentType_CloseShortPrice, IndComponentType_ClosePrice, IndComponentType_OpenClosePrice, IndComponentType_IndicatorValue, IndComponentType_AllowOpenLong, IndComponentType_AllowOpenShort, IndComponentType_ForceCloseLong, IndComponentType_ForceCloseShort, IndComponentType_ForceClose, IndComponentType_Other }; enum PositionPriceDependence { PositionPriceDependence_None, PositionPriceDependence_PriceBuyHigher, PositionPriceDependence_PriceBuyLower, PositionPriceDependence_PriceSellHigher, PositionPriceDependence_PriceSellLower, PositionPriceDependence_BuyHigherSellLower, PositionPriceDependence_BuyLowerSelHigher, // Deprecated PositionPriceDependence_BuyLowerSellHigher }; enum BasePrice { BasePrice_Open = 0, BasePrice_High = 1, BasePrice_Low = 2, BasePrice_Close = 3, BasePrice_Median = 4, // Price[bar] = (Low[bar] + High[bar]) / 2; BasePrice_Typical = 5, // Price[bar] = (Low[bar] + High[bar] + Close[bar]) / 3; BasePrice_Weighted = 6 // Price[bar] = (Low[bar] + High[bar] + 2 * Close[bar]) / 4; }; enum MAMethod { MAMethod_Simple = 0, MAMethod_Weighted = 1, MAMethod_Exponential = 2, MAMethod_Smoothed = 3 }; enum SameDirSignalAction { SameDirSignalAction_Nothing, SameDirSignalAction_Add, SameDirSignalAction_Winner, SameDirSignalAction_Loser, }; enum OppositeDirSignalAction { OppositeDirSignalAction_Nothing, OppositeDirSignalAction_Reduce, OppositeDirSignalAction_Close, OppositeDirSignalAction_Reverse }; enum PermanentProtectionType { PermanentProtectionType_Relative, PermanentProtectionType_Absolute }; enum TrailingStopMode { TrailingStopMode_Bar, TrailingStopMode_Tick }; enum IndicatorLogic { IndicatorLogic_The_indicator_rises, IndicatorLogic_The_indicator_falls, IndicatorLogic_The_indicator_is_higher_than_the_level_line, IndicatorLogic_The_indicator_is_lower_than_the_level_line, IndicatorLogic_The_indicator_crosses_the_level_line_upward, IndicatorLogic_The_indicator_crosses_the_level_line_downward, IndicatorLogic_The_indicator_changes_its_direction_upward, IndicatorLogic_The_indicator_changes_its_direction_downward, IndicatorLogic_The_price_buy_is_higher_than_the_ind_value, IndicatorLogic_The_price_buy_is_lower_than_the_ind_value, IndicatorLogic_The_price_open_is_higher_than_the_ind_value, IndicatorLogic_The_price_open_is_lower_than_the_ind_value, IndicatorLogic_It_does_not_act_as_a_filter, }; enum BandIndLogic { BandIndLogic_The_bar_opens_below_the_Upper_Band, BandIndLogic_The_bar_opens_above_the_Upper_Band, BandIndLogic_The_bar_opens_below_the_Lower_Band, BandIndLogic_The_bar_opens_above_the_Lower_Band, BandIndLogic_The_position_opens_below_the_Upper_Band, BandIndLogic_The_position_opens_above_the_Upper_Band, BandIndLogic_The_position_opens_below_the_Lower_Band, BandIndLogic_The_position_opens_above_the_Lower_Band, BandIndLogic_The_bar_opens_below_Upper_Band_after_above, BandIndLogic_The_bar_opens_above_Upper_Band_after_below, BandIndLogic_The_bar_opens_below_Lower_Band_after_above, BandIndLogic_The_bar_opens_above_Lower_Band_after_below, BandIndLogic_The_bar_closes_below_the_Upper_Band, BandIndLogic_The_bar_closes_above_the_Upper_Band, BandIndLogic_The_bar_closes_below_the_Lower_Band, BandIndLogic_The_bar_closes_above_the_Lower_Band, BandIndLogic_It_does_not_act_as_a_filter }; //## Import Start bool LabelCreate(const long chart_ID = 0, // chart's ID const string name = "Label", // label name const int sub_window = 0, // subwindow index const int x = 0, // X coordinate const int y = 0, // Y coordinate const ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER, // chart corner for anchoring const string text = "Label", // text const string font = "Arial", // font const int font_size = 8, // font size const color clr = clrWhite, // color const double angle = 0.0, // text slope const ENUM_ANCHOR_POINT anchor = ANCHOR_LEFT_UPPER, // anchor type const bool back = false, // in the background const bool selection = false, // highlight to move const bool hidden = true, // hidden in the object list const string tooltip = "\n", // sets the tooltip const long z_order = 0) // priority for mouse click { ResetLastError(); if (!ObjectCreate(chart_ID, name, OBJ_LABEL, sub_window, 0, 0)) { Print(__FUNCTION__, ": failed to create text label! Error code = ", GetLastError()); return (false); } ObjectSetInteger(chart_ID, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(chart_ID, name, OBJPROP_YDISTANCE, y); ObjectSetInteger(chart_ID, name, OBJPROP_CORNER, corner); ObjectSetString(chart_ID, name, OBJPROP_TEXT, text); ObjectSetString(chart_ID, name, OBJPROP_FONT, font); ObjectSetInteger(chart_ID, name, OBJPROP_FONTSIZE, font_size); ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr); ObjectSetDouble(chart_ID, name, OBJPROP_ANGLE, angle); ObjectSetInteger(chart_ID, name, OBJPROP_ANCHOR, anchor); ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back); ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection); ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection); ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden); ObjectSetString(chart_ID, name, OBJPROP_TOOLTIP, tooltip); ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order); return (true); } bool LabelTextChange(const long chart_ID, const string name, const string text) { ResetLastError(); if (!ObjectSetString(chart_ID, name, OBJPROP_TEXT, text)) { Print(__FUNCTION__, ": failed to change the text! Error code = ", GetLastError()); return (false); } return (true); } bool LabelDelete(const long chart_ID = 0, const string name = "Label") { if (!ObjectDelete(chart_ID, name)) { Print(__FUNCTION__, ": failed to delete a text label! Error code = ", GetLastError()); return (false); } return (true); } color GetChartForeColor(const long chartId = 0) { long foreColor = clrWhite; ChartGetInteger(chartId, CHART_COLOR_FOREGROUND, 0, foreColor); return ((color) foreColor); } color GetChartBackColor(const long chartId = 0) { long backColor = clrBlack; ChartGetInteger(chartId, CHART_COLOR_BACKGROUND, 0, backColor); return ((color) backColor); } string LoadStringFromFile(string filename) { string text; int intSize; int handle = FileOpen(filename, FILE_TXT | FILE_READ | FILE_ANSI); if (handle == INVALID_HANDLE) return ""; while (!FileIsEnding(handle)) { intSize = FileReadInteger(handle, INT_VALUE); text += FileReadString(handle, intSize); } FileClose(handle); return text; } void SaveStringToFile(string filename, string text) { int handle = FileOpen(filename, FILE_TXT | FILE_WRITE | FILE_ANSI); if (handle == INVALID_HANDLE) return; FileWriteString(handle, text); FileClose(handle); } bool ArrayContainsString(const string &array[], string text) { for (int i = 0; i < ArraySize(array); i++) if (array[i] == text) return true; return false; } void ArrayAppendString(string &array[], string text) { int size = ArraySize(array); ArrayResize(array, size + 1); array[size] = text; } string DataPeriodToString(DataPeriod period) { switch (period) { case DataPeriod_M1: return ("M1"); case DataPeriod_M5: return ("M5"); case DataPeriod_M15: return ("M15"); case DataPeriod_M30: return ("M30"); case DataPeriod_H1: return ("H1"); case DataPeriod_H4: return ("H4"); case DataPeriod_D1: return ("D1"); case DataPeriod_W1: return ("W1"); case DataPeriod_MN1: return ("MN1"); } return ("D1"); } DataPeriod StringToDataPeriod(string period) { if (period == "M1") return (DataPeriod_M1); if (period == "M5") return (DataPeriod_M5); if (period == "M15") return (DataPeriod_M15); if (period == "M30") return (DataPeriod_M30); if (period == "H1") return (DataPeriod_H1); if (period == "H4") return (DataPeriod_H4); if (period == "D1") return (DataPeriod_D1); if (period == "W1") return (DataPeriod_W1); if (period == "MN1") return (DataPeriod_MN1); return (DataPeriod_D1); } DataPeriod EnumTimeFramesToPeriod(int period) { switch (period) { case PERIOD_M1: return (DataPeriod_M1); case PERIOD_M5: return (DataPeriod_M5); case PERIOD_M15: return (DataPeriod_M15); case PERIOD_M30: return (DataPeriod_M30); case PERIOD_H1: return (DataPeriod_H1); case PERIOD_H4: return (DataPeriod_H4); case PERIOD_D1: return (DataPeriod_D1); case PERIOD_W1: return (DataPeriod_W1); case PERIOD_MN1: return (DataPeriod_MN1); } return (DataPeriod_D1); } void SetMAMethodsText(string &list[]) { ArrayResize(list, 4); list[0] = "Simple"; list[1] = "Weighted"; list[2] = "Exponential"; list[3] = "Smoothed"; } void SetBasePricesText(string &list[]) { ArrayResize(list, 8); list[0] = "Open"; list[1] = "High"; list[2] = "Low"; list[3] = "Close"; list[4] = "Median"; list[5] = "Typical"; list[6] = "Low"; list[7] = "Weighted"; } string SameDirSignalActionToString(SameDirSignalAction action) { switch (action) { case SameDirSignalAction_Add: return ("Add"); case SameDirSignalAction_Winner: return ("Winner"); case SameDirSignalAction_Loser: return ("Loser"); case SameDirSignalAction_Nothing: return ("Nothing"); } return (""); } string OppositeDirSignalActionToString(OppositeDirSignalAction action) { switch (action) { case OppositeDirSignalAction_Close: return ("Close"); case OppositeDirSignalAction_Nothing: return ("Nothing"); case OppositeDirSignalAction_Reduce: return ("Reduce"); case OppositeDirSignalAction_Reverse: return ("Reverse"); } return (""); } string SlotTypeToString(SlotTypes slotType) { string stringCaptionText = "Not Defined"; switch (slotType) { case SlotTypes_Open: stringCaptionText = "Opening Point of the Position"; break; case SlotTypes_OpenFilter: stringCaptionText = "Opening Logic Condition"; break; case SlotTypes_Close: stringCaptionText = "Closing Point of the Position"; break; case SlotTypes_CloseFilter: stringCaptionText = "Closing Logic Condition"; break; } return (stringCaptionText); } SlotTypes SlotTypeFromShortString(string shortString) { if (shortString == "Open") return (SlotTypes_Open); if (shortString == "OpenFilter") return (SlotTypes_OpenFilter); if (shortString == "Close") return (SlotTypes_Close); if (shortString == "CloseFilter") return (SlotTypes_CloseFilter); return (SlotTypes_NotDefined); } bool StringBoolToBool(string flag) { return (flag == "True" || flag == "true"); } string StringRemoveWhite(string instring) { if (instring == "" || instring == NULL) return (""); string out = instring; string white[4] = {" ", "\r", "\n", "\t"}; for (int i = 0; i < ArraySize(white); i++) StringReplace(out, white[i], ""); return (out); } class ListString { int m_count; string m_data[]; public: int Count() { return (m_count); } bool Contains(string element); void Add(string element); string Get(int index) { return (m_data[index]); }; }; void ListString::Add(string element) { ArrayResize(m_data, m_count + 1); m_data[m_count] = element; m_count++; } bool ListString::Contains(string element) { for (int i = 0; i < m_count; i++) if (m_data[i] == element) return (true); return (false); } class DictStringBool { int m_count; string m_key[]; bool m_val[]; public: int Count() { return m_count; } bool ContainsKey(string key); void Add(string key, bool value); void Set(string key, bool value); string Key(int index) { return (m_key[index]); } bool Value(string key); }; bool DictStringBool::ContainsKey(string key) { if (m_count == 0) return (false); for (int i = 0; i < m_count; i++) if (m_key[i] == key) return (true); return (false); } void DictStringBool::Add(string key, bool value) { ArrayResize(m_key, m_count + 1); ArrayResize(m_val, m_count + 1); m_key[m_count] = key; m_val[m_count] = value; m_count++; } void DictStringBool::Set(string key, bool value) { for (int i = 0; i < m_count; i++) if (m_key[i] == key) { m_val[i] = value; break; } } bool DictStringBool::Value(string key) { for (int i = 0; i < m_count; i++) if (m_key[i] == key) return (m_val[i]); Print("ERROR DictStringBool::Value: Geven key does not exist."); return (false); } //## Import Start string GetErrorDescription(int errorCode) { string message; switch (errorCode) { //--- codes returned from trade server case 0: message = "No error"; break; case 1: message = "No error, trade conditions not changed"; break; case 2: message = "Common error"; break; case 3: message = "Invalid trade parameters"; break; case 4: message = "Trade server is busy"; break; case 5: message = "Old version of the client terminal"; break; case 6: message = "No connection with trade server"; break; case 7: message = "Not enough rights"; break; case 8: message = "Too frequent requests"; break; case 9: message = "Malfunctional trade operation (never returned error)"; break; case 64: message = "Account disabled"; break; case 65: message = "Invalid account"; break; case 128: message = "Trade timeout"; break; case 129: message = "Invalid price"; break; case 130: message = "Invalid stops"; break; case 131: message = "Invalid trade volume"; break; case 132: message = "Market is closed"; break; case 133: message = "Trade is disabled"; break; case 134: message = "Not enough money"; break; case 135: message = "Price changed"; break; case 136: message = "Off quotes"; break; case 137: message = "Broker is busy (never returned error)"; break; case 138: message = "Requote"; break; case 139: message = "Order is locked"; break; case 140: message = "Long positions only allowed"; break; case 141: message = "Too many requests"; break; case 145: message = "Modification denied because order is too close to market"; break; case 146: message = "Trade context is busy"; break; case 147: message = "Expirations are denied by broker"; break; case 148: message = "Amount of open and pending orders has reached the limit"; break; case 149: message = "Hedging is prohibited"; break; case 150: message = "Prohibited by FIFO rules"; break; //--- mql4 errors case 4000: message = "No error (never generated code)"; case 4001: message = "Wrong function pointer"; break; case 4002: message = "Array index is out of range"; break; case 4003: message = "No memory for function call stack"; break; case 4004: message = "Recursive stack overflow"; break; case 4005: message = "Not enough stack for parameter"; break; case 4006: message = "No memory for parameter string"; break; case 4007: message = "No memory for temp string"; break; case 4008: message = "Non-initialized string"; break; case 4009: message = "Non-initialized string in array"; break; case 4010: message = "No memory for array string"; break; case 4011: message = "Too long string"; break; case 4012: message = "Remainder from zero divide"; break; case 4013: message = "Zero divide"; break; case 4014: message = "Unknown command"; break; case 4015: message = "Wrong jump (never generated error)"; break; case 4016: message = "Non-initialized array"; break; case 4017: message = "Dll calls are not allowed"; break; case 4018: message = "Cannot load library"; break; case 4019: message = "Cannot call function"; break; case 4020: message = "Expert function calls are not allowed"; break; case 4021: message = "Not enough memory for temp string returned from function"; break; case 4022: message = "System is busy (never generated error)"; break; case 4023: message = "Dll-function call critical error"; break; case 4024: message = "Internal error"; break; case 4025: message = "Out of memory"; break; case 4026: message = "Invalid pointer"; break; case 4027: message = "Too many formatters in the format function"; break; case 4028: message = "Parameters count is more than formatters count"; break; case 4029: message = "Invalid array"; break; case 4030: message = "No reply from chart"; break; case 4050: message = "Invalid function parameters count"; break; case 4051: message = "Invalid function parameter value"; break; case 4052: message = "String function internal error"; break; case 4053: message = "Some array error"; break; case 4054: message = "Incorrect series array usage"; break; case 4055: message = "Custom indicator error"; break; case 4056: message = "Arrays are incompatible"; break; case 4057: message = "Global variables processing error"; break; case 4058: message = "Global variable not found"; break; case 4059: message = "Function is not allowed in testing mode"; break; case 4060: message = "Function is not confirmed"; break; case 4061: message = "Send mail error"; break; case 4062: message = "String parameter expected"; break; case 4063: message = "Integer parameter expected"; break; case 4064: message = "Double parameter expected"; break; case 4065: message = "Array as parameter expected"; break; case 4066: message = "Requested history data is in update state"; break; case 4067: message = "Internal trade error"; break; case 4068: message = "Resource not found"; break; case 4069: message = "Resource not supported"; break; case 4070: message = "Duplicate resource"; break; case 4071: message = "Cannot initialize custom indicator"; break; case 4072: message = "Cannot load custom indicator"; break; case 4073: message = "No history data"; break; case 4074: message = "No memory for history data"; break; case 4099: message = "End of file"; break; case 4100: message = "Some file error"; break; case 4101: message = "Wrong file name"; break; case 4102: message = "Too many opened files"; break; case 4103: message = "Cannot open file"; break; case 4104: message = "Incompatible access to a file"; break; case 4105: message = "No order selected"; break; case 4106: message = "Unknown symbol"; break; case 4107: message = "Invalid price parameter for trade function"; break; case 4108: message = "Invalid ticket"; break; case 4109: message = "Trade is not allowed in the expert properties"; break; case 4110: message = "Longs are not allowed in the expert properties"; break; case 4111: message = "Shorts are not allowed in the expert properties"; break; case 4200: message = "Object already exists"; break; case 4201: message = "Unknown object property"; break; case 4202: message = "Object does not exist"; break; case 4203: message = "Unknown object type"; break; case 4204: message = "No object name"; break; case 4205: message = "Object coordinates error"; break; case 4206: message = "No specified subwindow"; break; case 4207: message = "Graphical object error"; break; case 4210: message = "Unknown chart property"; break; case 4211: message = "Chart not found"; break; case 4212: message = "Chart subwindow not found"; break; case 4213: message = "Chart indicator not found"; break; case 4220: message = "Symbol select error"; break; case 4250: message = "Notification error"; break; case 4251: message = "Notification parameter error"; break; case 4252: message = "Notifications disabled"; break; case 4253: message = "Notification send too frequent"; break; case 5001: message = "Too many opened files"; break; case 5002: message = "Wrong file name"; break; case 5003: message = "Too long file name"; break; case 5004: message = "Cannot open file"; break; case 5005: message = "Text file buffer allocation error"; break; case 5006: message = "Cannot delete file"; break; case 5007: message = "Invalid file handle (file closed or was not opened)"; break; case 5008: message = "Wrong file handle (handle index is out of handle table)"; break; case 5009: message = "File must be opened with FILE_WRITE flag"; break; case 5010: message = "File must be opened with FILE_READ flag"; break; case 5011: message = "File must be opened with FILE_BIN flag"; break; case 5012: message = "File must be opened with FILE_TXT flag"; break; case 5013: message = "File must be opened with FILE_TXT or FILE_CSV flag"; break; case 5014: message = "File must be opened with FILE_CSV flag"; break; case 5015: message = "File read error"; break; case 5016: message = "File write error"; break; case 5017: message = "String size must be specified for binary file"; break; case 5018: message = "Incompatible file (for string arrays-TXT, for others-BIN)"; break; case 5019: message = "File is directory, not file"; break; case 5020: message = "File does not exist"; break; case 5021: message = "File cannot be rewritten"; break; case 5022: message = "Wrong directory name"; break; case 5023: message = "Directory does not exist"; break; case 5024: message = "Specified file is not directory"; break; case 5025: message = "Cannot delete directory"; break; case 5026: message = "Cannot clean directory"; break; case 5027: message = "Array resize error"; break; case 5028: message = "String resize error"; break; case 5029: message = "Structure contains strings or dynamic arrays"; break; default: message = "Unknown error"; break; } return (message); } //## Import Start class DataSet { public: // Constructor DataSet(string chart); // Properties string Chart; string Symbol; DataPeriod Period; int LotSize; double Spread; int Digits; double Point; double Pip; bool IsFiveDigits; int StopLevel; double TickValue; double MinLot; double MaxLot; double LotStep; double MarginRequired; int Bars; datetime ServerTime; double Bid; double Ask; datetime Time[]; double Open[]; double High[]; double Low[]; double Close[]; long Volume[]; // Methods void SetPrecision(void); }; DataSet::DataSet(string chart) { Chart = chart; string parts[]; StringSplit(chart, ',', parts); Symbol = parts[0]; Period = StringToDataPeriod(parts[1]); } void DataSet::SetPrecision(void) { IsFiveDigits = (Digits == 3 || Digits == 5); Point = 1/MathPow(10, Digits); Pip = IsFiveDigits ? 10*Point : Point; } //## Import Start class DataMarket { public: string Symbol; DataPeriod Period; bool IsNewBid; double OldBid; double OldAsk; double OldClose; double Bid; double Ask; double Close; long Volume; datetime TickLocalTime; datetime TickServerTime; datetime BarTime; double AccountBalance; double AccountEquity; double AccountFreeMargin; double PositionLots; double PositionOpenPrice; datetime PositionOpenTime; double PositionStopLoss; double PositionTakeProfit; double PositionProfit; PosDirection PositionDirection; int ConsecutiveLosses; int WrongStopLoss; int WrongTakeProf; int WrongStopsRetry; bool IsFailedCloseOrder; int CloseOrderTickCounter; bool IsSentCloseOrder; int LotSize; double Spread; double Point; int StopLevel; double TickValue; double MinLot; double MaxLot; double LotStep; double MarginRequired; }; //## Import Start class IndicatorComp { public: // Constructors IndicatorComp() { CompName = "Not defined"; DataType = IndComponentType_NotDefined; FirstBar = 0; UsePreviousBar = 0; ShowInDynInfo = true; PosPriceDependence = PositionPriceDependence_None; } // Properties string CompName; int FirstBar; int UsePreviousBar; // Deprecated IndComponentType DataType; PositionPriceDependence PosPriceDependence; bool ShowInDynInfo; double Value[]; // Methods double GetLastValue(int indexFromEnd); }; double IndicatorComp::GetLastValue(int indexFromEnd = 0) { int bars = ArraySize(Value); return (bars > indexFromEnd ? Value[bars - indexFromEnd - 1]: 0); } //## Import Start class ListParameter { public: // Constructors ListParameter() { Caption = ""; Text = ""; Index = -1; Enabled = false; } // Properties string Caption; string Text; int Index; bool Enabled; }; class NumericParameter { public: // Constructor NumericParameter() { Caption = ""; Value = 0; Enabled = false; } // Properties string Caption; double Value; bool Enabled; }; class CheckParameter { public: // Constructor CheckParameter() { Caption = ""; Checked = false; Enabled = false; } // Properties string Caption; bool Checked; bool Enabled; }; //## Import Start //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class Indicator { protected: double Sigma(void); double Epsilon(void); void NormalizeComponentValue(const double &componentValue[], const datetime &strategyTime[], double &output[], int ltfShift); int NormalizeComponentFirstBar(int componentFirstBar, datetime &strategyTime[]); bool IsSignalComponent(IndComponentType componentType); void Price(BasePrice priceType, double &price[]); void MovingAverage(int period, int shift, MAMethod maMethod, const double &source[], double &movingAverage[]); // Logic rules and signals void OscillatorLogic(int firstBar, int previous, const double &adIndValue[], double levelLong, double levelShort, IndicatorComp &indCompLong, IndicatorComp &indCompShort, IndicatorLogic indLogic); void NoDirectionOscillatorLogic(int firstBar, int previous, const double &adIndValue[], double dLevel, IndicatorComp &indComp, IndicatorLogic indLogic); void BandIndicatorLogic(int firstBar, int previous, const double &adUpperBand[], const double &adLowerBand[], IndicatorComp &indCompLong, IndicatorComp &indCompShort, BandIndLogic indLogic); void IndicatorRisesLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void IndicatorFallsLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void IndicatorChangesItsDirectionUpward(int firstBar, int previous, double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void IndicatorChangesItsDirectionDownward(int firstBar, int previous, double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void IndicatorIsHigherThanAnotherIndicatorLogic(int firstBar, int previous, const double &adIndValue[], double &adAnotherIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void IndicatorIsLowerThanAnotherIndicatorLogic(int firstBar, int previous, const double &adIndValue[], double &adAnotherIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void IndicatorCrossesAnotherIndicatorUpwardLogic(int firstBar, int previous, const double &adIndValue[], double &adAnotherIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void IndicatorCrossesAnotherIndicatorDownwardLogic(int firstBar, int previous, const double &adIndValue[], double &adAnotherIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void BarOpensAboveIndicatorLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void BarOpensBelowIndicatorLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void BarOpensAboveIndicatorAfterOpeningBelowLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void BarOpensBelowIndicatorAfterOpeningAboveLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void BarClosesAboveIndicatorLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); void BarClosesBelowIndicatorLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort); public: // Constructors Indicator(void); ~Indicator(void); // Properties string IndicatorName; string WarningMessage; bool IsDiscreteValues; bool UsePreviousBarValue; bool IsSeparateChart; bool IsBacktester; bool IsDeafultGroupAll; // XXX Outdated. Do not use. bool IsDefaultGroupAll; bool IsAllowLTF; SlotTypes SlotType; ExecutionTime ExecTime; ListParameter *ListParam[5]; NumericParameter *NumParam[6]; CheckParameter *CheckParam[2]; IndicatorComp *Component[10]; DataSet *Data; // Methods virtual void Calculate(DataSet &dataSet); void NormalizeComponents(DataSet &strategyDataSet); void ShiftSignal(int shift); void RepeatSignal(int repeat); int Components(void); string IndicatorParamToString(void); }; Indicator::Indicator(void) { IndicatorName = ""; IsBacktester = false; IsDiscreteValues = false; IsSeparateChart = false; IsDeafultGroupAll = false; IsDefaultGroupAll = false; IsAllowLTF = true; SlotType = SlotTypes_NotDefined; ExecTime = ExecutionTime_DuringTheBar; for (int i = 0; i < 5; i++) ListParam[i] = new ListParameter(); for (int i = 0; i < 6; i++) NumParam[i] = new NumericParameter(); for (int i = 0; i < 2; i++) CheckParam[i] = new CheckParameter(); for (int i = 0; i < 10; i++) Component[i] = new IndicatorComp(); } Indicator::~Indicator(void) { for (int i = 0; i < 5; i++) delete ListParam[i]; for (int i = 0; i < 6; i++) delete NumParam[i]; for (int i = 0; i < 2; i++) delete CheckParam[i]; for (int i = 0; i < 10; i++) delete Component[i]; } void Indicator::Calculate(DataSet &dataSet) { } void Indicator::NormalizeComponents(DataSet &strategyDataSet) { for (int i = 0; i < Components(); i++) { double value[]; int ltfShift = 0; if (Component[i].PosPriceDependence != PositionPriceDependence_None) ltfShift = Data.Period > strategyDataSet.Period ? 1 : 0; NormalizeComponentValue(Component[i].Value, strategyDataSet.Time, value, ltfShift); ArrayCopy(Component[i].Value, value); Component[i].FirstBar = NormalizeComponentFirstBar(Component[i].FirstBar, strategyDataSet.Time); } } void Indicator::ShiftSignal(int shift) { for (int i = 0; i < Components(); i++) { if (!IsSignalComponent(Component[i].DataType)) continue; int bars = ArraySize(Component[i].Value); double value[]; ArrayResize(value, bars); ArrayInitialize(value, 0); ArrayCopy(value, Component[i].Value, shift, 0, WHOLE_ARRAY); for (int bar = 0; bar < bars; bar++) Component[i].Value[bar] = value[bar]; } } void Indicator::RepeatSignal(int repeat) { for (int i = 0; i < Components(); i++) { if (!IsSignalComponent(Component[i].DataType)) continue; int bars = ArraySize(Component[i].Value); for (int bar = 0; bar < bars; bar++) { if (Component[i].Value[bar] < 0.5) continue; for (int r = 1; r <= repeat; r++) if (++bar < bars) Component[i].Value[bar] = 1; } } } Indicator::NormalizeComponentValue(const double &componentValue[], const datetime &strategyTime[], double &output[], int ltfShift) { int strategyBars = ArraySize(strategyTime); ArrayResize(output, strategyBars); ArrayInitialize(output, 0); int reachedBar = 0; for (int ltfBar = ltfShift; ltfBar < Data.Bars; ltfBar++) { datetime ltfOpenTime = Data.Time[ltfBar]; datetime ltfCloseTime = ltfOpenTime + ((int) Data.Period) * 60; for (int bar = reachedBar; bar < strategyBars; bar++) { reachedBar = bar; datetime time = strategyTime[bar]; if (time >= ltfOpenTime && time < ltfCloseTime) output[bar] = componentValue[ltfBar - ltfShift]; else if (time >= ltfCloseTime) break; } } } int Indicator::NormalizeComponentFirstBar(int componentFirstBar, datetime &strategyTime[]) { datetime firstBarTime = Data.Time[componentFirstBar]; for (int bar = 0; bar < ArraySize(strategyTime); bar++) if (strategyTime[bar] >= firstBarTime) return bar; return componentFirstBar; } bool Indicator::IsSignalComponent(IndComponentType componentType) { return componentType == IndComponentType_AllowOpenLong || componentType == IndComponentType_AllowOpenShort || componentType == IndComponentType_CloseLongPrice || componentType == IndComponentType_ClosePrice || componentType == IndComponentType_CloseShortPrice || componentType == IndComponentType_ForceClose || componentType == IndComponentType_ForceCloseLong || componentType == IndComponentType_ForceCloseShort || componentType == IndComponentType_OpenClosePrice || componentType == IndComponentType_OpenLongPrice || componentType == IndComponentType_OpenPrice || componentType == IndComponentType_OpenShortPrice; } int Indicator::Components(void) { for (int i = 0; i < 10; i++) if (Component[i].DataType == IndComponentType_NotDefined) return (i); return (10); } string Indicator::IndicatorParamToString(void) { string text; for (int i = 0; i < 5; i++) if (ListParam[i].Enabled) text += StringFormat("%s: %s\n", ListParam[i].Caption, ListParam[i].Text); for (int i = 0; i < 6; i++) if (NumParam[i].Enabled) text += StringFormat("%s: %g\n", NumParam[i].Caption, NumParam[i].Value); for (int i = 0; i < 2; i++) if (CheckParam[i].Enabled) text += StringFormat("%s: %s\n", CheckParam[i].Caption, (CheckParam[i].Checked ? "Yes" : "No")); return (text); } void Indicator::Price(BasePrice priceType, double &price[]) { ArrayResize(price, Data.Bars); ArrayInitialize(price, 0); switch (priceType) { case BasePrice_Open: ArrayCopy(price, Data.Open); break; case BasePrice_High: ArrayCopy(price, Data.High); break; case BasePrice_Low: ArrayCopy(price, Data.Low); break; case BasePrice_Close: ArrayCopy(price, Data.Close); break; case BasePrice_Median: for (int bar = 0; bar < Data.Bars; bar++) price[bar] = (Data.Low[bar] + Data.High[bar]) / 2; break; case BasePrice_Typical: for (int bar = 0; bar < Data.Bars; bar++) price[bar] = (Data.Low[bar] + Data.High[bar] + Data.Close[bar]) / 3; break; case BasePrice_Weighted: for (int bar = 0; bar < Data.Bars; bar++) price[bar] = (Data.Low[bar] + Data.High[bar] + 2 * Data.Close[bar]) / 4; break; } } void Indicator::MovingAverage(int period, int shift, MAMethod maMethod, const double &source[], double &movingAverage[]) { int bars = ArraySize(source); ArrayResize(movingAverage, bars); ArrayInitialize(movingAverage, 0); if (period <= 1 && shift == 0) { // There is no smoothing ArrayCopy(movingAverage, source); return; } if (period > bars || period + shift <= 0 || period + shift > bars) { // Error in the parameters string message = IndicatorName + " " + Data.Symbol + " " + DataPeriodToString(Data.Period) + "Wrong MovingAverage parameters(Period: " + IntegerToString(period) + ", Shift: " + IntegerToString(shift) + ", Source bars: " + IntegerToString(bars) + ")"; Print(message); ArrayCopy(movingAverage, source); return; } for (int bar = 0; bar < period + shift - 1; bar++) movingAverage[bar] = 0; double sum = 0; for (int bar = 0; bar < period; bar++) sum += source[bar]; movingAverage[period + shift - 1] = sum / period; int lastBar = MathMin(bars, bars - shift); switch (maMethod) { case MAMethod_Simple: { for (int bar = period; bar < lastBar; bar++) movingAverage[bar + shift] = movingAverage[bar + shift - 1] + source[bar] / period - source[bar - period] / period; } break; case MAMethod_Exponential: { double pr = 2.0 / (period + 1); for (int bar = period; bar < lastBar; bar++) movingAverage[bar + shift] = source[bar] * pr + movingAverage[bar + shift - 1] * (1 - pr); } break; case MAMethod_Weighted: { double weight = period * (period + 1) / 2.0; for (int bar = period; bar < lastBar; bar++) { sum = 0; for (int i = 0; i < period; i++) sum += source[bar - i] * (period - i); movingAverage[bar + shift] = sum / weight; } } break; case MAMethod_Smoothed: { for (int bar = period; bar < lastBar; bar++) movingAverage[bar + shift] = (movingAverage[bar + shift - 1] * (period - 1) + source[bar]) / period; } break; default: break; } for (int bar = bars + shift; bar < bars; bar++) movingAverage[bar] = 0; } double Indicator::Sigma(void) { return (IsSeparateChart ? 0.000005 : Data.Point * 0.5); } double Indicator::Epsilon(void) { return (0.0000001); } void Indicator::OscillatorLogic(int firstBar, int previous, const double &adIndValue[], double levelLong, double levelShort, IndicatorComp &indCompLong, IndicatorComp &indCompShort, IndicatorLogic indLogic) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } switch (indLogic) { case IndicatorLogic_The_indicator_rises: for (int bar = firstBar; bar < Data.Bars; bar++) { int currentBar = bar - previous; int baseBar = currentBar - 1; bool isHigher = adIndValue[currentBar] > adIndValue[baseBar]; if (!IsDiscreteValues) // Aroon oscillator uses IsDiscreteValues = true { bool isNoChange = true; while (MathAbs(adIndValue[currentBar] - adIndValue[baseBar]) < sigma && isNoChange && baseBar > firstBar) { isNoChange = (isHigher == (adIndValue[baseBar + 1] > adIndValue[baseBar])); baseBar--; } } indCompLong.Value[bar] = adIndValue[baseBar] < adIndValue[currentBar] - sigma ? 1 : 0; indCompShort.Value[bar] = adIndValue[baseBar] > adIndValue[currentBar] + sigma ? 1 : 0; } break; case IndicatorLogic_The_indicator_falls: for (int bar = firstBar; bar < Data.Bars; bar++) { int currentBar = bar - previous; int baseBar = currentBar - 1; bool isHigher = adIndValue[currentBar] > adIndValue[baseBar]; if (!IsDiscreteValues) // Aroon oscillator uses IsDiscreteValues = true { bool isNoChange = true; while (MathAbs(adIndValue[currentBar] - adIndValue[baseBar]) < sigma && isNoChange && baseBar > firstBar) { isNoChange = (isHigher == (adIndValue[baseBar + 1] > adIndValue[baseBar])); baseBar--; } } indCompLong.Value[bar] = adIndValue[baseBar] > adIndValue[currentBar] + sigma ? 1 : 0; indCompShort.Value[bar] = adIndValue[baseBar] < adIndValue[currentBar] - sigma ? 1 : 0; } break; case IndicatorLogic_The_indicator_is_higher_than_the_level_line: for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = adIndValue[bar - previous] > levelLong + sigma ? 1 : 0; indCompShort.Value[bar] = adIndValue[bar - previous] < levelShort - sigma ? 1 : 0; } break; case IndicatorLogic_The_indicator_is_lower_than_the_level_line: for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = adIndValue[bar - previous] < levelLong - sigma ? 1 : 0; indCompShort.Value[bar] = adIndValue[bar - previous] > levelShort + sigma ? 1 : 0; } break; case IndicatorLogic_The_indicator_crosses_the_level_line_upward: for (int bar = firstBar; bar < Data.Bars; bar++) { int baseBar = bar - previous - 1; while (MathAbs(adIndValue[baseBar] - levelLong) < sigma && baseBar > firstBar) baseBar--; indCompLong.Value[bar] = (adIndValue[baseBar] < levelLong - sigma && adIndValue[bar - previous] > levelLong + sigma) ? 1 : 0; indCompShort.Value[bar] = (adIndValue[baseBar] > levelShort + sigma && adIndValue[bar - previous] < levelShort - sigma) ? 1 : 0; } break; case IndicatorLogic_The_indicator_crosses_the_level_line_downward: for (int bar = firstBar; bar < Data.Bars; bar++) { int baseBar = bar - previous - 1; while (MathAbs(adIndValue[baseBar] - levelLong) < sigma && baseBar > firstBar) baseBar--; indCompLong.Value[bar] = (adIndValue[baseBar] > levelLong + sigma && adIndValue[bar - previous] < levelLong - sigma) ? 1 : 0; indCompShort.Value[bar] = (adIndValue[baseBar] < levelShort - sigma && adIndValue[bar - previous] > levelShort + sigma) ? 1 : 0; } break; case IndicatorLogic_The_indicator_changes_its_direction_upward: for (int bar = firstBar; bar < Data.Bars; bar++) { int bar0 = bar - previous; int bar1 = bar0 - 1; while (MathAbs(adIndValue[bar0] - adIndValue[bar1]) < sigma && bar1 > firstBar) bar1--; int iBar2 = bar1 - 1 > firstBar ? bar1 - 1 : firstBar; while (MathAbs(adIndValue[bar1] - adIndValue[iBar2]) < sigma && iBar2 > firstBar) iBar2--; indCompLong.Value[bar] = (adIndValue[iBar2] > adIndValue[bar1] && adIndValue[bar1] < adIndValue[bar0] && bar1 == bar0 - 1) ? 1 : 0; indCompShort.Value[bar] = (adIndValue[iBar2] < adIndValue[bar1] && adIndValue[bar1] > adIndValue[bar0] && bar1 == bar0 - 1) ? 1 : 0; } break; case IndicatorLogic_The_indicator_changes_its_direction_downward: for (int bar = firstBar; bar < Data.Bars; bar++) { int bar0 = bar - previous; int bar1 = bar0 - 1; while (MathAbs(adIndValue[bar0] - adIndValue[bar1]) < sigma && bar1 > firstBar) bar1--; int iBar2 = bar1 - 1 > firstBar ? bar1 - 1 : firstBar; while (MathAbs(adIndValue[bar1] - adIndValue[iBar2]) < sigma && iBar2 > firstBar) iBar2--; indCompLong.Value[bar] = (adIndValue[iBar2] < adIndValue[bar1] && adIndValue[bar1] > adIndValue[bar0] && bar1 == bar0 - 1) ? 1 : 0; indCompShort.Value[bar] = (adIndValue[iBar2] > adIndValue[bar1] && adIndValue[bar1] < adIndValue[bar0] && bar1 == bar0 - 1) ? 1 : 0; } break; default: return; } } void Indicator::NoDirectionOscillatorLogic(int firstBar, int previous, const double &adIndValue[], double dLevel, IndicatorComp &indComp, IndicatorLogic indLogic) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) indComp.Value[bar] = 0; switch (indLogic) { case IndicatorLogic_The_indicator_rises: for (int bar = firstBar; bar < Data.Bars; bar++) { int currentBar = bar - previous; int baseBar = currentBar - 1; bool isHigher = adIndValue[currentBar] > adIndValue[baseBar]; bool isNoChange = true; while (MathAbs(adIndValue[currentBar] - adIndValue[baseBar]) < sigma && isNoChange && baseBar > firstBar) { isNoChange = (isHigher == (adIndValue[baseBar + 1] > adIndValue[baseBar])); baseBar--; } indComp.Value[bar] = adIndValue[baseBar] < adIndValue[currentBar] - sigma ? 1 : 0; } break; case IndicatorLogic_The_indicator_falls: for (int bar = firstBar; bar < Data.Bars; bar++) { int currentBar = bar - previous; int baseBar = currentBar - 1; bool isHigher = adIndValue[currentBar] > adIndValue[baseBar]; bool isNoChange = true; while (MathAbs(adIndValue[currentBar] - adIndValue[baseBar]) < sigma && isNoChange && baseBar > firstBar) { isNoChange = (isHigher == (adIndValue[baseBar + 1] > adIndValue[baseBar])); baseBar--; } indComp.Value[bar] = adIndValue[baseBar] > adIndValue[currentBar] + sigma ? 1 : 0; } break; case IndicatorLogic_The_indicator_is_higher_than_the_level_line: for (int bar = firstBar; bar < Data.Bars; bar++) indComp.Value[bar] = adIndValue[bar - previous] > dLevel + sigma ? 1 : 0; break; case IndicatorLogic_The_indicator_is_lower_than_the_level_line: for (int bar = firstBar; bar < Data.Bars; bar++) indComp.Value[bar] = adIndValue[bar - previous] < dLevel - sigma ? 1 : 0; break; case IndicatorLogic_The_indicator_crosses_the_level_line_upward: for (int bar = firstBar; bar < Data.Bars; bar++) { int baseBar = bar - previous - 1; while (MathAbs(adIndValue[baseBar] - dLevel) < sigma && baseBar > firstBar) baseBar--; indComp.Value[bar] = (adIndValue[baseBar] < dLevel - sigma && adIndValue[bar - previous] > dLevel + sigma) ? 1 : 0; } break; case IndicatorLogic_The_indicator_crosses_the_level_line_downward: for (int bar = firstBar; bar < Data.Bars; bar++) { int baseBar = bar - previous - 1; while (MathAbs(adIndValue[baseBar] - dLevel) < sigma && baseBar > firstBar) baseBar--; indComp.Value[bar] = (adIndValue[baseBar] > dLevel + sigma && adIndValue[bar - previous] < dLevel - sigma) ? 1 : 0; } break; case IndicatorLogic_The_indicator_changes_its_direction_upward: for (int bar = firstBar; bar < Data.Bars; bar++) { int bar0 = bar - previous; int bar1 = bar0 - 1; while (MathAbs(adIndValue[bar0] - adIndValue[bar1]) < sigma && bar1 > firstBar) bar1--; int bar2 = bar1 - 1 > firstBar ? bar1 - 1 : firstBar; while (MathAbs(adIndValue[bar1] - adIndValue[bar2]) < sigma && bar2 > firstBar) bar2--; indComp.Value[bar] = (adIndValue[bar2] > adIndValue[bar1] && adIndValue[bar1] < adIndValue[bar0] && bar1 == bar0 - 1) ? 1 : 0; } break; case IndicatorLogic_The_indicator_changes_its_direction_downward: for (int bar = firstBar; bar < Data.Bars; bar++) { int bar0 = bar - previous; int bar1 = bar0 - 1; while (MathAbs(adIndValue[bar0] - adIndValue[bar1]) < sigma && bar1 > firstBar) bar1--; int bar2 = bar1 - 1 > firstBar ? bar1 - 1 : firstBar; while (MathAbs(adIndValue[bar1] - adIndValue[bar2]) < sigma && bar2 > firstBar) bar2--; indComp.Value[bar] = (adIndValue[bar2] < adIndValue[bar1] && adIndValue[bar1] > adIndValue[bar0] && bar1 == bar0 - 1) ? 1 : 0; } break; default: return; } } void Indicator::BandIndicatorLogic(int firstBar, int previous, const double &adUpperBand[], const double &adLowerBand[], IndicatorComp &indCompLong, IndicatorComp &indCompShort, BandIndLogic indLogic) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } switch (indLogic) { case BandIndLogic_The_bar_opens_below_the_Upper_Band: for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = Data.Open[bar] < adUpperBand[bar - previous] - sigma ? 1 : 0; indCompShort.Value[bar] = Data.Open[bar] > adLowerBand[bar - previous] + sigma ? 1 : 0; } break; case BandIndLogic_The_bar_opens_above_the_Upper_Band: for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = Data.Open[bar] > adUpperBand[bar - previous] + sigma ? 1 : 0; indCompShort.Value[bar] = Data.Open[bar] < adLowerBand[bar - previous] - sigma ? 1 : 0; } break; case BandIndLogic_The_bar_opens_below_the_Lower_Band: for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = Data.Open[bar] < adLowerBand[bar - previous] - sigma ? 1 : 0; indCompShort.Value[bar] = Data.Open[bar] > adUpperBand[bar - previous] + sigma ? 1 : 0; } break; case BandIndLogic_The_bar_opens_above_the_Lower_Band: for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = Data.Open[bar] > adLowerBand[bar - previous] + sigma ? 1 : 0; indCompShort.Value[bar] = Data.Open[bar] < adUpperBand[bar - previous] - sigma ? 1 : 0; } break; case BandIndLogic_The_bar_opens_below_Upper_Band_after_above: for (int bar = firstBar; bar < Data.Bars; bar++) { int baseBar = bar - 1; while (MathAbs(Data.Open[baseBar] - adUpperBand[baseBar - previous]) < sigma && baseBar > firstBar) baseBar--; indCompLong.Value[bar] = Data.Open[bar] < adUpperBand[bar - previous] - sigma && Data.Open[baseBar] > adUpperBand[baseBar - previous] + sigma ? 1 : 0; baseBar = bar - 1; while (MathAbs(Data.Open[baseBar] - adLowerBand[baseBar - previous]) < sigma && baseBar > firstBar) baseBar--; indCompShort.Value[bar] = Data.Open[bar] > adLowerBand[bar - previous] + sigma && Data.Open[baseBar] < adLowerBand[baseBar - previous] - sigma ? 1 : 0; } break; case BandIndLogic_The_bar_opens_above_Upper_Band_after_below: for (int bar = firstBar; bar < Data.Bars; bar++) { int baseBar = bar - 1; while (MathAbs(Data.Open[baseBar] - adUpperBand[baseBar - previous]) < sigma && baseBar > firstBar) baseBar--; indCompLong.Value[bar] = Data.Open[bar] > adUpperBand[bar - previous] + sigma && Data.Open[baseBar] < adUpperBand[baseBar - previous] - sigma ? 1 : 0; baseBar = bar - 1; while (MathAbs(Data.Open[baseBar] - adLowerBand[baseBar - previous]) < sigma && baseBar > firstBar) baseBar--; indCompShort.Value[bar] = Data.Open[bar] < adLowerBand[bar - previous] - sigma && Data.Open[baseBar] > adLowerBand[baseBar - previous] + sigma ? 1 : 0; } break; case BandIndLogic_The_bar_opens_below_Lower_Band_after_above: for (int bar = firstBar; bar < Data.Bars; bar++) { int baseBar = bar - 1; while (MathAbs(Data.Open[baseBar] - adLowerBand[baseBar - previous]) < sigma && baseBar > firstBar) baseBar--; indCompLong.Value[bar] = Data.Open[bar] < adLowerBand[bar - previous] - sigma && Data.Open[baseBar] > adLowerBand[baseBar - previous] + sigma ? 1 : 0; baseBar = bar - 1; while (MathAbs(Data.Open[baseBar] - adUpperBand[baseBar - previous]) < sigma && baseBar > firstBar) baseBar--; indCompShort.Value[bar] = Data.Open[bar] > adUpperBand[bar - previous] + sigma && Data.Open[baseBar] < adUpperBand[baseBar - previous] - sigma ? 1 : 0; } break; case BandIndLogic_The_bar_opens_above_Lower_Band_after_below: for (int bar = firstBar; bar < Data.Bars; bar++) { int baseBar = bar - 1; while (MathAbs(Data.Open[baseBar] - adLowerBand[baseBar - previous]) < sigma && baseBar > firstBar) baseBar--; indCompLong.Value[bar] = Data.Open[bar] > adLowerBand[bar - previous] + sigma && Data.Open[baseBar] < adLowerBand[baseBar - previous] - sigma ? 1 : 0; baseBar = bar - 1; while (MathAbs(Data.Open[baseBar] - adUpperBand[baseBar - previous]) < sigma && baseBar > firstBar) baseBar--; indCompShort.Value[bar] = Data.Open[bar] < adUpperBand[bar - previous] - sigma && Data.Open[baseBar] > adUpperBand[baseBar - previous] + sigma ? 1 : 0; } break; case BandIndLogic_The_bar_closes_below_the_Upper_Band: for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = Data.Close[bar] < adUpperBand[bar - previous] - sigma ? 1 : 0; indCompShort.Value[bar] = Data.Close[bar] > adLowerBand[bar - previous] + sigma ? 1 : 0; } break; case BandIndLogic_The_bar_closes_above_the_Upper_Band: for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = Data.Close[bar] > adUpperBand[bar - previous] + sigma ? 1 : 0; indCompShort.Value[bar] = Data.Close[bar] < adLowerBand[bar - previous] - sigma ? 1 : 0; } break; case BandIndLogic_The_bar_closes_below_the_Lower_Band: for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = Data.Close[bar] < adLowerBand[bar - previous] - sigma ? 1 : 0; indCompShort.Value[bar] = Data.Close[bar] > adUpperBand[bar - previous] + sigma ? 1 : 0; } break; case BandIndLogic_The_bar_closes_above_the_Lower_Band: for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = Data.Close[bar] > adLowerBand[bar - previous] + sigma ? 1 : 0; indCompShort.Value[bar] = Data.Close[bar] < adUpperBand[bar - previous] - sigma ? 1 : 0; } break; default: return; } } void Indicator::IndicatorRisesLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } for (int bar = firstBar; bar < Data.Bars; bar++) { int currentBar = bar - previous; int baseBar = currentBar - 1; bool isNoChange = true; bool isHigher = adIndValue[currentBar] > adIndValue[baseBar]; while (MathAbs(adIndValue[currentBar] - adIndValue[baseBar]) < sigma && isNoChange && baseBar > firstBar) { isNoChange = (isHigher == (adIndValue[baseBar + 1] > adIndValue[baseBar])); baseBar--; } indCompLong.Value[bar] = adIndValue[currentBar] > adIndValue[baseBar] + sigma ? 1 : 0; indCompShort.Value[bar] = adIndValue[currentBar] < adIndValue[baseBar] - sigma ? 1 : 0; } } void Indicator::IndicatorFallsLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } for (int bar = firstBar; bar < Data.Bars; bar++) { int currentBar = bar - previous; int baseBar = currentBar - 1; bool isNoChange = true; bool isLower = adIndValue[currentBar] < adIndValue[baseBar]; while (MathAbs(adIndValue[currentBar] - adIndValue[baseBar]) < sigma && isNoChange && baseBar > firstBar) { isNoChange = (isLower == (adIndValue[baseBar + 1] < adIndValue[baseBar])); baseBar--; } indCompLong.Value[bar] = adIndValue[currentBar] < adIndValue[baseBar] - sigma ? 1 : 0; indCompShort.Value[bar] = adIndValue[currentBar] > adIndValue[baseBar] + sigma ? 1 : 0; } } void Indicator::IndicatorIsHigherThanAnotherIndicatorLogic(int firstBar, int previous, const double &adIndValue[], double &adAnotherIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } for (int bar = firstBar; bar < Data.Bars; bar++) { int currentBar = bar - previous; indCompLong.Value[bar] = adIndValue[currentBar] > adAnotherIndValue[currentBar] + sigma ? 1 : 0; indCompShort.Value[bar] = adIndValue[currentBar] < adAnotherIndValue[currentBar] - sigma ? 1 : 0; } } void Indicator::IndicatorIsLowerThanAnotherIndicatorLogic(int firstBar, int previous, const double &adIndValue[], double &adAnotherIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } for (int bar = firstBar; bar < Data.Bars; bar++) { int currentBar = bar - previous; indCompLong.Value[bar] = adIndValue[currentBar] < adAnotherIndValue[currentBar] - sigma ? 1 : 0; indCompShort.Value[bar] = adIndValue[currentBar] > adAnotherIndValue[currentBar] + sigma ? 1 : 0; } } void Indicator::IndicatorChangesItsDirectionUpward(int firstBar, int previous, double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); for (int bar = firstBar; bar < Data.Bars; bar++) { int bar0 = bar - previous; int bar1 = bar0 - 1; while (MathAbs(adIndValue[bar0] - adIndValue[bar1]) < sigma && bar1 > firstBar) bar1--; int bar2 = bar1 - 1 > firstBar ? bar1 - 1 : firstBar; while (MathAbs(adIndValue[bar1] - adIndValue[bar2]) < sigma && bar2 > firstBar) bar2--; indCompLong.Value[bar] = (adIndValue[bar2] > adIndValue[bar1] && adIndValue[bar1] < adIndValue[bar0] && bar1 == bar0 - 1) ? 1 : 0; indCompShort.Value[bar] = (adIndValue[bar2] < adIndValue[bar1] && adIndValue[bar1] > adIndValue[bar0] && bar1 == bar0 - 1) ? 1 : 0; } } void Indicator::IndicatorChangesItsDirectionDownward(int firstBar, int previous, double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); for (int bar = firstBar; bar < Data.Bars; bar++) { int bar0 = bar - previous; int bar1 = bar0 - 1; while (MathAbs(adIndValue[bar0] - adIndValue[bar1]) < sigma && bar1 > firstBar) bar1--; int bar2 = bar1 - 1 > firstBar ? bar1 - 1 : firstBar; while (MathAbs(adIndValue[bar1] - adIndValue[bar2]) < sigma && bar2 > firstBar) bar2--; indCompLong.Value[bar] = (adIndValue[bar2] < adIndValue[bar1] && adIndValue[bar1] > adIndValue[bar0] && bar1 == bar0 - 1) ? 1 : 0; indCompShort.Value[bar] = (adIndValue[bar2] > adIndValue[bar1] && adIndValue[bar1] < adIndValue[bar0] && bar1 == bar0 - 1) ? 1 : 0; } } void Indicator::IndicatorCrossesAnotherIndicatorUpwardLogic(int firstBar, int previous, const double &adIndValue[], double &adAnotherIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } for (int bar = firstBar; bar < Data.Bars; bar++) { int currentBar = bar - previous; int baseBar = currentBar - 1; while (MathAbs(adIndValue[baseBar] - adAnotherIndValue[baseBar]) < sigma && baseBar > firstBar) baseBar--; indCompLong.Value[bar] = adIndValue[currentBar] > adAnotherIndValue[currentBar] + sigma && adIndValue[baseBar] < adAnotherIndValue[baseBar] - sigma ? 1 : 0; indCompShort.Value[bar] = adIndValue[currentBar] < adAnotherIndValue[currentBar] - sigma && adIndValue[baseBar] > adAnotherIndValue[baseBar] + sigma ? 1 : 0; } } void Indicator::IndicatorCrossesAnotherIndicatorDownwardLogic(int firstBar, int previous, const double &adIndValue[], double &adAnotherIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } for (int bar = firstBar; bar < Data.Bars; bar++) { int currentBar = bar - previous; int baseBar = currentBar - 1; while (MathAbs(adIndValue[baseBar] - adAnotherIndValue[baseBar]) < sigma && baseBar > firstBar) { baseBar--; } indCompLong.Value[bar] = adIndValue[currentBar] < adAnotherIndValue[currentBar] - sigma && adIndValue[baseBar] > adAnotherIndValue[baseBar] + sigma ? 1 : 0; indCompShort.Value[bar] = adIndValue[currentBar] > adAnotherIndValue[currentBar] + sigma && adIndValue[baseBar] < adAnotherIndValue[baseBar] - sigma ? 1 : 0; } } void Indicator::BarOpensAboveIndicatorLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = Data.Open[bar] > adIndValue[bar - previous] + sigma ? 1 : 0; indCompShort.Value[bar] = Data.Open[bar] < adIndValue[bar - previous] - sigma ? 1 : 0; } } void Indicator::BarOpensBelowIndicatorLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = Data.Open[bar] < adIndValue[bar - previous] - sigma ? 1 : 0; indCompShort.Value[bar] = Data.Open[bar] > adIndValue[bar - previous] + sigma ? 1 : 0; } } void Indicator::BarOpensAboveIndicatorAfterOpeningBelowLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } for (int bar = firstBar; bar < Data.Bars; bar++) { int baseBar = bar - 1; while (MathAbs(Data.Open[baseBar] - adIndValue[baseBar - previous]) < sigma && baseBar > firstBar) baseBar--; indCompLong.Value[bar] = Data.Open[bar] > adIndValue[bar - previous] + sigma && Data.Open[baseBar] < adIndValue[baseBar - previous] - sigma ? 1 : 0; indCompShort.Value[bar] = Data.Open[bar] < adIndValue[bar - previous] - sigma && Data.Open[baseBar] > adIndValue[baseBar - previous] + sigma ? 1 : 0; } } void Indicator::BarOpensBelowIndicatorAfterOpeningAboveLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } for (int bar = firstBar; bar < Data.Bars; bar++) { int baseBar = bar - 1; while (MathAbs(Data.Open[baseBar] - adIndValue[baseBar - previous]) < sigma && baseBar > firstBar) baseBar--; indCompLong.Value[bar] = Data.Open[bar] < adIndValue[bar - previous] - sigma && Data.Open[baseBar] > adIndValue[baseBar - previous] + sigma ? 1 : 0; indCompShort.Value[bar] = Data.Open[bar] > adIndValue[bar - previous] + sigma && Data.Open[baseBar] < adIndValue[baseBar - previous] - sigma ? 1 : 0; } } void Indicator::BarClosesAboveIndicatorLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = Data.Close[bar] > adIndValue[bar - previous] + sigma ? 1 : 0; indCompShort.Value[bar] = Data.Close[bar] < adIndValue[bar - previous] - sigma ? 1 : 0; } } void Indicator::BarClosesBelowIndicatorLogic(int firstBar, int previous, const double &adIndValue[], IndicatorComp &indCompLong, IndicatorComp &indCompShort) { double sigma = Sigma(); firstBar = MathMax(firstBar, 2); for (int bar = 0; bar < firstBar; bar++) { indCompLong.Value[bar] = 0; indCompShort.Value[bar] = 0; } for (int bar = firstBar; bar < Data.Bars; bar++) { indCompLong.Value[bar] = Data.Close[bar] < adIndValue[bar - previous] - sigma ? 1 : 0; indCompShort.Value[bar] = Data.Close[bar] > adIndValue[bar - previous] + sigma ? 1 : 0; } } class BarOpening : public Indicator { public: BarOpening(SlotTypes slotType) { SlotType=slotType; IndicatorName="Bar Opening"; WarningMessage = ""; IsAllowLTF = true; ExecTime = ExecutionTime_AtBarOpening; IsSeparateChart = false; IsDiscreteValues = false; IsDefaultGroupAll = false; } virtual void Calculate(DataSet &dataSet); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void BarOpening::Calculate(DataSet &dataSet) { Data=GetPointer(dataSet); // Saving the components ArrayResize(Component[0].Value,Data.Bars); Component[0].CompName = "Opening price of the bar"; Component[0].DataType = IndComponentType_OpenPrice; Component[0].FirstBar = 2; ArrayCopy(Component[0].Value,Data.Open); } //+------------------------------------------------------------------+ class TrailingStop : public Indicator { public: TrailingStop(SlotTypes slotType) { SlotType=slotType; IndicatorName="Trailing Stop"; IsAllowLTF = true; ExecTime = ExecutionTime_DuringTheBar; IsSeparateChart = false; IsDiscreteValues = false; IsDefaultGroupAll = false; } virtual void Calculate(DataSet &dataSet); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void TrailingStop::Calculate(DataSet &dataSet) { Data=GetPointer(dataSet); // Saving the components ArrayResize(Component[0].Value,Data.Bars); ArrayInitialize(Component[0].Value,0); Component[0].CompName = "Trailing Stop for the transferred position"; Component[0].DataType = IndComponentType_Other; Component[0].ShowInDynInfo=false; Component[0].FirstBar=1; } //+------------------------------------------------------------------+ class CandleColor : public Indicator { public: CandleColor(SlotTypes slotType) { SlotType=slotType; IndicatorName="Candle Color"; WarningMessage = ""; IsAllowLTF = true; ExecTime = ExecutionTime_DuringTheBar; IsSeparateChart = false; IsDiscreteValues = false; IsDeafultGroupAll = false; } virtual void Calculate(DataSet &dataSet); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CandleColor::Calculate(DataSet &dataSet) { Data=GetPointer(dataSet); // Reading the parameters int minHeight = (int) NumParam[0].Value; int consecutive = (int) NumParam[1].Value; int previous = CheckParam[0].Checked ? 1 : 0; int firstBar = 2 + consecutive + previous; int whiteCandles[];ArrayResize(whiteCandles,Data.Bars);ArrayInitialize(whiteCandles,0); int blackCandles[];ArrayResize(blackCandles,Data.Bars);ArrayInitialize(blackCandles,0); double pipVal = dataSet.Pip*minHeight; for (int b = 1 + previous; b < dataSet.Bars; b++) { if (Data.Close[b - previous] - Data.Open[b - previous] >= pipVal) whiteCandles[b] = whiteCandles[b - 1] + 1; if (Data.Open[b - previous] - Data.Close[b - previous] >= pipVal) blackCandles[b] = blackCandles[b - 1] + 1; } // Saving the components ArrayResize(Component[0].Value,Data.Bars); ArrayInitialize(Component[0].Value,0); Component[0].FirstBar=firstBar; ArrayResize(Component[1].Value,Data.Bars); ArrayInitialize(Component[1].Value,0); Component[1].FirstBar=firstBar; // Sets the Component's type. if(SlotType==SlotTypes_OpenFilter) { Component[0].DataType = IndComponentType_AllowOpenLong; Component[0].CompName = "Is long entry allowed"; Component[1].DataType = IndComponentType_AllowOpenShort; Component[1].CompName = "Is short entry allowed"; } else if(SlotType==SlotTypes_CloseFilter) { Component[0].DataType = IndComponentType_ForceCloseLong; Component[0].CompName = "Close out long position"; Component[1].DataType = IndComponentType_ForceCloseShort; Component[1].CompName = "Close out short position"; } if(ListParam[0].Text == "Bullish Candle formed") for (int b = firstBar; b < Data.Bars; b++) { Component[0].Value[b] = whiteCandles[b] >= consecutive ? 1 : 0; Component[1].Value[b] = blackCandles[b] >= consecutive ? 1 : 0; } else if(ListParam[0].Text == "Bearish Candle formed") for (int b = firstBar; b < Data.Bars; b++) { Component[1].Value[b] = whiteCandles[b] >= consecutive ? 1 : 0; Component[0].Value[b] = blackCandles[b] >= consecutive ? 1 : 0; } } //+------------------------------------------------------------------+ class RisingVolume : public Indicator { public: RisingVolume(SlotTypes slotType) { SlotType = slotType; IndicatorName = "Rising Volume"; WarningMessage = ""; IsAllowLTF = true; ExecTime = ExecutionTime_DuringTheBar; IsSeparateChart = false; IsDiscreteValues = false; IsDefaultGroupAll = false; } virtual void Calculate(DataSet &dataSet); }; void RisingVolume::Calculate(DataSet &dataSet) { Data = GetPointer(dataSet); // Reading the parameters int period = (int) NumParam[0].Value; double threshold = NumParam[1].Value; int previous = CheckParam[0].Checked ? 1 : 0; // Calculation int firstBar = period + previous + 2; // Average volume double average[]; ArrayResize(average, Data.Bars); ArrayInitialize(average, 0); double sum = 0; for (int bar = 0; bar < period; bar++) sum += (double) Data.Volume[bar]; average[period - 1] = sum/period; for (int bar = period; bar < Data.Bars; bar++) average[bar] = average[bar - 1] + (Data.Volume[bar] - Data.Volume[bar - period])/(float)period; // Signal double signal[]; ArrayResize(signal, Data.Bars); ArrayInitialize(signal, 0); for (int bar = firstBar; bar < Data.Bars; bar++) signal[bar] = Data.Volume[bar - previous] >= threshold*average[bar - previous] ? 1 : 0; // Saving the components ArrayResize(Component[0].Value, Data.Bars); Component[0].FirstBar = firstBar; ArrayCopy(Component[0].Value, signal); ArrayResize(Component[1].Value, Data.Bars); Component[1].FirstBar = firstBar; ArrayCopy(Component[1].Value, signal); // Sets the Component's type if (SlotType == SlotTypes_OpenFilter) { Component[0].DataType = IndComponentType_AllowOpenLong; Component[0].CompName = "Is long entry allowed"; Component[1].DataType = IndComponentType_AllowOpenShort; Component[1].CompName = "Is short entry allowed"; } else if (SlotType == SlotTypes_CloseFilter) { Component[0].DataType = IndComponentType_ForceCloseLong; Component[0].CompName = "Close out long position"; Component[1].DataType = IndComponentType_ForceCloseShort; Component[1].CompName = "Close out short position"; } } //## Import Start class IndicatorManager { public: Indicator *CreateIndicator(string indicatorName,SlotTypes slotType); }; Indicator *IndicatorManager::CreateIndicator(string indicatorName,SlotTypes slotType) { if(indicatorName == "Bar Opening") return new BarOpening(slotType); if(indicatorName == "Trailing Stop") return new TrailingStop(slotType); if (indicatorName == "Candle Color") return new CandleColor(slotType); if (indicatorName == "Rising Volume") return new RisingVolume(slotType); return NULL; } //## Import Start class IndicatorSlot { public: // Constructors IndicatorSlot(); ~IndicatorSlot(); // Properties int SlotNumber; SlotTypes SlotType; string IndicatorName; string LogicalGroup; int SignalShift; int SignalRepeat; string IndicatorSymbol; DataPeriod IndicatorPeriod; Indicator *IndicatorPointer; // Methods bool GetUsePreviousBarValue(void); string LogicalGroupToString(void); string AdvancedParamsToString(void); string GetIndicatorSymbol(string baseSymbol); DataPeriod GetIndicatorPeriod(DataPeriod basePeriod); }; IndicatorSlot::IndicatorSlot(void) { } IndicatorSlot::~IndicatorSlot(void) { if (CheckPointer(IndicatorPointer) == POINTER_DYNAMIC) delete IndicatorPointer; } bool IndicatorSlot::GetUsePreviousBarValue(void) { for (int i = 0; i < ArraySize(IndicatorPointer.CheckParam); i++) if (IndicatorPointer.CheckParam[i].Caption == "Use previous bar value") return (IndicatorPointer.CheckParam[i].Checked); return (false); } string IndicatorSlot::LogicalGroupToString(void) { return ("Logical group: " + LogicalGroup); } string IndicatorSlot::AdvancedParamsToString(void) { string text = "Signal shift: " + IntegerToString(SignalShift) + "\n"; if (SlotType == SlotTypes_OpenFilter || SlotType == SlotTypes_CloseFilter) text += "Signal repeat: " + IntegerToString(SignalRepeat) + "\n"; string symbol = (IndicatorSymbol == "") ? "Default" : IndicatorSymbol; string period = (IndicatorPeriod == DataPeriod_M1) ? "Default" : DataPeriodToString(IndicatorPeriod); text += "Symbol: " + symbol + "\n"; text += "Period: " + period + "\n"; return (text); } string IndicatorSlot::GetIndicatorSymbol(string baseSymbol) { return (IndicatorSymbol == "" ? baseSymbol : IndicatorSymbol); } DataPeriod IndicatorSlot::GetIndicatorPeriod(DataPeriod basePeriod) { return (IndicatorPeriod < basePeriod ? basePeriod : IndicatorPeriod); } //## Import Start class Strategy { private: // Fields string strategySymbol; DataPeriod strategyPeriod; bool isInTester; int openSlotsCount; int closeSlotsCount; // Methods bool IsLongerTimeFrame(int slotNumber); string GetSlotChart(int slotNumber); public: // Constructors Strategy(int openSlots, int closeSlots); ~Strategy(void); // Properties string StrategyName; string Description; double AddingLots; double ReducingLots; double EntryLots; double MaxOpenLots; bool UseAccountPercentEntry; bool UsePermanentSL; int PermanentSL; bool UsePermanentTP; int PermanentTP; bool UseBreakEven; int BreakEven; bool UseMartingale; double MartingaleMultiplier; int FirstBar; int MinBarsRequired; int RecommendedBars; PermanentProtectionType PermanentTPType; PermanentProtectionType PermanentSLType; OppositeDirSignalAction OppSignalAction; SameDirSignalAction SameSignalAction; IndicatorSlot *Slot[]; // Methods void SetSymbol(string symbol) { strategySymbol = symbol; } void SetPeriod(int period) { strategyPeriod = EnumTimeFramesToPeriod(period); } void SetIsTester(bool isTester) { isInTester = isTester; } string GetSymbol(void) { return (strategySymbol); } DataPeriod GetPeriod(void) { return (strategyPeriod); } bool IsTester(void) { return (isInTester); } int OpenSlots(void) { return (openSlotsCount); }; int CloseSlots(void) { return (closeSlotsCount); }; int Slots(void) { return (openSlotsCount + closeSlotsCount + 2); } int CloseSlotNumber(void) { return (openSlotsCount + 1); } SlotTypes GetSlotType(int slotNumber); void GetRequiredCharts(string &charts[]); bool IsUsingLogicalGroups(void); bool IsLogicalGroupSpecial(int slotNumber); string GetDefaultGroup(int slotNumber); void CalculateStrategy(DataSet *&dataSet[]); string DynamicInfoText(void); void DynamicInfoInitArrays(string ¶ms[], string &values[]); void DynamicInfoSetValues(string &values[]); string ToString(void); }; Strategy::Strategy(int openSlots, int closeSlots) { openSlotsCount = openSlots; closeSlotsCount = closeSlots; ArrayResize(Slot, Slots()); for (int i = 0; i < Slots(); i++) { Slot[i] = new IndicatorSlot(); Slot[i].SlotNumber = i; Slot[i].SlotType = GetSlotType(i); } } Strategy::~Strategy(void) { for (int slot = 0; slot < ArraySize(Slot); slot++) delete Slot[slot]; } SlotTypes Strategy::GetSlotType(int slotNumber) { if (slotNumber == 0) return (SlotTypes_Open); else if (slotNumber < CloseSlotNumber()) return (SlotTypes_OpenFilter); else if (slotNumber == CloseSlotNumber()) return (SlotTypes_Close); else return (SlotTypes_CloseFilter); } bool Strategy::IsLongerTimeFrame(int slotNumber) { return !(Slot[slotNumber].IndicatorSymbol == "" && Slot[slotNumber].IndicatorPeriod == DataPeriod_M1); } string Strategy::GetSlotChart(int slotNumber) { string symbol = Slot[slotNumber].GetIndicatorSymbol(GetSymbol()); DataPeriod period = Slot[slotNumber].GetIndicatorPeriod(GetPeriod()); return (symbol + "," + DataPeriodToString(period)); } void Strategy::GetRequiredCharts(string &charts[]) { ArrayResize(charts, 1); charts[0] = GetSymbol() + "," + DataPeriodToString(GetPeriod()); for (int i = 0; i < Slots(); i++) { if (!Slot[i].IndicatorPointer.IsAllowLTF) continue; if (!IsLongerTimeFrame(i)) continue; string chart = GetSlotChart(i); if (!ArrayContainsString(charts, chart)) ArrayAppendString(charts, chart); } } void Strategy::CalculateStrategy(DataSet *&dataSet[]) { for (int i = 0; i < Slots(); i++) { string chart = GetSlotChart(i); for (int j = 0; j < ArraySize(dataSet); j++) { if (dataSet[j].Chart != chart) continue; Slot[i].IndicatorPointer.Calculate(dataSet[j]); if (IsLongerTimeFrame(i)) Slot[i].IndicatorPointer.NormalizeComponents(dataSet[0]); if (Slot[i].SignalShift > 0) Slot[i].IndicatorPointer.ShiftSignal(Slot[i].SignalShift); if (Slot[i].SignalRepeat > 0) Slot[i].IndicatorPointer.RepeatSignal(Slot[i].SignalRepeat); } } } bool Strategy::IsUsingLogicalGroups() { bool isUsingGroups = false; for (int slot = 0; slot < ArraySize(Slot); slot++) { SlotTypes slotType = Slot[slot].SlotType; if (slotType == SlotTypes_OpenFilter) { string defaultGroup = GetDefaultGroup(slot); string logicalGroup = Slot[slot].LogicalGroup; if (defaultGroup != logicalGroup && logicalGroup != "All") { isUsingGroups = true; break; } } else if (slotType == SlotTypes_CloseFilter) { string defaultGroup = GetDefaultGroup(slot); string logicalGroup = Slot[slot].LogicalGroup; if (defaultGroup != logicalGroup) { isUsingGroups = true; break; } } } return (isUsingGroups); } bool Strategy::IsLogicalGroupSpecial(int slotNumber) { SlotTypes slotType = Slot[slotNumber].SlotType; string group = Slot[slotNumber].LogicalGroup; if (slotType == SlotTypes_Open || slotType == SlotTypes_Close) return (false); if (slotType == SlotTypes_OpenFilter && group != GetDefaultGroup(slotNumber) && group != "[All]") return (true); if (slotType == SlotTypes_CloseFilter && group != GetDefaultGroup(slotNumber)) return (true); if (slotType == SlotTypes_CloseFilter) { int count = 0; for (int i = OpenSlots() + 2; i < Slots(); i++) if (Slot[i].LogicalGroup == group) count++; if (count > 1) return (true); } return (false); } string Strategy::GetDefaultGroup(int slotNumber) { string group = ""; SlotTypes slotType = GetSlotType(slotNumber); if (slotType == SlotTypes_OpenFilter) { bool isDefault = Slot[slotNumber].IndicatorPointer.IsDeafultGroupAll || Slot[slotNumber].IndicatorPointer.IsDefaultGroupAll; group = isDefault ? "All" : "A"; } else if (slotType == SlotTypes_CloseFilter) { int index = slotNumber - CloseSlotNumber() - 1; group = IntegerToString('a' + index); } return (group); } void Strategy::DynamicInfoInitArrays(string ¶ms[], string &values[]) { ArrayResize(params, 200); ArrayResize(values, 200); for (int i = 0; i < 200; i++) { params[i] = ""; values[i] = ""; } int index = -2; for (int slot = 0; slot < Slots(); slot++) { index++; index++; params[index] = Slot[slot].IndicatorName; for (int i = 0; i < Slot[slot].IndicatorPointer.Components(); i++) { IndComponentType type = Slot[slot].IndicatorPointer.Component[i].DataType; if (type == IndComponentType_NotDefined) continue; if (Slot[slot].IndicatorPointer.Component[i].ShowInDynInfo) { index++; params[index] = Slot[slot].IndicatorPointer.Component[i].CompName; } } } ArrayResize(params, index + 1); ArrayResize(values, index + 1); } void Strategy::DynamicInfoSetValues(string &values[]) { int index = -1; for (int slot = 0; slot < Slots(); slot++) { index++; index++; for (int i = 0; i < Slot[slot].IndicatorPointer.Components(); i++) { IndicatorComp *component = Slot[slot].IndicatorPointer.Component[i]; IndComponentType type = component.DataType; if (type == IndComponentType_NotDefined) { component = NULL; continue; } int bars = ArraySize(component.Value); if (bars < 3) { component = NULL; continue; } string name = component.CompName; double value0 = component.Value[bars - 1]; double value1 = component.Value[bars - 2]; double dl0 = MathAbs(value0); double dl1 = MathAbs(value0); string sFr0 = dl0 < 10 ? "%10.5f" : dl0 < 100 ? "%10.5f" : dl0 < 1000 ? "%10.3f" : dl0 < 10000 ? "%10.3f" : dl0 < 100000 ? "%10.2f" : "%10.1f"; string sFr1 = dl1 < 10 ? "%10.5f" : dl1 < 100 ? "%10.5f" : dl1 < 1000 ? "%10.3f" : dl1 < 10000 ? "%10.3f" : dl1 < 100000 ? "%10.2f" : "%10.1f"; string format = sFr1 + " " + sFr0; if (component.ShowInDynInfo) { if (type == IndComponentType_AllowOpenLong || type == IndComponentType_AllowOpenShort || type == IndComponentType_ForceClose || type == IndComponentType_ForceCloseLong || type == IndComponentType_ForceCloseShort) values[index] = StringFormat("%13s %13s", (value1 < 1 ? "No" : "Yes"), (value0 < 1 ? "No" : "Yes")); else values[index] = StringFormat(format, value1, value0); index++; } component = NULL; } } } string Strategy::DynamicInfoText() { string info; for (int slot = 0; slot < Slots(); slot++) { info += "\n\n" + Slot[slot].IndicatorName; for (int i = 0; i < Slot[slot].IndicatorPointer.Components(); i++) { IndicatorComp *component = Slot[slot].IndicatorPointer.Component[i]; IndComponentType type = component.DataType; if (type == IndComponentType_NotDefined) continue; int bars = ArraySize(component.Value); if (bars < 4) continue; string name = component.CompName; double value0 = component.Value[bars - 1]; double value1 = component.Value[bars - 2]; double value2 = component.Value[bars - 3]; double dl = MathAbs(value0); string sFr = dl < 10 ? "%10.6f" : dl < 100 ? "%10.5f" : dl < 1000 ? "%10.4" : dl < 10000 ? "%10.3f" : dl < 100000 ? "%10.2f" : "%10.1f"; string format = "\n%-40s " + sFr + " " + sFr + " " + sFr; if (component.ShowInDynInfo) { if (type == IndComponentType_AllowOpenLong || type == IndComponentType_AllowOpenShort || type == IndComponentType_ForceClose || type == IndComponentType_ForceCloseLong || type == IndComponentType_ForceCloseShort) { info += StringFormat("\n%-42s %-10s %-10s %-10s", name, (value2 < 1 ? "No" : "Yes"), (value1 < 1 ? "No" : "Yes"), (value0 < 1 ? "No" : "Yes")); } else { info += StringFormat(format, name, value2, value1, value0); } } component = NULL; } } return (info); } string Strategy::ToString() { string stopLoss = UsePermanentSL ? IntegerToString(PermanentSL) : "None"; string takeProfit = UsePermanentTP ? IntegerToString(PermanentTP) : "None"; string breakEven = UseBreakEven ? IntegerToString(BreakEven) : "None"; string martingale = UseMartingale ? DoubleToString(MartingaleMultiplier, 2) : "None"; string text = "Name: " + StrategyName + "\n" + "Symbol: " + GetSymbol() + "\n" + "Period: " + DataPeriodToString(GetPeriod()) + "\n\n" + "Trade unit: " + (UseAccountPercentEntry ? "Percent" : "Lot") + "\n" + "Entry amount: " + DoubleToString(EntryLots, 2) + "\n" + "Max open lots: " + DoubleToString(MaxOpenLots, 2) + "\n\n" + "Same signal: " + SameDirSignalActionToString(SameSignalAction) + "\n" + "Adding amount: " + DoubleToString(AddingLots, 2) + "\n" + "Opposite signal: " + OppositeDirSignalActionToString(OppSignalAction) + "\n" + "Reducing amount: " + DoubleToString(ReducingLots, 2) + "\n\n" + "Stop Loss: " + stopLoss + "\n" + "Take Profit: " + takeProfit + "\n" + "Break Even: " + breakEven + "\n\n" + "Martingale: " + martingale + "\n\n" + "Description: " + Description + "\n\n"; for (int slot = 0; slot < ArraySize(Slot); slot++) { text += SlotTypeToString(Slot[slot].SlotType) + "\n" + Slot[slot].IndicatorName + "\n" + Slot[slot].IndicatorPointer.IndicatorParamToString() + "\n"; if (Slot[slot].SlotType == SlotTypes_OpenFilter || Slot[slot].SlotType == SlotTypes_CloseFilter) text += Slot[slot].LogicalGroupToString(); if (Slot[slot].IndicatorPointer.IsAllowLTF) text += Slot[slot].AdvancedParamsToString() + "\n"; } return (text); } //## Import Start class StrategyManager { public: Strategy *GetStrategy(void); }; Strategy *StrategyManager::GetStrategy(void) { IndicatorManager *indicatorManager = new IndicatorManager(); Strategy *strategy = new Strategy(2, 0); strategy.StrategyName = "Rising Candle - TS"; strategy.SameSignalAction = SameDirSignalAction_Nothing; strategy.OppSignalAction = OppositeDirSignalAction_Nothing; strategy.MaxOpenLots = 20; strategy.UseAccountPercentEntry = false; strategy.EntryLots = 1; strategy.AddingLots = 1; strategy.ReducingLots = 1; strategy.MartingaleMultiplier = Martingale_Multiplier; strategy.UseMartingale = Martingale_Multiplier > 0; strategy.Description = "Exported on 12/16/2015 from Forex Strategy Builder Professional, v3.4.2"; strategy.RecommendedBars = 1000; strategy.FirstBar = 83; strategy.MinBarsRequired = 84; strategy.PermanentSL = Stop_Loss; strategy.UsePermanentSL = Stop_Loss > 0; strategy.PermanentSLType = PermanentProtectionType_Relative; strategy.PermanentTP = Take_Profit; strategy.UsePermanentTP = Take_Profit > 0; strategy.PermanentTPType = PermanentProtectionType_Relative; strategy.BreakEven = Break_Even; strategy.UseBreakEven = Break_Even > 0; strategy.Slot[0].IndicatorName = "Bar Opening"; strategy.Slot[0].SlotType = SlotTypes_Open; strategy.Slot[0].SignalShift = 0; strategy.Slot[0].SignalRepeat = 0; strategy.Slot[0].IndicatorPeriod = DataPeriod_M1; strategy.Slot[0].IndicatorSymbol = ""; strategy.Slot[0].LogicalGroup = ""; strategy.Slot[0].IndicatorPointer = indicatorManager.CreateIndicator("Bar Opening", SlotTypes_Open); strategy.Slot[0].IndicatorPointer.ListParam[0].Enabled = true; strategy.Slot[0].IndicatorPointer.ListParam[0].Caption = "Logic"; strategy.Slot[0].IndicatorPointer.ListParam[0].Index = 0; strategy.Slot[0].IndicatorPointer.ListParam[0].Text = "Enter the market at the beginning of the bar"; strategy.Slot[0].IndicatorPointer.ListParam[1].Enabled = true; strategy.Slot[0].IndicatorPointer.ListParam[1].Caption = "Base price"; strategy.Slot[0].IndicatorPointer.ListParam[1].Index = 0; strategy.Slot[0].IndicatorPointer.ListParam[1].Text = "Open"; strategy.Slot[1].IndicatorName = "Candle Color"; strategy.Slot[1].SlotType = SlotTypes_OpenFilter; strategy.Slot[1].SignalShift = 0; strategy.Slot[1].SignalRepeat = 0; strategy.Slot[1].IndicatorPeriod = DataPeriod_M1; strategy.Slot[1].IndicatorSymbol = ""; strategy.Slot[1].LogicalGroup = "A"; strategy.Slot[1].IndicatorPointer = indicatorManager.CreateIndicator("Candle Color", SlotTypes_OpenFilter); strategy.Slot[1].IndicatorPointer.ListParam[0].Enabled = true; strategy.Slot[1].IndicatorPointer.ListParam[0].Caption = "Logic"; strategy.Slot[1].IndicatorPointer.ListParam[0].Index = 0; strategy.Slot[1].IndicatorPointer.ListParam[0].Text = "Bullish Candle formed"; strategy.Slot[1].IndicatorPointer.ListParam[1].Enabled = true; strategy.Slot[1].IndicatorPointer.ListParam[1].Caption = "Base price"; strategy.Slot[1].IndicatorPointer.ListParam[1].Index = 0; strategy.Slot[1].IndicatorPointer.ListParam[1].Text = "Bar range"; strategy.Slot[1].IndicatorPointer.NumParam[0].Enabled = true; strategy.Slot[1].IndicatorPointer.NumParam[0].Caption = "Min body height [pips]"; strategy.Slot[1].IndicatorPointer.NumParam[0].Value = Slot1IndParam0; strategy.Slot[1].IndicatorPointer.NumParam[1].Enabled = true; strategy.Slot[1].IndicatorPointer.NumParam[1].Caption = "Consecutive candles"; strategy.Slot[1].IndicatorPointer.NumParam[1].Value = Slot1IndParam1; strategy.Slot[1].IndicatorPointer.CheckParam[0].Enabled = true; strategy.Slot[1].IndicatorPointer.CheckParam[0].Caption = "Use previous bar value"; strategy.Slot[1].IndicatorPointer.CheckParam[0].Checked = true; strategy.Slot[2].IndicatorName = "Rising Volume"; strategy.Slot[2].SlotType = SlotTypes_OpenFilter; strategy.Slot[2].SignalShift = 0; strategy.Slot[2].SignalRepeat = 0; strategy.Slot[2].IndicatorPeriod = DataPeriod_M1; strategy.Slot[2].IndicatorSymbol = ""; strategy.Slot[2].LogicalGroup = "A"; strategy.Slot[2].IndicatorPointer = indicatorManager.CreateIndicator("Rising Volume", SlotTypes_OpenFilter); strategy.Slot[2].IndicatorPointer.ListParam[0].Enabled = true; strategy.Slot[2].IndicatorPointer.ListParam[0].Caption = "Logic"; strategy.Slot[2].IndicatorPointer.ListParam[0].Index = 0; strategy.Slot[2].IndicatorPointer.ListParam[0].Text = "Volume higher than the average"; strategy.Slot[2].IndicatorPointer.NumParam[0].Enabled = true; strategy.Slot[2].IndicatorPointer.NumParam[0].Caption = "Volume period"; strategy.Slot[2].IndicatorPointer.NumParam[0].Value = Slot2IndParam0; strategy.Slot[2].IndicatorPointer.NumParam[1].Enabled = true; strategy.Slot[2].IndicatorPointer.NumParam[1].Caption = "Volume threshold"; strategy.Slot[2].IndicatorPointer.NumParam[1].Value = Slot2IndParam1; strategy.Slot[2].IndicatorPointer.CheckParam[0].Enabled = true; strategy.Slot[2].IndicatorPointer.CheckParam[0].Caption = "Use previous bar value"; strategy.Slot[2].IndicatorPointer.CheckParam[0].Checked = true; strategy.Slot[3].IndicatorName = "Trailing Stop"; strategy.Slot[3].SlotType = SlotTypes_Close; strategy.Slot[3].SignalShift = 0; strategy.Slot[3].SignalRepeat = 0; strategy.Slot[3].IndicatorPeriod = DataPeriod_M1; strategy.Slot[3].IndicatorSymbol = ""; strategy.Slot[3].LogicalGroup = ""; strategy.Slot[3].IndicatorPointer = indicatorManager.CreateIndicator("Trailing Stop", SlotTypes_Close); strategy.Slot[3].IndicatorPointer.ListParam[0].Enabled = true; strategy.Slot[3].IndicatorPointer.ListParam[0].Caption = "Logic"; strategy.Slot[3].IndicatorPointer.ListParam[0].Index = 0; strategy.Slot[3].IndicatorPointer.ListParam[0].Text = "Exit at the Trailing Stop level"; strategy.Slot[3].IndicatorPointer.ListParam[1].Enabled = true; strategy.Slot[3].IndicatorPointer.ListParam[1].Caption = "Trailing mode"; strategy.Slot[3].IndicatorPointer.ListParam[1].Index = 0; strategy.Slot[3].IndicatorPointer.ListParam[1].Text = "New bar"; strategy.Slot[3].IndicatorPointer.NumParam[0].Enabled = true; strategy.Slot[3].IndicatorPointer.NumParam[0].Caption = "Trailing Stop"; strategy.Slot[3].IndicatorPointer.NumParam[0].Value = Slot3IndParam0; delete indicatorManager; return strategy; } //## Import Start #define OP_FLAT -1 class Position { public: // Constructors Position(void); // Properties int PosType; PosDirection Direction; double Lots; datetime OpenTime; double OpenPrice; double StopLossPrice; double TakeProfitPrice; double Profit; double Commission; string PosComment; // Methods string ToString(); void SetPositionInfo(string &positionInfo[]); }; void Position::Position(void) { } string Position::ToString() { if (PosType == OP_FLAT) return ("Position: Flat"); string text = "Position: " + "Time=" + TimeToString(OpenTime, TIME_SECONDS) + ", " + "Type=" + (PosType == OP_BUY ? "Long" : "Short") + ", " + "Lots=" + DoubleToString(Lots, 2) + ", " + "Price=" + DoubleToString(OpenPrice, _Digits) + ", " + "StopLoss=" + DoubleToString(StopLossPrice, _Digits) + ", " + "TakeProfit=" + DoubleToString(TakeProfitPrice, _Digits) + ", " + "Commission=" + DoubleToString(Commission, 2) + ", " + "Profit=" + DoubleToString(Profit, 2); if (PosComment != "") text += ", \"" + PosComment + "\""; return (text); } void Position::SetPositionInfo(string &positionInfo[]) { if (PosType == OP_FLAT) { positionInfo[0] = "Position: Flat"; positionInfo[1] = "."; } else { positionInfo[0] = StringFormat("Position: %s %.2f at %s, Profit %.2f", (PosType == OP_BUY) ? "Long" : "Short", Lots, DoubleToString(OpenPrice, _Digits), Profit); positionInfo[1] = StringFormat("Stop Loss: %s, Take Profit: %s", DoubleToString(StopLossPrice, _Digits), DoubleToString(TakeProfitPrice, _Digits)); } } //## Import Start class Logger { int logLines; int fileHandle; public: string GetLogFileName(string symbol, int dataPeriod, int expertMagic); int CreateLogFile(string fileName); void WriteLogLine(string text); void WriteNewLogLine(string text); void WriteLogRequest(string text, string request); bool IsLogLinesLimitReached(int maxLines); void FlushLogFile(void); void CloseLogFile(void); int CloseExpert(void); }; string Logger::GetLogFileName(string symbol, int dataPeriod, int expertMagic) { string time = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); StringReplace(time, ":", ""); StringReplace(time, " ", "_"); string rnd = IntegerToString(MathRand()); string fileName = symbol + "_" + IntegerToString(dataPeriod) + "_" + IntegerToString(expertMagic) + "_" + time + "_" + rnd + ".log"; return (fileName); } int Logger::CreateLogFile(string fileName) { logLines = 0; int handle = FileOpen(fileName, FILE_CSV | FILE_WRITE, ","); if (handle > 0) fileHandle = handle; else Print("CreateFile: Error while creating log file!"); return (handle); } void Logger::WriteLogLine(string text) { if (fileHandle <= 0) return; FileWrite(fileHandle, TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS), text); logLines++; } void Logger::WriteNewLogLine(string text) { if (fileHandle <= 0) return; FileWrite(fileHandle, ""); FileWrite(fileHandle, TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS), text); logLines += 2; } void Logger::WriteLogRequest(string text, string request) { if (fileHandle <= 0) return; FileWrite(fileHandle, "\n" + text); FileWrite(fileHandle, TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS), request); logLines += 3; } void Logger::FlushLogFile() { if (fileHandle <= 0) return; FileFlush(fileHandle); } void Logger::CloseLogFile() { if (fileHandle <= 0) return; WriteNewLogLine(StringFormat("%s Closed.", MQLInfoString(MQL_PROGRAM_NAME))); FileClose(fileHandle); } bool Logger::IsLogLinesLimitReached(int maxLines) { return (logLines > maxLines); } //## Import Start class ActionTrade; class StrategyTrader { private: ActionTrade *actionTrade; Strategy *strategy; DataMarket *market; double epsilon; bool isEnteredLong; bool isEnteredShort; datetime timeLastEntryBar; datetime barOpenTimeForLastCloseTick; StrategyPriceType openStrPriceType; StrategyPriceType closeStrPriceType; int nBarExit; ExecutionTime openTimeExec; ExecutionTime closeTimeExec; bool useLogicalGroups; DictStringBool *groupsAllowLong; DictStringBool *groupsAllowShort; ListString *openingLogicGroups; ListString *closingLogicGroups; PosDirection GetNewPositionDirection(OrderDirection ordDir, double ordLots, PosDirection posDir, double posLots); TradeDirection AnalyzeEntryPrice(void); TradeDirection AnalyzeEntryDirection(void); void AnalyzeEntryLogicConditions(string group, double buyPrice, double sellPrice, bool &canOpenLong, bool &canOpenShort); double AnalyzeEntrySize(OrderDirection ordDir, PosDirection &newPosDir); TradeDirection AnalyzeExitPrice(void); double TradingSize(double size); int AccountPercentStopPoint(double percent, double lots); TradeDirection AnalyzeExitDirection(void); TradeDirection ReduceDirectionStatus(TradeDirection baseDirection, TradeDirection direction); TradeDirection IncreaseDirectionStatus(TradeDirection baseDirection, TradeDirection direction); TradeDirection GetClosingDirection(TradeDirection baseDirection, IndComponentType compDataType); int GetStopLossPoints(double lots); int GetTakeProfitPoints(void); void DoEntryTrade(TradeDirection tradeDir); bool DoExitTrade(void); public: // Constructors StrategyTrader(ActionTrade *actTrade); ~StrategyTrader(void); // Methods void OnInit(Strategy *strat, DataMarket *dataMarket); void OnDeinit(); void InitTrade(void); TickType GetTickType(bool isNewBar, int closeAdvance); void CalculateTrade(TickType ticktype); bool IsWrongStopsExecution(void); void ResendWrongStops(void); }; void StrategyTrader::StrategyTrader(ActionTrade *actTrade) { actionTrade = actTrade; epsilon = 0.000001; } void StrategyTrader::~StrategyTrader(void) { actionTrade = NULL; strategy = NULL; market = NULL; } void StrategyTrader::OnInit(Strategy *strat, DataMarket *dataMarket) { strategy = strat; market = dataMarket; groupsAllowLong = new DictStringBool(); groupsAllowShort = new DictStringBool(); openingLogicGroups = new ListString(); closingLogicGroups = new ListString(); } void StrategyTrader::OnDeinit(void) { delete groupsAllowLong; delete groupsAllowShort; delete openingLogicGroups; delete closingLogicGroups; } TickType StrategyTrader::GetTickType(bool isNewBar, int closeAdvance) { TickType tickType = TickType_Regular; if (isNewBar) { barOpenTimeForLastCloseTick = -1; tickType = TickType_Open; } if (((market.BarTime + market.Period * 60) - market.TickServerTime) < closeAdvance) { if (barOpenTimeForLastCloseTick == market.BarTime) { tickType = TickType_AfterClose; } else { barOpenTimeForLastCloseTick = market.BarTime; tickType = isNewBar ? TickType_OpenClose : TickType_Close; } } return (tickType); } void StrategyTrader::CalculateTrade(TickType ticktype) { // Exit bool closeOk = false; if (closeStrPriceType != StrategyPriceType_CloseAndReverse && (market.PositionDirection == PosDirection_Short || market.PositionDirection == PosDirection_Long)) { if (ticktype == TickType_Open && closeStrPriceType == StrategyPriceType_Close && openStrPriceType != StrategyPriceType_Close) { // We have missed close at the previous Bar Close TradeDirection direction = AnalyzeExitDirection(); if (direction == TradeDirection_Both || (direction == TradeDirection_Long && market.PositionDirection == PosDirection_Short) || (direction == TradeDirection_Short && market.PositionDirection == PosDirection_Long)) { // we have a missed close Order if (DoExitTrade()) actionTrade.UpdateDataMarket(market); } } else if (((closeStrPriceType == StrategyPriceType_Open) && (ticktype == TickType_Open || ticktype == TickType_OpenClose)) || ((closeStrPriceType == StrategyPriceType_Close) && (ticktype == TickType_Close || ticktype == TickType_OpenClose))) { // Exit at Bar Open or Bar Close. TradeDirection direction = AnalyzeExitDirection(); if (direction == TradeDirection_Both || (direction == TradeDirection_Long && market.PositionDirection == PosDirection_Short) || (direction == TradeDirection_Short && market.PositionDirection == PosDirection_Long)) { // Close the current position. closeOk = DoExitTrade(); if (closeOk) actionTrade.UpdateDataMarket(market); } } else if (closeStrPriceType == StrategyPriceType_Close && openStrPriceType != StrategyPriceType_Close && ticktype == TickType_AfterClose) { // Exit at after close tick. TradeDirection direction = AnalyzeExitDirection(); if (direction == TradeDirection_Both || (direction == TradeDirection_Long && market.PositionDirection == PosDirection_Short) || (direction == TradeDirection_Short && market.PositionDirection == PosDirection_Long)) closeOk = DoExitTrade(); // Close the current position. } else if (closeStrPriceType == StrategyPriceType_Indicator) { // Exit at an indicator value. TradeDirection priceReached = AnalyzeExitPrice(); if (priceReached == TradeDirection_Long) { TradeDirection direction = AnalyzeExitDirection(); if (direction == TradeDirection_Long || direction == TradeDirection_Both) if (market.PositionDirection == PosDirection_Short) closeOk = DoExitTrade(); // Close a short position. } else if (priceReached == TradeDirection_Short) { TradeDirection direction = AnalyzeExitDirection(); if (direction == TradeDirection_Short || direction == TradeDirection_Both) if (market.PositionDirection == PosDirection_Long) closeOk = DoExitTrade(); // Close a long position. } else if (priceReached == TradeDirection_Both) { TradeDirection direction = AnalyzeExitDirection(); if (direction == TradeDirection_Long || direction == TradeDirection_Short || direction == TradeDirection_Both) closeOk = DoExitTrade(); // Close the current position. } } } // Checks if we closed a position successfully. if (closeOk && !(openStrPriceType == StrategyPriceType_Close && ticktype == TickType_Close)) return; // This is to prevent new entry after Bar Closing has been executed. if (closeStrPriceType == StrategyPriceType_Close && ticktype == TickType_AfterClose) return; if (((openStrPriceType == StrategyPriceType_Open) && (ticktype == TickType_Open || ticktype == TickType_OpenClose)) || ((openStrPriceType == StrategyPriceType_Close) && (ticktype == TickType_Close || ticktype == TickType_OpenClose))) { // Entry at Bar Open or Bar Close. TradeDirection direction = AnalyzeEntryDirection(); if (direction == TradeDirection_Long || direction == TradeDirection_Short) DoEntryTrade(direction); } else if (openStrPriceType == StrategyPriceType_Indicator) { // Entry at an indicator value. TradeDirection priceReached = AnalyzeEntryPrice(); if (priceReached == TradeDirection_Long) { TradeDirection direction = AnalyzeEntryDirection(); if (direction == TradeDirection_Long || direction == TradeDirection_Both) DoEntryTrade(TradeDirection_Long); } else if (priceReached == TradeDirection_Short) { TradeDirection direction = AnalyzeEntryDirection(); if (direction == TradeDirection_Short || direction == TradeDirection_Both) DoEntryTrade(TradeDirection_Short); } else if (priceReached == TradeDirection_Both) { TradeDirection direction = AnalyzeEntryDirection(); if (direction == TradeDirection_Long || direction == TradeDirection_Short) DoEntryTrade(direction); } } } PosDirection StrategyTrader::GetNewPositionDirection(OrderDirection ordDir, double ordLots, PosDirection posDir, double posLots) { if (ordDir != OrderDirection_Buy && ordDir != OrderDirection_Sell) return (PosDirection_None); PosDirection currentDir = posDir; double currentLots = posLots; switch (currentDir) { case PosDirection_Long: if (ordDir == OrderDirection_Buy) return (PosDirection_Long); if (currentLots > ordLots + epsilon) return (PosDirection_Long); return (currentLots < ordLots - epsilon ? PosDirection_Short : PosDirection_None); case PosDirection_Short: if (ordDir == OrderDirection_Sell) return (PosDirection_Short); if (currentLots > ordLots + epsilon) return (PosDirection_Short); return (currentLots < ordLots - epsilon ? PosDirection_Long : PosDirection_None); } return (ordDir == OrderDirection_Buy ? PosDirection_Long : PosDirection_Short); } void StrategyTrader::InitTrade() { openTimeExec = strategy.Slot[0].IndicatorPointer.ExecTime; openStrPriceType = StrategyPriceType_Unknown; if (openTimeExec == ExecutionTime_AtBarOpening) openStrPriceType = StrategyPriceType_Open; else if (openTimeExec == ExecutionTime_AtBarClosing) openStrPriceType = StrategyPriceType_Close; else openStrPriceType = StrategyPriceType_Indicator; closeTimeExec = strategy.Slot[strategy.CloseSlotNumber()].IndicatorPointer.ExecTime; closeStrPriceType = StrategyPriceType_Unknown; if (closeTimeExec == ExecutionTime_AtBarOpening) closeStrPriceType = StrategyPriceType_Open; else if (closeTimeExec == ExecutionTime_AtBarClosing) closeStrPriceType = StrategyPriceType_Close; else if (closeTimeExec == ExecutionTime_CloseAndReverse) closeStrPriceType = StrategyPriceType_CloseAndReverse; else closeStrPriceType = StrategyPriceType_Indicator; useLogicalGroups = strategy.IsUsingLogicalGroups(); if (useLogicalGroups) { strategy.Slot[0].LogicalGroup = "All"; strategy.Slot[strategy.CloseSlotNumber()].LogicalGroup = "All"; for (int slot = 0; slot < strategy.CloseSlotNumber(); slot++) { if (!groupsAllowLong.ContainsKey(strategy.Slot[slot].LogicalGroup)) groupsAllowLong.Add(strategy.Slot[slot].LogicalGroup, false); if (!groupsAllowShort.ContainsKey(strategy.Slot[slot].LogicalGroup)) groupsAllowShort.Add(strategy.Slot[slot].LogicalGroup, false); } // List of logical groups int longCount = groupsAllowLong.Count(); for (int i = 0; i < longCount; i++) openingLogicGroups.Add(groupsAllowLong.Key(i)); // Logical groups of the closing conditions. for (int slot = strategy.CloseSlotNumber() + 1; slot < strategy.Slots(); slot++) { string group = strategy.Slot[slot].LogicalGroup; if (!closingLogicGroups.Contains(group) && group != "all") closingLogicGroups.Add(group); // Adds all groups except "all" } if (closingLogicGroups.Count() == 0) closingLogicGroups.Add("all"); } // Search if N Bars Exit is present as CloseFilter, // could be any slot after first closing slot. nBarExit = 0; for (int slot = strategy.CloseSlotNumber(); slot < strategy.Slots(); slot++) { if (strategy.Slot[slot].IndicatorName != "N Bars Exit") continue; nBarExit = (int) strategy.Slot[slot].IndicatorPointer.NumParam[0].Value; break; } } TradeDirection StrategyTrader::AnalyzeEntryPrice(void) { double buyPrice = 0; double sellPrice = 0; for (int i = 0; i < strategy.Slot[0].IndicatorPointer.Components(); i++) { IndicatorComp *component = strategy.Slot[0].IndicatorPointer.Component[i]; IndComponentType compType = component.DataType; if (compType == IndComponentType_OpenLongPrice) buyPrice = component.GetLastValue(); else if (compType == IndComponentType_OpenShortPrice) sellPrice = component.GetLastValue(); else if (compType == IndComponentType_OpenPrice || compType == IndComponentType_OpenClosePrice) buyPrice = sellPrice = component.GetLastValue(); component = NULL; } double basePrice = market.Close; double oldPrice = market.OldClose; bool canOpenLong = false; bool canOpenShort = false; if (oldPrice < epsilon) { // OldClose==0 for the first tick. canOpenLong = MathAbs(buyPrice - basePrice) < epsilon; canOpenShort = MathAbs(sellPrice - basePrice) < epsilon; } else { canOpenLong = (buyPrice > oldPrice + epsilon && buyPrice < basePrice + epsilon) || (buyPrice > basePrice - epsilon && buyPrice < oldPrice - epsilon); canOpenShort = (sellPrice > oldPrice + epsilon && sellPrice < basePrice + epsilon) || (sellPrice > basePrice - epsilon && sellPrice < oldPrice - epsilon); } TradeDirection direction = TradeDirection_None; if (canOpenLong && canOpenShort) direction = TradeDirection_Both; else if (canOpenLong) direction = TradeDirection_Long; else if (canOpenShort) direction = TradeDirection_Short; return (direction); } TradeDirection StrategyTrader::AnalyzeEntryDirection() { // Do not send entry order when we are not on time if (openTimeExec == ExecutionTime_AtBarOpening) for (int i = 0; i < strategy.Slot[0].IndicatorPointer.Components(); i++) { IndicatorComp *component = strategy.Slot[0].IndicatorPointer.Component[i]; if (component.DataType != IndComponentType_OpenLongPrice && component.DataType != IndComponentType_OpenShortPrice && component.DataType != IndComponentType_OpenPrice) continue; if (component.GetLastValue() < epsilon) return (TradeDirection_None); component = NULL; } for (int i = 0; i < strategy.Slots(); i++) { if (strategy.Slot[i].IndicatorName == "Enter Once") { string logicText = strategy.Slot[i].IndicatorPointer.ListParam[0].Text; if (logicText == "Enter no more than once a bar") { if (market.BarTime == timeLastEntryBar) return (TradeDirection_None); } else if (logicText == "Enter no more than once a day") { if (TimeDayOfYear(market.BarTime) == TimeDayOfYear(timeLastEntryBar)) return (TradeDirection_None); } else if (logicText == "Enter no more than once a week") { if (TimeDayOfWeek(market.BarTime) >= TimeDayOfWeek(timeLastEntryBar) && market.BarTime < timeLastEntryBar + 7 * 24 * 60 * 60) return (TradeDirection_None); } else if (logicText == "Enter no more than once a month") { if (TimeMonth(market.BarTime) == TimeMonth(timeLastEntryBar)) return (TradeDirection_None); } } } // Determining of the buy/sell entry prices. double buyPrice = 0; double sellPrice = 0; for (int i = 0; i < strategy.Slot[0].IndicatorPointer.Components(); i++) { IndicatorComp *component = strategy.Slot[0].IndicatorPointer.Component[i]; IndComponentType compType = component.DataType; if (compType == IndComponentType_OpenLongPrice) buyPrice = component.GetLastValue(); else if (compType == IndComponentType_OpenShortPrice) sellPrice = component.GetLastValue(); else if (compType == IndComponentType_OpenPrice || compType == IndComponentType_OpenClosePrice) buyPrice = sellPrice = component.GetLastValue(); component = NULL; } // Decide whether to open bool canOpenLong = buyPrice > epsilon; bool canOpenShort = sellPrice > epsilon; if (useLogicalGroups) { for (int i = 0; i < openingLogicGroups.Count(); i++) { string group = openingLogicGroups.Get(i); bool groupOpenLong = canOpenLong; bool groupOpenShort = canOpenShort; AnalyzeEntryLogicConditions(group, buyPrice, sellPrice, groupOpenLong, groupOpenShort); groupsAllowLong.Set(group, groupOpenLong); groupsAllowShort.Set(group, groupOpenShort); } bool groupLongEntry = false; for (int i = 0; i < groupsAllowLong.Count(); i++) { string key = groupsAllowLong.Key(i); bool value = groupsAllowLong.Value(key); if ((groupsAllowLong.Count() > 1 && key != "All") || groupsAllowLong.Count() == 1) groupLongEntry = groupLongEntry || value; } bool groupShortEntry = false; for (int i = 0; i < groupsAllowShort.Count(); i++) { string key = groupsAllowShort.Key(i); bool value = groupsAllowShort.Value(key); if ((groupsAllowShort.Count() > 1 && key != "All") || groupsAllowShort.Count() == 1) groupShortEntry = groupShortEntry || value; } canOpenLong = canOpenLong && groupLongEntry && groupsAllowLong.Value("All"); canOpenShort = canOpenShort && groupShortEntry && groupsAllowShort.Value("All"); } else { AnalyzeEntryLogicConditions("A", buyPrice, sellPrice, canOpenLong, canOpenShort); } TradeDirection direction = TradeDirection_None; if (canOpenLong && canOpenShort) direction = TradeDirection_Both; else if (canOpenLong) direction = TradeDirection_Long; else if (canOpenShort) direction = TradeDirection_Short; return (direction); } void StrategyTrader::AnalyzeEntryLogicConditions(string group, double buyPrice, double sellPrice, bool &canOpenLong, bool &canOpenShort) { for (int slotIndex = 0; slotIndex <= strategy.CloseSlotNumber(); slotIndex++) { if (useLogicalGroups && strategy.Slot[slotIndex].LogicalGroup != group && strategy.Slot[slotIndex].LogicalGroup != "All") continue; for (int i = 0; i < strategy.Slot[slotIndex].IndicatorPointer.Components(); i++) { IndicatorComp *component = strategy.Slot[slotIndex].IndicatorPointer.Component[i]; if (component.PosPriceDependence == PositionPriceDependence_None) { if (component.DataType == IndComponentType_AllowOpenLong && component.GetLastValue() < 0.5) canOpenLong = false; if (component.DataType == IndComponentType_AllowOpenShort && component.GetLastValue() < 0.5) canOpenShort = false; } else { double indicatorValue = component.GetLastValue(); switch (component.PosPriceDependence) { case PositionPriceDependence_PriceBuyHigher: canOpenLong = canOpenLong && buyPrice > indicatorValue + epsilon; break; case PositionPriceDependence_PriceBuyLower: canOpenLong = canOpenLong && buyPrice < indicatorValue - epsilon; break; case PositionPriceDependence_PriceSellHigher: canOpenShort = canOpenShort && sellPrice > indicatorValue + epsilon; break; case PositionPriceDependence_PriceSellLower: canOpenShort = canOpenShort && sellPrice < indicatorValue - epsilon; break; case PositionPriceDependence_BuyHigherSellLower: canOpenLong = canOpenLong && buyPrice > indicatorValue + epsilon; canOpenShort = canOpenShort && sellPrice < indicatorValue - epsilon; break; case PositionPriceDependence_BuyLowerSelHigher: // Deprecated case PositionPriceDependence_BuyLowerSellHigher: canOpenLong = canOpenLong && buyPrice < indicatorValue - epsilon; canOpenShort = canOpenShort && sellPrice > indicatorValue + epsilon; break; } } component = NULL; } } } double StrategyTrader::AnalyzeEntrySize(OrderDirection ordDir, PosDirection &newPosDir) { double size = 0; PosDirection posDir = market.PositionDirection; // Orders modification on a fly // Checks whether we are on the market if (posDir == PosDirection_Long || posDir == PosDirection_Short) { // We are on the market and have Same Dir Signal if ((ordDir == OrderDirection_Buy && posDir == PosDirection_Long) || (ordDir == OrderDirection_Sell && posDir == PosDirection_Short)) { size = 0; newPosDir = posDir; if (market.PositionLots + TradingSize(strategy.AddingLots) < strategy.MaxOpenLots + market.LotStep / 2) { switch (strategy.SameSignalAction) { case SameDirSignalAction_Add: size = TradingSize(strategy.AddingLots); break; case SameDirSignalAction_Winner: if (market.PositionProfit > epsilon) size = TradingSize(strategy.AddingLots); break; case SameDirSignalAction_Loser: if (market.PositionProfit < -epsilon) size = TradingSize(strategy.AddingLots); break; } } } else if ((ordDir == OrderDirection_Buy && posDir == PosDirection_Short) || (ordDir == OrderDirection_Sell && posDir == PosDirection_Long)) { // In case of an Opposite Dir Signal switch (strategy.OppSignalAction) { case OppositeDirSignalAction_Reduce: if (market.PositionLots > TradingSize(strategy.ReducingLots)) { // Reducing size = TradingSize(strategy.ReducingLots); newPosDir = posDir; } else { // Closing size = market.PositionLots; newPosDir = PosDirection_Closed; } break; case OppositeDirSignalAction_Close: size = market.PositionLots; newPosDir = PosDirection_Closed; break; case OppositeDirSignalAction_Reverse: size = market.PositionLots + TradingSize(strategy.EntryLots); newPosDir = (posDir == PosDirection_Long) ? PosDirection_Short : PosDirection_Long; break; case OppositeDirSignalAction_Nothing: size = 0; newPosDir = posDir; break; } } } else { // We are square on the market size = TradingSize(strategy.EntryLots); if (strategy.UseMartingale && market.ConsecutiveLosses > 0) { double correctedAmount = size * MathPow(strategy.MartingaleMultiplier, market.ConsecutiveLosses); double normalizedAmount = actionTrade.NormalizeEntrySize(correctedAmount); size = MathMax(normalizedAmount, market.MinLot); } size = MathMin(size, strategy.MaxOpenLots); newPosDir = (ordDir == OrderDirection_Buy) ? PosDirection_Long : PosDirection_Short; } return (size); } TradeDirection StrategyTrader::AnalyzeExitPrice() { IndicatorSlot *slot = strategy.Slot[strategy.CloseSlotNumber()]; // Searching the exit price in the exit indicator slot. double buyPrice = 0; double sellPrice = 0; for (int i = 0; i < slot.IndicatorPointer.Components(); i++) { IndicatorComp *comp = slot.IndicatorPointer.Component[i]; IndComponentType compType = comp.DataType; if (compType == IndComponentType_CloseLongPrice) sellPrice = comp.GetLastValue(); else if (compType == IndComponentType_CloseShortPrice) buyPrice = comp.GetLastValue(); else if (compType == IndComponentType_ClosePrice || compType == IndComponentType_OpenClosePrice) buyPrice = sellPrice = comp.GetLastValue(); comp = NULL; } // We can close if the closing price is higher than zero. bool canCloseLong = sellPrice > epsilon; bool canCloseShort = buyPrice > epsilon; // Check if the closing price was reached. if (canCloseLong) canCloseLong = (sellPrice > market.OldBid + epsilon && sellPrice < market.Bid + epsilon) || (sellPrice < market.OldBid - epsilon && sellPrice > market.Bid - epsilon); if (canCloseShort) canCloseShort = (buyPrice > market.OldBid + epsilon && buyPrice < market.Bid + epsilon) || (buyPrice < market.OldBid - epsilon && buyPrice > market.Bid - epsilon); // Determine the trading direction. TradeDirection direction = TradeDirection_None; if (canCloseLong && canCloseShort) direction = TradeDirection_Both; else if (canCloseLong) direction = TradeDirection_Short; else if (canCloseShort) direction = TradeDirection_Long; slot = NULL; return (direction); } TradeDirection StrategyTrader::AnalyzeExitDirection() { int closeSlot = strategy.CloseSlotNumber(); if (closeTimeExec == ExecutionTime_AtBarClosing) for (int i = 0; i < strategy.Slot[closeSlot].IndicatorPointer.Components(); i++) { IndComponentType dataType = strategy.Slot[closeSlot].IndicatorPointer.Component[i].DataType; double value = strategy.Slot[closeSlot].IndicatorPointer.Component[i].GetLastValue(); if (dataType != IndComponentType_CloseLongPrice && dataType != IndComponentType_CloseShortPrice && dataType != IndComponentType_ClosePrice) continue; if (value < epsilon) return (TradeDirection_None); } if (strategy.CloseSlots() == 0) return (TradeDirection_Both); if (nBarExit > 0 && (market.PositionOpenTime + (nBarExit * ((int) market.Period * 60)) < market.TickServerTime)) return (TradeDirection_Both); TradeDirection direction = TradeDirection_None; if (useLogicalGroups) { for (int i = 0; i < closingLogicGroups.Count(); i++) { string group = closingLogicGroups.Get(i); TradeDirection groupDirection = TradeDirection_Both; // Determining of the slot direction for (int slot = strategy.CloseSlotNumber() + 1; slot < strategy.Slots(); slot++) { TradeDirection slotDirection = TradeDirection_None; if (strategy.Slot[slot].LogicalGroup == group || strategy.Slot[slot].LogicalGroup == "all") { for (int c = 0; c < strategy.Slot[slot].IndicatorPointer.Components(); c++) if (strategy.Slot[slot].IndicatorPointer.Component[c].GetLastValue() > 0) slotDirection = GetClosingDirection(slotDirection, strategy.Slot[slot].IndicatorPointer.Component[c].DataType); groupDirection = ReduceDirectionStatus(groupDirection, slotDirection); } } direction = IncreaseDirectionStatus(direction, groupDirection); } } else { // Search close filters for a closing signal. for (int slot = strategy.CloseSlotNumber() + 1; slot < strategy.Slots(); slot++) for (int c = 0; c < strategy.Slot[slot].IndicatorPointer.Components(); c++) if (strategy.Slot[slot].IndicatorPointer.Component[c].GetLastValue() > epsilon) direction = GetClosingDirection(direction, strategy.Slot[slot].IndicatorPointer.Component[c].DataType); } return (direction); } TradeDirection StrategyTrader::ReduceDirectionStatus(TradeDirection baseDirection, TradeDirection direction) { if (baseDirection == direction || direction == TradeDirection_Both) return (baseDirection); if (baseDirection == TradeDirection_Both) return (direction); return (TradeDirection_None); } TradeDirection StrategyTrader::IncreaseDirectionStatus(TradeDirection baseDirection, TradeDirection direction) { if (baseDirection == direction || direction == TradeDirection_None) return (baseDirection); if (baseDirection == TradeDirection_None) return (direction); return (TradeDirection_Both); } TradeDirection StrategyTrader::GetClosingDirection(TradeDirection baseDirection, IndComponentType compDataType) { TradeDirection newDirection = baseDirection; if (compDataType == IndComponentType_ForceClose) { newDirection = TradeDirection_Both; } else if (compDataType == IndComponentType_ForceCloseShort) { if (baseDirection == TradeDirection_None) newDirection = TradeDirection_Long; else if (baseDirection == TradeDirection_Short) newDirection = TradeDirection_Both; } else if (compDataType == IndComponentType_ForceCloseLong) { if (baseDirection == TradeDirection_None) newDirection = TradeDirection_Short; else if (baseDirection == TradeDirection_Long) newDirection = TradeDirection_Both; } return (newDirection); } int StrategyTrader::GetStopLossPoints(double lots) { int indStop = INT_MAX; bool isIndStop = true; int closeSlot = strategy.CloseSlotNumber(); string name = strategy.Slot[closeSlot].IndicatorName; if (name == "Account Percent Stop") indStop = AccountPercentStopPoint(strategy.Slot[closeSlot].IndicatorPointer.NumParam[0].Value, lots); else if (name == "ATR Stop") indStop = (int) MathRound(strategy.Slot[closeSlot].IndicatorPointer.Component[0].GetLastValue() / market.Point); else if (name == "Stop Loss" || name == "Stop Limit") indStop = (int) strategy.Slot[closeSlot].IndicatorPointer.NumParam[0].Value; else if (name == "Trailing Stop" || name == "Trailing Stop Limit") indStop = (int) strategy.Slot[closeSlot].IndicatorPointer.NumParam[0].Value; else isIndStop = false; int permStop = strategy.UsePermanentSL ? strategy.PermanentSL : INT_MAX; int stopLoss = 0; if (isIndStop || strategy.UsePermanentSL) { stopLoss = MathMin(indStop, permStop); if (stopLoss < market.StopLevel) stopLoss = market.StopLevel; } return (stopLoss); } int StrategyTrader::GetTakeProfitPoints() { int takeprofit = 0; int permLimit = strategy.UsePermanentTP ? strategy.PermanentTP : INT_MAX; int indLimit = INT_MAX; bool isIndLimit = true; int closeSlot = strategy.CloseSlotNumber(); string name = strategy.Slot[closeSlot].IndicatorName; if (name == "Take Profit") indLimit = (int) strategy.Slot[closeSlot].IndicatorPointer.NumParam[0].Value; else if (name == "Stop Limit" || name == "Trailing Stop Limit") indLimit = (int) strategy.Slot[closeSlot].IndicatorPointer.NumParam[1].Value; else isIndLimit = false; if (isIndLimit || strategy.UsePermanentTP) { takeprofit = MathMin(indLimit, permLimit); if (takeprofit < market.StopLevel) takeprofit = market.StopLevel; } return (takeprofit); } void StrategyTrader::DoEntryTrade(TradeDirection tradeDir) { OrderDirection ordDir; OperationType opType; TraderOrderType type; if (timeLastEntryBar != market.BarTime) isEnteredLong = isEnteredShort = false; switch (tradeDir) { case TradeDirection_Long: // Buy if (isEnteredLong) return; ordDir = OrderDirection_Buy; opType = OperationType_Buy; type = TraderOrderType_Buy; break; case TradeDirection_Short: // Sell if (isEnteredShort) return; ordDir = OrderDirection_Sell; opType = OperationType_Sell; type = TraderOrderType_Sell; break; default: // Wrong direction of trade. return; } PosDirection newPosDir = PosDirection_None; double size = AnalyzeEntrySize(ordDir, newPosDir); if (size < market.MinLot - epsilon) return; // The entry trade is cancelled. TrailingStopMode trlMode = TrailingStopMode_Bar; int trlStop = 0; int closeSlot = strategy.CloseSlotNumber(); string name = strategy.Slot[closeSlot].IndicatorName; if (name == "Trailing Stop" || name == "Trailing Stop Limit") { trlStop = (int) strategy.Slot[closeSlot].IndicatorPointer.NumParam[0].Value; string mode = strategy.Slot[closeSlot].IndicatorPointer.ListParam[1].Text; if (mode != "New bar") trlMode = TrailingStopMode_Tick; } int stopLoss = GetStopLossPoints(size); int takeProfit = GetTakeProfitPoints(); int breakEven = strategy.UseBreakEven ? strategy.BreakEven : 0; bool response = actionTrade.ManageOrderSend(type, size, stopLoss, takeProfit, trlMode, trlStop, breakEven); if (response) { // The order was executed successfully. timeLastEntryBar = market.BarTime; if (type == TraderOrderType_Buy) isEnteredLong = true; else isEnteredShort = true; market.WrongStopLoss = 0; market.WrongTakeProf = 0; market.WrongStopsRetry = 0; } else { // Error in operation execution. market.WrongStopLoss = stopLoss; market.WrongTakeProf = takeProfit; } } bool StrategyTrader::DoExitTrade() { if (!market.IsFailedCloseOrder) market.IsSentCloseOrder = true; market.CloseOrderTickCounter = 0; bool orderResponse = actionTrade.CloseCurrentPosition(); market.WrongStopLoss = 0; market.WrongTakeProf = 0; market.WrongStopsRetry = 0; return (orderResponse); } bool StrategyTrader::IsWrongStopsExecution() { const int maxRetry = 4; if (market.PositionDirection == PosDirection_Closed || market.PositionLots < epsilon || market.WrongStopsRetry >= maxRetry) { market.WrongStopLoss = 0; market.WrongTakeProf = 0; market.WrongStopsRetry = 0; return (false); } bool isWrongStop = (market.WrongStopLoss > 0 && market.PositionStopLoss < epsilon) || (market.WrongTakeProf > 0 && market.PositionTakeProfit < epsilon); return (isWrongStop); } void StrategyTrader::ResendWrongStops() { double stopLossPrice = 0; int stopLoss = market.WrongStopLoss; if (stopLoss > 0) { if (market.PositionDirection == PosDirection_Long) stopLossPrice = market.Bid - stopLoss * market.Point; else if (market.PositionDirection == PosDirection_Short) stopLossPrice = market.Ask + stopLoss * market.Point; } double takeProfitPrice = 0; int takeProfit = market.WrongTakeProf; if (takeProfit > 0) { if (market.PositionDirection == PosDirection_Long) takeProfitPrice = market.Bid + takeProfit * market.Point; else if (market.PositionDirection == PosDirection_Short) takeProfitPrice = market.Ask - takeProfit * market.Point; } bool isSucess = actionTrade.ModifyPosition(stopLossPrice, takeProfitPrice); if (isSucess) { market.WrongStopLoss = 0; market.WrongTakeProf = 0; market.WrongStopsRetry = 0; } else { market.WrongStopsRetry++; } } double StrategyTrader::TradingSize(double size) { if (strategy.UseAccountPercentEntry) size = (size / 100) * market.AccountEquity / market.MarginRequired; return (actionTrade.NormalizeEntrySize(size)); } int StrategyTrader::AccountPercentStopPoint(double percent, double lots) { double balance = market.AccountBalance; double moneyrisk = balance * percent / 100; double spread = market.Spread; double tickvalue = market.TickValue; double stoploss = moneyrisk / (lots * tickvalue) - spread; return ((int) MathRound(stoploss)); } //## Import Start #define TRADE_RETRY_COUNT 4 #define TRADE_RETRY_WAIT 100 class ActionTrade { private: double epsilon; // Fields Strategy *strategy; DataSet *dataSet[]; DataMarket *dataMarket; Position *position; Logger *logger; StrategyTrader *strategyTrader; // Properties int lastError; double pipsValue; int pipsPoint; int stopLevel; datetime barTime; datetime barHighTime; datetime barLowTime; double barHighPrice; double barLowPrice; int trailingStop; TrailingStopMode trailingMode; int breakEven; int consecutiveLosses; string dynamicInfoParams[]; string dynamicInfoValues[]; // Methods bool CheckEnvironment(int minDataBars); bool CheckChartBarsCount(int minDataBars); int FindBarsCountNeeded(int minDataBars); int SetAggregatePosition(Position *pos); void UpdateDataSet(DataSet *data, int maxBars); bool IsTradeContextFree(void); void ActivateProtectionMinAccount(void); void CloseExpert(void); // Trading methods double GetTakeProfitPrice(int type, int takeProfit); double GetStopLossPrice(int type, int stopLoss); double CorrectTakeProfitPrice(int type, double takeProfitPrice); double CorrectStopLossPrice(int type, double stopLossPrice); double NormalizeEntryPrice(double price); void SetMaxStopLoss(void); void SetBreakEvenStop(void); void SetTrailingStop(bool isNewBar); void SetTrailingStopBarMode(void); void SetTrailingStopTickMode(void); void DetectPositionClosing(void); // Specific MQ4 trading methods bool OpenNewPosition(int type, double lots, int stopLoss, int takeProfit); bool AddToCurrentPosition(int type, double lots, int stopLoss, int takeProfit); bool ReduceCurrentPosition(double lots, int stopLoss, int takeProfit); bool ReverseCurrentPosition(int type, double lots, int stopLoss, int takeProfit); bool CloseOrder(int orderTicket, double lots); bool SelectOrder(int orderTicket); bool SendOrder(int type, double lots, int stopLoss, int takeProfit); bool ModifyOrder(int orderTicket, double stopLossPrice, double takeProfitPrice); public: // Constructors ActionTrade(void); ~ActionTrade(void); // Properties double EntryAmount; double MaximumAmount; double AddingAmount; double ReducingAmount; string OrderComment; int MinDataBars; int ProtectionMinAccount; int ProtectionMaxStopLoss; int ExpertMagic; bool SeparateSLTP; bool WriteLogFile; bool FIFOorder; int TrailingStopMovingStep; int MaxLogLinesInFile; int BarCloseAdvance; // Methods int OnInit(void); void OnTick(void); void OnDeinit(const int reason); void UpdateDataMarket(DataMarket *market); double NormalizeEntrySize(double size); bool ManageOrderSend(int type, double lots, int stopLoss, int takeProfit, TrailingStopMode trlMode, int trlStop, int brkEven); bool ModifyPosition(double stopLossPrice, double takeProfitPrice); bool CloseCurrentPosition(void); }; void ActionTrade::ActionTrade(void) { epsilon = 0.000001; position = new Position(); logger = new Logger(); strategyTrader = new StrategyTrader(GetPointer(this)); } void ActionTrade::~ActionTrade(void) { if (CheckPointer(position) == POINTER_DYNAMIC) delete position; if (CheckPointer(logger) == POINTER_DYNAMIC) delete logger; if (CheckPointer(strategyTrader) == POINTER_DYNAMIC) delete strategyTrader; } int ActionTrade::OnInit() { dataMarket = new DataMarket(); barHighTime = 0; barLowTime = 0; barHighPrice = 0; barLowPrice = 1000000; string message = StringFormat("%s loaded.", MQLInfoString(MQL_PROGRAM_NAME)); Comment(message); Print(message); if (WriteLogFile) { logger.CreateLogFile(logger.GetLogFileName(_Symbol, _Period, ExpertMagic)); logger.WriteLogLine(message); logger.WriteLogLine("Entry Amount: " + DoubleToString(EntryAmount, 2) + ", " + "Maximum Amount: " + DoubleToString(MaximumAmount, 2) + ", " + "Adding Amount: " + DoubleToString(AddingAmount, 2) + ", " + "Reducing Amount: " + DoubleToString(ReducingAmount, 2)); logger.WriteLogLine("Protection Min Account: " + IntegerToString(ProtectionMinAccount) + ", " + "Protection Max StopLoss: " + IntegerToString(ProtectionMaxStopLoss)); logger.WriteLogLine("Expert Magic: " + IntegerToString(ExpertMagic) + ", " + "Bar Close Advance: " + IntegerToString(BarCloseAdvance)); logger.FlushLogFile(); } if (_Digits == 2 || _Digits == 3) pipsValue = 0.01; else if (_Digits == 4 || _Digits == 5) pipsValue = 0.0001; else pipsValue = _Digits; if (_Digits == 3 || _Digits == 5) pipsPoint = 10; else pipsPoint = 1; stopLevel = (int) MarketInfo(_Symbol, MODE_STOPLEVEL) + pipsPoint; if (stopLevel < 3 * pipsPoint) stopLevel = 3 * pipsPoint; if (ProtectionMaxStopLoss > 0 && ProtectionMaxStopLoss < stopLevel) ProtectionMaxStopLoss = stopLevel; if (TrailingStopMovingStep < pipsPoint) TrailingStopMovingStep = pipsPoint; StrategyManager *strategyManager = new StrategyManager(); // Strategy initialization strategy = strategyManager.GetStrategy(); strategy.SetSymbol(_Symbol); strategy.SetPeriod((DataPeriod) _Period); strategy.SetIsTester(MQLInfoInteger(MQL_TESTER)); strategy.EntryLots = EntryAmount; strategy.MaxOpenLots = MaximumAmount; strategy.AddingLots = AddingAmount; strategy.ReducingLots = ReducingAmount; delete strategyManager; // Checks the requirements. bool isEnvironmentGood = CheckEnvironment(strategy.MinBarsRequired); if (!isEnvironmentGood) { // There is a non fulfilled condition, therefore we must exit. Sleep(20 * 1000); ExpertRemove(); return (INIT_FAILED); } // Market initialization string charts[]; strategy.GetRequiredCharts(charts); string chartsNote = "Loading data: "; for (int i = 0; i < ArraySize(charts); i++) chartsNote += charts[i] + ", "; chartsNote += "Minumum bars: " + IntegerToString(strategy.MinBarsRequired) + "..."; Comment(chartsNote); Print(chartsNote); // Initial data loading ArrayResize(dataSet, ArraySize(charts)); for (int i = 0; i < ArraySize(charts); i++) dataSet[i] = new DataSet(charts[i]); SetAggregatePosition(position); // Checks the necessary bars. MinDataBars = FindBarsCountNeeded(MinDataBars); // Initial strategy calculation for (int i = 0; i < ArraySize(dataSet); i++) UpdateDataSet(dataSet[i], MinDataBars); strategy.CalculateStrategy(dataSet); // Initialize StrategyTrader strategyTrader.OnInit(strategy, dataMarket); strategyTrader.InitTrade(); // Initialize the chart's info label. strategy.DynamicInfoInitArrays(dynamicInfoParams, dynamicInfoValues); int paramsX = 0; int valuesX = 140; int locationY = 40; color foreColor = GetChartForeColor(0); int count = ArraySize(dynamicInfoParams); for (int i = 0; i < count; i++) { string namep = "Lbl_prm_" + IntegerToString(i); string namev = "Lbl_val_" + IntegerToString(i); string param = dynamicInfoParams[i] == "" ? "." : dynamicInfoParams[i]; LabelCreate(0, namep, 0, paramsX, locationY, CORNER_LEFT_UPPER, param, "Ariel", 8, foreColor); LabelCreate(0, namev, 0, valuesX, locationY, CORNER_LEFT_UPPER, ".", "Ariel", 8, foreColor); locationY += 12; } LabelCreate(0, "Lbl_pos_0", 0, 350, 0, CORNER_LEFT_UPPER, ".", "Ariel", 10, foreColor); LabelCreate(0, "Lbl_pos_1", 0, 350, 15, CORNER_LEFT_UPPER, ".", "Ariel", 10, foreColor); LabelCreate(0, "Lbl_pos_2", 0, 350, 29, CORNER_LEFT_UPPER, ".", "Ariel", 10, foreColor); Comment(""); return (INIT_SUCCEEDED); } void ActionTrade::OnTick() { RefreshRates(); for (int i = 0; i < ArraySize(dataSet); i++) UpdateDataSet(dataSet[i], MinDataBars); UpdateDataMarket(dataMarket); bool isNewBar = (barTime < dataMarket.BarTime && dataMarket.Volume < 5); barTime = dataMarket.BarTime; lastError = 0; // Checks if minimum account was reached. if (ProtectionMinAccount > 0 && AccountEquity() < ProtectionMinAccount) ActivateProtectionMinAccount(); // Checks and sets Max SL protection. if (ProtectionMaxStopLoss > 0) SetMaxStopLoss(); // Checks if position was closed. DetectPositionClosing(); if (breakEven > 0) SetBreakEvenStop(); if (trailingStop > 0) SetTrailingStop(isNewBar); SetAggregatePosition(position); if (isNewBar && WriteLogFile) logger.WriteNewLogLine(position.ToString()); if (dataSet[0].Bars >= strategy.MinBarsRequired) { strategy.CalculateStrategy(dataSet); TickType tickType = strategyTrader.GetTickType(isNewBar, BarCloseAdvance); strategyTrader.CalculateTrade(tickType); } // Sends OrderModify on SL/TP errors if (strategyTrader.IsWrongStopsExecution()) strategyTrader.ResendWrongStops(); string accountInfo = StringFormat("%s Balance: %.2f, Equity: %.2f", TimeToString(dataMarket.TickServerTime, TIME_SECONDS), AccountInfoDouble(ACCOUNT_BALANCE), AccountInfoDouble(ACCOUNT_EQUITY)); LabelTextChange(0, "Lbl_pos_0", accountInfo); string positionInfo[2]; position.SetPositionInfo(positionInfo); for (int i = 0; i < 2; i++) LabelTextChange(0, "Lbl_pos_" + IntegerToString(i + 1), positionInfo[i]); strategy.DynamicInfoSetValues(dynamicInfoValues); int count = ArraySize(dynamicInfoValues); for (int i = 0; i < count; i++) { string namev = "Lbl_val_" + IntegerToString(i); string val = dynamicInfoValues[i] == "" ? "." : dynamicInfoValues[i]; LabelTextChange(0, namev, val); } if (WriteLogFile) { if (logger.IsLogLinesLimitReached(MaxLogLinesInFile)) { logger.CloseLogFile(); logger.CreateLogFile(logger.GetLogFileName(_Symbol, _Period, ExpertMagic)); } logger.FlushLogFile(); } } void ActionTrade::OnDeinit(const int reason) { strategyTrader.OnDeinit(); if (WriteLogFile) logger.CloseLogFile(); if (CheckPointer(strategy) == POINTER_DYNAMIC) delete strategy; for (int i = 0; i < ArraySize(dataSet); i++) if (CheckPointer(dataSet[i]) == POINTER_DYNAMIC) delete dataSet[i]; ArrayFree(dataSet); if (CheckPointer(dataMarket) == POINTER_DYNAMIC) delete dataMarket; int count = ArraySize(dynamicInfoParams); for (int i = 0; i < count; i++) { LabelDelete(0, "Lbl_val_" + IntegerToString(i)); LabelDelete(0, "Lbl_prm_" + IntegerToString(i)); } for (int i = 0; i < 3; i++) LabelDelete(0, "Lbl_pos_" + IntegerToString(i)); } bool ActionTrade::CheckEnvironment(int minDataBars) { if (!CheckChartBarsCount(minDataBars)) return (false); if (MQLInfoInteger(MQL_TESTER)) { SetAggregatePosition(position); return (true); } if (AccountNumber() == 0) { Comment("\n You are not logged in. Please login first."); for (int attempt = 0; attempt < 200; attempt++) { if (AccountNumber() == 0) Sleep(300); else break; } if (AccountNumber() == 0) return (false); } if (SetAggregatePosition(position) == -1) return (false); return (true); } int ActionTrade::FindBarsCountNeeded(int minDataBars) { int barStep = 50; int minBars = MathMax(minDataBars, 50); int maxBars = MathMax(minBars, 3000); // Initial state int initialBars = MathMax(strategy.MinBarsRequired, minBars); initialBars = MathMax(strategy.FirstBar, initialBars); for (int i = 0; i < ArraySize(dataSet); i++) UpdateDataSet(dataSet[i], initialBars); UpdateDataMarket(dataMarket); double initialBid = dataMarket.Bid; strategy.CalculateStrategy(dataSet); string dynamicInfo = strategy.DynamicInfoText(); int necessaryBars = initialBars; int roundedInitialBars = (int) (barStep * MathCeil(((double) initialBars) / barStep)); int firstTestBars = roundedInitialBars >= initialBars + barStep / 2 ? roundedInitialBars : roundedInitialBars + barStep; for (int bars = firstTestBars; bars <= maxBars; bars += barStep) { for (int i = 0; i < ArraySize(dataSet); i++) UpdateDataSet(dataSet[i], bars); UpdateDataMarket(dataMarket); strategy.CalculateStrategy(dataSet); string currentInfo = strategy.DynamicInfoText(); if (dynamicInfo == currentInfo) break; dynamicInfo = currentInfo; necessaryBars = bars; if (MathAbs(initialBid - dataMarket.Bid) > epsilon) { // Reset the test if new tick has arrived. for (int i = 0; i < ArraySize(dataSet); i++) UpdateDataSet(dataSet[i], initialBars); UpdateDataMarket(dataMarket); initialBid = dataMarket.Bid; strategy.CalculateStrategy(dataSet); dynamicInfo = strategy.DynamicInfoText(); bars = firstTestBars - barStep; } } string barsMessage = "The expert uses " + IntegerToString(necessaryBars) + " bars."; if (WriteLogFile) { logger.WriteLogLine(barsMessage); string timeLastBar = TimeToString(dataMarket.TickServerTime, TIME_DATE | TIME_MINUTES); logger.WriteLogLine("Indicator values: " + dataSet[0].Chart + ", Time last bar: " + timeLastBar); logger.WriteLogLine(dynamicInfo); } Print(barsMessage); return (necessaryBars); } void ActionTrade::UpdateDataSet(DataSet *data, int maxBars) { string symbol = data.Symbol; int period = (int) data.Period; int bars = MathMin(Bars(symbol, period), maxBars); data.LotSize = (int) MarketInfo(symbol, MODE_LOTSIZE); data.Digits = (int) MarketInfo(symbol, MODE_DIGITS); data.StopLevel = (int) MarketInfo(symbol, MODE_STOPLEVEL); data.Point = MarketInfo(symbol, MODE_POINT); data.TickValue = MarketInfo(symbol, MODE_TICKVALUE); data.MinLot = MarketInfo(symbol, MODE_MINLOT); data.MaxLot = MarketInfo(symbol, MODE_MAXLOT); data.LotStep = MarketInfo(symbol, MODE_LOTSTEP); data.MarginRequired = MarketInfo(symbol, MODE_MARGINREQUIRED); data.Bars = bars; data.ServerTime = TimeCurrent(); data.Bid = MarketInfo(symbol, MODE_BID); data.Ask = MarketInfo(symbol, MODE_ASK); data.Spread = (data.Ask - data.Bid) / data.Point; if (data.MarginRequired < epsilon) data.MarginRequired = data.Bid * data.LotSize / 100; data.SetPrecision(); MqlRates rates[]; RefreshRates(); ArraySetAsSeries(rates, false); int copied = CopyRates(symbol, period, 0, bars, rates); ArrayResize(data.Time, bars); ArrayResize(data.Open, bars); ArrayResize(data.High, bars); ArrayResize(data.Low, bars); ArrayResize(data.Close, bars); ArrayResize(data.Volume, bars); for (int i = 0; i < bars; i++) { data.Time[i] = rates[i].time; data.Open[i] = rates[i].open; data.High[i] = rates[i].high; data.Low[i] = rates[i].low; data.Close[i] = rates[i].close; data.Volume[i] = (int) rates[i].tick_volume; } } void ActionTrade::UpdateDataMarket(DataMarket *market) { market.Symbol = _Symbol; market.Period = (DataPeriod) _Period; market.TickLocalTime = TimeLocal(); market.TickServerTime = TimeCurrent(); market.BarTime = Time[0]; market.PositionLots = position.Lots; market.PositionOpenPrice = position.OpenPrice; market.PositionOpenTime = position.OpenTime; market.PositionStopLoss = position.StopLossPrice; market.PositionTakeProfit = position.TakeProfitPrice; market.PositionProfit = position.Profit; market.PositionDirection = position.Direction; market.AccountBalance = AccountBalance(); market.AccountEquity = AccountEquity(); market.AccountFreeMargin = AccountFreeMargin(); market.ConsecutiveLosses = consecutiveLosses; market.OldAsk = market.Ask; market.OldBid = market.Bid; market.OldClose = market.Close; market.Ask = MarketInfo(_Symbol, MODE_ASK); market.Bid = MarketInfo(_Symbol, MODE_BID); market.Close = Close[0]; market.Volume = Volume[0]; market.IsNewBid = MathAbs(market.OldBid - market.Bid) > epsilon; market.LotSize = (int) MarketInfo(_Symbol, MODE_LOTSIZE); market.StopLevel = (int) MarketInfo(_Symbol, MODE_STOPLEVEL); market.Point = MarketInfo(_Symbol, MODE_POINT); market.TickValue = MarketInfo(_Symbol, MODE_TICKVALUE); market.MinLot = MarketInfo(_Symbol, MODE_MINLOT); market.MaxLot = MarketInfo(_Symbol, MODE_MAXLOT); market.LotStep = MarketInfo(_Symbol, MODE_LOTSTEP); market.MarginRequired = MarketInfo(_Symbol, MODE_MARGINREQUIRED); market.Spread = (market.Ask - market.Bid) / market.Point; if (market.MarginRequired < epsilon) market.MarginRequired = market.Bid * market.LotSize / 100; } bool ActionTrade::CheckChartBarsCount(int minDataBars) { if (MQLInfoInteger(MQL_TESTER)) { if (Bars(_Symbol, _Period) >= minDataBars) return (true); string message = "\n Cannot load enough bars! The expert needs minimum " + IntegerToString(minDataBars) + " bars." + "\n Please check the \"Use date\" option" + " and set the \"From:\" and \"To:\" dates properly."; Comment(message); Print(message); return (false); } int bars = 0; double rates[][6]; for (int attempt = 0; attempt < 10; attempt++) { RefreshRates(); bars = ArrayCopyRates(rates, _Symbol, _Period); if (bars < minDataBars && GetLastError() == 4066) { Comment("Loading..."); Sleep(500); } else break; if (IsStopped()) break; } bool isEnoughBars = (bars >= minDataBars); if (!isEnoughBars) { string message = "There isn\'t enough bars. The expert needs minimum " + IntegerToString(minDataBars) + " bars. " + "Currently " + IntegerToString(bars) + " bars are loaded." + "\n Press and hold the Home key to force MetaTrader loading more bars."; Comment(message); Print(message); } return (isEnoughBars); } int ActionTrade::SetAggregatePosition(Position *pos) { pos.PosType = OP_FLAT; pos.Direction = PosDirection_None; pos.OpenTime = D'2050.01.01 00:00'; pos.Lots = 0; pos.OpenPrice = 0; pos.StopLossPrice = 0; pos.TakeProfitPrice = 0; pos.Profit = 0; pos.Commission = 0; pos.PosComment = ""; int positions = 0; for (int i = 0; i < OrdersTotal(); i++) { if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { Print("Error with OrderSelect: ", GetErrorDescription(GetLastError())); Comment("Cannot check current position!"); continue; } if (OrderMagicNumber() != ExpertMagic || OrderSymbol() != _Symbol) continue; // An order not sent by Forex Strategy Builder. if (OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLLIMIT || OrderType() == OP_BUYSTOP || OrderType() == OP_SELLSTOP) continue; // A pending order. if (pos.PosType >= 0 && pos.PosType != OrderType()) { string message = "There are open positions in different directions!"; Comment(message); Print(message); return (-1); } pos.PosType = OrderType(); pos.Direction = position.PosType == OP_FLAT ? PosDirection_None : position.PosType == OP_BUY ? PosDirection_Long : PosDirection_Short; pos.OpenTime = (OrderOpenTime() < pos.OpenTime) ? OrderOpenTime() : pos.OpenTime; pos.OpenPrice = (pos.Lots * pos.OpenPrice + OrderLots() * OrderOpenPrice()) / (pos.Lots + OrderLots()); pos.Lots += OrderLots(); pos.Commission += OrderCommission(); pos.Profit += OrderProfit() + pos.Commission; pos.StopLossPrice = OrderStopLoss(); pos.TakeProfitPrice = OrderTakeProfit(); pos.PosComment = OrderComment(); positions += 1; } if (pos.OpenPrice > 0) pos.OpenPrice = NormalizeDouble(pos.OpenPrice, _Digits); if (pos.Lots == 0) pos.OpenTime = D'2050.01.01 00:00'; return (positions); } bool ActionTrade::ManageOrderSend(int type, double lots, int stopLoss, int takeProfit, TrailingStopMode trlMode, int trlStop, int brkEven) { trailingMode = trlMode; trailingStop = trlStop; breakEven = brkEven; bool orderResponse = false; int positions = SetAggregatePosition(position); if (positions < 0) return (false); if (positions == 0) { // Open a new position. orderResponse = OpenNewPosition(type, lots, stopLoss, takeProfit); } else if (positions > 0) { // There is an open position. if ((position.PosType == OP_BUY && type == OP_BUY) || (position.PosType == OP_SELL && type == OP_SELL)) { orderResponse = AddToCurrentPosition(type, lots, stopLoss, takeProfit); } else if ((position.PosType == OP_BUY && type == OP_SELL) || (position.PosType == OP_SELL && type == OP_BUY)) { if (MathAbs(position.Lots - lots) < epsilon) orderResponse = CloseCurrentPosition(); else if (position.Lots > lots) orderResponse = ReduceCurrentPosition(lots, stopLoss, takeProfit); else if (position.Lots < lots) orderResponse = ReverseCurrentPosition(type, lots, stopLoss, takeProfit); } } return (orderResponse); } bool ActionTrade::OpenNewPosition(int type, double lots, int stopLoss, int takeProfit) { bool orderResponse = false; if (type != OP_BUY && type != OP_SELL) { // Error. Wrong order type! Print("Wrong 'Open new position' request - Wrong order type!"); return (false); } double orderLots = NormalizeEntrySize(lots); if (AccountFreeMarginCheck(_Symbol, type, orderLots) > 0) { if (SeparateSLTP) { if (WriteLogFile) logger.WriteLogLine("OpenNewPosition => SendOrder"); orderResponse = SendOrder(type, orderLots, 0, 0); if (orderResponse) { if (WriteLogFile) logger.WriteLogLine("OpenNewPosition => ModifyPosition"); double stopLossPrice = GetStopLossPrice(type, stopLoss); double takeProfitPrice = GetTakeProfitPrice(type, takeProfit); orderResponse = ModifyOrder(orderResponse, stopLossPrice, takeProfitPrice); } } else { orderResponse = SendOrder(type, orderLots, stopLoss, takeProfit); if (WriteLogFile) logger.WriteLogLine("OpenNewPosition: SendOrder Response = " + (orderResponse ? "Ok" : "Failed")); if (!orderResponse && lastError == 130) { // Invalid Stops. We'll check for forbidden direct set of SL and TP if (WriteLogFile) logger.WriteLogLine("OpenNewPosition: SendOrder"); orderResponse = SendOrder(type, lots, 0, 0); if (orderResponse) { if (WriteLogFile) logger.WriteLogLine("OpenNewPosition: ModifyPosition"); double stopLossPrice = GetStopLossPrice(type, stopLoss); double takeProfitPrice = GetTakeProfitPrice(type, takeProfit); orderResponse = ModifyOrder(orderResponse, stopLossPrice, takeProfitPrice); if (orderResponse) { SeparateSLTP = true; Print(AccountCompany(), " marked with separate stops setting."); } } } } } SetAggregatePosition(position); if (WriteLogFile) logger.WriteLogLine(position.ToString()); return (orderResponse); } bool ActionTrade::CloseCurrentPosition() { bool orderResponse = false; int totalOrders = OrdersTotal(); int orders = 0; datetime openPos[][2]; for (int i = 0; i < totalOrders; i++) { if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { lastError = GetLastError(); Print("Error in OrderSelect: ", GetErrorDescription(lastError)); continue; } if (OrderMagicNumber() != ExpertMagic || OrderSymbol() != _Symbol) continue; int orderType = OrderType(); if (orderType != OP_BUY && orderType != OP_SELL) continue; orders++; ArrayResize(openPos, orders); openPos[orders - 1][0] = OrderOpenTime(); openPos[orders - 1][1] = OrderTicket(); } if (FIFOorder) ArraySort(openPos, WHOLE_ARRAY, 0, MODE_ASCEND); else ArraySort(openPos, WHOLE_ARRAY, 0, MODE_DESCEND); for (int i = 0; i < orders; i++) { if (!OrderSelect((int) openPos[i][1], SELECT_BY_TICKET)) { lastError = GetLastError(); Print("Error in OrderSelect: ", GetErrorDescription(lastError)); continue; } orderResponse = CloseOrder(OrderTicket(), OrderLots()); } consecutiveLosses = (position.Profit < 0) ? consecutiveLosses + 1 : 0; SetAggregatePosition(position); Print("ConsecutiveLosses=", consecutiveLosses); return (orderResponse); } bool ActionTrade::AddToCurrentPosition(int type, double lots, int stopLoss, int takeProfit) { if (AccountFreeMarginCheck(_Symbol, type, lots) <= 0) return (false); if (WriteLogFile) logger.WriteLogLine("AddToCurrentPosition: OpenNewPosition"); bool orderResponse = OpenNewPosition(type, lots, stopLoss, takeProfit); if (!orderResponse) return (false); double stopLossPrice = GetStopLossPrice(type, stopLoss); double takeProfitPrice = GetTakeProfitPrice(type, takeProfit); orderResponse = ModifyPosition(stopLossPrice, takeProfitPrice); SetAggregatePosition(position); return (orderResponse); } bool ActionTrade::ReduceCurrentPosition(double lots, int stopLoss, int takeProfit) { int totalOrders = OrdersTotal(); int orders = 0; datetime openPos[][2]; for (int i = 0; i < totalOrders; i++) { if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { lastError = GetLastError(); Print("Error in OrderSelect: ", GetErrorDescription(lastError)); continue; } if (OrderMagicNumber() != ExpertMagic || OrderSymbol() != _Symbol) continue; int orderType = OrderType(); if (orderType != OP_BUY && orderType != OP_SELL) continue; orders++; ArrayResize(openPos, orders); openPos[orders - 1][0] = OrderOpenTime(); openPos[orders - 1][1] = OrderTicket(); } if (FIFOorder) ArraySort(openPos, WHOLE_ARRAY, 0, MODE_ASCEND); else ArraySort(openPos, WHOLE_ARRAY, 0, MODE_DESCEND); for (int i = 0; i < orders; i++) { if (!OrderSelect((int) openPos[i][1], SELECT_BY_TICKET)) { lastError = GetLastError(); Print("Error in OrderSelect: ", GetErrorDescription(lastError)); continue; } double orderLots = (lots >= OrderLots()) ? OrderLots() : lots; CloseOrder(OrderTicket(), orderLots); lots -= orderLots; if (lots <= 0) break; } double stopLossPrice = GetStopLossPrice(position.PosType, stopLoss); double takeProfitPrice = GetTakeProfitPrice(position.PosType, takeProfit); bool orderResponse = ModifyPosition(stopLossPrice, takeProfitPrice); SetAggregatePosition(position); consecutiveLosses = 0; return (orderResponse); } bool ActionTrade::ReverseCurrentPosition(int type, double lots, int stopLoss, int takeProfit) { lots -= position.Lots; bool orderResponse = CloseCurrentPosition(); if (!orderResponse) return (false); orderResponse = OpenNewPosition(type, lots, stopLoss, takeProfit); SetAggregatePosition(position); consecutiveLosses = 0; return (orderResponse); } bool ActionTrade::SendOrder(int type, double lots, int stopLoss, int takeProfit) { bool orderResponse = false; int response = -1; for (int attempt = 0; attempt < TRADE_RETRY_COUNT; attempt++) { if (IsTradeContextFree()) { double orderLots = NormalizeEntrySize(lots); double orderPrice = type == OP_BUY ? MarketInfo(_Symbol, MODE_ASK) : MarketInfo(_Symbol, MODE_BID); double stopLossPrice = GetStopLossPrice(type, stopLoss); double takeProfitPrice = GetTakeProfitPrice(type, takeProfit); color colorDeal = type == OP_BUY ? Lime : Red; string comment = (OrderComment == "") ? "Magic=" + IntegerToString(ExpertMagic) : OrderComment; response = OrderSend(_Symbol, type, orderLots, orderPrice, 100, stopLossPrice, takeProfitPrice, comment, ExpertMagic, 0, colorDeal); lastError = GetLastError(); if (WriteLogFile) logger.WriteLogLine( "SendOrder: " + _Symbol + ", Type=" + (type == OP_BUY ? "Buy" : "Sell") + ", Lots=" + DoubleToString(orderLots, 2) + ", Price=" + DoubleToString(orderPrice, _Digits) + ", StopLoss=" + DoubleToString(stopLossPrice, _Digits) + ", TakeProfit=" + DoubleToString(takeProfitPrice, _Digits) + ", Magic=" + IntegerToString(ExpertMagic) + ", Response=" + IntegerToString(response) + ", LastError=" + IntegerToString(lastError)); } orderResponse = response > 0; if (orderResponse) break; if (lastError != 135 && lastError != 136 && lastError != 137 && lastError != 138) break; Print("Error with SendOrder: ", GetErrorDescription(lastError)); Sleep(TRADE_RETRY_WAIT); } return (orderResponse); } bool ActionTrade::CloseOrder(int orderTicket, double orderLots) { if (!OrderSelect(orderTicket, SELECT_BY_TICKET)) { lastError = GetLastError(); Print("Error with OrderSelect: ", GetErrorDescription(lastError)); return (false); } int orderType = OrderType(); for (int attempt = 0; attempt < TRADE_RETRY_COUNT; attempt++) { bool orderResponse = false; if (IsTradeContextFree()) { double orderPrice = (orderType == OP_BUY) ? MarketInfo(_Symbol, MODE_BID) : MarketInfo(_Symbol, MODE_ASK); orderPrice = NormalizeDouble(orderPrice, Digits); orderResponse = OrderClose(orderTicket, orderLots, orderPrice, 100, Gold); lastError = GetLastError(); if (WriteLogFile) logger.WriteLogLine("OrderClose: " + _Symbol + ", Ticket=" + IntegerToString(orderTicket) + ", Lots=" + DoubleToString(orderLots, 2) + ", Price=" + DoubleToString(orderPrice, _Digits) + ", Response=" + (orderResponse ? "True" : "False") + ", LastError=" + IntegerToString(lastError)); } if (orderResponse) return (true); if (lastError == 4108) return (false); // Invalid ticket error. Print("Error with CloseOrder: ", GetErrorDescription(lastError), ". Attempt No: ", (attempt + 1)); Sleep(TRADE_RETRY_WAIT); } return (false); } bool ActionTrade::ModifyPosition(double stopLossPrice, double takeProfitPrice) { bool orderResponse = true; for (int i = 0; i < OrdersTotal(); i++) { if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { lastError = GetLastError(); Print("Error with OrderSelect: ", GetErrorDescription(lastError)); continue; } if (OrderMagicNumber() != ExpertMagic || OrderSymbol() != _Symbol) continue; int type = OrderType(); if (type != OP_BUY && type != OP_SELL) continue; orderResponse = ModifyOrder(OrderTicket(), stopLossPrice, takeProfitPrice); } return (orderResponse); } bool ActionTrade::ModifyOrder(int orderTicket, double stopLossPrice, double takeProfitPrice) { if (!SelectOrder(orderTicket)) return (false); stopLossPrice = NormalizeEntryPrice(stopLossPrice); takeProfitPrice = NormalizeEntryPrice(takeProfitPrice); double oldStopLoss = NormalizeEntryPrice(OrderStopLoss()); double oldTakeProfit = NormalizeEntryPrice(OrderTakeProfit()); for (int attempt = 0; attempt < TRADE_RETRY_COUNT; attempt++) { if (attempt > 0) { stopLossPrice = CorrectStopLossPrice(OrderType(), stopLossPrice); takeProfitPrice = CorrectTakeProfitPrice(OrderType(), takeProfitPrice); } if (MathAbs(stopLossPrice - oldStopLoss) < pipsValue && MathAbs(takeProfitPrice - oldTakeProfit) < pipsValue) return (true); // There isn't anything to change. bool isSuccess = false; string logline = ""; double orderOpenPrice = 0; if (IsTradeContextFree()) { orderOpenPrice = NormalizeDouble(OrderOpenPrice(), _Digits); isSuccess = OrderModify(orderTicket, orderOpenPrice, stopLossPrice, takeProfitPrice, 0); lastError = GetLastError(); if (WriteLogFile) logline = "ModifyOrder: " + _Symbol + ", Ticket=" + IntegerToString(orderTicket) + ", Price=" + DoubleToString(orderOpenPrice, _Digits) + ", StopLoss=" + DoubleToString(stopLossPrice, _Digits) + ", TakeProfit=" + DoubleToString(takeProfitPrice, _Digits) + ")" + " Magic=" + IntegerToString(ExpertMagic) + ", Response=" + IntegerToString(isSuccess) + ", LastError=" + IntegerToString(lastError); } if (isSuccess) { // Modification was successful. if (WriteLogFile) logger.WriteLogLine(logline); return (true); } else if (lastError == 1) { if (!SelectOrder(orderTicket)) return (false); if (MathAbs(stopLossPrice - OrderStopLoss()) < pipsValue && MathAbs(takeProfitPrice - OrderTakeProfit()) < pipsValue) { if (WriteLogFile) logger.WriteLogLine(logline + ", Checked OK"); lastError = 0; return (true); // We assume that there is no error. } } Print("Error with ModifyOrder(", orderTicket, ", ", orderOpenPrice, ", ", stopLossPrice, ", ", takeProfitPrice, ") ", GetErrorDescription(lastError), "."); Sleep(TRADE_RETRY_WAIT); RefreshRates(); if (lastError == 4108) return (false); // Invalid ticket error. } return (false); } bool ActionTrade::SelectOrder(int orderTicket) { bool orderResponse = OrderSelect(orderTicket, SELECT_BY_TICKET); if (!orderResponse) { lastError = GetLastError(); string message = "Error with OrderSelect(" + IntegerToString(orderTicket) + ")" + ", LastError=" + IntegerToString(lastError) + ", " + GetErrorDescription(lastError); Print(message); if (WriteLogFile) logger.WriteLogLine(message); } return (orderResponse); } double ActionTrade::GetTakeProfitPrice(int type, int takeProfit) { if (takeProfit < epsilon) return (0); if (takeProfit < stopLevel) takeProfit = stopLevel; double takeProfitPrice = (type == OP_BUY) ? MarketInfo(_Symbol, MODE_BID) + takeProfit * _Point : MarketInfo(_Symbol, MODE_ASK) - takeProfit * _Point; return (NormalizeEntryPrice(takeProfitPrice)); } double ActionTrade::GetStopLossPrice(int type, int stopLoss) { if (stopLoss < epsilon) return (0); if (stopLoss < stopLevel) stopLoss = stopLevel; double stopLossPrice = (type == OP_BUY) ? MarketInfo(_Symbol, MODE_BID) - stopLoss * _Point : MarketInfo(_Symbol, MODE_ASK) + stopLoss * _Point; return (NormalizeEntryPrice(stopLossPrice)); } double ActionTrade::CorrectTakeProfitPrice(int type, double takeProfitPrice) { if (takeProfitPrice < epsilon) return (0); double bid = MarketInfo(_Symbol, MODE_BID); double ask = MarketInfo(_Symbol, MODE_ASK); if (type == OP_BUY) { double minTPPrice = bid + stopLevel * _Point; if (takeProfitPrice < minTPPrice) takeProfitPrice = minTPPrice; } else if (type == OP_SELL) { double maxTPPrice = ask - stopLevel * _Point; if (takeProfitPrice > maxTPPrice) takeProfitPrice = maxTPPrice; } return (NormalizeEntryPrice(takeProfitPrice)); } double ActionTrade::CorrectStopLossPrice(int type, double stopLossPrice) { if (stopLossPrice == epsilon) return (0); double bid = MarketInfo(_Symbol, MODE_BID); double ask = MarketInfo(_Symbol, MODE_ASK); if (type == OP_BUY) { double minSLPrice = bid - stopLevel * _Point; if (stopLossPrice > minSLPrice) stopLossPrice = minSLPrice; } else if (type == OP_SELL) { double maxSLPrice = ask + stopLevel * _Point; if (stopLossPrice < maxSLPrice) stopLossPrice = maxSLPrice; } return (NormalizeEntryPrice(stopLossPrice)); } double ActionTrade::NormalizeEntryPrice(double price) { double tickSize = MarketInfo(_Symbol, MODE_TICKSIZE); if (tickSize != 0) return (NormalizeDouble(MathRound(price / tickSize) * tickSize, _Digits)); return (NormalizeDouble(price, _Digits)); } double ActionTrade::NormalizeEntrySize(double size) { double minlot = MarketInfo(_Symbol, MODE_MINLOT); double maxlot = MarketInfo(_Symbol, MODE_MAXLOT); double lotstep = MarketInfo(_Symbol, MODE_LOTSTEP); if (size < minlot - epsilon) return (0); if (MathAbs(size - minlot) < epsilon) return (minlot); int steps = (int) MathRound((size - minlot) / lotstep); size = minlot + steps * lotstep; if (size >= maxlot) size = maxlot; return (size); } void ActionTrade::SetMaxStopLoss() { double bid = MarketInfo(_Symbol, MODE_BID); double ask = MarketInfo(_Symbol, MODE_ASK); double spread = (ask - bid) / _Point; for (int i = 0; i < OrdersTotal(); i++) { if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { lastError = GetLastError(); Print("Error with OrderSelect: ", GetErrorDescription(lastError)); continue; } if (OrderMagicNumber() != ExpertMagic || OrderSymbol() != _Symbol) continue; int type = OrderType(); if (type != OP_BUY && type != OP_SELL) continue; int orderTicket = OrderTicket(); double posOpenPrice = OrderOpenPrice(); double stopLossPrice = OrderStopLoss(); double takeProfitPrice = OrderTakeProfit(); int stopLossPoints = (int) MathRound(MathAbs(posOpenPrice - stopLossPrice) / _Point); if (stopLossPrice < epsilon || stopLossPoints > ProtectionMaxStopLoss + spread) { stopLossPrice = (type == OP_BUY) ? posOpenPrice - _Point * (ProtectionMaxStopLoss + spread) : posOpenPrice + _Point * (ProtectionMaxStopLoss + spread); stopLossPrice = CorrectStopLossPrice(type, stopLossPrice); if (WriteLogFile) logger.WriteLogRequest("SetMaxStopLoss", "StopLoss=" + DoubleToString(stopLossPrice, _Digits)); bool isSuccess = ModifyOrder(orderTicket, stopLossPrice, takeProfitPrice); if (isSuccess) Print("MaxStopLoss(", ProtectionMaxStopLoss, ") set StopLoss to ", DoubleToString(stopLossPrice, _Digits)); } } } void ActionTrade::SetBreakEvenStop() { if (SetAggregatePosition(position) <= 0) return; double breakeven = stopLevel; if (breakeven < breakEven) breakeven = breakEven; double breakprice = 0; // Break Even price including commission. double commission = 0; // Commission in points. if (position.Commission != 0) commission = MathAbs(position.Commission) / MarketInfo(_Symbol, MODE_TICKVALUE); double bid = MarketInfo(_Symbol, MODE_BID); double ask = MarketInfo(_Symbol, MODE_ASK); if (position.PosType == OP_BUY) { breakprice = NormalizeEntryPrice(position.OpenPrice + _Point * commission / position.Lots); if (bid - breakprice >= _Point * breakeven) { if (position.StopLossPrice < breakprice) { if (WriteLogFile) logger.WriteLogRequest("SetBreakEvenStop", "BreakPrice=" + DoubleToString(breakprice, _Digits)); ModifyPosition(breakprice, position.TakeProfitPrice); Print("SetBreakEvenStop(", breakEven, ") set StopLoss to ", DoubleToString(breakprice, _Digits), ", Bid=", DoubleToString(bid, _Digits)); } } } else if (position.PosType == OP_SELL) { breakprice = NormalizeEntryPrice(position.OpenPrice - _Point * commission / position.Lots); if (breakprice - ask >= _Point * breakeven) { if (position.StopLossPrice == 0 || position.StopLossPrice > breakprice) { if (WriteLogFile) logger.WriteLogRequest("SetBreakEvenStop", "BreakPrice=" + DoubleToString(breakprice, _Digits)); ModifyPosition(breakprice, position.TakeProfitPrice); Print("SetBreakEvenStop(", breakEven, ") set StopLoss to ", DoubleToString(breakprice, _Digits), ", Ask=", DoubleToString(ask, _Digits)); } } } } void ActionTrade::SetTrailingStop(bool isNewBar) { bool isCheckTS = true; if (isNewBar) { if (position.PosType == OP_BUY && position.OpenTime > barHighTime) isCheckTS = false; if (position.PosType == OP_SELL && position.OpenTime > barLowTime) isCheckTS = false; barHighTime = Time[0]; barLowTime = Time[0]; barHighPrice = High[0]; barLowPrice = Low[0]; } else { if (High[0] > barHighPrice) { barHighPrice = High[0]; barHighTime = Time[0]; } if (Low[0] < barLowPrice) { barLowPrice = Low[0]; barLowTime = Time[0]; } } if (SetAggregatePosition(position) <= 0) return; if (trailingMode == TrailingStopMode_Tick) SetTrailingStopTickMode(); else if (trailingMode == TrailingStopMode_Bar && isNewBar && isCheckTS) SetTrailingStopBarMode(); } void ActionTrade::SetTrailingStopBarMode() { double bid = MarketInfo(_Symbol, MODE_BID); double ask = MarketInfo(_Symbol, MODE_ASK); if (position.PosType == OP_BUY) { // Long position double stopLossPrice = High[1] - _Point * trailingStop; if (position.StopLossPrice < stopLossPrice - pipsValue) { if (stopLossPrice < bid) { if (stopLossPrice > bid - _Point * stopLevel) stopLossPrice = bid - _Point * stopLevel; if (WriteLogFile) logger.WriteLogRequest("SetTrailingStopBarMode", "StopLoss=" + DoubleToString(stopLossPrice, _Digits)); ModifyPosition(stopLossPrice, position.TakeProfitPrice); Print("Trailing Stop (", trailingStop, ") moved to: ", DoubleToString(stopLossPrice, _Digits), ", Bid=", DoubleToString(bid, _Digits)); } else { if (WriteLogFile) logger.WriteLogRequest("SetTrailingStopBarMode", "StopLoss=" + DoubleToString(stopLossPrice, _Digits)); bool orderResponse = CloseCurrentPosition(); int lastErrorOrdClose = GetLastError(); lastErrorOrdClose = (lastErrorOrdClose > 0) ? lastErrorOrdClose : lastError; if (!orderResponse) Print("Error in OrderClose: ", GetErrorDescription(lastErrorOrdClose)); } } } else if (position.PosType == OP_SELL) { // Short position double stopLossPrice = Low[1] + _Point * trailingStop; if (position.StopLossPrice > stopLossPrice + pipsValue) { if (stopLossPrice > ask) { if (stopLossPrice < ask + _Point * stopLevel) stopLossPrice = ask + _Point * stopLevel; if (WriteLogFile) logger.WriteLogRequest("SetTrailingStopBarMode", "StopLoss=" + DoubleToString(stopLossPrice, _Digits)); ModifyPosition(stopLossPrice, position.TakeProfitPrice); Print("Trailing Stop (", trailingStop, ") moved to: ", DoubleToString(stopLossPrice, _Digits), ", Ask=", DoubleToString(ask, _Digits)); } else { if (WriteLogFile) logger.WriteLogRequest("SetTrailingStopBarMode", "StopLoss=" + DoubleToString(stopLossPrice, _Digits)); bool orderResponse = CloseCurrentPosition(); int lastErrorOrdClose = GetLastError(); lastErrorOrdClose = (lastErrorOrdClose > 0) ? lastErrorOrdClose : lastError; if (!orderResponse) Print("Error in OrderClose: ", GetErrorDescription(lastErrorOrdClose)); } } } } void ActionTrade::SetTrailingStopTickMode() { if (position.PosType == OP_BUY) { // Long position double bid = MarketInfo(_Symbol, MODE_BID); if (bid >= position.OpenPrice + _Point * trailingStop) { if (position.StopLossPrice < bid - _Point * (trailingStop + TrailingStopMovingStep)) { double stopLossPrice = bid - _Point * trailingStop; if (WriteLogFile) logger.WriteLogRequest("SetTrailingStopTickMode", "StopLoss=" + DoubleToString(stopLossPrice, _Digits)); ModifyPosition(stopLossPrice, position.TakeProfitPrice); Print("Trailing Stop (", trailingStop, ") moved to: ", DoubleToString(stopLossPrice, _Digits), ", Bid=", DoubleToString(bid, _Digits)); } } } else if (position.PosType == OP_SELL) { // Short position double ask = MarketInfo(_Symbol, MODE_ASK); if (position.OpenPrice - ask >= _Point * trailingStop) { if (position.StopLossPrice > ask + _Point * (trailingStop + TrailingStopMovingStep)) { double stopLossPrice = ask + _Point * trailingStop; if (WriteLogFile) logger.WriteLogRequest("SetTrailingStopTickMode", "StopLoss=" + DoubleToString(stopLossPrice, _Digits)); ModifyPosition(stopLossPrice, position.TakeProfitPrice); Print("Trailing Stop (", trailingStop, ") moved to: ", DoubleToString(stopLossPrice, _Digits), ", Ask=", DoubleToString(ask, _Digits)); } } } } void ActionTrade::DetectPositionClosing() { double oldStopLoss = position.StopLossPrice; double oldTakeProfit = position.TakeProfitPrice; double oldProfit = position.Profit; int oldType = position.PosType; double oldLots = position.Lots; SetAggregatePosition(position); if (oldType == OP_FLAT || position.PosType != OP_FLAT) return; double closePrice = (oldType == OP_BUY) ? dataMarket.Bid : dataMarket.Ask; string stopMessage = "Position was closed"; string closePriceText = DoubleToString(closePrice, _Digits); if (MathAbs(oldStopLoss - closePrice) < 2 * pipsValue) stopMessage = "Activated StopLoss=" + closePriceText; else if (MathAbs(oldTakeProfit - closePrice) < 2 * pipsValue) stopMessage = "Activated TakeProfit=" + closePriceText; consecutiveLosses = (oldProfit < 0) ? consecutiveLosses + 1 : 0; string message = stopMessage + ", ClosePrice=" + closePriceText + ", ClosedLots= " + DoubleToString(oldLots, 2) + ", Profit=" + DoubleToString(oldProfit, 2) + ", ConsecutiveLosses=" + IntegerToString(consecutiveLosses); if (WriteLogFile) logger.WriteNewLogLine(message); Print(message); } bool ActionTrade::IsTradeContextFree() { if (IsTradeAllowed()) return (true); uint startWait = GetTickCount(); Print("Trade context is busy! Waiting..."); while (true) { if (IsStopped()) return (false); uint diff = GetTickCount() - startWait; if (diff > 30 * 1000) { Print("The waiting limit exceeded!"); return (false); } if (IsTradeAllowed()) { RefreshRates(); return (true); } Sleep(100); } return (true); } void ActionTrade::ActivateProtectionMinAccount() { CloseCurrentPosition(); string account = DoubleToString(AccountEquity(), 2); string message = "\n" + "The account equity (" + account + ") dropped below the minimum allowed (" + IntegerToString(ProtectionMinAccount) + ")."; Comment(message); Print(message); if (WriteLogFile) logger.WriteLogLine(message); Sleep(20 * 1000); CloseExpert(); } void ActionTrade::CloseExpert(void) { ExpertRemove(); OnDeinit(0); } ActionTrade* actionTrade; int OnInit() { actionTrade = new ActionTrade(); actionTrade.EntryAmount = Entry_Amount > 77700 ? 0.1 : Entry_Amount; actionTrade.MaximumAmount = Maximum_Amount > 77700 ? 0.1 : Maximum_Amount; actionTrade.AddingAmount = Adding_Amount > 77700 ? 0.1 : Adding_Amount; actionTrade.ReducingAmount = Reducing_Amount > 77700 ? 0.1 : Reducing_Amount; actionTrade.OrderComment = Order_Comment; actionTrade.MinDataBars = Min_Data_Bars; actionTrade.ProtectionMinAccount = Protection_Min_Account; actionTrade.ProtectionMaxStopLoss = Protection_Max_StopLoss; actionTrade.ExpertMagic = Expert_Magic; actionTrade.SeparateSLTP = Separate_SL_TP; actionTrade.WriteLogFile = Write_Log_File; actionTrade.TrailingStopMovingStep = TrailingStop_Moving_Step; actionTrade.FIFOorder = FIFO_order; actionTrade.MaxLogLinesInFile = Max_Log_Lines_in_File; actionTrade.BarCloseAdvance = Bar_Close_Advance; int result = actionTrade.OnInit(); if (result == INIT_SUCCEEDED) actionTrade.OnTick(); return (result); } void OnTick() { if (__symbol != _Symbol || __period != _Period) { if(__period > 0) { actionTrade.OnDeinit(-1); actionTrade.OnInit(); } __symbol = _Symbol; __period = _Period; } actionTrade.OnTick(); } void OnDeinit(const int reason) { actionTrade.OnDeinit(reason); if (CheckPointer(actionTrade) == POINTER_DYNAMIC) delete actionTrade; }