Second(s) timeframes on your MT4 - page 2

Ynal Al Khalil #:
so ?????
did I make any mistake ???

In my opinion, none whatsoever.

Note that this thread has been up for 3 months now without objection from the Moderators.

hong phuc #:
Hello, I came across your post on FXBlue and wanted to express my sincere thanks. However, the script currently only works with XAUUSD; could you "unlock" and rewrite the source code so it works with all instruments? For instance, I trade gold on an Exness Zero account where the symbol is XAUUSDz rather than XAUUSD (without the 'z' suffix). Please rewrite it. Also, if possible, could you create versions for both MT4 and MT5? Thank you very much.
here is the modified MT4's version >>>>>>
    //+------------------------------------------------------------------+
//|                                              Universal_Seconds.mq4|
//|                Universal Seconds Offline Chart Generator         |
//|                MT4 Build 600+                                    |
//+------------------------------------------------------------------+
#property strict
#property show_inputs
#property version   "2.00"

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input int  InpSecondsPeriod = 10;       // Seconds timeframe
input bool InpAutoOpenChart = true;     // Auto open offline chart
input bool InpShowComment   = true;     // Show status comment

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
int      ExtHandle       = -1;
long     OfflineChartID  = 0;
int      OfflinePeriod   = 0;

datetime CurrentBarTime  = 0;

MqlRates Rate;

string   CurrentSymbol;
string   OfflineFileName;

//+------------------------------------------------------------------+
//| Script start                                                     |
//+------------------------------------------------------------------+
void OnStart()
{
   // Store the exact symbol to which the script is attached.
   // This automatically supports:
   // EURUSD
   // GBPUSD
   // XAUUSD
   // XAUUSDm
   // GOLD
   // BTCUSD
   // etc.
   CurrentSymbol = Symbol();

   // Validate seconds period.
   if(InpSecondsPeriod < 1)
   {
      Alert("Seconds period must be >= 1");
      return;
   }

   // Make sure the symbol has a valid market price.
   RefreshRates();

   double current_bid = MarketInfo(CurrentSymbol, MODE_BID);

   if(current_bid <= 0.0)
   {
      Alert(
         "No valid market price is available for ",
         CurrentSymbol,
         ". Make sure the symbol is active and receiving ticks."
      );
      return;
   }

   // ---------------------------------------------------------------
   // Create a unique offline timeframe.
   //
   // Example:
   // S10 -> 20010
   // S30 -> 20030
   // S60 -> 20060
   //
   // The symbol itself remains part of the HST filename.
   // ---------------------------------------------------------------
   OfflinePeriod = 20000 + InpSecondsPeriod;

   // Use the actual broker symbol.
   OfflineFileName = CurrentSymbol + (string)OfflinePeriod + ".hst";

   // ---------------------------------------------------------------
   // Open/create the offline history file.
   // FILE_WRITE creates a fresh file for this instance.
   // ---------------------------------------------------------------
   ExtHandle = FileOpenHistory(
      OfflineFileName,
      FILE_BIN |
      FILE_WRITE |
      FILE_SHARE_WRITE |
      FILE_SHARE_READ
   );

   if(ExtHandle < 0)
   {
      Alert(
         "Failed to create offline history file.\n",
         "Symbol: ", CurrentSymbol, "\n",
         "File: ", OfflineFileName, "\n",
         "Error: ", GetLastError()
      );
      return;
   }

   // Write standard MT4 HST header.
   WriteHistoryHeader();

   // Wait for the first market tick.
   WaitForFirstTick();

   if(IsStopped())
      return;

   // Build the first seconds candle.
   BuildInitialBar();

   // Open the offline chart if requested.
   if(InpAutoOpenChart)
      OpenOfflineChart();

   // Start processing ticks.
   MainLoop();
}

//+------------------------------------------------------------------+
//| Wait for first market tick                                       |
//+------------------------------------------------------------------+
void WaitForFirstTick()
{
   while(!IsStopped())
   {
      RefreshRates();

      double bid = MarketInfo(CurrentSymbol, MODE_BID);

      datetime server_time = TimeCurrent();

      if(server_time > 0 && bid > 0.0)
         break;

      Sleep(100);
   }
}

