Files
mt5-quant-lib/Orders/CalculatePositionData.mqh
T

303 lines
9.5 KiB
Plaintext
Raw Normal View History

2025-07-04 16:56:12 +02:00
#include <MyLibs/Utils/MarketDataUtils.mqh>
2025-07-07 15:43:17 +02:00
#include <MyLibs/Utils/TimeZones.mqh>
#include <Trade/Trade.mqh>
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
class CalculatePositionData : public CObject {
protected:
CTrade trade;
CPositionInfo position;
MarketDataUtils mdu;
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
bool check_lots(double& lots, string symbol);
bool normalise_price(double price, double& normalizedPrice, string symbol);
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
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);
2025-07-04 16:56:12 +02:00
};
2025-07-07 15:43:17 +02:00
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
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
double sl = 0;
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
if (mode_sl == "NO_STOPLOSS") {
sl = 0;
2025-07-04 16:56:12 +02:00
}
2025-07-07 15:43:17 +02:00
if (mode_sl == "SL_BREAKEVEN") {
2025-07-04 16:56:12 +02:00
// https://www.youtube.com/watch?v=idPulZ3_iR0
Alert("Not implemented yet yet");
}
2025-07-07 15:43:17 +02:00
if (mode_sl == "SL_FIXED_PIPS") {
2025-07-04 16:56:12 +02:00
// pips/poins = https://www.mql5.com/en/forum/187757
double adj_point = mdu.adjusted_point(symbol);
2025-07-07 15:43:17 +02:00
if (order_side == 1) {
sl = price - sl_var * adj_point;
if (!normalise_price(sl, sl, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
2025-07-07 15:43:17 +02:00
if (order_side == 2) {
2025-07-04 16:56:12 +02:00
sl = price + sl_var * adj_point;
2025-07-07 15:43:17 +02:00
if (!normalise_price(sl, sl, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
}
2025-07-07 15:43:17 +02:00
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;
}
2025-07-04 16:56:12 +02:00
}
2025-07-07 15:43:17 +02:00
if (order_side == 2) {
2025-07-04 16:56:12 +02:00
sl = sl_var * price / 100.00 + price;
2025-07-07 15:43:17 +02:00
if (!normalise_price(sl, sl, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
}
2025-07-07 15:43:17 +02:00
if (mode_sl == "SL_ATR_MULTIPLE") {
int _atr_handle = iATR(symbol, atr_period, 14);
2025-07-04 16:56:12 +02:00
double atr[];
2025-07-07 15:43:17 +02:00
ArraySetAsSeries(atr, true);
CopyBuffer(_atr_handle, MAIN_LINE, 1, 1, atr);
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
if (order_side == 1) {
sl = price - (atr[0] * sl_var);
if (!normalise_price(sl, sl, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
2025-07-07 15:43:17 +02:00
if (order_side == 2) {
2025-07-04 16:56:12 +02:00
sl = price + (atr[0] * sl_var);
2025-07-07 15:43:17 +02:00
if (!normalise_price(sl, sl, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
}
2025-07-07 15:43:17 +02:00
if (mode_sl == "SL_SPECIFIED_VALUE") {
2025-07-04 16:56:12 +02:00
double adj_point = mdu.adjusted_point(symbol);
2025-07-07 15:43:17 +02:00
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;
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
if (!normalise_price(sl, sl, symbol)) {
return false;
2025-07-04 16:56:12 +02:00
}
}
2025-07-07 15:43:17 +02:00
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;
2025-07-04 16:56:12 +02:00
sl = sl = sl_var;
2025-07-07 15:43:17 +02:00
if (!normalise_price(sl, sl, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
}
return sl;
}
2025-07-07 15:43:17 +02:00
double CalculatePositionData::calculate_take_profit(string symbol, double price, double stoploss, int order_side, string mode_tp,
double _tp_var, ENUM_TIMEFRAMES atr_period) {
2025-07-04 16:56:12 +02:00
// order_side int must be 1 for BUY or 2 for SELL
2025-07-07 15:43:17 +02:00
double tp = 0;
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
if (mode_tp == "NO_TAKE_PROFIT") {
tp = 0;
2025-07-04 16:56:12 +02:00
}
2025-07-07 15:43:17 +02:00
if (mode_tp == "TP_FIXED_PIPS") {
2025-07-04 16:56:12 +02:00
double adj_point = mdu.adjusted_point(symbol);
2025-07-07 15:43:17 +02:00
if (order_side == 1) {
tp = price + _tp_var * adj_point;
if (!normalise_price(tp, tp, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
2025-07-07 15:43:17 +02:00
if (order_side == 2) {
2025-07-04 16:56:12 +02:00
tp = price - _tp_var * adj_point;
2025-07-07 15:43:17 +02:00
if (!normalise_price(tp, tp, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
}
2025-07-07 15:43:17 +02:00
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;
}
2025-07-04 16:56:12 +02:00
}
2025-07-07 15:43:17 +02:00
if (order_side == 2) {
2025-07-04 16:56:12 +02:00
tp = (-1 * _tp_var * price / 100.00) + price;
2025-07-07 15:43:17 +02:00
if (!normalise_price(tp, tp, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
}
2025-07-07 15:43:17 +02:00
if (mode_tp == "TP_ATR_MULTIPLE") {
int _atr_handle = iATR(symbol, atr_period, 14);
2025-07-04 16:56:12 +02:00
double atr[];
2025-07-07 15:43:17 +02:00
ArraySetAsSeries(atr, true);
CopyBuffer(_atr_handle, MAIN_LINE, 1, 1, atr);
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
if (order_side == 1) {
2025-07-04 16:56:12 +02:00
tp = price + (atr[0] * _tp_var);
2025-07-07 15:43:17 +02:00
if (!normalise_price(tp, tp, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
2025-07-07 15:43:17 +02:00
if (order_side == 2) {
2025-07-04 16:56:12 +02:00
tp = price - (atr[0] * _tp_var);
2025-07-07 15:43:17 +02:00
if (!normalise_price(tp, tp, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
}
2025-07-07 15:43:17 +02:00
if (mode_tp == "TP_SL_MULTIPLE") {
if (order_side == 1) {
2025-07-04 16:56:12 +02:00
double sl_size = price - stoploss;
tp = price + (_tp_var * sl_size);
2025-07-07 15:43:17 +02:00
if (!normalise_price(tp, tp, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
2025-07-07 15:43:17 +02:00
if (order_side == 2) {
2025-07-04 16:56:12 +02:00
double sl_size = stoploss - price;
2025-07-07 15:43:17 +02:00
tp = price - (_tp_var * sl_size);
if (!normalise_price(tp, tp, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
}
2025-07-07 15:43:17 +02:00
if (mode_tp == "TP_SPECIFIED_VALUE") {
if (_tp_var != 0) {
2025-07-04 16:56:12 +02:00
double adj_point = mdu.adjusted_point(symbol);
2025-07-07 15:43:17 +02:00
if (order_side == 1) {
double pip_limit = price + 10 * adj_point;
if (_tp_var <= pip_limit) {
tp = pip_limit;
} else
tp = _tp_var;
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
if (!normalise_price(tp, tp, symbol)) {
return false;
2025-07-04 16:56:12 +02:00
}
2025-07-07 15:43:17 +02:00
}
if (order_side == 2) {
double pip_limit = price - 10 * adj_point;
if (_tp_var >= pip_limit) {
tp = pip_limit;
} else
tp = _tp_var;
2025-07-04 16:56:12 +02:00
tp = tp = _tp_var;
2025-07-07 15:43:17 +02:00
if (!normalise_price(tp, tp, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
}
}
}
return tp;
}
2025-07-07 15:43:17 +02:00
double CalculatePositionData::calculate_lots(string symbol, double sl_distance, double price, string mode_lot, double lot_var) {
2025-07-04 16:56:12 +02:00
double lots = 0;
2025-07-07 15:43:17 +02:00
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
2025-07-04 16:56:12 +02:00
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
2025-07-07 15:43:17 +02:00
double volume_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
double account_value =
fmin(fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE)), AccountInfoDouble(ACCOUNT_MARGIN_FREE));
2025-07-04 16:56:12 +02:00
double risk_money = account_value * lot_var / 100;
2025-07-07 15:43:17 +02:00
if (mode_lot == "LOT_MODE_FIXED") {
2025-07-04 16:56:12 +02:00
lots = lot_var;
}
2025-07-07 15:43:17 +02:00
if (mode_lot == "LOT_MODE_PCT_RISK") {
2025-07-04 16:56:12 +02:00
double money_lot_step = (sl_distance / tick_size) * tick_value * volume_step;
2025-07-07 15:43:17 +02:00
lots = MathFloor(risk_money / money_lot_step) * volume_step;
2025-07-04 16:56:12 +02:00
}
2025-07-07 15:43:17 +02:00
if (mode_lot == "LOT_MODE_PCT_ACCOUNT") {
2025-07-04 16:56:12 +02:00
double money_lot_step = (price / tick_size) * tick_value * volume_step;
2025-07-07 15:43:17 +02:00
lots = MathFloor(risk_money / money_lot_step) * volume_step;
2025-07-04 16:56:12 +02:00
}
2025-07-07 15:43:17 +02:00
if (!check_lots(lots, symbol)) {
return false;
}
2025-07-04 16:56:12 +02:00
return lots;
}
2025-07-07 15:43:17 +02:00
bool CalculatePositionData::check_lots(double& lots, string symbol) {
double min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
2025-07-04 16:56:12 +02:00
double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
2025-07-07 15:43:17 +02:00
if (lots < min) {
2025-07-04 16:56:12 +02:00
Print("Lot size will be set to minimum allowed volume");
lots = min;
return true;
}
2025-07-07 15:43:17 +02:00
if (lots > max) {
Print("Lot size greater than maximum allowed volume. lots:", lots, "max:", max);
2025-07-04 16:56:12 +02:00
return false;
}
2025-07-07 15:43:17 +02:00
lots = (int) MathFloor(lots / step) * step;
2025-07-04 16:56:12 +02:00
return true;
}
2025-07-07 15:43:17 +02:00
bool CalculatePositionData::normalise_price(double price, double& normalizedPrice, string symbol) {
2025-07-04 16:56:12 +02:00
double tickSize;
2025-07-07 15:43:17 +02:00
if (!SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE, tickSize)) {
2025-07-04 16:56:12 +02:00
Print("Failed to get tick size");
return false;
}
2025-07-07 15:43:17 +02:00
int symbol_digits = (int) SymbolInfoInteger(symbol, SYMBOL_DIGITS);
normalizedPrice = NormalizeDouble(MathRound(price / tickSize) * tickSize, symbol_digits);
2025-07-04 16:56:12 +02:00
return true;
}
2025-07-07 15:43:17 +02:00
double CalculatePositionData::calculate_trading_cost(string symbol, ulong position_ticket) {
position.SelectByTicket(position_ticket);
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
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);
2025-07-04 16:56:12 +02:00
2025-07-07 15:43:17 +02:00
return trading_cost;
2024-10-25 12:08:52 +02:00
}