Shape of Price: An Introduction to TDA and Takens Embedding in MQL5
Contents
- Introduction: Why Shape Matters
- A Point Cloud on a Chart
- Topology, Not Geometry
- The Takens Embedding Theorem
- Implementation: CTDAPointCloud
- Implementation: CTDADistance
- The EnsureCapacity Pattern
- Example: Embedding a Live Chart Window
- What's Next
- Conclusion
Introduction: Why Shape Matters
Moving averages see level. RSI sees range. Bollinger bands see volatility. None of them see shape.
Consider two one-hour EURUSD windows. In the first, price drifts upward in small mean-reverting steps. In the second, price oscillates around a flat mean. The two windows can be tuned to share almost everything an ordinary chart shows. Mean close: the same to the pip. Average range: the same. Realized volatility: the same. Number of bars above the moving average: the same. A trader who only sees those numbers cannot tell the two windows apart. The geometry of how price moves through its own past, however, is completely different.
| Quantity | Trending window | Ranging window |
|---|---|---|
| Mean close | 1.08423 | 1.08423 |
| Average bar range | 9.7 pips | 9.7 pips |
| Standard deviation of close | 0.00042 | 0.00042 |
| Bars above SMA(20) | 41 | 40 |
| Shape of phase-space trajectory | Elongated arc | Closed loop |
Every row above except the last is something a standard indicator sees. The last row is what the rest of this article unlocks.
Topological Data Analysis asks different questions about a time series. It focuses on the trajectory's shape and its stability under perturbations. The answer comes back as a small set of birth-death pairs called a persistence diagram. Each pair encodes a feature of the data, a connected component or a loop or a higher-dimensional void, together with the scale at which the feature appears and the scale at which it disappears.
This first article does not yet trade. It does not yet produce a persistence diagram. It builds the two classes that turn a one-dimensional price series into a cloud of points in higher-dimensional space and equips that cloud with a notion of distance. Those are the inputs the rest of the library consumes.
A Point Cloud on a Chart

