preview
Price Action Analysis Toolkit Development (Part 81): Adding Persistent Historical Bookmarks to an MQL5 Navigator

Price Action Analysis Toolkit Development (Part 81): Adding Persistent Historical Bookmarks to an MQL5 Navigator

MetaTrader 5Trading systems |
142 0
Christian Benjamin
Christian Benjamin

Contents


Introduction

In the previous instalment of this series, we developed a History Navigator for MetaTrader 5 that allowed a chart to be positioned at a specified historical date and time. The navigator provided a structured alternative to manually scrolling through large amounts of historical data.

However, date-based navigation remains temporary. Once a useful historical location has been identified, the navigator does not retain that location for future analysis. Returning to the same point therefore requires the date and time to be entered again.

This limitation is most noticeable during historical market analysis, where the same price-action events may be revisited repeatedly.  A trader may want to preserve a significant market structure, a historical setup, an unusual price movement, or an important support or resistance area and return to it later without having to remember its exact timestamp.

The natural extension is to introduce persistent historical bookmarks.

A bookmark associates a historical chart position with a user-defined name and optional notes. In addition to the timestamp, the bookmark records the corresponding symbol and timeframe, allowing the navigator to reconstruct the original chart context when the bookmark is selected.

The bookmarks are persisted to a CSV file, allowing them to remain available beyond the lifetime of the navigator. They can therefore be reused after the navigator is removed and reattached or after the MetaTrader 5 terminal is restarted.

In this article, we extend the History Navigator by adding a bookmark storage layer and integrating it into the interface. The implementation covers creation, persistence, loading, selection, navigation, and deletion, while preserving the application's modular structure.

This transforms the navigator from a date-based positioning tool into a reusable historical reference system.


From Date Navigation to Historical Bookmarks

Date Navigation

The GIF below demonstrates navigation to a specific historical date using the History Navigator developed in the previous instalment. Although this provides a convenient way to locate historical price action, the selected location is not retained as a reusable reference.

Historical Market Events Worth Revisiting

Market history contains numerous events that can be valuable for traders, analysts, and researchers to revisit. These may include distinctive price-action patterns, sudden price spikes, periods of extreme volatility, major reversals, breakouts, or unusual market behavior.

Reexamining such events allows the underlying price action to be studied in greater detail, including what happened, how the market responded, and what conditions preceded the movement. Studying these situations can provide useful insights for future analysis, while preserving references to them can help build a documented record of significant market behavior.

Some well-known historical market events and periods are presented in the table below.

Date / Period
Market event
Example instruments
Behavior worth studying
March 2020
COVID-19 market shock
S&P 500, Nasdaq 100, GBP/USD, EUR/USD, Gold, WTI
Extreme volatility, rapid sell-offs and reversals
2022–2023
Global inflation and interest-rate tightening
USD/JPY, EUR/USD, GBP/USD, Gold, Nasdaq 100
Strong trends, breakouts and monetary-policy repricing
March 2023
US banking-sector stressBank stocks, S&P 500, Nasdaq 100, Gold
Sharp declines, volatility and safe-haven flows
5 August 2024
Global market sell-off and yen carry-trade unwind
USD/JPY, EUR/JPY, AUD/JPY, Nikkei 225, S&P 500
Sudden volatility, rapid reversals and cross-market movement
2024–2025
Strong gold price expansion
XAU/USD, Gold futures
Sustained bullish movement, breakouts and pullbacks

These examples are not exhaustive; the relevance of a historical date depends on the trader's strategy, interests, and the specific market behavior or pattern being studied.

Historical Bookmarks

The examples above illustrate why traders may need to preserve specific chart locations for later analysis. A historical bookmark is a saved reference to a particular position in market history, allowing a trader to return to that location without manually searching for the date and time again.

In this development, each bookmark identifies the symbol, timeframe, date and time, together with a user-defined name and optional notes. This allows traders to attach meaningful context to locations such as a significant price-action pattern, support or resistance level, breakout, reversal, or period of unusual volatility.

Because the bookmarks are stored persistently, they remain available after the navigator is closed and reopened.


MQL5 Implementation

This implementation extends the History Navigator developed in the previous instalment with a persistent bookmark layer. A bookmark stores the chart symbol, timeframe, historical position, name, and optional notes so that an important market location can be revisited later.

Existing date and time navigation continues to handle chart positioning. Bookmark functionality is built around that navigation engine and adds persistent storage and bookmark management without duplicating the historical search logic.

