Last active
August 21, 2025 08:47
-
-
Save tomas-rampas/afc73134101e159b43354d83ad8b38cb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //+------------------------------------------------------------------+ | |
| //| ATR_Position_Sizing.mq5 | | |
| //| Equal Risk Allocation Based on ATR | | |
| //+------------------------------------------------------------------+ | |
| #property copyright "2025" | |
| #property version "1.02" | |
| #property indicator_chart_window | |
| #property indicator_plots 0 | |
| // Input parameters | |
| input double InpCapitalThousands = 30.0; // Capital in thousands (e.g., 30 = $30,000) | |
| input int InpATRPeriod = 13; // ATR Period | |
| input color InpTableColor = clrWhite; // Table Text Color | |
| input color InpHeaderColor = clrYellow; // Table Header Color | |
| input color InpBgColor = clrBlack; // Table Background Color | |
| input int InpFontSize = 10; // Font Size | |
| input int InpXOffset = 20; // X Position from left | |
| input int InpYOffset = 50; // Y Position from top | |
| // Asset structure | |
| struct AssetData | |
| { | |
| string symbol; | |
| double price; | |
| double atr; | |
| double atr_usd; | |
| double atr_percent; | |
| double weight; | |
| double allocation; | |
| double units; | |
| double risk_value; | |
| }; | |
| // Global variables | |
| AssetData assets[3]; | |
| int atr_handles[3]; | |
| double total_capital; | |
| string display_text; | |
| //+------------------------------------------------------------------+ | |
| //| Custom indicator initialization function | | |
| //+------------------------------------------------------------------+ | |
| int OnInit() | |
| { | |
| // Initialize capital | |
| total_capital = InpCapitalThousands * 1000.0; | |
| // Define assets | |
| assets[0].symbol = "EURUSD"; | |
| assets[1].symbol = "GDAXI"; // DAX index | |
| assets[2].symbol = "SP500"; // S&P 500 | |
| // Initialize ATR handles for each symbol | |
| for(int i = 0; i < 3; i++) | |
| { | |
| atr_handles[i] = iATR(assets[i].symbol, PERIOD_CURRENT, InpATRPeriod); | |
| if(atr_handles[i] == INVALID_HANDLE) | |
| { | |
| PrintFormat("Failed to create ATR handle for %s", assets[i].symbol); | |
| return(INIT_FAILED); | |
| } | |
| } | |
| // Set timer for regular updates (every 1 second) | |
| EventSetTimer(1); | |
| return(INIT_SUCCEEDED); | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Custom indicator deinitialization function | | |
| //+------------------------------------------------------------------+ | |
| void OnDeinit(const int reason) | |
| { | |
| // Clean up ATR handles | |
| for(int i = 0; i < 3; i++) | |
| { | |
| if(atr_handles[i] != INVALID_HANDLE) | |
| IndicatorRelease(atr_handles[i]); | |
| } | |
| // Remove timer | |
| EventKillTimer(); | |
| // Clear display | |
| Comment(""); | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| 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[]) | |
| { | |
| CalculatePositions(); | |
| DisplayTable(); | |
| return(rates_total); | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Timer function | | |
| //+------------------------------------------------------------------+ | |
| void OnTimer() | |
| { | |
| CalculatePositions(); | |
| DisplayTable(); | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Calculate position sizes | | |
| //+------------------------------------------------------------------+ | |
| void CalculatePositions() | |
| { | |
| double eurusd_rate = 0; | |
| // Get current EURUSD rate for conversion | |
| if(SymbolInfoDouble("EURUSD", SYMBOL_BID, eurusd_rate)) | |
| { | |
| if(eurusd_rate == 0) eurusd_rate = 1.1641; // Fallback value | |
| } | |
| else | |
| { | |
| eurusd_rate = 1.1641; // Use provided value as fallback | |
| } | |
| // Get prices and ATR values for each asset | |
| for(int i = 0; i < 3; i++) | |
| { | |
| // Get current price | |
| double bid = SymbolInfoDouble(assets[i].symbol, SYMBOL_BID); | |
| if(bid > 0) | |
| assets[i].price = bid; | |
| else | |
| { | |
| // Use fallback values if symbols not available | |
| if(assets[i].symbol == "EURUSD") | |
| assets[i].price = 1.1641; | |
| else if(assets[i].symbol == "GDAXI") | |
| assets[i].price = 24270.6; | |
| else if(assets[i].symbol == "SP500") | |
| assets[i].price = 6396.7; | |
| } | |
| // Get ATR value | |
| double atr_buffer[]; | |
| ArraySetAsSeries(atr_buffer, true); | |
| if(CopyBuffer(atr_handles[i], 0, 0, 1, atr_buffer) > 0) | |
| { | |
| assets[i].atr = atr_buffer[0]; | |
| } | |
| else | |
| { | |
| // Use fallback ATR values | |
| if(assets[i].symbol == "EURUSD") | |
| assets[i].atr = 0.00048; | |
| else if(assets[i].symbol == "GDAXI") | |
| assets[i].atr = 19.4; | |
| else if(assets[i].symbol == "SP500") | |
| assets[i].atr = 2.5; | |
| } | |
| // Convert to USD where necessary | |
| if(assets[i].symbol == "GDAXI") | |
| { | |
| // DAX is in EUR, convert to USD | |
| assets[i].atr_usd = assets[i].atr * eurusd_rate; | |
| assets[i].price = assets[i].price * eurusd_rate; // Convert price to USD for calculations | |
| } | |
| else | |
| { | |
| assets[i].atr_usd = assets[i].atr; | |
| } | |
| // Calculate ATR as percentage of price | |
| if(assets[i].price > 0) | |
| assets[i].atr_percent = (assets[i].atr_usd / assets[i].price) * 100.0; | |
| else | |
| assets[i].atr_percent = 0; | |
| } | |
| // Calculate weights for equal risk allocation (inverse volatility) | |
| double total_weight = 0; | |
| for(int i = 0; i < 3; i++) | |
| { | |
| if(assets[i].atr_percent > 0) | |
| assets[i].weight = 1.0 / (assets[i].atr_percent / 100.0); | |
| else | |
| assets[i].weight = 0; | |
| total_weight += assets[i].weight; | |
| } | |
| // Calculate allocations and positions | |
| for(int i = 0; i < 3; i++) | |
| { | |
| if(total_weight > 0) | |
| { | |
| // Calculate allocation percentage and dollar amount | |
| double allocation_percent = assets[i].weight / total_weight; | |
| assets[i].allocation = total_capital * allocation_percent; | |
| // Calculate units to buy | |
| if(assets[i].price > 0) | |
| { | |
| if(assets[i].symbol == "GDAXI") | |
| { | |
| // For DAX, we need to convert back to EUR price for unit calculation | |
| double dax_eur_price = assets[i].price / eurusd_rate; | |
| assets[i].units = assets[i].allocation / assets[i].price; | |
| } | |
| else | |
| { | |
| assets[i].units = assets[i].allocation / assets[i].price; | |
| } | |
| } | |
| else | |
| assets[i].units = 0; | |
| // Calculate risk value (allocation * ATR%) | |
| assets[i].risk_value = assets[i].allocation * (assets[i].atr_percent / 100.0); | |
| } | |
| } | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Pad string with underscores for consistent width | | |
| //+------------------------------------------------------------------+ | |
| string PadString(string value, int targetWidth, bool padLeft = false) | |
| { | |
| int len = StringLen(value); | |
| if(len >= targetWidth) | |
| return StringSubstr(value, 0, targetWidth); | |
| string result = value; | |
| for(int i = len; i < targetWidth; i++) | |
| { | |
| if(padLeft) | |
| result = "_" + result; | |
| else | |
| result = result + "_"; | |
| } | |
| return result; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Display table with results | | |
| //+------------------------------------------------------------------+ | |
| void DisplayTable() | |
| { | |
| string text = ""; | |
| string separator = "================================================================================\n"; | |
| string separator2 = "--------------------------------------------------------------------------------\n"; | |
| // Title | |
| text += separator; | |
| text += StringFormat("ATR_POSITION_SIZING_-_CAPITAL:_$%,.0f\n", total_capital); | |
| text += separator; | |
| text += "\n"; | |
| // Create header row using tabs | |
| text += "Symbol____Price_________ATR__________ATR%______Allocation____Units_________Risk____\n"; | |
| text += separator2; | |
| // Data rows | |
| for(int i = 0; i < 3; i++) | |
| { | |
| string symbol_col, price_col, atr_col, atr_pct_col, alloc_col, units_col, risk_col; | |
| // Format symbol column (fixed width 10) | |
| symbol_col = PadString(assets[i].symbol, 10); | |
| // Format price column (fixed width 14) | |
| if(assets[i].symbol == "EURUSD") | |
| { | |
| price_col = PadString(StringFormat("$%.5f", assets[i].price), 14); | |
| } | |
| else if(assets[i].symbol == "GDAXI") | |
| { | |
| double dax_eur_price = assets[i].price / 1.1641; | |
| price_col = PadString(StringFormat("EUR%.1f", dax_eur_price), 14); | |
| } | |
| else // SP500 | |
| { | |
| price_col = PadString(StringFormat("$%.1f", assets[i].price), 14); | |
| } | |
| // Format ATR column (fixed width 13) | |
| if(assets[i].symbol == "EURUSD") | |
| { | |
| atr_col = PadString(StringFormat("%.6f", assets[i].atr), 13); | |
| } | |
| else | |
| { | |
| atr_col = PadString(StringFormat("%.2f", assets[i].atr), 13); | |
| } | |
| // Format ATR% column (fixed width 10) | |
| atr_pct_col = PadString(StringFormat("%.4f%%", assets[i].atr_percent), 10); | |
| // Format Allocation column (fixed width 14) | |
| alloc_col = PadString(StringFormat("$%.2f", assets[i].allocation), 14); | |
| // Format Units column (fixed width 14) | |
| if(assets[i].symbol == "EURUSD") | |
| { | |
| units_col = PadString(StringFormat("%.2f", assets[i].units), 14); | |
| } | |
| else | |
| { | |
| units_col = PadString(StringFormat("%.4f", assets[i].units), 14); | |
| } | |
| // Format Risk column (fixed width 12) | |
| risk_col = PadString(StringFormat("$%.2f", assets[i].risk_value), 12); | |
| // Combine all columns | |
| text += symbol_col + price_col + atr_col + atr_pct_col + alloc_col + units_col + risk_col + "\n"; | |
| } | |
| text += separator2; | |
| text += "\n"; | |
| // Allocation percentages | |
| text += "ALLOCATION_PERCENTAGES:\n"; | |
| text += separator2; | |
| double total_weight = 0; | |
| for(int i = 0; i < 3; i++) | |
| total_weight += assets[i].weight; | |
| for(int i = 0; i < 3; i++) | |
| { | |
| double percent = (total_weight > 0) ? (assets[i].weight / total_weight * 100.0) : 0; | |
| string symbol_name = PadString(assets[i].symbol + ":", 10); | |
| string percent_str = StringFormat("%.2f%%", percent); | |
| text += symbol_name + percent_str + "\n"; | |
| } | |
| text += "\n"; | |
| text += separator2; | |
| // Summary statistics | |
| double total_risk = 0; | |
| double total_alloc = 0; | |
| for(int i = 0; i < 3; i++) | |
| { | |
| total_risk += assets[i].risk_value; | |
| total_alloc += assets[i].allocation; | |
| } | |
| text += "SUMMARY_STATISTICS:\n"; | |
| text += separator2; | |
| string label1 = PadString("Total_Allocated:", 25); | |
| text += label1 + StringFormat("$%.2f\n", total_alloc); | |
| string label2 = PadString("Average_Risk_per_Position:", 25); | |
| text += label2 + StringFormat("$%.2f\n", total_risk/3.0); | |
| string label3 = PadString("Total_Portfolio_Risk:", 25); | |
| text += label3 + StringFormat("$%.2f\n", total_risk); | |
| text += separator; | |
| // Add timestamp | |
| text += "Last_Update:_" + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + "\n"; | |
| // Display the formatted table | |
| Comment(text); | |
| } | |
| //+------------------------------------------------------------------+ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment