Fix download links
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| GridEA.mq4 |
|
||||
//| Copyright 2018, Valentinos Galanos <sonidelav@hotmail.com> |
|
||||
//+------------------------------------------------------------------+
|
||||
#define ver "1.00"
|
||||
#property copyright "Copyright 2018, Valentinos Galanos <sonidelav@hotmail.com>"
|
||||
#property version ver
|
||||
#property strict
|
||||
|
||||
//--- input parameters
|
||||
input int GridGap = 50; // Grid Gap (Pips)
|
||||
input double LotSize = 0.01; // Trade Lot Volume
|
||||
input int TotalGridLines = 7; // Total Grid Lines Each Side
|
||||
|
||||
//--- Includes
|
||||
#include "Library\GridExpert.mqh"
|
||||
|
||||
//--- Expert
|
||||
CGridExpert* GridEA;
|
||||
//--- Memory
|
||||
bool initialized=false;
|
||||
bool timerCalled=false;
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
//--- create timer
|
||||
EventSetMillisecondTimer(500);
|
||||
|
||||
if( initialized == false )
|
||||
{
|
||||
initialized = true;
|
||||
GridEA = new CGridExpert;
|
||||
return GridEA.OnInit();
|
||||
}
|
||||
|
||||
//---
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
//--- destroy timer
|
||||
EventKillTimer();
|
||||
|
||||
switch(reason)
|
||||
{
|
||||
case REASON_CLOSE:
|
||||
case REASON_INITFAILED:
|
||||
case REASON_RECOMPILE:
|
||||
case REASON_REMOVE:
|
||||
case REASON_ACCOUNT:
|
||||
case REASON_CHARTCLOSE:
|
||||
case REASON_PROGRAM:
|
||||
if( initialized && GridEA != NULL)
|
||||
{
|
||||
GridEA.OnDeinit(reason);
|
||||
initialized = false;
|
||||
delete GridEA;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
//---
|
||||
if( initialized && GridEA != NULL )
|
||||
{
|
||||
GridEA.OnTick();
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Timer function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTimer()
|
||||
{
|
||||
if(timerCalled == false)
|
||||
{
|
||||
timerCalled = true;
|
||||
//---
|
||||
if( initialized && GridEA != NULL )
|
||||
{
|
||||
GridEA.OnTimer();
|
||||
}
|
||||
timerCalled = false;
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,332 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| GridMaster Pro.mq5 |
|
||||
//| Copyright 2024, Sajid. |
|
||||
//| https://www.mql5.com/en/users/sajidmahamud835 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2024, Sajid."
|
||||
#property link "https://www.mql5.com/en/users/sajidmahamud835"
|
||||
#property version "2.00"
|
||||
#property strict
|
||||
#property description "GridMaster Pro v2 — Bi-directional ATR grid with proper MM, min-stop awareness, and drawdown protection."
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
//--- Enums
|
||||
enum ENUM_GRID_MODE {
|
||||
GRID_NEUTRAL = 0, // Neutral: BUY below + SELL above
|
||||
GRID_BULLISH = 1, // Bullish: BUY only
|
||||
GRID_BEARISH = 2, // Bearish: SELL only
|
||||
};
|
||||
|
||||
enum ENUM_LOT_MODE {
|
||||
LOT_FIXED = 0, // Fixed lot size
|
||||
LOT_DYNAMIC = 1, // Risk-based (% of balance per order)
|
||||
};
|
||||
|
||||
//--- Input Parameters — Grid
|
||||
input ENUM_GRID_MODE GridMode = GRID_NEUTRAL; // Grid Mode
|
||||
input int MaxOrders = 5; // Max orders per side
|
||||
input int ATRPeriod = 14; // ATR period
|
||||
input double ATRMultiplier = 1.5; // ATR multiplier for grid distance
|
||||
|
||||
//--- Input Parameters — Orders
|
||||
input ENUM_LOT_MODE LotMode = LOT_FIXED; // Lot sizing mode
|
||||
input double LotSize = 0.1; // Fixed lot size
|
||||
input double RiskPercent = 1.0; // Risk % per order (dynamic mode)
|
||||
input bool UseTakeProfit = true; // Enable Take Profit
|
||||
input double DefaultTP = 200.0; // Min TP in points (auto-adjusted for broker)
|
||||
input bool UseStopLoss = true; // Enable Stop Loss
|
||||
input double DefaultSL = 1000.0; // Min SL in points (auto-adjusted for broker)
|
||||
input bool UseTrailingStop = true; // Enable Trailing Stop
|
||||
input double TrailingPoints = 100.0; // Trailing stop in points
|
||||
input double TrailingStep = 20.0; // Trailing step in points
|
||||
|
||||
//--- Input Parameters — Risk Management
|
||||
input double MaxDrawdownPct = 5.0; // Max drawdown % before pausing
|
||||
input bool CloseOnDrawdown = true; // Close all orders on drawdown breach
|
||||
|
||||
//--- Input Parameters — Magic & Debug
|
||||
input int MagicBase = 47291; // Base magic number
|
||||
input bool DebugMode = false; // Enable debug logging
|
||||
|
||||
//--- Global Variables
|
||||
CTrade trade;
|
||||
int magicNumber;
|
||||
double gridDistance;
|
||||
double accountEquityStart;
|
||||
bool gridPaused = false;
|
||||
string logFile;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit() {
|
||||
// Generate collision-safe magic number: base + symbol hash + timeframe
|
||||
magicNumber = MagicBase + (int)(StringLen(_Symbol) * 1000) + (int)Period();
|
||||
trade.SetExpertMagicNumber(magicNumber);
|
||||
trade.SetDeviationInPoints(50);
|
||||
trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
|
||||
accountEquityStart = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
logFile = "GridMasterPro_" + _Symbol + "_" + IntegerToString(Period()) + ".log";
|
||||
|
||||
WriteLog("GridMaster Pro v2.00 initialized | Magic: " + IntegerToString(magicNumber) +
|
||||
" | Symbol: " + _Symbol + " | Grid mode: " + EnumToString(GridMode));
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason) {
|
||||
WriteLog("EA deinitialized. Reason: " + IntegerToString(reason) + " | Open positions left: " + IntegerToString(CountOurPositions(ORDER_TYPE_BUY) + CountOurPositions(ORDER_TYPE_SELL)));
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick() {
|
||||
// Drawdown check
|
||||
if (CloseOnDrawdown && CheckDrawdown()) {
|
||||
if (!gridPaused) {
|
||||
WriteLog("DRAWDOWN LIMIT REACHED — closing all positions and pausing grid");
|
||||
CloseAllPositions();
|
||||
gridPaused = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Resume grid if paused and equity has recovered
|
||||
if (gridPaused) {
|
||||
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
if (currentEquity >= accountEquityStart * (1.0 - MaxDrawdownPct / 200.0)) {
|
||||
gridPaused = false;
|
||||
accountEquityStart = currentEquity;
|
||||
WriteLog("Grid resumed after equity recovery");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Recalculate grid distance every tick
|
||||
gridDistance = CalculateGridDistance();
|
||||
if (gridDistance <= 0) return;
|
||||
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
int buyCount = CountOurPositions(ORDER_TYPE_BUY);
|
||||
int sellCount = CountOurPositions(ORDER_TYPE_SELL);
|
||||
|
||||
// Manage trailing stops
|
||||
if (UseTrailingStop) ManageTrailingStops();
|
||||
|
||||
// Place BUY grid orders
|
||||
if (GridMode != GRID_BEARISH && buyCount < MaxOrders) {
|
||||
double buyPrice = ask - gridDistance * (buyCount + 1) * _Point;
|
||||
PlaceGridOrder(ORDER_TYPE_BUY, buyPrice);
|
||||
}
|
||||
|
||||
// Place SELL grid orders
|
||||
if (GridMode != GRID_BULLISH && sellCount < MaxOrders) {
|
||||
double sellPrice = bid + gridDistance * (sellCount + 1) * _Point;
|
||||
PlaceGridOrder(ORDER_TYPE_SELL, sellPrice);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Place a grid order with proper SL/TP |
|
||||
//+------------------------------------------------------------------+
|
||||
void PlaceGridOrder(ENUM_ORDER_TYPE type, double price) {
|
||||
// Check if order already exists near this price level
|
||||
if (OrderExistsNearPrice(type, price, gridDistance * 0.5 * _Point)) return;
|
||||
|
||||
// Broker minimum stop distance
|
||||
long stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
double minStop = MathMax((double)stopLevel, 10.0) * _Point * 1.2; // 20% buffer over broker min
|
||||
|
||||
double sl = 0, tp = 0;
|
||||
double lot = CalculateLot();
|
||||
|
||||
if (type == ORDER_TYPE_BUY) {
|
||||
if (UseTakeProfit) tp = price + MathMax(DefaultTP * _Point, minStop * 1.5);
|
||||
if (UseStopLoss) sl = price - MathMax(DefaultSL * _Point, minStop * MaxOrders);
|
||||
} else {
|
||||
if (UseTakeProfit) tp = price - MathMax(DefaultTP * _Point, minStop * 1.5);
|
||||
if (UseStopLoss) sl = price + MathMax(DefaultSL * _Point, minStop * MaxOrders);
|
||||
}
|
||||
|
||||
// Normalize prices
|
||||
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
price = NormalizeDouble(price, digits);
|
||||
sl = sl > 0 ? NormalizeDouble(sl, digits) : 0;
|
||||
tp = tp > 0 ? NormalizeDouble(tp, digits) : 0;
|
||||
|
||||
bool sent;
|
||||
if (type == ORDER_TYPE_BUY) {
|
||||
sent = trade.Buy(lot, _Symbol, 0, sl, tp, "Grid BUY");
|
||||
} else {
|
||||
sent = trade.Sell(lot, _Symbol, 0, sl, tp, "Grid SELL");
|
||||
}
|
||||
|
||||
if (sent) {
|
||||
WriteLog("Placed " + (type == ORDER_TYPE_BUY ? "BUY" : "SELL") +
|
||||
" | Lot: " + DoubleToString(lot, 2) +
|
||||
" | Price: ~" + DoubleToString(price, digits) +
|
||||
" | SL: " + DoubleToString(sl, digits) +
|
||||
" | TP: " + DoubleToString(tp, digits));
|
||||
} else {
|
||||
WriteLog("FAILED to place " + (type == ORDER_TYPE_BUY ? "BUY" : "SELL") +
|
||||
" | Error: " + IntegerToString(GetLastError()) +
|
||||
" | Price: " + DoubleToString(price, digits) +
|
||||
" | MinStop: " + DoubleToString(minStop / _Point, 0) + " pts");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Manage trailing stops for all our positions |
|
||||
//+------------------------------------------------------------------+
|
||||
void ManageTrailingStops() {
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
long stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
double minStop = MathMax((double)stopLevel, 10.0) * _Point * 1.2;
|
||||
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
if (!PositionSelectByTicket(PositionGetTicket(i))) continue;
|
||||
if (PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
|
||||
if (PositionGetInteger(POSITION_MAGIC) != magicNumber) continue;
|
||||
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double currentSL = PositionGetDouble(POSITION_SL);
|
||||
ulong ticket = PositionGetInteger(POSITION_TICKET);
|
||||
|
||||
double newSL = 0;
|
||||
|
||||
if (posType == POSITION_TYPE_BUY) {
|
||||
double profit = bid - openPrice;
|
||||
if (profit >= TrailingPoints * _Point) {
|
||||
newSL = NormalizeDouble(bid - TrailingPoints * _Point, digits);
|
||||
if (newSL > currentSL + TrailingStep * _Point && newSL > openPrice - minStop) {
|
||||
trade.PositionModify(ticket, newSL, PositionGetDouble(POSITION_TP));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
double profit = openPrice - ask;
|
||||
if (profit >= TrailingPoints * _Point) {
|
||||
newSL = NormalizeDouble(ask + TrailingPoints * _Point, digits);
|
||||
if ((currentSL == 0 || newSL < currentSL - TrailingStep * _Point) && newSL < openPrice + minStop) {
|
||||
trade.PositionModify(ticket, newSL, PositionGetDouble(POSITION_TP));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate ATR-based grid distance in points |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateGridDistance() {
|
||||
double atr = iATR(_Symbol, 0, ATRPeriod);
|
||||
if (atr <= 0) return 0;
|
||||
return (atr * ATRMultiplier) / _Point; // Return in points
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate lot size based on mode |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateLot() {
|
||||
if (LotMode == LOT_FIXED) return LotSize;
|
||||
|
||||
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
||||
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
|
||||
if (tickValue <= 0 || tickSize <= 0 || DefaultSL <= 0) return LotSize;
|
||||
|
||||
double riskAmount = balance * RiskPercent / 100.0;
|
||||
double slValue = DefaultSL * _Point / tickSize * tickValue;
|
||||
double lot = NormalizeDouble(riskAmount / slValue, 2);
|
||||
|
||||
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
||||
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
|
||||
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
||||
|
||||
lot = MathMax(minLot, MathMin(maxLot, MathRound(lot / lotStep) * lotStep));
|
||||
return lot;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Count our open positions by type |
|
||||
//+------------------------------------------------------------------+
|
||||
int CountOurPositions(ENUM_ORDER_TYPE type) {
|
||||
int count = 0;
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
if (!PositionSelectByTicket(PositionGetTicket(i))) continue;
|
||||
if (PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
|
||||
if (PositionGetInteger(POSITION_MAGIC) != magicNumber) continue;
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
if ((type == ORDER_TYPE_BUY && posType == POSITION_TYPE_BUY) ||
|
||||
(type == ORDER_TYPE_SELL && posType == POSITION_TYPE_SELL)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if order already exists near a price level |
|
||||
//+------------------------------------------------------------------+
|
||||
bool OrderExistsNearPrice(ENUM_ORDER_TYPE type, double price, double tolerance) {
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
if (!PositionSelectByTicket(PositionGetTicket(i))) continue;
|
||||
if (PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
|
||||
if (PositionGetInteger(POSITION_MAGIC) != magicNumber) continue;
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
if ((type == ORDER_TYPE_BUY && posType == POSITION_TYPE_BUY) ||
|
||||
(type == ORDER_TYPE_SELL && posType == POSITION_TYPE_SELL)) {
|
||||
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
if (MathAbs(openPrice - price) <= tolerance) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if drawdown limit breached |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CheckDrawdown() {
|
||||
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
double maxLoss = accountEquityStart * MaxDrawdownPct / 100.0;
|
||||
return (accountEquityStart - equity) >= maxLoss;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close all our positions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CloseAllPositions() {
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if (!PositionSelectByTicket(ticket)) continue;
|
||||
if (PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
|
||||
if (PositionGetInteger(POSITION_MAGIC) != magicNumber) continue;
|
||||
trade.PositionClose(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Append log to file (fixes overwrite bug) |
|
||||
//+------------------------------------------------------------------+
|
||||
void WriteLog(string message) {
|
||||
if (!DebugMode && StringFind(message, "FAILED") < 0 && StringFind(message, "DRAWDOWN") < 0) return;
|
||||
|
||||
int handle = FileOpen(logFile, FILE_READ | FILE_WRITE | FILE_TXT | FILE_COMMON);
|
||||
if (handle != INVALID_HANDLE) {
|
||||
FileSeek(handle, 0, SEEK_END); // Append — seek to end
|
||||
string ts = TimeToString(TimeCurrent(), TIME_DATE | TIME_MINUTES | TIME_SECONDS);
|
||||
FileWriteString(handle, ts + " | " + message + "\n");
|
||||
FileClose(handle);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
//Pending: Awaiting confirmation on whether it will be released for free or as a paid-only option.
|
||||
Binary file not shown.
@@ -0,0 +1,540 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Biased Martingale.mq4 |
|
||||
//| Matthew Kastor |
|
||||
//| https://github.com/matthewkastor |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Matthew Kastor"
|
||||
#property link "https://github.com/matthewkastor"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
bool TestDisabled=false;
|
||||
//+------------------------------------------------------------------+
|
||||
//|Enumeration to indicate directional bias. |
|
||||
//+------------------------------------------------------------------+
|
||||
enum Enum_Direction
|
||||
{
|
||||
BUYING,
|
||||
SELLING,
|
||||
NONE
|
||||
};
|
||||
|
||||
Enum_Direction Direction=NONE;
|
||||
|
||||
input double StartFactor=0.5;
|
||||
input double IncreaseFactor=2;
|
||||
input ENUM_TIMEFRAMES BiasTimeframe=PERIOD_D1;
|
||||
input int BiasPeriod=20;
|
||||
input int BiasLookback=12;
|
||||
|
||||
input ENUM_DAY_OF_WEEK StartDay=SUNDAY;
|
||||
input ENUM_DAY_OF_WEEK EndDay=SUNDAY;
|
||||
input int StartHour=0;
|
||||
input int EndHour=0;
|
||||
|
||||
datetime lastBarTime=0;
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
if(IsTesting() && TestDisabled==true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
PositionManagement();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsTimeBetween(int startDay,int startHour,int endDay,int endHour)
|
||||
{
|
||||
if((startDay==endDay) && (startHour==endHour))
|
||||
{
|
||||
// The schedule starts and ends at the same time, it is neither
|
||||
// day trading nor night trading... so it's always trading because
|
||||
// never trading is as simple as removing the EA.
|
||||
return true;
|
||||
}
|
||||
int D=TimeDayOfWeek(TimeCurrent());
|
||||
int H=TimeHour(TimeCurrent());
|
||||
|
||||
if((startDay<=D) && (endDay>=D) && (startHour<=H) && (endHour>=H))
|
||||
{
|
||||
//Print(StartDay+" "+StartHour+" < "+D+" "+H+" < "+EndDay+" "+EndHour);
|
||||
return true;
|
||||
}
|
||||
//Print(StartDay+" "+StartHour+" > "+D+" "+H+" > "+EndDay+" "+EndHour);
|
||||
return false;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Gets the profit of the last closed order. |
|
||||
//+------------------------------------------------------------------+
|
||||
double PairProfitSince(string symbol,int secondsAgo)
|
||||
{
|
||||
double num=0;
|
||||
for(int i=OrdersHistoryTotal()-1;i>=0;i--)
|
||||
{
|
||||
if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY) && OrderSymbol()==symbol && (OrderType()==OP_BUY || OrderType()==OP_SELL))
|
||||
{
|
||||
if(OrderCloseTime()>(Time[0]-secondsAgo))
|
||||
{
|
||||
num+=OrderProfit();
|
||||
}
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Gets the total size on the given currency pair. |
|
||||
//+------------------------------------------------------------------+
|
||||
double PairLotsTotal(string symbol)
|
||||
{
|
||||
double num=0;
|
||||
for(int i=0;i<OrdersTotal();i++)
|
||||
{
|
||||
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) && OrderSymbol()==symbol && (OrderType()==OP_BUY || OrderType()==OP_SELL))
|
||||
{
|
||||
num=num+OrderLots();
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Gets the current average price paid for the given currency pair. |
|
||||
//+------------------------------------------------------------------+
|
||||
double PairAveragePrice(string symbol)
|
||||
{
|
||||
double num=0;
|
||||
double sum=0;
|
||||
for(int i=0;i<OrdersTotal();i++)
|
||||
{
|
||||
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) && OrderSymbol()==symbol && (OrderType()==OP_BUY || OrderType()==OP_SELL))
|
||||
{
|
||||
sum=sum+OrderOpenPrice() * OrderLots();
|
||||
num=num+OrderLots();
|
||||
}
|
||||
}
|
||||
if(num>0 && sum>0)
|
||||
{
|
||||
return (sum / num);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Gets the lowest price paid for any order on the given pair. |
|
||||
//+------------------------------------------------------------------+
|
||||
double PairLowestPricePaid(string symbol)
|
||||
{
|
||||
double num=0;
|
||||
for(int i=0;i<OrdersTotal();i++)
|
||||
{
|
||||
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) && OrderSymbol()==symbol && (OrderType()==OP_BUY || OrderType()==OP_SELL))
|
||||
{
|
||||
if(num==0 || OrderOpenPrice()<num)
|
||||
{
|
||||
num=OrderOpenPrice();
|
||||
}
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Gets the highest price paid for any order on the given pair. |
|
||||
//+------------------------------------------------------------------+
|
||||
double PairHighestPricePaid(string symbol)
|
||||
{
|
||||
double num=0;
|
||||
for(int i=0;i<OrdersTotal();i++)
|
||||
{
|
||||
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) && OrderSymbol()==symbol && (OrderType()==OP_BUY || OrderType()==OP_SELL))
|
||||
{
|
||||
if(num==0 || OrderOpenPrice()>num)
|
||||
{
|
||||
num=OrderOpenPrice();
|
||||
}
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Gets the direction the given symbol is already traded in. |
|
||||
//+------------------------------------------------------------------+
|
||||
Enum_Direction PairDirection(string symbol)
|
||||
{
|
||||
for(int i=0;i<OrdersTotal();i++)
|
||||
{
|
||||
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) && OrderSymbol()==symbol && (OrderType()==OP_BUY || OrderType()==OP_SELL))
|
||||
{
|
||||
if(OrderType()==OP_BUY)
|
||||
{
|
||||
return BUYING;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SELLING;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Closes all open orders on the given currency pair. |
|
||||
//+------------------------------------------------------------------+
|
||||
void CloseOpenOrders(string symbol)
|
||||
{
|
||||
double bid = MarketInfo(symbol, MODE_BID);
|
||||
double ask = MarketInfo(symbol, MODE_ASK);
|
||||
while(PairLotsTotal(symbol)>0)
|
||||
{
|
||||
for(int i=0;i<OrdersTotal();i++)
|
||||
{
|
||||
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) && OrderSymbol()==symbol)
|
||||
{
|
||||
if(OrderType()==OP_BUY)
|
||||
{
|
||||
bool ret=OrderClose(OrderTicket(),OrderLots(),bid,0);
|
||||
if(!ret)
|
||||
{
|
||||
PrintFormat("Attempt to CLOSE ticket %25.0i for %2.2f %s @ %2.5f failed. (mql error %5.0i)",OrderTicket(),OrderLots(),symbol,bid,GetLastError());
|
||||
}
|
||||
}
|
||||
else if(OrderType()==OP_SELL)
|
||||
{
|
||||
bool ret=OrderClose(OrderTicket(),OrderLots(),ask,0);
|
||||
if(!ret)
|
||||
{
|
||||
PrintFormat("Attempt to CLOSE ticket %25.0i for %2.2f %s @ %2.5f failed. (mql error %5.0i)",OrderTicket(),OrderLots(),symbol,ask,GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|set stop loss on all open orders on the given currency pair. |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetStopLossOpenOrders(string symbol)
|
||||
{
|
||||
double bid = MarketInfo(symbol, MODE_BID);
|
||||
double ask = MarketInfo(symbol, MODE_ASK);
|
||||
double pa = PairAveragePrice(symbol);
|
||||
double sl = 0;
|
||||
for(int i=0;i<OrdersTotal();i++)
|
||||
{
|
||||
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) && OrderSymbol()==symbol)
|
||||
{
|
||||
if(OrderType()==OP_BUY)
|
||||
{
|
||||
sl=NormalizeDouble((bid+pa)/2,Digits);
|
||||
if(OrderStopLoss()==0 || sl>OrderStopLoss())
|
||||
{
|
||||
bool ret=OrderModify(OrderTicket(),OrderOpenPrice(),sl,OrderTakeProfit(),0);
|
||||
if(!ret)
|
||||
{
|
||||
PrintFormat("Attempt to set stop loss on ticket %25.0i for %2.2f %s @ %2.5f failed. (mql error %5.0i)",OrderTicket(),OrderLots(),symbol,bid,GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(OrderType()==OP_SELL)
|
||||
{
|
||||
sl=NormalizeDouble((ask+pa)/2,Digits);
|
||||
if(OrderStopLoss()==0 || sl<OrderStopLoss())
|
||||
{
|
||||
bool ret=OrderModify(OrderTicket(),OrderOpenPrice(),sl,OrderTakeProfit(),0);
|
||||
if(!ret)
|
||||
{
|
||||
PrintFormat("Attempt to set stop loss on ticket %25.0i for %2.2f %s @ %2.5f failed. (mql error %5.0i)",OrderTicket(),OrderLots(),symbol,ask,GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Opens an order on the given currency pair. |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenOrder(string symbol,Enum_Direction buyingOrSelling)
|
||||
{
|
||||
double bid = MarketInfo(symbol, MODE_BID);
|
||||
double ask = MarketInfo(symbol, MODE_ASK);
|
||||
double minLot=MarketInfo(symbol,MODE_MINLOT);
|
||||
double maxLot=MarketInfo(symbol,MODE_MAXLOT);
|
||||
double orderSize=PairLotsTotal(symbol)*IncreaseFactor;
|
||||
//double riskFactor = (20 / iADX(symbol,PERIOD_W1,104,PRICE_CLOSE,MODE_MAIN,0));
|
||||
double riskFactor=1;
|
||||
if(orderSize<minLot)
|
||||
{
|
||||
double accountSizedLots=(AccountBalance()/100000)*StartFactor*riskFactor;
|
||||
if(minLot>accountSizedLots)
|
||||
{
|
||||
orderSize=minLot;
|
||||
}
|
||||
else
|
||||
{
|
||||
orderSize=accountSizedLots;
|
||||
}
|
||||
}
|
||||
orderSize=NormalizeDouble(orderSize,2);
|
||||
if(orderSize>maxLot)
|
||||
{
|
||||
//PrintFormat("Not Opening order, maximum lot size exceeded. Reduce the StartFactor. (Max) %2.2f < (Requested) %2.2f lots.",maxLot,orderSize);
|
||||
//CloseOpenOrders(symbol);
|
||||
orderSize=maxLot-minLot;
|
||||
//return;
|
||||
}
|
||||
if(buyingOrSelling==BUYING)
|
||||
{
|
||||
if(AccountFreeMarginCheck(symbol,OP_BUY,orderSize)<=AccountEquity()*0.1 || GetLastError()==134)
|
||||
{
|
||||
PrintFormat("Not Opening order, not enough free margin for %2.2f lots. Reduce the StartFactor.",orderSize);
|
||||
//CloseOpenOrders(symbol);
|
||||
return;
|
||||
}
|
||||
int ret=OrderSend(symbol,OP_BUY,orderSize,ask,0,0,0);
|
||||
if(ret == -1)
|
||||
{
|
||||
PrintFormat("Failed attempt to BUY %2.2f %s @ %2.5f (mql error %5.0i)",orderSize,symbol,ask,GetLastError());
|
||||
}
|
||||
}
|
||||
else if(buyingOrSelling==SELLING)
|
||||
{
|
||||
if(AccountFreeMarginCheck(symbol,OP_SELL,orderSize)<=AccountEquity()*0.1 || GetLastError()==134)
|
||||
{
|
||||
PrintFormat("Not Opening order, not enough free margin for %2.2f lots. Reduce the StartFactor.",orderSize);
|
||||
//CloseOpenOrders(symbol);
|
||||
return;
|
||||
}
|
||||
int ret=OrderSend(symbol,OP_SELL,orderSize,bid,0,0,0);
|
||||
if(ret == -1)
|
||||
{
|
||||
PrintFormat("Failed attempt to SELL %2.2f %s @ %2.5f (mql error %5.0i)",orderSize,symbol,bid,GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Gets the direction to trade in for the given symbol. |
|
||||
//+------------------------------------------------------------------+
|
||||
Enum_Direction GetBiasDirection(string symbol)
|
||||
{
|
||||
double custAvgNow=iMA(symbol,BiasTimeframe,BiasPeriod,0,MODE_SMA,PRICE_CLOSE,1);
|
||||
double custAvgLookback=iMA(symbol,BiasTimeframe,BiasPeriod,0,MODE_SMA,PRICE_CLOSE,BiasLookback);
|
||||
if(custAvgNow>custAvgLookback)
|
||||
{
|
||||
return BUYING;
|
||||
}
|
||||
if(custAvgNow<custAvgLookback)
|
||||
{
|
||||
return SELLING;
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Calculates the standard range. |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetVolatilityFactor(string symbol)
|
||||
{
|
||||
return iATR(symbol,PERIOD_W1,6,1) / iATR(symbol,PERIOD_W1,52,1);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Calculates the price to take profit at. |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetProfitPoints(string symbol)
|
||||
{
|
||||
double bid = MarketInfo(symbol, MODE_BID);
|
||||
double ask = MarketInfo(symbol, MODE_ASK);
|
||||
double spread=ask-bid;
|
||||
if(spread<=0)
|
||||
{
|
||||
spread=Point*1000;
|
||||
}
|
||||
double vf=GetVolatilityFactor(symbol);
|
||||
if(vf>2)
|
||||
{
|
||||
vf=2;
|
||||
}
|
||||
if(vf<=0)
|
||||
{
|
||||
vf=1;
|
||||
}
|
||||
double output=1.5 *(vf*iATR(symbol,BiasTimeframe,BiasPeriod,1));
|
||||
if(output<spread*6)
|
||||
{
|
||||
output=spread*6;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Calculates the price to open another order in the same direction. |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetStepSize(string symbol)
|
||||
{
|
||||
double bid = MarketInfo(symbol, MODE_BID);
|
||||
double ask = MarketInfo(symbol, MODE_ASK);
|
||||
double spread=ask-bid;
|
||||
if(spread<=0)
|
||||
{
|
||||
spread=Point*1000;
|
||||
}
|
||||
double vf=GetVolatilityFactor(symbol);
|
||||
if(vf<0.25)
|
||||
{
|
||||
vf=0.25;
|
||||
}
|
||||
double output=0.5 *(vf*iATR(symbol,BiasTimeframe,BiasPeriod,1));
|
||||
if(output<spread*4)
|
||||
{
|
||||
output=spread*4;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetDrawdownPercent()
|
||||
{
|
||||
return 100 * (1 - (AccountEquity()/AccountBalance()));
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
int GetLockFactor()
|
||||
{
|
||||
int lockFactor=-25+OrdersTotal();
|
||||
if(lockFactor>-1)
|
||||
{
|
||||
lockFactor=-1;
|
||||
}
|
||||
return lockFactor;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Manages the position. |
|
||||
//+------------------------------------------------------------------+
|
||||
void PositionManagement()
|
||||
{
|
||||
string Pair=Symbol();
|
||||
|
||||
int biasBarCount=Bars(Pair,BiasTimeframe);
|
||||
int volatilityBarsCount=Bars(Pair,PERIOD_W1);
|
||||
if((BiasLookback+BiasPeriod)>biasBarCount)
|
||||
{
|
||||
Print("Not enough history to form bias : BiasLookback + BiasPeriod");
|
||||
return;
|
||||
}
|
||||
if(52>volatilityBarsCount)
|
||||
{
|
||||
Print("Not enough history to form bias : volatilityBarsCount");
|
||||
return;
|
||||
}
|
||||
|
||||
double ProfitPoints=GetProfitPoints(Pair);
|
||||
double PairAveragePrice=PairAveragePrice(Pair);
|
||||
double PairLots=PairLotsTotal(Pair);
|
||||
double bid = MarketInfo(Pair, MODE_BID);
|
||||
double ask = MarketInfo(Pair, MODE_ASK);
|
||||
double ProfitTarget=0;
|
||||
double CurrentOpenPrice=0;
|
||||
double CurrentClosePrice=0;
|
||||
string dirMsg="No Direction";
|
||||
|
||||
// manual entry could initialize the trades while the
|
||||
// "Direction" latch is considering the opposite direction.
|
||||
if(PairAveragePrice>0)
|
||||
{
|
||||
Direction=PairDirection(Pair);
|
||||
}
|
||||
|
||||
if(Direction==BUYING)
|
||||
{
|
||||
dirMsg="Buying";
|
||||
ProfitTarget=PairAveragePrice+ProfitPoints;
|
||||
CurrentOpenPrice=ask;
|
||||
CurrentClosePrice=bid;
|
||||
}
|
||||
|
||||
if(Direction==SELLING)
|
||||
{
|
||||
dirMsg="Selling";
|
||||
ProfitTarget=PairAveragePrice-ProfitPoints;
|
||||
CurrentOpenPrice=bid;
|
||||
CurrentClosePrice=ask;
|
||||
}
|
||||
|
||||
double dd=GetDrawdownPercent();
|
||||
Comment(
|
||||
StringFormat(
|
||||
"%s %3.2f Lots at %3.5f, Targeting %3.5f %2.2f DD %2.2f"
|
||||
,dirMsg,PairLots,PairAveragePrice,ProfitTarget,PairProfitSince(Pair,60*60*24*30),dd));
|
||||
|
||||
bool tradingTime=IsTimeBetween(StartDay,StartHour,EndDay,EndHour);
|
||||
if(PairAveragePrice==0 && tradingTime)
|
||||
{
|
||||
Direction=GetBiasDirection(Pair);
|
||||
if(Direction!=NONE)
|
||||
{
|
||||
Print("Opening order, initializing position.");
|
||||
OpenOrder(Pair,Direction);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(AccountFreeMargin()<AccountBalance()*0.05)
|
||||
{
|
||||
Print("Closing orders, account free margin is too low.");
|
||||
CloseOpenOrders(Pair);
|
||||
return;
|
||||
}
|
||||
else if(PairAveragePrice!=0 && Direction==BUYING && CurrentClosePrice>ProfitTarget)
|
||||
{
|
||||
Print("Closing orders, profit target reached.");
|
||||
CloseOpenOrders(Pair);
|
||||
return;
|
||||
}
|
||||
else if(PairAveragePrice!=0 && Direction==SELLING && CurrentClosePrice<ProfitTarget)
|
||||
{
|
||||
Print("Closing orders, profit target reached.");
|
||||
CloseOpenOrders(Pair);
|
||||
return;
|
||||
}
|
||||
else if(GetDrawdownPercent()<GetLockFactor())
|
||||
{
|
||||
Print("Setting stop loss to prevent losing all of this gain.");
|
||||
SetStopLossOpenOrders(Pair);
|
||||
return;
|
||||
}
|
||||
else if(PairAveragePrice!=0 && Direction==BUYING)
|
||||
{
|
||||
double stepSize=GetStepSize(Pair);
|
||||
if((CurrentClosePrice+stepSize)<PairLowestPricePaid(Pair))
|
||||
{
|
||||
Print("Opening order, averaging down.");
|
||||
OpenOrder(Pair,Direction);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(PairAveragePrice!=0 && Direction==SELLING)
|
||||
{
|
||||
double stepSize=GetStepSize(Pair);
|
||||
if((CurrentClosePrice-stepSize)>PairHighestPricePaid(Pair))
|
||||
{
|
||||
Print("Opening order, averaging down.");
|
||||
OpenOrder(Pair,Direction);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,90 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Basket Case.mq4 |
|
||||
//| Matthew Kastor |
|
||||
//| https://github.com/matthewkastor |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Matthew Kastor"
|
||||
#property link "https://github.com/matthewkastor"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
#include <CurrencyBasket\Basket.mqh>
|
||||
|
||||
#property indicator_separate_window
|
||||
#property indicator_buffers 1
|
||||
#property indicator_plots 1
|
||||
//--- plot Label1
|
||||
#property indicator_type1 DRAW_LINE
|
||||
#property indicator_color1 clrDeepSkyBlue
|
||||
#property indicator_style1 STYLE_SOLID
|
||||
#property indicator_width1 1
|
||||
//--- indicator buffers
|
||||
double IndexLineBuffer[];
|
||||
|
||||
extern int BarsLimit=5000;
|
||||
extern color colorOne=clrDeepSkyBlue; //Line Color
|
||||
extern string BasketSpecs1="EURUSDpro,sell,0.576"; //Pair,direction,weight;Pair,direction,weight
|
||||
extern string BasketSpecs2=";USDJPYpro,buy,0.136"; //;Pair,direction,weight;Pair,direction,weight
|
||||
extern string BasketSpecs3=";GBPUSDpro,sell,0.119"; //;Pair,direction,weight;Pair,direction,weight
|
||||
extern string BasketSpecs4=";USDCADpro,buy,0.091"; //;Pair,direction,weight;Pair,direction,weight
|
||||
extern string BasketSpecs5=";USDSEKpro,buy,0.042"; //;Pair,direction,weight;Pair,direction,weight
|
||||
extern string BasketSpecs6=";USDCHFpro,buy,0.036"; //;Pair,direction,weight;Pair,direction,weight
|
||||
extern string BasketSpecs7=""; //;Pair,direction,weight;Pair,direction,weight
|
||||
|
||||
Basket *basket;
|
||||
//+------------------------------------------------------------------+
|
||||
//| Custom indicator initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
IndicatorShortName("Basket Case");
|
||||
SetIndexBuffer(0,IndexLineBuffer);
|
||||
SetIndexLabel(0,"Basket Points");
|
||||
SetIndexStyle(0,0,0,1,colorOne);
|
||||
|
||||
string basketSpecs=StringConcatenate(BasketSpecs1,BasketSpecs2,BasketSpecs3,BasketSpecs4,BasketSpecs5,BasketSpecs6,BasketSpecs7);
|
||||
basket=new Basket(basketSpecs);
|
||||
if(!basket.ValidatePairsExist())
|
||||
{
|
||||
return (INIT_FAILED);
|
||||
}
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Custom indicator iteration function |
|
||||
//+------------------------------------------------------------------+
|
||||
int start()
|
||||
{
|
||||
int i=Bars-IndicatorCounted()-1;
|
||||
|
||||
if(i>BarsLimit)
|
||||
{
|
||||
i=BarsLimit;
|
||||
}
|
||||
|
||||
double val=0;
|
||||
|
||||
while(i>0)
|
||||
{
|
||||
val=basket.GetWeightedPoints(i);
|
||||
if(val==0)
|
||||
{
|
||||
IndexLineBuffer[i]=EMPTY_VALUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
IndexLineBuffer[i]=val;
|
||||
}
|
||||
|
||||
i--;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
int deinit()
|
||||
{
|
||||
delete basket;
|
||||
return(0);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,165 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MA Cross.mq4 |
|
||||
//| Copyright 2013, Eugene Sia |
|
||||
//| http://eugenesia.co.uk |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
/**
|
||||
* This is a simple Metatrader 4 Expert Advisor I made to revise my
|
||||
* MQL4, after a long hiatus. Hoping to re-explore automated forex
|
||||
* trading!
|
||||
*
|
||||
* This EA trades based on a moving average crossovers, a common
|
||||
* breakout strategy. When the short MA crosses the long MA, enter a
|
||||
* trade.
|
||||
*/
|
||||
|
||||
#property copyright "Copyright 2013, Eugene Sia"
|
||||
#property link "http://eugenesia.co.uk"
|
||||
|
||||
//--- Constant definitions
|
||||
|
||||
// Prefix a unique identifier e.g. MACROSS so we don't conflict with
|
||||
// other predefined constants.
|
||||
|
||||
// This defines the magic number for this EA. A magic number can be
|
||||
// assigned to an order, so that orders opened by this EA have this magic
|
||||
// number. This is how we distinguish between orders opened by this EA,
|
||||
// and those opened by the user or other EAs.
|
||||
// Ref: http://articles.mql4.com/145
|
||||
#define MACROSS_MAGIC_NUM 20130715
|
||||
#define MACROSS_OPEN_BUY_SIGNAL 1
|
||||
#define MACROSS_OPEN_SELL_SIGNAL -1
|
||||
#define MACROSS_NO_SIGNAL 0
|
||||
|
||||
//--- input parameters
|
||||
|
||||
// extern keyword defines parameters that can be set by the user in the
|
||||
// "Expert properties" dialog.
|
||||
extern int ShortMaPeriod = 10;
|
||||
extern int LongMaPeriod = 50;
|
||||
|
||||
// These are in fractional pips, which are 0.1 of a pip.
|
||||
extern int StopLoss = 500;
|
||||
extern int TakeProfit = 1600;
|
||||
|
||||
// Number of lots for each trade.
|
||||
extern double Lots = 1;
|
||||
|
||||
/**
|
||||
* Get moving average values for the most recent price points.
|
||||
*
|
||||
* Params:
|
||||
* maPeriod: period of the MA.
|
||||
* numValues: Number of values to insert into the returned array.
|
||||
* ma: returned array of MA values, with ma[0] being the value for the
|
||||
* current price, ma[1] the value for the previous bar's price, etc.
|
||||
*
|
||||
*/
|
||||
void MaRecentValues(double& ma[], int maPeriod, int numValues = 3)
|
||||
{
|
||||
// i is the index of the price array to calculate the MA value for.
|
||||
// e.g. i=0 is the current price, i=1 is the previous bar's price.
|
||||
for (int i=0; i < numValues; i++)
|
||||
{
|
||||
ma[i] = iMA(NULL,0,maPeriod,0,MODE_SMA,PRICE_CLOSE,i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if we should open a trade.
|
||||
*
|
||||
* Returns: +1 to open a buy order, -1 to open a sell order, 0 for no action.
|
||||
*/
|
||||
int OpenSignal()
|
||||
{
|
||||
int signal = MACROSS_NO_SIGNAL;
|
||||
|
||||
// Execute only on the first tick of a new bar, to avoid repeatedly
|
||||
// opening orders when an open condition is satisfied.
|
||||
if (Volume[0] > 1) return(0);
|
||||
|
||||
//---- get Moving Average values
|
||||
|
||||
double shortMa[3];
|
||||
MaRecentValues(shortMa, ShortMaPeriod, 3);
|
||||
|
||||
double longMa[3];
|
||||
MaRecentValues(longMa, LongMaPeriod, 3);
|
||||
|
||||
//---- buy conditions
|
||||
if (shortMa[2] < longMa[2]
|
||||
&& shortMa[1] > longMa[1])
|
||||
{
|
||||
signal = MACROSS_OPEN_BUY_SIGNAL;
|
||||
}
|
||||
|
||||
//---- sell conditions
|
||||
if (shortMa[2] > longMa[2]
|
||||
&& shortMa[1] < longMa[1])
|
||||
{
|
||||
signal = MACROSS_OPEN_SELL_SIGNAL;
|
||||
}
|
||||
|
||||
//----
|
||||
return(signal);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int init()
|
||||
{
|
||||
//----
|
||||
|
||||
|
||||
//----
|
||||
return(0);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int deinit()
|
||||
{
|
||||
//----
|
||||
|
||||
//----
|
||||
return(0);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| expert start function |
|
||||
//+------------------------------------------------------------------+
|
||||
int start()
|
||||
{
|
||||
//----
|
||||
int signal = OpenSignal();
|
||||
|
||||
// Set slippage to a large enough number to avoid error 138 - quote
|
||||
// outdated.
|
||||
int slippage = 30;
|
||||
|
||||
if (signal == MACROSS_OPEN_BUY_SIGNAL)
|
||||
{
|
||||
Print("Buy signal");
|
||||
OrderSend(Symbol(),OP_BUY,Lots,Bid,slippage,
|
||||
Bid-StopLoss*Point, // Stop loss price.
|
||||
Bid+TakeProfit*Point, // Take profit price.
|
||||
NULL,MACROSS_MAGIC_NUM,0,Green);
|
||||
}
|
||||
|
||||
else if (signal == MACROSS_OPEN_SELL_SIGNAL)
|
||||
{
|
||||
Print("Sell signal");
|
||||
OrderSend(Symbol(),OP_SELL,Lots,Ask,slippage,
|
||||
Ask+StopLoss*Point, // Stop loss price.
|
||||
Ask-TakeProfit*Point, // Take profit price.
|
||||
NULL,MACROSS_MAGIC_NUM,0,Red);
|
||||
}
|
||||
|
||||
//----
|
||||
return(0);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @copyright 2019, pipbolt.io <beta@pipbolt.io>
|
||||
* @license https://github.com/pipbolt/experts/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
#include <PipboltFramework\Constants.mqh>
|
||||
|
||||
#define NAME "Moving Average Cross EA"
|
||||
#define VERSION "0.022"
|
||||
|
||||
#property copyright COPYRIGHT
|
||||
#property link LINK
|
||||
#property icon ICON
|
||||
#property description DESCRIPTION
|
||||
#property version VERSION
|
||||
|
||||
#include <PipboltFramework\Params\MainSettings.mqh>
|
||||
|
||||
input group "Entry Strategy";
|
||||
|
||||
input group "Exit Strategy";
|
||||
input bool UseExitStrategy = false; // Use Exit Strategy
|
||||
|
||||
input group "Moving Averages";
|
||||
|
||||
input string Fast_Moving_Average = "----------"; // ---------- Fast Moving Average ----------
|
||||
input int MaFastPeriod = 12; // Moving Average Period
|
||||
input ENUM_MA_METHOD MaFastMethod = MODE_SMA; // Method
|
||||
input ENUM_APPLIED_PRICE MaFastAppliedPrice = PRICE_CLOSE; // Applied Price
|
||||
|
||||
input string Slow_Moving_Average = "----------"; // ---------- Slow Moving Average ----------
|
||||
input int MaSlowPeriod = 30; // Moving Average Period
|
||||
input ENUM_MA_METHOD MaSlowMethod = MODE_SMA; // Method
|
||||
input ENUM_APPLIED_PRICE MaSlowAppliedPrice = PRICE_CLOSE; // Applied Price
|
||||
|
||||
#include <PipboltFramework\Experts.mqh>
|
||||
|
||||
CiMA MAFast;
|
||||
CiMA MASlow;
|
||||
|
||||
int OnInit(void)
|
||||
{
|
||||
if (ONINIT() != INIT_SUCCEEDED)
|
||||
return INIT_FAILED;
|
||||
|
||||
MAFast.Init(MaFastPeriod, 0, MaFastMethod, MaFastAppliedPrice);
|
||||
MASlow.Init(MaSlowPeriod, 0, MaSlowMethod, MaSlowAppliedPrice);
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnTick(void) { ONTICK(); }
|
||||
void OnDeinit(const int reason) { ONDEINIT(reason); }
|
||||
void OnTimer() { ONTIMER(); }
|
||||
|
||||
void CheckForOpen(bool &openBuy, bool &openSell)
|
||||
{
|
||||
// Buy Entry Strategy
|
||||
if (MAFast.Main(1) < MASlow.Main(1) && MAFast.Main(0) >= MASlow.Main(0))
|
||||
openBuy = true;
|
||||
|
||||
// Sell Entry Strategy
|
||||
else if (MAFast.Main(1) > MASlow.Main(1) && MAFast.Main(0) <= MASlow.Main(0))
|
||||
openSell = true;
|
||||
|
||||
// Apply MA Filter
|
||||
openBuy = openBuy && MAFilter.Check(DIR_BUY);
|
||||
openSell = openSell && MAFilter.Check(DIR_SELL);
|
||||
}
|
||||
|
||||
void CheckForClose(bool &closeBuy, bool &closeSell)
|
||||
{
|
||||
// Buy Exit Strategy
|
||||
closeBuy = (MAFast.Main(1) > MASlow.Main(1) && MAFast.Main(0) <= MASlow.Main(0));
|
||||
|
||||
// Sell Exit Strategy
|
||||
closeSell = (MAFast.Main(1) < MASlow.Main(1) && MAFast.Main(0) >= MASlow.Main(0));
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @copyright 2019, pipbolt.io <beta@pipbolt.io>
|
||||
* @license https://github.com/pipbolt/experts/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
#include <PipboltFramework\Constants.mqh>
|
||||
|
||||
#define NAME "Moving Average EA"
|
||||
#define VERSION "0.022"
|
||||
|
||||
#property copyright COPYRIGHT
|
||||
#property link LINK
|
||||
#property icon ICON
|
||||
#property description DESCRIPTION
|
||||
#property version VERSION
|
||||
|
||||
#include <PipboltFramework\Params\MainSettings.mqh>
|
||||
|
||||
input group "Entry Strategy";
|
||||
|
||||
input group "Exit Strategy";
|
||||
input bool UseExitStrategy = false; // Use Exit Strategy
|
||||
|
||||
input group "Moving Average";
|
||||
input int MaPeriod = 10; // Period
|
||||
input ENUM_MA_METHOD MaMethod = MODE_SMA; // Method
|
||||
input ENUM_APPLIED_PRICE MaAppliedPrice = PRICE_CLOSE; // Applied Price
|
||||
|
||||
#include <PipboltFramework\Experts.mqh>
|
||||
|
||||
CiMA MA;
|
||||
|
||||
int OnInit(void)
|
||||
{
|
||||
if (ONINIT() != INIT_SUCCEEDED)
|
||||
return INIT_FAILED;
|
||||
|
||||
MA.Init(MaPeriod, 0, MaMethod, MaAppliedPrice);
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnTick(void) { ONTICK(); }
|
||||
void OnDeinit(const int reason) { ONDEINIT(reason); }
|
||||
void OnTimer() { ONTIMER(); }
|
||||
|
||||
void CheckForOpen(bool &openBuy, bool &openSell)
|
||||
{
|
||||
// Close prices
|
||||
double close0 = iClose(NULL, NULL, _indicatorShift + 0);
|
||||
double close1 = iClose(NULL, NULL, _indicatorShift + 1);
|
||||
|
||||
// Buy Entry Strategy
|
||||
openBuy = (close0 > MA.Main(0) && close1 <= MA.Main(1));
|
||||
|
||||
// Sell Entry Strategy
|
||||
openSell = (close0 < MA.Main(0) && close1 >= MA.Main(1));
|
||||
|
||||
// Apply MA Filter
|
||||
openBuy = openBuy && MAFilter.Check(DIR_BUY);
|
||||
openSell = openSell && MAFilter.Check(DIR_SELL);
|
||||
}
|
||||
|
||||
void CheckForClose(bool &closeBuy, bool &closeSell)
|
||||
{
|
||||
// Close price
|
||||
double close0 = iClose(NULL, NULL, _indicatorShift + 0);
|
||||
|
||||
// Buy Exit Strategy
|
||||
closeBuy = (close0 < MA.Main(0));
|
||||
|
||||
// Sell Exit Strategy
|
||||
closeSell = (close0 > MA.Main(0));
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,169 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| SpikeTrader.mq4 |
|
||||
//| Copyright © 2012-2022, EarnForex.com |
|
||||
//| https://www.earnforex.com/ |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright © 2012-2022, EarnForex"
|
||||
#property link "https://www.earnforex.com/metatrader-expert-advisors/Spike-Trader/"
|
||||
#property version "1.01"
|
||||
#property strict
|
||||
|
||||
#property description "Trades on spikes that are:"
|
||||
#property description "1) Higher/lower than N preceding bars;"
|
||||
#property description "2) Higher/lower than the previous bar by X percent;"
|
||||
#property description "3) Close in bottom or upper third/half of the bar."
|
||||
|
||||
input group "Main"
|
||||
input int Hold = 11; // Hold: Position holding time in bars.
|
||||
input int BarsNumber = 3; // BarsNumber: N preceding bars to check.
|
||||
input double PercentageDifference = 0.003; // PercentageDifference1: X percentage for bar comparison.
|
||||
input double ThirdOrHalf = 0.5; // ThirdOrHalf: Top/bottom share of a bar to close in.
|
||||
input group "Money management"
|
||||
input double Lots = 0.1;
|
||||
input group "Miscellaneous"
|
||||
input int Slippage = 30;
|
||||
input string OrderCommentary = "Spike Trader";
|
||||
input int Magic = 173923183;
|
||||
|
||||
int LastBars = 0;
|
||||
int Timer = 0;
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
if ((!IsTradeAllowed()) || (IsTradeContextBusy()) || (!IsConnected()) || ((!MarketInfo(Symbol(), MODE_TRADEALLOWED)) && (!IsTesting()))) return;
|
||||
|
||||
//Wait for the new Bar in a chart.
|
||||
if (LastBars == Bars) return;
|
||||
else LastBars = Bars;
|
||||
|
||||
if (Timer == 1) ClosePrev();
|
||||
if (Timer > 0) Timer--;
|
||||
|
||||
CheckEntry();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for entry conditions and trade if necessary. |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEntry()
|
||||
{
|
||||
// Empty bar.
|
||||
if (High[1] - Low[1] == 0) return;
|
||||
|
||||
if (CheckSellEntry())
|
||||
{
|
||||
// If found a BUY order, close it and open a SELL. Otherwise, only reset timer.
|
||||
if (ClosePrev(OP_SELL)) fSell();
|
||||
Timer = Hold;
|
||||
}
|
||||
else if (CheckBuyEntry())
|
||||
{
|
||||
// If found a SELL order, close it and open a BUY. Otherwise, only reset timer.
|
||||
if (ClosePrev(OP_BUY)) fBuy();
|
||||
Timer = Hold;
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckSellEntry()
|
||||
{
|
||||
// If the bar isn't higher than at least one of the previous bars - return false.
|
||||
for (int i = 2; i < BarsNumber + 2; i++)
|
||||
if (High[1] <= High[i]) return false;
|
||||
|
||||
// If not higher than the previous bar by required percentage difference - return false.
|
||||
if ((High[1] - High[2]) / High[2] < PercentageDifference) return false;
|
||||
|
||||
// If closed above the lower third/half - return false.
|
||||
if ((Close[1] - Low[1]) / (High[1] - Low[1]) > ThirdOrHalf) return false;
|
||||
|
||||
// Passed all tests.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckBuyEntry()
|
||||
{
|
||||
// If the bar isn't lower than at least one of the previous bars - return false.
|
||||
for (int i = 2; i < BarsNumber + 2; i++)
|
||||
if (Low[1] >= Low[i]) return false;
|
||||
|
||||
// If not lower than the previous bar by required percentage difference - return false.
|
||||
if ((Low[2] - Low[1]) / Low[2] < PercentageDifference) return false;
|
||||
|
||||
// If closed below the upper third/half - return false.
|
||||
if ((High[1] - Close[1]) / (High[1] - Low[1]) > ThirdOrHalf) return false;
|
||||
|
||||
// Passed all tests.
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close previous position. |
|
||||
//| order_type - skip positions of this directions. |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ClosePrev(int order_type = -1)
|
||||
{
|
||||
int total = OrdersTotal();
|
||||
for (int i = total - 1; i >= 0; i--)
|
||||
{
|
||||
if (OrderSelect(i, SELECT_BY_POS) == false) continue;
|
||||
if ((OrderSymbol() == Symbol()) && (OrderMagicNumber() == Magic))
|
||||
{
|
||||
if (OrderType() == OP_BUY)
|
||||
{
|
||||
if (order_type == OP_BUY) return false;
|
||||
RefreshRates();
|
||||
if (!OrderClose(OrderTicket(), OrderLots(), Bid, Slippage))
|
||||
{
|
||||
int e = GetLastError();
|
||||
Print("OrderClose Error: ", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (OrderType() == OP_SELL)
|
||||
{
|
||||
if (order_type == OP_SELL) return false;
|
||||
RefreshRates();
|
||||
if (!OrderClose(OrderTicket(), OrderLots(), Ask, Slippage))
|
||||
{
|
||||
int e = GetLastError();
|
||||
Print("OrderClose Error: ", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Sell |
|
||||
//+------------------------------------------------------------------+
|
||||
int fSell()
|
||||
{
|
||||
RefreshRates();
|
||||
int result = OrderSend(Symbol(), OP_SELL, Lots, Bid, Slippage, 0, 0, OrderCommentary, Magic);
|
||||
if (result == -1)
|
||||
{
|
||||
int e = GetLastError();
|
||||
Print("OrderSend Error: ", e);
|
||||
}
|
||||
else return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Buy |
|
||||
//+------------------------------------------------------------------+
|
||||
int fBuy()
|
||||
{
|
||||
RefreshRates();
|
||||
int result = OrderSend(Symbol(), OP_BUY, Lots, Ask, Slippage, 0, 0, OrderCommentary, Magic);
|
||||
if (result == -1)
|
||||
{
|
||||
int e = GetLastError();
|
||||
Print("OrderSend Error: ", e);
|
||||
}
|
||||
else return result;
|
||||
return 0;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,519 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DonchianTurtle_v3_Consensus.mq5 |
|
||||
//| EA v4 — Donchian Turtle + QuantAgent-Inspired Consensus Filter |
|
||||
//| |
|
||||
//| Base: V3.13 (Volatility Scaling + CSV Logging) |
|
||||
//| New: Consensus gate — RSI + MACD + LinReg must agree |
|
||||
//| Inspired by QuantAgent paper (arXiv:2509.09995) |
|
||||
//| |
|
||||
//| Validated params (Python IS/OOS + MT5 86% quality OOS): |
|
||||
//| S1: Donchian(20/8), S2: Donchian(40/8) |
|
||||
//| ADX>20, SL=2.0xATR(20), Vol Scaling ON |
|
||||
//| Consensus: RSI>50 + MACD cross + Price>LinReg(50) |
|
||||
//| Scorecard: 81/100 Grade B (OOS 2023-2025) |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "EA v4 — Turtle Consensus"
|
||||
#property version "4.00"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
//--- System 1
|
||||
input group "=== System 1 (Donchian 20) ==="
|
||||
input int S1_EntryPeriod = 20;
|
||||
input int S1_ExitPeriod = 8; // Updated: was 10
|
||||
input double S1_RiskPct = 0.5;
|
||||
|
||||
//--- System 2
|
||||
input group "=== System 2 (Donchian 40) ==="
|
||||
input int S2_EntryPeriod = 40; // Updated: was 55
|
||||
input int S2_ExitPeriod = 8; // Updated: was 10
|
||||
input double S2_RiskPct = 0.5;
|
||||
|
||||
//--- ATR / Base Filters
|
||||
input group "=== Filters ==="
|
||||
input int ADX_Period = 20;
|
||||
input double ADX_MinLevel = 20.0; // Updated: was 25
|
||||
input int ATR_Period = 20;
|
||||
input double ATR_StopMult = 2.0;
|
||||
input int MA_Period = 200;
|
||||
input double ATR_SpikeMult = 3.0;
|
||||
input double MaxDrawdownPct = 20.0;
|
||||
|
||||
//--- Break-Even + Trailing
|
||||
input group "=== Break-Even + Trailing Stop ==="
|
||||
input bool UseBreakEven = true;
|
||||
input double BE_RMultiple = 1.0;
|
||||
input bool UseTrailing = true;
|
||||
input double Trail_RMultiple = 2.0;
|
||||
input double Trail_ATRMult = 1.5;
|
||||
|
||||
//--- Volatility Scaling
|
||||
input group "=== Volatility Scaling ==="
|
||||
input bool UseVolScaling = true;
|
||||
input int VolScale_Period = 252;
|
||||
input double VolScale_LowPct = 0.33;
|
||||
input double VolScale_HighPct = 0.67;
|
||||
input double VolScale_LowMult = 1.5;
|
||||
input double VolScale_HighMult = 0.5;
|
||||
|
||||
//--- Consensus Filter (QuantAgent-Inspired)
|
||||
input group "=== Consensus Filter (QuantAgent-Inspired) ==="
|
||||
input bool UseConsensus = true; // เปิด/ปิด consensus gate
|
||||
input int ConsensusMin = 2; // ต้องผ่านอย่างน้อยกี่ conditions (max=3)
|
||||
// Condition 1: RSI momentum
|
||||
input int RSI_Period = 14;
|
||||
input double RSI_BullLevel = 50.0; // RSI > 50 = bullish
|
||||
// Condition 2: MACD direction
|
||||
input int MACD_Fast = 12;
|
||||
input int MACD_Slow = 26;
|
||||
input int MACD_Signal = 9;
|
||||
// Condition 3: Price vs OLS trend line
|
||||
input int LinReg_Period = 50; // Linear regression period (TrendAgent)
|
||||
|
||||
//--- Magic Numbers
|
||||
input group "=== Order Settings ==="
|
||||
input int MagicS1 = 202901; // New magic (v4)
|
||||
input int MagicS2 = 202902;
|
||||
input string TradeComment = "Turtle_v4_Consensus";
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//--- Globals
|
||||
CTrade trade;
|
||||
int g_hATR = INVALID_HANDLE;
|
||||
int g_hADX = INVALID_HANDLE;
|
||||
int g_hMA = INVALID_HANDLE;
|
||||
int g_hRSI = INVALID_HANDLE;
|
||||
int g_hMACD = INVALID_HANDLE;
|
||||
int g_hLR = INVALID_HANDLE; // Linear Regression handle
|
||||
|
||||
double g_AccountPeak = 0;
|
||||
datetime g_LastBarTime = 0;
|
||||
int g_hLog = INVALID_HANDLE;
|
||||
double g_lastVolMult = 1.0;
|
||||
double g_lastATRpct = -1.0;
|
||||
int g_lastConsensus = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Base indicators
|
||||
g_hATR = iATR(Symbol(), PERIOD_D1, ATR_Period);
|
||||
g_hADX = iADX(Symbol(), PERIOD_D1, ADX_Period);
|
||||
g_hMA = iMA(Symbol(), PERIOD_D1, MA_Period, 0, MODE_SMA, PRICE_CLOSE);
|
||||
|
||||
// Consensus indicators
|
||||
g_hRSI = iRSI(Symbol(), PERIOD_D1, RSI_Period, PRICE_CLOSE);
|
||||
g_hMACD = iMACD(Symbol(), PERIOD_D1, MACD_Fast, MACD_Slow, MACD_Signal, PRICE_CLOSE);
|
||||
g_hLR = iMA(Symbol(), PERIOD_D1, LinReg_Period, 0, MODE_SMA, PRICE_CLOSE); // SMA50 as trend proxy (iLinReg not in MQL5 std)
|
||||
|
||||
if(g_hATR == INVALID_HANDLE || g_hADX == INVALID_HANDLE ||
|
||||
g_hMA == INVALID_HANDLE || g_hRSI == INVALID_HANDLE ||
|
||||
g_hMACD == INVALID_HANDLE || g_hLR == INVALID_HANDLE)
|
||||
{
|
||||
Print("ERROR: Failed to create indicator handles");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Warmup (skip in backtester)
|
||||
bool inTester = (bool)MQLInfoInteger(MQL_TESTER);
|
||||
if(!inTester)
|
||||
{
|
||||
double dummy[1];
|
||||
int attempts = 0;
|
||||
while(CopyBuffer(g_hMA, 0, 1, 1, dummy) <= 0 && attempts < 100)
|
||||
{
|
||||
Sleep(100);
|
||||
attempts++;
|
||||
}
|
||||
if(attempts >= 100)
|
||||
{
|
||||
Print("ERROR: Indicators not ready.");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
trade.SetDeviationInPoints(50);
|
||||
trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
g_AccountPeak = AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
|
||||
// CSV log
|
||||
string fname = "TurtleConsensus_" + Symbol() + "_trades.csv";
|
||||
g_hLog = FileOpen(fname, FILE_WRITE|FILE_READ|FILE_CSV|FILE_ANSI|FILE_SHARE_READ, ',');
|
||||
if(g_hLog == INVALID_HANDLE)
|
||||
Print("WARNING: Cannot open log file");
|
||||
else
|
||||
{
|
||||
if(FileTell(g_hLog) == 0)
|
||||
FileWrite(g_hLog,
|
||||
"Timestamp","Event","System","Magic",
|
||||
"Lots","Price","SL","RiskPct",
|
||||
"ATR_pct","VolMult","Consensus","PnL","Balance","Note");
|
||||
FileSeek(g_hLog, 0, SEEK_END);
|
||||
FileFlush(g_hLog);
|
||||
}
|
||||
|
||||
PrintFormat("DonchianTurtle v4 Consensus | %s D1 | S1:%d/%d S2:%d/%d ADX>%.0f SL=%.1fx ConsMin=%d",
|
||||
Symbol(), S1_EntryPeriod, S1_ExitPeriod,
|
||||
S2_EntryPeriod, S2_ExitPeriod,
|
||||
ADX_MinLevel, ATR_StopMult, ConsensusMin);
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
IndicatorRelease(g_hATR);
|
||||
IndicatorRelease(g_hADX);
|
||||
IndicatorRelease(g_hMA);
|
||||
IndicatorRelease(g_hRSI);
|
||||
IndicatorRelease(g_hMACD);
|
||||
IndicatorRelease(g_hLR);
|
||||
if(g_hLog != INVALID_HANDLE) { FileFlush(g_hLog); FileClose(g_hLog); }
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Trail runs every tick
|
||||
double atrNow[1];
|
||||
double atr = 0;
|
||||
if(CopyBuffer(g_hATR, 0, 0, 1, atrNow) > 0) atr = atrNow[0];
|
||||
if(atr > 0)
|
||||
{
|
||||
ManageTrail(MagicS1, atr);
|
||||
ManageTrail(MagicS2, atr);
|
||||
}
|
||||
|
||||
// New bar check
|
||||
datetime barTime = iTime(Symbol(), PERIOD_D1, 0);
|
||||
if(barTime == g_LastBarTime) return;
|
||||
g_LastBarTime = barTime;
|
||||
|
||||
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
if(balance > g_AccountPeak) g_AccountPeak = balance;
|
||||
|
||||
// DD halt
|
||||
if(g_AccountPeak > 0)
|
||||
{
|
||||
double dd = (g_AccountPeak - balance) / g_AccountPeak * 100.0;
|
||||
if(dd >= MaxDrawdownPct)
|
||||
{
|
||||
PrintFormat("DD HALT: %.1f%% >= %.1f%%", dd, MaxDrawdownPct);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Read base indicators (bar[1] = last completed bar)
|
||||
double atrBuf[1], adxBuf[1], maBuf[1];
|
||||
if(CopyBuffer(g_hATR, 0, 1, 1, atrBuf) <= 0) return;
|
||||
if(CopyBuffer(g_hADX, 0, 1, 1, adxBuf) <= 0) return;
|
||||
if(CopyBuffer(g_hMA, 0, 1, 1, maBuf) <= 0) return;
|
||||
|
||||
double atrD1 = atrBuf[0];
|
||||
double adx = adxBuf[0];
|
||||
double ma200 = maBuf[0];
|
||||
double close1 = iClose(Symbol(), PERIOD_D1, 1);
|
||||
|
||||
if(atrD1 <= 0 || adx <= 0 || ma200 <= 0 || close1 <= 0) return;
|
||||
|
||||
// ATR spike filter
|
||||
double atrArr[20];
|
||||
double atrAvg = 0;
|
||||
if(CopyBuffer(g_hATR, 0, 1, 20, atrArr) == 20)
|
||||
{
|
||||
for(int k = 0; k < 20; k++) atrAvg += atrArr[k];
|
||||
atrAvg /= 20.0;
|
||||
}
|
||||
if(atrAvg > 0 && atrD1 > atrAvg * ATR_SpikeMult) return;
|
||||
|
||||
// Donchian exits
|
||||
ManageExits(MagicS1, S1_ExitPeriod);
|
||||
ManageExits(MagicS2, S2_ExitPeriod);
|
||||
|
||||
// Base entry filters
|
||||
if(close1 <= ma200) return; // Below MA200
|
||||
if(adx < ADX_MinLevel) return; // Weak trend
|
||||
|
||||
// Consensus check (QuantAgent-inspired)
|
||||
g_lastConsensus = 0;
|
||||
if(UseConsensus)
|
||||
{
|
||||
g_lastConsensus = GetConsensusScore();
|
||||
if(g_lastConsensus < ConsensusMin)
|
||||
{
|
||||
PrintFormat("Consensus FAIL: score=%d/%d (need %d) — skip entry",
|
||||
g_lastConsensus, 3, ConsensusMin);
|
||||
return;
|
||||
}
|
||||
PrintFormat("Consensus PASS: score=%d/3", g_lastConsensus);
|
||||
}
|
||||
|
||||
// Volatility scaling
|
||||
double scaledRiskS1 = S1_RiskPct;
|
||||
double scaledRiskS2 = S2_RiskPct;
|
||||
g_lastVolMult = 1.0;
|
||||
g_lastATRpct = -1.0;
|
||||
if(UseVolScaling)
|
||||
{
|
||||
double atrPct = GetATRPercentile(VolScale_Period, 1);
|
||||
double mult = 1.0;
|
||||
if(atrPct >= 0 && atrPct < VolScale_LowPct) mult = VolScale_LowMult;
|
||||
else if(atrPct > VolScale_HighPct) mult = VolScale_HighMult;
|
||||
scaledRiskS1 = S1_RiskPct * mult;
|
||||
scaledRiskS2 = S2_RiskPct * mult;
|
||||
g_lastVolMult = mult;
|
||||
g_lastATRpct = atrPct;
|
||||
}
|
||||
|
||||
// Entries
|
||||
if(!HasPosition(MagicS1))
|
||||
TryEntry(MagicS1, S1_EntryPeriod, scaledRiskS1, atrD1, close1, "S1");
|
||||
if(!HasPosition(MagicS2))
|
||||
TryEntry(MagicS2, S2_EntryPeriod, scaledRiskS2, atrD1, close1, "S2");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Consensus Score — 3 conditions from QuantAgent |
|
||||
//| Returns 0-3. Called after base filters pass. |
|
||||
//+------------------------------------------------------------------+
|
||||
int GetConsensusScore()
|
||||
{
|
||||
int score = 0;
|
||||
|
||||
// Condition 1: RSI(14) > 50 — bullish momentum (IndicatorAgent)
|
||||
double rsiBuf[1];
|
||||
if(CopyBuffer(g_hRSI, 0, 1, 1, rsiBuf) > 0)
|
||||
{
|
||||
if(rsiBuf[0] > RSI_BullLevel)
|
||||
{
|
||||
score++;
|
||||
PrintFormat(" [C1] RSI=%.1f > %.1f PASS", rsiBuf[0], RSI_BullLevel);
|
||||
}
|
||||
else
|
||||
PrintFormat(" [C1] RSI=%.1f <= %.1f FAIL", rsiBuf[0], RSI_BullLevel);
|
||||
}
|
||||
|
||||
// Condition 2: MACD line > Signal line — directional confirm (IndicatorAgent)
|
||||
double macdMain[1], macdSig[1];
|
||||
if(CopyBuffer(g_hMACD, MAIN_LINE, 1, 1, macdMain) > 0 &&
|
||||
CopyBuffer(g_hMACD, SIGNAL_LINE, 1, 1, macdSig) > 0)
|
||||
{
|
||||
if(macdMain[0] > macdSig[0])
|
||||
{
|
||||
score++;
|
||||
PrintFormat(" [C2] MACD=%.4f > Signal=%.4f PASS", macdMain[0], macdSig[0]);
|
||||
}
|
||||
else
|
||||
PrintFormat(" [C2] MACD=%.4f <= Signal=%.4f FAIL", macdMain[0], macdSig[0]);
|
||||
}
|
||||
|
||||
// Condition 3: Price above OLS Linear Regression line — trend bias (TrendAgent)
|
||||
double lrBuf[1];
|
||||
double close1 = iClose(Symbol(), PERIOD_D1, 1);
|
||||
if(CopyBuffer(g_hLR, 0, 1, 1, lrBuf) > 0)
|
||||
{
|
||||
if(close1 > lrBuf[0])
|
||||
{
|
||||
score++;
|
||||
PrintFormat(" [C3] Close=%.2f > LinReg=%.2f PASS", close1, lrBuf[0]);
|
||||
}
|
||||
else
|
||||
PrintFormat(" [C3] Close=%.2f <= LinReg=%.2f FAIL", close1, lrBuf[0]);
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
void TryEntry(int magic, int period, double riskPct, double atr,
|
||||
double close1, string label)
|
||||
{
|
||||
// Donchian entry band (bars 2..period+1, shift=2 matching MQL5 convention)
|
||||
int hiIdx = iHighest(Symbol(), PERIOD_D1, MODE_HIGH, period, 2);
|
||||
if(hiIdx < 0) return;
|
||||
double prevBand = iHigh(Symbol(), PERIOD_D1, hiIdx);
|
||||
|
||||
if(close1 <= prevBand) return; // No breakout
|
||||
|
||||
double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
|
||||
double sl = ask - atr * ATR_StopMult;
|
||||
double lots = CalcLots(ask, sl, riskPct);
|
||||
if(lots <= 0) return;
|
||||
|
||||
trade.SetExpertMagicNumber(magic);
|
||||
if(trade.Buy(lots, Symbol(), ask, sl, 0, TradeComment + "_" + label))
|
||||
{
|
||||
PrintFormat("%s ENTRY | Ask=%.2f SL=%.2f Lots=%.2f Band=%.2f Vol=%.1f Cons=%d/3",
|
||||
label, ask, sl, lots, prevBand, g_lastVolMult, g_lastConsensus);
|
||||
if(g_hLog != INVALID_HANDLE)
|
||||
{
|
||||
FileWrite(g_hLog,
|
||||
TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS),
|
||||
"ENTRY", label, magic,
|
||||
DoubleToString(lots, 2),
|
||||
DoubleToString(ask, 2),
|
||||
DoubleToString(sl, 2),
|
||||
DoubleToString(riskPct, 3),
|
||||
DoubleToString(g_lastATRpct, 3),
|
||||
DoubleToString(g_lastVolMult, 2),
|
||||
IntegerToString(g_lastConsensus),
|
||||
"",
|
||||
DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2),
|
||||
"");
|
||||
FileFlush(g_hLog);
|
||||
}
|
||||
}
|
||||
else
|
||||
PrintFormat("%s FAIL | code=%d %s", label,
|
||||
trade.ResultRetcode(), trade.ResultRetcodeDescription());
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
void ManageTrail(int magic, double atr)
|
||||
{
|
||||
if(!UseBreakEven && !UseTrailing) return;
|
||||
if(atr <= 0) return;
|
||||
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(!PositionSelectByTicket(ticket)) continue;
|
||||
if(PositionGetInteger(POSITION_MAGIC) != magic) continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != Symbol()) continue;
|
||||
if(PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_BUY) continue;
|
||||
|
||||
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double curSL = PositionGetDouble(POSITION_SL);
|
||||
double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
|
||||
double initRisk = atr * ATR_StopMult;
|
||||
double profit = bid - entry;
|
||||
double newSL = curSL;
|
||||
|
||||
if(UseBreakEven && profit >= BE_RMultiple * initRisk)
|
||||
{
|
||||
double beLevel = entry + 2 * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
|
||||
if(beLevel > curSL) newSL = MathMax(newSL, beLevel);
|
||||
}
|
||||
|
||||
if(UseTrailing && profit >= Trail_RMultiple * initRisk)
|
||||
{
|
||||
double trailLevel = bid - atr * Trail_ATRMult;
|
||||
if(trailLevel > curSL) newSL = MathMax(newSL, trailLevel);
|
||||
}
|
||||
|
||||
if(newSL > curSL + SymbolInfoDouble(Symbol(), SYMBOL_POINT))
|
||||
{
|
||||
double tp = PositionGetDouble(POSITION_TP);
|
||||
trade.SetExpertMagicNumber(magic);
|
||||
trade.PositionModify(ticket, NormalizeDouble(newSL, _Digits), tp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
void ManageExits(int magic, int exitPeriod)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(!PositionSelectByTicket(ticket)) continue;
|
||||
if(PositionGetInteger(POSITION_MAGIC) != magic) continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != Symbol()) continue;
|
||||
|
||||
int loIdx = iLowest(Symbol(), PERIOD_D1, MODE_LOW, exitPeriod, 1);
|
||||
if(loIdx < 0) continue;
|
||||
double exitLow = iLow(Symbol(), PERIOD_D1, loIdx);
|
||||
double close1 = iClose(Symbol(), PERIOD_D1, 1);
|
||||
|
||||
if(close1 < exitLow)
|
||||
{
|
||||
trade.SetExpertMagicNumber(magic);
|
||||
trade.PositionClose(ticket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTradeTransaction(const MqlTradeTransaction& trans,
|
||||
const MqlTradeRequest& request,
|
||||
const MqlTradeResult& result)
|
||||
{
|
||||
if(trans.type != TRADE_TRANSACTION_DEAL_ADD) return;
|
||||
if(g_hLog == INVALID_HANDLE) return;
|
||||
if(!HistoryDealSelect(trans.deal)) return;
|
||||
|
||||
long dealEntry = HistoryDealGetInteger(trans.deal, DEAL_ENTRY);
|
||||
if(dealEntry != DEAL_ENTRY_OUT && dealEntry != DEAL_ENTRY_INOUT) return;
|
||||
|
||||
long magic = HistoryDealGetInteger(trans.deal, DEAL_MAGIC);
|
||||
if(magic != MagicS1 && magic != MagicS2) return;
|
||||
|
||||
string system = (magic == MagicS1) ? "S1" : "S2";
|
||||
double profit = HistoryDealGetDouble(trans.deal, DEAL_PROFIT)
|
||||
+ HistoryDealGetDouble(trans.deal, DEAL_SWAP)
|
||||
+ HistoryDealGetDouble(trans.deal, DEAL_COMMISSION);
|
||||
double price = HistoryDealGetDouble(trans.deal, DEAL_PRICE);
|
||||
double lots = HistoryDealGetDouble(trans.deal, DEAL_VOLUME);
|
||||
string outcome = (profit >= 0) ? "WIN" : "LOSS";
|
||||
|
||||
FileWrite(g_hLog,
|
||||
TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS),
|
||||
"EXIT_" + outcome, system, magic,
|
||||
DoubleToString(lots, 2),
|
||||
DoubleToString(price, 2),
|
||||
"", "", "", "", "",
|
||||
DoubleToString(profit, 2),
|
||||
DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2),
|
||||
outcome);
|
||||
FileFlush(g_hLog);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
bool HasPosition(int magic)
|
||||
{
|
||||
for(int i = 0; i < PositionsTotal(); i++)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(!PositionSelectByTicket(ticket)) continue;
|
||||
if(PositionGetInteger(POSITION_MAGIC) == magic &&
|
||||
PositionGetString(POSITION_SYMBOL) == Symbol())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
double GetATRPercentile(int period, int shift)
|
||||
{
|
||||
double atrArr[];
|
||||
ArraySetAsSeries(atrArr, true);
|
||||
int copied = CopyBuffer(g_hATR, 0, shift, period, atrArr);
|
||||
if(copied < period) return -1.0;
|
||||
double curATR = atrArr[0];
|
||||
int rank = 0;
|
||||
for(int i = 1; i < period; i++)
|
||||
if(atrArr[i] < curATR) rank++;
|
||||
return (double)rank / (double)(period - 1);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
double CalcLots(double entry, double sl, double riskPct)
|
||||
{
|
||||
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
double riskAmt = balance * riskPct / 100.0;
|
||||
double slDist = MathAbs(entry - sl);
|
||||
if(slDist <= 0) return 0;
|
||||
|
||||
double tickVal = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE);
|
||||
double tickSize = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE);
|
||||
if(tickSize <= 0 || tickVal <= 0) return 0;
|
||||
|
||||
double lots = riskAmt / ((slDist / tickSize) * tickVal);
|
||||
double step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
|
||||
double minL = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
|
||||
double maxL = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX);
|
||||
|
||||
lots = MathFloor(lots / step) * step;
|
||||
return MathMax(minL, MathMin(maxL, lots));
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,38 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| TrendEngine Core Demo – MQL5 EA |
|
||||
//| Author: Hossein Asgari (Fintor AI) |
|
||||
//+------------------------------------------------------------------+
|
||||
#property strict
|
||||
#property copyright "Hossein Asgari - Fintor AI"
|
||||
#property link "https://fintorai.com"
|
||||
#property version "1.00"
|
||||
|
||||
#include <TrendEngine/TrendEngineCore.mqh>
|
||||
|
||||
//--- inputs
|
||||
input double InpLots = 0.10;
|
||||
input int InpMaFast = 20;
|
||||
input int InpMaSlow = 50;
|
||||
input int InpSlPoints = 300;
|
||||
input int InpTpPoints = 600;
|
||||
|
||||
//--- core engine
|
||||
CTrendEngineCore g_engine;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
g_engine.Init(InpLots,InpMaFast,InpMaSlow,InpSlPoints,InpTpPoints);
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
g_engine.Deinit();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
g_engine.OnTick();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,106 @@
|
||||
#include <stderror.mqh>
|
||||
#include <stdlib.mqh>
|
||||
|
||||
#property copyright "Copyright 2023, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
// config parameters
|
||||
double stopLoss = 2.5;
|
||||
double takeProfit = 3;
|
||||
double Lots = 0.01;
|
||||
int waitForOrderTime = 60; // seconds
|
||||
|
||||
// global variables
|
||||
double tickPrices[];
|
||||
int tickCounts = 0;
|
||||
datetime lastOrderOpenTime = 0;
|
||||
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
// create zero array for tickPrices
|
||||
ArraySetAsSeries(tickPrices, true);
|
||||
ArrayResize(tickPrices, stdTicksNumbers);
|
||||
ArraySetAsSeries(tickPrices, false);
|
||||
ArrayInitialize(tickPrices, 0.0);
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void PlaceOrderBuy()
|
||||
{
|
||||
double orderPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double tp = orderPrice + takeProfitDistance;
|
||||
double sl = orderPrice - stopLossDistance;
|
||||
int result = OrderSend(_Symbol, OP_BUY, Lots, orderPrice , 10, sl, tp, "Buy Order", 0, 0, clrGreen);
|
||||
if (result > 0){
|
||||
Print("Buy order placed. Ticket: ", result);
|
||||
}
|
||||
else{
|
||||
int error = GetLastError();
|
||||
string errorDescription = ErrorDescription(error);
|
||||
Print("OrderSend failed with error #", error, ": ", errorDescription);
|
||||
}
|
||||
}
|
||||
|
||||
void PlaceOrderSell()
|
||||
{
|
||||
double orderPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double tp = orderPrice - takeProfitDistance;
|
||||
double sl = orderPrice + stopLossDistance;
|
||||
int result = OrderSend(_Symbol, OP_SELL, Lots, orderPrice , 10, sl, tp, "Sell Order", 0, 0, clrRed);
|
||||
if (result > 0){
|
||||
Print("Sell order placed. Ticket: ", result);
|
||||
}
|
||||
else{
|
||||
int error = GetLastError();
|
||||
string errorDescription = ErrorDescription(error);
|
||||
Print("OrderSend failed with error #", error, ": ", errorDescription);
|
||||
}
|
||||
}
|
||||
|
||||
void updateTickData()
|
||||
{
|
||||
tickCounts ++;
|
||||
// shift
|
||||
for(int i = stdTicksNumbers - 1; i > 0; i--)
|
||||
tickPrices[i] = tickPrices[i-1];
|
||||
tickPrices[0] = (Bid + Ask) / 2.0;
|
||||
}
|
||||
|
||||
double getSlope(const double &array[]){
|
||||
return 0;
|
||||
}
|
||||
bool BuyIsOK(){
|
||||
|
||||
}
|
||||
|
||||
bool SellIsOK(){
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
updateTickData();
|
||||
datetime currentTime = iTime(NULL, 0, 0);
|
||||
int timeDifferenceSinceLastOrder = currentTime - lastOrderOpenTime;
|
||||
if (timeDifferenceSinceLastOrder > waitForOrderTime || lastOrderOpenTime == 0)
|
||||
{
|
||||
if (BuyIsOK())
|
||||
{
|
||||
PlaceOrderBuy();
|
||||
lastOrderOpenTime = iTime(NULL, 0, 0);
|
||||
}
|
||||
else if (SellIsOK())
|
||||
{
|
||||
PlaceOrderSell();
|
||||
lastOrderOpenTime = iTime(NULL, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @copyright 2019, pipbolt.io <beta@pipbolt.io>
|
||||
* @license https://github.com/pipbolt/experts/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
#include <PipboltFramework\Constants.mqh>
|
||||
|
||||
#define NAME "Bollinger Bands EA"
|
||||
#define VERSION "0.022"
|
||||
|
||||
#property copyright COPYRIGHT
|
||||
#property link LINK
|
||||
#property icon ICON
|
||||
#property description DESCRIPTION
|
||||
#property version VERSION
|
||||
|
||||
#include <PipboltFramework\Params\MainSettings.mqh>
|
||||
|
||||
input group "Entry Strategy";
|
||||
|
||||
input group "Exit Strategy";
|
||||
input bool UseExitStrategy = false; // Use Exit Strategy
|
||||
|
||||
input group "Bollinger Bands";
|
||||
input int Bands_Period = 20; // Period
|
||||
input double Bands_Deviation = 2; // Deviation
|
||||
input ENUM_APPLIED_PRICE Bands_Applied_Price = PRICE_CLOSE; // Applied Price
|
||||
|
||||
#include <PipboltFramework\Experts.mqh>
|
||||
|
||||
CiBollinger BBands;
|
||||
|
||||
int OnInit(void)
|
||||
{
|
||||
if (ONINIT() != INIT_SUCCEEDED)
|
||||
return INIT_FAILED;
|
||||
|
||||
BBands.Init(NULL, NULL, Bands_Period, 0, Bands_Deviation, Bands_Applied_Price);
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnTick(void) { ONTICK(); }
|
||||
void OnDeinit(const int reason) { ONDEINIT(reason); }
|
||||
void OnTimer() { ONTIMER(); }
|
||||
|
||||
void CheckForOpen(bool &openBuy, bool &openSell)
|
||||
{
|
||||
// Close variable
|
||||
double close = iClose(NULL, NULL, _indicatorShift);
|
||||
|
||||
// Buy Entry Strategy
|
||||
openBuy = close < BBands.Lower(0);
|
||||
|
||||
// Sell Entry Strategy
|
||||
openSell = close > BBands.Upper(0);
|
||||
|
||||
// Apply MA Filter
|
||||
openBuy = openBuy && MAFilter.Check(DIR_BUY);
|
||||
openSell = openSell && MAFilter.Check(DIR_SELL);
|
||||
}
|
||||
|
||||
void CheckForClose(bool &closeBuy, bool &closeSell)
|
||||
{
|
||||
// Close variable
|
||||
double close = iClose(NULL, NULL, _indicatorShift);
|
||||
|
||||
// Buy Exit Strategy
|
||||
closeBuy = close > BBands.Upper(0);
|
||||
|
||||
// Sell Exit Strategy
|
||||
closeSell = close < BBands.Lower(0);
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @copyright 2019, pipbolt.io <beta@pipbolt.io>
|
||||
* @license https://github.com/pipbolt/experts/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
#include <PipboltFramework\Constants.mqh>
|
||||
|
||||
#define NAME "Ichimoku EA"
|
||||
#define VERSION "0.022"
|
||||
|
||||
#property copyright COPYRIGHT
|
||||
#property link LINK
|
||||
#property icon ICON
|
||||
#property description DESCRIPTION
|
||||
#property version VERSION
|
||||
|
||||
#include <PipboltFramework\Params\MainSettings.mqh>
|
||||
|
||||
input group "Entry Strategy";
|
||||
|
||||
input group "Exit Strategy";
|
||||
input bool UseExitStrategy = false; // Use Exit Strategy
|
||||
|
||||
input group "Ichimoku Kynko Hyo";
|
||||
input int tenkanSen = 9; // period of Tenkan-sen
|
||||
input int kijunSen = 26; // period of Kijun-sen
|
||||
input int senkouSpanB = 52; // period of Senkou Span B
|
||||
|
||||
#include <PipboltFramework\Experts.mqh>
|
||||
|
||||
CiIchimoku Ichimoku;
|
||||
|
||||
int OnInit(void)
|
||||
{
|
||||
if (ONINIT() != INIT_SUCCEEDED)
|
||||
return INIT_FAILED;
|
||||
|
||||
Ichimoku.Init(NULL, NULL, tenkanSen, kijunSen, senkouSpanB);
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnTick(void) { ONTICK(); }
|
||||
void OnDeinit(const int reason) { ONDEINIT(reason); }
|
||||
void OnTimer() { ONTIMER(); }
|
||||
|
||||
void CheckForOpen(bool &openBuy, bool &openSell)
|
||||
{
|
||||
// Buy Entry Strategy
|
||||
openBuy = Ichimoku.TenkanSen(0) > Ichimoku.KijunSen(0) && Ichimoku.TenkanSen(1) <= Ichimoku.KijunSen(1);
|
||||
|
||||
// Sell Entry Stategy
|
||||
openSell = Ichimoku.TenkanSen(0) < Ichimoku.KijunSen(0) && Ichimoku.TenkanSen(1) >= Ichimoku.KijunSen(1);
|
||||
|
||||
// Apply MA Filter
|
||||
openBuy = openBuy && MAFilter.Check(DIR_BUY);
|
||||
openSell = openSell && MAFilter.Check(DIR_SELL);
|
||||
}
|
||||
|
||||
void CheckForClose(bool &closeBuy, bool &closeSell)
|
||||
{
|
||||
// Buy Exit Strategy
|
||||
closeBuy = Ichimoku.TenkanSen(0) <= Ichimoku.KijunSen(0);
|
||||
|
||||
// Sell Exit Stategy
|
||||
closeSell = Ichimoku.TenkanSen(0) >= Ichimoku.KijunSen(0);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,825 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| ConservativeScalper.mq4 |
|
||||
//| MT4 Conservative Scalping Expert Advisor|
|
||||
//| |
|
||||
//| Strategy: |
|
||||
//| - M15 trend bias via EMA 50/200 |
|
||||
//| - M5 execution via EMA 20 + RSI 14 + candle breakout |
|
||||
//| - Session, spread, ATR, rollover, and day-of-week filters |
|
||||
//| - Fixed-fractional risk sizing (default 0.25% per trade) |
|
||||
//| - Hard SL/TP on every trade — no martingale, no grid |
|
||||
//| - Break-even, trailing stop, time-based exit |
|
||||
//| - Daily loss cap, max trades/day, consecutive loss pause |
|
||||
//| - Equity drawdown hard stop |
|
||||
//| |
|
||||
//| Pairs: EURUSD, GBPUSD, USDJPY (tune per pair) |
|
||||
//| Timeframe: M5 (with M15 bias) |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "NAK"
|
||||
#property link "https://github.com/NadirAliOffical/conservative-scalper-ea"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
//====================================================================
|
||||
// GENERAL INPUTS
|
||||
//====================================================================
|
||||
extern int MagicNumber = 20260409; // Unique EA identifier
|
||||
extern string TradeComment = "CScalp"; // Order comment tag
|
||||
extern bool EnableLong = true; // Allow buy trades
|
||||
extern bool EnableShort = true; // Allow sell trades
|
||||
extern bool OneTradePerSymbol = true; // One open trade per symbol
|
||||
extern bool AllowNewTrades = true; // Master on/off switch
|
||||
|
||||
//====================================================================
|
||||
// SESSION / TIME FILTERS
|
||||
//====================================================================
|
||||
extern int SessionStartHour = 8; // Server hour to start trading
|
||||
extern int SessionEndHour = 17; // Server hour to stop new trades
|
||||
extern bool AllowMonday = true;
|
||||
extern bool AllowTuesday = true;
|
||||
extern bool AllowWednesday = true;
|
||||
extern bool AllowThursday = true;
|
||||
extern bool AllowFriday = false; // Off by default — thin close
|
||||
extern int RolloverBlockBefore = 30; // Mins to block before 00:00
|
||||
extern int RolloverBlockAfter = 30; // Mins to block after 00:00
|
||||
|
||||
//====================================================================
|
||||
// BIAS INDICATORS (Higher timeframe)
|
||||
//====================================================================
|
||||
extern ENUM_TIMEFRAMES BiasTimeframe = PERIOD_M15; // Trend filter timeframe
|
||||
extern int BiasFastEMA = 50; // Fast EMA period on bias TF
|
||||
extern int BiasSlowEMA = 200; // Slow EMA period on bias TF
|
||||
|
||||
//====================================================================
|
||||
// EXECUTION INDICATORS (Chart timeframe — run EA on M5)
|
||||
//====================================================================
|
||||
extern int ExecEMA_Period = 20; // EMA for local direction
|
||||
extern int RSI_Period = 14; // RSI period
|
||||
extern double RSI_LongLevel = 50.0; // RSI cross-above for longs
|
||||
extern double RSI_ShortLevel = 50.0; // RSI cross-below for shorts
|
||||
extern int ATR_Period = 14; // ATR period
|
||||
extern double MinATR_Pips = 3.0; // Min ATR (pips) — avoid dead mkt
|
||||
extern int BreakoutBars = 1; // Bars back for high/low breakout
|
||||
|
||||
//====================================================================
|
||||
// RISK SIZING
|
||||
//====================================================================
|
||||
extern int LotSizingMode = 1; // 0=Fixed lot 1=Risk %
|
||||
extern double FixedLot = 0.01; // Used when mode=0
|
||||
extern double RiskPercent = 0.50; // % of equity risked per trade
|
||||
extern double MaxSpreadPips = 2.5; // Max allowed spread in pips
|
||||
extern int MaxSlippagePts = 3; // Max slippage in broker points
|
||||
|
||||
//====================================================================
|
||||
// STOP LOSS / TAKE PROFIT
|
||||
//====================================================================
|
||||
extern int StopLossMode = 1; // 0=Fixed pips 1=ATR multiple
|
||||
extern double StopLossPips = 8.0; // Fixed SL (pips) mode=0
|
||||
extern double StopLossATRMult = 1.2; // ATR multiplier for SL mode=1
|
||||
extern int TakeProfitMode = 1; // 0=Fixed pips 1=ATR multiple
|
||||
extern double TakeProfitPips = 10.0; // Fixed TP (pips) mode=0
|
||||
extern double TakeProfitATRMult = 1.2; // ATR multiplier for TP mode=1
|
||||
|
||||
//====================================================================
|
||||
// TRADE MANAGEMENT
|
||||
//====================================================================
|
||||
extern bool UseBreakEven = true;
|
||||
extern double BreakEvenTriggerR = 0.8; // Move SL to BE after 0.8R profit
|
||||
extern double BreakEvenOffsetPips = 0.5; // Buffer pips beyond entry for BE
|
||||
extern bool UseTrailingStop = false;
|
||||
extern double TrailingStartR = 1.0; // Start trailing after 1R profit
|
||||
extern double TrailingDistancePips = 5.0; // Trail distance in pips
|
||||
extern bool UseTimeExit = true;
|
||||
extern int MaxTradeMinutes = 20; // Close stalled trades after N min
|
||||
extern bool CloseAtSessionEnd = true; // Close open trades at session end
|
||||
|
||||
//====================================================================
|
||||
// DAILY / SESSION PROTECTION
|
||||
//====================================================================
|
||||
extern int MaxTradesPerDay = 6; // Max new trades per session day
|
||||
extern double MaxDailyLossPercent = 2.0; // Stop trading if daily loss >= X%
|
||||
extern int MaxConsecutiveLosses = 3; // Pause after N consecutive losses
|
||||
extern double MaxDrawdownPercent = 20.0; // Hard stop if equity DD >= X%
|
||||
extern double MaxTotalOpenRiskPct = 1.0; // Cap on total open risk %
|
||||
|
||||
//====================================================================
|
||||
// NEWS FILTER (auto-fetches ForexFactory calendar)
|
||||
//====================================================================
|
||||
extern bool UseNewsFilter = true; // Enable automatic news filter
|
||||
extern string NewsFilterCurrencies = "USD,EUR,GBP";// Block news for these currencies
|
||||
extern int NewsBlockMinsBefore = 30; // Mins to block before event
|
||||
extern int NewsBlockMinsAfter = 30; // Mins to block after event
|
||||
extern int BrokerGMTOffset = 2; // Broker server GMT offset (check chart)
|
||||
|
||||
//====================================================================
|
||||
// GLOBALS
|
||||
//====================================================================
|
||||
double g_pip; // Value of 1 pip in price units
|
||||
double g_point; // Broker point
|
||||
int g_digits; // Symbol digits
|
||||
int g_todayTrades; // Trades opened today
|
||||
double g_todayStartEquity; // Equity at start of today
|
||||
int g_consecutiveLosses; // Rolling loss streak count
|
||||
int g_lastHistoryTotal; // History size snapshot (for tracking closed orders)
|
||||
bool g_tradingHalted; // True when max DD hit (persists across days)
|
||||
double g_peakEquity; // All-time equity high for DD calculation
|
||||
datetime g_lastTradeDay; // Date of last counter reset
|
||||
|
||||
// News filter globals
|
||||
datetime g_newsEvents[];
|
||||
int g_newsEventCount = 0;
|
||||
datetime g_lastNewsFetch = 0;
|
||||
datetime g_lastNewsLogTime = 0;
|
||||
|
||||
// GlobalVariable key names (set in OnInit)
|
||||
string g_gvPeak;
|
||||
string g_gvHalt;
|
||||
string g_gvConsec;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| INIT |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
g_digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
|
||||
|
||||
// Normalise pip for 4-digit and 5-digit brokers
|
||||
if(g_digits == 5 || g_digits == 3)
|
||||
g_pip = Point * 10;
|
||||
else
|
||||
g_pip = Point;
|
||||
g_point = Point;
|
||||
|
||||
// GlobalVariable keys unique to this symbol + magic number
|
||||
string suffix = Symbol() + "_" + IntegerToString(MagicNumber);
|
||||
g_gvPeak = "CScalp_Peak_" + suffix;
|
||||
g_gvHalt = "CScalp_Halt_" + suffix;
|
||||
g_gvConsec = "CScalp_Consec_" + suffix;
|
||||
|
||||
g_todayTrades = 0;
|
||||
g_todayStartEquity = AccountEquity();
|
||||
g_lastHistoryTotal = OrdersHistoryTotal();
|
||||
g_lastTradeDay = 0;
|
||||
|
||||
// Restore persistent state so restarts don't reset DD protection
|
||||
g_peakEquity = GlobalVariableCheck(g_gvPeak) ? GlobalVariableGet(g_gvPeak) : AccountEquity();
|
||||
g_tradingHalted = GlobalVariableCheck(g_gvHalt) && GlobalVariableGet(g_gvHalt) > 0;
|
||||
g_consecutiveLosses = GlobalVariableCheck(g_gvConsec) ? (int)GlobalVariableGet(g_gvConsec) : 0;
|
||||
|
||||
Log("Initialized | Symbol=" + Symbol() +
|
||||
" Digits=" + IntegerToString(g_digits) +
|
||||
" Pip=" + DoubleToString(g_pip, g_digits + 1));
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| DEINIT |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
Log("Deinitialized | Reason=" + IntegerToString(reason));
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| TICK |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// 1. Reset daily counters if calendar date changed
|
||||
ResetDailyIfNewDay();
|
||||
|
||||
// 2a. Refresh news calendar once per day
|
||||
FetchNewsCalendar();
|
||||
|
||||
// 2. Track peak equity
|
||||
if(AccountEquity() > g_peakEquity)
|
||||
{
|
||||
g_peakEquity = AccountEquity();
|
||||
GlobalVariableSet(g_gvPeak, g_peakEquity);
|
||||
}
|
||||
|
||||
// 3. Track closed order outcomes (update consecutive loss counter)
|
||||
TrackClosedOrders();
|
||||
|
||||
// 4. Manage existing open trades (BE, trail, time/session exit)
|
||||
ManageOpenTrades();
|
||||
|
||||
// 5. Evaluate new entry
|
||||
if(!CanOpenNewTrade()) return;
|
||||
|
||||
int signal = GetEntrySignal();
|
||||
if(signal != 0) ExecuteTrade(signal);
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// DAILY RESET
|
||||
//====================================================================
|
||||
void ResetDailyIfNewDay()
|
||||
{
|
||||
datetime today = StringToTime(TimeToStr(TimeCurrent(), TIME_DATE));
|
||||
if(today == g_lastTradeDay) return;
|
||||
|
||||
g_lastTradeDay = today;
|
||||
g_todayTrades = 0;
|
||||
g_todayStartEquity = AccountEquity();
|
||||
|
||||
// Consecutive loss streak is NOT reset on new day — only a win resets it
|
||||
|
||||
Log("New day reset | Equity=" + DoubleToString(AccountEquity(), 2));
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// PRE-TRADE GATE CHECKS
|
||||
//====================================================================
|
||||
bool CanOpenNewTrade()
|
||||
{
|
||||
if(!AllowNewTrades) return false;
|
||||
if(g_tradingHalted) return false;
|
||||
|
||||
// Hard drawdown check
|
||||
if(g_peakEquity > 0)
|
||||
{
|
||||
double dd = (g_peakEquity - AccountEquity()) / g_peakEquity * 100.0;
|
||||
if(dd >= MaxDrawdownPercent)
|
||||
{
|
||||
Log("HARD HALT — max drawdown " + DoubleToString(dd, 2) + "% reached");
|
||||
g_tradingHalted = true;
|
||||
GlobalVariableSet(g_gvHalt, 1.0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Daily loss cap
|
||||
if(g_todayStartEquity > 0)
|
||||
{
|
||||
double dailyLoss = (g_todayStartEquity - AccountEquity()) / g_todayStartEquity * 100.0;
|
||||
if(dailyLoss >= MaxDailyLossPercent) return false;
|
||||
}
|
||||
|
||||
// Max trades today
|
||||
if(g_todayTrades >= MaxTradesPerDay) return false;
|
||||
|
||||
// Consecutive loss pause
|
||||
if(g_consecutiveLosses >= MaxConsecutiveLosses) return false;
|
||||
|
||||
// Day of week
|
||||
if(!IsAllowedDay()) return false;
|
||||
|
||||
// Session hours
|
||||
if(!IsSessionTime()) return false;
|
||||
|
||||
// Rollover block
|
||||
if(IsRolloverTime()) return false;
|
||||
|
||||
// News filter
|
||||
if(IsNewsTime()) return false;
|
||||
|
||||
// Spread
|
||||
double spreadPips = MarketInfo(Symbol(), MODE_SPREAD) * g_point / g_pip;
|
||||
if(spreadPips > MaxSpreadPips) return false;
|
||||
|
||||
// ATR minimum (avoid dead market)
|
||||
double atrPips = iATR(Symbol(), Period(), ATR_Period, 1) / g_pip;
|
||||
if(atrPips < MinATR_Pips) return false;
|
||||
|
||||
// One trade per symbol
|
||||
if(OneTradePerSymbol && HasOpenTrade()) return false;
|
||||
|
||||
// Total open risk cap
|
||||
if(GetTotalOpenRiskPct() >= MaxTotalOpenRiskPct) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// ENTRY SIGNAL
|
||||
// Returns: 1 = BUY -1 = SELL 0 = NONE
|
||||
//====================================================================
|
||||
int GetEntrySignal()
|
||||
{
|
||||
// --- Higher timeframe bias (M15 EMA 50 vs 200) ---
|
||||
double biasFast = iMA(Symbol(), BiasTimeframe, BiasFastEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
|
||||
double biasSlow = iMA(Symbol(), BiasTimeframe, BiasSlowEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
|
||||
double biasFastPrev = iMA(Symbol(), BiasTimeframe, BiasFastEMA, 0, MODE_EMA, PRICE_CLOSE, 2);
|
||||
|
||||
bool bullBias = (biasFast > biasSlow) && (biasFast >= biasFastPrev);
|
||||
bool bearBias = (biasFast < biasSlow) && (biasFast <= biasFastPrev);
|
||||
|
||||
// --- Execution timeframe indicators ---
|
||||
double execEMA = iMA(Symbol(), Period(), ExecEMA_Period, 0, MODE_EMA, PRICE_CLOSE, 1);
|
||||
double rsiNow = iRSI(Symbol(), Period(), RSI_Period, PRICE_CLOSE, 1);
|
||||
double rsiPrev = iRSI(Symbol(), Period(), RSI_Period, PRICE_CLOSE, 2);
|
||||
|
||||
double prevHigh = iHigh(Symbol(), Period(), BreakoutBars + 1);
|
||||
double prevLow = iLow(Symbol(), Period(), BreakoutBars + 1);
|
||||
double closeNow = iClose(Symbol(), Period(), 1);
|
||||
|
||||
// --- LONG ---
|
||||
if(EnableLong && bullBias)
|
||||
{
|
||||
bool aboveEMA = (closeNow > execEMA);
|
||||
bool rsiCross = (rsiNow >= RSI_LongLevel) && (rsiPrev < RSI_LongLevel);
|
||||
bool breakout = (closeNow > prevHigh);
|
||||
|
||||
if(aboveEMA && rsiCross && breakout) return 1;
|
||||
}
|
||||
|
||||
// --- SHORT ---
|
||||
if(EnableShort && bearBias)
|
||||
{
|
||||
bool belowEMA = (closeNow < execEMA);
|
||||
bool rsiCross = (rsiNow <= RSI_ShortLevel) && (rsiPrev > RSI_ShortLevel);
|
||||
bool breakout = (closeNow < prevLow);
|
||||
|
||||
if(belowEMA && rsiCross && breakout) return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// EXECUTE TRADE
|
||||
//====================================================================
|
||||
void ExecuteTrade(int direction)
|
||||
{
|
||||
double atr = iATR(Symbol(), Period(), ATR_Period, 1);
|
||||
|
||||
// SL distance
|
||||
double slDist = (StopLossMode == 0)
|
||||
? StopLossPips * g_pip
|
||||
: StopLossATRMult * atr;
|
||||
|
||||
// TP distance
|
||||
double tpDist = (TakeProfitMode == 0)
|
||||
? TakeProfitPips * g_pip
|
||||
: TakeProfitATRMult * atr;
|
||||
|
||||
// Enforce broker minimum stop level
|
||||
double minStop = MarketInfo(Symbol(), MODE_STOPLEVEL) * g_point;
|
||||
if(slDist < minStop + g_pip) slDist = minStop + g_pip;
|
||||
if(tpDist < minStop + g_pip) tpDist = minStop + g_pip;
|
||||
|
||||
// Lot size
|
||||
double lots = (LotSizingMode == 0)
|
||||
? FixedLot
|
||||
: CalcLotByRisk(slDist);
|
||||
lots = NormalizeLots(lots);
|
||||
|
||||
if(lots <= 0)
|
||||
{
|
||||
Log("ERROR: Lot size <=0 — trade skipped");
|
||||
return;
|
||||
}
|
||||
|
||||
// Free margin check
|
||||
double reqMargin = MarketInfo(Symbol(), MODE_MARGINREQUIRED) * lots;
|
||||
if(AccountFreeMargin() < reqMargin)
|
||||
{
|
||||
Log("ERROR: Insufficient margin — trade skipped");
|
||||
return;
|
||||
}
|
||||
|
||||
double sl, tp;
|
||||
int cmd;
|
||||
double price;
|
||||
color arrowCol;
|
||||
|
||||
if(direction == 1)
|
||||
{
|
||||
cmd = OP_BUY;
|
||||
price = Ask;
|
||||
sl = NormalizeDouble(price - slDist, g_digits);
|
||||
tp = NormalizeDouble(price + tpDist, g_digits);
|
||||
arrowCol = clrDodgerBlue;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = OP_SELL;
|
||||
price = Bid;
|
||||
sl = NormalizeDouble(price + slDist, g_digits);
|
||||
tp = NormalizeDouble(price - tpDist, g_digits);
|
||||
arrowCol = clrOrangeRed;
|
||||
}
|
||||
|
||||
int ticket = OrderSend(Symbol(), cmd, lots, price, MaxSlippagePts,
|
||||
sl, tp, TradeComment, MagicNumber, 0, arrowCol);
|
||||
|
||||
if(ticket < 0)
|
||||
{
|
||||
Log("ORDER FAILED | Error=" + IntegerToString(GetLastError()) +
|
||||
" Dir=" + IntegerToString(direction));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_todayTrades++;
|
||||
Log("ORDER OPEN | Ticket=" + IntegerToString(ticket) +
|
||||
" Dir=" + IntegerToString(direction) +
|
||||
" Lots=" + DoubleToString(lots, 2) +
|
||||
" Price=" + DoubleToString(price, g_digits) +
|
||||
" SL=" + DoubleToString(sl, g_digits) +
|
||||
" TP=" + DoubleToString(tp, g_digits) +
|
||||
" SLpips=" + DoubleToString(slDist / g_pip, 1));
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// MANAGE OPEN TRADES (BE, trail, time/session exits)
|
||||
//====================================================================
|
||||
void ManageOpenTrades()
|
||||
{
|
||||
for(int i = OrdersTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
|
||||
if(OrderSymbol() != Symbol()) continue;
|
||||
if(OrderMagicNumber() != MagicNumber) continue;
|
||||
if(OrderType() > OP_SELL) continue; // skip pending
|
||||
|
||||
int ticket = OrderTicket();
|
||||
int type = OrderType();
|
||||
double openPrice = OrderOpenPrice();
|
||||
double curSL = OrderStopLoss();
|
||||
double curTP = OrderTakeProfit();
|
||||
double slDist = MathAbs(openPrice - curSL);
|
||||
|
||||
// --- Session-end close ---
|
||||
if(CloseAtSessionEnd && !IsSessionTime())
|
||||
{
|
||||
CloseOrder(ticket, type, "SessionEnd");
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- Time-based exit ---
|
||||
if(UseTimeExit)
|
||||
{
|
||||
int minsOpen = (int)((TimeCurrent() - OrderOpenTime()) / 60);
|
||||
if(minsOpen >= MaxTradeMinutes)
|
||||
{
|
||||
CloseOrder(ticket, type, "TimeExit");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Current P&L in price units
|
||||
double profit = (type == OP_BUY)
|
||||
? Bid - openPrice
|
||||
: openPrice - Ask;
|
||||
double profitR = (slDist > 0) ? profit / slDist : 0;
|
||||
|
||||
// --- Break-even ---
|
||||
if(UseBreakEven && slDist > 0 && profitR >= BreakEvenTriggerR)
|
||||
{
|
||||
double beOffset = BreakEvenOffsetPips * g_pip;
|
||||
if(type == OP_BUY)
|
||||
{
|
||||
double newSL = NormalizeDouble(openPrice + beOffset, g_digits);
|
||||
if(newSL > curSL + g_point)
|
||||
{
|
||||
if(OrderModify(ticket, openPrice, newSL, curTP, 0, clrGold))
|
||||
Log("BE set | Ticket=" + IntegerToString(ticket) +
|
||||
" NewSL=" + DoubleToString(newSL, g_digits));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double newSL = NormalizeDouble(openPrice - beOffset, g_digits);
|
||||
if(curSL == 0 || newSL < curSL - g_point)
|
||||
{
|
||||
if(OrderModify(ticket, openPrice, newSL, curTP, 0, clrGold))
|
||||
Log("BE set | Ticket=" + IntegerToString(ticket) +
|
||||
" NewSL=" + DoubleToString(newSL, g_digits));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Trailing stop ---
|
||||
if(UseTrailingStop && slDist > 0 && profitR >= TrailingStartR)
|
||||
{
|
||||
double trailDist = TrailingDistancePips * g_pip;
|
||||
if(type == OP_BUY)
|
||||
{
|
||||
double newSL = NormalizeDouble(Bid - trailDist, g_digits);
|
||||
if(newSL > curSL + g_point)
|
||||
{
|
||||
if(OrderModify(ticket, openPrice, newSL, curTP, 0, clrAqua))
|
||||
Log("Trail updated | Ticket=" + IntegerToString(ticket) +
|
||||
" NewSL=" + DoubleToString(newSL, g_digits));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double newSL = NormalizeDouble(Ask + trailDist, g_digits);
|
||||
if(curSL == 0 || newSL < curSL - g_point)
|
||||
{
|
||||
if(OrderModify(ticket, openPrice, newSL, curTP, 0, clrAqua))
|
||||
Log("Trail updated | Ticket=" + IntegerToString(ticket) +
|
||||
" NewSL=" + DoubleToString(newSL, g_digits));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// TRACK CLOSED ORDERS (update consecutive loss counter)
|
||||
//====================================================================
|
||||
void TrackClosedOrders()
|
||||
{
|
||||
int histTotal = OrdersHistoryTotal();
|
||||
if(histTotal <= g_lastHistoryTotal) return;
|
||||
|
||||
for(int i = g_lastHistoryTotal; i < histTotal; i++)
|
||||
{
|
||||
if(!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) continue;
|
||||
if(OrderSymbol() != Symbol()) continue;
|
||||
if(OrderMagicNumber() != MagicNumber) continue;
|
||||
if(OrderType() > OP_SELL) continue;
|
||||
|
||||
double netPnl = OrderProfit() + OrderSwap() + OrderCommission();
|
||||
|
||||
if(netPnl < 0)
|
||||
{
|
||||
g_consecutiveLosses++;
|
||||
GlobalVariableSet(g_gvConsec, (double)g_consecutiveLosses);
|
||||
Log("LOSS | Ticket=" + IntegerToString(OrderTicket()) +
|
||||
" PnL=" + DoubleToString(netPnl, 2) +
|
||||
" ConsecLosses=" + IntegerToString(g_consecutiveLosses));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(g_consecutiveLosses > 0)
|
||||
Log("WIN — loss streak reset from " + IntegerToString(g_consecutiveLosses));
|
||||
g_consecutiveLosses = 0;
|
||||
GlobalVariableSet(g_gvConsec, 0.0);
|
||||
}
|
||||
}
|
||||
g_lastHistoryTotal = histTotal;
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// CLOSE ORDER HELPER
|
||||
//====================================================================
|
||||
void CloseOrder(int ticket, int type, string reason)
|
||||
{
|
||||
double price = (type == OP_BUY) ? Bid : Ask;
|
||||
bool ok = OrderClose(ticket, OrderLots(), price, MaxSlippagePts, clrWhite);
|
||||
|
||||
if(ok)
|
||||
Log("ORDER CLOSED | Ticket=" + IntegerToString(ticket) + " Reason=" + reason);
|
||||
else
|
||||
Log("CLOSE FAILED | Ticket=" + IntegerToString(ticket) +
|
||||
" Error=" + IntegerToString(GetLastError()));
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// POSITION SIZING
|
||||
//====================================================================
|
||||
double CalcLotByRisk(double slDist)
|
||||
{
|
||||
double equity = AccountEquity();
|
||||
double riskAmt = equity * RiskPercent / 100.0;
|
||||
double tickVal = MarketInfo(Symbol(), MODE_TICKVALUE);
|
||||
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
|
||||
|
||||
if(tickVal <= 0 || tickSize <= 0 || slDist <= 0) return FixedLot;
|
||||
|
||||
double slTicks = slDist / tickSize;
|
||||
return riskAmt / (slTicks * tickVal);
|
||||
}
|
||||
|
||||
double NormalizeLots(double lots)
|
||||
{
|
||||
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
|
||||
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
|
||||
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
|
||||
|
||||
if(lotStep > 0)
|
||||
lots = MathFloor(lots / lotStep) * lotStep;
|
||||
|
||||
return NormalizeDouble(MathMax(minLot, MathMin(maxLot, lots)), 2);
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// UTILITY FUNCTIONS
|
||||
//====================================================================
|
||||
bool HasOpenTrade()
|
||||
{
|
||||
for(int i = 0; i < OrdersTotal(); i++)
|
||||
{
|
||||
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
|
||||
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
double GetTotalOpenRiskPct()
|
||||
{
|
||||
double totalRisk = 0;
|
||||
double equity = AccountEquity();
|
||||
if(equity <= 0) return 0;
|
||||
|
||||
for(int i = 0; i < OrdersTotal(); i++)
|
||||
{
|
||||
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
|
||||
if(OrderMagicNumber() != MagicNumber) continue;
|
||||
if(OrderType() > OP_SELL) continue;
|
||||
|
||||
double sl = OrderStopLoss();
|
||||
if(sl == 0) continue;
|
||||
|
||||
double slDist = MathAbs(OrderOpenPrice() - sl);
|
||||
double tickVal = MarketInfo(OrderSymbol(), MODE_TICKVALUE);
|
||||
double tickSize = MarketInfo(OrderSymbol(), MODE_TICKSIZE);
|
||||
if(tickSize <= 0) continue;
|
||||
|
||||
totalRisk += (slDist / tickSize) * tickVal * OrderLots() / equity * 100.0;
|
||||
}
|
||||
return totalRisk;
|
||||
}
|
||||
|
||||
bool IsSessionTime()
|
||||
{
|
||||
int h = TimeHour(TimeCurrent());
|
||||
return (h >= SessionStartHour && h < SessionEndHour);
|
||||
}
|
||||
|
||||
bool IsRolloverTime()
|
||||
{
|
||||
int h = TimeHour(TimeCurrent());
|
||||
int m = TimeMinute(TimeCurrent());
|
||||
int totalMin = h * 60 + m;
|
||||
|
||||
// Minutes until next midnight
|
||||
int beforeMid = 1440 - totalMin;
|
||||
// Minutes since last midnight
|
||||
int afterMid = totalMin;
|
||||
|
||||
return (beforeMid <= RolloverBlockBefore || afterMid <= RolloverBlockAfter);
|
||||
}
|
||||
|
||||
bool IsAllowedDay()
|
||||
{
|
||||
int dow = TimeDayOfWeek(TimeCurrent());
|
||||
switch(dow)
|
||||
{
|
||||
case 1: return AllowMonday;
|
||||
case 2: return AllowTuesday;
|
||||
case 3: return AllowWednesday;
|
||||
case 4: return AllowThursday;
|
||||
case 5: return AllowFriday;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
// NEWS FILTER — auto-fetches ForexFactory high-impact calendar
|
||||
// Requires: MT4 Tools → Options → Expert Advisors →
|
||||
// Allow WebRequest for: https://nfs.faireconomy.media
|
||||
//====================================================================
|
||||
void FetchNewsCalendar()
|
||||
{
|
||||
if(!UseNewsFilter) return;
|
||||
|
||||
datetime today = StringToTime(TimeToStr(TimeCurrent(), TIME_DATE));
|
||||
if(g_lastNewsFetch == today) return; // Already fetched today
|
||||
|
||||
string url = "https://nfs.faireconomy.media/ff_calendar_thisweek.json";
|
||||
string headers = "User-Agent: Mozilla/5.0\r\n";
|
||||
char post[];
|
||||
char result[];
|
||||
string resultHeaders;
|
||||
|
||||
ResetLastError();
|
||||
int httpCode = WebRequest("GET", url, headers, 10000, post, result, resultHeaders);
|
||||
|
||||
if(httpCode != 200)
|
||||
{
|
||||
Log("NEWS: Fetch failed. HTTP=" + IntegerToString(httpCode) +
|
||||
" Error=" + IntegerToString(GetLastError()) +
|
||||
" — Check WebRequest whitelist in MT4 options");
|
||||
return;
|
||||
}
|
||||
|
||||
string json = CharArrayToString(result);
|
||||
ParseNewsJSON(json);
|
||||
g_lastNewsFetch = today;
|
||||
Log("NEWS: Calendar updated. High-impact events found=" + IntegerToString(g_newsEventCount));
|
||||
}
|
||||
|
||||
void ParseNewsJSON(string json)
|
||||
{
|
||||
g_newsEventCount = 0;
|
||||
ArrayResize(g_newsEvents, 200);
|
||||
|
||||
int pos = 0;
|
||||
int jsonLen = StringLen(json);
|
||||
|
||||
while(pos < jsonLen)
|
||||
{
|
||||
// Find next JSON object
|
||||
int objStart = StringFind(json, "{", pos);
|
||||
if(objStart < 0) break;
|
||||
int objEnd = StringFind(json, "}", objStart);
|
||||
if(objEnd < 0) break;
|
||||
|
||||
string obj = StringSubstr(json, objStart, objEnd - objStart + 1);
|
||||
|
||||
// Only process High impact events
|
||||
if(StringFind(obj, "\"impact\":\"High\"") >= 0)
|
||||
{
|
||||
// Check currency filter
|
||||
string country = ExtractJSONString(obj, "country");
|
||||
if(StringFind(NewsFilterCurrencies, country) >= 0)
|
||||
{
|
||||
// Parse date
|
||||
string dateStr = ExtractJSONString(obj, "date");
|
||||
datetime eventTime = ParseISODate(dateStr);
|
||||
if(eventTime > 0 && g_newsEventCount < 200)
|
||||
{
|
||||
g_newsEvents[g_newsEventCount] = eventTime;
|
||||
g_newsEventCount++;
|
||||
string title = ExtractJSONString(obj, "title");
|
||||
Log("NEWS: Loaded | " + country + " " + title +
|
||||
" @ " + TimeToStr(eventTime, TIME_DATE | TIME_MINUTES));
|
||||
}
|
||||
}
|
||||
}
|
||||
pos = objEnd + 1;
|
||||
}
|
||||
ArrayResize(g_newsEvents, g_newsEventCount);
|
||||
}
|
||||
|
||||
string ExtractJSONString(string obj, string key)
|
||||
{
|
||||
string search = "\"" + key + "\":\"";
|
||||
int start = StringFind(obj, search);
|
||||
if(start < 0) return "";
|
||||
start += StringLen(search);
|
||||
int end = StringFind(obj, "\"", start);
|
||||
if(end < 0) return "";
|
||||
return StringSubstr(obj, start, end - start);
|
||||
}
|
||||
|
||||
datetime ParseISODate(string iso)
|
||||
{
|
||||
// Format: "2026-04-04T08:30:00-0400"
|
||||
if(StringLen(iso) < 19) return 0;
|
||||
|
||||
int year = (int)StringToInteger(StringSubstr(iso, 0, 4));
|
||||
int month = (int)StringToInteger(StringSubstr(iso, 5, 2));
|
||||
int day = (int)StringToInteger(StringSubstr(iso, 8, 2));
|
||||
int hour = (int)StringToInteger(StringSubstr(iso, 11, 2));
|
||||
int min = (int)StringToInteger(StringSubstr(iso, 14, 2));
|
||||
|
||||
// Parse timezone offset (e.g. -0400 or +0000)
|
||||
int tzOffsetSecs = 0;
|
||||
int tzPos = StringFind(iso, "+", 19);
|
||||
int tzSign = 1;
|
||||
if(tzPos < 0) { tzPos = StringFind(iso, "-", 19); tzSign = -1; }
|
||||
if(tzPos >= 0)
|
||||
{
|
||||
int tzH = (int)StringToInteger(StringSubstr(iso, tzPos + 1, 2));
|
||||
int tzM = (int)StringToInteger(StringSubstr(iso, tzPos + 3, 2));
|
||||
tzOffsetSecs = tzSign * (tzH * 3600 + tzM * 60);
|
||||
}
|
||||
|
||||
// Build UTC datetime
|
||||
string dtStr = StringFormat("%04d.%02d.%02d %02d:%02d", year, month, day, hour, min);
|
||||
datetime utc = StringToTime(dtStr) - tzOffsetSecs;
|
||||
|
||||
// Convert UTC → broker server time
|
||||
datetime serverTime = utc + BrokerGMTOffset * 3600;
|
||||
return serverTime;
|
||||
}
|
||||
|
||||
bool IsNewsTime()
|
||||
{
|
||||
if(!UseNewsFilter || g_newsEventCount == 0) return false;
|
||||
|
||||
datetime now = TimeCurrent();
|
||||
int blockBefore = NewsBlockMinsBefore * 60;
|
||||
int blockAfter = NewsBlockMinsAfter * 60;
|
||||
|
||||
for(int i = 0; i < g_newsEventCount; i++)
|
||||
{
|
||||
if(now >= g_newsEvents[i] - blockBefore &&
|
||||
now <= g_newsEvents[i] + blockAfter)
|
||||
{
|
||||
if(g_newsEvents[i] != g_lastNewsLogTime)
|
||||
{
|
||||
Log("NEWS: Trading blocked near event @ " +
|
||||
TimeToStr(g_newsEvents[i], TIME_DATE | TIME_MINUTES));
|
||||
g_lastNewsLogTime = g_newsEvents[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Log(string msg)
|
||||
{
|
||||
Print("[CScalp] " + TimeToStr(TimeCurrent(), TIME_DATE | TIME_MINUTES) + " | " + msg);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,103 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| XAUUSD / USDJPY ATR Scalper EA |
|
||||
//| Author: GIMS_Dev |
|
||||
//| Platform: MetaTrader 4 |
|
||||
//| Strategy: ATR + Trend + Price Action |
|
||||
//+------------------------------------------------------------------+
|
||||
#property strict
|
||||
|
||||
// ================= INPUTS =================
|
||||
input double Lots = 0.01;
|
||||
input int ATR_Period = 14;
|
||||
input double ATR_Multiplier_SL = 1.2;
|
||||
input double ATR_Multiplier_TP = 1.5;
|
||||
input int EMA_Fast = 50;
|
||||
input int EMA_Slow = 200;
|
||||
input int Slippage = 3;
|
||||
input int BreakevenPips = 20;
|
||||
input int TrailingStopPips = 15;
|
||||
input int MaxTradesPerSymbol = 3;
|
||||
input int MagicNumber = 123456;
|
||||
|
||||
// ================= SYMBOLS =================
|
||||
string Symbols[] = {"XAUUSD", "USDJPY"};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
CreateDashboard();
|
||||
Print("ATR Scalper EA initialized");
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
ObjectsDeleteAll(0, OBJ_LABEL);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
for(int i=0; i<ArraySize(Symbols); i++)
|
||||
{
|
||||
string sym = Symbols[i];
|
||||
if(!SymbolSelect(sym, true)) continue;
|
||||
|
||||
ManageTrades(sym);
|
||||
|
||||
if(CanOpenTrade(sym))
|
||||
EvaluateEntry(sym);
|
||||
|
||||
UpdateDashboard(sym);
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
// ================= ENTRY LOGIC =================
|
||||
void EvaluateEntry(string sym)
|
||||
{
|
||||
if(!TrendIsValid(sym)) return;
|
||||
if(!EngulfingPattern(sym)) return;
|
||||
|
||||
PlaceMarketOrder(sym);
|
||||
}
|
||||
|
||||
// ================= TREND FILTER =================
|
||||
bool TrendIsValid(string sym)
|
||||
{
|
||||
double fast = iMA(sym, PERIOD_H1, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE, 0);
|
||||
double slow = iMA(sym, PERIOD_H1, EMA_Slow, 0, MODE_EMA, PRICE_CLOSE, 0);
|
||||
|
||||
return (fast > slow || fast < slow);
|
||||
}
|
||||
|
||||
// ================= PRICE ACTION =================
|
||||
bool EngulfingPattern(string sym)
|
||||
{
|
||||
double o1 = iOpen(sym, PERIOD_M5, 1);
|
||||
double c1 = iClose(sym, PERIOD_M5, 1);
|
||||
double o2 = iOpen(sym, PERIOD_M5, 2);
|
||||
double c2 = iClose(sym, PERIOD_M5, 2);
|
||||
|
||||
bool bullish = c2 < o2 && c1 > o1 && c1 > o2;
|
||||
bool bearish = c2 > o2 && c1 < o1 && c1 < o2;
|
||||
|
||||
return (bullish || bearish);
|
||||
}
|
||||
|
||||
// ================= ORDER PLACEMENT =================
|
||||
void PlaceMarketOrder(string sym)
|
||||
{
|
||||
double atr = iATR(sym, PERIOD_M5, ATR_Period, 0);
|
||||
if(atr <= 0) return;
|
||||
|
||||
double point = MarketInfo(sym, MODE_POINT);
|
||||
int digits = (int)MarketInfo(sym, MODE_DIGITS);
|
||||
|
||||
double ask = MarketInfo(sym, MODE_ASK);
|
||||
double bid = MarketInfo(sym, MODE_BID);
|
||||
|
||||
double sl, tp;
|
||||
int type;
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,409 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| ThreeBarPlay.mq4 |
|
||||
//| Copyright 2020, MetaQuotes Software Corp. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2020, MetaQuotes Software Corp."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
//---
|
||||
|
||||
//---
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
//---
|
||||
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| trading mechanics is the same as BUYSTOCK/SELLSTOCK scripts |
|
||||
//+------------------------------------------------------------------+
|
||||
int numOfTrades=0;
|
||||
int takeprofit=100;
|
||||
int stoploss=100;
|
||||
void trade(bool buy, int sl)
|
||||
{
|
||||
numOfTrades++;
|
||||
if(buy)
|
||||
{
|
||||
int pips=sl;
|
||||
double risk=AccountBalance()*0.02/1.3;
|
||||
double shares = (int)(risk / pips * 100);
|
||||
double maxShares = (int)(AccountFreeMargin()/1.3 * 14.8 / (Ask));
|
||||
if((MarketInfo(_Symbol,17))<0.01)//if forex
|
||||
{
|
||||
maxShares/=100;
|
||||
shares/=100;
|
||||
}
|
||||
if(shares>(MarketInfo(_Symbol,MODE_MAXLOT)))
|
||||
{
|
||||
shares=MarketInfo(_Symbol,MODE_MAXLOT);
|
||||
}
|
||||
if(maxShares < shares)
|
||||
{
|
||||
shares = maxShares;
|
||||
pips=(int)(risk/shares*100);
|
||||
}
|
||||
|
||||
Alert("Buy ",shares," shares of ", _Symbol);
|
||||
double x=Ask;
|
||||
double y=shares*0.67;
|
||||
double z=shares*0.33;
|
||||
int pipstostoploss=pips;
|
||||
int takeprofit2= pips;
|
||||
int takeprofit1= (int)(pips/2);
|
||||
int order0=OrderSend(
|
||||
_Symbol,//currencyPair
|
||||
OP_BUY,//buy
|
||||
y,//howmuch*SYMBOL_VOLUME_MIN
|
||||
x,//price
|
||||
3,//tolerance
|
||||
x-pipstostoploss*_Point, //stoploss
|
||||
x+takeprofit1*_Point,//takeprofit
|
||||
NULL,//comment
|
||||
0,//magic number
|
||||
0,//expiration
|
||||
CLR_NONE//color of arrow
|
||||
);
|
||||
int order1=OrderSend(
|
||||
_Symbol,//currencyPair
|
||||
OP_BUY,//buy
|
||||
z,//howmuch*SYMBOL_VOLUME_MIN
|
||||
x,//price
|
||||
3,//tolerance
|
||||
x-pipstostoploss*_Point, //stoploss
|
||||
x+takeprofit2*_Point,//takeprofit
|
||||
NULL,//comment
|
||||
0,//magic number
|
||||
0,//expiration
|
||||
CLR_NONE//color of arrow
|
||||
);
|
||||
}
|
||||
else
|
||||
if(!buy)
|
||||
{
|
||||
int pips=sl;
|
||||
double risk=AccountBalance()*0.02/1.3;
|
||||
double shares = (int)(risk / pips * 100);
|
||||
double maxShares = (int)(AccountFreeMargin()/1.3 * 14.8 / (Bid));
|
||||
if((MarketInfo(_Symbol,17))<0.01)//if forex
|
||||
{
|
||||
maxShares/=100;
|
||||
shares/=100;
|
||||
}
|
||||
if(shares>(MarketInfo(_Symbol,MODE_MAXLOT)))
|
||||
{
|
||||
shares=MarketInfo(_Symbol,MODE_MAXLOT);
|
||||
}
|
||||
if(maxShares < shares)
|
||||
{
|
||||
shares = maxShares;
|
||||
pips=(int)(risk/shares*100);
|
||||
}
|
||||
|
||||
Alert("Sell ",shares," shares of ", _Symbol);
|
||||
double x=Bid;
|
||||
double y=shares*0.67;
|
||||
double z=shares*0.33;
|
||||
int pipstostoploss=pips;
|
||||
int takeprofit2= pips;
|
||||
int takeprofit1= (int)(pips/2);
|
||||
|
||||
int order0=OrderSend(
|
||||
_Symbol,//currencyPair
|
||||
OP_SELL,//sell
|
||||
y,//howmuch*SYMBOL_VOLUME_MIN
|
||||
x,//price
|
||||
3,//tolerance
|
||||
x+pipstostoploss*_Point, //stoploss
|
||||
x-takeprofit1*_Point,//takeprofit
|
||||
NULL,//comment
|
||||
0,//magic number
|
||||
0,//expiration
|
||||
CLR_NONE//color of arrow
|
||||
);
|
||||
int order1=OrderSend(
|
||||
_Symbol,//currencyPair
|
||||
OP_SELL,//sell
|
||||
z,//howmuch*SYMBOL_VOLUME_MIN
|
||||
x,//price
|
||||
3,//tolerance
|
||||
x+pipstostoploss*_Point, //stoploss
|
||||
x-takeprofit2*_Point,//takeprofit
|
||||
NULL,//comment
|
||||
0,//magic number
|
||||
0,//expiration
|
||||
CLR_NONE//color of arrow
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
double height;
|
||||
bool traded=false;
|
||||
void OnTick()
|
||||
{
|
||||
//find average size of candlesticks
|
||||
int numOfCandles=200;
|
||||
double total=0;
|
||||
for(int i=0; i<numOfCandles; i++)
|
||||
{
|
||||
total+=(MathAbs(Open[i]-Close[i]));
|
||||
}
|
||||
double average=(int)((total/numOfCandles)/_Point);
|
||||
//average volume
|
||||
double totalVol=0;
|
||||
for(int i=0; i<50; i++)
|
||||
{
|
||||
totalVol+=Volume[i];
|
||||
}
|
||||
double averageVol=(totalVol/(50));
|
||||
//delete all trades with second candle doji
|
||||
bool doji=false;
|
||||
bool hammer =false;
|
||||
bool star=false;
|
||||
double high=High[1];
|
||||
double low=Low[1];
|
||||
double open=Open[1];
|
||||
double close=Close[1];
|
||||
double body=open-close;
|
||||
double top=0;
|
||||
double bot=0;
|
||||
double topratio,botratio;
|
||||
if(body>0)
|
||||
{
|
||||
top=high-close;
|
||||
bot=open-low;
|
||||
}
|
||||
else
|
||||
if(body<0)
|
||||
{
|
||||
top=high-open;
|
||||
bot=close-low;
|
||||
}
|
||||
if(body!=0)
|
||||
{
|
||||
topratio=top/(MathAbs(body));
|
||||
botratio=bot/(MathAbs(body));
|
||||
}
|
||||
else
|
||||
{
|
||||
//if body is 0
|
||||
topratio=0;
|
||||
botratio=0;
|
||||
}
|
||||
double dojiratio=1;
|
||||
if((botratio>=dojiratio)&&(topratio>=dojiratio)&&(botratio>=(dojiratio+2))&&(topratio>=(dojiratio+2)))
|
||||
{
|
||||
doji=true;
|
||||
if((botratio/topratio)>=2)
|
||||
{
|
||||
doji=false;
|
||||
hammer=true;
|
||||
}
|
||||
else
|
||||
if((topratio/botratio)>=2)
|
||||
{
|
||||
doji=false;
|
||||
star=true;
|
||||
}
|
||||
}
|
||||
else
|
||||
if((botratio>=dojiratio)&&(botratio>=(dojiratio+2)))
|
||||
{
|
||||
hammer=true;
|
||||
}
|
||||
else
|
||||
if((topratio>=(dojiratio+2))&&(topratio>=dojiratio))
|
||||
{
|
||||
star=true;
|
||||
}
|
||||
/*
|
||||
calculations:
|
||||
if spread<=16, then look for plays
|
||||
ignition is any bar [a certain number] times or more than spread
|
||||
correction is if second bar is less than 1 quarter ignition in op direction
|
||||
enter trade when bid passes the ignition close
|
||||
stoploss will be at ignition's open+spread
|
||||
*/
|
||||
//init variables
|
||||
double MFI=iMFI(_Symbol,_Period,14,0);
|
||||
int spread=SYMBOL_SPREAD;
|
||||
double goldenNum=0.8;
|
||||
double goldenMax=10;
|
||||
double goldenNum2=2.5;
|
||||
bool bull=false;
|
||||
bool bear=false;
|
||||
bool ignition=false;
|
||||
bool correction=false;
|
||||
bool confirmation=false;
|
||||
bool rejection=false;
|
||||
int height2=(int)((Close[2]-Open[2])/_Point);
|
||||
int height1=(int)((Close[1]-Open[1])/_Point);
|
||||
//one trade per 3barcombo
|
||||
if(height!=Close[1])
|
||||
{
|
||||
traded=false;
|
||||
}
|
||||
//ignition : any bar [a certain number] times or more than average candle
|
||||
if((height2>=(goldenNum*average))&&(height2<=(goldenMax*average)))
|
||||
{
|
||||
ignition=true;
|
||||
//bull
|
||||
bull=true;
|
||||
bear=false;
|
||||
}
|
||||
if((height2<=(goldenNum*-1*average))&&(height2>=(goldenMax*-1*average)))
|
||||
{
|
||||
ignition=true;
|
||||
bear=true;
|
||||
bull=false;
|
||||
height2*=-1;
|
||||
}
|
||||
//correction : second bar is less than [a second certain number] times of ignition in op direction
|
||||
|
||||
if(bull && (height1<-2) && (height1>=((-1)*height2/goldenNum2)))
|
||||
{
|
||||
if(!doji && !star)
|
||||
{
|
||||
correction=true;
|
||||
}
|
||||
}
|
||||
else
|
||||
if((bear &&(height1>2) && (height1<=(height2/goldenNum2))))
|
||||
{
|
||||
if(!doji && !hammer)
|
||||
{
|
||||
correction=true;
|
||||
}
|
||||
}
|
||||
//confirmation : when the price passes the ignition close
|
||||
if(((bull)&&(Ask>=(Close[2]+(spread*_Point))))||((bear)&&(Bid<=(Close[2]-(spread*_Point)))))
|
||||
{
|
||||
if(correction)
|
||||
{
|
||||
confirmation=true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(((bull)&&(Bid<=(Close[1]-(1.5*spread*_Point))))||((bear)&&(Ask>=(Close[1]+(1.5*spread*_Point)))))
|
||||
{
|
||||
if((bull==true)&&(bear==false)&&(star||doji))
|
||||
{
|
||||
rejection=true;
|
||||
bear=true;
|
||||
bull=false;
|
||||
}
|
||||
else
|
||||
if((bear==true)&&(bull==false)&&(hammer||doji))
|
||||
{
|
||||
rejection=true;
|
||||
bull=true;
|
||||
bear=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//make trade if all true
|
||||
//only trade with higher than average relative volume
|
||||
bool withVol=false;
|
||||
withVol=((Volume[1]/averageVol)>=1);
|
||||
//trading format "trade(bool buy,int pipstostoploss));"
|
||||
|
||||
if(ignition && correction && confirmation && bull && !traded && withVol)
|
||||
{
|
||||
//buy after confirmation
|
||||
stoploss=height2+(2*spread);
|
||||
takeprofit=stoploss/2;
|
||||
trade(true,stoploss);
|
||||
traded=true;
|
||||
}
|
||||
else
|
||||
if(ignition && correction && confirmation && bear && !traded && withVol)
|
||||
{
|
||||
//sell after confirmation
|
||||
stoploss=height2+(2*spread);
|
||||
takeprofit=stoploss/2;
|
||||
trade(false,stoploss);
|
||||
traded=true;
|
||||
}
|
||||
else
|
||||
if(!traded && ignition && bull && rejection && withVol)
|
||||
{
|
||||
//buy after rejection
|
||||
stoploss=height2+spread;
|
||||
takeprofit=stoploss/2;
|
||||
trade(true,stoploss);
|
||||
traded=true;
|
||||
}
|
||||
else
|
||||
if(!traded && ignition && bear && rejection && withVol)
|
||||
{
|
||||
//sell after rejection
|
||||
stoploss=height2+spread;
|
||||
takeprofit=stoploss/2;
|
||||
trade(false,stoploss);
|
||||
traded=true;
|
||||
}
|
||||
Comment(
|
||||
"balance : ",AccountBalance(),"\n",
|
||||
"ignition : ",ignition,"\n",
|
||||
"correction : ",correction,"\n",
|
||||
"confirmation: ",confirmation,"\n",
|
||||
"doji: ",doji,"\n",
|
||||
"hammer: ",hammer,"\n",
|
||||
"star: ",star,"\n",
|
||||
numOfTrades," trades "
|
||||
);
|
||||
height=Close[1];
|
||||
//if there is a trade open, make a trailing stop while profitable by 20+ pips
|
||||
if(OrdersTotal()>0)
|
||||
{
|
||||
for(int i=OrdersTotal(); i>=0; i--)
|
||||
{
|
||||
int pips=(int)(takeprofit);
|
||||
//select an order
|
||||
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
|
||||
{
|
||||
//make sure its the right currency pair
|
||||
if(OrderSymbol()==_Symbol)
|
||||
{
|
||||
//check if buy or sell
|
||||
if(OrderType()==OP_BUY)
|
||||
{
|
||||
if((Bid>(OrderOpenPrice()+pips*_Point))&&(OrderStopLoss()<OrderOpenPrice()) && (OrderStopLoss()<(Ask-pips*_Point)))
|
||||
{
|
||||
bool evenbuy=OrderModify(OrderTicket(),OrderOpenPrice(),Ask-pips*_Point,OrderTakeProfit(),0);
|
||||
}
|
||||
}
|
||||
else
|
||||
if(OrderType()==OP_SELL)
|
||||
{
|
||||
if((Ask<(OrderOpenPrice()-pips*_Point))&&(OrderStopLoss()>OrderOpenPrice()) && (OrderStopLoss()>(Bid+pips*_Point)))
|
||||
{
|
||||
bool evensell=OrderModify(OrderTicket(),OrderOpenPrice(),Bid+pips*_Point,OrderTakeProfit(),0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,764 @@
|
||||
//-PROPERTIES-//
|
||||
// Properties help the software look better when you load it in MT5.
|
||||
// They provide more information and details
|
||||
// This is what you see in the About tab when you attach the expert advisor to a chart.
|
||||
#property link "https://www.earnforex.com/metatrader-expert-advisors/rsi-expert-advisor/"
|
||||
#property version "1.00"
|
||||
#property copyright "EarnForex.com - 2024"
|
||||
#property description "A basic RSI EA created using an EA template."
|
||||
#property description ""
|
||||
#property description "WARNING: There is no guarantee that this expert advisor will work as intended. Use at your own risk."
|
||||
#property description ""
|
||||
#property description "Find more on www.EarnForex.com"
|
||||
#property icon "\\Files\\EF-Icon-64x64px.ico"
|
||||
|
||||
//-INCLUDES-//
|
||||
// '#include' allows to import code from other files.
|
||||
// In the following instance the file has to be placed in the MQL5\Include folder.
|
||||
#include <Trade\Trade.mqh> // This file is required to easily manage orders and positions.
|
||||
#include <MQLTA ErrorHandling.mqh> // This file contains useful descriptions for errors.
|
||||
#include <MQLTA Utils.mqh> // This file contains some useful functions.
|
||||
|
||||
//-COMMENTS-//
|
||||
// This is a single line comment and you can do it by placing // at the start of the comment, this text is ignored when compiling.
|
||||
|
||||
/*
|
||||
This is a multi-line comment.
|
||||
It starts with /* and it finishes with the * and / like below
|
||||
*/
|
||||
|
||||
enum ENUM_RISK_BASE
|
||||
{
|
||||
RISK_BASE_EQUITY = 1, // EQUITY
|
||||
RISK_BASE_BALANCE = 2, // BALANCE
|
||||
RISK_BASE_FREEMARGIN = 3, // FREE MARGIN
|
||||
};
|
||||
|
||||
enum ENUM_RISK_DEFAULT_SIZE
|
||||
{
|
||||
RISK_DEFAULT_FIXED = 1, // FIXED SIZE
|
||||
RISK_DEFAULT_AUTO = 2, // AUTOMATIC SIZE BASED ON RISK
|
||||
};
|
||||
|
||||
enum ENUM_MODE_SL
|
||||
{
|
||||
SL_FIXED = 0, // FIXED STOP LOSS
|
||||
SL_AUTO = 1, // AUTOMATIC STOP LOSS
|
||||
};
|
||||
|
||||
enum ENUM_MODE_TP
|
||||
{
|
||||
TP_FIXED = 0, // FIXED TAKE PROFIT
|
||||
TP_AUTO = 1, // AUTOMATIC TAKE PROFIT
|
||||
};
|
||||
|
||||
// EA Parameters
|
||||
input string Comment_0 = "=========="; // EA-Specific Parameters
|
||||
// !! Declare parameters specific to your EA here.
|
||||
// For example, a moving average period, an RSI level, or anything else your EA needs to know to implement its trading strategy.
|
||||
// All input parameters start with 'input' keyword.
|
||||
// input int example = 10; // This is an example input parameter
|
||||
input int RSIPeriod = 14; // RSI period
|
||||
input double RSIOverbought = 80; // RSI overbought level
|
||||
input double RSIOversold = 20; // RSI oversold level
|
||||
input ENUM_APPLIED_PRICE RSIPrice = PRICE_CLOSE; // RSI applied price
|
||||
|
||||
input string Comment_1 = "=========="; // Trading Hours Settings
|
||||
input bool UseTradingHours = false; // Limit trading hours
|
||||
input ENUM_HOUR TradingHourStart = h07; // Trading start hour (Broker server hour)
|
||||
input ENUM_HOUR TradingHourEnd = h19; // Trading end hour (Broker server hour)
|
||||
|
||||
input string Comment_2 = "=========="; // ATR Settings
|
||||
input int ATRPeriod = 100; // ATR period
|
||||
input ENUM_TIMEFRAMES ATRTimeFrame = PERIOD_CURRENT; // ATR timeframe
|
||||
input double ATRMultiplierSL = 2; // ATR multiplier for stop-loss
|
||||
input double ATRMultiplierTP = 3; // ATR multiplier for take-profit
|
||||
|
||||
// General input parameters
|
||||
input string Comment_a = "=========="; // Risk Management Settings
|
||||
input ENUM_RISK_DEFAULT_SIZE RiskDefaultSize = RISK_DEFAULT_FIXED; // Position size mode
|
||||
input double DefaultLotSize = 0.1; // Position size (if fixed or if no stop loss defined)
|
||||
input ENUM_RISK_BASE RiskBase = RISK_BASE_BALANCE; // Risk base
|
||||
input int MaxRiskPerTrade = 2; // Percentage to risk each trade
|
||||
input double MinLotSize = 0.01; // Minimum position size allowed
|
||||
input double MaxLotSize = 100; // Maximum position size allowed
|
||||
input int MaxPositions = 1; // Maximum number of positions for this EA
|
||||
|
||||
input string Comment_b = "=========="; // Stop-Loss and Take-Profit Settings
|
||||
input ENUM_MODE_SL StopLossMode = SL_FIXED; // Stop-loss mode
|
||||
input int DefaultStopLoss = 0; // Default stop-loss in points (0 = no stop-loss)
|
||||
input int MinStopLoss = 0; // Minimum allowed stop-loss in points
|
||||
input int MaxStopLoss = 5000; // Maximum allowed stop-loss in points
|
||||
input ENUM_MODE_TP TakeProfitMode = TP_FIXED; // Take-profit mode
|
||||
input int DefaultTakeProfit = 0; // Default take-profit in points (0 = no take-profit)
|
||||
input int MinTakeProfit = 0; // Minimum allowed take-profit in points
|
||||
input int MaxTakeProfit = 5000; // Maximum allowed take-profit in points
|
||||
|
||||
input string Comment_c = "=========="; // Partial Close Settings
|
||||
input bool UsePartialClose = false; // Use partial close
|
||||
input double PartialClosePerc = 50; // Partial close percentage
|
||||
input double ATRMultiplierPC = 1; // ATR multiplier for partial close
|
||||
|
||||
input string Comment_d = "=========="; // Additional Settings
|
||||
input int MagicNumber = 0; // Magic number
|
||||
input string OrderNote = ""; // Comment for orders
|
||||
input int Slippage = 5; // Slippage in points
|
||||
input int MaxSpread = 50; // Maximum allowed spread to trade, in points
|
||||
|
||||
|
||||
// Global Variables
|
||||
CTrade Trade; // Trade object.
|
||||
int ATRHandle; // Indicator handle for ATR.
|
||||
int IndicatorHandle = -1; // Global indicator handle for the EA's main signal indicator.
|
||||
double ATR_current, ATR_previous; // ATR values.
|
||||
double Indicator_current, Indicator_previous; // Indicator values.
|
||||
|
||||
// Here go all the event handling functions. They all run on specific events generated for the expert advisor.
|
||||
// All event handlers are optional and can be removed if you don't need to process that specific event.
|
||||
|
||||
//+-------------------------------------------------------------------+
|
||||
//| Expert initialization handler |
|
||||
//| Here goes the code that runs just once each time you load the EA. |
|
||||
//+-------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// EventSetTimer(60); // Starting a 60-second timer.
|
||||
// EventSetMillisecondTimer(500); // Starting a 500-millisecond timer.
|
||||
|
||||
if (!Prechecks()) // Check if everything is OK with input parameters.
|
||||
{
|
||||
return INIT_FAILED; // Don't initialize the EA if checks fail.
|
||||
}
|
||||
|
||||
if (!InitializeHandles()) // Initialize indicator handles.
|
||||
{
|
||||
PrintFormat("Error initializing indicator handles - %s - %d", GetLastErrorText(GetLastError()), GetLastError());
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
SetTradeObject();
|
||||
|
||||
return INIT_SUCCEEDED; // Successful initialization.
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------+
|
||||
//| Expert deinitialization handler |
|
||||
//| Here goes the code that runs just once each time you unload the EA. |
|
||||
//+---------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
// Normally, there isn't much stuff you need to do on deinitialization.
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick handler |
|
||||
//| Here goes the code that runs every tick. |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
ProcessTick(); // Calling the EA's main processing function here. It's defined farther below.
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Timer event handler |
|
||||
//| Here goes the code that runs on timer. |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTimer()
|
||||
{
|
||||
// For example, you can update a display timer here if you have one in your EA.
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------------------+
|
||||
//| Trade event handler |
|
||||
//| Here goes the code that runs each time something related to trading happens. |
|
||||
//+------------------------------------------------------------------------------+
|
||||
void OnTrade()
|
||||
{
|
||||
// For example, if you want to do something when a pending order gets triggered, you can do it here without overloading the OnTick() handler too much.
|
||||
}
|
||||
|
||||
//+--------------------------------------------------------------------------------+
|
||||
//| Backtest end handler |
|
||||
//| Here goes the code that runs each time a backtest in Strategy Tester finishes. |
|
||||
//| The goal is to calculate the value of a custom optimization criterion. |
|
||||
//+--------------------------------------------------------------------------------+
|
||||
double OnTester()
|
||||
{
|
||||
double NetProfit = TesterStatistics(STAT_PROFIT);
|
||||
double InitialDeposit = TesterStatistics(STAT_INITIAL_DEPOSIT);
|
||||
double MaxDrawDownPerc = TesterStatistics(STAT_EQUITYDD_PERCENT);
|
||||
double TotalTrades = TesterStatistics(STAT_TRADES);
|
||||
if (InitialDeposit == 0) return 0; // Avoiding division by zero.
|
||||
if (TotalTrades == 0) return -100; // Discard a backtest with zero trades.
|
||||
if ((TotalTrades > 0) && (MaxDrawDownPerc == 0)) MaxDrawDownPerc = 0.01; // Avoiding division by zero.
|
||||
|
||||
double NetProfitPerc = NetProfit / InitialDeposit * 100;
|
||||
|
||||
double Max = 0;
|
||||
if (NetProfitPerc > 0) Max = NetProfitPerc / MaxDrawDownPerc; // Adjust net profit by maximum drawdown.
|
||||
if (NetProfitPerc < 0) Max = NetProfitPerc;
|
||||
|
||||
return Max; // Return the value as a custom optimization criterion.
|
||||
}
|
||||
|
||||
|
||||
// Here go all custom functions. They all are called either from the above-defined event handlers or from other custom functions.
|
||||
|
||||
// Entry and exit processing
|
||||
void ProcessTick()
|
||||
{
|
||||
if (!GetIndicatorsData()) return;
|
||||
|
||||
if (CountPositions())
|
||||
{
|
||||
// There is a position open. Manage SL, TP, or close if necessary.
|
||||
if (UsePartialClose) PartialCloseAll();
|
||||
CheckExitSignal();
|
||||
}
|
||||
|
||||
// A block of code that lets the subsequent code execute only when a new bar appears on the chart.
|
||||
// This means that the entry signals will be checked only twice per bar.
|
||||
static datetime current_bar_time = WRONG_VALUE;
|
||||
datetime previous_bar_time = current_bar_time;
|
||||
current_bar_time = iTime(Symbol(), Period(), 0);
|
||||
static int ticks_of_new_bar = 0; // Process two ticks of each new bar to allow indicator buffers to refresh.
|
||||
if (current_bar_time == previous_bar_time)
|
||||
{
|
||||
ticks_of_new_bar++;
|
||||
if (ticks_of_new_bar > 1) return; // Skip after two ticks.
|
||||
}
|
||||
else ticks_of_new_bar = 0;
|
||||
|
||||
// The number is recalculated after the first call because some trades could have been gotten closed.
|
||||
if (CountPositions() < MaxPositions) CheckEntrySignal(); // Check entry signals only if there aren't too many positions already.
|
||||
}
|
||||
|
||||
int CountPositions()
|
||||
{
|
||||
int count = 0;
|
||||
int TotalPositions = PositionsTotal();
|
||||
for (int i = 0; i < TotalPositions; i++)
|
||||
{
|
||||
string Instrument = PositionGetSymbol(i);
|
||||
if (Instrument == "")
|
||||
{
|
||||
PrintFormat(__FUNCTION__, ": ERROR - Unable to select the position - %s - %d.", GetLastErrorText(GetLastError()), GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip positions in other symbols.
|
||||
if (Instrument != Symbol()) continue;
|
||||
// Skip counting positions with a different Magic number if the EA has non-zero Magic number set.
|
||||
if ((MagicNumber != 0) && (PositionGetInteger(POSITION_MAGIC) != MagicNumber)) continue;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Initialize handles. Indicator handles have to be initialized at the beginning of the EA's operation.
|
||||
bool InitializeHandles()
|
||||
{
|
||||
// Indicator handle is the main handle for the signal generating indicator.
|
||||
IndicatorHandle = iRSI(Symbol(), Period(), RSIPeriod, RSIPrice);
|
||||
if (IndicatorHandle == INVALID_HANDLE)
|
||||
{
|
||||
PrintFormat("Unable to create main indicator handle - %s - %d.", GetLastErrorText(GetLastError()), GetLastError());
|
||||
return false;
|
||||
}
|
||||
// ATR handle for stop-loss and take-profit.
|
||||
ATRHandle = iATR(Symbol(), ATRTimeFrame, ATRPeriod);
|
||||
if (ATRHandle == INVALID_HANDLE)
|
||||
{
|
||||
PrintFormat("Unable to create ATR handle - %s - %d.", GetLastErrorText(GetLastError()), GetLastError());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Trading functions
|
||||
|
||||
// Set the basic parameters of the Trade object.
|
||||
void SetTradeObject()
|
||||
{
|
||||
// All future trade operations will take into account these parameters - Magic number and deviation/slippage.
|
||||
Trade.SetExpertMagicNumber(MagicNumber);
|
||||
Trade.SetDeviationInPoints(Slippage);
|
||||
}
|
||||
|
||||
// Open a position with a buy order.
|
||||
bool OpenBuy()
|
||||
{
|
||||
double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
|
||||
double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
|
||||
double OpenPrice = Ask; // Buy at Ask.
|
||||
double StopLossPrice = StopLoss(ORDER_TYPE_BUY, OpenPrice); // Calculate SL based on direction, price, and SL rules.
|
||||
double TakeProfitPrice = TakeProfit(ORDER_TYPE_BUY, OpenPrice); // Calculate TP based on direction, price, and TP rules.
|
||||
double Size = LotSize(StopLossPrice, OpenPrice); // Calculate position size based on the SL, price, and the given rules.
|
||||
// Use the standard Trade object to open the position with calculated parameters.
|
||||
if (!Trade.Buy(Size, Symbol(), OpenPrice, StopLossPrice, TakeProfitPrice))
|
||||
{
|
||||
PrintFormat("Unable to open BUY: %s - %d", Trade.ResultRetcodeDescription(), Trade.ResultRetcode());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Open a position with a sell order.
|
||||
bool OpenSell()
|
||||
{
|
||||
double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
|
||||
double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
|
||||
double OpenPrice = Bid; // Sell at Bid.
|
||||
double StopLossPrice = StopLoss(ORDER_TYPE_SELL, OpenPrice); // Calculate SL based on direction, price, and SL rules.
|
||||
double TakeProfitPrice = TakeProfit(ORDER_TYPE_SELL, OpenPrice); // Calculate TP based on direction, price, and TP rules.
|
||||
double Size = LotSize(StopLossPrice, OpenPrice); // Calculate position size based on the SL, price, and the given rules.
|
||||
// Use the standard Trade object to open the position with calculated parameters.
|
||||
if (!Trade.Sell(Size, Symbol(), OpenPrice, StopLossPrice, TakeProfitPrice))
|
||||
{
|
||||
PrintFormat("Unable to open SELL: %s - %d", Trade.ResultRetcodeDescription(), Trade.ResultRetcode());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Close the specified position completely.
|
||||
//!! Unused. Can be uncommented and used to close specific positions.
|
||||
/* bool ClosePosition(ulong ticket)
|
||||
{
|
||||
if (!Trade.PositionClose(ticket))
|
||||
{
|
||||
PrintFormat(__FUNCTION__, ": ERROR - Unable to close position: %s - %d", Trade.ResultRetcodeDescription(), Trade.ResultRetcode());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}*/
|
||||
|
||||
void CloseAllSell()
|
||||
{
|
||||
int total = PositionsTotal();
|
||||
|
||||
// Start a loop to scan all the positions.
|
||||
// The loop starts from the last, otherwise it could skip positions.
|
||||
for (int i = total - 1; i >= 0; i--)
|
||||
{
|
||||
// If the position cannot be selected log an error.
|
||||
if (PositionGetSymbol(i) == "")
|
||||
{
|
||||
PrintFormat(__FUNCTION__, ": ERROR - Unable to select the position - %s - %d.", GetLastErrorText(GetLastError()), GetLastError());
|
||||
continue;
|
||||
}
|
||||
if (PositionGetString(POSITION_SYMBOL) != Symbol()) continue; // Only close current symbol trades.
|
||||
if (PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_SELL) continue; // Only close Sell positions.
|
||||
if (PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue; // Only close own positions.
|
||||
|
||||
for (int try = 0; try < 10; try++)
|
||||
{
|
||||
bool result = Trade.PositionClose(PositionGetInteger(POSITION_TICKET));
|
||||
if (!result)
|
||||
{
|
||||
PrintFormat(__FUNCTION__, ": ERROR - Unable to close position: %s - %d", Trade.ResultRetcodeDescription(), Trade.ResultRetcode());
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CloseAllBuy()
|
||||
{
|
||||
int total = PositionsTotal();
|
||||
|
||||
// Start a loop to scan all the positions.
|
||||
// The loop starts from the last, otherwise it could skip positions.
|
||||
for (int i = total - 1; i >= 0; i--)
|
||||
{
|
||||
// If the position cannot be selected log an error.
|
||||
if (PositionGetSymbol(i) == "")
|
||||
{
|
||||
PrintFormat(__FUNCTION__, ": ERROR - Unable to select the position - %s - %d.", GetLastErrorText(GetLastError()), GetLastError());
|
||||
continue;
|
||||
}
|
||||
if (PositionGetString(POSITION_SYMBOL) != Symbol()) continue; // Only close current symbol trades.
|
||||
if (PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_BUY) continue; // Only close Buy positions.
|
||||
if (PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue; // Only close own positions.
|
||||
|
||||
for (int try = 0; try < 10; try++)
|
||||
{
|
||||
bool result = Trade.PositionClose(PositionGetInteger(POSITION_TICKET));
|
||||
if (!result)
|
||||
{
|
||||
PrintFormat(__FUNCTION__, ": ERROR - Unable to close position: %s - %d", Trade.ResultRetcodeDescription(), Trade.ResultRetcode());
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close all positions opened by this EA.
|
||||
void CloseAllPositions()
|
||||
{
|
||||
int total = PositionsTotal();
|
||||
|
||||
// Start a loop to scan all the positions.
|
||||
// The loop starts from the last, otherwise it could skip positions.
|
||||
for (int i = total - 1; i >= 0; i--)
|
||||
{
|
||||
// If the position cannot be selected log an error.
|
||||
if (PositionGetSymbol(i) == "")
|
||||
{
|
||||
PrintFormat(__FUNCTION__, ": ERROR - Unable to select the position - %s - %d.", GetLastErrorText(GetLastError()), GetLastError());
|
||||
continue;
|
||||
}
|
||||
if (PositionGetString(POSITION_SYMBOL) != Symbol()) continue; // Only close current symbol trades.
|
||||
if (PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue; // Only close own positions.
|
||||
|
||||
for (int try = 0; try < 10; try++)
|
||||
{
|
||||
bool result = Trade.PositionClose(PositionGetInteger(POSITION_TICKET));
|
||||
if (!result)
|
||||
{
|
||||
PrintFormat(__FUNCTION__, ": ERROR - Unable to close position: %s - %d", Trade.ResultRetcodeDescription(), Trade.ResultRetcode());
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Partially close a position with a given ticket.
|
||||
bool PartialClose(ulong ticket, double percentage)
|
||||
{
|
||||
if (!PositionSelectByTicket(ticket))
|
||||
{
|
||||
PrintFormat("ERROR - Unable to select position by ticket #%d: %s - %d", ticket, GetLastErrorText(GetLastError()), GetLastError());
|
||||
return false;
|
||||
}
|
||||
double OriginalSize = PositionGetDouble(POSITION_VOLUME);
|
||||
double Size = OriginalSize * percentage / 100;
|
||||
double LotStep = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
|
||||
double MaxLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX);
|
||||
double MinLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
|
||||
Size = MathFloor(Size / LotStep) * LotStep;
|
||||
if (Size < MinLot) return false;
|
||||
if (!Trade.PositionClosePartial(ticket, Size))
|
||||
{
|
||||
PrintFormat("ERROR - Unable to partially close position #%d: %s - %d", ticket, Trade.ResultRetcodeDescription(), Trade.ResultRetcode());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculate a stop-loss price for an order.
|
||||
double StopLoss(ENUM_ORDER_TYPE order_type, double open_price)
|
||||
{
|
||||
double StopLossPrice = 0;
|
||||
if (StopLossMode == SL_FIXED) // Easy way.
|
||||
{
|
||||
if (DefaultStopLoss == 0) return 0;
|
||||
if (order_type == ORDER_TYPE_BUY)
|
||||
{
|
||||
StopLossPrice = open_price - DefaultStopLoss * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
|
||||
}
|
||||
if (order_type == ORDER_TYPE_SELL)
|
||||
{
|
||||
StopLossPrice = open_price + DefaultStopLoss * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
|
||||
}
|
||||
}
|
||||
else // Special cases.
|
||||
{
|
||||
StopLossPrice = DynamicStopLossPrice(order_type, open_price);
|
||||
}
|
||||
return NormalizeDouble(StopLossPrice, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
|
||||
}
|
||||
|
||||
// Calculate the take-profit price for an order.
|
||||
double TakeProfit(ENUM_ORDER_TYPE order_type, double open_price)
|
||||
{
|
||||
double TakeProfitPrice = 0;
|
||||
if (TakeProfitMode == TP_FIXED) // Easy way.
|
||||
{
|
||||
if (DefaultTakeProfit == 0) return 0;
|
||||
if (order_type == ORDER_TYPE_BUY)
|
||||
{
|
||||
TakeProfitPrice = open_price + DefaultTakeProfit * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
|
||||
}
|
||||
if (order_type == ORDER_TYPE_SELL)
|
||||
{
|
||||
TakeProfitPrice = open_price - DefaultTakeProfit * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
|
||||
}
|
||||
}
|
||||
else // Special cases.
|
||||
{
|
||||
TakeProfitPrice = DynamicTakeProfitPrice(order_type, open_price);
|
||||
}
|
||||
return NormalizeDouble(TakeProfitPrice, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
|
||||
}
|
||||
|
||||
// Calculate the position size for an order.
|
||||
double LotSize(double stop_loss, double open_price)
|
||||
{
|
||||
double Size = DefaultLotSize;
|
||||
if (RiskDefaultSize == RISK_DEFAULT_AUTO) // If the position size is dynamic.
|
||||
{
|
||||
if (stop_loss != 0) // Calculate position size only if SL is non-zero, otherwise there will be a division by zero error.
|
||||
{
|
||||
double RiskBaseAmount = 0;
|
||||
// TickValue is the value of the individual price increment for 1 lot of the instrument expressed in the account currency.
|
||||
double TickValue = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE);
|
||||
// Define the base for the risk calculation depending on the parameter chosen
|
||||
if (RiskBase == RISK_BASE_BALANCE) RiskBaseAmount = AccountBalance();
|
||||
else if (RiskBase == RISK_BASE_EQUITY) RiskBaseAmount = AccountEquity();
|
||||
else if (RiskBase == RISK_BASE_FREEMARGIN) RiskBaseAmount = AccountFreeMargin();
|
||||
double SL = MathAbs(open_price - stop_loss) / SymbolInfoDouble(Symbol(), SYMBOL_POINT); // SL as a number of points.
|
||||
// Calculate the Position Size.
|
||||
Size = (RiskBaseAmount * MaxRiskPerTrade / 100) / (SL * TickValue);
|
||||
}
|
||||
// If the stop loss is zero, then use the default size.
|
||||
if (stop_loss == 0)
|
||||
{
|
||||
Size = DefaultLotSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize the Lot Size to satisfy the allowed lot increment and minimum and maximum position size.
|
||||
double LotStep = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
|
||||
double MaxLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX);
|
||||
double MinLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
|
||||
Size = MathFloor(Size / LotStep) * LotStep;
|
||||
// Limit the lot size in case it is greater than the maximum allowed by the user.
|
||||
if (Size > MaxLotSize) Size = MaxLotSize;
|
||||
// Limit the lot size in case it is greater than the maximum allowed by the broker.
|
||||
if (Size > MaxLot) Size = MaxLot;
|
||||
// If the lot size is too small, then set it to 0 and don't trade.
|
||||
if ((Size < MinLotSize) || (Size < MinLot)) Size = 0;
|
||||
|
||||
return Size;
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
|
||||
// Checks to run at initialization to complete it.
|
||||
bool Prechecks()
|
||||
{
|
||||
// An example of a check to run here.
|
||||
if (MaxLotSize < MinLotSize)
|
||||
{
|
||||
Print("MaxLotSize cannot be less than MinLotSize");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Retrieve indicator data necessary for entry, update, and exit.
|
||||
// Boolean type, so it can return true if all the data is available or false if it is not.
|
||||
// Other advantage of this function is to move part of repetitive code into one location to make it leaner.
|
||||
bool GetIndicatorsData()
|
||||
{
|
||||
double buf[2]; // Needed for CopyBuffer().
|
||||
int count; // Will store the number of array elements returned by CopyBuffer().
|
||||
bool AllDataAvailable = false;
|
||||
int MaxAttemptsForData = 5;
|
||||
int DelayBetweenAttempts = 200; // Milliseconds.
|
||||
int Attempt = 0;
|
||||
|
||||
while ((!AllDataAvailable) && (Attempt < MaxAttemptsForData))
|
||||
{
|
||||
AllDataAvailable = true;
|
||||
|
||||
count = CopyBuffer(ATRHandle, 0, 0, 2, buf); // Copy using ATR indicator handle 2 latest values from 0th buffer to the buf array.
|
||||
if ((count < 2) || (buf[0] == NULL) || (buf[0] == EMPTY_VALUE))
|
||||
{
|
||||
Print("Unable to get ATR values.");
|
||||
AllDataAvailable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ATR_current = buf[1];
|
||||
ATR_previous = buf[0];
|
||||
}
|
||||
|
||||
// This is where the main indicator data is read.
|
||||
count = CopyBuffer(IndicatorHandle, 0, 1, 2, buf); // Copying using main indicator handle 2 latest completed candles (hence starting from the 1st, and not 0th, candle) from 0th buffer to the buf array.
|
||||
if (count < 2)
|
||||
{
|
||||
Print("Main indicator buffer not ready yet.");
|
||||
AllDataAvailable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Indicator_current = buf[1];
|
||||
Indicator_previous = buf[0];
|
||||
}
|
||||
|
||||
Attempt++;
|
||||
Sleep(DelayBetweenAttempts);
|
||||
}
|
||||
|
||||
if (!AllDataAvailable)
|
||||
{
|
||||
Print("Unable to get some data for the entry signal, skipping candle.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Entry signal
|
||||
void CheckEntrySignal()
|
||||
{
|
||||
if ((UseTradingHours) && (!IsCurrentTimeInInterval(TradingHourStart, TradingHourEnd))) return; // Trading hours restrictions for entry.
|
||||
|
||||
bool BuySignal = false;
|
||||
bool SellSignal = false;
|
||||
|
||||
// Buy signal conditions
|
||||
|
||||
// This is where you should insert your entry signal for BUY orders.
|
||||
// Include a condition to open a buy order, the condition will have to set BuySignal to true or false.
|
||||
|
||||
//!! Uncomment and modify this buy entry signal check line:
|
||||
if ((Indicator_current > RSIOversold) && (Indicator_previous <= RSIOversold)) BuySignal = true; // Check if the RSI crossed the oversold level from below.
|
||||
|
||||
if (BuySignal)
|
||||
{
|
||||
OpenBuy();
|
||||
}
|
||||
|
||||
// Sell signal conditions
|
||||
|
||||
// This is where you should insert your entry signal for SELL orders.
|
||||
// Include a condition to open a sell order, the condition will have to set SellSignal to true or false.
|
||||
|
||||
//!! Uncomment and modify this sell entry signal check line:
|
||||
if ((Indicator_current < RSIOverbought) && (Indicator_previous >= RSIOverbought)) SellSignal = true; // Check if the RSI crossed the overbought level from above.
|
||||
|
||||
if (SellSignal)
|
||||
{
|
||||
OpenSell();
|
||||
}
|
||||
}
|
||||
|
||||
// Exit signal
|
||||
void CheckExitSignal()
|
||||
{
|
||||
//!! if ((UseTradingHours) && (!IsCurrentTimeInInterval(TradingHourStart, TradingHourEnd))) return; // Trading hours restrictions for exit. Normally, you don't want to restrict exit by hours. Still, it's a possibility.
|
||||
|
||||
bool SignalExitLong = false;
|
||||
bool SignalExitShort = false;
|
||||
|
||||
//!! Uncomment and modify these exit signal checks:
|
||||
if ((Indicator_current > RSIOversold) && (Indicator_previous <= RSIOversold)) SignalExitShort = true; // Check if the RSI crossed the oversold level from below.
|
||||
else if ((Indicator_current < RSIOverbought) && (Indicator_previous >= RSIOverbought)) SignalExitLong = true; // Check if the RSI crossed the overbought level from above.
|
||||
|
||||
if (SignalExitLong) CloseAllBuy();
|
||||
if (SignalExitShort) CloseAllSell();
|
||||
}
|
||||
|
||||
// Dynamic stop-loss calculation
|
||||
double DynamicStopLossPrice(ENUM_ORDER_TYPE type, double open_price)
|
||||
{
|
||||
double StopLossPrice = 0;
|
||||
if (type == ORDER_TYPE_BUY)
|
||||
{
|
||||
StopLossPrice = open_price - ATR_previous * ATRMultiplierSL;
|
||||
}
|
||||
else if (type == ORDER_TYPE_SELL)
|
||||
{
|
||||
StopLossPrice = open_price + ATR_previous * ATRMultiplierSL;
|
||||
}
|
||||
return NormalizeDouble(StopLossPrice, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
|
||||
}
|
||||
|
||||
// Dynamic take-profit calculation
|
||||
double DynamicTakeProfitPrice(ENUM_ORDER_TYPE type, double open_price)
|
||||
{
|
||||
double TakeProfitPrice = 0;
|
||||
if (type == ORDER_TYPE_BUY)
|
||||
{
|
||||
TakeProfitPrice = open_price + ATR_previous * ATRMultiplierTP;
|
||||
}
|
||||
else if (type == ORDER_TYPE_SELL)
|
||||
{
|
||||
TakeProfitPrice = open_price - ATR_previous * ATRMultiplierTP;
|
||||
}
|
||||
return NormalizeDouble(TakeProfitPrice, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
|
||||
}
|
||||
|
||||
// Partially close all positions opened by this EA.
|
||||
void PartialCloseAll()
|
||||
{
|
||||
int total = PositionsTotal();
|
||||
|
||||
// Start a loop to scan all the positions.
|
||||
// The loop starts from the last, otherwise it could skip positions.
|
||||
for (int i = total - 1; i >= 0; i--)
|
||||
{
|
||||
// If the position cannot be selected log an error.
|
||||
if (PositionGetSymbol(i) == "")
|
||||
{
|
||||
Print(__FUNCTION__, ": ERROR - Unable to select the position - ", GetLastError());
|
||||
continue;
|
||||
}
|
||||
if (PositionGetString(POSITION_SYMBOL) != Symbol()) continue; // Only close current symbol trades.
|
||||
if (PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue; // Only close own positions.
|
||||
|
||||
int position_ticket = (int)PositionGetInteger(POSITION_TICKET);
|
||||
|
||||
// Retrieve the history of deals and orders for that position to check if it hasn't been already partially closed.
|
||||
if (!HistorySelectByPosition(PositionGetInteger(POSITION_IDENTIFIER)))
|
||||
{
|
||||
PrintFormat("ERROR - Unable to get position history for %d - %s - %d", position_ticket, GetLastErrorText(GetLastError()), GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
bool need_partial_close = true;
|
||||
|
||||
// Process partial close for a long position.
|
||||
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
||||
{
|
||||
for (int j = HistoryDealsTotal() - 1; j >= 0; j--)
|
||||
{
|
||||
long deal_ticket = (int)HistoryDealGetTicket(j);
|
||||
if (!deal_ticket)
|
||||
{
|
||||
PrintFormat("Unable to get deal for %d - %s - %d", position_ticket, GetLastErrorText(GetLastError()), GetLastError());
|
||||
break;
|
||||
}
|
||||
if (HistoryDealGetInteger(deal_ticket, DEAL_TYPE) == DEAL_TYPE_SELL) // Looks like this long position has already been partially closed at least once.
|
||||
{
|
||||
need_partial_close = false;
|
||||
break; // No need to partially close this position.
|
||||
}
|
||||
}
|
||||
// Condition for partial close of a long position.
|
||||
if ((need_partial_close) && (SymbolInfoDouble(Symbol(), SYMBOL_BID) - PositionGetDouble(POSITION_PRICE_OPEN) > ATR_previous * ATRMultiplierPC))
|
||||
{
|
||||
PartialClose(position_ticket, PartialClosePerc);
|
||||
}
|
||||
}
|
||||
// Process partial close for a short position.
|
||||
else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
|
||||
{
|
||||
for (int j = HistoryDealsTotal() - 1; j >= 0; j--)
|
||||
{
|
||||
long deal_ticket = (int)HistoryDealGetTicket(j);
|
||||
if (!deal_ticket)
|
||||
{
|
||||
PrintFormat("Unable to get deal for %d - %s - %d", position_ticket, GetLastErrorText(GetLastError()), GetLastError());
|
||||
return;
|
||||
}
|
||||
if (HistoryDealGetInteger(deal_ticket, DEAL_TYPE) == DEAL_TYPE_BUY) // Looks like this short position has already been partially closed at least once.
|
||||
{
|
||||
need_partial_close = false;
|
||||
break; // No need to partially close this position.
|
||||
}
|
||||
}
|
||||
// Condition for partial close of a short position.
|
||||
if ((need_partial_close) && (PositionGetDouble(POSITION_PRICE_OPEN) - SymbolInfoDouble(Symbol(), SYMBOL_ASK) > ATR_previous * ATRMultiplierPC))
|
||||
{
|
||||
PartialClose(position_ticket, PartialClosePerc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @copyright 2019, pipbolt.io <beta@pipbolt.io>
|
||||
* @license https://github.com/pipbolt/experts/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
#include <PipboltFramework\Constants.mqh>
|
||||
|
||||
#define NAME "Stochastic Oscillator EA"
|
||||
#define VERSION "0.022"
|
||||
|
||||
#property copyright COPYRIGHT
|
||||
#property link LINK
|
||||
#property icon ICON
|
||||
#property description DESCRIPTION
|
||||
#property version VERSION
|
||||
|
||||
#include <PipboltFramework\Params\MainSettings.mqh>
|
||||
|
||||
input group "Entry Strategy";
|
||||
|
||||
input group "Exit Strategy";
|
||||
input bool UseExitStrategy = false; // Use Exit Strategy
|
||||
|
||||
input group "Stochastic Oscillator";
|
||||
input int StochKPeriod = 5; // %K Period
|
||||
input int StochDPeriod = 3; // %D Period
|
||||
input int StochSlowing = 3; // Slowing
|
||||
input ENUM_MA_METHOD StochMethod = MODE_SMA; // Method
|
||||
input ENUM_STO_PRICE StochPrice = STO_LOWHIGH; // Price
|
||||
input int StochBuyLevel = 20; // Buy Level
|
||||
input int StochSellLevel = 80; // Sell Level
|
||||
|
||||
#include <PipboltFramework\Experts.mqh>
|
||||
|
||||
CiStochastic Stoch;
|
||||
|
||||
int OnInit(void)
|
||||
{
|
||||
if (ONINIT() != INIT_SUCCEEDED)
|
||||
return INIT_FAILED;
|
||||
|
||||
Stoch.Init(NULL, NULL, StochKPeriod, StochDPeriod, StochSlowing, StochMethod, StochPrice);
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnTick(void) { ONTICK(); }
|
||||
void OnDeinit(const int reason) { ONDEINIT(reason); }
|
||||
void OnTimer() { ONTIMER(); }
|
||||
|
||||
void CheckForOpen(bool &openBuy, bool &openSell)
|
||||
{
|
||||
// Buy Entry Strategy
|
||||
if (Stoch.Signal(1) <= StochBuyLevel && Stoch.Main(1) <= Stoch.Signal(1) &&
|
||||
Stoch.Main(0) <= StochBuyLevel && Stoch.Signal(0) <= Stoch.Main(0))
|
||||
openBuy = true;
|
||||
|
||||
// Sell Entry Strategy
|
||||
else if (Stoch.Signal(1) >= StochSellLevel && Stoch.Main(1) >= Stoch.Signal(1) &&
|
||||
Stoch.Main(0) >= StochSellLevel && Stoch.Signal(0) >= Stoch.Main(0))
|
||||
openSell = true;
|
||||
|
||||
// Apply MA Filter
|
||||
openBuy = openBuy && MAFilter.Check(DIR_BUY);
|
||||
openSell = openSell && MAFilter.Check(DIR_SELL);
|
||||
}
|
||||
|
||||
void CheckForClose(bool &closeBuy, bool &closeSell)
|
||||
{
|
||||
// Buy Exit Strategy
|
||||
closeBuy = Stoch.Main(0) >= StochSellLevel;
|
||||
|
||||
// Sell Exit Strategy
|
||||
closeSell = Stoch.Main(0) <= StochBuyLevel;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @copyright 2019, pipbolt.io <beta@pipbolt.io>
|
||||
* @license https://github.com/pipbolt/experts/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
#include <PipboltFramework\Constants.mqh>
|
||||
|
||||
#define NAME "Parabolic SAR EA"
|
||||
#define VERSION "0.022"
|
||||
|
||||
#property copyright COPYRIGHT
|
||||
#property link LINK
|
||||
#property icon ICON
|
||||
#property description DESCRIPTION
|
||||
#property version VERSION
|
||||
|
||||
#include <PipboltFramework\Params\MainSettings.mqh>
|
||||
|
||||
input group "Entry Strategy";
|
||||
|
||||
input group "Exit Strategy";
|
||||
input bool UseExitStrategy = false; // Use Exit Strategy
|
||||
|
||||
input group "Parabolic SAR";
|
||||
input double PSAR_Step = 0.02; // Step
|
||||
input double PSAR_Maximum = 0.2; // Maximum
|
||||
|
||||
#include <PipboltFramework\Experts.mqh>
|
||||
|
||||
CiSAR PSAR;
|
||||
|
||||
int OnInit(void)
|
||||
{
|
||||
if (ONINIT() != INIT_SUCCEEDED)
|
||||
return INIT_FAILED;
|
||||
|
||||
PSAR.Init(NULL, NULL, PSAR_Step, PSAR_Maximum);
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnTick(void) { ONTICK(); }
|
||||
void OnDeinit(const int reason) { ONDEINIT(reason); }
|
||||
void OnTimer() { ONTIMER(); }
|
||||
|
||||
void CheckForOpen(bool &openBuy, bool &openSell)
|
||||
{
|
||||
// Close variables
|
||||
double close0 = iClose(NULL, NULL, _indicatorShift);
|
||||
double close1 = iClose(NULL, NULL, _indicatorShift + 1);
|
||||
|
||||
// Buy Entry Strategy
|
||||
if (PSAR.Main(1) > close1 && PSAR.Main(0) < close0)
|
||||
openBuy = true;
|
||||
|
||||
// Sell Entry Strategy
|
||||
else if (PSAR.Main(1) < close1 && PSAR.Main(0) > close0)
|
||||
openSell = true;
|
||||
|
||||
// Apply MA Filter
|
||||
openBuy = openBuy && MAFilter.Check(DIR_BUY);
|
||||
openSell = openSell && MAFilter.Check(DIR_SELL);
|
||||
}
|
||||
|
||||
void CheckForClose(bool &closeBuy, bool &closeSell)
|
||||
{
|
||||
// Close variables
|
||||
double close0 = iClose(NULL, NULL, _indicatorShift);
|
||||
double close1 = iClose(NULL, NULL, _indicatorShift + 1);
|
||||
|
||||
// Buy Exit Strategy
|
||||
closeBuy = PSAR.Main(0) > close0 && PSAR.Main(1) < close1;
|
||||
|
||||
// Sell Exit Strategy
|
||||
closeSell = PSAR.Main(0) < close0 && PSAR.Main(1) > close1;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,253 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DAILY RANGE BREAKOUT EA.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"
|
||||
|
||||
double maximum_price = -DBL_MAX;
|
||||
double minimum_price = +DBL_MAX;
|
||||
datetime maximum_time, minimum_time;
|
||||
|
||||
bool isHaveDailyRange_Prices = false;
|
||||
bool isHaveDailyRange_Break = false;
|
||||
|
||||
#define RECTANGLE_PREFIX "RANGE RECTANGLE "
|
||||
#define UPPER_LINE_PREFIX "UPPER LINE"
|
||||
#define LOWER_LINE_PREFIX "LOWER LINE"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit(){
|
||||
//---
|
||||
|
||||
//---
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason){
|
||||
//---
|
||||
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick(){
|
||||
//---
|
||||
|
||||
static datetime midnight = iTime(_Symbol,PERIOD_D1,0);
|
||||
static datetime sixAM = midnight + 6 * 3600;
|
||||
static datetime scanBarTime = sixAM + 1 * PeriodSeconds(_Period); // next bar
|
||||
|
||||
static datetime validBreakTime_start = scanBarTime;
|
||||
static datetime validBreakTime_end = midnight + (6+5) * 3600; // 11 AM
|
||||
|
||||
//Print("END TIME = ",validBreakTime_end);
|
||||
|
||||
//Print("Midnight T = ",midnight,", 6 AM = ",sixAM,", SCAN BAT T = ",scanBarTime);
|
||||
|
||||
if (isNewDay()){
|
||||
|
||||
midnight = iTime(_Symbol,PERIOD_D1,0);
|
||||
sixAM = midnight + 6 * 3600;
|
||||
scanBarTime = sixAM + 1 * PeriodSeconds(_Period); // next bar
|
||||
|
||||
validBreakTime_start = scanBarTime;
|
||||
validBreakTime_end = midnight + (6+5) * 3600; // 11 AM
|
||||
|
||||
maximum_price = -DBL_MAX;
|
||||
minimum_price = +DBL_MAX;
|
||||
|
||||
isHaveDailyRange_Prices = false;
|
||||
isHaveDailyRange_Break = false;
|
||||
}
|
||||
|
||||
if (isNewBar()){
|
||||
datetime currentBarTime = iTime(_Symbol,_Period,0);
|
||||
|
||||
if (currentBarTime == scanBarTime && !isHaveDailyRange_Prices){
|
||||
Print("WE HAVE ENOUGH BARS DATA FOR DOCUMENTATION. MAKE THE DATA EXTRACTION NOW");
|
||||
int total_bars = int((sixAM - midnight)/PeriodSeconds(_Period))+1;
|
||||
Print("Total bars for scan = ",total_bars);
|
||||
int highest_price_bar_index = -1;
|
||||
int lowest_price_bar_index = -1;
|
||||
|
||||
for (int i=1; i<=total_bars; i++){
|
||||
double open_i = open(i);
|
||||
double close_i = close(i);
|
||||
|
||||
double highest_price_i = (open_i > close_i) ? open_i : close_i;
|
||||
double lowest_price_i = (open_i < close_i) ? open_i : close_i;
|
||||
|
||||
if (highest_price_i > maximum_price){
|
||||
maximum_price = highest_price_i;
|
||||
highest_price_bar_index = i;
|
||||
maximum_time = time(i);
|
||||
}
|
||||
if (lowest_price_i < minimum_price){
|
||||
minimum_price = lowest_price_i;
|
||||
lowest_price_bar_index = i;
|
||||
minimum_time = time(i);
|
||||
}
|
||||
}
|
||||
Print("Maximum Price = ",maximum_price,", Bar Index = ",highest_price_bar_index,", Time = ",maximum_time);
|
||||
Print("Minimum Price = ",minimum_price,", Bar Index = ",lowest_price_bar_index,", Time = ",minimum_time);
|
||||
|
||||
create_Rectangle(RECTANGLE_PREFIX+TimeToString(maximum_time),maximum_time,maximum_price,minimum_time,minimum_price,clrBlue);
|
||||
create_Line(UPPER_LINE_PREFIX+TimeToString(midnight),midnight,maximum_price,sixAM,maximum_price,3,clrBlack,DoubleToString(maximum_price,_Digits));
|
||||
create_Line(LOWER_LINE_PREFIX+TimeToString(midnight),midnight,minimum_price,sixAM,minimum_price,3,clrRed,DoubleToString(minimum_price,_Digits));
|
||||
|
||||
isHaveDailyRange_Prices = true;
|
||||
}
|
||||
}
|
||||
|
||||
double barClose = close(1);
|
||||
datetime barTime = time(1);
|
||||
|
||||
if (barClose > maximum_price && isHaveDailyRange_Prices && !isHaveDailyRange_Break
|
||||
&& barTime >= validBreakTime_start && barTime <= validBreakTime_end
|
||||
){
|
||||
Print("CLOSE Price broke the HIGH range. ",barClose," > ",maximum_price);
|
||||
isHaveDailyRange_Break = true;
|
||||
drawBreakPoint(TimeToString(barTime),barTime,barClose,234,clrBlack,-1);
|
||||
}
|
||||
else if (barClose < minimum_price && isHaveDailyRange_Prices && !isHaveDailyRange_Break
|
||||
&& barTime >= validBreakTime_start && barTime <= validBreakTime_end
|
||||
){
|
||||
Print("CLOSE Price broke the LOW range. ",barClose," < ",minimum_price);
|
||||
isHaveDailyRange_Break = true;
|
||||
drawBreakPoint(TimeToString(barTime),barTime,barClose,233,clrBlue,+1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
double open(int index){return (iOpen(_Symbol,_Period,index));}
|
||||
double high(int index){return (iHigh(_Symbol,_Period,index));}
|
||||
double low(int index){return (iLow(_Symbol,_Period,index));}
|
||||
double close(int index){return (iClose(_Symbol,_Period,index));}
|
||||
datetime time(int index){return (iTime(_Symbol,_Period,index));}
|
||||
|
||||
|
||||
|
||||
|
||||
bool isNewBar(){
|
||||
static int prevbars = 0;
|
||||
int currbars = iBars(_Symbol,_Period);
|
||||
if (prevbars == currbars) return (false);
|
||||
prevbars = currbars;
|
||||
return (true);
|
||||
}
|
||||
|
||||
void create_Rectangle(string objName,datetime time1,double price1,
|
||||
datetime time2,double price2,color clr){
|
||||
if (ObjectFind(0,objName) < 0){
|
||||
ObjectCreate(0,objName,OBJ_RECTANGLE,0,time1,price1,time2,price2);
|
||||
|
||||
ObjectSetInteger(0,objName,OBJPROP_TIME,0,time1);
|
||||
ObjectSetDouble(0,objName,OBJPROP_PRICE,0,price1);
|
||||
ObjectSetInteger(0,objName,OBJPROP_TIME,1,time2);
|
||||
ObjectSetDouble(0,objName,OBJPROP_PRICE,1,price2);
|
||||
|
||||
ObjectSetInteger(0,objName,OBJPROP_FILL,true);
|
||||
ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
|
||||
ObjectSetInteger(0,objName,OBJPROP_BACK,false);
|
||||
|
||||
ChartRedraw(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool isNewDay(){
|
||||
bool newDay = false;
|
||||
|
||||
MqlDateTime str_datetime;
|
||||
TimeToStruct(TimeCurrent(), str_datetime);
|
||||
|
||||
static int prevday = 0;
|
||||
int currday = str_datetime.day;
|
||||
|
||||
if (prevday == currday){// we are still in current day
|
||||
newDay = false;
|
||||
}
|
||||
else if (prevday != currday){// we have a new day
|
||||
Print("WE HAVE A NEW DAY WITH DATE ",currday);
|
||||
prevday = currday;
|
||||
newDay = true;
|
||||
}
|
||||
return (newDay);
|
||||
}
|
||||
|
||||
void create_Line(string objName,datetime time1,double price1,
|
||||
datetime time2,double price2,int width,color clr,string text){
|
||||
if (ObjectFind(0,objName) < 0){
|
||||
ObjectCreate(0,objName,OBJ_TREND,0,time1,price1,time2,price2);
|
||||
|
||||
ObjectSetInteger(0,objName,OBJPROP_TIME,0,time1);
|
||||
ObjectSetDouble(0,objName,OBJPROP_PRICE,0,price1);
|
||||
ObjectSetInteger(0,objName,OBJPROP_TIME,1,time2);
|
||||
ObjectSetDouble(0,objName,OBJPROP_PRICE,1,price2);
|
||||
|
||||
ObjectSetInteger(0,objName,OBJPROP_WIDTH,width);
|
||||
ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
|
||||
ObjectSetInteger(0,objName,OBJPROP_BACK,false);
|
||||
|
||||
long scale = 0;
|
||||
if (!ChartGetInteger(0,CHART_SCALE,0,scale)){
|
||||
Print("UNABLE TO GET THE CHART SCALE. DEFAULT VALUE OF ",scale," IS CONSIDERED.");
|
||||
}
|
||||
|
||||
int fontsize = 11;
|
||||
// 0=minimized, 5 = maximized
|
||||
if (scale==0){fontsize=5;}
|
||||
else if (scale==1){fontsize=6;}
|
||||
else if (scale==2){fontsize=7;}
|
||||
else if (scale==3){fontsize=9;}
|
||||
else if (scale==4){fontsize=11;}
|
||||
else if (scale==5){fontsize=13;}
|
||||
|
||||
string txt = " Right Price";
|
||||
string objNameDescr = objName + txt;
|
||||
ObjectCreate(0,objNameDescr,OBJ_TEXT,0,time2,price2);
|
||||
ObjectSetInteger(0,objNameDescr,OBJPROP_COLOR,clr);
|
||||
ObjectSetInteger(0,objNameDescr,OBJPROP_FONTSIZE,fontsize);
|
||||
ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT);
|
||||
ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + text);
|
||||
ObjectSetString(0,objNameDescr,OBJPROP_FONT,"Calibri");
|
||||
|
||||
ChartRedraw(0);
|
||||
}
|
||||
}
|
||||
|
||||
void drawBreakPoint(string objName,datetime time,double price,int arrCode,
|
||||
color clr,int direction){
|
||||
|
||||
if (ObjectFind(0,objName) < 0){
|
||||
ObjectCreate(0,objName,OBJ_ARROW,0,time,price);
|
||||
ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrCode);
|
||||
ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
|
||||
ObjectSetInteger(0,objName,OBJPROP_FONTSIZE,12);
|
||||
if (direction > 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP);
|
||||
if (direction < 0) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM);
|
||||
|
||||
string txt = " Breakout";
|
||||
string objNameDescr = objName + txt;
|
||||
ObjectCreate(0,objNameDescr,OBJ_TEXT,0,time,price);
|
||||
ObjectSetInteger(0,objNameDescr,OBJPROP_COLOR,clr);
|
||||
ObjectSetInteger(0,objNameDescr,OBJPROP_FONTSIZE,12);
|
||||
if (direction > 0) {
|
||||
ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_UPPER);
|
||||
ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
|
||||
}
|
||||
if (direction < 0) {
|
||||
ObjectSetInteger(0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
|
||||
ObjectSetString(0,objNameDescr,OBJPROP_TEXT, " " + txt);
|
||||
}
|
||||
}
|
||||
ChartRedraw(0);
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,206 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| JamesOBR.mq4 |
|
||||
//| Copyright 2012,Clifford H. James |
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
#include <user/utils_trades.mq4>
|
||||
|
||||
#property copyright "Copyright 2012,Clifford H. James"
|
||||
#property link ""
|
||||
|
||||
|
||||
// CONSTANTS
|
||||
extern double OBR_PIP_OFFSET = 0.0002;
|
||||
extern int EET_START = 10;
|
||||
extern double OBR_RATIO = 1.9;
|
||||
extern double ATR_PERIOD = 72;
|
||||
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int init()
|
||||
{
|
||||
//---
|
||||
|
||||
|
||||
//----
|
||||
return(0);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int deinit()
|
||||
{
|
||||
//----
|
||||
|
||||
//----
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
//---
|
||||
// calculates the ORB
|
||||
//---
|
||||
double CalcCurrORB()
|
||||
{
|
||||
// Get the ATR of the 10EET Bar...we run on the
|
||||
double currATR = iATR(NULL, 0, ATR_PERIOD, 1);
|
||||
//Print("Curr ATR(72): ", currATR);
|
||||
return (currATR + OBR_PIP_OFFSET);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Generate Daily pending orders based on the specified ORB value
|
||||
// This will generate both a BUY_STOP and SELL_STOP pending order.
|
||||
//---
|
||||
void generateDailyPendingOrders(double orbval)
|
||||
{
|
||||
|
||||
double tenEETHi = High[1]; //Goes back 1 bar to compute 10EET bar high
|
||||
double tenEETLo = Low[1]; // Goes back 1 bar to compute 10EET bar low
|
||||
int slippage = 2;
|
||||
|
||||
double buyEntry = tenEETHi + orbval;
|
||||
double SL = buyEntry - (1.65 * orbval);
|
||||
double TP = buyEntry + orbval;
|
||||
double SL_Dist = RelDistToPoints(SL);
|
||||
double TP_Dist = RelDistToPoints(TP);
|
||||
int lotSize = 1;
|
||||
|
||||
Alert("Current Price: ", Bid,"/",Ask);
|
||||
|
||||
// buy side
|
||||
PlacePendingStopOrder(
|
||||
OP_BUYSTOP,
|
||||
Symbol(),
|
||||
buyEntry,
|
||||
lotSize,
|
||||
SL_Dist,
|
||||
TP_Dist
|
||||
);
|
||||
|
||||
double sellEntry = tenEETLo - orbval;
|
||||
SL = sellEntry + (1.65 * orbval);
|
||||
TP = sellEntry - orbval;
|
||||
SL_Dist = RelDistToPoints(SL);
|
||||
TP_Dist = RelDistToPoints(TP);
|
||||
|
||||
// sell side
|
||||
PlacePendingStopOrder(
|
||||
OP_SELLSTOP,
|
||||
Symbol(),
|
||||
sellEntry,
|
||||
lotSize,
|
||||
SL_Dist,
|
||||
TP_Dist
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
//---
|
||||
// determine if we are at the close of the day or not
|
||||
//---
|
||||
bool AtCloseOfDay() {
|
||||
int currHour=TimeHour(TimeCurrent());
|
||||
int currMin=TimeMinute(TimeCurrent());
|
||||
return(currHour == 17 && currMin == 30);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Calculate trade volume (lot size) for the current symbol based on:
|
||||
// - Current free margin in your account
|
||||
// - SL dist in points
|
||||
// - Desired risk % (0-100)
|
||||
// - Tick Value of current symbol
|
||||
//
|
||||
// Additional MIN/MAX lot constraints for the current symbold are applied
|
||||
//
|
||||
// - If you are requesting a volume < minLots for the current symbol
|
||||
// then -1 is returned, this indicates that the current trade cannot be made
|
||||
//
|
||||
// - If you are requesting a volume > maxLots for the current symbol
|
||||
// then your trade volume is effectively "capped" at maxLotSize
|
||||
//-------------------------------------------------------------------------------
|
||||
double calcTradeVolume(double risk, double stopLossPoints)
|
||||
{
|
||||
double minLotAllowed = MarketInfo(Symbol(), MODE_MINLOT);
|
||||
double maxLotAllowed = MarketInfo(Symbol(), MODE_MAXLOT);
|
||||
|
||||
double vol = (AccountFreeMargin() * (risk/100)) /
|
||||
( stopLossPoints * MarketInfo(Symbol(), MODE_TICKVALUE) );
|
||||
|
||||
if(vol < minLotAllowed)
|
||||
vol = -1.0;
|
||||
if(vol > maxLotAllowed)
|
||||
vol = maxLotAllowed;
|
||||
|
||||
return(vol);
|
||||
}
|
||||
|
||||
double calcSLDist(double entryPrice, double stopLossPrice)
|
||||
{
|
||||
return(-1.0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| expert start function |
|
||||
//+------------------------------------------------------------------+
|
||||
int start()
|
||||
{
|
||||
//----
|
||||
// TODO: Check some start conditions ...
|
||||
|
||||
// little magic to detect new bars ...
|
||||
static datetime Time0;
|
||||
static bool processedClose;
|
||||
|
||||
|
||||
//int currMin = TimeMinute(Time[0]);
|
||||
|
||||
if(AtCloseOfDay()) {
|
||||
if(!processedClose) {
|
||||
//Alert("Got Close of Day @: ", TimeToStr(Time[0],TIME_DATE|TIME_MINUTES));
|
||||
//printInfo();
|
||||
CloseAllOutstandingOrders();
|
||||
processedClose = true;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
processedClose = false;
|
||||
|
||||
// check for first bar of the hour ...
|
||||
if (Time0 == Time[0]) return;
|
||||
Time0 = Time[0];
|
||||
int currHour = TimeHour(Time[0]);
|
||||
|
||||
//Alert("Got a new bar at time: ", TimeToStr(Time[0],TIME_DATE|TIME_MINUTES));
|
||||
double currOrb = 0;
|
||||
if(currHour == 11) {
|
||||
currOrb = CalcCurrORB();
|
||||
|
||||
//Alert("ORB value on: ", TimeToStr(Time[0],TIME_DATE|TIME_MINUTES), " is: ", currOrb);
|
||||
|
||||
// generate daily pending orders for buy/sell
|
||||
generateDailyPendingOrders(currOrb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//----
|
||||
return(0);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
============================================================
|
||||
Demo File: Asia_Session_Breakout_EA - Signal Logic Showcase
|
||||
Category: Session Breakout
|
||||
Platform: MetaTrader 4 (MQL4)
|
||||
Version: 1.0
|
||||
Author: Giacomo Cipolat Bares
|
||||
Portfolio: MQL4 Expert Advisors Portfolio
|
||||
============================================================
|
||||
|
||||
Description:
|
||||
This is a simplified public demo derived from the full
|
||||
Asia Session Breakout EA.
|
||||
|
||||
Included in this demo:
|
||||
- Asia session high/low detection
|
||||
- session range calculation
|
||||
- breakout trigger calculation
|
||||
- Asia range filter
|
||||
- late breakout filter
|
||||
- basic on-chart signal output
|
||||
|
||||
Excluded from this demo:
|
||||
- order execution
|
||||
- pending order management
|
||||
- risk management engine
|
||||
- break-even / trailing stop
|
||||
- retry logic
|
||||
- broker protection handling
|
||||
- full production trade framework
|
||||
- chart visualization layer
|
||||
============================================================
|
||||
*/
|
||||
|
||||
#property strict
|
||||
#property version "1.00"
|
||||
|
||||
//========================= INPUTS ==================================
|
||||
input string __01_SessionSettings = "01 ======== Session Settings ========";
|
||||
input int AsiaStartHour = 0;
|
||||
input int AsiaEndHour = 6;
|
||||
input int LondonStartHour = 7;
|
||||
input int LondonEndHour = 12;
|
||||
input int SessionCalculationTimeframe = PERIOD_M1;
|
||||
|
||||
input string __02_BreakoutSettings = "02 ======== Breakout Settings ========";
|
||||
input double BreakoutBufferPips = 1.0;
|
||||
input bool UseAsiaRangeFilter = true;
|
||||
input bool NoLateBreakout = true;
|
||||
input double MinAsiaRangePips = 5.0;
|
||||
input double MaxAsiaRangePips = 30.0;
|
||||
|
||||
//======================= GLOBALS ===================================
|
||||
double g_point;
|
||||
double g_pip;
|
||||
int g_digits;
|
||||
|
||||
double g_asiaHigh = -1.0;
|
||||
double g_asiaLow = -1.0;
|
||||
bool g_asiaRangeFinalized = false;
|
||||
datetime g_lastSessionDay = -1;
|
||||
datetime g_lastBarTime = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
g_point = Point;
|
||||
g_digits = Digits;
|
||||
|
||||
if(g_digits == 5 || g_digits == 3)
|
||||
g_pip = g_point * 10.0;
|
||||
else
|
||||
g_pip = g_point;
|
||||
|
||||
Print("Asia Session Breakout demo initialized");
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Detects a new bar |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime currentBarTime = iTime(NULL, 0, 0);
|
||||
|
||||
if(currentBarTime != g_lastBarTime)
|
||||
{
|
||||
g_lastBarTime = currentBarTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Current hour helper |
|
||||
//+------------------------------------------------------------------+
|
||||
int CurrentHour()
|
||||
{
|
||||
return TimeHour(TimeCurrent());
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Session checks |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsInsideAsiaSession()
|
||||
{
|
||||
int hour = TimeHour(TimeCurrent());
|
||||
return (hour >= AsiaStartHour && hour < AsiaEndHour);
|
||||
}
|
||||
|
||||
bool IsLondonSession()
|
||||
{
|
||||
int hour = CurrentHour();
|
||||
return (hour >= LondonStartHour && hour < LondonEndHour);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Date helper |
|
||||
//+------------------------------------------------------------------+
|
||||
datetime DateOfDay(datetime t)
|
||||
{
|
||||
return t - (t % 86400);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Session time builders |
|
||||
//+------------------------------------------------------------------+
|
||||
datetime GetSessionStart(datetime dayStart, int startHour)
|
||||
{
|
||||
return dayStart + startHour * 3600;
|
||||
}
|
||||
|
||||
datetime GetSessionEnd(datetime dayStart, int endHour)
|
||||
{
|
||||
return dayStart + endHour * 3600;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculates Asia session high/low |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CalculateAsiaRange(double &asiaHigh, double &asiaLow)
|
||||
{
|
||||
datetime today = DateOfDay(TimeCurrent());
|
||||
|
||||
datetime asiaStart = GetSessionStart(today, AsiaStartHour);
|
||||
datetime asiaEnd = GetSessionEnd(today, AsiaEndHour);
|
||||
|
||||
int tf = SessionCalculationTimeframe;
|
||||
|
||||
int startShift = iBarShift(Symbol(), tf, asiaEnd - 1, false);
|
||||
int endShift = iBarShift(Symbol(), tf, asiaStart, false);
|
||||
|
||||
if(startShift < 0 || endShift < 0)
|
||||
return false;
|
||||
|
||||
int count = endShift - startShift + 1;
|
||||
|
||||
if(count <= 0)
|
||||
return false;
|
||||
|
||||
int highestShift = iHighest(Symbol(), tf, MODE_HIGH, count, startShift);
|
||||
int lowestShift = iLowest(Symbol(), tf, MODE_LOW, count, startShift);
|
||||
|
||||
if(highestShift < 0 || lowestShift < 0)
|
||||
return false;
|
||||
|
||||
asiaHigh = iHigh(Symbol(), tf, highestShift);
|
||||
asiaLow = iLow(Symbol(), tf, lowestShift);
|
||||
|
||||
return (asiaHigh > 0 && asiaLow > 0);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Updates tracked Asia range |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateAsiaRange()
|
||||
{
|
||||
if(g_asiaRangeFinalized)
|
||||
return;
|
||||
|
||||
double asiaHigh = -1.0;
|
||||
double asiaLow = -1.0;
|
||||
|
||||
if(CalculateAsiaRange(asiaHigh, asiaLow))
|
||||
{
|
||||
g_asiaHigh = asiaHigh;
|
||||
g_asiaLow = asiaLow;
|
||||
}
|
||||
|
||||
datetime today = DateOfDay(TimeCurrent());
|
||||
datetime asiaEnd = GetSessionEnd(today, AsiaEndHour);
|
||||
|
||||
if(TimeCurrent() >= asiaEnd)
|
||||
g_asiaRangeFinalized = true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Reset session state daily |
|
||||
//+------------------------------------------------------------------+
|
||||
void ResetSessionState()
|
||||
{
|
||||
datetime today = DateOfDay(TimeCurrent());
|
||||
|
||||
if(today != g_lastSessionDay)
|
||||
{
|
||||
g_lastSessionDay = today;
|
||||
g_asiaHigh = -1.0;
|
||||
g_asiaLow = -1.0;
|
||||
g_asiaRangeFinalized = false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Range and trigger helpers |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetAsiaRangePips()
|
||||
{
|
||||
if(g_asiaHigh <= 0 || g_asiaLow <= 0)
|
||||
return 0.0;
|
||||
|
||||
return (g_asiaHigh - g_asiaLow) / g_pip;
|
||||
}
|
||||
|
||||
bool AsiaRangeFilterPassed()
|
||||
{
|
||||
if(!UseAsiaRangeFilter)
|
||||
return true;
|
||||
|
||||
double asiaRangePips = GetAsiaRangePips();
|
||||
|
||||
if(asiaRangePips < MinAsiaRangePips)
|
||||
return false;
|
||||
|
||||
if(asiaRangePips > MaxAsiaRangePips)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double GetBuyTrigger()
|
||||
{
|
||||
return g_asiaHigh + BreakoutBufferPips * g_pip;
|
||||
}
|
||||
|
||||
double GetSellTrigger()
|
||||
{
|
||||
return g_asiaLow - BreakoutBufferPips * g_pip;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Late breakout filter |
|
||||
//+------------------------------------------------------------------+
|
||||
bool LateBreakout()
|
||||
{
|
||||
if(!NoLateBreakout)
|
||||
return true;
|
||||
|
||||
if(Ask > GetBuyTrigger() || Bid < GetSellTrigger())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Demo breakout signal wrappers |
|
||||
//+------------------------------------------------------------------+
|
||||
bool BuySignal()
|
||||
{
|
||||
if(!IsLondonSession())
|
||||
return false;
|
||||
|
||||
if(!AsiaRangeFilterPassed())
|
||||
return false;
|
||||
|
||||
if(!LateBreakout())
|
||||
return false;
|
||||
|
||||
if(g_asiaHigh <= 0 || g_asiaLow <= 0)
|
||||
return false;
|
||||
|
||||
return (Ask > GetBuyTrigger());
|
||||
}
|
||||
|
||||
bool SellSignal()
|
||||
{
|
||||
if(!IsLondonSession())
|
||||
return false;
|
||||
|
||||
if(!AsiaRangeFilterPassed())
|
||||
return false;
|
||||
|
||||
if(!LateBreakout())
|
||||
return false;
|
||||
|
||||
if(g_asiaHigh <= 0 || g_asiaLow <= 0)
|
||||
return false;
|
||||
|
||||
return (Bid < GetSellTrigger());
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
if(!IsNewBar())
|
||||
return;
|
||||
|
||||
ResetSessionState();
|
||||
UpdateAsiaRange();
|
||||
|
||||
if(BuySignal())
|
||||
{
|
||||
Comment("Demo Signal: BUY Asia breakout detected");
|
||||
return;
|
||||
}
|
||||
|
||||
if(SellSignal())
|
||||
{
|
||||
Comment("Demo Signal: SELL Asia breakout detected");
|
||||
return;
|
||||
}
|
||||
|
||||
Comment("Demo Signal: No valid Asia breakout");
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,647 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trendline Breakout Trader EA.mq5 |
|
||||
//| Copyright 2025, Allan Munene Mutiiria. |
|
||||
//| https://t.me/Forex_Algo_Trader |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, Allan Munene Mutiiria."
|
||||
#property link "https://t.me/Forex_Algo_Trader"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh> //--- Include Trade library for trading operations
|
||||
CTrade obj_Trade; //--- Instantiate trade object
|
||||
//+------------------------------------------------------------------+
|
||||
//| Breakout definition enumeration |
|
||||
//+------------------------------------------------------------------+
|
||||
enum ENUM_BREAKOUT_TYPE {
|
||||
BREAKOUT_CLOSE = 0, // Breakout on close above/below line
|
||||
BREAKOUT_CANDLE = 1 // Breakout on entire candle above/below line
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| Swing point structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct Swing { //--- Define swing point structure
|
||||
datetime time; //--- Store swing time
|
||||
double price; //--- Store swing price
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| Starting point structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct StartingPoint { //--- Define starting point structure
|
||||
datetime time; //--- Store starting point time
|
||||
double price; //--- Store starting point price
|
||||
bool is_support; //--- Indicate support/resistance flag
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trendline storage structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct TrendlineInfo { //--- Define trendline info structure
|
||||
string name; //--- Store trendline name
|
||||
datetime start_time; //--- Store start time
|
||||
datetime end_time; //--- Store end time
|
||||
double start_price; //--- Store start price
|
||||
double end_price; //--- Store end price
|
||||
double slope; //--- Store slope
|
||||
bool is_support; //--- Indicate support/resistance flag
|
||||
int touch_count; //--- Store number of touches
|
||||
datetime creation_time; //--- Store creation time
|
||||
int touch_indices[]; //--- Store touch indices array
|
||||
bool is_signaled; //--- Indicate signal flag
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
//| Forward declarations |
|
||||
//+------------------------------------------------------------------+
|
||||
void DetectSwings(); //--- Declare swing detection function
|
||||
void SortSwings(Swing &swings[], int count); //--- Declare swing sorting function
|
||||
double CalculateAngle(datetime time1, double price1, datetime time2, double price2); //--- Declare angle calculation function
|
||||
bool ValidateTrendline(bool isSupport, datetime start_time, datetime ref_time, double ref_price, double slope, double tolerance_pen); //--- Declare trendline validation function
|
||||
void FindAndDrawTrendlines(bool isSupport); //--- Declare trendline finding/drawing function
|
||||
void UpdateTrendlines(); //--- Declare trendline update function
|
||||
void RemoveTrendlineFromStorage(int index); //--- Declare trendline removal function
|
||||
bool IsStartingPointUsed(datetime time, double price, bool is_support); //--- Declare starting point usage check function
|
||||
double CalculateRSquared(const datetime ×[], const double &prices[], int n, double slope, double intercept); //--- Declare R-squared calculation function
|
||||
//+------------------------------------------------------------------+
|
||||
//| Inputs |
|
||||
//+------------------------------------------------------------------+
|
||||
input ENUM_BREAKOUT_TYPE BreakoutType = BREAKOUT_CLOSE; // Breakout Definition
|
||||
input int LookbackBars = 200; // Set bars for swing detection lookback
|
||||
input double TouchTolerance = 10.0; // Set tolerance for touch points (points)
|
||||
input int MinTouches = 3; // Set minimum touch points for valid trendline
|
||||
input double PenetrationTolerance = 5.0; // Set allowance for bar penetration (points)
|
||||
input int ExtensionBars = 100; // Set bars to extend trendline right
|
||||
input int MinBarSpacing = 10; // Set minimum bar spacing between touches
|
||||
input double inpLot = 0.01; // Set lot size
|
||||
input double inpSLPoints = 100.0; // Set stop loss (points)
|
||||
input double inpRRRatio = 1.1; // Set risk:reward ratio
|
||||
input double MinAngle = 1.0; // Set minimum inclination angle (degrees)
|
||||
input double MaxAngle = 89.0; // Set maximum inclination angle (degrees)
|
||||
input double MinRSquared = 0.8; // Minimum R-squared for trendline acceptance
|
||||
input bool DeleteExpiredObjects = false; // Enable deletion of expired/broken objects
|
||||
input bool EnableTradingSignals = true; // Enable buy/sell signals and trades
|
||||
input bool DrawTouchArrows = true; // Enable drawing arrows at touch points
|
||||
input bool DrawLabels = true; // Enable drawing trendline/point labels
|
||||
input color SupportLineColor = clrGreen; // Set color for support trendlines
|
||||
input color ResistanceLineColor = clrRed; // Set color for resistance trendlines
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global variables |
|
||||
//+------------------------------------------------------------------+
|
||||
Swing swingLows[]; //--- Store swing lows
|
||||
int numLows = 0; //--- Track number of swing lows
|
||||
Swing swingHighs[]; //--- Store swing highs
|
||||
int numHighs = 0; //--- Track number of swing highs
|
||||
TrendlineInfo trendlines[]; //--- Store trendlines
|
||||
int numTrendlines = 0; //--- Track number of trendlines
|
||||
StartingPoint startingPoints[]; //--- Store used starting points
|
||||
int numStartingPoints = 0; //--- Track number of starting points
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit() {
|
||||
ArrayResize(trendlines, 0); //--- Resize trendlines array
|
||||
numTrendlines = 0; //--- Reset trendlines count
|
||||
ArrayResize(startingPoints, 0); //--- Resize starting points array
|
||||
numStartingPoints = 0; //--- Reset starting points count
|
||||
return(INIT_SUCCEEDED); //--- Return success
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason) {
|
||||
ArrayResize(trendlines, 0); //--- Resize trendlines array
|
||||
numTrendlines = 0; //--- Reset trendlines count
|
||||
ArrayResize(startingPoints, 0); //--- Resize starting points array
|
||||
numStartingPoints = 0; //--- Reset starting points count
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick() {
|
||||
if (!IsNewBar()) return; //--- Exit if not new bar
|
||||
DetectSwings(); //--- Detect swings
|
||||
UpdateTrendlines(); //--- Update trendlines
|
||||
FindAndDrawTrendlines(true); //--- Find/draw support trendlines
|
||||
FindAndDrawTrendlines(false); //--- Find/draw resistance trendlines
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for new bar |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsNewBar() {
|
||||
static datetime lastTime = 0; //--- Store last bar time
|
||||
datetime currentTime = iTime(_Symbol, _Period, 0); //--- Get current bar time
|
||||
if (lastTime != currentTime) { //--- Check for new bar
|
||||
lastTime = currentTime; //--- Update last time
|
||||
return true; //--- Indicate new bar
|
||||
}
|
||||
return false; //--- Indicate no new bar
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Sort swings by time (ascending, oldest first) |
|
||||
//+------------------------------------------------------------------+
|
||||
void SortSwings(Swing &swings[], int count) {
|
||||
for (int i = 0; i < count - 1; i++) { //--- Iterate through swings
|
||||
for (int j = 0; j < count - i - 1; j++) { //--- Compare adjacent swings
|
||||
if (swings[j].time > swings[j + 1].time) { //--- Check time order
|
||||
Swing temp = swings[j]; //--- Store temporary swing
|
||||
swings[j] = swings[j + 1]; //--- Swap swings
|
||||
swings[j + 1] = temp; //--- Complete swap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Detect swing highs and lows |
|
||||
//+------------------------------------------------------------------+
|
||||
void DetectSwings() {
|
||||
numLows = 0; //--- Reset lows count
|
||||
ArrayResize(swingLows, 0); //--- Resize lows array
|
||||
numHighs = 0; //--- Reset highs count
|
||||
ArrayResize(swingHighs, 0); //--- Resize highs array
|
||||
int totalBars = iBars(_Symbol, _Period); //--- Get total bars
|
||||
int effectiveLookback = MathMin(LookbackBars, totalBars); //--- Calculate effective lookback
|
||||
if (effectiveLookback < 5) { //--- Check sufficient bars
|
||||
Print("Not enough bars for swing detection."); //--- Log insufficient bars
|
||||
return; //--- Exit function
|
||||
}
|
||||
for (int i = 2; i < effectiveLookback - 2; i++) { //--- Iterate through bars
|
||||
double low_i = iLow(_Symbol, _Period, i); //--- Get current low
|
||||
double low_im1 = iLow(_Symbol, _Period, i - 1); //--- Get previous low
|
||||
double low_im2 = iLow(_Symbol, _Period, i - 2); //--- Get two bars prior low
|
||||
double low_ip1 = iLow(_Symbol, _Period, i + 1); //--- Get next low
|
||||
double low_ip2 = iLow(_Symbol, _Period, i + 2); //--- Get two bars next low
|
||||
if (low_i < low_im1 && low_i < low_im2 && low_i < low_ip1 && low_i < low_ip2) { //--- Check for swing low
|
||||
Swing s; //--- Create swing struct
|
||||
s.time = iTime(_Symbol, _Period, i); //--- Set swing time
|
||||
s.price = low_i; //--- Set swing price
|
||||
ArrayResize(swingLows, numLows + 1); //--- Resize lows array
|
||||
swingLows[numLows] = s; //--- Add swing low
|
||||
numLows++; //--- Increment lows count
|
||||
}
|
||||
double high_i = iHigh(_Symbol, _Period, i); //--- Get current high
|
||||
double high_im1 = iHigh(_Symbol, _Period, i - 1); //--- Get previous high
|
||||
double high_im2 = iHigh(_Symbol, _Period, i - 2); //--- Get two bars prior high
|
||||
double high_ip1 = iHigh(_Symbol, _Period, i + 1); //--- Get next high
|
||||
double high_ip2 = iHigh(_Symbol, _Period, i + 2); //--- Get two bars next high
|
||||
if (high_i > high_im1 && high_i > high_im2 && high_i > high_ip1 && high_i > high_ip2) { //--- Check for swing high
|
||||
Swing s; //--- Create swing struct
|
||||
s.time = iTime(_Symbol, _Period, i); //--- Set swing time
|
||||
s.price = high_i; //--- Set swing price
|
||||
ArrayResize(swingHighs, numHighs + 1); //--- Resize highs array
|
||||
swingHighs[numHighs] = s; //--- Add swing high
|
||||
numHighs++; //--- Increment highs count
|
||||
}
|
||||
}
|
||||
if (numLows > 0) SortSwings(swingLows, numLows); //--- Sort swing lows
|
||||
if (numHighs > 0) SortSwings(swingHighs, numHighs); //--- Sort swing highs
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate visual inclination angle |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateAngle(datetime time1, double price1, datetime time2, double price2) {
|
||||
int x1, y1, x2, y2; //--- Declare coordinate variables
|
||||
if (!ChartTimePriceToXY(0, 0, time1, price1, x1, y1)) return 0.0; //--- Convert time1/price1 to XY
|
||||
if (!ChartTimePriceToXY(0, 0, time2, price2, x2, y2)) return 0.0; //--- Convert time2/price2 to XY
|
||||
double dx = (double)(x2 - x1); //--- Calculate x difference
|
||||
double dy = (double)(y2 - y1); //--- Calculate y difference
|
||||
if (dx == 0.0) return (dy > 0.0 ? -90.0 : 90.0); //--- Handle vertical line case
|
||||
double angle = MathArctan(-dy / dx) * 180.0 / M_PI; //--- Calculate angle in degrees
|
||||
return angle; //--- Return angle
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Validate trendline |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ValidateTrendline(bool isSupport, datetime start_time, datetime ref_time, double ref_price, double slope, double tolerance_pen) {
|
||||
int bar_start = iBarShift(_Symbol, _Period, start_time); //--- Get start bar index
|
||||
if (bar_start < 0) return false; //--- Check invalid bar index
|
||||
for (int bar = bar_start; bar >= 0; bar--) { //--- Iterate through bars
|
||||
datetime bar_time = iTime(_Symbol, _Period, bar); //--- Get bar time
|
||||
double dk = (double)(bar_time - ref_time); //--- Calculate time difference
|
||||
double line_price = ref_price + slope * dk; //--- Calculate line price
|
||||
if (isSupport) { //--- Check support case
|
||||
double low = iLow(_Symbol, _Period, bar); //--- Get bar low
|
||||
if (low < line_price - tolerance_pen) return false;//--- Check if broken
|
||||
} else { //--- Handle resistance case
|
||||
double high = iHigh(_Symbol, _Period, bar); //--- Get bar high
|
||||
if (high > line_price + tolerance_pen) return false;//--- Check if broken
|
||||
}
|
||||
}
|
||||
return true; //--- Return valid
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate R-squared for goodness of fit |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateRSquared(const datetime ×[], const double &prices[], int n, double slope, double intercept) {
|
||||
double sum_y = 0.0; //--- Initialize sum of y
|
||||
for (int k = 0; k < n; k++) { //--- Iterate through points
|
||||
sum_y += prices[k]; //--- Accumulate y
|
||||
}
|
||||
double mean_y = sum_y / n; //--- Calculate mean y
|
||||
double ss_tot = 0.0, ss_res = 0.0; //--- Initialize sums of squares
|
||||
for (int k = 0; k < n; k++) { //--- Iterate through points
|
||||
double x = (double)times[k]; //--- Get x (time)
|
||||
double y_pred = intercept + slope * x; //--- Calculate predicted y
|
||||
double y = prices[k]; //--- Get actual y
|
||||
ss_res += (y - y_pred) * (y - y_pred); //--- Accumulate residual sum
|
||||
ss_tot += (y - mean_y) * (y - mean_y); //--- Accumulate total sum
|
||||
}
|
||||
if (ss_tot == 0.0) return 1.0; //--- Handle constant y case
|
||||
return 1.0 - ss_res / ss_tot; //--- Calculate and return R-squared
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if starting point is already used |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsStartingPointUsed(datetime time, double price, bool is_support) {
|
||||
for (int i = 0; i < numStartingPoints; i++) { //--- Iterate through starting points
|
||||
if (startingPoints[i].time == time && MathAbs(startingPoints[i].price - price) < TouchTolerance * _Point && startingPoints[i].is_support == is_support) { //--- Check match
|
||||
return true; //--- Return used
|
||||
}
|
||||
}
|
||||
return false; //--- Return not used
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Remove trendline from storage and optionally chart objects |
|
||||
//+------------------------------------------------------------------+
|
||||
void RemoveTrendlineFromStorage(int index) {
|
||||
if (index < 0 || index >= numTrendlines) return; //--- Check valid index
|
||||
Print("Removing trendline from storage: ", trendlines[index].name); //--- Log removal
|
||||
if (DeleteExpiredObjects) { //--- Check deletion flag
|
||||
ObjectDelete(0, trendlines[index].name); //--- Delete trendline object
|
||||
for (int m = 0; m < trendlines[index].touch_count; m++) { //--- Iterate touches
|
||||
string arrow_name = trendlines[index].name + "_touch" + IntegerToString(m); //--- Generate arrow name
|
||||
ObjectDelete(0, arrow_name); //--- Delete touch arrow
|
||||
string text_name = trendlines[index].name + "_point_label" + IntegerToString(m); //--- Generate text name
|
||||
ObjectDelete(0, text_name); //--- Delete point label
|
||||
}
|
||||
string label_name = trendlines[index].name + "_label"; //--- Generate label name
|
||||
ObjectDelete(0, label_name); //--- Delete trendline label
|
||||
string signal_arrow = trendlines[index].name + "_signal_arrow"; //--- Generate signal arrow name
|
||||
ObjectDelete(0, signal_arrow); //--- Delete signal arrow
|
||||
string signal_text = trendlines[index].name + "_signal_text"; //--- Generate signal text name
|
||||
ObjectDelete(0, signal_text); //--- Delete signal text
|
||||
}
|
||||
for (int i = index; i < numTrendlines - 1; i++) { //--- Shift array
|
||||
trendlines[i] = trendlines[i + 1]; //--- Copy next trendline
|
||||
}
|
||||
ArrayResize(trendlines, numTrendlines - 1); //--- Resize trendlines array
|
||||
numTrendlines--; //--- Decrement trendlines count
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update trendlines and check for signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateTrendlines() {
|
||||
datetime current_time = iTime(_Symbol, _Period, 0); //--- Get current time
|
||||
double pointValue = _Point; //--- Get point value
|
||||
double pen_tolerance = PenetrationTolerance * pointValue; //--- Calculate penetration tolerance
|
||||
double touch_tolerance = TouchTolerance * pointValue; //--- Calculate touch tolerance
|
||||
for (int i = numTrendlines - 1; i >= 0; i--) { //--- Iterate trendlines backward
|
||||
string type = trendlines[i].is_support ? "Support" : "Resistance"; //--- Determine trendline type
|
||||
string name = trendlines[i].name; //--- Get trendline name
|
||||
if (current_time > trendlines[i].end_time) { //--- Check if expired
|
||||
PrintFormat("%s trendline %s is no longer valid (expired). End time: %s, Current time: %s.", type, name, TimeToString(trendlines[i].end_time), TimeToString(current_time)); //--- Log expiration
|
||||
RemoveTrendlineFromStorage(i); //--- Remove trendline
|
||||
continue; //--- Skip to next
|
||||
}
|
||||
datetime prev_bar_time = iTime(_Symbol, _Period, 1); //--- Get previous bar time
|
||||
double dk = (double)(prev_bar_time - trendlines[i].start_time); //--- Calculate time difference
|
||||
double line_price = trendlines[i].start_price + trendlines[i].slope * dk; //--- Calculate line price
|
||||
double prev_close = iClose(_Symbol, _Period, 1); //--- Get previous bar close
|
||||
double prev_low = iLow(_Symbol, _Period, 1); //--- Get previous bar low
|
||||
double prev_high = iHigh(_Symbol, _Period, 1); //--- Get previous bar high
|
||||
bool broken = false; //--- Initialize broken flag
|
||||
if (BreakoutType == BREAKOUT_CLOSE) { //--- Check breakout on close
|
||||
if (trendlines[i].is_support && prev_close < line_price) { //--- Support break by close
|
||||
PrintFormat("%s trendline %s is no longer valid (broken by close). Line price: %.5f, Prev close: %.5f.", type, name, line_price, prev_close); //--- Log break
|
||||
broken = true; //--- Set broken flag
|
||||
} else if (!trendlines[i].is_support && prev_close > line_price) { //--- Resistance break by close
|
||||
PrintFormat("%s trendline %s is no longer valid (broken by close). Line price: %.5f, Prev close: %.5f.", type, name, line_price, prev_close); //--- Log break
|
||||
broken = true; //--- Set broken flag
|
||||
}
|
||||
} else if (BreakoutType == BREAKOUT_CANDLE) { //--- Check breakout on entire candle
|
||||
if (trendlines[i].is_support && prev_high < line_price) { //--- Entire candle below support
|
||||
PrintFormat("%s trendline %s is no longer valid (entire candle below). Line price: %.5f, Prev high: %.5f.", type, name, line_price, prev_high); //--- Log break
|
||||
broken = true; //--- Set broken flag
|
||||
} else if (!trendlines[i].is_support && prev_low > line_price) { //--- Entire candle above resistance
|
||||
PrintFormat("%s trendline %s is no longer valid (entire candle above). Line price: %.5f, Prev low: %.5f.", type, name, line_price, prev_low); //--- Log break
|
||||
broken = true; //--- Set broken flag
|
||||
}
|
||||
}
|
||||
if (broken && EnableTradingSignals && !trendlines[i].is_signaled) { //--- Check for breakout signal
|
||||
bool signaled = false; //--- Initialize signaled flag
|
||||
string signal_type = ""; //--- Initialize signal type
|
||||
color signal_color = clrNONE; //--- Initialize signal color
|
||||
int arrow_code = 0; //--- Initialize arrow code
|
||||
int anchor = 0; //--- Initialize anchor
|
||||
double text_angle = 0.0; //--- Initialize text angle
|
||||
double text_offset = 0.0; //--- Initialize text offset
|
||||
double text_price = 0.0; //--- Initialize text price
|
||||
int text_anchor = 0; //--- Initialize text anchor
|
||||
if (trendlines[i].is_support) { //--- Support break: SELL
|
||||
signaled = true; //--- Set signaled flag
|
||||
signal_type = "SELL BREAK"; //--- Set sell break signal
|
||||
signal_color = clrRed; //--- Set red color
|
||||
arrow_code = 218; //--- Set down arrow
|
||||
anchor = ANCHOR_BOTTOM; //--- Set bottom anchor
|
||||
text_angle = 90.0; //--- Set vertical downward
|
||||
text_offset = 20 * pointValue; //--- Set text offset
|
||||
text_price = line_price + text_offset; //--- Calculate text price
|
||||
text_anchor = ANCHOR_BOTTOM; //--- Set bottom anchor
|
||||
double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits); //--- Get bid price
|
||||
double SL = NormalizeDouble(line_price + inpSLPoints * _Point, _Digits); //--- SL above the line
|
||||
double risk = SL - Bid; //--- Calculate risk
|
||||
double TP = NormalizeDouble(Bid - risk * inpRRRatio, _Digits); //--- Calculate take profit
|
||||
obj_Trade.Sell(inpLot, _Symbol, Bid, SL, TP); //--- Execute sell trade
|
||||
} else { //--- Resistance break: BUY
|
||||
signaled = true; //--- Set signaled flag
|
||||
signal_type = "BUY BREAK"; //--- Set buy break signal
|
||||
signal_color = clrBlue; //--- Set blue color
|
||||
arrow_code = 217; //--- Set up arrow
|
||||
anchor = ANCHOR_TOP; //--- Set top anchor
|
||||
text_angle = -90.0; //--- Set vertical upward
|
||||
text_offset = -20 * pointValue; //--- Set text offset
|
||||
text_price = line_price + text_offset; //--- Calculate text price
|
||||
text_anchor = ANCHOR_LEFT; //--- Set left anchor
|
||||
double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits); //--- Get ask price
|
||||
double SL = NormalizeDouble(line_price - inpSLPoints * _Point, _Digits); //--- SL below the line
|
||||
double risk = Ask - SL; //--- Calculate risk
|
||||
double TP = NormalizeDouble(Ask + risk * inpRRRatio, _Digits); //--- Calculate take profit
|
||||
obj_Trade.Buy(inpLot, _Symbol, Ask, SL, TP); //--- Execute buy trade
|
||||
}
|
||||
if (signaled) { //--- Check if signaled
|
||||
PrintFormat("Breakout signal generated for %s trendline %s: %s at price %.5f, time %s.", type, name, signal_type, line_price, TimeToString(current_time)); //--- Log signal
|
||||
string arrow_name = name + "_signal_arrow"; //--- Generate signal arrow name
|
||||
if (ObjectFind(0, arrow_name) < 0) { //--- Check if arrow exists
|
||||
ObjectCreate(0, arrow_name, OBJ_ARROW, 0, prev_bar_time, line_price); //--- Create signal arrow
|
||||
ObjectSetInteger(0, arrow_name, OBJPROP_ARROWCODE, arrow_code); //--- Set arrow code
|
||||
ObjectSetInteger(0, arrow_name, OBJPROP_ANCHOR, anchor); //--- Set anchor
|
||||
ObjectSetInteger(0, arrow_name, OBJPROP_COLOR, signal_color); //--- Set color
|
||||
ObjectSetInteger(0, arrow_name, OBJPROP_WIDTH, 1); //--- Set width
|
||||
ObjectSetInteger(0, arrow_name, OBJPROP_BACK, false); //--- Set to foreground
|
||||
}
|
||||
string text_name = name + "_signal_text"; //--- Generate signal text name
|
||||
if (ObjectFind(0, text_name) < 0) { //--- Check if text exists
|
||||
ObjectCreate(0, text_name, OBJ_TEXT, 0, prev_bar_time, text_price); //--- Create signal text
|
||||
ObjectSetString(0, text_name, OBJPROP_TEXT, " " + signal_type); //--- Set text content
|
||||
ObjectSetInteger(0, text_name, OBJPROP_COLOR, signal_color); //--- Set color
|
||||
ObjectSetInteger(0, text_name, OBJPROP_FONTSIZE, 10); //--- Set font size
|
||||
ObjectSetInteger(0, text_name, OBJPROP_ANCHOR, text_anchor); //--- Set anchor
|
||||
ObjectSetDouble(0, text_name, OBJPROP_ANGLE, text_angle); //--- Set angle
|
||||
ObjectSetInteger(0, text_name, OBJPROP_BACK, false); //--- Set to foreground
|
||||
}
|
||||
trendlines[i].is_signaled = true; //--- Set signaled flag
|
||||
}
|
||||
}
|
||||
if (broken) { //--- Remove if broken
|
||||
RemoveTrendlineFromStorage(i); //--- Remove trendline
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Find and draw trendlines if no active one exists |
|
||||
//+------------------------------------------------------------------+
|
||||
void FindAndDrawTrendlines(bool isSupport) {
|
||||
bool has_active = false; //--- Initialize active flag
|
||||
for (int i = 0; i < numTrendlines; i++) { //--- Iterate through trendlines
|
||||
if (trendlines[i].is_support == isSupport) { //--- Check type match
|
||||
has_active = true; //--- Set active flag
|
||||
break; //--- Exit loop
|
||||
}
|
||||
}
|
||||
if (has_active) return; //--- Exit if active trendline exists
|
||||
Swing swings[]; //--- Initialize swings array
|
||||
int numSwings; //--- Initialize swings count
|
||||
color lineColor; //--- Initialize line color
|
||||
string prefix; //--- Initialize prefix
|
||||
if (isSupport) { //--- Handle support case
|
||||
numSwings = numLows; //--- Set number of lows
|
||||
ArrayResize(swings, numSwings); //--- Resize swings array
|
||||
for (int i = 0; i < numSwings; i++) { //--- Iterate through lows
|
||||
swings[i].time = swingLows[i].time; //--- Copy low time
|
||||
swings[i].price = swingLows[i].price; //--- Copy low price
|
||||
}
|
||||
lineColor = SupportLineColor; //--- Set support line color
|
||||
prefix = "Trendline_Support_"; //--- Set support prefix
|
||||
} else { //--- Handle resistance case
|
||||
numSwings = numHighs; //--- Set number of highs
|
||||
ArrayResize(swings, numSwings); //--- Resize swings array
|
||||
for (int i = 0; i < numSwings; i++) { //--- Iterate through highs
|
||||
swings[i].time = swingHighs[i].time; //--- Copy high time
|
||||
swings[i].price = swingHighs[i].price; //--- Copy high price
|
||||
}
|
||||
lineColor = ResistanceLineColor; //--- Set resistance line color
|
||||
prefix = "Trendline_Resistance_"; //--- Set resistance prefix
|
||||
}
|
||||
if (numSwings < 2) return; //--- Exit if insufficient swings
|
||||
double pointValue = _Point; //--- Get point value
|
||||
double touch_tolerance = TouchTolerance * pointValue; //--- Calculate touch tolerance
|
||||
double pen_tolerance = PenetrationTolerance * pointValue; //--- Calculate penetration tolerance
|
||||
int best_j = -1; //--- Initialize best j index
|
||||
int max_touches = 0; //--- Initialize max touches
|
||||
double best_rsquared = -1.0; //--- Initialize best R-squared
|
||||
int best_touch_indices[]; //--- Initialize best touch indices
|
||||
double best_slope = 0.0; //--- Initialize best slope
|
||||
double best_intercept = 0.0; //--- Initialize best intercept
|
||||
datetime best_min_time = 0; //--- Initialize best min time
|
||||
for (int i = 0; i < numSwings - 1; i++) { //--- Iterate through first points
|
||||
for (int j = i + 1; j < numSwings; j++) { //--- Iterate through second points
|
||||
datetime time1 = swings[i].time; //--- Get first time
|
||||
double price1 = swings[i].price; //--- Get first price
|
||||
datetime time2 = swings[j].time; //--- Get second time
|
||||
double price2 = swings[j].price; //--- Get second price
|
||||
double dt = (double)(time2 - time1); //--- Calculate time difference
|
||||
if (dt <= 0) continue; //--- Skip invalid time difference
|
||||
double initial_slope = (price2 - price1) / dt; //--- Calculate initial slope
|
||||
int touch_indices[]; //--- Initialize touch indices
|
||||
ArrayResize(touch_indices, 0); //--- Resize touch indices
|
||||
int touches = 0; //--- Initialize touches count
|
||||
ArrayResize(touch_indices, touches + 1); //--- Add first index
|
||||
touch_indices[touches] = i; //--- Set first index
|
||||
touches++; //--- Increment touches
|
||||
ArrayResize(touch_indices, touches + 1); //--- Add second index
|
||||
touch_indices[touches] = j; //--- Set second index
|
||||
touches++; //--- Increment touches
|
||||
for (int k = 0; k < numSwings; k++) { //--- Iterate through swings
|
||||
if (k == i || k == j) continue; //--- Skip used indices
|
||||
datetime tk = swings[k].time; //--- Get swing time
|
||||
double dk = (double)(tk - time1); //--- Calculate time difference
|
||||
double expected = price1 + initial_slope * dk; //--- Calculate expected price
|
||||
double actual = swings[k].price; //--- Get actual price
|
||||
if (MathAbs(expected - actual) <= touch_tolerance) { //--- Check touch within tolerance
|
||||
ArrayResize(touch_indices, touches + 1); //--- Add index
|
||||
touch_indices[touches] = k; //--- Set index
|
||||
touches++; //--- Increment touches
|
||||
}
|
||||
}
|
||||
if (touches >= MinTouches) { //--- Check minimum touches
|
||||
ArraySort(touch_indices); //--- Sort touch indices
|
||||
bool valid_spacing = true; //--- Initialize spacing flag
|
||||
for (int m = 0; m < touches - 1; m++) { //--- Iterate through touches
|
||||
int idx1 = touch_indices[m]; //--- Get first index
|
||||
int idx2 = touch_indices[m + 1]; //--- Get second index
|
||||
int bar1 = iBarShift(_Symbol, _Period, swings[idx1].time); //--- Get first bar
|
||||
int bar2 = iBarShift(_Symbol, _Period, swings[idx2].time); //--- Get second bar
|
||||
int diff = MathAbs(bar1 - bar2); //--- Calculate bar difference
|
||||
if (diff < MinBarSpacing) { //--- Check minimum spacing
|
||||
valid_spacing = false; //--- Mark invalid spacing
|
||||
break; //--- Exit loop
|
||||
}
|
||||
}
|
||||
if (valid_spacing) { //--- Check valid spacing
|
||||
datetime touch_times[]; //--- Initialize touch times
|
||||
double touch_prices[]; //--- Initialize touch prices
|
||||
ArrayResize(touch_times, touches); //--- Resize times array
|
||||
ArrayResize(touch_prices, touches); //--- Resize prices array
|
||||
for (int m = 0; m < touches; m++) { //--- Iterate through touches
|
||||
int idx = touch_indices[m]; //--- Get index
|
||||
touch_times[m] = swings[idx].time; //--- Set time
|
||||
touch_prices[m] = swings[idx].price; //--- Set price
|
||||
}
|
||||
double slope = initial_slope; //--- Use initial slope from two points
|
||||
double intercept = price1 - slope * (double)time1; //--- Calculate intercept
|
||||
double rsquared = CalculateRSquared(touch_times, touch_prices, touches, slope, intercept); //--- Calculate R-squared
|
||||
if (rsquared >= MinRSquared) { //--- Check minimum R-squared
|
||||
int adjusted_touch_indices[]; //--- Initialize adjusted indices
|
||||
ArrayResize(adjusted_touch_indices, touches); //--- Resize to current touches
|
||||
ArrayCopy(adjusted_touch_indices, touch_indices); //--- Copy indices
|
||||
int adjusted_touches = touches; //--- Set adjusted touches
|
||||
if (adjusted_touches >= MinTouches) { //--- Check minimum adjusted touches
|
||||
datetime temp_min_time = swings[adjusted_touch_indices[0]].time; //--- Get min time
|
||||
double temp_ref_price = intercept + slope * (double)temp_min_time; //--- Calculate ref price
|
||||
if (ValidateTrendline(isSupport, temp_min_time, temp_min_time, temp_ref_price, slope, pen_tolerance)) { //--- Validate trendline
|
||||
datetime temp_max_time = swings[adjusted_touch_indices[adjusted_touches - 1]].time; //--- Get max time
|
||||
double temp_max_price = intercept + slope * (double)temp_max_time; //--- Calculate max price
|
||||
double angle = CalculateAngle(temp_min_time, temp_ref_price, temp_max_time, temp_max_price); //--- Calculate angle
|
||||
double abs_angle = MathAbs(angle); //--- Get absolute angle
|
||||
if (abs_angle >= MinAngle && abs_angle <= MaxAngle) { //--- Check angle range
|
||||
if (adjusted_touches > max_touches || (adjusted_touches == max_touches && rsquared > best_rsquared)) { //--- Check better trendline
|
||||
max_touches = adjusted_touches; //--- Update max touches
|
||||
best_rsquared = rsquared; //--- Update best R-squared
|
||||
best_j = j; //--- Update best j
|
||||
best_slope = slope; //--- Update best slope
|
||||
best_intercept = intercept; //--- Update best intercept
|
||||
best_min_time = temp_min_time; //--- Update best min time
|
||||
ArrayResize(best_touch_indices, adjusted_touches); //--- Resize best indices
|
||||
ArrayCopy(best_touch_indices, adjusted_touch_indices); //--- Copy indices
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (max_touches < MinTouches) { //--- Check insufficient touches
|
||||
string type = isSupport ? "Support" : "Resistance"; //--- Set type string
|
||||
return; //--- Exit function
|
||||
}
|
||||
int touch_indices[]; //--- Initialize touch indices
|
||||
ArrayResize(touch_indices, max_touches); //--- Resize touch indices
|
||||
ArrayCopy(touch_indices, best_touch_indices); //--- Copy best indices
|
||||
int touches = max_touches; //--- Set touches count
|
||||
datetime min_time = best_min_time; //--- Set min time
|
||||
double price_min = best_intercept + best_slope * (double)min_time; //--- Calculate min price
|
||||
datetime max_time = swings[touch_indices[touches - 1]].time; //--- Set max time
|
||||
double price_max = best_intercept + best_slope * (double)max_time; //--- Calculate max price
|
||||
datetime start_time_check = min_time; //--- Set start time check
|
||||
double start_price_check = price_min; //--- Set start price check (approximate if not exact)
|
||||
if (IsStartingPointUsed(start_time_check, start_price_check, isSupport)) { //--- Check used starting point
|
||||
return; //--- Skip if used
|
||||
}
|
||||
datetime time_end = iTime(_Symbol, _Period, 0) + PeriodSeconds(_Period) * ExtensionBars; //--- Calculate end time
|
||||
double dk_end = (double)(time_end - min_time); //--- Calculate end time difference
|
||||
double price_end = price_min + best_slope * dk_end; //--- Calculate end price
|
||||
string unique_name = prefix + TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES|TIME_SECONDS); //--- Generate unique name
|
||||
if (ObjectFind(0, unique_name) < 0) { //--- Check if trendline exists
|
||||
ObjectCreate(0, unique_name, OBJ_TREND, 0, min_time, price_min, time_end, price_end); //--- Create trendline
|
||||
ObjectSetInteger(0, unique_name, OBJPROP_COLOR, lineColor); //--- Set color
|
||||
ObjectSetInteger(0, unique_name, OBJPROP_STYLE, STYLE_SOLID); //--- Set style
|
||||
ObjectSetInteger(0, unique_name, OBJPROP_WIDTH, 1); //--- Set width
|
||||
ObjectSetInteger(0, unique_name, OBJPROP_RAY_RIGHT, false); //--- Disable right ray
|
||||
ObjectSetInteger(0, unique_name, OBJPROP_RAY_LEFT, false); //--- Disable left ray
|
||||
ObjectSetInteger(0, unique_name, OBJPROP_BACK, false); //--- Set to foreground
|
||||
}
|
||||
ArrayResize(trendlines, numTrendlines + 1); //--- Resize trendlines array
|
||||
trendlines[numTrendlines].name = unique_name; //--- Set trendline name
|
||||
trendlines[numTrendlines].start_time = min_time; //--- Set start time
|
||||
trendlines[numTrendlines].end_time = time_end; //--- Set end time
|
||||
trendlines[numTrendlines].start_price = price_min; //--- Set start price
|
||||
trendlines[numTrendlines].end_price = price_end; //--- Set end price
|
||||
trendlines[numTrendlines].slope = best_slope; //--- Set slope
|
||||
trendlines[numTrendlines].is_support = isSupport; //--- Set type
|
||||
trendlines[numTrendlines].touch_count = touches; //--- Set touch count
|
||||
trendlines[numTrendlines].creation_time = TimeCurrent(); //--- Set creation time
|
||||
trendlines[numTrendlines].is_signaled = false; //--- Set signaled flag
|
||||
ArrayResize(trendlines[numTrendlines].touch_indices, touches); //--- Resize touch indices
|
||||
ArrayCopy(trendlines[numTrendlines].touch_indices, touch_indices); //--- Copy touch indices
|
||||
numTrendlines++; //--- Increment trendlines count
|
||||
ArrayResize(startingPoints, numStartingPoints + 1); //--- Resize starting points array
|
||||
startingPoints[numStartingPoints].time = start_time_check;//--- Set starting point time
|
||||
startingPoints[numStartingPoints].price = start_price_check; //--- Set starting point price
|
||||
startingPoints[numStartingPoints].is_support = isSupport; //--- Set starting point type
|
||||
numStartingPoints++; //--- Increment starting points count
|
||||
if (DrawTouchArrows) { //--- Check draw arrows
|
||||
for (int m = 0; m < touches; m++) { //--- Iterate through touches
|
||||
int idx = touch_indices[m]; //--- Get touch index
|
||||
datetime tk_time = swings[idx].time; //--- Get touch time
|
||||
double tk_price = swings[idx].price; //--- Get touch price
|
||||
string arrow_name = unique_name + "_touch" + IntegerToString(m); //--- Generate arrow name
|
||||
if (ObjectFind(0, arrow_name) < 0) { //--- Check if arrow exists
|
||||
ObjectCreate(0, arrow_name, OBJ_ARROW, 0, tk_time, tk_price); //--- Create touch arrow
|
||||
ObjectSetInteger(0, arrow_name, OBJPROP_ARROWCODE, 159); //--- Set arrow code
|
||||
ObjectSetInteger(0, arrow_name, OBJPROP_ANCHOR, isSupport ? ANCHOR_TOP : ANCHOR_BOTTOM); //--- Set anchor
|
||||
ObjectSetInteger(0, arrow_name, OBJPROP_COLOR, lineColor); //--- Set color
|
||||
ObjectSetInteger(0, arrow_name, OBJPROP_WIDTH, 1); //--- Set width
|
||||
ObjectSetInteger(0, arrow_name, OBJPROP_BACK, false); //--- Set to foreground
|
||||
}
|
||||
}
|
||||
}
|
||||
double angle = CalculateAngle(min_time, price_min, max_time, price_max); //--- Calculate angle
|
||||
string type = isSupport ? "Support" : "Resistance"; //--- Set type string
|
||||
Print(type + " Trendline " + unique_name + " drawn with " + IntegerToString(touches) + " touches. Inclination angle: " + DoubleToString(angle, 2) + " degrees."); //--- Log trendline
|
||||
if (DrawLabels) { //--- Check draw labels
|
||||
datetime mid_time = min_time + (max_time - min_time) / 2; //--- Calculate mid time
|
||||
double dk_mid = (double)(mid_time - min_time); //--- Calculate mid time difference
|
||||
double mid_price = price_min + best_slope * dk_mid; //--- Calculate mid price
|
||||
double label_offset = 20 * _Point * (isSupport ? -1 : 1); //--- Calculate label offset
|
||||
double label_price = mid_price + label_offset; //--- Calculate label price
|
||||
int label_anchor = isSupport ? ANCHOR_TOP : ANCHOR_BOTTOM;//--- Set label anchor
|
||||
string label_text = type + " Trendline"; //--- Set label text
|
||||
string label_name = unique_name + "_label"; //--- Generate label name
|
||||
if (ObjectFind(0, label_name) < 0) { //--- Check if label exists
|
||||
ObjectCreate(0, label_name, OBJ_TEXT, 0, mid_time, label_price); //--- Create label
|
||||
ObjectSetString(0, label_name, OBJPROP_TEXT, label_text); //--- Set text
|
||||
ObjectSetInteger(0, label_name, OBJPROP_COLOR, clrBlack); //--- Set color
|
||||
ObjectSetInteger(0, label_name, OBJPROP_FONTSIZE, 8); //--- Set font size
|
||||
ObjectSetInteger(0, label_name, OBJPROP_ANCHOR, label_anchor); //--- Set anchor
|
||||
ObjectSetDouble(0, label_name, OBJPROP_ANGLE, angle); //--- Set angle
|
||||
ObjectSetInteger(0, label_name, OBJPROP_BACK, false); //--- Set to foreground
|
||||
}
|
||||
color point_label_color = isSupport ? clrSaddleBrown : clrDarkGoldenrod; //--- Set point label color
|
||||
double point_text_offset = 20.0 * _Point; //--- Set point text offset
|
||||
for (int m = 0; m < touches; m++) { //--- Iterate through touches
|
||||
int idx = touch_indices[m]; //--- Get touch index
|
||||
datetime tk_time = swings[idx].time; //--- Get touch time
|
||||
double tk_price = swings[idx].price; //--- Get touch price
|
||||
double text_price; //--- Initialize text price
|
||||
int point_text_anchor; //--- Initialize text anchor
|
||||
if (isSupport) { //--- Handle support
|
||||
text_price = tk_price - point_text_offset; //--- Set text price below
|
||||
point_text_anchor = ANCHOR_LEFT; //--- Set left anchor
|
||||
} else { //--- Handle resistance
|
||||
text_price = tk_price + point_text_offset; //--- Set text price above
|
||||
point_text_anchor = ANCHOR_BOTTOM; //--- Set bottom anchor
|
||||
}
|
||||
string text_name = unique_name + "_point_label" + IntegerToString(m); //--- Generate text name
|
||||
string point_text = "Pt " + IntegerToString(m + 1); //--- Set point text
|
||||
if (ObjectFind(0, text_name) < 0) { //--- Check if text exists
|
||||
ObjectCreate(0, text_name, OBJ_TEXT, 0, tk_time, text_price); //--- Create text
|
||||
ObjectSetString(0, text_name, OBJPROP_TEXT, point_text); //--- Set text
|
||||
ObjectSetInteger(0, text_name, OBJPROP_COLOR, point_label_color); //--- Set color
|
||||
ObjectSetInteger(0, text_name, OBJPROP_FONTSIZE, 8); //--- Set font size
|
||||
ObjectSetInteger(0, text_name, OBJPROP_ANCHOR, point_text_anchor); //--- Set anchor
|
||||
ObjectSetDouble(0, text_name, OBJPROP_ANGLE, 0); //--- Set angle
|
||||
ObjectSetInteger(0, text_name, OBJPROP_BACK, false); //--- Set to foreground
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
============================================================
|
||||
Demo File: Range_Breakout_EA - Signal Logic Showcase
|
||||
Category: Breakout
|
||||
Platform: MetaTrader 4 (MQL4)
|
||||
Version: 1.0
|
||||
Author: Giacomo Cipolat Bares
|
||||
Portfolio: MQL4 Expert Advisors Portfolio
|
||||
============================================================
|
||||
|
||||
Description:
|
||||
This is a simplified public demo derived from the full
|
||||
Range Breakout EA.
|
||||
|
||||
Included in this demo:
|
||||
- range high/low detection
|
||||
- breakout trigger calculation
|
||||
- breakout buffer logic
|
||||
- one-breakout-per-range logic
|
||||
- breakout cooldown logic
|
||||
- basic on-chart signal output
|
||||
|
||||
Excluded from this demo:
|
||||
- order execution
|
||||
- risk management engine
|
||||
- break-even / trailing stop
|
||||
- retry logic
|
||||
- broker protection handling
|
||||
- full production trade framework
|
||||
- chart visualization layer
|
||||
============================================================
|
||||
*/
|
||||
|
||||
#property strict
|
||||
#property version "1.00"
|
||||
|
||||
//========================= INPUTS ==================================
|
||||
input string __01_BreakoutSettings = "01 ======== Breakout Settings ========";
|
||||
input int BreakoutBars = 20;
|
||||
input double BreakoutBufferPips = 0.5;
|
||||
input bool UseCloseBreakout = false;
|
||||
input bool OneBreakoutPerRange = true;
|
||||
input bool ResetRangeAfterTrade = true;
|
||||
input bool UseBreakoutCooldown = true;
|
||||
input int BreakoutCooldownBars = 5;
|
||||
|
||||
//======================= BREAKOUT GLOBALS ==========================
|
||||
double g_lastRangeHigh = 0.0;
|
||||
double g_lastRangeLow = 0.0;
|
||||
bool g_rangeAlreadyTraded = false;
|
||||
int g_lastTradeBarIndex = -1;
|
||||
|
||||
//======================= GENERAL GLOBALS ===========================
|
||||
double g_point;
|
||||
double g_pip;
|
||||
int g_digits;
|
||||
datetime g_lastBarTime = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
g_point = Point;
|
||||
g_digits = Digits;
|
||||
|
||||
if(g_digits == 5 || g_digits == 3)
|
||||
g_pip = g_point * 10.0;
|
||||
else
|
||||
g_pip = g_point;
|
||||
|
||||
Print("Range Breakout demo initialized");
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Detects a new bar |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime currentBarTime = iTime(NULL, 0, 0);
|
||||
|
||||
if(currentBarTime != g_lastBarTime)
|
||||
{
|
||||
g_lastBarTime = currentBarTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Range high from previous N bars |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetBreakoutHigh()
|
||||
{
|
||||
int highestIndex = iHighest(NULL, 0, MODE_HIGH, BreakoutBars, 1);
|
||||
return High[highestIndex];
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Range low from previous N bars |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetBreakoutLow()
|
||||
{
|
||||
int lowestIndex = iLowest(NULL, 0, MODE_LOW, BreakoutBars, 1);
|
||||
return Low[lowestIndex];
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Bullish breakout condition |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsBullishBreakout()
|
||||
{
|
||||
double breakoutHigh = GetBreakoutHigh();
|
||||
double triggerPrice = breakoutHigh + BreakoutBufferPips * g_pip;
|
||||
|
||||
if(UseCloseBreakout)
|
||||
return (Close[1] > triggerPrice);
|
||||
|
||||
return (Ask > triggerPrice);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Bearish breakout condition |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsBearishBreakout()
|
||||
{
|
||||
double breakoutLow = GetBreakoutLow();
|
||||
double triggerPrice = breakoutLow - BreakoutBufferPips * g_pip;
|
||||
|
||||
if(UseCloseBreakout)
|
||||
return (Close[1] < triggerPrice);
|
||||
|
||||
return (Bid < triggerPrice);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Checks if current range is same as previous tracked range |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsSameRange(double rangeHigh, double rangeLow)
|
||||
{
|
||||
if(MathAbs(rangeHigh - g_lastRangeHigh) < (g_point * 0.5) &&
|
||||
MathAbs(rangeLow - g_lastRangeLow) < (g_point * 0.5))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Updates tracked breakout range |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateRangeState()
|
||||
{
|
||||
double currentRangeHigh = GetBreakoutHigh();
|
||||
double currentRangeLow = GetBreakoutLow();
|
||||
|
||||
if(!IsSameRange(currentRangeHigh, currentRangeLow))
|
||||
{
|
||||
g_lastRangeHigh = currentRangeHigh;
|
||||
g_lastRangeLow = currentRangeLow;
|
||||
|
||||
if(ResetRangeAfterTrade)
|
||||
g_rangeAlreadyTraded = false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| One breakout per range filter |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CanTradeCurrentRange()
|
||||
{
|
||||
if(!OneBreakoutPerRange)
|
||||
return true;
|
||||
|
||||
if(g_rangeAlreadyTraded)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Breakout cooldown filter |
|
||||
//+------------------------------------------------------------------+
|
||||
bool BreakoutCooldownPassed()
|
||||
{
|
||||
if(!UseBreakoutCooldown)
|
||||
return true;
|
||||
|
||||
if(g_lastTradeBarIndex < 0)
|
||||
return true;
|
||||
|
||||
int barsPassed = Bars - g_lastTradeBarIndex;
|
||||
|
||||
if(barsPassed >= BreakoutCooldownBars)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Demo buy signal wrapper |
|
||||
//+------------------------------------------------------------------+
|
||||
bool BuySignal()
|
||||
{
|
||||
if(!CanTradeCurrentRange())
|
||||
return false;
|
||||
|
||||
if(!BreakoutCooldownPassed())
|
||||
return false;
|
||||
|
||||
return IsBullishBreakout();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Demo sell signal wrapper |
|
||||
//+------------------------------------------------------------------+
|
||||
bool SellSignal()
|
||||
{
|
||||
if(!CanTradeCurrentRange())
|
||||
return false;
|
||||
|
||||
if(!BreakoutCooldownPassed())
|
||||
return false;
|
||||
|
||||
return IsBearishBreakout();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
if(!IsNewBar())
|
||||
return;
|
||||
|
||||
UpdateRangeState();
|
||||
|
||||
if(BuySignal())
|
||||
{
|
||||
g_lastTradeBarIndex = Bars;
|
||||
g_rangeAlreadyTraded = true;
|
||||
Comment("Demo Signal: BUY breakout detected");
|
||||
return;
|
||||
}
|
||||
|
||||
if(SellSignal())
|
||||
{
|
||||
g_lastTradeBarIndex = Bars;
|
||||
g_rangeAlreadyTraded = true;
|
||||
Comment("Demo Signal: SELL breakout detected");
|
||||
return;
|
||||
}
|
||||
|
||||
Comment("Demo Signal: No valid breakout");
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,317 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Support and Resistance EA.mq4 |
|
||||
//| Copyright 2023,JBlanked |
|
||||
//| https://www.jblanked.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2023,JBlanked"
|
||||
#property link "https://www.jblanked.com"
|
||||
#property strict
|
||||
|
||||
#include <CustomFunctionsFix.mqh>
|
||||
|
||||
|
||||
input string orderSeng = "======= ORDER SETTINGS ======"; //--------------------------->
|
||||
input double StopLoss = 10; // Stop Loss
|
||||
input double TakeProfit = 600; // Take Profit
|
||||
input bool usepercentrisk = true; // Use risk per trade?
|
||||
input double percentrisk = 0.10; // Percent risk
|
||||
input bool uselotsize = false; // Use lot size?
|
||||
input double lotsizee = 0.10; // Lot size
|
||||
|
||||
input string orderSeting = "======= TREND SETTINGS ======"; //--------------------------->
|
||||
input int MA_Period = 160; // Period for moving average
|
||||
input int RSI_Period = 8; // Period for RSI
|
||||
input int rsibuylevel = 20; // RSI under which level to buy
|
||||
input int rsiselllevel = 80; // RSI above which level to sell
|
||||
input bool reverseorder = false; // Reverse trend?
|
||||
input bool HODL = false; // HODL til opposite setup?
|
||||
|
||||
|
||||
input string BreakEvenSettings = "--------TAKE PARTIAL SETTINGS-------"; //--------------------------->
|
||||
input bool UseBreakEvenStop = true; //Use take partials?
|
||||
input double BEclosePercent = 50.0; //Close how much percent?
|
||||
input double breakstart = 200; // Take partials after how many pips in profit (1)
|
||||
input double breakstart2 = 300; // Take partials after how many pips in profit (2)
|
||||
input double breakstart3 = 400; // Take partials after how many pips in profit (3)
|
||||
input double breakstart4 = 500; // Take partials after how many pips in profit (4)
|
||||
|
||||
input double breakstop = 20; // Move stop loss in profit X pips
|
||||
|
||||
input string BkEvnSettings = "======= MARTINGALE SETTINGS ======="; //--------------------------->
|
||||
input bool useMartingale = false; // Use martingale?
|
||||
input double martinPips = 78; // Pips in between martingales
|
||||
input double martinMULTI = 5; // Martingale multiplier
|
||||
|
||||
|
||||
input string timeSettings = "======= TIME SETTINGS ======"; //--------------------
|
||||
input bool UseTimer = false; // Custom trading hours (true/false)
|
||||
input string StartTime1 = "16:30"; //1 Trading start time (hh:mm)
|
||||
input string StopTime1 = "16:31"; //1 Trading stop time (hh:mm)
|
||||
|
||||
input string DAILY_TARGETS = "======= Gain/Loss ======="; //---------------
|
||||
input double dailyTargetP = 10.0; // Daily Profit Target (%)
|
||||
input double dailyLossP = 0.4; // Daily Max DD (%)
|
||||
|
||||
|
||||
input string orderSettins = "======= OTHER SETTINGS ======"; //---------------
|
||||
input string orderComments = "Support/Resistance EA"; // Order Comment
|
||||
input int magicnumb = 918119; // Magic Number
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
//---
|
||||
|
||||
JBlankedInitCEO(magicnumb,918119,"Support/Resistance EA");
|
||||
JBlankedBranding("Support/Resistance EA",magicnumb,string(expiryDateVIP));
|
||||
|
||||
//---
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
//---
|
||||
JBlankedDeinit();
|
||||
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
//---
|
||||
|
||||
if(ApplyDailyTarget(dailyTargetP,dailyLossP,magicnumb)) return;
|
||||
|
||||
if(useMartingale) { martingale(_Symbol,magicnumb,OrderStopLoss(),martinMULTI,martinPips); }
|
||||
|
||||
//////////////////// Start Take Partials Tempalte
|
||||
|
||||
if(UseBreakEvenStop)DoBreak2(magicnumb,breakstart,BEclosePercent,breakstop,breakstart2,breakstart3,breakstart4);
|
||||
|
||||
|
||||
|
||||
//////////////////// End Take Partials Tempalte
|
||||
|
||||
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
|
||||
double MA = iMA(NULL,0,MA_Period,0,MODE_SMA,PRICE_CLOSE,0);
|
||||
double RSI = iRSI(NULL,0,RSI_Period,PRICE_CLOSE,0);
|
||||
double currentPrice = Close[0];
|
||||
|
||||
if(allowTime(UseTimer,StartTime1,StopTime1))
|
||||
{
|
||||
if(!CheckIfOpenOrdersByMagicNB(magicnumb,orderComments) && StopLoss != 0 && !HODL)
|
||||
{
|
||||
if(!reverseorder)
|
||||
{
|
||||
if (currentPrice > MA && RSI < rsibuylevel)
|
||||
{
|
||||
//price is above moving average and RSI is below 30, indicating oversold
|
||||
//enter long position
|
||||
int orderr= OrderSend(Symbol(),OP_BUY,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Ask,3,Ask-StopLoss*GetPipValue(),Ask+TakeProfit*GetPipValue(),orderComments,magicnumb,0,Green);
|
||||
|
||||
}
|
||||
|
||||
|
||||
else if (currentPrice < MA && RSI > rsiselllevel)
|
||||
{
|
||||
//price is below moving average and RSI is above 70, indicating overbought
|
||||
//enter short position
|
||||
int orderr= OrderSend(Symbol(),OP_SELL,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Bid,3,Bid+StopLoss*GetPipValue(),Bid-TakeProfit*GetPipValue(),orderComments,magicnumb,0,Red);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(reverseorder)
|
||||
{
|
||||
if (currentPrice > MA && RSI < rsibuylevel)
|
||||
{
|
||||
//price is above moving average and RSI is below 30, indicating oversold
|
||||
//enter long position
|
||||
int orderr= OrderSend(Symbol(),OP_SELL,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Bid,3,Bid+StopLoss*GetPipValue(),Bid-TakeProfit*GetPipValue(),orderComments,magicnumb,0,Red);
|
||||
|
||||
}
|
||||
|
||||
|
||||
else if (currentPrice < MA && RSI > rsiselllevel)
|
||||
{
|
||||
//price is below moving average and RSI is above 70, indicating overbought
|
||||
//enter short position
|
||||
int orderr= OrderSend(Symbol(),OP_BUY,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Ask,3,Ask-StopLoss*GetPipValue(),Ask+TakeProfit*GetPipValue(),orderComments,magicnumb,0,Green);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!CheckIfOpenOrdersByMagicNB(magicnumb,orderComments) && StopLoss == 0)
|
||||
{
|
||||
if(!reverseorder)
|
||||
{
|
||||
if (currentPrice > MA && RSI < rsibuylevel)
|
||||
{
|
||||
//price is above moving average and RSI is below 30, indicating oversold
|
||||
//enter long position
|
||||
int orderr= OrderSend(Symbol(),OP_BUY,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Ask,3,0,Ask+TakeProfit*GetPipValue(),orderComments,magicnumb,0,Green);
|
||||
|
||||
}
|
||||
|
||||
|
||||
else if (currentPrice < MA && RSI > rsiselllevel)
|
||||
{
|
||||
//price is below moving average and RSI is above 70, indicating overbought
|
||||
//enter short position
|
||||
int orderr= OrderSend(Symbol(),OP_SELL,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Bid,3,0,Bid-TakeProfit*GetPipValue(),orderComments,magicnumb,0,Red);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(reverseorder)
|
||||
{
|
||||
if (currentPrice > MA && RSI < rsibuylevel)
|
||||
{
|
||||
//price is above moving average and RSI is below 30, indicating oversold
|
||||
//enter long position
|
||||
int orderr= OrderSend(Symbol(),OP_SELL,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Bid,3,0,Bid-TakeProfit*GetPipValue(),orderComments,magicnumb,0,Red);
|
||||
|
||||
}
|
||||
|
||||
|
||||
else if (currentPrice < MA && RSI > rsiselllevel)
|
||||
{
|
||||
//price is below moving average and RSI is above 70, indicating overbought
|
||||
//enter short position
|
||||
int orderr= OrderSend(Symbol(),OP_BUY,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Ask,3,0,Ask+TakeProfit*GetPipValue(),orderComments,magicnumb,0,Green);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(!CheckIfOpenOrdersByMagicNB(magicnumb,orderComments) && StopLoss != 0 && HODL)
|
||||
{
|
||||
if(!reverseorder)
|
||||
{
|
||||
if (currentPrice > MA && RSI < rsibuylevel)
|
||||
{
|
||||
//price is above moving average and RSI is below 30, indicating oversold
|
||||
//enter long position
|
||||
int orderr= OrderSend(Symbol(),OP_BUY,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Ask,3,Ask-StopLoss*GetPipValue(),0,orderComments,magicnumb,0,Green);
|
||||
|
||||
}
|
||||
|
||||
|
||||
else if (currentPrice < MA && RSI > rsiselllevel)
|
||||
{
|
||||
//price is below moving average and RSI is above 70, indicating overbought
|
||||
//enter short position
|
||||
int orderr= OrderSend(Symbol(),OP_SELL,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Bid,3,Bid+StopLoss*GetPipValue(),0,orderComments,magicnumb,0,Red);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(reverseorder)
|
||||
{
|
||||
if (currentPrice > MA && RSI < rsibuylevel)
|
||||
{
|
||||
//price is above moving average and RSI is below 30, indicating oversold
|
||||
//enter long position
|
||||
int orderr= OrderSend(Symbol(),OP_SELL,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Bid,3,Bid+StopLoss*GetPipValue(),0,orderComments,magicnumb,0,Red);
|
||||
|
||||
}
|
||||
|
||||
|
||||
else if (currentPrice < MA && RSI > rsiselllevel)
|
||||
{
|
||||
//price is below moving average and RSI is above 70, indicating overbought
|
||||
//enter short position
|
||||
int orderr= OrderSend(Symbol(),OP_BUY,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Ask,3,Ask-StopLoss*GetPipValue(),0,orderComments,magicnumb,0,Green);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!CheckIfOpenOrdersByMagicNB(magicnumb,orderComments) && StopLoss == 0)
|
||||
{
|
||||
if(!reverseorder)
|
||||
{
|
||||
if (currentPrice > MA && RSI < rsibuylevel)
|
||||
{
|
||||
//price is above moving average and RSI is below 30, indicating oversold
|
||||
//enter long position
|
||||
int orderr= OrderSend(Symbol(),OP_BUY,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Ask,3,0,0,orderComments,magicnumb,0,Green);
|
||||
|
||||
}
|
||||
|
||||
|
||||
else if (currentPrice < MA && RSI > rsiselllevel)
|
||||
{
|
||||
//price is below moving average and RSI is above 70, indicating overbought
|
||||
//enter short position
|
||||
int orderr= OrderSend(Symbol(),OP_SELL,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Bid,3,0,0,orderComments,magicnumb,0,Red);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(reverseorder)
|
||||
{
|
||||
if (currentPrice > MA && RSI < rsibuylevel)
|
||||
{
|
||||
//price is above moving average and RSI is below 30, indicating oversold
|
||||
//enter long position
|
||||
int orderr= OrderSend(Symbol(),OP_SELL,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Bid,3,0,0,orderComments,magicnumb,0,Red);
|
||||
|
||||
}
|
||||
|
||||
|
||||
else if (currentPrice < MA && RSI > rsiselllevel)
|
||||
{
|
||||
//price is below moving average and RSI is above 70, indicating overbought
|
||||
//enter short position
|
||||
int orderr= OrderSend(Symbol(),OP_BUY,GetRisk(usepercentrisk,uselotsize,percentrisk,StopLoss,lotsizee),Ask,3,0,0,orderComments,magicnumb,0,Green);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,413 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| HedgeEA.mq4 |
|
||||
//| Copyright 2024, MetaQuotes Software Corp. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2024"
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
// Input Parameters
|
||||
input double LotSize = 0.1; // Lot Size
|
||||
input double HedgeLotSize = 0.1; // Hedge Lot Size
|
||||
input int StopLoss = 50; // Stop Loss in pips
|
||||
input int TakeProfit = 100; // Take Profit in pips
|
||||
input int MagicNumber = 123456; // Magic Number
|
||||
input int Slippage = 3; // Slippage in pips
|
||||
input double MaxLossPercent = 1.0; // Maximum Loss Percent before Hedging
|
||||
input double TargetProfit = 2.0; // Target Profit Percent to Close All
|
||||
input int StartHour = 0; // Trading Start Hour
|
||||
input int EndHour = 24; // Trading End Hour
|
||||
input double MaxMarginLevel = 1000; // Maximum margin level before closing positions
|
||||
input bool UseTrailingStop = true; // Use trailing stop
|
||||
input int TrailingStop = 20; // Trailing stop in pips
|
||||
input int TrailingStep = 5; // Trailing step in pips
|
||||
input bool UseDynamicLotSize = false; // Use dynamic lot size
|
||||
input double RiskPercent = 1.0; // Risk percent for dynamic lot size
|
||||
input int MaxPositionAge = 24; // Maximum position age in hours
|
||||
input bool UseNewsFilter = true; // Use news filter
|
||||
input int NewsMinutesBefore = 30; // Minutes before news to avoid trading
|
||||
input int NewsMinutesAfter = 30; // Minutes after news to avoid trading
|
||||
input bool UseSessionFilter = true; // Use session filter
|
||||
input string AsianSession = "00:00-08:00"; // Asian session
|
||||
input string LondonSession = "08:00-16:00"; // London session
|
||||
input string NewYorkSession = "13:00-21:00"; // New York session
|
||||
input bool UseRiskManagement = true; // Use advanced risk management
|
||||
input double DailyLossLimit = 2.0; // Daily loss limit in percent
|
||||
input double WeeklyLossLimit = 5.0; // Weekly loss limit in percent
|
||||
|
||||
// Global Variables
|
||||
int ticket = 0;
|
||||
bool hedgedPositions[]; // Array to track hedged positions
|
||||
int totalPositions = 0;
|
||||
datetime lastTradeTime = 0;
|
||||
double dailyProfit = 0;
|
||||
double weeklyProfit = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
ArrayResize(hedgedPositions, 100); // Initialize array for 100 positions
|
||||
ArrayInitialize(hedgedPositions, false);
|
||||
lastTradeTime = TimeCurrent();
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
ArrayFree(hedgedPositions);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if trading is allowed in current time |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsTradingTime()
|
||||
{
|
||||
int currentHour = TimeHour(TimeCurrent());
|
||||
return (currentHour >= StartHour && currentHour < EndHour);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if current time is within a trading session |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsInSession(string session)
|
||||
{
|
||||
string times[];
|
||||
StringSplit(session, '-', times);
|
||||
if(ArraySize(times) != 2) return false;
|
||||
|
||||
string currentTime = TimeToString(TimeCurrent(), TIME_MINUTES);
|
||||
return (currentTime >= times[0] && currentTime < times[1]);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if trading is allowed based on sessions |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsSessionAllowed()
|
||||
{
|
||||
if(!UseSessionFilter) return true;
|
||||
|
||||
return (IsInSession(AsianSession) || IsInSession(LondonSession) || IsInSession(NewYorkSession));
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if there is important news coming |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsNewsTime()
|
||||
{
|
||||
if(!UseNewsFilter) return false;
|
||||
|
||||
// Here you would implement your news checking logic
|
||||
// This is a placeholder - you would need to integrate with a news API
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate total profit of all positions |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateTotalProfit()
|
||||
{
|
||||
double totalProfit = 0;
|
||||
for(int i = 0; i < OrdersTotal(); i++)
|
||||
{
|
||||
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
|
||||
{
|
||||
if(OrderMagicNumber() == MagicNumber)
|
||||
{
|
||||
totalProfit += OrderProfit();
|
||||
}
|
||||
}
|
||||
}
|
||||
return totalProfit;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate dynamic lot size based on risk |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateLotSize()
|
||||
{
|
||||
if(!UseDynamicLotSize) return LotSize;
|
||||
|
||||
double riskAmount = AccountBalance() * RiskPercent / 100;
|
||||
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
|
||||
double stopLossPoints = StopLoss * Point;
|
||||
|
||||
return NormalizeDouble(riskAmount / (stopLossPoints * tickValue), 2);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if position is too old |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsPositionTooOld(int ticket)
|
||||
{
|
||||
if(OrderSelect(ticket, SELECT_BY_TICKET))
|
||||
{
|
||||
datetime positionAge = TimeCurrent() - OrderOpenTime();
|
||||
return (positionAge > MaxPositionAge * 3600);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if daily or weekly loss limits are reached |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsLossLimitReached()
|
||||
{
|
||||
if(!UseRiskManagement) return false;
|
||||
|
||||
datetime currentTime = TimeCurrent();
|
||||
if(TimeDay(currentTime) != TimeDay(lastTradeTime))
|
||||
{
|
||||
dailyProfit = 0;
|
||||
lastTradeTime = currentTime;
|
||||
}
|
||||
|
||||
if(TimeDayOfWeek(currentTime) == 0) // Sunday
|
||||
{
|
||||
weeklyProfit = 0;
|
||||
}
|
||||
|
||||
double currentProfit = CalculateTotalProfit();
|
||||
dailyProfit += currentProfit;
|
||||
weeklyProfit += currentProfit;
|
||||
|
||||
return (dailyProfit <= -AccountBalance() * DailyLossLimit / 100 ||
|
||||
weeklyProfit <= -AccountBalance() * WeeklyLossLimit / 100);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close all positions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CloseAllPositions()
|
||||
{
|
||||
for(int i = OrdersTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
|
||||
{
|
||||
if(OrderMagicNumber() == MagicNumber)
|
||||
{
|
||||
bool result = false;
|
||||
if(OrderType() == OP_BUY)
|
||||
result = OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, clrRed);
|
||||
else if(OrderType() == OP_SELL)
|
||||
result = OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, clrGreen);
|
||||
|
||||
if(!result)
|
||||
{
|
||||
Print("Error closing order #", OrderTicket(), ": ", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if trading conditions are met |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CheckTradeConditions()
|
||||
{
|
||||
if(!IsTradeAllowed()) return false;
|
||||
if(!IsTradingTime()) return false;
|
||||
if(!IsSessionAllowed()) return false;
|
||||
if(IsNewsTime()) return false;
|
||||
if(AccountMargin() > MaxMarginLevel) return false;
|
||||
if(IsLossLimitReached()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Print trade information |
|
||||
//+------------------------------------------------------------------+
|
||||
void PrintTradeInfo()
|
||||
{
|
||||
Print("Total Positions: ", OrdersTotal());
|
||||
Print("Total Profit: ", CalculateTotalProfit());
|
||||
Print("Account Balance: ", AccountBalance());
|
||||
Print("Daily Profit: ", dailyProfit);
|
||||
Print("Weekly Profit: ", weeklyProfit);
|
||||
Print("Current Session: ", IsInSession(AsianSession) ? "Asian" :
|
||||
(IsInSession(LondonSession) ? "London" :
|
||||
(IsInSession(NewYorkSession) ? "New York" : "No Session")));
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if lot size is valid and can be opened |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsValidLotSize(double lot)
|
||||
{
|
||||
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
|
||||
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
|
||||
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
|
||||
|
||||
// بررسی محدوده حجم
|
||||
if(lot < minLot || lot > maxLot)
|
||||
{
|
||||
Print("Invalid lot size: ", lot, " (Min: ", minLot, ", Max: ", maxLot, ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
// بررسی گام حجم
|
||||
if(MathAbs(MathMod(lot, lotStep)) > 0.00001)
|
||||
{
|
||||
Print("Lot size must be a multiple of ", lotStep);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if there is enough margin to open position |
|
||||
//+------------------------------------------------------------------+
|
||||
bool HasEnoughMargin(int type, double lot)
|
||||
{
|
||||
double margin = MarketInfo(Symbol(), MODE_MARGINREQUIRED) * lot;
|
||||
double freeMargin = AccountFreeMargin();
|
||||
|
||||
if(freeMargin < margin)
|
||||
{
|
||||
Print("Not enough margin. Required: ", margin, ", Free: ", freeMargin);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Try to reduce lot size if original size cannot be opened |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetAdjustedLotSize(double originalLot)
|
||||
{
|
||||
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
|
||||
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
|
||||
double adjustedLot = originalLot;
|
||||
|
||||
while(adjustedLot > minLot)
|
||||
{
|
||||
if(IsValidLotSize(adjustedLot) && HasEnoughMargin(OP_BUY, adjustedLot))
|
||||
{
|
||||
Print("Adjusted lot size from ", originalLot, " to ", adjustedLot);
|
||||
return adjustedLot;
|
||||
}
|
||||
adjustedLot -= lotStep;
|
||||
}
|
||||
|
||||
return 0; // اگر هیچ حجمی نتواند باز شود
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
if(!CheckTradeConditions()) return;
|
||||
|
||||
// Check total profit and close all if target reached
|
||||
double totalProfit = CalculateTotalProfit();
|
||||
if(totalProfit >= AccountBalance() * TargetProfit / 100)
|
||||
{
|
||||
CloseAllPositions();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we have any open positions
|
||||
if(OrdersTotal() > 0)
|
||||
{
|
||||
for(int i = 0; i < OrdersTotal(); i++)
|
||||
{
|
||||
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
|
||||
{
|
||||
if(OrderMagicNumber() == MagicNumber)
|
||||
{
|
||||
// Check if position is too old
|
||||
if(IsPositionTooOld(OrderTicket()))
|
||||
{
|
||||
bool result = false;
|
||||
if(OrderType() == OP_BUY)
|
||||
result = OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, clrRed);
|
||||
else if(OrderType() == OP_SELL)
|
||||
result = OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, clrGreen);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate loss percentage
|
||||
double lossPercent = MathAbs(OrderProfit()) / AccountBalance() * 100;
|
||||
|
||||
// If position is in loss and not hedged yet and loss exceeds threshold
|
||||
if(OrderProfit() < 0 && !hedgedPositions[OrderTicket()] && lossPercent >= MaxLossPercent)
|
||||
{
|
||||
double hedgeLot = CalculateLotSize();
|
||||
|
||||
// بررسی و تنظیم حجم
|
||||
if(!IsValidLotSize(hedgeLot) || !HasEnoughMargin(OrderType() == OP_BUY ? OP_SELL : OP_BUY, hedgeLot))
|
||||
{
|
||||
hedgeLot = GetAdjustedLotSize(hedgeLot);
|
||||
if(hedgeLot == 0)
|
||||
{
|
||||
Print("Cannot open hedge position - no valid lot size available");
|
||||
// اینجا میتوانید تصمیم بگیرید چه کاری انجام شود
|
||||
// مثلاً بستن پوزیشن اصلی یا ارسال هشدار
|
||||
if(CloseOnHedgeFailure) // یک پارامتر جدید
|
||||
{
|
||||
OrderClose(OrderTicket(), OrderLots(),
|
||||
OrderType() == OP_BUY ? Bid : Ask,
|
||||
Slippage, clrRed);
|
||||
Print("Closed original position due to hedge failure");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// باز کردن پوزیشن هدج با حجم تنظیم شده
|
||||
if(OrderType() == OP_BUY)
|
||||
{
|
||||
ticket = OrderSend(Symbol(), OP_SELL, hedgeLot, Bid, Slippage,
|
||||
OrderOpenPrice() + StopLoss * Point,
|
||||
OrderOpenPrice() - TakeProfit * Point,
|
||||
"Hedge", MagicNumber, 0, clrRed);
|
||||
}
|
||||
else if(OrderType() == OP_SELL)
|
||||
{
|
||||
ticket = OrderSend(Symbol(), OP_BUY, hedgeLot, Ask, Slippage,
|
||||
OrderOpenPrice() - StopLoss * Point,
|
||||
OrderOpenPrice() + TakeProfit * Point,
|
||||
"Hedge", MagicNumber, 0, clrGreen);
|
||||
}
|
||||
|
||||
if(ticket > 0)
|
||||
{
|
||||
hedgedPositions[OrderTicket()] = true;
|
||||
Print("Hedge position opened successfully for order #", OrderTicket(),
|
||||
" with adjusted lot size: ", hedgeLot);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error opening hedge position: ", GetLastError());
|
||||
if(CloseOnHedgeFailure)
|
||||
{
|
||||
OrderClose(OrderTicket(), OrderLots(),
|
||||
OrderType() == OP_BUY ? Bid : Ask,
|
||||
Slippage, clrRed);
|
||||
Print("Closed original position due to hedge failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print trade information periodically
|
||||
static datetime lastPrintTime = 0;
|
||||
if(TimeCurrent() - lastPrintTime >= 3600) // Print every hour
|
||||
{
|
||||
PrintTradeInfo();
|
||||
lastPrintTime = TimeCurrent();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,199 @@
|
||||
#include <stderror.mqh>
|
||||
#include <stdlib.mqh>
|
||||
|
||||
#property copyright "Copyright 2023, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
// config parameters
|
||||
double initialDistance = 3; // Initial distance for the first order
|
||||
double orderInterval = 0.5; // Distance between orders in points
|
||||
int orderCount = 20; // Number of orders to place on each side
|
||||
double stopLossDistance = 0.5; // Stop loss distance in points
|
||||
double takeProfitDistance = 0.5; // Take profit distance in points
|
||||
double Lots = 0.01; // Lot size for orders
|
||||
int expirationMinutes = 1000; // Expiration time for orders in minutes
|
||||
double stdThreshold = 0.5; // Standard deviation threshold for placing orders
|
||||
int stdTicksNumbers = 200;
|
||||
int removeOrderTime = 180; // seconds
|
||||
int waitForOrderTime = 180; // seconds
|
||||
|
||||
datetime lastOrderOpenTime = 0;
|
||||
|
||||
// global variables
|
||||
double tickPrices[];
|
||||
int tickCounts = 0;
|
||||
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
// create zero array for tickPrices
|
||||
ArraySetAsSeries(tickPrices, true);
|
||||
ArrayResize(tickPrices, stdTicksNumbers);
|
||||
ArraySetAsSeries(tickPrices, false);
|
||||
ArrayInitialize(tickPrices, 0.0);
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
}
|
||||
|
||||
void PrintArray(const double &array[])
|
||||
{
|
||||
string arrayString = "";
|
||||
int arraySize = ArraySize(array);
|
||||
for (int i = 0; i < arraySize; i++)
|
||||
arrayString += DoubleToString(array[i], 5) + " ";
|
||||
Print(arrayString);
|
||||
}
|
||||
|
||||
double getAverage(const int _period)
|
||||
{
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < _period; i++)
|
||||
sum += iClose(NULL, 0, i);
|
||||
return sum / _period;
|
||||
}
|
||||
|
||||
void PlaceOrders()
|
||||
{
|
||||
double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
// Calculate the distance for the first order
|
||||
double firstOrderDistance = initialDistance;
|
||||
|
||||
for (int i = 0; i < orderCount; i++)
|
||||
{
|
||||
// Calculate expiration time
|
||||
datetime expirationTime = iTime(NULL, 0, 0) + expirationMinutes * 60;
|
||||
|
||||
// Place Buy Orders1
|
||||
double orderPrice = currentPrice + firstOrderDistance;
|
||||
double tp = orderPrice + takeProfitDistance;
|
||||
double sl = orderPrice - stopLossDistance;
|
||||
int result = OrderSend(_Symbol, OP_BUYSTOP, Lots, orderPrice , 10, sl, tp, "Buy Order", 0, expirationTime, clrGreen);
|
||||
if (result > 0){
|
||||
Print("Buy order placed. Ticket: ", result);
|
||||
}
|
||||
else{
|
||||
int error = GetLastError();
|
||||
string errorDescription = ErrorDescription(error);
|
||||
Print("OrderSend failed with error #", error, ": ", errorDescription);
|
||||
}
|
||||
orderPrice = currentPrice - firstOrderDistance;
|
||||
tp = orderPrice - takeProfitDistance;
|
||||
sl = orderPrice + stopLossDistance;
|
||||
result = OrderSend(_Symbol, OP_SELLSTOP, Lots, orderPrice , 10, sl, tp , "Sell Order", 0, expirationTime, clrRed);
|
||||
if (result > 0){
|
||||
Print("Buy order placed. Ticket: ", result);
|
||||
}
|
||||
else{
|
||||
int error = GetLastError();
|
||||
string errorDescription = ErrorDescription(error);
|
||||
Print("OrderSend failed with error #", error, ": ", errorDescription);
|
||||
}
|
||||
// Increment distance for subsequent orders
|
||||
firstOrderDistance += orderInterval;
|
||||
}
|
||||
}
|
||||
|
||||
double getStdTicks()
|
||||
{
|
||||
if (tickCounts < stdTicksNumbers )
|
||||
return stdThreshold + 1;
|
||||
|
||||
// Calculate mean
|
||||
double mean = 0.0;
|
||||
for (int i = 0; i < stdTicksNumbers ; i++)
|
||||
mean += tickPrices[i];
|
||||
mean /= stdTicksNumbers ;
|
||||
|
||||
|
||||
// Calculate sum of squared differences
|
||||
double ss = 0.0;
|
||||
for (int i = 0; i < stdTicksNumbers ; i++)
|
||||
{
|
||||
double tickPrice = tickPrices[i];
|
||||
ss += MathPow(tickPrice - mean, 2);
|
||||
}
|
||||
|
||||
return MathSqrt(ss / stdTicksNumbers );
|
||||
}
|
||||
|
||||
|
||||
void updateTickData()
|
||||
{
|
||||
tickCounts ++;
|
||||
// shift
|
||||
for(int i = stdTicksNumbers - 1; i > 0; i--)
|
||||
tickPrices[i] = tickPrices[i-1];
|
||||
tickPrices[0] = (Bid + Ask) / 2.0;
|
||||
}
|
||||
|
||||
|
||||
void CheckAndRemovePendingOrders()
|
||||
{
|
||||
int totalOrders = OrdersTotal();
|
||||
|
||||
for (int i = 0; i < totalOrders; i++)
|
||||
{
|
||||
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)==false){
|
||||
Print("ERROR - Unable to select the order - ", GetLastError());
|
||||
continue;
|
||||
}
|
||||
// check time
|
||||
int type = OrderType();
|
||||
if (type != OP_BUYSTOP && type != OP_SELLSTOP)
|
||||
continue;
|
||||
|
||||
// Check if the order hasn't been opened yet
|
||||
if (OrderSymbol() == _Symbol && OrderMagicNumber() ==0)
|
||||
{
|
||||
datetime orderOpenTime = OrderOpenTime();
|
||||
datetime currentTime = iTime(NULL, 0, 0);
|
||||
// Calculate the time difference in seconds
|
||||
int timeDifference = currentTime - orderOpenTime;
|
||||
// If the order's pending time exceeds 3 minutes (180 seconds), remove the order
|
||||
if (timeDifference > removeOrderTime )
|
||||
{
|
||||
bool deleteResult = OrderDelete(OrderTicket());
|
||||
if (deleteResult)
|
||||
{
|
||||
Print("Pending order removed. Ticket: ", OrderTicket());
|
||||
}
|
||||
else
|
||||
{
|
||||
int error = GetLastError();
|
||||
string errorDescription = ErrorDescription(error);
|
||||
Print("OrderDelete failed with error #", error, ": ", errorDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
updateTickData();
|
||||
double mean = getAverage(10); // average per minute
|
||||
double std = getStdTicks(); // std per ticks
|
||||
CheckAndRemovePendingOrders();
|
||||
|
||||
datetime currentTime = iTime(NULL, 0, 0);
|
||||
int timeDifferenceSinceLastOrder = currentTime - lastOrderOpenTime;
|
||||
|
||||
if (timeDifferenceSinceLastOrder > waitForOrderTime || lastOrderOpenTime == 0) // 120 seconds = 2 minutes
|
||||
{
|
||||
// Additional condition: if std is below the threshold, place orders
|
||||
Print(std);
|
||||
if (std < stdThreshold)
|
||||
{
|
||||
PlaceOrders();
|
||||
lastOrderOpenTime = iTime(NULL, 0, 0); // Update the last order open time
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,28 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| EA31337 - multi-strategy advanced trading robot. |
|
||||
//| Copyright 2016-2023, EA31337 Ltd |
|
||||
//| https://ea31337.github.io/ |
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
// Main code.
|
||||
#include "EA31337.mq5"
|
||||
|
||||
// EA indicator resources.
|
||||
#ifdef __resource__
|
||||
// Indicator resources.
|
||||
// #resource INDI_ATR_MA_TREND_PATH + MQL_EXT // @todo: Not supported in MT4.
|
||||
#resource INDI_EWO_OSC_PATH + MQL_EXT
|
||||
#resource INDI_SVEBB_PATH + MQL_EXT
|
||||
#resource INDI_TMA_CG_PATH + MQL_EXT
|
||||
#resource INDI_TMA_TRUE_PATH + MQL_EXT
|
||||
#resource INDI_SAWA_PATH + MQL_EXT
|
||||
// #resource INDI_SUPERTREND_PATH + MQL_EXT // @todo: Not supported in MT4.
|
||||
// Strategy resources (MQL4 workaround).
|
||||
string MetaNewsData2018 = "";
|
||||
string MetaNewsData2019 = "";
|
||||
string MetaNewsData2020 = "";
|
||||
string MetaNewsData2021 = "";
|
||||
string MetaNewsData2022 = "";
|
||||
string MetaNewsData2023 = "";
|
||||
string MetaNewsData2024 = "";
|
||||
#endif
|
||||
Binary file not shown.
@@ -0,0 +1,373 @@
|
||||
#property version "1.20";
|
||||
#include <Trade\PositionInfo.mqh>;
|
||||
#include <Trade\Trade.mqh>;
|
||||
CPositionInfo position;
|
||||
CTrade ctrade;
|
||||
|
||||
#define HR0800 28800
|
||||
#define HR0830 30600
|
||||
#define HR1300 46800
|
||||
#define HR1330 48600
|
||||
#define HR1900 68400
|
||||
#define HR2400 86400
|
||||
#define SECONDS uint
|
||||
|
||||
// settings
|
||||
input string RISK_MANAGEMENT_SETTINGS;
|
||||
input double lot_size;
|
||||
input int bars_look_back;
|
||||
input bool one_trade_per_session;
|
||||
|
||||
// global variables
|
||||
ulong orderTicket = OrderGetTicket(1);
|
||||
|
||||
double shortFirstBarLow;
|
||||
double shortFirstBarHigh;
|
||||
double shortThirdBarHigh;
|
||||
double shortThirdBarLow;
|
||||
|
||||
double longFirstBarLow;
|
||||
double longFirstBarHigh;
|
||||
double longThirdBarHigh;
|
||||
double longThirdBarLow;
|
||||
|
||||
bool isShortEntry = false;
|
||||
bool isLongEntry = false;
|
||||
bool londonReset = false;
|
||||
bool newYorkReset = false;
|
||||
bool longReEntry = false;
|
||||
bool shortReEntry = false;
|
||||
int longImbalanceStorage;
|
||||
int shortImbalanceStorage;
|
||||
int longOrShort = 3;
|
||||
|
||||
datetime shortBeginningCandleTime;
|
||||
datetime longBeginningCandleTime;
|
||||
|
||||
// returns the time in GMT in seconds
|
||||
SECONDS time(datetime when = 0) {
|
||||
return SECONDS(when == 0 ? TimeCurrent() : when) % HR2400;
|
||||
}
|
||||
datetime date(datetime when = 0) {
|
||||
return datetime((when == 0 ? TimeCurrent() : when) - time(when));
|
||||
}
|
||||
bool isValidTime(SECONDS start, SECONDS end, datetime when = 0) {
|
||||
SECONDS now = time(when);
|
||||
return start < end ? start <= now && now < end : !isValidTime(end, start, when);
|
||||
}
|
||||
|
||||
// returns true if it finds a short imbalance
|
||||
bool isImbalanceShort(bool sa = false) {
|
||||
// set the chart to 1 min to get precise entries
|
||||
ChartSetSymbolPeriod(0, NULL, PERIOD_M1);
|
||||
|
||||
// store a reference to the first bar
|
||||
shortBeginningCandleTime = TimeCurrent();
|
||||
|
||||
// store the high of the first bar
|
||||
shortFirstBarLow = iLow(NULL, 0, 1);
|
||||
shortFirstBarHigh = iHigh(NULL, 0, 1);
|
||||
|
||||
// store the low of the third bar
|
||||
shortThirdBarHigh = iHigh(NULL, 0, 3);
|
||||
|
||||
return shortFirstBarLow > shortThirdBarHigh ? sa = true : sa = false;
|
||||
}
|
||||
|
||||
// returns true if it finds a long imbalance
|
||||
bool isImbalanceLong(bool la = false) {
|
||||
// set the chart to 1 min to get precise entries
|
||||
ChartSetSymbolPeriod(0, NULL, PERIOD_M1);
|
||||
|
||||
// store a reference to the first candle
|
||||
longBeginningCandleTime = TimeCurrent();
|
||||
|
||||
// store the high of the first bar
|
||||
longFirstBarHigh = iHigh(NULL, 0, 1);
|
||||
|
||||
// store the low of the third bar
|
||||
longThirdBarLow = iLow(NULL, 0, 3);
|
||||
|
||||
return longFirstBarHigh < longThirdBarLow ? la = true : la = false;
|
||||
}
|
||||
|
||||
// returns true or false if the short imbalance has been entered
|
||||
bool isImbalanaceShortEntered(bool sb = false) {
|
||||
// set the chart to 1 min to get precise entries
|
||||
ChartSetSymbolPeriod(0, NULL, PERIOD_M1);
|
||||
return iClose(NULL, 0, 1) > shortThirdBarLow && iClose(NULL, 0, 3) < shortFirstBarHigh ? sb = true : sb = false;
|
||||
}
|
||||
|
||||
// returns true or false is the long imbalance has been entered
|
||||
bool isImbalanceLongEntered(bool lb = false) {
|
||||
// set the chart to 1 min to get precise entries
|
||||
ChartSetSymbolPeriod(0, NULL, PERIOD_M1);
|
||||
return iClose(NULL, 0, 1) < longThirdBarLow && iClose(NULL, 0, 3) > longFirstBarHigh ? lb = true : lb = false;
|
||||
}
|
||||
|
||||
// returns true if there is a long imbalance (for use in a for loop)
|
||||
bool initialLongImbalance(bool ik = false, int firstInt = 1, int secondInt = 1) {
|
||||
return iHigh(NULL, 0, firstInt) < iLow(NULL, 0, secondInt) ? ik = true : ik = false;
|
||||
}
|
||||
|
||||
// returns true if there is a short imbalance (for use in a for loop)
|
||||
bool initialShortImbalance(bool on = false, int firstInt = 1, int secondInt = 1) {
|
||||
return iLow(NULL, 0, firstInt) > iHigh(NULL, 0, secondInt) ? on = true : on = false;
|
||||
}
|
||||
|
||||
// as there are no imbalances, it checks the close of the first candle of the more recently closed session to the first of the new one
|
||||
bool alternateStrategyShort(bool ass = false) {
|
||||
return iClose(NULL, 0, 12) > iClose(NULL, 0, 1) ? ass = true : ass = false;
|
||||
}
|
||||
|
||||
// as there are no imbalances, it check the close of the first candle of the more recently closed session to the first of the new one
|
||||
bool alternateStrategyLong(bool asl = false) {
|
||||
return iClose(NULL, 0, 12) < iClose(NULL, 0, 1) ? asl = true : asl = false;
|
||||
}
|
||||
|
||||
// creates a stop order
|
||||
void stopOrderAction(int x, double tbl, double fbl, double tbh, double fbh)
|
||||
{
|
||||
// place a limit order at the first bar's low
|
||||
MqlTradeRequest stopOrderRequest;
|
||||
MqlTradeResult stopOrderResult;
|
||||
|
||||
stopOrderRequest.symbol = Symbol();
|
||||
stopOrderRequest.order = orderTicket;
|
||||
stopOrderRequest.volume = lot_size;
|
||||
stopOrderRequest.deviation = 2;
|
||||
stopOrderRequest.action = TRADE_ACTION_PENDING;
|
||||
stopOrderRequest.type_filling = ENUM_ORDER_TYPE_FILLING::ORDER_FILLING_FOK;
|
||||
|
||||
if(x == 1) {
|
||||
stopOrderRequest.price = tbl;
|
||||
stopOrderRequest.type = ORDER_TYPE_BUY_STOP;
|
||||
stopOrderRequest.sl = fbl + 5 * Point();
|
||||
}
|
||||
if(x == 2) {
|
||||
stopOrderRequest.price = tbh;
|
||||
stopOrderRequest.type = ORDER_TYPE_SELL_STOP;
|
||||
stopOrderRequest.sl = fbh + 5 * Point();
|
||||
}
|
||||
// send the order with the inputs above
|
||||
OrderSend(stopOrderRequest, stopOrderResult);
|
||||
}
|
||||
|
||||
// makes order ticket for use in cancelling the stop order if it doesn't get triggered
|
||||
void removeStopOrder()
|
||||
{
|
||||
MqlTradeRequest removeOrderRequest;
|
||||
MqlTradeResult removeOrderResult;
|
||||
|
||||
removeOrderRequest.action = TRADE_ACTION_REMOVE;
|
||||
removeOrderRequest.order = orderTicket;
|
||||
|
||||
OrderSend(removeOrderRequest, removeOrderResult);
|
||||
}
|
||||
|
||||
// this function closes the current position
|
||||
void closePosition()
|
||||
{
|
||||
if(position.Symbol()==Symbol()) {
|
||||
ctrade.PositionClose(position.Ticket());
|
||||
}
|
||||
}
|
||||
|
||||
// this function finds the entry for both New York and London trades
|
||||
void entry()
|
||||
{
|
||||
// go through each candle and see if there is an imbalance
|
||||
for(int i = 12; i > 3; i--) {
|
||||
int k = i - 2;
|
||||
if(initialLongImbalance(false, i, k) == true) {
|
||||
longImbalanceStorage = longImbalanceStorage + 1;
|
||||
} else if(initialShortImbalance(false, i, k) == true) {
|
||||
shortImbalanceStorage = shortImbalanceStorage + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// check to make sure there are imbalances, and if not do a different calculation for the same bias thing
|
||||
if((longImbalanceStorage == shortImbalanceStorage) || (longImbalanceStorage == 0 && shortImbalanceStorage == 0)) {
|
||||
// do the check the first candle of the end of the last session to the one before the session now
|
||||
if(alternateStrategyLong() == true) {
|
||||
longImbalanceStorage = longImbalanceStorage + 1;
|
||||
} else if (alternateStrategyShort() == true) {
|
||||
shortImbalanceStorage = shortImbalanceStorage + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// see if more short imbalances have been found and then find an entry in the opposite direction
|
||||
if(longImbalanceStorage < shortImbalanceStorage) {
|
||||
ChartSetSymbolPeriod(0, NULL, PERIOD_M5);
|
||||
while(isValidTime(HR0800, HR0830) && isLongEntry == false) {
|
||||
if(isImbalanceLong() == true && isImbalanceLongEntered() == true) {
|
||||
printf("got an entry");
|
||||
longOrShort = 1;
|
||||
stopOrderAction(longOrShort, longThirdBarLow, longFirstBarLow, longThirdBarHigh, longFirstBarHigh);
|
||||
isLongEntry = true;
|
||||
}
|
||||
}
|
||||
} else if(longImbalanceStorage > shortImbalanceStorage) {
|
||||
ChartSetSymbolPeriod(0, NULL, PERIOD_M5);
|
||||
// see if more long imbalances have been found and then find an entry in the opposite direction
|
||||
while(isValidTime(HR0800, HR0830) && isShortEntry == false) {
|
||||
if(isImbalanceShort() == true && isImbalanaceShortEntered() == true) {
|
||||
printf("got an entry");
|
||||
longOrShort = 2;
|
||||
stopOrderAction(longOrShort, shortThirdBarLow, shortFirstBarLow, shortThirdBarHigh, shortFirstBarHigh);
|
||||
isShortEntry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("reset variables");
|
||||
// reset these variables
|
||||
shortImbalanceStorage = 0;
|
||||
longImbalanceStorage = 0;
|
||||
londonReset = true;
|
||||
newYorkReset = true;
|
||||
}
|
||||
|
||||
|
||||
void reEntryLong()
|
||||
{
|
||||
printf("if long entry");
|
||||
// remove entry if price doesn't enter it within 6 bars
|
||||
if(iBarShift(Symbol(), 0, longBeginningCandleTime, true) == 6) {
|
||||
printf("cancelled order after time");
|
||||
removeStopOrder();
|
||||
isLongEntry = false;
|
||||
}
|
||||
if(iLow(NULL, 0, 1) > iHigh(NULL, 0, 3)) {
|
||||
printf("went opposite direction");
|
||||
closePosition();
|
||||
if(one_trade_per_session == false) {
|
||||
if(isImbalanceShort() == true && isImbalanaceShortEntered() == true) {
|
||||
longBeginningCandleTime = TimeCurrent();
|
||||
longOrShort = 2;
|
||||
stopOrderAction(longOrShort, shortThirdBarLow, shortFirstBarLow, shortThirdBarHigh, shortFirstBarLow);
|
||||
longReEntry = true;
|
||||
}
|
||||
} else {
|
||||
isLongEntry = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reEntryShort()
|
||||
{
|
||||
printf("if short entry");
|
||||
if(iBarShift(Symbol(), 0, shortBeginningCandleTime, true) == 6) {
|
||||
removeStopOrder();
|
||||
isShortEntry = false;
|
||||
}
|
||||
// finds an imbalance in the opposing direction
|
||||
if(iHigh(NULL, 0, 1) < iLow(NULL, 0, 3)) {
|
||||
closePosition();
|
||||
if(one_trade_per_session == false) {
|
||||
if(isImbalanceLong() == true && isImbalanceLongEntered() == true) {
|
||||
shortBeginningCandleTime = TimeCurrent();
|
||||
longOrShort = 1;
|
||||
stopOrderAction(longOrShort, shortThirdBarLow, shortFirstBarLow, shortThirdBarHigh, shortFirstBarLow);
|
||||
shortReEntry = true;
|
||||
}
|
||||
} else {
|
||||
isShortEntry = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnInit()
|
||||
{
|
||||
// sets the timeframe to the 5 minute
|
||||
ChartSetSymbolPeriod(0, NULL, PERIOD_M5);
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
// when it approcahes london session, it can start to find an entry
|
||||
if(isValidTime(HR0800, HR0830) && londonReset == false) {
|
||||
closePosition();
|
||||
entry();
|
||||
newYorkReset = false;
|
||||
}
|
||||
|
||||
// when it approaches new york session, it can start to find an entry
|
||||
if(isValidTime(HR1300, HR1330) && newYorkReset == false) {
|
||||
printf("in new york session");
|
||||
closePosition();
|
||||
entry();
|
||||
londonReset = false;
|
||||
}
|
||||
|
||||
// finds when a long trade has been opened in the London session and it manages stop loss and what to do if prices reverse
|
||||
if(isLongEntry == true && isValidTime(HR0800, HR1300)) {
|
||||
reEntryLong();
|
||||
if(longReEntry == true) {
|
||||
if(iBarShift(Symbol(), 0, longBeginningCandleTime, true) == 6) {
|
||||
removeStopOrder();
|
||||
isLongEntry = false;
|
||||
}
|
||||
}
|
||||
ChartSetSymbolPeriod(0, NULL, PERIOD_M5);
|
||||
// finds an imbalance in the opposing direction
|
||||
if(iLow(NULL, 0, 1) > iHigh(NULL, 0, 3)) {
|
||||
closePosition();
|
||||
longReEntry = false;
|
||||
}
|
||||
}
|
||||
|
||||
// finds when a long trade has been opened in the New York session and it manages stop loss and what to do if prices reverse
|
||||
if(isLongEntry == true && isValidTime(HR1300, HR1900)) {
|
||||
reEntryLong();
|
||||
if(longReEntry == true) {
|
||||
if(iBarShift(Symbol(), 0, longBeginningCandleTime, true) == 6) {
|
||||
removeStopOrder();
|
||||
isLongEntry = false;
|
||||
}
|
||||
}
|
||||
ChartSetSymbolPeriod(0, NULL, PERIOD_M5);
|
||||
// finds an imbalance in the opposing direction
|
||||
if(iLow(NULL, 0, 1) > iHigh(NULL, 0, 3)) {
|
||||
closePosition();
|
||||
longReEntry = false;
|
||||
}
|
||||
}
|
||||
|
||||
// finds when a short trade in London session has been opened and it manages stop loss and what to do if prices reverse
|
||||
if(isShortEntry == true && isValidTime(HR0800, HR1300)) {
|
||||
reEntryShort();
|
||||
if(shortReEntry == true) {
|
||||
if(iBarShift(Symbol(), 0, shortBeginningCandleTime, true) == 6) {
|
||||
removeStopOrder();
|
||||
isShortEntry = false;
|
||||
}
|
||||
}
|
||||
// finds an imbalance in the opposing direction
|
||||
if(iHigh(NULL, 0, 1) < iLow(NULL, 0, 3)) {
|
||||
closePosition();
|
||||
shortReEntry = false;
|
||||
}
|
||||
}
|
||||
// finds when a short trade in New York session has been opened and it manages stop loss and what to do if prices reverse
|
||||
if(isShortEntry == true && isValidTime(HR1300, HR1900)) {
|
||||
reEntryShort();
|
||||
if(shortReEntry == true) {
|
||||
if(iBarShift(Symbol(), 0, shortBeginningCandleTime, true) == 6) {
|
||||
removeStopOrder();
|
||||
isShortEntry = false;
|
||||
}
|
||||
}
|
||||
// finds an imbalance in the opposing direction
|
||||
if(iHigh(NULL, 0, 1) < iLow(NULL, 0, 3)) {
|
||||
closePosition();
|
||||
shortReEntry = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove stop order and any open postions just in case
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
removeStopOrder();
|
||||
closePosition();
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,769 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| FxChartAI OpenEA |
|
||||
//| Copyright 2025, FxChartAI |
|
||||
//| https://www.fxchartai.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, FxChartAI"
|
||||
#property link "https://www.fxchartai.com"
|
||||
#property version "1.1.0"
|
||||
|
||||
#include <Files\File.mqh>
|
||||
#include <JAson.mqh> // JSON parser library per https://www.mql5.com/en/articles/14108
|
||||
|
||||
//--- Input Parameters
|
||||
input double LotSize = 1; // Risk management: Position size
|
||||
input int StopLossPips = 400; // Stop loss in pips
|
||||
input int TakeProfitPips = 500; // Take profit in pips
|
||||
input int ConfidenceLevel = 5; // Minimum confidence level required (1-5)
|
||||
input int MaxDataSize = 7; // Maximum dataset size for analysis
|
||||
input int OperationMode = 0; // 0 = Test (CSV), 1 = Live (API)
|
||||
input int MaxRetryAttempts = 9; // Maximum data loading retry attempts
|
||||
input int MagicNumber = 12345;
|
||||
input int TrailingPips = 50;
|
||||
|
||||
#define RETRY_DELAY_MS 60000 // 1 minute delay between retries
|
||||
|
||||
//--- Constants
|
||||
enum SIGNAL_POSITION { SIGNAL_SELL, SIGNAL_BUY, SIGNAL_NONE };
|
||||
enum TREND_WEIGHT { TREND_HIGH, TREND_LOW, TREND_NONE };
|
||||
|
||||
//--- Global variables
|
||||
string m10FileName = "signal_dataset_" + _Symbol + "_m10.csv";
|
||||
string h1FileName = "signal_dataset_" + _Symbol + "_h1.csv";
|
||||
datetime lastM10UpdateTime = 0;
|
||||
datetime lastH1UpdateTime = 0;
|
||||
int pendingOrderTicket = -1;
|
||||
|
||||
//--- SignalData structure
|
||||
struct SignalData
|
||||
{
|
||||
datetime time;
|
||||
SIGNAL_POSITION position;
|
||||
TREND_WEIGHT weight;
|
||||
};
|
||||
|
||||
//--- Global arrays for signal data (declared externally, e.g., in a header)
|
||||
SignalData m10Data[];
|
||||
int m10DataIndex = 0;
|
||||
SignalData h1Data[];
|
||||
int h1DataIndex = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
if(OperationMode != 0 && OperationMode != 1)
|
||||
{ Print("Invalid OperationMode value"); return(INIT_FAILED); }
|
||||
if(MaxRetryAttempts < 1 || MaxRetryAttempts > _Period)
|
||||
{ Print("Invalid MaxRetryAttempts value"); return(INIT_FAILED); }
|
||||
if(MaxDataSize < 1)
|
||||
{ Print("Invalid MaxDataSize value"); return(INIT_FAILED); }
|
||||
if(ConfidenceLevel < 1)
|
||||
{ Print("Invalid ConfidenceLevel value"); return(INIT_FAILED); }
|
||||
if(TakeProfitPips < 1)
|
||||
{ Print("Invalid TakeProfitPips value"); return(INIT_FAILED); }
|
||||
if(StopLossPips < 1)
|
||||
{ Print("Invalid StopLossPips value"); return(INIT_FAILED); }
|
||||
if(LotSize < 0)
|
||||
{ Print("Invalid LotSize value"); return(INIT_FAILED); }
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
static datetime prevM10Bar = 0;
|
||||
static datetime prevH1Bar = 0;
|
||||
|
||||
if(_Period == PERIOD_M10)
|
||||
{
|
||||
datetime currentM10Bar = iTime(_Symbol, PERIOD_M10, 1);
|
||||
if(currentM10Bar != prevM10Bar)
|
||||
{
|
||||
prevM10Bar = currentM10Bar;
|
||||
ProcessTimeframe(PERIOD_M10, m10FileName, m10Data, m10DataIndex, lastM10UpdateTime);
|
||||
}
|
||||
}
|
||||
else
|
||||
if(_Period == PERIOD_H1)
|
||||
{
|
||||
datetime currentH1Bar = iTime(_Symbol, PERIOD_H1, 1);
|
||||
if(currentH1Bar != prevH1Bar)
|
||||
{
|
||||
prevH1Bar = currentH1Bar;
|
||||
ProcessTimeframe(PERIOD_H1, h1FileName, h1Data, h1DataIndex, lastH1UpdateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Process timeframe data |
|
||||
//+------------------------------------------------------------------+
|
||||
void ProcessTimeframe(ENUM_TIMEFRAMES tf, string filename, SignalData &data[], int &dataIndex, datetime &lastUpdate)
|
||||
{
|
||||
datetime currentTime = iTime(_Symbol, tf, 1);
|
||||
bool result = false;
|
||||
|
||||
for(int attempt = 0; attempt < MaxRetryAttempts; attempt++)
|
||||
{
|
||||
if(OperationMode == 0 && LoadCSVData(filename, data, dataIndex, lastUpdate, currentTime))
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
if(OperationMode == 1 && LoadAPIRequest(data, dataIndex, lastUpdate, currentTime, tf))
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(attempt < MaxRetryAttempts - 1)
|
||||
{
|
||||
Print("Load ", (OperationMode == 0 ? "test" : "live"), " data failed, retrying in 1 minute... Attempt ", attempt + 1, "/", MaxRetryAttempts);
|
||||
Sleep(RETRY_DELAY_MS);
|
||||
}
|
||||
}
|
||||
|
||||
if(result)
|
||||
{
|
||||
lastUpdate = currentTime;
|
||||
AnalyzeAndTrade(tf, data);
|
||||
ManageOpenOrders(tf);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Failed to load ", (OperationMode == 0 ? "test" : "live"), " data after ", MaxRetryAttempts, " attempts");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Load CSV data using circular buffer |
|
||||
//+------------------------------------------------------------------+
|
||||
bool LoadCSVData(string filePath, SignalData &data[], int &index, datetime &lastUpdate, datetime currentTime)
|
||||
{
|
||||
|
||||
int handle = FileOpen(filePath, FILE_READ|FILE_CSV|FILE_ANSI, '\n');
|
||||
if(handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Unable to load file");
|
||||
return false;
|
||||
}
|
||||
Print("Reading file");
|
||||
|
||||
bool updated = false;
|
||||
while(!FileIsEnding(handle))
|
||||
{
|
||||
string line = FileReadString(handle);
|
||||
StringReplace(line, "\r", "");
|
||||
string parts[];
|
||||
|
||||
if(StringSplit(line, ',', parts) == 3)
|
||||
{
|
||||
datetime dt = StringToTime(parts[0]);
|
||||
if(dt > lastUpdate && dt <= currentTime)
|
||||
{
|
||||
Print("Data found for ", currentTime);
|
||||
SignalData newData;
|
||||
newData.time = dt;
|
||||
newData.position = (SIGNAL_POSITION)StringToInteger(parts[1]);
|
||||
newData.weight = (TREND_WEIGHT)StringToInteger(parts[2]);
|
||||
|
||||
// Update circular buffer
|
||||
int size = ArraySize(data);
|
||||
if(size < MaxDataSize)
|
||||
ArrayResize(data, size + 1);
|
||||
for(int x = size - 1; x > 0; x--)
|
||||
data[x] = data[x - 1];
|
||||
data[0] = newData;
|
||||
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Print("Done read csv");
|
||||
|
||||
FileClose(handle);
|
||||
return updated;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Function: LoadAPIRequest |
|
||||
//| Description: Calls FxChartAI API via GET and parses the JSON |
|
||||
//| response into an array of SignalData. |
|
||||
//+------------------------------------------------------------------+
|
||||
bool LoadAPIRequest(SignalData &data[], int &index, datetime &lastUpdate, datetime currentTime, ENUM_TIMEFRAMES timeframe)
|
||||
{
|
||||
// Convert timeframe to string representation
|
||||
string tfString;
|
||||
switch(timeframe)
|
||||
{
|
||||
case PERIOD_M10:
|
||||
tfString = "M10";
|
||||
break;
|
||||
case PERIOD_H1:
|
||||
tfString = "H1";
|
||||
break;
|
||||
default:
|
||||
tfString = "M10";
|
||||
break;
|
||||
}
|
||||
|
||||
// Construct API URL
|
||||
string url = BuildAPIRequestURL(timeframe, currentTime);
|
||||
|
||||
// Send HTTP GET request
|
||||
uchar result[];
|
||||
string headers;
|
||||
string requestMethod = "GET";
|
||||
int timeout = 5000;
|
||||
string resulthHeaders;
|
||||
char postData[]; // GET request uses empty POST data
|
||||
int response = WebRequest(requestMethod,url, headers, timeout, postData, result, resulthHeaders);
|
||||
Print(CharArrayToString(result));
|
||||
|
||||
if(response != 200)
|
||||
{
|
||||
Print("API request failed with error: ", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse JSON response
|
||||
CJAVal parser;
|
||||
string jsonStr = CharArrayToString(result);
|
||||
|
||||
if(!parser.Deserialize(jsonStr))
|
||||
{
|
||||
Print("Failed to parse JSON response");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(parser.m_type != jtARRAY)
|
||||
{
|
||||
Print("Invalid JSON structure received");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dataUpdated = false;
|
||||
|
||||
// Process array in reverse chronological order
|
||||
for(int i = parser.Size() - 1; i >= 0; i--)
|
||||
{
|
||||
CJAVal *item = parser[i];
|
||||
|
||||
// Parse trade date
|
||||
string dateStr = item["tradedate"].ToStr();
|
||||
StringReplace(dateStr, "-", ".");
|
||||
Print(dateStr);
|
||||
datetime tradeDate = StringToTime(dateStr);
|
||||
|
||||
if(tradeDate <= lastUpdate)
|
||||
continue;
|
||||
|
||||
// Create new signal data entry
|
||||
SignalData newData;
|
||||
newData.time = tradeDate;
|
||||
newData.position = (SIGNAL_POSITION)item["position"].ToInt();
|
||||
newData.weight = (TREND_WEIGHT)item["weight"].ToInt();
|
||||
|
||||
// Update data array with new entry
|
||||
int size = ArraySize(data);
|
||||
if(size < MaxDataSize)
|
||||
ArrayResize(data, size + 1);
|
||||
|
||||
// Shift existing elements
|
||||
for(int j = size - 1; j > 0; j--)
|
||||
data[j] = data[j - 1];
|
||||
|
||||
data[0] = newData;
|
||||
lastUpdate = tradeDate;
|
||||
dataUpdated = true;
|
||||
}
|
||||
|
||||
// Maintain maximum data size
|
||||
if(ArraySize(data) > MaxDataSize)
|
||||
ArrayResize(data, MaxDataSize);
|
||||
|
||||
if(ArraySize(data) > 0)
|
||||
Print("Index0: "+data[0].time);
|
||||
|
||||
return dataUpdated;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Build API Request URL |
|
||||
//+------------------------------------------------------------------+
|
||||
string BuildAPIRequestURL(ENUM_TIMEFRAMES tf, datetime time)
|
||||
{
|
||||
string timeframeStr = (tf == PERIOD_M10) ? "M10" : "H1";
|
||||
string formattedTime = TimeToString(time, TIME_DATE) + "T" +
|
||||
TimeToString(time, TIME_MINUTES);
|
||||
|
||||
return StringFormat(
|
||||
"https://chartapi.fxchartai.com/easignal?currencypair=%s&size=%d&tradedate=%s&timeframe=%s",
|
||||
_Symbol, MaxDataSize, formattedTime, timeframeStr
|
||||
);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trend confirmation check |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsTrendConfirmed(const SignalData &data[], int requiredConsecutive, SIGNAL_POSITION &result)
|
||||
{
|
||||
int count = 0;
|
||||
SIGNAL_POSITION lastSignal = SIGNAL_NONE;
|
||||
|
||||
for(int i = 0; i < ArraySize(data); i++)
|
||||
{
|
||||
if(data[i].position == SIGNAL_NONE)
|
||||
continue;
|
||||
|
||||
if(data[i].position == lastSignal)
|
||||
{
|
||||
if(++count >= requiredConsecutive)
|
||||
{
|
||||
result = data[i].position;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
count = 1;
|
||||
lastSignal = data[i].position;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Order management |
|
||||
//+------------------------------------------------------------------+
|
||||
void DeletePendingOrders()
|
||||
{
|
||||
for(int i = OrdersTotal()-1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = OrderGetTicket(i);
|
||||
if(ticket <= 0)
|
||||
continue;
|
||||
|
||||
if(OrderGetInteger(ORDER_MAGIC) == MagicNumber &&
|
||||
(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP ||
|
||||
OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP))
|
||||
{
|
||||
MqlTradeRequest req = {};
|
||||
MqlTradeResult res = {};
|
||||
req.action = TRADE_ACTION_REMOVE;
|
||||
req.order = ticket;
|
||||
OrderSend(req, res);
|
||||
}
|
||||
}
|
||||
pendingOrderTicket = -1;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Candle tail signal detection |
|
||||
//+------------------------------------------------------------------+
|
||||
SIGNAL_POSITION GetCandleTailSignal(ENUM_TIMEFRAMES tf)
|
||||
{
|
||||
double open = iOpen(_Symbol, tf, 1);
|
||||
double close = iClose(_Symbol, tf, 1);
|
||||
double high = iHigh(_Symbol, tf, 1);
|
||||
double low = iLow(_Symbol, tf, 1);
|
||||
|
||||
if(close > open) // Bullish
|
||||
{
|
||||
double upperTail = high - close;
|
||||
double lowerTail = open - low;
|
||||
return (upperTail > lowerTail*2) ? SIGNAL_SELL :
|
||||
(lowerTail > upperTail*2) ? SIGNAL_BUY : SIGNAL_NONE;
|
||||
}
|
||||
|
||||
// Bearish
|
||||
double upperTail = high - open;
|
||||
double lowerTail = close - low;
|
||||
return (upperTail > lowerTail*2) ? SIGNAL_SELL :
|
||||
(lowerTail > upperTail*2) ? SIGNAL_BUY : SIGNAL_NONE;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trendline check |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CheckTrendline(ENUM_TIMEFRAMES tf, bool bullish)
|
||||
{
|
||||
double price = bullish ? iLow(_Symbol, tf, 1) : iHigh(_Symbol, tf, 1);
|
||||
datetime time = iTime(_Symbol, tf, 1);
|
||||
|
||||
int touches = 0;
|
||||
for(int i = 2; i <= 20; i++)
|
||||
{
|
||||
double testPrice = bullish ? iLow(_Symbol, tf, i) : iHigh(_Symbol, tf, i);
|
||||
datetime testTime = iTime(_Symbol, tf, i);
|
||||
|
||||
if((bullish && testPrice <= price) || (!bullish && testPrice >= price))
|
||||
{
|
||||
if(++touches >= 2)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
if(iTime(_Symbol, tf, i) < time)
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Execute trade |
|
||||
//+------------------------------------------------------------------+
|
||||
void ExecuteTrade(SIGNAL_POSITION signal, ENUM_TIMEFRAMES tf)
|
||||
{
|
||||
DeletePendingOrders();
|
||||
|
||||
double price = (signal == SIGNAL_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
|
||||
: SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double sl = StopLossPips * _Point * ((tf == PERIOD_H1) ? 10 : 1);
|
||||
double tp = TakeProfitPips * _Point * ((tf == PERIOD_H1) ? 10 : 1);
|
||||
|
||||
MqlTradeRequest req = {};
|
||||
MqlTradeResult res = {};
|
||||
req.action = TRADE_ACTION_PENDING;
|
||||
req.symbol = _Symbol;
|
||||
req.volume = LotSize;
|
||||
req.type = (signal == SIGNAL_BUY) ? ORDER_TYPE_BUY_STOP : ORDER_TYPE_SELL_STOP;
|
||||
req.price = price + ((signal == SIGNAL_BUY) ? 100*_Point : -100*_Point);
|
||||
req.sl = (signal == SIGNAL_BUY) ? req.price - sl : req.price + sl;
|
||||
req.tp = (signal == SIGNAL_BUY) ? req.price + tp : req.price - tp;
|
||||
req.magic = MagicNumber;
|
||||
|
||||
if(OrderSend(req, res))
|
||||
pendingOrderTicket = res.order;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Main trading logic |
|
||||
//+------------------------------------------------------------------+
|
||||
void AnalyzeAndTrade(ENUM_TIMEFRAMES tf, const SignalData &data[])
|
||||
{
|
||||
if(PositionsTotal() > 0)
|
||||
return;
|
||||
|
||||
SIGNAL_POSITION trendSignal;
|
||||
if(IsTrendConfirmed(data,ConfidenceLevel, trendSignal))
|
||||
{
|
||||
SIGNAL_POSITION candleSignal = GetCandleTailSignal(tf);
|
||||
if(candleSignal == trendSignal)
|
||||
ExecuteTrade(trendSignal, tf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Manage Open Positions |
|
||||
//+------------------------------------------------------------------+
|
||||
void ManageOpenOrders(ENUM_TIMEFRAMES timeframe)
|
||||
{
|
||||
// Process market positions
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket > 0)
|
||||
{
|
||||
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber)
|
||||
{
|
||||
string symbol = PositionGetString(POSITION_SYMBOL);
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double sl = PositionGetDouble(POSITION_SL);
|
||||
double tp = PositionGetDouble(POSITION_TP);
|
||||
double volume = PositionGetDouble(POSITION_VOLUME);
|
||||
double currentPrice = (posType == POSITION_TYPE_BUY) ?
|
||||
SymbolInfoDouble(symbol, SYMBOL_BID) :
|
||||
SymbolInfoDouble(symbol, SYMBOL_ASK);
|
||||
|
||||
// Check for TP/SL hit
|
||||
if((posType == POSITION_TYPE_BUY && currentPrice >= tp) ||
|
||||
(posType == POSITION_TYPE_SELL && currentPrice <= tp))
|
||||
{
|
||||
ClosePosition(ticket);
|
||||
}
|
||||
else
|
||||
if((posType == POSITION_TYPE_BUY && currentPrice <= sl) ||
|
||||
(posType == POSITION_TYPE_SELL && currentPrice >= sl))
|
||||
{
|
||||
ClosePosition(ticket);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Trailing stop logic
|
||||
UpdateTrailingStop(ticket, posType, currentPrice, timeframe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process pending orders
|
||||
for(int i = OrdersTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong orderTicket = OrderGetTicket(i);
|
||||
if(orderTicket > 0 && OrderGetInteger(ORDER_MAGIC) == MagicNumber)
|
||||
{
|
||||
CheckPendingOrderExpiry(orderTicket, timeframe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close position |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ClosePosition(ulong ticket)
|
||||
{
|
||||
MqlTradeRequest request = {};
|
||||
MqlTradeResult result = {};
|
||||
|
||||
request.action = TRADE_ACTION_DEAL;
|
||||
request.position = ticket;
|
||||
request.symbol = PositionGetString(POSITION_SYMBOL);
|
||||
request.volume = PositionGetDouble(POSITION_VOLUME);
|
||||
request.deviation = 5;
|
||||
request.type = (ENUM_ORDER_TYPE)(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
|
||||
request.price = (request.type == ORDER_TYPE_BUY) ?
|
||||
SymbolInfoDouble(request.symbol, SYMBOL_ASK) :
|
||||
SymbolInfoDouble(request.symbol, SYMBOL_BID);
|
||||
|
||||
if(OrderSend(request, result))
|
||||
{
|
||||
Print("Position closed: ", ticket);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error closing position: ", GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Perforrm Minor Trailing stop |
|
||||
//+------------------------------------------------------------------+
|
||||
bool performMinorTrail(ulong ticket, ENUM_POSITION_TYPE posType, double priceOpen, double currentSl, int trailingPips, ENUM_TIMEFRAMES timeframe)
|
||||
{
|
||||
MqlTradeRequest request = {};
|
||||
MqlTradeResult result = {};
|
||||
double newSl = 0.0;
|
||||
if(posType == POSITION_TYPE_BUY)
|
||||
{
|
||||
newSl = priceOpen + trailingPips * _Point;
|
||||
if(newSl > currentSl)
|
||||
{
|
||||
request.action = TRADE_ACTION_SLTP;
|
||||
request.position = ticket;
|
||||
request.symbol = PositionGetString(POSITION_SYMBOL);
|
||||
request.sl = newSl;
|
||||
request.tp = PositionGetDouble(POSITION_TP);
|
||||
if(OrderSend(request, result))
|
||||
{
|
||||
Print("Minor trailing stop updated for buy position");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newSl = priceOpen - trailingPips * _Point;
|
||||
if(newSl < currentSl || currentSl == 0)
|
||||
{
|
||||
request.action = TRADE_ACTION_SLTP;
|
||||
request.position = ticket;
|
||||
request.symbol = PositionGetString(POSITION_SYMBOL);
|
||||
request.sl = newSl;
|
||||
request.tp = PositionGetDouble(POSITION_TP);
|
||||
if(OrderSend(request, result))
|
||||
{
|
||||
Print("Minor trailing stop updated for sell position");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Perforrm Major Trailing stop |
|
||||
//+------------------------------------------------------------------+
|
||||
bool performMajorTrail(ulong ticket, ENUM_POSITION_TYPE posType, double currentPrice, double lastCandleSize, double currentSl, int trailingPips, ENUM_TIMEFRAMES timeframe)
|
||||
{
|
||||
MqlTradeRequest request = {};
|
||||
MqlTradeResult result = {};
|
||||
double newSl = 0.0;
|
||||
double newTrailSL = 0.0;
|
||||
if(posType == POSITION_TYPE_BUY)
|
||||
{
|
||||
newSl = currentPrice - lastCandleSize;
|
||||
newTrailSL = currentPrice - trailingPips * _Point;
|
||||
newSl = (newSl < newTrailSL) ? newTrailSL : newSl;
|
||||
if(newSl > currentSl)
|
||||
{
|
||||
request.action = TRADE_ACTION_SLTP;
|
||||
request.position = ticket;
|
||||
request.symbol = PositionGetString(POSITION_SYMBOL);
|
||||
request.sl = newSl;
|
||||
request.tp = PositionGetDouble(POSITION_TP);
|
||||
if(OrderSend(request, result))
|
||||
{
|
||||
Print("Major trailing stop updated for buy position");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Failed: Major trailing stop for buy position");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newSl = currentPrice + lastCandleSize;
|
||||
newTrailSL = currentPrice + trailingPips * _Point;
|
||||
newSl = (newSl > newTrailSL) ? newTrailSL : newSl;
|
||||
if(newSl < currentSl || currentSl == 0)
|
||||
{
|
||||
request.action = TRADE_ACTION_SLTP;
|
||||
request.position = ticket;
|
||||
request.symbol = PositionGetString(POSITION_SYMBOL);
|
||||
request.sl = newSl;
|
||||
request.tp = PositionGetDouble(POSITION_TP);
|
||||
if(OrderSend(request, result))
|
||||
{
|
||||
Print("Major trailing stop updated for sell position");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Failed: Major trailing stop for sell position");
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update Trailing stop |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateTrailingStop(ulong ticket, ENUM_POSITION_TYPE posType, double currentPrice, ENUM_TIMEFRAMES timeframe)
|
||||
{
|
||||
double currentSl = PositionGetDouble(POSITION_SL);
|
||||
double priceOpen = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double currentProfit = PositionGetDouble(POSITION_PROFIT);
|
||||
double lastCandleHigh = iHigh(Symbol(), timeframe, 1);
|
||||
double lastCandleLow = iLow(Symbol(), timeframe, 1);
|
||||
double lastCandleSize = MathAbs(lastCandleHigh - lastCandleLow);
|
||||
int candlesOpen = (int)((TimeCurrent() - PositionGetInteger(POSITION_TIME)) / PeriodSeconds(timeframe));
|
||||
|
||||
|
||||
if(candlesOpen > 1 && candlesOpen <= 3 && currentProfit > 0)
|
||||
{
|
||||
performMinorTrail(ticket, posType, priceOpen, currentSl, TrailingPips, timeframe);
|
||||
}
|
||||
else
|
||||
if(candlesOpen >= 4 && currentProfit > 0)
|
||||
{
|
||||
performMajorTrail(ticket, posType, currentPrice, lastCandleSize, currentSl, TrailingPips, timeframe);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Modify pending order |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ModifyPendingOrder(ulong ticket, double price, ENUM_TIMEFRAMES timeframe)
|
||||
{
|
||||
MqlTradeRequest request = {};
|
||||
MqlTradeResult result = {};
|
||||
|
||||
if(OrderSelect(ticket))
|
||||
{
|
||||
double stopLoss = (timeframe == PERIOD_M10) ? StopLossPips * _Point : StopLossPips * _Point * 10;
|
||||
double takeProfit = (timeframe == PERIOD_M10) ? TakeProfitPips * _Point : TakeProfitPips * _Point * 10;
|
||||
|
||||
request.action = TRADE_ACTION_MODIFY;
|
||||
request.order = ticket;
|
||||
request.price = price;
|
||||
request.sl = (OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP) ? price - stopLoss : price + stopLoss;
|
||||
request.tp = (OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP) ? price + takeProfit : price - takeProfit;
|
||||
request.deviation = 5;
|
||||
|
||||
if(OrderSend(request, result))
|
||||
{
|
||||
Print("Pending order modified successfully");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("ModifyPendingOrder::Error modifying orderrr: ", GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("ModifyPendingOrder::order select failed for ticket"+ticket);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check and delete expired pending orders |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckPendingOrderExpiry(ulong ticket, ENUM_TIMEFRAMES timeframe)
|
||||
{
|
||||
datetime expiration = OrderGetInteger(ORDER_TIME_EXPIRATION);
|
||||
if(expiration > 0 && expiration < TimeCurrent())
|
||||
{
|
||||
MqlTradeRequest request = {};
|
||||
MqlTradeResult result = {};
|
||||
|
||||
request.action = TRADE_ACTION_REMOVE;
|
||||
request.order = ticket;
|
||||
|
||||
if(OrderSend(request, result))
|
||||
{
|
||||
Print("Expired order removed: ", ticket);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error removing order: ", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Place pending order |
|
||||
//+------------------------------------------------------------------+
|
||||
ulong PlacePendingOrder(ENUM_ORDER_TYPE orderType, double price, ENUM_TIMEFRAMES timeframe)
|
||||
{
|
||||
MqlTradeRequest request = {};
|
||||
MqlTradeResult result = {};
|
||||
|
||||
double stopLoss = (timeframe == PERIOD_M10) ? StopLossPips * _Point : StopLossPips * _Point * 10;
|
||||
double takeProfit = (timeframe == PERIOD_M10) ? TakeProfitPips * _Point : TakeProfitPips * _Point * 10;
|
||||
|
||||
request.action = TRADE_ACTION_PENDING;
|
||||
request.symbol = _Symbol;
|
||||
request.volume = LotSize;
|
||||
request.type = orderType;
|
||||
request.price = price;
|
||||
request.sl = (orderType == ORDER_TYPE_BUY_STOP) ? price - stopLoss : price + stopLoss;
|
||||
request.tp = (orderType == ORDER_TYPE_BUY_STOP) ? price + takeProfit : price - takeProfit;
|
||||
request.deviation = 5;
|
||||
request.magic = MagicNumber;
|
||||
|
||||
if(OrderSend(request, result))
|
||||
{
|
||||
Print("Pending order placed: ", result.order);
|
||||
return result.order;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error placing order: ", GetLastError());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,339 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Mitigation Order Blocks EA.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
|
||||
#include <Trade/Trade.mqh>
|
||||
CTrade obj_Trade;
|
||||
|
||||
input double tradeLotSize = 0.01;
|
||||
input bool enableTrading = true;
|
||||
input bool enableTrailingStop = true;
|
||||
input double trailingStopPoints = 30;
|
||||
input double minProfitToTrail = 50;
|
||||
input int uniqueMagicNumber = 1234567;
|
||||
input int consolidationBars = 7;
|
||||
input double maxconsolidationSpread = 50;
|
||||
input int barstowaitafterbreakout = 3;
|
||||
input double impulseMultiplier = 1.0;
|
||||
input double stoplossDistance = 1500;
|
||||
input double takeProfitdistance = 1500;
|
||||
input color bullishOrderBlockColor = clrGreen;
|
||||
input color bearishOrderBlockColor = clrRed;
|
||||
input color mitigatedOrderBlockColor = clrGray;
|
||||
input color labelTextColor = clrBlack;
|
||||
|
||||
struct PriceAndIndex{
|
||||
double price;
|
||||
int index;
|
||||
};
|
||||
|
||||
PriceAndIndex rangeHighestHigh = {0,0};
|
||||
PriceAndIndex rangeLowestLow = {0,0};
|
||||
bool isBreakoutDetected = false;
|
||||
double lastImpulseLow = 0.0;
|
||||
double lastImpulseHigh = 0.0;
|
||||
int breakoutBarNumber = -1;
|
||||
datetime breakoutTimestamp = 0;
|
||||
string orderBlockNames[];
|
||||
string orderBlockLabels[];
|
||||
datetime orderBlockEndTimes[];
|
||||
bool orderblockMitigatedStatus[];
|
||||
bool isBullishImpulse = false;
|
||||
bool isBearishImpulse = false;
|
||||
|
||||
#define OB_Prefix "OB REC "
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit(){
|
||||
//---
|
||||
obj_Trade.SetExpertMagicNumber(uniqueMagicNumber);
|
||||
//---
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason){
|
||||
//---
|
||||
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick(){
|
||||
//---
|
||||
|
||||
if (enableTrailingStop){
|
||||
applyTrailingStop(trailingStopPoints,obj_Trade,uniqueMagicNumber);
|
||||
}
|
||||
|
||||
static bool isNewBar = false;
|
||||
int currentBarCount = iBars(_Symbol,_Period);
|
||||
static int previousBarCount = currentBarCount;
|
||||
if (previousBarCount == currentBarCount){
|
||||
isNewBar = false;
|
||||
}
|
||||
else if (previousBarCount != currentBarCount){
|
||||
isNewBar = true;
|
||||
previousBarCount = currentBarCount;
|
||||
}
|
||||
|
||||
if (!isNewBar){
|
||||
return;
|
||||
}
|
||||
|
||||
int startBarIndex = 1;
|
||||
|
||||
int chartscale = (int)ChartGetInteger(0,CHART_SCALE);
|
||||
int dynamicFontSize = 8+(chartscale*2);
|
||||
|
||||
if (!isBreakoutDetected){
|
||||
if (rangeHighestHigh.price == 0 && rangeLowestLow.price == 0){
|
||||
bool isConsolidated = true;
|
||||
for (int i=startBarIndex; i<startBarIndex+consolidationBars-1; i++){
|
||||
if (MathAbs(high(i) - high(i+1)) > maxconsolidationSpread * _Point){
|
||||
isConsolidated = false;
|
||||
break;
|
||||
}
|
||||
if (MathAbs(low(i) - low(i+1)) > maxconsolidationSpread * _Point){
|
||||
isConsolidated = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isConsolidated){
|
||||
rangeHighestHigh.price = high(startBarIndex);
|
||||
rangeHighestHigh.index = startBarIndex;
|
||||
for (int i=startBarIndex+1; i<startBarIndex+consolidationBars; i++){
|
||||
if (high(i) > rangeHighestHigh.price){
|
||||
rangeHighestHigh.price = high(i);
|
||||
rangeHighestHigh.index = i;
|
||||
}
|
||||
}
|
||||
rangeLowestLow.price = low(startBarIndex);
|
||||
rangeLowestLow.index = startBarIndex;
|
||||
for (int i=startBarIndex+1; i<startBarIndex+consolidationBars; i++){
|
||||
if (low(i) < rangeLowestLow.price){
|
||||
rangeLowestLow.price = low(i);
|
||||
rangeLowestLow.index = i;
|
||||
}
|
||||
}
|
||||
Print("Consolidation Range Established./nHighest High: ",rangeHighestHigh.price,
|
||||
", Lowest Low: ",rangeLowestLow.price);
|
||||
}
|
||||
}
|
||||
else {
|
||||
double currentHigh = high(1);
|
||||
double currentLow = low(1);
|
||||
if (currentHigh <= rangeHighestHigh.price && currentLow >= rangeLowestLow.price){
|
||||
Print("Range EXTENDED: High = ",currentHigh, ", Low = ",currentLow);
|
||||
}
|
||||
else {
|
||||
Print("No extension: Bar outside range.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rangeHighestHigh.price > 0 && rangeLowestLow.price > 0){
|
||||
double currentClosePrice = close(1);
|
||||
if (currentClosePrice > rangeHighestHigh.price){
|
||||
Print("Upward Breakout at ",currentClosePrice, " > ",rangeHighestHigh.price);
|
||||
isBreakoutDetected = true;
|
||||
}
|
||||
else if (currentClosePrice < rangeLowestLow.price){
|
||||
Print("Downward Breakout at ",currentClosePrice, " < ",rangeLowestLow.price);
|
||||
isBreakoutDetected = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isBreakoutDetected){
|
||||
Print("Breakout detected. Resetting for the next range.");
|
||||
breakoutBarNumber = 1;
|
||||
breakoutTimestamp = TimeCurrent();
|
||||
lastImpulseHigh = rangeHighestHigh.price;
|
||||
lastImpulseLow = rangeLowestLow.price;
|
||||
isBreakoutDetected = false;
|
||||
rangeHighestHigh.price = 0;
|
||||
rangeLowestLow.price = 0;
|
||||
rangeHighestHigh.index = 0;
|
||||
rangeLowestLow.index = 0;
|
||||
}
|
||||
|
||||
if (breakoutBarNumber >= 0 && TimeCurrent() > breakoutTimestamp+barstowaitafterbreakout*PeriodSeconds()){
|
||||
double impulseRange = lastImpulseHigh - lastImpulseLow;
|
||||
double impulseThresholdPrice = impulseRange * impulseMultiplier;
|
||||
isBullishImpulse = false;
|
||||
isBearishImpulse = false;
|
||||
|
||||
for (int i=1; i<=barstowaitafterbreakout; i++){
|
||||
double closePrice = close(i);
|
||||
if (closePrice >= lastImpulseHigh+impulseThresholdPrice){
|
||||
isBullishImpulse = true;
|
||||
Print("Impulsive upward move: ",closePrice," >= ",lastImpulseHigh+impulseThresholdPrice);
|
||||
break;
|
||||
}
|
||||
else if (closePrice <= lastImpulseLow-impulseThresholdPrice){
|
||||
isBearishImpulse = true;
|
||||
Print("Impulsive downward move: ",closePrice," <= ",lastImpulseLow-impulseThresholdPrice);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isBullishImpulse && !isBearishImpulse){
|
||||
Print("No impulsive movement detected.");
|
||||
}
|
||||
|
||||
bool isOrderBlockValid = isBearishImpulse || isBullishImpulse;
|
||||
|
||||
if (isOrderBlockValid){
|
||||
datetime blockStartTime = iTime(_Symbol,_Period,consolidationBars+barstowaitafterbreakout+1);
|
||||
double blockTopPrice = lastImpulseHigh;
|
||||
int visibleBarsOnchart = (int)ChartGetInteger(0,CHART_VISIBLE_BARS);
|
||||
datetime blockEndTime = blockStartTime+(visibleBarsOnchart/1)*PeriodSeconds();
|
||||
double blockBottomPrice = lastImpulseLow;
|
||||
string orderBlockName = OB_Prefix+"("+TimeToString(blockStartTime)+")";
|
||||
color orderBlockColor = isBullishImpulse ? bullishOrderBlockColor : bearishOrderBlockColor;
|
||||
string orderBlockLabel = isBullishImpulse ? "Bullish OB" : "Bearish OB";
|
||||
|
||||
if (ObjectFind(0, orderBlockName) < 0){
|
||||
ObjectCreate(0,orderBlockName,OBJ_RECTANGLE,0,blockStartTime,blockTopPrice,blockEndTime,blockBottomPrice);
|
||||
ObjectSetInteger(0,orderBlockName,OBJPROP_TIME,0,blockStartTime);
|
||||
ObjectSetDouble(0,orderBlockName,OBJPROP_PRICE,0,blockTopPrice);
|
||||
ObjectSetInteger(0,orderBlockName,OBJPROP_TIME,1,blockEndTime);
|
||||
ObjectSetDouble(0,orderBlockName,OBJPROP_PRICE,1,blockBottomPrice);
|
||||
ObjectSetInteger(0,orderBlockName,OBJPROP_FILL,true);
|
||||
ObjectSetInteger(0,orderBlockName,OBJPROP_COLOR,orderBlockColor);
|
||||
ObjectSetInteger(0,orderBlockName,OBJPROP_BACK,false);
|
||||
|
||||
datetime labelTime = blockStartTime + (blockEndTime-blockStartTime)/2;
|
||||
double labelPrice = (blockTopPrice+blockBottomPrice)/2;
|
||||
string labelObjectName = orderBlockName+orderBlockLabel;
|
||||
if (ObjectFind(0,labelObjectName) < 0){
|
||||
ObjectCreate(0,labelObjectName,OBJ_TEXT,0,labelTime,labelPrice);
|
||||
ObjectSetString(0,labelObjectName,OBJPROP_TEXT,orderBlockLabel);
|
||||
ObjectSetInteger(0,labelObjectName,OBJPROP_COLOR,labelTextColor);
|
||||
ObjectSetInteger(0,labelObjectName,OBJPROP_ANCHOR,ANCHOR_CENTER);
|
||||
ObjectSetInteger(0,labelObjectName,OBJPROP_FONTSIZE,dynamicFontSize);
|
||||
}
|
||||
ChartRedraw(0);
|
||||
|
||||
ArrayResize(orderBlockNames,ArraySize(orderBlockNames)+1);
|
||||
orderBlockNames[ArraySize(orderBlockNames)-1] = orderBlockName;
|
||||
ArrayResize(orderBlockLabels,ArraySize(orderBlockLabels)+1);
|
||||
orderBlockLabels[ArraySize(orderBlockLabels)-1] = labelObjectName;
|
||||
ArrayResize(orderBlockEndTimes,ArraySize(orderBlockEndTimes)+1);
|
||||
orderBlockEndTimes[ArraySize(orderBlockEndTimes)-1] = blockEndTime;
|
||||
ArrayResize(orderblockMitigatedStatus,ArraySize(orderblockMitigatedStatus)+1);
|
||||
orderblockMitigatedStatus[ArraySize(orderblockMitigatedStatus)-1] = false;
|
||||
|
||||
Print("Order Block created: ",orderBlockName);
|
||||
}
|
||||
}
|
||||
breakoutBarNumber = -1;
|
||||
breakoutTimestamp = 0;
|
||||
lastImpulseHigh = 0;
|
||||
lastImpulseLow = 0;
|
||||
isBullishImpulse = false;
|
||||
isBearishImpulse = false;
|
||||
}
|
||||
|
||||
for (int j=ArraySize(orderBlockNames)-1; j>=0; j--){
|
||||
string currentOrderBlockName = orderBlockNames[j];
|
||||
string currentOrderBlockLabel = orderBlockLabels[j];
|
||||
bool doesOrderBlockExist = false;
|
||||
|
||||
double orderBlockHigh = ObjectGetDouble(0,currentOrderBlockName,OBJPROP_PRICE,0);
|
||||
double orderBlockLow = ObjectGetDouble(0,currentOrderBlockName,OBJPROP_PRICE,1);
|
||||
datetime orderBlockStartTime = (datetime)ObjectGetInteger(0,currentOrderBlockName,OBJPROP_TIME,0);
|
||||
datetime orderBlockEndTime = (datetime)ObjectGetInteger(0,currentOrderBlockName,OBJPROP_TIME,1);
|
||||
color orderBlockCurrentColor = (color)ObjectGetInteger(0,currentOrderBlockName,OBJPROP_COLOR);
|
||||
|
||||
if (time(1) < orderBlockEndTime){
|
||||
doesOrderBlockExist = true;
|
||||
}
|
||||
|
||||
double currentAskPrice = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
|
||||
double currentBidPrice = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
|
||||
|
||||
if (enableTrading && orderBlockCurrentColor == bullishOrderBlockColor && close(1) < orderBlockLow && !orderblockMitigatedStatus[j]){
|
||||
double entryPrice = currentBidPrice;
|
||||
double stoplossPrice = entryPrice+stoplossDistance*_Point;
|
||||
double takeprofitPrice = entryPrice-takeProfitdistance*_Point;
|
||||
obj_Trade.Sell(tradeLotSize,_Symbol,entryPrice,stoplossPrice,takeprofitPrice);
|
||||
orderblockMitigatedStatus[j] = true;
|
||||
ObjectSetInteger(0,currentOrderBlockName,OBJPROP_COLOR,mitigatedOrderBlockColor);
|
||||
string blockDescription = "Bullish Order Block";
|
||||
string textObjectName = currentOrderBlockName+blockDescription;
|
||||
ObjectSetString(0,currentOrderBlockLabel,OBJPROP_TEXT,"Mitigated "+blockDescription);
|
||||
|
||||
Print("Sell trade entered upon mitigation of the bullish OB: ",currentOrderBlockName);
|
||||
}
|
||||
else if (enableTrading && orderBlockCurrentColor == bearishOrderBlockColor && close(1) > orderBlockHigh && !orderblockMitigatedStatus[j]){
|
||||
double entryPrice = currentAskPrice;
|
||||
double stoplossPrice = entryPrice-stoplossDistance*_Point;
|
||||
double takeprofitPrice = entryPrice+takeProfitdistance*_Point;
|
||||
obj_Trade.Buy(tradeLotSize,_Symbol,entryPrice,stoplossPrice,takeprofitPrice);
|
||||
orderblockMitigatedStatus[j] = true;
|
||||
ObjectSetInteger(0,currentOrderBlockName,OBJPROP_COLOR,mitigatedOrderBlockColor);
|
||||
string blockDescription = "Bearish Order Block";
|
||||
string textObjectName = currentOrderBlockName+blockDescription;
|
||||
ObjectSetString(0,currentOrderBlockLabel,OBJPROP_TEXT,"Mitigated "+blockDescription);
|
||||
|
||||
Print("Buy trade entered upon mitigation of the bearish OB: ",currentOrderBlockName);
|
||||
}
|
||||
|
||||
if (!doesOrderBlockExist){
|
||||
bool removedName = ArrayRemove(orderBlockNames,j,1);
|
||||
bool removedLabel = ArrayRemove(orderBlockLabels,j,1);
|
||||
bool removedTime = ArrayRemove(orderBlockEndTimes,j,1);
|
||||
bool removedStatus = ArrayRemove(orderblockMitigatedStatus,j,1);
|
||||
if (removedName && removedTime && removedStatus && removedLabel){
|
||||
Print("Success removing OB data from arrays at index ",j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
double high (int index) {return iHigh(_Symbol,_Period,index);}
|
||||
double low (int index) {return iLow(_Symbol,_Period,index);}
|
||||
double open (int index) {return iOpen(_Symbol,_Period,index);}
|
||||
double close (int index) {return iClose(_Symbol,_Period,index);}
|
||||
datetime time (int index) {return iTime(_Symbol,_Period,index);}
|
||||
|
||||
void applyTrailingStop(double trailingPoints, CTrade &trade_object, int magicNo = 0){
|
||||
double buyStopLoss = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID)-trailingPoints*_Point,_Digits);
|
||||
double sellStopLoss = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK)+trailingPoints*_Point,_Digits);
|
||||
|
||||
for (int i=PositionsTotal()-1; i>=0; i--){
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if (ticket > 0){
|
||||
if (PositionSelectByTicket(ticket)){
|
||||
if (PositionGetString(POSITION_SYMBOL)==_Symbol &&
|
||||
(magicNo == 0 || PositionGetInteger(POSITION_MAGIC)==magicNo)
|
||||
){
|
||||
if (PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY &&
|
||||
buyStopLoss > PositionGetDouble(POSITION_PRICE_OPEN) &&
|
||||
(buyStopLoss > PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL) == 0)
|
||||
){
|
||||
trade_object.PositionModify(ticket,buyStopLoss,PositionGetDouble(POSITION_TP));
|
||||
}
|
||||
else if (PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL &&
|
||||
sellStopLoss < PositionGetDouble(POSITION_PRICE_OPEN) &&
|
||||
(sellStopLoss < PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL) == 0)
|
||||
){
|
||||
trade_object.PositionModify(ticket,sellStopLoss,PositionGetDouble(POSITION_TP));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,175 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| prueba.mq5 |
|
||||
//| Copyright 2020, MetaQuotes Software Corp. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2020, MetaQuotes Software Corp."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
//+------------------------------------------------------------------+
|
||||
//| Include |
|
||||
//+------------------------------------------------------------------+
|
||||
#include <Expert\Expert.mqh>
|
||||
//--- available signals
|
||||
#include <Expert\Signal\SignalMACD.mqh>
|
||||
//--- available trailing
|
||||
#include <Expert\Trailing\TrailingNone.mqh>
|
||||
//--- available money management
|
||||
#include <Expert\Money\MoneyFixedLot.mqh>
|
||||
//+------------------------------------------------------------------+
|
||||
//| Inputs |
|
||||
//+------------------------------------------------------------------+
|
||||
//--- inputs for expert
|
||||
input string Expert_Title ="prueba"; // Document name
|
||||
ulong Expert_MagicNumber =2727; //
|
||||
bool Expert_EveryTick =false; //
|
||||
//--- inputs for main signal
|
||||
input int Signal_ThresholdOpen =10; // Signal threshold value to open [0...100]
|
||||
input int Signal_ThresholdClose =10; // Signal threshold value to close [0...100]
|
||||
input double Signal_PriceLevel =0.0; // Price level to execute a deal
|
||||
input double Signal_StopLevel =50.0; // Stop Loss level (in points)
|
||||
input double Signal_TakeLevel =50.0; // Take Profit level (in points)
|
||||
input int Signal_Expiration =4; // Expiration of pending orders (in bars)
|
||||
input int Signal_MACD_PeriodFast =12; // MACD(12,24,9,PRICE_CLOSE) Period of fast EMA
|
||||
input int Signal_MACD_PeriodSlow =24; // MACD(12,24,9,PRICE_CLOSE) Period of slow EMA
|
||||
input int Signal_MACD_PeriodSignal=9; // MACD(12,24,9,PRICE_CLOSE) Period of averaging of difference
|
||||
input ENUM_APPLIED_PRICE Signal_MACD_Applied =PRICE_CLOSE; // MACD(12,24,9,PRICE_CLOSE) Prices series
|
||||
input double Signal_MACD_Weight =1.0; // MACD(12,24,9,PRICE_CLOSE) Weight [0...1.0]
|
||||
//--- inputs for money
|
||||
input double Money_FixLot_Percent =10.0; // Percent
|
||||
input double Money_FixLot_Lots =0.1; // Fixed volume
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global expert object |
|
||||
//+------------------------------------------------------------------+
|
||||
CExpert ExtExpert;
|
||||
//+------------------------------------------------------------------+
|
||||
//| Initialization function of the expert |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
//--- Initializing expert
|
||||
if(!ExtExpert.Init(Symbol(),Period(),Expert_EveryTick,Expert_MagicNumber))
|
||||
{
|
||||
//--- failed
|
||||
printf(__FUNCTION__+": error initializing expert");
|
||||
ExtExpert.Deinit();
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
//--- Creating signal
|
||||
CExpertSignal *signal=new CExpertSignal;
|
||||
if(signal==NULL)
|
||||
{
|
||||
//--- failed
|
||||
printf(__FUNCTION__+": error creating signal");
|
||||
ExtExpert.Deinit();
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
//---
|
||||
ExtExpert.InitSignal(signal);
|
||||
signal.ThresholdOpen(Signal_ThresholdOpen);
|
||||
signal.ThresholdClose(Signal_ThresholdClose);
|
||||
signal.PriceLevel(Signal_PriceLevel);
|
||||
signal.StopLevel(Signal_StopLevel);
|
||||
signal.TakeLevel(Signal_TakeLevel);
|
||||
signal.Expiration(Signal_Expiration);
|
||||
//--- Creating filter CSignalMACD
|
||||
CSignalMACD *filter0=new CSignalMACD;
|
||||
if(filter0==NULL)
|
||||
{
|
||||
//--- failed
|
||||
printf(__FUNCTION__+": error creating filter0");
|
||||
ExtExpert.Deinit();
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
signal.AddFilter(filter0);
|
||||
//--- Set filter parameters
|
||||
filter0.PeriodFast(Signal_MACD_PeriodFast);
|
||||
filter0.PeriodSlow(Signal_MACD_PeriodSlow);
|
||||
filter0.PeriodSignal(Signal_MACD_PeriodSignal);
|
||||
filter0.Applied(Signal_MACD_Applied);
|
||||
filter0.Weight(Signal_MACD_Weight);
|
||||
//--- Creation of trailing object
|
||||
CTrailingNone *trailing=new CTrailingNone;
|
||||
if(trailing==NULL)
|
||||
{
|
||||
//--- failed
|
||||
printf(__FUNCTION__+": error creating trailing");
|
||||
ExtExpert.Deinit();
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
//--- Add trailing to expert (will be deleted automatically))
|
||||
if(!ExtExpert.InitTrailing(trailing))
|
||||
{
|
||||
//--- failed
|
||||
printf(__FUNCTION__+": error initializing trailing");
|
||||
ExtExpert.Deinit();
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
//--- Set trailing parameters
|
||||
//--- Creation of money object
|
||||
CMoneyFixedLot *money=new CMoneyFixedLot;
|
||||
if(money==NULL)
|
||||
{
|
||||
//--- failed
|
||||
printf(__FUNCTION__+": error creating money");
|
||||
ExtExpert.Deinit();
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
//--- Add money to expert (will be deleted automatically))
|
||||
if(!ExtExpert.InitMoney(money))
|
||||
{
|
||||
//--- failed
|
||||
printf(__FUNCTION__+": error initializing money");
|
||||
ExtExpert.Deinit();
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
//--- Set money parameters
|
||||
money.Percent(Money_FixLot_Percent);
|
||||
money.Lots(Money_FixLot_Lots);
|
||||
//--- Check all trading objects parameters
|
||||
if(!ExtExpert.ValidationSettings())
|
||||
{
|
||||
//--- failed
|
||||
ExtExpert.Deinit();
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
//--- Tuning of all necessary indicators
|
||||
if(!ExtExpert.InitIndicators())
|
||||
{
|
||||
//--- failed
|
||||
printf(__FUNCTION__+": error initializing indicators");
|
||||
ExtExpert.Deinit();
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
//--- ok
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Deinitialization function of the expert |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
ExtExpert.Deinit();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| "Tick" event handler function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
ExtExpert.OnTick();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| "Trade" event handler function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTrade()
|
||||
{
|
||||
ExtExpert.OnTrade();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| "Timer" event handler function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTimer()
|
||||
{
|
||||
ExtExpert.OnTimer();
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,483 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| AlphaStrategyV1.mq4 |
|
||||
//| Copyright 2020, MetaQuotes Software Corp. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2020, MetaQuotes Software Corp."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
#include <Mql4Book\Timer.mqh>
|
||||
#include <NNFX\Indicators\Baseline\NiKijun.mqh>
|
||||
#include <NNFX\Indicators\NiSSLActivator.mqh>
|
||||
#include <NNFX\Indicators\Confirmation\NiASH.mqh>
|
||||
#include <NNFX\Indicators\Volumen\NiWAE.mqh>
|
||||
#include <NNFX\Indicators\Exit\NiRex.mqh>
|
||||
#include <NNFX\Indicators\NiNone.mqh>
|
||||
#include <NNFX\Money\NiMoney.mqh>
|
||||
#include <NNFX\Money\NiMoneyScaleOut.mqh>
|
||||
#include <NNFX\functions.mqh>
|
||||
|
||||
|
||||
//--- inputs
|
||||
input int InpMagic = 1024;
|
||||
input int Deviation = 50;
|
||||
|
||||
input string x7 = "------Special Rules-----";
|
||||
input bool ApplySevenCandleRule = false;
|
||||
input bool ApplyOneCandleRule = true;
|
||||
input bool ApplyPullbackRule = true;
|
||||
input bool ApplyContinuationRule = true;
|
||||
|
||||
|
||||
input string x = "----Baseline-----";
|
||||
input int InpKijun_b = 26; //InpKijun
|
||||
|
||||
input string x2 = "------C1-----------";
|
||||
input int InpPeriods_C1 = 14; // InpPeriods
|
||||
input ENUM_MA_METHOD InpMethod_C1 = MODE_SMA; //InpMethod
|
||||
|
||||
input string x3 = "------C2-----------";
|
||||
input string InpModeStr_C2="Mode: 0 - RSI, 1 - Stoch";
|
||||
input int InpMode_C2=0; // InpMode
|
||||
input int InpLength_C2=9; //InpLength
|
||||
input int InpSmooth_Length_C2=2; // InpSmooth_Length
|
||||
input int InpPrice_C2=0; // InpPrice
|
||||
// Applied price
|
||||
// 0 - Close
|
||||
// 1 - Open
|
||||
// 2 - High
|
||||
// 3 - Low
|
||||
// 4 - Median
|
||||
// 5 - Typical
|
||||
// 6 - Weighted
|
||||
input int InpMethod_C2=0;
|
||||
// 0 - SMA
|
||||
// 1 - EMA
|
||||
// 2 - SMMA
|
||||
// 3 - LWMA
|
||||
input string x4 = "---------Volumen------";
|
||||
input int InpSensetive_v = 150; //InpSensitive
|
||||
input int InpDeadZonePip_v = 30; // InpDeadZonePip
|
||||
input int InpExplosionPower_v = 15; // InpExplosionPower
|
||||
input int InpTrendPower_v = 15; // InpTrendPower
|
||||
|
||||
input string x5 = "---------Exit---------";
|
||||
input int InpSmoothing_Length_x=14; // InpSmoothing_Length
|
||||
input int InpSmoothing_Method_x=0; // InpSmoothing_Method
|
||||
// 0 - SMA
|
||||
// 1 - EMA
|
||||
// 2 - SMMA
|
||||
// 3 - LWMA
|
||||
input int InpSignal_Length_x=14; // InpSignal_Length
|
||||
input int InpSignal_Method_x=0; // InpSignal_Method
|
||||
// 0 - SMA
|
||||
// 1 - EMA
|
||||
// 2 - SMMA
|
||||
// 3 - LWMA
|
||||
input string x6 = "---------Monet management------";
|
||||
input int InpTakeProfit = 100;
|
||||
input int InpStopLoss = 100;
|
||||
input double InpLotSize = 0.05;
|
||||
|
||||
//--- global variables
|
||||
CNewBar NewBar;
|
||||
string symbol;
|
||||
int period;
|
||||
|
||||
bool longPosition;
|
||||
bool shortPosition;
|
||||
bool isPositionOpened;
|
||||
bool OneCandleForLong;
|
||||
bool OneCandleForShort;
|
||||
OrderState orderState;
|
||||
|
||||
EnterTime forLong;
|
||||
EnterTime forShort;
|
||||
TrendStatus trendStatus;
|
||||
|
||||
NiKijun *baseline;
|
||||
/*
|
||||
NiSSLActivator *c1;
|
||||
NiASH *c2;
|
||||
NiWAE *volumeIndicator;
|
||||
NiRex *exitIndicator;
|
||||
*/
|
||||
|
||||
NiNone *c1;
|
||||
NiNone *c2;
|
||||
NiNone *volumeIndicator;
|
||||
NiNone *exitIndicator;
|
||||
|
||||
|
||||
NiMoneyScaleOut *money;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
//---
|
||||
orderState.LongPosition = false;
|
||||
orderState.ShortPosition = false;
|
||||
orderState.OrderStatus = false;
|
||||
|
||||
symbol = _Symbol;
|
||||
period = _Period;
|
||||
|
||||
baseline = new NiKijun(InpKijun_b);
|
||||
/*
|
||||
c1 = new NiSSLActivator(InpPeriods_C1,InpMethod_C1);
|
||||
c2 = new NiASH(InpModeStr_C2,InpMode_C2,InpLength_C2,InpSmooth_Length_C2,InpPrice_C2,InpMethod_C2);
|
||||
volumeIndicator = new NiWAE(InpSensetive_v,InpDeadZonePip_v,InpExplosionPower_v,InpTrendPower_v);
|
||||
exitIndicator = new NiRex(InpSmoothing_Length_x,InpSmoothing_Method_x,InpSignal_Length_x,InpSignal_Method_x);
|
||||
*/
|
||||
|
||||
c1 = new NiNone();
|
||||
c2 = new NiNone();
|
||||
volumeIndicator = new NiNone();
|
||||
exitIndicator = new NiNone();
|
||||
|
||||
baseline.InitIndicator(symbol,period);
|
||||
c1.InitIndicator(symbol,period);
|
||||
c2.InitIndicator(symbol,period);
|
||||
volumeIndicator.InitIndicator(symbol,period);
|
||||
exitIndicator.InitIndicator(symbol,period);
|
||||
|
||||
//money = new NiMoney(InpLotSize,InpTakeProfit,InpStopLoss,InpMagic,Deviation);
|
||||
money = new NiMoneyScaleOut(InpLotSize,InpMagic,Deviation);
|
||||
|
||||
|
||||
//---
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
//---
|
||||
delete baseline;
|
||||
delete c1;
|
||||
delete c2;
|
||||
delete volumeIndicator;
|
||||
delete exitIndicator;
|
||||
delete money;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
//---Detect New Bar
|
||||
bool isNewBar = NewBar.checkNewBar(_Symbol,_Period);
|
||||
//datetime t = iTime(_Symbol,_Period,0);
|
||||
|
||||
//Print("now: " + t);
|
||||
if(!isNewBar){
|
||||
return;
|
||||
}
|
||||
|
||||
//Print("On new Bar");
|
||||
OnBar();
|
||||
|
||||
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|OnBar function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnBar()
|
||||
{
|
||||
|
||||
//---Update Buffers
|
||||
Refresh();
|
||||
//---Exit rules
|
||||
if(money.isOpenedPosition()){
|
||||
manageClose();
|
||||
}
|
||||
|
||||
//---Entry Rules
|
||||
if(!money.isOpenedPosition()){
|
||||
manageOpen();
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Open Positions: Entry rules |
|
||||
//+------------------------------------------------------------------+
|
||||
void manageOpen()
|
||||
{
|
||||
|
||||
|
||||
manageLong();
|
||||
|
||||
manageShort();
|
||||
|
||||
if(money.isOpenedPosition()) {
|
||||
//Print("Position openend...");
|
||||
//Print("position status: "+orderState.OrderStatus);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Checks conditions if is possible open long positions
|
||||
*/
|
||||
bool manageLong()
|
||||
{
|
||||
bool LongSignal = false;
|
||||
bool InSevenCandles = false;
|
||||
|
||||
//--- Triggers an entry signal event, then checks all rules
|
||||
if(c1.entryLong() || c2.entryLong()|| baseline.entryLong() || (OneCandleForLong && ApplyOneCandleRule)){
|
||||
//--- Apply Seven Candle rule
|
||||
if(ApplySevenCandleRule)
|
||||
{ InSevenCandles = SevenCandleRuleForLong(); }
|
||||
else{ InSevenCandles = false;}
|
||||
|
||||
//-- Checks all Indicators for a long trade
|
||||
if((InSevenCandles && ApplySevenCandleRule) || !ApplySevenCandleRule ){
|
||||
LongSignal = checkLongConditions();
|
||||
if(OneCandleForLong && LongSignal) Print("Trade ok by one candle rule");
|
||||
}
|
||||
|
||||
//--- Apply One candle Rule
|
||||
if(!LongSignal && !OneCandleForLong && ApplyOneCandleRule)
|
||||
{ OneCandleForLong = true;}
|
||||
else if(OneCandleForLong == true)
|
||||
{ OneCandleForLong = false;}
|
||||
|
||||
|
||||
//---Apply Continuation Trade Rule
|
||||
if(!LongSignal && ApplyContinuationRule && ((InSevenCandles && ApplySevenCandleRule) || !ApplySevenCandleRule))
|
||||
{
|
||||
if(baseline.entryLong()) {
|
||||
trendStatus.EnteredInLong = Time[1];
|
||||
}
|
||||
if(trendStatus.EnteredInLong > trendStatus.EnteredInShort && trendStatus.EnteredInLong > Time[7])
|
||||
{
|
||||
LongSignal = checkContinuationLongConditions();
|
||||
if(LongSignal) Print("Continuation Trade ok");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(LongSignal){
|
||||
money.OpenLong();
|
||||
Print("Open Signal Long");
|
||||
}
|
||||
return LongSignal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use Special conditions in a continuation trade
|
||||
*/
|
||||
bool checkContinuationLongConditions()
|
||||
{
|
||||
bool LongSignal = baseline.baselineDirection() > 0.0 && c1.confirmationLong() && c2.confirmationLong();
|
||||
return LongSignal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if c1,c2 gives the signal with in seven candles
|
||||
*/
|
||||
bool SevenCandleRuleForLong()
|
||||
{
|
||||
bool entrySignal = false;
|
||||
|
||||
if(c1.entryLong()) forLong.c1EnteredTime = Time[1];
|
||||
if(c2.entryLong()) forLong.c2EnteredTime = Time[1];
|
||||
|
||||
//Print("7 candle rule: c1:" + forLong.c1EnteredTime + ", c2:" + forLong.c2EnteredTime);
|
||||
//Print("7 candle rule: time: " + Time[7]);
|
||||
if(forLong.c1EnteredTime > Time[7] && forLong.c2EnteredTime > Time[7])
|
||||
{
|
||||
//Print("7 candle rule: entry");
|
||||
entrySignal = true;
|
||||
}
|
||||
return entrySignal;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Check conditions for entry long position |
|
||||
//+------------------------------------------------------------------+
|
||||
bool checkLongConditions(){
|
||||
bool openSignal = false;
|
||||
double atr_value = iATR(symbol,period,14,1);
|
||||
if(volumeIndicator.confirmationLong()){ // Enough volumen to open a position
|
||||
if(baseline.baselineDirection() > 0.0 && (!ApplyPullbackRule || (ApplyPullbackRule && baseline.baselineDirection() < atr_value))){ // In long tendency
|
||||
if(c1.confirmationLong() && c2.confirmationLong()){
|
||||
openSignal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Print("In long: " +volumeIndicator.confirmationLong() +" " + baseline.baselineDirection() + " "+ c1.confirmationLong() + c2.confirmationLong());
|
||||
//openSignal = volumeIndicator.isAbleOpenPosition() && (baseline.baselineDirection(0) > 0.0) && c1.signalLong() && c2.signalLong();
|
||||
|
||||
return openSignal;
|
||||
|
||||
}
|
||||
|
||||
bool manageShort()
|
||||
{
|
||||
bool ShortSignal = false;
|
||||
bool InSevenCandles = false;
|
||||
|
||||
if(c1.entryShort() || c2.entryShort() || baseline.entryShort() || (OneCandleForShort && ApplyOneCandleRule) ){
|
||||
//--- Apply Seven Candle rule
|
||||
if(ApplySevenCandleRule)
|
||||
{ InSevenCandles = SevenCandleRuleForShort(); }
|
||||
else{ InSevenCandles = false;}
|
||||
//--------------------
|
||||
|
||||
//-- Checks all Indicators for a short trade
|
||||
if((InSevenCandles && ApplySevenCandleRule) || !ApplySevenCandleRule ){
|
||||
ShortSignal = checkShortConditions();
|
||||
if(OneCandleForShort && ShortSignal) Print("Short trade ok by one candle rule");
|
||||
} //-----------
|
||||
|
||||
//--- Apply One candle Rule
|
||||
if(!ShortSignal && !OneCandleForShort && ApplyOneCandleRule)
|
||||
{ OneCandleForShort = true;}
|
||||
else if(OneCandleForShort == true)
|
||||
{ OneCandleForShort = false;}
|
||||
//--------
|
||||
|
||||
//---Apply Continuation Trade Rule
|
||||
if(!ShortSignal && ApplyContinuationRule && ((InSevenCandles && ApplySevenCandleRule) || !ApplySevenCandleRule))
|
||||
{
|
||||
if(baseline.entryShort()) {
|
||||
trendStatus.EnteredInShort = Time[1];
|
||||
}
|
||||
if(trendStatus.EnteredInShort > trendStatus.EnteredInLong && trendStatus.EnteredInShort > Time[7])
|
||||
{
|
||||
ShortSignal = checkContinuationShortConditions();
|
||||
if(ShortSignal) Print("Continuation short Trade ok");
|
||||
}
|
||||
|
||||
}// end continuation trade rule
|
||||
}
|
||||
|
||||
|
||||
if(ShortSignal){
|
||||
money.OpenShort();
|
||||
Print("Open Signal Short");
|
||||
}
|
||||
|
||||
return ShortSignal;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Use Special conditions in a continuation trade
|
||||
*/
|
||||
bool checkContinuationShortConditions()
|
||||
{
|
||||
bool ShortSignal = baseline.baselineDirection() < 0.0 && c1.confirmationShort() && c2.confirmationShort();
|
||||
|
||||
return ShortSignal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if c1,c2 gives the signal with in seven candles
|
||||
*/
|
||||
bool SevenCandleRuleForShort()
|
||||
{
|
||||
bool entrySignal = false;
|
||||
|
||||
if(c1.entryShort()) forShort.c1EnteredTime = Time[1];
|
||||
if(c2.entryShort()) forShort.c2EnteredTime = Time[1];
|
||||
|
||||
if(forShort.c1EnteredTime > Time[7] && forShort.c2EnteredTime > Time[7])
|
||||
{
|
||||
entrySignal = true;
|
||||
}
|
||||
return entrySignal;
|
||||
}
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//|Check conditions for entry short position |
|
||||
//+------------------------------------------------------------------+
|
||||
bool checkShortConditions(){
|
||||
bool openSignal = false;
|
||||
double atr_value = iATR(symbol,period,14,1);
|
||||
|
||||
if(volumeIndicator.confirmationShort()){
|
||||
if(baseline.baselineDirection() < 0.0 && (!ApplyPullbackRule || (ApplyPullbackRule && baseline.baselineDirection() < -1*atr_value)) ){
|
||||
if(c1.confirmationShort() && c2.confirmationShort()){
|
||||
openSignal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Print("In short: " + volumeIndicator.confirmationShort() +" " + baseline.baselineDirection() + " "+ c1.confirmationShort() + c2.confirmationShort());
|
||||
//openSignal = volumeIndicator.isAbleOpenPosition() && (baseline.baselineDirection(0) < 0.0) && c1.signalShort() && c2.signalShort();
|
||||
|
||||
return openSignal;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//|Close Positions: Exit rules |
|
||||
//+------------------------------------------------------------------+
|
||||
void manageClose()
|
||||
{
|
||||
bool closeSignal = false;
|
||||
|
||||
if(money.isLongPosition()){
|
||||
closeSignal = exitIndicator.exitLong();
|
||||
}else if(money.isShortPosition()){
|
||||
closeSignal = exitIndicator.exitShort();
|
||||
}
|
||||
|
||||
if(closeSignal)
|
||||
{
|
||||
if(money.isLongPosition()){
|
||||
money.CloseLongPosition();
|
||||
}else if(money.isShortPosition()){
|
||||
money.CloseShortPosition();
|
||||
}
|
||||
|
||||
Print("Close Signal");
|
||||
//Print("Position closed...");
|
||||
//Print("position status: "+orderState.OrderStatus);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//|Refresh indicators buffers data |
|
||||
//+------------------------------------------------------------------+
|
||||
void Refresh()
|
||||
{
|
||||
|
||||
baseline.Refresh();
|
||||
c1.Refresh();
|
||||
c2.Refresh();
|
||||
volumeIndicator.Refresh();
|
||||
exitIndicator.Refresh();
|
||||
money.Refresh();
|
||||
|
||||
|
||||
|
||||
/*Maybe In NiMomey class*/
|
||||
int i = 0;
|
||||
bool flag = false;
|
||||
while(i< OrdersTotal() && !flag)
|
||||
{
|
||||
OrderSelect(i,SELECT_BY_POS);
|
||||
if(OrderMagicNumber() == InpMagic)
|
||||
{
|
||||
flag = true;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if(!flag){ /*No orders made by this EA founded*/
|
||||
money.InitTSParams();
|
||||
}
|
||||
|
||||
//Print("Orders total :" + OrdersTotal());
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,486 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| StrategyOne.mq4 |
|
||||
//| Copyright 2020, MetaQuotes Software Corp. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2020, MetaQuotes Software Corp."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "3.00"
|
||||
#property strict
|
||||
|
||||
#include <Mql4Book\Timer.mqh>
|
||||
#include <NNFX\Indicators\Baseline\NiKijun.mqh>
|
||||
#include <NNFX\Indicators\Baseline\NiMA.mqh>
|
||||
#include <NNFX\Indicators\NiSSLActivator.mqh>
|
||||
#include <NNFX\Indicators\Confirmation\NiASH.mqh>
|
||||
#include <NNFX\Indicators\Volumen\NiWAE.mqh>
|
||||
#include <NNFX\Indicators\Exit\NiRex.mqh>
|
||||
#include <NNFX\Indicators\NiNone.mqh>
|
||||
#include <NNFX\Money\NiMoney.mqh>
|
||||
#include <NNFX\Money\NiMoneyScaleOut.mqh>
|
||||
#include <NNFX\functions.mqh>
|
||||
|
||||
//--- inputs
|
||||
input int InpMagic = 1024;
|
||||
input int Deviation = 50;
|
||||
|
||||
input string x7 = "------Special Rules-----";
|
||||
input bool ApplySevenCandleRule = false;
|
||||
input bool ApplyOneCandleRule = true;
|
||||
input bool ApplyPullbackRule = true;
|
||||
input bool ApplyContinuationRule = true;
|
||||
|
||||
|
||||
input string x = "----Baseline-----";
|
||||
input int InpKijun_b = 26; //InpKijun
|
||||
|
||||
input string x2 = "------C1-----------";
|
||||
input int InpPeriods_C1 = 14; // InpPeriods
|
||||
input ENUM_MA_METHOD InpMethod_C1 = MODE_SMA; //InpMethod
|
||||
|
||||
input string x3 = "------C2-----------";
|
||||
input string InpModeStr_C2="Mode: 0 - RSI, 1 - Stoch";
|
||||
input int InpMode_C2=0; // InpMode
|
||||
input int InpLength_C2=9; //InpLength
|
||||
input int InpSmooth_Length_C2=2; // InpSmooth_Length
|
||||
input int InpPrice_C2=0; // InpPrice
|
||||
// Applied price
|
||||
// 0 - Close
|
||||
// 1 - Open
|
||||
// 2 - High
|
||||
// 3 - Low
|
||||
// 4 - Median
|
||||
// 5 - Typical
|
||||
// 6 - Weighted
|
||||
input int InpMethod_C2=0;
|
||||
// 0 - SMA
|
||||
// 1 - EMA
|
||||
// 2 - SMMA
|
||||
// 3 - LWMA
|
||||
input string x4 = "---------Volumen------";
|
||||
input int InpSensetive_v = 150; //InpSensitive
|
||||
input int InpDeadZonePip_v = 30; // InpDeadZonePip
|
||||
input int InpExplosionPower_v = 15; // InpExplosionPower
|
||||
input int InpTrendPower_v = 15; // InpTrendPower
|
||||
|
||||
input string x5 = "---------Exit---------";
|
||||
input int InpSmoothing_Length_x=14; // InpSmoothing_Length
|
||||
input int InpSmoothing_Method_x=0; // InpSmoothing_Method
|
||||
// 0 - SMA
|
||||
// 1 - EMA
|
||||
// 2 - SMMA
|
||||
// 3 - LWMA
|
||||
input int InpSignal_Length_x=14; // InpSignal_Length
|
||||
input int InpSignal_Method_x=0; // InpSignal_Method
|
||||
// 0 - SMA
|
||||
// 1 - EMA
|
||||
// 2 - SMMA
|
||||
// 3 - LWMA
|
||||
input string x6 = "---------Monet management------";
|
||||
input int InpTakeProfit = 100;
|
||||
input int InpStopLoss = 100;
|
||||
input double InpLotSize = 0.05;
|
||||
|
||||
//--- global variables
|
||||
CNewBar NewBar;
|
||||
string symbol;
|
||||
int period;
|
||||
|
||||
bool longPosition;
|
||||
bool shortPosition;
|
||||
bool isPositionOpened;
|
||||
bool OneCandleForLong;
|
||||
bool OneCandleForShort;
|
||||
OrderState orderState;
|
||||
|
||||
EnterTime forLong;
|
||||
EnterTime forShort;
|
||||
TrendStatus trendStatus;
|
||||
|
||||
//NiKijun *baseline;
|
||||
NiMA *baseline;
|
||||
/*
|
||||
NiSSLActivator *c1;
|
||||
NiASH *c2;
|
||||
NiWAE *volumeIndicator;
|
||||
NiRex *exitIndicator;
|
||||
*/
|
||||
|
||||
NiNone *c1;
|
||||
NiNone *c2;
|
||||
NiNone *volumeIndicator;
|
||||
NiNone *exitIndicator;
|
||||
|
||||
|
||||
IMoney *money;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
//---
|
||||
orderState.LongPosition = false;
|
||||
orderState.ShortPosition = false;
|
||||
orderState.OrderStatus = false;
|
||||
|
||||
symbol = _Symbol;
|
||||
period = _Period;
|
||||
|
||||
//baseline = new NiKijun(InpKijun_b);
|
||||
baseline = new NiMA(30,0,MODE_SMA,PRICE_CLOSE);
|
||||
/*
|
||||
c1 = new NiSSLActivator(InpPeriods_C1,InpMethod_C1);
|
||||
c2 = new NiASH(InpModeStr_C2,InpMode_C2,InpLength_C2,InpSmooth_Length_C2,InpPrice_C2,InpMethod_C2);
|
||||
volumeIndicator = new NiWAE(InpSensetive_v,InpDeadZonePip_v,InpExplosionPower_v,InpTrendPower_v);
|
||||
exitIndicator = new NiRex(InpSmoothing_Length_x,InpSmoothing_Method_x,InpSignal_Length_x,InpSignal_Method_x);
|
||||
*/
|
||||
|
||||
c1 = new NiNone();
|
||||
c2 = new NiNone();
|
||||
volumeIndicator = new NiNone();
|
||||
exitIndicator = new NiNone();
|
||||
|
||||
baseline.InitIndicator(symbol,period);
|
||||
c1.InitIndicator(symbol,period);
|
||||
c2.InitIndicator(symbol,period);
|
||||
volumeIndicator.InitIndicator(symbol,period);
|
||||
exitIndicator.InitIndicator(symbol,period);
|
||||
|
||||
//money = new NiMoney(InpLotSize,InpTakeProfit,InpStopLoss,InpMagic,Deviation);
|
||||
money = new NiMoneyScaleOut(InpLotSize,InpMagic,Deviation);
|
||||
|
||||
|
||||
//---
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
//---
|
||||
delete baseline;
|
||||
delete c1;
|
||||
delete c2;
|
||||
delete volumeIndicator;
|
||||
delete exitIndicator;
|
||||
delete money;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
//---Detect New Bar
|
||||
bool isNewBar = NewBar.checkNewBar(_Symbol,_Period);
|
||||
//datetime t = iTime(_Symbol,_Period,0);
|
||||
|
||||
//Print("now: " + t);
|
||||
if(!isNewBar){
|
||||
return;
|
||||
}
|
||||
|
||||
//Print("On new Bar");
|
||||
OnBar();
|
||||
|
||||
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|OnBar function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnBar()
|
||||
{
|
||||
|
||||
//---Update Buffers
|
||||
Refresh();
|
||||
//---Exit rules
|
||||
if(money.isOpenedPosition()){
|
||||
manageClose();
|
||||
}
|
||||
|
||||
//---Entry Rules
|
||||
if(!money.isOpenedPosition()){
|
||||
manageOpen();
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Open Positions: Entry rules |
|
||||
//+------------------------------------------------------------------+
|
||||
void manageOpen()
|
||||
{
|
||||
|
||||
|
||||
manageLong();
|
||||
|
||||
manageShort();
|
||||
|
||||
if(money.isOpenedPosition()) {
|
||||
//Print("Position openend...");
|
||||
//Print("position status: "+orderState.OrderStatus);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks conditions if is possible open long positions
|
||||
*/
|
||||
bool manageLong()
|
||||
{
|
||||
bool LongSignal = false;
|
||||
bool InSevenCandles = false;
|
||||
|
||||
//--- Triggers an entry signal event, then checks all rules
|
||||
if(c1.entryLong() || c2.entryLong()|| baseline.entryLong() || (OneCandleForLong && ApplyOneCandleRule)){
|
||||
//--- Apply Seven Candle rule
|
||||
if(ApplySevenCandleRule)
|
||||
{ InSevenCandles = SevenCandleRuleForLong(); }
|
||||
else{ InSevenCandles = false;}
|
||||
|
||||
//-- Checks all Indicators for a long trade
|
||||
if((InSevenCandles && ApplySevenCandleRule) || !ApplySevenCandleRule ){
|
||||
LongSignal = checkLongConditions();
|
||||
if(OneCandleForLong && LongSignal) Print("Trade ok by one candle rule");
|
||||
}
|
||||
|
||||
//--- Apply One candle Rule
|
||||
if(!LongSignal && !OneCandleForLong && ApplyOneCandleRule)
|
||||
{ OneCandleForLong = true;}
|
||||
else if(OneCandleForLong == true)
|
||||
{ OneCandleForLong = false;}
|
||||
|
||||
|
||||
//---Apply Continuation Trade Rule
|
||||
if(!LongSignal && ApplyContinuationRule && ((InSevenCandles && ApplySevenCandleRule) || !ApplySevenCandleRule))
|
||||
{
|
||||
if(baseline.entryLong()) {
|
||||
trendStatus.EnteredInLong = Time[1];
|
||||
}
|
||||
if(trendStatus.EnteredInLong > trendStatus.EnteredInShort && trendStatus.EnteredInLong > Time[7])
|
||||
{
|
||||
LongSignal = checkContinuationLongConditions();
|
||||
if(LongSignal) Print("Continuation Trade ok");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(LongSignal){
|
||||
money.OpenLong();
|
||||
Print("Open Signal Long");
|
||||
}
|
||||
return LongSignal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use Special conditions in a continuation trade
|
||||
*/
|
||||
bool checkContinuationLongConditions()
|
||||
{
|
||||
bool LongSignal = baseline.baselineDirection() > 0.0 && c1.confirmationLong() && c2.confirmationLong();
|
||||
return LongSignal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if c1,c2 gives the signal with in seven candles
|
||||
*/
|
||||
bool SevenCandleRuleForLong()
|
||||
{
|
||||
bool entrySignal = false;
|
||||
|
||||
if(c1.entryLong()) forLong.c1EnteredTime = Time[1];
|
||||
if(c2.entryLong()) forLong.c2EnteredTime = Time[1];
|
||||
|
||||
//Print("7 candle rule: c1:" + forLong.c1EnteredTime + ", c2:" + forLong.c2EnteredTime);
|
||||
//Print("7 candle rule: time: " + Time[7]);
|
||||
if(forLong.c1EnteredTime > Time[7] && forLong.c2EnteredTime > Time[7])
|
||||
{
|
||||
//Print("7 candle rule: entry");
|
||||
entrySignal = true;
|
||||
}
|
||||
return entrySignal;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//|Check conditions for entry long position |
|
||||
//+------------------------------------------------------------------+
|
||||
bool checkLongConditions(){
|
||||
bool openSignal = false;
|
||||
double atr_value = iATR(symbol,period,14,1);
|
||||
if(volumeIndicator.confirmationLong()){ // Enough volumen to open a position
|
||||
if(baseline.baselineDirection() > 0.0 && (!ApplyPullbackRule || (ApplyPullbackRule && baseline.baselineDirection() < atr_value))){ // In long tendency
|
||||
if(c1.confirmationLong() && c2.confirmationLong()){
|
||||
openSignal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Print("In long: " +volumeIndicator.confirmationLong() +" " + baseline.baselineDirection() + " "+ c1.confirmationLong() + c2.confirmationLong());
|
||||
//openSignal = volumeIndicator.isAbleOpenPosition() && (baseline.baselineDirection(0) > 0.0) && c1.signalLong() && c2.signalLong();
|
||||
|
||||
return openSignal;
|
||||
|
||||
}
|
||||
|
||||
bool manageShort()
|
||||
{
|
||||
bool ShortSignal = false;
|
||||
bool InSevenCandles = false;
|
||||
|
||||
if(c1.entryShort() || c2.entryShort() || baseline.entryShort() || (OneCandleForShort && ApplyOneCandleRule) ){
|
||||
//--- Apply Seven Candle rule
|
||||
if(ApplySevenCandleRule)
|
||||
{ InSevenCandles = SevenCandleRuleForShort(); }
|
||||
else{ InSevenCandles = false;}
|
||||
//--------------------
|
||||
|
||||
//-- Checks all Indicators for a short trade
|
||||
if((InSevenCandles && ApplySevenCandleRule) || !ApplySevenCandleRule ){
|
||||
ShortSignal = checkShortConditions();
|
||||
if(OneCandleForShort && ShortSignal) Print("Short trade ok by one candle rule");
|
||||
} //-----------
|
||||
|
||||
//--- Apply One candle Rule
|
||||
if(!ShortSignal && !OneCandleForShort && ApplyOneCandleRule)
|
||||
{ OneCandleForShort = true;}
|
||||
else if(OneCandleForShort == true)
|
||||
{ OneCandleForShort = false;}
|
||||
//--------
|
||||
|
||||
//---Apply Continuation Trade Rule
|
||||
if(!ShortSignal && ApplyContinuationRule && ((InSevenCandles && ApplySevenCandleRule) || !ApplySevenCandleRule))
|
||||
{
|
||||
if(baseline.entryShort()) {
|
||||
trendStatus.EnteredInShort = Time[1];
|
||||
}
|
||||
if(trendStatus.EnteredInShort > trendStatus.EnteredInLong && trendStatus.EnteredInShort > Time[7])
|
||||
{
|
||||
ShortSignal = checkContinuationShortConditions();
|
||||
if(ShortSignal) Print("Continuation short Trade ok");
|
||||
}
|
||||
|
||||
}// end continuation trade rule
|
||||
}
|
||||
|
||||
|
||||
if(ShortSignal){
|
||||
money.OpenShort();
|
||||
Print("Open Signal Short");
|
||||
}
|
||||
|
||||
return ShortSignal;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Use Special conditions in a continuation trade
|
||||
*/
|
||||
bool checkContinuationShortConditions()
|
||||
{
|
||||
bool ShortSignal = baseline.baselineDirection() < 0.0 && c1.confirmationShort() && c2.confirmationShort();
|
||||
|
||||
return ShortSignal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if c1,c2 gives the signal with in seven candles
|
||||
*/
|
||||
bool SevenCandleRuleForShort()
|
||||
{
|
||||
bool entrySignal = false;
|
||||
|
||||
if(c1.entryShort()) forShort.c1EnteredTime = Time[1];
|
||||
if(c2.entryShort()) forShort.c2EnteredTime = Time[1];
|
||||
|
||||
if(forShort.c1EnteredTime > Time[7] && forShort.c2EnteredTime > Time[7])
|
||||
{
|
||||
entrySignal = true;
|
||||
}
|
||||
return entrySignal;
|
||||
}
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//|Check conditions for entry short position |
|
||||
//+------------------------------------------------------------------+
|
||||
bool checkShortConditions(){
|
||||
bool openSignal = false;
|
||||
double atr_value = iATR(symbol,period,14,1);
|
||||
|
||||
if(volumeIndicator.confirmationShort()){
|
||||
if(baseline.baselineDirection() < 0.0 && (!ApplyPullbackRule || (ApplyPullbackRule && baseline.baselineDirection() < -1*atr_value)) ){
|
||||
if(c1.confirmationShort() && c2.confirmationShort()){
|
||||
openSignal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Print("In short: " + volumeIndicator.confirmationShort() +" " + baseline.baselineDirection() + " "+ c1.confirmationShort() + c2.confirmationShort());
|
||||
//openSignal = volumeIndicator.isAbleOpenPosition() && (baseline.baselineDirection(0) < 0.0) && c1.signalShort() && c2.signalShort();
|
||||
|
||||
return openSignal;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//|Close Positions: Exit rules |
|
||||
//+------------------------------------------------------------------+
|
||||
void manageClose()
|
||||
{
|
||||
bool closeSignal = false;
|
||||
|
||||
if(money.isLongPosition()){
|
||||
closeSignal = exitIndicator.exitLong();
|
||||
}else if(money.isShortPosition()){
|
||||
closeSignal = exitIndicator.exitShort();
|
||||
}
|
||||
|
||||
if(closeSignal)
|
||||
{
|
||||
if(money.isLongPosition()){
|
||||
money.CloseLongPosition();
|
||||
}else if(money.isShortPosition()){
|
||||
money.CloseShortPosition();
|
||||
}
|
||||
|
||||
Print("Close Signal");
|
||||
//Print("Position closed...");
|
||||
//Print("position status: "+orderState.OrderStatus);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//|Refresh indicators buffers data |
|
||||
//+------------------------------------------------------------------+
|
||||
void Refresh()
|
||||
{
|
||||
|
||||
baseline.Refresh();
|
||||
c1.Refresh();
|
||||
c2.Refresh();
|
||||
volumeIndicator.Refresh();
|
||||
exitIndicator.Refresh();
|
||||
money.Refresh();
|
||||
|
||||
|
||||
|
||||
/*Maybe In NiMomey class*/
|
||||
int i = 0;
|
||||
bool flag = false;
|
||||
while(i< OrdersTotal() && !flag)
|
||||
{
|
||||
OrderSelect(i,SELECT_BY_POS);
|
||||
if(OrderMagicNumber() == InpMagic)
|
||||
{
|
||||
flag = true;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if(!flag){ /*No orders made by this EA founded*/
|
||||
money.InitTSParams();
|
||||
}
|
||||
|
||||
//Print("Orders total :" + OrdersTotal());
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,208 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| StrategyOne.mq4 |
|
||||
//| Copyright 2020, MetaQuotes Software Corp. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2020, MetaQuotes Software Corp."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "4.00"
|
||||
#property strict
|
||||
|
||||
#include <Mql4Book\Timer.mqh>
|
||||
|
||||
#include <NNFX\Indicators\Baseline\NiKijun.mqh>
|
||||
#include <NNFX\Indicators\Baseline\NiMA.mqh>
|
||||
#include <NNFX\Indicators\NiSSLActivator.mqh>
|
||||
#include <NNFX\Indicators\Confirmation\NiASH.mqh>
|
||||
#include <NNFX\Indicators\Volumen\NiWAE.mqh>
|
||||
#include <NNFX\Indicators\Exit\NiRex.mqh>
|
||||
#include <NNFX\Indicators\NiNone.mqh>
|
||||
#include <NNFX\Money\NiMoney.mqh>
|
||||
#include <NNFX\Money\NiMoneyScaleOut.mqh>
|
||||
|
||||
#include <NNFX\NExpert.mqh>
|
||||
#include <NNFX\functions.mqh>
|
||||
#include <NNFX\Rules\NRules.mqh>
|
||||
|
||||
|
||||
|
||||
|
||||
//--- inputs
|
||||
input int InpMagic = 1024;
|
||||
input int Deviation = 50;
|
||||
input double InpLotSize = 0.05;
|
||||
input double InpSLalpha = 1.5;
|
||||
input double InpTPbeta = 1;
|
||||
|
||||
input string x7 = "------Special Rules-----";
|
||||
input bool ApplySevenCandleRule = true;
|
||||
input bool ApplyOneCandleRule = true;
|
||||
input bool ApplyPullbackRule = true;
|
||||
input bool ApplyContinuationRule = true;
|
||||
|
||||
|
||||
input string x = "----Baseline-----";
|
||||
input int InpKijun_b = 26; //InpKijun
|
||||
|
||||
input string x2 = "------C1-----------";
|
||||
input int InpPeriods_C1 = 14; // InpPeriods
|
||||
input ENUM_MA_METHOD InpMethod_C1 = MODE_SMA; //InpMethod
|
||||
|
||||
input string x3 = "------C2-----------";
|
||||
input string InpModeStr_C2="Mode: 0 - RSI, 1 - Stoch";
|
||||
input int InpMode_C2=0; // InpMode
|
||||
input int InpLength_C2=9; //InpLength
|
||||
input int InpSmooth_Length_C2=2; // InpSmooth_Length
|
||||
input int InpPrice_C2=0; // InpPrice
|
||||
// Applied price
|
||||
// 0 - Close
|
||||
// 1 - Open
|
||||
// 2 - High
|
||||
// 3 - Low
|
||||
// 4 - Median
|
||||
// 5 - Typical
|
||||
// 6 - Weighted
|
||||
input int InpMethod_C2=0;
|
||||
// 0 - SMA
|
||||
// 1 - EMA
|
||||
// 2 - SMMA
|
||||
// 3 - LWMA
|
||||
input string x4 = "---------Volumen------";
|
||||
input int InpSensetive_v = 150; //InpSensitive
|
||||
input int InpDeadZonePip_v = 30; // InpDeadZonePip
|
||||
input int InpExplosionPower_v = 15; // InpExplosionPower
|
||||
input int InpTrendPower_v = 15; // InpTrendPower
|
||||
|
||||
input string x5 = "---------Exit---------";
|
||||
input int InpSmoothing_Length_x=14; // InpSmoothing_Length
|
||||
input int InpSmoothing_Method_x=0; // InpSmoothing_Method
|
||||
// 0 - SMA
|
||||
// 1 - EMA
|
||||
// 2 - SMMA
|
||||
// 3 - LWMA
|
||||
input int InpSignal_Length_x=14; // InpSignal_Length
|
||||
input int InpSignal_Method_x=0; // InpSignal_Method
|
||||
// 0 - SMA
|
||||
// 1 - EMA
|
||||
// 2 - SMMA
|
||||
// 3 - LWMA
|
||||
input string x6 = "---------Monet management------";
|
||||
input int InpTakeProfit = 100;
|
||||
input int InpStopLoss = 100;
|
||||
|
||||
|
||||
//--- global variables
|
||||
CNewBar NewBar;
|
||||
string symbol;
|
||||
int period;
|
||||
|
||||
bool longPosition;
|
||||
bool shortPosition;
|
||||
bool isPositionOpened;
|
||||
bool OneCandleForLong;
|
||||
bool OneCandleForShort;
|
||||
OrderState orderState;
|
||||
EnterTime forLong;
|
||||
EnterTime forShort;
|
||||
TrendStatus trendStatus;
|
||||
|
||||
|
||||
IBaseline *baseline;
|
||||
IConfirmation *c1;
|
||||
IConfirmation *c2;
|
||||
IVolume *volumeIndicator;
|
||||
IExit *exitIndicator;
|
||||
|
||||
IMoney *money;
|
||||
NExpert *expert;
|
||||
/*
|
||||
NiNone *c1;
|
||||
NiNone *c2;
|
||||
NiNone *volumeIndicator;
|
||||
NiNone *exitIndicator;
|
||||
*/
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
//---
|
||||
orderState.LongPosition = false;
|
||||
orderState.ShortPosition = false;
|
||||
orderState.OrderStatus = false;
|
||||
|
||||
symbol = _Symbol;
|
||||
period = _Period;
|
||||
|
||||
baseline = new NiKijun(InpKijun_b);
|
||||
//baseline = new NiMA(30,0,MODE_SMA,PRICE_CLOSE);
|
||||
|
||||
c1 = new NiSSLActivator(InpPeriods_C1,InpMethod_C1);
|
||||
c2 = new NiASH(InpModeStr_C2,InpMode_C2,InpLength_C2,InpSmooth_Length_C2,InpPrice_C2,InpMethod_C2);
|
||||
volumeIndicator = new NiWAE(InpSensetive_v,InpDeadZonePip_v,InpExplosionPower_v,InpTrendPower_v);
|
||||
exitIndicator = new NiRex(InpSmoothing_Length_x,InpSmoothing_Method_x,InpSignal_Length_x,InpSignal_Method_x);
|
||||
//money = new NiMoneyScaleOut(InpLotSize,InpMagic,Deviation);
|
||||
money = new NiMoneyScaleOut(InpLotSize,InpSLalpha,InpTPbeta,14,InpMagic,Deviation);
|
||||
|
||||
/*
|
||||
c1 = new NiNoneC();
|
||||
c2 = new NiNoneC();
|
||||
volumeIndicator = new NiNoneV();
|
||||
exitIndicator = new NiNoneE();
|
||||
*/
|
||||
|
||||
baseline.InitIndicator(symbol,period);
|
||||
c1.InitIndicator(symbol,period);
|
||||
c2.InitIndicator(symbol,period);
|
||||
volumeIndicator.InitIndicator(symbol,period);
|
||||
exitIndicator.InitIndicator(symbol,period);
|
||||
|
||||
|
||||
expert = new NExpert(baseline,c1,c2,exitIndicator,volumeIndicator,money,InpMagic,
|
||||
ApplyPullbackRule,ApplySevenCandleRule,ApplyContinuationRule,ApplyOneCandleRule);
|
||||
|
||||
|
||||
|
||||
|
||||
//money = new NiMoney(InpLotSize,InpTakeProfit,InpStopLoss,InpMagic,Deviation);
|
||||
|
||||
|
||||
|
||||
//---
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
//---
|
||||
delete baseline;
|
||||
delete c1;
|
||||
delete c2;
|
||||
delete volumeIndicator;
|
||||
delete exitIndicator;
|
||||
delete money;
|
||||
delete expert;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
//---Detect New Bar
|
||||
bool isNewBar = NewBar.checkNewBar(_Symbol,_Period);
|
||||
//datetime t = iTime(_Symbol,_Period,0);
|
||||
|
||||
//Print("now: " + t);
|
||||
if(!isNewBar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Print("On new Bar");
|
||||
expert.OnBar();
|
||||
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
============================================================
|
||||
Demo File: RSI_Mean_Reversion_EA - Signal Logic Showcase
|
||||
Category: Mean Reversion
|
||||
Platform: MetaTrader 4 (MQL4)
|
||||
Version: 1.0
|
||||
Author: Giacomo Cipolat Bares
|
||||
Portfolio: MQL4 Expert Advisors Portfolio
|
||||
============================================================
|
||||
|
||||
Description:
|
||||
This is a simplified public demo derived from the full
|
||||
RSI Mean Reversion EA.
|
||||
|
||||
Included in this demo:
|
||||
- RSI overbought / oversold logic
|
||||
- optional candle-close confirmation
|
||||
- optional moving average trend filter
|
||||
- optional ATR volatility filter
|
||||
- basic on-chart signal output
|
||||
|
||||
Excluded from this demo:
|
||||
- order execution
|
||||
- risk management engine
|
||||
- break-even / trailing stop
|
||||
- retry logic
|
||||
- broker protection handling
|
||||
- full production trade framework
|
||||
- chart visualization layer
|
||||
============================================================
|
||||
*/
|
||||
|
||||
#property strict
|
||||
#property version "1.00"
|
||||
|
||||
//========================= INPUTS ==================================
|
||||
input string __01_RSISettings = "01 =========== RSI Settings ==========";
|
||||
input int RSIPeriod = 14;
|
||||
input double RSIBuyLevel = 30.0;
|
||||
input double RSISellLevel = 70.0;
|
||||
input double RSIExitLevel = 50.0;
|
||||
input int RSIPrice = PRICE_CLOSE;
|
||||
input bool UseClosedCandleSignal = true;
|
||||
|
||||
input string __02_TrendFilter = "02 ========= Trend Filter =========";
|
||||
input bool UseTrendFilter = false;
|
||||
input int MAPeriod = 200;
|
||||
input int MAMethod = MODE_SMA;
|
||||
input int MAPrice = PRICE_CLOSE;
|
||||
|
||||
input string __03_ATRFilter = "03 =========== ATR Filter ==========";
|
||||
input bool UseATRFilter = false;
|
||||
input int ATRPeriod = 14;
|
||||
input double MinATRValuePips = 5.0;
|
||||
|
||||
//======================= GLOBALS ===================================
|
||||
double g_point;
|
||||
double g_pip;
|
||||
int g_digits;
|
||||
datetime g_lastBarTime = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
g_point = Point;
|
||||
g_digits = Digits;
|
||||
|
||||
if(g_digits == 5 || g_digits == 3)
|
||||
g_pip = g_point * 10.0;
|
||||
else
|
||||
g_pip = g_point;
|
||||
|
||||
Print("RSI Mean Reversion demo initialized");
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Detects a new bar |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime currentBarTime = iTime(NULL, 0, 0);
|
||||
|
||||
if(currentBarTime != g_lastBarTime)
|
||||
{
|
||||
g_lastBarTime = currentBarTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Indicator helpers |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetRSI(int shift)
|
||||
{
|
||||
return iRSI(NULL, 0, RSIPeriod, RSIPrice, shift);
|
||||
}
|
||||
|
||||
double GetMA(int shift)
|
||||
{
|
||||
return iMA(NULL, 0, MAPeriod, 0, MAMethod, MAPrice, shift);
|
||||
}
|
||||
|
||||
double GetATRInPips(int shift)
|
||||
{
|
||||
double atr = iATR(NULL, 0, ATRPeriod, shift);
|
||||
return atr / g_pip;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Filters |
|
||||
//+------------------------------------------------------------------+
|
||||
bool TrendFilterBuyPassed()
|
||||
{
|
||||
if(!UseTrendFilter)
|
||||
return true;
|
||||
|
||||
return (Close[1] >= GetMA(1));
|
||||
}
|
||||
|
||||
bool TrendFilterSellPassed()
|
||||
{
|
||||
if(!UseTrendFilter)
|
||||
return true;
|
||||
|
||||
return (Close[1] <= GetMA(1));
|
||||
}
|
||||
|
||||
bool ATRFilterPassed()
|
||||
{
|
||||
if(!UseATRFilter)
|
||||
return true;
|
||||
|
||||
return (GetATRInPips(1) >= MinATRValuePips);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSI signal logic |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsOversoldSignal()
|
||||
{
|
||||
double rsiPrev = GetRSI(2);
|
||||
double rsiCurr = GetRSI(1);
|
||||
|
||||
if(UseClosedCandleSignal)
|
||||
return (rsiPrev <= RSIBuyLevel && rsiCurr > RSIBuyLevel);
|
||||
|
||||
return (GetRSI(0) <= RSIBuyLevel);
|
||||
}
|
||||
|
||||
bool IsOverboughtSignal()
|
||||
{
|
||||
double rsiPrev = GetRSI(2);
|
||||
double rsiCurr = GetRSI(1);
|
||||
|
||||
if(UseClosedCandleSignal)
|
||||
return (rsiPrev >= RSISellLevel && rsiCurr < RSISellLevel);
|
||||
|
||||
return (GetRSI(0) >= RSISellLevel);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Demo wrappers |
|
||||
//+------------------------------------------------------------------+
|
||||
bool BuySignal()
|
||||
{
|
||||
if(!ATRFilterPassed())
|
||||
return false;
|
||||
|
||||
if(!TrendFilterBuyPassed())
|
||||
return false;
|
||||
|
||||
return IsOversoldSignal();
|
||||
}
|
||||
|
||||
bool SellSignal()
|
||||
{
|
||||
if(!ATRFilterPassed())
|
||||
return false;
|
||||
|
||||
if(!TrendFilterSellPassed())
|
||||
return false;
|
||||
|
||||
return IsOverboughtSignal();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
if(!IsNewBar())
|
||||
return;
|
||||
|
||||
if(BuySignal())
|
||||
{
|
||||
Comment("Demo Signal: BUY RSI mean reversion detected");
|
||||
return;
|
||||
}
|
||||
|
||||
if(SellSignal())
|
||||
{
|
||||
Comment("Demo Signal: SELL RSI mean reversion detected");
|
||||
return;
|
||||
}
|
||||
|
||||
Comment("Demo Signal: No valid RSI mean reversion setup");
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,741 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| NewsTrader.mq4 |
|
||||
//| Copyright © 2024, EarnForex.com |
|
||||
//| https://www.earnforex.com/ |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright © 2024, EarnForex"
|
||||
#property link "https://www.earnforex.com/metatrader-expert-advisors/News-Trader/"
|
||||
#property version "1.12"
|
||||
#property strict
|
||||
|
||||
#property description "Opens a buy/sell trade (random, chosen direction, or both directions) seconds before news release."
|
||||
#property description "Sets SL and TP. Keeps updating them until the very release."
|
||||
#property description "Can set use trailing stop and breakeven."
|
||||
#property description "ATR-based stop-loss option is also available."
|
||||
#property description "Closes trade after one hour."
|
||||
|
||||
enum dir_enum
|
||||
{
|
||||
Buy,
|
||||
Sell,
|
||||
Both,
|
||||
Random
|
||||
};
|
||||
|
||||
enum trailing_enum
|
||||
{
|
||||
None,
|
||||
Breakeven,
|
||||
Normal, // Normal trailing stop
|
||||
NormalPlusBE // Normal trailing stop + Breakeven
|
||||
};
|
||||
|
||||
input group "Trading"
|
||||
input datetime NewsTime = -1; // News date/time (Server)
|
||||
input int StopLoss = 15; // Stop-loss in points
|
||||
input int TakeProfit = 75; // Take-profit in points
|
||||
input dir_enum Direction = Both; // Direction of the trade to open
|
||||
input trailing_enum TrailingStop = None; // Trailing stop type
|
||||
input int BEOnProfit = 0; // Profit to trigger breakeven, points
|
||||
input int BEExtraProfit = 0; // Extra profit for breakeven, points
|
||||
input int TSOnProfit = 0; // Profit to start trailing stop, points
|
||||
input bool PreAdjustSLTP = false; // Preadjust SL/TP until news is out
|
||||
input int SecondsBefore = 18; // Seconds before the news to open a trade
|
||||
input int CloseAfterSeconds = 3600; // Close trade X seconds after the news, 0 - turn the feature off
|
||||
input bool SpreadFuse = true; // SpreadFuse - prevent trading if spread >= stop-loss
|
||||
input group "ATR"
|
||||
input bool UseATR = false; // Use ATR-based stop-loss and take-profit levels
|
||||
input int ATR_Period = 14; // ATR Period
|
||||
input double ATR_Multiplier_SL = 1; // ATR multiplier for SL
|
||||
input double ATR_Multiplier_TP = 5; // ATR multiplier for TP
|
||||
input group "Money management"
|
||||
input double Lots = 0.01;
|
||||
input bool MM = true; // Money Management, if true - position sizing based on stop-loss
|
||||
input double Risk = 1; // Risk - Risk tolerance in percentage points
|
||||
input double FixedBalance = 0; // FixedBalance: If > 0, trade size calc. uses it as balance
|
||||
input double MoneyRisk = 0; // MoneyRisk: Risk tolerance in account currency
|
||||
input bool UseMoneyInsteadOfPercentage = false; // Use money risk instead of percentage
|
||||
input bool UseEquityInsteadOfBalance = false; // Use equity instead of balance
|
||||
input group "Timer"
|
||||
input bool ShowTimer = true; // Show timer before and after news
|
||||
input int FontSize = 18;
|
||||
input string Font = "Arial";
|
||||
input color FontColor = clrRed;
|
||||
input ENUM_BASE_CORNER Corner = CORNER_LEFT_UPPER;
|
||||
input int X_Distance = 10; // X-axis distance from the chart corner
|
||||
input int Y_Distance = 130; // Y-axis distance from the chart corner
|
||||
input group "Miscellaneous"
|
||||
input int Slippage = 3;
|
||||
input int Magic = 794823491;
|
||||
input string Commentary = "NewsTrader"; // Comment - trade description (e.g. "US CPI", "EU GDP", etc.)
|
||||
input bool IgnoreECNMode = true; // IgnoreECNMode: Always attach SL/TP immediately
|
||||
|
||||
// Global variables:
|
||||
bool HaveLongPosition, HaveShortPosition;
|
||||
bool ECN_Mode;
|
||||
|
||||
int news_time;
|
||||
bool CanTrade = false;
|
||||
bool Terminal_Trade_Allowed = true;
|
||||
|
||||
double SL, TP;
|
||||
|
||||
// For tick value adjustment:
|
||||
string ProfitCurrency = "", account_currency = "", BaseCurrency = "", ReferenceSymbol = NULL, AdditionalReferenceSymbol = NULL;
|
||||
bool ReferenceSymbolMode, AdditionalReferenceSymbolMode;
|
||||
int ProfitCalcMode;
|
||||
|
||||
void OnInit()
|
||||
{
|
||||
news_time = (int)NewsTime;
|
||||
double min_lot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
|
||||
if ((Lots < min_lot) && (!MM))
|
||||
{
|
||||
double lot_step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
|
||||
int LotStep_digits = CountDecimalPlaces(lot_step);
|
||||
Print("Minimum lot: ", DoubleToString(min_lot, LotStep_digits), ", lot step: ", DoubleToString(lot_step, LotStep_digits), ".");
|
||||
Alert("Lots should be not less than: ", DoubleToString(min_lot, LotStep_digits), ".");
|
||||
}
|
||||
else CanTrade = true;
|
||||
|
||||
if (ShowTimer)
|
||||
{
|
||||
ObjectCreate(ChartID(), "NewsTraderTimer", OBJ_LABEL, 0, 0, 0);
|
||||
ObjectSetInteger(ChartID(), "NewsTraderTimer", OBJPROP_CORNER, Corner);
|
||||
ObjectSetInteger(ChartID(), "NewsTraderTimer", OBJPROP_XDISTANCE, X_Distance);
|
||||
ObjectSetInteger(ChartID(), "NewsTraderTimer", OBJPROP_YDISTANCE, Y_Distance);
|
||||
ObjectSetInteger(ChartID(), "NewsTraderTimer", OBJPROP_SELECTABLE, true);
|
||||
EventSetMillisecondTimer(100); // For smooth updates.
|
||||
}
|
||||
|
||||
// If UseATR = false, these values will be used. Otherwise, ATR values will be calculated later.
|
||||
SL = StopLoss;
|
||||
TP = TakeProfit;
|
||||
|
||||
if (BEExtraProfit > BEOnProfit) Print("Extra profit for breakeven shouldn't be greater than the profit to trigger breakeven parameter. Please check your input parameters.");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Deletes graphical object if needed. |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
ObjectDelete(ChartID(), "NewsTraderTimer");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Updates text about time left to news or passed after news. |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTimer()
|
||||
{
|
||||
DoTrading();
|
||||
string text;
|
||||
int difference = (int)TimeCurrent() - news_time;
|
||||
if (difference <= 0) text = "Time to news: " + TimeDistance(-difference);
|
||||
else text = "Time after news: " + TimeDistance(difference) + ".";
|
||||
ObjectSetString(ChartID(), "NewsTraderTimer", OBJPROP_TEXT, text);
|
||||
ObjectSetString(ChartID(), "NewsTraderTimer", OBJPROP_FONT, Font);
|
||||
ObjectSetInteger(ChartID(), "NewsTraderTimer", OBJPROP_FONTSIZE, FontSize);
|
||||
ObjectSetInteger(ChartID(), "NewsTraderTimer", OBJPROP_COLOR, FontColor);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Format time distance from the number of seconds to normal string |
|
||||
//| of years, days, hours, minutes, and seconds. |
|
||||
//| t - number of seconds |
|
||||
//| Returns: formatted string. |
|
||||
//+------------------------------------------------------------------+
|
||||
string TimeDistance(int t)
|
||||
{
|
||||
if (t == 0) return "0 seconds";
|
||||
string s = "";
|
||||
int y = 0;
|
||||
int d = 0;
|
||||
int h = 0;
|
||||
int m = 0;
|
||||
|
||||
y = t / 31536000;
|
||||
t -= y * 31536000;
|
||||
|
||||
d = t / 86400;
|
||||
t -= d * 86400;
|
||||
|
||||
h = t / 3600;
|
||||
t -= h * 3600;
|
||||
|
||||
m = t / 60;
|
||||
t -= m * 60;
|
||||
|
||||
if (y) s += IntegerToString(y) + " year";
|
||||
if (y > 1) s += "s";
|
||||
|
||||
if (d) s += " " + IntegerToString(d) + " day";
|
||||
if (d > 1) s += "s";
|
||||
|
||||
if (h) s += " " + IntegerToString(h) + " hour";
|
||||
if (h > 1) s += "s";
|
||||
|
||||
if (m) s += " " + IntegerToString(m) + " minute";
|
||||
if (m > 1) s += "s";
|
||||
|
||||
if (t) s += " " + IntegerToString(t) + " second";
|
||||
if (t > 1) s += "s";
|
||||
|
||||
return StringTrimLeft(s);
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
DoTrading();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Main execution procedure. |
|
||||
//+------------------------------------------------------------------+
|
||||
void DoTrading()
|
||||
{
|
||||
if ((TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) == false) || (!CanTrade))
|
||||
{
|
||||
if ((TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) == false) && (Terminal_Trade_Allowed == true))
|
||||
{
|
||||
Print("Trading not allowed.");
|
||||
Terminal_Trade_Allowed = false;
|
||||
}
|
||||
else if ((TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) == true) && (Terminal_Trade_Allowed == false))
|
||||
{
|
||||
Print("Trading allowed.");
|
||||
Terminal_Trade_Allowed = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ENUM_SYMBOL_TRADE_EXECUTION Execution_Mode = (ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(Symbol(), SYMBOL_TRADE_EXEMODE);
|
||||
if (Execution_Mode == SYMBOL_TRADE_EXECUTION_MARKET) ECN_Mode = true;
|
||||
else ECN_Mode = false;
|
||||
if (IgnoreECNMode) ECN_Mode = false;
|
||||
|
||||
// Do nothing if it is too early.
|
||||
int time = (int)TimeCurrent();
|
||||
if (time < news_time - SecondsBefore) return;
|
||||
|
||||
if (UseATR)
|
||||
{
|
||||
// Getting the ATR values
|
||||
double ATR = iATR(NULL, 0, ATR_Period, 0);
|
||||
SL = ATR * ATR_Multiplier_SL;
|
||||
if (SL <= (SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) + SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)) * Point) SL = (SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) + SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)) * Point;
|
||||
TP = ATR * ATR_Multiplier_TP;
|
||||
if (TP <= (SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) + SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)) * Point) TP = (SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) + SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)) * Point;
|
||||
SL /= Point;
|
||||
TP /= Point;
|
||||
}
|
||||
|
||||
// Check what position is currently open.
|
||||
GetPositionStates();
|
||||
|
||||
// Adjust SL and TP of the current position.
|
||||
if ((HaveLongPosition) || (HaveShortPosition)) ControlPosition();
|
||||
else
|
||||
{
|
||||
// Time to news is less or equal to SecondsBefore but is not negative.
|
||||
if ((news_time - time <= SecondsBefore) && (news_time > time))
|
||||
{
|
||||
// Prevent position opening when spreads are too wide (bigger than StopLoss input).
|
||||
int spread = (int)MarketInfo(Symbol(), MODE_SPREAD);
|
||||
if ((SpreadFuse) && (spread >= StopLoss))
|
||||
{
|
||||
Print(Symbol(), ": Spread fuse prevents positions from opening. Current spread: ", spread, " points.");
|
||||
return;
|
||||
}
|
||||
if (Direction == Buy) fBuy();
|
||||
else if (Direction == Sell) fSell();
|
||||
else if (Direction == Both)
|
||||
{
|
||||
fBuy();
|
||||
fSell();
|
||||
}
|
||||
else if (Direction == Random)
|
||||
{
|
||||
MathSrand((uint)TimeCurrent());
|
||||
if (MathRand() % 2 == 1) fBuy();
|
||||
else fSell();
|
||||
}
|
||||
if (ECN_Mode) ControlPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check what positions are currently open. |
|
||||
//+------------------------------------------------------------------+
|
||||
void GetPositionStates()
|
||||
{
|
||||
HaveLongPosition = false;
|
||||
HaveShortPosition = false;
|
||||
int total = OrdersTotal();
|
||||
for (int cnt = 0; cnt < total; cnt++)
|
||||
{
|
||||
if (OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES) == false) continue;
|
||||
if (OrderMagicNumber() != Magic) continue;
|
||||
if (OrderSymbol() != Symbol()) continue;
|
||||
|
||||
if (OrderType() == OP_BUY) HaveLongPosition = true;
|
||||
else if (OrderType() == OP_SELL) HaveShortPosition = true;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Add SL/TP, adjust SL/TP, set breakeven, close trade. |
|
||||
//+------------------------------------------------------------------+
|
||||
void ControlPosition()
|
||||
{
|
||||
int total = OrdersTotal();
|
||||
for (int cnt = total - 1; cnt >= 0; cnt--)
|
||||
{
|
||||
if (OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES) == false) continue;
|
||||
if (OrderMagicNumber() != Magic) continue;
|
||||
if (OrderSymbol() != Symbol()) continue;
|
||||
|
||||
if ((OrderType() == OP_BUY) || (OrderType() == OP_SELL))
|
||||
{
|
||||
int time = (int)TimeCurrent();
|
||||
|
||||
double new_sl, new_tp;
|
||||
if (SL < MarketInfo(Symbol(), MODE_STOPLEVEL) + MarketInfo(Symbol(), MODE_SPREAD)) SL = MarketInfo(Symbol(), MODE_STOPLEVEL) + MarketInfo(Symbol(), MODE_SPREAD);
|
||||
if (TP < MarketInfo(Symbol(), MODE_STOPLEVEL) + MarketInfo(Symbol(), MODE_SPREAD)) TP = MarketInfo(Symbol(), MODE_STOPLEVEL) + MarketInfo(Symbol(), MODE_SPREAD);
|
||||
|
||||
if (OrderType() == OP_BUY)
|
||||
{
|
||||
RefreshRates();
|
||||
new_sl = NormalizeDouble(Ask - SL * Point, Digits);
|
||||
new_tp = NormalizeDouble(Ask + TP * Point, Digits);
|
||||
}
|
||||
else if (OrderType() == OP_SELL)
|
||||
{
|
||||
RefreshRates();
|
||||
new_sl = NormalizeDouble(Bid + SL * Point, Digits);
|
||||
new_tp = NormalizeDouble(Bid - TP * Point, Digits);
|
||||
}
|
||||
// Need to adjust or add SL/TP.
|
||||
if (time < news_time)
|
||||
{
|
||||
// Adjust only if parameter is set or if in ECN mode and need to assign SL/TP first time.
|
||||
if ((((new_sl != NormalizeDouble(OrderStopLoss(), Digits)) || (new_tp != NormalizeDouble(OrderTakeProfit(), Digits))) && (PreAdjustSLTP)) ||
|
||||
(((OrderStopLoss() == 0) || (OrderTakeProfit() == 0)) && (ECN_Mode)))
|
||||
{
|
||||
Print("Adjusting SL: ", DoubleToString(new_sl, _Digits), " and TP: ", DoubleToString(new_tp, _Digits), ".");
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
bool result = OrderModify(OrderTicket(), OrderOpenPrice(), new_sl, new_tp, 0);
|
||||
if (result) return;
|
||||
else Print("Error modifying the order: ", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for breakeven or trade time out. Plus, sometimes, in ECN mode, it is necessary to check if SL/TP was set even after the news.
|
||||
else
|
||||
{
|
||||
RefreshRates();
|
||||
// Adjust only if in ECN mode and need to assign SL/TP first time.
|
||||
if (((OrderStopLoss() == 0) || (OrderTakeProfit() == 0)) && (ECN_Mode))
|
||||
{
|
||||
Print("Adjusting SL: ", DoubleToString(new_sl, _Digits), " and TP: ", DoubleToString(new_tp, _Digits), ".");
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
bool result = OrderModify(OrderTicket(), OrderOpenPrice(), new_sl, new_tp, 0);
|
||||
if (result) return;
|
||||
else Print("Error modifying the order: ", GetLastError());
|
||||
}
|
||||
}
|
||||
// Breakeven.
|
||||
if (((TrailingStop == Breakeven) || (TrailingStop == NormalPlusBE)) && ((((OrderType() == OP_BUY) && (Bid - OrderOpenPrice() >= BEOnProfit * _Point)) || ((OrderType() == OP_SELL) && (OrderOpenPrice() - Ask >= BEOnProfit * _Point)))))
|
||||
{
|
||||
new_sl = NormalizeDouble(OrderOpenPrice(), _Digits);
|
||||
if (BEExtraProfit > 0) // Breakeven extra profit?
|
||||
{
|
||||
if (OrderType() == OP_BUY) new_sl += BEExtraProfit * _Point; // For buys.
|
||||
else new_sl -= BEExtraProfit * _Point; // For sells.
|
||||
new_sl = NormalizeDouble(new_sl, _Digits);
|
||||
}
|
||||
if (((OrderType() == OP_BUY) && (new_sl > OrderStopLoss())) || ((OrderType() == OP_SELL) && ((new_sl < OrderStopLoss()) || (OrderStopLoss() == 0)))) // Avoid moving SL to BE if this SL is already there or in a better position.
|
||||
{
|
||||
Print("Moving SL to breakeven: ", new_sl, ".");
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
bool result = OrderModify(OrderTicket(), OrderOpenPrice(), new_sl, OrderTakeProfit(), 0);
|
||||
if (result) break;
|
||||
else Print("Position modification error: ", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Trailing stop.
|
||||
if (((TrailingStop == Normal) || (TrailingStop == NormalPlusBE)) && ((TSOnProfit == 0) || ((OrderType() == OP_BUY) && (Bid - OrderOpenPrice() >= TSOnProfit * _Point)) || ((OrderType() == OP_SELL) && (OrderOpenPrice() - Ask >= TSOnProfit * _Point))))
|
||||
{
|
||||
if (OrderType() == OP_BUY) new_sl = NormalizeDouble(Bid - SL * _Point, _Digits);
|
||||
else if (OrderType() == OP_SELL) new_sl = NormalizeDouble(Ask + SL * _Point, _Digits);
|
||||
if (((OrderType() == OP_BUY) && (new_sl > OrderStopLoss())) || ((OrderType() == OP_SELL) && ((new_sl < OrderStopLoss()) || (OrderStopLoss() == 0)))) // Avoid moving the SL if this SL is already in a better position.
|
||||
{
|
||||
Print("Moving trailing SL to ", new_sl, ".");
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
bool result = OrderModify(OrderTicket(), OrderOpenPrice(), new_sl, OrderTakeProfit(), 0);
|
||||
if (result) break;
|
||||
else Print("Position modification error: ", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (CloseAfterSeconds > 0)
|
||||
{
|
||||
if (time - news_time >= CloseAfterSeconds)
|
||||
{
|
||||
Print("Closing trade by time out.");
|
||||
double price;
|
||||
RefreshRates();
|
||||
if (OrderType() == OP_BUY) price = Bid;
|
||||
else if (OrderType() == OP_SELL) price = Ask;
|
||||
if (!OrderClose(OrderTicket(), OrderLots(), price, Slippage, clrBlue))
|
||||
{
|
||||
Print("OrderClose() failed: ", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Generic buy. |
|
||||
//+------------------------------------------------------------------+
|
||||
void fBuy()
|
||||
{
|
||||
Print("Opening Buy.");
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
double new_sl = 0, new_tp = 0;
|
||||
double lots = LotsOptimized(OP_BUY);
|
||||
RefreshRates();
|
||||
// Bid and Ask are swapped to preserve the probabilities and decrease/increase profit/loss size.
|
||||
if (!ECN_Mode)
|
||||
{
|
||||
new_sl = NormalizeDouble(Ask - SL * Point, Digits);
|
||||
new_tp = NormalizeDouble(Ask + TP * Point, Digits);
|
||||
}
|
||||
int result = OrderSend(Symbol(), OP_BUY, lots, Ask, Slippage, new_sl, new_tp, Commentary, Magic, 0, clrBlue);
|
||||
Sleep(1000);
|
||||
if (result == -1)
|
||||
{
|
||||
int e = GetLastError();
|
||||
Print("OrderSend Error: ", e, ".");
|
||||
}
|
||||
else return;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Generic sell. |
|
||||
//+------------------------------------------------------------------+
|
||||
void fSell()
|
||||
{
|
||||
Print("Opening Sell.");
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
double new_sl = 0, new_tp = 0;
|
||||
double lots = LotsOptimized(OP_SELL);
|
||||
RefreshRates();
|
||||
// Bid and Ask are swapped to preserve the probabilities and decrease/increase profit/loss size.
|
||||
if (!ECN_Mode)
|
||||
{
|
||||
new_sl = NormalizeDouble(Bid + SL * Point, Digits);
|
||||
new_tp = NormalizeDouble(Bid - TP * Point, Digits);
|
||||
}
|
||||
int result = OrderSend(Symbol(), OP_SELL, lots, Bid, Slippage, new_sl, new_tp, Commentary, Magic, 0, clrRed);
|
||||
Sleep(1000);
|
||||
if (result == -1)
|
||||
{
|
||||
int e = GetLastError();
|
||||
Print("OrderSend Error: ", e, ".");
|
||||
}
|
||||
else return;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate position size depending on money management parameters.|
|
||||
//+------------------------------------------------------------------+
|
||||
double LotsOptimized(int dir)
|
||||
{
|
||||
if (!MM) return Lots;
|
||||
|
||||
double Size, RiskMoney, PositionSize = 0, UnitCost;
|
||||
ProfitCurrency = SymbolInfoString(Symbol(), SYMBOL_CURRENCY_PROFIT);
|
||||
BaseCurrency = SymbolInfoString(Symbol(), SYMBOL_CURRENCY_BASE);
|
||||
ProfitCalcMode = (int)MarketInfo(Symbol(), MODE_PROFITCALCMODE);
|
||||
account_currency = AccountCurrency();
|
||||
// A rough patch for cases when account currency is set as RUR instead of RUB.
|
||||
if (account_currency == "RUR") account_currency = "RUB";
|
||||
if (ProfitCurrency == "RUR") ProfitCurrency = "RUB";
|
||||
if (BaseCurrency == "RUR") BaseCurrency = "RUB";
|
||||
double LotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
|
||||
int LotStep_digits = CountDecimalPlaces(LotStep);
|
||||
|
||||
if (AccountCurrency() == "") return 0;
|
||||
|
||||
if (FixedBalance > 0)
|
||||
{
|
||||
Size = FixedBalance;
|
||||
}
|
||||
else if (UseEquityInsteadOfBalance)
|
||||
{
|
||||
Size = AccountEquity();
|
||||
}
|
||||
else
|
||||
{
|
||||
Size = AccountBalance();
|
||||
}
|
||||
|
||||
if (!UseMoneyInsteadOfPercentage) RiskMoney = Size * Risk / 100;
|
||||
else RiskMoney = MoneyRisk;
|
||||
|
||||
// If Symbol is CFD.
|
||||
if (ProfitCalcMode == 1)
|
||||
UnitCost = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE) * SymbolInfoDouble(Symbol(), SYMBOL_TRADE_CONTRACT_SIZE); // Apparently, it is more accurate than taking TICKVALUE directly in some cases.
|
||||
else UnitCost = MarketInfo(Symbol(), MODE_TICKVALUE); // Futures or Forex.
|
||||
|
||||
if (ProfitCalcMode != 0) // Non-Forex might need to be adjusted.
|
||||
{
|
||||
// If profit currency is different from account currency.
|
||||
if (ProfitCurrency != account_currency)
|
||||
{
|
||||
double CCC = CalculateAdjustment(); // Valid only for loss calculation.
|
||||
// Adjust the unit cost.
|
||||
UnitCost *= CCC;
|
||||
}
|
||||
}
|
||||
|
||||
// If account currency == pair's base currency, adjust UnitCost to future rate (SL). Works only for Forex pairs.
|
||||
if ((account_currency == BaseCurrency) && (ProfitCalcMode == 0))
|
||||
{
|
||||
double current_rate = 1, future_rate = 1;
|
||||
RefreshRates();
|
||||
if (dir == OP_BUY)
|
||||
{
|
||||
current_rate = Ask;
|
||||
future_rate = current_rate - SL * _Point;
|
||||
}
|
||||
else if (dir == OP_SELL)
|
||||
{
|
||||
current_rate = Bid;
|
||||
future_rate = current_rate + SL * _Point;
|
||||
}
|
||||
if (future_rate == 0) future_rate = _Point; // Zero divide prevention.
|
||||
UnitCost *= (current_rate / future_rate);
|
||||
}
|
||||
|
||||
double TickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
|
||||
|
||||
if ((SL != 0) && (UnitCost != 0) && (TickSize != 0)) PositionSize = NormalizeDouble(RiskMoney / (SL * _Point * UnitCost / TickSize), LotStep_digits);
|
||||
|
||||
if (PositionSize < MarketInfo(Symbol(), MODE_MINLOT))
|
||||
{
|
||||
Print("Calculated position size (" + DoubleToString(PositionSize, 2) + ") is less than minimum position size (" + DoubleToString(MarketInfo(Symbol(), MODE_MINLOT), 2) + "). Setting position size to minimum.");
|
||||
PositionSize = MarketInfo(Symbol(), MODE_MINLOT);
|
||||
}
|
||||
else if (PositionSize > MarketInfo(Symbol(), MODE_MAXLOT))
|
||||
{
|
||||
Print("Calculated position size (" + DoubleToString(PositionSize, 2) + ") is greater than maximum position size (" + DoubleToString(MarketInfo(Symbol(), MODE_MAXLOT), 2) + "). Setting position size to maximum.");
|
||||
PositionSize = MarketInfo(Symbol(), MODE_MAXLOT);
|
||||
}
|
||||
|
||||
double steps = PositionSize / LotStep;
|
||||
if (MathFloor(steps) < steps)
|
||||
{
|
||||
Print("Calculated position size (" + DoubleToString(PositionSize, 2) + ") uses uneven step size. Allowed step size = " + DoubleToString(MarketInfo(Symbol(), MODE_LOTSTEP), 2) + ". Setting position size to " + DoubleToString(MathFloor(steps) * LotStep, 2) + ".");
|
||||
PositionSize = MathFloor(steps) * LotStep;
|
||||
}
|
||||
|
||||
return PositionSize;
|
||||
}
|
||||
|
||||
//+-----------------------------------------------------------------------------------+
|
||||
//| Calculates necessary adjustments for cases when ProfitCurrency != AccountCurrency.|
|
||||
//+-----------------------------------------------------------------------------------+
|
||||
#define FOREX_SYMBOLS_ONLY 0
|
||||
#define NONFOREX_SYMBOLS_ONLY 1
|
||||
double CalculateAdjustment()
|
||||
{
|
||||
double add_coefficient = 1; // Might be necessary for correction coefficient calculation if two pairs are used for profit currency to account currency conversion. This is handled differently in MT5 version.
|
||||
if (ReferenceSymbol == NULL)
|
||||
{
|
||||
ReferenceSymbol = GetSymbolByCurrencies(ProfitCurrency, account_currency, FOREX_SYMBOLS_ONLY);
|
||||
if (ReferenceSymbol == NULL) ReferenceSymbol = GetSymbolByCurrencies(ProfitCurrency, account_currency, NONFOREX_SYMBOLS_ONLY);
|
||||
ReferenceSymbolMode = true;
|
||||
// Failed.
|
||||
if (ReferenceSymbol == NULL)
|
||||
{
|
||||
// Reversing currencies.
|
||||
ReferenceSymbol = GetSymbolByCurrencies(account_currency, ProfitCurrency, FOREX_SYMBOLS_ONLY);
|
||||
if (ReferenceSymbol == NULL) ReferenceSymbol = GetSymbolByCurrencies(account_currency, ProfitCurrency, NONFOREX_SYMBOLS_ONLY);
|
||||
ReferenceSymbolMode = false;
|
||||
}
|
||||
if (ReferenceSymbol == NULL)
|
||||
{
|
||||
// The condition checks whether we are caclulating conversion coefficient for the chart's symbol or for some other.
|
||||
// The error output is OK for the current symbol only because it won't be repeated ad infinitum.
|
||||
// It should be avoided for non-chart symbols because it will just flood the log.
|
||||
Print("Couldn't detect proper currency pair for adjustment calculation. Profit currency: ", ProfitCurrency, ". Account currency: ", account_currency, ". Trying to find a possible two-symbol combination.");
|
||||
if ((FindDoubleReferenceSymbol("USD")) // USD should work in 99.9% of cases.
|
||||
|| (FindDoubleReferenceSymbol("EUR")) // For very rare cases.
|
||||
|| (FindDoubleReferenceSymbol("GBP")) // For extremely rare cases.
|
||||
|| (FindDoubleReferenceSymbol("JPY"))) // For extremely rare cases.
|
||||
{
|
||||
Print("Converting via ", ReferenceSymbol, " and ", AdditionalReferenceSymbol, ".");
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Adjustment calculation critical failure. Failed both simple and two-pair conversion methods.");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AdditionalReferenceSymbol != NULL) // If two reference pairs are used.
|
||||
{
|
||||
// Calculate just the additional symbol's coefficient and then use it in final return's multiplication.
|
||||
MqlTick tick;
|
||||
SymbolInfoTick(AdditionalReferenceSymbol, tick);
|
||||
add_coefficient = GetCurrencyCorrectionCoefficient(tick, AdditionalReferenceSymbolMode);
|
||||
}
|
||||
MqlTick tick;
|
||||
SymbolInfoTick(ReferenceSymbol, tick);
|
||||
return GetCurrencyCorrectionCoefficient(tick, ReferenceSymbolMode) * add_coefficient;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------+
|
||||
//| Returns a currency pair with specified base currency and profit currency. |
|
||||
//+---------------------------------------------------------------------------+
|
||||
string GetSymbolByCurrencies(const string base_currency, const string profit_currency, const uint symbol_type)
|
||||
{
|
||||
// Cycle through all symbols.
|
||||
for (int s = 0; s < SymbolsTotal(false); s++)
|
||||
{
|
||||
// Get symbol name by number.
|
||||
string symbolname = SymbolName(s, false);
|
||||
string b_cur;
|
||||
|
||||
// Normal case - Forex pairs:
|
||||
if (MarketInfo(symbolname, MODE_PROFITCALCMODE) == 0)
|
||||
{
|
||||
if (symbol_type == NONFOREX_SYMBOLS_ONLY) continue; // Avoid checking symbols of a wrong type.
|
||||
// Get its base currency.
|
||||
b_cur = SymbolInfoString(symbolname, SYMBOL_CURRENCY_BASE);
|
||||
}
|
||||
else // Weird case for brokers that set conversion pairs as CFDs.
|
||||
{
|
||||
if (symbol_type == FOREX_SYMBOLS_ONLY) continue; // Avoid checking symbols of a wrong type.
|
||||
// Get its base currency as the initial three letters - prone to huge errors!
|
||||
b_cur = StringSubstr(symbolname, 0, 3);
|
||||
}
|
||||
|
||||
// Get its profit currency.
|
||||
string p_cur = SymbolInfoString(symbolname, SYMBOL_CURRENCY_PROFIT);
|
||||
|
||||
// If the currency pair matches both currencies, select it in Market Watch and return its name.
|
||||
if ((b_cur == base_currency) && (p_cur == profit_currency))
|
||||
{
|
||||
// Select if necessary.
|
||||
if (!(bool)SymbolInfoInteger(symbolname, SYMBOL_SELECT)) SymbolSelect(symbolname, true);
|
||||
|
||||
return symbolname;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Finds reference symbols using 2-pair method. |
|
||||
//| Results are returned via reference parameters. |
|
||||
//| Returns true if found the pairs, false otherwise. |
|
||||
//+----------------------------------------------------------------------------+
|
||||
bool FindDoubleReferenceSymbol(const string cross_currency)
|
||||
{
|
||||
// A hypothetical example for better understanding:
|
||||
// The trader buys CAD/CHF.
|
||||
// account_currency is known = SEK.
|
||||
// cross_currency = USD.
|
||||
// profit_currency = CHF.
|
||||
// I.e., we have to buy dollars with francs (using the Ask price) and then sell those for SEKs (using the Bid price).
|
||||
|
||||
ReferenceSymbol = GetSymbolByCurrencies(cross_currency, account_currency, FOREX_SYMBOLS_ONLY);
|
||||
if (ReferenceSymbol == NULL) ReferenceSymbol = GetSymbolByCurrencies(cross_currency, account_currency, NONFOREX_SYMBOLS_ONLY);
|
||||
ReferenceSymbolMode = true; // If found, we've got USD/SEK.
|
||||
|
||||
// Failed.
|
||||
if (ReferenceSymbol == NULL)
|
||||
{
|
||||
// Reversing currencies.
|
||||
ReferenceSymbol = GetSymbolByCurrencies(account_currency, cross_currency, FOREX_SYMBOLS_ONLY);
|
||||
if (ReferenceSymbol == NULL) ReferenceSymbol = GetSymbolByCurrencies(account_currency, cross_currency, NONFOREX_SYMBOLS_ONLY);
|
||||
ReferenceSymbolMode = false; // If found, we've got SEK/USD.
|
||||
}
|
||||
if (ReferenceSymbol == NULL)
|
||||
{
|
||||
Print("Error. Couldn't detect proper currency pair for 2-pair adjustment calculation. Cross currency: ", cross_currency, ". Account currency: ", account_currency, ".");
|
||||
return false;
|
||||
}
|
||||
|
||||
AdditionalReferenceSymbol = GetSymbolByCurrencies(cross_currency, ProfitCurrency, FOREX_SYMBOLS_ONLY);
|
||||
if (AdditionalReferenceSymbol == NULL) AdditionalReferenceSymbol = GetSymbolByCurrencies(cross_currency, ProfitCurrency, NONFOREX_SYMBOLS_ONLY);
|
||||
AdditionalReferenceSymbolMode = false; // If found, we've got USD/CHF. Notice that mode is swapped for cross/profit compared to cross/acc, because it is used in the opposite way.
|
||||
|
||||
// Failed.
|
||||
if (AdditionalReferenceSymbol == NULL)
|
||||
{
|
||||
// Reversing currencies.
|
||||
AdditionalReferenceSymbol = GetSymbolByCurrencies(ProfitCurrency, cross_currency, FOREX_SYMBOLS_ONLY);
|
||||
if (AdditionalReferenceSymbol == NULL) AdditionalReferenceSymbol = GetSymbolByCurrencies(ProfitCurrency, cross_currency, NONFOREX_SYMBOLS_ONLY);
|
||||
AdditionalReferenceSymbolMode = true; // If found, we've got CHF/USD. Notice that mode is swapped for profit/cross compared to acc/cross, because it is used in the opposite way.
|
||||
}
|
||||
if (AdditionalReferenceSymbol == NULL)
|
||||
{
|
||||
Print("Error. Couldn't detect proper currency pair for 2-pair adjustment calculation. Cross currency: ", cross_currency, ". Chart's pair currency: ", ProfitCurrency, ".");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get profit correction coefficient based on current prices. |
|
||||
//| Valid for loss calculation only. |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetCurrencyCorrectionCoefficient(MqlTick &tick, const bool ref_symbol_mode)
|
||||
{
|
||||
if ((tick.ask == 0) || (tick.bid == 0)) return -1; // Data is not yet ready.
|
||||
// Reverse quote.
|
||||
if (ref_symbol_mode)
|
||||
{
|
||||
// Using Buy price for reverse quote.
|
||||
return tick.ask;
|
||||
}
|
||||
// Direct quote.
|
||||
else
|
||||
{
|
||||
// Using Sell price for direct quote.
|
||||
return (1 / tick.bid);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Counts decimal places. |
|
||||
//+------------------------------------------------------------------+
|
||||
int CountDecimalPlaces(double number)
|
||||
{
|
||||
// 100 as maximum length of number.
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
double pwr = MathPow(10, i);
|
||||
if (MathRound(number * pwr) / pwr == number) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,728 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Amazing |
|
||||
//| Copyright © 2023, EarnForex.com |
|
||||
//| https://www.earnforex.com/ |
|
||||
//| Based on the EA by FiFtHeLeMeNt. |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright © 2023, EarnForex"
|
||||
#property link "https://www.earnforex.com/metatrader-expert-advisors/Amazing/"
|
||||
#property version "1.04"
|
||||
#property strict
|
||||
|
||||
#property description "Amazing - EA that helps to trade on news."
|
||||
#property description "Set the NewsDateTime input parameter to the actual date and time of the news."
|
||||
#property description "EA will set up the pending orders (buy and sell) to be triggered by the news."
|
||||
#property description "It will use a profit target, breakeven, and trailing stop to manage it."
|
||||
|
||||
#include <stdlib.mqh>
|
||||
|
||||
input group "Main"
|
||||
input datetime NewsDateTime = __DATE__; // NewsDateTime: Date and time of the news release.
|
||||
input int EntryDistance = 100; // EntryDistance: Entry distance from recent high/low in points.
|
||||
input int StopLoss = 200; // StopLoss: Stop-loss in points.
|
||||
input int TakeProfit = 200; // TakeProfit: Take-profit in points.
|
||||
input int CTCBN = 0; // CTCBN: Number of candles to check before news for High & Low.
|
||||
input int SecBPO = 300; // SecBPO: Seconds before news to place pending orders.
|
||||
input int SecBMO = 0; // SecBMO: Seconds before news when to stop modifying orders.
|
||||
input int STWAN = 150; // STWAN: Seconds to wait after news to delete pending orders.
|
||||
input bool OCO = true; // OCO: EA will cancel the other pending order if one is hit.
|
||||
input int BEPoints = 0; // BEPoints: Points of profit when EA will move SL to breakeven + 1.
|
||||
input int TrailingStop = 0; // Trailing Stop in points
|
||||
input group "ATR"
|
||||
input bool UseATR = false; // Use ATR-based stop-loss and take-profit levels.
|
||||
input int ATR_Period = 14; // ATR Period.
|
||||
input double ATR_Multiplier_SL = 5; // ATR multiplier for SL.
|
||||
input double ATR_Multiplier_TP = 5; // ATR multiplier for TP.
|
||||
input group "Money management"
|
||||
input double Lots = 0.01;
|
||||
input bool MM = true; // Money Management, if true - position sizing based on stop-loss.
|
||||
input double Risk = 1; // Risk - Risk tolerance in percentage points.
|
||||
input double FixedBalance = 0; // FixedBalance: If > 0, trade size calc. uses it as balance.
|
||||
input double MoneyRisk = 0; // MoneyRisk: Risk tolerance in account currency.
|
||||
input bool UseMoneyInsteadOfPercentage = false; // Use money risk instead of percentage.
|
||||
input bool UseEquityInsteadOfBalance = false; // Use equity instead of balance.
|
||||
input group "Miscellaneous"
|
||||
input string TradeLog = "Am_Log_"; // TradeLog: Log file prefix.
|
||||
input string Commentary = "Amazing"; // Commentary: trade description.
|
||||
|
||||
// Global variables:
|
||||
double buy_stop_entry, sell_stop_entry, buy_stop_loss, sell_stop_loss, buy_take_profit, sell_take_profit;
|
||||
int Magic;
|
||||
string filename;
|
||||
|
||||
double SL, TP;
|
||||
double RiskMoney;
|
||||
|
||||
// For tick value adjustment:
|
||||
string ProfitCurrency = "", account_currency = "", BaseCurrency = "", ReferenceSymbol = NULL, AdditionalReferenceSymbol = NULL;
|
||||
bool ReferenceSymbolMode, AdditionalReferenceSymbolMode;
|
||||
int ProfitCalcMode;
|
||||
|
||||
void OnInit()
|
||||
{
|
||||
Magic = (int)NewsDateTime; // Dynamically generated Magic number to allow multiple instances for different news announcements.
|
||||
|
||||
double min_lot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
|
||||
double lot_step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
|
||||
Print("Minimum lot: ", DoubleToString(min_lot, 2), ", lot step: ", DoubleToString(lot_step, 2), ".");
|
||||
if ((Lots < min_lot) && (!MM)) Alert("Lots should be not less than: ", DoubleToString(min_lot, 2), ".");
|
||||
|
||||
// If UseATR = false, these values will be used. Otherwise, ATR values will be calculated later.
|
||||
SL = StopLoss;
|
||||
TP = TakeProfit;
|
||||
|
||||
if (StringLen(Commentary) > 0) filename = TradeLog + Symbol() + "-" + IntegerToString(Month()) + "-" + IntegerToString(Day()) + ".txt";
|
||||
else filename = ""; // Turning logging off.
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
Comment("");
|
||||
}
|
||||
|
||||
// Result Pattern
|
||||
// 1 1 1 1
|
||||
// | | | |
|
||||
// | | | -------- Sell Stop Order
|
||||
// | | --------Buy Stop Order
|
||||
// | --------Sell Position
|
||||
// --------Buy Position
|
||||
int CheckOrdersCondition()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
for (int i = 0; i < OrdersTotal(); i++)
|
||||
{
|
||||
if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
|
||||
{
|
||||
Write(__FUNCTION__ + " | Error selecting an order: " + ErrorDescription(GetLastError()));
|
||||
continue;
|
||||
}
|
||||
if ((OrderSymbol() != Symbol()) || (OrderMagicNumber() != Magic)) continue;
|
||||
|
||||
if (OrderType() == OP_BUY)
|
||||
{
|
||||
result = result + 1000;
|
||||
}
|
||||
else if (OrderType() == OP_SELL)
|
||||
{
|
||||
result = result + 100;
|
||||
}
|
||||
else if (OrderType() == OP_BUYSTOP)
|
||||
{
|
||||
result = result + 10;
|
||||
}
|
||||
else if (OrderType() == OP_SELLSTOP)
|
||||
{
|
||||
result = result + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result; // 0 means there are no trades.
|
||||
}
|
||||
|
||||
void OpenBuyStop()
|
||||
{
|
||||
for (int tries = 0; tries < 10; tries++)
|
||||
{
|
||||
int ticket = OrderSend(Symbol(), OP_BUYSTOP, LotsOptimized(OP_BUY, buy_stop_entry), buy_stop_entry, 0, buy_stop_loss, buy_take_profit, Commentary, Magic);
|
||||
if (ticket < 0)
|
||||
{
|
||||
Write("Error in OrderSend: " + ErrorDescription(GetLastError()) + " Buy Stop @ " + DoubleToString(buy_stop_entry, _Digits) + " SL @ " + DoubleToString(buy_stop_loss, _Digits) + " TP @" + DoubleToString(buy_take_profit, _Digits));
|
||||
}
|
||||
else
|
||||
{
|
||||
Write("Open Buy Stop: OrderSend executed. Ticket = " + IntegerToString(ticket));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenSellStop()
|
||||
{
|
||||
for (int tries = 0; tries < 10; tries++)
|
||||
{
|
||||
int ticket = OrderSend(Symbol(), OP_SELLSTOP, LotsOptimized(OP_SELL, sell_stop_entry), sell_stop_entry, 0, sell_stop_loss, sell_take_profit, Commentary, Magic);
|
||||
if (ticket < 0)
|
||||
{
|
||||
Write("Error in OrderSend: " + ErrorDescription(GetLastError()) + " Sell Stop @ " + DoubleToString(sell_stop_entry, _Digits) + " SL @ " + DoubleToString(sell_stop_loss, _Digits) + " TP @" + DoubleToString(sell_take_profit, _Digits));
|
||||
}
|
||||
else
|
||||
{
|
||||
Write("Open Sell Stop: OrderSend executed. Ticket = " + IntegerToString(ticket));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set breakeven on positions if needed.
|
||||
void DoBE(int byPoints)
|
||||
{
|
||||
for (int i = 0; i < OrdersTotal(); i++)
|
||||
{
|
||||
if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
|
||||
{
|
||||
Write(__FUNCTION__ + " | Error selecting an order: " + ErrorDescription(GetLastError()));
|
||||
continue;
|
||||
}
|
||||
if ((OrderSymbol() != Symbol()) || (OrderMagicNumber() != Magic)) continue;
|
||||
|
||||
if ((OrderType() == OP_BUY) && (NormalizeDouble(Bid - OrderOpenPrice(), _Digits) > NormalizeDouble(byPoints * _Point, _Digits)) && (OrderStopLoss() < OrderOpenPrice()))
|
||||
{
|
||||
Write("Moving stop-loss of Buy order to breakeven + 1 point.");
|
||||
if (!OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice() + _Point, OrderTakeProfit(), OrderExpiration()))
|
||||
{
|
||||
Write(__FUNCTION__ + " | Error modifying Buy: " + ErrorDescription(GetLastError()));
|
||||
}
|
||||
}
|
||||
else if ((OrderType() == OP_SELL) && (NormalizeDouble(OrderOpenPrice() - Ask, _Digits) > NormalizeDouble(byPoints * _Point, _Digits)) && (OrderStopLoss() > OrderOpenPrice()))
|
||||
{
|
||||
Write("Moving stop-loss of Sell order to breakeven - 1 point.");
|
||||
if (!OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice() - _Point, OrderTakeProfit(), OrderExpiration()))
|
||||
{
|
||||
Write(__FUNCTION__ + " | Error modifying Sell: " + ErrorDescription(GetLastError()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trailing stop for open positions.
|
||||
void DoTrail()
|
||||
{
|
||||
for (int i = 0; i < OrdersTotal(); i++)
|
||||
{
|
||||
if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
|
||||
{
|
||||
Write(__FUNCTION__ + " | Error selecting an order: " + ErrorDescription(GetLastError()));
|
||||
continue;
|
||||
}
|
||||
if ((OrderSymbol() != Symbol()) || (OrderMagicNumber() != Magic)) continue;
|
||||
|
||||
if (OrderType() == OP_BUY)
|
||||
{
|
||||
if (Bid - OrderOpenPrice() > _Point * TrailingStop)
|
||||
{
|
||||
if (OrderStopLoss() < NormalizeDouble(Bid - _Point * TrailingStop, _Digits))
|
||||
{
|
||||
if (!OrderModify(OrderTicket(), OrderOpenPrice(), Bid - _Point * TrailingStop, OrderTakeProfit(), OrderExpiration()))
|
||||
{
|
||||
Write(__FUNCTION__ + " | Error modifying Buy: " + ErrorDescription(GetLastError()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (OrderType() == OP_SELL)
|
||||
{
|
||||
if (OrderOpenPrice() - Ask > _Point * TrailingStop)
|
||||
{
|
||||
if ((OrderStopLoss() > NormalizeDouble(Ask + _Point * TrailingStop, _Digits)) || (OrderStopLoss() == 0))
|
||||
{
|
||||
if (!OrderModify(OrderTicket(), OrderOpenPrice(), Ask + _Point * TrailingStop, OrderTakeProfit(), OrderExpiration()))
|
||||
{
|
||||
Write(__FUNCTION__ + " | Error modifying Sell: " + ErrorDescription(GetLastError()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteBuyStop()
|
||||
{
|
||||
for (int i = 0; i < OrdersTotal(); i++) // The order of cycle doesn't matter as only one order will be deleted.
|
||||
{
|
||||
if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
|
||||
{
|
||||
Write(__FUNCTION__ + " | Error selecting an order: " + ErrorDescription(GetLastError()));
|
||||
continue;
|
||||
}
|
||||
if ((OrderSymbol() != Symbol()) || (OrderMagicNumber() != Magic)) continue;
|
||||
|
||||
if (OrderType() == OP_BUYSTOP)
|
||||
{
|
||||
if (!OrderDelete(OrderTicket()))
|
||||
{
|
||||
Write("Error deleting Buy Stop: " + ErrorDescription(GetLastError()));
|
||||
}
|
||||
else Write("Buy Stop order deleted.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteSellStop()
|
||||
{
|
||||
for (int i = 0; i < OrdersTotal(); i++)
|
||||
{
|
||||
if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
|
||||
{
|
||||
Write(__FUNCTION__ + " | Error selecting an order: " + ErrorDescription(GetLastError()));
|
||||
continue;
|
||||
}
|
||||
if ((OrderSymbol() != Symbol()) || (OrderMagicNumber() != Magic)) continue;
|
||||
|
||||
if (OrderType() == OP_SELLSTOP)
|
||||
{
|
||||
if (!OrderDelete(OrderTicket()))
|
||||
{
|
||||
Write("Error deleting Sell Stop: " + ErrorDescription(GetLastError()));
|
||||
}
|
||||
else Write("Sell Stop order deleted.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update pending stop orders according to new price levels.
|
||||
void DoModify()
|
||||
{
|
||||
for (int i = 0; i < OrdersTotal(); i++)
|
||||
{
|
||||
if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
|
||||
{
|
||||
Write(__FUNCTION__ + " | Error selecting an order: " + ErrorDescription(GetLastError()));
|
||||
continue;
|
||||
}
|
||||
if ((OrderSymbol() != Symbol()) || (OrderMagicNumber() != Magic)) continue;
|
||||
|
||||
if (OrderType() == OP_BUYSTOP)
|
||||
{
|
||||
if (OrderOpenPrice() != buy_stop_entry)
|
||||
{
|
||||
if (!OrderModify(OrderTicket(), buy_stop_entry, buy_stop_loss, buy_take_profit, OrderExpiration()))
|
||||
{
|
||||
Write(__FUNCTION__ + " | Error modifying Buy Stop: " + ErrorDescription(GetLastError()));
|
||||
}
|
||||
else Write("Buy Stop OrderModify executed: " + DoubleToString(OrderOpenPrice(), _Digits) + " -> " + DoubleToString(buy_stop_entry, _Digits));
|
||||
}
|
||||
}
|
||||
|
||||
if (OrderType() == OP_SELLSTOP)
|
||||
{
|
||||
if (OrderOpenPrice() != sell_stop_entry)
|
||||
{
|
||||
if (!OrderModify(OrderTicket(), sell_stop_entry, sell_stop_loss, sell_take_profit, OrderExpiration()))
|
||||
{
|
||||
Write(__FUNCTION__ + " | Error modifying Sell Stop: " + ErrorDescription(GetLastError()));
|
||||
}
|
||||
else Write("Sell Stop OrderModify executed: " + DoubleToString(OrderOpenPrice(), _Digits) + " -> " + DoubleToString(sell_stop_entry, _Digits));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prints a string and writes it to a log file too.
|
||||
void Write(string str)
|
||||
{
|
||||
Print(str);
|
||||
|
||||
if (filename == "") return;
|
||||
|
||||
int handle = FileOpen(filename, FILE_READ | FILE_WRITE | FILE_TXT);
|
||||
if (handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Error opening file ", filename, ": ", ErrorDescription(GetLastError()));
|
||||
return;
|
||||
}
|
||||
FileSeek(handle, 0, SEEK_END);
|
||||
FileWrite(handle, str + " Time " + TimeToStr(CurTime(), TIME_DATE | TIME_SECONDS));
|
||||
FileClose(handle);
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
if (BEPoints > 0) DoBE(BEPoints);
|
||||
if (TrailingStop > 0) DoTrail();
|
||||
|
||||
int OrdersCondition = CheckOrdersCondition();
|
||||
|
||||
// Find recent High/Low for pre-news orders.
|
||||
double recent_high = iHigh(NULL, PERIOD_M1, 0);
|
||||
double recent_low = iLow(NULL, PERIOD_M1, 0);
|
||||
for (int i = 1; i <= CTCBN; i++)
|
||||
{
|
||||
if (iHigh(NULL, PERIOD_M1, i) > recent_high) recent_high = iHigh(NULL, PERIOD_M1, i);
|
||||
if (iLow(NULL, PERIOD_M1, i) < recent_low) recent_low = iLow(NULL, PERIOD_M1, i);
|
||||
}
|
||||
|
||||
double spread = Ask - Bid;
|
||||
buy_stop_entry = NormalizeDouble(recent_high + spread + EntryDistance * _Point, _Digits);
|
||||
sell_stop_entry = NormalizeDouble(recent_low - EntryDistance * _Point, _Digits);
|
||||
|
||||
if (UseATR)
|
||||
{
|
||||
// Getting the ATR values
|
||||
double ATR = iATR(NULL, 0, ATR_Period, 0);
|
||||
SL = ATR * ATR_Multiplier_SL;
|
||||
if (SL <= (SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) + SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)) * Point) SL = (SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) + SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)) * Point;
|
||||
TP = ATR * ATR_Multiplier_TP;
|
||||
if (TP <= (SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) + SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)) * Point) TP = (SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) + SymbolInfoInteger(Symbol(), SYMBOL_SPREAD)) * Point;
|
||||
SL /= Point;
|
||||
TP /= Point;
|
||||
}
|
||||
|
||||
buy_stop_loss = NormalizeDouble(buy_stop_entry - SL * _Point, _Digits);
|
||||
sell_stop_loss = NormalizeDouble(sell_stop_entry + SL * _Point, _Digits);
|
||||
buy_take_profit = NormalizeDouble(buy_stop_entry + TP * _Point, _Digits);
|
||||
sell_take_profit= NormalizeDouble(sell_stop_entry - TP * _Point, _Digits);
|
||||
|
||||
int sectonews = (int)(NewsDateTime - TimeCurrent());
|
||||
Comment("\nAmazing Expert Advisor",
|
||||
"\nHigh @ ", recent_high, " Buy Order @ ", buy_stop_entry, " Stop-loss @ ", buy_stop_loss, " Take-profit @ ", buy_take_profit,
|
||||
"\nLow @ ", recent_low, " Sell Order @ ", sell_stop_entry, " Stop-loss @ ", sell_stop_loss, " Take-profit @ ", sell_take_profit,
|
||||
"\nNews time: ", TimeToString(NewsDateTime),
|
||||
"\nCurrent time: ", TimeToString(TimeCurrent()),
|
||||
"\nSeconds left to news: ", IntegerToString(sectonews),
|
||||
"\nCTCBN: ", CTCBN, " SecBPO: ", SecBPO, " SecBMO: ", SecBMO, " STWAN: ", STWAN, " OCO: ", OCO, " BEPips: ", BEPoints,
|
||||
"\nMoney management: ", MM, " Risk: ", DoubleToString(RiskMoney, 2), " ", AccountCurrency(), " Lots (B/S): ", DoubleToString(LotsOptimized(OP_BUY, buy_stop_entry), 2), "/", DoubleToString(LotsOptimized(OP_SELL, sell_stop_entry), 2));
|
||||
|
||||
// Before the news, but after the time when orders have to be placed.
|
||||
if ((TimeCurrent() < NewsDateTime) && (TimeCurrent() >= NewsDateTime - SecBPO))
|
||||
{
|
||||
if (OrdersCondition == 0) // No orders.
|
||||
{
|
||||
Write("Opening Buy Stop and Sell Stop. OrdersCondition = " + IntegerToString(OrdersCondition) + " Timestamp = " + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + ".");
|
||||
OpenBuyStop();
|
||||
OpenSellStop();
|
||||
}
|
||||
else if (OrdersCondition == 10)
|
||||
{
|
||||
Write("Opening Sell Stop. OrdersCondition = " + IntegerToString(OrdersCondition) + " Timestamp = " + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + ".");
|
||||
OpenSellStop();
|
||||
}
|
||||
else if (OrdersCondition == 1)
|
||||
{
|
||||
Write("Opening Buy Stop. OrdersCondition = " + IntegerToString(OrdersCondition) + " Timestamp = " + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + ".");
|
||||
OpenBuyStop();
|
||||
}
|
||||
}
|
||||
|
||||
// Still have time to modify the orders.
|
||||
if ((TimeCurrent() < NewsDateTime) && (TimeCurrent() >= NewsDateTime - SecBPO) && (TimeCurrent() < NewsDateTime - SecBMO))
|
||||
{
|
||||
Write("Modifying orders. OrdersCondition = " + IntegerToString(OrdersCondition) + " Timestamp = " + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + ".");
|
||||
DoModify();
|
||||
}
|
||||
|
||||
// News announcement already happened, but it is too early to delete all untriggered orders, yet the EA has to delete the untriggered one due to OCO if the opposite was hit.
|
||||
if ((TimeCurrent() > NewsDateTime) && (TimeCurrent() < NewsDateTime + STWAN) && (OCO))
|
||||
{
|
||||
if (OrdersCondition == 1001)
|
||||
{
|
||||
Write("Deleting Sell Stop because Buy Stop was hit. OrdersCondition = " + IntegerToString(OrdersCondition) + " Timestamp = " + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + ".");
|
||||
DeleteSellStop();
|
||||
}
|
||||
else if (OrdersCondition == 110)
|
||||
{
|
||||
Write("Deleting Buy Stop because Sell Stop was hit. OrdersCondition=" + IntegerToString(OrdersCondition) + " Timestamp=" + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + ".");
|
||||
DeleteBuyStop();
|
||||
}
|
||||
}
|
||||
|
||||
// News has passed and it is time to delete untriggered orders.
|
||||
if ((TimeCurrent() > NewsDateTime) && (TimeCurrent() > NewsDateTime + STWAN))
|
||||
{
|
||||
if (OrdersCondition == 11)
|
||||
{
|
||||
Write("Deleting Buy Stop and Sell Stop because time expired. OrdersCondition = " + IntegerToString(OrdersCondition) + " Timestamp=" + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + ".");
|
||||
DeleteBuyStop();
|
||||
DeleteSellStop();
|
||||
}
|
||||
|
||||
if ((OrdersCondition == 10) || (OrdersCondition == 110))
|
||||
{
|
||||
Write("Deleting BuyStop Because expired, OrdersCondition=" + IntegerToString(OrdersCondition) + " Timestamp = " + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + ".");
|
||||
DeleteBuyStop();
|
||||
}
|
||||
|
||||
if ((OrdersCondition == 1) || (OrdersCondition == 1001))
|
||||
{
|
||||
Write("Deleting SellStop Because expired, OrdersCondition=" + IntegerToString(OrdersCondition) + " Timestamp = " + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + ".");
|
||||
DeleteSellStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate position size depending on money management parameters.|
|
||||
//+------------------------------------------------------------------+
|
||||
double LotsOptimized(int dir, double entry)
|
||||
{
|
||||
if (!MM) return Lots;
|
||||
|
||||
double Size, PositionSize = 0, UnitCost;
|
||||
ProfitCurrency = SymbolInfoString(Symbol(), SYMBOL_CURRENCY_PROFIT);
|
||||
BaseCurrency = SymbolInfoString(Symbol(), SYMBOL_CURRENCY_BASE);
|
||||
ProfitCalcMode = (int)MarketInfo(Symbol(), MODE_PROFITCALCMODE);
|
||||
account_currency = AccountCurrency();
|
||||
// A rough patch for cases when account currency is set as RUR instead of RUB.
|
||||
if (account_currency == "RUR") account_currency = "RUB";
|
||||
if (ProfitCurrency == "RUR") ProfitCurrency = "RUB";
|
||||
if (BaseCurrency == "RUR") BaseCurrency = "RUB";
|
||||
double LotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
|
||||
int LotStep_digits = CountDecimalPlaces(LotStep);
|
||||
|
||||
if (AccountCurrency() == "") return 0;
|
||||
|
||||
if (FixedBalance > 0)
|
||||
{
|
||||
Size = FixedBalance;
|
||||
}
|
||||
else if (UseEquityInsteadOfBalance)
|
||||
{
|
||||
Size = AccountEquity();
|
||||
}
|
||||
else
|
||||
{
|
||||
Size = AccountBalance();
|
||||
}
|
||||
|
||||
if (!UseMoneyInsteadOfPercentage) RiskMoney = Size * Risk / 100;
|
||||
else RiskMoney = MoneyRisk;
|
||||
|
||||
// If Symbol is CFD.
|
||||
if (ProfitCalcMode == 1)
|
||||
UnitCost = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE) * SymbolInfoDouble(Symbol(), SYMBOL_TRADE_CONTRACT_SIZE); // Apparently, it is more accurate than taking TICKVALUE directly in some cases.
|
||||
else UnitCost = MarketInfo(Symbol(), MODE_TICKVALUE); // Futures or Forex.
|
||||
|
||||
if (ProfitCalcMode != 0) // Non-Forex might need to be adjusted.
|
||||
{
|
||||
// If profit currency is different from account currency.
|
||||
if (ProfitCurrency != account_currency)
|
||||
{
|
||||
double CCC = CalculateAdjustment(); // Valid only for loss calculation.
|
||||
// Adjust the unit cost.
|
||||
UnitCost *= CCC;
|
||||
}
|
||||
}
|
||||
|
||||
// If account currency == pair's base currency, adjust UnitCost to future rate (SL). Works only for Forex pairs.
|
||||
if ((account_currency == BaseCurrency) && (ProfitCalcMode == 0))
|
||||
{
|
||||
double current_rate = 1, future_rate = 1;
|
||||
RefreshRates();
|
||||
if (dir == OP_BUY)
|
||||
{
|
||||
if (entry == 0) current_rate = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
else current_rate = entry;
|
||||
future_rate = current_rate - SL * _Point;
|
||||
}
|
||||
else if (dir == OP_SELL)
|
||||
{
|
||||
if (entry == 0) current_rate = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
else current_rate = entry;
|
||||
future_rate = current_rate + SL * _Point;
|
||||
}
|
||||
if (future_rate == 0) future_rate = _Point; // Zero divide prevention.
|
||||
UnitCost *= (current_rate / future_rate);
|
||||
}
|
||||
|
||||
double TickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
|
||||
|
||||
if ((SL != 0) && (UnitCost != 0) && (TickSize != 0)) PositionSize = NormalizeDouble(RiskMoney / (SL * _Point * UnitCost / TickSize), LotStep_digits);
|
||||
|
||||
if (PositionSize < MarketInfo(Symbol(), MODE_MINLOT))
|
||||
{
|
||||
Print("Calculated position size (" + DoubleToString(PositionSize, 2) + ") is less than minimum position size (" + DoubleToString(MarketInfo(Symbol(), MODE_MINLOT), 2) + "). Setting position size to minimum.");
|
||||
PositionSize = MarketInfo(Symbol(), MODE_MINLOT);
|
||||
}
|
||||
else if (PositionSize > MarketInfo(Symbol(), MODE_MAXLOT))
|
||||
{
|
||||
Print("Calculated position size (" + DoubleToString(PositionSize, 2) + ") is greater than maximum position size (" + DoubleToString(MarketInfo(Symbol(), MODE_MAXLOT), 2) + "). Setting position size to maximum.");
|
||||
PositionSize = MarketInfo(Symbol(), MODE_MAXLOT);
|
||||
}
|
||||
|
||||
double steps = PositionSize / LotStep;
|
||||
if (MathAbs(MathRound(steps) - steps) < 0.00000001) steps = MathRound(steps);
|
||||
if (steps - MathFloor(steps) > 0.5)
|
||||
{
|
||||
Print(steps, " ", MathFloor(steps));
|
||||
Print("Calculated position size (" + DoubleToString(PositionSize, 2) + ") uses uneven step size. Allowed step size = " + DoubleToString(MarketInfo(Symbol(), MODE_LOTSTEP), 2) + ". Setting position size to " + DoubleToString(MathFloor(steps) * LotStep, 2) + ".");
|
||||
PositionSize = MathFloor(steps) * LotStep;
|
||||
}
|
||||
|
||||
return PositionSize;
|
||||
}
|
||||
|
||||
//+-----------------------------------------------------------------------------------+
|
||||
//| Calculates necessary adjustments for cases when ProfitCurrency != AccountCurrency.|
|
||||
//+-----------------------------------------------------------------------------------+
|
||||
#define FOREX_SYMBOLS_ONLY 0
|
||||
#define NONFOREX_SYMBOLS_ONLY 1
|
||||
double CalculateAdjustment()
|
||||
{
|
||||
double add_coefficient = 1; // Might be necessary for correction coefficient calculation if two pairs are used for profit currency to account currency conversion. This is handled differently in MT5 version.
|
||||
if (ReferenceSymbol == NULL)
|
||||
{
|
||||
ReferenceSymbol = GetSymbolByCurrencies(ProfitCurrency, account_currency, FOREX_SYMBOLS_ONLY);
|
||||
if (ReferenceSymbol == NULL) ReferenceSymbol = GetSymbolByCurrencies(ProfitCurrency, account_currency, NONFOREX_SYMBOLS_ONLY);
|
||||
ReferenceSymbolMode = true;
|
||||
// Failed.
|
||||
if (ReferenceSymbol == NULL)
|
||||
{
|
||||
// Reversing currencies.
|
||||
ReferenceSymbol = GetSymbolByCurrencies(account_currency, ProfitCurrency, FOREX_SYMBOLS_ONLY);
|
||||
if (ReferenceSymbol == NULL) ReferenceSymbol = GetSymbolByCurrencies(account_currency, ProfitCurrency, NONFOREX_SYMBOLS_ONLY);
|
||||
ReferenceSymbolMode = false;
|
||||
}
|
||||
if (ReferenceSymbol == NULL)
|
||||
{
|
||||
// The condition checks whether we are caclulating conversion coefficient for the chart's symbol or for some other.
|
||||
// The error output is OK for the current symbol only because it won't be repeated ad infinitum.
|
||||
// It should be avoided for non-chart symbols because it will just flood the log.
|
||||
Print("Couldn't detect proper currency pair for adjustment calculation. Profit currency: ", ProfitCurrency, ". Account currency: ", account_currency, ". Trying to find a possible two-symbol combination.");
|
||||
if ((FindDoubleReferenceSymbol("USD")) // USD should work in 99.9% of cases.
|
||||
|| (FindDoubleReferenceSymbol("EUR")) // For very rare cases.
|
||||
|| (FindDoubleReferenceSymbol("GBP")) // For extremely rare cases.
|
||||
|| (FindDoubleReferenceSymbol("JPY"))) // For extremely rare cases.
|
||||
{
|
||||
Print("Converting via ", ReferenceSymbol, " and ", AdditionalReferenceSymbol, ".");
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Adjustment calculation critical failure. Failed both simple and two-pair conversion methods.");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AdditionalReferenceSymbol != NULL) // If two reference pairs are used.
|
||||
{
|
||||
// Calculate just the additional symbol's coefficient and then use it in final return's multiplication.
|
||||
MqlTick tick;
|
||||
SymbolInfoTick(AdditionalReferenceSymbol, tick);
|
||||
add_coefficient = GetCurrencyCorrectionCoefficient(tick, AdditionalReferenceSymbolMode);
|
||||
}
|
||||
MqlTick tick;
|
||||
SymbolInfoTick(ReferenceSymbol, tick);
|
||||
return GetCurrencyCorrectionCoefficient(tick, ReferenceSymbolMode) * add_coefficient;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------+
|
||||
//| Returns a currency pair with specified base currency and profit currency. |
|
||||
//+---------------------------------------------------------------------------+
|
||||
string GetSymbolByCurrencies(const string base_currency, const string profit_currency, const uint symbol_type)
|
||||
{
|
||||
// Cycle through all symbols.
|
||||
for (int s = 0; s < SymbolsTotal(false); s++)
|
||||
{
|
||||
// Get symbol name by number.
|
||||
string symbolname = SymbolName(s, false);
|
||||
string b_cur;
|
||||
|
||||
// Normal case - Forex pairs:
|
||||
if (MarketInfo(symbolname, MODE_PROFITCALCMODE) == 0)
|
||||
{
|
||||
if (symbol_type == NONFOREX_SYMBOLS_ONLY) continue; // Avoid checking symbols of a wrong type.
|
||||
// Get its base currency.
|
||||
b_cur = SymbolInfoString(symbolname, SYMBOL_CURRENCY_BASE);
|
||||
}
|
||||
else // Weird case for brokers that set conversion pairs as CFDs.
|
||||
{
|
||||
if (symbol_type == FOREX_SYMBOLS_ONLY) continue; // Avoid checking symbols of a wrong type.
|
||||
// Get its base currency as the initial three letters - prone to huge errors!
|
||||
b_cur = StringSubstr(symbolname, 0, 3);
|
||||
}
|
||||
|
||||
// Get its profit currency.
|
||||
string p_cur = SymbolInfoString(symbolname, SYMBOL_CURRENCY_PROFIT);
|
||||
|
||||
// If the currency pair matches both currencies, select it in Market Watch and return its name.
|
||||
if ((b_cur == base_currency) && (p_cur == profit_currency))
|
||||
{
|
||||
// Select if necessary.
|
||||
if (!(bool)SymbolInfoInteger(symbolname, SYMBOL_SELECT)) SymbolSelect(symbolname, true);
|
||||
|
||||
return symbolname;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Finds reference symbols using 2-pair method. |
|
||||
//| Results are returned via reference parameters. |
|
||||
//| Returns true if found the pairs, false otherwise. |
|
||||
//+----------------------------------------------------------------------------+
|
||||
bool FindDoubleReferenceSymbol(const string cross_currency)
|
||||
{
|
||||
// A hypothetical example for better understanding:
|
||||
// The trader buys CAD/CHF.
|
||||
// account_currency is known = SEK.
|
||||
// cross_currency = USD.
|
||||
// profit_currency = CHF.
|
||||
// I.e., we have to buy dollars with francs (using the Ask price) and then sell those for SEKs (using the Bid price).
|
||||
|
||||
ReferenceSymbol = GetSymbolByCurrencies(cross_currency, account_currency, FOREX_SYMBOLS_ONLY);
|
||||
if (ReferenceSymbol == NULL) ReferenceSymbol = GetSymbolByCurrencies(cross_currency, account_currency, NONFOREX_SYMBOLS_ONLY);
|
||||
ReferenceSymbolMode = true; // If found, we've got USD/SEK.
|
||||
|
||||
// Failed.
|
||||
if (ReferenceSymbol == NULL)
|
||||
{
|
||||
// Reversing currencies.
|
||||
ReferenceSymbol = GetSymbolByCurrencies(account_currency, cross_currency, FOREX_SYMBOLS_ONLY);
|
||||
if (ReferenceSymbol == NULL) ReferenceSymbol = GetSymbolByCurrencies(account_currency, cross_currency, NONFOREX_SYMBOLS_ONLY);
|
||||
ReferenceSymbolMode = false; // If found, we've got SEK/USD.
|
||||
}
|
||||
if (ReferenceSymbol == NULL)
|
||||
{
|
||||
Print("Error. Couldn't detect proper currency pair for 2-pair adjustment calculation. Cross currency: ", cross_currency, ". Account currency: ", account_currency, ".");
|
||||
return false;
|
||||
}
|
||||
|
||||
AdditionalReferenceSymbol = GetSymbolByCurrencies(cross_currency, ProfitCurrency, FOREX_SYMBOLS_ONLY);
|
||||
if (AdditionalReferenceSymbol == NULL) AdditionalReferenceSymbol = GetSymbolByCurrencies(cross_currency, ProfitCurrency, NONFOREX_SYMBOLS_ONLY);
|
||||
AdditionalReferenceSymbolMode = false; // If found, we've got USD/CHF. Notice that mode is swapped for cross/profit compared to cross/acc, because it is used in the opposite way.
|
||||
|
||||
// Failed.
|
||||
if (AdditionalReferenceSymbol == NULL)
|
||||
{
|
||||
// Reversing currencies.
|
||||
AdditionalReferenceSymbol = GetSymbolByCurrencies(ProfitCurrency, cross_currency, FOREX_SYMBOLS_ONLY);
|
||||
if (AdditionalReferenceSymbol == NULL) AdditionalReferenceSymbol = GetSymbolByCurrencies(ProfitCurrency, cross_currency, NONFOREX_SYMBOLS_ONLY);
|
||||
AdditionalReferenceSymbolMode = true; // If found, we've got CHF/USD. Notice that mode is swapped for profit/cross compared to acc/cross, because it is used in the opposite way.
|
||||
}
|
||||
if (AdditionalReferenceSymbol == NULL)
|
||||
{
|
||||
Print("Error. Couldn't detect proper currency pair for 2-pair adjustment calculation. Cross currency: ", cross_currency, ". Chart's pair currency: ", ProfitCurrency, ".");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get profit correction coefficient based on current prices. |
|
||||
//| Valid for loss calculation only. |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetCurrencyCorrectionCoefficient(MqlTick &tick, const bool ref_symbol_mode)
|
||||
{
|
||||
if ((tick.ask == 0) || (tick.bid == 0)) return -1; // Data is not yet ready.
|
||||
// Reverse quote.
|
||||
if (ref_symbol_mode)
|
||||
{
|
||||
// Using Buy price for reverse quote.
|
||||
return tick.ask;
|
||||
}
|
||||
// Direct quote.
|
||||
else
|
||||
{
|
||||
// Using Sell price for direct quote.
|
||||
return (1 / tick.bid);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Counts decimal places. |
|
||||
//+------------------------------------------------------------------+
|
||||
int CountDecimalPlaces(double number)
|
||||
{
|
||||
// 100 as maximum length of number.
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
double pwr = MathPow(10, i);
|
||||
if (MathRound(number * pwr) / pwr == number) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
@@ -0,0 +1,174 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| Binario.mq4 |
|
||||
//| Copyright © 2008-2022, EarnForex.com |
|
||||
//| https://www.earnforex.com/ |
|
||||
//| Based on the EA by don_forex. |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright © 2008-2022, EarnForex"
|
||||
#property link "https://www.earnforex.com/metatrader-expert-advisors/Binario/"
|
||||
#property version "1.01"
|
||||
#property strict
|
||||
|
||||
#property description "Uses a band of two same period MAs - one over High prices, one over Low prices."
|
||||
#property description "A breakout from within the bands triggers a trade."
|
||||
|
||||
input group "Main"
|
||||
input int MA_Period = 144; // MA Period
|
||||
input ENUM_MA_METHOD MA_Method = MODE_EMA; // MA Method
|
||||
input int TakeProfit = 100;
|
||||
input int PipDifference = 25; // PipDifference: distance from MA for breakout.
|
||||
input group "Money management"
|
||||
input double Lots = 0.1; // Lots: Fixed position size.
|
||||
input double MaximumRisk = 2; // MaximumRisk: Position sizing increase coefficient. 0 - disable.
|
||||
input group "Miscellaneous"
|
||||
input int Slippage = 3;
|
||||
input string OrderCommentary = "Binario";
|
||||
input int Magic = 16384;
|
||||
|
||||
double Poin;
|
||||
|
||||
void OnInit()
|
||||
{
|
||||
// Checking for unconvetional Point digits number.
|
||||
if (Point == 0.00001) Poin = 0.0001; // 5 digits.
|
||||
else if (Point == 0.001) Poin = 0.01; // 3 digits.
|
||||
else Poin = Point; // Normal.
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
if (Bars(Symbol(), Period()) < 144)
|
||||
{
|
||||
Print("Fewer than 144 bars on the chart. Trading disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
double MA144H = MathRound(iMA(NULL, 0, 144, 0, MODE_EMA, PRICE_HIGH, 0) / Poin) * Poin;
|
||||
double MA144L = MathRound(iMA(NULL, 0, 144, 0, MODE_EMA, PRICE_LOW, 0) / Poin) * Poin;
|
||||
|
||||
double Spread = Ask - Bid;
|
||||
|
||||
double BuyPrice = NormalizeDouble(MA144H + Spread + PipDifference * Poin, _Digits);
|
||||
double BuyStopLoss = NormalizeDouble(MA144L - Poin, _Digits);
|
||||
double BuyTakeProfit = NormalizeDouble(MA144H + (PipDifference + TakeProfit) * Poin, _Digits);
|
||||
double SellPrice = NormalizeDouble(MA144L - (PipDifference) * Poin, _Digits);
|
||||
double SellStopLoss = NormalizeDouble(MA144H + Spread + Poin, _Digits);
|
||||
double SellTakeProfit = NormalizeDouble(MA144L - Spread - (PipDifference + TakeProfit) * Poin, _Digits);
|
||||
|
||||
double Lot = Lots;
|
||||
if (MaximumRisk > 0) // Use increasing position size.
|
||||
{
|
||||
int LotStep_digits = CountDecimalPlaces(SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP));
|
||||
Lot = NormalizeDouble(AccountInfoDouble(ACCOUNT_MARGIN_FREE) * MaximumRisk / 50000, LotStep_digits);
|
||||
}
|
||||
if (Lot < SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN)) Lot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
|
||||
if (Lot > SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX)) Lot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX);
|
||||
|
||||
bool need_long = true;
|
||||
bool need_short = true;
|
||||
|
||||
int total = OrdersTotal();
|
||||
for (int cnt = 0; cnt < total; cnt++)
|
||||
{
|
||||
if (!OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES))
|
||||
{
|
||||
Print("OrderSelect() failed. Error: ", GetLastError());
|
||||
continue;
|
||||
}
|
||||
if ((OrderSymbol() != Symbol()) || (OrderMagicNumber() != Magic)) continue;
|
||||
|
||||
if (OrderType() == OP_BUYSTOP)
|
||||
{
|
||||
need_long = false;
|
||||
if (OrderStopLoss() != BuyStopLoss)
|
||||
{
|
||||
if (!OrderModify(OrderTicket(), BuyPrice, BuyStopLoss, BuyTakeProfit, 0))
|
||||
{
|
||||
Print("OrderModify() failed. Error: ", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (OrderType() == OP_SELLSTOP)
|
||||
{
|
||||
need_short = false;
|
||||
if (OrderStopLoss() != SellStopLoss)
|
||||
{
|
||||
if (!OrderModify(OrderTicket(), SellPrice, SellStopLoss, SellTakeProfit, 0))
|
||||
{
|
||||
Print("OrderModify() failed. Error: ", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (OrderType() == OP_BUY)
|
||||
{
|
||||
need_long = false;
|
||||
if (OrderStopLoss() < BuyStopLoss)
|
||||
{
|
||||
if (!OrderModify(OrderTicket(), OrderOpenPrice(), BuyStopLoss, BuyTakeProfit, 0))
|
||||
{
|
||||
Print("OrderModify() failed. Error: ", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (OrderType() == OP_SELL)
|
||||
{
|
||||
need_short = false;
|
||||
if (OrderStopLoss() > SellStopLoss)
|
||||
{
|
||||
if (!OrderModify(OrderTicket(), OrderOpenPrice(), SellStopLoss, SellTakeProfit, 0))
|
||||
{
|
||||
Print("OrderModify() failed. Error: ", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AccountFreeMargin() < (1000 * Lot))
|
||||
{
|
||||
Print("No money. Free margin = ", AccountFreeMargin());
|
||||
return;
|
||||
}
|
||||
|
||||
if ((Bid < MA144H) && (Bid > MA144L)) // Inside the MA bands.
|
||||
{
|
||||
if (need_long)
|
||||
{
|
||||
for (int i = 0; i < 10; i++) // 10 attempts.
|
||||
{
|
||||
int ticket = OrderSend(Symbol(), OP_BUYSTOP, Lot, BuyPrice, Slippage, BuyStopLoss, BuyTakeProfit, OrderCommentary, Magic, 0, clrGreen);
|
||||
if (ticket == -1)
|
||||
{
|
||||
Print("OrderSend() failed. Error: ", GetLastError());
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
else if (need_short)
|
||||
{
|
||||
for (int i = 0; i < 10; i++) // 10 attempts.
|
||||
{
|
||||
int ticket = OrderSend(Symbol(), OP_SELLSTOP, Lot, SellPrice, Slippage, SellStopLoss, SellTakeProfit, OrderCommentary, Magic, 0, clrRed);
|
||||
if (ticket == -1)
|
||||
{
|
||||
Print("OrderSend() failed. Error: ", GetLastError());
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Counts decimal places. |
|
||||
//+------------------------------------------------------------------+
|
||||
int CountDecimalPlaces(double number)
|
||||
{
|
||||
// 100 as maximum length of number.
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
double pwr = MathPow(10, i);
|
||||
if (MathRound(number * pwr) / pwr == number) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user