Bookmark Data Model

A bookmark needs enough information to reconstruct a historical chart location. The SBookmark structure stores the chart context together with a user-defined name and optional notes.

//+------------------------------------------------------------------+
//| Bookmark structure                                               |
//+------------------------------------------------------------------+
struct SBookmark
  {
   string            name;
   string            symbol;
   ENUM_TIMEFRAMES   timeframe;
   datetime          dateTime;
   string            notes;
  };

The name identifies the bookmark in the navigator, while symbol and timeframe preserve the chart context in which it was created. The notes field provides an optional place for additional information about the saved location.

Using datetime instead of a bar index is important. A bar index depends on the current historical series and can change as data is loaded or updated. A timestamp remains tied to a specific point in market time and can be resolved again when the bookmark is recalled.

Persistent Bookmark Storage

Bookmark data must survive beyond the current session of the navigator. CBookmarkStorage provides this persistence layer.

The class keeps bookmarks in memory and handles CSV operations separately from CNavigatorDialog. This separation allows the dialog to concentrate on user interaction and navigation while the storage class manages loading, saving, validation, and record management.

CSV File Management

Bookmarks are stored in HistoryNavigatorBookmarks.csv. The filename is assigned when the storage object is created:

//+------------------------------------------------------------------+
//| Constructor – sets the filename                                  |
//+------------------------------------------------------------------+
CBookmarkStorage::CBookmarkStorage()
  {
   m_filename = "HistoryNavigatorBookmarks.csv";
  }

A CSV file is sufficient for this application because bookmark records are small and structured. It also makes the stored data easy to inspect during development.

Each record contains five fields:

Name, Symbol, Timeframe, DateTime, Notes

The writer stores ENUM_TIMEFRAMES as its numeric value and datetime as an integer value. The loader reads the fields in the same order and converts them back to their corresponding MQL5 types.

Maintaining the same field order in both directions ensures that saved records can be reconstructed correctly.

Loading Existing Bookmarks

When the navigator starts, it loads any previously saved bookmarks:

//--- Load existing bookmarks from disk into memory and show them
LoadBookmarks();

LoadBookmarks() delegates file access to CBookmarkStorage::Load(). The storage class opens the CSV file and reads each record. It skips header/empty rows, validates values, converts them to MQL5 types, and adds valid entries to the in-memory collection.

CBookmarkStorage::Load() brings these operations together:

//+------------------------------------------------------------------+
//| Loads bookmarks from the CSV file.                               |
//+------------------------------------------------------------------+
bool CBookmarkStorage::Load()
  {
   Clear();

   if(!FileIsExist(m_filename))
     {
      Print("BookmarkStorage: no bookmark file found. Starting empty.");
      return(true);
     }

   ResetLastError();

   int handle = FileOpen(m_filename,
                         FILE_READ | FILE_CSV | FILE_ANSI |
                         FILE_SHARE_READ | FILE_SHARE_WRITE,
                         ',');

   if(handle == INVALID_HANDLE)
     {
      PrintFormat("BookmarkStorage: failed to open '%s' for reading. Error=%d",
                  m_filename, GetLastError());
      return(false);
     }

//--- Read field by field. We expect exactly 5 fields per record.
   while(!FileIsEnding(handle))
     {
      string name     = FileReadString(handle);
      string symbol   = FileReadString(handle);
      string tf_text  = FileReadString(handle);
      string dt_text  = FileReadString(handle);
      string notes    = FileReadString(handle);

      //--- Skip an empty trailing record (sometimes appears)
      if(name == "" && symbol == "" && tf_text == "" && dt_text == "" && notes == "")
         continue;

      //--- Skip the header row
      if(name == "Name" && symbol == "Symbol")
         continue;

      //--- Validate the fields
      long tf_value = StringToInteger(tf_text);
      long dt_value = StringToInteger(dt_text);

      if(name == "" ||
         symbol == "" ||
         tf_text == "" ||
         dt_text == "" ||
         tf_value < 0 ||
         dt_value <= 0)
        {
         PrintFormat("BookmarkStorage: skipped invalid record: Name='%s' Symbol='%s' Timeframe='%s' DateTime='%s' Notes='%s'",
                     name, symbol, tf_text, dt_text, notes);
         continue;
        }

      //--- Convert and store
      SBookmark bm;
      bm.name       = name;
      bm.symbol     = symbol;
      bm.timeframe  = (ENUM_TIMEFRAMES)tf_value;
      bm.dateTime   = (datetime)dt_value;
      bm.notes      = notes;

      int index = ArraySize(m_bookmarks);
      ArrayResize(m_bookmarks, index + 1);
      m_bookmarks[index] = bm;
     }

   FileClose(handle);

   PrintFormat("BookmarkStorage: loaded %d bookmark(s).", ArraySize(m_bookmarks));

   return(true);
  }

