//+------------------------------------------------------------------+ //| 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.00" #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 3 #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 #property indicator_label3 "MA2" #property indicator_type3 DRAW_LINE #property indicator_color3 clrGold #property indicator_width3 1 #property indicator_style3 STYLE_DASH input color Dot_Color = clrOrangeRed; input double StartingBalance = 10000; input int MagicNumber = 0; input int MA_Period = 5; input int MA2_Period = 12; input ENUM_MA_METHOD MA_Method = MODE_SMA; double ACCOUNTBALANCE[]; double MA[]; double MA2[]; int TOTAL_HISTORY = 0; datetime TT = 0; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping EventSetTimer(1); SetIndexBuffer(0, ACCOUNTBALANCE, INDICATOR_DATA); SetIndexBuffer(1, MA, INDICATOR_DATA); SetIndexBuffer(2, MA2, INDICATOR_DATA); ArraySetAsSeries(ACCOUNTBALANCE, true); ArraySetAsSeries(MA, true); ArraySetAsSeries(MA2, true); TOTAL_HISTORY = 0; TT = 0; //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- destroy timer EventKillTimer(); 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); } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //MyStart(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void MyStart(int ratesTotal, int prevCalculated) { if(TOTAL_HISTORY != HistoryDealsTotal() || TT != iTime(Symbol(), PERIOD_CURRENT, 0)) { int limit = 0; if(prevCalculated == 0) { limit = ratesTotal - prevCalculated -2; } else { limit = ratesTotal - prevCalculated; } //subDeleteObjects("Balance: "); //subDeleteObjects("Start Balance: "); for(int i=limit; i>= 0; i--) { ACCOUNTBALANCE[i] = EMPTY_VALUE; MA[i] = EMPTY_VALUE; MA2[i] = EMPTY_VALUE; } if(!HistorySelect(0, TimeCurrent())) return; int deals = HistoryDealsTotal(); int total = 0; int total2 = 0; int ticket = 0; double AB = StartingBalance; double BAL = AB; Print("ACCOUNT BALANCE: " + DoubleToString(AB)); // First pass to count relevant deals for(int i = 0; i < deals; i++) { ulong deal_ticket = HistoryDealGetTicket(i); if(deal_ticket == 0) { Print("Error getting deal ticket!"); break; } long deal_magic = HistoryDealGetInteger(deal_ticket, DEAL_MAGIC); long deal_type = HistoryDealGetInteger(deal_ticket, DEAL_TYPE); // Filter for position deals (open/close) with matching magic number if(HistoryDealGetInteger(deal_ticket, DEAL_ENTRY) == DEAL_ENTRY_OUT) { if((deal_type == DEAL_TYPE_BUY || deal_type == DEAL_TYPE_SELL) && deal_magic == MagicNumber) { total2++; } } } total = total2; // Second pass to calculate balance for(int i = 0; i < deals; i++) { ulong deal_ticket = HistoryDealGetTicket(i); if(deal_ticket == 0) { Print("Error getting deal ticket!"); break; } long deal_magic = HistoryDealGetInteger(deal_ticket, DEAL_MAGIC); long deal_type = HistoryDealGetInteger(deal_ticket, DEAL_TYPE); datetime deal_time = (datetime)HistoryDealGetInteger(deal_ticket, DEAL_TIME); //dealout check add if(HistoryDealGetInteger(deal_ticket, DEAL_ENTRY) == DEAL_ENTRY_OUT) { if((deal_type == DEAL_TYPE_BUY || deal_type == DEAL_TYPE_SELL) && deal_magic == MagicNumber) { double deal_profit = HistoryDealGetDouble(deal_ticket, DEAL_PROFIT); double deal_swap = HistoryDealGetDouble(deal_ticket, DEAL_SWAP); double deal_commission = HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION); double profit = deal_profit + deal_swap + deal_commission; if(total == total2) { ACCOUNTBALANCE[total + 1] = BAL; string name = "Start Balance: " + DoubleToString(ACCOUNTBALANCE[total + 1], 2) + "\n" + TimeToString(deal_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), deal_time); total--; } } } // Calculate moving averages and create labels for(int x = total2; x > 0; x--) { if(x < total2 - MathMax(MA_Period, MA2_Period)) { MA[x] = iMAOnArray(ACCOUNTBALANCE, 0, MA_Period, 0, MA_Method, x); MA2[x] = iMAOnArray(ACCOUNTBALANCE, 0, MA2_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) + ", " + DoubleToString(MA2[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 = HistoryDealsTotal(); TT = iTime(Symbol(), PERIOD_CURRENT, 0); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CreateDotLabel(string name, string text, datetime T1, double P1, color C) { int window_x = ChartWindowFind(); //Print("window_x",window_x); 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); } //+------------------------------------------------------------------+