Warrior_EA/Expert/ExpertCustom.mqh
AnimateDread 061c3d1c8b feat: suppress noisy performance warnings unless VerboseMode
Wrap the `TCWarnIfSlow` calls in `CExpertCustom::OnTick` and the
`TCWarnIfMemoryAbove` call in `CExpertCustom::OnTimer` with `VerboseMode`
guards. This silences routine diagnostic noise during normal operation
while still allowing detailed performance tracing when `VerboseMode`
is enabled.
2026-07-27 10:58:29 -04:00

701 lines
30 KiB
MQL5

//+------------------------------------------------------------------+
//| Expert.mqh |
//| Copyright 2000-2023, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Expert\Expert.mqh>
#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<CExpertSignalCustom*>(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);
}
//+------------------------------------------------------------------+