After loading, the navigator works with m_bookmarks in memory instead of reading the CSV file for each operation. This gives the interface direct access to the current collection while keeping file handling inside CBookmarkStorage.

Handling the First Run

A bookmark file does not exist when the navigator is used for the first time. Load() treats this as an empty starting state rather than an error.

//--- Start with an empty collection when no bookmark file exists
if(!FileIsExist(m_filename))
  {
   Print("BookmarkStorage: no bookmark file found. Starting empty.");
   return(true);
  }

No error is generated in this case. The in-memory collection remains empty, and the file is created when the first bookmark is saved.

A missing file means that no bookmarks have been saved yet. File-access failures and invalid records are handled as storage errors.

Saving Changes

Changes are written to persistent storage as soon as they are made. When a bookmark is added, it is first appended to the in-memory array and then saved to the CSV file.

CBookmarkStorage::AddBookmark() validates the bookmark, updates the collection, saves the new state, and rolls back the memory change if persistence fails.

//+------------------------------------------------------------------+
//| Adds a new bookmark.                                             |
//+------------------------------------------------------------------+
bool CBookmarkStorage::AddBookmark(const SBookmark &bm)
  {
//--- Basic validation
   if(bm.name == "")
      return(false);

   if(bm.symbol == "")
      return(false);

   if(bm.dateTime <= 0)
      return(false);

//--- Append to memory
   int index = ArraySize(m_bookmarks);
   ArrayResize(m_bookmarks, index + 1);
   m_bookmarks[index] = bm;

//--- Try to save to file
   if(!Save())
     {
//--- Roll back the memory addition
      ArrayResize(m_bookmarks, index);
      return(false);
     }

   return(true);
  }

The rollback prevents the in-memory collection from diverging from persistent storage. When the file operation fails, the newly added bookmark is removed from memory as well.

Deletion and renaming use the same principle: modify the collection, save the new state, and restore the previous state if saving fails.

Validation of Stored Records

CSV data is external to the current execution, so each record must be validated before entering the in-memory collection.

Load() checks that required fields are present, the stored timeframe value is valid, and the timestamp is positive. Invalid records are skipped so that valid bookmarks in the same file can still be restored.

Rejected records are also reported in the Experts log, making storage problems visible during development.

Storage Workflow

The storage process can be summarized as:

CBookmarkStorage owns this workflow. CNavigatorDialog interacts with it through Load(), AddBookmark(), DeleteBookmark(), and RenameBookmark() without performing CSV serialization itself.

This keeps persistence separate from the user interface and gives each class a clear responsibility.

Bookmark Management in the Navigator

CNavigatorDialog connects the bookmark collection to the user interface. It provides the controls and handlers required to create, select, navigate to, and delete bookmarks.

The dialog also maintains the index of the currently selected bookmark:

//--- Store the index of the currently selected bookmark
   int               m_selectedIndex;

The index provides a stable reference to the corresponding entry in CBookmarkStorage because the list control is recreated when its contents change.

Saving a Bookmark

OnSaveBookmark() collects the bookmark name and notes, determines the chart context, selects a historical position, and passes the completed SBookmark to the storage layer.

//+------------------------------------------------------------------+
//| Saves the current chart position as a new bookmark.              |
//+------------------------------------------------------------------+
void CNavigatorDialog::OnSaveBookmark(void)
  {
   string name = m_editBookmarkName.Text();
   StringTrimLeft(name);
   StringTrimRight(name);

   if(name == "")
     {
      UpdateStatus("Please enter a bookmark name.", clrRed);
      return;
     }

   string symbol = Symbol();
   ENUM_TIMEFRAMES tf = (ENUM_TIMEFRAMES)Period();

   datetime dt = m_lastNavigatedTime;

//--- If the user hasn't navigated yet, use the bar in the middle of the screen
   if(dt <= 0)
     {
      int visibleBars = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
      int firstVisible = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);

      if(visibleBars < 1)
         visibleBars = 50;

      int centerShift = firstVisible + visibleBars / 2;
      dt = iTime(symbol, tf, centerShift);
     }

   if(dt <= 0)
     {
      UpdateStatus("Unable to determine current chart time.", clrRed);
      return;
     }

