//+------------------------------------------------------------------+ //| CAPM_Forex.mq5 | //| Copyright 2025, Your Name Here | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Your Name Here" #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_buffers 2 #property indicator_plots 2 //--- plot Expected Return #property indicator_label1 "Expected Return" #property indicator_type1 DRAW_LINE #property indicator_color1 clrBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 2 //--- plot Risk Premium #property indicator_label2 "Risk Premium" #property indicator_type2 DRAW_LINE #property indicator_color2 clrRed #property indicator_style2 STYLE_DASH #property indicator_width2 1 //--- input parameters input int InpPeriod = 20; // Calculation period input double InpRiskFreeRate = 0.05; // Risk-free rate (annual) input double InpRiskPremium = 0.02; // Currency pair risk premium input bool InpShowInfo = true; // Show the info panel //--- indicator buffers double ExpectedReturnBuffer[]; double RiskPremiumBuffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0, ExpectedReturnBuffer, INDICATOR_DATA); SetIndexBuffer(1, RiskPremiumBuffer, INDICATOR_DATA); //--- setting labels PlotIndexSetString(0, PLOT_LABEL, "Expected Return"); PlotIndexSetString(1, PLOT_LABEL, "Risk Premium"); //--- setting the format of accuracy IndicatorSetInteger(INDICATOR_DIGITS, 4); //--- name for indicator subwindow label IndicatorSetString(INDICATOR_SHORTNAME, "CAPM Forex Model"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- check for data sufficiency if(rates_total < InpPeriod + 1) return(0); //--- define the initial position for calculation int start = MathMax(prev_calculated - 1, InpPeriod); if(start == 0) start = InpPeriod; //--- main calculation loop for(int i = start; i < rates_total; i++) { //--- check the array boundaries if(i - InpPeriod < 0) { ExpectedReturnBuffer[i] = EMPTY_VALUE; RiskPremiumBuffer[i] = EMPTY_VALUE; continue; } //--- calculate volatility for the period double returns_sum = 0.0; double returns_sq_sum = 0.0; int valid_count = 0; for(int j = 1; j <= InpPeriod; j++) { int idx1 = i - j + 1; int idx2 = i - j; //--- check the boundaries if(idx1 >= 0 && idx2 >= 0 && idx1 < rates_total && idx2 < rates_total) { if(close[idx2] > 0) { double daily_return = (close[idx1] - close[idx2]) / close[idx2]; returns_sum += daily_return; returns_sq_sum += daily_return * daily_return; valid_count++; } } } if(valid_count < InpPeriod / 2) // at least half of the data should be valid { ExpectedReturnBuffer[i] = EMPTY_VALUE; RiskPremiumBuffer[i] = EMPTY_VALUE; continue; } //--- calculate the average return and volatility double mean_return = returns_sum / valid_count; double variance = (returns_sq_sum / valid_count) - (mean_return * mean_return); double volatility = MathSqrt(MathMax(variance, 0.0)); //--- annualize the indicators (252 trading days) double annual_volatility = volatility * MathSqrt(252.0); double annual_return = mean_return * 252.0; //--- calculate the risk premium based on volatility double dynamic_risk_premium = InpRiskPremium * (annual_volatility / 0.15); // normalize to 15% of volatility RiskPremiumBuffer[i] = dynamic_risk_premium; //--- calculate the expected return double expected_return = InpRiskFreeRate + dynamic_risk_premium; ExpectedReturnBuffer[i] = expected_return; } //--- show the info panel if(InpShowInfo && rates_total > InpPeriod) { ShowInfoPanel(rates_total - 1); } return(rates_total); } //+------------------------------------------------------------------+ //| Show Information Panel | //+------------------------------------------------------------------+ void ShowInfoPanel(int current_bar) { if(current_bar < 0 || current_bar >= ArraySize(ExpectedReturnBuffer)) return; string info = ""; info += "=== CAPM Forex Model ===\n"; info += StringFormat("Symbol: %s\n", Symbol()); info += StringFormat("Period: %d bars\n", InpPeriod); info += StringFormat("Risk-Free Rate: %.2f%%\n", InpRiskFreeRate * 100); info += "========================\n"; if(ExpectedReturnBuffer[current_bar] != EMPTY_VALUE) { info += StringFormat("Risk Premium: %.2f%%\n", RiskPremiumBuffer[current_bar] * 100); info += StringFormat("Expected Return: %.2f%%\n", ExpectedReturnBuffer[current_bar] * 100); //--- calculate the current volatility for reference double current_volatility = CalculateCurrentVolatility(current_bar); if(current_volatility > 0) { info += StringFormat("Current Volatility: %.2f%%\n", current_volatility * 100); if(current_volatility > 0.20) info += "Status: HIGH RISK\n"; else if(current_volatility > 0.10) info += "Status: MEDIUM RISK\n"; else info += "Status: LOW RISK\n"; } //--- compare with risk-free rate double excess_return = ExpectedReturnBuffer[current_bar] - InpRiskFreeRate; info += StringFormat("Excess Return: %.2f%%\n", excess_return * 100); } else { info += "Insufficient data for calculation\n"; } Comment(info); } //+------------------------------------------------------------------+ //| Calculate Current Volatility | //+------------------------------------------------------------------+ double CalculateCurrentVolatility(int current_bar) { if(current_bar < InpPeriod) return 0.0; double returns_sum = 0.0; double returns_sq_sum = 0.0; int valid_count = 0; // Obtain close price data for calculation double prices[]; int copied = CopyClose(Symbol(), PERIOD_CURRENT, current_bar - InpPeriod, InpPeriod + 1, prices); if(copied <= InpPeriod) return 0.0; // Calculate returns for(int j = 1; j < ArraySize(prices); j++) { if(prices[j-1] > 0) { double daily_return = (prices[j] - prices[j-1]) / prices[j-1]; returns_sum += daily_return; returns_sq_sum += daily_return * daily_return; valid_count++; } } if(valid_count < 2) return 0.0; double mean_return = returns_sum / valid_count; double variance = (returns_sq_sum / valid_count) - (mean_return * mean_return); double volatility = MathSqrt(MathMax(variance, 0.0)); return volatility * MathSqrt(252.0); // annualized volatility } //+------------------------------------------------------------------+ //| Deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Comment(""); } //+------------------------------------------------------------------+