Skip to content

Commit

Permalink
1.12
Browse files Browse the repository at this point in the history
1. Added input parameters to control the minimum profit to trigger breakeven, breakeven extra profit, and minimum profit to start trailing stop.
2. Added an option to combine trailing stop and breakeven.
3. Changed the news date and time parameter to default to the current local time.
4. Fixed some issues with trailing stop and breakeven application.
5. Fixed the bug with the MT5 version not filtering positions by magic number.
  • Loading branch information
EarnForex authored Jul 24, 2024
1 parent 2379e5f commit 1a3534c
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 130 deletions.
74 changes: 50 additions & 24 deletions NewsTrader.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// -------------------------------------------------------------------------------
// Opens a buy/sell trade (random, chosen direction, or both directions) seconds before news release.
// Sets SL and TP. Keeps updating them until the very release.
// Can set SL to breakeven when unrealized profit = SL.
// Alternatively, adds ATR based trailing stop.
// Can set use trailing stop and breakeven.
// ATR-based stop-loss option is also available.
// Closes trade after given time period passes.
// Version 1.11.
// Version 1.12.
// Copyright 2024, EarnForex.com
// https://www.earnforex.com/metatrader-expert-advisors/News-Trader/
// -------------------------------------------------------------------------------
Expand All @@ -16,6 +16,14 @@
using cAlgo.API.Internals;
using cAlgo.Indicators;

public enum Trailing_Enum
{
None,
Breakeven,
TrailingStop, // Normal trailing stop
TS_plus_BE // Normal trailing stop + Breakeven
}

namespace cAlgo
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
Expand Down Expand Up @@ -51,23 +59,28 @@ public class NewsTrader : Robot
[Parameter("Randomize Buy/Sell", DefaultValue = false)]
public bool Rnd { get; set; }

// Trailing Stop will supersede Breakeven Stop if true.
[Parameter("Trailing Stop", DefaultValue = false)]
public bool Trailing { get; set; }
[Parameter("Trailing Stop", DefaultValue = Trailing_Enum.None)]
public Trailing_Enum Trailing { get; set; }

[Parameter("Breakeven Stop", DefaultValue = false)]
public bool Breakeven { get; set; }
[Parameter("Profit to trigger breakeven, pips", DefaultValue = 0, MinValue = 0)]
public int BEOnProfit { get; set; }

[Parameter("Breakeven extra profit, pips", DefaultValue = 0, MinValue = 0)]
public int BEExtraProfit { get; set; }

[Parameter("Profit to start trailing stop, pips", DefaultValue = 0, MinValue = 0)]
public int TSOnProfit { get; set; }

[Parameter("Preadjust SL/TP", DefaultValue = false)]
public bool PreAdjustSLTP { get; set; }

[Parameter("Seconds Before News", DefaultValue = 10, MinValue = 1)]
public int SecondsBefore { get; set; }

[Parameter("Close After Seconds", DefaultValue = 3600, MinValue = 1)]
[Parameter("Close After Seconds", DefaultValue = 3600, MinValue = 0)]
public int CloseAfterSeconds { get; set; }

[Parameter("Use ATR Position Sizing", DefaultValue = false)]
[Parameter("Use ATR Stop-Loss", DefaultValue = false)]
public bool UseATR { get; set; }

[Parameter("ATR Period", DefaultValue = 14, MinValue = 1)]
Expand Down Expand Up @@ -125,6 +138,9 @@ public class NewsTrader : Robot

private bool HaveLongPosition = false;
private bool HaveShortPosition = false;

private bool Breakeven_Needed = false;
private bool Trailing_Needed = false;

protected string MyLabel
{
Expand Down Expand Up @@ -159,6 +175,10 @@ protected override void OnStart()
// 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.");
if ((Trailing == Trailing_Enum.Breakeven) || (Trailing == Trailing_Enum.TS_plus_BE)) Breakeven_Needed = true;
if ((Trailing == Trailing_Enum.TrailingStop) || (Trailing == Trailing_Enum.TS_plus_BE)) Trailing_Needed = true;
}

