Inclustion of example EA

This commit is contained in:
Matt Corcoran
2025-07-12 16:47:54 +02:00
parent 1222236ded
commit 657125d61c
37 changed files with 466 additions and 3341 deletions
+105
View File
@@ -0,0 +1,105 @@
#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;
double win_loss_ratio(int min_required_trades);
double win_percent_min_trades(int min_required_trades);
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:
custom_criteria = win_loss_ratio(min_trades);
break;
case CM_WIN_PERCENT:
custom_criteria = win_percent_min_trades(min_trades);
break;
default:
custom_criteria = 0;
break;
}
return custom_criteria;
}
// ---------------------------------------------------------------------
// 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;
return wins / losses;
}
// ---------------------------------------------------------------------
// 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;
return result;
}
+81
View File
@@ -0,0 +1,81 @@
// ---------------------------------------------------------------------
// 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,
ODD_MONTHS,
EVEN_MONTHS,
ODD_WEEKS,
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);
};
// ---------------------------------------------------------------------
// 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());
// 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;
// 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;
return false;
}
+264
View File
@@ -0,0 +1,264 @@
#include <Trade/Trade.mqh>
#include <MyLibs/utils/AtrHandleManager.mqh>
// ---------------------------------------------------------------------
// GLOBALS
// ---------------------------------------------------------------------
CTrade trade;
AtrHandleManager atr_manager;
// ---------------------------------------------------------------------
// CLASS: AdjustPosition
// ---------------------------------------------------------------------
// Provides methods to manage stop-loss logic for runner trades.
// Includes breakeven, trailing stop (fixed and ATR), and virtual TP SLs.
// ---------------------------------------------------------------------
class AdjustPosition {
public:
void set_breakeven_sl(string symbol, int runner_magic_no, double buffer_points = 5);
void set_breakeven_if_profit_target_hit(string symbol, int runner_magic_no, double buffer_points = 5);
void set_fixed_sl(string symbol, int runner_magic_no, double fixed_sl_price);
void set_trailing_sl(string symbol, int runner_magic_no, double sl_offset_points = 5);
void trailing_stop_atr(string symbol, int magic_number, ENUM_TIMEFRAMES tf = PERIOD_CURRENT, double activation_mult = 1.0,
double trail_mult = 1.0, int atr_period = 14, bool use_bar_close = false);
private:
void set_breakeven_sl_for_ticket(string symbol, ulong ticket, long order_type, double entry_price,
double current_sl, double current_tp, int digits, double buffer_price, bool remove_tp);
};
// ---------------------------------------------------------------------
// Sets SL to breakeven for all matching runner trades.
// ---------------------------------------------------------------------
void AdjustPosition::set_breakeven_sl(string symbol, int runner_magic_no, double buffer_points) {
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double buffer_price = buffer_points * _Point;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue;
long order_type = PositionGetInteger(POSITION_TYPE);
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
set_breakeven_sl_for_ticket(symbol, ticket, order_type, entry, sl, tp, digits, buffer_price, false);
}
}
// ---------------------------------------------------------------------
// Sets SL to breakeven if virtual TP (in comment) was hit.
// ---------------------------------------------------------------------
void AdjustPosition::set_breakeven_if_profit_target_hit(string symbol, int runner_magic_no, double buffer_points) {
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double buffer_price = buffer_points * _Point;
double price_margin = 50 * _Point;
bool has_runner = false;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
if ((int) PositionGetInteger(POSITION_MAGIC) == runner_magic_no) {
has_runner = true;
break;
}
}
if (!has_runner) return;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue;
long order_type = PositionGetInteger(POSITION_TYPE);
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
string comment = PositionGetString(POSITION_COMMENT);
double virtual_tp = 0.0;
if (StringFind(comment, "runner_tp:") == 0) {
string tp_str = StringSubstr(comment, StringLen("runner_tp:"));
virtual_tp = StringToDouble(tp_str);
}
if (virtual_tp <= 0.0) continue;
if (tp > 0.0)
PrintFormat("Warning: Runner trade on %s (ticket %d) has TP set: %.5f", symbol, ticket, tp);
if (order_type == POSITION_TYPE_BUY && bid < virtual_tp - price_margin) continue;
if (order_type == POSITION_TYPE_SELL && ask > virtual_tp + price_margin) continue;
bool tp_hit = (order_type == POSITION_TYPE_BUY && bid >= virtual_tp) ||
(order_type == POSITION_TYPE_SELL && ask <= virtual_tp);
if (!tp_hit) continue;
double expected_sl = (order_type == POSITION_TYPE_BUY)
? entry + buffer_price
: entry - buffer_price;
if (NormalizeDouble(sl, digits) == NormalizeDouble(expected_sl, digits)) continue;
set_breakeven_sl_for_ticket(symbol, ticket, order_type, entry, sl, tp, digits, buffer_price, true);
}
}
// ---------------------------------------------------------------------
// Sets SL for a single trade to breakeven, optionally removes TP.
// ---------------------------------------------------------------------
void AdjustPosition::set_breakeven_sl_for_ticket(string symbol, ulong ticket, long order_type, double entry_price,
double current_sl, double current_tp, int digits,
double buffer_price, bool remove_tp) {
double breakeven_sl = (order_type == POSITION_TYPE_BUY)
? entry_price + buffer_price
: entry_price - buffer_price;
if ((order_type == POSITION_TYPE_BUY && current_sl >= breakeven_sl) ||
(order_type == POSITION_TYPE_SELL && current_sl <= breakeven_sl)) return;
MqlTradeRequest request = {};
MqlTradeResult result;
request.action = TRADE_ACTION_SLTP;
request.symbol = symbol;
request.position = ticket;
request.sl = NormalizeDouble(breakeven_sl, digits);
request.tp = remove_tp ? 0.0 : current_tp;
request.magic = (int) PositionGetInteger(POSITION_MAGIC);
if (!OrderSend(request, result))
Print("Failed to adjust runner: ", symbol, ". Error: ", result.retcode);
else if (remove_tp)
Print("Runner upgraded to trailing: SL at breakeven, TP removed for ", symbol);
}
// ---------------------------------------------------------------------
// Sets a fixed SL price for all runner trades.
// ---------------------------------------------------------------------
void AdjustPosition::set_fixed_sl(string symbol, int runner_magic_no, double fixed_sl_price) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue;
double current_sl = PositionGetDouble(POSITION_SL);
double current_tp = PositionGetDouble(POSITION_TP);
if (current_sl == fixed_sl_price) continue;
MqlTradeRequest request = {};
MqlTradeResult result;
request.action = TRADE_ACTION_SLTP;
request.symbol = symbol;
request.position = ticket;
request.sl = fixed_sl_price;
request.tp = current_tp;
request.magic = runner_magic_no;
if (!OrderSend(request, result))
Print("Failed to set fixed SL for runner on ", symbol, ". Error: ", result.retcode);
}
}
// ---------------------------------------------------------------------
// Applies a fixed-point trailing stop to runner trades.
// ---------------------------------------------------------------------
void AdjustPosition::set_trailing_sl(string symbol, int runner_magic_no, double sl_offset_points) {
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double offset = sl_offset_points * _Point;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue;
long type = PositionGetInteger(POSITION_TYPE);
double current_sl = PositionGetDouble(POSITION_SL);
double current_tp = PositionGetDouble(POSITION_TP);
double price = (type == POSITION_TYPE_BUY)
? SymbolInfoDouble(symbol, SYMBOL_BID)
: SymbolInfoDouble(symbol, SYMBOL_ASK);
double sl = (type == POSITION_TYPE_BUY) ? price - offset : price + offset;
if ((type == POSITION_TYPE_BUY && sl <= current_sl) ||
(type == POSITION_TYPE_SELL && sl >= current_sl)) continue;
MqlTradeRequest request = {};
MqlTradeResult result;
request.action = TRADE_ACTION_SLTP;
request.symbol = symbol;
request.position = ticket;
request.sl = NormalizeDouble(sl, digits);
request.tp = current_tp;
request.magic = runner_magic_no;
if (!OrderSend(request, result))
Print("Failed to update trailing SL for runner on ", symbol, ". Error: ", result.retcode);
}
}
// ---------------------------------------------------------------------
// Applies an ATR-based trailing stop to runner trades.
//
// Parameters:
// - symbol : Trading symbol.
// - _magic_number : Magic number to identify trades.
// - tf : Timeframe used for ATR calculation.
// - activation_mult: Multiplier to determine when to activate trailing.
// - trail_mult : Multiplier to determine trailing distance.
// - atr_period : ATR period to use.
// - use_bar_close : If true, use bar close instead of live price.
//
// Logic:
// - Trailing starts only after activation distance is reached.
// - SL is only updated if it moves closer to price (i.e., improves).
// ---------------------------------------------------------------------
void AdjustPosition::trailing_stop_atr(string symbol, int _magic_number, ENUM_TIMEFRAMES tf, double activation_mult,
double trail_mult, int atr_period, bool use_bar_close) {
double atr = atr_manager.get_atr_value(symbol, tf, atr_period);
if (atr == EMPTY_VALUE) return;
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
if ((int) PositionGetInteger(POSITION_MAGIC) != _magic_number) continue;
long type = PositionGetInteger(POSITION_TYPE);
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = PositionGetDouble(POSITION_SL);
double price = use_bar_close ? iClose(symbol, tf, 1) : (type == POSITION_TYPE_BUY ? bid : ask);
double trail_distance = atr * trail_mult;
double activation_distance = atr * activation_mult;
bool should_trail = (type == POSITION_TYPE_BUY && price >= entry + activation_distance) ||
(type == POSITION_TYPE_SELL && price <= entry - activation_distance);
if (!should_trail) continue;
double new_sl = (type == POSITION_TYPE_BUY) ? price - trail_distance : price + trail_distance;
new_sl = NormalizeDouble(new_sl, digits);
if ((type == POSITION_TYPE_BUY && sl >= new_sl) ||
(type == POSITION_TYPE_SELL && sl <= new_sl)) continue;
if (!trade.PositionModify(ticket, new_sl, PositionGetDouble(POSITION_TP)))
PrintFormat("Trailing SL update failed for %s ticket=%d", symbol, ticket);
else
PrintFormat("Trailing SL updated: %s ticket=%d new SL=%.5f", symbol, ticket, new_sl);
}
}
+220
View File
@@ -0,0 +1,220 @@
#include <MyLibs/Utils/MarketDataUtils.mqh>
#include <MyLibs/Utils/TimeZones.mqh>
#include <MyLibs/Utils/AtrHandleManager.mqh>
#include <Trade/Trade.mqh>
// ---------------------------------------------------------------------
// CLASS: CalculatePositionData
// ---------------------------------------------------------------------
// Provides core logic for computing stop loss, take profit, lot size,
// and trading costs based on symbol, price, and risk parameters.
// ---------------------------------------------------------------------
class CalculatePositionData : public CObject {
protected:
CTrade trade;
CPositionInfo position;
MarketDataUtils mdu;
AtrHandleManager atr_manager;
bool check_lots(double& lots, string symbol);
bool normalise_price(double price, double& normalizedPrice, string symbol);
public:
double calculate_stoploss(string symbol, double price, int order_side, string mode_sl, double sl_var, ENUM_TIMEFRAMES atr_tf);
double calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double tp_var, ENUM_TIMEFRAMES atr_tf);
double calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var);
double calculate_trading_cost(string symbol, ulong ticket);
};
// ---------------------------------------------------------------------
// Calculates stop loss based on selected method.
//
// Parameters:
// - symbol : Symbol for the trade.
// - price : Entry price.
// - order_side: 1 = Buy, 2 = Sell.
// - mode_sl : SL method ("NO_STOPLOSS", "SL_FIXED_PIPS", etc).
// - sl_var : SL parameter (pips, %, ATR multiplier, or absolute).
// - atr_tf : Timeframe for ATR.
//
// Returns:
// - Calculated SL price, or 0 if invalid.
// ---------------------------------------------------------------------
double CalculatePositionData::calculate_stoploss(string symbol, double price, int order_side, string mode_sl, double sl_var, ENUM_TIMEFRAMES atr_tf) {
double sl = 0;
if (mode_sl == "NO_STOPLOSS") return 0;
if (mode_sl == "SL_FIXED_PIPS") {
double adj_point = mdu.adjusted_point(symbol);
sl = (order_side == 1) ? price - sl_var * adj_point : price + sl_var * adj_point;
if (!normalise_price(sl, sl, symbol)) return 0;
}
if (mode_sl == "SL_FIXED_PERCENT") {
sl = (order_side == 1) ? price - (sl_var * price / 100.0) : price + (sl_var * price / 100.0);
if (!normalise_price(sl, sl, symbol)) return 0;
}
if (mode_sl == "SL_ATR_MULTIPLE") {
double atr = atr_manager.get_atr_value(symbol, atr_tf, 14);
if (atr == EMPTY_VALUE) return 0;
sl = (order_side == 1) ? price - atr * sl_var : price + atr * sl_var;
if (!normalise_price(sl, sl, symbol)) return 0;
}
if (mode_sl == "SL_SPECIFIED_VALUE") {
double adj_point = mdu.adjusted_point(symbol);
double limit_sl = (order_side == 1) ? price - 10 * adj_point : price + 10 * adj_point;
sl = (order_side == 1) ? fmax(sl_var, limit_sl) : fmin(sl_var, limit_sl);
if (!normalise_price(sl, sl, symbol)) return 0;
}
return sl;
}
// ---------------------------------------------------------------------
// Calculates take profit based on selected method.
//
// Parameters:
// - symbol : Symbol for the trade.
// - price : Entry price.
// - stoploss : SL value (used in TP/SL ratio mode).
// - order_side: 1 = Buy, -1 = Sell.
// - mode_tp : TP method ("NO_TAKE_PROFIT", "TP_FIXED_PIPS", etc).
// - tp_var : TP parameter (pips, %, ATR multiplier, SL multiple).
// - atr_tf : Timeframe for ATR.
//
// Returns:
// - Calculated TP price, or 0 if invalid.
// ---------------------------------------------------------------------
double CalculatePositionData::calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double tp_var, ENUM_TIMEFRAMES atr_tf) {
double tp = 0;
if (mode_tp == "NO_TAKE_PROFIT") return 0;
if (mode_tp == "TP_FIXED_PIPS") {
double adj_point = mdu.adjusted_point(symbol);
tp = (order_side == 1) ? price + tp_var * adj_point : price - tp_var * adj_point;
if (!normalise_price(tp, tp, symbol)) return 0;
}
if (mode_tp == "TP_FIXED_PERCENT") {
tp = (order_side == 1) ? price + tp_var * price / 100.0 : price - tp_var * price / 100.0;
if (!normalise_price(tp, tp, symbol)) return 0;
}
if (mode_tp == "TP_ATR_MULTIPLE") {
double atr = atr_manager.get_atr_value(symbol, atr_tf, 14);
if (atr == EMPTY_VALUE) return 0;
tp = (order_side == 1) ? price + atr * tp_var : price - atr * tp_var;
if (!normalise_price(tp, tp, symbol)) return 0;
}
if (mode_tp == "TP_SL_MULTIPLE") {
double sl_size = (order_side == 1) ? price - stoploss : stoploss - price;
tp = (order_side == 1) ? price + tp_var * sl_size : price - tp_var * sl_size;
if (!normalise_price(tp, tp, symbol)) return 0;
}
if (mode_tp == "TP_SPECIFIED_VALUE") {
double adj_point = mdu.adjusted_point(symbol);
double limit_tp = (order_side == 1) ? price + 10 * adj_point : price - 10 * adj_point;
tp = (order_side == 1) ? fmin(tp_var, limit_tp) : fmax(tp_var, limit_tp);
if (!normalise_price(tp, tp, symbol)) return 0;
}
return tp;
}
// ---------------------------------------------------------------------
// Calculates lot size based on selected lot mode.
//
// Parameters:
// - symbol : Symbol for the trade.
// - sl_distance: SL distance in points.
// - price : Current price.
// - mode_lot : Lot mode ("LOT_MODE_FIXED", "LOT_MODE_PCT_RISK", etc).
// - lot_var : Value for lot calculation.
//
// Returns:
// - Computed lot size (rounded and validated).
// ---------------------------------------------------------------------
double CalculatePositionData::calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var) {
double lots = 0;
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double volume_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
double account_value = fmin(fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE)), AccountInfoDouble(ACCOUNT_MARGIN_FREE));
double risk_money = account_value * lot_var / 100.0;
if (mode_lot == "LOT_MODE_FIXED") {
lots = lot_var;
}
if (mode_lot == "LOT_MODE_PCT_RISK") {
double money_per_step = (sl_distance / tick_size) * tick_value * volume_step;
lots = MathFloor(risk_money / money_per_step) * volume_step;
}
if (mode_lot == "LOT_MODE_PCT_ACCOUNT") {
double money_per_step = (price / tick_size) * tick_value * volume_step;
lots = MathFloor(risk_money / money_per_step) * volume_step;
}
if (!check_lots(lots, symbol)) return 0;
return lots;
}
// ---------------------------------------------------------------------
// Validates and adjusts lot size to symbol constraints.
//
// Parameters:
// - lots : Input/output lot size.
// - symbol : Trading symbol.
//
// Returns:
// - true if lots are valid after correction.
// ---------------------------------------------------------------------
bool CalculatePositionData::check_lots(double& lots, string symbol) {
double min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if (lots < min) {
lots = min;
return true;
}
if (lots > max) {
Print("Lot size exceeds max for ", symbol);
return false;
}
lots = (int)MathFloor(lots / step) * step;
return true;
}
// ---------------------------------------------------------------------
// Normalizes price to the nearest valid tick size.
//
// Parameters:
// - price : Raw price.
// - normalizedPrice: Output normalized price.
// - symbol : Trading symbol.
//
// Returns:
// - true if successful, false if tick size lookup failed.
// ---------------------------------------------------------------------
bool CalculatePositionData::normalise_price(double price, double& normalizedPrice, string symbol) {
double tick_size;
if (!SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE, tick_size)) {
Print("Failed to get tick size for ", symbol);
return false;
}
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
normalizedPrice = NormalizeDouble(MathRound(price / tick_size) * tick_size, digits);
return true;
}
+308
View File
@@ -0,0 +1,308 @@
#include <MyLibs/Orders/CalculatePositionData.mqh>
#include <Trade/Trade.mqh>
class EntryOrders {
protected:
CTrade trade;
CalculatePositionData calc;
public:
int count_open_positions(string symbol, int order_side, long _magic_number);
bool open_buy_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var, string _tp_mode,
double tp_var, string _lot_mode, double lot_var, long _magic_number);
bool open_sell_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var, string _tp_mode,
double tp_var, string _lot_mode, double lot_var, long _magic_number);
bool open_buy_stop_order(string symbol, bool condition, double entry_price, datetime expiration, ENUM_TIMEFRAMES atr_period,
string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long _magic_number);
bool open_sell_stop_order(string symbol, bool condition, double entry_price, datetime expiration, ENUM_TIMEFRAMES atr_period,
string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long _magic_number);
bool open_runner_buy_order_with_virtual_tp(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var,
string _tp_mode, double tp_var, string _lot_mode, double lot_var, long _magic_number);
bool open_runner_sell_order_with_virtual_tp(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var,
string _tp_mode, double tp_var, string _lot_mode, double lot_var, long _magic_number);
};
// ---------------------------------------------------------------------
// Counts open positions by symbol, side, and magic number.
//
// Parameters:
// - symbol : Symbol to check.
// - order_side : 1 = Buy, 2 = Sell, 0 = Any.
// - _magic_number : Magic number to filter.
//
// Returns:
// - Number of matching open positions.
// ---------------------------------------------------------------------
int EntryOrders::count_open_positions(string symbol, int order_side, long _magic_number) {
int count = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == _magic_number) {
int type = (int) PositionGetInteger(POSITION_TYPE);
if (order_side == 0 || (order_side == 1 && type == POSITION_TYPE_BUY) || (order_side == 2 && type == POSITION_TYPE_SELL)) {
count++;
}
}
}
return count;
}
// ---------------------------------------------------------------------
// Opens a market BUY position.
//
// Parameters:
// - symbol : Symbol to trade.
// - condition : If false, trade will not execute.
// - atr_period : Timeframe for ATR-based SL/TP.
// - _sl_mode : SL calculation method.
// - sl_var : SL variable (e.g., pips or ATR multiplier).
// - _tp_mode : TP calculation method.
// - tp_var : TP variable.
// - _lot_mode : Lot calculation method.
// - lot_var : Lot sizing variable.
// - _magic_number : Magic number for trade.
//
// Returns:
// - True if trade was placed successfully.
// ---------------------------------------------------------------------
bool EntryOrders::open_buy_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var,
string _tp_mode, double tp_var, string _lot_mode, double lot_var, long _magic_number) {
if (!condition) return false;
double current_price = SymbolInfoDouble(symbol, SYMBOL_ASK);
if (count_open_positions(symbol, 1, _magic_number) > 0) return false;
double stop_loss = calc.calculate_stoploss(symbol, current_price, 1, _sl_mode, sl_var, atr_period);
double take_profit = calc.calculate_take_profit(symbol, current_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
double sl_distance = current_price - stop_loss;
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
if (lots <= 0) {
Print("Lot calculation failed for ", symbol);
return false;
}
trade.SetExpertMagicNumber(_magic_number);
string comment = "Magic Number: " + IntegerToString(_magic_number);
bool result = trade.PositionOpen(symbol, ORDER_TYPE_BUY, lots, current_price, stop_loss, take_profit, comment);
if (!result) Print("Trade open failed for BUY ", symbol);
return result;
}
// ---------------------------------------------------------------------
// Opens a market SELL position.
//
// Parameters:
// - symbol : Symbol to trade.
// - condition : If false, trade will not execute.
// - atr_period : Timeframe for ATR-based SL/TP.
// - _sl_mode : SL calculation method.
// - sl_var : SL variable (e.g., pips or ATR multiplier).
// - _tp_mode : TP calculation method.
// - tp_var : TP variable.
// - _lot_mode : Lot calculation method.
// - lot_var : Lot sizing variable.
// - _magic_number : Magic number for trade.
//
// Returns:
// - True if trade was placed successfully.
// ---------------------------------------------------------------------
bool EntryOrders::open_sell_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var,
string _tp_mode, double tp_var, string _lot_mode, double lot_var, long _magic_number) {
if (!condition) return false;
double current_price = SymbolInfoDouble(symbol, SYMBOL_BID);
if (count_open_positions(symbol, 2, _magic_number) > 0) return false;
double stop_loss = calc.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period);
double take_profit = calc.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
double sl_distance = stop_loss - current_price;
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
if (lots <= 0) {
Print("Lot calculation failed for ", symbol);
return false;
}
trade.SetExpertMagicNumber(_magic_number);
string comment = "Magic Number: " + IntegerToString(_magic_number);
bool result = trade.PositionOpen(symbol, ORDER_TYPE_SELL, lots, current_price, stop_loss, take_profit, comment);
if (!result) Print("Trade open failed for SELL ", symbol);
return result;
}
// ---------------------------------------------------------------------
// Opens a pending BUY STOP order.
//
// Parameters:
// - symbol : Symbol to trade.
// - condition : If false, order will not be placed.
// - entry_price : Trigger price for Buy Stop.
// - expiration : Expiration time for pending order.
// - atr_period : Timeframe for ATR-based SL/TP.
// - _sl_mode : SL calculation method.
// - sl_var : SL variable.
// - _tp_mode : TP calculation method.
// - tp_var : TP variable.
// - _lot_mode : Lot calculation method.
// - lot_var : Lot sizing variable.
// - _magic_number : Magic number for order.
//
// Returns:
// - True if order was placed successfully.
// ---------------------------------------------------------------------
bool EntryOrders::open_buy_stop_order(string symbol, bool condition, double entry_price, datetime expiration, ENUM_TIMEFRAMES atr_period,
string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long _magic_number) {
if (!condition) return false;
if (count_open_positions(symbol, 1, _magic_number) > 0) return false;
double stop_loss = calc.calculate_stoploss(symbol, entry_price, 1, _sl_mode, sl_var, atr_period);
double take_profit = calc.calculate_take_profit(symbol, entry_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
double sl_distance = entry_price - stop_loss;
double lots = calc.calculate_lots(symbol, sl_distance, entry_price, _lot_mode, lot_var);
if (lots <= 0) {
Print("Lot calculation failed for BUY STOP ", symbol);
return false;
}
trade.SetExpertMagicNumber(_magic_number);
string comment = "Magic Number: " + IntegerToString(_magic_number);
bool result = trade.BuyStop(lots, entry_price, symbol, stop_loss, take_profit, ORDER_TIME_SPECIFIED, expiration, comment);
if (!result) Print("BuyStop order failed for ", symbol);
return result;
}
// ---------------------------------------------------------------------
// Opens a pending SELL STOP order.
//
// Parameters:
// - symbol : Symbol to trade.
// - condition : If false, order will not be placed.
// - entry_price : Trigger price for Sell Stop.
// - expiration : Expiration time for pending order.
// - atr_period : Timeframe for ATR-based SL/TP.
// - _sl_mode : SL calculation method.
// - sl_var : SL variable.
// - _tp_mode : TP calculation method.
// - tp_var : TP variable.
// - _lot_mode : Lot calculation method.
// - lot_var : Lot sizing variable.
// - _magic_number : Magic number for order.
//
// Returns:
// - True if order was placed successfully.
// ---------------------------------------------------------------------
bool EntryOrders::open_sell_stop_order(string symbol, bool condition, double entry_price, datetime expiration, ENUM_TIMEFRAMES atr_period,
string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long _magic_number) {
if (!condition) return false;
if (count_open_positions(symbol, 2, _magic_number) > 0) return false;
double stop_loss = calc.calculate_stoploss(symbol, entry_price, 2, _sl_mode, sl_var, atr_period);
double take_profit = calc.calculate_take_profit(symbol, entry_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
double sl_distance = stop_loss - entry_price;
double lots = calc.calculate_lots(symbol, sl_distance, entry_price, _lot_mode, lot_var);
if (lots <= 0) {
Print("Lot calculation failed for SELL STOP ", symbol);
return false;
}
trade.SetExpertMagicNumber(_magic_number);
string comment = "Magic Number: " + IntegerToString(_magic_number);
bool result = trade.SellStop(lots, entry_price, symbol, stop_loss, take_profit, ORDER_TIME_SPECIFIED, expiration, comment);
if (!result) Print("SellStop order failed for ", symbol);
return result;
}
// ---------------------------------------------------------------------
// Opens a market BUY runner with virtual TP in comment.
//
// Parameters:
// - symbol : Symbol to trade.
// - condition : If false, trade will not execute.
// - atr_period : Timeframe for ATR-based SL.
// - _sl_mode : SL calculation method.
// - sl_var : SL variable.
// - _tp_mode : TP calculation method.
// - tp_var : TP variable (used for virtual TP).
// - _lot_mode : Lot calculation method.
// - lot_var : Lot sizing variable.
// - _magic_number : Magic number for trade.
//
// Returns:
// - True if trade was placed successfully.
// ---------------------------------------------------------------------
bool EntryOrders::open_runner_buy_order_with_virtual_tp(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode,
double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long _magic_number) {
if (!condition) return false;
double current_price = SymbolInfoDouble(symbol, SYMBOL_ASK);
if (count_open_positions(symbol, 1, _magic_number) > 0) return false;
double stop_loss = calc.calculate_stoploss(symbol, current_price, 1, _sl_mode, sl_var, atr_period);
double virtual_tp = calc.calculate_take_profit(symbol, current_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
double sl_distance = current_price - stop_loss;
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
if (lots <= 0) {
Print("Lot calculation failed for runner BUY ", symbol);
return false;
}
trade.SetExpertMagicNumber(_magic_number);
string comment = StringFormat("runner_tp:%.5f", virtual_tp);
bool result = trade.PositionOpen(symbol, ORDER_TYPE_BUY, lots, current_price, stop_loss, 0.0, comment);
if (!result) Print("Runner BUY order failed for ", symbol);
return result;
}
// ---------------------------------------------------------------------
// Opens a market SELL runner with virtual TP in comment.
//
// Parameters:
// - symbol : Symbol to trade.
// - condition : If false, trade will not execute.
// - atr_period : Timeframe for ATR-based SL.
// - _sl_mode : SL calculation method.
// - sl_var : SL variable.
// - _tp_mode : TP calculation method.
// - tp_var : TP variable (used for virtual TP).
// - _lot_mode : Lot calculation method.
// - lot_var : Lot sizing variable.
// - _magic_number : Magic number for trade.
//
// Returns:
// - True if trade was placed successfully.
// ---------------------------------------------------------------------
bool EntryOrders::open_runner_sell_order_with_virtual_tp(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode,
double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
long _magic_number) {
if (!condition) return false;
double current_price = SymbolInfoDouble(symbol, SYMBOL_BID);
if (count_open_positions(symbol, 2, _magic_number) > 0) return false;
double stop_loss = calc.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period);
double virtual_tp = calc.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
double sl_distance = stop_loss - current_price;
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
if (lots <= 0) {
Print("Lot calculation failed for runner SELL ", symbol);
return false;
}
trade.SetExpertMagicNumber(_magic_number);
string comment = StringFormat("runner_tp:%.5f", virtual_tp);
bool result = trade.PositionOpen(symbol, ORDER_TYPE_SELL, lots, current_price, stop_loss, 0.0, comment);
if (!result) Print("Runner SELL order failed for ", symbol);
return result;
}
+199
View File
@@ -0,0 +1,199 @@
#include <MyLibs/Orders/CalculatePositionData.mqh>
#include <MyLibs/Utils/TimeZones.mqh>
#include <Trade/Trade.mqh>
class ExitOrders {
protected:
CTrade trade;
TimeZones tz;
CalculatePositionData calc;
ulong posTicket;
long position_open_time;
long first_allowed_close_time;
public:
bool close_buy_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period, long _magic_number);
bool close_sell_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period, long _magic_number);
bool daily_timed_exit(string symbol, datetime exit_time, int delay_days, long _magic_number);
bool daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, string exit_time, string cw_tzone, int delay_days, long _magic_number);
bool first_profitable_close_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, long _magic_number);
};
// ---------------------------------------------------------------------
// Closes BUY positions on condition + after a number of bars (if non 0) .
//
// Parameters:
// - symbol : Symbol to evaluate positions for.
// - condition : If true, closes position immediately.
// - close_bars : Minimum number of bars before auto close.
// - close_bar_period : Timeframe to count bars on.
// - _magic_number : Magic number to identify the trade group.
//
// Returns:
// - True after evaluation and any attempted closes.
// ---------------------------------------------------------------------
bool ExitOrders::close_buy_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period, long _magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == _magic_number) {
int time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1;
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
if (condition || (close_bars > 0 && time_difference >= close_bars)) {
trade.PositionClose(posTicket);
}
}
}
}
return true;
}
// ---------------------------------------------------------------------
// Closes SELL positions on condition + after a number of bars (if non 0) .
//
// Parameters:
// - symbol : Symbol to evaluate positions for.
// - condition : If true, closes position immediately.
// - close_bars : Minimum number of bars before auto close.
// - close_bar_period : Timeframe to count bars on.
// - _magic_number : Magic number to identify the trade group.
//
// Returns:
// - True after evaluation and any attempted closes.
// ---------------------------------------------------------------------
bool ExitOrders::close_sell_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period, long _magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == _magic_number) {
int time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1;
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
if (condition || (close_bars > 0 && time_difference >= close_bars)) {
trade.PositionClose(posTicket);
}
}
}
}
return true;
}
// ---------------------------------------------------------------------
// Closes position after a fixed exit time and delay in days.
//
// Parameters:
// - symbol : Symbol to evaluate.
// - exit_time : Time of day when exit is permitted.
// - delay_days : Number of full days before close allowed.
// - _magic_number : Magic number to identify the trade group.
//
// Returns:
// - True after evaluation and any attempted closes.
// ---------------------------------------------------------------------
bool ExitOrders::daily_timed_exit(string symbol, datetime exit_time, int delay_days, long _magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
position_open_time = PositionGetInteger(POSITION_TIME);
if ((int) position_open_time > 0) {
first_allowed_close_time = position_open_time + (delay_days * PeriodSeconds(PERIOD_D1));
if (TimeCurrent() > first_allowed_close_time && TimeCurrent() >= exit_time) {
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == _magic_number) {
trade.PositionClose(posTicket);
}
}
}
}
return true;
}
// ---------------------------------------------------------------------
// Closes a position only if it's profitable after a given time.
//
// Parameters:
// - symbol : Symbol to evaluate.
// - close_bar_period : Bar timeframe for bar-close evaluation.
// - exit_time : Time of day when profit exit is checked.
// - cw_tzone : Clockwork time zone for exit conversion.
// - delay_days : Minimum days to wait before closing.
// - _magic_number : Magic number to identify the trade group.
//
// Returns:
// - True after evaluation and any attempted closes.
// ---------------------------------------------------------------------
bool ExitOrders::daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, string exit_time, string cw_tzone, int delay_days,
long _magic_number) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
position_open_time = PositionGetInteger(POSITION_TIME);
if ((int) position_open_time > 0) {
first_allowed_close_time = position_open_time + (delay_days * PeriodSeconds(PERIOD_D1));
if (TimeCurrent() > first_allowed_close_time) {
datetime broker_close_time = tz.timezone_conversions(cw_tzone, StringToTime(exit_time), "Broker");
if (TimeCurrent() >= broker_close_time && PositionGetString(POSITION_SYMBOL) == symbol &&
PositionGetInteger(POSITION_MAGIC) == _magic_number) {
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double spread = SymbolInfoDouble(symbol, SYMBOL_ASK) - SymbolInfoDouble(symbol, SYMBOL_BID);
double bar_close = iClose(_Symbol, close_bar_period, 1); // shift 1 because 0 is live candle
double trading_cost = calc.calculate_trading_cost(symbol, posTicket);
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY &&
bar_close > (position_open_price + spread + trading_cost)) {
trade.PositionClose(posTicket);
}
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL &&
bar_close < (position_open_price - spread - trading_cost)) {
trade.PositionClose(posTicket);
}
}
}
}
}
return true;
}
// ---------------------------------------------------------------------
// Closes position on first profitable bar after one bar completes.
//
// Parameters:
// - symbol : Symbol to evaluate.
// - close_bar_period : Timeframe for bar-close evaluation.
// - _magic_number : Magic number to identify the trade group.
//
// Returns:
// - True after evaluation and any attempted closes.
// ---------------------------------------------------------------------
bool ExitOrders::first_profitable_close_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, long _magic_number) {
position_open_time = PositionGetInteger(POSITION_TIME);
first_allowed_close_time = position_open_time + PeriodSeconds(close_bar_period);
if ((int) position_open_time > 0 && TimeCurrent() > first_allowed_close_time) {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
posTicket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == _magic_number) {
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double spread = SymbolInfoDouble(symbol, SYMBOL_ASK) - SymbolInfoDouble(symbol, SYMBOL_BID);
double bar_close = iClose(_Symbol, close_bar_period, 1);
double trading_cost = calc.calculate_trading_cost(symbol, posTicket);
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && bar_close > (position_open_price + spread + trading_cost)) {
trade.PositionClose(posTicket);
}
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && bar_close < (position_open_price - spread - trading_cost)) {
trade.PositionClose(posTicket);
}
}
}
}
return true;
}
+96
View File
@@ -0,0 +1,96 @@
#include <Trade/OrderInfo.mqh>
#include <Trade/PositionInfo.mqh>
class OrderTracker {
protected:
COrderInfo m_order;
CPositionInfo m_position;
public:
int count_open_positions(string symbol, int order_side, long magic_number);
int count_all_positions(string symbol, long magic_number);
int count_pending_orders(string symbol, ENUM_ORDER_TYPE order_type, long magic);
};
// ---------------------------------------------------------------------
// Counts the number of open BUY or SELL positions for a given symbol.
//
// Parameters:
// - symbol : Trading symbol (e.g., "EURUSD").
// - order_side : 1 = BUY, 2 = SELL.
// - magic_number : Magic number identifying strategy group.
//
// Returns:
// - Number of matching open positions.
// ---------------------------------------------------------------------
int OrderTracker::count_open_positions(string symbol, int order_side, long magic_number) {
int count = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
if (order_side == 1 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
count++;
}
if (order_side == 2 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
count++;
}
}
}
return count;
}
// ---------------------------------------------------------------------
// Counts all open positions for a symbol regardless of direction.
//
// Parameters:
// - symbol : Trading symbol.
// - magic_number : Magic number identifying strategy group.
//
// Returns:
// - Total number of matching positions.
// ---------------------------------------------------------------------
int OrderTracker::count_all_positions(string symbol, long magic_number) {
int count = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
count++;
}
}
return count;
}
// ---------------------------------------------------------------------
// Counts pending orders of a specific type for a symbol and magic number.
//
// Parameters:
// - symbol : Trading symbol.
// - order_type : Type of pending order (e.g., ORDER_TYPE_BUY_STOP).
// - magic : Magic number identifying strategy group.
//
// Returns:
// - Number of matching pending orders.
// ---------------------------------------------------------------------
int OrderTracker::count_pending_orders(string symbol, ENUM_ORDER_TYPE order_type, long magic) {
int count = 0;
for (int i = OrdersTotal() - 1; i >= 0; i--) {
if (m_order.SelectByIndex(i)) {
if (OrderGetInteger(ORDER_MAGIC) == magic && OrderGetString(ORDER_SYMBOL) == symbol) {
if (m_order.OrderType() == order_type) {
count++;
}
}
}
}
return count;
}
+43
View File
@@ -0,0 +1,43 @@
class StopLogic {
public:
double sl_specified_value_switch(string sl_mode, double inp_sl_var, double value);
double tp_specified_value_switch(string tp_mode, double inp_tp_var, double value);
};
// ---------------------------------------------------------------------
// Selects stop loss value based on SL mode.
//
// Parameters:
// - sl_mode : Stop loss mode ("SL_SPECIFIED_VALUE", etc).
// - inp_sl_var : User-input SL value (pips, percent, etc).
// - value : Directly specified SL value.
//
// Returns:
// - `value` if SL mode is "SL_SPECIFIED_VALUE", otherwise `inp_sl_var`.
// ---------------------------------------------------------------------
double StopLogic::sl_specified_value_switch(string sl_mode, double inp_sl_var, double value) {
if (sl_mode == "SL_SPECIFIED_VALUE") {
return value;
} else {
return inp_sl_var;
}
}
// ---------------------------------------------------------------------
// Selects take profit value based on TP mode.
//
// Parameters:
// - tp_mode : Take profit mode ("TP_SPECIFIED_VALUE", etc).
// - inp_tp_var : User-input TP value (pips, percent, etc).
// - value : Directly specified TP value.
//
// Returns:
// - `value` if TP mode is "TP_SPECIFIED_VALUE", otherwise `inp_tp_var`.
// ---------------------------------------------------------------------
double StopLogic::tp_specified_value_switch(string tp_mode, double inp_tp_var, double value) {
if (tp_mode == "TP_SPECIFIED_VALUE") {
return value;
} else {
return inp_tp_var;
}
}
+245
View File
@@ -0,0 +1,245 @@
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/Utils/MarketDataUtils.mqh>
class DrawdownControl : public CObject {
protected:
CTrade trade;
MarketDataUtils m_utils;
string data_file;
double daily_max_dd_per;
string daily_reset_time;
bool print_statments;
double acc_max_dd_per;
double equaty_control_high;
double equaty_control_low;
double daily_equity_start;
double daily_max_dd_target;
bool daily_dd_limit_reached;
bool write_global_var_data();
bool print_messages();
public:
void 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);
bool determine_daily_dd_limit();
double 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 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;
acc_max_dd_per = inp_acc_max_dd_per;
daily_max_dd_per = inp_daily_max_dd_per;
daily_reset_time = inp_daily_reset_time;
print_statments = inp_print_statments;
// If no data file exisits, create one and set global vairiables:
if(FileIsExist(data_file) == false) {
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100));
daily_dd_limit_reached = false;
equaty_control_high = 9999999;
equaty_control_low = 0;
write_global_var_data();
}
// If file exisits read file:
if(FileIsExist(data_file) == true) {
int file_handle = FileOpen(data_file, FILE_READ | FILE_ANSI | FILE_TXT);
if(file_handle == INVALID_HANDLE) {
Print("Error opening file: ", data_file);
}
// If data file is older than 24h 10min create a new file and reset global vars:
long modifided_date = FileGetInteger(file_handle, FILE_MODIFY_DATE);
long time_delta = ((long)TimeCurrent() - modifided_date) / 60;
if(time_delta >= 1450) {
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100));
daily_dd_limit_reached = false;
equaty_control_high = equaty_control_high;
equaty_control_low = equaty_control_low;
write_global_var_data();
Print(data_file, " is older than 24h and 10min; global vars reset!");
}
// If data file is younger than 24h+10 min read data and set global vars:
else {
daily_equity_start = (double)FileReadString(file_handle, 0);
daily_max_dd_target = (double)FileReadString(file_handle, 1);
daily_dd_limit_reached = FileReadBool(file_handle);
equaty_control_high = (double)FileReadString(file_handle, 3);
equaty_control_low = (double)FileReadString(file_handle, 4);;
}
FileClose(file_handle);
}
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:
string ct = TimeToString(TimeCurrent(), TIME_MINUTES);
if(ct == daily_reset_time) {
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
daily_max_dd_target = (daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100)));
daily_dd_limit_reached = false;
write_global_var_data();
print_messages();
}
// If in drawdown close all positions and delete orders
if(daily_dd_limit_reached || AccountInfoDouble(ACCOUNT_EQUITY) <= daily_max_dd_target) {
if(daily_dd_limit_reached == false) {
daily_dd_limit_reached = true;
write_global_var_data();
print_messages();
}
for(int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
trade.PositionClose(ticket);
}
for(int i = OrdersTotal() - 1; i >= 0; i--) {
ulong ticket = OrderGetTicket(i);
trade.OrderDelete(ticket);
}
}
return daily_dd_limit_reached;
}
// ---------------------------------------------------------------------
// 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));
double lot_factor;
// Interpolate to find lot factor between given min and max values.
if (account_value < acc_equity_start){
double acc_equity_min = acc_equity_start - (acc_equity_start * (acc_max_dd_per / 100));
double y1 = min_lot_factor;
double y2 = max_lot_factor;
double x1 = acc_equity_min;
double x = account_value;
double x2 = acc_equity_start;
lot_factor = y1 + (x - x1) * ((y2 - y1) / (x2 - x1));
}
else if(account_value >= acc_equity_start) {
if(dynm_lot_factor=true){
lot_factor = lot_correction_dynamic(dlf_trail_per, min_lot_factor, max_lot_factor);
}
else {
lot_factor = max_lot_factor;
}
}
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));
double trail_point = account_value - (account_value * (acc_dd_percent / 100));
if(equaty_control_low < trail_point){
equaty_control_low = trail_point;
}
if(equaty_control_high < account_value){
equaty_control_high = account_value;
}
if(account_value < equaty_control_low){
equaty_control_low = account_value;
equaty_control_high = account_value + (account_value * (acc_dd_percent / 100));
}
// back-up to file every hour:
if(m_utils.is_new_bar(_Symbol, PERIOD_H1) == true){
write_global_var_data();
}
// Linear interpolation:
double y1 = min_lot_factor;
double y2 = max_lot_factor;
double x1 = equaty_control_low;
double x = account_value;
double x2 = equaty_control_high;
double y = y1 + (x - x1) * ((y2 - y1) / (x2 - x1));
return y;
}
bool DrawdownControl::write_global_var_data() {
int file_handle = FileOpen(data_file, FILE_WRITE | FILE_ANSI | FILE_TXT);
FileWrite(file_handle, daily_equity_start);
FileWrite(file_handle, daily_max_dd_target);
FileWrite(file_handle, daily_dd_limit_reached);
FileClose(file_handle);
Print(data_file, " written");
return true;
}
bool DrawdownControl::print_messages() {
if(print_statments == true) {
Print("TimeCurrent(): ", TimeToString(TimeCurrent()));
Print("Daily Equity Start: ", (int)daily_equity_start);
Print("Current Equity: ", (int)AccountInfoDouble(ACCOUNT_EQUITY));
Print("Daily Drawdown Limit: ", (int)daily_max_dd_target, " (", daily_max_dd_per, "%) of DES");
Print("Daily Drawdown Limit Hit: ", daily_dd_limit_reached);
}
return true;
}
+501
View File
@@ -0,0 +1,501 @@
#property library
#include <Trade/Trade.mqh>
#include <MyLibs/Utils/TimeZones.mqh>
class RangeCalculator : public CObject{
protected:
TimeZones tz;
bool days_initlised;
bool range_initlised;
string symbol;
ENUM_TIMEFRAMES calc_period;
string inp_r_start_string;
int r_duration;
int r_expire;
int r_close;
string inp_timezone;
bool sun;
bool mon;
bool tue;
bool wed;
bool thu;
bool fri;
bool sat;
bool plot_range;
datetime start_time; // Start of the range
datetime end_time; // end of the range
datetime order_expire_time; // end of the range
datetime close_time; // Close time
double high; // high of the range
double low; // low of the range
double mid; // mid of the range
bool f_entry; // flag if we are inside of the range
bool f_high_breakout; // flag if a high breakout occurred
bool f_low_breakout; // flag if a low breakout occurred
bool above_last;
bool above_current;
bool below_last;
bool below_current;
// private functions
void update_objects();
void draw_objects();
void define_new_range();
bool convert_input_time_strings(string t1, string t2, string t3, string t4);
public:
void calculate_range();
double get_range_high();
double get_range_low();
double get_range_mid();
datetime get_range_start();
datetime get_range_end();
datetime get_order_expire_time();
datetime get_range_close();
bool get_range_high_breakout();
bool get_range_low_breakout();
bool initilise_range(string inp_symbol, ENUM_TIMEFRAMES _calc_period, string t0, string t1, string t2, string t3, string time_zone, bool plot_range_inp);
void range_days(bool _inp_sun, bool _inp_mon, bool _inp_tue, bool _inp_wed, bool _inp_thu, bool _inp_fri, bool _inp_sat);
};
// ---------------------------------------------------------------------
// 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;
tue = _inp_tue;
wed = _inp_wed;
thu = _inp_thu;
fri = _inp_fri;
sat = _inp_sat;
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;
symbol = inp_symbol;
calc_period =_calc_period;
plot_range = plot_range_inp;
start_time = 0;
end_time = 0;
close_time = 0;
high = 0;
low = DBL_MAX;
mid = 0;
f_entry = false;
f_high_breakout = false;
f_low_breakout = false;
above_last = false;
above_current= false;
below_last= false;
below_current= false;
if(!days_initlised){
sun = true;
mon = true;
tue = true;
wed = true;
thu = true;
fri = true;
sat = true;
}
range_initlised = true;
bool corret_inputs = convert_input_time_strings(t1, t2, t3, t4);
if(corret_inputs = false){
return false;
}
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);
datetime _t2 = StringToTime(t2);
datetime _t3 = StringToTime(t3);
datetime _t4 = StringToTime(t4);
if(_t1 > _t2){
_t2 = _t2 + PeriodSeconds(PERIOD_D1);
_t3 = _t3 + PeriodSeconds(PERIOD_D1);
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
}
if(_t2 > _t3){
_t3 = _t3 + PeriodSeconds(PERIOD_D1);
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
}
if(_t3 > _t4){
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
}
r_duration = (int)(_t2 - _t1);
r_expire = (int)(_t3 - _t1);
r_close = (int)(_t4 - _t1);
if(_t4 - _t1 >= PeriodSeconds(PERIOD_D1)){
Alert("INCORRECT RANGE INPUTS!");
return false;
}
return true;
}
// ---------------------------------------------------------------------
// Returns the high value of the current range.
//
// Returns:
// - High price of the range.
// ---------------------------------------------------------------------
double RangeCalculator::get_range_high(){
return high;
};
// ---------------------------------------------------------------------
// Returns the low value of the current range.
//
// Returns:
// - Low price of the range.
// ---------------------------------------------------------------------
double RangeCalculator::get_range_low(){
return low;
};
// ---------------------------------------------------------------------
// 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;
};
// ---------------------------------------------------------------------
// 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;
};
// ---------------------------------------------------------------------
// 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;
};
void RangeCalculator::calculate_range(){
f_high_breakout = false;
f_low_breakout = false;
double last_bar_high = iHigh(symbol, calc_period, 1); // shift 1 because 0 = live candle:
double last_bar_low = iLow(symbol, calc_period, 1); // shift 1 because 0 = live candle:
// range calculation
if(TimeCurrent() >= start_time && TimeCurrent() <= end_time){
// set flag
f_entry = true;
// new high
if(last_bar_high > high){
high = last_bar_high;
mid = (high + low)/2;
if(plot_range){
update_objects();
}
}
// new low
if(last_bar_low < low){
low = last_bar_low;
mid = (high + low)/2;
if(plot_range){
update_objects();
}
}
}
// calculate new reange if
if( (TimeCurrent() >= close_time) // close time reached
|| (end_time == 0) // range not calculated yet
|| (end_time !=0 && TimeCurrent() > end_time && !f_entry) // there was a range calculated but no tick inside.
){
define_new_range();
}
// check if we are after the range end
if(TimeCurrent() >= end_time && end_time > 0 && f_entry){
if(!f_high_breakout && last_bar_high >= high){
above_last = above_current;
above_current= true;
if(above_last==false && above_current == true){
f_high_breakout = true;
}
else(f_high_breakout = false);
}
if(!f_low_breakout && last_bar_low >= low){
below_last = below_current;
below_current = true;
if(below_last == false && below_current == true){
f_low_breakout = true;
}
else(f_low_breakout = false);
}
}
}
void RangeCalculator::define_new_range(){
// reset range vars
start_time = 0;
end_time = 0;
order_expire_time = 0;
close_time = 0;
high = 0;
low = INT_MAX;
mid = 0;
f_entry = false;
// calculate range start time:
datetime r_st = StringToTime(inp_r_start_string);
start_time = tz.timezone_conversions(inp_timezone, r_st, "Broker");
for(int i=0; i<8; i++){
MqlDateTime tmp;
TimeToStruct(start_time,tmp);
int dow = tmp.day_of_week;
if(TimeCurrent()>=start_time
|| (dow==0 && !sun)
|| (dow==1 && !mon)
|| (dow==2 && !tue)
|| (dow==3 && !wed)
|| (dow==4 && !thu)
|| (dow==5 && !fri)
|| (dow==6 && !sat)
){
start_time += PeriodSeconds(PERIOD_D1);
}
}
end_time = start_time + r_duration;
order_expire_time = start_time + r_expire;
close_time = start_time + r_close;
if(plot_range){
draw_objects();
}
}
void RangeCalculator::update_objects(){
string name = "Range Mid " + (string)start_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, mid);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, mid);
// ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid");
name = "Order expire " + (string)order_expire_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "Range start " + (string)start_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "Range end " + (string)end_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
datetime rct = r_close>=0 ? close_time : INT_MAX;
name = "Range close " + (string)rct;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "Range High " + (string)rct;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, high);
name = "Range Low " + (string)rct;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, low);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
name = "range box "+ (string)start_time;
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,0, high);
ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,1, low);
}
void RangeCalculator::draw_objects(){
datetime rct = r_close>=0 ? close_time : INT_MAX;
// Range mid line
string name = "Range Mid " + (string)start_time;;
ObjectCreate(NULL, name, OBJ_TREND, 0, start_time, mid, rct, mid);
ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid" + (string)mid);
ObjectSetInteger(NULL, name, OBJPROP_COLOR, clrGray);
ObjectSetInteger(NULL, name, OBJPROP_WIDTH, 1);
ObjectSetInteger(NULL, name, OBJPROP_STYLE, STYLE_DOT);
// order lines
string name2 = "Order expire " + (string)order_expire_time;
ObjectCreate(NULL, name2, OBJ_TREND, 0, order_expire_time, low, order_expire_time, high);
ObjectSetString(NULL, name2, OBJPROP_TOOLTIP, "start of the range \n" + TimeToString(order_expire_time,TIME_DATE|TIME_MINUTES));
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, C'139,41,41');
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range start " + (string)start_time;
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, start_time, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range end " + (string)end_time;
ObjectCreate(NULL, name2, OBJ_TREND, 0, end_time, low, end_time, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range close " + (string)rct;
ObjectCreate(NULL, name2, OBJ_TREND, 0, rct, low, rct, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range High " + (string)rct;
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, high, rct, high);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
name2 = "Range Low " + (string)rct;
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, rct, low);
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
ObjectSetInteger(NULL, name2 ,OBJPROP_BACK, true);
// Box
name = "range box " + (string)start_time;
ObjectCreate(NULL, name, OBJ_RECTANGLE, 0, start_time, high, end_time, low);
ObjectSetString(NULL,name,OBJPROP_TOOLTIP,"\n");
ObjectSetInteger(NULL, name,OBJPROP_COLOR, C'128,177,173');
ObjectSetInteger(NULL, name,OBJPROP_FILL, true);
ObjectSetInteger(NULL, name,OBJPROP_BACK, true);
ObjectCreate(NULL, name + " ", OBJ_RECTANGLE, 0, end_time, high, rct, low);
ObjectSetString(NULL, name+ " ", OBJPROP_TOOLTIP, "\n");
ObjectSetInteger(NULL, name + " ",OBJPROP_FILL, true);
ObjectSetInteger(NULL, name + " ",OBJPROP_COLOR, C'165,220,215' );
ObjectSetInteger(NULL, name + " ",OBJPROP_BACK, true);
ChartRedraw();
}
+91
View File
@@ -0,0 +1,91 @@
#include <Trade/SymbolInfo.mqh>
class AtrHandleManager {
private:
struct AtrEntry {
string symbol;
ENUM_TIMEFRAMES tf;
int period;
int handle;
};
AtrEntry cache[]; // internal cache of ATR handles
public:
// ---------------------------------------------------------------------
// 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)
return cache[i].handle;
}
int handle = iATR(symbol, tf, period);
if (handle == INVALID_HANDLE) {
Print("Failed to create ATR handle for ", symbol);
return INVALID_HANDLE;
}
AtrEntry entry = { symbol, tf, period, handle };
ArrayResize(cache, ArraySize(cache) + 1);
cache[ArraySize(cache) - 1] = entry;
return handle;
}
// ---------------------------------------------------------------------
// 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) {
PrintFormat("Invalid ATR handle for %s (TF=%d, Period=%d)", symbol, tf, period);
return EMPTY_VALUE;
}
double buffer[];
ArraySetAsSeries(buffer, true);
if (CopyBuffer(handle, 0, shift, 1, buffer) != 1 || buffer[0] == EMPTY_VALUE) {
PrintFormat("Failed to read ATR buffer for %s shift=%d", symbol, shift);
return EMPTY_VALUE;
}
return buffer[0];
}
// ---------------------------------------------------------------------
// 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)
IndicatorRelease(cache[i].handle);
}
ArrayResize(cache, 0);
}
};
+43
View File
@@ -0,0 +1,43 @@
#include <Object.mqh>
class ChartUtils : public CObject {
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 (!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();
}
Binary file not shown.
+41
View File
@@ -0,0 +1,41 @@
enum LOT_MODE{
LOT_MODE_FIXED, // Fixed Lot Size
LOT_MODE_PCT_ACCOUNT, // Percent of Account (fixed)
LOT_MODE_PCT_RISK // Percent of Account at Risk (from SL)
};
enum SL_MODE{
SL_FIXED_PIPS, // Fixed Pips
SL_FIXED_PERCENT, // Fixed Percent
SL_ATR_MULTIPLE, // ATR Multiple
SL_SPECIFIED_VALUE, // Bespoke calculation in code
NO_STOPLOSS, // No Stop-loss
SL_BREAKEVEN, // Breakeven
};
enum TP_MODE{
TP_FIXED_PIPS, // Fixed Pips
TP_FIXED_PERCENT, // Fixed Percent
TP_ATR_MULTIPLE, // ATR Multiple
TP_SL_MULTIPLE, // Multiple of Risk (from sl)
TP_SPECIFIED_VALUE, // Bespoke calculation in code
NO_TAKE_PROFIT, // No Take-Profit
};
enum TSL_MODE{
TSL_ATR_MULTIPLE, // ATR Multiple
TSL_FIXED_PIPS, // Fixed Pips
TSL_FIXED_PERCENT, // Fixed Percent
};
enum TIME_ZONES{
NY, // New York
Lon, // London
Ffm, // Frankfurt
Syd, // Sidney
Mosc, // Moscow
Tok, // Tokyo - no DST
};
enum MULTI_SYM_MODE{
MULTI_SYM_CHART, // Chart Symbol only
MULTI_SYM_FX_B5, // FX Benchmark 5
MULTI_SYM_FX_28 // FX 28 Majors
};
+148
View File
@@ -0,0 +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);
protected:
datetime previousTimes[]; // Stores last recorded open time per key
string bar_keys[]; // Keys are symbol+TF combinations, e.g. "EURUSD_PERIOD_H1"
};
// ---------------------------------------------------------------------
// 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;
}
// ---------------------------------------------------------------------
// 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);
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;
}
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;
}
}
return false;
}
// ---------------------------------------------------------------------
// 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);
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;
}
return val[0];
}
// ---------------------------------------------------------------------
// 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;
}
// ---------------------------------------------------------------------
// 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);
if (price_side == 1) return ask;
if (price_side == 2) return bid;
return 0.0;
}
+82
View File
@@ -0,0 +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:
// ---------------------------------------------------------------------
// Internal struct to associate a symbol with a SignalStateTracker.
// ---------------------------------------------------------------------
struct SymbolTracker {
string symbol;
SignalStateTracker* tracker;
};
SymbolTracker trackers[]; // Dynamic array of symbol-tracker mappings
// ---------------------------------------------------------------------
// 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;
}
return -1;
}
public:
// ---------------------------------------------------------------------
// Destructor. Cleans up allocated memory when the object is destroyed.
// ---------------------------------------------------------------------
~MultiSymbolSignalTracker() {
clear();
}
// ---------------------------------------------------------------------
// 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;
// Create new tracker
SignalStateTracker* tracker = new SignalStateTracker();
SymbolTracker item;
item.symbol = symbol;
item.tracker = tracker;
ArrayResize(trackers, ArraySize(trackers) + 1);
trackers[ArraySize(trackers) - 1] = item;
return tracker;
}
// ---------------------------------------------------------------------
// 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;
}
ArrayResize(trackers, 0);
}
};
+74
View File
@@ -0,0 +1,74 @@
#include <MyLibs/Utils/AtrHandleManager.mqh>
// ---------------------------------------------------------------------
// 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:
AtrHandleManager* atr_manager;
// ---------------------------------------------------------------------
// 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);
}
// ---------------------------------------------------------------------
// 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:
int handles[];
// ---------------------------------------------------------------------
// Adds a handle to the internal tracking list.
// ---------------------------------------------------------------------
void add_handle(int handle) {
int size = ArraySize(handles);
ArrayResize(handles, size + 1);
handles[size] = handle;
}
// ---------------------------------------------------------------------
// 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)
IndicatorRelease(handles[i]);
}
ArrayFree(handles);
}
// ---------------------------------------------------------------------
// Releases any cached ATR handles using the external AtrHandleManager.
// ---------------------------------------------------------------------
void release_internal_handles() {
if (atr_manager != NULL)
atr_manager.release_handles();
}
};
+128
View File
@@ -0,0 +1,128 @@
class SignalStateTracker {
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 the signal tracker to a reset state.
//
// Logic:
// - Sets both long and short signal indices to -1000.
// ---------------------------------------------------------------------
SignalStateTracker() {
reset();
}
// ---------------------------------------------------------------------
// 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 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;
} else if (last_signal_long > 0) {
last_signal_long++;
}
if (signal_short) {
last_signal_short = 1;
} else if (last_signal_short > 0) {
last_signal_short++;
}
}
// ---------------------------------------------------------------------
// 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 {
return has_long_signal() && (1 - last_signal_long <= max_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 {
return has_short_signal() && (1 - last_signal_short <= max_bars);
}
// ---------------------------------------------------------------------
// 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;
}
// ---------------------------------------------------------------------
// 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 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 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;
}
};
+176
View File
@@ -0,0 +1,176 @@
#property library
#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 required_close;
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);
};
// ---------------------------------------------------------------------
// 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
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);
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);
}
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;
}
// ---------------------------------------------------------------------
// 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
// 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);
// 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);
return tREQ;
}
// ---------------------------------------------------------------------
// 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;
}
// ---------------------------------------------------------------------
// 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 -= PeriodSeconds(PERIOD_D1);
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);
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;
}
+84
View File
@@ -0,0 +1,84 @@
#include <MyLibs/Utils/TimeZones.mqh>
class TradeSessionUtils {
protected:
TimeZones tz; // For handling timezone conversion
bool in_window; // Whether the current time is in the allowed window
datetime start_time; // Session start time (converted to Broker time)
datetime end_time; // Session end time (converted to Broker time)
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
// Handle overnight windows (e.g. 22:0001:00)
if (_t1 > _t2) {
_t2 = _t2 + PeriodSeconds(PERIOD_D1);
}
int w_duration = (int)(_t2 - _t1); // Duration of the session in seconds
// Check if we're currently within the window
if (TimeCurrent() >= start_time && TimeCurrent() <= end_time) {
in_window = true;
}
// If we've moved beyond the previous window, define a new one
if (TimeCurrent() >= end_time) {
in_window = false;
// Convert start time to broker time based on user timezone input
start_time = tz.timezone_conversions(time_zone, StringToTime(t1), "Broker");
// If we've already passed today's start time, push it to tomorrow
if (TimeCurrent() >= start_time) {
start_time += PeriodSeconds(PERIOD_D1);
}
// End time is relative to updated start time
end_time = start_time + w_duration;
// Plot vertical lines if requested
if (plot_range_inp) {
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;
}
+217
View File
@@ -0,0 +1,217 @@
#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:
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);
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);
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);
};
// ---------------------------------------------------------------------
// 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;
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;
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;
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;
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;
double atr = get_atr(symbol, atr_period, tf, i);
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);
if (ObjectFind(0, upper_name) < 0) {
ObjectCreate(0, upper_name, OBJ_TREND, 0, time1, upper, time2, upper);
ObjectSetInteger(0, upper_name, OBJPROP_COLOR, line_color);
ObjectSetInteger(0, upper_name, OBJPROP_WIDTH, width);
ObjectSetInteger(0, upper_name, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, upper_name, OBJPROP_BACK, true);
ObjectSetInteger(0, upper_name, OBJPROP_SELECTED, false);
}
if (ObjectFind(0, lower_name) < 0) {
ObjectCreate(0, lower_name, OBJ_TREND, 0, time1, lower, time2, lower);
ObjectSetInteger(0, lower_name, OBJPROP_COLOR, line_color);
ObjectSetInteger(0, lower_name, OBJPROP_WIDTH, width);
ObjectSetInteger(0, lower_name, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, lower_name, OBJPROP_BACK, true);
ObjectSetInteger(0, lower_name, OBJPROP_SELECTED, false);
}
if (ObjectFind(0, middle_name) < 0) {
ObjectCreate(0, middle_name, OBJ_TREND, 0, time1, trendline, time2, trendline);
ObjectSetInteger(0, middle_name, OBJPROP_COLOR, line_color);
ObjectSetInteger(0, middle_name, OBJPROP_WIDTH, width);
ObjectSetInteger(0, middle_name, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, middle_name, OBJPROP_BACK, true);
ObjectSetInteger(0, middle_name, OBJPROP_SELECTED, false);
}
}
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);
}
+76
View File
@@ -0,0 +1,76 @@
#include <MyLibs/Utils/MarketDataUtils.mqh>
// ---------------------------------------------------------------------
// CLASS: TrendlineAnalyser
// ---------------------------------------------------------------------
// A utility class to detect price crossovers with a trendline buffer.
// Supports both crossover detection and trend direction checks.
// ---------------------------------------------------------------------
class TrendlineAnalyser {
private:
MarketDataUtils market_data_utils;
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);
}