308 lines
12 KiB
MQL5
308 lines
12 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| TDI_Momentum_Pulse.mq5 |
|
|
//| Generated by Ruben Works |
|
|
//| Based on Master Trading Plan v1 |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Ruben Works"
|
|
#property link ""
|
|
#property version "1.00"
|
|
|
|
// Include Standard Libraries
|
|
#include <Trade\Trade.mqh>
|
|
#include <Trade\PositionInfo.mqh>
|
|
#include <Trade\OrderInfo.mqh>
|
|
|
|
//--- Input Parameters
|
|
input group "Strategy Settings"
|
|
input double InpLots = 0.02; // Lot Size (Reduced for 750 pip risk)
|
|
input int InpStopLoss = 7500; // Stop Loss (Points, 750 Pips - Safe Mode)
|
|
input int InpTakeProfit = 3000; // Take Profit (Points, 300 Pips)
|
|
input int InpMagic = 123456; // Magic Number
|
|
input int InpTimeExitHours = 16; // Max Holding Time (Hours)
|
|
|
|
input group "Trailing Stop Settings"
|
|
input bool InpUseTrailing = true; // Enable Trailing Stop
|
|
input int InpTrailingStart = 500; // Start Trailing After Profit (Points)
|
|
input int InpTrailingDist = 500; // Trailing Distance (Points)
|
|
input int InpTrailingStep = 50; // Trailing Step (Points)
|
|
|
|
input group "TDI Settings"
|
|
input int InpRSI_Period = 13; // RSI Period
|
|
input int InpBand_Period = 34; // Volatility Band Period
|
|
input double InpBand_Std = 1.6185; // Volatility Band Std Dev
|
|
input int InpGreen_Period = 2; // RSI Price Line (Green)
|
|
input int InpRed_Period = 7; // Trade Signal Line (Red)
|
|
|
|
input group "Filter Settings"
|
|
input int InpEMA_Period = 10; // Momentum Filter (EMA)
|
|
input int InpTrend_Period = 200; // Trend Filter (SMA)
|
|
|
|
//--- Global Objects
|
|
CTrade m_trade;
|
|
CPositionInfo m_position;
|
|
COrderInfo m_order;
|
|
|
|
//--- Indicator Handles
|
|
int hRSI; // Base RSI
|
|
int hTDI_Green; // SMA(2) of RSI
|
|
int hTDI_Red; // SMA(7) of RSI
|
|
int hStoch; // Stochastic
|
|
int hEMA; // Price EMA 10
|
|
int hTrend; // Price SMA 200
|
|
|
|
//--- Buffers (Resizing managed automatically by CopyBuffer)
|
|
double bufRSI[];
|
|
double bufGreen[];
|
|
double bufRed[];
|
|
double bufStochK[];
|
|
double bufStochD[];
|
|
double bufEMA[];
|
|
double bufTrend[];
|
|
|
|
//--- Time Management
|
|
datetime lastBarTime = 0;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
// 1. Initialize Trade Object
|
|
m_trade.SetExpertMagicNumber(InpMagic);
|
|
m_trade.SetMarginMode();
|
|
m_trade.SetTypeFillingBySymbol(Symbol());
|
|
|
|
// 2. Indicator Handles
|
|
|
|
// --- TDI Construction ---
|
|
// Base RSI
|
|
hRSI = iRSI(Symbol(), Period(), InpRSI_Period, PRICE_CLOSE);
|
|
if(hRSI == INVALID_HANDLE) { Print("Failed to create RSI handle"); return INIT_FAILED; }
|
|
|
|
// Green Line: MA(2) applied to RSI handle
|
|
hTDI_Green = iMA(Symbol(), Period(), InpGreen_Period, 0, MODE_SMA, hRSI);
|
|
if(hTDI_Green == INVALID_HANDLE) { Print("Failed to create TDI Green handle"); return INIT_FAILED; }
|
|
|
|
// Red Line: MA(7) applied to RSI handle
|
|
hTDI_Red = iMA(Symbol(), Period(), InpRed_Period, 0, MODE_SMA, hRSI);
|
|
if(hTDI_Red == INVALID_HANDLE) { Print("Failed to create TDI Red handle"); return INIT_FAILED; }
|
|
|
|
// Note: We don't strictly *need* the bands for the specific Buy Signal logic (Green Cross Red),
|
|
// but if you wanted Squeeze logic, we'd add Bands here. For Master Plan v1, Cross is sufficient.
|
|
|
|
// --- Stochastic ---
|
|
hStoch = iStochastic(Symbol(), Period(), 5, 3, 3, MODE_SMA, STO_LOWHIGH);
|
|
if(hStoch == INVALID_HANDLE) { Print("Failed to create Stoch handle"); return INIT_FAILED; }
|
|
|
|
// --- Filters ---
|
|
hEMA = iMA(Symbol(), Period(), InpEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
|
|
if(hEMA == INVALID_HANDLE) { Print("Failed to create EMA handle"); return INIT_FAILED; }
|
|
|
|
hTrend = iMA(Symbol(), Period(), InpTrend_Period, 0, MODE_SMA, PRICE_CLOSE);
|
|
if(hTrend == INVALID_HANDLE) { Print("Failed to create Trend handle"); return INIT_FAILED; }
|
|
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
IndicatorRelease(hRSI);
|
|
IndicatorRelease(hTDI_Green);
|
|
IndicatorRelease(hTDI_Red);
|
|
IndicatorRelease(hStoch);
|
|
IndicatorRelease(hEMA);
|
|
IndicatorRelease(hTrend);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
// 1. Check For Time Exits (Every Tick)
|
|
CheckTimeExits();
|
|
|
|
// 1b. Check Trailing Stop (Every Tick)
|
|
if(InpUseTrailing) CheckTrailingStop();
|
|
|
|
// 2. Check for New Bar (Entry Logic)
|
|
if(!IsNewBar()) return;
|
|
|
|
// 3. Data Buffers Copy (We need Index 1 and 2 for crossovers)
|
|
// We copy 3 values: [0] current, [1] closed, [2] prev-closed
|
|
if(CopyBuffer(hTDI_Green, 0, 0, 3, bufGreen) < 3) return;
|
|
if(CopyBuffer(hTDI_Red, 0, 0, 3, bufRed) < 3) return;
|
|
if(CopyBuffer(hStoch, 0, 0, 3, bufStochK) < 3) return;
|
|
if(CopyBuffer(hStoch, 1, 0, 3, bufStochD) < 3) return; // Buffer 1 is Signal line in Stoch
|
|
if(CopyBuffer(hEMA, 0, 0, 3, bufEMA) < 3) return;
|
|
if(CopyBuffer(hTrend, 0, 0, 3, bufTrend) < 3) return;
|
|
|
|
// MQL5 Arrays are index 0 = oldest by default for CopyBuffer?
|
|
// Actually defaults are 0 = start index.
|
|
// Using ArraySetAsSeries(true) makes 0 = newest (Current Bar).
|
|
ArraySetAsSeries(bufGreen, true);
|
|
ArraySetAsSeries(bufRed, true);
|
|
ArraySetAsSeries(bufStochK, true);
|
|
ArraySetAsSeries(bufStochD, true);
|
|
ArraySetAsSeries(bufEMA, true);
|
|
ArraySetAsSeries(bufTrend, true);
|
|
|
|
// Get Price Data for Candle 1
|
|
double close1 = iClose(Symbol(), Period(), 1);
|
|
double high1 = iHigh(Symbol(), Period(), 1);
|
|
double low1 = iLow(Symbol(), Period(), 1);
|
|
|
|
// --- FRIDAY FILTER ---
|
|
MqlDateTime dt;
|
|
TimeCurrent(dt);
|
|
if(dt.day_of_week == 5) return; // Do not enter on Friday
|
|
|
|
// --- SIGNAL LOGIC ---
|
|
|
|
// 1. TDI Cross UP (Green crosses Red)
|
|
// Current Closed (1) > Red AND Prev (2) <= Red
|
|
bool tdiCross = (bufGreen[1] > bufRed[1]) && (bufGreen[2] <= bufRed[2]);
|
|
|
|
// 2. Stoch Cross UP
|
|
bool stochCross = (bufStochK[1] > bufStochD[1]) && (bufStochK[2] <= bufStochD[2]);
|
|
|
|
// 3. EMA Filter (Close > EMA)
|
|
bool momentumOk = (close1 > bufEMA[1]);
|
|
|
|
// 4. Trend Filter (Close > SMA 200)
|
|
bool trendOk = (close1 > bufTrend[1]);
|
|
|
|
// --- EXECUTION ---
|
|
if(tdiCross && stochCross && momentumOk && trendOk)
|
|
{
|
|
// --- SPLIT ENTRY LOGIC ---
|
|
|
|
double sl_price = close1 - (InpStopLoss * _Point);
|
|
double tp_price = close1 + (InpTakeProfit * _Point);
|
|
|
|
// Calculate Normalized Prices
|
|
double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
|
|
sl_price = NormalizeDouble(sl_price, _Digits);
|
|
tp_price = NormalizeDouble(tp_price, _Digits);
|
|
|
|
// Trade A: Market Buy (50% Lots)
|
|
// Note: We assume InpLots is the Total Risk. So we divide by 2?
|
|
// Or InpLots is per trade. Let's assume InpLots is TOTAL SIZE.
|
|
double splitLot = NormalizeDouble(InpLots / 2.0, 2);
|
|
if(splitLot < SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN))
|
|
splitLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
|
|
|
|
m_trade.Buy(splitLot, Symbol(), ask, sl_price, tp_price, "TDI Market");
|
|
|
|
// Trade B: Limit Buy (50% Lots)
|
|
// FIX: Add Spread Buffer to ensure fill.
|
|
// Ask must hit Limit. If we place at Bid MidPrice, Ask is higher by Spread.
|
|
// So we lift the limit by the Spread.
|
|
|
|
// Use Ask-Bid to get the exact spread at this moment.
|
|
double market_spread = ask - SymbolInfoDouble(Symbol(), SYMBOL_BID);
|
|
|
|
double midPrice = (high1 + low1) / 2.0;
|
|
// Add Spread to Limit to ensure Ask touches it when Bid touches Mid
|
|
midPrice = midPrice + market_spread;
|
|
midPrice = NormalizeDouble(midPrice, _Digits);
|
|
|
|
// Limit SL/TP
|
|
double limit_sl = midPrice - (InpStopLoss * _Point);
|
|
double limit_tp = midPrice + (InpTakeProfit * _Point);
|
|
limit_sl = NormalizeDouble(limit_sl, _Digits);
|
|
limit_tp = NormalizeDouble(limit_tp, _Digits);
|
|
|
|
// Expiration: 4 Hours (16200 sec approx, or TimeCurrent + 4*3600)
|
|
datetime expiry = TimeCurrent() + (4 * 3600);
|
|
|
|
m_trade.BuyLimit(splitLot, midPrice, Symbol(), limit_sl, limit_tp, ORDER_TIME_SPECIFIED, expiry, "TDI Pullback");
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if New Bar |
|
|
//+------------------------------------------------------------------+
|
|
bool IsNewBar()
|
|
{
|
|
datetime currTime = iTime(Symbol(), Period(), 0);
|
|
if(currTime != lastBarTime)
|
|
{
|
|
lastBarTime = currTime;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check Time Exits |
|
|
//+------------------------------------------------------------------+
|
|
void CheckTimeExits()
|
|
{
|
|
// Loop all open positions
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
if(m_position.SelectByIndex(i))
|
|
{
|
|
if(m_position.Symbol() == Symbol() && m_position.Magic() == InpMagic)
|
|
{
|
|
// Calculate duration in seconds
|
|
long duration = TimeCurrent() - m_position.Time();
|
|
// Convert hours to seconds
|
|
if(duration > (InpTimeExitHours * 3600))
|
|
{
|
|
m_trade.PositionClose(m_position.Ticket());
|
|
Print("Time Exit Triggered for Ticket: ", m_position.Ticket());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check Trailing Stop Logic |
|
|
//+------------------------------------------------------------------+
|
|
void CheckTrailingStop()
|
|
{
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
if(m_position.SelectByIndex(i))
|
|
{
|
|
if(m_position.Symbol() == Symbol() && m_position.Magic() == InpMagic)
|
|
{
|
|
if(m_position.PositionType() == POSITION_TYPE_BUY)
|
|
{
|
|
double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID);
|
|
double openPrice = m_position.PriceOpen();
|
|
double currentProfitPoints = (currentPrice - openPrice) / SymbolInfoDouble(Symbol(), SYMBOL_POINT);
|
|
|
|
// Start Trailing if Profit > TrailingStart
|
|
if(currentProfitPoints > InpTrailingStart)
|
|
{
|
|
double newSL = currentPrice - (InpTrailingDist * SymbolInfoDouble(Symbol(), SYMBOL_POINT));
|
|
double currentSL = m_position.StopLoss();
|
|
|
|
// Check if New SL is an improvement (Higher than current)
|
|
// And check if change is significant enough (TrailingStep)
|
|
if(newSL > currentSL + (InpTrailingStep * SymbolInfoDouble(Symbol(), SYMBOL_POINT)))
|
|
{
|
|
// Normalize
|
|
newSL = NormalizeDouble(newSL, _Digits);
|
|
double tp = m_position.TakeProfit();
|
|
|
|
// Modify
|
|
if(m_trade.PositionModify(m_position.Ticket(), newSL, tp))
|
|
{
|
|
// Success
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|