//+------------------------------------------------------------------+ //| Expert.mqh | //| Copyright 2000-2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include #include "ExpertSignalCustom.mqh" #include "..\System\TradeChecks.mqh" //--- Article 2555 #16: a soft budget for one OnTick() pass, in microseconds. The article's own //--- yardstick is that the FIRST (heaviest) calculation over 10+ years of M1 data should stay //--- under 100 ms; a steady-state tick that regularly costs more than that is starving every other //--- program sharing this symbol's single terminal thread, so it gets a throttled journal warning //--- pointing at the MetaEditor profiler. Diagnostic only - it never skips or truncates any work. #define EXPERT_TICK_BUDGET_US 100000 //--- Article 2555 #16: soft ceiling, in MB, on MQL_MEMORY_USED before a throttled warning is logged. //--- Sized well above what the largest shipped AI topology needs (3 models x live+shadow net under //--- HYBRID) so it flags a genuine leak/runaway allocation, not ordinary training memory. #define EXPERT_MEMORY_SOFT_LIMIT_MB 1024 //+------------------------------------------------------------------+ //| Class CExpert. | //| Purpose: Base class expert advisor. | //| Derives from class CExpert. | //+------------------------------------------------------------------+ class CExpertCustom : public CExpert { protected: CExpertSignalCustom* GetCustomSignal() { return dynamic_cast(m_signal); } bool CloseAndDeleteAllForSymbol(void); //--- Article 2555 #7 - SYMBOL_TRADE_FREEZE_LEVEL. While the market sits within freeze-level //--- points of a position's SL/TP (or of a pending order's activation price) the server refuses //--- to modify, close or delete it, and answering that refusal with a request anyway is exactly //--- the error the article says a Market product must never produce. These two report on the //--- CURRENTLY SELECTED m_position / m_order, which is the state every caller below runs in. bool SelectedPositionIsFrozen(void); bool SelectedOrderIsFrozen(void); //--- Mirrors CExpertTrade::Buy()/Sell()'s price-vs-stops-level routing, so the checks below know //--- whether a given entry price is about to become a pending order or a market fill. ENUM_ORDER_TYPE ResolveOrderType(bool isLong, double price); public: CExpertCustom(void); ~CExpertCustom(void); //--- event handlers virtual void OnTick(void) override; virtual void OnTimer(void) override; virtual void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) override; //--- initialization trading objects virtual bool InitSignal(CExpertSignal *signal = NULL) override; //--- methods of creating the indicator and timeseries virtual bool SetPriceSeries(CiOpen *open, CiHigh *high, CiLow *low, CiClose *close) override; virtual bool SetOtherSeries(CiSpread *spread, CiTime *time, CiTickVolume *tick_volume, CiRealVolume *real_volume) override; //--- initialization trading objects virtual bool InitTrade(ulong magic, CExpertTrade *trade = NULL) override; protected: //--- refreshing virtual bool Refresh(void) override; //--- processing (main method) virtual bool Processing(void) override; //--- Article 2555 #2/#3/#4/#5/#6 - final gate immediately before the order is sent. Everything //--- here was already validated when the setup was shaped (CExpertSignalCustom::OpenParams) and //--- when the lot was sized (CExpertMoneyCustom::ValidateLotForTrade), but those ran earlier in //--- the tick and the market has moved since: a price that was a legal pending distance then can //--- be inside the stops level now. This is the last point at which a bad request can still be //--- withheld rather than rejected by the server. virtual bool OpenLong(double price, double sl, double tp) override; virtual bool OpenShort(double price, double sl, double tp) override; //--- Article 2555 #7 - never send a close/reverse request for a frozen position. virtual bool CheckClose(void) override; virtual bool CheckReverse(void) override; //--- Article 2555 #7/#11 - never send a modification for a frozen position, and never send one //--- that changes nothing (TRADE_RETCODE_NO_CHANGES = 10025). virtual bool CheckTrailingStop(void) override; virtual bool TrailingStopLong(double sl, double tp) override; virtual bool TrailingStopShort(double sl, double tp) override; //--- Article 2555 #7/#11 - the same two rules for pending orders. virtual bool CheckDeleteOrderLong(void) override; virtual bool CheckDeleteOrderShort(void) override; virtual bool CheckTrailingOrderLong(void) override; virtual bool CheckTrailingOrderShort(void) override; virtual bool TrailingOrderLong(double delta) override; virtual bool TrailingOrderShort(double delta) override; }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CExpertCustom::CExpertCustom(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CExpertCustom::~CExpertCustom(void) { } //+------------------------------------------------------------------+ //| Setting pointers of price timeseries. | //+------------------------------------------------------------------+ bool CExpertCustom::SetPriceSeries(CiOpen *open, CiHigh *high, CiLow *low, CiClose *close) override { //--- check the initialization phase if(m_init_phase != INIT_PHASE_VALIDATION) { return(false); } //--- check pointers if((IS_OPEN_SERIES_USAGE && open == NULL) || (IS_HIGH_SERIES_USAGE && high == NULL) || (IS_LOW_SERIES_USAGE && low == NULL) || (IS_CLOSE_SERIES_USAGE && close == NULL)) { Print(__FUNCTION__ + ": NULL pointer"); return(false); } m_open = open; m_high = high; m_low = low; m_close = close; //--- ok return(true); } //+------------------------------------------------------------------+ //| Setting pointers of other timeseries. | //+------------------------------------------------------------------+ bool CExpertCustom::SetOtherSeries(CiSpread *spread, CiTime *time, CiTickVolume *tick_volume, CiRealVolume *real_volume) override { //--- check the initialization phase if(m_init_phase != INIT_PHASE_VALIDATION) { return(false); } //--- check pointers if((IS_SPREAD_SERIES_USAGE && spread == NULL) || (IS_TIME_SERIES_USAGE && time == NULL) || (IS_TICK_VOLUME_SERIES_USAGE && tick_volume == NULL) || (IS_REAL_VOLUME_SERIES_USAGE && real_volume == NULL)) { Print(__FUNCTION__ + ": NULL pointer"); return(false); } m_spread = spread; m_time = time; m_tick_volume = tick_volume; m_real_volume = real_volume; //--- ok return(true); } //+------------------------------------------------------------------+ //| Initialization signal object | //+------------------------------------------------------------------+ bool CExpertCustom::InitSignal(CExpertSignal *signal) { if(m_signal != NULL) delete m_signal; //--- if(signal == NULL) { if((m_signal = new CExpertSignalCustom) == NULL) return(false); } else m_signal = signal; //--- initializing signal object if(!m_signal.Init(GetPointer(m_symbol), m_period, m_adjusted_point)) return(false); m_signal.EveryTick(m_every_tick); m_signal.Magic(m_magic); //--- ok return(true); } //+------------------------------------------------------------------+ //| Refreshing data for processing | //+------------------------------------------------------------------+ bool CExpertCustom::Refresh(void) { MqlDateTime time; //--- Article 2555 #8 - errors caused by insufficient quote history. Everything below this point //--- (RefreshRates, then every indicator in m_indicators) reads a timeseries that the terminal may //--- still be downloading: on a symbol/timeframe just switched to, on a thin instrument, or right //--- after reconnecting, the series reports far fewer bars than the indicators' warm-up needs and //--- the buffers come back zero-filled rather than empty. Skipping the tick until the series is //--- synchronised is the article's "check for the required history and request the missing data" //--- approach - touching the series here is itself what asks the terminal to build it. string history_reason; if(!TCHasEnoughHistory(m_symbol.Name(), m_period, 2, history_reason)) { TCLog("refresh-history:" + m_symbol.Name(), __FUNCTION__ + ": skipping tick - " + history_reason); return(false); } //--- refresh rates if(!m_symbol.RefreshRates()) return(false); //--- check need processing TimeToStruct(m_symbol.Time(), time); if(m_period_flags != WRONG_VALUE && m_period_flags != 0) if((m_period_flags & TimeframesFlags(time)) == 0) return(false); m_last_tick_time = time; //--- refresh indicators m_indicators.Refresh(); //--- ok return(true); } //+------------------------------------------------------------------+ //| Main function | //+------------------------------------------------------------------+ bool CExpertCustom::Processing(void) { //--- calculate signal direction once m_signal.SetDirection(); //--- check if open positions if(SelectPosition()) { //--- open position is available //--- check the possibility of reverse the position if(CheckReverse()) return(true); //--- check the possibility of closing the position/delete pending orders if(!CheckClose()) { //--- check the possibility of modifying the position if(CheckTrailingStop()) return(true); //--- return without operations return(false); } } //--- check if plased pending orders int total = OrdersTotal(); if(total != 0) { for(int i = total - 1; i >= 0; i--) { m_order.SelectByIndex(i); if(m_order.Symbol() != m_symbol.Name()) continue; if(m_order.OrderType() == ORDER_TYPE_BUY_LIMIT || m_order.OrderType() == ORDER_TYPE_BUY_STOP) { //--- check the ability to delete a pending order to buy if(CheckDeleteOrderLong()) return(true); //--- check the possibility of modifying a pending order to buy if(CheckTrailingOrderLong()) return(true); } else { //--- check the ability to delete a pending order to sell if(CheckDeleteOrderShort()) return(true); //--- check the possibility of modifying a pending order to sell if(CheckTrailingOrderShort()) return(true); } //--- return without operations return(false); } } //--- check the possibility of opening a position/setting pending order if(CheckOpen()) return(true); //--- return without operations return(false); } //+------------------------------------------------------------------+ //| Article 2555 #7 - is the SELECTED position inside the broker's | //| freeze zone? A frozen position cannot be modified or closed. | //+------------------------------------------------------------------+ bool CExpertCustom::SelectedPositionIsFrozen(void) { string reason; if(TCFreezeOkForPosition(m_position.Symbol(), (ENUM_POSITION_TYPE)m_position.PositionType(), m_position.StopLoss(), m_position.TakeProfit(), reason)) return false; TCLog("frozen-position:" + m_position.Symbol(), __FUNCTION__ + ": " + reason + " - request withheld"); return true; } //+------------------------------------------------------------------+ //| Article 2555 #7 - is the SELECTED pending order inside the | //| broker's freeze zone? A frozen order cannot be modified or deleted.| //+------------------------------------------------------------------+ bool CExpertCustom::SelectedOrderIsFrozen(void) { string reason; if(TCFreezeOkForOrder(m_order.Symbol(), (ENUM_ORDER_TYPE)m_order.OrderType(), m_order.PriceOpen(), reason)) return false; TCLog("frozen-order:" + m_order.Symbol(), __FUNCTION__ + ": " + reason + " - request withheld"); return true; } //+------------------------------------------------------------------+ //| Which order type an entry price will produce - see | //| CExpertTrade::Buy()/Sell(), whose routing this reproduces. | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE CExpertCustom::ResolveOrderType(bool isLong, double price) { if(price <= 0.0 || price == EMPTY_VALUE) return(isLong ? ORDER_TYPE_BUY : ORDER_TYPE_SELL); double stops = TCStopsLevel(m_symbol.Name()); if(isLong) { double ask = m_symbol.Ask(); if(price > ask + stops) return(ORDER_TYPE_BUY_STOP); if(price < ask - stops) return(ORDER_TYPE_BUY_LIMIT); return(ORDER_TYPE_BUY); } double bid = m_symbol.Bid(); if(price > bid + stops) return(ORDER_TYPE_SELL_LIMIT); if(price < bid - stops) return(ORDER_TYPE_SELL_STOP); return(ORDER_TYPE_SELL); } //+------------------------------------------------------------------+ //| Final pre-send gate for a long entry (article #4/#6/#14). | //+------------------------------------------------------------------+ bool CExpertCustom::OpenLong(double price, double sl, double tp) { if(price == EMPTY_VALUE) return(false); string reason; //--- #14: symbol-property reads below are meaningless for an unquoted symbol if(!TCSymbolIsTradeable(m_symbol.Name(), reason)) { TCLog("open-symbol:" + m_symbol.Name(), __FUNCTION__ + ": withheld - " + reason); return(false); } ENUM_ORDER_TYPE type = ResolveOrderType(true, price); if(type != ORDER_TYPE_BUY) { //--- #4: the account's ACCOUNT_LIMIT_ORDERS cap applies to pending orders only if(!TCIsNewOrderAllowed(reason)) { TCLog("open-orderlimit", __FUNCTION__ + ": withheld - " + reason); return(false); } //--- #6: the activation price must still clear the stops level after the price moved if(!TCCheckPendingPrice(m_symbol.Name(), type, price, reason)) { TCLog("open-pendingprice:" + m_symbol.Name(), __FUNCTION__ + ": withheld - " + reason); return(false); } } //--- #6: SL/TP against the stops level, measured from this order type's own reference price if(!TCCheckStops(m_symbol.Name(), type, price, sl, tp, reason)) { TCLog("open-stops:" + m_symbol.Name(), __FUNCTION__ + ": withheld - " + reason); return(false); } return(CExpert::OpenLong(price, sl, tp)); } //+------------------------------------------------------------------+ //| Final pre-send gate for a short entry (article #4/#6/#14). | //+------------------------------------------------------------------+ bool CExpertCustom::OpenShort(double price, double sl, double tp) { if(price == EMPTY_VALUE) return(false); string reason; if(!TCSymbolIsTradeable(m_symbol.Name(), reason)) { TCLog("open-symbol:" + m_symbol.Name(), __FUNCTION__ + ": withheld - " + reason); return(false); } ENUM_ORDER_TYPE type = ResolveOrderType(false, price); if(type != ORDER_TYPE_SELL) { if(!TCIsNewOrderAllowed(reason)) { TCLog("open-orderlimit", __FUNCTION__ + ": withheld - " + reason); return(false); } if(!TCCheckPendingPrice(m_symbol.Name(), type, price, reason)) { TCLog("open-pendingprice:" + m_symbol.Name(), __FUNCTION__ + ": withheld - " + reason); return(false); } } if(!TCCheckStops(m_symbol.Name(), type, price, sl, tp, reason)) { TCLog("open-stops:" + m_symbol.Name(), __FUNCTION__ + ": withheld - " + reason); return(false); } return(CExpert::OpenShort(price, sl, tp)); } //+------------------------------------------------------------------+ //| Article #7 - do not attempt to close a frozen position. | //+------------------------------------------------------------------+ bool CExpertCustom::CheckClose(void) { if(SelectedPositionIsFrozen()) return(false); return(CExpert::CheckClose()); } //+------------------------------------------------------------------+ //| Article #7 - a reverse closes the open position first, so the | //| same freeze restriction applies to it. | //+------------------------------------------------------------------+ bool CExpertCustom::CheckReverse(void) { if(SelectedPositionIsFrozen()) return(false); return(CExpert::CheckReverse()); } //+------------------------------------------------------------------+ //| Article #7 - do not attempt to modify a frozen position. | //+------------------------------------------------------------------+ bool CExpertCustom::CheckTrailingStop(void) { if(SelectedPositionIsFrozen()) return(false); return(CExpert::CheckTrailingStop()); } //+------------------------------------------------------------------+ //| Article #11 - a PositionModify() that changes nothing is an error | //| (TRADE_RETCODE_NO_CHANGES = 10025). CExpert's own caller compares | //| the new levels for EXACT equality, which a recomputed double can | //| miss by one ulp and still be "no change" to the server; compare | //| with the article's one-point tolerance instead. | //+------------------------------------------------------------------+ bool CExpertCustom::TrailingStopLong(double sl, double tp) { if(!TCPositionModifyIsMeaningful(m_position.Symbol(), m_position.StopLoss(), sl, m_position.TakeProfit(), tp)) return(false); //--- the new levels are themselves subject to the stops level (article #6) string reason; if(!TCCheckStops(m_position.Symbol(), ORDER_TYPE_BUY, 0.0, sl, tp, reason)) { TCLog("trail-stops-long:" + m_position.Symbol(), __FUNCTION__ + ": withheld - " + reason); return(false); } return(CExpert::TrailingStopLong(sl, tp)); } //+------------------------------------------------------------------+ //| See TrailingStopLong() - same two rules for a short position. | //+------------------------------------------------------------------+ bool CExpertCustom::TrailingStopShort(double sl, double tp) { if(!TCPositionModifyIsMeaningful(m_position.Symbol(), m_position.StopLoss(), sl, m_position.TakeProfit(), tp)) return(false); string reason; if(!TCCheckStops(m_position.Symbol(), ORDER_TYPE_SELL, 0.0, sl, tp, reason)) { TCLog("trail-stops-short:" + m_position.Symbol(), __FUNCTION__ + ": withheld - " + reason); return(false); } return(CExpert::TrailingStopShort(sl, tp)); } //+------------------------------------------------------------------+ //| Article #7 - a pending order inside the freeze zone cannot be | //| deleted; withhold the request instead of having it rejected. | //+------------------------------------------------------------------+ bool CExpertCustom::CheckDeleteOrderLong(void) { if(SelectedOrderIsFrozen()) return(false); return(CExpert::CheckDeleteOrderLong()); } bool CExpertCustom::CheckDeleteOrderShort(void) { if(SelectedOrderIsFrozen()) return(false); return(CExpert::CheckDeleteOrderShort()); } //+------------------------------------------------------------------+ //| Article #7 - nor can it be modified. | //+------------------------------------------------------------------+ bool CExpertCustom::CheckTrailingOrderLong(void) { if(SelectedOrderIsFrozen()) return(false); return(CExpert::CheckTrailingOrderLong()); } bool CExpertCustom::CheckTrailingOrderShort(void) { if(SelectedOrderIsFrozen()) return(false); return(CExpert::CheckTrailingOrderShort()); } //+------------------------------------------------------------------+ //| Article #6/#11 - the shifted order must still change something, | //| and its new activation price plus SL/TP must clear the stops | //| level. CExpert::TrailingOrder*() shifts price, sl and tp by the | //| same delta and sends the modification unconditionally, so a | //| delta of 0 (or one that walks the order inside the stops level) | //| produced a guaranteed-rejected request. | //+------------------------------------------------------------------+ bool CExpertCustom::TrailingOrderLong(double delta) { string symbol = m_order.Symbol(); ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)m_order.OrderType(); double price = m_symbol.NormalizePrice(m_order.PriceOpen() - delta); double sl = m_symbol.NormalizePrice(m_order.StopLoss() - delta); double tp = m_symbol.NormalizePrice(m_order.TakeProfit() - delta); if(!TCOrderModifyIsMeaningful(symbol, m_order.PriceOpen(), price, m_order.StopLoss(), sl, m_order.TakeProfit(), tp)) return(false); string reason; if(!TCCheckPendingPrice(symbol, type, price, reason) || !TCCheckStops(symbol, type, price, sl, tp, reason)) { TCLog("trail-order-long:" + symbol, __FUNCTION__ + ": withheld - " + reason); return(false); } return(CExpert::TrailingOrderLong(delta)); } bool CExpertCustom::TrailingOrderShort(double delta) { string symbol = m_order.Symbol(); ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)m_order.OrderType(); double price = m_symbol.NormalizePrice(m_order.PriceOpen() - delta); double sl = m_symbol.NormalizePrice(m_order.StopLoss() - delta); double tp = m_symbol.NormalizePrice(m_order.TakeProfit() - delta); if(!TCOrderModifyIsMeaningful(symbol, m_order.PriceOpen(), price, m_order.StopLoss(), sl, m_order.TakeProfit(), tp)) return(false); string reason; if(!TCCheckPendingPrice(symbol, type, price, reason) || !TCCheckStops(symbol, type, price, sl, tp, reason)) { TCLog("trail-order-short:" + symbol, __FUNCTION__ + ": withheld - " + reason); return(false); } return(CExpert::TrailingOrderShort(delta)); } //+------------------------------------------------------------------+ //| OnTick handler | //+------------------------------------------------------------------+ void CExpertCustom::OnTick(void) { //--- check process flag if(!m_on_tick_process) return; //--- Article 2555 #16 - measure the tick with GetMicrosecondCount() and report (throttled) when it //--- overruns EXPERT_TICK_BUDGET_US. Purely diagnostic; see the macro's comment. ulong tick_started_us = TCNow(); //--- close positions and orders at specified time if(targetDayOfWeek != -1 && targetMinutes != -1 && targetHour != -1) { // Buffer in minutes for checking the condition int bufferMinutes = 1; // ±1 minute buffer // Get current server time datetime currentTimeValue = TimeCurrent(); MqlDateTime currentTime; TimeToStruct(currentTimeValue, currentTime); // Convert to MqlDateTime // Extract the current hour and minute int currentHour = currentTime.hour; int currentMinute = currentTime.min; // Check if the current day matches the target day if(currentTime.day_of_week == targetDayOfWeek || targetDayOfWeek == CLOSE_EVERYDAY) { // Check if the current time is within the buffer range around the target time if(currentHour == targetHour) { // Check if the current minute falls within the ±1 minute buffer of the target minute if(currentMinute >= (targetMinutes - bufferMinutes) && currentMinute <= (targetMinutes + bufferMinutes)) { // If it matches, call CloseAndDeleteAllForSymbol if(CloseAndDeleteAllForSymbol()) { // Log or handle the successful close Print("Positions and orders closed."); } } } } } CExpertSignalCustom* customSignal = GetCustomSignal(); if(customSignal != NULL) customSignal.OnTickHandler(); //--- updated quotes and indicators if(!Refresh()) { if(VerboseMode) TCWarnIfSlow("CExpertCustom::OnTick", tick_started_us, EXPERT_TICK_BUDGET_US); return; } //--- expert processing Processing(); if(VerboseMode) TCWarnIfSlow("CExpertCustom::OnTick", tick_started_us, EXPERT_TICK_BUDGET_US); } //+------------------------------------------------------------------+ //| OnChartEvent handler | //+------------------------------------------------------------------+ void CExpertCustom::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- check process flag if(!m_on_chart_event_process) return; CExpertSignalCustom* customSignal = GetCustomSignal(); if(customSignal != NULL) customSignal.OnChartEventHandler(id, lparam, dparam, sparam); } //+------------------------------------------------------------------+ //| Close all positions and delete all pending orders for Symbol() | //+------------------------------------------------------------------+ bool CExpertCustom::CloseAndDeleteAllForSymbol() { bool result = false; // Track if any action was successfully performed string symbol = m_symbol.Name(); string reason; // Iterate through all positions. The symbol/magic filter is read off the TICKET being closed: // PositionSelect(_Symbol) selects "some position on the chart symbol" and says nothing about // positionTicket, so the guard it used to stand in for let this close every position on the // account - including other symbols' and other EAs' - the moment the chart symbol happened to // hold one. Same for the order loop below, whose OrderSelect() succeeded for any ticket at all. for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong positionTicket = PositionGetTicket(i); if(positionTicket == 0 || !PositionSelectByTicket(positionTicket)) continue; if(PositionGetString(POSITION_SYMBOL) != symbol) continue; if(PositionGetInteger(POSITION_MAGIC) != (long)m_magic) continue; //--- article 2555 #7: a frozen position cannot be closed; retry on the next timer/tick pass if(!TCFreezeOkForPosition(symbol, (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE), PositionGetDouble(POSITION_SL), PositionGetDouble(POSITION_TP), reason)) { TCLog("closeall-frozen-position:" + symbol, __FUNCTION__ + ": " + reason + " - close deferred"); continue; } if(m_trade.PositionClose(positionTicket)) result = true; } // Iterate through all pending orders for(int j = OrdersTotal() - 1; j >= 0; j--) { ulong orderTicket = OrderGetTicket(j); if(orderTicket == 0) continue; if(OrderGetString(ORDER_SYMBOL) != symbol) continue; if(OrderGetInteger(ORDER_MAGIC) != (long)m_magic) continue; //--- article 2555 #7: nor can a frozen pending order be deleted if(!TCFreezeOkForOrder(symbol, (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE), OrderGetDouble(ORDER_PRICE_OPEN), reason)) { TCLog("closeall-frozen-order:" + symbol, __FUNCTION__ + ": " + reason + " - delete deferred"); continue; } if(m_trade.OrderDelete(orderTicket)) result = true; } return result; // Return true if any action was performed, false otherwise } //+------------------------------------------------------------------+ //| OnTimer handler | //+------------------------------------------------------------------+ void CExpertCustom::OnTimer(void) { //--- check process flag if(!m_on_timer_process) return; //--- Article 2555 #16 - keep an eye on the memory the program holds. The AI signals allocate the //--- bulk of it (network weights, replay buffers), so the timer - not the tick - is the right place //--- to sample it. Throttled and diagnostic only; nothing is freed or truncated on the back of it. if(VerboseMode) TCWarnIfMemoryAbove(EXPERT_MEMORY_SOFT_LIMIT_MB); CExpertSignalCustom* customSignal = GetCustomSignal(); if(customSignal == NULL) return; if(!dbm.OpenDatabase()) { Print(__FUNCTION__ + ": Failed to open database, skipping this timer cycle's signal processing/weight update."); return; } customSignal.ProcessBufferedSignals(); if(!customSignal.UpdateSignalsWeights()) Print(__FUNCTION__ + ": UpdateSignalsWeights failed, DB-derived filter weights not refreshed this cycle."); // This function owns the connection it opened above - ProcessBufferedSignals() deliberately // leaves it open so UpdateSignalsWeights() can reuse the same handle. if(!IsBacktesting) dbm.CloseDatabase(); } //+------------------------------------------------------------------+ //| Initialization trade object | //+------------------------------------------------------------------+ bool CExpertCustom::InitTrade(ulong magic, CExpertTrade *trade = NULL) { if(m_trade != NULL) delete m_trade; //--- if(trade == NULL) { if((m_trade = new CExpertTrade) == NULL) return(false); } else m_trade = trade; //--- tune trade object m_trade.SetSymbol(GetPointer(m_symbol)); m_trade.SetExpertMagicNumber(magic); m_trade.SetMarginMode(); m_trade.SetAsyncMode(true); //--- set default deviation for trading in adjusted points m_trade.SetDeviationInPoints((ulong)(3 * m_adjusted_point / m_symbol.Point())); //--- ok return(true); } //+------------------------------------------------------------------+