Fig. 1. Two windows of equal mean and volatility embed into very different clouds: a trending window into an elongated arc, a ranging window into a closed loop
The diagram above sketches the idea before any code. It contrasts two windows of the same length embedded into three-dimensional phase space. The left cloud stands for a trending window, where price drifts in one direction with small mean-reverting steps. The right cloud stands for a ranging window, where price oscillates inside a narrow band. Two such windows can be tuned to share almost identical means and volatilities, yet their point clouds do not look similar at all.
The trending cloud stretches out along a curved arc. The ranging cloud collapses onto a tight, near-circular tangle. A persistence calculation on each would assign them very different topological signatures. A standard indicator that only saw the closing prices would assign them very similar readings.
That is what this article delivers. The rest of it explains how.
Topology, Not Geometry
The word topology gets used loosely. In a TDA context the meaning is specific and worth pinning down before any code appears.
Geometry studies distances, angles, and shapes that depend on a notion of size. A circle of radius one is geometrically different from a circle of radius ten. Topology studies properties that are invariant under continuous deformation. A circle of radius one and a circle of radius ten are topologically the same. So is a square. So is a long oval. So is a triangle with rounded corners. All of them have exactly one connected component and exactly one loop.
The standard illustration is the donut and the coffee cup. A coffee cup can be continuously deformed into a donut without cutting or gluing, because both have exactly one hole (the handle of the cup, and the hole through the middle of the donut). A sphere cannot be deformed into a donut, because there is no continuous way to make a hole. The number of holes is a topological invariant. The size of the donut is not.
For a discrete cloud of points, the relevant invariants are counted at every scale. At a very small scale, every point is its own connected component and there are no loops. At a very large scale, all points are connected to all others, and the cloud looks like one solid blob. In between, interesting things happen. A circle of sampled points becomes connected before it becomes filled in, leaving a loop alive across a range of scales. A genuine cluster becomes one connected component very early and stays that way. A random cloud generates many short-lived loops and no long-lived ones.
Persistent homology is the formal machinery for tracking this. Each topological feature, whether a connected component or a loop, gets a birth scale (the smallest distance threshold at which it appears) and a death scale (the smallest threshold at which it disappears). Long-lived features are the signal. Short-lived features are noise. The diagram of birth and death pairs is the output.
For price series, this matters because most conventional tools are coordinate-bound. They measure levels or widths at a fixed scale rather than how structure changes across scales. None of them tracks how features evolve continuously as a scale parameter changes; they each pick one scale and report a value. Persistence is scale-invariant by construction. The same shape rescaled by a constant factor produces a persistence diagram with all birth and death values multiplied by the same constant, and every feature still present.
One disclaimer is in order before going further. Topology is a lens, not a complete description. It throws away a lot of information that other indicators care about. The right way to think about a TDA-derived feature is as a complement to existing tools, capturing something they cannot. The fourth article in this series will show exactly what that something is, and how to read it on a chart.
The Takens Embedding Theorem
A price series is one number per bar. Topology lives in higher dimensions. The bridge between the two is a construction Floris Takens proved correct in 1981, called the time-delay embedding.
The idea is mechanical. Given a sequence x[0], x[1], x[2], ... and two parameters, an embedding dimension d and a delay tau, build a sequence of vectors:
v_i = ( x[i], x[i + tau], x[i + 2*tau], ..., x[i + (d-1)*tau] )
Each vector lives in d-dimensional space. The first coordinate is the current value, the second is the value tau bars earlier (or later, depending on convention), and so on. The result is a cloud of N = L - (d-1)*tau points in R^d, where L is the length of the input series.
A small concrete example fixes the construction. Suppose the input is the eight-element series
x = [ 10, 12, 15, 13, 11, 14, 17, 16 ]
with embedding dimension d = 3 and delay tau = 1. The formula N = L - (d-1)*tau gives N = 8 - 2 = 6 embedded points. Each point picks three values from x, one bar apart:
| i | v_i = ( x[i], x[i+1], x[i+2] ) |
|---|---|
| 0 | ( 10, 12, 15 ) |
| 1 | ( 12, 15, 13 ) |
| 2 | ( 15, 13, 11 ) |
| 3 | ( 13, 11, 14 ) |
| 4 | ( 11, 14, 17 ) |
| 5 | ( 14, 17, 16 ) |
Six points in three-dimensional space. The original one-dimensional series has been unfolded into a cloud whose geometry the rest of the library can analyze. Notice that each successive point shares two of three coordinates with its predecessor: the cloud is a discrete curve, not a random scatter. A larger delay would space the coordinates further apart and produce a less curved cloud; a different embedding dimension would lift the cloud into a different number of dimensions. The shape of the cloud is what topology will operate on.

