chart-lookalike-transform
Find the affine transform — vertical scale a, offset b, and optional
inversion — that best maps a candidate ticker's price series onto a chart shape
you already recognize, then screen a list of tickers by how well they "look
like" that setup.
A chart's shape is invariant to vertical scale and shift: $10 → $11 over a
month traces the same curve as $100 → $110. So to ask "does NVDA right now
look like the cup-and-handle I'm watching for?" you fit
reference ≈ a · candidate + b (a < 0 ⇒ candidate is the mirror image)
by least squares (closed form), report the fit quality as a 0..1 similarity,
and overlay the transformed candidate on the reference with moving-average
indicators.
This is not dynamic time warping — the time axis is held fixed, so "lookalike" means the literal same shape over the same window, just rescaled vertically. (DTW would match shapes that drift in time; deliberately out of scope.)
Install
pip install -e ".[test]" # includes matplotlib for plotting + pytest
# or, runtime only:
pip install -e . # numpy only; add ".[plot]" for PNG overlays
Python ≥ 3.10. Core dependency is just NumPy; matplotlib is optional (only for
--plot).
Quick start (no network)
Everything works offline against deterministic synthetic data via --offline
(or CHART_LOOKALIKE_API=mock), so you can try it instantly:
# List the built-in reference shapes.
chart-lookalike shapes
# Fit one ticker to a recognized shape and print the transform.
chart-lookalike match AAPL --shape cup-with-handle --offline
# Screen a basket, keep the best 5, and use a real ticker as the reference shape.
chart-lookalike screen AAPL MSFT NVDA TSLA AMD --ref-ticker NVDA --offline --top 5
# Write a PNG overlay (transformed candidate + reference + SMAs).
chart-lookalike match TSLA --shape v-bottom --offline --plot match.png
Drop --offline to pull live daily closes from Yahoo Finance (no API key
needed). The data layer is provider-pluggable behind the CHART_LOOKALIKE_API
env var.
Reference shapes
Three ways to specify the target shape (choose exactly one):
| Flag | Meaning |
|---|---|
--shape <name> |
a built-in shape (chart-lookalike shapes lists them) |
--ref-csv <path> |
a file of reference prices (one number per line, or date,close rows — the last numeric field per line is used) |
--ref-ticker <sym> |
use another ticker's own series as the target shape |
Built-in shapes: v-bottom, inverse-v-top, cup, cup-with-handle,
head-and-shoulders, uptrend, downtrend, double-bottom.
How the fit works
For a reference t and candidate c (resampled to a common length), the
least-squares affine fit has a closed form:
a = cov(c, t) / var(c)
b = mean(t) − a · mean(c)
When inversion is allowed, the mirror candidate −c is also fit and the
lower-residual solution wins (a negative a ⇒ inverted = True). Fit quality:
rmse = sqrt( mean( (a·c + b − t)² ) )
similarity = clip( 1 − rmse / rms(t − mean(t)), 0, 1 )
similarity = 1.0 is a perfect shape match; 0.0 is no better than predicting a
flat line at the reference's mean.
Library API
import numpy as np
from chart_lookalike import fit_affine, apply_affine, get_provider, screen
from chart_lookalike.shapes import get_shape
provider = get_provider("mock") # or "yahoo"
series = provider.history("AAPL", days=180)
ref = get_shape("cup-with-handle")
fit = fit_affine(series.closes, ref) # AffineFit(scale, offset, inverted, rmse, similarity, n)
overlay = apply_affine(series.closes, fit) # candidate mapped onto the reference's units
ranked = screen(ref, ["AAPL", "MSFT", "NVDA"], provider, top=3)
for r in ranked:
print(r.symbol, round(r.similarity, 3), "inverted" if r.fit.inverted else "")
Live-data dependency & testing
The Yahoo provider hits query1.finance.yahoo.com/v8/finance/chart at runtime —
that endpoint is undocumented and rate-limited, so it is the one piece that can
break outside our control. The JSON parser (parse_yahoo_chart) is split out
as a pure function and unit-tested against a saved fixture
(fixtures/yahoo_aapl.json, including a null-close hole and the error envelope) —
the test suite never touches the network.
pytest -q
Disclaimer
This is a charting/screening aid, not investment advice. A high similarity score means two curves have a similar shape over a window — nothing about what happens next.
License
MIT — see LICENSE.
Metadata
Release files for chart-lookalike-transform 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| chart_lookalike_transform-0.1.0.tar.gz | 24.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| chart_lookalike_transform-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 43.5 kB
Release files / chart_lookalike_transform-0.1.0.tar.gz
| Download URL | chart_lookalike_transform-0.1.0.tar.gz |
|---|---|
| Size | 24.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
65dfd5786e9ea4ab9945df41bc24a74fd8acb1fe24afa0febf6eb631fbee05a8
|
|
BLAKE2b-256 checksum How to use checksums |
a2651e93ca40b851694d180a1c1c318245e6a615186b6782cf4766483b379cd6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / chart_lookalike_transform-0.1.0-py3-none-any.whl
| Download URL | chart_lookalike_transform-0.1.0-py3-none-any.whl |
|---|---|
| Size | 19.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1ba7898d92082b53cff0e7b786c5ce55cae654c26626151d9fbf19fbdaab740f
|
|
BLAKE2b-256 checksum How to use checksums |
5aed06227c89621616989e2fe4c37acce31f36571416d2f39b62fbbd88589579
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Security for chart-lookalike-transform 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.