update
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
enum CUSTOM_MAX_TYPE {
|
||||
CM_WIN_LOSS_RATIO,
|
||||
CM_WIN_PERCENT
|
||||
};
|
||||
|
||||
class CustomMax : public CObject {
|
||||
protected:
|
||||
double custom_criteria;
|
||||
|
||||
double win_loss_ratio(int min_required_trades);
|
||||
double win_percent_min_trades(int min_required_trades);
|
||||
|
||||
public:
|
||||
double calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type, int min_trades = 0);
|
||||
};
|
||||
|
||||
double CustomMax::calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type, int min_trades) {
|
||||
switch(cm_type) {
|
||||
case CM_WIN_LOSS_RATIO:
|
||||
custom_criteria = win_loss_ratio(min_trades);
|
||||
break;
|
||||
case CM_WIN_PERCENT:
|
||||
custom_criteria = win_percent_min_trades(min_trades);
|
||||
break;
|
||||
default:
|
||||
custom_criteria = 0;
|
||||
break;
|
||||
}
|
||||
return custom_criteria;
|
||||
}
|
||||
|
||||
// Returns the win/loss ratio, with min trades check
|
||||
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
|
||||
return wins / losses;
|
||||
}
|
||||
|
||||
// Returns the win percentage, with min trades check
|
||||
double CustomMax::win_percent_min_trades(int min_required_trades) {
|
||||
double wins = TesterStatistics(STAT_PROFIT_TRADES);
|
||||
double total_trades = TesterStatistics(STAT_TRADES);
|
||||
if((min_required_trades > 0 && total_trades < min_required_trades) || total_trades == 0) return 0;
|
||||
double result = wins / total_trades * 100;
|
||||
if(!MathIsValidNumber(result)) return 0;
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
enum MODE_SPLIT_DATA{
|
||||
NO_SPLIT,
|
||||
ODD_YEARS,
|
||||
EVEN_YEARS,
|
||||
ODD_MONTHS,
|
||||
EVEN_MONTHS,
|
||||
ODD_WEEKS,
|
||||
EVEN_WEEKS
|
||||
};
|
||||
|
||||
class TestDataSplit {
|
||||
public:
|
||||
bool in_test_period(MODE_SPLIT_DATA data_split_method);
|
||||
};
|
||||
|
||||
bool TestDataSplit::in_test_period(MODE_SPLIT_DATA data_split_method) {
|
||||
string result[];
|
||||
string string_tc = TimeToString(TimeCurrent());
|
||||
|
||||
// Extract components from datetime string (assumes YYYY.MM.DD format)
|
||||
ushort u_sep = StringGetCharacter(".", 0);
|
||||
StringSplit(string_tc, u_sep, result);
|
||||
|
||||
bool odd_year = int(result[0]) % 2;
|
||||
bool odd_month = int(result[1]) % 2;
|
||||
|
||||
// Calculate week of the year (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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
#property library
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
enum CUSTOM_MAX_TYPE{
|
||||
CM_WIN_LOSS_RATIO,
|
||||
CM_WIN_PERCENT,
|
||||
CM_WIN_PERCENT_200T,
|
||||
CM_WIN_PERCENT_300T,
|
||||
CM_WIN_PERCENT_400T,
|
||||
CM_WIN_PERCENT_500T,
|
||||
CM_WIN_PERCENT_600T,
|
||||
CM_WIN_PERCENT_700T,
|
||||
CM_WIN_PERCENT_800T,
|
||||
CM_WIN_PERCENT_900T,
|
||||
CM_WIN_PERCENT_1000T,
|
||||
};
|
||||
|
||||
class CustomMax : public CObject{
|
||||
|
||||
protected:
|
||||
double custom_criteria;
|
||||
|
||||
double CustomMax::win_loss_ratio();
|
||||
double CustomMax::win_percent();
|
||||
double CustomMax::win_percent_min_trades(int min_resuired_trades);
|
||||
|
||||
public:
|
||||
double CustomMax::calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type);
|
||||
|
||||
};
|
||||
|
||||
// CM_WIN_LOSS_RATIO,
|
||||
// CM_WIN_PERCENT
|
||||
double CustomMax::calculate_custom_criteria(CUSTOM_MAX_TYPE cm_type){
|
||||
if(cm_type==CM_WIN_LOSS_RATIO){
|
||||
custom_criteria = win_loss_ratio();
|
||||
}
|
||||
if(cm_type==CM_WIN_PERCENT){
|
||||
custom_criteria = win_percent_min_trades(0);
|
||||
}
|
||||
if(cm_type==CM_WIN_PERCENT_200T){
|
||||
custom_criteria = win_percent_min_trades(200);
|
||||
}
|
||||
if(cm_type==CM_WIN_PERCENT_300T){
|
||||
custom_criteria = win_percent_min_trades(300);
|
||||
}
|
||||
if(cm_type==CM_WIN_PERCENT_400T){
|
||||
custom_criteria = win_percent_min_trades(400);
|
||||
}
|
||||
if(cm_type==CM_WIN_PERCENT_500T){
|
||||
custom_criteria = win_percent_min_trades(500);
|
||||
}
|
||||
if(cm_type==CM_WIN_PERCENT_600T){
|
||||
custom_criteria = win_percent_min_trades(600);
|
||||
}
|
||||
if(cm_type==CM_WIN_PERCENT_700T){
|
||||
custom_criteria = win_percent_min_trades(700);
|
||||
}
|
||||
if(cm_type==CM_WIN_PERCENT_800T){
|
||||
custom_criteria = win_percent_min_trades(800);
|
||||
}
|
||||
if(cm_type==CM_WIN_PERCENT_900T){
|
||||
custom_criteria = win_percent_min_trades(900);
|
||||
}
|
||||
if(cm_type==CM_WIN_PERCENT_1000T){
|
||||
custom_criteria = win_percent_min_trades(1000);
|
||||
}
|
||||
return custom_criteria;
|
||||
}
|
||||
|
||||
double CustomMax::win_loss_ratio(){
|
||||
double wins = TesterStatistics(STAT_PROFIT_TRADES);
|
||||
double losses = TesterStatistics(STAT_LOSS_TRADES);
|
||||
return wins/losses;
|
||||
}
|
||||
|
||||
double CustomMax::win_percent_min_trades(int min_resuired_trades){
|
||||
double wins = TesterStatistics(STAT_PROFIT_TRADES);
|
||||
double total_trades = TesterStatistics(STAT_TRADES);
|
||||
double result = wins / total_trades * 100;
|
||||
|
||||
if(!MathIsValidNumber(result) || total_trades<min_resuired_trades){
|
||||
return 0;
|
||||
}
|
||||
|
||||
else{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
#include <Trade/Trade.mqh>
|
||||
#include <MyLibs/Utils/TimeZones.mqh>
|
||||
#include <MyLibs/Utils/MarketDataUtils.mqh>
|
||||
|
||||
class CalculatePositionData : public CObject{
|
||||
|
||||
protected:
|
||||
CTrade trade;
|
||||
CPositionInfo position;
|
||||
MarketDataUtils mdu;
|
||||
|
||||
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);
|
||||
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 = 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;}
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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;}
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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;}
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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;}
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#include <Trade/Trade.mqh>
|
||||
#include <MyLibs/Orders/CalculatePositionData.mqh>
|
||||
|
||||
class EntryOrders {
|
||||
|
||||
protected:
|
||||
CTrade trade;
|
||||
CalculatePositionData calc;
|
||||
double stop_loss;
|
||||
double take_profit;
|
||||
int total_open_buy_orders;
|
||||
int total_open_sell_orders;
|
||||
double current_price;
|
||||
|
||||
int count_open_positions(string symbol, int order_side, long magic_number);
|
||||
|
||||
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_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);
|
||||
};
|
||||
|
||||
|
||||
int EntryOrders::count_open_positions(string symbol, int order_side, long magic_number) {
|
||||
int count = 0;
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if (PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number) {
|
||||
if ((order_side == 1 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ||
|
||||
(order_side == 2 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
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) {
|
||||
current_price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
||||
total_open_buy_orders = count_open_positions(symbol, 1, magic_number);
|
||||
if (total_open_buy_orders == 0) {
|
||||
stop_loss = calc.calculate_stoploss(symbol, current_price, 1, _sl_mode, sl_var, atr_period);
|
||||
take_profit = calc.calculate_take_profit(symbol, current_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
|
||||
double sl_distance = current_price - stop_loss;
|
||||
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
|
||||
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 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) {
|
||||
current_price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
||||
total_open_sell_orders = count_open_positions(symbol, 2, magic_number);
|
||||
if (total_open_sell_orders == 0) {
|
||||
stop_loss = calc.calculate_stoploss(symbol, current_price, 2, _sl_mode, sl_var, atr_period);
|
||||
take_profit = calc.calculate_take_profit(symbol, current_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
|
||||
double sl_distance = stop_loss - current_price;
|
||||
double lots = calc.calculate_lots(symbol, sl_distance, current_price, _lot_mode, lot_var);
|
||||
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 EntryOrders::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) {
|
||||
total_open_buy_orders = count_open_positions(symbol, 1, magic_number);
|
||||
if (total_open_buy_orders == 0) {
|
||||
stop_loss = calc.calculate_stoploss(symbol, entry_price, 1, _sl_mode, sl_var, atr_period);
|
||||
take_profit = calc.calculate_take_profit(symbol, entry_price, stop_loss, 1, _tp_mode, tp_var, atr_period);
|
||||
double sl_distance = entry_price - stop_loss;
|
||||
double lots = calc.calculate_lots(symbol, sl_distance, entry_price, _lot_mode, lot_var);
|
||||
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 EntryOrders::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) {
|
||||
total_open_sell_orders = count_open_positions(symbol, 2, magic_number);
|
||||
if (total_open_sell_orders == 0) {
|
||||
stop_loss = calc.calculate_stoploss(symbol, entry_price, 2, _sl_mode, sl_var, atr_period);
|
||||
take_profit = calc.calculate_take_profit(symbol, entry_price, stop_loss, 2, _tp_mode, tp_var, atr_period);
|
||||
double sl_distance = stop_loss - entry_price;
|
||||
double lots = calc.calculate_lots(symbol, sl_distance, entry_price, _lot_mode, lot_var);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
#include <Trade/Trade.mqh>
|
||||
#include <MyLibs/Utils/TimeZones.mqh>
|
||||
#include <MyLibs/Orders/CalculatePositionData.mqh>
|
||||
|
||||
class ExitOrders {
|
||||
|
||||
protected:
|
||||
CTrade trade;
|
||||
TimeZones tz;
|
||||
CalculatePositionData cpd;
|
||||
|
||||
ulong posTicket;
|
||||
long position_open_time;
|
||||
long first_allowed_close_time;
|
||||
|
||||
public:
|
||||
bool close_buy_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period,long magic_number);
|
||||
bool close_sell_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period,long magic_number);
|
||||
bool daily_timed_exit(string symbol, datetime exit_time, int delay_days, long magic_number);
|
||||
bool daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, string exit_time, string cw_tzone, int delay_days, long magic_number);
|
||||
bool first_profitable_close_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, long magic_number);
|
||||
};
|
||||
|
||||
|
||||
bool ExitOrders::close_buy_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period,
|
||||
long magic_number) {
|
||||
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
posTicket = PositionGetTicket(i);
|
||||
|
||||
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
|
||||
int time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1;
|
||||
|
||||
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
||||
if (condition || (close_bars > 0 && time_difference >= close_bars)) {
|
||||
trade.PositionClose(posTicket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ExitOrders::close_sell_orders(string symbol, bool condition, int close_bars, ENUM_TIMEFRAMES close_bar_period,
|
||||
long magic_number) {
|
||||
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
posTicket = PositionGetTicket(i);
|
||||
|
||||
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
|
||||
int time_difference = Bars(symbol, close_bar_period, PositionGetInteger(POSITION_TIME), TimeCurrent()) - 1;
|
||||
|
||||
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
||||
if (condition || (close_bars > 0 && time_difference >= close_bars)) {
|
||||
trade.PositionClose(posTicket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ExitOrders::daily_timed_exit(string symbol, datetime exit_time, int delay_days, long magic_number) {
|
||||
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
posTicket = PositionGetTicket(i);
|
||||
position_open_time = PositionGetInteger(POSITION_TIME);
|
||||
|
||||
if ((int)position_open_time > 0) {
|
||||
first_allowed_close_time = position_open_time + (delay_days * PeriodSeconds(PERIOD_D1));
|
||||
|
||||
if (TimeCurrent() > first_allowed_close_time && TimeCurrent() >= exit_time) {
|
||||
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
|
||||
trade.PositionClose(posTicket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ExitOrders::daily_timed_profit_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, string exit_time,
|
||||
string cw_tzone, int delay_days, long magic_number) {
|
||||
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
posTicket = PositionGetTicket(i);
|
||||
position_open_time = PositionGetInteger(POSITION_TIME);
|
||||
|
||||
if ((int)position_open_time > 0) {
|
||||
first_allowed_close_time = position_open_time + (delay_days * PeriodSeconds(PERIOD_D1));
|
||||
|
||||
if (TimeCurrent() > first_allowed_close_time) {
|
||||
datetime broker_close_time = tz.timezone_conversions(cw_tzone, StringToTime(exit_time), "Broker");
|
||||
|
||||
if (TimeCurrent() >= broker_close_time &&
|
||||
PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number) {
|
||||
|
||||
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double spread = SymbolInfoDouble(symbol, SYMBOL_ASK) - SymbolInfoDouble(symbol, SYMBOL_BID);
|
||||
double bar_close = iClose(_Symbol, close_bar_period, 1); // shift 1 because 0 is live candle
|
||||
double trading_cost = cpd.calculate_trading_cost(symbol, posTicket);
|
||||
|
||||
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY &&
|
||||
bar_close > (position_open_price + spread + trading_cost)) {
|
||||
trade.PositionClose(posTicket);
|
||||
}
|
||||
|
||||
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL &&
|
||||
bar_close < (position_open_price - spread - trading_cost)) {
|
||||
trade.PositionClose(posTicket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ExitOrders::first_profitable_close_exit(string symbol, ENUM_TIMEFRAMES close_bar_period, long magic_number) {
|
||||
|
||||
position_open_time = PositionGetInteger(POSITION_TIME);
|
||||
first_allowed_close_time = position_open_time + PeriodSeconds(close_bar_period);
|
||||
|
||||
if ((int)position_open_time > 0 && TimeCurrent() > first_allowed_close_time) {
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
posTicket = PositionGetTicket(i);
|
||||
|
||||
if (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number) {
|
||||
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double spread = SymbolInfoDouble(symbol, SYMBOL_ASK) - SymbolInfoDouble(symbol, SYMBOL_BID);
|
||||
double bar_close = iClose(_Symbol, close_bar_period, 1);
|
||||
double trading_cost = cpd.calculate_trading_cost(symbol, posTicket);
|
||||
|
||||
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY &&
|
||||
bar_close > (position_open_price + spread + trading_cost)) {
|
||||
trade.PositionClose(posTicket);
|
||||
}
|
||||
|
||||
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL &&
|
||||
bar_close < (position_open_price - spread - trading_cost)) {
|
||||
trade.PositionClose(posTicket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <Trade/OrderInfo.mqh>
|
||||
#include <Trade/PositionInfo.mqh>
|
||||
|
||||
class OrderTracker {
|
||||
|
||||
protected:
|
||||
COrderInfo m_order;
|
||||
CPositionInfo m_position;
|
||||
|
||||
public:
|
||||
int count_open_positions(string symbol, int order_side, long magic_number);
|
||||
int count_all_positions(string symbol, long magic_number);
|
||||
int count_pending_orders(string symbol, ENUM_ORDER_TYPE order_type, long magic);
|
||||
};
|
||||
|
||||
int OrderTracker::count_open_positions(string symbol, int order_side, long magic_number) {
|
||||
int count = 0;
|
||||
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
|
||||
if (PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number) {
|
||||
|
||||
if (order_side == 1 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (order_side == 2 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int OrderTracker::count_all_positions(string symbol, long magic_number) {
|
||||
int count = 0;
|
||||
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
|
||||
if (PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int OrderTracker::count_pending_orders(string symbol, ENUM_ORDER_TYPE order_type, long magic) {
|
||||
int count = 0;
|
||||
|
||||
for (int i = OrdersTotal() - 1; i >= 0; i--) {
|
||||
if (m_order.SelectByIndex(i)) {
|
||||
if (OrderGetInteger(ORDER_MAGIC) == magic && OrderGetString(ORDER_SYMBOL) == symbol) {
|
||||
if (m_order.OrderType() == order_type) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
class StopLogic {
|
||||
public:
|
||||
double sl_specified_value_switch(string sl_mode, double inp_sl_var, double value);
|
||||
double tp_specified_value_switch(string tp_mode, double inp_tp_var, double value);
|
||||
};
|
||||
|
||||
double StopLogic::sl_specified_value_switch(string sl_mode, double inp_sl_var, double value) {
|
||||
if (sl_mode == "SL_SPECIFIED_VALUE") {
|
||||
return value;
|
||||
} else {
|
||||
return inp_sl_var;
|
||||
}
|
||||
}
|
||||
|
||||
double StopLogic::tp_specified_value_switch(string tp_mode, double inp_tp_var, double value) {
|
||||
if (tp_mode == "SL_SPECIFIED_VALUE") {
|
||||
return value;
|
||||
} else {
|
||||
return inp_tp_var;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#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,205 +1,205 @@
|
||||
#property library
|
||||
#include <Trade/Trade.mqh>
|
||||
#include <MyLibs/MyFunctions.mqh>
|
||||
|
||||
class DrawdownControl : public CObject {
|
||||
protected:
|
||||
CTrade trade;
|
||||
MyFunctions mf;
|
||||
|
||||
string data_file;
|
||||
double daily_max_dd_per;
|
||||
string daily_reset_time;
|
||||
bool print_statments;
|
||||
|
||||
double acc_max_dd_per;
|
||||
double equaty_control_high;
|
||||
double equaty_control_low;
|
||||
|
||||
|
||||
double daily_equity_start;
|
||||
double daily_max_dd_target;
|
||||
bool daily_dd_limit_reached;
|
||||
|
||||
bool write_global_var_data();
|
||||
bool print_messages();
|
||||
|
||||
public:
|
||||
void init_dd_control(string inp_data_file, double inp_acc_max_dd_per, double inp_daily_max_dd_per, string inp_daily_reset_time, bool inp_print_statments = true);
|
||||
bool determine_daily_dd_limit();
|
||||
double lot_correction_factor(double acc_equity_start, double min_lot_factor, double max_lot_factor, bool dynm_lot_factor=false, double dlf_trail_per=20);
|
||||
double lot_correction_dynamic(double acc_dd_percent, double min_lot_factor, double max_lot_factor);
|
||||
};
|
||||
|
||||
void DrawdownControl::init_dd_control(string inp_data_file, double inp_acc_max_dd_per, double inp_daily_max_dd_per, string inp_daily_reset_time, bool inp_print_statments = true) {
|
||||
|
||||
data_file = inp_data_file;
|
||||
acc_max_dd_per = inp_acc_max_dd_per;
|
||||
daily_max_dd_per = inp_daily_max_dd_per;
|
||||
daily_reset_time = inp_daily_reset_time;
|
||||
print_statments = inp_print_statments;
|
||||
|
||||
// If no data file exisits, create one and set global vairiables:
|
||||
if(FileIsExist(data_file) == false) {
|
||||
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100));
|
||||
daily_dd_limit_reached = false;
|
||||
equaty_control_high = 9999999;
|
||||
equaty_control_low = 0;
|
||||
write_global_var_data();
|
||||
}
|
||||
// If file exisits read file:
|
||||
if(FileIsExist(data_file) == true) {
|
||||
|
||||
int file_handle = FileOpen(data_file, FILE_READ | FILE_ANSI | FILE_TXT);
|
||||
if(file_handle == INVALID_HANDLE) {
|
||||
Print("Error opening file: ", data_file);
|
||||
}
|
||||
|
||||
// If data file is older than 24h 10min create a new file and reset global vars:
|
||||
long modifided_date = FileGetInteger(file_handle, FILE_MODIFY_DATE);
|
||||
long time_delta = ((long)TimeCurrent() - modifided_date) / 60;
|
||||
|
||||
if(time_delta >= 1450) {
|
||||
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100));
|
||||
daily_dd_limit_reached = false;
|
||||
equaty_control_high = equaty_control_high;
|
||||
equaty_control_low = equaty_control_low;
|
||||
write_global_var_data();
|
||||
Print(data_file, " is older than 24h and 10min; global vars reset!");
|
||||
}
|
||||
// If data file is younger than 24h+10 min read data and set global vars:
|
||||
else {
|
||||
daily_equity_start = (double)FileReadString(file_handle, 0);
|
||||
daily_max_dd_target = (double)FileReadString(file_handle, 1);
|
||||
daily_dd_limit_reached = FileReadBool(file_handle);
|
||||
equaty_control_high = (double)FileReadString(file_handle, 3);
|
||||
equaty_control_low = (double)FileReadString(file_handle, 4);;
|
||||
}
|
||||
FileClose(file_handle);
|
||||
}
|
||||
print_messages();
|
||||
}
|
||||
|
||||
bool DrawdownControl::determine_daily_dd_limit() {
|
||||
|
||||
// Reset max equity at the start of each day:
|
||||
string ct = TimeToString(TimeCurrent(), TIME_MINUTES);
|
||||
if(ct == daily_reset_time) {
|
||||
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
daily_max_dd_target = (daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100)));
|
||||
daily_dd_limit_reached = false;
|
||||
write_global_var_data();
|
||||
print_messages();
|
||||
}
|
||||
|
||||
// If in drawdown close all positions and delete orders
|
||||
if(daily_dd_limit_reached || AccountInfoDouble(ACCOUNT_EQUITY) <= daily_max_dd_target) {
|
||||
|
||||
if(daily_dd_limit_reached == false) {
|
||||
daily_dd_limit_reached = true;
|
||||
write_global_var_data();
|
||||
print_messages();
|
||||
}
|
||||
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
trade.PositionClose(ticket);
|
||||
}
|
||||
|
||||
for(int i = OrdersTotal() - 1; i >= 0; i--) {
|
||||
ulong ticket = OrderGetTicket(i);
|
||||
trade.OrderDelete(ticket);
|
||||
}
|
||||
}
|
||||
return daily_dd_limit_reached;
|
||||
}
|
||||
|
||||
// Reduces lot size as account apporchaes max allowed drawdown limit.
|
||||
double DrawdownControl::lot_correction_factor(double acc_equity_start, double min_lot_factor, double max_lot_factor, bool dynm_lot_factor=false, double dlf_trail_per=20) {
|
||||
|
||||
double account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE));
|
||||
double lot_factor;
|
||||
|
||||
// Interpolate to find lot factor between given min and max values.
|
||||
if (account_value < acc_equity_start){
|
||||
|
||||
double acc_equity_min = acc_equity_start - (acc_equity_start * (acc_max_dd_per / 100));
|
||||
double y1 = min_lot_factor;
|
||||
double y2 = max_lot_factor;
|
||||
double x1 = acc_equity_min;
|
||||
double x = account_value;
|
||||
double x2 = acc_equity_start;
|
||||
lot_factor = y1 + (x - x1) * ((y2 - y1) / (x2 - x1));
|
||||
}
|
||||
|
||||
else if(account_value >= acc_equity_start) {
|
||||
|
||||
if(dynm_lot_factor=true){
|
||||
lot_factor = lot_correction_dynamic(dlf_trail_per, min_lot_factor, max_lot_factor);
|
||||
}
|
||||
|
||||
else {
|
||||
lot_factor = max_lot_factor;
|
||||
}
|
||||
}
|
||||
return max_lot_factor;
|
||||
}
|
||||
|
||||
|
||||
double DrawdownControl::lot_correction_dynamic(double acc_dd_percent, double min_lot_factor, double max_lot_factor) {
|
||||
|
||||
double account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE));
|
||||
double trail_point = account_value - (account_value * (acc_dd_percent / 100));
|
||||
|
||||
if(equaty_control_low < trail_point){
|
||||
equaty_control_low = trail_point;
|
||||
}
|
||||
|
||||
if(equaty_control_high < account_value){
|
||||
equaty_control_high = account_value;
|
||||
}
|
||||
|
||||
if(account_value < equaty_control_low){
|
||||
equaty_control_low = account_value;
|
||||
equaty_control_high = account_value + (account_value * (acc_dd_percent / 100));
|
||||
}
|
||||
|
||||
// back-up to file every hour:
|
||||
if(mf.is_new_bar(_Symbol, PERIOD_H1) == true){
|
||||
write_global_var_data();
|
||||
}
|
||||
|
||||
// Linear interpolation:
|
||||
double y1 = min_lot_factor;
|
||||
double y2 = max_lot_factor;
|
||||
double x1 = equaty_control_low;
|
||||
double x = account_value;
|
||||
double x2 = equaty_control_high;
|
||||
|
||||
double y = y1 + (x - x1) * ((y2 - y1) / (x2 - x1));
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
bool DrawdownControl::write_global_var_data() {
|
||||
int file_handle = FileOpen(data_file, FILE_WRITE | FILE_ANSI | FILE_TXT);
|
||||
FileWrite(file_handle, daily_equity_start);
|
||||
FileWrite(file_handle, daily_max_dd_target);
|
||||
FileWrite(file_handle, daily_dd_limit_reached);
|
||||
FileClose(file_handle);
|
||||
Print(data_file, " written");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrawdownControl::print_messages() {
|
||||
if(print_statments == true) {
|
||||
Print("TimeCurrent(): ", TimeToString(TimeCurrent()));
|
||||
Print("Daily Equity Start: ", (int)daily_equity_start);
|
||||
Print("Current Equity: ", (int)AccountInfoDouble(ACCOUNT_EQUITY));
|
||||
Print("Daily Drawdown Limit: ", (int)daily_max_dd_target, " (", daily_max_dd_per, "%) of DES");
|
||||
Print("Daily Drawdown Limit Hit: ", daily_dd_limit_reached);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#property library
|
||||
#include <Trade/Trade.mqh>
|
||||
#include <MyLibs/Utils/BarUtils.mqh>
|
||||
|
||||
class DrawdownControl : public CObject {
|
||||
protected:
|
||||
CTrade trade;
|
||||
BarUtils bar_utils;
|
||||
|
||||
string data_file;
|
||||
double daily_max_dd_per;
|
||||
string daily_reset_time;
|
||||
bool print_statments;
|
||||
|
||||
double acc_max_dd_per;
|
||||
double equaty_control_high;
|
||||
double equaty_control_low;
|
||||
|
||||
|
||||
double daily_equity_start;
|
||||
double daily_max_dd_target;
|
||||
bool daily_dd_limit_reached;
|
||||
|
||||
bool write_global_var_data();
|
||||
bool print_messages();
|
||||
|
||||
public:
|
||||
void init_dd_control(string inp_data_file, double inp_acc_max_dd_per, double inp_daily_max_dd_per, string inp_daily_reset_time, bool inp_print_statments = true);
|
||||
bool determine_daily_dd_limit();
|
||||
double lot_correction_factor(double acc_equity_start, double min_lot_factor, double max_lot_factor, bool dynm_lot_factor=false, double dlf_trail_per=20);
|
||||
double lot_correction_dynamic(double acc_dd_percent, double min_lot_factor, double max_lot_factor);
|
||||
};
|
||||
|
||||
void DrawdownControl::init_dd_control(string inp_data_file, double inp_acc_max_dd_per, double inp_daily_max_dd_per, string inp_daily_reset_time, bool inp_print_statments = true) {
|
||||
|
||||
data_file = inp_data_file;
|
||||
acc_max_dd_per = inp_acc_max_dd_per;
|
||||
daily_max_dd_per = inp_daily_max_dd_per;
|
||||
daily_reset_time = inp_daily_reset_time;
|
||||
print_statments = inp_print_statments;
|
||||
|
||||
// If no data file exisits, create one and set global vairiables:
|
||||
if(FileIsExist(data_file) == false) {
|
||||
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100));
|
||||
daily_dd_limit_reached = false;
|
||||
equaty_control_high = 9999999;
|
||||
equaty_control_low = 0;
|
||||
write_global_var_data();
|
||||
}
|
||||
// If file exisits read file:
|
||||
if(FileIsExist(data_file) == true) {
|
||||
|
||||
int file_handle = FileOpen(data_file, FILE_READ | FILE_ANSI | FILE_TXT);
|
||||
if(file_handle == INVALID_HANDLE) {
|
||||
Print("Error opening file: ", data_file);
|
||||
}
|
||||
|
||||
// If data file is older than 24h 10min create a new file and reset global vars:
|
||||
long modifided_date = FileGetInteger(file_handle, FILE_MODIFY_DATE);
|
||||
long time_delta = ((long)TimeCurrent() - modifided_date) / 60;
|
||||
|
||||
if(time_delta >= 1450) {
|
||||
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
daily_max_dd_target = daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100));
|
||||
daily_dd_limit_reached = false;
|
||||
equaty_control_high = equaty_control_high;
|
||||
equaty_control_low = equaty_control_low;
|
||||
write_global_var_data();
|
||||
Print(data_file, " is older than 24h and 10min; global vars reset!");
|
||||
}
|
||||
// If data file is younger than 24h+10 min read data and set global vars:
|
||||
else {
|
||||
daily_equity_start = (double)FileReadString(file_handle, 0);
|
||||
daily_max_dd_target = (double)FileReadString(file_handle, 1);
|
||||
daily_dd_limit_reached = FileReadBool(file_handle);
|
||||
equaty_control_high = (double)FileReadString(file_handle, 3);
|
||||
equaty_control_low = (double)FileReadString(file_handle, 4);;
|
||||
}
|
||||
FileClose(file_handle);
|
||||
}
|
||||
print_messages();
|
||||
}
|
||||
|
||||
bool DrawdownControl::determine_daily_dd_limit() {
|
||||
|
||||
// Reset max equity at the start of each day:
|
||||
string ct = TimeToString(TimeCurrent(), TIME_MINUTES);
|
||||
if(ct == daily_reset_time) {
|
||||
daily_equity_start = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
daily_max_dd_target = (daily_equity_start - (daily_equity_start * (daily_max_dd_per / 100)));
|
||||
daily_dd_limit_reached = false;
|
||||
write_global_var_data();
|
||||
print_messages();
|
||||
}
|
||||
|
||||
// If in drawdown close all positions and delete orders
|
||||
if(daily_dd_limit_reached || AccountInfoDouble(ACCOUNT_EQUITY) <= daily_max_dd_target) {
|
||||
|
||||
if(daily_dd_limit_reached == false) {
|
||||
daily_dd_limit_reached = true;
|
||||
write_global_var_data();
|
||||
print_messages();
|
||||
}
|
||||
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
trade.PositionClose(ticket);
|
||||
}
|
||||
|
||||
for(int i = OrdersTotal() - 1; i >= 0; i--) {
|
||||
ulong ticket = OrderGetTicket(i);
|
||||
trade.OrderDelete(ticket);
|
||||
}
|
||||
}
|
||||
return daily_dd_limit_reached;
|
||||
}
|
||||
|
||||
// Reduces lot size as account apporchaes max allowed drawdown limit.
|
||||
double DrawdownControl::lot_correction_factor(double acc_equity_start, double min_lot_factor, double max_lot_factor, bool dynm_lot_factor=false, double dlf_trail_per=20) {
|
||||
|
||||
double account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE));
|
||||
double lot_factor;
|
||||
|
||||
// Interpolate to find lot factor between given min and max values.
|
||||
if (account_value < acc_equity_start){
|
||||
|
||||
double acc_equity_min = acc_equity_start - (acc_equity_start * (acc_max_dd_per / 100));
|
||||
double y1 = min_lot_factor;
|
||||
double y2 = max_lot_factor;
|
||||
double x1 = acc_equity_min;
|
||||
double x = account_value;
|
||||
double x2 = acc_equity_start;
|
||||
lot_factor = y1 + (x - x1) * ((y2 - y1) / (x2 - x1));
|
||||
}
|
||||
|
||||
else if(account_value >= acc_equity_start) {
|
||||
|
||||
if(dynm_lot_factor=true){
|
||||
lot_factor = lot_correction_dynamic(dlf_trail_per, min_lot_factor, max_lot_factor);
|
||||
}
|
||||
|
||||
else {
|
||||
lot_factor = max_lot_factor;
|
||||
}
|
||||
}
|
||||
return max_lot_factor;
|
||||
}
|
||||
|
||||
|
||||
double DrawdownControl::lot_correction_dynamic(double acc_dd_percent, double min_lot_factor, double max_lot_factor) {
|
||||
|
||||
double account_value = fmin(AccountInfoDouble(ACCOUNT_EQUITY), AccountInfoDouble(ACCOUNT_BALANCE));
|
||||
double trail_point = account_value - (account_value * (acc_dd_percent / 100));
|
||||
|
||||
if(equaty_control_low < trail_point){
|
||||
equaty_control_low = trail_point;
|
||||
}
|
||||
|
||||
if(equaty_control_high < account_value){
|
||||
equaty_control_high = account_value;
|
||||
}
|
||||
|
||||
if(account_value < equaty_control_low){
|
||||
equaty_control_low = account_value;
|
||||
equaty_control_high = account_value + (account_value * (acc_dd_percent / 100));
|
||||
}
|
||||
|
||||
// back-up to file every hour:
|
||||
if(bar_utils.is_new_bar(_Symbol, PERIOD_H1) == true){
|
||||
write_global_var_data();
|
||||
}
|
||||
|
||||
// Linear interpolation:
|
||||
double y1 = min_lot_factor;
|
||||
double y2 = max_lot_factor;
|
||||
double x1 = equaty_control_low;
|
||||
double x = account_value;
|
||||
double x2 = equaty_control_high;
|
||||
|
||||
double y = y1 + (x - x1) * ((y2 - y1) / (x2 - x1));
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
bool DrawdownControl::write_global_var_data() {
|
||||
int file_handle = FileOpen(data_file, FILE_WRITE | FILE_ANSI | FILE_TXT);
|
||||
FileWrite(file_handle, daily_equity_start);
|
||||
FileWrite(file_handle, daily_max_dd_target);
|
||||
FileWrite(file_handle, daily_dd_limit_reached);
|
||||
FileClose(file_handle);
|
||||
Print(data_file, " written");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrawdownControl::print_messages() {
|
||||
if(print_statments == true) {
|
||||
Print("TimeCurrent(): ", TimeToString(TimeCurrent()));
|
||||
Print("Daily Equity Start: ", (int)daily_equity_start);
|
||||
Print("Current Equity: ", (int)AccountInfoDouble(ACCOUNT_EQUITY));
|
||||
Print("Daily Drawdown Limit: ", (int)daily_max_dd_target, " (", daily_max_dd_per, "%) of DES");
|
||||
Print("Daily Drawdown Limit Hit: ", daily_dd_limit_reached);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
@@ -1,414 +1,414 @@
|
||||
#property library
|
||||
#include <Trade/Trade.mqh>
|
||||
#include <MyLibs/TimeZones.mqh>
|
||||
|
||||
class RangeCalculator : public CObject{
|
||||
|
||||
protected:
|
||||
TimeZones tz;
|
||||
|
||||
bool days_initlised;
|
||||
bool range_initlised;
|
||||
string symbol;
|
||||
ENUM_TIMEFRAMES calc_period;
|
||||
|
||||
string inp_r_start_string;
|
||||
int r_duration;
|
||||
int r_expire;
|
||||
int r_close;
|
||||
string inp_timezone;
|
||||
|
||||
bool sun;
|
||||
bool mon;
|
||||
bool tue;
|
||||
bool wed;
|
||||
bool thu;
|
||||
bool fri;
|
||||
bool sat;
|
||||
bool plot_range;
|
||||
datetime start_time; // Start of the range
|
||||
datetime end_time; // end of the range
|
||||
datetime order_expire_time; // end of the range
|
||||
datetime close_time; // Close time
|
||||
double high; // high of the range
|
||||
double low; // low of the range
|
||||
double mid; // mid of the range
|
||||
bool f_entry; // flag if we are inside of the range
|
||||
bool f_high_breakout; // flag if a high breakout occurred
|
||||
bool f_low_breakout; // flag if a low breakout occurred
|
||||
bool above_last;
|
||||
bool above_current;
|
||||
bool below_last;
|
||||
bool below_current;
|
||||
|
||||
// private functions
|
||||
void update_objects();
|
||||
void draw_objects();
|
||||
void define_new_range();
|
||||
bool convert_input_time_strings(string t1, string t2, string t3, string t4);
|
||||
|
||||
|
||||
public:
|
||||
void calculate_range();
|
||||
|
||||
double get_range_high();
|
||||
double get_range_low();
|
||||
double get_range_mid();
|
||||
datetime get_range_start();
|
||||
datetime get_range_end();
|
||||
datetime get_order_expire_time();
|
||||
datetime get_range_close();
|
||||
bool get_range_high_breakout();
|
||||
bool get_range_low_breakout();
|
||||
bool initilise_range(string inp_symbol, ENUM_TIMEFRAMES _calc_period, string t0, string t1, string t2, string t3, string time_zone, bool plot_range_inp);
|
||||
void range_days(bool _inp_sun, bool _inp_mon, bool _inp_tue, bool _inp_wed, bool _inp_thu, bool _inp_fri, bool _inp_sat);
|
||||
|
||||
};
|
||||
|
||||
void RangeCalculator::range_days(bool _inp_sun, bool _inp_mon, bool _inp_tue, bool _inp_wed, bool _inp_thu, bool _inp_fri, bool _inp_sat){
|
||||
sun = _inp_sun;
|
||||
mon = _inp_mon;
|
||||
tue = _inp_tue;
|
||||
wed = _inp_wed;
|
||||
thu = _inp_thu;
|
||||
fri = _inp_fri;
|
||||
sat = _inp_sat;
|
||||
days_initlised = true;
|
||||
}
|
||||
|
||||
bool RangeCalculator::initilise_range(string inp_symbol, ENUM_TIMEFRAMES _calc_period, string t1, string t2, string t3, string t4, string time_zone, bool plot_range_inp){
|
||||
inp_r_start_string = t1;
|
||||
inp_timezone = time_zone;
|
||||
symbol = inp_symbol;
|
||||
calc_period =_calc_period;
|
||||
plot_range = plot_range_inp;
|
||||
start_time = 0;
|
||||
end_time = 0;
|
||||
close_time = 0;
|
||||
high = 0;
|
||||
low = DBL_MAX;
|
||||
mid = 0;
|
||||
f_entry = false;
|
||||
f_high_breakout = false;
|
||||
f_low_breakout = false;
|
||||
above_last = false;
|
||||
above_current= false;
|
||||
below_last= false;
|
||||
below_current= false;
|
||||
if(!days_initlised){
|
||||
sun = true;
|
||||
mon = true;
|
||||
tue = true;
|
||||
wed = true;
|
||||
thu = true;
|
||||
fri = true;
|
||||
sat = true;
|
||||
}
|
||||
range_initlised = true;
|
||||
|
||||
bool corret_inputs = convert_input_time_strings(t1, t2, t3, t4);
|
||||
if(corret_inputs = false){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool RangeCalculator::convert_input_time_strings(string t1, string t2, string t3, string t4){
|
||||
|
||||
datetime _t1 = StringToTime(t1);
|
||||
datetime _t2 = StringToTime(t2);
|
||||
datetime _t3 = StringToTime(t3);
|
||||
datetime _t4 = StringToTime(t4);
|
||||
|
||||
|
||||
if(_t1 > _t2){
|
||||
_t2 = _t2 + PeriodSeconds(PERIOD_D1);
|
||||
_t3 = _t3 + PeriodSeconds(PERIOD_D1);
|
||||
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
|
||||
}
|
||||
|
||||
if(_t2 > _t3){
|
||||
_t3 = _t3 + PeriodSeconds(PERIOD_D1);
|
||||
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
|
||||
}
|
||||
|
||||
if(_t3 > _t4){
|
||||
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
|
||||
}
|
||||
|
||||
r_duration = (int)(_t2 - _t1);
|
||||
r_expire = (int)(_t3 - _t1);
|
||||
r_close = (int)(_t4 - _t1);
|
||||
|
||||
if(_t4 - _t1 >= PeriodSeconds(PERIOD_D1)){
|
||||
Alert("INCORRECT RANGE INPUTS!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// high of the range
|
||||
double RangeCalculator::get_range_high(){
|
||||
return high;
|
||||
};
|
||||
|
||||
// low of the range
|
||||
double RangeCalculator::get_range_low(){
|
||||
return low;
|
||||
};
|
||||
|
||||
// mid of the range
|
||||
double RangeCalculator::get_range_mid(){
|
||||
return mid;
|
||||
};
|
||||
|
||||
|
||||
datetime RangeCalculator::get_range_start(){
|
||||
return start_time;
|
||||
};
|
||||
|
||||
datetime RangeCalculator::get_range_end(){
|
||||
return end_time;
|
||||
};
|
||||
|
||||
datetime RangeCalculator::get_order_expire_time(){
|
||||
return order_expire_time;
|
||||
};
|
||||
|
||||
datetime RangeCalculator::get_range_close(){
|
||||
return close_time;
|
||||
};
|
||||
|
||||
// flag if a high breakout occurred
|
||||
bool RangeCalculator::get_range_high_breakout(){
|
||||
return f_high_breakout;
|
||||
};
|
||||
|
||||
// flag if a low breakout occurred
|
||||
bool RangeCalculator::get_range_low_breakout(){
|
||||
return f_low_breakout;
|
||||
};
|
||||
|
||||
|
||||
void RangeCalculator::calculate_range(){
|
||||
|
||||
f_high_breakout = false;
|
||||
f_low_breakout = false;
|
||||
|
||||
double last_bar_high = iHigh(symbol, calc_period, 1); // shift 1 because 0 = live candle:
|
||||
double last_bar_low = iLow(symbol, calc_period, 1); // shift 1 because 0 = live candle:
|
||||
|
||||
// range calculation
|
||||
if(TimeCurrent() >= start_time && TimeCurrent() <= end_time){
|
||||
|
||||
// set flag
|
||||
f_entry = true;
|
||||
|
||||
// new high
|
||||
if(last_bar_high > high){
|
||||
high = last_bar_high;
|
||||
mid = (high + low)/2;
|
||||
if(plot_range){
|
||||
update_objects();
|
||||
}
|
||||
}
|
||||
|
||||
// new low
|
||||
if(last_bar_low < low){
|
||||
low = last_bar_low;
|
||||
mid = (high + low)/2;
|
||||
if(plot_range){
|
||||
update_objects();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// calculate new reange if
|
||||
if( (TimeCurrent() >= close_time) // close time reached
|
||||
|| (end_time == 0) // range not calculated yet
|
||||
|| (end_time !=0 && TimeCurrent() > end_time && !f_entry) // there was a range calculated but no tick inside.
|
||||
){
|
||||
define_new_range();
|
||||
}
|
||||
|
||||
// check if we are after the range end
|
||||
if(TimeCurrent() >= end_time && end_time > 0 && f_entry){
|
||||
|
||||
if(!f_high_breakout && last_bar_high >= high){
|
||||
above_last = above_current;
|
||||
above_current= true;
|
||||
|
||||
if(above_last==false && above_current == true){
|
||||
f_high_breakout = true;
|
||||
}
|
||||
else(f_high_breakout = false);
|
||||
}
|
||||
|
||||
if(!f_low_breakout && last_bar_low >= low){
|
||||
below_last = below_current;
|
||||
below_current = true;
|
||||
if(below_last == false && below_current == true){
|
||||
f_low_breakout = true;
|
||||
}
|
||||
else(f_low_breakout = false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void RangeCalculator::define_new_range(){
|
||||
|
||||
// reset range vars
|
||||
start_time = 0;
|
||||
end_time = 0;
|
||||
order_expire_time = 0;
|
||||
close_time = 0;
|
||||
high = 0;
|
||||
low = INT_MAX;
|
||||
mid = 0;
|
||||
f_entry = false;
|
||||
|
||||
// calculate range start time:
|
||||
datetime r_st = StringToTime(inp_r_start_string);
|
||||
start_time = tz.timezone_conversions(inp_timezone, r_st, "Broker");
|
||||
|
||||
|
||||
for(int i=0; i<8; i++){
|
||||
|
||||
MqlDateTime tmp;
|
||||
TimeToStruct(start_time,tmp);
|
||||
int dow = tmp.day_of_week;
|
||||
|
||||
if(TimeCurrent()>=start_time
|
||||
|| (dow==0 && !sun)
|
||||
|| (dow==1 && !mon)
|
||||
|| (dow==2 && !tue)
|
||||
|| (dow==3 && !wed)
|
||||
|| (dow==4 && !thu)
|
||||
|| (dow==5 && !fri)
|
||||
|| (dow==6 && !sat)
|
||||
){
|
||||
start_time += PeriodSeconds(PERIOD_D1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
end_time = start_time + r_duration;
|
||||
order_expire_time = start_time + r_expire;
|
||||
close_time = start_time + r_close;
|
||||
|
||||
if(plot_range){
|
||||
draw_objects();
|
||||
}
|
||||
}
|
||||
|
||||
void RangeCalculator::update_objects(){
|
||||
|
||||
string name = "Range Mid " + (string)start_time;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, mid);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, mid);
|
||||
// ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid");
|
||||
|
||||
name = "Order expire " + (string)order_expire_time;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
|
||||
|
||||
name = "Range start " + (string)start_time;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
|
||||
|
||||
name = "Range end " + (string)end_time;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
|
||||
|
||||
datetime rct = r_close>=0 ? close_time : INT_MAX;
|
||||
name = "Range close " + (string)rct;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
|
||||
|
||||
name = "Range High " + (string)rct;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, high);
|
||||
|
||||
name = "Range Low " + (string)rct;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, low);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
|
||||
|
||||
name = "range box "+ (string)start_time;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
|
||||
ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,1, low);
|
||||
|
||||
}
|
||||
|
||||
void RangeCalculator::draw_objects(){
|
||||
|
||||
datetime rct = r_close>=0 ? close_time : INT_MAX;
|
||||
|
||||
// Range mid line
|
||||
string name = "Range Mid " + (string)start_time;;
|
||||
ObjectCreate(NULL, name, OBJ_TREND, 0, start_time, mid, rct, mid);
|
||||
ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid" + (string)mid);
|
||||
ObjectSetInteger(NULL, name, OBJPROP_COLOR, clrGray);
|
||||
ObjectSetInteger(NULL, name, OBJPROP_WIDTH, 1);
|
||||
ObjectSetInteger(NULL, name, OBJPROP_STYLE, STYLE_DOT);
|
||||
|
||||
// order lines
|
||||
string name2 = "Order expire " + (string)order_expire_time;
|
||||
ObjectCreate(NULL, name2, OBJ_TREND, 0, order_expire_time, low, order_expire_time, high);
|
||||
ObjectSetString(NULL, name2, OBJPROP_TOOLTIP, "start of the range \n" + TimeToString(order_expire_time,TIME_DATE|TIME_MINUTES));
|
||||
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, C'139,41,41');
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
|
||||
|
||||
name2 = "Range start " + (string)start_time;
|
||||
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, start_time, high);
|
||||
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
|
||||
|
||||
name2 = "Range end " + (string)end_time;
|
||||
ObjectCreate(NULL, name2, OBJ_TREND, 0, end_time, low, end_time, high);
|
||||
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
|
||||
|
||||
name2 = "Range close " + (string)rct;
|
||||
ObjectCreate(NULL, name2, OBJ_TREND, 0, rct, low, rct, high);
|
||||
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
|
||||
|
||||
name2 = "Range High " + (string)rct;
|
||||
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, high, rct, high);
|
||||
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
|
||||
|
||||
name2 = "Range Low " + (string)rct;
|
||||
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, rct, low);
|
||||
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_BACK, true);
|
||||
|
||||
// Box
|
||||
name = "range box " + (string)start_time;
|
||||
ObjectCreate(NULL, name, OBJ_RECTANGLE, 0, start_time, high, end_time, low);
|
||||
ObjectSetString(NULL,name,OBJPROP_TOOLTIP,"\n");
|
||||
ObjectSetInteger(NULL, name,OBJPROP_COLOR, C'128,177,173');
|
||||
ObjectSetInteger(NULL, name,OBJPROP_FILL, true);
|
||||
ObjectSetInteger(NULL, name,OBJPROP_BACK, true);
|
||||
|
||||
ObjectCreate(NULL, name + " ", OBJ_RECTANGLE, 0, end_time, high, rct, low);
|
||||
ObjectSetString(NULL, name+ " ", OBJPROP_TOOLTIP, "\n");
|
||||
ObjectSetInteger(NULL, name + " ",OBJPROP_FILL, true);
|
||||
ObjectSetInteger(NULL, name + " ",OBJPROP_COLOR, C'165,220,215' );
|
||||
ObjectSetInteger(NULL, name + " ",OBJPROP_BACK, true);
|
||||
|
||||
ChartRedraw();
|
||||
}
|
||||
|
||||
|
||||
#property library
|
||||
#include <Trade/Trade.mqh>
|
||||
#include <MyLibs/Utils/TimeZones.mqh>
|
||||
|
||||
class RangeCalculator : public CObject{
|
||||
|
||||
protected:
|
||||
TimeZones tz;
|
||||
|
||||
bool days_initlised;
|
||||
bool range_initlised;
|
||||
string symbol;
|
||||
ENUM_TIMEFRAMES calc_period;
|
||||
|
||||
string inp_r_start_string;
|
||||
int r_duration;
|
||||
int r_expire;
|
||||
int r_close;
|
||||
string inp_timezone;
|
||||
|
||||
bool sun;
|
||||
bool mon;
|
||||
bool tue;
|
||||
bool wed;
|
||||
bool thu;
|
||||
bool fri;
|
||||
bool sat;
|
||||
bool plot_range;
|
||||
datetime start_time; // Start of the range
|
||||
datetime end_time; // end of the range
|
||||
datetime order_expire_time; // end of the range
|
||||
datetime close_time; // Close time
|
||||
double high; // high of the range
|
||||
double low; // low of the range
|
||||
double mid; // mid of the range
|
||||
bool f_entry; // flag if we are inside of the range
|
||||
bool f_high_breakout; // flag if a high breakout occurred
|
||||
bool f_low_breakout; // flag if a low breakout occurred
|
||||
bool above_last;
|
||||
bool above_current;
|
||||
bool below_last;
|
||||
bool below_current;
|
||||
|
||||
// private functions
|
||||
void update_objects();
|
||||
void draw_objects();
|
||||
void define_new_range();
|
||||
bool convert_input_time_strings(string t1, string t2, string t3, string t4);
|
||||
|
||||
|
||||
public:
|
||||
void calculate_range();
|
||||
|
||||
double get_range_high();
|
||||
double get_range_low();
|
||||
double get_range_mid();
|
||||
datetime get_range_start();
|
||||
datetime get_range_end();
|
||||
datetime get_order_expire_time();
|
||||
datetime get_range_close();
|
||||
bool get_range_high_breakout();
|
||||
bool get_range_low_breakout();
|
||||
bool initilise_range(string inp_symbol, ENUM_TIMEFRAMES _calc_period, string t0, string t1, string t2, string t3, string time_zone, bool plot_range_inp);
|
||||
void range_days(bool _inp_sun, bool _inp_mon, bool _inp_tue, bool _inp_wed, bool _inp_thu, bool _inp_fri, bool _inp_sat);
|
||||
|
||||
};
|
||||
|
||||
void RangeCalculator::range_days(bool _inp_sun, bool _inp_mon, bool _inp_tue, bool _inp_wed, bool _inp_thu, bool _inp_fri, bool _inp_sat){
|
||||
sun = _inp_sun;
|
||||
mon = _inp_mon;
|
||||
tue = _inp_tue;
|
||||
wed = _inp_wed;
|
||||
thu = _inp_thu;
|
||||
fri = _inp_fri;
|
||||
sat = _inp_sat;
|
||||
days_initlised = true;
|
||||
}
|
||||
|
||||
bool RangeCalculator::initilise_range(string inp_symbol, ENUM_TIMEFRAMES _calc_period, string t1, string t2, string t3, string t4, string time_zone, bool plot_range_inp){
|
||||
inp_r_start_string = t1;
|
||||
inp_timezone = time_zone;
|
||||
symbol = inp_symbol;
|
||||
calc_period =_calc_period;
|
||||
plot_range = plot_range_inp;
|
||||
start_time = 0;
|
||||
end_time = 0;
|
||||
close_time = 0;
|
||||
high = 0;
|
||||
low = DBL_MAX;
|
||||
mid = 0;
|
||||
f_entry = false;
|
||||
f_high_breakout = false;
|
||||
f_low_breakout = false;
|
||||
above_last = false;
|
||||
above_current= false;
|
||||
below_last= false;
|
||||
below_current= false;
|
||||
if(!days_initlised){
|
||||
sun = true;
|
||||
mon = true;
|
||||
tue = true;
|
||||
wed = true;
|
||||
thu = true;
|
||||
fri = true;
|
||||
sat = true;
|
||||
}
|
||||
range_initlised = true;
|
||||
|
||||
bool corret_inputs = convert_input_time_strings(t1, t2, t3, t4);
|
||||
if(corret_inputs = false){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool RangeCalculator::convert_input_time_strings(string t1, string t2, string t3, string t4){
|
||||
|
||||
datetime _t1 = StringToTime(t1);
|
||||
datetime _t2 = StringToTime(t2);
|
||||
datetime _t3 = StringToTime(t3);
|
||||
datetime _t4 = StringToTime(t4);
|
||||
|
||||
|
||||
if(_t1 > _t2){
|
||||
_t2 = _t2 + PeriodSeconds(PERIOD_D1);
|
||||
_t3 = _t3 + PeriodSeconds(PERIOD_D1);
|
||||
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
|
||||
}
|
||||
|
||||
if(_t2 > _t3){
|
||||
_t3 = _t3 + PeriodSeconds(PERIOD_D1);
|
||||
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
|
||||
}
|
||||
|
||||
if(_t3 > _t4){
|
||||
_t4 = _t4 + PeriodSeconds(PERIOD_D1);
|
||||
}
|
||||
|
||||
r_duration = (int)(_t2 - _t1);
|
||||
r_expire = (int)(_t3 - _t1);
|
||||
r_close = (int)(_t4 - _t1);
|
||||
|
||||
if(_t4 - _t1 >= PeriodSeconds(PERIOD_D1)){
|
||||
Alert("INCORRECT RANGE INPUTS!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// high of the range
|
||||
double RangeCalculator::get_range_high(){
|
||||
return high;
|
||||
};
|
||||
|
||||
// low of the range
|
||||
double RangeCalculator::get_range_low(){
|
||||
return low;
|
||||
};
|
||||
|
||||
// mid of the range
|
||||
double RangeCalculator::get_range_mid(){
|
||||
return mid;
|
||||
};
|
||||
|
||||
|
||||
datetime RangeCalculator::get_range_start(){
|
||||
return start_time;
|
||||
};
|
||||
|
||||
datetime RangeCalculator::get_range_end(){
|
||||
return end_time;
|
||||
};
|
||||
|
||||
datetime RangeCalculator::get_order_expire_time(){
|
||||
return order_expire_time;
|
||||
};
|
||||
|
||||
datetime RangeCalculator::get_range_close(){
|
||||
return close_time;
|
||||
};
|
||||
|
||||
// flag if a high breakout occurred
|
||||
bool RangeCalculator::get_range_high_breakout(){
|
||||
return f_high_breakout;
|
||||
};
|
||||
|
||||
// flag if a low breakout occurred
|
||||
bool RangeCalculator::get_range_low_breakout(){
|
||||
return f_low_breakout;
|
||||
};
|
||||
|
||||
|
||||
void RangeCalculator::calculate_range(){
|
||||
|
||||
f_high_breakout = false;
|
||||
f_low_breakout = false;
|
||||
|
||||
double last_bar_high = iHigh(symbol, calc_period, 1); // shift 1 because 0 = live candle:
|
||||
double last_bar_low = iLow(symbol, calc_period, 1); // shift 1 because 0 = live candle:
|
||||
|
||||
// range calculation
|
||||
if(TimeCurrent() >= start_time && TimeCurrent() <= end_time){
|
||||
|
||||
// set flag
|
||||
f_entry = true;
|
||||
|
||||
// new high
|
||||
if(last_bar_high > high){
|
||||
high = last_bar_high;
|
||||
mid = (high + low)/2;
|
||||
if(plot_range){
|
||||
update_objects();
|
||||
}
|
||||
}
|
||||
|
||||
// new low
|
||||
if(last_bar_low < low){
|
||||
low = last_bar_low;
|
||||
mid = (high + low)/2;
|
||||
if(plot_range){
|
||||
update_objects();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// calculate new reange if
|
||||
if( (TimeCurrent() >= close_time) // close time reached
|
||||
|| (end_time == 0) // range not calculated yet
|
||||
|| (end_time !=0 && TimeCurrent() > end_time && !f_entry) // there was a range calculated but no tick inside.
|
||||
){
|
||||
define_new_range();
|
||||
}
|
||||
|
||||
// check if we are after the range end
|
||||
if(TimeCurrent() >= end_time && end_time > 0 && f_entry){
|
||||
|
||||
if(!f_high_breakout && last_bar_high >= high){
|
||||
above_last = above_current;
|
||||
above_current= true;
|
||||
|
||||
if(above_last==false && above_current == true){
|
||||
f_high_breakout = true;
|
||||
}
|
||||
else(f_high_breakout = false);
|
||||
}
|
||||
|
||||
if(!f_low_breakout && last_bar_low >= low){
|
||||
below_last = below_current;
|
||||
below_current = true;
|
||||
if(below_last == false && below_current == true){
|
||||
f_low_breakout = true;
|
||||
}
|
||||
else(f_low_breakout = false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void RangeCalculator::define_new_range(){
|
||||
|
||||
// reset range vars
|
||||
start_time = 0;
|
||||
end_time = 0;
|
||||
order_expire_time = 0;
|
||||
close_time = 0;
|
||||
high = 0;
|
||||
low = INT_MAX;
|
||||
mid = 0;
|
||||
f_entry = false;
|
||||
|
||||
// calculate range start time:
|
||||
datetime r_st = StringToTime(inp_r_start_string);
|
||||
start_time = tz.timezone_conversions(inp_timezone, r_st, "Broker");
|
||||
|
||||
|
||||
for(int i=0; i<8; i++){
|
||||
|
||||
MqlDateTime tmp;
|
||||
TimeToStruct(start_time,tmp);
|
||||
int dow = tmp.day_of_week;
|
||||
|
||||
if(TimeCurrent()>=start_time
|
||||
|| (dow==0 && !sun)
|
||||
|| (dow==1 && !mon)
|
||||
|| (dow==2 && !tue)
|
||||
|| (dow==3 && !wed)
|
||||
|| (dow==4 && !thu)
|
||||
|| (dow==5 && !fri)
|
||||
|| (dow==6 && !sat)
|
||||
){
|
||||
start_time += PeriodSeconds(PERIOD_D1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
end_time = start_time + r_duration;
|
||||
order_expire_time = start_time + r_expire;
|
||||
close_time = start_time + r_close;
|
||||
|
||||
if(plot_range){
|
||||
draw_objects();
|
||||
}
|
||||
}
|
||||
|
||||
void RangeCalculator::update_objects(){
|
||||
|
||||
string name = "Range Mid " + (string)start_time;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, mid);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, mid);
|
||||
// ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid");
|
||||
|
||||
name = "Order expire " + (string)order_expire_time;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
|
||||
|
||||
name = "Range start " + (string)start_time;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
|
||||
|
||||
name = "Range end " + (string)end_time;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
|
||||
|
||||
datetime rct = r_close>=0 ? close_time : INT_MAX;
|
||||
name = "Range close " + (string)rct;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
|
||||
|
||||
name = "Range High " + (string)rct;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, high);
|
||||
|
||||
name = "Range Low " + (string)rct;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, low);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
|
||||
|
||||
name = "range box "+ (string)start_time;
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name, OBJPROP_PRICE,1, low);
|
||||
ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,0, high);
|
||||
ObjectSetDouble(NULL, name +" ", OBJPROP_PRICE,1, low);
|
||||
|
||||
}
|
||||
|
||||
void RangeCalculator::draw_objects(){
|
||||
|
||||
datetime rct = r_close>=0 ? close_time : INT_MAX;
|
||||
|
||||
// Range mid line
|
||||
string name = "Range Mid " + (string)start_time;;
|
||||
ObjectCreate(NULL, name, OBJ_TREND, 0, start_time, mid, rct, mid);
|
||||
ObjectSetString(NULL, name , OBJPROP_TOOLTIP, "Range Mid" + (string)mid);
|
||||
ObjectSetInteger(NULL, name, OBJPROP_COLOR, clrGray);
|
||||
ObjectSetInteger(NULL, name, OBJPROP_WIDTH, 1);
|
||||
ObjectSetInteger(NULL, name, OBJPROP_STYLE, STYLE_DOT);
|
||||
|
||||
// order lines
|
||||
string name2 = "Order expire " + (string)order_expire_time;
|
||||
ObjectCreate(NULL, name2, OBJ_TREND, 0, order_expire_time, low, order_expire_time, high);
|
||||
ObjectSetString(NULL, name2, OBJPROP_TOOLTIP, "start of the range \n" + TimeToString(order_expire_time,TIME_DATE|TIME_MINUTES));
|
||||
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, C'139,41,41');
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
|
||||
|
||||
name2 = "Range start " + (string)start_time;
|
||||
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, start_time, high);
|
||||
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
|
||||
|
||||
name2 = "Range end " + (string)end_time;
|
||||
ObjectCreate(NULL, name2, OBJ_TREND, 0, end_time, low, end_time, high);
|
||||
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
|
||||
|
||||
name2 = "Range close " + (string)rct;
|
||||
ObjectCreate(NULL, name2, OBJ_TREND, 0, rct, low, rct, high);
|
||||
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
|
||||
|
||||
name2 = "Range High " + (string)rct;
|
||||
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, high, rct, high);
|
||||
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(NULL, name2,OBJPROP_BACK, true);
|
||||
|
||||
name2 = "Range Low " + (string)rct;
|
||||
ObjectCreate(NULL, name2, OBJ_TREND, 0, start_time, low, rct, low);
|
||||
ObjectSetInteger(NULL, name2, OBJPROP_COLOR, clrBlack);
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_WIDTH, 2);
|
||||
ObjectSetInteger(NULL, name2 ,OBJPROP_BACK, true);
|
||||
|
||||
// Box
|
||||
name = "range box " + (string)start_time;
|
||||
ObjectCreate(NULL, name, OBJ_RECTANGLE, 0, start_time, high, end_time, low);
|
||||
ObjectSetString(NULL,name,OBJPROP_TOOLTIP,"\n");
|
||||
ObjectSetInteger(NULL, name,OBJPROP_COLOR, C'128,177,173');
|
||||
ObjectSetInteger(NULL, name,OBJPROP_FILL, true);
|
||||
ObjectSetInteger(NULL, name,OBJPROP_BACK, true);
|
||||
|
||||
ObjectCreate(NULL, name + " ", OBJ_RECTANGLE, 0, end_time, high, rct, low);
|
||||
ObjectSetString(NULL, name+ " ", OBJPROP_TOOLTIP, "\n");
|
||||
ObjectSetInteger(NULL, name + " ",OBJPROP_FILL, true);
|
||||
ObjectSetInteger(NULL, name + " ",OBJPROP_COLOR, C'165,220,215' );
|
||||
ObjectSetInteger(NULL, name + " ",OBJPROP_BACK, true);
|
||||
|
||||
ChartRedraw();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include <Object.mqh>
|
||||
|
||||
class ChartUtils : public CObject {
|
||||
|
||||
public:
|
||||
void draw_line(double value, string name, color clr = clrBlack);
|
||||
};
|
||||
|
||||
void ChartUtils::draw_line(double value, string name, color clr) {
|
||||
if (ObjectFind(0, name) < 0) {
|
||||
ResetLastError();
|
||||
|
||||
if (!ObjectCreate(0, name, OBJ_HLINE, 0, 0, value)) {
|
||||
Print(__FUNCTION__, ": failed to create a horizontal line! Error code = ", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||||
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
|
||||
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
|
||||
}
|
||||
|
||||
ResetLastError();
|
||||
|
||||
if (!ObjectMove(0, name, 0, 0, value)) {
|
||||
Print(__FUNCTION__, ": failed to move the horizontal line! Error code = ", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
ChartRedraw();
|
||||
}
|
||||
@@ -1,53 +1,41 @@
|
||||
#property library
|
||||
|
||||
enum LOT_MODE{
|
||||
LOT_MODE_FIXED, // Fixed Lot Size
|
||||
LOT_MODE_PCT_ACCOUNT, // Percent of Account (fixed)
|
||||
LOT_MODE_PCT_RISK // Percent of Account at Risk (from SL)
|
||||
};
|
||||
enum SL_MODE{
|
||||
SL_FIXED_PIPS, // Fixed Pips
|
||||
SL_FIXED_PERCENT, // Fixed Percent
|
||||
SL_ATR_MULTIPLE, // ATR Multiple
|
||||
SL_SPECIFIED_VALUE, // Bespoke calculation in code
|
||||
NO_STOPLOSS, // No Stop-loss
|
||||
SL_BREAKEVEN, // Breakeven
|
||||
};
|
||||
enum TP_MODE{
|
||||
TP_FIXED_PIPS, // Fixed Pips
|
||||
TP_FIXED_PERCENT, // Fixed Percent
|
||||
TP_ATR_MULTIPLE, // ATR Multiple
|
||||
TP_SL_MULTIPLE, // Multiple of Risk (from sl)
|
||||
TP_SPECIFIED_VALUE, // Bespoke calculation in code
|
||||
NO_TAKE_PROFIT, // No Take-Profit
|
||||
};
|
||||
|
||||
enum TSL_MODE{
|
||||
TSL_ATR_MULTIPLE, // ATR Multiple
|
||||
TSL_FIXED_PIPS, // Fixed Pips
|
||||
TSL_FIXED_PERCENT, // Fixed Percent
|
||||
};
|
||||
|
||||
enum TIME_ZONES{
|
||||
NY, // New York
|
||||
Lon, // London
|
||||
Ffm, // Frankfurt
|
||||
Syd, // Sidney
|
||||
Mosc, // Moscow
|
||||
Tok, // Tokyo - no DST
|
||||
};
|
||||
enum MULTI_SYM_MODE{
|
||||
MULTI_SYM_CHART, // Chart Symbol only
|
||||
MULTI_SYM_FX_B5, // FX Benchmark 5
|
||||
MULTI_SYM_FX_28 // FX 28 Majors
|
||||
};
|
||||
// used to generate in and out of sample data sets
|
||||
enum MODE_SPLIT_DATA{
|
||||
NO_SPLIT,
|
||||
ODD_YEARS,
|
||||
EVEN_YEARS,
|
||||
ODD_MONTHS,
|
||||
EVEN_MONTHS,
|
||||
ODD_WEEKS,
|
||||
EVEN_WEEKS
|
||||
};
|
||||
enum LOT_MODE{
|
||||
LOT_MODE_FIXED, // Fixed Lot Size
|
||||
LOT_MODE_PCT_ACCOUNT, // Percent of Account (fixed)
|
||||
LOT_MODE_PCT_RISK // Percent of Account at Risk (from SL)
|
||||
};
|
||||
enum SL_MODE{
|
||||
SL_FIXED_PIPS, // Fixed Pips
|
||||
SL_FIXED_PERCENT, // Fixed Percent
|
||||
SL_ATR_MULTIPLE, // ATR Multiple
|
||||
SL_SPECIFIED_VALUE, // Bespoke calculation in code
|
||||
NO_STOPLOSS, // No Stop-loss
|
||||
SL_BREAKEVEN, // Breakeven
|
||||
};
|
||||
enum TP_MODE{
|
||||
TP_FIXED_PIPS, // Fixed Pips
|
||||
TP_FIXED_PERCENT, // Fixed Percent
|
||||
TP_ATR_MULTIPLE, // ATR Multiple
|
||||
TP_SL_MULTIPLE, // Multiple of Risk (from sl)
|
||||
TP_SPECIFIED_VALUE, // Bespoke calculation in code
|
||||
NO_TAKE_PROFIT, // No Take-Profit
|
||||
};
|
||||
|
||||
enum TSL_MODE{
|
||||
TSL_ATR_MULTIPLE, // ATR Multiple
|
||||
TSL_FIXED_PIPS, // Fixed Pips
|
||||
TSL_FIXED_PERCENT, // Fixed Percent
|
||||
};
|
||||
|
||||
enum TIME_ZONES{
|
||||
NY, // New York
|
||||
Lon, // London
|
||||
Ffm, // Frankfurt
|
||||
Syd, // Sidney
|
||||
Mosc, // Moscow
|
||||
Tok, // Tokyo - no DST
|
||||
};
|
||||
enum MULTI_SYM_MODE{
|
||||
MULTI_SYM_CHART, // Chart Symbol only
|
||||
MULTI_SYM_FX_B5, // FX Benchmark 5
|
||||
MULTI_SYM_FX_28 // FX 28 Majors
|
||||
};
|
||||
@@ -1,28 +1,28 @@
|
||||
#include <MyLibs/Myfunctions.mqh>
|
||||
#include <MyLibs/OrderManagement.mqh>
|
||||
#include <MyLibs/MyEnums.mqh>
|
||||
#include <MyLibs/CustomMax.mqh>
|
||||
CustomMax cm;
|
||||
MyFunctions mf;
|
||||
OrderManagment om;
|
||||
//---
|
||||
string SymbolsArray[];
|
||||
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_200T;
|
||||
input MULTI_SYM_MODE inp_sym_mode = MULTI_SYM_FX_B5;
|
||||
input MODE_SPLIT_DATA inp_data_split_method = NO_SPLIT;
|
||||
input int inp_force_opt = 1;
|
||||
input group "-----------------------------------------"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#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 "-----------------------------------------"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
class MarketDataUtils {
|
||||
public:
|
||||
bool is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time = "00:10");
|
||||
double get_latest_buffer_value(int handle);
|
||||
double get_buffer_value(int handle, int shift);
|
||||
double adjusted_point(string symbol);
|
||||
double get_bid_ask_price(string symbol, int price_side);
|
||||
|
||||
protected:
|
||||
datetime previousTime; // Stores the last recorded bar open time
|
||||
datetime bar_open_time; // Stores the current bar's open time
|
||||
};
|
||||
|
||||
// Checks if a new bar has opened on the given timeframe and symbol
|
||||
bool MarketDataUtils::is_new_bar(string symbol, ENUM_TIMEFRAMES time_frame, string daily_start_time) {
|
||||
bar_open_time = iTime(symbol, time_frame, 0); // Current open time
|
||||
|
||||
if (previousTime != 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)) {
|
||||
previousTime = bar_open_time;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
previousTime = bar_open_time;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Retrieves a historical buffer value at specified shift
|
||||
double MarketDataUtils::get_buffer_value(int handle, int shift) {
|
||||
double val[];
|
||||
ArraySetAsSeries(val, true);
|
||||
|
||||
if (CopyBuffer(handle, 0, shift, 1, val) == 1)
|
||||
return val[0]; // Historical value at given shift
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Adjusts the point value for symbol to account for fractional pips (e.g., 5-digit brokers)
|
||||
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
|
||||
}
|
||||
|
||||
// Returns current Bid or Ask price for a symbol based on side (1 = Ask, 2 = Bid)
|
||||
double MarketDataUtils::get_bid_ask_price(string symbol, int price_side) {
|
||||
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
|
||||
double ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), digits);
|
||||
double bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), digits);
|
||||
|
||||
if (price_side == 1) return ask;
|
||||
if (price_side == 2) return bid;
|
||||
|
||||
return 0.0; // Invalid input
|
||||
}
|
||||
+143
-143
@@ -1,144 +1,144 @@
|
||||
#property library
|
||||
#include <Trade/Trade.mqh>
|
||||
#include <MyLibs/DealingWithTime.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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
checkTimeOffset(TimeCurrent()); // check changes of DST
|
||||
// cto();
|
||||
|
||||
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(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;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
tGIVEN = time_given; //StringToTime(time_given);
|
||||
|
||||
checkTimeOffset(tGIVEN); // check changes of DST
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
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
|
||||
|
||||
if(TimeCurrent()<ny_close_time){
|
||||
ny_close_time = ny_close_time - PeriodSeconds(PERIOD_D1); // ny close today
|
||||
}
|
||||
|
||||
// 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:
|
||||
|
||||
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;
|
||||
#property library
|
||||
#include <Trade/Trade.mqh>
|
||||
#include <MyLibs/Utils/DealingWithTime.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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
checkTimeOffset(TimeCurrent()); // check changes of DST
|
||||
// cto();
|
||||
|
||||
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(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;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
tGIVEN = time_given; //StringToTime(time_given);
|
||||
|
||||
checkTimeOffset(tGIVEN); // check changes of DST
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
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
|
||||
|
||||
if(TimeCurrent()<ny_close_time){
|
||||
ny_close_time = ny_close_time - PeriodSeconds(PERIOD_D1); // ny close today
|
||||
}
|
||||
|
||||
// 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:
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#include <MyLibs/Utils/TimeZones.mqh>
|
||||
|
||||
class TradeSessionUtils {
|
||||
|
||||
protected:
|
||||
TimeZones tz; // For handling timezone conversion
|
||||
bool in_window; // Whether the current time is in the allowed window
|
||||
datetime start_time; // Session start time (converted to Broker time)
|
||||
datetime end_time; // Session end time (converted to Broker time)
|
||||
|
||||
public:
|
||||
bool trade_window(string t1, string t2, string time_zone = "Broker", bool plot_range_inp = true);
|
||||
};
|
||||
|
||||
bool TradeSessionUtils::trade_window(string t1, string t2, string time_zone, bool plot_range_inp) {
|
||||
datetime _t1 = StringToTime(t1); // Convert string to datetime
|
||||
datetime _t2 = StringToTime(t2); // Convert string to datetime
|
||||
|
||||
// Handle overnight windows (e.g. 22:00–01:00)
|
||||
if (_t1 > _t2) {
|
||||
_t2 = _t2 + PeriodSeconds(PERIOD_D1);
|
||||
}
|
||||
|
||||
int w_duration = (int)(_t2 - _t1); // Duration of the session in seconds
|
||||
|
||||
// Check if we're currently within the window
|
||||
if (TimeCurrent() >= start_time && TimeCurrent() <= end_time) {
|
||||
in_window = true;
|
||||
}
|
||||
|
||||
// If we've moved beyond the previous window, define a new one
|
||||
if (TimeCurrent() >= end_time) {
|
||||
in_window = false;
|
||||
|
||||
// Convert start time to broker time based on user timezone input
|
||||
start_time = tz.timezone_conversions(time_zone, StringToTime(t1), "Broker");
|
||||
|
||||
// If we've already passed today's start time, push it to tomorrow
|
||||
if (TimeCurrent() >= start_time) {
|
||||
start_time += PeriodSeconds(PERIOD_D1);
|
||||
}
|
||||
|
||||
// End time is relative to updated start time
|
||||
end_time = start_time + w_duration;
|
||||
|
||||
// Plot vertical lines if requested
|
||||
if (plot_range_inp) {
|
||||
string name = "Start Time" + (string)start_time;
|
||||
if (start_time > 0) {
|
||||
ObjectCreate(NULL, name, OBJ_VLINE, 0, start_time, 0);
|
||||
ObjectSetInteger(NULL, name, OBJPROP_COLOR, clrBlue);
|
||||
ObjectSetInteger(NULL, name, OBJPROP_BACK, true);
|
||||
}
|
||||
|
||||
name = "End Time" + (string)end_time;
|
||||
if (end_time > 0) {
|
||||
ObjectCreate(NULL, name, OBJ_VLINE, 0, end_time, 0);
|
||||
ObjectSetInteger(NULL, name, OBJPROP_COLOR, C'56,108,26');
|
||||
ObjectSetInteger(NULL, name, OBJPROP_BACK, true);
|
||||
}
|
||||
|
||||
ChartRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
return in_window;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
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 +1,278 @@
|
||||
#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;
|
||||
#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 +1,211 @@
|
||||
#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;
|
||||
|
||||
#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;
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
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 +1,65 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
#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.
Reference in New Issue
Block a user