Fig. 2. Takens time-delay embedding: a sliding window over the 1D series produces one vector per position, lifting the series into phase space
What Takens proved is non-obvious. Under reasonable conditions, the topological properties of this delay-embedded cloud are the same as the topological properties of the original dynamical system that produced the series. A series produced by a noisy circle in phase space yields a delay-embedded cloud whose topology is a noisy circle. A series produced by a chaotic attractor yields a cloud that traces out the attractor. A pure trend yields a cloud that drifts in one direction. The one-dimensional projection (the series itself) hides the shape; the embedding recovers it.
For market data the strong form of the theorem does not apply directly. Prices are not a clean dynamical system, and any conclusions are statistical rather than deterministic. What the embedding does give us is a principled way to turn a windowed series into a cloud of points whose geometry reflects the local dynamics. Trending and mean-reverting windows produce visibly different clouds. Volatile and quiet windows produce visibly different clouds. The persistence diagram makes those differences quantitative.
Two parameters must be chosen: embedding dimension d and delay tau. The embedding dimension d controls how much memory the construction folds into each vector. Typical choices are 2 (cheap, for visualization) or 3 (the default in this library). Higher dimensions can in principle separate more topologies but cost more in distance computation and in the size of the simplicial complex downstream. The delay tau controls how spread out the lagged samples are. A delay of 1 uses consecutive bars; a larger delay uses bars further apart. The standard heuristic for stationary series is to choose tau as the first zero of the autocorrelation or the first minimum of the average mutual information; for our purposes the default value 1 is a reasonable starting point and easy to reason about.
The library does not try to be clever about parameter selection. It exposes embDim and delay as user-controlled inputs. The fourth article returns to the tuning question with empirical guidance on EURUSD windows.
Implementation: CTDAPointCloud
The first class in the library is CTDAPointCloud. It takes a one-dimensional series and produces the Takens-embedded cloud. The class is intentionally small. It stores the cloud as a flattened array, exposes accessors for downstream consumers, and does no analysis of its own.
//+------------------------------------------------------------------+ //| CTDAPointCloud - Takens time-delay embedding of a series | //| | //| Given a series x[0..L-1], dimension d and delay tau, it | //| produces N = L - (d-1)*tau vectors: | //| v_i = ( x[i], x[i+tau], ..., x[i+(d-1)*tau] ) | //+------------------------------------------------------------------+ class CTDAPointCloud { private: double m_points[]; // flattened [N x embDim] int m_N; // number of embedded points int m_embDim; // embedding dimension int m_delay; // time delay tau public: bool Build(const double &series[], int seriesLen, int embDim = 3, int delay = 1); int Size() const { return m_N; } int Dim() const { return m_embDim; } int Delay() const { return m_delay; } bool Get(int idx, double &coords[]) const; double GetCoord(int idx, int d) const; void GetRaw(double &out[]) const { ArrayCopy(out, m_points); } };
The storage choice is a single flat double array of length N times embDim. The point at index i has its d-th coordinate at offset i*embDim + d. This layout has one important property: it is cache-friendly. The downstream distance computation reads points pair by pair, and reading two contiguous strides is faster than chasing pointers into a jagged 2D array. MQL5 does not give us native 2D arrays of variable shape, so the flat-array approach is also the natural one.
The accessors come in two forms. Get(idx, coords[]) copies a full point into a caller-supplied array, which is convenient when the caller wants to manipulate the whole point at once. GetCoord(idx, d) returns a single coordinate by value, which is what the distance calculation actually needs and which avoids any array allocation on every read.
The build method is where the embedding is realized.
//+------------------------------------------------------------------+ //| Build N = seriesLen - (embDim-1)*delay embedded vectors | //+------------------------------------------------------------------+ bool CTDAPointCloud::Build(const double &series[], int seriesLen, int embDim, int delay) { if(seriesLen < 2 || embDim < 1 || delay < 1) { Print("TDAPointCloud::Build - invalid parameters"); return false; } m_embDim = embDim; m_delay = delay; m_N = seriesLen - (embDim - 1) * delay; if(m_N <= 0) { Print("TDAPointCloud::Build - series too short for given embedding"); m_N = 0; return false; } ArrayResize(m_points, m_N * m_embDim); for(int i = 0; i < m_N; i++) for(int d = 0; d < m_embDim; d++) m_points[i * m_embDim + d] = series[i + d * m_delay]; return true; }
The parameter check at the top guards against three different mistakes: a series of one element or less, a non-positive embedding dimension, and a non-positive delay. Each of these would silently produce nonsense further down, so they are rejected explicitly with a printed message.
The size formula N = seriesLen - (embDim - 1) * delay is the key line. With dimension d and delay tau, the last valid vector is the one whose final coordinate lands on the last bar. That bar is at index i + (d-1)*tau, and for it to equal seriesLen - 1 we need i to be at most seriesLen - 1 - (d-1)*tau. The number of valid starting points is one more than that maximum, so N = seriesLen - (d-1)*tau. If this number is zero or negative the input series is too short for the requested embedding and the method returns false.
The double loop fills the flat array. Note that the cloud size is known the moment the parameters are validated. There is no incremental growth, no append-as-we-go. A single ArrayResize allocates the entire buffer once, and the loop writes into it directly. This is the simplest performance pattern in the library and it works here because the geometry of the embedding tells us up front exactly how many points we will produce.
Implementation: CTDADistance
The second class is CTDADistance. It takes a point cloud and produces the full pairwise distance matrix. Every entry D[i][j] is the distance between points i and j under a chosen norm. The matrix is symmetric, has zero on the diagonal, and is stored as a flattened NxN array of doubles.
Why use the full matrix rather than a thresholded one? A naive approach might store a binary matrix that simply records, at some fixed scale, which pairs of points are within range of each other. Persistent homology asks a strictly stronger question: as the scale parameter epsilon grows continuously from zero, at exactly what value does each pair first cross the threshold. Persistent homology needs the actual distances between all pairs. A binary in-range matrix at a single scale is insufficient. The price of that precision is O(N^2) memory and O(N^2) construction time. For the window sizes typical in trading applications (50 to 200 bars) this is a few thousand doubles and finishes in milliseconds. For larger clouds it becomes the dominant cost in the pipeline.
//+------------------------------------------------------------------+ //| Distance norms: Chebyshev, Euclidean, Manhattan | //+------------------------------------------------------------------+ enum ENUM_TDA_NORM { TDA_NORM_MAX = 0, // Maximum norm (Chebyshev) TDA_NORM_EUCLIDEAN = 1, // Euclidean norm TDA_NORM_MANHATTAN = 2 // Manhattan (L1) norm };
The three norms are not interchangeable. Euclidean is the default and is what most theoretical results in persistent homology assume; it produces the smoothest persistence diagrams under small perturbations of the input. Manhattan (the sum of absolute coordinate differences) is more robust to outliers because no single coordinate dominates the distance. Chebyshev (the maximum of absolute coordinate differences) treats the embedding as a hypercube neighborhood and is the cheapest of the three to compute. The choice is exposed as an enum so the caller can experiment; the verification results presented in the third article assume Euclidean unless explicitly noted.

