3
0
Derivar 0
US_rates_4_forex/US_rates_4_forex.mq5
exceltrader 744f7efaef
2025-12-31 15:49:57 +02:00

450 linhas
Sem EOL
16 KiB
MQL5

//+------------------------------------------------------------------+
//| US_Rates_WFO.mq5 |
//| Beta-Driven Strategy with Walk-Forward Optimization |
//| Copyright 2025, https://www.mql5.com/en/users/exceltrader |
//+------------------------------------------------------------------+
#property copyright "exceltrader"
#property link "https://www.mql5.com/en/users/exceltrader"
#property version "1.0"
#include <Trade\Trade.mqh>
#include <Math\Stat\Math.mqh>
//--- Inputs: Asset Proxies
input group "1. Asset Proxies"
input string Bond_Symbol = "TLT"; // Bond ETF (Yield Proxy) - Darwinex: "TLT" Ensure you Pick and US Bond Inde Proxy
//--- Inputs: Strategy Parameters
input group "2. Forecast Logic"
input int Beta_Period = 5; // Default Lookback (Fallback if Opt off)
input double Min_Correlation = 0.5; // Filter: Minimum correlation absolute value
input bool Use_Theory_Filter = false; // Filter: Trade only if Stats match Econ Theory
input int Open_Hour = 2; // Entry Hour (Server Time - usually NY Close)
//--- Inputs: Self-Optimization (WFO)
input group "3. Self-Optimization"
input bool Do_Self_Optimize = true; // Enable Pre-Trade Optimization
input int Opt_Lookback_Days= 40; // Optimization Window (Days to test)
input int Opt_Beta_Start = 10; // Start of Beta Period range
input int Opt_Beta_End = 40; // End of Beta Period range
input int Opt_Beta_Step = 5; // Step size for optimization
//--- Inputs: Risk Management
input group "4. Risk Management"
input double Risk_Percent = 1.0; // Risk per trade (% of Equity)
input int ATR_Period = 20; // ATR Period for Volatility
input double SL_ATR_Mult = 2; // Stop Loss distance in ATRs
input double TP_ATR_Mult = 4.0; // Take Profit distance in ATRs
//--- Inputs: Trade Management
input group "5. Trade Management"
input bool Use_Trailing = true; // Enable Trailing Stop
input double Trail_Trigger_ATR= 2.0; // Start trailing after price moves X ATRs
input double Trail_Step_ATR = 0.5; // Trail Step in ATRs
//--- Global Variables
CTrade Trader;
ulong EA_MAGIC = 775599;
datetime lastTradeDay = 0;
//--- Symbol Enum (Major Liquid Pairs)
enum ENUM_STRATEGY_SYMBOLS {
EURUSD, USDJPY, GBPUSD, AUDUSD, USDCAD, USDCHF
};
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
Trader.SetExpertMagicNumber(EA_MAGIC);
Trader.SetTypeFilling(ORDER_FILLING_IOC); // Crucial for Darwinex Execution
if(!SymbolSelect(Bond_Symbol, true)) {
Print("Error: Symbol '", Bond_Symbol, "' not found in Market Watch.");
return(INIT_FAILED);
}
EventSetTimer(60); // Run every minute
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
datetime currentTime = TimeCurrent();
MqlDateTime dt;
TimeToStruct(currentTime, dt);
// 1. Management: Run Trailing Stop logic constantly
ManageExits();
// 2. Entry: Run once per day at Open_Hour
MqlDateTime lastDt;
TimeToStruct(lastTradeDay, lastDt);
if(dt.hour == Open_Hour && dt.day_of_year != lastDt.day_of_year)
{
lastTradeDay = currentTime;
RunDailyAnalysis();
}
}
//+------------------------------------------------------------------+
//| Main Strategy Logic (With Optimization) |
//+------------------------------------------------------------------+
void RunDailyAnalysis()
{
for(int i = 0; i <= (int)USDCHF; i++)
{
ENUM_STRATEGY_SYMBOLS sym_enum = (ENUM_STRATEGY_SYMBOLS)i;
string symbol = EnumToString(sym_enum);
if(!SymbolSelect(symbol, true)) continue;
// --- DYNAMIC PARAMETER SELECTION ---
int current_beta_period = Beta_Period; // Default
if(Do_Self_Optimize)
{
// Find the best performing period over the last X days
int best_period = GetBestBetaPeriod(symbol, Bond_Symbol);
if(best_period > 0)
{
current_beta_period = best_period;
// Print(symbol, " Optimized Beta Period: ", current_beta_period);
}
}
// -----------------------------------
// A. Get Statistical Metrics
double beta = 0.0;
double correlation = 0.0;
double bond_ret_lag = 0.0;
bool data_ok = CalculateMetrics(symbol, Bond_Symbol, current_beta_period, beta, correlation, bond_ret_lag);
if(!data_ok) continue;
// B. FILTER: Statistical Significance
if(MathAbs(correlation) < Min_Correlation) continue;
// C. FILTER: Theoretical Validity (Econ Theory Check)
if(Use_Theory_Filter) {
if(!IsTheoreticallyValid(symbol, beta)) continue;
}
// D. Forecast
double forecast = beta * bond_ret_lag;
// E. Execution
ExecuteEntry(symbol, forecast, beta);
}
}
//+------------------------------------------------------------------+
//| WFO: Find Best Beta Period based on recent history |
//+------------------------------------------------------------------+
int GetBestBetaPeriod(string sym, string bond)
{
int best_p = -1;
double max_profit = -99999.0;
// 1. Bulk download history
// Needed: Lookback Window + Max Beta Period + Buffers
int req_bars = Opt_Lookback_Days + Opt_Beta_End + 10;
double close_sym[], close_bond[];
// Use Series flag to make index 0 the most recent
if(CopyClose(sym, PERIOD_D1, 1, req_bars, close_sym) < req_bars) return -1;
if(CopyClose(bond, PERIOD_D1, 1, req_bars, close_bond) < req_bars) return -1;
ArraySetAsSeries(close_sym, true);
ArraySetAsSeries(close_bond, true);
// 2. Iterate through parameters
for(int p = Opt_Beta_Start; p <= Opt_Beta_End; p += Opt_Beta_Step)
{
double simulated_profit = 0.0;
// 3. Simulate performance over the Lookback Window
// Loop from 'Opt_Lookback_Days' ago up to yesterday (index 0)
for(int day = Opt_Lookback_Days - 1; day >= 0; day--)
{
// day = 0 is "yesterday" (the most recent completed bar in this history slice)
// We pretend we are at 'day' and need to calculate Beta using data BEFORE 'day'
double mean_y=0, mean_x=0;
double vec_y[], vec_x[];
ArrayResize(vec_y, p);
ArrayResize(vec_x, p);
bool math_error = false;
// Fill vectors for Beta Calculation
for(int k=0; k<p; k++) {
// Index logic:
// Target Day: 'day'
// Data used: 'day + 1' to 'day + p'
int idx_curr = day + 1 + k;
int idx_prev = idx_curr + 1;
if(close_sym[idx_prev] == 0 || close_bond[idx_prev] == 0) { math_error=true; break; }
double ret_y = MathLog(close_sym[idx_curr] / close_sym[idx_prev]);
// Lagged Bond: Bond(t-1)
double ret_x = MathLog(close_bond[idx_curr+1] / close_bond[idx_prev+1]);
vec_y[k] = ret_y;
vec_x[k] = ret_x;
mean_y += ret_y;
mean_x += ret_x;
}
if(math_error) continue;
mean_y /= p;
mean_x /= p;
double cov=0, var_x=0;
for(int k=0; k<p; k++) {
cov += (vec_x[k] - mean_x) * (vec_y[k] - mean_y);
var_x += (vec_x[k] - mean_x) * (vec_x[k] - mean_x);
}
double beta = (var_x > 0.00000001) ? cov/var_x : 0;
// Predict 'day' returns
// Bond move that precedes 'day'
double bond_ret_predict = MathLog(close_bond[day+1] / close_bond[day+2]);
double forecast = beta * bond_ret_predict;
// Actual Result of 'day'
double actual_sym_ret = MathLog(close_sym[day] / close_sym[day+1]);
// Simulate PnL
if(forecast > 0) simulated_profit += actual_sym_ret; // Long
if(forecast < 0) simulated_profit -= actual_sym_ret; // Short
}
// 4. Store Best Result
if(simulated_profit > max_profit) {
max_profit = simulated_profit;
best_p = p;
}
}
return best_p;
}
//+------------------------------------------------------------------+
//| Helper: Check if Stats match Econ Theory |
//+------------------------------------------------------------------+
bool IsTheoreticallyValid(string symbol, double beta)
{
// Theory: TLT Down (Yields Up) -> USD Strong
if(StringFind(symbol, "USD") == 0) { // USDxxx
if(beta < 0) return true;
return false;
}
else { // xxxUSD
if(beta > 0) return true;
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Execute Entry with Risk Sizing |
//+------------------------------------------------------------------+
void ExecuteEntry(string symbol, double forecast, double beta_val)
{
if(PositionSelect(symbol)) return; // One trade per pair
if(forecast == 0.0) return;
bool isBuy = (forecast > 0);
ENUM_ORDER_TYPE type = isBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
double atr = GetATR_Value(symbol);
if(atr <= 0) return;
double current_price = isBuy ? SymbolInfoDouble(symbol, SYMBOL_ASK) : SymbolInfoDouble(symbol, SYMBOL_BID);
// SL/TP Calc
double sl_dist = atr * SL_ATR_Mult;
double tp_dist = atr * TP_ATR_Mult;
double sl = isBuy ? current_price - sl_dist : current_price + sl_dist;
double tp = isBuy ? current_price + tp_dist : current_price - tp_dist;
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
sl = NormalizeDouble(sl, digits);
tp = NormalizeDouble(tp, digits);
// Volume Calc
double volume = CalculateRiskVolume(symbol, sl_dist, Risk_Percent);
if(volume <= 0) return;
string comment = StringFormat("B:%.2f", beta_val);
Trader.PositionOpen(symbol, type, volume, current_price, sl, tp, comment);
}
//+------------------------------------------------------------------+
//| Math: Beta & Correlation Calculation |
//+------------------------------------------------------------------+
bool CalculateMetrics(string sym_y, string sym_x, int period, double &out_beta, double &out_corr, double &out_x_ret)
{
int bars = period + 2;
double close_y[], close_x[];
if(CopyClose(sym_y, PERIOD_D1, 1, bars, close_y) < bars) return false;
if(CopyClose(sym_x, PERIOD_D1, 1, bars, close_x) < bars) return false;
double vec_y[], vec_x_lag[];
ArrayResize(vec_y, period);
ArrayResize(vec_x_lag, period);
// Log Returns: Y(t) vs X(t-1)
for(int i=0; i<period; i++)
{
int idx_curr = bars - 1 - i;
int idx_prev = idx_curr - 1;
int idx_x_curr = bars - 1 - i - 1;
int idx_x_prev = idx_x_curr - 1;
if(close_y[idx_prev] == 0 || close_x[idx_x_prev] == 0) return false;
vec_y[i] = MathLog(close_y[idx_curr] / close_y[idx_prev]);
vec_x_lag[i] = MathLog(close_x[idx_x_curr] / close_x[idx_x_prev]);
}
double mean_y = MathMean(vec_y);
double mean_x = MathMean(vec_x_lag);
double cov = 0, var_x = 0, var_y = 0;
for(int i=0; i<period; i++)
{
double dy = vec_y[i] - mean_y;
double dx = vec_x_lag[i] - mean_x;
cov += dx * dy;
var_x += dx * dx;
var_y += dy * dy;
}
if(var_x == 0 || var_y == 0) return false;
out_beta = cov / var_x;
out_corr = cov / MathSqrt(var_x * var_y);
// Current Predictor: X(t)
out_x_ret = MathLog(close_x[bars-1] / close_x[bars-2]);
return true;
}
//+------------------------------------------------------------------+
//| Logic: Trailing Stop |
//+------------------------------------------------------------------+
void ManageExits()
{
if(!Use_Trailing) return;
for(int i=PositionsTotal()-1; i>=0; i--)
{
if(PositionGetSymbol(i) == Bond_Symbol) continue;
ulong ticket = PositionGetTicket(i);
if(PositionGetInteger(POSITION_MAGIC) != EA_MAGIC) continue;
string symbol = PositionGetSymbol(i);
double atr = GetATR_Value(symbol);
if(atr <= 0) continue;
double sl_price = PositionGetDouble(POSITION_SL);
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
long type = PositionGetInteger(POSITION_TYPE);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
double activation = atr * Trail_Trigger_ATR;
double step = atr * Trail_Step_ATR;
double sl_dist = atr * SL_ATR_Mult;
if(type == POSITION_TYPE_BUY)
{
if(current_price > open_price + activation) {
double new_sl = current_price - sl_dist;
if(new_sl > sl_price + step || sl_price == 0) {
if(new_sl < open_price) new_sl = open_price; // Secure Breakeven
Trader.PositionModify(ticket, NormalizeDouble(new_sl, digits), PositionGetDouble(POSITION_TP));
}
}
}
else if(type == POSITION_TYPE_SELL)
{
if(current_price < open_price - activation) {
double new_sl = current_price + sl_dist;
if(new_sl < sl_price - step || sl_price == 0) {
if(new_sl > open_price) new_sl = open_price;
Trader.PositionModify(ticket, NormalizeDouble(new_sl, digits), PositionGetDouble(POSITION_TP));
}
}
}
}
}
//+------------------------------------------------------------------+
//| Risk: Calculate Lots based on % Equity |
//+------------------------------------------------------------------+
double CalculateRiskVolume(string symbol, double sl_distance_price, double risk_pct)
{
if(sl_distance_price <= 0) return 0.0;
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double risk_amount = equity * (risk_pct / 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 0.0;
double loss_per_lot = (sl_distance_price / tick_size) * tick_value;
if(loss_per_lot == 0) return 0.0;
double lots = risk_amount / loss_per_lot;
double min_vol = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double max_vol = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double step_vol = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
lots = MathFloor(lots / step_vol) * step_vol;
if(lots < min_vol) return 0.0;
if(lots > max_vol) lots = max_vol;
return lots;
}
//+------------------------------------------------------------------+
//| Utility: Get ATR |
//+------------------------------------------------------------------+
double GetATR_Value(string symbol)
{
int handle = iATR(symbol, PERIOD_D1, ATR_Period);
if(handle == INVALID_HANDLE) return 0.0;
double val[1];
if(CopyBuffer(handle, 0, 1, 1, val) < 1) {
IndicatorRelease(handle);
return 0.0;
}
IndicatorRelease(handle);
return val[0];
}