Normalized Template Matching
Install and import#
npm install fintech-algorithmsimport { match } from "fintech-algorithms/geometric-chart-patterns/pattern-matching/normalized-template-matching";Signature#
match(series, template)Slides a template over a series and finds the window whose shape is closest to it. Both the template and every candidate window are z-normalized with the population standard deviation first, so the match is on shape alone and is blind to level and scale.
Parameters#
| Name | Type | Notes |
|---|---|---|
series | number[] | The series to search, in order. Needs at least three finite values and must be no shorter than the template. min_length: 3 · finite: true nulls: reject |
template | number[] | The shape being looked for. Needs at least three finite values, and cannot be constant, since a constant sequence has no z-score. min_length: 3 · finite: true nulls: reject |
Returns#
{ template_length: number; best_start: number; best_end: number; correlation: number | null; distance: number | null; profile: Array<{ start: number; correlation: number | null; distance: number | null; eligible: boolean }>; normalization: string }
An object naming the winning window by best_start and best_end with its correlation and Euclidean distance in normalized space, alongside template_length, the full profile of one row per window start, and normalization, which is always population-z-score-per-window. A window that is constant is scored eligible: false with null correlation and distance; the winner is the eligible window of least distance, earliest start breaking ties.
Errors#
- When series or template holds fewer than three values, or any value is not a finite number — throws Error
- When template is longer than series — throws Error
- When template is constant, so its z-normalization is undefined — throws Error
- When every window of series is constant, leaving nothing eligible — throws Error
Complexity: time O(n * m),
space O(n + m).
Worked example#
executed This entry is a thin wrapper its test never calls directly, so it was invoked with the arguments its shared implementation received. Real output, not asserted against a published figure.
Input#
[99.874987, 100.159243, 100.421014, 100.350703, 100.560173, 100.558238]Showing 6 of 96 elements.
[0, 1, 2.5, 4.5, 6, 5]Showing 6 of 12 elements.
Call#
match(series, template)Returns#
object with 7 fields: template_length, best_start, best_end, correlation, distance, profile, normalization
{
"template_length": 12,
"best_start": 58,
"best_end": 69,
"correlation": 0.9999999999999999,
"distance": 8.382057464382987e-15,
"profile": [
{
"start": 0,
"correlation": 0.5900738849254371,
"distance": 3.136594771689436,
"eligible": true
},
{
"start": 1,
"correlation": 0.3358103596358022,
"distance": 3.9925620056225477,
"eligible": true
},
{
"start": 2,
"correlation": -0.07196549176585781,
"distance": 5.072195954651257,
"eligible": true
}
],
"normalization": "population-z-score-per-window"
}Diagrams#
Calculation flow#
Reasoning Flow — Normalized Template Matching
flowchart LR
A["Validate sequence roles and clocks"] --> B["Apply declared local normalization"]
B --> C["Enumerate eligible search space"]
C --> D["Calculate distance, path, profile, or gain"]
D --> E{"Candidate eligible?"}
E -->|No| R["INELIGIBLE with reason"]
E -->|Yes| F["Apply exclusion, radius, threshold, and tie rules"]
F --> G["Return result plus complete diagnostics"]
G --> H["Evaluate separately from financial usefulness"]
How it works#
This page states the contract — how to call it correctly. The article explains the concept: why it works, and where it breaks.
References#
- Foundations of Technical Analysis — Andrew W. Lo, Harry Mamaysky, and Jiang Wang
- SciPy `find_peaks` — SciPy project
- The Probability of Backtest Overfitting — David H. Bailey, Jonathan M. Borwein, Marcos López de Prado, and Qiji Jim Zhu
- UCR Suite for Time-Series Subsequence Search — Eamonn Keogh and the University of California, Riverside time-series group
- Applicability decision