Fig. 3. The unit ball of each norm in two dimensions: a circle for Euclidean, a tilted square for Manhattan, an axis-aligned square for Chebyshev
The image above shows the unit ball of each norm in two dimensions. A point at Euclidean distance one from the origin lies on a circle. A point at Manhattan distance one lies on a tilted square. A point at Chebyshev distance one lies on an axis-aligned square. The same pair of points generally has different distances under different norms, and the persistence diagram that results is sensitive to that choice.
//+------------------------------------------------------------------+ //| CTDADistance - full NxN pairwise distance matrix of a cloud | //| | //| The full matrix lets the Vietoris-Rips filtration grow | //| epsilon continuously and record every birth/death event. | //| Symmetric, zero diagonal, stored as a flattened NxN array. | //+------------------------------------------------------------------+ class CTDADistance { private: double m_D[]; // flattened NxN distance matrix int m_N; // number of points ENUM_TDA_NORM m_norm; // distance norm double m_maxDist; // max pairwise distance double ComputePairDistance(const CTDAPointCloud &cloud, int i, int j) const; public: bool Build(const CTDAPointCloud &cloud, ENUM_TDA_NORM norm = TDA_NORM_EUCLIDEAN); double Get(int i, int j) const; int Size() const { return m_N; } ENUM_TDA_NORM Norm() const { return m_norm; } double MaxDistance() const { return m_maxDist; } };
The interface mirrors the point cloud's: a single Build method that consumes a CTDAPointCloud and a norm choice, and a small set of accessors. MaxDistance is worth a note. The Vietoris-Rips filtration in the next article walks epsilon from zero up to this maximum, because at any larger epsilon every pair is already connected and nothing topologically interesting can happen. Pre-computing it during the build is essentially free (one comparison per pair) and saves a full pass later.
//+------------------------------------------------------------------+ //| Distance between point i and point j under the selected norm | //+------------------------------------------------------------------+ double CTDADistance::ComputePairDistance(const CTDAPointCloud &cloud, int i, int j) const { int dim = cloud.Dim(); double dist = 0.0; for(int d = 0; d < dim; d++) { double diff = cloud.GetCoord(i, d) - cloud.GetCoord(j, d); switch(m_norm) { case TDA_NORM_MAX: dist = MathMax(dist, MathAbs(diff)); break; case TDA_NORM_MANHATTAN: dist += MathAbs(diff); break; case TDA_NORM_EUCLIDEAN: default: dist += diff * diff; break; } } if(m_norm == TDA_NORM_EUCLIDEAN) dist = MathSqrt(dist); return dist; }
The per-pair routine walks the embedding dimensions, accumulates the running distance according to the chosen norm, and applies the final square root only for the Euclidean case. Pulling the square root outside the loop saves time and avoids accumulating rounding error inside the sum. The default branch covers any unrecognized norm by falling through to Euclidean, which keeps the call safe even if a caller passes a stray integer.
//+------------------------------------------------------------------+ //| Build the full pairwise distance matrix | //+------------------------------------------------------------------+ bool CTDADistance::Build(const CTDAPointCloud &cloud, ENUM_TDA_NORM norm) { m_N = cloud.Size(); m_norm = norm; if(m_N < 2) { Print("TDADistance::Build - need at least 2 points"); return false; } ArrayResize(m_D, m_N * m_N); m_maxDist = 0.0; for(int i = 0; i < m_N; i++) { m_D[i * m_N + i] = 0.0; for(int j = i + 1; j < m_N; j++) { double d = ComputePairDistance(cloud, i, j); m_D[i * m_N + j] = d; m_D[j * m_N + i] = d; // symmetric if(d > m_maxDist) m_maxDist = d; } } return true; }
The outer loop iterates over the upper triangle of the matrix only, j strictly greater than i. The diagonal is set to zero explicitly. For each pair, the computed distance is written into both the (i,j) and (j,i) cells so that downstream lookups can index either way without thinking about which is the smaller index. The maximum-distance tracker updates on every pair.
The Get accessor performs a bounds check and returns the entry from the flat array. The check is cheap and prevents the kind of silent out-of-range read that would otherwise produce a misleading distance value of zero or garbage.
A worked example fixes the layout. Take three points in two dimensions:
p0 = (0, 0), p1 = (3, 0), p2 = (0, 4)
Under the Euclidean norm the pairwise distances are 3 between p0 and p1, 4 between p0 and p2, and 5 between p1 and p2 (the classic 3-4-5 right triangle). The full matrix produced by Build looks like this:
| p0 | p1 | p2 | |
|---|---|---|---|
| p0 | 0 | 3 | 4 |
| p1 | 3 | 0 | 5 |
| p2 | 4 | 5 | 0 |
The matrix is symmetric and has zero on the diagonal. The maximum entry is 5, so m_maxDist would be 5 after Build returns. In the flattened storage with N = 3, the value at logical row 1 column 2 lives at offset 1*3 + 2 = 5 of the internal m_D array. The mirror copy at logical row 2 column 1 lives at offset 2*3 + 1 = 7. Build writes both, so downstream lookups can index either way without thinking about which is the smaller index. MQL5 lacks native variable-shape 2D arrays, so we use a flat array. The benefit is a single contiguous allocation that is cache-friendly and easy to bulk-copy.
The EnsureCapacity Pattern
Both classes in this article use a single ArrayResize call. The point cloud knows its size from the formula N = seriesLen - (embDim - 1) * delay. The distance matrix knows its size from N * N. In both cases there is no growth, just one allocation up front.
The rest of the library is not so lucky. The Vietoris-Rips filtration in the next article enumerates simplices on the fly: every vertex, every edge whose length is below epsilon, every triangle whose three edges are all below epsilon. The total number of edges in a 200-point cloud can be up to 19,900. The number of triangles can reach 1.3 million. There is no clean up-front formula. Each simplex is discovered, evaluated, and either accepted or rejected during the enumeration.
The naive pattern for that situation is to start with an empty array and call ArrayResize(arr, count + 1) every time a new simplex is accepted. This is also catastrophic for performance. ArrayResize on a managed array copies all existing entries into a new buffer of the requested size, so every append is O(N) and a sequence of N appends is O(N^2). On a 200-point cloud at maxDim 2 this single mistake turns a sub-second computation into a sixty-second one.
The fix is a textbook doubling pattern, adapted to MQL5's container semantics.
//+------------------------------------------------------------------+ //| Grow a dynamic array by capacity doubling (amortised push) | //+------------------------------------------------------------------+ void EnsureCapacity(int needed) { if(needed <= m_capacity) return; int newCap = (m_capacity == 0 ? 256 : m_capacity); while(newCap < needed) newCap *= 2; ArrayResize(m_arr, newCap); m_capacity = newCap; }
The class keeps two counts: m_count, the number of entries actually populated, and m_capacity, the size of the underlying array. EnsureCapacity is called before every append with the new required count. If the existing capacity is enough, nothing happens. Otherwise the capacity doubles until it covers the requested size and ArrayResize runs once, not once per append.
A Clear method (not shown here) resets m_count to zero but leaves m_capacity in place, so a reused container does not pay for reallocation on its second use. This is the difference between an indicator that recomputes once per bar and an indicator that quietly leaks growing allocations.
This pattern is not used by the two classes in this article because their sizes are known up front. It is introduced here so that the moment it appears in the Vietoris-Rips and boundary classes in the next article, the reader knows exactly what it is doing and why. Any new container code added to the library should follow the same pattern.
Example: Embedding a Live Chart Window
The two classes already do something visible. The attached script TDA_Demo.mq5 pulls a window of closes off the current chart, calls Build on the point cloud, calls Build on the distance matrix, and prints what comes out. It is the smallest complete program that exercises both classes in this article.
//+------------------------------------------------------------------+ //| TDA_Demo.mq5 | //| TDA Library for MQL5 | //| TDA_Demo: Takens embedding + pairwise distance matrix | //+------------------------------------------------------------------+ #include <TDA/TDAPointCloud.mqh> #include <TDA/TDADistance.mqh> input int InpWindow = 80; // bars of close history to embed input int InpEmbDim = 3; // embedding dimension d input int InpDelay = 1; // time delay tau void OnStart() { double closes[]; int copied = CopyClose(_Symbol, _Period, 0, InpWindow, closes); if(copied != InpWindow) { PrintFormat("TDA_Demo: needed %d closes, got %d - aborting", InpWindow, copied); return; } //--- Takens embedding CTDAPointCloud cloud; if(!cloud.Build(closes, InpWindow, InpEmbDim, InpDelay)) return; //--- pairwise distance matrix CTDADistance dist; if(!dist.Build(cloud, TDA_NORM_EUCLIDEAN)) return; PrintFormat("Cloud size %d, dim %d, max distance %.6f", cloud.Size(), cloud.Dim(), dist.MaxDistance()); //--- show the first few embedded points int show = (cloud.Size() < 4 ? cloud.Size() : 4); for(int i = 0; i < show; i++) { double coords[]; cloud.Get(i, coords); string s = ""; for(int d = 0; d < cloud.Dim(); d++) s += StringFormat("%s%.5f", (d == 0 ? "" : ", "), coords[d]); PrintFormat(" v[%d] = ( %s )", i, s); } //--- sample distances from the matrix if(cloud.Size() >= 3) PrintFormat(" d(0,1) = %.6f d(0,2) = %.6f d(1,2) = %.6f", dist.Get(0, 1), dist.Get(0, 2), dist.Get(1, 2)); }
With the default inputs, an 80-bar window with embedding dimension 3 and delay 1 produces a cloud of 80 minus 2, that is 78 points in R^3. The max distance reported by the distance matrix is the diameter of the cloud, the upper bound that the Vietoris-Rips filtration will walk to. On a quiet EURUSD H1 window this might be on the order of 0.002 (in price units); on a volatile window it might be ten times that. The persistence diagram, when it arrives in the third article, scales with this number. The first few printed vectors make the overlapping-window structure of the embedding concrete: each one shares two of its three coordinates with the line above it.

