Inclustion of example EA

This commit is contained in:
Matt Corcoran
2025-07-12 16:47:54 +02:00
parent 1222236ded
commit 657125d61c
37 changed files with 466 additions and 3341 deletions
+264
View File
@@ -0,0 +1,264 @@
#include <Trade/Trade.mqh>
#include <MyLibs/utils/AtrHandleManager.mqh>
// ---------------------------------------------------------------------
// GLOBALS
// ---------------------------------------------------------------------
CTrade trade;
AtrHandleManager atr_manager;
// ---------------------------------------------------------------------
// CLASS: AdjustPosition
// ---------------------------------------------------------------------
// Provides methods to manage stop-loss logic for runner trades.
// Includes breakeven, trailing stop (fixed and ATR), and virtual TP SLs.
// ---------------------------------------------------------------------
class AdjustPosition {
public:
void set_breakeven_sl(string symbol, int runner_magic_no, double buffer_points = 5);
void set_breakeven_if_profit_target_hit(string symbol, int runner_magic_no, double buffer_points = 5);
void set_fixed_sl(string symbol, int runner_magic_no, double fixed_sl_price);
void set_trailing_sl(string symbol, int runner_magic_no, double sl_offset_points = 5);
void trailing_stop_atr(string symbol, int magic_number, ENUM_TIMEFRAMES tf = PERIOD_CURRENT, double activation_mult = 1.0,
double trail_mult = 1.0, int atr_period = 14, bool use_bar_close = false);
private:
void set_breakeven_sl_for_ticket(string symbol, ulong ticket, long order_type, double entry_price,
double current_sl, double current_tp, int digits, double buffer_price, bool remove_tp);
};
// ---------------------------------------------------------------------
// Sets SL to breakeven for all matching runner trades.
// ---------------------------------------------------------------------
void AdjustPosition::set_breakeven_sl(string symbol, int runner_magic_no, double buffer_points) {
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double buffer_price = buffer_points * _Point;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue;
long order_type = PositionGetInteger(POSITION_TYPE);
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
set_breakeven_sl_for_ticket(symbol, ticket, order_type, entry, sl, tp, digits, buffer_price, false);
}
}
// ---------------------------------------------------------------------
// Sets SL to breakeven if virtual TP (in comment) was hit.
// ---------------------------------------------------------------------
void AdjustPosition::set_breakeven_if_profit_target_hit(string symbol, int runner_magic_no, double buffer_points) {
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double buffer_price = buffer_points * _Point;
double price_margin = 50 * _Point;
bool has_runner = false;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
if ((int) PositionGetInteger(POSITION_MAGIC) == runner_magic_no) {
has_runner = true;
break;
}
}
if (!has_runner) return;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue;
long order_type = PositionGetInteger(POSITION_TYPE);
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
string comment = PositionGetString(POSITION_COMMENT);
double virtual_tp = 0.0;
if (StringFind(comment, "runner_tp:") == 0) {
string tp_str = StringSubstr(comment, StringLen("runner_tp:"));
virtual_tp = StringToDouble(tp_str);
}
if (virtual_tp <= 0.0) continue;
if (tp > 0.0)
PrintFormat("Warning: Runner trade on %s (ticket %d) has TP set: %.5f", symbol, ticket, tp);
if (order_type == POSITION_TYPE_BUY && bid < virtual_tp - price_margin) continue;
if (order_type == POSITION_TYPE_SELL && ask > virtual_tp + price_margin) continue;
bool tp_hit = (order_type == POSITION_TYPE_BUY && bid >= virtual_tp) ||
(order_type == POSITION_TYPE_SELL && ask <= virtual_tp);
if (!tp_hit) continue;
double expected_sl = (order_type == POSITION_TYPE_BUY)
? entry + buffer_price
: entry - buffer_price;
if (NormalizeDouble(sl, digits) == NormalizeDouble(expected_sl, digits)) continue;
set_breakeven_sl_for_ticket(symbol, ticket, order_type, entry, sl, tp, digits, buffer_price, true);
}
}
// ---------------------------------------------------------------------
// Sets SL for a single trade to breakeven, optionally removes TP.
// ---------------------------------------------------------------------
void AdjustPosition::set_breakeven_sl_for_ticket(string symbol, ulong ticket, long order_type, double entry_price,
double current_sl, double current_tp, int digits,
double buffer_price, bool remove_tp) {
double breakeven_sl = (order_type == POSITION_TYPE_BUY)
? entry_price + buffer_price
: entry_price - buffer_price;
if ((order_type == POSITION_TYPE_BUY && current_sl >= breakeven_sl) ||
(order_type == POSITION_TYPE_SELL && current_sl <= breakeven_sl)) return;
MqlTradeRequest request = {};
MqlTradeResult result;
request.action = TRADE_ACTION_SLTP;
request.symbol = symbol;
request.position = ticket;
request.sl = NormalizeDouble(breakeven_sl, digits);
request.tp = remove_tp ? 0.0 : current_tp;
request.magic = (int) PositionGetInteger(POSITION_MAGIC);
if (!OrderSend(request, result))
Print("Failed to adjust runner: ", symbol, ". Error: ", result.retcode);
else if (remove_tp)
Print("Runner upgraded to trailing: SL at breakeven, TP removed for ", symbol);
}
// ---------------------------------------------------------------------
// Sets a fixed SL price for all runner trades.
// ---------------------------------------------------------------------
void AdjustPosition::set_fixed_sl(string symbol, int runner_magic_no, double fixed_sl_price) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue;
double current_sl = PositionGetDouble(POSITION_SL);
double current_tp = PositionGetDouble(POSITION_TP);
if (current_sl == fixed_sl_price) continue;
MqlTradeRequest request = {};
MqlTradeResult result;
request.action = TRADE_ACTION_SLTP;
request.symbol = symbol;
request.position = ticket;
request.sl = fixed_sl_price;
request.tp = current_tp;
request.magic = runner_magic_no;
if (!OrderSend(request, result))
Print("Failed to set fixed SL for runner on ", symbol, ". Error: ", result.retcode);
}
}
// ---------------------------------------------------------------------
// Applies a fixed-point trailing stop to runner trades.
// ---------------------------------------------------------------------
void AdjustPosition::set_trailing_sl(string symbol, int runner_magic_no, double sl_offset_points) {
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double offset = sl_offset_points * _Point;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue;
long type = PositionGetInteger(POSITION_TYPE);
double current_sl = PositionGetDouble(POSITION_SL);
double current_tp = PositionGetDouble(POSITION_TP);
double price = (type == POSITION_TYPE_BUY)
? SymbolInfoDouble(symbol, SYMBOL_BID)
: SymbolInfoDouble(symbol, SYMBOL_ASK);
double sl = (type == POSITION_TYPE_BUY) ? price - offset : price + offset;
if ((type == POSITION_TYPE_BUY && sl <= current_sl) ||
(type == POSITION_TYPE_SELL && sl >= current_sl)) continue;
MqlTradeRequest request = {};
MqlTradeResult result;
request.action = TRADE_ACTION_SLTP;
request.symbol = symbol;
request.position = ticket;
request.sl = NormalizeDouble(sl, digits);
request.tp = current_tp;
request.magic = runner_magic_no;
if (!OrderSend(request, result))
Print("Failed to update trailing SL for runner on ", symbol, ". Error: ", result.retcode);
}
}
// ---------------------------------------------------------------------
// Applies an ATR-based trailing stop to runner trades.
//
// Parameters:
// - symbol : Trading symbol.
// - _magic_number : Magic number to identify trades.
// - tf : Timeframe used for ATR calculation.
// - activation_mult: Multiplier to determine when to activate trailing.
// - trail_mult : Multiplier to determine trailing distance.
// - atr_period : ATR period to use.
// - use_bar_close : If true, use bar close instead of live price.
//
// Logic:
// - Trailing starts only after activation distance is reached.
// - SL is only updated if it moves closer to price (i.e., improves).
// ---------------------------------------------------------------------
void AdjustPosition::trailing_stop_atr(string symbol, int _magic_number, ENUM_TIMEFRAMES tf, double activation_mult,
double trail_mult, int atr_period, bool use_bar_close) {
double atr = atr_manager.get_atr_value(symbol, tf, atr_period);
if (atr == EMPTY_VALUE) return;
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
if ((int) PositionGetInteger(POSITION_MAGIC) != _magic_number) continue;
long type = PositionGetInteger(POSITION_TYPE);
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = PositionGetDouble(POSITION_SL);
double price = use_bar_close ? iClose(symbol, tf, 1) : (type == POSITION_TYPE_BUY ? bid : ask);
double trail_distance = atr * trail_mult;
double activation_distance = atr * activation_mult;
bool should_trail = (type == POSITION_TYPE_BUY && price >= entry + activation_distance) ||
(type == POSITION_TYPE_SELL && price <= entry - activation_distance);
if (!should_trail) continue;
double new_sl = (type == POSITION_TYPE_BUY) ? price - trail_distance : price + trail_distance;
new_sl = NormalizeDouble(new_sl, digits);
if ((type == POSITION_TYPE_BUY && sl >= new_sl) ||
(type == POSITION_TYPE_SELL && sl <= new_sl)) continue;
if (!trade.PositionModify(ticket, new_sl, PositionGetDouble(POSITION_TP)))
PrintFormat("Trailing SL update failed for %s ticket=%d", symbol, ticket);
else
PrintFormat("Trailing SL updated: %s ticket=%d new SL=%.5f", symbol, ticket, new_sl);
}
}
+220
View File
@@ -0,0 +1,220 @@
#include <MyLibs/Utils/MarketDataUtils.mqh>
#include <MyLibs/Utils/TimeZones.mqh>
#include <MyLibs/Utils/AtrHandleManager.mqh>
#include <Trade/Trade.mqh>
// ---------------------------------------------------------------------
// CLASS: CalculatePositionData
// ---------------------------------------------------------------------
// Provides core logic for computing stop loss, take profit, lot size,
// and trading costs based on symbol, price, and risk parameters.
// ---------------------------------------------------------------------
class CalculatePositionData : public CObject {
protected:
CTrade trade;
CPositionInfo position;
MarketDataUtils mdu;
AtrHandleManager atr_manager;
bool check_lots(double& lots, string symbol);
bool normalise_price(double price, double& normalizedPrice, string symbol);
public:
double calculate_stoploss(string symbol, double price, int order_side, string mode_sl, double sl_var, ENUM_TIMEFRAMES atr_tf);
double calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double tp_var, ENUM_TIMEFRAMES atr_tf);
double calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var);
double calculate_trading_cost(string symbol, ulong ticket);
};
// ---------------------------------------------------------------------
// Calculates stop loss based on selected method.
//
// Parameters:
// - symbol : Symbol for the trade.
// - price : Entry price.
// - order_side: 1 = Buy, 2 = Sell.
// - mode_sl : SL method ("NO_STOPLOSS", "SL_FIXED_PIPS", etc).
// - sl_var : SL parameter (pips, %, ATR multiplier, or absolute).
// - atr_tf : Timeframe for ATR.
//
// Returns:
// - Calculated SL price, or 0 if invalid.
// ---------------------------------------------------------------------
double CalculatePositionData::calculate_stoploss(string symbol, double price, int order_side, string mode_sl, double sl_var, ENUM_TIMEFRAMES atr_tf) {
double sl = 0;
if (mode_sl == "NO_STOPLOSS") return 0;
if (mode_sl == "SL_FIXED_PIPS") {
double adj_point = mdu.adjusted_point(symbol);
sl = (order_side == 1) ? price - sl_var * adj_point : price + sl_var * adj_point;
if (!normalise_price(sl, sl, symbol)) return 0;
}
if (mode_sl == "SL_FIXED_PERCENT") {
sl = (order_side == 1) ? price - (sl_var * price / 100.0) : price + (sl_var * price / 100.0);
if (!normalise_price(sl, sl, symbol)) return 0;
}
if (mode_sl == "SL_ATR_MULTIPLE") {
double atr = atr_manager.get_atr_value(symbol, atr_tf, 14);
if (atr == EMPTY_VALUE) return 0;
sl = (order_side == 1) ? price - atr * sl_var : price + atr * sl_var;
if (!normalise_price(sl, sl, symbol)) return 0;
}
if (mode_sl == "SL_SPECIFIED_VALUE") {
double adj_point = mdu.adjusted_point(symbol);
double limit_sl = (order_side == 1) ? price - 10 * adj_point : price + 10 * adj_point;
sl = (order_side == 1) ? fmax(sl_var, limit_sl) : fmin(sl_var, limit_sl);
if (!normalise_price(sl, sl, symbol)) return 0;
}
return sl;
}
// ---------------------------------------------------------------------
// Calculates take profit based on selected method.
//
// Parameters:
// - symbol : Symbol for the trade.
// - price : Entry price.
// - stoploss : SL value (used in TP/SL ratio mode).
// - order_side: 1 = Buy, -1 = Sell.
// - mode_tp : TP method ("NO_TAKE_PROFIT", "TP_FIXED_PIPS", etc).
// - tp_var : TP parameter (pips, %, ATR multiplier, SL multiple).
// - atr_tf : Timeframe for ATR.
//
// Returns:
// - Calculated TP price, or 0 if invalid.
// ---------------------------------------------------------------------
double CalculatePositionData::calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double tp_var, ENUM_TIMEFRAMES atr_tf) {
double tp = 0;
if (mode_tp == "NO_TAKE_PROFIT") return 0;
if (mode_tp == "TP_FIXED_PIPS") {
double adj_point = mdu.adjusted_point(symbol);
tp = (order_side == 1) ? price + tp_var * adj_point : price - tp_var * adj_point;
if (!normalise_price(tp, tp, symbol)) return 0;
}
if (mode_tp == "TP_FIXED_PERCENT") {
tp = (order_side == 1) ? price + tp_var * price / 100.0 : price - tp_var * price / 100.0;
if (!normalise_price(tp, tp, symbol)) return 0;
}
if (mode_tp == "TP_ATR_MULTIPLE") {
double atr = atr_manager.get_atr_value(symbol, atr_tf, 14);
if (atr == EMPTY_VALUE) return 0;
tp = (order_side == 1) ? price + atr * tp_var : price - atr * tp_var;
if (!normalise_price(tp, tp, symbol)) return 0;
}
if (mode_tp == "TP_SL_MULTIPLE") {
double sl_size = (order_side == 1) ? price - stoploss : stoploss - price;
tp = (order_side == 1) ? price + tp_var * sl_size : price - tp_var * sl_size;
if (!normalise_price(tp, tp, symbol)) return 0;
}
if (mode_tp == "TP_SPECIFIED_VALUE") {
double adj_point = mdu.adjusted_point(symbol);
double limit_tp = (order_side == 1) ? price + 10 * adj_point : price - 10 * adj_point;
tp = (order_side == 1) ? fmin(tp_var, limit_tp) : fmax(tp_var, limit_tp);
if (!normalise_price(tp, tp, symbol)) return 0;
}
return tp;
}
// ---------------------------------------------------------------------
// Calculates lot size based on selected lot mode.
//
// Parameters:
// - symbol : Symbol for the trade.
// - sl_distance: SL distance in points.
// - price : Current price.
// - mode_lot : Lot mode ("LOT_MODE_FIXED", "LOT_MODE_PCT_RISK", etc).
// - lot_var : Value for lot calculation.
//
// Returns:
// - Computed lot size (rounded and validated).
// ---------------------------------------------------------------------
double CalculatePositionData::calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var) {
double lots = 0;
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double volume_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
double account_value = fmin(fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE)), AccountInfoDouble(ACCOUNT_MARGIN_FREE));
double risk_money = account_value * lot_var / 100.0;
if (mode_lot == "LOT_MODE_FIXED") {
lots = lot_var;
}
if (mode_lot == "LOT_MODE_PCT_RISK") {
double money_per_step = (sl_distance / tick_size) * tick_value * volume_step;
lots = MathFloor(risk_money / money_per_step) * volume_step;
}
if (mode_lot == "LOT_MODE_PCT_ACCOUNT") {
double money_per_step = (price / tick_size) * tick_value * volume_step;
lots = MathFloor(risk_money / money_per_step) * volume_step;
}
if (!check_lots(lots, symbol)) return 0;
return lots;
}
// ---------------------------------------------------------------------
// Validates and adjusts lot size to symbol constraints.
//
// Parameters:
// - lots : Input/output lot size.
// - symbol : Trading symbol.
//
// Returns:
// - true if lots are valid after correction.
// ---------------------------------------------------------------------
bool CalculatePositionData::check_lots(double& lots, string symbol) {
double min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if (lots < min) {
lots = min;
return true;
}
if (lots > max) {
Print("Lot size exceeds max for ", symbol);
return false;
}
lots = (int)MathFloor(lots / step) * step;
return true;
}
// ---------------------------------------------------------------------
// Normalizes price to the nearest valid tick size.
//
// Parameters:
// - price : Raw price.
// - normalizedPrice: Output normalized price.
// - symbol : Trading symbol.
//
// Returns:
// - true if successful, false if tick size lookup failed.
// ---------------------------------------------------------------------
bool CalculatePositionData::normalise_price(double price, double& normalizedPrice, string symbol) {
double tick_size;
if (!SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE, tick_size)) {
Print("Failed to get tick size for ", symbol);
return false;
}
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
normalizedPrice = NormalizeDouble(MathRound(price / tick_size) * tick_size, digits);
return true;
}
+308
View File
@@ -0,0 +1,308 @@
#include <MyLibs/Orders/CalculatePositionData.mqh>
#include <Trade/Trade.mqh>
class EntryOrders {
protected:
CTrade trade;
CalculatePositionData calc;
public:
int count_open_positions(string symbol, int order_side, long _magic_number);
bool open_buy_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var, string _tp_mode,
double tp_var, string _lot_mode, double lot_var, long _magic_number);
bool open_sell_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var, string _tp_mode,
double tp_var, string _lot_mode, double lot_var, long _magic_number);
bool open_buy_stop_order(string symbol, bool condition, double entry_price, datetime expiration, ENUM_TIMEFRAMES atr_period,
string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long _magic_number);
bool open_sell_stop_order(string symbol, bool condition, double entry_price, datetime expiration, ENUM_TIMEFRAMES atr_period,
string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long _magic_number);
bool open_runner_buy_order_with_virtual_tp(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var,
string _tp_mode, double tp_var, string _lot_mode, double lot_var, long _magic_number);
bool open_runner_sell_order_with_virtual_tp(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var,
string _tp_mode, double tp_var, string _lot_mode, double lot_var, long _magic_number);
};
// ---------------------------------------------------------------------
// Counts open positions by symbol, side, and magic number.
//
// Parameters:
// - symbol : Symbol to check.
// - order_side : 1 = Buy, 2 = Sell, 0 = Any.
// - _magic_number : Magic number to filter.
//
// Returns:
// - Number of matching open positions.
// ---------------------------------------------------------------------
int EntryOrders::count_open_positions(string symbol, int order_side, long _magic_number) {
int count = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == _magic_number) {
int type = (int) PositionGetInteger(POSITION_TYPE);
if (order_side == 0 || (order_side == 1 && type == POSITION_TYPE_BUY) || (order_side == 2 && type == POSITION_TYPE_SELL)) {
count++;
}
}
}
return count;
}
// ---------------------------------------------------------------------
// Opens a market BUY position.
//
// Parameters:
// - symbol : Symbol to trade.
// - condition : If false, trade will not execute.
// - atr_period : Timeframe for ATR-based SL/TP.
// - _sl_mode : SL calculation method.
// - sl_var : SL variable (e.g., pips or ATR multiplier).
// - _tp_mode : TP calculation method.
// - tp_var : TP variable.
// - _lot_mode : Lot calculation method.
// - lot_var : Lot sizing variable.
// - _magic_number : Magic number for trade.
//
// Returns:
// - True if trade was placed successfully.
// ---------------------------------------------------------------------
bool EntryOrders::open_buy_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var,
string _tp_mode, double tp_var, string _lot_mode, double lot_var, long _magic_number) {
if (!condition) return false;
double current_price = SymbolInfoDouble(symbol, SYMBOL_ASK);
if (count_open_positions(symbol, 1, _magic_number) > 0) return false;
double stop_loss = calc.calculate_stoploss(symbol, current_price, 1, _sl_mode, sl_var, atr_period);
double take_profit = calc.calculate_take_profit(symbol, current_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
double sl_distance = current_price - stop_loss;
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
if (lots <= 0) {
Print("Lot calculation failed for ", symbol);
return false;
}
trade.SetExpertMagicNumber(_magic_number);
string comment = "Magic Number: " + IntegerToString(_magic_number);
bool result = trade.PositionOpen(symbol, ORDER_TYPE_BUY, lots, current_price, stop_loss, take_profit, comment);
if (!result) Print("Trade open failed for BUY ", symbol);
return result;
}
// ---------------------------------------------------------------------
// Opens a market SELL position.
//
// Parameters:
// - symbol : Symbol to trade.
// - condition : If false, trade will not execute.
// - atr_period : Timeframe for ATR-based SL/TP.
// - _sl_mode : SL calculation method.
// - sl_var : SL variable (e.g., pips or ATR multiplier).
// - _tp_mode : TP calculation method.
// - tp_var : TP variable.
// - _lot_mode : Lot calculation method.
// - lot_var : Lot sizing variable.
// - _magic_number : Magic number for trade.
//
// Returns:
// - True if trade was placed successfully.
// ---------------------------------------------------------------------
bool EntryOrders::open_sell_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var,
string _tp_mode, double tp_var, string _lot_mode, double lot_var, long _magic_number) {
if (!condition) return false;
double current_price = SymbolInfoDouble(symbol, SYMBOL_BID);
if (count_open_positions(symbol, 2, _magic_number) > 0) return false;
double stop_loss = calc.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period);
double take_profit = calc.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
double sl_distance = stop_loss - current_price;
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
if (lots <= 0) {
Print("Lot calculation failed for ", symbol);
return false;
}
trade.SetExpertMagicNumber(_magic_number);
string comment = "Magic Number: " + IntegerToString(_magic_number);
bool result = trade.PositionOpen(symbol, ORDER_TYPE_SELL, lots, current_price, stop_loss, take_profit, comment);
if (!result) Print("Trade open failed for SELL ", symbol);
return result;
}
// ---------------------------------------------------------------------
// Opens a pending BUY STOP order.
//
// Parameters:
// - symbol : Symbol to trade.
// - condition : If false, order will not be placed.
// - entry_price : Trigger price for Buy Stop.
// - expiration : Expiration time for pending order.
// - atr_period : Timeframe for ATR-based SL/TP.
// - _sl_mode : SL calculation method.
// - sl_var : SL variable.
// - _tp_mode : TP calculation method.
// - tp_var : TP variable.
// - _lot_mode : Lot calculation method.
// - lot_var : Lot sizing variable.
// - _magic_number : Magic number for order.
//
// Returns:
// - True if order was placed successfully.
// ---------------------------------------------------------------------
bool EntryOrders::open_buy_stop_order(string symbol, bool condition, double entry_price, datetime expiration, ENUM_TIMEFRAMES atr_period,
string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long _magic_number) {
if (!condition) return false;
if (count_open_positions(symbol, 1, _magic_number) > 0) return false;
double stop_loss = calc.calculate_stoploss(symbol, entry_price, 1, _sl_mode, sl_var, atr_period);
double take_profit = calc.calculate_take_profit(symbol, entry_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
double sl_distance = entry_price - stop_loss;
double lots = calc.calculate_lots(symbol, sl_distance, entry_price, _lot_mode, lot_var);
if (lots <= 0) {
Print("Lot calculation failed for BUY STOP ", symbol);
return false;
}
trade.SetExpertMagicNumber(_magic_number);
string comment = "Magic Number: " + IntegerToString(_magic_number);
bool result = trade.BuyStop(lots, entry_price, symbol, stop_loss, take_profit, ORDER_TIME_SPECIFIED, expiration, comment);
if (!result) Print("BuyStop order failed for ", symbol);
return result;
}
// ---------------------------------------------------------------------
// Opens a pending SELL STOP order.
//
// Parameters:
// - symbol : Symbol to trade.
// - condition : If false, order will not be placed.
// - entry_price : Trigger price for Sell Stop.
// - expiration : Expiration time for pending order.
// - atr_period : Timeframe for ATR-based SL/TP.
// - _sl_mode : SL calculation method.
// - sl_var : SL variable.
// - _tp_mode : TP calculation method.
// - tp_var : TP variable.
// - _lot_mode : Lot calculation method.
// - lot_var : Lot sizing variable.
// - _magic_number : Magic number for order.
//
// Returns:
// - True if order was placed successfully.
// ---------------------------------------------------------------------
bool EntryOrders::open_sell_stop_order(string symbol, bool condition, double entry_price, datetime expiration, ENUM_TIMEFRAMES atr_period,
string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long _magic_number) {
if (!condition) return false;
if (count_open_positions(symbol, 2, _magic_number) > 0) return false;
double stop_loss = calc.calculate_stoploss(symbol, entry_price, 2, _sl_mode, sl_var, atr_period);
double take_profit = calc.calculate_take_profit(symbol, entry_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
double sl_distance = stop_loss - entry_price;
double lots = calc.calculate_lots(symbol, sl_distance, entry_price, _lot_mode, lot_var);
if (lots <= 0) {
Print("Lot calculation failed for SELL STOP ", symbol);
return false;
}
trade.SetExpertMagicNumber(_magic_number);
string comment = "Magic Number: " + IntegerToString(_magic_number);
bool result = trade.SellStop(lots, entry_price, symbol, stop_loss, take_profit, ORDER_TIME_SPECIFIED, expiration, comment);
if (!result) Print("SellStop order failed for ", symbol);
return result;
}
// ---------------------------------------------------------------------
// Opens a market BUY runner with virtual TP in comment.
//
// Parameters:
// - symbol : Symbol to trade.
// - condition : If false, trade will not execute.
// - atr_period : Timeframe for ATR-based SL.
// - _sl_mode : SL calculation method.
// - sl_var : SL variable.
// - _tp_mode : TP calculation method.
// - tp_var : TP variable (used for virtual TP).
// - _lot_mode : Lot calculation method.
// - lot_var : Lot sizing variable.
// - _magic_number : Magic number for trade.
//
// Returns:
// - True if trade was placed successfully.
// ---------------------------------------------------------------------
bool EntryOrders::open_runner_buy_order_with_virtual_tp(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode,
double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long _magic_number) {
if (!condition) return false;
double current_price = SymbolInfoDouble(symbol, SYMBOL_ASK);
if (count_open_positions(symbol, 1, _magic_number) > 0) return false;
double stop_loss = calc.calculate_stoploss(symbol, current_price, 1, _sl_mode, sl_var, atr_period);
double virtual_tp = calc.calculate_take_profit(symbol, current_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
double sl_distance = current_price - stop_loss;
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
if (lots <= 0) {
Print("Lot calculation failed for runner BUY ", symbol);
return false;
}
trade.SetExpertMagicNumber(_magic_number);
string comment = StringFormat("runner_tp:%.5f", virtual_tp);
bool result = trade.PositionOpen(symbol, ORDER_TYPE_BUY, lots, current_price, stop_loss, 0.0, comment);
if (!result) Print("Runner BUY order failed for ", symbol);
return result;
}
// ---------------------------------------------------------------------
// Opens a market SELL runner with virtual TP in comment.
//
// Parameters:
// - symbol : Symbol to trade.
// - condition : If false, trade will not execute.
// - atr_period : Timeframe for ATR-based SL.
// - _sl_mode : SL calculation method.
// - sl_var : SL variable.
// - _tp_mode : TP calculation method.
// - tp_var : TP variable (used for virtual TP).
// - _lot_mode : Lot calculation method.
// - lot_var : Lot sizing variable.
// - _magic_number : Magic number for trade.
//
// Returns:
// - True if trade was placed successfully.
// ---------------------------------------------------------------------
bool EntryOrders::open_runner_sell_order_with_virtual_tp(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode,
double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long _magic_number) {
if (!condition) return false;
double current_price = SymbolInfoDouble(symbol, SYMBOL_BID);
if (count_open_positions(symbol, 2, _magic_number) > 0) return false;
double stop_loss = calc.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period);
double virtual_tp = calc.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
double sl_distance = stop_loss - current_price;
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
if (lots <= 0) {
Print("Lot calculation failed for runner SELL ", symbol);
return false;
}
trade.SetExpertMagicNumber(_magic_number);
string comment = StringFormat("runner_tp:%.5f", virtual_tp);
bool result = trade.PositionOpen(symbol, ORDER_TYPE_SELL, lots, current_price, stop_loss, 0.0, comment);
if (!result) Print("Runner SELL order failed for ", symbol);
return result;
}
+199
View File
@@ -0,0 +1,199 @@
#include <MyLibs/Orders/CalculatePositionData.mqh>
#include <MyLibs/Utils/TimeZones.mqh>
#include <Trade/Trade.mqh>
class ExitOrders {
protected:
CTrade trade;
TimeZones tz;
CalculatePositionData calc;
ulong posTicket;
long position_open_time;
long first_allowed_close_time;
public:
bool close_buy_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period, long _magic_number);
bool close_sell_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period, long _magic_number);
bool daily_timed_exit(string symbol, datetime exit_time, int delay_days, long _magic_number);
bool daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, string exit_time, string cw_tzone, int delay_days, long _magic_number);
bool first_profitable_close_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, long _magic_number);
};
// ---------------------------------------------------------------------
// Closes BUY positions on condition + after a number of bars (if non 0) .
//
// Parameters:
// - symbol : Symbol to evaluate positions for.
// - condition : If true, closes position immediately.
// - close_bars : Minimum number of bars before auto close.
// - close_bar_period : Timeframe to count bars on.
// - _magic_number : Magic number to identify the trade group.
//
// Returns:
// - True after evaluation and any attempted closes.
// ---------------------------------------------------------------------
bool ExitOrders::close_buy_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period, long _magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == _magic_number) {
int time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1;
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
if (condition || (close_bars > 0 && time_difference >= close_bars)) {
trade.PositionClose(posTicket);
}
}
}
}
return true;
}
// ---------------------------------------------------------------------
// Closes SELL positions on condition + after a number of bars (if non 0) .
//
// Parameters:
// - symbol : Symbol to evaluate positions for.
// - condition : If true, closes position immediately.
// - close_bars : Minimum number of bars before auto close.
// - close_bar_period : Timeframe to count bars on.
// - _magic_number : Magic number to identify the trade group.
//
// Returns:
// - True after evaluation and any attempted closes.
// ---------------------------------------------------------------------
bool ExitOrders::close_sell_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period, long _magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == _magic_number) {
int time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1;
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
if (condition || (close_bars > 0 && time_difference >= close_bars)) {
trade.PositionClose(posTicket);
}
}
}
}
return true;
}
// ---------------------------------------------------------------------
// Closes position after a fixed exit time and delay in days.
//
// Parameters:
// - symbol : Symbol to evaluate.
// - exit_time : Time of day when exit is permitted.
// - delay_days : Number of full days before close allowed.
// - _magic_number : Magic number to identify the trade group.
//
// Returns:
// - True after evaluation and any attempted closes.
// ---------------------------------------------------------------------
bool ExitOrders::daily_timed_exit(string symbol, datetime exit_time, int delay_days, long _magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
position_open_time = PositionGetInteger(POSITION_TIME);
if ((int) position_open_time > 0) {
first_allowed_close_time = position_open_time + (delay_days * PeriodSeconds(PERIOD_D1));
if (TimeCurrent() > first_allowed_close_time && TimeCurrent() >= exit_time) {
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == _magic_number) {
trade.PositionClose(posTicket);
}
}
}
}
return true;
}
// ---------------------------------------------------------------------
// Closes a position only if it's profitable after a given time.
//
// Parameters:
// - symbol : Symbol to evaluate.
// - close_bar_period : Bar timeframe for bar-close evaluation.
// - exit_time : Time of day when profit exit is checked.
// - cw_tzone : Clockwork time zone for exit conversion.
// - delay_days : Minimum days to wait before closing.
// - _magic_number : Magic number to identify the trade group.
//
// Returns:
// - True after evaluation and any attempted closes.
// ---------------------------------------------------------------------
bool ExitOrders::daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, string exit_time, string cw_tzone, int delay_days,
long _magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
position_open_time = PositionGetInteger(POSITION_TIME);
if ((int) position_open_time > 0) {
first_allowed_close_time = position_open_time + (delay_days * PeriodSeconds(PERIOD_D1));
if (TimeCurrent() > first_allowed_close_time) {
datetime broker_close_time = tz.timezone_conversions(cw_tzone, StringToTime(exit_time), "Broker");
if (TimeCurrent() >= broker_close_time && PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == _magic_number) {
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double spread = SymbolInfoDouble(symbol, SYMBOL_ASK) - SymbolInfoDouble(symbol, SYMBOL_BID);
double bar_close = iClose(_Symbol, close_bar_period, 1); // shift 1 because 0 is live candle
double trading_cost = calc.calculate_trading_cost(symbol, posTicket);
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY &&
bar_close > (position_open_price + spread + trading_cost)) {
trade.PositionClose(posTicket);
}
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL &&
bar_close < (position_open_price - spread - trading_cost)) {
trade.PositionClose(posTicket);
}
}
}
}
}
return true;
}
// ---------------------------------------------------------------------
// Closes position on first profitable bar after one bar completes.
//
// Parameters:
// - symbol : Symbol to evaluate.
// - close_bar_period : Timeframe for bar-close evaluation.
// - _magic_number : Magic number to identify the trade group.
//
// Returns:
// - True after evaluation and any attempted closes.
// ---------------------------------------------------------------------
bool ExitOrders::first_profitable_close_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, long _magic_number) {
position_open_time = PositionGetInteger(POSITION_TIME);
first_allowed_close_time = position_open_time + PeriodSeconds(close_bar_period);
if ((int) position_open_time > 0 && TimeCurrent() > first_allowed_close_time) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == _magic_number) {
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double spread = SymbolInfoDouble(symbol, SYMBOL_ASK) - SymbolInfoDouble(symbol, SYMBOL_BID);
double bar_close = iClose(_Symbol, close_bar_period, 1);
double trading_cost = calc.calculate_trading_cost(symbol, posTicket);
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && bar_close > (position_open_price + spread + trading_cost)) {
trade.PositionClose(posTicket);
}
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && bar_close < (position_open_price - spread - trading_cost)) {
trade.PositionClose(posTicket);
}
}
}
}
return true;
}
+96
View File
@@ -0,0 +1,96 @@
#include <Trade/OrderInfo.mqh>
#include <Trade/PositionInfo.mqh>
class OrderTracker {
protected:
COrderInfo m_order;
CPositionInfo m_position;
public:
int count_open_positions(string symbol, int order_side, long magic_number);
int count_all_positions(string symbol, long magic_number);
int count_pending_orders(string symbol, ENUM_ORDER_TYPE order_type, long magic);
};
// ---------------------------------------------------------------------
// Counts the number of open BUY or SELL positions for a given symbol.
//
// Parameters:
// - symbol : Trading symbol (e.g., "EURUSD").
// - order_side : 1 = BUY, 2 = SELL.
// - magic_number : Magic number identifying strategy group.
//
// Returns:
// - Number of matching open positions.
// ---------------------------------------------------------------------
int OrderTracker::count_open_positions(string symbol, int order_side, long magic_number) {
int count = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
if (order_side == 1 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
count++;
}
if (order_side == 2 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
count++;
}
}
}
return count;
}
// ---------------------------------------------------------------------
// Counts all open positions for a symbol regardless of direction.
//
// Parameters:
// - symbol : Trading symbol.
// - magic_number : Magic number identifying strategy group.
//
// Returns:
// - Total number of matching positions.
// ---------------------------------------------------------------------
int OrderTracker::count_all_positions(string symbol, long magic_number) {
int count = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
count++;
}
}
return count;
}
// ---------------------------------------------------------------------
// Counts pending orders of a specific type for a symbol and magic number.
//
// Parameters:
// - symbol : Trading symbol.
// - order_type : Type of pending order (e.g., ORDER_TYPE_BUY_STOP).
// - magic : Magic number identifying strategy group.
//
// Returns:
// - Number of matching pending orders.
// ---------------------------------------------------------------------
int OrderTracker::count_pending_orders(string symbol, ENUM_ORDER_TYPE order_type, long magic) {
int count = 0;
for (int i = OrdersTotal() - 1; i >= 0; i--) {
if (m_order.SelectByIndex(i)) {
if (OrderGetInteger(ORDER_MAGIC) == magic && OrderGetString(ORDER_SYMBOL) == symbol) {
if (m_order.OrderType() == order_type) {
count++;
}
}
}
}
return count;
}
+43
View File
@@ -0,0 +1,43 @@
class StopLogic {
public:
double sl_specified_value_switch(string sl_mode, double inp_sl_var, double value);
double tp_specified_value_switch(string tp_mode, double inp_tp_var, double value);
};
// ---------------------------------------------------------------------
// Selects stop loss value based on SL mode.
//
// Parameters:
// - sl_mode : Stop loss mode ("SL_SPECIFIED_VALUE", etc).
// - inp_sl_var : User-input SL value (pips, percent, etc).
// - value : Directly specified SL value.
//
// Returns:
// - `value` if SL mode is "SL_SPECIFIED_VALUE", otherwise `inp_sl_var`.
// ---------------------------------------------------------------------
double StopLogic::sl_specified_value_switch(string sl_mode, double inp_sl_var, double value) {
if (sl_mode == "SL_SPECIFIED_VALUE") {
return value;
} else {
return inp_sl_var;
}
}
// ---------------------------------------------------------------------
// Selects take profit value based on TP mode.
//
// Parameters:
// - tp_mode : Take profit mode ("TP_SPECIFIED_VALUE", etc).
// - inp_tp_var : User-input TP value (pips, percent, etc).
// - value : Directly specified TP value.
//
// Returns:
// - `value` if TP mode is "TP_SPECIFIED_VALUE", otherwise `inp_tp_var`.
// ---------------------------------------------------------------------
double StopLogic::tp_specified_value_switch(string tp_mode, double inp_tp_var, double value) {
if (tp_mode == "TP_SPECIFIED_VALUE") {
return value;
} else {
return inp_tp_var;
}
}