//+------------------------------------------------------------------+
//| Build first candle                                               |
//+------------------------------------------------------------------+
void BuildInitialBar()
{
   RefreshRates();

   double bid = MarketInfo(CurrentSymbol, MODE_BID);

   datetime now = TimeCurrent();

   CurrentBarTime = NormalizeSecondTime(now);

   ZeroMemory(Rate);

   Rate.time        = CurrentBarTime;
   Rate.open        = bid;
   Rate.high        = bid;
   Rate.low         = bid;
   Rate.close       = bid;
   Rate.tick_volume = 1;
   Rate.spread      = (long)MarketInfo(CurrentSymbol, MODE_SPREAD);
   Rate.real_volume = 0;

   FileWriteStruct(ExtHandle, Rate);
   FileFlush(ExtHandle);
}

//+------------------------------------------------------------------+
//| Main processing loop                                             |
//+------------------------------------------------------------------+
void MainLoop()
{
   datetime last_refresh = 0;

   while(!IsStopped())
   {
      // Update market data.
      RefreshRates();

      double bid = MarketInfo(CurrentSymbol, MODE_BID);

      // Some instruments can temporarily have no valid price.
      if(bid <= 0.0)
      {
         Sleep(20);
         continue;
      }

      datetime now = TimeCurrent();

      if(now <= 0)
      {
         Sleep(20);
         continue;
      }

      datetime normalized = NormalizeSecondTime(now);

      // ------------------------------------------------------------
      // A new seconds candle has started.
      // ------------------------------------------------------------
      if(normalized > CurrentBarTime)
      {
         FinalizeCurrentBar();

         StartNewBar(normalized);
      }
      else
      {
         // Same candle - update OHLC.
         UpdateCurrentBar();
      }

      // ------------------------------------------------------------
      // Display status.
      // ------------------------------------------------------------
      if(InpShowComment)
      {
         Comment(
            "Universal Seconds Chart Generator\n",
            "Symbol     : ", CurrentSymbol, "\n",
            "Seconds TF : S", InpSecondsPeriod, "\n",
            "Offline ID : ", OfflinePeriod, "\n",
            "Status     : RUNNING\n",
            "Bid        : ",
            DoubleToString(bid, (int)MarketInfo(CurrentSymbol, MODE_DIGITS))
         );
      }

      // ------------------------------------------------------------
      // Refresh offline chart periodically.
      // ------------------------------------------------------------
      if(TimeLocal() - last_refresh >= 2)
      {
         RefreshOfflineChart();
         last_refresh = TimeLocal();
      }

      Sleep(20);
   }

   Comment("");
}

//+------------------------------------------------------------------+
//| Start new candle                                                 |
//+------------------------------------------------------------------+
void StartNewBar(datetime bar_time)
{
   RefreshRates();

   double bid = MarketInfo(CurrentSymbol, MODE_BID);

   if(bid <= 0.0)
      return;

   CurrentBarTime = bar_time;

   ZeroMemory(Rate);

   Rate.time        = CurrentBarTime;
   Rate.open        = bid;
   Rate.high        = bid;
   Rate.low         = bid;
   Rate.close       = bid;
   Rate.tick_volume = 1;
   Rate.spread      = (long)MarketInfo(CurrentSymbol, MODE_SPREAD);
   Rate.real_volume = 0;

   FileWriteStruct(ExtHandle, Rate);
   FileFlush(ExtHandle);
}

//+------------------------------------------------------------------+
//| Update current candle                                            |
//+------------------------------------------------------------------+
void UpdateCurrentBar()
{
   RefreshRates();

   double bid = MarketInfo(CurrentSymbol, MODE_BID);

   if(bid <= 0.0)
      return;

   // Update high.
   if(bid > Rate.high)
      Rate.high = bid;

   // Update low.
   if(bid < Rate.low)
      Rate.low = bid;

   // Update close.
   Rate.close = bid;

   // Count ticks.
   Rate.tick_volume++;

   // Update current spread.
   Rate.spread = (long)MarketInfo(CurrentSymbol, MODE_SPREAD);

   // Rewrite the last HST record.
   int struct_size = sizeof(MqlRates);

   if(FileSeek(ExtHandle, -struct_size, SEEK_END))
   {
      FileWriteStruct(ExtHandle, Rate);
      FileFlush(ExtHandle);
   }
}

