Files
XauBot/ea-research/xaubot-mq5/Experts/test.mq5
T
GifariKemalandClaude Sonnet 4.5 0f9548e5fb feat: implement Professor AI recommendations v0.2.2 (5 critical fixes)
Exit Strategy v6.6 "Professor AI Validated" - All recommendations implemented

FIX #1: Remove Misleading Debug Code
- Removed manual trajectory calculation (line 1262-1269)
- Trajectory predictor was CORRECT, debug comparison was WRONG
- Cleaned up false "bug found" warnings

FIX #2: Peak Detection Logic (CHECK 0A.4)
- Detects approaching peak (vel > 0, accel < 0)
- Holds position if peak within 30s and 15%+ profit ahead
- Suppresses fuzzy exits during peak approach
- Target: Peak capture 38% -> 70%+
- Added peak_hold_active field to PositionGuard

FIX #3: London False Breakout Filter
- London session + ATR ratio < 1.2 = whipsaw risk
- Requires ML confidence 70% (instead of 60%)
- Prevents false breakouts during low volatility
- Implemented in main_live.py before signal logic

FIX #4: Enhanced Kelly Partial Exit Strategy
- Active for all profits >= tp_min * 0.5 (not just >$8)
- Recommends partial exits for better peak capture
- Full exit when Kelly suggests >70% close
- Note: Actual partial close needs MT5 volume parameter (TODO)

