//+------------------------------------------------------------------+ //| pulseBalaceIndicator.mq5| //| Copyright 2025, MQL Development | //| https://www.mqldevelopment.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MQL Development" #property link "https://www.mqldevelopment.com/" #property version "1.1" #property indicator_separate_window #property indicator_buffers 2 #property indicator_plots 2 #property indicator_label1 "ACCOUNT BALANCE" #property indicator_type1 DRAW_LINE #property indicator_color1 clrRed #property indicator_width1 1 #property indicator_label2 "MA" #property indicator_type2 DRAW_LINE #property indicator_color2 clrDodgerBlue #property indicator_width2 1 #property indicator_style2 STYLE_DOT input color Dot_Color = clrOrangeRed; input double StartingBalance = 10000; input string MagicNumbers = "123"; input int MA_Period = 5; input ENUM_MA_METHOD MA_Method = MODE_SMA; input string fileName = "TradeDataFile"; // File Name input int historyTrades = 50; double ACCOUNTBALANCE[]; double MA[]; int TOTAL_HISTORY = 0; datetime TT = 0; int MagicArray[]; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ string filename = fileName + Symbol() + ".csv"; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping parseMagicNumbers(); SetIndexBuffer(0, ACCOUNTBALANCE, INDICATOR_DATA); SetIndexBuffer(1, MA, INDICATOR_DATA); ArraySetAsSeries(ACCOUNTBALANCE, true); ArraySetAsSeries(MA, true); TOTAL_HISTORY = 0; TT = 0; //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- destroy timer subDeleteObjects("Balance: "); subDeleteObjects("Start Balance: "); } //+------------------------------------------------------------------+ //| 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[]) { //--- MyStart(rates_total, prev_calculated); //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void MyStart(int ratesTotal, int prevCalculated) { if(TOTAL_HISTORY != orderCount() || TT != iTime(Symbol(), PERIOD_CURRENT, 0)) { int limit = 0; if(prevCalculated == 0) { limit = ratesTotal - prevCalculated -2; } else { limit = ratesTotal - prevCalculated; } for(int i=1000; i>= 0; i--) { ACCOUNTBALANCE[i] = EMPTY_VALUE; MA[i] = EMPTY_VALUE; DeleteStartBalanceObjects(); } int total = 0; double AB = StartingBalance; double BAL = AB; Print("ACCOUNT BALANCE: " + DoubleToString(AB)); total = orderCount(); int totalOrderCount = orderCount(); // Print(" Total Order Count = ",totalOrderCount); int startIndexToConsiderTrade = totalOrderCount - historyTrades; int totalLoop = historyTrades; total = historyTrades; if(startIndexToConsiderTrade < 0) { startIndexToConsiderTrade = 1; total = totalOrderCount; totalLoop = totalOrderCount; } int handle = FileOpen(filename, FILE_READ | FILE_COMMON); // Print("startIndexToConsiderTrade: ",startIndexToConsiderTrade); if(handle == INVALID_HANDLE) { Print("File not found: ", filename); return; } while(!FileIsEnding(handle)) { string line = FileReadString(handle); // Skip empty line if(StringLen(line) < 2) continue; string parts[]; int count = StringSplit(line, ',', parts); int ticket = (int)StringToInteger(parts[0]); int type = (int)StringToInteger(parts[1]); int magic = (int)StringToInteger(parts[2]); double open_price = StringToDouble(parts[3]); double close_price = StringToDouble(parts[4]); datetime open_time = StringToTime(parts[5]); datetime close_time = StringToTime(parts[6]); double sl = StringToDouble(parts[7]); double tp = StringToDouble(parts[8]); double lot = StringToDouble(parts[9]); double profit = StringToDouble(parts[10]); if(isMagicMatch(magic) && ticket >= startIndexToConsiderTrade && total > 0) { // Print(" startIndexToConsiderTrade ",startIndexToConsiderTrade," ticket = ",ticket," total + 1: ",total + 1," totalOrderCount ",totalOrderCount," historyTrades = ",historyTrades); double deal_profit = profit; ACCOUNTBALANCE[total + 1] = BAL; string name = "Start Balance: " + DoubleToString(ACCOUNTBALANCE[total + 1], 2) + "\n" + TimeToString(close_time, TIME_DATE|TIME_SECONDS) + "\n"; CreateDotLabel(name, "=", iTime(Symbol(), PERIOD_CURRENT, total + 1), ACCOUNTBALANCE[total + 1], Dot_Color); BAL = BAL + profit; ACCOUNTBALANCE[total] = BAL; GlobalVariableSet("TIME:" + IntegerToString(total), close_time); total--; } } FileClose(handle); // Calculate moving averages and create labels for(int x = totalLoop; x > 0/*startIndexToConsiderTrade*/; x--) { if(x < /*orderCount()*/totalLoop - MA_Period) { MA[x] = iMAOnArray(ACCOUNTBALANCE, 0, MA_Period, 0, MA_Method, x); datetime time_val = (datetime)GlobalVariableGet("TIME:" + IntegerToString(x)); string name = "Balance: " + DoubleToString(ACCOUNTBALANCE[x], 2) + "\n" + TimeToString(time_val, TIME_DATE|TIME_SECONDS) + "\n" + DoubleToString(MA[x], 2); //CreateDotLabel(name, "=", iTime(Symbol(), PERIOD_CURRENT, x), ACCOUNTBALANCE[x], Dot_Color); } else { datetime time_val = (datetime)GlobalVariableGet("TIME:" + IntegerToString(x)); string name = "Balance: " + DoubleToString(ACCOUNTBALANCE[x], 2) + "\n" + TimeToString(time_val, TIME_DATE|TIME_SECONDS); //CreateDotLabel(name, "=", iTime(Symbol(), PERIOD_CURRENT, x), ACCOUNTBALANCE[x], Dot_Color); } } TOTAL_HISTORY = orderCount(); TT = iTime(Symbol(), PERIOD_CURRENT, 0); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CreateDotLabel(string name, string text, datetime T1, double P1, color C) { int window_x = ChartWindowFind(); if(ObjectFind(0, name) == -1) { ObjectCreate(0, name, OBJ_TEXT, window_x, T1, NormalizeDouble(P1, Digits())); } ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_CENTER); ObjectSetString(0, name, OBJPROP_TEXT, text); ObjectSetString(0, name, OBJPROP_FONT, "webdings"); ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8); ObjectSetInteger(0, name, OBJPROP_COLOR, C); ObjectSetDouble(0, name, OBJPROP_PRICE, NormalizeDouble(P1, Digits())); ObjectSetInteger(0, name, OBJPROP_TIME, T1); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void subDeleteObjects(string ObjName) { int obj_total = ObjectsTotal(0); string name; int i = 0; for(i = obj_total - 1; i >= 0; i--) { name = ObjectName(0, i); if(StringFind(name, ObjName, 0) != -1) { ObjectDelete(0, name); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double iMAOnArray(double &array[], int total, int period, int ma_shift, int ma_method, int shift) { double buf[],arr[]; if(total==0) total=ArraySize(array); if(total>0 && total<=period) return(0); if(shift>total-period-ma_shift) return(0); switch(ma_method) { case MODE_SMA : { total=ArrayCopy(arr,array,0,shift+ma_shift,period); if(ArrayResize(buf,total)<0) return(0); double sum=0; int i,pos=total-1; for(i=1;i=0) { sum+=arr[pos]; buf[pos]=sum/period; sum-=arr[pos+period-1]; pos--; } return(buf[0]); } case MODE_EMA : { if(ArrayResize(buf,total)<0) return(0); double pr=2.0/(period+1); int pos=total-2; while(pos>=0) { if(pos==total-2) { buf[pos+1]=array[pos+1]; } buf[pos]=array[pos]*pr+buf[pos+1]*(1-pr); pos--; } return(buf[shift+ma_shift]); } case MODE_SMMA : { if(ArrayResize(buf,total)<0) return(0); double sum=0; int i,k,pos; pos=total-period; while(pos>=0) { if(pos==total-period) { for(i=0,k=pos;i=0) { buf[pos]=sum/weight; if(pos==0) break; pos--; i--; price=array[pos]; sum=sum-lsum+price*period; lsum-=array[i]; lsum+=price; } return(buf[shift+ma_shift]); } default: return(0); } return(0); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void parseMagicNumbers() { string magicList[]; int count = StringSplit(MagicNumbers, ',', magicList); //Print("Magic Count: ", count); if(count > 0) { ArrayResize(MagicArray, count); for(int i = 0; i < count; i++) { string cleanMagic = trimString(magicList[i]); MagicArray[i] = (int)StringToInteger(cleanMagic); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool isMagicMatch(long deal_magic) { // If "0" Magic Num, process all trades if(MagicNumbers == "0") { return true; } for(int i = 0; i < ArraySize(MagicArray); i++) { if(deal_magic == MagicArray[i]) return true; } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ string trimString(string inputt) { // Remove spaces from the left and right sides int start = 0; int end = StringLen(inputt) - 1; // Find the first non-space character while(start <= end && StringGetCharacter(inputt, start) == ' ') start++; // Find the last non-space character while(end >= start && StringGetCharacter(inputt, end) == ' ') end--; // Extract the substring without leading or trailing spaces return StringSubstr(inputt, start, end - start + 1); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void ReadDataFromFile() { int handle = FileOpen(filename, FILE_READ | FILE_COMMON); if(handle == INVALID_HANDLE) { Print("File not found: ", filename); return; } while(!FileIsEnding(handle)) { string line = FileReadString(handle); // Skip empty line if(StringLen(line) < 2) continue; string parts[]; int count = StringSplit(line, ',', parts); int ticket = (int)StringToInteger(parts[0]); int type = (int)StringToInteger(parts[1]); int magic = (int)StringToInteger(parts[2]); double open_price = StringToDouble(parts[3]); double close_price = StringToDouble(parts[4]); datetime open_time = StringToTime(parts[5]); datetime close_time = StringToTime(parts[6]); double sl = StringToDouble(parts[7]); double tp = StringToDouble(parts[8]); double lot = StringToDouble(parts[9]); double profit = StringToDouble(parts[10]); // Print or use values Print("READ: ticket=", ticket, " type=", type, " magic=", magic, " open=", open_price, " close=", close_price, " open_time=", open_time, " close_time=", close_time, " sl=", sl, " tp=", tp, " lot=", lot, " profit=", profit); } FileClose(handle); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int orderCount() { int handle = FileOpen(filename, FILE_READ | FILE_COMMON); if(handle == INVALID_HANDLE) { Print("File not found: ", filename); return 0; } int index = 0; while(!FileIsEnding(handle)) { string line = FileReadString(handle); // Skip empty line if(StringLen(line) < 2) continue; string parts[]; int count = StringSplit(line, ',', parts); int ticket = (int)StringToInteger(parts[0]); int type = (int)StringToInteger(parts[1]); int magic = (int)StringToInteger(parts[2]); double open_price = StringToDouble(parts[3]); double close_price = StringToDouble(parts[4]); datetime open_time = StringToTime(parts[5]); datetime close_time = StringToTime(parts[6]); double sl = StringToDouble(parts[7]); double tp = StringToDouble(parts[8]); double lot = StringToDouble(parts[9]); double profit = StringToDouble(parts[10]); index++; } FileClose(handle); return index; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void DeleteStartBalanceObjects() { int total = ObjectsTotal(0); // Loop from last to first (important for deletion) for(int i = total - 1; i >= 0; i--) { string objName = ObjectName(0, i); // Check if object name starts with "Start Balance:" if(StringFind(objName,"Start Balance:") > 0 || StringFind(objName,"Balance:") > 0) { ObjectDelete(0, objName); } } } //+------------------------------------------------------------------+