Find the affine transform that maps a candidate ticker's price series onto a familiar chart shape, and screen tickers that 'look like' a setup.
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file chart_lookalike_transform-0.1.0.tar.gz.
File metadata
- Download URL: chart_lookalike_transform-0.1.0.tar.gz
- Upload date:
- Size: 24.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65dfd5786e9ea4ab9945df41bc24a74fd8acb1fe24afa0febf6eb631fbee05a8
|
|
| MD5 |
4815f923ae2c900f72c6927b4cff83da
|
|
| BLAKE2b-256 |
a2651e93ca40b851694d180a1c1c318245e6a615186b6782cf4766483b379cd6
|
File details
Details for the file chart_lookalike_transform-0.1.0-py3-none-any.whl.
File metadata
- Download URL: chart_lookalike_transform-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ba7898d92082b53cff0e7b786c5ce55cae654c26626151d9fbf19fbdaab740f
|
|
| MD5 |
8805a79743729e7c83f6c703108ac7a9
|
|
| BLAKE2b-256 |
5aed06227c89621616989e2fe4c37acce31f36571416d2f39b62fbbd88589579
|