Fig. 4. An 80-bar close series (top) and the same data lifted into 3D by the delay embedding (bottom); the trajectory shape is invisible in 1D but explicit in phase space
The diagram above pairs an input series with its embedding. The top panel is an 80-bar close series. The bottom panel shows the same data lifted into three dimensions by the delay embedding. The shape of the price trajectory, which is invisible in the one-dimensional view, becomes the geometry the rest of the library will operate on.
A trending window produces a cloud that drifts. A ranging window produces a cloud that recirculates. A volatile window produces a cloud that is loose and spread out. These differences are not yet quantitative; the third article will assign them numerical signatures via persistent homology. For now it is enough to see that the embedding is doing something visible.
What's Next
We have a cloud. A cloud is not a shape yet. The next article in the series builds the Vietoris-Rips filtration that turns the cloud into a sequence of simplicial complexes parametrized by a scale epsilon.
- Simplices: vertex, edge, triangle, and how they fit together.
- The filtration as a function of epsilon, with the same cloud shown at three different scales.
- CTDARips, the class that enumerates simplices with O(1) lookup tables and the EnsureCapacity pattern introduced above.
- CTDABoundary, the sparse matrix over Z/2 that encodes face relations and prepares the cloud for persistence computation.
The third article reduces the boundary matrix and emits the persistence diagram. The fourth article uses persistence entropy as a market regime indicator. The library is also numerically verified in the third article, with the verification results discussed there rather than here.
Conclusion
This article opened a new series on Topological Data Analysis for MQL5 and built the first two classes of the library.
- CTDAPointCloud embeds a one-dimensional price series into phase space using the Takens time-delay method. The cloud has N = seriesLen minus (embDim minus 1) times delay points in R^embDim.
- CTDADistance computes the full pairwise distance matrix under one of three norms (Euclidean, Manhattan, Chebyshev) and records the maximum pairwise distance for the filtration to follow.
Topology is a different question than the one indicators usually ask. The Takens embedding is the bridge between a one-dimensional series and a topological object. A pairwise distance matrix is the input to every step that follows. The two classes built here are the foundation the next three articles stand on.
Only the files built and explained in this article are attached, so the download matches what you have just read. The remaining classes of the library, the Vietoris-Rips filtration, the boundary matrix, the column reduction, the persistence diagram, and the unified facade, are introduced in the later articles as the topology pipeline is constructed around the two foundations laid here.
| # | Filename | Type | Description |
|---|---|---|---|
| 1 | TDAPointCloud.mqh | Header | Takens phase-space embedding of a 1D price series |
| 2 | TDADistance.mqh | Header | Full pairwise distance matrix under three norms |
| 3 | TDA_Demo.mq5 | Script | Runnable demo: embeds chart closes and prints the cloud and distances |
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.
Community of Scientists Optimization (CoSO): Theory
MQL5 Trading Tools (Part 37): Adding a Per-Object Property-Editing Ribbon to the Canvas Drawing Layer
Features of Experts Advisors
Price Action Analysis Toolkit Development (Part 73): Building a Weekend Gap Trading Signal System in MQL5
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use