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