348 líneas
24 KiB
MQL5
348 líneas
24 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| MovingAverageFilteredL1.mq5 |
|
|
//| Copyright 2000-2026, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2000-2026, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.00"
|
|
//--- best MovingAverage parameters for EURUSD,H1,2025
|
|
input int MovingPeriod = 61; // MA period
|
|
input int MovingShift = 0; // MA shift
|
|
//--- trade volume
|
|
input double TradeLot = 0.1; // Lot size
|
|
//--- L1 filter parameters
|
|
input int L1TotalBars = 1000; // Total bars for L1 filter
|
|
input bool L1FilterOpen = false; // Use filter for Open
|
|
input bool L1FilterClose = false; // Use filter for Close
|
|
input double L1CoefLambda = 0.2; // Lambda in lambda_max units
|
|
//--- save statistics
|
|
input bool SaveStatistics = false; // Save statistics to file
|
|
//---
|
|
#define MA_MAGIC 1234501
|
|
#include <Trade\Trade.mqh>
|
|
CTrade ExtTrade;
|
|
int ExtHandle = INVALID_HANDLE;
|
|
bool ExtHedging = false;
|
|
string ExtStrategyName="MA";
|
|
string ExtStrategyFileName="";
|
|
//+------------------------------------------------------------------+
|
|
//| Check new bar |
|
|
//+------------------------------------------------------------------+
|
|
bool IsNewBar()
|
|
{
|
|
static datetime last_time = 0;
|
|
datetime t[1];
|
|
if(CopyTime(_Symbol,_Period,0,1,t) > 0)
|
|
{
|
|
if(t[0] != last_time)
|
|
{
|
|
last_time = t[0];
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Print("CopyTime error: ", GetLastError());
|
|
ResetLastError();
|
|
}
|
|
return false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| CheckTrendL1 |
|
|
//+------------------------------------------------------------------+
|
|
double CheckTrendL1()
|
|
{
|
|
int max_bars=L1TotalBars;
|
|
MqlRates rates_data[];
|
|
ArrayResize(rates_data,max_bars);
|
|
ArraySetAsSeries(rates_data,false);
|
|
if(CopyRates(_Symbol,_Period,0,max_bars,rates_data) != max_bars)
|
|
{
|
|
Print("CopyRates failed for L1Trend");
|
|
return 0;
|
|
}
|
|
//--- prepare data (close prices vector)
|
|
int data_count=max_bars;
|
|
vector<double> data_close;
|
|
data_close.Resize(data_count);
|
|
for(int i=0; i<data_count; i++)
|
|
data_close[i] = rates_data[i].close;
|
|
//--- calculate L1 filter
|
|
vector<double> data_filtered;
|
|
data_filtered.Resize(data_count);
|
|
double delta=0.0;
|
|
//double lambda_max=data_close.L1TrendFilterLambdaMax();
|
|
//bool res=data_close.L1TrendFilter(data_filtered,L1CoefLambda*lambda_max,false);
|
|
bool res=data_close.L1TrendFilter(data_filtered,L1CoefLambda,true);
|
|
if(res)
|
|
delta = data_filtered[data_count-1] - data_filtered[data_count-2];
|
|
//---
|
|
return delta;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| GetTradeSignal |
|
|
//+------------------------------------------------------------------+
|
|
bool GetTradeSignal(ENUM_ORDER_TYPE &signal)
|
|
{
|
|
signal = WRONG_VALUE;
|
|
MqlRates bars[];
|
|
double ma[];
|
|
ArraySetAsSeries(bars,true);
|
|
ArraySetAsSeries(ma,true);
|
|
ArrayResize(bars,2);
|
|
ArrayResize(ma,2);
|
|
//-- two last closed bars
|
|
if(CopyRates(_Symbol,_Period,2,2,bars) != 2)
|
|
{
|
|
Print("CopyRates failed");
|
|
return false;
|
|
}
|
|
if(CopyBuffer(ExtHandle,0,2,2,ma) != 2)
|
|
{
|
|
Print("CopyBuffer failed");
|
|
return false;
|
|
}
|
|
double close_prev = bars[1].close;
|
|
double close_last = bars[0].close;
|
|
double ma_prev = ma[1];
|
|
double ma_last = ma[0];
|
|
//--- check MA crossover
|
|
if(close_prev < ma_prev && close_last > ma_last)
|
|
signal = ORDER_TYPE_BUY;
|
|
else
|
|
if(close_prev > ma_prev && close_last < ma_last)
|
|
signal = ORDER_TYPE_SELL;
|
|
//--- log
|
|
PrintFormat("PrevBar: time=%s close=%.5f ma=%.5f | LastBar: time=%s close=%.5f ma=%.5f | Signal=%s",
|
|
TimeToString(bars[0].time,TIME_DATE|TIME_MINUTES), close_prev, ma_prev,
|
|
TimeToString(bars[1].time,TIME_DATE|TIME_MINUTES), close_last, ma_last,
|
|
(signal==ORDER_TYPE_BUY?"BUY":signal==ORDER_TYPE_SELL?"SELL":"NONE"));
|
|
//---
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| CheckForOpen |
|
|
//+------------------------------------------------------------------+
|
|
void CheckForOpen()
|
|
{
|
|
ENUM_ORDER_TYPE signal;
|
|
if(!GetTradeSignal(signal))
|
|
return;
|
|
if(signal == WRONG_VALUE)
|
|
return;
|
|
//--- L1 filter
|
|
if(L1FilterOpen)
|
|
{
|
|
double delta = CheckTrendL1();
|
|
if(signal == ORDER_TYPE_BUY && delta < 0)
|
|
{
|
|
signal = WRONG_VALUE;
|
|
PrintFormat("Open BUY signal cancelled by L1 trend delta=%.5f", delta);
|
|
}
|
|
if(signal == ORDER_TYPE_SELL && delta > 0)
|
|
{
|
|
signal = WRONG_VALUE;
|
|
PrintFormat("Open SELL signal cancelled by L1 trend delta=%.5f", delta);
|
|
}
|
|
}
|
|
//---
|
|
if(signal == WRONG_VALUE)
|
|
return;
|
|
//---
|
|
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || Bars(_Symbol,_Period)<L1TotalBars)
|
|
return;
|
|
//---
|
|
double price = (signal==ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol,SYMBOL_ASK): SymbolInfoDouble(_Symbol,SYMBOL_BID);
|
|
//---
|
|
ExtTrade.PositionOpen(_Symbol, signal, TradeLot, price, 0, 0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| CheckForClose |
|
|
//+------------------------------------------------------------------+
|
|
void CheckForClose()
|
|
{
|
|
//--- check position
|
|
if(!PositionSelect(_Symbol))
|
|
return;
|
|
//--- check position magic
|
|
if(PositionGetInteger(POSITION_MAGIC)!=MA_MAGIC)
|
|
return;
|
|
//--- check trade signal
|
|
ENUM_ORDER_TYPE signal;
|
|
if(!GetTradeSignal(signal))
|
|
return;
|
|
//---
|
|
long type = PositionGetInteger(POSITION_TYPE);
|
|
bool close_signal = false;
|
|
//---
|
|
if(type == POSITION_TYPE_BUY && signal == ORDER_TYPE_SELL)
|
|
close_signal = true;
|
|
if(type == POSITION_TYPE_SELL && signal == ORDER_TYPE_BUY)
|
|
close_signal = true;
|
|
//--- check L1 filter
|
|
if(L1FilterClose)
|
|
{
|
|
double delta = CheckTrendL1();
|
|
if(type == POSITION_TYPE_BUY && delta > 0)
|
|
{
|
|
close_signal = false;
|
|
PrintFormat("Close BUY signal cancelled by L1 trend delta=%.5f", delta);
|
|
}
|
|
if(type == POSITION_TYPE_SELL && delta < 0)
|
|
{
|
|
close_signal = false;
|
|
PrintFormat("Close SELL signal cancelled by L1 trend delta=%.5f", delta);
|
|
}
|
|
}
|
|
//---
|
|
if(close_signal && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>=L1TotalBars)
|
|
ExtTrade.PositionClose(_Symbol,3);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| SelectPosition |
|
|
//+------------------------------------------------------------------+
|
|
bool SelectPosition()
|
|
{
|
|
bool res = false;
|
|
if(ExtHedging)
|
|
{
|
|
uint total = PositionsTotal();
|
|
for(uint i=0; i<total; i++)
|
|
{
|
|
string sym = PositionGetSymbol(i);
|
|
if(sym == _Symbol && PositionGetInteger(POSITION_MAGIC)==MA_MAGIC)
|
|
{
|
|
res = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(PositionSelect(_Symbol))
|
|
res = (PositionGetInteger(POSITION_MAGIC)==MA_MAGIC);
|
|
}
|
|
return res;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
//--- check parameters
|
|
if(MovingPeriod<=0)
|
|
{
|
|
Print("Error: MovingPeriod parameter must be positive");
|
|
return(INIT_PARAMETERS_INCORRECT);
|
|
}
|
|
ExtHedging = (AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
|
|
ExtTrade.SetExpertMagicNumber(MA_MAGIC);
|
|
ExtTrade.SetMarginMode();
|
|
ExtTrade.SetTypeFillingBySymbol(_Symbol);
|
|
//--- prepare indicator
|
|
ExtHandle = iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE);
|
|
if(ExtHandle==INVALID_HANDLE)
|
|
{
|
|
Print("Failed to create MA handle");
|
|
return INIT_FAILED;
|
|
}
|
|
//--- prepare filename
|
|
ExtStrategyFileName=PrepareStrategyFileName(ExtStrategyName);
|
|
//--- delete old file if exists
|
|
if(FileIsExist(ExtStrategyFileName))
|
|
FileDelete(ExtStrategyFileName);
|
|
//---
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| PrepareStrategyFileName |
|
|
//+------------------------------------------------------------------+
|
|
string PrepareStrategyFileName(string strategy_name)
|
|
{
|
|
int v=0;
|
|
if(L1FilterOpen)
|
|
v=v | 1;
|
|
//---
|
|
if(L1FilterClose)
|
|
v=v | 2;
|
|
//---
|
|
string filename=IntegerToString(v)+"_"+strategy_name+"_"+_Symbol+".txt";
|
|
return filename;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Save account statistics to file |
|
|
//+------------------------------------------------------------------+
|
|
void SaveAccountStatistics()
|
|
{
|
|
//--- check file name
|
|
if(ExtStrategyFileName=="")
|
|
return;
|
|
//---
|
|
int file=FileOpen(ExtStrategyFileName,FILE_WRITE|FILE_READ|FILE_TXT|FILE_SHARE_WRITE|FILE_ANSI);
|
|
if(file==INVALID_HANDLE)
|
|
{
|
|
Print("File open error: ",GetLastError());
|
|
return;
|
|
}
|
|
//--- append
|
|
FileSeek(file,0,SEEK_END);
|
|
//--- account data
|
|
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
|
double margin = AccountInfoDouble(ACCOUNT_MARGIN);
|
|
double free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
|
|
double margin_lvl = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
|
|
//--- volume
|
|
double volume=0.0;
|
|
if(PositionSelect(_Symbol))
|
|
volume=PositionGetDouble(POSITION_VOLUME);
|
|
//--- time
|
|
datetime t[1];
|
|
if(CopyTime(_Symbol,_Period,0,1,t)<=0)
|
|
{
|
|
FileClose(file);
|
|
return;
|
|
}
|
|
double current_close[1];
|
|
if(CopyClose(_Symbol,_Period,0,1,current_close)<=0)
|
|
{
|
|
FileClose(file);
|
|
return;
|
|
}
|
|
string line=StringFormat("%s;%.2f;%.2f;%.2f;%.2f;%.2f;%.2f;%f",TimeToString(t[0],TIME_DATE|TIME_SECONDS),
|
|
balance,equity,margin,free_margin,margin_lvl,volume,current_close[0]);
|
|
//---
|
|
FileWrite(file,line);
|
|
//---
|
|
FileClose(file);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert OnTick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
//--- trade only at new bar
|
|
if(!IsNewBar())
|
|
return;
|
|
//--- check trade conditions
|
|
if(SelectPosition())
|
|
CheckForClose();
|
|
else
|
|
CheckForOpen();
|
|
//--- save account statistics
|
|
if(SaveStatistics)
|
|
SaveAccountStatistics();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
//--- save account statistics
|
|
if(SaveStatistics)
|
|
SaveAccountStatistics();
|
|
//---
|
|
IndicatorRelease(ExtHandle);
|
|
}
|
|
//+------------------------------------------------------------------+
|