2025-12-31 11:25:54 +02:00
|
|
|
//+------------------------------------------------------------------+
|
2025-12-31 13:04:30 +02:00
|
|
|
//| US_Rates_WFO.mq5 |
|
2025-12-31 12:59:45 +02:00
|
|
|
//| Beta-Driven Strategy with Walk-Forward Optimization |
|
2025-12-31 13:04:30 +02:00
|
|
|
//| Copyright 2025, https://www.mql5.com/en/users/exceltrader |
|
2025-12-31 11:25:54 +02:00
|
|
|
//+------------------------------------------------------------------+
|
2025-12-31 13:04:30 +02:00
|
|
|
#property copyright "exceltrader"
|
|
|
|
|
#property link "https://www.mql5.com/en/users/exceltrader"
|
2025-12-31 15:46:28 +02:00
|
|
|
#property version "1.0"
|
2025-12-31 12:59:45 +02:00
|
|
|
|
|
|
|
|
#include <Trade\Trade.mqh>
|
|
|
|
|
#include <Math\Stat\Math.mqh>
|
|
|
|
|
|
|
|
|
|
//--- Inputs: Asset Proxies
|
|
|
|
|
input group "1. Asset Proxies"
|
2025-12-31 15:49:57 +02:00
|
|
|
input string Bond_Symbol = "TLT"; // Bond ETF (Yield Proxy) - Darwinex: "TLT" Ensure you Pick and US Bond Inde Proxy
|
2025-12-31 12:59:45 +02:00
|
|
|
|
|
|
|
|
//--- 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
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-31 11:25:54 +02:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Expert initialization function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int OnInit()
|
2025-12-31 12:59:45 +02:00
|
|
|
{
|
|
|
|
|
Trader.SetExpertMagicNumber(EA_MAGIC);
|
|
|
|
|
Trader.SetTypeFilling(ORDER_FILLING_IOC); // Crucial for Darwinex Execution
|
2025-12-31 11:25:54 +02:00
|
|
|
|
2025-12-31 12:59:45 +02:00
|
|
|
if(!SymbolSelect(Bond_Symbol, true)) {
|
|
|
|
|
Print("Error: Symbol '", Bond_Symbol, "' not found in Market Watch.");
|
|
|
|
|
return(INIT_FAILED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventSetTimer(60); // Run every minute
|
2025-12-31 11:25:54 +02:00
|
|
|
return(INIT_SUCCEEDED);
|
2025-12-31 12:59:45 +02:00
|
|
|
}
|
|
|
|
|
|
2025-12-31 11:25:54 +02:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Expert deinitialization function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnDeinit(const int reason)
|
2025-12-31 12:59:45 +02:00
|
|
|
{
|
2025-12-31 11:25:54 +02:00
|
|
|
EventKillTimer();
|
2025-12-31 12:59:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Timer function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnTimer()
|
|
|
|
|
{
|
|
|
|
|
datetime currentTime = TimeCurrent();
|
|
|
|
|
MqlDateTime dt;
|
|
|
|
|
TimeToStruct(currentTime, dt);
|
2025-12-31 11:25:54 +02:00
|
|
|
|
2025-12-31 12:59:45 +02:00
|
|
|
// 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) |
|
2025-12-31 11:25:54 +02:00
|
|
|
//+------------------------------------------------------------------+
|
2025-12-31 12:59:45 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-31 11:25:54 +02:00
|
|
|
//+------------------------------------------------------------------+
|
2025-12-31 12:59:45 +02:00
|
|
|
//| 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;
|
2025-12-31 11:25:54 +02:00
|
|
|
|
2025-12-31 12:59:45 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-31 11:25:54 +02:00
|
|
|
//+------------------------------------------------------------------+
|
2025-12-31 12:59:45 +02:00
|
|
|
//| Helper: Check if Stats match Econ Theory |
|
2025-12-31 11:25:54 +02:00
|
|
|
//+------------------------------------------------------------------+
|
2025-12-31 12:59:45 +02:00
|
|
|
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);
|
2025-12-31 11:25:54 +02:00
|
|
|
|
2025-12-31 12:59:45 +02:00
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-31 11:25:54 +02:00
|
|
|
//+------------------------------------------------------------------+
|
2025-12-31 12:59:45 +02:00
|
|
|
//| 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];
|
|
|
|
|
}
|