Trading_Strategy/TradingView_1zu1_MT5_Execution_EA.txt

349 lines
9.3 KiB
Text
Raw Permalink Normal View History

2026-05-06 13:36:00 +00:00
//+------------------------------------------------------------------+
//| TradingView 1:1 Execution EA for MetaTrader 5 |
//| Executes the exact signals produced by the TradingView strategy |
//| File format: ACTION;SYMBOL;ENTRY;SL;TP1;TP2;TPFINAL;ID |
//| Example: BUY;EURUSD;1.08500;1.08300;1.08700;1.08900;1.10100;TV01 |
//+------------------------------------------------------------------+
#property strict
#property version "1.00"
#include <Trade/Trade.mqh>
CTrade trade;
//============================== INPUTS ==============================
input string InpSignalFileName = "tv_signal.txt"; // Common/Files/tv_signal.txt
input bool InpUseCommonFolder = true;
input int InpMagic = 26050601;
input double InpLots = 0.10;
input bool InpUseRiskPercent = false;
input double InpRiskPercent = 1.0;
input bool InpUsePartials = true;
input double InpTP1Percent = 30.0;
input double InpTP2Percent = 30.0;
input double InpFinalPercent = 40.0;
input int InpSlippagePoints = 20;
input bool InpOnePositionAtATime = true;
input bool InpCloseOpposite = true;
input int InpPollSeconds = 1;
//============================== GLOBALS ==============================
string g_last_id = "";
datetime g_last_poll = 0;
//============================== HELPERS ==============================
string Trim(string s)
{
StringTrimLeft(s);
StringTrimRight(s);
return s;
}
int Split(string text, string sep, string &arr[])
{
return StringSplit(text, StringGetCharacter(sep, 0), arr);
}
double NormalizeLots(double lots)
{
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lots = MathMax(minLot, MathMin(maxLot, lots));
lots = MathFloor(lots / step) * step;
int digits = 2;
if(step == 0.001) digits = 3;
if(step == 0.01) digits = 2;
if(step == 0.1) digits = 1;
return NormalizeDouble(lots, digits);
}
double LotsByRisk(double entry, double sl)
{
if(!InpUseRiskPercent)
return NormalizeLots(InpLots);
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskMoney = balance * InpRiskPercent / 100.0;
double dist = MathAbs(entry - sl);
if(dist <= 0.0)
return NormalizeLots(InpLots);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
if(tickValue <= 0.0 || tickSize <= 0.0)
return NormalizeLots(InpLots);
double lossPerLot = dist / tickSize * tickValue;
if(lossPerLot <= 0.0)
return NormalizeLots(InpLots);
return NormalizeLots(riskMoney / lossPerLot);
}
bool IsOurPosition(ulong ticket)
{
if(!PositionSelectByTicket(ticket))
return false;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
return false;
if((int)PositionGetInteger(POSITION_MAGIC) != InpMagic)
return false;
return true;
}
bool HasOurPosition()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(IsOurPosition(ticket))
return true;
}
return false;
}
void CloseOppositePositions(bool buySignal)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(!IsOurPosition(ticket)) continue;
long type = PositionGetInteger(POSITION_TYPE);
if(buySignal && type == POSITION_TYPE_SELL)
trade.PositionClose(ticket);
if(!buySignal && type == POSITION_TYPE_BUY)
trade.PositionClose(ticket);
}
}
bool ReadSignalLine(string &line)
{
int flags = FILE_READ | FILE_TXT | FILE_ANSI;
if(InpUseCommonFolder)
flags |= FILE_COMMON;
int handle = FileOpen(InpSignalFileName, flags);
if(handle == INVALID_HANDLE)
return false;
line = "";
while(!FileIsEnding(handle))
{
string tmp = FileReadString(handle);
if(StringLen(Trim(tmp)) > 0)
line = Trim(tmp);
}
FileClose(handle);
return StringLen(line) > 0;
}
bool ExecuteSingleOrder(bool isBuy, double lots, double sl, double tp, string comment)
{
trade.SetExpertMagicNumber(InpMagic);
trade.SetDeviationInPoints(InpSlippagePoints);
bool ok = false;
if(isBuy)
ok = trade.Buy(lots, _Symbol, 0.0, sl, tp, comment);
else
ok = trade.Sell(lots, _Symbol, 0.0, sl, tp, comment);
if(!ok)
{
Print("Order failed: ", comment,
" retcode=", trade.ResultRetcode(),
" desc=", trade.ResultRetcodeDescription());
}
return ok;
}
bool ExecuteSignal(string line)
{
string p[];
int n = Split(line, ";", p);
if(n < 8)
{
Print("Invalid signal format. Expected 8 fields: ", line);
return false;
}
string action = StringUpper(Trim(p[0]));
string symbol = Trim(p[1]);
if(symbol != _Symbol)
{
Print("Signal ignored. Signal symbol=", symbol, " chart symbol=", _Symbol);
return false;
}
double entry = StringToDouble(Trim(p[2]));
double sl = NormalizeDouble(StringToDouble(Trim(p[3])), _Digits);
double tp1 = NormalizeDouble(StringToDouble(Trim(p[4])), _Digits);
double tp2 = NormalizeDouble(StringToDouble(Trim(p[5])), _Digits);
double tpFinal = NormalizeDouble(StringToDouble(Trim(p[6])), _Digits);
string id = Trim(p[7]);
if(id == "" || id == g_last_id)
return false;
bool isBuy = action == "BUY" || action == "LONG" || action == "BULL";
bool isSell = action == "SELL" || action == "SHORT" || action == "BEAR";
if(!isBuy && !isSell)
{
Print("Invalid action: ", action);
return false;
}
if(InpCloseOpposite)
CloseOppositePositions(isBuy);
if(InpOnePositionAtATime && HasOurPosition())
{
Print("Signal ignored: existing position and InpOnePositionAtATime=true");
return false;
}
double lotsTotal = LotsByRisk(entry, sl);
if(lotsTotal <= 0.0)
{
Print("Invalid lots");
return false;
}
bool ok = true;
if(!InpUsePartials)
{
ok = ExecuteSingleOrder(isBuy, lotsTotal, sl, tpFinal, "TV 1:1 " + id);
}
else
{
double lot1 = NormalizeLots(lotsTotal * InpTP1Percent / 100.0);
double lot2 = NormalizeLots(lotsTotal * InpTP2Percent / 100.0);
double lot3 = NormalizeLots(lotsTotal * InpFinalPercent / 100.0);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
if(lot1 < minLot) lot1 = 0.0;
if(lot2 < minLot) lot2 = 0.0;
if(lot3 < minLot) lot3 = 0.0;
if(lot1 + lot2 + lot3 <= 0.0)
lot3 = lotsTotal;
if(lot1 > 0.0)
ok = ExecuteSingleOrder(isBuy, lot1, sl, tp1, "TV 1:1 TP1 " + id) && ok;
if(lot2 > 0.0)
ok = ExecuteSingleOrder(isBuy, lot2, sl, tp2, "TV 1:1 TP2 " + id) && ok;
if(lot3 > 0.0)
ok = ExecuteSingleOrder(isBuy, lot3, sl, tpFinal, "TV 1:1 FINAL " + id) && ok;
}
if(ok)
{
g_last_id = id;
Print("Executed TradingView signal: ", line);
}
return ok;
}
//============================== MT5 EVENTS ============================
int OnInit()
{
trade.SetExpertMagicNumber(InpMagic);
EventSetTimer(InpPollSeconds);
Print("TradingView 1:1 Execution EA started.");
Print("Signal file: Common/Files/", InpSignalFileName);
Print("Format: ACTION;SYMBOL;ENTRY;SL;TP1;TP2;TPFINAL;ID");
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
EventKillTimer();
Print("TradingView 1:1 Execution EA stopped.");
}
void OnTick()
{
// Trading is handled in OnTimer so it can react even without a new tick.
}
void OnTimer()
{
string line;
if(ReadSignalLine(line))
ExecuteSignal(line);
}
//+------------------------------------------------------------------+
//====================================================================
// TradingView Alert-Format für deine Pine Strategy
//====================================================================
//
// Damit der MT5-EA 1:1 dieselben Signale handelt, muss TradingView
// bei jedem echten Strategy-Signal eine Zeile in folgendem Format
// an deinen Bridge-Dienst senden:
//
// BUY;{{ticker}};ENTRY;SL;TP1;TP2;TPFINAL;UNIQUE_ID
// SELL;{{ticker}};ENTRY;SL;TP1;TP2;TPFINAL;UNIQUE_ID
//
// In deinem Pine Script sind die Werte bereits vorhanden:
//
// Bear confirmed:
// bear_entry, bear_sl, short_tp1, short_tp2, bear_tp
//
// Bull confirmed:
// bull_entry, bull_sl, long_tp1, long_tp2, bull_tp
//
// Final bear:
// bear_entry, bear_sl, short_tp1, short_tp2, bear_tp
//
// Final bull:
// bull_entry, bull_sl, long_tp1, long_tp2, bull_tp
//
// Beispiel Alert-Message in Pine:
//
// alert("SELL;" + syminfo.ticker + ";" +
// str.tostring(bear_entry, format.mintick) + ";" +
// str.tostring(bear_sl, format.mintick) + ";" +
// str.tostring(short_tp1, format.mintick) + ";" +
// str.tostring(short_tp2, format.mintick) + ";" +
// str.tostring(bear_tp, format.mintick) + ";" +
// "FINAL_BEAR_" + str.tostring(time),
// alert.freq_once_per_bar)
//
// Der Bridge-Dienst schreibt diese Message als letzte Zeile in:
// MetaTrader 5 / Common / Files / tv_signal.txt
//
// Dadurch bestimmt TradingView exakt Entry, SL und TP.
// MT5 führt nur aus.
//====================================================================