//--- Build the bookmark structure
   SBookmark bm;
   bm.name       = name;
   bm.symbol     = symbol;
   bm.timeframe  = tf;
   bm.dateTime   = dt;
   bm.notes      = m_editBookmarkNotes.Text();

//--- Debug output to the Experts log
   PrintFormat("HistoryNavigator: saving bookmark '%s' -> %s %s %s",
               bm.name, bm.symbol, EnumToString(bm.timeframe),
               TimeToString(bm.dateTime, TIME_DATE | TIME_MINUTES));

//--- Ask the storage class to add it and write to file
   if(!m_bookmarkStorage.AddBookmark(bm))
     {
      UpdateStatus("Failed to save bookmark.", clrRed);
      return;
     }

//--- The newly added bookmark is at the end of the storage array
   int newIndex = m_bookmarkStorage.Count() - 1;

   RefreshBookmarkList();

//--- Auto-select the newly created bookmark so the user sees it
if(newIndex >= 0)
   SelectBookmarkIndex(newIndex);

UpdateStatus("Bookmark saved.",clrGreen);

//--- Clear the input fields
m_editBookmarkName.Text("");
m_editBookmarkNotes.Text("");
  }

symbol and timeframe come directly from the active chart. For the historical position, the function first uses m_lastNavigatedTime. When no date navigation has occurred, it falls back to a bar near the center of the visible chart.

After the storage operation succeeds, the list is refreshed and the new bookmark is selected. The name and notes fields are then cleared so that another bookmark can be entered.

Navigating to a Bookmark

OnGoToBookmark() restores the chart context stored in a selected bookmark and passes its timestamp to the existing navigation engine.

//+------------------------------------------------------------------+
//| Navigates to the location stored in the selected bookmark        |
//+------------------------------------------------------------------+
void CNavigatorDialog::OnGoToBookmark(void)
  {
//--- Ensure that a valid bookmark is selected
   if(!CaptureSelectedBookmark())
     {
      UpdateStatus("Please select a bookmark first.", clrRed);
      return;
     }

//--- Retrieve the selected bookmark from persistent storage
   SBookmark bm = m_bookmarkStorage.GetBookmark(m_selectedIndex);

   if(bm.name == "" || bm.symbol == "" || bm.dateTime <= 0)
     {
      UpdateStatus("Invalid bookmark selected.", clrRed);
      return;
     }

   PrintFormat("HistoryNavigator: opening bookmark '%s' -> %s %s %s",
               bm.name, bm.symbol, EnumToString(bm.timeframe),
               TimeToString(bm.dateTime, TIME_DATE | TIME_MINUTES));

//--- If the symbol or timeframe differ, switch the chart
   if(!ChartSetSymbolPeriod(0, bm.symbol, bm.timeframe))
     {
      UpdateStatus("Failed to switch symbol/timeframe.", clrRed);
      return;
     }

//--- Wait a moment for MT5 to load the new history data
   Sleep(100);

//--- Now navigate to the exact datetime within that new context
   NavigateToDateTime(bm.dateTime);

   UpdateStatus("Navigated to: " + bm.name, clrGreen);
  }

No separate bookmark-specific bar-search routine is required. NavigateToDateTime() already validates the requested time, locates the corresponding bar, and positions the chart. Bookmark navigation supplies that routine with a stored timestamp.

ChartSetSymbolPeriod() may require time to switch the chart context and load the corresponding history. The short delay allows that update to complete before NavigateToDateTime() searches for the stored position.

Deleting a Bookmark

Deletion uses the current selection, asks for confirmation, removes the bookmark from storage, and rebuilds the list.

After deletion, the list is rebuilt. If other bookmarks remain, the nearest available entry is selected; otherwise, the selection is cleared.