//+------------------------------------------------------------------+
//| Finalize current candle                                          |
//+------------------------------------------------------------------+
void FinalizeCurrentBar()
{
   int struct_size = sizeof(MqlRates);

   if(FileSeek(ExtHandle, -struct_size, SEEK_END))
   {
      FileWriteStruct(ExtHandle, Rate);
      FileFlush(ExtHandle);
   }
}

//+------------------------------------------------------------------+
//| Normalize time                                                   |
//+------------------------------------------------------------------+
datetime NormalizeSecondTime(datetime t)
{
   if(InpSecondsPeriod <= 1)
      return t;

   return (t / InpSecondsPeriod) * InpSecondsPeriod;
}

//+------------------------------------------------------------------+
//| Write HST header                                                 |
//+------------------------------------------------------------------+
void WriteHistoryHeader()
{
   int    version = 401;
   string copyright = "Universal Seconds Chart";
   string symbol = CurrentSymbol;
   int    period = OfflinePeriod;
   int    digits = (int)MarketInfo(CurrentSymbol, MODE_DIGITS);
   int    unused[13];

   ArrayInitialize(unused, 0);

   // HST header.
   FileWriteInteger(ExtHandle, version, LONG_VALUE);

   FileWriteString(
      ExtHandle,
      copyright,
      64
   );

   FileWriteString(
      ExtHandle,
      symbol,
      12
   );

   FileWriteInteger(
      ExtHandle,
      period,
      LONG_VALUE
   );

   FileWriteInteger(
      ExtHandle,
      digits,
      LONG_VALUE
   );

   FileWriteInteger(
      ExtHandle,
      0,
      LONG_VALUE
   );

   FileWriteInteger(
      ExtHandle,
      0,
      LONG_VALUE
   );

   FileWriteArray(
      ExtHandle,
      unused,
      0,
      13
   );
}

//+------------------------------------------------------------------+
//| Open offline chart                                               |
//+------------------------------------------------------------------+
void OpenOfflineChart()
{
   // Open the exact current symbol.
   OfflineChartID = ChartOpen(
      CurrentSymbol,
      OfflinePeriod
   );

   if(OfflineChartID <= 0)
   {
      Print(
         "Unable to open offline chart. ",
         "Symbol=", CurrentSymbol,
         " Period=", OfflinePeriod,
         " Error=", GetLastError()
      );

      return;
   }

   Sleep(1000);

   RefreshOfflineChart();
}

//+------------------------------------------------------------------+
//| Refresh offline chart                                            |
//+------------------------------------------------------------------+
void RefreshOfflineChart()
{
   if(OfflineChartID <= 0)
      return;

   // Make sure the chart belongs to the correct symbol/timeframe.
   ChartSetSymbolPeriod(
      OfflineChartID,
      CurrentSymbol,
      OfflinePeriod
   );

   ChartRedraw(OfflineChartID);
}

//+------------------------------------------------------------------+
//| Cleanup                                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   Comment("");

   // Close history file.
   if(ExtHandle >= 0)
   {
      FileFlush(ExtHandle);
      FileClose(ExtHandle);
      ExtHandle = -1;
   }

   // We intentionally do NOT close the offline chart here.
   // The user may want to keep the chart open after stopping
   // the generator.
   OfflineChartID = 0;
}
//+------------------------------------------------------------------+ 
and here is the modified MT5's version >>>>>>
    //+------------------------------------------------------------------+
//|                                              GoldSecondsMT5.mq5  |
//|                     MT5 Native Seconds Chart Generator            |
//|                     Custom Symbol Engine Version                  |
//+------------------------------------------------------------------+
#property strict

enum ENUM_SOURCE_MODE
{
   SOURCE_MODE_AUTO_GOLD = 0,  // Auto-detect Gold (any XAU/GOLD name)
   SOURCE_MODE_CHART     = 1,  // Chart symbol (works for any instrument)
   SOURCE_MODE_MANUAL    = 2   // Manual symbol (any instrument)
};

