302 lines
10 KiB
MQL5
302 lines
10 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Dijkstars Algo.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>
|
||
|
|
CTrade trade;
|
||
|
|
|
||
|
|
// Input Parameters
|
||
|
|
input int TakeProfit = 1000;
|
||
|
|
input int StopLoss = 385;
|
||
|
|
input double In_Lot = 0.01;
|
||
|
|
input int LeftBars = 3;
|
||
|
|
input int RightBars = 3;
|
||
|
|
input int MaxSwings = 50;
|
||
|
|
input double Lots = 0.1;
|
||
|
|
input double PointBuffer = 10;
|
||
|
|
input int Slippage = 5;
|
||
|
|
ENUM_TIMEFRAMES TimeFrame;
|
||
|
|
|
||
|
|
// Node Structure
|
||
|
|
struct SwingPoint {
|
||
|
|
int index;
|
||
|
|
datetime time;
|
||
|
|
double price;
|
||
|
|
bool isHigh;
|
||
|
|
bool visited;
|
||
|
|
double distance;
|
||
|
|
bool used;
|
||
|
|
int previous;
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
SwingPoint swingPoints[];
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| OnInit |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int OnInit() {
|
||
|
|
Print("Dijkstra Swing EA initialized");
|
||
|
|
return INIT_SUCCEEDED;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| OnTick |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnTick() {
|
||
|
|
static datetime lastBarTime = 0;
|
||
|
|
datetime currentBarTime = iTime(_Symbol, _Period, 0);
|
||
|
|
|
||
|
|
if (currentBarTime != lastBarTime) {
|
||
|
|
lastBarTime = currentBarTime;
|
||
|
|
|
||
|
|
DetectSwings(LeftBars, RightBars);
|
||
|
|
ApplyDijkstra();
|
||
|
|
VisualizeSwings();
|
||
|
|
GenerateSignalAndTrade();
|
||
|
|
FilterAndMarkValidSwings(swingPoints);
|
||
|
|
CleanOldSwingObjects();
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Detect swing highs and lows |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void DetectSwings(int left, int right) {
|
||
|
|
ArrayResize(swingPoints, 0);
|
||
|
|
|
||
|
|
int totalBars = Bars(_Symbol, PERIOD_CURRENT) - right;
|
||
|
|
for (int i = left; i < totalBars; i++) {
|
||
|
|
bool isHigh = true, isLow = true;
|
||
|
|
double high = High(i), low = Low(i);
|
||
|
|
|
||
|
|
for (int j = 1; j <= left; j++) {
|
||
|
|
if (High(i - j) >= high) isHigh = false;
|
||
|
|
if (Low(i - j) <= low) isLow = false;
|
||
|
|
}
|
||
|
|
for (int j = 1; j <= right; j++) {
|
||
|
|
if (High(i + j) >= high) isHigh = false;
|
||
|
|
if (Low(i + j) <= low) isLow = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (isHigh || isLow) {
|
||
|
|
int idx = ArraySize(swingPoints);
|
||
|
|
ArrayResize(swingPoints, idx + 1);
|
||
|
|
swingPoints[idx].index = i;
|
||
|
|
swingPoints[idx].time = Time(i);
|
||
|
|
swingPoints[idx].price = isHigh ? high : low;
|
||
|
|
swingPoints[idx].isHigh = isHigh;
|
||
|
|
swingPoints[idx].visited = false;
|
||
|
|
swingPoints[idx].distance = DBL_MAX;
|
||
|
|
swingPoints[idx].previous = -1;
|
||
|
|
|
||
|
|
if (idx >= MaxSwings) break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Apply Dijkstra's algorithm |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ApplyDijkstra() {
|
||
|
|
if (ArraySize(swingPoints) == 0) return;
|
||
|
|
|
||
|
|
swingPoints[0].distance = 0;
|
||
|
|
|
||
|
|
for (int i = 0; i < ArraySize(swingPoints); i++) {
|
||
|
|
int u = -1;
|
||
|
|
double minDist = DBL_MAX;
|
||
|
|
|
||
|
|
for (int j = 0; j < ArraySize(swingPoints); j++) {
|
||
|
|
if (!swingPoints[j].visited && swingPoints[j].distance < minDist) {
|
||
|
|
minDist = swingPoints[j].distance;
|
||
|
|
u = j;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (u == -1) break;
|
||
|
|
|
||
|
|
swingPoints[u].visited = true;
|
||
|
|
|
||
|
|
for (int v = 0; v < ArraySize(swingPoints); v++) {
|
||
|
|
if (!swingPoints[v].visited) {
|
||
|
|
double cost = MathAbs(swingPoints[u].price - swingPoints[v].price);
|
||
|
|
if (swingPoints[u].distance + cost < swingPoints[v].distance) {
|
||
|
|
swingPoints[v].distance = swingPoints[u].distance + cost;
|
||
|
|
swingPoints[v].previous = u;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Visualize Swing Points and Connections |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void VisualizeSwings() {
|
||
|
|
for (int i = 0; i < ArraySize(swingPoints); i++) {
|
||
|
|
string objName = "Swing_" + IntegerToString(i);
|
||
|
|
ObjectDelete(0, objName);
|
||
|
|
|
||
|
|
ObjectCreate(0, objName, OBJ_ARROW, 0, swingPoints[i].time, swingPoints[i].price);
|
||
|
|
ObjectSetInteger(0, objName, OBJPROP_ARROWCODE, swingPoints[i].isHigh ? 233 : 234);
|
||
|
|
ObjectSetInteger(0, objName, OBJPROP_COLOR, swingPoints[i].isHigh ? clrRed : clrBlue);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (int i = 1; i < ArraySize(swingPoints); i++) {
|
||
|
|
int prev = swingPoints[i].previous;
|
||
|
|
if (prev != -1) {
|
||
|
|
string lineName = "Line_" + IntegerToString(i);
|
||
|
|
ObjectDelete(0, lineName);
|
||
|
|
|
||
|
|
ObjectCreate(0, lineName, OBJ_TREND, 0,
|
||
|
|
swingPoints[prev].time, swingPoints[prev].price,
|
||
|
|
swingPoints[i].time, swingPoints[i].price);
|
||
|
|
ObjectSetInteger(0, lineName, OBJPROP_COLOR, clrGray);
|
||
|
|
ObjectSetInteger(0, lineName, OBJPROP_WIDTH, 1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
double High(int index){return (iHigh(_Symbol, _Period, index));}
|
||
|
|
double Low(int index){return (iLow(_Symbol, _Period, index));}
|
||
|
|
datetime Time(int index){return (iTime(_Symbol, _Period, index));}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Filter and mark |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void FilterAndMarkValidSwings(SwingPoint &points[]) {
|
||
|
|
int count = ArraySize(points);
|
||
|
|
if(count < 2) return;
|
||
|
|
|
||
|
|
for(int i = 0; i < count; i++) {
|
||
|
|
if(points[i].used) continue;
|
||
|
|
|
||
|
|
bool isValid = true;
|
||
|
|
double swingPrice = points[i].price;
|
||
|
|
int swingIndex = points[i].index;
|
||
|
|
|
||
|
|
// Scan forward in time from the swing point
|
||
|
|
for(int j = swingIndex - 1; j >= 0; j--) {
|
||
|
|
double high = iHigh(_Symbol, TimeFrame, j);
|
||
|
|
double low = iLow(_Symbol, TimeFrame, j);
|
||
|
|
|
||
|
|
// Invalidate swing high if price went higher later
|
||
|
|
if(points[i].isHigh && high > swingPrice) {
|
||
|
|
isValid = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Invalidate swing low if price went lower later
|
||
|
|
if(!points[i].isHigh && low < swingPrice) {
|
||
|
|
isValid = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if(isValid) {
|
||
|
|
points[i].used = true;
|
||
|
|
|
||
|
|
// Draw object on chart
|
||
|
|
string objName = points[i].isHigh ?
|
||
|
|
StringFormat("SwingHigh_%d", TimeToString(iTime(_Symbol, TimeFrame, swingIndex))) :
|
||
|
|
StringFormat("SwingLow_%d", TimeToString(iTime(_Symbol, TimeFrame, swingIndex)));
|
||
|
|
|
||
|
|
color swingColor = points[i].isHigh ? clrRed : clrBlue;
|
||
|
|
|
||
|
|
ObjectCreate(0, objName, OBJ_HLINE, 0, 0, swingPrice);
|
||
|
|
ObjectSetInteger(0, objName, OBJPROP_COLOR, swingColor);
|
||
|
|
ObjectSetInteger(0, objName, OBJPROP_STYLE, STYLE_DASH);
|
||
|
|
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Cleaning up old swings |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void CleanOldSwingObjects(int keepBars = 100) {
|
||
|
|
datetime oldestDate = iTime(_Symbol, TimeFrame, keepBars);
|
||
|
|
int total = ObjectsTotal(0);
|
||
|
|
|
||
|
|
for(int i = total - 1; i >= 0; i--) {
|
||
|
|
string name = ObjectName(0, i);
|
||
|
|
if(StringFind(name, "SwingHigh_") == 0 || StringFind(name, "SwingLow_") == 0) {
|
||
|
|
datetime swingTime = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME);
|
||
|
|
if(swingTime < oldestDate) {
|
||
|
|
ObjectDelete(0, name);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Generate Signal & Trade |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void GenerateSignalAndTrade() {
|
||
|
|
if (ArraySize(swingPoints) < 2) return;
|
||
|
|
|
||
|
|
int last = ArraySize(swingPoints) - 1;
|
||
|
|
int prev = swingPoints[last].previous;
|
||
|
|
if (prev == -1) return;
|
||
|
|
|
||
|
|
double entry = swingPoints[last].price;
|
||
|
|
double reference = swingPoints[prev].price;
|
||
|
|
double sl, tp;
|
||
|
|
bool isBuy = entry > reference, isSell = entry < reference;
|
||
|
|
|
||
|
|
SetSLTP(entry, reference, isBuy, sl, tp);
|
||
|
|
|
||
|
|
if (PositionSelect(_Symbol)) return;
|
||
|
|
|
||
|
|
if (isBuy)
|
||
|
|
ExecuteTrade(ORDER_TYPE_BUY);
|
||
|
|
else if(isSell)
|
||
|
|
ExecuteTrade(ORDER_TYPE_SELL);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Calculate SL and TP based on distance to previous node |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void SetSLTP(double entry, double ref, bool isBuy, double &sl, double &tp) {
|
||
|
|
double distance = MathAbs(entry - ref) + PointBuffer * _Point;
|
||
|
|
if (isBuy) {
|
||
|
|
sl = entry - distance;
|
||
|
|
tp = entry + distance;
|
||
|
|
} else {
|
||
|
|
sl = entry + distance;
|
||
|
|
tp = entry - distance;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Execute trade with risk parameters |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ExecuteTrade(ENUM_ORDER_TYPE tradeType){
|
||
|
|
|
||
|
|
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||
|
|
double price = (tradeType == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) :
|
||
|
|
SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||
|
|
|
||
|
|
// Convert StopLoss and TakeProfit from pips to actual price distances
|
||
|
|
double sl_distance = StopLoss * point;
|
||
|
|
double tp_distance = TakeProfit * point;
|
||
|
|
|
||
|
|
double sl = (tradeType == ORDER_TYPE_BUY) ? price - sl_distance :
|
||
|
|
price + sl_distance;
|
||
|
|
|
||
|
|
double tp = (tradeType == ORDER_TYPE_BUY) ? price + tp_distance :
|
||
|
|
price - tp_distance;
|
||
|
|
|
||
|
|
trade.PositionOpen(_Symbol, tradeType, In_Lot, price, sl, tp, NULL);
|
||
|
|
}
|
||
|
|
|
||
|
|
|