#property copyright "Copyright 2025, MARKT - Mark kasempast kumkaew" #property link "https://www.facebook.com/elitetradersbymarkt" #property version "1.0" #property description "The Oracle Dashboard - By MARKT - VERSION_V1.0" #property description "Contact : Elite Trader's Guild - EA by MARKT" #include #include #include CTrade trade; //input group "----GUARD----" // เว้นว่าง/ค่าเริ่มต้น = ไม่ตรวจข้อนั้น const string InpGuard_AccountIDs = ""; // ใส่ได้หลายเลขพอร์ต คั่นด้วย , เช่น "123456,789012" const string InpGuard_ExpiryDate = ""; // รูปแบบ "YYYY-MM-DD" เช่น "2025-12-31" const string InpGuard_Server = ""; // ชื่อ/บางส่วนของ server เช่น "ICMarketsSC" enum ENUM_GUARD_ACCTYPE { GUARD_ANY, GUARD_DEMO, GUARD_REAL }; const ENUM_GUARD_ACCTYPE InpGuard_AccountType = GUARD_ANY; // เลือกโหมดปฏิบัติการเมื่อไม่ผ่าน (ล็อกแข็ง / ล็อกนิ่ม) enum ENUM_GUARD_MODE { GUARD_HARD_LOCK, GUARD_SOFT_LOCK }; const ENUM_GUARD_MODE InpGuard_Mode = GUARD_HARD_LOCK; input group "โปรดอ่านคู่มือให้เข้าใจก่อนนะครับ (MARKT)" input group "#### Broker Symbols & Magic Number ####" // ปล่อยว่างไว้ = ให้ระบบตรวจจับอัตโนมัติจาก Market Watch (เช่น EURUSD.sc -> suffix = ".sc") input string InpSymbolSuffix = ""; //SymbalSuffix input ulong InpMagicNumber = 112233; //MagicNumber input group "#### Logic Intensity ####" input double InpLogicUsePercent = 80.0; //LogicPercent //input group "----Realtime----" const bool InpRealtimeEnable = true; // อัปเดตระดับมิลลิวินาที const int InpRefreshMs = 200; // ความถี่อัปเดตข้อมูล (ms) const int InpUiUpdateMs = 150; // ความถี่รีเฟรช UI (ms) const bool InpCalcOnClosedBar = true; input group "#### Execution Control ####" input int InpRowCooldownBars = 2; //RowCooldownBar input int InpMaxPositionsPerRow = 2; input double InpHighlightSignalAtPercent = 60.0; //HighlightSignalPercent const int InpFontSize = 8; input ENUM_TIMEFRAMES InpTimeframe_Confirmation = PERIOD_H4; //ConfirmTimeframe input ENUM_TIMEFRAMES InpTimeframe = PERIOD_H1; //MainTimeframe input int InpLookback = 220; //LookbackBar input int InpRefreshSec = 1; //RefreshSec input group "#### SL/TP Settings ####" input double InpRR_TP_to_SL = 1.50; // Risk:Reward input group "#### Money Management ####" input double InpLotSize = 0.03; //LotSize(Fixed Lot) input double InpRiskPercent = 1.5; //RiskPercent (Auto Risk%) input int InpATR_Period = 14; //ATR-Period input double InpATR_Multiplier= 2.0; //ATR-Multiplier input int InpADX_Period = 14; //ADX-Period input double InpADX_TrendLevel= 25.0; //ADX-TrendLevel input group "#### Basket Exit Strategies ####" input double InpBasketTakeProfitPercent = 5.0; //BasketTakeProfitPercent input double InpBasketStopLossPercent = 2.5; //BasketStopLossPercent input double InpBasketTakeProfitMoney = 50.0; //BasketTakeProfitMoney input double InpBasketStopLossMoney = 25.0; //BasketStopLossMoney input group "#### Individual Exit Strategies ####" input int InpMaxBarsHold = 150; //MaxBars input double InpCorrDecayThreshold = 0.3; //CorrDecayThreshold input group "#### Core Settings ####" input bool InpAutoConfirm = false; //AutoTrade input double InpMinConfidenceForAutoTrade = 65.0; //AutoTradeMinCONF% input double InpMinAbsCorrForOpportunity = 0.60; //AutoTradeMinCorr (Opportunity) input group "#### Force Auto Entry ####" input bool InpForceAuto = false; //EnableFoceAutoTrade input bool InpForceAuto_OnSignalOnly = false; //ForceAuto_OnSignalOnly input double InpForceAutoMinConf = 70.0; //ForceAutoMinConf input bool InpForceAutoBypassFilters = false; //ForceAutoBypassFilters enum ENUM_ROW_TP_MODE { ROW_TP_MODE_PERCENT, ROW_TP_MODE_MONEY }; input group "#### Row Auto Close (TP) ####" input bool InpRowAutoTP_Enable = false; //EnableAutoCloseROW input ENUM_ROW_TP_MODE InpRowTPMode = ROW_TP_MODE_MONEY; //ROW-TP-Mode input double InpRowTP_Percent = 0.80; //RowTP-Percent input double InpRowTP_Money = 10.0; //RowTP-Money enum ENUM_OPP_TP_MODE { OPP_TP_MODE_PERCENT, OPP_TP_MODE_MONEY }; input bool InpOppAutoTP_Enable = false; //EnableAutoCloseOpportunity input ENUM_OPP_TP_MODE InpOppTPMode = OPP_TP_MODE_MONEY; //Opportunity-TP-Mode input double InpOppTP_Percent = 0.50; //Opportunity-Percent input double InpOppTP_Money = 8.0; //Opportunity-Money input group "#### Signal Filters ####" input bool InpUsePreFilters = true; //PreFillters input double InpMaxSpreadPoints = 30; //MaxSpreadPoint input double InpMinATRPoints = 10; //MinATRPoints input bool InpRequireHAFreshFlip = false; //EnableRequireHAFreshFlip input int InpHAFreshBars = 3; //HAFreshBars enum ENUM_OPP_MODE { OPP_MODE_MEANREVERT, OPP_MODE_SWING, OPP_MODE_SCALP, OPP_MODE_AUTO }; input group "#### Opportunity Finder Mode ####" input ENUM_OPP_MODE InpOppMode = OPP_MODE_MEANREVERT; //OF-Mode input group "#### Correlation ZScore ####" input double InpCorrPosThreshold = 0.60; //CorrPosThreshold input double InpCorrNegThreshold = -0.60; //CorrNegThreshold input double InpZSpikeEnter = 1.20; //ZSpikeEnter input double InpZSpikeExit = 0.80; //ZSpikeExit input group "#### Basket Trailing ####" input bool InpEnableBasketTrailing = false; //EnableBasketTrailing input double InpBasketTrailArmPercent = 3.0; //BasketTrailArmPercent input double InpBasketTrailStepPercent = 1.0; //BasketTrailStepPercent const int InpRightPanelShiftX = 0; const bool InpForceManualButtons = true; const double InpRiskMismatchWarnPct = 30.0; enum ENUM_BASKET_EXIT_MODE { MODE_PERCENT, MODE_MONEY }; enum ENUM_SIGNAL_TYPE { SIGNAL_NONE, SIGNAL_BB, SIGNAL_SS, SIGNAL_BS, SIGNAL_SB }; enum ENUM_LOT_SIZING_MODE { LOT_MODE_FIXED, LOT_MODE_AUTO }; struct CorrelationPairInfo { string symbol1; string symbol2; double current_corr; double avg_corr; double delta_corr; double history[]; }; struct SymbolPriceCache { string name; double prices[]; }; struct SignalInfo { ENUM_SIGNAL_TYPE type; double confidence; SignalInfo() : type(SIGNAL_NONE), confidence(0.0) {} SignalInfo(const SignalInfo &other) : type(other.type), confidence(other.confidence) {} }; struct OpportunityInfo { string symbol; double correlation; string style; ENUM_SIGNAL_TYPE trade_action; double confidence; OpportunityInfo() : correlation(0), trade_action(SIGNAL_NONE), confidence(0.0) {} OpportunityInfo(const OpportunityInfo &other) : symbol(other.symbol), correlation(other.correlation), style(other.style), trade_action(other.trade_action), confidence(other.confidence) {} }; #define TOTAL_PAIRS 20 const int CORR_HISTORY_SIZE = 20; const int SYDNEY_OPEN = 22; const int SYDNEY_CLOSE = 7; const int TOKYO_OPEN = 0; const int TOKYO_CLOSE = 9; const int LONDON_OPEN = 8; const int LONDON_CLOSE = 17; const int NY_OPEN = 13; const int NY_CLOSE = 22; const int c_row_h = 25; const int c_margin = 0; const color c_main_bg = clrWhiteSmoke; const color c_bg_dark = C'30, 45, 70'; const color c_bg_light = C'40, 60, 90'; const color c_header_bg = C'10, 20, 40'; const color c_font_color = clrWhiteSmoke; const color c_edit_bg = C'20, 40, 60'; const color c_edit_border = clrWhiteSmoke; const color c_btn_off_bg = C'128,0,0'; const color c_btn_on_bg = C'0,128,0'; const color c_btn_bb_ss_bg = C'0, 80, 170'; const color c_btn_bs_sb_bg = C'170, 80, 0'; const color c_btn_highlight_bg = clrGold; const int c_col_close_w = 40; const int c_col_profit_w = 65; const int c_col_order_w = 40; const int c_col_entry_buy_w = 60; const int c_col_auto_w = 40; const int c_col_entry_sell_w = 60; const int c_col_corr_w = 160; const int c_col_current_corr_w = 55; const int c_col_delta_corr_w = 55; const int c_col_sprd_div_w = 80; const int c_col_confidence_w = 60; const int c_info_panel_w = 165; const int c_col_close_x = 15; const int c_col_profit_x = c_col_close_x + c_col_close_w + c_margin; const int c_col_order_x = c_col_profit_x + c_col_profit_w + c_margin; const int c_col_entry_buy_x = c_col_order_x + c_col_order_w + c_margin; const int c_col_auto_x = c_col_entry_buy_x + c_col_entry_buy_w + c_margin; const int c_col_entry_sell_x = c_col_auto_x + c_col_auto_w + c_margin; const int c_col_corr_x = c_col_entry_sell_x + c_col_entry_sell_w + c_margin; const int c_col_current_corr_x = c_col_corr_x + c_col_corr_w + c_margin; const int c_col_delta_corr_x = c_col_current_corr_x + c_col_current_corr_w + c_margin; const int c_col_sprd_div_x = c_col_delta_corr_x + c_col_delta_corr_w + c_margin; const int c_col_confidence_x = c_col_sprd_div_x + c_col_sprd_div_w + c_margin; const int c_main_panel_w = c_col_confidence_x + c_col_confidence_w + 5; const int c_info_panel_x = c_main_panel_w + 10; const int c_total_panel_w = c_info_panel_x + c_info_panel_w; string g_object_prefix = "AdvDash_"; string g_pairs[TOTAL_PAIRS][2] = { {"EURUSD","USDCHF"}, {"GBPUSD","USDJPY"}, {"AUDUSD","USDCAD"}, {"EURGBP","GBPUSD"}, {"USDJPY","EURCHF"}, {"USDCAD","AUDCHF"}, {"GBPCHF","USDJPY"}, {"AUDJPY","USDCAD"}, {"EURAUD","AUDUSD"}, {"GBPJPY","USDCHF"}, {"EURNZD","NZDUSD"}, {"CADJPY","USDCHF"}, {"CHFJPY","USDCHF"}, {"EURCAD","CADJPY"}, {"GBPAUD","AUDUSD"}, {"AUDNZD","NZDUSD"}, {"EURJPY","USDCAD"}, {"GBPCAD","USDCAD"}, {"AUDCHF","USDCHF"}, {"NZDJPY","USDJPY"} }; bool g_auto_status[TOTAL_PAIRS]; CorrelationPairInfo g_corr_data[TOTAL_PAIRS]; SymbolPriceCache g_price_cache[]; string g_matrix_symbols[] = {"EUR","USD","JPY","GBP","AUD","NZD","CAD","CHF","XAU"}; double g_matrix_corr[9][9]; ENUM_LOT_SIZING_MODE g_lot_mode = LOT_MODE_FIXED; bool g_use_time_exit = false; bool g_use_corr_decay_exit = false; bool g_basket_exit_enabled = false; ENUM_BASKET_EXIT_MODE g_basket_exit_mode = MODE_PERCENT; bool g_use_mtf_filter = false; ENUM_TIMEFRAMES g_current_timeframe; OpportunityInfo g_opportunities[5]; int g_ha_handles_main[TOTAL_PAIRS][2]; int g_ha_handles_conf[TOTAL_PAIRS][2]; datetime g_last_cache_time[]; int g_timer_counter = 0; double g_basket_tp_percent; double g_basket_sl_percent; double g_basket_tp_money; double g_basket_sl_money; double g_fixed_lot_size; double g_risk_percent; int g_max_bars_hold; double g_corr_decay_threshold; string g_last_corr_text[TOTAL_PAIRS]; string g_last_delta_text[TOTAL_PAIRS]; string g_last_conf_text[TOTAL_PAIRS]; string g_last_sprd_text[TOTAL_PAIRS]; color g_last_corr_color[TOTAL_PAIRS]; color g_last_delta_color[TOTAL_PAIRS]; ENUM_SIGNAL_TYPE g_last_highlight[TOTAL_PAIRS]; int g_active_graph_row = -1; bool g_use_sl_tp = false; double g_rr = 1.5; string g_symbol_suffix = ""; datetime g_last_row_close_bar[TOTAL_PAIRS]; datetime g_row_last_hist_bar[TOTAL_PAIRS]; int g_adx_handles_main[TOTAL_PAIRS][2]; bool g_silent_force = false; enum ENUM_EXEC_CTX { EXEC_MANUAL, EXEC_AUTO, EXEC_FORCE_AUTO }; ENUM_EXEC_CTX g_exec_ctx = EXEC_MANUAL; ulong g_last_ui_ms = 0; double g_closed_profit_cache = 0.0; int g_closed_count_cache = 0; double g_bt_peak_percent = 0.0; double g_bt_stop_percent = -DBL_MAX; bool g_bt_armed = false; bool g_guard_ok = true; string g_guard_reason = ""; void Orc_BasketTrailing_Reset() { g_bt_peak_percent = 0.0; g_bt_stop_percent = -DBL_MAX; g_bt_armed = false; } void CreateDashboardUI(); void UpdateInfoPanel(); void UpdateDashboardData(); void CreateCellWithText(string name, string text, int x, int y, int w, int h, color bg_clr, color text_clr, int font_size, ENUM_ANCHOR_POINT anchor, string font="Arial", ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER); void CreateRectangle(string name, int x, int y, int w, int h, color clr, ENUM_BASE_CORNER corner, ENUM_BORDER_TYPE border=BORDER_FLAT, bool is_background=true); void CreateLabel(string name, string text, int x, int y, color clr, ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT, string font="Arial", int font_size=-1, ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER); void CreateButton(string name, string text, int x, int y, int w, int h, color bg_clr, ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER); void CreateEditBox(string name, string text, int x, int y, int w, int h, ENUM_ALIGN_MODE align=ALIGN_RIGHT); void UpdateAutoButton(int index); void BuildUniqueSymbolListAndPrepareCache(); bool GetPricesFromCache(string symbol, double &prices[]); double CalculatePearsonCorrelation(const double &x[], const double &y[], int count); double ArraySum(const double &arr[]); double ArrayMean(const double &arr[]); void ArrayRemove(double &arr[], int start_index, int count); void CreateTopPanels(int start_x, int start_y); void CreateBottomPanels(int start_x, int start_y); void UpdateTimePanel(); void SafeRedraw(); void CalculateClosedProfitAndCount(double &profit_out, int &count_out); void CreateCorrelationMatrix(int start_x, int start_y); void CreateHeader(int y_pos); void CreateDataRow(int index, int y_pos); color InterpolateColor(color start_color, color end_color, double factor); SignalInfo CheckTradingSignals(int index); double CalculateSignalConfidence(double correlation, double delta_corr); void UpdateSignalHighlight(int index, string button_to_highlight); void ResetAllHighlights(int index); void ExecuteTrade(int index, ENUM_SIGNAL_TYPE signal_type, bool allow_force=false); void ExecuteMatrixTrade(string symbol, ENUM_SIGNAL_TYPE trade_action); string SignalTypeToString(ENUM_SIGNAL_TYPE signal_type); void ManageOpenTrades(); void UpdateAndManageBasket(); void CloseAllBasketTrades(); void CloseProfitableBasketTrades(); double CalculateLotSize(string symbol); int GetRowIndexFromSymbol(string symbol); string GetSymbolForCurrencies(string ccy1, string ccy2); void UpdateCorrelationMatrix(); void FindMatrixOpportunities(); void UpdateOpportunityPanel(); void RecreateIndicatorHandles(); bool UpdatePriceCache(); void UpdatePairTradeStatus(); void ClosePairTrades(int row_index); void CloseAllProfitableTrades(); void CloseAllLosingTrades(); void CreateMainBackground(); bool IsSessionOpen(int open_hour, int close_hour, int current_hour_gmt); void CreateAnalysisPanel(int start_x, int start_y); void CloseCorrelationGraph(); void ShowCorrelationGraph(int row_index); double CalcOpportunityConfidence(const string symbol, const double corr, const ENUM_SIGNAL_TYPE action); void UpdateRowConfAndHighlight(const int index); int GetMatrixPixelHeight(); double AdjustConfidenceByLogic(double conf); double SmoothStep01(double t); double SoftCap100(double x, double k); double GetSpreadPts(const string sym); bool IsMajorSessionOpen(); void ComputeMatrixStrength(double &strength[]); bool LegsForCurrencies(const string a, const string b, string &legA, string &legB); bool ZSpreadByLegs(const string ccy1, const string ccy2, double &z, ENUM_SIGNAL_TYPE &sig); void AddOpp(OpportunityInfo &arr[], int &count, const string sym, const double corr, const string style, const ENUM_SIGNAL_TYPE sig); void NotifyUser(const string msg); void ManageAutoCloseRowsAndOpp(); void CloseMatrixSymbolTrades(const string symbol); string LegLabel(const ENUM_ORDER_TYPE t); bool PreflightDeal(const string sym, const ENUM_ORDER_TYPE typ, double vol, double &sl, double &tp, string &reason, uint &retcodeOut); bool SendDealRawEx(const string sym, const ENUM_ORDER_TYPE typ, double vol, double sl, double tp, const string &cmt, uint &retcodeOut, string &commentOut); double CalcOpportunityConfidence(const string symbol, const double corr, const ENUM_SIGNAL_TYPE action) { double absCorr = MathAbs(corr); double t0 = InpMinAbsCorrForOpportunity; double t = (absCorr - t0) / MathMax(1e-6, 1.0 - t0); double base = 60.0 * SmoothStep01(t); double align = 0.0; ENUM_SIGNAL_TYPE dir; if(SingleSymbolDirection(symbol, dir)) align = (dir == action ? 12.0 : 4.0); double sprPts = GetSpreadPts(symbol); double sprLim = MathMax(1.0, GetSpreadLimit(symbol)); double sprRatio= sprPts / sprLim; double spread_pen = (sprRatio > 1.0) ? -12.0 * MathMin(1.0, sprRatio - 1.0) : 0.0; double atr_bonus = 0.0; int h = iATR(symbol, g_current_timeframe, InpATR_Period); double b[]; double pt = SymbolInfoDouble(symbol, SYMBOL_POINT); if(h != INVALID_HANDLE && CopyBuffer(h,0,0,2,b) == 2 && pt > 0.0) { double atr_pts = b[1] / pt; double r = atr_pts / MathMax(1.0, InpMinATRPoints); double rr = Clamp01((r - 1.0) / 2.0); atr_bonus = -4.0 + rr * (8.0 + 4.0); } double sess = IsMajorSessionOpen() ? 6.0 : -3.0; double pf = PassesPreTradeFilters(symbol) ? 5.0 : -6.0; double raw = base + align + atr_bonus + sess + pf + spread_pen; double conf = SoftCap100(MathMax(0.0, raw), 85.0); conf = AdjustConfidenceByLogic(conf); if(conf > 97.0) conf = 97.0; if(conf < 3.0) conf = 3.0; return conf; } void UpdateRowConfAndHighlight(const int index) { SignalInfo sig = CheckTradingSignals(index); string conf_text = StringFormat("%.1f%%", sig.confidence); if(g_last_conf_text[index] != conf_text) { ObjectSetString(0, g_object_prefix + "CONF_CELL_" + (string)index + "_TXT", OBJPROP_TEXT, conf_text); g_last_conf_text[index] = conf_text; } color conf_color = (sig.confidence >= 70.0) ? C'30,255,150' : (sig.confidence >= 40.0 ? clrOrange : C'255,99,71'); ObjectSetInteger(0, g_object_prefix + "CONF_CELL_" + (string)index + "_TXT", OBJPROP_COLOR, conf_color); if(sig.type != SIGNAL_NONE && sig.confidence >= InpHighlightSignalAtPercent) { if(g_last_highlight[index] != sig.type) { UpdateSignalHighlight(index, SignalTypeToString(sig.type)); g_last_highlight[index] = sig.type; } } else { if(g_last_highlight[index] != SIGNAL_NONE) { ResetAllHighlights(index); g_last_highlight[index] = SIGNAL_NONE; } } if(g_auto_status[index] && sig.type != SIGNAL_NONE) { if(HasOpenForRow(index)) return; if(InRowCooldown(index)) return; bool normal_auto_ready = (InpAutoConfirm && sig.confidence >= InpMinConfidenceForAutoTrade); bool force_auto_ready = false; if(InpForceAuto) { if(InpForceAuto_OnSignalOnly) force_auto_ready = true; else if(sig.confidence >= InpForceAutoMinConf) force_auto_ready = true; } if(normal_auto_ready || force_auto_ready) { ENUM_EXEC_CTX prev_ctx = g_exec_ctx; g_exec_ctx = (force_auto_ready ? EXEC_FORCE_AUTO : EXEC_AUTO); if(force_auto_ready && InpForceAutoBypassFilters) { bool prev_silent = g_silent_force; g_silent_force = true; ExecuteTrade(index, sig.type, true); g_silent_force = prev_silent; } else { ExecuteTrade(index, sig.type); } g_exec_ctx = prev_ctx; } } } int OnInit() { g_guard_ok = Guard_Passes(g_guard_reason); if(!g_guard_ok) { Print("[GUARD] Blocked: ", g_guard_reason, " | login=", (long)AccountInfoInteger(ACCOUNT_LOGIN), " | server=", AccountInfoString(ACCOUNT_SERVER)); if(InpGuard_Mode == GUARD_HARD_LOCK) return(INIT_FAILED); } g_current_timeframe = InpTimeframe; DetectSymbolSuffix(g_symbol_suffix); Print("Symbol suffix in use: '", g_symbol_suffix, "'"); ApplySuffixToPairs(); g_basket_tp_percent = InpBasketTakeProfitPercent; g_basket_sl_percent = InpBasketStopLossPercent; g_basket_tp_money = InpBasketTakeProfitMoney; g_basket_sl_money = InpBasketStopLossMoney; g_fixed_lot_size = InpLotSize; g_risk_percent = InpRiskPercent; g_max_bars_hold = InpMaxBarsHold; g_corr_decay_threshold = InpCorrDecayThreshold; g_rr = InpRR_TP_to_SL; ArrayInitialize(g_auto_status, false); ArrayInitialize(g_ha_handles_main, -1); ArrayInitialize(g_ha_handles_conf, -1); ArrayInitialize(g_adx_handles_main, -1); ArrayInitialize(g_row_last_hist_bar, 0); for(int i=0; i= 0) { CloseCorrelationGraph(); return; } if(StringFind(sparam, g_object_prefix + "CTRL_") >= 0) { if(sparam == g_object_prefix + "CTRL_CLOSE_PROFIT_BTN") { CloseAllProfitableTrades(); } else if(sparam == g_object_prefix + "CTRL_CLOSE_LOSS_BTN") { CloseAllLosingTrades(); } else if(sparam == g_object_prefix + "CTRL_CLOSE_ALL_BTN") { CloseAllBasketTrades(); } else if(sparam == g_object_prefix + "CTRL_BASKET_BTN_MODE_PERCENT") { g_basket_exit_mode = MODE_PERCENT; ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, c_btn_on_bg); ObjectSetInteger(0, g_object_prefix + "CTRL_BASKET_BTN_MODE_MONEY", OBJPROP_BGCOLOR, c_bg_light); } else if(sparam == g_object_prefix + "CTRL_BASKET_BTN_MODE_MONEY") { g_basket_exit_mode = MODE_MONEY; ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, c_btn_on_bg); ObjectSetInteger(0, g_object_prefix + "CTRL_BASKET_BTN_MODE_PERCENT", OBJPROP_BGCOLOR, c_bg_light); } else if(sparam == g_object_prefix + "CTRL_BASKET_BTN_ENABLE") { g_basket_exit_enabled = !g_basket_exit_enabled; ObjectSetString(0, sparam, OBJPROP_TEXT, g_basket_exit_enabled ? "BASKET EXIT IS ON" : "ENABLE BASKET EXIT"); ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, g_basket_exit_enabled ? c_btn_on_bg : c_btn_off_bg); if(!g_basket_exit_enabled) Orc_BasketTrailing_Reset(); } else if(sparam == g_object_prefix + "CTRL_TRADE_SETTINGS_BTN_LOT_FIXED") { g_lot_mode = LOT_MODE_FIXED; ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, c_btn_on_bg); ObjectSetInteger(0, g_object_prefix + "CTRL_TRADE_SETTINGS_BTN_LOT_AUTO", OBJPROP_BGCOLOR, c_btn_off_bg); } else if(sparam == g_object_prefix + "CTRL_TRADE_SETTINGS_BTN_LOT_AUTO") { g_lot_mode = LOT_MODE_AUTO; ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, c_btn_on_bg); ObjectSetInteger(0, g_object_prefix + "CTRL_TRADE_SETTINGS_BTN_LOT_FIXED", OBJPROP_BGCOLOR, c_btn_off_bg); } else if(sparam == g_object_prefix + "CTRL_TRADE_SETTINGS_BTN_TIME_EXIT") { g_use_time_exit = !g_use_time_exit; ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, g_use_time_exit ? c_btn_on_bg : c_btn_off_bg); } else if(sparam == g_object_prefix + "CTRL_TRADE_SETTINGS_BTN_CORR_DECAY") { g_use_corr_decay_exit = !g_use_corr_decay_exit; ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, g_use_corr_decay_exit ? c_btn_on_bg : c_btn_off_bg); } else if(sparam == g_object_prefix + "CTRL_TRADE_SETTINGS_BTN_MTF_FILTER") { g_use_mtf_filter = !g_use_mtf_filter; ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, g_use_mtf_filter ? c_btn_on_bg : c_btn_off_bg); } else if(sparam == g_object_prefix + "CTRL_TRADE_SETTINGS_BTN_SLTP") { g_use_sl_tp = !g_use_sl_tp; ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, g_use_sl_tp ? c_btn_on_bg : c_btn_off_bg); string cap = g_use_sl_tp ? "SL/TP (ON • RR " + StringFormat("%.1f", g_rr) + ")" : "SL/TP (OFF)"; ObjectSetString(0, sparam, OBJPROP_TEXT, cap); } else if(StringFind(sparam, g_object_prefix + "CTRL_RELOAD") >= 0 || sparam == g_object_prefix + "CTRL_RELOAD") { OnTimer(); } else if(StringFind(sparam, g_object_prefix + "CTRL_TF_") >= 0) { string tfs_lbl[] = {"M15","M30","H1","H4","D1"}; for(int kk=0; kk= 0) g_current_timeframe = PERIOD_M15; else if(StringFind(sparam, "M30") >= 0) g_current_timeframe = PERIOD_M30; else if(StringFind(sparam, "H1") >= 0) g_current_timeframe = PERIOD_H1; else if(StringFind(sparam, "H4") >= 0) g_current_timeframe = PERIOD_H4; else if(StringFind(sparam, "D1") >= 0) g_current_timeframe = PERIOD_D1; ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, c_btn_on_bg); RecreateIndicatorHandles(); OnTimer(); } SafeRedraw(); return; } if(StringFind(sparam, g_object_prefix + "ANALYSIS_OPP_TRADE_BTN_") >= 0) { int idx = -1; int last_us = -1; int L = StringLen(sparam); for(int i=L-1; i>=0; --i){ if(StringGetCharacter(sparam,i)=='_'){ last_us=i; break; } } if(last_us >= 0) { string idx_str = StringSubstr(sparam, last_us+1); idx = (int)StringToInteger(idx_str); } if(idx >= 0 && idx < ArraySize(g_opportunities) && g_opportunities[idx].trade_action != SIGNAL_NONE) { ExecuteMatrixTrade(g_opportunities[idx].symbol, g_opportunities[idx].trade_action); } SafeRedraw(); return; } if(StringFind(sparam, "_CLOSE_BTN_") >= 0) { int row_index = -1; int last_us = -1; int L = StringLen(sparam); for(int i=L-1; i>=0; --i){ if(StringGetCharacter(sparam,i)=='_'){ last_us=i; break; } } if(last_us >= 0) { string idx_str = StringSubstr(sparam, last_us+1); row_index = (int)StringToInteger(idx_str); } if(row_index != -1) ClosePairTrades(row_index); SafeRedraw(); return; } { int row_index = -1; int last_us = -1; int L = StringLen(sparam); for(int i=L-1; i>=0; --i){ if(StringGetCharacter(sparam,i)=='_'){ last_us=i; break; } } if(last_us >= 0) { string idx_str = StringSubstr(sparam, last_us+1); row_index = (int)StringToInteger(idx_str); } if(row_index != -1) { if(StringFind(sparam, "_AUTO_BTN_") >= 0) { g_auto_status[row_index] = !g_auto_status[row_index]; UpdateAutoButton(row_index); } else if(StringFind(sparam, "_BB_BTN_") >= 0) { ExecuteTrade(row_index, SIGNAL_BB, InpForceManualButtons); } else if(StringFind(sparam, "_SS_BTN_") >= 0) { ExecuteTrade(row_index, SIGNAL_SS, InpForceManualButtons); } else if(StringFind(sparam, "_BS_BTN_") >= 0) { ExecuteTrade(row_index, SIGNAL_BS, InpForceManualButtons); } else if(StringFind(sparam, "_SB_BTN_") >= 0) { ExecuteTrade(row_index, SIGNAL_SB, InpForceManualButtons); } } SafeRedraw(); return; } } if(id == CHARTEVENT_OBJECT_ENDEDIT) { string value = ObjectGetString(0, sparam, OBJPROP_TEXT); if(sparam == g_object_prefix + "CTRL_BASKET_EDIT_TP_PERC") g_basket_tp_percent = StringToDouble(value); if(sparam == g_object_prefix + "CTRL_BASKET_EDIT_SL_PERC") g_basket_sl_percent = StringToDouble(value); if(sparam == g_object_prefix + "CTRL_BASKET_EDIT_TP_MONEY") g_basket_tp_money = StringToDouble(value); if(sparam == g_object_prefix + "CTRL_BASKET_EDIT_SL_MONEY") g_basket_sl_money = StringToDouble(value); if(sparam == g_object_prefix + "CTRL_EDIT_FIXED_LOT") g_fixed_lot_size = StringToDouble(value); if(sparam == g_object_prefix + "CTRL_EDIT_RISK_PERC") g_risk_percent = StringToDouble(value); SafeRedraw(); } } void OnTimer() { if(!TerminalInfoInteger(TERMINAL_CONNECTED) || !TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return; if(!UpdatePriceCache()) return; int task = g_timer_counter % 3; switch(task) { case 0: { for(int i = 0; i < TOTAL_PAIRS; i++) { g_corr_data[i].current_corr = ComputePairCorrelation(i); bool newBar = false; datetime bt[1]; if(CopyTime(g_corr_data[i].symbol1, g_current_timeframe, 1, 1, bt) > 0) { if(g_row_last_hist_bar[i] != bt[0]) { newBar = true; g_row_last_hist_bar[i] = bt[0]; int hs = ArraySize(g_corr_data[i].history); ArrayResize(g_corr_data[i].history, hs + 1); g_corr_data[i].history[hs] = g_corr_data[i].current_corr; if(ArraySize(g_corr_data[i].history) > CORR_HISTORY_SIZE) ArrayRemove(g_corr_data[i].history, 0, 1); } } g_corr_data[i].avg_corr = ArrayMean(g_corr_data[i].history); g_corr_data[i].delta_corr = g_corr_data[i].current_corr - g_corr_data[i].avg_corr; double p1[], p2[]; if(GetPricesFromCache(g_corr_data[i].symbol1, p1) && GetPricesFromCache(g_corr_data[i].symbol2, p2) && ArraySize(p1) > 20 && ArraySize(p2) > 20) { double norm_p1 = (p1[0] / p1[20] - 1) * 10000.0; double norm_p2 = (p2[0] / p2[20] - 1) * 10000.0; double spread = norm_p1 - norm_p2; string sprd_text = StringFormat("%+.2f", spread); if(g_last_sprd_text[i] != sprd_text) { ObjectSetString(0, g_object_prefix+"SPRD_DIV_CELL_"+(string)i+"_TXT", OBJPROP_TEXT, sprd_text); g_last_sprd_text[i] = sprd_text; } } UpdateRowConfAndHighlight(i); } UpdateDashboardData(); UpdateSignalPanels(); break; } case 1: { UpdateCorrelationMatrix(); FindMatrixOpportunities(); UpdateOpportunityPanel(); break; } case 2: { break; } } UpdateInfoPanel(); UpdateTimePanel(); UpdateAndManageBasket(); ManageOpenTrades(); ManageAutoCloseRowsAndOpp(); UpdatePairTradeStatus(); g_timer_counter++; SafeRedraw(); } bool UpdatePriceCache() { for(int i=0; i=2) ? tms[1] : tms[0]; if(g_last_cache_time[i] == 0) { double px[]; int need = InpLookback; int n = CopyClose(sym, g_current_timeframe, 0, need, px); if(n <= 0) continue; ArraySetAsSeries(px, true); ArrayResize(g_price_cache[i].prices, n); ArrayCopy(g_price_cache[i].prices, px, 0, 0, n); g_last_cache_time[i] = lastBar; continue; } if(g_last_cache_time[i] == lastBar) { double c0[]; if(CopyClose(sym, g_current_timeframe, 0, 1, c0) == 1) g_price_cache[i].prices[0] = c0[0]; else { MqlTick tk; if(SymbolInfoTick(sym, tk)) g_price_cache[i].prices[0] = 0.5*(tk.bid + tk.ask); } continue; } const int curN = ArraySize(g_price_cache[i].prices); if(curN == 0) { double c0[]; if(CopyClose(sym, g_current_timeframe, 0, 1, c0) == 1) { ArrayResize(g_price_cache[i].prices, 1); g_price_cache[i].prices[0] = c0[0]; } } else { ArrayResize(g_price_cache[i].prices, MathMin(curN+1, InpLookback)); for(int k=ArraySize(g_price_cache[i].prices)-1; k>0; --k) g_price_cache[i].prices[k] = g_price_cache[i].prices[k-1]; double c0[]; if(CopyClose(sym, g_current_timeframe, 0, 1, c0) == 1) g_price_cache[i].prices[0] = c0[0]; } g_last_cache_time[i] = lastBar; } return true; } void UpdateSignalPanels() { int top_pos_idx = -1; int top_neg_idx = -1; int top_delta_idx = -1; double max_pos_corr = 0.75; double min_neg_corr = -0.75; double max_abs_delta = 0.10; for(int i = 0; i < TOTAL_PAIRS; i++) { if(g_corr_data[i].current_corr > max_pos_corr) { max_pos_corr = g_corr_data[i].current_corr; top_pos_idx = i; } if(g_corr_data[i].current_corr < min_neg_corr) { min_neg_corr = g_corr_data[i].current_corr; top_neg_idx = i; } if(MathAbs(g_corr_data[i].delta_corr) > max_abs_delta) { max_abs_delta = MathAbs(g_corr_data[i].delta_corr); top_delta_idx = i; } } string P_ANLS = g_object_prefix + "ANALYSIS_"; string P_POS = P_ANLS + "SIG_POS_L1"; if(top_pos_idx != -1) { string pair_text = g_corr_data[top_pos_idx].symbol1 + "/" + g_corr_data[top_pos_idx].symbol2; string new_text = "1. " + pair_text + ": " + StringFormat("%+.2f", g_corr_data[top_pos_idx].current_corr); ObjectSetString(0, P_POS, OBJPROP_TEXT, new_text); } else { ObjectSetString(0, P_POS, OBJPROP_TEXT, "1. (None > +0.75)"); } string P_NEG = P_ANLS + "SIG_NEG_L1"; if(top_neg_idx != -1) { string pair_text = g_corr_data[top_neg_idx].symbol1 + "/" + g_corr_data[top_neg_idx].symbol2; string new_text = "1. " + pair_text + ": " + StringFormat("%+.2f", g_corr_data[top_neg_idx].current_corr); ObjectSetString(0, P_NEG, OBJPROP_TEXT, new_text); } else { ObjectSetString(0, P_NEG, OBJPROP_TEXT, "1. (None < -0.75)"); } string P_DEL = P_ANLS + "SIG_DEL_L1"; if(top_delta_idx != -1) { string pair_text = g_corr_data[top_delta_idx].symbol1 + "/" + g_corr_data[top_delta_idx].symbol2; string new_text = "1. " + pair_text + ": " + StringFormat("%+.2f", g_corr_data[top_delta_idx].delta_corr); ObjectSetString(0, P_DEL, OBJPROP_TEXT, new_text); } else { ObjectSetString(0, P_DEL, OBJPROP_TEXT, "1. (None > |0.10|)"); } } void UpdateAndManageBasket() { double total_profit = 0; int pairs_counter = 0; bool seen_row[TOTAL_PAIRS]; ArrayInitialize(seen_row, false); CPositionInfo pos; for(int i=PositionsTotal()-1; i>=0; --i) { if(!pos.SelectByIndex(i)) continue; if(pos.Magic() != InpMagicNumber) continue; total_profit += pos.Profit() + pos.Swap() + pos.Commission(); string cmt = pos.Comment(); string pref = g_object_prefix + "ROW:"; if(StringFind(cmt, pref) == 0){ int idx = (int)StringToInteger(StringSubstr(cmt, (int)StringLen(pref))); if(idx>=0 && idx0) ? (total_profit/equity)*100.0 : 0.0; ObjectSetString(0, P + "VAL_PL_PERC", OBJPROP_TEXT, StringFormat("%+.2f%%", pl_percent)); if(!g_basket_exit_enabled || PositionsTotal()==0) { if(g_bt_armed || g_bt_peak_percent!=0.0) Orc_BasketTrailing_Reset(); return; } if(g_basket_exit_mode == MODE_PERCENT) { if(pl_percent >= g_basket_tp_percent) { Print("[Basket] TP% hit -> close all"); CloseAllBasketTrades(); Orc_BasketTrailing_Reset(); return; } if(pl_percent <= -g_basket_sl_percent) { Print("[Basket] SL% hit -> close all"); CloseAllBasketTrades(); Orc_BasketTrailing_Reset(); return; } if(!InpEnableBasketTrailing) { if(g_bt_armed || g_bt_peak_percent!=0.0) Orc_BasketTrailing_Reset(); return; } if(InpBasketTrailArmPercent <= 0 || InpBasketTrailStepPercent <= 0) { if(g_bt_armed || g_bt_peak_percent!=0.0) Orc_BasketTrailing_Reset(); return; } if(!g_bt_armed && pl_percent >= InpBasketTrailArmPercent) { g_bt_armed = true; g_bt_peak_percent = pl_percent; g_bt_stop_percent = g_bt_peak_percent - InpBasketTrailStepPercent; PrintFormat("[BasketTrail] ARMED at %.2f%% stop=%.2f%%", g_bt_peak_percent, g_bt_stop_percent); } if(g_bt_armed) { if(pl_percent > g_bt_peak_percent) { g_bt_peak_percent = pl_percent; g_bt_stop_percent = g_bt_peak_percent - InpBasketTrailStepPercent; } if(pl_percent <= g_bt_stop_percent) { PrintFormat("[BasketTrail] STOP HIT at %.2f%% (stop %.2f%%) -> close all", pl_percent, g_bt_stop_percent); CloseAllBasketTrades(); Orc_BasketTrailing_Reset(); return; } } } else { if(total_profit >= g_basket_tp_money) { Print("[Basket] TP$ hit -> close all"); CloseAllBasketTrades(); Orc_BasketTrailing_Reset(); return; } if(total_profit <= -g_basket_sl_money) { Print("[Basket] SL$ hit -> close all"); CloseAllBasketTrades(); Orc_BasketTrailing_Reset(); return; } if(g_bt_armed || g_bt_peak_percent!=0.0) Orc_BasketTrailing_Reset(); } } void CloseAllBasketTrades() { CPositionInfo pi; for(int i=PositionsTotal()-1; i>=0; --i) { if(pi.SelectByIndex(i) && pi.Magic()==InpMagicNumber) trade.PositionClose(pi.Ticket()); } } void CloseProfitableBasketTrades() { Print("Attempting to close all profitable trades..."); CPositionInfo pi; for(int i=PositionsTotal()-1; i>=0; --i) { if(pi.SelectByIndex(i) && pi.Magic()==InpMagicNumber && (pi.Profit()+pi.Swap()) >= 0) trade.PositionClose(pi.Ticket()); } } void ManageOpenTrades() { CPositionInfo pos_info; string comment_prefix = g_object_prefix + "ROW:"; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(!pos_info.SelectByIndex(i)) continue; if(pos_info.Magic() != InpMagicNumber) continue; string comment = pos_info.Comment(); if(StringFind(comment, comment_prefix) != 0) continue; string symbol = pos_info.Symbol(); ulong ticket = pos_info.Ticket(); int row_index = (int)StringToInteger(StringSubstr(comment, StringLen(comment_prefix))); if(row_index < 0 || row_index >= TOTAL_PAIRS) continue; if(g_use_time_exit) { long open_time = pos_info.Time(); int open_bar_index = iBarShift(symbol, g_current_timeframe, (datetime)open_time); if(open_bar_index >= g_max_bars_hold) { Print("Time-based Exit triggered for ", symbol, " (row ", row_index, ")"); if(trade.PositionClose(ticket)) g_last_row_close_bar[row_index] = TimeCurrent(); continue; } } if(g_use_corr_decay_exit) { double current_corr = g_corr_data[row_index].current_corr; if(MathAbs(current_corr) < g_corr_decay_threshold) { Print("Correlation Decay Exit triggered for ", symbol, " (row ", row_index, ")"); if(trade.PositionClose(ticket)) g_last_row_close_bar[row_index] = TimeCurrent(); } } } } void ManageAutoCloseRowsAndOpp() { if(InpRowAutoTP_Enable) { double equity = AccountInfoDouble(ACCOUNT_EQUITY); double row_pl[TOTAL_PAIRS]; int row_has[TOTAL_PAIRS]; ArrayInitialize(row_pl, 0.0); ArrayInitialize(row_has, 0); CPositionInfo pi; string prefRow = g_object_prefix + "ROW:"; for(int i=PositionsTotal()-1;i>=0;--i) { if(!pi.SelectByIndex(i) || pi.Magic()!=InpMagicNumber) continue; string c = pi.Comment(); if(StringFind(c, prefRow)==0) { int row = (int)StringToInteger(StringSubstr(c, (int)StringLen(prefRow))); if(row>=0 && row= InpRowTP_Money); else { double perc = (equity>0? (row_pl[r]/equity)*100.0 : 0.0); hit = (perc >= InpRowTP_Percent); } if(hit) { PrintFormat("[OracleDash] Row %d TP hit -> closing (P/L=%.2f)", r, row_pl[r]); ClosePairTrades(r); g_last_row_close_bar[r] = TimeCurrent(); } } } if(InpOppAutoTP_Enable) { double equity = AccountInfoDouble(ACCOUNT_EQUITY); string keys[]; double vals[]; CPositionInfo pi; string prefOpp = g_object_prefix + "MATRIX:"; for(int i=PositionsTotal()-1;i>=0;--i) { if(!pi.SelectByIndex(i) || pi.Magic()!=InpMagicNumber) continue; string c = pi.Comment(); if(StringFind(c, prefOpp)==0) { string sym = StringSubstr(c, (int)StringLen(prefOpp)); if(sym=="") continue; int idx=-1; for(int k=0;k= InpOppTP_Money); else { double perc = (equity>0? (vals[k]/equity)*100.0 : 0.0); hit = (perc >= InpOppTP_Percent); } if(hit) { PrintFormat("[OracleDash] OPP TP hit on %s -> closing (P/L=%.2f)", keys[k], vals[k]); CloseMatrixSymbolTrades(keys[k]); } } } } double CalculateLotSize(string symbol) { if(g_lot_mode == LOT_MODE_FIXED) { return g_fixed_lot_size; } if(g_lot_mode == LOT_MODE_AUTO) { double atr_val_array[]; int atr_handle = iATR(symbol, g_current_timeframe, InpATR_Period); if(atr_handle == INVALID_HANDLE) return g_fixed_lot_size; if(CopyBuffer(atr_handle, 0, 0, 2, atr_val_array) < 2) return g_fixed_lot_size; double atr_value = atr_val_array[1]; double stop_loss_pips = atr_value * InpATR_Multiplier; if(stop_loss_pips == 0) return g_fixed_lot_size; double account_equity = AccountInfoDouble(ACCOUNT_EQUITY); double risk_amount = account_equity * (g_risk_percent / 100.0); double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE); double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE); if(tick_value == 0 || tick_size == 0) return g_fixed_lot_size; double stop_loss_money_per_lot = (stop_loss_pips / tick_size) * tick_value; if(stop_loss_money_per_lot == 0) return g_fixed_lot_size; double lot_size = risk_amount / stop_loss_money_per_lot; double volume_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); lot_size = MathFloor(lot_size / volume_step) * volume_step; double min_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double max_lot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); return(MathMax(min_lot, MathMin(max_lot, lot_size))); } return g_fixed_lot_size; } double NormalizePriceToTick(const string sym, const double price){ const double tick = SymbolInfoDouble(sym, SYMBOL_TRADE_TICK_SIZE); const int digs = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS); if(tick<=0) return NormalizeDouble(price, digs); double steps = MathRound(price / tick); return NormalizeDouble(steps * tick, digs); } double NormalizeVolumeToStep(const string sym, double vol){ const double step = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP); const double vmin = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN); const double vmax = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX); if(step<=0) return vol; vol = MathFloor(vol/step)*step; if(vol < vmin) vol = vmin; if(vol > vmax) vol = vmax; return vol; } double SnapToTick(const string sym, const double price, const bool down) { const double tick = SymbolInfoDouble(sym, SYMBOL_TRADE_TICK_SIZE); const int digs = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS); if(tick <= 0) return NormalizeDouble(price, digs); double steps = price / tick; return NormalizeDouble((down ? MathFloor(steps) : MathCeil(steps)) * tick, digs); } void EnsureStopsRespectLevels(const string sym, const bool isBuy, double &sl, double &tp) { const double pt = SymbolInfoDouble(sym, SYMBOL_POINT); const int stops = (int)SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL); const int freeze = (int)SymbolInfoInteger(sym, SYMBOL_TRADE_FREEZE_LEVEL); const double minDist = pt * MathMax(stops, freeze); const double ask = SymbolInfoDouble(sym, SYMBOL_ASK); const double bid = SymbolInfoDouble(sym, SYMBOL_BID); if(isBuy){ if(sl>0 && (ask - sl) < minDist) sl = SnapToTick(sym, ask - minDist, true); if(tp>0 && (tp - ask) < minDist) tp = SnapToTick(sym, ask + minDist, false); }else{ if(sl>0 && (sl - bid) < minDist) sl = SnapToTick(sym, bid + minDist, false); if(tp>0 && (bid - tp) < minDist) tp = SnapToTick(sym, bid - minDist, true); } } bool TryOrderSend(MqlTradeRequest &req, MqlTradeResult &res) { static ENUM_ORDER_TYPE_FILLING tries[3] = { ORDER_FILLING_FOK, ORDER_FILLING_IOC, ORDER_FILLING_RETURN }; for (int i = 0; i < 3; ++i) { req.type_filling = tries[i]; if (OrderSend(req, res)) return true; if (res.retcode == TRADE_RETCODE_INVALID_FILL) continue; } return false; } bool SendDealRawEx(const string sym, const ENUM_ORDER_TYPE typ, double vol, double sl, double tp, const string &cmt, uint &retcodeOut, string &commentOut) { retcodeOut=0; commentOut=""; if(sym=="" || (typ!=ORDER_TYPE_BUY && typ!=ORDER_TYPE_SELL)) return false; if(!EnsureSymbolSelected(sym)) return false; vol = NormalizeVolumeToStep(sym, vol); if(vol<=0) return false; double px = (typ==ORDER_TYPE_BUY)? SymbolInfoDouble(sym, SYMBOL_ASK): SymbolInfoDouble(sym, SYMBOL_BID); px = NormalizePriceToTick(sym, px); if(sl>0 || tp>0) EnsureStopsRespectLevels(sym, (typ==ORDER_TYPE_BUY), sl, tp); MqlTradeRequest req; MqlTradeResult res; ZeroMemory(req); req.action = TRADE_ACTION_DEAL; req.type = typ; req.symbol = sym; req.volume = vol; req.price = px; req.sl = (sl>0? NormalizePriceToTick(sym, sl):0.0); req.tp = (tp>0? NormalizePriceToTick(sym, tp):0.0); req.magic = InpMagicNumber; req.type_time = ORDER_TIME_GTC; req.deviation = 300; req.type_filling = (ENUM_ORDER_TYPE_FILLING)SymbolInfoInteger(sym, SYMBOL_FILLING_MODE); req.comment = cmt; bool ok = TryOrderSend(req, res); retcodeOut = res.retcode; commentOut = res.comment; return ok; } bool SendDeal(const string sym, const ENUM_ORDER_TYPE typ, double vol, const string &trade_comment) { if(sym=="" || (typ!=ORDER_TYPE_BUY && typ!=ORDER_TYPE_SELL)) return false; if(!g_guard_ok){ Print("[GUARD] Trading blocked: ", g_guard_reason); return false; } if(!EnsureSymbolSelected(sym)){ Print("Symbol not available: ", sym); return false; } if(!PassesPreTradeFilters(sym)){ Print("PreFilter blocked trade on ", sym); return false; } vol = NormalizeVolumeToStep(sym, vol); if(vol <= 0){ Print("Volume rejected after normalize: ", vol); return false; } double sl=0.0, tp=0.0; if(g_use_sl_tp){ BuildSLTP(sym, (typ==ORDER_TYPE_BUY), InpATR_Multiplier, sl, tp, g_rr); EnsureStopsRespectLevels(sym, (typ==ORDER_TYPE_BUY), sl, tp); if(sl>0) sl = NormalizePriceToTick(sym, sl); if(tp>0) tp = NormalizePriceToTick(sym, tp); } const double ask = SymbolInfoDouble(sym, SYMBOL_ASK); const double bid = SymbolInfoDouble(sym, SYMBOL_BID); double px = (typ==ORDER_TYPE_BUY) ? ask : bid; px = NormalizePriceToTick(sym, px); MqlTradeRequest req; MqlTradeResult res; ZeroMemory(req); req.action = TRADE_ACTION_DEAL; req.type = typ; req.symbol = sym; req.volume = vol; req.price = px; req.sl = sl; req.tp = tp; req.magic = InpMagicNumber; req.deviation = 300; req.type_time = ORDER_TIME_GTC; req.comment = trade_comment; if(!TryOrderSend(req, res)){ Print("OrderSend failed on ", sym, " retcode=", res.retcode, " comment=", res.comment); return false; } return true; } void ExecuteTrade(int index, ENUM_SIGNAL_TYPE signal_type, bool allow_force) { if(!g_guard_ok){ Print("[GUARD] Trading blocked: ", g_guard_reason); return; } if(signal_type == SIGNAL_NONE) return; if(RowOpenCount(index) >= InpMaxPositionsPerRow){ NotifyUser(StringFormat("Skip row %d: reached max %d positions.", index, InpMaxPositionsPerRow)); return; } if(InRowCooldown(index)) { NotifyUser(StringFormat("Skip row %d: cooldown.", index)); return; } string s1 = g_corr_data[index].symbol1; string s2 = g_corr_data[index].symbol2; ENUM_ORDER_TYPE t1, t2; if(signal_type==SIGNAL_BB){ t1=ORDER_TYPE_BUY; t2=ORDER_TYPE_BUY; } else if(signal_type==SIGNAL_SS){ t1=ORDER_TYPE_SELL; t2=ORDER_TYPE_SELL; } else if(signal_type==SIGNAL_BS){ t1=ORDER_TYPE_BUY; t2=ORDER_TYPE_SELL; } else if(signal_type==SIGNAL_SB){ t1=ORDER_TYPE_SELL; t2=ORDER_TYPE_BUY; } else return; double lot1 = CalculateLotSize(s1); double lot2 = CalculateLotSize(s2); AdjustLotsRiskParity(s1, s2, lot1, lot2); double sl1=0,tp1=0, sl2=0,tp2=0; if(g_use_sl_tp){ BuildSLTP(s1, (t1==ORDER_TYPE_BUY), InpATR_Multiplier, sl1, tp1, g_rr); BuildSLTP(s2, (t2==ORDER_TYPE_BUY), InpATR_Multiplier, sl2, tp2, g_rr); EnsureStopsRespectLevels(s1, (t1==ORDER_TYPE_BUY), sl1, tp1); EnsureStopsRespectLevels(s2, (t2==ORDER_TYPE_BUY), sl2, tp2); } bool pf1 = PassesPreTradeFilters(s1); bool pf2 = PassesPreTradeFilters(s2); string reasonA="", reasonB=""; uint rcA=0, rcB=0; bool pre1 = PreflightDeal(s1, t1, lot1, sl1, tp1, reasonA, rcA); bool pre2 = PreflightDeal(s2, t2, lot2, sl2, tp2, reasonB, rcB); double pv1 = PipValuePerLot(s1), pv2 = PipValuePerLot(s2); double rA = lot1 * pv1, rB = lot2 * pv2; double diffpct = (MathMax(rA,rB)>0 ? 100.0 * MathAbs(rA-rB) / MathMax(rA,rB) : 0.0); bool any_fail = (!pf1 || !pf2 || !pre1 || !pre2); bool high_mismatch = (diffpct > InpRiskMismatchWarnPct); if(any_fail || high_mismatch) { if(!allow_force){ string msg = StringFormat("Row %d blocked. pf:%s/%s pre:%s/%s Δrisk≈%.1f%%", index, (pf1?"OK":"FAIL"), (pf2?"OK":"FAIL"), (pre1?"OK":"FAIL"), (pre2?"OK":"FAIL"), diffpct); NotifyUser(msg); return; } if(!g_silent_force) { string warn = "Force pair entry?\n\n"; if(!pf1) warn += s1 + " prefilter: FAIL\n"; if(!pre1) warn += s1 + " precheck: " + reasonA + "\n"; if(!pf2) warn += s2 + " prefilter: FAIL\n"; if(!pre2) warn += s2 + " precheck: " + reasonB + "\n"; if(high_mismatch) warn += StringFormat("Risk mismatch ~%.1f%%\n", diffpct); warn += "\nกด OK เพื่อบังคับเข้าออเดอร์ทั้งสองขา หรือ Cancel เพื่อยกเลิก"; if(!ConfirmForce("OracleDash — Force Entry", warn)) return; } } string tag = g_object_prefix + "ROW:" + (string)index; if(g_exec_ctx == EXEC_FORCE_AUTO) tag += " | MODE:FORCE_AUTO"; else if(g_exec_ctx == EXEC_AUTO) tag += " | MODE:AUTO"; uint s_rcA=0, s_rcB=0; string s_cmA="", s_cmB=""; bool okA = SendDealRawEx(s1, t1, lot1, sl1, tp1, tag, s_rcA, s_cmA); if(!okA){ NotifyUser(StringFormat("Row %d %s %s SEND FAIL (%u): %s", index, s1, LegLabel(t1), s_rcA, s_cmA)); return; } NotifyUser(StringFormat("Row %d %s %s OK (%u): %s vol=%.2f", index, s1, LegLabel(t1), s_rcA, s_cmA, lot1)); bool okB = SendDealRawEx(s2, t2, lot2, sl2, tp2, tag, s_rcB, s_cmB); if(!okB){ NotifyUser(StringFormat("Row %d %s %s SEND FAIL (%u): %s", index, s2, LegLabel(t2), s_rcB, s_cmB)); CPositionInfo pi; bool closed=false; string pref = g_object_prefix + "ROW:" + (string)index; for(int i=PositionsTotal()-1;i>=0;--i) if(pi.SelectByIndex(i) && pi.Magic()==InpMagicNumber && pi.Symbol()==s1 && StringFind(pi.Comment(), pref)==0) { closed = trade.PositionClose(pi.Ticket()); break; } NotifyUser(StringFormat("Row %d compensate legA %s %s -> %s", index, s1, LegLabel(t1), (closed?"CLOSED":"FAILED"))); return; } NotifyUser(StringFormat("Row %d %s %s OK (%u): %s vol=%.2f", index, s2, LegLabel(t2), s_rcB, s_cmB, lot2)); } void ExecuteMatrixTrade(string symbol, ENUM_SIGNAL_TYPE trade_action) { if(!g_guard_ok){ Print("[GUARD] Trading blocked: ", g_guard_reason); return; } if (trade_action == SIGNAL_NONE || symbol == "") return; if(StringSubstr(symbol,0,3)=="XAU") { ENUM_SIGNAL_TYPE s; if(!SingleSymbolDirection(symbol, s) || s != trade_action) { Print("XAU filter disagrees. Skip ", symbol); return; } } double lot = CalculateLotSize(symbol); string cmt = g_object_prefix + "MATRIX:" + symbol; if(trade_action == SIGNAL_BS) SendDeal(symbol, ORDER_TYPE_BUY, lot, cmt); else if(trade_action == SIGNAL_SB) SendDeal(symbol, ORDER_TYPE_SELL, lot, cmt); } double CalculateSignalConfidence(double correlation, double delta_corr) { double confidence = 0; double corr_abs = MathAbs(correlation); if(corr_abs >= 0.7) { confidence += ((corr_abs - 0.7) / 0.3) * 50.0; } double delta_abs = MathAbs(delta_corr); if(delta_abs > 0.05) { double delta_capped = MathMin(delta_abs, 0.25); confidence += ((delta_capped - 0.05) / 0.20) * 50.0; } return MathMin(confidence, 100.0); } SignalInfo CheckTradingSignals(int index) { SignalInfo out; if(index < 0 || index >= TOTAL_PAIRS) return out; string s1 = g_corr_data[index].symbol1; string s2 = g_corr_data[index].symbol2; double corr = g_corr_data[index].current_corr; double avg = g_corr_data[index].avg_corr; double dCorr = g_corr_data[index].delta_corr; double z_hist = ZscoreArray(g_corr_data[index].history); int ha1 = g_ha_handles_main[index][0]; int ha2 = g_ha_handles_main[index][1]; double o1[], c1[], o2[], c2[]; bool haveHA = !(ha1==INVALID_HANDLE || ha2==INVALID_HANDLE || CopyBuffer(ha1,0,0,1,o1)<1 || CopyBuffer(ha1,3,0,1,c1)<1 || CopyBuffer(ha2,0,0,1,o2)<1 || CopyBuffer(ha2,3,0,1,c2)<1); bool s1_bull=false, s1_bear=false, s2_bull=false, s2_bear=false; if(haveHA){ s1_bull = (c1[0] > o1[0]); s1_bear = (c1[0] < o1[0]); s2_bull = (c2[0] > o2[0]); s2_bear = (c2[0] < o2[0]); } bool trend_ok = true; double a1[], a2[]; if(g_adx_handles_main[index][0]!=INVALID_HANDLE && g_adx_handles_main[index][1]!=INVALID_HANDLE && CopyBuffer(g_adx_handles_main[index][0],2,0,2,a1)==2 && CopyBuffer(g_adx_handles_main[index][1],2,0,2,a2)==2) { double adx_val = (a1[1]+a2[1])*0.5; trend_ok = (adx_val >= InpADX_TrendLevel); } double baseConf = CalculateSignalConfidence(corr, dCorr); if(!haveHA) baseConf *= 0.85; bool ok_bb=true, ok_ss=true, ok_bs=true, ok_sb=true; if(g_use_mtf_filter) { int ha1c = g_ha_handles_conf[index][0]; int ha2c = g_ha_handles_conf[index][1]; double oc1[], cc1[], oc2[], cc2[]; bool haveMTF = !(ha1c==INVALID_HANDLE || ha2c==INVALID_HANDLE || CopyBuffer(ha1c,0,0,1,oc1)<1 || CopyBuffer(ha1c,3,0,1,cc1)<1 || CopyBuffer(ha2c,0,0,1,oc2)<1 || CopyBuffer(ha2c,3,0,1,cc2)<1); if(haveMTF){ bool s1c_bull = (cc1[0] > oc1[0]); bool s1c_bear = (cc1[0] < oc1[0]); bool s2c_bull = (cc2[0] > oc2[0]); bool s2c_bear = (cc2[0] < oc2[0]); ok_bb = (s1c_bull && s2c_bull); ok_ss = (s1c_bear && s2c_bear); ok_bs = (s1c_bull && s2c_bear); ok_sb = (s1c_bear && s2c_bull); }else{ baseConf *= 0.90; } } const double POS_THR = InpCorrPosThreshold; const double NEG_THR = InpCorrNegThreshold; const double Z_ENTER = InpZSpikeEnter; const double Z_EXIT = InpZSpikeExit; double scBB = baseConf; { double align = (haveHA && s1_bull && s2_bull ? 1.0 : 0.6); double mtf = (ok_bb ? 1.0 : 0.7); double sign = (corr >= POS_THR ? 1.0 : (avg >= POS_THR ? 0.8 : 0.5)); bool bb_trend = (trend_ok && haveHA && s1_bull && s2_bull && (!g_use_mtf_filter || ok_bb)); double zok = (MathAbs(z_hist) <= Z_EXIT || bb_trend ? 1.0 : 0.85); double adx = (trend_ok ? 1.0 : 0.75); scBB *= Clamp01(align*mtf*sign*zok*adx); } double scSS = baseConf; { double align = (haveHA && s1_bear && s2_bear ? 1.0 : 0.6); double mtf = (ok_ss ? 1.0 : 0.7); double sign = (corr >= POS_THR ? 1.0 : (avg >= POS_THR ? 0.8 : 0.5)); bool ss_trend = (trend_ok && haveHA && s1_bear && s2_bear && (!g_use_mtf_filter || ok_ss)); double zok = (MathAbs(z_hist) <= Z_EXIT || ss_trend ? 1.0 : 0.85); double adx = (trend_ok ? 1.0 : 0.75); scSS *= Clamp01(align*mtf*sign*zok*adx); } double scBS = baseConf; { double align = (haveHA && s1_bull && s2_bear ? 1.0 : 0.6); double mtf = (ok_bs ? 1.0 : 0.7); double sign = (corr <= NEG_THR ? 1.0 : (avg <= NEG_THR ? 0.8 : 0.5)); double zok = (MathAbs(z_hist) >= Z_ENTER ? 1.0 : 0.9); double adx = (trend_ok ? 0.95 : 0.90); scBS *= Clamp01(align*mtf*sign*zok*adx); } double scSB = baseConf; { double align = (haveHA && s1_bear && s2_bull ? 1.0 : 0.6); double mtf = (ok_sb ? 1.0 : 0.7); double sign = (corr <= NEG_THR ? 1.0 : (avg <= NEG_THR ? 0.8 : 0.5)); double zok = (MathAbs(z_hist) >= Z_ENTER ? 1.0 : 0.9); double adx = (trend_ok ? 0.95 : 0.90); scSB *= Clamp01(align*mtf*sign*zok*adx); } double z_spread=0.0; ENUM_SIGNAL_TYPE spr_sig=SIGNAL_NONE; if(PairSignalBySpread(index, z_spread, spr_sig)) { double opp_mult = (corr >= POS_THR ? 1.10 : 1.25); if(spr_sig==SIGNAL_BS) scBS *= opp_mult; if(spr_sig==SIGNAL_SB) scSB *= opp_mult; if(MathAbs(z_spread) >= (Z_ENTER + 0.4)) { double add = (corr >= POS_THR ? 4.0 : 10.0); if(spr_sig==SIGNAL_BS) scBS += add; if(spr_sig==SIGNAL_SB) scSB += add; } } bool pos_corr_strong = (corr >= POS_THR); bool neg_corr_strong = (corr <= NEG_THR); if(pos_corr_strong) { bool bb_trend = (haveHA && s1_bull && s2_bull && (!g_use_mtf_filter || ok_bb) && trend_ok); bool ss_trend = (haveHA && s1_bear && s2_bear && (!g_use_mtf_filter || ok_ss) && trend_ok); if(bb_trend) scBB = MathMin(100.0, scBB*1.20 + 6.0); if(ss_trend) scSS = MathMin(100.0, scSS*1.20 + 6.0); scBS *= 0.75; scSB *= 0.75; } else if(neg_corr_strong) { scBB *= 0.75; scSS *= 0.75; } if(!PassesPreTradeFilters(s1) || !PassesPreTradeFilters(s2)) { scBB *= 0.85; scSS *= 0.85; scBS *= 0.85; scSB *= 0.85; } scBB = MathMin(100.0, MathMax(0.0, scBB)); scSS = MathMin(100.0, MathMax(0.0, scSS)); scBS = MathMin(100.0, MathMax(0.0, scBS)); scSB = MathMin(100.0, MathMax(0.0, scSB)); out.type = SIGNAL_BB; out.confidence = scBB; if(scSS > out.confidence){ out.type = SIGNAL_SS; out.confidence = scSS; } if(scBS > out.confidence){ out.type = SIGNAL_BS; out.confidence = scBS; } if(scSB > out.confidence){ out.type = SIGNAL_SB; out.confidence = scSB; } out.confidence = AdjustConfidenceByLogic(out.confidence); return out; } void ComputeMatrixStrength(double &strength[]) { int n = ArraySize(g_matrix_symbols); ArrayResize(strength, n); for(int i=0;i0) ? (s/cnt) : 0.0; } } bool LegsForCurrencies(const string a, const string b, string &legA, string &legB) { legA = GetSymbolForCurrencies(a, "USD"); legB = GetSymbolForCurrencies(b, "USD"); if(legA=="" || legB==""){ if(legA=="") legA = GetSymbolForCurrencies(a, "EUR"); if(legB=="") legB = GetSymbolForCurrencies(b, "EUR"); } return (legA!="" && legB!=""); } bool ZSpreadByLegs(const string ccy1, const string ccy2, double &z, ENUM_SIGNAL_TYPE &sig) { z=0.0; sig=SIGNAL_NONE; string l1,l2; if(!LegsForCurrencies(ccy1, ccy2, l1, l2)) return false; double p1[], p2[]; if(!GetPricesFromCache(l1,p1) || !GetPricesFromCache(l2,p2)) return false; int n = MathMin(ArraySize(p1), ArraySize(p2)); if(n < 60) return false; double L1[], L2[]; ArrayResize(L1,n); ArrayResize(L2,n); for(int i=0;i= 1.20) sig = SIGNAL_SB; else if(z0 <= -1.20) sig = SIGNAL_BS; return true; } void FindMatrixOpportunities() { for(int i=0;i= MR_minAbsCorr) { double z; ENUM_SIGNAL_TYPE sig = SIGNAL_NONE; if(ZSpreadByLegs(c1, c2, z, sig) && sig!=SIGNAL_NONE && MathAbs(z) >= MR_minZ) AddOpp(found, found_count, symbol, corr, "MR", sig); } if(wantSW && MathAbs(corr) >= SW_minAbsCorr) { double gap = strength[r] - strength[c]; if(MathAbs(gap) >= SW_minGap) { ENUM_SIGNAL_TYPE sig = (gap>0 ? SIGNAL_BS : SIGNAL_SB); AddOpp(found, found_count, symbol, corr, "SWING", sig); } } if(wantSC && MathAbs(corr) >= SC_minAbsCorr) { double gap = strength[r] - strength[c]; bool ok_gap = (MathAbs(gap) >= SC_minGap); bool ok_sp = (GetSpreadPts(symbol) <= SC_maxSpread); bool ok_sess = (!SC_needSess || IsMajorSessionOpen()); if(ok_gap && ok_sp && ok_sess) { ENUM_SIGNAL_TYPE sig = (gap>0 ? SIGNAL_BS : SIGNAL_SB); AddOpp(found, found_count, symbol, corr, "SCALP", sig); } } } } for(int i=0;i found[i].confidence) { OpportunityInfo t=found[i]; found[i]=found[j]; found[j]=t; } int k = MathMin(5, found_count); for(int i=0;i 100.0) conf = 100.0; if(conf < 0.0) conf = 0.0; arr[count].confidence = conf; count++; } bool MakeReturns(const double &px[], int n_in, double &ret[]) { int n = MathMin(n_in, ArraySize(px)); if(n < 2) return false; ArrayResize(ret, n-1); for(int i=1; i= TOTAL_PAIRS) return 0.0; double p1[], p2[]; if(!GetPricesFromCache(g_corr_data[pair_index].symbol1, p1)) return 0.0; if(!GetPricesFromCache(g_corr_data[pair_index].symbol2, p2)) return 0.0; int n = MathMin(ArraySize(p1), ArraySize(p2)); if(InpCalcOnClosedBar && n>1) n -= 1; return CorrOfReturns(p1, p2, MathMax(n, 0)); } void UpdateCorrelationMatrix() { int num_currencies = ArraySize(g_matrix_symbols); double pA[], pB[], u1[], u2[]; color green = C'0, 200, 100'; color red = C'200, 50, 50'; for(int r = 0; r < num_currencies; r++) { for(int c = 0; c < num_currencies; c++) { if(r == c) { g_matrix_corr[r][c] = 1.0; } else { string leg1 = GetSymbolForCurrencies(g_matrix_symbols[r], "USD"); string leg2 = GetSymbolForCurrencies(g_matrix_symbols[c], "USD"); if(leg1 == "") leg1 = GetSymbolForCurrencies(g_matrix_symbols[r], "EUR"); if(leg2 == "") leg2 = GetSymbolForCurrencies(g_matrix_symbols[c], "EUR"); if(leg1 != "" && leg2 != "" && GetPricesFromCache(leg1, pA) && GetPricesFromCache(leg2, pB)) { double corr = CorrOfReturns(pA, pB, InpLookback); if(StringSubstr(leg1, 0, 3) != g_matrix_symbols[r]) corr *= -1; if(StringSubstr(leg2, 0, 3) != g_matrix_symbols[c]) corr *= -1; g_matrix_corr[r][c] = corr; } else g_matrix_corr[r][c] = 0.0; } string name = g_object_prefix + "MATRIX_CELL_" + (string)r + "_" + (string)c; double cell_corr = g_matrix_corr[r][c]; color bg_color = (cell_corr > 0) ? InterpolateColor(c_bg_dark, green, MathMin(1.0, cell_corr)) : InterpolateColor(c_bg_dark, red, MathMin(1.0, -cell_corr)); if(r==c) bg_color = c_header_bg; ObjectSetString(0, name + "_TXT", OBJPROP_TEXT, StringFormat("%+.2f", cell_corr)); ObjectSetInteger(0, name + "_BG", OBJPROP_BGCOLOR, bg_color); } } } bool Guard_ParseDate(const string ymd, datetime &out) { out = 0; if(ymd == "" || StringLen(ymd) < 10) return false; int y = (int)StringToInteger(StringSubstr(ymd,0,4)); int m = (int)StringToInteger(StringSubstr(ymd,5,2)); int d = (int)StringToInteger(StringSubstr(ymd,8,2)); if(y<2000 || m<1 || m>12 || d<1 || d>31) return false; MqlDateTime dt; dt.year=y; dt.mon=m; dt.day=d; dt.hour=23; dt.min=59; dt.sec=59; out = StructToTime(dt); return true; } bool Guard_LoginAllowed(const string csv_ids, const long login) { if(csv_ids == "") return true; string tok; int p=0; while(p < (int)StringLen(csv_ids)) { int comma = StringFind(csv_ids, ",", p); if(comma < 0) comma = (int)StringLen(csv_ids); tok = Trim(StringSubstr(csv_ids, p, comma - p)); if(tok != "" && (long)StringToInteger(tok) == login) return true; p = comma + 1; } return false; } bool Guard_ServerAllowed(const string must_contain, const string server_name) { if(must_contain == "") return true; return (StringFind(server_name, must_contain) >= 0); } bool Guard_AcctTypeAllowed(ENUM_GUARD_ACCTYPE need, const long trade_mode) { if(need == GUARD_ANY) return true; bool is_demo = (trade_mode == ACCOUNT_TRADE_MODE_DEMO || trade_mode == ACCOUNT_TRADE_MODE_CONTEST); bool is_real = (trade_mode == ACCOUNT_TRADE_MODE_REAL); if(need == GUARD_DEMO) return is_demo; if(need == GUARD_REAL) return is_real; return true; } bool Guard_ExpiryOK(const string ymd) { if(ymd == "") return true; datetime exp; if(!Guard_ParseDate(ymd, exp)) return false; return (TimeCurrent() <= exp); } bool Guard_Passes(string &reason_out) { reason_out = ""; const long login = AccountInfoInteger(ACCOUNT_LOGIN); const long trade_mode = AccountInfoInteger(ACCOUNT_TRADE_MODE); const string server = AccountInfoString(ACCOUNT_SERVER); if(!Guard_LoginAllowed(InpGuard_AccountIDs, login)){ reason_out = "Login not allowed"; return false; } if(!Guard_ExpiryOK(InpGuard_ExpiryDate)){ reason_out = "Expired"; return false; } if(!Guard_ServerAllowed(InpGuard_Server, server)){ reason_out = "Server mismatch"; return false; } if(!Guard_AcctTypeAllowed(InpGuard_AccountType, trade_mode)){ reason_out = "Account type mismatch"; return false; } return true; } void UpdatePairTradeStatus() { double row_profit[TOTAL_PAIRS]; int row_orders[TOTAL_PAIRS]; ArrayInitialize(row_profit, 0.0); ArrayInitialize(row_orders, 0); CPositionInfo pos_info; string comment_prefix = g_object_prefix + "ROW:"; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(pos_info.SelectByIndex(i)) { if(pos_info.Magic() == InpMagicNumber) { string comment = pos_info.Comment(); if(StringFind(comment, comment_prefix) == 0) { string index_str = StringSubstr(comment, StringLen(comment_prefix)); int row_index = (int)StringToInteger(index_str); if(row_index >= 0 && row_index < TOTAL_PAIRS) { row_profit[row_index] += pos_info.Profit() + pos_info.Swap() + pos_info.Commission(); row_orders[row_index]++; } } } } } for(int i = 0; i < TOTAL_PAIRS; i++) { string profit_text; color profit_color; if(row_orders[i] > 0) { profit_text = StringFormat("%+.2f", row_profit[i]); profit_color = (row_profit[i] >= 0) ? C'127,255,212' : C'255,99,71'; } else { profit_text = "+0.00"; profit_color = c_font_color; } ObjectSetString(0, g_object_prefix + "PROFIT_CELL_" + (string)i + "_TXT", OBJPROP_TEXT, profit_text); ObjectSetInteger(0, g_object_prefix + "PROFIT_CELL_" + (string)i + "_TXT", OBJPROP_COLOR, profit_color); string order_text = (string)row_orders[i]; ObjectSetString(0, g_object_prefix + "ORDER_CELL_" + (string)i + "_TXT", OBJPROP_TEXT, order_text); } } void ClosePairTrades(const int row_index) { if(row_index < 0 || row_index >= TOTAL_PAIRS) return; const string pref = g_object_prefix + "ROW:" + (string)row_index; CPositionInfo pi; for(int i = PositionsTotal() - 1; i >= 0; --i) { if(pi.SelectByIndex(i) && pi.Magic()==InpMagicNumber && StringFind(pi.Comment(), pref) == 0) { if(trade.PositionClose(pi.Ticket())) g_last_row_close_bar[row_index] = TimeCurrent(); } } } void CloseMatrixSymbolTrades(const string symbol) { if(symbol=="") return; const string pref = g_object_prefix + "MATRIX:" + symbol; CPositionInfo pi; for(int i=PositionsTotal()-1;i>=0;--i) if(pi.SelectByIndex(i) && pi.Magic()==InpMagicNumber && StringFind(pi.Comment(), pref)==0) trade.PositionClose(pi.Ticket()); } void CloseAllProfitableTrades() { CPositionInfo pi; int idx = PositionsTotal() - 1; while(idx >= 0) { if(pi.SelectByIndex(idx) && pi.Magic()==InpMagicNumber) { const double pl = pi.Profit() + pi.Swap(); if(pl >= 0.0) trade.PositionClose(pi.Ticket()); } idx--; } } void CloseAllLosingTrades() { CPositionInfo pi; int idx = PositionsTotal() - 1; while(idx >= 0) { if(pi.SelectByIndex(idx) && pi.Magic()==InpMagicNumber) { const double pl = pi.Profit() + pi.Swap(); if(pl < 0.0) trade.PositionClose(pi.Ticket()); } idx--; } } void CreateMainBackground() { int width = c_total_panel_w + 245; int height = 860; CreateRectangle(g_object_prefix + "MAIN_BG", 5, 5, width, height, C'0, 0, 0', CORNER_LEFT_UPPER, BORDER_FLAT, true); } void CreateDashboardUI() { CreateMainBackground(); int y_pos = 10; int main_panel_start_x = 15; CreateTopPanels(main_panel_start_x, y_pos); y_pos += 120 + c_margin; CreateHeader(y_pos); y_pos += c_row_h + c_margin; for(int i = 0; i < TOTAL_PAIRS; i++) { CreateDataRow(i, y_pos); y_pos += c_row_h; } y_pos += c_margin; CreateAnalysisPanel(main_panel_start_x, y_pos); int right_panel_x = c_main_panel_w + 20; int right_y_pos = 10; CreateCorrelationMatrix(right_panel_x, right_y_pos); right_y_pos += GetMatrixPixelHeight() + 20; int bottom_panel_x = right_panel_x + 40 + InpRightPanelShiftX; CreateBottomPanels(bottom_panel_x, right_y_pos); int need_h = right_y_pos + 260; int cur_h = (int)ObjectGetInteger(0, g_object_prefix + "MAIN_BG", OBJPROP_YSIZE); if(need_h > cur_h) ObjectSetInteger(0, g_object_prefix + "MAIN_BG", OBJPROP_YSIZE, need_h); } void CreateGuardBannerIfNeeded() { if(g_guard_ok) return; string name = g_object_prefix + "GUARD_BANNER"; CreateRectangle(name, 15, 8, 360, 22, C'120,0,0', CORNER_LEFT_UPPER); CreateLabel(name+"_TXT", "GUARD: " + g_guard_reason, 22, 17, clrWhite, ANCHOR_LEFT, "Arial", 9); } void CreateTopPanels(int start_x, int start_y) { string P_PORT = g_object_prefix + "PORT_"; int port_panel_w = 440; CreateRectangle(P_PORT + "BG", start_x, start_y, port_panel_w, 120, c_header_bg, CORNER_LEFT_UPPER); CreateLabel(P_PORT + "HEADER", "Portfolio Summary", start_x + port_panel_w / 2, start_y + 8, clrGold, ANCHOR_CENTER); int x1 = start_x + 10; int x2 = start_x + 180; int y1 = start_y + 25; int y2 = y1 + 18; int y3 = y2 + 18; int y4 = y3 + 18; int x1_val = x1 + 150; CreateLabel(P_PORT + "LBL_Balance", "Balance:", x1, y1, c_font_color, ANCHOR_LEFT); CreateLabel(P_PORT + "VAL_Balance", "0.00", x1_val, y1, clrWhite, ANCHOR_RIGHT); CreateLabel(P_PORT + "LBL_Equity", "Equity:", x1, y2, c_font_color, ANCHOR_LEFT); CreateLabel(P_PORT + "VAL_Equity", "0.00", x1_val, y2, clrWhite, ANCHOR_RIGHT); CreateLabel(P_PORT + "LBL_Profit", "Total Profit:", x1, y3, c_font_color, ANCHOR_LEFT); CreateLabel(P_PORT + "VAL_Profit", "0.00", x1_val, y3, clrWhite, ANCHOR_RIGHT); CreateLabel(P_PORT + "LBL_ProfitC", "Profit Closed:", x1, y4, c_font_color, ANCHOR_LEFT); CreateLabel(P_PORT + "VAL_ProfitC", "0.00", x1_val, y4, clrWhite, ANCHOR_RIGHT); int x2_val = x2 + 240; CreateLabel(P_PORT + "LBL_Login", "Login:", x2, y1, c_font_color, ANCHOR_LEFT); CreateLabel(P_PORT + "VAL_Login", "0", x2_val, y1, clrWhite, ANCHOR_RIGHT); CreateLabel(P_PORT + "LBL_Broker", "Broker:", x2, y2, c_font_color, ANCHOR_LEFT); CreateLabel(P_PORT + "VAL_Broker", "N/A", x2_val, y2, clrWhite, ANCHOR_RIGHT); CreateLabel(P_PORT + "LBL_User", "User:", x2, y3, c_font_color, ANCHOR_LEFT); CreateLabel(P_PORT + "VAL_User", "N/A", x2_val, y3, clrWhite, ANCHOR_RIGHT); CreateLabel(P_PORT + "LBL_MarginL", "Margin Level:", x2, y4, c_font_color, ANCHOR_LEFT); CreateLabel(P_PORT + "VAL_MarginL", "0.00%", x2_val, y4, clrWhite, ANCHOR_RIGHT); string P_TIME = g_object_prefix + "TIME_"; int time_panel_x = start_x + port_panel_w + 5; int time_panel_w = c_main_panel_w - port_panel_w - 20; CreateRectangle(P_TIME + "BG", time_panel_x -5, start_y, time_panel_w, 120, c_header_bg, CORNER_LEFT_UPPER); CreateLabel(P_TIME + "HEADER", "Time & Market Sessions", time_panel_x + time_panel_w / 2, start_y + 8, clrGold, ANCHOR_CENTER); int xt1 = time_panel_x + 10; int xt2 = time_panel_x + 130; int yt1 = start_y + 25; int yt2 = yt1 + 18; int yt3 = yt2 + 18; int yt4 = yt3 + 18; int xt2_val = xt2 + 100; CreateLabel(P_TIME + "LBL_Local", "Local:", xt1, yt1, c_font_color, ANCHOR_LEFT); CreateLabel(P_TIME + "VAL_Local", "00:00:00", xt1 + 110, yt1, clrWhite, ANCHOR_RIGHT); CreateLabel(P_TIME + "LBL_Server", "Server:", xt1, yt2, c_font_color, ANCHOR_LEFT); CreateLabel(P_TIME + "VAL_Server", "00:00:00", xt1 + 110, yt2, clrWhite, ANCHOR_RIGHT); CreateLabel(P_TIME + "LBL_NY", "New York:", xt2, yt1, c_font_color, ANCHOR_LEFT); CreateLabel(P_TIME + "VAL_NY", "-", xt2_val, yt1, clrWhite, ANCHOR_RIGHT); CreateLabel(P_TIME + "LBL_LN", "London:", xt2, yt2, c_font_color, ANCHOR_LEFT); CreateLabel(P_TIME + "VAL_LN", "-", xt2_val, yt2, clrWhite, ANCHOR_RIGHT); CreateLabel(P_TIME + "LBL_SY", "Sydney:", xt2, yt3, c_font_color, ANCHOR_LEFT); CreateLabel(P_TIME + "VAL_SY", "-", xt2_val, yt3, clrWhite, ANCHOR_RIGHT); CreateLabel(P_TIME + "LBL_TK", "Tokyo:", xt2, yt4, c_font_color, ANCHOR_LEFT); CreateLabel(P_TIME + "VAL_TK", "-", xt2_val, yt4, clrWhite, ANCHOR_RIGHT); } int RowOpenCount(const int row){ int cnt=0; CPositionInfo pi; string pref = g_object_prefix + "ROW:" + (string)row; for(int i=PositionsTotal()-1;i>=0;--i) if(pi.SelectByIndex(i) && pi.Magic()==InpMagicNumber && StringFind(pi.Comment(), pref)==0) cnt++; return cnt; } void CreateBottomPanels(int start_x, int start_y) { string P_CTRL = g_object_prefix + "CTRL_"; int panel_w = 320; int gap = 8; int col_w = panel_w - 10; int x_lbl = start_x + 5; CreateRectangle(P_CTRL + "BG", start_x, start_y, panel_w, 135, c_header_bg, CORNER_LEFT_UPPER); int y_r1 = start_y + 10; int y_r2 = y_r1 + 20; int y_r3 = y_r2 + 22; int y_r4 = y_r3 + 22; int y_r5 = y_r4 + 26; CreateLabel(P_CTRL + "H_BASKET", "Basket & Money Management", start_x + panel_w/2, y_r1, clrGold, ANCHOR_CENTER, "Arial", InpFontSize); int btn_w_mode = (col_w - gap) / 2; CreateButton(P_CTRL + "BASKET_BTN_MODE_PERCENT", "Percent %", x_lbl, y_r2, btn_w_mode, 18, g_basket_exit_mode == MODE_PERCENT ? c_btn_on_bg : c_bg_light); CreateButton(P_CTRL + "BASKET_BTN_MODE_MONEY", "Money $", x_lbl + btn_w_mode + gap, y_r2, btn_w_mode, 18, g_basket_exit_mode == MODE_MONEY ? c_btn_on_bg : c_bg_light); int edit_box_h = c_row_h - 8; int label_y_offset = y_r3 + (int)((double)edit_box_h/1.5) - (InpFontSize/2); CreateLabel(P_CTRL + "LBL_TP_PERC", "Basket TP (%):", x_lbl, label_y_offset, c_font_color); CreateEditBox(P_CTRL + "BASKET_EDIT_TP_PERC", StringFormat("%.2f", g_basket_tp_percent), x_lbl + 90, y_r3, 50, edit_box_h); CreateLabel(P_CTRL + "LBL_TP_MONEY", "TP ($):", x_lbl + 165, label_y_offset, c_font_color); CreateEditBox(P_CTRL + "BASKET_EDIT_TP_MONEY", StringFormat("%.2f", g_basket_tp_money), x_lbl + 225, y_r3, 50, edit_box_h); label_y_offset = y_r4 + (int)((double)edit_box_h/1.3) - (InpFontSize/2); CreateLabel(P_CTRL + "LBL_SL_PERC", "Basket SL (%):", x_lbl, label_y_offset, c_font_color); CreateEditBox(P_CTRL + "BASKET_EDIT_SL_PERC", StringFormat("%.2f", g_basket_sl_percent), x_lbl + 90, y_r4, 50, edit_box_h); CreateLabel(P_CTRL + "LBL_SL_MONEY", "SL ($):", x_lbl + 165, label_y_offset, c_font_color); CreateEditBox(P_CTRL + "BASKET_EDIT_SL_MONEY", StringFormat("%.2f", g_basket_sl_money), x_lbl + 225, y_r4, 50, edit_box_h); int btn_w_enable = 120; int btn_x_enable = x_lbl + (col_w / 2) - (btn_w_enable / 2); CreateButton(P_CTRL + "BASKET_BTN_ENABLE", g_basket_exit_enabled ? "BASKET EXIT IS ON" : "ENABLE BASKET EXIT", btn_x_enable, y_r5, btn_w_enable, 18, g_basket_exit_enabled ? c_btn_on_bg : c_btn_off_bg); int exec_y_start = y_r5 + 22; CreateRectangle(P_CTRL + "EXEC_BG", start_x, exec_y_start, panel_w, 180, c_header_bg, CORNER_LEFT_UPPER); CreateLabel(P_CTRL + "H_EXECUTION", "Trade Execution & Timeframe", start_x + panel_w/2, exec_y_start + 8, clrGold, ANCHOR_CENTER, "Arial", InpFontSize); int exec_y2 = exec_y_start + 20; int exec_y3 = exec_y2 + 22; int exec_y4 = exec_y3 + 25; int exec_y5 = exec_y4 + 22; int exec_y5b = exec_y5 + 22; int exec_y6 = exec_y5b + 24; int btn_w_half = (col_w - gap) / 2; int x_col2_btn = x_lbl + btn_w_half + gap; CreateButton(P_CTRL + "TRADE_SETTINGS_BTN_LOT_FIXED", "FIXED LOT", x_lbl, exec_y2, btn_w_half, 18, g_lot_mode == LOT_MODE_FIXED ? c_btn_on_bg : c_btn_off_bg); CreateButton(P_CTRL + "TRADE_SETTINGS_BTN_LOT_AUTO", "AUTO RISK %", x_col2_btn, exec_y2, btn_w_half, 18, g_lot_mode == LOT_MODE_AUTO ? c_btn_on_bg : c_btn_off_bg); CreateEditBox(P_CTRL + "EDIT_FIXED_LOT", StringFormat("%.2f", g_fixed_lot_size), x_lbl, exec_y3, btn_w_half, 18); CreateEditBox(P_CTRL + "EDIT_RISK_PERC", StringFormat("%.2f", g_risk_percent), x_col2_btn, exec_y3, btn_w_half, 18); CreateButton(P_CTRL + "TRADE_SETTINGS_BTN_TIME_EXIT", "Time Exit", x_lbl, exec_y4, btn_w_half, 18, g_use_time_exit ? c_btn_on_bg : c_btn_off_bg); CreateButton(P_CTRL + "TRADE_SETTINGS_BTN_CORR_DECAY", "Corr Decay", x_col2_btn, exec_y4, btn_w_half, 18, g_use_corr_decay_exit? c_btn_on_bg : c_btn_off_bg); int btn_w_mtf = 150; int btn_x_mtf = x_lbl + (col_w/2) - (btn_w_mtf/2); CreateButton(P_CTRL + "TRADE_SETTINGS_BTN_MTF_FILTER", "MTF Filter", btn_x_mtf, exec_y5, btn_w_mtf, 20, g_use_mtf_filter ? c_btn_on_bg : c_btn_off_bg); int btn_w_sltp = 150; int btn_x_sltp = x_lbl + (col_w/2) - (btn_w_sltp/2); string cap_sltp = g_use_sl_tp ? ("SL/TP (ON • RR " + StringFormat("%.2f", g_rr) + ")") : "SL/TP (OFF)"; CreateButton(P_CTRL + "TRADE_SETTINGS_BTN_SLTP", cap_sltp, btn_x_sltp, exec_y5b, btn_w_sltp, 20, g_use_sl_tp ? c_btn_on_bg : c_btn_off_bg); int btn_w_tf = 35; int btn_w_reload = 55; int total_tf_width = (5 * btn_w_tf) + (4 * gap) + btn_w_reload + 5; int x_pos_tf = x_lbl + (col_w/2) - (total_tf_width/2); ENUM_TIMEFRAMES tfs_enum[] = {PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4, PERIOD_D1}; for(int ii=0; ii+0.75)", current_x+5, header_y, clrGold, ANCHOR_LEFT, "Arial", InpFontSize); CreateLabel(P_ANLS+"SIG_POS_L1", "1. (None)", current_x+5, data_y, c_font_color, ANCHOR_LEFT, "Arial", InpFontSize); current_x += sig_panel_w + c_margin; CreateLabel(P_ANLS+"SIG_NEG_H", "Top Negative (<-0.75)", current_x+5, header_y, clrGold, ANCHOR_LEFT, "Arial", InpFontSize); CreateLabel(P_ANLS+"SIG_NEG_L1", "1. (None)", current_x+5, data_y, c_font_color, ANCHOR_LEFT, "Arial", InpFontSize); current_x += sig_panel_w + c_margin; CreateLabel(P_ANLS+"SIG_DEL_H", "Delta Corr Spikes (>|0.1|)", current_x+5, header_y, clrGold, ANCHOR_LEFT, "Arial", InpFontSize); CreateLabel(P_ANLS+"SIG_DEL_L1", "1. (None)", current_x+5, data_y, c_font_color, ANCHOR_LEFT, "Arial", InpFontSize); string P_OPP = P_ANLS + "OPP_"; int opp_y_start = start_y + 55; CreateLabel(P_OPP + "HEADER", "Opportunity Finder (from Matrix)", start_x + 10, opp_y_start, clrGold, ANCHOR_LEFT, "Arial", InpFontSize); int opp_y = opp_y_start + 14; int x_rank = start_x + 10; int x_pair = start_x + 45; int x_corr = start_x + 135; int x_style = start_x + 225; int x_signal = start_x + 355; int x_btn = start_x + 470; int opp_row_h = 18; for(int ii=0; ii<5; ii++) { int centered_y = opp_y + (int)((double)opp_row_h/1.35) - InpFontSize/2; CreateLabel(P_OPP+"RANK_"+(string)ii, "#"+(string)(ii+1), x_rank, centered_y, c_font_color); CreateLabel(P_OPP+"PAIR_"+(string)ii, "-", x_pair, centered_y, clrWhite); CreateLabel(P_OPP+"CORR_"+(string)ii, "Corr: -", x_corr, centered_y, c_font_color); CreateLabel(P_OPP+"STYLE_"+(string)ii, "Style: -", x_style, centered_y, clrAquamarine); CreateLabel(P_OPP+"SIGNAL_"+(string)ii, "-", x_signal, centered_y, c_font_color); CreateButton(P_OPP+"TRADE_BTN_"+(string)ii, "[ TRADE ]", x_btn, opp_y, 80, opp_row_h, c_btn_bb_ss_bg); CreateLabel(P_OPP+"CONF_"+(string)ii, "CONF: -", x_btn + 150, centered_y, c_font_color); opp_y += opp_row_h + 4; } } void UpdateOpportunityPanel() { string P = g_object_prefix + "ANALYSIS_OPP_"; for(int ii = 0; ii < 5; ii++) { string btn_name = P + "TRADE_BTN_" + (string)ii; if(g_opportunities[ii].trade_action != SIGNAL_NONE) { string symbol = g_opportunities[ii].symbol; ObjectSetString(0, P + "PAIR_" + (string)ii, OBJPROP_TEXT, symbol); ObjectSetString(0, P + "CORR_" + (string)ii, OBJPROP_TEXT, "Corr: " + StringFormat("%+.2f", g_opportunities[ii].correlation)); ObjectSetString(0, P + "STYLE_" + (string)ii, OBJPROP_TEXT, "Style: " + g_opportunities[ii].style); string signal_text = ""; string btn_text = ""; color btn_color = c_btn_bb_ss_bg; if(g_opportunities[ii].trade_action == SIGNAL_BS) { signal_text = "Signal: BUY " + symbol; btn_text = "[ BUY " + symbol + " ]"; btn_color = c_btn_on_bg; } else if(g_opportunities[ii].trade_action == SIGNAL_SB) { signal_text = "Signal: SELL " + symbol; btn_text = "[ SELL " + symbol + " ]"; btn_color = c_btn_off_bg; } ObjectSetString(0, P + "SIGNAL_" + (string)ii, OBJPROP_TEXT, signal_text); ObjectSetString(0, btn_name, OBJPROP_TEXT, btn_text); ObjectSetInteger(0, btn_name, OBJPROP_XSIZE, 120); ObjectSetInteger(0, btn_name, OBJPROP_BGCOLOR, btn_color); double conf = g_opportunities[ii].confidence; ObjectSetString(0, P + "CONF_" + (string)ii, OBJPROP_TEXT, "CONF: " + StringFormat("%.0f%%", conf)); color conf_clr = (conf >= 70) ? C'30,255,150' : (conf >= 40 ? clrOrange : C'255,99,71'); ObjectSetInteger(0, P + "CONF_" + (string)ii, OBJPROP_COLOR, conf_clr); } else { ObjectSetString(0, P + "PAIR_" + (string)ii, OBJPROP_TEXT, "-"); ObjectSetString(0, P + "CORR_" + (string)ii, OBJPROP_TEXT, "Corr: -"); ObjectSetString(0, P + "STYLE_" + (string)ii, OBJPROP_TEXT, "Style: -"); ObjectSetString(0, P + "SIGNAL_" + (string)ii, OBJPROP_TEXT, "-"); ObjectSetString(0, btn_name, OBJPROP_TEXT, "[ TRADE ]"); ObjectSetInteger(0, btn_name, OBJPROP_XSIZE, 80); ObjectSetInteger(0, btn_name, OBJPROP_BGCOLOR, c_btn_bb_ss_bg); ObjectSetString(0, P + "CONF_" + (string)ii, OBJPROP_TEXT, "CONF: -"); ObjectSetInteger(0, P + "CONF_" + (string)ii, OBJPROP_COLOR, c_font_color); } } } int MakeHA(const string sym, ENUM_TIMEFRAMES tf) { int h = iCustom(sym, tf, "Examples\\Heiken_Ashi"); if(h == INVALID_HANDLE) h = iCustom(sym, tf, "Heiken_Ashi"); return h; } void RecreateIndicatorHandles() { Print("Recreating indicator handles. Main TF: ", EnumToString(g_current_timeframe), ", Conf TF: ", EnumToString(InpTimeframe_Confirmation)); for(int i = 0; i < TOTAL_PAIRS; i++) { IndicatorRelease(g_ha_handles_main[i][0]); IndicatorRelease(g_ha_handles_main[i][1]); IndicatorRelease(g_ha_handles_conf[i][0]); IndicatorRelease(g_ha_handles_conf[i][1]); g_ha_handles_main[i][0] = MakeHA(g_pairs[i][0], g_current_timeframe); g_ha_handles_main[i][1] = MakeHA(g_pairs[i][1], g_current_timeframe); g_ha_handles_conf[i][0] = MakeHA(g_pairs[i][0], InpTimeframe_Confirmation); g_ha_handles_conf[i][1] = MakeHA(g_pairs[i][1], InpTimeframe_Confirmation); g_adx_handles_main[i][0] = iADX(g_pairs[i][0], g_current_timeframe, InpADX_Period); g_adx_handles_main[i][1] = iADX(g_pairs[i][1], g_current_timeframe, InpADX_Period); } } void UpdateDashboardData() { for(int i = 0; i < TOTAL_PAIRS; i++) { double corr = g_corr_data[i].current_corr; double delta = g_corr_data[i].delta_corr; string corr_text = StringFormat("%+.2f", corr); if(g_last_corr_text[i] != corr_text) { ObjectSetString(0, g_object_prefix + "CURR_CORR_CELL_" + (string)i + "_TXT", OBJPROP_TEXT, corr_text); g_last_corr_text[i] = corr_text; } string delta_text = StringFormat("%+.3f", delta); if(g_last_delta_text[i] != delta_text) { ObjectSetString(0, g_object_prefix + "DELTA_CORR_CELL_" + (string)i + "_TXT", OBJPROP_TEXT, delta_text); g_last_delta_text[i] = delta_text; } color corr_color = c_font_color; if(corr >= 0.7) corr_color = C'30,255,150'; else if(corr > 0.3) corr_color = C'152,251,152'; else if(corr <= -0.7) corr_color = C'255,99,71'; else if(corr < -0.3) corr_color = C'250,128,114'; if(g_last_corr_color[i] != corr_color) { ObjectSetInteger(0, g_object_prefix + "CURR_CORR_CELL_" + (string)i + "_TXT", OBJPROP_COLOR, corr_color); g_last_corr_color[i] = corr_color; } color delta_color = c_font_color; if(MathAbs(delta) > 0.1) delta_color = clrOrange; if(g_last_delta_color[i] != delta_color) { ObjectSetInteger(0, g_object_prefix + "DELTA_CORR_CELL_" + (string)i + "_TXT", OBJPROP_COLOR, delta_color); g_last_delta_color[i] = delta_color; } } } void UpdateInfoPanel() { string P_PORT = g_object_prefix + "PORT_"; double balance = AccountInfoDouble(ACCOUNT_BALANCE); double equity = AccountInfoDouble(ACCOUNT_EQUITY); double credit = AccountInfoDouble(ACCOUNT_CREDIT); double total_profit = AccountInfoDouble(ACCOUNT_PROFIT); double margin_level = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL); double total_lots = 0; int total_orders = 0; CPositionInfo pos_info; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(pos_info.SelectByIndex(i) && pos_info.Magic() == InpMagicNumber) { total_orders++; total_lots += pos_info.Volume(); } } double closed_profit; int closed_count; CalculateClosedProfitAndCount(closed_profit, closed_count); long login = AccountInfoInteger(ACCOUNT_LOGIN); string broker_name = AccountInfoString(ACCOUNT_SERVER); string user_name = AccountInfoString(ACCOUNT_NAME); ObjectSetString(0, P_PORT + "VAL_Balance", OBJPROP_TEXT, StringFormat("%.2f", balance)); ObjectSetString(0, P_PORT + "VAL_Equity", OBJPROP_TEXT, StringFormat("%.2f", equity)); ObjectSetString(0, P_PORT + "VAL_Profit", OBJPROP_TEXT, StringFormat("%+.2f", total_profit)); ObjectSetInteger(0, P_PORT + "VAL_Profit", OBJPROP_COLOR, (total_profit >= 0) ? C'127,255,212' : C'255,99,71'); ObjectSetString(0, P_PORT + "VAL_ProfitC", OBJPROP_TEXT, StringFormat("%+.2f (%d)", closed_profit, closed_count)); ObjectSetInteger(0, P_PORT + "VAL_ProfitC", OBJPROP_COLOR, (closed_profit >= 0) ? C'127,255,212' : C'255,99,71'); ObjectSetString(0, P_PORT + "VAL_Login", OBJPROP_TEXT, (string)login); ObjectSetString(0, P_PORT + "VAL_Broker", OBJPROP_TEXT, broker_name); ObjectSetString(0, P_PORT + "VAL_User", OBJPROP_TEXT, user_name); ObjectSetString(0, P_PORT + "VAL_MarginL", OBJPROP_TEXT, StringFormat("%.2f%%", margin_level)); } void CreateCorrelationMatrix(int start_x, int start_y) { int cell_size = 40; for(int i = 0; i < ArraySize(g_matrix_symbols); i++) { CreateLabel(g_object_prefix + "MATRIX_H_COL_" + (string)i, g_matrix_symbols[i], start_x + cell_size / 2 + (i + 1) * cell_size, start_y + cell_size / 2, c_font_color, ANCHOR_CENTER, "Arial", 8); CreateLabel(g_object_prefix + "MATRIX_H_ROW_" + (string)i, g_matrix_symbols[i], start_x + cell_size / 2, start_y + cell_size / 2 + (i + 1) * cell_size, c_font_color, ANCHOR_CENTER, "Arial", 8); } for(int r = 0; r < ArraySize(g_matrix_symbols); r++) { for(int c = 0; c < ArraySize(g_matrix_symbols); c++) { string name = g_object_prefix + "MATRIX_CELL_" + (string)r + "_" + (string)c; CreateCellWithText(name, "...", start_x + (c + 1) * cell_size, start_y + (r + 1) * cell_size, cell_size, cell_size, c_bg_dark, c_font_color, 7, ANCHOR_CENTER); } } } void UpdateTimePanel() { string P_TIME = g_object_prefix + "TIME_"; datetime server_time = TimeCurrent(); datetime gmt_time = TimeGMT(); MqlDateTime gmt_struct; TimeToStruct(gmt_time, gmt_struct); int current_hour_gmt = gmt_struct.hour; ObjectSetString(0, P_TIME + "VAL_Local", OBJPROP_TEXT, TimeToString(TimeLocal(), TIME_SECONDS)); ObjectSetString(0, P_TIME + "VAL_Server", OBJPROP_TEXT, TimeToString(server_time, TIME_SECONDS)); string ny_status = IsSessionOpen(NY_OPEN, NY_CLOSE, current_hour_gmt) ? "Open" : "Closed"; string ln_status = IsSessionOpen(LONDON_OPEN, LONDON_CLOSE, current_hour_gmt) ? "Open" : "Closed"; string sy_status = IsSessionOpen(SYDNEY_OPEN, SYDNEY_CLOSE, current_hour_gmt) ? "Open" : "Closed"; string tk_status = IsSessionOpen(TOKYO_OPEN, TOKYO_CLOSE, current_hour_gmt) ? "Open" : "Closed"; ObjectSetString(0, P_TIME + "VAL_NY", OBJPROP_TEXT, ny_status); ObjectSetString(0, P_TIME + "VAL_LN", OBJPROP_TEXT, ln_status); ObjectSetString(0, P_TIME + "VAL_SY", OBJPROP_TEXT, sy_status); ObjectSetString(0, P_TIME + "VAL_TK", OBJPROP_TEXT, tk_status); ObjectSetInteger(0, P_TIME + "VAL_NY", OBJPROP_COLOR, (ny_status == "Open") ? c_btn_on_bg : c_btn_off_bg); ObjectSetInteger(0, P_TIME + "VAL_LN", OBJPROP_COLOR, (ln_status == "Open") ? c_btn_on_bg : c_btn_off_bg); ObjectSetInteger(0, P_TIME + "VAL_SY", OBJPROP_COLOR, (sy_status == "Open") ? c_btn_on_bg : c_btn_off_bg); ObjectSetInteger(0, P_TIME + "VAL_TK", OBJPROP_COLOR, (tk_status == "Open") ? c_btn_on_bg : c_btn_off_bg); } void CloseCorrelationGraph() { ObjectsDeleteAll(0, g_object_prefix + "Graph_"); g_active_graph_row = -1; ChartRedraw(); } void ShowCorrelationGraph(int row_index) { CloseCorrelationGraph(); if(row_index < 0 || row_index >= TOTAL_PAIRS || ArraySize(g_corr_data[row_index].history) < 1) return; g_active_graph_row = row_index; string P = g_object_prefix + "Graph_"; int graph_w = 250, graph_h = 150; int graph_x = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) / 2 - graph_w / 2; int graph_y = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS) / 2 - graph_h / 2; color bg_color = C'15,25,45', border_color = C'80,100,130'; CreateRectangle(P + "BG", graph_x, graph_y, graph_w, graph_h, bg_color, CORNER_LEFT_UPPER, BORDER_FLAT, false); ObjectSetInteger(0, P + "BG", OBJPROP_BORDER_COLOR, border_color); string title = "Recent Corr. History: " + g_corr_data[row_index].symbol1 + "/" + g_corr_data[row_index].symbol2; CreateLabel(P + "Title", title, graph_x + 10, graph_y + 8, clrGold, ANCHOR_LEFT, "Arial", 9); CreateButton(P + "CloseBtn", "X", graph_x + graph_w - 25, graph_y + 5, 20, 20, c_btn_off_bg); int num_to_show = 5; int history_size = ArraySize(g_corr_data[row_index].history); int start_index = MathMax(0, history_size - num_to_show); int current_y = graph_y + 35; int line_height = 18; int tick_counter = 1; CreateLabel(P + "Header", " Tick Value", graph_x + 10, current_y, C'180,180,180', ANCHOR_LEFT, "Courier New", 9); current_y += line_height; for(int i = start_index; i < history_size; i++) { string corr_value_text = StringFormat("%+.4f", g_corr_data[row_index].history[i]); string line_text = StringFormat(" %-10s %s", "Tick " + (string)tick_counter, corr_value_text); CreateLabel(P + "HistoryLine_" + (string)i, line_text, graph_x + 10, current_y, clrWhite, ANCHOR_LEFT, "Courier New", 9); current_y++; current_y += (line_height - 1); tick_counter++; } ChartRedraw(); } string SignalTypeToString(ENUM_SIGNAL_TYPE signal_type) { switch(signal_type) { case SIGNAL_BB: return "BB"; case SIGNAL_SS: return "SS"; case SIGNAL_BS: return "BS"; case SIGNAL_SB: return "SB"; default: return ""; } } int GetRowIndexFromSymbol(string symbol) { for(int i = 0; i < TOTAL_PAIRS; i++) { if(g_pairs[i][0] == symbol || g_pairs[i][1] == symbol) return i; } return -1; } void ResetAllHighlights(int index) { ObjectSetInteger(0, g_object_prefix + "BB_BTN_" + (string)index, OBJPROP_BGCOLOR, c_btn_bb_ss_bg); ObjectSetInteger(0, g_object_prefix + "SS_BTN_" + (string)index, OBJPROP_BGCOLOR, c_btn_bb_ss_bg); ObjectSetInteger(0, g_object_prefix + "BS_BTN_" + (string)index, OBJPROP_BGCOLOR, c_btn_bs_sb_bg); ObjectSetInteger(0, g_object_prefix + "SB_BTN_" + (string)index, OBJPROP_BGCOLOR, c_btn_bs_sb_bg); } void UpdateSignalHighlight(int index, string button_to_highlight) { ResetAllHighlights(index); if(button_to_highlight != "") { string btn_name = g_object_prefix + button_to_highlight + "_BTN_" + (string)index; ObjectSetInteger(0, btn_name, OBJPROP_BGCOLOR, c_btn_highlight_bg); } } string Trim(const string s) { string t = s; StringTrimLeft(t); StringTrimRight(t); return t; } bool ConfirmForce(const string title, const string body) { int r = MessageBox(body, title, MB_OKCANCEL | MB_ICONWARNING); return (r == IDOK); } void NotifyUser(const string msg) { Print("[OracleDash] ", msg); } string LegLabel(const ENUM_ORDER_TYPE t) { return (t == ORDER_TYPE_BUY ? "BUY" : "SELL"); } bool HasOpenForRow(const int row) { CPositionInfo pi; string pref = g_object_prefix + "ROW:" + (string)row; for(int i = PositionsTotal() - 1; i >= 0; --i) if(pi.SelectByIndex(i) && pi.Magic() == InpMagicNumber && StringFind(pi.Comment(), pref) == 0) return true; return false; } double Clamp01(double v) { if(v < 0.0) return 0.0; if(v > 1.0) return 1.0; return v; } double SmoothStep01(double t) { t = Clamp01(t); return t * t * (3.0 - 2.0 * t); } double SoftCap100(double x, double k) { if(k <= 1e-6) k = 1.0; if(x <= 0.0) return 0.0; double v = 100.0 * (1.0 - MathExp(-x / k)); if(v > 100.0) v = 100.0; return v; } double GetSpreadPts(const string sym) { MqlTick tk; if(!SymbolInfoTick(sym, tk)) return 0.0; double pt = SymbolInfoDouble(sym, SYMBOL_POINT); if(pt <= 0) return 0.0; return (tk.ask - tk.bid) / pt; } bool IsMajorSessionOpen() { datetime gmt = TimeGMT(); MqlDateTime st; TimeToStruct(gmt, st); int h = st.hour; return IsSessionOpen(LONDON_OPEN, LONDON_CLOSE, h) || IsSessionOpen(NY_OPEN, NY_CLOSE, h); } double AdjustConfidenceByLogic(double conf) { double pct = InpLogicUsePercent; if(pct < 10.0) pct = 10.0; if(pct > 200.0) pct = 200.0; double scale = 100.0 / pct; double out = conf * scale; if(out > 100.0) out = 100.0; if(out < 0.0) out = 0.0; return out; } bool EnsureSymbolSelected(const string sym) { if(sym == "") return false; if((bool)SymbolInfoInteger(sym, SYMBOL_SELECT)) return true; return SymbolSelect(sym, true); } bool DetectSymbolSuffix(string &out_suffix) { if(InpSymbolSuffix != "") { out_suffix = InpSymbolSuffix; return true; } string probes[] = {"EURUSD","USDJPY","GBPUSD","AUDUSD","USDCAD","USDCHF","NZDUSD","XAUUSD"}; int total = SymbolsTotal(true); for(int p=0; p= (int)StringLen(InpSymbolSuffix) && StringSubstr(tail, StringLen(tail)-StringLen(InpSymbolSuffix)) == InpSymbolSuffix) { SymbolSelect(nm, true); return nm; } } } return ""; } if(SymbolSelect(base, true)) return base; int total = SymbolsTotal(true); int base_len = (int)StringLen(base); for(int i=0;i= base_len && StringSubstr(nm,0,base_len)==base) { SymbolSelect(nm, true); return nm; } } return ""; } #ifndef __PAIR_SPREAD_SUITE__ #define __PAIR_SPREAD_SUITE__ bool HedgeBeta(const double &x[], const double &y[], int n, double &beta) { if(n < 20) return false; double mx = ArrayMean(x), my = ArrayMean(y), num = 0, den = 0; for(int i = 0; i < n; i++) { double dx = x[i] - mx, dy = y[i] - my; num += dx * dy; den += dy * dy; } if(den <= 0) return false; beta = num / den; return true; } double ZScoreSeriesLatest(const double &arr[]) { int n = ArraySize(arr); if(n < 10) return 0.0; double m = 0, v = 0; for(int i = 0; i < n; i++) m += arr[i]; m /= n; for(int i = 0; i < n; i++) { double d = arr[i] - m; v += d * d; } double sd = (n > 1) ? MathSqrt(v / (n - 1)) : 0.0; if(sd == 0) return 0.0; return (arr[0] - m) / sd; } bool PairSignalBySpread(int idx, double &z, ENUM_SIGNAL_TYPE &sig) { sig = SIGNAL_NONE; z = 0.0; if(idx < 0 || idx >= TOTAL_PAIRS) return false; string s1 = g_corr_data[idx].symbol1, s2 = g_corr_data[idx].symbol2; double p1[], p2[]; if(!GetPricesFromCache(s1, p1) || !GetPricesFromCache(s2, p2)) return false; int n = MathMin(ArraySize(p1), ArraySize(p2)); if(n < 60) return false; double l1[], l2[]; ArrayResize(l1, n); ArrayResize(l2, n); for(int i = 0; i < n; i++){ l1[i] = MathLog(p1[i]); l2[i] = MathLog(p2[i]); } double beta; if(!HedgeBeta(l1, l2, n, beta)) return false; double spr[]; ArrayResize(spr, n); for(int i = 0; i < n; i++) spr[i] = l1[i] - beta * l2[i]; z = ZScoreSeriesLatest(spr); if(z >= InpZSpikeEnter) { sig = SIGNAL_SB; return true; } if(z <= -InpZSpikeEnter) { sig = SIGNAL_BS; return true; } return true; } #endif void ApplySuffixToPairs() { int missing = 0; for(int i=0; i0) PrintFormat("[PAIR-RESOLVE] %d leg(s) could not be resolved with suffix '%s'. Check broker symbol list.", missing, InpSymbolSuffix); } double BuildSLTP(const string sym, const bool isBuy, const double atrMult, double &sl, double &tp, const double RR) { sl = 0.0; tp = 0.0; int h = iATR(sym, g_current_timeframe, InpATR_Period); if(h == INVALID_HANDLE) return 0.0; double buff[]; if(CopyBuffer(h, 0, 0, 2, buff) < 2) return 0.0; double atr = buff[1]; if(atr <= 0) return 0.0; double pt = atr * atrMult; double ask = SymbolInfoDouble(sym, SYMBOL_ASK); double bid = SymbolInfoDouble(sym, SYMBOL_BID); if(isBuy) { sl = ask - pt; tp = ask + pt * RR; } else { sl = bid + pt; tp = bid - pt * RR; } return pt; } double ZscoreArray(const double &arr[]) { int n = ArraySize(arr); if(n < 5) return 0.0; double m = 0, v = 0; for(int i = 0; i < n; i++) m += arr[i]; m /= n; for(int i = 0; i < n; i++) { double d = arr[i] - m; v += d * d; } double sd = (n > 1) ? MathSqrt(v / (n - 1)) : 0; if(sd == 0) return 0.0; return (arr[n - 1] - m) / sd; } bool PassesPreTradeFilters(const string sym) { if(!InpUsePreFilters) return true; MqlTick tick; if(!SymbolInfoTick(sym, tick)) return false; double pt = SymbolInfoDouble(sym, SYMBOL_POINT); double spreadPts = (tick.ask - tick.bid) / pt; if(spreadPts > GetSpreadLimit(sym)) return false; int h = iATR(sym, g_current_timeframe, InpATR_Period); double b[]; if(h == INVALID_HANDLE || CopyBuffer(h, 0, 0, 2, b) < 2) return false; if(b[1] / pt < InpMinATRPoints) return false; if(InpRequireHAFreshFlip) { int ha = MakeHA(sym, g_current_timeframe); double o[], c[]; if(ha == INVALID_HANDLE) return false; if(CopyBuffer(ha, 0, 0, InpHAFreshBars + 1, o) < InpHAFreshBars + 1) return false; if(CopyBuffer(ha, 3, 0, InpHAFreshBars + 1, c) < InpHAFreshBars + 1) return false; bool flipped = false; for(int i = 0; i < InpHAFreshBars; i++) { bool up = c[i] > o[i]; bool upPrev = c[i + 1] > o[i + 1]; if(up != upPrev){ flipped = true; break; } } if(!flipped) return false; } return true; } bool PreflightDeal(const string sym, ENUM_ORDER_TYPE typ, double vol, double &sl, double &tp, string &reason, uint &retcodeOut) { reason = ""; retcodeOut = 0; if(sym == ""){ reason = "symbol empty"; return false; } if(!EnsureSymbolSelected(sym)){ reason = "symbol not tradable"; return false; } vol = NormalizeVolumeToStep(sym, vol); if(vol <= 0){ reason = "volume<=0 after normalize"; return false; } if(!PassesPreTradeFilters(sym)){ reason = "pre-filter failed"; return false; } double price = (typ == ORDER_TYPE_BUY) ? SymbolInfoDouble(sym, SYMBOL_ASK) : SymbolInfoDouble(sym, SYMBOL_BID); if(price <= 0){ reason = "no market price"; return false; } if(sl > 0 || tp > 0) EnsureStopsRespectLevels(sym, (typ == ORDER_TYPE_BUY), sl, tp); double mg = 0; if(!OrderCalcMargin(typ, sym, vol, price, mg)) mg = 0; const double fm = AccountInfoDouble(ACCOUNT_MARGIN_FREE); if(fm < mg){ reason = StringFormat("insufficient margin need=%.2f free=%.2f", mg, fm); return false; } retcodeOut = 0; return true; } double GetSpreadLimit(const string sym) { if(StringSubstr(sym, 0, 3) == "XAU") return MathMax(InpMaxSpreadPoints, 200); return InpMaxSpreadPoints; } bool SingleSymbolDirection(const string sym, ENUM_SIGNAL_TYPE &out_action) { int adx = iADX(sym, g_current_timeframe, InpADX_Period); double a[]; if(adx == INVALID_HANDLE || CopyBuffer(adx, 2, 0, 2, a) < 2) return false; if(a[1] < InpADX_TrendLevel) return false; int ha = MakeHA(sym, g_current_timeframe); double o[], c[]; if(ha == INVALID_HANDLE || CopyBuffer(ha, 0, 0, 1, o) < 1 || CopyBuffer(ha, 3, 0, 1, c) < 1) return false; bool bull = (c[0] > o[0]); bool bear = (c[0] < o[0]); if(g_use_mtf_filter) { int hac = MakeHA(sym, InpTimeframe_Confirmation); double oc[], cc[]; if(hac == INVALID_HANDLE || CopyBuffer(hac, 0, 0, 1, oc) < 1 || CopyBuffer(hac, 3, 0, 1, cc) < 1) return false; if(bull && !(cc[0] > oc[0])) return false; if(bear && !(cc[0] < oc[0])) return false; } if(bull) out_action = SIGNAL_BS; else if(bear) out_action = SIGNAL_SB; else return false; return true; } double PipValuePerLot(const string sym) { double tv = SymbolInfoDouble(sym, SYMBOL_TRADE_TICK_VALUE); double ts = SymbolInfoDouble(sym, SYMBOL_TRADE_TICK_SIZE); int dg = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS); if(tv <= 0 || ts <= 0) return 0.0; double pip_points = (dg == 5 || dg == 3) ? 10.0 : 1.0; return tv * pip_points; } void AdjustLotsRiskParity(const string sym1, const string sym2, double &lot1, double &lot2, bool allowUpsize = false) { double pv1 = PipValuePerLot(sym1), pv2 = PipValuePerLot(sym2); if(pv1 <= 0 || pv2 <= 0){ lot2 = lot1; return; } double step1 = SymbolInfoDouble(sym1, SYMBOL_VOLUME_STEP); double step2 = SymbolInfoDouble(sym2, SYMBOL_VOLUME_STEP); double min1 = SymbolInfoDouble(sym1, SYMBOL_VOLUME_MIN); double max1 = SymbolInfoDouble(sym1, SYMBOL_VOLUME_MAX); double min2 = SymbolInfoDouble(sym2, SYMBOL_VOLUME_MIN); double max2 = SymbolInfoDouble(sym2, SYMBOL_VOLUME_MAX); lot1 = MathMax(min1, MathMin(max1, MathFloor(lot1 / step1) * step1)); double R = lot1 * pv1; double raw2 = (pv2 > 0 ? R / pv2 : lot1); double rounded2 = MathFloor(raw2 / step2 + 0.5) * step2; if(!allowUpsize && rounded2 > raw2) rounded2 = MathFloor(raw2 / step2) * step2; lot2 = MathMax(min2, MathMin(max2, rounded2)); PrintFormat("[RiskParity] %s pip=$%.2f, %s pip=$%.2f -> lots %.2f / %.2f (target raw2=%.5f)", sym1, pv1, sym2, pv2, lot1, lot2, raw2); } bool InRowCooldown(const int row) { if(row < 0 || row >= TOTAL_PAIRS) return false; datetime t0 = g_last_row_close_bar[row]; if(t0 == 0) return false; string sym = g_corr_data[row].symbol1; int idx = iBarShift(sym, g_current_timeframe, t0, true); if(idx < 0) return false; return (idx < InpRowCooldownBars); } int GetMatrixPixelHeight() { const int cell = 40; const int pad = 10; int n = ArraySize(g_matrix_symbols); return (n + 1) * cell + pad; } void BuildUniqueSymbolListAndPrepareCache() { string temp_list[]; int k = 0; for(int i = 0; i < TOTAL_PAIRS; i++) { ArrayResize(temp_list, k + 2); temp_list[k++] = g_pairs[i][0]; temp_list[k++] = g_pairs[i][1]; } int nccy = ArraySize(g_matrix_symbols); for(int r = 0; r < nccy; r++) { for(int c = r; c < nccy; c++) { string s_cross = GetSymbolForCurrencies(g_matrix_symbols[r], g_matrix_symbols[c]); if(s_cross != "") { ArrayResize(temp_list, k + 1); temp_list[k++] = s_cross; } string s_usd_r = GetSymbolForCurrencies(g_matrix_symbols[r], "USD"); if(s_usd_r != "") { ArrayResize(temp_list, k + 1); temp_list[k++] = s_usd_r; } string s_usd_c = GetSymbolForCurrencies(g_matrix_symbols[c], "USD"); if(s_usd_c != "") { ArrayResize(temp_list, k + 1); temp_list[k++] = s_usd_c; } string s_eur_r = GetSymbolForCurrencies(g_matrix_symbols[r], "EUR"); if(s_eur_r != "") { ArrayResize(temp_list, k + 1); temp_list[k++] = s_eur_r; } string s_eur_c = GetSymbolForCurrencies(g_matrix_symbols[c], "EUR"); if(s_eur_c != "") { ArrayResize(temp_list, k + 1); temp_list[k++] = s_eur_c; } } } string unique_symbols[]; for(int i = 0; i < ArraySize(temp_list); i++) { string s = temp_list[i]; if(s == "") continue; bool seen = false; for(int j = 0; j < ArraySize(unique_symbols); j++) { if(unique_symbols[j] == s) { seen = true; break; } } if(!seen) { int sz = ArraySize(unique_symbols); ArrayResize(unique_symbols, sz + 1); unique_symbols[sz] = s; } } int total_unique = ArraySize(unique_symbols); ArrayResize(g_price_cache, total_unique); ArrayResize(g_last_cache_time, total_unique); ArrayInitialize(g_last_cache_time, 0); for(int i = 0; i < total_unique; i++) { g_price_cache[i].name = unique_symbols[i]; ArrayResize(g_price_cache[i].prices, InpLookback); if(!EnsureSymbolSelected(unique_symbols[i])) Print("Warning: cannot select symbol ", unique_symbols[i], " to Market Watch."); } } bool GetPricesFromCache(string symbol, double &prices[]) { for(int i = 0; i < ArraySize(g_price_cache); i++) { if(g_price_cache[i].name == symbol) { ArrayCopy(prices, g_price_cache[i].prices, 0, 0, WHOLE_ARRAY); return true; } } return false; } double CalculatePearsonCorrelation(const double &x[], const double &y[], int count) { if(count == 0) return 0; double sum_x = 0, sum_y = 0, sum_xy = 0, sum_x2 = 0, sum_y2 = 0; for(int i = 0; i < count; i++) { sum_x += x[i]; sum_y += y[i]; sum_xy += x[i] * y[i]; sum_x2 += x[i] * x[i]; sum_y2 += y[i] * y[i]; } double num = count * sum_xy - sum_x * sum_y; double den_x = count * sum_x2 - sum_x * sum_x; double den_y = count * sum_y2 - sum_y * sum_y; if(den_x <= 0 || den_y <= 0) return 0.0; return num / (MathSqrt(den_x * den_y)); } double ArraySum(const double &arr[]) { double s = 0; for(int i = 0; i < ArraySize(arr); i++) s += arr[i]; return s; } double ArrayMean(const double &arr[]) { int s = ArraySize(arr); if(s == 0) return 0; return ArraySum(arr) / s; } void ArrayRemove(double &arr[], int start_index, int count) { int size = ArraySize(arr); if(start_index < 0 || start_index >= size || count <= 0) return; int to_move = size - (start_index + count); if(to_move > 0) ArrayCopy(arr, arr, start_index, start_index + count, to_move); ArrayResize(arr, size - count); } void CalculateClosedProfitAndCount(double &profit_out, int &count_out) { profit_out = 0.0; count_out = 0; HistorySelect((datetime)0, TimeCurrent()); CDealInfo deal; for(int i = 0; i < HistoryDealsTotal(); i++) { if(deal.SelectByIndex(i)) { if(deal.Magic() == InpMagicNumber && (deal.Entry() == DEAL_ENTRY_OUT || deal.Entry() == DEAL_ENTRY_INOUT)) { profit_out += deal.Profit() + deal.Swap() + deal.Commission(); count_out++; } } } } bool IsSessionOpen(int open_hour, int close_hour, int current_hour_gmt) { if (open_hour < close_hour) { return current_hour_gmt >= open_hour && current_hour_gmt < close_hour; } else { return current_hour_gmt >= open_hour || current_hour_gmt < close_hour; } } void CreateCellWithText(string name,string text,int x,int y,int w,int h,color bg_clr,color text_clr,int font_size,ENUM_ANCHOR_POINT anchor,string font="Arial",ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER) { CreateRectangle(name+"_BG",x,y,w,h,bg_clr,corner,BORDER_FLAT); int label_x; if(anchor==ANCHOR_CENTER) label_x=x+w/2; else label_x=x+5; CreateLabel(name+"_TXT",text,label_x,y+h/2-font_size/2+3,text_clr,anchor,font,font_size,corner); } void CreateRectangle(string name,int x,int y,int w,int h,color clr,ENUM_BASE_CORNER corner,ENUM_BORDER_TYPE border=BORDER_FLAT,bool is_background=true) { ObjectCreate(0,name,OBJ_RECTANGLE_LABEL,0,0,0); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_XSIZE,w); ObjectSetInteger(0,name,OBJPROP_YSIZE,h); ObjectSetInteger(0,name,OBJPROP_BGCOLOR,clr); ObjectSetInteger(0,name,OBJPROP_CORNER,corner); ObjectSetInteger(0,name,OBJPROP_BORDER_TYPE,border); ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false); if(is_background) ObjectSetInteger(0,name,OBJPROP_BACK,true); } void CreateLabel(string name,string text,int x,int y,color clr,ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT,string font="Arial",int font_size=-1,ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER) { if(font_size==-1) font_size=InpFontSize; ObjectCreate(0,name,OBJ_LABEL,0,0,0); ObjectSetString(0,name,OBJPROP_TEXT,text); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_COLOR,clr); ObjectSetInteger(0,name,OBJPROP_CORNER,corner); ObjectSetInteger(0,name,OBJPROP_ANCHOR,anchor); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,font_size); ObjectSetString(0,name,OBJPROP_FONT,font); ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false); } void CreateButton(string name,string text,int x,int y,int w,int h,color bg_clr,ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER) { ObjectCreate(0,name,OBJ_BUTTON,0,0,0); ObjectSetString(0,name,OBJPROP_TEXT,text); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_XSIZE,w); ObjectSetInteger(0,name,OBJPROP_YSIZE,h); ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bg_clr); ObjectSetInteger(0,name,OBJPROP_COLOR,clrWhite); ObjectSetInteger(0,name,OBJPROP_CORNER,corner); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,InpFontSize); ObjectSetInteger(0,name,OBJPROP_STATE,false); ObjectSetInteger(0,name,OBJPROP_BORDER_COLOR,C'80,80,80'); } void CreateEditBox(string name, string text, int x, int y, int w, int h, ENUM_ALIGN_MODE align=ALIGN_RIGHT) { ObjectCreate(0,name,OBJ_EDIT,0,0,0); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_XSIZE,w); ObjectSetInteger(0,name,OBJPROP_YSIZE,h); ObjectSetString(0,name,OBJPROP_TEXT,text); ObjectSetInteger(0,name,OBJPROP_COLOR,c_font_color); ObjectSetInteger(0,name,OBJPROP_BGCOLOR,c_edit_bg); ObjectSetInteger(0,name,OBJPROP_BORDER_COLOR,c_edit_border); ObjectSetInteger(0,name,OBJPROP_ALIGN,align); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,InpFontSize); ObjectSetString(0,name,OBJPROP_FONT,"Arial"); } void UpdateAutoButton(int index) { string btn_name=g_object_prefix+"AUTO_BTN_"+(string)index; if(g_auto_status[index]) { ObjectSetString(0,btn_name,OBJPROP_TEXT,"on"); ObjectSetInteger(0,btn_name,OBJPROP_BGCOLOR,c_btn_on_bg); } else { ObjectSetString(0,btn_name,OBJPROP_TEXT,"off"); ObjectSetInteger(0,btn_name,OBJPROP_BGCOLOR,c_btn_off_bg); } } color InterpolateColor(color start_color,color end_color,double factor) { if(factor<0) factor=0; if(factor>1) factor=1; uchar start_r=(uchar)((start_color>>16)&0xFF); uchar start_g=(uchar)((start_color>>8)&0xFF); uchar start_b=(uchar)(start_color&0xFF); uchar end_r=(uchar)((end_color>>16)&0xFF); uchar end_g=(uchar)((end_color>>8)&0xFF); uchar end_b=(uchar)(end_color&0xFF); uchar r=(uchar)(start_r+(end_r-start_r)*factor); uchar g=(uchar)(start_g+(end_g-start_g)*factor); uchar b=(uchar)(start_b+(end_b-start_b)*factor); return StringToColor((string)r+","+(string)g+","+(string)b); } string GetSymbolForCurrencies(string ccy1, string ccy2) { if(ccy1 == ccy2) return ""; string base = ccy1 + ccy2; string real1 = ResolveSymbolFromBase(base); if(real1 != "") return real1; base = ccy2 + ccy1; string real2 = ResolveSymbolFromBase(base); if(real2 != "") return real2; return ""; } void SafeRedraw() { const ulong now_ms = GetMicrosecondCount() / 1000; if(now_ms - g_last_ui_ms >= (ulong)InpUiUpdateMs) { ChartRedraw(); g_last_ui_ms = now_ms; } } void OnTradeTransaction(const MqlTradeTransaction& trans,const MqlTradeRequest& req,const MqlTradeResult& res) { if(trans.type==TRADE_TRANSACTION_DEAL_ADD || trans.type==TRADE_TRANSACTION_DEAL_UPDATE || trans.type==TRADE_TRANSACTION_POSITION) { UpdatePairTradeStatus(); UpdateAndManageBasket(); } ulong deal_ticket = (ulong)res.deal; if(deal_ticket>0 && HistoryDealSelect(deal_ticket)) { long magic = (long)HistoryDealGetInteger(deal_ticket, DEAL_MAGIC); long entry = (long)HistoryDealGetInteger(deal_ticket, DEAL_ENTRY); if(magic==(long)InpMagicNumber && (entry==DEAL_ENTRY_OUT || entry==DEAL_ENTRY_INOUT)) { double pl = HistoryDealGetDouble(deal_ticket, DEAL_PROFIT); double swap = HistoryDealGetDouble(deal_ticket, DEAL_SWAP); double com = HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION); g_closed_profit_cache += (pl + swap + com); g_closed_count_cache += 1; ObjectSetString(0, g_object_prefix + "PORT_VAL_ProfitC",OBJPROP_TEXT,StringFormat("%+.2f (%d)", g_closed_profit_cache, g_closed_count_cache)); } } SafeRedraw(); }