Update
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# XAUUSD H1 — ONNX action model
|
||||
|
||||
Same pipeline as **`../xauusd_m15`**, but **H1** bars, **H1-scaled label windows** (~wall-clock parity with M15 defaults), and **`XAUUSD_H1_ActionEA.mq5`**.
|
||||
|
||||
## Label scaling (vs M15)
|
||||
|
||||
| M15 (bars) | Wall time | H1 (bars) |
|
||||
|------------|-----------|-----------|
|
||||
| horizon 32 | ~8 h | 8 |
|
||||
| local 24 | ~6 h | 6 |
|
||||
| pullback 20| ~5 h | 5 |
|
||||
|
||||
## Setup
|
||||
|
||||
1. MT5: **XAUUSD** visible; download **H1** history.
|
||||
2. Python:
|
||||
|
||||
```bash
|
||||
cd ai/xauusd_h1
|
||||
pip install -r requirements.txt
|
||||
python main.py
|
||||
```
|
||||
|
||||
Env: `XAU_SYMBOL`, **`XAU_H1_LOOKBACK`** (default **48**, must match EA **InpLookback**), `XAU_EPOCHS`, `XAU_BATCH`, `SESSION_HOUR_OFFSET`.
|
||||
|
||||
3. Copy **`models/XAUUSD_H1_action.onnx`** next to **`XAUUSD_H1_ActionEA.mq5`** (for `#resource` embed) or adjust include path per your workflow.
|
||||
4. Compile EA on **H1** chart; paste **24** floats into **InpFeatMinStr** / **InpFeatMaxStr** from training stdout.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `main.py` | MT5 H1 fetch, train, `XAUUSD_H1_action.onnx` + meta |
|
||||
| `labeling.py` | `compute_action_labels` (H1 default horizons) |
|
||||
| `features.py` | 24-dim features (same order as M15 EA) |
|
||||
| `XAUUSD_H1_ActionEA.mq5` | Inference + trading |
|
||||
| `XAUUSD_H1_ActionEA_optimize.set` | Tester optimization skeleton |
|
||||
|
||||
Feature semantics: **`../xauusd_m15/FRONTLINE_RSI_INTEGRATION.md`**.
|
||||
|
||||
## ONNX
|
||||
|
||||
- Input: `[1, lookback, 24]` float32, row **0** = newest bar.
|
||||
- Output: `[1, 5]` softmax.
|
||||
|
||||
Research tooling — not investment advice.
|
||||
@@ -0,0 +1,372 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| XAUUSD_H1_ActionEA.mq5 |
|
||||
//| ONNX softmax [5]: HOLD, BUY, SELL_SHORT, CLOSE_LONG, CLOSE_SHORT |
|
||||
//| 24 features: base 13 + RSI/frontline (see ../xauusd_m15 doc) |
|
||||
//| Train: ai/xauusd_h1/main.py → XAUUSD_H1_action.onnx |
|
||||
//| Exits: model CLOSE_* + optional InpTakeProfitATR; adverse ATR |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Profitable EA Project"
|
||||
#property version "1.00"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
#resource "XAUUSD_H1_action.onnx" as uchar ExtModel[]
|
||||
|
||||
#define FEAT_COUNT 24
|
||||
|
||||
input group "Model"
|
||||
input int InpLookback = 48;
|
||||
// 0 = legacy: p(BUY)>=InpProbBuy etc.; 1 = directional beats HOLD (5-class softmax)
|
||||
input int InpEntryMode = 1;
|
||||
input double InpProbBuy = 0.18;
|
||||
input double InpProbSell = 0.18;
|
||||
input double InpMinBeatHold = 0.0;
|
||||
input int InpExitMode = 2;
|
||||
input double InpProbCloseL = 0.18;
|
||||
input double InpProbCloseS = 0.18;
|
||||
input double InpMinCloseBeatHold = 0.0;
|
||||
|
||||
input group "Session (match Python SESSION_HOUR_OFFSET)"
|
||||
input int InpSessionHourOffset = 0;
|
||||
|
||||
input group "Scaler: paste 24 floats each from python main.py"
|
||||
input string InpFeatMinStr = "";
|
||||
input string InpFeatMaxStr = "";
|
||||
|
||||
input group "Risk"
|
||||
input double InpLotSize = 0.01;
|
||||
input int InpMagic = 902016;
|
||||
input int InpSlippage = 30;
|
||||
input double InpMaxAdverseATR = 2.0;
|
||||
input double InpTakeProfitATR = 0.0;
|
||||
|
||||
double g_feat_min[FEAT_COUNT];
|
||||
double g_feat_max[FEAT_COUNT];
|
||||
|
||||
CTrade trade;
|
||||
long g_onnx = INVALID_HANDLE;
|
||||
datetime g_last_bar = 0;
|
||||
|
||||
void InitDefaultScalerBounds()
|
||||
{
|
||||
double def_min[FEAT_COUNT] = {
|
||||
0,0,0,0,0,0,-0.05,-0.05,0,-0.02,1.0,0,0.1,
|
||||
0,0,-1,-0.2,-0.2,0,0,0,0,0,0
|
||||
};
|
||||
double def_max[FEAT_COUNT] = {
|
||||
5000,5000,5000,5000,1,1,0.05,0.05,0.05,0.02,1.02,1,5.0,
|
||||
1,1,1,0.2,0.2,1,1,1,1,1,1
|
||||
};
|
||||
for(int i = 0; i < FEAT_COUNT; i++)
|
||||
{
|
||||
g_feat_min[i] = def_min[i];
|
||||
g_feat_max[i] = def_max[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool ParseFeatCsv(const string s, double &arr[])
|
||||
{
|
||||
if(StringLen(s) < 3) return false;
|
||||
string parts[];
|
||||
int n = StringSplit(s, ',', parts);
|
||||
if(n != FEAT_COUNT) return false;
|
||||
for(int i = 0; i < FEAT_COUNT; i++)
|
||||
arr[i] = StringToDouble(parts[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
InitDefaultScalerBounds();
|
||||
trade.SetExpertMagicNumber(InpMagic);
|
||||
trade.SetDeviationInPoints(InpSlippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
|
||||
if(StringLen(InpFeatMinStr) > 0 && ParseFeatCsv(InpFeatMinStr, g_feat_min))
|
||||
Print("Loaded InpFeatMinStr (24)");
|
||||
if(StringLen(InpFeatMaxStr) > 0 && ParseFeatCsv(InpFeatMaxStr, g_feat_max))
|
||||
Print("Loaded InpFeatMaxStr (24)");
|
||||
|
||||
g_onnx = OnnxCreateFromBuffer(ExtModel, ONNX_DEBUG_LOGS);
|
||||
if(g_onnx == INVALID_HANDLE)
|
||||
{
|
||||
Print("OnnxCreateFromBuffer failed ", GetLastError());
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
const long inShape[] = {1, InpLookback, FEAT_COUNT};
|
||||
if(!OnnxSetInputShape(g_onnx, 0, inShape))
|
||||
{
|
||||
Print("OnnxSetInputShape failed ", GetLastError());
|
||||
OnnxRelease(g_onnx);
|
||||
return INIT_FAILED;
|
||||
}
|
||||
const long outShape[] = {1, 5};
|
||||
if(!OnnxSetOutputShape(g_onnx, 0, outShape))
|
||||
{
|
||||
Print("OnnxSetOutputShape failed ", GetLastError());
|
||||
OnnxRelease(g_onnx);
|
||||
return INIT_FAILED;
|
||||
}
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int r)
|
||||
{
|
||||
if(g_onnx != INVALID_HANDLE) OnnxRelease(g_onnx);
|
||||
}
|
||||
|
||||
double AtrNow()
|
||||
{
|
||||
double b[];
|
||||
ArraySetAsSeries(b, true);
|
||||
int h = iATR(_Symbol, PERIOD_CURRENT, 14);
|
||||
if(h == INVALID_HANDLE) return 0;
|
||||
if(CopyBuffer(h, 0, 0, 2, b) < 1) { IndicatorRelease(h); return 0; }
|
||||
double v = b[0];
|
||||
IndicatorRelease(h);
|
||||
return v;
|
||||
}
|
||||
|
||||
bool AdverseExit(const long type, const double open_price)
|
||||
{
|
||||
double atr = AtrNow();
|
||||
if(atr <= 0) return false;
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
if(type == POSITION_TYPE_BUY)
|
||||
{
|
||||
double adv = (open_price - bid) / atr;
|
||||
return adv >= InpMaxAdverseATR;
|
||||
}
|
||||
double adv = (ask - open_price) / atr;
|
||||
return adv >= InpMaxAdverseATR;
|
||||
}
|
||||
|
||||
bool ProfitExit(const long type, const double open_price)
|
||||
{
|
||||
if(InpTakeProfitATR <= 0.0) return false;
|
||||
double atr = AtrNow();
|
||||
if(atr <= 0.0) return false;
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
if(type == POSITION_TYPE_BUY)
|
||||
return (bid - open_price) >= InpTakeProfitATR * atr;
|
||||
return (open_price - ask) >= InpTakeProfitATR * atr;
|
||||
}
|
||||
|
||||
bool ModelCloseLong(const double p0, const double p1, const double p3)
|
||||
{
|
||||
if(InpExitMode == 0)
|
||||
return (p3 >= InpProbCloseL);
|
||||
if(InpExitMode == 1)
|
||||
return (p3 > p0 + InpMinCloseBeatHold && p3 > p1);
|
||||
return (p3 > p0 + InpMinCloseBeatHold);
|
||||
}
|
||||
|
||||
bool ModelCloseShort(const double p0, const double p2, const double p4)
|
||||
{
|
||||
if(InpExitMode == 0)
|
||||
return (p4 >= InpProbCloseS);
|
||||
if(InpExitMode == 1)
|
||||
return (p4 > p0 + InpMinCloseBeatHold && p4 > p2);
|
||||
return (p4 > p0 + InpMinCloseBeatHold);
|
||||
}
|
||||
|
||||
void ScaleFeatures(const float &raw[], float &out[])
|
||||
{
|
||||
for(int f = 0; f < FEAT_COUNT; f++)
|
||||
{
|
||||
double den = g_feat_max[f] - g_feat_min[f];
|
||||
if(den < 1e-12) den = 1e-12;
|
||||
double x = (double)raw[f] - g_feat_min[f];
|
||||
out[f] = (float)MathMax(0.0, MathMin(1.0, x / den));
|
||||
}
|
||||
}
|
||||
|
||||
bool PrepareMatrix(matrixf &M)
|
||||
{
|
||||
int L = InpLookback;
|
||||
double open[], high[], low[], close[];
|
||||
long vol[];
|
||||
datetime bt[];
|
||||
ArraySetAsSeries(open, true);
|
||||
ArraySetAsSeries(high, true);
|
||||
ArraySetAsSeries(low, true);
|
||||
ArraySetAsSeries(close, true);
|
||||
ArraySetAsSeries(vol, true);
|
||||
ArraySetAsSeries(bt, true);
|
||||
|
||||
int need = L + 55;
|
||||
if(CopyOpen(_Symbol, PERIOD_CURRENT, 0, need, open) < L) return false;
|
||||
if(CopyHigh(_Symbol, PERIOD_CURRENT, 0, need, high) < L) return false;
|
||||
if(CopyLow(_Symbol, PERIOD_CURRENT, 0, need, low) < L) return false;
|
||||
if(CopyClose(_Symbol, PERIOD_CURRENT, 0, need, close) < L) return false;
|
||||
if(CopyTickVolume(_Symbol, PERIOD_CURRENT, 0, need, vol) < L) return false;
|
||||
if(CopyTime(_Symbol, PERIOD_CURRENT, 0, need, bt) < L) return false;
|
||||
|
||||
double rsi7[], rsi14[], rsi21[], ema20[], ema50[], atr[];
|
||||
ArraySetAsSeries(rsi7, true);
|
||||
ArraySetAsSeries(rsi14, true);
|
||||
ArraySetAsSeries(rsi21, true);
|
||||
ArraySetAsSeries(ema20, true);
|
||||
ArraySetAsSeries(ema50, true);
|
||||
ArraySetAsSeries(atr, true);
|
||||
|
||||
int h7 = iRSI(_Symbol, PERIOD_CURRENT, 7, PRICE_CLOSE);
|
||||
int h14 = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
|
||||
int h21 = iRSI(_Symbol, PERIOD_CURRENT, 21, PRICE_CLOSE);
|
||||
int hE20 = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_EMA, PRICE_CLOSE);
|
||||
int hE50 = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
|
||||
int hA = iATR(_Symbol, PERIOD_CURRENT, 14);
|
||||
if(h7 == INVALID_HANDLE || h14 == INVALID_HANDLE || h21 == INVALID_HANDLE ||
|
||||
hE20 == INVALID_HANDLE || hE50 == INVALID_HANDLE || hA == INVALID_HANDLE)
|
||||
return false;
|
||||
|
||||
if(CopyBuffer(h7, 0, 0, need, rsi7) < L ||
|
||||
CopyBuffer(h14, 0, 0, need, rsi14) < L ||
|
||||
CopyBuffer(h21, 0, 0, need, rsi21) < L ||
|
||||
CopyBuffer(hE20, 0, 0, need, ema20) < L ||
|
||||
CopyBuffer(hE50, 0, 0, need, ema50) < L ||
|
||||
CopyBuffer(hA, 0, 0, need, atr) < L)
|
||||
{
|
||||
IndicatorRelease(h7); IndicatorRelease(h14); IndicatorRelease(h21);
|
||||
IndicatorRelease(hE20); IndicatorRelease(hE50); IndicatorRelease(hA);
|
||||
return false;
|
||||
}
|
||||
IndicatorRelease(h7); IndicatorRelease(h14); IndicatorRelease(h21);
|
||||
IndicatorRelease(hE20); IndicatorRelease(hE50); IndicatorRelease(hA);
|
||||
|
||||
M.Resize(L, FEAT_COUNT);
|
||||
const double RSI_OB = 70.0;
|
||||
const double RSI_OS = 30.0;
|
||||
|
||||
for(int i = 0; i < L; i++)
|
||||
{
|
||||
double vma = 0;
|
||||
int cnt = 0;
|
||||
for(int k = i; k < i + 20 && k < ArraySize(vol); k++) { vma += (double)vol[k]; cnt++; }
|
||||
if(cnt < 1) cnt = 1;
|
||||
vma /= cnt;
|
||||
|
||||
double r0 = rsi14[i];
|
||||
double r1 = (i + 1 < ArraySize(rsi14)) ? rsi14[i + 1] : r0;
|
||||
double r2 = (i + 2 < ArraySize(rsi14)) ? rsi14[i + 2] : r1;
|
||||
double rv7 = rsi7[i];
|
||||
double rv21 = rsi21[i];
|
||||
|
||||
double spread = (r0 - rv7) / 50.0;
|
||||
if(spread > 1.0) spread = 1.0;
|
||||
if(spread < -1.0) spread = -1.0;
|
||||
double vel = (r0 - r1) / 25.0;
|
||||
double acc = ((r0 - r1) - (r1 - r2)) / 25.0;
|
||||
double dist_mid = MathAbs(r0 - 50.0) / 50.0;
|
||||
double c_ob = (r1 < RSI_OB && r0 >= RSI_OB) ? 1.0 : 0.0;
|
||||
double c_os = (r1 > RSI_OS && r0 <= RSI_OS) ? 1.0 : 0.0;
|
||||
double c50u = (r1 < 50.0 && r0 >= 50.0) ? 1.0 : 0.0;
|
||||
double c50d = (r1 > 50.0 && r0 <= 50.0) ? 1.0 : 0.0;
|
||||
|
||||
MqlDateTime st;
|
||||
TimeToStruct(bt[i], st);
|
||||
int hr = (st.hour + InpSessionHourOffset) % 24;
|
||||
if(hr < 0) hr += 24;
|
||||
double asian = (hr >= 0 && hr < 8) ? 1.0 : 0.0;
|
||||
|
||||
float raw[FEAT_COUNT];
|
||||
raw[0] = (float)open[i];
|
||||
raw[1] = (float)high[i];
|
||||
raw[2] = (float)low[i];
|
||||
raw[3] = (float)close[i];
|
||||
raw[4] = (float)((double)vol[i] / 1000000.0);
|
||||
raw[5] = (float)(r0 / 100.0);
|
||||
raw[6] = (float)((ema20[i] - close[i]) / close[i]);
|
||||
raw[7] = (float)((ema50[i] - close[i]) / close[i]);
|
||||
raw[8] = (float)(atr[i] / close[i]);
|
||||
double pc = (i < L - 1) ? (close[i] - close[i + 1]) / close[i + 1] : 0.0;
|
||||
raw[9] = (float)pc;
|
||||
raw[10] = (float)(high[i] / low[i]);
|
||||
raw[11] = (float)(vma / 1000000.0);
|
||||
raw[12] = (float)(vma > 0 ? (double)vol[i] / vma : 1.0);
|
||||
raw[13] = (float)(rv7 / 100.0);
|
||||
raw[14] = (float)(rv21 / 100.0);
|
||||
raw[15] = (float)spread;
|
||||
raw[16] = (float)vel;
|
||||
raw[17] = (float)acc;
|
||||
raw[18] = (float)dist_mid;
|
||||
raw[19] = (float)c_ob;
|
||||
raw[20] = (float)c_os;
|
||||
raw[21] = (float)c50u;
|
||||
raw[22] = (float)c50d;
|
||||
raw[23] = (float)asian;
|
||||
|
||||
float sc[FEAT_COUNT];
|
||||
ScaleFeatures(raw, sc);
|
||||
for(int j = 0; j < FEAT_COUNT; j++)
|
||||
M[i][j] = sc[j];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
datetime t = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||||
if(t == g_last_bar) return;
|
||||
g_last_bar = t;
|
||||
|
||||
matrixf Min;
|
||||
if(!PrepareMatrix(Min))
|
||||
{
|
||||
Print("PrepareMatrix failed");
|
||||
return;
|
||||
}
|
||||
|
||||
vectorf out;
|
||||
out.Resize(5);
|
||||
if(!OnnxRun(g_onnx, ONNX_NO_CONVERSION, Min, out))
|
||||
{
|
||||
Print("OnnxRun failed ", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
double p0 = out[0], p1 = out[1], p2 = out[2], p3 = out[3], p4 = out[4];
|
||||
Print("ONNX H1 HOLD=", p0, " BUY=", p1, " SELL=", p2, " CL=", p3, " CS=", p4);
|
||||
|
||||
if(!PositionSelect(_Symbol))
|
||||
{
|
||||
if(InpEntryMode == 1)
|
||||
{
|
||||
double dir = MathMax(p1, p2);
|
||||
if(dir <= p0 + InpMinBeatHold)
|
||||
return;
|
||||
if(p1 >= p2 && p1 > p0 + InpMinBeatHold)
|
||||
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "AI H1 BUY");
|
||||
else if(p2 > p1 && p2 > p0 + InpMinBeatHold)
|
||||
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "AI H1 SELL");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(p1 >= InpProbBuy && p1 >= p2)
|
||||
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "AI H1 BUY");
|
||||
else if(p2 >= InpProbSell && p2 > p1)
|
||||
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "AI H1 SELL");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
long typ = (long)PositionGetInteger(POSITION_TYPE);
|
||||
double opn = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
if(AdverseExit(typ, opn))
|
||||
{
|
||||
trade.PositionClose(_Symbol);
|
||||
return;
|
||||
}
|
||||
if(ProfitExit(typ, opn))
|
||||
{
|
||||
trade.PositionClose(_Symbol);
|
||||
return;
|
||||
}
|
||||
if(typ == POSITION_TYPE_BUY && ModelCloseLong(p0, p1, p3))
|
||||
trade.PositionClose(_Symbol);
|
||||
else if(typ == POSITION_TYPE_SELL && ModelCloseShort(p0, p2, p4))
|
||||
trade.PositionClose(_Symbol);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
; XAUUSD_H1_ActionEA — optimization preset (match trained InpLookback to ONNX)
|
||||
; Copy to MetaQuotes\Terminal\<ID>\MQL5\Profiles\Tester\
|
||||
;
|
||||
; Model
|
||||
InpLookback=48||32||8||96||Y
|
||||
InpEntryMode=1||0||1||1||Y
|
||||
InpProbBuy=0.18||0.14||0.02||0.26||Y
|
||||
InpProbSell=0.18||0.14||0.02||0.26||Y
|
||||
InpMinBeatHold=0.0||0.0||0.01||0.05||Y
|
||||
InpExitMode=2||0||1||2||Y
|
||||
InpProbCloseL=0.18||0.14||0.02||0.26||Y
|
||||
InpProbCloseS=0.18||0.14||0.02||0.26||Y
|
||||
InpMinCloseBeatHold=0.0||0.0||0.01||0.04||Y
|
||||
; Session
|
||||
InpSessionHourOffset=0||-3||1||3||N
|
||||
; Scaler
|
||||
InpFeatMinStr=
|
||||
InpFeatMaxStr=
|
||||
; Risk
|
||||
InpLotSize=0.01||0.01||0.001000||0.100000||N
|
||||
InpMagic=902016||902016||1||9020160||N
|
||||
InpSlippage=30||30||1||300||N
|
||||
InpMaxAdverseATR=2.0||1.0||0.25||3.5||Y
|
||||
InpTakeProfitATR=0.0||0.0||0.25||3.0||Y
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,167 @@
|
||||
"""
|
||||
Feature pipeline: base 13 (EA-compatible) + 11 RSI / session features.
|
||||
|
||||
Same 24 dims as XAUUSD M15 EA (see ../xauusd_m15/FRONTLINE_RSI_INTEGRATION.md).
|
||||
RSI uses Wilder smoothing (ewm alpha=1/period) to align with MT5 iRSI.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
NUM_BASE_FEATURES = 13
|
||||
NUM_RSI_EXTRA = 11
|
||||
NUM_FEATURES = NUM_BASE_FEATURES + NUM_RSI_EXTRA # 24
|
||||
|
||||
RSI_OVERBOUGHT = 70.0
|
||||
RSI_OVERSOLD = 30.0
|
||||
|
||||
|
||||
def wilder_rsi(close: pd.Series, period: int) -> np.ndarray:
|
||||
"""Wilder RSI (matches MetaTrader iRSI closely)."""
|
||||
delta = close.diff()
|
||||
gain = delta.clip(lower=0.0)
|
||||
loss = (-delta).clip(lower=0.0)
|
||||
avg_g = gain.ewm(alpha=1.0 / period, min_periods=period, adjust=False).mean()
|
||||
avg_l = loss.ewm(alpha=1.0 / period, min_periods=period, adjust=False).mean()
|
||||
rs = avg_g / avg_l.replace(0, np.nan)
|
||||
rsi = 100.0 - (100.0 / (1.0 + rs))
|
||||
return rsi.fillna(50.0).to_numpy(dtype=np.float64)
|
||||
|
||||
|
||||
def prepare_features_full(
|
||||
df: pd.DataFrame,
|
||||
*,
|
||||
session_hour_offset: int | None = None,
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
Build (N, 24) feature table, chronological index matching df.
|
||||
Drops first ~50 rows (warmup).
|
||||
"""
|
||||
if session_hour_offset is None:
|
||||
session_hour_offset = int(os.environ.get("SESSION_HOUR_OFFSET", "0"))
|
||||
|
||||
o = df["open"].to_numpy(dtype=np.float64)
|
||||
h = df["high"].to_numpy(dtype=np.float64)
|
||||
l = df["low"].to_numpy(dtype=np.float64)
|
||||
c = df["close"].astype(float)
|
||||
vol = df["tick_volume"].to_numpy(dtype=np.float64)
|
||||
n = len(df)
|
||||
idx = df.index
|
||||
|
||||
rsi7 = wilder_rsi(c, 7)
|
||||
rsi14 = wilder_rsi(c, 14)
|
||||
rsi21 = wilder_rsi(c, 21)
|
||||
|
||||
ema20 = c.ewm(span=20, adjust=False).mean().to_numpy()
|
||||
ema50 = c.ewm(span=50, adjust=False).mean().to_numpy()
|
||||
|
||||
tr = np.maximum(
|
||||
h - l,
|
||||
np.maximum(np.abs(h - np.roll(c.to_numpy(), 1)), np.abs(l - np.roll(c.to_numpy(), 1))),
|
||||
)
|
||||
tr[0] = h[0] - l[0]
|
||||
atr = pd.Series(tr).rolling(14).mean().to_numpy()
|
||||
|
||||
vol_ma = np.zeros(n)
|
||||
for j in range(n):
|
||||
s = 0.0
|
||||
cnt = 0
|
||||
for k in range(j, min(j + 20, n)):
|
||||
s += vol[k]
|
||||
cnt += 1
|
||||
vol_ma[j] = s / cnt if cnt else vol[j]
|
||||
|
||||
pc_ea = np.zeros(n)
|
||||
cvals = c.to_numpy()
|
||||
for j in range(1, n):
|
||||
den = cvals[j - 1]
|
||||
pc_ea[j] = (cvals[j] - den) / den if den else 0.0
|
||||
|
||||
hours = np.zeros(n, dtype=np.int32)
|
||||
for j in range(n):
|
||||
ts = idx[j]
|
||||
try:
|
||||
hts = int(ts.hour)
|
||||
except Exception:
|
||||
hts = 0
|
||||
hours[j] = (hts + session_hour_offset) % 24
|
||||
|
||||
rows = []
|
||||
for j in range(n):
|
||||
r0 = rsi14[j]
|
||||
r1 = rsi14[j - 1] if j > 0 else r0
|
||||
r2 = rsi14[j - 2] if j > 1 else r1
|
||||
|
||||
spread = np.clip((r0 - rsi7[j]) / 50.0, -1.0, 1.0)
|
||||
vel = (r0 - r1) / 25.0
|
||||
acc = ((r0 - r1) - (r1 - r2)) / 25.0
|
||||
dist_mid = abs(r0 - 50.0) / 50.0
|
||||
|
||||
cross_ob = 1.0 if (r1 < RSI_OVERBOUGHT and r0 >= RSI_OVERBOUGHT) else 0.0
|
||||
cross_os = 1.0 if (r1 > RSI_OVERSOLD and r0 <= RSI_OVERSOLD) else 0.0
|
||||
cross_50_up = 1.0 if (r1 < 50.0 and r0 >= 50.0) else 0.0
|
||||
cross_50_dn = 1.0 if (r1 > 50.0 and r0 <= 50.0) else 0.0
|
||||
asian = 1.0 if (0 <= hours[j] < 8) else 0.0
|
||||
|
||||
rows.append(
|
||||
[
|
||||
float(o[j]),
|
||||
float(h[j]),
|
||||
float(l[j]),
|
||||
float(cvals[j]),
|
||||
float(vol[j] / 1_000_000.0),
|
||||
float(rsi14[j] / 100.0),
|
||||
float((ema20[j] - cvals[j]) / cvals[j]) if cvals[j] else 0.0,
|
||||
float((ema50[j] - cvals[j]) / cvals[j]) if cvals[j] else 0.0,
|
||||
float(atr[j] / cvals[j]) if cvals[j] else 0.0,
|
||||
float(pc_ea[j]),
|
||||
float(h[j] / l[j]) if l[j] else 1.0,
|
||||
float(vol_ma[j] / 1_000_000.0),
|
||||
float(vol[j] / vol_ma[j]) if vol_ma[j] > 0 else 1.0,
|
||||
float(rsi7[j] / 100.0),
|
||||
float(rsi21[j] / 100.0),
|
||||
float(spread),
|
||||
float(vel),
|
||||
float(acc),
|
||||
float(dist_mid),
|
||||
float(cross_ob),
|
||||
float(cross_os),
|
||||
float(cross_50_up),
|
||||
float(cross_50_dn),
|
||||
float(asian),
|
||||
]
|
||||
)
|
||||
|
||||
cols = [
|
||||
"open",
|
||||
"high",
|
||||
"low",
|
||||
"close",
|
||||
"tick_volume",
|
||||
"rsi",
|
||||
"ema20_n",
|
||||
"ema50_n",
|
||||
"atr_n",
|
||||
"price_change",
|
||||
"high_low_ratio",
|
||||
"volume_ma",
|
||||
"volume_ratio",
|
||||
"rsi7_n",
|
||||
"rsi21_n",
|
||||
"rsi_fast_slow_spread",
|
||||
"rsi_velocity",
|
||||
"rsi_accel",
|
||||
"rsi_dist_mid_50",
|
||||
"rsi_cross_overbought",
|
||||
"rsi_cross_oversold",
|
||||
"rsi_cross_50_up",
|
||||
"rsi_cross_50_down",
|
||||
"session_asian_utc",
|
||||
]
|
||||
|
||||
out = pd.DataFrame(rows, index=idx, columns=cols)
|
||||
return out.iloc[50:].copy()
|
||||
@@ -0,0 +1,128 @@
|
||||
"""
|
||||
Buy-low / sell-high style labels for OHLCV bars (no fixed SL/TP in labels).
|
||||
|
||||
H1 defaults scale M15 bar counts to ~similar wall-clock horizons:
|
||||
M15 horizon=32 -> 8h -> H1 horizon=8
|
||||
M15 local=24 -> 6h -> H1 local=6
|
||||
M15 pullback=20 -> 5h -> H1 pullback=5
|
||||
|
||||
Classes (integer, matches EA):
|
||||
0 HOLD
|
||||
1 BUY — forward upside vs ATR + local swing low
|
||||
2 SELL_SHORT — forward downside vs ATR + local swing high
|
||||
3 CLOSE_LONG — past-only: pullback from recent range high
|
||||
4 CLOSE_SHORT — past-only: bounce from recent range low
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def atr_series(df: pd.DataFrame, period: int = 14) -> pd.Series:
|
||||
high, low, close = df["high"], df["low"], df["close"]
|
||||
tr = pd.concat(
|
||||
[
|
||||
high - low,
|
||||
(high - close.shift()).abs(),
|
||||
(low - close.shift()).abs(),
|
||||
],
|
||||
axis=1,
|
||||
).max(axis=1)
|
||||
return tr.rolling(period).mean()
|
||||
|
||||
|
||||
def compute_action_labels(
|
||||
df: pd.DataFrame,
|
||||
*,
|
||||
horizon: int = 8,
|
||||
local_window: int = 6,
|
||||
pullback_window: int = 5,
|
||||
k_forward_atr: float = 0.75,
|
||||
local_pct: float = 0.28,
|
||||
pullback_mult: float = 0.55,
|
||||
trend_mult: float = 1.05,
|
||||
) -> pd.Series:
|
||||
"""
|
||||
Return a Series of int labels 0..4 aligned to df index.
|
||||
Last `horizon` rows → HOLD (no forward path for buy/sell scoring).
|
||||
"""
|
||||
close = df["close"].values
|
||||
high = df["high"].values
|
||||
low = df["low"].values
|
||||
n = len(df)
|
||||
atr = atr_series(df, 14).values
|
||||
labels = np.zeros(n, dtype=np.int64)
|
||||
|
||||
lw = local_window
|
||||
pw = pullback_window
|
||||
need = max(lw, pw) + 2
|
||||
|
||||
for t in range(n):
|
||||
if t < need or t >= n - horizon:
|
||||
labels[t] = 0
|
||||
continue
|
||||
|
||||
a = atr[t]
|
||||
if not np.isfinite(a) or a <= 0:
|
||||
a = close[t] * 1e-4
|
||||
|
||||
sl = low[t + 1 : t + horizon + 1]
|
||||
sh = high[t + 1 : t + horizon + 1]
|
||||
fwd_max = float(np.max(sh))
|
||||
fwd_min = float(np.min(sl))
|
||||
up_move = (fwd_max - close[t]) / a
|
||||
down_move = (close[t] - fwd_min) / a
|
||||
|
||||
loc_low = float(np.min(low[t - lw : t + 1]))
|
||||
loc_high = float(np.max(high[t - lw : t + 1]))
|
||||
rng = max(loc_high - loc_low, a * 0.15)
|
||||
near_low = (close[t] - loc_low) / rng <= local_pct
|
||||
near_high = (loc_high - close[t]) / rng <= local_pct
|
||||
|
||||
buy_sig = near_low and (up_move >= k_forward_atr) and (up_move >= down_move * 0.85)
|
||||
sell_sig = near_high and (down_move >= k_forward_atr) and (down_move > up_move * 1.05)
|
||||
|
||||
seg_h = high[t - pw : t + 1]
|
||||
seg_l = low[t - pw : t + 1]
|
||||
rh = float(np.max(seg_h))
|
||||
rl = float(np.min(seg_l))
|
||||
range_atr = (rh - rl) / a
|
||||
pull_from_high = (rh - close[t]) / a
|
||||
bounce_from_low = (close[t] - rl) / a
|
||||
|
||||
exit_long = (
|
||||
range_atr >= trend_mult
|
||||
and pull_from_high >= pullback_mult
|
||||
and close[t] < close[t - 1]
|
||||
)
|
||||
exit_short = (
|
||||
range_atr >= trend_mult
|
||||
and bounce_from_low >= pullback_mult
|
||||
and close[t] > close[t - 1]
|
||||
)
|
||||
|
||||
if exit_long and not buy_sig:
|
||||
labels[t] = 3
|
||||
elif exit_short and not sell_sig:
|
||||
labels[t] = 4
|
||||
elif buy_sig and not sell_sig:
|
||||
labels[t] = 1
|
||||
elif sell_sig and not buy_sig:
|
||||
labels[t] = 2
|
||||
elif buy_sig and sell_sig:
|
||||
labels[t] = 1 if up_move >= down_move else 2
|
||||
else:
|
||||
labels[t] = 0
|
||||
|
||||
return pd.Series(labels, index=df.index, name="action_label")
|
||||
|
||||
|
||||
def class_weights(y: np.ndarray, n_classes: int = 5) -> dict[int, float]:
|
||||
from sklearn.utils.class_weight import compute_class_weight
|
||||
|
||||
y_int = y.astype(int)
|
||||
classes = np.arange(n_classes)
|
||||
cw = compute_class_weight("balanced", classes=classes, y=y_int)
|
||||
return {i: float(cw[i]) for i in range(n_classes)}
|
||||
@@ -0,0 +1,209 @@
|
||||
"""
|
||||
XAUUSD H1 — ONNX action model (buy / sell short / close long / close short / hold).
|
||||
|
||||
Same 24 features as M15 stack; labels use H1-scaled horizons (~wall-clock parity with M15).
|
||||
Row order matches XAUUSD_H1_ActionEA.mq5 (row 0 = newest bar).
|
||||
Data: MT5, 2008–2026 (limited by downloaded history).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import pickle
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import MetaTrader5 as mt5
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import tensorflow as tf
|
||||
from sklearn.model_selection import train_test_split
|
||||
from sklearn.preprocessing import MinMaxScaler
|
||||
from tensorflow import keras
|
||||
from tensorflow.keras import layers
|
||||
from tqdm import tqdm
|
||||
import tf2onnx
|
||||
import onnx
|
||||
|
||||
from labeling import class_weights, compute_action_labels
|
||||
from features import NUM_FEATURES, prepare_features_full
|
||||
|
||||
NUM_CLASSES = 5
|
||||
CLASS_NAMES = ["HOLD", "BUY", "SELL_SHORT", "CLOSE_LONG", "CLOSE_SHORT"]
|
||||
|
||||
|
||||
def fetch_mt5_range(
|
||||
symbol: str,
|
||||
timeframe: int,
|
||||
start_date: datetime,
|
||||
end_date: datetime,
|
||||
) -> pd.DataFrame:
|
||||
if not mt5.initialize():
|
||||
raise RuntimeError(f"MT5 init failed: {mt5.last_error()}")
|
||||
|
||||
info = mt5.symbol_info(symbol)
|
||||
if info is None:
|
||||
mt5.shutdown()
|
||||
raise ValueError(f"Symbol {symbol} not found")
|
||||
if not info.visible and not mt5.symbol_select(symbol, True):
|
||||
mt5.shutdown()
|
||||
raise ValueError(f"Cannot select {symbol}")
|
||||
|
||||
all_rows: list[dict] = []
|
||||
chunk_days = 120
|
||||
cur = start_date
|
||||
while cur < end_date:
|
||||
chunk_end = min(cur + timedelta(days=chunk_days), end_date)
|
||||
rates = mt5.copy_rates_range(symbol, timeframe, cur, chunk_end)
|
||||
if rates is not None and len(rates) > 1:
|
||||
for row in rates:
|
||||
all_rows.append({n: row[n] for n in rates.dtype.names})
|
||||
cur = chunk_end
|
||||
|
||||
if not all_rows:
|
||||
mt5.shutdown()
|
||||
raise ValueError("No rates returned — download XAUUSD H1 in MT5 History Center")
|
||||
|
||||
df = pd.DataFrame(all_rows)
|
||||
df["time"] = pd.to_datetime(df["time"], unit="s")
|
||||
df = df.set_index("time").sort_index()
|
||||
df = df[~df.index.duplicated(keep="first")]
|
||||
return df
|
||||
|
||||
|
||||
def create_sequences(
|
||||
X: np.ndarray, y: np.ndarray, lookback: int
|
||||
) -> tuple[np.ndarray, np.ndarray]:
|
||||
xs, ys = [], []
|
||||
for i in tqdm(range(lookback - 1, len(X)), desc="sequences"):
|
||||
window = X[i - lookback + 1 : i + 1].copy()
|
||||
window = window[::-1]
|
||||
xs.append(window)
|
||||
ys.append(y[i])
|
||||
return np.asarray(xs, dtype=np.float32), np.asarray(ys, dtype=np.int64)
|
||||
|
||||
|
||||
def build_model(lookback: int, n_feat: int) -> keras.Model:
|
||||
inp = layers.Input(shape=(lookback, n_feat))
|
||||
x = layers.LSTM(96, return_sequences=True)(inp)
|
||||
x = layers.Dropout(0.25)(x)
|
||||
x = layers.LSTM(48)(x)
|
||||
x = layers.Dropout(0.25)(x)
|
||||
x = layers.Dense(32, activation="relu")(x)
|
||||
out = layers.Dense(NUM_CLASSES, activation="softmax", name="action_probs")(x)
|
||||
model = keras.Model(inp, out)
|
||||
model.compile(
|
||||
optimizer=keras.optimizers.Adam(1e-3),
|
||||
loss="sparse_categorical_crossentropy",
|
||||
metrics=["accuracy"],
|
||||
)
|
||||
return model
|
||||
|
||||
|
||||
def main() -> int:
|
||||
symbol = os.environ.get("XAU_SYMBOL", "XAUUSD")
|
||||
lookback = int(os.environ.get("XAU_H1_LOOKBACK", os.environ.get("XAU_LOOKBACK", "48")))
|
||||
epochs = int(os.environ.get("XAU_EPOCHS", "40"))
|
||||
batch_size = int(os.environ.get("XAU_BATCH", "64"))
|
||||
|
||||
start_date = datetime(2008, 1, 1)
|
||||
end_date = datetime(2026, 12, 31)
|
||||
|
||||
out_dir = os.path.join(os.path.dirname(__file__), "models")
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
onnx_path = os.path.join(out_dir, f"{symbol}_H1_action.onnx")
|
||||
meta_path = os.path.join(out_dir, f"{symbol}_H1_action_meta.json")
|
||||
|
||||
print("Fetching MT5 H1 data …")
|
||||
try:
|
||||
raw = fetch_mt5_range(symbol, mt5.TIMEFRAME_H1, start_date, end_date)
|
||||
finally:
|
||||
mt5.shutdown()
|
||||
print(f"Bars: {len(raw)} range: {raw.index[0]} → {raw.index[-1]}")
|
||||
|
||||
feat = prepare_features_full(raw)
|
||||
labels_full = compute_action_labels(raw)
|
||||
labels = labels_full.loc[feat.index]
|
||||
|
||||
y = labels.loc[feat.index].values.astype(np.int64)
|
||||
X_raw = feat.values.astype(np.float32)
|
||||
|
||||
valid = np.isfinite(X_raw).all(axis=1) & (y >= 0) & (y < NUM_CLASSES)
|
||||
X_raw = X_raw[valid]
|
||||
y = y[valid]
|
||||
|
||||
print("Label counts:", {CLASS_NAMES[i]: int((y == i).sum()) for i in range(NUM_CLASSES)})
|
||||
|
||||
scaler = MinMaxScaler()
|
||||
Xn = scaler.fit_transform(X_raw).astype(np.float32)
|
||||
|
||||
X_seq, y_seq = create_sequences(Xn, y, lookback)
|
||||
if len(X_seq) < 500:
|
||||
print("ERROR: Too few sequences — need more H1 history in MT5.")
|
||||
return 1
|
||||
|
||||
X_train, X_val, y_train, y_val = train_test_split(
|
||||
X_seq, y_seq, test_size=0.15, shuffle=False
|
||||
)
|
||||
|
||||
cw = class_weights(y_train, NUM_CLASSES)
|
||||
sample_w = np.array([cw[int(c)] for c in y_train], dtype=np.float32)
|
||||
|
||||
model = build_model(lookback, NUM_FEATURES)
|
||||
model.summary()
|
||||
|
||||
model.fit(
|
||||
X_train,
|
||||
y_train,
|
||||
sample_weight=sample_w,
|
||||
validation_data=(X_val, y_val),
|
||||
epochs=epochs,
|
||||
batch_size=batch_size,
|
||||
verbose=1,
|
||||
callbacks=[
|
||||
keras.callbacks.EarlyStopping(
|
||||
monitor="val_loss", patience=8, restore_best_weights=True
|
||||
),
|
||||
keras.callbacks.ReduceLROnPlateau(
|
||||
monitor="val_loss", factor=0.5, patience=4, min_lr=1e-6
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
spec = (tf.TensorSpec((None, lookback, NUM_FEATURES), tf.float32, name="input"),)
|
||||
onnx_m, _ = tf2onnx.convert.from_keras(model, input_signature=spec, opset=13)
|
||||
onnx.save_model(onnx_m, onnx_path)
|
||||
|
||||
with open(onnx_path.replace(".onnx", "_scaler.pkl"), "wb") as f:
|
||||
pickle.dump(scaler, f)
|
||||
|
||||
meta = {
|
||||
"symbol": symbol,
|
||||
"timeframe": "H1",
|
||||
"lookback": lookback,
|
||||
"num_features": int(NUM_FEATURES),
|
||||
"feature_columns": feat.columns.tolist(),
|
||||
"num_classes": NUM_CLASSES,
|
||||
"class_names": CLASS_NAMES,
|
||||
"label_horizon_bars": 8,
|
||||
"label_note": "H1 labeling defaults: horizon=8, local=6, pullback=5 (~M15 wall-clock parity)",
|
||||
"scaler_feature_min": scaler.data_min_.tolist(),
|
||||
"scaler_feature_max": scaler.data_max_.tolist(),
|
||||
"scaler_scale": scaler.scale_.tolist() if hasattr(scaler, "scale_") else None,
|
||||
"notes": "MinMax in EA; row0=newest. Match EA InpLookback to lookback here.",
|
||||
}
|
||||
with open(meta_path, "w", encoding="utf-8") as f:
|
||||
json.dump(meta, f, indent=2)
|
||||
|
||||
print(f"Saved: {onnx_path}")
|
||||
print(f"Meta: {meta_path}")
|
||||
print("\n--- Paste into EA InpFeatMinStr / InpFeatMaxStr (comma-separated, %d floats each) ---" % NUM_FEATURES)
|
||||
print(",".join(f"{x:.8g}" for x in scaler.data_min_))
|
||||
print(",".join(f"{x:.8g}" for x in scaler.data_max_))
|
||||
print(f"\nSet EA InpLookback = {lookback} (must match ONNX input dim).")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Binary file not shown.
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"symbol": "XAUUSD",
|
||||
"timeframe": "H1",
|
||||
"lookback": 48,
|
||||
"num_features": 24,
|
||||
"feature_columns": [
|
||||
"open",
|
||||
"high",
|
||||
"low",
|
||||
"close",
|
||||
"tick_volume",
|
||||
"rsi",
|
||||
"ema20_n",
|
||||
"ema50_n",
|
||||
"atr_n",
|
||||
"price_change",
|
||||
"high_low_ratio",
|
||||
"volume_ma",
|
||||
"volume_ratio",
|
||||
"rsi7_n",
|
||||
"rsi21_n",
|
||||
"rsi_fast_slow_spread",
|
||||
"rsi_velocity",
|
||||
"rsi_accel",
|
||||
"rsi_dist_mid_50",
|
||||
"rsi_cross_overbought",
|
||||
"rsi_cross_oversold",
|
||||
"rsi_cross_50_up",
|
||||
"rsi_cross_50_down",
|
||||
"session_asian_utc"
|
||||
],
|
||||
"num_classes": 5,
|
||||
"class_names": [
|
||||
"HOLD",
|
||||
"BUY",
|
||||
"SELL_SHORT",
|
||||
"CLOSE_LONG",
|
||||
"CLOSE_SHORT"
|
||||
],
|
||||
"label_horizon_bars": 8,
|
||||
"label_note": "H1 labeling defaults: horizon=8, local=6, pullback=5 (~M15 wall-clock parity)",
|
||||
"scaler_feature_min": [
|
||||
679.5499877929688,
|
||||
735.0499877929688,
|
||||
679.5499877929688,
|
||||
711.2999877929688,
|
||||
0.0,
|
||||
0.0778568685054779,
|
||||
-0.08346110582351685,
|
||||
-0.13061486184597015,
|
||||
0.0006287021678872406,
|
||||
-0.09134025126695633,
|
||||
1.0,
|
||||
0.0018113000551238656,
|
||||
0.0,
|
||||
0.020070146769285202,
|
||||
0.1127406507730484,
|
||||
-0.45013511180877686,
|
||||
-1.6049232482910156,
|
||||
-2.0694637298583984,
|
||||
1.0986201232299209e-05,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"scaler_feature_max": [
|
||||
5562.419921875,
|
||||
5598.06005859375,
|
||||
5554.68994140625,
|
||||
5562.43994140625,
|
||||
0.15629400312900543,
|
||||
0.9388294816017151,
|
||||
0.1516682505607605,
|
||||
0.17963257431983948,
|
||||
0.07575831562280655,
|
||||
0.10734681040048599,
|
||||
1.138908863067627,
|
||||
0.11270634829998016,
|
||||
11.032988548278809,
|
||||
0.9843139052391052,
|
||||
0.8853746056556702,
|
||||
0.44974473118782043,
|
||||
1.5471272468566895,
|
||||
2.1628992557525635,
|
||||
0.8776589632034302,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"scaler_scale": [
|
||||
0.00020479758677538484,
|
||||
0.00020563394355122,
|
||||
0.00020512231276370585,
|
||||
0.00020613710512407124,
|
||||
6.398198127746582,
|
||||
1.1614770889282227,
|
||||
4.2529778480529785,
|
||||
3.223233938217163,
|
||||
13.310330390930176,
|
||||
5.033040523529053,
|
||||
7.198964595794678,
|
||||
9.017535209655762,
|
||||
0.09063727408647537,
|
||||
1.0370821952819824,
|
||||
1.2942739725112915,
|
||||
1.1112594604492188,
|
||||
0.31725379824638367,
|
||||
0.23627464473247528,
|
||||
1.1394089460372925,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"notes": "MinMax in EA; row0=newest. Match EA InpLookback to lookback here."
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
numpy>=1.23
|
||||
pandas>=2.0
|
||||
MetaTrader5>=5.0.45
|
||||
tensorflow>=2.14
|
||||
tf2onnx>=1.16
|
||||
onnx>=1.15
|
||||
scikit-learn>=1.3
|
||||
tqdm>=4.66
|
||||
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
Dynamic adverse risk (conceptual mirror of EA InpMaxAdverseATR).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def adverse_hit_long(
|
||||
entry: float,
|
||||
low_path: np.ndarray,
|
||||
atr_path: np.ndarray,
|
||||
max_adverse_atr: float,
|
||||
) -> int | None:
|
||||
for i in range(len(low_path)):
|
||||
atr = max(atr_path[i], entry * 1e-6)
|
||||
adv = (entry - low_path[i]) / atr
|
||||
if adv >= max_adverse_atr:
|
||||
return i
|
||||
return None
|
||||
@@ -0,0 +1,43 @@
|
||||
# Frontline RSI 经验 → `ai/xauusd_m15` 特征映射
|
||||
|
||||
本文把 `frontline/MQL5/_united/Strategies` 里与 RSI 相关的**可量化**逻辑,映射到训练用的 **24 维特征**(前 13 维与原版 EA 一致,后 11 维为 RSI/时段扩展)。
|
||||
|
||||
## 策略来源与特征对应
|
||||
|
||||
| Frontline 模块 | 经验要点 | 模型中的体现 |
|
||||
|----------------|----------|----------------|
|
||||
| **RSIReversalAsianStrategy** | 上穿超买 / 下穿超卖的**交叉**;亚洲时段(UTC 0–8)语境 | `rsi_cross_overbought` / `rsi_cross_oversold`(默认 70/30);`session_asian_utc` |
|
||||
| **RSICrossOverReversalStrategy** | 超买/超卖区附近的**反转入场**、RSI 退出位 | 交叉特征 + `rsi_velocity` / `rsi_accel` 描述短期摆动 |
|
||||
| **RSIScalpingStrategy** | 极值区外的**回升/回落**(多根 RSI 结构) | `rsi_velocity`、`rsi_accel`(3 根 RSI14 近似) |
|
||||
| **RSIMidPointHijackStrategy** | 相对 **50** 中轴、快慢 RSI 状态 | `rsi_dist_mid_50`;`rsi_fast_slow_spread`(RSI14 vs RSI7) |
|
||||
| **多品种 RSI Scalping** | 更短周期敏感 | `rsi7_n`(快周期)、`rsi21_n`(慢周期) |
|
||||
|
||||
## 特征索引(与 Python / EA 顺序一致)
|
||||
|
||||
| 索引 | 名称 | 说明 |
|
||||
|------|------|------|
|
||||
| 0–4 | OHLC + tick_volume | 与原版一致 |
|
||||
| 5 | rsi | Wilder RSI(14)/100 |
|
||||
| 6–12 | EMA/ATR/价量 | 与原版一致 |
|
||||
| 13 | rsi7_n | RSI(7)/100 |
|
||||
| 14 | rsi21_n | RSI(21)/100 |
|
||||
| 15 | rsi_fast_slow_spread | clip((RSI14−RSI7)/50, −1, 1) |
|
||||
| 16 | rsi_velocity | (RSI14₀−RSI14₁)/25 |
|
||||
| 17 | rsi_accel | ((RSI14₀−RSI14₁)−(RSI14₁−RSI14₂))/25 |
|
||||
| 18 | rsi_dist_mid_50 | \|RSI14−50\|/50 |
|
||||
| 19–22 | cross_* | 0/1,与 frontline 交叉定义一致(上一根→当前根) |
|
||||
| 23 | session_asian_utc | 小时经偏移后 ∈ [0,8) 则为 1 |
|
||||
|
||||
## 时段偏移
|
||||
|
||||
MT5 K 线时间多为**服务器时区**。若要与 UTC 亚洲窗对齐,训练时设环境变量 `SESSION_HOUR_OFFSET`,EA 使用 `InpSessionHourOffset`,使 `(hour + offset) % 24` 与你在回测里认定的 UTC 一致。
|
||||
|
||||
## 未直接编码的规则(可后续扩展)
|
||||
|
||||
- **点差、最大持仓时长、Magic 分策略**:可作为额外标量特征或单独过滤层。
|
||||
- **RSIMidPoint 的「先标记超买再下穿退出线」**:可用连续两 bar 的 cross 组合特征或 LSTM 隐式学习;当前用 cross + dist_mid 近似。
|
||||
- **Darvas / EMA 等非 RSI 策略**:未并入本 ONNX 特征;可在 `features.py` 中追加列并同步改 `NUM_FEATURES` 与 EA。
|
||||
|
||||
## 再训练提醒
|
||||
|
||||
修改 `NUM_FEATURES` 后必须:**重新导出 ONNX**、更新 EA 中 `#resource` 模型、`OnnxSetInputShape` 第三维、**24 个 scaler min/max**。
|
||||
@@ -0,0 +1,49 @@
|
||||
# XAUUSD M15 — ONNX action model (buy / sell / close)
|
||||
|
||||
## What it does
|
||||
|
||||
- Pulls **XAUUSD** (**M15**) from **MetaTrader 5** (2008–2026 requested; actual range depends on History Center).
|
||||
- **24 features**: 13 legacy OHLC/EMA/ATR/volume + **11 RSI / session** features aligned with **frontline** strategies (crosses, velocity, RSI7/21, Asian window). See **`FRONTLINE_RSI_INTEGRATION.md`**.
|
||||
- Labels: **buy-low / sell-high** (forward window) + **close-long / close-short** (past-only). RSI enters as **inputs**, not as hard-coded label rules.
|
||||
- Trains **LSTM → softmax(5)**: `HOLD`, `BUY`, `SELL_SHORT`, `CLOSE_LONG`, `CLOSE_SHORT`.
|
||||
- Exports **`models/XAUUSD_M15_action.onnx`** + scaler + **`XAUUSD_M15_action_meta.json`** (includes `feature_columns`).
|
||||
- **EA**: **SL=0, TP=0**; **InpMaxAdverseATR**; **InpSessionHourOffset** should match training `SESSION_HOUR_OFFSET` for Asian flag.
|
||||
|
||||
This is research tooling — not investment advice. Past labels do not guarantee live performance.
|
||||
|
||||
## Setup
|
||||
|
||||
1. MT5 installed, logged in, **XAUUSD** visible; download **M15** history (Tools → History Center or chart scroll).
|
||||
2. Python 3.10+:
|
||||
|
||||
```bash
|
||||
cd ai/xauusd_m15
|
||||
pip install -r requirements.txt
|
||||
python main.py
|
||||
```
|
||||
|
||||
Optional env: `XAU_SYMBOL`, `XAU_LOOKBACK` (default 64), `XAU_EPOCHS`, `XAU_BATCH`, `SESSION_HOUR_OFFSET` (Asian session hour alignment vs server time).
|
||||
|
||||
3. Copy `models/XAUUSD_M15_action.onnx` to **`MQL5/Files/`** (same path as `#resource` in the EA).
|
||||
4. Open `XAUUSD_M15_ActionEA.mq5` in MetaEditor; compile.
|
||||
5. Paste two lines from training stdout into **InpFeatMinStr** and **InpFeatMaxStr** (comma-separated **24** floats each).
|
||||
|
||||
## ONNX I/O
|
||||
|
||||
- Input: `[1, lookback, 24]` float32, **row 0 = newest bar**.
|
||||
- Output: `[1, 5]` softmax probabilities.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `main.py` | Fetch, features, labels, train, ONNX + meta |
|
||||
| `features.py` | 24-dim pipeline + Wilder RSI |
|
||||
| `FRONTLINE_RSI_INTEGRATION.md` | frontline 策略 → 特征对照 |
|
||||
| `labeling.py` | `compute_action_labels` |
|
||||
| `risk_controls.py` | Adverse ATR helper for Python backtests |
|
||||
| `XAUUSD_M15_ActionEA.mq5` | Live inference + trading skeleton |
|
||||
|
||||
## Tuning labels
|
||||
|
||||
Edit parameters in `labeling.compute_action_labels()` (`horizon`, `k_forward_atr`, `pullback_mult`, etc.) and retrain.
|
||||
@@ -0,0 +1,375 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| XAUUSD_M15_ActionEA.mq5 |
|
||||
//| ONNX softmax [5]: HOLD, BUY, SELL_SHORT, CLOSE_LONG, CLOSE_SHORT |
|
||||
//| 24 features: base 13 + RSI/frontline (see FRONTLINE_RSI_*.md) |
|
||||
//| Exits: model CLOSE_* + optional InpTakeProfitATR; adverse ATR |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Profitable EA Project"
|
||||
#property version "1.03"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
#resource "XAUUSD_M15_action.onnx" as uchar ExtModel[]
|
||||
|
||||
#define FEAT_COUNT 24
|
||||
|
||||
input group "Model"
|
||||
input int InpLookback = 64;
|
||||
// 0 = legacy: require p(BUY)>=InpProbBuy and p(SELL)>=InpProbSell (use ~0.18 for 5-class softmax)
|
||||
// 1 = default: open only when directional prob beats HOLD (typical 5-way outputs ~0.15–0.25 each)
|
||||
input int InpEntryMode = 1;
|
||||
input double InpProbBuy = 0.18;
|
||||
input double InpProbSell = 0.18;
|
||||
input double InpMinBeatHold = 0.0; // mode 1: require max(p1,p2)-p0 >= this (e.g. 0.02)
|
||||
// Exit: 0 = p3/p4 >= thresholds (use ~0.18 for 5-class); 1 = CLOSE beats HOLD and beats add (p3>p1 / p4>p2)
|
||||
// 2 = default: CLOSE beats HOLD only (lets winners exit when pullback signal > hold; still weak in trends)
|
||||
input int InpExitMode = 2;
|
||||
input double InpProbCloseL = 0.18;
|
||||
input double InpProbCloseS = 0.18;
|
||||
input double InpMinCloseBeatHold = 0.0; // exit modes 1–2: require p3/p4 > p0 + this
|
||||
|
||||
input group "Session (match Python SESSION_HOUR_OFFSET)"
|
||||
input int InpSessionHourOffset = 0; // add to bar hour so Asian 0–8 matches training
|
||||
|
||||
input group "Scaler: paste 24 floats each from python main.py"
|
||||
input string InpFeatMinStr = "";
|
||||
input string InpFeatMaxStr = "";
|
||||
|
||||
input group "Risk"
|
||||
input double InpLotSize = 0.01;
|
||||
input int InpMagic = 902015;
|
||||
input int InpSlippage = 30;
|
||||
input double InpMaxAdverseATR = 2.0;
|
||||
input double InpTakeProfitATR = 0.0; // >0: close in profit when price move >= this * ATR(14) (banks winners)
|
||||
|
||||
double g_feat_min[FEAT_COUNT];
|
||||
double g_feat_max[FEAT_COUNT];
|
||||
|
||||
CTrade trade;
|
||||
long g_onnx = INVALID_HANDLE;
|
||||
datetime g_last_bar = 0;
|
||||
|
||||
void InitDefaultScalerBounds()
|
||||
{
|
||||
double def_min[FEAT_COUNT] = {
|
||||
0,0,0,0,0,0,-0.05,-0.05,0,-0.02,1.0,0,0.1,
|
||||
0,0,-1,-0.2,-0.2,0,0,0,0,0,0
|
||||
};
|
||||
double def_max[FEAT_COUNT] = {
|
||||
5000,5000,5000,5000,1,1,0.05,0.05,0.05,0.02,1.02,1,5.0,
|
||||
1,1,1,0.2,0.2,1,1,1,1,1,1
|
||||
};
|
||||
for(int i = 0; i < FEAT_COUNT; i++)
|
||||
{
|
||||
g_feat_min[i] = def_min[i];
|
||||
g_feat_max[i] = def_max[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool ParseFeatCsv(const string s, double &arr[])
|
||||
{
|
||||
if(StringLen(s) < 3) return false;
|
||||
string parts[];
|
||||
int n = StringSplit(s, ',', parts);
|
||||
if(n != FEAT_COUNT) return false;
|
||||
for(int i = 0; i < FEAT_COUNT; i++)
|
||||
arr[i] = StringToDouble(parts[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
InitDefaultScalerBounds();
|
||||
trade.SetExpertMagicNumber(InpMagic);
|
||||
trade.SetDeviationInPoints(InpSlippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
|
||||
if(StringLen(InpFeatMinStr) > 0 && ParseFeatCsv(InpFeatMinStr, g_feat_min))
|
||||
Print("Loaded InpFeatMinStr (24)");
|
||||
if(StringLen(InpFeatMaxStr) > 0 && ParseFeatCsv(InpFeatMaxStr, g_feat_max))
|
||||
Print("Loaded InpFeatMaxStr (24)");
|
||||
|
||||
g_onnx = OnnxCreateFromBuffer(ExtModel, ONNX_DEBUG_LOGS);
|
||||
if(g_onnx == INVALID_HANDLE)
|
||||
{
|
||||
Print("OnnxCreateFromBuffer failed ", GetLastError());
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
const long inShape[] = {1, InpLookback, FEAT_COUNT};
|
||||
if(!OnnxSetInputShape(g_onnx, 0, inShape))
|
||||
{
|
||||
Print("OnnxSetInputShape failed ", GetLastError());
|
||||
OnnxRelease(g_onnx);
|
||||
return INIT_FAILED;
|
||||
}
|
||||
const long outShape[] = {1, 5};
|
||||
if(!OnnxSetOutputShape(g_onnx, 0, outShape))
|
||||
{
|
||||
Print("OnnxSetOutputShape failed ", GetLastError());
|
||||
OnnxRelease(g_onnx);
|
||||
return INIT_FAILED;
|
||||
}
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int r)
|
||||
{
|
||||
if(g_onnx != INVALID_HANDLE) OnnxRelease(g_onnx);
|
||||
}
|
||||
|
||||
double AtrNow()
|
||||
{
|
||||
double b[];
|
||||
ArraySetAsSeries(b, true);
|
||||
int h = iATR(_Symbol, PERIOD_CURRENT, 14);
|
||||
if(h == INVALID_HANDLE) return 0;
|
||||
if(CopyBuffer(h, 0, 0, 2, b) < 1) { IndicatorRelease(h); return 0; }
|
||||
double v = b[0];
|
||||
IndicatorRelease(h);
|
||||
return v;
|
||||
}
|
||||
|
||||
bool AdverseExit(const long type, const double open_price)
|
||||
{
|
||||
double atr = AtrNow();
|
||||
if(atr <= 0) return false;
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
if(type == POSITION_TYPE_BUY)
|
||||
{
|
||||
double adv = (open_price - bid) / atr;
|
||||
return adv >= InpMaxAdverseATR;
|
||||
}
|
||||
double adv = (ask - open_price) / atr;
|
||||
return adv >= InpMaxAdverseATR;
|
||||
}
|
||||
|
||||
bool ProfitExit(const long type, const double open_price)
|
||||
{
|
||||
if(InpTakeProfitATR <= 0.0) return false;
|
||||
double atr = AtrNow();
|
||||
if(atr <= 0.0) return false;
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
if(type == POSITION_TYPE_BUY)
|
||||
return (bid - open_price) >= InpTakeProfitATR * atr;
|
||||
return (open_price - ask) >= InpTakeProfitATR * atr;
|
||||
}
|
||||
|
||||
bool ModelCloseLong(const double p0, const double p1, const double p3)
|
||||
{
|
||||
if(InpExitMode == 0)
|
||||
return (p3 >= InpProbCloseL);
|
||||
if(InpExitMode == 1)
|
||||
return (p3 > p0 + InpMinCloseBeatHold && p3 > p1);
|
||||
// mode 2: close-long probability beats hold (trends can still keep p1 high; use InpTakeProfitATR then)
|
||||
return (p3 > p0 + InpMinCloseBeatHold);
|
||||
}
|
||||
|
||||
bool ModelCloseShort(const double p0, const double p2, const double p4)
|
||||
{
|
||||
if(InpExitMode == 0)
|
||||
return (p4 >= InpProbCloseS);
|
||||
if(InpExitMode == 1)
|
||||
return (p4 > p0 + InpMinCloseBeatHold && p4 > p2);
|
||||
return (p4 > p0 + InpMinCloseBeatHold);
|
||||
}
|
||||
|
||||
void ScaleFeatures(const float &raw[], float &out[])
|
||||
{
|
||||
for(int f = 0; f < FEAT_COUNT; f++)
|
||||
{
|
||||
double den = g_feat_max[f] - g_feat_min[f];
|
||||
if(den < 1e-12) den = 1e-12;
|
||||
double x = (double)raw[f] - g_feat_min[f];
|
||||
out[f] = (float)MathMax(0.0, MathMin(1.0, x / den));
|
||||
}
|
||||
}
|
||||
|
||||
bool PrepareMatrix(matrixf &M)
|
||||
{
|
||||
int L = InpLookback;
|
||||
double open[], high[], low[], close[];
|
||||
long vol[];
|
||||
datetime bt[];
|
||||
ArraySetAsSeries(open, true);
|
||||
ArraySetAsSeries(high, true);
|
||||
ArraySetAsSeries(low, true);
|
||||
ArraySetAsSeries(close, true);
|
||||
ArraySetAsSeries(vol, true);
|
||||
ArraySetAsSeries(bt, true);
|
||||
|
||||
int need = L + 55;
|
||||
if(CopyOpen(_Symbol, PERIOD_CURRENT, 0, need, open) < L) return false;
|
||||
if(CopyHigh(_Symbol, PERIOD_CURRENT, 0, need, high) < L) return false;
|
||||
if(CopyLow(_Symbol, PERIOD_CURRENT, 0, need, low) < L) return false;
|
||||
if(CopyClose(_Symbol, PERIOD_CURRENT, 0, need, close) < L) return false;
|
||||
if(CopyTickVolume(_Symbol, PERIOD_CURRENT, 0, need, vol) < L) return false;
|
||||
if(CopyTime(_Symbol, PERIOD_CURRENT, 0, need, bt) < L) return false;
|
||||
|
||||
double rsi7[], rsi14[], rsi21[], ema20[], ema50[], atr[];
|
||||
ArraySetAsSeries(rsi7, true);
|
||||
ArraySetAsSeries(rsi14, true);
|
||||
ArraySetAsSeries(rsi21, true);
|
||||
ArraySetAsSeries(ema20, true);
|
||||
ArraySetAsSeries(ema50, true);
|
||||
ArraySetAsSeries(atr, true);
|
||||
|
||||
int h7 = iRSI(_Symbol, PERIOD_CURRENT, 7, PRICE_CLOSE);
|
||||
int h14 = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
|
||||
int h21 = iRSI(_Symbol, PERIOD_CURRENT, 21, PRICE_CLOSE);
|
||||
int hE20 = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_EMA, PRICE_CLOSE);
|
||||
int hE50 = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
|
||||
int hA = iATR(_Symbol, PERIOD_CURRENT, 14);
|
||||
if(h7 == INVALID_HANDLE || h14 == INVALID_HANDLE || h21 == INVALID_HANDLE ||
|
||||
hE20 == INVALID_HANDLE || hE50 == INVALID_HANDLE || hA == INVALID_HANDLE)
|
||||
return false;
|
||||
|
||||
if(CopyBuffer(h7, 0, 0, need, rsi7) < L ||
|
||||
CopyBuffer(h14, 0, 0, need, rsi14) < L ||
|
||||
CopyBuffer(h21, 0, 0, need, rsi21) < L ||
|
||||
CopyBuffer(hE20, 0, 0, need, ema20) < L ||
|
||||
CopyBuffer(hE50, 0, 0, need, ema50) < L ||
|
||||
CopyBuffer(hA, 0, 0, need, atr) < L)
|
||||
{
|
||||
IndicatorRelease(h7); IndicatorRelease(h14); IndicatorRelease(h21);
|
||||
IndicatorRelease(hE20); IndicatorRelease(hE50); IndicatorRelease(hA);
|
||||
return false;
|
||||
}
|
||||
IndicatorRelease(h7); IndicatorRelease(h14); IndicatorRelease(h21);
|
||||
IndicatorRelease(hE20); IndicatorRelease(hE50); IndicatorRelease(hA);
|
||||
|
||||
M.Resize(L, FEAT_COUNT);
|
||||
const double RSI_OB = 70.0;
|
||||
const double RSI_OS = 30.0;
|
||||
|
||||
for(int i = 0; i < L; i++)
|
||||
{
|
||||
double vma = 0;
|
||||
int cnt = 0;
|
||||
for(int k = i; k < i + 20 && k < ArraySize(vol); k++) { vma += (double)vol[k]; cnt++; }
|
||||
if(cnt < 1) cnt = 1;
|
||||
vma /= cnt;
|
||||
|
||||
double r0 = rsi14[i];
|
||||
double r1 = (i + 1 < ArraySize(rsi14)) ? rsi14[i + 1] : r0;
|
||||
double r2 = (i + 2 < ArraySize(rsi14)) ? rsi14[i + 2] : r1;
|
||||
double rv7 = rsi7[i];
|
||||
double rv21 = rsi21[i];
|
||||
|
||||
double spread = (r0 - rv7) / 50.0;
|
||||
if(spread > 1.0) spread = 1.0;
|
||||
if(spread < -1.0) spread = -1.0;
|
||||
double vel = (r0 - r1) / 25.0;
|
||||
double acc = ((r0 - r1) - (r1 - r2)) / 25.0;
|
||||
double dist_mid = MathAbs(r0 - 50.0) / 50.0;
|
||||
double c_ob = (r1 < RSI_OB && r0 >= RSI_OB) ? 1.0 : 0.0;
|
||||
double c_os = (r1 > RSI_OS && r0 <= RSI_OS) ? 1.0 : 0.0;
|
||||
double c50u = (r1 < 50.0 && r0 >= 50.0) ? 1.0 : 0.0;
|
||||
double c50d = (r1 > 50.0 && r0 <= 50.0) ? 1.0 : 0.0;
|
||||
|
||||
MqlDateTime st;
|
||||
TimeToStruct(bt[i], st);
|
||||
int hr = (st.hour + InpSessionHourOffset) % 24;
|
||||
if(hr < 0) hr += 24;
|
||||
double asian = (hr >= 0 && hr < 8) ? 1.0 : 0.0;
|
||||
|
||||
float raw[FEAT_COUNT];
|
||||
raw[0] = (float)open[i];
|
||||
raw[1] = (float)high[i];
|
||||
raw[2] = (float)low[i];
|
||||
raw[3] = (float)close[i];
|
||||
raw[4] = (float)((double)vol[i] / 1000000.0);
|
||||
raw[5] = (float)(r0 / 100.0);
|
||||
raw[6] = (float)((ema20[i] - close[i]) / close[i]);
|
||||
raw[7] = (float)((ema50[i] - close[i]) / close[i]);
|
||||
raw[8] = (float)(atr[i] / close[i]);
|
||||
double pc = (i < L - 1) ? (close[i] - close[i + 1]) / close[i + 1] : 0.0;
|
||||
raw[9] = (float)pc;
|
||||
raw[10] = (float)(high[i] / low[i]);
|
||||
raw[11] = (float)(vma / 1000000.0);
|
||||
raw[12] = (float)(vma > 0 ? (double)vol[i] / vma : 1.0);
|
||||
raw[13] = (float)(rv7 / 100.0);
|
||||
raw[14] = (float)(rv21 / 100.0);
|
||||
raw[15] = (float)spread;
|
||||
raw[16] = (float)vel;
|
||||
raw[17] = (float)acc;
|
||||
raw[18] = (float)dist_mid;
|
||||
raw[19] = (float)c_ob;
|
||||
raw[20] = (float)c_os;
|
||||
raw[21] = (float)c50u;
|
||||
raw[22] = (float)c50d;
|
||||
raw[23] = (float)asian;
|
||||
|
||||
float sc[FEAT_COUNT];
|
||||
ScaleFeatures(raw, sc);
|
||||
for(int j = 0; j < FEAT_COUNT; j++)
|
||||
M[i][j] = sc[j];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
datetime t = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||||
if(t == g_last_bar) return;
|
||||
g_last_bar = t;
|
||||
|
||||
matrixf Min;
|
||||
if(!PrepareMatrix(Min))
|
||||
{
|
||||
Print("PrepareMatrix failed");
|
||||
return;
|
||||
}
|
||||
|
||||
vectorf out;
|
||||
out.Resize(5);
|
||||
if(!OnnxRun(g_onnx, ONNX_NO_CONVERSION, Min, out))
|
||||
{
|
||||
Print("OnnxRun failed ", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
double p0 = out[0], p1 = out[1], p2 = out[2], p3 = out[3], p4 = out[4];
|
||||
Print("ONNX HOLD=", p0, " BUY=", p1, " SELL=", p2, " CL=", p3, " CS=", p4);
|
||||
|
||||
if(!PositionSelect(_Symbol))
|
||||
{
|
||||
if(InpEntryMode == 1)
|
||||
{
|
||||
double dir = MathMax(p1, p2);
|
||||
if(dir <= p0 + InpMinBeatHold)
|
||||
return;
|
||||
if(p1 >= p2 && p1 > p0 + InpMinBeatHold)
|
||||
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "AI BUY");
|
||||
else if(p2 > p1 && p2 > p0 + InpMinBeatHold)
|
||||
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "AI SELL");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(p1 >= InpProbBuy && p1 >= p2)
|
||||
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "AI BUY");
|
||||
else if(p2 >= InpProbSell && p2 > p1)
|
||||
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "AI SELL");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
long typ = (long)PositionGetInteger(POSITION_TYPE);
|
||||
double opn = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
if(AdverseExit(typ, opn))
|
||||
{
|
||||
trade.PositionClose(_Symbol);
|
||||
return;
|
||||
}
|
||||
if(ProfitExit(typ, opn))
|
||||
{
|
||||
trade.PositionClose(_Symbol);
|
||||
return;
|
||||
}
|
||||
if(typ == POSITION_TYPE_BUY && ModelCloseLong(p0, p1, p3))
|
||||
trade.PositionClose(_Symbol);
|
||||
else if(typ == POSITION_TYPE_SELL && ModelCloseShort(p0, p2, p4))
|
||||
trade.PositionClose(_Symbol);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
; XAUUSD_M15_ActionEA v1.03 — optimization preset
|
||||
; Copy to: MetaQuotes\Terminal\<ID>\MQL5\Profiles\Tester\
|
||||
; Strategy Tester → Inputs → context menu → Load
|
||||
;
|
||||
; Format: Name=value||optimize_start||step||stop||Y|N (Y = optimize this parameter)
|
||||
;
|
||||
; Model
|
||||
InpLookback=64||48||8||96||Y
|
||||
InpEntryMode=1||0||1||1||Y
|
||||
InpProbBuy=0.18||0.14||0.02||0.26||Y
|
||||
InpProbSell=0.18||0.14||0.02||0.26||Y
|
||||
InpMinBeatHold=0.0||0.0||0.01||0.05||Y
|
||||
InpExitMode=2||0||1||2||Y
|
||||
InpProbCloseL=0.18||0.14||0.02||0.26||Y
|
||||
InpProbCloseS=0.18||0.14||0.02||0.26||Y
|
||||
InpMinCloseBeatHold=0.0||0.0||0.01||0.04||Y
|
||||
; Session (match Python SESSION_HOUR_OFFSET)
|
||||
InpSessionHourOffset=0||-3||1||3||N
|
||||
; Scaler: paste 24 floats from python main.py (not optimizable)
|
||||
InpFeatMinStr=
|
||||
InpFeatMaxStr=
|
||||
; Risk
|
||||
InpLotSize=0.01||0.01||0.001000||0.100000||N
|
||||
InpMagic=902015||902015||1||9020150||N
|
||||
InpSlippage=30||30||1||300||N
|
||||
InpMaxAdverseATR=2.0||1.0||0.25||3.5||Y
|
||||
InpTakeProfitATR=0.0||0.0||0.25||3.0||Y
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,173 @@
|
||||
"""
|
||||
Feature pipeline: base 13 (EA-compatible) + 11 RSI / session features from frontline experience.
|
||||
|
||||
Frontline mapping (see FRONTLINE_RSI_INTEGRATION.md):
|
||||
- RSIReversalAsianStrategy / RSICrossOverReversal: cross OB/OS, cross 50
|
||||
- RSIScalpingStrategy: RSI velocity (bounce from extreme uses 3-bar structure → vel/acc)
|
||||
- RSIMidPointHijack: distance from 50, RSI(7) vs RSI(14) spread
|
||||
- Asian session gate → binary feature (hour window; offset for server vs UTC)
|
||||
|
||||
RSI uses Wilder smoothing (ewm alpha=1/period) to align with MT5 iRSI.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
NUM_BASE_FEATURES = 13
|
||||
NUM_RSI_EXTRA = 11
|
||||
NUM_FEATURES = NUM_BASE_FEATURES + NUM_RSI_EXTRA # 24
|
||||
|
||||
# Default thresholds aligned with common frontline inputs (Asian / scalping)
|
||||
RSI_OVERBOUGHT = 70.0
|
||||
RSI_OVERSOLD = 30.0
|
||||
|
||||
|
||||
def wilder_rsi(close: pd.Series, period: int) -> np.ndarray:
|
||||
"""Wilder RSI (matches MetaTrader iRSI closely)."""
|
||||
delta = close.diff()
|
||||
gain = delta.clip(lower=0.0)
|
||||
loss = (-delta).clip(lower=0.0)
|
||||
avg_g = gain.ewm(alpha=1.0 / period, min_periods=period, adjust=False).mean()
|
||||
avg_l = loss.ewm(alpha=1.0 / period, min_periods=period, adjust=False).mean()
|
||||
rs = avg_g / avg_l.replace(0, np.nan)
|
||||
rsi = 100.0 - (100.0 / (1.0 + rs))
|
||||
return rsi.fillna(50.0).to_numpy(dtype=np.float64)
|
||||
|
||||
|
||||
def prepare_features_full(
|
||||
df: pd.DataFrame,
|
||||
*,
|
||||
session_hour_offset: int | None = None,
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
Build (N, 24) feature table, chronological index matching df.
|
||||
Drops first ~50 rows (warmup) like the original pipeline.
|
||||
"""
|
||||
if session_hour_offset is None:
|
||||
session_hour_offset = int(os.environ.get("SESSION_HOUR_OFFSET", "0"))
|
||||
|
||||
o = df["open"].to_numpy(dtype=np.float64)
|
||||
h = df["high"].to_numpy(dtype=np.float64)
|
||||
l = df["low"].to_numpy(dtype=np.float64)
|
||||
c = df["close"].astype(float)
|
||||
vol = df["tick_volume"].to_numpy(dtype=np.float64)
|
||||
n = len(df)
|
||||
idx = df.index
|
||||
|
||||
rsi7 = wilder_rsi(c, 7)
|
||||
rsi14 = wilder_rsi(c, 14)
|
||||
rsi21 = wilder_rsi(c, 21)
|
||||
|
||||
ema20 = c.ewm(span=20, adjust=False).mean().to_numpy()
|
||||
ema50 = c.ewm(span=50, adjust=False).mean().to_numpy()
|
||||
|
||||
tr = np.maximum(
|
||||
h - l,
|
||||
np.maximum(np.abs(h - np.roll(c.to_numpy(), 1)), np.abs(l - np.roll(c.to_numpy(), 1))),
|
||||
)
|
||||
tr[0] = h[0] - l[0]
|
||||
atr = pd.Series(tr).rolling(14).mean().to_numpy()
|
||||
|
||||
vol_ma = np.zeros(n)
|
||||
for j in range(n):
|
||||
s = 0.0
|
||||
cnt = 0
|
||||
for k in range(j, min(j + 20, n)):
|
||||
s += vol[k]
|
||||
cnt += 1
|
||||
vol_ma[j] = s / cnt if cnt else vol[j]
|
||||
|
||||
pc_ea = np.zeros(n)
|
||||
cvals = c.to_numpy()
|
||||
for j in range(1, n):
|
||||
den = cvals[j - 1]
|
||||
pc_ea[j] = (cvals[j] - den) / den if den else 0.0
|
||||
|
||||
hours = np.zeros(n, dtype=np.int32)
|
||||
for j in range(n):
|
||||
ts = idx[j]
|
||||
try:
|
||||
hts = int(ts.hour)
|
||||
except Exception:
|
||||
hts = 0
|
||||
hours[j] = (hts + session_hour_offset) % 24
|
||||
|
||||
rows = []
|
||||
for j in range(n):
|
||||
r0 = rsi14[j]
|
||||
r1 = rsi14[j - 1] if j > 0 else r0
|
||||
r2 = rsi14[j - 2] if j > 1 else r1
|
||||
|
||||
spread = np.clip((r0 - rsi7[j]) / 50.0, -1.0, 1.0)
|
||||
vel = (r0 - r1) / 25.0
|
||||
acc = ((r0 - r1) - (r1 - r2)) / 25.0
|
||||
dist_mid = abs(r0 - 50.0) / 50.0
|
||||
|
||||
cross_ob = 1.0 if (r1 < RSI_OVERBOUGHT and r0 >= RSI_OVERBOUGHT) else 0.0
|
||||
cross_os = 1.0 if (r1 > RSI_OVERSOLD and r0 <= RSI_OVERSOLD) else 0.0
|
||||
cross_50_up = 1.0 if (r1 < 50.0 and r0 >= 50.0) else 0.0
|
||||
cross_50_dn = 1.0 if (r1 > 50.0 and r0 <= 50.0) else 0.0
|
||||
asian = 1.0 if (0 <= hours[j] < 8) else 0.0
|
||||
|
||||
rows.append(
|
||||
[
|
||||
float(o[j]),
|
||||
float(h[j]),
|
||||
float(l[j]),
|
||||
float(cvals[j]),
|
||||
float(vol[j] / 1_000_000.0),
|
||||
float(rsi14[j] / 100.0),
|
||||
float((ema20[j] - cvals[j]) / cvals[j]) if cvals[j] else 0.0,
|
||||
float((ema50[j] - cvals[j]) / cvals[j]) if cvals[j] else 0.0,
|
||||
float(atr[j] / cvals[j]) if cvals[j] else 0.0,
|
||||
float(pc_ea[j]),
|
||||
float(h[j] / l[j]) if l[j] else 1.0,
|
||||
float(vol_ma[j] / 1_000_000.0),
|
||||
float(vol[j] / vol_ma[j]) if vol_ma[j] > 0 else 1.0,
|
||||
float(rsi7[j] / 100.0),
|
||||
float(rsi21[j] / 100.0),
|
||||
float(spread),
|
||||
float(vel),
|
||||
float(acc),
|
||||
float(dist_mid),
|
||||
float(cross_ob),
|
||||
float(cross_os),
|
||||
float(cross_50_up),
|
||||
float(cross_50_dn),
|
||||
float(asian),
|
||||
]
|
||||
)
|
||||
|
||||
cols = [
|
||||
"open",
|
||||
"high",
|
||||
"low",
|
||||
"close",
|
||||
"tick_volume",
|
||||
"rsi",
|
||||
"ema20_n",
|
||||
"ema50_n",
|
||||
"atr_n",
|
||||
"price_change",
|
||||
"high_low_ratio",
|
||||
"volume_ma",
|
||||
"volume_ratio",
|
||||
"rsi7_n",
|
||||
"rsi21_n",
|
||||
"rsi_fast_slow_spread",
|
||||
"rsi_velocity",
|
||||
"rsi_accel",
|
||||
"rsi_dist_mid_50",
|
||||
"rsi_cross_overbought",
|
||||
"rsi_cross_oversold",
|
||||
"rsi_cross_50_up",
|
||||
"rsi_cross_50_down",
|
||||
"session_asian_utc",
|
||||
]
|
||||
|
||||
out = pd.DataFrame(rows, index=idx, columns=cols)
|
||||
return out.iloc[50:].copy()
|
||||
@@ -0,0 +1,130 @@
|
||||
"""
|
||||
Buy-low / sell-high style labels for OHLCV bars (no fixed SL/TP in labels).
|
||||
|
||||
Optional context: frontline RSI strategies (Asian reversal, scalping, mid-50)
|
||||
are encoded as *features* in features.py (crosses, velocity, session), not as
|
||||
hard rules here — the network learns joint patterns with price/volume.
|
||||
|
||||
Classes (integer, matches EA):
|
||||
0 HOLD
|
||||
1 BUY — forward upside vs ATR + local swing low
|
||||
2 SELL_SHORT — forward downside vs ATR + local swing high
|
||||
3 CLOSE_LONG — past-only: pullback from recent range high
|
||||
4 CLOSE_SHORT — past-only: bounce from recent range low
|
||||
|
||||
CLOSE_* use only bars <= t (no future leak).
|
||||
BUY/SELL use forward window [t+1, t+horizon] (supervised targets).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def atr_series(df: pd.DataFrame, period: int = 14) -> pd.Series:
|
||||
high, low, close = df["high"], df["low"], df["close"]
|
||||
tr = pd.concat(
|
||||
[
|
||||
high - low,
|
||||
(high - close.shift()).abs(),
|
||||
(low - close.shift()).abs(),
|
||||
],
|
||||
axis=1,
|
||||
).max(axis=1)
|
||||
return tr.rolling(period).mean()
|
||||
|
||||
|
||||
def compute_action_labels(
|
||||
df: pd.DataFrame,
|
||||
*,
|
||||
horizon: int = 32,
|
||||
local_window: int = 24,
|
||||
pullback_window: int = 20,
|
||||
k_forward_atr: float = 0.75,
|
||||
local_pct: float = 0.28,
|
||||
pullback_mult: float = 0.55,
|
||||
trend_mult: float = 1.05,
|
||||
) -> pd.Series:
|
||||
"""
|
||||
Return a Series of int labels 0..4 aligned to df index.
|
||||
Last `horizon` rows → HOLD (no forward path for buy/sell scoring).
|
||||
"""
|
||||
close = df["close"].values
|
||||
high = df["high"].values
|
||||
low = df["low"].values
|
||||
n = len(df)
|
||||
atr = atr_series(df, 14).values
|
||||
labels = np.zeros(n, dtype=np.int64)
|
||||
|
||||
lw = local_window
|
||||
pw = pullback_window
|
||||
need = max(lw, pw) + 2
|
||||
|
||||
for t in range(n):
|
||||
if t < need or t >= n - horizon:
|
||||
labels[t] = 0
|
||||
continue
|
||||
|
||||
a = atr[t]
|
||||
if not np.isfinite(a) or a <= 0:
|
||||
a = close[t] * 1e-4
|
||||
|
||||
sl = low[t + 1 : t + horizon + 1]
|
||||
sh = high[t + 1 : t + horizon + 1]
|
||||
fwd_max = float(np.max(sh))
|
||||
fwd_min = float(np.min(sl))
|
||||
up_move = (fwd_max - close[t]) / a
|
||||
down_move = (close[t] - fwd_min) / a
|
||||
|
||||
loc_low = float(np.min(low[t - lw : t + 1]))
|
||||
loc_high = float(np.max(high[t - lw : t + 1]))
|
||||
rng = max(loc_high - loc_low, a * 0.15)
|
||||
near_low = (close[t] - loc_low) / rng <= local_pct
|
||||
near_high = (loc_high - close[t]) / rng <= local_pct
|
||||
|
||||
buy_sig = near_low and (up_move >= k_forward_atr) and (up_move >= down_move * 0.85)
|
||||
sell_sig = near_high and (down_move >= k_forward_atr) and (down_move > up_move * 1.05)
|
||||
|
||||
# Past window [t-pw, t]
|
||||
seg_h = high[t - pw : t + 1]
|
||||
seg_l = low[t - pw : t + 1]
|
||||
rh = float(np.max(seg_h))
|
||||
rl = float(np.min(seg_l))
|
||||
range_atr = (rh - rl) / a
|
||||
pull_from_high = (rh - close[t]) / a
|
||||
bounce_from_low = (close[t] - rl) / a
|
||||
|
||||
exit_long = (
|
||||
range_atr >= trend_mult
|
||||
and pull_from_high >= pullback_mult
|
||||
and close[t] < close[t - 1]
|
||||
)
|
||||
exit_short = (
|
||||
range_atr >= trend_mult
|
||||
and bounce_from_low >= pullback_mult
|
||||
and close[t] > close[t - 1]
|
||||
)
|
||||
|
||||
if exit_long and not buy_sig:
|
||||
labels[t] = 3
|
||||
elif exit_short and not sell_sig:
|
||||
labels[t] = 4
|
||||
elif buy_sig and not sell_sig:
|
||||
labels[t] = 1
|
||||
elif sell_sig and not buy_sig:
|
||||
labels[t] = 2
|
||||
elif buy_sig and sell_sig:
|
||||
labels[t] = 1 if up_move >= down_move else 2
|
||||
else:
|
||||
labels[t] = 0
|
||||
|
||||
return pd.Series(labels, index=df.index, name="action_label")
|
||||
|
||||
|
||||
def class_weights(y: np.ndarray, n_classes: int = 5) -> dict[int, float]:
|
||||
from sklearn.utils.class_weight import compute_class_weight
|
||||
|
||||
y_int = y.astype(int)
|
||||
classes = np.arange(n_classes)
|
||||
cw = compute_class_weight("balanced", classes=classes, y=y_int)
|
||||
return {i: float(cw[i]) for i in range(n_classes)}
|
||||
@@ -0,0 +1,210 @@
|
||||
"""
|
||||
XAUUSD M15 — ONNX action model (buy / sell short / close long / close short / hold).
|
||||
|
||||
Features: 24 dims — base 13 + RSI/frontline stack (see features.py, FRONTLINE_RSI_INTEGRATION.md).
|
||||
Row order matches XAUUSD_M15_ActionEA.mq5 (row 0 = newest bar).
|
||||
Data: MT5, 2008–2026 (limited by downloaded history).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import pickle
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import MetaTrader5 as mt5
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import tensorflow as tf
|
||||
from sklearn.model_selection import train_test_split
|
||||
from sklearn.preprocessing import MinMaxScaler
|
||||
from tensorflow import keras
|
||||
from tensorflow.keras import layers
|
||||
from tqdm import tqdm
|
||||
import tf2onnx
|
||||
import onnx
|
||||
|
||||
from labeling import class_weights, compute_action_labels
|
||||
from features import NUM_FEATURES, prepare_features_full
|
||||
|
||||
NUM_CLASSES = 5
|
||||
CLASS_NAMES = ["HOLD", "BUY", "SELL_SHORT", "CLOSE_LONG", "CLOSE_SHORT"]
|
||||
|
||||
|
||||
def fetch_mt5_range(
|
||||
symbol: str,
|
||||
timeframe: int,
|
||||
start_date: datetime,
|
||||
end_date: datetime,
|
||||
) -> pd.DataFrame:
|
||||
if not mt5.initialize():
|
||||
raise RuntimeError(f"MT5 init failed: {mt5.last_error()}")
|
||||
|
||||
info = mt5.symbol_info(symbol)
|
||||
if info is None:
|
||||
mt5.shutdown()
|
||||
raise ValueError(f"Symbol {symbol} not found")
|
||||
if not info.visible and not mt5.symbol_select(symbol, True):
|
||||
mt5.shutdown()
|
||||
raise ValueError(f"Cannot select {symbol}")
|
||||
|
||||
all_rows: list[dict] = []
|
||||
chunk_days = 30
|
||||
cur = start_date
|
||||
while cur < end_date:
|
||||
chunk_end = min(cur + timedelta(days=chunk_days), end_date)
|
||||
rates = mt5.copy_rates_range(symbol, timeframe, cur, chunk_end)
|
||||
if rates is not None and len(rates) > 1:
|
||||
for row in rates:
|
||||
all_rows.append({n: row[n] for n in rates.dtype.names})
|
||||
cur = chunk_end
|
||||
|
||||
if not all_rows:
|
||||
mt5.shutdown()
|
||||
raise ValueError("No rates returned — download XAUUSD M15 in MT5 History Center")
|
||||
|
||||
df = pd.DataFrame(all_rows)
|
||||
df["time"] = pd.to_datetime(df["time"], unit="s")
|
||||
df = df.set_index("time").sort_index()
|
||||
df = df[~df.index.duplicated(keep="first")]
|
||||
return df
|
||||
|
||||
|
||||
def create_sequences(
|
||||
X: np.ndarray, y: np.ndarray, lookback: int
|
||||
) -> tuple[np.ndarray, np.ndarray]:
|
||||
"""
|
||||
Window ends at bar i (chronological). Rows: newest-first inside each window
|
||||
(matches MT5 series arrays in EA).
|
||||
"""
|
||||
xs, ys = [], []
|
||||
for i in tqdm(range(lookback - 1, len(X)), desc="sequences"):
|
||||
window = X[i - lookback + 1 : i + 1].copy()
|
||||
window = window[::-1] # newest bar first → same as EA matrix row 0
|
||||
xs.append(window)
|
||||
ys.append(y[i])
|
||||
return np.asarray(xs, dtype=np.float32), np.asarray(ys, dtype=np.int64)
|
||||
|
||||
|
||||
def build_model(lookback: int, n_feat: int) -> keras.Model:
|
||||
inp = layers.Input(shape=(lookback, n_feat))
|
||||
x = layers.LSTM(96, return_sequences=True)(inp)
|
||||
x = layers.Dropout(0.25)(x)
|
||||
x = layers.LSTM(48)(x)
|
||||
x = layers.Dropout(0.25)(x)
|
||||
x = layers.Dense(32, activation="relu")(x)
|
||||
out = layers.Dense(NUM_CLASSES, activation="softmax", name="action_probs")(x)
|
||||
model = keras.Model(inp, out)
|
||||
model.compile(
|
||||
optimizer=keras.optimizers.Adam(1e-3),
|
||||
loss="sparse_categorical_crossentropy",
|
||||
metrics=["accuracy"],
|
||||
)
|
||||
return model
|
||||
|
||||
|
||||
def main() -> int:
|
||||
symbol = os.environ.get("XAU_SYMBOL", "XAUUSD")
|
||||
lookback = int(os.environ.get("XAU_LOOKBACK", "64"))
|
||||
epochs = int(os.environ.get("XAU_EPOCHS", "40"))
|
||||
batch_size = int(os.environ.get("XAU_BATCH", "64"))
|
||||
|
||||
start_date = datetime(2008, 1, 1)
|
||||
end_date = datetime(2026, 12, 31)
|
||||
|
||||
out_dir = os.path.join(os.path.dirname(__file__), "models")
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
onnx_path = os.path.join(out_dir, f"{symbol}_M15_action.onnx")
|
||||
meta_path = os.path.join(out_dir, f"{symbol}_M15_action_meta.json")
|
||||
|
||||
print("Fetching MT5 data …")
|
||||
try:
|
||||
raw = fetch_mt5_range(symbol, mt5.TIMEFRAME_M15, start_date, end_date)
|
||||
finally:
|
||||
mt5.shutdown()
|
||||
print(f"Bars: {len(raw)} range: {raw.index[0]} → {raw.index[-1]}")
|
||||
|
||||
feat = prepare_features_full(raw)
|
||||
labels_full = compute_action_labels(raw)
|
||||
labels = labels_full.loc[feat.index]
|
||||
|
||||
y = labels.loc[feat.index].values.astype(np.int64)
|
||||
X_raw = feat.values.astype(np.float32)
|
||||
|
||||
valid = np.isfinite(X_raw).all(axis=1) & (y >= 0) & (y < NUM_CLASSES)
|
||||
X_raw = X_raw[valid]
|
||||
y = y[valid]
|
||||
|
||||
print("Label counts:", {CLASS_NAMES[i]: int((y == i).sum()) for i in range(NUM_CLASSES)})
|
||||
|
||||
scaler = MinMaxScaler()
|
||||
Xn = scaler.fit_transform(X_raw).astype(np.float32)
|
||||
|
||||
X_seq, y_seq = create_sequences(Xn, y, lookback)
|
||||
if len(X_seq) < 500:
|
||||
print("ERROR: Too few sequences — need more M15 history in MT5.")
|
||||
return 1
|
||||
|
||||
X_train, X_val, y_train, y_val = train_test_split(
|
||||
X_seq, y_seq, test_size=0.15, shuffle=False
|
||||
)
|
||||
|
||||
cw = class_weights(y_train, NUM_CLASSES)
|
||||
sample_w = np.array([cw[int(c)] for c in y_train], dtype=np.float32)
|
||||
|
||||
model = build_model(lookback, NUM_FEATURES)
|
||||
model.summary()
|
||||
|
||||
model.fit(
|
||||
X_train,
|
||||
y_train,
|
||||
sample_weight=sample_w,
|
||||
validation_data=(X_val, y_val),
|
||||
epochs=epochs,
|
||||
batch_size=batch_size,
|
||||
verbose=1,
|
||||
callbacks=[
|
||||
keras.callbacks.EarlyStopping(
|
||||
monitor="val_loss", patience=8, restore_best_weights=True
|
||||
),
|
||||
keras.callbacks.ReduceLROnPlateau(
|
||||
monitor="val_loss", factor=0.5, patience=4, min_lr=1e-6
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
spec = (tf.TensorSpec((None, lookback, NUM_FEATURES), tf.float32, name="input"),)
|
||||
onnx_m, _ = tf2onnx.convert.from_keras(model, input_signature=spec, opset=13)
|
||||
onnx.save_model(onnx_m, onnx_path)
|
||||
|
||||
with open(onnx_path.replace(".onnx", "_scaler.pkl"), "wb") as f:
|
||||
pickle.dump(scaler, f)
|
||||
|
||||
meta = {
|
||||
"symbol": symbol,
|
||||
"timeframe": "M15",
|
||||
"lookback": lookback,
|
||||
"num_features": int(NUM_FEATURES),
|
||||
"feature_columns": feat.columns.tolist(),
|
||||
"num_classes": NUM_CLASSES,
|
||||
"class_names": CLASS_NAMES,
|
||||
"scaler_feature_min": scaler.data_min_.tolist(),
|
||||
"scaler_feature_max": scaler.data_max_.tolist(),
|
||||
"scaler_scale": scaler.scale_.tolist() if hasattr(scaler, "scale_") else None,
|
||||
"notes": "MinMax in EA; row0=newest. See FRONTLINE_RSI_INTEGRATION.md.",
|
||||
}
|
||||
with open(meta_path, "w", encoding="utf-8") as f:
|
||||
json.dump(meta, f, indent=2)
|
||||
|
||||
print(f"Saved: {onnx_path}")
|
||||
print(f"Meta: {meta_path}")
|
||||
print("\n--- Paste into EA InpFeatMinStr / InpFeatMaxStr (comma-separated, %d floats each) ---" % NUM_FEATURES)
|
||||
print(",".join(f"{x:.8g}" for x in scaler.data_min_))
|
||||
print(",".join(f"{x:.8g}" for x in scaler.data_max_))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Binary file not shown.
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"symbol": "XAUUSD",
|
||||
"timeframe": "M15",
|
||||
"lookback": 64,
|
||||
"num_features": 24,
|
||||
"feature_columns": [
|
||||
"open",
|
||||
"high",
|
||||
"low",
|
||||
"close",
|
||||
"tick_volume",
|
||||
"rsi",
|
||||
"ema20_n",
|
||||
"ema50_n",
|
||||
"atr_n",
|
||||
"price_change",
|
||||
"high_low_ratio",
|
||||
"volume_ma",
|
||||
"volume_ratio",
|
||||
"rsi7_n",
|
||||
"rsi21_n",
|
||||
"rsi_fast_slow_spread",
|
||||
"rsi_velocity",
|
||||
"rsi_accel",
|
||||
"rsi_dist_mid_50",
|
||||
"rsi_cross_overbought",
|
||||
"rsi_cross_oversold",
|
||||
"rsi_cross_50_up",
|
||||
"rsi_cross_50_down",
|
||||
"session_asian_utc"
|
||||
],
|
||||
"num_classes": 5,
|
||||
"class_names": [
|
||||
"HOLD",
|
||||
"BUY",
|
||||
"SELL_SHORT",
|
||||
"CLOSE_LONG",
|
||||
"CLOSE_SHORT"
|
||||
],
|
||||
"scaler_feature_min": [
|
||||
1616.6700439453125,
|
||||
1618.8499755859375,
|
||||
1614.8199462890625,
|
||||
1616.6800537109375,
|
||||
0.0,
|
||||
0.08494461327791214,
|
||||
-0.03888450935482979,
|
||||
-0.0373079888522625,
|
||||
0.0001997762155951932,
|
||||
-0.036351919174194336,
|
||||
1.0,
|
||||
0.00017494999337941408,
|
||||
0.0,
|
||||
0.02623281255364418,
|
||||
0.12661120295524597,
|
||||
-0.5349156260490417,
|
||||
-2.0385215282440186,
|
||||
-2.397653818130493,
|
||||
2.298711478943005e-06,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"scaler_feature_max": [
|
||||
5585.740234375,
|
||||
5598.06005859375,
|
||||
5577.580078125,
|
||||
5585.740234375,
|
||||
0.013647999614477158,
|
||||
0.9493793845176697,
|
||||
0.06255777180194855,
|
||||
0.06892234832048416,
|
||||
0.01657661236822605,
|
||||
0.04392698407173157,
|
||||
1.056401252746582,
|
||||
0.010342299938201904,
|
||||
5.7435832023620605,
|
||||
0.9802423715591431,
|
||||
0.9216755032539368,
|
||||
0.5254908204078674,
|
||||
1.544172763824463,
|
||||
2.009221076965332,
|
||||
0.8987588286399841,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"scaler_scale": [
|
||||
0.00025194816407747567,
|
||||
0.0002513061626814306,
|
||||
0.00025234936038032174,
|
||||
0.00025194883346557617,
|
||||
73.27081298828125,
|
||||
1.156825304031372,
|
||||
9.85782241821289,
|
||||
9.413507461547852,
|
||||
61.06185531616211,
|
||||
12.456572532653809,
|
||||
17.7301025390625,
|
||||
98.35404205322266,
|
||||
0.17410734295845032,
|
||||
1.0482075214385986,
|
||||
1.2577598094940186,
|
||||
0.9430346488952637,
|
||||
0.2791195511817932,
|
||||
0.22691819071769714,
|
||||
1.112648367881775,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"notes": "MinMax in EA; row0=newest. See FRONTLINE_RSI_INTEGRATION.md."
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
numpy>=1.23
|
||||
pandas>=2.0
|
||||
MetaTrader5>=5.0.45
|
||||
tensorflow>=2.14
|
||||
tf2onnx>=1.16
|
||||
onnx>=1.15
|
||||
scikit-learn>=1.3
|
||||
tqdm>=4.66
|
||||
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
Dynamic adverse risk (conceptual mirror of EA InpMaxAdverseATR).
|
||||
|
||||
For backtests in Python: given entry price, ATR series, and bid/ask path,
|
||||
exit when (entry - bid)/atr >= max_adv for long.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def adverse_hit_long(
|
||||
entry: float,
|
||||
low_path: np.ndarray,
|
||||
atr_path: np.ndarray,
|
||||
max_adverse_atr: float,
|
||||
) -> int | None:
|
||||
"""Return first index where adverse >= threshold, else None."""
|
||||
for i in range(len(low_path)):
|
||||
atr = max(atr_path[i], entry * 1e-6)
|
||||
adv = (entry - low_path[i]) / atr
|
||||
if adv >= max_adverse_atr:
|
||||
return i
|
||||
return None
|
||||
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
@@ -0,0 +1,508 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSI_SecretSauce_XAUUSD.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.01"
|
||||
#property description "RSI Secret Sauce Strategy: Wait for RSI to leave 70/30 zone, then enter when it comes back in"
|
||||
#property description "Based on momentum flip concept - not traditional overbought/oversold"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include <Trade\PositionInfo.mqh>
|
||||
|
||||
//--- Input Parameters
|
||||
input group "=== Trading Settings ==="
|
||||
input string InpSymbol = "XAUUSD"; // Trading Symbol (set was tuned on BTCUSD)
|
||||
input double InpLotSize = 0.1; // Lot Size (Profiles/Tester/secret_sauce.set)
|
||||
input int InpMagicNumber = 789012; // Magic Number
|
||||
input int InpSlippage = 10; // Slippage in points
|
||||
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M30; // Trading Timeframe (set value 30 = M30)
|
||||
|
||||
input group "=== RSI Settings ==="
|
||||
input int InpRSIPeriod = 16; // RSI Period
|
||||
input double InpRSIOverbought = 72.5; // RSI Overbought Level
|
||||
input double InpRSIOversold = 32.5; // RSI Oversold Level
|
||||
input int InpRSILookback = 60; // RSI Lookback for Peak/Bottom Detection
|
||||
|
||||
input group "=== Entry Logic ==="
|
||||
input int InpPeakBars = 2; // Bars to confirm peak/bottom
|
||||
input bool InpRequireDivergence = false; // Require divergence confirmation (optional)
|
||||
|
||||
input group "=== Risk Management ==="
|
||||
input double InpStopLossATR = 2.75; // Stop Loss (ATR multiples)
|
||||
input double InpTakeProfitATR = 5.0; // Take Profit (ATR multiples)
|
||||
input int InpATRPeriod = 14; // ATR Period
|
||||
input bool InpUseSwingStopLoss = false; // Use previous swing high/low for stop loss
|
||||
input int InpSwingLookback = 30; // Bars to look back for swing points
|
||||
|
||||
input group "=== Position Management ==="
|
||||
input int InpMaxPositions = 1; // Max Simultaneous Positions
|
||||
input int InpMinBarsBetweenTrades = 7; // Min Bars Between Trades
|
||||
|
||||
//--- Global Variables
|
||||
CTrade trade;
|
||||
CPositionInfo positionInfo;
|
||||
|
||||
string actualSymbol;
|
||||
int rsiHandle = INVALID_HANDLE;
|
||||
int atrHandle = INVALID_HANDLE;
|
||||
|
||||
double rsiBuffer[];
|
||||
double atrBuffer[];
|
||||
double highBuffer[];
|
||||
double lowBuffer[];
|
||||
|
||||
// RSI state tracking
|
||||
bool rsiWasOverbought = false; // RSI was above 70
|
||||
bool rsiWasOversold = false; // RSI was below 30
|
||||
bool rsiBackInRange = false; // RSI came back into range
|
||||
datetime lastRSIExitTime = 0; // When RSI left the range
|
||||
datetime lastRSIReentryTime = 0; // When RSI came back in
|
||||
|
||||
// Trade tracking
|
||||
datetime lastTradeTime = 0;
|
||||
int barsSinceLastTrade = 0;
|
||||
|
||||
datetime lastBarTime = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Determine actual symbol
|
||||
if(InpSymbol == "" || InpSymbol == NULL)
|
||||
actualSymbol = _Symbol;
|
||||
else
|
||||
actualSymbol = InpSymbol;
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolInfoInteger(actualSymbol, SYMBOL_SELECT))
|
||||
{
|
||||
Print("Error: Symbol ", actualSymbol, " not found. Using chart symbol.");
|
||||
actualSymbol = _Symbol;
|
||||
}
|
||||
|
||||
// Initialize RSI indicator
|
||||
rsiHandle = iRSI(actualSymbol, InpTimeframe, InpRSIPeriod, PRICE_CLOSE);
|
||||
if(rsiHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Error creating RSI indicator");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
ArraySetAsSeries(rsiBuffer, true);
|
||||
|
||||
// Initialize ATR indicator
|
||||
atrHandle = iATR(actualSymbol, InpTimeframe, InpATRPeriod);
|
||||
if(atrHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Error creating ATR indicator");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
ArraySetAsSeries(atrBuffer, true);
|
||||
|
||||
// Initialize price buffers
|
||||
ArraySetAsSeries(highBuffer, true);
|
||||
ArraySetAsSeries(lowBuffer, true);
|
||||
|
||||
// Set trade parameters
|
||||
trade.SetExpertMagicNumber(InpMagicNumber);
|
||||
trade.SetDeviationInPoints(InpSlippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
Print("=== RSI Secret Sauce Strategy Initialized ===");
|
||||
Print("Symbol: ", actualSymbol);
|
||||
Print("Timeframe: ", EnumToString(InpTimeframe));
|
||||
Print("RSI Period: ", InpRSIPeriod, " | Overbought: ", InpRSIOverbought, " | Oversold: ", InpRSIOversold);
|
||||
Print("Stop Loss: ", InpStopLossATR, "x ATR | Take Profit: ", InpTakeProfitATR, "x ATR");
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(rsiHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(rsiHandle);
|
||||
if(atrHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(atrHandle);
|
||||
|
||||
Print("Expert Advisor deinitialized. Reason: ", reason);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Check if we have enough bars
|
||||
int requiredBars = MathMax(InpRSILookback, InpSwingLookback) + 10;
|
||||
if(Bars(actualSymbol, InpTimeframe) < requiredBars)
|
||||
return;
|
||||
|
||||
// Check if this is a new bar (wait for candle close)
|
||||
datetime currentBarTime = iTime(actualSymbol, InpTimeframe, 0);
|
||||
if(currentBarTime == lastBarTime)
|
||||
return; // Still the same bar, don't process
|
||||
|
||||
lastBarTime = currentBarTime;
|
||||
|
||||
// Update indicators
|
||||
if(!UpdateIndicators())
|
||||
return;
|
||||
|
||||
// Update RSI state tracking
|
||||
UpdateRSIState();
|
||||
|
||||
// Check existing positions
|
||||
CheckExistingPositions();
|
||||
|
||||
// Check for entry signals
|
||||
if(CanOpenNewPosition())
|
||||
{
|
||||
CheckEntrySignals();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update indicator values |
|
||||
//+------------------------------------------------------------------+
|
||||
bool UpdateIndicators()
|
||||
{
|
||||
// Update RSI (need enough bars for lookback)
|
||||
int rsiBarsNeeded = InpRSILookback + 5;
|
||||
if(CopyBuffer(rsiHandle, 0, 0, rsiBarsNeeded, rsiBuffer) < rsiBarsNeeded)
|
||||
return false;
|
||||
|
||||
// Update ATR
|
||||
if(CopyBuffer(atrHandle, 0, 0, 2, atrBuffer) < 2)
|
||||
return false;
|
||||
|
||||
// Update price buffers for swing detection
|
||||
if(CopyHigh(actualSymbol, InpTimeframe, 0, InpSwingLookback + 5, highBuffer) < InpSwingLookback + 5)
|
||||
return false;
|
||||
if(CopyLow(actualSymbol, InpTimeframe, 0, InpSwingLookback + 5, lowBuffer) < InpSwingLookback + 5)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update RSI state tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateRSIState()
|
||||
{
|
||||
double rsiCurrent = rsiBuffer[0];
|
||||
double rsiPrev = rsiBuffer[1];
|
||||
|
||||
// Check if RSI left overbought zone (was above 70, now below 70)
|
||||
if(rsiPrev >= InpRSIOverbought && rsiCurrent < InpRSIOverbought)
|
||||
{
|
||||
rsiWasOverbought = true;
|
||||
rsiBackInRange = true;
|
||||
lastRSIExitTime = TimeCurrent();
|
||||
lastRSIReentryTime = TimeCurrent();
|
||||
Print(TimeToString(TimeCurrent()), " - RSI left overbought zone (", rsiPrev, " -> ", rsiCurrent, ")");
|
||||
}
|
||||
|
||||
// Check if RSI left oversold zone (was below 30, now above 30)
|
||||
if(rsiPrev <= InpRSIOversold && rsiCurrent > InpRSIOversold)
|
||||
{
|
||||
rsiWasOversold = true;
|
||||
rsiBackInRange = true;
|
||||
lastRSIExitTime = TimeCurrent();
|
||||
lastRSIReentryTime = TimeCurrent();
|
||||
Print(TimeToString(TimeCurrent()), " - RSI left oversold zone (", rsiPrev, " -> ", rsiCurrent, ")");
|
||||
}
|
||||
|
||||
// Reset flags if RSI goes back to extreme
|
||||
if(rsiCurrent >= InpRSIOverbought)
|
||||
{
|
||||
rsiWasOverbought = false;
|
||||
rsiBackInRange = false;
|
||||
}
|
||||
|
||||
if(rsiCurrent <= InpRSIOversold)
|
||||
{
|
||||
rsiWasOversold = false;
|
||||
rsiBackInRange = false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if we can open a new position |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CanOpenNewPosition()
|
||||
{
|
||||
// Check max positions
|
||||
int positionCount = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(positionInfo.SelectByIndex(i))
|
||||
{
|
||||
if(positionInfo.Symbol() == actualSymbol && positionInfo.Magic() == InpMagicNumber)
|
||||
positionCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if(positionCount >= InpMaxPositions)
|
||||
return false;
|
||||
|
||||
// Check minimum bars between trades
|
||||
if(lastTradeTime > 0)
|
||||
{
|
||||
int barsSince = Bars(actualSymbol, InpTimeframe, lastTradeTime, TimeCurrent());
|
||||
if(barsSince < InpMinBarsBetweenTrades)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for entry signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEntrySignals()
|
||||
{
|
||||
// LONG Entry: RSI was overbought (>70), came back in range, now look for peak
|
||||
if(rsiWasOverbought && rsiBackInRange)
|
||||
{
|
||||
// Check if RSI is back in normal range (below 70)
|
||||
if(rsiBuffer[0] < InpRSIOverbought)
|
||||
{
|
||||
// Look for a peak in RSI after re-entry
|
||||
if(IsRSIPeak())
|
||||
{
|
||||
Print(TimeToString(TimeCurrent()), " - LONG Signal: RSI peak detected after leaving overbought zone");
|
||||
OpenPosition(POSITION_TYPE_BUY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SHORT Entry: RSI was oversold (<30), came back in range, now look for bottom
|
||||
if(rsiWasOversold && rsiBackInRange)
|
||||
{
|
||||
// Check if RSI is back in normal range (above 30)
|
||||
if(rsiBuffer[0] > InpRSIOversold)
|
||||
{
|
||||
// Look for a bottom in RSI after re-entry
|
||||
if(IsRSIBottom())
|
||||
{
|
||||
Print(TimeToString(TimeCurrent()), " - SHORT Signal: RSI bottom detected after leaving oversold zone");
|
||||
OpenPosition(POSITION_TYPE_SELL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if RSI is forming a peak (for LONG entry) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsRSIPeak()
|
||||
{
|
||||
// We need at least InpPeakBars + 1 bars to confirm a peak
|
||||
if(ArraySize(rsiBuffer) < InpPeakBars + 2)
|
||||
return false;
|
||||
|
||||
// Check if current RSI is higher than previous bars (forming a peak)
|
||||
double currentRSI = rsiBuffer[0];
|
||||
bool isPeak = true;
|
||||
|
||||
// Check if current is higher than the next few bars
|
||||
for(int i = 1; i <= InpPeakBars; i++)
|
||||
{
|
||||
if(rsiBuffer[i] >= currentRSI)
|
||||
{
|
||||
isPeak = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Also check if previous bar was lower (confirming upward movement before peak)
|
||||
if(rsiBuffer[1] >= currentRSI)
|
||||
isPeak = false;
|
||||
|
||||
return isPeak;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if RSI is forming a bottom (for SHORT entry) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsRSIBottom()
|
||||
{
|
||||
// We need at least InpPeakBars + 1 bars to confirm a bottom
|
||||
if(ArraySize(rsiBuffer) < InpPeakBars + 2)
|
||||
return false;
|
||||
|
||||
// Check if current RSI is lower than previous bars (forming a bottom)
|
||||
double currentRSI = rsiBuffer[0];
|
||||
bool isBottom = true;
|
||||
|
||||
// Check if current is lower than the next few bars
|
||||
for(int i = 1; i <= InpPeakBars; i++)
|
||||
{
|
||||
if(rsiBuffer[i] <= currentRSI)
|
||||
{
|
||||
isBottom = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Also check if previous bar was higher (confirming downward movement before bottom)
|
||||
if(rsiBuffer[1] <= currentRSI)
|
||||
isBottom = false;
|
||||
|
||||
return isBottom;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenPosition(ENUM_POSITION_TYPE type)
|
||||
{
|
||||
double price = (type == POSITION_TYPE_BUY) ?
|
||||
SymbolInfoDouble(actualSymbol, SYMBOL_ASK) :
|
||||
SymbolInfoDouble(actualSymbol, SYMBOL_BID);
|
||||
|
||||
if(price <= 0)
|
||||
return;
|
||||
|
||||
// Calculate stop loss and take profit
|
||||
double sl = 0.0, tp = 0.0;
|
||||
if(!CalculateStops(price, type, sl, tp))
|
||||
{
|
||||
Print("Error: Failed to calculate stops");
|
||||
return;
|
||||
}
|
||||
|
||||
string comment = "RSI_Secret_" + (type == POSITION_TYPE_BUY ? "LONG" : "SHORT");
|
||||
|
||||
bool result = false;
|
||||
if(type == POSITION_TYPE_BUY)
|
||||
result = trade.Buy(InpLotSize, actualSymbol, 0, sl, tp, comment);
|
||||
else
|
||||
result = trade.Sell(InpLotSize, actualSymbol, 0, sl, tp, comment);
|
||||
|
||||
if(result)
|
||||
{
|
||||
lastTradeTime = TimeCurrent();
|
||||
ulong ticket = trade.ResultOrder();
|
||||
Print(TimeToString(TimeCurrent()), " - Position opened: ", comment, " Ticket: ", ticket,
|
||||
" Price: ", price, " SL: ", sl, " TP: ", tp);
|
||||
|
||||
// Reset RSI state after opening position
|
||||
if(type == POSITION_TYPE_BUY)
|
||||
rsiWasOverbought = false;
|
||||
else
|
||||
rsiWasOversold = false;
|
||||
rsiBackInRange = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Failed to open position: ", comment, " Error: ",
|
||||
trade.ResultRetcode(), " - ", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate stop loss and take profit |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CalculateStops(double price, ENUM_POSITION_TYPE type, double &sl, double &tp)
|
||||
{
|
||||
double atrValue = atrBuffer[0];
|
||||
if(atrValue <= 0)
|
||||
atrValue = price * 0.01; // Fallback: 1% of price
|
||||
|
||||
double slDistance = atrValue * InpStopLossATR;
|
||||
double tpDistance = atrValue * InpTakeProfitATR;
|
||||
|
||||
int digits = (int)SymbolInfoInteger(actualSymbol, SYMBOL_DIGITS);
|
||||
double point = SymbolInfoDouble(actualSymbol, SYMBOL_POINT);
|
||||
int stopsLevel = (int)SymbolInfoInteger(actualSymbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
double minStopDistance = MathMax(stopsLevel * point, point * 10);
|
||||
|
||||
// Use swing-based stop loss if enabled
|
||||
if(InpUseSwingStopLoss)
|
||||
{
|
||||
double swingStop = GetSwingStopLoss(price, type);
|
||||
if(swingStop > 0)
|
||||
{
|
||||
if(type == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(swingStop < price && (price - swingStop) > minStopDistance)
|
||||
slDistance = price - swingStop;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(swingStop > price && (swingStop - price) > minStopDistance)
|
||||
slDistance = swingStop - price;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure minimum distance
|
||||
if(slDistance < minStopDistance)
|
||||
slDistance = minStopDistance;
|
||||
if(tpDistance < minStopDistance)
|
||||
tpDistance = minStopDistance;
|
||||
|
||||
if(type == POSITION_TYPE_BUY)
|
||||
{
|
||||
sl = NormalizeDouble(price - slDistance, digits);
|
||||
tp = NormalizeDouble(price + tpDistance, digits);
|
||||
}
|
||||
else
|
||||
{
|
||||
sl = NormalizeDouble(price + slDistance, digits);
|
||||
tp = NormalizeDouble(price - tpDistance, digits);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get swing-based stop loss (previous swing high/low) |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetSwingStopLoss(double currentPrice, ENUM_POSITION_TYPE type)
|
||||
{
|
||||
// For LONG: find previous swing low
|
||||
// For SHORT: find previous swing high
|
||||
|
||||
if(type == POSITION_TYPE_BUY)
|
||||
{
|
||||
// Find the lowest low in the lookback period
|
||||
double lowestLow = lowBuffer[0];
|
||||
for(int i = 1; i < InpSwingLookback && i < ArraySize(lowBuffer); i++)
|
||||
{
|
||||
if(lowBuffer[i] < lowestLow)
|
||||
lowestLow = lowBuffer[i];
|
||||
}
|
||||
return lowestLow;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find the highest high in the lookback period
|
||||
double highestHigh = highBuffer[0];
|
||||
for(int i = 1; i < InpSwingLookback && i < ArraySize(highBuffer); i++)
|
||||
{
|
||||
if(highBuffer[i] > highestHigh)
|
||||
highestHigh = highBuffer[i];
|
||||
}
|
||||
return highestHigh;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check existing positions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckExistingPositions()
|
||||
{
|
||||
// Position management can be added here if needed
|
||||
// For now, positions are managed by TP/SL
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -259,7 +259,7 @@ bool InitRSIReversalAsian(RSIReversalAsianData& data, string symbol,
|
||||
double rsi[];
|
||||
ArraySetAsSeries(rsi, true);
|
||||
|
||||
int retryCount = 0;
|
||||
retryCount = 0;
|
||||
bool rsiInitialized = false;
|
||||
|
||||
while(retryCount < 10 && !rsiInitialized)
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MagicNumberHelpers.mqh |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Select position by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PositionSelectByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
// First try to find position by symbol
|
||||
if(!PositionSelect(symbol))
|
||||
return false;
|
||||
|
||||
// Check if the selected position has the correct magic number
|
||||
if(PositionGetInteger(POSITION_MAGIC) != magic_number)
|
||||
{
|
||||
// Position exists but wrong magic number, search all positions
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(PositionGetTicket(i) > 0)
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Select position by ticket and verify magic number and symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PositionSelectByTicketAndMagic(ulong ticket, ulong magic_number)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
return false;
|
||||
|
||||
return (PositionGetInteger(POSITION_MAGIC) == magic_number);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Select position by ticket and verify symbol, magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PositionSelectByTicketSymbolAndMagic(ulong ticket, string symbol, ulong magic_number)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
return false;
|
||||
|
||||
return (PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if position exists with correct magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PositionExistsByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
return PositionSelectByMagic(symbol, magic_number);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get position ticket by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
ulong GetPositionTicketByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket > 0)
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number)
|
||||
{
|
||||
return ticket;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close position by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ClosePositionByMagic(CTrade &trade_obj, string symbol, ulong magic_number)
|
||||
{
|
||||
ulong ticket = GetPositionTicketByMagic(symbol, magic_number);
|
||||
if(ticket == 0)
|
||||
return false;
|
||||
|
||||
return trade_obj.PositionClose(ticket);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Modify position by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ModifyPositionByMagic(CTrade &trade_obj, string symbol, ulong magic_number,
|
||||
double sl, double tp)
|
||||
{
|
||||
ulong ticket = GetPositionTicketByMagic(symbol, magic_number);
|
||||
if(ticket == 0)
|
||||
return false;
|
||||
|
||||
return trade_obj.PositionModify(ticket, sl, tp);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get position profit by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetPositionProfitByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
if(!PositionSelectByMagic(symbol, magic_number))
|
||||
return 0.0;
|
||||
|
||||
return PositionGetDouble(POSITION_PROFIT);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get position type by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
ENUM_POSITION_TYPE GetPositionTypeByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
if(!PositionSelectByMagic(symbol, magic_number))
|
||||
return WRONG_VALUE;
|
||||
|
||||
return (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Count positions by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
int CountPositionsByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
int count = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket > 0)
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,75 @@
|
||||
# Pepperstone US - Symbol Setup Guide
|
||||
|
||||
## Finding Correct Symbol Names in MetaTrader 5
|
||||
|
||||
### Step-by-Step Instructions:
|
||||
|
||||
1. **Open Market Watch Window**
|
||||
- Press `Ctrl+M` or go to `View > Market Watch`
|
||||
|
||||
2. **Show All Symbols**
|
||||
- Right-click in the Market Watch window
|
||||
- Select `Show All` or `Symbols`
|
||||
- This shows all available symbols from your broker
|
||||
|
||||
3. **Search for Your Symbols**
|
||||
- Use the search box in the Market Watch window
|
||||
- Search for: "AAPL", "MSFT", "NVDA", "TSLA", "BTCUSD", "XAUUSD"
|
||||
|
||||
4. **Note the Exact Symbol Name**
|
||||
- The symbol name shown in Market Watch is what you need to use
|
||||
- Common formats for Pepperstone US:
|
||||
- Stocks: `AAPL.US`, `MSFT.US`, `NVDA.US`, `TSLA.US`
|
||||
- Or: `NASDAQ:AAPL`, `NASDAQ:MSFT`, etc.
|
||||
- Or: Just `AAPL`, `MSFT`, etc. (if available)
|
||||
|
||||
5. **Add to Market Watch**
|
||||
- Double-click the symbol to add it to your Market Watch
|
||||
- Or right-click and select `Show`
|
||||
|
||||
6. **Update EA Inputs**
|
||||
- Open the EA inputs in MetaTrader 5
|
||||
- Update each symbol parameter with the exact name from Market Watch
|
||||
|
||||
## Common Pepperstone US Symbol Formats
|
||||
|
||||
### US Stocks:
|
||||
- **Apple**: `AAPL.US` or `NASDAQ:AAPL` or `AAPL`
|
||||
- **Microsoft**: `MSFT.US` or `NASDAQ:MSFT` or `MSFT`
|
||||
- **NVIDIA**: `NVDA.US` or `NASDAQ:NVDA` or `NVDA`
|
||||
- **Tesla**: `TSLA.US` or `NASDAQ:TSLA` or `TSLA`
|
||||
|
||||
### Cryptocurrencies:
|
||||
- **Bitcoin**: `BTCUSD` or `BTC/USD` or `BTCUSD.c`
|
||||
|
||||
### Precious Metals:
|
||||
- **Gold**: `XAUUSD` or `GOLD` or `XAU/USD`
|
||||
|
||||
## Important Notes:
|
||||
|
||||
1. **Symbol Names are Case-Sensitive**: Use exact capitalization
|
||||
2. **Add Symbols to Market Watch**: Symbols must be in Market Watch for the EA to access them
|
||||
3. **Check Trading Hours**: US stocks trade during US market hours (9:30 AM - 4:00 PM ET)
|
||||
4. **CFD vs Stock**: Pepperstone offers CFDs on stocks, not actual stocks
|
||||
5. **Spread**: Check the spread for each symbol - some may have wider spreads
|
||||
|
||||
## Troubleshooting:
|
||||
|
||||
### If Symbol Not Found:
|
||||
1. Check if you're connected to Pepperstone US server
|
||||
2. Verify your account type supports the symbol
|
||||
3. Contact Pepperstone support for symbol availability
|
||||
4. Check if symbol requires special account permissions
|
||||
|
||||
### If EA Shows "Symbol Not Available":
|
||||
1. Make sure symbol is added to Market Watch
|
||||
2. Verify symbol name matches exactly (including dots, colons, etc.)
|
||||
3. Check broker connection status
|
||||
4. Try different symbol format variations
|
||||
|
||||
## Testing Symbols:
|
||||
|
||||
You can test if a symbol works by:
|
||||
1. Opening a chart with that symbol
|
||||
2. If chart opens successfully, the symbol name is correct
|
||||
3. Use that exact symbol name in the EA inputs
|
||||
@@ -0,0 +1,607 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| PerformanceEvaluator.mqh |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Performance Metrics Structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct StrategyPerformance {
|
||||
string strategyName;
|
||||
string symbol; // Store symbol to determine if it's a stock
|
||||
int magicNumber;
|
||||
double initialLotSize;
|
||||
double currentLotSize;
|
||||
double quarterProfit;
|
||||
double quarterTrades;
|
||||
double quarterWins;
|
||||
double quarterLosses;
|
||||
double maxDrawdown;
|
||||
double winRate;
|
||||
datetime quarterStart;
|
||||
datetime quarterEnd;
|
||||
bool isActive;
|
||||
bool inPenaltyMode; // True if strategy is in penalty (worst performer)
|
||||
double lotSizeBeforePenalty; // Store lot size before penalty
|
||||
datetime penaltyStartTime; // When penalty started
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Performance Tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
StrategyPerformance strategyPerformances[];
|
||||
int totalStrategies = 0;
|
||||
datetime lastMonthCheck = 0;
|
||||
datetime currentMonthStart = 0;
|
||||
datetime currentMonthEnd = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Performance Adjustment Parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== Performance Evaluation Settings ==="
|
||||
input bool PE_EnableAutoAdjustment = true; // Enable automatic lot size adjustment
|
||||
input double PE_LotSizeIncreasePercent = 10.0; // % increase for top-ranked strategies
|
||||
input double PE_LotSizeDecreasePercent = 10.0; // % decrease for bottom-ranked strategies
|
||||
input double PE_MinLotSize = 0.01; // Minimum lot size for forex/crypto
|
||||
input double PE_MinLotSizeStocks = 5.0; // Minimum lot size for stocks (5-10 range)
|
||||
input double PE_MaxLotSize = 100.0; // Maximum lot size after adjustment
|
||||
input int PE_TopPerformersCount = 3; // Number of top strategies to increase lot size
|
||||
input int PE_BottomPerformersCount = 3; // Number of bottom strategies to decrease lot size
|
||||
input bool PE_UseWinRateWeight = true; // Consider win rate in ranking (50% profit, 50% win rate)
|
||||
input bool PE_EnableBlitzPlay = true; // Enable blitz play: worst performer gets minimum lot size penalty
|
||||
input bool PE_EnableLogging = true; // Enable performance logging
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Initialize Performance Tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void InitPerformanceTracking()
|
||||
{
|
||||
// Calculate current month dates
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeCurrent(), dt);
|
||||
|
||||
// Determine month start (first day of current month)
|
||||
dt.day = 1;
|
||||
dt.hour = 0;
|
||||
dt.min = 0;
|
||||
dt.sec = 0;
|
||||
currentMonthStart = StructToTime(dt);
|
||||
|
||||
// Calculate month end (first day of next month - 1 second)
|
||||
dt.mon += 1;
|
||||
if(dt.mon > 12)
|
||||
{
|
||||
dt.mon = 1;
|
||||
dt.year++;
|
||||
}
|
||||
currentMonthEnd = StructToTime(dt) - 1; // End of last day of month
|
||||
|
||||
lastMonthCheck = TimeCurrent();
|
||||
|
||||
if(PE_EnableLogging)
|
||||
{
|
||||
Print("Performance Evaluator: Initialized");
|
||||
Print("Current Month Start: ", TimeToString(currentMonthStart));
|
||||
Print("Current Month End: ", TimeToString(currentMonthEnd));
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if Symbol is a Stock |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsStockSymbol(string symbol)
|
||||
{
|
||||
// Check if symbol contains common stock indicators
|
||||
if(StringFind(symbol, ".US") >= 0) return true;
|
||||
if(StringFind(symbol, "NASDAQ:") >= 0) return true;
|
||||
if(StringFind(symbol, "NYSE:") >= 0) return true;
|
||||
|
||||
// Note: Symbol category check removed to avoid enum conversion issues
|
||||
// String-based checks (.US, NASDAQ:, NYSE:, common tickers) are sufficient
|
||||
|
||||
// Common stock tickers (without .US suffix)
|
||||
string commonStocks[] = {"AAPL", "NVDA", "TSLA", "GOOGL", "AMZN", "META", "AMD", "NFLX"};
|
||||
for(int i = 0; i < ArraySize(commonStocks); i++)
|
||||
{
|
||||
if(StringFind(symbol, commonStocks[i]) == 0) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get Minimum Lot Size for Symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetMinLotSizeForSymbol(string symbol)
|
||||
{
|
||||
if(IsStockSymbol(symbol))
|
||||
return PE_MinLotSizeStocks;
|
||||
else
|
||||
return PE_MinLotSize;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Register Strategy for Performance Tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void RegisterStrategy(string strategyName, int magicNumber, double initialLotSize, string symbol = "")
|
||||
{
|
||||
// Check if strategy already registered
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].strategyName == strategyName &&
|
||||
strategyPerformances[i].magicNumber == magicNumber)
|
||||
{
|
||||
if(PE_EnableLogging)
|
||||
Print("Performance Evaluator: Strategy '", strategyName, "' already registered");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new strategy
|
||||
int newSize = ArraySize(strategyPerformances) + 1;
|
||||
ArrayResize(strategyPerformances, newSize);
|
||||
|
||||
strategyPerformances[newSize - 1].strategyName = strategyName;
|
||||
strategyPerformances[newSize - 1].symbol = symbol;
|
||||
strategyPerformances[newSize - 1].magicNumber = magicNumber;
|
||||
strategyPerformances[newSize - 1].initialLotSize = initialLotSize;
|
||||
// Start with minimum lot size for safety (symbol-specific minimum)
|
||||
double minLot = GetMinLotSizeForSymbol(symbol);
|
||||
strategyPerformances[newSize - 1].currentLotSize = minLot;
|
||||
strategyPerformances[newSize - 1].quarterProfit = 0.0;
|
||||
strategyPerformances[newSize - 1].quarterTrades = 0;
|
||||
strategyPerformances[newSize - 1].quarterWins = 0;
|
||||
strategyPerformances[newSize - 1].quarterLosses = 0;
|
||||
strategyPerformances[newSize - 1].maxDrawdown = 0.0;
|
||||
strategyPerformances[newSize - 1].winRate = 0.0;
|
||||
strategyPerformances[newSize - 1].quarterStart = currentMonthStart;
|
||||
strategyPerformances[newSize - 1].quarterEnd = currentMonthEnd;
|
||||
strategyPerformances[newSize - 1].isActive = true;
|
||||
strategyPerformances[newSize - 1].inPenaltyMode = false;
|
||||
strategyPerformances[newSize - 1].lotSizeBeforePenalty = initialLotSize;
|
||||
strategyPerformances[newSize - 1].penaltyStartTime = 0;
|
||||
|
||||
totalStrategies = newSize;
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print("Performance Evaluator: Registered strategy '", strategyName,
|
||||
"' (Magic: ", magicNumber, ", Initial Lot: ", initialLotSize, ")");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update Strategy Performance Metrics |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateStrategyPerformance(string strategyName, int magicNumber)
|
||||
{
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].strategyName == strategyName &&
|
||||
strategyPerformances[i].magicNumber == magicNumber &&
|
||||
strategyPerformances[i].isActive)
|
||||
{
|
||||
// Calculate performance for current quarter
|
||||
double totalProfit = 0.0;
|
||||
int totalTrades = 0;
|
||||
int wins = 0;
|
||||
int losses = 0;
|
||||
double maxDD = 0.0;
|
||||
double peakBalance = 0.0;
|
||||
|
||||
// Scan all closed deals in current quarter
|
||||
datetime quarterStart = strategyPerformances[i].quarterStart;
|
||||
datetime quarterEnd = strategyPerformances[i].quarterEnd;
|
||||
|
||||
// Select history for the quarter
|
||||
if(HistorySelect(quarterStart, quarterEnd))
|
||||
{
|
||||
int totalDeals = HistoryDealsTotal();
|
||||
for(int j = 0; j < totalDeals; j++)
|
||||
{
|
||||
ulong ticket = HistoryDealGetTicket(j);
|
||||
if(ticket > 0)
|
||||
{
|
||||
long dealMagic = HistoryDealGetInteger(ticket, DEAL_MAGIC);
|
||||
if(dealMagic == magicNumber)
|
||||
{
|
||||
double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
|
||||
double swap = HistoryDealGetDouble(ticket, DEAL_SWAP);
|
||||
double commission = HistoryDealGetDouble(ticket, DEAL_COMMISSION);
|
||||
double totalDealProfit = profit + swap + commission;
|
||||
|
||||
totalProfit += totalDealProfit;
|
||||
totalTrades++;
|
||||
|
||||
if(totalDealProfit > 0)
|
||||
wins++;
|
||||
else if(totalDealProfit < 0)
|
||||
losses++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate win rate
|
||||
double winRate = 0.0;
|
||||
if(totalTrades > 0)
|
||||
winRate = (double)wins / (double)totalTrades * 100.0;
|
||||
|
||||
// Update metrics
|
||||
strategyPerformances[i].quarterProfit = totalProfit;
|
||||
strategyPerformances[i].quarterTrades = totalTrades;
|
||||
strategyPerformances[i].quarterWins = wins;
|
||||
strategyPerformances[i].quarterLosses = losses;
|
||||
strategyPerformances[i].winRate = winRate;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy Ranking Structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct StrategyRank {
|
||||
int index;
|
||||
double score;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate Strategy Score for Ranking |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateStrategyScore(int strategyIndex)
|
||||
{
|
||||
double profit = strategyPerformances[strategyIndex].quarterProfit;
|
||||
double winRate = strategyPerformances[strategyIndex].winRate;
|
||||
double trades = strategyPerformances[strategyIndex].quarterTrades;
|
||||
|
||||
// Normalize profit (scale to 0-100 range, assuming max profit of $1000)
|
||||
double normalizedProfit = MathMin(profit / 10.0, 100.0);
|
||||
if(profit < 0) normalizedProfit = profit / 5.0; // Penalize losses more
|
||||
|
||||
// Calculate score
|
||||
double score = 0.0;
|
||||
if(PE_UseWinRateWeight)
|
||||
{
|
||||
// 50% profit, 50% win rate (if enough trades)
|
||||
if(trades >= 5)
|
||||
score = (normalizedProfit * 0.5) + (winRate * 0.5);
|
||||
else
|
||||
score = normalizedProfit; // Not enough trades, use profit only
|
||||
}
|
||||
else
|
||||
{
|
||||
// Profit only
|
||||
score = normalizedProfit;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if Month Ended and Evaluate Performance |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckMonthEnd()
|
||||
{
|
||||
datetime now = TimeCurrent();
|
||||
|
||||
// Check if we've entered a new month
|
||||
if(now >= currentMonthEnd)
|
||||
{
|
||||
if(PE_EnableLogging)
|
||||
Print("Performance Evaluator: Month ended. Evaluating and ranking strategies...");
|
||||
|
||||
// Update performance metrics for all strategies
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive)
|
||||
{
|
||||
UpdateStrategyPerformance(strategyPerformances[i].strategyName,
|
||||
strategyPerformances[i].magicNumber);
|
||||
}
|
||||
}
|
||||
|
||||
// Rank strategies
|
||||
int activeCount = 0;
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive)
|
||||
activeCount++;
|
||||
}
|
||||
|
||||
if(activeCount > 0)
|
||||
{
|
||||
// Create ranking array
|
||||
StrategyRank ranks[];
|
||||
ArrayResize(ranks, activeCount);
|
||||
int rankIndex = 0;
|
||||
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive)
|
||||
{
|
||||
ranks[rankIndex].index = i;
|
||||
ranks[rankIndex].score = CalculateStrategyScore(i);
|
||||
rankIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by score (descending - highest score first)
|
||||
for(int i = 0; i < activeCount - 1; i++)
|
||||
{
|
||||
for(int j = i + 1; j < activeCount; j++)
|
||||
{
|
||||
if(ranks[j].score > ranks[i].score)
|
||||
{
|
||||
StrategyRank temp = ranks[i];
|
||||
ranks[i] = ranks[j];
|
||||
ranks[j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust lot sizes based on ranking
|
||||
if(PE_EnableAutoAdjustment)
|
||||
{
|
||||
// Increase top performers (skip if in penalty mode)
|
||||
int topCount = MathMin(PE_TopPerformersCount, activeCount);
|
||||
for(int i = 0; i < topCount; i++)
|
||||
{
|
||||
int strategyIdx = ranks[i].index;
|
||||
|
||||
// Skip if strategy is in penalty mode
|
||||
if(strategyPerformances[strategyIdx].inPenaltyMode)
|
||||
continue;
|
||||
|
||||
double oldLotSize = strategyPerformances[strategyIdx].currentLotSize;
|
||||
double newLotSize = oldLotSize * (1.0 + PE_LotSizeIncreasePercent / 100.0);
|
||||
|
||||
if(newLotSize > PE_MaxLotSize)
|
||||
newLotSize = PE_MaxLotSize;
|
||||
|
||||
strategyPerformances[strategyIdx].currentLotSize = newLotSize;
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print("Performance Evaluator: Rank #", (i+1), " - Increasing '",
|
||||
strategyPerformances[strategyIdx].strategyName,
|
||||
"' lot size from ", oldLotSize, " to ", newLotSize,
|
||||
" (Score: ", DoubleToString(ranks[i].score, 2),
|
||||
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
|
||||
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%)");
|
||||
}
|
||||
|
||||
// Decrease bottom performers (skip worst one if blitz play is enabled)
|
||||
int bottomCount = MathMin(PE_BottomPerformersCount, activeCount);
|
||||
int startIdx = activeCount - bottomCount;
|
||||
|
||||
// If blitz play is enabled, skip the worst performer (it will get minimum penalty)
|
||||
if(PE_EnableBlitzPlay && activeCount > 0)
|
||||
startIdx = activeCount - bottomCount + 1;
|
||||
|
||||
for(int i = startIdx; i < activeCount; i++)
|
||||
{
|
||||
int strategyIdx = ranks[i].index;
|
||||
|
||||
// Skip if strategy is in penalty mode
|
||||
if(strategyPerformances[strategyIdx].inPenaltyMode)
|
||||
continue;
|
||||
|
||||
double oldLotSize = strategyPerformances[strategyIdx].currentLotSize;
|
||||
double newLotSize = oldLotSize * (1.0 - PE_LotSizeDecreasePercent / 100.0);
|
||||
|
||||
// Use symbol-specific minimum lot size
|
||||
double minLot = GetMinLotSizeForSymbol(strategyPerformances[strategyIdx].symbol);
|
||||
if(newLotSize < minLot)
|
||||
newLotSize = minLot;
|
||||
|
||||
strategyPerformances[strategyIdx].currentLotSize = newLotSize;
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print("Performance Evaluator: Rank #", (i+1), " - Decreasing '",
|
||||
strategyPerformances[strategyIdx].strategyName,
|
||||
"' lot size from ", oldLotSize, " to ", newLotSize,
|
||||
" (Score: ", DoubleToString(ranks[i].score, 2),
|
||||
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
|
||||
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%)");
|
||||
}
|
||||
}
|
||||
|
||||
// Blitz Play: Apply penalty to worst performer
|
||||
if(PE_EnableBlitzPlay && activeCount > 0)
|
||||
{
|
||||
// Find worst performer (last in ranking)
|
||||
int worstIdx = ranks[activeCount - 1].index;
|
||||
|
||||
// Remove penalty from previous worst performer (if any)
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive && strategyPerformances[i].inPenaltyMode)
|
||||
{
|
||||
// Check if penalty period has passed (one month)
|
||||
if(now - strategyPerformances[i].penaltyStartTime >= 2592000) // ~30 days
|
||||
{
|
||||
// Restore lot size to before penalty
|
||||
strategyPerformances[i].currentLotSize = strategyPerformances[i].lotSizeBeforePenalty;
|
||||
strategyPerformances[i].inPenaltyMode = false;
|
||||
strategyPerformances[i].penaltyStartTime = 0;
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print("Blitz Play: Penalty removed from '", strategyPerformances[i].strategyName,
|
||||
"'. Lot size restored to ", strategyPerformances[i].currentLotSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply penalty to new worst performer
|
||||
if(!strategyPerformances[worstIdx].inPenaltyMode)
|
||||
{
|
||||
strategyPerformances[worstIdx].lotSizeBeforePenalty = strategyPerformances[worstIdx].currentLotSize;
|
||||
// Use symbol-specific minimum lot size
|
||||
double minLot = GetMinLotSizeForSymbol(strategyPerformances[worstIdx].symbol);
|
||||
strategyPerformances[worstIdx].currentLotSize = minLot;
|
||||
strategyPerformances[worstIdx].inPenaltyMode = true;
|
||||
strategyPerformances[worstIdx].penaltyStartTime = now;
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print("Blitz Play: WORST PERFORMER - '", strategyPerformances[worstIdx].strategyName,
|
||||
"' penalized! Lot size reduced from ", strategyPerformances[worstIdx].lotSizeBeforePenalty,
|
||||
" to minimum ", minLot, " (Score: ", DoubleToString(ranks[activeCount - 1].score, 2),
|
||||
", Profit: $", DoubleToString(strategyPerformances[worstIdx].quarterProfit, 2), ")");
|
||||
}
|
||||
}
|
||||
|
||||
// Log performance report
|
||||
if(PE_EnableLogging)
|
||||
{
|
||||
Print("=== Monthly Performance Ranking ===");
|
||||
for(int i = 0; i < activeCount; i++)
|
||||
{
|
||||
int strategyIdx = ranks[i].index;
|
||||
Print("Rank #", (i+1), ": ", strategyPerformances[strategyIdx].strategyName,
|
||||
" - Score: ", DoubleToString(ranks[i].score, 2),
|
||||
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
|
||||
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%",
|
||||
", Trades: ", (int)strategyPerformances[strategyIdx].quarterTrades,
|
||||
", Lot Size: ", DoubleToString(strategyPerformances[strategyIdx].currentLotSize, 2));
|
||||
}
|
||||
Print("===================================");
|
||||
}
|
||||
}
|
||||
|
||||
// Reset month metrics for all strategies
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive)
|
||||
{
|
||||
strategyPerformances[i].quarterProfit = 0.0;
|
||||
strategyPerformances[i].quarterTrades = 0;
|
||||
strategyPerformances[i].quarterWins = 0;
|
||||
strategyPerformances[i].quarterLosses = 0;
|
||||
strategyPerformances[i].maxDrawdown = 0.0;
|
||||
strategyPerformances[i].winRate = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Update month dates
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(now, dt);
|
||||
|
||||
// First day of current month
|
||||
dt.day = 1;
|
||||
dt.hour = 0;
|
||||
dt.min = 0;
|
||||
dt.sec = 0;
|
||||
currentMonthStart = StructToTime(dt);
|
||||
|
||||
// First day of next month - 1 second
|
||||
dt.mon += 1;
|
||||
if(dt.mon > 12)
|
||||
{
|
||||
dt.mon = 1;
|
||||
dt.year++;
|
||||
}
|
||||
currentMonthEnd = StructToTime(dt) - 1;
|
||||
|
||||
// Update month dates for all strategies
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
strategyPerformances[i].quarterStart = currentMonthStart;
|
||||
strategyPerformances[i].quarterEnd = currentMonthEnd;
|
||||
}
|
||||
|
||||
lastMonthCheck = now;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get Current Lot Size for Strategy |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetStrategyLotSize(string strategyName, int magicNumber)
|
||||
{
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].strategyName == strategyName &&
|
||||
strategyPerformances[i].magicNumber == magicNumber &&
|
||||
strategyPerformances[i].isActive)
|
||||
{
|
||||
return strategyPerformances[i].currentLotSize;
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Process Performance Evaluation (call from OnTick) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ProcessPerformanceEvaluation()
|
||||
{
|
||||
// Check if month ended
|
||||
CheckMonthEnd();
|
||||
|
||||
// Check for penalty expiration (blitz play)
|
||||
if(PE_EnableBlitzPlay)
|
||||
{
|
||||
datetime now = TimeCurrent();
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive && strategyPerformances[i].inPenaltyMode)
|
||||
{
|
||||
// Check if penalty period has passed (one month = ~30 days)
|
||||
if(now - strategyPerformances[i].penaltyStartTime >= 2592000)
|
||||
{
|
||||
// Restore lot size to before penalty
|
||||
strategyPerformances[i].currentLotSize = strategyPerformances[i].lotSizeBeforePenalty;
|
||||
strategyPerformances[i].inPenaltyMode = false;
|
||||
strategyPerformances[i].penaltyStartTime = 0;
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print("Blitz Play: Penalty expired for '", strategyPerformances[i].strategyName,
|
||||
"'. Lot size restored to ", strategyPerformances[i].currentLotSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update performance metrics periodically (every hour)
|
||||
static datetime lastUpdate = 0;
|
||||
if(TimeCurrent() - lastUpdate >= 3600)
|
||||
{
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive)
|
||||
{
|
||||
UpdateStrategyPerformance(strategyPerformances[i].strategyName,
|
||||
strategyPerformances[i].magicNumber);
|
||||
}
|
||||
}
|
||||
lastUpdate = TimeCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get Performance Summary |
|
||||
//+------------------------------------------------------------------+
|
||||
string GetPerformanceSummary()
|
||||
{
|
||||
string summary = "\n=== Performance Summary ===\n";
|
||||
summary += "Current Month: " + TimeToString(currentMonthStart) + " to " + TimeToString(currentMonthEnd) + "\n\n";
|
||||
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive)
|
||||
{
|
||||
summary += strategyPerformances[i].strategyName + ":\n";
|
||||
summary += " Profit: $" + DoubleToString(strategyPerformances[i].quarterProfit, 2) + "\n";
|
||||
summary += " Trades: " + IntegerToString((int)strategyPerformances[i].quarterTrades) + "\n";
|
||||
summary += " Win Rate: " + DoubleToString(strategyPerformances[i].winRate, 2) + "%\n";
|
||||
summary += " Lot Size: " + DoubleToString(strategyPerformances[i].currentLotSize, 2) + "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,76 @@
|
||||
# United EA Strategy Configuration Summary
|
||||
|
||||
## Strategy Symbols and Magic Numbers
|
||||
|
||||
### Strategy 1: DarvasBox
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Number**: 135790
|
||||
|
||||
### Strategy 2: EMASlopeDistance
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Number**: 12350
|
||||
|
||||
### Strategy 3: RSICrossOverReversal
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Number**: 7
|
||||
|
||||
### Strategy 4: RSIMidPointHijack
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Numbers**:
|
||||
- RSIFollow: 1001
|
||||
- RSIReverse: 1002
|
||||
- EMACross: 1003
|
||||
|
||||
### Strategy 5: RSI Scalping APPL (Apple)
|
||||
- **Symbol**: AAPL (Apple stock)
|
||||
- **Magic Number**: 20001
|
||||
- **Note**: Changed from "APPL" to "AAPL" (correct ticker symbol)
|
||||
|
||||
### Strategy 6: RSI Scalping BTCUSD
|
||||
- **Symbol**: BTCUSD (Bitcoin/USD)
|
||||
- **Magic Number**: 123459123
|
||||
|
||||
### Strategy 7: RSI Scalping MSFT
|
||||
- **Symbol**: MSFT (Microsoft stock)
|
||||
- **Magic Number**: 20002
|
||||
|
||||
### Strategy 8: RSI Scalping NVDA
|
||||
- **Symbol**: NVDA (NVIDIA stock)
|
||||
- **Magic Number**: 20003
|
||||
|
||||
### Strategy 9: RSI Scalping TSLA
|
||||
- **Symbol**: TSLA (Tesla stock)
|
||||
- **Magic Number**: 125421321
|
||||
|
||||
### Strategy 10: RSI Scalping XAUUSD
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Number**: 129102315
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Stock Symbols**: Stock symbols (AAPL, MSFT, NVDA, TSLA) must be:
|
||||
- Added to Market Watch in MetaTrader 5
|
||||
- Available from your broker
|
||||
- Use the correct ticker symbol (e.g., "AAPL" not "APPL")
|
||||
|
||||
2. **Magic Numbers**: All strategies have unique magic numbers to prevent interference:
|
||||
- Each strategy can be identified by its magic number
|
||||
- RSIMidPointHijack uses 3 magic numbers (one for each sub-strategy)
|
||||
|
||||
3. **Symbol Configuration**: Each strategy trades on its own symbol:
|
||||
- You can change symbols in the input parameters
|
||||
- The EA will log warnings if a symbol is not available
|
||||
- Strategies with unavailable symbols will be skipped (EA continues running)
|
||||
|
||||
4. **RSI Scalping Strategies**:
|
||||
- Each RSI Scalping variant trades on a different symbol
|
||||
- They all use the same strategy logic but with different parameters
|
||||
- Buy and sell signals are generated based on RSI levels for each symbol
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If stock symbols are not working:
|
||||
1. Check if the symbol exists in your broker's symbol list
|
||||
2. Add the symbol to Market Watch in MetaTrader 5
|
||||
3. Verify the symbol name matches your broker's naming convention
|
||||
4. Some brokers use prefixes/suffixes (e.g., "NASDAQ:AAPL" or "AAPL.US")
|
||||
@@ -0,0 +1,300 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DarvasBoxStrategy.mqh |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
bool InitDarvasBox(string symbol)
|
||||
{
|
||||
dbData.symbol = symbol;
|
||||
dbData.boxHigh = 0;
|
||||
dbData.boxLow = 0;
|
||||
dbData.boxFormed = false;
|
||||
dbData.lastBoxTime = 0;
|
||||
dbData.boxName = "DarvasBox_" + IntegerToString(DB_MagicNumber) + "_";
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Print("DarvasBox: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sleep(100); // Wait for symbol to be ready
|
||||
|
||||
dbData.point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
dbData.minStopLevel = SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL) * dbData.point;
|
||||
|
||||
dbData.maHandle = iMA(symbol, DB_TrendTimeframe, DB_MA_Period, 0, DB_MA_Method, DB_MA_Price);
|
||||
dbData.volumeHandle = iVolumes(symbol, PERIOD_CURRENT, VOLUME_TICK);
|
||||
|
||||
if(dbData.maHandle == INVALID_HANDLE || dbData.volumeHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("DarvasBox: Error creating indicators for '", symbol, "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
dbData.trade.SetDeviationInPoints(10);
|
||||
dbData.trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
dbData.trade.SetAsyncMode(false);
|
||||
dbData.trade.SetExpertMagicNumber(DB_MagicNumber);
|
||||
|
||||
ObjectsDeleteAll(0, dbData.boxName);
|
||||
dbData.isInitialized = true;
|
||||
Print("DarvasBox: Successfully initialized for symbol '", symbol, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeinitDarvasBox()
|
||||
{
|
||||
if(dbData.maHandle != INVALID_HANDLE) IndicatorRelease(dbData.maHandle);
|
||||
if(dbData.volumeHandle != INVALID_HANDLE) IndicatorRelease(dbData.volumeHandle);
|
||||
ObjectsDeleteAll(0, dbData.boxName);
|
||||
}
|
||||
|
||||
void DrawDarvasBox()
|
||||
{
|
||||
if(!dbData.boxFormed) return;
|
||||
|
||||
datetime time1 = iTime(dbData.symbol, PERIOD_H1, DB_BoxPeriod);
|
||||
datetime time2 = iTime(dbData.symbol, PERIOD_H1, 0);
|
||||
|
||||
ObjectsDeleteAll(0, dbData.boxName);
|
||||
|
||||
ObjectCreate(0, dbData.boxName + "Top", OBJ_TREND, 0, time1, dbData.boxHigh, time2, dbData.boxHigh);
|
||||
ObjectCreate(0, dbData.boxName + "Bottom", OBJ_TREND, 0, time1, dbData.boxLow, time2, dbData.boxLow);
|
||||
|
||||
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_COLOR, DB_BoxColor);
|
||||
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_COLOR, DB_BoxColor);
|
||||
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_WIDTH, DB_BoxWidth);
|
||||
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_WIDTH, DB_BoxWidth);
|
||||
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_RAY_RIGHT, true);
|
||||
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_RAY_RIGHT, true);
|
||||
}
|
||||
|
||||
void CalculateDarvasBox()
|
||||
{
|
||||
double high = 0;
|
||||
double low = DBL_MAX;
|
||||
|
||||
// Find highest high and lowest low in the period - EXACTLY like original
|
||||
for(int i = 0; i < DB_BoxPeriod; i++)
|
||||
{
|
||||
high = MathMax(high, iHigh(dbData.symbol, PERIOD_H1, i));
|
||||
low = MathMin(low, iLow(dbData.symbol, PERIOD_H1, i));
|
||||
}
|
||||
|
||||
double range = high - low;
|
||||
double allowedRange = DB_BoxDeviation * dbData.point; // Use dbData.point instead of _Point
|
||||
|
||||
if(DB_EnableLogging)
|
||||
{
|
||||
Print("DarvasBox: Box Calculation - High: ", high, " Low: ", low, " Range: ", range, " Allowed Range: ", allowedRange);
|
||||
}
|
||||
|
||||
// Check if box is formed - EXACTLY like original
|
||||
if(range <= allowedRange)
|
||||
{
|
||||
dbData.boxHigh = high;
|
||||
dbData.boxLow = low;
|
||||
dbData.boxFormed = true;
|
||||
dbData.lastBoxTime = iTime(dbData.symbol, PERIOD_CURRENT, 0);
|
||||
|
||||
// Draw the box
|
||||
DrawDarvasBox();
|
||||
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Box Formed - High: ", dbData.boxHigh, " Low: ", dbData.boxLow, " Time: ", dbData.lastBoxTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbData.boxFormed = false;
|
||||
// Delete box if it exists
|
||||
ObjectsDeleteAll(0, dbData.boxName);
|
||||
}
|
||||
}
|
||||
|
||||
bool ValidateStopLevels(double price, double &sl, double &tp, ENUM_ORDER_TYPE orderType)
|
||||
{
|
||||
double minSlDistance = MathMax(dbData.minStopLevel, DB_StopLoss * dbData.point);
|
||||
double minTpDistance = MathMax(dbData.minStopLevel, DB_TakeProfit * dbData.point);
|
||||
|
||||
if(orderType == ORDER_TYPE_BUY)
|
||||
{
|
||||
sl = price - minSlDistance;
|
||||
tp = price + minTpDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
sl = price + minSlDistance;
|
||||
tp = price - minTpDistance;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsTrendFavorable(ENUM_ORDER_TYPE orderType)
|
||||
{
|
||||
double ma[];
|
||||
ArraySetAsSeries(ma, true);
|
||||
|
||||
if(CopyBuffer(dbData.maHandle, 0, 0, 2, ma) <= 0)
|
||||
return false;
|
||||
|
||||
double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK);
|
||||
double trendStrength = MathAbs(currentPrice - ma[0]) / dbData.point;
|
||||
|
||||
if(orderType == ORDER_TYPE_BUY)
|
||||
return (currentPrice > ma[0] && trendStrength > DB_TrendThreshold);
|
||||
else
|
||||
return (currentPrice < ma[0] && trendStrength > DB_TrendThreshold);
|
||||
}
|
||||
|
||||
bool CheckVolumeConditions()
|
||||
{
|
||||
double volumes[];
|
||||
ArraySetAsSeries(volumes, true);
|
||||
|
||||
if(CopyBuffer(dbData.volumeHandle, 0, 0, DB_VolumeMA_Period + 1, volumes) <= 0)
|
||||
return false;
|
||||
|
||||
double volumeMA = 0;
|
||||
for(int i = 1; i <= DB_VolumeMA_Period; i++)
|
||||
volumeMA += volumes[i];
|
||||
volumeMA /= DB_VolumeMA_Period;
|
||||
|
||||
double currentVolume = volumes[0];
|
||||
double volumeRatio = currentVolume / volumeMA;
|
||||
|
||||
return (volumeRatio > DB_VolumeThresholdMultiplier);
|
||||
}
|
||||
|
||||
bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price, double sl, double tp)
|
||||
{
|
||||
if(!ValidateStopLevels(price, sl, tp, orderType))
|
||||
{
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Order rejected - Stop levels validation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!IsTrendFavorable(orderType))
|
||||
{
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Order rejected - Trend not favorable for ", EnumToString(orderType));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!CheckVolumeConditions())
|
||||
{
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Order rejected - Volume conditions not met");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
|
||||
// Use market price (0) instead of explicit price - this ensures market order execution
|
||||
// In backtesting, explicit price might fail if price has moved
|
||||
if(orderType == ORDER_TYPE_BUY)
|
||||
result = dbData.trade.Buy(g_DB_LotSize, dbData.symbol, 0, sl, tp, "Darvas Box Breakout");
|
||||
else
|
||||
result = dbData.trade.Sell(g_DB_LotSize, dbData.symbol, 0, sl, tp, "Darvas Box Breakdown");
|
||||
|
||||
// Always log errors, success only if logging enabled
|
||||
if(result)
|
||||
{
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: ", (orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"), " Order Placed Successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Always log failures with detailed info
|
||||
uint retcode_uint = dbData.trade.ResultRetcode();
|
||||
int retcode = (int)retcode_uint;
|
||||
string desc = dbData.trade.ResultRetcodeDescription();
|
||||
ulong deal = dbData.trade.ResultDeal();
|
||||
ulong order = dbData.trade.ResultOrder();
|
||||
Print("DarvasBox: ", (orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"),
|
||||
" Order Failed - Retcode: ", retcode,
|
||||
", Description: ", desc,
|
||||
", Deal: ", deal,
|
||||
", Order: ", order,
|
||||
", Symbol: ", dbData.symbol,
|
||||
", Requested Price: ", price,
|
||||
", SL: ", sl,
|
||||
", TP: ", tp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ProcessDarvasBox(string symbol)
|
||||
{
|
||||
// Skip if not initialized (symbol not available)
|
||||
if(!dbData.isInitialized)
|
||||
return;
|
||||
|
||||
dbData.symbol = symbol; // Update symbol in case it changed
|
||||
|
||||
// Calculate new box levels - EXACTLY like original (called every tick)
|
||||
CalculateDarvasBox();
|
||||
|
||||
// Check for trading signals - EXACTLY like original (checked every tick)
|
||||
if(dbData.boxFormed)
|
||||
{
|
||||
double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK);
|
||||
long currentVolume_long = iVolume(dbData.symbol, PERIOD_CURRENT, 0);
|
||||
double currentVolume = (double)currentVolume_long;
|
||||
|
||||
if(DB_EnableLogging)
|
||||
{
|
||||
Print("DarvasBox: Current Price: ", currentPrice, " Box High: ", dbData.boxHigh, " Box Low: ", dbData.boxLow);
|
||||
Print("DarvasBox: Current Volume: ", currentVolume, " Volume Threshold: ", DB_VolumeThreshold);
|
||||
}
|
||||
|
||||
// Check for breakout above box - EXACTLY like original
|
||||
if(currentPrice > dbData.boxHigh && currentVolume > DB_VolumeThreshold)
|
||||
{
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Breakout Signal Detected - Price above box high");
|
||||
|
||||
// Buy signal
|
||||
if(!PositionExistsByMagic(dbData.symbol, (ulong)DB_MagicNumber)) // No existing positions with our magic number
|
||||
{
|
||||
double sl = currentPrice - DB_StopLoss * dbData.point;
|
||||
double tp = currentPrice + DB_TakeProfit * dbData.point;
|
||||
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Preparing Buy Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
|
||||
|
||||
PlaceOrder(ORDER_TYPE_BUY, currentPrice, sl, tp);
|
||||
}
|
||||
else if(DB_EnableLogging)
|
||||
Print("DarvasBox: Skipping Buy Signal - Position already exists");
|
||||
}
|
||||
|
||||
// Check for breakdown below box - EXACTLY like original
|
||||
if(currentPrice < dbData.boxLow && currentVolume > DB_VolumeThreshold)
|
||||
{
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Breakdown Signal Detected - Price below box low");
|
||||
|
||||
// Sell signal
|
||||
if(!PositionExistsByMagic(dbData.symbol, (ulong)DB_MagicNumber)) // No existing positions with our magic number
|
||||
{
|
||||
double sl = currentPrice + DB_StopLoss * dbData.point;
|
||||
double tp = currentPrice - DB_TakeProfit * dbData.point;
|
||||
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Preparing Sell Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
|
||||
|
||||
PlaceOrder(ORDER_TYPE_SELL, currentPrice, sl, tp);
|
||||
}
|
||||
else if(DB_EnableLogging)
|
||||
Print("DarvasBox: Skipping Sell Signal - Position already exists");
|
||||
}
|
||||
}
|
||||
else if(DB_EnableLogging)
|
||||
Print("DarvasBox: No Box Formed - Waiting for consolidation");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,496 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| EMASlopeDistanceStrategy.mqh |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
bool InitEMASlopeDistance(string symbol)
|
||||
{
|
||||
esData.symbol = symbol;
|
||||
esData.letzte_überwachung_zeit = 0;
|
||||
esData.überwachung_aktiv = false;
|
||||
esData.preis_trigger_aktiv = false;
|
||||
esData.steigung_trigger_aktiv = false;
|
||||
esData.ticket = 0;
|
||||
esData.trades_in_current_crossover = 0;
|
||||
esData.crossover_detected = false;
|
||||
esData.trade_open_time = 0;
|
||||
esData.last_bar_time = 0;
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Print("EMASlopeDistance: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sleep(100); // Wait for symbol to be ready
|
||||
|
||||
esData.trade.SetExpertMagicNumber(ES_MagicNumber);
|
||||
esData.trade.SetDeviationInPoints(10);
|
||||
esData.trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
|
||||
esData.ema_handle = iMA(symbol, ES_Timeframe, ES_EMA_Periode, 0, MODE_EMA, PRICE_CLOSE);
|
||||
|
||||
if(esData.ema_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("EMASlopeDistance: Error creating EMA indicator for '", symbol, "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
ArraySetAsSeries(esData.ema_array, true);
|
||||
esData.isInitialized = true;
|
||||
Print("EMASlopeDistance: Successfully initialized for symbol '", symbol, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeinitEMASlopeDistance()
|
||||
{
|
||||
if(esData.ema_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(esData.ema_handle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| EMA Berechnung (EMA Calculation) |
|
||||
//+------------------------------------------------------------------+
|
||||
void BerechneEMA()
|
||||
{
|
||||
//--- EMA Werte vom Indicator kopieren (Copy EMA values from indicator)
|
||||
int copied = CopyBuffer(esData.ema_handle, 0, 0, 3, esData.ema_array);
|
||||
|
||||
if(copied <= 0)
|
||||
{
|
||||
Print("TRACE: Fehler beim Kopieren der EMA Werte - Copied: ", copied);
|
||||
return;
|
||||
}
|
||||
|
||||
Print("TRACE: EMA Werte kopiert: ", copied, " Bars");
|
||||
Print("TRACE: EMA [0]: ", esData.ema_array[0], " [1]: ", esData.ema_array[1], " [2]: ", esData.ema_array[2]);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trigger-Bedingungen prüfen (Check trigger conditions) |
|
||||
//+------------------------------------------------------------------+
|
||||
void PrüfeTrigger()
|
||||
{
|
||||
if(ArraySize(esData.ema_array) < 2)
|
||||
{
|
||||
Print("TRACE: Array zu klein - Größe: ", ArraySize(esData.ema_array));
|
||||
return;
|
||||
}
|
||||
|
||||
//--- Aktuelle Werte (Current values)
|
||||
double aktueller_preis = SymbolInfoDouble(esData.symbol, SYMBOL_BID);
|
||||
double aktueller_ask = SymbolInfoDouble(esData.symbol, SYMBOL_ASK);
|
||||
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
|
||||
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
|
||||
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
|
||||
double pips_multiplier = (digits == 3 || digits == 5) ? 10.0 : 1.0;
|
||||
|
||||
//--- EMA Werte in Variablen (EMA values in variables)
|
||||
double ema_aktuell = esData.ema_array[0];
|
||||
double ema_vorher = esData.ema_array[1];
|
||||
|
||||
//--- EMA Crossover Erkennung (EMA Crossover Detection)
|
||||
// Prüfe ob Preis die EMA kreuzt (Check if price crosses EMA)
|
||||
static double last_close = 0;
|
||||
static double last_ema = 0;
|
||||
|
||||
if(last_close != 0 && last_ema != 0)
|
||||
{
|
||||
bool crossover_bullish = (last_close <= last_ema) && (aktueller_close > ema_aktuell);
|
||||
bool crossover_bearish = (last_close >= last_ema) && (aktueller_close < ema_aktuell);
|
||||
|
||||
//--- Neues Crossover-Ereignis erkannt (New crossover event detected)
|
||||
if(crossover_bullish || crossover_bearish)
|
||||
{
|
||||
esData.trades_in_current_crossover = 0; // Reset trade counter
|
||||
Print("TRACE: EMA Crossover erkannt - ", (crossover_bullish ? "BULLISH" : "BEARISH"), " - Trade-Counter zurückgesetzt");
|
||||
Print("TRACE: Vorher: Close=", last_close, " EMA=", last_ema, " Jetzt: Close=", aktueller_close, " EMA=", ema_aktuell);
|
||||
}
|
||||
}
|
||||
|
||||
//--- Aktuelle Werte für nächsten Vergleich speichern (Save current values for next comparison)
|
||||
last_close = aktueller_close;
|
||||
last_ema = ema_aktuell;
|
||||
|
||||
//--- Preisbewegung zur EMA prüfen (Check price action to EMA)
|
||||
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / point / pips_multiplier;
|
||||
|
||||
Print("TRACE: Preis-Abstand: ", preis_abstand, " Pips (Schwelle: ", ES_PreisSchwelle, ")");
|
||||
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
|
||||
Print("TRACE: Trades im aktuellen Crossover: ", esData.trades_in_current_crossover, "/", ES_MaxTradesPerCrossover);
|
||||
|
||||
if(preis_abstand > ES_PreisSchwelle && !esData.preis_trigger_aktiv)
|
||||
{
|
||||
esData.preis_trigger_aktiv = true;
|
||||
Print("TRACE: Preis-Trigger aktiviert: ", preis_abstand, " Pips");
|
||||
}
|
||||
|
||||
//--- EMA Steigung prüfen (Check EMA slope)
|
||||
double steigung = (ema_aktuell - ema_vorher) / point / pips_multiplier;
|
||||
|
||||
Print("TRACE: EMA Steigung: ", steigung, " Pips (Schwelle: ", ES_SteigungSchwelle, ")");
|
||||
|
||||
if(MathAbs(steigung) > ES_SteigungSchwelle && !esData.steigung_trigger_aktiv)
|
||||
{
|
||||
esData.steigung_trigger_aktiv = true;
|
||||
Print("TRACE: Steigungs-Trigger aktiviert: ", steigung, " Pips");
|
||||
}
|
||||
|
||||
//--- Überwachung starten wenn beide Trigger aktiv sind (Start monitoring when both triggers are active)
|
||||
if(esData.preis_trigger_aktiv && esData.steigung_trigger_aktiv && !esData.überwachung_aktiv)
|
||||
{
|
||||
esData.überwachung_aktiv = true;
|
||||
|
||||
if(ES_UseBarData)
|
||||
{
|
||||
esData.letzte_überwachung_zeit = iTime(esData.symbol, ES_Timeframe, 0); // Aktuelle Bar-Zeit
|
||||
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Bar: ", TimeToString(esData.letzte_überwachung_zeit), ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
esData.letzte_überwachung_zeit = TimeCurrent(); // Aktuelle Tick-Zeit
|
||||
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Tick)");
|
||||
}
|
||||
}
|
||||
|
||||
//--- Trade platzieren wenn Überwachung aktiv und Preis über/unter EMA (Place trade when monitoring active and price above/below EMA)
|
||||
if(esData.überwachung_aktiv)
|
||||
{
|
||||
bool bullish_signal = aktueller_close > ema_aktuell;
|
||||
bool bearish_signal = aktueller_close < ema_aktuell;
|
||||
|
||||
Print("TRACE: Signal Check - Bullish: ", bullish_signal, " Bearish: ", bearish_signal);
|
||||
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
|
||||
Print("TRACE: Differenz: ", aktueller_close - ema_aktuell);
|
||||
|
||||
//--- Trade-Limit prüfen (Check trade limit)
|
||||
if(esData.trades_in_current_crossover >= ES_MaxTradesPerCrossover)
|
||||
{
|
||||
Print("TRACE: Trade-Limit erreicht (", ES_MaxTradesPerCrossover, ") - Kein neuer Trade");
|
||||
return;
|
||||
}
|
||||
|
||||
if(bullish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
|
||||
{
|
||||
Print("TRACE: Versuche KAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
|
||||
if(PlatziereTrade(ORDER_TYPE_BUY))
|
||||
{
|
||||
esData.trades_in_current_crossover++;
|
||||
}
|
||||
}
|
||||
else if(bearish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
|
||||
{
|
||||
Print("TRACE: Versuche VERKAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
|
||||
if(PlatziereTrade(ORDER_TYPE_SELL))
|
||||
{
|
||||
esData.trades_in_current_crossover++;
|
||||
}
|
||||
}
|
||||
else if(PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
|
||||
{
|
||||
Print("TRACE: Position bereits offen - kein neuer Trade");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trade platzieren (Place trade) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PlatziereTrade(ENUM_ORDER_TYPE order_type)
|
||||
{
|
||||
Print("TRACE: Versuche Trade zu platzieren - Typ: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF");
|
||||
Print("TRACE: Lot: ", g_ES_LotSize);
|
||||
|
||||
bool success = false;
|
||||
|
||||
if(order_type == ORDER_TYPE_BUY)
|
||||
{
|
||||
success = esData.trade.Buy(g_ES_LotSize, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
|
||||
}
|
||||
else
|
||||
{
|
||||
success = esData.trade.Sell(g_ES_LotSize, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
|
||||
}
|
||||
|
||||
if(success)
|
||||
{
|
||||
esData.ticket = (int)esData.trade.ResultOrder();
|
||||
Print("TRACE: Trade erfolgreich platziert: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF", " Ticket: ", esData.ticket);
|
||||
|
||||
//--- Trade-Öffnungszeit speichern (Save trade opening time)
|
||||
esData.trade_open_time = iTime(esData.symbol, ES_Timeframe, 0);
|
||||
Print("TRACE: Trade-Öffnungszeit: ", TimeToString(esData.trade_open_time));
|
||||
|
||||
//--- Überwachung zurücksetzen (Reset monitoring)
|
||||
esData.überwachung_aktiv = false;
|
||||
esData.preis_trigger_aktiv = false;
|
||||
esData.steigung_trigger_aktiv = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Fehler beim Platzieren des Trades - Retcode: ", esData.trade.ResultRetcode());
|
||||
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trades verwalten (Manage trades) |
|
||||
//+------------------------------------------------------------------+
|
||||
void VerwalteTrades()
|
||||
{
|
||||
if(!PositionSelectByMagic(esData.symbol, (ulong)ES_MagicNumber))
|
||||
return;
|
||||
|
||||
double position_profit = PositionGetDouble(POSITION_PROFIT);
|
||||
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
|
||||
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
|
||||
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
|
||||
double pips_multiplier = (digits == 3 || digits == 5) ? 10.0 : 1.0;
|
||||
double trailing_stop_pips = ES_TrailingStop;
|
||||
|
||||
//--- Gleitender Stop (Trailing Stop) - nur wenn Position im Profit ist
|
||||
if(position_profit > 0) // Only apply trailing stop when in profit
|
||||
{
|
||||
if(position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
double new_stop_loss = current_price - (trailing_stop_pips * point * pips_multiplier);
|
||||
double current_stop_loss = PositionGetDouble(POSITION_SL);
|
||||
|
||||
// Only move stop loss if new stop is higher than current stop
|
||||
if(new_stop_loss > current_stop_loss)
|
||||
{
|
||||
ÄndereStopLoss(new_stop_loss);
|
||||
}
|
||||
}
|
||||
else if(position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
double new_stop_loss = current_price + (trailing_stop_pips * point * pips_multiplier);
|
||||
double current_stop_loss = PositionGetDouble(POSITION_SL);
|
||||
|
||||
// Only move stop loss if new stop is lower than current stop
|
||||
if(new_stop_loss < current_stop_loss || current_stop_loss == 0)
|
||||
{
|
||||
ÄndereStopLoss(new_stop_loss);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--- Ausstieg bei Preis unter/über EMA (Exit when price below/above EMA)
|
||||
if(ArraySize(esData.ema_array) >= 1)
|
||||
{
|
||||
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
|
||||
double ema_aktuell = esData.ema_array[0];
|
||||
bool exit_bullish = (position_type == POSITION_TYPE_SELL && aktueller_close > ema_aktuell);
|
||||
bool exit_bearish = (position_type == POSITION_TYPE_BUY && aktueller_close < ema_aktuell);
|
||||
|
||||
if(exit_bullish || exit_bearish)
|
||||
{
|
||||
Print("TRACE: Ausstiegssignal - Close: ", aktueller_close, " EMA: ", ema_aktuell);
|
||||
SchließePosition("EMA Crossover Exit");
|
||||
|
||||
Print("TRACE: Position geschlossen - Trade-Counter bleibt bei ", esData.trades_in_current_crossover);
|
||||
}
|
||||
}
|
||||
|
||||
//--- Profit-Prüfung nach X Bars (Profit check after X bars)
|
||||
if(ES_CloseUnprofitableTrades && esData.trade_open_time != 0 && PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
|
||||
{
|
||||
Print("TRACE: Profit-Prüfung aktiviert - CloseUnprofitableTrades: ", ES_CloseUnprofitableTrades);
|
||||
PrüfeProfitNachBars();
|
||||
}
|
||||
else if(!ES_CloseUnprofitableTrades)
|
||||
{
|
||||
Print("TRACE: Profit-Prüfung deaktiviert - CloseUnprofitableTrades: ", ES_CloseUnprofitableTrades);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Profit-Prüfung nach X Bars (Profit check after X bars) |
|
||||
//+------------------------------------------------------------------+
|
||||
void PrüfeProfitNachBars()
|
||||
{
|
||||
if(!PositionSelectByMagic(esData.symbol, (ulong)ES_MagicNumber))
|
||||
{
|
||||
return; // Keine Position offen
|
||||
}
|
||||
|
||||
datetime current_bar_time = iTime(esData.symbol, ES_Timeframe, 0);
|
||||
int bars_since_trade_open = iBarShift(esData.symbol, ES_Timeframe, esData.trade_open_time);
|
||||
|
||||
Print("TRACE: Bars seit Trade-Öffnung: ", bars_since_trade_open, "/", ES_ProfitCheckBars);
|
||||
|
||||
//--- Prüfe ob genügend Bars vergangen sind (Check if enough bars have passed)
|
||||
if(bars_since_trade_open >= ES_ProfitCheckBars)
|
||||
{
|
||||
double position_profit = PositionGetDouble(POSITION_PROFIT);
|
||||
double position_volume = PositionGetDouble(POSITION_VOLUME);
|
||||
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
Print("TRACE: Profit-Prüfung nach ", ES_ProfitCheckBars, " Bars");
|
||||
Print("TRACE: Position Profit: ", position_profit, " USD");
|
||||
|
||||
//--- Schließe Position wenn nicht im Profit (Close position if not in profit)
|
||||
if(position_profit <= 0)
|
||||
{
|
||||
Print("TRACE: Position nicht im Profit - Schließe Position");
|
||||
SchließePosition("Profit Check - Unprofitable");
|
||||
|
||||
//--- Trade-Öffnungszeit zurücksetzen (Reset trade opening time)
|
||||
esData.trade_open_time = 0;
|
||||
Print("TRACE: Trade-Öffnungszeit zurückgesetzt");
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Position im Profit - Behalte Position");
|
||||
//--- Trade-Öffnungszeit zurücksetzen um weitere Prüfungen zu vermeiden (Reset to avoid further checks)
|
||||
esData.trade_open_time = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Stop Loss ändern (Modify Stop Loss) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ÄndereStopLoss(double new_stop_loss)
|
||||
{
|
||||
Print("TRACE: Versuche Stop Loss zu ändern auf: ", new_stop_loss);
|
||||
|
||||
bool success = ModifyPositionByMagic(esData.trade, esData.symbol, (ulong)ES_MagicNumber, new_stop_loss, PositionGetDouble(POSITION_TP));
|
||||
|
||||
if(success)
|
||||
{
|
||||
Print("TRACE: Stop Loss erfolgreich geändert auf: ", new_stop_loss);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Fehler beim Ändern des Stop Loss - Retcode: ", esData.trade.ResultRetcode());
|
||||
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Position schließen (Close position) |
|
||||
//+------------------------------------------------------------------+
|
||||
void SchließePosition(string reason = "Unbekannt")
|
||||
{
|
||||
Print("TRACE: Versuche Position zu schließen - Grund: ", reason);
|
||||
|
||||
bool success = ClosePositionByMagic(esData.trade, esData.symbol, (ulong)ES_MagicNumber);
|
||||
|
||||
if(success)
|
||||
{
|
||||
Print("TRACE: Position erfolgreich geschlossen - Grund: ", reason);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Fehler beim Schließen der Position - Retcode: ", esData.trade.ResultRetcode());
|
||||
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void ProcessEMASlopeDistance(string symbol)
|
||||
{
|
||||
// Skip if not initialized (symbol not available)
|
||||
if(!esData.isInitialized)
|
||||
return;
|
||||
|
||||
esData.symbol = symbol; // Update symbol in case it changed
|
||||
|
||||
//--- Bar-Daten oder Tick-Daten verwenden (Use bar data or tick data)
|
||||
if(ES_UseBarData)
|
||||
{
|
||||
//--- Nur bei neuen Bars ausführen (Only execute on new bars)
|
||||
datetime current_bar_time = iTime(esData.symbol, ES_Timeframe, 0);
|
||||
|
||||
if(current_bar_time == esData.last_bar_time)
|
||||
{
|
||||
return; // Kein neuer Bar, nichts tun
|
||||
}
|
||||
|
||||
esData.last_bar_time = current_bar_time;
|
||||
}
|
||||
|
||||
//--- EMA Werte berechnen (Calculate EMA values)
|
||||
BerechneEMA();
|
||||
|
||||
//--- Debug: Aktuelle Werte ausgeben (Debug: Output current values)
|
||||
if(ArraySize(esData.ema_array) > 0)
|
||||
{
|
||||
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
|
||||
double ema_aktuell = esData.ema_array[0];
|
||||
double ema_vorher = esData.ema_array[1];
|
||||
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
|
||||
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
|
||||
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / point;
|
||||
double steigung = (ema_aktuell - ema_vorher) / point;
|
||||
|
||||
if(ES_UseBarData)
|
||||
{
|
||||
Print("=== DEBUG INFO (Neuer Bar) ===");
|
||||
Print("Bar Zeit: ", TimeToString(iTime(esData.symbol, ES_Timeframe, 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("=== DEBUG INFO (Tick) ===");
|
||||
}
|
||||
|
||||
Print("Aktueller Close: ", aktueller_close);
|
||||
Print("EMA: ", ema_aktuell);
|
||||
Print("Preis-Abstand: ", preis_abstand, " Pips");
|
||||
Print("EMA Steigung: ", steigung, " Pips");
|
||||
Print("Differenz Close-EMA: ", aktueller_close - ema_aktuell);
|
||||
Print("Preis-Trigger: ", esData.preis_trigger_aktiv, " Steigungs-Trigger: ", esData.steigung_trigger_aktiv);
|
||||
Print("Überwachung aktiv: ", esData.überwachung_aktiv);
|
||||
Print("Position offen: ", PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber));
|
||||
Print("Trades im aktuellen Crossover: ", esData.trades_in_current_crossover, "/", ES_MaxTradesPerCrossover);
|
||||
Print("==================");
|
||||
}
|
||||
|
||||
//--- Überwachung prüfen (Check monitoring)
|
||||
if(esData.überwachung_aktiv)
|
||||
{
|
||||
if(ES_UseBarData)
|
||||
{
|
||||
// Bar-basierte Überwachungszeit
|
||||
int bars_since_monitoring = iBarShift(esData.symbol, ES_Timeframe, esData.letzte_überwachung_zeit);
|
||||
int timeout_bars = (int)(ES_ÜberwachungTimeout / PeriodSeconds(ES_Timeframe));
|
||||
|
||||
if(bars_since_monitoring > timeout_bars)
|
||||
{
|
||||
esData.überwachung_aktiv = false;
|
||||
esData.preis_trigger_aktiv = false;
|
||||
esData.steigung_trigger_aktiv = false;
|
||||
Print("Überwachung beendet - Bar-basierte Zeitüberschreitung (", bars_since_monitoring, " Bars)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Tick-basierte Überwachungszeit
|
||||
if(TimeCurrent() - esData.letzte_überwachung_zeit > ES_ÜberwachungTimeout)
|
||||
{
|
||||
esData.überwachung_aktiv = false;
|
||||
esData.preis_trigger_aktiv = false;
|
||||
esData.steigung_trigger_aktiv = false;
|
||||
Print("Überwachung beendet - Tick-basierte Zeitüberschreitung");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--- Trigger-Bedingungen prüfen (Check trigger conditions)
|
||||
PrüfeTrigger();
|
||||
|
||||
//--- Trade Management (Trade management)
|
||||
VerwalteTrades();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,240 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSICrossOverReversalStrategy.mqh |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
void WeekDays_Init()
|
||||
{
|
||||
rcData.WeekDays[0] = RC_Sunday;
|
||||
rcData.WeekDays[1] = RC_Monday;
|
||||
rcData.WeekDays[2] = RC_Tuesday;
|
||||
rcData.WeekDays[3] = RC_Wednesday;
|
||||
rcData.WeekDays[4] = RC_Thursday;
|
||||
rcData.WeekDays[5] = RC_Friday;
|
||||
rcData.WeekDays[6] = RC_Saturday;
|
||||
}
|
||||
|
||||
bool WeekDays_Check(datetime aTime)
|
||||
{
|
||||
MqlDateTime stm;
|
||||
TimeToStruct(aTime, stm);
|
||||
return(rcData.WeekDays[stm.day_of_week]);
|
||||
}
|
||||
|
||||
int TimeHour(datetime when = 0)
|
||||
{
|
||||
if(when == 0) when = TimeCurrent();
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(when, dt);
|
||||
return dt.hour;
|
||||
}
|
||||
|
||||
bool InitRSICrossOverReversal(string symbol)
|
||||
{
|
||||
WeekDays_Init();
|
||||
|
||||
rcData.symbol = symbol;
|
||||
rcData.previousRSIDef = 0;
|
||||
rcData.lastTradeTime = 0;
|
||||
rcData.bartime = 0;
|
||||
rcData.lastBarTime = 0;
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Print("RSICrossOverReversal: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sleep(100); // Wait for symbol to be ready
|
||||
|
||||
rcData.rsiHandle = iRSI(symbol, RC_TimeFrame1, RC_rsiPeriod, PRICE_CLOSE);
|
||||
if(rcData.rsiHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("RSICrossOverReversal: Error creating RSI handle for '", symbol, "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
rcData.emaHandle = iMA(symbol, RC_TimeFrame2, RC_emaPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
if(rcData.emaHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("RSICrossOverReversal: Error creating EMA handle for '", symbol, "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
|
||||
rcData.isInitialized = true;
|
||||
Print("RSICrossOverReversal: Successfully initialized for symbol '", symbol, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeinitRSICrossOverReversal()
|
||||
{
|
||||
if(rcData.rsiHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(rcData.rsiHandle);
|
||||
if(rcData.emaHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(rcData.emaHandle);
|
||||
}
|
||||
|
||||
void Close_Position_MN(ulong magicNumber)
|
||||
{
|
||||
ClosePositionByMagic(rcData.trade, rcData.symbol, (int)magicNumber);
|
||||
}
|
||||
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
if(!PositionSelectByMagic(rcData.symbol, RC_MagicNumber))
|
||||
return;
|
||||
|
||||
ulong PositionTicket = PositionGetInteger(POSITION_TICKET);
|
||||
ENUM_POSITION_TYPE trade_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
string symbol = rcData.symbol;
|
||||
|
||||
double POINT = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
int DIGIT = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
|
||||
|
||||
if(trade_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
double Bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), DIGIT);
|
||||
|
||||
if(Bid - PositionGetDouble(POSITION_PRICE_OPEN) > NormalizeDouble(POINT * RC_TrailingStop, DIGIT))
|
||||
{
|
||||
if(PositionGetDouble(POSITION_SL) < NormalizeDouble(Bid - POINT * RC_TrailingStop, DIGIT))
|
||||
{
|
||||
ModifyPositionByMagic(rcData.trade, symbol, RC_MagicNumber,
|
||||
NormalizeDouble(Bid - POINT * RC_TrailingStop, DIGIT),
|
||||
PositionGetDouble(POSITION_TP));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(trade_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
double Ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), DIGIT);
|
||||
|
||||
if((PositionGetDouble(POSITION_PRICE_OPEN) - Ask) > NormalizeDouble(POINT * RC_TrailingStop, DIGIT))
|
||||
{
|
||||
if((PositionGetDouble(POSITION_SL) > NormalizeDouble(Ask + POINT * RC_TrailingStop, DIGIT)) ||
|
||||
(PositionGetDouble(POSITION_SL) == 0))
|
||||
{
|
||||
ModifyPositionByMagic(rcData.trade, symbol, RC_MagicNumber,
|
||||
NormalizeDouble(Ask + POINT * RC_TrailingStop, DIGIT),
|
||||
PositionGetDouble(POSITION_TP));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessRSICrossOverReversal(string symbol)
|
||||
{
|
||||
// Skip if not initialized (symbol not available)
|
||||
if(!rcData.isInitialized)
|
||||
return;
|
||||
|
||||
rcData.symbol = symbol; // Update symbol in case it changed
|
||||
if(rcData.bartime == iTime(rcData.symbol, RC_BarTimeFrame, 0))
|
||||
return;
|
||||
rcData.bartime = iTime(rcData.symbol, RC_BarTimeFrame, 0);
|
||||
|
||||
double rsi[];
|
||||
if(CopyBuffer(rcData.rsiHandle, 0, 0, 2, rsi) <= 0)
|
||||
return;
|
||||
|
||||
double ema[];
|
||||
if(CopyBuffer(rcData.emaHandle, 0, 0, 2, ema) <= 0)
|
||||
return;
|
||||
|
||||
datetime currentTime = TimeCurrent();
|
||||
int currentHour = TimeHour(TimeCurrent());
|
||||
|
||||
if(!WeekDays_Check(TimeTradeServer()))
|
||||
{
|
||||
Close_Position_MN(RC_MagicNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!((currentHour < RC_tradingHourOneEnd && currentHour > RC_tradingHourOneBegin) ||
|
||||
(currentHour < RC_tradingHourTwoEnd && currentHour > RC_tradingHourTwoBegin)))
|
||||
{
|
||||
Close_Position_MN(RC_MagicNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasPosition = PositionExistsByMagic(rcData.symbol, RC_MagicNumber);
|
||||
|
||||
double currentRSI = rsi[0];
|
||||
double previousRSI = rsi[1];
|
||||
|
||||
if(rcData.previousRSIDef == 0)
|
||||
{
|
||||
rcData.previousRSIDef = currentRSI;
|
||||
return;
|
||||
}
|
||||
|
||||
double currentEMA = ema[0];
|
||||
double previousEMA = ema[1];
|
||||
|
||||
double emaSlope = (currentEMA - previousEMA) * 100;
|
||||
double closeCurr = iClose(Symbol(), Period(), 0);
|
||||
double priceToEmaDistance = (closeCurr - currentEMA) * 10;
|
||||
|
||||
bool isBuyPosition = false;
|
||||
bool isSellPosition = false;
|
||||
if(hasPosition)
|
||||
{
|
||||
if(PositionSelectByMagic(rcData.symbol, RC_MagicNumber))
|
||||
{
|
||||
ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
if(positionType == POSITION_TYPE_BUY)
|
||||
isBuyPosition = true;
|
||||
else if(positionType == POSITION_TYPE_SELL)
|
||||
isSellPosition = true;
|
||||
}
|
||||
}
|
||||
|
||||
ApplyTrailingStop();
|
||||
|
||||
bool cooldownPassed = (currentTime - rcData.lastTradeTime) >= RC_cooldownSeconds;
|
||||
bool isTrendStrong = MathAbs(emaSlope) > RC_emaSlopeThreshold || MathAbs(priceToEmaDistance) > RC_emaDistanceThreshold;
|
||||
|
||||
if(isBuyPosition && currentRSI > RC_exitBuyRSI)
|
||||
{
|
||||
Close_Position_MN(RC_MagicNumber);
|
||||
rcData.lastTradeTime = currentTime;
|
||||
}
|
||||
|
||||
if(isSellPosition && currentRSI < RC_exitSellRSI)
|
||||
{
|
||||
Close_Position_MN(RC_MagicNumber);
|
||||
rcData.lastTradeTime = currentTime;
|
||||
}
|
||||
|
||||
if(isTrendStrong)
|
||||
{
|
||||
Close_Position_MN(RC_MagicNumber);
|
||||
rcData.lastTradeTime = currentTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if(currentRSI < RC_overboughtLevel - RC_entryRSISellSpread && rcData.previousRSIDef >= RC_overboughtLevel &&
|
||||
!isSellPosition && !hasPosition && cooldownPassed)
|
||||
{
|
||||
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
|
||||
if(rcData.trade.Sell(g_RC_LotSize, rcData.symbol, 0.0, 0.0, 0.0, "Sell Order"))
|
||||
{
|
||||
rcData.lastTradeTime = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
if(currentRSI > RC_oversoldLevel + RC_entryRSIBuySpread && rcData.previousRSIDef <= RC_oversoldLevel &&
|
||||
!isBuyPosition && !hasPosition && cooldownPassed)
|
||||
{
|
||||
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
|
||||
if(rcData.trade.Buy(g_RC_LotSize, rcData.symbol, 0.0, 0.0, 0.0, "Buy Order"))
|
||||
{
|
||||
rcData.lastTradeTime = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
rcData.previousRSIDef = currentRSI;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,471 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIMidPointHijackStrategy.mqh |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
bool IsNewBar(string symbol)
|
||||
{
|
||||
datetime time[];
|
||||
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
if(time[0] != rmData.lastBarTime)
|
||||
{
|
||||
rmData.lastBarTime = time[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsWithinTradingHours(int startHour, int endHour)
|
||||
{
|
||||
MqlDateTime currentTime;
|
||||
TimeToStruct(TimeCurrent(), currentTime);
|
||||
|
||||
if(startHour <= endHour)
|
||||
return (currentTime.hour >= startHour && currentTime.hour < endHour);
|
||||
else
|
||||
return (currentTime.hour >= startHour || currentTime.hour < endHour);
|
||||
}
|
||||
|
||||
bool HasPosition(string symbol, int magic)
|
||||
{
|
||||
return PositionExistsByMagic(symbol, magic);
|
||||
}
|
||||
|
||||
bool HasProfitablePosition(int excludeMagic)
|
||||
{
|
||||
bool hasProfitable = false;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(rmData.positionInfo.SelectByIndex(i))
|
||||
{
|
||||
if(rmData.positionInfo.Magic() != excludeMagic)
|
||||
{
|
||||
double profit = rmData.positionInfo.Profit();
|
||||
if(profit > RM_InpLockProfitThreshold * _Point)
|
||||
{
|
||||
hasProfitable = true;
|
||||
if(RM_InpCloseOppositeTrades)
|
||||
{
|
||||
if((excludeMagic == RM_InpMagicNumberRSIFollow && rmData.positionInfo.Magic() == RM_InpMagicNumberRSIReverse) ||
|
||||
(excludeMagic == RM_InpMagicNumberRSIReverse && rmData.positionInfo.Magic() == RM_InpMagicNumberRSIFollow) ||
|
||||
(excludeMagic == RM_InpMagicNumberEMACross && (rmData.positionInfo.Magic() == RM_InpMagicNumberRSIReverse || rmData.positionInfo.Magic() == RM_InpMagicNumberRSIFollow)) ||
|
||||
((excludeMagic == RM_InpMagicNumberRSIFollow || excludeMagic == RM_InpMagicNumberRSIReverse) && rmData.positionInfo.Magic() == RM_InpMagicNumberEMACross))
|
||||
{
|
||||
ClosePosition(rmData.symbol, (int)rmData.positionInfo.Magic());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasProfitable;
|
||||
}
|
||||
|
||||
bool IsRSIReverseInCooldown(string symbol)
|
||||
{
|
||||
if(RM_InpRSIReverseCooldownBars <= 0)
|
||||
return false;
|
||||
|
||||
if(!rmData.rsiReverseInCooldown)
|
||||
return false;
|
||||
|
||||
datetime time[];
|
||||
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
datetime currentBarTime = time[0];
|
||||
datetime cooldownEndTime = rmData.rsiReverseLastCloseTime + RM_InpRSIReverseCooldownBars * PeriodSeconds(RM_InpTimeframe);
|
||||
|
||||
if(currentBarTime >= cooldownEndTime)
|
||||
{
|
||||
rmData.rsiReverseInCooldown = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CheckRSIFollowStrategy(string symbol)
|
||||
{
|
||||
if(!IsWithinTradingHours(RM_InpRSIFollowStartHour, RM_InpRSIFollowEndHour))
|
||||
{
|
||||
if(RM_InpRSIFollowCloseOutsideHours)
|
||||
{
|
||||
if(HasPosition(symbol, RM_InpMagicNumberRSIFollow))
|
||||
ClosePosition(symbol, RM_InpMagicNumberRSIFollow);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberRSIFollow))
|
||||
return;
|
||||
|
||||
if(rmData.lastBarRSI > RM_InpRSIOverbought)
|
||||
rmData.rsiOverbought = true;
|
||||
else if(rmData.lastBarRSI < RM_InpRSIOversold)
|
||||
rmData.rsiOversold = true;
|
||||
|
||||
if(rmData.rsiOverbought && rmData.lastBarRSI < RM_InpRSIExitLevel)
|
||||
{
|
||||
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
|
||||
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
|
||||
}
|
||||
rmData.rsiOverbought = false;
|
||||
}
|
||||
else if(rmData.rsiOversold && rmData.lastBarRSI > RM_InpRSIExitLevel)
|
||||
{
|
||||
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
|
||||
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
|
||||
}
|
||||
rmData.rsiOversold = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckRSIReverseStrategy(string symbol)
|
||||
{
|
||||
if(!IsWithinTradingHours(RM_InpRSIReverseStartHour, RM_InpRSIReverseEndHour))
|
||||
{
|
||||
if(RM_InpRSIReverseCloseOutsideHours)
|
||||
{
|
||||
if(HasPosition(symbol, RM_InpMagicNumberRSIReverse))
|
||||
ClosePosition(symbol, RM_InpMagicNumberRSIReverse);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberRSIReverse))
|
||||
return;
|
||||
|
||||
if(IsRSIReverseInCooldown(symbol))
|
||||
return;
|
||||
|
||||
if(rmData.lastBarRSIReverse > RM_InpRSIReverseOverbought)
|
||||
rmData.rsiReverseOverbought = true;
|
||||
else if(rmData.lastBarRSIReverse < RM_InpRSIReverseOversold)
|
||||
rmData.rsiReverseOversold = true;
|
||||
|
||||
if(rmData.rsiReverseOverbought && rmData.lastBarRSIReverse < RM_InpRSIReverseCrossLevel)
|
||||
{
|
||||
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
|
||||
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
|
||||
}
|
||||
rmData.rsiReverseOverbought = false;
|
||||
}
|
||||
else if(rmData.rsiReverseOversold && rmData.lastBarRSIReverse > RM_InpRSIReverseCrossLevel)
|
||||
{
|
||||
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
|
||||
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
|
||||
}
|
||||
rmData.rsiReverseOversold = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckEMACrossStrategy(string symbol)
|
||||
{
|
||||
if(!IsWithinTradingHours(RM_InpEMACrossStartHour, RM_InpEMACrossEndHour))
|
||||
{
|
||||
if(RM_InpEMACrossCloseOutsideHours)
|
||||
{
|
||||
if(HasPosition(symbol, RM_InpMagicNumberEMACross))
|
||||
ClosePosition(symbol, RM_InpMagicNumberEMACross);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberEMACross))
|
||||
return;
|
||||
|
||||
if(rmData.lastBarEMAPrev < rmData.lastBarClosePrev && rmData.lastBarEMA > rmData.lastBarClose)
|
||||
{
|
||||
rmData.emaCrossBuySignal = true;
|
||||
rmData.emaCrossSellSignal = false;
|
||||
rmData.emaCrossSignalBar = 0;
|
||||
}
|
||||
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
|
||||
{
|
||||
rmData.emaCrossSellSignal = true;
|
||||
rmData.emaCrossBuySignal = false;
|
||||
rmData.emaCrossSignalBar = 0;
|
||||
}
|
||||
|
||||
if(RM_InpUseEMADistanceEntry)
|
||||
{
|
||||
if(rmData.emaCrossBuySignal)
|
||||
{
|
||||
bool distanceConditionMet = true;
|
||||
double emaHistory[], closeHistory[];
|
||||
ArraySetAsSeries(emaHistory, true);
|
||||
ArraySetAsSeries(closeHistory, true);
|
||||
|
||||
if(CopyBuffer(rmData.emaHandle, 0, 0, RM_InpEMADistancePeriod, emaHistory) > 0 &&
|
||||
CopyClose(symbol, RM_InpTimeframe, 0, RM_InpEMADistancePeriod, closeHistory) > 0)
|
||||
{
|
||||
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
for(int i = 0; i < RM_InpEMADistancePeriod; i++)
|
||||
{
|
||||
double distance = (closeHistory[i] - emaHistory[i]) / point;
|
||||
if(distance < RM_InpEMADistancePips)
|
||||
{
|
||||
distanceConditionMet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
|
||||
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
|
||||
rmData.emaCrossBuySignal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(rmData.emaCrossSellSignal)
|
||||
{
|
||||
bool distanceConditionMet = true;
|
||||
double emaHistory[], closeHistory[];
|
||||
ArraySetAsSeries(emaHistory, true);
|
||||
ArraySetAsSeries(closeHistory, true);
|
||||
|
||||
if(CopyBuffer(rmData.emaHandle, 0, 0, RM_InpEMADistancePeriod, emaHistory) > 0 &&
|
||||
CopyClose(symbol, RM_InpTimeframe, 0, RM_InpEMADistancePeriod, closeHistory) > 0)
|
||||
{
|
||||
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
for(int i = 0; i < RM_InpEMADistancePeriod; i++)
|
||||
{
|
||||
double distance = (emaHistory[i] - closeHistory[i]) / point;
|
||||
if(distance < RM_InpEMADistancePips)
|
||||
{
|
||||
distanceConditionMet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
|
||||
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
|
||||
rmData.emaCrossSellSignal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(rmData.lastBarEMAPrev < rmData.lastBarClosePrev && rmData.lastBarEMA > rmData.lastBarClose)
|
||||
{
|
||||
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
|
||||
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
|
||||
}
|
||||
}
|
||||
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
|
||||
{
|
||||
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
|
||||
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(rmData.emaCrossBuySignal || rmData.emaCrossSellSignal)
|
||||
{
|
||||
rmData.emaCrossSignalBar++;
|
||||
if(rmData.emaCrossSignalBar > RM_InpEMADistancePeriod * 2)
|
||||
{
|
||||
rmData.emaCrossBuySignal = false;
|
||||
rmData.emaCrossSellSignal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckExitConditions(string symbol)
|
||||
{
|
||||
if(RM_InpEnableRSIFollow)
|
||||
{
|
||||
if(HasPosition(symbol, RM_InpMagicNumberRSIFollow))
|
||||
{
|
||||
if(PositionSelectByMagic(symbol, RM_InpMagicNumberRSIFollow))
|
||||
{
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
if((posType == POSITION_TYPE_BUY && rmData.lastBarRSI < RM_InpRSIExitLevel) ||
|
||||
(posType == POSITION_TYPE_SELL && rmData.lastBarRSI > RM_InpRSIExitLevel))
|
||||
{
|
||||
ClosePosition(symbol, RM_InpMagicNumberRSIFollow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(RM_InpEnableRSIReverse)
|
||||
{
|
||||
if(HasPosition(symbol, RM_InpMagicNumberRSIReverse))
|
||||
{
|
||||
if(PositionSelectByMagic(symbol, RM_InpMagicNumberRSIReverse))
|
||||
{
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
if((posType == POSITION_TYPE_BUY && rmData.lastBarRSIReverse < RM_InpRSIReverseExitLevel) ||
|
||||
(posType == POSITION_TYPE_SELL && rmData.lastBarRSIReverse > RM_InpRSIReverseExitLevel))
|
||||
{
|
||||
ClosePosition(symbol, RM_InpMagicNumberRSIReverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(RM_InpEnableEMACross)
|
||||
{
|
||||
if(HasPosition(symbol, RM_InpMagicNumberEMACross))
|
||||
{
|
||||
if(PositionSelectByMagic(symbol, RM_InpMagicNumberEMACross))
|
||||
{
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
if((posType == POSITION_TYPE_BUY && rmData.lastBarEMA > rmData.lastBarClose) ||
|
||||
(posType == POSITION_TYPE_SELL && rmData.lastBarEMA < rmData.lastBarClose))
|
||||
{
|
||||
ClosePosition(symbol, RM_InpMagicNumberEMACross);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClosePosition(string symbol, int magic)
|
||||
{
|
||||
if(!PositionExistsByMagic(symbol, magic))
|
||||
return;
|
||||
|
||||
ulong ticket = GetPositionTicketByMagic(symbol, magic);
|
||||
if(ticket == 0)
|
||||
return;
|
||||
|
||||
if(magic == RM_InpMagicNumberRSIReverse)
|
||||
{
|
||||
if(PositionSelectByTicketSymbolAndMagic(ticket, symbol, magic))
|
||||
{
|
||||
datetime time[];
|
||||
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
rmData.rsiReverseLastCloseTime = time[0];
|
||||
double profit = PositionGetDouble(POSITION_PROFIT);
|
||||
if(!RM_InpRSIReverseCooldownOnLoss || profit < 0)
|
||||
{
|
||||
rmData.rsiReverseInCooldown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClosePositionByMagic(rmData.trade, symbol, magic);
|
||||
}
|
||||
|
||||
bool InitRSIMidPointHijack(string symbol)
|
||||
{
|
||||
rmData.symbol = symbol;
|
||||
rmData.rsiOverbought = false;
|
||||
rmData.rsiOversold = false;
|
||||
rmData.rsiReverseOverbought = false;
|
||||
rmData.rsiReverseOversold = false;
|
||||
rmData.emaCrossBuySignal = false;
|
||||
rmData.emaCrossSellSignal = false;
|
||||
rmData.emaCrossSignalBar = 0;
|
||||
rmData.rsiReverseInCooldown = false;
|
||||
rmData.lastBarRSI = 0;
|
||||
rmData.lastBarRSIReverse = 0;
|
||||
rmData.lastBarEMA = 0;
|
||||
rmData.lastBarClose = 0;
|
||||
rmData.lastBarEMAPrev = 0;
|
||||
rmData.lastBarClosePrev = 0;
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Print("RSIMidPointHijack: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sleep(100); // Wait for symbol to be ready
|
||||
|
||||
rmData.rsiHandle = iRSI(symbol, RM_InpTimeframe, RM_InpRSIPeriod, PRICE_CLOSE);
|
||||
rmData.rsiReverseHandle = iRSI(symbol, RM_InpTimeframe, RM_InpRSIReversePeriod, PRICE_CLOSE);
|
||||
rmData.emaHandle = iMA(symbol, RM_InpTimeframe, RM_InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
|
||||
if(rmData.rsiHandle == INVALID_HANDLE || rmData.rsiReverseHandle == INVALID_HANDLE || rmData.emaHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("RSIMidPointHijack: Error creating indicators for '", symbol, "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
|
||||
rmData.trade.SetMarginMode();
|
||||
rmData.trade.SetTypeFillingBySymbol(symbol);
|
||||
rmData.trade.SetDeviationInPoints(10);
|
||||
|
||||
datetime time[];
|
||||
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
|
||||
rmData.lastBarTime = time[0];
|
||||
|
||||
rmData.isInitialized = true;
|
||||
Print("RSIMidPointHijack: Successfully initialized for symbol '", symbol, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeinitRSIMidPointHijack()
|
||||
{
|
||||
if(rmData.rsiHandle != INVALID_HANDLE) IndicatorRelease(rmData.rsiHandle);
|
||||
if(rmData.rsiReverseHandle != INVALID_HANDLE) IndicatorRelease(rmData.rsiReverseHandle);
|
||||
if(rmData.emaHandle != INVALID_HANDLE) IndicatorRelease(rmData.emaHandle);
|
||||
}
|
||||
|
||||
void ProcessRSIMidPointHijack(string symbol)
|
||||
{
|
||||
// Skip if not initialized (symbol not available)
|
||||
if(!rmData.isInitialized)
|
||||
return;
|
||||
|
||||
rmData.symbol = symbol; // Update symbol in case it changed
|
||||
if(!IsNewBar(rmData.symbol))
|
||||
return;
|
||||
|
||||
double rsi[], rsiReverse[], ema[], close[];
|
||||
ArraySetAsSeries(rsi, true);
|
||||
ArraySetAsSeries(rsiReverse, true);
|
||||
ArraySetAsSeries(ema, true);
|
||||
ArraySetAsSeries(close, true);
|
||||
|
||||
rmData.lastBarEMAPrev = rmData.lastBarEMA;
|
||||
rmData.lastBarClosePrev = rmData.lastBarClose;
|
||||
|
||||
if(CopyBuffer(rmData.rsiHandle, 0, 0, 1, rsi) > 0)
|
||||
rmData.lastBarRSI = rsi[0];
|
||||
|
||||
if(CopyBuffer(rmData.rsiReverseHandle, 0, 0, 1, rsiReverse) > 0)
|
||||
rmData.lastBarRSIReverse = rsiReverse[0];
|
||||
|
||||
if(CopyBuffer(rmData.emaHandle, 0, 0, 1, ema) > 0)
|
||||
rmData.lastBarEMA = ema[0];
|
||||
|
||||
if(CopyClose(rmData.symbol, RM_InpTimeframe, 0, 1, close) > 0)
|
||||
rmData.lastBarClose = close[0];
|
||||
|
||||
if(RM_InpEnableRSIFollow)
|
||||
CheckRSIFollowStrategy(rmData.symbol);
|
||||
if(RM_InpEnableRSIReverse)
|
||||
CheckRSIReverseStrategy(rmData.symbol);
|
||||
if(RM_InpEnableEMACross)
|
||||
CheckEMACrossStrategy(rmData.symbol);
|
||||
|
||||
CheckExitConditions(rmData.symbol);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,493 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIReversalAsianStrategy.mqh |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSI Reversal Asian Strategy Data Structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSIReversalAsianData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
CTrade trade;
|
||||
bool isPositionOpen;
|
||||
double positionOpenPrice;
|
||||
datetime positionOpenTime;
|
||||
ENUM_POSITION_TYPE lastPositionType;
|
||||
bool sessionCloseAttempted;
|
||||
|
||||
// RSI crossover variables
|
||||
double rsiCurrent;
|
||||
double rsiPrevious;
|
||||
double rsiPrevious2;
|
||||
bool rsiCrossedOverbought;
|
||||
bool rsiCrossedOversold;
|
||||
bool rsiCrossedExitLevel;
|
||||
|
||||
// Strategy parameters
|
||||
int RSIPeriod;
|
||||
double OverboughtLevel;
|
||||
double OversoldLevel;
|
||||
int TakeProfitPips;
|
||||
int StopLossPips;
|
||||
double MaxLotSize;
|
||||
int MaxSpread;
|
||||
int MaxDuration;
|
||||
bool UseStopLoss;
|
||||
bool UseTakeProfit;
|
||||
bool UseRSIExit;
|
||||
double RSIExitLevel;
|
||||
bool CloseOutsideSession;
|
||||
ENUM_TIMEFRAMES TimeFrame;
|
||||
int MagicNumber;
|
||||
int Slippage;
|
||||
double point;
|
||||
};
|
||||
|
||||
// Session times (UTC)
|
||||
const int AsianSessionStart = 0; // 00:00 UTC
|
||||
const int AsianSessionEnd = 8; // 08:00 UTC
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if current time is in Asian session |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsAsianSession()
|
||||
{
|
||||
datetime currentTime = TimeCurrent();
|
||||
MqlDateTime timeStruct;
|
||||
TimeToStruct(currentTime, timeStruct);
|
||||
|
||||
return (timeStruct.hour >= AsianSessionStart && timeStruct.hour < AsianSessionEnd);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if trading is allowed for symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsTradingAllowed(RSIReversalAsianData& data)
|
||||
{
|
||||
// Check if market is open
|
||||
long tradeMode = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_MODE);
|
||||
if(tradeMode != SYMBOL_TRADE_MODE_FULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we have enough money
|
||||
if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check RSI crossover conditions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckRSICrossover(RSIReversalAsianData& data)
|
||||
{
|
||||
// Reset crossover flags
|
||||
data.rsiCrossedOverbought = false;
|
||||
data.rsiCrossedOversold = false;
|
||||
data.rsiCrossedExitLevel = false;
|
||||
|
||||
// Check for overbought crossover (RSI crosses above overbought level)
|
||||
if(data.rsiPrevious < data.OverboughtLevel && data.rsiCurrent >= data.OverboughtLevel)
|
||||
{
|
||||
data.rsiCrossedOverbought = true;
|
||||
}
|
||||
|
||||
// Check for oversold crossover (RSI crosses below oversold level)
|
||||
if(data.rsiPrevious > data.OversoldLevel && data.rsiCurrent <= data.OversoldLevel)
|
||||
{
|
||||
data.rsiCrossedOversold = true;
|
||||
}
|
||||
|
||||
// Check for exit level crossover
|
||||
if(data.rsiPrevious < data.RSIExitLevel && data.rsiCurrent >= data.RSIExitLevel)
|
||||
{
|
||||
data.rsiCrossedExitLevel = true;
|
||||
}
|
||||
else if(data.rsiPrevious > data.RSIExitLevel && data.rsiCurrent <= data.RSIExitLevel)
|
||||
{
|
||||
data.rsiCrossedExitLevel = true;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close all trades for the symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CloseAllTrades(RSIReversalAsianData& data, string reason = "")
|
||||
{
|
||||
bool allClosed = true;
|
||||
int totalPositions = PositionsTotal();
|
||||
|
||||
if(totalPositions == 0)
|
||||
return true;
|
||||
|
||||
for(int i = totalPositions - 1; i >= 0; i--)
|
||||
{
|
||||
if(PositionGetSymbol(i) == data.symbol)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket > 0 && PositionSelectByTicket(ticket))
|
||||
{
|
||||
if(PositionGetInteger(POSITION_MAGIC) == (ulong)data.MagicNumber)
|
||||
{
|
||||
// Try to close position with retry logic
|
||||
int retryCount = 0;
|
||||
bool positionClosed = false;
|
||||
|
||||
while(retryCount < 3 && !positionClosed)
|
||||
{
|
||||
if(data.trade.PositionClose(ticket))
|
||||
{
|
||||
data.isPositionOpen = false;
|
||||
positionClosed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int error = GetLastError();
|
||||
|
||||
// If error is 4756 (Trade disabled), wait longer before retry
|
||||
if(error == 4756)
|
||||
{
|
||||
Sleep(5000); // Wait 5 seconds before retry
|
||||
retryCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For other errors, break the loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!positionClosed)
|
||||
{
|
||||
allClosed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allClosed;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Initialize RSI Reversal Asian Strategy |
|
||||
//+------------------------------------------------------------------+
|
||||
bool InitRSIReversalAsian(RSIReversalAsianData& data, string symbol,
|
||||
int RSIPeriod, double OverboughtLevel, double OversoldLevel,
|
||||
int TakeProfitPips, int StopLossPips, double MaxLotSize,
|
||||
int MaxSpread, int MaxDuration, bool UseStopLoss,
|
||||
bool UseTakeProfit, bool UseRSIExit, double RSIExitLevel,
|
||||
bool CloseOutsideSession, ENUM_TIMEFRAMES TimeFrame,
|
||||
int MagicNumber, int Slippage)
|
||||
{
|
||||
data.symbol = symbol;
|
||||
data.isInitialized = false;
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Print("RSIReversalAsian: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait a bit for symbol to be ready
|
||||
Sleep(100);
|
||||
|
||||
// Get symbol point
|
||||
data.point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
|
||||
// Store parameters
|
||||
data.RSIPeriod = RSIPeriod;
|
||||
data.OverboughtLevel = OverboughtLevel;
|
||||
data.OversoldLevel = OversoldLevel;
|
||||
data.TakeProfitPips = TakeProfitPips;
|
||||
data.StopLossPips = StopLossPips;
|
||||
data.MaxLotSize = MaxLotSize;
|
||||
data.MaxSpread = MaxSpread;
|
||||
data.MaxDuration = MaxDuration;
|
||||
data.UseStopLoss = UseStopLoss;
|
||||
data.UseTakeProfit = UseTakeProfit;
|
||||
data.UseRSIExit = UseRSIExit;
|
||||
data.RSIExitLevel = RSIExitLevel;
|
||||
data.CloseOutsideSession = CloseOutsideSession;
|
||||
data.TimeFrame = TimeFrame;
|
||||
data.MagicNumber = MagicNumber;
|
||||
data.Slippage = Slippage;
|
||||
|
||||
// Initialize RSI indicator with retry logic (for insufficient history in backtesting)
|
||||
data.rsiHandle = INVALID_HANDLE;
|
||||
int retryCount = 0;
|
||||
int maxRetries = 5;
|
||||
|
||||
while(retryCount < maxRetries && data.rsiHandle == INVALID_HANDLE)
|
||||
{
|
||||
data.rsiHandle = iRSI(symbol, TimeFrame, RSIPeriod, PRICE_CLOSE);
|
||||
|
||||
if(data.rsiHandle == INVALID_HANDLE)
|
||||
{
|
||||
int error = GetLastError();
|
||||
|
||||
// Error 4805 = insufficient history - wait longer and retry
|
||||
if(error == 4805 && retryCount < maxRetries - 1)
|
||||
{
|
||||
Sleep(1000); // Wait 1 second for history to load
|
||||
retryCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Print("RSIReversalAsian: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", error == 4805 ? "Insufficient history data" : "Unknown", ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(data.rsiHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("RSIReversalAsian: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait a bit for the indicator to be ready
|
||||
Sleep(100);
|
||||
|
||||
// Initialize RSI values with retry logic
|
||||
double rsi[];
|
||||
ArraySetAsSeries(rsi, true);
|
||||
|
||||
retryCount = 0;
|
||||
bool rsiInitialized = false;
|
||||
|
||||
while(retryCount < 10 && !rsiInitialized)
|
||||
{
|
||||
int copied = CopyBuffer(data.rsiHandle, 0, 0, 3, rsi);
|
||||
if(copied >= 3)
|
||||
{
|
||||
data.rsiCurrent = rsi[0];
|
||||
data.rsiPrevious = rsi[1];
|
||||
data.rsiPrevious2 = rsi[2];
|
||||
rsiInitialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
retryCount++;
|
||||
Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
if(!rsiInitialized)
|
||||
{
|
||||
// Don't fail initialization, just set default values
|
||||
data.rsiCurrent = 50.0;
|
||||
data.rsiPrevious = 50.0;
|
||||
data.rsiPrevious2 = 50.0;
|
||||
}
|
||||
|
||||
// Set trade parameters
|
||||
data.trade.SetExpertMagicNumber(MagicNumber);
|
||||
data.trade.SetDeviationInPoints(Slippage);
|
||||
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
|
||||
// Initialize state
|
||||
data.isPositionOpen = false;
|
||||
data.positionOpenPrice = 0;
|
||||
data.positionOpenTime = 0;
|
||||
data.lastPositionType = POSITION_TYPE_BUY;
|
||||
data.sessionCloseAttempted = false;
|
||||
data.rsiCrossedOverbought = false;
|
||||
data.rsiCrossedOversold = false;
|
||||
data.rsiCrossedExitLevel = false;
|
||||
|
||||
data.isInitialized = true;
|
||||
|
||||
Print("RSIReversalAsian: Successfully initialized for symbol '", symbol, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Deinitialize RSI Reversal Asian Strategy |
|
||||
//+------------------------------------------------------------------+
|
||||
void DeinitRSIReversalAsian(RSIReversalAsianData& data)
|
||||
{
|
||||
if(data.rsiHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(data.rsiHandle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Process RSI Reversal Asian Strategy |
|
||||
//+------------------------------------------------------------------+
|
||||
void ProcessRSIReversalAsian(RSIReversalAsianData& data, double lotSize)
|
||||
{
|
||||
if(!data.isInitialized)
|
||||
return;
|
||||
|
||||
// Check if trading is allowed
|
||||
if(!IsTradingAllowed(data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we're in Asian session
|
||||
if(!IsAsianSession())
|
||||
{
|
||||
// Close all positions if outside Asian session and CloseOutsideSession is true
|
||||
if(data.CloseOutsideSession && !data.sessionCloseAttempted)
|
||||
{
|
||||
CloseAllTrades(data, "Outside Asian session");
|
||||
data.sessionCloseAttempted = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset the session close attempt flag when we enter Asian session
|
||||
data.sessionCloseAttempted = false;
|
||||
}
|
||||
|
||||
// Get current spread
|
||||
double spread = SymbolInfoDouble(data.symbol, SYMBOL_ASK) - SymbolInfoDouble(data.symbol, SYMBOL_BID);
|
||||
int spreadInPips = (int)(spread / data.point);
|
||||
|
||||
// Check if spread is too high
|
||||
if(spreadInPips > data.MaxSpread)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get RSI values from bar data
|
||||
double rsi[];
|
||||
ArraySetAsSeries(rsi, true);
|
||||
|
||||
int copied = CopyBuffer(data.rsiHandle, 0, 0, 3, rsi);
|
||||
if(copied < 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update RSI values
|
||||
data.rsiPrevious2 = data.rsiPrevious;
|
||||
data.rsiPrevious = data.rsiCurrent;
|
||||
data.rsiCurrent = rsi[0];
|
||||
|
||||
// Validate RSI values
|
||||
if(data.rsiCurrent == 0 || data.rsiPrevious == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for RSI crossovers
|
||||
CheckRSICrossover(data);
|
||||
|
||||
// Get current prices
|
||||
double currentBid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
|
||||
double currentAsk = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
|
||||
|
||||
// Check for open position
|
||||
bool hasOpenPosition = PositionExistsByMagic(data.symbol, (ulong)data.MagicNumber);
|
||||
|
||||
if(hasOpenPosition)
|
||||
{
|
||||
// Get position details
|
||||
ulong ticket = GetPositionTicketByMagic(data.symbol, (ulong)data.MagicNumber);
|
||||
if(ticket > 0 && PositionSelectByTicket(ticket))
|
||||
{
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
|
||||
|
||||
// Check for RSI exit if enabled
|
||||
if(data.UseRSIExit && data.rsiCrossedExitLevel)
|
||||
{
|
||||
bool shouldExit = false;
|
||||
|
||||
// For long positions, exit when RSI crosses above exit level
|
||||
if(posType == POSITION_TYPE_BUY && data.rsiCurrent >= data.RSIExitLevel && data.rsiPrevious < data.RSIExitLevel)
|
||||
{
|
||||
shouldExit = true;
|
||||
}
|
||||
// For short positions, exit when RSI crosses below exit level
|
||||
else if(posType == POSITION_TYPE_SELL && data.rsiCurrent <= data.RSIExitLevel && data.rsiPrevious > data.RSIExitLevel)
|
||||
{
|
||||
shouldExit = true;
|
||||
}
|
||||
|
||||
if(shouldExit)
|
||||
{
|
||||
CloseAllTrades(data, "RSI Exit Crossover");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for timeout
|
||||
if(TimeCurrent() - openTime > data.MaxDuration * 3600)
|
||||
{
|
||||
CloseAllTrades(data, "Timeout");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no position is open, look for entry signals based on RSI crossover
|
||||
if(!hasOpenPosition)
|
||||
{
|
||||
// Place buy order if RSI crosses below oversold level (oversold crossover)
|
||||
if(data.rsiCrossedOversold)
|
||||
{
|
||||
double sl = data.UseStopLoss ? currentBid - data.StopLossPips * data.point : 0;
|
||||
double tp = data.UseTakeProfit ? currentBid + data.TakeProfitPips * data.point : 0;
|
||||
|
||||
if(data.UseStopLoss && sl >= currentBid)
|
||||
return;
|
||||
if(data.UseTakeProfit && tp <= currentBid)
|
||||
return;
|
||||
|
||||
// Set trade parameters
|
||||
data.trade.SetDeviationInPoints(data.Slippage);
|
||||
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
data.trade.SetExpertMagicNumber(data.MagicNumber);
|
||||
|
||||
// Use dynamic lot size
|
||||
double tradeLotSize = lotSize > 0 ? lotSize : data.MaxLotSize;
|
||||
|
||||
// Place buy order using CTrade
|
||||
if(data.trade.Buy(tradeLotSize, data.symbol, currentAsk, sl, tp, "RSI Oversold Crossover Buy"))
|
||||
{
|
||||
data.isPositionOpen = true;
|
||||
data.positionOpenPrice = currentAsk;
|
||||
data.positionOpenTime = TimeCurrent();
|
||||
data.lastPositionType = POSITION_TYPE_BUY;
|
||||
}
|
||||
}
|
||||
// Place sell order if RSI crosses above overbought level (overbought crossover)
|
||||
else if(data.rsiCrossedOverbought)
|
||||
{
|
||||
double sl = data.UseStopLoss ? currentAsk + data.StopLossPips * data.point : 0;
|
||||
double tp = data.UseTakeProfit ? currentAsk - data.TakeProfitPips * data.point : 0;
|
||||
|
||||
if(data.UseStopLoss && sl <= currentAsk)
|
||||
return;
|
||||
if(data.UseTakeProfit && tp >= currentAsk)
|
||||
return;
|
||||
|
||||
// Set trade parameters
|
||||
data.trade.SetDeviationInPoints(data.Slippage);
|
||||
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
data.trade.SetExpertMagicNumber(data.MagicNumber);
|
||||
|
||||
// Use dynamic lot size
|
||||
double tradeLotSize = lotSize > 0 ? lotSize : data.MaxLotSize;
|
||||
|
||||
// Place sell order using CTrade
|
||||
if(data.trade.Sell(tradeLotSize, data.symbol, currentBid, sl, tp, "RSI Overbought Crossover Sell"))
|
||||
{
|
||||
data.isPositionOpen = true;
|
||||
data.positionOpenPrice = currentBid;
|
||||
data.positionOpenTime = TimeCurrent();
|
||||
data.lastPositionType = POSITION_TYPE_SELL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIScalpingStrategy.mqh |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSI Scalping Strategy Data Structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSIScalpingData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
CTrade trade;
|
||||
int rsi_handle;
|
||||
double rsi_buffer[];
|
||||
double rsi_prev;
|
||||
double rsi_current;
|
||||
double rsi_two_bars_ago;
|
||||
bool position_open;
|
||||
ulong position_ticket;
|
||||
ENUM_POSITION_TYPE current_position_type;
|
||||
datetime last_bar_time;
|
||||
bool rsi_against_position;
|
||||
int bars_against_count;
|
||||
};
|
||||
|
||||
string ErrorDescription(int errorCode)
|
||||
{
|
||||
switch(errorCode)
|
||||
{
|
||||
case 4801: return "Symbol not found";
|
||||
case 4802: return "Symbol not selected";
|
||||
case 4803: return "Symbol not visible";
|
||||
case 4804: return "Symbol not available";
|
||||
case 4805: return "Cannot load indicator - insufficient history data";
|
||||
default: return "Unknown error " + IntegerToString(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
bool InitRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
|
||||
ENUM_APPLIED_PRICE RSI_Applied_Price, int MagicNumber, int Slippage)
|
||||
{
|
||||
data.symbol = symbol;
|
||||
data.isInitialized = false;
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Print("RSIScalping: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
||||
return false; // Return false but don't fail entire EA
|
||||
}
|
||||
|
||||
// Wait a bit for symbol to be ready
|
||||
Sleep(100);
|
||||
|
||||
// Try to create RSI indicator with retry logic (for insufficient history in backtesting)
|
||||
data.rsi_handle = INVALID_HANDLE;
|
||||
int retryCount = 0;
|
||||
int maxRetries = 5;
|
||||
|
||||
while(retryCount < maxRetries && data.rsi_handle == INVALID_HANDLE)
|
||||
{
|
||||
data.rsi_handle = iRSI(symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
|
||||
|
||||
if(data.rsi_handle == INVALID_HANDLE)
|
||||
{
|
||||
int error = GetLastError();
|
||||
|
||||
// Error 4805 = insufficient history - wait longer and retry
|
||||
if(error == 4805 && retryCount < maxRetries - 1)
|
||||
{
|
||||
Sleep(1000); // Wait 1 second for history to load
|
||||
retryCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Print("RSIScalping: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", ErrorDescription(error), ")");
|
||||
return false; // Return false but don't fail entire EA
|
||||
}
|
||||
}
|
||||
|
||||
if(data.rsi_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("RSIScalping: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries");
|
||||
return false;
|
||||
}
|
||||
|
||||
data.trade.SetExpertMagicNumber(MagicNumber);
|
||||
data.trade.SetDeviationInPoints(Slippage);
|
||||
data.trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
ArraySetAsSeries(data.rsi_buffer, true);
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
data.isInitialized = true;
|
||||
|
||||
Print("RSIScalping: Successfully initialized for symbol '", symbol, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeinitRSIScalping(RSIScalpingData& data)
|
||||
{
|
||||
if(data.rsi_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(data.rsi_handle);
|
||||
}
|
||||
|
||||
bool UpdateRSI(RSIScalpingData& data)
|
||||
{
|
||||
if(CopyBuffer(data.rsi_handle, 0, 0, 3, data.rsi_buffer) < 3)
|
||||
return false;
|
||||
|
||||
data.rsi_current = data.rsi_buffer[0];
|
||||
data.rsi_prev = data.rsi_buffer[1];
|
||||
data.rsi_two_bars_ago = data.rsi_buffer[2];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CheckExistingPosition(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
|
||||
double RSI_Oversold, double RSI_Overbought, double RSI_Target_Buy,
|
||||
double RSI_Target_Sell, int BarsToWait)
|
||||
{
|
||||
// Always check if position exists, even if tracking says it doesn't
|
||||
bool positionExists = PositionExistsByMagic(data.symbol, MagicNumber);
|
||||
|
||||
if(!positionExists && data.position_open)
|
||||
{
|
||||
// Position was closed externally, reset tracking
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!positionExists)
|
||||
return;
|
||||
|
||||
// Update tracking if we have a position but tracking was lost
|
||||
if(!data.position_open && positionExists)
|
||||
{
|
||||
ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
|
||||
if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
|
||||
{
|
||||
data.position_ticket = ticket;
|
||||
data.position_open = true;
|
||||
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify our tracked position still exists
|
||||
if(data.position_open && data.position_ticket > 0)
|
||||
{
|
||||
if(!PositionSelectByTicketSymbolAndMagic(data.position_ticket, data.symbol, MagicNumber))
|
||||
{
|
||||
// Try to find the position again
|
||||
ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
|
||||
if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
|
||||
{
|
||||
data.position_ticket = ticket;
|
||||
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Position doesn't exist, reset tracking
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update position type in case it changed (shouldn't happen, but be safe)
|
||||
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
if(data.current_position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(data.rsi_current < RSI_Oversold)
|
||||
{
|
||||
if(!data.rsi_against_position)
|
||||
{
|
||||
data.rsi_against_position = true;
|
||||
data.bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.bars_against_count++;
|
||||
}
|
||||
|
||||
if(data.bars_against_count >= BarsToWait)
|
||||
{
|
||||
ClosePosition(data, MagicNumber);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(data.rsi_against_position)
|
||||
{
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
}
|
||||
|
||||
if(data.rsi_current >= RSI_Target_Buy)
|
||||
{
|
||||
ClosePosition(data, MagicNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(data.current_position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
if(data.rsi_current > RSI_Overbought)
|
||||
{
|
||||
if(!data.rsi_against_position)
|
||||
{
|
||||
data.rsi_against_position = true;
|
||||
data.bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.bars_against_count++;
|
||||
}
|
||||
|
||||
if(data.bars_against_count >= BarsToWait)
|
||||
{
|
||||
ClosePosition(data, MagicNumber);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(data.rsi_against_position)
|
||||
{
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
}
|
||||
|
||||
if(data.rsi_current <= RSI_Target_Sell)
|
||||
{
|
||||
ClosePosition(data, MagicNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckEntrySignals(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
|
||||
double RSI_Oversold, double RSI_Overbought, double LotSize)
|
||||
{
|
||||
if(data.rsi_two_bars_ago <= RSI_Oversold && data.rsi_prev > RSI_Oversold)
|
||||
{
|
||||
OpenBuyPosition(data, MagicNumber, LotSize);
|
||||
}
|
||||
|
||||
if(data.rsi_two_bars_ago >= RSI_Overbought && data.rsi_prev < RSI_Overbought)
|
||||
{
|
||||
OpenSellPosition(data, MagicNumber, LotSize);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Normalize Lot Size According to Symbol Properties |
|
||||
//+------------------------------------------------------------------+
|
||||
double NormalizeLotSize(string symbol, double lotSize)
|
||||
{
|
||||
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
||||
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
||||
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
||||
|
||||
// Round to lot step
|
||||
if(lotStep > 0)
|
||||
lotSize = MathFloor(lotSize / lotStep) * lotStep;
|
||||
|
||||
// Apply min/max constraints
|
||||
if(lotSize < minLot)
|
||||
lotSize = minLot;
|
||||
if(lotSize > maxLot)
|
||||
lotSize = maxLot;
|
||||
|
||||
return lotSize;
|
||||
}
|
||||
|
||||
void OpenBuyPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
|
||||
{
|
||||
if(PositionExistsByMagic(data.symbol, MagicNumber))
|
||||
return;
|
||||
|
||||
// Normalize lot size according to symbol properties
|
||||
double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
|
||||
|
||||
double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
|
||||
|
||||
if(data.trade.Buy(normalizedLot, data.symbol, ask, 0, 0, "RSI Scalping Buy"))
|
||||
{
|
||||
ulong new_ticket = data.trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
|
||||
{
|
||||
data.position_ticket = new_ticket;
|
||||
data.position_open = true;
|
||||
data.current_position_type = POSITION_TYPE_BUY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenSellPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
|
||||
{
|
||||
if(PositionExistsByMagic(data.symbol, MagicNumber))
|
||||
return;
|
||||
|
||||
// Normalize lot size according to symbol properties
|
||||
double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
|
||||
|
||||
double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
|
||||
|
||||
if(data.trade.Sell(normalizedLot, data.symbol, bid, 0, 0, "RSI Scalping Sell"))
|
||||
{
|
||||
ulong new_ticket = data.trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
|
||||
{
|
||||
data.position_ticket = new_ticket;
|
||||
data.position_open = true;
|
||||
data.current_position_type = POSITION_TYPE_SELL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClosePosition(RSIScalpingData& data, int MagicNumber)
|
||||
{
|
||||
// First verify position still exists
|
||||
if(!PositionExistsByMagic(data.symbol, MagicNumber))
|
||||
{
|
||||
// Position doesn't exist, reset tracking
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to close by ticket first (more reliable)
|
||||
bool closed = false;
|
||||
if(data.position_ticket > 0)
|
||||
{
|
||||
if(PositionSelectByTicket(data.position_ticket))
|
||||
{
|
||||
// Verify it's our position
|
||||
if(PositionGetString(POSITION_SYMBOL) == data.symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
|
||||
{
|
||||
closed = data.trade.PositionClose(data.position_ticket);
|
||||
if(!closed)
|
||||
{
|
||||
Print("RSIScalping: Failed to close position by ticket ", data.position_ticket,
|
||||
" - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If ticket method failed, try magic number method
|
||||
if(!closed)
|
||||
{
|
||||
closed = ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
|
||||
if(!closed)
|
||||
{
|
||||
Print("RSIScalping: Failed to close position by magic number for '", data.symbol,
|
||||
"' - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
|
||||
}
|
||||
}
|
||||
|
||||
// Verify position is actually closed
|
||||
if(closed)
|
||||
{
|
||||
// Wait a moment and verify
|
||||
Sleep(50);
|
||||
if(!PositionExistsByMagic(data.symbol, MagicNumber))
|
||||
{
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
Print("RSIScalping: Position successfully closed for '", data.symbol, "'");
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("RSIScalping: Warning - Close returned success but position still exists for '", data.symbol, "'");
|
||||
// Try one more time
|
||||
Sleep(100);
|
||||
if(PositionExistsByMagic(data.symbol, MagicNumber))
|
||||
{
|
||||
ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
|
||||
}
|
||||
// Reset tracking anyway to prevent getting stuck
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Close failed, but reset tracking to prevent getting stuck
|
||||
// The position might have been closed externally
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
|
||||
ENUM_APPLIED_PRICE RSI_Applied_Price, double RSI_Overbought,
|
||||
double RSI_Oversold, double RSI_Target_Buy, double RSI_Target_Sell,
|
||||
int BarsToWait, double LotSize, int MagicNumber)
|
||||
{
|
||||
// Skip if not initialized (symbol not available)
|
||||
if(!data.isInitialized)
|
||||
return;
|
||||
|
||||
data.symbol = symbol; // Update symbol in case it changed
|
||||
if(Bars(data.symbol, TimeFrame) < RSI_Period + 2)
|
||||
return;
|
||||
|
||||
datetime current_bar_time = iTime(data.symbol, TimeFrame, 0);
|
||||
if(current_bar_time == data.last_bar_time)
|
||||
return;
|
||||
|
||||
data.last_bar_time = current_bar_time;
|
||||
|
||||
if(!UpdateRSI(data))
|
||||
return;
|
||||
|
||||
CheckExistingPosition(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought,
|
||||
RSI_Target_Buy, RSI_Target_Sell, BarsToWait);
|
||||
|
||||
if(!data.position_open && !PositionExistsByMagic(data.symbol, MagicNumber))
|
||||
{
|
||||
CheckEntrySignals(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought, LotSize);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,675 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| UnitedEA.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.07"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include <Trade\PositionInfo.mqh>
|
||||
#include <Indicators\Trend.mqh>
|
||||
#include <Indicators\Volumes.mqh>
|
||||
#include "MagicNumberHelpers.mqh"
|
||||
|
||||
// Lot globals must exist before strategy .mqh (Darvas uses g_DB_LotSize; EMA/RC/RM use g_ES/g_RC/g_RM)
|
||||
double g_ES_LotSize;
|
||||
double g_RC_LotSize;
|
||||
double g_RM_LotSize;
|
||||
double g_DB_LotSize;
|
||||
double g_DynMultLast = 1.0;
|
||||
|
||||
// Include strategy implementations early so structs are available
|
||||
#include "Strategies/DarvasBoxStrategy.mqh"
|
||||
#include "Strategies/EMASlopeDistanceStrategy.mqh"
|
||||
#include "Strategies/RSICrossOverReversalStrategy.mqh"
|
||||
#include "Strategies/RSIMidPointHijackStrategy.mqh"
|
||||
#include "Strategies/RSIScalpingStrategy.mqh"
|
||||
#include "Strategies/RSIReversalAsianStrategy.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy Enable/Disable Switches |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== Strategy Enable/Disable ==="
|
||||
input bool EnableDarvasBox = true;
|
||||
input bool EnableEMASlopeDistance = true;
|
||||
input bool EnableRSICrossOverReversal = true;
|
||||
input bool EnableRSIMidPointHijack = true;
|
||||
input bool EnableRSIScalpingAPPL = true;
|
||||
input bool EnableRSIScalpingBTCUSD = true;
|
||||
input bool EnableRSIScalpingNVDA = true;
|
||||
input bool EnableRSIScalpingTSLA = true;
|
||||
input bool EnableRSIScalpingXAUUSD = true;
|
||||
input bool EnableRSIReversalAsianEURUSD = true;
|
||||
input bool EnableRSIReversalAsianAUDUSD = true;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Dynamic lot sizing — scale base lots vs reference deposit |
|
||||
//| mult=(equity/ref)^exp; maxMult<=0 上不封顶; minMult<=0 不锁下限 |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== Dynamic lot sizing (动态手数) ==="
|
||||
input bool InpDynamicLotEnable = true; // Enable balance/equity-based scaling
|
||||
input double InpDynamicRefDeposit = 3000.0; // Reference balance (match Tester initial deposit)
|
||||
input double InpDynamicExponent = 1.15; // 1.0=linear; >1 faster growth; <1 conservative
|
||||
input double InpDynamicMinMult = 0.0; // <=0 不锁下限; >0 例如0.25 为最低倍数
|
||||
input double InpDynamicMaxMult = 0.0; // <=0 动态倍数不封顶; >0 上限封顶
|
||||
input bool InpDynamicUseEquity = true; // true=ACCOUNT_EQUITY, false=ACCOUNT_BALANCE
|
||||
input double InpDynamicStockLotCap = 0.0; // Extra cap for stock CFDs (0 = none)
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 1: DarvasBoxXAUUSD |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== DarvasBox Strategy ==="
|
||||
input string DB_Symbol = "XAUUSD";
|
||||
input int DB_BoxPeriod = 165;
|
||||
input double DB_BoxDeviation = 30000; // Increased to allow larger ranges (was 25140)
|
||||
input int DB_VolumeThreshold = 0; // Set to 0 to disable volume threshold check. Volume data from indicator used instead.
|
||||
input double DB_StopLoss = 1665;
|
||||
input double DB_TakeProfit = 3685;
|
||||
input bool DB_EnableLogging = false;
|
||||
input color DB_BoxColor = clrBlue;
|
||||
input int DB_BoxWidth = 1;
|
||||
input ENUM_TIMEFRAMES DB_TrendTimeframe = PERIOD_H2;
|
||||
input int DB_MA_Period = 125;
|
||||
input ENUM_MA_METHOD DB_MA_Method = MODE_EMA;
|
||||
input ENUM_APPLIED_PRICE DB_MA_Price = PRICE_WEIGHTED;
|
||||
input double DB_TrendThreshold = 4.94;
|
||||
input int DB_VolumeMA_Period = 110;
|
||||
input double DB_VolumeThresholdMultiplier = 1.5;
|
||||
input int DB_MagicNumber = 135790;
|
||||
input double DB_BaseLotSize = 0.01; // Base lot at InpDynamicRefDeposit (Darvas)
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 2: EMASlopeDistanceCocktailXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== EMA Slope Distance Strategy ==="
|
||||
input string ES_Symbol = "XAUUSD";
|
||||
input int ES_EMA_Periode = 46;
|
||||
input double ES_PreisSchwelle = 600.0;
|
||||
input double ES_SteigungSchwelle = 80.0;
|
||||
input int ES_ÜberwachungTimeout = 800;
|
||||
input double ES_TrailingStop = 250.0;
|
||||
input double ES_LotGröße = 0.03;
|
||||
input int ES_MagicNumber = 12350;
|
||||
input bool ES_UseSpreadAdjustment = true;
|
||||
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
|
||||
input bool ES_UseBarData = true;
|
||||
input int ES_MaxTradesPerCrossover = 9;
|
||||
input int ES_ProfitCheckBars = 18;
|
||||
input bool ES_CloseUnprofitableTrades = true;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 3: RSICrossOverReversalXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI CrossOver Reversal Strategy ==="
|
||||
input string RC_Symbol = "XAUUSD";
|
||||
input int RC_MagicNumber = 7;
|
||||
input int RC_rsiPeriod = 19;
|
||||
input int RC_overboughtLevel = 93;
|
||||
input int RC_oversoldLevel = 22;
|
||||
input double RC_entryRSIBuySpread = 0;
|
||||
input double RC_entryRSISellSpread = 0;
|
||||
input double RC_lotSize = 0.01;
|
||||
input int RC_slippage = 3;
|
||||
input int RC_cooldownSeconds = 209;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame2 = PERIOD_M1;
|
||||
input ENUM_TIMEFRAMES RC_BarTimeFrame = PERIOD_M12;
|
||||
input int RC_emaPeriod = 140;
|
||||
input double RC_emaSlopeThreshold = 105;
|
||||
input double RC_exitBuyRSI = 86;
|
||||
input double RC_exitSellRSI = 10;
|
||||
input double RC_TrailingStop = 295;
|
||||
input double RC_emaDistanceThreshold = 165;
|
||||
input int RC_tradingHourOneBegin = 24;
|
||||
input int RC_tradingHourOneEnd = 22;
|
||||
input int RC_tradingHourTwoBegin = 6;
|
||||
input int RC_tradingHourTwoEnd = 19;
|
||||
input bool RC_Sunday = false;
|
||||
input bool RC_Monday = false;
|
||||
input bool RC_Tuesday = true;
|
||||
input bool RC_Wednesday = true;
|
||||
input bool RC_Thursday = true;
|
||||
input bool RC_Friday = false;
|
||||
input bool RC_Saturday = false;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 4: RSIMidPointHijackXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI MidPoint Hijack Strategy ==="
|
||||
input string RM_Symbol = "XAUUSD";
|
||||
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
|
||||
input double RM_InpLotSize = 0.02;
|
||||
input int RM_InpMagicNumberRSIFollow = 1001;
|
||||
input int RM_InpMagicNumberRSIReverse = 1002;
|
||||
input int RM_InpMagicNumberEMACross = 1003;
|
||||
input bool RM_InpEnableRSIFollow = true;
|
||||
input bool RM_InpEnableRSIReverse = true;
|
||||
input bool RM_InpEnableEMACross = true;
|
||||
input bool RM_InpEnableStrategyLock = false;
|
||||
input double RM_InpLockProfitThreshold = 0.0;
|
||||
input bool RM_InpCloseOppositeTrades = false;
|
||||
input int RM_InpRSIPeriod = 32;
|
||||
input int RM_InpRSIOverbought = 78;
|
||||
input int RM_InpRSIOversold = 46;
|
||||
input int RM_InpRSIExitLevel = 44;
|
||||
input int RM_InpRSIFollowStartHour = 23;
|
||||
input int RM_InpRSIFollowEndHour = 8;
|
||||
input bool RM_InpRSIFollowCloseOutsideHours = false;
|
||||
input int RM_InpRSIReversePeriod = 59;
|
||||
input int RM_InpRSIReverseOverbought = 51;
|
||||
input int RM_InpRSIReverseOversold = 49;
|
||||
input int RM_InpRSIReverseCrossLevel = 53;
|
||||
input int RM_InpRSIReverseExitLevel = 48;
|
||||
input int RM_InpRSIReverseStartHour = 7;
|
||||
input int RM_InpRSIReverseEndHour = 13;
|
||||
input bool RM_InpRSIReverseCloseOutsideHours = false;
|
||||
input int RM_InpRSIReverseCooldownBars = 15;
|
||||
input bool RM_InpRSIReverseCooldownOnLoss = true;
|
||||
input int RM_InpEMAPeriod = 120;
|
||||
input int RM_InpEMACrossStartHour = 8;
|
||||
input int RM_InpEMACrossEndHour = 14;
|
||||
input bool RM_InpEMACrossCloseOutsideHours = true;
|
||||
input bool RM_InpUseEMADistanceEntry = true;
|
||||
input double RM_InpEMADistancePips = 160.0;
|
||||
input int RM_InpEMADistancePeriod = 26;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 5-10: RSI Scalping Strategies |
|
||||
//| Each RSI Scalping strategy trades on its own symbol: |
|
||||
//| - APPL: Apple stock (AAPL) |
|
||||
//| - BTCUSD: Bitcoin/USD |
|
||||
//| - NVDA: NVIDIA stock |
|
||||
//| - TSLA: Tesla stock |
|
||||
//| - XAUUSD: Gold/USD |
|
||||
//| |
|
||||
//| PEPPERSTONE US SYMBOL FORMATS: |
|
||||
//| - Stocks may use: "AAPL.US", "NASDAQ:AAPL", or just "AAPL" |
|
||||
//| - To find correct symbols: |
|
||||
//| 1. Open Market Watch (Ctrl+M) |
|
||||
//| 2. Right-click > Show All |
|
||||
//| 3. Search for the stock name |
|
||||
//| 4. Use the exact symbol name shown |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI Scalping APPL (AAPL) - Pepperstone US ==="
|
||||
input string RS_APPL_Symbol = "AAPL.US"; // Try: "AAPL.US", "NASDAQ:AAPL", or "AAPL"
|
||||
input ENUM_TIMEFRAMES RS_APPL_TimeFrame = PERIOD_M10;
|
||||
input int RS_APPL_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_APPL_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_APPL_RSI_Overbought = 80;
|
||||
input double RS_APPL_RSI_Oversold = 78;
|
||||
input double RS_APPL_RSI_Target_Buy = 94;
|
||||
input double RS_APPL_RSI_Target_Sell = 44;
|
||||
input int RS_APPL_BarsToWait = 7;
|
||||
input double RS_APPL_LotSize = 25;
|
||||
input int RS_APPL_MagicNumber = 20001;
|
||||
input int RS_APPL_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping BTCUSD ==="
|
||||
input string RS_BTCUSD_Symbol = "BTCUSD"; // Pepperstone may use: "BTCUSD", "BTC/USD", or "BTCUSD.c"
|
||||
input ENUM_TIMEFRAMES RS_BTCUSD_TimeFrame = PERIOD_H1;
|
||||
input int RS_BTCUSD_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_BTCUSD_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_BTCUSD_RSI_Overbought = 90;
|
||||
input double RS_BTCUSD_RSI_Oversold = 73;
|
||||
input double RS_BTCUSD_RSI_Target_Buy = 88;
|
||||
input double RS_BTCUSD_RSI_Target_Sell = 48;
|
||||
input int RS_BTCUSD_BarsToWait = 6;
|
||||
input double RS_BTCUSD_LotSize = 0.1;
|
||||
input int RS_BTCUSD_MagicNumber = 123459123;
|
||||
input int RS_BTCUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping NVDA - Pepperstone US ==="
|
||||
input string RS_NVDA_Symbol = "NVDA.US"; // Try: "NVDA.US", "NASDAQ:NVDA", or "NVDA"
|
||||
input ENUM_TIMEFRAMES RS_NVDA_TimeFrame = PERIOD_M15;
|
||||
input int RS_NVDA_RSI_Period = 8;
|
||||
input ENUM_APPLIED_PRICE RS_NVDA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_NVDA_RSI_Overbought = 36;
|
||||
input double RS_NVDA_RSI_Oversold = 38;
|
||||
input double RS_NVDA_RSI_Target_Buy = 90;
|
||||
input double RS_NVDA_RSI_Target_Sell = 70;
|
||||
input int RS_NVDA_BarsToWait = 5;
|
||||
input double RS_NVDA_LotSize = 50;
|
||||
input int RS_NVDA_MagicNumber = 20003;
|
||||
input int RS_NVDA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping TSLA - Pepperstone US ==="
|
||||
input string RS_TSLA_Symbol = "TSLA.US"; // Try: "TSLA.US", "NASDAQ:TSLA", or "TSLA"
|
||||
input ENUM_TIMEFRAMES RS_TSLA_TimeFrame = PERIOD_H1;
|
||||
input int RS_TSLA_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_TSLA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_TSLA_RSI_Overbought = 54;
|
||||
input double RS_TSLA_RSI_Oversold = 73;
|
||||
input double RS_TSLA_RSI_Target_Buy = 87;
|
||||
input double RS_TSLA_RSI_Target_Sell = 33;
|
||||
input int RS_TSLA_BarsToWait = 1;
|
||||
input double RS_TSLA_LotSize = 50;
|
||||
input int RS_TSLA_MagicNumber = 125421321;
|
||||
input int RS_TSLA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping XAUUSD ==="
|
||||
input string RS_XAUUSD_Symbol = "XAUUSD";
|
||||
input ENUM_TIMEFRAMES RS_XAUUSD_TimeFrame = PERIOD_H1;
|
||||
input int RS_XAUUSD_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_XAUUSD_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_XAUUSD_RSI_Overbought = 71;
|
||||
input double RS_XAUUSD_RSI_Oversold = 57;
|
||||
input double RS_XAUUSD_RSI_Target_Buy = 80;
|
||||
input double RS_XAUUSD_RSI_Target_Sell = 57;
|
||||
input int RS_XAUUSD_BarsToWait = 4;
|
||||
input double RS_XAUUSD_LotSize = 0.1;
|
||||
input int RS_XAUUSD_MagicNumber = 129102315;
|
||||
input int RS_XAUUSD_Slippage = 3;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 11-12: RSI Reversal Asian Strategies |
|
||||
//| Each RSI Reversal Asian strategy trades on its own symbol: |
|
||||
//| - EURUSD: Euro/USD |
|
||||
//| - AUDUSD: Australian Dollar/USD |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI Reversal Asian EURUSD ==="
|
||||
input string RRA_EURUSD_Symbol = "EURUSD";
|
||||
input int RRA_EURUSD_RSIPeriod = 28;
|
||||
input double RRA_EURUSD_OverboughtLevel = 60;
|
||||
input double RRA_EURUSD_OversoldLevel = 8;
|
||||
input int RRA_EURUSD_TakeProfitPips = 175;
|
||||
input int RRA_EURUSD_StopLossPips = 5;
|
||||
input double RRA_EURUSD_MaxLotSize = 0.1;
|
||||
input int RRA_EURUSD_MaxSpread = 1000;
|
||||
input int RRA_EURUSD_MaxDuration = 270;
|
||||
input bool RRA_EURUSD_UseStopLoss = false;
|
||||
input bool RRA_EURUSD_UseTakeProfit = false;
|
||||
input bool RRA_EURUSD_UseRSIExit = true;
|
||||
input double RRA_EURUSD_RSIExitLevel = 55;
|
||||
input bool RRA_EURUSD_CloseOutsideSession = false;
|
||||
input ENUM_TIMEFRAMES RRA_EURUSD_TimeFrame = PERIOD_M15;
|
||||
input int RRA_EURUSD_MagicNumber = 30001;
|
||||
input int RRA_EURUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Reversal Asian AUDUSD ==="
|
||||
input string RRA_AUDUSD_Symbol = "AUDUSD";
|
||||
input int RRA_AUDUSD_RSIPeriod = 28;
|
||||
input double RRA_AUDUSD_OverboughtLevel = 68;
|
||||
input double RRA_AUDUSD_OversoldLevel = 30;
|
||||
input int RRA_AUDUSD_TakeProfitPips = 175;
|
||||
input int RRA_AUDUSD_StopLossPips = 5;
|
||||
input double RRA_AUDUSD_MaxLotSize = 0.2;
|
||||
input int RRA_AUDUSD_MaxSpread = 1000;
|
||||
input int RRA_AUDUSD_MaxDuration = 340;
|
||||
input bool RRA_AUDUSD_UseStopLoss = false;
|
||||
input bool RRA_AUDUSD_UseTakeProfit = false;
|
||||
input bool RRA_AUDUSD_UseRSIExit = true;
|
||||
input double RRA_AUDUSD_RSIExitLevel = 48;
|
||||
input bool RRA_AUDUSD_CloseOutsideSession = true;
|
||||
input ENUM_TIMEFRAMES RRA_AUDUSD_TimeFrame = PERIOD_M15;
|
||||
input int RRA_AUDUSD_MagicNumber = 30002;
|
||||
input int RRA_AUDUSD_Slippage = 3;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - DarvasBox |
|
||||
//+------------------------------------------------------------------+
|
||||
struct DarvasBoxData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
double boxHigh;
|
||||
double boxLow;
|
||||
bool boxFormed;
|
||||
datetime lastBoxTime;
|
||||
string boxName;
|
||||
double minStopLevel;
|
||||
double point;
|
||||
CTrade trade;
|
||||
int maHandle;
|
||||
int volumeHandle;
|
||||
datetime lastBarTime;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - EMA Slope Distance |
|
||||
//+------------------------------------------------------------------+
|
||||
struct EMASlopeData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int ema_handle;
|
||||
double ema_array[];
|
||||
datetime letzte_überwachung_zeit;
|
||||
bool überwachung_aktiv;
|
||||
bool preis_trigger_aktiv;
|
||||
bool steigung_trigger_aktiv;
|
||||
int ticket;
|
||||
CTrade trade;
|
||||
int trades_in_current_crossover;
|
||||
bool crossover_detected;
|
||||
datetime trade_open_time;
|
||||
datetime last_bar_time;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI CrossOver Reversal |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSICrossOverData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
int emaHandle;
|
||||
double previousRSIDef;
|
||||
CTrade trade;
|
||||
datetime lastTradeTime;
|
||||
datetime bartime;
|
||||
bool WeekDays[7];
|
||||
datetime lastBarTime;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI MidPoint Hijack |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSIMidPointData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
int rsiReverseHandle;
|
||||
int emaHandle;
|
||||
bool rsiOverbought;
|
||||
bool rsiOversold;
|
||||
bool rsiReverseOverbought;
|
||||
bool rsiReverseOversold;
|
||||
CTrade trade;
|
||||
CPositionInfo positionInfo;
|
||||
bool emaCrossBuySignal;
|
||||
bool emaCrossSellSignal;
|
||||
int emaCrossSignalBar;
|
||||
datetime lastBarTime;
|
||||
datetime rsiReverseLastCloseTime;
|
||||
bool rsiReverseInCooldown;
|
||||
double lastBarRSI;
|
||||
double lastBarRSIReverse;
|
||||
double lastBarEMA;
|
||||
double lastBarClose;
|
||||
double lastBarEMAPrev;
|
||||
double lastBarClosePrev;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Strategy Instances |
|
||||
//+------------------------------------------------------------------+
|
||||
DarvasBoxData dbData;
|
||||
EMASlopeData esData;
|
||||
RSICrossOverData rcData;
|
||||
RSIMidPointData rmData;
|
||||
RSIScalpingData rsAPPLData;
|
||||
RSIScalpingData rsBTCUSDData;
|
||||
RSIScalpingData rsNVDAData;
|
||||
RSIScalpingData rsTSLAData;
|
||||
RSIScalpingData rsXAUUSDData;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI Reversal Asian |
|
||||
//+------------------------------------------------------------------+
|
||||
RSIReversalAsianData rraEURUSDData;
|
||||
RSIReversalAsianData rraAUDUSDData;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Dynamic lot helpers |
|
||||
//+------------------------------------------------------------------+
|
||||
double DynClamp(const double v, const double lo, const double hi)
|
||||
{
|
||||
return MathMax(lo, MathMin(hi, v));
|
||||
}
|
||||
|
||||
// maxMult<=0: no ceiling. minMult<=0: no floor on raw (equity/ref)^exp.
|
||||
double ApplyDynamicMultClamp(const double mult)
|
||||
{
|
||||
double m = mult;
|
||||
if(InpDynamicMinMult > 0.0)
|
||||
m = MathMax(m, InpDynamicMinMult);
|
||||
if(InpDynamicMaxMult > 0.0)
|
||||
m = MathMin(m, InpDynamicMaxMult);
|
||||
return m;
|
||||
}
|
||||
|
||||
double NormalizeVolumeForSymbol(const string symbol, double lots)
|
||||
{
|
||||
double minL = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
||||
double maxL = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
||||
double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
||||
if(step > 0.0)
|
||||
lots = MathFloor(lots / step + 1e-12) * step;
|
||||
if(lots < minL) lots = minL;
|
||||
if(lots > maxL) lots = maxL;
|
||||
return lots;
|
||||
}
|
||||
|
||||
double GetDynamicMultiplier()
|
||||
{
|
||||
if(!InpDynamicLotEnable)
|
||||
return 1.0;
|
||||
double cap = InpDynamicUseEquity ? AccountInfoDouble(ACCOUNT_EQUITY) : AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
if(cap <= 0.0)
|
||||
cap = InpDynamicRefDeposit;
|
||||
double refv = MathMax(InpDynamicRefDeposit, 1.0);
|
||||
double ratio = cap / refv;
|
||||
if(ratio <= 0.0)
|
||||
ratio = 1.0;
|
||||
double mult = MathPow(ratio, InpDynamicExponent);
|
||||
return ApplyDynamicMultClamp(mult);
|
||||
}
|
||||
|
||||
// baseLot = size at reference deposit; optionalCap 0 = no extra ceiling (broker min/max still apply)
|
||||
double DynamicLotForSymbol(const string symbol, const double baseLot, const double optionalCap = 0.0)
|
||||
{
|
||||
double mult = GetDynamicMultiplier();
|
||||
g_DynMultLast = mult;
|
||||
double v = baseLot * mult;
|
||||
if(optionalCap > 0.0 && v > optionalCap)
|
||||
v = optionalCap;
|
||||
return NormalizeVolumeForSymbol(symbol, v);
|
||||
}
|
||||
|
||||
void RefreshDynamicStrategyLots()
|
||||
{
|
||||
if(!InpDynamicLotEnable)
|
||||
{
|
||||
g_ES_LotSize = NormalizeVolumeForSymbol(ES_Symbol, ES_LotGröße);
|
||||
g_RC_LotSize = NormalizeVolumeForSymbol(RC_Symbol, RC_lotSize);
|
||||
g_RM_LotSize = NormalizeVolumeForSymbol(RM_Symbol, RM_InpLotSize);
|
||||
g_DB_LotSize = NormalizeVolumeForSymbol(DB_Symbol, DB_BaseLotSize);
|
||||
g_DynMultLast = 1.0;
|
||||
return;
|
||||
}
|
||||
g_ES_LotSize = DynamicLotForSymbol(ES_Symbol, ES_LotGröße);
|
||||
g_RC_LotSize = DynamicLotForSymbol(RC_Symbol, RC_lotSize);
|
||||
g_RM_LotSize = DynamicLotForSymbol(RM_Symbol, RM_InpLotSize);
|
||||
g_DB_LotSize = DynamicLotForSymbol(DB_Symbol, DB_BaseLotSize);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
int initResult = INIT_SUCCEEDED;
|
||||
|
||||
RefreshDynamicStrategyLots();
|
||||
|
||||
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
|
||||
if(EnableDarvasBox)
|
||||
if(!InitDarvasBox(DB_Symbol))
|
||||
Print("Warning: DarvasBox strategy failed to initialize for symbol '", DB_Symbol, "'");
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
if(!InitEMASlopeDistance(ES_Symbol))
|
||||
Print("Warning: EMASlopeDistance strategy failed to initialize for symbol '", ES_Symbol, "'");
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
if(!InitRSICrossOverReversal(RC_Symbol))
|
||||
Print("Warning: RSICrossOverReversal strategy failed to initialize for symbol '", RC_Symbol, "'");
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
if(!InitRSIMidPointHijack(RM_Symbol))
|
||||
Print("Warning: RSIMidPointHijack strategy failed to initialize for symbol '", RM_Symbol, "'");
|
||||
|
||||
// Initialize RSI Scalping strategies - don't fail entire EA if symbol unavailable
|
||||
if(EnableRSIScalpingAPPL)
|
||||
InitRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price, RS_APPL_MagicNumber, RS_APPL_Slippage);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
InitRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price, RS_BTCUSD_MagicNumber, RS_BTCUSD_Slippage);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
InitRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price, RS_NVDA_MagicNumber, RS_NVDA_Slippage);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
InitRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price, RS_TSLA_MagicNumber, RS_TSLA_Slippage);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
InitRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price, RS_XAUUSD_MagicNumber, RS_XAUUSD_Slippage);
|
||||
|
||||
// Initialize RSI Reversal Asian strategies
|
||||
if(EnableRSIReversalAsianEURUSD)
|
||||
if(!InitRSIReversalAsian(rraEURUSDData, RRA_EURUSD_Symbol, RRA_EURUSD_RSIPeriod, RRA_EURUSD_OverboughtLevel, RRA_EURUSD_OversoldLevel,
|
||||
RRA_EURUSD_TakeProfitPips, RRA_EURUSD_StopLossPips, RRA_EURUSD_MaxLotSize,
|
||||
RRA_EURUSD_MaxSpread, RRA_EURUSD_MaxDuration, RRA_EURUSD_UseStopLoss,
|
||||
RRA_EURUSD_UseTakeProfit, RRA_EURUSD_UseRSIExit, RRA_EURUSD_RSIExitLevel,
|
||||
RRA_EURUSD_CloseOutsideSession, RRA_EURUSD_TimeFrame, RRA_EURUSD_MagicNumber, RRA_EURUSD_Slippage))
|
||||
Print("Warning: RSIReversalAsianEURUSD strategy failed to initialize for symbol '", RRA_EURUSD_Symbol, "'");
|
||||
|
||||
if(EnableRSIReversalAsianAUDUSD)
|
||||
if(!InitRSIReversalAsian(rraAUDUSDData, RRA_AUDUSD_Symbol, RRA_AUDUSD_RSIPeriod, RRA_AUDUSD_OverboughtLevel, RRA_AUDUSD_OversoldLevel,
|
||||
RRA_AUDUSD_TakeProfitPips, RRA_AUDUSD_StopLossPips, RRA_AUDUSD_MaxLotSize,
|
||||
RRA_AUDUSD_MaxSpread, RRA_AUDUSD_MaxDuration, RRA_AUDUSD_UseStopLoss,
|
||||
RRA_AUDUSD_UseTakeProfit, RRA_AUDUSD_UseRSIExit, RRA_AUDUSD_RSIExitLevel,
|
||||
RRA_AUDUSD_CloseOutsideSession, RRA_AUDUSD_TimeFrame, RRA_AUDUSD_MagicNumber, RRA_AUDUSD_Slippage))
|
||||
Print("Warning: RSIReversalAsianAUDUSD strategy failed to initialize for symbol '", RRA_AUDUSD_Symbol, "'");
|
||||
|
||||
string acctCur = AccountInfoString(ACCOUNT_CURRENCY);
|
||||
double eq0 = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
double refvInit = MathMax(InpDynamicRefDeposit, 1.0);
|
||||
double capInit = InpDynamicUseEquity ? eq0 : AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
if(capInit <= 0.0)
|
||||
capInit = refvInit;
|
||||
double ratioInit = capInit / refvInit;
|
||||
double rawPowInit = MathPow(ratioInit, InpDynamicExponent);
|
||||
Print("United EA v1.07 ", acctCur, " equity=", DoubleToString(eq0, 2), " equity/ref=", DoubleToString(ratioInit, 6),
|
||||
" raw^exp=", DoubleToString(rawPowInit, 6), " multOut=", DoubleToString(g_DynMultLast, 6),
|
||||
" minM=", InpDynamicMinMult, " maxM=", InpDynamicMaxMult, " ref=", InpDynamicRefDeposit, " exp=", InpDynamicExponent,
|
||||
" lots ES=", g_ES_LotSize, " RC=", g_RC_LotSize, " RM=", g_RM_LotSize, " DB=", g_DB_LotSize);
|
||||
Print("United EA initialized. Active strategies: ",
|
||||
(EnableDarvasBox ? "DarvasBox " : ""),
|
||||
(EnableEMASlopeDistance ? "EMASlope " : ""),
|
||||
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
|
||||
(EnableRSIMidPointHijack ? "RSIMidPoint " : ""),
|
||||
(EnableRSIScalpingAPPL ? "RSIScalpingAPPL " : ""),
|
||||
(EnableRSIScalpingBTCUSD ? "RSIScalpingBTCUSD " : ""),
|
||||
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
|
||||
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
|
||||
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""),
|
||||
(EnableRSIReversalAsianEURUSD ? "RSIReversalAsianEURUSD " : ""),
|
||||
(EnableRSIReversalAsianAUDUSD ? "RSIReversalAsianAUDUSD " : ""));
|
||||
|
||||
return initResult;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(EnableDarvasBox)
|
||||
DeinitDarvasBox();
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
DeinitEMASlopeDistance();
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
DeinitRSICrossOverReversal();
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
DeinitRSIMidPointHijack();
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
DeinitRSIScalping(rsAPPLData);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
DeinitRSIScalping(rsBTCUSDData);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
DeinitRSIScalping(rsNVDAData);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
DeinitRSIScalping(rsTSLAData);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
DeinitRSIScalping(rsXAUUSDData);
|
||||
|
||||
if(EnableRSIReversalAsianEURUSD)
|
||||
DeinitRSIReversalAsian(rraEURUSDData);
|
||||
|
||||
if(EnableRSIReversalAsianAUDUSD)
|
||||
DeinitRSIReversalAsian(rraAUDUSDData);
|
||||
|
||||
Print("United EA deinitialized. Reason: ", reason);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
RefreshDynamicStrategyLots();
|
||||
|
||||
if(EnableDarvasBox)
|
||||
ProcessDarvasBox(DB_Symbol);
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
ProcessEMASlopeDistance(ES_Symbol);
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
ProcessRSICrossOverReversal(RC_Symbol);
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
ProcessRSIMidPointHijack(RM_Symbol);
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
ProcessRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price,
|
||||
RS_APPL_RSI_Overbought, RS_APPL_RSI_Oversold, RS_APPL_RSI_Target_Buy, RS_APPL_RSI_Target_Sell,
|
||||
RS_APPL_BarsToWait,
|
||||
DynamicLotForSymbol(RS_APPL_Symbol, RS_APPL_LotSize, InpDynamicStockLotCap),
|
||||
RS_APPL_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
ProcessRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price,
|
||||
RS_BTCUSD_RSI_Overbought, RS_BTCUSD_RSI_Oversold, RS_BTCUSD_RSI_Target_Buy, RS_BTCUSD_RSI_Target_Sell,
|
||||
RS_BTCUSD_BarsToWait, DynamicLotForSymbol(RS_BTCUSD_Symbol, RS_BTCUSD_LotSize), RS_BTCUSD_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
ProcessRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price,
|
||||
RS_NVDA_RSI_Overbought, RS_NVDA_RSI_Oversold, RS_NVDA_RSI_Target_Buy, RS_NVDA_RSI_Target_Sell,
|
||||
RS_NVDA_BarsToWait,
|
||||
DynamicLotForSymbol(RS_NVDA_Symbol, RS_NVDA_LotSize, InpDynamicStockLotCap),
|
||||
RS_NVDA_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
ProcessRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price,
|
||||
RS_TSLA_RSI_Overbought, RS_TSLA_RSI_Oversold, RS_TSLA_RSI_Target_Buy, RS_TSLA_RSI_Target_Sell,
|
||||
RS_TSLA_BarsToWait,
|
||||
DynamicLotForSymbol(RS_TSLA_Symbol, RS_TSLA_LotSize, InpDynamicStockLotCap),
|
||||
RS_TSLA_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
ProcessRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price,
|
||||
RS_XAUUSD_RSI_Overbought, RS_XAUUSD_RSI_Oversold, RS_XAUUSD_RSI_Target_Buy, RS_XAUUSD_RSI_Target_Sell,
|
||||
RS_XAUUSD_BarsToWait, DynamicLotForSymbol(RS_XAUUSD_Symbol, RS_XAUUSD_LotSize), RS_XAUUSD_MagicNumber);
|
||||
|
||||
if(EnableRSIReversalAsianEURUSD)
|
||||
ProcessRSIReversalAsian(rraEURUSDData, DynamicLotForSymbol(RRA_EURUSD_Symbol, RRA_EURUSD_MaxLotSize));
|
||||
|
||||
if(EnableRSIReversalAsianAUDUSD)
|
||||
ProcessRSIReversalAsian(rraAUDUSDData, DynamicLotForSymbol(RRA_AUDUSD_Symbol, RRA_AUDUSD_MaxLotSize));
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
@@ -0,0 +1,641 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| UnitedEA.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include <Trade\PositionInfo.mqh>
|
||||
#include <Indicators\Trend.mqh>
|
||||
#include <Indicators\Volumes.mqh>
|
||||
#include "MagicNumberHelpers.mqh"
|
||||
#include "PerformanceEvaluator.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy Enable/Disable Switches |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== Strategy Enable/Disable ==="
|
||||
input bool EnableDarvasBox = true;
|
||||
input bool EnableEMASlopeDistance = true;
|
||||
input bool EnableRSICrossOverReversal = true;
|
||||
input bool EnableRSIMidPointHijack = true;
|
||||
input bool EnableRSIScalpingAPPL = true;
|
||||
input bool EnableRSIScalpingBTCUSD = true;
|
||||
input bool EnableRSIScalpingNVDA = true;
|
||||
input bool EnableRSIScalpingTSLA = true;
|
||||
input bool EnableRSIScalpingXAUUSD = true;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 1: DarvasBoxXAUUSD |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== DarvasBox Strategy ==="
|
||||
input string DB_Symbol = "XAUUSD";
|
||||
input int DB_BoxPeriod = 165;
|
||||
input double DB_BoxDeviation = 30000; // Increased to allow larger ranges (was 25140)
|
||||
input int DB_VolumeThreshold = 0; // Set to 0 to disable volume threshold check. Volume data from indicator used instead.
|
||||
input double DB_StopLoss = 1665;
|
||||
input double DB_TakeProfit = 3685;
|
||||
input bool DB_EnableLogging = false;
|
||||
input color DB_BoxColor = clrBlue;
|
||||
input int DB_BoxWidth = 1;
|
||||
input ENUM_TIMEFRAMES DB_TrendTimeframe = PERIOD_H2;
|
||||
input int DB_MA_Period = 125;
|
||||
input ENUM_MA_METHOD DB_MA_Method = MODE_EMA;
|
||||
input ENUM_APPLIED_PRICE DB_MA_Price = PRICE_WEIGHTED;
|
||||
input double DB_TrendThreshold = 4.94;
|
||||
input int DB_VolumeMA_Period = 110;
|
||||
input double DB_VolumeThresholdMultiplier = 1.5;
|
||||
input int DB_MagicNumber = 135790;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 2: EMASlopeDistanceCocktailXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== EMA Slope Distance Strategy ==="
|
||||
input string ES_Symbol = "XAUUSD";
|
||||
input int ES_EMA_Periode = 46;
|
||||
input double ES_PreisSchwelle = 600.0;
|
||||
input double ES_SteigungSchwelle = 80.0;
|
||||
input int ES_ÜberwachungTimeout = 800;
|
||||
input double ES_TrailingStop = 250.0;
|
||||
input double ES_LotGröße = 0.03;
|
||||
input int ES_MagicNumber = 12350;
|
||||
input bool ES_UseSpreadAdjustment = true;
|
||||
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
|
||||
input bool ES_UseBarData = true;
|
||||
input int ES_MaxTradesPerCrossover = 9;
|
||||
input int ES_ProfitCheckBars = 18;
|
||||
input bool ES_CloseUnprofitableTrades = true;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 3: RSICrossOverReversalXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI CrossOver Reversal Strategy ==="
|
||||
input string RC_Symbol = "XAUUSD";
|
||||
input int RC_MagicNumber = 7;
|
||||
input int RC_rsiPeriod = 19;
|
||||
input int RC_overboughtLevel = 93;
|
||||
input int RC_oversoldLevel = 22;
|
||||
input double RC_entryRSIBuySpread = 0;
|
||||
input double RC_entryRSISellSpread = 0;
|
||||
input double RC_lotSize = 0.01;
|
||||
input int RC_slippage = 3;
|
||||
input int RC_cooldownSeconds = 209;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame2 = PERIOD_M1;
|
||||
input ENUM_TIMEFRAMES RC_BarTimeFrame = PERIOD_M12;
|
||||
input int RC_emaPeriod = 140;
|
||||
input double RC_emaSlopeThreshold = 105;
|
||||
input double RC_exitBuyRSI = 86;
|
||||
input double RC_exitSellRSI = 10;
|
||||
input double RC_TrailingStop = 295;
|
||||
input double RC_emaDistanceThreshold = 165;
|
||||
input int RC_tradingHourOneBegin = 24;
|
||||
input int RC_tradingHourOneEnd = 22;
|
||||
input int RC_tradingHourTwoBegin = 6;
|
||||
input int RC_tradingHourTwoEnd = 19;
|
||||
input bool RC_Sunday = false;
|
||||
input bool RC_Monday = false;
|
||||
input bool RC_Tuesday = true;
|
||||
input bool RC_Wednesday = true;
|
||||
input bool RC_Thursday = true;
|
||||
input bool RC_Friday = false;
|
||||
input bool RC_Saturday = false;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 4: RSIMidPointHijackXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI MidPoint Hijack Strategy ==="
|
||||
input string RM_Symbol = "XAUUSD";
|
||||
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
|
||||
input double RM_InpLotSize = 0.02;
|
||||
input int RM_InpMagicNumberRSIFollow = 1001;
|
||||
input int RM_InpMagicNumberRSIReverse = 1002;
|
||||
input int RM_InpMagicNumberEMACross = 1003;
|
||||
input bool RM_InpEnableRSIFollow = true;
|
||||
input bool RM_InpEnableRSIReverse = true;
|
||||
input bool RM_InpEnableEMACross = true;
|
||||
input bool RM_InpEnableStrategyLock = false;
|
||||
input double RM_InpLockProfitThreshold = 0.0;
|
||||
input bool RM_InpCloseOppositeTrades = false;
|
||||
input int RM_InpRSIPeriod = 32;
|
||||
input int RM_InpRSIOverbought = 78;
|
||||
input int RM_InpRSIOversold = 46;
|
||||
input int RM_InpRSIExitLevel = 44;
|
||||
input int RM_InpRSIFollowStartHour = 23;
|
||||
input int RM_InpRSIFollowEndHour = 8;
|
||||
input bool RM_InpRSIFollowCloseOutsideHours = false;
|
||||
input int RM_InpRSIReversePeriod = 59;
|
||||
input int RM_InpRSIReverseOverbought = 51;
|
||||
input int RM_InpRSIReverseOversold = 49;
|
||||
input int RM_InpRSIReverseCrossLevel = 53;
|
||||
input int RM_InpRSIReverseExitLevel = 48;
|
||||
input int RM_InpRSIReverseStartHour = 7;
|
||||
input int RM_InpRSIReverseEndHour = 13;
|
||||
input bool RM_InpRSIReverseCloseOutsideHours = false;
|
||||
input int RM_InpRSIReverseCooldownBars = 15;
|
||||
input bool RM_InpRSIReverseCooldownOnLoss = true;
|
||||
input int RM_InpEMAPeriod = 120;
|
||||
input int RM_InpEMACrossStartHour = 8;
|
||||
input int RM_InpEMACrossEndHour = 14;
|
||||
input bool RM_InpEMACrossCloseOutsideHours = true;
|
||||
input bool RM_InpUseEMADistanceEntry = true;
|
||||
input double RM_InpEMADistancePips = 160.0;
|
||||
input int RM_InpEMADistancePeriod = 26;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 5-10: RSI Scalping Strategies |
|
||||
//| Each RSI Scalping strategy trades on its own symbol: |
|
||||
//| - APPL: Apple stock (AAPL) |
|
||||
//| - BTCUSD: Bitcoin/USD |
|
||||
//| - NVDA: NVIDIA stock |
|
||||
//| - TSLA: Tesla stock |
|
||||
//| - XAUUSD: Gold/USD |
|
||||
//| |
|
||||
//| PEPPERSTONE US SYMBOL FORMATS: |
|
||||
//| - Stocks may use: "AAPL.US", "NASDAQ:AAPL", or just "AAPL" |
|
||||
//| - To find correct symbols: |
|
||||
//| 1. Open Market Watch (Ctrl+M) |
|
||||
//| 2. Right-click > Show All |
|
||||
//| 3. Search for the stock name |
|
||||
//| 4. Use the exact symbol name shown |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI Scalping APPL (AAPL) - Pepperstone US ==="
|
||||
input string RS_APPL_Symbol = "AAPL.US"; // Try: "AAPL.US", "NASDAQ:AAPL", or "AAPL"
|
||||
input ENUM_TIMEFRAMES RS_APPL_TimeFrame = PERIOD_M10;
|
||||
input int RS_APPL_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_APPL_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_APPL_RSI_Overbought = 80;
|
||||
input double RS_APPL_RSI_Oversold = 78;
|
||||
input double RS_APPL_RSI_Target_Buy = 94;
|
||||
input double RS_APPL_RSI_Target_Sell = 44;
|
||||
input int RS_APPL_BarsToWait = 7;
|
||||
input double RS_APPL_LotSize = 25;
|
||||
input int RS_APPL_MagicNumber = 20001;
|
||||
input int RS_APPL_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping BTCUSD ==="
|
||||
input string RS_BTCUSD_Symbol = "BTCUSD"; // Pepperstone may use: "BTCUSD", "BTC/USD", or "BTCUSD.c"
|
||||
input ENUM_TIMEFRAMES RS_BTCUSD_TimeFrame = PERIOD_H1;
|
||||
input int RS_BTCUSD_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_BTCUSD_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_BTCUSD_RSI_Overbought = 90;
|
||||
input double RS_BTCUSD_RSI_Oversold = 73;
|
||||
input double RS_BTCUSD_RSI_Target_Buy = 88;
|
||||
input double RS_BTCUSD_RSI_Target_Sell = 48;
|
||||
input int RS_BTCUSD_BarsToWait = 6;
|
||||
input double RS_BTCUSD_LotSize = 0.1;
|
||||
input int RS_BTCUSD_MagicNumber = 123459123;
|
||||
input int RS_BTCUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping NVDA - Pepperstone US ==="
|
||||
input string RS_NVDA_Symbol = "NVDA.US"; // Try: "NVDA.US", "NASDAQ:NVDA", or "NVDA"
|
||||
input ENUM_TIMEFRAMES RS_NVDA_TimeFrame = PERIOD_M15;
|
||||
input int RS_NVDA_RSI_Period = 8;
|
||||
input ENUM_APPLIED_PRICE RS_NVDA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_NVDA_RSI_Overbought = 36;
|
||||
input double RS_NVDA_RSI_Oversold = 38;
|
||||
input double RS_NVDA_RSI_Target_Buy = 90;
|
||||
input double RS_NVDA_RSI_Target_Sell = 70;
|
||||
input int RS_NVDA_BarsToWait = 5;
|
||||
input double RS_NVDA_LotSize = 50;
|
||||
input int RS_NVDA_MagicNumber = 20003;
|
||||
input int RS_NVDA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping TSLA - Pepperstone US ==="
|
||||
input string RS_TSLA_Symbol = "TSLA.US"; // Try: "TSLA.US", "NASDAQ:TSLA", or "TSLA"
|
||||
input ENUM_TIMEFRAMES RS_TSLA_TimeFrame = PERIOD_H1;
|
||||
input int RS_TSLA_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_TSLA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_TSLA_RSI_Overbought = 54;
|
||||
input double RS_TSLA_RSI_Oversold = 73;
|
||||
input double RS_TSLA_RSI_Target_Buy = 87;
|
||||
input double RS_TSLA_RSI_Target_Sell = 33;
|
||||
input int RS_TSLA_BarsToWait = 1;
|
||||
input double RS_TSLA_LotSize = 50;
|
||||
input int RS_TSLA_MagicNumber = 125421321;
|
||||
input int RS_TSLA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping XAUUSD ==="
|
||||
input string RS_XAUUSD_Symbol = "XAUUSD";
|
||||
input ENUM_TIMEFRAMES RS_XAUUSD_TimeFrame = PERIOD_H1;
|
||||
input int RS_XAUUSD_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_XAUUSD_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_XAUUSD_RSI_Overbought = 71;
|
||||
input double RS_XAUUSD_RSI_Oversold = 57;
|
||||
input double RS_XAUUSD_RSI_Target_Buy = 80;
|
||||
input double RS_XAUUSD_RSI_Target_Sell = 57;
|
||||
input int RS_XAUUSD_BarsToWait = 4;
|
||||
input double RS_XAUUSD_LotSize = 0.1;
|
||||
input int RS_XAUUSD_MagicNumber = 129102315;
|
||||
input int RS_XAUUSD_Slippage = 3;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - DarvasBox |
|
||||
//+------------------------------------------------------------------+
|
||||
struct DarvasBoxData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
double boxHigh;
|
||||
double boxLow;
|
||||
bool boxFormed;
|
||||
datetime lastBoxTime;
|
||||
string boxName;
|
||||
double minStopLevel;
|
||||
double point;
|
||||
CTrade trade;
|
||||
int maHandle;
|
||||
int volumeHandle;
|
||||
datetime lastBarTime;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - EMA Slope Distance |
|
||||
//+------------------------------------------------------------------+
|
||||
struct EMASlopeData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int ema_handle;
|
||||
double ema_array[];
|
||||
datetime letzte_überwachung_zeit;
|
||||
bool überwachung_aktiv;
|
||||
bool preis_trigger_aktiv;
|
||||
bool steigung_trigger_aktiv;
|
||||
int ticket;
|
||||
CTrade trade;
|
||||
int trades_in_current_crossover;
|
||||
bool crossover_detected;
|
||||
datetime trade_open_time;
|
||||
datetime last_bar_time;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI CrossOver Reversal |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSICrossOverData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
int emaHandle;
|
||||
double previousRSIDef;
|
||||
CTrade trade;
|
||||
datetime lastTradeTime;
|
||||
datetime bartime;
|
||||
bool WeekDays[7];
|
||||
datetime lastBarTime;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI MidPoint Hijack |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSIMidPointData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
int rsiReverseHandle;
|
||||
int emaHandle;
|
||||
bool rsiOverbought;
|
||||
bool rsiOversold;
|
||||
bool rsiReverseOverbought;
|
||||
bool rsiReverseOversold;
|
||||
CTrade trade;
|
||||
CPositionInfo positionInfo;
|
||||
bool emaCrossBuySignal;
|
||||
bool emaCrossSellSignal;
|
||||
int emaCrossSignalBar;
|
||||
datetime lastBarTime;
|
||||
datetime rsiReverseLastCloseTime;
|
||||
bool rsiReverseInCooldown;
|
||||
double lastBarRSI;
|
||||
double lastBarRSIReverse;
|
||||
double lastBarEMA;
|
||||
double lastBarClose;
|
||||
double lastBarEMAPrev;
|
||||
double lastBarClosePrev;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI Scalping |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSIScalpingData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
CTrade trade;
|
||||
int rsi_handle;
|
||||
double rsi_buffer[];
|
||||
double rsi_prev;
|
||||
double rsi_current;
|
||||
double rsi_two_bars_ago;
|
||||
bool position_open;
|
||||
ulong position_ticket;
|
||||
ENUM_POSITION_TYPE current_position_type;
|
||||
datetime last_bar_time;
|
||||
bool rsi_against_position;
|
||||
int bars_against_count;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Strategy Instances |
|
||||
//+------------------------------------------------------------------+
|
||||
DarvasBoxData dbData;
|
||||
EMASlopeData esData;
|
||||
RSICrossOverData rcData;
|
||||
RSIMidPointData rmData;
|
||||
RSIScalpingData rsAPPLData;
|
||||
RSIScalpingData rsBTCUSDData;
|
||||
RSIScalpingData rsNVDAData;
|
||||
RSIScalpingData rsTSLAData;
|
||||
RSIScalpingData rsXAUUSDData;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables for Dynamic Lot Sizes |
|
||||
//+------------------------------------------------------------------+
|
||||
// All strategies start with minimum lot size for safety (will be adjusted by performance evaluator)
|
||||
double g_DB_LotSize = 0.01; // DarvasBox uses fixed lot size
|
||||
double g_ES_LotSize = 0.01; // EMA Slope Distance - start with minimum
|
||||
double g_RC_LotSize = 0.01; // RSI CrossOver Reversal - start with minimum
|
||||
double g_RM_LotSize = 0.01; // RSI MidPoint Hijack - start with minimum
|
||||
double g_RS_APPL_LotSize = 5.0; // Stock - start with stock minimum (5.0)
|
||||
double g_RS_BTCUSD_LotSize = 0.01; // Crypto - start with forex minimum (0.01)
|
||||
double g_RS_NVDA_LotSize = 5.0; // Stock - start with stock minimum (5.0)
|
||||
double g_RS_TSLA_LotSize = 5.0; // Stock - start with stock minimum (5.0)
|
||||
double g_RS_XAUUSD_LotSize = 0.01; // Forex - start with forex minimum (0.01)
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
int initResult = INIT_SUCCEEDED;
|
||||
|
||||
// Initialize Performance Evaluator
|
||||
InitPerformanceTracking();
|
||||
|
||||
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
|
||||
if(EnableDarvasBox)
|
||||
{
|
||||
if(!InitDarvasBox(DB_Symbol))
|
||||
Print("Warning: DarvasBox strategy failed to initialize for symbol '", DB_Symbol, "'");
|
||||
else
|
||||
RegisterStrategy("DarvasBox", DB_MagicNumber, 0.01, DB_Symbol); // Fixed lot size
|
||||
}
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
{
|
||||
if(!InitEMASlopeDistance(ES_Symbol))
|
||||
Print("Warning: EMASlopeDistance strategy failed to initialize for symbol '", ES_Symbol, "'");
|
||||
else
|
||||
{
|
||||
RegisterStrategy("EMASlopeDistance", ES_MagicNumber, ES_LotGröße, ES_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(ES_Symbol);
|
||||
g_ES_LotSize = minLot;
|
||||
}
|
||||
}
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
{
|
||||
if(!InitRSICrossOverReversal(RC_Symbol))
|
||||
Print("Warning: RSICrossOverReversal strategy failed to initialize for symbol '", RC_Symbol, "'");
|
||||
else
|
||||
{
|
||||
RegisterStrategy("RSICrossOverReversal", RC_MagicNumber, RC_lotSize, RC_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RC_Symbol);
|
||||
g_RC_LotSize = minLot;
|
||||
}
|
||||
}
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
{
|
||||
if(!InitRSIMidPointHijack(RM_Symbol))
|
||||
Print("Warning: RSIMidPointHijack strategy failed to initialize for symbol '", RM_Symbol, "'");
|
||||
else
|
||||
{
|
||||
RegisterStrategy("RSIMidPointHijack", RM_InpMagicNumberRSIFollow, RM_InpLotSize, RM_Symbol);
|
||||
RegisterStrategy("RSIMidPointHijack_Reverse", RM_InpMagicNumberRSIReverse, RM_InpLotSize, RM_Symbol);
|
||||
RegisterStrategy("RSIMidPointHijack_EMACross", RM_InpMagicNumberEMACross, RM_InpLotSize, RM_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RM_Symbol);
|
||||
g_RM_LotSize = minLot;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize RSI Scalping strategies - don't fail entire EA if symbol unavailable
|
||||
if(EnableRSIScalpingAPPL)
|
||||
{
|
||||
InitRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price, RS_APPL_MagicNumber, RS_APPL_Slippage);
|
||||
RegisterStrategy("RSIScalpingAPPL", RS_APPL_MagicNumber, RS_APPL_LotSize, RS_APPL_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RS_APPL_Symbol);
|
||||
g_RS_APPL_LotSize = minLot;
|
||||
}
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
{
|
||||
InitRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price, RS_BTCUSD_MagicNumber, RS_BTCUSD_Slippage);
|
||||
RegisterStrategy("RSIScalpingBTCUSD", RS_BTCUSD_MagicNumber, RS_BTCUSD_LotSize, RS_BTCUSD_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RS_BTCUSD_Symbol);
|
||||
g_RS_BTCUSD_LotSize = minLot;
|
||||
}
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
{
|
||||
InitRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price, RS_NVDA_MagicNumber, RS_NVDA_Slippage);
|
||||
RegisterStrategy("RSIScalpingNVDA", RS_NVDA_MagicNumber, RS_NVDA_LotSize, RS_NVDA_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RS_NVDA_Symbol);
|
||||
g_RS_NVDA_LotSize = minLot;
|
||||
}
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
{
|
||||
InitRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price, RS_TSLA_MagicNumber, RS_TSLA_Slippage);
|
||||
RegisterStrategy("RSIScalpingTSLA", RS_TSLA_MagicNumber, RS_TSLA_LotSize, RS_TSLA_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RS_TSLA_Symbol);
|
||||
g_RS_TSLA_LotSize = minLot;
|
||||
}
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
{
|
||||
InitRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price, RS_XAUUSD_MagicNumber, RS_XAUUSD_Slippage);
|
||||
RegisterStrategy("RSIScalpingXAUUSD", RS_XAUUSD_MagicNumber, RS_XAUUSD_LotSize, RS_XAUUSD_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RS_XAUUSD_Symbol);
|
||||
g_RS_XAUUSD_LotSize = minLot;
|
||||
}
|
||||
|
||||
// Load adjusted lot sizes from performance evaluator
|
||||
if(PE_EnableAutoAdjustment)
|
||||
{
|
||||
double adjustedLot;
|
||||
adjustedLot = GetStrategyLotSize("EMASlopeDistance", ES_MagicNumber);
|
||||
if(adjustedLot > 0) g_ES_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSICrossOverReversal", RC_MagicNumber);
|
||||
if(adjustedLot > 0) g_RC_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIMidPointHijack", RM_InpMagicNumberRSIFollow);
|
||||
if(adjustedLot > 0) g_RM_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingAPPL", RS_APPL_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_APPL_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingBTCUSD", RS_BTCUSD_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_BTCUSD_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingNVDA", RS_NVDA_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_NVDA_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingTSLA", RS_TSLA_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_TSLA_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingXAUUSD", RS_XAUUSD_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_XAUUSD_LotSize = adjustedLot;
|
||||
}
|
||||
|
||||
Print("United EA initialized. Active strategies: ",
|
||||
(EnableDarvasBox ? "DarvasBox " : ""),
|
||||
(EnableEMASlopeDistance ? "EMASlope " : ""),
|
||||
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
|
||||
(EnableRSIMidPointHijack ? "RSIMidPoint " : ""),
|
||||
(EnableRSIScalpingAPPL ? "RSIScalpingAPPL " : ""),
|
||||
(EnableRSIScalpingBTCUSD ? "RSIScalpingBTCUSD " : ""),
|
||||
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
|
||||
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
|
||||
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""));
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print(GetPerformanceSummary());
|
||||
|
||||
return initResult;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(EnableDarvasBox)
|
||||
DeinitDarvasBox();
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
DeinitEMASlopeDistance();
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
DeinitRSICrossOverReversal();
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
DeinitRSIMidPointHijack();
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
DeinitRSIScalping(rsAPPLData);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
DeinitRSIScalping(rsBTCUSDData);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
DeinitRSIScalping(rsNVDAData);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
DeinitRSIScalping(rsTSLAData);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
DeinitRSIScalping(rsXAUUSDData);
|
||||
|
||||
Print("United EA deinitialized. Reason: ", reason);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Process performance evaluation (checks for quarter end and adjusts lot sizes)
|
||||
ProcessPerformanceEvaluation();
|
||||
|
||||
// Update lot sizes from performance evaluator if auto-adjustment is enabled
|
||||
if(PE_EnableAutoAdjustment)
|
||||
{
|
||||
double adjustedLot;
|
||||
adjustedLot = GetStrategyLotSize("EMASlopeDistance", ES_MagicNumber);
|
||||
if(adjustedLot > 0) g_ES_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSICrossOverReversal", RC_MagicNumber);
|
||||
if(adjustedLot > 0) g_RC_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIMidPointHijack", RM_InpMagicNumberRSIFollow);
|
||||
if(adjustedLot > 0) g_RM_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingAPPL", RS_APPL_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_APPL_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingBTCUSD", RS_BTCUSD_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_BTCUSD_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingNVDA", RS_NVDA_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_NVDA_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingTSLA", RS_TSLA_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_TSLA_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingXAUUSD", RS_XAUUSD_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_XAUUSD_LotSize = adjustedLot;
|
||||
}
|
||||
|
||||
if(EnableDarvasBox)
|
||||
ProcessDarvasBox(DB_Symbol);
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
ProcessEMASlopeDistance(ES_Symbol);
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
ProcessRSICrossOverReversal(RC_Symbol);
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
ProcessRSIMidPointHijack(RM_Symbol);
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
ProcessRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price,
|
||||
RS_APPL_RSI_Overbought, RS_APPL_RSI_Oversold, RS_APPL_RSI_Target_Buy, RS_APPL_RSI_Target_Sell,
|
||||
RS_APPL_BarsToWait, g_RS_APPL_LotSize, RS_APPL_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
ProcessRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price,
|
||||
RS_BTCUSD_RSI_Overbought, RS_BTCUSD_RSI_Oversold, RS_BTCUSD_RSI_Target_Buy, RS_BTCUSD_RSI_Target_Sell,
|
||||
RS_BTCUSD_BarsToWait, g_RS_BTCUSD_LotSize, RS_BTCUSD_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
ProcessRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price,
|
||||
RS_NVDA_RSI_Overbought, RS_NVDA_RSI_Oversold, RS_NVDA_RSI_Target_Buy, RS_NVDA_RSI_Target_Sell,
|
||||
RS_NVDA_BarsToWait, g_RS_NVDA_LotSize, RS_NVDA_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
ProcessRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price,
|
||||
RS_TSLA_RSI_Overbought, RS_TSLA_RSI_Oversold, RS_TSLA_RSI_Target_Buy, RS_TSLA_RSI_Target_Sell,
|
||||
RS_TSLA_BarsToWait, g_RS_TSLA_LotSize, RS_TSLA_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
ProcessRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price,
|
||||
RS_XAUUSD_RSI_Overbought, RS_XAUUSD_RSI_Oversold, RS_XAUUSD_RSI_Target_Buy, RS_XAUUSD_RSI_Target_Sell,
|
||||
RS_XAUUSD_BarsToWait, g_RS_XAUUSD_LotSize, RS_XAUUSD_MagicNumber);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Include strategy implementations |
|
||||
//+------------------------------------------------------------------+
|
||||
#include "Strategies/DarvasBoxStrategy.mqh"
|
||||
#include "Strategies/EMASlopeDistanceStrategy.mqh"
|
||||
#include "Strategies/RSICrossOverReversalStrategy.mqh"
|
||||
#include "Strategies/RSIMidPointHijackStrategy.mqh"
|
||||
#include "Strategies/RSIScalpingStrategy.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,159 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MagicNumberHelpers.mqh |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Select position by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PositionSelectByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
// First try to find position by symbol
|
||||
if(!PositionSelect(symbol))
|
||||
return false;
|
||||
|
||||
// Check if the selected position has the correct magic number
|
||||
if(PositionGetInteger(POSITION_MAGIC) != magic_number)
|
||||
{
|
||||
// Position exists but wrong magic number, search all positions
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(PositionGetTicket(i) > 0)
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Select position by ticket and verify magic number and symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PositionSelectByTicketAndMagic(ulong ticket, ulong magic_number)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
return false;
|
||||
|
||||
return (PositionGetInteger(POSITION_MAGIC) == magic_number);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Select position by ticket and verify symbol, magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PositionSelectByTicketSymbolAndMagic(ulong ticket, string symbol, ulong magic_number)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
return false;
|
||||
|
||||
return (PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if position exists with correct magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PositionExistsByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
return PositionSelectByMagic(symbol, magic_number);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get position ticket by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
ulong GetPositionTicketByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket > 0)
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number)
|
||||
{
|
||||
return ticket;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close position by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ClosePositionByMagic(CTrade &trade_obj, string symbol, ulong magic_number)
|
||||
{
|
||||
ulong ticket = GetPositionTicketByMagic(symbol, magic_number);
|
||||
if(ticket == 0)
|
||||
return false;
|
||||
|
||||
return trade_obj.PositionClose(ticket);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Modify position by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ModifyPositionByMagic(CTrade &trade_obj, string symbol, ulong magic_number,
|
||||
double sl, double tp)
|
||||
{
|
||||
ulong ticket = GetPositionTicketByMagic(symbol, magic_number);
|
||||
if(ticket == 0)
|
||||
return false;
|
||||
|
||||
return trade_obj.PositionModify(ticket, sl, tp);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get position profit by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetPositionProfitByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
if(!PositionSelectByMagic(symbol, magic_number))
|
||||
return 0.0;
|
||||
|
||||
return PositionGetDouble(POSITION_PROFIT);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get position type by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
ENUM_POSITION_TYPE GetPositionTypeByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
if(!PositionSelectByMagic(symbol, magic_number))
|
||||
return WRONG_VALUE;
|
||||
|
||||
return (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Count positions by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
int CountPositionsByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
int count = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket > 0)
|
||||
{
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,75 @@
|
||||
# Pepperstone US - Symbol Setup Guide
|
||||
|
||||
## Finding Correct Symbol Names in MetaTrader 5
|
||||
|
||||
### Step-by-Step Instructions:
|
||||
|
||||
1. **Open Market Watch Window**
|
||||
- Press `Ctrl+M` or go to `View > Market Watch`
|
||||
|
||||
2. **Show All Symbols**
|
||||
- Right-click in the Market Watch window
|
||||
- Select `Show All` or `Symbols`
|
||||
- This shows all available symbols from your broker
|
||||
|
||||
3. **Search for Your Symbols**
|
||||
- Use the search box in the Market Watch window
|
||||
- Search for: "AAPL", "MSFT", "NVDA", "TSLA", "BTCUSD", "XAUUSD"
|
||||
|
||||
4. **Note the Exact Symbol Name**
|
||||
- The symbol name shown in Market Watch is what you need to use
|
||||
- Common formats for Pepperstone US:
|
||||
- Stocks: `AAPL.US`, `MSFT.US`, `NVDA.US`, `TSLA.US`
|
||||
- Or: `NASDAQ:AAPL`, `NASDAQ:MSFT`, etc.
|
||||
- Or: Just `AAPL`, `MSFT`, etc. (if available)
|
||||
|
||||
5. **Add to Market Watch**
|
||||
- Double-click the symbol to add it to your Market Watch
|
||||
- Or right-click and select `Show`
|
||||
|
||||
6. **Update EA Inputs**
|
||||
- Open the EA inputs in MetaTrader 5
|
||||
- Update each symbol parameter with the exact name from Market Watch
|
||||
|
||||
## Common Pepperstone US Symbol Formats
|
||||
|
||||
### US Stocks:
|
||||
- **Apple**: `AAPL.US` or `NASDAQ:AAPL` or `AAPL`
|
||||
- **Microsoft**: `MSFT.US` or `NASDAQ:MSFT` or `MSFT`
|
||||
- **NVIDIA**: `NVDA.US` or `NASDAQ:NVDA` or `NVDA`
|
||||
- **Tesla**: `TSLA.US` or `NASDAQ:TSLA` or `TSLA`
|
||||
|
||||
### Cryptocurrencies:
|
||||
- **Bitcoin**: `BTCUSD` or `BTC/USD` or `BTCUSD.c`
|
||||
|
||||
### Precious Metals:
|
||||
- **Gold**: `XAUUSD` or `GOLD` or `XAU/USD`
|
||||
|
||||
## Important Notes:
|
||||
|
||||
1. **Symbol Names are Case-Sensitive**: Use exact capitalization
|
||||
2. **Add Symbols to Market Watch**: Symbols must be in Market Watch for the EA to access them
|
||||
3. **Check Trading Hours**: US stocks trade during US market hours (9:30 AM - 4:00 PM ET)
|
||||
4. **CFD vs Stock**: Pepperstone offers CFDs on stocks, not actual stocks
|
||||
5. **Spread**: Check the spread for each symbol - some may have wider spreads
|
||||
|
||||
## Troubleshooting:
|
||||
|
||||
### If Symbol Not Found:
|
||||
1. Check if you're connected to Pepperstone US server
|
||||
2. Verify your account type supports the symbol
|
||||
3. Contact Pepperstone support for symbol availability
|
||||
4. Check if symbol requires special account permissions
|
||||
|
||||
### If EA Shows "Symbol Not Available":
|
||||
1. Make sure symbol is added to Market Watch
|
||||
2. Verify symbol name matches exactly (including dots, colons, etc.)
|
||||
3. Check broker connection status
|
||||
4. Try different symbol format variations
|
||||
|
||||
## Testing Symbols:
|
||||
|
||||
You can test if a symbol works by:
|
||||
1. Opening a chart with that symbol
|
||||
2. If chart opens successfully, the symbol name is correct
|
||||
3. Use that exact symbol name in the EA inputs
|
||||
@@ -0,0 +1,607 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| PerformanceEvaluator.mqh |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Performance Metrics Structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct StrategyPerformance {
|
||||
string strategyName;
|
||||
string symbol; // Store symbol to determine if it's a stock
|
||||
int magicNumber;
|
||||
double initialLotSize;
|
||||
double currentLotSize;
|
||||
double quarterProfit;
|
||||
double quarterTrades;
|
||||
double quarterWins;
|
||||
double quarterLosses;
|
||||
double maxDrawdown;
|
||||
double winRate;
|
||||
datetime quarterStart;
|
||||
datetime quarterEnd;
|
||||
bool isActive;
|
||||
bool inPenaltyMode; // True if strategy is in penalty (worst performer)
|
||||
double lotSizeBeforePenalty; // Store lot size before penalty
|
||||
datetime penaltyStartTime; // When penalty started
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Performance Tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
StrategyPerformance strategyPerformances[];
|
||||
int totalStrategies = 0;
|
||||
datetime lastMonthCheck = 0;
|
||||
datetime currentMonthStart = 0;
|
||||
datetime currentMonthEnd = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Performance Adjustment Parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== Performance Evaluation Settings ==="
|
||||
input bool PE_EnableAutoAdjustment = true; // Enable automatic lot size adjustment
|
||||
input double PE_LotSizeIncreasePercent = 10.0; // % increase for top-ranked strategies
|
||||
input double PE_LotSizeDecreasePercent = 10.0; // % decrease for bottom-ranked strategies
|
||||
input double PE_MinLotSize = 0.01; // Minimum lot size for forex/crypto
|
||||
input double PE_MinLotSizeStocks = 5.0; // Minimum lot size for stocks (5-10 range)
|
||||
input double PE_MaxLotSize = 100.0; // Maximum lot size after adjustment
|
||||
input int PE_TopPerformersCount = 3; // Number of top strategies to increase lot size
|
||||
input int PE_BottomPerformersCount = 3; // Number of bottom strategies to decrease lot size
|
||||
input bool PE_UseWinRateWeight = true; // Consider win rate in ranking (50% profit, 50% win rate)
|
||||
input bool PE_EnableBlitzPlay = true; // Enable blitz play: worst performer gets minimum lot size penalty
|
||||
input bool PE_EnableLogging = true; // Enable performance logging
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Initialize Performance Tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void InitPerformanceTracking()
|
||||
{
|
||||
// Calculate current month dates
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeCurrent(), dt);
|
||||
|
||||
// Determine month start (first day of current month)
|
||||
dt.day = 1;
|
||||
dt.hour = 0;
|
||||
dt.min = 0;
|
||||
dt.sec = 0;
|
||||
currentMonthStart = StructToTime(dt);
|
||||
|
||||
// Calculate month end (first day of next month - 1 second)
|
||||
dt.mon += 1;
|
||||
if(dt.mon > 12)
|
||||
{
|
||||
dt.mon = 1;
|
||||
dt.year++;
|
||||
}
|
||||
currentMonthEnd = StructToTime(dt) - 1; // End of last day of month
|
||||
|
||||
lastMonthCheck = TimeCurrent();
|
||||
|
||||
if(PE_EnableLogging)
|
||||
{
|
||||
Print("Performance Evaluator: Initialized");
|
||||
Print("Current Month Start: ", TimeToString(currentMonthStart));
|
||||
Print("Current Month End: ", TimeToString(currentMonthEnd));
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if Symbol is a Stock |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsStockSymbol(string symbol)
|
||||
{
|
||||
// Check if symbol contains common stock indicators
|
||||
if(StringFind(symbol, ".US") >= 0) return true;
|
||||
if(StringFind(symbol, "NASDAQ:") >= 0) return true;
|
||||
if(StringFind(symbol, "NYSE:") >= 0) return true;
|
||||
|
||||
// Note: Symbol category check removed to avoid enum conversion issues
|
||||
// String-based checks (.US, NASDAQ:, NYSE:, common tickers) are sufficient
|
||||
|
||||
// Common stock tickers (without .US suffix)
|
||||
string commonStocks[] = {"AAPL", "NVDA", "TSLA", "GOOGL", "AMZN", "META", "AMD", "NFLX"};
|
||||
for(int i = 0; i < ArraySize(commonStocks); i++)
|
||||
{
|
||||
if(StringFind(symbol, commonStocks[i]) == 0) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get Minimum Lot Size for Symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetMinLotSizeForSymbol(string symbol)
|
||||
{
|
||||
if(IsStockSymbol(symbol))
|
||||
return PE_MinLotSizeStocks;
|
||||
else
|
||||
return PE_MinLotSize;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Register Strategy for Performance Tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void RegisterStrategy(string strategyName, int magicNumber, double initialLotSize, string symbol = "")
|
||||
{
|
||||
// Check if strategy already registered
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].strategyName == strategyName &&
|
||||
strategyPerformances[i].magicNumber == magicNumber)
|
||||
{
|
||||
if(PE_EnableLogging)
|
||||
Print("Performance Evaluator: Strategy '", strategyName, "' already registered");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new strategy
|
||||
int newSize = ArraySize(strategyPerformances) + 1;
|
||||
ArrayResize(strategyPerformances, newSize);
|
||||
|
||||
strategyPerformances[newSize - 1].strategyName = strategyName;
|
||||
strategyPerformances[newSize - 1].symbol = symbol;
|
||||
strategyPerformances[newSize - 1].magicNumber = magicNumber;
|
||||
strategyPerformances[newSize - 1].initialLotSize = initialLotSize;
|
||||
// Start with minimum lot size for safety (symbol-specific minimum)
|
||||
double minLot = GetMinLotSizeForSymbol(symbol);
|
||||
strategyPerformances[newSize - 1].currentLotSize = minLot;
|
||||
strategyPerformances[newSize - 1].quarterProfit = 0.0;
|
||||
strategyPerformances[newSize - 1].quarterTrades = 0;
|
||||
strategyPerformances[newSize - 1].quarterWins = 0;
|
||||
strategyPerformances[newSize - 1].quarterLosses = 0;
|
||||
strategyPerformances[newSize - 1].maxDrawdown = 0.0;
|
||||
strategyPerformances[newSize - 1].winRate = 0.0;
|
||||
strategyPerformances[newSize - 1].quarterStart = currentMonthStart;
|
||||
strategyPerformances[newSize - 1].quarterEnd = currentMonthEnd;
|
||||
strategyPerformances[newSize - 1].isActive = true;
|
||||
strategyPerformances[newSize - 1].inPenaltyMode = false;
|
||||
strategyPerformances[newSize - 1].lotSizeBeforePenalty = initialLotSize;
|
||||
strategyPerformances[newSize - 1].penaltyStartTime = 0;
|
||||
|
||||
totalStrategies = newSize;
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print("Performance Evaluator: Registered strategy '", strategyName,
|
||||
"' (Magic: ", magicNumber, ", Initial Lot: ", initialLotSize, ")");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update Strategy Performance Metrics |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateStrategyPerformance(string strategyName, int magicNumber)
|
||||
{
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].strategyName == strategyName &&
|
||||
strategyPerformances[i].magicNumber == magicNumber &&
|
||||
strategyPerformances[i].isActive)
|
||||
{
|
||||
// Calculate performance for current quarter
|
||||
double totalProfit = 0.0;
|
||||
int totalTrades = 0;
|
||||
int wins = 0;
|
||||
int losses = 0;
|
||||
double maxDD = 0.0;
|
||||
double peakBalance = 0.0;
|
||||
|
||||
// Scan all closed deals in current quarter
|
||||
datetime quarterStart = strategyPerformances[i].quarterStart;
|
||||
datetime quarterEnd = strategyPerformances[i].quarterEnd;
|
||||
|
||||
// Select history for the quarter
|
||||
if(HistorySelect(quarterStart, quarterEnd))
|
||||
{
|
||||
int totalDeals = HistoryDealsTotal();
|
||||
for(int j = 0; j < totalDeals; j++)
|
||||
{
|
||||
ulong ticket = HistoryDealGetTicket(j);
|
||||
if(ticket > 0)
|
||||
{
|
||||
long dealMagic = HistoryDealGetInteger(ticket, DEAL_MAGIC);
|
||||
if(dealMagic == magicNumber)
|
||||
{
|
||||
double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
|
||||
double swap = HistoryDealGetDouble(ticket, DEAL_SWAP);
|
||||
double commission = HistoryDealGetDouble(ticket, DEAL_COMMISSION);
|
||||
double totalDealProfit = profit + swap + commission;
|
||||
|
||||
totalProfit += totalDealProfit;
|
||||
totalTrades++;
|
||||
|
||||
if(totalDealProfit > 0)
|
||||
wins++;
|
||||
else if(totalDealProfit < 0)
|
||||
losses++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate win rate
|
||||
double winRate = 0.0;
|
||||
if(totalTrades > 0)
|
||||
winRate = (double)wins / (double)totalTrades * 100.0;
|
||||
|
||||
// Update metrics
|
||||
strategyPerformances[i].quarterProfit = totalProfit;
|
||||
strategyPerformances[i].quarterTrades = totalTrades;
|
||||
strategyPerformances[i].quarterWins = wins;
|
||||
strategyPerformances[i].quarterLosses = losses;
|
||||
strategyPerformances[i].winRate = winRate;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy Ranking Structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct StrategyRank {
|
||||
int index;
|
||||
double score;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate Strategy Score for Ranking |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateStrategyScore(int strategyIndex)
|
||||
{
|
||||
double profit = strategyPerformances[strategyIndex].quarterProfit;
|
||||
double winRate = strategyPerformances[strategyIndex].winRate;
|
||||
double trades = strategyPerformances[strategyIndex].quarterTrades;
|
||||
|
||||
// Normalize profit (scale to 0-100 range, assuming max profit of $1000)
|
||||
double normalizedProfit = MathMin(profit / 10.0, 100.0);
|
||||
if(profit < 0) normalizedProfit = profit / 5.0; // Penalize losses more
|
||||
|
||||
// Calculate score
|
||||
double score = 0.0;
|
||||
if(PE_UseWinRateWeight)
|
||||
{
|
||||
// 50% profit, 50% win rate (if enough trades)
|
||||
if(trades >= 5)
|
||||
score = (normalizedProfit * 0.5) + (winRate * 0.5);
|
||||
else
|
||||
score = normalizedProfit; // Not enough trades, use profit only
|
||||
}
|
||||
else
|
||||
{
|
||||
// Profit only
|
||||
score = normalizedProfit;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if Month Ended and Evaluate Performance |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckMonthEnd()
|
||||
{
|
||||
datetime now = TimeCurrent();
|
||||
|
||||
// Check if we've entered a new month
|
||||
if(now >= currentMonthEnd)
|
||||
{
|
||||
if(PE_EnableLogging)
|
||||
Print("Performance Evaluator: Month ended. Evaluating and ranking strategies...");
|
||||
|
||||
// Update performance metrics for all strategies
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive)
|
||||
{
|
||||
UpdateStrategyPerformance(strategyPerformances[i].strategyName,
|
||||
strategyPerformances[i].magicNumber);
|
||||
}
|
||||
}
|
||||
|
||||
// Rank strategies
|
||||
int activeCount = 0;
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive)
|
||||
activeCount++;
|
||||
}
|
||||
|
||||
if(activeCount > 0)
|
||||
{
|
||||
// Create ranking array
|
||||
StrategyRank ranks[];
|
||||
ArrayResize(ranks, activeCount);
|
||||
int rankIndex = 0;
|
||||
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive)
|
||||
{
|
||||
ranks[rankIndex].index = i;
|
||||
ranks[rankIndex].score = CalculateStrategyScore(i);
|
||||
rankIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by score (descending - highest score first)
|
||||
for(int i = 0; i < activeCount - 1; i++)
|
||||
{
|
||||
for(int j = i + 1; j < activeCount; j++)
|
||||
{
|
||||
if(ranks[j].score > ranks[i].score)
|
||||
{
|
||||
StrategyRank temp = ranks[i];
|
||||
ranks[i] = ranks[j];
|
||||
ranks[j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust lot sizes based on ranking
|
||||
if(PE_EnableAutoAdjustment)
|
||||
{
|
||||
// Increase top performers (skip if in penalty mode)
|
||||
int topCount = MathMin(PE_TopPerformersCount, activeCount);
|
||||
for(int i = 0; i < topCount; i++)
|
||||
{
|
||||
int strategyIdx = ranks[i].index;
|
||||
|
||||
// Skip if strategy is in penalty mode
|
||||
if(strategyPerformances[strategyIdx].inPenaltyMode)
|
||||
continue;
|
||||
|
||||
double oldLotSize = strategyPerformances[strategyIdx].currentLotSize;
|
||||
double newLotSize = oldLotSize * (1.0 + PE_LotSizeIncreasePercent / 100.0);
|
||||
|
||||
if(newLotSize > PE_MaxLotSize)
|
||||
newLotSize = PE_MaxLotSize;
|
||||
|
||||
strategyPerformances[strategyIdx].currentLotSize = newLotSize;
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print("Performance Evaluator: Rank #", (i+1), " - Increasing '",
|
||||
strategyPerformances[strategyIdx].strategyName,
|
||||
"' lot size from ", oldLotSize, " to ", newLotSize,
|
||||
" (Score: ", DoubleToString(ranks[i].score, 2),
|
||||
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
|
||||
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%)");
|
||||
}
|
||||
|
||||
// Decrease bottom performers (skip worst one if blitz play is enabled)
|
||||
int bottomCount = MathMin(PE_BottomPerformersCount, activeCount);
|
||||
int startIdx = activeCount - bottomCount;
|
||||
|
||||
// If blitz play is enabled, skip the worst performer (it will get minimum penalty)
|
||||
if(PE_EnableBlitzPlay && activeCount > 0)
|
||||
startIdx = activeCount - bottomCount + 1;
|
||||
|
||||
for(int i = startIdx; i < activeCount; i++)
|
||||
{
|
||||
int strategyIdx = ranks[i].index;
|
||||
|
||||
// Skip if strategy is in penalty mode
|
||||
if(strategyPerformances[strategyIdx].inPenaltyMode)
|
||||
continue;
|
||||
|
||||
double oldLotSize = strategyPerformances[strategyIdx].currentLotSize;
|
||||
double newLotSize = oldLotSize * (1.0 - PE_LotSizeDecreasePercent / 100.0);
|
||||
|
||||
// Use symbol-specific minimum lot size
|
||||
double minLot = GetMinLotSizeForSymbol(strategyPerformances[strategyIdx].symbol);
|
||||
if(newLotSize < minLot)
|
||||
newLotSize = minLot;
|
||||
|
||||
strategyPerformances[strategyIdx].currentLotSize = newLotSize;
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print("Performance Evaluator: Rank #", (i+1), " - Decreasing '",
|
||||
strategyPerformances[strategyIdx].strategyName,
|
||||
"' lot size from ", oldLotSize, " to ", newLotSize,
|
||||
" (Score: ", DoubleToString(ranks[i].score, 2),
|
||||
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
|
||||
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%)");
|
||||
}
|
||||
}
|
||||
|
||||
// Blitz Play: Apply penalty to worst performer
|
||||
if(PE_EnableBlitzPlay && activeCount > 0)
|
||||
{
|
||||
// Find worst performer (last in ranking)
|
||||
int worstIdx = ranks[activeCount - 1].index;
|
||||
|
||||
// Remove penalty from previous worst performer (if any)
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive && strategyPerformances[i].inPenaltyMode)
|
||||
{
|
||||
// Check if penalty period has passed (one month)
|
||||
if(now - strategyPerformances[i].penaltyStartTime >= 2592000) // ~30 days
|
||||
{
|
||||
// Restore lot size to before penalty
|
||||
strategyPerformances[i].currentLotSize = strategyPerformances[i].lotSizeBeforePenalty;
|
||||
strategyPerformances[i].inPenaltyMode = false;
|
||||
strategyPerformances[i].penaltyStartTime = 0;
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print("Blitz Play: Penalty removed from '", strategyPerformances[i].strategyName,
|
||||
"'. Lot size restored to ", strategyPerformances[i].currentLotSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply penalty to new worst performer
|
||||
if(!strategyPerformances[worstIdx].inPenaltyMode)
|
||||
{
|
||||
strategyPerformances[worstIdx].lotSizeBeforePenalty = strategyPerformances[worstIdx].currentLotSize;
|
||||
// Use symbol-specific minimum lot size
|
||||
double minLot = GetMinLotSizeForSymbol(strategyPerformances[worstIdx].symbol);
|
||||
strategyPerformances[worstIdx].currentLotSize = minLot;
|
||||
strategyPerformances[worstIdx].inPenaltyMode = true;
|
||||
strategyPerformances[worstIdx].penaltyStartTime = now;
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print("Blitz Play: WORST PERFORMER - '", strategyPerformances[worstIdx].strategyName,
|
||||
"' penalized! Lot size reduced from ", strategyPerformances[worstIdx].lotSizeBeforePenalty,
|
||||
" to minimum ", minLot, " (Score: ", DoubleToString(ranks[activeCount - 1].score, 2),
|
||||
", Profit: $", DoubleToString(strategyPerformances[worstIdx].quarterProfit, 2), ")");
|
||||
}
|
||||
}
|
||||
|
||||
// Log performance report
|
||||
if(PE_EnableLogging)
|
||||
{
|
||||
Print("=== Monthly Performance Ranking ===");
|
||||
for(int i = 0; i < activeCount; i++)
|
||||
{
|
||||
int strategyIdx = ranks[i].index;
|
||||
Print("Rank #", (i+1), ": ", strategyPerformances[strategyIdx].strategyName,
|
||||
" - Score: ", DoubleToString(ranks[i].score, 2),
|
||||
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
|
||||
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%",
|
||||
", Trades: ", (int)strategyPerformances[strategyIdx].quarterTrades,
|
||||
", Lot Size: ", DoubleToString(strategyPerformances[strategyIdx].currentLotSize, 2));
|
||||
}
|
||||
Print("===================================");
|
||||
}
|
||||
}
|
||||
|
||||
// Reset month metrics for all strategies
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive)
|
||||
{
|
||||
strategyPerformances[i].quarterProfit = 0.0;
|
||||
strategyPerformances[i].quarterTrades = 0;
|
||||
strategyPerformances[i].quarterWins = 0;
|
||||
strategyPerformances[i].quarterLosses = 0;
|
||||
strategyPerformances[i].maxDrawdown = 0.0;
|
||||
strategyPerformances[i].winRate = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Update month dates
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(now, dt);
|
||||
|
||||
// First day of current month
|
||||
dt.day = 1;
|
||||
dt.hour = 0;
|
||||
dt.min = 0;
|
||||
dt.sec = 0;
|
||||
currentMonthStart = StructToTime(dt);
|
||||
|
||||
// First day of next month - 1 second
|
||||
dt.mon += 1;
|
||||
if(dt.mon > 12)
|
||||
{
|
||||
dt.mon = 1;
|
||||
dt.year++;
|
||||
}
|
||||
currentMonthEnd = StructToTime(dt) - 1;
|
||||
|
||||
// Update month dates for all strategies
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
strategyPerformances[i].quarterStart = currentMonthStart;
|
||||
strategyPerformances[i].quarterEnd = currentMonthEnd;
|
||||
}
|
||||
|
||||
lastMonthCheck = now;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get Current Lot Size for Strategy |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetStrategyLotSize(string strategyName, int magicNumber)
|
||||
{
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].strategyName == strategyName &&
|
||||
strategyPerformances[i].magicNumber == magicNumber &&
|
||||
strategyPerformances[i].isActive)
|
||||
{
|
||||
return strategyPerformances[i].currentLotSize;
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Process Performance Evaluation (call from OnTick) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ProcessPerformanceEvaluation()
|
||||
{
|
||||
// Check if month ended
|
||||
CheckMonthEnd();
|
||||
|
||||
// Check for penalty expiration (blitz play)
|
||||
if(PE_EnableBlitzPlay)
|
||||
{
|
||||
datetime now = TimeCurrent();
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive && strategyPerformances[i].inPenaltyMode)
|
||||
{
|
||||
// Check if penalty period has passed (one month = ~30 days)
|
||||
if(now - strategyPerformances[i].penaltyStartTime >= 2592000)
|
||||
{
|
||||
// Restore lot size to before penalty
|
||||
strategyPerformances[i].currentLotSize = strategyPerformances[i].lotSizeBeforePenalty;
|
||||
strategyPerformances[i].inPenaltyMode = false;
|
||||
strategyPerformances[i].penaltyStartTime = 0;
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print("Blitz Play: Penalty expired for '", strategyPerformances[i].strategyName,
|
||||
"'. Lot size restored to ", strategyPerformances[i].currentLotSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update performance metrics periodically (every hour)
|
||||
static datetime lastUpdate = 0;
|
||||
if(TimeCurrent() - lastUpdate >= 3600)
|
||||
{
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive)
|
||||
{
|
||||
UpdateStrategyPerformance(strategyPerformances[i].strategyName,
|
||||
strategyPerformances[i].magicNumber);
|
||||
}
|
||||
}
|
||||
lastUpdate = TimeCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get Performance Summary |
|
||||
//+------------------------------------------------------------------+
|
||||
string GetPerformanceSummary()
|
||||
{
|
||||
string summary = "\n=== Performance Summary ===\n";
|
||||
summary += "Current Month: " + TimeToString(currentMonthStart) + " to " + TimeToString(currentMonthEnd) + "\n\n";
|
||||
|
||||
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
||||
{
|
||||
if(strategyPerformances[i].isActive)
|
||||
{
|
||||
summary += strategyPerformances[i].strategyName + ":\n";
|
||||
summary += " Profit: $" + DoubleToString(strategyPerformances[i].quarterProfit, 2) + "\n";
|
||||
summary += " Trades: " + IntegerToString((int)strategyPerformances[i].quarterTrades) + "\n";
|
||||
summary += " Win Rate: " + DoubleToString(strategyPerformances[i].winRate, 2) + "%\n";
|
||||
summary += " Lot Size: " + DoubleToString(strategyPerformances[i].currentLotSize, 2) + "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,76 @@
|
||||
# United EA Strategy Configuration Summary
|
||||
|
||||
## Strategy Symbols and Magic Numbers
|
||||
|
||||
### Strategy 1: DarvasBox
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Number**: 135790
|
||||
|
||||
### Strategy 2: EMASlopeDistance
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Number**: 12350
|
||||
|
||||
### Strategy 3: RSICrossOverReversal
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Number**: 7
|
||||
|
||||
### Strategy 4: RSIMidPointHijack
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Numbers**:
|
||||
- RSIFollow: 1001
|
||||
- RSIReverse: 1002
|
||||
- EMACross: 1003
|
||||
|
||||
### Strategy 5: RSI Scalping APPL (Apple)
|
||||
- **Symbol**: AAPL (Apple stock)
|
||||
- **Magic Number**: 20001
|
||||
- **Note**: Changed from "APPL" to "AAPL" (correct ticker symbol)
|
||||
|
||||
### Strategy 6: RSI Scalping BTCUSD
|
||||
- **Symbol**: BTCUSD (Bitcoin/USD)
|
||||
- **Magic Number**: 123459123
|
||||
|
||||
### Strategy 7: RSI Scalping MSFT
|
||||
- **Symbol**: MSFT (Microsoft stock)
|
||||
- **Magic Number**: 20002
|
||||
|
||||
### Strategy 8: RSI Scalping NVDA
|
||||
- **Symbol**: NVDA (NVIDIA stock)
|
||||
- **Magic Number**: 20003
|
||||
|
||||
### Strategy 9: RSI Scalping TSLA
|
||||
- **Symbol**: TSLA (Tesla stock)
|
||||
- **Magic Number**: 125421321
|
||||
|
||||
### Strategy 10: RSI Scalping XAUUSD
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Number**: 129102315
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Stock Symbols**: Stock symbols (AAPL, MSFT, NVDA, TSLA) must be:
|
||||
- Added to Market Watch in MetaTrader 5
|
||||
- Available from your broker
|
||||
- Use the correct ticker symbol (e.g., "AAPL" not "APPL")
|
||||
|
||||
2. **Magic Numbers**: All strategies have unique magic numbers to prevent interference:
|
||||
- Each strategy can be identified by its magic number
|
||||
- RSIMidPointHijack uses 3 magic numbers (one for each sub-strategy)
|
||||
|
||||
3. **Symbol Configuration**: Each strategy trades on its own symbol:
|
||||
- You can change symbols in the input parameters
|
||||
- The EA will log warnings if a symbol is not available
|
||||
- Strategies with unavailable symbols will be skipped (EA continues running)
|
||||
|
||||
4. **RSI Scalping Strategies**:
|
||||
- Each RSI Scalping variant trades on a different symbol
|
||||
- They all use the same strategy logic but with different parameters
|
||||
- Buy and sell signals are generated based on RSI levels for each symbol
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If stock symbols are not working:
|
||||
1. Check if the symbol exists in your broker's symbol list
|
||||
2. Add the symbol to Market Watch in MetaTrader 5
|
||||
3. Verify the symbol name matches your broker's naming convention
|
||||
4. Some brokers use prefixes/suffixes (e.g., "NASDAQ:AAPL" or "AAPL.US")
|
||||
@@ -0,0 +1,300 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DarvasBoxStrategy.mqh |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
bool InitDarvasBox(string symbol)
|
||||
{
|
||||
dbData.symbol = symbol;
|
||||
dbData.boxHigh = 0;
|
||||
dbData.boxLow = 0;
|
||||
dbData.boxFormed = false;
|
||||
dbData.lastBoxTime = 0;
|
||||
dbData.boxName = "DarvasBox_" + IntegerToString(DB_MagicNumber) + "_";
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Print("DarvasBox: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sleep(100); // Wait for symbol to be ready
|
||||
|
||||
dbData.point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
dbData.minStopLevel = SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL) * dbData.point;
|
||||
|
||||
dbData.maHandle = iMA(symbol, DB_TrendTimeframe, DB_MA_Period, 0, DB_MA_Method, DB_MA_Price);
|
||||
dbData.volumeHandle = iVolumes(symbol, PERIOD_CURRENT, VOLUME_TICK);
|
||||
|
||||
if(dbData.maHandle == INVALID_HANDLE || dbData.volumeHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("DarvasBox: Error creating indicators for '", symbol, "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
dbData.trade.SetDeviationInPoints(10);
|
||||
dbData.trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
dbData.trade.SetAsyncMode(false);
|
||||
dbData.trade.SetExpertMagicNumber(DB_MagicNumber);
|
||||
|
||||
ObjectsDeleteAll(0, dbData.boxName);
|
||||
dbData.isInitialized = true;
|
||||
Print("DarvasBox: Successfully initialized for symbol '", symbol, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeinitDarvasBox()
|
||||
{
|
||||
if(dbData.maHandle != INVALID_HANDLE) IndicatorRelease(dbData.maHandle);
|
||||
if(dbData.volumeHandle != INVALID_HANDLE) IndicatorRelease(dbData.volumeHandle);
|
||||
ObjectsDeleteAll(0, dbData.boxName);
|
||||
}
|
||||
|
||||
void DrawDarvasBox()
|
||||
{
|
||||
if(!dbData.boxFormed) return;
|
||||
|
||||
datetime time1 = iTime(dbData.symbol, PERIOD_H1, DB_BoxPeriod);
|
||||
datetime time2 = iTime(dbData.symbol, PERIOD_H1, 0);
|
||||
|
||||
ObjectsDeleteAll(0, dbData.boxName);
|
||||
|
||||
ObjectCreate(0, dbData.boxName + "Top", OBJ_TREND, 0, time1, dbData.boxHigh, time2, dbData.boxHigh);
|
||||
ObjectCreate(0, dbData.boxName + "Bottom", OBJ_TREND, 0, time1, dbData.boxLow, time2, dbData.boxLow);
|
||||
|
||||
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_COLOR, DB_BoxColor);
|
||||
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_COLOR, DB_BoxColor);
|
||||
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_WIDTH, DB_BoxWidth);
|
||||
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_WIDTH, DB_BoxWidth);
|
||||
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_RAY_RIGHT, true);
|
||||
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_RAY_RIGHT, true);
|
||||
}
|
||||
|
||||
void CalculateDarvasBox()
|
||||
{
|
||||
double high = 0;
|
||||
double low = DBL_MAX;
|
||||
|
||||
// Find highest high and lowest low in the period - EXACTLY like original
|
||||
for(int i = 0; i < DB_BoxPeriod; i++)
|
||||
{
|
||||
high = MathMax(high, iHigh(dbData.symbol, PERIOD_H1, i));
|
||||
low = MathMin(low, iLow(dbData.symbol, PERIOD_H1, i));
|
||||
}
|
||||
|
||||
double range = high - low;
|
||||
double allowedRange = DB_BoxDeviation * dbData.point; // Use dbData.point instead of _Point
|
||||
|
||||
if(DB_EnableLogging)
|
||||
{
|
||||
Print("DarvasBox: Box Calculation - High: ", high, " Low: ", low, " Range: ", range, " Allowed Range: ", allowedRange);
|
||||
}
|
||||
|
||||
// Check if box is formed - EXACTLY like original
|
||||
if(range <= allowedRange)
|
||||
{
|
||||
dbData.boxHigh = high;
|
||||
dbData.boxLow = low;
|
||||
dbData.boxFormed = true;
|
||||
dbData.lastBoxTime = iTime(dbData.symbol, PERIOD_CURRENT, 0);
|
||||
|
||||
// Draw the box
|
||||
DrawDarvasBox();
|
||||
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Box Formed - High: ", dbData.boxHigh, " Low: ", dbData.boxLow, " Time: ", dbData.lastBoxTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbData.boxFormed = false;
|
||||
// Delete box if it exists
|
||||
ObjectsDeleteAll(0, dbData.boxName);
|
||||
}
|
||||
}
|
||||
|
||||
bool ValidateStopLevels(double price, double &sl, double &tp, ENUM_ORDER_TYPE orderType)
|
||||
{
|
||||
double minSlDistance = MathMax(dbData.minStopLevel, DB_StopLoss * dbData.point);
|
||||
double minTpDistance = MathMax(dbData.minStopLevel, DB_TakeProfit * dbData.point);
|
||||
|
||||
if(orderType == ORDER_TYPE_BUY)
|
||||
{
|
||||
sl = price - minSlDistance;
|
||||
tp = price + minTpDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
sl = price + minSlDistance;
|
||||
tp = price - minTpDistance;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsTrendFavorable(ENUM_ORDER_TYPE orderType)
|
||||
{
|
||||
double ma[];
|
||||
ArraySetAsSeries(ma, true);
|
||||
|
||||
if(CopyBuffer(dbData.maHandle, 0, 0, 2, ma) <= 0)
|
||||
return false;
|
||||
|
||||
double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK);
|
||||
double trendStrength = MathAbs(currentPrice - ma[0]) / dbData.point;
|
||||
|
||||
if(orderType == ORDER_TYPE_BUY)
|
||||
return (currentPrice > ma[0] && trendStrength > DB_TrendThreshold);
|
||||
else
|
||||
return (currentPrice < ma[0] && trendStrength > DB_TrendThreshold);
|
||||
}
|
||||
|
||||
bool CheckVolumeConditions()
|
||||
{
|
||||
double volumes[];
|
||||
ArraySetAsSeries(volumes, true);
|
||||
|
||||
if(CopyBuffer(dbData.volumeHandle, 0, 0, DB_VolumeMA_Period + 1, volumes) <= 0)
|
||||
return false;
|
||||
|
||||
double volumeMA = 0;
|
||||
for(int i = 1; i <= DB_VolumeMA_Period; i++)
|
||||
volumeMA += volumes[i];
|
||||
volumeMA /= DB_VolumeMA_Period;
|
||||
|
||||
double currentVolume = volumes[0];
|
||||
double volumeRatio = currentVolume / volumeMA;
|
||||
|
||||
return (volumeRatio > DB_VolumeThresholdMultiplier);
|
||||
}
|
||||
|
||||
bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price, double sl, double tp)
|
||||
{
|
||||
if(!ValidateStopLevels(price, sl, tp, orderType))
|
||||
{
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Order rejected - Stop levels validation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!IsTrendFavorable(orderType))
|
||||
{
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Order rejected - Trend not favorable for ", EnumToString(orderType));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!CheckVolumeConditions())
|
||||
{
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Order rejected - Volume conditions not met");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
|
||||
// Use market price (0) instead of explicit price - this ensures market order execution
|
||||
// In backtesting, explicit price might fail if price has moved
|
||||
if(orderType == ORDER_TYPE_BUY)
|
||||
result = dbData.trade.Buy(g_DB_LotSize, dbData.symbol, 0, sl, tp, "Darvas Box Breakout");
|
||||
else
|
||||
result = dbData.trade.Sell(g_DB_LotSize, dbData.symbol, 0, sl, tp, "Darvas Box Breakdown");
|
||||
|
||||
// Always log errors, success only if logging enabled
|
||||
if(result)
|
||||
{
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: ", (orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"), " Order Placed Successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Always log failures with detailed info
|
||||
uint retcode_uint = dbData.trade.ResultRetcode();
|
||||
int retcode = (int)retcode_uint;
|
||||
string desc = dbData.trade.ResultRetcodeDescription();
|
||||
ulong deal = dbData.trade.ResultDeal();
|
||||
ulong order = dbData.trade.ResultOrder();
|
||||
Print("DarvasBox: ", (orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"),
|
||||
" Order Failed - Retcode: ", retcode,
|
||||
", Description: ", desc,
|
||||
", Deal: ", deal,
|
||||
", Order: ", order,
|
||||
", Symbol: ", dbData.symbol,
|
||||
", Requested Price: ", price,
|
||||
", SL: ", sl,
|
||||
", TP: ", tp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ProcessDarvasBox(string symbol)
|
||||
{
|
||||
// Skip if not initialized (symbol not available)
|
||||
if(!dbData.isInitialized)
|
||||
return;
|
||||
|
||||
dbData.symbol = symbol; // Update symbol in case it changed
|
||||
|
||||
// Calculate new box levels - EXACTLY like original (called every tick)
|
||||
CalculateDarvasBox();
|
||||
|
||||
// Check for trading signals - EXACTLY like original (checked every tick)
|
||||
if(dbData.boxFormed)
|
||||
{
|
||||
double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK);
|
||||
long currentVolume_long = iVolume(dbData.symbol, PERIOD_CURRENT, 0);
|
||||
double currentVolume = (double)currentVolume_long;
|
||||
|
||||
if(DB_EnableLogging)
|
||||
{
|
||||
Print("DarvasBox: Current Price: ", currentPrice, " Box High: ", dbData.boxHigh, " Box Low: ", dbData.boxLow);
|
||||
Print("DarvasBox: Current Volume: ", currentVolume, " Volume Threshold: ", DB_VolumeThreshold);
|
||||
}
|
||||
|
||||
// Check for breakout above box - EXACTLY like original
|
||||
if(currentPrice > dbData.boxHigh && currentVolume > DB_VolumeThreshold)
|
||||
{
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Breakout Signal Detected - Price above box high");
|
||||
|
||||
// Buy signal
|
||||
if(!PositionExistsByMagic(dbData.symbol, (ulong)DB_MagicNumber)) // No existing positions with our magic number
|
||||
{
|
||||
double sl = currentPrice - DB_StopLoss * dbData.point;
|
||||
double tp = currentPrice + DB_TakeProfit * dbData.point;
|
||||
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Preparing Buy Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
|
||||
|
||||
PlaceOrder(ORDER_TYPE_BUY, currentPrice, sl, tp);
|
||||
}
|
||||
else if(DB_EnableLogging)
|
||||
Print("DarvasBox: Skipping Buy Signal - Position already exists");
|
||||
}
|
||||
|
||||
// Check for breakdown below box - EXACTLY like original
|
||||
if(currentPrice < dbData.boxLow && currentVolume > DB_VolumeThreshold)
|
||||
{
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Breakdown Signal Detected - Price below box low");
|
||||
|
||||
// Sell signal
|
||||
if(!PositionExistsByMagic(dbData.symbol, (ulong)DB_MagicNumber)) // No existing positions with our magic number
|
||||
{
|
||||
double sl = currentPrice + DB_StopLoss * dbData.point;
|
||||
double tp = currentPrice - DB_TakeProfit * dbData.point;
|
||||
|
||||
if(DB_EnableLogging)
|
||||
Print("DarvasBox: Preparing Sell Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
|
||||
|
||||
PlaceOrder(ORDER_TYPE_SELL, currentPrice, sl, tp);
|
||||
}
|
||||
else if(DB_EnableLogging)
|
||||
Print("DarvasBox: Skipping Sell Signal - Position already exists");
|
||||
}
|
||||
}
|
||||
else if(DB_EnableLogging)
|
||||
Print("DarvasBox: No Box Formed - Waiting for consolidation");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,496 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| EMASlopeDistanceStrategy.mqh |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
bool InitEMASlopeDistance(string symbol)
|
||||
{
|
||||
esData.symbol = symbol;
|
||||
esData.letzte_überwachung_zeit = 0;
|
||||
esData.überwachung_aktiv = false;
|
||||
esData.preis_trigger_aktiv = false;
|
||||
esData.steigung_trigger_aktiv = false;
|
||||
esData.ticket = 0;
|
||||
esData.trades_in_current_crossover = 0;
|
||||
esData.crossover_detected = false;
|
||||
esData.trade_open_time = 0;
|
||||
esData.last_bar_time = 0;
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Print("EMASlopeDistance: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sleep(100); // Wait for symbol to be ready
|
||||
|
||||
esData.trade.SetExpertMagicNumber(ES_MagicNumber);
|
||||
esData.trade.SetDeviationInPoints(10);
|
||||
esData.trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
|
||||
esData.ema_handle = iMA(symbol, ES_Timeframe, ES_EMA_Periode, 0, MODE_EMA, PRICE_CLOSE);
|
||||
|
||||
if(esData.ema_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("EMASlopeDistance: Error creating EMA indicator for '", symbol, "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
ArraySetAsSeries(esData.ema_array, true);
|
||||
esData.isInitialized = true;
|
||||
Print("EMASlopeDistance: Successfully initialized for symbol '", symbol, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeinitEMASlopeDistance()
|
||||
{
|
||||
if(esData.ema_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(esData.ema_handle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| EMA Berechnung (EMA Calculation) |
|
||||
//+------------------------------------------------------------------+
|
||||
void BerechneEMA()
|
||||
{
|
||||
//--- EMA Werte vom Indicator kopieren (Copy EMA values from indicator)
|
||||
int copied = CopyBuffer(esData.ema_handle, 0, 0, 3, esData.ema_array);
|
||||
|
||||
if(copied <= 0)
|
||||
{
|
||||
Print("TRACE: Fehler beim Kopieren der EMA Werte - Copied: ", copied);
|
||||
return;
|
||||
}
|
||||
|
||||
Print("TRACE: EMA Werte kopiert: ", copied, " Bars");
|
||||
Print("TRACE: EMA [0]: ", esData.ema_array[0], " [1]: ", esData.ema_array[1], " [2]: ", esData.ema_array[2]);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trigger-Bedingungen prüfen (Check trigger conditions) |
|
||||
//+------------------------------------------------------------------+
|
||||
void PrüfeTrigger()
|
||||
{
|
||||
if(ArraySize(esData.ema_array) < 2)
|
||||
{
|
||||
Print("TRACE: Array zu klein - Größe: ", ArraySize(esData.ema_array));
|
||||
return;
|
||||
}
|
||||
|
||||
//--- Aktuelle Werte (Current values)
|
||||
double aktueller_preis = SymbolInfoDouble(esData.symbol, SYMBOL_BID);
|
||||
double aktueller_ask = SymbolInfoDouble(esData.symbol, SYMBOL_ASK);
|
||||
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
|
||||
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
|
||||
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
|
||||
double pips_multiplier = (digits == 3 || digits == 5) ? 10.0 : 1.0;
|
||||
|
||||
//--- EMA Werte in Variablen (EMA values in variables)
|
||||
double ema_aktuell = esData.ema_array[0];
|
||||
double ema_vorher = esData.ema_array[1];
|
||||
|
||||
//--- EMA Crossover Erkennung (EMA Crossover Detection)
|
||||
// Prüfe ob Preis die EMA kreuzt (Check if price crosses EMA)
|
||||
static double last_close = 0;
|
||||
static double last_ema = 0;
|
||||
|
||||
if(last_close != 0 && last_ema != 0)
|
||||
{
|
||||
bool crossover_bullish = (last_close <= last_ema) && (aktueller_close > ema_aktuell);
|
||||
bool crossover_bearish = (last_close >= last_ema) && (aktueller_close < ema_aktuell);
|
||||
|
||||
//--- Neues Crossover-Ereignis erkannt (New crossover event detected)
|
||||
if(crossover_bullish || crossover_bearish)
|
||||
{
|
||||
esData.trades_in_current_crossover = 0; // Reset trade counter
|
||||
Print("TRACE: EMA Crossover erkannt - ", (crossover_bullish ? "BULLISH" : "BEARISH"), " - Trade-Counter zurückgesetzt");
|
||||
Print("TRACE: Vorher: Close=", last_close, " EMA=", last_ema, " Jetzt: Close=", aktueller_close, " EMA=", ema_aktuell);
|
||||
}
|
||||
}
|
||||
|
||||
//--- Aktuelle Werte für nächsten Vergleich speichern (Save current values for next comparison)
|
||||
last_close = aktueller_close;
|
||||
last_ema = ema_aktuell;
|
||||
|
||||
//--- Preisbewegung zur EMA prüfen (Check price action to EMA)
|
||||
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / point / pips_multiplier;
|
||||
|
||||
Print("TRACE: Preis-Abstand: ", preis_abstand, " Pips (Schwelle: ", ES_PreisSchwelle, ")");
|
||||
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
|
||||
Print("TRACE: Trades im aktuellen Crossover: ", esData.trades_in_current_crossover, "/", ES_MaxTradesPerCrossover);
|
||||
|
||||
if(preis_abstand > ES_PreisSchwelle && !esData.preis_trigger_aktiv)
|
||||
{
|
||||
esData.preis_trigger_aktiv = true;
|
||||
Print("TRACE: Preis-Trigger aktiviert: ", preis_abstand, " Pips");
|
||||
}
|
||||
|
||||
//--- EMA Steigung prüfen (Check EMA slope)
|
||||
double steigung = (ema_aktuell - ema_vorher) / point / pips_multiplier;
|
||||
|
||||
Print("TRACE: EMA Steigung: ", steigung, " Pips (Schwelle: ", ES_SteigungSchwelle, ")");
|
||||
|
||||
if(MathAbs(steigung) > ES_SteigungSchwelle && !esData.steigung_trigger_aktiv)
|
||||
{
|
||||
esData.steigung_trigger_aktiv = true;
|
||||
Print("TRACE: Steigungs-Trigger aktiviert: ", steigung, " Pips");
|
||||
}
|
||||
|
||||
//--- Überwachung starten wenn beide Trigger aktiv sind (Start monitoring when both triggers are active)
|
||||
if(esData.preis_trigger_aktiv && esData.steigung_trigger_aktiv && !esData.überwachung_aktiv)
|
||||
{
|
||||
esData.überwachung_aktiv = true;
|
||||
|
||||
if(ES_UseBarData)
|
||||
{
|
||||
esData.letzte_überwachung_zeit = iTime(esData.symbol, ES_Timeframe, 0); // Aktuelle Bar-Zeit
|
||||
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Bar: ", TimeToString(esData.letzte_überwachung_zeit), ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
esData.letzte_überwachung_zeit = TimeCurrent(); // Aktuelle Tick-Zeit
|
||||
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Tick)");
|
||||
}
|
||||
}
|
||||
|
||||
//--- Trade platzieren wenn Überwachung aktiv und Preis über/unter EMA (Place trade when monitoring active and price above/below EMA)
|
||||
if(esData.überwachung_aktiv)
|
||||
{
|
||||
bool bullish_signal = aktueller_close > ema_aktuell;
|
||||
bool bearish_signal = aktueller_close < ema_aktuell;
|
||||
|
||||
Print("TRACE: Signal Check - Bullish: ", bullish_signal, " Bearish: ", bearish_signal);
|
||||
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
|
||||
Print("TRACE: Differenz: ", aktueller_close - ema_aktuell);
|
||||
|
||||
//--- Trade-Limit prüfen (Check trade limit)
|
||||
if(esData.trades_in_current_crossover >= ES_MaxTradesPerCrossover)
|
||||
{
|
||||
Print("TRACE: Trade-Limit erreicht (", ES_MaxTradesPerCrossover, ") - Kein neuer Trade");
|
||||
return;
|
||||
}
|
||||
|
||||
if(bullish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
|
||||
{
|
||||
Print("TRACE: Versuche KAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
|
||||
if(PlatziereTrade(ORDER_TYPE_BUY))
|
||||
{
|
||||
esData.trades_in_current_crossover++;
|
||||
}
|
||||
}
|
||||
else if(bearish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
|
||||
{
|
||||
Print("TRACE: Versuche VERKAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
|
||||
if(PlatziereTrade(ORDER_TYPE_SELL))
|
||||
{
|
||||
esData.trades_in_current_crossover++;
|
||||
}
|
||||
}
|
||||
else if(PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
|
||||
{
|
||||
Print("TRACE: Position bereits offen - kein neuer Trade");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trade platzieren (Place trade) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PlatziereTrade(ENUM_ORDER_TYPE order_type)
|
||||
{
|
||||
Print("TRACE: Versuche Trade zu platzieren - Typ: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF");
|
||||
Print("TRACE: Lot: ", g_ES_LotSize);
|
||||
|
||||
bool success = false;
|
||||
|
||||
if(order_type == ORDER_TYPE_BUY)
|
||||
{
|
||||
success = esData.trade.Buy(g_ES_LotSize, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
|
||||
}
|
||||
else
|
||||
{
|
||||
success = esData.trade.Sell(g_ES_LotSize, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
|
||||
}
|
||||
|
||||
if(success)
|
||||
{
|
||||
esData.ticket = (int)esData.trade.ResultOrder();
|
||||
Print("TRACE: Trade erfolgreich platziert: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF", " Ticket: ", esData.ticket);
|
||||
|
||||
//--- Trade-Öffnungszeit speichern (Save trade opening time)
|
||||
esData.trade_open_time = iTime(esData.symbol, ES_Timeframe, 0);
|
||||
Print("TRACE: Trade-Öffnungszeit: ", TimeToString(esData.trade_open_time));
|
||||
|
||||
//--- Überwachung zurücksetzen (Reset monitoring)
|
||||
esData.überwachung_aktiv = false;
|
||||
esData.preis_trigger_aktiv = false;
|
||||
esData.steigung_trigger_aktiv = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Fehler beim Platzieren des Trades - Retcode: ", esData.trade.ResultRetcode());
|
||||
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trades verwalten (Manage trades) |
|
||||
//+------------------------------------------------------------------+
|
||||
void VerwalteTrades()
|
||||
{
|
||||
if(!PositionSelectByMagic(esData.symbol, (ulong)ES_MagicNumber))
|
||||
return;
|
||||
|
||||
double position_profit = PositionGetDouble(POSITION_PROFIT);
|
||||
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
|
||||
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
|
||||
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
|
||||
double pips_multiplier = (digits == 3 || digits == 5) ? 10.0 : 1.0;
|
||||
double trailing_stop_pips = ES_TrailingStop;
|
||||
|
||||
//--- Gleitender Stop (Trailing Stop) - nur wenn Position im Profit ist
|
||||
if(position_profit > 0) // Only apply trailing stop when in profit
|
||||
{
|
||||
if(position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
double new_stop_loss = current_price - (trailing_stop_pips * point * pips_multiplier);
|
||||
double current_stop_loss = PositionGetDouble(POSITION_SL);
|
||||
|
||||
// Only move stop loss if new stop is higher than current stop
|
||||
if(new_stop_loss > current_stop_loss)
|
||||
{
|
||||
ÄndereStopLoss(new_stop_loss);
|
||||
}
|
||||
}
|
||||
else if(position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
double new_stop_loss = current_price + (trailing_stop_pips * point * pips_multiplier);
|
||||
double current_stop_loss = PositionGetDouble(POSITION_SL);
|
||||
|
||||
// Only move stop loss if new stop is lower than current stop
|
||||
if(new_stop_loss < current_stop_loss || current_stop_loss == 0)
|
||||
{
|
||||
ÄndereStopLoss(new_stop_loss);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--- Ausstieg bei Preis unter/über EMA (Exit when price below/above EMA)
|
||||
if(ArraySize(esData.ema_array) >= 1)
|
||||
{
|
||||
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
|
||||
double ema_aktuell = esData.ema_array[0];
|
||||
bool exit_bullish = (position_type == POSITION_TYPE_SELL && aktueller_close > ema_aktuell);
|
||||
bool exit_bearish = (position_type == POSITION_TYPE_BUY && aktueller_close < ema_aktuell);
|
||||
|
||||
if(exit_bullish || exit_bearish)
|
||||
{
|
||||
Print("TRACE: Ausstiegssignal - Close: ", aktueller_close, " EMA: ", ema_aktuell);
|
||||
SchließePosition("EMA Crossover Exit");
|
||||
|
||||
Print("TRACE: Position geschlossen - Trade-Counter bleibt bei ", esData.trades_in_current_crossover);
|
||||
}
|
||||
}
|
||||
|
||||
//--- Profit-Prüfung nach X Bars (Profit check after X bars)
|
||||
if(ES_CloseUnprofitableTrades && esData.trade_open_time != 0 && PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
|
||||
{
|
||||
Print("TRACE: Profit-Prüfung aktiviert - CloseUnprofitableTrades: ", ES_CloseUnprofitableTrades);
|
||||
PrüfeProfitNachBars();
|
||||
}
|
||||
else if(!ES_CloseUnprofitableTrades)
|
||||
{
|
||||
Print("TRACE: Profit-Prüfung deaktiviert - CloseUnprofitableTrades: ", ES_CloseUnprofitableTrades);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Profit-Prüfung nach X Bars (Profit check after X bars) |
|
||||
//+------------------------------------------------------------------+
|
||||
void PrüfeProfitNachBars()
|
||||
{
|
||||
if(!PositionSelectByMagic(esData.symbol, (ulong)ES_MagicNumber))
|
||||
{
|
||||
return; // Keine Position offen
|
||||
}
|
||||
|
||||
datetime current_bar_time = iTime(esData.symbol, ES_Timeframe, 0);
|
||||
int bars_since_trade_open = iBarShift(esData.symbol, ES_Timeframe, esData.trade_open_time);
|
||||
|
||||
Print("TRACE: Bars seit Trade-Öffnung: ", bars_since_trade_open, "/", ES_ProfitCheckBars);
|
||||
|
||||
//--- Prüfe ob genügend Bars vergangen sind (Check if enough bars have passed)
|
||||
if(bars_since_trade_open >= ES_ProfitCheckBars)
|
||||
{
|
||||
double position_profit = PositionGetDouble(POSITION_PROFIT);
|
||||
double position_volume = PositionGetDouble(POSITION_VOLUME);
|
||||
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
Print("TRACE: Profit-Prüfung nach ", ES_ProfitCheckBars, " Bars");
|
||||
Print("TRACE: Position Profit: ", position_profit, " USD");
|
||||
|
||||
//--- Schließe Position wenn nicht im Profit (Close position if not in profit)
|
||||
if(position_profit <= 0)
|
||||
{
|
||||
Print("TRACE: Position nicht im Profit - Schließe Position");
|
||||
SchließePosition("Profit Check - Unprofitable");
|
||||
|
||||
//--- Trade-Öffnungszeit zurücksetzen (Reset trade opening time)
|
||||
esData.trade_open_time = 0;
|
||||
Print("TRACE: Trade-Öffnungszeit zurückgesetzt");
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Position im Profit - Behalte Position");
|
||||
//--- Trade-Öffnungszeit zurücksetzen um weitere Prüfungen zu vermeiden (Reset to avoid further checks)
|
||||
esData.trade_open_time = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Stop Loss ändern (Modify Stop Loss) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ÄndereStopLoss(double new_stop_loss)
|
||||
{
|
||||
Print("TRACE: Versuche Stop Loss zu ändern auf: ", new_stop_loss);
|
||||
|
||||
bool success = ModifyPositionByMagic(esData.trade, esData.symbol, (ulong)ES_MagicNumber, new_stop_loss, PositionGetDouble(POSITION_TP));
|
||||
|
||||
if(success)
|
||||
{
|
||||
Print("TRACE: Stop Loss erfolgreich geändert auf: ", new_stop_loss);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Fehler beim Ändern des Stop Loss - Retcode: ", esData.trade.ResultRetcode());
|
||||
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Position schließen (Close position) |
|
||||
//+------------------------------------------------------------------+
|
||||
void SchließePosition(string reason = "Unbekannt")
|
||||
{
|
||||
Print("TRACE: Versuche Position zu schließen - Grund: ", reason);
|
||||
|
||||
bool success = ClosePositionByMagic(esData.trade, esData.symbol, (ulong)ES_MagicNumber);
|
||||
|
||||
if(success)
|
||||
{
|
||||
Print("TRACE: Position erfolgreich geschlossen - Grund: ", reason);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Fehler beim Schließen der Position - Retcode: ", esData.trade.ResultRetcode());
|
||||
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void ProcessEMASlopeDistance(string symbol)
|
||||
{
|
||||
// Skip if not initialized (symbol not available)
|
||||
if(!esData.isInitialized)
|
||||
return;
|
||||
|
||||
esData.symbol = symbol; // Update symbol in case it changed
|
||||
|
||||
//--- Bar-Daten oder Tick-Daten verwenden (Use bar data or tick data)
|
||||
if(ES_UseBarData)
|
||||
{
|
||||
//--- Nur bei neuen Bars ausführen (Only execute on new bars)
|
||||
datetime current_bar_time = iTime(esData.symbol, ES_Timeframe, 0);
|
||||
|
||||
if(current_bar_time == esData.last_bar_time)
|
||||
{
|
||||
return; // Kein neuer Bar, nichts tun
|
||||
}
|
||||
|
||||
esData.last_bar_time = current_bar_time;
|
||||
}
|
||||
|
||||
//--- EMA Werte berechnen (Calculate EMA values)
|
||||
BerechneEMA();
|
||||
|
||||
//--- Debug: Aktuelle Werte ausgeben (Debug: Output current values)
|
||||
if(ArraySize(esData.ema_array) > 0)
|
||||
{
|
||||
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
|
||||
double ema_aktuell = esData.ema_array[0];
|
||||
double ema_vorher = esData.ema_array[1];
|
||||
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
|
||||
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
|
||||
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / point;
|
||||
double steigung = (ema_aktuell - ema_vorher) / point;
|
||||
|
||||
if(ES_UseBarData)
|
||||
{
|
||||
Print("=== DEBUG INFO (Neuer Bar) ===");
|
||||
Print("Bar Zeit: ", TimeToString(iTime(esData.symbol, ES_Timeframe, 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("=== DEBUG INFO (Tick) ===");
|
||||
}
|
||||
|
||||
Print("Aktueller Close: ", aktueller_close);
|
||||
Print("EMA: ", ema_aktuell);
|
||||
Print("Preis-Abstand: ", preis_abstand, " Pips");
|
||||
Print("EMA Steigung: ", steigung, " Pips");
|
||||
Print("Differenz Close-EMA: ", aktueller_close - ema_aktuell);
|
||||
Print("Preis-Trigger: ", esData.preis_trigger_aktiv, " Steigungs-Trigger: ", esData.steigung_trigger_aktiv);
|
||||
Print("Überwachung aktiv: ", esData.überwachung_aktiv);
|
||||
Print("Position offen: ", PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber));
|
||||
Print("Trades im aktuellen Crossover: ", esData.trades_in_current_crossover, "/", ES_MaxTradesPerCrossover);
|
||||
Print("==================");
|
||||
}
|
||||
|
||||
//--- Überwachung prüfen (Check monitoring)
|
||||
if(esData.überwachung_aktiv)
|
||||
{
|
||||
if(ES_UseBarData)
|
||||
{
|
||||
// Bar-basierte Überwachungszeit
|
||||
int bars_since_monitoring = iBarShift(esData.symbol, ES_Timeframe, esData.letzte_überwachung_zeit);
|
||||
int timeout_bars = (int)(ES_ÜberwachungTimeout / PeriodSeconds(ES_Timeframe));
|
||||
|
||||
if(bars_since_monitoring > timeout_bars)
|
||||
{
|
||||
esData.überwachung_aktiv = false;
|
||||
esData.preis_trigger_aktiv = false;
|
||||
esData.steigung_trigger_aktiv = false;
|
||||
Print("Überwachung beendet - Bar-basierte Zeitüberschreitung (", bars_since_monitoring, " Bars)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Tick-basierte Überwachungszeit
|
||||
if(TimeCurrent() - esData.letzte_überwachung_zeit > ES_ÜberwachungTimeout)
|
||||
{
|
||||
esData.überwachung_aktiv = false;
|
||||
esData.preis_trigger_aktiv = false;
|
||||
esData.steigung_trigger_aktiv = false;
|
||||
Print("Überwachung beendet - Tick-basierte Zeitüberschreitung");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--- Trigger-Bedingungen prüfen (Check trigger conditions)
|
||||
PrüfeTrigger();
|
||||
|
||||
//--- Trade Management (Trade management)
|
||||
VerwalteTrades();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,240 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSICrossOverReversalStrategy.mqh |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
void WeekDays_Init()
|
||||
{
|
||||
rcData.WeekDays[0] = RC_Sunday;
|
||||
rcData.WeekDays[1] = RC_Monday;
|
||||
rcData.WeekDays[2] = RC_Tuesday;
|
||||
rcData.WeekDays[3] = RC_Wednesday;
|
||||
rcData.WeekDays[4] = RC_Thursday;
|
||||
rcData.WeekDays[5] = RC_Friday;
|
||||
rcData.WeekDays[6] = RC_Saturday;
|
||||
}
|
||||
|
||||
bool WeekDays_Check(datetime aTime)
|
||||
{
|
||||
MqlDateTime stm;
|
||||
TimeToStruct(aTime, stm);
|
||||
return(rcData.WeekDays[stm.day_of_week]);
|
||||
}
|
||||
|
||||
int TimeHour(datetime when = 0)
|
||||
{
|
||||
if(when == 0) when = TimeCurrent();
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(when, dt);
|
||||
return dt.hour;
|
||||
}
|
||||
|
||||
bool InitRSICrossOverReversal(string symbol)
|
||||
{
|
||||
WeekDays_Init();
|
||||
|
||||
rcData.symbol = symbol;
|
||||
rcData.previousRSIDef = 0;
|
||||
rcData.lastTradeTime = 0;
|
||||
rcData.bartime = 0;
|
||||
rcData.lastBarTime = 0;
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Print("RSICrossOverReversal: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sleep(100); // Wait for symbol to be ready
|
||||
|
||||
rcData.rsiHandle = iRSI(symbol, RC_TimeFrame1, RC_rsiPeriod, PRICE_CLOSE);
|
||||
if(rcData.rsiHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("RSICrossOverReversal: Error creating RSI handle for '", symbol, "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
rcData.emaHandle = iMA(symbol, RC_TimeFrame2, RC_emaPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
if(rcData.emaHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("RSICrossOverReversal: Error creating EMA handle for '", symbol, "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
|
||||
rcData.isInitialized = true;
|
||||
Print("RSICrossOverReversal: Successfully initialized for symbol '", symbol, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeinitRSICrossOverReversal()
|
||||
{
|
||||
if(rcData.rsiHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(rcData.rsiHandle);
|
||||
if(rcData.emaHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(rcData.emaHandle);
|
||||
}
|
||||
|
||||
void Close_Position_MN(ulong magicNumber)
|
||||
{
|
||||
ClosePositionByMagic(rcData.trade, rcData.symbol, (int)magicNumber);
|
||||
}
|
||||
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
if(!PositionSelectByMagic(rcData.symbol, RC_MagicNumber))
|
||||
return;
|
||||
|
||||
ulong PositionTicket = PositionGetInteger(POSITION_TICKET);
|
||||
ENUM_POSITION_TYPE trade_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
string symbol = rcData.symbol;
|
||||
|
||||
double POINT = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
int DIGIT = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
|
||||
|
||||
if(trade_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
double Bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), DIGIT);
|
||||
|
||||
if(Bid - PositionGetDouble(POSITION_PRICE_OPEN) > NormalizeDouble(POINT * RC_TrailingStop, DIGIT))
|
||||
{
|
||||
if(PositionGetDouble(POSITION_SL) < NormalizeDouble(Bid - POINT * RC_TrailingStop, DIGIT))
|
||||
{
|
||||
ModifyPositionByMagic(rcData.trade, symbol, RC_MagicNumber,
|
||||
NormalizeDouble(Bid - POINT * RC_TrailingStop, DIGIT),
|
||||
PositionGetDouble(POSITION_TP));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(trade_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
double Ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), DIGIT);
|
||||
|
||||
if((PositionGetDouble(POSITION_PRICE_OPEN) - Ask) > NormalizeDouble(POINT * RC_TrailingStop, DIGIT))
|
||||
{
|
||||
if((PositionGetDouble(POSITION_SL) > NormalizeDouble(Ask + POINT * RC_TrailingStop, DIGIT)) ||
|
||||
(PositionGetDouble(POSITION_SL) == 0))
|
||||
{
|
||||
ModifyPositionByMagic(rcData.trade, symbol, RC_MagicNumber,
|
||||
NormalizeDouble(Ask + POINT * RC_TrailingStop, DIGIT),
|
||||
PositionGetDouble(POSITION_TP));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessRSICrossOverReversal(string symbol)
|
||||
{
|
||||
// Skip if not initialized (symbol not available)
|
||||
if(!rcData.isInitialized)
|
||||
return;
|
||||
|
||||
rcData.symbol = symbol; // Update symbol in case it changed
|
||||
if(rcData.bartime == iTime(rcData.symbol, RC_BarTimeFrame, 0))
|
||||
return;
|
||||
rcData.bartime = iTime(rcData.symbol, RC_BarTimeFrame, 0);
|
||||
|
||||
double rsi[];
|
||||
if(CopyBuffer(rcData.rsiHandle, 0, 0, 2, rsi) <= 0)
|
||||
return;
|
||||
|
||||
double ema[];
|
||||
if(CopyBuffer(rcData.emaHandle, 0, 0, 2, ema) <= 0)
|
||||
return;
|
||||
|
||||
datetime currentTime = TimeCurrent();
|
||||
int currentHour = TimeHour(TimeCurrent());
|
||||
|
||||
if(!WeekDays_Check(TimeTradeServer()))
|
||||
{
|
||||
Close_Position_MN(RC_MagicNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!((currentHour < RC_tradingHourOneEnd && currentHour > RC_tradingHourOneBegin) ||
|
||||
(currentHour < RC_tradingHourTwoEnd && currentHour > RC_tradingHourTwoBegin)))
|
||||
{
|
||||
Close_Position_MN(RC_MagicNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasPosition = PositionExistsByMagic(rcData.symbol, RC_MagicNumber);
|
||||
|
||||
double currentRSI = rsi[0];
|
||||
double previousRSI = rsi[1];
|
||||
|
||||
if(rcData.previousRSIDef == 0)
|
||||
{
|
||||
rcData.previousRSIDef = currentRSI;
|
||||
return;
|
||||
}
|
||||
|
||||
double currentEMA = ema[0];
|
||||
double previousEMA = ema[1];
|
||||
|
||||
double emaSlope = (currentEMA - previousEMA) * 100;
|
||||
double closeCurr = iClose(Symbol(), Period(), 0);
|
||||
double priceToEmaDistance = (closeCurr - currentEMA) * 10;
|
||||
|
||||
bool isBuyPosition = false;
|
||||
bool isSellPosition = false;
|
||||
if(hasPosition)
|
||||
{
|
||||
if(PositionSelectByMagic(rcData.symbol, RC_MagicNumber))
|
||||
{
|
||||
ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
if(positionType == POSITION_TYPE_BUY)
|
||||
isBuyPosition = true;
|
||||
else if(positionType == POSITION_TYPE_SELL)
|
||||
isSellPosition = true;
|
||||
}
|
||||
}
|
||||
|
||||
ApplyTrailingStop();
|
||||
|
||||
bool cooldownPassed = (currentTime - rcData.lastTradeTime) >= RC_cooldownSeconds;
|
||||
bool isTrendStrong = MathAbs(emaSlope) > RC_emaSlopeThreshold || MathAbs(priceToEmaDistance) > RC_emaDistanceThreshold;
|
||||
|
||||
if(isBuyPosition && currentRSI > RC_exitBuyRSI)
|
||||
{
|
||||
Close_Position_MN(RC_MagicNumber);
|
||||
rcData.lastTradeTime = currentTime;
|
||||
}
|
||||
|
||||
if(isSellPosition && currentRSI < RC_exitSellRSI)
|
||||
{
|
||||
Close_Position_MN(RC_MagicNumber);
|
||||
rcData.lastTradeTime = currentTime;
|
||||
}
|
||||
|
||||
if(isTrendStrong)
|
||||
{
|
||||
Close_Position_MN(RC_MagicNumber);
|
||||
rcData.lastTradeTime = currentTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if(currentRSI < RC_overboughtLevel - RC_entryRSISellSpread && rcData.previousRSIDef >= RC_overboughtLevel &&
|
||||
!isSellPosition && !hasPosition && cooldownPassed)
|
||||
{
|
||||
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
|
||||
if(rcData.trade.Sell(g_RC_LotSize, rcData.symbol, 0.0, 0.0, 0.0, "Sell Order"))
|
||||
{
|
||||
rcData.lastTradeTime = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
if(currentRSI > RC_oversoldLevel + RC_entryRSIBuySpread && rcData.previousRSIDef <= RC_oversoldLevel &&
|
||||
!isBuyPosition && !hasPosition && cooldownPassed)
|
||||
{
|
||||
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
|
||||
if(rcData.trade.Buy(g_RC_LotSize, rcData.symbol, 0.0, 0.0, 0.0, "Buy Order"))
|
||||
{
|
||||
rcData.lastTradeTime = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
rcData.previousRSIDef = currentRSI;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,471 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIMidPointHijackStrategy.mqh |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
bool IsNewBar(string symbol)
|
||||
{
|
||||
datetime time[];
|
||||
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
if(time[0] != rmData.lastBarTime)
|
||||
{
|
||||
rmData.lastBarTime = time[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsWithinTradingHours(int startHour, int endHour)
|
||||
{
|
||||
MqlDateTime currentTime;
|
||||
TimeToStruct(TimeCurrent(), currentTime);
|
||||
|
||||
if(startHour <= endHour)
|
||||
return (currentTime.hour >= startHour && currentTime.hour < endHour);
|
||||
else
|
||||
return (currentTime.hour >= startHour || currentTime.hour < endHour);
|
||||
}
|
||||
|
||||
bool HasPosition(string symbol, int magic)
|
||||
{
|
||||
return PositionExistsByMagic(symbol, magic);
|
||||
}
|
||||
|
||||
bool HasProfitablePosition(int excludeMagic)
|
||||
{
|
||||
bool hasProfitable = false;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(rmData.positionInfo.SelectByIndex(i))
|
||||
{
|
||||
if(rmData.positionInfo.Magic() != excludeMagic)
|
||||
{
|
||||
double profit = rmData.positionInfo.Profit();
|
||||
if(profit > RM_InpLockProfitThreshold * _Point)
|
||||
{
|
||||
hasProfitable = true;
|
||||
if(RM_InpCloseOppositeTrades)
|
||||
{
|
||||
if((excludeMagic == RM_InpMagicNumberRSIFollow && rmData.positionInfo.Magic() == RM_InpMagicNumberRSIReverse) ||
|
||||
(excludeMagic == RM_InpMagicNumberRSIReverse && rmData.positionInfo.Magic() == RM_InpMagicNumberRSIFollow) ||
|
||||
(excludeMagic == RM_InpMagicNumberEMACross && (rmData.positionInfo.Magic() == RM_InpMagicNumberRSIReverse || rmData.positionInfo.Magic() == RM_InpMagicNumberRSIFollow)) ||
|
||||
((excludeMagic == RM_InpMagicNumberRSIFollow || excludeMagic == RM_InpMagicNumberRSIReverse) && rmData.positionInfo.Magic() == RM_InpMagicNumberEMACross))
|
||||
{
|
||||
ClosePosition(rmData.symbol, (int)rmData.positionInfo.Magic());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasProfitable;
|
||||
}
|
||||
|
||||
bool IsRSIReverseInCooldown(string symbol)
|
||||
{
|
||||
if(RM_InpRSIReverseCooldownBars <= 0)
|
||||
return false;
|
||||
|
||||
if(!rmData.rsiReverseInCooldown)
|
||||
return false;
|
||||
|
||||
datetime time[];
|
||||
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
datetime currentBarTime = time[0];
|
||||
datetime cooldownEndTime = rmData.rsiReverseLastCloseTime + RM_InpRSIReverseCooldownBars * PeriodSeconds(RM_InpTimeframe);
|
||||
|
||||
if(currentBarTime >= cooldownEndTime)
|
||||
{
|
||||
rmData.rsiReverseInCooldown = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CheckRSIFollowStrategy(string symbol)
|
||||
{
|
||||
if(!IsWithinTradingHours(RM_InpRSIFollowStartHour, RM_InpRSIFollowEndHour))
|
||||
{
|
||||
if(RM_InpRSIFollowCloseOutsideHours)
|
||||
{
|
||||
if(HasPosition(symbol, RM_InpMagicNumberRSIFollow))
|
||||
ClosePosition(symbol, RM_InpMagicNumberRSIFollow);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberRSIFollow))
|
||||
return;
|
||||
|
||||
if(rmData.lastBarRSI > RM_InpRSIOverbought)
|
||||
rmData.rsiOverbought = true;
|
||||
else if(rmData.lastBarRSI < RM_InpRSIOversold)
|
||||
rmData.rsiOversold = true;
|
||||
|
||||
if(rmData.rsiOverbought && rmData.lastBarRSI < RM_InpRSIExitLevel)
|
||||
{
|
||||
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
|
||||
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
|
||||
}
|
||||
rmData.rsiOverbought = false;
|
||||
}
|
||||
else if(rmData.rsiOversold && rmData.lastBarRSI > RM_InpRSIExitLevel)
|
||||
{
|
||||
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
|
||||
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
|
||||
}
|
||||
rmData.rsiOversold = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckRSIReverseStrategy(string symbol)
|
||||
{
|
||||
if(!IsWithinTradingHours(RM_InpRSIReverseStartHour, RM_InpRSIReverseEndHour))
|
||||
{
|
||||
if(RM_InpRSIReverseCloseOutsideHours)
|
||||
{
|
||||
if(HasPosition(symbol, RM_InpMagicNumberRSIReverse))
|
||||
ClosePosition(symbol, RM_InpMagicNumberRSIReverse);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberRSIReverse))
|
||||
return;
|
||||
|
||||
if(IsRSIReverseInCooldown(symbol))
|
||||
return;
|
||||
|
||||
if(rmData.lastBarRSIReverse > RM_InpRSIReverseOverbought)
|
||||
rmData.rsiReverseOverbought = true;
|
||||
else if(rmData.lastBarRSIReverse < RM_InpRSIReverseOversold)
|
||||
rmData.rsiReverseOversold = true;
|
||||
|
||||
if(rmData.rsiReverseOverbought && rmData.lastBarRSIReverse < RM_InpRSIReverseCrossLevel)
|
||||
{
|
||||
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
|
||||
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
|
||||
}
|
||||
rmData.rsiReverseOverbought = false;
|
||||
}
|
||||
else if(rmData.rsiReverseOversold && rmData.lastBarRSIReverse > RM_InpRSIReverseCrossLevel)
|
||||
{
|
||||
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
|
||||
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
|
||||
}
|
||||
rmData.rsiReverseOversold = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckEMACrossStrategy(string symbol)
|
||||
{
|
||||
if(!IsWithinTradingHours(RM_InpEMACrossStartHour, RM_InpEMACrossEndHour))
|
||||
{
|
||||
if(RM_InpEMACrossCloseOutsideHours)
|
||||
{
|
||||
if(HasPosition(symbol, RM_InpMagicNumberEMACross))
|
||||
ClosePosition(symbol, RM_InpMagicNumberEMACross);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberEMACross))
|
||||
return;
|
||||
|
||||
if(rmData.lastBarEMAPrev < rmData.lastBarClosePrev && rmData.lastBarEMA > rmData.lastBarClose)
|
||||
{
|
||||
rmData.emaCrossBuySignal = true;
|
||||
rmData.emaCrossSellSignal = false;
|
||||
rmData.emaCrossSignalBar = 0;
|
||||
}
|
||||
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
|
||||
{
|
||||
rmData.emaCrossSellSignal = true;
|
||||
rmData.emaCrossBuySignal = false;
|
||||
rmData.emaCrossSignalBar = 0;
|
||||
}
|
||||
|
||||
if(RM_InpUseEMADistanceEntry)
|
||||
{
|
||||
if(rmData.emaCrossBuySignal)
|
||||
{
|
||||
bool distanceConditionMet = true;
|
||||
double emaHistory[], closeHistory[];
|
||||
ArraySetAsSeries(emaHistory, true);
|
||||
ArraySetAsSeries(closeHistory, true);
|
||||
|
||||
if(CopyBuffer(rmData.emaHandle, 0, 0, RM_InpEMADistancePeriod, emaHistory) > 0 &&
|
||||
CopyClose(symbol, RM_InpTimeframe, 0, RM_InpEMADistancePeriod, closeHistory) > 0)
|
||||
{
|
||||
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
for(int i = 0; i < RM_InpEMADistancePeriod; i++)
|
||||
{
|
||||
double distance = (closeHistory[i] - emaHistory[i]) / point;
|
||||
if(distance < RM_InpEMADistancePips)
|
||||
{
|
||||
distanceConditionMet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
|
||||
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
|
||||
rmData.emaCrossBuySignal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(rmData.emaCrossSellSignal)
|
||||
{
|
||||
bool distanceConditionMet = true;
|
||||
double emaHistory[], closeHistory[];
|
||||
ArraySetAsSeries(emaHistory, true);
|
||||
ArraySetAsSeries(closeHistory, true);
|
||||
|
||||
if(CopyBuffer(rmData.emaHandle, 0, 0, RM_InpEMADistancePeriod, emaHistory) > 0 &&
|
||||
CopyClose(symbol, RM_InpTimeframe, 0, RM_InpEMADistancePeriod, closeHistory) > 0)
|
||||
{
|
||||
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
for(int i = 0; i < RM_InpEMADistancePeriod; i++)
|
||||
{
|
||||
double distance = (emaHistory[i] - closeHistory[i]) / point;
|
||||
if(distance < RM_InpEMADistancePips)
|
||||
{
|
||||
distanceConditionMet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
|
||||
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
|
||||
rmData.emaCrossSellSignal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(rmData.lastBarEMAPrev < rmData.lastBarClosePrev && rmData.lastBarEMA > rmData.lastBarClose)
|
||||
{
|
||||
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
|
||||
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
|
||||
}
|
||||
}
|
||||
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
|
||||
{
|
||||
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
|
||||
{
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
|
||||
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(rmData.emaCrossBuySignal || rmData.emaCrossSellSignal)
|
||||
{
|
||||
rmData.emaCrossSignalBar++;
|
||||
if(rmData.emaCrossSignalBar > RM_InpEMADistancePeriod * 2)
|
||||
{
|
||||
rmData.emaCrossBuySignal = false;
|
||||
rmData.emaCrossSellSignal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckExitConditions(string symbol)
|
||||
{
|
||||
if(RM_InpEnableRSIFollow)
|
||||
{
|
||||
if(HasPosition(symbol, RM_InpMagicNumberRSIFollow))
|
||||
{
|
||||
if(PositionSelectByMagic(symbol, RM_InpMagicNumberRSIFollow))
|
||||
{
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
if((posType == POSITION_TYPE_BUY && rmData.lastBarRSI < RM_InpRSIExitLevel) ||
|
||||
(posType == POSITION_TYPE_SELL && rmData.lastBarRSI > RM_InpRSIExitLevel))
|
||||
{
|
||||
ClosePosition(symbol, RM_InpMagicNumberRSIFollow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(RM_InpEnableRSIReverse)
|
||||
{
|
||||
if(HasPosition(symbol, RM_InpMagicNumberRSIReverse))
|
||||
{
|
||||
if(PositionSelectByMagic(symbol, RM_InpMagicNumberRSIReverse))
|
||||
{
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
if((posType == POSITION_TYPE_BUY && rmData.lastBarRSIReverse < RM_InpRSIReverseExitLevel) ||
|
||||
(posType == POSITION_TYPE_SELL && rmData.lastBarRSIReverse > RM_InpRSIReverseExitLevel))
|
||||
{
|
||||
ClosePosition(symbol, RM_InpMagicNumberRSIReverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(RM_InpEnableEMACross)
|
||||
{
|
||||
if(HasPosition(symbol, RM_InpMagicNumberEMACross))
|
||||
{
|
||||
if(PositionSelectByMagic(symbol, RM_InpMagicNumberEMACross))
|
||||
{
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
if((posType == POSITION_TYPE_BUY && rmData.lastBarEMA > rmData.lastBarClose) ||
|
||||
(posType == POSITION_TYPE_SELL && rmData.lastBarEMA < rmData.lastBarClose))
|
||||
{
|
||||
ClosePosition(symbol, RM_InpMagicNumberEMACross);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClosePosition(string symbol, int magic)
|
||||
{
|
||||
if(!PositionExistsByMagic(symbol, magic))
|
||||
return;
|
||||
|
||||
ulong ticket = GetPositionTicketByMagic(symbol, magic);
|
||||
if(ticket == 0)
|
||||
return;
|
||||
|
||||
if(magic == RM_InpMagicNumberRSIReverse)
|
||||
{
|
||||
if(PositionSelectByTicketSymbolAndMagic(ticket, symbol, magic))
|
||||
{
|
||||
datetime time[];
|
||||
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
rmData.rsiReverseLastCloseTime = time[0];
|
||||
double profit = PositionGetDouble(POSITION_PROFIT);
|
||||
if(!RM_InpRSIReverseCooldownOnLoss || profit < 0)
|
||||
{
|
||||
rmData.rsiReverseInCooldown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClosePositionByMagic(rmData.trade, symbol, magic);
|
||||
}
|
||||
|
||||
bool InitRSIMidPointHijack(string symbol)
|
||||
{
|
||||
rmData.symbol = symbol;
|
||||
rmData.rsiOverbought = false;
|
||||
rmData.rsiOversold = false;
|
||||
rmData.rsiReverseOverbought = false;
|
||||
rmData.rsiReverseOversold = false;
|
||||
rmData.emaCrossBuySignal = false;
|
||||
rmData.emaCrossSellSignal = false;
|
||||
rmData.emaCrossSignalBar = 0;
|
||||
rmData.rsiReverseInCooldown = false;
|
||||
rmData.lastBarRSI = 0;
|
||||
rmData.lastBarRSIReverse = 0;
|
||||
rmData.lastBarEMA = 0;
|
||||
rmData.lastBarClose = 0;
|
||||
rmData.lastBarEMAPrev = 0;
|
||||
rmData.lastBarClosePrev = 0;
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Print("RSIMidPointHijack: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sleep(100); // Wait for symbol to be ready
|
||||
|
||||
rmData.rsiHandle = iRSI(symbol, RM_InpTimeframe, RM_InpRSIPeriod, PRICE_CLOSE);
|
||||
rmData.rsiReverseHandle = iRSI(symbol, RM_InpTimeframe, RM_InpRSIReversePeriod, PRICE_CLOSE);
|
||||
rmData.emaHandle = iMA(symbol, RM_InpTimeframe, RM_InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
|
||||
if(rmData.rsiHandle == INVALID_HANDLE || rmData.rsiReverseHandle == INVALID_HANDLE || rmData.emaHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("RSIMidPointHijack: Error creating indicators for '", symbol, "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
|
||||
rmData.trade.SetMarginMode();
|
||||
rmData.trade.SetTypeFillingBySymbol(symbol);
|
||||
rmData.trade.SetDeviationInPoints(10);
|
||||
|
||||
datetime time[];
|
||||
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
|
||||
rmData.lastBarTime = time[0];
|
||||
|
||||
rmData.isInitialized = true;
|
||||
Print("RSIMidPointHijack: Successfully initialized for symbol '", symbol, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeinitRSIMidPointHijack()
|
||||
{
|
||||
if(rmData.rsiHandle != INVALID_HANDLE) IndicatorRelease(rmData.rsiHandle);
|
||||
if(rmData.rsiReverseHandle != INVALID_HANDLE) IndicatorRelease(rmData.rsiReverseHandle);
|
||||
if(rmData.emaHandle != INVALID_HANDLE) IndicatorRelease(rmData.emaHandle);
|
||||
}
|
||||
|
||||
void ProcessRSIMidPointHijack(string symbol)
|
||||
{
|
||||
// Skip if not initialized (symbol not available)
|
||||
if(!rmData.isInitialized)
|
||||
return;
|
||||
|
||||
rmData.symbol = symbol; // Update symbol in case it changed
|
||||
if(!IsNewBar(rmData.symbol))
|
||||
return;
|
||||
|
||||
double rsi[], rsiReverse[], ema[], close[];
|
||||
ArraySetAsSeries(rsi, true);
|
||||
ArraySetAsSeries(rsiReverse, true);
|
||||
ArraySetAsSeries(ema, true);
|
||||
ArraySetAsSeries(close, true);
|
||||
|
||||
rmData.lastBarEMAPrev = rmData.lastBarEMA;
|
||||
rmData.lastBarClosePrev = rmData.lastBarClose;
|
||||
|
||||
if(CopyBuffer(rmData.rsiHandle, 0, 0, 1, rsi) > 0)
|
||||
rmData.lastBarRSI = rsi[0];
|
||||
|
||||
if(CopyBuffer(rmData.rsiReverseHandle, 0, 0, 1, rsiReverse) > 0)
|
||||
rmData.lastBarRSIReverse = rsiReverse[0];
|
||||
|
||||
if(CopyBuffer(rmData.emaHandle, 0, 0, 1, ema) > 0)
|
||||
rmData.lastBarEMA = ema[0];
|
||||
|
||||
if(CopyClose(rmData.symbol, RM_InpTimeframe, 0, 1, close) > 0)
|
||||
rmData.lastBarClose = close[0];
|
||||
|
||||
if(RM_InpEnableRSIFollow)
|
||||
CheckRSIFollowStrategy(rmData.symbol);
|
||||
if(RM_InpEnableRSIReverse)
|
||||
CheckRSIReverseStrategy(rmData.symbol);
|
||||
if(RM_InpEnableEMACross)
|
||||
CheckEMACrossStrategy(rmData.symbol);
|
||||
|
||||
CheckExitConditions(rmData.symbol);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,493 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIReversalAsianStrategy.mqh |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSI Reversal Asian Strategy Data Structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSIReversalAsianData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
CTrade trade;
|
||||
bool isPositionOpen;
|
||||
double positionOpenPrice;
|
||||
datetime positionOpenTime;
|
||||
ENUM_POSITION_TYPE lastPositionType;
|
||||
bool sessionCloseAttempted;
|
||||
|
||||
// RSI crossover variables
|
||||
double rsiCurrent;
|
||||
double rsiPrevious;
|
||||
double rsiPrevious2;
|
||||
bool rsiCrossedOverbought;
|
||||
bool rsiCrossedOversold;
|
||||
bool rsiCrossedExitLevel;
|
||||
|
||||
// Strategy parameters
|
||||
int RSIPeriod;
|
||||
double OverboughtLevel;
|
||||
double OversoldLevel;
|
||||
int TakeProfitPips;
|
||||
int StopLossPips;
|
||||
double MaxLotSize;
|
||||
int MaxSpread;
|
||||
int MaxDuration;
|
||||
bool UseStopLoss;
|
||||
bool UseTakeProfit;
|
||||
bool UseRSIExit;
|
||||
double RSIExitLevel;
|
||||
bool CloseOutsideSession;
|
||||
ENUM_TIMEFRAMES TimeFrame;
|
||||
int MagicNumber;
|
||||
int Slippage;
|
||||
double point;
|
||||
};
|
||||
|
||||
// Session times (UTC)
|
||||
const int AsianSessionStart = 0; // 00:00 UTC
|
||||
const int AsianSessionEnd = 8; // 08:00 UTC
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if current time is in Asian session |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsAsianSession()
|
||||
{
|
||||
datetime currentTime = TimeCurrent();
|
||||
MqlDateTime timeStruct;
|
||||
TimeToStruct(currentTime, timeStruct);
|
||||
|
||||
return (timeStruct.hour >= AsianSessionStart && timeStruct.hour < AsianSessionEnd);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if trading is allowed for symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsTradingAllowed(RSIReversalAsianData& data)
|
||||
{
|
||||
// Check if market is open
|
||||
long tradeMode = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_MODE);
|
||||
if(tradeMode != SYMBOL_TRADE_MODE_FULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we have enough money
|
||||
if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check RSI crossover conditions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckRSICrossover(RSIReversalAsianData& data)
|
||||
{
|
||||
// Reset crossover flags
|
||||
data.rsiCrossedOverbought = false;
|
||||
data.rsiCrossedOversold = false;
|
||||
data.rsiCrossedExitLevel = false;
|
||||
|
||||
// Check for overbought crossover (RSI crosses above overbought level)
|
||||
if(data.rsiPrevious < data.OverboughtLevel && data.rsiCurrent >= data.OverboughtLevel)
|
||||
{
|
||||
data.rsiCrossedOverbought = true;
|
||||
}
|
||||
|
||||
// Check for oversold crossover (RSI crosses below oversold level)
|
||||
if(data.rsiPrevious > data.OversoldLevel && data.rsiCurrent <= data.OversoldLevel)
|
||||
{
|
||||
data.rsiCrossedOversold = true;
|
||||
}
|
||||
|
||||
// Check for exit level crossover
|
||||
if(data.rsiPrevious < data.RSIExitLevel && data.rsiCurrent >= data.RSIExitLevel)
|
||||
{
|
||||
data.rsiCrossedExitLevel = true;
|
||||
}
|
||||
else if(data.rsiPrevious > data.RSIExitLevel && data.rsiCurrent <= data.RSIExitLevel)
|
||||
{
|
||||
data.rsiCrossedExitLevel = true;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close all trades for the symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CloseAllTrades(RSIReversalAsianData& data, string reason = "")
|
||||
{
|
||||
bool allClosed = true;
|
||||
int totalPositions = PositionsTotal();
|
||||
|
||||
if(totalPositions == 0)
|
||||
return true;
|
||||
|
||||
for(int i = totalPositions - 1; i >= 0; i--)
|
||||
{
|
||||
if(PositionGetSymbol(i) == data.symbol)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket > 0 && PositionSelectByTicket(ticket))
|
||||
{
|
||||
if(PositionGetInteger(POSITION_MAGIC) == (ulong)data.MagicNumber)
|
||||
{
|
||||
// Try to close position with retry logic
|
||||
int retryCount = 0;
|
||||
bool positionClosed = false;
|
||||
|
||||
while(retryCount < 3 && !positionClosed)
|
||||
{
|
||||
if(data.trade.PositionClose(ticket))
|
||||
{
|
||||
data.isPositionOpen = false;
|
||||
positionClosed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int error = GetLastError();
|
||||
|
||||
// If error is 4756 (Trade disabled), wait longer before retry
|
||||
if(error == 4756)
|
||||
{
|
||||
Sleep(5000); // Wait 5 seconds before retry
|
||||
retryCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For other errors, break the loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!positionClosed)
|
||||
{
|
||||
allClosed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allClosed;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Initialize RSI Reversal Asian Strategy |
|
||||
//+------------------------------------------------------------------+
|
||||
bool InitRSIReversalAsian(RSIReversalAsianData& data, string symbol,
|
||||
int RSIPeriod, double OverboughtLevel, double OversoldLevel,
|
||||
int TakeProfitPips, int StopLossPips, double MaxLotSize,
|
||||
int MaxSpread, int MaxDuration, bool UseStopLoss,
|
||||
bool UseTakeProfit, bool UseRSIExit, double RSIExitLevel,
|
||||
bool CloseOutsideSession, ENUM_TIMEFRAMES TimeFrame,
|
||||
int MagicNumber, int Slippage)
|
||||
{
|
||||
data.symbol = symbol;
|
||||
data.isInitialized = false;
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Print("RSIReversalAsian: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait a bit for symbol to be ready
|
||||
Sleep(100);
|
||||
|
||||
// Get symbol point
|
||||
data.point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
||||
|
||||
// Store parameters
|
||||
data.RSIPeriod = RSIPeriod;
|
||||
data.OverboughtLevel = OverboughtLevel;
|
||||
data.OversoldLevel = OversoldLevel;
|
||||
data.TakeProfitPips = TakeProfitPips;
|
||||
data.StopLossPips = StopLossPips;
|
||||
data.MaxLotSize = MaxLotSize;
|
||||
data.MaxSpread = MaxSpread;
|
||||
data.MaxDuration = MaxDuration;
|
||||
data.UseStopLoss = UseStopLoss;
|
||||
data.UseTakeProfit = UseTakeProfit;
|
||||
data.UseRSIExit = UseRSIExit;
|
||||
data.RSIExitLevel = RSIExitLevel;
|
||||
data.CloseOutsideSession = CloseOutsideSession;
|
||||
data.TimeFrame = TimeFrame;
|
||||
data.MagicNumber = MagicNumber;
|
||||
data.Slippage = Slippage;
|
||||
|
||||
// Initialize RSI indicator with retry logic (for insufficient history in backtesting)
|
||||
data.rsiHandle = INVALID_HANDLE;
|
||||
int retryCount = 0;
|
||||
int maxRetries = 5;
|
||||
|
||||
while(retryCount < maxRetries && data.rsiHandle == INVALID_HANDLE)
|
||||
{
|
||||
data.rsiHandle = iRSI(symbol, TimeFrame, RSIPeriod, PRICE_CLOSE);
|
||||
|
||||
if(data.rsiHandle == INVALID_HANDLE)
|
||||
{
|
||||
int error = GetLastError();
|
||||
|
||||
// Error 4805 = insufficient history - wait longer and retry
|
||||
if(error == 4805 && retryCount < maxRetries - 1)
|
||||
{
|
||||
Sleep(1000); // Wait 1 second for history to load
|
||||
retryCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Print("RSIReversalAsian: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", error == 4805 ? "Insufficient history data" : "Unknown", ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(data.rsiHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("RSIReversalAsian: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait a bit for the indicator to be ready
|
||||
Sleep(100);
|
||||
|
||||
// Initialize RSI values with retry logic
|
||||
double rsi[];
|
||||
ArraySetAsSeries(rsi, true);
|
||||
|
||||
retryCount = 0;
|
||||
bool rsiInitialized = false;
|
||||
|
||||
while(retryCount < 10 && !rsiInitialized)
|
||||
{
|
||||
int copied = CopyBuffer(data.rsiHandle, 0, 0, 3, rsi);
|
||||
if(copied >= 3)
|
||||
{
|
||||
data.rsiCurrent = rsi[0];
|
||||
data.rsiPrevious = rsi[1];
|
||||
data.rsiPrevious2 = rsi[2];
|
||||
rsiInitialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
retryCount++;
|
||||
Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
if(!rsiInitialized)
|
||||
{
|
||||
// Don't fail initialization, just set default values
|
||||
data.rsiCurrent = 50.0;
|
||||
data.rsiPrevious = 50.0;
|
||||
data.rsiPrevious2 = 50.0;
|
||||
}
|
||||
|
||||
// Set trade parameters
|
||||
data.trade.SetExpertMagicNumber(MagicNumber);
|
||||
data.trade.SetDeviationInPoints(Slippage);
|
||||
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
|
||||
// Initialize state
|
||||
data.isPositionOpen = false;
|
||||
data.positionOpenPrice = 0;
|
||||
data.positionOpenTime = 0;
|
||||
data.lastPositionType = POSITION_TYPE_BUY;
|
||||
data.sessionCloseAttempted = false;
|
||||
data.rsiCrossedOverbought = false;
|
||||
data.rsiCrossedOversold = false;
|
||||
data.rsiCrossedExitLevel = false;
|
||||
|
||||
data.isInitialized = true;
|
||||
|
||||
Print("RSIReversalAsian: Successfully initialized for symbol '", symbol, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Deinitialize RSI Reversal Asian Strategy |
|
||||
//+------------------------------------------------------------------+
|
||||
void DeinitRSIReversalAsian(RSIReversalAsianData& data)
|
||||
{
|
||||
if(data.rsiHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(data.rsiHandle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Process RSI Reversal Asian Strategy |
|
||||
//+------------------------------------------------------------------+
|
||||
void ProcessRSIReversalAsian(RSIReversalAsianData& data, double lotSize)
|
||||
{
|
||||
if(!data.isInitialized)
|
||||
return;
|
||||
|
||||
// Check if trading is allowed
|
||||
if(!IsTradingAllowed(data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we're in Asian session
|
||||
if(!IsAsianSession())
|
||||
{
|
||||
// Close all positions if outside Asian session and CloseOutsideSession is true
|
||||
if(data.CloseOutsideSession && !data.sessionCloseAttempted)
|
||||
{
|
||||
CloseAllTrades(data, "Outside Asian session");
|
||||
data.sessionCloseAttempted = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset the session close attempt flag when we enter Asian session
|
||||
data.sessionCloseAttempted = false;
|
||||
}
|
||||
|
||||
// Get current spread
|
||||
double spread = SymbolInfoDouble(data.symbol, SYMBOL_ASK) - SymbolInfoDouble(data.symbol, SYMBOL_BID);
|
||||
int spreadInPips = (int)(spread / data.point);
|
||||
|
||||
// Check if spread is too high
|
||||
if(spreadInPips > data.MaxSpread)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get RSI values from bar data
|
||||
double rsi[];
|
||||
ArraySetAsSeries(rsi, true);
|
||||
|
||||
int copied = CopyBuffer(data.rsiHandle, 0, 0, 3, rsi);
|
||||
if(copied < 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update RSI values
|
||||
data.rsiPrevious2 = data.rsiPrevious;
|
||||
data.rsiPrevious = data.rsiCurrent;
|
||||
data.rsiCurrent = rsi[0];
|
||||
|
||||
// Validate RSI values
|
||||
if(data.rsiCurrent == 0 || data.rsiPrevious == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for RSI crossovers
|
||||
CheckRSICrossover(data);
|
||||
|
||||
// Get current prices
|
||||
double currentBid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
|
||||
double currentAsk = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
|
||||
|
||||
// Check for open position
|
||||
bool hasOpenPosition = PositionExistsByMagic(data.symbol, (ulong)data.MagicNumber);
|
||||
|
||||
if(hasOpenPosition)
|
||||
{
|
||||
// Get position details
|
||||
ulong ticket = GetPositionTicketByMagic(data.symbol, (ulong)data.MagicNumber);
|
||||
if(ticket > 0 && PositionSelectByTicket(ticket))
|
||||
{
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
|
||||
|
||||
// Check for RSI exit if enabled
|
||||
if(data.UseRSIExit && data.rsiCrossedExitLevel)
|
||||
{
|
||||
bool shouldExit = false;
|
||||
|
||||
// For long positions, exit when RSI crosses above exit level
|
||||
if(posType == POSITION_TYPE_BUY && data.rsiCurrent >= data.RSIExitLevel && data.rsiPrevious < data.RSIExitLevel)
|
||||
{
|
||||
shouldExit = true;
|
||||
}
|
||||
// For short positions, exit when RSI crosses below exit level
|
||||
else if(posType == POSITION_TYPE_SELL && data.rsiCurrent <= data.RSIExitLevel && data.rsiPrevious > data.RSIExitLevel)
|
||||
{
|
||||
shouldExit = true;
|
||||
}
|
||||
|
||||
if(shouldExit)
|
||||
{
|
||||
CloseAllTrades(data, "RSI Exit Crossover");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for timeout
|
||||
if(TimeCurrent() - openTime > data.MaxDuration * 3600)
|
||||
{
|
||||
CloseAllTrades(data, "Timeout");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no position is open, look for entry signals based on RSI crossover
|
||||
if(!hasOpenPosition)
|
||||
{
|
||||
// Place buy order if RSI crosses below oversold level (oversold crossover)
|
||||
if(data.rsiCrossedOversold)
|
||||
{
|
||||
double sl = data.UseStopLoss ? currentBid - data.StopLossPips * data.point : 0;
|
||||
double tp = data.UseTakeProfit ? currentBid + data.TakeProfitPips * data.point : 0;
|
||||
|
||||
if(data.UseStopLoss && sl >= currentBid)
|
||||
return;
|
||||
if(data.UseTakeProfit && tp <= currentBid)
|
||||
return;
|
||||
|
||||
// Set trade parameters
|
||||
data.trade.SetDeviationInPoints(data.Slippage);
|
||||
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
data.trade.SetExpertMagicNumber(data.MagicNumber);
|
||||
|
||||
// Use dynamic lot size
|
||||
double tradeLotSize = lotSize > 0 ? lotSize : data.MaxLotSize;
|
||||
|
||||
// Place buy order using CTrade
|
||||
if(data.trade.Buy(tradeLotSize, data.symbol, currentAsk, sl, tp, "RSI Oversold Crossover Buy"))
|
||||
{
|
||||
data.isPositionOpen = true;
|
||||
data.positionOpenPrice = currentAsk;
|
||||
data.positionOpenTime = TimeCurrent();
|
||||
data.lastPositionType = POSITION_TYPE_BUY;
|
||||
}
|
||||
}
|
||||
// Place sell order if RSI crosses above overbought level (overbought crossover)
|
||||
else if(data.rsiCrossedOverbought)
|
||||
{
|
||||
double sl = data.UseStopLoss ? currentAsk + data.StopLossPips * data.point : 0;
|
||||
double tp = data.UseTakeProfit ? currentAsk - data.TakeProfitPips * data.point : 0;
|
||||
|
||||
if(data.UseStopLoss && sl <= currentAsk)
|
||||
return;
|
||||
if(data.UseTakeProfit && tp >= currentAsk)
|
||||
return;
|
||||
|
||||
// Set trade parameters
|
||||
data.trade.SetDeviationInPoints(data.Slippage);
|
||||
data.trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
data.trade.SetExpertMagicNumber(data.MagicNumber);
|
||||
|
||||
// Use dynamic lot size
|
||||
double tradeLotSize = lotSize > 0 ? lotSize : data.MaxLotSize;
|
||||
|
||||
// Place sell order using CTrade
|
||||
if(data.trade.Sell(tradeLotSize, data.symbol, currentBid, sl, tp, "RSI Overbought Crossover Sell"))
|
||||
{
|
||||
data.isPositionOpen = true;
|
||||
data.positionOpenPrice = currentBid;
|
||||
data.positionOpenTime = TimeCurrent();
|
||||
data.lastPositionType = POSITION_TYPE_SELL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIScalpingStrategy.mqh |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSI Scalping Strategy Data Structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSIScalpingData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
CTrade trade;
|
||||
int rsi_handle;
|
||||
double rsi_buffer[];
|
||||
double rsi_prev;
|
||||
double rsi_current;
|
||||
double rsi_two_bars_ago;
|
||||
bool position_open;
|
||||
ulong position_ticket;
|
||||
ENUM_POSITION_TYPE current_position_type;
|
||||
datetime last_bar_time;
|
||||
bool rsi_against_position;
|
||||
int bars_against_count;
|
||||
};
|
||||
|
||||
string ErrorDescription(int errorCode)
|
||||
{
|
||||
switch(errorCode)
|
||||
{
|
||||
case 4801: return "Symbol not found";
|
||||
case 4802: return "Symbol not selected";
|
||||
case 4803: return "Symbol not visible";
|
||||
case 4804: return "Symbol not available";
|
||||
case 4805: return "Cannot load indicator - insufficient history data";
|
||||
default: return "Unknown error " + IntegerToString(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
bool InitRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
|
||||
ENUM_APPLIED_PRICE RSI_Applied_Price, int MagicNumber, int Slippage)
|
||||
{
|
||||
data.symbol = symbol;
|
||||
data.isInitialized = false;
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolSelect(symbol, true))
|
||||
{
|
||||
Print("RSIScalping: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
||||
return false; // Return false but don't fail entire EA
|
||||
}
|
||||
|
||||
// Wait a bit for symbol to be ready
|
||||
Sleep(100);
|
||||
|
||||
// Try to create RSI indicator with retry logic (for insufficient history in backtesting)
|
||||
data.rsi_handle = INVALID_HANDLE;
|
||||
int retryCount = 0;
|
||||
int maxRetries = 5;
|
||||
|
||||
while(retryCount < maxRetries && data.rsi_handle == INVALID_HANDLE)
|
||||
{
|
||||
data.rsi_handle = iRSI(symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
|
||||
|
||||
if(data.rsi_handle == INVALID_HANDLE)
|
||||
{
|
||||
int error = GetLastError();
|
||||
|
||||
// Error 4805 = insufficient history - wait longer and retry
|
||||
if(error == 4805 && retryCount < maxRetries - 1)
|
||||
{
|
||||
Sleep(1000); // Wait 1 second for history to load
|
||||
retryCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Print("RSIScalping: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", ErrorDescription(error), ")");
|
||||
return false; // Return false but don't fail entire EA
|
||||
}
|
||||
}
|
||||
|
||||
if(data.rsi_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("RSIScalping: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries");
|
||||
return false;
|
||||
}
|
||||
|
||||
data.trade.SetExpertMagicNumber(MagicNumber);
|
||||
data.trade.SetDeviationInPoints(Slippage);
|
||||
data.trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
ArraySetAsSeries(data.rsi_buffer, true);
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
data.isInitialized = true;
|
||||
|
||||
Print("RSIScalping: Successfully initialized for symbol '", symbol, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeinitRSIScalping(RSIScalpingData& data)
|
||||
{
|
||||
if(data.rsi_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(data.rsi_handle);
|
||||
}
|
||||
|
||||
bool UpdateRSI(RSIScalpingData& data)
|
||||
{
|
||||
if(CopyBuffer(data.rsi_handle, 0, 0, 3, data.rsi_buffer) < 3)
|
||||
return false;
|
||||
|
||||
data.rsi_current = data.rsi_buffer[0];
|
||||
data.rsi_prev = data.rsi_buffer[1];
|
||||
data.rsi_two_bars_ago = data.rsi_buffer[2];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CheckExistingPosition(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
|
||||
double RSI_Oversold, double RSI_Overbought, double RSI_Target_Buy,
|
||||
double RSI_Target_Sell, int BarsToWait)
|
||||
{
|
||||
// Always check if position exists, even if tracking says it doesn't
|
||||
bool positionExists = PositionExistsByMagic(data.symbol, MagicNumber);
|
||||
|
||||
if(!positionExists && data.position_open)
|
||||
{
|
||||
// Position was closed externally, reset tracking
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!positionExists)
|
||||
return;
|
||||
|
||||
// Update tracking if we have a position but tracking was lost
|
||||
if(!data.position_open && positionExists)
|
||||
{
|
||||
ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
|
||||
if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
|
||||
{
|
||||
data.position_ticket = ticket;
|
||||
data.position_open = true;
|
||||
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify our tracked position still exists
|
||||
if(data.position_open && data.position_ticket > 0)
|
||||
{
|
||||
if(!PositionSelectByTicketSymbolAndMagic(data.position_ticket, data.symbol, MagicNumber))
|
||||
{
|
||||
// Try to find the position again
|
||||
ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
|
||||
if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
|
||||
{
|
||||
data.position_ticket = ticket;
|
||||
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Position doesn't exist, reset tracking
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update position type in case it changed (shouldn't happen, but be safe)
|
||||
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
if(data.current_position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(data.rsi_current < RSI_Oversold)
|
||||
{
|
||||
if(!data.rsi_against_position)
|
||||
{
|
||||
data.rsi_against_position = true;
|
||||
data.bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.bars_against_count++;
|
||||
}
|
||||
|
||||
if(data.bars_against_count >= BarsToWait)
|
||||
{
|
||||
ClosePosition(data, MagicNumber);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(data.rsi_against_position)
|
||||
{
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
}
|
||||
|
||||
if(data.rsi_current >= RSI_Target_Buy)
|
||||
{
|
||||
ClosePosition(data, MagicNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(data.current_position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
if(data.rsi_current > RSI_Overbought)
|
||||
{
|
||||
if(!data.rsi_against_position)
|
||||
{
|
||||
data.rsi_against_position = true;
|
||||
data.bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.bars_against_count++;
|
||||
}
|
||||
|
||||
if(data.bars_against_count >= BarsToWait)
|
||||
{
|
||||
ClosePosition(data, MagicNumber);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(data.rsi_against_position)
|
||||
{
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
}
|
||||
|
||||
if(data.rsi_current <= RSI_Target_Sell)
|
||||
{
|
||||
ClosePosition(data, MagicNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckEntrySignals(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
|
||||
double RSI_Oversold, double RSI_Overbought, double LotSize)
|
||||
{
|
||||
if(data.rsi_two_bars_ago <= RSI_Oversold && data.rsi_prev > RSI_Oversold)
|
||||
{
|
||||
OpenBuyPosition(data, MagicNumber, LotSize);
|
||||
}
|
||||
|
||||
if(data.rsi_two_bars_ago >= RSI_Overbought && data.rsi_prev < RSI_Overbought)
|
||||
{
|
||||
OpenSellPosition(data, MagicNumber, LotSize);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Normalize Lot Size According to Symbol Properties |
|
||||
//+------------------------------------------------------------------+
|
||||
double NormalizeLotSize(string symbol, double lotSize)
|
||||
{
|
||||
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
||||
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
||||
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
||||
|
||||
// Round to lot step
|
||||
if(lotStep > 0)
|
||||
lotSize = MathFloor(lotSize / lotStep) * lotStep;
|
||||
|
||||
// Apply min/max constraints
|
||||
if(lotSize < minLot)
|
||||
lotSize = minLot;
|
||||
if(lotSize > maxLot)
|
||||
lotSize = maxLot;
|
||||
|
||||
return lotSize;
|
||||
}
|
||||
|
||||
void OpenBuyPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
|
||||
{
|
||||
if(PositionExistsByMagic(data.symbol, MagicNumber))
|
||||
return;
|
||||
|
||||
// Normalize lot size according to symbol properties
|
||||
double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
|
||||
|
||||
double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
|
||||
|
||||
if(data.trade.Buy(normalizedLot, data.symbol, ask, 0, 0, "RSI Scalping Buy"))
|
||||
{
|
||||
ulong new_ticket = data.trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
|
||||
{
|
||||
data.position_ticket = new_ticket;
|
||||
data.position_open = true;
|
||||
data.current_position_type = POSITION_TYPE_BUY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenSellPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
|
||||
{
|
||||
if(PositionExistsByMagic(data.symbol, MagicNumber))
|
||||
return;
|
||||
|
||||
// Normalize lot size according to symbol properties
|
||||
double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
|
||||
|
||||
double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
|
||||
|
||||
if(data.trade.Sell(normalizedLot, data.symbol, bid, 0, 0, "RSI Scalping Sell"))
|
||||
{
|
||||
ulong new_ticket = data.trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
|
||||
{
|
||||
data.position_ticket = new_ticket;
|
||||
data.position_open = true;
|
||||
data.current_position_type = POSITION_TYPE_SELL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClosePosition(RSIScalpingData& data, int MagicNumber)
|
||||
{
|
||||
// First verify position still exists
|
||||
if(!PositionExistsByMagic(data.symbol, MagicNumber))
|
||||
{
|
||||
// Position doesn't exist, reset tracking
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to close by ticket first (more reliable)
|
||||
bool closed = false;
|
||||
if(data.position_ticket > 0)
|
||||
{
|
||||
if(PositionSelectByTicket(data.position_ticket))
|
||||
{
|
||||
// Verify it's our position
|
||||
if(PositionGetString(POSITION_SYMBOL) == data.symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
|
||||
{
|
||||
closed = data.trade.PositionClose(data.position_ticket);
|
||||
if(!closed)
|
||||
{
|
||||
Print("RSIScalping: Failed to close position by ticket ", data.position_ticket,
|
||||
" - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If ticket method failed, try magic number method
|
||||
if(!closed)
|
||||
{
|
||||
closed = ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
|
||||
if(!closed)
|
||||
{
|
||||
Print("RSIScalping: Failed to close position by magic number for '", data.symbol,
|
||||
"' - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
|
||||
}
|
||||
}
|
||||
|
||||
// Verify position is actually closed
|
||||
if(closed)
|
||||
{
|
||||
// Wait a moment and verify
|
||||
Sleep(50);
|
||||
if(!PositionExistsByMagic(data.symbol, MagicNumber))
|
||||
{
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
Print("RSIScalping: Position successfully closed for '", data.symbol, "'");
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("RSIScalping: Warning - Close returned success but position still exists for '", data.symbol, "'");
|
||||
// Try one more time
|
||||
Sleep(100);
|
||||
if(PositionExistsByMagic(data.symbol, MagicNumber))
|
||||
{
|
||||
ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
|
||||
}
|
||||
// Reset tracking anyway to prevent getting stuck
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Close failed, but reset tracking to prevent getting stuck
|
||||
// The position might have been closed externally
|
||||
data.position_open = false;
|
||||
data.position_ticket = 0;
|
||||
data.rsi_against_position = false;
|
||||
data.bars_against_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
|
||||
ENUM_APPLIED_PRICE RSI_Applied_Price, double RSI_Overbought,
|
||||
double RSI_Oversold, double RSI_Target_Buy, double RSI_Target_Sell,
|
||||
int BarsToWait, double LotSize, int MagicNumber)
|
||||
{
|
||||
// Skip if not initialized (symbol not available)
|
||||
if(!data.isInitialized)
|
||||
return;
|
||||
|
||||
data.symbol = symbol; // Update symbol in case it changed
|
||||
if(Bars(data.symbol, TimeFrame) < RSI_Period + 2)
|
||||
return;
|
||||
|
||||
datetime current_bar_time = iTime(data.symbol, TimeFrame, 0);
|
||||
if(current_bar_time == data.last_bar_time)
|
||||
return;
|
||||
|
||||
data.last_bar_time = current_bar_time;
|
||||
|
||||
if(!UpdateRSI(data))
|
||||
return;
|
||||
|
||||
CheckExistingPosition(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought,
|
||||
RSI_Target_Buy, RSI_Target_Sell, BarsToWait);
|
||||
|
||||
if(!data.position_open && !PositionExistsByMagic(data.symbol, MagicNumber))
|
||||
{
|
||||
CheckEntrySignals(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought, LotSize);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,736 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| UnitedEA.mq5 |
|
||||
//| Cent ".c" symbols; InpDynamicRefDeposit MUST match ACCOUNT_ |
|
||||
//| CURRENCY numbers (USC ~50k for ~$500, or USD ~500 — not mixed). |
|
||||
//| Per-order max lots = broker spec 最大量 SYMBOL_VOLUME_MAX |
|
||||
//| (often 1000 on *.c); EA cannot exceed it — see symbol contract. |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.10"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include <Trade\PositionInfo.mqh>
|
||||
#include <Indicators\Trend.mqh>
|
||||
#include <Indicators\Volumes.mqh>
|
||||
#include "MagicNumberHelpers.mqh"
|
||||
|
||||
// Lot globals must exist before strategy .mqh (Darvas uses g_DB_LotSize; EMA/RC/RM use g_ES/g_RC/g_RM)
|
||||
double g_ES_LotSize;
|
||||
double g_RC_LotSize;
|
||||
double g_RM_LotSize;
|
||||
double g_DB_LotSize;
|
||||
double g_DynLotScaleLast = 1.0; // last applied scale: lots = baseLot * scale * InpLotSizeScale
|
||||
double g_DynamicRefBaseline = 0.0; // when InpDynamicRefDeposit<=0, frozen ref = equity at first sizing call
|
||||
|
||||
// Include strategy implementations early so structs are available
|
||||
#include "Strategies/DarvasBoxStrategy.mqh"
|
||||
#include "Strategies/EMASlopeDistanceStrategy.mqh"
|
||||
#include "Strategies/RSICrossOverReversalStrategy.mqh"
|
||||
#include "Strategies/RSIMidPointHijackStrategy.mqh"
|
||||
#include "Strategies/RSIScalpingStrategy.mqh"
|
||||
#include "Strategies/RSIReversalAsianStrategy.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy Enable/Disable Switches |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== Strategy Enable/Disable ==="
|
||||
input bool EnableDarvasBox = true;
|
||||
input bool EnableEMASlopeDistance = true;
|
||||
input bool EnableRSICrossOverReversal = true;
|
||||
input bool EnableRSIMidPointHijack = true;
|
||||
input bool EnableRSIScalpingAPPL = true;
|
||||
input bool EnableRSIScalpingBTCUSD = true;
|
||||
input bool EnableRSIScalpingNVDA = true;
|
||||
input bool EnableRSIScalpingTSLA = true;
|
||||
input bool EnableRSIScalpingXAUUSD = true;
|
||||
input bool EnableRSIReversalAsianEURUSD = true;
|
||||
input bool EnableRSIReversalAsianAUDUSD = true;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Dynamic lot sizing — 默认「按比例」:lots = base × (equity/ref) × lotScale |
|
||||
//| 可选幂曲线:lots = base × (equity/ref)^exp × lotScale(旧行为) |
|
||||
//| min/max 约束的是「比例系数」不是手数本身;max<=0 表示比例系数上不封顶 |
|
||||
//| |
|
||||
//| USC (deposit currency) vs "lot size": |
|
||||
//| • Equity/ref for the multiplier are BOTH in account currency |
|
||||
//| (USC). Same units → ratio is correct; no ×100 on the ratio. |
|
||||
//| • Strategy base lots (e.g. DB_BaseLotSize) are ORDER VOLUME in |
|
||||
//| lots, not "USC lots". Broker SYMBOL_VOLUME_* / contract define |
|
||||
//| how much margin and P/L appear in USC. |
|
||||
//| • Do not multiply lot inputs by 100 only because balance is USC. |
|
||||
//| If DD too high: raise InpDynamicRefDeposit and/or lower base lots.|
|
||||
//| If balance is USD ~500 but ref is ~50k–300k: mult→floor, lots→0.01.|
|
||||
//| Ref<=0: 挂上时余额/净值为参考,之后手数随净值相对该基准的比例变化。 |
|
||||
//| 单笔上限:品种规格里的「最大量」(SYMBOL_VOLUME_MAX),非 EA 参数。 |
|
||||
//| InpMaxLotsPerOrder:EA 再截一刀,防止动态+scale 顶满 1000 爆仓。 |
|
||||
//+------------------------------------------------------------------+
|
||||
enum ENUM_LOT_SCALE_CURVE
|
||||
{
|
||||
LOT_CURVE_PROPORTIONAL = 0, // 按比例:scale = 净值/参考(线性)
|
||||
LOT_CURVE_POWER = 1 // 幂:scale = (净值/参考)^exp
|
||||
};
|
||||
|
||||
input group "=== Dynamic lot sizing (动态手数) ==="
|
||||
input bool InpDynamicLotEnable = true; // Enable balance/equity-based scaling
|
||||
input ENUM_LOT_SCALE_CURVE InpDynamicLotCurve = LOT_CURVE_PROPORTIONAL; // 默认按比例;幂曲线=旧 (equity/ref)^exp
|
||||
input double InpDynamicRefDeposit = 0.0; // <=0: auto — ref=挂上时净值/余额(与测试器初始一致则手数随盈利涨); >0 手动参考金
|
||||
input bool InpDynamicRefEqualsEquity = false; // true: 比例系数固定为 1(只用基础手×lotScale)
|
||||
input double InpDynamicExponent = 1.22; // 仅 LOT_CURVE_POWER 时:(净值/参考) 的指数
|
||||
input double InpDynamicMinMult = 0.0; // 比例系数下限;<=0 不抬(按比例时净值<参考会缩小手数)
|
||||
input double InpDynamicMaxMult = 20.0; // 比例系数上限;<=0 不封顶(高风险)
|
||||
input bool InpDynamicUseEquity = true; // true=ACCOUNT_EQUITY, false=ACCOUNT_BALANCE
|
||||
input double InpDynamicStockLotCap = 0.0; // Max lots after scale (0=off); raise if InpLotSizeScale is large
|
||||
input double InpLotSizeScale = 1.0; // 全局手数倍率; 曾用100易过大,默认1按需再加
|
||||
input double InpMaxLotsPerOrder = 20.0; // 单笔最大手数(0=仅券商SYMBOL_VOLUME_MAX); 保守可设5~10
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 1: DarvasBoxXAUUSD (cent symbol) |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== DarvasBox Strategy ==="
|
||||
input string DB_Symbol = "XAUUSD.c";
|
||||
input int DB_BoxPeriod = 165;
|
||||
input double DB_BoxDeviation = 30000; // Increased to allow larger ranges (was 25140)
|
||||
input int DB_VolumeThreshold = 0; // Set to 0 to disable volume threshold check. Volume data from indicator used instead.
|
||||
input double DB_StopLoss = 1665;
|
||||
input double DB_TakeProfit = 3685;
|
||||
input bool DB_EnableLogging = false;
|
||||
input color DB_BoxColor = clrBlue;
|
||||
input int DB_BoxWidth = 1;
|
||||
input ENUM_TIMEFRAMES DB_TrendTimeframe = PERIOD_H2;
|
||||
input int DB_MA_Period = 125;
|
||||
input ENUM_MA_METHOD DB_MA_Method = MODE_EMA;
|
||||
input ENUM_APPLIED_PRICE DB_MA_Price = PRICE_WEIGHTED;
|
||||
input double DB_TrendThreshold = 4.94;
|
||||
input int DB_VolumeMA_Period = 110;
|
||||
input double DB_VolumeThresholdMultiplier = 1.5;
|
||||
input int DB_MagicNumber = 135790;
|
||||
input double DB_BaseLotSize = 0.02; // Base lot at InpDynamicRefDeposit (Darvas)
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 2: EMASlopeDistanceCocktailXAUUSD |
|
||||
//| Cent: gold is usually "XAUUSD.c" (verify in Market Watch). |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== EMA Slope Distance Strategy ==="
|
||||
input string ES_Symbol = "XAUUSD.c";
|
||||
input int ES_EMA_Periode = 46;
|
||||
input double ES_PreisSchwelle = 600.0;
|
||||
input double ES_SteigungSchwelle = 80.0;
|
||||
input int ES_ÜberwachungTimeout = 800;
|
||||
input double ES_TrailingStop = 250.0;
|
||||
input double ES_LotGröße = 0.05;
|
||||
input int ES_MagicNumber = 12350;
|
||||
input bool ES_UseSpreadAdjustment = true;
|
||||
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
|
||||
input bool ES_UseBarData = true;
|
||||
input int ES_MaxTradesPerCrossover = 9;
|
||||
input int ES_ProfitCheckBars = 18;
|
||||
input bool ES_CloseUnprofitableTrades = true;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 3: RSICrossOverReversalXAUUSD |
|
||||
//| Cent: use "XAUUSD.c" if that is what the broker lists. |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI CrossOver Reversal Strategy ==="
|
||||
input string RC_Symbol = "XAUUSD.c";
|
||||
input int RC_MagicNumber = 7;
|
||||
input int RC_rsiPeriod = 19;
|
||||
input int RC_overboughtLevel = 93;
|
||||
input int RC_oversoldLevel = 22;
|
||||
input double RC_entryRSIBuySpread = 0;
|
||||
input double RC_entryRSISellSpread = 0;
|
||||
input double RC_lotSize = 0.02;
|
||||
input int RC_slippage = 3;
|
||||
input int RC_cooldownSeconds = 209;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame2 = PERIOD_M1;
|
||||
input ENUM_TIMEFRAMES RC_BarTimeFrame = PERIOD_M12;
|
||||
input int RC_emaPeriod = 140;
|
||||
input double RC_emaSlopeThreshold = 105;
|
||||
input double RC_exitBuyRSI = 86;
|
||||
input double RC_exitSellRSI = 10;
|
||||
input double RC_TrailingStop = 295;
|
||||
input double RC_emaDistanceThreshold = 165;
|
||||
input int RC_tradingHourOneBegin = 24;
|
||||
input int RC_tradingHourOneEnd = 22;
|
||||
input int RC_tradingHourTwoBegin = 6;
|
||||
input int RC_tradingHourTwoEnd = 19;
|
||||
input bool RC_Sunday = false;
|
||||
input bool RC_Monday = false;
|
||||
input bool RC_Tuesday = true;
|
||||
input bool RC_Wednesday = true;
|
||||
input bool RC_Thursday = true;
|
||||
input bool RC_Friday = false;
|
||||
input bool RC_Saturday = false;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 4: RSIMidPointHijackXAUUSD |
|
||||
//| Cent: use "XAUUSD.c" if that is what the broker lists. |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI MidPoint Hijack Strategy ==="
|
||||
input string RM_Symbol = "XAUUSD.c";
|
||||
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
|
||||
input double RM_InpLotSize = 0.03;
|
||||
input int RM_InpMagicNumberRSIFollow = 1001;
|
||||
input int RM_InpMagicNumberRSIReverse = 1002;
|
||||
input int RM_InpMagicNumberEMACross = 1003;
|
||||
input bool RM_InpEnableRSIFollow = true;
|
||||
input bool RM_InpEnableRSIReverse = true;
|
||||
input bool RM_InpEnableEMACross = true;
|
||||
input bool RM_InpEnableStrategyLock = false;
|
||||
input double RM_InpLockProfitThreshold = 0.0;
|
||||
input bool RM_InpCloseOppositeTrades = false;
|
||||
input int RM_InpRSIPeriod = 32;
|
||||
input int RM_InpRSIOverbought = 78;
|
||||
input int RM_InpRSIOversold = 46;
|
||||
input int RM_InpRSIExitLevel = 44;
|
||||
input int RM_InpRSIFollowStartHour = 23;
|
||||
input int RM_InpRSIFollowEndHour = 8;
|
||||
input bool RM_InpRSIFollowCloseOutsideHours = false;
|
||||
input int RM_InpRSIReversePeriod = 59;
|
||||
input int RM_InpRSIReverseOverbought = 51;
|
||||
input int RM_InpRSIReverseOversold = 49;
|
||||
input int RM_InpRSIReverseCrossLevel = 53;
|
||||
input int RM_InpRSIReverseExitLevel = 48;
|
||||
input int RM_InpRSIReverseStartHour = 7;
|
||||
input int RM_InpRSIReverseEndHour = 13;
|
||||
input bool RM_InpRSIReverseCloseOutsideHours = false;
|
||||
input int RM_InpRSIReverseCooldownBars = 15;
|
||||
input bool RM_InpRSIReverseCooldownOnLoss = true;
|
||||
input int RM_InpEMAPeriod = 120;
|
||||
input int RM_InpEMACrossStartHour = 8;
|
||||
input int RM_InpEMACrossEndHour = 14;
|
||||
input bool RM_InpEMACrossCloseOutsideHours = true;
|
||||
input bool RM_InpUseEMADistanceEntry = true;
|
||||
input double RM_InpEMADistancePips = 160.0;
|
||||
input int RM_InpEMADistancePeriod = 26;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 5-10: RSI Scalping Strategies |
|
||||
//| Each RSI Scalping strategy trades on its own symbol: |
|
||||
//| - APPL: Apple stock (AAPL) |
|
||||
//| - BTCUSD: Bitcoin/USD |
|
||||
//| - NVDA: NVIDIA stock |
|
||||
//| - TSLA: Tesla stock |
|
||||
//| - XAUUSD: Gold/USD |
|
||||
//| |
|
||||
//| USC cent: many symbols end with ".c" — use Market Watch names. |
|
||||
//| Stocks may be "AAPL.US.c" or unchanged; verify before live. |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI Scalping APPL (AAPL) - cent ==="
|
||||
input string RS_APPL_Symbol = "AAPL.US.c"; // If missing, try AAPL.US / NASDAQ:AAPL / AAPL
|
||||
input ENUM_TIMEFRAMES RS_APPL_TimeFrame = PERIOD_M10;
|
||||
input int RS_APPL_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_APPL_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_APPL_RSI_Overbought = 80;
|
||||
input double RS_APPL_RSI_Oversold = 78;
|
||||
input double RS_APPL_RSI_Target_Buy = 94;
|
||||
input double RS_APPL_RSI_Target_Sell = 44;
|
||||
input int RS_APPL_BarsToWait = 7;
|
||||
input double RS_APPL_LotSize = 38;
|
||||
input int RS_APPL_MagicNumber = 20001;
|
||||
input int RS_APPL_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping BTCUSD ==="
|
||||
input string RS_BTCUSD_Symbol = "BTCUSD.c"; // If missing, try BTCUSD or BTC/USD
|
||||
input ENUM_TIMEFRAMES RS_BTCUSD_TimeFrame = PERIOD_H1;
|
||||
input int RS_BTCUSD_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_BTCUSD_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_BTCUSD_RSI_Overbought = 90;
|
||||
input double RS_BTCUSD_RSI_Oversold = 73;
|
||||
input double RS_BTCUSD_RSI_Target_Buy = 88;
|
||||
input double RS_BTCUSD_RSI_Target_Sell = 48;
|
||||
input int RS_BTCUSD_BarsToWait = 6;
|
||||
input double RS_BTCUSD_LotSize = 0.15;
|
||||
input int RS_BTCUSD_MagicNumber = 123459123;
|
||||
input int RS_BTCUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping NVDA - cent ==="
|
||||
input string RS_NVDA_Symbol = "NVDA.US.c"; // If missing, try NVDA.US / NASDAQ:NVDA / NVDA
|
||||
input ENUM_TIMEFRAMES RS_NVDA_TimeFrame = PERIOD_M15;
|
||||
input int RS_NVDA_RSI_Period = 8;
|
||||
input ENUM_APPLIED_PRICE RS_NVDA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_NVDA_RSI_Overbought = 36;
|
||||
input double RS_NVDA_RSI_Oversold = 38;
|
||||
input double RS_NVDA_RSI_Target_Buy = 90;
|
||||
input double RS_NVDA_RSI_Target_Sell = 70;
|
||||
input int RS_NVDA_BarsToWait = 5;
|
||||
input double RS_NVDA_LotSize = 75;
|
||||
input int RS_NVDA_MagicNumber = 20003;
|
||||
input int RS_NVDA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping TSLA - cent ==="
|
||||
input string RS_TSLA_Symbol = "TSLA.US.c"; // If missing, try TSLA.US / NASDAQ:TSLA / TSLA
|
||||
input ENUM_TIMEFRAMES RS_TSLA_TimeFrame = PERIOD_H1;
|
||||
input int RS_TSLA_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_TSLA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_TSLA_RSI_Overbought = 54;
|
||||
input double RS_TSLA_RSI_Oversold = 73;
|
||||
input double RS_TSLA_RSI_Target_Buy = 87;
|
||||
input double RS_TSLA_RSI_Target_Sell = 33;
|
||||
input int RS_TSLA_BarsToWait = 1;
|
||||
input double RS_TSLA_LotSize = 75;
|
||||
input int RS_TSLA_MagicNumber = 125421321;
|
||||
input int RS_TSLA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping XAUUSD ==="
|
||||
input string RS_XAUUSD_Symbol = "XAUUSD.c";
|
||||
input ENUM_TIMEFRAMES RS_XAUUSD_TimeFrame = PERIOD_H1;
|
||||
input int RS_XAUUSD_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_XAUUSD_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_XAUUSD_RSI_Overbought = 71;
|
||||
input double RS_XAUUSD_RSI_Oversold = 57;
|
||||
input double RS_XAUUSD_RSI_Target_Buy = 80;
|
||||
input double RS_XAUUSD_RSI_Target_Sell = 57;
|
||||
input int RS_XAUUSD_BarsToWait = 4;
|
||||
input double RS_XAUUSD_LotSize = 0.15;
|
||||
input int RS_XAUUSD_MagicNumber = 129102315;
|
||||
input int RS_XAUUSD_Slippage = 3;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 11-12: RSI Reversal Asian Strategies |
|
||||
//| Each RSI Reversal Asian strategy trades on its own symbol: |
|
||||
//| - EURUSD: Euro/USD |
|
||||
//| - AUDUSD: Australian Dollar/USD |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI Reversal Asian EURUSD ==="
|
||||
input string RRA_EURUSD_Symbol = "EURUSD.c";
|
||||
input int RRA_EURUSD_RSIPeriod = 28;
|
||||
input double RRA_EURUSD_OverboughtLevel = 60;
|
||||
input double RRA_EURUSD_OversoldLevel = 8;
|
||||
input int RRA_EURUSD_TakeProfitPips = 175;
|
||||
input int RRA_EURUSD_StopLossPips = 5;
|
||||
input double RRA_EURUSD_MaxLotSize = 0.15;
|
||||
input int RRA_EURUSD_MaxSpread = 1000;
|
||||
input int RRA_EURUSD_MaxDuration = 270;
|
||||
input bool RRA_EURUSD_UseStopLoss = false;
|
||||
input bool RRA_EURUSD_UseTakeProfit = false;
|
||||
input bool RRA_EURUSD_UseRSIExit = true;
|
||||
input double RRA_EURUSD_RSIExitLevel = 55;
|
||||
input bool RRA_EURUSD_CloseOutsideSession = false;
|
||||
input ENUM_TIMEFRAMES RRA_EURUSD_TimeFrame = PERIOD_M15;
|
||||
input int RRA_EURUSD_MagicNumber = 30001;
|
||||
input int RRA_EURUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Reversal Asian AUDUSD ==="
|
||||
input string RRA_AUDUSD_Symbol = "AUDUSD.c";
|
||||
input int RRA_AUDUSD_RSIPeriod = 28;
|
||||
input double RRA_AUDUSD_OverboughtLevel = 68;
|
||||
input double RRA_AUDUSD_OversoldLevel = 30;
|
||||
input int RRA_AUDUSD_TakeProfitPips = 175;
|
||||
input int RRA_AUDUSD_StopLossPips = 5;
|
||||
input double RRA_AUDUSD_MaxLotSize = 0.3;
|
||||
input int RRA_AUDUSD_MaxSpread = 1000;
|
||||
input int RRA_AUDUSD_MaxDuration = 340;
|
||||
input bool RRA_AUDUSD_UseStopLoss = false;
|
||||
input bool RRA_AUDUSD_UseTakeProfit = false;
|
||||
input bool RRA_AUDUSD_UseRSIExit = true;
|
||||
input double RRA_AUDUSD_RSIExitLevel = 48;
|
||||
input bool RRA_AUDUSD_CloseOutsideSession = true;
|
||||
input ENUM_TIMEFRAMES RRA_AUDUSD_TimeFrame = PERIOD_M15;
|
||||
input int RRA_AUDUSD_MagicNumber = 30002;
|
||||
input int RRA_AUDUSD_Slippage = 3;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - DarvasBox |
|
||||
//+------------------------------------------------------------------+
|
||||
struct DarvasBoxData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
double boxHigh;
|
||||
double boxLow;
|
||||
bool boxFormed;
|
||||
datetime lastBoxTime;
|
||||
string boxName;
|
||||
double minStopLevel;
|
||||
double point;
|
||||
CTrade trade;
|
||||
int maHandle;
|
||||
int volumeHandle;
|
||||
datetime lastBarTime;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - EMA Slope Distance |
|
||||
//+------------------------------------------------------------------+
|
||||
struct EMASlopeData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int ema_handle;
|
||||
double ema_array[];
|
||||
datetime letzte_überwachung_zeit;
|
||||
bool überwachung_aktiv;
|
||||
bool preis_trigger_aktiv;
|
||||
bool steigung_trigger_aktiv;
|
||||
int ticket;
|
||||
CTrade trade;
|
||||
int trades_in_current_crossover;
|
||||
bool crossover_detected;
|
||||
datetime trade_open_time;
|
||||
datetime last_bar_time;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI CrossOver Reversal |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSICrossOverData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
int emaHandle;
|
||||
double previousRSIDef;
|
||||
CTrade trade;
|
||||
datetime lastTradeTime;
|
||||
datetime bartime;
|
||||
bool WeekDays[7];
|
||||
datetime lastBarTime;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI MidPoint Hijack |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSIMidPointData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
int rsiReverseHandle;
|
||||
int emaHandle;
|
||||
bool rsiOverbought;
|
||||
bool rsiOversold;
|
||||
bool rsiReverseOverbought;
|
||||
bool rsiReverseOversold;
|
||||
CTrade trade;
|
||||
CPositionInfo positionInfo;
|
||||
bool emaCrossBuySignal;
|
||||
bool emaCrossSellSignal;
|
||||
int emaCrossSignalBar;
|
||||
datetime lastBarTime;
|
||||
datetime rsiReverseLastCloseTime;
|
||||
bool rsiReverseInCooldown;
|
||||
double lastBarRSI;
|
||||
double lastBarRSIReverse;
|
||||
double lastBarEMA;
|
||||
double lastBarClose;
|
||||
double lastBarEMAPrev;
|
||||
double lastBarClosePrev;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Strategy Instances |
|
||||
//+------------------------------------------------------------------+
|
||||
DarvasBoxData dbData;
|
||||
EMASlopeData esData;
|
||||
RSICrossOverData rcData;
|
||||
RSIMidPointData rmData;
|
||||
RSIScalpingData rsAPPLData;
|
||||
RSIScalpingData rsBTCUSDData;
|
||||
RSIScalpingData rsNVDAData;
|
||||
RSIScalpingData rsTSLAData;
|
||||
RSIScalpingData rsXAUUSDData;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI Reversal Asian |
|
||||
//+------------------------------------------------------------------+
|
||||
RSIReversalAsianData rraEURUSDData;
|
||||
RSIReversalAsianData rraAUDUSDData;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Dynamic lot helpers |
|
||||
//+------------------------------------------------------------------+
|
||||
double DynClamp(const double v, const double lo, const double hi)
|
||||
{
|
||||
return MathMax(lo, MathMin(hi, v));
|
||||
}
|
||||
|
||||
// Clamp scale factor (proportion or pow result). max<=0 = no upper clamp.
|
||||
double ApplyDynamicScaleClamp(const double scaleRaw)
|
||||
{
|
||||
double s = scaleRaw;
|
||||
if(InpDynamicMinMult > 0.0)
|
||||
s = MathMax(s, InpDynamicMinMult);
|
||||
if(InpDynamicMaxMult > 0.0)
|
||||
s = MathMin(s, InpDynamicMaxMult);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Reference for (equity/ref)^exp: manual deposit, or first-seen balance when input <= 0
|
||||
double GetDynamicRefForRatio()
|
||||
{
|
||||
if(InpDynamicRefDeposit > 0.0)
|
||||
return MathMax(InpDynamicRefDeposit, 1.0);
|
||||
double capNow = InpDynamicUseEquity ? AccountInfoDouble(ACCOUNT_EQUITY) : AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
if(g_DynamicRefBaseline <= 0.0)
|
||||
g_DynamicRefBaseline = MathMax(capNow, 1.0);
|
||||
return MathMax(g_DynamicRefBaseline, 1.0);
|
||||
}
|
||||
|
||||
// Broker hard cap: 最大量 = SYMBOL_VOLUME_MAX (e.g. 1000 on EURUSD.c / XAUUSD.c)
|
||||
double NormalizeVolumeForSymbol(const string symbol, double lots)
|
||||
{
|
||||
double minL = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
||||
double maxL = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
||||
double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
||||
if(step > 0.0)
|
||||
lots = MathFloor(lots / step + 1e-12) * step;
|
||||
if(lots < minL) lots = minL;
|
||||
if(lots > maxL) lots = maxL;
|
||||
return lots;
|
||||
}
|
||||
|
||||
// Apply EA risk cap before broker min/step/max (InpMaxLotsPerOrder 0 = disabled)
|
||||
double NormalizeVolumeForSymbolWithEACap(const string symbol, double lots)
|
||||
{
|
||||
if(InpMaxLotsPerOrder > 0.0)
|
||||
lots = MathMin(lots, InpMaxLotsPerOrder);
|
||||
return NormalizeVolumeForSymbol(symbol, lots);
|
||||
}
|
||||
|
||||
// Scale factor for lots: baseLot * scale * InpLotSizeScale (then caps)
|
||||
double GetDynamicLotScaleFactor()
|
||||
{
|
||||
if(!InpDynamicLotEnable)
|
||||
return 1.0;
|
||||
if(InpDynamicRefEqualsEquity)
|
||||
return ApplyDynamicScaleClamp(1.0);
|
||||
double cap = InpDynamicUseEquity ? AccountInfoDouble(ACCOUNT_EQUITY) : AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
double refv = GetDynamicRefForRatio();
|
||||
if(cap <= 0.0)
|
||||
cap = refv;
|
||||
double ratio = cap / refv;
|
||||
if(ratio <= 0.0)
|
||||
ratio = 1.0;
|
||||
double scaleRaw = ratio;
|
||||
if(InpDynamicLotCurve == LOT_CURVE_POWER)
|
||||
scaleRaw = MathPow(ratio, InpDynamicExponent);
|
||||
return ApplyDynamicScaleClamp(scaleRaw);
|
||||
}
|
||||
|
||||
// baseLot = size at reference deposit; optionalCap 0 = no extra ceiling (broker min/max still apply)
|
||||
double DynamicLotForSymbol(const string symbol, const double baseLot, const double optionalCap = 0.0)
|
||||
{
|
||||
double scale = GetDynamicLotScaleFactor();
|
||||
g_DynLotScaleLast = scale;
|
||||
double sc = (InpLotSizeScale > 0.0 ? InpLotSizeScale : 1.0);
|
||||
double v = baseLot * scale * sc;
|
||||
if(optionalCap > 0.0 && v > optionalCap)
|
||||
v = optionalCap;
|
||||
return NormalizeVolumeForSymbolWithEACap(symbol, v);
|
||||
}
|
||||
|
||||
void RefreshDynamicStrategyLots()
|
||||
{
|
||||
double sc = (InpLotSizeScale > 0.0 ? InpLotSizeScale : 1.0);
|
||||
if(!InpDynamicLotEnable)
|
||||
{
|
||||
g_ES_LotSize = NormalizeVolumeForSymbolWithEACap(ES_Symbol, ES_LotGröße * sc);
|
||||
g_RC_LotSize = NormalizeVolumeForSymbolWithEACap(RC_Symbol, RC_lotSize * sc);
|
||||
g_RM_LotSize = NormalizeVolumeForSymbolWithEACap(RM_Symbol, RM_InpLotSize * sc);
|
||||
g_DB_LotSize = NormalizeVolumeForSymbolWithEACap(DB_Symbol, DB_BaseLotSize * sc);
|
||||
g_DynLotScaleLast = 1.0;
|
||||
return;
|
||||
}
|
||||
g_ES_LotSize = DynamicLotForSymbol(ES_Symbol, ES_LotGröße);
|
||||
g_RC_LotSize = DynamicLotForSymbol(RC_Symbol, RC_lotSize);
|
||||
g_RM_LotSize = DynamicLotForSymbol(RM_Symbol, RM_InpLotSize);
|
||||
g_DB_LotSize = DynamicLotForSymbol(DB_Symbol, DB_BaseLotSize);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
int initResult = INIT_SUCCEEDED;
|
||||
|
||||
g_DynamicRefBaseline = 0.0;
|
||||
|
||||
RefreshDynamicStrategyLots();
|
||||
|
||||
string acctCur = AccountInfoString(ACCOUNT_CURRENCY);
|
||||
double eq0 = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
if(InpDynamicLotEnable && !InpDynamicRefEqualsEquity && InpDynamicRefDeposit > 1000.0 && eq0 > 0.0
|
||||
&& eq0 <= InpDynamicRefDeposit / 25.0)
|
||||
Print("United EA: equity ", DoubleToString(eq0, 2), " ", acctCur, " vs ref ", InpDynamicRefDeposit,
|
||||
" — dynamic mult is tiny; set InpDynamicRefDeposit to your balance in ", acctCur,
|
||||
" (e.g. 500 for USD) or enable InpDynamicRefEqualsEquity. Else lots stay at broker minimum.");
|
||||
|
||||
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
|
||||
if(EnableDarvasBox)
|
||||
if(!InitDarvasBox(DB_Symbol))
|
||||
Print("Warning: DarvasBox strategy failed to initialize for symbol '", DB_Symbol, "'");
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
if(!InitEMASlopeDistance(ES_Symbol))
|
||||
Print("Warning: EMASlopeDistance strategy failed to initialize for symbol '", ES_Symbol, "'");
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
if(!InitRSICrossOverReversal(RC_Symbol))
|
||||
Print("Warning: RSICrossOverReversal strategy failed to initialize for symbol '", RC_Symbol, "'");
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
if(!InitRSIMidPointHijack(RM_Symbol))
|
||||
Print("Warning: RSIMidPointHijack strategy failed to initialize for symbol '", RM_Symbol, "'");
|
||||
|
||||
// Initialize RSI Scalping strategies - don't fail entire EA if symbol unavailable
|
||||
if(EnableRSIScalpingAPPL)
|
||||
InitRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price, RS_APPL_MagicNumber, RS_APPL_Slippage);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
InitRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price, RS_BTCUSD_MagicNumber, RS_BTCUSD_Slippage);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
InitRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price, RS_NVDA_MagicNumber, RS_NVDA_Slippage);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
InitRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price, RS_TSLA_MagicNumber, RS_TSLA_Slippage);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
InitRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price, RS_XAUUSD_MagicNumber, RS_XAUUSD_Slippage);
|
||||
|
||||
// Initialize RSI Reversal Asian strategies
|
||||
if(EnableRSIReversalAsianEURUSD)
|
||||
if(!InitRSIReversalAsian(rraEURUSDData, RRA_EURUSD_Symbol, RRA_EURUSD_RSIPeriod, RRA_EURUSD_OverboughtLevel, RRA_EURUSD_OversoldLevel,
|
||||
RRA_EURUSD_TakeProfitPips, RRA_EURUSD_StopLossPips, RRA_EURUSD_MaxLotSize,
|
||||
RRA_EURUSD_MaxSpread, RRA_EURUSD_MaxDuration, RRA_EURUSD_UseStopLoss,
|
||||
RRA_EURUSD_UseTakeProfit, RRA_EURUSD_UseRSIExit, RRA_EURUSD_RSIExitLevel,
|
||||
RRA_EURUSD_CloseOutsideSession, RRA_EURUSD_TimeFrame, RRA_EURUSD_MagicNumber, RRA_EURUSD_Slippage))
|
||||
Print("Warning: RSIReversalAsianEURUSD strategy failed to initialize for symbol '", RRA_EURUSD_Symbol, "'");
|
||||
|
||||
if(EnableRSIReversalAsianAUDUSD)
|
||||
if(!InitRSIReversalAsian(rraAUDUSDData, RRA_AUDUSD_Symbol, RRA_AUDUSD_RSIPeriod, RRA_AUDUSD_OverboughtLevel, RRA_AUDUSD_OversoldLevel,
|
||||
RRA_AUDUSD_TakeProfitPips, RRA_AUDUSD_StopLossPips, RRA_AUDUSD_MaxLotSize,
|
||||
RRA_AUDUSD_MaxSpread, RRA_AUDUSD_MaxDuration, RRA_AUDUSD_UseStopLoss,
|
||||
RRA_AUDUSD_UseTakeProfit, RRA_AUDUSD_UseRSIExit, RRA_AUDUSD_RSIExitLevel,
|
||||
RRA_AUDUSD_CloseOutsideSession, RRA_AUDUSD_TimeFrame, RRA_AUDUSD_MagicNumber, RRA_AUDUSD_Slippage))
|
||||
Print("Warning: RSIReversalAsianAUDUSD strategy failed to initialize for symbol '", RRA_AUDUSD_Symbol, "'");
|
||||
|
||||
double refEffInit = GetDynamicRefForRatio();
|
||||
double capInit = InpDynamicUseEquity ? eq0 : AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
if(capInit <= 0.0)
|
||||
capInit = refEffInit;
|
||||
double ratioInit = capInit / refEffInit;
|
||||
double powInit = MathPow(ratioInit, InpDynamicExponent);
|
||||
string curveStr = (InpDynamicLotCurve == LOT_CURVE_POWER ? "POWER" : "PROP");
|
||||
Print("United EA v1.10 ", acctCur, " curve=", curveStr, " equity=", DoubleToString(eq0, 2), " refEff=", DoubleToString(refEffInit, 2),
|
||||
" (inpRef=", InpDynamicRefDeposit, " baseline=", DoubleToString(g_DynamicRefBaseline, 2), ") equity/ref=", DoubleToString(ratioInit, 6),
|
||||
" pow^exp=", DoubleToString(powInit, 6), " scaleOut=", DoubleToString(g_DynLotScaleLast, 6),
|
||||
" minS=", InpDynamicMinMult, " maxS=", InpDynamicMaxMult, " lotScale=", InpLotSizeScale, " maxLots=", InpMaxLotsPerOrder,
|
||||
" lots ES=", g_ES_LotSize, " RC=", g_RC_LotSize, " RM=", g_RM_LotSize, " DB=", g_DB_LotSize);
|
||||
Print("United EA initialized. Active strategies: ",
|
||||
(EnableDarvasBox ? "DarvasBox " : ""),
|
||||
(EnableEMASlopeDistance ? "EMASlope " : ""),
|
||||
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
|
||||
(EnableRSIMidPointHijack ? "RSIMidPoint " : ""),
|
||||
(EnableRSIScalpingAPPL ? "RSIScalpingAPPL " : ""),
|
||||
(EnableRSIScalpingBTCUSD ? "RSIScalpingBTCUSD " : ""),
|
||||
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
|
||||
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
|
||||
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""),
|
||||
(EnableRSIReversalAsianEURUSD ? "RSIReversalAsianEURUSD " : ""),
|
||||
(EnableRSIReversalAsianAUDUSD ? "RSIReversalAsianAUDUSD " : ""));
|
||||
|
||||
return initResult;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(EnableDarvasBox)
|
||||
DeinitDarvasBox();
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
DeinitEMASlopeDistance();
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
DeinitRSICrossOverReversal();
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
DeinitRSIMidPointHijack();
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
DeinitRSIScalping(rsAPPLData);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
DeinitRSIScalping(rsBTCUSDData);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
DeinitRSIScalping(rsNVDAData);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
DeinitRSIScalping(rsTSLAData);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
DeinitRSIScalping(rsXAUUSDData);
|
||||
|
||||
if(EnableRSIReversalAsianEURUSD)
|
||||
DeinitRSIReversalAsian(rraEURUSDData);
|
||||
|
||||
if(EnableRSIReversalAsianAUDUSD)
|
||||
DeinitRSIReversalAsian(rraAUDUSDData);
|
||||
|
||||
Print("United EA deinitialized. Reason: ", reason);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
RefreshDynamicStrategyLots();
|
||||
|
||||
if(EnableDarvasBox)
|
||||
ProcessDarvasBox(DB_Symbol);
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
ProcessEMASlopeDistance(ES_Symbol);
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
ProcessRSICrossOverReversal(RC_Symbol);
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
ProcessRSIMidPointHijack(RM_Symbol);
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
ProcessRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price,
|
||||
RS_APPL_RSI_Overbought, RS_APPL_RSI_Oversold, RS_APPL_RSI_Target_Buy, RS_APPL_RSI_Target_Sell,
|
||||
RS_APPL_BarsToWait,
|
||||
DynamicLotForSymbol(RS_APPL_Symbol, RS_APPL_LotSize, InpDynamicStockLotCap),
|
||||
RS_APPL_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
ProcessRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price,
|
||||
RS_BTCUSD_RSI_Overbought, RS_BTCUSD_RSI_Oversold, RS_BTCUSD_RSI_Target_Buy, RS_BTCUSD_RSI_Target_Sell,
|
||||
RS_BTCUSD_BarsToWait, DynamicLotForSymbol(RS_BTCUSD_Symbol, RS_BTCUSD_LotSize), RS_BTCUSD_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
ProcessRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price,
|
||||
RS_NVDA_RSI_Overbought, RS_NVDA_RSI_Oversold, RS_NVDA_RSI_Target_Buy, RS_NVDA_RSI_Target_Sell,
|
||||
RS_NVDA_BarsToWait,
|
||||
DynamicLotForSymbol(RS_NVDA_Symbol, RS_NVDA_LotSize, InpDynamicStockLotCap),
|
||||
RS_NVDA_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
ProcessRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price,
|
||||
RS_TSLA_RSI_Overbought, RS_TSLA_RSI_Oversold, RS_TSLA_RSI_Target_Buy, RS_TSLA_RSI_Target_Sell,
|
||||
RS_TSLA_BarsToWait,
|
||||
DynamicLotForSymbol(RS_TSLA_Symbol, RS_TSLA_LotSize, InpDynamicStockLotCap),
|
||||
RS_TSLA_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
ProcessRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price,
|
||||
RS_XAUUSD_RSI_Overbought, RS_XAUUSD_RSI_Oversold, RS_XAUUSD_RSI_Target_Buy, RS_XAUUSD_RSI_Target_Sell,
|
||||
RS_XAUUSD_BarsToWait, DynamicLotForSymbol(RS_XAUUSD_Symbol, RS_XAUUSD_LotSize), RS_XAUUSD_MagicNumber);
|
||||
|
||||
if(EnableRSIReversalAsianEURUSD)
|
||||
ProcessRSIReversalAsian(rraEURUSDData, DynamicLotForSymbol(RRA_EURUSD_Symbol, RRA_EURUSD_MaxLotSize));
|
||||
|
||||
if(EnableRSIReversalAsianAUDUSD)
|
||||
ProcessRSIReversalAsian(rraAUDUSDData, DynamicLotForSymbol(RRA_AUDUSD_Symbol, RRA_AUDUSD_MaxLotSize));
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
@@ -0,0 +1,641 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| UnitedEA.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include <Trade\PositionInfo.mqh>
|
||||
#include <Indicators\Trend.mqh>
|
||||
#include <Indicators\Volumes.mqh>
|
||||
#include "MagicNumberHelpers.mqh"
|
||||
#include "PerformanceEvaluator.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy Enable/Disable Switches |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== Strategy Enable/Disable ==="
|
||||
input bool EnableDarvasBox = true;
|
||||
input bool EnableEMASlopeDistance = true;
|
||||
input bool EnableRSICrossOverReversal = true;
|
||||
input bool EnableRSIMidPointHijack = true;
|
||||
input bool EnableRSIScalpingAPPL = true;
|
||||
input bool EnableRSIScalpingBTCUSD = true;
|
||||
input bool EnableRSIScalpingNVDA = true;
|
||||
input bool EnableRSIScalpingTSLA = true;
|
||||
input bool EnableRSIScalpingXAUUSD = true;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 1: DarvasBoxXAUUSD |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== DarvasBox Strategy ==="
|
||||
input string DB_Symbol = "XAUUSD";
|
||||
input int DB_BoxPeriod = 165;
|
||||
input double DB_BoxDeviation = 30000; // Increased to allow larger ranges (was 25140)
|
||||
input int DB_VolumeThreshold = 0; // Set to 0 to disable volume threshold check. Volume data from indicator used instead.
|
||||
input double DB_StopLoss = 1665;
|
||||
input double DB_TakeProfit = 3685;
|
||||
input bool DB_EnableLogging = false;
|
||||
input color DB_BoxColor = clrBlue;
|
||||
input int DB_BoxWidth = 1;
|
||||
input ENUM_TIMEFRAMES DB_TrendTimeframe = PERIOD_H2;
|
||||
input int DB_MA_Period = 125;
|
||||
input ENUM_MA_METHOD DB_MA_Method = MODE_EMA;
|
||||
input ENUM_APPLIED_PRICE DB_MA_Price = PRICE_WEIGHTED;
|
||||
input double DB_TrendThreshold = 4.94;
|
||||
input int DB_VolumeMA_Period = 110;
|
||||
input double DB_VolumeThresholdMultiplier = 1.5;
|
||||
input int DB_MagicNumber = 135790;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 2: EMASlopeDistanceCocktailXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== EMA Slope Distance Strategy ==="
|
||||
input string ES_Symbol = "XAUUSD";
|
||||
input int ES_EMA_Periode = 46;
|
||||
input double ES_PreisSchwelle = 600.0;
|
||||
input double ES_SteigungSchwelle = 80.0;
|
||||
input int ES_ÜberwachungTimeout = 800;
|
||||
input double ES_TrailingStop = 250.0;
|
||||
input double ES_LotGröße = 0.03;
|
||||
input int ES_MagicNumber = 12350;
|
||||
input bool ES_UseSpreadAdjustment = true;
|
||||
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
|
||||
input bool ES_UseBarData = true;
|
||||
input int ES_MaxTradesPerCrossover = 9;
|
||||
input int ES_ProfitCheckBars = 18;
|
||||
input bool ES_CloseUnprofitableTrades = true;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 3: RSICrossOverReversalXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI CrossOver Reversal Strategy ==="
|
||||
input string RC_Symbol = "XAUUSD";
|
||||
input int RC_MagicNumber = 7;
|
||||
input int RC_rsiPeriod = 19;
|
||||
input int RC_overboughtLevel = 93;
|
||||
input int RC_oversoldLevel = 22;
|
||||
input double RC_entryRSIBuySpread = 0;
|
||||
input double RC_entryRSISellSpread = 0;
|
||||
input double RC_lotSize = 0.01;
|
||||
input int RC_slippage = 3;
|
||||
input int RC_cooldownSeconds = 209;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame2 = PERIOD_M1;
|
||||
input ENUM_TIMEFRAMES RC_BarTimeFrame = PERIOD_M12;
|
||||
input int RC_emaPeriod = 140;
|
||||
input double RC_emaSlopeThreshold = 105;
|
||||
input double RC_exitBuyRSI = 86;
|
||||
input double RC_exitSellRSI = 10;
|
||||
input double RC_TrailingStop = 295;
|
||||
input double RC_emaDistanceThreshold = 165;
|
||||
input int RC_tradingHourOneBegin = 24;
|
||||
input int RC_tradingHourOneEnd = 22;
|
||||
input int RC_tradingHourTwoBegin = 6;
|
||||
input int RC_tradingHourTwoEnd = 19;
|
||||
input bool RC_Sunday = false;
|
||||
input bool RC_Monday = false;
|
||||
input bool RC_Tuesday = true;
|
||||
input bool RC_Wednesday = true;
|
||||
input bool RC_Thursday = true;
|
||||
input bool RC_Friday = false;
|
||||
input bool RC_Saturday = false;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 4: RSIMidPointHijackXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI MidPoint Hijack Strategy ==="
|
||||
input string RM_Symbol = "XAUUSD";
|
||||
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
|
||||
input double RM_InpLotSize = 0.02;
|
||||
input int RM_InpMagicNumberRSIFollow = 1001;
|
||||
input int RM_InpMagicNumberRSIReverse = 1002;
|
||||
input int RM_InpMagicNumberEMACross = 1003;
|
||||
input bool RM_InpEnableRSIFollow = true;
|
||||
input bool RM_InpEnableRSIReverse = true;
|
||||
input bool RM_InpEnableEMACross = true;
|
||||
input bool RM_InpEnableStrategyLock = false;
|
||||
input double RM_InpLockProfitThreshold = 0.0;
|
||||
input bool RM_InpCloseOppositeTrades = false;
|
||||
input int RM_InpRSIPeriod = 32;
|
||||
input int RM_InpRSIOverbought = 78;
|
||||
input int RM_InpRSIOversold = 46;
|
||||
input int RM_InpRSIExitLevel = 44;
|
||||
input int RM_InpRSIFollowStartHour = 23;
|
||||
input int RM_InpRSIFollowEndHour = 8;
|
||||
input bool RM_InpRSIFollowCloseOutsideHours = false;
|
||||
input int RM_InpRSIReversePeriod = 59;
|
||||
input int RM_InpRSIReverseOverbought = 51;
|
||||
input int RM_InpRSIReverseOversold = 49;
|
||||
input int RM_InpRSIReverseCrossLevel = 53;
|
||||
input int RM_InpRSIReverseExitLevel = 48;
|
||||
input int RM_InpRSIReverseStartHour = 7;
|
||||
input int RM_InpRSIReverseEndHour = 13;
|
||||
input bool RM_InpRSIReverseCloseOutsideHours = false;
|
||||
input int RM_InpRSIReverseCooldownBars = 15;
|
||||
input bool RM_InpRSIReverseCooldownOnLoss = true;
|
||||
input int RM_InpEMAPeriod = 120;
|
||||
input int RM_InpEMACrossStartHour = 8;
|
||||
input int RM_InpEMACrossEndHour = 14;
|
||||
input bool RM_InpEMACrossCloseOutsideHours = true;
|
||||
input bool RM_InpUseEMADistanceEntry = true;
|
||||
input double RM_InpEMADistancePips = 160.0;
|
||||
input int RM_InpEMADistancePeriod = 26;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 5-10: RSI Scalping Strategies |
|
||||
//| Each RSI Scalping strategy trades on its own symbol: |
|
||||
//| - APPL: Apple stock (AAPL) |
|
||||
//| - BTCUSD: Bitcoin/USD |
|
||||
//| - NVDA: NVIDIA stock |
|
||||
//| - TSLA: Tesla stock |
|
||||
//| - XAUUSD: Gold/USD |
|
||||
//| |
|
||||
//| PEPPERSTONE US SYMBOL FORMATS: |
|
||||
//| - Stocks may use: "AAPL.US", "NASDAQ:AAPL", or just "AAPL" |
|
||||
//| - To find correct symbols: |
|
||||
//| 1. Open Market Watch (Ctrl+M) |
|
||||
//| 2. Right-click > Show All |
|
||||
//| 3. Search for the stock name |
|
||||
//| 4. Use the exact symbol name shown |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI Scalping APPL (AAPL) - Pepperstone US ==="
|
||||
input string RS_APPL_Symbol = "AAPL.US"; // Try: "AAPL.US", "NASDAQ:AAPL", or "AAPL"
|
||||
input ENUM_TIMEFRAMES RS_APPL_TimeFrame = PERIOD_M10;
|
||||
input int RS_APPL_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_APPL_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_APPL_RSI_Overbought = 80;
|
||||
input double RS_APPL_RSI_Oversold = 78;
|
||||
input double RS_APPL_RSI_Target_Buy = 94;
|
||||
input double RS_APPL_RSI_Target_Sell = 44;
|
||||
input int RS_APPL_BarsToWait = 7;
|
||||
input double RS_APPL_LotSize = 25;
|
||||
input int RS_APPL_MagicNumber = 20001;
|
||||
input int RS_APPL_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping BTCUSD ==="
|
||||
input string RS_BTCUSD_Symbol = "BTCUSD"; // Pepperstone may use: "BTCUSD", "BTC/USD", or "BTCUSD.c"
|
||||
input ENUM_TIMEFRAMES RS_BTCUSD_TimeFrame = PERIOD_H1;
|
||||
input int RS_BTCUSD_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_BTCUSD_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_BTCUSD_RSI_Overbought = 90;
|
||||
input double RS_BTCUSD_RSI_Oversold = 73;
|
||||
input double RS_BTCUSD_RSI_Target_Buy = 88;
|
||||
input double RS_BTCUSD_RSI_Target_Sell = 48;
|
||||
input int RS_BTCUSD_BarsToWait = 6;
|
||||
input double RS_BTCUSD_LotSize = 0.1;
|
||||
input int RS_BTCUSD_MagicNumber = 123459123;
|
||||
input int RS_BTCUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping NVDA - Pepperstone US ==="
|
||||
input string RS_NVDA_Symbol = "NVDA.US"; // Try: "NVDA.US", "NASDAQ:NVDA", or "NVDA"
|
||||
input ENUM_TIMEFRAMES RS_NVDA_TimeFrame = PERIOD_M15;
|
||||
input int RS_NVDA_RSI_Period = 8;
|
||||
input ENUM_APPLIED_PRICE RS_NVDA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_NVDA_RSI_Overbought = 36;
|
||||
input double RS_NVDA_RSI_Oversold = 38;
|
||||
input double RS_NVDA_RSI_Target_Buy = 90;
|
||||
input double RS_NVDA_RSI_Target_Sell = 70;
|
||||
input int RS_NVDA_BarsToWait = 5;
|
||||
input double RS_NVDA_LotSize = 50;
|
||||
input int RS_NVDA_MagicNumber = 20003;
|
||||
input int RS_NVDA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping TSLA - Pepperstone US ==="
|
||||
input string RS_TSLA_Symbol = "TSLA.US"; // Try: "TSLA.US", "NASDAQ:TSLA", or "TSLA"
|
||||
input ENUM_TIMEFRAMES RS_TSLA_TimeFrame = PERIOD_H1;
|
||||
input int RS_TSLA_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_TSLA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_TSLA_RSI_Overbought = 54;
|
||||
input double RS_TSLA_RSI_Oversold = 73;
|
||||
input double RS_TSLA_RSI_Target_Buy = 87;
|
||||
input double RS_TSLA_RSI_Target_Sell = 33;
|
||||
input int RS_TSLA_BarsToWait = 1;
|
||||
input double RS_TSLA_LotSize = 50;
|
||||
input int RS_TSLA_MagicNumber = 125421321;
|
||||
input int RS_TSLA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping XAUUSD ==="
|
||||
input string RS_XAUUSD_Symbol = "XAUUSD";
|
||||
input ENUM_TIMEFRAMES RS_XAUUSD_TimeFrame = PERIOD_H1;
|
||||
input int RS_XAUUSD_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_XAUUSD_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_XAUUSD_RSI_Overbought = 71;
|
||||
input double RS_XAUUSD_RSI_Oversold = 57;
|
||||
input double RS_XAUUSD_RSI_Target_Buy = 80;
|
||||
input double RS_XAUUSD_RSI_Target_Sell = 57;
|
||||
input int RS_XAUUSD_BarsToWait = 4;
|
||||
input double RS_XAUUSD_LotSize = 0.1;
|
||||
input int RS_XAUUSD_MagicNumber = 129102315;
|
||||
input int RS_XAUUSD_Slippage = 3;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - DarvasBox |
|
||||
//+------------------------------------------------------------------+
|
||||
struct DarvasBoxData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
double boxHigh;
|
||||
double boxLow;
|
||||
bool boxFormed;
|
||||
datetime lastBoxTime;
|
||||
string boxName;
|
||||
double minStopLevel;
|
||||
double point;
|
||||
CTrade trade;
|
||||
int maHandle;
|
||||
int volumeHandle;
|
||||
datetime lastBarTime;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - EMA Slope Distance |
|
||||
//+------------------------------------------------------------------+
|
||||
struct EMASlopeData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int ema_handle;
|
||||
double ema_array[];
|
||||
datetime letzte_überwachung_zeit;
|
||||
bool überwachung_aktiv;
|
||||
bool preis_trigger_aktiv;
|
||||
bool steigung_trigger_aktiv;
|
||||
int ticket;
|
||||
CTrade trade;
|
||||
int trades_in_current_crossover;
|
||||
bool crossover_detected;
|
||||
datetime trade_open_time;
|
||||
datetime last_bar_time;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI CrossOver Reversal |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSICrossOverData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
int emaHandle;
|
||||
double previousRSIDef;
|
||||
CTrade trade;
|
||||
datetime lastTradeTime;
|
||||
datetime bartime;
|
||||
bool WeekDays[7];
|
||||
datetime lastBarTime;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI MidPoint Hijack |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSIMidPointData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
int rsiReverseHandle;
|
||||
int emaHandle;
|
||||
bool rsiOverbought;
|
||||
bool rsiOversold;
|
||||
bool rsiReverseOverbought;
|
||||
bool rsiReverseOversold;
|
||||
CTrade trade;
|
||||
CPositionInfo positionInfo;
|
||||
bool emaCrossBuySignal;
|
||||
bool emaCrossSellSignal;
|
||||
int emaCrossSignalBar;
|
||||
datetime lastBarTime;
|
||||
datetime rsiReverseLastCloseTime;
|
||||
bool rsiReverseInCooldown;
|
||||
double lastBarRSI;
|
||||
double lastBarRSIReverse;
|
||||
double lastBarEMA;
|
||||
double lastBarClose;
|
||||
double lastBarEMAPrev;
|
||||
double lastBarClosePrev;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI Scalping |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSIScalpingData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
CTrade trade;
|
||||
int rsi_handle;
|
||||
double rsi_buffer[];
|
||||
double rsi_prev;
|
||||
double rsi_current;
|
||||
double rsi_two_bars_ago;
|
||||
bool position_open;
|
||||
ulong position_ticket;
|
||||
ENUM_POSITION_TYPE current_position_type;
|
||||
datetime last_bar_time;
|
||||
bool rsi_against_position;
|
||||
int bars_against_count;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Strategy Instances |
|
||||
//+------------------------------------------------------------------+
|
||||
DarvasBoxData dbData;
|
||||
EMASlopeData esData;
|
||||
RSICrossOverData rcData;
|
||||
RSIMidPointData rmData;
|
||||
RSIScalpingData rsAPPLData;
|
||||
RSIScalpingData rsBTCUSDData;
|
||||
RSIScalpingData rsNVDAData;
|
||||
RSIScalpingData rsTSLAData;
|
||||
RSIScalpingData rsXAUUSDData;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables for Dynamic Lot Sizes |
|
||||
//+------------------------------------------------------------------+
|
||||
// All strategies start with minimum lot size for safety (will be adjusted by performance evaluator)
|
||||
double g_DB_LotSize = 0.01; // DarvasBox uses fixed lot size
|
||||
double g_ES_LotSize = 0.01; // EMA Slope Distance - start with minimum
|
||||
double g_RC_LotSize = 0.01; // RSI CrossOver Reversal - start with minimum
|
||||
double g_RM_LotSize = 0.01; // RSI MidPoint Hijack - start with minimum
|
||||
double g_RS_APPL_LotSize = 5.0; // Stock - start with stock minimum (5.0)
|
||||
double g_RS_BTCUSD_LotSize = 0.01; // Crypto - start with forex minimum (0.01)
|
||||
double g_RS_NVDA_LotSize = 5.0; // Stock - start with stock minimum (5.0)
|
||||
double g_RS_TSLA_LotSize = 5.0; // Stock - start with stock minimum (5.0)
|
||||
double g_RS_XAUUSD_LotSize = 0.01; // Forex - start with forex minimum (0.01)
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
int initResult = INIT_SUCCEEDED;
|
||||
|
||||
// Initialize Performance Evaluator
|
||||
InitPerformanceTracking();
|
||||
|
||||
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
|
||||
if(EnableDarvasBox)
|
||||
{
|
||||
if(!InitDarvasBox(DB_Symbol))
|
||||
Print("Warning: DarvasBox strategy failed to initialize for symbol '", DB_Symbol, "'");
|
||||
else
|
||||
RegisterStrategy("DarvasBox", DB_MagicNumber, 0.01, DB_Symbol); // Fixed lot size
|
||||
}
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
{
|
||||
if(!InitEMASlopeDistance(ES_Symbol))
|
||||
Print("Warning: EMASlopeDistance strategy failed to initialize for symbol '", ES_Symbol, "'");
|
||||
else
|
||||
{
|
||||
RegisterStrategy("EMASlopeDistance", ES_MagicNumber, ES_LotGröße, ES_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(ES_Symbol);
|
||||
g_ES_LotSize = minLot;
|
||||
}
|
||||
}
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
{
|
||||
if(!InitRSICrossOverReversal(RC_Symbol))
|
||||
Print("Warning: RSICrossOverReversal strategy failed to initialize for symbol '", RC_Symbol, "'");
|
||||
else
|
||||
{
|
||||
RegisterStrategy("RSICrossOverReversal", RC_MagicNumber, RC_lotSize, RC_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RC_Symbol);
|
||||
g_RC_LotSize = minLot;
|
||||
}
|
||||
}
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
{
|
||||
if(!InitRSIMidPointHijack(RM_Symbol))
|
||||
Print("Warning: RSIMidPointHijack strategy failed to initialize for symbol '", RM_Symbol, "'");
|
||||
else
|
||||
{
|
||||
RegisterStrategy("RSIMidPointHijack", RM_InpMagicNumberRSIFollow, RM_InpLotSize, RM_Symbol);
|
||||
RegisterStrategy("RSIMidPointHijack_Reverse", RM_InpMagicNumberRSIReverse, RM_InpLotSize, RM_Symbol);
|
||||
RegisterStrategy("RSIMidPointHijack_EMACross", RM_InpMagicNumberEMACross, RM_InpLotSize, RM_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RM_Symbol);
|
||||
g_RM_LotSize = minLot;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize RSI Scalping strategies - don't fail entire EA if symbol unavailable
|
||||
if(EnableRSIScalpingAPPL)
|
||||
{
|
||||
InitRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price, RS_APPL_MagicNumber, RS_APPL_Slippage);
|
||||
RegisterStrategy("RSIScalpingAPPL", RS_APPL_MagicNumber, RS_APPL_LotSize, RS_APPL_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RS_APPL_Symbol);
|
||||
g_RS_APPL_LotSize = minLot;
|
||||
}
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
{
|
||||
InitRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price, RS_BTCUSD_MagicNumber, RS_BTCUSD_Slippage);
|
||||
RegisterStrategy("RSIScalpingBTCUSD", RS_BTCUSD_MagicNumber, RS_BTCUSD_LotSize, RS_BTCUSD_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RS_BTCUSD_Symbol);
|
||||
g_RS_BTCUSD_LotSize = minLot;
|
||||
}
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
{
|
||||
InitRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price, RS_NVDA_MagicNumber, RS_NVDA_Slippage);
|
||||
RegisterStrategy("RSIScalpingNVDA", RS_NVDA_MagicNumber, RS_NVDA_LotSize, RS_NVDA_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RS_NVDA_Symbol);
|
||||
g_RS_NVDA_LotSize = minLot;
|
||||
}
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
{
|
||||
InitRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price, RS_TSLA_MagicNumber, RS_TSLA_Slippage);
|
||||
RegisterStrategy("RSIScalpingTSLA", RS_TSLA_MagicNumber, RS_TSLA_LotSize, RS_TSLA_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RS_TSLA_Symbol);
|
||||
g_RS_TSLA_LotSize = minLot;
|
||||
}
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
{
|
||||
InitRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price, RS_XAUUSD_MagicNumber, RS_XAUUSD_Slippage);
|
||||
RegisterStrategy("RSIScalpingXAUUSD", RS_XAUUSD_MagicNumber, RS_XAUUSD_LotSize, RS_XAUUSD_Symbol);
|
||||
// Start with minimum lot size (will be adjusted by performance evaluator)
|
||||
double minLot = GetMinLotSizeForSymbol(RS_XAUUSD_Symbol);
|
||||
g_RS_XAUUSD_LotSize = minLot;
|
||||
}
|
||||
|
||||
// Load adjusted lot sizes from performance evaluator
|
||||
if(PE_EnableAutoAdjustment)
|
||||
{
|
||||
double adjustedLot;
|
||||
adjustedLot = GetStrategyLotSize("EMASlopeDistance", ES_MagicNumber);
|
||||
if(adjustedLot > 0) g_ES_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSICrossOverReversal", RC_MagicNumber);
|
||||
if(adjustedLot > 0) g_RC_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIMidPointHijack", RM_InpMagicNumberRSIFollow);
|
||||
if(adjustedLot > 0) g_RM_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingAPPL", RS_APPL_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_APPL_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingBTCUSD", RS_BTCUSD_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_BTCUSD_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingNVDA", RS_NVDA_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_NVDA_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingTSLA", RS_TSLA_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_TSLA_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingXAUUSD", RS_XAUUSD_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_XAUUSD_LotSize = adjustedLot;
|
||||
}
|
||||
|
||||
Print("United EA initialized. Active strategies: ",
|
||||
(EnableDarvasBox ? "DarvasBox " : ""),
|
||||
(EnableEMASlopeDistance ? "EMASlope " : ""),
|
||||
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
|
||||
(EnableRSIMidPointHijack ? "RSIMidPoint " : ""),
|
||||
(EnableRSIScalpingAPPL ? "RSIScalpingAPPL " : ""),
|
||||
(EnableRSIScalpingBTCUSD ? "RSIScalpingBTCUSD " : ""),
|
||||
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
|
||||
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
|
||||
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""));
|
||||
|
||||
if(PE_EnableLogging)
|
||||
Print(GetPerformanceSummary());
|
||||
|
||||
return initResult;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(EnableDarvasBox)
|
||||
DeinitDarvasBox();
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
DeinitEMASlopeDistance();
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
DeinitRSICrossOverReversal();
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
DeinitRSIMidPointHijack();
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
DeinitRSIScalping(rsAPPLData);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
DeinitRSIScalping(rsBTCUSDData);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
DeinitRSIScalping(rsNVDAData);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
DeinitRSIScalping(rsTSLAData);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
DeinitRSIScalping(rsXAUUSDData);
|
||||
|
||||
Print("United EA deinitialized. Reason: ", reason);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Process performance evaluation (checks for quarter end and adjusts lot sizes)
|
||||
ProcessPerformanceEvaluation();
|
||||
|
||||
// Update lot sizes from performance evaluator if auto-adjustment is enabled
|
||||
if(PE_EnableAutoAdjustment)
|
||||
{
|
||||
double adjustedLot;
|
||||
adjustedLot = GetStrategyLotSize("EMASlopeDistance", ES_MagicNumber);
|
||||
if(adjustedLot > 0) g_ES_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSICrossOverReversal", RC_MagicNumber);
|
||||
if(adjustedLot > 0) g_RC_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIMidPointHijack", RM_InpMagicNumberRSIFollow);
|
||||
if(adjustedLot > 0) g_RM_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingAPPL", RS_APPL_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_APPL_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingBTCUSD", RS_BTCUSD_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_BTCUSD_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingNVDA", RS_NVDA_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_NVDA_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingTSLA", RS_TSLA_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_TSLA_LotSize = adjustedLot;
|
||||
|
||||
adjustedLot = GetStrategyLotSize("RSIScalpingXAUUSD", RS_XAUUSD_MagicNumber);
|
||||
if(adjustedLot > 0) g_RS_XAUUSD_LotSize = adjustedLot;
|
||||
}
|
||||
|
||||
if(EnableDarvasBox)
|
||||
ProcessDarvasBox(DB_Symbol);
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
ProcessEMASlopeDistance(ES_Symbol);
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
ProcessRSICrossOverReversal(RC_Symbol);
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
ProcessRSIMidPointHijack(RM_Symbol);
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
ProcessRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price,
|
||||
RS_APPL_RSI_Overbought, RS_APPL_RSI_Oversold, RS_APPL_RSI_Target_Buy, RS_APPL_RSI_Target_Sell,
|
||||
RS_APPL_BarsToWait, g_RS_APPL_LotSize, RS_APPL_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
ProcessRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price,
|
||||
RS_BTCUSD_RSI_Overbought, RS_BTCUSD_RSI_Oversold, RS_BTCUSD_RSI_Target_Buy, RS_BTCUSD_RSI_Target_Sell,
|
||||
RS_BTCUSD_BarsToWait, g_RS_BTCUSD_LotSize, RS_BTCUSD_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
ProcessRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price,
|
||||
RS_NVDA_RSI_Overbought, RS_NVDA_RSI_Oversold, RS_NVDA_RSI_Target_Buy, RS_NVDA_RSI_Target_Sell,
|
||||
RS_NVDA_BarsToWait, g_RS_NVDA_LotSize, RS_NVDA_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
ProcessRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price,
|
||||
RS_TSLA_RSI_Overbought, RS_TSLA_RSI_Oversold, RS_TSLA_RSI_Target_Buy, RS_TSLA_RSI_Target_Sell,
|
||||
RS_TSLA_BarsToWait, g_RS_TSLA_LotSize, RS_TSLA_MagicNumber);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
ProcessRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price,
|
||||
RS_XAUUSD_RSI_Overbought, RS_XAUUSD_RSI_Oversold, RS_XAUUSD_RSI_Target_Buy, RS_XAUUSD_RSI_Target_Sell,
|
||||
RS_XAUUSD_BarsToWait, g_RS_XAUUSD_LotSize, RS_XAUUSD_MagicNumber);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Include strategy implementations |
|
||||
//+------------------------------------------------------------------+
|
||||
#include "Strategies/DarvasBoxStrategy.mqh"
|
||||
#include "Strategies/EMASlopeDistanceStrategy.mqh"
|
||||
#include "Strategies/RSICrossOverReversalStrategy.mqh"
|
||||
#include "Strategies/RSIMidPointHijackStrategy.mqh"
|
||||
#include "Strategies/RSIScalpingStrategy.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,45 @@
|
||||
; RSIFollowReverseEMACross (RSIMidPointHijackBTCUSD\main.mq5) — optimization preset
|
||||
; Strategy Tester → Inputs → Load
|
||||
; Format: Name=value||start||step||stop||Y|N
|
||||
;
|
||||
; Timeframe: leave N (ENUM not a linear range). Set manually or duplicate preset per TF.
|
||||
; General Settings
|
||||
InpTimeframe=16385||16385||0||16385||N
|
||||
InpLotSize=0.02||0.02||0.001000||0.100000||N
|
||||
InpMagicNumberRSIFollow=1001||1001||1||10010||N
|
||||
InpMagicNumberRSIReverse=1002||1002||1||10020||N
|
||||
InpMagicNumberEMACross=1003||1003||1||10030||N
|
||||
; Strategy Switches
|
||||
InpEnableRSIFollow=true||false||0||true||Y
|
||||
InpEnableRSIReverse=true||false||0||true||Y
|
||||
InpEnableEMACross=true||false||0||true||Y
|
||||
InpEnableStrategyLock=false||false||0||true||Y
|
||||
InpLockProfitThreshold=0.0||0.0||5.0||200.0||Y
|
||||
InpCloseOppositeTrades=false||false||0||true||Y
|
||||
; RSI Follow Strategy
|
||||
InpRSIPeriod=32||14||2||48||Y
|
||||
InpRSIOverbought=78||65||2||88||Y
|
||||
InpRSIOversold=46||20||2||50||Y
|
||||
InpRSIExitLevel=44||35||1||55||Y
|
||||
InpRSIFollowStartHour=23||20||1||23||Y
|
||||
InpRSIFollowEndHour=8||4||1||12||Y
|
||||
InpRSIFollowCloseOutsideHours=false||false||0||true||Y
|
||||
; RSI Reverse Strategy
|
||||
InpRSIReversePeriod=59||28||3||80||Y
|
||||
InpRSIReverseOverbought=51||48||1||78||Y
|
||||
InpRSIReverseOversold=49||20||2||55||Y
|
||||
InpRSIReverseCrossLevel=53||45||1||60||Y
|
||||
InpRSIReverseExitLevel=48||35||1||55||Y
|
||||
InpRSIReverseStartHour=7||0||1||12||Y
|
||||
InpRSIReverseEndHour=13||10||1||18||Y
|
||||
InpRSIReverseCloseOutsideHours=false||false||0||true||Y
|
||||
InpRSIReverseCooldownBars=15||0||3||30||Y
|
||||
InpRSIReverseCooldownOnLoss=true||false||0||true||Y
|
||||
; EMA Cross Strategy
|
||||
InpEMAPeriod=120||60||10||200||Y
|
||||
InpEMACrossStartHour=8||0||1||12||Y
|
||||
InpEMACrossEndHour=14||12||1||20||Y
|
||||
InpEMACrossCloseOutsideHours=true||false||0||true||Y
|
||||
InpUseEMADistanceEntry=true||false||0||true||Y
|
||||
InpEMADistancePips=160.0||40.0||20.0||400.0||Y
|
||||
InpEMADistancePeriod=26||10||2||40||Y
|
||||
@@ -0,0 +1,604 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIFollowReverseEMACrossOver.mq5 |
|
||||
//| Copyright 2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2024, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include <Trade\PositionInfo.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
|
||||
// Input Parameters
|
||||
input group "General Settings"
|
||||
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_H1; // Trading Timeframe
|
||||
input double InpLotSize = 0.02; // Lot Size
|
||||
input int InpMagicNumberRSIFollow = 1001; // Magic Number RSI Follow
|
||||
input int InpMagicNumberRSIReverse = 1002;// Magic Number RSI Reverse
|
||||
input int InpMagicNumberEMACross = 1003; // Magic Number EMA Cross
|
||||
|
||||
input group "Strategy Switches"
|
||||
input bool InpEnableRSIFollow = true; // Enable RSI Follow Strategy
|
||||
input bool InpEnableRSIReverse = true; // Enable RSI Reverse Strategy
|
||||
input bool InpEnableEMACross = true; // Enable EMA Cross Strategy
|
||||
input bool InpEnableStrategyLock = false; // Enable Strategy Lock
|
||||
input double InpLockProfitThreshold = 0.0; // Lock Profit Threshold (pips)
|
||||
input bool InpCloseOppositeTrades = false; // Close Opposite Trades When Profiting
|
||||
|
||||
input group "RSI Follow Strategy"
|
||||
input int InpRSIPeriod = 32; // RSI Period
|
||||
input int InpRSIOverbought = 78; // RSI Overbought Level
|
||||
input int InpRSIOversold = 46; // RSI Oversold Level
|
||||
input int InpRSIExitLevel = 44; // RSI Exit Level
|
||||
input int InpRSIFollowStartHour = 23; // RSI Follow Start Hour (0-23)
|
||||
input int InpRSIFollowEndHour = 8; // RSI Follow End Hour (0-23)
|
||||
input bool InpRSIFollowCloseOutsideHours = false; // Close trades outside trading hours
|
||||
|
||||
input group "RSI Reverse Strategy"
|
||||
input int InpRSIReversePeriod = 59; // RSI Period
|
||||
input int InpRSIReverseOverbought = 51; // RSI Overbought Level
|
||||
input int InpRSIReverseOversold = 49; // RSI Oversold Level
|
||||
input int InpRSIReverseCrossLevel = 53; // RSI Cross Level
|
||||
input int InpRSIReverseExitLevel = 48; // RSI Exit Level
|
||||
input int InpRSIReverseStartHour = 7; // RSI Reverse Start Hour (0-23)
|
||||
input int InpRSIReverseEndHour = 13; // RSI Reverse End Hour (0-23)
|
||||
input bool InpRSIReverseCloseOutsideHours = false; // Close trades outside trading hours
|
||||
input int InpRSIReverseCooldownBars = 15; // RSI Reverse Cooldown (bars)
|
||||
input bool InpRSIReverseCooldownOnLoss = true; // Apply cooldown only on loss
|
||||
|
||||
input group "EMA Cross Strategy"
|
||||
input int InpEMAPeriod = 120; // EMA Period
|
||||
input int InpEMACrossStartHour = 8; // EMA Cross Start Hour (0-23)
|
||||
input int InpEMACrossEndHour = 14; // EMA Cross End Hour (0-23)
|
||||
input bool InpEMACrossCloseOutsideHours = true; // Close trades outside trading hours
|
||||
input bool InpUseEMADistanceEntry = true; // Use EMA Distance Entry
|
||||
input double InpEMADistancePips = 160.0; // EMA Distance Threshold (pips)
|
||||
input int InpEMADistancePeriod = 26; // EMA Distance Period (bars)
|
||||
|
||||
// Global Variables
|
||||
int rsiHandle;
|
||||
int rsiReverseHandle;
|
||||
int emaHandle;
|
||||
bool rsiOverbought = false;
|
||||
bool rsiOversold = false;
|
||||
bool rsiReverseOverbought = false;
|
||||
bool rsiReverseOversold = false;
|
||||
CTrade trade;
|
||||
CPositionInfo positionInfo;
|
||||
bool emaCrossBuySignal = false;
|
||||
bool emaCrossSellSignal = false;
|
||||
int emaCrossSignalBar = 0;
|
||||
datetime lastBarTime = 0;
|
||||
datetime rsiReverseLastCloseTime = 0;
|
||||
bool rsiReverseInCooldown = false;
|
||||
double lastBarRSI = 0; // Store last bar's RSI value
|
||||
double lastBarRSIReverse = 0; // Store last bar's RSI Reverse value
|
||||
double lastBarEMA = 0; // Store last bar's EMA value
|
||||
double lastBarClose = 0; // Store last bar's close value
|
||||
double lastBarEMAPrev = 0; // Store previous bar's EMA value
|
||||
double lastBarClosePrev = 0; // Store previous bar's close value
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Initialize indicators
|
||||
rsiHandle = iRSI(_Symbol, InpTimeframe, InpRSIPeriod, PRICE_CLOSE);
|
||||
rsiReverseHandle = iRSI(_Symbol, InpTimeframe, InpRSIReversePeriod, PRICE_CLOSE);
|
||||
emaHandle = iMA(_Symbol, InpTimeframe, InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
|
||||
if(rsiHandle == INVALID_HANDLE || rsiReverseHandle == INVALID_HANDLE || emaHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Error creating indicators");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Initialize trade settings
|
||||
trade.SetExpertMagicNumber(InpMagicNumberRSIFollow);
|
||||
trade.SetMarginMode();
|
||||
trade.SetTypeFillingBySymbol(_Symbol);
|
||||
trade.SetDeviationInPoints(10);
|
||||
|
||||
// Initialize last bar time
|
||||
datetime time[];
|
||||
if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
lastBarTime = time[0];
|
||||
}
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if new bar has formed |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime time[];
|
||||
if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
if(time[0] != lastBarTime)
|
||||
{
|
||||
lastBarTime = time[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
// Release indicator handles
|
||||
IndicatorRelease(rsiHandle);
|
||||
IndicatorRelease(rsiReverseHandle);
|
||||
IndicatorRelease(emaHandle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if current time is within trading hours |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsWithinTradingHours(int startHour, int endHour)
|
||||
{
|
||||
MqlDateTime currentTime;
|
||||
TimeToStruct(TimeCurrent(), currentTime);
|
||||
|
||||
if(startHour <= endHour)
|
||||
{
|
||||
return (currentTime.hour >= startHour && currentTime.hour < endHour);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (currentTime.hour >= startHour || currentTime.hour < endHour);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if position exists for given magic number AND symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
bool HasPosition(int magic)
|
||||
{
|
||||
// Use helper function that verifies BOTH symbol AND magic number for THIS EA
|
||||
return PositionExistsByMagic(_Symbol, magic);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if any strategy has profitable position |
|
||||
//+------------------------------------------------------------------+
|
||||
bool HasProfitablePosition(int excludeMagic)
|
||||
{
|
||||
bool hasProfitable = false;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(positionInfo.SelectByIndex(i))
|
||||
{
|
||||
if(positionInfo.Magic() != excludeMagic)
|
||||
{
|
||||
double profit = positionInfo.Profit();
|
||||
if(profit > InpLockProfitThreshold * _Point)
|
||||
{
|
||||
hasProfitable = true;
|
||||
// If enabled, close opposite trades
|
||||
if(InpCloseOppositeTrades)
|
||||
{
|
||||
// Check if this is an opposite trade to the excluded magic number
|
||||
if((excludeMagic == InpMagicNumberRSIFollow && positionInfo.Magic() == InpMagicNumberRSIReverse) ||
|
||||
(excludeMagic == InpMagicNumberRSIReverse && positionInfo.Magic() == InpMagicNumberRSIFollow) ||
|
||||
(excludeMagic == InpMagicNumberEMACross && (positionInfo.Magic() == InpMagicNumberRSIReverse || positionInfo.Magic() == InpMagicNumberRSIFollow)) ||
|
||||
((excludeMagic == InpMagicNumberRSIFollow || excludeMagic == InpMagicNumberRSIReverse) && positionInfo.Magic() == InpMagicNumberEMACross))
|
||||
{
|
||||
ClosePosition(positionInfo.Magic());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasProfitable;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for RSI Follow Strategy signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckRSIFollowStrategy()
|
||||
{
|
||||
// Check if within trading hours
|
||||
if(!IsWithinTradingHours(InpRSIFollowStartHour, InpRSIFollowEndHour))
|
||||
{
|
||||
if(InpRSIFollowCloseOutsideHours)
|
||||
{
|
||||
if(HasPosition(InpMagicNumberRSIFollow))
|
||||
{
|
||||
ClosePosition(InpMagicNumberRSIFollow);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check strategy lock
|
||||
if(InpEnableStrategyLock && HasProfitablePosition(InpMagicNumberRSIFollow))
|
||||
return;
|
||||
|
||||
// Use lastBarRSI instead of copying buffer
|
||||
if(lastBarRSI > InpRSIOverbought)
|
||||
rsiOverbought = true;
|
||||
else if(lastBarRSI < InpRSIOversold)
|
||||
rsiOversold = true;
|
||||
|
||||
// Check for entry signals
|
||||
if(rsiOverbought && lastBarRSI < InpRSIExitLevel)
|
||||
{
|
||||
// Sell signal
|
||||
if(!HasPosition(InpMagicNumberRSIFollow))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberRSIFollow);
|
||||
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "RSI Follow");
|
||||
}
|
||||
rsiOverbought = false;
|
||||
}
|
||||
else if(rsiOversold && lastBarRSI > InpRSIExitLevel)
|
||||
{
|
||||
// Buy signal
|
||||
if(!HasPosition(InpMagicNumberRSIFollow))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberRSIFollow);
|
||||
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "RSI Follow");
|
||||
}
|
||||
rsiOversold = false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if RSI Reverse is in cooldown |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsRSIReverseInCooldown()
|
||||
{
|
||||
if(InpRSIReverseCooldownBars <= 0)
|
||||
return false;
|
||||
|
||||
if(!rsiReverseInCooldown)
|
||||
return false;
|
||||
|
||||
datetime time[];
|
||||
if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
datetime currentBarTime = time[0];
|
||||
datetime cooldownEndTime = rsiReverseLastCloseTime + InpRSIReverseCooldownBars * PeriodSeconds(InpTimeframe);
|
||||
|
||||
if(currentBarTime >= cooldownEndTime)
|
||||
{
|
||||
rsiReverseInCooldown = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for RSI Reverse Strategy signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckRSIReverseStrategy()
|
||||
{
|
||||
// Check if within trading hours
|
||||
if(!IsWithinTradingHours(InpRSIReverseStartHour, InpRSIReverseEndHour))
|
||||
{
|
||||
if(InpRSIReverseCloseOutsideHours)
|
||||
{
|
||||
if(HasPosition(InpMagicNumberRSIReverse))
|
||||
{
|
||||
ClosePosition(InpMagicNumberRSIReverse);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check strategy lock
|
||||
if(InpEnableStrategyLock && HasProfitablePosition(InpMagicNumberRSIReverse))
|
||||
return;
|
||||
|
||||
// Check cooldown
|
||||
if(IsRSIReverseInCooldown())
|
||||
return;
|
||||
|
||||
// Use lastBarRSIReverse instead of copying buffer
|
||||
if(lastBarRSIReverse > InpRSIReverseOverbought)
|
||||
rsiReverseOverbought = true;
|
||||
else if(lastBarRSIReverse < InpRSIReverseOversold)
|
||||
rsiReverseOversold = true;
|
||||
|
||||
// Check for entry signals
|
||||
if(rsiReverseOverbought && lastBarRSIReverse < InpRSIReverseCrossLevel)
|
||||
{
|
||||
// Sell signal
|
||||
if(!HasPosition(InpMagicNumberRSIReverse))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberRSIReverse);
|
||||
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "RSI Reverse");
|
||||
}
|
||||
rsiReverseOverbought = false;
|
||||
}
|
||||
else if(rsiReverseOversold && lastBarRSIReverse > InpRSIReverseCrossLevel)
|
||||
{
|
||||
// Buy signal
|
||||
if(!HasPosition(InpMagicNumberRSIReverse))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberRSIReverse);
|
||||
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "RSI Reverse");
|
||||
}
|
||||
rsiReverseOversold = false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for EMA Cross Strategy signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEMACrossStrategy()
|
||||
{
|
||||
// Check if within trading hours
|
||||
if(!IsWithinTradingHours(InpEMACrossStartHour, InpEMACrossEndHour))
|
||||
{
|
||||
if(InpEMACrossCloseOutsideHours)
|
||||
{
|
||||
if(HasPosition(InpMagicNumberEMACross))
|
||||
{
|
||||
ClosePosition(InpMagicNumberEMACross);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check strategy lock
|
||||
if(InpEnableStrategyLock && HasProfitablePosition(InpMagicNumberEMACross))
|
||||
return;
|
||||
|
||||
// Check for cross signals using stored values
|
||||
if(lastBarEMAPrev < lastBarClosePrev && lastBarEMA > lastBarClose)
|
||||
{
|
||||
// Buy cross signal
|
||||
emaCrossBuySignal = true;
|
||||
emaCrossSellSignal = false;
|
||||
emaCrossSignalBar = 0;
|
||||
}
|
||||
else if(lastBarEMAPrev > lastBarClosePrev && lastBarEMA < lastBarClose)
|
||||
{
|
||||
// Sell cross signal
|
||||
emaCrossSellSignal = true;
|
||||
emaCrossBuySignal = false;
|
||||
emaCrossSignalBar = 0;
|
||||
}
|
||||
|
||||
// Check for distance entry conditions
|
||||
if(InpUseEMADistanceEntry)
|
||||
{
|
||||
if(emaCrossBuySignal)
|
||||
{
|
||||
// Check if price has moved above EMA by the required distance for the required period
|
||||
bool distanceConditionMet = true;
|
||||
double emaHistory[], closeHistory[];
|
||||
ArraySetAsSeries(emaHistory, true);
|
||||
ArraySetAsSeries(closeHistory, true);
|
||||
|
||||
if(CopyBuffer(emaHandle, 0, 0, InpEMADistancePeriod, emaHistory) > 0 &&
|
||||
CopyClose(_Symbol, InpTimeframe, 0, InpEMADistancePeriod, closeHistory) > 0)
|
||||
{
|
||||
for(int i = 0; i < InpEMADistancePeriod; i++)
|
||||
{
|
||||
double distance = (closeHistory[i] - emaHistory[i]) / _Point;
|
||||
if(distance < InpEMADistancePips)
|
||||
{
|
||||
distanceConditionMet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(distanceConditionMet && !HasPosition(InpMagicNumberEMACross))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberEMACross);
|
||||
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross Distance");
|
||||
emaCrossBuySignal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(emaCrossSellSignal)
|
||||
{
|
||||
// Check if price has moved below EMA by the required distance for the required period
|
||||
bool distanceConditionMet = true;
|
||||
double emaHistory[], closeHistory[];
|
||||
ArraySetAsSeries(emaHistory, true);
|
||||
ArraySetAsSeries(closeHistory, true);
|
||||
|
||||
if(CopyBuffer(emaHandle, 0, 0, InpEMADistancePeriod, emaHistory) > 0 &&
|
||||
CopyClose(_Symbol, InpTimeframe, 0, InpEMADistancePeriod, closeHistory) > 0)
|
||||
{
|
||||
for(int i = 0; i < InpEMADistancePeriod; i++)
|
||||
{
|
||||
double distance = (emaHistory[i] - closeHistory[i]) / _Point;
|
||||
if(distance < InpEMADistancePips)
|
||||
{
|
||||
distanceConditionMet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(distanceConditionMet && !HasPosition(InpMagicNumberEMACross))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberEMACross);
|
||||
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross Distance");
|
||||
emaCrossSellSignal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Original cross entry logic using stored values
|
||||
if(lastBarEMAPrev < lastBarClosePrev && lastBarEMA > lastBarClose)
|
||||
{
|
||||
// Buy signal
|
||||
if(!HasPosition(InpMagicNumberEMACross))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberEMACross);
|
||||
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross");
|
||||
}
|
||||
}
|
||||
else if(lastBarEMAPrev > lastBarClosePrev && lastBarEMA < lastBarClose)
|
||||
{
|
||||
// Sell signal
|
||||
if(!HasPosition(InpMagicNumberEMACross))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberEMACross);
|
||||
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Increment signal bar counter
|
||||
if(emaCrossBuySignal || emaCrossSellSignal)
|
||||
{
|
||||
emaCrossSignalBar++;
|
||||
// Reset signals if they're too old (optional, can be removed if not needed)
|
||||
if(emaCrossSignalBar > InpEMADistancePeriod * 2)
|
||||
{
|
||||
emaCrossBuySignal = false;
|
||||
emaCrossSellSignal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Only process on new bar
|
||||
if(!IsNewBar())
|
||||
return;
|
||||
|
||||
// Get indicator values for the new bar
|
||||
double rsi[], rsiReverse[], ema[], close[];
|
||||
ArraySetAsSeries(rsi, true);
|
||||
ArraySetAsSeries(rsiReverse, true);
|
||||
ArraySetAsSeries(ema, true);
|
||||
ArraySetAsSeries(close, true);
|
||||
|
||||
// Store previous values
|
||||
lastBarEMAPrev = lastBarEMA;
|
||||
lastBarClosePrev = lastBarClose;
|
||||
|
||||
// Get new values
|
||||
if(CopyBuffer(rsiHandle, 0, 0, 1, rsi) > 0)
|
||||
lastBarRSI = rsi[0];
|
||||
|
||||
if(CopyBuffer(rsiReverseHandle, 0, 0, 1, rsiReverse) > 0)
|
||||
lastBarRSIReverse = rsiReverse[0];
|
||||
|
||||
if(CopyBuffer(emaHandle, 0, 0, 1, ema) > 0)
|
||||
lastBarEMA = ema[0];
|
||||
|
||||
if(CopyClose(_Symbol, InpTimeframe, 0, 1, close) > 0)
|
||||
lastBarClose = close[0];
|
||||
|
||||
// Check for new signals
|
||||
if(InpEnableRSIFollow)
|
||||
CheckRSIFollowStrategy();
|
||||
if(InpEnableRSIReverse)
|
||||
CheckRSIReverseStrategy();
|
||||
if(InpEnableEMACross)
|
||||
CheckEMACrossStrategy();
|
||||
|
||||
// Check for exit conditions
|
||||
CheckExitConditions();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check exit conditions for all strategies |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckExitConditions()
|
||||
{
|
||||
if(InpEnableRSIFollow)
|
||||
{
|
||||
// Check RSI Follow exit conditions
|
||||
if(HasPosition(InpMagicNumberRSIFollow))
|
||||
{
|
||||
if((positionInfo.PositionType() == POSITION_TYPE_BUY && lastBarRSI < InpRSIExitLevel) ||
|
||||
(positionInfo.PositionType() == POSITION_TYPE_SELL && lastBarRSI > InpRSIExitLevel))
|
||||
{
|
||||
ClosePosition(InpMagicNumberRSIFollow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(InpEnableRSIReverse)
|
||||
{
|
||||
// Check RSI Reverse exit conditions
|
||||
if(HasPosition(InpMagicNumberRSIReverse))
|
||||
{
|
||||
if((positionInfo.PositionType() == POSITION_TYPE_BUY && lastBarRSIReverse < InpRSIReverseExitLevel) ||
|
||||
(positionInfo.PositionType() == POSITION_TYPE_SELL && lastBarRSIReverse > InpRSIReverseExitLevel))
|
||||
{
|
||||
ClosePosition(InpMagicNumberRSIReverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(InpEnableEMACross)
|
||||
{
|
||||
// Check EMA Cross exit conditions using stored values
|
||||
if(HasPosition(InpMagicNumberEMACross))
|
||||
{
|
||||
if((positionInfo.PositionType() == POSITION_TYPE_BUY && lastBarEMA > lastBarClose) ||
|
||||
(positionInfo.PositionType() == POSITION_TYPE_SELL && lastBarEMA < lastBarClose))
|
||||
{
|
||||
ClosePosition(InpMagicNumberEMACross);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close position by magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
void ClosePosition(int magic)
|
||||
{
|
||||
// Close position using helper that verifies symbol AND magic number for THIS EA
|
||||
// First check if position exists for this EA on this symbol
|
||||
if(!PositionExistsByMagic(_Symbol, magic))
|
||||
{
|
||||
return; // No position for this EA on this symbol
|
||||
}
|
||||
|
||||
// Get the position ticket for this EA on this symbol
|
||||
ulong ticket = GetPositionTicketByMagic(_Symbol, magic);
|
||||
if(ticket == 0)
|
||||
{
|
||||
return; // No valid ticket found
|
||||
}
|
||||
|
||||
// Check if this is RSI Reverse position and update cooldown
|
||||
if(magic == InpMagicNumberRSIReverse)
|
||||
{
|
||||
if(PositionSelectByTicketSymbolAndMagic(ticket, _Symbol, magic))
|
||||
{
|
||||
datetime time[];
|
||||
if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
rsiReverseLastCloseTime = time[0];
|
||||
// Only enter cooldown if it's a loss or if cooldown on loss is disabled
|
||||
double profit = PositionGetDouble(POSITION_PROFIT);
|
||||
if(!InpRSIReverseCooldownOnLoss || profit < 0)
|
||||
{
|
||||
rsiReverseInCooldown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the position using helper function
|
||||
ClosePositionByMagic(trade, _Symbol, magic);
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
Reference in New Issue
Block a user