//+------------------------------------------------------------------+
Expand Down Expand Up @@ -219,19 +239,19 @@ protected override void OnTick()
{
Random r = new Random();
if (r.Next() % 2 == 1)
fBuy();
OpenBuy();
else
fSell();
OpenSell();
}
else if ((Buy) && (Sell))
{
fSell();
fBuy();
OpenSell();
OpenBuy();
}
else if (Buy)
fBuy();
OpenBuy();
else if (Sell)
fSell();
OpenSell();
}
}
}
Expand All @@ -258,15 +278,15 @@ private void GetPositionStates()
//+------------------------------------------------------------------+
//| Generic buy. |
//+------------------------------------------------------------------+
private void fBuy()
private void OpenBuy()
{
ExecuteMarketRangeOrder(TradeType.Buy, Symbol.Name, LotsOptimized(), Slippage, Symbol.Ask, MyLabel, SL, TP, Commentary);
}

//+------------------------------------------------------------------+
//| Generic sell. |
//+------------------------------------------------------------------+
private void fSell()
private void OpenSell()
{
ExecuteMarketRangeOrder(TradeType.Sell, Symbol.Name, LotsOptimized(), Slippage, Symbol.Bid, MyLabel, SL, TP, Commentary);
}
Expand Down Expand Up @@ -303,23 +323,29 @@ private void ControlPosition()
// Check for breakeven or trade time out.
else
{
if ((!Trailing) && (Breakeven) && ((((position.TradeType == TradeType.Buy) && (Symbol.Ask - position.EntryPrice >= SL * Symbol.PipSize)) || ((position.TradeType == TradeType.Sell) && (position.EntryPrice - Symbol.Bid >= SL * Symbol.PipSize)))))
if ((Breakeven_Needed) && ((((position.TradeType == TradeType.Buy) && (Symbol.Bid - position.EntryPrice >= BEOnProfit * Symbol.PipSize)) || ((position.TradeType == TradeType.Sell) && (position.EntryPrice - Symbol.Ask >= BEOnProfit * Symbol.PipSize)))))
{
double new_sl = Math.Round(position.EntryPrice, Symbol.Digits);
if (new_sl != position.StopLoss)
if (BEExtraProfit > 0) // Breakeven extra profit?
{
if (position.TradeType == TradeType.Buy) new_sl += BEExtraProfit * Symbol.PipSize; // For buys.
else new_sl -= BEExtraProfit * Symbol.PipSize; // For sells.
new_sl = Math.Round(new_sl, Symbol.Digits);
}
if (((position.TradeType == TradeType.Buy) && (new_sl > position.StopLoss)) || ((position.TradeType == TradeType.Sell) && ((new_sl < position.StopLoss) || (position.StopLoss == null)))) // Avoid moving SL to BE if this SL is already there or in a better position.
{
Print("Moving SL to breakeven: ", new_sl, ".");
ModifyPosition(position, new_sl, position.TakeProfit);
}
}
else if ((Trailing) && ((((position.TradeType == TradeType.Buy) && (Symbol.Ask - position.StopLoss >= SL * Symbol.PipSize)) || ((position.TradeType == TradeType.Sell) && (position.StopLoss - Symbol.Bid >= SL * Symbol.PipSize)))))
if ((Trailing_Needed) && ((TSOnProfit == 0) || ((position.TradeType == TradeType.Buy) && (Symbol.Bid - position.EntryPrice >= TSOnProfit * Symbol.PipSize)) || ((position.TradeType == TradeType.Sell) && (position.EntryPrice - Symbol.Ask >= TSOnProfit * Symbol.PipSize))))
{
double new_sl = 0;
if (position.TradeType == TradeType.Buy)
new_sl = Math.Round(Symbol.Ask - SL * Symbol.PipSize, Symbol.Digits);
new_sl = Math.Round(Symbol.Bid - SL * Symbol.PipSize, Symbol.Digits);
else if (position.TradeType == TradeType.Sell)
new_sl = Math.Round(Symbol.Bid + SL * Symbol.PipSize, Symbol.Digits);
if (((position.TradeType == TradeType.Buy) && (new_sl > position.StopLoss)) || ((position.TradeType == TradeType.Sell) && (new_sl < position.StopLoss)))
new_sl = Math.Round(Symbol.Ask + SL * Symbol.PipSize, Symbol.Digits);
if (((position.TradeType == TradeType.Buy) && (new_sl > position.StopLoss)) || ((position.TradeType == TradeType.Sell) && ((new_sl < position.StopLoss) || (position.StopLoss == null))))
{
Print("Moving trailing SL to ", new_sl, ".");
ModifyPosition(position, new_sl, position.TakeProfit);
Expand Down
92 changes: 54 additions & 38 deletions NewsTrader.mq4
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
//+------------------------------------------------------------------+
#property copyright "Copyright © 2024, EarnForex"
#property link "https://www.earnforex.com/metatrader-expert-advisors/News-Trader/"
#property version "1.11"
#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 SL to breakeven when unrealized profit = SL."
#property description "Alternatively, adds ATR based trailing stop."
#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
Expand All @@ -24,47 +24,51 @@ enum dir_enum

enum trailing_enum
{
None,
Breakeven,
Full,
None
Normal, // Normal trailing stop
NormalPlusBE // Normal trailing stop + Breakeven
};

input group "Trading"
input datetime NewsTime = __DATE__; // News date/time.
input int StopLoss = 15; // Stop-loss in broker's pips.
input int TakeProfit = 75; // Take-profit in broker's pips.
input dir_enum Direction = Both; // Direction of the trade to open.
input trailing_enum TrailingStop = None; // Trailing stop type.
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 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 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 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 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 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.
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;
Expand Down Expand Up @@ -107,6 +111,8 @@ void OnInit()
// 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.");
}

//+------------------------------------------------------------------+
Expand Down Expand Up @@ -317,7 +323,7 @@ void ControlPosition()
if ((((new_sl != NormalizeDouble(OrderStopLoss(), Digits)) || (new_tp != NormalizeDouble(OrderTakeProfit(), Digits))) && (PreAdjustSLTP)) ||
(((OrderStopLoss() == 0) || (OrderTakeProfit() == 0)) && (ECN_Mode)))
{
Print("Adjusting SL: ", new_sl, " and TP: ", new_tp, ".");
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);
Expand All @@ -333,38 +339,48 @@ void ControlPosition()
// Adjust only if in ECN mode and need to assign SL/TP first time.
if (((OrderStopLoss() == 0) || (OrderTakeProfit() == 0)) && (ECN_Mode))
{
Print("Adjusting SL: ", new_sl, " and TP: ", new_tp, ".");
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());
}
}
if ((TrailingStop == Breakeven) && ((((OrderType() == OP_BUY) && (Ask - OrderOpenPrice() >= SL * Point)) || ((OrderType() == OP_SELL) && (OrderOpenPrice() - Bid >= SL * Point)))))
// 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 (new_sl != NormalizeDouble(OrderStopLoss(), Digits))
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());
}
}
}
else if ((TrailingStop == Full) && ((((OrderType() == OP_BUY) && (Ask - OrderStopLoss() >= SL * Point)) || ((OrderType() == OP_SELL) && (OrderStopLoss() - Bid >= SL * Point)))))
// 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(Ask - SL * Point, Digits);
else if (OrderType() == OP_SELL) new_sl = NormalizeDouble(Bid + SL * Point, Digits);
if (((OrderType() == OP_BUY) && (new_sl > NormalizeDouble(OrderStopLoss(), Digits))) || ((OrderType() == OP_SELL) && (new_sl < NormalizeDouble(OrderStopLoss(), Digits))))
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());
}
}
}
Expand Down
Loading

0 comments on commit 1a3534c

Please sign in to comment.