This commit is contained in:
zhutoutoutousan
2026-04-09 11:47:56 +02:00
parent 842a2f8fac
commit b50b430d1a
63 changed files with 12842 additions and 1 deletions
+46
View File
@@ -0,0 +1,46 @@
# XAUUSD H1 — ONNX action model
Same pipeline as **`../xauusd_m15`**, but **H1** bars, **H1-scaled label windows** (~wall-clock parity with M15 defaults), and **`XAUUSD_H1_ActionEA.mq5`**.
## Label scaling (vs M15)
| M15 (bars) | Wall time | H1 (bars) |
|------------|-----------|-----------|
| horizon 32 | ~8 h | 8 |
| local 24 | ~6 h | 6 |
| pullback 20| ~5 h | 5 |
## Setup
1. MT5: **XAUUSD** visible; download **H1** history.
2. Python:
```bash
cd ai/xauusd_h1
pip install -r requirements.txt
python main.py
```
Env: `XAU_SYMBOL`, **`XAU_H1_LOOKBACK`** (default **48**, must match EA **InpLookback**), `XAU_EPOCHS`, `XAU_BATCH`, `SESSION_HOUR_OFFSET`.
3. Copy **`models/XAUUSD_H1_action.onnx`** next to **`XAUUSD_H1_ActionEA.mq5`** (for `#resource` embed) or adjust include path per your workflow.
4. Compile EA on **H1** chart; paste **24** floats into **InpFeatMinStr** / **InpFeatMaxStr** from training stdout.
## Files
| File | Role |
|------|------|
| `main.py` | MT5 H1 fetch, train, `XAUUSD_H1_action.onnx` + meta |
| `labeling.py` | `compute_action_labels` (H1 default horizons) |
| `features.py` | 24-dim features (same order as M15 EA) |
| `XAUUSD_H1_ActionEA.mq5` | Inference + trading |
| `XAUUSD_H1_ActionEA_optimize.set` | Tester optimization skeleton |
Feature semantics: **`../xauusd_m15/FRONTLINE_RSI_INTEGRATION.md`**.
## ONNX
- Input: `[1, lookback, 24]` float32, row **0** = newest bar.
- Output: `[1, 5]` softmax.
Research tooling — not investment advice.
+372
View File
@@ -0,0 +1,372 @@
//+------------------------------------------------------------------+
//| XAUUSD_H1_ActionEA.mq5 |
//| ONNX softmax [5]: HOLD, BUY, SELL_SHORT, CLOSE_LONG, CLOSE_SHORT |
//| 24 features: base 13 + RSI/frontline (see ../xauusd_m15 doc) |
//| Train: ai/xauusd_h1/main.py → XAUUSD_H1_action.onnx |
//| Exits: model CLOSE_* + optional InpTakeProfitATR; adverse ATR |
//+------------------------------------------------------------------+
#property copyright "Profitable EA Project"
#property version "1.00"
#include <Trade\Trade.mqh>
#resource "XAUUSD_H1_action.onnx" as uchar ExtModel[]
#define FEAT_COUNT 24
input group "Model"
input int InpLookback = 48;
// 0 = legacy: p(BUY)>=InpProbBuy etc.; 1 = directional beats HOLD (5-class softmax)
input int InpEntryMode = 1;
input double InpProbBuy = 0.18;
input double InpProbSell = 0.18;
input double InpMinBeatHold = 0.0;
input int InpExitMode = 2;
input double InpProbCloseL = 0.18;
input double InpProbCloseS = 0.18;
input double InpMinCloseBeatHold = 0.0;
input group "Session (match Python SESSION_HOUR_OFFSET)"
input int InpSessionHourOffset = 0;
input group "Scaler: paste 24 floats each from python main.py"
input string InpFeatMinStr = "";
input string InpFeatMaxStr = "";
input group "Risk"
input double InpLotSize = 0.01;
input int InpMagic = 902016;
input int InpSlippage = 30;
input double InpMaxAdverseATR = 2.0;
input double InpTakeProfitATR = 0.0;
double g_feat_min[FEAT_COUNT];
double g_feat_max[FEAT_COUNT];
CTrade trade;
long g_onnx = INVALID_HANDLE;
datetime g_last_bar = 0;
void InitDefaultScalerBounds()
{
double def_min[FEAT_COUNT] = {
0,0,0,0,0,0,-0.05,-0.05,0,-0.02,1.0,0,0.1,
0,0,-1,-0.2,-0.2,0,0,0,0,0,0
};
double def_max[FEAT_COUNT] = {
5000,5000,5000,5000,1,1,0.05,0.05,0.05,0.02,1.02,1,5.0,
1,1,1,0.2,0.2,1,1,1,1,1,1
};
for(int i = 0; i < FEAT_COUNT; i++)
{
g_feat_min[i] = def_min[i];
g_feat_max[i] = def_max[i];
}
}
bool ParseFeatCsv(const string s, double &arr[])
{
if(StringLen(s) < 3) return false;
string parts[];
int n = StringSplit(s, ',', parts);
if(n != FEAT_COUNT) return false;
for(int i = 0; i < FEAT_COUNT; i++)
arr[i] = StringToDouble(parts[i]);
return true;
}
int OnInit()
{
InitDefaultScalerBounds();
trade.SetExpertMagicNumber(InpMagic);
trade.SetDeviationInPoints(InpSlippage);
trade.SetTypeFilling(ORDER_FILLING_IOC);
if(StringLen(InpFeatMinStr) > 0 && ParseFeatCsv(InpFeatMinStr, g_feat_min))
Print("Loaded InpFeatMinStr (24)");
if(StringLen(InpFeatMaxStr) > 0 && ParseFeatCsv(InpFeatMaxStr, g_feat_max))
Print("Loaded InpFeatMaxStr (24)");
g_onnx = OnnxCreateFromBuffer(ExtModel, ONNX_DEBUG_LOGS);
if(g_onnx == INVALID_HANDLE)
{
Print("OnnxCreateFromBuffer failed ", GetLastError());
return INIT_FAILED;
}
const long inShape[] = {1, InpLookback, FEAT_COUNT};
if(!OnnxSetInputShape(g_onnx, 0, inShape))
{
Print("OnnxSetInputShape failed ", GetLastError());
OnnxRelease(g_onnx);
return INIT_FAILED;
}
const long outShape[] = {1, 5};
if(!OnnxSetOutputShape(g_onnx, 0, outShape))
{
Print("OnnxSetOutputShape failed ", GetLastError());
OnnxRelease(g_onnx);
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
void OnDeinit(const int r)
{
if(g_onnx != INVALID_HANDLE) OnnxRelease(g_onnx);
}
double AtrNow()
{
double b[];
ArraySetAsSeries(b, true);
int h = iATR(_Symbol, PERIOD_CURRENT, 14);
if(h == INVALID_HANDLE) return 0;
if(CopyBuffer(h, 0, 0, 2, b) < 1) { IndicatorRelease(h); return 0; }
double v = b[0];
IndicatorRelease(h);
return v;
}
bool AdverseExit(const long type, const double open_price)
{
double atr = AtrNow();
if(atr <= 0) return false;
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(type == POSITION_TYPE_BUY)
{
double adv = (open_price - bid) / atr;
return adv >= InpMaxAdverseATR;
}
double adv = (ask - open_price) / atr;
return adv >= InpMaxAdverseATR;
}
bool ProfitExit(const long type, const double open_price)
{
if(InpTakeProfitATR <= 0.0) return false;
double atr = AtrNow();
if(atr <= 0.0) return false;
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(type == POSITION_TYPE_BUY)
return (bid - open_price) >= InpTakeProfitATR * atr;
return (open_price - ask) >= InpTakeProfitATR * atr;
}
bool ModelCloseLong(const double p0, const double p1, const double p3)
{
if(InpExitMode == 0)
return (p3 >= InpProbCloseL);
if(InpExitMode == 1)
return (p3 > p0 + InpMinCloseBeatHold && p3 > p1);
return (p3 > p0 + InpMinCloseBeatHold);
}
bool ModelCloseShort(const double p0, const double p2, const double p4)
{
if(InpExitMode == 0)
return (p4 >= InpProbCloseS);
if(InpExitMode == 1)
return (p4 > p0 + InpMinCloseBeatHold && p4 > p2);
return (p4 > p0 + InpMinCloseBeatHold);
}
void ScaleFeatures(const float &raw[], float &out[])
{
for(int f = 0; f < FEAT_COUNT; f++)
{
double den = g_feat_max[f] - g_feat_min[f];
if(den < 1e-12) den = 1e-12;
double x = (double)raw[f] - g_feat_min[f];
out[f] = (float)MathMax(0.0, MathMin(1.0, x / den));
}
}
bool PrepareMatrix(matrixf &M)
{
int L = InpLookback;
double open[], high[], low[], close[];
long vol[];
datetime bt[];
ArraySetAsSeries(open, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);
ArraySetAsSeries(close, true);
ArraySetAsSeries(vol, true);
ArraySetAsSeries(bt, true);
int need = L + 55;
if(CopyOpen(_Symbol, PERIOD_CURRENT, 0, need, open) < L) return false;
if(CopyHigh(_Symbol, PERIOD_CURRENT, 0, need, high) < L) return false;
if(CopyLow(_Symbol, PERIOD_CURRENT, 0, need, low) < L) return false;
if(CopyClose(_Symbol, PERIOD_CURRENT, 0, need, close) < L) return false;
if(CopyTickVolume(_Symbol, PERIOD_CURRENT, 0, need, vol) < L) return false;
if(CopyTime(_Symbol, PERIOD_CURRENT, 0, need, bt) < L) return false;
double rsi7[], rsi14[], rsi21[], ema20[], ema50[], atr[];
ArraySetAsSeries(rsi7, true);
ArraySetAsSeries(rsi14, true);
ArraySetAsSeries(rsi21, true);
ArraySetAsSeries(ema20, true);
ArraySetAsSeries(ema50, true);
ArraySetAsSeries(atr, true);
int h7 = iRSI(_Symbol, PERIOD_CURRENT, 7, PRICE_CLOSE);
int h14 = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
int h21 = iRSI(_Symbol, PERIOD_CURRENT, 21, PRICE_CLOSE);
int hE20 = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_EMA, PRICE_CLOSE);
int hE50 = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
int hA = iATR(_Symbol, PERIOD_CURRENT, 14);
if(h7 == INVALID_HANDLE || h14 == INVALID_HANDLE || h21 == INVALID_HANDLE ||
hE20 == INVALID_HANDLE || hE50 == INVALID_HANDLE || hA == INVALID_HANDLE)
return false;
if(CopyBuffer(h7, 0, 0, need, rsi7) < L ||
CopyBuffer(h14, 0, 0, need, rsi14) < L ||
CopyBuffer(h21, 0, 0, need, rsi21) < L ||
CopyBuffer(hE20, 0, 0, need, ema20) < L ||
CopyBuffer(hE50, 0, 0, need, ema50) < L ||
CopyBuffer(hA, 0, 0, need, atr) < L)
{
IndicatorRelease(h7); IndicatorRelease(h14); IndicatorRelease(h21);
IndicatorRelease(hE20); IndicatorRelease(hE50); IndicatorRelease(hA);
return false;
}
IndicatorRelease(h7); IndicatorRelease(h14); IndicatorRelease(h21);
IndicatorRelease(hE20); IndicatorRelease(hE50); IndicatorRelease(hA);
M.Resize(L, FEAT_COUNT);
const double RSI_OB = 70.0;
const double RSI_OS = 30.0;
for(int i = 0; i < L; i++)
{
double vma = 0;
int cnt = 0;
for(int k = i; k < i + 20 && k < ArraySize(vol); k++) { vma += (double)vol[k]; cnt++; }
if(cnt < 1) cnt = 1;
vma /= cnt;
double r0 = rsi14[i];
double r1 = (i + 1 < ArraySize(rsi14)) ? rsi14[i + 1] : r0;
double r2 = (i + 2 < ArraySize(rsi14)) ? rsi14[i + 2] : r1;
double rv7 = rsi7[i];
double rv21 = rsi21[i];
double spread = (r0 - rv7) / 50.0;
if(spread > 1.0) spread = 1.0;
if(spread < -1.0) spread = -1.0;
double vel = (r0 - r1) / 25.0;
double acc = ((r0 - r1) - (r1 - r2)) / 25.0;
double dist_mid = MathAbs(r0 - 50.0) / 50.0;
double c_ob = (r1 < RSI_OB && r0 >= RSI_OB) ? 1.0 : 0.0;
double c_os = (r1 > RSI_OS && r0 <= RSI_OS) ? 1.0 : 0.0;
double c50u = (r1 < 50.0 && r0 >= 50.0) ? 1.0 : 0.0;
double c50d = (r1 > 50.0 && r0 <= 50.0) ? 1.0 : 0.0;
MqlDateTime st;
TimeToStruct(bt[i], st);
int hr = (st.hour + InpSessionHourOffset) % 24;
if(hr < 0) hr += 24;
double asian = (hr >= 0 && hr < 8) ? 1.0 : 0.0;
float raw[FEAT_COUNT];
raw[0] = (float)open[i];
raw[1] = (float)high[i];
raw[2] = (float)low[i];
raw[3] = (float)close[i];
raw[4] = (float)((double)vol[i] / 1000000.0);
raw[5] = (float)(r0 / 100.0);
raw[6] = (float)((ema20[i] - close[i]) / close[i]);
raw[7] = (float)((ema50[i] - close[i]) / close[i]);
raw[8] = (float)(atr[i] / close[i]);
double pc = (i < L - 1) ? (close[i] - close[i + 1]) / close[i + 1] : 0.0;
raw[9] = (float)pc;
raw[10] = (float)(high[i] / low[i]);
raw[11] = (float)(vma / 1000000.0);
raw[12] = (float)(vma > 0 ? (double)vol[i] / vma : 1.0);
raw[13] = (float)(rv7 / 100.0);
raw[14] = (float)(rv21 / 100.0);
raw[15] = (float)spread;
raw[16] = (float)vel;
raw[17] = (float)acc;
raw[18] = (float)dist_mid;
raw[19] = (float)c_ob;
raw[20] = (float)c_os;
raw[21] = (float)c50u;
raw[22] = (float)c50d;
raw[23] = (float)asian;
float sc[FEAT_COUNT];
ScaleFeatures(raw, sc);
for(int j = 0; j < FEAT_COUNT; j++)
M[i][j] = sc[j];
}
return true;
}
void OnTick()
{
datetime t = iTime(_Symbol, PERIOD_CURRENT, 0);
if(t == g_last_bar) return;
g_last_bar = t;
matrixf Min;
if(!PrepareMatrix(Min))
{
Print("PrepareMatrix failed");
return;
}
vectorf out;
out.Resize(5);
if(!OnnxRun(g_onnx, ONNX_NO_CONVERSION, Min, out))
{
Print("OnnxRun failed ", GetLastError());
return;
}
double p0 = out[0], p1 = out[1], p2 = out[2], p3 = out[3], p4 = out[4];
Print("ONNX H1 HOLD=", p0, " BUY=", p1, " SELL=", p2, " CL=", p3, " CS=", p4);
if(!PositionSelect(_Symbol))
{
if(InpEntryMode == 1)
{
double dir = MathMax(p1, p2);
if(dir <= p0 + InpMinBeatHold)
return;
if(p1 >= p2 && p1 > p0 + InpMinBeatHold)
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "AI H1 BUY");
else if(p2 > p1 && p2 > p0 + InpMinBeatHold)
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "AI H1 SELL");
}
else
{
if(p1 >= InpProbBuy && p1 >= p2)
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "AI H1 BUY");
else if(p2 >= InpProbSell && p2 > p1)
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "AI H1 SELL");
}
return;
}
long typ = (long)PositionGetInteger(POSITION_TYPE);
double opn = PositionGetDouble(POSITION_PRICE_OPEN);
if(AdverseExit(typ, opn))
{
trade.PositionClose(_Symbol);
return;
}
if(ProfitExit(typ, opn))
{
trade.PositionClose(_Symbol);
return;
}
if(typ == POSITION_TYPE_BUY && ModelCloseLong(p0, p1, p3))
trade.PositionClose(_Symbol);
else if(typ == POSITION_TYPE_SELL && ModelCloseShort(p0, p2, p4))
trade.PositionClose(_Symbol);
}
@@ -0,0 +1,24 @@
; XAUUSD_H1_ActionEA — optimization preset (match trained InpLookback to ONNX)
; Copy to MetaQuotes\Terminal\<ID>\MQL5\Profiles\Tester\
;
; Model
InpLookback=48||32||8||96||Y
InpEntryMode=1||0||1||1||Y
InpProbBuy=0.18||0.14||0.02||0.26||Y
InpProbSell=0.18||0.14||0.02||0.26||Y
InpMinBeatHold=0.0||0.0||0.01||0.05||Y
InpExitMode=2||0||1||2||Y
InpProbCloseL=0.18||0.14||0.02||0.26||Y
InpProbCloseS=0.18||0.14||0.02||0.26||Y
InpMinCloseBeatHold=0.0||0.0||0.01||0.04||Y
; Session
InpSessionHourOffset=0||-3||1||3||N
; Scaler
InpFeatMinStr=
InpFeatMaxStr=
; Risk
InpLotSize=0.01||0.01||0.001000||0.100000||N
InpMagic=902016||902016||1||9020160||N
InpSlippage=30||30||1||300||N
InpMaxAdverseATR=2.0||1.0||0.25||3.5||Y
InpTakeProfitATR=0.0||0.0||0.25||3.0||Y
Binary file not shown.
Binary file not shown.
Binary file not shown.
+167
View File
@@ -0,0 +1,167 @@
"""
Feature pipeline: base 13 (EA-compatible) + 11 RSI / session features.
Same 24 dims as XAUUSD M15 EA (see ../xauusd_m15/FRONTLINE_RSI_INTEGRATION.md).
RSI uses Wilder smoothing (ewm alpha=1/period) to align with MT5 iRSI.
"""
from __future__ import annotations
import os
import numpy as np
import pandas as pd
NUM_BASE_FEATURES = 13
NUM_RSI_EXTRA = 11
NUM_FEATURES = NUM_BASE_FEATURES + NUM_RSI_EXTRA # 24
RSI_OVERBOUGHT = 70.0
RSI_OVERSOLD = 30.0
def wilder_rsi(close: pd.Series, period: int) -> np.ndarray:
"""Wilder RSI (matches MetaTrader iRSI closely)."""
delta = close.diff()
gain = delta.clip(lower=0.0)
loss = (-delta).clip(lower=0.0)
avg_g = gain.ewm(alpha=1.0 / period, min_periods=period, adjust=False).mean()
avg_l = loss.ewm(alpha=1.0 / period, min_periods=period, adjust=False).mean()
rs = avg_g / avg_l.replace(0, np.nan)
rsi = 100.0 - (100.0 / (1.0 + rs))
return rsi.fillna(50.0).to_numpy(dtype=np.float64)
def prepare_features_full(
df: pd.DataFrame,
*,
session_hour_offset: int | None = None,
) -> pd.DataFrame:
"""
Build (N, 24) feature table, chronological index matching df.
Drops first ~50 rows (warmup).
"""
if session_hour_offset is None:
session_hour_offset = int(os.environ.get("SESSION_HOUR_OFFSET", "0"))
o = df["open"].to_numpy(dtype=np.float64)
h = df["high"].to_numpy(dtype=np.float64)
l = df["low"].to_numpy(dtype=np.float64)
c = df["close"].astype(float)
vol = df["tick_volume"].to_numpy(dtype=np.float64)
n = len(df)
idx = df.index
rsi7 = wilder_rsi(c, 7)
rsi14 = wilder_rsi(c, 14)
rsi21 = wilder_rsi(c, 21)
ema20 = c.ewm(span=20, adjust=False).mean().to_numpy()
ema50 = c.ewm(span=50, adjust=False).mean().to_numpy()
tr = np.maximum(
h - l,
np.maximum(np.abs(h - np.roll(c.to_numpy(), 1)), np.abs(l - np.roll(c.to_numpy(), 1))),
)
tr[0] = h[0] - l[0]
atr = pd.Series(tr).rolling(14).mean().to_numpy()
vol_ma = np.zeros(n)
for j in range(n):
s = 0.0
cnt = 0
for k in range(j, min(j + 20, n)):
s += vol[k]
cnt += 1
vol_ma[j] = s / cnt if cnt else vol[j]
pc_ea = np.zeros(n)
cvals = c.to_numpy()
for j in range(1, n):
den = cvals[j - 1]
pc_ea[j] = (cvals[j] - den) / den if den else 0.0
hours = np.zeros(n, dtype=np.int32)
for j in range(n):
ts = idx[j]
try:
hts = int(ts.hour)
except Exception:
hts = 0
hours[j] = (hts + session_hour_offset) % 24
rows = []
for j in range(n):
r0 = rsi14[j]
r1 = rsi14[j - 1] if j > 0 else r0
r2 = rsi14[j - 2] if j > 1 else r1
spread = np.clip((r0 - rsi7[j]) / 50.0, -1.0, 1.0)
vel = (r0 - r1) / 25.0
acc = ((r0 - r1) - (r1 - r2)) / 25.0
dist_mid = abs(r0 - 50.0) / 50.0
cross_ob = 1.0 if (r1 < RSI_OVERBOUGHT and r0 >= RSI_OVERBOUGHT) else 0.0
cross_os = 1.0 if (r1 > RSI_OVERSOLD and r0 <= RSI_OVERSOLD) else 0.0
cross_50_up = 1.0 if (r1 < 50.0 and r0 >= 50.0) else 0.0
cross_50_dn = 1.0 if (r1 > 50.0 and r0 <= 50.0) else 0.0
asian = 1.0 if (0 <= hours[j] < 8) else 0.0
rows.append(
[
float(o[j]),
float(h[j]),
float(l[j]),
float(cvals[j]),
float(vol[j] / 1_000_000.0),
float(rsi14[j] / 100.0),
float((ema20[j] - cvals[j]) / cvals[j]) if cvals[j] else 0.0,
float((ema50[j] - cvals[j]) / cvals[j]) if cvals[j] else 0.0,
float(atr[j] / cvals[j]) if cvals[j] else 0.0,
float(pc_ea[j]),
float(h[j] / l[j]) if l[j] else 1.0,
float(vol_ma[j] / 1_000_000.0),
float(vol[j] / vol_ma[j]) if vol_ma[j] > 0 else 1.0,
float(rsi7[j] / 100.0),
float(rsi21[j] / 100.0),
float(spread),
float(vel),
float(acc),
float(dist_mid),
float(cross_ob),
float(cross_os),
float(cross_50_up),
float(cross_50_dn),
float(asian),
]
)
cols = [
"open",
"high",
"low",
"close",
"tick_volume",
"rsi",
"ema20_n",
"ema50_n",
"atr_n",
"price_change",
"high_low_ratio",
"volume_ma",
"volume_ratio",
"rsi7_n",
"rsi21_n",
"rsi_fast_slow_spread",
"rsi_velocity",
"rsi_accel",
"rsi_dist_mid_50",
"rsi_cross_overbought",
"rsi_cross_oversold",
"rsi_cross_50_up",
"rsi_cross_50_down",
"session_asian_utc",
]
out = pd.DataFrame(rows, index=idx, columns=cols)
return out.iloc[50:].copy()
+128
View File
@@ -0,0 +1,128 @@
"""
Buy-low / sell-high style labels for OHLCV bars (no fixed SL/TP in labels).
H1 defaults scale M15 bar counts to ~similar wall-clock horizons:
M15 horizon=32 -> 8h -> H1 horizon=8
M15 local=24 -> 6h -> H1 local=6
M15 pullback=20 -> 5h -> H1 pullback=5
Classes (integer, matches EA):
0 HOLD
1 BUY — forward upside vs ATR + local swing low
2 SELL_SHORT — forward downside vs ATR + local swing high
3 CLOSE_LONG — past-only: pullback from recent range high
4 CLOSE_SHORT — past-only: bounce from recent range low
"""
from __future__ import annotations
import numpy as np
import pandas as pd
def atr_series(df: pd.DataFrame, period: int = 14) -> pd.Series:
high, low, close = df["high"], df["low"], df["close"]
tr = pd.concat(
[
high - low,
(high - close.shift()).abs(),
(low - close.shift()).abs(),
],
axis=1,
).max(axis=1)
return tr.rolling(period).mean()
def compute_action_labels(
df: pd.DataFrame,
*,
horizon: int = 8,
local_window: int = 6,
pullback_window: int = 5,
k_forward_atr: float = 0.75,
local_pct: float = 0.28,
pullback_mult: float = 0.55,
trend_mult: float = 1.05,
) -> pd.Series:
"""
Return a Series of int labels 0..4 aligned to df index.
Last `horizon` rows → HOLD (no forward path for buy/sell scoring).
"""
close = df["close"].values
high = df["high"].values
low = df["low"].values
n = len(df)
atr = atr_series(df, 14).values
labels = np.zeros(n, dtype=np.int64)
lw = local_window
pw = pullback_window
need = max(lw, pw) + 2
for t in range(n):
if t < need or t >= n - horizon:
labels[t] = 0
continue
a = atr[t]
if not np.isfinite(a) or a <= 0:
a = close[t] * 1e-4
sl = low[t + 1 : t + horizon + 1]
sh = high[t + 1 : t + horizon + 1]
fwd_max = float(np.max(sh))
fwd_min = float(np.min(sl))
up_move = (fwd_max - close[t]) / a
down_move = (close[t] - fwd_min) / a
loc_low = float(np.min(low[t - lw : t + 1]))
loc_high = float(np.max(high[t - lw : t + 1]))
rng = max(loc_high - loc_low, a * 0.15)
near_low = (close[t] - loc_low) / rng <= local_pct
near_high = (loc_high - close[t]) / rng <= local_pct
buy_sig = near_low and (up_move >= k_forward_atr) and (up_move >= down_move * 0.85)
sell_sig = near_high and (down_move >= k_forward_atr) and (down_move > up_move * 1.05)
seg_h = high[t - pw : t + 1]
seg_l = low[t - pw : t + 1]
rh = float(np.max(seg_h))
rl = float(np.min(seg_l))
range_atr = (rh - rl) / a
pull_from_high = (rh - close[t]) / a
bounce_from_low = (close[t] - rl) / a
exit_long = (
range_atr >= trend_mult
and pull_from_high >= pullback_mult
and close[t] < close[t - 1]
)
exit_short = (
range_atr >= trend_mult
and bounce_from_low >= pullback_mult
and close[t] > close[t - 1]
)
if exit_long and not buy_sig:
labels[t] = 3
elif exit_short and not sell_sig:
labels[t] = 4
elif buy_sig and not sell_sig:
labels[t] = 1
elif sell_sig and not buy_sig:
labels[t] = 2
elif buy_sig and sell_sig:
labels[t] = 1 if up_move >= down_move else 2
else:
labels[t] = 0
return pd.Series(labels, index=df.index, name="action_label")
def class_weights(y: np.ndarray, n_classes: int = 5) -> dict[int, float]:
from sklearn.utils.class_weight import compute_class_weight
y_int = y.astype(int)
classes = np.arange(n_classes)
cw = compute_class_weight("balanced", classes=classes, y=y_int)
return {i: float(cw[i]) for i in range(n_classes)}
+209
View File
@@ -0,0 +1,209 @@
"""
XAUUSD H1 — ONNX action model (buy / sell short / close long / close short / hold).
Same 24 features as M15 stack; labels use H1-scaled horizons (~wall-clock parity with M15).
Row order matches XAUUSD_H1_ActionEA.mq5 (row 0 = newest bar).
Data: MT5, 20082026 (limited by downloaded history).
"""
from __future__ import annotations
import json
import os
import pickle
import sys
from datetime import datetime, timedelta
import MetaTrader5 as mt5
import numpy as np
import pandas as pd
import tensorflow as tf
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from tensorflow import keras
from tensorflow.keras import layers
from tqdm import tqdm
import tf2onnx
import onnx
from labeling import class_weights, compute_action_labels
from features import NUM_FEATURES, prepare_features_full
NUM_CLASSES = 5
CLASS_NAMES = ["HOLD", "BUY", "SELL_SHORT", "CLOSE_LONG", "CLOSE_SHORT"]
def fetch_mt5_range(
symbol: str,
timeframe: int,
start_date: datetime,
end_date: datetime,
) -> pd.DataFrame:
if not mt5.initialize():
raise RuntimeError(f"MT5 init failed: {mt5.last_error()}")
info = mt5.symbol_info(symbol)
if info is None:
mt5.shutdown()
raise ValueError(f"Symbol {symbol} not found")
if not info.visible and not mt5.symbol_select(symbol, True):
mt5.shutdown()
raise ValueError(f"Cannot select {symbol}")
all_rows: list[dict] = []
chunk_days = 120
cur = start_date
while cur < end_date:
chunk_end = min(cur + timedelta(days=chunk_days), end_date)
rates = mt5.copy_rates_range(symbol, timeframe, cur, chunk_end)
if rates is not None and len(rates) > 1:
for row in rates:
all_rows.append({n: row[n] for n in rates.dtype.names})
cur = chunk_end
if not all_rows:
mt5.shutdown()
raise ValueError("No rates returned — download XAUUSD H1 in MT5 History Center")
df = pd.DataFrame(all_rows)
df["time"] = pd.to_datetime(df["time"], unit="s")
df = df.set_index("time").sort_index()
df = df[~df.index.duplicated(keep="first")]
return df
def create_sequences(
X: np.ndarray, y: np.ndarray, lookback: int
) -> tuple[np.ndarray, np.ndarray]:
xs, ys = [], []
for i in tqdm(range(lookback - 1, len(X)), desc="sequences"):
window = X[i - lookback + 1 : i + 1].copy()
window = window[::-1]
xs.append(window)
ys.append(y[i])
return np.asarray(xs, dtype=np.float32), np.asarray(ys, dtype=np.int64)
def build_model(lookback: int, n_feat: int) -> keras.Model:
inp = layers.Input(shape=(lookback, n_feat))
x = layers.LSTM(96, return_sequences=True)(inp)
x = layers.Dropout(0.25)(x)
x = layers.LSTM(48)(x)
x = layers.Dropout(0.25)(x)
x = layers.Dense(32, activation="relu")(x)
out = layers.Dense(NUM_CLASSES, activation="softmax", name="action_probs")(x)
model = keras.Model(inp, out)
model.compile(
optimizer=keras.optimizers.Adam(1e-3),
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)
return model
def main() -> int:
symbol = os.environ.get("XAU_SYMBOL", "XAUUSD")
lookback = int(os.environ.get("XAU_H1_LOOKBACK", os.environ.get("XAU_LOOKBACK", "48")))
epochs = int(os.environ.get("XAU_EPOCHS", "40"))
batch_size = int(os.environ.get("XAU_BATCH", "64"))
start_date = datetime(2008, 1, 1)
end_date = datetime(2026, 12, 31)
out_dir = os.path.join(os.path.dirname(__file__), "models")
os.makedirs(out_dir, exist_ok=True)
onnx_path = os.path.join(out_dir, f"{symbol}_H1_action.onnx")
meta_path = os.path.join(out_dir, f"{symbol}_H1_action_meta.json")
print("Fetching MT5 H1 data …")
try:
raw = fetch_mt5_range(symbol, mt5.TIMEFRAME_H1, start_date, end_date)
finally:
mt5.shutdown()
print(f"Bars: {len(raw)} range: {raw.index[0]}{raw.index[-1]}")
feat = prepare_features_full(raw)
labels_full = compute_action_labels(raw)
labels = labels_full.loc[feat.index]
y = labels.loc[feat.index].values.astype(np.int64)
X_raw = feat.values.astype(np.float32)
valid = np.isfinite(X_raw).all(axis=1) & (y >= 0) & (y < NUM_CLASSES)
X_raw = X_raw[valid]
y = y[valid]
print("Label counts:", {CLASS_NAMES[i]: int((y == i).sum()) for i in range(NUM_CLASSES)})
scaler = MinMaxScaler()
Xn = scaler.fit_transform(X_raw).astype(np.float32)
X_seq, y_seq = create_sequences(Xn, y, lookback)
if len(X_seq) < 500:
print("ERROR: Too few sequences — need more H1 history in MT5.")
return 1
X_train, X_val, y_train, y_val = train_test_split(
X_seq, y_seq, test_size=0.15, shuffle=False
)
cw = class_weights(y_train, NUM_CLASSES)
sample_w = np.array([cw[int(c)] for c in y_train], dtype=np.float32)
model = build_model(lookback, NUM_FEATURES)
model.summary()
model.fit(
X_train,
y_train,
sample_weight=sample_w,
validation_data=(X_val, y_val),
epochs=epochs,
batch_size=batch_size,
verbose=1,
callbacks=[
keras.callbacks.EarlyStopping(
monitor="val_loss", patience=8, restore_best_weights=True
),
keras.callbacks.ReduceLROnPlateau(
monitor="val_loss", factor=0.5, patience=4, min_lr=1e-6
),
],
)
spec = (tf.TensorSpec((None, lookback, NUM_FEATURES), tf.float32, name="input"),)
onnx_m, _ = tf2onnx.convert.from_keras(model, input_signature=spec, opset=13)
onnx.save_model(onnx_m, onnx_path)
with open(onnx_path.replace(".onnx", "_scaler.pkl"), "wb") as f:
pickle.dump(scaler, f)
meta = {
"symbol": symbol,
"timeframe": "H1",
"lookback": lookback,
"num_features": int(NUM_FEATURES),
"feature_columns": feat.columns.tolist(),
"num_classes": NUM_CLASSES,
"class_names": CLASS_NAMES,
"label_horizon_bars": 8,
"label_note": "H1 labeling defaults: horizon=8, local=6, pullback=5 (~M15 wall-clock parity)",
"scaler_feature_min": scaler.data_min_.tolist(),
"scaler_feature_max": scaler.data_max_.tolist(),
"scaler_scale": scaler.scale_.tolist() if hasattr(scaler, "scale_") else None,
"notes": "MinMax in EA; row0=newest. Match EA InpLookback to lookback here.",
}
with open(meta_path, "w", encoding="utf-8") as f:
json.dump(meta, f, indent=2)
print(f"Saved: {onnx_path}")
print(f"Meta: {meta_path}")
print("\n--- Paste into EA InpFeatMinStr / InpFeatMaxStr (comma-separated, %d floats each) ---" % NUM_FEATURES)
print(",".join(f"{x:.8g}" for x in scaler.data_min_))
print(",".join(f"{x:.8g}" for x in scaler.data_max_))
print(f"\nSet EA InpLookback = {lookback} (must match ONNX input dim).")
return 0
if __name__ == "__main__":
sys.exit(main())
Binary file not shown.
@@ -0,0 +1,121 @@
{
"symbol": "XAUUSD",
"timeframe": "H1",
"lookback": 48,
"num_features": 24,
"feature_columns": [
"open",
"high",
"low",
"close",
"tick_volume",
"rsi",
"ema20_n",
"ema50_n",
"atr_n",
"price_change",
"high_low_ratio",
"volume_ma",
"volume_ratio",
"rsi7_n",
"rsi21_n",
"rsi_fast_slow_spread",
"rsi_velocity",
"rsi_accel",
"rsi_dist_mid_50",
"rsi_cross_overbought",
"rsi_cross_oversold",
"rsi_cross_50_up",
"rsi_cross_50_down",
"session_asian_utc"
],
"num_classes": 5,
"class_names": [
"HOLD",
"BUY",
"SELL_SHORT",
"CLOSE_LONG",
"CLOSE_SHORT"
],
"label_horizon_bars": 8,
"label_note": "H1 labeling defaults: horizon=8, local=6, pullback=5 (~M15 wall-clock parity)",
"scaler_feature_min": [
679.5499877929688,
735.0499877929688,
679.5499877929688,
711.2999877929688,
0.0,
0.0778568685054779,
-0.08346110582351685,
-0.13061486184597015,
0.0006287021678872406,
-0.09134025126695633,
1.0,
0.0018113000551238656,
0.0,
0.020070146769285202,
0.1127406507730484,
-0.45013511180877686,
-1.6049232482910156,
-2.0694637298583984,
1.0986201232299209e-05,
0.0,
0.0,
0.0,
0.0,
0.0
],
"scaler_feature_max": [
5562.419921875,
5598.06005859375,
5554.68994140625,
5562.43994140625,
0.15629400312900543,
0.9388294816017151,
0.1516682505607605,
0.17963257431983948,
0.07575831562280655,
0.10734681040048599,
1.138908863067627,
0.11270634829998016,
11.032988548278809,
0.9843139052391052,
0.8853746056556702,
0.44974473118782043,
1.5471272468566895,
2.1628992557525635,
0.8776589632034302,
1.0,
1.0,
1.0,
1.0,
1.0
],
"scaler_scale": [
0.00020479758677538484,
0.00020563394355122,
0.00020512231276370585,
0.00020613710512407124,
6.398198127746582,
1.1614770889282227,
4.2529778480529785,
3.223233938217163,
13.310330390930176,
5.033040523529053,
7.198964595794678,
9.017535209655762,
0.09063727408647537,
1.0370821952819824,
1.2942739725112915,
1.1112594604492188,
0.31725379824638367,
0.23627464473247528,
1.1394089460372925,
1.0,
1.0,
1.0,
1.0,
1.0
],
"notes": "MinMax in EA; row0=newest. Match EA InpLookback to lookback here."
}
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
numpy>=1.23
pandas>=2.0
MetaTrader5>=5.0.45
tensorflow>=2.14
tf2onnx>=1.16
onnx>=1.15
scikit-learn>=1.3
tqdm>=4.66
+21
View File
@@ -0,0 +1,21 @@
"""
Dynamic adverse risk (conceptual mirror of EA InpMaxAdverseATR).
"""
from __future__ import annotations
import numpy as np
def adverse_hit_long(
entry: float,
low_path: np.ndarray,
atr_path: np.ndarray,
max_adverse_atr: float,
) -> int | None:
for i in range(len(low_path)):
atr = max(atr_path[i], entry * 1e-6)
adv = (entry - low_path[i]) / atr
if adv >= max_adverse_atr:
return i
return None
@@ -0,0 +1,43 @@
# Frontline RSI 经验 → `ai/xauusd_m15` 特征映射
本文把 `frontline/MQL5/_united/Strategies` 里与 RSI 相关的**可量化**逻辑,映射到训练用的 **24 维特征**(前 13 维与原版 EA 一致,后 11 维为 RSI/时段扩展)。
## 策略来源与特征对应
| Frontline 模块 | 经验要点 | 模型中的体现 |
|----------------|----------|----------------|
| **RSIReversalAsianStrategy** | 上穿超买 / 下穿超卖的**交叉**;亚洲时段(UTC 0–8)语境 | `rsi_cross_overbought` / `rsi_cross_oversold`(默认 70/30);`session_asian_utc` |
| **RSICrossOverReversalStrategy** | 超买/超卖区附近的**反转入场**、RSI 退出位 | 交叉特征 + `rsi_velocity` / `rsi_accel` 描述短期摆动 |
| **RSIScalpingStrategy** | 极值区外的**回升/回落**(多根 RSI 结构) | `rsi_velocity``rsi_accel`3 根 RSI14 近似) |
| **RSIMidPointHijackStrategy** | 相对 **50** 中轴、快慢 RSI 状态 | `rsi_dist_mid_50``rsi_fast_slow_spread`RSI14 vs RSI7 |
| **多品种 RSI Scalping** | 更短周期敏感 | `rsi7_n`(快周期)、`rsi21_n`(慢周期) |
## 特征索引(与 Python / EA 顺序一致)
| 索引 | 名称 | 说明 |
|------|------|------|
| 04 | OHLC + tick_volume | 与原版一致 |
| 5 | rsi | Wilder RSI(14)/100 |
| 612 | EMA/ATR/价量 | 与原版一致 |
| 13 | rsi7_n | RSI(7)/100 |
| 14 | rsi21_n | RSI(21)/100 |
| 15 | rsi_fast_slow_spread | clip((RSI14RSI7)/50, 1, 1) |
| 16 | rsi_velocity | (RSI14₀−RSI14₁)/25 |
| 17 | rsi_accel | ((RSI14₀−RSI14₁)(RSI14₁−RSI14₂))/25 |
| 18 | rsi_dist_mid_50 | \|RSI1450\|/50 |
| 1922 | cross_* | 0/1,与 frontline 交叉定义一致(上一根→当前根) |
| 23 | session_asian_utc | 小时经偏移后 ∈ [0,8) 则为 1 |
## 时段偏移
MT5 K 线时间多为**服务器时区**。若要与 UTC 亚洲窗对齐,训练时设环境变量 `SESSION_HOUR_OFFSET`EA 使用 `InpSessionHourOffset`,使 `(hour + offset) % 24` 与你在回测里认定的 UTC 一致。
## 未直接编码的规则(可后续扩展)
- **点差、最大持仓时长、Magic 分策略**:可作为额外标量特征或单独过滤层。
- **RSIMidPoint 的「先标记超买再下穿退出线」**:可用连续两 bar 的 cross 组合特征或 LSTM 隐式学习;当前用 cross + dist_mid 近似。
- **Darvas / EMA 等非 RSI 策略**:未并入本 ONNX 特征;可在 `features.py` 中追加列并同步改 `NUM_FEATURES` 与 EA。
## 再训练提醒
修改 `NUM_FEATURES` 后必须:**重新导出 ONNX**、更新 EA 中 `#resource` 模型、`OnnxSetInputShape` 第三维、**24 个 scaler min/max**。
+49
View File
@@ -0,0 +1,49 @@
# XAUUSD M15 — ONNX action model (buy / sell / close)
## What it does
- Pulls **XAUUSD** (**M15**) from **MetaTrader 5** (20082026 requested; actual range depends on History Center).
- **24 features**: 13 legacy OHLC/EMA/ATR/volume + **11 RSI / session** features aligned with **frontline** strategies (crosses, velocity, RSI7/21, Asian window). See **`FRONTLINE_RSI_INTEGRATION.md`**.
- Labels: **buy-low / sell-high** (forward window) + **close-long / close-short** (past-only). RSI enters as **inputs**, not as hard-coded label rules.
- Trains **LSTM → softmax(5)**: `HOLD`, `BUY`, `SELL_SHORT`, `CLOSE_LONG`, `CLOSE_SHORT`.
- Exports **`models/XAUUSD_M15_action.onnx`** + scaler + **`XAUUSD_M15_action_meta.json`** (includes `feature_columns`).
- **EA**: **SL=0, TP=0**; **InpMaxAdverseATR**; **InpSessionHourOffset** should match training `SESSION_HOUR_OFFSET` for Asian flag.
This is research tooling — not investment advice. Past labels do not guarantee live performance.
## Setup
1. MT5 installed, logged in, **XAUUSD** visible; download **M15** history (Tools → History Center or chart scroll).
2. Python 3.10+:
```bash
cd ai/xauusd_m15
pip install -r requirements.txt
python main.py
```
Optional env: `XAU_SYMBOL`, `XAU_LOOKBACK` (default 64), `XAU_EPOCHS`, `XAU_BATCH`, `SESSION_HOUR_OFFSET` (Asian session hour alignment vs server time).
3. Copy `models/XAUUSD_M15_action.onnx` to **`MQL5/Files/`** (same path as `#resource` in the EA).
4. Open `XAUUSD_M15_ActionEA.mq5` in MetaEditor; compile.
5. Paste two lines from training stdout into **InpFeatMinStr** and **InpFeatMaxStr** (comma-separated **24** floats each).
## ONNX I/O
- Input: `[1, lookback, 24]` float32, **row 0 = newest bar**.
- Output: `[1, 5]` softmax probabilities.
## Files
| File | Role |
|------|------|
| `main.py` | Fetch, features, labels, train, ONNX + meta |
| `features.py` | 24-dim pipeline + Wilder RSI |
| `FRONTLINE_RSI_INTEGRATION.md` | frontline 策略 → 特征对照 |
| `labeling.py` | `compute_action_labels` |
| `risk_controls.py` | Adverse ATR helper for Python backtests |
| `XAUUSD_M15_ActionEA.mq5` | Live inference + trading skeleton |
## Tuning labels
Edit parameters in `labeling.compute_action_labels()` (`horizon`, `k_forward_atr`, `pullback_mult`, etc.) and retrain.
+375
View File
@@ -0,0 +1,375 @@
//+------------------------------------------------------------------+
//| XAUUSD_M15_ActionEA.mq5 |
//| ONNX softmax [5]: HOLD, BUY, SELL_SHORT, CLOSE_LONG, CLOSE_SHORT |
//| 24 features: base 13 + RSI/frontline (see FRONTLINE_RSI_*.md) |
//| Exits: model CLOSE_* + optional InpTakeProfitATR; adverse ATR |
//+------------------------------------------------------------------+
#property copyright "Profitable EA Project"
#property version "1.03"
#include <Trade\Trade.mqh>
#resource "XAUUSD_M15_action.onnx" as uchar ExtModel[]
#define FEAT_COUNT 24
input group "Model"
input int InpLookback = 64;
// 0 = legacy: require p(BUY)>=InpProbBuy and p(SELL)>=InpProbSell (use ~0.18 for 5-class softmax)
// 1 = default: open only when directional prob beats HOLD (typical 5-way outputs ~0.150.25 each)
input int InpEntryMode = 1;
input double InpProbBuy = 0.18;
input double InpProbSell = 0.18;
input double InpMinBeatHold = 0.0; // mode 1: require max(p1,p2)-p0 >= this (e.g. 0.02)
// Exit: 0 = p3/p4 >= thresholds (use ~0.18 for 5-class); 1 = CLOSE beats HOLD and beats add (p3>p1 / p4>p2)
// 2 = default: CLOSE beats HOLD only (lets winners exit when pullback signal > hold; still weak in trends)
input int InpExitMode = 2;
input double InpProbCloseL = 0.18;
input double InpProbCloseS = 0.18;
input double InpMinCloseBeatHold = 0.0; // exit modes 12: require p3/p4 > p0 + this
input group "Session (match Python SESSION_HOUR_OFFSET)"
input int InpSessionHourOffset = 0; // add to bar hour so Asian 08 matches training
input group "Scaler: paste 24 floats each from python main.py"
input string InpFeatMinStr = "";
input string InpFeatMaxStr = "";
input group "Risk"
input double InpLotSize = 0.01;
input int InpMagic = 902015;
input int InpSlippage = 30;
input double InpMaxAdverseATR = 2.0;
input double InpTakeProfitATR = 0.0; // >0: close in profit when price move >= this * ATR(14) (banks winners)
double g_feat_min[FEAT_COUNT];
double g_feat_max[FEAT_COUNT];
CTrade trade;
long g_onnx = INVALID_HANDLE;
datetime g_last_bar = 0;
void InitDefaultScalerBounds()
{
double def_min[FEAT_COUNT] = {
0,0,0,0,0,0,-0.05,-0.05,0,-0.02,1.0,0,0.1,
0,0,-1,-0.2,-0.2,0,0,0,0,0,0
};
double def_max[FEAT_COUNT] = {
5000,5000,5000,5000,1,1,0.05,0.05,0.05,0.02,1.02,1,5.0,
1,1,1,0.2,0.2,1,1,1,1,1,1
};
for(int i = 0; i < FEAT_COUNT; i++)
{
g_feat_min[i] = def_min[i];
g_feat_max[i] = def_max[i];
}
}
bool ParseFeatCsv(const string s, double &arr[])
{
if(StringLen(s) < 3) return false;
string parts[];
int n = StringSplit(s, ',', parts);
if(n != FEAT_COUNT) return false;
for(int i = 0; i < FEAT_COUNT; i++)
arr[i] = StringToDouble(parts[i]);
return true;
}
int OnInit()
{
InitDefaultScalerBounds();
trade.SetExpertMagicNumber(InpMagic);
trade.SetDeviationInPoints(InpSlippage);
trade.SetTypeFilling(ORDER_FILLING_IOC);
if(StringLen(InpFeatMinStr) > 0 && ParseFeatCsv(InpFeatMinStr, g_feat_min))
Print("Loaded InpFeatMinStr (24)");
if(StringLen(InpFeatMaxStr) > 0 && ParseFeatCsv(InpFeatMaxStr, g_feat_max))
Print("Loaded InpFeatMaxStr (24)");
g_onnx = OnnxCreateFromBuffer(ExtModel, ONNX_DEBUG_LOGS);
if(g_onnx == INVALID_HANDLE)
{
Print("OnnxCreateFromBuffer failed ", GetLastError());
return INIT_FAILED;
}
const long inShape[] = {1, InpLookback, FEAT_COUNT};
if(!OnnxSetInputShape(g_onnx, 0, inShape))
{
Print("OnnxSetInputShape failed ", GetLastError());
OnnxRelease(g_onnx);
return INIT_FAILED;
}
const long outShape[] = {1, 5};
if(!OnnxSetOutputShape(g_onnx, 0, outShape))
{
Print("OnnxSetOutputShape failed ", GetLastError());
OnnxRelease(g_onnx);
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
void OnDeinit(const int r)
{
if(g_onnx != INVALID_HANDLE) OnnxRelease(g_onnx);
}
double AtrNow()
{
double b[];
ArraySetAsSeries(b, true);
int h = iATR(_Symbol, PERIOD_CURRENT, 14);
if(h == INVALID_HANDLE) return 0;
if(CopyBuffer(h, 0, 0, 2, b) < 1) { IndicatorRelease(h); return 0; }
double v = b[0];
IndicatorRelease(h);
return v;
}
bool AdverseExit(const long type, const double open_price)
{
double atr = AtrNow();
if(atr <= 0) return false;
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(type == POSITION_TYPE_BUY)
{
double adv = (open_price - bid) / atr;
return adv >= InpMaxAdverseATR;
}
double adv = (ask - open_price) / atr;
return adv >= InpMaxAdverseATR;
}
bool ProfitExit(const long type, const double open_price)
{
if(InpTakeProfitATR <= 0.0) return false;
double atr = AtrNow();
if(atr <= 0.0) return false;
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(type == POSITION_TYPE_BUY)
return (bid - open_price) >= InpTakeProfitATR * atr;
return (open_price - ask) >= InpTakeProfitATR * atr;
}
bool ModelCloseLong(const double p0, const double p1, const double p3)
{
if(InpExitMode == 0)
return (p3 >= InpProbCloseL);
if(InpExitMode == 1)
return (p3 > p0 + InpMinCloseBeatHold && p3 > p1);
// mode 2: close-long probability beats hold (trends can still keep p1 high; use InpTakeProfitATR then)
return (p3 > p0 + InpMinCloseBeatHold);
}
bool ModelCloseShort(const double p0, const double p2, const double p4)
{
if(InpExitMode == 0)
return (p4 >= InpProbCloseS);
if(InpExitMode == 1)
return (p4 > p0 + InpMinCloseBeatHold && p4 > p2);
return (p4 > p0 + InpMinCloseBeatHold);
}
void ScaleFeatures(const float &raw[], float &out[])
{
for(int f = 0; f < FEAT_COUNT; f++)
{
double den = g_feat_max[f] - g_feat_min[f];
if(den < 1e-12) den = 1e-12;
double x = (double)raw[f] - g_feat_min[f];
out[f] = (float)MathMax(0.0, MathMin(1.0, x / den));
}
}
bool PrepareMatrix(matrixf &M)
{
int L = InpLookback;
double open[], high[], low[], close[];
long vol[];
datetime bt[];
ArraySetAsSeries(open, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);
ArraySetAsSeries(close, true);
ArraySetAsSeries(vol, true);
ArraySetAsSeries(bt, true);
int need = L + 55;
if(CopyOpen(_Symbol, PERIOD_CURRENT, 0, need, open) < L) return false;
if(CopyHigh(_Symbol, PERIOD_CURRENT, 0, need, high) < L) return false;
if(CopyLow(_Symbol, PERIOD_CURRENT, 0, need, low) < L) return false;
if(CopyClose(_Symbol, PERIOD_CURRENT, 0, need, close) < L) return false;
if(CopyTickVolume(_Symbol, PERIOD_CURRENT, 0, need, vol) < L) return false;
if(CopyTime(_Symbol, PERIOD_CURRENT, 0, need, bt) < L) return false;
double rsi7[], rsi14[], rsi21[], ema20[], ema50[], atr[];
ArraySetAsSeries(rsi7, true);
ArraySetAsSeries(rsi14, true);
ArraySetAsSeries(rsi21, true);
ArraySetAsSeries(ema20, true);
ArraySetAsSeries(ema50, true);
ArraySetAsSeries(atr, true);
int h7 = iRSI(_Symbol, PERIOD_CURRENT, 7, PRICE_CLOSE);
int h14 = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
int h21 = iRSI(_Symbol, PERIOD_CURRENT, 21, PRICE_CLOSE);
int hE20 = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_EMA, PRICE_CLOSE);
int hE50 = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
int hA = iATR(_Symbol, PERIOD_CURRENT, 14);
if(h7 == INVALID_HANDLE || h14 == INVALID_HANDLE || h21 == INVALID_HANDLE ||
hE20 == INVALID_HANDLE || hE50 == INVALID_HANDLE || hA == INVALID_HANDLE)
return false;
if(CopyBuffer(h7, 0, 0, need, rsi7) < L ||
CopyBuffer(h14, 0, 0, need, rsi14) < L ||
CopyBuffer(h21, 0, 0, need, rsi21) < L ||
CopyBuffer(hE20, 0, 0, need, ema20) < L ||
CopyBuffer(hE50, 0, 0, need, ema50) < L ||
CopyBuffer(hA, 0, 0, need, atr) < L)
{
IndicatorRelease(h7); IndicatorRelease(h14); IndicatorRelease(h21);
IndicatorRelease(hE20); IndicatorRelease(hE50); IndicatorRelease(hA);
return false;
}
IndicatorRelease(h7); IndicatorRelease(h14); IndicatorRelease(h21);
IndicatorRelease(hE20); IndicatorRelease(hE50); IndicatorRelease(hA);
M.Resize(L, FEAT_COUNT);
const double RSI_OB = 70.0;
const double RSI_OS = 30.0;
for(int i = 0; i < L; i++)
{
double vma = 0;
int cnt = 0;
for(int k = i; k < i + 20 && k < ArraySize(vol); k++) { vma += (double)vol[k]; cnt++; }
if(cnt < 1) cnt = 1;
vma /= cnt;
double r0 = rsi14[i];
double r1 = (i + 1 < ArraySize(rsi14)) ? rsi14[i + 1] : r0;
double r2 = (i + 2 < ArraySize(rsi14)) ? rsi14[i + 2] : r1;
double rv7 = rsi7[i];
double rv21 = rsi21[i];
double spread = (r0 - rv7) / 50.0;
if(spread > 1.0) spread = 1.0;
if(spread < -1.0) spread = -1.0;
double vel = (r0 - r1) / 25.0;
double acc = ((r0 - r1) - (r1 - r2)) / 25.0;
double dist_mid = MathAbs(r0 - 50.0) / 50.0;
double c_ob = (r1 < RSI_OB && r0 >= RSI_OB) ? 1.0 : 0.0;
double c_os = (r1 > RSI_OS && r0 <= RSI_OS) ? 1.0 : 0.0;
double c50u = (r1 < 50.0 && r0 >= 50.0) ? 1.0 : 0.0;
double c50d = (r1 > 50.0 && r0 <= 50.0) ? 1.0 : 0.0;
MqlDateTime st;
TimeToStruct(bt[i], st);
int hr = (st.hour + InpSessionHourOffset) % 24;
if(hr < 0) hr += 24;
double asian = (hr >= 0 && hr < 8) ? 1.0 : 0.0;
float raw[FEAT_COUNT];
raw[0] = (float)open[i];
raw[1] = (float)high[i];
raw[2] = (float)low[i];
raw[3] = (float)close[i];
raw[4] = (float)((double)vol[i] / 1000000.0);
raw[5] = (float)(r0 / 100.0);
raw[6] = (float)((ema20[i] - close[i]) / close[i]);
raw[7] = (float)((ema50[i] - close[i]) / close[i]);
raw[8] = (float)(atr[i] / close[i]);
double pc = (i < L - 1) ? (close[i] - close[i + 1]) / close[i + 1] : 0.0;
raw[9] = (float)pc;
raw[10] = (float)(high[i] / low[i]);
raw[11] = (float)(vma / 1000000.0);
raw[12] = (float)(vma > 0 ? (double)vol[i] / vma : 1.0);
raw[13] = (float)(rv7 / 100.0);
raw[14] = (float)(rv21 / 100.0);
raw[15] = (float)spread;
raw[16] = (float)vel;
raw[17] = (float)acc;
raw[18] = (float)dist_mid;
raw[19] = (float)c_ob;
raw[20] = (float)c_os;
raw[21] = (float)c50u;
raw[22] = (float)c50d;
raw[23] = (float)asian;
float sc[FEAT_COUNT];
ScaleFeatures(raw, sc);
for(int j = 0; j < FEAT_COUNT; j++)
M[i][j] = sc[j];
}
return true;
}
void OnTick()
{
datetime t = iTime(_Symbol, PERIOD_CURRENT, 0);
if(t == g_last_bar) return;
g_last_bar = t;
matrixf Min;
if(!PrepareMatrix(Min))
{
Print("PrepareMatrix failed");
return;
}
vectorf out;
out.Resize(5);
if(!OnnxRun(g_onnx, ONNX_NO_CONVERSION, Min, out))
{
Print("OnnxRun failed ", GetLastError());
return;
}
double p0 = out[0], p1 = out[1], p2 = out[2], p3 = out[3], p4 = out[4];
Print("ONNX HOLD=", p0, " BUY=", p1, " SELL=", p2, " CL=", p3, " CS=", p4);
if(!PositionSelect(_Symbol))
{
if(InpEntryMode == 1)
{
double dir = MathMax(p1, p2);
if(dir <= p0 + InpMinBeatHold)
return;
if(p1 >= p2 && p1 > p0 + InpMinBeatHold)
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "AI BUY");
else if(p2 > p1 && p2 > p0 + InpMinBeatHold)
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "AI SELL");
}
else
{
if(p1 >= InpProbBuy && p1 >= p2)
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "AI BUY");
else if(p2 >= InpProbSell && p2 > p1)
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "AI SELL");
}
return;
}
long typ = (long)PositionGetInteger(POSITION_TYPE);
double opn = PositionGetDouble(POSITION_PRICE_OPEN);
if(AdverseExit(typ, opn))
{
trade.PositionClose(_Symbol);
return;
}
if(ProfitExit(typ, opn))
{
trade.PositionClose(_Symbol);
return;
}
if(typ == POSITION_TYPE_BUY && ModelCloseLong(p0, p1, p3))
trade.PositionClose(_Symbol);
else if(typ == POSITION_TYPE_SELL && ModelCloseShort(p0, p2, p4))
trade.PositionClose(_Symbol);
}
@@ -0,0 +1,27 @@
; XAUUSD_M15_ActionEA v1.03 — optimization preset
; Copy to: MetaQuotes\Terminal\<ID>\MQL5\Profiles\Tester\
; Strategy Tester → Inputs → context menu → Load
;
; Format: Name=value||optimize_start||step||stop||Y|N (Y = optimize this parameter)
;
; Model
InpLookback=64||48||8||96||Y
InpEntryMode=1||0||1||1||Y
InpProbBuy=0.18||0.14||0.02||0.26||Y
InpProbSell=0.18||0.14||0.02||0.26||Y
InpMinBeatHold=0.0||0.0||0.01||0.05||Y
InpExitMode=2||0||1||2||Y
InpProbCloseL=0.18||0.14||0.02||0.26||Y
InpProbCloseS=0.18||0.14||0.02||0.26||Y
InpMinCloseBeatHold=0.0||0.0||0.01||0.04||Y
; Session (match Python SESSION_HOUR_OFFSET)
InpSessionHourOffset=0||-3||1||3||N
; Scaler: paste 24 floats from python main.py (not optimizable)
InpFeatMinStr=
InpFeatMaxStr=
; Risk
InpLotSize=0.01||0.01||0.001000||0.100000||N
InpMagic=902015||902015||1||9020150||N
InpSlippage=30||30||1||300||N
InpMaxAdverseATR=2.0||1.0||0.25||3.5||Y
InpTakeProfitATR=0.0||0.0||0.25||3.0||Y
Binary file not shown.
Binary file not shown.
+173
View File
@@ -0,0 +1,173 @@
"""
Feature pipeline: base 13 (EA-compatible) + 11 RSI / session features from frontline experience.
Frontline mapping (see FRONTLINE_RSI_INTEGRATION.md):
- RSIReversalAsianStrategy / RSICrossOverReversal: cross OB/OS, cross 50
- RSIScalpingStrategy: RSI velocity (bounce from extreme uses 3-bar structure → vel/acc)
- RSIMidPointHijack: distance from 50, RSI(7) vs RSI(14) spread
- Asian session gate → binary feature (hour window; offset for server vs UTC)
RSI uses Wilder smoothing (ewm alpha=1/period) to align with MT5 iRSI.
"""
from __future__ import annotations
import os
import numpy as np
import pandas as pd
NUM_BASE_FEATURES = 13
NUM_RSI_EXTRA = 11
NUM_FEATURES = NUM_BASE_FEATURES + NUM_RSI_EXTRA # 24
# Default thresholds aligned with common frontline inputs (Asian / scalping)
RSI_OVERBOUGHT = 70.0
RSI_OVERSOLD = 30.0
def wilder_rsi(close: pd.Series, period: int) -> np.ndarray:
"""Wilder RSI (matches MetaTrader iRSI closely)."""
delta = close.diff()
gain = delta.clip(lower=0.0)
loss = (-delta).clip(lower=0.0)
avg_g = gain.ewm(alpha=1.0 / period, min_periods=period, adjust=False).mean()
avg_l = loss.ewm(alpha=1.0 / period, min_periods=period, adjust=False).mean()
rs = avg_g / avg_l.replace(0, np.nan)
rsi = 100.0 - (100.0 / (1.0 + rs))
return rsi.fillna(50.0).to_numpy(dtype=np.float64)
def prepare_features_full(
df: pd.DataFrame,
*,
session_hour_offset: int | None = None,
) -> pd.DataFrame:
"""
Build (N, 24) feature table, chronological index matching df.
Drops first ~50 rows (warmup) like the original pipeline.
"""
if session_hour_offset is None:
session_hour_offset = int(os.environ.get("SESSION_HOUR_OFFSET", "0"))
o = df["open"].to_numpy(dtype=np.float64)
h = df["high"].to_numpy(dtype=np.float64)
l = df["low"].to_numpy(dtype=np.float64)
c = df["close"].astype(float)
vol = df["tick_volume"].to_numpy(dtype=np.float64)
n = len(df)
idx = df.index
rsi7 = wilder_rsi(c, 7)
rsi14 = wilder_rsi(c, 14)
rsi21 = wilder_rsi(c, 21)
ema20 = c.ewm(span=20, adjust=False).mean().to_numpy()
ema50 = c.ewm(span=50, adjust=False).mean().to_numpy()
tr = np.maximum(
h - l,
np.maximum(np.abs(h - np.roll(c.to_numpy(), 1)), np.abs(l - np.roll(c.to_numpy(), 1))),
)
tr[0] = h[0] - l[0]
atr = pd.Series(tr).rolling(14).mean().to_numpy()
vol_ma = np.zeros(n)
for j in range(n):
s = 0.0
cnt = 0
for k in range(j, min(j + 20, n)):
s += vol[k]
cnt += 1
vol_ma[j] = s / cnt if cnt else vol[j]
pc_ea = np.zeros(n)
cvals = c.to_numpy()
for j in range(1, n):
den = cvals[j - 1]
pc_ea[j] = (cvals[j] - den) / den if den else 0.0
hours = np.zeros(n, dtype=np.int32)
for j in range(n):
ts = idx[j]
try:
hts = int(ts.hour)
except Exception:
hts = 0
hours[j] = (hts + session_hour_offset) % 24
rows = []
for j in range(n):
r0 = rsi14[j]
r1 = rsi14[j - 1] if j > 0 else r0
r2 = rsi14[j - 2] if j > 1 else r1
spread = np.clip((r0 - rsi7[j]) / 50.0, -1.0, 1.0)
vel = (r0 - r1) / 25.0
acc = ((r0 - r1) - (r1 - r2)) / 25.0
dist_mid = abs(r0 - 50.0) / 50.0
cross_ob = 1.0 if (r1 < RSI_OVERBOUGHT and r0 >= RSI_OVERBOUGHT) else 0.0
cross_os = 1.0 if (r1 > RSI_OVERSOLD and r0 <= RSI_OVERSOLD) else 0.0
cross_50_up = 1.0 if (r1 < 50.0 and r0 >= 50.0) else 0.0
cross_50_dn = 1.0 if (r1 > 50.0 and r0 <= 50.0) else 0.0
asian = 1.0 if (0 <= hours[j] < 8) else 0.0
rows.append(
[
float(o[j]),
float(h[j]),
float(l[j]),
float(cvals[j]),
float(vol[j] / 1_000_000.0),
float(rsi14[j] / 100.0),
float((ema20[j] - cvals[j]) / cvals[j]) if cvals[j] else 0.0,
float((ema50[j] - cvals[j]) / cvals[j]) if cvals[j] else 0.0,
float(atr[j] / cvals[j]) if cvals[j] else 0.0,
float(pc_ea[j]),
float(h[j] / l[j]) if l[j] else 1.0,
float(vol_ma[j] / 1_000_000.0),
float(vol[j] / vol_ma[j]) if vol_ma[j] > 0 else 1.0,
float(rsi7[j] / 100.0),
float(rsi21[j] / 100.0),
float(spread),
float(vel),
float(acc),
float(dist_mid),
float(cross_ob),
float(cross_os),
float(cross_50_up),
float(cross_50_dn),
float(asian),
]
)
cols = [
"open",
"high",
"low",
"close",
"tick_volume",
"rsi",
"ema20_n",
"ema50_n",
"atr_n",
"price_change",
"high_low_ratio",
"volume_ma",
"volume_ratio",
"rsi7_n",
"rsi21_n",
"rsi_fast_slow_spread",
"rsi_velocity",
"rsi_accel",
"rsi_dist_mid_50",
"rsi_cross_overbought",
"rsi_cross_oversold",
"rsi_cross_50_up",
"rsi_cross_50_down",
"session_asian_utc",
]
out = pd.DataFrame(rows, index=idx, columns=cols)
return out.iloc[50:].copy()
+130
View File
@@ -0,0 +1,130 @@
"""
Buy-low / sell-high style labels for OHLCV bars (no fixed SL/TP in labels).
Optional context: frontline RSI strategies (Asian reversal, scalping, mid-50)
are encoded as *features* in features.py (crosses, velocity, session), not as
hard rules here — the network learns joint patterns with price/volume.
Classes (integer, matches EA):
0 HOLD
1 BUY — forward upside vs ATR + local swing low
2 SELL_SHORT — forward downside vs ATR + local swing high
3 CLOSE_LONG — past-only: pullback from recent range high
4 CLOSE_SHORT — past-only: bounce from recent range low
CLOSE_* use only bars <= t (no future leak).
BUY/SELL use forward window [t+1, t+horizon] (supervised targets).
"""
from __future__ import annotations
import numpy as np
import pandas as pd
def atr_series(df: pd.DataFrame, period: int = 14) -> pd.Series:
high, low, close = df["high"], df["low"], df["close"]
tr = pd.concat(
[
high - low,
(high - close.shift()).abs(),
(low - close.shift()).abs(),
],
axis=1,
).max(axis=1)
return tr.rolling(period).mean()
def compute_action_labels(
df: pd.DataFrame,
*,
horizon: int = 32,
local_window: int = 24,
pullback_window: int = 20,
k_forward_atr: float = 0.75,
local_pct: float = 0.28,
pullback_mult: float = 0.55,
trend_mult: float = 1.05,
) -> pd.Series:
"""
Return a Series of int labels 0..4 aligned to df index.
Last `horizon` rows → HOLD (no forward path for buy/sell scoring).
"""
close = df["close"].values
high = df["high"].values
low = df["low"].values
n = len(df)
atr = atr_series(df, 14).values
labels = np.zeros(n, dtype=np.int64)
lw = local_window
pw = pullback_window
need = max(lw, pw) + 2
for t in range(n):
if t < need or t >= n - horizon:
labels[t] = 0
continue
a = atr[t]
if not np.isfinite(a) or a <= 0:
a = close[t] * 1e-4
sl = low[t + 1 : t + horizon + 1]
sh = high[t + 1 : t + horizon + 1]
fwd_max = float(np.max(sh))
fwd_min = float(np.min(sl))
up_move = (fwd_max - close[t]) / a
down_move = (close[t] - fwd_min) / a
loc_low = float(np.min(low[t - lw : t + 1]))
loc_high = float(np.max(high[t - lw : t + 1]))
rng = max(loc_high - loc_low, a * 0.15)
near_low = (close[t] - loc_low) / rng <= local_pct
near_high = (loc_high - close[t]) / rng <= local_pct
buy_sig = near_low and (up_move >= k_forward_atr) and (up_move >= down_move * 0.85)
sell_sig = near_high and (down_move >= k_forward_atr) and (down_move > up_move * 1.05)
# Past window [t-pw, t]
seg_h = high[t - pw : t + 1]
seg_l = low[t - pw : t + 1]
rh = float(np.max(seg_h))
rl = float(np.min(seg_l))
range_atr = (rh - rl) / a
pull_from_high = (rh - close[t]) / a
bounce_from_low = (close[t] - rl) / a
exit_long = (
range_atr >= trend_mult
and pull_from_high >= pullback_mult
and close[t] < close[t - 1]
)
exit_short = (
range_atr >= trend_mult
and bounce_from_low >= pullback_mult
and close[t] > close[t - 1]
)
if exit_long and not buy_sig:
labels[t] = 3
elif exit_short and not sell_sig:
labels[t] = 4
elif buy_sig and not sell_sig:
labels[t] = 1
elif sell_sig and not buy_sig:
labels[t] = 2
elif buy_sig and sell_sig:
labels[t] = 1 if up_move >= down_move else 2
else:
labels[t] = 0
return pd.Series(labels, index=df.index, name="action_label")
def class_weights(y: np.ndarray, n_classes: int = 5) -> dict[int, float]:
from sklearn.utils.class_weight import compute_class_weight
y_int = y.astype(int)
classes = np.arange(n_classes)
cw = compute_class_weight("balanced", classes=classes, y=y_int)
return {i: float(cw[i]) for i in range(n_classes)}
+210
View File
@@ -0,0 +1,210 @@
"""
XAUUSD M15 — ONNX action model (buy / sell short / close long / close short / hold).
Features: 24 dims — base 13 + RSI/frontline stack (see features.py, FRONTLINE_RSI_INTEGRATION.md).
Row order matches XAUUSD_M15_ActionEA.mq5 (row 0 = newest bar).
Data: MT5, 20082026 (limited by downloaded history).
"""
from __future__ import annotations
import json
import os
import pickle
import sys
from datetime import datetime, timedelta
import MetaTrader5 as mt5
import numpy as np
import pandas as pd
import tensorflow as tf
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from tensorflow import keras
from tensorflow.keras import layers
from tqdm import tqdm
import tf2onnx
import onnx
from labeling import class_weights, compute_action_labels
from features import NUM_FEATURES, prepare_features_full
NUM_CLASSES = 5
CLASS_NAMES = ["HOLD", "BUY", "SELL_SHORT", "CLOSE_LONG", "CLOSE_SHORT"]
def fetch_mt5_range(
symbol: str,
timeframe: int,
start_date: datetime,
end_date: datetime,
) -> pd.DataFrame:
if not mt5.initialize():
raise RuntimeError(f"MT5 init failed: {mt5.last_error()}")
info = mt5.symbol_info(symbol)
if info is None:
mt5.shutdown()
raise ValueError(f"Symbol {symbol} not found")
if not info.visible and not mt5.symbol_select(symbol, True):
mt5.shutdown()
raise ValueError(f"Cannot select {symbol}")
all_rows: list[dict] = []
chunk_days = 30
cur = start_date
while cur < end_date:
chunk_end = min(cur + timedelta(days=chunk_days), end_date)
rates = mt5.copy_rates_range(symbol, timeframe, cur, chunk_end)
if rates is not None and len(rates) > 1:
for row in rates:
all_rows.append({n: row[n] for n in rates.dtype.names})
cur = chunk_end
if not all_rows:
mt5.shutdown()
raise ValueError("No rates returned — download XAUUSD M15 in MT5 History Center")
df = pd.DataFrame(all_rows)
df["time"] = pd.to_datetime(df["time"], unit="s")
df = df.set_index("time").sort_index()
df = df[~df.index.duplicated(keep="first")]
return df
def create_sequences(
X: np.ndarray, y: np.ndarray, lookback: int
) -> tuple[np.ndarray, np.ndarray]:
"""
Window ends at bar i (chronological). Rows: newest-first inside each window
(matches MT5 series arrays in EA).
"""
xs, ys = [], []
for i in tqdm(range(lookback - 1, len(X)), desc="sequences"):
window = X[i - lookback + 1 : i + 1].copy()
window = window[::-1] # newest bar first → same as EA matrix row 0
xs.append(window)
ys.append(y[i])
return np.asarray(xs, dtype=np.float32), np.asarray(ys, dtype=np.int64)
def build_model(lookback: int, n_feat: int) -> keras.Model:
inp = layers.Input(shape=(lookback, n_feat))
x = layers.LSTM(96, return_sequences=True)(inp)
x = layers.Dropout(0.25)(x)
x = layers.LSTM(48)(x)
x = layers.Dropout(0.25)(x)
x = layers.Dense(32, activation="relu")(x)
out = layers.Dense(NUM_CLASSES, activation="softmax", name="action_probs")(x)
model = keras.Model(inp, out)
model.compile(
optimizer=keras.optimizers.Adam(1e-3),
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)
return model
def main() -> int:
symbol = os.environ.get("XAU_SYMBOL", "XAUUSD")
lookback = int(os.environ.get("XAU_LOOKBACK", "64"))
epochs = int(os.environ.get("XAU_EPOCHS", "40"))
batch_size = int(os.environ.get("XAU_BATCH", "64"))
start_date = datetime(2008, 1, 1)
end_date = datetime(2026, 12, 31)
out_dir = os.path.join(os.path.dirname(__file__), "models")
os.makedirs(out_dir, exist_ok=True)
onnx_path = os.path.join(out_dir, f"{symbol}_M15_action.onnx")
meta_path = os.path.join(out_dir, f"{symbol}_M15_action_meta.json")
print("Fetching MT5 data …")
try:
raw = fetch_mt5_range(symbol, mt5.TIMEFRAME_M15, start_date, end_date)
finally:
mt5.shutdown()
print(f"Bars: {len(raw)} range: {raw.index[0]}{raw.index[-1]}")
feat = prepare_features_full(raw)
labels_full = compute_action_labels(raw)
labels = labels_full.loc[feat.index]
y = labels.loc[feat.index].values.astype(np.int64)
X_raw = feat.values.astype(np.float32)
valid = np.isfinite(X_raw).all(axis=1) & (y >= 0) & (y < NUM_CLASSES)
X_raw = X_raw[valid]
y = y[valid]
print("Label counts:", {CLASS_NAMES[i]: int((y == i).sum()) for i in range(NUM_CLASSES)})
scaler = MinMaxScaler()
Xn = scaler.fit_transform(X_raw).astype(np.float32)
X_seq, y_seq = create_sequences(Xn, y, lookback)
if len(X_seq) < 500:
print("ERROR: Too few sequences — need more M15 history in MT5.")
return 1
X_train, X_val, y_train, y_val = train_test_split(
X_seq, y_seq, test_size=0.15, shuffle=False
)
cw = class_weights(y_train, NUM_CLASSES)
sample_w = np.array([cw[int(c)] for c in y_train], dtype=np.float32)
model = build_model(lookback, NUM_FEATURES)
model.summary()
model.fit(
X_train,
y_train,
sample_weight=sample_w,
validation_data=(X_val, y_val),
epochs=epochs,
batch_size=batch_size,
verbose=1,
callbacks=[
keras.callbacks.EarlyStopping(
monitor="val_loss", patience=8, restore_best_weights=True
),
keras.callbacks.ReduceLROnPlateau(
monitor="val_loss", factor=0.5, patience=4, min_lr=1e-6
),
],
)
spec = (tf.TensorSpec((None, lookback, NUM_FEATURES), tf.float32, name="input"),)
onnx_m, _ = tf2onnx.convert.from_keras(model, input_signature=spec, opset=13)
onnx.save_model(onnx_m, onnx_path)
with open(onnx_path.replace(".onnx", "_scaler.pkl"), "wb") as f:
pickle.dump(scaler, f)
meta = {
"symbol": symbol,
"timeframe": "M15",
"lookback": lookback,
"num_features": int(NUM_FEATURES),
"feature_columns": feat.columns.tolist(),
"num_classes": NUM_CLASSES,
"class_names": CLASS_NAMES,
"scaler_feature_min": scaler.data_min_.tolist(),
"scaler_feature_max": scaler.data_max_.tolist(),
"scaler_scale": scaler.scale_.tolist() if hasattr(scaler, "scale_") else None,
"notes": "MinMax in EA; row0=newest. See FRONTLINE_RSI_INTEGRATION.md.",
}
with open(meta_path, "w", encoding="utf-8") as f:
json.dump(meta, f, indent=2)
print(f"Saved: {onnx_path}")
print(f"Meta: {meta_path}")
print("\n--- Paste into EA InpFeatMinStr / InpFeatMaxStr (comma-separated, %d floats each) ---" % NUM_FEATURES)
print(",".join(f"{x:.8g}" for x in scaler.data_min_))
print(",".join(f"{x:.8g}" for x in scaler.data_max_))
return 0
if __name__ == "__main__":
sys.exit(main())
Binary file not shown.
@@ -0,0 +1,119 @@
{
"symbol": "XAUUSD",
"timeframe": "M15",
"lookback": 64,
"num_features": 24,
"feature_columns": [
"open",
"high",
"low",
"close",
"tick_volume",
"rsi",
"ema20_n",
"ema50_n",
"atr_n",
"price_change",
"high_low_ratio",
"volume_ma",
"volume_ratio",
"rsi7_n",
"rsi21_n",
"rsi_fast_slow_spread",
"rsi_velocity",
"rsi_accel",
"rsi_dist_mid_50",
"rsi_cross_overbought",
"rsi_cross_oversold",
"rsi_cross_50_up",
"rsi_cross_50_down",
"session_asian_utc"
],
"num_classes": 5,
"class_names": [
"HOLD",
"BUY",
"SELL_SHORT",
"CLOSE_LONG",
"CLOSE_SHORT"
],
"scaler_feature_min": [
1616.6700439453125,
1618.8499755859375,
1614.8199462890625,
1616.6800537109375,
0.0,
0.08494461327791214,
-0.03888450935482979,
-0.0373079888522625,
0.0001997762155951932,
-0.036351919174194336,
1.0,
0.00017494999337941408,
0.0,
0.02623281255364418,
0.12661120295524597,
-0.5349156260490417,
-2.0385215282440186,
-2.397653818130493,
2.298711478943005e-06,
0.0,
0.0,
0.0,
0.0,
0.0
],
"scaler_feature_max": [
5585.740234375,
5598.06005859375,
5577.580078125,
5585.740234375,
0.013647999614477158,
0.9493793845176697,
0.06255777180194855,
0.06892234832048416,
0.01657661236822605,
0.04392698407173157,
1.056401252746582,
0.010342299938201904,
5.7435832023620605,
0.9802423715591431,
0.9216755032539368,
0.5254908204078674,
1.544172763824463,
2.009221076965332,
0.8987588286399841,
1.0,
1.0,
1.0,
1.0,
1.0
],
"scaler_scale": [
0.00025194816407747567,
0.0002513061626814306,
0.00025234936038032174,
0.00025194883346557617,
73.27081298828125,
1.156825304031372,
9.85782241821289,
9.413507461547852,
61.06185531616211,
12.456572532653809,
17.7301025390625,
98.35404205322266,
0.17410734295845032,
1.0482075214385986,
1.2577598094940186,
0.9430346488952637,
0.2791195511817932,
0.22691819071769714,
1.112648367881775,
1.0,
1.0,
1.0,
1.0,
1.0
],
"notes": "MinMax in EA; row0=newest. See FRONTLINE_RSI_INTEGRATION.md."
}
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
numpy>=1.23
pandas>=2.0
MetaTrader5>=5.0.45
tensorflow>=2.14
tf2onnx>=1.16
onnx>=1.15
scikit-learn>=1.3
tqdm>=4.66
+25
View File
@@ -0,0 +1,25 @@
"""
Dynamic adverse risk (conceptual mirror of EA InpMaxAdverseATR).
For backtests in Python: given entry price, ATR series, and bid/ask path,
exit when (entry - bid)/atr >= max_adv for long.
"""
from __future__ import annotations
import numpy as np
def adverse_hit_long(
entry: float,
low_path: np.ndarray,
atr_path: np.ndarray,
max_adverse_atr: float,
) -> int | None:
"""Return first index where adverse >= threshold, else None."""
for i in range(len(low_path)):
atr = max(atr_path[i], entry * 1e-6)
adv = (entry - low_path[i]) / atr
if adv >= max_adverse_atr:
return i
return None

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

@@ -0,0 +1,508 @@
//+------------------------------------------------------------------+
//| RSI_SecretSauce_XAUUSD.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.01"
#property description "RSI Secret Sauce Strategy: Wait for RSI to leave 70/30 zone, then enter when it comes back in"
#property description "Based on momentum flip concept - not traditional overbought/oversold"
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
//--- Input Parameters
input group "=== Trading Settings ==="
input string InpSymbol = "XAUUSD"; // Trading Symbol (set was tuned on BTCUSD)
input double InpLotSize = 0.1; // Lot Size (Profiles/Tester/secret_sauce.set)
input int InpMagicNumber = 789012; // Magic Number
input int InpSlippage = 10; // Slippage in points
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M30; // Trading Timeframe (set value 30 = M30)
input group "=== RSI Settings ==="
input int InpRSIPeriod = 16; // RSI Period
input double InpRSIOverbought = 72.5; // RSI Overbought Level
input double InpRSIOversold = 32.5; // RSI Oversold Level
input int InpRSILookback = 60; // RSI Lookback for Peak/Bottom Detection
input group "=== Entry Logic ==="
input int InpPeakBars = 2; // Bars to confirm peak/bottom
input bool InpRequireDivergence = false; // Require divergence confirmation (optional)
input group "=== Risk Management ==="
input double InpStopLossATR = 2.75; // Stop Loss (ATR multiples)
input double InpTakeProfitATR = 5.0; // Take Profit (ATR multiples)
input int InpATRPeriod = 14; // ATR Period
input bool InpUseSwingStopLoss = false; // Use previous swing high/low for stop loss
input int InpSwingLookback = 30; // Bars to look back for swing points
input group "=== Position Management ==="
input int InpMaxPositions = 1; // Max Simultaneous Positions
input int InpMinBarsBetweenTrades = 7; // Min Bars Between Trades
//--- Global Variables
CTrade trade;
CPositionInfo positionInfo;
string actualSymbol;
int rsiHandle = INVALID_HANDLE;
int atrHandle = INVALID_HANDLE;
double rsiBuffer[];
double atrBuffer[];
double highBuffer[];
double lowBuffer[];
// RSI state tracking
bool rsiWasOverbought = false; // RSI was above 70
bool rsiWasOversold = false; // RSI was below 30
bool rsiBackInRange = false; // RSI came back into range
datetime lastRSIExitTime = 0; // When RSI left the range
datetime lastRSIReentryTime = 0; // When RSI came back in
// Trade tracking
datetime lastTradeTime = 0;
int barsSinceLastTrade = 0;
datetime lastBarTime = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Determine actual symbol
if(InpSymbol == "" || InpSymbol == NULL)
actualSymbol = _Symbol;
else
actualSymbol = InpSymbol;
// Check if symbol exists
if(!SymbolInfoInteger(actualSymbol, SYMBOL_SELECT))
{
Print("Error: Symbol ", actualSymbol, " not found. Using chart symbol.");
actualSymbol = _Symbol;
}
// Initialize RSI indicator
rsiHandle = iRSI(actualSymbol, InpTimeframe, InpRSIPeriod, PRICE_CLOSE);
if(rsiHandle == INVALID_HANDLE)
{
Print("Error creating RSI indicator");
return INIT_FAILED;
}
ArraySetAsSeries(rsiBuffer, true);
// Initialize ATR indicator
atrHandle = iATR(actualSymbol, InpTimeframe, InpATRPeriod);
if(atrHandle == INVALID_HANDLE)
{
Print("Error creating ATR indicator");
return INIT_FAILED;
}
ArraySetAsSeries(atrBuffer, true);
// Initialize price buffers
ArraySetAsSeries(highBuffer, true);
ArraySetAsSeries(lowBuffer, true);
// Set trade parameters
trade.SetExpertMagicNumber(InpMagicNumber);
trade.SetDeviationInPoints(InpSlippage);
trade.SetTypeFilling(ORDER_FILLING_FOK);
Print("=== RSI Secret Sauce Strategy Initialized ===");
Print("Symbol: ", actualSymbol);
Print("Timeframe: ", EnumToString(InpTimeframe));
Print("RSI Period: ", InpRSIPeriod, " | Overbought: ", InpRSIOverbought, " | Oversold: ", InpRSIOversold);
Print("Stop Loss: ", InpStopLossATR, "x ATR | Take Profit: ", InpTakeProfitATR, "x ATR");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(rsiHandle != INVALID_HANDLE)
IndicatorRelease(rsiHandle);
if(atrHandle != INVALID_HANDLE)
IndicatorRelease(atrHandle);
Print("Expert Advisor deinitialized. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check if we have enough bars
int requiredBars = MathMax(InpRSILookback, InpSwingLookback) + 10;
if(Bars(actualSymbol, InpTimeframe) < requiredBars)
return;
// Check if this is a new bar (wait for candle close)
datetime currentBarTime = iTime(actualSymbol, InpTimeframe, 0);
if(currentBarTime == lastBarTime)
return; // Still the same bar, don't process
lastBarTime = currentBarTime;
// Update indicators
if(!UpdateIndicators())
return;
// Update RSI state tracking
UpdateRSIState();
// Check existing positions
CheckExistingPositions();
// Check for entry signals
if(CanOpenNewPosition())
{
CheckEntrySignals();
}
}
//+------------------------------------------------------------------+
//| Update indicator values |
//+------------------------------------------------------------------+
bool UpdateIndicators()
{
// Update RSI (need enough bars for lookback)
int rsiBarsNeeded = InpRSILookback + 5;
if(CopyBuffer(rsiHandle, 0, 0, rsiBarsNeeded, rsiBuffer) < rsiBarsNeeded)
return false;
// Update ATR
if(CopyBuffer(atrHandle, 0, 0, 2, atrBuffer) < 2)
return false;
// Update price buffers for swing detection
if(CopyHigh(actualSymbol, InpTimeframe, 0, InpSwingLookback + 5, highBuffer) < InpSwingLookback + 5)
return false;
if(CopyLow(actualSymbol, InpTimeframe, 0, InpSwingLookback + 5, lowBuffer) < InpSwingLookback + 5)
return false;
return true;
}
//+------------------------------------------------------------------+
//| Update RSI state tracking |
//+------------------------------------------------------------------+
void UpdateRSIState()
{
double rsiCurrent = rsiBuffer[0];
double rsiPrev = rsiBuffer[1];
// Check if RSI left overbought zone (was above 70, now below 70)
if(rsiPrev >= InpRSIOverbought && rsiCurrent < InpRSIOverbought)
{
rsiWasOverbought = true;
rsiBackInRange = true;
lastRSIExitTime = TimeCurrent();
lastRSIReentryTime = TimeCurrent();
Print(TimeToString(TimeCurrent()), " - RSI left overbought zone (", rsiPrev, " -> ", rsiCurrent, ")");
}
// Check if RSI left oversold zone (was below 30, now above 30)
if(rsiPrev <= InpRSIOversold && rsiCurrent > InpRSIOversold)
{
rsiWasOversold = true;
rsiBackInRange = true;
lastRSIExitTime = TimeCurrent();
lastRSIReentryTime = TimeCurrent();
Print(TimeToString(TimeCurrent()), " - RSI left oversold zone (", rsiPrev, " -> ", rsiCurrent, ")");
}
// Reset flags if RSI goes back to extreme
if(rsiCurrent >= InpRSIOverbought)
{
rsiWasOverbought = false;
rsiBackInRange = false;
}
if(rsiCurrent <= InpRSIOversold)
{
rsiWasOversold = false;
rsiBackInRange = false;
}
}
//+------------------------------------------------------------------+
//| Check if we can open a new position |
//+------------------------------------------------------------------+
bool CanOpenNewPosition()
{
// Check max positions
int positionCount = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(positionInfo.SelectByIndex(i))
{
if(positionInfo.Symbol() == actualSymbol && positionInfo.Magic() == InpMagicNumber)
positionCount++;
}
}
if(positionCount >= InpMaxPositions)
return false;
// Check minimum bars between trades
if(lastTradeTime > 0)
{
int barsSince = Bars(actualSymbol, InpTimeframe, lastTradeTime, TimeCurrent());
if(barsSince < InpMinBarsBetweenTrades)
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Check for entry signals |
//+------------------------------------------------------------------+
void CheckEntrySignals()
{
// LONG Entry: RSI was overbought (>70), came back in range, now look for peak
if(rsiWasOverbought && rsiBackInRange)
{
// Check if RSI is back in normal range (below 70)
if(rsiBuffer[0] < InpRSIOverbought)
{
// Look for a peak in RSI after re-entry
if(IsRSIPeak())
{
Print(TimeToString(TimeCurrent()), " - LONG Signal: RSI peak detected after leaving overbought zone");
OpenPosition(POSITION_TYPE_BUY);
}
}
}
// SHORT Entry: RSI was oversold (<30), came back in range, now look for bottom
if(rsiWasOversold && rsiBackInRange)
{
// Check if RSI is back in normal range (above 30)
if(rsiBuffer[0] > InpRSIOversold)
{
// Look for a bottom in RSI after re-entry
if(IsRSIBottom())
{
Print(TimeToString(TimeCurrent()), " - SHORT Signal: RSI bottom detected after leaving oversold zone");
OpenPosition(POSITION_TYPE_SELL);
}
}
}
}
//+------------------------------------------------------------------+
//| Check if RSI is forming a peak (for LONG entry) |
//+------------------------------------------------------------------+
bool IsRSIPeak()
{
// We need at least InpPeakBars + 1 bars to confirm a peak
if(ArraySize(rsiBuffer) < InpPeakBars + 2)
return false;
// Check if current RSI is higher than previous bars (forming a peak)
double currentRSI = rsiBuffer[0];
bool isPeak = true;
// Check if current is higher than the next few bars
for(int i = 1; i <= InpPeakBars; i++)
{
if(rsiBuffer[i] >= currentRSI)
{
isPeak = false;
break;
}
}
// Also check if previous bar was lower (confirming upward movement before peak)
if(rsiBuffer[1] >= currentRSI)
isPeak = false;
return isPeak;
}
//+------------------------------------------------------------------+
//| Check if RSI is forming a bottom (for SHORT entry) |
//+------------------------------------------------------------------+
bool IsRSIBottom()
{
// We need at least InpPeakBars + 1 bars to confirm a bottom
if(ArraySize(rsiBuffer) < InpPeakBars + 2)
return false;
// Check if current RSI is lower than previous bars (forming a bottom)
double currentRSI = rsiBuffer[0];
bool isBottom = true;
// Check if current is lower than the next few bars
for(int i = 1; i <= InpPeakBars; i++)
{
if(rsiBuffer[i] <= currentRSI)
{
isBottom = false;
break;
}
}
// Also check if previous bar was higher (confirming downward movement before bottom)
if(rsiBuffer[1] <= currentRSI)
isBottom = false;
return isBottom;
}
//+------------------------------------------------------------------+
//| Open position |
//+------------------------------------------------------------------+
void OpenPosition(ENUM_POSITION_TYPE type)
{
double price = (type == POSITION_TYPE_BUY) ?
SymbolInfoDouble(actualSymbol, SYMBOL_ASK) :
SymbolInfoDouble(actualSymbol, SYMBOL_BID);
if(price <= 0)
return;
// Calculate stop loss and take profit
double sl = 0.0, tp = 0.0;
if(!CalculateStops(price, type, sl, tp))
{
Print("Error: Failed to calculate stops");
return;
}
string comment = "RSI_Secret_" + (type == POSITION_TYPE_BUY ? "LONG" : "SHORT");
bool result = false;
if(type == POSITION_TYPE_BUY)
result = trade.Buy(InpLotSize, actualSymbol, 0, sl, tp, comment);
else
result = trade.Sell(InpLotSize, actualSymbol, 0, sl, tp, comment);
if(result)
{
lastTradeTime = TimeCurrent();
ulong ticket = trade.ResultOrder();
Print(TimeToString(TimeCurrent()), " - Position opened: ", comment, " Ticket: ", ticket,
" Price: ", price, " SL: ", sl, " TP: ", tp);
// Reset RSI state after opening position
if(type == POSITION_TYPE_BUY)
rsiWasOverbought = false;
else
rsiWasOversold = false;
rsiBackInRange = false;
}
else
{
Print("Failed to open position: ", comment, " Error: ",
trade.ResultRetcode(), " - ", trade.ResultRetcodeDescription());
}
}
//+------------------------------------------------------------------+
//| Calculate stop loss and take profit |
//+------------------------------------------------------------------+
bool CalculateStops(double price, ENUM_POSITION_TYPE type, double &sl, double &tp)
{
double atrValue = atrBuffer[0];
if(atrValue <= 0)
atrValue = price * 0.01; // Fallback: 1% of price
double slDistance = atrValue * InpStopLossATR;
double tpDistance = atrValue * InpTakeProfitATR;
int digits = (int)SymbolInfoInteger(actualSymbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(actualSymbol, SYMBOL_POINT);
int stopsLevel = (int)SymbolInfoInteger(actualSymbol, SYMBOL_TRADE_STOPS_LEVEL);
double minStopDistance = MathMax(stopsLevel * point, point * 10);
// Use swing-based stop loss if enabled
if(InpUseSwingStopLoss)
{
double swingStop = GetSwingStopLoss(price, type);
if(swingStop > 0)
{
if(type == POSITION_TYPE_BUY)
{
if(swingStop < price && (price - swingStop) > minStopDistance)
slDistance = price - swingStop;
}
else
{
if(swingStop > price && (swingStop - price) > minStopDistance)
slDistance = swingStop - price;
}
}
}
// Ensure minimum distance
if(slDistance < minStopDistance)
slDistance = minStopDistance;
if(tpDistance < minStopDistance)
tpDistance = minStopDistance;
if(type == POSITION_TYPE_BUY)
{
sl = NormalizeDouble(price - slDistance, digits);
tp = NormalizeDouble(price + tpDistance, digits);
}
else
{
sl = NormalizeDouble(price + slDistance, digits);
tp = NormalizeDouble(price - tpDistance, digits);
}
return true;
}
//+------------------------------------------------------------------+
//| Get swing-based stop loss (previous swing high/low) |
//+------------------------------------------------------------------+
double GetSwingStopLoss(double currentPrice, ENUM_POSITION_TYPE type)
{
// For LONG: find previous swing low
// For SHORT: find previous swing high
if(type == POSITION_TYPE_BUY)
{
// Find the lowest low in the lookback period
double lowestLow = lowBuffer[0];
for(int i = 1; i < InpSwingLookback && i < ArraySize(lowBuffer); i++)
{
if(lowBuffer[i] < lowestLow)
lowestLow = lowBuffer[i];
}
return lowestLow;
}
else
{
// Find the highest high in the lookback period
double highestHigh = highBuffer[0];
for(int i = 1; i < InpSwingLookback && i < ArraySize(highBuffer); i++)
{
if(highBuffer[i] > highestHigh)
highestHigh = highBuffer[i];
}
return highestHigh;
}
}
//+------------------------------------------------------------------+
//| Check existing positions |
//+------------------------------------------------------------------+
void CheckExistingPositions()
{
// Position management can be added here if needed
// For now, positions are managed by TP/SL
}
//+------------------------------------------------------------------+
@@ -259,7 +259,7 @@ bool InitRSIReversalAsian(RSIReversalAsianData& data, string symbol,
double rsi[];
ArraySetAsSeries(rsi, true);
int retryCount = 0;
retryCount = 0;
bool rsiInitialized = false;
while(retryCount < 10 && !rsiInitialized)
@@ -0,0 +1,159 @@
//+------------------------------------------------------------------+
//| MagicNumberHelpers.mqh |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
//+------------------------------------------------------------------+
//| Select position by symbol and magic number |
//+------------------------------------------------------------------+
bool PositionSelectByMagic(string symbol, ulong magic_number)
{
// First try to find position by symbol
if(!PositionSelect(symbol))
return false;
// Check if the selected position has the correct magic number
if(PositionGetInteger(POSITION_MAGIC) != magic_number)
{
// Position exists but wrong magic number, search all positions
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionGetTicket(i) > 0)
{
if(PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == magic_number)
{
return true;
}
}
}
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Select position by ticket and verify magic number and symbol |
//+------------------------------------------------------------------+
bool PositionSelectByTicketAndMagic(ulong ticket, ulong magic_number)
{
if(!PositionSelectByTicket(ticket))
return false;
return (PositionGetInteger(POSITION_MAGIC) == magic_number);
}
//+------------------------------------------------------------------+
//| Select position by ticket and verify symbol, magic number |
//+------------------------------------------------------------------+
bool PositionSelectByTicketSymbolAndMagic(ulong ticket, string symbol, ulong magic_number)
{
if(!PositionSelectByTicket(ticket))
return false;
return (PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == magic_number);
}
//+------------------------------------------------------------------+
//| Check if position exists with correct magic number |
//+------------------------------------------------------------------+
bool PositionExistsByMagic(string symbol, ulong magic_number)
{
return PositionSelectByMagic(symbol, magic_number);
}
//+------------------------------------------------------------------+
//| Get position ticket by symbol and magic number |
//+------------------------------------------------------------------+
ulong GetPositionTicketByMagic(string symbol, ulong magic_number)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0)
{
if(PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == magic_number)
{
return ticket;
}
}
}
return 0;
}
//+------------------------------------------------------------------+
//| Close position by symbol and magic number |
//+------------------------------------------------------------------+
bool ClosePositionByMagic(CTrade &trade_obj, string symbol, ulong magic_number)
{
ulong ticket = GetPositionTicketByMagic(symbol, magic_number);
if(ticket == 0)
return false;
return trade_obj.PositionClose(ticket);
}
//+------------------------------------------------------------------+
//| Modify position by symbol and magic number |
//+------------------------------------------------------------------+
bool ModifyPositionByMagic(CTrade &trade_obj, string symbol, ulong magic_number,
double sl, double tp)
{
ulong ticket = GetPositionTicketByMagic(symbol, magic_number);
if(ticket == 0)
return false;
return trade_obj.PositionModify(ticket, sl, tp);
}
//+------------------------------------------------------------------+
//| Get position profit by symbol and magic number |
//+------------------------------------------------------------------+
double GetPositionProfitByMagic(string symbol, ulong magic_number)
{
if(!PositionSelectByMagic(symbol, magic_number))
return 0.0;
return PositionGetDouble(POSITION_PROFIT);
}
//+------------------------------------------------------------------+
//| Get position type by symbol and magic number |
//+------------------------------------------------------------------+
ENUM_POSITION_TYPE GetPositionTypeByMagic(string symbol, ulong magic_number)
{
if(!PositionSelectByMagic(symbol, magic_number))
return WRONG_VALUE;
return (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
//+------------------------------------------------------------------+
//| Count positions by symbol and magic number |
//+------------------------------------------------------------------+
int CountPositionsByMagic(string symbol, ulong magic_number)
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0)
{
if(PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == magic_number)
{
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
@@ -0,0 +1,75 @@
# Pepperstone US - Symbol Setup Guide
## Finding Correct Symbol Names in MetaTrader 5
### Step-by-Step Instructions:
1. **Open Market Watch Window**
- Press `Ctrl+M` or go to `View > Market Watch`
2. **Show All Symbols**
- Right-click in the Market Watch window
- Select `Show All` or `Symbols`
- This shows all available symbols from your broker
3. **Search for Your Symbols**
- Use the search box in the Market Watch window
- Search for: "AAPL", "MSFT", "NVDA", "TSLA", "BTCUSD", "XAUUSD"
4. **Note the Exact Symbol Name**
- The symbol name shown in Market Watch is what you need to use
- Common formats for Pepperstone US:
- Stocks: `AAPL.US`, `MSFT.US`, `NVDA.US`, `TSLA.US`
- Or: `NASDAQ:AAPL`, `NASDAQ:MSFT`, etc.
- Or: Just `AAPL`, `MSFT`, etc. (if available)
5. **Add to Market Watch**
- Double-click the symbol to add it to your Market Watch
- Or right-click and select `Show`
6. **Update EA Inputs**
- Open the EA inputs in MetaTrader 5
- Update each symbol parameter with the exact name from Market Watch
## Common Pepperstone US Symbol Formats
### US Stocks:
- **Apple**: `AAPL.US` or `NASDAQ:AAPL` or `AAPL`
- **Microsoft**: `MSFT.US` or `NASDAQ:MSFT` or `MSFT`
- **NVIDIA**: `NVDA.US` or `NASDAQ:NVDA` or `NVDA`
- **Tesla**: `TSLA.US` or `NASDAQ:TSLA` or `TSLA`
### Cryptocurrencies:
- **Bitcoin**: `BTCUSD` or `BTC/USD` or `BTCUSD.c`
### Precious Metals:
- **Gold**: `XAUUSD` or `GOLD` or `XAU/USD`
## Important Notes:
1. **Symbol Names are Case-Sensitive**: Use exact capitalization
2. **Add Symbols to Market Watch**: Symbols must be in Market Watch for the EA to access them
3. **Check Trading Hours**: US stocks trade during US market hours (9:30 AM - 4:00 PM ET)
4. **CFD vs Stock**: Pepperstone offers CFDs on stocks, not actual stocks
5. **Spread**: Check the spread for each symbol - some may have wider spreads
## Troubleshooting:
### If Symbol Not Found:
1. Check if you're connected to Pepperstone US server
2. Verify your account type supports the symbol
3. Contact Pepperstone support for symbol availability
4. Check if symbol requires special account permissions
### If EA Shows "Symbol Not Available":
1. Make sure symbol is added to Market Watch
2. Verify symbol name matches exactly (including dots, colons, etc.)
3. Check broker connection status
4. Try different symbol format variations
## Testing Symbols:
You can test if a symbol works by:
1. Opening a chart with that symbol
2. If chart opens successfully, the symbol name is correct
3. Use that exact symbol name in the EA inputs
@@ -0,0 +1,607 @@
//+------------------------------------------------------------------+
//| PerformanceEvaluator.mqh |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
//+------------------------------------------------------------------+
//| Performance Metrics Structure |
//+------------------------------------------------------------------+
struct StrategyPerformance {
string strategyName;
string symbol; // Store symbol to determine if it's a stock
int magicNumber;
double initialLotSize;
double currentLotSize;
double quarterProfit;
double quarterTrades;
double quarterWins;
double quarterLosses;
double maxDrawdown;
double winRate;
datetime quarterStart;
datetime quarterEnd;
bool isActive;
bool inPenaltyMode; // True if strategy is in penalty (worst performer)
double lotSizeBeforePenalty; // Store lot size before penalty
datetime penaltyStartTime; // When penalty started
};
//+------------------------------------------------------------------+
//| Global Performance Tracking |
//+------------------------------------------------------------------+
StrategyPerformance strategyPerformances[];
int totalStrategies = 0;
datetime lastMonthCheck = 0;
datetime currentMonthStart = 0;
datetime currentMonthEnd = 0;
//+------------------------------------------------------------------+
//| Performance Adjustment Parameters |
//+------------------------------------------------------------------+
input group "=== Performance Evaluation Settings ==="
input bool PE_EnableAutoAdjustment = true; // Enable automatic lot size adjustment
input double PE_LotSizeIncreasePercent = 10.0; // % increase for top-ranked strategies
input double PE_LotSizeDecreasePercent = 10.0; // % decrease for bottom-ranked strategies
input double PE_MinLotSize = 0.01; // Minimum lot size for forex/crypto
input double PE_MinLotSizeStocks = 5.0; // Minimum lot size for stocks (5-10 range)
input double PE_MaxLotSize = 100.0; // Maximum lot size after adjustment
input int PE_TopPerformersCount = 3; // Number of top strategies to increase lot size
input int PE_BottomPerformersCount = 3; // Number of bottom strategies to decrease lot size
input bool PE_UseWinRateWeight = true; // Consider win rate in ranking (50% profit, 50% win rate)
input bool PE_EnableBlitzPlay = true; // Enable blitz play: worst performer gets minimum lot size penalty
input bool PE_EnableLogging = true; // Enable performance logging
//+------------------------------------------------------------------+
//| Initialize Performance Tracking |
//+------------------------------------------------------------------+
void InitPerformanceTracking()
{
// Calculate current month dates
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
// Determine month start (first day of current month)
dt.day = 1;
dt.hour = 0;
dt.min = 0;
dt.sec = 0;
currentMonthStart = StructToTime(dt);
// Calculate month end (first day of next month - 1 second)
dt.mon += 1;
if(dt.mon > 12)
{
dt.mon = 1;
dt.year++;
}
currentMonthEnd = StructToTime(dt) - 1; // End of last day of month
lastMonthCheck = TimeCurrent();
if(PE_EnableLogging)
{
Print("Performance Evaluator: Initialized");
Print("Current Month Start: ", TimeToString(currentMonthStart));
Print("Current Month End: ", TimeToString(currentMonthEnd));
}
}
//+------------------------------------------------------------------+
//| Check if Symbol is a Stock |
//+------------------------------------------------------------------+
bool IsStockSymbol(string symbol)
{
// Check if symbol contains common stock indicators
if(StringFind(symbol, ".US") >= 0) return true;
if(StringFind(symbol, "NASDAQ:") >= 0) return true;
if(StringFind(symbol, "NYSE:") >= 0) return true;
// Note: Symbol category check removed to avoid enum conversion issues
// String-based checks (.US, NASDAQ:, NYSE:, common tickers) are sufficient
// Common stock tickers (without .US suffix)
string commonStocks[] = {"AAPL", "NVDA", "TSLA", "GOOGL", "AMZN", "META", "AMD", "NFLX"};
for(int i = 0; i < ArraySize(commonStocks); i++)
{
if(StringFind(symbol, commonStocks[i]) == 0) return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Get Minimum Lot Size for Symbol |
//+------------------------------------------------------------------+
double GetMinLotSizeForSymbol(string symbol)
{
if(IsStockSymbol(symbol))
return PE_MinLotSizeStocks;
else
return PE_MinLotSize;
}
//+------------------------------------------------------------------+
//| Register Strategy for Performance Tracking |
//+------------------------------------------------------------------+
void RegisterStrategy(string strategyName, int magicNumber, double initialLotSize, string symbol = "")
{
// Check if strategy already registered
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].strategyName == strategyName &&
strategyPerformances[i].magicNumber == magicNumber)
{
if(PE_EnableLogging)
Print("Performance Evaluator: Strategy '", strategyName, "' already registered");
return;
}
}
// Add new strategy
int newSize = ArraySize(strategyPerformances) + 1;
ArrayResize(strategyPerformances, newSize);
strategyPerformances[newSize - 1].strategyName = strategyName;
strategyPerformances[newSize - 1].symbol = symbol;
strategyPerformances[newSize - 1].magicNumber = magicNumber;
strategyPerformances[newSize - 1].initialLotSize = initialLotSize;
// Start with minimum lot size for safety (symbol-specific minimum)
double minLot = GetMinLotSizeForSymbol(symbol);
strategyPerformances[newSize - 1].currentLotSize = minLot;
strategyPerformances[newSize - 1].quarterProfit = 0.0;
strategyPerformances[newSize - 1].quarterTrades = 0;
strategyPerformances[newSize - 1].quarterWins = 0;
strategyPerformances[newSize - 1].quarterLosses = 0;
strategyPerformances[newSize - 1].maxDrawdown = 0.0;
strategyPerformances[newSize - 1].winRate = 0.0;
strategyPerformances[newSize - 1].quarterStart = currentMonthStart;
strategyPerformances[newSize - 1].quarterEnd = currentMonthEnd;
strategyPerformances[newSize - 1].isActive = true;
strategyPerformances[newSize - 1].inPenaltyMode = false;
strategyPerformances[newSize - 1].lotSizeBeforePenalty = initialLotSize;
strategyPerformances[newSize - 1].penaltyStartTime = 0;
totalStrategies = newSize;
if(PE_EnableLogging)
Print("Performance Evaluator: Registered strategy '", strategyName,
"' (Magic: ", magicNumber, ", Initial Lot: ", initialLotSize, ")");
}
//+------------------------------------------------------------------+
//| Update Strategy Performance Metrics |
//+------------------------------------------------------------------+
void UpdateStrategyPerformance(string strategyName, int magicNumber)
{
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].strategyName == strategyName &&
strategyPerformances[i].magicNumber == magicNumber &&
strategyPerformances[i].isActive)
{
// Calculate performance for current quarter
double totalProfit = 0.0;
int totalTrades = 0;
int wins = 0;
int losses = 0;
double maxDD = 0.0;
double peakBalance = 0.0;
// Scan all closed deals in current quarter
datetime quarterStart = strategyPerformances[i].quarterStart;
datetime quarterEnd = strategyPerformances[i].quarterEnd;
// Select history for the quarter
if(HistorySelect(quarterStart, quarterEnd))
{
int totalDeals = HistoryDealsTotal();
for(int j = 0; j < totalDeals; j++)
{
ulong ticket = HistoryDealGetTicket(j);
if(ticket > 0)
{
long dealMagic = HistoryDealGetInteger(ticket, DEAL_MAGIC);
if(dealMagic == magicNumber)
{
double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
double swap = HistoryDealGetDouble(ticket, DEAL_SWAP);
double commission = HistoryDealGetDouble(ticket, DEAL_COMMISSION);
double totalDealProfit = profit + swap + commission;
totalProfit += totalDealProfit;
totalTrades++;
if(totalDealProfit > 0)
wins++;
else if(totalDealProfit < 0)
losses++;
}
}
}
}
// Calculate win rate
double winRate = 0.0;
if(totalTrades > 0)
winRate = (double)wins / (double)totalTrades * 100.0;
// Update metrics
strategyPerformances[i].quarterProfit = totalProfit;
strategyPerformances[i].quarterTrades = totalTrades;
strategyPerformances[i].quarterWins = wins;
strategyPerformances[i].quarterLosses = losses;
strategyPerformances[i].winRate = winRate;
break;
}
}
}
//+------------------------------------------------------------------+
//| Strategy Ranking Structure |
//+------------------------------------------------------------------+
struct StrategyRank {
int index;
double score;
};
//+------------------------------------------------------------------+
//| Calculate Strategy Score for Ranking |
//+------------------------------------------------------------------+
double CalculateStrategyScore(int strategyIndex)
{
double profit = strategyPerformances[strategyIndex].quarterProfit;
double winRate = strategyPerformances[strategyIndex].winRate;
double trades = strategyPerformances[strategyIndex].quarterTrades;
// Normalize profit (scale to 0-100 range, assuming max profit of $1000)
double normalizedProfit = MathMin(profit / 10.0, 100.0);
if(profit < 0) normalizedProfit = profit / 5.0; // Penalize losses more
// Calculate score
double score = 0.0;
if(PE_UseWinRateWeight)
{
// 50% profit, 50% win rate (if enough trades)
if(trades >= 5)
score = (normalizedProfit * 0.5) + (winRate * 0.5);
else
score = normalizedProfit; // Not enough trades, use profit only
}
else
{
// Profit only
score = normalizedProfit;
}
return score;
}
//+------------------------------------------------------------------+
//| Check if Month Ended and Evaluate Performance |
//+------------------------------------------------------------------+
void CheckMonthEnd()
{
datetime now = TimeCurrent();
// Check if we've entered a new month
if(now >= currentMonthEnd)
{
if(PE_EnableLogging)
Print("Performance Evaluator: Month ended. Evaluating and ranking strategies...");
// Update performance metrics for all strategies
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive)
{
UpdateStrategyPerformance(strategyPerformances[i].strategyName,
strategyPerformances[i].magicNumber);
}
}
// Rank strategies
int activeCount = 0;
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive)
activeCount++;
}
if(activeCount > 0)
{
// Create ranking array
StrategyRank ranks[];
ArrayResize(ranks, activeCount);
int rankIndex = 0;
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive)
{
ranks[rankIndex].index = i;
ranks[rankIndex].score = CalculateStrategyScore(i);
rankIndex++;
}
}
// Sort by score (descending - highest score first)
for(int i = 0; i < activeCount - 1; i++)
{
for(int j = i + 1; j < activeCount; j++)
{
if(ranks[j].score > ranks[i].score)
{
StrategyRank temp = ranks[i];
ranks[i] = ranks[j];
ranks[j] = temp;
}
}
}
// Adjust lot sizes based on ranking
if(PE_EnableAutoAdjustment)
{
// Increase top performers (skip if in penalty mode)
int topCount = MathMin(PE_TopPerformersCount, activeCount);
for(int i = 0; i < topCount; i++)
{
int strategyIdx = ranks[i].index;
// Skip if strategy is in penalty mode
if(strategyPerformances[strategyIdx].inPenaltyMode)
continue;
double oldLotSize = strategyPerformances[strategyIdx].currentLotSize;
double newLotSize = oldLotSize * (1.0 + PE_LotSizeIncreasePercent / 100.0);
if(newLotSize > PE_MaxLotSize)
newLotSize = PE_MaxLotSize;
strategyPerformances[strategyIdx].currentLotSize = newLotSize;
if(PE_EnableLogging)
Print("Performance Evaluator: Rank #", (i+1), " - Increasing '",
strategyPerformances[strategyIdx].strategyName,
"' lot size from ", oldLotSize, " to ", newLotSize,
" (Score: ", DoubleToString(ranks[i].score, 2),
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%)");
}
// Decrease bottom performers (skip worst one if blitz play is enabled)
int bottomCount = MathMin(PE_BottomPerformersCount, activeCount);
int startIdx = activeCount - bottomCount;
// If blitz play is enabled, skip the worst performer (it will get minimum penalty)
if(PE_EnableBlitzPlay && activeCount > 0)
startIdx = activeCount - bottomCount + 1;
for(int i = startIdx; i < activeCount; i++)
{
int strategyIdx = ranks[i].index;
// Skip if strategy is in penalty mode
if(strategyPerformances[strategyIdx].inPenaltyMode)
continue;
double oldLotSize = strategyPerformances[strategyIdx].currentLotSize;
double newLotSize = oldLotSize * (1.0 - PE_LotSizeDecreasePercent / 100.0);
// Use symbol-specific minimum lot size
double minLot = GetMinLotSizeForSymbol(strategyPerformances[strategyIdx].symbol);
if(newLotSize < minLot)
newLotSize = minLot;
strategyPerformances[strategyIdx].currentLotSize = newLotSize;
if(PE_EnableLogging)
Print("Performance Evaluator: Rank #", (i+1), " - Decreasing '",
strategyPerformances[strategyIdx].strategyName,
"' lot size from ", oldLotSize, " to ", newLotSize,
" (Score: ", DoubleToString(ranks[i].score, 2),
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%)");
}
}
// Blitz Play: Apply penalty to worst performer
if(PE_EnableBlitzPlay && activeCount > 0)
{
// Find worst performer (last in ranking)
int worstIdx = ranks[activeCount - 1].index;
// Remove penalty from previous worst performer (if any)
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive && strategyPerformances[i].inPenaltyMode)
{
// Check if penalty period has passed (one month)
if(now - strategyPerformances[i].penaltyStartTime >= 2592000) // ~30 days
{
// Restore lot size to before penalty
strategyPerformances[i].currentLotSize = strategyPerformances[i].lotSizeBeforePenalty;
strategyPerformances[i].inPenaltyMode = false;
strategyPerformances[i].penaltyStartTime = 0;
if(PE_EnableLogging)
Print("Blitz Play: Penalty removed from '", strategyPerformances[i].strategyName,
"'. Lot size restored to ", strategyPerformances[i].currentLotSize);
}
}
}
// Apply penalty to new worst performer
if(!strategyPerformances[worstIdx].inPenaltyMode)
{
strategyPerformances[worstIdx].lotSizeBeforePenalty = strategyPerformances[worstIdx].currentLotSize;
// Use symbol-specific minimum lot size
double minLot = GetMinLotSizeForSymbol(strategyPerformances[worstIdx].symbol);
strategyPerformances[worstIdx].currentLotSize = minLot;
strategyPerformances[worstIdx].inPenaltyMode = true;
strategyPerformances[worstIdx].penaltyStartTime = now;
if(PE_EnableLogging)
Print("Blitz Play: WORST PERFORMER - '", strategyPerformances[worstIdx].strategyName,
"' penalized! Lot size reduced from ", strategyPerformances[worstIdx].lotSizeBeforePenalty,
" to minimum ", minLot, " (Score: ", DoubleToString(ranks[activeCount - 1].score, 2),
", Profit: $", DoubleToString(strategyPerformances[worstIdx].quarterProfit, 2), ")");
}
}
// Log performance report
if(PE_EnableLogging)
{
Print("=== Monthly Performance Ranking ===");
for(int i = 0; i < activeCount; i++)
{
int strategyIdx = ranks[i].index;
Print("Rank #", (i+1), ": ", strategyPerformances[strategyIdx].strategyName,
" - Score: ", DoubleToString(ranks[i].score, 2),
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%",
", Trades: ", (int)strategyPerformances[strategyIdx].quarterTrades,
", Lot Size: ", DoubleToString(strategyPerformances[strategyIdx].currentLotSize, 2));
}
Print("===================================");
}
}
// Reset month metrics for all strategies
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive)
{
strategyPerformances[i].quarterProfit = 0.0;
strategyPerformances[i].quarterTrades = 0;
strategyPerformances[i].quarterWins = 0;
strategyPerformances[i].quarterLosses = 0;
strategyPerformances[i].maxDrawdown = 0.0;
strategyPerformances[i].winRate = 0.0;
}
}
// Update month dates
MqlDateTime dt;
TimeToStruct(now, dt);
// First day of current month
dt.day = 1;
dt.hour = 0;
dt.min = 0;
dt.sec = 0;
currentMonthStart = StructToTime(dt);
// First day of next month - 1 second
dt.mon += 1;
if(dt.mon > 12)
{
dt.mon = 1;
dt.year++;
}
currentMonthEnd = StructToTime(dt) - 1;
// Update month dates for all strategies
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
strategyPerformances[i].quarterStart = currentMonthStart;
strategyPerformances[i].quarterEnd = currentMonthEnd;
}
lastMonthCheck = now;
}
}
//+------------------------------------------------------------------+
//| Get Current Lot Size for Strategy |
//+------------------------------------------------------------------+
double GetStrategyLotSize(string strategyName, int magicNumber)
{
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].strategyName == strategyName &&
strategyPerformances[i].magicNumber == magicNumber &&
strategyPerformances[i].isActive)
{
return strategyPerformances[i].currentLotSize;
}
}
return 0.0;
}
//+------------------------------------------------------------------+
//| Process Performance Evaluation (call from OnTick) |
//+------------------------------------------------------------------+
void ProcessPerformanceEvaluation()
{
// Check if month ended
CheckMonthEnd();
// Check for penalty expiration (blitz play)
if(PE_EnableBlitzPlay)
{
datetime now = TimeCurrent();
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive && strategyPerformances[i].inPenaltyMode)
{
// Check if penalty period has passed (one month = ~30 days)
if(now - strategyPerformances[i].penaltyStartTime >= 2592000)
{
// Restore lot size to before penalty
strategyPerformances[i].currentLotSize = strategyPerformances[i].lotSizeBeforePenalty;
strategyPerformances[i].inPenaltyMode = false;
strategyPerformances[i].penaltyStartTime = 0;
if(PE_EnableLogging)
Print("Blitz Play: Penalty expired for '", strategyPerformances[i].strategyName,
"'. Lot size restored to ", strategyPerformances[i].currentLotSize);
}
}
}
}
// Update performance metrics periodically (every hour)
static datetime lastUpdate = 0;
if(TimeCurrent() - lastUpdate >= 3600)
{
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive)
{
UpdateStrategyPerformance(strategyPerformances[i].strategyName,
strategyPerformances[i].magicNumber);
}
}
lastUpdate = TimeCurrent();
}
}
//+------------------------------------------------------------------+
//| Get Performance Summary |
//+------------------------------------------------------------------+
string GetPerformanceSummary()
{
string summary = "\n=== Performance Summary ===\n";
summary += "Current Month: " + TimeToString(currentMonthStart) + " to " + TimeToString(currentMonthEnd) + "\n\n";
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive)
{
summary += strategyPerformances[i].strategyName + ":\n";
summary += " Profit: $" + DoubleToString(strategyPerformances[i].quarterProfit, 2) + "\n";
summary += " Trades: " + IntegerToString((int)strategyPerformances[i].quarterTrades) + "\n";
summary += " Win Rate: " + DoubleToString(strategyPerformances[i].winRate, 2) + "%\n";
summary += " Lot Size: " + DoubleToString(strategyPerformances[i].currentLotSize, 2) + "\n\n";
}
}
return summary;
}
//+------------------------------------------------------------------+
@@ -0,0 +1,76 @@
# United EA Strategy Configuration Summary
## Strategy Symbols and Magic Numbers
### Strategy 1: DarvasBox
- **Symbol**: XAUUSD (Gold/USD)
- **Magic Number**: 135790
### Strategy 2: EMASlopeDistance
- **Symbol**: XAUUSD (Gold/USD)
- **Magic Number**: 12350
### Strategy 3: RSICrossOverReversal
- **Symbol**: XAUUSD (Gold/USD)
- **Magic Number**: 7
### Strategy 4: RSIMidPointHijack
- **Symbol**: XAUUSD (Gold/USD)
- **Magic Numbers**:
- RSIFollow: 1001
- RSIReverse: 1002
- EMACross: 1003
### Strategy 5: RSI Scalping APPL (Apple)
- **Symbol**: AAPL (Apple stock)
- **Magic Number**: 20001
- **Note**: Changed from "APPL" to "AAPL" (correct ticker symbol)
### Strategy 6: RSI Scalping BTCUSD
- **Symbol**: BTCUSD (Bitcoin/USD)
- **Magic Number**: 123459123
### Strategy 7: RSI Scalping MSFT
- **Symbol**: MSFT (Microsoft stock)
- **Magic Number**: 20002
### Strategy 8: RSI Scalping NVDA
- **Symbol**: NVDA (NVIDIA stock)
- **Magic Number**: 20003
### Strategy 9: RSI Scalping TSLA
- **Symbol**: TSLA (Tesla stock)
- **Magic Number**: 125421321
### Strategy 10: RSI Scalping XAUUSD
- **Symbol**: XAUUSD (Gold/USD)
- **Magic Number**: 129102315
## Important Notes
1. **Stock Symbols**: Stock symbols (AAPL, MSFT, NVDA, TSLA) must be:
- Added to Market Watch in MetaTrader 5
- Available from your broker
- Use the correct ticker symbol (e.g., "AAPL" not "APPL")
2. **Magic Numbers**: All strategies have unique magic numbers to prevent interference:
- Each strategy can be identified by its magic number
- RSIMidPointHijack uses 3 magic numbers (one for each sub-strategy)
3. **Symbol Configuration**: Each strategy trades on its own symbol:
- You can change symbols in the input parameters
- The EA will log warnings if a symbol is not available
- Strategies with unavailable symbols will be skipped (EA continues running)
4. **RSI Scalping Strategies**:
- Each RSI Scalping variant trades on a different symbol
- They all use the same strategy logic but with different parameters
- Buy and sell signals are generated based on RSI levels for each symbol
## Troubleshooting
If stock symbols are not working:
1. Check if the symbol exists in your broker's symbol list
2. Add the symbol to Market Watch in MetaTrader 5
3. Verify the symbol name matches your broker's naming convention
4. Some brokers use prefixes/suffixes (e.g., "NASDAQ:AAPL" or "AAPL.US")
@@ -0,0 +1,300 @@
//+------------------------------------------------------------------+
//| DarvasBoxStrategy.mqh |
//+------------------------------------------------------------------+
bool InitDarvasBox(string symbol)
{
dbData.symbol = symbol;
dbData.boxHigh = 0;
dbData.boxLow = 0;
dbData.boxFormed = false;
dbData.lastBoxTime = 0;
dbData.boxName = "DarvasBox_" + IntegerToString(DB_MagicNumber) + "_";
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("DarvasBox: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
Sleep(100); // Wait for symbol to be ready
dbData.point = SymbolInfoDouble(symbol, SYMBOL_POINT);
dbData.minStopLevel = SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL) * dbData.point;
dbData.maHandle = iMA(symbol, DB_TrendTimeframe, DB_MA_Period, 0, DB_MA_Method, DB_MA_Price);
dbData.volumeHandle = iVolumes(symbol, PERIOD_CURRENT, VOLUME_TICK);
if(dbData.maHandle == INVALID_HANDLE || dbData.volumeHandle == INVALID_HANDLE)
{
Print("DarvasBox: Error creating indicators for '", symbol, "'");
return false;
}
dbData.trade.SetDeviationInPoints(10);
dbData.trade.SetTypeFilling(ORDER_FILLING_IOC);
dbData.trade.SetAsyncMode(false);
dbData.trade.SetExpertMagicNumber(DB_MagicNumber);
ObjectsDeleteAll(0, dbData.boxName);
dbData.isInitialized = true;
Print("DarvasBox: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitDarvasBox()
{
if(dbData.maHandle != INVALID_HANDLE) IndicatorRelease(dbData.maHandle);
if(dbData.volumeHandle != INVALID_HANDLE) IndicatorRelease(dbData.volumeHandle);
ObjectsDeleteAll(0, dbData.boxName);
}
void DrawDarvasBox()
{
if(!dbData.boxFormed) return;
datetime time1 = iTime(dbData.symbol, PERIOD_H1, DB_BoxPeriod);
datetime time2 = iTime(dbData.symbol, PERIOD_H1, 0);
ObjectsDeleteAll(0, dbData.boxName);
ObjectCreate(0, dbData.boxName + "Top", OBJ_TREND, 0, time1, dbData.boxHigh, time2, dbData.boxHigh);
ObjectCreate(0, dbData.boxName + "Bottom", OBJ_TREND, 0, time1, dbData.boxLow, time2, dbData.boxLow);
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_COLOR, DB_BoxColor);
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_COLOR, DB_BoxColor);
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_WIDTH, DB_BoxWidth);
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_WIDTH, DB_BoxWidth);
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_RAY_RIGHT, true);
}
void CalculateDarvasBox()
{
double high = 0;
double low = DBL_MAX;
// Find highest high and lowest low in the period - EXACTLY like original
for(int i = 0; i < DB_BoxPeriod; i++)
{
high = MathMax(high, iHigh(dbData.symbol, PERIOD_H1, i));
low = MathMin(low, iLow(dbData.symbol, PERIOD_H1, i));
}
double range = high - low;
double allowedRange = DB_BoxDeviation * dbData.point; // Use dbData.point instead of _Point
if(DB_EnableLogging)
{
Print("DarvasBox: Box Calculation - High: ", high, " Low: ", low, " Range: ", range, " Allowed Range: ", allowedRange);
}
// Check if box is formed - EXACTLY like original
if(range <= allowedRange)
{
dbData.boxHigh = high;
dbData.boxLow = low;
dbData.boxFormed = true;
dbData.lastBoxTime = iTime(dbData.symbol, PERIOD_CURRENT, 0);
// Draw the box
DrawDarvasBox();
if(DB_EnableLogging)
Print("DarvasBox: Box Formed - High: ", dbData.boxHigh, " Low: ", dbData.boxLow, " Time: ", dbData.lastBoxTime);
}
else
{
dbData.boxFormed = false;
// Delete box if it exists
ObjectsDeleteAll(0, dbData.boxName);
}
}
bool ValidateStopLevels(double price, double &sl, double &tp, ENUM_ORDER_TYPE orderType)
{
double minSlDistance = MathMax(dbData.minStopLevel, DB_StopLoss * dbData.point);
double minTpDistance = MathMax(dbData.minStopLevel, DB_TakeProfit * dbData.point);
if(orderType == ORDER_TYPE_BUY)
{
sl = price - minSlDistance;
tp = price + minTpDistance;
}
else
{
sl = price + minSlDistance;
tp = price - minTpDistance;
}
return true;
}
bool IsTrendFavorable(ENUM_ORDER_TYPE orderType)
{
double ma[];
ArraySetAsSeries(ma, true);
if(CopyBuffer(dbData.maHandle, 0, 0, 2, ma) <= 0)
return false;
double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK);
double trendStrength = MathAbs(currentPrice - ma[0]) / dbData.point;
if(orderType == ORDER_TYPE_BUY)
return (currentPrice > ma[0] && trendStrength > DB_TrendThreshold);
else
return (currentPrice < ma[0] && trendStrength > DB_TrendThreshold);
}
bool CheckVolumeConditions()
{
double volumes[];
ArraySetAsSeries(volumes, true);
if(CopyBuffer(dbData.volumeHandle, 0, 0, DB_VolumeMA_Period + 1, volumes) <= 0)
return false;
double volumeMA = 0;
for(int i = 1; i <= DB_VolumeMA_Period; i++)
volumeMA += volumes[i];
volumeMA /= DB_VolumeMA_Period;
double currentVolume = volumes[0];
double volumeRatio = currentVolume / volumeMA;
return (volumeRatio > DB_VolumeThresholdMultiplier);
}
bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price, double sl, double tp)
{
if(!ValidateStopLevels(price, sl, tp, orderType))
{
if(DB_EnableLogging)
Print("DarvasBox: Order rejected - Stop levels validation failed");
return false;
}
if(!IsTrendFavorable(orderType))
{
if(DB_EnableLogging)
Print("DarvasBox: Order rejected - Trend not favorable for ", EnumToString(orderType));
return false;
}
if(!CheckVolumeConditions())
{
if(DB_EnableLogging)
Print("DarvasBox: Order rejected - Volume conditions not met");
return false;
}
bool result = false;
// Use market price (0) instead of explicit price - this ensures market order execution
// In backtesting, explicit price might fail if price has moved
if(orderType == ORDER_TYPE_BUY)
result = dbData.trade.Buy(g_DB_LotSize, dbData.symbol, 0, sl, tp, "Darvas Box Breakout");
else
result = dbData.trade.Sell(g_DB_LotSize, dbData.symbol, 0, sl, tp, "Darvas Box Breakdown");
// Always log errors, success only if logging enabled
if(result)
{
if(DB_EnableLogging)
Print("DarvasBox: ", (orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"), " Order Placed Successfully");
}
else
{
// Always log failures with detailed info
uint retcode_uint = dbData.trade.ResultRetcode();
int retcode = (int)retcode_uint;
string desc = dbData.trade.ResultRetcodeDescription();
ulong deal = dbData.trade.ResultDeal();
ulong order = dbData.trade.ResultOrder();
Print("DarvasBox: ", (orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"),
" Order Failed - Retcode: ", retcode,
", Description: ", desc,
", Deal: ", deal,
", Order: ", order,
", Symbol: ", dbData.symbol,
", Requested Price: ", price,
", SL: ", sl,
", TP: ", tp);
}
return result;
}
void ProcessDarvasBox(string symbol)
{
// Skip if not initialized (symbol not available)
if(!dbData.isInitialized)
return;
dbData.symbol = symbol; // Update symbol in case it changed
// Calculate new box levels - EXACTLY like original (called every tick)
CalculateDarvasBox();
// Check for trading signals - EXACTLY like original (checked every tick)
if(dbData.boxFormed)
{
double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK);
long currentVolume_long = iVolume(dbData.symbol, PERIOD_CURRENT, 0);
double currentVolume = (double)currentVolume_long;
if(DB_EnableLogging)
{
Print("DarvasBox: Current Price: ", currentPrice, " Box High: ", dbData.boxHigh, " Box Low: ", dbData.boxLow);
Print("DarvasBox: Current Volume: ", currentVolume, " Volume Threshold: ", DB_VolumeThreshold);
}
// Check for breakout above box - EXACTLY like original
if(currentPrice > dbData.boxHigh && currentVolume > DB_VolumeThreshold)
{
if(DB_EnableLogging)
Print("DarvasBox: Breakout Signal Detected - Price above box high");
// Buy signal
if(!PositionExistsByMagic(dbData.symbol, (ulong)DB_MagicNumber)) // No existing positions with our magic number
{
double sl = currentPrice - DB_StopLoss * dbData.point;
double tp = currentPrice + DB_TakeProfit * dbData.point;
if(DB_EnableLogging)
Print("DarvasBox: Preparing Buy Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
PlaceOrder(ORDER_TYPE_BUY, currentPrice, sl, tp);
}
else if(DB_EnableLogging)
Print("DarvasBox: Skipping Buy Signal - Position already exists");
}
// Check for breakdown below box - EXACTLY like original
if(currentPrice < dbData.boxLow && currentVolume > DB_VolumeThreshold)
{
if(DB_EnableLogging)
Print("DarvasBox: Breakdown Signal Detected - Price below box low");
// Sell signal
if(!PositionExistsByMagic(dbData.symbol, (ulong)DB_MagicNumber)) // No existing positions with our magic number
{
double sl = currentPrice + DB_StopLoss * dbData.point;
double tp = currentPrice - DB_TakeProfit * dbData.point;
if(DB_EnableLogging)
Print("DarvasBox: Preparing Sell Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
PlaceOrder(ORDER_TYPE_SELL, currentPrice, sl, tp);
}
else if(DB_EnableLogging)
Print("DarvasBox: Skipping Sell Signal - Position already exists");
}
}
else if(DB_EnableLogging)
Print("DarvasBox: No Box Formed - Waiting for consolidation");
}
//+------------------------------------------------------------------+
@@ -0,0 +1,496 @@
//+------------------------------------------------------------------+
//| EMASlopeDistanceStrategy.mqh |
//+------------------------------------------------------------------+
bool InitEMASlopeDistance(string symbol)
{
esData.symbol = symbol;
esData.letzte_überwachung_zeit = 0;
esData.überwachung_aktiv = false;
esData.preis_trigger_aktiv = false;
esData.steigung_trigger_aktiv = false;
esData.ticket = 0;
esData.trades_in_current_crossover = 0;
esData.crossover_detected = false;
esData.trade_open_time = 0;
esData.last_bar_time = 0;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("EMASlopeDistance: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
Sleep(100); // Wait for symbol to be ready
esData.trade.SetExpertMagicNumber(ES_MagicNumber);
esData.trade.SetDeviationInPoints(10);
esData.trade.SetTypeFilling(ORDER_FILLING_IOC);
esData.ema_handle = iMA(symbol, ES_Timeframe, ES_EMA_Periode, 0, MODE_EMA, PRICE_CLOSE);
if(esData.ema_handle == INVALID_HANDLE)
{
Print("EMASlopeDistance: Error creating EMA indicator for '", symbol, "'");
return false;
}
ArraySetAsSeries(esData.ema_array, true);
esData.isInitialized = true;
Print("EMASlopeDistance: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitEMASlopeDistance()
{
if(esData.ema_handle != INVALID_HANDLE)
IndicatorRelease(esData.ema_handle);
}
//+------------------------------------------------------------------+
//| EMA Berechnung (EMA Calculation) |
//+------------------------------------------------------------------+
void BerechneEMA()
{
//--- EMA Werte vom Indicator kopieren (Copy EMA values from indicator)
int copied = CopyBuffer(esData.ema_handle, 0, 0, 3, esData.ema_array);
if(copied <= 0)
{
Print("TRACE: Fehler beim Kopieren der EMA Werte - Copied: ", copied);
return;
}
Print("TRACE: EMA Werte kopiert: ", copied, " Bars");
Print("TRACE: EMA [0]: ", esData.ema_array[0], " [1]: ", esData.ema_array[1], " [2]: ", esData.ema_array[2]);
}
//+------------------------------------------------------------------+
//| Trigger-Bedingungen prüfen (Check trigger conditions) |
//+------------------------------------------------------------------+
void PrüfeTrigger()
{
if(ArraySize(esData.ema_array) < 2)
{
Print("TRACE: Array zu klein - Größe: ", ArraySize(esData.ema_array));
return;
}
//--- Aktuelle Werte (Current values)
double aktueller_preis = SymbolInfoDouble(esData.symbol, SYMBOL_BID);
double aktueller_ask = SymbolInfoDouble(esData.symbol, SYMBOL_ASK);
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
double pips_multiplier = (digits == 3 || digits == 5) ? 10.0 : 1.0;
//--- EMA Werte in Variablen (EMA values in variables)
double ema_aktuell = esData.ema_array[0];
double ema_vorher = esData.ema_array[1];
//--- EMA Crossover Erkennung (EMA Crossover Detection)
// Prüfe ob Preis die EMA kreuzt (Check if price crosses EMA)
static double last_close = 0;
static double last_ema = 0;
if(last_close != 0 && last_ema != 0)
{
bool crossover_bullish = (last_close <= last_ema) && (aktueller_close > ema_aktuell);
bool crossover_bearish = (last_close >= last_ema) && (aktueller_close < ema_aktuell);
//--- Neues Crossover-Ereignis erkannt (New crossover event detected)
if(crossover_bullish || crossover_bearish)
{
esData.trades_in_current_crossover = 0; // Reset trade counter
Print("TRACE: EMA Crossover erkannt - ", (crossover_bullish ? "BULLISH" : "BEARISH"), " - Trade-Counter zurückgesetzt");
Print("TRACE: Vorher: Close=", last_close, " EMA=", last_ema, " Jetzt: Close=", aktueller_close, " EMA=", ema_aktuell);
}
}
//--- Aktuelle Werte für nächsten Vergleich speichern (Save current values for next comparison)
last_close = aktueller_close;
last_ema = ema_aktuell;
//--- Preisbewegung zur EMA prüfen (Check price action to EMA)
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / point / pips_multiplier;
Print("TRACE: Preis-Abstand: ", preis_abstand, " Pips (Schwelle: ", ES_PreisSchwelle, ")");
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
Print("TRACE: Trades im aktuellen Crossover: ", esData.trades_in_current_crossover, "/", ES_MaxTradesPerCrossover);
if(preis_abstand > ES_PreisSchwelle && !esData.preis_trigger_aktiv)
{
esData.preis_trigger_aktiv = true;
Print("TRACE: Preis-Trigger aktiviert: ", preis_abstand, " Pips");
}
//--- EMA Steigung prüfen (Check EMA slope)
double steigung = (ema_aktuell - ema_vorher) / point / pips_multiplier;
Print("TRACE: EMA Steigung: ", steigung, " Pips (Schwelle: ", ES_SteigungSchwelle, ")");
if(MathAbs(steigung) > ES_SteigungSchwelle && !esData.steigung_trigger_aktiv)
{
esData.steigung_trigger_aktiv = true;
Print("TRACE: Steigungs-Trigger aktiviert: ", steigung, " Pips");
}
//--- Überwachung starten wenn beide Trigger aktiv sind (Start monitoring when both triggers are active)
if(esData.preis_trigger_aktiv && esData.steigung_trigger_aktiv && !esData.überwachung_aktiv)
{
esData.überwachung_aktiv = true;
if(ES_UseBarData)
{
esData.letzte_überwachung_zeit = iTime(esData.symbol, ES_Timeframe, 0); // Aktuelle Bar-Zeit
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Bar: ", TimeToString(esData.letzte_überwachung_zeit), ")");
}
else
{
esData.letzte_überwachung_zeit = TimeCurrent(); // Aktuelle Tick-Zeit
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Tick)");
}
}
//--- Trade platzieren wenn Überwachung aktiv und Preis über/unter EMA (Place trade when monitoring active and price above/below EMA)
if(esData.überwachung_aktiv)
{
bool bullish_signal = aktueller_close > ema_aktuell;
bool bearish_signal = aktueller_close < ema_aktuell;
Print("TRACE: Signal Check - Bullish: ", bullish_signal, " Bearish: ", bearish_signal);
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
Print("TRACE: Differenz: ", aktueller_close - ema_aktuell);
//--- Trade-Limit prüfen (Check trade limit)
if(esData.trades_in_current_crossover >= ES_MaxTradesPerCrossover)
{
Print("TRACE: Trade-Limit erreicht (", ES_MaxTradesPerCrossover, ") - Kein neuer Trade");
return;
}
if(bullish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
Print("TRACE: Versuche KAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_BUY))
{
esData.trades_in_current_crossover++;
}
}
else if(bearish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
Print("TRACE: Versuche VERKAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_SELL))
{
esData.trades_in_current_crossover++;
}
}
else if(PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
Print("TRACE: Position bereits offen - kein neuer Trade");
}
}
}
//+------------------------------------------------------------------+
//| Trade platzieren (Place trade) |
//+------------------------------------------------------------------+
bool PlatziereTrade(ENUM_ORDER_TYPE order_type)
{
Print("TRACE: Versuche Trade zu platzieren - Typ: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF");
Print("TRACE: Lot: ", g_ES_LotSize);
bool success = false;
if(order_type == ORDER_TYPE_BUY)
{
success = esData.trade.Buy(g_ES_LotSize, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
}
else
{
success = esData.trade.Sell(g_ES_LotSize, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
}
if(success)
{
esData.ticket = (int)esData.trade.ResultOrder();
Print("TRACE: Trade erfolgreich platziert: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF", " Ticket: ", esData.ticket);
//--- Trade-Öffnungszeit speichern (Save trade opening time)
esData.trade_open_time = iTime(esData.symbol, ES_Timeframe, 0);
Print("TRACE: Trade-Öffnungszeit: ", TimeToString(esData.trade_open_time));
//--- Überwachung zurücksetzen (Reset monitoring)
esData.überwachung_aktiv = false;
esData.preis_trigger_aktiv = false;
esData.steigung_trigger_aktiv = false;
return true;
}
else
{
Print("TRACE: Fehler beim Platzieren des Trades - Retcode: ", esData.trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
return false;
}
}
//+------------------------------------------------------------------+
//| Trades verwalten (Manage trades) |
//+------------------------------------------------------------------+
void VerwalteTrades()
{
if(!PositionSelectByMagic(esData.symbol, (ulong)ES_MagicNumber))
return;
double position_profit = PositionGetDouble(POSITION_PROFIT);
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
double pips_multiplier = (digits == 3 || digits == 5) ? 10.0 : 1.0;
double trailing_stop_pips = ES_TrailingStop;
//--- Gleitender Stop (Trailing Stop) - nur wenn Position im Profit ist
if(position_profit > 0) // Only apply trailing stop when in profit
{
if(position_type == POSITION_TYPE_BUY)
{
double new_stop_loss = current_price - (trailing_stop_pips * point * pips_multiplier);
double current_stop_loss = PositionGetDouble(POSITION_SL);
// Only move stop loss if new stop is higher than current stop
if(new_stop_loss > current_stop_loss)
{
ÄndereStopLoss(new_stop_loss);
}
}
else if(position_type == POSITION_TYPE_SELL)
{
double new_stop_loss = current_price + (trailing_stop_pips * point * pips_multiplier);
double current_stop_loss = PositionGetDouble(POSITION_SL);
// Only move stop loss if new stop is lower than current stop
if(new_stop_loss < current_stop_loss || current_stop_loss == 0)
{
ÄndereStopLoss(new_stop_loss);
}
}
}
//--- Ausstieg bei Preis unter/über EMA (Exit when price below/above EMA)
if(ArraySize(esData.ema_array) >= 1)
{
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
double ema_aktuell = esData.ema_array[0];
bool exit_bullish = (position_type == POSITION_TYPE_SELL && aktueller_close > ema_aktuell);
bool exit_bearish = (position_type == POSITION_TYPE_BUY && aktueller_close < ema_aktuell);
if(exit_bullish || exit_bearish)
{
Print("TRACE: Ausstiegssignal - Close: ", aktueller_close, " EMA: ", ema_aktuell);
SchließePosition("EMA Crossover Exit");
Print("TRACE: Position geschlossen - Trade-Counter bleibt bei ", esData.trades_in_current_crossover);
}
}
//--- Profit-Prüfung nach X Bars (Profit check after X bars)
if(ES_CloseUnprofitableTrades && esData.trade_open_time != 0 && PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
Print("TRACE: Profit-Prüfung aktiviert - CloseUnprofitableTrades: ", ES_CloseUnprofitableTrades);
PrüfeProfitNachBars();
}
else if(!ES_CloseUnprofitableTrades)
{
Print("TRACE: Profit-Prüfung deaktiviert - CloseUnprofitableTrades: ", ES_CloseUnprofitableTrades);
}
}
//+------------------------------------------------------------------+
//| Profit-Prüfung nach X Bars (Profit check after X bars) |
//+------------------------------------------------------------------+
void PrüfeProfitNachBars()
{
if(!PositionSelectByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
return; // Keine Position offen
}
datetime current_bar_time = iTime(esData.symbol, ES_Timeframe, 0);
int bars_since_trade_open = iBarShift(esData.symbol, ES_Timeframe, esData.trade_open_time);
Print("TRACE: Bars seit Trade-Öffnung: ", bars_since_trade_open, "/", ES_ProfitCheckBars);
//--- Prüfe ob genügend Bars vergangen sind (Check if enough bars have passed)
if(bars_since_trade_open >= ES_ProfitCheckBars)
{
double position_profit = PositionGetDouble(POSITION_PROFIT);
double position_volume = PositionGetDouble(POSITION_VOLUME);
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
Print("TRACE: Profit-Prüfung nach ", ES_ProfitCheckBars, " Bars");
Print("TRACE: Position Profit: ", position_profit, " USD");
//--- Schließe Position wenn nicht im Profit (Close position if not in profit)
if(position_profit <= 0)
{
Print("TRACE: Position nicht im Profit - Schließe Position");
SchließePosition("Profit Check - Unprofitable");
//--- Trade-Öffnungszeit zurücksetzen (Reset trade opening time)
esData.trade_open_time = 0;
Print("TRACE: Trade-Öffnungszeit zurückgesetzt");
}
else
{
Print("TRACE: Position im Profit - Behalte Position");
//--- Trade-Öffnungszeit zurücksetzen um weitere Prüfungen zu vermeiden (Reset to avoid further checks)
esData.trade_open_time = 0;
}
}
}
//+------------------------------------------------------------------+
//| Stop Loss ändern (Modify Stop Loss) |
//+------------------------------------------------------------------+
void ÄndereStopLoss(double new_stop_loss)
{
Print("TRACE: Versuche Stop Loss zu ändern auf: ", new_stop_loss);
bool success = ModifyPositionByMagic(esData.trade, esData.symbol, (ulong)ES_MagicNumber, new_stop_loss, PositionGetDouble(POSITION_TP));
if(success)
{
Print("TRACE: Stop Loss erfolgreich geändert auf: ", new_stop_loss);
}
else
{
Print("TRACE: Fehler beim Ändern des Stop Loss - Retcode: ", esData.trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
}
}
//+------------------------------------------------------------------+
//| Position schließen (Close position) |
//+------------------------------------------------------------------+
void SchließePosition(string reason = "Unbekannt")
{
Print("TRACE: Versuche Position zu schließen - Grund: ", reason);
bool success = ClosePositionByMagic(esData.trade, esData.symbol, (ulong)ES_MagicNumber);
if(success)
{
Print("TRACE: Position erfolgreich geschlossen - Grund: ", reason);
}
else
{
Print("TRACE: Fehler beim Schließen der Position - Retcode: ", esData.trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void ProcessEMASlopeDistance(string symbol)
{
// Skip if not initialized (symbol not available)
if(!esData.isInitialized)
return;
esData.symbol = symbol; // Update symbol in case it changed
//--- Bar-Daten oder Tick-Daten verwenden (Use bar data or tick data)
if(ES_UseBarData)
{
//--- Nur bei neuen Bars ausführen (Only execute on new bars)
datetime current_bar_time = iTime(esData.symbol, ES_Timeframe, 0);
if(current_bar_time == esData.last_bar_time)
{
return; // Kein neuer Bar, nichts tun
}
esData.last_bar_time = current_bar_time;
}
//--- EMA Werte berechnen (Calculate EMA values)
BerechneEMA();
//--- Debug: Aktuelle Werte ausgeben (Debug: Output current values)
if(ArraySize(esData.ema_array) > 0)
{
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
double ema_aktuell = esData.ema_array[0];
double ema_vorher = esData.ema_array[1];
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / point;
double steigung = (ema_aktuell - ema_vorher) / point;
if(ES_UseBarData)
{
Print("=== DEBUG INFO (Neuer Bar) ===");
Print("Bar Zeit: ", TimeToString(iTime(esData.symbol, ES_Timeframe, 0)));
}
else
{
Print("=== DEBUG INFO (Tick) ===");
}
Print("Aktueller Close: ", aktueller_close);
Print("EMA: ", ema_aktuell);
Print("Preis-Abstand: ", preis_abstand, " Pips");
Print("EMA Steigung: ", steigung, " Pips");
Print("Differenz Close-EMA: ", aktueller_close - ema_aktuell);
Print("Preis-Trigger: ", esData.preis_trigger_aktiv, " Steigungs-Trigger: ", esData.steigung_trigger_aktiv);
Print("Überwachung aktiv: ", esData.überwachung_aktiv);
Print("Position offen: ", PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber));
Print("Trades im aktuellen Crossover: ", esData.trades_in_current_crossover, "/", ES_MaxTradesPerCrossover);
Print("==================");
}
//--- Überwachung prüfen (Check monitoring)
if(esData.überwachung_aktiv)
{
if(ES_UseBarData)
{
// Bar-basierte Überwachungszeit
int bars_since_monitoring = iBarShift(esData.symbol, ES_Timeframe, esData.letzte_überwachung_zeit);
int timeout_bars = (int)(ES_ÜberwachungTimeout / PeriodSeconds(ES_Timeframe));
if(bars_since_monitoring > timeout_bars)
{
esData.überwachung_aktiv = false;
esData.preis_trigger_aktiv = false;
esData.steigung_trigger_aktiv = false;
Print("Überwachung beendet - Bar-basierte Zeitüberschreitung (", bars_since_monitoring, " Bars)");
}
}
else
{
// Tick-basierte Überwachungszeit
if(TimeCurrent() - esData.letzte_überwachung_zeit > ES_ÜberwachungTimeout)
{
esData.überwachung_aktiv = false;
esData.preis_trigger_aktiv = false;
esData.steigung_trigger_aktiv = false;
Print("Überwachung beendet - Tick-basierte Zeitüberschreitung");
}
}
}
//--- Trigger-Bedingungen prüfen (Check trigger conditions)
PrüfeTrigger();
//--- Trade Management (Trade management)
VerwalteTrades();
}
//+------------------------------------------------------------------+
@@ -0,0 +1,240 @@
//+------------------------------------------------------------------+
//| RSICrossOverReversalStrategy.mqh |
//+------------------------------------------------------------------+
void WeekDays_Init()
{
rcData.WeekDays[0] = RC_Sunday;
rcData.WeekDays[1] = RC_Monday;
rcData.WeekDays[2] = RC_Tuesday;
rcData.WeekDays[3] = RC_Wednesday;
rcData.WeekDays[4] = RC_Thursday;
rcData.WeekDays[5] = RC_Friday;
rcData.WeekDays[6] = RC_Saturday;
}
bool WeekDays_Check(datetime aTime)
{
MqlDateTime stm;
TimeToStruct(aTime, stm);
return(rcData.WeekDays[stm.day_of_week]);
}
int TimeHour(datetime when = 0)
{
if(when == 0) when = TimeCurrent();
MqlDateTime dt;
TimeToStruct(when, dt);
return dt.hour;
}
bool InitRSICrossOverReversal(string symbol)
{
WeekDays_Init();
rcData.symbol = symbol;
rcData.previousRSIDef = 0;
rcData.lastTradeTime = 0;
rcData.bartime = 0;
rcData.lastBarTime = 0;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("RSICrossOverReversal: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
Sleep(100); // Wait for symbol to be ready
rcData.rsiHandle = iRSI(symbol, RC_TimeFrame1, RC_rsiPeriod, PRICE_CLOSE);
if(rcData.rsiHandle == INVALID_HANDLE)
{
Print("RSICrossOverReversal: Error creating RSI handle for '", symbol, "'");
return false;
}
rcData.emaHandle = iMA(symbol, RC_TimeFrame2, RC_emaPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(rcData.emaHandle == INVALID_HANDLE)
{
Print("RSICrossOverReversal: Error creating EMA handle for '", symbol, "'");
return false;
}
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
rcData.isInitialized = true;
Print("RSICrossOverReversal: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitRSICrossOverReversal()
{
if(rcData.rsiHandle != INVALID_HANDLE)
IndicatorRelease(rcData.rsiHandle);
if(rcData.emaHandle != INVALID_HANDLE)
IndicatorRelease(rcData.emaHandle);
}
void Close_Position_MN(ulong magicNumber)
{
ClosePositionByMagic(rcData.trade, rcData.symbol, (int)magicNumber);
}
void ApplyTrailingStop()
{
if(!PositionSelectByMagic(rcData.symbol, RC_MagicNumber))
return;
ulong PositionTicket = PositionGetInteger(POSITION_TICKET);
ENUM_POSITION_TYPE trade_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
string symbol = rcData.symbol;
double POINT = SymbolInfoDouble(symbol, SYMBOL_POINT);
int DIGIT = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
if(trade_type == POSITION_TYPE_BUY)
{
double Bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), DIGIT);
if(Bid - PositionGetDouble(POSITION_PRICE_OPEN) > NormalizeDouble(POINT * RC_TrailingStop, DIGIT))
{
if(PositionGetDouble(POSITION_SL) < NormalizeDouble(Bid - POINT * RC_TrailingStop, DIGIT))
{
ModifyPositionByMagic(rcData.trade, symbol, RC_MagicNumber,
NormalizeDouble(Bid - POINT * RC_TrailingStop, DIGIT),
PositionGetDouble(POSITION_TP));
}
}
}
else if(trade_type == POSITION_TYPE_SELL)
{
double Ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), DIGIT);
if((PositionGetDouble(POSITION_PRICE_OPEN) - Ask) > NormalizeDouble(POINT * RC_TrailingStop, DIGIT))
{
if((PositionGetDouble(POSITION_SL) > NormalizeDouble(Ask + POINT * RC_TrailingStop, DIGIT)) ||
(PositionGetDouble(POSITION_SL) == 0))
{
ModifyPositionByMagic(rcData.trade, symbol, RC_MagicNumber,
NormalizeDouble(Ask + POINT * RC_TrailingStop, DIGIT),
PositionGetDouble(POSITION_TP));
}
}
}
}
void ProcessRSICrossOverReversal(string symbol)
{
// Skip if not initialized (symbol not available)
if(!rcData.isInitialized)
return;
rcData.symbol = symbol; // Update symbol in case it changed
if(rcData.bartime == iTime(rcData.symbol, RC_BarTimeFrame, 0))
return;
rcData.bartime = iTime(rcData.symbol, RC_BarTimeFrame, 0);
double rsi[];
if(CopyBuffer(rcData.rsiHandle, 0, 0, 2, rsi) <= 0)
return;
double ema[];
if(CopyBuffer(rcData.emaHandle, 0, 0, 2, ema) <= 0)
return;
datetime currentTime = TimeCurrent();
int currentHour = TimeHour(TimeCurrent());
if(!WeekDays_Check(TimeTradeServer()))
{
Close_Position_MN(RC_MagicNumber);
return;
}
if(!((currentHour < RC_tradingHourOneEnd && currentHour > RC_tradingHourOneBegin) ||
(currentHour < RC_tradingHourTwoEnd && currentHour > RC_tradingHourTwoBegin)))
{
Close_Position_MN(RC_MagicNumber);
return;
}
bool hasPosition = PositionExistsByMagic(rcData.symbol, RC_MagicNumber);
double currentRSI = rsi[0];
double previousRSI = rsi[1];
if(rcData.previousRSIDef == 0)
{
rcData.previousRSIDef = currentRSI;
return;
}
double currentEMA = ema[0];
double previousEMA = ema[1];
double emaSlope = (currentEMA - previousEMA) * 100;
double closeCurr = iClose(Symbol(), Period(), 0);
double priceToEmaDistance = (closeCurr - currentEMA) * 10;
bool isBuyPosition = false;
bool isSellPosition = false;
if(hasPosition)
{
if(PositionSelectByMagic(rcData.symbol, RC_MagicNumber))
{
ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(positionType == POSITION_TYPE_BUY)
isBuyPosition = true;
else if(positionType == POSITION_TYPE_SELL)
isSellPosition = true;
}
}
ApplyTrailingStop();
bool cooldownPassed = (currentTime - rcData.lastTradeTime) >= RC_cooldownSeconds;
bool isTrendStrong = MathAbs(emaSlope) > RC_emaSlopeThreshold || MathAbs(priceToEmaDistance) > RC_emaDistanceThreshold;
if(isBuyPosition && currentRSI > RC_exitBuyRSI)
{
Close_Position_MN(RC_MagicNumber);
rcData.lastTradeTime = currentTime;
}
if(isSellPosition && currentRSI < RC_exitSellRSI)
{
Close_Position_MN(RC_MagicNumber);
rcData.lastTradeTime = currentTime;
}
if(isTrendStrong)
{
Close_Position_MN(RC_MagicNumber);
rcData.lastTradeTime = currentTime;
return;
}
if(currentRSI < RC_overboughtLevel - RC_entryRSISellSpread && rcData.previousRSIDef >= RC_overboughtLevel &&
!isSellPosition && !hasPosition && cooldownPassed)
{
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
if(rcData.trade.Sell(g_RC_LotSize, rcData.symbol, 0.0, 0.0, 0.0, "Sell Order"))
{
rcData.lastTradeTime = currentTime;
}
}
if(currentRSI > RC_oversoldLevel + RC_entryRSIBuySpread && rcData.previousRSIDef <= RC_oversoldLevel &&
!isBuyPosition && !hasPosition && cooldownPassed)
{
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
if(rcData.trade.Buy(g_RC_LotSize, rcData.symbol, 0.0, 0.0, 0.0, "Buy Order"))
{
rcData.lastTradeTime = currentTime;
}
}
rcData.previousRSIDef = currentRSI;
}
//+------------------------------------------------------------------+
@@ -0,0 +1,471 @@
//+------------------------------------------------------------------+
//| RSIMidPointHijackStrategy.mqh |
//+------------------------------------------------------------------+
bool IsNewBar(string symbol)
{
datetime time[];
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
{
if(time[0] != rmData.lastBarTime)
{
rmData.lastBarTime = time[0];
return true;
}
}
return false;
}
bool IsWithinTradingHours(int startHour, int endHour)
{
MqlDateTime currentTime;
TimeToStruct(TimeCurrent(), currentTime);
if(startHour <= endHour)
return (currentTime.hour >= startHour && currentTime.hour < endHour);
else
return (currentTime.hour >= startHour || currentTime.hour < endHour);
}
bool HasPosition(string symbol, int magic)
{
return PositionExistsByMagic(symbol, magic);
}
bool HasProfitablePosition(int excludeMagic)
{
bool hasProfitable = false;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(rmData.positionInfo.SelectByIndex(i))
{
if(rmData.positionInfo.Magic() != excludeMagic)
{
double profit = rmData.positionInfo.Profit();
if(profit > RM_InpLockProfitThreshold * _Point)
{
hasProfitable = true;
if(RM_InpCloseOppositeTrades)
{
if((excludeMagic == RM_InpMagicNumberRSIFollow && rmData.positionInfo.Magic() == RM_InpMagicNumberRSIReverse) ||
(excludeMagic == RM_InpMagicNumberRSIReverse && rmData.positionInfo.Magic() == RM_InpMagicNumberRSIFollow) ||
(excludeMagic == RM_InpMagicNumberEMACross && (rmData.positionInfo.Magic() == RM_InpMagicNumberRSIReverse || rmData.positionInfo.Magic() == RM_InpMagicNumberRSIFollow)) ||
((excludeMagic == RM_InpMagicNumberRSIFollow || excludeMagic == RM_InpMagicNumberRSIReverse) && rmData.positionInfo.Magic() == RM_InpMagicNumberEMACross))
{
ClosePosition(rmData.symbol, (int)rmData.positionInfo.Magic());
}
}
}
}
}
}
return hasProfitable;
}
bool IsRSIReverseInCooldown(string symbol)
{
if(RM_InpRSIReverseCooldownBars <= 0)
return false;
if(!rmData.rsiReverseInCooldown)
return false;
datetime time[];
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
{
datetime currentBarTime = time[0];
datetime cooldownEndTime = rmData.rsiReverseLastCloseTime + RM_InpRSIReverseCooldownBars * PeriodSeconds(RM_InpTimeframe);
if(currentBarTime >= cooldownEndTime)
{
rmData.rsiReverseInCooldown = false;
return false;
}
}
return true;
}
void CheckRSIFollowStrategy(string symbol)
{
if(!IsWithinTradingHours(RM_InpRSIFollowStartHour, RM_InpRSIFollowEndHour))
{
if(RM_InpRSIFollowCloseOutsideHours)
{
if(HasPosition(symbol, RM_InpMagicNumberRSIFollow))
ClosePosition(symbol, RM_InpMagicNumberRSIFollow);
}
return;
}
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberRSIFollow))
return;
if(rmData.lastBarRSI > RM_InpRSIOverbought)
rmData.rsiOverbought = true;
else if(rmData.lastBarRSI < RM_InpRSIOversold)
rmData.rsiOversold = true;
if(rmData.rsiOverbought && rmData.lastBarRSI < RM_InpRSIExitLevel)
{
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
}
rmData.rsiOverbought = false;
}
else if(rmData.rsiOversold && rmData.lastBarRSI > RM_InpRSIExitLevel)
{
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
}
rmData.rsiOversold = false;
}
}
void CheckRSIReverseStrategy(string symbol)
{
if(!IsWithinTradingHours(RM_InpRSIReverseStartHour, RM_InpRSIReverseEndHour))
{
if(RM_InpRSIReverseCloseOutsideHours)
{
if(HasPosition(symbol, RM_InpMagicNumberRSIReverse))
ClosePosition(symbol, RM_InpMagicNumberRSIReverse);
}
return;
}
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberRSIReverse))
return;
if(IsRSIReverseInCooldown(symbol))
return;
if(rmData.lastBarRSIReverse > RM_InpRSIReverseOverbought)
rmData.rsiReverseOverbought = true;
else if(rmData.lastBarRSIReverse < RM_InpRSIReverseOversold)
rmData.rsiReverseOversold = true;
if(rmData.rsiReverseOverbought && rmData.lastBarRSIReverse < RM_InpRSIReverseCrossLevel)
{
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
}
rmData.rsiReverseOverbought = false;
}
else if(rmData.rsiReverseOversold && rmData.lastBarRSIReverse > RM_InpRSIReverseCrossLevel)
{
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
}
rmData.rsiReverseOversold = false;
}
}
void CheckEMACrossStrategy(string symbol)
{
if(!IsWithinTradingHours(RM_InpEMACrossStartHour, RM_InpEMACrossEndHour))
{
if(RM_InpEMACrossCloseOutsideHours)
{
if(HasPosition(symbol, RM_InpMagicNumberEMACross))
ClosePosition(symbol, RM_InpMagicNumberEMACross);
}
return;
}
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberEMACross))
return;
if(rmData.lastBarEMAPrev < rmData.lastBarClosePrev && rmData.lastBarEMA > rmData.lastBarClose)
{
rmData.emaCrossBuySignal = true;
rmData.emaCrossSellSignal = false;
rmData.emaCrossSignalBar = 0;
}
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
{
rmData.emaCrossSellSignal = true;
rmData.emaCrossBuySignal = false;
rmData.emaCrossSignalBar = 0;
}
if(RM_InpUseEMADistanceEntry)
{
if(rmData.emaCrossBuySignal)
{
bool distanceConditionMet = true;
double emaHistory[], closeHistory[];
ArraySetAsSeries(emaHistory, true);
ArraySetAsSeries(closeHistory, true);
if(CopyBuffer(rmData.emaHandle, 0, 0, RM_InpEMADistancePeriod, emaHistory) > 0 &&
CopyClose(symbol, RM_InpTimeframe, 0, RM_InpEMADistancePeriod, closeHistory) > 0)
{
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
for(int i = 0; i < RM_InpEMADistancePeriod; i++)
{
double distance = (closeHistory[i] - emaHistory[i]) / point;
if(distance < RM_InpEMADistancePips)
{
distanceConditionMet = false;
break;
}
}
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
rmData.emaCrossBuySignal = false;
}
}
}
else if(rmData.emaCrossSellSignal)
{
bool distanceConditionMet = true;
double emaHistory[], closeHistory[];
ArraySetAsSeries(emaHistory, true);
ArraySetAsSeries(closeHistory, true);
if(CopyBuffer(rmData.emaHandle, 0, 0, RM_InpEMADistancePeriod, emaHistory) > 0 &&
CopyClose(symbol, RM_InpTimeframe, 0, RM_InpEMADistancePeriod, closeHistory) > 0)
{
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
for(int i = 0; i < RM_InpEMADistancePeriod; i++)
{
double distance = (emaHistory[i] - closeHistory[i]) / point;
if(distance < RM_InpEMADistancePips)
{
distanceConditionMet = false;
break;
}
}
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
rmData.emaCrossSellSignal = false;
}
}
}
}
else
{
if(rmData.lastBarEMAPrev < rmData.lastBarClosePrev && rmData.lastBarEMA > rmData.lastBarClose)
{
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
}
}
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
{
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
}
}
}
if(rmData.emaCrossBuySignal || rmData.emaCrossSellSignal)
{
rmData.emaCrossSignalBar++;
if(rmData.emaCrossSignalBar > RM_InpEMADistancePeriod * 2)
{
rmData.emaCrossBuySignal = false;
rmData.emaCrossSellSignal = false;
}
}
}
void CheckExitConditions(string symbol)
{
if(RM_InpEnableRSIFollow)
{
if(HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
if(PositionSelectByMagic(symbol, RM_InpMagicNumberRSIFollow))
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if((posType == POSITION_TYPE_BUY && rmData.lastBarRSI < RM_InpRSIExitLevel) ||
(posType == POSITION_TYPE_SELL && rmData.lastBarRSI > RM_InpRSIExitLevel))
{
ClosePosition(symbol, RM_InpMagicNumberRSIFollow);
}
}
}
}
if(RM_InpEnableRSIReverse)
{
if(HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
if(PositionSelectByMagic(symbol, RM_InpMagicNumberRSIReverse))
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if((posType == POSITION_TYPE_BUY && rmData.lastBarRSIReverse < RM_InpRSIReverseExitLevel) ||
(posType == POSITION_TYPE_SELL && rmData.lastBarRSIReverse > RM_InpRSIReverseExitLevel))
{
ClosePosition(symbol, RM_InpMagicNumberRSIReverse);
}
}
}
}
if(RM_InpEnableEMACross)
{
if(HasPosition(symbol, RM_InpMagicNumberEMACross))
{
if(PositionSelectByMagic(symbol, RM_InpMagicNumberEMACross))
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if((posType == POSITION_TYPE_BUY && rmData.lastBarEMA > rmData.lastBarClose) ||
(posType == POSITION_TYPE_SELL && rmData.lastBarEMA < rmData.lastBarClose))
{
ClosePosition(symbol, RM_InpMagicNumberEMACross);
}
}
}
}
}
void ClosePosition(string symbol, int magic)
{
if(!PositionExistsByMagic(symbol, magic))
return;
ulong ticket = GetPositionTicketByMagic(symbol, magic);
if(ticket == 0)
return;
if(magic == RM_InpMagicNumberRSIReverse)
{
if(PositionSelectByTicketSymbolAndMagic(ticket, symbol, magic))
{
datetime time[];
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
{
rmData.rsiReverseLastCloseTime = time[0];
double profit = PositionGetDouble(POSITION_PROFIT);
if(!RM_InpRSIReverseCooldownOnLoss || profit < 0)
{
rmData.rsiReverseInCooldown = true;
}
}
}
}
ClosePositionByMagic(rmData.trade, symbol, magic);
}
bool InitRSIMidPointHijack(string symbol)
{
rmData.symbol = symbol;
rmData.rsiOverbought = false;
rmData.rsiOversold = false;
rmData.rsiReverseOverbought = false;
rmData.rsiReverseOversold = false;
rmData.emaCrossBuySignal = false;
rmData.emaCrossSellSignal = false;
rmData.emaCrossSignalBar = 0;
rmData.rsiReverseInCooldown = false;
rmData.lastBarRSI = 0;
rmData.lastBarRSIReverse = 0;
rmData.lastBarEMA = 0;
rmData.lastBarClose = 0;
rmData.lastBarEMAPrev = 0;
rmData.lastBarClosePrev = 0;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("RSIMidPointHijack: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
Sleep(100); // Wait for symbol to be ready
rmData.rsiHandle = iRSI(symbol, RM_InpTimeframe, RM_InpRSIPeriod, PRICE_CLOSE);
rmData.rsiReverseHandle = iRSI(symbol, RM_InpTimeframe, RM_InpRSIReversePeriod, PRICE_CLOSE);
rmData.emaHandle = iMA(symbol, RM_InpTimeframe, RM_InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(rmData.rsiHandle == INVALID_HANDLE || rmData.rsiReverseHandle == INVALID_HANDLE || rmData.emaHandle == INVALID_HANDLE)
{
Print("RSIMidPointHijack: Error creating indicators for '", symbol, "'");
return false;
}
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
rmData.trade.SetMarginMode();
rmData.trade.SetTypeFillingBySymbol(symbol);
rmData.trade.SetDeviationInPoints(10);
datetime time[];
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
rmData.lastBarTime = time[0];
rmData.isInitialized = true;
Print("RSIMidPointHijack: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitRSIMidPointHijack()
{
if(rmData.rsiHandle != INVALID_HANDLE) IndicatorRelease(rmData.rsiHandle);
if(rmData.rsiReverseHandle != INVALID_HANDLE) IndicatorRelease(rmData.rsiReverseHandle);
if(rmData.emaHandle != INVALID_HANDLE) IndicatorRelease(rmData.emaHandle);
}
void ProcessRSIMidPointHijack(string symbol)
{
// Skip if not initialized (symbol not available)
if(!rmData.isInitialized)
return;
rmData.symbol = symbol; // Update symbol in case it changed
if(!IsNewBar(rmData.symbol))
return;
double rsi[], rsiReverse[], ema[], close[];
ArraySetAsSeries(rsi, true);
ArraySetAsSeries(rsiReverse, true);
ArraySetAsSeries(ema, true);
ArraySetAsSeries(close, true);
rmData.lastBarEMAPrev = rmData.lastBarEMA;
rmData.lastBarClosePrev = rmData.lastBarClose;
if(CopyBuffer(rmData.rsiHandle, 0, 0, 1, rsi) > 0)
rmData.lastBarRSI = rsi[0];
if(CopyBuffer(rmData.rsiReverseHandle, 0, 0, 1, rsiReverse) > 0)
rmData.lastBarRSIReverse = rsiReverse[0];
if(CopyBuffer(rmData.emaHandle, 0, 0, 1, ema) > 0)
rmData.lastBarEMA = ema[0];
if(CopyClose(rmData.symbol, RM_InpTimeframe, 0, 1, close) > 0)
rmData.lastBarClose = close[0];
if(RM_InpEnableRSIFollow)
CheckRSIFollowStrategy(rmData.symbol);
if(RM_InpEnableRSIReverse)
CheckRSIReverseStrategy(rmData.symbol);
if(RM_InpEnableEMACross)
CheckEMACrossStrategy(rmData.symbol);
CheckExitConditions(rmData.symbol);
}
//+------------------------------------------------------------------+
@@ -0,0 +1,493 @@
//+------------------------------------------------------------------+
//| RSIReversalAsianStrategy.mqh |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| RSI Reversal Asian Strategy Data Structure |
//+------------------------------------------------------------------+
struct RSIReversalAsianData {
string symbol;
bool isInitialized;
int rsiHandle;
CTrade trade;
bool isPositionOpen;
double positionOpenPrice;
datetime positionOpenTime;
ENUM_POSITION_TYPE lastPositionType;
bool sessionCloseAttempted;
// RSI crossover variables
double rsiCurrent;
double rsiPrevious;
double rsiPrevious2;
bool rsiCrossedOverbought;
bool rsiCrossedOversold;
bool rsiCrossedExitLevel;
// Strategy parameters
int RSIPeriod;
double OverboughtLevel;
double OversoldLevel;
int TakeProfitPips;
int StopLossPips;
double MaxLotSize;
int MaxSpread;
int MaxDuration;
bool UseStopLoss;
bool UseTakeProfit;
bool UseRSIExit;
double RSIExitLevel;
bool CloseOutsideSession;
ENUM_TIMEFRAMES TimeFrame;
int MagicNumber;
int Slippage;
double point;
};
// Session times (UTC)
const int AsianSessionStart = 0; // 00:00 UTC
const int AsianSessionEnd = 8; // 08:00 UTC
//+------------------------------------------------------------------+
//| Check if current time is in Asian session |
//+------------------------------------------------------------------+
bool IsAsianSession()
{
datetime currentTime = TimeCurrent();
MqlDateTime timeStruct;
TimeToStruct(currentTime, timeStruct);
return (timeStruct.hour >= AsianSessionStart && timeStruct.hour < AsianSessionEnd);
}
//+------------------------------------------------------------------+
//| Check if trading is allowed for symbol |
//+------------------------------------------------------------------+
bool IsTradingAllowed(RSIReversalAsianData& data)
{
// Check if market is open
long tradeMode = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_MODE);
if(tradeMode != SYMBOL_TRADE_MODE_FULL)
{
return false;
}
// Check if we have enough money
if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0)
{
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Check RSI crossover conditions |
//+------------------------------------------------------------------+
void CheckRSICrossover(RSIReversalAsianData& data)
{
// Reset crossover flags
data.rsiCrossedOverbought = false;
data.rsiCrossedOversold = false;
data.rsiCrossedExitLevel = false;
// Check for overbought crossover (RSI crosses above overbought level)
if(data.rsiPrevious < data.OverboughtLevel && data.rsiCurrent >= data.OverboughtLevel)
{
data.rsiCrossedOverbought = true;
}
// Check for oversold crossover (RSI crosses below oversold level)
if(data.rsiPrevious > data.OversoldLevel && data.rsiCurrent <= data.OversoldLevel)
{
data.rsiCrossedOversold = true;
}
// Check for exit level crossover
if(data.rsiPrevious < data.RSIExitLevel && data.rsiCurrent >= data.RSIExitLevel)
{
data.rsiCrossedExitLevel = true;
}
else if(data.rsiPrevious > data.RSIExitLevel && data.rsiCurrent <= data.RSIExitLevel)
{
data.rsiCrossedExitLevel = true;
}
}
//+------------------------------------------------------------------+
//| Close all trades for the symbol |
//+------------------------------------------------------------------+
bool CloseAllTrades(RSIReversalAsianData& data, string reason = "")
{
bool allClosed = true;
int totalPositions = PositionsTotal();
if(totalPositions == 0)
return true;
for(int i = totalPositions - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == data.symbol)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == (ulong)data.MagicNumber)
{
// Try to close position with retry logic
int retryCount = 0;
bool positionClosed = false;
while(retryCount < 3 && !positionClosed)
{
if(data.trade.PositionClose(ticket))
{
data.isPositionOpen = false;
positionClosed = true;
}
else
{
int error = GetLastError();
// If error is 4756 (Trade disabled), wait longer before retry
if(error == 4756)
{
Sleep(5000); // Wait 5 seconds before retry
retryCount++;
}
else
{
// For other errors, break the loop
break;
}
}
}
if(!positionClosed)
{
allClosed = false;
}
}
}
}
}
return allClosed;
}
//+------------------------------------------------------------------+
//| Initialize RSI Reversal Asian Strategy |
//+------------------------------------------------------------------+
bool InitRSIReversalAsian(RSIReversalAsianData& data, string symbol,
int RSIPeriod, double OverboughtLevel, double OversoldLevel,
int TakeProfitPips, int StopLossPips, double MaxLotSize,
int MaxSpread, int MaxDuration, bool UseStopLoss,
bool UseTakeProfit, bool UseRSIExit, double RSIExitLevel,
bool CloseOutsideSession, ENUM_TIMEFRAMES TimeFrame,
int MagicNumber, int Slippage)
{
data.symbol = symbol;
data.isInitialized = false;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("RSIReversalAsian: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
// Wait a bit for symbol to be ready
Sleep(100);
// Get symbol point
data.point = SymbolInfoDouble(symbol, SYMBOL_POINT);
// Store parameters
data.RSIPeriod = RSIPeriod;
data.OverboughtLevel = OverboughtLevel;
data.OversoldLevel = OversoldLevel;
data.TakeProfitPips = TakeProfitPips;
data.StopLossPips = StopLossPips;
data.MaxLotSize = MaxLotSize;
data.MaxSpread = MaxSpread;
data.MaxDuration = MaxDuration;
data.UseStopLoss = UseStopLoss;
data.UseTakeProfit = UseTakeProfit;
data.UseRSIExit = UseRSIExit;
data.RSIExitLevel = RSIExitLevel;
data.CloseOutsideSession = CloseOutsideSession;
data.TimeFrame = TimeFrame;
data.MagicNumber = MagicNumber;
data.Slippage = Slippage;
// Initialize RSI indicator with retry logic (for insufficient history in backtesting)
data.rsiHandle = INVALID_HANDLE;
int retryCount = 0;
int maxRetries = 5;
while(retryCount < maxRetries && data.rsiHandle == INVALID_HANDLE)
{
data.rsiHandle = iRSI(symbol, TimeFrame, RSIPeriod, PRICE_CLOSE);
if(data.rsiHandle == INVALID_HANDLE)
{
int error = GetLastError();
// Error 4805 = insufficient history - wait longer and retry
if(error == 4805 && retryCount < maxRetries - 1)
{
Sleep(1000); // Wait 1 second for history to load
retryCount++;
continue;
}
Print("RSIReversalAsian: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", error == 4805 ? "Insufficient history data" : "Unknown", ")");
return false;
}
}
if(data.rsiHandle == INVALID_HANDLE)
{
Print("RSIReversalAsian: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries");
return false;
}
// Wait a bit for the indicator to be ready
Sleep(100);
// Initialize RSI values with retry logic
double rsi[];
ArraySetAsSeries(rsi, true);
retryCount = 0;
bool rsiInitialized = false;
while(retryCount < 10 && !rsiInitialized)
{
int copied = CopyBuffer(data.rsiHandle, 0, 0, 3, rsi);
if(copied >= 3)
{
data.rsiCurrent = rsi[0];
data.rsiPrevious = rsi[1];
data.rsiPrevious2 = rsi[2];
rsiInitialized = true;
}
else
{
retryCount++;
Sleep(100);
}
}
if(!rsiInitialized)
{
// Don't fail initialization, just set default values
data.rsiCurrent = 50.0;
data.rsiPrevious = 50.0;
data.rsiPrevious2 = 50.0;
}
// Set trade parameters
data.trade.SetExpertMagicNumber(MagicNumber);
data.trade.SetDeviationInPoints(Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
// Initialize state
data.isPositionOpen = false;
data.positionOpenPrice = 0;
data.positionOpenTime = 0;
data.lastPositionType = POSITION_TYPE_BUY;
data.sessionCloseAttempted = false;
data.rsiCrossedOverbought = false;
data.rsiCrossedOversold = false;
data.rsiCrossedExitLevel = false;
data.isInitialized = true;
Print("RSIReversalAsian: Successfully initialized for symbol '", symbol, "'");
return true;
}
//+------------------------------------------------------------------+
//| Deinitialize RSI Reversal Asian Strategy |
//+------------------------------------------------------------------+
void DeinitRSIReversalAsian(RSIReversalAsianData& data)
{
if(data.rsiHandle != INVALID_HANDLE)
IndicatorRelease(data.rsiHandle);
}
//+------------------------------------------------------------------+
//| Process RSI Reversal Asian Strategy |
//+------------------------------------------------------------------+
void ProcessRSIReversalAsian(RSIReversalAsianData& data, double lotSize)
{
if(!data.isInitialized)
return;
// Check if trading is allowed
if(!IsTradingAllowed(data))
{
return;
}
// Check if we're in Asian session
if(!IsAsianSession())
{
// Close all positions if outside Asian session and CloseOutsideSession is true
if(data.CloseOutsideSession && !data.sessionCloseAttempted)
{
CloseAllTrades(data, "Outside Asian session");
data.sessionCloseAttempted = true;
}
return;
}
else
{
// Reset the session close attempt flag when we enter Asian session
data.sessionCloseAttempted = false;
}
// Get current spread
double spread = SymbolInfoDouble(data.symbol, SYMBOL_ASK) - SymbolInfoDouble(data.symbol, SYMBOL_BID);
int spreadInPips = (int)(spread / data.point);
// Check if spread is too high
if(spreadInPips > data.MaxSpread)
{
return;
}
// Get RSI values from bar data
double rsi[];
ArraySetAsSeries(rsi, true);
int copied = CopyBuffer(data.rsiHandle, 0, 0, 3, rsi);
if(copied < 3)
{
return;
}
// Update RSI values
data.rsiPrevious2 = data.rsiPrevious;
data.rsiPrevious = data.rsiCurrent;
data.rsiCurrent = rsi[0];
// Validate RSI values
if(data.rsiCurrent == 0 || data.rsiPrevious == 0)
{
return;
}
// Check for RSI crossovers
CheckRSICrossover(data);
// Get current prices
double currentBid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
double currentAsk = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
// Check for open position
bool hasOpenPosition = PositionExistsByMagic(data.symbol, (ulong)data.MagicNumber);
if(hasOpenPosition)
{
// Get position details
ulong ticket = GetPositionTicketByMagic(data.symbol, (ulong)data.MagicNumber);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
// Check for RSI exit if enabled
if(data.UseRSIExit && data.rsiCrossedExitLevel)
{
bool shouldExit = false;
// For long positions, exit when RSI crosses above exit level
if(posType == POSITION_TYPE_BUY && data.rsiCurrent >= data.RSIExitLevel && data.rsiPrevious < data.RSIExitLevel)
{
shouldExit = true;
}
// For short positions, exit when RSI crosses below exit level
else if(posType == POSITION_TYPE_SELL && data.rsiCurrent <= data.RSIExitLevel && data.rsiPrevious > data.RSIExitLevel)
{
shouldExit = true;
}
if(shouldExit)
{
CloseAllTrades(data, "RSI Exit Crossover");
return;
}
}
// Check for timeout
if(TimeCurrent() - openTime > data.MaxDuration * 3600)
{
CloseAllTrades(data, "Timeout");
return;
}
}
}
// If no position is open, look for entry signals based on RSI crossover
if(!hasOpenPosition)
{
// Place buy order if RSI crosses below oversold level (oversold crossover)
if(data.rsiCrossedOversold)
{
double sl = data.UseStopLoss ? currentBid - data.StopLossPips * data.point : 0;
double tp = data.UseTakeProfit ? currentBid + data.TakeProfitPips * data.point : 0;
if(data.UseStopLoss && sl >= currentBid)
return;
if(data.UseTakeProfit && tp <= currentBid)
return;
// Set trade parameters
data.trade.SetDeviationInPoints(data.Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
data.trade.SetExpertMagicNumber(data.MagicNumber);
// Use dynamic lot size
double tradeLotSize = lotSize > 0 ? lotSize : data.MaxLotSize;
// Place buy order using CTrade
if(data.trade.Buy(tradeLotSize, data.symbol, currentAsk, sl, tp, "RSI Oversold Crossover Buy"))
{
data.isPositionOpen = true;
data.positionOpenPrice = currentAsk;
data.positionOpenTime = TimeCurrent();
data.lastPositionType = POSITION_TYPE_BUY;
}
}
// Place sell order if RSI crosses above overbought level (overbought crossover)
else if(data.rsiCrossedOverbought)
{
double sl = data.UseStopLoss ? currentAsk + data.StopLossPips * data.point : 0;
double tp = data.UseTakeProfit ? currentAsk - data.TakeProfitPips * data.point : 0;
if(data.UseStopLoss && sl <= currentAsk)
return;
if(data.UseTakeProfit && tp >= currentAsk)
return;
// Set trade parameters
data.trade.SetDeviationInPoints(data.Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
data.trade.SetExpertMagicNumber(data.MagicNumber);
// Use dynamic lot size
double tradeLotSize = lotSize > 0 ? lotSize : data.MaxLotSize;
// Place sell order using CTrade
if(data.trade.Sell(tradeLotSize, data.symbol, currentBid, sl, tp, "RSI Overbought Crossover Sell"))
{
data.isPositionOpen = true;
data.positionOpenPrice = currentBid;
data.positionOpenTime = TimeCurrent();
data.lastPositionType = POSITION_TYPE_SELL;
}
}
}
}
@@ -0,0 +1,451 @@
//+------------------------------------------------------------------+
//| RSIScalpingStrategy.mqh |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| RSI Scalping Strategy Data Structure |
//+------------------------------------------------------------------+
struct RSIScalpingData {
string symbol;
bool isInitialized;
CTrade trade;
int rsi_handle;
double rsi_buffer[];
double rsi_prev;
double rsi_current;
double rsi_two_bars_ago;
bool position_open;
ulong position_ticket;
ENUM_POSITION_TYPE current_position_type;
datetime last_bar_time;
bool rsi_against_position;
int bars_against_count;
};
string ErrorDescription(int errorCode)
{
switch(errorCode)
{
case 4801: return "Symbol not found";
case 4802: return "Symbol not selected";
case 4803: return "Symbol not visible";
case 4804: return "Symbol not available";
case 4805: return "Cannot load indicator - insufficient history data";
default: return "Unknown error " + IntegerToString(errorCode);
}
}
bool InitRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
ENUM_APPLIED_PRICE RSI_Applied_Price, int MagicNumber, int Slippage)
{
data.symbol = symbol;
data.isInitialized = false;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("RSIScalping: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false; // Return false but don't fail entire EA
}
// Wait a bit for symbol to be ready
Sleep(100);
// Try to create RSI indicator with retry logic (for insufficient history in backtesting)
data.rsi_handle = INVALID_HANDLE;
int retryCount = 0;
int maxRetries = 5;
while(retryCount < maxRetries && data.rsi_handle == INVALID_HANDLE)
{
data.rsi_handle = iRSI(symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
if(data.rsi_handle == INVALID_HANDLE)
{
int error = GetLastError();
// Error 4805 = insufficient history - wait longer and retry
if(error == 4805 && retryCount < maxRetries - 1)
{
Sleep(1000); // Wait 1 second for history to load
retryCount++;
continue;
}
Print("RSIScalping: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", ErrorDescription(error), ")");
return false; // Return false but don't fail entire EA
}
}
if(data.rsi_handle == INVALID_HANDLE)
{
Print("RSIScalping: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries");
return false;
}
data.trade.SetExpertMagicNumber(MagicNumber);
data.trade.SetDeviationInPoints(Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_FOK);
ArraySetAsSeries(data.rsi_buffer, true);
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
data.isInitialized = true;
Print("RSIScalping: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitRSIScalping(RSIScalpingData& data)
{
if(data.rsi_handle != INVALID_HANDLE)
IndicatorRelease(data.rsi_handle);
}
bool UpdateRSI(RSIScalpingData& data)
{
if(CopyBuffer(data.rsi_handle, 0, 0, 3, data.rsi_buffer) < 3)
return false;
data.rsi_current = data.rsi_buffer[0];
data.rsi_prev = data.rsi_buffer[1];
data.rsi_two_bars_ago = data.rsi_buffer[2];
return true;
}
void CheckExistingPosition(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
double RSI_Oversold, double RSI_Overbought, double RSI_Target_Buy,
double RSI_Target_Sell, int BarsToWait)
{
// Always check if position exists, even if tracking says it doesn't
bool positionExists = PositionExistsByMagic(data.symbol, MagicNumber);
if(!positionExists && data.position_open)
{
// Position was closed externally, reset tracking
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
return;
}
if(!positionExists)
return;
// Update tracking if we have a position but tracking was lost
if(!data.position_open && positionExists)
{
ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
{
data.position_ticket = ticket;
data.position_open = true;
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
}
// Verify our tracked position still exists
if(data.position_open && data.position_ticket > 0)
{
if(!PositionSelectByTicketSymbolAndMagic(data.position_ticket, data.symbol, MagicNumber))
{
// Try to find the position again
ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
{
data.position_ticket = ticket;
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
else
{
// Position doesn't exist, reset tracking
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
return;
}
}
else
{
// Update position type in case it changed (shouldn't happen, but be safe)
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
}
if(data.current_position_type == POSITION_TYPE_BUY)
{
if(data.rsi_current < RSI_Oversold)
{
if(!data.rsi_against_position)
{
data.rsi_against_position = true;
data.bars_against_count = 1;
}
else
{
data.bars_against_count++;
}
if(data.bars_against_count >= BarsToWait)
{
ClosePosition(data, MagicNumber);
return;
}
}
else
{
if(data.rsi_against_position)
{
data.rsi_against_position = false;
data.bars_against_count = 0;
}
if(data.rsi_current >= RSI_Target_Buy)
{
ClosePosition(data, MagicNumber);
}
}
}
else if(data.current_position_type == POSITION_TYPE_SELL)
{
if(data.rsi_current > RSI_Overbought)
{
if(!data.rsi_against_position)
{
data.rsi_against_position = true;
data.bars_against_count = 1;
}
else
{
data.bars_against_count++;
}
if(data.bars_against_count >= BarsToWait)
{
ClosePosition(data, MagicNumber);
return;
}
}
else
{
if(data.rsi_against_position)
{
data.rsi_against_position = false;
data.bars_against_count = 0;
}
if(data.rsi_current <= RSI_Target_Sell)
{
ClosePosition(data, MagicNumber);
}
}
}
}
void CheckEntrySignals(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
double RSI_Oversold, double RSI_Overbought, double LotSize)
{
if(data.rsi_two_bars_ago <= RSI_Oversold && data.rsi_prev > RSI_Oversold)
{
OpenBuyPosition(data, MagicNumber, LotSize);
}
if(data.rsi_two_bars_ago >= RSI_Overbought && data.rsi_prev < RSI_Overbought)
{
OpenSellPosition(data, MagicNumber, LotSize);
}
}
//+------------------------------------------------------------------+
//| Normalize Lot Size According to Symbol Properties |
//+------------------------------------------------------------------+
double NormalizeLotSize(string symbol, double lotSize)
{
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
// Round to lot step
if(lotStep > 0)
lotSize = MathFloor(lotSize / lotStep) * lotStep;
// Apply min/max constraints
if(lotSize < minLot)
lotSize = minLot;
if(lotSize > maxLot)
lotSize = maxLot;
return lotSize;
}
void OpenBuyPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
{
if(PositionExistsByMagic(data.symbol, MagicNumber))
return;
// Normalize lot size according to symbol properties
double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
if(data.trade.Buy(normalizedLot, data.symbol, ask, 0, 0, "RSI Scalping Buy"))
{
ulong new_ticket = data.trade.ResultOrder();
if(new_ticket > 0)
{
if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
{
data.position_ticket = new_ticket;
data.position_open = true;
data.current_position_type = POSITION_TYPE_BUY;
}
}
}
}
void OpenSellPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
{
if(PositionExistsByMagic(data.symbol, MagicNumber))
return;
// Normalize lot size according to symbol properties
double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
if(data.trade.Sell(normalizedLot, data.symbol, bid, 0, 0, "RSI Scalping Sell"))
{
ulong new_ticket = data.trade.ResultOrder();
if(new_ticket > 0)
{
if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
{
data.position_ticket = new_ticket;
data.position_open = true;
data.current_position_type = POSITION_TYPE_SELL;
}
}
}
}
void ClosePosition(RSIScalpingData& data, int MagicNumber)
{
// First verify position still exists
if(!PositionExistsByMagic(data.symbol, MagicNumber))
{
// Position doesn't exist, reset tracking
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
return;
}
// Try to close by ticket first (more reliable)
bool closed = false;
if(data.position_ticket > 0)
{
if(PositionSelectByTicket(data.position_ticket))
{
// Verify it's our position
if(PositionGetString(POSITION_SYMBOL) == data.symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
closed = data.trade.PositionClose(data.position_ticket);
if(!closed)
{
Print("RSIScalping: Failed to close position by ticket ", data.position_ticket,
" - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
}
}
}
}
// If ticket method failed, try magic number method
if(!closed)
{
closed = ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
if(!closed)
{
Print("RSIScalping: Failed to close position by magic number for '", data.symbol,
"' - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
}
}
// Verify position is actually closed
if(closed)
{
// Wait a moment and verify
Sleep(50);
if(!PositionExistsByMagic(data.symbol, MagicNumber))
{
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
Print("RSIScalping: Position successfully closed for '", data.symbol, "'");
}
else
{
Print("RSIScalping: Warning - Close returned success but position still exists for '", data.symbol, "'");
// Try one more time
Sleep(100);
if(PositionExistsByMagic(data.symbol, MagicNumber))
{
ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
}
// Reset tracking anyway to prevent getting stuck
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
}
}
else
{
// Close failed, but reset tracking to prevent getting stuck
// The position might have been closed externally
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
}
}
void ProcessRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
ENUM_APPLIED_PRICE RSI_Applied_Price, double RSI_Overbought,
double RSI_Oversold, double RSI_Target_Buy, double RSI_Target_Sell,
int BarsToWait, double LotSize, int MagicNumber)
{
// Skip if not initialized (symbol not available)
if(!data.isInitialized)
return;
data.symbol = symbol; // Update symbol in case it changed
if(Bars(data.symbol, TimeFrame) < RSI_Period + 2)
return;
datetime current_bar_time = iTime(data.symbol, TimeFrame, 0);
if(current_bar_time == data.last_bar_time)
return;
data.last_bar_time = current_bar_time;
if(!UpdateRSI(data))
return;
CheckExistingPosition(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought,
RSI_Target_Buy, RSI_Target_Sell, BarsToWait);
if(!data.position_open && !PositionExistsByMagic(data.symbol, MagicNumber))
{
CheckEntrySignals(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought, LotSize);
}
}
//+------------------------------------------------------------------+
+675
View File
@@ -0,0 +1,675 @@
//+------------------------------------------------------------------+
//| UnitedEA.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.07"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Indicators\Trend.mqh>
#include <Indicators\Volumes.mqh>
#include "MagicNumberHelpers.mqh"
// Lot globals must exist before strategy .mqh (Darvas uses g_DB_LotSize; EMA/RC/RM use g_ES/g_RC/g_RM)
double g_ES_LotSize;
double g_RC_LotSize;
double g_RM_LotSize;
double g_DB_LotSize;
double g_DynMultLast = 1.0;
// Include strategy implementations early so structs are available
#include "Strategies/DarvasBoxStrategy.mqh"
#include "Strategies/EMASlopeDistanceStrategy.mqh"
#include "Strategies/RSICrossOverReversalStrategy.mqh"
#include "Strategies/RSIMidPointHijackStrategy.mqh"
#include "Strategies/RSIScalpingStrategy.mqh"
#include "Strategies/RSIReversalAsianStrategy.mqh"
//+------------------------------------------------------------------+
//| Strategy Enable/Disable Switches |
//+------------------------------------------------------------------+
input group "=== Strategy Enable/Disable ==="
input bool EnableDarvasBox = true;
input bool EnableEMASlopeDistance = true;
input bool EnableRSICrossOverReversal = true;
input bool EnableRSIMidPointHijack = true;
input bool EnableRSIScalpingAPPL = true;
input bool EnableRSIScalpingBTCUSD = true;
input bool EnableRSIScalpingNVDA = true;
input bool EnableRSIScalpingTSLA = true;
input bool EnableRSIScalpingXAUUSD = true;
input bool EnableRSIReversalAsianEURUSD = true;
input bool EnableRSIReversalAsianAUDUSD = true;
//+------------------------------------------------------------------+
//| Dynamic lot sizing — scale base lots vs reference deposit |
//| mult=(equity/ref)^exp; maxMult<=0 上不封顶; minMult<=0 不锁下限 |
//+------------------------------------------------------------------+
input group "=== Dynamic lot sizing (动态手数) ==="
input bool InpDynamicLotEnable = true; // Enable balance/equity-based scaling
input double InpDynamicRefDeposit = 3000.0; // Reference balance (match Tester initial deposit)
input double InpDynamicExponent = 1.15; // 1.0=linear; >1 faster growth; <1 conservative
input double InpDynamicMinMult = 0.0; // <=0 不锁下限; >0 例如0.25 为最低倍数
input double InpDynamicMaxMult = 0.0; // <=0 动态倍数不封顶; >0 上限封顶
input bool InpDynamicUseEquity = true; // true=ACCOUNT_EQUITY, false=ACCOUNT_BALANCE
input double InpDynamicStockLotCap = 0.0; // Extra cap for stock CFDs (0 = none)
//+------------------------------------------------------------------+
//| Strategy 1: DarvasBoxXAUUSD |
//+------------------------------------------------------------------+
input group "=== DarvasBox Strategy ==="
input string DB_Symbol = "XAUUSD";
input int DB_BoxPeriod = 165;
input double DB_BoxDeviation = 30000; // Increased to allow larger ranges (was 25140)
input int DB_VolumeThreshold = 0; // Set to 0 to disable volume threshold check. Volume data from indicator used instead.
input double DB_StopLoss = 1665;
input double DB_TakeProfit = 3685;
input bool DB_EnableLogging = false;
input color DB_BoxColor = clrBlue;
input int DB_BoxWidth = 1;
input ENUM_TIMEFRAMES DB_TrendTimeframe = PERIOD_H2;
input int DB_MA_Period = 125;
input ENUM_MA_METHOD DB_MA_Method = MODE_EMA;
input ENUM_APPLIED_PRICE DB_MA_Price = PRICE_WEIGHTED;
input double DB_TrendThreshold = 4.94;
input int DB_VolumeMA_Period = 110;
input double DB_VolumeThresholdMultiplier = 1.5;
input int DB_MagicNumber = 135790;
input double DB_BaseLotSize = 0.01; // Base lot at InpDynamicRefDeposit (Darvas)
//+------------------------------------------------------------------+
//| Strategy 2: EMASlopeDistanceCocktailXAUUSD |
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
//+------------------------------------------------------------------+
input group "=== EMA Slope Distance Strategy ==="
input string ES_Symbol = "XAUUSD";
input int ES_EMA_Periode = 46;
input double ES_PreisSchwelle = 600.0;
input double ES_SteigungSchwelle = 80.0;
input int ES_ÜberwachungTimeout = 800;
input double ES_TrailingStop = 250.0;
input double ES_LotGröße = 0.03;
input int ES_MagicNumber = 12350;
input bool ES_UseSpreadAdjustment = true;
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
input bool ES_UseBarData = true;
input int ES_MaxTradesPerCrossover = 9;
input int ES_ProfitCheckBars = 18;
input bool ES_CloseUnprofitableTrades = true;
//+------------------------------------------------------------------+
//| Strategy 3: RSICrossOverReversalXAUUSD |
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
//+------------------------------------------------------------------+
input group "=== RSI CrossOver Reversal Strategy ==="
input string RC_Symbol = "XAUUSD";
input int RC_MagicNumber = 7;
input int RC_rsiPeriod = 19;
input int RC_overboughtLevel = 93;
input int RC_oversoldLevel = 22;
input double RC_entryRSIBuySpread = 0;
input double RC_entryRSISellSpread = 0;
input double RC_lotSize = 0.01;
input int RC_slippage = 3;
input int RC_cooldownSeconds = 209;
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
input ENUM_TIMEFRAMES RC_TimeFrame2 = PERIOD_M1;
input ENUM_TIMEFRAMES RC_BarTimeFrame = PERIOD_M12;
input int RC_emaPeriod = 140;
input double RC_emaSlopeThreshold = 105;
input double RC_exitBuyRSI = 86;
input double RC_exitSellRSI = 10;
input double RC_TrailingStop = 295;
input double RC_emaDistanceThreshold = 165;
input int RC_tradingHourOneBegin = 24;
input int RC_tradingHourOneEnd = 22;
input int RC_tradingHourTwoBegin = 6;
input int RC_tradingHourTwoEnd = 19;
input bool RC_Sunday = false;
input bool RC_Monday = false;
input bool RC_Tuesday = true;
input bool RC_Wednesday = true;
input bool RC_Thursday = true;
input bool RC_Friday = false;
input bool RC_Saturday = false;
//+------------------------------------------------------------------+
//| Strategy 4: RSIMidPointHijackXAUUSD |
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
//+------------------------------------------------------------------+
input group "=== RSI MidPoint Hijack Strategy ==="
input string RM_Symbol = "XAUUSD";
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
input double RM_InpLotSize = 0.02;
input int RM_InpMagicNumberRSIFollow = 1001;
input int RM_InpMagicNumberRSIReverse = 1002;
input int RM_InpMagicNumberEMACross = 1003;
input bool RM_InpEnableRSIFollow = true;
input bool RM_InpEnableRSIReverse = true;
input bool RM_InpEnableEMACross = true;
input bool RM_InpEnableStrategyLock = false;
input double RM_InpLockProfitThreshold = 0.0;
input bool RM_InpCloseOppositeTrades = false;
input int RM_InpRSIPeriod = 32;
input int RM_InpRSIOverbought = 78;
input int RM_InpRSIOversold = 46;
input int RM_InpRSIExitLevel = 44;
input int RM_InpRSIFollowStartHour = 23;
input int RM_InpRSIFollowEndHour = 8;
input bool RM_InpRSIFollowCloseOutsideHours = false;
input int RM_InpRSIReversePeriod = 59;
input int RM_InpRSIReverseOverbought = 51;
input int RM_InpRSIReverseOversold = 49;
input int RM_InpRSIReverseCrossLevel = 53;
input int RM_InpRSIReverseExitLevel = 48;
input int RM_InpRSIReverseStartHour = 7;
input int RM_InpRSIReverseEndHour = 13;
input bool RM_InpRSIReverseCloseOutsideHours = false;
input int RM_InpRSIReverseCooldownBars = 15;
input bool RM_InpRSIReverseCooldownOnLoss = true;
input int RM_InpEMAPeriod = 120;
input int RM_InpEMACrossStartHour = 8;
input int RM_InpEMACrossEndHour = 14;
input bool RM_InpEMACrossCloseOutsideHours = true;
input bool RM_InpUseEMADistanceEntry = true;
input double RM_InpEMADistancePips = 160.0;
input int RM_InpEMADistancePeriod = 26;
//+------------------------------------------------------------------+
//| Strategy 5-10: RSI Scalping Strategies |
//| Each RSI Scalping strategy trades on its own symbol: |
//| - APPL: Apple stock (AAPL) |
//| - BTCUSD: Bitcoin/USD |
//| - NVDA: NVIDIA stock |
//| - TSLA: Tesla stock |
//| - XAUUSD: Gold/USD |
//| |
//| PEPPERSTONE US SYMBOL FORMATS: |
//| - Stocks may use: "AAPL.US", "NASDAQ:AAPL", or just "AAPL" |
//| - To find correct symbols: |
//| 1. Open Market Watch (Ctrl+M) |
//| 2. Right-click > Show All |
//| 3. Search for the stock name |
//| 4. Use the exact symbol name shown |
//+------------------------------------------------------------------+
input group "=== RSI Scalping APPL (AAPL) - Pepperstone US ==="
input string RS_APPL_Symbol = "AAPL.US"; // Try: "AAPL.US", "NASDAQ:AAPL", or "AAPL"
input ENUM_TIMEFRAMES RS_APPL_TimeFrame = PERIOD_M10;
input int RS_APPL_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_APPL_RSI_Applied_Price = PRICE_CLOSE;
input double RS_APPL_RSI_Overbought = 80;
input double RS_APPL_RSI_Oversold = 78;
input double RS_APPL_RSI_Target_Buy = 94;
input double RS_APPL_RSI_Target_Sell = 44;
input int RS_APPL_BarsToWait = 7;
input double RS_APPL_LotSize = 25;
input int RS_APPL_MagicNumber = 20001;
input int RS_APPL_Slippage = 3;
input group "=== RSI Scalping BTCUSD ==="
input string RS_BTCUSD_Symbol = "BTCUSD"; // Pepperstone may use: "BTCUSD", "BTC/USD", or "BTCUSD.c"
input ENUM_TIMEFRAMES RS_BTCUSD_TimeFrame = PERIOD_H1;
input int RS_BTCUSD_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_BTCUSD_RSI_Applied_Price = PRICE_CLOSE;
input double RS_BTCUSD_RSI_Overbought = 90;
input double RS_BTCUSD_RSI_Oversold = 73;
input double RS_BTCUSD_RSI_Target_Buy = 88;
input double RS_BTCUSD_RSI_Target_Sell = 48;
input int RS_BTCUSD_BarsToWait = 6;
input double RS_BTCUSD_LotSize = 0.1;
input int RS_BTCUSD_MagicNumber = 123459123;
input int RS_BTCUSD_Slippage = 3;
input group "=== RSI Scalping NVDA - Pepperstone US ==="
input string RS_NVDA_Symbol = "NVDA.US"; // Try: "NVDA.US", "NASDAQ:NVDA", or "NVDA"
input ENUM_TIMEFRAMES RS_NVDA_TimeFrame = PERIOD_M15;
input int RS_NVDA_RSI_Period = 8;
input ENUM_APPLIED_PRICE RS_NVDA_RSI_Applied_Price = PRICE_CLOSE;
input double RS_NVDA_RSI_Overbought = 36;
input double RS_NVDA_RSI_Oversold = 38;
input double RS_NVDA_RSI_Target_Buy = 90;
input double RS_NVDA_RSI_Target_Sell = 70;
input int RS_NVDA_BarsToWait = 5;
input double RS_NVDA_LotSize = 50;
input int RS_NVDA_MagicNumber = 20003;
input int RS_NVDA_Slippage = 3;
input group "=== RSI Scalping TSLA - Pepperstone US ==="
input string RS_TSLA_Symbol = "TSLA.US"; // Try: "TSLA.US", "NASDAQ:TSLA", or "TSLA"
input ENUM_TIMEFRAMES RS_TSLA_TimeFrame = PERIOD_H1;
input int RS_TSLA_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_TSLA_RSI_Applied_Price = PRICE_CLOSE;
input double RS_TSLA_RSI_Overbought = 54;
input double RS_TSLA_RSI_Oversold = 73;
input double RS_TSLA_RSI_Target_Buy = 87;
input double RS_TSLA_RSI_Target_Sell = 33;
input int RS_TSLA_BarsToWait = 1;
input double RS_TSLA_LotSize = 50;
input int RS_TSLA_MagicNumber = 125421321;
input int RS_TSLA_Slippage = 3;
input group "=== RSI Scalping XAUUSD ==="
input string RS_XAUUSD_Symbol = "XAUUSD";
input ENUM_TIMEFRAMES RS_XAUUSD_TimeFrame = PERIOD_H1;
input int RS_XAUUSD_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_XAUUSD_RSI_Applied_Price = PRICE_CLOSE;
input double RS_XAUUSD_RSI_Overbought = 71;
input double RS_XAUUSD_RSI_Oversold = 57;
input double RS_XAUUSD_RSI_Target_Buy = 80;
input double RS_XAUUSD_RSI_Target_Sell = 57;
input int RS_XAUUSD_BarsToWait = 4;
input double RS_XAUUSD_LotSize = 0.1;
input int RS_XAUUSD_MagicNumber = 129102315;
input int RS_XAUUSD_Slippage = 3;
//+------------------------------------------------------------------+
//| Strategy 11-12: RSI Reversal Asian Strategies |
//| Each RSI Reversal Asian strategy trades on its own symbol: |
//| - EURUSD: Euro/USD |
//| - AUDUSD: Australian Dollar/USD |
//+------------------------------------------------------------------+
input group "=== RSI Reversal Asian EURUSD ==="
input string RRA_EURUSD_Symbol = "EURUSD";
input int RRA_EURUSD_RSIPeriod = 28;
input double RRA_EURUSD_OverboughtLevel = 60;
input double RRA_EURUSD_OversoldLevel = 8;
input int RRA_EURUSD_TakeProfitPips = 175;
input int RRA_EURUSD_StopLossPips = 5;
input double RRA_EURUSD_MaxLotSize = 0.1;
input int RRA_EURUSD_MaxSpread = 1000;
input int RRA_EURUSD_MaxDuration = 270;
input bool RRA_EURUSD_UseStopLoss = false;
input bool RRA_EURUSD_UseTakeProfit = false;
input bool RRA_EURUSD_UseRSIExit = true;
input double RRA_EURUSD_RSIExitLevel = 55;
input bool RRA_EURUSD_CloseOutsideSession = false;
input ENUM_TIMEFRAMES RRA_EURUSD_TimeFrame = PERIOD_M15;
input int RRA_EURUSD_MagicNumber = 30001;
input int RRA_EURUSD_Slippage = 3;
input group "=== RSI Reversal Asian AUDUSD ==="
input string RRA_AUDUSD_Symbol = "AUDUSD";
input int RRA_AUDUSD_RSIPeriod = 28;
input double RRA_AUDUSD_OverboughtLevel = 68;
input double RRA_AUDUSD_OversoldLevel = 30;
input int RRA_AUDUSD_TakeProfitPips = 175;
input int RRA_AUDUSD_StopLossPips = 5;
input double RRA_AUDUSD_MaxLotSize = 0.2;
input int RRA_AUDUSD_MaxSpread = 1000;
input int RRA_AUDUSD_MaxDuration = 340;
input bool RRA_AUDUSD_UseStopLoss = false;
input bool RRA_AUDUSD_UseTakeProfit = false;
input bool RRA_AUDUSD_UseRSIExit = true;
input double RRA_AUDUSD_RSIExitLevel = 48;
input bool RRA_AUDUSD_CloseOutsideSession = true;
input ENUM_TIMEFRAMES RRA_AUDUSD_TimeFrame = PERIOD_M15;
input int RRA_AUDUSD_MagicNumber = 30002;
input int RRA_AUDUSD_Slippage = 3;
//+------------------------------------------------------------------+
//| Global Variables - DarvasBox |
//+------------------------------------------------------------------+
struct DarvasBoxData {
string symbol;
bool isInitialized;
double boxHigh;
double boxLow;
bool boxFormed;
datetime lastBoxTime;
string boxName;
double minStopLevel;
double point;
CTrade trade;
int maHandle;
int volumeHandle;
datetime lastBarTime;
};
//+------------------------------------------------------------------+
//| Global Variables - EMA Slope Distance |
//+------------------------------------------------------------------+
struct EMASlopeData {
string symbol;
bool isInitialized;
int ema_handle;
double ema_array[];
datetime letzte_überwachung_zeit;
bool überwachung_aktiv;
bool preis_trigger_aktiv;
bool steigung_trigger_aktiv;
int ticket;
CTrade trade;
int trades_in_current_crossover;
bool crossover_detected;
datetime trade_open_time;
datetime last_bar_time;
};
//+------------------------------------------------------------------+
//| Global Variables - RSI CrossOver Reversal |
//+------------------------------------------------------------------+
struct RSICrossOverData {
string symbol;
bool isInitialized;
int rsiHandle;
int emaHandle;
double previousRSIDef;
CTrade trade;
datetime lastTradeTime;
datetime bartime;
bool WeekDays[7];
datetime lastBarTime;
};
//+------------------------------------------------------------------+
//| Global Variables - RSI MidPoint Hijack |
//+------------------------------------------------------------------+
struct RSIMidPointData {
string symbol;
bool isInitialized;
int rsiHandle;
int rsiReverseHandle;
int emaHandle;
bool rsiOverbought;
bool rsiOversold;
bool rsiReverseOverbought;
bool rsiReverseOversold;
CTrade trade;
CPositionInfo positionInfo;
bool emaCrossBuySignal;
bool emaCrossSellSignal;
int emaCrossSignalBar;
datetime lastBarTime;
datetime rsiReverseLastCloseTime;
bool rsiReverseInCooldown;
double lastBarRSI;
double lastBarRSIReverse;
double lastBarEMA;
double lastBarClose;
double lastBarEMAPrev;
double lastBarClosePrev;
};
//+------------------------------------------------------------------+
//| Global Strategy Instances |
//+------------------------------------------------------------------+
DarvasBoxData dbData;
EMASlopeData esData;
RSICrossOverData rcData;
RSIMidPointData rmData;
RSIScalpingData rsAPPLData;
RSIScalpingData rsBTCUSDData;
RSIScalpingData rsNVDAData;
RSIScalpingData rsTSLAData;
RSIScalpingData rsXAUUSDData;
//+------------------------------------------------------------------+
//| Global Variables - RSI Reversal Asian |
//+------------------------------------------------------------------+
RSIReversalAsianData rraEURUSDData;
RSIReversalAsianData rraAUDUSDData;
//+------------------------------------------------------------------+
//| Dynamic lot helpers |
//+------------------------------------------------------------------+
double DynClamp(const double v, const double lo, const double hi)
{
return MathMax(lo, MathMin(hi, v));
}
// maxMult<=0: no ceiling. minMult<=0: no floor on raw (equity/ref)^exp.
double ApplyDynamicMultClamp(const double mult)
{
double m = mult;
if(InpDynamicMinMult > 0.0)
m = MathMax(m, InpDynamicMinMult);
if(InpDynamicMaxMult > 0.0)
m = MathMin(m, InpDynamicMaxMult);
return m;
}
double NormalizeVolumeForSymbol(const string symbol, double lots)
{
double minL = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double maxL = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if(step > 0.0)
lots = MathFloor(lots / step + 1e-12) * step;
if(lots < minL) lots = minL;
if(lots > maxL) lots = maxL;
return lots;
}
double GetDynamicMultiplier()
{
if(!InpDynamicLotEnable)
return 1.0;
double cap = InpDynamicUseEquity ? AccountInfoDouble(ACCOUNT_EQUITY) : AccountInfoDouble(ACCOUNT_BALANCE);
if(cap <= 0.0)
cap = InpDynamicRefDeposit;
double refv = MathMax(InpDynamicRefDeposit, 1.0);
double ratio = cap / refv;
if(ratio <= 0.0)
ratio = 1.0;
double mult = MathPow(ratio, InpDynamicExponent);
return ApplyDynamicMultClamp(mult);
}
// baseLot = size at reference deposit; optionalCap 0 = no extra ceiling (broker min/max still apply)
double DynamicLotForSymbol(const string symbol, const double baseLot, const double optionalCap = 0.0)
{
double mult = GetDynamicMultiplier();
g_DynMultLast = mult;
double v = baseLot * mult;
if(optionalCap > 0.0 && v > optionalCap)
v = optionalCap;
return NormalizeVolumeForSymbol(symbol, v);
}
void RefreshDynamicStrategyLots()
{
if(!InpDynamicLotEnable)
{
g_ES_LotSize = NormalizeVolumeForSymbol(ES_Symbol, ES_LotGröße);
g_RC_LotSize = NormalizeVolumeForSymbol(RC_Symbol, RC_lotSize);
g_RM_LotSize = NormalizeVolumeForSymbol(RM_Symbol, RM_InpLotSize);
g_DB_LotSize = NormalizeVolumeForSymbol(DB_Symbol, DB_BaseLotSize);
g_DynMultLast = 1.0;
return;
}
g_ES_LotSize = DynamicLotForSymbol(ES_Symbol, ES_LotGröße);
g_RC_LotSize = DynamicLotForSymbol(RC_Symbol, RC_lotSize);
g_RM_LotSize = DynamicLotForSymbol(RM_Symbol, RM_InpLotSize);
g_DB_LotSize = DynamicLotForSymbol(DB_Symbol, DB_BaseLotSize);
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
int initResult = INIT_SUCCEEDED;
RefreshDynamicStrategyLots();
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
if(EnableDarvasBox)
if(!InitDarvasBox(DB_Symbol))
Print("Warning: DarvasBox strategy failed to initialize for symbol '", DB_Symbol, "'");
if(EnableEMASlopeDistance)
if(!InitEMASlopeDistance(ES_Symbol))
Print("Warning: EMASlopeDistance strategy failed to initialize for symbol '", ES_Symbol, "'");
if(EnableRSICrossOverReversal)
if(!InitRSICrossOverReversal(RC_Symbol))
Print("Warning: RSICrossOverReversal strategy failed to initialize for symbol '", RC_Symbol, "'");
if(EnableRSIMidPointHijack)
if(!InitRSIMidPointHijack(RM_Symbol))
Print("Warning: RSIMidPointHijack strategy failed to initialize for symbol '", RM_Symbol, "'");
// Initialize RSI Scalping strategies - don't fail entire EA if symbol unavailable
if(EnableRSIScalpingAPPL)
InitRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price, RS_APPL_MagicNumber, RS_APPL_Slippage);
if(EnableRSIScalpingBTCUSD)
InitRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price, RS_BTCUSD_MagicNumber, RS_BTCUSD_Slippage);
if(EnableRSIScalpingNVDA)
InitRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price, RS_NVDA_MagicNumber, RS_NVDA_Slippage);
if(EnableRSIScalpingTSLA)
InitRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price, RS_TSLA_MagicNumber, RS_TSLA_Slippage);
if(EnableRSIScalpingXAUUSD)
InitRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price, RS_XAUUSD_MagicNumber, RS_XAUUSD_Slippage);
// Initialize RSI Reversal Asian strategies
if(EnableRSIReversalAsianEURUSD)
if(!InitRSIReversalAsian(rraEURUSDData, RRA_EURUSD_Symbol, RRA_EURUSD_RSIPeriod, RRA_EURUSD_OverboughtLevel, RRA_EURUSD_OversoldLevel,
RRA_EURUSD_TakeProfitPips, RRA_EURUSD_StopLossPips, RRA_EURUSD_MaxLotSize,
RRA_EURUSD_MaxSpread, RRA_EURUSD_MaxDuration, RRA_EURUSD_UseStopLoss,
RRA_EURUSD_UseTakeProfit, RRA_EURUSD_UseRSIExit, RRA_EURUSD_RSIExitLevel,
RRA_EURUSD_CloseOutsideSession, RRA_EURUSD_TimeFrame, RRA_EURUSD_MagicNumber, RRA_EURUSD_Slippage))
Print("Warning: RSIReversalAsianEURUSD strategy failed to initialize for symbol '", RRA_EURUSD_Symbol, "'");
if(EnableRSIReversalAsianAUDUSD)
if(!InitRSIReversalAsian(rraAUDUSDData, RRA_AUDUSD_Symbol, RRA_AUDUSD_RSIPeriod, RRA_AUDUSD_OverboughtLevel, RRA_AUDUSD_OversoldLevel,
RRA_AUDUSD_TakeProfitPips, RRA_AUDUSD_StopLossPips, RRA_AUDUSD_MaxLotSize,
RRA_AUDUSD_MaxSpread, RRA_AUDUSD_MaxDuration, RRA_AUDUSD_UseStopLoss,
RRA_AUDUSD_UseTakeProfit, RRA_AUDUSD_UseRSIExit, RRA_AUDUSD_RSIExitLevel,
RRA_AUDUSD_CloseOutsideSession, RRA_AUDUSD_TimeFrame, RRA_AUDUSD_MagicNumber, RRA_AUDUSD_Slippage))
Print("Warning: RSIReversalAsianAUDUSD strategy failed to initialize for symbol '", RRA_AUDUSD_Symbol, "'");
string acctCur = AccountInfoString(ACCOUNT_CURRENCY);
double eq0 = AccountInfoDouble(ACCOUNT_EQUITY);
double refvInit = MathMax(InpDynamicRefDeposit, 1.0);
double capInit = InpDynamicUseEquity ? eq0 : AccountInfoDouble(ACCOUNT_BALANCE);
if(capInit <= 0.0)
capInit = refvInit;
double ratioInit = capInit / refvInit;
double rawPowInit = MathPow(ratioInit, InpDynamicExponent);
Print("United EA v1.07 ", acctCur, " equity=", DoubleToString(eq0, 2), " equity/ref=", DoubleToString(ratioInit, 6),
" raw^exp=", DoubleToString(rawPowInit, 6), " multOut=", DoubleToString(g_DynMultLast, 6),
" minM=", InpDynamicMinMult, " maxM=", InpDynamicMaxMult, " ref=", InpDynamicRefDeposit, " exp=", InpDynamicExponent,
" lots ES=", g_ES_LotSize, " RC=", g_RC_LotSize, " RM=", g_RM_LotSize, " DB=", g_DB_LotSize);
Print("United EA initialized. Active strategies: ",
(EnableDarvasBox ? "DarvasBox " : ""),
(EnableEMASlopeDistance ? "EMASlope " : ""),
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
(EnableRSIMidPointHijack ? "RSIMidPoint " : ""),
(EnableRSIScalpingAPPL ? "RSIScalpingAPPL " : ""),
(EnableRSIScalpingBTCUSD ? "RSIScalpingBTCUSD " : ""),
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""),
(EnableRSIReversalAsianEURUSD ? "RSIReversalAsianEURUSD " : ""),
(EnableRSIReversalAsianAUDUSD ? "RSIReversalAsianAUDUSD " : ""));
return initResult;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(EnableDarvasBox)
DeinitDarvasBox();
if(EnableEMASlopeDistance)
DeinitEMASlopeDistance();
if(EnableRSICrossOverReversal)
DeinitRSICrossOverReversal();
if(EnableRSIMidPointHijack)
DeinitRSIMidPointHijack();
if(EnableRSIScalpingAPPL)
DeinitRSIScalping(rsAPPLData);
if(EnableRSIScalpingBTCUSD)
DeinitRSIScalping(rsBTCUSDData);
if(EnableRSIScalpingNVDA)
DeinitRSIScalping(rsNVDAData);
if(EnableRSIScalpingTSLA)
DeinitRSIScalping(rsTSLAData);
if(EnableRSIScalpingXAUUSD)
DeinitRSIScalping(rsXAUUSDData);
if(EnableRSIReversalAsianEURUSD)
DeinitRSIReversalAsian(rraEURUSDData);
if(EnableRSIReversalAsianAUDUSD)
DeinitRSIReversalAsian(rraAUDUSDData);
Print("United EA deinitialized. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
RefreshDynamicStrategyLots();
if(EnableDarvasBox)
ProcessDarvasBox(DB_Symbol);
if(EnableEMASlopeDistance)
ProcessEMASlopeDistance(ES_Symbol);
if(EnableRSICrossOverReversal)
ProcessRSICrossOverReversal(RC_Symbol);
if(EnableRSIMidPointHijack)
ProcessRSIMidPointHijack(RM_Symbol);
if(EnableRSIScalpingAPPL)
ProcessRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price,
RS_APPL_RSI_Overbought, RS_APPL_RSI_Oversold, RS_APPL_RSI_Target_Buy, RS_APPL_RSI_Target_Sell,
RS_APPL_BarsToWait,
DynamicLotForSymbol(RS_APPL_Symbol, RS_APPL_LotSize, InpDynamicStockLotCap),
RS_APPL_MagicNumber);
if(EnableRSIScalpingBTCUSD)
ProcessRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price,
RS_BTCUSD_RSI_Overbought, RS_BTCUSD_RSI_Oversold, RS_BTCUSD_RSI_Target_Buy, RS_BTCUSD_RSI_Target_Sell,
RS_BTCUSD_BarsToWait, DynamicLotForSymbol(RS_BTCUSD_Symbol, RS_BTCUSD_LotSize), RS_BTCUSD_MagicNumber);
if(EnableRSIScalpingNVDA)
ProcessRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price,
RS_NVDA_RSI_Overbought, RS_NVDA_RSI_Oversold, RS_NVDA_RSI_Target_Buy, RS_NVDA_RSI_Target_Sell,
RS_NVDA_BarsToWait,
DynamicLotForSymbol(RS_NVDA_Symbol, RS_NVDA_LotSize, InpDynamicStockLotCap),
RS_NVDA_MagicNumber);
if(EnableRSIScalpingTSLA)
ProcessRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price,
RS_TSLA_RSI_Overbought, RS_TSLA_RSI_Oversold, RS_TSLA_RSI_Target_Buy, RS_TSLA_RSI_Target_Sell,
RS_TSLA_BarsToWait,
DynamicLotForSymbol(RS_TSLA_Symbol, RS_TSLA_LotSize, InpDynamicStockLotCap),
RS_TSLA_MagicNumber);
if(EnableRSIScalpingXAUUSD)
ProcessRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price,
RS_XAUUSD_RSI_Overbought, RS_XAUUSD_RSI_Oversold, RS_XAUUSD_RSI_Target_Buy, RS_XAUUSD_RSI_Target_Sell,
RS_XAUUSD_BarsToWait, DynamicLotForSymbol(RS_XAUUSD_Symbol, RS_XAUUSD_LotSize), RS_XAUUSD_MagicNumber);
if(EnableRSIReversalAsianEURUSD)
ProcessRSIReversalAsian(rraEURUSDData, DynamicLotForSymbol(RRA_EURUSD_Symbol, RRA_EURUSD_MaxLotSize));
if(EnableRSIReversalAsianAUDUSD)
ProcessRSIReversalAsian(rraAUDUSDData, DynamicLotForSymbol(RRA_AUDUSD_Symbol, RRA_AUDUSD_MaxLotSize));
}
//+------------------------------------------------------------------+
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

@@ -0,0 +1,641 @@
//+------------------------------------------------------------------+
//| UnitedEA.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Indicators\Trend.mqh>
#include <Indicators\Volumes.mqh>
#include "MagicNumberHelpers.mqh"
#include "PerformanceEvaluator.mqh"
//+------------------------------------------------------------------+
//| Strategy Enable/Disable Switches |
//+------------------------------------------------------------------+
input group "=== Strategy Enable/Disable ==="
input bool EnableDarvasBox = true;
input bool EnableEMASlopeDistance = true;
input bool EnableRSICrossOverReversal = true;
input bool EnableRSIMidPointHijack = true;
input bool EnableRSIScalpingAPPL = true;
input bool EnableRSIScalpingBTCUSD = true;
input bool EnableRSIScalpingNVDA = true;
input bool EnableRSIScalpingTSLA = true;
input bool EnableRSIScalpingXAUUSD = true;
//+------------------------------------------------------------------+
//| Strategy 1: DarvasBoxXAUUSD |
//+------------------------------------------------------------------+
input group "=== DarvasBox Strategy ==="
input string DB_Symbol = "XAUUSD";
input int DB_BoxPeriod = 165;
input double DB_BoxDeviation = 30000; // Increased to allow larger ranges (was 25140)
input int DB_VolumeThreshold = 0; // Set to 0 to disable volume threshold check. Volume data from indicator used instead.
input double DB_StopLoss = 1665;
input double DB_TakeProfit = 3685;
input bool DB_EnableLogging = false;
input color DB_BoxColor = clrBlue;
input int DB_BoxWidth = 1;
input ENUM_TIMEFRAMES DB_TrendTimeframe = PERIOD_H2;
input int DB_MA_Period = 125;
input ENUM_MA_METHOD DB_MA_Method = MODE_EMA;
input ENUM_APPLIED_PRICE DB_MA_Price = PRICE_WEIGHTED;
input double DB_TrendThreshold = 4.94;
input int DB_VolumeMA_Period = 110;
input double DB_VolumeThresholdMultiplier = 1.5;
input int DB_MagicNumber = 135790;
//+------------------------------------------------------------------+
//| Strategy 2: EMASlopeDistanceCocktailXAUUSD |
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
//+------------------------------------------------------------------+
input group "=== EMA Slope Distance Strategy ==="
input string ES_Symbol = "XAUUSD";
input int ES_EMA_Periode = 46;
input double ES_PreisSchwelle = 600.0;
input double ES_SteigungSchwelle = 80.0;
input int ES_ÜberwachungTimeout = 800;
input double ES_TrailingStop = 250.0;
input double ES_LotGröße = 0.03;
input int ES_MagicNumber = 12350;
input bool ES_UseSpreadAdjustment = true;
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
input bool ES_UseBarData = true;
input int ES_MaxTradesPerCrossover = 9;
input int ES_ProfitCheckBars = 18;
input bool ES_CloseUnprofitableTrades = true;
//+------------------------------------------------------------------+
//| Strategy 3: RSICrossOverReversalXAUUSD |
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
//+------------------------------------------------------------------+
input group "=== RSI CrossOver Reversal Strategy ==="
input string RC_Symbol = "XAUUSD";
input int RC_MagicNumber = 7;
input int RC_rsiPeriod = 19;
input int RC_overboughtLevel = 93;
input int RC_oversoldLevel = 22;
input double RC_entryRSIBuySpread = 0;
input double RC_entryRSISellSpread = 0;
input double RC_lotSize = 0.01;
input int RC_slippage = 3;
input int RC_cooldownSeconds = 209;
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
input ENUM_TIMEFRAMES RC_TimeFrame2 = PERIOD_M1;
input ENUM_TIMEFRAMES RC_BarTimeFrame = PERIOD_M12;
input int RC_emaPeriod = 140;
input double RC_emaSlopeThreshold = 105;
input double RC_exitBuyRSI = 86;
input double RC_exitSellRSI = 10;
input double RC_TrailingStop = 295;
input double RC_emaDistanceThreshold = 165;
input int RC_tradingHourOneBegin = 24;
input int RC_tradingHourOneEnd = 22;
input int RC_tradingHourTwoBegin = 6;
input int RC_tradingHourTwoEnd = 19;
input bool RC_Sunday = false;
input bool RC_Monday = false;
input bool RC_Tuesday = true;
input bool RC_Wednesday = true;
input bool RC_Thursday = true;
input bool RC_Friday = false;
input bool RC_Saturday = false;
//+------------------------------------------------------------------+
//| Strategy 4: RSIMidPointHijackXAUUSD |
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
//+------------------------------------------------------------------+
input group "=== RSI MidPoint Hijack Strategy ==="
input string RM_Symbol = "XAUUSD";
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
input double RM_InpLotSize = 0.02;
input int RM_InpMagicNumberRSIFollow = 1001;
input int RM_InpMagicNumberRSIReverse = 1002;
input int RM_InpMagicNumberEMACross = 1003;
input bool RM_InpEnableRSIFollow = true;
input bool RM_InpEnableRSIReverse = true;
input bool RM_InpEnableEMACross = true;
input bool RM_InpEnableStrategyLock = false;
input double RM_InpLockProfitThreshold = 0.0;
input bool RM_InpCloseOppositeTrades = false;
input int RM_InpRSIPeriod = 32;
input int RM_InpRSIOverbought = 78;
input int RM_InpRSIOversold = 46;
input int RM_InpRSIExitLevel = 44;
input int RM_InpRSIFollowStartHour = 23;
input int RM_InpRSIFollowEndHour = 8;
input bool RM_InpRSIFollowCloseOutsideHours = false;
input int RM_InpRSIReversePeriod = 59;
input int RM_InpRSIReverseOverbought = 51;
input int RM_InpRSIReverseOversold = 49;
input int RM_InpRSIReverseCrossLevel = 53;
input int RM_InpRSIReverseExitLevel = 48;
input int RM_InpRSIReverseStartHour = 7;
input int RM_InpRSIReverseEndHour = 13;
input bool RM_InpRSIReverseCloseOutsideHours = false;
input int RM_InpRSIReverseCooldownBars = 15;
input bool RM_InpRSIReverseCooldownOnLoss = true;
input int RM_InpEMAPeriod = 120;
input int RM_InpEMACrossStartHour = 8;
input int RM_InpEMACrossEndHour = 14;
input bool RM_InpEMACrossCloseOutsideHours = true;
input bool RM_InpUseEMADistanceEntry = true;
input double RM_InpEMADistancePips = 160.0;
input int RM_InpEMADistancePeriod = 26;
//+------------------------------------------------------------------+
//| Strategy 5-10: RSI Scalping Strategies |
//| Each RSI Scalping strategy trades on its own symbol: |
//| - APPL: Apple stock (AAPL) |
//| - BTCUSD: Bitcoin/USD |
//| - NVDA: NVIDIA stock |
//| - TSLA: Tesla stock |
//| - XAUUSD: Gold/USD |
//| |
//| PEPPERSTONE US SYMBOL FORMATS: |
//| - Stocks may use: "AAPL.US", "NASDAQ:AAPL", or just "AAPL" |
//| - To find correct symbols: |
//| 1. Open Market Watch (Ctrl+M) |
//| 2. Right-click > Show All |
//| 3. Search for the stock name |
//| 4. Use the exact symbol name shown |
//+------------------------------------------------------------------+
input group "=== RSI Scalping APPL (AAPL) - Pepperstone US ==="
input string RS_APPL_Symbol = "AAPL.US"; // Try: "AAPL.US", "NASDAQ:AAPL", or "AAPL"
input ENUM_TIMEFRAMES RS_APPL_TimeFrame = PERIOD_M10;
input int RS_APPL_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_APPL_RSI_Applied_Price = PRICE_CLOSE;
input double RS_APPL_RSI_Overbought = 80;
input double RS_APPL_RSI_Oversold = 78;
input double RS_APPL_RSI_Target_Buy = 94;
input double RS_APPL_RSI_Target_Sell = 44;
input int RS_APPL_BarsToWait = 7;
input double RS_APPL_LotSize = 25;
input int RS_APPL_MagicNumber = 20001;
input int RS_APPL_Slippage = 3;
input group "=== RSI Scalping BTCUSD ==="
input string RS_BTCUSD_Symbol = "BTCUSD"; // Pepperstone may use: "BTCUSD", "BTC/USD", or "BTCUSD.c"
input ENUM_TIMEFRAMES RS_BTCUSD_TimeFrame = PERIOD_H1;
input int RS_BTCUSD_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_BTCUSD_RSI_Applied_Price = PRICE_CLOSE;
input double RS_BTCUSD_RSI_Overbought = 90;
input double RS_BTCUSD_RSI_Oversold = 73;
input double RS_BTCUSD_RSI_Target_Buy = 88;
input double RS_BTCUSD_RSI_Target_Sell = 48;
input int RS_BTCUSD_BarsToWait = 6;
input double RS_BTCUSD_LotSize = 0.1;
input int RS_BTCUSD_MagicNumber = 123459123;
input int RS_BTCUSD_Slippage = 3;
input group "=== RSI Scalping NVDA - Pepperstone US ==="
input string RS_NVDA_Symbol = "NVDA.US"; // Try: "NVDA.US", "NASDAQ:NVDA", or "NVDA"
input ENUM_TIMEFRAMES RS_NVDA_TimeFrame = PERIOD_M15;
input int RS_NVDA_RSI_Period = 8;
input ENUM_APPLIED_PRICE RS_NVDA_RSI_Applied_Price = PRICE_CLOSE;
input double RS_NVDA_RSI_Overbought = 36;
input double RS_NVDA_RSI_Oversold = 38;
input double RS_NVDA_RSI_Target_Buy = 90;
input double RS_NVDA_RSI_Target_Sell = 70;
input int RS_NVDA_BarsToWait = 5;
input double RS_NVDA_LotSize = 50;
input int RS_NVDA_MagicNumber = 20003;
input int RS_NVDA_Slippage = 3;
input group "=== RSI Scalping TSLA - Pepperstone US ==="
input string RS_TSLA_Symbol = "TSLA.US"; // Try: "TSLA.US", "NASDAQ:TSLA", or "TSLA"
input ENUM_TIMEFRAMES RS_TSLA_TimeFrame = PERIOD_H1;
input int RS_TSLA_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_TSLA_RSI_Applied_Price = PRICE_CLOSE;
input double RS_TSLA_RSI_Overbought = 54;
input double RS_TSLA_RSI_Oversold = 73;
input double RS_TSLA_RSI_Target_Buy = 87;
input double RS_TSLA_RSI_Target_Sell = 33;
input int RS_TSLA_BarsToWait = 1;
input double RS_TSLA_LotSize = 50;
input int RS_TSLA_MagicNumber = 125421321;
input int RS_TSLA_Slippage = 3;
input group "=== RSI Scalping XAUUSD ==="
input string RS_XAUUSD_Symbol = "XAUUSD";
input ENUM_TIMEFRAMES RS_XAUUSD_TimeFrame = PERIOD_H1;
input int RS_XAUUSD_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_XAUUSD_RSI_Applied_Price = PRICE_CLOSE;
input double RS_XAUUSD_RSI_Overbought = 71;
input double RS_XAUUSD_RSI_Oversold = 57;
input double RS_XAUUSD_RSI_Target_Buy = 80;
input double RS_XAUUSD_RSI_Target_Sell = 57;
input int RS_XAUUSD_BarsToWait = 4;
input double RS_XAUUSD_LotSize = 0.1;
input int RS_XAUUSD_MagicNumber = 129102315;
input int RS_XAUUSD_Slippage = 3;
//+------------------------------------------------------------------+
//| Global Variables - DarvasBox |
//+------------------------------------------------------------------+
struct DarvasBoxData {
string symbol;
bool isInitialized;
double boxHigh;
double boxLow;
bool boxFormed;
datetime lastBoxTime;
string boxName;
double minStopLevel;
double point;
CTrade trade;
int maHandle;
int volumeHandle;
datetime lastBarTime;
};
//+------------------------------------------------------------------+
//| Global Variables - EMA Slope Distance |
//+------------------------------------------------------------------+
struct EMASlopeData {
string symbol;
bool isInitialized;
int ema_handle;
double ema_array[];
datetime letzte_überwachung_zeit;
bool überwachung_aktiv;
bool preis_trigger_aktiv;
bool steigung_trigger_aktiv;
int ticket;
CTrade trade;
int trades_in_current_crossover;
bool crossover_detected;
datetime trade_open_time;
datetime last_bar_time;
};
//+------------------------------------------------------------------+
//| Global Variables - RSI CrossOver Reversal |
//+------------------------------------------------------------------+
struct RSICrossOverData {
string symbol;
bool isInitialized;
int rsiHandle;
int emaHandle;
double previousRSIDef;
CTrade trade;
datetime lastTradeTime;
datetime bartime;
bool WeekDays[7];
datetime lastBarTime;
};
//+------------------------------------------------------------------+
//| Global Variables - RSI MidPoint Hijack |
//+------------------------------------------------------------------+
struct RSIMidPointData {
string symbol;
bool isInitialized;
int rsiHandle;
int rsiReverseHandle;
int emaHandle;
bool rsiOverbought;
bool rsiOversold;
bool rsiReverseOverbought;
bool rsiReverseOversold;
CTrade trade;
CPositionInfo positionInfo;
bool emaCrossBuySignal;
bool emaCrossSellSignal;
int emaCrossSignalBar;
datetime lastBarTime;
datetime rsiReverseLastCloseTime;
bool rsiReverseInCooldown;
double lastBarRSI;
double lastBarRSIReverse;
double lastBarEMA;
double lastBarClose;
double lastBarEMAPrev;
double lastBarClosePrev;
};
//+------------------------------------------------------------------+
//| Global Variables - RSI Scalping |
//+------------------------------------------------------------------+
struct RSIScalpingData {
string symbol;
bool isInitialized;
CTrade trade;
int rsi_handle;
double rsi_buffer[];
double rsi_prev;
double rsi_current;
double rsi_two_bars_ago;
bool position_open;
ulong position_ticket;
ENUM_POSITION_TYPE current_position_type;
datetime last_bar_time;
bool rsi_against_position;
int bars_against_count;
};
//+------------------------------------------------------------------+
//| Global Strategy Instances |
//+------------------------------------------------------------------+
DarvasBoxData dbData;
EMASlopeData esData;
RSICrossOverData rcData;
RSIMidPointData rmData;
RSIScalpingData rsAPPLData;
RSIScalpingData rsBTCUSDData;
RSIScalpingData rsNVDAData;
RSIScalpingData rsTSLAData;
RSIScalpingData rsXAUUSDData;
//+------------------------------------------------------------------+
//| Global Variables for Dynamic Lot Sizes |
//+------------------------------------------------------------------+
// All strategies start with minimum lot size for safety (will be adjusted by performance evaluator)
double g_DB_LotSize = 0.01; // DarvasBox uses fixed lot size
double g_ES_LotSize = 0.01; // EMA Slope Distance - start with minimum
double g_RC_LotSize = 0.01; // RSI CrossOver Reversal - start with minimum
double g_RM_LotSize = 0.01; // RSI MidPoint Hijack - start with minimum
double g_RS_APPL_LotSize = 5.0; // Stock - start with stock minimum (5.0)
double g_RS_BTCUSD_LotSize = 0.01; // Crypto - start with forex minimum (0.01)
double g_RS_NVDA_LotSize = 5.0; // Stock - start with stock minimum (5.0)
double g_RS_TSLA_LotSize = 5.0; // Stock - start with stock minimum (5.0)
double g_RS_XAUUSD_LotSize = 0.01; // Forex - start with forex minimum (0.01)
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
int initResult = INIT_SUCCEEDED;
// Initialize Performance Evaluator
InitPerformanceTracking();
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
if(EnableDarvasBox)
{
if(!InitDarvasBox(DB_Symbol))
Print("Warning: DarvasBox strategy failed to initialize for symbol '", DB_Symbol, "'");
else
RegisterStrategy("DarvasBox", DB_MagicNumber, 0.01, DB_Symbol); // Fixed lot size
}
if(EnableEMASlopeDistance)
{
if(!InitEMASlopeDistance(ES_Symbol))
Print("Warning: EMASlopeDistance strategy failed to initialize for symbol '", ES_Symbol, "'");
else
{
RegisterStrategy("EMASlopeDistance", ES_MagicNumber, ES_LotGröße, ES_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(ES_Symbol);
g_ES_LotSize = minLot;
}
}
if(EnableRSICrossOverReversal)
{
if(!InitRSICrossOverReversal(RC_Symbol))
Print("Warning: RSICrossOverReversal strategy failed to initialize for symbol '", RC_Symbol, "'");
else
{
RegisterStrategy("RSICrossOverReversal", RC_MagicNumber, RC_lotSize, RC_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RC_Symbol);
g_RC_LotSize = minLot;
}
}
if(EnableRSIMidPointHijack)
{
if(!InitRSIMidPointHijack(RM_Symbol))
Print("Warning: RSIMidPointHijack strategy failed to initialize for symbol '", RM_Symbol, "'");
else
{
RegisterStrategy("RSIMidPointHijack", RM_InpMagicNumberRSIFollow, RM_InpLotSize, RM_Symbol);
RegisterStrategy("RSIMidPointHijack_Reverse", RM_InpMagicNumberRSIReverse, RM_InpLotSize, RM_Symbol);
RegisterStrategy("RSIMidPointHijack_EMACross", RM_InpMagicNumberEMACross, RM_InpLotSize, RM_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RM_Symbol);
g_RM_LotSize = minLot;
}
}
// Initialize RSI Scalping strategies - don't fail entire EA if symbol unavailable
if(EnableRSIScalpingAPPL)
{
InitRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price, RS_APPL_MagicNumber, RS_APPL_Slippage);
RegisterStrategy("RSIScalpingAPPL", RS_APPL_MagicNumber, RS_APPL_LotSize, RS_APPL_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_APPL_Symbol);
g_RS_APPL_LotSize = minLot;
}
if(EnableRSIScalpingBTCUSD)
{
InitRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price, RS_BTCUSD_MagicNumber, RS_BTCUSD_Slippage);
RegisterStrategy("RSIScalpingBTCUSD", RS_BTCUSD_MagicNumber, RS_BTCUSD_LotSize, RS_BTCUSD_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_BTCUSD_Symbol);
g_RS_BTCUSD_LotSize = minLot;
}
if(EnableRSIScalpingNVDA)
{
InitRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price, RS_NVDA_MagicNumber, RS_NVDA_Slippage);
RegisterStrategy("RSIScalpingNVDA", RS_NVDA_MagicNumber, RS_NVDA_LotSize, RS_NVDA_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_NVDA_Symbol);
g_RS_NVDA_LotSize = minLot;
}
if(EnableRSIScalpingTSLA)
{
InitRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price, RS_TSLA_MagicNumber, RS_TSLA_Slippage);
RegisterStrategy("RSIScalpingTSLA", RS_TSLA_MagicNumber, RS_TSLA_LotSize, RS_TSLA_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_TSLA_Symbol);
g_RS_TSLA_LotSize = minLot;
}
if(EnableRSIScalpingXAUUSD)
{
InitRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price, RS_XAUUSD_MagicNumber, RS_XAUUSD_Slippage);
RegisterStrategy("RSIScalpingXAUUSD", RS_XAUUSD_MagicNumber, RS_XAUUSD_LotSize, RS_XAUUSD_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_XAUUSD_Symbol);
g_RS_XAUUSD_LotSize = minLot;
}
// Load adjusted lot sizes from performance evaluator
if(PE_EnableAutoAdjustment)
{
double adjustedLot;
adjustedLot = GetStrategyLotSize("EMASlopeDistance", ES_MagicNumber);
if(adjustedLot > 0) g_ES_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSICrossOverReversal", RC_MagicNumber);
if(adjustedLot > 0) g_RC_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIMidPointHijack", RM_InpMagicNumberRSIFollow);
if(adjustedLot > 0) g_RM_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingAPPL", RS_APPL_MagicNumber);
if(adjustedLot > 0) g_RS_APPL_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingBTCUSD", RS_BTCUSD_MagicNumber);
if(adjustedLot > 0) g_RS_BTCUSD_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingNVDA", RS_NVDA_MagicNumber);
if(adjustedLot > 0) g_RS_NVDA_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingTSLA", RS_TSLA_MagicNumber);
if(adjustedLot > 0) g_RS_TSLA_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingXAUUSD", RS_XAUUSD_MagicNumber);
if(adjustedLot > 0) g_RS_XAUUSD_LotSize = adjustedLot;
}
Print("United EA initialized. Active strategies: ",
(EnableDarvasBox ? "DarvasBox " : ""),
(EnableEMASlopeDistance ? "EMASlope " : ""),
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
(EnableRSIMidPointHijack ? "RSIMidPoint " : ""),
(EnableRSIScalpingAPPL ? "RSIScalpingAPPL " : ""),
(EnableRSIScalpingBTCUSD ? "RSIScalpingBTCUSD " : ""),
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""));
if(PE_EnableLogging)
Print(GetPerformanceSummary());
return initResult;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(EnableDarvasBox)
DeinitDarvasBox();
if(EnableEMASlopeDistance)
DeinitEMASlopeDistance();
if(EnableRSICrossOverReversal)
DeinitRSICrossOverReversal();
if(EnableRSIMidPointHijack)
DeinitRSIMidPointHijack();
if(EnableRSIScalpingAPPL)
DeinitRSIScalping(rsAPPLData);
if(EnableRSIScalpingBTCUSD)
DeinitRSIScalping(rsBTCUSDData);
if(EnableRSIScalpingNVDA)
DeinitRSIScalping(rsNVDAData);
if(EnableRSIScalpingTSLA)
DeinitRSIScalping(rsTSLAData);
if(EnableRSIScalpingXAUUSD)
DeinitRSIScalping(rsXAUUSDData);
Print("United EA deinitialized. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Process performance evaluation (checks for quarter end and adjusts lot sizes)
ProcessPerformanceEvaluation();
// Update lot sizes from performance evaluator if auto-adjustment is enabled
if(PE_EnableAutoAdjustment)
{
double adjustedLot;
adjustedLot = GetStrategyLotSize("EMASlopeDistance", ES_MagicNumber);
if(adjustedLot > 0) g_ES_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSICrossOverReversal", RC_MagicNumber);
if(adjustedLot > 0) g_RC_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIMidPointHijack", RM_InpMagicNumberRSIFollow);
if(adjustedLot > 0) g_RM_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingAPPL", RS_APPL_MagicNumber);
if(adjustedLot > 0) g_RS_APPL_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingBTCUSD", RS_BTCUSD_MagicNumber);
if(adjustedLot > 0) g_RS_BTCUSD_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingNVDA", RS_NVDA_MagicNumber);
if(adjustedLot > 0) g_RS_NVDA_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingTSLA", RS_TSLA_MagicNumber);
if(adjustedLot > 0) g_RS_TSLA_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingXAUUSD", RS_XAUUSD_MagicNumber);
if(adjustedLot > 0) g_RS_XAUUSD_LotSize = adjustedLot;
}
if(EnableDarvasBox)
ProcessDarvasBox(DB_Symbol);
if(EnableEMASlopeDistance)
ProcessEMASlopeDistance(ES_Symbol);
if(EnableRSICrossOverReversal)
ProcessRSICrossOverReversal(RC_Symbol);
if(EnableRSIMidPointHijack)
ProcessRSIMidPointHijack(RM_Symbol);
if(EnableRSIScalpingAPPL)
ProcessRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price,
RS_APPL_RSI_Overbought, RS_APPL_RSI_Oversold, RS_APPL_RSI_Target_Buy, RS_APPL_RSI_Target_Sell,
RS_APPL_BarsToWait, g_RS_APPL_LotSize, RS_APPL_MagicNumber);
if(EnableRSIScalpingBTCUSD)
ProcessRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price,
RS_BTCUSD_RSI_Overbought, RS_BTCUSD_RSI_Oversold, RS_BTCUSD_RSI_Target_Buy, RS_BTCUSD_RSI_Target_Sell,
RS_BTCUSD_BarsToWait, g_RS_BTCUSD_LotSize, RS_BTCUSD_MagicNumber);
if(EnableRSIScalpingNVDA)
ProcessRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price,
RS_NVDA_RSI_Overbought, RS_NVDA_RSI_Oversold, RS_NVDA_RSI_Target_Buy, RS_NVDA_RSI_Target_Sell,
RS_NVDA_BarsToWait, g_RS_NVDA_LotSize, RS_NVDA_MagicNumber);
if(EnableRSIScalpingTSLA)
ProcessRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price,
RS_TSLA_RSI_Overbought, RS_TSLA_RSI_Oversold, RS_TSLA_RSI_Target_Buy, RS_TSLA_RSI_Target_Sell,
RS_TSLA_BarsToWait, g_RS_TSLA_LotSize, RS_TSLA_MagicNumber);
if(EnableRSIScalpingXAUUSD)
ProcessRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price,
RS_XAUUSD_RSI_Overbought, RS_XAUUSD_RSI_Oversold, RS_XAUUSD_RSI_Target_Buy, RS_XAUUSD_RSI_Target_Sell,
RS_XAUUSD_BarsToWait, g_RS_XAUUSD_LotSize, RS_XAUUSD_MagicNumber);
}
//+------------------------------------------------------------------+
//| Include strategy implementations |
//+------------------------------------------------------------------+
#include "Strategies/DarvasBoxStrategy.mqh"
#include "Strategies/EMASlopeDistanceStrategy.mqh"
#include "Strategies/RSICrossOverReversalStrategy.mqh"
#include "Strategies/RSIMidPointHijackStrategy.mqh"
#include "Strategies/RSIScalpingStrategy.mqh"
//+------------------------------------------------------------------+
@@ -0,0 +1,159 @@
//+------------------------------------------------------------------+
//| MagicNumberHelpers.mqh |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
//+------------------------------------------------------------------+
//| Select position by symbol and magic number |
//+------------------------------------------------------------------+
bool PositionSelectByMagic(string symbol, ulong magic_number)
{
// First try to find position by symbol
if(!PositionSelect(symbol))
return false;
// Check if the selected position has the correct magic number
if(PositionGetInteger(POSITION_MAGIC) != magic_number)
{
// Position exists but wrong magic number, search all positions
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionGetTicket(i) > 0)
{
if(PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == magic_number)
{
return true;
}
}
}
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Select position by ticket and verify magic number and symbol |
//+------------------------------------------------------------------+
bool PositionSelectByTicketAndMagic(ulong ticket, ulong magic_number)
{
if(!PositionSelectByTicket(ticket))
return false;
return (PositionGetInteger(POSITION_MAGIC) == magic_number);
}
//+------------------------------------------------------------------+
//| Select position by ticket and verify symbol, magic number |
//+------------------------------------------------------------------+
bool PositionSelectByTicketSymbolAndMagic(ulong ticket, string symbol, ulong magic_number)
{
if(!PositionSelectByTicket(ticket))
return false;
return (PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == magic_number);
}
//+------------------------------------------------------------------+
//| Check if position exists with correct magic number |
//+------------------------------------------------------------------+
bool PositionExistsByMagic(string symbol, ulong magic_number)
{
return PositionSelectByMagic(symbol, magic_number);
}
//+------------------------------------------------------------------+
//| Get position ticket by symbol and magic number |
//+------------------------------------------------------------------+
ulong GetPositionTicketByMagic(string symbol, ulong magic_number)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0)
{
if(PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == magic_number)
{
return ticket;
}
}
}
return 0;
}
//+------------------------------------------------------------------+
//| Close position by symbol and magic number |
//+------------------------------------------------------------------+
bool ClosePositionByMagic(CTrade &trade_obj, string symbol, ulong magic_number)
{
ulong ticket = GetPositionTicketByMagic(symbol, magic_number);
if(ticket == 0)
return false;
return trade_obj.PositionClose(ticket);
}
//+------------------------------------------------------------------+
//| Modify position by symbol and magic number |
//+------------------------------------------------------------------+
bool ModifyPositionByMagic(CTrade &trade_obj, string symbol, ulong magic_number,
double sl, double tp)
{
ulong ticket = GetPositionTicketByMagic(symbol, magic_number);
if(ticket == 0)
return false;
return trade_obj.PositionModify(ticket, sl, tp);
}
//+------------------------------------------------------------------+
//| Get position profit by symbol and magic number |
//+------------------------------------------------------------------+
double GetPositionProfitByMagic(string symbol, ulong magic_number)
{
if(!PositionSelectByMagic(symbol, magic_number))
return 0.0;
return PositionGetDouble(POSITION_PROFIT);
}
//+------------------------------------------------------------------+
//| Get position type by symbol and magic number |
//+------------------------------------------------------------------+
ENUM_POSITION_TYPE GetPositionTypeByMagic(string symbol, ulong magic_number)
{
if(!PositionSelectByMagic(symbol, magic_number))
return WRONG_VALUE;
return (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
//+------------------------------------------------------------------+
//| Count positions by symbol and magic number |
//+------------------------------------------------------------------+
int CountPositionsByMagic(string symbol, ulong magic_number)
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0)
{
if(PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == magic_number)
{
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
@@ -0,0 +1,75 @@
# Pepperstone US - Symbol Setup Guide
## Finding Correct Symbol Names in MetaTrader 5
### Step-by-Step Instructions:
1. **Open Market Watch Window**
- Press `Ctrl+M` or go to `View > Market Watch`
2. **Show All Symbols**
- Right-click in the Market Watch window
- Select `Show All` or `Symbols`
- This shows all available symbols from your broker
3. **Search for Your Symbols**
- Use the search box in the Market Watch window
- Search for: "AAPL", "MSFT", "NVDA", "TSLA", "BTCUSD", "XAUUSD"
4. **Note the Exact Symbol Name**
- The symbol name shown in Market Watch is what you need to use
- Common formats for Pepperstone US:
- Stocks: `AAPL.US`, `MSFT.US`, `NVDA.US`, `TSLA.US`
- Or: `NASDAQ:AAPL`, `NASDAQ:MSFT`, etc.
- Or: Just `AAPL`, `MSFT`, etc. (if available)
5. **Add to Market Watch**
- Double-click the symbol to add it to your Market Watch
- Or right-click and select `Show`
6. **Update EA Inputs**
- Open the EA inputs in MetaTrader 5
- Update each symbol parameter with the exact name from Market Watch
## Common Pepperstone US Symbol Formats
### US Stocks:
- **Apple**: `AAPL.US` or `NASDAQ:AAPL` or `AAPL`
- **Microsoft**: `MSFT.US` or `NASDAQ:MSFT` or `MSFT`
- **NVIDIA**: `NVDA.US` or `NASDAQ:NVDA` or `NVDA`
- **Tesla**: `TSLA.US` or `NASDAQ:TSLA` or `TSLA`
### Cryptocurrencies:
- **Bitcoin**: `BTCUSD` or `BTC/USD` or `BTCUSD.c`
### Precious Metals:
- **Gold**: `XAUUSD` or `GOLD` or `XAU/USD`
## Important Notes:
1. **Symbol Names are Case-Sensitive**: Use exact capitalization
2. **Add Symbols to Market Watch**: Symbols must be in Market Watch for the EA to access them
3. **Check Trading Hours**: US stocks trade during US market hours (9:30 AM - 4:00 PM ET)
4. **CFD vs Stock**: Pepperstone offers CFDs on stocks, not actual stocks
5. **Spread**: Check the spread for each symbol - some may have wider spreads
## Troubleshooting:
### If Symbol Not Found:
1. Check if you're connected to Pepperstone US server
2. Verify your account type supports the symbol
3. Contact Pepperstone support for symbol availability
4. Check if symbol requires special account permissions
### If EA Shows "Symbol Not Available":
1. Make sure symbol is added to Market Watch
2. Verify symbol name matches exactly (including dots, colons, etc.)
3. Check broker connection status
4. Try different symbol format variations
## Testing Symbols:
You can test if a symbol works by:
1. Opening a chart with that symbol
2. If chart opens successfully, the symbol name is correct
3. Use that exact symbol name in the EA inputs
@@ -0,0 +1,607 @@
//+------------------------------------------------------------------+
//| PerformanceEvaluator.mqh |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
//+------------------------------------------------------------------+
//| Performance Metrics Structure |
//+------------------------------------------------------------------+
struct StrategyPerformance {
string strategyName;
string symbol; // Store symbol to determine if it's a stock
int magicNumber;
double initialLotSize;
double currentLotSize;
double quarterProfit;
double quarterTrades;
double quarterWins;
double quarterLosses;
double maxDrawdown;
double winRate;
datetime quarterStart;
datetime quarterEnd;
bool isActive;
bool inPenaltyMode; // True if strategy is in penalty (worst performer)
double lotSizeBeforePenalty; // Store lot size before penalty
datetime penaltyStartTime; // When penalty started
};
//+------------------------------------------------------------------+
//| Global Performance Tracking |
//+------------------------------------------------------------------+
StrategyPerformance strategyPerformances[];
int totalStrategies = 0;
datetime lastMonthCheck = 0;
datetime currentMonthStart = 0;
datetime currentMonthEnd = 0;
//+------------------------------------------------------------------+
//| Performance Adjustment Parameters |
//+------------------------------------------------------------------+
input group "=== Performance Evaluation Settings ==="
input bool PE_EnableAutoAdjustment = true; // Enable automatic lot size adjustment
input double PE_LotSizeIncreasePercent = 10.0; // % increase for top-ranked strategies
input double PE_LotSizeDecreasePercent = 10.0; // % decrease for bottom-ranked strategies
input double PE_MinLotSize = 0.01; // Minimum lot size for forex/crypto
input double PE_MinLotSizeStocks = 5.0; // Minimum lot size for stocks (5-10 range)
input double PE_MaxLotSize = 100.0; // Maximum lot size after adjustment
input int PE_TopPerformersCount = 3; // Number of top strategies to increase lot size
input int PE_BottomPerformersCount = 3; // Number of bottom strategies to decrease lot size
input bool PE_UseWinRateWeight = true; // Consider win rate in ranking (50% profit, 50% win rate)
input bool PE_EnableBlitzPlay = true; // Enable blitz play: worst performer gets minimum lot size penalty
input bool PE_EnableLogging = true; // Enable performance logging
//+------------------------------------------------------------------+
//| Initialize Performance Tracking |
//+------------------------------------------------------------------+
void InitPerformanceTracking()
{
// Calculate current month dates
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
// Determine month start (first day of current month)
dt.day = 1;
dt.hour = 0;
dt.min = 0;
dt.sec = 0;
currentMonthStart = StructToTime(dt);
// Calculate month end (first day of next month - 1 second)
dt.mon += 1;
if(dt.mon > 12)
{
dt.mon = 1;
dt.year++;
}
currentMonthEnd = StructToTime(dt) - 1; // End of last day of month
lastMonthCheck = TimeCurrent();
if(PE_EnableLogging)
{
Print("Performance Evaluator: Initialized");
Print("Current Month Start: ", TimeToString(currentMonthStart));
Print("Current Month End: ", TimeToString(currentMonthEnd));
}
}
//+------------------------------------------------------------------+
//| Check if Symbol is a Stock |
//+------------------------------------------------------------------+
bool IsStockSymbol(string symbol)
{
// Check if symbol contains common stock indicators
if(StringFind(symbol, ".US") >= 0) return true;
if(StringFind(symbol, "NASDAQ:") >= 0) return true;
if(StringFind(symbol, "NYSE:") >= 0) return true;
// Note: Symbol category check removed to avoid enum conversion issues
// String-based checks (.US, NASDAQ:, NYSE:, common tickers) are sufficient
// Common stock tickers (without .US suffix)
string commonStocks[] = {"AAPL", "NVDA", "TSLA", "GOOGL", "AMZN", "META", "AMD", "NFLX"};
for(int i = 0; i < ArraySize(commonStocks); i++)
{
if(StringFind(symbol, commonStocks[i]) == 0) return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Get Minimum Lot Size for Symbol |
//+------------------------------------------------------------------+
double GetMinLotSizeForSymbol(string symbol)
{
if(IsStockSymbol(symbol))
return PE_MinLotSizeStocks;
else
return PE_MinLotSize;
}
//+------------------------------------------------------------------+
//| Register Strategy for Performance Tracking |
//+------------------------------------------------------------------+
void RegisterStrategy(string strategyName, int magicNumber, double initialLotSize, string symbol = "")
{
// Check if strategy already registered
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].strategyName == strategyName &&
strategyPerformances[i].magicNumber == magicNumber)
{
if(PE_EnableLogging)
Print("Performance Evaluator: Strategy '", strategyName, "' already registered");
return;
}
}
// Add new strategy
int newSize = ArraySize(strategyPerformances) + 1;
ArrayResize(strategyPerformances, newSize);
strategyPerformances[newSize - 1].strategyName = strategyName;
strategyPerformances[newSize - 1].symbol = symbol;
strategyPerformances[newSize - 1].magicNumber = magicNumber;
strategyPerformances[newSize - 1].initialLotSize = initialLotSize;
// Start with minimum lot size for safety (symbol-specific minimum)
double minLot = GetMinLotSizeForSymbol(symbol);
strategyPerformances[newSize - 1].currentLotSize = minLot;
strategyPerformances[newSize - 1].quarterProfit = 0.0;
strategyPerformances[newSize - 1].quarterTrades = 0;
strategyPerformances[newSize - 1].quarterWins = 0;
strategyPerformances[newSize - 1].quarterLosses = 0;
strategyPerformances[newSize - 1].maxDrawdown = 0.0;
strategyPerformances[newSize - 1].winRate = 0.0;
strategyPerformances[newSize - 1].quarterStart = currentMonthStart;
strategyPerformances[newSize - 1].quarterEnd = currentMonthEnd;
strategyPerformances[newSize - 1].isActive = true;
strategyPerformances[newSize - 1].inPenaltyMode = false;
strategyPerformances[newSize - 1].lotSizeBeforePenalty = initialLotSize;
strategyPerformances[newSize - 1].penaltyStartTime = 0;
totalStrategies = newSize;
if(PE_EnableLogging)
Print("Performance Evaluator: Registered strategy '", strategyName,
"' (Magic: ", magicNumber, ", Initial Lot: ", initialLotSize, ")");
}
//+------------------------------------------------------------------+
//| Update Strategy Performance Metrics |
//+------------------------------------------------------------------+
void UpdateStrategyPerformance(string strategyName, int magicNumber)
{
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].strategyName == strategyName &&
strategyPerformances[i].magicNumber == magicNumber &&
strategyPerformances[i].isActive)
{
// Calculate performance for current quarter
double totalProfit = 0.0;
int totalTrades = 0;
int wins = 0;
int losses = 0;
double maxDD = 0.0;
double peakBalance = 0.0;
// Scan all closed deals in current quarter
datetime quarterStart = strategyPerformances[i].quarterStart;
datetime quarterEnd = strategyPerformances[i].quarterEnd;
// Select history for the quarter
if(HistorySelect(quarterStart, quarterEnd))
{
int totalDeals = HistoryDealsTotal();
for(int j = 0; j < totalDeals; j++)
{
ulong ticket = HistoryDealGetTicket(j);
if(ticket > 0)
{
long dealMagic = HistoryDealGetInteger(ticket, DEAL_MAGIC);
if(dealMagic == magicNumber)
{
double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
double swap = HistoryDealGetDouble(ticket, DEAL_SWAP);
double commission = HistoryDealGetDouble(ticket, DEAL_COMMISSION);
double totalDealProfit = profit + swap + commission;
totalProfit += totalDealProfit;
totalTrades++;
if(totalDealProfit > 0)
wins++;
else if(totalDealProfit < 0)
losses++;
}
}
}
}
// Calculate win rate
double winRate = 0.0;
if(totalTrades > 0)
winRate = (double)wins / (double)totalTrades * 100.0;
// Update metrics
strategyPerformances[i].quarterProfit = totalProfit;
strategyPerformances[i].quarterTrades = totalTrades;
strategyPerformances[i].quarterWins = wins;
strategyPerformances[i].quarterLosses = losses;
strategyPerformances[i].winRate = winRate;
break;
}
}
}
//+------------------------------------------------------------------+
//| Strategy Ranking Structure |
//+------------------------------------------------------------------+
struct StrategyRank {
int index;
double score;
};
//+------------------------------------------------------------------+
//| Calculate Strategy Score for Ranking |
//+------------------------------------------------------------------+
double CalculateStrategyScore(int strategyIndex)
{
double profit = strategyPerformances[strategyIndex].quarterProfit;
double winRate = strategyPerformances[strategyIndex].winRate;
double trades = strategyPerformances[strategyIndex].quarterTrades;
// Normalize profit (scale to 0-100 range, assuming max profit of $1000)
double normalizedProfit = MathMin(profit / 10.0, 100.0);
if(profit < 0) normalizedProfit = profit / 5.0; // Penalize losses more
// Calculate score
double score = 0.0;
if(PE_UseWinRateWeight)
{
// 50% profit, 50% win rate (if enough trades)
if(trades >= 5)
score = (normalizedProfit * 0.5) + (winRate * 0.5);
else
score = normalizedProfit; // Not enough trades, use profit only
}
else
{
// Profit only
score = normalizedProfit;
}
return score;
}
//+------------------------------------------------------------------+
//| Check if Month Ended and Evaluate Performance |
//+------------------------------------------------------------------+
void CheckMonthEnd()
{
datetime now = TimeCurrent();
// Check if we've entered a new month
if(now >= currentMonthEnd)
{
if(PE_EnableLogging)
Print("Performance Evaluator: Month ended. Evaluating and ranking strategies...");
// Update performance metrics for all strategies
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive)
{
UpdateStrategyPerformance(strategyPerformances[i].strategyName,
strategyPerformances[i].magicNumber);
}
}
// Rank strategies
int activeCount = 0;
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive)
activeCount++;
}
if(activeCount > 0)
{
// Create ranking array
StrategyRank ranks[];
ArrayResize(ranks, activeCount);
int rankIndex = 0;
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive)
{
ranks[rankIndex].index = i;
ranks[rankIndex].score = CalculateStrategyScore(i);
rankIndex++;
}
}
// Sort by score (descending - highest score first)
for(int i = 0; i < activeCount - 1; i++)
{
for(int j = i + 1; j < activeCount; j++)
{
if(ranks[j].score > ranks[i].score)
{
StrategyRank temp = ranks[i];
ranks[i] = ranks[j];
ranks[j] = temp;
}
}
}
// Adjust lot sizes based on ranking
if(PE_EnableAutoAdjustment)
{
// Increase top performers (skip if in penalty mode)
int topCount = MathMin(PE_TopPerformersCount, activeCount);
for(int i = 0; i < topCount; i++)
{
int strategyIdx = ranks[i].index;
// Skip if strategy is in penalty mode
if(strategyPerformances[strategyIdx].inPenaltyMode)
continue;
double oldLotSize = strategyPerformances[strategyIdx].currentLotSize;
double newLotSize = oldLotSize * (1.0 + PE_LotSizeIncreasePercent / 100.0);
if(newLotSize > PE_MaxLotSize)
newLotSize = PE_MaxLotSize;
strategyPerformances[strategyIdx].currentLotSize = newLotSize;
if(PE_EnableLogging)
Print("Performance Evaluator: Rank #", (i+1), " - Increasing '",
strategyPerformances[strategyIdx].strategyName,
"' lot size from ", oldLotSize, " to ", newLotSize,
" (Score: ", DoubleToString(ranks[i].score, 2),
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%)");
}
// Decrease bottom performers (skip worst one if blitz play is enabled)
int bottomCount = MathMin(PE_BottomPerformersCount, activeCount);
int startIdx = activeCount - bottomCount;
// If blitz play is enabled, skip the worst performer (it will get minimum penalty)
if(PE_EnableBlitzPlay && activeCount > 0)
startIdx = activeCount - bottomCount + 1;
for(int i = startIdx; i < activeCount; i++)
{
int strategyIdx = ranks[i].index;
// Skip if strategy is in penalty mode
if(strategyPerformances[strategyIdx].inPenaltyMode)
continue;
double oldLotSize = strategyPerformances[strategyIdx].currentLotSize;
double newLotSize = oldLotSize * (1.0 - PE_LotSizeDecreasePercent / 100.0);
// Use symbol-specific minimum lot size
double minLot = GetMinLotSizeForSymbol(strategyPerformances[strategyIdx].symbol);
if(newLotSize < minLot)
newLotSize = minLot;
strategyPerformances[strategyIdx].currentLotSize = newLotSize;
if(PE_EnableLogging)
Print("Performance Evaluator: Rank #", (i+1), " - Decreasing '",
strategyPerformances[strategyIdx].strategyName,
"' lot size from ", oldLotSize, " to ", newLotSize,
" (Score: ", DoubleToString(ranks[i].score, 2),
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%)");
}
}
// Blitz Play: Apply penalty to worst performer
if(PE_EnableBlitzPlay && activeCount > 0)
{
// Find worst performer (last in ranking)
int worstIdx = ranks[activeCount - 1].index;
// Remove penalty from previous worst performer (if any)
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive && strategyPerformances[i].inPenaltyMode)
{
// Check if penalty period has passed (one month)
if(now - strategyPerformances[i].penaltyStartTime >= 2592000) // ~30 days
{
// Restore lot size to before penalty
strategyPerformances[i].currentLotSize = strategyPerformances[i].lotSizeBeforePenalty;
strategyPerformances[i].inPenaltyMode = false;
strategyPerformances[i].penaltyStartTime = 0;
if(PE_EnableLogging)
Print("Blitz Play: Penalty removed from '", strategyPerformances[i].strategyName,
"'. Lot size restored to ", strategyPerformances[i].currentLotSize);
}
}
}
// Apply penalty to new worst performer
if(!strategyPerformances[worstIdx].inPenaltyMode)
{
strategyPerformances[worstIdx].lotSizeBeforePenalty = strategyPerformances[worstIdx].currentLotSize;
// Use symbol-specific minimum lot size
double minLot = GetMinLotSizeForSymbol(strategyPerformances[worstIdx].symbol);
strategyPerformances[worstIdx].currentLotSize = minLot;
strategyPerformances[worstIdx].inPenaltyMode = true;
strategyPerformances[worstIdx].penaltyStartTime = now;
if(PE_EnableLogging)
Print("Blitz Play: WORST PERFORMER - '", strategyPerformances[worstIdx].strategyName,
"' penalized! Lot size reduced from ", strategyPerformances[worstIdx].lotSizeBeforePenalty,
" to minimum ", minLot, " (Score: ", DoubleToString(ranks[activeCount - 1].score, 2),
", Profit: $", DoubleToString(strategyPerformances[worstIdx].quarterProfit, 2), ")");
}
}
// Log performance report
if(PE_EnableLogging)
{
Print("=== Monthly Performance Ranking ===");
for(int i = 0; i < activeCount; i++)
{
int strategyIdx = ranks[i].index;
Print("Rank #", (i+1), ": ", strategyPerformances[strategyIdx].strategyName,
" - Score: ", DoubleToString(ranks[i].score, 2),
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%",
", Trades: ", (int)strategyPerformances[strategyIdx].quarterTrades,
", Lot Size: ", DoubleToString(strategyPerformances[strategyIdx].currentLotSize, 2));
}
Print("===================================");
}
}
// Reset month metrics for all strategies
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive)
{
strategyPerformances[i].quarterProfit = 0.0;
strategyPerformances[i].quarterTrades = 0;
strategyPerformances[i].quarterWins = 0;
strategyPerformances[i].quarterLosses = 0;
strategyPerformances[i].maxDrawdown = 0.0;
strategyPerformances[i].winRate = 0.0;
}
}
// Update month dates
MqlDateTime dt;
TimeToStruct(now, dt);
// First day of current month
dt.day = 1;
dt.hour = 0;
dt.min = 0;
dt.sec = 0;
currentMonthStart = StructToTime(dt);
// First day of next month - 1 second
dt.mon += 1;
if(dt.mon > 12)
{
dt.mon = 1;
dt.year++;
}
currentMonthEnd = StructToTime(dt) - 1;
// Update month dates for all strategies
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
strategyPerformances[i].quarterStart = currentMonthStart;
strategyPerformances[i].quarterEnd = currentMonthEnd;
}
lastMonthCheck = now;
}
}
//+------------------------------------------------------------------+
//| Get Current Lot Size for Strategy |
//+------------------------------------------------------------------+
double GetStrategyLotSize(string strategyName, int magicNumber)
{
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].strategyName == strategyName &&
strategyPerformances[i].magicNumber == magicNumber &&
strategyPerformances[i].isActive)
{
return strategyPerformances[i].currentLotSize;
}
}
return 0.0;
}
//+------------------------------------------------------------------+
//| Process Performance Evaluation (call from OnTick) |
//+------------------------------------------------------------------+
void ProcessPerformanceEvaluation()
{
// Check if month ended
CheckMonthEnd();
// Check for penalty expiration (blitz play)
if(PE_EnableBlitzPlay)
{
datetime now = TimeCurrent();
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive && strategyPerformances[i].inPenaltyMode)
{
// Check if penalty period has passed (one month = ~30 days)
if(now - strategyPerformances[i].penaltyStartTime >= 2592000)
{
// Restore lot size to before penalty
strategyPerformances[i].currentLotSize = strategyPerformances[i].lotSizeBeforePenalty;
strategyPerformances[i].inPenaltyMode = false;
strategyPerformances[i].penaltyStartTime = 0;
if(PE_EnableLogging)
Print("Blitz Play: Penalty expired for '", strategyPerformances[i].strategyName,
"'. Lot size restored to ", strategyPerformances[i].currentLotSize);
}
}
}
}
// Update performance metrics periodically (every hour)
static datetime lastUpdate = 0;
if(TimeCurrent() - lastUpdate >= 3600)
{
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive)
{
UpdateStrategyPerformance(strategyPerformances[i].strategyName,
strategyPerformances[i].magicNumber);
}
}
lastUpdate = TimeCurrent();
}
}
//+------------------------------------------------------------------+
//| Get Performance Summary |
//+------------------------------------------------------------------+
string GetPerformanceSummary()
{
string summary = "\n=== Performance Summary ===\n";
summary += "Current Month: " + TimeToString(currentMonthStart) + " to " + TimeToString(currentMonthEnd) + "\n\n";
for(int i = 0; i < ArraySize(strategyPerformances); i++)
{
if(strategyPerformances[i].isActive)
{
summary += strategyPerformances[i].strategyName + ":\n";
summary += " Profit: $" + DoubleToString(strategyPerformances[i].quarterProfit, 2) + "\n";
summary += " Trades: " + IntegerToString((int)strategyPerformances[i].quarterTrades) + "\n";
summary += " Win Rate: " + DoubleToString(strategyPerformances[i].winRate, 2) + "%\n";
summary += " Lot Size: " + DoubleToString(strategyPerformances[i].currentLotSize, 2) + "\n\n";
}
}
return summary;
}
//+------------------------------------------------------------------+
@@ -0,0 +1,76 @@
# United EA Strategy Configuration Summary
## Strategy Symbols and Magic Numbers
### Strategy 1: DarvasBox
- **Symbol**: XAUUSD (Gold/USD)
- **Magic Number**: 135790
### Strategy 2: EMASlopeDistance
- **Symbol**: XAUUSD (Gold/USD)
- **Magic Number**: 12350
### Strategy 3: RSICrossOverReversal
- **Symbol**: XAUUSD (Gold/USD)
- **Magic Number**: 7
### Strategy 4: RSIMidPointHijack
- **Symbol**: XAUUSD (Gold/USD)
- **Magic Numbers**:
- RSIFollow: 1001
- RSIReverse: 1002
- EMACross: 1003
### Strategy 5: RSI Scalping APPL (Apple)
- **Symbol**: AAPL (Apple stock)
- **Magic Number**: 20001
- **Note**: Changed from "APPL" to "AAPL" (correct ticker symbol)
### Strategy 6: RSI Scalping BTCUSD
- **Symbol**: BTCUSD (Bitcoin/USD)
- **Magic Number**: 123459123
### Strategy 7: RSI Scalping MSFT
- **Symbol**: MSFT (Microsoft stock)
- **Magic Number**: 20002
### Strategy 8: RSI Scalping NVDA
- **Symbol**: NVDA (NVIDIA stock)
- **Magic Number**: 20003
### Strategy 9: RSI Scalping TSLA
- **Symbol**: TSLA (Tesla stock)
- **Magic Number**: 125421321
### Strategy 10: RSI Scalping XAUUSD
- **Symbol**: XAUUSD (Gold/USD)
- **Magic Number**: 129102315
## Important Notes
1. **Stock Symbols**: Stock symbols (AAPL, MSFT, NVDA, TSLA) must be:
- Added to Market Watch in MetaTrader 5
- Available from your broker
- Use the correct ticker symbol (e.g., "AAPL" not "APPL")
2. **Magic Numbers**: All strategies have unique magic numbers to prevent interference:
- Each strategy can be identified by its magic number
- RSIMidPointHijack uses 3 magic numbers (one for each sub-strategy)
3. **Symbol Configuration**: Each strategy trades on its own symbol:
- You can change symbols in the input parameters
- The EA will log warnings if a symbol is not available
- Strategies with unavailable symbols will be skipped (EA continues running)
4. **RSI Scalping Strategies**:
- Each RSI Scalping variant trades on a different symbol
- They all use the same strategy logic but with different parameters
- Buy and sell signals are generated based on RSI levels for each symbol
## Troubleshooting
If stock symbols are not working:
1. Check if the symbol exists in your broker's symbol list
2. Add the symbol to Market Watch in MetaTrader 5
3. Verify the symbol name matches your broker's naming convention
4. Some brokers use prefixes/suffixes (e.g., "NASDAQ:AAPL" or "AAPL.US")
@@ -0,0 +1,300 @@
//+------------------------------------------------------------------+
//| DarvasBoxStrategy.mqh |
//+------------------------------------------------------------------+
bool InitDarvasBox(string symbol)
{
dbData.symbol = symbol;
dbData.boxHigh = 0;
dbData.boxLow = 0;
dbData.boxFormed = false;
dbData.lastBoxTime = 0;
dbData.boxName = "DarvasBox_" + IntegerToString(DB_MagicNumber) + "_";
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("DarvasBox: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
Sleep(100); // Wait for symbol to be ready
dbData.point = SymbolInfoDouble(symbol, SYMBOL_POINT);
dbData.minStopLevel = SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL) * dbData.point;
dbData.maHandle = iMA(symbol, DB_TrendTimeframe, DB_MA_Period, 0, DB_MA_Method, DB_MA_Price);
dbData.volumeHandle = iVolumes(symbol, PERIOD_CURRENT, VOLUME_TICK);
if(dbData.maHandle == INVALID_HANDLE || dbData.volumeHandle == INVALID_HANDLE)
{
Print("DarvasBox: Error creating indicators for '", symbol, "'");
return false;
}
dbData.trade.SetDeviationInPoints(10);
dbData.trade.SetTypeFilling(ORDER_FILLING_IOC);
dbData.trade.SetAsyncMode(false);
dbData.trade.SetExpertMagicNumber(DB_MagicNumber);
ObjectsDeleteAll(0, dbData.boxName);
dbData.isInitialized = true;
Print("DarvasBox: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitDarvasBox()
{
if(dbData.maHandle != INVALID_HANDLE) IndicatorRelease(dbData.maHandle);
if(dbData.volumeHandle != INVALID_HANDLE) IndicatorRelease(dbData.volumeHandle);
ObjectsDeleteAll(0, dbData.boxName);
}
void DrawDarvasBox()
{
if(!dbData.boxFormed) return;
datetime time1 = iTime(dbData.symbol, PERIOD_H1, DB_BoxPeriod);
datetime time2 = iTime(dbData.symbol, PERIOD_H1, 0);
ObjectsDeleteAll(0, dbData.boxName);
ObjectCreate(0, dbData.boxName + "Top", OBJ_TREND, 0, time1, dbData.boxHigh, time2, dbData.boxHigh);
ObjectCreate(0, dbData.boxName + "Bottom", OBJ_TREND, 0, time1, dbData.boxLow, time2, dbData.boxLow);
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_COLOR, DB_BoxColor);
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_COLOR, DB_BoxColor);
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_WIDTH, DB_BoxWidth);
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_WIDTH, DB_BoxWidth);
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_RAY_RIGHT, true);
}
void CalculateDarvasBox()
{
double high = 0;
double low = DBL_MAX;
// Find highest high and lowest low in the period - EXACTLY like original
for(int i = 0; i < DB_BoxPeriod; i++)
{
high = MathMax(high, iHigh(dbData.symbol, PERIOD_H1, i));
low = MathMin(low, iLow(dbData.symbol, PERIOD_H1, i));
}
double range = high - low;
double allowedRange = DB_BoxDeviation * dbData.point; // Use dbData.point instead of _Point
if(DB_EnableLogging)
{
Print("DarvasBox: Box Calculation - High: ", high, " Low: ", low, " Range: ", range, " Allowed Range: ", allowedRange);
}
// Check if box is formed - EXACTLY like original
if(range <= allowedRange)
{
dbData.boxHigh = high;
dbData.boxLow = low;
dbData.boxFormed = true;
dbData.lastBoxTime = iTime(dbData.symbol, PERIOD_CURRENT, 0);
// Draw the box
DrawDarvasBox();
if(DB_EnableLogging)
Print("DarvasBox: Box Formed - High: ", dbData.boxHigh, " Low: ", dbData.boxLow, " Time: ", dbData.lastBoxTime);
}
else
{
dbData.boxFormed = false;
// Delete box if it exists
ObjectsDeleteAll(0, dbData.boxName);
}
}
bool ValidateStopLevels(double price, double &sl, double &tp, ENUM_ORDER_TYPE orderType)
{
double minSlDistance = MathMax(dbData.minStopLevel, DB_StopLoss * dbData.point);
double minTpDistance = MathMax(dbData.minStopLevel, DB_TakeProfit * dbData.point);
if(orderType == ORDER_TYPE_BUY)
{
sl = price - minSlDistance;
tp = price + minTpDistance;
}
else
{
sl = price + minSlDistance;
tp = price - minTpDistance;
}
return true;
}
bool IsTrendFavorable(ENUM_ORDER_TYPE orderType)
{
double ma[];
ArraySetAsSeries(ma, true);
if(CopyBuffer(dbData.maHandle, 0, 0, 2, ma) <= 0)
return false;
double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK);
double trendStrength = MathAbs(currentPrice - ma[0]) / dbData.point;
if(orderType == ORDER_TYPE_BUY)
return (currentPrice > ma[0] && trendStrength > DB_TrendThreshold);
else
return (currentPrice < ma[0] && trendStrength > DB_TrendThreshold);
}
bool CheckVolumeConditions()
{
double volumes[];
ArraySetAsSeries(volumes, true);
if(CopyBuffer(dbData.volumeHandle, 0, 0, DB_VolumeMA_Period + 1, volumes) <= 0)
return false;
double volumeMA = 0;
for(int i = 1; i <= DB_VolumeMA_Period; i++)
volumeMA += volumes[i];
volumeMA /= DB_VolumeMA_Period;
double currentVolume = volumes[0];
double volumeRatio = currentVolume / volumeMA;
return (volumeRatio > DB_VolumeThresholdMultiplier);
}
bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price, double sl, double tp)
{
if(!ValidateStopLevels(price, sl, tp, orderType))
{
if(DB_EnableLogging)
Print("DarvasBox: Order rejected - Stop levels validation failed");
return false;
}
if(!IsTrendFavorable(orderType))
{
if(DB_EnableLogging)
Print("DarvasBox: Order rejected - Trend not favorable for ", EnumToString(orderType));
return false;
}
if(!CheckVolumeConditions())
{
if(DB_EnableLogging)
Print("DarvasBox: Order rejected - Volume conditions not met");
return false;
}
bool result = false;
// Use market price (0) instead of explicit price - this ensures market order execution
// In backtesting, explicit price might fail if price has moved
if(orderType == ORDER_TYPE_BUY)
result = dbData.trade.Buy(g_DB_LotSize, dbData.symbol, 0, sl, tp, "Darvas Box Breakout");
else
result = dbData.trade.Sell(g_DB_LotSize, dbData.symbol, 0, sl, tp, "Darvas Box Breakdown");
// Always log errors, success only if logging enabled
if(result)
{
if(DB_EnableLogging)
Print("DarvasBox: ", (orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"), " Order Placed Successfully");
}
else
{
// Always log failures with detailed info
uint retcode_uint = dbData.trade.ResultRetcode();
int retcode = (int)retcode_uint;
string desc = dbData.trade.ResultRetcodeDescription();
ulong deal = dbData.trade.ResultDeal();
ulong order = dbData.trade.ResultOrder();
Print("DarvasBox: ", (orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"),
" Order Failed - Retcode: ", retcode,
", Description: ", desc,
", Deal: ", deal,
", Order: ", order,
", Symbol: ", dbData.symbol,
", Requested Price: ", price,
", SL: ", sl,
", TP: ", tp);
}
return result;
}
void ProcessDarvasBox(string symbol)
{
// Skip if not initialized (symbol not available)
if(!dbData.isInitialized)
return;
dbData.symbol = symbol; // Update symbol in case it changed
// Calculate new box levels - EXACTLY like original (called every tick)
CalculateDarvasBox();
// Check for trading signals - EXACTLY like original (checked every tick)
if(dbData.boxFormed)
{
double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK);
long currentVolume_long = iVolume(dbData.symbol, PERIOD_CURRENT, 0);
double currentVolume = (double)currentVolume_long;
if(DB_EnableLogging)
{
Print("DarvasBox: Current Price: ", currentPrice, " Box High: ", dbData.boxHigh, " Box Low: ", dbData.boxLow);
Print("DarvasBox: Current Volume: ", currentVolume, " Volume Threshold: ", DB_VolumeThreshold);
}
// Check for breakout above box - EXACTLY like original
if(currentPrice > dbData.boxHigh && currentVolume > DB_VolumeThreshold)
{
if(DB_EnableLogging)
Print("DarvasBox: Breakout Signal Detected - Price above box high");
// Buy signal
if(!PositionExistsByMagic(dbData.symbol, (ulong)DB_MagicNumber)) // No existing positions with our magic number
{
double sl = currentPrice - DB_StopLoss * dbData.point;
double tp = currentPrice + DB_TakeProfit * dbData.point;
if(DB_EnableLogging)
Print("DarvasBox: Preparing Buy Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
PlaceOrder(ORDER_TYPE_BUY, currentPrice, sl, tp);
}
else if(DB_EnableLogging)
Print("DarvasBox: Skipping Buy Signal - Position already exists");
}
// Check for breakdown below box - EXACTLY like original
if(currentPrice < dbData.boxLow && currentVolume > DB_VolumeThreshold)
{
if(DB_EnableLogging)
Print("DarvasBox: Breakdown Signal Detected - Price below box low");
// Sell signal
if(!PositionExistsByMagic(dbData.symbol, (ulong)DB_MagicNumber)) // No existing positions with our magic number
{
double sl = currentPrice + DB_StopLoss * dbData.point;
double tp = currentPrice - DB_TakeProfit * dbData.point;
if(DB_EnableLogging)
Print("DarvasBox: Preparing Sell Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
PlaceOrder(ORDER_TYPE_SELL, currentPrice, sl, tp);
}
else if(DB_EnableLogging)
Print("DarvasBox: Skipping Sell Signal - Position already exists");
}
}
else if(DB_EnableLogging)
Print("DarvasBox: No Box Formed - Waiting for consolidation");
}
//+------------------------------------------------------------------+
@@ -0,0 +1,496 @@
//+------------------------------------------------------------------+
//| EMASlopeDistanceStrategy.mqh |
//+------------------------------------------------------------------+
bool InitEMASlopeDistance(string symbol)
{
esData.symbol = symbol;
esData.letzte_überwachung_zeit = 0;
esData.überwachung_aktiv = false;
esData.preis_trigger_aktiv = false;
esData.steigung_trigger_aktiv = false;
esData.ticket = 0;
esData.trades_in_current_crossover = 0;
esData.crossover_detected = false;
esData.trade_open_time = 0;
esData.last_bar_time = 0;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("EMASlopeDistance: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
Sleep(100); // Wait for symbol to be ready
esData.trade.SetExpertMagicNumber(ES_MagicNumber);
esData.trade.SetDeviationInPoints(10);
esData.trade.SetTypeFilling(ORDER_FILLING_IOC);
esData.ema_handle = iMA(symbol, ES_Timeframe, ES_EMA_Periode, 0, MODE_EMA, PRICE_CLOSE);
if(esData.ema_handle == INVALID_HANDLE)
{
Print("EMASlopeDistance: Error creating EMA indicator for '", symbol, "'");
return false;
}
ArraySetAsSeries(esData.ema_array, true);
esData.isInitialized = true;
Print("EMASlopeDistance: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitEMASlopeDistance()
{
if(esData.ema_handle != INVALID_HANDLE)
IndicatorRelease(esData.ema_handle);
}
//+------------------------------------------------------------------+
//| EMA Berechnung (EMA Calculation) |
//+------------------------------------------------------------------+
void BerechneEMA()
{
//--- EMA Werte vom Indicator kopieren (Copy EMA values from indicator)
int copied = CopyBuffer(esData.ema_handle, 0, 0, 3, esData.ema_array);
if(copied <= 0)
{
Print("TRACE: Fehler beim Kopieren der EMA Werte - Copied: ", copied);
return;
}
Print("TRACE: EMA Werte kopiert: ", copied, " Bars");
Print("TRACE: EMA [0]: ", esData.ema_array[0], " [1]: ", esData.ema_array[1], " [2]: ", esData.ema_array[2]);
}
//+------------------------------------------------------------------+
//| Trigger-Bedingungen prüfen (Check trigger conditions) |
//+------------------------------------------------------------------+
void PrüfeTrigger()
{
if(ArraySize(esData.ema_array) < 2)
{
Print("TRACE: Array zu klein - Größe: ", ArraySize(esData.ema_array));
return;
}
//--- Aktuelle Werte (Current values)
double aktueller_preis = SymbolInfoDouble(esData.symbol, SYMBOL_BID);
double aktueller_ask = SymbolInfoDouble(esData.symbol, SYMBOL_ASK);
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
double pips_multiplier = (digits == 3 || digits == 5) ? 10.0 : 1.0;
//--- EMA Werte in Variablen (EMA values in variables)
double ema_aktuell = esData.ema_array[0];
double ema_vorher = esData.ema_array[1];
//--- EMA Crossover Erkennung (EMA Crossover Detection)
// Prüfe ob Preis die EMA kreuzt (Check if price crosses EMA)
static double last_close = 0;
static double last_ema = 0;
if(last_close != 0 && last_ema != 0)
{
bool crossover_bullish = (last_close <= last_ema) && (aktueller_close > ema_aktuell);
bool crossover_bearish = (last_close >= last_ema) && (aktueller_close < ema_aktuell);
//--- Neues Crossover-Ereignis erkannt (New crossover event detected)
if(crossover_bullish || crossover_bearish)
{
esData.trades_in_current_crossover = 0; // Reset trade counter
Print("TRACE: EMA Crossover erkannt - ", (crossover_bullish ? "BULLISH" : "BEARISH"), " - Trade-Counter zurückgesetzt");
Print("TRACE: Vorher: Close=", last_close, " EMA=", last_ema, " Jetzt: Close=", aktueller_close, " EMA=", ema_aktuell);
}
}
//--- Aktuelle Werte für nächsten Vergleich speichern (Save current values for next comparison)
last_close = aktueller_close;
last_ema = ema_aktuell;
//--- Preisbewegung zur EMA prüfen (Check price action to EMA)
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / point / pips_multiplier;
Print("TRACE: Preis-Abstand: ", preis_abstand, " Pips (Schwelle: ", ES_PreisSchwelle, ")");
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
Print("TRACE: Trades im aktuellen Crossover: ", esData.trades_in_current_crossover, "/", ES_MaxTradesPerCrossover);
if(preis_abstand > ES_PreisSchwelle && !esData.preis_trigger_aktiv)
{
esData.preis_trigger_aktiv = true;
Print("TRACE: Preis-Trigger aktiviert: ", preis_abstand, " Pips");
}
//--- EMA Steigung prüfen (Check EMA slope)
double steigung = (ema_aktuell - ema_vorher) / point / pips_multiplier;
Print("TRACE: EMA Steigung: ", steigung, " Pips (Schwelle: ", ES_SteigungSchwelle, ")");
if(MathAbs(steigung) > ES_SteigungSchwelle && !esData.steigung_trigger_aktiv)
{
esData.steigung_trigger_aktiv = true;
Print("TRACE: Steigungs-Trigger aktiviert: ", steigung, " Pips");
}
//--- Überwachung starten wenn beide Trigger aktiv sind (Start monitoring when both triggers are active)
if(esData.preis_trigger_aktiv && esData.steigung_trigger_aktiv && !esData.überwachung_aktiv)
{
esData.überwachung_aktiv = true;
if(ES_UseBarData)
{
esData.letzte_überwachung_zeit = iTime(esData.symbol, ES_Timeframe, 0); // Aktuelle Bar-Zeit
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Bar: ", TimeToString(esData.letzte_überwachung_zeit), ")");
}
else
{
esData.letzte_überwachung_zeit = TimeCurrent(); // Aktuelle Tick-Zeit
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Tick)");
}
}
//--- Trade platzieren wenn Überwachung aktiv und Preis über/unter EMA (Place trade when monitoring active and price above/below EMA)
if(esData.überwachung_aktiv)
{
bool bullish_signal = aktueller_close > ema_aktuell;
bool bearish_signal = aktueller_close < ema_aktuell;
Print("TRACE: Signal Check - Bullish: ", bullish_signal, " Bearish: ", bearish_signal);
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
Print("TRACE: Differenz: ", aktueller_close - ema_aktuell);
//--- Trade-Limit prüfen (Check trade limit)
if(esData.trades_in_current_crossover >= ES_MaxTradesPerCrossover)
{
Print("TRACE: Trade-Limit erreicht (", ES_MaxTradesPerCrossover, ") - Kein neuer Trade");
return;
}
if(bullish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
Print("TRACE: Versuche KAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_BUY))
{
esData.trades_in_current_crossover++;
}
}
else if(bearish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
Print("TRACE: Versuche VERKAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_SELL))
{
esData.trades_in_current_crossover++;
}
}
else if(PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
Print("TRACE: Position bereits offen - kein neuer Trade");
}
}
}
//+------------------------------------------------------------------+
//| Trade platzieren (Place trade) |
//+------------------------------------------------------------------+
bool PlatziereTrade(ENUM_ORDER_TYPE order_type)
{
Print("TRACE: Versuche Trade zu platzieren - Typ: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF");
Print("TRACE: Lot: ", g_ES_LotSize);
bool success = false;
if(order_type == ORDER_TYPE_BUY)
{
success = esData.trade.Buy(g_ES_LotSize, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
}
else
{
success = esData.trade.Sell(g_ES_LotSize, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
}
if(success)
{
esData.ticket = (int)esData.trade.ResultOrder();
Print("TRACE: Trade erfolgreich platziert: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF", " Ticket: ", esData.ticket);
//--- Trade-Öffnungszeit speichern (Save trade opening time)
esData.trade_open_time = iTime(esData.symbol, ES_Timeframe, 0);
Print("TRACE: Trade-Öffnungszeit: ", TimeToString(esData.trade_open_time));
//--- Überwachung zurücksetzen (Reset monitoring)
esData.überwachung_aktiv = false;
esData.preis_trigger_aktiv = false;
esData.steigung_trigger_aktiv = false;
return true;
}
else
{
Print("TRACE: Fehler beim Platzieren des Trades - Retcode: ", esData.trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
return false;
}
}
//+------------------------------------------------------------------+
//| Trades verwalten (Manage trades) |
//+------------------------------------------------------------------+
void VerwalteTrades()
{
if(!PositionSelectByMagic(esData.symbol, (ulong)ES_MagicNumber))
return;
double position_profit = PositionGetDouble(POSITION_PROFIT);
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
double pips_multiplier = (digits == 3 || digits == 5) ? 10.0 : 1.0;
double trailing_stop_pips = ES_TrailingStop;
//--- Gleitender Stop (Trailing Stop) - nur wenn Position im Profit ist
if(position_profit > 0) // Only apply trailing stop when in profit
{
if(position_type == POSITION_TYPE_BUY)
{
double new_stop_loss = current_price - (trailing_stop_pips * point * pips_multiplier);
double current_stop_loss = PositionGetDouble(POSITION_SL);
// Only move stop loss if new stop is higher than current stop
if(new_stop_loss > current_stop_loss)
{
ÄndereStopLoss(new_stop_loss);
}
}
else if(position_type == POSITION_TYPE_SELL)
{
double new_stop_loss = current_price + (trailing_stop_pips * point * pips_multiplier);
double current_stop_loss = PositionGetDouble(POSITION_SL);
// Only move stop loss if new stop is lower than current stop
if(new_stop_loss < current_stop_loss || current_stop_loss == 0)
{
ÄndereStopLoss(new_stop_loss);
}
}
}
//--- Ausstieg bei Preis unter/über EMA (Exit when price below/above EMA)
if(ArraySize(esData.ema_array) >= 1)
{
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
double ema_aktuell = esData.ema_array[0];
bool exit_bullish = (position_type == POSITION_TYPE_SELL && aktueller_close > ema_aktuell);
bool exit_bearish = (position_type == POSITION_TYPE_BUY && aktueller_close < ema_aktuell);
if(exit_bullish || exit_bearish)
{
Print("TRACE: Ausstiegssignal - Close: ", aktueller_close, " EMA: ", ema_aktuell);
SchließePosition("EMA Crossover Exit");
Print("TRACE: Position geschlossen - Trade-Counter bleibt bei ", esData.trades_in_current_crossover);
}
}
//--- Profit-Prüfung nach X Bars (Profit check after X bars)
if(ES_CloseUnprofitableTrades && esData.trade_open_time != 0 && PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
Print("TRACE: Profit-Prüfung aktiviert - CloseUnprofitableTrades: ", ES_CloseUnprofitableTrades);
PrüfeProfitNachBars();
}
else if(!ES_CloseUnprofitableTrades)
{
Print("TRACE: Profit-Prüfung deaktiviert - CloseUnprofitableTrades: ", ES_CloseUnprofitableTrades);
}
}
//+------------------------------------------------------------------+
//| Profit-Prüfung nach X Bars (Profit check after X bars) |
//+------------------------------------------------------------------+
void PrüfeProfitNachBars()
{
if(!PositionSelectByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
return; // Keine Position offen
}
datetime current_bar_time = iTime(esData.symbol, ES_Timeframe, 0);
int bars_since_trade_open = iBarShift(esData.symbol, ES_Timeframe, esData.trade_open_time);
Print("TRACE: Bars seit Trade-Öffnung: ", bars_since_trade_open, "/", ES_ProfitCheckBars);
//--- Prüfe ob genügend Bars vergangen sind (Check if enough bars have passed)
if(bars_since_trade_open >= ES_ProfitCheckBars)
{
double position_profit = PositionGetDouble(POSITION_PROFIT);
double position_volume = PositionGetDouble(POSITION_VOLUME);
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
Print("TRACE: Profit-Prüfung nach ", ES_ProfitCheckBars, " Bars");
Print("TRACE: Position Profit: ", position_profit, " USD");
//--- Schließe Position wenn nicht im Profit (Close position if not in profit)
if(position_profit <= 0)
{
Print("TRACE: Position nicht im Profit - Schließe Position");
SchließePosition("Profit Check - Unprofitable");
//--- Trade-Öffnungszeit zurücksetzen (Reset trade opening time)
esData.trade_open_time = 0;
Print("TRACE: Trade-Öffnungszeit zurückgesetzt");
}
else
{
Print("TRACE: Position im Profit - Behalte Position");
//--- Trade-Öffnungszeit zurücksetzen um weitere Prüfungen zu vermeiden (Reset to avoid further checks)
esData.trade_open_time = 0;
}
}
}
//+------------------------------------------------------------------+
//| Stop Loss ändern (Modify Stop Loss) |
//+------------------------------------------------------------------+
void ÄndereStopLoss(double new_stop_loss)
{
Print("TRACE: Versuche Stop Loss zu ändern auf: ", new_stop_loss);
bool success = ModifyPositionByMagic(esData.trade, esData.symbol, (ulong)ES_MagicNumber, new_stop_loss, PositionGetDouble(POSITION_TP));
if(success)
{
Print("TRACE: Stop Loss erfolgreich geändert auf: ", new_stop_loss);
}
else
{
Print("TRACE: Fehler beim Ändern des Stop Loss - Retcode: ", esData.trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
}
}
//+------------------------------------------------------------------+
//| Position schließen (Close position) |
//+------------------------------------------------------------------+
void SchließePosition(string reason = "Unbekannt")
{
Print("TRACE: Versuche Position zu schließen - Grund: ", reason);
bool success = ClosePositionByMagic(esData.trade, esData.symbol, (ulong)ES_MagicNumber);
if(success)
{
Print("TRACE: Position erfolgreich geschlossen - Grund: ", reason);
}
else
{
Print("TRACE: Fehler beim Schließen der Position - Retcode: ", esData.trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void ProcessEMASlopeDistance(string symbol)
{
// Skip if not initialized (symbol not available)
if(!esData.isInitialized)
return;
esData.symbol = symbol; // Update symbol in case it changed
//--- Bar-Daten oder Tick-Daten verwenden (Use bar data or tick data)
if(ES_UseBarData)
{
//--- Nur bei neuen Bars ausführen (Only execute on new bars)
datetime current_bar_time = iTime(esData.symbol, ES_Timeframe, 0);
if(current_bar_time == esData.last_bar_time)
{
return; // Kein neuer Bar, nichts tun
}
esData.last_bar_time = current_bar_time;
}
//--- EMA Werte berechnen (Calculate EMA values)
BerechneEMA();
//--- Debug: Aktuelle Werte ausgeben (Debug: Output current values)
if(ArraySize(esData.ema_array) > 0)
{
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
double ema_aktuell = esData.ema_array[0];
double ema_vorher = esData.ema_array[1];
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / point;
double steigung = (ema_aktuell - ema_vorher) / point;
if(ES_UseBarData)
{
Print("=== DEBUG INFO (Neuer Bar) ===");
Print("Bar Zeit: ", TimeToString(iTime(esData.symbol, ES_Timeframe, 0)));
}
else
{
Print("=== DEBUG INFO (Tick) ===");
}
Print("Aktueller Close: ", aktueller_close);
Print("EMA: ", ema_aktuell);
Print("Preis-Abstand: ", preis_abstand, " Pips");
Print("EMA Steigung: ", steigung, " Pips");
Print("Differenz Close-EMA: ", aktueller_close - ema_aktuell);
Print("Preis-Trigger: ", esData.preis_trigger_aktiv, " Steigungs-Trigger: ", esData.steigung_trigger_aktiv);
Print("Überwachung aktiv: ", esData.überwachung_aktiv);
Print("Position offen: ", PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber));
Print("Trades im aktuellen Crossover: ", esData.trades_in_current_crossover, "/", ES_MaxTradesPerCrossover);
Print("==================");
}
//--- Überwachung prüfen (Check monitoring)
if(esData.überwachung_aktiv)
{
if(ES_UseBarData)
{
// Bar-basierte Überwachungszeit
int bars_since_monitoring = iBarShift(esData.symbol, ES_Timeframe, esData.letzte_überwachung_zeit);
int timeout_bars = (int)(ES_ÜberwachungTimeout / PeriodSeconds(ES_Timeframe));
if(bars_since_monitoring > timeout_bars)
{
esData.überwachung_aktiv = false;
esData.preis_trigger_aktiv = false;
esData.steigung_trigger_aktiv = false;
Print("Überwachung beendet - Bar-basierte Zeitüberschreitung (", bars_since_monitoring, " Bars)");
}
}
else
{
// Tick-basierte Überwachungszeit
if(TimeCurrent() - esData.letzte_überwachung_zeit > ES_ÜberwachungTimeout)
{
esData.überwachung_aktiv = false;
esData.preis_trigger_aktiv = false;
esData.steigung_trigger_aktiv = false;
Print("Überwachung beendet - Tick-basierte Zeitüberschreitung");
}
}
}
//--- Trigger-Bedingungen prüfen (Check trigger conditions)
PrüfeTrigger();
//--- Trade Management (Trade management)
VerwalteTrades();
}
//+------------------------------------------------------------------+
@@ -0,0 +1,240 @@
//+------------------------------------------------------------------+
//| RSICrossOverReversalStrategy.mqh |
//+------------------------------------------------------------------+
void WeekDays_Init()
{
rcData.WeekDays[0] = RC_Sunday;
rcData.WeekDays[1] = RC_Monday;
rcData.WeekDays[2] = RC_Tuesday;
rcData.WeekDays[3] = RC_Wednesday;
rcData.WeekDays[4] = RC_Thursday;
rcData.WeekDays[5] = RC_Friday;
rcData.WeekDays[6] = RC_Saturday;
}
bool WeekDays_Check(datetime aTime)
{
MqlDateTime stm;
TimeToStruct(aTime, stm);
return(rcData.WeekDays[stm.day_of_week]);
}
int TimeHour(datetime when = 0)
{
if(when == 0) when = TimeCurrent();
MqlDateTime dt;
TimeToStruct(when, dt);
return dt.hour;
}
bool InitRSICrossOverReversal(string symbol)
{
WeekDays_Init();
rcData.symbol = symbol;
rcData.previousRSIDef = 0;
rcData.lastTradeTime = 0;
rcData.bartime = 0;
rcData.lastBarTime = 0;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("RSICrossOverReversal: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
Sleep(100); // Wait for symbol to be ready
rcData.rsiHandle = iRSI(symbol, RC_TimeFrame1, RC_rsiPeriod, PRICE_CLOSE);
if(rcData.rsiHandle == INVALID_HANDLE)
{
Print("RSICrossOverReversal: Error creating RSI handle for '", symbol, "'");
return false;
}
rcData.emaHandle = iMA(symbol, RC_TimeFrame2, RC_emaPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(rcData.emaHandle == INVALID_HANDLE)
{
Print("RSICrossOverReversal: Error creating EMA handle for '", symbol, "'");
return false;
}
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
rcData.isInitialized = true;
Print("RSICrossOverReversal: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitRSICrossOverReversal()
{
if(rcData.rsiHandle != INVALID_HANDLE)
IndicatorRelease(rcData.rsiHandle);
if(rcData.emaHandle != INVALID_HANDLE)
IndicatorRelease(rcData.emaHandle);
}
void Close_Position_MN(ulong magicNumber)
{
ClosePositionByMagic(rcData.trade, rcData.symbol, (int)magicNumber);
}
void ApplyTrailingStop()
{
if(!PositionSelectByMagic(rcData.symbol, RC_MagicNumber))
return;
ulong PositionTicket = PositionGetInteger(POSITION_TICKET);
ENUM_POSITION_TYPE trade_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
string symbol = rcData.symbol;
double POINT = SymbolInfoDouble(symbol, SYMBOL_POINT);
int DIGIT = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
if(trade_type == POSITION_TYPE_BUY)
{
double Bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), DIGIT);
if(Bid - PositionGetDouble(POSITION_PRICE_OPEN) > NormalizeDouble(POINT * RC_TrailingStop, DIGIT))
{
if(PositionGetDouble(POSITION_SL) < NormalizeDouble(Bid - POINT * RC_TrailingStop, DIGIT))
{
ModifyPositionByMagic(rcData.trade, symbol, RC_MagicNumber,
NormalizeDouble(Bid - POINT * RC_TrailingStop, DIGIT),
PositionGetDouble(POSITION_TP));
}
}
}
else if(trade_type == POSITION_TYPE_SELL)
{
double Ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), DIGIT);
if((PositionGetDouble(POSITION_PRICE_OPEN) - Ask) > NormalizeDouble(POINT * RC_TrailingStop, DIGIT))
{
if((PositionGetDouble(POSITION_SL) > NormalizeDouble(Ask + POINT * RC_TrailingStop, DIGIT)) ||
(PositionGetDouble(POSITION_SL) == 0))
{
ModifyPositionByMagic(rcData.trade, symbol, RC_MagicNumber,
NormalizeDouble(Ask + POINT * RC_TrailingStop, DIGIT),
PositionGetDouble(POSITION_TP));
}
}
}
}
void ProcessRSICrossOverReversal(string symbol)
{
// Skip if not initialized (symbol not available)
if(!rcData.isInitialized)
return;
rcData.symbol = symbol; // Update symbol in case it changed
if(rcData.bartime == iTime(rcData.symbol, RC_BarTimeFrame, 0))
return;
rcData.bartime = iTime(rcData.symbol, RC_BarTimeFrame, 0);
double rsi[];
if(CopyBuffer(rcData.rsiHandle, 0, 0, 2, rsi) <= 0)
return;
double ema[];
if(CopyBuffer(rcData.emaHandle, 0, 0, 2, ema) <= 0)
return;
datetime currentTime = TimeCurrent();
int currentHour = TimeHour(TimeCurrent());
if(!WeekDays_Check(TimeTradeServer()))
{
Close_Position_MN(RC_MagicNumber);
return;
}
if(!((currentHour < RC_tradingHourOneEnd && currentHour > RC_tradingHourOneBegin) ||
(currentHour < RC_tradingHourTwoEnd && currentHour > RC_tradingHourTwoBegin)))
{
Close_Position_MN(RC_MagicNumber);
return;
}
bool hasPosition = PositionExistsByMagic(rcData.symbol, RC_MagicNumber);
double currentRSI = rsi[0];
double previousRSI = rsi[1];
if(rcData.previousRSIDef == 0)
{
rcData.previousRSIDef = currentRSI;
return;
}
double currentEMA = ema[0];
double previousEMA = ema[1];
double emaSlope = (currentEMA - previousEMA) * 100;
double closeCurr = iClose(Symbol(), Period(), 0);
double priceToEmaDistance = (closeCurr - currentEMA) * 10;
bool isBuyPosition = false;
bool isSellPosition = false;
if(hasPosition)
{
if(PositionSelectByMagic(rcData.symbol, RC_MagicNumber))
{
ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(positionType == POSITION_TYPE_BUY)
isBuyPosition = true;
else if(positionType == POSITION_TYPE_SELL)
isSellPosition = true;
}
}
ApplyTrailingStop();
bool cooldownPassed = (currentTime - rcData.lastTradeTime) >= RC_cooldownSeconds;
bool isTrendStrong = MathAbs(emaSlope) > RC_emaSlopeThreshold || MathAbs(priceToEmaDistance) > RC_emaDistanceThreshold;
if(isBuyPosition && currentRSI > RC_exitBuyRSI)
{
Close_Position_MN(RC_MagicNumber);
rcData.lastTradeTime = currentTime;
}
if(isSellPosition && currentRSI < RC_exitSellRSI)
{
Close_Position_MN(RC_MagicNumber);
rcData.lastTradeTime = currentTime;
}
if(isTrendStrong)
{
Close_Position_MN(RC_MagicNumber);
rcData.lastTradeTime = currentTime;
return;
}
if(currentRSI < RC_overboughtLevel - RC_entryRSISellSpread && rcData.previousRSIDef >= RC_overboughtLevel &&
!isSellPosition && !hasPosition && cooldownPassed)
{
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
if(rcData.trade.Sell(g_RC_LotSize, rcData.symbol, 0.0, 0.0, 0.0, "Sell Order"))
{
rcData.lastTradeTime = currentTime;
}
}
if(currentRSI > RC_oversoldLevel + RC_entryRSIBuySpread && rcData.previousRSIDef <= RC_oversoldLevel &&
!isBuyPosition && !hasPosition && cooldownPassed)
{
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
if(rcData.trade.Buy(g_RC_LotSize, rcData.symbol, 0.0, 0.0, 0.0, "Buy Order"))
{
rcData.lastTradeTime = currentTime;
}
}
rcData.previousRSIDef = currentRSI;
}
//+------------------------------------------------------------------+
@@ -0,0 +1,471 @@
//+------------------------------------------------------------------+
//| RSIMidPointHijackStrategy.mqh |
//+------------------------------------------------------------------+
bool IsNewBar(string symbol)
{
datetime time[];
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
{
if(time[0] != rmData.lastBarTime)
{
rmData.lastBarTime = time[0];
return true;
}
}
return false;
}
bool IsWithinTradingHours(int startHour, int endHour)
{
MqlDateTime currentTime;
TimeToStruct(TimeCurrent(), currentTime);
if(startHour <= endHour)
return (currentTime.hour >= startHour && currentTime.hour < endHour);
else
return (currentTime.hour >= startHour || currentTime.hour < endHour);
}
bool HasPosition(string symbol, int magic)
{
return PositionExistsByMagic(symbol, magic);
}
bool HasProfitablePosition(int excludeMagic)
{
bool hasProfitable = false;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(rmData.positionInfo.SelectByIndex(i))
{
if(rmData.positionInfo.Magic() != excludeMagic)
{
double profit = rmData.positionInfo.Profit();
if(profit > RM_InpLockProfitThreshold * _Point)
{
hasProfitable = true;
if(RM_InpCloseOppositeTrades)
{
if((excludeMagic == RM_InpMagicNumberRSIFollow && rmData.positionInfo.Magic() == RM_InpMagicNumberRSIReverse) ||
(excludeMagic == RM_InpMagicNumberRSIReverse && rmData.positionInfo.Magic() == RM_InpMagicNumberRSIFollow) ||
(excludeMagic == RM_InpMagicNumberEMACross && (rmData.positionInfo.Magic() == RM_InpMagicNumberRSIReverse || rmData.positionInfo.Magic() == RM_InpMagicNumberRSIFollow)) ||
((excludeMagic == RM_InpMagicNumberRSIFollow || excludeMagic == RM_InpMagicNumberRSIReverse) && rmData.positionInfo.Magic() == RM_InpMagicNumberEMACross))
{
ClosePosition(rmData.symbol, (int)rmData.positionInfo.Magic());
}
}
}
}
}
}
return hasProfitable;
}
bool IsRSIReverseInCooldown(string symbol)
{
if(RM_InpRSIReverseCooldownBars <= 0)
return false;
if(!rmData.rsiReverseInCooldown)
return false;
datetime time[];
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
{
datetime currentBarTime = time[0];
datetime cooldownEndTime = rmData.rsiReverseLastCloseTime + RM_InpRSIReverseCooldownBars * PeriodSeconds(RM_InpTimeframe);
if(currentBarTime >= cooldownEndTime)
{
rmData.rsiReverseInCooldown = false;
return false;
}
}
return true;
}
void CheckRSIFollowStrategy(string symbol)
{
if(!IsWithinTradingHours(RM_InpRSIFollowStartHour, RM_InpRSIFollowEndHour))
{
if(RM_InpRSIFollowCloseOutsideHours)
{
if(HasPosition(symbol, RM_InpMagicNumberRSIFollow))
ClosePosition(symbol, RM_InpMagicNumberRSIFollow);
}
return;
}
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberRSIFollow))
return;
if(rmData.lastBarRSI > RM_InpRSIOverbought)
rmData.rsiOverbought = true;
else if(rmData.lastBarRSI < RM_InpRSIOversold)
rmData.rsiOversold = true;
if(rmData.rsiOverbought && rmData.lastBarRSI < RM_InpRSIExitLevel)
{
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
}
rmData.rsiOverbought = false;
}
else if(rmData.rsiOversold && rmData.lastBarRSI > RM_InpRSIExitLevel)
{
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
}
rmData.rsiOversold = false;
}
}
void CheckRSIReverseStrategy(string symbol)
{
if(!IsWithinTradingHours(RM_InpRSIReverseStartHour, RM_InpRSIReverseEndHour))
{
if(RM_InpRSIReverseCloseOutsideHours)
{
if(HasPosition(symbol, RM_InpMagicNumberRSIReverse))
ClosePosition(symbol, RM_InpMagicNumberRSIReverse);
}
return;
}
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberRSIReverse))
return;
if(IsRSIReverseInCooldown(symbol))
return;
if(rmData.lastBarRSIReverse > RM_InpRSIReverseOverbought)
rmData.rsiReverseOverbought = true;
else if(rmData.lastBarRSIReverse < RM_InpRSIReverseOversold)
rmData.rsiReverseOversold = true;
if(rmData.rsiReverseOverbought && rmData.lastBarRSIReverse < RM_InpRSIReverseCrossLevel)
{
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
}
rmData.rsiReverseOverbought = false;
}
else if(rmData.rsiReverseOversold && rmData.lastBarRSIReverse > RM_InpRSIReverseCrossLevel)
{
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
}
rmData.rsiReverseOversold = false;
}
}
void CheckEMACrossStrategy(string symbol)
{
if(!IsWithinTradingHours(RM_InpEMACrossStartHour, RM_InpEMACrossEndHour))
{
if(RM_InpEMACrossCloseOutsideHours)
{
if(HasPosition(symbol, RM_InpMagicNumberEMACross))
ClosePosition(symbol, RM_InpMagicNumberEMACross);
}
return;
}
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberEMACross))
return;
if(rmData.lastBarEMAPrev < rmData.lastBarClosePrev && rmData.lastBarEMA > rmData.lastBarClose)
{
rmData.emaCrossBuySignal = true;
rmData.emaCrossSellSignal = false;
rmData.emaCrossSignalBar = 0;
}
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
{
rmData.emaCrossSellSignal = true;
rmData.emaCrossBuySignal = false;
rmData.emaCrossSignalBar = 0;
}
if(RM_InpUseEMADistanceEntry)
{
if(rmData.emaCrossBuySignal)
{
bool distanceConditionMet = true;
double emaHistory[], closeHistory[];
ArraySetAsSeries(emaHistory, true);
ArraySetAsSeries(closeHistory, true);
if(CopyBuffer(rmData.emaHandle, 0, 0, RM_InpEMADistancePeriod, emaHistory) > 0 &&
CopyClose(symbol, RM_InpTimeframe, 0, RM_InpEMADistancePeriod, closeHistory) > 0)
{
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
for(int i = 0; i < RM_InpEMADistancePeriod; i++)
{
double distance = (closeHistory[i] - emaHistory[i]) / point;
if(distance < RM_InpEMADistancePips)
{
distanceConditionMet = false;
break;
}
}
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
rmData.emaCrossBuySignal = false;
}
}
}
else if(rmData.emaCrossSellSignal)
{
bool distanceConditionMet = true;
double emaHistory[], closeHistory[];
ArraySetAsSeries(emaHistory, true);
ArraySetAsSeries(closeHistory, true);
if(CopyBuffer(rmData.emaHandle, 0, 0, RM_InpEMADistancePeriod, emaHistory) > 0 &&
CopyClose(symbol, RM_InpTimeframe, 0, RM_InpEMADistancePeriod, closeHistory) > 0)
{
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
for(int i = 0; i < RM_InpEMADistancePeriod; i++)
{
double distance = (emaHistory[i] - closeHistory[i]) / point;
if(distance < RM_InpEMADistancePips)
{
distanceConditionMet = false;
break;
}
}
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
rmData.emaCrossSellSignal = false;
}
}
}
}
else
{
if(rmData.lastBarEMAPrev < rmData.lastBarClosePrev && rmData.lastBarEMA > rmData.lastBarClose)
{
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
}
}
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
{
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
}
}
}
if(rmData.emaCrossBuySignal || rmData.emaCrossSellSignal)
{
rmData.emaCrossSignalBar++;
if(rmData.emaCrossSignalBar > RM_InpEMADistancePeriod * 2)
{
rmData.emaCrossBuySignal = false;
rmData.emaCrossSellSignal = false;
}
}
}
void CheckExitConditions(string symbol)
{
if(RM_InpEnableRSIFollow)
{
if(HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
if(PositionSelectByMagic(symbol, RM_InpMagicNumberRSIFollow))
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if((posType == POSITION_TYPE_BUY && rmData.lastBarRSI < RM_InpRSIExitLevel) ||
(posType == POSITION_TYPE_SELL && rmData.lastBarRSI > RM_InpRSIExitLevel))
{
ClosePosition(symbol, RM_InpMagicNumberRSIFollow);
}
}
}
}
if(RM_InpEnableRSIReverse)
{
if(HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
if(PositionSelectByMagic(symbol, RM_InpMagicNumberRSIReverse))
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if((posType == POSITION_TYPE_BUY && rmData.lastBarRSIReverse < RM_InpRSIReverseExitLevel) ||
(posType == POSITION_TYPE_SELL && rmData.lastBarRSIReverse > RM_InpRSIReverseExitLevel))
{
ClosePosition(symbol, RM_InpMagicNumberRSIReverse);
}
}
}
}
if(RM_InpEnableEMACross)
{
if(HasPosition(symbol, RM_InpMagicNumberEMACross))
{
if(PositionSelectByMagic(symbol, RM_InpMagicNumberEMACross))
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if((posType == POSITION_TYPE_BUY && rmData.lastBarEMA > rmData.lastBarClose) ||
(posType == POSITION_TYPE_SELL && rmData.lastBarEMA < rmData.lastBarClose))
{
ClosePosition(symbol, RM_InpMagicNumberEMACross);
}
}
}
}
}
void ClosePosition(string symbol, int magic)
{
if(!PositionExistsByMagic(symbol, magic))
return;
ulong ticket = GetPositionTicketByMagic(symbol, magic);
if(ticket == 0)
return;
if(magic == RM_InpMagicNumberRSIReverse)
{
if(PositionSelectByTicketSymbolAndMagic(ticket, symbol, magic))
{
datetime time[];
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
{
rmData.rsiReverseLastCloseTime = time[0];
double profit = PositionGetDouble(POSITION_PROFIT);
if(!RM_InpRSIReverseCooldownOnLoss || profit < 0)
{
rmData.rsiReverseInCooldown = true;
}
}
}
}
ClosePositionByMagic(rmData.trade, symbol, magic);
}
bool InitRSIMidPointHijack(string symbol)
{
rmData.symbol = symbol;
rmData.rsiOverbought = false;
rmData.rsiOversold = false;
rmData.rsiReverseOverbought = false;
rmData.rsiReverseOversold = false;
rmData.emaCrossBuySignal = false;
rmData.emaCrossSellSignal = false;
rmData.emaCrossSignalBar = 0;
rmData.rsiReverseInCooldown = false;
rmData.lastBarRSI = 0;
rmData.lastBarRSIReverse = 0;
rmData.lastBarEMA = 0;
rmData.lastBarClose = 0;
rmData.lastBarEMAPrev = 0;
rmData.lastBarClosePrev = 0;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("RSIMidPointHijack: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
Sleep(100); // Wait for symbol to be ready
rmData.rsiHandle = iRSI(symbol, RM_InpTimeframe, RM_InpRSIPeriod, PRICE_CLOSE);
rmData.rsiReverseHandle = iRSI(symbol, RM_InpTimeframe, RM_InpRSIReversePeriod, PRICE_CLOSE);
rmData.emaHandle = iMA(symbol, RM_InpTimeframe, RM_InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(rmData.rsiHandle == INVALID_HANDLE || rmData.rsiReverseHandle == INVALID_HANDLE || rmData.emaHandle == INVALID_HANDLE)
{
Print("RSIMidPointHijack: Error creating indicators for '", symbol, "'");
return false;
}
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
rmData.trade.SetMarginMode();
rmData.trade.SetTypeFillingBySymbol(symbol);
rmData.trade.SetDeviationInPoints(10);
datetime time[];
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
rmData.lastBarTime = time[0];
rmData.isInitialized = true;
Print("RSIMidPointHijack: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitRSIMidPointHijack()
{
if(rmData.rsiHandle != INVALID_HANDLE) IndicatorRelease(rmData.rsiHandle);
if(rmData.rsiReverseHandle != INVALID_HANDLE) IndicatorRelease(rmData.rsiReverseHandle);
if(rmData.emaHandle != INVALID_HANDLE) IndicatorRelease(rmData.emaHandle);
}
void ProcessRSIMidPointHijack(string symbol)
{
// Skip if not initialized (symbol not available)
if(!rmData.isInitialized)
return;
rmData.symbol = symbol; // Update symbol in case it changed
if(!IsNewBar(rmData.symbol))
return;
double rsi[], rsiReverse[], ema[], close[];
ArraySetAsSeries(rsi, true);
ArraySetAsSeries(rsiReverse, true);
ArraySetAsSeries(ema, true);
ArraySetAsSeries(close, true);
rmData.lastBarEMAPrev = rmData.lastBarEMA;
rmData.lastBarClosePrev = rmData.lastBarClose;
if(CopyBuffer(rmData.rsiHandle, 0, 0, 1, rsi) > 0)
rmData.lastBarRSI = rsi[0];
if(CopyBuffer(rmData.rsiReverseHandle, 0, 0, 1, rsiReverse) > 0)
rmData.lastBarRSIReverse = rsiReverse[0];
if(CopyBuffer(rmData.emaHandle, 0, 0, 1, ema) > 0)
rmData.lastBarEMA = ema[0];
if(CopyClose(rmData.symbol, RM_InpTimeframe, 0, 1, close) > 0)
rmData.lastBarClose = close[0];
if(RM_InpEnableRSIFollow)
CheckRSIFollowStrategy(rmData.symbol);
if(RM_InpEnableRSIReverse)
CheckRSIReverseStrategy(rmData.symbol);
if(RM_InpEnableEMACross)
CheckEMACrossStrategy(rmData.symbol);
CheckExitConditions(rmData.symbol);
}
//+------------------------------------------------------------------+
@@ -0,0 +1,493 @@
//+------------------------------------------------------------------+
//| RSIReversalAsianStrategy.mqh |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| RSI Reversal Asian Strategy Data Structure |
//+------------------------------------------------------------------+
struct RSIReversalAsianData {
string symbol;
bool isInitialized;
int rsiHandle;
CTrade trade;
bool isPositionOpen;
double positionOpenPrice;
datetime positionOpenTime;
ENUM_POSITION_TYPE lastPositionType;
bool sessionCloseAttempted;
// RSI crossover variables
double rsiCurrent;
double rsiPrevious;
double rsiPrevious2;
bool rsiCrossedOverbought;
bool rsiCrossedOversold;
bool rsiCrossedExitLevel;
// Strategy parameters
int RSIPeriod;
double OverboughtLevel;
double OversoldLevel;
int TakeProfitPips;
int StopLossPips;
double MaxLotSize;
int MaxSpread;
int MaxDuration;
bool UseStopLoss;
bool UseTakeProfit;
bool UseRSIExit;
double RSIExitLevel;
bool CloseOutsideSession;
ENUM_TIMEFRAMES TimeFrame;
int MagicNumber;
int Slippage;
double point;
};
// Session times (UTC)
const int AsianSessionStart = 0; // 00:00 UTC
const int AsianSessionEnd = 8; // 08:00 UTC
//+------------------------------------------------------------------+
//| Check if current time is in Asian session |
//+------------------------------------------------------------------+
bool IsAsianSession()
{
datetime currentTime = TimeCurrent();
MqlDateTime timeStruct;
TimeToStruct(currentTime, timeStruct);
return (timeStruct.hour >= AsianSessionStart && timeStruct.hour < AsianSessionEnd);
}
//+------------------------------------------------------------------+
//| Check if trading is allowed for symbol |
//+------------------------------------------------------------------+
bool IsTradingAllowed(RSIReversalAsianData& data)
{
// Check if market is open
long tradeMode = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_MODE);
if(tradeMode != SYMBOL_TRADE_MODE_FULL)
{
return false;
}
// Check if we have enough money
if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0)
{
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Check RSI crossover conditions |
//+------------------------------------------------------------------+
void CheckRSICrossover(RSIReversalAsianData& data)
{
// Reset crossover flags
data.rsiCrossedOverbought = false;
data.rsiCrossedOversold = false;
data.rsiCrossedExitLevel = false;
// Check for overbought crossover (RSI crosses above overbought level)
if(data.rsiPrevious < data.OverboughtLevel && data.rsiCurrent >= data.OverboughtLevel)
{
data.rsiCrossedOverbought = true;
}
// Check for oversold crossover (RSI crosses below oversold level)
if(data.rsiPrevious > data.OversoldLevel && data.rsiCurrent <= data.OversoldLevel)
{
data.rsiCrossedOversold = true;
}
// Check for exit level crossover
if(data.rsiPrevious < data.RSIExitLevel && data.rsiCurrent >= data.RSIExitLevel)
{
data.rsiCrossedExitLevel = true;
}
else if(data.rsiPrevious > data.RSIExitLevel && data.rsiCurrent <= data.RSIExitLevel)
{
data.rsiCrossedExitLevel = true;
}
}
//+------------------------------------------------------------------+
//| Close all trades for the symbol |
//+------------------------------------------------------------------+
bool CloseAllTrades(RSIReversalAsianData& data, string reason = "")
{
bool allClosed = true;
int totalPositions = PositionsTotal();
if(totalPositions == 0)
return true;
for(int i = totalPositions - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == data.symbol)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == (ulong)data.MagicNumber)
{
// Try to close position with retry logic
int retryCount = 0;
bool positionClosed = false;
while(retryCount < 3 && !positionClosed)
{
if(data.trade.PositionClose(ticket))
{
data.isPositionOpen = false;
positionClosed = true;
}
else
{
int error = GetLastError();
// If error is 4756 (Trade disabled), wait longer before retry
if(error == 4756)
{
Sleep(5000); // Wait 5 seconds before retry
retryCount++;
}
else
{
// For other errors, break the loop
break;
}
}
}
if(!positionClosed)
{
allClosed = false;
}
}
}
}
}
return allClosed;
}
//+------------------------------------------------------------------+
//| Initialize RSI Reversal Asian Strategy |
//+------------------------------------------------------------------+
bool InitRSIReversalAsian(RSIReversalAsianData& data, string symbol,
int RSIPeriod, double OverboughtLevel, double OversoldLevel,
int TakeProfitPips, int StopLossPips, double MaxLotSize,
int MaxSpread, int MaxDuration, bool UseStopLoss,
bool UseTakeProfit, bool UseRSIExit, double RSIExitLevel,
bool CloseOutsideSession, ENUM_TIMEFRAMES TimeFrame,
int MagicNumber, int Slippage)
{
data.symbol = symbol;
data.isInitialized = false;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("RSIReversalAsian: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
// Wait a bit for symbol to be ready
Sleep(100);
// Get symbol point
data.point = SymbolInfoDouble(symbol, SYMBOL_POINT);
// Store parameters
data.RSIPeriod = RSIPeriod;
data.OverboughtLevel = OverboughtLevel;
data.OversoldLevel = OversoldLevel;
data.TakeProfitPips = TakeProfitPips;
data.StopLossPips = StopLossPips;
data.MaxLotSize = MaxLotSize;
data.MaxSpread = MaxSpread;
data.MaxDuration = MaxDuration;
data.UseStopLoss = UseStopLoss;
data.UseTakeProfit = UseTakeProfit;
data.UseRSIExit = UseRSIExit;
data.RSIExitLevel = RSIExitLevel;
data.CloseOutsideSession = CloseOutsideSession;
data.TimeFrame = TimeFrame;
data.MagicNumber = MagicNumber;
data.Slippage = Slippage;
// Initialize RSI indicator with retry logic (for insufficient history in backtesting)
data.rsiHandle = INVALID_HANDLE;
int retryCount = 0;
int maxRetries = 5;
while(retryCount < maxRetries && data.rsiHandle == INVALID_HANDLE)
{
data.rsiHandle = iRSI(symbol, TimeFrame, RSIPeriod, PRICE_CLOSE);
if(data.rsiHandle == INVALID_HANDLE)
{
int error = GetLastError();
// Error 4805 = insufficient history - wait longer and retry
if(error == 4805 && retryCount < maxRetries - 1)
{
Sleep(1000); // Wait 1 second for history to load
retryCount++;
continue;
}
Print("RSIReversalAsian: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", error == 4805 ? "Insufficient history data" : "Unknown", ")");
return false;
}
}
if(data.rsiHandle == INVALID_HANDLE)
{
Print("RSIReversalAsian: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries");
return false;
}
// Wait a bit for the indicator to be ready
Sleep(100);
// Initialize RSI values with retry logic
double rsi[];
ArraySetAsSeries(rsi, true);
retryCount = 0;
bool rsiInitialized = false;
while(retryCount < 10 && !rsiInitialized)
{
int copied = CopyBuffer(data.rsiHandle, 0, 0, 3, rsi);
if(copied >= 3)
{
data.rsiCurrent = rsi[0];
data.rsiPrevious = rsi[1];
data.rsiPrevious2 = rsi[2];
rsiInitialized = true;
}
else
{
retryCount++;
Sleep(100);
}
}
if(!rsiInitialized)
{
// Don't fail initialization, just set default values
data.rsiCurrent = 50.0;
data.rsiPrevious = 50.0;
data.rsiPrevious2 = 50.0;
}
// Set trade parameters
data.trade.SetExpertMagicNumber(MagicNumber);
data.trade.SetDeviationInPoints(Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
// Initialize state
data.isPositionOpen = false;
data.positionOpenPrice = 0;
data.positionOpenTime = 0;
data.lastPositionType = POSITION_TYPE_BUY;
data.sessionCloseAttempted = false;
data.rsiCrossedOverbought = false;
data.rsiCrossedOversold = false;
data.rsiCrossedExitLevel = false;
data.isInitialized = true;
Print("RSIReversalAsian: Successfully initialized for symbol '", symbol, "'");
return true;
}
//+------------------------------------------------------------------+
//| Deinitialize RSI Reversal Asian Strategy |
//+------------------------------------------------------------------+
void DeinitRSIReversalAsian(RSIReversalAsianData& data)
{
if(data.rsiHandle != INVALID_HANDLE)
IndicatorRelease(data.rsiHandle);
}
//+------------------------------------------------------------------+
//| Process RSI Reversal Asian Strategy |
//+------------------------------------------------------------------+
void ProcessRSIReversalAsian(RSIReversalAsianData& data, double lotSize)
{
if(!data.isInitialized)
return;
// Check if trading is allowed
if(!IsTradingAllowed(data))
{
return;
}
// Check if we're in Asian session
if(!IsAsianSession())
{
// Close all positions if outside Asian session and CloseOutsideSession is true
if(data.CloseOutsideSession && !data.sessionCloseAttempted)
{
CloseAllTrades(data, "Outside Asian session");
data.sessionCloseAttempted = true;
}
return;
}
else
{
// Reset the session close attempt flag when we enter Asian session
data.sessionCloseAttempted = false;
}
// Get current spread
double spread = SymbolInfoDouble(data.symbol, SYMBOL_ASK) - SymbolInfoDouble(data.symbol, SYMBOL_BID);
int spreadInPips = (int)(spread / data.point);
// Check if spread is too high
if(spreadInPips > data.MaxSpread)
{
return;
}
// Get RSI values from bar data
double rsi[];
ArraySetAsSeries(rsi, true);
int copied = CopyBuffer(data.rsiHandle, 0, 0, 3, rsi);
if(copied < 3)
{
return;
}
// Update RSI values
data.rsiPrevious2 = data.rsiPrevious;
data.rsiPrevious = data.rsiCurrent;
data.rsiCurrent = rsi[0];
// Validate RSI values
if(data.rsiCurrent == 0 || data.rsiPrevious == 0)
{
return;
}
// Check for RSI crossovers
CheckRSICrossover(data);
// Get current prices
double currentBid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
double currentAsk = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
// Check for open position
bool hasOpenPosition = PositionExistsByMagic(data.symbol, (ulong)data.MagicNumber);
if(hasOpenPosition)
{
// Get position details
ulong ticket = GetPositionTicketByMagic(data.symbol, (ulong)data.MagicNumber);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
// Check for RSI exit if enabled
if(data.UseRSIExit && data.rsiCrossedExitLevel)
{
bool shouldExit = false;
// For long positions, exit when RSI crosses above exit level
if(posType == POSITION_TYPE_BUY && data.rsiCurrent >= data.RSIExitLevel && data.rsiPrevious < data.RSIExitLevel)
{
shouldExit = true;
}
// For short positions, exit when RSI crosses below exit level
else if(posType == POSITION_TYPE_SELL && data.rsiCurrent <= data.RSIExitLevel && data.rsiPrevious > data.RSIExitLevel)
{
shouldExit = true;
}
if(shouldExit)
{
CloseAllTrades(data, "RSI Exit Crossover");
return;
}
}
// Check for timeout
if(TimeCurrent() - openTime > data.MaxDuration * 3600)
{
CloseAllTrades(data, "Timeout");
return;
}
}
}
// If no position is open, look for entry signals based on RSI crossover
if(!hasOpenPosition)
{
// Place buy order if RSI crosses below oversold level (oversold crossover)
if(data.rsiCrossedOversold)
{
double sl = data.UseStopLoss ? currentBid - data.StopLossPips * data.point : 0;
double tp = data.UseTakeProfit ? currentBid + data.TakeProfitPips * data.point : 0;
if(data.UseStopLoss && sl >= currentBid)
return;
if(data.UseTakeProfit && tp <= currentBid)
return;
// Set trade parameters
data.trade.SetDeviationInPoints(data.Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
data.trade.SetExpertMagicNumber(data.MagicNumber);
// Use dynamic lot size
double tradeLotSize = lotSize > 0 ? lotSize : data.MaxLotSize;
// Place buy order using CTrade
if(data.trade.Buy(tradeLotSize, data.symbol, currentAsk, sl, tp, "RSI Oversold Crossover Buy"))
{
data.isPositionOpen = true;
data.positionOpenPrice = currentAsk;
data.positionOpenTime = TimeCurrent();
data.lastPositionType = POSITION_TYPE_BUY;
}
}
// Place sell order if RSI crosses above overbought level (overbought crossover)
else if(data.rsiCrossedOverbought)
{
double sl = data.UseStopLoss ? currentAsk + data.StopLossPips * data.point : 0;
double tp = data.UseTakeProfit ? currentAsk - data.TakeProfitPips * data.point : 0;
if(data.UseStopLoss && sl <= currentAsk)
return;
if(data.UseTakeProfit && tp >= currentAsk)
return;
// Set trade parameters
data.trade.SetDeviationInPoints(data.Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
data.trade.SetExpertMagicNumber(data.MagicNumber);
// Use dynamic lot size
double tradeLotSize = lotSize > 0 ? lotSize : data.MaxLotSize;
// Place sell order using CTrade
if(data.trade.Sell(tradeLotSize, data.symbol, currentBid, sl, tp, "RSI Overbought Crossover Sell"))
{
data.isPositionOpen = true;
data.positionOpenPrice = currentBid;
data.positionOpenTime = TimeCurrent();
data.lastPositionType = POSITION_TYPE_SELL;
}
}
}
}
@@ -0,0 +1,451 @@
//+------------------------------------------------------------------+
//| RSIScalpingStrategy.mqh |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| RSI Scalping Strategy Data Structure |
//+------------------------------------------------------------------+
struct RSIScalpingData {
string symbol;
bool isInitialized;
CTrade trade;
int rsi_handle;
double rsi_buffer[];
double rsi_prev;
double rsi_current;
double rsi_two_bars_ago;
bool position_open;
ulong position_ticket;
ENUM_POSITION_TYPE current_position_type;
datetime last_bar_time;
bool rsi_against_position;
int bars_against_count;
};
string ErrorDescription(int errorCode)
{
switch(errorCode)
{
case 4801: return "Symbol not found";
case 4802: return "Symbol not selected";
case 4803: return "Symbol not visible";
case 4804: return "Symbol not available";
case 4805: return "Cannot load indicator - insufficient history data";
default: return "Unknown error " + IntegerToString(errorCode);
}
}
bool InitRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
ENUM_APPLIED_PRICE RSI_Applied_Price, int MagicNumber, int Slippage)
{
data.symbol = symbol;
data.isInitialized = false;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("RSIScalping: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false; // Return false but don't fail entire EA
}
// Wait a bit for symbol to be ready
Sleep(100);
// Try to create RSI indicator with retry logic (for insufficient history in backtesting)
data.rsi_handle = INVALID_HANDLE;
int retryCount = 0;
int maxRetries = 5;
while(retryCount < maxRetries && data.rsi_handle == INVALID_HANDLE)
{
data.rsi_handle = iRSI(symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
if(data.rsi_handle == INVALID_HANDLE)
{
int error = GetLastError();
// Error 4805 = insufficient history - wait longer and retry
if(error == 4805 && retryCount < maxRetries - 1)
{
Sleep(1000); // Wait 1 second for history to load
retryCount++;
continue;
}
Print("RSIScalping: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", ErrorDescription(error), ")");
return false; // Return false but don't fail entire EA
}
}
if(data.rsi_handle == INVALID_HANDLE)
{
Print("RSIScalping: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries");
return false;
}
data.trade.SetExpertMagicNumber(MagicNumber);
data.trade.SetDeviationInPoints(Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_FOK);
ArraySetAsSeries(data.rsi_buffer, true);
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
data.isInitialized = true;
Print("RSIScalping: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitRSIScalping(RSIScalpingData& data)
{
if(data.rsi_handle != INVALID_HANDLE)
IndicatorRelease(data.rsi_handle);
}
bool UpdateRSI(RSIScalpingData& data)
{
if(CopyBuffer(data.rsi_handle, 0, 0, 3, data.rsi_buffer) < 3)
return false;
data.rsi_current = data.rsi_buffer[0];
data.rsi_prev = data.rsi_buffer[1];
data.rsi_two_bars_ago = data.rsi_buffer[2];
return true;
}
void CheckExistingPosition(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
double RSI_Oversold, double RSI_Overbought, double RSI_Target_Buy,
double RSI_Target_Sell, int BarsToWait)
{
// Always check if position exists, even if tracking says it doesn't
bool positionExists = PositionExistsByMagic(data.symbol, MagicNumber);
if(!positionExists && data.position_open)
{
// Position was closed externally, reset tracking
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
return;
}
if(!positionExists)
return;
// Update tracking if we have a position but tracking was lost
if(!data.position_open && positionExists)
{
ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
{
data.position_ticket = ticket;
data.position_open = true;
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
}
// Verify our tracked position still exists
if(data.position_open && data.position_ticket > 0)
{
if(!PositionSelectByTicketSymbolAndMagic(data.position_ticket, data.symbol, MagicNumber))
{
// Try to find the position again
ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
{
data.position_ticket = ticket;
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
else
{
// Position doesn't exist, reset tracking
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
return;
}
}
else
{
// Update position type in case it changed (shouldn't happen, but be safe)
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
}
if(data.current_position_type == POSITION_TYPE_BUY)
{
if(data.rsi_current < RSI_Oversold)
{
if(!data.rsi_against_position)
{
data.rsi_against_position = true;
data.bars_against_count = 1;
}
else
{
data.bars_against_count++;
}
if(data.bars_against_count >= BarsToWait)
{
ClosePosition(data, MagicNumber);
return;
}
}
else
{
if(data.rsi_against_position)
{
data.rsi_against_position = false;
data.bars_against_count = 0;
}
if(data.rsi_current >= RSI_Target_Buy)
{
ClosePosition(data, MagicNumber);
}
}
}
else if(data.current_position_type == POSITION_TYPE_SELL)
{
if(data.rsi_current > RSI_Overbought)
{
if(!data.rsi_against_position)
{
data.rsi_against_position = true;
data.bars_against_count = 1;
}
else
{
data.bars_against_count++;
}
if(data.bars_against_count >= BarsToWait)
{
ClosePosition(data, MagicNumber);
return;
}
}
else
{
if(data.rsi_against_position)
{
data.rsi_against_position = false;
data.bars_against_count = 0;
}
if(data.rsi_current <= RSI_Target_Sell)
{
ClosePosition(data, MagicNumber);
}
}
}
}
void CheckEntrySignals(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
double RSI_Oversold, double RSI_Overbought, double LotSize)
{
if(data.rsi_two_bars_ago <= RSI_Oversold && data.rsi_prev > RSI_Oversold)
{
OpenBuyPosition(data, MagicNumber, LotSize);
}
if(data.rsi_two_bars_ago >= RSI_Overbought && data.rsi_prev < RSI_Overbought)
{
OpenSellPosition(data, MagicNumber, LotSize);
}
}
//+------------------------------------------------------------------+
//| Normalize Lot Size According to Symbol Properties |
//+------------------------------------------------------------------+
double NormalizeLotSize(string symbol, double lotSize)
{
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
// Round to lot step
if(lotStep > 0)
lotSize = MathFloor(lotSize / lotStep) * lotStep;
// Apply min/max constraints
if(lotSize < minLot)
lotSize = minLot;
if(lotSize > maxLot)
lotSize = maxLot;
return lotSize;
}
void OpenBuyPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
{
if(PositionExistsByMagic(data.symbol, MagicNumber))
return;
// Normalize lot size according to symbol properties
double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
if(data.trade.Buy(normalizedLot, data.symbol, ask, 0, 0, "RSI Scalping Buy"))
{
ulong new_ticket = data.trade.ResultOrder();
if(new_ticket > 0)
{
if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
{
data.position_ticket = new_ticket;
data.position_open = true;
data.current_position_type = POSITION_TYPE_BUY;
}
}
}
}
void OpenSellPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
{
if(PositionExistsByMagic(data.symbol, MagicNumber))
return;
// Normalize lot size according to symbol properties
double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
if(data.trade.Sell(normalizedLot, data.symbol, bid, 0, 0, "RSI Scalping Sell"))
{
ulong new_ticket = data.trade.ResultOrder();
if(new_ticket > 0)
{
if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
{
data.position_ticket = new_ticket;
data.position_open = true;
data.current_position_type = POSITION_TYPE_SELL;
}
}
}
}
void ClosePosition(RSIScalpingData& data, int MagicNumber)
{
// First verify position still exists
if(!PositionExistsByMagic(data.symbol, MagicNumber))
{
// Position doesn't exist, reset tracking
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
return;
}
// Try to close by ticket first (more reliable)
bool closed = false;
if(data.position_ticket > 0)
{
if(PositionSelectByTicket(data.position_ticket))
{
// Verify it's our position
if(PositionGetString(POSITION_SYMBOL) == data.symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
closed = data.trade.PositionClose(data.position_ticket);
if(!closed)
{
Print("RSIScalping: Failed to close position by ticket ", data.position_ticket,
" - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
}
}
}
}
// If ticket method failed, try magic number method
if(!closed)
{
closed = ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
if(!closed)
{
Print("RSIScalping: Failed to close position by magic number for '", data.symbol,
"' - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
}
}
// Verify position is actually closed
if(closed)
{
// Wait a moment and verify
Sleep(50);
if(!PositionExistsByMagic(data.symbol, MagicNumber))
{
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
Print("RSIScalping: Position successfully closed for '", data.symbol, "'");
}
else
{
Print("RSIScalping: Warning - Close returned success but position still exists for '", data.symbol, "'");
// Try one more time
Sleep(100);
if(PositionExistsByMagic(data.symbol, MagicNumber))
{
ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
}
// Reset tracking anyway to prevent getting stuck
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
}
}
else
{
// Close failed, but reset tracking to prevent getting stuck
// The position might have been closed externally
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
}
}
void ProcessRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
ENUM_APPLIED_PRICE RSI_Applied_Price, double RSI_Overbought,
double RSI_Oversold, double RSI_Target_Buy, double RSI_Target_Sell,
int BarsToWait, double LotSize, int MagicNumber)
{
// Skip if not initialized (symbol not available)
if(!data.isInitialized)
return;
data.symbol = symbol; // Update symbol in case it changed
if(Bars(data.symbol, TimeFrame) < RSI_Period + 2)
return;
datetime current_bar_time = iTime(data.symbol, TimeFrame, 0);
if(current_bar_time == data.last_bar_time)
return;
data.last_bar_time = current_bar_time;
if(!UpdateRSI(data))
return;
CheckExistingPosition(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought,
RSI_Target_Buy, RSI_Target_Sell, BarsToWait);
if(!data.position_open && !PositionExistsByMagic(data.symbol, MagicNumber))
{
CheckEntrySignals(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought, LotSize);
}
}
//+------------------------------------------------------------------+
@@ -0,0 +1,736 @@
//+------------------------------------------------------------------+
//| UnitedEA.mq5 |
//| Cent ".c" symbols; InpDynamicRefDeposit MUST match ACCOUNT_ |
//| CURRENCY numbers (USC ~50k for ~$500, or USD ~500 — not mixed). |
//| Per-order max lots = broker spec 最大量 SYMBOL_VOLUME_MAX |
//| (often 1000 on *.c); EA cannot exceed it — see symbol contract. |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.10"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Indicators\Trend.mqh>
#include <Indicators\Volumes.mqh>
#include "MagicNumberHelpers.mqh"
// Lot globals must exist before strategy .mqh (Darvas uses g_DB_LotSize; EMA/RC/RM use g_ES/g_RC/g_RM)
double g_ES_LotSize;
double g_RC_LotSize;
double g_RM_LotSize;
double g_DB_LotSize;
double g_DynLotScaleLast = 1.0; // last applied scale: lots = baseLot * scale * InpLotSizeScale
double g_DynamicRefBaseline = 0.0; // when InpDynamicRefDeposit<=0, frozen ref = equity at first sizing call
// Include strategy implementations early so structs are available
#include "Strategies/DarvasBoxStrategy.mqh"
#include "Strategies/EMASlopeDistanceStrategy.mqh"
#include "Strategies/RSICrossOverReversalStrategy.mqh"
#include "Strategies/RSIMidPointHijackStrategy.mqh"
#include "Strategies/RSIScalpingStrategy.mqh"
#include "Strategies/RSIReversalAsianStrategy.mqh"
//+------------------------------------------------------------------+
//| Strategy Enable/Disable Switches |
//+------------------------------------------------------------------+
input group "=== Strategy Enable/Disable ==="
input bool EnableDarvasBox = true;
input bool EnableEMASlopeDistance = true;
input bool EnableRSICrossOverReversal = true;
input bool EnableRSIMidPointHijack = true;
input bool EnableRSIScalpingAPPL = true;
input bool EnableRSIScalpingBTCUSD = true;
input bool EnableRSIScalpingNVDA = true;
input bool EnableRSIScalpingTSLA = true;
input bool EnableRSIScalpingXAUUSD = true;
input bool EnableRSIReversalAsianEURUSD = true;
input bool EnableRSIReversalAsianAUDUSD = true;
//+------------------------------------------------------------------+
//| Dynamic lot sizing — 默认「按比例」:lots = base × (equity/ref) × lotScale |
//| 可选幂曲线:lots = base × (equity/ref)^exp × lotScale(旧行为) |
//| min/max 约束的是「比例系数」不是手数本身;max<=0 表示比例系数上不封顶 |
//| |
//| USC (deposit currency) vs "lot size": |
//| • Equity/ref for the multiplier are BOTH in account currency |
//| (USC). Same units → ratio is correct; no ×100 on the ratio. |
//| • Strategy base lots (e.g. DB_BaseLotSize) are ORDER VOLUME in |
//| lots, not "USC lots". Broker SYMBOL_VOLUME_* / contract define |
//| how much margin and P/L appear in USC. |
//| • Do not multiply lot inputs by 100 only because balance is USC. |
//| If DD too high: raise InpDynamicRefDeposit and/or lower base lots.|
//| If balance is USD ~500 but ref is ~50k300k: mult→floor, lots→0.01.|
//| Ref<=0: 挂上时余额/净值为参考,之后手数随净值相对该基准的比例变化。 |
//| 单笔上限:品种规格里的「最大量」(SYMBOL_VOLUME_MAX),非 EA 参数。 |
//| InpMaxLotsPerOrderEA 再截一刀,防止动态+scale 顶满 1000 爆仓。 |
//+------------------------------------------------------------------+
enum ENUM_LOT_SCALE_CURVE
{
LOT_CURVE_PROPORTIONAL = 0, // 按比例:scale = 净值/参考(线性)
LOT_CURVE_POWER = 1 // 幂:scale = (净值/参考)^exp
};
input group "=== Dynamic lot sizing (动态手数) ==="
input bool InpDynamicLotEnable = true; // Enable balance/equity-based scaling
input ENUM_LOT_SCALE_CURVE InpDynamicLotCurve = LOT_CURVE_PROPORTIONAL; // 默认按比例;幂曲线=旧 (equity/ref)^exp
input double InpDynamicRefDeposit = 0.0; // <=0: auto — ref=挂上时净值/余额(与测试器初始一致则手数随盈利涨); >0 手动参考金
input bool InpDynamicRefEqualsEquity = false; // true: 比例系数固定为 1(只用基础手×lotScale)
input double InpDynamicExponent = 1.22; // 仅 LOT_CURVE_POWER 时:(净值/参考) 的指数
input double InpDynamicMinMult = 0.0; // 比例系数下限;<=0 不抬(按比例时净值<参考会缩小手数)
input double InpDynamicMaxMult = 20.0; // 比例系数上限;<=0 不封顶(高风险)
input bool InpDynamicUseEquity = true; // true=ACCOUNT_EQUITY, false=ACCOUNT_BALANCE
input double InpDynamicStockLotCap = 0.0; // Max lots after scale (0=off); raise if InpLotSizeScale is large
input double InpLotSizeScale = 1.0; // 全局手数倍率; 曾用100易过大,默认1按需再加
input double InpMaxLotsPerOrder = 20.0; // 单笔最大手数(0=仅券商SYMBOL_VOLUME_MAX); 保守可设5~10
//+------------------------------------------------------------------+
//| Strategy 1: DarvasBoxXAUUSD (cent symbol) |
//+------------------------------------------------------------------+
input group "=== DarvasBox Strategy ==="
input string DB_Symbol = "XAUUSD.c";
input int DB_BoxPeriod = 165;
input double DB_BoxDeviation = 30000; // Increased to allow larger ranges (was 25140)
input int DB_VolumeThreshold = 0; // Set to 0 to disable volume threshold check. Volume data from indicator used instead.
input double DB_StopLoss = 1665;
input double DB_TakeProfit = 3685;
input bool DB_EnableLogging = false;
input color DB_BoxColor = clrBlue;
input int DB_BoxWidth = 1;
input ENUM_TIMEFRAMES DB_TrendTimeframe = PERIOD_H2;
input int DB_MA_Period = 125;
input ENUM_MA_METHOD DB_MA_Method = MODE_EMA;
input ENUM_APPLIED_PRICE DB_MA_Price = PRICE_WEIGHTED;
input double DB_TrendThreshold = 4.94;
input int DB_VolumeMA_Period = 110;
input double DB_VolumeThresholdMultiplier = 1.5;
input int DB_MagicNumber = 135790;
input double DB_BaseLotSize = 0.02; // Base lot at InpDynamicRefDeposit (Darvas)
//+------------------------------------------------------------------+
//| Strategy 2: EMASlopeDistanceCocktailXAUUSD |
//| Cent: gold is usually "XAUUSD.c" (verify in Market Watch). |
//+------------------------------------------------------------------+
input group "=== EMA Slope Distance Strategy ==="
input string ES_Symbol = "XAUUSD.c";
input int ES_EMA_Periode = 46;
input double ES_PreisSchwelle = 600.0;
input double ES_SteigungSchwelle = 80.0;
input int ES_ÜberwachungTimeout = 800;
input double ES_TrailingStop = 250.0;
input double ES_LotGröße = 0.05;
input int ES_MagicNumber = 12350;
input bool ES_UseSpreadAdjustment = true;
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
input bool ES_UseBarData = true;
input int ES_MaxTradesPerCrossover = 9;
input int ES_ProfitCheckBars = 18;
input bool ES_CloseUnprofitableTrades = true;
//+------------------------------------------------------------------+
//| Strategy 3: RSICrossOverReversalXAUUSD |
//| Cent: use "XAUUSD.c" if that is what the broker lists. |
//+------------------------------------------------------------------+
input group "=== RSI CrossOver Reversal Strategy ==="
input string RC_Symbol = "XAUUSD.c";
input int RC_MagicNumber = 7;
input int RC_rsiPeriod = 19;
input int RC_overboughtLevel = 93;
input int RC_oversoldLevel = 22;
input double RC_entryRSIBuySpread = 0;
input double RC_entryRSISellSpread = 0;
input double RC_lotSize = 0.02;
input int RC_slippage = 3;
input int RC_cooldownSeconds = 209;
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
input ENUM_TIMEFRAMES RC_TimeFrame2 = PERIOD_M1;
input ENUM_TIMEFRAMES RC_BarTimeFrame = PERIOD_M12;
input int RC_emaPeriod = 140;
input double RC_emaSlopeThreshold = 105;
input double RC_exitBuyRSI = 86;
input double RC_exitSellRSI = 10;
input double RC_TrailingStop = 295;
input double RC_emaDistanceThreshold = 165;
input int RC_tradingHourOneBegin = 24;
input int RC_tradingHourOneEnd = 22;
input int RC_tradingHourTwoBegin = 6;
input int RC_tradingHourTwoEnd = 19;
input bool RC_Sunday = false;
input bool RC_Monday = false;
input bool RC_Tuesday = true;
input bool RC_Wednesday = true;
input bool RC_Thursday = true;
input bool RC_Friday = false;
input bool RC_Saturday = false;
//+------------------------------------------------------------------+
//| Strategy 4: RSIMidPointHijackXAUUSD |
//| Cent: use "XAUUSD.c" if that is what the broker lists. |
//+------------------------------------------------------------------+
input group "=== RSI MidPoint Hijack Strategy ==="
input string RM_Symbol = "XAUUSD.c";
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
input double RM_InpLotSize = 0.03;
input int RM_InpMagicNumberRSIFollow = 1001;
input int RM_InpMagicNumberRSIReverse = 1002;
input int RM_InpMagicNumberEMACross = 1003;
input bool RM_InpEnableRSIFollow = true;
input bool RM_InpEnableRSIReverse = true;
input bool RM_InpEnableEMACross = true;
input bool RM_InpEnableStrategyLock = false;
input double RM_InpLockProfitThreshold = 0.0;
input bool RM_InpCloseOppositeTrades = false;
input int RM_InpRSIPeriod = 32;
input int RM_InpRSIOverbought = 78;
input int RM_InpRSIOversold = 46;
input int RM_InpRSIExitLevel = 44;
input int RM_InpRSIFollowStartHour = 23;
input int RM_InpRSIFollowEndHour = 8;
input bool RM_InpRSIFollowCloseOutsideHours = false;
input int RM_InpRSIReversePeriod = 59;
input int RM_InpRSIReverseOverbought = 51;
input int RM_InpRSIReverseOversold = 49;
input int RM_InpRSIReverseCrossLevel = 53;
input int RM_InpRSIReverseExitLevel = 48;
input int RM_InpRSIReverseStartHour = 7;
input int RM_InpRSIReverseEndHour = 13;
input bool RM_InpRSIReverseCloseOutsideHours = false;
input int RM_InpRSIReverseCooldownBars = 15;
input bool RM_InpRSIReverseCooldownOnLoss = true;
input int RM_InpEMAPeriod = 120;
input int RM_InpEMACrossStartHour = 8;
input int RM_InpEMACrossEndHour = 14;
input bool RM_InpEMACrossCloseOutsideHours = true;
input bool RM_InpUseEMADistanceEntry = true;
input double RM_InpEMADistancePips = 160.0;
input int RM_InpEMADistancePeriod = 26;
//+------------------------------------------------------------------+
//| Strategy 5-10: RSI Scalping Strategies |
//| Each RSI Scalping strategy trades on its own symbol: |
//| - APPL: Apple stock (AAPL) |
//| - BTCUSD: Bitcoin/USD |
//| - NVDA: NVIDIA stock |
//| - TSLA: Tesla stock |
//| - XAUUSD: Gold/USD |
//| |
//| USC cent: many symbols end with ".c" — use Market Watch names. |
//| Stocks may be "AAPL.US.c" or unchanged; verify before live. |
//+------------------------------------------------------------------+
input group "=== RSI Scalping APPL (AAPL) - cent ==="
input string RS_APPL_Symbol = "AAPL.US.c"; // If missing, try AAPL.US / NASDAQ:AAPL / AAPL
input ENUM_TIMEFRAMES RS_APPL_TimeFrame = PERIOD_M10;
input int RS_APPL_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_APPL_RSI_Applied_Price = PRICE_CLOSE;
input double RS_APPL_RSI_Overbought = 80;
input double RS_APPL_RSI_Oversold = 78;
input double RS_APPL_RSI_Target_Buy = 94;
input double RS_APPL_RSI_Target_Sell = 44;
input int RS_APPL_BarsToWait = 7;
input double RS_APPL_LotSize = 38;
input int RS_APPL_MagicNumber = 20001;
input int RS_APPL_Slippage = 3;
input group "=== RSI Scalping BTCUSD ==="
input string RS_BTCUSD_Symbol = "BTCUSD.c"; // If missing, try BTCUSD or BTC/USD
input ENUM_TIMEFRAMES RS_BTCUSD_TimeFrame = PERIOD_H1;
input int RS_BTCUSD_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_BTCUSD_RSI_Applied_Price = PRICE_CLOSE;
input double RS_BTCUSD_RSI_Overbought = 90;
input double RS_BTCUSD_RSI_Oversold = 73;
input double RS_BTCUSD_RSI_Target_Buy = 88;
input double RS_BTCUSD_RSI_Target_Sell = 48;
input int RS_BTCUSD_BarsToWait = 6;
input double RS_BTCUSD_LotSize = 0.15;
input int RS_BTCUSD_MagicNumber = 123459123;
input int RS_BTCUSD_Slippage = 3;
input group "=== RSI Scalping NVDA - cent ==="
input string RS_NVDA_Symbol = "NVDA.US.c"; // If missing, try NVDA.US / NASDAQ:NVDA / NVDA
input ENUM_TIMEFRAMES RS_NVDA_TimeFrame = PERIOD_M15;
input int RS_NVDA_RSI_Period = 8;
input ENUM_APPLIED_PRICE RS_NVDA_RSI_Applied_Price = PRICE_CLOSE;
input double RS_NVDA_RSI_Overbought = 36;
input double RS_NVDA_RSI_Oversold = 38;
input double RS_NVDA_RSI_Target_Buy = 90;
input double RS_NVDA_RSI_Target_Sell = 70;
input int RS_NVDA_BarsToWait = 5;
input double RS_NVDA_LotSize = 75;
input int RS_NVDA_MagicNumber = 20003;
input int RS_NVDA_Slippage = 3;
input group "=== RSI Scalping TSLA - cent ==="
input string RS_TSLA_Symbol = "TSLA.US.c"; // If missing, try TSLA.US / NASDAQ:TSLA / TSLA
input ENUM_TIMEFRAMES RS_TSLA_TimeFrame = PERIOD_H1;
input int RS_TSLA_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_TSLA_RSI_Applied_Price = PRICE_CLOSE;
input double RS_TSLA_RSI_Overbought = 54;
input double RS_TSLA_RSI_Oversold = 73;
input double RS_TSLA_RSI_Target_Buy = 87;
input double RS_TSLA_RSI_Target_Sell = 33;
input int RS_TSLA_BarsToWait = 1;
input double RS_TSLA_LotSize = 75;
input int RS_TSLA_MagicNumber = 125421321;
input int RS_TSLA_Slippage = 3;
input group "=== RSI Scalping XAUUSD ==="
input string RS_XAUUSD_Symbol = "XAUUSD.c";
input ENUM_TIMEFRAMES RS_XAUUSD_TimeFrame = PERIOD_H1;
input int RS_XAUUSD_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_XAUUSD_RSI_Applied_Price = PRICE_CLOSE;
input double RS_XAUUSD_RSI_Overbought = 71;
input double RS_XAUUSD_RSI_Oversold = 57;
input double RS_XAUUSD_RSI_Target_Buy = 80;
input double RS_XAUUSD_RSI_Target_Sell = 57;
input int RS_XAUUSD_BarsToWait = 4;
input double RS_XAUUSD_LotSize = 0.15;
input int RS_XAUUSD_MagicNumber = 129102315;
input int RS_XAUUSD_Slippage = 3;
//+------------------------------------------------------------------+
//| Strategy 11-12: RSI Reversal Asian Strategies |
//| Each RSI Reversal Asian strategy trades on its own symbol: |
//| - EURUSD: Euro/USD |
//| - AUDUSD: Australian Dollar/USD |
//+------------------------------------------------------------------+
input group "=== RSI Reversal Asian EURUSD ==="
input string RRA_EURUSD_Symbol = "EURUSD.c";
input int RRA_EURUSD_RSIPeriod = 28;
input double RRA_EURUSD_OverboughtLevel = 60;
input double RRA_EURUSD_OversoldLevel = 8;
input int RRA_EURUSD_TakeProfitPips = 175;
input int RRA_EURUSD_StopLossPips = 5;
input double RRA_EURUSD_MaxLotSize = 0.15;
input int RRA_EURUSD_MaxSpread = 1000;
input int RRA_EURUSD_MaxDuration = 270;
input bool RRA_EURUSD_UseStopLoss = false;
input bool RRA_EURUSD_UseTakeProfit = false;
input bool RRA_EURUSD_UseRSIExit = true;
input double RRA_EURUSD_RSIExitLevel = 55;
input bool RRA_EURUSD_CloseOutsideSession = false;
input ENUM_TIMEFRAMES RRA_EURUSD_TimeFrame = PERIOD_M15;
input int RRA_EURUSD_MagicNumber = 30001;
input int RRA_EURUSD_Slippage = 3;
input group "=== RSI Reversal Asian AUDUSD ==="
input string RRA_AUDUSD_Symbol = "AUDUSD.c";
input int RRA_AUDUSD_RSIPeriod = 28;
input double RRA_AUDUSD_OverboughtLevel = 68;
input double RRA_AUDUSD_OversoldLevel = 30;
input int RRA_AUDUSD_TakeProfitPips = 175;
input int RRA_AUDUSD_StopLossPips = 5;
input double RRA_AUDUSD_MaxLotSize = 0.3;
input int RRA_AUDUSD_MaxSpread = 1000;
input int RRA_AUDUSD_MaxDuration = 340;
input bool RRA_AUDUSD_UseStopLoss = false;
input bool RRA_AUDUSD_UseTakeProfit = false;
input bool RRA_AUDUSD_UseRSIExit = true;
input double RRA_AUDUSD_RSIExitLevel = 48;
input bool RRA_AUDUSD_CloseOutsideSession = true;
input ENUM_TIMEFRAMES RRA_AUDUSD_TimeFrame = PERIOD_M15;
input int RRA_AUDUSD_MagicNumber = 30002;
input int RRA_AUDUSD_Slippage = 3;
//+------------------------------------------------------------------+
//| Global Variables - DarvasBox |
//+------------------------------------------------------------------+
struct DarvasBoxData {
string symbol;
bool isInitialized;
double boxHigh;
double boxLow;
bool boxFormed;
datetime lastBoxTime;
string boxName;
double minStopLevel;
double point;
CTrade trade;
int maHandle;
int volumeHandle;
datetime lastBarTime;
};
//+------------------------------------------------------------------+
//| Global Variables - EMA Slope Distance |
//+------------------------------------------------------------------+
struct EMASlopeData {
string symbol;
bool isInitialized;
int ema_handle;
double ema_array[];
datetime letzte_überwachung_zeit;
bool überwachung_aktiv;
bool preis_trigger_aktiv;
bool steigung_trigger_aktiv;
int ticket;
CTrade trade;
int trades_in_current_crossover;
bool crossover_detected;
datetime trade_open_time;
datetime last_bar_time;
};
//+------------------------------------------------------------------+
//| Global Variables - RSI CrossOver Reversal |
//+------------------------------------------------------------------+
struct RSICrossOverData {
string symbol;
bool isInitialized;
int rsiHandle;
int emaHandle;
double previousRSIDef;
CTrade trade;
datetime lastTradeTime;
datetime bartime;
bool WeekDays[7];
datetime lastBarTime;
};
//+------------------------------------------------------------------+
//| Global Variables - RSI MidPoint Hijack |
//+------------------------------------------------------------------+
struct RSIMidPointData {
string symbol;
bool isInitialized;
int rsiHandle;
int rsiReverseHandle;
int emaHandle;
bool rsiOverbought;
bool rsiOversold;
bool rsiReverseOverbought;
bool rsiReverseOversold;
CTrade trade;
CPositionInfo positionInfo;
bool emaCrossBuySignal;
bool emaCrossSellSignal;
int emaCrossSignalBar;
datetime lastBarTime;
datetime rsiReverseLastCloseTime;
bool rsiReverseInCooldown;
double lastBarRSI;
double lastBarRSIReverse;
double lastBarEMA;
double lastBarClose;
double lastBarEMAPrev;
double lastBarClosePrev;
};
//+------------------------------------------------------------------+
//| Global Strategy Instances |
//+------------------------------------------------------------------+
DarvasBoxData dbData;
EMASlopeData esData;
RSICrossOverData rcData;
RSIMidPointData rmData;
RSIScalpingData rsAPPLData;
RSIScalpingData rsBTCUSDData;
RSIScalpingData rsNVDAData;
RSIScalpingData rsTSLAData;
RSIScalpingData rsXAUUSDData;
//+------------------------------------------------------------------+
//| Global Variables - RSI Reversal Asian |
//+------------------------------------------------------------------+
RSIReversalAsianData rraEURUSDData;
RSIReversalAsianData rraAUDUSDData;
//+------------------------------------------------------------------+
//| Dynamic lot helpers |
//+------------------------------------------------------------------+
double DynClamp(const double v, const double lo, const double hi)
{
return MathMax(lo, MathMin(hi, v));
}
// Clamp scale factor (proportion or pow result). max<=0 = no upper clamp.
double ApplyDynamicScaleClamp(const double scaleRaw)
{
double s = scaleRaw;
if(InpDynamicMinMult > 0.0)
s = MathMax(s, InpDynamicMinMult);
if(InpDynamicMaxMult > 0.0)
s = MathMin(s, InpDynamicMaxMult);
return s;
}
// Reference for (equity/ref)^exp: manual deposit, or first-seen balance when input <= 0
double GetDynamicRefForRatio()
{
if(InpDynamicRefDeposit > 0.0)
return MathMax(InpDynamicRefDeposit, 1.0);
double capNow = InpDynamicUseEquity ? AccountInfoDouble(ACCOUNT_EQUITY) : AccountInfoDouble(ACCOUNT_BALANCE);
if(g_DynamicRefBaseline <= 0.0)
g_DynamicRefBaseline = MathMax(capNow, 1.0);
return MathMax(g_DynamicRefBaseline, 1.0);
}
// Broker hard cap: 最大量 = SYMBOL_VOLUME_MAX (e.g. 1000 on EURUSD.c / XAUUSD.c)
double NormalizeVolumeForSymbol(const string symbol, double lots)
{
double minL = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double maxL = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if(step > 0.0)
lots = MathFloor(lots / step + 1e-12) * step;
if(lots < minL) lots = minL;
if(lots > maxL) lots = maxL;
return lots;
}
// Apply EA risk cap before broker min/step/max (InpMaxLotsPerOrder 0 = disabled)
double NormalizeVolumeForSymbolWithEACap(const string symbol, double lots)
{
if(InpMaxLotsPerOrder > 0.0)
lots = MathMin(lots, InpMaxLotsPerOrder);
return NormalizeVolumeForSymbol(symbol, lots);
}
// Scale factor for lots: baseLot * scale * InpLotSizeScale (then caps)
double GetDynamicLotScaleFactor()
{
if(!InpDynamicLotEnable)
return 1.0;
if(InpDynamicRefEqualsEquity)
return ApplyDynamicScaleClamp(1.0);
double cap = InpDynamicUseEquity ? AccountInfoDouble(ACCOUNT_EQUITY) : AccountInfoDouble(ACCOUNT_BALANCE);
double refv = GetDynamicRefForRatio();
if(cap <= 0.0)
cap = refv;
double ratio = cap / refv;
if(ratio <= 0.0)
ratio = 1.0;
double scaleRaw = ratio;
if(InpDynamicLotCurve == LOT_CURVE_POWER)
scaleRaw = MathPow(ratio, InpDynamicExponent);
return ApplyDynamicScaleClamp(scaleRaw);
}
// baseLot = size at reference deposit; optionalCap 0 = no extra ceiling (broker min/max still apply)
double DynamicLotForSymbol(const string symbol, const double baseLot, const double optionalCap = 0.0)
{
double scale = GetDynamicLotScaleFactor();
g_DynLotScaleLast = scale;
double sc = (InpLotSizeScale > 0.0 ? InpLotSizeScale : 1.0);
double v = baseLot * scale * sc;
if(optionalCap > 0.0 && v > optionalCap)
v = optionalCap;
return NormalizeVolumeForSymbolWithEACap(symbol, v);
}
void RefreshDynamicStrategyLots()
{
double sc = (InpLotSizeScale > 0.0 ? InpLotSizeScale : 1.0);
if(!InpDynamicLotEnable)
{
g_ES_LotSize = NormalizeVolumeForSymbolWithEACap(ES_Symbol, ES_LotGröße * sc);
g_RC_LotSize = NormalizeVolumeForSymbolWithEACap(RC_Symbol, RC_lotSize * sc);
g_RM_LotSize = NormalizeVolumeForSymbolWithEACap(RM_Symbol, RM_InpLotSize * sc);
g_DB_LotSize = NormalizeVolumeForSymbolWithEACap(DB_Symbol, DB_BaseLotSize * sc);
g_DynLotScaleLast = 1.0;
return;
}
g_ES_LotSize = DynamicLotForSymbol(ES_Symbol, ES_LotGröße);
g_RC_LotSize = DynamicLotForSymbol(RC_Symbol, RC_lotSize);
g_RM_LotSize = DynamicLotForSymbol(RM_Symbol, RM_InpLotSize);
g_DB_LotSize = DynamicLotForSymbol(DB_Symbol, DB_BaseLotSize);
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
int initResult = INIT_SUCCEEDED;
g_DynamicRefBaseline = 0.0;
RefreshDynamicStrategyLots();
string acctCur = AccountInfoString(ACCOUNT_CURRENCY);
double eq0 = AccountInfoDouble(ACCOUNT_EQUITY);
if(InpDynamicLotEnable && !InpDynamicRefEqualsEquity && InpDynamicRefDeposit > 1000.0 && eq0 > 0.0
&& eq0 <= InpDynamicRefDeposit / 25.0)
Print("United EA: equity ", DoubleToString(eq0, 2), " ", acctCur, " vs ref ", InpDynamicRefDeposit,
" — dynamic mult is tiny; set InpDynamicRefDeposit to your balance in ", acctCur,
" (e.g. 500 for USD) or enable InpDynamicRefEqualsEquity. Else lots stay at broker minimum.");
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
if(EnableDarvasBox)
if(!InitDarvasBox(DB_Symbol))
Print("Warning: DarvasBox strategy failed to initialize for symbol '", DB_Symbol, "'");
if(EnableEMASlopeDistance)
if(!InitEMASlopeDistance(ES_Symbol))
Print("Warning: EMASlopeDistance strategy failed to initialize for symbol '", ES_Symbol, "'");
if(EnableRSICrossOverReversal)
if(!InitRSICrossOverReversal(RC_Symbol))
Print("Warning: RSICrossOverReversal strategy failed to initialize for symbol '", RC_Symbol, "'");
if(EnableRSIMidPointHijack)
if(!InitRSIMidPointHijack(RM_Symbol))
Print("Warning: RSIMidPointHijack strategy failed to initialize for symbol '", RM_Symbol, "'");
// Initialize RSI Scalping strategies - don't fail entire EA if symbol unavailable
if(EnableRSIScalpingAPPL)
InitRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price, RS_APPL_MagicNumber, RS_APPL_Slippage);
if(EnableRSIScalpingBTCUSD)
InitRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price, RS_BTCUSD_MagicNumber, RS_BTCUSD_Slippage);
if(EnableRSIScalpingNVDA)
InitRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price, RS_NVDA_MagicNumber, RS_NVDA_Slippage);
if(EnableRSIScalpingTSLA)
InitRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price, RS_TSLA_MagicNumber, RS_TSLA_Slippage);
if(EnableRSIScalpingXAUUSD)
InitRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price, RS_XAUUSD_MagicNumber, RS_XAUUSD_Slippage);
// Initialize RSI Reversal Asian strategies
if(EnableRSIReversalAsianEURUSD)
if(!InitRSIReversalAsian(rraEURUSDData, RRA_EURUSD_Symbol, RRA_EURUSD_RSIPeriod, RRA_EURUSD_OverboughtLevel, RRA_EURUSD_OversoldLevel,
RRA_EURUSD_TakeProfitPips, RRA_EURUSD_StopLossPips, RRA_EURUSD_MaxLotSize,
RRA_EURUSD_MaxSpread, RRA_EURUSD_MaxDuration, RRA_EURUSD_UseStopLoss,
RRA_EURUSD_UseTakeProfit, RRA_EURUSD_UseRSIExit, RRA_EURUSD_RSIExitLevel,
RRA_EURUSD_CloseOutsideSession, RRA_EURUSD_TimeFrame, RRA_EURUSD_MagicNumber, RRA_EURUSD_Slippage))
Print("Warning: RSIReversalAsianEURUSD strategy failed to initialize for symbol '", RRA_EURUSD_Symbol, "'");
if(EnableRSIReversalAsianAUDUSD)
if(!InitRSIReversalAsian(rraAUDUSDData, RRA_AUDUSD_Symbol, RRA_AUDUSD_RSIPeriod, RRA_AUDUSD_OverboughtLevel, RRA_AUDUSD_OversoldLevel,
RRA_AUDUSD_TakeProfitPips, RRA_AUDUSD_StopLossPips, RRA_AUDUSD_MaxLotSize,
RRA_AUDUSD_MaxSpread, RRA_AUDUSD_MaxDuration, RRA_AUDUSD_UseStopLoss,
RRA_AUDUSD_UseTakeProfit, RRA_AUDUSD_UseRSIExit, RRA_AUDUSD_RSIExitLevel,
RRA_AUDUSD_CloseOutsideSession, RRA_AUDUSD_TimeFrame, RRA_AUDUSD_MagicNumber, RRA_AUDUSD_Slippage))
Print("Warning: RSIReversalAsianAUDUSD strategy failed to initialize for symbol '", RRA_AUDUSD_Symbol, "'");
double refEffInit = GetDynamicRefForRatio();
double capInit = InpDynamicUseEquity ? eq0 : AccountInfoDouble(ACCOUNT_BALANCE);
if(capInit <= 0.0)
capInit = refEffInit;
double ratioInit = capInit / refEffInit;
double powInit = MathPow(ratioInit, InpDynamicExponent);
string curveStr = (InpDynamicLotCurve == LOT_CURVE_POWER ? "POWER" : "PROP");
Print("United EA v1.10 ", acctCur, " curve=", curveStr, " equity=", DoubleToString(eq0, 2), " refEff=", DoubleToString(refEffInit, 2),
" (inpRef=", InpDynamicRefDeposit, " baseline=", DoubleToString(g_DynamicRefBaseline, 2), ") equity/ref=", DoubleToString(ratioInit, 6),
" pow^exp=", DoubleToString(powInit, 6), " scaleOut=", DoubleToString(g_DynLotScaleLast, 6),
" minS=", InpDynamicMinMult, " maxS=", InpDynamicMaxMult, " lotScale=", InpLotSizeScale, " maxLots=", InpMaxLotsPerOrder,
" lots ES=", g_ES_LotSize, " RC=", g_RC_LotSize, " RM=", g_RM_LotSize, " DB=", g_DB_LotSize);
Print("United EA initialized. Active strategies: ",
(EnableDarvasBox ? "DarvasBox " : ""),
(EnableEMASlopeDistance ? "EMASlope " : ""),
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
(EnableRSIMidPointHijack ? "RSIMidPoint " : ""),
(EnableRSIScalpingAPPL ? "RSIScalpingAPPL " : ""),
(EnableRSIScalpingBTCUSD ? "RSIScalpingBTCUSD " : ""),
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""),
(EnableRSIReversalAsianEURUSD ? "RSIReversalAsianEURUSD " : ""),
(EnableRSIReversalAsianAUDUSD ? "RSIReversalAsianAUDUSD " : ""));
return initResult;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(EnableDarvasBox)
DeinitDarvasBox();
if(EnableEMASlopeDistance)
DeinitEMASlopeDistance();
if(EnableRSICrossOverReversal)
DeinitRSICrossOverReversal();
if(EnableRSIMidPointHijack)
DeinitRSIMidPointHijack();
if(EnableRSIScalpingAPPL)
DeinitRSIScalping(rsAPPLData);
if(EnableRSIScalpingBTCUSD)
DeinitRSIScalping(rsBTCUSDData);
if(EnableRSIScalpingNVDA)
DeinitRSIScalping(rsNVDAData);
if(EnableRSIScalpingTSLA)
DeinitRSIScalping(rsTSLAData);
if(EnableRSIScalpingXAUUSD)
DeinitRSIScalping(rsXAUUSDData);
if(EnableRSIReversalAsianEURUSD)
DeinitRSIReversalAsian(rraEURUSDData);
if(EnableRSIReversalAsianAUDUSD)
DeinitRSIReversalAsian(rraAUDUSDData);
Print("United EA deinitialized. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
RefreshDynamicStrategyLots();
if(EnableDarvasBox)
ProcessDarvasBox(DB_Symbol);
if(EnableEMASlopeDistance)
ProcessEMASlopeDistance(ES_Symbol);
if(EnableRSICrossOverReversal)
ProcessRSICrossOverReversal(RC_Symbol);
if(EnableRSIMidPointHijack)
ProcessRSIMidPointHijack(RM_Symbol);
if(EnableRSIScalpingAPPL)
ProcessRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price,
RS_APPL_RSI_Overbought, RS_APPL_RSI_Oversold, RS_APPL_RSI_Target_Buy, RS_APPL_RSI_Target_Sell,
RS_APPL_BarsToWait,
DynamicLotForSymbol(RS_APPL_Symbol, RS_APPL_LotSize, InpDynamicStockLotCap),
RS_APPL_MagicNumber);
if(EnableRSIScalpingBTCUSD)
ProcessRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price,
RS_BTCUSD_RSI_Overbought, RS_BTCUSD_RSI_Oversold, RS_BTCUSD_RSI_Target_Buy, RS_BTCUSD_RSI_Target_Sell,
RS_BTCUSD_BarsToWait, DynamicLotForSymbol(RS_BTCUSD_Symbol, RS_BTCUSD_LotSize), RS_BTCUSD_MagicNumber);
if(EnableRSIScalpingNVDA)
ProcessRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price,
RS_NVDA_RSI_Overbought, RS_NVDA_RSI_Oversold, RS_NVDA_RSI_Target_Buy, RS_NVDA_RSI_Target_Sell,
RS_NVDA_BarsToWait,
DynamicLotForSymbol(RS_NVDA_Symbol, RS_NVDA_LotSize, InpDynamicStockLotCap),
RS_NVDA_MagicNumber);
if(EnableRSIScalpingTSLA)
ProcessRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price,
RS_TSLA_RSI_Overbought, RS_TSLA_RSI_Oversold, RS_TSLA_RSI_Target_Buy, RS_TSLA_RSI_Target_Sell,
RS_TSLA_BarsToWait,
DynamicLotForSymbol(RS_TSLA_Symbol, RS_TSLA_LotSize, InpDynamicStockLotCap),
RS_TSLA_MagicNumber);
if(EnableRSIScalpingXAUUSD)
ProcessRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price,
RS_XAUUSD_RSI_Overbought, RS_XAUUSD_RSI_Oversold, RS_XAUUSD_RSI_Target_Buy, RS_XAUUSD_RSI_Target_Sell,
RS_XAUUSD_BarsToWait, DynamicLotForSymbol(RS_XAUUSD_Symbol, RS_XAUUSD_LotSize), RS_XAUUSD_MagicNumber);
if(EnableRSIReversalAsianEURUSD)
ProcessRSIReversalAsian(rraEURUSDData, DynamicLotForSymbol(RRA_EURUSD_Symbol, RRA_EURUSD_MaxLotSize));
if(EnableRSIReversalAsianAUDUSD)
ProcessRSIReversalAsian(rraAUDUSDData, DynamicLotForSymbol(RRA_AUDUSD_Symbol, RRA_AUDUSD_MaxLotSize));
}
//+------------------------------------------------------------------+
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

@@ -0,0 +1,641 @@
//+------------------------------------------------------------------+
//| UnitedEA.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Indicators\Trend.mqh>
#include <Indicators\Volumes.mqh>
#include "MagicNumberHelpers.mqh"
#include "PerformanceEvaluator.mqh"
//+------------------------------------------------------------------+
//| Strategy Enable/Disable Switches |
//+------------------------------------------------------------------+
input group "=== Strategy Enable/Disable ==="
input bool EnableDarvasBox = true;
input bool EnableEMASlopeDistance = true;
input bool EnableRSICrossOverReversal = true;
input bool EnableRSIMidPointHijack = true;
input bool EnableRSIScalpingAPPL = true;
input bool EnableRSIScalpingBTCUSD = true;
input bool EnableRSIScalpingNVDA = true;
input bool EnableRSIScalpingTSLA = true;
input bool EnableRSIScalpingXAUUSD = true;
//+------------------------------------------------------------------+
//| Strategy 1: DarvasBoxXAUUSD |
//+------------------------------------------------------------------+
input group "=== DarvasBox Strategy ==="
input string DB_Symbol = "XAUUSD";
input int DB_BoxPeriod = 165;
input double DB_BoxDeviation = 30000; // Increased to allow larger ranges (was 25140)
input int DB_VolumeThreshold = 0; // Set to 0 to disable volume threshold check. Volume data from indicator used instead.
input double DB_StopLoss = 1665;
input double DB_TakeProfit = 3685;
input bool DB_EnableLogging = false;
input color DB_BoxColor = clrBlue;
input int DB_BoxWidth = 1;
input ENUM_TIMEFRAMES DB_TrendTimeframe = PERIOD_H2;
input int DB_MA_Period = 125;
input ENUM_MA_METHOD DB_MA_Method = MODE_EMA;
input ENUM_APPLIED_PRICE DB_MA_Price = PRICE_WEIGHTED;
input double DB_TrendThreshold = 4.94;
input int DB_VolumeMA_Period = 110;
input double DB_VolumeThresholdMultiplier = 1.5;
input int DB_MagicNumber = 135790;
//+------------------------------------------------------------------+
//| Strategy 2: EMASlopeDistanceCocktailXAUUSD |
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
//+------------------------------------------------------------------+
input group "=== EMA Slope Distance Strategy ==="
input string ES_Symbol = "XAUUSD";
input int ES_EMA_Periode = 46;
input double ES_PreisSchwelle = 600.0;
input double ES_SteigungSchwelle = 80.0;
input int ES_ÜberwachungTimeout = 800;
input double ES_TrailingStop = 250.0;
input double ES_LotGröße = 0.03;
input int ES_MagicNumber = 12350;
input bool ES_UseSpreadAdjustment = true;
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
input bool ES_UseBarData = true;
input int ES_MaxTradesPerCrossover = 9;
input int ES_ProfitCheckBars = 18;
input bool ES_CloseUnprofitableTrades = true;
//+------------------------------------------------------------------+
//| Strategy 3: RSICrossOverReversalXAUUSD |
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
//+------------------------------------------------------------------+
input group "=== RSI CrossOver Reversal Strategy ==="
input string RC_Symbol = "XAUUSD";
input int RC_MagicNumber = 7;
input int RC_rsiPeriod = 19;
input int RC_overboughtLevel = 93;
input int RC_oversoldLevel = 22;
input double RC_entryRSIBuySpread = 0;
input double RC_entryRSISellSpread = 0;
input double RC_lotSize = 0.01;
input int RC_slippage = 3;
input int RC_cooldownSeconds = 209;
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
input ENUM_TIMEFRAMES RC_TimeFrame2 = PERIOD_M1;
input ENUM_TIMEFRAMES RC_BarTimeFrame = PERIOD_M12;
input int RC_emaPeriod = 140;
input double RC_emaSlopeThreshold = 105;
input double RC_exitBuyRSI = 86;
input double RC_exitSellRSI = 10;
input double RC_TrailingStop = 295;
input double RC_emaDistanceThreshold = 165;
input int RC_tradingHourOneBegin = 24;
input int RC_tradingHourOneEnd = 22;
input int RC_tradingHourTwoBegin = 6;
input int RC_tradingHourTwoEnd = 19;
input bool RC_Sunday = false;
input bool RC_Monday = false;
input bool RC_Tuesday = true;
input bool RC_Wednesday = true;
input bool RC_Thursday = true;
input bool RC_Friday = false;
input bool RC_Saturday = false;
//+------------------------------------------------------------------+
//| Strategy 4: RSIMidPointHijackXAUUSD |
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
//+------------------------------------------------------------------+
input group "=== RSI MidPoint Hijack Strategy ==="
input string RM_Symbol = "XAUUSD";
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
input double RM_InpLotSize = 0.02;
input int RM_InpMagicNumberRSIFollow = 1001;
input int RM_InpMagicNumberRSIReverse = 1002;
input int RM_InpMagicNumberEMACross = 1003;
input bool RM_InpEnableRSIFollow = true;
input bool RM_InpEnableRSIReverse = true;
input bool RM_InpEnableEMACross = true;
input bool RM_InpEnableStrategyLock = false;
input double RM_InpLockProfitThreshold = 0.0;
input bool RM_InpCloseOppositeTrades = false;
input int RM_InpRSIPeriod = 32;
input int RM_InpRSIOverbought = 78;
input int RM_InpRSIOversold = 46;
input int RM_InpRSIExitLevel = 44;
input int RM_InpRSIFollowStartHour = 23;
input int RM_InpRSIFollowEndHour = 8;
input bool RM_InpRSIFollowCloseOutsideHours = false;
input int RM_InpRSIReversePeriod = 59;
input int RM_InpRSIReverseOverbought = 51;
input int RM_InpRSIReverseOversold = 49;
input int RM_InpRSIReverseCrossLevel = 53;
input int RM_InpRSIReverseExitLevel = 48;
input int RM_InpRSIReverseStartHour = 7;
input int RM_InpRSIReverseEndHour = 13;
input bool RM_InpRSIReverseCloseOutsideHours = false;
input int RM_InpRSIReverseCooldownBars = 15;
input bool RM_InpRSIReverseCooldownOnLoss = true;
input int RM_InpEMAPeriod = 120;
input int RM_InpEMACrossStartHour = 8;
input int RM_InpEMACrossEndHour = 14;
input bool RM_InpEMACrossCloseOutsideHours = true;
input bool RM_InpUseEMADistanceEntry = true;
input double RM_InpEMADistancePips = 160.0;
input int RM_InpEMADistancePeriod = 26;
//+------------------------------------------------------------------+
//| Strategy 5-10: RSI Scalping Strategies |
//| Each RSI Scalping strategy trades on its own symbol: |
//| - APPL: Apple stock (AAPL) |
//| - BTCUSD: Bitcoin/USD |
//| - NVDA: NVIDIA stock |
//| - TSLA: Tesla stock |
//| - XAUUSD: Gold/USD |
//| |
//| PEPPERSTONE US SYMBOL FORMATS: |
//| - Stocks may use: "AAPL.US", "NASDAQ:AAPL", or just "AAPL" |
//| - To find correct symbols: |
//| 1. Open Market Watch (Ctrl+M) |
//| 2. Right-click > Show All |
//| 3. Search for the stock name |
//| 4. Use the exact symbol name shown |
//+------------------------------------------------------------------+
input group "=== RSI Scalping APPL (AAPL) - Pepperstone US ==="
input string RS_APPL_Symbol = "AAPL.US"; // Try: "AAPL.US", "NASDAQ:AAPL", or "AAPL"
input ENUM_TIMEFRAMES RS_APPL_TimeFrame = PERIOD_M10;
input int RS_APPL_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_APPL_RSI_Applied_Price = PRICE_CLOSE;
input double RS_APPL_RSI_Overbought = 80;
input double RS_APPL_RSI_Oversold = 78;
input double RS_APPL_RSI_Target_Buy = 94;
input double RS_APPL_RSI_Target_Sell = 44;
input int RS_APPL_BarsToWait = 7;
input double RS_APPL_LotSize = 25;
input int RS_APPL_MagicNumber = 20001;
input int RS_APPL_Slippage = 3;
input group "=== RSI Scalping BTCUSD ==="
input string RS_BTCUSD_Symbol = "BTCUSD"; // Pepperstone may use: "BTCUSD", "BTC/USD", or "BTCUSD.c"
input ENUM_TIMEFRAMES RS_BTCUSD_TimeFrame = PERIOD_H1;
input int RS_BTCUSD_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_BTCUSD_RSI_Applied_Price = PRICE_CLOSE;
input double RS_BTCUSD_RSI_Overbought = 90;
input double RS_BTCUSD_RSI_Oversold = 73;
input double RS_BTCUSD_RSI_Target_Buy = 88;
input double RS_BTCUSD_RSI_Target_Sell = 48;
input int RS_BTCUSD_BarsToWait = 6;
input double RS_BTCUSD_LotSize = 0.1;
input int RS_BTCUSD_MagicNumber = 123459123;
input int RS_BTCUSD_Slippage = 3;
input group "=== RSI Scalping NVDA - Pepperstone US ==="
input string RS_NVDA_Symbol = "NVDA.US"; // Try: "NVDA.US", "NASDAQ:NVDA", or "NVDA"
input ENUM_TIMEFRAMES RS_NVDA_TimeFrame = PERIOD_M15;
input int RS_NVDA_RSI_Period = 8;
input ENUM_APPLIED_PRICE RS_NVDA_RSI_Applied_Price = PRICE_CLOSE;
input double RS_NVDA_RSI_Overbought = 36;
input double RS_NVDA_RSI_Oversold = 38;
input double RS_NVDA_RSI_Target_Buy = 90;
input double RS_NVDA_RSI_Target_Sell = 70;
input int RS_NVDA_BarsToWait = 5;
input double RS_NVDA_LotSize = 50;
input int RS_NVDA_MagicNumber = 20003;
input int RS_NVDA_Slippage = 3;
input group "=== RSI Scalping TSLA - Pepperstone US ==="
input string RS_TSLA_Symbol = "TSLA.US"; // Try: "TSLA.US", "NASDAQ:TSLA", or "TSLA"
input ENUM_TIMEFRAMES RS_TSLA_TimeFrame = PERIOD_H1;
input int RS_TSLA_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_TSLA_RSI_Applied_Price = PRICE_CLOSE;
input double RS_TSLA_RSI_Overbought = 54;
input double RS_TSLA_RSI_Oversold = 73;
input double RS_TSLA_RSI_Target_Buy = 87;
input double RS_TSLA_RSI_Target_Sell = 33;
input int RS_TSLA_BarsToWait = 1;
input double RS_TSLA_LotSize = 50;
input int RS_TSLA_MagicNumber = 125421321;
input int RS_TSLA_Slippage = 3;
input group "=== RSI Scalping XAUUSD ==="
input string RS_XAUUSD_Symbol = "XAUUSD";
input ENUM_TIMEFRAMES RS_XAUUSD_TimeFrame = PERIOD_H1;
input int RS_XAUUSD_RSI_Period = 14;
input ENUM_APPLIED_PRICE RS_XAUUSD_RSI_Applied_Price = PRICE_CLOSE;
input double RS_XAUUSD_RSI_Overbought = 71;
input double RS_XAUUSD_RSI_Oversold = 57;
input double RS_XAUUSD_RSI_Target_Buy = 80;
input double RS_XAUUSD_RSI_Target_Sell = 57;
input int RS_XAUUSD_BarsToWait = 4;
input double RS_XAUUSD_LotSize = 0.1;
input int RS_XAUUSD_MagicNumber = 129102315;
input int RS_XAUUSD_Slippage = 3;
//+------------------------------------------------------------------+
//| Global Variables - DarvasBox |
//+------------------------------------------------------------------+
struct DarvasBoxData {
string symbol;
bool isInitialized;
double boxHigh;
double boxLow;
bool boxFormed;
datetime lastBoxTime;
string boxName;
double minStopLevel;
double point;
CTrade trade;
int maHandle;
int volumeHandle;
datetime lastBarTime;
};
//+------------------------------------------------------------------+
//| Global Variables - EMA Slope Distance |
//+------------------------------------------------------------------+
struct EMASlopeData {
string symbol;
bool isInitialized;
int ema_handle;
double ema_array[];
datetime letzte_überwachung_zeit;
bool überwachung_aktiv;
bool preis_trigger_aktiv;
bool steigung_trigger_aktiv;
int ticket;
CTrade trade;
int trades_in_current_crossover;
bool crossover_detected;
datetime trade_open_time;
datetime last_bar_time;
};
//+------------------------------------------------------------------+
//| Global Variables - RSI CrossOver Reversal |
//+------------------------------------------------------------------+
struct RSICrossOverData {
string symbol;
bool isInitialized;
int rsiHandle;
int emaHandle;
double previousRSIDef;
CTrade trade;
datetime lastTradeTime;
datetime bartime;
bool WeekDays[7];
datetime lastBarTime;
};
//+------------------------------------------------------------------+
//| Global Variables - RSI MidPoint Hijack |
//+------------------------------------------------------------------+
struct RSIMidPointData {
string symbol;
bool isInitialized;
int rsiHandle;
int rsiReverseHandle;
int emaHandle;
bool rsiOverbought;
bool rsiOversold;
bool rsiReverseOverbought;
bool rsiReverseOversold;
CTrade trade;
CPositionInfo positionInfo;
bool emaCrossBuySignal;
bool emaCrossSellSignal;
int emaCrossSignalBar;
datetime lastBarTime;
datetime rsiReverseLastCloseTime;
bool rsiReverseInCooldown;
double lastBarRSI;
double lastBarRSIReverse;
double lastBarEMA;
double lastBarClose;
double lastBarEMAPrev;
double lastBarClosePrev;
};
//+------------------------------------------------------------------+
//| Global Variables - RSI Scalping |
//+------------------------------------------------------------------+
struct RSIScalpingData {
string symbol;
bool isInitialized;
CTrade trade;
int rsi_handle;
double rsi_buffer[];
double rsi_prev;
double rsi_current;
double rsi_two_bars_ago;
bool position_open;
ulong position_ticket;
ENUM_POSITION_TYPE current_position_type;
datetime last_bar_time;
bool rsi_against_position;
int bars_against_count;
};
//+------------------------------------------------------------------+
//| Global Strategy Instances |
//+------------------------------------------------------------------+
DarvasBoxData dbData;
EMASlopeData esData;
RSICrossOverData rcData;
RSIMidPointData rmData;
RSIScalpingData rsAPPLData;
RSIScalpingData rsBTCUSDData;
RSIScalpingData rsNVDAData;
RSIScalpingData rsTSLAData;
RSIScalpingData rsXAUUSDData;
//+------------------------------------------------------------------+
//| Global Variables for Dynamic Lot Sizes |
//+------------------------------------------------------------------+
// All strategies start with minimum lot size for safety (will be adjusted by performance evaluator)
double g_DB_LotSize = 0.01; // DarvasBox uses fixed lot size
double g_ES_LotSize = 0.01; // EMA Slope Distance - start with minimum
double g_RC_LotSize = 0.01; // RSI CrossOver Reversal - start with minimum
double g_RM_LotSize = 0.01; // RSI MidPoint Hijack - start with minimum
double g_RS_APPL_LotSize = 5.0; // Stock - start with stock minimum (5.0)
double g_RS_BTCUSD_LotSize = 0.01; // Crypto - start with forex minimum (0.01)
double g_RS_NVDA_LotSize = 5.0; // Stock - start with stock minimum (5.0)
double g_RS_TSLA_LotSize = 5.0; // Stock - start with stock minimum (5.0)
double g_RS_XAUUSD_LotSize = 0.01; // Forex - start with forex minimum (0.01)
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
int initResult = INIT_SUCCEEDED;
// Initialize Performance Evaluator
InitPerformanceTracking();
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
if(EnableDarvasBox)
{
if(!InitDarvasBox(DB_Symbol))
Print("Warning: DarvasBox strategy failed to initialize for symbol '", DB_Symbol, "'");
else
RegisterStrategy("DarvasBox", DB_MagicNumber, 0.01, DB_Symbol); // Fixed lot size
}
if(EnableEMASlopeDistance)
{
if(!InitEMASlopeDistance(ES_Symbol))
Print("Warning: EMASlopeDistance strategy failed to initialize for symbol '", ES_Symbol, "'");
else
{
RegisterStrategy("EMASlopeDistance", ES_MagicNumber, ES_LotGröße, ES_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(ES_Symbol);
g_ES_LotSize = minLot;
}
}
if(EnableRSICrossOverReversal)
{
if(!InitRSICrossOverReversal(RC_Symbol))
Print("Warning: RSICrossOverReversal strategy failed to initialize for symbol '", RC_Symbol, "'");
else
{
RegisterStrategy("RSICrossOverReversal", RC_MagicNumber, RC_lotSize, RC_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RC_Symbol);
g_RC_LotSize = minLot;
}
}
if(EnableRSIMidPointHijack)
{
if(!InitRSIMidPointHijack(RM_Symbol))
Print("Warning: RSIMidPointHijack strategy failed to initialize for symbol '", RM_Symbol, "'");
else
{
RegisterStrategy("RSIMidPointHijack", RM_InpMagicNumberRSIFollow, RM_InpLotSize, RM_Symbol);
RegisterStrategy("RSIMidPointHijack_Reverse", RM_InpMagicNumberRSIReverse, RM_InpLotSize, RM_Symbol);
RegisterStrategy("RSIMidPointHijack_EMACross", RM_InpMagicNumberEMACross, RM_InpLotSize, RM_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RM_Symbol);
g_RM_LotSize = minLot;
}
}
// Initialize RSI Scalping strategies - don't fail entire EA if symbol unavailable
if(EnableRSIScalpingAPPL)
{
InitRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price, RS_APPL_MagicNumber, RS_APPL_Slippage);
RegisterStrategy("RSIScalpingAPPL", RS_APPL_MagicNumber, RS_APPL_LotSize, RS_APPL_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_APPL_Symbol);
g_RS_APPL_LotSize = minLot;
}
if(EnableRSIScalpingBTCUSD)
{
InitRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price, RS_BTCUSD_MagicNumber, RS_BTCUSD_Slippage);
RegisterStrategy("RSIScalpingBTCUSD", RS_BTCUSD_MagicNumber, RS_BTCUSD_LotSize, RS_BTCUSD_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_BTCUSD_Symbol);
g_RS_BTCUSD_LotSize = minLot;
}
if(EnableRSIScalpingNVDA)
{
InitRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price, RS_NVDA_MagicNumber, RS_NVDA_Slippage);
RegisterStrategy("RSIScalpingNVDA", RS_NVDA_MagicNumber, RS_NVDA_LotSize, RS_NVDA_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_NVDA_Symbol);
g_RS_NVDA_LotSize = minLot;
}
if(EnableRSIScalpingTSLA)
{
InitRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price, RS_TSLA_MagicNumber, RS_TSLA_Slippage);
RegisterStrategy("RSIScalpingTSLA", RS_TSLA_MagicNumber, RS_TSLA_LotSize, RS_TSLA_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_TSLA_Symbol);
g_RS_TSLA_LotSize = minLot;
}
if(EnableRSIScalpingXAUUSD)
{
InitRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price, RS_XAUUSD_MagicNumber, RS_XAUUSD_Slippage);
RegisterStrategy("RSIScalpingXAUUSD", RS_XAUUSD_MagicNumber, RS_XAUUSD_LotSize, RS_XAUUSD_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_XAUUSD_Symbol);
g_RS_XAUUSD_LotSize = minLot;
}
// Load adjusted lot sizes from performance evaluator
if(PE_EnableAutoAdjustment)
{
double adjustedLot;
adjustedLot = GetStrategyLotSize("EMASlopeDistance", ES_MagicNumber);
if(adjustedLot > 0) g_ES_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSICrossOverReversal", RC_MagicNumber);
if(adjustedLot > 0) g_RC_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIMidPointHijack", RM_InpMagicNumberRSIFollow);
if(adjustedLot > 0) g_RM_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingAPPL", RS_APPL_MagicNumber);
if(adjustedLot > 0) g_RS_APPL_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingBTCUSD", RS_BTCUSD_MagicNumber);
if(adjustedLot > 0) g_RS_BTCUSD_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingNVDA", RS_NVDA_MagicNumber);
if(adjustedLot > 0) g_RS_NVDA_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingTSLA", RS_TSLA_MagicNumber);
if(adjustedLot > 0) g_RS_TSLA_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingXAUUSD", RS_XAUUSD_MagicNumber);
if(adjustedLot > 0) g_RS_XAUUSD_LotSize = adjustedLot;
}
Print("United EA initialized. Active strategies: ",
(EnableDarvasBox ? "DarvasBox " : ""),
(EnableEMASlopeDistance ? "EMASlope " : ""),
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
(EnableRSIMidPointHijack ? "RSIMidPoint " : ""),
(EnableRSIScalpingAPPL ? "RSIScalpingAPPL " : ""),
(EnableRSIScalpingBTCUSD ? "RSIScalpingBTCUSD " : ""),
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""));
if(PE_EnableLogging)
Print(GetPerformanceSummary());
return initResult;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(EnableDarvasBox)
DeinitDarvasBox();
if(EnableEMASlopeDistance)
DeinitEMASlopeDistance();
if(EnableRSICrossOverReversal)
DeinitRSICrossOverReversal();
if(EnableRSIMidPointHijack)
DeinitRSIMidPointHijack();
if(EnableRSIScalpingAPPL)
DeinitRSIScalping(rsAPPLData);
if(EnableRSIScalpingBTCUSD)
DeinitRSIScalping(rsBTCUSDData);
if(EnableRSIScalpingNVDA)
DeinitRSIScalping(rsNVDAData);
if(EnableRSIScalpingTSLA)
DeinitRSIScalping(rsTSLAData);
if(EnableRSIScalpingXAUUSD)
DeinitRSIScalping(rsXAUUSDData);
Print("United EA deinitialized. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Process performance evaluation (checks for quarter end and adjusts lot sizes)
ProcessPerformanceEvaluation();
// Update lot sizes from performance evaluator if auto-adjustment is enabled
if(PE_EnableAutoAdjustment)
{
double adjustedLot;
adjustedLot = GetStrategyLotSize("EMASlopeDistance", ES_MagicNumber);
if(adjustedLot > 0) g_ES_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSICrossOverReversal", RC_MagicNumber);
if(adjustedLot > 0) g_RC_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIMidPointHijack", RM_InpMagicNumberRSIFollow);
if(adjustedLot > 0) g_RM_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingAPPL", RS_APPL_MagicNumber);
if(adjustedLot > 0) g_RS_APPL_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingBTCUSD", RS_BTCUSD_MagicNumber);
if(adjustedLot > 0) g_RS_BTCUSD_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingNVDA", RS_NVDA_MagicNumber);
if(adjustedLot > 0) g_RS_NVDA_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingTSLA", RS_TSLA_MagicNumber);
if(adjustedLot > 0) g_RS_TSLA_LotSize = adjustedLot;
adjustedLot = GetStrategyLotSize("RSIScalpingXAUUSD", RS_XAUUSD_MagicNumber);
if(adjustedLot > 0) g_RS_XAUUSD_LotSize = adjustedLot;
}
if(EnableDarvasBox)
ProcessDarvasBox(DB_Symbol);
if(EnableEMASlopeDistance)
ProcessEMASlopeDistance(ES_Symbol);
if(EnableRSICrossOverReversal)
ProcessRSICrossOverReversal(RC_Symbol);
if(EnableRSIMidPointHijack)
ProcessRSIMidPointHijack(RM_Symbol);
if(EnableRSIScalpingAPPL)
ProcessRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price,
RS_APPL_RSI_Overbought, RS_APPL_RSI_Oversold, RS_APPL_RSI_Target_Buy, RS_APPL_RSI_Target_Sell,
RS_APPL_BarsToWait, g_RS_APPL_LotSize, RS_APPL_MagicNumber);
if(EnableRSIScalpingBTCUSD)
ProcessRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price,
RS_BTCUSD_RSI_Overbought, RS_BTCUSD_RSI_Oversold, RS_BTCUSD_RSI_Target_Buy, RS_BTCUSD_RSI_Target_Sell,
RS_BTCUSD_BarsToWait, g_RS_BTCUSD_LotSize, RS_BTCUSD_MagicNumber);
if(EnableRSIScalpingNVDA)
ProcessRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price,
RS_NVDA_RSI_Overbought, RS_NVDA_RSI_Oversold, RS_NVDA_RSI_Target_Buy, RS_NVDA_RSI_Target_Sell,
RS_NVDA_BarsToWait, g_RS_NVDA_LotSize, RS_NVDA_MagicNumber);
if(EnableRSIScalpingTSLA)
ProcessRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price,
RS_TSLA_RSI_Overbought, RS_TSLA_RSI_Oversold, RS_TSLA_RSI_Target_Buy, RS_TSLA_RSI_Target_Sell,
RS_TSLA_BarsToWait, g_RS_TSLA_LotSize, RS_TSLA_MagicNumber);
if(EnableRSIScalpingXAUUSD)
ProcessRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price,
RS_XAUUSD_RSI_Overbought, RS_XAUUSD_RSI_Oversold, RS_XAUUSD_RSI_Target_Buy, RS_XAUUSD_RSI_Target_Sell,
RS_XAUUSD_BarsToWait, g_RS_XAUUSD_LotSize, RS_XAUUSD_MagicNumber);
}
//+------------------------------------------------------------------+
//| Include strategy implementations |
//+------------------------------------------------------------------+
#include "Strategies/DarvasBoxStrategy.mqh"
#include "Strategies/EMASlopeDistanceStrategy.mqh"
#include "Strategies/RSICrossOverReversalStrategy.mqh"
#include "Strategies/RSIMidPointHijackStrategy.mqh"
#include "Strategies/RSIScalpingStrategy.mqh"
//+------------------------------------------------------------------+
@@ -0,0 +1,45 @@
; RSIFollowReverseEMACross (RSIMidPointHijackBTCUSD\main.mq5) — optimization preset
; Strategy Tester → Inputs → Load
; Format: Name=value||start||step||stop||Y|N
;
; Timeframe: leave N (ENUM not a linear range). Set manually or duplicate preset per TF.
; General Settings
InpTimeframe=16385||16385||0||16385||N
InpLotSize=0.02||0.02||0.001000||0.100000||N
InpMagicNumberRSIFollow=1001||1001||1||10010||N
InpMagicNumberRSIReverse=1002||1002||1||10020||N
InpMagicNumberEMACross=1003||1003||1||10030||N
; Strategy Switches
InpEnableRSIFollow=true||false||0||true||Y
InpEnableRSIReverse=true||false||0||true||Y
InpEnableEMACross=true||false||0||true||Y
InpEnableStrategyLock=false||false||0||true||Y
InpLockProfitThreshold=0.0||0.0||5.0||200.0||Y
InpCloseOppositeTrades=false||false||0||true||Y
; RSI Follow Strategy
InpRSIPeriod=32||14||2||48||Y
InpRSIOverbought=78||65||2||88||Y
InpRSIOversold=46||20||2||50||Y
InpRSIExitLevel=44||35||1||55||Y
InpRSIFollowStartHour=23||20||1||23||Y
InpRSIFollowEndHour=8||4||1||12||Y
InpRSIFollowCloseOutsideHours=false||false||0||true||Y
; RSI Reverse Strategy
InpRSIReversePeriod=59||28||3||80||Y
InpRSIReverseOverbought=51||48||1||78||Y
InpRSIReverseOversold=49||20||2||55||Y
InpRSIReverseCrossLevel=53||45||1||60||Y
InpRSIReverseExitLevel=48||35||1||55||Y
InpRSIReverseStartHour=7||0||1||12||Y
InpRSIReverseEndHour=13||10||1||18||Y
InpRSIReverseCloseOutsideHours=false||false||0||true||Y
InpRSIReverseCooldownBars=15||0||3||30||Y
InpRSIReverseCooldownOnLoss=true||false||0||true||Y
; EMA Cross Strategy
InpEMAPeriod=120||60||10||200||Y
InpEMACrossStartHour=8||0||1||12||Y
InpEMACrossEndHour=14||12||1||20||Y
InpEMACrossCloseOutsideHours=true||false||0||true||Y
InpUseEMADistanceEntry=true||false||0||true||Y
InpEMADistancePips=160.0||40.0||20.0||400.0||Y
InpEMADistancePeriod=26||10||2||40||Y
+604
View File
@@ -0,0 +1,604 @@
//+------------------------------------------------------------------+
//| RSIFollowReverseEMACrossOver.mq5 |
//| Copyright 2024, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include "../_united/MagicNumberHelpers.mqh"
// Input Parameters
input group "General Settings"
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_H1; // Trading Timeframe
input double InpLotSize = 0.02; // Lot Size
input int InpMagicNumberRSIFollow = 1001; // Magic Number RSI Follow
input int InpMagicNumberRSIReverse = 1002;// Magic Number RSI Reverse
input int InpMagicNumberEMACross = 1003; // Magic Number EMA Cross
input group "Strategy Switches"
input bool InpEnableRSIFollow = true; // Enable RSI Follow Strategy
input bool InpEnableRSIReverse = true; // Enable RSI Reverse Strategy
input bool InpEnableEMACross = true; // Enable EMA Cross Strategy
input bool InpEnableStrategyLock = false; // Enable Strategy Lock
input double InpLockProfitThreshold = 0.0; // Lock Profit Threshold (pips)
input bool InpCloseOppositeTrades = false; // Close Opposite Trades When Profiting
input group "RSI Follow Strategy"
input int InpRSIPeriod = 32; // RSI Period
input int InpRSIOverbought = 78; // RSI Overbought Level
input int InpRSIOversold = 46; // RSI Oversold Level
input int InpRSIExitLevel = 44; // RSI Exit Level
input int InpRSIFollowStartHour = 23; // RSI Follow Start Hour (0-23)
input int InpRSIFollowEndHour = 8; // RSI Follow End Hour (0-23)
input bool InpRSIFollowCloseOutsideHours = false; // Close trades outside trading hours
input group "RSI Reverse Strategy"
input int InpRSIReversePeriod = 59; // RSI Period
input int InpRSIReverseOverbought = 51; // RSI Overbought Level
input int InpRSIReverseOversold = 49; // RSI Oversold Level
input int InpRSIReverseCrossLevel = 53; // RSI Cross Level
input int InpRSIReverseExitLevel = 48; // RSI Exit Level
input int InpRSIReverseStartHour = 7; // RSI Reverse Start Hour (0-23)
input int InpRSIReverseEndHour = 13; // RSI Reverse End Hour (0-23)
input bool InpRSIReverseCloseOutsideHours = false; // Close trades outside trading hours
input int InpRSIReverseCooldownBars = 15; // RSI Reverse Cooldown (bars)
input bool InpRSIReverseCooldownOnLoss = true; // Apply cooldown only on loss
input group "EMA Cross Strategy"
input int InpEMAPeriod = 120; // EMA Period
input int InpEMACrossStartHour = 8; // EMA Cross Start Hour (0-23)
input int InpEMACrossEndHour = 14; // EMA Cross End Hour (0-23)
input bool InpEMACrossCloseOutsideHours = true; // Close trades outside trading hours
input bool InpUseEMADistanceEntry = true; // Use EMA Distance Entry
input double InpEMADistancePips = 160.0; // EMA Distance Threshold (pips)
input int InpEMADistancePeriod = 26; // EMA Distance Period (bars)
// Global Variables
int rsiHandle;
int rsiReverseHandle;
int emaHandle;
bool rsiOverbought = false;
bool rsiOversold = false;
bool rsiReverseOverbought = false;
bool rsiReverseOversold = false;
CTrade trade;
CPositionInfo positionInfo;
bool emaCrossBuySignal = false;
bool emaCrossSellSignal = false;
int emaCrossSignalBar = 0;
datetime lastBarTime = 0;
datetime rsiReverseLastCloseTime = 0;
bool rsiReverseInCooldown = false;
double lastBarRSI = 0; // Store last bar's RSI value
double lastBarRSIReverse = 0; // Store last bar's RSI Reverse value
double lastBarEMA = 0; // Store last bar's EMA value
double lastBarClose = 0; // Store last bar's close value
double lastBarEMAPrev = 0; // Store previous bar's EMA value
double lastBarClosePrev = 0; // Store previous bar's close value
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize indicators
rsiHandle = iRSI(_Symbol, InpTimeframe, InpRSIPeriod, PRICE_CLOSE);
rsiReverseHandle = iRSI(_Symbol, InpTimeframe, InpRSIReversePeriod, PRICE_CLOSE);
emaHandle = iMA(_Symbol, InpTimeframe, InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(rsiHandle == INVALID_HANDLE || rsiReverseHandle == INVALID_HANDLE || emaHandle == INVALID_HANDLE)
{
Print("Error creating indicators");
return INIT_FAILED;
}
// Initialize trade settings
trade.SetExpertMagicNumber(InpMagicNumberRSIFollow);
trade.SetMarginMode();
trade.SetTypeFillingBySymbol(_Symbol);
trade.SetDeviationInPoints(10);
// Initialize last bar time
datetime time[];
if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0)
{
lastBarTime = time[0];
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Check if new bar has formed |
//+------------------------------------------------------------------+
bool IsNewBar()
{
datetime time[];
if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0)
{
if(time[0] != lastBarTime)
{
lastBarTime = time[0];
return true;
}
}
return false;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Release indicator handles
IndicatorRelease(rsiHandle);
IndicatorRelease(rsiReverseHandle);
IndicatorRelease(emaHandle);
}
//+------------------------------------------------------------------+
//| Check if current time is within trading hours |
//+------------------------------------------------------------------+
bool IsWithinTradingHours(int startHour, int endHour)
{
MqlDateTime currentTime;
TimeToStruct(TimeCurrent(), currentTime);
if(startHour <= endHour)
{
return (currentTime.hour >= startHour && currentTime.hour < endHour);
}
else
{
return (currentTime.hour >= startHour || currentTime.hour < endHour);
}
}
//+------------------------------------------------------------------+
//| Check if position exists for given magic number AND symbol |
//+------------------------------------------------------------------+
bool HasPosition(int magic)
{
// Use helper function that verifies BOTH symbol AND magic number for THIS EA
return PositionExistsByMagic(_Symbol, magic);
}
//+------------------------------------------------------------------+
//| Check if any strategy has profitable position |
//+------------------------------------------------------------------+
bool HasProfitablePosition(int excludeMagic)
{
bool hasProfitable = false;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(positionInfo.SelectByIndex(i))
{
if(positionInfo.Magic() != excludeMagic)
{
double profit = positionInfo.Profit();
if(profit > InpLockProfitThreshold * _Point)
{
hasProfitable = true;
// If enabled, close opposite trades
if(InpCloseOppositeTrades)
{
// Check if this is an opposite trade to the excluded magic number
if((excludeMagic == InpMagicNumberRSIFollow && positionInfo.Magic() == InpMagicNumberRSIReverse) ||
(excludeMagic == InpMagicNumberRSIReverse && positionInfo.Magic() == InpMagicNumberRSIFollow) ||
(excludeMagic == InpMagicNumberEMACross && (positionInfo.Magic() == InpMagicNumberRSIReverse || positionInfo.Magic() == InpMagicNumberRSIFollow)) ||
((excludeMagic == InpMagicNumberRSIFollow || excludeMagic == InpMagicNumberRSIReverse) && positionInfo.Magic() == InpMagicNumberEMACross))
{
ClosePosition(positionInfo.Magic());
}
}
}
}
}
}
return hasProfitable;
}
//+------------------------------------------------------------------+
//| Check for RSI Follow Strategy signals |
//+------------------------------------------------------------------+
void CheckRSIFollowStrategy()
{
// Check if within trading hours
if(!IsWithinTradingHours(InpRSIFollowStartHour, InpRSIFollowEndHour))
{
if(InpRSIFollowCloseOutsideHours)
{
if(HasPosition(InpMagicNumberRSIFollow))
{
ClosePosition(InpMagicNumberRSIFollow);
}
}
return;
}
// Check strategy lock
if(InpEnableStrategyLock && HasProfitablePosition(InpMagicNumberRSIFollow))
return;
// Use lastBarRSI instead of copying buffer
if(lastBarRSI > InpRSIOverbought)
rsiOverbought = true;
else if(lastBarRSI < InpRSIOversold)
rsiOversold = true;
// Check for entry signals
if(rsiOverbought && lastBarRSI < InpRSIExitLevel)
{
// Sell signal
if(!HasPosition(InpMagicNumberRSIFollow))
{
trade.SetExpertMagicNumber(InpMagicNumberRSIFollow);
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "RSI Follow");
}
rsiOverbought = false;
}
else if(rsiOversold && lastBarRSI > InpRSIExitLevel)
{
// Buy signal
if(!HasPosition(InpMagicNumberRSIFollow))
{
trade.SetExpertMagicNumber(InpMagicNumberRSIFollow);
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "RSI Follow");
}
rsiOversold = false;
}
}
//+------------------------------------------------------------------+
//| Check if RSI Reverse is in cooldown |
//+------------------------------------------------------------------+
bool IsRSIReverseInCooldown()
{
if(InpRSIReverseCooldownBars <= 0)
return false;
if(!rsiReverseInCooldown)
return false;
datetime time[];
if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0)
{
datetime currentBarTime = time[0];
datetime cooldownEndTime = rsiReverseLastCloseTime + InpRSIReverseCooldownBars * PeriodSeconds(InpTimeframe);
if(currentBarTime >= cooldownEndTime)
{
rsiReverseInCooldown = false;
return false;
}
}
return true;
}
//+------------------------------------------------------------------+
//| Check for RSI Reverse Strategy signals |
//+------------------------------------------------------------------+
void CheckRSIReverseStrategy()
{
// Check if within trading hours
if(!IsWithinTradingHours(InpRSIReverseStartHour, InpRSIReverseEndHour))
{
if(InpRSIReverseCloseOutsideHours)
{
if(HasPosition(InpMagicNumberRSIReverse))
{
ClosePosition(InpMagicNumberRSIReverse);
}
}
return;
}
// Check strategy lock
if(InpEnableStrategyLock && HasProfitablePosition(InpMagicNumberRSIReverse))
return;
// Check cooldown
if(IsRSIReverseInCooldown())
return;
// Use lastBarRSIReverse instead of copying buffer
if(lastBarRSIReverse > InpRSIReverseOverbought)
rsiReverseOverbought = true;
else if(lastBarRSIReverse < InpRSIReverseOversold)
rsiReverseOversold = true;
// Check for entry signals
if(rsiReverseOverbought && lastBarRSIReverse < InpRSIReverseCrossLevel)
{
// Sell signal
if(!HasPosition(InpMagicNumberRSIReverse))
{
trade.SetExpertMagicNumber(InpMagicNumberRSIReverse);
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "RSI Reverse");
}
rsiReverseOverbought = false;
}
else if(rsiReverseOversold && lastBarRSIReverse > InpRSIReverseCrossLevel)
{
// Buy signal
if(!HasPosition(InpMagicNumberRSIReverse))
{
trade.SetExpertMagicNumber(InpMagicNumberRSIReverse);
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "RSI Reverse");
}
rsiReverseOversold = false;
}
}
//+------------------------------------------------------------------+
//| Check for EMA Cross Strategy signals |
//+------------------------------------------------------------------+
void CheckEMACrossStrategy()
{
// Check if within trading hours
if(!IsWithinTradingHours(InpEMACrossStartHour, InpEMACrossEndHour))
{
if(InpEMACrossCloseOutsideHours)
{
if(HasPosition(InpMagicNumberEMACross))
{
ClosePosition(InpMagicNumberEMACross);
}
}
return;
}
// Check strategy lock
if(InpEnableStrategyLock && HasProfitablePosition(InpMagicNumberEMACross))
return;
// Check for cross signals using stored values
if(lastBarEMAPrev < lastBarClosePrev && lastBarEMA > lastBarClose)
{
// Buy cross signal
emaCrossBuySignal = true;
emaCrossSellSignal = false;
emaCrossSignalBar = 0;
}
else if(lastBarEMAPrev > lastBarClosePrev && lastBarEMA < lastBarClose)
{
// Sell cross signal
emaCrossSellSignal = true;
emaCrossBuySignal = false;
emaCrossSignalBar = 0;
}
// Check for distance entry conditions
if(InpUseEMADistanceEntry)
{
if(emaCrossBuySignal)
{
// Check if price has moved above EMA by the required distance for the required period
bool distanceConditionMet = true;
double emaHistory[], closeHistory[];
ArraySetAsSeries(emaHistory, true);
ArraySetAsSeries(closeHistory, true);
if(CopyBuffer(emaHandle, 0, 0, InpEMADistancePeriod, emaHistory) > 0 &&
CopyClose(_Symbol, InpTimeframe, 0, InpEMADistancePeriod, closeHistory) > 0)
{
for(int i = 0; i < InpEMADistancePeriod; i++)
{
double distance = (closeHistory[i] - emaHistory[i]) / _Point;
if(distance < InpEMADistancePips)
{
distanceConditionMet = false;
break;
}
}
if(distanceConditionMet && !HasPosition(InpMagicNumberEMACross))
{
trade.SetExpertMagicNumber(InpMagicNumberEMACross);
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross Distance");
emaCrossBuySignal = false;
}
}
}
else if(emaCrossSellSignal)
{
// Check if price has moved below EMA by the required distance for the required period
bool distanceConditionMet = true;
double emaHistory[], closeHistory[];
ArraySetAsSeries(emaHistory, true);
ArraySetAsSeries(closeHistory, true);
if(CopyBuffer(emaHandle, 0, 0, InpEMADistancePeriod, emaHistory) > 0 &&
CopyClose(_Symbol, InpTimeframe, 0, InpEMADistancePeriod, closeHistory) > 0)
{
for(int i = 0; i < InpEMADistancePeriod; i++)
{
double distance = (emaHistory[i] - closeHistory[i]) / _Point;
if(distance < InpEMADistancePips)
{
distanceConditionMet = false;
break;
}
}
if(distanceConditionMet && !HasPosition(InpMagicNumberEMACross))
{
trade.SetExpertMagicNumber(InpMagicNumberEMACross);
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross Distance");
emaCrossSellSignal = false;
}
}
}
}
else
{
// Original cross entry logic using stored values
if(lastBarEMAPrev < lastBarClosePrev && lastBarEMA > lastBarClose)
{
// Buy signal
if(!HasPosition(InpMagicNumberEMACross))
{
trade.SetExpertMagicNumber(InpMagicNumberEMACross);
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross");
}
}
else if(lastBarEMAPrev > lastBarClosePrev && lastBarEMA < lastBarClose)
{
// Sell signal
if(!HasPosition(InpMagicNumberEMACross))
{
trade.SetExpertMagicNumber(InpMagicNumberEMACross);
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross");
}
}
}
// Increment signal bar counter
if(emaCrossBuySignal || emaCrossSellSignal)
{
emaCrossSignalBar++;
// Reset signals if they're too old (optional, can be removed if not needed)
if(emaCrossSignalBar > InpEMADistancePeriod * 2)
{
emaCrossBuySignal = false;
emaCrossSellSignal = false;
}
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Only process on new bar
if(!IsNewBar())
return;
// Get indicator values for the new bar
double rsi[], rsiReverse[], ema[], close[];
ArraySetAsSeries(rsi, true);
ArraySetAsSeries(rsiReverse, true);
ArraySetAsSeries(ema, true);
ArraySetAsSeries(close, true);
// Store previous values
lastBarEMAPrev = lastBarEMA;
lastBarClosePrev = lastBarClose;
// Get new values
if(CopyBuffer(rsiHandle, 0, 0, 1, rsi) > 0)
lastBarRSI = rsi[0];
if(CopyBuffer(rsiReverseHandle, 0, 0, 1, rsiReverse) > 0)
lastBarRSIReverse = rsiReverse[0];
if(CopyBuffer(emaHandle, 0, 0, 1, ema) > 0)
lastBarEMA = ema[0];
if(CopyClose(_Symbol, InpTimeframe, 0, 1, close) > 0)
lastBarClose = close[0];
// Check for new signals
if(InpEnableRSIFollow)
CheckRSIFollowStrategy();
if(InpEnableRSIReverse)
CheckRSIReverseStrategy();
if(InpEnableEMACross)
CheckEMACrossStrategy();
// Check for exit conditions
CheckExitConditions();
}
//+------------------------------------------------------------------+
//| Check exit conditions for all strategies |
//+------------------------------------------------------------------+
void CheckExitConditions()
{
if(InpEnableRSIFollow)
{
// Check RSI Follow exit conditions
if(HasPosition(InpMagicNumberRSIFollow))
{
if((positionInfo.PositionType() == POSITION_TYPE_BUY && lastBarRSI < InpRSIExitLevel) ||
(positionInfo.PositionType() == POSITION_TYPE_SELL && lastBarRSI > InpRSIExitLevel))
{
ClosePosition(InpMagicNumberRSIFollow);
}
}
}
if(InpEnableRSIReverse)
{
// Check RSI Reverse exit conditions
if(HasPosition(InpMagicNumberRSIReverse))
{
if((positionInfo.PositionType() == POSITION_TYPE_BUY && lastBarRSIReverse < InpRSIReverseExitLevel) ||
(positionInfo.PositionType() == POSITION_TYPE_SELL && lastBarRSIReverse > InpRSIReverseExitLevel))
{
ClosePosition(InpMagicNumberRSIReverse);
}
}
}
if(InpEnableEMACross)
{
// Check EMA Cross exit conditions using stored values
if(HasPosition(InpMagicNumberEMACross))
{
if((positionInfo.PositionType() == POSITION_TYPE_BUY && lastBarEMA > lastBarClose) ||
(positionInfo.PositionType() == POSITION_TYPE_SELL && lastBarEMA < lastBarClose))
{
ClosePosition(InpMagicNumberEMACross);
}
}
}
}
//+------------------------------------------------------------------+
//| Close position by magic number |
//+------------------------------------------------------------------+
void ClosePosition(int magic)
{
// Close position using helper that verifies symbol AND magic number for THIS EA
// First check if position exists for this EA on this symbol
if(!PositionExistsByMagic(_Symbol, magic))
{
return; // No position for this EA on this symbol
}
// Get the position ticket for this EA on this symbol
ulong ticket = GetPositionTicketByMagic(_Symbol, magic);
if(ticket == 0)
{
return; // No valid ticket found
}
// Check if this is RSI Reverse position and update cooldown
if(magic == InpMagicNumberRSIReverse)
{
if(PositionSelectByTicketSymbolAndMagic(ticket, _Symbol, magic))
{
datetime time[];
if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0)
{
rsiReverseLastCloseTime = time[0];
// Only enter cooldown if it's a loss or if cooldown on loss is disabled
double profit = PositionGetDouble(POSITION_PROFIT);
if(!InpRSIReverseCooldownOnLoss || profit < 0)
{
rsiReverseInCooldown = true;
}
}
}
}
// Close the position using helper function
ClosePositionByMagic(trade, _Symbol, magic);
}
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB