This commit is contained in:
zhutoutoutousan
2026-05-02 15:55:04 +02:00
parent 0b4a70b843
commit b5acd37754
198 changed files with 18743 additions and 11368 deletions
+386
View File
@@ -0,0 +1,386 @@
//+------------------------------------------------------------------+
//| EURUSD_H1_ActionEA.mq5 |
//| ai/eurusd1h/main.py — 24 features, 5-class softmax |
//| Classes: 0=HOLD 1=BUY 2=SELL_SHORT 3=CLOSE_LONG 4=CLOSE_SHORT |
//| Entry: strict trio winner among p0,p1,p2 only. |
//| Exit: unique 5-class argmax != held side (1 long, 2 short). |
//| No SL / TP / ATR stops. Attach EURUSD H1. |
//+------------------------------------------------------------------+
#property copyright "Profitable EA Project"
#property version "1.00"
#property description "EURUSD H1 action ONNX; ordinal entry/exit; no fixed SL/TP"
#include <Trade\Trade.mqh>
#resource "models\\EURUSD_H1_action.onnx" as uchar ExtModel[]
#define FEAT_COUNT 24
#define REL_EPS 1e-9
input group "Model"
input int InpLookback = 48;
input int InpSessionHourOffset = 0;
input string InpFeatMinStr = "";
input string InpFeatMaxStr = "";
input group "Timing"
input int InpMinBarsInTrade = 1; // model exit only after this many bars in position (0=off)
input group "Trade"
input double InpLotSize = 0.01;
input int InpMagic = 902601;
input int InpSlippage = 30;
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 InitDefaultScalerFromMeta()
{
// EURUSD_H1_action_meta.json scaler_feature_min / max (train fit)
double def_min[FEAT_COUNT] = {
0.9539399743080139,
0.9559400081634521,
0.9536200165748596,
0.9538999795913696,
9.999999974752427e-07,
0.07019035518169403,
-0.02143237181007862,
-0.028110405430197716,
0.0002704667276702821,
-0.02017582766711712,
1.0,
0.0004555500054266304,
0.0002461568801663816,
0.022001149132847786,
0.11231997609138489,
-0.5339273810386658,
-1.623793125152588,
-1.837566614151001,
6.83732741890708e-06,
0.0,
0.0,
0.0,
0.0,
0.0
};
double def_max[FEAT_COUNT] = {
1.493149995803833,
1.4938499927520752,
1.4904999732971191,
1.493190050125122,
0.06699500232934952,
0.9350273013114929,
0.028008731082081795,
0.03147505968809128,
0.009249407798051834,
0.01742853783071041,
1.0232577323913574,
0.02111775055527687,
7.801275253295898,
0.9864169955253601,
0.8837512731552124,
0.49708572030067444,
1.8055412769317627,
1.927569031715393,
0.8700546026229858,
1.0,
1.0,
1.0,
1.0,
1.0
};
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[];
if(StringSplit(s, ',', parts) != FEAT_COUNT) return false;
for(int i = 0; i < FEAT_COUNT; i++)
arr[i] = StringToDouble(parts[i]);
return true;
}
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 spr = (r0 - rv7) / 50.0;
if(spr > 1.0) spr = 1.0;
if(spr < -1.0) spr = -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)spr;
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;
}
int TrioStrictWinner012(const double p0, const double p1, const double p2)
{
if(p0 > p1 + REL_EPS && p0 > p2 + REL_EPS) return 0;
if(p1 > p0 + REL_EPS && p1 > p2 + REL_EPS) return 1;
if(p2 > p0 + REL_EPS && p2 > p1 + REL_EPS) return 2;
return -1;
}
int FiveStrictWinner01234(const double p0, const double p1, const double p2, const double p3, const double p4)
{
const double p[5] = {p0, p1, p2, p3, p4};
int best = 0;
for(int k = 1; k < 5; k++)
if(p[k] > p[best])
best = k;
const double m = p[best];
int cnt = 0;
for(int k = 0; k < 5; k++)
if(p[k] + REL_EPS >= m)
cnt++;
if(cnt != 1)
return -1;
return best;
}
bool SelectOurPosition()
{
if(!PositionSelect(_Symbol))
return false;
if((long)PositionGetInteger(POSITION_MAGIC) != InpMagic)
return false;
return true;
}
int PositionBarsInTrade()
{
if(!SelectOurPosition())
return 0;
const datetime tOpen = (datetime)PositionGetInteger(POSITION_TIME);
const int sh = iBarShift(_Symbol, PERIOD_CURRENT, tOpen, false);
if(sh < 0)
return 9999;
return sh + 1;
}
int OnInit()
{
InitDefaultScalerFromMeta();
trade.SetExpertMagicNumber(InpMagic);
trade.SetDeviationInPoints(InpSlippage);
trade.SetTypeFilling(ORDER_FILLING_IOC);
if(StringLen(InpFeatMinStr) > 0 && ParseFeatCsv(InpFeatMinStr, g_feat_min))
Print("EURUSD Action EA: loaded InpFeatMinStr");
if(StringLen(InpFeatMaxStr) > 0 && ParseFeatCsv(InpFeatMaxStr, g_feat_max))
Print("EURUSD Action EA: loaded InpFeatMaxStr");
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;
}
if(_Period != PERIOD_H1)
Print("EURUSD_H1_ActionEA: chart period is ", EnumToString((ENUM_TIMEFRAMES)_Period),
" — training is H1; mismatch may hurt.");
Print("EURUSD_H1_ActionEA: ONNX OK. Ordinal entry/exit, no SL/TP. Lookback=", InpLookback);
return INIT_SUCCEEDED;
}
void OnDeinit(const int r)
{
if(g_onnx != INVALID_HANDLE)
OnnxRelease(g_onnx);
}
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("EURUSD Action EA: PrepareMatrix failed");
return;
}
vectorf out;
out.Resize(5);
if(!OnnxRun(g_onnx, ONNX_NO_CONVERSION, Min, out))
{
Print("OnnxRun failed ", GetLastError());
return;
}
const double p0 = out[0], p1 = out[1], p2 = out[2], p3 = out[3], p4 = out[4];
if(!SelectOurPosition())
{
const int w3 = TrioStrictWinner012(p0, p1, p2);
if(w3 == 1)
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "EURUSD act BUY");
else if(w3 == 2)
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "EURUSD act SELL");
return;
}
const bool allow = (InpMinBarsInTrade <= 0) || (PositionBarsInTrade() >= InpMinBarsInTrade);
if(!allow)
return;
const int w5 = FiveStrictWinner01234(p0, p1, p2, p3, p4);
const long typ = (long)PositionGetInteger(POSITION_TYPE);
bool close_it = false;
if(typ == POSITION_TYPE_BUY)
close_it = (w5 != -1 && w5 != 1);
else if(typ == POSITION_TYPE_SELL)
close_it = (w5 != -1 && w5 != 2);
if(close_it)
trade.PositionClose(_Symbol);
}
Binary file not shown.
+37
View File
@@ -0,0 +1,37 @@
# EURUSD H1 — ONNX action model (full MT5 history)
Same methodology as `ai/yt/train_article_split.py`:
- **24 features** + **5 softmax classes** (`ai/xauusd_h1/features.py`, `labeling.py`).
- **MinMaxScaler** is fit on **every** valid feature row MT5 returns (no 20102020 cut).
- **Training sequences**: all but a **chronological tail** (default **12%**) used only for `val_loss` / EarlyStopping (does not remove data from the scaler).
- Optional **KMeans** on forward-return fingerprints + class-balanced `sample_weight` on the train split.
## Run
```bash
cd ai/eurusd1h
pip install -r requirements.txt
python main.py
```
Requires MetaTrader 5 with **EURUSD H1** history downloaded (Tools → History Center).
## Environment overrides
| Variable | Default | Meaning |
|-----------------|-----------|----------------------------------------------|
| `EUR_SYMBOL` | `EURUSD` | MT5 symbol |
| `EUR_LOOKBACK` | `48` | Sequence length |
| `EUR_EPOCHS` | `40` | Max epochs |
| `EUR_BATCH` | `64` | Batch size |
| `EUR_CLUSTERS` | `12` | KMeans clusters (`0` = off) |
| `EUR_VAL_FRAC` | `0.12` | Fraction of sequences at **end** for val |
## Outputs
- `models/EURUSD_H1_action.onnx`
- `models/EURUSD_H1_action_meta.json`
- `models/EURUSD_H1_action_scaler.pkl`
Deploy like `ai/yt/US500_H1_ArticleEA.mq5`: embed ONNX, set `InpLookback`, paste `scaler_feature_min` / `max` from the meta JSON into the EA inputs.
+316
View File
@@ -0,0 +1,316 @@
"""
EURUSD H1 — same stack as ai/yt (24 features, 5-class softmax, optional KMeans weights).
MinMaxScaler is fit on **all** feature rows returned by MT5 (full downloaded history).
A chronological **tail** slice (default 12%% of sequences) is used only for val_loss /
EarlyStopping; all earlier sequences are used for training.
"""
from __future__ import annotations
import json
import os
import pickle
import sys
from datetime import datetime, timedelta
from pathlib import Path
import MetaTrader5 as mt5
import numpy as np
import pandas as pd
import tensorflow as tf
import tf2onnx
import onnx
from sklearn.cluster import KMeans
from sklearn.preprocessing import MinMaxScaler
from tensorflow import keras
from tensorflow.keras import layers
from tqdm import tqdm
_XH1 = Path(__file__).resolve().parent.parent / "xauusd_h1"
sys.path.insert(0, str(_XH1))
from features import NUM_FEATURES, prepare_features_full # noqa: E402
from labeling import atr_series, class_weights, compute_action_labels # noqa: E402
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 EURUSD 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 forward_return_fingerprints(
df: pd.DataFrame,
feat_index: pd.DatetimeIndex,
horizons: tuple[int, ...] = (1, 2, 4, 8, 16),
) -> tuple[np.ndarray, np.ndarray]:
close = df["close"].to_numpy(dtype=np.float64)
atr = atr_series(df, 14).to_numpy(dtype=np.float64)
pos = df.index.get_indexer(feat_index)
n = len(feat_index)
d = len(horizons)
M = np.zeros((n, d), dtype=np.float64)
valid = np.ones(n, dtype=bool)
max_h = max(horizons)
for j, i in enumerate(pos):
if i < 0 or i + max_h >= len(close):
valid[j] = False
continue
a = float(atr[i]) if np.isfinite(atr[i]) and atr[i] > 0 else close[i] * 1e-4
for k, h in enumerate(horizons):
if i + h >= len(close):
valid[j] = False
break
M[j, k] = (close[i + h] - close[i]) / a
return M, valid
def create_sequences(
X: np.ndarray,
y: np.ndarray,
times: np.ndarray,
lookback: int,
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
xs, ys, t_end = [], [], []
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])
t_end.append(times[i])
return (
np.asarray(xs, dtype=np.float32),
np.asarray(ys, dtype=np.int64),
np.asarray(t_end),
)
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("EUR_SYMBOL", "EURUSD")
lookback = int(os.environ.get("EUR_LOOKBACK", "48"))
epochs = int(os.environ.get("EUR_EPOCHS", "40"))
batch_size = int(os.environ.get("EUR_BATCH", "64"))
n_clusters = int(os.environ.get("EUR_CLUSTERS", "12"))
val_frac = float(os.environ.get("EUR_VAL_FRAC", "0.12"))
val_frac = min(max(val_frac, 0.05), 0.35)
fetch_start = datetime(1990, 1, 1)
fetch_end = datetime(2030, 12, 31)
out_dir = Path(__file__).resolve().parent / "models"
out_dir.mkdir(parents=True, exist_ok=True)
onnx_path = out_dir / f"{symbol}_H1_action.onnx"
meta_path = out_dir / f"{symbol}_H1_action_meta.json"
print(f"Symbol={symbol} H1 | fetch [{fetch_start.date()} .. {fetch_end.date()}]")
print("Scaler: ALL bars | Val: chronological tail for early stopping only")
print("Fetching MT5 …")
try:
raw = fetch_mt5_range(symbol, mt5.TIMEFRAME_H1, fetch_start, fetch_end)
finally:
mt5.shutdown()
if len(raw) < 500:
print("ERROR: Not enough H1 bars — check EURUSD history in MT5.")
return 1
print(f"Bars: {len(raw)} range: {raw.index[0]}{raw.index[-1]}")
feat = prepare_features_full(raw)
labels = compute_action_labels(raw).loc[feat.index]
y = labels.values.astype(np.int64)
X_raw = feat.values.astype(np.float32)
times = feat.index.to_numpy()
valid = np.isfinite(X_raw).all(axis=1) & (y >= 0) & (y < NUM_CLASSES)
X_raw = X_raw[valid]
y = y[valid]
times = times[valid]
scaler = MinMaxScaler()
scaler.fit(X_raw)
Xn = scaler.transform(X_raw).astype(np.float32)
X_seq, y_seq, t_end = create_sequences(Xn, y, times, lookback)
if len(X_seq) < 500:
print("ERROR: Too few sequences.")
return 1
n_seq = len(X_seq)
split_i = int(n_seq * (1.0 - val_frac))
split_i = max(split_i, lookback + 100)
split_i = min(split_i, n_seq - 200)
train_m = np.zeros(n_seq, dtype=bool)
train_m[:split_i] = True
val_m = ~train_m
X_train, y_train = X_seq[train_m], y_seq[train_m]
X_val, y_val = X_seq[val_m], y_seq[val_m]
print(
f"Sequences train={len(X_train)} val_tail={len(X_val)} ({100*val_frac:.1f}%%) "
f"lookback={lookback}"
)
cw = class_weights(y_train, NUM_CLASSES)
tr_idx = np.flatnonzero(train_m)
base_w = np.array([cw[int(y_seq[i])] for i in tr_idx], dtype=np.float32)
sample_w = base_w.copy()
if n_clusters > 1:
fp, fp_ok = forward_return_fingerprints(raw, pd.DatetimeIndex(times))
fp_seq = fp[lookback - 1 :]
ok_seq = fp_ok[lookback - 1 :]
fp_tr = fp_seq[tr_idx]
ok_tr = ok_seq[tr_idx]
fit_mask = ok_tr & np.isfinite(fp_tr).all(axis=1)
if int(fit_mask.sum()) >= n_clusters * 5:
km = KMeans(n_clusters=n_clusters, random_state=42, n_init=10)
km.fit(fp_tr[fit_mask])
labels_tr = np.full(len(tr_idx), -1, dtype=np.int32)
labels_tr[fit_mask] = km.predict(fp_tr[fit_mask])
counts = np.zeros(n_clusters, dtype=np.float64)
for c in labels_tr:
if 0 <= c < n_clusters:
counts[c] += 1.0
counts = np.maximum(counts, 1.0)
total_assigned = max(int((labels_tr >= 0).sum()), 1)
w_cl = np.ones(len(tr_idx), dtype=np.float32)
for j in range(len(tr_idx)):
c = int(labels_tr[j])
if c >= 0:
w_cl[j] = float(total_assigned / (n_clusters * counts[c]))
sample_w = base_w * w_cl
sample_w *= len(sample_w) / float(np.sum(sample_w))
print(f"KMeans clusters={n_clusters} (train subset only)")
else:
print("Skipping KMeans: not enough valid fingerprints.")
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=10, restore_best_weights=True
),
keras.callbacks.ReduceLROnPlateau(
monitor="val_loss", factor=0.5, patience=4, min_lr=1e-6
),
],
)
loss, acc = model.evaluate(X_val, y_val, verbose=0)
print(f"Tail val_loss={loss:.4f} val_accuracy={acc:.4f}")
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, str(onnx_path))
with open(str(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,
"mt5_bar_range": [str(raw.index[0]), str(raw.index[-1])],
"scaler_fit_on": "all_valid_feature_rows_full_mt5_range",
"validation_split": {
"mode": "chronological_tail_fraction",
"val_fraction": val_frac,
"train_sequences": int(train_m.sum()),
"val_sequences": int(val_m.sum()),
},
"clustering": (
f"KMeans n={n_clusters} on forward returns (1,2,4,8,16); train-only fit"
if n_clusters > 1
else "disabled"
),
"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,
"tail_val_accuracy": float(acc),
"tail_val_loss": float(loss),
"ea_note": "Copy ai/yt/US500_H1_ArticleEA.mq5 pattern: #resource ONNX + paste scaler from meta.",
}
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 (%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}")
return 0
if __name__ == "__main__":
sys.exit(main())
Binary file not shown.
@@ -0,0 +1,133 @@
{
"symbol": "EURUSD",
"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"
],
"mt5_bar_range": [
"2010-03-17 23:00:00",
"2026-04-24 23:00:00"
],
"scaler_fit_on": "all_valid_feature_rows_full_mt5_range",
"validation_split": {
"mode": "chronological_tail_fraction",
"val_fraction": 0.12,
"train_sequences": 87914,
"val_sequences": 11989
},
"clustering": "KMeans n=12 on forward returns (1,2,4,8,16); train-only fit",
"scaler_feature_min": [
0.9539399743080139,
0.9559400081634521,
0.9536200165748596,
0.9538999795913696,
9.999999974752427e-07,
0.07019035518169403,
-0.02143237181007862,
-0.028110405430197716,
0.0002704667276702821,
-0.02017582766711712,
1.0,
0.0004555500054266304,
0.0002461568801663816,
0.022001149132847786,
0.11231997609138489,
-0.5339273810386658,
-1.623793125152588,
-1.837566614151001,
6.83732741890708e-06,
0.0,
0.0,
0.0,
0.0,
0.0
],
"scaler_feature_max": [
1.493149995803833,
1.4938499927520752,
1.4904999732971191,
1.493190050125122,
0.06699500232934952,
0.9350273013114929,
0.028008731082081795,
0.03147505968809128,
0.009249407798051834,
0.01742853783071041,
1.0232577323913574,
0.02111775055527687,
7.801275253295898,
0.9864169955253601,
0.8837512731552124,
0.49708572030067444,
1.8055412769317627,
1.927569031715393,
0.8700546026229858,
1.0,
1.0,
1.0,
1.0,
1.0
],
"scaler_scale": [
1.854564905166626,
1.8590470552444458,
1.8626137971878052,
1.8542896509170532,
14.926709175109863,
1.1562873125076294,
20.226085662841797,
16.782615661621094,
111.3717041015625,
26.5926570892334,
42.99645233154297,
48.39755630493164,
0.12818820774555206,
1.03689706325531,
1.296291708946228,
0.969919741153717,
0.2916017770767212,
0.2655946612358093,
1.1493622064590454,
1.0,
1.0,
1.0,
1.0,
1.0
],
"tail_val_accuracy": 0.2594878673553467,
"tail_val_loss": 1.5548540353775024,
"ea_note": "Copy ai/yt/US500_H1_ArticleEA.mq5 pattern: #resource ONNX + paste scaler from meta."
}
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
-r ../xauusd_h1/requirements.txt
+663
View File
@@ -0,0 +1,663 @@
//+------------------------------------------------------------------+
//| US500_H1_ArticleEA.mq5 |
//| ai/yt: article-split ONNX (train 20102019 / OOS 20202024) |
//| Train: python train_article_split.py → models/*.onnx |
//| Attach to US500 (or broker equivalent) H1 chart. |
//+------------------------------------------------------------------+
#property copyright "Profitable EA Project"
#property version "1.05"
#property description "Embedded US500 H1 article-split ONNX; scaler from US500_H1_article_split_meta.json"
#include <Trade\Trade.mqh>
#resource "models\\US500_H1_article_split.onnx" as uchar ExtModel[]
#define FEAT_COUNT 24
#define PRED_HIST_CAP 32
#define REL_EPS 1e-9
input group "Model"
input int InpLookback = 48;
input int InpEntryMode = 1;
input double InpProbBuy = 0.18;
input double InpProbSell = 0.18;
input double InpMinBeatHold = 0.0;
input int InpExitMode = 1; // 0=fixed prob; 1/2=close must beat HOLD and stay-in-trade (2 legacy; old 2 vs-HOLD-only removed)
input double InpProbCloseL = 0.18;
input double InpProbCloseS = 0.18;
input double InpMinCloseBeatHold = 0.0;
input int InpMinBarsInTradeModelExit = 1; // min bars before model exit (0=off); pure mode uses 5-class winner
input bool InpPureRelative = true; // true: no prob cutoffs/edges — entry=trio strict winner, exit=5-class strict winner != side
input bool InpUseCloseHeadExit = true; // legacy only when InpPureRelative=false (CL/CS vs HOLD/stay; see InpExitMode)
input bool InpUseDirFlipExit = true; // legacy only when InpPureRelative=false (gap edges InpFlipExitEdge)
input double InpFlipExitEdge = 0.03; // legacy dir-flip min gap (ignored when InpPureRelative)
input int InpMinBarsAfterExit = 6; // after any close, wait this many flat bars before a new entry (0=off)
input int InpCooldownBarsAfterAdverse = 12; // extra flat-bar pause after adverse (ATR) stop; 0 = use only MinBarsAfterExit
input group "Decision (aggregate + sample, lowers trade churn)"
input int InpSampleEveryNBars = 2; // run ONNX / refresh history every N new bars (>=1)
input int InpAggWindow = 4; // rolling mean over last K samples (>=1)
input int InpMinAggSamples = 2; // need this many samples in window before new entries
input int InpMinBarsBetweenEntries = 0; // after an open, wait this many flat bars before next entry (0=off)
input double InpMinDirEdge = 0.03; // legacy entry mode 1 only (ignored when InpPureRelative)
input bool InpRequireStayOverClose = true; // legacy entry (ignored when InpPureRelative)
input group "Session (match Python SESSION_HOUR_OFFSET)"
input int InpSessionHourOffset = 0;
input group "Scaler override (empty = use built-in US500 train split)"
input string InpFeatMinStr = "";
input string InpFeatMaxStr = "";
input group "Risk"
input double InpLotSize = 0.01;
input int InpMagic = 902503;
input int InpSlippage = 30;
input group "Hard exits (fixed ATR in price — optional)"
input bool InpUseAdverseAtrExit = false; // stop by adverse move in ATR multiples (off = model-only risk)
input bool InpUseProfitAtrExit = false; // take-profit in ATR multiples (needs InpTakeProfitATR > 0)
input double InpMaxAdverseATR = 3.5;
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;
double g_pred_hist[PRED_HIST_CAP][5];
int g_pred_hist_len = 0;
double g_smooth[5] = {0.2, 0.2, 0.2, 0.2, 0.2};
ulong g_bar_index = 0;
int g_entry_cooldown_bars = 0;
int g_agg_w = 4;
int g_sample_n = 2;
int g_min_agg_samples = 2;
void InitDefaultScalerBounds()
{
// MinMax bounds from ai/yt/models/US500_H1_article_split_meta.json (train-only scaler)
double def_min[FEAT_COUNT] = {
1352.5,
1352.5999755859375,
1347.9000244140625,
1352.0999755859375,
0.0,
0.04497450217604637,
-0.030356179922819138,
-0.04839427396655083,
0.00028562467196024954,
-0.047754231840372086,
1.0,
0.00017100000695791095,
0.0,
0.01168255414813757,
0.0760856345295906,
-0.49618232250213623,
-1.6348180770874023,
-1.731970191001892,
0.000006116794793342706,
0.0,
0.0,
0.0,
0.0,
0.0
};
double def_max[FEAT_COUNT] = {
3250.199951171875,
3251.5,
3249.5,
3250.199951171875,
26050000896.0,
0.887104868888855,
0.09538312256336212,
0.10739167034626007,
0.02898731827735901,
0.036042287945747375,
1.0754634141921997,
6759499776.0,
20.0,
0.9637425541877747,
0.8267387747764587,
0.5573697686195374,
1.2618913650512695,
1.8784747123718262,
0.9100509881973267,
1.0,
1.0,
1.0,
1.0,
1.0
};
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("US500 Article EA: loaded InpFeatMinStr (24)");
if(StringLen(InpFeatMaxStr) > 0 && ParseFeatCsv(InpFeatMaxStr, g_feat_max))
Print("US500 Article EA: 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;
}
g_agg_w = MathMax(1, MathMin(InpAggWindow, PRED_HIST_CAP));
g_sample_n = MathMax(1, InpSampleEveryNBars);
g_min_agg_samples = MathMax(1, MathMin(InpMinAggSamples, g_agg_w));
g_pred_hist_len = 0;
g_bar_index = 0;
g_entry_cooldown_bars = 0;
for(int k = 0; k < 5; k++)
g_smooth[k] = 0.2;
const bool has_atr = InpUseAdverseAtrExit || (InpUseProfitAtrExit && InpTakeProfitATR > 0.0);
const bool has_model_exit = InpPureRelative || InpUseCloseHeadExit || InpUseDirFlipExit;
if(!has_atr && !has_model_exit)
Print("US500_H1_ArticleEA: WARNING — no exit path enabled (enable InpPureRelative and/or legacy exits / ATR)");
Print("US500_H1_ArticleEA: ONNX OK. Chart TF=", EnumToString(PERIOD_CURRENT), "; Lookback=", InpLookback,
" sampleEvery=", g_sample_n, " aggWindow=", g_agg_w, " minAggSamples=", g_min_agg_samples,
" pureRelative=", InpPureRelative,
" entryCooldownBars=", InpMinBarsBetweenEntries, " minDirEdge=", InpMinDirEdge,
" stayOverClose=", InpRequireStayOverClose,
" exitMode=", InpExitMode, " minBarsInTradeModelExit=", InpMinBarsInTradeModelExit,
" closeHeadExit=", InpUseCloseHeadExit, " dirFlipExit=", InpUseDirFlipExit, " flipExitEdge=", InpFlipExitEdge,
" minBarsAfterExit=", InpMinBarsAfterExit, " cooldownAfterAdverse=", InpCooldownBarsAfterAdverse,
" useAdverseATR=", InpUseAdverseAtrExit, " useProfitATR=", InpUseProfitAtrExit,
" maxAdverseATR=", InpMaxAdverseATR, " takeProfitATR=", InpTakeProfitATR);
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)
{
if(!InpUseAdverseAtrExit || InpMaxAdverseATR <= 0.0)
return false;
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(!InpUseProfitAtrExit || 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);
// Modes 1/2 (and default): close-long must beat HOLD and stay-long (BUY). Old mode-2 "vs HOLD only" fired almost every bar on softmax.
return (p3 > p0 + InpMinCloseBeatHold && p3 > p1);
}
bool ModelCloseShort(const double p0, const double p2, const double p4)
{
if(InpExitMode == 0)
return (p4 >= InpProbCloseS);
return (p4 > p0 + InpMinCloseBeatHold && p4 > p2);
}
bool ModelDirFlipExitLong(const double p0, const double p1, const double p2)
{
if(!InpUseDirFlipExit)
return false;
const double e = MathMax(0.0, InpFlipExitEdge);
return (p2 > p1 + e && p2 > p0 + InpMinBeatHold);
}
bool ModelDirFlipExitShort(const double p0, const double p1, const double p2)
{
if(!InpUseDirFlipExit)
return false;
const double e = MathMax(0.0, InpFlipExitEdge);
return (p1 > p2 + e && p1 > p0 + InpMinBeatHold);
}
int TrioStrictWinner012(const double p0, const double p1, const double p2)
{
if(p0 > p1 + REL_EPS && p0 > p2 + REL_EPS)
return 0;
if(p1 > p0 + REL_EPS && p1 > p2 + REL_EPS)
return 1;
if(p2 > p0 + REL_EPS && p2 > p1 + REL_EPS)
return 2;
return -1;
}
int FiveStrictWinner01234(const double p0, const double p1, const double p2, const double p3, const double p4)
{
const double p[5] = {p0, p1, p2, p3, p4};
int best = 0;
for(int k = 1; k < 5; k++)
if(p[k] > p[best])
best = k;
const double m = p[best];
int cnt = 0;
for(int k = 0; k < 5; k++)
if(p[k] + REL_EPS >= m)
cnt++;
if(cnt != 1)
return -1;
return best;
}
int PositionBarsInTrade()
{
if(!PositionSelect(_Symbol))
return 0;
const datetime tOpen = (datetime)PositionGetInteger(POSITION_TIME);
const int sh = iBarShift(_Symbol, PERIOD_CURRENT, tOpen, false);
if(sh < 0)
return 9999;
return sh + 1;
}
void ApplyExitCooldown(const bool adverse_stop)
{
int b = MathMax(0, InpMinBarsAfterExit);
if(adverse_stop)
b = MathMax(b, MathMax(0, InpCooldownBarsAfterAdverse));
if(b > 0)
g_entry_cooldown_bars = MathMax(g_entry_cooldown_bars, b);
}
void PushPrediction(const double p0, const double p1, const double p2, const double p3, const double p4, const int maxKeep)
{
for(int i = PRED_HIST_CAP - 1; i > 0; i--)
for(int k = 0; k < 5; k++)
g_pred_hist[i][k] = g_pred_hist[i - 1][k];
g_pred_hist[0][0] = p0;
g_pred_hist[0][1] = p1;
g_pred_hist[0][2] = p2;
g_pred_hist[0][3] = p3;
g_pred_hist[0][4] = p4;
int cap = MathMax(1, MathMin(maxKeep, PRED_HIST_CAP));
g_pred_hist_len = MathMin(g_pred_hist_len + 1, cap);
}
void RecomputeSmooth(const int aggWindow)
{
int w = MathMax(1, MathMin(aggWindow, PRED_HIST_CAP));
int n = MathMin(w, g_pred_hist_len);
if(n < 1)
return;
for(int k = 0; k < 5; k++)
{
double s = 0.0;
for(int i = 0; i < n; i++)
s += g_pred_hist[i][k];
g_smooth[k] = s / (double)n;
}
}
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;
const bool had_pos = PositionSelect(_Symbol);
const bool flat = !had_pos;
if(flat && g_entry_cooldown_bars > 0)
g_entry_cooldown_bars--;
g_bar_index++;
const bool do_sample = (g_sample_n < 2) || ((g_bar_index % (ulong)g_sample_n) == 0);
bool fresh_predict = false;
if(do_sample)
{
matrixf Min;
if(!PrepareMatrix(Min))
{
Print("US500 Article EA: PrepareMatrix failed");
if(!had_pos)
return;
}
else
{
vectorf out;
out.Resize(5);
if(!OnnxRun(g_onnx, ONNX_NO_CONVERSION, Min, out))
{
Print("OnnxRun failed ", GetLastError());
if(!had_pos)
return;
}
else
{
PushPrediction(out[0], out[1], out[2], out[3], out[4], g_agg_w);
RecomputeSmooth(g_agg_w);
fresh_predict = true;
Print("US500 Article H1 raw HOLD=", out[0], " BUY=", out[1], " SELL=", out[2], " CL=", out[3], " CS=", out[4],
" | smooth HOLD=", g_smooth[0], " BUY=", g_smooth[1], " SELL=", g_smooth[2], " CL=", g_smooth[3], " CS=", g_smooth[4]);
}
}
}
const double p0 = g_smooth[0];
const double p1 = g_smooth[1];
const double p2 = g_smooth[2];
const double p3 = g_smooth[3];
const double p4 = g_smooth[4];
if(flat)
{
if(!do_sample || !fresh_predict)
return;
if(g_pred_hist_len < g_min_agg_samples)
return;
if(g_entry_cooldown_bars > 0)
return;
if(InpEntryMode == 1)
{
if(InpPureRelative)
{
const int w3 = TrioStrictWinner012(p0, p1, p2);
if(w3 == 1)
{
if(trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "US500 article BUY"))
g_entry_cooldown_bars = MathMax(0, InpMinBarsBetweenEntries);
}
else if(w3 == 2)
{
if(trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "US500 article SELL"))
g_entry_cooldown_bars = MathMax(0, InpMinBarsBetweenEntries);
}
}
else
{
double dir = MathMax(p1, p2);
if(dir <= p0 + InpMinBeatHold)
return;
const double edge = MathMax(0.0, InpMinDirEdge);
const bool stay_ok_buy = (!InpRequireStayOverClose) || (p1 > p3);
const bool stay_ok_sell = (!InpRequireStayOverClose) || (p2 > p4);
if(p1 >= p2 && p1 > p0 + InpMinBeatHold && (p1 - p2) >= edge && stay_ok_buy)
{
if(trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "US500 article BUY"))
g_entry_cooldown_bars = MathMax(0, InpMinBarsBetweenEntries);
}
else if(p2 > p1 && p2 > p0 + InpMinBeatHold && (p2 - p1) >= edge && stay_ok_sell)
{
if(trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "US500 article SELL"))
g_entry_cooldown_bars = MathMax(0, InpMinBarsBetweenEntries);
}
}
}
else
{
if(p1 >= InpProbBuy && p1 >= p2)
{
if(trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "US500 article BUY"))
g_entry_cooldown_bars = MathMax(0, InpMinBarsBetweenEntries);
}
else if(p2 >= InpProbSell && p2 > p1)
{
if(trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "US500 article SELL"))
g_entry_cooldown_bars = MathMax(0, InpMinBarsBetweenEntries);
}
}
return;
}
long typ = (long)PositionGetInteger(POSITION_TYPE);
double opn = PositionGetDouble(POSITION_PRICE_OPEN);
if(AdverseExit(typ, opn))
{
if(trade.PositionClose(_Symbol))
ApplyExitCooldown(true);
return;
}
if(ProfitExit(typ, opn))
{
if(trade.PositionClose(_Symbol))
ApplyExitCooldown(false);
return;
}
const int bars_in = PositionBarsInTrade();
const bool allow_model_exit = (InpMinBarsInTradeModelExit <= 0) || (bars_in >= InpMinBarsInTradeModelExit);
if(allow_model_exit)
{
bool want_close = false;
if(InpPureRelative)
{
const int w5 = FiveStrictWinner01234(p0, p1, p2, p3, p4);
if(typ == POSITION_TYPE_BUY)
want_close = (w5 != -1 && w5 != 1);
else
want_close = (w5 != -1 && w5 != 2);
}
else
{
if(typ == POSITION_TYPE_BUY)
{
const bool head = InpUseCloseHeadExit && ModelCloseLong(p0, p1, p3);
const bool flip = ModelDirFlipExitLong(p0, p1, p2);
want_close = (head || flip);
}
else
{
const bool head = InpUseCloseHeadExit && ModelCloseShort(p0, p2, p4);
const bool flip = ModelDirFlipExitShort(p0, p1, p2);
want_close = (head || flip);
}
}
if(want_close)
{
if(trade.PositionClose(_Symbol))
ApplyExitCooldown(false);
}
}
}
+30
View File
@@ -0,0 +1,30 @@
; US500_H1_ArticleEA — Strategy Tester preset (fixed inputs, no optimization)
; Copy to: MetaQuotes\Terminal\<instance>\MQL5\Profiles\Tester\
; In Tester: Inputs tab → rightclick → Load → pick this file
;
; Notes:
; - InpLookback must stay 48 unless you retrain/reembed ONNX with another lookback.
; - Use micro lot (0.01) for tests; 1.0 lot caused very large exposure on US500.
; - InpMinBeatHold / InpMinCloseBeatHold > 0 reduce churn when softmax is flat.
;
; Model
InpLookback=48||48||1||48||N
InpEntryMode=1||1||1||1||N
InpProbBuy=0.18||0.18||0.02||0.30||N
InpProbSell=0.18||0.18||0.02||0.30||N
InpMinBeatHold=0.04||0.04||0.01||0.10||N
InpExitMode=2||2||1||2||N
InpProbCloseL=0.18||0.18||0.02||0.30||N
InpProbCloseS=0.18||0.18||0.02||0.30||N
InpMinCloseBeatHold=0.03||0.03||0.01||0.08||N
; Session (match Python SESSION_HOUR_OFFSET)
InpSessionHourOffset=0||0||1||1||N
; Scaler override (empty = builtin scaler from US500_H1_article_split_meta.json)
InpFeatMinStr=
InpFeatMaxStr=
; Risk
InpLotSize=0.01||0.01||0.01||0.10||N
InpMagic=902503||902503||1||902503||N
InpSlippage=30||30||1||300||N
InpMaxAdverseATR=2.0||2.0||0.25||4.0||N
InpTakeProfitATR=0.0||0.0||0.25||3.0||N
+24
View File
@@ -0,0 +1,24 @@
; US500_H1_ArticleEA — genetic / slow optimization preset
; InpLookback fixed at 48 (must match embedded ONNX). Copy to MQL5\Profiles\Tester\
;
; Model
InpLookback=48||48||1||48||N
InpEntryMode=1||0||1||1||Y
InpProbBuy=0.18||0.12||0.02||0.28||Y
InpProbSell=0.18||0.12||0.02||0.28||Y
InpMinBeatHold=0.04||0.0||0.01||0.10||Y
InpExitMode=2||0||1||2||Y
InpProbCloseL=0.18||0.12||0.02||0.28||Y
InpProbCloseS=0.18||0.12||0.02||0.28||Y
InpMinCloseBeatHold=0.03||0.0||0.01||0.08||Y
; Session (match Python SESSION_HOUR_OFFSET)
InpSessionHourOffset=0||-2||1||2||N
; Scaler override (leave empty unless you paste new train bounds)
InpFeatMinStr=
InpFeatMaxStr=
; Risk
InpLotSize=0.01||0.01||0.01||0.10||N
InpMagic=902503||902503||1||902503||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
+30
View File
@@ -0,0 +1,30 @@
# ENKS / clustering article — notes → training in this repo
Summary of the methodology described in the article (MQL5 / ENKS trader clusters):
- Models are trained in **Python**, then converted to **ENKS** for the MetaTrader include/bot stack. This repository does **not** ship an ENKS encoder; training here exports **ONNX + JSON meta + scaler** like `ai/xauusd_h1/`. Convert ENKS with the authors tool or workflow from the article.
- **Clustering** (article: Cayley / trade matching): use **forward-return fingerprints** per bar and **KMeans** on the in-sample window only, then optional **per-cluster balancing** of sample weights during training (see `train_article_split.py`).
- **Windows**: train **2010-01-01 → 2019-12-31**; out-of-sample / forward **2020-01-01 → 2024-12-31**. Scaler is fit **only** on the train window (no leakage).
- **Capital / Capodon-style US H1**: default symbol `US500` on **H1**; override with `YT_SYMBOL`. The article notes models can be attached on other timeframes; EA SL/TP and filters are tuned separately.
- **Includes (`tendq`, etc.)**: not present in this repo; wire your ONNX EA to the exported `*_meta.json` and scaler like the existing XAUUSD H1 action EA.
## Run training
From `ai/yt` (MetaTrader 5 must be installed and history available for the symbol):
```bash
pip install -r requirements.txt
python train_article_split.py
```
Environment overrides:
| Variable | Default | Meaning |
|----------------|----------------|----------------------------------|
| `YT_SYMBOL` | `US500` | MT5 symbol |
| `YT_LOOKBACK` | `48` | Sequence length (bars) |
| `YT_EPOCHS` | `40` | Max epochs |
| `YT_BATCH` | `64` | Batch size |
| `YT_CLUSTERS` | `12` | KMeans clusters (0 = disable) |
Outputs: `ai/yt/models/<SYMBOL>_H1_article_split.onnx`, scaler `.pkl`, `*_meta.json`.
Binary file not shown.
@@ -0,0 +1,131 @@
{
"symbol": "US500",
"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"
],
"train_window": [
"2010-01-01",
"2020-01-01"
],
"oos_window": [
"2020-01-01",
"2025-01-01"
],
"scaler_fit_on": "train_only_rows_before_2020",
"clustering": "KMeans n=12 on forward returns (1,2,4,8,16) ATR-norm; sample reweight train",
"scaler_feature_min": [
1352.5,
1352.5999755859375,
1347.9000244140625,
1352.0999755859375,
0.0,
0.04497450217604637,
-0.030356179922819138,
-0.04839427396655083,
0.00028562467196024954,
-0.047754231840372086,
1.0,
0.00017100000695791095,
0.0,
0.01168255414813757,
0.0760856345295906,
-0.49618232250213623,
-1.6348180770874023,
-1.731970191001892,
6.116794793342706e-06,
0.0,
0.0,
0.0,
0.0,
0.0
],
"scaler_feature_max": [
3250.199951171875,
3251.5,
3249.5,
3250.199951171875,
26050000896.0,
0.887104868888855,
0.09538312256336212,
0.10739167034626007,
0.02898731827735901,
0.036042287945747375,
1.0754634141921997,
6759499776.0,
20.0,
0.9637425541877747,
0.8267387747764587,
0.5573697686195374,
1.2618913650512695,
1.8784747123718262,
0.9100509881973267,
1.0,
1.0,
1.0,
1.0,
1.0
],
"scaler_scale": [
0.0005269537214189768,
0.0005266206571832299,
0.0005258729797787964,
0.0005268426612019539,
3.8387713147125524e-11,
1.1874645948410034,
7.952962398529053,
6.419064044952393,
34.84115219116211,
11.933670043945312,
13.25145435333252,
1.4793993807771244e-10,
0.05000000074505806,
1.05035400390625,
1.332173228263855,
0.949169933795929,
0.34521928429603577,
0.2769741714000702,
1.0988469123840332,
1.0,
1.0,
1.0,
1.0,
1.0
],
"oos_val_accuracy": 0.293920636177063,
"oos_val_loss": 1.5438036918640137,
"notes": "ONNX for repo EAs; convert to ENKS externally if required."
}
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
-r ../xauusd_h1/requirements.txt
+315
View File
@@ -0,0 +1,315 @@
"""
Article-style training: chronological train (20102019) vs OOS (20202024),
optional KMeans on forward-return fingerprints (trade-shape clustering),
ONNX export compatible with the repo's MT5 ONNX EAs.
Reuses feature + label definitions from ai/xauusd_h1 (24 features, 5 classes).
ENKS conversion is out of scope — use the article's tooling after ONNX if needed.
"""
from __future__ import annotations
import json
import os
import pickle
import sys
from datetime import datetime, timedelta
from pathlib import Path
import MetaTrader5 as mt5
import numpy as np
import pandas as pd
import tensorflow as tf
import tf2onnx
import onnx
from sklearn.cluster import KMeans
from sklearn.preprocessing import MinMaxScaler
from tensorflow import keras
from tensorflow.keras import layers
from tqdm import tqdm
# Reuse XAUUSD H1 stack (same 24 dims / 5 classes as published EAs).
_XH1 = Path(__file__).resolve().parent.parent / "xauusd_h1"
sys.path.insert(0, str(_XH1))
from features import NUM_FEATURES, prepare_features_full # noqa: E402
from labeling import atr_series, class_weights, compute_action_labels # noqa: E402
NUM_CLASSES = 5
CLASS_NAMES = ["HOLD", "BUY", "SELL_SHORT", "CLOSE_LONG", "CLOSE_SHORT"]
TRAIN_START = pd.Timestamp("2010-01-01")
TRAIN_END = pd.Timestamp("2020-01-01") # exclusive: train < this
OOS_END = pd.Timestamp("2025-01-01") # exclusive: val < this (covers through 2024)
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 symbol history in MT5")
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 forward_return_fingerprints(
df: pd.DataFrame,
feat_index: pd.DatetimeIndex,
horizons: tuple[int, ...] = (1, 2, 4, 8, 16),
) -> tuple[np.ndarray, np.ndarray]:
"""Normalized forward returns at listed horizons; aligned to feat rows."""
close = df["close"].to_numpy(dtype=np.float64)
atr = atr_series(df, 14).to_numpy(dtype=np.float64)
pos = df.index.get_indexer(feat_index)
n = len(feat_index)
d = len(horizons)
M = np.zeros((n, d), dtype=np.float64)
valid = np.ones(n, dtype=bool)
max_h = max(horizons)
for j, i in enumerate(pos):
if i < 0 or i + max_h >= len(close):
valid[j] = False
continue
a = float(atr[i]) if np.isfinite(atr[i]) and atr[i] > 0 else close[i] * 1e-4
for k, h in enumerate(horizons):
if i + h >= len(close):
valid[j] = False
break
M[j, k] = (close[i + h] - close[i]) / a
return M, valid
def create_sequences(
X: np.ndarray,
y: np.ndarray,
times: np.ndarray,
lookback: int,
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
xs, ys, t_end = [], [], []
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])
t_end.append(times[i])
return (
np.asarray(xs, dtype=np.float32),
np.asarray(ys, dtype=np.int64),
np.asarray(t_end),
)
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("YT_SYMBOL", "US500")
lookback = int(os.environ.get("YT_LOOKBACK", "48"))
epochs = int(os.environ.get("YT_EPOCHS", "40"))
batch_size = int(os.environ.get("YT_BATCH", "64"))
n_clusters = int(os.environ.get("YT_CLUSTERS", "12"))
fetch_start = datetime(2009, 6, 1)
fetch_end = datetime(2025, 1, 1)
out_dir = Path(__file__).resolve().parent / "models"
out_dir.mkdir(parents=True, exist_ok=True)
onnx_path = out_dir / f"{symbol}_H1_article_split.onnx"
meta_path = out_dir / f"{symbol}_H1_article_split_meta.json"
print(f"Symbol={symbol} H1 | train [{TRAIN_START.date()} , {TRAIN_END.date()}) | OOS [{TRAIN_END.date()} , {OOS_END.date()})")
print("Fetching MT5 …")
try:
raw = fetch_mt5_range(symbol, mt5.TIMEFRAME_H1, fetch_start, fetch_end)
finally:
mt5.shutdown()
raw = raw.loc[raw.index >= TRAIN_START]
if len(raw) < 500:
print("ERROR: Not enough H1 bars after 2010 — check symbol and History Center.")
return 1
print(f"Bars: {len(raw)} range: {raw.index[0]}{raw.index[-1]}")
feat = prepare_features_full(raw)
labels = compute_action_labels(raw).loc[feat.index]
y = labels.values.astype(np.int64)
X_raw = feat.values.astype(np.float32)
times = feat.index.to_numpy()
valid = np.isfinite(X_raw).all(axis=1) & (y >= 0) & (y < NUM_CLASSES)
X_raw = X_raw[valid]
y = y[valid]
times = times[valid]
train_row = times < np.datetime64(TRAIN_END)
if int(train_row.sum()) < 800:
print("ERROR: Too few train rows before 2020 — need deeper history.")
return 1
scaler = MinMaxScaler()
scaler.fit(X_raw[train_row])
Xn = scaler.transform(X_raw).astype(np.float32)
X_seq, y_seq, t_end = create_sequences(Xn, y, times, lookback)
if len(X_seq) < 500:
print("ERROR: Too few sequences.")
return 1
train_m = t_end < np.datetime64(TRAIN_END)
val_m = (t_end >= np.datetime64(TRAIN_END)) & (t_end < np.datetime64(OOS_END))
X_train, y_train = X_seq[train_m], y_seq[train_m]
X_val, y_val = X_seq[val_m], y_seq[val_m]
if len(X_val) < 200:
print("ERROR: Too few OOS sequences in 20202024.")
return 1
print(f"Sequences train={len(X_train)} val(OOS)={len(X_val)} lookback={lookback}")
cw = class_weights(y_train, NUM_CLASSES)
tr_idx = np.flatnonzero(train_m)
base_w = np.array([cw[int(y_seq[i])] for i in tr_idx], dtype=np.float32)
sample_w = base_w.copy()
if n_clusters > 1:
fp, fp_ok = forward_return_fingerprints(raw, pd.DatetimeIndex(times))
fp_seq = fp[lookback - 1 :]
ok_seq = fp_ok[lookback - 1 :]
fp_tr = fp_seq[tr_idx]
ok_tr = ok_seq[tr_idx]
fit_mask = ok_tr & np.isfinite(fp_tr).all(axis=1)
if int(fit_mask.sum()) >= n_clusters * 5:
km = KMeans(n_clusters=n_clusters, random_state=42, n_init=10)
km.fit(fp_tr[fit_mask])
labels_tr = np.full(len(tr_idx), -1, dtype=np.int32)
labels_tr[fit_mask] = km.predict(fp_tr[fit_mask])
counts = np.zeros(n_clusters, dtype=np.float64)
for c in labels_tr:
if 0 <= c < n_clusters:
counts[c] += 1.0
counts = np.maximum(counts, 1.0)
total_assigned = max(int((labels_tr >= 0).sum()), 1)
w_cl = np.ones(len(tr_idx), dtype=np.float32)
for j in range(len(tr_idx)):
c = int(labels_tr[j])
if c >= 0:
w_cl[j] = float(total_assigned / (n_clusters * counts[c]))
sample_w = base_w * w_cl
sample_w *= len(sample_w) / float(np.sum(sample_w))
print(
f"KMeans trade-fingerprint clusters={n_clusters} "
"(fit on in-sample train only; sample_weight × class balance)"
)
else:
print("Skipping KMeans: not enough valid fingerprint rows in train.")
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=10, restore_best_weights=True
),
keras.callbacks.ReduceLROnPlateau(
monitor="val_loss", factor=0.5, patience=4, min_lr=1e-6
),
],
)
loss, acc = model.evaluate(X_val, y_val, verbose=0)
print(f"OOS val_loss={loss:.4f} val_accuracy={acc:.4f}")
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, str(onnx_path))
with open(str(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,
"train_window": [str(TRAIN_START.date()), str(TRAIN_END.date())],
"oos_window": [str(TRAIN_END.date()), str(OOS_END.date())],
"scaler_fit_on": "train_only_rows_before_2020",
"clustering": f"KMeans n={n_clusters} on forward returns (1,2,4,8,16) ATR-norm; sample reweight train" if n_clusters > 1 else "disabled",
"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,
"oos_val_accuracy": float(acc),
"oos_val_loss": float(loss),
"notes": "ONNX for repo EAs; convert to ENKS externally if required.",
}
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 (%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}")
return 0
if __name__ == "__main__":
sys.exit(main())
View File
@@ -1,607 +0,0 @@
//+------------------------------------------------------------------+
//| 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", "MSFT", "NVDA", "TSLA", "GOOGL", "AMZN", "META", "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,27 @@
; saved on 2026.04.22
; genetic optimization set for DarvasBoxXAUUSD/main.mq5
; load in MT5 Strategy Tester -> Inputs -> Load
;
; === Core Darvas Box Parameters ===
BoxPeriod=165||80||5||280||Y
BoxDeviation=25140||8000||500||50000||Y
VolumeThreshold=938||200||25||2500||Y
StopLoss=1665||600||50||4000||Y
TakeProfit=3685||1200||75||8000||Y
EnableLogging=false||false||0||true||N
BoxColor=255||0||1||16777215||N
BoxWidth=1||1||1||3||N
;
; === Trend Confirmation Parameters ===
TrendTimeframe=16386||16385||1||16388||Y
MA_Period=125||20||5||260||Y
MA_Method=1||0||1||3||Y
MA_Price=6||0||1||6||Y
TrendThreshold=4.94||1.0||0.2||20.0||Y
;
; === Volume Analysis Parameters ===
VolumeMA_Period=110||20||5||220||Y
VolumeThresholdMultiplier=1.5||1.0||0.1||3.5||Y
;
; === Execution / ID ===
MagicNumber=135790||135790||1||1357900||N
@@ -0,0 +1,443 @@
//+------------------------------------------------------------------+
//| DarvasBox.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"
#property strict
#include <Trade\Trade.mqh>
#include <Indicators\Trend.mqh>
#include <Indicators\Volumes.mqh>
#include "../_united/MagicNumberHelpers.mqh"
// Input parameters
input int BoxPeriod = 165; // Period for Darvas Box calculation
input double BoxDeviation = 25140; // Box deviation in points
input int VolumeThreshold = 938; // Minimum volume for confirmation
input double StopLoss = 1665; // Stop loss in points (increased for BTCUSD)
input double TakeProfit = 3685; // Take profit in points (increased for BTCUSD)
input bool EnableLogging = false; // Enable detailed logging
input color BoxColor = clrBlue; // Color for Darvas Box
input int BoxWidth = 1; // Width of box lines
// Trend confirmation parameters
input ENUM_TIMEFRAMES TrendTimeframe = PERIOD_H2; // Timeframe for trend analysis
input int MA_Period = 125; // Moving Average period for trend
input ENUM_MA_METHOD MA_Method = MODE_EMA; // Moving Average method
input ENUM_APPLIED_PRICE MA_Price = PRICE_WEIGHTED; // Price type for MA
input double TrendThreshold = 4.94; // Trend strength threshold
// Volume analysis parameters
input int VolumeMA_Period = 110; // Period for Volume MA
input double VolumeThresholdMultiplier = 1.5; // Volume spike threshold
// Magic Number
input int MagicNumber = 135790; // Magic Number for Trades
// Global variables
double boxHigh = 0;
double boxLow = 0;
bool boxFormed = false;
datetime lastBoxTime = 0;
string boxName = "DarvasBox_";
double minStopLevel = 0;
double point = 0;
CTrade trade;
// Indicator handles
int maHandle;
int volumeHandle;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize indicators and variables
boxHigh = 0;
boxLow = 0;
boxFormed = false;
lastBoxTime = 0;
// Get symbol properties
point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
minStopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * point;
// Initialize indicators
maHandle = iMA(_Symbol, TrendTimeframe, MA_Period, 0, MA_Method, MA_Price);
volumeHandle = iVolumes(_Symbol, PERIOD_CURRENT, VOLUME_TICK);
if(maHandle == INVALID_HANDLE || volumeHandle == INVALID_HANDLE)
{
Print("Error creating indicators");
return(INIT_FAILED);
}
// Configure trade object
trade.SetDeviationInPoints(10);
trade.SetTypeFilling(ORDER_FILLING_IOC);
trade.SetAsyncMode(false);
trade.SetExpertMagicNumber(MagicNumber);
if(EnableLogging)
{
Print("Darvas Box Expert Advisor initialized");
Print("Symbol: ", _Symbol);
Print("Point: ", point);
Print("Minimum Stop Level: ", minStopLevel);
}
// Delete any existing box objects
ObjectsDeleteAll(0, boxName);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Draw Darvas Box on chart |
//+------------------------------------------------------------------+
void DrawDarvasBox()
{
if(!boxFormed) return;
datetime time1 = iTime(_Symbol, PERIOD_H1, BoxPeriod);
datetime time2 = iTime(_Symbol, PERIOD_H1, 0);
// Delete old box
ObjectsDeleteAll(0, boxName);
// Draw box
ObjectCreate(0, boxName + "Top", OBJ_TREND, 0, time1, boxHigh, time2, boxHigh);
ObjectCreate(0, boxName + "Bottom", OBJ_TREND, 0, time1, boxLow, time2, boxLow);
// Set box properties
ObjectSetInteger(0, boxName + "Top", OBJPROP_COLOR, BoxColor);
ObjectSetInteger(0, boxName + "Bottom", OBJPROP_COLOR, BoxColor);
ObjectSetInteger(0, boxName + "Top", OBJPROP_WIDTH, BoxWidth);
ObjectSetInteger(0, boxName + "Bottom", OBJPROP_WIDTH, BoxWidth);
ObjectSetInteger(0, boxName + "Top", OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, boxName + "Bottom", OBJPROP_RAY_RIGHT, true);
}
//+------------------------------------------------------------------+
//| Calculate Darvas Box levels |
//+------------------------------------------------------------------+
void CalculateDarvasBox()
{
double high = 0;
double low = DBL_MAX;
// Find highest high and lowest low in the period
for(int i = 0; i < BoxPeriod; i++)
{
high = MathMax(high, iHigh(_Symbol, PERIOD_H1, i));
low = MathMin(low, iLow(_Symbol, PERIOD_H1, i));
}
double range = high - low;
double allowedRange = BoxDeviation * _Point;
if(EnableLogging)
{
Print("Box Calculation - High: ", high, " Low: ", low, " Range: ", range, " Allowed Range: ", allowedRange);
}
// Check if box is formed
if(range <= allowedRange)
{
boxHigh = high;
boxLow = low;
boxFormed = true;
lastBoxTime = iTime(_Symbol, PERIOD_CURRENT, 0);
// Draw the box
DrawDarvasBox();
if(EnableLogging)
Print("Box Formed - High: ", boxHigh, " Low: ", boxLow, " Time: ", lastBoxTime);
}
else
{
boxFormed = false;
// Delete box if it exists
ObjectsDeleteAll(0, boxName);
}
}
//+------------------------------------------------------------------+
//| Validate and adjust stop levels |
//+------------------------------------------------------------------+
bool ValidateStopLevels(double price, double &sl, double &tp, ENUM_ORDER_TYPE orderType)
{
double minSlDistance = MathMax(minStopLevel, StopLoss * point);
double minTpDistance = MathMax(minStopLevel, TakeProfit * point);
if(EnableLogging)
{
Print("Minimum SL Distance: ", minSlDistance);
Print("Minimum TP Distance: ", minTpDistance);
}
// Adjust stop loss
if(orderType == ORDER_TYPE_BUY)
{
sl = price - minSlDistance;
tp = price + minTpDistance;
if(EnableLogging)
{
Print("Buy Order Levels:");
Print("Entry: ", price);
Print("Stop Loss: ", sl);
Print("Take Profit: ", tp);
}
}
else // ORDER_TYPE_SELL
{
sl = price + minSlDistance;
tp = price - minTpDistance;
if(EnableLogging)
{
Print("Sell Order Levels:");
Print("Entry: ", price);
Print("Stop Loss: ", sl);
Print("Take Profit: ", tp);
}
}
return true;
}
//+------------------------------------------------------------------+
//| Check trend direction and strength |
//+------------------------------------------------------------------+
bool IsTrendFavorable(ENUM_ORDER_TYPE orderType)
{
double ma[];
ArraySetAsSeries(ma, true);
if(CopyBuffer(maHandle, 0, 0, 2, ma) <= 0)
return false;
double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double trendStrength = MathAbs(currentPrice - ma[0]) / point;
if(EnableLogging)
Print("Trend Strength: ", trendStrength);
if(orderType == ORDER_TYPE_BUY)
return (currentPrice > ma[0] && trendStrength > TrendThreshold);
else
return (currentPrice < ma[0] && trendStrength > TrendThreshold);
}
//+------------------------------------------------------------------+
//| Check volume conditions |
//+------------------------------------------------------------------+
bool CheckVolumeConditions()
{
double volumes[];
ArraySetAsSeries(volumes, true);
if(CopyBuffer(volumeHandle, 0, 0, VolumeMA_Period + 1, volumes) <= 0)
return false;
double volumeMA = 0;
for(int i = 1; i <= VolumeMA_Period; i++)
volumeMA += volumes[i];
volumeMA /= VolumeMA_Period;
double currentVolume = volumes[0];
double volumeRatio = currentVolume / volumeMA;
if(EnableLogging)
Print("Volume Ratio: ", volumeRatio);
return (volumeRatio > VolumeThresholdMultiplier);
}
//+------------------------------------------------------------------+
//| Place trade order |
//+------------------------------------------------------------------+
bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price, double sl, double tp)
{
// Validate and adjust stop levels
if(!ValidateStopLevels(price, sl, tp, orderType))
{
if(EnableLogging)
Print("Invalid stop levels after adjustment");
return false;
}
// Check trend and volume conditions
if(!IsTrendFavorable(orderType))
{
if(EnableLogging)
Print("Trend not favorable for trade");
return false;
}
if(!CheckVolumeConditions())
{
if(EnableLogging)
Print("Volume conditions not met");
return false;
}
if(EnableLogging)
{
Print("Order Details:");
Print("Type: ", EnumToString(orderType));
Print("Price: ", price);
Print("Stop Loss: ", sl);
Print("Take Profit: ", tp);
}
bool result = false;
if(orderType == ORDER_TYPE_BUY)
{
result = trade.Buy(0.01, _Symbol, price, sl, tp, "Darvas Box Breakout");
}
else
{
result = trade.Sell(0.01, _Symbol, price, sl, tp, "Darvas Box Breakdown");
}
if(EnableLogging)
{
if(result)
Print((orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"), " Order Placed Successfully");
else
Print((orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"), " Order Failed - Error: ", trade.ResultRetcode(), " Description: ", trade.ResultRetcodeDescription());
}
return result;
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Calculate new box levels
CalculateDarvasBox();
// Check for trading signals
if(boxFormed)
{
double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double currentVolume = iVolume(_Symbol, PERIOD_CURRENT, 0);
if(EnableLogging)
{
Print("Current Price: ", currentPrice, " Box High: ", boxHigh, " Box Low: ", boxLow);
Print("Current Volume: ", currentVolume, " Volume Threshold: ", VolumeThreshold);
}
// Check for breakout above box
if(currentPrice > boxHigh && currentVolume > VolumeThreshold)
{
if(EnableLogging)
Print("Breakout Signal Detected - Price above box high");
// Buy signal
if(!PositionExistsByMagic(_Symbol, MagicNumber)) // No existing positions with our magic number
{
double sl = currentPrice - StopLoss * _Point;
double tp = currentPrice + TakeProfit * _Point;
if(EnableLogging)
Print("Preparing Buy Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
PlaceOrder(ORDER_TYPE_BUY, currentPrice, sl, tp);
}
else if(EnableLogging)
Print("Skipping Buy Signal - Position already exists");
}
// Check for breakdown below box
if(currentPrice < boxLow && currentVolume > VolumeThreshold)
{
if(EnableLogging)
Print("Breakdown Signal Detected - Price below box low");
// Sell signal
if(!PositionExistsByMagic(_Symbol, MagicNumber)) // No existing positions with our magic number
{
double sl = currentPrice + StopLoss * _Point;
double tp = currentPrice - TakeProfit * _Point;
if(EnableLogging)
Print("Preparing Sell Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
PlaceOrder(ORDER_TYPE_SELL, currentPrice, sl, tp);
}
else if(EnableLogging)
Print("Skipping Sell Signal - Position already exists");
}
}
else if(EnableLogging)
Print("No Box Formed - Waiting for consolidation");
}
//+------------------------------------------------------------------+
//| Get last error description |
//+------------------------------------------------------------------+
string GetLastErrorDescription()
{
string errorDescription;
switch(GetLastError())
{
case 0: errorDescription = "No error"; break;
case 1: errorDescription = "No error, but result unknown"; break;
case 2: errorDescription = "Common error"; break;
case 3: errorDescription = "Invalid trade parameters"; break;
case 4: errorDescription = "Trade server is busy"; break;
case 5: errorDescription = "Old version of the client terminal"; break;
case 6: errorDescription = "No connection with trade server"; break;
case 7: errorDescription = "Not enough rights"; break;
case 8: errorDescription = "Too frequent requests"; break;
case 9: errorDescription = "Malfunctional trade operation"; break;
case 64: errorDescription = "Account disabled"; break;
case 65: errorDescription = "Invalid account"; break;
case 128: errorDescription = "Trade timeout"; break;
case 129: errorDescription = "Invalid price"; break;
case 130: errorDescription = "Invalid stops"; break;
case 131: errorDescription = "Invalid trade volume"; break;
case 132: errorDescription = "Market is closed"; break;
case 133: errorDescription = "Trade is disabled"; break;
case 134: errorDescription = "Not enough money"; break;
case 135: errorDescription = "Price changed"; break;
case 136: errorDescription = "Off quotes"; break;
case 137: errorDescription = "Broker is busy"; break;
case 138: errorDescription = "Requote"; break;
case 139: errorDescription = "Order is locked"; break;
case 140: errorDescription = "Long positions only allowed"; break;
case 141: errorDescription = "Too many requests"; break;
case 145: errorDescription = "Modification denied because order is too close to market"; break;
case 146: errorDescription = "Trade context is busy"; break;
case 147: errorDescription = "Expirations are denied by broker"; break;
case 148: errorDescription = "Amount of open and pending orders has reached the limit"; break;
case 149: errorDescription = "Hedging is prohibited"; break;
case 150: errorDescription = "Prohibited by FIFO rules"; break;
default: errorDescription = "Unknown error"; break;
}
return errorDescription;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Delete all box objects
ObjectsDeleteAll(0, boxName);
if(EnableLogging)
Print("Expert Advisor deinitialized - Reason: ", reason);
}
@@ -0,0 +1,628 @@
//+------------------------------------------------------------------+
//| EMACrossOver.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"
#include <Trade\Trade.mqh>
#include "../_united/MagicNumberHelpers.mqh"
//--- Eingabeparameter (Input Parameters) - Optimized Profitable Parameters
input int EMA_Periode = 50; // EMA Periode
input double PreisSchwelle = 700.0; // Preisbewegung Schwelle in Pips
input double SteigungSchwelle = 25.0; // EMA Steigung Schwelle in Pips
input int ÜberwachungTimeout = 340; // Überwachungszeit in Sekunden
input double TrailingStop = 370.0; // Gleitender Stop in Pips
input bool UseTrailingStop = true; // Gleitenden Stop anwenden
input double TrailingActivationPips = 0.0; // Mindestgewinn in Pips bis Trail startet (0 = Konto-Profit>0)
input bool UseStaleStopLossExit = false; // schließen wenn SL zu lange nicht angepasst wurde
input int StaleStopLossSeconds = 33800; // Sekunden ohne SL-Änderung -> Close (0 = aus)
input double LotGröße = 0.07; // Handelsvolumen
input int MagicNumber = 135790; // Magic Number für Trades
input bool UseSpreadAdjustment = true; // Spread-Anpassung verwenden
input ENUM_TIMEFRAMES Timeframe = PERIOD_H1; // Zeitraum für Analyse
input bool UseBarData = true; // Bar-Daten statt Tick-Daten verwenden
input int MaxTradesPerCrossover = 3; // Maximale Trades pro Crossover-Ereignis
input int ProfitCheckBars = 11; // Bars bis zur Profit-Prüfung
input bool CloseUnprofitableTrades = true; // Unprofitable Trades nach X Bars schließen
input bool UseWeeklyADXFilter = true; // W1 ADX Trendfilter aktivieren
input int WeeklyADXPeriod = 15; // ADX-Periode auf W1
input double WeeklyADXMin = 40.0; // Minimaler ADX fuer Trendfreigabe
input int WeeklyADXBarShift = 2; // 1=letzte geschlossene W1-Kerze
input bool WeeklyADXUseDirection = true; // +DI/-DI Richtung mitpruefen
//--- Globale Variablen (Global Variables)
int ema_handle; // EMA Indicator Handle
double ema_array[]; // Array für EMA
datetime letzte_überwachung_zeit; // Zeit der letzten Überwachung
bool überwachung_aktiv = false; // Überwachungsstatus
bool preis_trigger_aktiv = false; // Preis-Trigger Status
bool steigung_trigger_aktiv = false; // Steigungs-Trigger Status
int ticket = 0; // Trade Ticket
CTrade trade; // CTrade Objekt
int trades_in_current_crossover = 0; // Anzahl Trades im aktuellen Crossover
bool crossover_detected = false; // Crossover erkannt
datetime trade_open_time = 0; // Zeitpunkt des Trade-Öffnens
datetime g_last_sl_adjust_success_time = 0; // letzte erfolgreiche SL-Verschiebung (Stale-Exit)
//+------------------------------------------------------------------+
//| Weekly ADX trend filter |
//+------------------------------------------------------------------+
bool IsWeeklyADXTrendFavorable(ENUM_ORDER_TYPE order_type)
{
if(!UseWeeklyADXFilter)
return true;
int adxShift = WeeklyADXBarShift;
if(adxShift < 0)
adxShift = 0;
int adx_handle = iADX(_Symbol, PERIOD_W1, WeeklyADXPeriod);
if(adx_handle == INVALID_HANDLE)
{
Print("TRACE: Weekly ADX Handle ungültig - Filter blockiert Entry");
return false;
}
double adx_buf[], plus_di_buf[], minus_di_buf[];
ArraySetAsSeries(adx_buf, true);
ArraySetAsSeries(plus_di_buf, true);
ArraySetAsSeries(minus_di_buf, true);
bool ok_adx = (CopyBuffer(adx_handle, 0, adxShift, 1, adx_buf) > 0);
bool ok_plus = (CopyBuffer(adx_handle, 1, adxShift, 1, plus_di_buf) > 0);
bool ok_minus = (CopyBuffer(adx_handle, 2, adxShift, 1, minus_di_buf) > 0);
IndicatorRelease(adx_handle);
if(!ok_adx || !ok_plus || !ok_minus)
{
Print("TRACE: Weekly ADX Daten nicht verfügbar - Filter blockiert Entry");
return false;
}
double adx_value = adx_buf[0];
double plus_di = plus_di_buf[0];
double minus_di = minus_di_buf[0];
bool strength_ok = (adx_value >= WeeklyADXMin);
bool direction_ok = true;
if(WeeklyADXUseDirection)
{
if(order_type == ORDER_TYPE_BUY)
direction_ok = (plus_di > minus_di);
else
direction_ok = (minus_di > plus_di);
}
Print("TRACE: Weekly ADX Filter | ADX=", DoubleToString(adx_value, 2),
" +DI=", DoubleToString(plus_di, 2),
" -DI=", DoubleToString(minus_di, 2),
" strength_ok=", strength_ok,
" direction_ok=", direction_ok);
return (strength_ok && direction_ok);
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- CTrade konfigurieren (Configure CTrade)
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(10);
trade.SetTypeFilling(ORDER_FILLING_IOC);
//--- EMA Indicator Handle erstellen (Create EMA indicator handle)
ema_handle = iMA(_Symbol, Timeframe, EMA_Periode, 0, MODE_EMA, PRICE_CLOSE);
if(ema_handle == INVALID_HANDLE)
{
Print("Fehler beim Erstellen des EMA Indicators");
return(INIT_FAILED);
}
//--- Arrays initialisieren (Initialize arrays)
ArraySetAsSeries(ema_array, true);
//--- Arrays mit aktuellen Werten füllen (Fill arrays with current values)
BerechneEMA();
Print("EMA EA initialisiert - Periode: ", EMA_Periode, " Timeframe: ", EnumToString(Timeframe), " Handle: ", ema_handle);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Indicator Handle freigeben (Release indicator handle)
if(ema_handle != INVALID_HANDLE)
{
IndicatorRelease(ema_handle);
}
Print("EA beendet - Grund: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
static datetime last_bar_time = 0;
const datetime current_bar_time = iTime(_Symbol, Timeframe, 0);
const bool new_bar = (current_bar_time != last_bar_time);
const bool has_position = PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
// Offene Positionen: Management jeden Tick (Trailing / Stale-SL). Sonst bei Bar-Modus nur neuer Bar.
if(UseBarData)
{
if(!new_bar && !has_position)
return;
if(new_bar)
last_bar_time = current_bar_time;
}
//--- EMA Werte berechnen (Calculate EMA values)
BerechneEMA();
const bool run_signals = (!UseBarData || new_bar);
//--- Debug / Überwachung / Entry nur bei neuem Bar (Bar-Modus) oder jeden Tick (Tick-Modus)
if(run_signals && ArraySize(ema_array) > 0)
{
double aktueller_close = iClose(_Symbol, Timeframe, 0);
double ema_aktuell = ema_array[0];
double ema_vorher = ema_array[1];
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / _Point;
double steigung = (ema_aktuell - ema_vorher) / _Point;
if(UseBarData)
{
Print("=== DEBUG INFO (Neuer Bar) ===");
Print("Bar Zeit: ", TimeToString(iTime(_Symbol, 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: ", preis_trigger_aktiv, " Steigungs-Trigger: ", steigung_trigger_aktiv);
Print("Überwachung aktiv: ", überwachung_aktiv);
Print("Position offen: ", PositionExistsByMagic(_Symbol, (ulong)MagicNumber));
Print("Trades im aktuellen Crossover: ", trades_in_current_crossover, "/", MaxTradesPerCrossover);
Print("==================");
}
if(run_signals)
{
//--- Überwachung prüfen (Check monitoring)
if(überwachung_aktiv)
{
if(UseBarData)
{
int bars_since_monitoring = iBarShift(_Symbol, Timeframe, letzte_überwachung_zeit);
int timeout_bars = (int)(ÜberwachungTimeout / PeriodSeconds(Timeframe));
if(bars_since_monitoring > timeout_bars)
{
überwachung_aktiv = false;
preis_trigger_aktiv = false;
steigung_trigger_aktiv = false;
Print("Überwachung beendet - Bar-basierte Zeitüberschreitung (", bars_since_monitoring, " Bars)");
}
}
else
{
if(TimeCurrent() - letzte_überwachung_zeit > ÜberwachungTimeout)
{
überwachung_aktiv = false;
preis_trigger_aktiv = false;
steigung_trigger_aktiv = false;
Print("Überwachung beendet - Tick-basierte Zeitüberschreitung");
}
}
}
PrüfeTrigger();
}
VerwalteTrades();
}
//+------------------------------------------------------------------+
//| EMA Berechnung (EMA Calculation) |
//+------------------------------------------------------------------+
void BerechneEMA()
{
//--- EMA Werte vom Indicator kopieren (Copy EMA values from indicator)
int copied = CopyBuffer(ema_handle, 0, 0, 3, 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]: ", ema_array[0], " [1]: ", ema_array[1], " [2]: ", ema_array[2]);
}
//+------------------------------------------------------------------+
//| Trigger-Bedingungen prüfen (Check trigger conditions) |
//+------------------------------------------------------------------+
void PrüfeTrigger()
{
if(ArraySize(ema_array) < 2)
{
Print("TRACE: Array zu klein - Größe: ", ArraySize(ema_array));
return;
}
//--- Aktuelle Werte (Current values)
double aktueller_preis = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double aktueller_ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double aktueller_close = iClose(_Symbol, Timeframe, 0);
double pips_multiplier = (_Digits == 3 || _Digits == 5) ? 10.0 : 1.0;
//--- EMA Werte in Variablen (EMA values in variables)
double ema_aktuell = ema_array[0];
double ema_vorher = 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)
{
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: ", PreisSchwelle, ")");
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
Print("TRACE: Trades im aktuellen Crossover: ", trades_in_current_crossover, "/", MaxTradesPerCrossover);
if(preis_abstand > PreisSchwelle && !preis_trigger_aktiv)
{
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: ", SteigungSchwelle, ")");
if(MathAbs(steigung) > SteigungSchwelle && !steigung_trigger_aktiv)
{
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(preis_trigger_aktiv && steigung_trigger_aktiv && !überwachung_aktiv)
{
überwachung_aktiv = true;
if(UseBarData)
{
letzte_überwachung_zeit = iTime(_Symbol, Timeframe, 0); // Aktuelle Bar-Zeit
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Bar: ", TimeToString(letzte_überwachung_zeit), ")");
}
else
{
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(ü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(trades_in_current_crossover >= MaxTradesPerCrossover)
{
Print("TRACE: Trade-Limit erreicht (", MaxTradesPerCrossover, ") - Kein neuer Trade");
return;
}
if(bullish_signal && !PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
if(!IsWeeklyADXTrendFavorable(ORDER_TYPE_BUY))
{
Print("TRACE: Weekly ADX blockiert BUY-Entry");
return;
}
Print("TRACE: Versuche KAUF-Trade zu platzieren (Trade #", trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_BUY))
{
trades_in_current_crossover++;
}
}
else if(bearish_signal && !PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
if(!IsWeeklyADXTrendFavorable(ORDER_TYPE_SELL))
{
Print("TRACE: Weekly ADX blockiert SELL-Entry");
return;
}
Print("TRACE: Versuche VERKAUF-Trade zu platzieren (Trade #", trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_SELL))
{
trades_in_current_crossover++;
}
}
else if(PositionExistsByMagic(_Symbol, (ulong)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: ", LotGröße);
bool success = false;
if(order_type == ORDER_TYPE_BUY)
{
success = trade.Buy(LotGröße, _Symbol, 0, 0, 0, "EMA Crossover Trade");
}
else
{
success = trade.Sell(LotGröße, _Symbol, 0, 0, 0, "EMA Crossover Trade");
}
if(success)
{
ticket = (int)trade.ResultOrder();
Print("TRACE: Trade erfolgreich platziert: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF", " Ticket: ", ticket);
//--- Trade-Öffnungszeit speichern (Save trade opening time)
trade_open_time = iTime(_Symbol, Timeframe, 0);
g_last_sl_adjust_success_time = 0;
Print("TRACE: Trade-Öffnungszeit: ", TimeToString(trade_open_time));
//--- Überwachung zurücksetzen (Reset monitoring)
überwachung_aktiv = false;
preis_trigger_aktiv = false;
steigung_trigger_aktiv = false;
return true;
}
else
{
Print("TRACE: Fehler beim Platzieren des Trades - Retcode: ", trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", trade.ResultRetcodeDescription());
return false;
}
}
//+------------------------------------------------------------------+
//| Mindestgewinn fuer Trailing erreicht? |
//+------------------------------------------------------------------+
bool TrailingActivationReached(const double position_profit, const ENUM_POSITION_TYPE position_type,
const double pips_multiplier)
{
if(TrailingActivationPips <= 0.0)
return (position_profit > 0.0);
const double open_px = PositionGetDouble(POSITION_PRICE_OPEN);
if(position_type == POSITION_TYPE_BUY)
{
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
return ((bid - open_px) / _Point / pips_multiplier >= TrailingActivationPips);
}
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
return ((open_px - ask) / _Point / pips_multiplier >= TrailingActivationPips);
}
//+------------------------------------------------------------------+
//| Trades verwalten (Manage trades) |
//+------------------------------------------------------------------+
void VerwalteTrades()
{
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
return;
if(UseStaleStopLossExit && StaleStopLossSeconds > 0)
{
const datetime stale_ref = (g_last_sl_adjust_success_time > 0)
? g_last_sl_adjust_success_time
: (datetime)PositionGetInteger(POSITION_TIME);
if(TimeCurrent() - stale_ref >= StaleStopLossSeconds)
{
SchließePosition("Stale stop loss - keine SL-Anpassung");
return;
}
}
double position_profit = PositionGetDouble(POSITION_PROFIT);
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double pips_multiplier = (_Digits == 3 || _Digits == 5) ? 10.0 : 1.0;
const double trail_dist = TrailingStop * _Point * pips_multiplier;
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
const double min_dist = (double)stops_level * _Point;
//--- Gleitender Stop (Trailing Stop)
if(UseTrailingStop && TrailingStop > 0.0 && TrailingActivationReached(position_profit, position_type, pips_multiplier))
{
if(position_type == POSITION_TYPE_BUY)
{
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double new_stop_loss = NormalizeDouble(bid - trail_dist, digits);
if(min_dist > 0.0 && bid - new_stop_loss < min_dist)
new_stop_loss = NormalizeDouble(bid - min_dist, digits);
const double current_stop_loss = PositionGetDouble(POSITION_SL);
if(new_stop_loss < bid && new_stop_loss > 0.0 && new_stop_loss > current_stop_loss)
ÄndereStopLoss(new_stop_loss);
}
else if(position_type == POSITION_TYPE_SELL)
{
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double new_stop_loss = NormalizeDouble(ask + trail_dist, digits);
if(min_dist > 0.0 && new_stop_loss - ask < min_dist)
new_stop_loss = NormalizeDouble(ask + min_dist, digits);
const double current_stop_loss = PositionGetDouble(POSITION_SL);
if(new_stop_loss > ask && new_stop_loss > 0.0 &&
(new_stop_loss < current_stop_loss || current_stop_loss == 0.0))
ÄndereStopLoss(new_stop_loss);
}
}
//--- Ausstieg bei Preis unter/über EMA (Exit when price below/above EMA)
if(ArraySize(ema_array) >= 1)
{
double aktueller_close = iClose(_Symbol, Timeframe, 0);
double ema_aktuell = 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 ", trades_in_current_crossover);
}
}
//--- Profit-Prüfung nach X Bars (Profit check after X bars)
if(CloseUnprofitableTrades && trade_open_time != 0 && PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
Print("TRACE: Profit-Prüfung aktiviert - CloseUnprofitableTrades: ", CloseUnprofitableTrades);
PrüfeProfitNachBars();
}
else if(!CloseUnprofitableTrades)
{
Print("TRACE: Profit-Prüfung deaktiviert - CloseUnprofitableTrades: ", CloseUnprofitableTrades);
}
}
//+------------------------------------------------------------------+
//| Profit-Prüfung nach X Bars (Profit check after X bars) |
//+------------------------------------------------------------------+
void PrüfeProfitNachBars()
{
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
{
return; // Keine Position offen
}
datetime current_bar_time = iTime(_Symbol, Timeframe, 0);
int bars_since_trade_open = iBarShift(_Symbol, Timeframe, trade_open_time);
Print("TRACE: Bars seit Trade-Öffnung: ", bars_since_trade_open, "/", ProfitCheckBars);
//--- Prüfe ob genügend Bars vergangen sind (Check if enough bars have passed)
if(bars_since_trade_open >= 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 ", 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)
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)
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(trade, _Symbol, (ulong)MagicNumber, new_stop_loss, PositionGetDouble(POSITION_TP));
if(success)
{
g_last_sl_adjust_success_time = TimeCurrent();
Print("TRACE: Stop Loss erfolgreich geändert auf: ", new_stop_loss);
}
else
{
Print("TRACE: Fehler beim Ändern des Stop Loss - Retcode: ", trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", 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(trade, _Symbol, (ulong)MagicNumber);
if(success)
{
g_last_sl_adjust_success_time = 0;
Print("TRACE: Position erfolgreich geschlossen - Grund: ", reason);
}
else
{
Print("TRACE: Fehler beim Schließen der Position - Retcode: ", trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", trade.ResultRetcodeDescription());
}
}
//+------------------------------------------------------------------+
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

@@ -0,0 +1,347 @@
//+------------------------------------------------------------------+
//| RSIConsolidation.mq5 |
//| Mean-reversion RSI for ranging markets; trend filters block runs |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025"
#property link "https://www.mql5.com"
#property version "1.00"
#include <Trade\Trade.mqh>
//--- Symbol (empty = chart symbol)
input group "=== Symbol & session ==="
input string InpSymbol = "";
input group "=== Timeframe & bar logic ==="
input ENUM_TIMEFRAMES SignalTF = PERIOD_M15;
input bool EntryOnNewBarOnly = true;
//--- Core: no trend / consolidation regime
input group "=== Regime: consolidation (anti-trend) ==="
input int ADX_Period = 23;
input double ADX_Max = 29.0;
input bool UseATRRatioFilter = true;
input int ATR_Period = 8;
input int ATR_SMA_Period = 35;
input double ATR_Ratio_Max = 1.36;
input bool UseFlatEMAFilter = true;
input int EMA_Fast = 13;
input int EMA_Slow = 17;
input double EMA_Separation_MaxPct = 0.26;
//--- RSI entries (fade extremes toward mean)
input group "=== RSI entries ==="
input int RSI_Period = 8;
input ENUM_APPLIED_PRICE RSI_Price = PRICE_OPEN;
input double RSI_Oversold = 22.0;
input double RSI_Overbought = 63.0;
//--- Exits: mean target + hard ATR bracket
input group "=== Exits ==="
input bool UseRSI_MeanExit = true;
input double RSI_Exit_Long = 48.0;
input double RSI_Exit_Short = 52.0;
input double SL_ATR_Mult = 2.15;
input double TP_ATR_Mult = 2.40;
input int MaxBarsInTrade = 54;
input group "=== Risk & execution ==="
input double Lots = 0.10;
input ulong MagicNumber = 20250420;
input int Slippage = 10;
input int MaxSpreadPoints = 28;
CTrade trade;
string g_sym;
int h_rsi = INVALID_HANDLE;
int h_adx = INVALID_HANDLE;
int h_atr = INVALID_HANDLE;
int h_ema_fast = INVALID_HANDLE;
int h_ema_slow = INVALID_HANDLE;
datetime g_last_bar = 0;
bool PositionExistsByMagicSym(string sym, ulong magic)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong t = PositionGetTicket(i);
if(t == 0) continue;
if(PositionGetString(POSITION_SYMBOL) == sym && PositionGetInteger(POSITION_MAGIC) == (long)magic)
return true;
}
return false;
}
ulong GetPositionTicketByMagicSym(string sym, ulong magic)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong t = PositionGetTicket(i);
if(t == 0) continue;
if(PositionGetString(POSITION_SYMBOL) == sym && PositionGetInteger(POSITION_MAGIC) == (long)magic)
return t;
}
return 0;
}
bool SelectPositionTicketSymMagic(ulong ticket, string sym, ulong magic)
{
if(!PositionSelectByTicket(ticket)) return false;
return PositionGetString(POSITION_SYMBOL) == sym && PositionGetInteger(POSITION_MAGIC) == (long)magic;
}
double NormalizeVolume(string sym, double vol)
{
double minLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP);
if(step > 0.0)
vol = MathFloor(vol / step) * step;
if(vol < minLot) vol = minLot;
if(vol > maxLot) vol = maxLot;
return vol;
}
int CurrentSpreadPoints(string sym)
{
long spread = 0;
if(!SymbolInfoInteger(sym, SYMBOL_SPREAD, spread))
return 999999;
return (int)spread;
}
double MinStopsDistancePrice(string sym)
{
long lvl = 0;
if(!SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL, lvl))
return 0;
double pt = SymbolInfoDouble(sym, SYMBOL_POINT);
if(pt <= 0)
return 0;
return (double)lvl * pt;
}
bool Copy1(int handle, double &v)
{
double b[];
ArraySetAsSeries(b, true);
if(CopyBuffer(handle, 0, 0, 1, b) < 1) return false;
v = b[0];
return true;
}
bool RSI_Buffers(double &cur, double &prev, double &twoAgo)
{
double b[];
ArraySetAsSeries(b, true);
if(CopyBuffer(h_rsi, 0, 0, 3, b) < 3) return false;
cur = b[0];
prev = b[1];
twoAgo = b[2];
return true;
}
bool Regime_IsConsolidation()
{
double adx = 0;
if(!Copy1(h_adx, adx))
return false;
if(adx >= ADX_Max)
return false;
if(UseATRRatioFilter)
{
double atrArr[], atrSma[];
ArraySetAsSeries(atrArr, true);
if(CopyBuffer(h_atr, 0, 0, ATR_SMA_Period + 1, atrArr) < ATR_SMA_Period + 1)
return false;
double sum = 0;
for(int i = 1; i <= ATR_SMA_Period; i++)
sum += atrArr[i];
double smaAtr = sum / (double)ATR_SMA_Period;
if(smaAtr <= 0.0)
return false;
double ratio = atrArr[0] / smaAtr;
if(ratio > ATR_Ratio_Max)
return false;
}
if(UseFlatEMAFilter)
{
double ef[], es[];
ArraySetAsSeries(ef, true);
ArraySetAsSeries(es, true);
if(CopyBuffer(h_ema_fast, 0, 0, 1, ef) < 1) return false;
if(CopyBuffer(h_ema_slow, 0, 0, 1, es) < 1) return false;
double c = SymbolInfoDouble(g_sym, SYMBOL_BID);
if(c <= 0) return false;
double sep = MathAbs(ef[0] - es[0]) / c * 100.0;
if(sep > EMA_Separation_MaxPct)
return false;
}
return true;
}
bool Entry_BuyCross(double twoAgo, double prev)
{
return (twoAgo <= RSI_Oversold && prev > RSI_Oversold);
}
bool Entry_SellCross(double twoAgo, double prev)
{
return (twoAgo >= RSI_Overbought && prev < RSI_Overbought);
}
void TryCloseByRSI(ENUM_POSITION_TYPE typ, double rsi)
{
ulong tk = GetPositionTicketByMagicSym(g_sym, MagicNumber);
if(tk == 0 || !SelectPositionTicketSymMagic(tk, g_sym, MagicNumber))
return;
if(!UseRSI_MeanExit)
return;
if(typ == POSITION_TYPE_BUY && rsi >= RSI_Exit_Long)
trade.PositionClose(tk);
else if(typ == POSITION_TYPE_SELL && rsi <= RSI_Exit_Short)
trade.PositionClose(tk);
}
void ManageOpenPosition(double rsi)
{
ulong tk = GetPositionTicketByMagicSym(g_sym, MagicNumber);
if(tk == 0 || !SelectPositionTicketSymMagic(tk, g_sym, MagicNumber))
return;
ENUM_POSITION_TYPE typ = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
datetime openT = (datetime)PositionGetInteger(POSITION_TIME);
int barsAgo = iBarShift(g_sym, SignalTF, openT, false);
if(barsAgo >= 0 && barsAgo >= MaxBarsInTrade)
{
trade.PositionClose(tk);
return;
}
TryCloseByRSI(typ, rsi);
}
int OnInit()
{
g_sym = InpSymbol;
StringTrimLeft(g_sym);
StringTrimRight(g_sym);
if(StringLen(g_sym) == 0)
g_sym = _Symbol;
if(!SymbolSelect(g_sym, true))
{
Print("RSIConsolidation: SymbolSelect failed: ", g_sym);
return INIT_FAILED;
}
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(Slippage);
trade.SetTypeFilling(ORDER_FILLING_RETURN);
h_rsi = iRSI(g_sym, SignalTF, RSI_Period, RSI_Price);
h_adx = iADX(g_sym, SignalTF, ADX_Period);
h_atr = iATR(g_sym, SignalTF, ATR_Period);
h_ema_fast = iMA(g_sym, SignalTF, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE);
h_ema_slow = iMA(g_sym, SignalTF, EMA_Slow, 0, MODE_EMA, PRICE_CLOSE);
if(h_rsi == INVALID_HANDLE || h_adx == INVALID_HANDLE || h_atr == INVALID_HANDLE
|| h_ema_fast == INVALID_HANDLE || h_ema_slow == INVALID_HANDLE)
{
Print("RSIConsolidation: indicator init failed");
return INIT_FAILED;
}
Print("RSIConsolidation: symbol=", g_sym, " TF=", EnumToString(SignalTF));
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(h_rsi != INVALID_HANDLE) IndicatorRelease(h_rsi);
if(h_adx != INVALID_HANDLE) IndicatorRelease(h_adx);
if(h_atr != INVALID_HANDLE) IndicatorRelease(h_atr);
if(h_ema_fast != INVALID_HANDLE) IndicatorRelease(h_ema_fast);
if(h_ema_slow != INVALID_HANDLE) IndicatorRelease(h_ema_slow);
}
bool EnoughHistory()
{
int need = MathMax(RSI_Period + 3, MathMax(ADX_Period + 2, ATR_SMA_Period + 3));
if(Bars(g_sym, SignalTF) < need)
return false;
return true;
}
void OnTick()
{
if(!EnoughHistory())
return;
if(MaxSpreadPoints > 0 && CurrentSpreadPoints(g_sym) > MaxSpreadPoints)
return;
double rsi, rsiPrev, rsi2;
if(!RSI_Buffers(rsi, rsiPrev, rsi2))
return;
datetime barTime = iTime(g_sym, SignalTF, 0);
bool isNew = (barTime != g_last_bar);
if(PositionExistsByMagicSym(g_sym, MagicNumber))
{
ManageOpenPosition(rsi);
if(isNew)
g_last_bar = barTime;
return;
}
if(EntryOnNewBarOnly && !isNew)
return;
g_last_bar = barTime;
if(!Regime_IsConsolidation())
return;
double atrArr[];
ArraySetAsSeries(atrArr, true);
if(CopyBuffer(h_atr, 0, 0, 1, atrArr) < 1)
return;
double atr = atrArr[0];
int dig = (int)SymbolInfoInteger(g_sym, SYMBOL_DIGITS);
double slDist = atr * SL_ATR_Mult;
double tpDist = atr * TP_ATR_Mult;
double minD = MinStopsDistancePrice(g_sym);
if(slDist < minD)
slDist = minD;
if(tpDist < minD)
tpDist = minD;
double vol = NormalizeVolume(g_sym, Lots);
if(Entry_BuyCross(rsi2, rsiPrev))
{
double ask = SymbolInfoDouble(g_sym, SYMBOL_ASK);
double sl = ask - slDist;
double tp = ask + tpDist;
sl = NormalizeDouble(sl, dig);
tp = NormalizeDouble(tp, dig);
trade.Buy(vol, g_sym, ask, sl, tp, "RSIConsolidation BUY");
}
else if(Entry_SellCross(rsi2, rsiPrev))
{
double bid = SymbolInfoDouble(g_sym, SYMBOL_BID);
double sl = bid + slDist;
double tp = bid - tpDist;
sl = NormalizeDouble(sl, dig);
tp = NormalizeDouble(tp, dig);
trade.Sell(vol, g_sym, bid, sl, tp, "RSIConsolidation SELL");
}
}
//+------------------------------------------------------------------+
@@ -0,0 +1,37 @@
; RSIConsolidation.mq5 — optimization preset (Strategy Tester → Inputs → Load)
; Format: Name=Current||Start||Step||Stop||Y|N (Y = include in optimization)
;
; === Symbol & session ===
InpSymbol=
; === Timeframe & bar logic ===
; SignalTF: optimize per run (ENUM is non-sequential); M15=15, H1=16385, H4=16388
SignalTF=15||15||0||15||N
EntryOnNewBarOnly=true||false||0||true||N
; === Regime: consolidation (anti-trend) ===
ADX_Period=14||7||1||28||Y
ADX_Max=22.0||16.0||1.0||32.0||Y
UseATRRatioFilter=true||false||0||true||N
ATR_Period=14||7||1||21||Y
ATR_SMA_Period=50||20||5||100||Y
ATR_Ratio_Max=1.18||1.0||0.02||1.35||Y
UseFlatEMAFilter=true||false||0||true||N
EMA_Fast=8||5||1||13||Y
EMA_Slow=21||13||2||34||Y
EMA_Separation_MaxPct=0.22||0.08||0.02||0.45||Y
; === RSI entries ===
RSI_Period=14||7||1||21||Y
RSI_Price=1||1||1||7||Y
RSI_Oversold=32.0||22.0||1.0||42.0||Y
RSI_Overbought=68.0||58.0||1.0||78.0||Y
; === Exits ===
UseRSI_MeanExit=true||false||0||true||N
RSI_Exit_Long=52.0||48.0||1.0||62.0||Y
RSI_Exit_Short=48.0||38.0||1.0||52.0||Y
SL_ATR_Mult=1.35||0.9||0.05||2.2||Y
TP_ATR_Mult=1.85||1.0||0.05||3.0||Y
MaxBarsInTrade=36||12||2||80||Y
; === Risk & execution ===
Lots=0.1||0.1||0.01||1.0||N
MagicNumber=20250420||20250420||1||20250420||N
Slippage=10||10||1||100||N
MaxSpreadPoints=0||0||1||30||Y
@@ -0,0 +1,295 @@
// Input Parameters
#include <Trade\Trade.mqh>
#include "../_united/MagicNumberHelpers.mqh"
input group "Trade Management"
input int MagicNumber = 7;
input int rsiPeriod = 19; // RSI period
input int overboughtLevel = 93; // Overbought level (RSI > 70 for sell)
input int oversoldLevel = 22; // Oversold level (RSI < 30 for buy)
input double entryRSIBuySpread = 0;
input double entryRSISellSpread = 0;
input double lotSize = 0.1; // Trade lot size
input int slippage = 3; // Slippage for orders
input int cooldownSeconds = 209; // Cooldown period in seconds
input ENUM_TIMEFRAMES TimeFrame1 = PERIOD_M1; // RSI Timeframe
input ENUM_TIMEFRAMES TimeFrame2 = PERIOD_M1; // EMA Timeframe
input ENUM_TIMEFRAMES BarTimeFrame = PERIOD_M12; // EMA Timeframe
input int emaPeriod = 140; // EMA period
input double emaSlopeThreshold = 105; // EMA slope threshold for trend strength
input double exitBuyRSI = 86;
input double exitSellRSI = 10;
input double TrailingStop = 295;
input double emaDistanceThreshold = 165;
input int tradingHourOneBegin = 24;
input int tradingHourOneEnd = 22;
input int tradingHourTwoBegin = 6;
input int tradingHourTwoEnd = 19;
datetime bartime;
// RSI Handle
int rsiHandle;
input bool Sunday =false; // Sunday
input bool Monday =false; // Monday
input bool Tuesday =true; // Tuesday
input bool Wednesday=true; // Wednesday
input bool Thursday =true; // Thursday
input bool Friday =false; // Friday
input bool Saturday =false; // Saturday
bool WeekDays[7];
void WeekDays_Init()
{
WeekDays[0]=Sunday;
WeekDays[1]=Monday;
WeekDays[2]=Tuesday;
WeekDays[3]=Wednesday;
WeekDays[4]=Thursday;
WeekDays[5]=Friday;
WeekDays[6]=Saturday;
}
bool WeekDays_Check(datetime aTime)
{
MqlDateTime stm;
TimeToStruct(aTime,stm);
return(WeekDays[stm.day_of_week]);
}
// EMA Handle
int emaHandle;
double previousRSIDef = 0;
// Create CTrade object for executing trades
CTrade trade;
// Track the last trade time
datetime lastTradeTime = 0;
void OnInit() {
WeekDays_Init();
// Create RSI handle
rsiHandle = iRSI(_Symbol, TimeFrame1, rsiPeriod, PRICE_CLOSE);
if (rsiHandle == INVALID_HANDLE) {
Print("Error creating RSI handle: ", GetLastError());
return;
}
// Create EMA handle
emaHandle = iMA(_Symbol, TimeFrame2, emaPeriod, 0, MODE_EMA, PRICE_CLOSE);
if (emaHandle == INVALID_HANDLE) {
Print("Error creating EMA handle: ", GetLastError());
return;
}
// Initialization successful
Print("RSI and EMA Reversal Strategy Initialized.");
}
void OnTick() {
if(bartime==iTime(_Symbol,BarTimeFrame,0))return;
bartime=iTime(_Symbol,BarTimeFrame,0);
// Check if RSI data is available
double rsi[];
if (CopyBuffer(rsiHandle, 0, 0, 2, rsi) <= 0) {
Print("Error copying RSI data: ", GetLastError());
return;
}
// Check if EMA data is available
double ema[];
if (CopyBuffer(emaHandle, 0, 0, 2, ema) <= 0) {
Print("Error copying EMA data: ", GetLastError());
return;
}
// Get the current time
datetime currentTime = TimeCurrent();
int currentHour = TimeHour(TimeCurrent());
if(!WeekDays_Check(TimeTradeServer())) {
Close_Position_MN(MagicNumber);
return;
}
if (!(currentHour < tradingHourOneEnd && currentHour > tradingHourOneBegin || currentHour < tradingHourTwoEnd && currentHour > tradingHourTwoBegin))
{
Close_Position_MN(MagicNumber);
return; // Prevent further trading during this time
}
// Ensure there is at least one position
bool hasPosition = PositionExistsByMagic(_Symbol, MagicNumber);
// Get the current and previous RSI values
double currentRSI = rsi[0];
double previousRSI = rsi[1];
if(previousRSIDef == 0) {
previousRSIDef = currentRSI;
return;
}
// Get the current and previous EMA values
double currentEMA = ema[0];
double previousEMA = ema[1];
// Calculate the EMA slope (difference between current and previous EMA values)
double emaSlope = (currentEMA - previousEMA) * 100;
Print(emaSlope);
double closeCurr = iClose(Symbol(), Period(), 0); // Close of current bar
// ** NEW CODE: Calculate distance to EMA and adjust score **
double priceToEmaDistance = (closeCurr - currentEMA) * 10; // Distance between the current price and the EMA
Print("priceToEmaDistance");
Print(priceToEmaDistance);
// Determine if there are existing buy or sell positions
bool isBuyPosition = false;
bool isSellPosition = false;
if (hasPosition) {
if (PositionSelectByMagic(_Symbol, MagicNumber)) {
int positionType = PositionGetInteger(POSITION_TYPE);
if (positionType == POSITION_TYPE_BUY) {
isBuyPosition = true;
} else if (positionType == POSITION_TYPE_SELL) {
isSellPosition = true;
}
}
}
ApplyTrailingStop();
// Check if the cooldown period has elapsed since the last trade
bool cooldownPassed = (currentTime - lastTradeTime) >= cooldownSeconds;
// Check if EMA slope is above the threshold (indicating strong trend)
bool isTrendStrong = MathAbs(emaSlope) > emaSlopeThreshold || MathAbs(priceToEmaDistance) > emaDistanceThreshold;
// Close trade logic when RSI crosses 50
if (isBuyPosition && currentRSI > exitBuyRSI) {
// Close buy position
Close_Position_MN(MagicNumber);
lastTradeTime = currentTime; // Update last trade time
}
if (isSellPosition && currentRSI < exitSellRSI) {
Close_Position_MN(MagicNumber);
lastTradeTime = currentTime; // Update last trade time
}
// If the EMA slope is strong, do not place new trades
if (isTrendStrong) {
Close_Position_MN(MagicNumber);
lastTradeTime = currentTime; // Update last trade time
Print("Strong trend detected (EMA slope), skipping new trade.");
return;
}
// SELL logic (RSI crosses over the overbought level)
if (currentRSI < overboughtLevel - entryRSISellSpread && previousRSIDef >= overboughtLevel && !isSellPosition && !hasPosition && cooldownPassed) {
trade.SetExpertMagicNumber(MagicNumber);
if (trade.Sell(lotSize, _Symbol, 0, 0, "Sell Order")) {
Print("Sell order placed.");
lastTradeTime = currentTime; // Update last trade time
} else {
Print("Error placing sell order: ", GetLastError());
}
}
// BUY logic (RSI crosses below the oversold level)
if (currentRSI > oversoldLevel + entryRSIBuySpread && previousRSIDef <= oversoldLevel && !isBuyPosition && !hasPosition && cooldownPassed) {
trade.SetExpertMagicNumber(MagicNumber);
if (trade.Buy(lotSize, _Symbol, 0, 0, "Buy Order")) {
Print("Buy order placed.");
lastTradeTime = currentTime; // Update last trade time
} else {
Print("Error placing buy order: ", GetLastError());
}
}
previousRSIDef = currentRSI;
}
void OnDeinit(const int reason) {
// Release RSI and EMA handles on deinitialization
if (rsiHandle != INVALID_HANDLE) {
IndicatorRelease(rsiHandle);
Print("RSI handle released.");
}
if (emaHandle != INVALID_HANDLE) {
IndicatorRelease(emaHandle);
Print("EMA handle released.");
}
}
void Close_Position_MN(ulong magicNumber)
{
// Use helper function to close position by magic number
ClosePositionByMagic(trade, _Symbol, (int)magicNumber);
}
void ApplyTrailingStop()
{
Print("Scanning for trailing stop");
// Check if position exists with our magic number
if(!PositionSelectByMagic(_Symbol, MagicNumber))
{
return; // No position with our magic number
}
ulong PositionTicket = PositionGetInteger(POSITION_TICKET);
long trade_type = PositionGetInteger(POSITION_TYPE);
string symbol = _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 * TrailingStop, DIGIT))
{
if(PositionGetDouble(POSITION_SL) < NormalizeDouble(Bid - POINT * TrailingStop, DIGIT))
{
ModifyPositionByMagic(trade, symbol, MagicNumber,
NormalizeDouble(Bid - POINT * 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 * TrailingStop, DIGIT))
{
if((PositionGetDouble(POSITION_SL) > NormalizeDouble(Ask + POINT * TrailingStop, DIGIT)) ||
(PositionGetDouble(POSITION_SL) == 0))
{
ModifyPositionByMagic(trade, symbol, MagicNumber,
NormalizeDouble(Ask + POINT * TrailingStop, DIGIT),
PositionGetDouble(POSITION_TP));
}
}
}
}
int TimeHour(datetime when=0){ if(when == 0) when = TimeCurrent();
return when / 3600 % 24;
}
@@ -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.1; // 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 = true; // Enable Strategy Lock
input double InpLockProfitThreshold = 6.0; // Lock Profit Threshold (pips)
input bool InpCloseOppositeTrades = true; // 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.

After

Width:  |  Height:  |  Size: 29 KiB

@@ -0,0 +1,539 @@
//+------------------------------------------------------------------+
//| SimpleRSIReversalAUDUSD.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"
#property strict
// Include trade class
#include <Trade\Trade.mqh>
// Input parameters
input int RSIPeriod = 28; // RSI period
input double OverboughtLevel = 68; // Overbought level
input double OversoldLevel = 30; // Oversold level
input int TakeProfitPips = 175; // Take profit in pips
input int StopLossPips = 5; // Stop loss in pips
input double MaxLotSize = 0.2; // Maximum lot size
input int MaxSpread = 1000; // Maximum allowed spread in pips
input int MaxDuration = 340; // Maximum trade duration in hours
input bool UseStopLoss = false; // Use stop loss
input bool UseTakeProfit = false; // Use take profit
input bool UseRSIExit = true; // Use RSI for exit
input double RSIExitLevel = 48; // RSI level to exit (50 = neutral)
input bool CloseOutsideSession = true; // Close trades outside Asian session
input color PanelBackground = clrBlack; // Panel background color
input color PanelText = clrWhite; // Panel text color
input int PanelX = 10; // Panel X position
input int PanelY = 20; // Panel Y position
// Global variables
CTrade trade;
int rsiHandle;
bool isPositionOpen = false;
double positionOpenPrice = 0;
datetime positionOpenTime = 0;
ENUM_POSITION_TYPE lastPositionType = POSITION_TYPE_BUY;
bool sessionCloseAttempted = false; // Track if we've attempted to close positions for current session
// RSI crossover variables
double rsiCurrent = 0;
double rsiPrevious = 0;
double rsiPrevious2 = 0;
bool rsiCrossedOverbought = false;
bool rsiCrossedOversold = false;
bool rsiCrossedExitLevel = false;
// Panel objects
string panelName = "RSIPanel";
int panelWidth = 200;
int panelHeight = 200;
int labelHeight = 20;
int labelSpacing = 5;
// Session times (UTC)
const int AsianSessionStart = 0; // 00:00 UTC
const int AsianSessionEnd = 8; // 08:00 UTC
//+------------------------------------------------------------------+
//| Create panel |
//+------------------------------------------------------------------+
void CreatePanel()
{
// Create panel background
ObjectCreate(0, panelName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, PanelX);
ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, PanelY);
ObjectSetInteger(0, panelName, OBJPROP_XSIZE, panelWidth);
ObjectSetInteger(0, panelName, OBJPROP_YSIZE, panelHeight);
ObjectSetInteger(0, panelName, OBJPROP_BGCOLOR, PanelBackground);
ObjectSetInteger(0, panelName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, panelName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, panelName, OBJPROP_COLOR, PanelText);
ObjectSetInteger(0, panelName, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, panelName, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, panelName, OBJPROP_BACK, false);
ObjectSetInteger(0, panelName, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, panelName, OBJPROP_SELECTED, false);
ObjectSetInteger(0, panelName, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, panelName, OBJPROP_ZORDER, 0);
// Create title label
ObjectCreate(0, panelName + "Title", OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, panelName + "Title", OBJPROP_XDISTANCE, PanelX + 5);
ObjectSetInteger(0, panelName + "Title", OBJPROP_YDISTANCE, PanelY + 5);
ObjectSetInteger(0, panelName + "Title", OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetString(0, panelName + "Title", OBJPROP_TEXT, "RSI Reversal");
ObjectSetInteger(0, panelName + "Title", OBJPROP_COLOR, PanelText);
ObjectSetInteger(0, panelName + "Title", OBJPROP_FONTSIZE, 10);
// Create score labels
CreateScoreLabel("RSI", "RSI: ", 0);
CreateScoreLabel("Position", "Position: ", 1);
CreateScoreLabel("Spread", "Spread: ", 2);
CreateScoreLabel("Session", "Session: ", 3);
CreateScoreLabel("SL", "Stop Loss: ", 4);
CreateScoreLabel("TP", "Take Profit: ", 5);
CreateScoreLabel("Cross", "Cross: ", 6);
}
//+------------------------------------------------------------------+
//| Create score label |
//+------------------------------------------------------------------+
void CreateScoreLabel(string name, string text, int index)
{
ObjectCreate(0, panelName + name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, panelName + name, OBJPROP_XDISTANCE, PanelX + 5);
ObjectSetInteger(0, panelName + name, OBJPROP_YDISTANCE, PanelY + 30 + index * (labelHeight + labelSpacing));
ObjectSetInteger(0, panelName + name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetString(0, panelName + name, OBJPROP_TEXT, text);
ObjectSetInteger(0, panelName + name, OBJPROP_COLOR, PanelText);
ObjectSetInteger(0, panelName + name, OBJPROP_FONTSIZE, 8);
}
//+------------------------------------------------------------------+
//| Update panel values |
//+------------------------------------------------------------------+
void UpdatePanel(double rsi, string position, int spread, string session, double sl, double tp, string crossInfo)
{
ObjectSetString(0, panelName + "RSI", OBJPROP_TEXT, "RSI: " + DoubleToString(rsi, 2));
ObjectSetString(0, panelName + "Position", OBJPROP_TEXT, "Position: " + position);
ObjectSetString(0, panelName + "Spread", OBJPROP_TEXT, "Spread: " + IntegerToString(spread) + " pips");
ObjectSetString(0, panelName + "Session", OBJPROP_TEXT, "Session: " + session);
ObjectSetString(0, panelName + "SL", OBJPROP_TEXT, "Stop Loss: " + IntegerToString(StopLossPips) + " pips");
ObjectSetString(0, panelName + "TP", OBJPROP_TEXT, "Take Profit: " + IntegerToString(TakeProfitPips) + " pips");
ObjectSetString(0, panelName + "Cross", OBJPROP_TEXT, "Cross: " + crossInfo);
}
//+------------------------------------------------------------------+
//| 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);
}
//+------------------------------------------------------------------+
//| Get current session name |
//+------------------------------------------------------------------+
string GetCurrentSession()
{
datetime currentTime = TimeCurrent();
MqlDateTime timeStruct;
TimeToStruct(currentTime, timeStruct);
if(timeStruct.hour >= AsianSessionStart && timeStruct.hour < AsianSessionEnd)
return "Asian";
else if(timeStruct.hour >= 8 && timeStruct.hour < 16)
return "London";
else if(timeStruct.hour >= 13 && timeStruct.hour < 21)
return "New York";
else
return "Other";
}
//+------------------------------------------------------------------+
//| Check if trading is allowed |
//+------------------------------------------------------------------+
bool IsTradingAllowed()
{
// Check if market is open
if(!SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE) == 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()
{
// Reset crossover flags
rsiCrossedOverbought = false;
rsiCrossedOversold = false;
rsiCrossedExitLevel = false;
// Check for overbought crossover (RSI crosses above overbought level)
if(rsiPrevious < OverboughtLevel && rsiCurrent >= OverboughtLevel)
{
rsiCrossedOverbought = true;
}
// Check for oversold crossover (RSI crosses below oversold level)
if(rsiPrevious > OversoldLevel && rsiCurrent <= OversoldLevel)
{
rsiCrossedOversold = true;
}
// Check for exit level crossover
if(rsiPrevious < RSIExitLevel && rsiCurrent >= RSIExitLevel)
{
rsiCrossedExitLevel = true;
}
else if(rsiPrevious > RSIExitLevel && rsiCurrent <= RSIExitLevel)
{
rsiCrossedExitLevel = true;
}
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize RSI indicator
rsiHandle = iRSI(_Symbol, PERIOD_M15, RSIPeriod, PRICE_CLOSE);
if(rsiHandle == INVALID_HANDLE)
{
return(INIT_FAILED);
}
// Wait a bit for the indicator to be ready
Sleep(100);
// Initialize RSI values with retry logic
double rsi[];
ArraySetAsSeries(rsi, true);
int retryCount = 0;
bool rsiInitialized = false;
while(retryCount < 10 && !rsiInitialized)
{
int copied = CopyBuffer(rsiHandle, 0, 0, 3, rsi);
if(copied >= 3)
{
rsiCurrent = rsi[0];
rsiPrevious = rsi[1];
rsiPrevious2 = rsi[2];
rsiInitialized = true;
}
else
{
retryCount++;
Sleep(100);
}
}
if(!rsiInitialized)
{
// Don't fail initialization, just set default values
rsiCurrent = 50.0;
rsiPrevious = 50.0;
rsiPrevious2 = 50.0;
}
// Create panel
CreatePanel();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Release indicator handles
IndicatorRelease(rsiHandle);
// Remove panel objects
ObjectsDeleteAll(0, panelName);
}
//+------------------------------------------------------------------+
//| Close all trades for the current symbol |
//+------------------------------------------------------------------+
bool CloseAllTrades(string reason = "")
{
bool allClosed = true;
int totalPositions = PositionsTotal();
if(totalPositions == 0)
return true;
// Check if there are any positions with our magic number
bool hasOurPositions = false;
for(int i = 0; i < totalPositions; i++)
{
if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == 123456)
{
hasOurPositions = true;
break;
}
}
for(int i = totalPositions - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == _Symbol)
{
// Try to close position with retry logic
int retryCount = 0;
bool positionClosed = false;
while(retryCount < 3 && !positionClosed)
{
if(trade.PositionClose(_Symbol))
{
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;
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check if trading is allowed
if(!IsTradingAllowed())
{
return;
}
// Check if we're in Asian session
if(!IsAsianSession())
{
// Close all positions if outside Asian session and CloseOutsideSession is true
if(CloseOutsideSession && !sessionCloseAttempted)
{
CloseAllTrades("Outside Asian session");
sessionCloseAttempted = true;
}
return;
}
else
{
// Reset the session close attempt flag when we enter Asian session
sessionCloseAttempted = false;
}
// Get current spread
double spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID);
int spreadInPips = (int)(spread / _Point);
// Check if spread is too high
if(spreadInPips > MaxSpread)
{
return;
}
// Get RSI values from bar data
double rsi[];
ArraySetAsSeries(rsi, true);
int copied = CopyBuffer(rsiHandle, 0, 0, 3, rsi);
if(copied < 3)
{
return;
}
// Update RSI values
rsiPrevious2 = rsiPrevious;
rsiPrevious = rsiCurrent;
rsiCurrent = rsi[0];
// Validate RSI values
if(rsiCurrent == 0 || rsiPrevious == 0)
{
return;
}
// Check for RSI crossovers
CheckRSICrossover();
// Get current prices
double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// Get position status
string positionStatus = "None";
for(int i = 0; i < PositionsTotal(); i++)
{
if(PositionGetSymbol(i) == _Symbol)
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
positionStatus = (posType == POSITION_TYPE_BUY) ? "Long" : "Short";
break;
}
}
// Calculate stop loss and take profit levels
double sl = 0;
double tp = 0;
// Prepare crossover info for panel
string crossInfo = "None";
if(rsiCrossedOverbought) crossInfo = "Overbought";
else if(rsiCrossedOversold) crossInfo = "Oversold";
else if(rsiCrossedExitLevel) crossInfo = "Exit";
// Update panel
UpdatePanel(rsiCurrent, positionStatus, spreadInPips, GetCurrentSession(), sl, tp, crossInfo);
// Check for open position
bool hasOpenPosition = false;
for(int i = 0; i < PositionsTotal(); i++)
{
if(PositionGetSymbol(i) == _Symbol)
{
hasOpenPosition = true;
// Get position details
double positionProfit = PositionGetDouble(POSITION_PROFIT);
double positionVolume = PositionGetDouble(POSITION_VOLUME);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
// Check for RSI exit if enabled
if(UseRSIExit && rsiCrossedExitLevel)
{
bool shouldExit = false;
// For long positions, exit when RSI crosses above exit level
if(posType == POSITION_TYPE_BUY && rsiCurrent >= RSIExitLevel && rsiPrevious < RSIExitLevel)
{
shouldExit = true;
}
// For short positions, exit when RSI crosses below exit level
else if(posType == POSITION_TYPE_SELL && rsiCurrent <= RSIExitLevel && rsiPrevious > RSIExitLevel)
{
shouldExit = true;
}
if(shouldExit)
{
CloseAllTrades("RSI Exit Crossover");
return;
}
}
// Check for timeout
if(TimeCurrent() - positionOpenTime > MaxDuration * 3600)
{
CloseAllTrades("Timeout");
return;
}
break;
}
}
// 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(rsiCrossedOversold)
{
double sl = UseStopLoss ? currentBid - StopLossPips * _Point : 0;
double tp = UseTakeProfit ? currentBid + TakeProfitPips * _Point : 0;
if(UseStopLoss && sl >= currentBid)
return;
if(UseTakeProfit && tp <= currentBid)
return;
// Set trade parameters
trade.SetDeviationInPoints(3);
trade.SetTypeFilling(ORDER_FILLING_IOC);
trade.SetExpertMagicNumber(123456);
// Place buy order using CTrade
if(trade.Buy(MaxLotSize, _Symbol, currentAsk, sl, tp, "RSI Oversold Crossover Buy"))
{
isPositionOpen = true;
positionOpenPrice = currentAsk;
positionOpenTime = TimeCurrent();
lastPositionType = POSITION_TYPE_BUY;
}
}
// Place sell order if RSI crosses above overbought level (overbought crossover)
else if(rsiCrossedOverbought)
{
double sl = UseStopLoss ? currentAsk + StopLossPips * _Point : 0;
double tp = UseTakeProfit ? currentAsk - TakeProfitPips * _Point : 0;
if(UseStopLoss && sl <= currentAsk)
return;
if(UseTakeProfit && tp >= currentAsk)
return;
// Set trade parameters
trade.SetDeviationInPoints(3);
trade.SetTypeFilling(ORDER_FILLING_IOC);
trade.SetExpertMagicNumber(123456);
// Place sell order using CTrade
if(trade.Sell(MaxLotSize, _Symbol, currentBid, sl, tp, "RSI Overbought Crossover Sell"))
{
isPositionOpen = true;
positionOpenPrice = currentBid;
positionOpenTime = TimeCurrent();
lastPositionType = POSITION_TYPE_SELL;
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

@@ -0,0 +1,539 @@
//+------------------------------------------------------------------+
//| SimpleRSIReversalAUDUSD.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"
#property strict
// Include trade class
#include <Trade\Trade.mqh>
// Input parameters
input int RSIPeriod = 28; // RSI period
input double OverboughtLevel = 60; // Overbought level
input double OversoldLevel = 8; // Oversold level
input int TakeProfitPips = 175; // Take profit in pips
input int StopLossPips = 5; // Stop loss in pips
input double MaxLotSize = 0.1; // Maximum lot size
input int MaxSpread = 1000; // Maximum allowed spread in pips
input int MaxDuration = 270; // Maximum trade duration in hours
input bool UseStopLoss = false; // Use stop loss
input bool UseTakeProfit = false; // Use take profit
input bool UseRSIExit = true; // Use RSI for exit
input double RSIExitLevel = 55; // RSI level to exit (50 = neutral)
input bool CloseOutsideSession = false; // Close trades outside Asian session
input color PanelBackground = clrBlack; // Panel background color
input color PanelText = clrWhite; // Panel text color
input int PanelX = 10; // Panel X position
input int PanelY = 20; // Panel Y position
// Global variables
CTrade trade;
int rsiHandle;
bool isPositionOpen = false;
double positionOpenPrice = 0;
datetime positionOpenTime = 0;
ENUM_POSITION_TYPE lastPositionType = POSITION_TYPE_BUY;
bool sessionCloseAttempted = false; // Track if we've attempted to close positions for current session
// RSI crossover variables
double rsiCurrent = 0;
double rsiPrevious = 0;
double rsiPrevious2 = 0;
bool rsiCrossedOverbought = false;
bool rsiCrossedOversold = false;
bool rsiCrossedExitLevel = false;
// Panel objects
string panelName = "RSIPanel";
int panelWidth = 200;
int panelHeight = 200;
int labelHeight = 20;
int labelSpacing = 5;
// Session times (UTC)
const int AsianSessionStart = 0; // 00:00 UTC
const int AsianSessionEnd = 8; // 08:00 UTC
//+------------------------------------------------------------------+
//| Create panel |
//+------------------------------------------------------------------+
void CreatePanel()
{
// Create panel background
ObjectCreate(0, panelName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, PanelX);
ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, PanelY);
ObjectSetInteger(0, panelName, OBJPROP_XSIZE, panelWidth);
ObjectSetInteger(0, panelName, OBJPROP_YSIZE, panelHeight);
ObjectSetInteger(0, panelName, OBJPROP_BGCOLOR, PanelBackground);
ObjectSetInteger(0, panelName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, panelName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, panelName, OBJPROP_COLOR, PanelText);
ObjectSetInteger(0, panelName, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, panelName, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, panelName, OBJPROP_BACK, false);
ObjectSetInteger(0, panelName, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, panelName, OBJPROP_SELECTED, false);
ObjectSetInteger(0, panelName, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, panelName, OBJPROP_ZORDER, 0);
// Create title label
ObjectCreate(0, panelName + "Title", OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, panelName + "Title", OBJPROP_XDISTANCE, PanelX + 5);
ObjectSetInteger(0, panelName + "Title", OBJPROP_YDISTANCE, PanelY + 5);
ObjectSetInteger(0, panelName + "Title", OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetString(0, panelName + "Title", OBJPROP_TEXT, "RSI Reversal");
ObjectSetInteger(0, panelName + "Title", OBJPROP_COLOR, PanelText);
ObjectSetInteger(0, panelName + "Title", OBJPROP_FONTSIZE, 10);
// Create score labels
CreateScoreLabel("RSI", "RSI: ", 0);
CreateScoreLabel("Position", "Position: ", 1);
CreateScoreLabel("Spread", "Spread: ", 2);
CreateScoreLabel("Session", "Session: ", 3);
CreateScoreLabel("SL", "Stop Loss: ", 4);
CreateScoreLabel("TP", "Take Profit: ", 5);
CreateScoreLabel("Cross", "Cross: ", 6);
}
//+------------------------------------------------------------------+
//| Create score label |
//+------------------------------------------------------------------+
void CreateScoreLabel(string name, string text, int index)
{
ObjectCreate(0, panelName + name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, panelName + name, OBJPROP_XDISTANCE, PanelX + 5);
ObjectSetInteger(0, panelName + name, OBJPROP_YDISTANCE, PanelY + 30 + index * (labelHeight + labelSpacing));
ObjectSetInteger(0, panelName + name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetString(0, panelName + name, OBJPROP_TEXT, text);
ObjectSetInteger(0, panelName + name, OBJPROP_COLOR, PanelText);
ObjectSetInteger(0, panelName + name, OBJPROP_FONTSIZE, 8);
}
//+------------------------------------------------------------------+
//| Update panel values |
//+------------------------------------------------------------------+
void UpdatePanel(double rsi, string position, int spread, string session, double sl, double tp, string crossInfo)
{
ObjectSetString(0, panelName + "RSI", OBJPROP_TEXT, "RSI: " + DoubleToString(rsi, 2));
ObjectSetString(0, panelName + "Position", OBJPROP_TEXT, "Position: " + position);
ObjectSetString(0, panelName + "Spread", OBJPROP_TEXT, "Spread: " + IntegerToString(spread) + " pips");
ObjectSetString(0, panelName + "Session", OBJPROP_TEXT, "Session: " + session);
ObjectSetString(0, panelName + "SL", OBJPROP_TEXT, "Stop Loss: " + IntegerToString(StopLossPips) + " pips");
ObjectSetString(0, panelName + "TP", OBJPROP_TEXT, "Take Profit: " + IntegerToString(TakeProfitPips) + " pips");
ObjectSetString(0, panelName + "Cross", OBJPROP_TEXT, "Cross: " + crossInfo);
}
//+------------------------------------------------------------------+
//| 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);
}
//+------------------------------------------------------------------+
//| Get current session name |
//+------------------------------------------------------------------+
string GetCurrentSession()
{
datetime currentTime = TimeCurrent();
MqlDateTime timeStruct;
TimeToStruct(currentTime, timeStruct);
if(timeStruct.hour >= AsianSessionStart && timeStruct.hour < AsianSessionEnd)
return "Asian";
else if(timeStruct.hour >= 8 && timeStruct.hour < 16)
return "London";
else if(timeStruct.hour >= 13 && timeStruct.hour < 21)
return "New York";
else
return "Other";
}
//+------------------------------------------------------------------+
//| Check if trading is allowed |
//+------------------------------------------------------------------+
bool IsTradingAllowed()
{
// Check if market is open
if(!SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE) == 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()
{
// Reset crossover flags
rsiCrossedOverbought = false;
rsiCrossedOversold = false;
rsiCrossedExitLevel = false;
// Check for overbought crossover (RSI crosses above overbought level)
if(rsiPrevious < OverboughtLevel && rsiCurrent >= OverboughtLevel)
{
rsiCrossedOverbought = true;
}
// Check for oversold crossover (RSI crosses below oversold level)
if(rsiPrevious > OversoldLevel && rsiCurrent <= OversoldLevel)
{
rsiCrossedOversold = true;
}
// Check for exit level crossover
if(rsiPrevious < RSIExitLevel && rsiCurrent >= RSIExitLevel)
{
rsiCrossedExitLevel = true;
}
else if(rsiPrevious > RSIExitLevel && rsiCurrent <= RSIExitLevel)
{
rsiCrossedExitLevel = true;
}
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize RSI indicator
rsiHandle = iRSI(_Symbol, PERIOD_M15, RSIPeriod, PRICE_CLOSE);
if(rsiHandle == INVALID_HANDLE)
{
return(INIT_FAILED);
}
// Wait a bit for the indicator to be ready
Sleep(100);
// Initialize RSI values with retry logic
double rsi[];
ArraySetAsSeries(rsi, true);
int retryCount = 0;
bool rsiInitialized = false;
while(retryCount < 10 && !rsiInitialized)
{
int copied = CopyBuffer(rsiHandle, 0, 0, 3, rsi);
if(copied >= 3)
{
rsiCurrent = rsi[0];
rsiPrevious = rsi[1];
rsiPrevious2 = rsi[2];
rsiInitialized = true;
}
else
{
retryCount++;
Sleep(100);
}
}
if(!rsiInitialized)
{
// Don't fail initialization, just set default values
rsiCurrent = 50.0;
rsiPrevious = 50.0;
rsiPrevious2 = 50.0;
}
// Create panel
CreatePanel();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Release indicator handles
IndicatorRelease(rsiHandle);
// Remove panel objects
ObjectsDeleteAll(0, panelName);
}
//+------------------------------------------------------------------+
//| Close all trades for the current symbol |
//+------------------------------------------------------------------+
bool CloseAllTrades(string reason = "")
{
bool allClosed = true;
int totalPositions = PositionsTotal();
if(totalPositions == 0)
return true;
// Check if there are any positions with our magic number
bool hasOurPositions = false;
for(int i = 0; i < totalPositions; i++)
{
if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == 123456)
{
hasOurPositions = true;
break;
}
}
for(int i = totalPositions - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == _Symbol)
{
// Try to close position with retry logic
int retryCount = 0;
bool positionClosed = false;
while(retryCount < 3 && !positionClosed)
{
if(trade.PositionClose(_Symbol))
{
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;
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check if trading is allowed
if(!IsTradingAllowed())
{
return;
}
// Check if we're in Asian session
if(!IsAsianSession())
{
// Close all positions if outside Asian session and CloseOutsideSession is true
if(CloseOutsideSession && !sessionCloseAttempted)
{
CloseAllTrades("Outside Asian session");
sessionCloseAttempted = true;
}
return;
}
else
{
// Reset the session close attempt flag when we enter Asian session
sessionCloseAttempted = false;
}
// Get current spread
double spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID);
int spreadInPips = (int)(spread / _Point);
// Check if spread is too high
if(spreadInPips > MaxSpread)
{
return;
}
// Get RSI values from bar data
double rsi[];
ArraySetAsSeries(rsi, true);
int copied = CopyBuffer(rsiHandle, 0, 0, 3, rsi);
if(copied < 3)
{
return;
}
// Update RSI values
rsiPrevious2 = rsiPrevious;
rsiPrevious = rsiCurrent;
rsiCurrent = rsi[0];
// Validate RSI values
if(rsiCurrent == 0 || rsiPrevious == 0)
{
return;
}
// Check for RSI crossovers
CheckRSICrossover();
// Get current prices
double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// Get position status
string positionStatus = "None";
for(int i = 0; i < PositionsTotal(); i++)
{
if(PositionGetSymbol(i) == _Symbol)
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
positionStatus = (posType == POSITION_TYPE_BUY) ? "Long" : "Short";
break;
}
}
// Calculate stop loss and take profit levels
double sl = 0;
double tp = 0;
// Prepare crossover info for panel
string crossInfo = "None";
if(rsiCrossedOverbought) crossInfo = "Overbought";
else if(rsiCrossedOversold) crossInfo = "Oversold";
else if(rsiCrossedExitLevel) crossInfo = "Exit";
// Update panel
UpdatePanel(rsiCurrent, positionStatus, spreadInPips, GetCurrentSession(), sl, tp, crossInfo);
// Check for open position
bool hasOpenPosition = false;
for(int i = 0; i < PositionsTotal(); i++)
{
if(PositionGetSymbol(i) == _Symbol)
{
hasOpenPosition = true;
// Get position details
double positionProfit = PositionGetDouble(POSITION_PROFIT);
double positionVolume = PositionGetDouble(POSITION_VOLUME);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
// Check for RSI exit if enabled
if(UseRSIExit && rsiCrossedExitLevel)
{
bool shouldExit = false;
// For long positions, exit when RSI crosses above exit level
if(posType == POSITION_TYPE_BUY && rsiCurrent >= RSIExitLevel && rsiPrevious < RSIExitLevel)
{
shouldExit = true;
}
// For short positions, exit when RSI crosses below exit level
else if(posType == POSITION_TYPE_SELL && rsiCurrent <= RSIExitLevel && rsiPrevious > RSIExitLevel)
{
shouldExit = true;
}
if(shouldExit)
{
CloseAllTrades("RSI Exit Crossover");
return;
}
}
// Check for timeout
if(TimeCurrent() - positionOpenTime > MaxDuration * 3600)
{
CloseAllTrades("Timeout");
return;
}
break;
}
}
// 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(rsiCrossedOversold)
{
double sl = UseStopLoss ? currentBid - StopLossPips * _Point : 0;
double tp = UseTakeProfit ? currentBid + TakeProfitPips * _Point : 0;
if(UseStopLoss && sl >= currentBid)
return;
if(UseTakeProfit && tp <= currentBid)
return;
// Set trade parameters
trade.SetDeviationInPoints(3);
trade.SetTypeFilling(ORDER_FILLING_IOC);
trade.SetExpertMagicNumber(123456);
// Place buy order using CTrade
if(trade.Buy(MaxLotSize, _Symbol, currentAsk, sl, tp, "RSI Oversold Crossover Buy"))
{
isPositionOpen = true;
positionOpenPrice = currentAsk;
positionOpenTime = TimeCurrent();
lastPositionType = POSITION_TYPE_BUY;
}
}
// Place sell order if RSI crosses above overbought level (overbought crossover)
else if(rsiCrossedOverbought)
{
double sl = UseStopLoss ? currentAsk + StopLossPips * _Point : 0;
double tp = UseTakeProfit ? currentAsk - TakeProfitPips * _Point : 0;
if(UseStopLoss && sl <= currentAsk)
return;
if(UseTakeProfit && tp >= currentAsk)
return;
// Set trade parameters
trade.SetDeviationInPoints(3);
trade.SetTypeFilling(ORDER_FILLING_IOC);
trade.SetExpertMagicNumber(123456);
// Place sell order using CTrade
if(trade.Sell(MaxLotSize, _Symbol, currentBid, sl, tp, "RSI Overbought Crossover Sell"))
{
isPositionOpen = true;
positionOpenPrice = currentBid;
positionOpenTime = TimeCurrent();
lastPositionType = POSITION_TYPE_SELL;
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

@@ -0,0 +1,581 @@
//+------------------------------------------------------------------+
//| RSIScalping.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"
#include <Trade\Trade.mqh>
#include "../_united/MagicNumberHelpers.mqh"
//--- Input parameters
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // Timeframe for Analysis
input int RSI_Period = 14; // RSI Period
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
input double RSI_Overbought = 90; // RSI Overbought Level
input double RSI_Oversold = 73; // RSI Oversold Level
input double RSI_Target_Buy = 88; // RSI Target for Buy Exit
input double RSI_Target_Sell = 48; // RSI Target for Sell Exit
input int BarsToWait = 6; // Bars to wait when RSI goes against position
input double LotSize = 0.1; // Lot Size
input int MagicNumber = 123459123; // Magic Number
input int Slippage = 3; // Slippage in points
input group "=== Reversal escape (intrabar, multi-signal) ==="
input bool UseReversalEscape = true; // run while in position every tick
input int ReversalATRPeriod = 14; // ATR lookback on signal timeframe
input double ReversalAdverseAtrMult = 5.25; // close if price vs entry >= this * ATR
input int ReversalSignsRequired = 2; // how many independent signs must align
input double ReversalRsiVelocity = 16.0; // RSI points drop (long) / rise (short) vs prior buffer
input double ReversalBodyAtrMult = 5.1; // last closed bar body >= this * ATR counts as one sign
input group "=== Trailing stop ==="
input bool UseTrailingStop = true; // move SL behind bid/ask while in profit
input double TrailingStopDistancePoints = 120.0; // SL distance from bid/ask (points)
input double TrailingActivationPoints = 0.0; // min profit before trailing (0 = same as distance)
//--- Global variables
CTrade trade;
int rsi_handle;
double rsi_buffer[];
double rsi_prev, rsi_current, rsi_two_bars_ago;
bool position_open = false;
int position_ticket = 0;
ENUM_POSITION_TYPE current_position_type = POSITION_TYPE_BUY;
datetime last_bar_time = 0;
bool rsi_against_position = false;
int bars_against_count = 0;
void ResetPositionTracking();
void SyncTrackedPosition();
double ATRPriceOnTF(const int period);
int CountReversalEscapeSigns(const ENUM_POSITION_TYPE ptype, const double atr);
void TryReversalEscape();
void ApplyTrailingStop();
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize RSI indicator
rsi_handle = iRSI(_Symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
if(rsi_handle == INVALID_HANDLE)
{
return(INIT_FAILED);
}
// Initialize trade object
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(Slippage);
trade.SetTypeFilling(ORDER_FILLING_FOK);
// Allocate arrays
ArraySetAsSeries(rsi_buffer, true);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(rsi_handle != INVALID_HANDLE)
IndicatorRelease(rsi_handle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check if we have enough bars
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
{
return;
}
// Check if this is a new bar
datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
bool is_new_bar = (current_bar_time != last_bar_time);
bool in_position = position_open || PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
// While flat, process only on new bars. While in position, allow intrabar reversal escape checks.
if(!in_position && !is_new_bar)
{
return;
}
// Update RSI values
if(!UpdateRSI())
{
return;
}
if(in_position && UseReversalEscape)
{
TryReversalEscape();
}
if(in_position && UseTrailingStop)
ApplyTrailingStop();
if(!is_new_bar)
{
return;
}
last_bar_time = current_bar_time;
// Keep local tracking aligned with actual terminal positions for this symbol/magic.
SyncTrackedPosition();
// Check for existing position
CheckExistingPosition();
// Check for new entry signals - only if no position exists for THIS EA (magic number) on THIS symbol
if(!position_open && !PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
CheckEntrySignals();
}
}
//+------------------------------------------------------------------+
//| Update RSI values |
//+------------------------------------------------------------------+
bool UpdateRSI()
{
if(CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer) < 3)
{
return false;
}
rsi_current = rsi_buffer[0]; // Current bar
rsi_prev = rsi_buffer[1]; // Previous bar
rsi_two_bars_ago = rsi_buffer[2]; // Two bars ago
return true;
}
//+------------------------------------------------------------------+
//| Wilder ATR in price units (signal timeframe) |
//+------------------------------------------------------------------+
double ATRPriceOnTF(const int period)
{
if(period < 1)
return 0.0;
MqlRates rates[];
const int need = period + 2;
if(CopyRates(_Symbol, TimeFrame, 0, need, rates) < need)
return 0.0;
ArraySetAsSeries(rates, true);
double sum = 0.0;
for(int i = 1; i <= period; i++)
{
const double hl = rates[i].high - rates[i].low;
const double hc = MathAbs(rates[i].high - rates[i + 1].close);
const double lc = MathAbs(rates[i].low - rates[i + 1].close);
sum += MathMax(hl, MathMax(hc, lc));
}
return sum / (double)period;
}
//+------------------------------------------------------------------+
//| Independent adverse signs (need ReversalSignsRequired to exit) |
//+------------------------------------------------------------------+
int CountReversalEscapeSigns(const ENUM_POSITION_TYPE ptype, const double atr)
{
if(atr <= 0.0)
return 0;
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
int signs = 0;
if(ptype == POSITION_TYPE_BUY)
{
if(entry - bid >= ReversalAdverseAtrMult * atr)
signs++;
if(rsi_prev - rsi_current >= ReversalRsiVelocity)
signs++;
}
else if(ptype == POSITION_TYPE_SELL)
{
if(ask - entry >= ReversalAdverseAtrMult * atr)
signs++;
if(rsi_current - rsi_prev >= ReversalRsiVelocity)
signs++;
}
else
{
return 0;
}
MqlRates rates[];
if(CopyRates(_Symbol, TimeFrame, 0, 4, rates) >= 4)
{
ArraySetAsSeries(rates, true);
const double body = MathAbs(rates[1].close - rates[1].open);
if(body >= ReversalBodyAtrMult * atr)
{
if(ptype == POSITION_TYPE_BUY && rates[1].close < rates[1].open)
signs++;
else if(ptype == POSITION_TYPE_SELL && rates[1].close > rates[1].open)
signs++;
}
if(ptype == POSITION_TYPE_BUY)
{
if(rates[1].close < rates[2].close && rates[2].close < rates[3].close)
signs++;
}
else
{
if(rates[1].close > rates[2].close && rates[2].close > rates[3].close)
signs++;
}
}
return signs;
}
//+------------------------------------------------------------------+
//| Cut losers fast on violent reversals (evaluated every tick) |
//+------------------------------------------------------------------+
void TryReversalEscape()
{
ulong live_ticket = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
if(live_ticket == 0)
return;
if(!PositionSelectByTicketSymbolAndMagic(live_ticket, _Symbol, (ulong)MagicNumber))
return;
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double atr = ATRPriceOnTF(ReversalATRPeriod);
if(atr <= 0.0)
return;
const int signs = CountReversalEscapeSigns(ptype, atr);
if(signs < ReversalSignsRequired)
return;
ClosePosition();
Print("RSIScalpingBTCUSD: reversal escape signs=", signs, " need=", ReversalSignsRequired,
" ATR=", DoubleToString(atr, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS)));
}
//+------------------------------------------------------------------+
//| Trail SL behind favorable price (every tick when enabled) |
//+------------------------------------------------------------------+
void ApplyTrailingStop()
{
if(TrailingStopDistancePoints <= 0.0)
return;
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
return;
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(point <= 0.0)
return;
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
const double trail_dist = TrailingStopDistancePoints * point;
const double activation_pts = (TrailingActivationPoints > 0.0)
? TrailingActivationPoints
: TrailingStopDistancePoints;
const double activation = activation_pts * point;
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
const double min_dist = (double)stops_level * point;
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double cur_sl = PositionGetDouble(POSITION_SL);
const double cur_tp = PositionGetDouble(POSITION_TP);
if(ptype == POSITION_TYPE_BUY)
{
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(bid - entry <= activation)
return;
double new_sl = NormalizeDouble(bid - trail_dist, digits);
if(min_dist > 0.0 && bid - new_sl < min_dist)
new_sl = NormalizeDouble(bid - min_dist, digits);
if(new_sl >= bid || new_sl <= 0.0)
return;
if(cur_sl > 0.0 && new_sl <= cur_sl)
return;
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
else if(ptype == POSITION_TYPE_SELL)
{
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(entry - ask <= activation)
return;
double new_sl = NormalizeDouble(ask + trail_dist, digits);
if(min_dist > 0.0 && new_sl - ask < min_dist)
new_sl = NormalizeDouble(ask + min_dist, digits);
if(new_sl <= ask || new_sl <= 0.0)
return;
if(cur_sl > 0.0 && new_sl >= cur_sl)
return;
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
}
//+------------------------------------------------------------------+
//| Reset local position tracking |
//+------------------------------------------------------------------+
void ResetPositionTracking()
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
}
//+------------------------------------------------------------------+
//| Sync local state with real position in terminal |
//+------------------------------------------------------------------+
void SyncTrackedPosition()
{
ulong live_ticket = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
if(live_ticket == 0)
{
ResetPositionTracking();
return;
}
// If we were not tracking (or ticket changed), start tracking the live position.
if(!position_open || position_ticket != (int)live_ticket)
{
if(PositionSelectByTicketSymbolAndMagic(live_ticket, _Symbol, (ulong)MagicNumber))
{
position_open = true;
position_ticket = (int)live_ticket;
current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
rsi_against_position = false;
bars_against_count = 0;
}
return;
}
}
//+------------------------------------------------------------------+
//| Check existing position for exit conditions |
//+------------------------------------------------------------------+
void CheckExistingPosition()
{
if(!position_open)
{
return;
}
// Check if position still exists with correct magic number AND symbol for THIS EA
if(!PositionSelectByTicketSymbolAndMagic(position_ticket, _Symbol, (ulong)MagicNumber))
{
ResetPositionTracking();
return;
}
// Exit conditions based on RSI target
if(current_position_type == POSITION_TYPE_BUY)
{
// Check if RSI is against the position (below oversold)
if(rsi_current < RSI_Oversold)
{
if(!rsi_against_position)
{
rsi_against_position = true;
bars_against_count = 1;
}
else
{
bars_against_count++;
}
// Close position if RSI has been against for Y bars
if(bars_against_count >= BarsToWait)
{
ClosePosition();
return;
}
}
else
{
// RSI is no longer against the position, reset counter
if(rsi_against_position)
{
rsi_against_position = false;
bars_against_count = 0;
}
// Exit long position when RSI reaches buy target
if(rsi_current >= RSI_Target_Buy)
{
ClosePosition();
}
}
}
else if(current_position_type == POSITION_TYPE_SELL)
{
// Check if RSI is against the position (above overbought)
if(rsi_current > RSI_Overbought)
{
if(!rsi_against_position)
{
rsi_against_position = true;
bars_against_count = 1;
}
else
{
bars_against_count++;
}
// Close position if RSI has been against for Y bars
if(bars_against_count >= BarsToWait)
{
ClosePosition();
return;
}
}
else
{
// RSI is no longer against the position, reset counter
if(rsi_against_position)
{
rsi_against_position = false;
bars_against_count = 0;
}
// Exit short position when RSI reaches sell target
if(rsi_current <= RSI_Target_Sell)
{
ClosePosition();
}
}
}
}
//+------------------------------------------------------------------+
//| Check for entry signals |
//+------------------------------------------------------------------+
void CheckEntrySignals()
{
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold)
{
OpenBuyPosition();
}
// Sell signal: RSI crosses from overbought to below overbought (checking the actual crossover)
if(rsi_two_bars_ago >= RSI_Overbought && rsi_prev < RSI_Overbought)
{
OpenSellPosition();
}
}
//+------------------------------------------------------------------+
//| Open buy position |
//+------------------------------------------------------------------+
void OpenBuyPosition()
{
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
return; // Position already exists for this EA
}
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
{
ulong new_ticket = trade.ResultOrder();
if(new_ticket > 0)
{
// Verify position was opened for THIS EA (magic number) on THIS symbol
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
{
position_ticket = new_ticket;
position_open = true;
current_position_type = POSITION_TYPE_BUY;
}
else
{
Print("Error: Position opened but doesn't match EA magic number or symbol");
}
}
}
}
//+------------------------------------------------------------------+
//| Open sell position |
//+------------------------------------------------------------------+
void OpenSellPosition()
{
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
return; // Position already exists for this EA
}
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
{
ulong new_ticket = trade.ResultOrder();
if(new_ticket > 0)
{
// Verify position was opened for THIS EA (magic number) on THIS symbol
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
{
position_ticket = new_ticket;
position_open = true;
current_position_type = POSITION_TYPE_SELL;
}
else
{
Print("Error: Position opened but doesn't match EA magic number or symbol");
}
}
}
}
//+------------------------------------------------------------------+
//| Close current position |
//+------------------------------------------------------------------+
void ClosePosition()
{
bool position_exists_before_close = PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
if(!position_exists_before_close)
{
ResetPositionTracking();
return;
}
// Close position using helper that verifies symbol AND magic number for THIS EA
if(ClosePositionByMagic(trade, _Symbol, (ulong)MagicNumber))
{
ResetPositionTracking();
}
else
{
// Keep tracking when close fails (e.g. market closed); retry on next bar.
if(!PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
ResetPositionTracking();
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

@@ -0,0 +1,28 @@
; saved on 2026.02.07
; Genetic Algorithm Optimization Parameters for RSIScalpingNVDA
; Recommended ranges for profitable parameter discovery
;
; Format: Parameter=Start||Step||Min||Max||Optimize(Y/N)
;
; NOTE: Current values show RSI_Overbought=19 and RSI_Oversold=50 which are unusual.
; This config uses STANDARD RSI ranges (60-85 overbought, 15-40 oversold).
; If your current values are intentional, use the alternative ranges in OPTIMIZATION_GUIDE.md
;
; === PHASE 1: CORE RSI PARAMETERS (Primary Optimization) ===
RSI_Period=14||1||7||21||Y
RSI_Overbought=70.0||2.0||60.0||85.0||Y
RSI_Oversold=30.0||2.0||15.0||40.0||Y
RSI_Target_Buy=75.0||2.0||65.0||90.0||Y
RSI_Target_Sell=25.0||2.0||10.0||35.0||Y
; === PHASE 2: RISK MANAGEMENT (Secondary Optimization) ===
BarsToWait=2||1||1||8||Y
TimeFrame=16387||0||16385||16390||Y
; === PHASE 3: POSITION SIZING (Optimize with caution) ===
LotSize=50.0||5.0||10.0||100.0||Y
; === FIXED PARAMETERS (Do Not Optimize) ===
RSI_Applied_Price=1||0||1||1||N
MagicNumber=12345||0||12345||12345||N
Slippage=3||0||3||3||N
@@ -0,0 +1,24 @@
; saved on 2026.02.07
; Alternative Genetic Algorithm Optimization - Respects Current Unusual RSI Values
; Use this if RSI_Overbought=19 and RSI_Oversold=50 are intentional
;
; Format: Parameter=Start||Step||Min||Max||Optimize(Y/N)
;
; === PHASE 1: CORE RSI PARAMETERS ===
RSI_Period=14||1||7||21||Y
RSI_Overbought=19.0||1.0||15.0||30.0||Y
RSI_Oversold=50.0||2.0||40.0||60.0||Y
RSI_Target_Buy=71.0||2.0||65.0||80.0||Y
RSI_Target_Sell=70.0||2.0||60.0||75.0||Y
; === PHASE 2: RISK MANAGEMENT ===
BarsToWait=1||1||1||8||Y
TimeFrame=16387||0||16385||16390||Y
; === PHASE 3: POSITION SIZING ===
LotSize=50.0||5.0||10.0||100.0||Y
; === FIXED PARAMETERS ===
RSI_Applied_Price=1||0||1||1||N
MagicNumber=12345||0||12345||12345||N
Slippage=3||0||3||3||N
@@ -0,0 +1,134 @@
# Genetic Algorithm Optimization Guide for RSIScalpingNVDA
## Recommended Optimization Strategy
### Phase 1: Core RSI Parameters (Primary Focus)
These parameters directly control entry/exit signals and should be optimized first.
#### **RSI_Period** (Y - Optimize)
- **Current**: 14
- **Recommended Range**: 7-21
- **Step**: 1
- **Rationale**: Standard RSI periods. Shorter = more sensitive, longer = smoother signals
#### **RSI_Overbought** (Y - Optimize)
- **Current**: 19.0 (unusually low - verify if this is correct)
- **Standard Range**: 60.0-85.0
- **Step**: 2.0
- **Alternative Range** (if current is intentional): 15.0-30.0
- **Rationale**: Level where RSI indicates overbought condition for sell entries
#### **RSI_Oversold** (Y - Optimize)
- **Current**: 50.0 (unusually high - verify if this is correct)
- **Standard Range**: 15.0-40.0
- **Step**: 2.0
- **Alternative Range** (if current is intentional): 40.0-60.0
- **Rationale**: Level where RSI indicates oversold condition for buy entries
#### **RSI_Target_Buy** (Y - Optimize)
- **Current**: 71.0
- **Recommended Range**: 65.0-90.0
- **Step**: 2.0
- **Rationale**: Exit target for long positions. Must be > RSI_Oversold
#### **RSI_Target_Sell** (Y - Optimize)
- **Current**: 70.0
- **Recommended Range**: 10.0-35.0
- **Step**: 2.0
- **Rationale**: Exit target for short positions. Must be < RSI_Overbought
### Phase 2: Risk Management Parameters
#### **BarsToWait** (Y - Optimize)
- **Current**: 1
- **Recommended Range**: 1-8
- **Step**: 1
- **Rationale**: Bars to wait before closing when RSI goes against position. Higher = more patience
#### **TimeFrame** (Y - Optimize)
- **Current**: 16387 (M5)
- **Recommended**: Test M1, M5, M15, H1
- **Values**:
- M1 = 16385
- M5 = 16387
- M15 = 16388
- H1 = 16390
- **Rationale**: Different timeframes can significantly affect scalping performance
### Phase 3: Position Sizing (Optimize with Caution)
#### **LotSize** (Y - Optimize with Fixed Risk)
- **Current**: 50.0
- **Recommended Range**: 10.0-100.0
- **Step**: 5.0
- **Note**: Consider using fixed risk % instead of fixed lot size
- **Rationale**: Position sizing affects profitability but also risk
### Fixed Parameters (Do NOT Optimize)
#### **RSI_Applied_Price** (N)
- **Value**: 1 (PRICE_CLOSE)
- **Rationale**: Standard choice, changing may not improve results significantly
#### **MagicNumber** (N)
- **Value**: 12345
- **Rationale**: Identifier only, no impact on performance
#### **Slippage** (N)
- **Value**: 3
- **Rationale**: Broker-specific, should match your actual slippage
## Genetic Algorithm Settings
### Recommended GA Settings:
- **Optimization Criterion**: Balance (or Custom: Profit Factor * Total Net Profit)
- **Population Size**: 50-100
- **Mutation Probability**: 0.1-0.2
- **Crossover Probability**: 0.7-0.9
- **Optimization Passes**: 3-5
- **Forward Testing**: Always use out-of-sample data
### Optimization Phases:
1. **Broad Search** (First Pass):
- Optimize: RSI_Period, RSI_Overbought, RSI_Oversold, RSI_Target_Buy, RSI_Target_Sell
- Fix: BarsToWait=1, TimeFrame=M5, LotSize=50
2. **Refinement** (Second Pass):
- Use best results from Phase 1
- Optimize: BarsToWait, TimeFrame
- Narrow ranges around Phase 1 winners
3. **Fine-Tuning** (Third Pass):
- Optimize: LotSize (if needed)
- Very narrow ranges around Phase 2 winners
## Important Notes
⚠️ **Current Parameter Anomaly**:
- RSI_Overbought=19 and RSI_Oversold=50 are unusual
- Standard RSI ranges: Overbought 70-80, Oversold 20-30
- **Verify** if these are intentional or if there's a scaling issue
**Validation Checklist**:
- Ensure RSI_Target_Buy > RSI_Oversold
- Ensure RSI_Target_Sell < RSI_Overbought
- Test on sufficient historical data (at least 6-12 months)
- Use forward testing on unseen data
- Check for overfitting (too many parameters optimized)
## Example .set File Structure
```
RSI_Period=14||1||7||21||Y
RSI_Overbought=70.0||2.0||60.0||85.0||Y
RSI_Oversold=30.0||2.0||15.0||40.0||Y
RSI_Target_Buy=75.0||2.0||65.0||90.0||Y
RSI_Target_Sell=25.0||2.0||10.0||35.0||Y
BarsToWait=2||1||1||8||Y
TimeFrame=16387||0||16385||16390||Y
LotSize=50.0||5.0||10.0||100.0||Y
RSI_Applied_Price=1||0||1||1||N
MagicNumber=12345||0||12345||12345||N
Slippage=3||0||3||3||N
```
@@ -0,0 +1,408 @@
//+------------------------------------------------------------------+
//| RSIScalping.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"
#include <Trade\Trade.mqh>
#include "../_united/MagicNumberHelpers.mqh"
//--- Input parameters
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M15; // Timeframe for Analysis
input int RSI_Period = 8; // RSI Period
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
input double RSI_Overbought = 36; // RSI Overbought Level
input double RSI_Oversold = 38; // RSI Oversold Level
input double RSI_Target_Buy = 90; // RSI Target for Buy Exit
input double RSI_Target_Sell = 70; // RSI Target for Sell Exit
input int BarsToWait = 5; // Bars to wait when RSI goes against position
input double LotSize = 50; // Lot Size
input int MagicNumber = 12345; // Magic Number
input int Slippage = 3; // Slippage in points
input group "=== Trailing stop ==="
input bool UseTrailingStop = true; // move SL behind bid/ask while in profit
input double TrailingStopDistancePoints = 375.0; // SL distance from bid/ask (points)
input double TrailingActivationPoints = 75.0; // min profit before trailing (0 = same as distance)
//--- Global variables
CTrade trade;
int rsi_handle;
double rsi_buffer[];
double rsi_prev, rsi_current, rsi_two_bars_ago;
bool position_open = false;
int position_ticket = 0;
ENUM_POSITION_TYPE current_position_type = POSITION_TYPE_BUY;
datetime last_bar_time = 0;
bool rsi_against_position = false;
int bars_against_count = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize RSI indicator
rsi_handle = iRSI(_Symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
if(rsi_handle == INVALID_HANDLE)
{
return(INIT_FAILED);
}
// Initialize trade object
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(Slippage);
trade.SetTypeFilling(ORDER_FILLING_FOK);
// Allocate arrays
ArraySetAsSeries(rsi_buffer, true);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(rsi_handle != INVALID_HANDLE)
IndicatorRelease(rsi_handle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
return;
const datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
const bool new_bar = (current_bar_time != last_bar_time);
const bool in_pos = position_open || PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
if(!in_pos && !new_bar)
return;
if(!UpdateRSI())
return;
if(in_pos && UseTrailingStop)
ApplyTrailingStop();
if(!new_bar)
return;
last_bar_time = current_bar_time;
ResyncPositionFromMarket();
CheckExistingPosition();
if(!position_open && !PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
CheckEntrySignals();
}
//+------------------------------------------------------------------+
//| Update RSI values |
//+------------------------------------------------------------------+
bool UpdateRSI()
{
if(CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer) < 3)
{
return false;
}
rsi_current = rsi_buffer[0]; // Current bar
rsi_prev = rsi_buffer[1]; // Previous bar
rsi_two_bars_ago = rsi_buffer[2]; // Two bars ago
return true;
}
//+------------------------------------------------------------------+
//| Trail SL behind favorable price (every tick when enabled) |
//+------------------------------------------------------------------+
void ApplyTrailingStop()
{
if(TrailingStopDistancePoints <= 0.0)
return;
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
return;
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(point <= 0.0)
return;
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
const double trail_dist = TrailingStopDistancePoints * point;
const double activation_pts = (TrailingActivationPoints > 0.0)
? TrailingActivationPoints
: TrailingStopDistancePoints;
const double activation = activation_pts * point;
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
const double min_dist = (double)stops_level * point;
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double cur_sl = PositionGetDouble(POSITION_SL);
const double cur_tp = PositionGetDouble(POSITION_TP);
if(ptype == POSITION_TYPE_BUY)
{
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(bid - entry <= activation)
return;
double new_sl = NormalizeDouble(bid - trail_dist, digits);
if(min_dist > 0.0 && bid - new_sl < min_dist)
new_sl = NormalizeDouble(bid - min_dist, digits);
if(new_sl >= bid || new_sl <= 0.0)
return;
if(cur_sl > 0.0 && new_sl <= cur_sl)
return;
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
else if(ptype == POSITION_TYPE_SELL)
{
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(entry - ask <= activation)
return;
double new_sl = NormalizeDouble(ask + trail_dist, digits);
if(min_dist > 0.0 && new_sl - ask < min_dist)
new_sl = NormalizeDouble(ask + min_dist, digits);
if(new_sl <= ask || new_sl <= 0.0)
return;
if(cur_sl > 0.0 && new_sl >= cur_sl)
return;
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
}
//+------------------------------------------------------------------+
//| Sync ticket/state if a position exists after restart |
//+------------------------------------------------------------------+
void ResyncPositionFromMarket()
{
if(position_open)
return;
ulong t = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
if(t == 0 || !PositionSelectByTicket(t))
return;
position_ticket = (int)t;
position_open = true;
current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
//+------------------------------------------------------------------+
//| Check existing position for exit conditions |
//+------------------------------------------------------------------+
void CheckExistingPosition()
{
if(!position_open)
{
return;
}
// Check if position still exists with correct magic number AND symbol for THIS EA
if(!PositionSelectByTicketSymbolAndMagic(position_ticket, _Symbol, (ulong)MagicNumber))
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
return;
}
// Exit conditions based on RSI target
if(current_position_type == POSITION_TYPE_BUY)
{
// Check if RSI is against the position (below oversold)
if(rsi_current < RSI_Oversold)
{
if(!rsi_against_position)
{
rsi_against_position = true;
bars_against_count = 1;
}
else
{
bars_against_count++;
}
// Close position if RSI has been against for Y bars
if(bars_against_count >= BarsToWait)
{
ClosePosition();
return;
}
}
else
{
// RSI is no longer against the position, reset counter
if(rsi_against_position)
{
rsi_against_position = false;
bars_against_count = 0;
}
// Exit long position when RSI reaches buy target
if(rsi_current >= RSI_Target_Buy)
{
ClosePosition();
}
}
}
else if(current_position_type == POSITION_TYPE_SELL)
{
// Check if RSI is against the position (above overbought)
if(rsi_current > RSI_Overbought)
{
if(!rsi_against_position)
{
rsi_against_position = true;
bars_against_count = 1;
}
else
{
bars_against_count++;
}
// Close position if RSI has been against for Y bars
if(bars_against_count >= BarsToWait)
{
ClosePosition();
return;
}
}
else
{
// RSI is no longer against the position, reset counter
if(rsi_against_position)
{
rsi_against_position = false;
bars_against_count = 0;
}
// Exit short position when RSI reaches sell target
if(rsi_current <= RSI_Target_Sell)
{
ClosePosition();
}
}
}
}
//+------------------------------------------------------------------+
//| Check for entry signals |
//+------------------------------------------------------------------+
void CheckEntrySignals()
{
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold)
{
OpenBuyPosition();
}
// Sell signal: RSI crosses from overbought to below overbought (checking the actual crossover)
if(rsi_two_bars_ago >= RSI_Overbought && rsi_prev < RSI_Overbought)
{
OpenSellPosition();
}
}
//+------------------------------------------------------------------+
//| Open buy position |
//+------------------------------------------------------------------+
void OpenBuyPosition()
{
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
return; // Position already exists for this EA
}
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
{
ulong new_ticket = trade.ResultOrder();
if(new_ticket > 0)
{
// Verify position was opened for THIS EA (magic number) on THIS symbol
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
{
position_ticket = new_ticket;
position_open = true;
current_position_type = POSITION_TYPE_BUY;
}
else
{
Print("Error: Position opened but doesn't match EA magic number or symbol");
}
}
}
}
//+------------------------------------------------------------------+
//| Open sell position |
//+------------------------------------------------------------------+
void OpenSellPosition()
{
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
return; // Position already exists for this EA
}
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
{
ulong new_ticket = trade.ResultOrder();
if(new_ticket > 0)
{
// Verify position was opened for THIS EA (magic number) on THIS symbol
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
{
position_ticket = new_ticket;
position_open = true;
current_position_type = POSITION_TYPE_SELL;
}
else
{
Print("Error: Position opened but doesn't match EA magic number or symbol");
}
}
}
}
//+------------------------------------------------------------------+
//| Close current position |
//+------------------------------------------------------------------+
void ClosePosition()
{
// Close position using helper that verifies symbol AND magic number for THIS EA
if(ClosePositionByMagic(trade, _Symbol, (ulong)MagicNumber))
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
}
else
{
// Position doesn't exist or wrong magic number - reset tracking
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
}
}
@@ -0,0 +1,408 @@
//+------------------------------------------------------------------+
//| RSIScalping.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"
#include <Trade\Trade.mqh>
#include "../_united/MagicNumberHelpers.mqh"
//--- Input parameters
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // Timeframe for Analysis
input int RSI_Period = 14; // RSI Period
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
input double RSI_Overbought = 54; // RSI Overbought Level
input double RSI_Oversold = 73; // RSI Oversold Level
input double RSI_Target_Buy = 87; // RSI Target for Buy Exit
input double RSI_Target_Sell = 33; // RSI Target for Sell Exit
input int BarsToWait = 1; // Bars to wait when RSI goes against position
input double LotSize = 5; // Lot Size
input int MagicNumber = 125421321; // Magic Number
input int Slippage = 3; // Slippage in points
input group "=== Trailing stop ==="
input bool UseTrailingStop = true; // move SL behind bid/ask while in profit
input double TrailingStopDistancePoints = 900.0; // SL distance from bid/ask (points)
input double TrailingActivationPoints = 950.0; // min profit before trailing (0 = same as distance)
//--- Global variables
CTrade trade;
int rsi_handle;
double rsi_buffer[];
double rsi_prev, rsi_current, rsi_two_bars_ago;
bool position_open = false;
int position_ticket = 0;
ENUM_POSITION_TYPE current_position_type = POSITION_TYPE_BUY;
datetime last_bar_time = 0;
bool rsi_against_position = false;
int bars_against_count = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize RSI indicator
rsi_handle = iRSI(_Symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
if(rsi_handle == INVALID_HANDLE)
{
return(INIT_FAILED);
}
// Initialize trade object
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(Slippage);
trade.SetTypeFilling(ORDER_FILLING_FOK);
// Allocate arrays
ArraySetAsSeries(rsi_buffer, true);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(rsi_handle != INVALID_HANDLE)
IndicatorRelease(rsi_handle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
return;
const datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
const bool new_bar = (current_bar_time != last_bar_time);
const bool in_pos = position_open || PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
if(!in_pos && !new_bar)
return;
if(!UpdateRSI())
return;
if(in_pos && UseTrailingStop)
ApplyTrailingStop();
if(!new_bar)
return;
last_bar_time = current_bar_time;
ResyncPositionFromMarket();
CheckExistingPosition();
if(!position_open && !PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
CheckEntrySignals();
}
//+------------------------------------------------------------------+
//| Update RSI values |
//+------------------------------------------------------------------+
bool UpdateRSI()
{
if(CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer) < 3)
{
return false;
}
rsi_current = rsi_buffer[0]; // Current bar
rsi_prev = rsi_buffer[1]; // Previous bar
rsi_two_bars_ago = rsi_buffer[2]; // Two bars ago
return true;
}
//+------------------------------------------------------------------+
//| Trail SL behind favorable price (every tick when enabled) |
//+------------------------------------------------------------------+
void ApplyTrailingStop()
{
if(TrailingStopDistancePoints <= 0.0)
return;
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
return;
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(point <= 0.0)
return;
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
const double trail_dist = TrailingStopDistancePoints * point;
const double activation_pts = (TrailingActivationPoints > 0.0)
? TrailingActivationPoints
: TrailingStopDistancePoints;
const double activation = activation_pts * point;
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
const double min_dist = (double)stops_level * point;
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double cur_sl = PositionGetDouble(POSITION_SL);
const double cur_tp = PositionGetDouble(POSITION_TP);
if(ptype == POSITION_TYPE_BUY)
{
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(bid - entry <= activation)
return;
double new_sl = NormalizeDouble(bid - trail_dist, digits);
if(min_dist > 0.0 && bid - new_sl < min_dist)
new_sl = NormalizeDouble(bid - min_dist, digits);
if(new_sl >= bid || new_sl <= 0.0)
return;
if(cur_sl > 0.0 && new_sl <= cur_sl)
return;
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
else if(ptype == POSITION_TYPE_SELL)
{
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(entry - ask <= activation)
return;
double new_sl = NormalizeDouble(ask + trail_dist, digits);
if(min_dist > 0.0 && new_sl - ask < min_dist)
new_sl = NormalizeDouble(ask + min_dist, digits);
if(new_sl <= ask || new_sl <= 0.0)
return;
if(cur_sl > 0.0 && new_sl >= cur_sl)
return;
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
}
//+------------------------------------------------------------------+
//| Sync ticket/state if a position exists after restart |
//+------------------------------------------------------------------+
void ResyncPositionFromMarket()
{
if(position_open)
return;
ulong t = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
if(t == 0 || !PositionSelectByTicket(t))
return;
position_ticket = (int)t;
position_open = true;
current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
//+------------------------------------------------------------------+
//| Check existing position for exit conditions |
//+------------------------------------------------------------------+
void CheckExistingPosition()
{
if(!position_open)
{
return;
}
// Check if position still exists with correct magic number AND symbol for THIS EA
if(!PositionSelectByTicketSymbolAndMagic(position_ticket, _Symbol, (ulong)MagicNumber))
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
return;
}
// Exit conditions based on RSI target
if(current_position_type == POSITION_TYPE_BUY)
{
// Check if RSI is against the position (below oversold)
if(rsi_current < RSI_Oversold)
{
if(!rsi_against_position)
{
rsi_against_position = true;
bars_against_count = 1;
}
else
{
bars_against_count++;
}
// Close position if RSI has been against for Y bars
if(bars_against_count >= BarsToWait)
{
ClosePosition();
return;
}
}
else
{
// RSI is no longer against the position, reset counter
if(rsi_against_position)
{
rsi_against_position = false;
bars_against_count = 0;
}
// Exit long position when RSI reaches buy target
if(rsi_current >= RSI_Target_Buy)
{
ClosePosition();
}
}
}
else if(current_position_type == POSITION_TYPE_SELL)
{
// Check if RSI is against the position (above overbought)
if(rsi_current > RSI_Overbought)
{
if(!rsi_against_position)
{
rsi_against_position = true;
bars_against_count = 1;
}
else
{
bars_against_count++;
}
// Close position if RSI has been against for Y bars
if(bars_against_count >= BarsToWait)
{
ClosePosition();
return;
}
}
else
{
// RSI is no longer against the position, reset counter
if(rsi_against_position)
{
rsi_against_position = false;
bars_against_count = 0;
}
// Exit short position when RSI reaches sell target
if(rsi_current <= RSI_Target_Sell)
{
ClosePosition();
}
}
}
}
//+------------------------------------------------------------------+
//| Check for entry signals |
//+------------------------------------------------------------------+
void CheckEntrySignals()
{
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold)
{
OpenBuyPosition();
}
// Sell signal: RSI crosses from overbought to below overbought (checking the actual crossover)
if(rsi_two_bars_ago >= RSI_Overbought && rsi_prev < RSI_Overbought)
{
OpenSellPosition();
}
}
//+------------------------------------------------------------------+
//| Open buy position |
//+------------------------------------------------------------------+
void OpenBuyPosition()
{
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
return; // Position already exists for this EA
}
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
{
ulong new_ticket = trade.ResultOrder();
if(new_ticket > 0)
{
// Verify position was opened for THIS EA (magic number) on THIS symbol
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
{
position_ticket = new_ticket;
position_open = true;
current_position_type = POSITION_TYPE_BUY;
}
else
{
Print("Error: Position opened but doesn't match EA magic number or symbol");
}
}
}
}
//+------------------------------------------------------------------+
//| Open sell position |
//+------------------------------------------------------------------+
void OpenSellPosition()
{
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
return; // Position already exists for this EA
}
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
{
ulong new_ticket = trade.ResultOrder();
if(new_ticket > 0)
{
// Verify position was opened for THIS EA (magic number) on THIS symbol
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
{
position_ticket = new_ticket;
position_open = true;
current_position_type = POSITION_TYPE_SELL;
}
else
{
Print("Error: Position opened but doesn't match EA magic number or symbol");
}
}
}
}
//+------------------------------------------------------------------+
//| Close current position |
//+------------------------------------------------------------------+
void ClosePosition()
{
// Close position using helper that verifies symbol AND magic number for THIS EA
if(ClosePositionByMagic(trade, _Symbol, (ulong)MagicNumber))
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
}
else
{
// Position doesn't exist or wrong magic number - reset tracking
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

@@ -0,0 +1,34 @@
; RSIScalpingXAUUSD-trailing — matches main.mq5 v1.06 default inputs
; saved on 2026.05.01 22:32:43
; MT5 Strategy Tester: Inputs → Load
;
TimeFrame=16385||15||0||16385||N
RSI_Period=14||14||1||140||N
RSI_Applied_Price=1||1||0||7||N
RSI_Overbought=71.0||0||2||100||N
RSI_Oversold=57.0||0||2||100||N
UseEntrySlopeFilter=false||false||0||true||N
EntryMinSlopePerBar=1.0||1.0||0.100000||10.000000||N
RSI_Target_Buy=80.0||0||2||100||N
RSI_Target_Sell=57.0||0||2||100||N
BarsToWait=1||0||1||50||N
LotSize=0.1||0.1||0.010000||1.000000||N
MagicNumber=129102315||129102315||1||1291023150||N
Slippage=3||3||1||30||N
; === Reversal escape (intrabar, multi-signal) ===
UseReversalEscape=true||false||0||true||N
ReversalEscapeTimeFrame=5||0||0||49153||N
ReversalATRPeriod=14||14||1||140||N
ReversalAdverseAtrMult=5.25||5.25||0.525000||52.500000||N
ReversalSignsRequired=1||2||1||20||N
ReversalRsiVelocity=16.0||16.0||1.600000||160.000000||N
ReversalBodyAtrMult=5.1||5.1||0.510000||51.000000||N
; === Trailing stop ===
UseTrailingStop=true||false||0||true||N
TrailingStopDistancePoints=71.0||100||100||5000||Y
TrailingActivationPoints=41.0||100||100||5000||Y
; === Intrabar give-back (same bar reversals) ===
UseGiveBackExit=true||false||0||true||N
GiveBackATRPeriod=14||14||1||140||N
GiveBackAtrMult=0.1||1.85||0.185000||18.500000||N
GiveBackRequireMfe=true||false||0||true||N
@@ -0,0 +1,645 @@
//+------------------------------------------------------------------+
//| RSIScalping.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.06"
#include <Trade\Trade.mqh>
#include "../_united/MagicNumberHelpers.mqh"
//--- Input parameters
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // Timeframe for Analysis
input int RSI_Period = 14; // RSI Period
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
input double RSI_Overbought = 71; // RSI Overbought Level
input double RSI_Oversold = 57; // RSI Oversold Level
input bool UseEntrySlopeFilter = false; // require RSI momentum on entry bars
input double EntryMinSlopePerBar = 1.0; // minimum RSI delta per bar for entry
input double RSI_Target_Buy = 80; // RSI Target for Buy Exit
input double RSI_Target_Sell = 57; // RSI Target for Sell Exit
input int BarsToWait = 1; // Bars to wait when RSI goes against position
input double LotSize = 0.1; // Lot Size
input int MagicNumber = 129102315; // Magic Number
input int Slippage = 3; // Slippage in points
input group "=== Reversal escape (intrabar, multi-signal) ==="
input bool UseReversalEscape = true; // run while in position every tick (now uses ReversalEscapeTimeFrame)
input ENUM_TIMEFRAMES ReversalEscapeTimeFrame = PERIOD_M5; // ATR / RSI velocity / bar signs on this TF (not signal TF)
input int ReversalATRPeriod = 14; // ATR lookback on ReversalEscapeTimeFrame
input double ReversalAdverseAtrMult = 5.25; // close if price vs entry >= this * ATR
input int ReversalSignsRequired = 1; // how many independent signs must align
input double ReversalRsiVelocity = 16.0; // RSI points drop (long) / rise (short) vs prior buffer
input double ReversalBodyAtrMult = 5.1; // last closed bar body >= this * ATR counts as one sign
input group "=== Trailing stop ==="
input bool UseTrailingStop = true; // move SL behind price while in profit
input double TrailingStopDistancePoints = 71.0; // SL distance from current bid/ask (points)
input double TrailingActivationPoints = 41.0; // min profit before trailing (0 = same as distance)
input group "=== Intrabar give-back (same bar reversals) ==="
input bool UseGiveBackExit = true; // exit if price gives back vs best tick since entry
input int GiveBackATRPeriod = 14; // ATR period on signal timeframe (Wilder)
input double GiveBackAtrMult = 0.1; // close when retrace from peak/trough >= this * ATR
input bool GiveBackRequireMfe = true; // long: only after bid was above entry; short: ask below entry
//--- Global variables
CTrade trade;
int rsi_handle;
int rsi_escape_handle = INVALID_HANDLE; // RSI on ReversalEscapeTimeFrame (may alias rsi_handle)
double rsi_buffer[];
double rsi_prev, rsi_current, rsi_two_bars_ago;
bool position_open = false;
int position_ticket = 0;
ENUM_POSITION_TYPE current_position_type = POSITION_TYPE_BUY;
datetime last_bar_time = 0;
bool rsi_against_position = false;
int bars_against_count = 0;
ulong g_giveback_track_ticket = 0;
double g_peak_bid_since_entry = 0.0;
double g_trough_ask_since_entry = 0.0;
void ResetIntrabarGiveBackState();
void TryGiveBackExit();
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize RSI indicator
rsi_handle = iRSI(_Symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
if(rsi_handle == INVALID_HANDLE)
{
return(INIT_FAILED);
}
if(ReversalEscapeTimeFrame == TimeFrame)
rsi_escape_handle = rsi_handle;
else
{
rsi_escape_handle = iRSI(_Symbol, ReversalEscapeTimeFrame, RSI_Period, RSI_Applied_Price);
if(rsi_escape_handle == INVALID_HANDLE)
{
return(INIT_FAILED);
}
}
// Initialize trade object
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(Slippage);
trade.SetTypeFilling(ORDER_FILLING_FOK);
// Allocate arrays
ArraySetAsSeries(rsi_buffer, true);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(rsi_escape_handle != INVALID_HANDLE && rsi_escape_handle != rsi_handle)
IndicatorRelease(rsi_escape_handle);
if(rsi_handle != INVALID_HANDLE)
IndicatorRelease(rsi_handle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
return;
const datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
const bool new_bar = (current_bar_time != last_bar_time);
const bool in_pos = position_open || PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
if(!in_pos && !new_bar)
return;
if(!UpdateRSI())
return;
if(in_pos && UseReversalEscape)
TryReversalEscape();
if(in_pos && UseGiveBackExit)
TryGiveBackExit();
if(in_pos && UseTrailingStop)
ApplyTrailingStop();
if(!new_bar)
return;
last_bar_time = current_bar_time;
ResyncPositionFromMarket();
CheckExistingPosition();
if(!position_open && !PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
CheckEntrySignals();
}
//+------------------------------------------------------------------+
//| Update RSI values |
//+------------------------------------------------------------------+
bool UpdateRSI()
{
if(CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer) < 3)
{
return false;
}
rsi_current = rsi_buffer[0]; // Current bar
rsi_prev = rsi_buffer[1]; // Previous bar
rsi_two_bars_ago = rsi_buffer[2]; // Two bars ago
return true;
}
//+------------------------------------------------------------------+
//| Wilder ATR in price units (signal timeframe) |
//+------------------------------------------------------------------+
double ATRPriceOnTF(const int period)
{
if(period < 1)
return 0.0;
MqlRates rates[];
const int need = period + 2;
if(CopyRates(_Symbol, TimeFrame, 0, need, rates) < need)
return 0.0;
ArraySetAsSeries(rates, true);
double sum = 0.0;
for(int i = 1; i <= period; i++)
{
const double hl = rates[i].high - rates[i].low;
const double hc = MathAbs(rates[i].high - rates[i + 1].close);
const double lc = MathAbs(rates[i].low - rates[i + 1].close);
sum += MathMax(hl, MathMax(hc, lc));
}
return sum / (double)period;
}
//+------------------------------------------------------------------+
//| Wilder ATR on arbitrary timeframe |
//+------------------------------------------------------------------+
double WilderATRForTF(const ENUM_TIMEFRAMES tf, const int period)
{
if(period < 1)
return 0.0;
MqlRates rates[];
const int need = period + 2;
if(CopyRates(_Symbol, tf, 0, need, rates) < need)
return 0.0;
ArraySetAsSeries(rates, true);
double sum = 0.0;
for(int i = 1; i <= period; i++)
{
const double hl = rates[i].high - rates[i].low;
const double hc = MathAbs(rates[i].high - rates[i + 1].close);
const double lc = MathAbs(rates[i].low - rates[i + 1].close);
sum += MathMax(hl, MathMax(hc, lc));
}
return sum / (double)period;
}
//+------------------------------------------------------------------+
//| Independent adverse signs (need ReversalSignsRequired to exit) |
//+------------------------------------------------------------------+
int CountReversalEscapeSigns(const ENUM_POSITION_TYPE ptype, const double atr)
{
if(atr <= 0.0)
return 0;
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
int signs = 0;
double rsi_esc[];
ArraySetAsSeries(rsi_esc, true);
const bool ok_esc_rsi = (rsi_escape_handle != INVALID_HANDLE &&
CopyBuffer(rsi_escape_handle, 0, 0, 2, rsi_esc) >= 2);
if(ptype == POSITION_TYPE_BUY)
{
if(entry - bid >= ReversalAdverseAtrMult * atr)
signs++;
if(ok_esc_rsi && rsi_esc[1] - rsi_esc[0] >= ReversalRsiVelocity)
signs++;
}
else if(ptype == POSITION_TYPE_SELL)
{
if(ask - entry >= ReversalAdverseAtrMult * atr)
signs++;
if(ok_esc_rsi && rsi_esc[0] - rsi_esc[1] >= ReversalRsiVelocity)
signs++;
}
else
return 0;
MqlRates r[];
if(CopyRates(_Symbol, ReversalEscapeTimeFrame, 0, 4, r) >= 4)
{
ArraySetAsSeries(r, true);
const double body = MathAbs(r[1].close - r[1].open);
if(body >= ReversalBodyAtrMult * atr)
{
if(ptype == POSITION_TYPE_BUY && r[1].close < r[1].open)
signs++;
else if(ptype == POSITION_TYPE_SELL && r[1].close > r[1].open)
signs++;
}
if(ptype == POSITION_TYPE_BUY)
{
if(r[1].close < r[2].close && r[2].close < r[3].close)
signs++;
}
else
{
if(r[1].close > r[2].close && r[2].close > r[3].close)
signs++;
}
}
return signs;
}
//+------------------------------------------------------------------+
//| Cut losers fast on violent reversals (evaluated every tick) |
//+------------------------------------------------------------------+
void TryReversalEscape()
{
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
return;
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double atr = WilderATRForTF(ReversalEscapeTimeFrame, ReversalATRPeriod);
if(atr <= 0.0)
return;
const int n = CountReversalEscapeSigns(ptype, atr);
if(n < ReversalSignsRequired)
return;
ClosePosition();
Print("RSIScalpingXAUUSD: reversal escape TF=", EnumToString(ReversalEscapeTimeFrame),
" signs=", n, " need=", ReversalSignsRequired,
" ATR=", DoubleToString(atr, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS)));
}
//+------------------------------------------------------------------+
//| Reset give-back peak/trough tracking |
//+------------------------------------------------------------------+
void ResetIntrabarGiveBackState()
{
g_giveback_track_ticket = 0;
g_peak_bid_since_entry = 0.0;
g_trough_ask_since_entry = 0.0;
}
//+------------------------------------------------------------------+
//| Exit when intrabar price gives back sharply vs best since entry |
//+------------------------------------------------------------------+
void TryGiveBackExit()
{
if(!UseGiveBackExit || GiveBackAtrMult <= 0.0)
return;
const ulong ticket = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
if(ticket == 0 || !PositionSelectByTicket(ticket))
{
ResetIntrabarGiveBackState();
return;
}
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
if(g_giveback_track_ticket != ticket)
{
g_giveback_track_ticket = ticket;
if(ptype == POSITION_TYPE_BUY)
{
g_peak_bid_since_entry = bid;
g_trough_ask_since_entry = 0.0;
}
else
{
g_trough_ask_since_entry = ask;
g_peak_bid_since_entry = 0.0;
}
}
const double atr = ATRPriceOnTF(GiveBackATRPeriod);
if(atr <= 0.0)
return;
const double threshold = GiveBackAtrMult * atr;
if(ptype == POSITION_TYPE_BUY)
{
if(bid > g_peak_bid_since_entry)
g_peak_bid_since_entry = bid;
if(GiveBackRequireMfe && g_peak_bid_since_entry <= entry)
return;
if(g_peak_bid_since_entry - bid >= threshold)
{
ClosePosition();
Print("RSIScalpingXAUUSD: give-back exit BUY retrace=",
DoubleToString(g_peak_bid_since_entry - bid, digits),
" thr=", DoubleToString(threshold, digits));
}
}
else if(ptype == POSITION_TYPE_SELL)
{
if(ask < g_trough_ask_since_entry)
g_trough_ask_since_entry = ask;
if(GiveBackRequireMfe && g_trough_ask_since_entry >= entry)
return;
if(ask - g_trough_ask_since_entry >= threshold)
{
ClosePosition();
Print("RSIScalpingXAUUSD: give-back exit SELL retrace=",
DoubleToString(ask - g_trough_ask_since_entry, digits),
" thr=", DoubleToString(threshold, digits));
}
}
}
//+------------------------------------------------------------------+
//| Trail SL behind favorable price (every tick when enabled) |
//+------------------------------------------------------------------+
void ApplyTrailingStop()
{
if(TrailingStopDistancePoints <= 0.0)
return;
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
return;
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(point <= 0.0)
return;
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
const double trail_dist = TrailingStopDistancePoints * point;
const double activation_pts = (TrailingActivationPoints > 0.0)
? TrailingActivationPoints
: TrailingStopDistancePoints;
const double activation = activation_pts * point;
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
const double min_dist = (double)stops_level * point;
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double cur_sl = PositionGetDouble(POSITION_SL);
const double cur_tp = PositionGetDouble(POSITION_TP);
if(ptype == POSITION_TYPE_BUY)
{
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(bid - entry <= activation)
return;
double new_sl = NormalizeDouble(bid - trail_dist, digits);
if(min_dist > 0.0 && bid - new_sl < min_dist)
new_sl = NormalizeDouble(bid - min_dist, digits);
if(new_sl >= bid || new_sl <= 0.0)
return;
if(cur_sl > 0.0 && new_sl <= cur_sl)
return;
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
else if(ptype == POSITION_TYPE_SELL)
{
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(entry - ask <= activation)
return;
double new_sl = NormalizeDouble(ask + trail_dist, digits);
if(min_dist > 0.0 && new_sl - ask < min_dist)
new_sl = NormalizeDouble(ask + min_dist, digits);
if(new_sl <= ask || new_sl <= 0.0)
return;
if(cur_sl > 0.0 && new_sl >= cur_sl)
return;
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
}
void ResyncPositionFromMarket()
{
if(position_open)
return;
ulong t = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
if(t == 0 || !PositionSelectByTicket(t))
return;
position_ticket = (int)t;
position_open = true;
current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
//+------------------------------------------------------------------+
//| Check existing position for exit conditions |
//+------------------------------------------------------------------+
void CheckExistingPosition()
{
if(!position_open)
{
return;
}
// Check if position still exists with correct magic number
if(!PositionSelectByTicketAndMagic(position_ticket, (ulong)MagicNumber))
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
return;
}
// Exit conditions based on RSI target
if(current_position_type == POSITION_TYPE_BUY)
{
// Check if RSI is against the position (below oversold)
if(rsi_current < RSI_Oversold)
{
if(!rsi_against_position)
{
rsi_against_position = true;
bars_against_count = 1;
}
else
{
bars_against_count++;
}
// Close position if RSI has been against for Y bars
if(bars_against_count >= BarsToWait)
{
ClosePosition();
return;
}
}
else
{
// RSI is no longer against the position, reset counter
if(rsi_against_position)
{
rsi_against_position = false;
bars_against_count = 0;
}
// Exit long position when RSI reaches buy target
if(rsi_current >= RSI_Target_Buy)
{
ClosePosition();
}
}
}
else if(current_position_type == POSITION_TYPE_SELL)
{
// Check if RSI is against the position (above overbought)
if(rsi_current > RSI_Overbought)
{
if(!rsi_against_position)
{
rsi_against_position = true;
bars_against_count = 1;
}
else
{
bars_against_count++;
}
// Close position if RSI has been against for Y bars
if(bars_against_count >= BarsToWait)
{
ClosePosition();
return;
}
}
else
{
// RSI is no longer against the position, reset counter
if(rsi_against_position)
{
rsi_against_position = false;
bars_against_count = 0;
}
// Exit short position when RSI reaches sell target
if(rsi_current <= RSI_Target_Sell)
{
ClosePosition();
}
}
}
}
//+------------------------------------------------------------------+
//| Check for entry signals |
//+------------------------------------------------------------------+
void CheckEntrySignals()
{
const double upSlope1 = rsi_prev - rsi_two_bars_ago; // older->prev
const double upSlope2 = rsi_current - rsi_prev; // prev->current
const double dnSlope1 = rsi_two_bars_ago - rsi_prev; // older->prev
const double dnSlope2 = rsi_prev - rsi_current; // prev->current
const bool buySlopeOk = (!UseEntrySlopeFilter) || (upSlope1 >= EntryMinSlopePerBar && upSlope2 >= EntryMinSlopePerBar);
const bool sellSlopeOk = (!UseEntrySlopeFilter) || (dnSlope1 >= EntryMinSlopePerBar && dnSlope2 >= EntryMinSlopePerBar);
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold && buySlopeOk)
{
OpenBuyPosition();
}
// Sell signal: RSI crosses from overbought to below overbought (checking the actual crossover)
if(rsi_two_bars_ago >= RSI_Overbought && rsi_prev < RSI_Overbought && sellSlopeOk)
{
OpenSellPosition();
}
}
//+------------------------------------------------------------------+
//| Open buy position |
//+------------------------------------------------------------------+
void OpenBuyPosition()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
{
const ulong t = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
if(t > 0 && PositionSelectByTicketSymbolAndMagic(t, _Symbol, (ulong)MagicNumber))
{
position_ticket = (int)t;
position_open = true;
current_position_type = POSITION_TYPE_BUY;
}
}
}
//+------------------------------------------------------------------+
//| Open sell position |
//+------------------------------------------------------------------+
void OpenSellPosition()
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
{
const ulong t = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
if(t > 0 && PositionSelectByTicketSymbolAndMagic(t, _Symbol, (ulong)MagicNumber))
{
position_ticket = (int)t;
position_open = true;
current_position_type = POSITION_TYPE_SELL;
}
}
}
//+------------------------------------------------------------------+
//| Close current position |
//+------------------------------------------------------------------+
void ClosePosition()
{
if(ClosePositionByMagic(trade, _Symbol, (ulong)MagicNumber))
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
ResetIntrabarGiveBackState();
return;
}
if(!PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
ResetIntrabarGiveBackState();
return;
}
Print("RSIScalpingXAUUSD: close failed (will retry on next bar). retcode=",
trade.ResultRetcode(), " lastError=", GetLastError());
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 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"; // Default gold; same numbers as secret_sauce.set (that file uses BTCUSD as symbol)
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
}
//+------------------------------------------------------------------+
@@ -0,0 +1,284 @@
#property strict
#property version "1.00"
#include <Trade/Trade.mqh>
input ENUM_TIMEFRAMES InpHigherTF = PERIOD_H4; // Higher timeframe for MA/cross points
input int InpMAPeriod = 150; // MA period
input ENUM_MA_METHOD InpMAMethod = MODE_SMMA; // MA method
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_OPEN; // MA applied price
input int InpHTFBarsToScan = 1200; // HTF bars to scan for crossings
input double InpLineTouchTolerance = 170; // Pullback touch tolerance (points)
input double InpBreakBuffer = 90; // Break confirmation buffer (points)
input double InpLots = 0.10; // Position size
input long InpMagic = 26042501; // Magic number
input bool InpDrawTrendline = true; // Draw detected trendline
CTrade trade;
int g_maHandle = INVALID_HANDLE;
datetime g_lastBarTime = 0;
string g_lineName = "SimpleTrendline_Basis";
struct TrendlineModel
{
datetime t1;
datetime t2;
datetime t3;
double p1;
double p2;
double p3;
double a;
double b;
bool valid;
};
bool IsNewBar()
{
datetime t = iTime(_Symbol, _Period, 0);
if(t == 0)
return false;
if(t != g_lastBarTime)
{
g_lastBarTime = t;
return true;
}
return false;
}
int FindRecentCrossPoints(datetime &times[], double &prices[])
{
ArrayResize(times, 0);
ArrayResize(prices, 0);
if(g_maHandle == INVALID_HANDLE)
return 0;
int needBars = MathMax(InpHTFBarsToScan, InpMAPeriod + 20);
MqlRates rates[];
double maBuf[];
int copiedRates = CopyRates(_Symbol, InpHigherTF, 0, needBars, rates);
int copiedMa = CopyBuffer(g_maHandle, 0, 0, needBars, maBuf);
if(copiedRates <= 5 || copiedMa <= 5)
return 0;
int bars = MathMin(copiedRates, copiedMa);
ArraySetAsSeries(rates, true);
ArraySetAsSeries(maBuf, true);
for(int i = 2; i < bars - 1; i++)
{
double d0 = rates[i].close - maBuf[i];
double d1 = rates[i + 1].close - maBuf[i + 1];
if(d0 == 0.0 || d1 == 0.0 || (d0 * d1 < 0.0))
{
int n = ArraySize(times);
ArrayResize(times, n + 1);
ArrayResize(prices, n + 1);
times[n] = rates[i].time;
prices[n] = rates[i].close;
if(ArraySize(times) >= 3)
break;
}
}
return ArraySize(times);
}
bool BuildTrendlineFrom3Points(TrendlineModel &m)
{
m.valid = false;
datetime ts[];
double ps[];
int n = FindRecentCrossPoints(ts, ps);
if(n < 3)
return false;
// We collected from recent to older in series order.
// Re-map as oldest -> newest to stabilize slope direction.
datetime tOld[3];
double pOld[3];
for(int i = 0; i < 3; i++)
{
tOld[i] = ts[2 - i];
pOld[i] = ps[2 - i];
}
long t0 = (long)tOld[0];
double x1 = 0.0;
double x2 = (double)((long)tOld[1] - t0);
double x3 = (double)((long)tOld[2] - t0);
double y1 = pOld[0];
double y2 = pOld[1];
double y3 = pOld[2];
double sx = x1 + x2 + x3;
double sy = y1 + y2 + y3;
double sxx = x1 * x1 + x2 * x2 + x3 * x3;
double sxy = x1 * y1 + x2 * y2 + x3 * y3;
double den = 3.0 * sxx - sx * sx;
if(MathAbs(den) < 1e-10)
return false;
m.a = (3.0 * sxy - sx * sy) / den;
m.b = (sy - m.a * sx) / 3.0;
m.t1 = tOld[0];
m.t2 = tOld[1];
m.t3 = tOld[2];
m.p1 = pOld[0];
m.p2 = pOld[1];
m.p3 = pOld[2];
m.valid = true;
return true;
}
double TrendlinePriceAtTime(const TrendlineModel &m, datetime t)
{
if(!m.valid)
return 0.0;
double x = (double)((long)t - (long)m.t1);
return m.a * x + m.b;
}
void DrawTrendline(const TrendlineModel &m)
{
if(!InpDrawTrendline || !m.valid)
return;
datetime tStart = m.t1;
datetime tEnd = iTime(_Symbol, _Period, 0);
if(tEnd <= tStart)
tEnd = m.t3 + PeriodSeconds(_Period) * 20;
double pStart = TrendlinePriceAtTime(m, tStart);
double pEnd = TrendlinePriceAtTime(m, tEnd);
if(ObjectFind(0, g_lineName) < 0)
ObjectCreate(0, g_lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd);
else
{
ObjectMove(0, g_lineName, 0, tStart, pStart);
ObjectMove(0, g_lineName, 1, tEnd, pEnd);
}
ObjectSetInteger(0, g_lineName, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, g_lineName, OBJPROP_COLOR, clrGold);
ObjectSetInteger(0, g_lineName, OBJPROP_WIDTH, 2);
}
bool GetCurrentPosition(long &type, double &volume)
{
if(!PositionSelect(_Symbol))
return false;
if((long)PositionGetInteger(POSITION_MAGIC) != InpMagic)
return false;
type = PositionGetInteger(POSITION_TYPE);
volume = PositionGetDouble(POSITION_VOLUME);
return true;
}
void TryExitOnBreak(const TrendlineModel &m)
{
long posType;
double vol;
if(!GetCurrentPosition(posType, vol))
return;
double close1 = iClose(_Symbol, _Period, 1);
datetime t1 = iTime(_Symbol, _Period, 1);
double line1 = TrendlinePriceAtTime(m, t1);
double buf = InpBreakBuffer * _Point;
bool closePos = false;
if(posType == POSITION_TYPE_BUY && close1 < (line1 - buf))
closePos = true;
if(posType == POSITION_TYPE_SELL && close1 > (line1 + buf))
closePos = true;
if(closePos)
trade.PositionClose(_Symbol);
}
void TryPullbackEntry(const TrendlineModel &m)
{
long posType;
double vol;
if(GetCurrentPosition(posType, vol))
return;
MqlRates bars1[], bars2[];
if(CopyRates(_Symbol, _Period, 1, 1, bars1) != 1)
return;
if(CopyRates(_Symbol, _Period, 2, 1, bars2) != 1)
return;
if(ArraySize(bars1) < 1 || ArraySize(bars2) < 1)
return;
MqlRates b1 = bars1[0];
MqlRates b2 = bars2[0];
double line1 = TrendlinePriceAtTime(m, b1.time);
double tol = InpLineTouchTolerance * _Point;
bool upTrend = (m.a > 0.0);
bool downTrend = (m.a < 0.0);
if(upTrend)
{
bool touched = (b1.low <= (line1 + tol));
bool reclaim = (b1.close > line1);
bool bullish = (b1.close > b1.open);
bool stillHealthy = (b2.close >= TrendlinePriceAtTime(m, b2.time) - tol);
if(touched && reclaim && bullish && stillHealthy)
{
trade.Buy(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback buy");
}
}
else if(downTrend)
{
bool touched = (b1.high >= (line1 - tol));
bool reject = (b1.close < line1);
bool bearish = (b1.close < b1.open);
bool stillWeak = (b2.close <= TrendlinePriceAtTime(m, b2.time) + tol);
if(touched && reject && bearish && stillWeak)
{
trade.Sell(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback sell");
}
}
}
int OnInit()
{
g_maHandle = iMA(_Symbol, InpHigherTF, InpMAPeriod, 0, InpMAMethod, InpAppliedPrice);
if(g_maHandle == INVALID_HANDLE)
return INIT_FAILED;
trade.SetExpertMagicNumber(InpMagic);
g_lastBarTime = 0;
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(g_maHandle != INVALID_HANDLE)
IndicatorRelease(g_maHandle);
if(ObjectFind(0, g_lineName) >= 0)
ObjectDelete(0, g_lineName);
}
void OnTick()
{
if(!IsNewBar())
return;
TrendlineModel m;
if(!BuildTrendlineFrom3Points(m))
return;
DrawTrendline(m);
TryExitOnBreak(m);
TryPullbackEntry(m);
}
@@ -0,0 +1,14 @@
; SimpleTrendline.mq5 optimization preset
; Strategy Tester -> Inputs -> Load
; Focus: trendline pullback entries + break exits (no broker SL/TP)
;
InpHigherTF=16385||16385||0||16388||Y
InpMAPeriod=50||20||5||200||Y
InpMAMethod=1||0||1||3||Y
InpAppliedPrice=0||0||1||6||Y
InpHTFBarsToScan=400||200||100||1200||Y
InpLineTouchTolerance=100.0||30.0||10.0||300.0||Y
InpBreakBuffer=30.0||5.0||5.0||120.0||Y
InpLots=0.10||0.10||0.01||0.10||N
InpMagic=26042501||26042501||1||26042501||N
InpDrawTrendline=false||false||0||true||N
@@ -0,0 +1,284 @@
#property strict
#property version "1.00"
#include <Trade/Trade.mqh>
input ENUM_TIMEFRAMES InpHigherTF = PERIOD_M15; // Higher timeframe for MA/cross points
input int InpMAPeriod = 65; // MA period
input ENUM_MA_METHOD InpMAMethod = MODE_LWMA; // MA method
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_OPEN; // MA applied price
input int InpHTFBarsToScan = 1200; // HTF bars to scan for crossings
input double InpLineTouchTolerance = 100; // Pullback touch tolerance (points)
input double InpBreakBuffer = 80; // Break confirmation buffer (points)
input double InpLots = 0.10; // Position size
input long InpMagic = 26042501; // Magic number
input bool InpDrawTrendline = true; // Draw detected trendline
CTrade trade;
int g_maHandle = INVALID_HANDLE;
datetime g_lastBarTime = 0;
string g_lineName = "SimpleTrendline_Basis";
struct TrendlineModel
{
datetime t1;
datetime t2;
datetime t3;
double p1;
double p2;
double p3;
double a;
double b;
bool valid;
};
bool IsNewBar()
{
datetime t = iTime(_Symbol, _Period, 0);
if(t == 0)
return false;
if(t != g_lastBarTime)
{
g_lastBarTime = t;
return true;
}
return false;
}
int FindRecentCrossPoints(datetime &times[], double &prices[])
{
ArrayResize(times, 0);
ArrayResize(prices, 0);
if(g_maHandle == INVALID_HANDLE)
return 0;
int needBars = MathMax(InpHTFBarsToScan, InpMAPeriod + 20);
MqlRates rates[];
double maBuf[];
int copiedRates = CopyRates(_Symbol, InpHigherTF, 0, needBars, rates);
int copiedMa = CopyBuffer(g_maHandle, 0, 0, needBars, maBuf);
if(copiedRates <= 5 || copiedMa <= 5)
return 0;
int bars = MathMin(copiedRates, copiedMa);
ArraySetAsSeries(rates, true);
ArraySetAsSeries(maBuf, true);
for(int i = 2; i < bars - 1; i++)
{
double d0 = rates[i].close - maBuf[i];
double d1 = rates[i + 1].close - maBuf[i + 1];
if(d0 == 0.0 || d1 == 0.0 || (d0 * d1 < 0.0))
{
int n = ArraySize(times);
ArrayResize(times, n + 1);
ArrayResize(prices, n + 1);
times[n] = rates[i].time;
prices[n] = rates[i].close;
if(ArraySize(times) >= 3)
break;
}
}
return ArraySize(times);
}
bool BuildTrendlineFrom3Points(TrendlineModel &m)
{
m.valid = false;
datetime ts[];
double ps[];
int n = FindRecentCrossPoints(ts, ps);
if(n < 3)
return false;
// We collected from recent to older in series order.
// Re-map as oldest -> newest to stabilize slope direction.
datetime tOld[3];
double pOld[3];
for(int i = 0; i < 3; i++)
{
tOld[i] = ts[2 - i];
pOld[i] = ps[2 - i];
}
long t0 = (long)tOld[0];
double x1 = 0.0;
double x2 = (double)((long)tOld[1] - t0);
double x3 = (double)((long)tOld[2] - t0);
double y1 = pOld[0];
double y2 = pOld[1];
double y3 = pOld[2];
double sx = x1 + x2 + x3;
double sy = y1 + y2 + y3;
double sxx = x1 * x1 + x2 * x2 + x3 * x3;
double sxy = x1 * y1 + x2 * y2 + x3 * y3;
double den = 3.0 * sxx - sx * sx;
if(MathAbs(den) < 1e-10)
return false;
m.a = (3.0 * sxy - sx * sy) / den;
m.b = (sy - m.a * sx) / 3.0;
m.t1 = tOld[0];
m.t2 = tOld[1];
m.t3 = tOld[2];
m.p1 = pOld[0];
m.p2 = pOld[1];
m.p3 = pOld[2];
m.valid = true;
return true;
}
double TrendlinePriceAtTime(const TrendlineModel &m, datetime t)
{
if(!m.valid)
return 0.0;
double x = (double)((long)t - (long)m.t1);
return m.a * x + m.b;
}
void DrawTrendline(const TrendlineModel &m)
{
if(!InpDrawTrendline || !m.valid)
return;
datetime tStart = m.t1;
datetime tEnd = iTime(_Symbol, _Period, 0);
if(tEnd <= tStart)
tEnd = m.t3 + PeriodSeconds(_Period) * 20;
double pStart = TrendlinePriceAtTime(m, tStart);
double pEnd = TrendlinePriceAtTime(m, tEnd);
if(ObjectFind(0, g_lineName) < 0)
ObjectCreate(0, g_lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd);
else
{
ObjectMove(0, g_lineName, 0, tStart, pStart);
ObjectMove(0, g_lineName, 1, tEnd, pEnd);
}
ObjectSetInteger(0, g_lineName, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, g_lineName, OBJPROP_COLOR, clrGold);
ObjectSetInteger(0, g_lineName, OBJPROP_WIDTH, 2);
}
bool GetCurrentPosition(long &type, double &volume)
{
if(!PositionSelect(_Symbol))
return false;
if((long)PositionGetInteger(POSITION_MAGIC) != InpMagic)
return false;
type = PositionGetInteger(POSITION_TYPE);
volume = PositionGetDouble(POSITION_VOLUME);
return true;
}
void TryExitOnBreak(const TrendlineModel &m)
{
long posType;
double vol;
if(!GetCurrentPosition(posType, vol))
return;
double close1 = iClose(_Symbol, _Period, 1);
datetime t1 = iTime(_Symbol, _Period, 1);
double line1 = TrendlinePriceAtTime(m, t1);
double buf = InpBreakBuffer * _Point;
bool closePos = false;
if(posType == POSITION_TYPE_BUY && close1 < (line1 - buf))
closePos = true;
if(posType == POSITION_TYPE_SELL && close1 > (line1 + buf))
closePos = true;
if(closePos)
trade.PositionClose(_Symbol);
}
void TryPullbackEntry(const TrendlineModel &m)
{
long posType;
double vol;
if(GetCurrentPosition(posType, vol))
return;
MqlRates bars1[], bars2[];
if(CopyRates(_Symbol, _Period, 1, 1, bars1) != 1)
return;
if(CopyRates(_Symbol, _Period, 2, 1, bars2) != 1)
return;
if(ArraySize(bars1) < 1 || ArraySize(bars2) < 1)
return;
MqlRates b1 = bars1[0];
MqlRates b2 = bars2[0];
double line1 = TrendlinePriceAtTime(m, b1.time);
double tol = InpLineTouchTolerance * _Point;
bool upTrend = (m.a > 0.0);
bool downTrend = (m.a < 0.0);
if(upTrend)
{
bool touched = (b1.low <= (line1 + tol));
bool reclaim = (b1.close > line1);
bool bullish = (b1.close > b1.open);
bool stillHealthy = (b2.close >= TrendlinePriceAtTime(m, b2.time) - tol);
if(touched && reclaim && bullish && stillHealthy)
{
trade.Buy(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback buy");
}
}
else if(downTrend)
{
bool touched = (b1.high >= (line1 - tol));
bool reject = (b1.close < line1);
bool bearish = (b1.close < b1.open);
bool stillWeak = (b2.close <= TrendlinePriceAtTime(m, b2.time) + tol);
if(touched && reject && bearish && stillWeak)
{
trade.Sell(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback sell");
}
}
}
int OnInit()
{
g_maHandle = iMA(_Symbol, InpHigherTF, InpMAPeriod, 0, InpMAMethod, InpAppliedPrice);
if(g_maHandle == INVALID_HANDLE)
return INIT_FAILED;
trade.SetExpertMagicNumber(InpMagic);
g_lastBarTime = 0;
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(g_maHandle != INVALID_HANDLE)
IndicatorRelease(g_maHandle);
if(ObjectFind(0, g_lineName) >= 0)
ObjectDelete(0, g_lineName);
}
void OnTick()
{
if(!IsNewBar())
return;
TrendlineModel m;
if(!BuildTrendlineFrom3Points(m))
return;
DrawTrendline(m);
TryExitOnBreak(m);
TryPullbackEntry(m);
}
@@ -0,0 +1,14 @@
; SimpleTrendline.mq5 optimization preset
; Strategy Tester -> Inputs -> Load
; Focus: trendline pullback entries + break exits (no broker SL/TP)
;
InpHigherTF=16385||16385||0||16388||Y
InpMAPeriod=50||20||5||200||Y
InpMAMethod=1||0||1||3||Y
InpAppliedPrice=0||0||1||6||Y
InpHTFBarsToScan=400||200||100||1200||Y
InpLineTouchTolerance=100.0||30.0||10.0||300.0||Y
InpBreakBuffer=30.0||5.0||5.0||120.0||Y
InpLots=0.10||0.10||0.01||0.10||N
InpMagic=26042501||26042501||1||26042501||N
InpDrawTrendline=false||false||0||true||N
@@ -0,0 +1,376 @@
#property strict
#property version "1.00"
#include <Trade/Trade.mqh>
input ENUM_TIMEFRAMES InpHigherTF = PERIOD_M10; // Higher timeframe for MA/cross points
input int InpMAPeriod = 65; // MA period
input ENUM_MA_METHOD InpMAMethod = MODE_EMA; // MA method
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_OPEN; // MA applied price
input int InpHTFBarsToScan = 500; // HTF bars to scan for crossings
input double InpLineTouchTolerance = 220; // Pullback touch tolerance (points)
input double InpBreakBuffer = 110; // Break confirmation buffer (points)
input double InpLots = 0.10; // Position size
input long InpMagic = 26042501; // Magic number
input bool InpDrawTrendline = true; // Draw detected trendline
input bool InpUseSessionModeGate = true; // Block entries when symbol/session disallow opens
input bool InpBypassGateInTester = true; // Ignore gate in Strategy Tester for optimization
CTrade trade;
int g_maHandle = INVALID_HANDLE;
datetime g_lastBarTime = 0;
string g_lineName = "SimpleTrendline_Basis";
datetime g_lastEntryBlockLog = 0;
struct TrendlineModel
{
datetime t1;
datetime t2;
datetime t3;
double p1;
double p2;
double p3;
double a;
double b;
bool valid;
};
bool IsNewBar()
{
datetime t = iTime(_Symbol, _Period, 0);
if(t == 0)
return false;
if(t != g_lastBarTime)
{
g_lastBarTime = t;
return true;
}
return false;
}
int FindRecentCrossPoints(datetime &times[], double &prices[])
{
ArrayResize(times, 0);
ArrayResize(prices, 0);
if(g_maHandle == INVALID_HANDLE)
return 0;
int needBars = MathMax(InpHTFBarsToScan, InpMAPeriod + 20);
MqlRates rates[];
double maBuf[];
int copiedRates = CopyRates(_Symbol, InpHigherTF, 0, needBars, rates);
int copiedMa = CopyBuffer(g_maHandle, 0, 0, needBars, maBuf);
if(copiedRates <= 5 || copiedMa <= 5)
return 0;
int bars = MathMin(copiedRates, copiedMa);
ArraySetAsSeries(rates, true);
ArraySetAsSeries(maBuf, true);
for(int i = 2; i < bars - 1; i++)
{
double d0 = rates[i].close - maBuf[i];
double d1 = rates[i + 1].close - maBuf[i + 1];
if(d0 == 0.0 || d1 == 0.0 || (d0 * d1 < 0.0))
{
int n = ArraySize(times);
ArrayResize(times, n + 1);
ArrayResize(prices, n + 1);
times[n] = rates[i].time;
prices[n] = rates[i].close;
if(ArraySize(times) >= 3)
break;
}
}
return ArraySize(times);
}
bool BuildTrendlineFrom3Points(TrendlineModel &m)
{
m.valid = false;
datetime ts[];
double ps[];
int n = FindRecentCrossPoints(ts, ps);
if(n < 3)
return false;
// We collected from recent to older in series order.
// Re-map as oldest -> newest to stabilize slope direction.
datetime tOld[3];
double pOld[3];
for(int i = 0; i < 3; i++)
{
tOld[i] = ts[2 - i];
pOld[i] = ps[2 - i];
}
long t0 = (long)tOld[0];
double x1 = 0.0;
double x2 = (double)((long)tOld[1] - t0);
double x3 = (double)((long)tOld[2] - t0);
double y1 = pOld[0];
double y2 = pOld[1];
double y3 = pOld[2];
double sx = x1 + x2 + x3;
double sy = y1 + y2 + y3;
double sxx = x1 * x1 + x2 * x2 + x3 * x3;
double sxy = x1 * y1 + x2 * y2 + x3 * y3;
double den = 3.0 * sxx - sx * sx;
if(MathAbs(den) < 1e-10)
return false;
m.a = (3.0 * sxy - sx * sy) / den;
m.b = (sy - m.a * sx) / 3.0;
m.t1 = tOld[0];
m.t2 = tOld[1];
m.t3 = tOld[2];
m.p1 = pOld[0];
m.p2 = pOld[1];
m.p3 = pOld[2];
m.valid = true;
return true;
}
double TrendlinePriceAtTime(const TrendlineModel &m, datetime t)
{
if(!m.valid)
return 0.0;
double x = (double)((long)t - (long)m.t1);
return m.a * x + m.b;
}
void DrawTrendline(const TrendlineModel &m)
{
if(!InpDrawTrendline || !m.valid)
return;
datetime tStart = m.t1;
datetime tEnd = iTime(_Symbol, _Period, 0);
if(tEnd <= tStart)
tEnd = m.t3 + PeriodSeconds(_Period) * 20;
double pStart = TrendlinePriceAtTime(m, tStart);
double pEnd = TrendlinePriceAtTime(m, tEnd);
if(ObjectFind(0, g_lineName) < 0)
ObjectCreate(0, g_lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd);
else
{
ObjectMove(0, g_lineName, 0, tStart, pStart);
ObjectMove(0, g_lineName, 1, tEnd, pEnd);
}
ObjectSetInteger(0, g_lineName, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, g_lineName, OBJPROP_COLOR, clrGold);
ObjectSetInteger(0, g_lineName, OBJPROP_WIDTH, 2);
}
bool GetCurrentPosition(long &type, double &volume)
{
if(!PositionSelect(_Symbol))
return false;
if((long)PositionGetInteger(POSITION_MAGIC) != InpMagic)
return false;
type = PositionGetInteger(POSITION_TYPE);
volume = PositionGetDouble(POSITION_VOLUME);
return true;
}
bool IsWithinAnyTradeSession(const datetime nowServer)
{
MqlDateTime dt;
TimeToStruct(nowServer, dt);
ENUM_DAY_OF_WEEK day = (ENUM_DAY_OF_WEEK)dt.day_of_week;
int nowSec = dt.hour * 3600 + dt.min * 60 + dt.sec;
datetime from = 0;
datetime to = 0;
bool hasAny = false;
for(uint idx = 0; idx < 16; idx++)
{
if(!SymbolInfoSessionTrade(_Symbol, day, idx, from, to))
break;
hasAny = true;
// SymbolInfoSessionTrade returns session boundaries as time-of-day values.
MqlDateTime fdt, tdt;
TimeToStruct(from, fdt);
TimeToStruct(to, tdt);
int fromSec = fdt.hour * 3600 + fdt.min * 60 + fdt.sec;
int toSec = tdt.hour * 3600 + tdt.min * 60 + tdt.sec;
// from==to on some brokers means full-day session.
if(fromSec == toSec)
{
return true;
}
else if(fromSec < toSec)
{
if(nowSec >= fromSec && nowSec <= toSec)
return true;
}
else
{
// Session passes midnight.
if(nowSec >= fromSec || nowSec <= toSec)
return true;
}
}
// If broker does not expose sessions for this symbol, do not block by session.
if(!hasAny)
return true;
return false;
}
bool CanOpenNewPositionNow(const ENUM_ORDER_TYPE orderType)
{
if(!InpUseSessionModeGate)
return true;
if(InpBypassGateInTester && (bool)MQLInfoInteger(MQL_TESTER))
return true;
long tradeMode = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE);
if(tradeMode == SYMBOL_TRADE_MODE_DISABLED ||
tradeMode == SYMBOL_TRADE_MODE_CLOSEONLY)
return false;
if(orderType == ORDER_TYPE_BUY &&
tradeMode == SYMBOL_TRADE_MODE_SHORTONLY)
return false;
if(orderType == ORDER_TYPE_SELL &&
tradeMode == SYMBOL_TRADE_MODE_LONGONLY)
return false;
if(!IsWithinAnyTradeSession(TimeCurrent()))
return false;
return true;
}
void TryExitOnBreak(const TrendlineModel &m)
{
long posType;
double vol;
if(!GetCurrentPosition(posType, vol))
return;
double close1 = iClose(_Symbol, _Period, 1);
datetime t1 = iTime(_Symbol, _Period, 1);
double line1 = TrendlinePriceAtTime(m, t1);
double buf = InpBreakBuffer * _Point;
bool closePos = false;
if(posType == POSITION_TYPE_BUY && close1 < (line1 - buf))
closePos = true;
if(posType == POSITION_TYPE_SELL && close1 > (line1 + buf))
closePos = true;
if(closePos)
trade.PositionClose(_Symbol);
}
void TryPullbackEntry(const TrendlineModel &m)
{
long posType;
double vol;
if(GetCurrentPosition(posType, vol))
return;
MqlRates bars1[], bars2[];
if(CopyRates(_Symbol, _Period, 1, 1, bars1) != 1)
return;
if(CopyRates(_Symbol, _Period, 2, 1, bars2) != 1)
return;
if(ArraySize(bars1) < 1 || ArraySize(bars2) < 1)
return;
MqlRates b1 = bars1[0];
MqlRates b2 = bars2[0];
double line1 = TrendlinePriceAtTime(m, b1.time);
double tol = InpLineTouchTolerance * _Point;
bool upTrend = (m.a > 0.0);
bool downTrend = (m.a < 0.0);
if(upTrend)
{
bool touched = (b1.low <= (line1 + tol));
bool reclaim = (b1.close > line1);
bool bullish = (b1.close > b1.open);
bool stillHealthy = (b2.close >= TrendlinePriceAtTime(m, b2.time) - tol);
if(touched && reclaim && bullish && stillHealthy)
{
if(!CanOpenNewPositionNow(ORDER_TYPE_BUY))
{
datetime nowBar = iTime(_Symbol, _Period, 0);
if(nowBar != g_lastEntryBlockLog)
{
g_lastEntryBlockLog = nowBar;
Print("Buy entry skipped: symbol mode/session does not allow opening now");
}
return;
}
trade.Buy(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback buy");
}
}
else if(downTrend)
{
bool touched = (b1.high >= (line1 - tol));
bool reject = (b1.close < line1);
bool bearish = (b1.close < b1.open);
bool stillWeak = (b2.close <= TrendlinePriceAtTime(m, b2.time) + tol);
if(touched && reject && bearish && stillWeak)
{
if(!CanOpenNewPositionNow(ORDER_TYPE_SELL))
{
datetime nowBar = iTime(_Symbol, _Period, 0);
if(nowBar != g_lastEntryBlockLog)
{
g_lastEntryBlockLog = nowBar;
Print("Sell entry skipped: symbol mode/session does not allow opening now");
}
return;
}
trade.Sell(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback sell");
}
}
}
int OnInit()
{
g_maHandle = iMA(_Symbol, InpHigherTF, InpMAPeriod, 0, InpMAMethod, InpAppliedPrice);
if(g_maHandle == INVALID_HANDLE)
return INIT_FAILED;
trade.SetExpertMagicNumber(InpMagic);
g_lastBarTime = 0;
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(g_maHandle != INVALID_HANDLE)
IndicatorRelease(g_maHandle);
if(ObjectFind(0, g_lineName) >= 0)
ObjectDelete(0, g_lineName);
}
void OnTick()
{
if(!IsNewBar())
return;
TrendlineModel m;
if(!BuildTrendlineFrom3Points(m))
return;
DrawTrendline(m);
TryExitOnBreak(m);
TryPullbackEntry(m);
}
@@ -0,0 +1,14 @@
; SimpleTrendline.mq5 optimization preset
; Strategy Tester -> Inputs -> Load
; Focus: trendline pullback entries + break exits (no broker SL/TP)
;
InpHigherTF=16385||16385||0||16388||Y
InpMAPeriod=50||20||5||200||Y
InpMAMethod=1||0||1||3||Y
InpAppliedPrice=0||0||1||6||Y
InpHTFBarsToScan=400||200||100||1200||Y
InpLineTouchTolerance=100.0||30.0||10.0||300.0||Y
InpBreakBuffer=30.0||5.0||5.0||120.0||Y
InpLots=0.10||0.10||0.01||0.10||N
InpMagic=26042501||26042501||1||26042501||N
InpDrawTrendline=false||false||0||true||N
@@ -0,0 +1,36 @@
; SuperEMA — defaults aligned with lab/EAs/SuperEMA.mq5 (v1.01)
; Load from Strategy Tester → Inputs → context menu → Load
;
; === Market ===
InpSymbol=
InpTimeframe=15||15||0||49153||N
InpLots=0.01||0.01||0.01||0.10||N
InpSlippagePoints=55||20||5||120||Y
InpMagic=940001||940001||1||9400010||N
; === EMA (trend & structure) ===
InpEmaFast=40||20||10||120||Y
InpEmaMid=180||60||15||200||Y
InpEmaSlow=125||100||25||400||Y
InpEmaTrendBars=3||1||1||3||Y
; === CCI ===
InpCciPeriod=17||7||1||28||Y
InpCciOverbought=80.0||80.0||10.0||140.0||Y
InpCciOversold=-140.0||-140.0||10.0||-80.0||Y
InpPullbackCciLookback=20||4||2||24||Y
; === MACD (histogram = main - signal) ===
InpMacdFast=14||8||2||20||Y
InpMacdSlow=38||20||2||40||Y
InpMacdSignal=9||5||1||15||Y
; === Strategy ===
InpEntryStyle=1||0||1||2||Y
InpOneTradeOnly=true||false||0||true||N
InpUseStructuralSL=false||false||0||true||Y
InpSlBufferPoints=110.0||20.0||10.0||200.0||Y
; === Exits (so trades do not run forever) ===
InpExitOnTrendFlip=false||false||0||true||Y
InpExitOnMacdFlip=false||false||0||true||Y
InpExitOnCciZeroCross=true||false||0||true||Y
InpMaxHoldingBars=168||48||24||480||Y
InpExitBelowMidEma=false||false||0||true||Y
; === Debug ===
InpDebugLogs=false||false||0||true||N
@@ -0,0 +1,448 @@
//+------------------------------------------------------------------+
//| SuperEMA.mq5 |
//| EMA + CCI + MACD histogram — trend filter, momentum confirmation |
//+------------------------------------------------------------------+
#property strict
#property version "1.01"
#include <Trade/Trade.mqh>
enum ENUM_ENTRY_STYLE
{
ENTRY_CCIZERO_MACD = 0, // EMA trend + CCI crosses zero + MACD histogram agrees
ENTRY_LAMBERT = 1, // EMA trend + CCI crosses ±100 + MACD histogram agrees
ENTRY_PULLBACK = 2 // Uptrend: pullback to fast EMA + CCI was oversold + CCI crosses up through 0 + MACD > 0 (mirror for sells)
};
input group "=== Market ==="
input string InpSymbol = "";
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M15;
input double InpLots = 0.01;
input int InpSlippagePoints = 55;
input int InpMagic = 940001;
input group "=== EMA (trend & structure) ==="
input int InpEmaFast = 40;
input int InpEmaMid = 180;
input int InpEmaSlow = 125;
input int InpEmaTrendBars = 3; // closed bar shift for EMA reads
input group "=== CCI ==="
input int InpCciPeriod = 17;
input double InpCciOverbought = 80.0;
input double InpCciOversold = -140.0;
input int InpPullbackCciLookback = 20; // bars to check prior CCI oversold/overbought
input group "=== MACD (histogram = main - signal) ==="
input int InpMacdFast = 14;
input int InpMacdSlow = 38;
input int InpMacdSignal = 9;
input group "=== Strategy ==="
input ENUM_ENTRY_STYLE InpEntryStyle = ENTRY_LAMBERT;
input bool InpOneTradeOnly = true;
input bool InpUseStructuralSL = false;
input double InpSlBufferPoints = 110;
input group "=== Exits (so trades do not run forever) ==="
input bool InpExitOnTrendFlip = false; // close when price vs slow EMA flips against position
input bool InpExitOnMacdFlip = false; // close when MACD histogram flips against position
input bool InpExitOnCciZeroCross = true; // long: CCI crosses below 0; short: CCI crosses above 0
input int InpMaxHoldingBars = 168; // 0 = disabled (e.g. ~8 days M15)
input bool InpExitBelowMidEma = false; // long: close if close < mid EMA (invalidation)
input group "=== Debug ==="
input bool InpDebugLogs = false;
CTrade trade;
datetime g_lastBarTime = 0;
string WorkSymbol()
{
return (InpSymbol == "" || InpSymbol == NULL) ? _Symbol : InpSymbol;
}
void Log(const string s)
{
if(InpDebugLogs)
Print("[SuperEMA] ", s);
}
bool IsNewBar(const string sym, const ENUM_TIMEFRAMES tf)
{
datetime t = iTime(sym, tf, 0);
if(t <= 0 || t == g_lastBarTime)
return false;
g_lastBarTime = t;
return true;
}
double EmaAt(const string sym, const ENUM_TIMEFRAMES tf, const int period, const int shift)
{
int h = iMA(sym, tf, period, 0, MODE_EMA, PRICE_CLOSE);
if(h == INVALID_HANDLE)
return 0.0;
double b[1];
if(CopyBuffer(h, 0, shift, 1, b) <= 0)
{
IndicatorRelease(h);
return 0.0;
}
IndicatorRelease(h);
return b[0];
}
double CciAt(const string sym, const ENUM_TIMEFRAMES tf, const int period, const int shift)
{
int h = iCCI(sym, tf, period, PRICE_TYPICAL);
if(h == INVALID_HANDLE)
return 0.0;
double b[1];
if(CopyBuffer(h, 0, shift, 1, b) <= 0)
{
IndicatorRelease(h);
return 0.0;
}
IndicatorRelease(h);
return b[0];
}
bool MacdHistAt(const string sym, const ENUM_TIMEFRAMES tf, const int fast, const int slow, const int signal, const int shift, double &hist)
{
int h = iMACD(sym, tf, fast, slow, signal, PRICE_CLOSE);
if(h == INVALID_HANDLE)
return false;
double mainLine[1], sigLine[1];
if(CopyBuffer(h, 0, shift, 1, mainLine) <= 0 || CopyBuffer(h, 1, shift, 1, sigLine) <= 0)
{
IndicatorRelease(h);
return false;
}
IndicatorRelease(h);
hist = mainLine[0] - sigLine[0];
return true;
}
bool TrendUp(const string sym, const int sh)
{
double c = iClose(sym, InpTimeframe, sh);
double emaS = EmaAt(sym, InpTimeframe, InpEmaSlow, sh);
return (emaS > 0.0 && c > emaS);
}
bool TrendDown(const string sym, const int sh)
{
double c = iClose(sym, InpTimeframe, sh);
double emaS = EmaAt(sym, InpTimeframe, InpEmaSlow, sh);
return (emaS > 0.0 && c < emaS);
}
bool CciCrossAboveZero(const string sym)
{
double c1 = CciAt(sym, InpTimeframe, InpCciPeriod, 1);
double c2 = CciAt(sym, InpTimeframe, InpCciPeriod, 2);
return (c2 <= 0.0 && c1 > 0.0);
}
bool CciCrossBelowZero(const string sym)
{
double c1 = CciAt(sym, InpTimeframe, InpCciPeriod, 1);
double c2 = CciAt(sym, InpTimeframe, InpCciPeriod, 2);
return (c2 >= 0.0 && c1 < 0.0);
}
bool CciCrossAbove100(const string sym)
{
double c1 = CciAt(sym, InpTimeframe, InpCciPeriod, 1);
double c2 = CciAt(sym, InpTimeframe, InpCciPeriod, 2);
return (c2 < InpCciOverbought && c1 > InpCciOverbought);
}
bool CciCrossBelowMinus100(const string sym)
{
double c1 = CciAt(sym, InpTimeframe, InpCciPeriod, 1);
double c2 = CciAt(sym, InpTimeframe, InpCciPeriod, 2);
return (c2 > InpCciOversold && c1 < InpCciOversold);
}
bool HadCciOversoldRecently(const string sym)
{
for(int i = 2; i <= InpPullbackCciLookback + 1; i++)
{
double v = CciAt(sym, InpTimeframe, InpCciPeriod, i);
if(v <= InpCciOversold)
return true;
}
return false;
}
bool HadCciOverboughtRecently(const string sym)
{
for(int i = 2; i <= InpPullbackCciLookback + 1; i++)
{
double v = CciAt(sym, InpTimeframe, InpCciPeriod, i);
if(v >= InpCciOverbought)
return true;
}
return false;
}
bool PullbackNearFastEmaLong(const string sym)
{
double emaF = EmaAt(sym, InpTimeframe, InpEmaFast, 1);
double lo = iLow(sym, InpTimeframe, 1);
if(emaF <= 0.0)
return false;
return (lo <= emaF + InpSlBufferPoints * _Point * 3.0);
}
bool PullbackNearFastEmaShort(const string sym)
{
double emaF = EmaAt(sym, InpTimeframe, InpEmaFast, 1);
double hi = iHigh(sym, InpTimeframe, 1);
if(emaF <= 0.0)
return false;
return (hi >= emaF - InpSlBufferPoints * _Point * 3.0);
}
int PositionsByMagic(const string sym, const int magic)
{
int n = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong t = PositionGetTicket(i);
if(t == 0)
continue;
if(PositionGetString(POSITION_SYMBOL) == sym && (int)PositionGetInteger(POSITION_MAGIC) == magic)
n++;
}
return n;
}
void ComputeSLTP(const bool isBuy, const double entry, double &sl, double &tp)
{
const string sym = WorkSymbol();
sl = 0.0;
tp = 0.0;
if(!InpUseStructuralSL)
return;
double emaM = EmaAt(sym, InpTimeframe, InpEmaMid, InpEmaTrendBars);
double buf = InpSlBufferPoints * _Point;
if(isBuy)
sl = emaM - buf;
else
sl = emaM + buf;
}
int BarsSinceOpen(const string sym, const datetime openTime)
{
if(openTime <= 0)
return 0;
int sh = iBarShift(sym, InpTimeframe, openTime, false);
if(sh < 0)
return 999999;
return sh;
}
void ClosePositionTicket(const ulong ticket, const string reason)
{
trade.SetExpertMagicNumber(InpMagic);
if(trade.PositionClose(ticket))
Log("Close: " + reason);
}
void ManageSuperEMAExits(const string sym)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) != sym)
continue;
if((int)PositionGetInteger(POSITION_MAGIC) != InpMagic)
continue;
ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
double h1 = 0.0;
if(!MacdHistAt(sym, InpTimeframe, InpMacdFast, InpMacdSlow, InpMacdSignal, 1, h1))
continue;
bool closeLong = false;
bool closeShort = false;
string reason = "";
if(InpMaxHoldingBars > 0)
{
int held = BarsSinceOpen(sym, openTime);
if(held >= InpMaxHoldingBars)
{
if(ptype == POSITION_TYPE_BUY)
closeLong = true;
else
closeShort = true;
reason = "time stop (max bars)";
}
}
if(ptype == POSITION_TYPE_BUY)
{
if(InpExitOnTrendFlip && TrendDown(sym, InpEmaTrendBars))
{
closeLong = true;
reason = "trend flip (below slow EMA)";
}
if(InpExitOnMacdFlip && h1 < 0.0)
{
closeLong = true;
reason = "MACD histogram < 0";
}
if(InpExitOnCciZeroCross && CciCrossBelowZero(sym))
{
closeLong = true;
reason = "CCI crossed below zero";
}
if(InpExitBelowMidEma)
{
double c = iClose(sym, InpTimeframe, 1);
double emaM = EmaAt(sym, InpTimeframe, InpEmaMid, 1);
if(emaM > 0.0 && c < emaM)
{
closeLong = true;
reason = "close below mid EMA";
}
}
if(closeLong)
ClosePositionTicket(ticket, reason);
}
else if(ptype == POSITION_TYPE_SELL)
{
if(InpExitOnTrendFlip && TrendUp(sym, InpEmaTrendBars))
{
closeShort = true;
reason = "trend flip (above slow EMA)";
}
if(InpExitOnMacdFlip && h1 > 0.0)
{
closeShort = true;
reason = "MACD histogram > 0";
}
if(InpExitOnCciZeroCross && CciCrossAboveZero(sym))
{
closeShort = true;
reason = "CCI crossed above zero";
}
if(InpExitBelowMidEma)
{
double c = iClose(sym, InpTimeframe, 1);
double emaM = EmaAt(sym, InpTimeframe, InpEmaMid, 1);
if(emaM > 0.0 && c > emaM)
{
closeShort = true;
reason = "close above mid EMA";
}
}
if(closeShort)
ClosePositionTicket(ticket, reason);
}
}
}
int OnInit()
{
string sym = WorkSymbol();
if(!SymbolSelect(sym, true))
{
Print("SuperEMA: cannot select symbol ", sym);
return INIT_FAILED;
}
trade.SetExpertMagicNumber(InpMagic);
trade.SetDeviationInPoints(InpSlippagePoints);
return INIT_SUCCEEDED;
}
void OnTick()
{
string sym = WorkSymbol();
if(_Symbol != sym)
{
static datetime lastLog = 0;
datetime tb = iTime(_Symbol, PERIOD_M1, 0);
if(tb != lastLog && InpDebugLogs)
{
lastLog = tb;
Log("Chart symbol differs from WorkSymbol; attach to " + sym + " or set InpSymbol empty.");
}
return;
}
if(!IsNewBar(sym, InpTimeframe))
return;
// Exits must run every bar; do not skip when a position exists (otherwise trades never close with SL=0/TP=0).
ManageSuperEMAExits(sym);
if(InpOneTradeOnly && PositionsByMagic(sym, InpMagic) > 0)
return;
const int sh = InpEmaTrendBars;
double h1 = 0.0, h2 = 0.0;
if(!MacdHistAt(sym, InpTimeframe, InpMacdFast, InpMacdSlow, InpMacdSignal, 1, h1) ||
!MacdHistAt(sym, InpTimeframe, InpMacdFast, InpMacdSlow, InpMacdSignal, 2, h2))
return;
bool up = TrendUp(sym, sh);
bool dn = TrendDown(sym, sh);
bool wantBuy = false;
bool wantSell = false;
switch(InpEntryStyle)
{
case ENTRY_CCIZERO_MACD:
if(up && CciCrossAboveZero(sym) && h1 > 0.0)
wantBuy = true;
if(dn && CciCrossBelowZero(sym) && h1 < 0.0)
wantSell = true;
break;
case ENTRY_LAMBERT:
if(up && CciCrossAbove100(sym) && h1 > 0.0)
wantBuy = true;
if(dn && CciCrossBelowMinus100(sym) && h1 < 0.0)
wantSell = true;
break;
case ENTRY_PULLBACK:
if(up && HadCciOversoldRecently(sym) && CciCrossAboveZero(sym) && h1 > 0.0 && PullbackNearFastEmaLong(sym))
wantBuy = true;
if(dn && HadCciOverboughtRecently(sym) && CciCrossBelowZero(sym) && h1 < 0.0 && PullbackNearFastEmaShort(sym))
wantSell = true;
break;
}
MqlTick tick;
if(!SymbolInfoTick(sym, tick))
return;
double sl = 0.0, tp = 0.0;
if(wantBuy && !wantSell)
{
ComputeSLTP(true, tick.ask, sl, tp);
if(trade.Buy(InpLots, sym, tick.ask, sl, tp, "SuperEMA long"))
Log(StringFormat("BUY ask=%.5f sl=%.5f cci=%.2f macdHist=%.5f", tick.ask, sl,
CciAt(sym, InpTimeframe, InpCciPeriod, 1), h1));
}
else if(wantSell && !wantBuy)
{
ComputeSLTP(false, tick.bid, sl, tp);
if(trade.Sell(InpLots, sym, tick.bid, sl, tp, "SuperEMA short"))
Log(StringFormat("SELL bid=%.5f sl=%.5f cci=%.2f macdHist=%.5f", tick.bid, sl,
CciAt(sym, InpTimeframe, InpCciPeriod, 1), h1));
}
}
@@ -12,29 +12,18 @@
//+------------------------------------------------------------------+
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)
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
// 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;
ulong ticket = PositionGetTicket(i);
if(ticket == 0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) == symbol &&
(ulong)PositionGetInteger(POSITION_MAGIC) == magic_number)
return true;
}
return true;
return false;
}
//+------------------------------------------------------------------+
@@ -76,14 +65,13 @@ 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;
}
}
if(ticket == 0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) == symbol &&
(ulong)PositionGetInteger(POSITION_MAGIC) == magic_number)
return ticket;
}
return 0;
}
@@ -144,16 +132,42 @@ int CountPositionsByMagic(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)
{
count++;
}
}
if(ticket == 0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) == symbol &&
(ulong)PositionGetInteger(POSITION_MAGIC) == magic_number)
count++;
}
return count;
}
//+------------------------------------------------------------------+
//| Align volume to SYMBOL_VOLUME_STEP / min / max (avoids Invalid volume) |
//+------------------------------------------------------------------+
double United_NormalizeVolume(const string symbol, double volume)
{
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if(lotStep <= 0.0)
lotStep = 0.01;
double v = MathFloor(volume / lotStep) * lotStep;
if(v < minLot)
v = minLot;
if(v > maxLot)
v = maxLot;
int digits = (int)MathCeil(-MathLog10(lotStep));
if(digits < 0)
digits = 0;
if(digits > 8)
digits = 8;
return NormalizeDouble(v, digits);
}
//+------------------------------------------------------------------+
@@ -1,6 +1,18 @@
//+------------------------------------------------------------------+
//| DarvasBoxStrategy.mqh |
//+------------------------------------------------------------------+
// MQL5: no #if — use #ifdef only (no defined() / || in one #if)
#ifdef UNITED_V2_DYNAMIC_LOTS
extern double g_DB_LotSize;
#define DARVAS_TRADE_LOT (g_DB_LotSize)
#else
#ifdef CLUSTER0_ORCHESTRATOR
extern double g_DB_LotSize;
#define DARVAS_TRADE_LOT (g_DB_LotSize)
#else
#define DARVAS_TRADE_LOT 0.01
#endif
#endif
bool InitDarvasBox(string symbol)
{
@@ -24,7 +36,9 @@ bool InitDarvasBox(string symbol)
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);
// Same TF as box highs/lows (H1 loop in CalculateDarvasBox). PERIOD_CURRENT breaks when United EA
// runs on a chart timeframe other than H1 (volume/breakout no longer match the box).
dbData.volumeHandle = iVolumes(symbol, PERIOD_H1, VOLUME_TICK);
if(dbData.maHandle == INVALID_HANDLE || dbData.volumeHandle == INVALID_HANDLE)
{
@@ -133,6 +147,9 @@ bool ValidateStopLevels(double price, double &sl, double &tp, ENUM_ORDER_TYPE or
bool IsTrendFavorable(ENUM_ORDER_TYPE orderType)
{
if(!DB_UseTrendFilter)
return true;
double ma[];
ArraySetAsSeries(ma, true);
@@ -150,6 +167,9 @@ bool IsTrendFavorable(ENUM_ORDER_TYPE orderType)
bool CheckVolumeConditions()
{
if(!DB_UseVolumeSpikeFilter)
return true;
double volumes[];
ArraySetAsSeries(volumes, true);
@@ -162,8 +182,10 @@ bool CheckVolumeConditions()
volumeMA /= DB_VolumeMA_Period;
double currentVolume = volumes[0];
if(volumeMA <= 0.0)
return (currentVolume > 0.0);
double volumeRatio = currentVolume / volumeMA;
return (volumeRatio > DB_VolumeThresholdMultiplier);
}
@@ -191,13 +213,19 @@ bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price, double sl, double tp)
}
bool result = false;
const double lot = United_NormalizeVolume(dbData.symbol, DARVAS_TRADE_LOT);
if(lot <= 0.0)
{
Print("DarvasBox: Order rejected - invalid lot after normalize (raw=", DARVAS_TRADE_LOT, ")");
return 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(0.01, dbData.symbol, 0, sl, tp, "Darvas Box Breakout");
result = dbData.trade.Buy(lot, dbData.symbol, 0, sl, tp, "Darvas Box Breakout");
else
result = dbData.trade.Sell(0.01, dbData.symbol, 0, sl, tp, "Darvas Box Breakdown");
result = dbData.trade.Sell(lot, dbData.symbol, 0, sl, tp, "Darvas Box Breakdown");
// Always log errors, success only if logging enabled
if(result)
@@ -242,7 +270,7 @@ void ProcessDarvasBox(string symbol)
if(dbData.boxFormed)
{
double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK);
long currentVolume_long = iVolume(dbData.symbol, PERIOD_CURRENT, 0);
long currentVolume_long = iVolume(dbData.symbol, PERIOD_H1, 0);
double currentVolume = (double)currentVolume_long;
if(DB_EnableLogging)
@@ -14,6 +14,7 @@ bool InitEMASlopeDistance(string symbol)
esData.crossover_detected = false;
esData.trade_open_time = 0;
esData.last_bar_time = 0;
esData.es_last_sl_adjust_success_time = 0;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
@@ -48,6 +49,64 @@ void DeinitEMASlopeDistance()
IndicatorRelease(esData.ema_handle);
}
bool ES_IsWeeklyADXTrendFavorable(const ENUM_ORDER_TYPE order_type)
{
if(!ES_UseWeeklyADXFilter)
return true;
int adxShift = ES_WeeklyADXBarShift;
if(adxShift < 0)
adxShift = 0;
int adx_handle = iADX(esData.symbol, PERIOD_W1, ES_WeeklyADXPeriod);
if(adx_handle == INVALID_HANDLE)
return false;
double adx_buf[], plus_di_buf[], minus_di_buf[];
ArraySetAsSeries(adx_buf, true);
ArraySetAsSeries(plus_di_buf, true);
ArraySetAsSeries(minus_di_buf, true);
bool ok_adx = (CopyBuffer(adx_handle, 0, adxShift, 1, adx_buf) > 0);
bool ok_plus = (CopyBuffer(adx_handle, 1, adxShift, 1, plus_di_buf) > 0);
bool ok_minus = (CopyBuffer(adx_handle, 2, adxShift, 1, minus_di_buf) > 0);
IndicatorRelease(adx_handle);
if(!ok_adx || !ok_plus || !ok_minus)
return false;
double adx_value = adx_buf[0];
double plus_di = plus_di_buf[0];
double minus_di = minus_di_buf[0];
bool strength_ok = (adx_value >= ES_WeeklyADXMin);
bool direction_ok = true;
if(ES_WeeklyADXUseDirection)
{
if(order_type == ORDER_TYPE_BUY)
direction_ok = (plus_di > minus_di);
else
direction_ok = (minus_di > plus_di);
}
return strength_ok && direction_ok;
}
bool ES_TrailingActivationReached(const double position_profit, const ENUM_POSITION_TYPE position_type,
const double pips_multiplier)
{
if(ES_TrailingActivationPips <= 0.0)
return (position_profit > 0.0);
const double open_px = PositionGetDouble(POSITION_PRICE_OPEN);
if(position_type == POSITION_TYPE_BUY)
{
const double bid = SymbolInfoDouble(esData.symbol, SYMBOL_BID);
return ((bid - open_px) / SymbolInfoDouble(esData.symbol, SYMBOL_POINT) / pips_multiplier >= ES_TrailingActivationPips);
}
const double ask = SymbolInfoDouble(esData.symbol, SYMBOL_ASK);
return ((open_px - ask) / SymbolInfoDouble(esData.symbol, SYMBOL_POINT) / pips_multiplier >= ES_TrailingActivationPips);
}
//+------------------------------------------------------------------+
//| EMA Berechnung (EMA Calculation) |
//+------------------------------------------------------------------+
@@ -172,6 +231,11 @@ void PrüfeTrigger()
if(bullish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
if(!ES_IsWeeklyADXTrendFavorable(ORDER_TYPE_BUY))
{
Print("TRACE: Weekly ADX blockiert BUY-Entry");
return;
}
Print("TRACE: Versuche KAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_BUY))
{
@@ -180,6 +244,11 @@ void PrüfeTrigger()
}
else if(bearish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
if(!ES_IsWeeklyADXTrendFavorable(ORDER_TYPE_SELL))
{
Print("TRACE: Weekly ADX blockiert SELL-Entry");
return;
}
Print("TRACE: Versuche VERKAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_SELL))
{
@@ -199,17 +268,23 @@ void PrüfeTrigger()
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);
const double lot = United_NormalizeVolume(esData.symbol, g_ES_LotSize);
Print("TRACE: Lot (raw): ", g_ES_LotSize, " normalized: ", lot);
if(lot <= 0.0)
{
Print("TRACE: Abbruch — Lot nach Normalisierung ungültig");
return false;
}
bool success = false;
if(order_type == ORDER_TYPE_BUY)
{
success = esData.trade.Buy(g_ES_LotSize, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
success = esData.trade.Buy(lot, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
}
else
{
success = esData.trade.Sell(g_ES_LotSize, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
success = esData.trade.Sell(lot, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
}
if(success)
@@ -219,6 +294,7 @@ bool PlatziereTrade(ENUM_ORDER_TYPE order_type)
//--- Trade-Öffnungszeit speichern (Save trade opening time)
esData.trade_open_time = iTime(esData.symbol, ES_Timeframe, 0);
esData.es_last_sl_adjust_success_time = 0;
Print("TRACE: Trade-Öffnungszeit: ", TimeToString(esData.trade_open_time));
//--- Überwachung zurücksetzen (Reset monitoring)
@@ -244,41 +320,51 @@ void VerwalteTrades()
{
if(!PositionSelectByMagic(esData.symbol, (ulong)ES_MagicNumber))
return;
if(ES_UseStaleStopLossExit && ES_StaleStopLossSeconds > 0)
{
const datetime stale_ref = (esData.es_last_sl_adjust_success_time > 0)
? esData.es_last_sl_adjust_success_time
: (datetime)PositionGetInteger(POSITION_TIME);
if(TimeCurrent() - stale_ref >= ES_StaleStopLossSeconds)
{
SchließePosition("Stale stop loss - keine SL-Anpassung");
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
const double trail_dist = ES_TrailingStop * point * pips_multiplier;
const long stops_level = SymbolInfoInteger(esData.symbol, SYMBOL_TRADE_STOPS_LEVEL);
const double min_dist = (double)stops_level * point;
if(ES_UseTrailingStop && ES_TrailingStop > 0.0 && ES_TrailingActivationReached(position_profit, position_type, pips_multiplier))
{
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)
{
const double bid = SymbolInfoDouble(esData.symbol, SYMBOL_BID);
double new_stop_loss = NormalizeDouble(bid - trail_dist, digits);
if(min_dist > 0.0 && bid - new_stop_loss < min_dist)
new_stop_loss = NormalizeDouble(bid - min_dist, digits);
const double current_stop_loss = PositionGetDouble(POSITION_SL);
if(new_stop_loss < bid && new_stop_loss > 0.0 && 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)
{
const double ask = SymbolInfoDouble(esData.symbol, SYMBOL_ASK);
double new_stop_loss = NormalizeDouble(ask + trail_dist, digits);
if(min_dist > 0.0 && new_stop_loss - ask < min_dist)
new_stop_loss = NormalizeDouble(ask + min_dist, digits);
const double current_stop_loss = PositionGetDouble(POSITION_SL);
if(new_stop_loss > ask && new_stop_loss > 0.0 &&
(new_stop_loss < current_stop_loss || current_stop_loss == 0.0))
ÄndereStopLoss(new_stop_loss);
}
}
}
@@ -366,6 +452,7 @@ void ÄndereStopLoss(double new_stop_loss)
if(success)
{
esData.es_last_sl_adjust_success_time = TimeCurrent();
Print("TRACE: Stop Loss erfolgreich geändert auf: ", new_stop_loss);
}
else
@@ -386,6 +473,7 @@ void SchließePosition(string reason = "Unbekannt")
if(success)
{
esData.es_last_sl_adjust_success_time = 0;
Print("TRACE: Position erfolgreich geschlossen - Grund: ", reason);
}
else
@@ -400,40 +488,34 @@ void SchließePosition(string reason = "Unbekannt")
//+------------------------------------------------------------------+
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.symbol = symbol;
const datetime current_bar_time = iTime(esData.symbol, ES_Timeframe, 0);
const bool new_bar = (current_bar_time != esData.last_bar_time);
const bool has_position = PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber);
if(ES_UseBarData && !new_bar && !has_position)
return;
if(new_bar)
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)
const bool run_signals = (!ES_UseBarData || new_bar);
if(run_signals && 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) ===");
@@ -443,7 +525,7 @@ void ProcessEMASlopeDistance(string symbol)
{
Print("=== DEBUG INFO (Tick) ===");
}
Print("Aktueller Close: ", aktueller_close);
Print("EMA: ", ema_aktuell);
Print("Preis-Abstand: ", preis_abstand, " Pips");
@@ -455,41 +537,39 @@ void ProcessEMASlopeDistance(string symbol)
Print("Trades im aktuellen Crossover: ", esData.trades_in_current_crossover, "/", ES_MaxTradesPerCrossover);
Print("==================");
}
//--- Überwachung prüfen (Check monitoring)
if(esData.überwachung_aktiv)
if(run_signals)
{
if(ES_UseBarData)
if(esData.überwachung_aktiv)
{
// 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)
if(ES_UseBarData)
{
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");
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
{
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");
}
}
}
PrüfeTrigger();
}
//--- Trigger-Bedingungen prüfen (Check trigger conditions)
PrüfeTrigger();
//--- Trade Management (Trade management)
VerwalteTrades();
}
@@ -79,6 +79,7 @@ bool InitRSICrossOverReversal(string symbol)
}
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
rcData.trade.SetDeviationInPoints(RC_slippage);
rcData.isInitialized = true;
Print("RSICrossOverReversal: Successfully initialized for symbol '", symbol, "'");
return true;
@@ -190,7 +191,13 @@ void ProcessRSICrossOverReversal(string symbol)
double emaSlope = (currentEMA - previousEMA) * 100;
const double closeCurr = iClose(rcData.symbol, RC_TimeFrame1, 0);
double priceToEmaDistance = (closeCurr - currentEMA) * 10;
// Raw (close-EMA)*10 blows past threshold on XAUUSD (~2600) almost every bar — blocks all entries.
// Compare distance in pips so RC_emaDistanceThreshold matches intent across symbols.
const double point = SymbolInfoDouble(rcData.symbol, SYMBOL_POINT);
const int symDig = (int)SymbolInfoInteger(rcData.symbol, SYMBOL_DIGITS);
const double pipMult = (symDig == 3 || symDig == 5) ? 10.0 : 1.0;
const double pipSize = (point > 0.0 ? point * pipMult : point);
const double priceToEmaPips = (pipSize > 0.0 ? MathAbs(closeCurr - currentEMA) / pipSize : 0.0);
bool isBuyPosition = false;
bool isSellPosition = false;
@@ -209,7 +216,8 @@ void ProcessRSICrossOverReversal(string symbol)
ApplyTrailingStop();
bool cooldownPassed = (currentTime - rcData.lastTradeTime) >= RC_cooldownSeconds;
bool isTrendStrong = MathAbs(emaSlope) > RC_emaSlopeThreshold || MathAbs(priceToEmaDistance) > RC_emaDistanceThreshold;
const bool isTrendStrong = RC_UseTrendStrengthFilter &&
(MathAbs(emaSlope) > RC_emaSlopeThreshold || priceToEmaPips > RC_emaDistanceThreshold);
if(isBuyPosition && currentRSI > RC_exitBuyRSI)
{
@@ -233,10 +241,12 @@ void ProcessRSICrossOverReversal(string symbol)
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"))
const double vol = United_NormalizeVolume(rcData.symbol, g_RC_LotSize);
if(vol > 0.0)
{
rcData.lastTradeTime = currentTime;
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
if(rcData.trade.Sell(vol, rcData.symbol, 0.0, 0.0, 0.0, "Sell Order"))
rcData.lastTradeTime = currentTime;
}
}
@@ -244,10 +254,12 @@ void ProcessRSICrossOverReversal(string symbol)
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"))
const double vol = United_NormalizeVolume(rcData.symbol, g_RC_LotSize);
if(vol > 0.0)
{
rcData.lastTradeTime = currentTime;
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
if(rcData.trade.Buy(vol, rcData.symbol, 0.0, 0.0, 0.0, "Buy Order"))
rcData.lastTradeTime = currentTime;
}
}
@@ -2,6 +2,11 @@
//| RSIMidPointHijackStrategy.mqh |
//+------------------------------------------------------------------+
double RM_NormalizedLot(const string sym)
{
return United_NormalizeVolume(sym, g_RM_LotSize);
}
bool IsNewBar(string symbol)
{
datetime time[];
@@ -111,7 +116,9 @@ void CheckRSIFollowStrategy(string symbol)
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Sell(vol, symbol, 0, 0, 0, "RSI Follow");
}
rmData.rsiOverbought = false;
}
@@ -120,7 +127,9 @@ void CheckRSIFollowStrategy(string symbol)
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Buy(vol, symbol, 0, 0, 0, "RSI Follow");
}
rmData.rsiOversold = false;
}
@@ -154,7 +163,9 @@ void CheckRSIReverseStrategy(string symbol)
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Sell(vol, symbol, 0, 0, 0, "RSI Reverse");
}
rmData.rsiReverseOverbought = false;
}
@@ -163,7 +174,9 @@ void CheckRSIReverseStrategy(string symbol)
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Buy(vol, symbol, 0, 0, 0, "RSI Reverse");
}
rmData.rsiReverseOversold = false;
}
@@ -223,7 +236,9 @@ void CheckEMACrossStrategy(string symbol)
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Buy(vol, symbol, 0, 0, 0, "EMA Cross Distance");
rmData.emaCrossBuySignal = false;
}
}
@@ -252,7 +267,9 @@ void CheckEMACrossStrategy(string symbol)
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Sell(vol, symbol, 0, 0, 0, "EMA Cross Distance");
rmData.emaCrossSellSignal = false;
}
}
@@ -265,7 +282,9 @@ void CheckEMACrossStrategy(string symbol)
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Buy(vol, symbol, 0, 0, 0, "EMA Cross");
}
}
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
@@ -273,7 +292,9 @@ void CheckEMACrossStrategy(string symbol)
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Sell(vol, symbol, 0, 0, 0, "EMA Cross");
}
}
}
@@ -65,19 +65,14 @@ bool IsAsianSession()
//+------------------------------------------------------------------+
bool IsTradingAllowed(RSIReversalAsianData& data)
{
// Check if market is open
long tradeMode = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_MODE);
if(tradeMode != SYMBOL_TRADE_MODE_FULL)
{
// Do not require SYMBOL_TRADE_MODE_FULL: many symbols allow one side only (long/short).
const long tradeMode = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_MODE);
if(tradeMode == SYMBOL_TRADE_MODE_DISABLED || tradeMode == SYMBOL_TRADE_MODE_CLOSEONLY)
return false;
}
// Check if we have enough money
if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0)
{
return false;
}
return true;
}
@@ -290,7 +285,7 @@ bool InitRSIReversalAsian(RSIReversalAsianData& data, string symbol,
// Set trade parameters
data.trade.SetExpertMagicNumber(MagicNumber);
data.trade.SetDeviationInPoints(Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
data.trade.SetTypeFillingBySymbol(symbol);
// Initialize state
data.isPositionOpen = false;
@@ -444,16 +439,16 @@ void ProcessRSIReversalAsian(RSIReversalAsianData& data, double lotSize)
if(data.UseTakeProfit && tp <= currentBid)
return;
// Set trade parameters
data.trade.SetDeviationInPoints(data.Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
data.trade.SetTypeFillingBySymbol(data.symbol);
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"))
const double vol = United_NormalizeVolume(data.symbol, tradeLotSize);
if(vol <= 0.0)
return;
if(data.trade.Buy(vol, data.symbol, currentAsk, sl, tp, "RSI Oversold Crossover Buy"))
{
data.isPositionOpen = true;
data.positionOpenPrice = currentAsk;
@@ -472,16 +467,16 @@ void ProcessRSIReversalAsian(RSIReversalAsianData& data, double lotSize)
if(data.UseTakeProfit && tp >= currentAsk)
return;
// Set trade parameters
data.trade.SetDeviationInPoints(data.Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
data.trade.SetTypeFillingBySymbol(data.symbol);
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"))
const double vol = United_NormalizeVolume(data.symbol, tradeLotSize);
if(vol <= 0.0)
return;
if(data.trade.Sell(vol, data.symbol, currentBid, sl, tp, "RSI Overbought Crossover Sell"))
{
data.isPositionOpen = true;
data.positionOpenPrice = currentBid;
@@ -120,6 +120,70 @@ void RS_TryReversalEscape(RSIScalpingData& data, const ENUM_TIMEFRAMES tf, const
" ATR=", DoubleToString(atr, (int)SymbolInfoInteger(data.symbol, SYMBOL_DIGITS)));
}
void RS_ApplyTrailingStop(RSIScalpingData& data, const int MagicNumber,
const bool useTrailingStop,
const double trailingStopDistancePoints,
const double trailingActivationPoints)
{
if(!useTrailingStop || trailingStopDistancePoints <= 0.0)
return;
if(!PositionSelectByMagic(data.symbol, (ulong)MagicNumber))
return;
const double point = SymbolInfoDouble(data.symbol, SYMBOL_POINT);
if(point <= 0.0)
return;
const int digits = (int)SymbolInfoInteger(data.symbol, SYMBOL_DIGITS);
const double trail_dist = trailingStopDistancePoints * point;
const double activation_pts = (trailingActivationPoints > 0.0)
? trailingActivationPoints
: trailingStopDistancePoints;
const double activation = activation_pts * point;
const long stops_level = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_STOPS_LEVEL);
const double min_dist = (double)stops_level * point;
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double cur_sl = PositionGetDouble(POSITION_SL);
const double cur_tp = PositionGetDouble(POSITION_TP);
if(ptype == POSITION_TYPE_BUY)
{
const double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
if(bid - entry <= activation)
return;
double new_sl = NormalizeDouble(bid - trail_dist, digits);
if(min_dist > 0.0 && bid - new_sl < min_dist)
new_sl = NormalizeDouble(bid - min_dist, digits);
if(new_sl >= bid || new_sl <= 0.0)
return;
if(cur_sl > 0.0 && new_sl <= cur_sl)
return;
ModifyPositionByMagic(data.trade, data.symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
else if(ptype == POSITION_TYPE_SELL)
{
const double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
if(entry - ask <= activation)
return;
double new_sl = NormalizeDouble(ask + trail_dist, digits);
if(min_dist > 0.0 && new_sl - ask < min_dist)
new_sl = NormalizeDouble(ask + min_dist, digits);
if(new_sl <= ask || new_sl <= 0.0)
return;
if(cur_sl > 0.0 && new_sl >= cur_sl)
return;
ModifyPositionByMagic(data.trade, data.symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
}
string ErrorDescription(int errorCode)
{
switch(errorCode)
@@ -364,21 +428,7 @@ void CheckEntrySignals(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int Mag
//+------------------------------------------------------------------+
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;
return United_NormalizeVolume(symbol, lotSize);
}
void OpenBuyPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
@@ -520,7 +570,8 @@ void ProcessRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES Ti
double RSI_Oversold, double RSI_Target_Buy, double RSI_Target_Sell,
int BarsToWait, double LotSize, int MagicNumber,
bool UseReversalEscape, int ReversalATRPeriod, double ReversalAdverseAtrMult,
int ReversalSignsRequired, double ReversalRsiVelocity, double ReversalBodyAtrMult)
int ReversalSignsRequired, double ReversalRsiVelocity, double ReversalBodyAtrMult,
bool UseTrailingStop, double TrailingStopDistancePoints, double TrailingActivationPoints)
{
// Skip if not initialized (symbol not available)
if(!data.isInitialized)
@@ -543,6 +594,10 @@ void ProcessRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES Ti
RS_TryReversalEscape(data, TimeFrame, MagicNumber, ReversalATRPeriod, ReversalAdverseAtrMult,
ReversalSignsRequired, ReversalRsiVelocity, ReversalBodyAtrMult);
if(in_pos)
RS_ApplyTrailingStop(data, MagicNumber, UseTrailingStop,
TrailingStopDistancePoints, TrailingActivationPoints);
if(!new_bar)
return;
@@ -0,0 +1,336 @@
//+------------------------------------------------------------------+
//| RSISecretSauceStrategy.mqh |
//| Cluster-0 orchestrator: RSI leave extreme then peak/bottom entry |
//+------------------------------------------------------------------+
#ifndef RSI_SECRET_SAUCE_STRATEGY_MQH
#define RSI_SECRET_SAUCE_STRATEGY_MQH
#include <Trade/Trade.mqh>
#include <Trade/PositionInfo.mqh>
struct RSISecretSauceOrcData
{
string actualSymbol;
bool isInitialized;
CTrade trade;
CPositionInfo positionInfo;
int rsiHandle;
int atrHandle;
double rsiBuffer[];
double atrBuffer[];
double highBuffer[];
double lowBuffer[];
bool rsiWasOverbought;
bool rsiWasOversold;
bool rsiBackInRange;
datetime lastRSIExitTime;
datetime lastRSIReentryTime;
datetime lastTradeTime;
datetime lastBarTime;
};
bool RSS_UpdateIndicators(RSISecretSauceOrcData &d)
{
int rsiBarsNeeded = RSS_RSILookback + 5;
if(CopyBuffer(d.rsiHandle, 0, 0, rsiBarsNeeded, d.rsiBuffer) < rsiBarsNeeded)
return false;
if(CopyBuffer(d.atrHandle, 0, 0, 2, d.atrBuffer) < 2)
return false;
if(CopyHigh(d.actualSymbol, RSS_Timeframe, 0, RSS_SwingLookback + 5, d.highBuffer) < RSS_SwingLookback + 5)
return false;
if(CopyLow(d.actualSymbol, RSS_Timeframe, 0, RSS_SwingLookback + 5, d.lowBuffer) < RSS_SwingLookback + 5)
return false;
return true;
}
void RSS_UpdateRSIState(RSISecretSauceOrcData &d)
{
double rsiCurrent = d.rsiBuffer[0];
double rsiPrev = d.rsiBuffer[1];
if(rsiPrev >= RSS_RSIOverbought && rsiCurrent < RSS_RSIOverbought)
{
d.rsiWasOverbought = true;
d.rsiBackInRange = true;
d.lastRSIExitTime = TimeCurrent();
d.lastRSIReentryTime = TimeCurrent();
}
if(rsiPrev <= RSS_RSIOversold && rsiCurrent > RSS_RSIOversold)
{
d.rsiWasOversold = true;
d.rsiBackInRange = true;
d.lastRSIExitTime = TimeCurrent();
d.lastRSIReentryTime = TimeCurrent();
}
if(rsiCurrent >= RSS_RSIOverbought)
{
d.rsiWasOverbought = false;
d.rsiBackInRange = false;
}
if(rsiCurrent <= RSS_RSIOversold)
{
d.rsiWasOversold = false;
d.rsiBackInRange = false;
}
}
bool RSS_IsRSIPeak(RSISecretSauceOrcData &d)
{
if(ArraySize(d.rsiBuffer) < RSS_PeakBars + 2)
return false;
double currentRSI = d.rsiBuffer[0];
bool isPeak = true;
for(int i = 1; i <= RSS_PeakBars; i++)
{
if(d.rsiBuffer[i] >= currentRSI)
{
isPeak = false;
break;
}
}
if(d.rsiBuffer[1] >= currentRSI)
isPeak = false;
return isPeak;
}
bool RSS_IsRSIBottom(RSISecretSauceOrcData &d)
{
if(ArraySize(d.rsiBuffer) < RSS_PeakBars + 2)
return false;
double currentRSI = d.rsiBuffer[0];
bool isBottom = true;
for(int i = 1; i <= RSS_PeakBars; i++)
{
if(d.rsiBuffer[i] <= currentRSI)
{
isBottom = false;
break;
}
}
if(d.rsiBuffer[1] <= currentRSI)
isBottom = false;
return isBottom;
}
double RSS_GetSwingStopLoss(RSISecretSauceOrcData &d, double currentPrice, ENUM_POSITION_TYPE type)
{
if(type == POSITION_TYPE_BUY)
{
double lowestLow = d.lowBuffer[0];
for(int i = 1; i < RSS_SwingLookback && i < ArraySize(d.lowBuffer); i++)
{
if(d.lowBuffer[i] < lowestLow)
lowestLow = d.lowBuffer[i];
}
return lowestLow;
}
double highestHigh = d.highBuffer[0];
for(int i = 1; i < RSS_SwingLookback && i < ArraySize(d.highBuffer); i++)
{
if(d.highBuffer[i] > highestHigh)
highestHigh = d.highBuffer[i];
}
return highestHigh;
}
bool RSS_CalculateStops(RSISecretSauceOrcData &d, double price, ENUM_POSITION_TYPE type, double &sl, double &tp)
{
double atrValue = d.atrBuffer[0];
if(atrValue <= 0)
atrValue = price * 0.01;
double slDistance = atrValue * RSS_StopLossATR;
double tpDistance = atrValue * RSS_TakeProfitATR;
int digits = (int)SymbolInfoInteger(d.actualSymbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(d.actualSymbol, SYMBOL_POINT);
int stopsLevel = (int)SymbolInfoInteger(d.actualSymbol, SYMBOL_TRADE_STOPS_LEVEL);
double minStopDistance = MathMax(stopsLevel * point, point * 10);
if(RSS_UseSwingStopLoss)
{
double swingStop = RSS_GetSwingStopLoss(d, 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;
}
}
}
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;
}
bool RSS_CanOpenNewPosition(RSISecretSauceOrcData &d)
{
int positionCount = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(d.positionInfo.SelectByIndex(i))
{
if(d.positionInfo.Symbol() == d.actualSymbol && d.positionInfo.Magic() == RSS_MagicNumber)
positionCount++;
}
}
if(positionCount >= RSS_MaxPositions)
return false;
if(d.lastTradeTime > 0)
{
int barsSince = Bars(d.actualSymbol, RSS_Timeframe, d.lastTradeTime, TimeCurrent());
if(barsSince < RSS_MinBarsBetweenTrades)
return false;
}
return true;
}
void RSS_OpenPosition(RSISecretSauceOrcData &d, ENUM_POSITION_TYPE type, const double lotSize)
{
double price = (type == POSITION_TYPE_BUY) ?
SymbolInfoDouble(d.actualSymbol, SYMBOL_ASK) :
SymbolInfoDouble(d.actualSymbol, SYMBOL_BID);
if(price <= 0)
return;
double sl = 0.0, tp = 0.0;
if(!RSS_CalculateStops(d, price, type, sl, tp))
return;
string comment = "RSI_Secret_" + (type == POSITION_TYPE_BUY ? "LONG" : "SHORT");
bool result = false;
if(type == POSITION_TYPE_BUY)
result = d.trade.Buy(lotSize, d.actualSymbol, 0, sl, tp, comment);
else
result = d.trade.Sell(lotSize, d.actualSymbol, 0, sl, tp, comment);
if(result)
{
d.lastTradeTime = TimeCurrent();
if(type == POSITION_TYPE_BUY)
d.rsiWasOverbought = false;
else
d.rsiWasOversold = false;
d.rsiBackInRange = false;
}
}
void RSS_CheckEntrySignals(RSISecretSauceOrcData &d, const double lotSize)
{
if(d.rsiWasOverbought && d.rsiBackInRange)
{
if(d.rsiBuffer[0] < RSS_RSIOverbought && RSS_IsRSIPeak(d))
RSS_OpenPosition(d, POSITION_TYPE_BUY, lotSize);
}
if(d.rsiWasOversold && d.rsiBackInRange)
{
if(d.rsiBuffer[0] > RSS_RSIOversold && RSS_IsRSIBottom(d))
RSS_OpenPosition(d, POSITION_TYPE_SELL, lotSize);
}
}
bool InitRSISecretSauce(RSISecretSauceOrcData &d, const string symbol)
{
d.isInitialized = false;
d.rsiHandle = INVALID_HANDLE;
d.atrHandle = INVALID_HANDLE;
d.rsiWasOverbought = false;
d.rsiWasOversold = false;
d.rsiBackInRange = false;
d.lastRSIExitTime = 0;
d.lastRSIReentryTime = 0;
d.lastTradeTime = 0;
d.lastBarTime = 0;
d.actualSymbol = symbol;
StringTrimLeft(d.actualSymbol);
StringTrimRight(d.actualSymbol);
if(StringLen(d.actualSymbol) == 0)
d.actualSymbol = _Symbol;
if(!SymbolSelect(d.actualSymbol, true))
{
Print("RSISecretSauce: symbol not available '", d.actualSymbol, "'");
return false;
}
d.rsiHandle = iRSI(d.actualSymbol, RSS_Timeframe, RSS_RSIPeriod, PRICE_CLOSE);
d.atrHandle = iATR(d.actualSymbol, RSS_Timeframe, RSS_ATRPeriod);
if(d.rsiHandle == INVALID_HANDLE || d.atrHandle == INVALID_HANDLE)
return false;
ArraySetAsSeries(d.rsiBuffer, true);
ArraySetAsSeries(d.atrBuffer, true);
ArraySetAsSeries(d.highBuffer, true);
ArraySetAsSeries(d.lowBuffer, true);
d.trade.SetExpertMagicNumber(RSS_MagicNumber);
d.trade.SetDeviationInPoints(RSS_Slippage);
d.trade.SetTypeFilling(ORDER_FILLING_FOK);
d.isInitialized = true;
return true;
}
void DeinitRSISecretSauce(RSISecretSauceOrcData &d)
{
if(d.rsiHandle != INVALID_HANDLE)
IndicatorRelease(d.rsiHandle);
if(d.atrHandle != INVALID_HANDLE)
IndicatorRelease(d.atrHandle);
d.rsiHandle = INVALID_HANDLE;
d.atrHandle = INVALID_HANDLE;
d.isInitialized = false;
}
void ProcessRSISecretSauce(RSISecretSauceOrcData &d, const double lotSize)
{
if(!d.isInitialized)
return;
int requiredBars = MathMax(RSS_RSILookback, RSS_SwingLookback) + 10;
if(Bars(d.actualSymbol, RSS_Timeframe) < requiredBars)
return;
datetime currentBarTime = iTime(d.actualSymbol, RSS_Timeframe, 0);
if(currentBarTime == d.lastBarTime)
return;
d.lastBarTime = currentBarTime;
if(!RSS_UpdateIndicators(d))
return;
RSS_UpdateRSIState(d);
if(RSS_CanOpenNewPosition(d))
RSS_CheckEntrySignals(d, lotSize);
}
#endif
@@ -51,6 +51,12 @@ void SuperEMA_Log(SuperEMAData &d, const string s)
Print("[SuperEMA] ", s);
}
double SuperEMA_Point(const SuperEMAData &d)
{
double pt = SymbolInfoDouble(d.symbol, SYMBOL_POINT);
return (pt > 0.0 ? pt : _Point);
}
bool SuperEMA_IsNewBar(SuperEMAData &d)
{
datetime t = iTime(d.symbol, d.tf, 0);
@@ -176,7 +182,8 @@ bool SuperEMA_PullbackNearFastEmaLong(SuperEMAData &d)
double lo = iLow(d.symbol, d.tf, 1);
if(emaF <= 0.0)
return false;
return (lo <= emaF + d.slBufferPoints * _Point * 3.0);
const double pt = SuperEMA_Point(d);
return (lo <= emaF + d.slBufferPoints * pt * 3.0);
}
bool SuperEMA_PullbackNearFastEmaShort(SuperEMAData &d)
@@ -185,7 +192,8 @@ bool SuperEMA_PullbackNearFastEmaShort(SuperEMAData &d)
double hi = iHigh(d.symbol, d.tf, 1);
if(emaF <= 0.0)
return false;
return (hi >= emaF - d.slBufferPoints * _Point * 3.0);
const double pt = SuperEMA_Point(d);
return (hi >= emaF - d.slBufferPoints * pt * 3.0);
}
int SuperEMA_PositionsByMagic(SuperEMAData &d)
@@ -196,6 +204,8 @@ int SuperEMA_PositionsByMagic(SuperEMAData &d)
ulong t = PositionGetTicket(i);
if(t == 0)
continue;
if(!PositionSelectByTicket(t))
continue;
if(PositionGetString(POSITION_SYMBOL) == d.symbol && (int)PositionGetInteger(POSITION_MAGIC) == d.magic)
n++;
}
@@ -209,7 +219,7 @@ void SuperEMA_ComputeSLTP(SuperEMAData &d, const bool isBuy, double &sl, double
if(!d.useStructuralSL)
return;
double emaM = SuperEMA_EmaAt(d, d.emaMid, d.emaTrendBars);
double buf = d.slBufferPoints * _Point;
double buf = d.slBufferPoints * SuperEMA_Point(d);
if(isBuy)
sl = emaM - buf;
else
@@ -418,9 +428,13 @@ void ProcessSuperEMA(SuperEMAData &d, const double lots)
SuperEMA_ManageExits(d);
// Same order as standalone SuperEMAXAUUSD: skip entry logic when flat is not allowed.
if(d.oneTradeOnly && SuperEMA_PositionsByMagic(d) > 0)
return;
const int sh = d.emaTrendBars;
double h1 = 0.0, h2 = 0.0;
if(!SuperEMA_MacdHistAt(d, 1, h1) || !SuperEMA_MacdHistAt(d, 2, h2))
double h1 = 0.0;
if(!SuperEMA_MacdHistAt(d, 1, h1))
return;
bool up = SuperEMA_TrendUp(d, sh);
@@ -453,14 +467,14 @@ void ProcessSuperEMA(SuperEMAData &d, const double lots)
break;
}
if(d.oneTradeOnly && SuperEMA_PositionsByMagic(d) > 0)
if(!wantBuy && !wantSell)
return;
const double vol = United_NormalizeVolume(d.symbol, lots);
if(vol <= 0.0)
{
if(wantBuy && !United_MayOpenNewEntry(d.symbol, (ulong)d.magic, true))
wantBuy = false;
if(wantSell && !United_MayOpenNewEntry(d.symbol, (ulong)d.magic, false))
wantSell = false;
if(!wantBuy && !wantSell)
return;
SuperEMA_Log(d, "Skip entry: normalized volume <= 0");
return;
}
MqlTick tick;
@@ -474,7 +488,7 @@ void ProcessSuperEMA(SuperEMAData &d, const double lots)
#ifndef UNITED_MARTINGALE_NO_SELF_CLOSE
SuperEMA_ComputeSLTP(d, true, sl, tp);
#endif
if(d.trade.Buy(lots, d.symbol, tick.ask, sl, tp, "United SuperEMA long"))
if(d.trade.Buy(vol, d.symbol, tick.ask, sl, tp, "United SuperEMA long"))
SuperEMA_Log(d, StringFormat("BUY ask=%.5f sl=%.5f", tick.ask, sl));
}
else if(wantSell && !wantBuy)
@@ -482,7 +496,7 @@ void ProcessSuperEMA(SuperEMAData &d, const double lots)
#ifndef UNITED_MARTINGALE_NO_SELF_CLOSE
SuperEMA_ComputeSLTP(d, false, sl, tp);
#endif
if(d.trade.Sell(lots, d.symbol, tick.bid, sl, tp, "United SuperEMA short"))
if(d.trade.Sell(vol, d.symbol, tick.bid, sl, tp, "United SuperEMA short"))
SuperEMA_Log(d, StringFormat("SELL bid=%.5f sl=%.5f", tick.bid, sl));
}
}
@@ -5,14 +5,17 @@
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#property version "1.21"
#property strict
#property description "LOT_* nominal at ORCH_ReferenceBalance; scale = balance/equity ÷ reference (clamped). No performance-evaluator ranking."
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Indicators\Trend.mqh>
#include <Indicators\Volumes.mqh>
#include "MagicNumberHelpers.mqh"
#define UNITED_V2_DYNAMIC_LOTS
double g_DB_LotSize;
// Include strategy implementations early so structs are available
#include "Strategies/DarvasBoxStrategy.mqh"
#include "Strategies/EMASlopeDistanceStrategy.mqh"
@@ -22,6 +25,8 @@
#include "Strategies/SuperEMAStrategy.mqh"
#include "Strategies/RSIReversalAsianStrategy.mqh"
#include "Strategies/RSIConsolidationStrategy.mqh"
#include "Strategies/SimpleTrendlineStrategy.mqh"
#include "Strategies/RSISecretSauceStrategy.mqh"
//+------------------------------------------------------------------+
//| Global Lot Size Variables (for dynamic lot sizing) |
@@ -29,10 +34,20 @@
double g_ES_LotSize; // EMA Slope Distance lot size
double g_RC_LotSize; // RSI CrossOver Reversal lot size
double g_RM_LotSize; // RSI MidPoint Hijack lot size
double g_LotScaleGrowth = 1.0;
double g_LotScaleMargin = 1.0;
double g_LotScaleFinal = 1.0;
datetime g_LastScaleLogTime = 0;
double g_Pos_RS_APPL;
double g_Pos_RS_BTCUSD;
double g_Pos_RS_NVDA;
double g_Pos_RS_TSLA;
double g_Pos_RS_XAUUSD;
double g_Pos_RRA_EURUSD;
double g_Pos_RRA_AUDUSD;
double g_Pos_SE;
double g_Pos_RCO;
double g_Pos_ST_BTCUSD;
double g_Pos_ST_XAUUSD;
double g_Pos_ST_GER40;
double g_RSS_LotSize;
bool United_MayOpenNewEntry(const string symbol, const ulong magic, const bool isBuy)
{
@@ -58,139 +73,36 @@ input bool EnableSuperEMA = true;
input bool EnableRSIConsolidation = true;
input bool EnableRSIReversalAsianEURUSD = true;
input bool EnableRSIReversalAsianAUDUSD = true;
input bool EnableSimpleTrendlineBTCUSD = true;
input bool EnableSimpleTrendlineXAUUSD = true;
input bool EnableSimpleTrendlineGER40 = true;
input bool EnableRSISecretSauce = true;
input group "=== Centralized Lot Size (Granular Per Robot) ==="
input double LOT_DB_DarvasBox = 0.05;
input double LOT_ES_EMASlopeDistance = 0.05;
input double LOT_RC_RSICrossOver = 0.1;
input double LOT_RM_RSIMidPointHijack = 0.01;
input double LOT_RS_APPL = 100.0;
input double LOT_RS_BTCUSD = 0.15;
input double LOT_RS_NVDA = 60.0;
input double LOT_RS_TSLA = 20.0;
input double LOT_RS_XAUUSD = 0.02;
input double LOT_RRA_EURUSD = 0.01;
input double LOT_RRA_AUDUSD = 0.10;
input double LOT_SE_SuperEMA = 0.01;
input double LOT_RCO_RSIConsolidation = 0.04;
input double LOT_RC_RSICrossOver = 0.06;
input double LOT_RM_RSIMidPointHijack = 0.03;
input double LOT_RS_APPL = 5.0;
input double LOT_RS_BTCUSD = 0.1;
input double LOT_RS_NVDA = 10.0;
input double LOT_RS_TSLA = 15.0;
input double LOT_RS_XAUUSD = 0.1;
input double LOT_RRA_EURUSD = 0.05;
input double LOT_RRA_AUDUSD = 0.08;
input double LOT_SE_SuperEMA = 0.02;
input double LOT_RCO_RSIConsolidation = 0.02;
input double LOT_ST_BTCUSD = 0.07;
input double LOT_ST_XAUUSD = 0.01;
input double LOT_ST_GER40 = 0.10;
input double LOT_RSS_SecretSauce = 0.01;
input group "=== Auto Lot Scaling (Equity/Balance + Margin Guard) ==="
input bool EnableAutoLotScaling = true;
input double ScalingReferenceBalanceUSD = 1000.0;
input double ScalingCurveExponent = 0.70; // 1.0=linear, <1 smoother, >1 aggressive
input double ScaleMinMultiplier = 0.50;
input double ScaleMaxMultiplier = 3.00;
input bool EnableMarginGuard = true;
input double MarginSafeLevelPercent = 420.0; // >= safe: no reduction
input double MarginCriticalLevelPercent = 170.0;
input double MarginGuardMinMultiplier = 0.20;
input int ScaleLogIntervalSeconds = 300;
input bool ShowScaleStatusOnChart = true;
input group "=== Minimum Balance Recommendation ==="
input bool PrintMinimumBalanceRecommendation = true;
input double MinBalPerLot_ES = 1200.0;
input double MinBalPerLot_RC = 900.0;
input double MinBalPerLot_RM = 900.0;
input double MinBalPerLot_RS_APPL = 8.0;
input double MinBalPerLot_RS_BTCUSD = 2500.0;
input double MinBalPerLot_RS_NVDA = 8.0;
input double MinBalPerLot_RS_TSLA = 8.0;
input double MinBalPerLot_RS_XAUUSD = 3000.0;
input double MinBalPerLot_RRA_EURUSD = 1200.0;
input double MinBalPerLot_RRA_AUDUSD = 1200.0;
input double MinBalPerLot_SE = 1500.0;
input double MinBalPerLot_RCO = 1800.0;
input double MinBalanceSafetyBufferPercent = 25.0;
input double MinAbsoluteRecommendedBalance = 300.0;
double ClampValue(const double v, const double minV, const double maxV)
{
if(v < minV) return minV;
if(v > maxV) return maxV;
return v;
}
double GetAutoScaledLot(const double baseLot)
{
const double scaled = baseLot * g_LotScaleFinal;
if(scaled <= 0.0)
return 0.0;
return scaled;
}
double ComputeRecommendedMinBalance()
{
double required = 0.0;
required += LOT_ES_EMASlopeDistance * MinBalPerLot_ES;
required += LOT_RC_RSICrossOver * MinBalPerLot_RC;
required += LOT_RM_RSIMidPointHijack * MinBalPerLot_RM;
required += LOT_RS_APPL * MinBalPerLot_RS_APPL;
required += LOT_RS_BTCUSD * MinBalPerLot_RS_BTCUSD;
required += LOT_RS_NVDA * MinBalPerLot_RS_NVDA;
required += LOT_RS_TSLA * MinBalPerLot_RS_TSLA;
required += LOT_RS_XAUUSD * MinBalPerLot_RS_XAUUSD;
required += LOT_RRA_EURUSD * MinBalPerLot_RRA_EURUSD;
required += LOT_RRA_AUDUSD * MinBalPerLot_RRA_AUDUSD;
required += LOT_SE_SuperEMA * MinBalPerLot_SE;
required += LOT_RCO_RSIConsolidation * MinBalPerLot_RCO;
required *= (1.0 + MinBalanceSafetyBufferPercent / 100.0);
if(required < MinAbsoluteRecommendedBalance)
required = MinAbsoluteRecommendedBalance;
return required;
}
void UpdateAutoLotScaling()
{
if(!EnableAutoLotScaling)
{
g_LotScaleGrowth = 1.0;
g_LotScaleMargin = 1.0;
g_LotScaleFinal = 1.0;
return;
}
double refBalance = ScalingReferenceBalanceUSD;
if(refBalance < 1.0)
refBalance = 1.0;
const double balance = AccountInfoDouble(ACCOUNT_BALANCE);
const double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double base = MathMax(balance, equity);
if(base < 1.0)
base = 1.0;
const double growthRatio = base / refBalance;
g_LotScaleGrowth = MathPow(growthRatio, ScalingCurveExponent);
g_LotScaleGrowth = ClampValue(g_LotScaleGrowth, ScaleMinMultiplier, ScaleMaxMultiplier);
g_LotScaleMargin = 1.0;
if(EnableMarginGuard)
{
const double ml = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
if(ml <= 0.0 || ml != ml)
{
g_LotScaleMargin = 1.0;
}
else if(ml >= MarginSafeLevelPercent)
{
g_LotScaleMargin = 1.0;
}
else if(ml <= MarginCriticalLevelPercent)
{
g_LotScaleMargin = MarginGuardMinMultiplier;
}
else
{
const double span = MarginSafeLevelPercent - MarginCriticalLevelPercent;
const double t = (span > 0.0) ? (ml - MarginCriticalLevelPercent) / span : 0.0;
g_LotScaleMargin = MarginGuardMinMultiplier + t * (1.0 - MarginGuardMinMultiplier);
}
g_LotScaleMargin = ClampValue(g_LotScaleMargin, MarginGuardMinMultiplier, 1.0);
}
g_LotScaleFinal = g_LotScaleGrowth * g_LotScaleMargin;
g_LotScaleFinal = ClampValue(g_LotScaleFinal, ScaleMinMultiplier, ScaleMaxMultiplier);
}
input group "=== Balance-based position sizing ==="
input bool ORCH_ScaleLotsByBalance = true;
input bool ORCH_UseEquityInsteadOfBalance = false;
input double ORCH_ReferenceBalance = 10000.0;
input double ORCH_MinBalanceScale = 0.1;
input double ORCH_MaxBalanceScale = 10.0;
//+------------------------------------------------------------------+
//| Strategy 1: DarvasBoxXAUUSD |
@@ -203,7 +115,7 @@ input int DB_VolumeThreshold = 0; // Set to 0 to disable volume threshold ch
input double DB_StopLoss = 1665;
input double DB_TakeProfit = 3685;
input bool DB_EnableLogging = false;
input color DB_BoxColor = clrBlue;
input color DB_BoxColor = (color)16711680;
input int DB_BoxWidth = 1;
input ENUM_TIMEFRAMES DB_TrendTimeframe = PERIOD_H2;
input int DB_MA_Period = 125;
@@ -212,6 +124,8 @@ 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 bool DB_UseVolumeSpikeFilter = true;
input bool DB_UseTrendFilter = true;
input int DB_MagicNumber = 135790;
//+------------------------------------------------------------------+
@@ -224,7 +138,11 @@ 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_TrailingStop = 370.0;
input bool ES_UseTrailingStop = true;
input double ES_TrailingActivationPips = 0.0;
input bool ES_UseStaleStopLossExit = false;
input int ES_StaleStopLossSeconds = 33800;
input double ES_LotGröße = 0.03;
input int ES_MagicNumber = 12350;
input bool ES_UseSpreadAdjustment = true;
@@ -233,6 +151,11 @@ input bool ES_UseBarData = true;
input int ES_MaxTradesPerCrossover = 9;
input int ES_ProfitCheckBars = 18;
input bool ES_CloseUnprofitableTrades = true;
input bool ES_UseWeeklyADXFilter = true;
input int ES_WeeklyADXPeriod = 15;
input double ES_WeeklyADXMin = 40.0;
input int ES_WeeklyADXBarShift = 2;
input bool ES_WeeklyADXUseDirection = true;
//+------------------------------------------------------------------+
//| Strategy 3: RSICrossOverReversalXAUUSD |
@@ -258,6 +181,7 @@ input double RC_exitBuyRSI = 86;
input double RC_exitSellRSI = 10;
input double RC_TrailingStop = 295;
input double RC_emaDistanceThreshold = 165;
input bool RC_UseTrendStrengthFilter = true;
input int RC_tradingHourOneBegin = 24;
input int RC_tradingHourOneEnd = 22;
input int RC_tradingHourTwoBegin = 6;
@@ -330,7 +254,7 @@ input int RM_InpEMADistancePeriod = 26;
//| 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 string RS_APPL_Symbol = "AAPL.NAS"; // Pepperstone / match tester set (also try AAPL.US)
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;
@@ -358,7 +282,7 @@ 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 string RS_NVDA_Symbol = "NVDA.NAS"; // Pepperstone / match tester set (also try NVDA.US)
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;
@@ -372,7 +296,7 @@ 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 string RS_TSLA_Symbol = "TSLA.NAS"; // Pepperstone / match tester set (also try TSLA.US)
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;
@@ -407,6 +331,31 @@ input int RS_ReversalSignsRequired = 2;
input double RS_ReversalRsiVelocity = 16.0;
input double RS_ReversalBodyAtrMult = 5.1;
input group "=== RSI Scalping APPL — Trailing (cluster-fuck BTC-style defaults) ==="
input bool RS_APPL_UseTrailingStop = true;
input double RS_APPL_TrailDistancePoints = 120.0;
input double RS_APPL_TrailActivationPoints = 0.0;
input group "=== RSI Scalping BTCUSD — Trailing ==="
input bool RS_BTCUSD_UseTrailingStop = true;
input double RS_BTCUSD_TrailDistancePoints = 120.0;
input double RS_BTCUSD_TrailActivationPoints = 0.0;
input group "=== RSI Scalping NVDA — Trailing ==="
input bool RS_NVDA_UseTrailingStop = true;
input double RS_NVDA_TrailDistancePoints = 375.0;
input double RS_NVDA_TrailActivationPoints = 75.0;
input group "=== RSI Scalping TSLA — Trailing ==="
input bool RS_TSLA_UseTrailingStop = true;
input double RS_TSLA_TrailDistancePoints = 900.0;
input double RS_TSLA_TrailActivationPoints = 950.0;
input group "=== RSI Scalping XAUUSD — Trailing ==="
input bool RS_XAUUSD_UseTrailingStop = true;
input double RS_XAUUSD_TrailDistancePoints = 71.0;
input double RS_XAUUSD_TrailActivationPoints = 41.0;
//+------------------------------------------------------------------+
//| Strategy 11-12: RSI Reversal Asian Strategies |
//| Each RSI Reversal Asian strategy trades on its own symbol: |
@@ -508,6 +457,108 @@ input ulong RCO_MagicNumber = 20250420;
input int RCO_Slippage = 10;
input int RCO_MaxSpreadPoints = 28;
input group "=== SimpleTrendline BTCUSD ==="
input string ST_BTC_Symbol = "BTCUSD";
input ENUM_TIMEFRAMES ST_BTC_SignalTF = PERIOD_H1;
input ENUM_TIMEFRAMES ST_BTC_HigherTF = PERIOD_H4;
input int ST_BTC_MAPeriod = 150;
input ENUM_MA_METHOD ST_BTC_MAMethod = MODE_SMMA;
input ENUM_APPLIED_PRICE ST_BTC_AppliedPrice = PRICE_OPEN;
input int ST_BTC_HTFBarsToScan = 1200;
input double ST_BTC_LineTouchTolerance = 170.0;
input double ST_BTC_BreakBuffer = 90.0;
input ulong ST_BTC_MagicNumber = 26042501;
input bool ST_BTC_DrawTrendline = true;
input group "=== SimpleTrendline XAUUSD ==="
input string ST_XAU_Symbol = "XAUUSD";
input ENUM_TIMEFRAMES ST_XAU_SignalTF = PERIOD_H1;
input ENUM_TIMEFRAMES ST_XAU_HigherTF = PERIOD_M10;
input int ST_XAU_MAPeriod = 65;
input ENUM_MA_METHOD ST_XAU_MAMethod = MODE_EMA;
input ENUM_APPLIED_PRICE ST_XAU_AppliedPrice = PRICE_OPEN;
input int ST_XAU_HTFBarsToScan = 500;
input double ST_XAU_LineTouchTolerance = 220.0;
input double ST_XAU_BreakBuffer = 110.0;
input ulong ST_XAU_MagicNumber = 26042503;
input bool ST_XAU_DrawTrendline = true;
input group "=== SimpleTrendline GER40 ==="
input string ST_GER_Symbol = "GER40";
input ENUM_TIMEFRAMES ST_GER_SignalTF = PERIOD_M15;
input ENUM_TIMEFRAMES ST_GER_HigherTF = PERIOD_M15;
input int ST_GER_MAPeriod = 65;
input ENUM_MA_METHOD ST_GER_MAMethod = MODE_LWMA;
input ENUM_APPLIED_PRICE ST_GER_AppliedPrice = PRICE_OPEN;
input int ST_GER_HTFBarsToScan = 1200;
input double ST_GER_LineTouchTolerance = 100.0;
input double ST_GER_BreakBuffer = 80.0;
input ulong ST_GER_MagicNumber = 26042502;
input bool ST_GER_DrawTrendline = true;
input group "=== RSI Secret Sauce XAUUSD ==="
input string RSS_Symbol = "XAUUSD";
input int RSS_MagicNumber = 789012;
input int RSS_Slippage = 10;
input ENUM_TIMEFRAMES RSS_Timeframe = PERIOD_M30;
input int RSS_RSIPeriod = 16;
input double RSS_RSIOverbought = 72.5;
input double RSS_RSIOversold = 32.5;
input int RSS_RSILookback = 60;
input int RSS_PeakBars = 2;
input double RSS_StopLossATR = 2.75;
input double RSS_TakeProfitATR = 5.0;
input int RSS_ATRPeriod = 14;
input bool RSS_UseSwingStopLoss = false;
input int RSS_SwingLookback = 30;
input int RSS_MaxPositions = 1;
input int RSS_MinBarsBetweenTrades = 7;
//+------------------------------------------------------------------+
//| Balance scaling: LOT_* = nominal size at ORCH_ReferenceBalance |
//+------------------------------------------------------------------+
double United_BalanceScaleFactor()
{
if(!ORCH_ScaleLotsByBalance || ORCH_ReferenceBalance <= 0.0)
return 1.0;
const double money = ORCH_UseEquityInsteadOfBalance
? AccountInfoDouble(ACCOUNT_EQUITY)
: AccountInfoDouble(ACCOUNT_BALANCE);
double raw = money / ORCH_ReferenceBalance;
if(raw < ORCH_MinBalanceScale)
raw = ORCH_MinBalanceScale;
if(raw > ORCH_MaxBalanceScale)
raw = ORCH_MaxBalanceScale;
return raw;
}
double United_ScaledLot(const double baseLot)
{
const double lot = baseLot * United_BalanceScaleFactor();
return (lot > 0.0 ? lot : 0.0);
}
void United_RefreshScaledLots()
{
g_DB_LotSize = United_ScaledLot(LOT_DB_DarvasBox);
g_ES_LotSize = United_ScaledLot(LOT_ES_EMASlopeDistance);
g_RC_LotSize = United_ScaledLot(LOT_RC_RSICrossOver);
g_RM_LotSize = United_ScaledLot(LOT_RM_RSIMidPointHijack);
g_Pos_RS_APPL = United_ScaledLot(LOT_RS_APPL);
g_Pos_RS_BTCUSD = United_ScaledLot(LOT_RS_BTCUSD);
g_Pos_RS_NVDA = United_ScaledLot(LOT_RS_NVDA);
g_Pos_RS_TSLA = United_ScaledLot(LOT_RS_TSLA);
g_Pos_RS_XAUUSD = United_ScaledLot(LOT_RS_XAUUSD);
g_Pos_RRA_EURUSD = United_ScaledLot(LOT_RRA_EURUSD);
g_Pos_RRA_AUDUSD = United_ScaledLot(LOT_RRA_AUDUSD);
g_Pos_SE = United_ScaledLot(LOT_SE_SuperEMA);
g_Pos_RCO = United_ScaledLot(LOT_RCO_RSIConsolidation);
g_Pos_ST_BTCUSD = United_ScaledLot(LOT_ST_BTCUSD);
g_Pos_ST_XAUUSD = United_ScaledLot(LOT_ST_XAUUSD);
g_Pos_ST_GER40 = United_ScaledLot(LOT_ST_GER40);
g_RSS_LotSize = United_ScaledLot(LOT_RSS_SecretSauce);
}
//+------------------------------------------------------------------+
//| Global Variables - DarvasBox |
//+------------------------------------------------------------------+
@@ -545,6 +596,7 @@ struct EMASlopeData {
bool crossover_detected;
datetime trade_open_time;
datetime last_bar_time;
datetime es_last_sl_adjust_success_time;
};
//+------------------------------------------------------------------+
@@ -606,6 +658,10 @@ RSIScalpingData rsTSLAData;
RSIScalpingData rsXAUUSDData;
SuperEMAData seData;
RSIConsolidationData rcoData;
SimpleTrendlineData stBTCData;
SimpleTrendlineData stXAUData;
SimpleTrendlineData stGERData;
RSISecretSauceOrcData rssData;
//+------------------------------------------------------------------+
//| Global Variables - RSI Reversal Asian |
@@ -619,22 +675,8 @@ RSIReversalAsianData rraAUDUSDData;
int OnInit()
{
int initResult = INIT_SUCCEEDED;
UpdateAutoLotScaling();
// Initialize global lot size variables
g_ES_LotSize = GetAutoScaledLot(LOT_ES_EMASlopeDistance);
g_RC_LotSize = GetAutoScaledLot(LOT_RC_RSICrossOver);
g_RM_LotSize = GetAutoScaledLot(LOT_RM_RSIMidPointHijack);
if(PrintMinimumBalanceRecommendation)
{
const double minBalance = ComputeRecommendedMinBalance();
Print("Lot scaling initialized | scale=", DoubleToString(g_LotScaleFinal, 3),
" (growth=", DoubleToString(g_LotScaleGrowth, 3),
", margin=", DoubleToString(g_LotScaleMargin, 3), ")",
" | recommended minimum balance=", DoubleToString(minBalance, 2));
}
United_RefreshScaledLots();
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
if(EnableDarvasBox)
@@ -669,6 +711,10 @@ int OnInit()
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);
if(EnableRSISecretSauce)
if(!InitRSISecretSauce(rssData, RSS_Symbol))
Print("Warning: RSI Secret Sauce failed to initialize for symbol '", RSS_Symbol, "'");
if(EnableSuperEMA)
if(!InitSuperEMA(seData, SE_Symbol, SE_Timeframe, SE_SlippagePoints, SE_MagicNumber,
SE_EmaFast, SE_EmaMid, SE_EmaSlow, SE_EmaTrendBars,
@@ -704,6 +750,24 @@ int OnInit()
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, "'");
if(EnableSimpleTrendlineBTCUSD)
if(!InitSimpleTrendline(stBTCData, ST_BTC_Symbol, ST_BTC_SignalTF, ST_BTC_HigherTF, ST_BTC_MAPeriod,
ST_BTC_MAMethod, ST_BTC_AppliedPrice, ST_BTC_HTFBarsToScan,
ST_BTC_LineTouchTolerance, ST_BTC_BreakBuffer, ST_BTC_MagicNumber, ST_BTC_DrawTrendline))
Print("Warning: SimpleTrendlineBTCUSD failed to initialize for symbol '", ST_BTC_Symbol, "'");
if(EnableSimpleTrendlineXAUUSD)
if(!InitSimpleTrendline(stXAUData, ST_XAU_Symbol, ST_XAU_SignalTF, ST_XAU_HigherTF, ST_XAU_MAPeriod,
ST_XAU_MAMethod, ST_XAU_AppliedPrice, ST_XAU_HTFBarsToScan,
ST_XAU_LineTouchTolerance, ST_XAU_BreakBuffer, ST_XAU_MagicNumber, ST_XAU_DrawTrendline))
Print("Warning: SimpleTrendlineXAUUSD failed to initialize for symbol '", ST_XAU_Symbol, "'");
if(EnableSimpleTrendlineGER40)
if(!InitSimpleTrendline(stGERData, ST_GER_Symbol, ST_GER_SignalTF, ST_GER_HigherTF, ST_GER_MAPeriod,
ST_GER_MAMethod, ST_GER_AppliedPrice, ST_GER_HTFBarsToScan,
ST_GER_LineTouchTolerance, ST_GER_BreakBuffer, ST_GER_MagicNumber, ST_GER_DrawTrendline))
Print("Warning: SimpleTrendlineGER40 failed to initialize for symbol '", ST_GER_Symbol, "'");
Print("United EA initialized. Active strategies: ",
(EnableDarvasBox ? "DarvasBox " : ""),
@@ -715,10 +779,14 @@ int OnInit()
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""),
(EnableRSISecretSauce ? "RSISecretSauce " : ""),
(EnableSuperEMA ? "SuperEMA " : ""),
(EnableRSIConsolidation ? "RSIConsolidation " : ""),
(EnableRSIReversalAsianEURUSD ? "RSIReversalAsianEURUSD " : ""),
(EnableRSIReversalAsianAUDUSD ? "RSIReversalAsianAUDUSD " : ""));
(EnableRSIReversalAsianAUDUSD ? "RSIReversalAsianAUDUSD " : ""),
(EnableSimpleTrendlineBTCUSD ? "SimpleTrendlineBTCUSD " : ""),
(EnableSimpleTrendlineXAUUSD ? "SimpleTrendlineXAUUSD " : ""),
(EnableSimpleTrendlineGER40 ? "SimpleTrendlineGER40 " : ""));
return initResult;
}
@@ -755,6 +823,9 @@ void OnDeinit(const int reason)
if(EnableRSIScalpingXAUUSD)
DeinitRSIScalping(rsXAUUSDData);
if(EnableRSISecretSauce)
DeinitRSISecretSauce(rssData);
if(EnableSuperEMA)
DeinitSuperEMA(seData);
@@ -766,6 +837,13 @@ void OnDeinit(const int reason)
if(EnableRSIReversalAsianAUDUSD)
DeinitRSIReversalAsian(rraAUDUSDData);
if(EnableSimpleTrendlineBTCUSD)
DeinitSimpleTrendline(stBTCData);
if(EnableSimpleTrendlineXAUUSD)
DeinitSimpleTrendline(stXAUData);
if(EnableSimpleTrendlineGER40)
DeinitSimpleTrendline(stGERData);
Print("United EA deinitialized. Reason: ", reason);
}
@@ -775,35 +853,7 @@ void OnDeinit(const int reason)
//+------------------------------------------------------------------+
void OnTick()
{
UpdateAutoLotScaling();
g_ES_LotSize = GetAutoScaledLot(LOT_ES_EMASlopeDistance);
g_RC_LotSize = GetAutoScaledLot(LOT_RC_RSICrossOver);
g_RM_LotSize = GetAutoScaledLot(LOT_RM_RSIMidPointHijack);
if(ScaleLogIntervalSeconds > 0)
{
const datetime now = TimeCurrent();
if(g_LastScaleLogTime == 0 || (now - g_LastScaleLogTime) >= ScaleLogIntervalSeconds)
{
g_LastScaleLogTime = now;
Print("Lot scale update | final=", DoubleToString(g_LotScaleFinal, 3),
" growth=", DoubleToString(g_LotScaleGrowth, 3),
" margin=", DoubleToString(g_LotScaleMargin, 3),
" ml=", DoubleToString(AccountInfoDouble(ACCOUNT_MARGIN_LEVEL), 1),
" eq=", DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2),
" bal=", DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2));
}
}
if(ShowScaleStatusOnChart)
{
const double minBalance = ComputeRecommendedMinBalance();
Comment("Scale=", DoubleToString(g_LotScaleFinal, 3),
" (growth=", DoubleToString(g_LotScaleGrowth, 3),
", margin=", DoubleToString(g_LotScaleMargin, 3), ")",
" | Rec.Min.Balance=", DoubleToString(minBalance, 2),
" | MarginLevel=", DoubleToString(AccountInfoDouble(ACCOUNT_MARGIN_LEVEL), 1), "%");
}
United_RefreshScaledLots();
if(EnableDarvasBox)
ProcessDarvasBox(DB_Symbol);
@@ -820,49 +870,64 @@ void OnTick()
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, GetAutoScaledLot(LOT_RS_APPL), RS_APPL_MagicNumber,
RS_APPL_BarsToWait, g_Pos_RS_APPL, RS_APPL_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_APPL_UseTrailingStop, RS_APPL_TrailDistancePoints, RS_APPL_TrailActivationPoints);
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, GetAutoScaledLot(LOT_RS_BTCUSD), RS_BTCUSD_MagicNumber,
RS_BTCUSD_BarsToWait, g_Pos_RS_BTCUSD, RS_BTCUSD_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_BTCUSD_UseTrailingStop, RS_BTCUSD_TrailDistancePoints, RS_BTCUSD_TrailActivationPoints);
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, GetAutoScaledLot(LOT_RS_NVDA), RS_NVDA_MagicNumber,
RS_NVDA_BarsToWait, g_Pos_RS_NVDA, RS_NVDA_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_NVDA_UseTrailingStop, RS_NVDA_TrailDistancePoints, RS_NVDA_TrailActivationPoints);
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, GetAutoScaledLot(LOT_RS_TSLA), RS_TSLA_MagicNumber,
RS_TSLA_BarsToWait, g_Pos_RS_TSLA, RS_TSLA_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_TSLA_UseTrailingStop, RS_TSLA_TrailDistancePoints, RS_TSLA_TrailActivationPoints);
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, GetAutoScaledLot(LOT_RS_XAUUSD), RS_XAUUSD_MagicNumber,
RS_XAUUSD_BarsToWait, g_Pos_RS_XAUUSD, RS_XAUUSD_MagicNumber,
RS_UseReversalEscape, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_XAUUSD_UseTrailingStop, RS_XAUUSD_TrailDistancePoints, RS_XAUUSD_TrailActivationPoints);
if(EnableRSISecretSauce)
ProcessRSISecretSauce(rssData, g_RSS_LotSize);
if(EnableRSIReversalAsianEURUSD)
ProcessRSIReversalAsian(rraEURUSDData, GetAutoScaledLot(LOT_RRA_EURUSD));
ProcessRSIReversalAsian(rraEURUSDData, g_Pos_RRA_EURUSD);
if(EnableRSIReversalAsianAUDUSD)
ProcessRSIReversalAsian(rraAUDUSDData, GetAutoScaledLot(LOT_RRA_AUDUSD));
ProcessRSIReversalAsian(rraAUDUSDData, g_Pos_RRA_AUDUSD);
if(EnableSuperEMA)
ProcessSuperEMA(seData, GetAutoScaledLot(LOT_SE_SuperEMA));
ProcessSuperEMA(seData, g_Pos_SE);
if(EnableRSIConsolidation)
ProcessRSIConsolidation(rcoData, GetAutoScaledLot(LOT_RCO_RSIConsolidation));
ProcessRSIConsolidation(rcoData, g_Pos_RCO);
if(EnableSimpleTrendlineBTCUSD)
ProcessSimpleTrendline(stBTCData, g_Pos_ST_BTCUSD);
if(EnableSimpleTrendlineXAUUSD)
ProcessSimpleTrendline(stXAUData, g_Pos_ST_XAUUSD);
if(EnableSimpleTrendlineGER40)
ProcessSimpleTrendline(stGERData, g_Pos_ST_GER40);
}
//+------------------------------------------------------------------+

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

@@ -13,7 +13,6 @@
#include <Indicators\Trend.mqh>
#include <Indicators\Volumes.mqh>
#include "MagicNumberHelpers.mqh"
#include "PerformanceEvaluator.mqh"
//+------------------------------------------------------------------+
//| Strategy Enable/Disable Switches |
@@ -62,7 +61,11 @@ 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_TrailingStop = 370.0;
input bool ES_UseTrailingStop = true;
input double ES_TrailingActivationPips = 0.0;
input bool ES_UseStaleStopLossExit = false;
input int ES_StaleStopLossSeconds = 33800;
input double ES_LotGröße = 0.03;
input int ES_MagicNumber = 12350;
input bool ES_UseSpreadAdjustment = true;
@@ -71,6 +74,11 @@ input bool ES_UseBarData = true;
input int ES_MaxTradesPerCrossover = 9;
input int ES_ProfitCheckBars = 18;
input bool ES_CloseUnprofitableTrades = true;
input bool ES_UseWeeklyADXFilter = true;
input int ES_WeeklyADXPeriod = 15;
input double ES_WeeklyADXMin = 40.0;
input int ES_WeeklyADXBarShift = 2;
input bool ES_WeeklyADXUseDirection = true;
//+------------------------------------------------------------------+
//| Strategy 3: RSICrossOverReversalXAUUSD |
@@ -252,6 +260,44 @@ input double RS_XAUUSD_LotSize = 0.1;
input int RS_XAUUSD_MagicNumber = 129102315;
input int RS_XAUUSD_Slippage = 3;
input group "=== RSI Scalping Reversal escape (XAUUSD) ==="
input bool RS_UseReversalEscape = false;
input int RS_ReversalATRPeriod = 14;
input double RS_ReversalAdverseAtrMult = 5.25;
input int RS_ReversalSignsRequired = 2;
input double RS_ReversalRsiVelocity = 16.0;
input double RS_ReversalBodyAtrMult = 5.1;
input group "=== RSI Scalping APPL — Trailing ==="
input bool RS_APPL_UseTrailingStop = true;
input double RS_APPL_TrailDistancePoints = 120.0;
input double RS_APPL_TrailActivationPoints = 0.0;
input group "=== RSI Scalping BTCUSD — Trailing ==="
input bool RS_BTCUSD_UseTrailingStop = true;
input double RS_BTCUSD_TrailDistancePoints = 120.0;
input double RS_BTCUSD_TrailActivationPoints = 0.0;
input group "=== RSI Scalping MSFT — Trailing ==="
input bool RS_MSFT_UseTrailingStop = true;
input double RS_MSFT_TrailDistancePoints = 375.0;
input double RS_MSFT_TrailActivationPoints = 75.0;
input group "=== RSI Scalping NVDA — Trailing ==="
input bool RS_NVDA_UseTrailingStop = true;
input double RS_NVDA_TrailDistancePoints = 375.0;
input double RS_NVDA_TrailActivationPoints = 75.0;
input group "=== RSI Scalping TSLA — Trailing ==="
input bool RS_TSLA_UseTrailingStop = true;
input double RS_TSLA_TrailDistancePoints = 900.0;
input double RS_TSLA_TrailActivationPoints = 950.0;
input group "=== RSI Scalping XAUUSD — Trailing ==="
input bool RS_XAUUSD_UseTrailingStop = true;
input double RS_XAUUSD_TrailDistancePoints = 71.0;
input double RS_XAUUSD_TrailActivationPoints = 41.0;
//+------------------------------------------------------------------+
//| Global Variables - DarvasBox |
//+------------------------------------------------------------------+
@@ -289,6 +335,7 @@ struct EMASlopeData {
bool crossover_detected;
datetime trade_open_time;
datetime last_bar_time;
datetime es_last_sl_adjust_success_time;
};
//+------------------------------------------------------------------+
@@ -371,19 +418,18 @@ RSIScalpingData rsTSLAData;
RSIScalpingData rsXAUUSDData;
//+------------------------------------------------------------------+
//| Global Variables for Dynamic Lot Sizes |
//| Global Variables for lot sizes (from inputs below) |
//+------------------------------------------------------------------+
// 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_MSFT_LotSize = 5.0; // Stock - start with stock minimum (5.0)
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)
double g_DB_LotSize = 0.01;
double g_ES_LotSize;
double g_RC_LotSize;
double g_RM_LotSize;
double g_RS_APPL_LotSize;
double g_RS_BTCUSD_LotSize;
double g_RS_MSFT_LotSize;
double g_RS_NVDA_LotSize;
double g_RS_TSLA_LotSize;
double g_RS_XAUUSD_LotSize;
//+------------------------------------------------------------------+
//| Expert initialization function |
@@ -391,148 +437,52 @@ double g_RS_XAUUSD_LotSize = 0.01; // Forex - start with forex minimum (0.01)
int OnInit()
{
int initResult = INIT_SUCCEEDED;
// Initialize Performance Evaluator
InitPerformanceTracking();
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
g_ES_LotSize = ES_LotGröße;
g_RC_LotSize = RC_lotSize;
g_RM_LotSize = RM_InpLotSize;
g_RS_APPL_LotSize = RS_APPL_LotSize;
g_RS_BTCUSD_LotSize = RS_BTCUSD_LotSize;
g_RS_MSFT_LotSize = RS_MSFT_LotSize;
g_RS_NVDA_LotSize = RS_NVDA_LotSize;
g_RS_TSLA_LotSize = RS_TSLA_LotSize;
g_RS_XAUUSD_LotSize = RS_XAUUSD_LotSize;
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(EnableRSIScalpingMSFT)
{
InitRSIScalping(rsMSFTData, RS_MSFT_Symbol, RS_MSFT_TimeFrame, RS_MSFT_RSI_Period, RS_MSFT_RSI_Applied_Price, RS_MSFT_MagicNumber, RS_MSFT_Slippage);
RegisterStrategy("RSIScalpingMSFT", RS_MSFT_MagicNumber, RS_MSFT_LotSize, RS_MSFT_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_MSFT_Symbol);
g_RS_MSFT_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("RSIScalpingMSFT", RS_MSFT_MagicNumber);
if(adjustedLot > 0) g_RS_MSFT_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: ",
Print("United EA (self-evaluate build) initialized. Active strategies: ",
(EnableDarvasBox ? "DarvasBox " : ""),
(EnableEMASlopeDistance ? "EMASlope " : ""),
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
@@ -543,10 +493,7 @@ int OnInit()
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""));
if(PE_EnableLogging)
Print(GetPerformanceSummary());
return initResult;
}
@@ -593,41 +540,6 @@ void OnDeinit(const int reason)
//+------------------------------------------------------------------+
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("RSIScalpingMSFT", RS_MSFT_MagicNumber);
if(adjustedLot > 0) g_RS_MSFT_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);
@@ -643,32 +555,50 @@ void OnTick()
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);
RS_APPL_BarsToWait, g_RS_APPL_LotSize, RS_APPL_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_APPL_UseTrailingStop, RS_APPL_TrailDistancePoints, RS_APPL_TrailActivationPoints);
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);
RS_BTCUSD_BarsToWait, g_RS_BTCUSD_LotSize, RS_BTCUSD_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_BTCUSD_UseTrailingStop, RS_BTCUSD_TrailDistancePoints, RS_BTCUSD_TrailActivationPoints);
if(EnableRSIScalpingMSFT)
ProcessRSIScalping(rsMSFTData, RS_MSFT_Symbol, RS_MSFT_TimeFrame, RS_MSFT_RSI_Period, RS_MSFT_RSI_Applied_Price,
RS_MSFT_RSI_Overbought, RS_MSFT_RSI_Oversold, RS_MSFT_RSI_Target_Buy, RS_MSFT_RSI_Target_Sell,
RS_MSFT_BarsToWait, g_RS_MSFT_LotSize, RS_MSFT_MagicNumber);
RS_MSFT_BarsToWait, g_RS_MSFT_LotSize, RS_MSFT_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_MSFT_UseTrailingStop, RS_MSFT_TrailDistancePoints, RS_MSFT_TrailActivationPoints);
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);
RS_NVDA_BarsToWait, g_RS_NVDA_LotSize, RS_NVDA_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_NVDA_UseTrailingStop, RS_NVDA_TrailDistancePoints, RS_NVDA_TrailActivationPoints);
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);
RS_TSLA_BarsToWait, g_RS_TSLA_LotSize, RS_TSLA_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_TSLA_UseTrailingStop, RS_TSLA_TrailDistancePoints, RS_TSLA_TrailActivationPoints);
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);
RS_XAUUSD_BarsToWait, g_RS_XAUUSD_LotSize, RS_XAUUSD_MagicNumber,
RS_UseReversalEscape, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_XAUUSD_UseTrailingStop, RS_XAUUSD_TrailDistancePoints, RS_XAUUSD_TrailActivationPoints);
}
//+------------------------------------------------------------------+
@@ -12,29 +12,18 @@
//+------------------------------------------------------------------+
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)
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
// 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;
ulong ticket = PositionGetTicket(i);
if(ticket == 0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) == symbol &&
(ulong)PositionGetInteger(POSITION_MAGIC) == magic_number)
return true;
}
return true;
return false;
}
//+------------------------------------------------------------------+
@@ -76,14 +65,13 @@ 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;
}
}
if(ticket == 0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) == symbol &&
(ulong)PositionGetInteger(POSITION_MAGIC) == magic_number)
return ticket;
}
return 0;
}
@@ -144,16 +132,42 @@ int CountPositionsByMagic(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)
{
count++;
}
}
if(ticket == 0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) == symbol &&
(ulong)PositionGetInteger(POSITION_MAGIC) == magic_number)
count++;
}
return count;
}
//+------------------------------------------------------------------+
//| Align volume to SYMBOL_VOLUME_STEP / min / max (avoids Invalid volume) |
//+------------------------------------------------------------------+
double United_NormalizeVolume(const string symbol, double volume)
{
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if(lotStep <= 0.0)
lotStep = 0.01;
double v = MathFloor(volume / lotStep) * lotStep;
if(v < minLot)
v = minLot;
if(v > maxLot)
v = maxLot;
int digits = (int)MathCeil(-MathLog10(lotStep));
if(digits < 0)
digits = 0;
if(digits > 8)
digits = 8;
return NormalizeDouble(v, digits);
}
//+------------------------------------------------------------------+
@@ -1,6 +1,18 @@
//+------------------------------------------------------------------+
//| DarvasBoxStrategy.mqh |
//+------------------------------------------------------------------+
// MQL5: no #if — use #ifdef only (no defined() / || in one #if)
#ifdef UNITED_V2_DYNAMIC_LOTS
extern double g_DB_LotSize;
#define DARVAS_TRADE_LOT (g_DB_LotSize)
#else
#ifdef CLUSTER0_ORCHESTRATOR
extern double g_DB_LotSize;
#define DARVAS_TRADE_LOT (g_DB_LotSize)
#else
#define DARVAS_TRADE_LOT 0.01
#endif
#endif
bool InitDarvasBox(string symbol)
{
@@ -24,7 +36,9 @@ bool InitDarvasBox(string symbol)
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);
// Same TF as box highs/lows (H1 loop in CalculateDarvasBox). PERIOD_CURRENT breaks when United EA
// runs on a chart timeframe other than H1 (volume/breakout no longer match the box).
dbData.volumeHandle = iVolumes(symbol, PERIOD_H1, VOLUME_TICK);
if(dbData.maHandle == INVALID_HANDLE || dbData.volumeHandle == INVALID_HANDLE)
{
@@ -133,6 +147,9 @@ bool ValidateStopLevels(double price, double &sl, double &tp, ENUM_ORDER_TYPE or
bool IsTrendFavorable(ENUM_ORDER_TYPE orderType)
{
if(!DB_UseTrendFilter)
return true;
double ma[];
ArraySetAsSeries(ma, true);
@@ -150,6 +167,9 @@ bool IsTrendFavorable(ENUM_ORDER_TYPE orderType)
bool CheckVolumeConditions()
{
if(!DB_UseVolumeSpikeFilter)
return true;
double volumes[];
ArraySetAsSeries(volumes, true);
@@ -162,8 +182,10 @@ bool CheckVolumeConditions()
volumeMA /= DB_VolumeMA_Period;
double currentVolume = volumes[0];
if(volumeMA <= 0.0)
return (currentVolume > 0.0);
double volumeRatio = currentVolume / volumeMA;
return (volumeRatio > DB_VolumeThresholdMultiplier);
}
@@ -191,13 +213,19 @@ bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price, double sl, double tp)
}
bool result = false;
const double lot = United_NormalizeVolume(dbData.symbol, DARVAS_TRADE_LOT);
if(lot <= 0.0)
{
Print("DarvasBox: Order rejected - invalid lot after normalize (raw=", DARVAS_TRADE_LOT, ")");
return 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(0.01, dbData.symbol, 0, sl, tp, "Darvas Box Breakout");
result = dbData.trade.Buy(lot, dbData.symbol, 0, sl, tp, "Darvas Box Breakout");
else
result = dbData.trade.Sell(0.01, dbData.symbol, 0, sl, tp, "Darvas Box Breakdown");
result = dbData.trade.Sell(lot, dbData.symbol, 0, sl, tp, "Darvas Box Breakdown");
// Always log errors, success only if logging enabled
if(result)
@@ -242,7 +270,7 @@ void ProcessDarvasBox(string symbol)
if(dbData.boxFormed)
{
double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK);
long currentVolume_long = iVolume(dbData.symbol, PERIOD_CURRENT, 0);
long currentVolume_long = iVolume(dbData.symbol, PERIOD_H1, 0);
double currentVolume = (double)currentVolume_long;
if(DB_EnableLogging)
@@ -14,6 +14,7 @@ bool InitEMASlopeDistance(string symbol)
esData.crossover_detected = false;
esData.trade_open_time = 0;
esData.last_bar_time = 0;
esData.es_last_sl_adjust_success_time = 0;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
@@ -48,6 +49,64 @@ void DeinitEMASlopeDistance()
IndicatorRelease(esData.ema_handle);
}
bool ES_IsWeeklyADXTrendFavorable(const ENUM_ORDER_TYPE order_type)
{
if(!ES_UseWeeklyADXFilter)
return true;
int adxShift = ES_WeeklyADXBarShift;
if(adxShift < 0)
adxShift = 0;
int adx_handle = iADX(esData.symbol, PERIOD_W1, ES_WeeklyADXPeriod);
if(adx_handle == INVALID_HANDLE)
return false;
double adx_buf[], plus_di_buf[], minus_di_buf[];
ArraySetAsSeries(adx_buf, true);
ArraySetAsSeries(plus_di_buf, true);
ArraySetAsSeries(minus_di_buf, true);
bool ok_adx = (CopyBuffer(adx_handle, 0, adxShift, 1, adx_buf) > 0);
bool ok_plus = (CopyBuffer(adx_handle, 1, adxShift, 1, plus_di_buf) > 0);
bool ok_minus = (CopyBuffer(adx_handle, 2, adxShift, 1, minus_di_buf) > 0);
IndicatorRelease(adx_handle);
if(!ok_adx || !ok_plus || !ok_minus)
return false;
double adx_value = adx_buf[0];
double plus_di = plus_di_buf[0];
double minus_di = minus_di_buf[0];
bool strength_ok = (adx_value >= ES_WeeklyADXMin);
bool direction_ok = true;
if(ES_WeeklyADXUseDirection)
{
if(order_type == ORDER_TYPE_BUY)
direction_ok = (plus_di > minus_di);
else
direction_ok = (minus_di > plus_di);
}
return strength_ok && direction_ok;
}
bool ES_TrailingActivationReached(const double position_profit, const ENUM_POSITION_TYPE position_type,
const double pips_multiplier)
{
if(ES_TrailingActivationPips <= 0.0)
return (position_profit > 0.0);
const double open_px = PositionGetDouble(POSITION_PRICE_OPEN);
if(position_type == POSITION_TYPE_BUY)
{
const double bid = SymbolInfoDouble(esData.symbol, SYMBOL_BID);
return ((bid - open_px) / SymbolInfoDouble(esData.symbol, SYMBOL_POINT) / pips_multiplier >= ES_TrailingActivationPips);
}
const double ask = SymbolInfoDouble(esData.symbol, SYMBOL_ASK);
return ((open_px - ask) / SymbolInfoDouble(esData.symbol, SYMBOL_POINT) / pips_multiplier >= ES_TrailingActivationPips);
}
//+------------------------------------------------------------------+
//| EMA Berechnung (EMA Calculation) |
//+------------------------------------------------------------------+
@@ -172,6 +231,11 @@ void PrüfeTrigger()
if(bullish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
if(!ES_IsWeeklyADXTrendFavorable(ORDER_TYPE_BUY))
{
Print("TRACE: Weekly ADX blockiert BUY-Entry");
return;
}
Print("TRACE: Versuche KAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_BUY))
{
@@ -180,6 +244,11 @@ void PrüfeTrigger()
}
else if(bearish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
if(!ES_IsWeeklyADXTrendFavorable(ORDER_TYPE_SELL))
{
Print("TRACE: Weekly ADX blockiert SELL-Entry");
return;
}
Print("TRACE: Versuche VERKAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_SELL))
{
@@ -199,17 +268,23 @@ void PrüfeTrigger()
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);
const double lot = United_NormalizeVolume(esData.symbol, g_ES_LotSize);
Print("TRACE: Lot (raw): ", g_ES_LotSize, " normalized: ", lot);
if(lot <= 0.0)
{
Print("TRACE: Abbruch — Lot nach Normalisierung ungültig");
return false;
}
bool success = false;
if(order_type == ORDER_TYPE_BUY)
{
success = esData.trade.Buy(g_ES_LotSize, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
success = esData.trade.Buy(lot, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
}
else
{
success = esData.trade.Sell(g_ES_LotSize, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
success = esData.trade.Sell(lot, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
}
if(success)
@@ -219,6 +294,7 @@ bool PlatziereTrade(ENUM_ORDER_TYPE order_type)
//--- Trade-Öffnungszeit speichern (Save trade opening time)
esData.trade_open_time = iTime(esData.symbol, ES_Timeframe, 0);
esData.es_last_sl_adjust_success_time = 0;
Print("TRACE: Trade-Öffnungszeit: ", TimeToString(esData.trade_open_time));
//--- Überwachung zurücksetzen (Reset monitoring)
@@ -244,41 +320,51 @@ void VerwalteTrades()
{
if(!PositionSelectByMagic(esData.symbol, (ulong)ES_MagicNumber))
return;
if(ES_UseStaleStopLossExit && ES_StaleStopLossSeconds > 0)
{
const datetime stale_ref = (esData.es_last_sl_adjust_success_time > 0)
? esData.es_last_sl_adjust_success_time
: (datetime)PositionGetInteger(POSITION_TIME);
if(TimeCurrent() - stale_ref >= ES_StaleStopLossSeconds)
{
SchließePosition("Stale stop loss - keine SL-Anpassung");
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
const double trail_dist = ES_TrailingStop * point * pips_multiplier;
const long stops_level = SymbolInfoInteger(esData.symbol, SYMBOL_TRADE_STOPS_LEVEL);
const double min_dist = (double)stops_level * point;
if(ES_UseTrailingStop && ES_TrailingStop > 0.0 && ES_TrailingActivationReached(position_profit, position_type, pips_multiplier))
{
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)
{
const double bid = SymbolInfoDouble(esData.symbol, SYMBOL_BID);
double new_stop_loss = NormalizeDouble(bid - trail_dist, digits);
if(min_dist > 0.0 && bid - new_stop_loss < min_dist)
new_stop_loss = NormalizeDouble(bid - min_dist, digits);
const double current_stop_loss = PositionGetDouble(POSITION_SL);
if(new_stop_loss < bid && new_stop_loss > 0.0 && 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)
{
const double ask = SymbolInfoDouble(esData.symbol, SYMBOL_ASK);
double new_stop_loss = NormalizeDouble(ask + trail_dist, digits);
if(min_dist > 0.0 && new_stop_loss - ask < min_dist)
new_stop_loss = NormalizeDouble(ask + min_dist, digits);
const double current_stop_loss = PositionGetDouble(POSITION_SL);
if(new_stop_loss > ask && new_stop_loss > 0.0 &&
(new_stop_loss < current_stop_loss || current_stop_loss == 0.0))
ÄndereStopLoss(new_stop_loss);
}
}
}
@@ -366,6 +452,7 @@ void ÄndereStopLoss(double new_stop_loss)
if(success)
{
esData.es_last_sl_adjust_success_time = TimeCurrent();
Print("TRACE: Stop Loss erfolgreich geändert auf: ", new_stop_loss);
}
else
@@ -386,6 +473,7 @@ void SchließePosition(string reason = "Unbekannt")
if(success)
{
esData.es_last_sl_adjust_success_time = 0;
Print("TRACE: Position erfolgreich geschlossen - Grund: ", reason);
}
else
@@ -400,40 +488,34 @@ void SchließePosition(string reason = "Unbekannt")
//+------------------------------------------------------------------+
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.symbol = symbol;
const datetime current_bar_time = iTime(esData.symbol, ES_Timeframe, 0);
const bool new_bar = (current_bar_time != esData.last_bar_time);
const bool has_position = PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber);
if(ES_UseBarData && !new_bar && !has_position)
return;
if(new_bar)
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)
const bool run_signals = (!ES_UseBarData || new_bar);
if(run_signals && 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) ===");
@@ -443,7 +525,7 @@ void ProcessEMASlopeDistance(string symbol)
{
Print("=== DEBUG INFO (Tick) ===");
}
Print("Aktueller Close: ", aktueller_close);
Print("EMA: ", ema_aktuell);
Print("Preis-Abstand: ", preis_abstand, " Pips");
@@ -455,41 +537,39 @@ void ProcessEMASlopeDistance(string symbol)
Print("Trades im aktuellen Crossover: ", esData.trades_in_current_crossover, "/", ES_MaxTradesPerCrossover);
Print("==================");
}
//--- Überwachung prüfen (Check monitoring)
if(esData.überwachung_aktiv)
if(run_signals)
{
if(ES_UseBarData)
if(esData.überwachung_aktiv)
{
// 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)
if(ES_UseBarData)
{
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");
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
{
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");
}
}
}
PrüfeTrigger();
}
//--- Trigger-Bedingungen prüfen (Check trigger conditions)
PrüfeTrigger();
//--- Trade Management (Trade management)
VerwalteTrades();
}
@@ -79,6 +79,7 @@ bool InitRSICrossOverReversal(string symbol)
}
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
rcData.trade.SetDeviationInPoints(RC_slippage);
rcData.isInitialized = true;
Print("RSICrossOverReversal: Successfully initialized for symbol '", symbol, "'");
return true;
@@ -190,7 +191,13 @@ void ProcessRSICrossOverReversal(string symbol)
double emaSlope = (currentEMA - previousEMA) * 100;
const double closeCurr = iClose(rcData.symbol, RC_TimeFrame1, 0);
double priceToEmaDistance = (closeCurr - currentEMA) * 10;
// Raw (close-EMA)*10 blows past threshold on XAUUSD (~2600) almost every bar — blocks all entries.
// Compare distance in pips so RC_emaDistanceThreshold matches intent across symbols.
const double point = SymbolInfoDouble(rcData.symbol, SYMBOL_POINT);
const int symDig = (int)SymbolInfoInteger(rcData.symbol, SYMBOL_DIGITS);
const double pipMult = (symDig == 3 || symDig == 5) ? 10.0 : 1.0;
const double pipSize = (point > 0.0 ? point * pipMult : point);
const double priceToEmaPips = (pipSize > 0.0 ? MathAbs(closeCurr - currentEMA) / pipSize : 0.0);
bool isBuyPosition = false;
bool isSellPosition = false;
@@ -209,7 +216,8 @@ void ProcessRSICrossOverReversal(string symbol)
ApplyTrailingStop();
bool cooldownPassed = (currentTime - rcData.lastTradeTime) >= RC_cooldownSeconds;
bool isTrendStrong = MathAbs(emaSlope) > RC_emaSlopeThreshold || MathAbs(priceToEmaDistance) > RC_emaDistanceThreshold;
const bool isTrendStrong = RC_UseTrendStrengthFilter &&
(MathAbs(emaSlope) > RC_emaSlopeThreshold || priceToEmaPips > RC_emaDistanceThreshold);
if(isBuyPosition && currentRSI > RC_exitBuyRSI)
{
@@ -233,10 +241,12 @@ void ProcessRSICrossOverReversal(string symbol)
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"))
const double vol = United_NormalizeVolume(rcData.symbol, g_RC_LotSize);
if(vol > 0.0)
{
rcData.lastTradeTime = currentTime;
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
if(rcData.trade.Sell(vol, rcData.symbol, 0.0, 0.0, 0.0, "Sell Order"))
rcData.lastTradeTime = currentTime;
}
}
@@ -244,10 +254,12 @@ void ProcessRSICrossOverReversal(string symbol)
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"))
const double vol = United_NormalizeVolume(rcData.symbol, g_RC_LotSize);
if(vol > 0.0)
{
rcData.lastTradeTime = currentTime;
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
if(rcData.trade.Buy(vol, rcData.symbol, 0.0, 0.0, 0.0, "Buy Order"))
rcData.lastTradeTime = currentTime;
}
}
@@ -2,6 +2,11 @@
//| RSIMidPointHijackStrategy.mqh |
//+------------------------------------------------------------------+
double RM_NormalizedLot(const string sym)
{
return United_NormalizeVolume(sym, g_RM_LotSize);
}
bool IsNewBar(string symbol)
{
datetime time[];
@@ -111,7 +116,9 @@ void CheckRSIFollowStrategy(string symbol)
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Sell(vol, symbol, 0, 0, 0, "RSI Follow");
}
rmData.rsiOverbought = false;
}
@@ -120,7 +127,9 @@ void CheckRSIFollowStrategy(string symbol)
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Buy(vol, symbol, 0, 0, 0, "RSI Follow");
}
rmData.rsiOversold = false;
}
@@ -154,7 +163,9 @@ void CheckRSIReverseStrategy(string symbol)
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Sell(vol, symbol, 0, 0, 0, "RSI Reverse");
}
rmData.rsiReverseOverbought = false;
}
@@ -163,7 +174,9 @@ void CheckRSIReverseStrategy(string symbol)
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Buy(vol, symbol, 0, 0, 0, "RSI Reverse");
}
rmData.rsiReverseOversold = false;
}
@@ -223,7 +236,9 @@ void CheckEMACrossStrategy(string symbol)
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Buy(vol, symbol, 0, 0, 0, "EMA Cross Distance");
rmData.emaCrossBuySignal = false;
}
}
@@ -252,7 +267,9 @@ void CheckEMACrossStrategy(string symbol)
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Sell(vol, symbol, 0, 0, 0, "EMA Cross Distance");
rmData.emaCrossSellSignal = false;
}
}
@@ -265,7 +282,9 @@ void CheckEMACrossStrategy(string symbol)
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Buy(vol, symbol, 0, 0, 0, "EMA Cross");
}
}
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
@@ -273,7 +292,9 @@ void CheckEMACrossStrategy(string symbol)
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Sell(vol, symbol, 0, 0, 0, "EMA Cross");
}
}
}
@@ -65,19 +65,14 @@ bool IsAsianSession()
//+------------------------------------------------------------------+
bool IsTradingAllowed(RSIReversalAsianData& data)
{
// Check if market is open
long tradeMode = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_MODE);
if(tradeMode != SYMBOL_TRADE_MODE_FULL)
{
// Do not require SYMBOL_TRADE_MODE_FULL: many symbols allow one side only (long/short).
const long tradeMode = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_MODE);
if(tradeMode == SYMBOL_TRADE_MODE_DISABLED || tradeMode == SYMBOL_TRADE_MODE_CLOSEONLY)
return false;
}
// Check if we have enough money
if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0)
{
return false;
}
return true;
}
@@ -290,7 +285,7 @@ bool InitRSIReversalAsian(RSIReversalAsianData& data, string symbol,
// Set trade parameters
data.trade.SetExpertMagicNumber(MagicNumber);
data.trade.SetDeviationInPoints(Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
data.trade.SetTypeFillingBySymbol(symbol);
// Initialize state
data.isPositionOpen = false;
@@ -444,16 +439,16 @@ void ProcessRSIReversalAsian(RSIReversalAsianData& data, double lotSize)
if(data.UseTakeProfit && tp <= currentBid)
return;
// Set trade parameters
data.trade.SetDeviationInPoints(data.Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
data.trade.SetTypeFillingBySymbol(data.symbol);
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"))
const double vol = United_NormalizeVolume(data.symbol, tradeLotSize);
if(vol <= 0.0)
return;
if(data.trade.Buy(vol, data.symbol, currentAsk, sl, tp, "RSI Oversold Crossover Buy"))
{
data.isPositionOpen = true;
data.positionOpenPrice = currentAsk;
@@ -472,16 +467,16 @@ void ProcessRSIReversalAsian(RSIReversalAsianData& data, double lotSize)
if(data.UseTakeProfit && tp >= currentAsk)
return;
// Set trade parameters
data.trade.SetDeviationInPoints(data.Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
data.trade.SetTypeFillingBySymbol(data.symbol);
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"))
const double vol = United_NormalizeVolume(data.symbol, tradeLotSize);
if(vol <= 0.0)
return;
if(data.trade.Sell(vol, data.symbol, currentBid, sl, tp, "RSI Overbought Crossover Sell"))
{
data.isPositionOpen = true;
data.positionOpenPrice = currentBid;
@@ -120,6 +120,70 @@ void RS_TryReversalEscape(RSIScalpingData& data, const ENUM_TIMEFRAMES tf, const
" ATR=", DoubleToString(atr, (int)SymbolInfoInteger(data.symbol, SYMBOL_DIGITS)));
}
void RS_ApplyTrailingStop(RSIScalpingData& data, const int MagicNumber,
const bool useTrailingStop,
const double trailingStopDistancePoints,
const double trailingActivationPoints)
{
if(!useTrailingStop || trailingStopDistancePoints <= 0.0)
return;
if(!PositionSelectByMagic(data.symbol, (ulong)MagicNumber))
return;
const double point = SymbolInfoDouble(data.symbol, SYMBOL_POINT);
if(point <= 0.0)
return;
const int digits = (int)SymbolInfoInteger(data.symbol, SYMBOL_DIGITS);
const double trail_dist = trailingStopDistancePoints * point;
const double activation_pts = (trailingActivationPoints > 0.0)
? trailingActivationPoints
: trailingStopDistancePoints;
const double activation = activation_pts * point;
const long stops_level = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_STOPS_LEVEL);
const double min_dist = (double)stops_level * point;
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double cur_sl = PositionGetDouble(POSITION_SL);
const double cur_tp = PositionGetDouble(POSITION_TP);
if(ptype == POSITION_TYPE_BUY)
{
const double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
if(bid - entry <= activation)
return;
double new_sl = NormalizeDouble(bid - trail_dist, digits);
if(min_dist > 0.0 && bid - new_sl < min_dist)
new_sl = NormalizeDouble(bid - min_dist, digits);
if(new_sl >= bid || new_sl <= 0.0)
return;
if(cur_sl > 0.0 && new_sl <= cur_sl)
return;
ModifyPositionByMagic(data.trade, data.symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
else if(ptype == POSITION_TYPE_SELL)
{
const double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
if(entry - ask <= activation)
return;
double new_sl = NormalizeDouble(ask + trail_dist, digits);
if(min_dist > 0.0 && new_sl - ask < min_dist)
new_sl = NormalizeDouble(ask + min_dist, digits);
if(new_sl <= ask || new_sl <= 0.0)
return;
if(cur_sl > 0.0 && new_sl >= cur_sl)
return;
ModifyPositionByMagic(data.trade, data.symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
}
string ErrorDescription(int errorCode)
{
switch(errorCode)
@@ -364,21 +428,7 @@ void CheckEntrySignals(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int Mag
//+------------------------------------------------------------------+
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;
return United_NormalizeVolume(symbol, lotSize);
}
void OpenBuyPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
@@ -520,7 +570,8 @@ void ProcessRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES Ti
double RSI_Oversold, double RSI_Target_Buy, double RSI_Target_Sell,
int BarsToWait, double LotSize, int MagicNumber,
bool UseReversalEscape, int ReversalATRPeriod, double ReversalAdverseAtrMult,
int ReversalSignsRequired, double ReversalRsiVelocity, double ReversalBodyAtrMult)
int ReversalSignsRequired, double ReversalRsiVelocity, double ReversalBodyAtrMult,
bool UseTrailingStop, double TrailingStopDistancePoints, double TrailingActivationPoints)
{
// Skip if not initialized (symbol not available)
if(!data.isInitialized)
@@ -543,6 +594,10 @@ void ProcessRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES Ti
RS_TryReversalEscape(data, TimeFrame, MagicNumber, ReversalATRPeriod, ReversalAdverseAtrMult,
ReversalSignsRequired, ReversalRsiVelocity, ReversalBodyAtrMult);
if(in_pos)
RS_ApplyTrailingStop(data, MagicNumber, UseTrailingStop,
TrailingStopDistancePoints, TrailingActivationPoints);
if(!new_bar)
return;
@@ -0,0 +1,336 @@
//+------------------------------------------------------------------+
//| RSISecretSauceStrategy.mqh |
//| Cluster-0 orchestrator: RSI leave extreme then peak/bottom entry |
//+------------------------------------------------------------------+
#ifndef RSI_SECRET_SAUCE_STRATEGY_MQH
#define RSI_SECRET_SAUCE_STRATEGY_MQH
#include <Trade/Trade.mqh>
#include <Trade/PositionInfo.mqh>
struct RSISecretSauceOrcData
{
string actualSymbol;
bool isInitialized;
CTrade trade;
CPositionInfo positionInfo;
int rsiHandle;
int atrHandle;
double rsiBuffer[];
double atrBuffer[];
double highBuffer[];
double lowBuffer[];
bool rsiWasOverbought;
bool rsiWasOversold;
bool rsiBackInRange;
datetime lastRSIExitTime;
datetime lastRSIReentryTime;
datetime lastTradeTime;
datetime lastBarTime;
};
bool RSS_UpdateIndicators(RSISecretSauceOrcData &d)
{
int rsiBarsNeeded = RSS_RSILookback + 5;
if(CopyBuffer(d.rsiHandle, 0, 0, rsiBarsNeeded, d.rsiBuffer) < rsiBarsNeeded)
return false;
if(CopyBuffer(d.atrHandle, 0, 0, 2, d.atrBuffer) < 2)
return false;
if(CopyHigh(d.actualSymbol, RSS_Timeframe, 0, RSS_SwingLookback + 5, d.highBuffer) < RSS_SwingLookback + 5)
return false;
if(CopyLow(d.actualSymbol, RSS_Timeframe, 0, RSS_SwingLookback + 5, d.lowBuffer) < RSS_SwingLookback + 5)
return false;
return true;
}
void RSS_UpdateRSIState(RSISecretSauceOrcData &d)
{
double rsiCurrent = d.rsiBuffer[0];
double rsiPrev = d.rsiBuffer[1];
if(rsiPrev >= RSS_RSIOverbought && rsiCurrent < RSS_RSIOverbought)
{
d.rsiWasOverbought = true;
d.rsiBackInRange = true;
d.lastRSIExitTime = TimeCurrent();
d.lastRSIReentryTime = TimeCurrent();
}
if(rsiPrev <= RSS_RSIOversold && rsiCurrent > RSS_RSIOversold)
{
d.rsiWasOversold = true;
d.rsiBackInRange = true;
d.lastRSIExitTime = TimeCurrent();
d.lastRSIReentryTime = TimeCurrent();
}
if(rsiCurrent >= RSS_RSIOverbought)
{
d.rsiWasOverbought = false;
d.rsiBackInRange = false;
}
if(rsiCurrent <= RSS_RSIOversold)
{
d.rsiWasOversold = false;
d.rsiBackInRange = false;
}
}
bool RSS_IsRSIPeak(RSISecretSauceOrcData &d)
{
if(ArraySize(d.rsiBuffer) < RSS_PeakBars + 2)
return false;
double currentRSI = d.rsiBuffer[0];
bool isPeak = true;
for(int i = 1; i <= RSS_PeakBars; i++)
{
if(d.rsiBuffer[i] >= currentRSI)
{
isPeak = false;
break;
}
}
if(d.rsiBuffer[1] >= currentRSI)
isPeak = false;
return isPeak;
}
bool RSS_IsRSIBottom(RSISecretSauceOrcData &d)
{
if(ArraySize(d.rsiBuffer) < RSS_PeakBars + 2)
return false;
double currentRSI = d.rsiBuffer[0];
bool isBottom = true;
for(int i = 1; i <= RSS_PeakBars; i++)
{
if(d.rsiBuffer[i] <= currentRSI)
{
isBottom = false;
break;
}
}
if(d.rsiBuffer[1] <= currentRSI)
isBottom = false;
return isBottom;
}
double RSS_GetSwingStopLoss(RSISecretSauceOrcData &d, double currentPrice, ENUM_POSITION_TYPE type)
{
if(type == POSITION_TYPE_BUY)
{
double lowestLow = d.lowBuffer[0];
for(int i = 1; i < RSS_SwingLookback && i < ArraySize(d.lowBuffer); i++)
{
if(d.lowBuffer[i] < lowestLow)
lowestLow = d.lowBuffer[i];
}
return lowestLow;
}
double highestHigh = d.highBuffer[0];
for(int i = 1; i < RSS_SwingLookback && i < ArraySize(d.highBuffer); i++)
{
if(d.highBuffer[i] > highestHigh)
highestHigh = d.highBuffer[i];
}
return highestHigh;
}
bool RSS_CalculateStops(RSISecretSauceOrcData &d, double price, ENUM_POSITION_TYPE type, double &sl, double &tp)
{
double atrValue = d.atrBuffer[0];
if(atrValue <= 0)
atrValue = price * 0.01;
double slDistance = atrValue * RSS_StopLossATR;
double tpDistance = atrValue * RSS_TakeProfitATR;
int digits = (int)SymbolInfoInteger(d.actualSymbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(d.actualSymbol, SYMBOL_POINT);
int stopsLevel = (int)SymbolInfoInteger(d.actualSymbol, SYMBOL_TRADE_STOPS_LEVEL);
double minStopDistance = MathMax(stopsLevel * point, point * 10);
if(RSS_UseSwingStopLoss)
{
double swingStop = RSS_GetSwingStopLoss(d, 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;
}
}
}
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;
}
bool RSS_CanOpenNewPosition(RSISecretSauceOrcData &d)
{
int positionCount = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(d.positionInfo.SelectByIndex(i))
{
if(d.positionInfo.Symbol() == d.actualSymbol && d.positionInfo.Magic() == RSS_MagicNumber)
positionCount++;
}
}
if(positionCount >= RSS_MaxPositions)
return false;
if(d.lastTradeTime > 0)
{
int barsSince = Bars(d.actualSymbol, RSS_Timeframe, d.lastTradeTime, TimeCurrent());
if(barsSince < RSS_MinBarsBetweenTrades)
return false;
}
return true;
}
void RSS_OpenPosition(RSISecretSauceOrcData &d, ENUM_POSITION_TYPE type, const double lotSize)
{
double price = (type == POSITION_TYPE_BUY) ?
SymbolInfoDouble(d.actualSymbol, SYMBOL_ASK) :
SymbolInfoDouble(d.actualSymbol, SYMBOL_BID);
if(price <= 0)
return;
double sl = 0.0, tp = 0.0;
if(!RSS_CalculateStops(d, price, type, sl, tp))
return;
string comment = "RSI_Secret_" + (type == POSITION_TYPE_BUY ? "LONG" : "SHORT");
bool result = false;
if(type == POSITION_TYPE_BUY)
result = d.trade.Buy(lotSize, d.actualSymbol, 0, sl, tp, comment);
else
result = d.trade.Sell(lotSize, d.actualSymbol, 0, sl, tp, comment);
if(result)
{
d.lastTradeTime = TimeCurrent();
if(type == POSITION_TYPE_BUY)
d.rsiWasOverbought = false;
else
d.rsiWasOversold = false;
d.rsiBackInRange = false;
}
}
void RSS_CheckEntrySignals(RSISecretSauceOrcData &d, const double lotSize)
{
if(d.rsiWasOverbought && d.rsiBackInRange)
{
if(d.rsiBuffer[0] < RSS_RSIOverbought && RSS_IsRSIPeak(d))
RSS_OpenPosition(d, POSITION_TYPE_BUY, lotSize);
}
if(d.rsiWasOversold && d.rsiBackInRange)
{
if(d.rsiBuffer[0] > RSS_RSIOversold && RSS_IsRSIBottom(d))
RSS_OpenPosition(d, POSITION_TYPE_SELL, lotSize);
}
}
bool InitRSISecretSauce(RSISecretSauceOrcData &d, const string symbol)
{
d.isInitialized = false;
d.rsiHandle = INVALID_HANDLE;
d.atrHandle = INVALID_HANDLE;
d.rsiWasOverbought = false;
d.rsiWasOversold = false;
d.rsiBackInRange = false;
d.lastRSIExitTime = 0;
d.lastRSIReentryTime = 0;
d.lastTradeTime = 0;
d.lastBarTime = 0;
d.actualSymbol = symbol;
StringTrimLeft(d.actualSymbol);
StringTrimRight(d.actualSymbol);
if(StringLen(d.actualSymbol) == 0)
d.actualSymbol = _Symbol;
if(!SymbolSelect(d.actualSymbol, true))
{
Print("RSISecretSauce: symbol not available '", d.actualSymbol, "'");
return false;
}
d.rsiHandle = iRSI(d.actualSymbol, RSS_Timeframe, RSS_RSIPeriod, PRICE_CLOSE);
d.atrHandle = iATR(d.actualSymbol, RSS_Timeframe, RSS_ATRPeriod);
if(d.rsiHandle == INVALID_HANDLE || d.atrHandle == INVALID_HANDLE)
return false;
ArraySetAsSeries(d.rsiBuffer, true);
ArraySetAsSeries(d.atrBuffer, true);
ArraySetAsSeries(d.highBuffer, true);
ArraySetAsSeries(d.lowBuffer, true);
d.trade.SetExpertMagicNumber(RSS_MagicNumber);
d.trade.SetDeviationInPoints(RSS_Slippage);
d.trade.SetTypeFilling(ORDER_FILLING_FOK);
d.isInitialized = true;
return true;
}
void DeinitRSISecretSauce(RSISecretSauceOrcData &d)
{
if(d.rsiHandle != INVALID_HANDLE)
IndicatorRelease(d.rsiHandle);
if(d.atrHandle != INVALID_HANDLE)
IndicatorRelease(d.atrHandle);
d.rsiHandle = INVALID_HANDLE;
d.atrHandle = INVALID_HANDLE;
d.isInitialized = false;
}
void ProcessRSISecretSauce(RSISecretSauceOrcData &d, const double lotSize)
{
if(!d.isInitialized)
return;
int requiredBars = MathMax(RSS_RSILookback, RSS_SwingLookback) + 10;
if(Bars(d.actualSymbol, RSS_Timeframe) < requiredBars)
return;
datetime currentBarTime = iTime(d.actualSymbol, RSS_Timeframe, 0);
if(currentBarTime == d.lastBarTime)
return;
d.lastBarTime = currentBarTime;
if(!RSS_UpdateIndicators(d))
return;
RSS_UpdateRSIState(d);
if(RSS_CanOpenNewPosition(d))
RSS_CheckEntrySignals(d, lotSize);
}
#endif
@@ -0,0 +1,318 @@
//+------------------------------------------------------------------+
//| SimpleTrendlineStrategy.mqh |
//+------------------------------------------------------------------+
#ifndef SIMPLE_TRENDLINE_STRATEGY_MQH
#define SIMPLE_TRENDLINE_STRATEGY_MQH
struct SimpleTrendlineModel
{
datetime t1;
datetime t2;
datetime t3;
double a;
double b;
bool valid;
};
struct SimpleTrendlineData
{
string symbol;
bool isInitialized;
CTrade trade;
ENUM_TIMEFRAMES signalTF;
ENUM_TIMEFRAMES higherTF;
int maPeriod;
ENUM_MA_METHOD maMethod;
ENUM_APPLIED_PRICE appliedPrice;
int htfBarsToScan;
double touchTolerancePoints;
double breakBufferPoints;
ulong magic;
bool drawTrendline;
int maHandle;
datetime lastSignalBarTime;
string lineName;
};
double ST_NormalizeVolume(const string sym, double vol)
{
double minLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP);
if(step > 0.0)
vol = MathFloor(vol / step) * step;
if(vol < minLot)
vol = minLot;
if(vol > maxLot)
vol = maxLot;
return vol;
}
bool ST_GetPosition(const string sym, const ulong magic, ENUM_POSITION_TYPE &type, double &volume)
{
if(!PositionSelectByMagic(sym, magic))
return false;
type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
volume = PositionGetDouble(POSITION_VOLUME);
return true;
}
int ST_FindRecentCrossPoints(SimpleTrendlineData &d, datetime &times[], double &prices[])
{
ArrayResize(times, 0);
ArrayResize(prices, 0);
if(d.maHandle == INVALID_HANDLE)
return 0;
int needBars = MathMax(d.htfBarsToScan, d.maPeriod + 20);
MqlRates rates[];
double maBuf[];
ArraySetAsSeries(rates, true);
ArraySetAsSeries(maBuf, true);
int copiedRates = CopyRates(d.symbol, d.higherTF, 0, needBars, rates);
int copiedMa = CopyBuffer(d.maHandle, 0, 0, needBars, maBuf);
if(copiedRates <= 5 || copiedMa <= 5)
return 0;
int bars = MathMin(copiedRates, copiedMa);
for(int i = 2; i < bars - 1; i++)
{
double d0 = rates[i].close - maBuf[i];
double d1 = rates[i + 1].close - maBuf[i + 1];
if(d0 == 0.0 || d1 == 0.0 || (d0 * d1 < 0.0))
{
int n = ArraySize(times);
ArrayResize(times, n + 1);
ArrayResize(prices, n + 1);
times[n] = rates[i].time;
prices[n] = rates[i].close;
if(ArraySize(times) >= 3)
break;
}
}
return ArraySize(times);
}
bool ST_BuildTrendline(SimpleTrendlineData &d, SimpleTrendlineModel &m)
{
m.valid = false;
datetime ts[];
double ps[];
if(ST_FindRecentCrossPoints(d, ts, ps) < 3)
return false;
datetime tOld[3];
double pOld[3];
for(int i = 0; i < 3; i++)
{
tOld[i] = ts[2 - i];
pOld[i] = ps[2 - i];
}
long t0 = (long)tOld[0];
double x1 = 0.0;
double x2 = (double)((long)tOld[1] - t0);
double x3 = (double)((long)tOld[2] - t0);
double y1 = pOld[0];
double y2 = pOld[1];
double y3 = pOld[2];
double sx = x1 + x2 + x3;
double sy = y1 + y2 + y3;
double sxx = x1 * x1 + x2 * x2 + x3 * x3;
double sxy = x1 * y1 + x2 * y2 + x3 * y3;
double den = 3.0 * sxx - sx * sx;
if(MathAbs(den) < 1e-10)
return false;
m.a = (3.0 * sxy - sx * sy) / den;
m.b = (sy - m.a * sx) / 3.0;
m.t1 = tOld[0];
m.t2 = tOld[1];
m.t3 = tOld[2];
m.valid = true;
return true;
}
double ST_LinePriceAt(const SimpleTrendlineModel &m, const datetime t)
{
if(!m.valid)
return 0.0;
double x = (double)((long)t - (long)m.t1);
return m.a * x + m.b;
}
void ST_DrawTrendline(SimpleTrendlineData &d, const SimpleTrendlineModel &m)
{
if(!d.drawTrendline || !m.valid || d.symbol != _Symbol)
return;
datetime tStart = m.t1;
datetime tEnd = iTime(d.symbol, d.signalTF, 0);
if(tEnd <= tStart)
tEnd = m.t3 + PeriodSeconds(d.signalTF) * 20;
double pStart = ST_LinePriceAt(m, tStart);
double pEnd = ST_LinePriceAt(m, tEnd);
if(ObjectFind(0, d.lineName) < 0)
ObjectCreate(0, d.lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd);
else
{
ObjectMove(0, d.lineName, 0, tStart, pStart);
ObjectMove(0, d.lineName, 1, tEnd, pEnd);
}
ObjectSetInteger(0, d.lineName, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, d.lineName, OBJPROP_COLOR, clrGold);
ObjectSetInteger(0, d.lineName, OBJPROP_WIDTH, 2);
}
void ST_TryExitOnBreak(SimpleTrendlineData &d, const SimpleTrendlineModel &m)
{
ENUM_POSITION_TYPE posType;
double vol;
if(!ST_GetPosition(d.symbol, d.magic, posType, vol))
return;
double close1 = iClose(d.symbol, d.signalTF, 1);
datetime t1 = iTime(d.symbol, d.signalTF, 1);
double line1 = ST_LinePriceAt(m, t1);
double buf = d.breakBufferPoints * SymbolInfoDouble(d.symbol, SYMBOL_POINT);
bool closePos = false;
if(posType == POSITION_TYPE_BUY && close1 < (line1 - buf))
closePos = true;
if(posType == POSITION_TYPE_SELL && close1 > (line1 + buf))
closePos = true;
if(closePos)
ClosePositionByMagic(d.trade, d.symbol, d.magic);
}
void ST_TryPullbackEntry(SimpleTrendlineData &d, const SimpleTrendlineModel &m, const double lots)
{
if(PositionExistsByMagic(d.symbol, d.magic))
return;
MqlRates b1[], b2[];
ArraySetAsSeries(b1, true);
ArraySetAsSeries(b2, true);
if(CopyRates(d.symbol, d.signalTF, 1, 1, b1) != 1)
return;
if(CopyRates(d.symbol, d.signalTF, 2, 1, b2) != 1)
return;
if(ArraySize(b1) < 1 || ArraySize(b2) < 1)
return;
double line1 = ST_LinePriceAt(m, b1[0].time);
double tol = d.touchTolerancePoints * SymbolInfoDouble(d.symbol, SYMBOL_POINT);
bool upTrend = (m.a > 0.0);
bool downTrend = (m.a < 0.0);
double vol = ST_NormalizeVolume(d.symbol, lots);
if(upTrend)
{
bool touched = (b1[0].low <= (line1 + tol));
bool reclaim = (b1[0].close > line1);
bool bullish = (b1[0].close > b1[0].open);
bool stillHealthy = (b2[0].close >= ST_LinePriceAt(m, b2[0].time) - tol);
if(touched && reclaim && bullish && stillHealthy)
{
if(!d.trade.Buy(vol, d.symbol, 0.0, 0.0, 0.0, "SimpleTrendline BUY"))
Print("SimpleTrendline BUY failed [", d.symbol, "] retcode=", d.trade.ResultRetcode(), " ", d.trade.ResultRetcodeDescription());
}
}
else if(downTrend)
{
bool touched = (b1[0].high >= (line1 - tol));
bool reject = (b1[0].close < line1);
bool bearish = (b1[0].close < b1[0].open);
bool stillWeak = (b2[0].close <= ST_LinePriceAt(m, b2[0].time) + tol);
if(touched && reject && bearish && stillWeak)
{
if(!d.trade.Sell(vol, d.symbol, 0.0, 0.0, 0.0, "SimpleTrendline SELL"))
Print("SimpleTrendline SELL failed [", d.symbol, "] retcode=", d.trade.ResultRetcode(), " ", d.trade.ResultRetcodeDescription());
}
}
}
bool InitSimpleTrendline(SimpleTrendlineData &d,
const string symbol,
const ENUM_TIMEFRAMES signalTF,
const ENUM_TIMEFRAMES higherTF,
const int maPeriod,
const ENUM_MA_METHOD maMethod,
const ENUM_APPLIED_PRICE appliedPrice,
const int htfBarsToScan,
const double touchTolerancePoints,
const double breakBufferPoints,
const ulong magic,
const bool drawTrendline)
{
d.isInitialized = false;
d.symbol = symbol;
StringTrimLeft(d.symbol);
StringTrimRight(d.symbol);
if(StringLen(d.symbol) == 0)
d.symbol = _Symbol;
if(!SymbolSelect(d.symbol, true))
return false;
d.signalTF = signalTF;
d.higherTF = higherTF;
d.maPeriod = maPeriod;
d.maMethod = maMethod;
d.appliedPrice = appliedPrice;
d.htfBarsToScan = htfBarsToScan;
d.touchTolerancePoints = touchTolerancePoints;
d.breakBufferPoints = breakBufferPoints;
d.magic = magic;
d.drawTrendline = drawTrendline;
d.lastSignalBarTime = 0;
d.lineName = "SimpleTrendline_" + d.symbol + "_" + IntegerToString((int)d.magic);
d.trade.SetExpertMagicNumber((long)d.magic);
d.trade.SetTypeFillingBySymbol(d.symbol);
d.trade.SetDeviationInPoints(20);
d.maHandle = iMA(d.symbol, d.higherTF, d.maPeriod, 0, d.maMethod, d.appliedPrice);
if(d.maHandle == INVALID_HANDLE)
return false;
d.isInitialized = true;
return true;
}
void DeinitSimpleTrendline(SimpleTrendlineData &d)
{
if(d.maHandle != INVALID_HANDLE)
IndicatorRelease(d.maHandle);
d.maHandle = INVALID_HANDLE;
if(ObjectFind(0, d.lineName) >= 0)
ObjectDelete(0, d.lineName);
d.isInitialized = false;
}
void ProcessSimpleTrendline(SimpleTrendlineData &d, const double lots)
{
if(!d.isInitialized)
return;
datetime bar0 = iTime(d.symbol, d.signalTF, 0);
if(bar0 == 0 || bar0 == d.lastSignalBarTime)
return;
d.lastSignalBarTime = bar0;
SimpleTrendlineModel m;
if(!ST_BuildTrendline(d, m))
return;
ST_DrawTrendline(d, m);
ST_TryExitOnBreak(d, m);
ST_TryPullbackEntry(d, m, lots);
}
#endif // SIMPLE_TRENDLINE_STRATEGY_MQH
@@ -51,6 +51,12 @@ void SuperEMA_Log(SuperEMAData &d, const string s)
Print("[SuperEMA] ", s);
}
double SuperEMA_Point(const SuperEMAData &d)
{
double pt = SymbolInfoDouble(d.symbol, SYMBOL_POINT);
return (pt > 0.0 ? pt : _Point);
}
bool SuperEMA_IsNewBar(SuperEMAData &d)
{
datetime t = iTime(d.symbol, d.tf, 0);
@@ -176,7 +182,8 @@ bool SuperEMA_PullbackNearFastEmaLong(SuperEMAData &d)
double lo = iLow(d.symbol, d.tf, 1);
if(emaF <= 0.0)
return false;
return (lo <= emaF + d.slBufferPoints * _Point * 3.0);
const double pt = SuperEMA_Point(d);
return (lo <= emaF + d.slBufferPoints * pt * 3.0);
}
bool SuperEMA_PullbackNearFastEmaShort(SuperEMAData &d)
@@ -185,7 +192,8 @@ bool SuperEMA_PullbackNearFastEmaShort(SuperEMAData &d)
double hi = iHigh(d.symbol, d.tf, 1);
if(emaF <= 0.0)
return false;
return (hi >= emaF - d.slBufferPoints * _Point * 3.0);
const double pt = SuperEMA_Point(d);
return (hi >= emaF - d.slBufferPoints * pt * 3.0);
}
int SuperEMA_PositionsByMagic(SuperEMAData &d)
@@ -196,6 +204,8 @@ int SuperEMA_PositionsByMagic(SuperEMAData &d)
ulong t = PositionGetTicket(i);
if(t == 0)
continue;
if(!PositionSelectByTicket(t))
continue;
if(PositionGetString(POSITION_SYMBOL) == d.symbol && (int)PositionGetInteger(POSITION_MAGIC) == d.magic)
n++;
}
@@ -209,7 +219,7 @@ void SuperEMA_ComputeSLTP(SuperEMAData &d, const bool isBuy, double &sl, double
if(!d.useStructuralSL)
return;
double emaM = SuperEMA_EmaAt(d, d.emaMid, d.emaTrendBars);
double buf = d.slBufferPoints * _Point;
double buf = d.slBufferPoints * SuperEMA_Point(d);
if(isBuy)
sl = emaM - buf;
else
@@ -418,9 +428,13 @@ void ProcessSuperEMA(SuperEMAData &d, const double lots)
SuperEMA_ManageExits(d);
// Same order as standalone SuperEMAXAUUSD: skip entry logic when flat is not allowed.
if(d.oneTradeOnly && SuperEMA_PositionsByMagic(d) > 0)
return;
const int sh = d.emaTrendBars;
double h1 = 0.0, h2 = 0.0;
if(!SuperEMA_MacdHistAt(d, 1, h1) || !SuperEMA_MacdHistAt(d, 2, h2))
double h1 = 0.0;
if(!SuperEMA_MacdHistAt(d, 1, h1))
return;
bool up = SuperEMA_TrendUp(d, sh);
@@ -453,14 +467,14 @@ void ProcessSuperEMA(SuperEMAData &d, const double lots)
break;
}
if(d.oneTradeOnly && SuperEMA_PositionsByMagic(d) > 0)
if(!wantBuy && !wantSell)
return;
const double vol = United_NormalizeVolume(d.symbol, lots);
if(vol <= 0.0)
{
if(wantBuy && !United_MayOpenNewEntry(d.symbol, (ulong)d.magic, true))
wantBuy = false;
if(wantSell && !United_MayOpenNewEntry(d.symbol, (ulong)d.magic, false))
wantSell = false;
if(!wantBuy && !wantSell)
return;
SuperEMA_Log(d, "Skip entry: normalized volume <= 0");
return;
}
MqlTick tick;
@@ -474,7 +488,7 @@ void ProcessSuperEMA(SuperEMAData &d, const double lots)
#ifndef UNITED_MARTINGALE_NO_SELF_CLOSE
SuperEMA_ComputeSLTP(d, true, sl, tp);
#endif
if(d.trade.Buy(lots, d.symbol, tick.ask, sl, tp, "United SuperEMA long"))
if(d.trade.Buy(vol, d.symbol, tick.ask, sl, tp, "United SuperEMA long"))
SuperEMA_Log(d, StringFormat("BUY ask=%.5f sl=%.5f", tick.ask, sl));
}
else if(wantSell && !wantBuy)
@@ -482,7 +496,7 @@ void ProcessSuperEMA(SuperEMAData &d, const double lots)
#ifndef UNITED_MARTINGALE_NO_SELF_CLOSE
SuperEMA_ComputeSLTP(d, false, sl, tp);
#endif
if(d.trade.Sell(lots, d.symbol, tick.bid, sl, tp, "United SuperEMA short"))
if(d.trade.Sell(vol, d.symbol, tick.bid, sl, tp, "United SuperEMA short"))
SuperEMA_Log(d, StringFormat("SELL bid=%.5f sl=%.5f", tick.bid, sl));
}
}
@@ -5,14 +5,17 @@
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#property version "1.20"
#property strict
#property description "LOT_* nominal at ORCH_ReferenceBalance; scale = balance/equity ÷ reference (clamped). No performance-evaluator ranking."
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Indicators\Trend.mqh>
#include <Indicators\Volumes.mqh>
#include "MagicNumberHelpers.mqh"
#define UNITED_V2_DYNAMIC_LOTS
double g_DB_LotSize;
// Include strategy implementations early so structs are available
#include "Strategies/DarvasBoxStrategy.mqh"
#include "Strategies/EMASlopeDistanceStrategy.mqh"
@@ -23,6 +26,7 @@
#include "Strategies/RSIReversalAsianStrategy.mqh"
#include "Strategies/RSIConsolidationStrategy.mqh"
#include "Strategies/SimpleTrendlineStrategy.mqh"
#include "Strategies/RSISecretSauceStrategy.mqh"
//+------------------------------------------------------------------+
//| Global Lot Size Variables (for dynamic lot sizing) |
@@ -31,6 +35,19 @@ double g_ES_LotSize; // EMA Slope Distance lot size
double g_RC_LotSize; // RSI CrossOver Reversal lot size
double g_RM_LotSize; // RSI MidPoint Hijack lot size
double g_Pos_RS_APPL;
double g_Pos_RS_BTCUSD;
double g_Pos_RS_NVDA;
double g_Pos_RS_TSLA;
double g_Pos_RS_XAUUSD;
double g_Pos_RRA_EURUSD;
double g_Pos_RRA_AUDUSD;
double g_Pos_SE;
double g_Pos_RCO;
double g_Pos_ST_BTCUSD;
double g_Pos_ST_XAUUSD;
double g_RSS_LotSize;
bool United_MayOpenNewEntry(const string symbol, const ulong magic, const bool isBuy)
{
if(PositionExistsByMagic(symbol, magic))
@@ -45,47 +62,57 @@ 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 EnableRSIMidPointHijack = false;
input bool EnableRSIScalpingAPPL = false;
input bool EnableRSIScalpingBTCUSD = false;
input bool EnableRSIScalpingNVDA = false;
input bool EnableRSIScalpingTSLA = false;
input bool EnableRSIScalpingXAUUSD = true;
input bool EnableSuperEMA = true;
input bool EnableRSIConsolidation = true;
input bool EnableRSIReversalAsianEURUSD = true;
input bool EnableRSIReversalAsianAUDUSD = true;
input bool EnableSimpleTrendlineBTCUSD = true;
input bool EnableRSIReversalAsianEURUSD = false;
input bool EnableRSIReversalAsianAUDUSD = false;
input bool EnableSimpleTrendlineBTCUSD = false;
input bool EnableSimpleTrendlineXAUUSD = true;
input bool EnableRSISecretSauce = false;
input group "=== Centralized Lot Size (Granular Per Robot) ==="
input double LOT_ES_EMASlopeDistance = 0.05;
input double LOT_DB_DarvasBox = 0.04;
input double LOT_ES_EMASlopeDistance = 0.09;
input double LOT_RC_RSICrossOver = 0.1;
input double LOT_RM_RSIMidPointHijack = 0.01;
input double LOT_RS_APPL = 100.0;
input double LOT_RS_BTCUSD = 0.15;
input double LOT_RS_NVDA = 60.0;
input double LOT_RS_TSLA = 20.0;
input double LOT_RS_XAUUSD = 0.02;
input double LOT_RRA_EURUSD = 0.01;
input double LOT_RRA_AUDUSD = 0.10;
input double LOT_SE_SuperEMA = 0.01;
input double LOT_RCO_RSIConsolidation = 0.04;
input double LOT_ST_BTCUSD = 0.19;
input double LOT_RS_APPL = 5.0;
input double LOT_RS_BTCUSD = 0.1;
input double LOT_RS_NVDA = 10.0;
input double LOT_RS_TSLA = 15.0;
input double LOT_RS_XAUUSD = 0.05;
input double LOT_RRA_EURUSD = 0.05;
input double LOT_RRA_AUDUSD = 0.08;
input double LOT_SE_SuperEMA = 0.02;
input double LOT_RCO_RSIConsolidation = 0.02;
input double LOT_ST_BTCUSD = 0.07;
input double LOT_ST_XAUUSD = 0.02;
input double LOT_RSS_SecretSauce = 0.01;
input group "=== Balance-based position sizing ==="
input bool ORCH_ScaleLotsByBalance = true;
input bool ORCH_UseEquityInsteadOfBalance = false;
input double ORCH_ReferenceBalance = 10000.0;
input double ORCH_MinBalanceScale = 0.1;
input double ORCH_MaxBalanceScale = 10.0;
//+------------------------------------------------------------------+
//| Strategy 1: DarvasBoxXAUUSD |
//+------------------------------------------------------------------+
input group "=== DarvasBox Strategy ==="
input string DB_Symbol = "XAUUSD";
input string DB_Symbol = "XAU";
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 color DB_BoxColor = (color)16711680;
input int DB_BoxWidth = 1;
input ENUM_TIMEFRAMES DB_TrendTimeframe = PERIOD_H2;
input int DB_MA_Period = 125;
@@ -94,6 +121,8 @@ 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 bool DB_UseVolumeSpikeFilter = true;
input bool DB_UseTrendFilter = true;
input int DB_MagicNumber = 135790;
//+------------------------------------------------------------------+
@@ -101,12 +130,16 @@ input int DB_MagicNumber = 135790;
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
//+------------------------------------------------------------------+
input group "=== EMA Slope Distance Strategy ==="
input string ES_Symbol = "XAUUSD";
input string ES_Symbol = "XAU";
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_TrailingStop = 370.0;
input bool ES_UseTrailingStop = true;
input double ES_TrailingActivationPips = 0.0;
input bool ES_UseStaleStopLossExit = false;
input int ES_StaleStopLossSeconds = 33800;
input double ES_LotGröße = 0.03;
input int ES_MagicNumber = 12350;
input bool ES_UseSpreadAdjustment = true;
@@ -115,13 +148,18 @@ input bool ES_UseBarData = true;
input int ES_MaxTradesPerCrossover = 9;
input int ES_ProfitCheckBars = 18;
input bool ES_CloseUnprofitableTrades = true;
input bool ES_UseWeeklyADXFilter = true;
input int ES_WeeklyADXPeriod = 15;
input double ES_WeeklyADXMin = 40.0;
input int ES_WeeklyADXBarShift = 2;
input bool ES_WeeklyADXUseDirection = 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 string RC_Symbol = "XAU";
input int RC_MagicNumber = 7;
input int RC_rsiPeriod = 19;
input int RC_overboughtLevel = 93;
@@ -140,6 +178,7 @@ input double RC_exitBuyRSI = 86;
input double RC_exitSellRSI = 10;
input double RC_TrailingStop = 295;
input double RC_emaDistanceThreshold = 165;
input bool RC_UseTrendStrengthFilter = true;
input int RC_tradingHourOneBegin = 24;
input int RC_tradingHourOneEnd = 22;
input int RC_tradingHourTwoBegin = 6;
@@ -157,7 +196,7 @@ input bool RC_Saturday = false;
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
//+------------------------------------------------------------------+
input group "=== RSI MidPoint Hijack Strategy ==="
input string RM_Symbol = "XAUUSD";
input string RM_Symbol = "XAU";
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
input double RM_InpLotSize = 0.02;
input int RM_InpMagicNumberRSIFollow = 1001;
@@ -212,7 +251,7 @@ input int RM_InpEMADistancePeriod = 26;
//| 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 string RS_APPL_Symbol = "AAPL.NAS"; // Pepperstone / match tester set (also try AAPL.US)
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;
@@ -240,7 +279,7 @@ 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 string RS_NVDA_Symbol = "NVDA.NAS"; // Pepperstone / match tester set (also try NVDA.US)
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;
@@ -254,7 +293,7 @@ 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 string RS_TSLA_Symbol = "TSLA.NAS"; // Pepperstone / match tester set (also try TSLA.US)
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;
@@ -268,7 +307,7 @@ input int RS_TSLA_MagicNumber = 125421321;
input int RS_TSLA_Slippage = 3;
input group "=== RSI Scalping XAUUSD ==="
input string RS_XAUUSD_Symbol = "XAUUSD";
input string RS_XAUUSD_Symbol = "XAU";
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;
@@ -289,6 +328,31 @@ input int RS_ReversalSignsRequired = 2;
input double RS_ReversalRsiVelocity = 16.0;
input double RS_ReversalBodyAtrMult = 5.1;
input group "=== RSI Scalping APPL — Trailing (cluster-fuck BTC-style defaults) ==="
input bool RS_APPL_UseTrailingStop = true;
input double RS_APPL_TrailDistancePoints = 120.0;
input double RS_APPL_TrailActivationPoints = 0.0;
input group "=== RSI Scalping BTCUSD — Trailing ==="
input bool RS_BTCUSD_UseTrailingStop = true;
input double RS_BTCUSD_TrailDistancePoints = 120.0;
input double RS_BTCUSD_TrailActivationPoints = 0.0;
input group "=== RSI Scalping NVDA — Trailing ==="
input bool RS_NVDA_UseTrailingStop = true;
input double RS_NVDA_TrailDistancePoints = 375.0;
input double RS_NVDA_TrailActivationPoints = 75.0;
input group "=== RSI Scalping TSLA — Trailing ==="
input bool RS_TSLA_UseTrailingStop = true;
input double RS_TSLA_TrailDistancePoints = 900.0;
input double RS_TSLA_TrailActivationPoints = 950.0;
input group "=== RSI Scalping XAUUSD — Trailing ==="
input bool RS_XAUUSD_UseTrailingStop = true;
input double RS_XAUUSD_TrailDistancePoints = 1000.0;
input double RS_XAUUSD_TrailActivationPoints = 550.0;
//+------------------------------------------------------------------+
//| Strategy 11-12: RSI Reversal Asian Strategies |
//| Each RSI Reversal Asian strategy trades on its own symbol: |
@@ -334,7 +398,7 @@ input int RRA_AUDUSD_MagicNumber = 30002;
input int RRA_AUDUSD_Slippage = 3;
input group "=== SuperEMA (EMA + CCI + MACD) ==="
input string SE_Symbol = "XAUUSD";
input string SE_Symbol = "XAU";
input ENUM_TIMEFRAMES SE_Timeframe = PERIOD_M15;
input double SE_LotSize = 0.01;
input int SE_SlippagePoints = 55;
@@ -362,7 +426,7 @@ input bool SE_ExitBelowMidEma = false;
input bool SE_DebugLogs = false;
input group "=== RSI Consolidation (ranging / mean-reversion) ==="
input string RCO_Symbol = "XAUUSD";
input string RCO_Symbol = "XAU";
input ENUM_TIMEFRAMES RCO_SignalTF = PERIOD_M15;
input bool RCO_EntryOnNewBarOnly = true;
input int RCO_ADX_Period = 23;
@@ -404,7 +468,7 @@ input ulong ST_BTC_MagicNumber = 26042501;
input bool ST_BTC_DrawTrendline = true;
input group "=== SimpleTrendline XAUUSD ==="
input string ST_XAU_Symbol = "XAUUSD";
input string ST_XAU_Symbol = "XAU";
input ENUM_TIMEFRAMES ST_XAU_SignalTF = PERIOD_H1;
input ENUM_TIMEFRAMES ST_XAU_HigherTF = PERIOD_M10;
input int ST_XAU_MAPeriod = 65;
@@ -416,6 +480,68 @@ input double ST_XAU_BreakBuffer = 110.0;
input ulong ST_XAU_MagicNumber = 26042503;
input bool ST_XAU_DrawTrendline = true;
input group "=== RSI Secret Sauce XAUUSD ==="
input string RSS_Symbol = "XAU";
input int RSS_MagicNumber = 789012;
input int RSS_Slippage = 10;
input ENUM_TIMEFRAMES RSS_Timeframe = PERIOD_M30;
input int RSS_RSIPeriod = 16;
input double RSS_RSIOverbought = 72.5;
input double RSS_RSIOversold = 32.5;
input int RSS_RSILookback = 60;
input int RSS_PeakBars = 2;
input double RSS_StopLossATR = 2.75;
input double RSS_TakeProfitATR = 5.0;
input int RSS_ATRPeriod = 14;
input bool RSS_UseSwingStopLoss = false;
input int RSS_SwingLookback = 30;
input int RSS_MaxPositions = 1;
input int RSS_MinBarsBetweenTrades = 7;
//+------------------------------------------------------------------+
//| Balance scaling: LOT_* = nominal size at ORCH_ReferenceBalance |
//+------------------------------------------------------------------+
double United_BalanceScaleFactor()
{
if(!ORCH_ScaleLotsByBalance || ORCH_ReferenceBalance <= 0.0)
return 1.0;
const double money = ORCH_UseEquityInsteadOfBalance
? AccountInfoDouble(ACCOUNT_EQUITY)
: AccountInfoDouble(ACCOUNT_BALANCE);
double raw = money / ORCH_ReferenceBalance;
if(raw < ORCH_MinBalanceScale)
raw = ORCH_MinBalanceScale;
if(raw > ORCH_MaxBalanceScale)
raw = ORCH_MaxBalanceScale;
return raw;
}
double United_ScaledLot(const double baseLot)
{
const double lot = baseLot * United_BalanceScaleFactor();
return (lot > 0.0 ? lot : 0.0);
}
void United_RefreshScaledLots()
{
g_DB_LotSize = United_ScaledLot(LOT_DB_DarvasBox);
g_ES_LotSize = United_ScaledLot(LOT_ES_EMASlopeDistance);
g_RC_LotSize = United_ScaledLot(LOT_RC_RSICrossOver);
g_RM_LotSize = United_ScaledLot(LOT_RM_RSIMidPointHijack);
g_Pos_RS_APPL = United_ScaledLot(LOT_RS_APPL);
g_Pos_RS_BTCUSD = United_ScaledLot(LOT_RS_BTCUSD);
g_Pos_RS_NVDA = United_ScaledLot(LOT_RS_NVDA);
g_Pos_RS_TSLA = United_ScaledLot(LOT_RS_TSLA);
g_Pos_RS_XAUUSD = United_ScaledLot(LOT_RS_XAUUSD);
g_Pos_RRA_EURUSD = United_ScaledLot(LOT_RRA_EURUSD);
g_Pos_RRA_AUDUSD = United_ScaledLot(LOT_RRA_AUDUSD);
g_Pos_SE = United_ScaledLot(LOT_SE_SuperEMA);
g_Pos_RCO = United_ScaledLot(LOT_RCO_RSIConsolidation);
g_Pos_ST_BTCUSD = United_ScaledLot(LOT_ST_BTCUSD);
g_Pos_ST_XAUUSD = United_ScaledLot(LOT_ST_XAUUSD);
g_RSS_LotSize = United_ScaledLot(LOT_RSS_SecretSauce);
}
//+------------------------------------------------------------------+
//| Global Variables - DarvasBox |
//+------------------------------------------------------------------+
@@ -453,6 +579,7 @@ struct EMASlopeData {
bool crossover_detected;
datetime trade_open_time;
datetime last_bar_time;
datetime es_last_sl_adjust_success_time;
};
//+------------------------------------------------------------------+
@@ -516,6 +643,7 @@ SuperEMAData seData;
RSIConsolidationData rcoData;
SimpleTrendlineData stBTCData;
SimpleTrendlineData stXAUData;
RSISecretSauceOrcData rssData;
//+------------------------------------------------------------------+
//| Global Variables - RSI Reversal Asian |
@@ -530,10 +658,7 @@ int OnInit()
{
int initResult = INIT_SUCCEEDED;
// Initialize global lot size variables
g_ES_LotSize = LOT_ES_EMASlopeDistance;
g_RC_LotSize = LOT_RC_RSICrossOver;
g_RM_LotSize = LOT_RM_RSIMidPointHijack;
United_RefreshScaledLots();
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
if(EnableDarvasBox)
@@ -568,6 +693,10 @@ int OnInit()
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);
if(EnableRSISecretSauce)
if(!InitRSISecretSauce(rssData, RSS_Symbol))
Print("Warning: RSI Secret Sauce failed to initialize for symbol '", RSS_Symbol, "'");
if(EnableSuperEMA)
if(!InitSuperEMA(seData, SE_Symbol, SE_Timeframe, SE_SlippagePoints, SE_MagicNumber,
SE_EmaFast, SE_EmaMid, SE_EmaSlow, SE_EmaTrendBars,
@@ -626,6 +755,7 @@ int OnInit()
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""),
(EnableRSISecretSauce ? "RSISecretSauce " : ""),
(EnableSuperEMA ? "SuperEMA " : ""),
(EnableRSIConsolidation ? "RSIConsolidation " : ""),
(EnableRSIReversalAsianEURUSD ? "RSIReversalAsianEURUSD " : ""),
@@ -668,6 +798,9 @@ void OnDeinit(const int reason)
if(EnableRSIScalpingXAUUSD)
DeinitRSIScalping(rsXAUUSDData);
if(EnableRSISecretSauce)
DeinitRSISecretSauce(rssData);
if(EnableSuperEMA)
DeinitSuperEMA(seData);
@@ -693,6 +826,8 @@ void OnDeinit(const int reason)
//+------------------------------------------------------------------+
void OnTick()
{
United_RefreshScaledLots();
if(EnableDarvasBox)
ProcessDarvasBox(DB_Symbol);
@@ -708,54 +843,62 @@ void OnTick()
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, LOT_RS_APPL, RS_APPL_MagicNumber,
RS_APPL_BarsToWait, g_Pos_RS_APPL, RS_APPL_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_APPL_UseTrailingStop, RS_APPL_TrailDistancePoints, RS_APPL_TrailActivationPoints);
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, LOT_RS_BTCUSD, RS_BTCUSD_MagicNumber,
RS_BTCUSD_BarsToWait, g_Pos_RS_BTCUSD, RS_BTCUSD_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_BTCUSD_UseTrailingStop, RS_BTCUSD_TrailDistancePoints, RS_BTCUSD_TrailActivationPoints);
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, LOT_RS_NVDA, RS_NVDA_MagicNumber,
RS_NVDA_BarsToWait, g_Pos_RS_NVDA, RS_NVDA_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_NVDA_UseTrailingStop, RS_NVDA_TrailDistancePoints, RS_NVDA_TrailActivationPoints);
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, LOT_RS_TSLA, RS_TSLA_MagicNumber,
RS_TSLA_BarsToWait, g_Pos_RS_TSLA, RS_TSLA_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_TSLA_UseTrailingStop, RS_TSLA_TrailDistancePoints, RS_TSLA_TrailActivationPoints);
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, LOT_RS_XAUUSD, RS_XAUUSD_MagicNumber,
RS_XAUUSD_BarsToWait, g_Pos_RS_XAUUSD, RS_XAUUSD_MagicNumber,
RS_UseReversalEscape, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_XAUUSD_UseTrailingStop, RS_XAUUSD_TrailDistancePoints, RS_XAUUSD_TrailActivationPoints);
if(EnableRSISecretSauce)
ProcessRSISecretSauce(rssData, g_RSS_LotSize);
if(EnableRSIReversalAsianEURUSD)
ProcessRSIReversalAsian(rraEURUSDData, LOT_RRA_EURUSD);
ProcessRSIReversalAsian(rraEURUSDData, g_Pos_RRA_EURUSD);
if(EnableRSIReversalAsianAUDUSD)
ProcessRSIReversalAsian(rraAUDUSDData, LOT_RRA_AUDUSD);
ProcessRSIReversalAsian(rraAUDUSDData, g_Pos_RRA_AUDUSD);
if(EnableSuperEMA)
ProcessSuperEMA(seData, LOT_SE_SuperEMA);
ProcessSuperEMA(seData, g_Pos_SE);
if(EnableRSIConsolidation)
ProcessRSIConsolidation(rcoData, LOT_RCO_RSIConsolidation);
ProcessRSIConsolidation(rcoData, g_Pos_RCO);
if(EnableSimpleTrendlineBTCUSD)
ProcessSimpleTrendline(stBTCData, LOT_ST_BTCUSD);
ProcessSimpleTrendline(stBTCData, g_Pos_ST_BTCUSD);
if(EnableSimpleTrendlineXAUUSD)
ProcessSimpleTrendline(stXAUData, LOT_ST_XAUUSD);
ProcessSimpleTrendline(stXAUData, g_Pos_ST_XAUUSD);
}
//+------------------------------------------------------------------+

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

@@ -13,7 +13,6 @@
#include <Indicators\Trend.mqh>
#include <Indicators\Volumes.mqh>
#include "MagicNumberHelpers.mqh"
#include "PerformanceEvaluator.mqh"
//+------------------------------------------------------------------+
//| Strategy Enable/Disable Switches |
@@ -62,7 +61,11 @@ 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_TrailingStop = 370.0;
input bool ES_UseTrailingStop = true;
input double ES_TrailingActivationPips = 0.0;
input bool ES_UseStaleStopLossExit = false;
input int ES_StaleStopLossSeconds = 33800;
input double ES_LotGröße = 0.03;
input int ES_MagicNumber = 12350;
input bool ES_UseSpreadAdjustment = true;
@@ -71,6 +74,11 @@ input bool ES_UseBarData = true;
input int ES_MaxTradesPerCrossover = 9;
input int ES_ProfitCheckBars = 18;
input bool ES_CloseUnprofitableTrades = true;
input bool ES_UseWeeklyADXFilter = true;
input int ES_WeeklyADXPeriod = 15;
input double ES_WeeklyADXMin = 40.0;
input int ES_WeeklyADXBarShift = 2;
input bool ES_WeeklyADXUseDirection = true;
//+------------------------------------------------------------------+
//| Strategy 3: RSICrossOverReversalXAUUSD |
@@ -252,6 +260,44 @@ input double RS_XAUUSD_LotSize = 0.1;
input int RS_XAUUSD_MagicNumber = 129102315;
input int RS_XAUUSD_Slippage = 3;
input group "=== RSI Scalping Reversal escape (XAUUSD) ==="
input bool RS_UseReversalEscape = false;
input int RS_ReversalATRPeriod = 14;
input double RS_ReversalAdverseAtrMult = 5.25;
input int RS_ReversalSignsRequired = 2;
input double RS_ReversalRsiVelocity = 16.0;
input double RS_ReversalBodyAtrMult = 5.1;
input group "=== RSI Scalping APPL — Trailing ==="
input bool RS_APPL_UseTrailingStop = true;
input double RS_APPL_TrailDistancePoints = 120.0;
input double RS_APPL_TrailActivationPoints = 0.0;
input group "=== RSI Scalping BTCUSD — Trailing ==="
input bool RS_BTCUSD_UseTrailingStop = true;
input double RS_BTCUSD_TrailDistancePoints = 120.0;
input double RS_BTCUSD_TrailActivationPoints = 0.0;
input group "=== RSI Scalping MSFT — Trailing ==="
input bool RS_MSFT_UseTrailingStop = true;
input double RS_MSFT_TrailDistancePoints = 375.0;
input double RS_MSFT_TrailActivationPoints = 75.0;
input group "=== RSI Scalping NVDA — Trailing ==="
input bool RS_NVDA_UseTrailingStop = true;
input double RS_NVDA_TrailDistancePoints = 375.0;
input double RS_NVDA_TrailActivationPoints = 75.0;
input group "=== RSI Scalping TSLA — Trailing ==="
input bool RS_TSLA_UseTrailingStop = true;
input double RS_TSLA_TrailDistancePoints = 900.0;
input double RS_TSLA_TrailActivationPoints = 950.0;
input group "=== RSI Scalping XAUUSD — Trailing ==="
input bool RS_XAUUSD_UseTrailingStop = true;
input double RS_XAUUSD_TrailDistancePoints = 71.0;
input double RS_XAUUSD_TrailActivationPoints = 41.0;
//+------------------------------------------------------------------+
//| Global Variables - DarvasBox |
//+------------------------------------------------------------------+
@@ -289,6 +335,7 @@ struct EMASlopeData {
bool crossover_detected;
datetime trade_open_time;
datetime last_bar_time;
datetime es_last_sl_adjust_success_time;
};
//+------------------------------------------------------------------+
@@ -371,19 +418,18 @@ RSIScalpingData rsTSLAData;
RSIScalpingData rsXAUUSDData;
//+------------------------------------------------------------------+
//| Global Variables for Dynamic Lot Sizes |
//| Global Variables for lot sizes (from inputs below) |
//+------------------------------------------------------------------+
// 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_MSFT_LotSize = 5.0; // Stock - start with stock minimum (5.0)
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)
double g_DB_LotSize = 0.01;
double g_ES_LotSize;
double g_RC_LotSize;
double g_RM_LotSize;
double g_RS_APPL_LotSize;
double g_RS_BTCUSD_LotSize;
double g_RS_MSFT_LotSize;
double g_RS_NVDA_LotSize;
double g_RS_TSLA_LotSize;
double g_RS_XAUUSD_LotSize;
//+------------------------------------------------------------------+
//| Expert initialization function |
@@ -391,148 +437,52 @@ double g_RS_XAUUSD_LotSize = 0.01; // Forex - start with forex minimum (0.01)
int OnInit()
{
int initResult = INIT_SUCCEEDED;
// Initialize Performance Evaluator
InitPerformanceTracking();
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
g_ES_LotSize = ES_LotGröße;
g_RC_LotSize = RC_lotSize;
g_RM_LotSize = RM_InpLotSize;
g_RS_APPL_LotSize = RS_APPL_LotSize;
g_RS_BTCUSD_LotSize = RS_BTCUSD_LotSize;
g_RS_MSFT_LotSize = RS_MSFT_LotSize;
g_RS_NVDA_LotSize = RS_NVDA_LotSize;
g_RS_TSLA_LotSize = RS_TSLA_LotSize;
g_RS_XAUUSD_LotSize = RS_XAUUSD_LotSize;
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(EnableRSIScalpingMSFT)
{
InitRSIScalping(rsMSFTData, RS_MSFT_Symbol, RS_MSFT_TimeFrame, RS_MSFT_RSI_Period, RS_MSFT_RSI_Applied_Price, RS_MSFT_MagicNumber, RS_MSFT_Slippage);
RegisterStrategy("RSIScalpingMSFT", RS_MSFT_MagicNumber, RS_MSFT_LotSize, RS_MSFT_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_MSFT_Symbol);
g_RS_MSFT_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("RSIScalpingMSFT", RS_MSFT_MagicNumber);
if(adjustedLot > 0) g_RS_MSFT_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: ",
Print("United EA (self-evaluate build) initialized. Active strategies: ",
(EnableDarvasBox ? "DarvasBox " : ""),
(EnableEMASlopeDistance ? "EMASlope " : ""),
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
@@ -543,10 +493,7 @@ int OnInit()
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""));
if(PE_EnableLogging)
Print(GetPerformanceSummary());
return initResult;
}
@@ -593,41 +540,6 @@ void OnDeinit(const int reason)
//+------------------------------------------------------------------+
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("RSIScalpingMSFT", RS_MSFT_MagicNumber);
if(adjustedLot > 0) g_RS_MSFT_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);
@@ -643,32 +555,50 @@ void OnTick()
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);
RS_APPL_BarsToWait, g_RS_APPL_LotSize, RS_APPL_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_APPL_UseTrailingStop, RS_APPL_TrailDistancePoints, RS_APPL_TrailActivationPoints);
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);
RS_BTCUSD_BarsToWait, g_RS_BTCUSD_LotSize, RS_BTCUSD_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_BTCUSD_UseTrailingStop, RS_BTCUSD_TrailDistancePoints, RS_BTCUSD_TrailActivationPoints);
if(EnableRSIScalpingMSFT)
ProcessRSIScalping(rsMSFTData, RS_MSFT_Symbol, RS_MSFT_TimeFrame, RS_MSFT_RSI_Period, RS_MSFT_RSI_Applied_Price,
RS_MSFT_RSI_Overbought, RS_MSFT_RSI_Oversold, RS_MSFT_RSI_Target_Buy, RS_MSFT_RSI_Target_Sell,
RS_MSFT_BarsToWait, g_RS_MSFT_LotSize, RS_MSFT_MagicNumber);
RS_MSFT_BarsToWait, g_RS_MSFT_LotSize, RS_MSFT_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_MSFT_UseTrailingStop, RS_MSFT_TrailDistancePoints, RS_MSFT_TrailActivationPoints);
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);
RS_NVDA_BarsToWait, g_RS_NVDA_LotSize, RS_NVDA_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_NVDA_UseTrailingStop, RS_NVDA_TrailDistancePoints, RS_NVDA_TrailActivationPoints);
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);
RS_TSLA_BarsToWait, g_RS_TSLA_LotSize, RS_TSLA_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_TSLA_UseTrailingStop, RS_TSLA_TrailDistancePoints, RS_TSLA_TrailActivationPoints);
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);
RS_XAUUSD_BarsToWait, g_RS_XAUUSD_LotSize, RS_XAUUSD_MagicNumber,
RS_UseReversalEscape, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
RS_XAUUSD_UseTrailingStop, RS_XAUUSD_TrailDistancePoints, RS_XAUUSD_TrailActivationPoints);
}
//+------------------------------------------------------------------+
@@ -1,607 +0,0 @@
//+------------------------------------------------------------------+
//| 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", "MSFT", "NVDA", "TSLA", "GOOGL", "AMZN", "META", "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;
}
//+------------------------------------------------------------------+
@@ -1,6 +1,12 @@
//+------------------------------------------------------------------+
//| DarvasBoxStrategy.mqh |
//+------------------------------------------------------------------+
#if defined(CLUSTER0_ORCHESTRATOR) || defined(UNITED_V2_DYNAMIC_LOTS)
extern double g_DB_LotSize;
#define DARVAS_TRADE_LOT (g_DB_LotSize)
#else
#define DARVAS_TRADE_LOT 0.01
#endif
bool InitDarvasBox(string symbol)
{
@@ -195,9 +201,9 @@ bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price, double sl, double tp)
// 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(0.01, dbData.symbol, 0, sl, tp, "Darvas Box Breakout");
result = dbData.trade.Buy(DARVAS_TRADE_LOT, dbData.symbol, 0, sl, tp, "Darvas Box Breakout");
else
result = dbData.trade.Sell(0.01, dbData.symbol, 0, sl, tp, "Darvas Box Breakdown");
result = dbData.trade.Sell(DARVAS_TRADE_LOT, dbData.symbol, 0, sl, tp, "Darvas Box Breakdown");
// Always log errors, success only if logging enabled
if(result)
@@ -0,0 +1,318 @@
//+------------------------------------------------------------------+
//| SimpleTrendlineStrategy.mqh |
//+------------------------------------------------------------------+
#ifndef SIMPLE_TRENDLINE_STRATEGY_MQH
#define SIMPLE_TRENDLINE_STRATEGY_MQH
struct SimpleTrendlineModel
{
datetime t1;
datetime t2;
datetime t3;
double a;
double b;
bool valid;
};
struct SimpleTrendlineData
{
string symbol;
bool isInitialized;
CTrade trade;
ENUM_TIMEFRAMES signalTF;
ENUM_TIMEFRAMES higherTF;
int maPeriod;
ENUM_MA_METHOD maMethod;
ENUM_APPLIED_PRICE appliedPrice;
int htfBarsToScan;
double touchTolerancePoints;
double breakBufferPoints;
ulong magic;
bool drawTrendline;
int maHandle;
datetime lastSignalBarTime;
string lineName;
};
double ST_NormalizeVolume(const string sym, double vol)
{
double minLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP);
if(step > 0.0)
vol = MathFloor(vol / step) * step;
if(vol < minLot)
vol = minLot;
if(vol > maxLot)
vol = maxLot;
return vol;
}
bool ST_GetPosition(const string sym, const ulong magic, ENUM_POSITION_TYPE &type, double &volume)
{
if(!PositionSelectByMagic(sym, magic))
return false;
type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
volume = PositionGetDouble(POSITION_VOLUME);
return true;
}
int ST_FindRecentCrossPoints(SimpleTrendlineData &d, datetime &times[], double &prices[])
{
ArrayResize(times, 0);
ArrayResize(prices, 0);
if(d.maHandle == INVALID_HANDLE)
return 0;
int needBars = MathMax(d.htfBarsToScan, d.maPeriod + 20);
MqlRates rates[];
double maBuf[];
ArraySetAsSeries(rates, true);
ArraySetAsSeries(maBuf, true);
int copiedRates = CopyRates(d.symbol, d.higherTF, 0, needBars, rates);
int copiedMa = CopyBuffer(d.maHandle, 0, 0, needBars, maBuf);
if(copiedRates <= 5 || copiedMa <= 5)
return 0;
int bars = MathMin(copiedRates, copiedMa);
for(int i = 2; i < bars - 1; i++)
{
double d0 = rates[i].close - maBuf[i];
double d1 = rates[i + 1].close - maBuf[i + 1];
if(d0 == 0.0 || d1 == 0.0 || (d0 * d1 < 0.0))
{
int n = ArraySize(times);
ArrayResize(times, n + 1);
ArrayResize(prices, n + 1);
times[n] = rates[i].time;
prices[n] = rates[i].close;
if(ArraySize(times) >= 3)
break;
}
}
return ArraySize(times);
}
bool ST_BuildTrendline(SimpleTrendlineData &d, SimpleTrendlineModel &m)
{
m.valid = false;
datetime ts[];
double ps[];
if(ST_FindRecentCrossPoints(d, ts, ps) < 3)
return false;
datetime tOld[3];
double pOld[3];
for(int i = 0; i < 3; i++)
{
tOld[i] = ts[2 - i];
pOld[i] = ps[2 - i];
}
long t0 = (long)tOld[0];
double x1 = 0.0;
double x2 = (double)((long)tOld[1] - t0);
double x3 = (double)((long)tOld[2] - t0);
double y1 = pOld[0];
double y2 = pOld[1];
double y3 = pOld[2];
double sx = x1 + x2 + x3;
double sy = y1 + y2 + y3;
double sxx = x1 * x1 + x2 * x2 + x3 * x3;
double sxy = x1 * y1 + x2 * y2 + x3 * y3;
double den = 3.0 * sxx - sx * sx;
if(MathAbs(den) < 1e-10)
return false;
m.a = (3.0 * sxy - sx * sy) / den;
m.b = (sy - m.a * sx) / 3.0;
m.t1 = tOld[0];
m.t2 = tOld[1];
m.t3 = tOld[2];
m.valid = true;
return true;
}
double ST_LinePriceAt(const SimpleTrendlineModel &m, const datetime t)
{
if(!m.valid)
return 0.0;
double x = (double)((long)t - (long)m.t1);
return m.a * x + m.b;
}
void ST_DrawTrendline(SimpleTrendlineData &d, const SimpleTrendlineModel &m)
{
if(!d.drawTrendline || !m.valid || d.symbol != _Symbol)
return;
datetime tStart = m.t1;
datetime tEnd = iTime(d.symbol, d.signalTF, 0);
if(tEnd <= tStart)
tEnd = m.t3 + PeriodSeconds(d.signalTF) * 20;
double pStart = ST_LinePriceAt(m, tStart);
double pEnd = ST_LinePriceAt(m, tEnd);
if(ObjectFind(0, d.lineName) < 0)
ObjectCreate(0, d.lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd);
else
{
ObjectMove(0, d.lineName, 0, tStart, pStart);
ObjectMove(0, d.lineName, 1, tEnd, pEnd);
}
ObjectSetInteger(0, d.lineName, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, d.lineName, OBJPROP_COLOR, clrGold);
ObjectSetInteger(0, d.lineName, OBJPROP_WIDTH, 2);
}
void ST_TryExitOnBreak(SimpleTrendlineData &d, const SimpleTrendlineModel &m)
{
ENUM_POSITION_TYPE posType;
double vol;
if(!ST_GetPosition(d.symbol, d.magic, posType, vol))
return;
double close1 = iClose(d.symbol, d.signalTF, 1);
datetime t1 = iTime(d.symbol, d.signalTF, 1);
double line1 = ST_LinePriceAt(m, t1);
double buf = d.breakBufferPoints * SymbolInfoDouble(d.symbol, SYMBOL_POINT);
bool closePos = false;
if(posType == POSITION_TYPE_BUY && close1 < (line1 - buf))
closePos = true;
if(posType == POSITION_TYPE_SELL && close1 > (line1 + buf))
closePos = true;
if(closePos)
ClosePositionByMagic(d.trade, d.symbol, d.magic);
}
void ST_TryPullbackEntry(SimpleTrendlineData &d, const SimpleTrendlineModel &m, const double lots)
{
if(PositionExistsByMagic(d.symbol, d.magic))
return;
MqlRates b1[], b2[];
ArraySetAsSeries(b1, true);
ArraySetAsSeries(b2, true);
if(CopyRates(d.symbol, d.signalTF, 1, 1, b1) != 1)
return;
if(CopyRates(d.symbol, d.signalTF, 2, 1, b2) != 1)
return;
if(ArraySize(b1) < 1 || ArraySize(b2) < 1)
return;
double line1 = ST_LinePriceAt(m, b1[0].time);
double tol = d.touchTolerancePoints * SymbolInfoDouble(d.symbol, SYMBOL_POINT);
bool upTrend = (m.a > 0.0);
bool downTrend = (m.a < 0.0);
double vol = ST_NormalizeVolume(d.symbol, lots);
if(upTrend)
{
bool touched = (b1[0].low <= (line1 + tol));
bool reclaim = (b1[0].close > line1);
bool bullish = (b1[0].close > b1[0].open);
bool stillHealthy = (b2[0].close >= ST_LinePriceAt(m, b2[0].time) - tol);
if(touched && reclaim && bullish && stillHealthy)
{
if(!d.trade.Buy(vol, d.symbol, 0.0, 0.0, 0.0, "SimpleTrendline BUY"))
Print("SimpleTrendline BUY failed [", d.symbol, "] retcode=", d.trade.ResultRetcode(), " ", d.trade.ResultRetcodeDescription());
}
}
else if(downTrend)
{
bool touched = (b1[0].high >= (line1 - tol));
bool reject = (b1[0].close < line1);
bool bearish = (b1[0].close < b1[0].open);
bool stillWeak = (b2[0].close <= ST_LinePriceAt(m, b2[0].time) + tol);
if(touched && reject && bearish && stillWeak)
{
if(!d.trade.Sell(vol, d.symbol, 0.0, 0.0, 0.0, "SimpleTrendline SELL"))
Print("SimpleTrendline SELL failed [", d.symbol, "] retcode=", d.trade.ResultRetcode(), " ", d.trade.ResultRetcodeDescription());
}
}
}
bool InitSimpleTrendline(SimpleTrendlineData &d,
const string symbol,
const ENUM_TIMEFRAMES signalTF,
const ENUM_TIMEFRAMES higherTF,
const int maPeriod,
const ENUM_MA_METHOD maMethod,
const ENUM_APPLIED_PRICE appliedPrice,
const int htfBarsToScan,
const double touchTolerancePoints,
const double breakBufferPoints,
const ulong magic,
const bool drawTrendline)
{
d.isInitialized = false;
d.symbol = symbol;
StringTrimLeft(d.symbol);
StringTrimRight(d.symbol);
if(StringLen(d.symbol) == 0)
d.symbol = _Symbol;
if(!SymbolSelect(d.symbol, true))
return false;
d.signalTF = signalTF;
d.higherTF = higherTF;
d.maPeriod = maPeriod;
d.maMethod = maMethod;
d.appliedPrice = appliedPrice;
d.htfBarsToScan = htfBarsToScan;
d.touchTolerancePoints = touchTolerancePoints;
d.breakBufferPoints = breakBufferPoints;
d.magic = magic;
d.drawTrendline = drawTrendline;
d.lastSignalBarTime = 0;
d.lineName = "SimpleTrendline_" + d.symbol + "_" + IntegerToString((int)d.magic);
d.trade.SetExpertMagicNumber((long)d.magic);
d.trade.SetTypeFillingBySymbol(d.symbol);
d.trade.SetDeviationInPoints(20);
d.maHandle = iMA(d.symbol, d.higherTF, d.maPeriod, 0, d.maMethod, d.appliedPrice);
if(d.maHandle == INVALID_HANDLE)
return false;
d.isInitialized = true;
return true;
}
void DeinitSimpleTrendline(SimpleTrendlineData &d)
{
if(d.maHandle != INVALID_HANDLE)
IndicatorRelease(d.maHandle);
d.maHandle = INVALID_HANDLE;
if(ObjectFind(0, d.lineName) >= 0)
ObjectDelete(0, d.lineName);
d.isInitialized = false;
}
void ProcessSimpleTrendline(SimpleTrendlineData &d, const double lots)
{
if(!d.isInitialized)
return;
datetime bar0 = iTime(d.symbol, d.signalTF, 0);
if(bar0 == 0 || bar0 == d.lastSignalBarTime)
return;
d.lastSignalBarTime = bar0;
SimpleTrendlineModel m;
if(!ST_BuildTrendline(d, m))
return;
ST_DrawTrendline(d, m);
ST_TryExitOnBreak(d, m);
ST_TryPullbackEntry(d, m, lots);
}
#endif // SIMPLE_TRENDLINE_STRATEGY_MQH
+137 -15
View File
@@ -5,14 +5,17 @@
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#property version "1.10"
#property strict
#property description "LOT_* nominal at ORCH_ReferenceBalance; scale = balance/equity ÷ reference (clamped). No performance-evaluator ranking."
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Indicators\Trend.mqh>
#include <Indicators\Volumes.mqh>
#include "MagicNumberHelpers.mqh"
#define UNITED_V2_DYNAMIC_LOTS
double g_DB_LotSize;
// Include strategy implementations early so structs are available
#include "Strategies/DarvasBoxStrategy.mqh"
#include "Strategies/EMASlopeDistanceStrategy.mqh"
@@ -22,6 +25,7 @@
#include "Strategies/SuperEMAStrategy.mqh"
#include "Strategies/RSIReversalAsianStrategy.mqh"
#include "Strategies/RSIConsolidationStrategy.mqh"
#include "Strategies/SimpleTrendlineStrategy.mqh"
//+------------------------------------------------------------------+
//| Global Lot Size Variables (for dynamic lot sizing) |
@@ -30,6 +34,18 @@ double g_ES_LotSize; // EMA Slope Distance lot size
double g_RC_LotSize; // RSI CrossOver Reversal lot size
double g_RM_LotSize; // RSI MidPoint Hijack lot size
double g_Pos_RS_APPL;
double g_Pos_RS_BTCUSD;
double g_Pos_RS_NVDA;
double g_Pos_RS_TSLA;
double g_Pos_RS_XAUUSD;
double g_Pos_RRA_EURUSD;
double g_Pos_RRA_AUDUSD;
double g_Pos_SE;
double g_Pos_RCO;
double g_Pos_ST_BTCUSD;
double g_Pos_ST_XAUUSD;
bool United_MayOpenNewEntry(const string symbol, const ulong magic, const bool isBuy)
{
if(PositionExistsByMagic(symbol, magic))
@@ -54,8 +70,11 @@ input bool EnableSuperEMA = true;
input bool EnableRSIConsolidation = true;
input bool EnableRSIReversalAsianEURUSD = true;
input bool EnableRSIReversalAsianAUDUSD = true;
input bool EnableSimpleTrendlineBTCUSD = true;
input bool EnableSimpleTrendlineXAUUSD = true;
input group "=== Centralized Lot Size (Granular Per Robot) ==="
input double LOT_DB_DarvasBox = 0.01;
input double LOT_ES_EMASlopeDistance = 0.05;
input double LOT_RC_RSICrossOver = 0.1;
input double LOT_RM_RSIMidPointHijack = 0.01;
@@ -68,6 +87,15 @@ input double LOT_RRA_EURUSD = 0.01;
input double LOT_RRA_AUDUSD = 0.10;
input double LOT_SE_SuperEMA = 0.01;
input double LOT_RCO_RSIConsolidation = 0.04;
input double LOT_ST_BTCUSD = 0.19;
input double LOT_ST_XAUUSD = 0.02;
input group "=== Balance-based position sizing ==="
input bool ORCH_ScaleLotsByBalance = true;
input bool ORCH_UseEquityInsteadOfBalance = false;
input double ORCH_ReferenceBalance = 10000.0;
input double ORCH_MinBalanceScale = 0.1;
input double ORCH_MaxBalanceScale = 10.0;
//+------------------------------------------------------------------+
//| Strategy 1: DarvasBoxXAUUSD |
@@ -385,6 +413,75 @@ input ulong RCO_MagicNumber = 20250420;
input int RCO_Slippage = 10;
input int RCO_MaxSpreadPoints = 28;
input group "=== SimpleTrendline BTCUSD ==="
input string ST_BTC_Symbol = "BTCUSD";
input ENUM_TIMEFRAMES ST_BTC_SignalTF = PERIOD_H1;
input ENUM_TIMEFRAMES ST_BTC_HigherTF = PERIOD_H4;
input int ST_BTC_MAPeriod = 150;
input ENUM_MA_METHOD ST_BTC_MAMethod = MODE_SMMA;
input ENUM_APPLIED_PRICE ST_BTC_AppliedPrice = PRICE_OPEN;
input int ST_BTC_HTFBarsToScan = 1200;
input double ST_BTC_LineTouchTolerance = 170.0;
input double ST_BTC_BreakBuffer = 90.0;
input ulong ST_BTC_MagicNumber = 26042501;
input bool ST_BTC_DrawTrendline = true;
input group "=== SimpleTrendline XAUUSD ==="
input string ST_XAU_Symbol = "XAUUSD";
input ENUM_TIMEFRAMES ST_XAU_SignalTF = PERIOD_H1;
input ENUM_TIMEFRAMES ST_XAU_HigherTF = PERIOD_M10;
input int ST_XAU_MAPeriod = 65;
input ENUM_MA_METHOD ST_XAU_MAMethod = MODE_EMA;
input ENUM_APPLIED_PRICE ST_XAU_AppliedPrice = PRICE_OPEN;
input int ST_XAU_HTFBarsToScan = 500;
input double ST_XAU_LineTouchTolerance = 220.0;
input double ST_XAU_BreakBuffer = 110.0;
input ulong ST_XAU_MagicNumber = 26042503;
input bool ST_XAU_DrawTrendline = true;
//+------------------------------------------------------------------+
//| Balance scaling: LOT_* = nominal size at ORCH_ReferenceBalance |
//+------------------------------------------------------------------+
double United_BalanceScaleFactor()
{
if(!ORCH_ScaleLotsByBalance || ORCH_ReferenceBalance <= 0.0)
return 1.0;
const double money = ORCH_UseEquityInsteadOfBalance
? AccountInfoDouble(ACCOUNT_EQUITY)
: AccountInfoDouble(ACCOUNT_BALANCE);
double raw = money / ORCH_ReferenceBalance;
if(raw < ORCH_MinBalanceScale)
raw = ORCH_MinBalanceScale;
if(raw > ORCH_MaxBalanceScale)
raw = ORCH_MaxBalanceScale;
return raw;
}
double United_ScaledLot(const double baseLot)
{
const double lot = baseLot * United_BalanceScaleFactor();
return (lot > 0.0 ? lot : 0.0);
}
void United_RefreshScaledLots()
{
g_DB_LotSize = United_ScaledLot(LOT_DB_DarvasBox);
g_ES_LotSize = United_ScaledLot(LOT_ES_EMASlopeDistance);
g_RC_LotSize = United_ScaledLot(LOT_RC_RSICrossOver);
g_RM_LotSize = United_ScaledLot(LOT_RM_RSIMidPointHijack);
g_Pos_RS_APPL = United_ScaledLot(LOT_RS_APPL);
g_Pos_RS_BTCUSD = United_ScaledLot(LOT_RS_BTCUSD);
g_Pos_RS_NVDA = United_ScaledLot(LOT_RS_NVDA);
g_Pos_RS_TSLA = United_ScaledLot(LOT_RS_TSLA);
g_Pos_RS_XAUUSD = United_ScaledLot(LOT_RS_XAUUSD);
g_Pos_RRA_EURUSD = United_ScaledLot(LOT_RRA_EURUSD);
g_Pos_RRA_AUDUSD = United_ScaledLot(LOT_RRA_AUDUSD);
g_Pos_SE = United_ScaledLot(LOT_SE_SuperEMA);
g_Pos_RCO = United_ScaledLot(LOT_RCO_RSIConsolidation);
g_Pos_ST_BTCUSD = United_ScaledLot(LOT_ST_BTCUSD);
g_Pos_ST_XAUUSD = United_ScaledLot(LOT_ST_XAUUSD);
}
//+------------------------------------------------------------------+
//| Global Variables - DarvasBox |
//+------------------------------------------------------------------+
@@ -483,6 +580,8 @@ RSIScalpingData rsTSLAData;
RSIScalpingData rsXAUUSDData;
SuperEMAData seData;
RSIConsolidationData rcoData;
SimpleTrendlineData stBTCData;
SimpleTrendlineData stXAUData;
//+------------------------------------------------------------------+
//| Global Variables - RSI Reversal Asian |
@@ -497,10 +596,7 @@ int OnInit()
{
int initResult = INIT_SUCCEEDED;
// Initialize global lot size variables
g_ES_LotSize = LOT_ES_EMASlopeDistance;
g_RC_LotSize = LOT_RC_RSICrossOver;
g_RM_LotSize = LOT_RM_RSIMidPointHijack;
United_RefreshScaledLots();
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
if(EnableDarvasBox)
@@ -570,6 +666,18 @@ int OnInit()
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, "'");
if(EnableSimpleTrendlineBTCUSD)
if(!InitSimpleTrendline(stBTCData, ST_BTC_Symbol, ST_BTC_SignalTF, ST_BTC_HigherTF, ST_BTC_MAPeriod,
ST_BTC_MAMethod, ST_BTC_AppliedPrice, ST_BTC_HTFBarsToScan,
ST_BTC_LineTouchTolerance, ST_BTC_BreakBuffer, ST_BTC_MagicNumber, ST_BTC_DrawTrendline))
Print("Warning: SimpleTrendlineBTCUSD failed to initialize for symbol '", ST_BTC_Symbol, "'");
if(EnableSimpleTrendlineXAUUSD)
if(!InitSimpleTrendline(stXAUData, ST_XAU_Symbol, ST_XAU_SignalTF, ST_XAU_HigherTF, ST_XAU_MAPeriod,
ST_XAU_MAMethod, ST_XAU_AppliedPrice, ST_XAU_HTFBarsToScan,
ST_XAU_LineTouchTolerance, ST_XAU_BreakBuffer, ST_XAU_MagicNumber, ST_XAU_DrawTrendline))
Print("Warning: SimpleTrendlineXAUUSD failed to initialize for symbol '", ST_XAU_Symbol, "'");
Print("United EA initialized. Active strategies: ",
(EnableDarvasBox ? "DarvasBox " : ""),
@@ -584,7 +692,9 @@ int OnInit()
(EnableSuperEMA ? "SuperEMA " : ""),
(EnableRSIConsolidation ? "RSIConsolidation " : ""),
(EnableRSIReversalAsianEURUSD ? "RSIReversalAsianEURUSD " : ""),
(EnableRSIReversalAsianAUDUSD ? "RSIReversalAsianAUDUSD " : ""));
(EnableRSIReversalAsianAUDUSD ? "RSIReversalAsianAUDUSD " : ""),
(EnableSimpleTrendlineBTCUSD ? "SimpleTrendlineBTCUSD " : ""),
(EnableSimpleTrendlineXAUUSD ? "SimpleTrendlineXAUUSD " : ""));
return initResult;
}
@@ -632,6 +742,11 @@ void OnDeinit(const int reason)
if(EnableRSIReversalAsianAUDUSD)
DeinitRSIReversalAsian(rraAUDUSDData);
if(EnableSimpleTrendlineBTCUSD)
DeinitSimpleTrendline(stBTCData);
if(EnableSimpleTrendlineXAUUSD)
DeinitSimpleTrendline(stXAUData);
Print("United EA deinitialized. Reason: ", reason);
}
@@ -641,6 +756,8 @@ void OnDeinit(const int reason)
//+------------------------------------------------------------------+
void OnTick()
{
United_RefreshScaledLots();
if(EnableDarvasBox)
ProcessDarvasBox(DB_Symbol);
@@ -656,49 +773,54 @@ void OnTick()
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, LOT_RS_APPL, RS_APPL_MagicNumber,
RS_APPL_BarsToWait, g_Pos_RS_APPL, RS_APPL_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
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, LOT_RS_BTCUSD, RS_BTCUSD_MagicNumber,
RS_BTCUSD_BarsToWait, g_Pos_RS_BTCUSD, RS_BTCUSD_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
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, LOT_RS_NVDA, RS_NVDA_MagicNumber,
RS_NVDA_BarsToWait, g_Pos_RS_NVDA, RS_NVDA_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
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, LOT_RS_TSLA, RS_TSLA_MagicNumber,
RS_TSLA_BarsToWait, g_Pos_RS_TSLA, RS_TSLA_MagicNumber,
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
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, LOT_RS_XAUUSD, RS_XAUUSD_MagicNumber,
RS_XAUUSD_BarsToWait, g_Pos_RS_XAUUSD, RS_XAUUSD_MagicNumber,
RS_UseReversalEscape, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult);
if(EnableRSIReversalAsianEURUSD)
ProcessRSIReversalAsian(rraEURUSDData, LOT_RRA_EURUSD);
ProcessRSIReversalAsian(rraEURUSDData, g_Pos_RRA_EURUSD);
if(EnableRSIReversalAsianAUDUSD)
ProcessRSIReversalAsian(rraAUDUSDData, LOT_RRA_AUDUSD);
ProcessRSIReversalAsian(rraAUDUSDData, g_Pos_RRA_AUDUSD);
if(EnableSuperEMA)
ProcessSuperEMA(seData, LOT_SE_SuperEMA);
ProcessSuperEMA(seData, g_Pos_SE);
if(EnableRSIConsolidation)
ProcessRSIConsolidation(rcoData, LOT_RCO_RSIConsolidation);
ProcessRSIConsolidation(rcoData, g_Pos_RCO);
if(EnableSimpleTrendlineBTCUSD)
ProcessSimpleTrendline(stBTCData, g_Pos_ST_BTCUSD);
if(EnableSimpleTrendlineXAUUSD)
ProcessSimpleTrendline(stXAUData, g_Pos_ST_XAUUSD);
}
//+------------------------------------------------------------------+
@@ -13,7 +13,6 @@
#include <Indicators\Trend.mqh>
#include <Indicators\Volumes.mqh>
#include "MagicNumberHelpers.mqh"
#include "PerformanceEvaluator.mqh"
//+------------------------------------------------------------------+
//| Strategy Enable/Disable Switches |
@@ -371,19 +370,18 @@ RSIScalpingData rsTSLAData;
RSIScalpingData rsXAUUSDData;
//+------------------------------------------------------------------+
//| Global Variables for Dynamic Lot Sizes |
//| Global Variables for lot sizes (from inputs below) |
//+------------------------------------------------------------------+
// 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_MSFT_LotSize = 5.0; // Stock - start with stock minimum (5.0)
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)
double g_DB_LotSize = 0.01;
double g_ES_LotSize;
double g_RC_LotSize;
double g_RM_LotSize;
double g_RS_APPL_LotSize;
double g_RS_BTCUSD_LotSize;
double g_RS_MSFT_LotSize;
double g_RS_NVDA_LotSize;
double g_RS_TSLA_LotSize;
double g_RS_XAUUSD_LotSize;
//+------------------------------------------------------------------+
//| Expert initialization function |
@@ -391,148 +389,52 @@ double g_RS_XAUUSD_LotSize = 0.01; // Forex - start with forex minimum (0.01)
int OnInit()
{
int initResult = INIT_SUCCEEDED;
// Initialize Performance Evaluator
InitPerformanceTracking();
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
g_ES_LotSize = ES_LotGröße;
g_RC_LotSize = RC_lotSize;
g_RM_LotSize = RM_InpLotSize;
g_RS_APPL_LotSize = RS_APPL_LotSize;
g_RS_BTCUSD_LotSize = RS_BTCUSD_LotSize;
g_RS_MSFT_LotSize = RS_MSFT_LotSize;
g_RS_NVDA_LotSize = RS_NVDA_LotSize;
g_RS_TSLA_LotSize = RS_TSLA_LotSize;
g_RS_XAUUSD_LotSize = RS_XAUUSD_LotSize;
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(EnableRSIScalpingMSFT)
{
InitRSIScalping(rsMSFTData, RS_MSFT_Symbol, RS_MSFT_TimeFrame, RS_MSFT_RSI_Period, RS_MSFT_RSI_Applied_Price, RS_MSFT_MagicNumber, RS_MSFT_Slippage);
RegisterStrategy("RSIScalpingMSFT", RS_MSFT_MagicNumber, RS_MSFT_LotSize, RS_MSFT_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_MSFT_Symbol);
g_RS_MSFT_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("RSIScalpingMSFT", RS_MSFT_MagicNumber);
if(adjustedLot > 0) g_RS_MSFT_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: ",
Print("United EA (self-evaluate build) initialized. Active strategies: ",
(EnableDarvasBox ? "DarvasBox " : ""),
(EnableEMASlopeDistance ? "EMASlope " : ""),
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
@@ -543,10 +445,7 @@ int OnInit()
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""));
if(PE_EnableLogging)
Print(GetPerformanceSummary());
return initResult;
}
@@ -593,41 +492,6 @@ void OnDeinit(const int reason)
//+------------------------------------------------------------------+
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("RSIScalpingMSFT", RS_MSFT_MagicNumber);
if(adjustedLot > 0) g_RS_MSFT_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);
@@ -1,607 +0,0 @@
//+------------------------------------------------------------------+
//| 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", "MSFT", "NVDA", "TSLA", "GOOGL", "AMZN", "META", "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;
}
//+------------------------------------------------------------------+
@@ -1,607 +0,0 @@
//+------------------------------------------------------------------+
//| 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", "MSFT", "NVDA", "TSLA", "GOOGL", "AMZN", "META", "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;
}
//+------------------------------------------------------------------+
@@ -13,7 +13,6 @@
#include <Indicators\Trend.mqh>
#include <Indicators\Volumes.mqh>
#include "MagicNumberHelpers.mqh"
#include "PerformanceEvaluator.mqh"
//+------------------------------------------------------------------+
//| Strategy Enable/Disable Switches |
@@ -371,19 +370,18 @@ RSIScalpingData rsTSLAData;
RSIScalpingData rsXAUUSDData;
//+------------------------------------------------------------------+
//| Global Variables for Dynamic Lot Sizes |
//| Global Variables for lot sizes (from inputs below) |
//+------------------------------------------------------------------+
// 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_MSFT_LotSize = 5.0; // Stock - start with stock minimum (5.0)
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)
double g_DB_LotSize = 0.01;
double g_ES_LotSize;
double g_RC_LotSize;
double g_RM_LotSize;
double g_RS_APPL_LotSize;
double g_RS_BTCUSD_LotSize;
double g_RS_MSFT_LotSize;
double g_RS_NVDA_LotSize;
double g_RS_TSLA_LotSize;
double g_RS_XAUUSD_LotSize;
//+------------------------------------------------------------------+
//| Expert initialization function |
@@ -391,148 +389,52 @@ double g_RS_XAUUSD_LotSize = 0.01; // Forex - start with forex minimum (0.01)
int OnInit()
{
int initResult = INIT_SUCCEEDED;
// Initialize Performance Evaluator
InitPerformanceTracking();
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
g_ES_LotSize = ES_LotGröße;
g_RC_LotSize = RC_lotSize;
g_RM_LotSize = RM_InpLotSize;
g_RS_APPL_LotSize = RS_APPL_LotSize;
g_RS_BTCUSD_LotSize = RS_BTCUSD_LotSize;
g_RS_MSFT_LotSize = RS_MSFT_LotSize;
g_RS_NVDA_LotSize = RS_NVDA_LotSize;
g_RS_TSLA_LotSize = RS_TSLA_LotSize;
g_RS_XAUUSD_LotSize = RS_XAUUSD_LotSize;
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(EnableRSIScalpingMSFT)
{
InitRSIScalping(rsMSFTData, RS_MSFT_Symbol, RS_MSFT_TimeFrame, RS_MSFT_RSI_Period, RS_MSFT_RSI_Applied_Price, RS_MSFT_MagicNumber, RS_MSFT_Slippage);
RegisterStrategy("RSIScalpingMSFT", RS_MSFT_MagicNumber, RS_MSFT_LotSize, RS_MSFT_Symbol);
// Start with minimum lot size (will be adjusted by performance evaluator)
double minLot = GetMinLotSizeForSymbol(RS_MSFT_Symbol);
g_RS_MSFT_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("RSIScalpingMSFT", RS_MSFT_MagicNumber);
if(adjustedLot > 0) g_RS_MSFT_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: ",
Print("United EA (self-evaluate build) initialized. Active strategies: ",
(EnableDarvasBox ? "DarvasBox " : ""),
(EnableEMASlopeDistance ? "EMASlope " : ""),
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
@@ -543,10 +445,7 @@ int OnInit()
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""));
if(PE_EnableLogging)
Print(GetPerformanceSummary());
return initResult;
}
@@ -593,41 +492,6 @@ void OnDeinit(const int reason)
//+------------------------------------------------------------------+
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("RSIScalpingMSFT", RS_MSFT_MagicNumber);
if(adjustedLot > 0) g_RS_MSFT_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);
@@ -0,0 +1,36 @@
; saved on 2026.05.01
; Genetic optimization — EMASlopeDistanceCocktailXAUUSD (main.mq5)
; Baseline aligned with Desktop 123.set; Strategy Tester -> Inputs -> Load
;
; Format: Parameter=Start||Step||Min||Max||Optimize(Y/N)
;
; === Session / identity (usually fixed) ===
MagicNumber=12350||0||12350||12350||N
Timeframe=16385||0||16385||16385||N
; === Core signal thresholds (123.set starts) ===
EMA_Periode=46||2||10||120||Y
PreisSchwelle=600.0||25.0||200.0||1200.0||Y
SteigungSchwelle=80.0||5.0||15.0||200.0||Y
ÜberwachungTimeout=800||20||120||2400||Y
; === Trade management ===
TrailingStop=260.0||50.0||5.0||500.0||Y
LotGröße=0.03||0.01||0.01||0.30||N
; === Behaviour toggles (fixed unless you want regime tests) ===
UseSpreadAdjustment=true||false||0||true||N
UseBarData=true||false||0||true||N
CloseUnprofitableTrades=true||false||0||true||N
; === Crossover / profit logic (123.set flags) ===
MaxTradesPerCrossover=9||1||1||15||Y
ProfitCheckBars=12||1||1||18||Y
; === Weekly ADX filter ===
UseWeeklyADXFilter=true||false||0||true||N
WeeklyADXPeriod=15||1||7||28||Y
WeeklyADXMin=40.0||2.0||15.0||55.0||Y
WeeklyADXBarShift=2||1||1||5||N
WeeklyADXUseDirection=true||false||0||true||N
@@ -0,0 +1,589 @@
//+------------------------------------------------------------------+
//| EMACrossOver.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"
#include <Trade\Trade.mqh>
#include "../_united/MagicNumberHelpers.mqh"
//--- Eingabeparameter (Input Parameters) - Optimized Profitable Parameters
input int EMA_Periode = 50; // EMA Periode
input double PreisSchwelle = 700.0; // Preisbewegung Schwelle in Pips
input double SteigungSchwelle = 25.0; // EMA Steigung Schwelle in Pips
input int ÜberwachungTimeout = 340; // Überwachungszeit in Sekunden
input double TrailingStop = 370.0; // Gleitender Stop in Pips
input double LotGröße = 0.07; // Handelsvolumen
input int MagicNumber = 135790; // Magic Number für Trades
input bool UseSpreadAdjustment = true; // Spread-Anpassung verwenden
input ENUM_TIMEFRAMES Timeframe = PERIOD_H1; // Zeitraum für Analyse
input bool UseBarData = true; // Bar-Daten statt Tick-Daten verwenden
input int MaxTradesPerCrossover = 10; // Maximale Trades pro Crossover-Ereignis
input int ProfitCheckBars = 15; // Bars bis zur Profit-Prüfung
input bool CloseUnprofitableTrades = true; // Unprofitable Trades nach X Bars schließen
input bool UseWeeklyADXFilter = true; // W1 ADX Trendfilter aktivieren
input int WeeklyADXPeriod = 15; // ADX-Periode auf W1
input double WeeklyADXMin = 40.0; // Minimaler ADX fuer Trendfreigabe
input int WeeklyADXBarShift = 2; // 1=letzte geschlossene W1-Kerze
input bool WeeklyADXUseDirection = true; // +DI/-DI Richtung mitpruefen
//--- Globale Variablen (Global Variables)
int ema_handle; // EMA Indicator Handle
double ema_array[]; // Array für EMA
datetime letzte_überwachung_zeit; // Zeit der letzten Überwachung
bool überwachung_aktiv = false; // Überwachungsstatus
bool preis_trigger_aktiv = false; // Preis-Trigger Status
bool steigung_trigger_aktiv = false; // Steigungs-Trigger Status
int ticket = 0; // Trade Ticket
CTrade trade; // CTrade Objekt
int trades_in_current_crossover = 0; // Anzahl Trades im aktuellen Crossover
bool crossover_detected = false; // Crossover erkannt
datetime trade_open_time = 0; // Zeitpunkt des Trade-Öffnens
//+------------------------------------------------------------------+
//| Weekly ADX trend filter |
//+------------------------------------------------------------------+
bool IsWeeklyADXTrendFavorable(ENUM_ORDER_TYPE order_type)
{
if(!UseWeeklyADXFilter)
return true;
int adxShift = WeeklyADXBarShift;
if(adxShift < 0)
adxShift = 0;
int adx_handle = iADX(_Symbol, PERIOD_W1, WeeklyADXPeriod);
if(adx_handle == INVALID_HANDLE)
{
Print("TRACE: Weekly ADX Handle ungültig - Filter blockiert Entry");
return false;
}
double adx_buf[], plus_di_buf[], minus_di_buf[];
ArraySetAsSeries(adx_buf, true);
ArraySetAsSeries(plus_di_buf, true);
ArraySetAsSeries(minus_di_buf, true);
bool ok_adx = (CopyBuffer(adx_handle, 0, adxShift, 1, adx_buf) > 0);
bool ok_plus = (CopyBuffer(adx_handle, 1, adxShift, 1, plus_di_buf) > 0);
bool ok_minus = (CopyBuffer(adx_handle, 2, adxShift, 1, minus_di_buf) > 0);
IndicatorRelease(adx_handle);
if(!ok_adx || !ok_plus || !ok_minus)
{
Print("TRACE: Weekly ADX Daten nicht verfügbar - Filter blockiert Entry");
return false;
}
double adx_value = adx_buf[0];
double plus_di = plus_di_buf[0];
double minus_di = minus_di_buf[0];
bool strength_ok = (adx_value >= WeeklyADXMin);
bool direction_ok = true;
if(WeeklyADXUseDirection)
{
if(order_type == ORDER_TYPE_BUY)
direction_ok = (plus_di > minus_di);
else
direction_ok = (minus_di > plus_di);
}
Print("TRACE: Weekly ADX Filter | ADX=", DoubleToString(adx_value, 2),
" +DI=", DoubleToString(plus_di, 2),
" -DI=", DoubleToString(minus_di, 2),
" strength_ok=", strength_ok,
" direction_ok=", direction_ok);
return (strength_ok && direction_ok);
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- CTrade konfigurieren (Configure CTrade)
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(10);
trade.SetTypeFilling(ORDER_FILLING_IOC);
//--- EMA Indicator Handle erstellen (Create EMA indicator handle)
ema_handle = iMA(_Symbol, Timeframe, EMA_Periode, 0, MODE_EMA, PRICE_CLOSE);
if(ema_handle == INVALID_HANDLE)
{
Print("Fehler beim Erstellen des EMA Indicators");
return(INIT_FAILED);
}
//--- Arrays initialisieren (Initialize arrays)
ArraySetAsSeries(ema_array, true);
//--- Arrays mit aktuellen Werten füllen (Fill arrays with current values)
BerechneEMA();
Print("EMA EA initialisiert - Periode: ", EMA_Periode, " Timeframe: ", EnumToString(Timeframe), " Handle: ", ema_handle);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Indicator Handle freigeben (Release indicator handle)
if(ema_handle != INVALID_HANDLE)
{
IndicatorRelease(ema_handle);
}
Print("EA beendet - Grund: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Bar-Daten oder Tick-Daten verwenden (Use bar data or tick data)
if(UseBarData)
{
//--- Nur bei neuen Bars ausführen (Only execute on new bars)
static datetime last_bar_time = 0;
datetime current_bar_time = iTime(_Symbol, Timeframe, 0);
if(current_bar_time == last_bar_time)
{
return; // Kein neuer Bar, nichts tun
}
last_bar_time = current_bar_time;
}
//--- EMA Werte berechnen (Calculate EMA values)
BerechneEMA();
//--- Debug: Aktuelle Werte ausgeben (Debug: Output current values)
if(ArraySize(ema_array) > 0)
{
double aktueller_close = iClose(_Symbol, Timeframe, 0);
double ema_aktuell = ema_array[0];
double ema_vorher = ema_array[1];
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / _Point;
double steigung = (ema_aktuell - ema_vorher) / _Point;
if(UseBarData)
{
Print("=== DEBUG INFO (Neuer Bar) ===");
Print("Bar Zeit: ", TimeToString(iTime(_Symbol, 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: ", preis_trigger_aktiv, " Steigungs-Trigger: ", steigung_trigger_aktiv);
Print("Überwachung aktiv: ", überwachung_aktiv);
Print("Position offen: ", PositionExistsByMagic(_Symbol, MagicNumber));
Print("Trades im aktuellen Crossover: ", trades_in_current_crossover, "/", MaxTradesPerCrossover);
Print("==================");
}
//--- Überwachung prüfen (Check monitoring)
if(überwachung_aktiv)
{
if(UseBarData)
{
// Bar-basierte Überwachungszeit
int bars_since_monitoring = iBarShift(_Symbol, Timeframe, letzte_überwachung_zeit);
int timeout_bars = (int)(ÜberwachungTimeout / PeriodSeconds(Timeframe));
if(bars_since_monitoring > timeout_bars)
{
überwachung_aktiv = false;
preis_trigger_aktiv = false;
steigung_trigger_aktiv = false;
Print("Überwachung beendet - Bar-basierte Zeitüberschreitung (", bars_since_monitoring, " Bars)");
}
}
else
{
// Tick-basierte Überwachungszeit
if(TimeCurrent() - letzte_überwachung_zeit > ÜberwachungTimeout)
{
überwachung_aktiv = false;
preis_trigger_aktiv = false;
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();
}
//+------------------------------------------------------------------+
//| EMA Berechnung (EMA Calculation) |
//+------------------------------------------------------------------+
void BerechneEMA()
{
//--- EMA Werte vom Indicator kopieren (Copy EMA values from indicator)
int copied = CopyBuffer(ema_handle, 0, 0, 3, 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]: ", ema_array[0], " [1]: ", ema_array[1], " [2]: ", ema_array[2]);
}
//+------------------------------------------------------------------+
//| Trigger-Bedingungen prüfen (Check trigger conditions) |
//+------------------------------------------------------------------+
void PrüfeTrigger()
{
if(ArraySize(ema_array) < 2)
{
Print("TRACE: Array zu klein - Größe: ", ArraySize(ema_array));
return;
}
//--- Aktuelle Werte (Current values)
double aktueller_preis = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double aktueller_ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double aktueller_close = iClose(_Symbol, Timeframe, 0);
double pips_multiplier = (_Digits == 3 || _Digits == 5) ? 10.0 : 1.0;
//--- EMA Werte in Variablen (EMA values in variables)
double ema_aktuell = ema_array[0];
double ema_vorher = 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)
{
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: ", PreisSchwelle, ")");
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
Print("TRACE: Trades im aktuellen Crossover: ", trades_in_current_crossover, "/", MaxTradesPerCrossover);
if(preis_abstand > PreisSchwelle && !preis_trigger_aktiv)
{
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: ", SteigungSchwelle, ")");
if(MathAbs(steigung) > SteigungSchwelle && !steigung_trigger_aktiv)
{
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(preis_trigger_aktiv && steigung_trigger_aktiv && !überwachung_aktiv)
{
überwachung_aktiv = true;
if(UseBarData)
{
letzte_überwachung_zeit = iTime(_Symbol, Timeframe, 0); // Aktuelle Bar-Zeit
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Bar: ", TimeToString(letzte_überwachung_zeit), ")");
}
else
{
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(ü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(trades_in_current_crossover >= MaxTradesPerCrossover)
{
Print("TRACE: Trade-Limit erreicht (", MaxTradesPerCrossover, ") - Kein neuer Trade");
return;
}
if(bullish_signal && !PositionExistsByMagic(_Symbol, MagicNumber))
{
if(!IsWeeklyADXTrendFavorable(ORDER_TYPE_BUY))
{
Print("TRACE: Weekly ADX blockiert BUY-Entry");
return;
}
Print("TRACE: Versuche KAUF-Trade zu platzieren (Trade #", trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_BUY))
{
trades_in_current_crossover++;
}
}
else if(bearish_signal && !PositionExistsByMagic(_Symbol, MagicNumber))
{
if(!IsWeeklyADXTrendFavorable(ORDER_TYPE_SELL))
{
Print("TRACE: Weekly ADX blockiert SELL-Entry");
return;
}
Print("TRACE: Versuche VERKAUF-Trade zu platzieren (Trade #", trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_SELL))
{
trades_in_current_crossover++;
}
}
else if(PositionExistsByMagic(_Symbol, 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: ", LotGröße);
bool success = false;
if(order_type == ORDER_TYPE_BUY)
{
success = trade.Buy(LotGröße, _Symbol, 0, 0, 0, "EMA Crossover Trade");
}
else
{
success = trade.Sell(LotGröße, _Symbol, 0, 0, 0, "EMA Crossover Trade");
}
if(success)
{
ticket = (int)trade.ResultOrder();
Print("TRACE: Trade erfolgreich platziert: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF", " Ticket: ", ticket);
//--- Trade-Öffnungszeit speichern (Save trade opening time)
trade_open_time = iTime(_Symbol, Timeframe, 0);
Print("TRACE: Trade-Öffnungszeit: ", TimeToString(trade_open_time));
//--- Überwachung zurücksetzen (Reset monitoring)
überwachung_aktiv = false;
preis_trigger_aktiv = false;
steigung_trigger_aktiv = false;
return true;
}
else
{
Print("TRACE: Fehler beim Platzieren des Trades - Retcode: ", trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", trade.ResultRetcodeDescription());
return false;
}
}
//+------------------------------------------------------------------+
//| Trades verwalten (Manage trades) |
//+------------------------------------------------------------------+
void VerwalteTrades()
{
if(!PositionSelectByMagic(_Symbol, 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);
double pips_multiplier = (_Digits == 3 || _Digits == 5) ? 10.0 : 1.0;
double trailing_stop_pips = 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(ema_array) >= 1)
{
double aktueller_close = iClose(_Symbol, Timeframe, 0);
double ema_aktuell = 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 ", trades_in_current_crossover);
}
}
//--- Profit-Prüfung nach X Bars (Profit check after X bars)
if(CloseUnprofitableTrades && trade_open_time != 0 && PositionExistsByMagic(_Symbol, MagicNumber))
{
Print("TRACE: Profit-Prüfung aktiviert - CloseUnprofitableTrades: ", CloseUnprofitableTrades);
PrüfeProfitNachBars();
}
else if(!CloseUnprofitableTrades)
{
Print("TRACE: Profit-Prüfung deaktiviert - CloseUnprofitableTrades: ", CloseUnprofitableTrades);
}
}
//+------------------------------------------------------------------+
//| Profit-Prüfung nach X Bars (Profit check after X bars) |
//+------------------------------------------------------------------+
void PrüfeProfitNachBars()
{
if(!PositionSelectByMagic(_Symbol, MagicNumber))
{
return; // Keine Position offen
}
datetime current_bar_time = iTime(_Symbol, Timeframe, 0);
int bars_since_trade_open = iBarShift(_Symbol, Timeframe, trade_open_time);
Print("TRACE: Bars seit Trade-Öffnung: ", bars_since_trade_open, "/", ProfitCheckBars);
//--- Prüfe ob genügend Bars vergangen sind (Check if enough bars have passed)
if(bars_since_trade_open >= 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 ", 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)
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)
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(trade, _Symbol, 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: ", trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", 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(trade, _Symbol, MagicNumber);
if(success)
{
Print("TRACE: Position erfolgreich geschlossen - Grund: ", reason);
}
else
{
Print("TRACE: Fehler beim Schließen der Position - Retcode: ", trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", trade.ResultRetcodeDescription());
}
}
//+------------------------------------------------------------------+
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Some files were not shown because too many files have changed in this diff Show More