Files
TOL-LANGIT-Neural-Quant-Adv…/TOL LANGIT Neural Quant Advisor.mq5
T

293 lines
22 KiB
Plaintext

//+------------------------------------------------------------------+
//| TOL LANGIT Neural Quant Advisor.mq5 |
//| Copyright 2026, Adithyo Dewangga Wijaya |
//| https://www.mql5.com/en/users/adithyodw |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Adithyo Dewangga Wijaya"
#property link "https://www.mql5.com/en/users/adithyodw"
#property version "28.00"
#property strict
#property description "Institutional Gold System with Neural-Inspired Regime Detection and Volatility Filters"
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>
//--- INPUTS
input string s0 = "======= TRADING HOURS (GMT) =======";
input int InpLondonOpen = 8; // London open hour
input int InpSessionEnd = 18; // Session end hour
input string s1 = "======= INSTITUTIONAL SETUP =======";
input int InpEMA = 200; // EMA period for macro trend
input double InpATR_Mult = 3.5; // SuperTrend ATR multiplier
input int InpADX_Period = 14; // ADX period
input double InpADX_Thresh = 25.0;// ADX threshold for trending regime
input string s2 = "======= RISK MANAGEMENT =======";
input double InpRiskPercent = 0.25; // Risk per trade (%)
input double InpSL_Mult = 2.0; // SL ATR multiplier
input double InpTP_Mult = 3.0; // TP ATR multiplier
input double InpMaxSpread = 0.5; // Max allowed spread (in price units)
input double InpDailyLossPercent = 1.0; // Daily loss limit (%)
input int InpMaxTradesDay = 5; // Max trades per day
input double InpMaxDDPercent = 5.0; // Max drawdown percent for equity stop
input long InpMagic = 123456; // Magic number
//--- Globals
CTrade m_trade;
CPositionInfo m_pos;
CSymbolInfo m_sym;
int hEMA = INVALID_HANDLE;
int hATR = INVALID_HANDLE;
int hADX = INVALID_HANDLE;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
if (!m_sym.Name(_Symbol)) return(INIT_FAILED);
m_trade.SetExpertMagicNumber(InpMagic);
// Load indicator handles
hEMA = iMA(_Symbol, _Period, InpEMA, 0, MODE_EMA, PRICE_CLOSE);
hATR = iATR(_Symbol, _Period, 14);
hADX = iADX(_Symbol, _Period, InpADX_Period);
if (hEMA == INVALID_HANDLE || hATR == INVALID_HANDLE || hADX == INVALID_HANDLE) {
Print("Failed to initialize indicators");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
if (hEMA != INVALID_HANDLE) IndicatorRelease(hEMA);
if (hATR != INVALID_HANDLE) IndicatorRelease(hATR);
if (hADX != INVALID_HANDLE) IndicatorRelease(hADX);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
if (!m_sym.RefreshRates()) return;
if (Bars(_Symbol, _Period) < InpEMA) return;
// Spread filter
double spread = m_sym.Ask() - m_sym.Bid();
if (spread > InpMaxSpread) return;
// Session filter
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
bool isTradeTime = (dt.hour >= InpLondonOpen && dt.hour <= InpSessionEnd);
if (!isTradeTime) return;
// Equity stop protection
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
if (equity < balance * (1 - InpMaxDDPercent / 100)) {
CloseAllPositions();
ExpertRemove();
return;
}
// Daily loss limit
double daily_profit = GetDailyProfit();
double daily_loss_limit = equity * InpDailyLossPercent / 100;
if (daily_profit < -daily_loss_limit) return;
// Max trades per day
int trades_today = GetTradesToday();
if (trades_today >= InpMaxTradesDay) return;
// Get indicator values
double ema[1], atr[1], adx[1], close[1];
ArraySetAsSeries(ema, true); ArraySetAsSeries(atr, true); ArraySetAsSeries(adx, true); ArraySetAsSeries(close, true);
if (CopyBuffer(hEMA, 0, 0, 1, ema) < 1 ||
CopyBuffer(hATR, 0, 0, 1, atr) < 1 ||
CopyBuffer(hADX, 0, 0, 1, adx) < 1 ||
CopyClose(_Symbol, _Period, 0, 1, close) < 1) return;
// SuperTrend bands using previous bar (non-repainting)
double atr1[1];
ArraySetAsSeries(atr1, true);
if (CopyBuffer(hATR, 0, 1, 1, atr1) < 1) return;
double mid1 = (iHigh(_Symbol, _Period, 1) + iLow(_Symbol, _Period, 1)) / 2.0;
double upB = mid1 + (InpATR_Mult * atr1[0]);
double dnB = mid1 - (InpATR_Mult * atr1[0]);
// Signal logic with regime filter
int signal = 0;
if (adx[0] > InpADX_Thresh) {
if (close[0] > ema[0] && close[0] > dnB) signal = 1;
if (close[0] < ema[0] && close[0] < upB) signal = -1;
}
// Execute trade
ExecuteTrade(signal, atr[0]);
}
//+------------------------------------------------------------------+
//| Trade execution function |
//+------------------------------------------------------------------+
void ExecuteTrade(int signal, double current_atr) {
int current_dir = GetCurrentDirection();
if (signal != 0 && current_dir == -signal) {
CloseAllPositions();
}
if (signal != 0 && current_dir == 0) {
double sl_dist = current_atr * InpSL_Mult;
double tp_dist = current_atr * InpTP_Mult;
double lot = CalculateLot(sl_dist);
if (lot == 0) return;
double sl, tp;
ENUM_ORDER_TYPE order_type;
double price;
if (signal == 1) {
order_type = ORDER_TYPE_BUY;
price = m_sym.Ask();
sl = price - sl_dist;
tp = price + tp_dist;
} else {
order_type = ORDER_TYPE_SELL;
price = m_sym.Bid();
sl = price + sl_dist;
tp = price - tp_dist;
}
sl = m_sym.NormalizePrice(sl);
tp = m_sym.NormalizePrice(tp);
m_trade.PositionOpen(_Symbol, order_type, lot, 0, sl, tp);
}
}
//+------------------------------------------------------------------+
//| Adjust volume to comply with symbol specifications |
//+------------------------------------------------------------------+
double AdjustVolume(double vol) {
if (vol <= 0) return 0.0;
double min_vol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max_vol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if (step == 0) return 0.0;
// Calculate volume digits for normalization
int vol_digits = (int) MathMax(0, -MathLog10(step) + 1e-10); // Add epsilon to avoid log issues
// Normalize input volume to avoid floating-point precision errors
vol = NormalizeDouble(vol, vol_digits + 2); // Higher precision initially
// Round to nearest step multiple
long ratio = (long) MathRound(vol / step);
double adjusted = ratio * step;
// Final normalization to exact digits
adjusted = NormalizeDouble(adjusted, vol_digits);
if (adjusted < min_vol || adjusted > max_vol) return 0.0;
return adjusted;
}
//+------------------------------------------------------------------+
//| Calculate position size based on risk |
//+------------------------------------------------------------------+
double CalculateLot(double sl_dist) {
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double risk = equity * InpRiskPercent / 100.0;
double points = sl_dist / _Point;
double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
if (tick_value == 0 || points == 0) return 0.0;
double lot = risk / (points * tick_value);
lot = AdjustVolume(lot);
return lot;
}
//+------------------------------------------------------------------+
//| Get current position direction |
//+------------------------------------------------------------------+
int GetCurrentDirection() {
int total = PositionsTotal();
for (int i = total - 1; i >= 0; i--) {
if (m_pos.SelectByIndex(i) && m_pos.Symbol() == _Symbol && m_pos.Magic() == InpMagic) {
if (m_pos.PositionType() == POSITION_TYPE_BUY) return 1;
if (m_pos.PositionType() == POSITION_TYPE_SELL) return -1;
}
}
return 0;
}
//+------------------------------------------------------------------+
//| Close all positions |
//+------------------------------------------------------------------+
void CloseAllPositions() {
int total = PositionsTotal();
for (int i = total - 1; i >= 0; i--) {
if (m_pos.SelectByIndex(i) && m_pos.Symbol() == _Symbol && m_pos.Magic() == InpMagic) {
m_trade.PositionClose(m_pos.Ticket());
}
}
}
//+------------------------------------------------------------------+
//| Get daily closed profit |
//+------------------------------------------------------------------+
double GetDailyProfit() {
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
dt.hour = 0; dt.min = 0; dt.sec = 0;
datetime start = StructToTime(dt);
datetime end = TimeCurrent() + 1; // Include current time
if (!HistorySelect(start, end)) return 0.0;
double profit = 0.0;
int deals = HistoryDealsTotal();
for (int i = deals - 1; i >= 0; i--) {
ulong ticket = HistoryDealGetTicket(i);
if (ticket > 0 &&
HistoryDealGetInteger(ticket, DEAL_MAGIC) == InpMagic &&
HistoryDealGetString(ticket, DEAL_SYMBOL) == _Symbol &&
HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_OUT) { // Only closed deals
profit += HistoryDealGetDouble(ticket, DEAL_PROFIT) +
HistoryDealGetDouble(ticket, DEAL_SWAP) +
HistoryDealGetDouble(ticket, DEAL_COMMISSION);
}
}
return profit;
}
//+------------------------------------------------------------------+
//| Get number of trades opened today |
//+------------------------------------------------------------------+
int GetTradesToday() {
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
dt.hour = 0; dt.min = 0; dt.sec = 0;
datetime start = StructToTime(dt);
datetime end = TimeCurrent() + 1;
if (!HistorySelect(start, end)) return 0;
int count = 0;
int deals = HistoryDealsTotal();
for (int i = deals - 1; i >= 0; i--) {
ulong ticket = HistoryDealGetTicket(i);
if (ticket > 0 &&
HistoryDealGetInteger(ticket, DEAL_MAGIC) == InpMagic &&
HistoryDealGetString(ticket, DEAL_SYMBOL) == _Symbol &&
HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_IN) {
count++;
}
}
return count;
}