123
This commit is contained in:
+14
-3
@@ -16,7 +16,9 @@
|
||||
#property copyright "Bell's ICT EA"
|
||||
#property version "4.30"
|
||||
|
||||
#define MAX_FVG_POOL 30
|
||||
#define MAX_FVG_POOL 30
|
||||
#define MAX_ORDER_HISTORY 50
|
||||
#define ORDER_HIST_DAYS 3
|
||||
|
||||
#include "EA_ICT_CL/Config.mqh"
|
||||
#include "EA_ICT_CL/State.mqh"
|
||||
@@ -48,11 +50,16 @@ int g_TradeBarIndex = -1;
|
||||
OrderPlan g_OrderPlan;
|
||||
ulong g_PendingTicket = 0;
|
||||
|
||||
OrderHistRecord g_OrderHist[MAX_ORDER_HISTORY];
|
||||
int g_OrderHistCount = 0;
|
||||
int g_NextOrderHistId = 0;
|
||||
|
||||
const string PREFIX_SWING_MIDDLE = "SwingMiddle_";
|
||||
const string PREFIX_SWING_TRIGGER = "SwingTrigger_";
|
||||
const string PREFIX_MSS_MARKER = "MSSMarker_";
|
||||
const string PREFIX_FVG_POOL = "FVGPool_";
|
||||
const string PREFIX_ORDER_VISUAL = "OrderVisual_";
|
||||
const string PREFIX_ORDER_HIST = "OrdHist_";
|
||||
const string PREFIX_DEBUG_PANEL = "DebugPanel_";
|
||||
const string PREFIX_SESSION = "Session_";
|
||||
|
||||
@@ -69,12 +76,15 @@ int OnInit()
|
||||
ZeroMemory(g_MiddleTrend); ZeroMemory(g_TriggerTrend);
|
||||
ZeroMemory(g_DailyRisk); ZeroMemory(g_OrderPlan);
|
||||
for (int i = 0; i < MAX_FVG_POOL; i++) ZeroMemory(g_FVGPool[i]);
|
||||
for (int i = 0; i < MAX_ORDER_HISTORY; i++) ZeroMemory(g_OrderHist[i]);
|
||||
g_FVGCount = 0; g_NextFVGId = 0;
|
||||
g_ActiveFVGIdx = -1; g_TradeBarIndex = -1; g_PendingTicket = 0;
|
||||
g_OrderHistCount = 0; g_NextOrderHistId = 0;
|
||||
g_State = EA_IDLE; g_BlockReason = BLOCK_NONE;
|
||||
ObjectsDeleteAll(0, PREFIX_ORDER_VISUAL);
|
||||
|
||||
UpdateAllContexts();
|
||||
ScanAndRegisterFVGs();
|
||||
UpdateFVGPool();
|
||||
DrawVisuals();
|
||||
|
||||
PrintFormat("✅ ICT EA v4.3 | H1=%s | M5=%s | Pool=%d",
|
||||
@@ -83,10 +93,11 @@ int OnInit()
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
/** Each tick: update contexts, run guards and state machine, optionally redraw. */
|
||||
/** Each tick: update contexts & FVG pool always; run state machine only when guards pass. */
|
||||
void OnTick()
|
||||
{
|
||||
UpdateAllContexts();
|
||||
UpdateFVGPool();
|
||||
if (EvaluateGuards())
|
||||
RunStateMachine();
|
||||
else if (g_State != EA_IDLE)
|
||||
|
||||
+153
-147
@@ -176,174 +176,180 @@ inline void DrawMSSMarkers()
|
||||
}
|
||||
}
|
||||
|
||||
/** Draws order plan zones/lines/labels (entry, SL, TP) when valid and debug draw on. */
|
||||
inline void DrawOrderVisualization()
|
||||
/** Helper: creates or moves a rectangle. */
|
||||
inline void SetRect(string name, datetime t1, double p1, datetime t2, double p2, color clr)
|
||||
{
|
||||
if (!InpDebugDraw) return;
|
||||
if (ObjectFind(0, name) < 0)
|
||||
ObjectCreate(0, name, OBJ_RECTANGLE, 0, t1, p1, t2, p2);
|
||||
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||||
ObjectSetInteger(0, name, OBJPROP_FILL, true);
|
||||
ObjectSetInteger(0, name, OBJPROP_BACK, true);
|
||||
ObjectMove(0, name, 0, t1, p1);
|
||||
ObjectMove(0, name, 1, t2, p2);
|
||||
}
|
||||
|
||||
string tpZoneN = PREFIX_ORDER_VISUAL + "TP_ZONE";
|
||||
string slZoneN = PREFIX_ORDER_VISUAL + "SL_ZONE";
|
||||
string entLineN = PREFIX_ORDER_VISUAL + "ENTRY_LINE";
|
||||
string slLineN = PREFIX_ORDER_VISUAL + "SL_LINE";
|
||||
string tpLineN = PREFIX_ORDER_VISUAL + "TP_LINE";
|
||||
string entLblN = PREFIX_ORDER_VISUAL + "ENTRY_LBL";
|
||||
string slLblN = PREFIX_ORDER_VISUAL + "SL_LBL";
|
||||
string tpLblN = PREFIX_ORDER_VISUAL + "TP_LBL";
|
||||
string infoLblN = PREFIX_ORDER_VISUAL + "INFO_LBL";
|
||||
/** Helper: creates or moves a horizontal trend line. */
|
||||
inline void SetHLine(string name, datetime t1, double p, datetime t2, color clr, int style, int width)
|
||||
{
|
||||
if (ObjectFind(0, name) < 0)
|
||||
ObjectCreate(0, name, OBJ_TREND, 0, t1, p, t2, p);
|
||||
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||||
ObjectSetInteger(0, name, OBJPROP_STYLE, style);
|
||||
ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
|
||||
ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false);
|
||||
ObjectMove(0, name, 0, t1, p);
|
||||
ObjectMove(0, name, 1, t2, p);
|
||||
}
|
||||
|
||||
if (!g_OrderPlan.valid || g_State == EA_IDLE)
|
||||
{
|
||||
ObjectDelete(0, tpZoneN);
|
||||
ObjectDelete(0, slZoneN);
|
||||
ObjectDelete(0, entLineN);
|
||||
ObjectDelete(0, slLineN);
|
||||
ObjectDelete(0, tpLineN);
|
||||
ObjectDelete(0, entLblN);
|
||||
ObjectDelete(0, slLblN);
|
||||
ObjectDelete(0, tpLblN);
|
||||
ObjectDelete(0, infoLblN);
|
||||
return;
|
||||
}
|
||||
/** Helper: creates or moves a text label at a chart coordinate. */
|
||||
inline void SetText(string name, datetime t, double p, string txt, color clr, int fontSize, int anchor)
|
||||
{
|
||||
if (ObjectFind(0, name) < 0)
|
||||
ObjectCreate(0, name, OBJ_TEXT, 0, t, p);
|
||||
ObjectMove(0, name, 0, t, p);
|
||||
ObjectSetString (0, name, OBJPROP_TEXT, txt);
|
||||
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||||
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
|
||||
ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
|
||||
}
|
||||
|
||||
bool isBuy = (g_OrderPlan.direction > 0);
|
||||
double entry = g_OrderPlan.entry;
|
||||
double sl = g_OrderPlan.stopLoss;
|
||||
double tp = g_OrderPlan.takeProfit;
|
||||
/** Draws one order record in TradingView style (TP/SL zones, entry line, labels). */
|
||||
inline void DrawOneOrderHist(int idx)
|
||||
{
|
||||
string sid = IntegerToString(g_OrderHist[idx].id);
|
||||
string pfx = PREFIX_ORDER_HIST + sid + "_";
|
||||
|
||||
datetime tStart = (g_TriggerTrend.lastMssTime > 0) ? g_TriggerTrend.lastMssTime : iTime(_Symbol, InpTriggerTF, 20);
|
||||
datetime tEnd = iTime(_Symbol, InpTriggerTF, 0) + PeriodSeconds(InpTriggerTF) * 25;
|
||||
bool isBuy = (g_OrderHist[idx].direction > 0);
|
||||
double entry = g_OrderHist[idx].entry;
|
||||
double sl = g_OrderHist[idx].stopLoss;
|
||||
double tp = g_OrderHist[idx].takeProfit;
|
||||
int res = g_OrderHist[idx].result;
|
||||
|
||||
datetime tStart = g_OrderHist[idx].signalTime;
|
||||
datetime tEnd;
|
||||
if (res == 0)
|
||||
tEnd = iTime(_Symbol, InpTriggerTF, 0) + PeriodSeconds(InpTriggerTF) * 10;
|
||||
else
|
||||
tEnd = g_OrderHist[idx].closeTime;
|
||||
if (tEnd <= tStart)
|
||||
tEnd = tStart + PeriodSeconds(InpTriggerTF) * 10;
|
||||
|
||||
double slPips = MathAbs(entry - sl) / _Point;
|
||||
double tpPips = MathAbs(tp - entry) / _Point;
|
||||
double rr = (slPips > 0) ? tpPips / slPips : 0;
|
||||
|
||||
color entryClr = isBuy ? C'33,150,243' : C'255,152,0';
|
||||
color tpFill = C'15,65,35';
|
||||
color slFill = C'85,15,15';
|
||||
color tpClr = C'38,166,91';
|
||||
color slClr = C'229,57,53';
|
||||
color entClr, tpZoneClr, slZoneClr, tpLineClr, slLineClr, resultClr;
|
||||
if (res == 1)
|
||||
{
|
||||
entClr = C'0,180,80';
|
||||
tpZoneClr = C'10,70,30';
|
||||
slZoneClr = C'40,15,15';
|
||||
tpLineClr = C'38,200,91';
|
||||
slLineClr = C'120,50,50';
|
||||
resultClr = C'0,220,100';
|
||||
}
|
||||
else if (res == -1)
|
||||
{
|
||||
entClr = C'220,50,50';
|
||||
tpZoneClr = C'15,40,15';
|
||||
slZoneClr = C'90,12,12';
|
||||
tpLineClr = C'50,120,50';
|
||||
slLineClr = C'229,57,53';
|
||||
resultClr = C'229,57,53';
|
||||
}
|
||||
else
|
||||
{
|
||||
entClr = isBuy ? C'33,150,243' : C'255,152,0';
|
||||
tpZoneClr = C'15,60,30';
|
||||
slZoneClr = C'75,12,12';
|
||||
tpLineClr = C'38,166,91';
|
||||
slLineClr = C'229,57,53';
|
||||
resultClr = entClr;
|
||||
}
|
||||
|
||||
double tpTop = isBuy ? tp : entry;
|
||||
double tpTop = isBuy ? tp : entry;
|
||||
double tpBot = isBuy ? entry : tp;
|
||||
if (ObjectFind(0, tpZoneN) < 0)
|
||||
ObjectCreate(0, tpZoneN, OBJ_RECTANGLE, 0, tStart, tpTop, tEnd, tpBot);
|
||||
ObjectSetInteger(0, tpZoneN, OBJPROP_COLOR, tpFill);
|
||||
ObjectSetInteger(0, tpZoneN, OBJPROP_FILL, true);
|
||||
ObjectSetInteger(0, tpZoneN, OBJPROP_BACK, true);
|
||||
ObjectMove(0, tpZoneN, 0, tStart, tpTop);
|
||||
ObjectMove(0, tpZoneN, 1, tEnd, tpBot);
|
||||
SetRect(pfx + "TP", tStart, tpTop, tEnd, tpBot, tpZoneClr);
|
||||
|
||||
double slTop = isBuy ? entry : sl;
|
||||
double slBot = isBuy ? sl : entry;
|
||||
if (ObjectFind(0, slZoneN) < 0)
|
||||
ObjectCreate(0, slZoneN, OBJ_RECTANGLE, 0, tStart, slTop, tEnd, slBot);
|
||||
ObjectSetInteger(0, slZoneN, OBJPROP_COLOR, slFill);
|
||||
ObjectSetInteger(0, slZoneN, OBJPROP_FILL, true);
|
||||
ObjectSetInteger(0, slZoneN, OBJPROP_BACK, true);
|
||||
ObjectMove(0, slZoneN, 0, tStart, slTop);
|
||||
ObjectMove(0, slZoneN, 1, tEnd, slBot);
|
||||
SetRect(pfx + "SL", tStart, slTop, tEnd, slBot, slZoneClr);
|
||||
|
||||
if (ObjectFind(0, entLineN) < 0)
|
||||
ObjectCreate(0, entLineN, OBJ_TREND, 0, tStart, entry, tEnd, entry);
|
||||
ObjectSetInteger(0, entLineN, OBJPROP_COLOR, entryClr);
|
||||
ObjectSetInteger(0, entLineN, OBJPROP_STYLE, STYLE_SOLID);
|
||||
ObjectSetInteger(0, entLineN, OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(0, entLineN, OBJPROP_RAY_RIGHT, true);
|
||||
ObjectMove(0, entLineN, 0, tStart, entry);
|
||||
ObjectMove(0, entLineN, 1, tEnd, entry);
|
||||
SetHLine(pfx + "ENT", tStart, entry, tEnd, entClr, STYLE_SOLID, 2);
|
||||
SetHLine(pfx + "TPL", tStart, tp, tEnd, tpLineClr, STYLE_DASH, 1);
|
||||
SetHLine(pfx + "SLL", tStart, sl, tEnd, slLineClr, STYLE_DASH, 1);
|
||||
|
||||
if (ObjectFind(0, tpLineN) < 0)
|
||||
ObjectCreate(0, tpLineN, OBJ_TREND, 0, tStart, tp, tEnd, tp);
|
||||
ObjectSetInteger(0, tpLineN, OBJPROP_COLOR, tpClr);
|
||||
ObjectSetInteger(0, tpLineN, OBJPROP_STYLE, STYLE_DASH);
|
||||
ObjectSetInteger(0, tpLineN, OBJPROP_WIDTH, 1);
|
||||
ObjectSetInteger(0, tpLineN, OBJPROP_RAY_RIGHT, true);
|
||||
ObjectMove(0, tpLineN, 0, tStart, tp);
|
||||
ObjectMove(0, tpLineN, 1, tEnd, tp);
|
||||
datetime lblT = tEnd + PeriodSeconds(InpTriggerTF);
|
||||
|
||||
if (ObjectFind(0, slLineN) < 0)
|
||||
ObjectCreate(0, slLineN, OBJ_TREND, 0, tStart, sl, tEnd, sl);
|
||||
ObjectSetInteger(0, slLineN, OBJPROP_COLOR, slClr);
|
||||
ObjectSetInteger(0, slLineN, OBJPROP_STYLE, STYLE_DASH);
|
||||
ObjectSetInteger(0, slLineN, OBJPROP_WIDTH, 1);
|
||||
ObjectSetInteger(0, slLineN, OBJPROP_RAY_RIGHT, true);
|
||||
ObjectMove(0, slLineN, 0, tStart, sl);
|
||||
ObjectMove(0, slLineN, 1, tEnd, sl);
|
||||
string entTxt;
|
||||
if (res == 0)
|
||||
entTxt = StringFormat("%s %.3f | %.2f lot",
|
||||
isBuy ? "BUY LIM" : "SELL LIM", entry, g_OrderHist[idx].lot);
|
||||
else
|
||||
entTxt = StringFormat("%s %.3f | %.2f lot | %s",
|
||||
isBuy ? "BUY" : "SELL", entry, g_OrderHist[idx].lot,
|
||||
(res == 1) ? "WIN" : (res == -1) ? "LOSS" : "X");
|
||||
SetText(pfx + "ELBL", lblT, entry, entTxt, entClr, 8, ANCHOR_LEFT);
|
||||
|
||||
datetime lblT = tEnd;
|
||||
SetText(pfx + "TPLBL", lblT, tp,
|
||||
StringFormat("TP %.3f | +%.0fp (%.1fR)", tp, tpPips, rr),
|
||||
tpLineClr, 7, isBuy ? ANCHOR_LEFT_LOWER : ANCHOR_LEFT_UPPER);
|
||||
|
||||
string eTxt = StringFormat(
|
||||
"%s %s | %.2f lot",
|
||||
isBuy ? "▶ BUY LIM" : "▶ SELL LIM",
|
||||
DoubleToString(entry, _Digits),
|
||||
g_OrderPlan.lot
|
||||
);
|
||||
if (ObjectFind(0, entLblN) < 0)
|
||||
ObjectCreate(0, entLblN, OBJ_TEXT, 0, lblT, entry);
|
||||
ObjectMove(0, entLblN, 0, lblT, entry);
|
||||
ObjectSetString(0, entLblN, OBJPROP_TEXT, eTxt);
|
||||
ObjectSetInteger(0, entLblN, OBJPROP_COLOR, entryClr);
|
||||
ObjectSetInteger(0, entLblN, OBJPROP_FONTSIZE, 8);
|
||||
ObjectSetInteger(0, entLblN, OBJPROP_ANCHOR, ANCHOR_LEFT);
|
||||
SetText(pfx + "SLLBL", lblT, sl,
|
||||
StringFormat("SL %.3f | -%.0fp", sl, slPips),
|
||||
slLineClr, 7, isBuy ? ANCHOR_LEFT_UPPER : ANCHOR_LEFT_LOWER);
|
||||
|
||||
string tTxt = StringFormat(
|
||||
"◎ TP %s +%.0fp (%.1fR)",
|
||||
DoubleToString(tp, _Digits),
|
||||
tpPips,
|
||||
rr
|
||||
);
|
||||
if (ObjectFind(0, tpLblN) < 0)
|
||||
ObjectCreate(0, tpLblN, OBJ_TEXT, 0, lblT, tp);
|
||||
ObjectMove(0, tpLblN, 0, lblT, tp);
|
||||
ObjectSetString(0, tpLblN, OBJPROP_TEXT, tTxt);
|
||||
ObjectSetInteger(0, tpLblN, OBJPROP_COLOR, tpClr);
|
||||
ObjectSetInteger(0, tpLblN, OBJPROP_FONTSIZE, 8);
|
||||
ObjectSetInteger(
|
||||
0,
|
||||
tpLblN,
|
||||
OBJPROP_ANCHOR,
|
||||
isBuy ? ANCHOR_LEFT_LOWER : ANCHOR_LEFT_UPPER
|
||||
);
|
||||
if (res != 0)
|
||||
{
|
||||
string resTxt = (res == 1)
|
||||
? StringFormat("✓ +$%.2f", g_OrderHist[idx].profit)
|
||||
: (res == -1)
|
||||
? StringFormat("✗ $%.2f", g_OrderHist[idx].profit)
|
||||
: "✗ cancelled";
|
||||
SetText(pfx + "RES", lblT, (entry + sl) / 2.0, resTxt, resultClr, 8, ANCHOR_LEFT);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetText(pfx + "RES", lblT, (entry + sl) / 2.0,
|
||||
StringFormat("Risk %.1f%% | %.0f:%.0fp | %.1fR", InpRiskPercent, slPips, tpPips, rr),
|
||||
C'140,140,140', 7, ANCHOR_LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
string sTxt = StringFormat(
|
||||
"✕ SL %s -%.0fp",
|
||||
DoubleToString(sl, _Digits),
|
||||
slPips
|
||||
);
|
||||
if (ObjectFind(0, slLblN) < 0)
|
||||
ObjectCreate(0, slLblN, OBJ_TEXT, 0, lblT, sl);
|
||||
ObjectMove(0, slLblN, 0, lblT, sl);
|
||||
ObjectSetString(0, slLblN, OBJPROP_TEXT, sTxt);
|
||||
ObjectSetInteger(0, slLblN, OBJPROP_COLOR, slClr);
|
||||
ObjectSetInteger(0, slLblN, OBJPROP_FONTSIZE, 8);
|
||||
ObjectSetInteger(
|
||||
0,
|
||||
slLblN,
|
||||
OBJPROP_ANCHOR,
|
||||
isBuy ? ANCHOR_LEFT_UPPER : ANCHOR_LEFT_LOWER
|
||||
);
|
||||
/** Deletes all chart objects for one order history record. */
|
||||
inline void DeleteOrderHistObjects(int id)
|
||||
{
|
||||
string sid = IntegerToString(id);
|
||||
string pfx = PREFIX_ORDER_HIST + sid + "_";
|
||||
ObjectDelete(0, pfx + "TP");
|
||||
ObjectDelete(0, pfx + "SL");
|
||||
ObjectDelete(0, pfx + "ENT");
|
||||
ObjectDelete(0, pfx + "TPL");
|
||||
ObjectDelete(0, pfx + "SLL");
|
||||
ObjectDelete(0, pfx + "ELBL");
|
||||
ObjectDelete(0, pfx + "TPLBL");
|
||||
ObjectDelete(0, pfx + "SLLBL");
|
||||
ObjectDelete(0, pfx + "RES");
|
||||
}
|
||||
|
||||
string iTxt = StringFormat(
|
||||
"Risk %.1f%% | %.0f:%.0f pips | %.1fR",
|
||||
InpRiskPercent,
|
||||
slPips,
|
||||
tpPips,
|
||||
rr
|
||||
);
|
||||
if (ObjectFind(0, infoLblN) < 0)
|
||||
ObjectCreate(
|
||||
0,
|
||||
infoLblN,
|
||||
OBJ_TEXT,
|
||||
0,
|
||||
lblT,
|
||||
(entry + sl) / 2.0
|
||||
);
|
||||
ObjectMove(0, infoLblN, 0, lblT, (entry + sl) / 2.0);
|
||||
ObjectSetString(0, infoLblN, OBJPROP_TEXT, iTxt);
|
||||
ObjectSetInteger(0, infoLblN, OBJPROP_COLOR, C'160,160,160');
|
||||
ObjectSetInteger(0, infoLblN, OBJPROP_FONTSIZE, 7);
|
||||
ObjectSetInteger(0, infoLblN, OBJPROP_ANCHOR, ANCHOR_LEFT);
|
||||
/** Draws all order history records; cleans up records older than ORDER_HIST_DAYS. */
|
||||
inline void DrawAllOrders()
|
||||
{
|
||||
if (!InpDebugDraw) return;
|
||||
|
||||
datetime cutoff = TimeCurrent() - ORDER_HIST_DAYS * 86400;
|
||||
for (int i = g_OrderHistCount - 1; i >= 0; i--)
|
||||
{
|
||||
if (g_OrderHist[i].closeTime > 0 && g_OrderHist[i].closeTime < cutoff)
|
||||
{
|
||||
DeleteOrderHistObjects(g_OrderHist[i].id);
|
||||
for (int j = i; j < g_OrderHistCount - 1; j++)
|
||||
g_OrderHist[j] = g_OrderHist[j + 1];
|
||||
g_OrderHistCount--;
|
||||
continue;
|
||||
}
|
||||
DrawOneOrderHist(i);
|
||||
}
|
||||
}
|
||||
|
||||
/** Draws one FVG record (rectangle + mid line + label) by pool index. */
|
||||
@@ -757,7 +763,7 @@ inline void DrawSessionMarkers()
|
||||
}
|
||||
}
|
||||
|
||||
/** Draws all debug visuals: swings, MSS markers, FVG pool, order, debug panel. */
|
||||
/** Draws all debug visuals: swings, MSS markers, FVG pool, orders, debug panel. */
|
||||
inline void DrawVisuals()
|
||||
{
|
||||
DrawSessionMarkers();
|
||||
@@ -766,7 +772,7 @@ inline void DrawVisuals()
|
||||
DrawFVGPool();
|
||||
DrawTriggerSwingPoints();
|
||||
DrawMSSMarkers();
|
||||
DrawOrderVisualization();
|
||||
DrawAllOrders();
|
||||
}
|
||||
|
||||
#endif // EA_ICT_CL__DRAWING_MQH
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
/** True if current time (UTC) is within London or NY session (InpLondon* / InpNY*). */
|
||||
inline bool IsSessionAllowed()
|
||||
{
|
||||
return true;
|
||||
int hourUtc = GetUTCHour(TimeCurrent());
|
||||
return IsHourInRange(hourUtc, InpLondonStartHour, InpLondonEndHour)
|
||||
|| IsHourInRange(hourUtc, InpNYStartHour, InpNYEndHour);
|
||||
|
||||
@@ -100,21 +100,9 @@ inline void ScanAndRegisterFVGs()
|
||||
if (isBrokenByClose) { record.status = FVG_USED; record.usedCase = 1; record.usedTime = breakTime; }
|
||||
else
|
||||
{
|
||||
bool isTouchedInGap = false;
|
||||
datetime touchTimeInGap = 0;
|
||||
for (int j = rightBarIndex - 1; j >= 1; j--)
|
||||
{
|
||||
bool inGap = (record.direction == DIR_UP && iLow (_Symbol, InpMiddleTF, j) <= record.high) ||
|
||||
(record.direction == DIR_DOWN && iHigh(_Symbol, InpMiddleTF, j) >= record.low);
|
||||
if (inGap) { isTouchedInGap = true; touchTimeInGap = iTime(_Symbol, InpMiddleTF, j); break; }
|
||||
}
|
||||
if (isTouchedInGap) { record.status = FVG_TOUCHED; record.touchTime = touchTimeInGap; record.triggerTrendAtTouch = g_TriggerTrend.trend; }
|
||||
else
|
||||
{
|
||||
record.status = FVG_PENDING;
|
||||
if ((int)(TimeCurrent() - record.createdTime) > InpFVGMaxAliveMin * 60)
|
||||
{ record.status = FVG_USED; record.usedCase = 0; record.usedTime = TimeCurrent(); }
|
||||
}
|
||||
record.status = FVG_PENDING;
|
||||
if ((int)(TimeCurrent() - record.createdTime) > InpFVGMaxAliveMin * 60)
|
||||
{ record.status = FVG_USED; record.usedCase = 0; record.usedTime = TimeCurrent(); }
|
||||
}
|
||||
|
||||
g_FVGPool[g_FVGCount] = record;
|
||||
@@ -161,8 +149,10 @@ inline void UpdateFVGStatuses()
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isTouched = (g_FVGPool[i].direction == DIR_UP && bid <= g_FVGPool[i].high) ||
|
||||
(g_FVGPool[i].direction == DIR_DOWN && bid >= g_FVGPool[i].low);
|
||||
double fvgRange = g_FVGPool[i].high - g_FVGPool[i].low;
|
||||
double touchDepth = fvgRange * InpFVGTouchPct / 100.0;
|
||||
bool isTouched = (g_FVGPool[i].direction == DIR_UP && bid <= g_FVGPool[i].high - touchDepth) ||
|
||||
(g_FVGPool[i].direction == DIR_DOWN && bid >= g_FVGPool[i].low + touchDepth);
|
||||
if (isTouched)
|
||||
{
|
||||
g_FVGPool[i].status = FVG_TOUCHED;
|
||||
|
||||
@@ -60,6 +60,18 @@ struct OrderPlan
|
||||
int parentFVGId;
|
||||
};
|
||||
|
||||
struct OrderHistRecord
|
||||
{
|
||||
int id;
|
||||
int direction;
|
||||
double entry, stopLoss, takeProfit, lot;
|
||||
datetime signalTime;
|
||||
datetime closeTime; // 0 nếu đang active
|
||||
int result; // 0=active, 1=TP hit, -1=SL hit, 2=cancelled
|
||||
double profit;
|
||||
int parentFVGId;
|
||||
};
|
||||
|
||||
struct DailyRiskContext
|
||||
{
|
||||
double startBalance, currentBalance;
|
||||
|
||||
@@ -84,6 +84,7 @@ inline void OnStateWaitTrigger()
|
||||
{
|
||||
g_PendingTicket = ticket;
|
||||
g_TradeBarIndex = Bars(_Symbol, InpTriggerTF);
|
||||
SaveOrderToHistory(g_FVGPool[activeIndex].mssTime);
|
||||
TransitionTo(EA_IN_TRADE);
|
||||
}
|
||||
else
|
||||
@@ -142,9 +143,10 @@ inline void OnStateInTrade()
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cancelled) { ResetToIdle("order cancelled"); return; }
|
||||
if (cancelled) { CloseActiveOrderRecord(2, 0); ResetToIdle("order cancelled"); return; }
|
||||
|
||||
bool closed = false;
|
||||
double closedProfit = 0;
|
||||
for (int i = HistoryDealsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong dealTicket = HistoryDealGetTicket(i);
|
||||
@@ -152,11 +154,13 @@ inline void OnStateInTrade()
|
||||
&& HistoryDealGetString(dealTicket, DEAL_SYMBOL) == _Symbol
|
||||
&& HistoryDealGetInteger(dealTicket, DEAL_ENTRY) == DEAL_ENTRY_OUT)
|
||||
{
|
||||
double profit = HistoryDealGetDouble(dealTicket, DEAL_PROFIT);
|
||||
if (InpDebugLog) PrintFormat("[TRADE] Closed | profit=%.2f", profit);
|
||||
closedProfit = HistoryDealGetDouble(dealTicket, DEAL_PROFIT);
|
||||
if (InpDebugLog) PrintFormat("[TRADE] Closed | profit=%.2f", closedProfit);
|
||||
closed = true; break;
|
||||
}
|
||||
}
|
||||
if (closed) CloseActiveOrderRecord(closedProfit >= 0 ? 1 : -1, closedProfit);
|
||||
else CloseActiveOrderRecord(2, 0);
|
||||
ResetToIdle(closed ? "trade closed" : "order lost");
|
||||
return;
|
||||
}
|
||||
@@ -172,14 +176,33 @@ inline void OnStateInTrade()
|
||||
&& PositionGetString(POSITION_SYMBOL) == _Symbol)
|
||||
{ hasPosition = true; break; }
|
||||
}
|
||||
if (!hasPosition) ResetToIdle("position closed");
|
||||
if (!hasPosition)
|
||||
{
|
||||
HistorySelect(TimeCurrent() - 86400, TimeCurrent());
|
||||
double lastProfit = 0;
|
||||
for (int i = HistoryDealsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong dt = HistoryDealGetTicket(i);
|
||||
if (HistoryDealGetInteger(dt, DEAL_MAGIC) == InpMagicNumber
|
||||
&& HistoryDealGetString(dt, DEAL_SYMBOL) == _Symbol
|
||||
&& HistoryDealGetInteger(dt, DEAL_ENTRY) == DEAL_ENTRY_OUT)
|
||||
{ lastProfit = HistoryDealGetDouble(dt, DEAL_PROFIT); break; }
|
||||
}
|
||||
CloseActiveOrderRecord(lastProfit >= 0 ? 1 : -1, lastProfit);
|
||||
ResetToIdle("position closed");
|
||||
}
|
||||
}
|
||||
|
||||
/** Runs state machine: updates FVG statuses, scans/registers FVGs, then handles current state. */
|
||||
inline void RunStateMachine()
|
||||
/** Keeps FVG pool up to date regardless of guard state. */
|
||||
inline void UpdateFVGPool()
|
||||
{
|
||||
UpdateFVGStatuses();
|
||||
ScanAndRegisterFVGs();
|
||||
}
|
||||
|
||||
/** Runs state machine: handles current state (entry/exit logic). */
|
||||
inline void RunStateMachine()
|
||||
{
|
||||
switch (g_State)
|
||||
{
|
||||
case EA_IDLE: OnStateIdle(); break;
|
||||
|
||||
@@ -131,4 +131,63 @@ inline ulong ExecuteLimitOrder()
|
||||
return result.order;
|
||||
}
|
||||
|
||||
/** Scans trigger TF bars from signalTime forward to find which bar first hit SL or TP. */
|
||||
inline datetime FindHitBarTime(const OrderHistRecord &rec)
|
||||
{
|
||||
int startShift = MyBarShift(_Symbol, InpTriggerTF, rec.signalTime);
|
||||
if (startShift < 0) return TimeCurrent();
|
||||
|
||||
for (int i = startShift; i >= 0; i--)
|
||||
{
|
||||
double h = iHigh(_Symbol, InpTriggerTF, i);
|
||||
double l = iLow (_Symbol, InpTriggerTF, i);
|
||||
bool hitTP = (rec.direction > 0) ? (h >= rec.takeProfit) : (l <= rec.takeProfit);
|
||||
bool hitSL = (rec.direction > 0) ? (l <= rec.stopLoss) : (h >= rec.stopLoss);
|
||||
if (hitTP || hitSL)
|
||||
return iTime(_Symbol, InpTriggerTF, i);
|
||||
}
|
||||
return TimeCurrent();
|
||||
}
|
||||
|
||||
/** Saves the current order plan into order history as active (result=0). */
|
||||
inline void SaveOrderToHistory(datetime signalTime)
|
||||
{
|
||||
if (g_OrderHistCount >= MAX_ORDER_HISTORY)
|
||||
{
|
||||
for (int j = 0; j < g_OrderHistCount - 1; j++)
|
||||
g_OrderHist[j] = g_OrderHist[j + 1];
|
||||
g_OrderHistCount--;
|
||||
}
|
||||
|
||||
int n = g_OrderHistCount;
|
||||
ZeroMemory(g_OrderHist[n]);
|
||||
g_OrderHist[n].id = g_NextOrderHistId++;
|
||||
g_OrderHist[n].direction = g_OrderPlan.direction;
|
||||
g_OrderHist[n].entry = g_OrderPlan.entry;
|
||||
g_OrderHist[n].stopLoss = g_OrderPlan.stopLoss;
|
||||
g_OrderHist[n].takeProfit = g_OrderPlan.takeProfit;
|
||||
g_OrderHist[n].lot = g_OrderPlan.lot;
|
||||
g_OrderHist[n].signalTime = signalTime;
|
||||
g_OrderHist[n].parentFVGId = g_OrderPlan.parentFVGId;
|
||||
g_OrderHistCount++;
|
||||
}
|
||||
|
||||
/** Closes the most recent active order record with given result and profit. */
|
||||
inline void CloseActiveOrderRecord(int result, double profit)
|
||||
{
|
||||
for (int i = g_OrderHistCount - 1; i >= 0; i--)
|
||||
{
|
||||
if (g_OrderHist[i].result != 0) continue;
|
||||
g_OrderHist[i].result = result;
|
||||
g_OrderHist[i].profit = profit;
|
||||
g_OrderHist[i].closeTime = FindHitBarTime(g_OrderHist[i]);
|
||||
if (InpDebugLog)
|
||||
PrintFormat("[ORDER HIST] #%d %s | profit=%.2f | close=%s",
|
||||
g_OrderHist[i].id,
|
||||
(result == 1) ? "TP HIT" : (result == -1) ? "SL HIT" : "CANCELLED",
|
||||
profit, TimeToString(g_OrderHist[i].closeTime, TIME_MINUTES));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user