Creation of Atr haldle manger and other methods
This commit is contained in:
+123
-45
@@ -1,13 +1,20 @@
|
||||
#include <Trade/Trade.mqh>
|
||||
#include <MyLibs/utils/AtrHandleManager.mqh>
|
||||
|
||||
CTrade trade;
|
||||
AtrHandleManager atr_manager;
|
||||
|
||||
class AdjustPosition {
|
||||
public:
|
||||
void set_breakeven_sl(string symbol, int runner_magic_no, double buffer_points = 5);
|
||||
void set_breakeven_sl_if_tp_crossed(string symbol, int runner_magic_no, double buffer_points = 5);
|
||||
void set_breakeven_if_profit_target_hit(string symbol, int runner_magic_no, double buffer_points = 5);
|
||||
void set_fixed_sl(string symbol, int runner_magic_no, double fixed_sl_price);
|
||||
void set_trailing_sl(string symbol, int runner_magic_no, double sl_offset_points = 5);
|
||||
void trailing_stop_atr(string symbol, int magic_number, ENUM_TIMEFRAMES tf = PERIOD_CURRENT, double activation_mult = 1.0,
|
||||
double trail_mult = 1.0, int atr_period = 14, bool use_bar_close = false);
|
||||
|
||||
private:
|
||||
void set_breakeven_sl_for_ticket(string symbol, ulong ticket, long order_type, double entry_price, double current_sl, double current_tp,
|
||||
int digits, double buffer_price, bool remove_tp);
|
||||
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);
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------
|
||||
@@ -32,65 +39,136 @@ void AdjustPosition::set_breakeven_sl(string symbol, int runner_magic_no, double
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
/**
|
||||
* Check runner trades for virtual take-profit hits and set SL to breakeven if crossed.
|
||||
*
|
||||
* Runner trades are assumed to be opened with no TP and a special comment in the format: "runner_tp:1.10500".
|
||||
* This method parses that comment to determine the virtual TP, and if price has crossed it, moves SL to breakeven ± buffer.
|
||||
*
|
||||
* param symbol: Trading symbol to check positions for
|
||||
* param runner_magic_no: Magic number assigned to runner trades
|
||||
* param buffer_points: Buffer to add/subtract from entry price when setting SL (in points, converted internally)
|
||||
*/
|
||||
void AdjustPosition::set_breakeven_sl_if_tp_crossed(string symbol, int runner_magic_no, double buffer_points) {
|
||||
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK); // Current ask price (for sell comparisons)
|
||||
double bid = SymbolInfoDouble(symbol, SYMBOL_BID); // Current bid price (for buy comparisons)
|
||||
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS); // Symbol's decimal precision
|
||||
double buffer_price = buffer_points * _Point; // Convert buffer from points to price
|
||||
// Check runner trades for virtual TP hits and set SL to breakeven if crossed.
|
||||
// Optimized to avoid unnecessary processing on every OnTimer()/OnTick() call.
|
||||
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);
|
||||
double buffer_price = buffer_points * _Point;
|
||||
double price_margin = 50 * _Point; // avoid premature checking if far from TP
|
||||
|
||||
// --- 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); // Get the position ticket
|
||||
if (!PositionSelectByTicket(ticket)) continue; // Select the position
|
||||
if (PositionGetString(POSITION_SYMBOL) != symbol) continue; // Only process positions for this symbol
|
||||
if ((int) PositionGetInteger(POSITION_MAGIC) != runner_magic_no) continue; // Ensure it matches runner magic
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if (!PositionSelectByTicket(ticket)) continue;
|
||||
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
|
||||
if ((int)PositionGetInteger(POSITION_MAGIC) == runner_magic_no) {
|
||||
has_runner = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_runner) return;
|
||||
|
||||
// --- Get position details ---
|
||||
long order_type = PositionGetInteger(POSITION_TYPE); // POSITION_TYPE_BUY or POSITION_TYPE_SELL
|
||||
double entry = PositionGetDouble(POSITION_PRICE_OPEN); // Entry price of the trade
|
||||
double sl = PositionGetDouble(POSITION_SL); // Current stop loss
|
||||
double tp = PositionGetDouble(POSITION_TP); // Should be 0 for runners
|
||||
string comment = PositionGetString(POSITION_COMMENT); // Read comment to check for virtual TP
|
||||
// --- 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;
|
||||
|
||||
// --- Parse virtual TP from comment ---
|
||||
// Format expected: "runner_tp:1.10500"
|
||||
long order_type = PositionGetInteger(POSITION_TYPE);
|
||||
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double sl = PositionGetDouble(POSITION_SL);
|
||||
double tp = PositionGetDouble(POSITION_TP);
|
||||
string comment = PositionGetString(POSITION_COMMENT);
|
||||
|
||||
|
||||
// --- 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:")); // Extract number part
|
||||
virtual_tp = StringToDouble(tp_str); // Convert to double
|
||||
string tp_str = StringSubstr(comment, StringLen("runner_tp:"));
|
||||
virtual_tp = StringToDouble(tp_str);
|
||||
}
|
||||
|
||||
// Skip if no virtual TP was found or invalid
|
||||
if (virtual_tp <= 0.0) continue;
|
||||
|
||||
// Warn if a TP is still set (runner should not have one)
|
||||
if (virtual_tp <= 0.0) continue; // no valid virtual TP
|
||||
if (tp > 0.0) {
|
||||
PrintFormat("Warning: Runner trade on %s (ticket %d) has a non-zero TP: %.5f (should be 0)", symbol, ticket, tp);
|
||||
PrintFormat("Warning: Runner trade on %s (ticket %d) has TP set: %.5f", symbol, ticket, tp);
|
||||
}
|
||||
|
||||
// --- Check if price has hit virtual 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) // For buys, bid must be ≥ virtual TP
|
||||
tp_hit = true;
|
||||
if (order_type == POSITION_TYPE_SELL && ask <= virtual_tp) // For sells, ask must be ≤ virtual TP
|
||||
tp_hit = true;
|
||||
if (order_type == POSITION_TYPE_BUY && bid >= virtual_tp) tp_hit = true;
|
||||
if (order_type == POSITION_TYPE_SELL && ask <= virtual_tp) tp_hit = true;
|
||||
if (!tp_hit) continue;
|
||||
|
||||
// --- Set breakeven SL if TP was hit ---
|
||||
// Move SL to entry ± buffer. Leave TP unchanged (but should be 0.0 at init for runners)
|
||||
// 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
|
||||
// ---------------------------------------------------------------------
|
||||
// This function updates the stop-loss of runner trades based on ATR.
|
||||
// It only applies to trades with the given magic number and symbol.
|
||||
//
|
||||
// Parameters:
|
||||
// - symbol : The trading symbol.
|
||||
// - _magic_number : Magic number to identify trades.
|
||||
// - tf : Timeframe used for ATR calculation.
|
||||
// - activation_mult: Multiplier to determine when to activate trailing.
|
||||
// - trail_mult : Multiplier to determine trailing distance.
|
||||
// - atr_period : ATR period to use.
|
||||
// - use_bar_close : If true, use bar close instead of live price.
|
||||
//
|
||||
// Logic:
|
||||
// - Trailing starts only after activation distance is reached.
|
||||
// - SL is only updated if it moves closer to price (i.e., improves).
|
||||
// ---------------------------------------------------------------------
|
||||
void AdjustPosition::trailing_stop_atr(string symbol, int _magic_number, ENUM_TIMEFRAMES tf, double activation_mult, double trail_mult, int atr_period, bool use_bar_close) {
|
||||
|
||||
double atr = atr_manager.get_atr_value(symbol, tf, atr_period);
|
||||
if (atr == EMPTY_VALUE) return;
|
||||
|
||||
double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
|
||||
double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
||||
int digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
|
||||
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if (!PositionSelectByTicket(ticket)) continue;
|
||||
if (PositionGetString(POSITION_SYMBOL) != symbol) continue;
|
||||
if ((int) PositionGetInteger(POSITION_MAGIC) != _magic_number) continue;
|
||||
|
||||
long type = PositionGetInteger(POSITION_TYPE);
|
||||
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double sl = PositionGetDouble(POSITION_SL);
|
||||
double price = use_bar_close ? iClose(symbol, tf, 1) : (type == POSITION_TYPE_BUY ? bid : ask);
|
||||
double trail_distance = atr * trail_mult;
|
||||
double activation_distance = atr * activation_mult;
|
||||
|
||||
bool should_trail = (type == POSITION_TYPE_BUY && price >= entry + activation_distance) || (type == POSITION_TYPE_SELL && price <= entry - activation_distance);
|
||||
if (!should_trail) continue;
|
||||
|
||||
double new_sl = (type == POSITION_TYPE_BUY) ? price - trail_distance : price + trail_distance;
|
||||
new_sl = NormalizeDouble(new_sl, digits);
|
||||
|
||||
if ((type == POSITION_TYPE_BUY && sl >= new_sl) || (type == POSITION_TYPE_SELL && sl <= new_sl)) continue;
|
||||
|
||||
if (!trade.PositionModify(ticket, new_sl, PositionGetDouble(POSITION_TP)))
|
||||
PrintFormat("Trailing SL update failed for %s ticket=%d", symbol, ticket);
|
||||
else
|
||||
PrintFormat("Trailing SL updated: %s ticket=%d new SL=%.5f", symbol, ticket, new_sl);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
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) {
|
||||
@@ -112,7 +190,7 @@ void AdjustPosition::set_breakeven_sl_for_ticket(string symbol, ulong ticket, lo
|
||||
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);
|
||||
Print("Runner upgraded to trailing: SL at breakeven, TP removed for ", symbol);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,303 +1,175 @@
|
||||
#include <MyLibs/Utils/MarketDataUtils.mqh>
|
||||
#include <MyLibs/Utils/TimeZones.mqh>
|
||||
#include <MyLibs/Utils/AtrHandleManager.mqh>
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
class CalculatePositionData : public CObject {
|
||||
protected:
|
||||
protected:
|
||||
CTrade trade;
|
||||
CPositionInfo position;
|
||||
MarketDataUtils mdu;
|
||||
AtrHandleManager atr_manager;
|
||||
|
||||
bool check_lots(double& lots, string symbol);
|
||||
bool normalise_price(double price, double& normalizedPrice, string symbol);
|
||||
|
||||
public:
|
||||
double calculate_stoploss(string symbol, double price, int order_side, string _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);
|
||||
public:
|
||||
double calculate_stoploss(string symbol, double price, int order_side, string mode_sl, double sl_var, ENUM_TIMEFRAMES atr_tf);
|
||||
double calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double tp_var, ENUM_TIMEFRAMES atr_tf);
|
||||
double calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var);
|
||||
double calculate_trading_cost(string symbol, ulong position_ticket);
|
||||
double calculate_trading_cost(string symbol, ulong 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 CalculatePositionData::calculate_stoploss(string symbol, double price, int order_side, string mode_sl, double sl_var, ENUM_TIMEFRAMES atr_tf) {
|
||||
double sl = 0;
|
||||
|
||||
if (mode_sl == "NO_STOPLOSS") {
|
||||
sl = 0;
|
||||
}
|
||||
|
||||
if (mode_sl == "SL_BREAKEVEN") {
|
||||
// https://www.youtube.com/watch?v=idPulZ3_iR0
|
||||
Alert("Not implemented yet yet");
|
||||
}
|
||||
if (mode_sl == "NO_STOPLOSS") return 0;
|
||||
|
||||
if (mode_sl == "SL_FIXED_PIPS") {
|
||||
// pips/poins = https://www.mql5.com/en/forum/187757
|
||||
double adj_point = mdu.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;
|
||||
}
|
||||
}
|
||||
sl = (order_side == 1) ? price - sl_var * adj_point : price + sl_var * adj_point;
|
||||
if (!normalise_price(sl, sl, symbol)) return 0;
|
||||
}
|
||||
|
||||
if (mode_sl == "SL_FIXED_PERCENT") {
|
||||
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;
|
||||
}
|
||||
}
|
||||
sl = (order_side == 1) ? price - (sl_var * price / 100.0) : price + (sl_var * price / 100.0);
|
||||
if (!normalise_price(sl, sl, symbol)) return 0;
|
||||
}
|
||||
|
||||
if (mode_sl == "SL_ATR_MULTIPLE") {
|
||||
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;
|
||||
}
|
||||
}
|
||||
double atr = atr_manager.get_atr_value(symbol, atr_tf, 14);
|
||||
if (atr == EMPTY_VALUE) return 0;
|
||||
sl = (order_side == 1) ? price - atr * sl_var : price + atr * sl_var;
|
||||
if (!normalise_price(sl, sl, symbol)) return 0;
|
||||
}
|
||||
|
||||
if (mode_sl == "SL_SPECIFIED_VALUE") {
|
||||
double adj_point = mdu.adjusted_point(symbol);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
double limit_sl = (order_side == 1) ? price - 10 * adj_point : price + 10 * adj_point;
|
||||
sl = (order_side == 1) ? fmax(sl_var, limit_sl) : fmin(sl_var, limit_sl);
|
||||
if (!normalise_price(sl, sl, symbol)) return 0;
|
||||
}
|
||||
|
||||
return sl;
|
||||
}
|
||||
|
||||
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 CalculatePositionData::calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp, double tp_var, ENUM_TIMEFRAMES atr_tf) {
|
||||
double tp = 0;
|
||||
|
||||
if (mode_tp == "NO_TAKE_PROFIT") {
|
||||
tp = 0;
|
||||
}
|
||||
if (mode_tp == "NO_TAKE_PROFIT") return 0;
|
||||
|
||||
if (mode_tp == "TP_FIXED_PIPS") {
|
||||
double adj_point = mdu.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;
|
||||
}
|
||||
}
|
||||
tp = (order_side == 1) ? price + tp_var * adj_point : price - tp_var * adj_point;
|
||||
if (!normalise_price(tp, tp, symbol)) return 0;
|
||||
}
|
||||
|
||||
if (mode_tp == "TP_FIXED_PERCENT") {
|
||||
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;
|
||||
}
|
||||
}
|
||||
tp = (order_side == 1) ? price + tp_var * price / 100.0 : price - tp_var * price / 100.0;
|
||||
if (!normalise_price(tp, tp, symbol)) return 0;
|
||||
}
|
||||
|
||||
if (mode_tp == "TP_ATR_MULTIPLE") {
|
||||
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;
|
||||
}
|
||||
}
|
||||
double atr = atr_manager.get_atr_value(symbol, atr_tf, 14);
|
||||
if (atr == EMPTY_VALUE) return 0;
|
||||
tp = (order_side == 1) ? price + atr * tp_var : price - atr * tp_var;
|
||||
if (!normalise_price(tp, tp, symbol)) return 0;
|
||||
}
|
||||
|
||||
if (mode_tp == "TP_SL_MULTIPLE") {
|
||||
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;
|
||||
}
|
||||
}
|
||||
double sl_size = (order_side == 1) ? price - stoploss : stoploss - price;
|
||||
tp = (order_side == 1) ? price + tp_var * sl_size : price - tp_var * sl_size;
|
||||
if (!normalise_price(tp, tp, symbol)) return 0;
|
||||
}
|
||||
|
||||
if (mode_tp == "TP_SPECIFIED_VALUE") {
|
||||
if (_tp_var != 0) {
|
||||
double adj_point = mdu.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
double adj_point = mdu.adjusted_point(symbol);
|
||||
double limit_tp = (order_side == 1) ? price + 10 * adj_point : price - 10 * adj_point;
|
||||
tp = (order_side == 1) ? fmin(tp_var, limit_tp) : fmax(tp_var, limit_tp);
|
||||
if (!normalise_price(tp, tp, symbol)) return 0;
|
||||
}
|
||||
|
||||
return tp;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
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;
|
||||
double account_value = fmin(fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE)), AccountInfoDouble(ACCOUNT_MARGIN_FREE));
|
||||
double risk_money = account_value * lot_var / 100.0;
|
||||
|
||||
if (mode_lot == "LOT_MODE_FIXED") {
|
||||
lots = lot_var;
|
||||
}
|
||||
|
||||
if (mode_lot == "LOT_MODE_PCT_RISK") {
|
||||
double money_lot_step = (sl_distance / tick_size) * tick_value * volume_step;
|
||||
lots = MathFloor(risk_money / money_lot_step) * volume_step;
|
||||
double money_per_step = (sl_distance / tick_size) * tick_value * volume_step;
|
||||
lots = MathFloor(risk_money / money_per_step) * volume_step;
|
||||
}
|
||||
|
||||
if (mode_lot == "LOT_MODE_PCT_ACCOUNT") {
|
||||
double money_lot_step = (price / tick_size) * tick_value * volume_step;
|
||||
lots = MathFloor(risk_money / money_lot_step) * volume_step;
|
||||
double money_per_step = (price / tick_size) * tick_value * volume_step;
|
||||
lots = MathFloor(risk_money / money_per_step) * volume_step;
|
||||
}
|
||||
|
||||
if (!check_lots(lots, symbol)) {
|
||||
return false;
|
||||
}
|
||||
if (!check_lots(lots, symbol)) return 0;
|
||||
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);
|
||||
Print("Lot size exceeds max for ", symbol);
|
||||
return false;
|
||||
}
|
||||
|
||||
lots = (int) MathFloor(lots / step) * step;
|
||||
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");
|
||||
double tick_size;
|
||||
if (!SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE, tick_size)) {
|
||||
Print("Failed to get tick size for ", symbol);
|
||||
return false;
|
||||
}
|
||||
int symbol_digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
|
||||
normalizedPrice = NormalizeDouble(MathRound(price / tickSize) * tickSize, symbol_digits);
|
||||
|
||||
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
|
||||
normalizedPrice = NormalizeDouble(MathRound(price / tick_size) * tick_size, 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);
|
||||
// double CalculatePositionData::calculate_trading_cost(string symbol, ulong ticket) {
|
||||
// position.SelectByTicket(ticket);
|
||||
|
||||
return trading_cost;
|
||||
}
|
||||
// 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);
|
||||
// }
|
||||
|
||||
+55
-17
@@ -2,11 +2,11 @@
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
class EntryOrders {
|
||||
protected:
|
||||
protected:
|
||||
CTrade trade;
|
||||
CalculatePositionData calc;
|
||||
|
||||
public:
|
||||
public:
|
||||
int count_open_positions(string symbol, int order_side, long _magic_number);
|
||||
|
||||
bool open_buy_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var, string _tp_mode,
|
||||
@@ -15,11 +15,11 @@ class EntryOrders {
|
||||
bool open_sell_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var, string _tp_mode,
|
||||
double tp_var, string _lot_mode, double lot_var, long _magic_number);
|
||||
|
||||
bool open_buy_stop_order(string symbol, bool condition, double entry_price, datetime experation, ENUM_TIMEFRAMES atr_period,
|
||||
bool open_buy_stop_order(string symbol, bool condition, double entry_price, datetime expiration, ENUM_TIMEFRAMES atr_period,
|
||||
string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
|
||||
long _magic_number);
|
||||
|
||||
bool open_sell_stop_order(string symbol, bool condition, double entry_price, datetime experation, ENUM_TIMEFRAMES atr_period,
|
||||
bool open_sell_stop_order(string symbol, bool condition, double entry_price, datetime expiration, ENUM_TIMEFRAMES atr_period,
|
||||
string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
|
||||
long _magic_number);
|
||||
|
||||
@@ -28,6 +28,7 @@ class EntryOrders {
|
||||
|
||||
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);
|
||||
|
||||
};
|
||||
|
||||
int EntryOrders::count_open_positions(string symbol, int order_side, long _magic_number) {
|
||||
@@ -36,8 +37,6 @@ int EntryOrders::count_open_positions(string symbol, int order_side, long _magic
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == _magic_number) {
|
||||
int type = (int) PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
// Count: order_side == 0 (both sides), order_side == 1 (longs only), order_side == 2 (shorts only)
|
||||
if (order_side == 0 || (order_side == 1 && type == POSITION_TYPE_BUY) || (order_side == 2 && type == POSITION_TYPE_SELL)) {
|
||||
count++;
|
||||
}
|
||||
@@ -49,7 +48,6 @@ int EntryOrders::count_open_positions(string symbol, int order_side, long _magic
|
||||
bool EntryOrders::open_buy_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var,
|
||||
string _tp_mode, double tp_var, string _lot_mode, double lot_var, long _magic_number) {
|
||||
if (!condition) return false;
|
||||
|
||||
double current_price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
||||
if (count_open_positions(symbol, 1, _magic_number) > 0) return false;
|
||||
|
||||
@@ -58,15 +56,21 @@ bool EntryOrders::open_buy_orders(string symbol, bool condition, ENUM_TIMEFRAMES
|
||||
double sl_distance = current_price - stop_loss;
|
||||
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
|
||||
|
||||
if (lots <= 0) {
|
||||
Print("Lot calculation failed for ", symbol);
|
||||
return false;
|
||||
}
|
||||
|
||||
trade.SetExpertMagicNumber(_magic_number);
|
||||
string comment = "Magic Number: " + IntegerToString(_magic_number);
|
||||
return trade.PositionOpen(symbol, ORDER_TYPE_BUY, lots, current_price, stop_loss, take_profit, comment);
|
||||
bool result = trade.PositionOpen(symbol, ORDER_TYPE_BUY, lots, current_price, stop_loss, take_profit, comment);
|
||||
if (!result) Print("Trade open failed for BUY ", symbol);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EntryOrders::open_sell_orders(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode, double sl_var,
|
||||
string _tp_mode, double tp_var, string _lot_mode, double lot_var, long _magic_number) {
|
||||
if (!condition) return false;
|
||||
|
||||
double current_price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
||||
if (count_open_positions(symbol, 2, _magic_number) > 0) return false;
|
||||
|
||||
@@ -75,12 +79,19 @@ bool EntryOrders::open_sell_orders(string symbol, bool condition, ENUM_TIMEFRAME
|
||||
double sl_distance = stop_loss - current_price;
|
||||
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
|
||||
|
||||
if (lots <= 0) {
|
||||
Print("Lot calculation failed for ", symbol);
|
||||
return false;
|
||||
}
|
||||
|
||||
trade.SetExpertMagicNumber(_magic_number);
|
||||
string comment = "Magic Number: " + IntegerToString(_magic_number);
|
||||
return trade.PositionOpen(symbol, ORDER_TYPE_SELL, lots, current_price, stop_loss, take_profit, comment);
|
||||
bool result = trade.PositionOpen(symbol, ORDER_TYPE_SELL, lots, current_price, stop_loss, take_profit, comment);
|
||||
if (!result) Print("Trade open failed for SELL ", symbol);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EntryOrders::open_buy_stop_order(string symbol, bool condition, double entry_price, datetime experation, ENUM_TIMEFRAMES atr_period,
|
||||
bool EntryOrders::open_buy_stop_order(string symbol, bool condition, double entry_price, datetime expiration, ENUM_TIMEFRAMES atr_period,
|
||||
string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
|
||||
long _magic_number) {
|
||||
if (!condition) return false;
|
||||
@@ -91,12 +102,19 @@ bool EntryOrders::open_buy_stop_order(string symbol, bool condition, double entr
|
||||
double sl_distance = entry_price - stop_loss;
|
||||
double lots = calc.calculate_lots(symbol, sl_distance, entry_price, _lot_mode, lot_var);
|
||||
|
||||
if (lots <= 0) {
|
||||
Print("Lot calculation failed for BUY STOP ", symbol);
|
||||
return false;
|
||||
}
|
||||
|
||||
trade.SetExpertMagicNumber(_magic_number);
|
||||
string comment = "Magic Number: " + IntegerToString(_magic_number);
|
||||
return trade.BuyStop(lots, entry_price, symbol, stop_loss, take_profit, ORDER_TIME_SPECIFIED, experation, comment);
|
||||
bool result = trade.BuyStop(lots, entry_price, symbol, stop_loss, take_profit, ORDER_TIME_SPECIFIED, expiration, comment);
|
||||
if (!result) Print("BuyStop order failed for ", symbol);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EntryOrders::open_sell_stop_order(string symbol, bool condition, double entry_price, datetime experation, ENUM_TIMEFRAMES atr_period,
|
||||
bool EntryOrders::open_sell_stop_order(string symbol, bool condition, double entry_price, datetime expiration, ENUM_TIMEFRAMES atr_period,
|
||||
string _sl_mode, double sl_var, string _tp_mode, double tp_var, string _lot_mode, double lot_var,
|
||||
long _magic_number) {
|
||||
if (!condition) return false;
|
||||
@@ -107,9 +125,16 @@ bool EntryOrders::open_sell_stop_order(string symbol, bool condition, double ent
|
||||
double sl_distance = stop_loss - entry_price;
|
||||
double lots = calc.calculate_lots(symbol, sl_distance, entry_price, _lot_mode, lot_var);
|
||||
|
||||
if (lots <= 0) {
|
||||
Print("Lot calculation failed for SELL STOP ", symbol);
|
||||
return false;
|
||||
}
|
||||
|
||||
trade.SetExpertMagicNumber(_magic_number);
|
||||
string comment = "Magic Number: " + IntegerToString(_magic_number);
|
||||
return trade.SellStop(lots, entry_price, symbol, stop_loss, take_profit, ORDER_TIME_SPECIFIED, experation, comment);
|
||||
bool result = trade.SellStop(lots, entry_price, symbol, stop_loss, take_profit, ORDER_TIME_SPECIFIED, expiration, comment);
|
||||
if (!result) Print("SellStop order failed for ", symbol);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EntryOrders::open_runner_buy_order_with_virtual_tp(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode,
|
||||
@@ -124,9 +149,16 @@ bool EntryOrders::open_runner_buy_order_with_virtual_tp(string symbol, bool cond
|
||||
double sl_distance = current_price - stop_loss;
|
||||
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
|
||||
|
||||
if (lots <= 0) {
|
||||
Print("Lot calculation failed for runner BUY ", symbol);
|
||||
return false;
|
||||
}
|
||||
|
||||
trade.SetExpertMagicNumber(_magic_number);
|
||||
string comment = StringFormat("runner_tp:%.5f", virtual_tp);
|
||||
return trade.PositionOpen(symbol, ORDER_TYPE_BUY, lots, current_price, stop_loss, 0.0, comment);
|
||||
bool result = trade.PositionOpen(symbol, ORDER_TYPE_BUY, lots, current_price, stop_loss, 0.0, comment);
|
||||
if (!result) Print("Runner BUY order failed for ", symbol);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EntryOrders::open_runner_sell_order_with_virtual_tp(string symbol, bool condition, ENUM_TIMEFRAMES atr_period, string _sl_mode,
|
||||
@@ -138,11 +170,17 @@ bool EntryOrders::open_runner_sell_order_with_virtual_tp(string symbol, bool con
|
||||
|
||||
double stop_loss = calc.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period);
|
||||
double virtual_tp = calc.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
|
||||
|
||||
double sl_distance = stop_loss - current_price;
|
||||
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
|
||||
|
||||
if (lots <= 0) {
|
||||
Print("Lot calculation failed for runner SELL ", symbol);
|
||||
return false;
|
||||
}
|
||||
|
||||
trade.SetExpertMagicNumber(_magic_number);
|
||||
string comment = StringFormat("runner_tp:%.5f", virtual_tp);
|
||||
return trade.PositionOpen(symbol, ORDER_TYPE_SELL, lots, current_price, stop_loss, 0.0, comment);
|
||||
bool result = trade.PositionOpen(symbol, ORDER_TYPE_SELL, lots, current_price, stop_loss, 0.0, comment);
|
||||
if (!result) Print("Runner SELL order failed for ", symbol);
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user