update to several methods and EAs
This commit is contained in:
@@ -31,17 +31,6 @@ bool MarketDataUtils::is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, stri
|
||||
return false; // No new bar
|
||||
}
|
||||
|
||||
// Retrieves the latest value from an indicator buffer (shift 0)
|
||||
double MarketDataUtils::get_latest_buffer_value(int handle) {
|
||||
double val[];
|
||||
ArraySetAsSeries(val, true); // Aligns array with bar indexing (0 = latest)
|
||||
|
||||
if (CopyBuffer(handle, 0, 0, 1, val) == 1)
|
||||
return val[0]; // Latest value at shift 0
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#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
|
||||
struct SymbolTracker {
|
||||
string symbol; // Symbol name (e.g. "EURUSD", "HSI")
|
||||
SignalStateTracker* tracker; // Pointer to the signal tracker for that symbol
|
||||
};
|
||||
|
||||
SymbolTracker trackers[]; // Dynamic array of symbol-tracker pairs
|
||||
|
||||
// Helper function: Find the index of the given symbol in the 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
|
||||
}
|
||||
return -1; // Not found
|
||||
}
|
||||
|
||||
public:
|
||||
// Destructor: automatically called when this object is destroyed (e.g. at EA shutdown)
|
||||
~MultiSymbolSignalTracker() {
|
||||
clear(); // Clean up memory when done
|
||||
}
|
||||
|
||||
// Returns the SignalStateTracker for the given symbol.
|
||||
// If it doesn't exist yet, it creates one and stores it.
|
||||
SignalStateTracker* get_tracker(const string& symbol) {
|
||||
int idx = find_index(symbol);
|
||||
if (idx != -1) return trackers[idx].tracker; // Tracker already exists — return it
|
||||
|
||||
// If not found, create a new tracker for this symbol
|
||||
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.
|
||||
void clear() {
|
||||
for (int i = 0; i < ArraySize(trackers); i++) {
|
||||
delete trackers[i].tracker; // Manually free each dynamically created tracker
|
||||
}
|
||||
ArrayResize(trackers, 0); // Reset array to empty
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
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
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,56 @@
|
||||
class SignalStateTracker {
|
||||
private:
|
||||
int last_signal_long;
|
||||
int last_signal_short;
|
||||
|
||||
public:
|
||||
// Constructor initializes both directions to a default "unset" state.
|
||||
SignalStateTracker() {
|
||||
reset();
|
||||
}
|
||||
|
||||
// Resets the tracked signal bar indexes to an invalid default value (-1000).
|
||||
void reset() {
|
||||
last_signal_long = -1000;
|
||||
last_signal_short = -1000;
|
||||
}
|
||||
|
||||
// Updates the signal for both long and short directions in one call, if the respective triggers are true.
|
||||
// curr_bar should refer to the last CLOSED bar (typically bar index 1).
|
||||
void update_signal(bool trig_long, bool trig_short, int curr_bar=1) {
|
||||
if (trig_long) last_signal_long = curr_bar;
|
||||
if (trig_short) last_signal_short = curr_bar;
|
||||
}
|
||||
|
||||
// Returns true if a long signal occurred within the last `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.
|
||||
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.
|
||||
bool has_long_signal() const {
|
||||
return last_signal_long != -1000;
|
||||
}
|
||||
|
||||
// Returns true if a signal has been recorded for the short direction.
|
||||
bool has_short_signal() const {
|
||||
return last_signal_short != -1000;
|
||||
}
|
||||
|
||||
// Returns the last recorded signal bar index for the long direction.
|
||||
int get_long_signal() const {
|
||||
return last_signal_long;
|
||||
}
|
||||
|
||||
// Returns the last recorded signal bar index for the short direction.
|
||||
int get_short_signal() const {
|
||||
return last_signal_short;
|
||||
}
|
||||
};
|
||||
+108
-65
@@ -1,96 +1,139 @@
|
||||
// File: MyLibs/Utils/AtrBands.mqh
|
||||
#include <MyLibs/Utils/MarketDataUtils.mqh>
|
||||
|
||||
class AtrBands {
|
||||
protected:
|
||||
string symbol;
|
||||
int atr_period;
|
||||
ENUM_TIMEFRAMES timeframe;
|
||||
string prefix;
|
||||
color upper_color;
|
||||
color lower_color;
|
||||
color mid_color;
|
||||
int line_width;
|
||||
bool plot_bands;
|
||||
int atr_handle;
|
||||
private:
|
||||
MarketDataUtils market_data_utils;
|
||||
|
||||
public:
|
||||
AtrBands(string _symbol, int _atr_period = 14, ENUM_TIMEFRAMES _timeframe = PERIOD_CURRENT,
|
||||
color _upper = clrDodgerBlue, color _lower = clrDodgerBlue, color _mid = clrDodgerBlue,
|
||||
int _width = 1, bool plot = true);
|
||||
// Core band accessors
|
||||
double upper_band(string symbol, double trendline_var, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1,
|
||||
color clr = clrDodgerBlue, int width = 1, bool plot = true);
|
||||
|
||||
double upper_band(double baseline, int shift = 1, double mult = 1.0);
|
||||
double lower_band(double baseline, int shift = 1, double mult = 1.0);
|
||||
double middle_band(double baseline, int shift = 1);
|
||||
double lower_band(string symbol, double trendline_var, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1,
|
||||
color clr = clrDodgerBlue, int width = 1, bool plot = true);
|
||||
|
||||
double middle_band(string symbol, double trendline_var, ENUM_TIMEFRAMES tf, int shift = 1, color clr = clrDodgerBlue, int width = 1,
|
||||
bool plot = true);
|
||||
|
||||
// Band checkers
|
||||
bool inside_upper_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1, bool plot = true);
|
||||
|
||||
bool inside_lower_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1, bool plot = true);
|
||||
|
||||
bool crossed_below_upper_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1,
|
||||
bool plot = true);
|
||||
|
||||
bool crossed_above_lower_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult = 1.0, int shift = 1,
|
||||
bool plot = true);
|
||||
protected:
|
||||
double get_atr(int shift);
|
||||
void draw_line(string name, int shift, double price, color clr);
|
||||
double get_atr(string symbol, int atr_period, ENUM_TIMEFRAMES tf, int shift);
|
||||
void draw_line(string name, double price, color clr, int width);
|
||||
};
|
||||
|
||||
// Constructor
|
||||
AtrBands::AtrBands(string _symbol, int _atr_period, ENUM_TIMEFRAMES _timeframe,
|
||||
color _upper, color _lower, color _mid, int _width, bool plot) {
|
||||
symbol = _symbol;
|
||||
atr_period = _atr_period;
|
||||
timeframe = _timeframe;
|
||||
prefix = "ATRBand";
|
||||
upper_color = _upper;
|
||||
lower_color = _lower;
|
||||
mid_color = _mid;
|
||||
line_width = _width;
|
||||
plot_bands = plot;
|
||||
// -------------------
|
||||
// Public Methods
|
||||
// -------------------
|
||||
|
||||
atr_handle = iATR(symbol, timeframe, atr_period);
|
||||
if (atr_handle == INVALID_HANDLE) {
|
||||
Print("Failed to create ATR handle for symbol: ", symbol);
|
||||
}
|
||||
}
|
||||
|
||||
// Public methods
|
||||
double AtrBands::upper_band(double baseline, int shift, double mult) {
|
||||
double atr = get_atr(shift);
|
||||
double upper = baseline + atr * mult;
|
||||
if (plot_bands) draw_line(prefix + "_Upper_" + symbol, shift, upper, upper_color);
|
||||
double AtrBands::upper_band(string symbol, double trendline_var, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift, color clr, int width, bool plot) {
|
||||
double atr = get_atr(symbol, atr_period, tf, shift);
|
||||
double upper = trendline_var + atr * mult;
|
||||
if (plot && shift == 1) draw_line("ATRBand_Upper_" + symbol, upper, clr, width); // persistent line
|
||||
return upper;
|
||||
}
|
||||
|
||||
double AtrBands::lower_band(double baseline, int shift, double mult) {
|
||||
double atr = get_atr(shift);
|
||||
double lower = baseline - atr * mult;
|
||||
if (plot_bands) draw_line(prefix + "_Lower_" + symbol, shift, lower, lower_color);
|
||||
double AtrBands::lower_band(string symbol, double trendline_var, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift, color clr, int width, bool plot) {
|
||||
double atr = get_atr(symbol, atr_period, tf, shift);
|
||||
double lower = trendline_var - atr * mult;
|
||||
if (plot && shift == 1) draw_line("ATRBand_Lower_" + symbol, lower, clr, width); // persistent line
|
||||
return lower;
|
||||
}
|
||||
|
||||
double AtrBands::middle_band(double baseline, int shift) {
|
||||
if (plot_bands) draw_line(prefix + "_Mid_" + symbol, shift, baseline, mid_color);
|
||||
return baseline;
|
||||
double AtrBands::middle_band(string symbol, double trendline_var, ENUM_TIMEFRAMES tf, int shift, color clr, int width, bool plot) {
|
||||
if (plot && shift == 1) draw_line("ATRBand_Mid_" + symbol, trendline_var, clr, width); // persistent line
|
||||
return trendline_var;
|
||||
}
|
||||
|
||||
// Internal: Get ATR value from buffer
|
||||
double AtrBands::get_atr(int shift) {
|
||||
if (atr_handle == INVALID_HANDLE) return 0;
|
||||
bool AtrBands::inside_upper_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift, bool plot) {
|
||||
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, clrDodgerBlue, 1, plot);
|
||||
|
||||
if (price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || upper == EMPTY_VALUE) return false;
|
||||
return (price > trendline_var && price < upper);
|
||||
}
|
||||
|
||||
bool AtrBands::inside_lower_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift, bool plot) {
|
||||
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, clrDodgerBlue, 1, plot);
|
||||
|
||||
if (price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || lower == EMPTY_VALUE) return false;
|
||||
return (price < trendline_var && price > lower);
|
||||
}
|
||||
|
||||
// pull-back inside upper atr band:
|
||||
bool AtrBands::crossed_below_upper_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift, bool plot) {
|
||||
//--- checked bar
|
||||
double price = iClose(symbol, tf, shift);
|
||||
double trendline_var = market_data_utils.get_buffer_value(handle, shift);
|
||||
double atr_upper_band = upper_band(symbol, trendline_var, atr_period, tf, mult, shift, clrDodgerBlue, 1, plot);
|
||||
|
||||
//--- previous bar
|
||||
double prev_price = iClose(symbol, tf, shift + 1);
|
||||
double prev_trendline_var = market_data_utils.get_buffer_value(handle, shift + 1);
|
||||
double prev_atr_upper_band = upper_band(symbol, prev_trendline_var, atr_period, tf, mult, shift + 1, clrDodgerBlue, 1, plot);
|
||||
|
||||
if (price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || atr_upper_band == EMPTY_VALUE || prev_price == EMPTY_VALUE
|
||||
|| prev_trendline_var == EMPTY_VALUE || prev_atr_upper_band == EMPTY_VALUE) return false;
|
||||
|
||||
return (prev_price > prev_atr_upper_band && price < atr_upper_band);
|
||||
}
|
||||
|
||||
// pull-back inside lower atr band:
|
||||
bool AtrBands::crossed_above_lower_band(string symbol, int handle, int atr_period, ENUM_TIMEFRAMES tf, double mult, int shift, bool plot) {
|
||||
//--- checked bar
|
||||
double price = iClose(symbol, tf, shift);
|
||||
double trendline_var = market_data_utils.get_buffer_value(handle, shift);
|
||||
double atr_lower_band = lower_band(symbol, trendline_var, atr_period, tf, mult, shift, clrDodgerBlue, 1, plot);
|
||||
|
||||
//--- previous bar
|
||||
double prev_price = iClose(symbol, tf, shift + 1);
|
||||
double prev_trendline_var = market_data_utils.get_buffer_value(handle, shift + 1);
|
||||
double prev_atr_lower_band = lower_band(symbol, prev_trendline_var, atr_period, tf, mult, shift + 1, clrDodgerBlue, 1, plot);
|
||||
|
||||
if (price == EMPTY_VALUE || trendline_var == EMPTY_VALUE || atr_lower_band == EMPTY_VALUE || prev_price == EMPTY_VALUE
|
||||
|| prev_trendline_var == EMPTY_VALUE || prev_atr_lower_band == EMPTY_VALUE) return false;
|
||||
|
||||
return (prev_price < prev_atr_lower_band && price > atr_lower_band);
|
||||
}
|
||||
|
||||
// -------------------
|
||||
// Internal Helpers
|
||||
// -------------------
|
||||
|
||||
double AtrBands::get_atr(string symbol, int atr_period, ENUM_TIMEFRAMES tf, int shift) {
|
||||
int handle = iATR(symbol, tf, atr_period);
|
||||
if (handle == INVALID_HANDLE) {
|
||||
Print("Failed to create ATR handle for ", symbol);
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double buf[];
|
||||
ArraySetAsSeries(buf, true);
|
||||
if (CopyBuffer(atr_handle, 0, shift, 1, buf) == 1 && buf[0] != EMPTY_VALUE)
|
||||
return buf[0];
|
||||
if (CopyBuffer(handle, 0, shift, 1, buf) == 1 && buf[0] != EMPTY_VALUE) return buf[0];
|
||||
|
||||
return 0;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Internal: Draw line for a given price at a bar
|
||||
void AtrBands::draw_line(string name, int shift, double price, color clr) {
|
||||
datetime time0 = iTime(symbol, timeframe, shift);
|
||||
datetime time1 = time0 + PeriodSeconds(timeframe);
|
||||
|
||||
void AtrBands::draw_line(string name, double price, color clr, int width) {
|
||||
if (ObjectFind(0, name) < 0) {
|
||||
ObjectCreate(0, name, OBJ_TREND, 0, time0, price, time1, price);
|
||||
ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
|
||||
} else {
|
||||
ObjectMove(0, name, 0, time0, price);
|
||||
ObjectMove(0, name, 1, time1, price);
|
||||
ObjectSetDouble(0, name, OBJPROP_PRICE, price);
|
||||
}
|
||||
|
||||
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||||
ObjectSetInteger(0, name, OBJPROP_WIDTH, line_width);
|
||||
ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
|
||||
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DASH);
|
||||
ObjectSetInteger(0, name, OBJPROP_BACK, true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#include <MyLibs/Utils/MarketDataUtils.mqh>
|
||||
//+------------------------------------------------------------------+
|
||||
//| TrendlineSignal: A utility class to detect trendline crossovers |
|
||||
//+------------------------------------------------------------------+
|
||||
class TrendlineAnalyser {
|
||||
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) {
|
||||
cross_long = false;
|
||||
cross_short = false;
|
||||
|
||||
// --- Retrieve prices and trendline values from last 2 closed bars
|
||||
double price = iClose(symbol, PERIOD_CURRENT, 1); // most recent closed bar
|
||||
double prev_price = iClose(symbol, PERIOD_CURRENT, 2); // bar before that
|
||||
|
||||
double trendline = market_data_utils.get_buffer_value(handle, 1); // trendline now
|
||||
double prev_trendline = market_data_utils.get_buffer_value(handle, 2); // 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);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user