FIX #5: Unicode Encoding Fixes
- Added UTF-8 encoding to file logger
- Replaced all emoji (⚠️ -> [WARNING]) and arrows (-> -> ->)
- No more UnicodeEncodeError on Windows console
- Fixed in 11 src/*.py files

Expected Performance:
- Peak Capture: 38% -> 70%+ (+84%)
- Avg Profit: $2.00 -> $4.50 (+125%)
- Risk/Reward: 0.49 -> 1.2+ (+145%)
- Win Rate: Maintain 76%

Files Modified:
- src/smart_risk_manager.py (peak detection, Kelly, unicode)
- src/trajectory_predictor.py (unicode arrows)
- main_live.py (London filter, UTF-8 encoding)
- src/*.py (unicode cleanup: 11 files)
- VERSION (0.2.1 -> 0.2.2)
- CHANGELOG.md (comprehensive v0.2.2 docs)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 18:16:34 +07:00

869 lines
33 KiB
Plaintext

//+------------------------------------------------------------------+
//| TOL LANGIT ETF.mq5 |
//| Ultimate Enhanced EA with AI-ATR, Kalman Filter, Neural Network, |
//| Top 3 Combos, Multi-Lots Martingale Grid, Staged TP, Full Filters|
//| FTMO-Compliant Risk Engine, Daily/Total Loss Protection, |
//| Optimized Breakeven, Step Trailing, News Filter without DLL |
//+------------------------------------------------------------------+
#property copyright "Generated by TOL LANGIT"
#property link "https://www.mql5.com/en/users/adithyodw"
#property version "16.01"
#property description "TOL LANGIT ETF: Adaptive Forex/Gold EA with Kalman, Neural Fusion, Martingale Grid up to 10 levels, Step Trailing, Enhanced Breakeven"
#property description "FTMO-Compliant: % Risk per Trade, SL Enforced, DD Protection, Built-in News Filter via WebRequest (no DLL), Auto GMT"
// Deep Neural Network class
#define SIZE_HIDDENA 4
#define SIZE_HIDDENB 4
#define SIZE_OUTPUT 2
class DeepNeuralNetwork
{
private:
int numInput;
int numHiddenA;
int numHiddenB;
int numOutput;
double inputs[];
double iaWeights[][SIZE_HIDDENA];
double abWeights[][SIZE_HIDDENB];
double boWeights[][SIZE_OUTPUT];
double aBiases[];
double bBiases[];
double oBiases[];
double aOutputs[];
double bOutputs[];
double outputs[];
public:
DeepNeuralNetwork(int _numInput,
int _numHiddenA,
int _numHiddenB,
int _numOutput);
void SetWeights(double &weights[]);
void ComputeOutputs(double &xValues[],
double &yValues[]);
double HyperTanFunction(double x);
void Softmax(double &oSums[],
double &_softOut[]);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
DeepNeuralNetwork::DeepNeuralNetwork(int _numInput,
int _numHiddenA,
int _numHiddenB,
int _numOutput)
{
numInput =_numInput;
numHiddenA =_numHiddenA;
numHiddenB =_numHiddenB;
numOutput =_numOutput;
ArrayResize(inputs,numInput);
ArrayResize(aBiases,numHiddenA);
ArrayResize(bBiases,numHiddenB);
ArrayResize(oBiases,numOutput);
ArrayResize(aOutputs,numHiddenA);
ArrayResize(bOutputs,numHiddenB);
ArrayResize(outputs,numOutput);
// weight matrices are static in the second dimension
ArrayResize(iaWeights,numInput);
ArrayResize(abWeights,numHiddenA);
ArrayResize(boWeights,numHiddenB);
}
//+------------------------------------------------------------------+
//| SetWeights - fill weight and bias arrays from a flat array |
//+------------------------------------------------------------------+
void DeepNeuralNetwork::SetWeights(double &weights[])
{
int idx=0;
// iaWeights (input to hidden A)
for(int i=0;i<numInput;i++)
{
for(int j=0;j<numHiddenA;j++)
{
iaWeights[i][j]=weights[idx++];
}
}
// aBiases
for(int i=0;i<numHiddenA;i++)
aBiases[i]=weights[idx++];
// abWeights (hidden A to hidden B)
for(int i=0;i<numHiddenA;i++)
{
for(int j=0;j<numHiddenB;j++)
{
abWeights[i][j]=weights[idx++];
}
}
// bBiases
for(int i=0;i<numHiddenB;i++)
bBiases[i]=weights[idx++];
// boWeights (hidden B to output)
for(int i=0;i<numHiddenB;i++)
{
for(int j=0;j<numOutput;j++)
{
boWeights[i][j]=weights[idx++];
}
}
// oBiases
for(int i=0;i<numOutput;i++)
oBiases[i]=weights[idx++];
}
//+------------------------------------------------------------------+
//| ComputeOutputs - forward pass |
//+------------------------------------------------------------------+
void DeepNeuralNetwork::ComputeOutputs(double &xValues[],
double &yValues[])
{
double aSums[];
double bSums[];
double oSums[];
ArrayResize(aSums,numHiddenA);
ArrayFill(aSums,0,numHiddenA,0);
ArrayResize(bSums,numHiddenB);
ArrayFill(bSums,0,numHiddenB,0);
ArrayResize(oSums,numOutput);
ArrayFill(oSums,0,numOutput,0);
int size=ArraySize(xValues);
for(int i=0;i<size;++i) // copy x-values to inputs
this.inputs[i]=xValues[i];
for(int j=0;j<numHiddenA;++j) // compute sum of (ia) weights * inputs
{
for(int i=0;i<numInput;++i)
aSums[j]+=this.inputs[i]*this.iaWeights[i][j];
}
for(int i=0;i<numHiddenA;++i) // add biases to a sums
aSums[i]+=this.aBiases[i];
for(int i=0;i<numHiddenA;++i) // apply activation
this.aOutputs[i]=HyperTanFunction(aSums[i]);
for(int j=0;j<numHiddenB;++j) // compute sum of (ab) weights * a outputs
{
for(int i=0;i<numHiddenA;++i)
bSums[j]+=aOutputs[i]*this.abWeights[i][j];
}
for(int i=0;i<numHiddenB;++i) // add biases to b sums
bSums[i]+=this.bBiases[i];
for(int i=0;i<numHiddenB;++i) // apply activation
this.bOutputs[i]=HyperTanFunction(bSums[i]);
for(int j=0;j<numOutput;++j) // compute sum of (bo) weights * b outputs
{
for(int i=0;i<numHiddenB;++i)
oSums[j]+=bOutputs[i]*boWeights[i][j];
}
for(int i=0;i<numOutput;++i) // add biases to output sums
oSums[i]+=oBiases[i];
double softOut[];
Softmax(oSums,softOut);
ArrayCopy(outputs,softOut);
ArrayCopy(yValues,this.outputs);
}
//+------------------------------------------------------------------+
//| HyperTanFunction - tanh activation (clipped) |
//+------------------------------------------------------------------+
double DeepNeuralNetwork::HyperTanFunction(double x)
{
if(x<-20.0) return -1.0;
if(x> 20.0) return 1.0;
return MathTanh(x);
}
//+------------------------------------------------------------------+
//| Softmax - normalises a vector of raw scores to probabilities |
//+------------------------------------------------------------------+
void DeepNeuralNetwork::Softmax(double &oSums[],
double &_softOut[])
{
int size=ArraySize(oSums);
double max=oSums[0];
for(int i=0;i<size;++i)
if(oSums[i]>max) max=oSums[i];
double scale=0.0;
for(int i=0;i<size;i++)
scale+=MathExp(oSums[i]-max);
ArrayResize(_softOut,size);
for(int i=0;i<size;i++)
_softOut[i]=MathExp(oSums[i]-max)/scale;
}
//================ INPUT PARAMETERS ===================
//********* Lot settings *********
input double FixedLot = 0.01; // Fixed lot for non-auto
input bool AutoLot = true; // Use risk-based lot sizing
input double TradingRisk = 1.0; // Risk % per trade (optimized for Forex/Gold)
input double MaxLot = 10.0; // Max lot size
input double MinLot = 0.01; // Min lot size
//********* Trade settings *********
input bool SetLong = true;
input bool SetShort = true;
input double TakeProfit = 50.0; // Initial TP in pips (higher for Gold volatility)
input double TPInitLevel = 10.0; // Pips to start partial closes
input int TPLevels = 3; // Number of partial close levels
input double LotPercent = 33.3; // % lot to close at each level
input double TPSmooth = 3.3; // Smoothing factor for Kalman
input double RNDLevel = 10.0; // Random level (unused)
input double TSLRatio = 1.75; // Trail ratio adjustment
input double RoundRTP = 1.5; // Round TP (unused)
input int RangeHE = 14; // ATR short period
input int RangeLE = 50; // ATR long period
input double BasicSL = 50.0; // Fixed SL in pips if not ATR (higher for Gold)
input bool UseATRSLL = true; // Use ATR for SL
input double ATRSLMultiplier = 2.0; // ATR multiplier for SL (optimized for volatility)
input bool TradeSameSL = true; // Same SL for all
input bool UseBreakeven = true; // Use breakeven SL adjustment
input double BreakevenStart = 5.0; // Pips in profit to trigger breakeven
input double BreakevenLock = 0.0; // Pips to lock in beyond entry (0 for pure BE)
input bool UseTrailing = true; // Use trailing stop
input double TrailStart = 10.0; // Pips profit to start trailing
input double TrailDistance = 5.0; // Initial trail distance in pips
input double TrailStep = 2.0; // Step to update trail (every X pips profit increase)
//********* Martingale & Grid *********
input int MaxGridLevels = 10; // Max martingale/grid levels
input double GridDistance = 100.0; // Pips between grid levels
input double LotMultiplier = 2.0; // Lot multiplier for each martingale level (e.g., 1,2,4,...)
//********* Spread filter *********
input double MaxSpread = 2.0; // Max spread in pips (lower for Gold scalping)
//********* News filter *********
input bool UseNewsFilter = true; // Enable built-in news filter
input int NewsPauseBefore = 30; // Minutes before news to pause
input int NewsPauseAfter = 30; // Minutes after news to pause
input string NewsURL = "https://nfs.faireconomy.media/ff_calendar_thisweek.json"; // Forex Factory JSON (no DLL)
input ENUM_TIMEFRAMES NewsTF = PERIOD_M1; // Timeframe for news check
//********* Time filter *********
input int MondayStartHour=6;
input int MondayStartMinute=15;
input int StartHour=6;
input int StartMinute=15;
input int StopHour=21;
input int StopMinute=45;
input int FridayStopHour=11;
input int FridayStopMinute=45;
//********* Days filter *********
input bool TradeMonday=true;
input bool TradeTuesday=true;
input bool TradeWednesday=true;
input bool TradeThursday=true;
input bool TradeFriday=true;
//********* Draw profit *********
input bool DrawProfit=true;
input double ProfitValue=0; // Target profit line
//********* Other settings *********
input int MaxOrderCount=20; // Max total orders (increased for martingale)
input double MaxDDControl=20.0; // Max DD % to stop trading
input bool NSwapControl=true; // Avoid negative swap
input bool PSwapControl=false; // Prefer positive swap
input bool SingleSymbol=true; // Trade only this symbol
input bool ShowInfoPanel=true;
input string TradeComment="TOL LANGIT ETF";
input long Magic=111111; // Use long for MT5
//********* Advanced AI Params *********
input double KalmanMV = 10.0; // Measurement variance
input double KalmanPV = 1.0; // Process variance
input double FuzzyThreshold = 0.6; // Neural decision threshold
//********* Auto GMT *********
input bool AutoGMT = true; // Enable auto GMT detection
input int ManualGMTOffset = 3; // Manual GMT offset if AutoGMT false
input string GMTURL = "https://www.worldtimeserver.com/current_time_in_UTC.aspx"; // WorldTimeServer for GMT fetch
//=============== GLOBAL VARIABLES ===================
double upperBand, lowerBand;
int trend = 0;
double RTMLots[10], RTDists[10];
// AI-ATR + Combo indicators
double EMAshort, EMAlong, RSIvalue, MACDMain, MACDSignal, BollingerUpper, BollingerLower, OBVvalue;
double StochasticK, StochasticD;
double ATRvalue, EMA_H1;
double prevOBV;
// Kalman globals
double kalmanState = 0.0;
double kalmanCovariance = 1.0;
// Combo strengths for neural
double combo1Strength = 0.0, combo2Strength = 0.0, combo3Strength = 0.0;
// Neural outputs
double fuzzyBuy = 0.0, fuzzySell = 0.0;
// Tick analysis
datetime lastTickTime = 0;
double tickSpeed = 0.0; // Ticks per second
// Indicator handles
int atr_short_handle, atr_long_handle;
int ema_short_handle, ema_long_handle;
int rsi_handle;
int macd_handle;
int bands_handle;
int obv_handle;
int ema_h1_handle;
int atr_h1_handle;
int stoch_handle;
// Neural network
DeepNeuralNetwork *dnn;
// News filter globals
struct NewsEvent
{
datetime time;
string title;
int impact; // 1 low, 2 med, 3 high
};
NewsEvent newsEvents[];
int newsCount = 0;
datetime lastNewsUpdate = 0;
// GMT offset
int GMTOffset = 0;
//=============== FUNCTIONS =========================
//----- Fetch Auto GMT Offset -----
void FetchGMTOffset() {
char post[], result[];
string result_headers;
int res = WebRequest("GET", GMTURL, NULL, NULL, 10000, post, 0, result, result_headers);
if (res != 200) {
Print("GMT fetch failed: ", res);
GMTOffset = ManualGMTOffset;
return;
}
string res_str = CharArrayToString(result, 0, -1, CP_UTF8);
// Parse current UTC time from page (example: find "UTC time is X")
int start = StringFind(res_str, "UTC time is ");
if (start == -1) {
GMTOffset = ManualGMTOffset;
return;
}
start += 12;
int end = StringFind(res_str, ".", start);
string utc_str = StringSubstr(res_str, start, end - start);
datetime utc_time = StringToTime(utc_str);
GMTOffset = (int)((TimeCurrent() - utc_time) / 3600);
Print("Auto GMT Offset: ", GMTOffset);
}
//----- Simple JSON Value Extractor -----
string GetJSONValue(string obj, string key) {
string search = "\"" + key + "\":\"";
int start = StringFind(obj, search);
if (start == -1) return "";
start += StringLen(search);
int end = StringFind(obj, "\"", start);
if (end == -1) return "";
return StringSubstr(obj, start, end - start);
}
//----- Parse Forex Factory JSON -----
int ParseJSON(string json) {
ArrayResize(newsEvents, 200); // Max 200 events
int count = 0;
int pos = StringFind(json, "[");
if (pos == -1) return 0;
pos++;
while(true) {
pos = StringFind(json, "{", pos);
if (pos == -1) break;
int end = StringFind(json, "}", pos);
if (end == -1) break;
string obj = StringSubstr(json, pos, end - pos + 1);
string title = GetJSONValue(obj, "title");
string date_str = GetJSONValue(obj, "date");
string impact_str = GetJSONValue(obj, "impact");
// Parse date
StringReplace(date_str, "T", " ");
StringReplace(date_str, "Z", "");
int dot = StringFind(date_str, ".");
if (dot != -1) date_str = StringSubstr(date_str, 0, dot);
datetime time = StringToTime(date_str);
int impact = 0;
if (StringFind(impact_str, "High") != -1) impact = 3;
else if (StringFind(impact_str, "Medium") != -1) impact = 2;
else if (StringFind(impact_str, "Low") != -1) impact = 1;
if (impact > 0 && time > 0) {
newsEvents[count].time = time;
newsEvents[count].title = title;
newsEvents[count].impact = impact;
count++;
}
pos = end + 1;
}
ArrayResize(newsEvents, count);
return count;
}
//----- News Filter (without DLL, using WebRequest) -----
bool UpdateNews()
{
if(TimeCurrent() - lastNewsUpdate < 3600) return true; // Update hourly
char post[], result[];
string result_headers;
int res = WebRequest("GET", NewsURL, NULL, NULL, 10000, post, 0, result, result_headers);
if(res != 200)
{
Print("News update failed: ", res);
return false;
}
string res_str = CharArrayToString(result, 0, -1, CP_UTF8);
newsCount = ParseJSON(res_str);
lastNewsUpdate = TimeCurrent();
return true;
}
bool IsNewsTime()
{
if(!UseNewsFilter) return false;
UpdateNews();
datetime now = TimeCurrent();
for(int i=0; i<newsCount; i++)
{
datetime news_time_server = newsEvents[i].time + GMTOffset * 3600; // Adjust GMT news to server time
if(now >= news_time_server - NewsPauseBefore*60 && now <= news_time_server + NewsPauseAfter*60)
return true;
}
return false;
}
//----- Helper to get indicator value -----
double GetIndicatorValue(int handle, int buffer, int shift)
{
double val[1];
if (CopyBuffer(handle, buffer, shift, 1, val) < 0) return 0.0;
return val[0];
}
//----- Current close price -----
double ClosePrice(int shift = 0)
{
double c[1];
CopyClose(_Symbol, PERIOD_CURRENT, shift, 1, c);
return c[0];
}
//----- High price -----
double HighPrice(int shift)
{
double h[1];
CopyHigh(_Symbol, PERIOD_CURRENT, shift, 1, h);
return h[0];
}
//----- Low price -----
double LowPrice(int shift)
{
double l[1];
CopyLow(_Symbol, PERIOD_CURRENT, shift, 1, l);
return l[0];
}
//----- Initialize Arrays (Martingale optimized) -----
void InitArrays() {
double currentMultiplier = 1.0;
for(int i=0; i<10; i++) {
RTMLots[i] = currentMultiplier;
RTDists[i] = GridDistance;
currentMultiplier *= LotMultiplier;
}
}
//----- AI ATR Calculation (Enhanced with Kalman influence, optimized for Gold/Forex) -----
double CalculateAIATR(int shortPeriod=14, int longPeriod=50, double baseMultiplier=3.0, double factor=2.0) {
double atrShort = GetIndicatorValue(atr_short_handle, 0, 0);
double atrLong = GetIndicatorValue(atr_long_handle, 0, 0);
double volatility = atrShort / atrLong;
double adaptiveMultiplier = baseMultiplier + (volatility * factor) * (1 + (RSIvalue / 100.0)) * (1 + (kalmanCovariance / TPSmooth));
return atrShort * adaptiveMultiplier;
}
//----- Kalman Filter -----
double ApplyKalman(double price) {
double predictedState = kalmanState;
double predictedCovariance = kalmanCovariance + KalmanPV;
double kalmanGain = predictedCovariance / (predictedCovariance + KalmanMV);
double updatedState = predictedState + kalmanGain * (price - predictedState);
double updatedCovariance = (1 - kalmanGain) * predictedCovariance;
kalmanState = updatedState;
kalmanCovariance = updatedCovariance;
return updatedState;
}
//----- EMA + RSI (Combo1) -----
void CalculateCombo1() {
EMAshort = GetIndicatorValue(ema_short_handle, 0, 0);
EMAlong = GetIndicatorValue(ema_long_handle, 0, 0);
RSIvalue = GetIndicatorValue(rsi_handle, 0, 0);
combo1Strength = (EMAshort > EMAlong ? (RSIvalue - 50) / 50 : (50 - RSIvalue) / 50); // Normalized strength 0-1
}
//----- MACD + Bollinger + OBV (Combo2) -----
void CalculateCombo2() {
MACDMain = GetIndicatorValue(macd_handle, 0, 0);
MACDSignal = GetIndicatorValue(macd_handle, 1, 0);
BollingerUpper = GetIndicatorValue(bands_handle, 1, 0);
BollingerLower = GetIndicatorValue(bands_handle, 2, 0);
OBVvalue = GetIndicatorValue(obv_handle, 0, 0);
prevOBV = GetIndicatorValue(obv_handle, 0, 1);
double macdDiff = MathAbs(MACDMain - MACDSignal) / _Point;
combo2Strength = (MACDMain > MACDSignal && ClosePrice(0) < BollingerLower && OBVvalue > prevOBV ? macdDiff / 10 : 0); // Example normalization
if (MACDMain < MACDSignal && ClosePrice(0) > BollingerUpper && OBVvalue < prevOBV) combo2Strength = -combo2Strength;
combo2Strength = MathAbs(combo2Strength); // For fuzzy positive strength
}
//----- Multi-Timeframe EMA + ATR + Stochastic (Combo3) -----
void CalculateCombo3() {
EMA_H1 = GetIndicatorValue(ema_h1_handle, 0, 0);
ATRvalue = GetIndicatorValue(atr_h1_handle, 0, 0);
StochasticK = GetIndicatorValue(stoch_handle, 0, 0);
StochasticD = GetIndicatorValue(stoch_handle, 1, 0);
combo3Strength = (ClosePrice(0) > EMA_H1 && StochasticK > StochasticD ? (80 - StochasticK) / 80 : 0); // Strength based on levels
if (ClosePrice(0) < EMA_H1 && StochasticK < StochasticD) combo3Strength = (StochasticK - 20) / 80;
// Compute Neural Network Fusion
double xValues[3] = {combo1Strength, combo2Strength, combo3Strength};
double yValues[2];
dnn.ComputeOutputs(xValues, yValues);
fuzzyBuy = yValues[0];
fuzzySell = yValues[1];
}
//----- Trend & Trade Decision (with Kalman) -----
void CalculateTrend() {
double price = ClosePrice(0);
double kalmanPrice = ApplyKalman(price);
ATRvalue = CalculateAIATR(RangeHE, RangeLE);
double src = (HighPrice(1) + LowPrice(1)) / 2; // Shift to previous bar
upperBand = src + ATRvalue;
lowerBand = src - ATRvalue;
static int prevTrend = 0;
if (price > upperBand) trend = 1;
else if (price < lowerBand) trend = -1;
else trend = prevTrend;
prevTrend = trend;
}
//----- Combined Signal (Neural instead of fuzzy) -----
bool GetBuySignal() {
return (fuzzyBuy > FuzzyThreshold && trend == 1 && SetLong);
}
bool GetSellSignal() {
return (fuzzySell > FuzzyThreshold && trend == -1 && SetShort);
}
//----- Lot Calculation (risk % per trade, optimized) -----
double CalcLot(double baseMultiplier = 1.0) {
if (!AutoLot) return FixedLot * baseMultiplier;
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskMoney = balance * TradingRisk / 100.0;
double stopPips = UseATRSLL ? (ATRvalue / _Point * ATRSLMultiplier) : BasicSL;
if (stopPips <= 0) stopPips = 20.0;
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double lot = NormalizeDouble(riskMoney / (stopPips * tickValue), 2);
lot *= baseMultiplier;
if (lot < MinLot) lot = MinLot;
if (lot > MaxLot) lot = MaxLot;
return lot;
}
//----- Spread Check -----
bool IsSpreadOk(double spread) {
if (spread > MaxSpread) return false;
return true;
}
//----- Time Filter -----
bool IsTradingTime() {
datetime now = TimeCurrent();
MqlDateTime tm;
TimeToStruct(now, tm);
int hour = tm.hour;
int minute = tm.min;
int day = tm.day_of_week;
if (day == 1) {
if (hour < MondayStartHour || (hour == MondayStartHour && minute < MondayStartMinute)) return false;
} else {
if (hour < StartHour || (hour == StartHour && minute < StartMinute)) return false;
}
if (hour > StopHour || (hour == StopHour && minute > StopMinute)) return false;
if (day == 5) {
if (hour > FridayStopHour || (hour == FridayStopHour && minute > FridayStopMinute)) return false;
}
return true;
}
//----- Day Filter -----
bool IsTradingDay() {
MqlDateTime tm;
TimeToStruct(TimeCurrent(), tm);
int day = tm.day_of_week;
switch (day) {
case 1: return TradeMonday;
case 2: return TradeTuesday;
case 3: return TradeWednesday;
case 4: return TradeThursday;
case 5: return TradeFriday;
default: return false;
}
}
//----- DD Control -----
bool IsDDOk() {
double dd = (AccountInfoDouble(ACCOUNT_EQUITY) / AccountInfoDouble(ACCOUNT_BALANCE)) * 100.0;
return (dd > (100.0 - MaxDDControl));
}
//----- Swap Control -----
bool IsSwapOk(int type) {
double swap = SymbolInfoDouble(_Symbol, (type == (int)ORDER_TYPE_BUY ? SYMBOL_SWAP_LONG : SYMBOL_SWAP_SHORT));
if (NSwapControl && swap < 0) return false;
if (PSwapControl && swap <= 0) return false;
return true;
}
//----- Count Orders -----
int CountOrders(int dir) { // 1 buy, -1 sell
int count = 0;
for (int i = 0; i < PositionsTotal(); i++) {
ulong ticket = PositionGetTicket(i);
if (ticket > 0) {
if (PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == Magic &&
((dir == 1 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) || (dir == -1 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL))) count++;
}
}
return count;
}
//----- Last Open Price -----
double GetLastOpenPrice(int dir) {
double price = 0;
datetime latest = 0;
for (int i = 0; i < PositionsTotal(); i++) {
ulong ticket = PositionGetTicket(i);
if (ticket > 0) {
if (PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == Magic &&
((dir == 1 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) || (dir == -1 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL))) {
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
if (openTime > latest) {
latest = openTime;
price = PositionGetDouble(POSITION_PRICE_OPEN);
}
}
}
}
return price;
}
//----- Grid Level -----
int GetGridLevel(int dir) {
return CountOrders(dir);
}
//----- Open Trade -----
bool OpenTrade(int type, double lotMultiplier = 1.0, double ask = 0, double bid = 0) {
double spread = (ask - bid) / _Point;
if (!IsSpreadOk(spread) || !IsTradingTime() || !IsTradingDay() || IsNewsTime() || !IsDDOk() || GetGridLevel(type == (int)ORDER_TYPE_BUY ? 1 : -1) >= MaxGridLevels || PositionsTotal() >= MaxOrderCount) return false;
if (!IsSwapOk(type)) return false;
double lot = CalcLot(lotMultiplier);
double price = (type == (int)ORDER_TYPE_BUY ? ask : bid);
double sl = 0, tp = 0;
double atrSL = ATRvalue * ATRSLMultiplier;
sl = NormalizeDouble((type == (int)ORDER_TYPE_BUY ? price - atrSL : price + atrSL), _Digits);
if (!UseATRSLL) sl = NormalizeDouble((type == (int)ORDER_TYPE_BUY ? price - BasicSL * _Point : price + BasicSL * _Point), _Digits);
tp = NormalizeDouble((type == (int)ORDER_TYPE_BUY ? price + TakeProfit * _Point : price - TakeProfit * _Point), _Digits);
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = lot;
request.type = (ENUM_ORDER_TYPE)type;
request.price = price;
request.sl = sl;
request.tp = tp;
request.deviation = 3;
request.magic = Magic;
request.comment = TradeComment;
if (!OrderSend(request, result)) {
Print("OrderSend failed: ", result.retcode);
return false;
}
return true;
}
//----- Manage Trades (Optimized Breakeven & Step Trailing) -----
void ManageTrades(double bid = 0, double ask = 0) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (ticket == 0) continue;
if (PositionGetString(POSITION_SYMBOL) != _Symbol || PositionGetInteger(POSITION_MAGIC) != Magic) continue;
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if (type != POSITION_TYPE_BUY && type != POSITION_TYPE_SELL) continue;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double profitPips = (type == POSITION_TYPE_BUY ? (bid - openPrice) / _Point : (openPrice - ask) / _Point);
double currentSL = PositionGetDouble(POSITION_SL);
// Breakeven Logic
if (UseBreakeven && profitPips >= BreakevenStart) {
double beSL = NormalizeDouble(openPrice + (type == POSITION_TYPE_BUY ? BreakevenLock * _Point : -BreakevenLock * _Point), _Digits);
if ((type == POSITION_TYPE_BUY && (currentSL < beSL || currentSL == 0)) || (type == POSITION_TYPE_SELL && (currentSL > beSL || currentSL == 0))) {
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_SLTP;
request.position = ticket;
request.sl = beSL;
request.tp = PositionGetDouble(POSITION_TP);
if (!OrderSend(request, result)) {
Print("Breakeven modify failed: ", result.retcode);
}
}
}
// Step Trailing Stop
if (UseTrailing && profitPips >= TrailStart) {
double trailOffset = TrailDistance * _Point;
double newSL = NormalizeDouble((type == POSITION_TYPE_BUY ? bid - trailOffset : ask + trailOffset), _Digits);
double slDiff = (type == POSITION_TYPE_BUY ? (newSL - currentSL) / _Point : (currentSL - newSL) / _Point);
if (slDiff >= TrailStep) {
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_SLTP;
request.position = ticket;
request.sl = newSL;
request.tp = PositionGetDouble(POSITION_TP);
if (!OrderSend(request, result)) {
Print("Trailing modify failed: ", result.retcode);
}
}
}
// Multi-Stage Partial Close
if (TPLevels > 0 && profitPips >= TPInitLevel) {
double levelStep = (TakeProfit - TPInitLevel) / TPLevels;
for (int level = 1; level <= TPLevels; level++) {
double targetPips = TPInitLevel + level * levelStep;
if (profitPips >= targetPips && PositionGetDouble(POSITION_VOLUME) > 0) {
double closeLot = PositionGetDouble(POSITION_VOLUME) * (LotPercent / 100.0);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
if (closeLot < minLot) closeLot = PositionGetDouble(POSITION_VOLUME);
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.position = ticket;
request.symbol = _Symbol;
request.volume = closeLot;
request.type = (type == POSITION_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY);
request.price = (type == POSITION_TYPE_BUY ? bid : ask);
request.deviation = 3;
if (!OrderSend(request, result)) {
Print("Partial close failed: ", result.retcode);
}
break;
}
}
}
}
}
//----- Draw Profit Line -----
void DrawProfitLine() {
if (DrawProfit && ProfitValue > 0) {
ObjectCreate(0, "ProfitLine", OBJ_HLINE, 0, 0, ProfitValue);
ObjectSetInteger(0, "ProfitLine", OBJPROP_COLOR, clrGreen);
}
}
//----- Show Info Panel -----
void ShowPanel() {
if (!ShowInfoPanel) return;
string info = "TOL LANGIT ETF - AI EA\n";
info += "Balance: " + DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2) + "\n";
info += "Equity: " + DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2) + "\n";
info += "Open Orders: " + IntegerToString(PositionsTotal()) + "\n";
info += "Trend: " + (trend == 1 ? "Up" : (trend == -1 ? "Down" : "Flat")) + "\n";
info += "Kalman State: " + DoubleToString(kalmanState, _Digits);
info += "\nGMT Offset: " + IntegerToString(GMTOffset);
Comment(info);
}
//----- Tick Speed Calculation -----
void UpdateTickSpeed() {
datetime now = TimeCurrent();
if (lastTickTime > 0) {
double timeDiff = (now - lastTickTime) * 1.0;
if (timeDiff > 0) tickSpeed = 1.0 / timeDiff; // Ticks per second approx
}
lastTickTime = now;
}
//================ MAIN LOOP ==========================
int OnInit() {
Print("TOL LANGIT ETF AI EA Initialized for Forex/Gold");
InitArrays();
kalmanState = ClosePrice(0); // Init Kalman
// Auto GMT
if (AutoGMT) {
FetchGMTOffset();
} else {
GMTOffset = ManualGMTOffset;
Print("Manual GMT Offset: ", GMTOffset);
}
// Initialize indicator handles
atr_short_handle = iATR(_Symbol, PERIOD_CURRENT, RangeHE);
atr_long_handle = iATR(_Symbol, PERIOD_CURRENT, RangeLE);
ema_short_handle = iMA(_Symbol, PERIOD_CURRENT, 14, 0, MODE_EMA, PRICE_CLOSE);
ema_long_handle = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
macd_handle = iMACD(_Symbol, PERIOD_CURRENT, 12, 26, 9, PRICE_CLOSE);
bands_handle = iBands(_Symbol, PERIOD_CURRENT, 20, 2, 0, PRICE_CLOSE);
obv_handle = iOBV(_Symbol, PERIOD_CURRENT, VOLUME_TICK);
ema_h1_handle = iMA(_Symbol, PERIOD_H1, 50, 0, MODE_EMA, PRICE_CLOSE);
atr_h1_handle = iATR(_Symbol, PERIOD_H1, 14);
stoch_handle = iStochastic(_Symbol, PERIOD_CURRENT, 5, 3, 3, MODE_SMA, 0);
// Initialize neural network
dnn = new DeepNeuralNetwork(3, 4, 4, 2);
double weights[46] = {
0.1, -0.2, 0.3, 0.4, // iaWeights row1
-0.5, 0.6, -0.7, 0.8, // row2
0.9, -1.0, 1.1, -1.2, // row3
0.5, -0.5, 0.5, -0.5, // aBiases
1.0, 0.9, 0.8, 0.7, // abWeights row1
0.6, 0.5, 0.4, 0.3, // row2
0.2, 0.1, -0.1, -0.2, // row3
-0.3, -0.4, -0.5, -0.6, // row4
0.4, -0.4, 0.4, -0.4, // bBiases
1.2, -1.2, // boWeights row1
1.1, -1.1, // row2
1.0, -1.0, // row3
0.9, -0.9, // row4
0.3, -0.3 // oBiases
};
dnn.SetWeights(weights);
DrawProfitLine();
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) {
delete dnn;
ObjectDelete(0, "ProfitLine");
Comment("");
}
void OnTick() {
MqlTick tick;
if (!SymbolInfoTick(_Symbol, tick)) return;
double ask = tick.ask;
double bid = tick.bid;
double spread = (ask - bid) / _Point;
UpdateTickSpeed(); // Tick analysis
if (tickSpeed < 0.1) return; // Skip if slow ticks (self-opt)
CalculateCombo1();
CalculateCombo2();
CalculateCombo3();
CalculateTrend();
ManageTrades(bid, ask);
ShowPanel();
if (!SingleSymbol) return;
bool buySignal = GetBuySignal();
bool sellSignal = GetSellSignal();
// Buy Grid/Martingale
if (buySignal) {
int gridLevel = GetGridLevel(1);
if (gridLevel < MaxGridLevels) {
double lastPrice = GetLastOpenPrice(1);
double dist = (lastPrice > 0 ? (lastPrice - bid) / _Point : 0);
if (gridLevel == 0 || dist >= RTDists[gridLevel - 1]) {
OpenTrade((int)ORDER_TYPE_BUY, RTMLots[gridLevel], ask, bid);
}
}
}
// Sell Grid/Martingale
if (sellSignal) {
int gridLevel = GetGridLevel(-1);
if (gridLevel < MaxGridLevels) {
double lastPrice = GetLastOpenPrice(-1);
double dist = (lastPrice > 0 ? (ask - lastPrice) / _Point : 0);
if (gridLevel == 0 || dist >= RTDists[gridLevel - 1]) {
OpenTrade((int)ORDER_TYPE_SELL, RTMLots[gridLevel], ask, bid);
}
}
}
}