input ENUM_SOURCE_MODE InpSourceMode  = SOURCE_MODE_AUTO_GOLD;
input string           InpSourceSymbol = "XAUUSD";
input int              InpSeconds      = 10;
input bool             InpAutoOpen     = true;

string SourceSymbolName;
string CustomSymbolName;

MqlRates CurrentBar;

datetime CurrentBarTime = 0;

long LastTickTimeMSC = 0;

//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
{
   if(InpSeconds < 1)
   {
      Print("Seconds must be >= 1");
      return(INIT_FAILED);
   }

   //--- resolve the actual symbol to watch
   SourceSymbolName = ResolveSourceSymbol();

   if(StringLen(SourceSymbolName) == 0)
   {
      Print("Failed to resolve source symbol. Check the inputs.");
      return(INIT_FAILED);
   }

   //--- ticks stream only for symbols selected in Market Watch
   if(!SymbolSelect(SourceSymbolName, true))
   {
      Print("Failed to select symbol in Market Watch: ", SourceSymbolName, " (", GetLastError(), ")");
      return(INIT_FAILED);
   }

   CustomSymbolName = MakeCustomSymbolName(SourceSymbolName, InpSeconds);

   Print("Source symbol: ", SourceSymbolName);
   Print("Creating custom symbol: ", CustomSymbolName);

   CreateCustomSymbol();

   OpenCustomChart();

   EventSetMillisecondTimer(50);

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   EventKillTimer();
}

//+------------------------------------------------------------------+
//| Millisecond timer                                                |
//+------------------------------------------------------------------+
void OnTimer()
{
   MqlTick tick;

   if(!SymbolInfoTick(SourceSymbolName, tick))
      return;

   if(tick.time_msc == LastTickTimeMSC)
      return;

   LastTickTimeMSC = tick.time_msc;

   ProcessTick(tick);
}

//+------------------------------------------------------------------+
//| Process incoming tick                                            |
//+------------------------------------------------------------------+
void ProcessTick(MqlTick &tick)
{
   datetime bar_time =
      (datetime)((tick.time / InpSeconds) * InpSeconds);

   //--- First bar
   if(CurrentBarTime == 0)
   {
      StartNewBar(bar_time, tick);
      return;
   }

   //--- New candle
   if(bar_time > CurrentBarTime)
   {
      PushBarToCustomSymbol(CurrentBar);

      StartNewBar(bar_time, tick);
   }
   else
   {
      UpdateCurrentBar(tick);
   }
}

//+------------------------------------------------------------------+
//| Start new candle                                                 |
//+------------------------------------------------------------------+
void StartNewBar(datetime bar_time, MqlTick &tick)
{
   CurrentBarTime = bar_time;

   CurrentBar.time        = bar_time;
   CurrentBar.open        = tick.bid;
   CurrentBar.high        = tick.bid;
   CurrentBar.low         = tick.bid;
   CurrentBar.close       = tick.bid;
   CurrentBar.tick_volume = 1;
   CurrentBar.spread      = 0;
   CurrentBar.real_volume = 0;
}

//+------------------------------------------------------------------+
//| Update current candle                                            |
//+------------------------------------------------------------------+
void UpdateCurrentBar(MqlTick &tick)
{
   if(tick.bid > CurrentBar.high)
      CurrentBar.high = tick.bid;

   if(tick.bid < CurrentBar.low)
      CurrentBar.low = tick.bid;

   CurrentBar.close = tick.bid;

   CurrentBar.tick_volume++;
}

//+------------------------------------------------------------------+
//| Push candle to custom symbol                                     |
//+------------------------------------------------------------------+
void PushBarToCustomSymbol(MqlRates &bar)
{
   MqlRates rates[1];

   rates[0] = bar;

   if(!CustomRatesUpdate(CustomSymbolName, rates))
   {
      Print("CustomRatesUpdate failed. Error: ", GetLastError());
   }
}