//+------------------------------------------------------------------+
//| Deletes the selected bookmark after user confirmation.           |
//+------------------------------------------------------------------+
void CNavigatorDialog::OnDeleteBookmark(void)
  {
   if(!CaptureSelectedBookmark())
     {
      UpdateStatus("Please select a bookmark first.", clrRed);
      return;
     }

   int index = m_selectedIndex;
   SBookmark bm = m_bookmarkStorage.GetBookmark(index);

   if(bm.name == "")
     {
      UpdateStatus("Invalid bookmark selected.", clrRed);
      return;
     }

//--- Ask the user to confirm
   string message = "Delete bookmark '" + bm.name + "'?";
   int answer = MessageBox(message, "Confirm Delete", MB_YESNO);

   if(answer != IDYES)
      return;

   PrintFormat("HistoryNavigator: deleting selected index=%d name='%s'",
               index, bm.name);

   if(!m_bookmarkStorage.DeleteBookmark(index))
     {
      UpdateStatus("Failed to delete bookmark.", clrRed);
      return;
     }

//--- Clear stale selection before rebuilding the list
   m_selectedIndex = -1;

   RefreshBookmarkList();

//--- Select the nearest remaining row (if any)
   int remaining = m_bookmarkStorage.Count();

   if(remaining > 0)
     {
      int nextIndex = index;

      if(nextIndex >= remaining)
         nextIndex = remaining - 1;

      SelectBookmarkIndex(nextIndex);
     }
   else
     {
      ClearSelection();
     }

   UpdateStatus("Bookmark deleted.", clrGreen);
  }

Synchronizing the Bookmark List

Changes to the collection require the list control to reflect the current storage state. RefreshBookmarkList() recreates the control and populates it from the bookmark array.

//+------------------------------------------------------------------+
//| Completely rebuilds the bookmark list from the storage array.    |
//+------------------------------------------------------------------+
void CNavigatorDialog::RefreshBookmarkList(void)
  {
//--- Destroy the old control to reset its internal state
   m_listBookmarks.Destroy();

   if(!m_listBookmarks.Create(m_chart_id, m_name + "BmList", m_subwin,
                              m_listX1, m_listY1, m_listX2, m_listY2))
     {
      Print("HistoryNavigator: failed to recreate bookmark list.");
      m_selectedIndex = -1;
      return;
     }

   if(!Add(GetPointer(m_listBookmarks)))
     {
      Print("HistoryNavigator: failed to add recreated bookmark list.");
      m_selectedIndex = -1;
      return;
     }

   int total = m_bookmarkStorage.Count();

   for(int i = 0; i < total; i++)
     {
      SBookmark bm = m_bookmarkStorage.GetBookmark(i);
      m_listBookmarks.AddItem(BookmarkDisplay(bm));
     }

//--- Reset selection - the caller will restore it if needed
   m_selectedIndex = -1;
  }

Recreating the control clears its previous selection state, so SelectBookmarkIndex() restores the required selection when necessary.

CBookmarkStorage remains the source of bookmark data, while CListView presents that data to the user.

Integration with the Existing Navigation Engine

Bookmark functionality is built around the navigation engine developed in the previous instalment. CBookmarkStorage manages persistence, while CNavigatorDialog handles bookmark interaction and passes stored locations to the existing navigation routines.

NavigateToDateTime() remains the central method for locating a historical bar. Direct date input and bookmark navigation both use this method.

CenterChartOnBar() performs the final chart positioning, so bookmark navigation does not require a separate scrolling mechanism.

m_lastNavigatedTime also connects date navigation with bookmark creation. When the navigator has already moved to a historical location, this timestamp can be stored directly as part of a new bookmark.

The two navigation paths converge on the same engine:

Reusing this path avoids duplicating the bar-search and chart-positioning logic developed in the previous instalment.

Modified Files

The bookmark functionality is distributed across three source files, with each file handling a specific part of the implementation.

File
Modification
CNavigatorDialog.mqh
Extended with bookmark controls, selection state, and methods for creating, selecting, navigating to, deleting, and refreshing bookmarks.
BookmarkStorage.mqh
Added as the persistence layer, containing SBookmark and CBookmarkStorage for CSV-based storage and validation.
HistoryNavigator.mq5
Updated to include the new storage header; the EA entry point and existing initialization flow remain unchanged.

The unchanged framework code, file headers, and include guards are not discussed here. The focus is on components introduced for persistent bookmarks.

Preserving Existing Navigation

Existing navigation controls retain their original purpose. The date and time fields still provide direct access to historical dates, while GO TO DATE and RETURN TO TODAY continue to perform their original functions.

Bookmarks add another way to revisit historical locations without changing this workflow. A user can enter a date for one-time analysis or save frequently revisited locations as bookmarks.

Implementation Structure

The implementation consists of three cooperating layers:

  1. SBookmark — represents the information required to reconstruct a saved historical location.
  2. CBookmarkStorage — manages the persistent CSV representation and keeps the bookmark collection synchronized with the file.
  3. CNavigatorDialog — provides the user interface, maintains selection state, and connects bookmark operations to the existing navigation engine.

