update to my mt5 libs to include better comments

This commit is contained in:
Matt Corcoran
2025-07-12 15:29:30 +02:00
parent ec3fc120ce
commit 136522ef1c
31 changed files with 1389 additions and 2024 deletions
+60 -7
View File
@@ -1,10 +1,25 @@
#include <Trade/Trade.mqh>
// ---------------------------------------------------------------------
// ENUM: CUSTOM_MAX_TYPE
// ---------------------------------------------------------------------
// Defines the types of custom performance criteria that can be used
// for calculating max optimization targets.
//
// Values:
// - CM_WIN_LOSS_RATIO : Use win/loss ratio.
// - CM_WIN_PERCENT : Use win percentage.
// ---------------------------------------------------------------------
enum CUSTOM_MAX_TYPE {
CM_WIN_LOSS_RATIO,
CM_WIN_PERCENT
};
// ---------------------------------------------------------------------
// CLASS: CustomMax
// ---------------------------------------------------------------------
// Calculates custom performance criteria for use in optimizations.
// ---------------------------------------------------------------------
class CustomMax : public CObject {
protected:
double custom_criteria;
@@ -16,6 +31,17 @@ public:
double calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type, int min_trades = 0);
};
// ---------------------------------------------------------------------
// Calculates the selected custom criteria metric.
//
// Parameters:
// - cm_type : The selected criteria type to calculate.
// - min_trades : Optional minimum number of trades (default 0).
//
// Logic:
// - Calls the appropriate private method based on cm_type.
// - Returns the resulting metric (or 0 if invalid).
// ---------------------------------------------------------------------
double CustomMax::calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type, int min_trades) {
switch(cm_type) {
case CM_WIN_LOSS_RATIO:
@@ -31,22 +57,49 @@ double CustomMax::calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type, int min_tra
return custom_criteria;
}
// Returns the win/loss ratio, with min trades check
// ---------------------------------------------------------------------
// Calculates win/loss ratio with a minimum trade count check.
//
// Parameters:
// - min_required_trades : Minimum number of trades required.
//
// Logic:
// - Returns wins / losses if minimum is met.
// - Prevents division by zero.
// ---------------------------------------------------------------------
double CustomMax::win_loss_ratio(int min_required_trades) {
double wins = TesterStatistics(STAT_PROFIT_TRADES);
double losses = TesterStatistics(STAT_LOSS_TRADES);
double total_trades = TesterStatistics(STAT_TRADES);
if((min_required_trades > 0 && total_trades < min_required_trades) || total_trades == 0) return 0;
if(losses == 0) return 0; // Prevent division by zero
if ((min_required_trades > 0 && total_trades < min_required_trades) || total_trades == 0)
return 0;
if (losses == 0)
return 0;
return wins / losses;
}
// Returns the win percentage, with min trades check
// ---------------------------------------------------------------------
// Calculates win percentage with a minimum trade count check.
//
// Parameters:
// - min_required_trades : Minimum number of trades required.
//
// Logic:
// - Returns (wins / total) * 100 if minimum is met.
// - Filters out invalid math results.
// ---------------------------------------------------------------------
double CustomMax::win_percent_min_trades(int min_required_trades) {
double wins = TesterStatistics(STAT_PROFIT_TRADES);
double total_trades = TesterStatistics(STAT_TRADES);
if((min_required_trades > 0 && total_trades < min_required_trades) || total_trades == 0) return 0;
double result = wins / total_trades * 100;
if(!MathIsValidNumber(result)) return 0;
if ((min_required_trades > 0 && total_trades < min_required_trades) || total_trades == 0)
return 0;
double result = (wins / total_trades) * 100;
if (!MathIsValidNumber(result))
return 0;
return result;
}
+61 -31
View File
@@ -1,4 +1,18 @@
enum MODE_SPLIT_DATA{
// ---------------------------------------------------------------------
// ENUM: MODE_SPLIT_DATA
// ---------------------------------------------------------------------
// Defines how to split data during testing based on time attributes.
//
// Values:
// - NO_SPLIT : Do not split, always return true.
// - ODD_YEARS : Include only odd-numbered years.
// - EVEN_YEARS : Include only even-numbered years.
// - ODD_MONTHS : Include only odd-numbered months.
// - EVEN_MONTHS : Include only even-numbered months.
// - ODD_WEEKS : Include only odd-numbered weeks.
// - EVEN_WEEKS : Include only even-numbered weeks.
// ---------------------------------------------------------------------
enum MODE_SPLIT_DATA {
NO_SPLIT,
ODD_YEARS,
EVEN_YEARS,
@@ -8,44 +22,60 @@ enum MODE_SPLIT_DATA{
EVEN_WEEKS
};
// ---------------------------------------------------------------------
// CLASS: TestDataSplit
// ---------------------------------------------------------------------
// Provides logic to determine whether the current date falls within
// a selected split group for testing or optimization purposes.
// ---------------------------------------------------------------------
class TestDataSplit {
public:
bool in_test_period(MODE_SPLIT_DATA data_split_method);
bool in_test_period(MODE_SPLIT_DATA data_split_method);
};
// ---------------------------------------------------------------------
// Determines whether the current time falls in the selected group.
//
// Parameters:
// - data_split_method : Enum value defining the split strategy.
//
// Logic:
// - Extracts current date components (year, month, ISO week).
// - Returns true if current time matches the given split rule.
// ---------------------------------------------------------------------
bool TestDataSplit::in_test_period(MODE_SPLIT_DATA data_split_method) {
string result[];
string string_tc = TimeToString(TimeCurrent());
string result[];
string string_tc = TimeToString(TimeCurrent());
// Extract components from datetime string (assumes YYYY.MM.DD format)
ushort u_sep = StringGetCharacter(".", 0);
StringSplit(string_tc, u_sep, result);
// Extract components from datetime string (assumes YYYY.MM.DD format)
ushort u_sep = StringGetCharacter(".", 0);
StringSplit(string_tc, u_sep, result);
bool odd_year = int(result[0]) % 2;
bool odd_month = int(result[1]) % 2;
bool odd_year = int(result[0]) % 2;
bool odd_month = int(result[1]) % 2;
// Calculate week of the year (basic approximation)
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
int iDay = (dt.day_of_week + 6) % 7 + 1; // Convert to 1=Mon,...,7=Sun
int iWeek = (dt.day_of_year - iDay + 10) / 7; // Estimate ISO week number
bool odd_week = iWeek % 2;
// Calculate week of the year (approximate ISO week)
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
int i_day = (dt.day_of_week + 6) % 7 + 1; // Convert to 1=Mon,...,7=Sun
int i_week = (dt.day_of_year - i_day + 10) / 7; // Approximate ISO week number
bool odd_week = i_week % 2;
// Split logic depending on mode
if (data_split_method == NO_SPLIT)
return true;
if (data_split_method == ODD_YEARS && odd_year)
return true;
if (data_split_method == EVEN_YEARS && !odd_year)
return true;
if (data_split_method == ODD_MONTHS && odd_month)
return true;
if (data_split_method == EVEN_MONTHS && !odd_month)
return true;
if (data_split_method == ODD_WEEKS && odd_week)
return true;
if (data_split_method == EVEN_WEEKS && !odd_week)
return true;
// Split logic depending on mode
if (data_split_method == NO_SPLIT)
return true;
if (data_split_method == ODD_YEARS && odd_year)
return true;
if (data_split_method == EVEN_YEARS && !odd_year)
return true;
if (data_split_method == ODD_MONTHS && odd_month)
return true;
if (data_split_method == EVEN_MONTHS && !odd_month)
return true;
if (data_split_method == ODD_WEEKS && odd_week)
return true;
if (data_split_method == EVEN_WEEKS && !odd_week)
return true;
return false;
return false;
}
+134 -135
View File
@@ -1,24 +1,35 @@
#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:
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);
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);
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;
@@ -38,37 +49,33 @@ void AdjustPosition::set_breakeven_sl(string symbol, int runner_magic_no, double
}
}
// ---------------------------------------------------------
// Check runner trades for virtual TP hits and set SL to breakeven if crossed.
// Optimized to avoid unnecessary processing on every OnTimer()/OnTick() call.
// ---------------------------------------------------------------------
// 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) {
// --- Get current bid/ask and symbol precision
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double buffer_price = buffer_points * _Point;
double price_margin = 50 * _Point; // avoid premature checking if far from TP
double price_margin = 50 * _Point;
// --- Fast check: skip if no runner trades exist for this symbol
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) {
if ((int) PositionGetInteger(POSITION_MAGIC) == runner_magic_no) {
has_runner = true;
break;
}
}
if (!has_runner) return;
// --- Loop through positions for breakeven SL
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;
if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue;
long order_type = PositionGetInteger(POSITION_TYPE);
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
@@ -76,51 +83,135 @@ void AdjustPosition::set_breakeven_if_profit_target_hit(string symbol, int runne
double tp = PositionGetDouble(POSITION_TP);
string comment = PositionGetString(POSITION_COMMENT);
// --- Parse virtual TP from comment: expected format "runner_tp:1.10500"
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; // no valid virtual TP
if (tp > 0.0) {
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);
}
// --- Skip early if price not near virtual 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;
// --- Check if virtual TP was hit
bool tp_hit = false;
if (order_type == POSITION_TYPE_BUY && bid >= virtual_tp) tp_hit = true;
if (order_type == POSITION_TYPE_SELL && ask <= virtual_tp) tp_hit = true;
bool tp_hit = (order_type == POSITION_TYPE_BUY && bid >= virtual_tp) ||
(order_type == POSITION_TYPE_SELL && ask <= virtual_tp);
if (!tp_hit) continue;
// Calculate expected breakeven SL
double expected_sl = (order_type == POSITION_TYPE_BUY)
? entry + buffer_price
: entry - buffer_price;
// Skip if SL already set to breakeven
if (NormalizeDouble(sl, digits) == NormalizeDouble(expected_sl, digits)) continue;
// --- Set breakeven SL
set_breakeven_sl_for_ticket(symbol, ticket, order_type, entry, sl, tp, digits, buffer_price, true);
}
}
// ---------------------------------------------------------------------
// TRAILING STOP ATR LOGIC
// Sets SL for a single trade to breakeven, optionally removes TP.
// ---------------------------------------------------------------------
// This function updates the stop-loss of runner trades based on ATR.
// It only applies to trades with the given magic number and symbol.
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 : The trading symbol.
// - 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.
@@ -132,8 +223,8 @@ void AdjustPosition::set_breakeven_if_profit_target_hit(string symbol, int runne
// - 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) {
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;
@@ -151,16 +242,19 @@ void AdjustPosition::trailing_stop_atr(string symbol, int _magic_number, ENUM_TI
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);
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 ((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);
@@ -168,98 +262,3 @@ void AdjustPosition::trailing_stop_atr(string symbol, int _magic_number, ENUM_TI
PrintFormat("Trailing SL updated: %s ticket=%d new SL=%.5f", symbol, ticket, new_sl);
}
}
// ---------------------------------------------------------
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);
}
}
// ---------------------------------------------------------
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);
}
}
}
// ---------------------------------------------------------
void AdjustPosition::set_trailing_sl(string symbol, int runner_magic_no, double sl_offset_points) {
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double price = 0;
double sl = 0;
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);
price = (type == POSITION_TYPE_BUY) ? SymbolInfoDouble(symbol, SYMBOL_BID) : SymbolInfoDouble(symbol, SYMBOL_ASK);
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);
}
}
}
+69 -24
View File
@@ -3,6 +3,12 @@
#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;
@@ -20,8 +26,20 @@ public:
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;
@@ -55,8 +73,21 @@ double CalculatePositionData::calculate_stoploss(string symbol, double price, in
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;
@@ -96,8 +127,19 @@ double CalculatePositionData::calculate_take_profit(string symbol, double price,
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);
@@ -125,8 +167,16 @@ double CalculatePositionData::calculate_lots(string symbol, double sl_distance,
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);
@@ -146,8 +196,17 @@ bool CalculatePositionData::check_lots(double& lots, string symbol) {
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)) {
@@ -159,17 +218,3 @@ bool CalculatePositionData::normalise_price(double price, double& normalizedPric
normalizedPrice = NormalizeDouble(MathRound(price / tick_size) * tick_size, digits);
return true;
}
//+------------------------------------------------------------------+
// double CalculatePositionData::calculate_trading_cost(string symbol, ulong ticket) {
// position.SelectByTicket(ticket);
// double swap = PositionGetDouble(POSITION_SWAP);
// double commission = PositionGetDouble(POSITION_COMMISSION);
// double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
// double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
// double lots = PositionGetDouble(POSITION_VOLUME);
// return -1.0 * ((commission + swap) / tick_value * tick_size / lots);
// }
+123 -1
View File
@@ -28,9 +28,19 @@ public:
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--) {
@@ -45,6 +55,24 @@ int EntryOrders::count_open_positions(string symbol, int order_side, long _magic
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;
@@ -68,6 +96,24 @@ bool EntryOrders::open_buy_orders(string symbol, bool condition, ENUM_TIMEFRAMES
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;
@@ -91,6 +137,26 @@ bool EntryOrders::open_sell_orders(string symbol, bool condition, ENUM_TIMEFRAME
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) {
@@ -114,6 +180,26 @@ bool EntryOrders::open_buy_stop_order(string symbol, bool condition, double entr
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) {
@@ -137,6 +223,24 @@ bool EntryOrders::open_sell_stop_order(string symbol, bool condition, double ent
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) {
@@ -161,6 +265,24 @@ bool EntryOrders::open_runner_buy_order_with_virtual_tp(string symbol, bool cond
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) {
+66 -4
View File
@@ -3,7 +3,7 @@
#include <Trade/Trade.mqh>
class ExitOrders {
protected:
protected:
CTrade trade;
TimeZones tz;
CalculatePositionData calc;
@@ -12,15 +12,27 @@ class ExitOrders {
long position_open_time;
long first_allowed_close_time;
public:
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 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);
@@ -38,6 +50,19 @@ bool ExitOrders::close_buy_orders(string symbol, bool condition, int close_bars,
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);
@@ -55,6 +80,18 @@ bool ExitOrders::close_sell_orders(string symbol, bool condition, int close_bars
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);
@@ -73,6 +110,20 @@ bool ExitOrders::daily_timed_exit(string symbol, datetime exit_time, int delay_d
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--) {
@@ -108,6 +159,17 @@ bool ExitOrders::daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_ba
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);
+33
View File
@@ -12,6 +12,18 @@ class OrderTracker {
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;
@@ -32,6 +44,16 @@ int OrderTracker::count_open_positions(string symbol, int order_side, long magic
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;
@@ -46,6 +68,17 @@ int OrderTracker::count_all_positions(string symbol, long magic_number) {
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;
+24 -2
View File
@@ -1,9 +1,20 @@
class StopLogic {
public:
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;
@@ -12,8 +23,19 @@ double StopLogic::sl_specified_value_switch(string sl_mode, double inp_sl_var, d
}
}
// ---------------------------------------------------------------------
// 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 == "SL_SPECIFIED_VALUE") {
if (tp_mode == "TP_SPECIFIED_VALUE") {
return value;
} else {
return inp_tp_var;
-81
View File
@@ -1,81 +0,0 @@
#include <Trade/Trade.mqh>
class TrailingLogic {
protected:
CTrade trade;
public:
void break_even_stop(string symbol, ulong magic_number, int be_trigger_points, int be_puffer);
void nnfx_trailing_stop(string symbol, double sl_var, double tp_var, double atr_value, ulong magic_number);
};
void TrailingLogic::break_even_stop(string symbol, ulong magic_number, int be_trigger_points, int be_puffer) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
int symbol_digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), symbol_digits);
double bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), symbol_digits);
if (be_trigger_points != 0) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket)) {
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double position_volume = PositionGetDouble(POSITION_VOLUME);
double position_sl = PositionGetDouble(POSITION_SL);
double position_tp = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE) PositionGetInteger(POSITION_TYPE);
if (position_type == POSITION_TYPE_BUY && bid > position_open_price + be_trigger_points * symbol_point) {
double sl = NormalizeDouble(position_open_price + be_puffer * symbol_point, symbol_digits);
if (sl > position_sl) {
trade.PositionModify(ticket, sl, position_tp);
Print("-----------------------------------Stop moved to break even");
}
}
if (position_type == POSITION_TYPE_SELL && ask < position_open_price - be_trigger_points * symbol_point) {
double sl = NormalizeDouble(position_open_price - be_puffer * symbol_point, symbol_digits);
if (sl < position_sl) {
trade.PositionModify(ticket, sl, position_tp);
Print("-----------------------------------Stop moved to break even");
}
}
}
}
}
}
}
void TrailingLogic::nnfx_trailing_stop(string symbol, double sl_var, double tp_var, double atr_value, ulong magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket)) {
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
int symbol_digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), symbol_digits);
double bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), symbol_digits);
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double position_sl = PositionGetDouble(POSITION_SL);
double position_tp = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE) PositionGetInteger(POSITION_TYPE);
if (position_type == POSITION_TYPE_BUY && bid > position_open_price + (atr_value * tp_var)) {
double sl = NormalizeDouble(bid - (atr_value * sl_var), symbol_digits);
if (sl > (position_sl + (atr_value * 0.5))) {
trade.PositionModify(ticket, sl, position_tp);
}
}
if (position_type == POSITION_TYPE_SELL && ask < position_open_price - (atr_value * tp_var)) {
double sl = NormalizeDouble(ask + (atr_value * sl_var), symbol_digits);
if (sl < (position_sl + (atr_value * 0.5))) {
trade.PositionModify(ticket, sl, position_tp);
}
}
}
}
}
}
+45 -5
View File
@@ -1,11 +1,11 @@
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/Utils/BarUtils.mqh>
#include <MyLibs/Utils/MarketDataUtils.mqh>
class DrawdownControl : public CObject {
protected:
CTrade trade;
BarUtils bar_utils;
MarketDataUtils m_utils;
string data_file;
double daily_max_dd_per;
@@ -31,6 +31,17 @@ class DrawdownControl : public CObject {
double lot_correction_dynamic(double acc_dd_percent, double min_lot_factor, double max_lot_factor);
};
// ---------------------------------------------------------------------
// Initializes the drawdown control with input parameters and reads
// or resets persistent drawdown state.
//
// Parameters:
// - inp_data_file : Filename for drawdown data storage.
// - inp_acc_max_dd_per : Max absolute drawdown percentage.
// - inp_daily_max_dd_per : Max daily drawdown percentage.
// - inp_daily_reset_time : Reset time (e.g., "00:00").
// - inp_print_statments : Optional flag to print debug info.
// ---------------------------------------------------------------------
void DrawdownControl::init_dd_control(string inp_data_file, double inp_acc_max_dd_per, double inp_daily_max_dd_per, string inp_daily_reset_time, bool inp_print_statments = true) {
data_file = inp_data_file;
@@ -82,6 +93,13 @@ void DrawdownControl::init_dd_control(string inp_data_file, double inp_acc_max_d
print_messages();
}
// ---------------------------------------------------------------------
// Checks if current equity has breached the daily drawdown threshold.
// If so, closes all trades and cancels orders.
//
// Returns:
// - true if daily drawdown limit has been reached.
// ---------------------------------------------------------------------
bool DrawdownControl::determine_daily_dd_limit() {
// Reset max equity at the start of each day:
@@ -116,7 +134,19 @@ bool DrawdownControl::determine_daily_dd_limit() {
return daily_dd_limit_reached;
}
// Reduces lot size as account apporchaes max allowed drawdown limit.
// ---------------------------------------------------------------------
// Calculates a corrected lot multiplier based on account drawdown.
//
// Parameters:
// - acc_equity_start : Starting equity reference.
// - min_lot_factor : Minimum lot scaling factor.
// - max_lot_factor : Maximum lot scaling factor.
// - dynm_lot_factor : Enable trailing dynamic lot logic.
// - dlf_trail_per : Percent buffer for dynamic trail.
//
// Returns:
// - Scaled lot factor between min and max bounds.
// ---------------------------------------------------------------------
double DrawdownControl::lot_correction_factor(double acc_equity_start, double min_lot_factor, double max_lot_factor, bool dynm_lot_factor=false, double dlf_trail_per=20) {
double account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE));
@@ -147,7 +177,17 @@ double DrawdownControl::lot_correction_factor(double acc_equity_start, double mi
return max_lot_factor;
}
// ---------------------------------------------------------------------
// Computes dynamic lot factor based on trailing equity bounds.
//
// Parameters:
// - acc_dd_percent : Dynamic trailing buffer in percent.
// - min_lot_factor : Minimum lot factor.
// - max_lot_factor : Maximum lot factor.
//
// Returns:
// - Interpolated lot factor.
// ---------------------------------------------------------------------
double DrawdownControl::lot_correction_dynamic(double acc_dd_percent, double min_lot_factor, double max_lot_factor) {
double account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE));
@@ -167,7 +207,7 @@ double DrawdownControl::lot_correction_dynamic(double acc_dd_percent, double min
}
// back-up to file every hour:
if(bar_utils.is_new_bar(_Symbol, PERIOD_H1) == true){
if(m_utils.is_new_bar(_Symbol, PERIOD_H1) == true){
write_global_var_data();
}
-105
View File
@@ -1,105 +0,0 @@
class EntryState {
public:
int last_trigger_bar_long;
int last_trigger_bar_short;
int last_bl_cross_long;
int last_bl_cross_short;
int last_entry_long;
int last_entry_short;
// Constructor
EntryState() {
reset();
}
void reset() {
last_trigger_bar_long = -1000;
last_trigger_bar_short = -1000;
last_bl_cross_long = -1000;
last_bl_cross_short = -1000;
last_entry_long = -1000;
last_entry_short = -1000;
}
void update_trigger(bool trig_long, bool trig_short, int curr_bar) {
if (trig_long) last_trigger_bar_long = curr_bar;
if (trig_short) last_trigger_bar_short = curr_bar;
}
void update_baseline_cross(int curr_bar, double price, double baseline, double prev_price, double prev_baseline) {
if (prev_price < prev_baseline && price > baseline) last_bl_cross_long = curr_bar;
if (prev_price > prev_baseline && price < baseline) last_bl_cross_short = curr_bar;
}
void update_entry(bool is_long, int curr_bar) {
if (is_long)
last_entry_long = curr_bar;
else
last_entry_short = curr_bar;
}
int get_last_trigger(bool is_long) {
return is_long ? last_trigger_bar_long : last_trigger_bar_short;
}
int get_last_cross(bool is_long) {
return is_long ? last_bl_cross_long : last_bl_cross_short;
}
int get_last_entry(bool is_long) {
return is_long ? last_entry_long : last_entry_short;
}
};
// Snapshot of conditions for a potential entry signal
struct EntryContext {
bool trigger;
bool confirm;
bool volume;
bool recent;
bool base_ok;
bool near;
bool far;
int last_entry;
int last_cross;
};
// Utility: check if signal occurred recently
bool is_recent(int signal_bar, int curr_bar, int lookback) {
return (curr_bar - signal_bar) < lookback;
}
// Utility: build current entry condition context
EntryContext build_entry_context(bool is_long, double price, double baseline, double atr, int last_cross, int last_entry, bool trigger,
bool confirm, bool volume, bool recent) {
EntryContext ctx;
ctx.trigger = trigger;
ctx.confirm = confirm;
ctx.volume = volume;
ctx.recent = recent;
ctx.base_ok = is_long ? (price > baseline) : (price < baseline);
ctx.near = MathAbs(price - baseline) <= atr;
ctx.far = MathAbs(price - baseline) > atr;
ctx.last_entry = last_entry;
ctx.last_cross = last_cross;
return ctx;
}
// Class wrapper for entry logic
class EntryLogic {
public:
bool is_standard_entry(const EntryContext& ctx) {
return ctx.trigger && ctx.confirm && ctx.volume && ctx.recent && ctx.base_ok && ctx.near;
}
bool is_pullback_entry(const EntryContext& ctx, int curr_bar) {
return (curr_bar - ctx.last_cross <= 2) && ctx.confirm && ctx.volume && ctx.base_ok && ctx.far;
}
bool is_baseline_cross_entry(const EntryContext& ctx, double prev_price, double prev_baseline, double price, double baseline,
bool is_long) {
bool crossed = is_long ? (prev_price < prev_baseline && price > baseline) : (prev_price > prev_baseline && price < baseline);
return crossed && ctx.confirm && ctx.volume && ctx.near;
}
bool is_continuation_entry(const EntryContext& ctx, int curr_bar, int look_back) {
return (curr_bar - ctx.last_entry <= look_back) && ctx.last_entry > ctx.last_cross && ctx.trigger && ctx.confirm && ctx.base_ok;
}
};
@@ -50,7 +50,6 @@ class RangeCalculator : public CObject{
public:
void calculate_range();
double get_range_high();
double get_range_low();
double get_range_mid();
@@ -65,6 +64,18 @@ class RangeCalculator : public CObject{
};
// ---------------------------------------------------------------------
// Sets the allowed days for range calculation.
//
// Parameters:
// - _inp_sun : Allow Sunday.
// - _inp_mon : Allow Monday.
// - _inp_tue : Allow Tuesday.
// - _inp_wed : Allow Wednesday.
// - _inp_thu : Allow Thursday.
// - _inp_fri : Allow Friday.
// - _inp_sat : Allow Saturday.
// ---------------------------------------------------------------------
void RangeCalculator::range_days(bool _inp_sun, bool _inp_mon, bool _inp_tue, bool _inp_wed, bool _inp_thu, bool _inp_fri, bool _inp_sat){
sun = _inp_sun;
mon = _inp_mon;
@@ -76,6 +87,22 @@ void RangeCalculator::range_days(bool _inp_sun, bool _inp_mon, bool _inp_tue, bo
days_initlised = true;
}
// ---------------------------------------------------------------------
// Initializes the range parameters.
//
// Parameters:
// - inp_symbol : The symbol for the range.
// - _calc_period : Timeframe for range calculation.
// - t1 : Start time string.
// - t2 : End time string.
// - t3 : Expiry time string.
// - t4 : Close time string.
// - time_zone : Timezone name.
// - plot_range_inp : Whether to plot the range.
//
// Returns:
// - true if initialization was successful; false otherwise.
// ---------------------------------------------------------------------
bool RangeCalculator::initilise_range(string inp_symbol, ENUM_TIMEFRAMES _calc_period, string t1, string t2, string t3, string t4, string time_zone, bool plot_range_inp){
inp_r_start_string = t1;
inp_timezone = time_zone;
@@ -113,7 +140,18 @@ bool RangeCalculator::initilise_range(string inp_symbol, ENUM_TIMEFRAMES _calc_p
return true;
}
// ---------------------------------------------------------------------
// Converts time input strings to time deltas for range definition.
//
// Parameters:
// - t1 : Start time string.
// - t2 : End time string.
// - t3 : Expiry time string.
// - t4 : Close time string.
//
// Returns:
// - true if times were converted successfully; false on error.
// ---------------------------------------------------------------------
bool RangeCalculator::convert_input_time_strings(string t1, string t2, string t3, string t4){
datetime _t1 = StringToTime(t1);
@@ -149,44 +187,93 @@ bool RangeCalculator::convert_input_time_strings(string t1, string t2, string t3
return true;
}
// high of the range
// ---------------------------------------------------------------------
// Returns the high value of the current range.
//
// Returns:
// - High price of the range.
// ---------------------------------------------------------------------
double RangeCalculator::get_range_high(){
return high;
};
// low of the range
// ---------------------------------------------------------------------
// Returns the low value of the current range.
//
// Returns:
// - Low price of the range.
// ---------------------------------------------------------------------
double RangeCalculator::get_range_low(){
return low;
};
// mid of the range
// ---------------------------------------------------------------------
// Returns the mid value of the current range.
//
// Returns:
// - Mid price of the range.
// ---------------------------------------------------------------------
double RangeCalculator::get_range_mid(){
return mid;
};
// ---------------------------------------------------------------------
// Returns the start time of the current range.
//
// Returns:
// - Range start time.
// ---------------------------------------------------------------------
datetime RangeCalculator::get_range_start(){
return start_time;
};
// ---------------------------------------------------------------------
// Returns the end time of the current range.
//
// Returns:
// - Range end time.
// ---------------------------------------------------------------------
datetime RangeCalculator::get_range_end(){
return end_time;
};
// ---------------------------------------------------------------------
// Returns the expiration time for range-based orders.
//
// Returns:
// - Order expiration time.
// ---------------------------------------------------------------------
datetime RangeCalculator::get_order_expire_time(){
return order_expire_time;
};
// ---------------------------------------------------------------------
// Returns the close time of the current range.
//
// Returns:
// - Range close time.
// ---------------------------------------------------------------------
datetime RangeCalculator::get_range_close(){
return close_time;
};
// flag if a high breakout occurred
// ---------------------------------------------------------------------
// Returns the high breakout flag of the current range.
//
// Returns:
// - true if high breakout occurred; false otherwise.
// ---------------------------------------------------------------------
bool RangeCalculator::get_range_high_breakout(){
return f_high_breakout;
};
// flag if a low breakout occurred
// ---------------------------------------------------------------------
// Returns the low breakout flag of the current range.
//
// Returns:
// - true if low breakout occurred; false otherwise.
// ---------------------------------------------------------------------
bool RangeCalculator::get_range_low_breakout(){
return f_low_breakout;
};
+32 -4
View File
@@ -12,7 +12,19 @@ private:
AtrEntry cache[]; // internal cache of ATR handles
public:
// Get or create an ATR handle for a specific symbol/timeframe/period
// ---------------------------------------------------------------------
// Returns a valid ATR handle for the given symbol, timeframe, and period.
// Creates and caches the handle if not already available.
//
// Parameters:
// - symbol : Trading symbol (e.g., "EURUSD").
// - tf : Timeframe (e.g., PERIOD_H1).
// - period : ATR period (e.g., 14).
//
// Returns:
// - The ATR indicator handle, or INVALID_HANDLE if failed.
// ---------------------------------------------------------------------
int get_atr_handle(string symbol, ENUM_TIMEFRAMES tf, int period) {
for (int i = 0; i < ArraySize(cache); i++) {
if (cache[i].symbol == symbol && cache[i].tf == tf && cache[i].period == period)
@@ -32,7 +44,18 @@ public:
return handle;
}
// Get ATR value from buffer (returns EMPTY_VALUE if failure)
// ---------------------------------------------------------------------
// Gets the ATR value for a given symbol, timeframe, and period.
//
// Parameters:
// - symbol : Trading symbol (e.g., "EURUSD").
// - tf : Timeframe to use (e.g., PERIOD_H1).
// - period : ATR period to calculate.
// - shift : Bar shift to read the value from (default is 1 for last closed bar. NEVER use 0!).
//
// Returns:
// - ATR value at the given shift, or EMPTY_VALUE on failure.
// ---------------------------------------------------------------------
double get_atr_value(string symbol, ENUM_TIMEFRAMES tf, int period, int shift = 1) {
int handle = get_atr_handle(symbol, tf, period);
if (handle == INVALID_HANDLE) {
@@ -51,7 +74,13 @@ public:
return buffer[0];
}
// Release all handles in the cache
// ---------------------------------------------------------------------
// Releases all cached ATR handles and clears the internal cache.
//
// Logic:
// - Calls IndicatorRelease for each handle.
// - Clears the `cache` array.
// ---------------------------------------------------------------------
void release_handles() {
for (int i = 0; i < ArraySize(cache); i++) {
if (cache[i].handle != INVALID_HANDLE)
@@ -60,4 +89,3 @@ public:
ArrayResize(cache, 0);
}
};
+31 -19
View File
@@ -1,31 +1,43 @@
#include <Object.mqh>
class ChartUtils : public CObject {
public:
void draw_line(double value, string name, color clr = clrBlack);
public:
void draw_line(double value, string name, color clr = clrBlack);
};
// ---------------------------------------------------------------------
// Draws or updates a horizontal line on the chart at the given price level.
//
// Parameters:
// - value : Price level at which to draw the line.
// - name : Unique name for the line object.
// - clr : Line color (default is black).
//
// Logic:
// - If the object doesn't exist, it creates a new horizontal line.
// - If the object exists, it moves it to the new price level.
// - Calls ChartRedraw to update the chart visually.
// ---------------------------------------------------------------------
void ChartUtils::draw_line(double value, string name, color clr) {
if (ObjectFind(0, name) < 0) {
ResetLastError();
if (ObjectFind(0, name) < 0) {
ResetLastError();
if (!ObjectCreate(0, name, OBJ_HLINE, 0, 0, value)) {
Print(__FUNCTION__, ": failed to create a horizontal line! Error code = ", GetLastError());
return;
}
if (!ObjectCreate(0, name, OBJ_HLINE, 0, 0, value)) {
Print(__FUNCTION__, ": failed to create a horizontal line! Error code = ", GetLastError());
return;
}
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
}
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
}
ResetLastError();
ResetLastError();
if (!ObjectMove(0, name, 0, 0, value)) {
Print(__FUNCTION__, ": failed to move the horizontal line! Error code = ", GetLastError());
return;
}
if (!ObjectMove(0, name, 0, 0, value)) {
Print(__FUNCTION__, ": failed to move the horizontal line! Error code = ", GetLastError());
return;
}
ChartRedraw();
ChartRedraw();
}
-28
View File
@@ -1,28 +0,0 @@
#include <MyLibs/Myfunctions.mqh>
#include <MyLibs/OrderManagement.mqh>
#include <MyLibs/Utils/MyEnums.mqh>
#include <MyLibs/BacktestUtils/CustomMax.mqh>
#include <MyLibs/BacktestUtils/TestDataSplit.mqh>
CustomMax c_max;
// MyFunctions mf;
// OrderManagment om;
//---
input LOT_MODE inp_lot_mode = LOT_MODE_PCT_RISK; // Lot Size Mode
input double inp_lot_var = 2; // Lot Size Var
input SL_MODE inp_sl_mode = SL_ATR_MULTIPLE; // Stop-loss Mode
input double inp_sl_var = 1.5; // Stop-loss Var
input TP_MODE inp_tp_mode = TP_ATR_MULTIPLE; // Take-profit Mode
input double inp_tp_var = 1; // Take-Profit Var
string lot_mode = EnumToString(inp_lot_mode);
string sl_mode = EnumToString(inp_sl_mode);
string tp_mode = EnumToString(inp_tp_mode);
input CUSTOM_MAX_TYPE inp_custom_criteria = CM_WIN_PERCENT;
input int inp_opt_min_trades = 0; // 0/off
input MODE_SPLIT_DATA inp_data_split_method = NO_SPLIT;
input int inp_force_opt = 1;
input group "-----------------------------------------"
+120 -66
View File
@@ -1,94 +1,148 @@
class MarketDataUtils {
public:
bool is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time = "00:10");
double get_latest_buffer_value(int handle);
double get_buffer_value(int handle, int shift);
double adjusted_point(string symbol);
double get_bid_ask_price(string symbol, int price_side);
public:
bool is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time = "00:10");
double get_latest_buffer_value(int handle);
double get_buffer_value(int handle, int shift);
double adjusted_point(string symbol);
double get_bid_ask_price(string symbol, int price_side);
protected:
datetime previousTimes[]; // Stores last recorded open time per key
string bar_keys[]; // Keys are symbol+TF combinations, e.g. "EURUSD_PERIOD_H1"
protected:
datetime previousTimes[]; // Stores last recorded open time per key
string bar_keys[]; // Keys are symbol+TF combinations, e.g. "EURUSD_PERIOD_H1"
};
// Helper function to find index of a key in an array
int LinearSearch(string &arr[], string target) {
for (int i = 0; i < ArraySize(arr); i++) {
if (arr[i] == target)
return i;
}
return -1; // Not found
// ---------------------------------------------------------------------
// Performs linear search on a string array.
//
// Parameters:
// - arr : Array of strings.
// - target : Target string to find.
//
// Returns:
// - Index of the target, or -1 if not found.
// ---------------------------------------------------------------------
int LinearSearch(string& arr[], string target) {
for (int i = 0; i < ArraySize(arr); i++) {
if (arr[i] == target) return i;
}
return -1;
}
// Checks if a new bar has opened on the given timeframe and symbol
// ---------------------------------------------------------------------
// Implementation of is_new_bar. Tracks the open time of the last bar.
//
// Parameters:
// - symbol : Symbol to check.
// - time_frame : Timeframe to check.
// - daily_start_time: Time string for daily bar sync.
//
// Returns:
// - true if a new bar has formed, false otherwise.
// ---------------------------------------------------------------------
bool MarketDataUtils::is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time) {
datetime bar_open_time = iTime(symbol, time_frame, 0); // Current open time
string key = symbol + "_" + EnumToString(time_frame);
datetime bar_open_time = iTime(symbol, time_frame, 0); // Current open time
string key = symbol + "_" + EnumToString(time_frame);
int idx = LinearSearch(bar_keys, key);
if (idx == -1) {
int new_size = ArraySize(bar_keys) + 1;
ArrayResize(bar_keys, new_size);
ArrayResize(previousTimes, new_size);
int idx = LinearSearch(bar_keys, key);
if (idx == -1) {
int new_size = ArraySize(bar_keys) + 1;
ArrayResize(bar_keys, new_size);
ArrayResize(previousTimes, new_size);
idx = new_size - 1;
bar_keys[idx] = key;
previousTimes[idx] = 0;
}
idx = new_size - 1;
bar_keys[idx] = key;
previousTimes[idx] = 0;
}
if (previousTimes[idx] != bar_open_time) {
// For daily timeframe, wait for specific time (e.g., 00:10) before triggering
if (PeriodSeconds(time_frame) == PeriodSeconds(PERIOD_D1)) {
if (TimeCurrent() > StringToTime(daily_start_time)) {
if (previousTimes[idx] != bar_open_time) {
if (PeriodSeconds(time_frame) == PeriodSeconds(PERIOD_D1)) {
if (TimeCurrent() > StringToTime(daily_start_time)) {
previousTimes[idx] = bar_open_time;
return true;
}
} else {
previousTimes[idx] = bar_open_time;
return true;
}
} else {
previousTimes[idx] = bar_open_time;
return true;
}
}
}
}
return false; // No new bar
return false;
}
// shift = 0 refers to the live candle (still forming)
// shift = 1 is the most recently closed candle
// shift = 2 is the one before that, etc.
// ---------------------------------------------------------------------
// Implementation of get_buffer_value.
//
// Parameters:
// - handle : Indicator handle.
// - shift : Shift index for historical bars.
//
// Returns:
// - The buffer value, or EMPTY_VALUE if error.
// ---------------------------------------------------------------------
double MarketDataUtils::get_buffer_value(int handle, int shift) {
double val[];
ArraySetAsSeries(val, true);
double val[];
ArraySetAsSeries(val, true);
int copied = CopyBuffer(handle, 0, shift, 1, val);
if (copied <= 0) {
Print("CopyBuffer failed: handle=", handle, " shift=", shift);
return EMPTY_VALUE;
}
int copied = CopyBuffer(handle, 0, shift, 1, val);
if (copied <= 0) {
Print("CopyBuffer failed: handle=", handle, " shift=", shift);
return EMPTY_VALUE;
}
if (val[0] == EMPTY_VALUE) {
Print("EMPTY_VALUE returned for buffer at shift=", shift);
return EMPTY_VALUE;
}
if (val[0] == EMPTY_VALUE) {
Print("EMPTY_VALUE returned for buffer at shift=", shift);
return EMPTY_VALUE;
}
return val[0];
return val[0];
}
// Adjusts the point value for symbol to account for fractional pips (e.g., 5-digit brokers)
// ---------------------------------------------------------------------
// Gets the latest (live) value from buffer (shift = 0).
//
// Parameters:
// - handle : Indicator handle.
//
// Returns:
// - Buffer value at shift 0 or EMPTY_VALUE if failed.
// ---------------------------------------------------------------------
double MarketDataUtils::get_latest_buffer_value(int handle) {
return get_buffer_value(handle, 0);
}
// ---------------------------------------------------------------------
// Computes adjusted point value considering fractional pip brokers.
//
// Parameters:
// - symbol : Symbol name.
//
// Returns:
// - Adjusted point multiplier.
// ---------------------------------------------------------------------
double MarketDataUtils::adjusted_point(string symbol) {
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
int digits_adjust = (symbol_digits == 3 || symbol_digits == 5) ? 10 : 1;
double point_val = SymbolInfoDouble(symbol, SYMBOL_POINT);
return point_val * digits_adjust; // Adjusted pip value
int symbol_digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
int digits_adjust = (symbol_digits == 3 || symbol_digits == 5) ? 10 : 1;
double point_val = SymbolInfoDouble(symbol, SYMBOL_POINT);
return point_val * digits_adjust;
}
// Returns current Bid or Ask price for a symbol based on side (1 = Ask, 2 = Bid)
// ---------------------------------------------------------------------
// Returns bid or ask price for a given symbol.
//
// Parameters:
// - symbol : Symbol name.
// - price_side : 1 = Ask, 2 = Bid.
//
// Returns:
// - Price value or 0.0 if input is invalid.
// ---------------------------------------------------------------------
double MarketDataUtils::get_bid_ask_price(string symbol, int price_side) {
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), digits);
double bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), digits);
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), digits);
double bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), digits);
if (price_side == 1) return ask;
if (price_side == 2) return bid;
if (price_side == 1) return ask;
if (price_side == 2) return bid;
return 0.0; // Invalid input
return 0.0;
}
+45 -49
View File
@@ -1,86 +1,82 @@
#include <MyLibs/Utils/SignalStateTracker.mqh>
// ---------------------------------------------------------------------
// This class manages a collection of SignalStateTracker instances — one per symbol.
// It allows you to track signals separately for each symbol in a multi-symbol EA.
// ---------------------------------------------------------------------
class MultiSymbolSignalTracker {
private:
// Struct to hold one (symbol → tracker) mapping
private:
// ---------------------------------------------------------------------
// Internal struct to associate a symbol with a SignalStateTracker.
// ---------------------------------------------------------------------
struct SymbolTracker {
string symbol; // Symbol name (e.g. "EURUSD", "HSI")
SignalStateTracker* tracker; // Pointer to the signal tracker for that symbol
string symbol;
SignalStateTracker* tracker;
};
SymbolTracker trackers[]; // Dynamic array of symbol-tracker pairs
SymbolTracker trackers[]; // Dynamic array of symbol-tracker mappings
// Helper function: Find the index of the given symbol in the array
// ---------------------------------------------------------------------
// Finds the index of the symbol in the tracker array.
// ---------------------------------------------------------------------
int find_index(const string& symbol) {
for (int i = 0; i < ArraySize(trackers); i++) {
if (trackers[i].symbol == symbol) return i; // Found symbol, return its index
if (trackers[i].symbol == symbol)
return i;
}
return -1; // Not found
return -1;
}
public:
// Destructor: automatically called when this object is destroyed (e.g. at EA shutdown)
public:
// ---------------------------------------------------------------------
// Destructor. Cleans up allocated memory when the object is destroyed.
// ---------------------------------------------------------------------
~MultiSymbolSignalTracker() {
clear(); // Clean up memory when done
clear();
}
// Returns the SignalStateTracker for the given symbol.
// If it doesn't exist yet, it creates one and stores it.
// ---------------------------------------------------------------------
// Retrieves the SignalStateTracker instance for a given symbol.
// Creates and stores a new one if it doesn't exist yet.
//
// Parameters:
// - symbol : Symbol for which to retrieve the signal tracker.
//
// Returns:
// - Pointer to the SignalStateTracker instance.
// ---------------------------------------------------------------------
SignalStateTracker* get_tracker(const string& symbol) {
int idx = find_index(symbol);
if (idx != -1) return trackers[idx].tracker; // Tracker already exists — return it
if (idx != -1)
return trackers[idx].tracker;
// If not found, create a new tracker for this symbol
// Create new tracker
SignalStateTracker* tracker = new SignalStateTracker();
SymbolTracker item;
item.symbol = symbol;
item.tracker = tracker;
// Add new item to dynamic array
ArrayResize(trackers, ArraySize(trackers) + 1);
trackers[ArraySize(trackers) - 1] = item;
return tracker;
}
// Deletes all trackers and resets the array.
// Should be called in OnDeinit() to free memory.
// ---------------------------------------------------------------------
// Clears all SignalStateTracker instances and resets the internal array.
// Should be called in `OnDeinit()` to avoid memory leaks.
//
// Logic:
// - Deletes each dynamically allocated SignalStateTracker.
// - Resets array size to 0.
// ---------------------------------------------------------------------
void clear() {
for (int i = 0; i < ArraySize(trackers); i++) {
delete trackers[i].tracker; // Manually free each dynamically created tracker
delete trackers[i].tracker;
}
ArrayResize(trackers, 0); // Reset array to empty
ArrayResize(trackers, 0);
}
};
/* ---------------------------------------------------------------------------
Example usage in an EA:
---------------------------------------------------------------------------
// Declare the tracker globally (outside OnTick/OnTimer)
MultiSymbolSignalTracker track_trigger;
// Inside your strategy() or OnTick():
void strategy(string symbol, ...) {
bool trig_long = ...; // Your signal logic
bool trig_short = ...;
// Update the signal tracker for this symbol
track_trigger.get_tracker(symbol).update_signal(trig_long, trig_short);
// Example: Check if a recent long signal occurred
if (track_trigger.get_tracker(symbol).long_signal_recent(5)) {
// Do something like open a trade
}
}
// In OnDeinit():
void OnDeinit(const int reason) {
track_trigger.clear(); // Clean up memory
}
*/
+36 -18
View File
@@ -1,44 +1,61 @@
#include <MyLibs/Utils/AtrHandleManager.mqh>
//+------------------------------------------------------------------+
//| ResourceManager |
//| |
//| Tracks and releases indicator handles (iMA, iRSI, etc.) |
//| Also delegates ATR handle cleanup to an external ATR manager |
//+------------------------------------------------------------------+
// ---------------------------------------------------------------------
// ResourceManager
//
// Tracks and releases indicator handles (e.g., iMA, iRSI, iCCI).
// Delegates ATR handle management to an external AtrHandleManager instance.
//
// Usage:
// - Register indicator handles via `register_handle()`.
// - Call `release_all_handles()` to release all cached handles.
// ---------------------------------------------------------------------
class ResourceManager {
public:
// --- Pointer to external ATR manager (set externally)
// Used for releasing any internally cached ATR handles
AtrHandleManager* atr_manager;
// --- Register a new indicator handle to be released later
// Only valid (non-INVALID_HANDLE) handles are stored
// ---------------------------------------------------------------------
// Registers a generic indicator handle to be released later.
//
// Parameters:
// - handle : A valid (non-INVALID_HANDLE) indicator handle.
//
// Logic:
// - If the handle is valid, it is added to an internal list.
// ---------------------------------------------------------------------
void register_handle(int handle) {
if (handle != INVALID_HANDLE)
add_handle(handle);
}
// --- Release all tracked resources:
// ---------------------------------------------------------------------
// Releases all tracked indicator resources.
//
// Logic:
// - Releases generic handles tracked via `register_handle()`.
// - Also invokes `atr_manager.release_handles()` if assigned.
// ---------------------------------------------------------------------
void release_all_handles() {
release_internal_handles(); // ATR manager
release_tracked_handles(); // Generic indicator handles
}
private:
// --- Dynamic array of general indicator handles
int handles[];
// --- Append a valid indicator handle to the internal array
// Used by register_handle()
// ---------------------------------------------------------------------
// Adds a handle to the internal tracking list.
// ---------------------------------------------------------------------
void add_handle(int handle) {
int size = ArraySize(handles);
ArrayResize(handles, size + 1);
handles[size] = handle;
}
// --- Release all generic indicator handles tracked internally
// This covers iMA, iRSI, iCCI, etc.
// ---------------------------------------------------------------------
// Releases all tracked indicator handles (iMA, iRSI, etc.).
// ---------------------------------------------------------------------
void release_tracked_handles() {
for (int i = 0; i < ArraySize(handles); i++) {
if (handles[i] != INVALID_HANDLE)
@@ -47,8 +64,9 @@ private:
ArrayFree(handles);
}
// --- Release ATR handles via external AtrHandleManager
// No-op if atr_manager is not assigned
// ---------------------------------------------------------------------
// Releases any cached ATR handles using the external AtrHandleManager.
// ---------------------------------------------------------------------
void release_internal_handles() {
if (atr_manager != NULL)
atr_manager.release_handles();
+77 -14
View File
@@ -1,22 +1,51 @@
class SignalStateTracker {
private:
private:
// ---------------------------------------------------------------------
// Index of the last long signal detected (default -1000 when unset).
// ---------------------------------------------------------------------
int last_signal_long;
// ---------------------------------------------------------------------
// Index of the last short signal detected (default -1000 when unset).
// ---------------------------------------------------------------------
int last_signal_short;
public:
// Constructor initializes both directions to a default "unset" state.
public:
// ---------------------------------------------------------------------
// Constructor initializes the signal tracker to a reset state.
//
// Logic:
// - Sets both long and short signal indices to -1000.
// ---------------------------------------------------------------------
SignalStateTracker() {
reset();
}
// Resets the tracked signal bar indexes to an invalid default value (-1000).
// ---------------------------------------------------------------------
// Resets the signal tracker.
//
// Logic:
// - Sets `last_signal_long` and `last_signal_short` to -1000,
// representing no signal recorded.
// ---------------------------------------------------------------------
void reset() {
last_signal_long = -1000;
last_signal_short = -1000;
}
// Updates the signal tracker based on trigger presence per bar.
// If signal is detected, sets to 1. If not, increments previous value.
// ---------------------------------------------------------------------
// Updates internal state based on whether long/short signals occurred.
//
// Parameters:
// - signal_long : True if a long signal occurred this bar.
// - signal_short : True if a short signal occurred this bar.
//
// Logic:
// - If a signal is detected, sets the index to 1 (bar 1).
// - Otherwise, increments the previous value if it was positive.
// ---------------------------------------------------------------------
void update_signal_tracker(bool signal_long, bool signal_short) {
if (signal_long) {
last_signal_long = 1;
@@ -31,34 +60,68 @@ class SignalStateTracker {
}
}
// Returns true if a long signal occurred within the last `max_bars` bars.
// ---------------------------------------------------------------------
// Checks if a long signal occurred within the last N bars.
//
// Parameters:
// - max_bars : Number of bars to look back for the signal.
//
// Returns:
// - True if a long signal occurred within `max_bars` bars.
// ---------------------------------------------------------------------
bool long_signal_recent(int max_bars) const {
// Assumes current bar is always bar index 1 (last closed bar).
return has_long_signal() && (1 - last_signal_long <= max_bars);
}
// Returns true if a short signal occurred within the last `max_bars` bars.
// ---------------------------------------------------------------------
// Checks if a short signal occurred within the last N bars.
//
// Parameters:
// - max_bars : Number of bars to look back for the signal.
//
// Returns:
// - True if a short signal occurred within `max_bars` bars.
// ---------------------------------------------------------------------
bool short_signal_recent(int max_bars) const {
// Assumes current bar is always bar index 1 (last closed bar).
return has_short_signal() && (1 - last_signal_short <= max_bars);
}
// Returns true if a signal has been recorded for the long direction.
// ---------------------------------------------------------------------
// Indicates if any long signal has ever been recorded.
//
// Returns:
// - True if a long signal index is not equal to -1000.
// ---------------------------------------------------------------------
bool has_long_signal() const {
return last_signal_long != -1000;
}
// Returns true if a signal has been recorded for the short direction.
// ---------------------------------------------------------------------
// Indicates if any short signal has ever been recorded.
//
// Returns:
// - True if a short signal index is not equal to -1000.
// ---------------------------------------------------------------------
bool has_short_signal() const {
return last_signal_short != -1000;
}
// Returns the last recorded signal bar index for the long direction.
// ---------------------------------------------------------------------
// Returns the last bar index at which a long signal occurred.
//
// Returns:
// - Integer index representing bars since long signal.
// ---------------------------------------------------------------------
int get_long_signal() const {
return last_signal_long;
}
// Returns the last recorded signal bar index for the short direction.
// ---------------------------------------------------------------------
// Returns the last bar index at which a short signal occurred.
//
// Returns:
// - Integer index representing bars since short signal.
// ---------------------------------------------------------------------
int get_short_signal() const {
return last_signal_short;
}
+150 -118
View File
@@ -1,144 +1,176 @@
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/Utils/DealingWithTime.mqh>
#include <Trade/Trade.mqh>
class TimeZones: public CObject{
protected:
string dt_s;
int len;
string dt_string;
datetime tC, tGMT, tNY, tLon, tFfm, tMosc, tSyd, tTok;
datetime tz_time;
string tz_date;
datetime time_start;
datetime time_end;
bool is_time;
datetime tGIVEN;
datetime tREQ;
datetime tzt;
datetime tz_req;
double ny_daily_close_protected(string symbol, int shift_days, bool print_data=false);
double required_close;
class TimeZones : public CObject {
protected:
string dt_s;
int len;
string dt_string;
datetime tC, tGMT, tNY, tLon, tFfm, tMosc, tSyd, tTok;
datetime tz_time;
string tz_date;
datetime time_start;
datetime time_end;
bool is_time;
datetime tGIVEN;
datetime tREQ;
datetime tzt;
datetime tz_req;
double required_close;
public:
string get_date_string_from_datetime(datetime dt);
datetime get_timezone_time(string time_zone, bool print_time);
datetime timezone_conversions(string time_zone_known, datetime time_given, string time_zone_required);
double ny_daily_close(string symbol, int shift_days, bool print_data=false);
double ny_daily_close_protected(string symbol, int shift_days, bool print_data = false);
public:
string get_date_string_from_datetime(datetime dt);
datetime get_timezone_time(string time_zone, bool print_time);
datetime timezone_conversions(string time_zone_known, datetime time_given, string time_zone_required);
double ny_daily_close(string symbol, int shift_days, bool print_data = false);
};
string TimeZones::get_date_string_from_datetime(datetime dt){
dt_s = TimeToString(dt);
len = StringLen(dt_s);
dt_string = StringSubstr(dt_s, 0, len-5);
return dt_string;
// ---------------------------------------------------------------------
// Converts a datetime to a string excluding seconds.
//
// Parameters:
// - dt : Datetime object.
//
// Returns:
// - A string in the format "yyyy.mm.dd hh:mi".
// ---------------------------------------------------------------------
string TimeZones::get_date_string_from_datetime(datetime dt) {
dt_s = TimeToString(dt);
len = StringLen(dt_s);
dt_string = StringSubstr(dt_s, 0, len - 5);
return dt_string;
}
// ---------------------------------------------------------------------
// Gets the current time in the specified time zone.
//
// Parameters:
// - time_zone : One of "NY", "Lon", "Ffm", "Syd", "Mosc", "Tok".
// - print_time : If true, logs various times for debugging.
//
// Returns:
// - Current time in the specified time zone.
// ---------------------------------------------------------------------
datetime TimeZones::get_timezone_time(string time_zone, bool print_time) {
checkTimeOffset(TimeCurrent()); // Adjust DST
datetime TimeZones::get_timezone_time(string time_zone, bool print_time){
// https://www.mql5.com/en/code/45287
// https://www.mql5.com/en/articles/9926
// https://www.mql5.com/en/articles/9929
tC = TimeCurrent();
tGMT = TimeCurrent() + OffsetBroker.actOffset;
tNY = tGMT - (NYShift + DST_USD);
tLon = tGMT - (LondonShift + DST_EUR);
tFfm = tGMT - (FfmShift + DST_EUR);
tSyd = tGMT - (SidneyShift + DST_AUD);
tMosc = tGMT - (MoskwaShift + DST_RUS);
tTok = tGMT - (TokyoShift);
checkTimeOffset(TimeCurrent()); // check changes of DST
// cto();
if (print_time) {
Print("----------------------------------");
Print("Broker: ", tC);
Print("GMT: ", tGMT);
Print("time in New York: ", tNY);
Print("time in London: ", tLon);
Print("time in Frankfurt: ", tFfm);
Print("time in Sidney: ", tSyd);
Print("time in Moscow: ", tMosc);
Print("time in Tokyo: ", tTok);
}
tC = TimeCurrent();
tGMT = TimeCurrent() + OffsetBroker.actOffset; // GMT
tNY = tGMT - (NYShift+DST_USD); // time in New York (EST)
tLon = tGMT - (LondonShift+DST_EUR); // time in London
tFfm = tGMT - (FfmShift+DST_EUR); // time in Frankfurt
tSyd = tGMT - (SidneyShift+DST_AUD); // time in Sidney
tMosc = tGMT - (MoskwaShift+DST_RUS); // time in Moscow
tTok = tGMT - (TokyoShift); // time in Tokyo - no DST
if (time_zone == "NY") return tNY;
if (time_zone == "Lon") return tLon;
if (time_zone == "Ffm") return tFfm;
if (time_zone == "Syd") return tSyd;
if (time_zone == "Mosc") return tMosc;
if (time_zone == "Tok") return tTok;
if(print_time==true){
Print("----------------------------------");
Print("Broker: ", tC);
Print("GMT: ", tGMT);
Print("time in New York: ", tNY);
Print("time in London: ", tLon);
Print("time in Frankfurt: ", tFfm);
Print("time in Sidney: ", tSyd);
Print("time in Moscow: ", tMosc);
Print("time in Tokyo: ", tTok);
}
if(time_zone=="NY"){return tNY;}
if(time_zone=="Lon"){return tLon;}
if(time_zone=="Ffm"){return tFfm;}
if(time_zone=="Syd"){return tSyd;}
if(time_zone=="Mosc"){return tMosc;}
if(time_zone=="Tok"){return tTok;}
return NULL;
return NULL;
}
// ---------------------------------------------------------------------
// Converts a datetime from one timezone to another.
//
// Parameters:
// - time_zone_known : Original timezone of the datetime.
// - time_given : The datetime to convert.
// - time_zone_required : Desired output timezone.
//
// Returns:
// - The equivalent datetime in the target timezone.
// ---------------------------------------------------------------------
datetime TimeZones::timezone_conversions(string time_zone_known, datetime time_given, string time_zone_required) {
tGIVEN = time_given;
checkTimeOffset(tGIVEN); // Adjust DST
datetime TimeZones::timezone_conversions(string time_zone_known, datetime time_given, string time_zone_required){
// https://www.mql5.com/en/code/45287
// https://www.mql5.com/en/articles/9926
// https://www.mql5.com/en/articles/9929
// Step 1: Convert known timezone to GMT
if (time_zone_known == "GMT") tGMT = tGIVEN;
if (time_zone_known == "Broker") tGMT = tGIVEN + OffsetBroker.actOffset;
if (time_zone_known == "NY") tGMT = tGIVEN + (NYShift + DST_USD);
if (time_zone_known == "Lon") tGMT = tGIVEN + (LondonShift + DST_EUR);
if (time_zone_known == "Ffm") tGMT = tGIVEN + (FfmShift + DST_EUR);
if (time_zone_known == "Syd") tGMT = tGIVEN + (SidneyShift + DST_AUD);
if (time_zone_known == "Mosc") tGMT = tGIVEN + (MoskwaShift + DST_RUS);
if (time_zone_known == "Tok") tGMT = tGIVEN + (TokyoShift);
tGIVEN = time_given; //StringToTime(time_given);
checkTimeOffset(tGIVEN); // check changes of DST
// Step 2: Convert GMT to required timezone
if (time_zone_required == "GMT") tREQ = tGMT;
if (time_zone_required == "Broker") tREQ = tGMT - OffsetBroker.actOffset;
if (time_zone_required == "NY") tREQ = tGMT - (NYShift + DST_USD);
if (time_zone_required == "Lon") tREQ = tGMT - (LondonShift + DST_EUR);
if (time_zone_required == "Ffm") tREQ = tGMT - (FfmShift + DST_EUR);
if (time_zone_required == "Syd") tREQ = tGMT - (SidneyShift + DST_AUD);
if (time_zone_required == "Mosc") tREQ = tGMT - (MoskwaShift + DST_RUS);
if (time_zone_required == "Tok") tREQ = tGMT - (TokyoShift);
// Get GMT:
if(time_zone_known=="GMT" ){tGMT = tGIVEN;}
if(time_zone_known=="Broker" ){tGMT = tGIVEN + OffsetBroker.actOffset;}
if(time_zone_known=="NY" ){tGMT = tGIVEN + (NYShift+DST_USD);}
if(time_zone_known=="Lon" ){tGMT = tGIVEN + (LondonShift+DST_EUR);}
if(time_zone_known=="Ffm" ){tGMT = tGIVEN + (FfmShift+DST_EUR);}
if(time_zone_known=="Syd" ){tGMT = tGIVEN + (SidneyShift+DST_AUD);}
if(time_zone_known=="Mosc" ){tGMT = tGIVEN + (MoskwaShift+DST_RUS);}
if(time_zone_known=="Tok" ){tGMT = tGIVEN + (TokyoShift);}
// define the required time:
tREQ = NULL;
if(time_zone_required=="GMT" ){tREQ = tGMT;}
if(time_zone_required=="Broker" ){tREQ = tGMT - OffsetBroker.actOffset;}
if(time_zone_required=="NY" ){tREQ = tGMT - (NYShift+DST_USD);}
if(time_zone_required=="Lon" ){tREQ = tGMT - (LondonShift+DST_EUR);}
if(time_zone_required=="Ffm" ){tREQ = tGMT - (FfmShift+DST_EUR);}
if(time_zone_required=="Syd" ){tREQ = tGMT - (SidneyShift+DST_AUD) ;}
if(time_zone_required=="Mosc" ){tREQ = tGMT - (MoskwaShift+DST_RUS);}
if(time_zone_required=="Tok" ){tREQ = tGMT - (TokyoShift);}
return tREQ;
return tREQ;
}
// Calculte NY close time:
double TimeZones::ny_daily_close(string symbol, int shift_days, bool print_data=false){
required_close = ny_daily_close_protected(symbol, shift_days, print_data);
return required_close;
// ---------------------------------------------------------------------
// Returns the most recent NY daily close price.
//
// Parameters:
// - symbol : Trading symbol.
// - shift_days : How many NY daily closes back to return.
// - print_data : If true, logs debug information.
//
// Returns:
// - The NY close price.
// ---------------------------------------------------------------------
double TimeZones::ny_daily_close(string symbol, int shift_days, bool print_data) {
required_close = ny_daily_close_protected(symbol, shift_days, print_data);
return required_close;
}
double TimeZones::ny_daily_close_protected(string symbol, int shift_days, bool print_data=false){
// Get the brokers times for when NY openend today and tomorrow:
datetime time_5pm = iTime(symbol, PERIOD_D1 , 0) - (PeriodSeconds(PERIOD_H1) * 7);
datetime ny_close_in_brokers_time = timezone_conversions("NY", time_5pm, "Broker");
datetime ny_close_time = ny_close_in_brokers_time + PeriodSeconds(PERIOD_D1); // ny close tomorrow
// ---------------------------------------------------------------------
// Internal implementation to compute NY daily close price.
//
// Logic:
// - Defines NY close as 5pm NY time = 00:00 broker + 17H back.
// - Adjusts for day shifts if required.
// - Returns the close price of the NY daily session.
// ---------------------------------------------------------------------
double TimeZones::ny_daily_close_protected(string symbol, int shift_days, bool print_data) {
datetime time_5pm = iTime(symbol, PERIOD_D1, 0) - (PeriodSeconds(PERIOD_H1) * 7);
datetime ny_close_in_brokers_time = timezone_conversions("NY", time_5pm, "Broker");
datetime ny_close_time = ny_close_in_brokers_time + PeriodSeconds(PERIOD_D1);
if(TimeCurrent()<ny_close_time){
ny_close_time = ny_close_time - PeriodSeconds(PERIOD_D1); // ny close today
}
if (TimeCurrent() < ny_close_time) ny_close_time -= PeriodSeconds(PERIOD_D1);
// Get the number of hours since NY closed:
int shift = iBarShift(symbol, PERIOD_H1, ny_close_time, false) + 1;
shift = shift + (24 * (shift_days - 1)); // shift days if required:
int shift = iBarShift(symbol, PERIOD_H1, ny_close_time, false) + 1;
shift += (24 * (shift_days - 1));
double ny_close = iClose(symbol,PERIOD_H1, shift);
double br_close = iClose(symbol,PERIOD_H1, 1);
double ny_close = iClose(symbol, PERIOD_H1, shift);
double br_close = iClose(symbol, PERIOD_H1, 1);
if(print_data==true){
Print("shift ",shift);
Print("time_5pm ",time_5pm);
Print("ny_close_in_brokers_time ",ny_close_in_brokers_time);
Print("ny_close_time ",ny_close_time);
Print("ny_close ", ny_close);
Print("br_close ",br_close);
}
return ny_close;
if (print_data) {
Print("shift ", shift);
Print("time_5pm ", time_5pm);
Print("ny_close_in_brokers_time ", ny_close_in_brokers_time);
Print("ny_close_time ", ny_close_time);
Print("ny_close ", ny_close);
Print("br_close ", br_close);
}
return ny_close;
}
+17
View File
@@ -12,6 +12,23 @@ public:
bool trade_window(string t1, string t2, string time_zone = "Broker", bool plot_range_inp = true);
};
// ---------------------------------------------------------------------
// Determines whether the current time is inside a defined trade session.
//
// Parameters:
// - t1 : Start time string (e.g., "22:00").
// - t2 : End time string (e.g., "01:00").
// - time_zone : The timezone of input times (default = "Broker").
// - plot_range_inp : If true, draws vertical lines for start/end.
//
// Returns:
// - true if current time is within the session window, false otherwise.
//
// Logic:
// - Handles overnight sessions (e.g. 22:0001:00) correctly.
// - Automatically rolls the window to the next day when expired.
// - Uses TimeZones class to convert time to broker timezone.
// ---------------------------------------------------------------------
bool TradeSessionUtils::trade_window(string t1, string t2, string time_zone, bool plot_range_inp) {
datetime _t1 = StringToTime(t1); // Convert string to datetime
datetime _t2 = StringToTime(t2); // Convert string to datetime
-35
View File
@@ -1,35 +0,0 @@
class BarUtils {
protected:
datetime previousTime; // Stores the previous bar open time to detect new bars
datetime bar_open_time; // Current bar open time
public:
bool is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time = "00:10");
};
bool BarUtils::is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time) {
// Get the open time of the current bar
bar_open_time = iTime(symbol, time_frame, 0);
// Check if it's different from the last seen time — this implies a new bar has formed
if (previousTime != bar_open_time) {
// Special logic for daily bars: wait until a specific time-of-day threshold
if (PeriodSeconds(time_frame) == PeriodSeconds(PERIOD_D1)) {
// Don't trigger on midnight, wait until configured daily_start_time (e.g., "00:10")
if (TimeCurrent() > StringToTime(daily_start_time)) {
previousTime = bar_open_time; // Update the marker
return true;
}
} else {
// For all non-daily timeframes, treat any change in bar time as new bar
previousTime = bar_open_time;
return true;
}
}
// No new bar detected
return false;
}
-43
View File
@@ -1,43 +0,0 @@
class BufferUtils {
public:
double get_latest_buffer_value(int handle);
double get_buffer_value(int handle, int shift);
};
// ---- IMPLEMENTATION BELOW ----
/**
* Get the most recent value from the specified indicator buffer.
*
* param handle: Indicator handle (must be valid and previously created)
* return: Most recent buffer value (shift 0), or 0.0 if retrieval fails
*/
double BufferUtils::get_latest_buffer_value(int handle) {
double val[];
ArraySetAsSeries(val, true);
if (CopyBuffer(handle, 0, 0, 1, val) == 1)
return val[0];
return 0.0;
}
/**
* Get a specific historical value from the specified indicator buffer.
*
* param handle: Indicator handle
* param shift: Bar index (0 = current, 1 = previous, etc.)
* return: Buffer value at shift, or 0.0 if retrieval fails
*/
double BufferUtils::get_buffer_value(int handle, int shift) {
double val[];
ArraySetAsSeries(val, true);
if (CopyBuffer(handle, 0, shift, 1, val) == 1)
return val[0];
return 0.0;
}
-278
View File
@@ -1,278 +0,0 @@
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/TimeZones.mqh>
#include <MyLibs/Myfunctions.mqh>
class CalculatePositionData : public CObject{
protected:
CTrade trade;
TimeZones tz;
CPositionInfo position;
MyFunctions mf;
bool check_lots(double &lots, string symbol);
bool normalise_price(double price, double &normalizedPrice, string symbol);
// double adjusted_point(string symbol);
public:
double calculate_stoploss(string symbol, double price, int order_side, string _sl_mode, double sl_var, ENUM_TIMEFRAMES atr_period);
double calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double tp_var, ENUM_TIMEFRAMES atr_period);
double calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var);
double calculate_trading_cost(string symbol, ulong position_ticket);
};
double CalculatePositionData::calculate_stoploss(string symbol, double price, int order_side, string mode_sl, double sl_var, ENUM_TIMEFRAMES atr_period){
// order_side int must be 1 for BUY or 2 for
double sl=0;
if(mode_sl=="NO_STOPLOSS"){
sl=0;
}
if(mode_sl=="SL_BREAKEVEN"){
// https://www.youtube.com/watch?v=idPulZ3_iR0
Alert("Not implemented yet yet");
}
if(mode_sl=="SL_FIXED_PIPS"){
// pips/poins = https://www.mql5.com/en/forum/187757
double adj_point = mf.adjusted_point(symbol);
if(order_side == 1){
sl = price - sl_var * adj_point;
if(!normalise_price(sl,sl,symbol)){return false;}
}
if(order_side == 2){
sl = price + sl_var * adj_point;
if(!normalise_price(sl,sl,symbol)){return false;}
}
}
if(mode_sl=="SL_FIXED_PERCENT"){
if(order_side == 1){
sl = (-1.0 * sl_var * price / 100.00) + price;
if(!normalise_price(sl,sl,symbol)){return false;}
}
if(order_side == 2){
sl = sl_var * price / 100.00 + price;
if(!normalise_price(sl,sl,symbol)){return false;}
}
}
if(mode_sl=="SL_ATR_MULTIPLE"){
int atr_handle = iATR(symbol,atr_period,14);
double atr[];
ArraySetAsSeries(atr,true);
CopyBuffer(atr_handle,MAIN_LINE,1,1,atr);
if(order_side == 1){
sl = price - (atr[0] * sl_var);
if(!normalise_price(sl,sl,symbol)){return false;}
}
if(order_side == 2){
sl = price + (atr[0] * sl_var);
if(!normalise_price(sl,sl,symbol)){return false;}
}
}
if(mode_sl=="SL_SPECIFIED_VALUE"){
double adj_point = mf.adjusted_point(symbol);
if(order_side == 1){
double pip_50_sl = price - 10 * adj_point;
if(sl_var >= pip_50_sl){
sl = pip_50_sl;
}
else sl = sl_var;
if(!normalise_price(sl,sl,symbol)){return false;}
}
if(order_side == 2){
double pip_50_sl = price + 10 * adj_point;
if(sl_var <= pip_50_sl){
sl = pip_50_sl;
}
else sl = sl_var;
sl = sl = sl_var;
if(!normalise_price(sl,sl,symbol)){return false;}
}
}
return sl;
}
double CalculatePositionData::calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double _tp_var, ENUM_TIMEFRAMES atr_period){
// order_side int must be 1 for BUY or 2 for SELL
double tp=0;
if(mode_tp=="NO_TAKE_PROFIT"){
tp=0;
}
if(mode_tp=="TP_FIXED_PIPS"){
double adj_point = mf.adjusted_point(symbol);
if(order_side == 1){
tp = price + _tp_var * adj_point;
if(!normalise_price(tp,tp,symbol)){return false;}
}
if(order_side == 2){
tp = price - _tp_var * adj_point;
if(!normalise_price(tp,tp,symbol)){return false;}
}
}
if(mode_tp=="TP_FIXED_PERCENT"){
if(order_side == 1){
tp = _tp_var * price / 100.00 + price;
if(!normalise_price(tp,tp,symbol)){return false;}
}
if(order_side == 2){
tp = (-1 * _tp_var * price / 100.00) + price;
if(!normalise_price(tp,tp,symbol)){return false;}
}
}
if(mode_tp=="TP_ATR_MULTIPLE"){
int atr_handle = iATR(symbol,atr_period,14);
double atr[];
ArraySetAsSeries(atr,true);
CopyBuffer(atr_handle,MAIN_LINE,1,1,atr);
if(order_side == 1){
tp = price + (atr[0] * _tp_var);
if(!normalise_price(tp,tp,symbol)){return false;}
}
if(order_side == 2){
tp = price - (atr[0] * _tp_var);
if(!normalise_price(tp,tp,symbol)){return false;}
}
}
if(mode_tp=="TP_SL_MULTIPLE"){
if(order_side == 1){
double sl_size = price - stoploss;
tp = price + (_tp_var * sl_size);
if(!normalise_price(tp,tp,symbol)){return false;}
}
if(order_side == 2){
double sl_size = stoploss - price;
tp = price - (_tp_var * sl_size);
if(!normalise_price(tp,tp,symbol)){return false;}
}
}
if(mode_tp=="TP_SPECIFIED_VALUE"){
if(_tp_var!=0){
double adj_point = mf.adjusted_point(symbol);
if(order_side == 1){
double pip_limit = price + 10 * adj_point;
if(_tp_var <= pip_limit){
tp = pip_limit;
}
else tp = _tp_var;
if(!normalise_price(tp,tp,symbol)){return false;}
}
if(order_side == 2){
double pip_limit = price - 10 * adj_point;
if(_tp_var >= pip_limit){
tp = pip_limit;
}
else tp = _tp_var;
tp = tp = _tp_var;
if(!normalise_price(tp,tp,symbol)){return false;}
}
}
}
return tp;
}
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;
if(mode_lot=="LOT_MODE_FIXED"){
lots = lot_var;
}
if(mode_lot=="LOT_MODE_PCT_RISK"){
double money_lot_step = (sl_distance / tick_size) * tick_value * volume_step;
lots = MathFloor(risk_money/money_lot_step) * volume_step;
}
if(mode_lot=="LOT_MODE_PCT_ACCOUNT"){
double money_lot_step = (price / tick_size) * tick_value * volume_step;
lots = MathFloor(risk_money/money_lot_step) * volume_step;
}
if(!check_lots(lots, symbol)){return false;}
return lots;
}
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){
Print("Lot size will be set to minimum allowed volume");
lots = min;
return true;
}
if(lots>max){
Print("Lot size greater than maximum allowed volume. lots:",lots,"max:",max);
return false;
}
lots = (int)MathFloor(lots/step) * step;
return true;
}
bool CalculatePositionData::normalise_price(double price, double &normalizedPrice, string symbol){
double tickSize;
if(!SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE,tickSize)){
Print("Failed to get tick size");
return false;
}
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
normalizedPrice = NormalizeDouble(MathRound(price/tickSize)*tickSize, symbol_digits);
return true;
}
double CalculatePositionData::calculate_trading_cost(string symbol, ulong position_ticket){
position.SelectByTicket(position_ticket);
double swap = PositionGetDouble(POSITION_SWAP);
double commission = PositionGetDouble(POSITION_COMMISSION);
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double lot_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
double lots = PositionGetDouble(POSITION_VOLUME);
double trading_cost = -1 * ((commission + swap) / tick_value * tick_size / lots);
return trading_cost;
}
-211
View File
@@ -1,211 +0,0 @@
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/TradingWindow.mqh>
#include <MyLibs/MyEnums.mqh>
class MyFunctions : public CObject{
protected:
CTrade trade;
TradingWindow tw;
datetime previousTime;
datetime bar_open_time;
public:
void draw_line(double value, string name,color clr);
bool check_indicator_handles(int &indicator_handles[]);
double adjusted_point(string symbol);
double get_bid_ask_price(string symbol, int price_side);
bool is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time="00:10");
bool trade_window(string t1, string t2, string time_zone, bool plot_range_inp=true);
bool in_test_period(MODE_SPLIT_DATA data_period);
void get_white_list(MULTI_SYM_MODE mode, string& DataArray[]);
};
void MyFunctions::get_white_list(MULTI_SYM_MODE mode, string& DataArray[]){
if(mode==MULTI_SYM_CHART){
string a[] = {_Symbol};
ArrayResize(DataArray, ArraySize(a));
for(int i = 0; i < ArraySize(DataArray); i++){
DataArray[i]=a[i];
}
}
if(mode==MULTI_SYM_FX_B5){
string a[] = {"EURUSD", "AUDNZD", "EURGBP", "AUDCAD", "CHFJPY"};
ArrayResize(DataArray, ArraySize(a));
for(int i = 0; i < ArraySize(DataArray); i++){
DataArray[i]=a[i];
}
}
if(mode==MULTI_SYM_FX_28){
string a[] = {"EURUSD","AUDNZD","AUDUSD","AUDJPY","EURCHF","EURGBP","EURJPY","GBPCHF","GBPJPY","GBPUSD","NZDUSD","USDCAD","USDCHF","USDJPY","CADJPY","EURAUD","CHFJPY","EURCAD","AUDCAD","AUDCHF","CADCHF","EURNZD","GBPAUD","GBPCAD","GBPNZD","NZDCAD","NZDCHF","NZDJPY",};
ArrayResize(DataArray, ArraySize(a));
for(int i = 0; i < ArraySize(DataArray); i++){
DataArray[i]=a[i];
}
}
}
bool MyFunctions::trade_window(string t1, string t2, string time_zone="Broker", bool plot_range_inp=true){
bool in_window = tw.define_window(t1, t2, time_zone, plot_range_inp);
return in_window;
}
//if(!mf.is_new_bar(symbol, PERIOD_D1, "00:06")){return;}
bool MyFunctions::is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time="00:10"){
bar_open_time = iTime(symbol, time_frame, 0);
if(previousTime!=bar_open_time){
if(PeriodSeconds(time_frame)==PeriodSeconds(PERIOD_D1)){
if(TimeCurrent() > StringToTime(daily_start_time)){
previousTime=bar_open_time;
return true;
}
}
else{
previousTime=bar_open_time;
return true;
}
}
return false;
}
//if(!mf.in_test_period(data_split_method){return;}
bool MyFunctions::in_test_period(MODE_SPLIT_DATA data_split_method){
string result[];
string string_tc = TimeToString(TimeCurrent());
ushort u_sep = StringGetCharacter(".",0);
int split_string = StringSplit(string_tc, u_sep, result);
bool odd_year = int(result[0]) % 2;
bool odd_month = int(result[1]) % 2;
// get week of the year. rough estimate can be late the first week of jan:
MqlDateTime dt;
TimeToStruct(TimeCurrent(),dt);
int iDay = (dt.day_of_week + 6 ) % 7 + 1; // convert day to standard index (1=Mon,...,7=Sun)
int iWeek = (dt.day_of_year - iDay + 10 ) / 7; // calculate standard week number
bool odd_week = iWeek % 2;
if(data_split_method==NO_SPLIT){
return true;
}
if(data_split_method==ODD_YEARS){
if (odd_year){
return true;
}
}
if(data_split_method==EVEN_YEARS){
if (!odd_year){
return true;
}
}
if(data_split_method==ODD_MONTHS){
if (odd_month){
return true;
}
}
if(data_split_method==EVEN_MONTHS){
if (!odd_month){
return true;
}
}
if(data_split_method==ODD_WEEKS){
if (odd_week){
return true;
}
}
if(data_split_method==EVEN_WEEKS){
if (!odd_week){
return true;
}
}
return false;
}
void MyFunctions::draw_line(double value, string name,color clr=clrBlack){
// EG:
// ArrayResize(bar,1000);
// ArraySetAsSeries(bar, true);
// CopyRates(symbol,PERIOD_CURRENT,1,1000,bar);
// double close = bar[0].close;
// draw_line(close,"CLOSE",clrBlue);
if(ObjectFind(0,name)<0){
ResetLastError();
if(!ObjectCreate(0,name,OBJ_HLINE,0,0,value)){
Print(__FUNCTION__,": failed to create a horizontal line! Error code = ",GetLastError());
return;
}
ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
}
ResetLastError();
if(!ObjectMove(0,name,0,0,value)){
Print(__FUNCTION__,": failed to move the horizontal line! Error code = ",GetLastError());
return;
}
ChartRedraw();
}
double MyFunctions::adjusted_point(string symbol){
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
int digits_adjust=1;
if(symbol_digits==3 || symbol_digits==5){
digits_adjust=10;
}
double symbol_point_val = SymbolInfoDouble(symbol,SYMBOL_POINT);
double m_adjusted_point;
m_adjusted_point = symbol_point_val * digits_adjust;
return m_adjusted_point;
}
// price side - 1 for the ask price and 2 for the bid price
double MyFunctions::get_bid_ask_price(string symbol, int price_side){
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
ask = NormalizeDouble(ask, symbol_digits);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
bid = NormalizeDouble(bid, symbol_digits);
double price = 0;
if(price_side==1){
price = ask;
}
else if(price_side==2){
price = bid;
}
return price;
}
-556
View File
@@ -1,556 +0,0 @@
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/TimeZones.mqh>
#include <MyLibs/MyEnums.mqh>
#include <MyLibs/CalculatePositionData.mqh>
#include <Trade/PositionInfo.mqh>
#include <Trade/OrderInfo.mqh>
#include <MyLibs/Myfunctions.mqh>
class OrderManagment : public CObject{
protected:
CTrade trade;
TimeZones tz;
CalculatePositionData cpd;
CPositionInfo m_position;
COrderInfo m_order;
double stop_loss;
double take_profit;
ulong posTicket;
int time_difference;
int total_open_buy_orders;
int total_open_sell_orders;
double current_price;
int total_pos;
long position_open_time;
long first_allowed_close_time;
datetime current_bar_open_time;
public:
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_nnfx_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_nnfx_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 experation, 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 experation, 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 close_buy_orders(string symbol, bool buy_out, int close_bars, ENUM_TIMEFRAMES close_bar_period, long magic_number);
bool close_sell_orders(string symbol, bool sell_out, int close_bars, ENUM_TIMEFRAMES close_bar_period, long magic_number);
bool first_profitable_close_exit(string symbol, 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 tz, int delay_days, long magic_number);
int count_all_positions(string symbol, long magic_number);
int count_pending_orders(string symbol, ENUM_ORDER_TYPE pendingType, long magic);
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);
int count_open_positions(string symbol,int order_side, long magic_number);
void break_even_stop(string symbol, ulong magic_number, int be_trigger_points, int be_puffer);
void nnfx_trailing_stop(string symbol, double sl_var, double tp_var, double atr_value, ulong magic_number);
};
bool OrderManagment::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 == true){
current_price = SymbolInfoDouble(symbol, SYMBOL_ASK); // ask for buy side
total_open_buy_orders = count_open_positions(symbol, 1, magic_number);
if(total_open_buy_orders == 0){
stop_loss = cpd.calculate_stoploss(symbol, current_price, 1, _sl_mode, sl_var, atr_period);
take_profit = cpd.calculate_take_profit(symbol, current_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
double sl_distance = current_price-stop_loss;
double lots = cpd.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
trade.SetExpertMagicNumber(magic_number);
string comment = "Magic Number: " + IntegerToString(magic_number);
trade.PositionOpen(symbol,ORDER_TYPE_BUY,lots,current_price,stop_loss,take_profit,comment);
}
}
return true;
}
bool OrderManagment::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 == true){
// if(!SymbolInfoTick(symbol,currentTick)){Print("FAILED TO GET TICK:", symbol);return false;}
current_price = SymbolInfoDouble(symbol, SYMBOL_BID); // bid for sell side
total_open_sell_orders = count_open_positions(symbol, 2, magic_number);
if(total_open_sell_orders == 0){
stop_loss = cpd.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period);
take_profit = cpd.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
double sl_distance = stop_loss-current_price;
double lots = cpd.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
trade.SetExpertMagicNumber(magic_number);
string comment = "Magic Number: " + IntegerToString(magic_number);
trade.PositionOpen(symbol,ORDER_TYPE_SELL,lots,current_price,stop_loss,take_profit,comment);
}
}
return true;
}
bool OrderManagment::open_nnfx_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 == true){
current_price = SymbolInfoDouble(symbol, SYMBOL_ASK); // ask for buy side
total_open_buy_orders = count_open_positions(symbol, 1, magic_number);
if(total_open_buy_orders == 0){
stop_loss = cpd.calculate_stoploss(symbol, current_price, 1, _sl_mode, sl_var, atr_period);
take_profit = cpd.calculate_take_profit(symbol, current_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
double sl_distance = current_price-stop_loss;
double lots = cpd.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var/2);
trade.SetExpertMagicNumber(magic_number);
string comment = "Magic Number: " + IntegerToString(magic_number);
trade.PositionOpen(symbol,ORDER_TYPE_BUY,lots,current_price,stop_loss,take_profit,comment);
trade.PositionOpen(symbol,ORDER_TYPE_BUY,lots,current_price,stop_loss,0,comment);
}
}
return true;
}
bool OrderManagment::open_nnfx_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 == true){
// if(!SymbolInfoTick(symbol,currentTick)){Print("FAILED TO GET TICK:", symbol);return false;}
current_price = SymbolInfoDouble(symbol, SYMBOL_BID); // bid for sell side
total_open_sell_orders = count_open_positions(symbol, 2, magic_number);
if(total_open_sell_orders == 0){
stop_loss = cpd.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period);
take_profit = cpd.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
double sl_distance = stop_loss-current_price;
double lots = cpd.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
trade.SetExpertMagicNumber(magic_number);
string comment = "Magic Number: " + IntegerToString(magic_number);
trade.PositionOpen(symbol,ORDER_TYPE_SELL,lots,current_price,stop_loss,take_profit,comment);
trade.PositionOpen(symbol,ORDER_TYPE_SELL,lots,current_price,stop_loss,0,comment);
}
}
return true;
}
// some usfull comment here
bool OrderManagment::open_buy_stop_order(string symbol, bool condition, double entry_price, datetime experation, 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 == true){
total_open_buy_orders = count_open_positions(symbol, 1, magic_number);
if(total_open_buy_orders == 0){
stop_loss = cpd.calculate_stoploss(symbol, entry_price, 1, _sl_mode, sl_var, atr_period);
take_profit = cpd.calculate_take_profit(symbol, entry_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
double sl_distance = entry_price-stop_loss;
double lots = cpd.calculate_lots(symbol, sl_distance, entry_price, _lot_mode, lot_var);
trade.SetExpertMagicNumber(magic_number);
string comment = "Magic Number: " + IntegerToString(magic_number);
trade.BuyStop(lots, entry_price, symbol, stop_loss, take_profit, ORDER_TIME_SPECIFIED, experation, comment);
}
}
return true;
}
bool OrderManagment::open_sell_stop_order(string symbol, bool condition, double entry_price, datetime experation, 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 == true){
total_open_sell_orders = count_open_positions(symbol, 2, magic_number);
if(total_open_sell_orders == 0){
stop_loss = cpd.calculate_stoploss(symbol, entry_price, 2, _sl_mode, sl_var, atr_period);
take_profit = cpd.calculate_take_profit(symbol, entry_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
double sl_distance = stop_loss-entry_price;
double lots = cpd.calculate_lots(symbol, sl_distance, entry_price, _lot_mode, lot_var);
trade.SetExpertMagicNumber(magic_number);
string comment = "Magic Number: " + IntegerToString(magic_number);
trade.SellStop(lots, entry_price, symbol, stop_loss, take_profit, ORDER_TIME_SPECIFIED, experation, comment);
}
}
return true;
}
bool OrderManagment::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){
time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){
if(condition){
trade.PositionClose(posTicket);
}
if(close_bars > 0){
if(time_difference >= close_bars){
trade.PositionClose(posTicket);
}
}
}
}
}
return true;
}
bool OrderManagment::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){
time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){
if(condition){trade.PositionClose(posTicket);}
if(close_bars > 0){
if(time_difference >= close_bars){
trade.PositionClose(posTicket);
}
}
}
}
}
return true;
}
// order_side int must be 1 for BUY or 2 for SELL
int OrderManagment::count_open_positions(string symbol,int order_side, long magic_number){
int count = 0;
bool match = (PositionGetInteger(POSITION_MAGIC)==magic_number);
for(int i = PositionsTotal()-1; i >=0; i--){
ulong ticket = PositionGetTicket(i);
if(PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC)==magic_number){
// Count only Buy orders:
if(order_side == 1){
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){
count = count + 1;
}
}
// Count only Sell orders:
if(order_side == 2){
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){
count = count + 1;
}
}
}
}
return count;
}
int OrderManagment::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 = count + 1;
}
}
return count;
}
bool OrderManagment::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){
// datetime broker_close_time = tz.timezone_conversions(cw_tzone, StringToTime(exit_time), "Broker");
if(TimeCurrent()>= exit_time){
if(PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number){
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){
trade.PositionClose(posTicket);
}
// Sell orders:
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){
trade.PositionClose(posTicket);
}
}
}
}
}
}
return true;
}
bool OrderManagment::daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, string exit_time, string cw_tzone, int delay_days, long magic_number){
// om.daily_timed_profit_exit(_Symbol, PERIOD_CURRENT, "16:45", "17:00", "NY", 1, inp_magic);
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){
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); // shift 1 because 0 = live candle.
double trading_cost = cpd.calculate_trading_cost(symbol, posTicket);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){
if(bar_close > (position_open_price + spread + trading_cost)){
trade.PositionClose(posTicket);
}
}
// Sell orders:
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){
if(bar_close < position_open_price - spread - trading_cost){
trade.PositionClose(posTicket);
}
}
}
}
}
}
}
return true;
}
bool OrderManagment::first_profitable_close_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, long magic_number){
// om.first_profitable_close_exit(_Symbol, PERIOD_CURRENT, inp_magic);
position_open_time = PositionGetInteger(POSITION_TIME);
first_allowed_close_time = position_open_time + PeriodSeconds(close_bar_period);
if((int)position_open_time>0){
if(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); // shift 1 because 0 = live candle.
double trading_cost = cpd.calculate_trading_cost(symbol, posTicket);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){
if(bar_close > (position_open_price + spread + trading_cost)){
trade.PositionClose(posTicket);
}
}
// Sell orders:
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){
if(bar_close < position_open_price - spread - trading_cost){
trade.PositionClose(posTicket);
}
}
}
}
}
}
return true;
}
// e.g. int buy_stop_count = om.count_pending_orders(symbol, ORDER_TYPE_BUY_STOP, inp_magic);
// order types: ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_LIMIT, ORDER_TYPE_BUY_STOP, ORDER_TYPE_SELL_STOP
int OrderManagment::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);
}
void OrderManagment::break_even_stop(string symbol, ulong magic_number, int be_trigger_points, int be_puffer){
for(int i = PositionsTotal()-1; i >=0; i--){
if(PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number){
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
ask = NormalizeDouble(ask, symbol_digits);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
bid = NormalizeDouble(bid, symbol_digits);
if(be_trigger_points !=0){
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket)){
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double position_volume = PositionGetDouble(POSITION_VOLUME);
double position_sl = PositionGetDouble(POSITION_SL);
double position_tp = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(position_type == POSITION_TYPE_BUY){
if(bid > position_open_price + be_trigger_points * symbol_point){
double sl = position_open_price + be_puffer * symbol_point;
sl = NormalizeDouble(sl, symbol_digits);
if(sl > position_sl){
if(trade.PositionModify(ticket, sl, position_tp)){
Print("-----------------------------------Stop moved to break even");
}
}
}
}
else if(position_type == POSITION_TYPE_SELL){
if(ask < position_open_price - be_trigger_points * symbol_point){
double sl = position_open_price - be_puffer * symbol_point;
sl = NormalizeDouble(sl, symbol_digits);
if(sl < position_sl){
if(trade.PositionModify(ticket, sl, position_tp)){
Print("-----------------------------------Stop moved to break even");
}
}
}
}
}
}
}
}
}
void OrderManagment::nnfx_trailing_stop(string symbol, double sl_var, double tp_var, double atr_value, ulong magic_number){
MyFunctions mf3;
for(int i = PositionsTotal()-1; i >=0; i--){
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket)){
if(PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number){
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
ask = NormalizeDouble(ask, symbol_digits);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
bid = NormalizeDouble(bid, symbol_digits);
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double position_sl = PositionGetDouble(POSITION_SL);
double position_tp = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(position_type == POSITION_TYPE_BUY){
if(bid > position_open_price + (atr_value * tp_var)){
double sl = bid - (atr_value * sl_var);
sl = NormalizeDouble(sl, symbol_digits);
if(sl > (position_sl + (atr_value * 0.5))){
if(trade.PositionModify(ticket, sl, position_tp)){
}
}
}
}
else if(position_type == POSITION_TYPE_SELL){
if(ask < position_open_price - (atr_value * tp_var)){
double sl = ask + (atr_value * sl_var);
sl = NormalizeDouble(sl, symbol_digits);
if(sl < (position_sl + (atr_value * 0.5))){
if(trade.PositionModify(ticket, sl, position_tp)){
}
}
}
}
}
}
}
}
double OrderManagment::sl_specified_value_switch(string _sl_mode, double _inp_sl_var, double value){
double sl = 0;
if(_sl_mode=="SL_SPECIFIED_VALUE"){sl = value;}
if(_sl_mode!="SL_SPECIFIED_VALUE"){sl = _inp_sl_var;}
return sl;
}
double OrderManagment::tp_specified_value_switch(string _tp_mode, double _inp_tp_var, double value){
double tp = 0;
if(_tp_mode=="SL_SPECIFIED_VALUE"){tp = value;}
if(_tp_mode!="SL_SPECIFIED_VALUE"){tp = _inp_tp_var;}
return tp;
}
-35
View File
@@ -1,35 +0,0 @@
class SymbolUtils {
public:
double adjusted_point(string symbol);
double get_bid_ask_price(string symbol, int price_side);
};
/**
* Adjusts the point size for symbols with 3 or 5 digits (e.g. JPY pairs or fractional pips).
* Example: if symbol has 5 digits, 1 pip = 10 points.
*/
double SymbolUtils::adjusted_point(string symbol) {
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
int digits_adjust = (symbol_digits == 3 || symbol_digits == 5) ? 10 : 1;
double point_val = SymbolInfoDouble(symbol, SYMBOL_POINT);
return point_val * digits_adjust;
}
/**
* Returns either bid or ask price for a symbol, normalised to correct digits.
*
* param price_side: 1 for ASK, 2 for BID
*/
double SymbolUtils::get_bid_ask_price(string symbol, int price_side) {
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), digits);
double bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), digits);
if (price_side == 1) return ask;
if (price_side == 2) return bid;
return 0.0; // fallback if invalid side passed
}
-65
View File
@@ -1,65 +0,0 @@
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/TimeZones.mqh>
class TradingWindow : public CObject{
protected:
TimeZones tz;
bool in_window;
datetime start_time;
datetime end_time;
public:
bool define_window(string t1, string t2, string time_zone, bool plot_range_inp=true);
};
bool TradingWindow::define_window(string t1, string t2, string time_zone, bool plot_range=true){
datetime _t1 = StringToTime(t1);
datetime _t2 = StringToTime(t2);
if(_t1 > _t2){
_t2 = _t2 + PeriodSeconds(PERIOD_D1);
}
int w_duration = (int)(_t2 - _t1);
// window flag
if(TimeCurrent() >= start_time && TimeCurrent() <= end_time){
in_window = true;
}
// define new window
if(TimeCurrent() >= end_time){
in_window = false;
start_time = tz.timezone_conversions(time_zone, StringToTime(t1), "Broker");
if(TimeCurrent()>=start_time){
start_time += PeriodSeconds(PERIOD_D1);
}
end_time = start_time + w_duration;
if(plot_range){
string name = "Start Time" + (string)start_time;
if(start_time>0){
ObjectCreate(NULL, name, OBJ_VLINE, 0, start_time, 0);
ObjectSetInteger(NULL, name,OBJPROP_COLOR, clrBlue);
ObjectSetInteger(NULL, name,OBJPROP_BACK, true);
}
name = "End Time" + (string)end_time;
if(end_time>0){
ObjectCreate(NULL, name, OBJ_VLINE, 0, end_time, 0);
ObjectSetInteger(NULL, name,OBJPROP_COLOR, C'56,108,26');
ObjectSetInteger(NULL, name,OBJPROP_BACK, true);
}
ChartRedraw();
}
}
return in_window;
}
Binary file not shown.
+100 -28
View File
@@ -1,113 +1,173 @@
#include <MyLibs/Utils/MarketDataUtils.mqh>
#include <MyLibs/Utils/AtrHandleManager.mqh>
// ---------------------------------------------------------------------
// CLASS: AtrBands
// ---------------------------------------------------------------------
// Provides ATR-based upper/lower/middle band calculations and checks.
// Useful for volatility-based stop placement, trend filters, or overlays.
// ---------------------------------------------------------------------
class AtrBands {
private:
MarketDataUtils market_data_utils;
AtrHandleManager atr_manager;
double get_atr(string symbol, int atr_period, ENUM_TIMEFRAMES tf, int shift);
public:
// Core band accessors
double upper_band(string symbol, double trendline_var, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1);
double lower_band(string symbol, double trendline_var, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1);
double middle_band(string symbol, double trendline_var, ENUM_TIMEFRAMES tf, int shift = 1);
// Band checkers
bool inside_upper_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1);
bool inside_lower_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1);
bool crossed_below_upper_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1);
bool crossed_above_lower_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1);
// Plotting
void plot_bands(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int bars = 100, color clr = clrSkyBlue, int width = 1);
protected:
double get_atr(string symbol, int atr_period, ENUM_TIMEFRAMES tf, int shift);
};
// -------------------
// Public Methods
// -------------------
// ---------------------------------------------------------------------
// Returns the upper ATR band level.
//
// Parameters:
// - symbol : Symbol to use.
// - trendline_var : Base trendline value (e.g., MA).
// - atr_period : ATR period.
// - tf : Timeframe for ATR.
// - mult : ATR multiplier.
// - shift : Bar shift to evaluate.
// ---------------------------------------------------------------------
double AtrBands::upper_band(string symbol, double trendline_var, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift) {
double atr = get_atr(symbol, atr_period, tf, shift);
return trendline_var + atr * mult;
}
// ---------------------------------------------------------------------
// Returns the lower ATR band level.
//
// Parameters:
// - Same as upper_band, except lower band logic.
// ---------------------------------------------------------------------
double AtrBands::lower_band(string symbol, double trendline_var, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift) {
double atr = get_atr(symbol, atr_period, tf, shift);
return trendline_var - atr * mult;
}
// ---------------------------------------------------------------------
// Returns the middle band, which is simply the trendline.
//
// Parameters:
// - symbol : Symbol.
// - trendline_var : The trendline value.
// - tf : Timeframe (unused here).
// - shift : Shift index (unused here).
// ---------------------------------------------------------------------
double AtrBands::middle_band(string symbol, double trendline_var, ENUM_TIMEFRAMES tf, int shift) {
return trendline_var;
}
// ---------------------------------------------------------------------
// Checks if price is between trendline and upper band.
//
// Logic:
// - Retrieves trendline and price.
// - Compares if price lies between trendline and upper band.
// ---------------------------------------------------------------------
bool AtrBands::inside_upper_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift) {
double price = iClose(symbol, tf, shift);
double trendline_var = market_data_utils.get_buffer_value(handle, shift);
double upper = upper_band(symbol, trendline_var, atr_period, tf, mult, shift);
if (price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || upper == EMPTY_VALUE) return false;
if (price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || upper == EMPTY_VALUE)
return false;
return (price > trendline_var && price < upper);
}
// ---------------------------------------------------------------------
// Checks if price is between trendline and lower band.
//
// Logic:
// - Retrieves trendline and price.
// - Compares if price lies between trendline and lower band.
// ---------------------------------------------------------------------
bool AtrBands::inside_lower_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift) {
double price = iClose(symbol, tf, shift);
double trendline_var = market_data_utils.get_buffer_value(handle, shift);
double lower = lower_band(symbol, trendline_var, atr_period, tf, mult, shift);
if (price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || lower == EMPTY_VALUE) return false;
if (price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || lower == EMPTY_VALUE)
return false;
return (price < trendline_var && price > lower);
}
// ---------------------------------------------------------------------
// Detects if price has crossed below the upper band.
//
// Logic:
// - Checks crossover from above to below upper band between bars [shift+1] and [shift].
// ---------------------------------------------------------------------
bool AtrBands::crossed_below_upper_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift) {
double price = iClose(symbol, tf, shift);
double prev_price = iClose(symbol, tf, shift + 1);
double trendline_var = market_data_utils.get_buffer_value(handle, shift);
double prev_trendline_var = market_data_utils.get_buffer_value(handle, shift + 1);
double upper = upper_band(symbol, trendline_var, atr_period, tf, mult, shift);
double prev_upper = upper_band(symbol, prev_trendline_var, atr_period, tf, mult, shift + 1);
if (price == EMPTY_VALUE || prev_price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || prev_trendline_var == EMPTY_VALUE) return false;
if (price == EMPTY_VALUE || prev_price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || prev_trendline_var == EMPTY_VALUE)
return false;
return (prev_price > prev_upper && price < upper);
}
// ---------------------------------------------------------------------
// Detects if price has crossed above the lower band.
//
// Logic:
// - Checks crossover from below to above lower band between bars [shift+1] and [shift].
// ---------------------------------------------------------------------
bool AtrBands::crossed_above_lower_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift) {
double price = iClose(symbol, tf, shift);
double prev_price = iClose(symbol, tf, shift + 1);
double trendline_var = market_data_utils.get_buffer_value(handle, shift);
double prev_trendline_var = market_data_utils.get_buffer_value(handle, shift + 1);
double lower = lower_band(symbol, trendline_var, atr_period, tf, mult, shift);
double prev_lower = lower_band(symbol, prev_trendline_var, atr_period, tf, mult, shift + 1);
if (price == EMPTY_VALUE || prev_price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || prev_trendline_var == EMPTY_VALUE) return false;
if (price == EMPTY_VALUE || prev_price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || prev_trendline_var == EMPTY_VALUE)
return false;
return (prev_price < prev_lower && price > lower);
}
// ---------------------------------------------------------------------
// Plots the ATR bands as OBJ_TREND lines on chart.
//
// Parameters:
// - symbol : Symbol to draw on.
// - handle : Trendline handle (e.g., MA).
// - atr_period : ATR period for bands.
// - tf : Timeframe.
// - mult : ATR multiplier.
// - bars : Number of bars to plot.
// - clr : Line color.
// - width : Line width.
// ---------------------------------------------------------------------
void AtrBands::plot_bands(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int bars, color line_color, int width) {
for (int i = bars; i >= 1; i--) {
double trendline = market_data_utils.get_buffer_value(handle, i);
if (trendline == EMPTY_VALUE) continue;
if (trendline == EMPTY_VALUE)
continue;
double atr = get_atr(symbol, atr_period, tf, i);
if (atr == EMPTY_VALUE) continue;
if (atr == EMPTY_VALUE)
continue;
double upper = trendline + atr * mult;
double lower = trendline - atr * mult;
datetime time1 = iTime(symbol, tf, i);
datetime time2 = iTime(symbol, tf, i - 1);
string upper_name = "ATR_Upper_" + symbol + "_" + TimeToString(time1, TIME_DATE | TIME_MINUTES);
string lower_name = "ATR_Lower_" + symbol + "_" + TimeToString(time1, TIME_DATE | TIME_MINUTES);
string middle_name = "ATR_middle_" + symbol + "_" + TimeToString(time1, TIME_DATE | TIME_MINUTES);
string upper_name = "ATR_Upper_" + symbol + "_" + TimeToString(time1, TIME_DATE | TIME_MINUTES);
string lower_name = "ATR_Lower_" + symbol + "_" + TimeToString(time1, TIME_DATE | TIME_MINUTES);
string middle_name = "ATR_Middle_" + symbol + "_" + TimeToString(time1, TIME_DATE | TIME_MINUTES);
if (ObjectFind(0, upper_name) < 0) {
ObjectCreate(0, upper_name, OBJ_TREND, 0, time1, upper, time2, upper);
@@ -140,6 +200,18 @@ void AtrBands::plot_bands(string symbol, int handle, int atr_period, ENUM_TIMEFR
ChartRedraw();
}
// ---------------------------------------------------------------------
// Retrieves the ATR value via the shared ATR manager.
//
// Parameters:
// - symbol : Symbol to calculate ATR on.
// - atr_period : ATR calculation period.
// - tf : Timeframe of the ATR.
// - shift : Bar index to retrieve.
//
// Returns:
// - The ATR value at the given shift.
// ---------------------------------------------------------------------
double AtrBands::get_atr(string symbol, int atr_period, ENUM_TIMEFRAMES tf, int shift) {
return atr_manager.get_atr_value(symbol, tf, atr_period, shift);
}
+71 -54
View File
@@ -1,59 +1,76 @@
#include <MyLibs/Utils/MarketDataUtils.mqh>
//+------------------------------------------------------------------+
//| TrendlineSignal: A utility class to detect trendline crossovers |
//+------------------------------------------------------------------+
// ---------------------------------------------------------------------
// CLASS: TrendlineAnalyser
// ---------------------------------------------------------------------
// A utility class to detect price crossovers with a trendline buffer.
// Supports both crossover detection and trend direction checks.
// ---------------------------------------------------------------------
class TrendlineAnalyser {
private:
private:
MarketDataUtils market_data_utils;
public:
// Main method: detects whether price has crossed the trendline up (long) or down (short)
// Parameters:
// - symbol: the symbol we're evaluating (e.g., "EURUSD")
// - handle: the indicator handle for the trendline (e.g., iMA handle)
// - cross_long: output bool set to true if a bullish cross is detected
// - cross_short: output bool set to true if a bearish cross is detected
void detect_cross(string symbol, int handle, bool& cross_long, bool& cross_short, int shift=1) {
cross_long = false;
cross_short = false;
// --- Retrieve prices and trendline values from last 2 closed bars
double price = iClose(symbol, PERIOD_CURRENT, shift); // most recent closed bar
double prev_price = iClose(symbol, PERIOD_CURRENT, shift + 1); // bar before that
double trendline = market_data_utils.get_buffer_value(handle, shift); // trendline now
double prev_trendline = market_data_utils.get_buffer_value(handle, shift+1); // trendline before
// --- Exit early if any of the data is missing or invalid
if (price == EMPTY_VALUE || prev_price == EMPTY_VALUE || trendline == EMPTY_VALUE || prev_trendline == EMPTY_VALUE) {
return;
}
// --- Detect bullish crossover (price moves from below to above the trendline)
cross_long = (prev_price < prev_trendline && price > trendline);
// --- Detect bearish crossover (price moves from above to below the trendline)
cross_short = (prev_price > prev_trendline && price < trendline);
}
// METHOD: determines if price is currently trending above or below the trendline
// Parameters:
// - symbol: trading symbol (e.g., "EURUSD")
// - handle: trendline indicator handle
// - is_direction_long: output bool, true if price is above trendline
// - is_direction_short: output bool, true if price is below trendline
// - shift: which candle to check (default: 1 = last closed bar)
void trend_direction(string symbol, int handle, bool& direction_long, bool& direction_short, int shift = 1) {
direction_long = false;
direction_short = false;
double price = iClose(symbol, PERIOD_CURRENT, shift);
double trendline = market_data_utils.get_buffer_value(handle, shift);
if (price == EMPTY_VALUE || trendline == EMPTY_VALUE) {
return;
}
direction_long = (price > trendline);
direction_short = (price < trendline);
}
public:
void detect_cross(string symbol, int handle, bool& cross_long, bool& cross_short, int shift = 1);
void trend_direction(string symbol, int handle, bool& direction_long, bool& direction_short, int shift = 1);
};
// ---------------------------------------------------------------------
// Detects whether price has crossed above (long) or below (short)
// a trendline between the two most recent closed bars.
//
// Parameters:
// - symbol : Trading symbol (e.g., "EURUSD").
// - handle : Indicator handle for the trendline buffer.
// - cross_long : Output true if bullish crossover detected.
// - cross_short : Output true if bearish crossover detected.
// - shift : Bar shift to evaluate (default: 1 = last closed bar).
//
// Logic:
// - Retrieves price and trendline values for the current and previous bar.
// - Checks for a crossover by comparing price vs trendline movement.
// ---------------------------------------------------------------------
void TrendlineAnalyser::detect_cross(string symbol, int handle, bool& cross_long, bool& cross_short, int shift) {
cross_long = false;
cross_short = false;
double price = iClose(symbol, PERIOD_CURRENT, shift);
double prev_price = iClose(symbol, PERIOD_CURRENT, shift + 1);
double trendline = market_data_utils.get_buffer_value(handle, shift);
double prev_trendline = market_data_utils.get_buffer_value(handle, shift + 1);
if (price == EMPTY_VALUE || prev_price == EMPTY_VALUE || trendline == EMPTY_VALUE || prev_trendline == EMPTY_VALUE)
return;
cross_long = (prev_price < prev_trendline && price > trendline);
cross_short = (prev_price > prev_trendline && price < trendline);
}
// ---------------------------------------------------------------------
// Checks if price is currently above or below the trendline.
//
// Parameters:
// - symbol : Trading symbol (e.g., "EURUSD").
// - handle : Trendline indicator handle.
// - direction_long : Output true if price is above trendline.
// - direction_short : Output true if price is below trendline.
// - shift : Bar index to check (default: 1).
//
// Logic:
// - Retrieves price and trendline at the specified shift.
// - Compares relative position of price to trendline.
// ---------------------------------------------------------------------
void TrendlineAnalyser::trend_direction(string symbol, int handle, bool& direction_long, bool& direction_short, int shift) {
direction_long = false;
direction_short = false;
double price = iClose(symbol, PERIOD_CURRENT, shift);
double trendline = market_data_utils.get_buffer_value(handle, shift);
if (price == EMPTY_VALUE || trendline == EMPTY_VALUE)
return;
direction_long = (price > trendline);
direction_short = (price < trendline);
}