//+------------------------------------------------------------------+
//| Create custom symbol                                             |
//+------------------------------------------------------------------+
void CreateCustomSymbol()
{
   //--- already exists
   if(SymbolSelect(CustomSymbolName, true))
   {
      Print("Custom symbol already exists.");
      return;
   }

   //--- create
   if(!CustomSymbolCreate(
      CustomSymbolName,
      "Custom",
      SourceSymbolName))
   {
      Print("Failed creating custom symbol. Error: ", GetLastError());
      return;
   }

   //--- select
   SymbolSelect(CustomSymbolName, true);

   //--- synchronize properties
   CustomSymbolSetInteger(
      CustomSymbolName,
      SYMBOL_DIGITS,
      (int)SymbolInfoInteger(SourceSymbolName, SYMBOL_DIGITS)
   );

   CustomSymbolSetDouble(
      CustomSymbolName,
      SYMBOL_POINT,
      SymbolInfoDouble(SourceSymbolName, SYMBOL_POINT)
   );

   Print("Custom symbol created successfully.");
}

//+------------------------------------------------------------------+
//| Open custom chart                                                |
//+------------------------------------------------------------------+
void OpenCustomChart()
{
   if(!InpAutoOpen)
      return;

   long chart_id = ChartOpen(CustomSymbolName, PERIOD_M1);

   if(chart_id > 0)
   {
      Print("Custom chart opened.");
   }
   else
   {
      Print("Failed opening chart.");
   }
}

//+------------------------------------------------------------------+
//| Resolve which symbol the script should watch                     |
//+------------------------------------------------------------------+
string ResolveSourceSymbol()
{
   if(InpSourceMode == SOURCE_MODE_CHART)
      return(_Symbol);

   if(InpSourceMode == SOURCE_MODE_MANUAL)
   {
      if(SymbolSelect(InpSourceSymbol, true))
         return(InpSourceSymbol);
      return("");
   }

   //--- AUTO_GOLD: look for any gold alias, Market Watch first, then all symbols
   string found = FindGoldSymbol(true);

   if(StringLen(found) == 0)
      found = FindGoldSymbol(false);

   if(StringLen(found) == 0)
   {
      Print("No XAU/GOLD symbol found. Falling back to: ", InpSourceSymbol);

      if(SymbolSelect(InpSourceSymbol, true))
         found = InpSourceSymbol;
   }

   return(found);
}

//+------------------------------------------------------------------+
//| Scan terminal symbol list for a gold alias                       |
//+------------------------------------------------------------------+
string FindGoldSymbol(bool market_watch_only)
{
   string best_name  = "";
   int    best_score = 0;

   int total = SymbolsTotal(market_watch_only);

   for(int i = 0; i < total; i++)
   {
      string name = SymbolName(i, market_watch_only);

      if(StringLen(name) == 0)
         continue;

      int score = GoldScore(name);

      if(score > best_score)
      {
         best_score = score;
         best_name  = name;
      }
   }

   return(best_name);
}

//+------------------------------------------------------------------+
//| Score a symbol name as a gold alias (0 = not gold)               |
//+------------------------------------------------------------------+
int GoldScore(string symbol)
{
   string ucase = symbol;
   StringToUpper(ucase);

   int score = 0;

   if(StringFind(ucase, "XAUUSD") >= 0)
      score += 100;
   else if(StringFind(ucase, "XAU") >= 0)
      score += 60;
   else if(StringFind(ucase, "GOLD") >= 0)
      score += 50;
   else
      return(0);

   //--- slight preference for the most common names
   if(ucase == "XAUUSD")    score += 10;
   if(ucase == "XAUUSD.A")  score += 8;
   if(ucase == "XAUUSDM")   score += 8;
   if(ucase == "GOLD")      score += 5;

   return(score);
}

//+------------------------------------------------------------------+
//| Build a valid unique custom symbol name                          |
//+------------------------------------------------------------------+
string MakeCustomSymbolName(string source_symbol, int seconds)
{
   string name = source_symbol + "_S" + IntegerToString(seconds);

   //--- sanitize characters that are unsafe in custom symbol names
   StringReplace(name, ".", "_");
   StringReplace(name, "/", "_");
   StringReplace(name, "\\", "_");
   StringReplace(name, " ", "_");

   //--- keep inside the terminal symbol name length limit
   if(StringLen(name) > 60)
      name = StringSubstr(name, 0, 60);

   return(name);
}
//+------------------------------------------------------------------+