Together, these layers separate data representation, persistence, and user interaction while reusing the existing historical navigation engine.


Testing

The completed History Navigator was tested for both date navigation and the new bookmark workflow. Testing focused on persistence, selection, navigation, and deletion, because these operations involve both the UI and the CSV storage layer.

The test sequence included creating bookmarks with descriptive names, displaying them in the bookmark list, selecting individual entries, navigating to their stored historical positions, and deleting selected bookmarks. The navigator was also removed and reattached to confirm that previously saved bookmarks were loaded again from the CSV file.

The GIF below demonstrates the resulting interface and the bookmark workflow. It shows a saved historical location displayed in the bookmark list, together with the controls used to navigate to or delete the selected bookmark.

The tests confirmed that the bookmark data is retained independently of the current dialog session and can be restored when the navigator is initialized again. The selected bookmark is also used as the target for the corresponding operation, allowing individual historical locations to be revisited or removed without affecting the remaining entries.

Overall, the testing confirms that the bookmark layer integrates with the existing History Navigator while preserving its original date-based navigation functionality.


Conclusion

In this article, we extended the existing History Navigator with persistent historical bookmarks, allowing important chart locations to be saved and revisited without repeatedly entering their dates and times.

The implementation introduced a dedicated SBookmark data structure and a CBookmarkStorage class for managing persistent CSV storage. The existing CNavigatorDialog was then enhanced with bookmark creation, selection, navigation, and deletion while retaining the original date-based navigation workflow.

A key design decision was to reuse the existing navigation engine rather than creating a separate mechanism for bookmark navigation. A bookmark therefore provides the stored symbol, timeframe, and datetime, while the existing navigation routines remain responsible for locating and positioning the chart.

The resulting navigator provides a practical way to preserve personally significant areas of market history for later analysis. Because the bookmarks survive application and terminal restarts, they can serve as a reusable reference for historical price-action studies, market-event analysis, and trade review.

This enhancement establishes a persistent foundation that can be extended further, for example with richer bookmark notes, categories, filtering, or additional historical-analysis features.

Attachments

NameTypePurpose
CNavigatorDialog.mqhIncludeImplements the navigator interface, bookmark controls, selection handling, and bookmark operations.
BookmarkStorage.mqhIncludeDefines the bookmark data model and manages persistent CSV storage, loading, validation, and deletion.
HistoryNavigator.mq5Expert AdvisorMain EA file that initializes and runs the History Navigator.
MQL5.zipArchiveContains the complete MQL5 source code and supporting files used in this article.
Attached files |
BookmarkStorage.mqh (10.89 KB)
MQL5.zip (11.07 KB)
Building a Bar Replay Tool in MQL5 Building a Bar Replay Tool in MQL5
This article shows how to build an interactive bar replay tool in MQL5 for MetaTrader 5 that reveals historical candles one by one without exposing future data. You will implement custom candles with DRAW COLOR CANDLES, an event-driven engine with OnChartEvent and OnTimer, a dashboard with Play/Pause, a draggable replay anchor, and Buy/Sell paper trading with SL/TP lines, while keeping the active bar in view to practice discretionary analysis and execution.
Market Simulation: Position View (IX) Market Simulation: Position View (IX)
In this turning-point article, we will begin to explore in greater depth the interaction between the applications we are developing to ensure full support for the replay/simulation system. Here we will analyze a problem that, on the one hand, is quite unpleasant, but on the other hand, is very interesting to explain and solve. The problem is this: how can we restore the take-profit and stop-loss lines after they have been deleted, and do so without using the terminal by performing the operation directly on the chart? At first glance, it seems simple. However, there are several obstacles that must be overcome.
From Basic to Intermediate: Queues, Lists, and Trees (II) From Basic to Intermediate: Queues, Lists, and Trees (II)
This is an article that you, dear reader, should study carefully. That is due to the nature of the material presented here. Although we have tried to present the material as simply and informatively as possible, the information provided here can certainly seem quite complex to those who are just beginning to learn programming. Nevertheless, this is no reason to lose heart or ignore what is explained here, as this article will establish a link between two completely different, though closely related, topics.
From Basic to Intermediate: Queues, Lists, and Trees (I) From Basic to Intermediate: Queues, Lists, and Trees (I)
In this article, we'll begin exploring a short series of concepts that are of immense importance to anyone who truly wants to learn how to program properly. Since this may seem very complicated at first—even though it is based on simple elements—we will go through the material step by step. So, let's start by figuring out what queues are.