web: charts — catalogue encyclopédique des 60 indicateurs et 42 outils de dessin (src/charts/catalog : fiches complètes, formules, défauts expliqués, lecture, pièges, histoire ; recherche/tri ; test de complétude contre les registres du moteur)
10 changed files +2,000 −0
added
hfmarketdata/web/src/charts/catalog/ChartFigure.jsx
+52 −0
@@ -0,0 +1,52 @@ | ||
| 1 | +// Theme-aware thumbnails rendered by OUR engine (public/charts/thumbs, made by scripts/charts-thumbs.mjs). Shared by | |
| 2 | +// the /charts page (library cards, tooltips, About tab) and the generated docs pages (MDX component <ChartFigure>). | |
| 3 | +import React, { useEffect, useState } from 'react' | |
| 4 | +import { loadIndicators, loadTools, thumbUrl } from './index.js' | |
| 5 | + | |
| 6 | +/** 'dark' | 'light' — follows the site's data-theme attribute live. */ | |
| 7 | +export function useSiteTheme() { | |
| 8 | + const read = () => (typeof document !== 'undefined' && document.documentElement.getAttribute('data-theme') === 'light' ? 'light' : 'dark') | |
| 9 | + const [theme, setTheme] = useState(read) | |
| 10 | + useEffect(() => { | |
| 11 | + if (typeof MutationObserver === 'undefined') return undefined | |
| 12 | + const mo = new MutationObserver(() => setTheme(read())) | |
| 13 | + mo.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }) | |
| 14 | + return () => mo.disconnect() | |
| 15 | + }, []) | |
| 16 | + return theme | |
| 17 | +} | |
| 18 | + | |
| 19 | +/** Lazy-loaded catalogue (object keyed by id) or null while loading. `kind` = 'indicator' | 'tool' | null (do not load yet). */ | |
| 20 | +export function useCatalog(kind) { | |
| 21 | + const [entries, setEntries] = useState(null) | |
| 22 | + useEffect(() => { | |
| 23 | + if (!kind) return undefined | |
| 24 | + let alive = true | |
| 25 | + ;(kind === 'tool' ? loadTools() : loadIndicators()).then(m => { if (alive) setEntries(m) }, () => { if (alive) setEntries({}) }) | |
| 26 | + return () => { alive = false } | |
| 27 | + }, [kind]) | |
| 28 | + return kind ? entries : null | |
| 29 | +} | |
| 30 | + | |
| 31 | +/** | |
| 32 | + * <Thumb kind="indicator" id="rsi" size="sm|lg" /> — lazy <img> of the generated thumbnail for the current theme. | |
| 33 | + * Falls back to an empty frame (same aspect ratio) when the file is missing so layouts never jump. | |
| 34 | + */ | |
| 35 | +export function Thumb({ kind, id, size = 'sm', alt = '', className = '', theme, eager = false, ...rest }) { | |
| 36 | + const site = useSiteTheme() | |
| 37 | + const t = theme || site | |
| 38 | + const [failed, setFailed] = useState(false) | |
| 39 | + useEffect(() => { setFailed(false) }, [kind, id, t, size]) | |
| 40 | + if (failed) return <span className={`ch-thumb ch-thumb-missing ${className}`} aria-hidden="true" data-testid="ch-thumb-missing" /> | |
| 41 | + return <img className={`ch-thumb ${className}`} src={thumbUrl(kind, id, t, size)} width={size === 'lg' ? 960 : 480} height={size === 'lg' ? 540 : 270} loading={eager ? 'eager' : 'lazy'} decoding="async" alt={alt} onError={() => setFailed(true)} data-testid="ch-thumb" {...rest} /> | |
| 42 | +} | |
| 43 | + | |
| 44 | +/** Docs figure: large thumbnail + caption, used by the generated MDX pages. */ | |
| 45 | +export function ChartFigure({ kind = 'indicator', id, caption, alt }) { | |
| 46 | + return ( | |
| 47 | + <figure className="figure ch-figure" data-testid="ch-figure"> | |
| 48 | + <Thumb kind={kind} id={id} size="lg" alt={alt || caption || `${id} on a daily chart`} eager /> | |
| 49 | + {caption && <figcaption>{caption}</figcaption>} | |
| 50 | + </figure> | |
| 51 | + ) | |
| 52 | +} | |
added
hfmarketdata/web/src/charts/catalog/catalog.test.js
+77 −0
@@ -0,0 +1,77 @@ | ||
| 1 | +// Completeness and consistency of the encyclopedic catalogue against the engine registries. | |
| 2 | +import { test } from 'node:test' | |
| 3 | +import assert from 'node:assert/strict' | |
| 4 | +import { INDICATOR_TYPES, REGISTRY } from '../indicators/index.js' | |
| 5 | +import { TOOLS, POINT_COUNT } from '../engine/drawings/model.js' | |
| 6 | +import indicators from './indicators/all.js' | |
| 7 | +import tools from './tools.js' | |
| 8 | +import { searchEntries, sortEntries } from './index.js' | |
| 9 | + | |
| 10 | +const str = (v, min = 1) => typeof v === 'string' && v.trim().length >= min | |
| 11 | +const list = (v, min = 1) => Array.isArray(v) && v.length >= min && v.every(x => str(x, 8)) | |
| 12 | + | |
| 13 | +test('every engine indicator has a complete encyclopedia entry', () => { | |
| 14 | + const ids = Object.keys(indicators) | |
| 15 | + assert.deepEqual(ids.slice().sort(), INDICATOR_TYPES.slice().sort(), 'catalogue ids = registry ids') | |
| 16 | + for (const id of INDICATOR_TYPES) { | |
| 17 | + const e = indicators[id] | |
| 18 | + const spec = REGISTRY[id] | |
| 19 | + const where = `indicator ${id}` | |
| 20 | + assert.equal(e.id, id, where) | |
| 21 | + assert.ok(str(e.name, 2), `${where}: name`) | |
| 22 | + assert.ok(Array.isArray(e.aka) && e.aka.length >= 1 && e.aka.every(a => str(a)), `${where}: aka`) | |
| 23 | + assert.equal(e.category, spec.category, `${where}: category matches the engine`) | |
| 24 | + assert.equal(e.pane, spec.pane, `${where}: pane matches the engine`) | |
| 25 | + assert.ok(str(e.oneLiner, 20) && e.oneLiner.length <= 90, `${where}: oneLiner ≤ 90 chars (${e.oneLiner?.length})`) | |
| 26 | + assert.ok(str(e.summary, 80), `${where}: summary (2–3 sentences)`) | |
| 27 | + assert.ok(e.formula && str(e.formula.text, 3) && Array.isArray(e.formula.vars) && e.formula.vars.every(v => Array.isArray(v) && v.length === 2), `${where}: formula { text, vars }`) | |
| 28 | + assert.ok(list(e.howToRead, 3) && e.howToRead.length <= 6, `${where}: howToRead 3–6 bullets`) | |
| 29 | + assert.ok(Array.isArray(e.defaults), `${where}: defaults`) | |
| 30 | + const inputNames = (spec.inputs || []).map(i => i.name) | |
| 31 | + for (const name of inputNames) assert.ok(e.defaults.some(d => d[0] === name), `${where}: defaults explains input "${name}"`) | |
| 32 | + for (const d of e.defaults) { assert.ok(inputNames.includes(d[0]), `${where}: defaults row "${d[0]}" is a real input`); assert.ok(str(d[2], 15), `${where}: defaults "${d[0]}" needs a why`) } | |
| 33 | + assert.ok(e.bestFor && str(e.bestFor.timeframes) && str(e.bestFor.markets), `${where}: bestFor`) | |
| 34 | + assert.ok(Array.isArray(e.pairsWith) && e.pairsWith.length >= 1 && e.pairsWith.every(p => INDICATOR_TYPES.includes(p) && p !== id), `${where}: pairsWith ids exist (${e.pairsWith})`) | |
| 35 | + assert.ok(list(e.pitfalls, 2), `${where}: pitfalls`) | |
| 36 | + assert.ok(str(e.history, 20), `${where}: history`) | |
| 37 | + assert.ok(Array.isArray(e.related) && e.related.every(r => str(r.label) && /^\//.test(r.href)), `${where}: related internal links`) | |
| 38 | + } | |
| 39 | +}) | |
| 40 | + | |
| 41 | +test('every engine drawing tool has a complete encyclopedia entry', () => { | |
| 42 | + const ids = Object.keys(tools) | |
| 43 | + assert.deepEqual(ids.slice().sort(), TOOLS.slice().sort(), 'catalogue ids = engine tools') | |
| 44 | + const groups = new Set(['lines', 'fib', 'shapes', 'text', 'measure', 'patterns']) | |
| 45 | + for (const id of TOOLS) { | |
| 46 | + const e = tools[id] | |
| 47 | + const where = `tool ${id}` | |
| 48 | + assert.equal(e.id, id, where) | |
| 49 | + assert.ok(str(e.name, 2), `${where}: name`) | |
| 50 | + assert.ok(Array.isArray(e.aka), `${where}: aka`) | |
| 51 | + assert.ok(groups.has(e.group), `${where}: group`) | |
| 52 | + assert.equal(e.points, POINT_COUNT[id], `${where}: points = engine anchor count`) | |
| 53 | + assert.ok(str(e.oneLiner, 20) && e.oneLiner.length <= 90, `${where}: oneLiner ≤ 90 chars (${e.oneLiner?.length})`) | |
| 54 | + assert.ok(str(e.summary, 80), `${where}: summary`) | |
| 55 | + assert.ok(str(e.whatFor, 30), `${where}: whatFor`) | |
| 56 | + assert.ok(list(e.howToDraw, 1), `${where}: howToDraw steps`) | |
| 57 | + assert.ok(list(e.readingTips, 2), `${where}: readingTips`) | |
| 58 | + assert.ok(e.shortcut === null || (typeof e.shortcut === 'string' && e.shortcut.length === 1), `${where}: shortcut`) | |
| 59 | + assert.ok(str(e.example, 40), `${where}: example`) | |
| 60 | + assert.ok(Array.isArray(e.pairsWith) && e.pairsWith.every(p => TOOLS.includes(p) || INDICATOR_TYPES.includes(p)), `${where}: pairsWith ids exist`) | |
| 61 | + assert.ok(list(e.pitfalls, 2), `${where}: pitfalls`) | |
| 62 | + assert.ok(str(e.history, 15), `${where}: history`) | |
| 63 | + assert.ok(Array.isArray(e.related) && e.related.every(r => str(r.label) && /^\//.test(r.href)), `${where}: related`) | |
| 64 | + } | |
| 65 | +}) | |
| 66 | + | |
| 67 | +test('search and sort helpers', () => { | |
| 68 | + const rsi = searchEntries(indicators, 'relative strength') | |
| 69 | + assert.equal(rsi[0].id, 'rsi') | |
| 70 | + assert.equal(searchEntries(indicators, 'bb')[0].id, 'bollinger') | |
| 71 | + assert.ok(searchEntries(indicators, 'oversold').some(e => e.id === 'rsi')) | |
| 72 | + assert.equal(searchEntries(indicators, 'zzzznotfound').length, 0) | |
| 73 | + const sorted = sortEntries(Object.values(indicators), 'popular', ['vwap']) | |
| 74 | + assert.equal(sorted[0].id, 'vwap') | |
| 75 | + const cat = sortEntries(Object.values(indicators), 'category') | |
| 76 | + assert.equal(cat[0].category, 'trend') | |
| 77 | +}) | |
added
hfmarketdata/web/src/charts/catalog/index.js
+91 −0
@@ -0,0 +1,91 @@ | ||
| 1 | +// Encyclopedic catalogue of the /charts page — the single source of truth for every indicator (60) and drawing tool (42) | |
| 2 | +// description: library cards and detail panel, tool tooltips, the "About" tab of the settings dialog, the legend | |
| 3 | +// tooltips and the generated documentation pages (/docs/charts/indicators/<id>, /docs/charts/tools/<id>, made by | |
| 4 | +// scripts/charts-docs.mjs) all read from here. Everything is written in English (the product language). | |
| 5 | +// | |
| 6 | +// The content lives in separate modules so the charts page never pays for it up front: call `loadIndicators()` / | |
| 7 | +// `loadTools()` (dynamic import → one lazy chunk each) when the library, a tooltip or a docs page opens. | |
| 8 | +// | |
| 9 | +// ─── Indicator entry (src/charts/catalog/indicators/*.js) ──────────────────────────────────────────────────────────── | |
| 10 | +// id engine id (REGISTRY key) name full name ("Relative Strength Index") | |
| 11 | +// aka string[] of abbreviations / alternative names (first one = short label) | |
| 12 | +// category engine category: 'trend' | 'momentum' | 'volatility' | 'volume' | 'levels' | |
| 13 | +// pane 'main' (overlay) | 'new' (own pane) | |
| 14 | +// oneLiner ≤ 90 characters, what it shows — card subtitle and tooltips | |
| 15 | +// summary 2–3 sentences: what it measures and how it is built | |
| 16 | +// formula { text: mono-spaced pseudo-formula (plain text, no LaTeX), vars: [[symbol, meaning], …] } | |
| 17 | +// howToRead 3–5 bullet strings: signals, thresholds, divergences, traps | |
| 18 | +// defaults [[input name, default value, why this default], …] — one row per engine input | |
| 19 | +// bestFor { timeframes: string, markets: string, note?: string } | |
| 20 | +// pairsWith indicator ids that complement it (must exist in the registry) | |
| 21 | +// pitfalls 2–4 bullet strings | |
| 22 | +// history one sentence: author, year, publication | |
| 23 | +// related [{ label, href }] — internal links (docs pages, other catalogue entries) | |
| 24 | +// thumb optional rendering hints for scripts/charts-thumbs.mjs: { bars?: number (visible bars), volume?: bool, params?: object } | |
| 25 | +// | |
| 26 | +// ─── Tool entry (src/charts/catalog/tools.js) ──────────────────────────────────────────────────────────────────────── | |
| 27 | +// id engine tool id (DRAWING_TOOLS) name label shown in the flyouts | |
| 28 | +// aka string[] group drawtools.js group id: lines | fib | shapes | text | measure | patterns | |
| 29 | +// points number of anchor points (Infinity for free-form) | |
| 30 | +// oneLiner ≤ 90 characters summary 2–3 sentences | |
| 31 | +// whatFor 1–2 sentences: the job it does on a chart | |
| 32 | +// howToDraw ordered steps ("Click the swing low (A).", "Click the swing high (B)." …) | |
| 33 | +// readingTips 3–5 bullets shortcut single key or null | |
| 34 | +// example one concrete worked example (numbers welcome) | |
| 35 | +// pairsWith ids of tools or indicators that go with it pitfalls 2–4 bullets | |
| 36 | +// history one sentence related [{ label, href }] | |
| 37 | + | |
| 38 | +export const INDICATOR_CATEGORY_LABELS = { trend: 'Trend', momentum: 'Momentum', volatility: 'Volatility', volume: 'Volume', levels: 'Levels & structure' } | |
| 39 | +export const INDICATOR_CATEGORY_ORDER = ['trend', 'momentum', 'volatility', 'volume', 'levels'] | |
| 40 | +export const TOOL_GROUP_LABELS = { lines: 'Lines & channels', fib: 'Fibonacci & Gann', shapes: 'Shapes & annotations', text: 'Text & notes', measure: 'Measure & positions', patterns: 'Patterns' } | |
| 41 | +export const TOOL_GROUP_ORDER = ['lines', 'fib', 'shapes', 'text', 'measure', 'patterns'] | |
| 42 | + | |
| 43 | +/** Where the generated thumbnails live (see scripts/charts-thumbs.mjs → public/charts/thumbs/index.json). */ | |
| 44 | +export const THUMBS_BASE = '/charts/thumbs' | |
| 45 | +export const thumbUrl = (kind, id, theme = 'dark', size = 'sm') => `${THUMBS_BASE}/${kind}-${id}-${theme}${size === 'lg' ? '-lg' : ''}.png` | |
| 46 | +export const docsHref = (kind, id) => `/docs/charts/${kind === 'tool' ? 'tools' : 'indicators'}/${id}` | |
| 47 | +export const chartsHref = (kind, id) => (kind === 'tool' ? `/charts?tool=${id}` : `/charts?ind=${id}`) | |
| 48 | + | |
| 49 | +let indicatorsPromise = null | |
| 50 | +let toolsPromise = null | |
| 51 | + | |
| 52 | +/** All indicator entries, keyed by id (lazy chunk, cached). */ | |
| 53 | +export function loadIndicators() { | |
| 54 | + if (!indicatorsPromise) indicatorsPromise = import('./indicators/all.js').then(m => m.default) | |
| 55 | + return indicatorsPromise | |
| 56 | +} | |
| 57 | +/** All tool entries, keyed by id (lazy chunk, cached). */ | |
| 58 | +export function loadTools() { | |
| 59 | + if (!toolsPromise) toolsPromise = import('./tools.js').then(m => m.default) | |
| 60 | + return toolsPromise | |
| 61 | +} | |
| 62 | + | |
| 63 | +/** Case-insensitive search over name / aka / id / oneLiner / summary. `entries` = object or array. */ | |
| 64 | +export function searchEntries(entries, query) { | |
| 65 | + const q = (query || '').trim().toLowerCase() | |
| 66 | + const list = Array.isArray(entries) ? entries : Object.values(entries) | |
| 67 | + if (!q) return list | |
| 68 | + const terms = q.split(/\s+/).filter(Boolean) | |
| 69 | + const score = e => { | |
| 70 | + const hay = [e.id, e.name, ...(e.aka || [])].join(' ').toLowerCase() | |
| 71 | + const text = `${e.oneLiner || ''} ${e.summary || ''}`.toLowerCase() | |
| 72 | + let s = 0 | |
| 73 | + for (const t of terms) { | |
| 74 | + if (e.id === t || (e.aka || []).some(a => a.toLowerCase() === t)) s += 40 | |
| 75 | + else if (hay.startsWith(t)) s += 25 | |
| 76 | + else if (hay.includes(t)) s += 15 | |
| 77 | + else if (text.includes(t)) s += 6 | |
| 78 | + else return -1 | |
| 79 | + } | |
| 80 | + return s | |
| 81 | + } | |
| 82 | + return list.map(e => [e, score(e)]).filter(([, s]) => s >= 0).sort((a, b) => b[1] - a[1]).map(([e]) => e) | |
| 83 | +} | |
| 84 | + | |
| 85 | +/** Sort helper for the library: 'az' | 'category' | 'popular' (favourites first, then A–Z). */ | |
| 86 | +export function sortEntries(list, mode, favorites = []) { | |
| 87 | + const byName = (a, b) => a.name.localeCompare(b.name) | |
| 88 | + if (mode === 'category') return list.slice().sort((a, b) => INDICATOR_CATEGORY_ORDER.indexOf(a.category) - INDICATOR_CATEGORY_ORDER.indexOf(b.category) || byName(a, b)) | |
| 89 | + if (mode === 'popular') return list.slice().sort((a, b) => Number(favorites.includes(b.id)) - Number(favorites.includes(a.id)) || byName(a, b)) | |
| 90 | + return list.slice().sort(byName) | |
| 91 | +} | |
added
hfmarketdata/web/src/charts/catalog/indicators/all.js
+10 −0
@@ -0,0 +1,10 @@ | ||
| 1 | +// Indicator encyclopedia — one module per family, merged here. Schema: see ../index.js. | |
| 2 | +import trend from './trend.js' | |
| 3 | +import momentum from './momentum.js' | |
| 4 | +import volatility from './volatility.js' | |
| 5 | +import volume from './volume.js' | |
| 6 | + | |
| 7 | +const ALL = [...trend, ...momentum, ...volatility, ...volume] | |
| 8 | +const byId = Object.fromEntries(ALL.map(e => [e.id, e])) | |
| 9 | +export default byId | |
| 10 | +export const INDICATOR_ENTRIES = ALL | |
added
hfmarketdata/web/src/charts/catalog/indicators/momentum.js
+474 −0
@@ -0,0 +1,474 @@ | ||
| 1 | +// Momentum oscillators (own pane) — encyclopedia entries. Schema: see ../index.js. | |
| 2 | +export default [ | |
| 3 | + { | |
| 4 | + id: 'rsi', name: 'Relative Strength Index', aka: ['RSI'], category: 'momentum', pane: 'new', | |
| 5 | + oneLiner: 'Momentum 0–100: above 70 overbought, below 30 oversold, divergences warn of turns.', | |
| 6 | + summary: 'The RSI compares the average gain to the average loss over the last n bars and maps the ratio onto a 0–100 scale. It rises when up-closes dominate and falls when down-closes do, so it reads the speed of a move rather than its direction alone.', | |
| 7 | + formula: { text: 'RS = avgGain(n) / avgLoss(n) (Wilder smoothing)\nRSI = 100 − 100 / (1 + RS)', vars: [['n', 'lookback length (default 14)'], ['avgGain', 'Wilder-smoothed mean of positive close-to-close changes'], ['avgLoss', 'Wilder-smoothed mean of the absolute negative changes']] }, | |
| 8 | + howToRead: [ | |
| 9 | + 'Above 70: overbought — the move is stretched, not necessarily over; in a strong trend RSI can stay above 70 for weeks.', | |
| 10 | + 'Below 30: oversold — same caveat in a downtrend.', | |
| 11 | + 'Bearish divergence: price prints a higher high while RSI prints a lower high (and the mirror for bullish divergence).', | |
| 12 | + 'Trend filter: in uptrends RSI tends to hold 40–90, in downtrends 10–60 (Constance Brown\'s range rule).', | |
| 13 | + 'The 50 line separates bullish from bearish momentum regimes.', | |
| 14 | + ], | |
| 15 | + defaults: [ | |
| 16 | + ['length', 14, 'Wilder\'s original choice — half of a 28-day lunar cycle on daily bars; 9 or 7 for faster intraday reads, 21–25 for smoother swing signals.'], | |
| 17 | + ['source', 'close', 'Closes carry the settlement information; hl2 or ohlc4 smooth gaps on volatile intraday series.'], | |
| 18 | + ], | |
| 19 | + bestFor: { timeframes: 'Every timeframe; the 14-period reading is most meaningful on 1-hour and daily bars.', markets: 'Liquid stocks, indices, FX and crypto; ranges rather than strong trends.', note: 'Combine with a trend filter before acting on overbought / oversold readings.' }, | |
| 20 | + pairsWith: ['macd', 'bollinger', 'adx'], | |
| 21 | + pitfalls: [ | |
| 22 | + 'Selling every 70 in a bull trend is the classic way to fight the tape.', | |
| 23 | + 'Divergences can persist for many bars before price turns — they are context, not triggers.', | |
| 24 | + 'A short length produces many false extremes on 1-minute bars.', | |
| 25 | + ], | |
| 26 | + history: 'J. Welles Wilder Jr., 1978, in New Concepts in Technical Trading Systems.', | |
| 27 | + related: [{ label: 'Stochastic RSI', href: '/docs/charts/indicators/stochrsi' }, { label: 'Chande Momentum Oscillator', href: '/docs/charts/indicators/cmo' }, { label: 'Charting guide', href: '/docs/charts' }], | |
| 28 | + }, | |
| 29 | + { | |
| 30 | + id: 'stochrsi', name: 'Stochastic RSI', aka: ['StochRSI', 'Stoch RSI'], category: 'momentum', pane: 'new', | |
| 31 | + oneLiner: 'Stochastic applied to RSI: a faster 0–100 oscillator with %K / %D and 20 / 80 zones.', | |
| 32 | + summary: 'StochRSI locates the current RSI inside its own high–low range of the last n bars, then smooths the result into %K and a %D signal line. It is an oscillator of an oscillator: it hits its extremes far more often than the RSI itself, which makes it a timing tool rather than a regime gauge.', | |
| 33 | + formula: { text: 'raw = (RSI − min(RSI, n)) / (max(RSI, n) − min(RSI, n)) × 100\n%K = SMA(raw, k)\n%D = SMA(%K, d)', vars: [['RSI', 'RSI of the source over rsiLength bars (default 14)'], ['n', 'stochLength — bars of RSI history used for the range (default 14)'], ['k', 'smoothing of the raw stochastic (default 3)'], ['d', 'smoothing of %K that gives the signal line (default 3)']] }, | |
| 34 | + howToRead: [ | |
| 35 | + 'Above 80 / below 20: overbought / oversold — expect many visits per trend; wait for %K to cross back through the level before acting.', | |
| 36 | + '%K crossing above %D from below 20 is the classic buy timing signal; the mirror from above 80 is the sell signal.', | |
| 37 | + 'Readings pinned near 0 or 100 for several bars mean RSI sits at the edge of its own range: strong, one-sided momentum.', | |
| 38 | + 'Use the RSI (or a moving average) to decide the direction, StochRSI to time the entry inside that direction.', | |
| 39 | + ], | |
| 40 | + defaults: [ | |
| 41 | + ['rsiLength', 14, 'Same as the standard RSI, so the two indicators can be compared side by side.'], | |
| 42 | + ['stochLength', 14, 'Chande and Kroll used the same length for the RSI and its stochastic range.'], | |
| 43 | + ['k', 3, 'Three-bar smoothing tames the raw series, which otherwise jumps between 0 and 100.'], | |
| 44 | + ['d', 3, 'A three-bar signal line, as in the classic slow stochastic.'], | |
| 45 | + ['source', 'close', 'Closes feed the underlying RSI.'], | |
| 46 | + ], | |
| 47 | + bestFor: { timeframes: 'Intraday to daily; it shines on 5-minute to 1-hour bars where RSI alone rarely reaches its extremes.', markets: 'Range-bound stocks, FX pairs and crypto.', note: 'Too sensitive for a stand-alone signal on trending markets.' }, | |
| 48 | + pairsWith: ['rsi', 'ema', 'bollinger'], | |
| 49 | + pitfalls: [ | |
| 50 | + 'Twice as noisy as RSI: every small RSI wiggle inside a narrow range becomes a full 0–100 swing.', | |
| 51 | + 'A flat RSI range (max ≈ min) produces meaningless 0 readings — check the underlying RSI first.', | |
| 52 | + 'The smoothing adds lag on top of the RSI lag; on daily bars signals arrive after the first leg of the move.', | |
| 53 | + ], | |
| 54 | + history: 'Tushar Chande and Stanley Kroll, 1994, in The New Technical Trader.', | |
| 55 | + related: [{ label: 'Relative Strength Index', href: '/docs/charts/indicators/rsi' }, { label: 'Stochastic', href: '/docs/charts/indicators/stoch' }], | |
| 56 | + }, | |
| 57 | + { | |
| 58 | + id: 'macd', name: 'Moving Average Convergence Divergence', aka: ['MACD'], category: 'momentum', pane: 'new', | |
| 59 | + oneLiner: 'Fast EMA minus slow EMA, a signal EMA and the histogram between them; zero line = trend.', | |
| 60 | + summary: 'The MACD line is the distance between a fast and a slow exponential moving average, so it measures whether short-term prices are pulling away from or converging towards the longer trend. A signal EMA of the MACD line and the histogram of their difference turn that distance into crossovers and momentum readings.', | |
| 61 | + formula: { text: 'MACD = EMA(src, fast) − EMA(src, slow)\nsignal = EMA(MACD, signal)\nhistogram = MACD − signal', vars: [['fast', 'short EMA length (default 12)'], ['slow', 'long EMA length (default 26)'], ['signal', 'EMA length applied to the MACD line (default 9)'], ['src', 'price source (default close)']] }, | |
| 62 | + howToRead: [ | |
| 63 | + 'MACD above zero: the fast average is above the slow one — bullish regime; below zero, bearish regime.', | |
| 64 | + 'MACD crossing above the signal line is a bullish crossover; the histogram flipping from negative to positive shows the same event.', | |
| 65 | + 'Histogram bars shrinking towards zero mean momentum is fading before the crossover actually happens — the earliest warning.', | |
| 66 | + 'Bearish divergence: price makes a higher high while the MACD (or its histogram) makes a lower high.', | |
| 67 | + 'Distance from zero matters: crossovers far from zero after an extended move are more reliable than crossovers hugging the zero line.', | |
| 68 | + ], | |
| 69 | + defaults: [ | |
| 70 | + ['fast', 12, 'Appel built the default on the 1970s six-day trading week: 12 bars ≈ two weeks.'], | |
| 71 | + ['slow', 26, '26 bars ≈ one month of six-day weeks; 12 / 26 keeps the same ratio traders still use on daily bars.'], | |
| 72 | + ['signal', 9, 'Nine bars ≈ one and a half weeks, quick enough to catch turns without flipping every day.'], | |
| 73 | + ['source', 'close', 'The EMAs are computed on closes; hl2 or ohlc4 reduce gap noise on intraday bars.'], | |
| 74 | + ], | |
| 75 | + bestFor: { timeframes: 'Daily and weekly first; on intraday bars the standard lengths still work but the zero line loses meaning.', markets: 'Trending stocks, indices and futures; any liquid market with sustained moves.', note: 'A trend-following momentum tool — do not expect it to time reversals in a tight range.' }, | |
| 76 | + pairsWith: ['rsi', 'ema', 'adx', 'volume-ma'], | |
| 77 | + pitfalls: [ | |
| 78 | + 'In sideways markets the two EMAs braid around each other and the crossovers whipsaw.', | |
| 79 | + 'The MACD is in price units, so its values are not comparable across symbols or after a large price change.', | |
| 80 | + 'The histogram peaks and troughs are the signal — the crossover itself is late.', | |
| 81 | + ], | |
| 82 | + history: 'Gerald Appel, late 1970s; the histogram was added by Thomas Aspray in 1986.', | |
| 83 | + related: [{ label: 'Exponential Moving Average', href: '/docs/charts/indicators/ema' }, { label: 'True Strength Index', href: '/docs/charts/indicators/tsi' }, { label: 'Charting guide', href: '/docs/charts' }], | |
| 84 | + }, | |
| 85 | + { | |
| 86 | + id: 'stoch', name: 'Stochastic Oscillator', aka: ['Stoch', 'Slow stochastic', '%K %D'], category: 'momentum', pane: 'new', | |
| 87 | + oneLiner: 'Where the close sits in the recent high–low range, 0–100; %K / %D crosses at 20 / 80.', | |
| 88 | + summary: 'The stochastic measures the position of the latest close inside the highest high and lowest low of the last k bars: 100 means a close at the top of the range, 0 at the bottom. A first smoothing gives the slow %K, a second one the %D signal line, and the crossovers between the two are the classic signals.', | |
| 89 | + formula: { text: 'raw %K = (close − lowest(low, k)) / (highest(high, k) − lowest(low, k)) × 100\n%K = SMA(raw %K, smooth)\n%D = SMA(%K, d)', vars: [['k', 'range lookback in bars (default 14)'], ['smooth', 'smoothing of the raw %K → slow %K (default 3)'], ['d', 'smoothing of %K → signal line (default 3)']] }, | |
| 90 | + howToRead: [ | |
| 91 | + 'Above 80: closes are near the top of the range — overbought; below 20: near the bottom — oversold.', | |
| 92 | + '%K crossing %D below 20 (bullish) or above 80 (bearish) is Lane\'s original signal; a cross in the middle of the scale carries little weight.', | |
| 93 | + 'Divergence between the oscillator peaks and price peaks precedes most range reversals — Lane considered it the only signal that matters.', | |
| 94 | + 'In a strong trend the stochastic rides above 80 (or below 20) for long stretches: a pullback that only reaches 50 is a continuation, not a reversal.', | |
| 95 | + ], | |
| 96 | + defaults: [ | |
| 97 | + ['k', 14, 'Lane\'s own charts used 5 to 14 bars; 14 became the standard because it covers roughly three weeks of daily bars.'], | |
| 98 | + ['d', 3, 'Three-bar signal line, unchanged since Lane\'s original definition.'], | |
| 99 | + ['smooth', 3, 'The extra three-bar smoothing turns the fast stochastic into the slow version, which is what most traders mean by "stochastic".'], | |
| 100 | + ], | |
| 101 | + bestFor: { timeframes: 'All timeframes; particularly readable on 15-minute to daily bars.', markets: 'Range-bound markets — FX pairs, mean-reverting stocks, commodities in consolidation.', note: 'Use %K / %D crosses only in the direction of the higher-timeframe trend.' }, | |
| 102 | + pairsWith: ['rsi', 'bollinger', 'sma'], | |
| 103 | + pitfalls: [ | |
| 104 | + 'A ranging indicator applied to a trending market: overbought readings persist and shorting them loses.', | |
| 105 | + 'When the range of the last k bars is zero the engine outputs 0 — flat pre-market bars can produce odd spikes.', | |
| 106 | + 'Very short k values on 1-minute bars oscillate between the extremes almost every bar.', | |
| 107 | + ], | |
| 108 | + history: 'George C. Lane, late 1950s, popularised in his 1984 article "Lane\'s Stochastics".', | |
| 109 | + related: [{ label: 'Stochastic RSI', href: '/docs/charts/indicators/stochrsi' }, { label: 'Williams %R', href: '/docs/charts/indicators/williams' }], | |
| 110 | + }, | |
| 111 | + { | |
| 112 | + id: 'williams', name: 'Williams %R', aka: ['%R', 'Williams Percent Range'], category: 'momentum', pane: 'new', | |
| 113 | + oneLiner: 'Inverted stochastic on a −100…0 scale: above −20 overbought, below −80 oversold.', | |
| 114 | + summary: 'Williams %R measures how far the latest close sits below the highest high of the last n bars, as a share of the high–low range. It is the unsmoothed fast stochastic turned upside down: 0 is a close at the top of the range, −100 a close at the bottom.', | |
| 115 | + formula: { text: '%R = (highest(high, n) − close) / (highest(high, n) − lowest(low, n)) × −100', vars: [['n', 'lookback length in bars (default 14)'], ['highest / lowest', 'extreme high and low of the last n bars including the current one']] }, | |
| 116 | + howToRead: [ | |
| 117 | + 'Above −20: the close is in the top fifth of the range — overbought; below −80: bottom fifth — oversold.', | |
| 118 | + 'Williams\' own rule: wait for %R to leave the extreme zone (cross back below −20 or above −80) before taking the reversal.', | |
| 119 | + 'A failure to reach −20 during a rally (or −80 during a decline) shows the trend is losing its ability to close near the extremes — an early exhaustion clue.', | |
| 120 | + 'Because it is unsmoothed, %R reacts a bar earlier than the slow stochastic but with more noise.', | |
| 121 | + ], | |
| 122 | + defaults: [ | |
| 123 | + ['length', 14, 'Williams originally used 10 bars; 14 aligns it with the RSI and stochastic lookbacks most traders already watch.'], | |
| 124 | + ], | |
| 125 | + bestFor: { timeframes: 'Intraday and daily; on weekly bars a 14-period %R covers a quarter and turns slowly.', markets: 'Futures, FX and indices in ranges; Williams designed it for commodity trading.', note: 'A pure timing gauge — pair it with a trend filter.' }, | |
| 126 | + pairsWith: ['stoch', 'adx', 'ema'], | |
| 127 | + pitfalls: [ | |
| 128 | + 'Reads −50 whenever the range is flat (engine convention), which can look like a neutral signal on illiquid bars.', | |
| 129 | + 'Without smoothing it spends many consecutive bars pinned at 0 or −100 in a trend — those are not reversal signals.', | |
| 130 | + 'The negative scale confuses newcomers: −10 is the overbought side, −90 the oversold side.', | |
| 131 | + ], | |
| 132 | + history: 'Larry Williams, 1973, in How I Made One Million Dollars Last Year Trading Commodities.', | |
| 133 | + related: [{ label: 'Stochastic', href: '/docs/charts/indicators/stoch' }, { label: 'Commodity Channel Index', href: '/docs/charts/indicators/cci' }], | |
| 134 | + }, | |
| 135 | + { | |
| 136 | + id: 'cci', name: 'Commodity Channel Index', aka: ['CCI'], category: 'momentum', pane: 'new', | |
| 137 | + oneLiner: 'Typical price minus its SMA, scaled by mean deviation; ±100 marks unusual extension.', | |
| 138 | + summary: 'The CCI measures how far the typical price (high + low + close) / 3 has drifted from its own moving average, normalised by the mean absolute deviation and a 0.015 constant. The constant is chosen so that about 70–80 % of readings fall between −100 and +100; moves beyond those levels flag an unusually strong departure from the mean.', | |
| 139 | + formula: { text: 'TP = (high + low + close) / 3\nCCI = (TP − SMA(TP, n)) / (0.015 × meanDeviation(TP, n))', vars: [['n', 'lookback length (default 20)'], ['meanDeviation', 'average of |TP − SMA(TP, n)| over the last n bars'], ['0.015', 'Lambert\'s scaling constant that keeps ~75 % of values inside ±100']] }, | |
| 140 | + howToRead: [ | |
| 141 | + 'Above +100: the price is stretched well above its mean — either an overbought reading (range) or the start of a strong up-move (Lambert\'s original use: buy when CCI crosses above +100, exit when it falls back below).', | |
| 142 | + 'Below −100: the mirror image — oversold in a range, or an emerging downtrend.', | |
| 143 | + 'Zero line: CCI above 0 means the typical price is above its n-bar average; sustained readings on one side define the trend.', | |
| 144 | + 'Divergence between CCI extremes and price extremes is a reliable warning in commodities and indices.', | |
| 145 | + 'Very high readings (±200, ±300) are not "more overbought" — they measure how fast the price left its average.', | |
| 146 | + ], | |
| 147 | + defaults: [ | |
| 148 | + ['length', 20, 'Lambert recommended a third of the dominant cycle; 20 bars fit the roughly 60-day cycle he studied in commodities.'], | |
| 149 | + ], | |
| 150 | + bestFor: { timeframes: 'Daily and 4-hour bars for cycle work; 20 bars intraday for short breakouts.', markets: 'Commodities and futures (its original purpose), indices and large-cap stocks.', note: 'Unbounded: the scale differs from bounded oscillators like RSI.' }, | |
| 151 | + pairsWith: ['sma', 'atr', 'donchian'], | |
| 152 | + pitfalls: [ | |
| 153 | + 'Two schools read it in opposite ways (breakout vs mean reversion) — decide which regime you are in before using it.', | |
| 154 | + 'Because it is unbounded, fixed levels like ±100 are statistical, not hard limits; a strong trend keeps it beyond ±100 for long stretches.', | |
| 155 | + 'Mean deviation collapses on quiet bars, which inflates the reading when a normal-sized move finally comes.', | |
| 156 | + ], | |
| 157 | + history: 'Donald Lambert, 1980, in Commodities magazine.', | |
| 158 | + related: [{ label: 'Williams %R', href: '/docs/charts/indicators/williams' }, { label: 'Standard deviation', href: '/docs/charts/indicators/stddev' }], | |
| 159 | + }, | |
| 160 | + { | |
| 161 | + id: 'roc', name: 'Rate of Change', aka: ['ROC', 'Price Rate of Change'], category: 'momentum', pane: 'new', | |
| 162 | + oneLiner: 'Percent change of price versus n bars ago; the zero line separates rising from falling.', | |
| 163 | + summary: 'The Rate of Change is the percentage difference between the current price and the price n bars earlier. It is the simplest pure-momentum measure: positive when price is higher than it was, negative when lower, and its slope shows whether that momentum is accelerating or fading.', | |
| 164 | + formula: { text: 'ROC = (src − src[n bars ago]) / src[n bars ago] × 100', vars: [['n', 'lookback length in bars (default 9)'], ['src', 'price source (default close)']] }, | |
| 165 | + howToRead: [ | |
| 166 | + 'Above zero: price is up over the lookback; below zero: down. Crossings of the zero line are simple trend-change signals.', | |
| 167 | + 'ROC turning down while still positive means the advance is decelerating — often the first sign of a top.', | |
| 168 | + 'Extreme readings relative to the indicator\'s own history (not fixed levels) mark overbought / oversold conditions.', | |
| 169 | + 'Divergence: a new price high on a lower ROC high shows the rally is slowing.', | |
| 170 | + ], | |
| 171 | + defaults: [ | |
| 172 | + ['length', 9, 'Nine bars is a short swing horizon — about two weeks of daily bars; 12 and 25 are the other classic lengths (Pring).'], | |
| 173 | + ['source', 'close', 'Closes give the standard reading; hl2 damps single-bar spikes.'], | |
| 174 | + ], | |
| 175 | + bestFor: { timeframes: 'Daily and weekly bars for momentum comparisons; short lengths intraday.', markets: 'Any liquid market; percent scaling makes readings comparable across symbols.', note: 'Also used as a relative-strength ranking tool between assets.' }, | |
| 176 | + pairsWith: ['momentum', 'sma', 'coppock'], | |
| 177 | + pitfalls: [ | |
| 178 | + 'A single large bar entering or leaving the lookback window creates a jump unrelated to today\'s action.', | |
| 179 | + 'No fixed bounds, so "overbought" must be judged against the indicator\'s own recent range.', | |
| 180 | + 'Short lengths on 1-minute bars are mostly noise.', | |
| 181 | + ], | |
| 182 | + history: 'A classical momentum measure; popularised by Martin Pring in Technical Analysis Explained (1980).', | |
| 183 | + related: [{ label: 'Momentum', href: '/docs/charts/indicators/momentum' }, { label: 'Know Sure Thing', href: '/docs/charts/indicators/kst' }, { label: 'Coppock Curve', href: '/docs/charts/indicators/coppock' }], | |
| 184 | + }, | |
| 185 | + { | |
| 186 | + id: 'momentum', name: 'Momentum', aka: ['Mom', 'Price momentum'], category: 'momentum', pane: 'new', | |
| 187 | + oneLiner: 'Price minus price n bars ago, in price units; the zero line is the trend switch.', | |
| 188 | + summary: 'Momentum is the raw difference between the current price and the price n bars earlier. It is the Rate of Change without the division: same shape, but expressed in points instead of percent, which keeps it directly comparable to the price scale of the chart.', | |
| 189 | + formula: { text: 'Mom = src − src[n bars ago]', vars: [['n', 'lookback length in bars (default 10)'], ['src', 'price source (default close)']] }, | |
| 190 | + howToRead: [ | |
| 191 | + 'Above zero: price is higher than n bars ago; below zero: lower. The zero cross is the basic trend signal.', | |
| 192 | + 'The slope matters more than the level: momentum peaking while price still rises is the classic warning of a maturing move.', | |
| 193 | + 'A moving average of the momentum line (add an SMA to the pane) acts as a signal line for crossovers.', | |
| 194 | + 'Compare peaks: a higher price high with a lower momentum high is a bearish divergence.', | |
| 195 | + ], | |
| 196 | + defaults: [ | |
| 197 | + ['length', 10, 'Ten bars — two trading weeks on daily data — is the value used in most textbooks; 12 and 20 are the other common choices.'], | |
| 198 | + ['source', 'close', 'Closes are the standard; hl2 smooths intraday gaps.'], | |
| 199 | + ], | |
| 200 | + bestFor: { timeframes: 'Daily bars for swing work; shorter lengths on hourly bars.', markets: 'Single instruments — the point scale is not comparable across symbols.', note: 'Prefer ROC when comparing several assets.' }, | |
| 201 | + pairsWith: ['roc', 'sma', 'macd'], | |
| 202 | + pitfalls: [ | |
| 203 | + 'Measured in price units, so a reading of 5 means something different on a $20 stock and a $2 000 index.', | |
| 204 | + 'Like ROC, it jumps when an old outlier bar leaves the window.', | |
| 205 | + 'Unbounded, so there are no fixed overbought / oversold levels.', | |
| 206 | + ], | |
| 207 | + history: 'One of the oldest indicators in technical analysis; described in Edwards and Magee (1948) and formalised in the early charting literature.', | |
| 208 | + related: [{ label: 'Rate of Change', href: '/docs/charts/indicators/roc' }, { label: 'MACD', href: '/docs/charts/indicators/macd' }], | |
| 209 | + }, | |
| 210 | + { | |
| 211 | + id: 'ao', name: 'Awesome Oscillator', aka: ['AO'], category: 'momentum', pane: 'new', | |
| 212 | + oneLiner: 'SMA 5 minus SMA 34 of the bar midpoint, as a histogram coloured by its own direction.', | |
| 213 | + summary: 'The Awesome Oscillator subtracts a 34-bar simple moving average of the bar midpoints from a 5-bar one, so it compares recent driving force to the longer-term one. It is drawn as a histogram whose bars are green when the value rises from the previous bar and red when it falls — the colour, not the sign, carries the first-level signal.', | |
| 214 | + formula: { text: 'mid = (high + low) / 2\nAO = SMA(mid, fast) − SMA(mid, slow)', vars: [['fast', 'short SMA length (default 5)'], ['slow', 'long SMA length (default 34)'], ['bar colour', 'green when AO > previous AO, red when AO < previous AO']] }, | |
| 215 | + howToRead: [ | |
| 216 | + 'Zero-line cross: AO turning positive is a bullish momentum shift, negative is bearish.', | |
| 217 | + 'Saucer: three bars above zero, the first two falling (red) and the third rising (green) — a buy in the direction of the trend; the mirror below zero is a sell.', | |
| 218 | + 'Twin peaks: two troughs below zero where the second is shallower and followed by a green bar signals a bullish reversal; two peaks above zero, the second lower, signal a bearish one.', | |
| 219 | + 'Bar colour flipping without a zero cross is a pullback inside the trend, not a reversal.', | |
| 220 | + ], | |
| 221 | + defaults: [ | |
| 222 | + ['fast', 5, 'Bill Williams\' fast average — one trading week of daily bars.'], | |
| 223 | + ['slow', 34, 'A Fibonacci number roughly equal to seven trading weeks; Williams chose 5 / 34 for all the Profitunity tools.'], | |
| 224 | + ], | |
| 225 | + bestFor: { timeframes: 'Daily and 4-hour bars; the 5 / 34 pair is calibrated for swing trading.', markets: 'Futures, FX and indices — anything that trends in waves.', note: 'Part of Williams\' Profitunity method together with the Alligator and Fractals.' }, | |
| 226 | + pairsWith: ['macd', 'ema', 'elder-ray'], | |
| 227 | + pitfalls: [ | |
| 228 | + 'The saucer and twin-peak patterns need exact bar counts; eyeballing them produces many false setups.', | |
| 229 | + 'Two SMAs lag: the zero cross confirms a move that is often half done.', | |
| 230 | + 'On low-volatility bars the histogram flattens and colour flips constantly.', | |
| 231 | + ], | |
| 232 | + history: 'Bill Williams, 1995, in Trading Chaos.', | |
| 233 | + related: [{ label: 'MACD', href: '/docs/charts/indicators/macd' }, { label: 'Elder Ray', href: '/docs/charts/indicators/elder-ray' }], | |
| 234 | + }, | |
| 235 | + { | |
| 236 | + id: 'trix', name: 'TRIX', aka: ['TRIX', 'Triple exponential average rate of change'], category: 'momentum', pane: 'new', | |
| 237 | + oneLiner: 'Percent change of a triple-smoothed EMA with a signal line; filters out short-lived noise.', | |
| 238 | + summary: 'TRIX smooths the price three times with an EMA of the same length, then takes the one-bar percent change of that triple EMA. The triple smoothing removes cycles shorter than the length while the rate-of-change step keeps the indicator centred on zero, so TRIX shows the direction and acceleration of the underlying trend with very little noise.', | |
| 239 | + formula: { text: 'e1 = EMA(src, n); e2 = EMA(e1, n); e3 = EMA(e2, n)\nTRIX = (e3 − e3[1]) / e3[1] × 100\nsignal = EMA(TRIX, s)', vars: [['n', 'EMA length applied three times (default 15)'], ['s', 'signal EMA length (default 9)'], ['e3[1]', 'triple EMA one bar earlier'], ['src', 'price source (default close)']] }, | |
| 240 | + howToRead: [ | |
| 241 | + 'Above zero: the smoothed trend is rising; below zero: falling. Zero crosses are slow but rarely false.', | |
| 242 | + 'TRIX crossing its signal line gives earlier entries than the zero cross, at the cost of a few whipsaws.', | |
| 243 | + 'Divergence between TRIX peaks and price peaks precedes trend exhaustion, often by many bars because of the smoothing.', | |
| 244 | + 'The slope of TRIX (rising vs falling) reads the trend\'s acceleration — a flattening TRIX in positive territory is a maturing advance.', | |
| 245 | + ], | |
| 246 | + defaults: [ | |
| 247 | + ['length', 15, 'Hutson\'s default; three passes of a 15-bar EMA filter roughly a one-month cycle on daily bars.'], | |
| 248 | + ['signal', 9, 'The same nine-bar signal convention as the MACD.'], | |
| 249 | + ['source', 'close', 'Closes feed the first EMA.'], | |
| 250 | + ], | |
| 251 | + bestFor: { timeframes: 'Daily and weekly bars; on intraday bars shorten the length or accept long lag.', markets: 'Trending indices, ETFs and large caps where noise filtering pays off.', note: 'Values are tiny (hundredths of a percent) — read the shape, not the number.' }, | |
| 252 | + pairsWith: ['macd', 'ema', 'adx'], | |
| 253 | + pitfalls: [ | |
| 254 | + 'Three EMA passes plus a signal line means substantial lag; turns are confirmed late.', | |
| 255 | + 'The percent scale is minute (e.g. 0.05), so the axis needs several decimals — the engine uses 4.', | |
| 256 | + 'Sideways markets keep TRIX braided around zero with no useful signal.', | |
| 257 | + ], | |
| 258 | + history: 'Jack Hutson, 1983, in Technical Analysis of Stocks & Commodities.', | |
| 259 | + related: [{ label: 'Triple EMA', href: '/docs/charts/indicators/tema' }, { label: 'MACD', href: '/docs/charts/indicators/macd' }], | |
| 260 | + }, | |
| 261 | + { | |
| 262 | + id: 'ultimate', name: 'Ultimate Oscillator', aka: ['UO', 'Ultimate'], category: 'momentum', pane: 'new', | |
| 263 | + oneLiner: 'Buying pressure over true range on 7, 14 and 28 bars, weighted 4-2-1, on a 0–100 scale.', | |
| 264 | + summary: 'The Ultimate Oscillator measures buying pressure — how far the close sits above the true low — as a share of the true range, averaged over three lookbacks (short, medium, long) and combined with weights 4, 2 and 1. Blending three horizons reduces the false divergences that plague single-period oscillators, which was Williams\' stated goal.', | |
| 265 | + formula: { text: 'BP = close − min(low, prevClose)\nTR = max(high, prevClose) − min(low, prevClose)\navg(n) = sum(BP, n) / sum(TR, n)\nUO = 100 × (4 × avg(fast) + 2 × avg(mid) + avg(slow)) / 7', vars: [['fast / mid / slow', 'the three lookbacks (default 7 / 14 / 28)'], ['BP', 'buying pressure of the bar'], ['TR', 'true range of the bar (gap-adjusted)']] }, | |
| 266 | + howToRead: [ | |
| 267 | + 'Below 30: oversold; above 70: overbought — Williams used these as the setup zones, not as signals.', | |
| 268 | + 'Bullish setup: price makes a lower low while UO makes a higher low (divergence) with the first low below 30; the buy triggers when UO breaks above the high between the two lows.', | |
| 269 | + 'Bearish setup: the mirror image with a UO high above 70 and a break below the intermediate trough.', | |
| 270 | + 'The 50 line separates net buying pressure from net selling pressure across the three horizons.', | |
| 271 | + ], | |
| 272 | + defaults: [ | |
| 273 | + ['fast', 7, 'Williams\' short horizon — about one and a half weeks of daily bars.'], | |
| 274 | + ['mid', 14, 'Twice the fast length, the classic two-week / Wilder horizon.'], | |
| 275 | + ['slow', 28, 'Twice the mid length; the 1-2-4 spacing keeps the three cycles harmonically related.'], | |
| 276 | + ], | |
| 277 | + bestFor: { timeframes: 'Daily bars for the original divergence method; 1-hour bars with the same lengths for intraday swings.', markets: 'Stocks, futures and FX with reliable highs and lows.', note: 'Designed for divergence trading rather than simple level crossings.' }, | |
| 278 | + pairsWith: ['rsi', 'atr', 'obv'], | |
| 279 | + pitfalls: [ | |
| 280 | + 'The full setup (divergence + zone + breakout) is rare; forcing partial setups defeats the point.', | |
| 281 | + 'Needs true highs and lows — the daily-bar approximation on thin or gappy series degrades the reading.', | |
| 282 | + 'Weights 4-2-1 make the fast component dominant, so it is faster than a 28-bar look suggests.', | |
| 283 | + ], | |
| 284 | + history: 'Larry Williams, 1976, published in Technical Analysis of Stocks & Commodities in 1985.', | |
| 285 | + related: [{ label: 'Relative Strength Index', href: '/docs/charts/indicators/rsi' }, { label: 'Average True Range', href: '/docs/charts/indicators/atr' }], | |
| 286 | + }, | |
| 287 | + { | |
| 288 | + id: 'coppock', name: 'Coppock Curve', aka: ['Coppock', 'Coppock indicator'], category: 'momentum', pane: 'new', | |
| 289 | + oneLiner: 'WMA of two summed rates of change; a turn up from below zero marks major market bottoms.', | |
| 290 | + summary: 'The Coppock Curve adds a long and a short rate of change of price and smooths the sum with a weighted moving average. Coppock built it for monthly index data to identify the start of long-term bull markets: the buy signal is the curve turning upward from negative territory, and the indicator is drawn as a histogram coloured by its sign.', | |
| 291 | + formula: { text: 'Coppock = WMA(ROC(src, long) + ROC(src, short), wma)', vars: [['long', 'longer ROC length (default 14)'], ['short', 'shorter ROC length (default 11)'], ['wma', 'weighted moving average length (default 10)'], ['src', 'price source (default close)']] }, | |
| 292 | + howToRead: [ | |
| 293 | + 'Buy signal: the curve bottoms below zero and turns up — historically reliable on monthly S&P 500 and Dow data.', | |
| 294 | + 'Coppock did not define a sell signal; traders use a downturn from a high positive reading or a cross below zero as an exit.', | |
| 295 | + 'The sign tells the regime: positive means the sum of the two ROCs is still expanding — a bull phase.', | |
| 296 | + 'On daily bars the same rules work as a slow swing indicator, but the long-term "bottom finder" reputation applies to monthly data.', | |
| 297 | + ], | |
| 298 | + defaults: [ | |
| 299 | + ['wma', 10, 'Coppock\'s 10-period weighted average, chosen so the indicator turns cleanly once the two ROCs agree.'], | |
| 300 | + ['long', 14, 'The 14-month rate of change from Coppock\'s original 1962 Barron\'s article.'], | |
| 301 | + ['short', 11, 'The 11-month rate of change; 11 and 14 months were, per Coppock\'s anecdote, the grieving period after a loss suggested by clergy.'], | |
| 302 | + ['source', 'close', 'Closes feed both rates of change.'], | |
| 303 | + ], | |
| 304 | + bestFor: { timeframes: 'Weekly and monthly bars (the design case); daily bars as a slow momentum filter.', markets: 'Broad indices and index ETFs; less meaningful on single volatile stocks.', note: 'Needs 14 + 10 bars of history before the first value.' }, | |
| 305 | + pairsWith: ['roc', 'kst', 'sma'], | |
| 306 | + pitfalls: [ | |
| 307 | + 'Extremely slow: signals arrive weeks or months into the new trend.', | |
| 308 | + 'No sell rule by design — treating a downturn as a sell produces early exits in bull markets.', | |
| 309 | + 'On daily bars of a single stock it is just a smoothed ROC and loses its long-cycle rationale.', | |
| 310 | + ], | |
| 311 | + history: 'Edwin Sedge Coppock, 1962, in Barron\'s, originally for monthly Dow Jones data.', | |
| 312 | + related: [{ label: 'Rate of Change', href: '/docs/charts/indicators/roc' }, { label: 'Know Sure Thing', href: '/docs/charts/indicators/kst' }], | |
| 313 | + thumb: { bars: 160 }, | |
| 314 | + }, | |
| 315 | + { | |
| 316 | + id: 'dpo', name: 'Detrended Price Oscillator', aka: ['DPO'], category: 'momentum', pane: 'new', | |
| 317 | + oneLiner: 'Price minus a displaced SMA: removes the trend to expose the cycle around it.', | |
| 318 | + summary: 'The DPO subtracts from each price the simple moving average centred on it — the SMA shifted back by half its length plus one bar. That displacement aligns the average with the prices it summarises, so the difference isolates the short cycles around the trend instead of measuring momentum. Because of the shift, the last length / 2 + 1 bars have no value.', | |
| 319 | + formula: { text: 'shift = floor(n / 2) + 1\nDPO = src − SMA(src, n)[shift bars ago]', vars: [['n', 'SMA length (default 20)'], ['shift', 'displacement of the average (11 bars for n = 20)'], ['src', 'price source (default close)']] }, | |
| 320 | + howToRead: [ | |
| 321 | + 'Peaks and troughs of the DPO mark cycle highs and lows once the trend is removed — measure the distance between troughs to estimate the cycle length.', | |
| 322 | + 'Above zero: price is above its centred average (upper half of the cycle); below zero: lower half.', | |
| 323 | + 'Extreme DPO values relative to recent history flag cycle overbought / oversold conditions, useful for timing pullbacks inside a trend.', | |
| 324 | + 'The indicator ends before the last bars: it describes past cycles, not the current bar.', | |
| 325 | + ], | |
| 326 | + defaults: [ | |
| 327 | + ['length', 20, 'One trading month; cycles longer than 20 bars are treated as trend and removed.'], | |
| 328 | + ['source', 'close', 'Closes are the standard input.'], | |
| 329 | + ], | |
| 330 | + bestFor: { timeframes: 'Daily bars for cycle analysis; weekly bars for seasonal cycles.', markets: 'Commodities and indices with recognisable cycles.', note: 'A measurement tool for cycle length rather than an entry trigger.' }, | |
| 331 | + pairsWith: ['sma', 'stoch', 'roc'], | |
| 332 | + pitfalls: [ | |
| 333 | + 'The missing final bars are by design — the DPO can never tell you where the current bar stands.', | |
| 334 | + 'Choosing a length near the dominant cycle erases the very cycle you want to see; pick about half of it.', | |
| 335 | + 'It is not a momentum indicator despite its name and pane placement.', | |
| 336 | + ], | |
| 337 | + history: 'Attributed to the cycle-analysis literature of the 1970s–1980s; described by Walt Bressert and in Colby\'s Encyclopedia of Technical Market Indicators.', | |
| 338 | + related: [{ label: 'Simple Moving Average', href: '/docs/charts/indicators/sma' }, { label: 'Stochastic', href: '/docs/charts/indicators/stoch' }], | |
| 339 | + }, | |
| 340 | + { | |
| 341 | + id: 'kst', name: 'Know Sure Thing', aka: ['KST', 'Pring\'s KST'], category: 'momentum', pane: 'new', | |
| 342 | + oneLiner: 'Weighted sum of four smoothed rates of change plus a signal line: multi-cycle momentum.', | |
| 343 | + summary: 'KST combines four rates of change of different lengths, each smoothed by its own simple moving average and weighted 1 to 4 with the longest horizon counting most. Summing several cycles gives a smoother momentum curve than any single ROC, and a moving-average signal line turns it into a crossover system.', | |
| 344 | + formula: { text: 'KST = 1×SMA(ROC(src, r1), s1) + 2×SMA(ROC(src, r2), s2)\n + 3×SMA(ROC(src, r3), s3) + 4×SMA(ROC(src, r4), s4)\nsignal = SMA(KST, sig)', vars: [['r1…r4', 'ROC lengths (default 10, 15, 20, 30)'], ['s1…s4', 'SMA lengths applied to each ROC (default 10, 10, 10, 15)'], ['sig', 'signal SMA length (default 9)'], ['src', 'price source (default close)']] }, | |
| 345 | + howToRead: [ | |
| 346 | + 'KST crossing above its signal line is a buy, crossing below a sell — Pring\'s primary rule.', | |
| 347 | + 'Zero line: positive KST means the weighted momentum of all four cycles is net bullish.', | |
| 348 | + 'Divergence between KST peaks and price peaks is more trustworthy than on a single ROC because several horizons must agree.', | |
| 349 | + 'The direction of the signal line itself confirms the intermediate trend; trade crossovers only in that direction.', | |
| 350 | + ], | |
| 351 | + defaults: [ | |
| 352 | + ['roc1', 10, 'Pring\'s daily short-term set: the shortest cycle, two trading weeks.'], | |
| 353 | + ['roc2', 15, 'Three trading weeks.'], | |
| 354 | + ['roc3', 20, 'One trading month.'], | |
| 355 | + ['roc4', 30, 'Six weeks — the longest horizon, given the heaviest weight (4).'], | |
| 356 | + ['sma1', 10, 'Smoothing of the 10-bar ROC.'], | |
| 357 | + ['sma2', 10, 'Smoothing of the 15-bar ROC.'], | |
| 358 | + ['sma3', 10, 'Smoothing of the 20-bar ROC.'], | |
| 359 | + ['sma4', 15, 'Longer smoothing for the longest ROC so all four components have similar noise levels.'], | |
| 360 | + ['signal', 9, 'Nine-bar signal, the same convention as MACD.'], | |
| 361 | + ['source', 'close', 'Closes feed every rate of change.'], | |
| 362 | + ], | |
| 363 | + bestFor: { timeframes: 'Daily bars with these defaults; Pring published other sets for weekly and monthly charts.', markets: 'Indices, sectors and ETFs where cycle blending is meaningful.', note: 'Needs at least 45 bars of history before the first value.' }, | |
| 364 | + pairsWith: ['roc', 'coppock', 'macd'], | |
| 365 | + pitfalls: [ | |
| 366 | + 'Nine parameters invite over-fitting; keep one of Pring\'s published sets.', | |
| 367 | + 'Lag grows with the longest component — the 30-bar ROC smoothed over 15 bars reacts slowly.', | |
| 368 | + 'The 1-2-3-4 weighting means the long cycle dominates: short-term turns barely move the line.', | |
| 369 | + ], | |
| 370 | + history: 'Martin J. Pring, 1992, in Technical Analysis of Stocks & Commodities.', | |
| 371 | + related: [{ label: 'Rate of Change', href: '/docs/charts/indicators/roc' }, { label: 'Coppock Curve', href: '/docs/charts/indicators/coppock' }], | |
| 372 | + thumb: { bars: 160 }, | |
| 373 | + }, | |
| 374 | + { | |
| 375 | + id: 'tsi', name: 'True Strength Index', aka: ['TSI'], category: 'momentum', pane: 'new', | |
| 376 | + oneLiner: 'Double-smoothed price change over double-smoothed absolute change, −100…100, with signal.', | |
| 377 | + summary: 'The TSI takes one-bar price changes, smooths them twice with EMAs (a long then a short one), and divides by the same double smoothing of the absolute changes. The ratio is bounded between −100 and +100 and stays close to the sign of the trend, giving a momentum line that is smooth yet responsive; a signal EMA adds crossovers.', | |
| 378 | + formula: { text: 'm = src − src[1]\nTSI = 100 × EMA(EMA(m, long), short) / EMA(EMA(|m|, long), short)\nsignal = EMA(TSI, sig)', vars: [['long', 'first EMA length (default 25)'], ['short', 'second EMA length (default 13)'], ['sig', 'signal EMA length (default 13)'], ['src', 'price source (default close)']] }, | |
| 379 | + howToRead: [ | |
| 380 | + 'Zero-line cross: TSI positive means smoothed up-moves outweigh down-moves — bullish regime.', | |
| 381 | + 'Signal-line crossovers give the entries; the zero line gives the regime filter.', | |
| 382 | + 'Overbought / oversold: readings beyond ±25 are stretched for most stocks; the exact level depends on the instrument.', | |
| 383 | + 'Divergence between TSI and price is cleaner than on RSI thanks to the double smoothing.', | |
| 384 | + ], | |
| 385 | + defaults: [ | |
| 386 | + ['long', 25, 'Blau\'s first smoothing, long enough to remove day-to-day noise.'], | |
| 387 | + ['short', 13, 'Blau\'s second smoothing, roughly half the first; 25 / 13 was his published pair.'], | |
| 388 | + ['signal', 13, 'A signal EMA equal to the short smoothing, as in Blau\'s examples (7 is a common faster choice).'], | |
| 389 | + ['source', 'close', 'Closes are the standard input.'], | |
| 390 | + ], | |
| 391 | + bestFor: { timeframes: 'Daily and 4-hour bars; the defaults are calibrated for swing trading.', markets: 'Stocks, indices, FX — anything with persistent trends.', note: 'Bounded like RSI but with far fewer whipsaws in trends.' }, | |
| 392 | + pairsWith: ['macd', 'rsi', 'ema'], | |
| 393 | + pitfalls: [ | |
| 394 | + 'The double smoothing means a substantial lag on turns; treat crossovers as confirmation.', | |
| 395 | + 'No universal overbought level — calibrate ±25 / ±35 to the instrument\'s history.', | |
| 396 | + 'Needs about 40 bars of history before it stabilises.', | |
| 397 | + ], | |
| 398 | + history: 'William Blau, 1991, in Technical Analysis of Stocks & Commodities, later in Momentum, Direction, and Divergence (1995).', | |
| 399 | + related: [{ label: 'MACD', href: '/docs/charts/indicators/macd' }, { label: 'Relative Strength Index', href: '/docs/charts/indicators/rsi' }], | |
| 400 | + }, | |
| 401 | + { | |
| 402 | + id: 'rvi', name: 'Relative Vigor Index', aka: ['RVI', 'RVGI'], category: 'momentum', pane: 'new', | |
| 403 | + oneLiner: 'Close-minus-open over high-minus-low, symmetrically smoothed, with a signal line.', | |
| 404 | + summary: 'The Relative Vigor Index compares where bars close relative to where they open with the bar range: prices tend to close above the open in rising markets and below it in falling ones. Both numerator and denominator are passed through a four-bar symmetric weighted filter and a simple moving average, and the signal line is the same filter applied to the RVI.', | |
| 405 | + formula: { text: 'sym(x) = (x + 2·x[1] + 2·x[2] + x[3]) / 6\nRVI = SMA(sym(close − open), n) / SMA(sym(high − low), n)\nsignal = sym(RVI)', vars: [['n', 'averaging length (default 10)'], ['sym', 'four-bar symmetric weighted filter (1-2-2-1)'], ['x[k]', 'value k bars earlier']] }, | |
| 406 | + howToRead: [ | |
| 407 | + 'RVI crossing above its signal line is a buy, below a sell; the zero line tells whether closes are on average above or below opens.', | |
| 408 | + 'Positive and rising: bars are closing in their upper halves — buyers control the session.', | |
| 409 | + 'Divergence between RVI and price is Ehlers\' preferred use: a lower RVI high on a higher price high warns of exhaustion.', | |
| 410 | + 'Values live roughly between −0.5 and +0.5; the engine shows three decimals.', | |
| 411 | + ], | |
| 412 | + defaults: [ | |
| 413 | + ['length', 10, 'Ehlers\' default, tuned to the dominant short cycle of daily equity data; the 1-2-2-1 filter adds about three bars of smoothing.'], | |
| 414 | + ], | |
| 415 | + bestFor: { timeframes: 'Daily and hourly bars; the symmetric filter suits regular bar spacing.', markets: 'Liquid stocks, futures and FX where open and close are meaningful (not thin crypto pairs).', note: 'Sensitive to gaps because close − open ignores them.' }, | |
| 416 | + pairsWith: ['stoch', 'rsi', 'macd'], | |
| 417 | + pitfalls: [ | |
| 418 | + 'Bars with a gap open can show strong "vigor" even when the day itself was flat.', | |
| 419 | + 'Small values with several decimals make level reading awkward — use crossovers and shape.', | |
| 420 | + 'Illiquid instruments with random open prices produce noise.', | |
| 421 | + ], | |
| 422 | + history: 'John F. Ehlers, 2002, in Technical Analysis of Stocks & Commodities.', | |
| 423 | + related: [{ label: 'Stochastic', href: '/docs/charts/indicators/stoch' }, { label: 'Relative Strength Index', href: '/docs/charts/indicators/rsi' }], | |
| 424 | + }, | |
| 425 | + { | |
| 426 | + id: 'cmo', name: 'Chande Momentum Oscillator', aka: ['CMO'], category: 'momentum', pane: 'new', | |
| 427 | + oneLiner: 'Sum of gains minus losses over their total, −100…100, unsmoothed; ±50 mark the extremes.', | |
| 428 | + summary: 'The CMO divides the difference between the sum of up-moves and the sum of down-moves by their total over the last n bars. Unlike the RSI it uses raw sums instead of Wilder smoothing and is centred on zero, so it is faster, symmetric and reaches ±100 when every bar in the window moved the same way.', | |
| 429 | + formula: { text: 'CMO = 100 × (sum(up, n) − sum(down, n)) / (sum(up, n) + sum(down, n))', vars: [['n', 'lookback length (default 9)'], ['up', 'positive close-to-close changes (0 otherwise)'], ['down', 'absolute negative changes (0 otherwise)']] }, | |
| 430 | + howToRead: [ | |
| 431 | + 'Above +50: overbought (equivalent to RSI 75); below −50: oversold (RSI 25).', | |
| 432 | + 'Zero line: positive means gains outweigh losses over the window — a simple trend read.', | |
| 433 | + 'Chande used the absolute CMO level as a trend-strength filter: high |CMO| = trending, low |CMO| = choppy.', | |
| 434 | + 'Divergence between CMO extremes and price extremes works as with RSI, one or two bars earlier.', | |
| 435 | + ], | |
| 436 | + defaults: [ | |
| 437 | + ['length', 9, 'Chande\'s published default — shorter than RSI\'s 14 because raw sums are already less smoothed and he wanted a fast oscillator; 14 or 20 for a calmer line.'], | |
| 438 | + ['source', 'close', 'Closes are the standard input.'], | |
| 439 | + ], | |
| 440 | + bestFor: { timeframes: 'Intraday to daily; the 9-bar default suits short swings.', markets: 'Any liquid market; the bounded scale is comparable across symbols.', note: 'Also used to pick the adaptive length of Chande\'s VIDYA moving average.' }, | |
| 441 | + pairsWith: ['rsi', 'adx', 'ema'], | |
| 442 | + pitfalls: [ | |
| 443 | + 'Without smoothing it is noticeably noisier than RSI and can flip from +50 to −50 in a few bars.', | |
| 444 | + 'The zero-total case (no price change in the window) returns 0, which can look like neutrality on dead bars.', | |
| 445 | + 'A short length in a strong trend pins it near ±100 without any reversal implication.', | |
| 446 | + ], | |
| 447 | + history: 'Tushar Chande, 1994, in The New Technical Trader (with Stanley Kroll).', | |
| 448 | + related: [{ label: 'Relative Strength Index', href: '/docs/charts/indicators/rsi' }, { label: 'Stochastic RSI', href: '/docs/charts/indicators/stochrsi' }], | |
| 449 | + }, | |
| 450 | + { | |
| 451 | + id: 'elder-ray', name: 'Elder Ray', aka: ['Bull Power / Bear Power', 'Elder-Ray Index'], category: 'momentum', pane: 'new', | |
| 452 | + oneLiner: 'Bull power (high − EMA 13) and bear power (low − EMA 13) as two histograms around zero.', | |
| 453 | + summary: 'Elder Ray splits each bar into the buyers\' ability to push the high above the 13-bar EMA (bull power) and the sellers\' ability to push the low below it (bear power). The EMA stands for the consensus of value; the two histograms show how far each side managed to move price away from it, and the EMA\'s slope gives the trend direction.', | |
| 454 | + formula: { text: 'EMA = EMA(close, n)\nbull = high − EMA\nbear = low − EMA', vars: [['n', 'EMA length (default 13)'], ['bull', 'bull power — positive when the high is above the EMA'], ['bear', 'bear power — negative when the low is below the EMA']] }, | |
| 455 | + howToRead: [ | |
| 456 | + 'Buy in an uptrend (rising EMA) when bear power is negative but rising — sellers are weakening while the trend is up.', | |
| 457 | + 'Sell in a downtrend (falling EMA) when bull power is positive but falling.', | |
| 458 | + 'Bear power making a higher low while price makes a lower low is a strong bullish divergence; bull power lower highs on higher price highs are bearish.', | |
| 459 | + 'Bull power negative in an uptrend, or bear power positive in a downtrend, is abnormal and warns the trend is in trouble.', | |
| 460 | + ], | |
| 461 | + defaults: [ | |
| 462 | + ['length', 13, 'Elder\'s 13-bar EMA — his standard value proxy on daily charts, also used in the Triple Screen system.'], | |
| 463 | + ], | |
| 464 | + bestFor: { timeframes: 'Daily bars first (Elder\'s design), then weekly for the trend and daily for entries.', markets: 'Stocks and futures with reliable highs and lows.', note: 'Read together with the EMA slope on the price pane — add an EMA 13 overlay.' }, | |
| 465 | + pairsWith: ['ema', 'macd', 'force'], | |
| 466 | + pitfalls: [ | |
| 467 | + 'Both histograms are in price units — compare shapes and divergences, not absolute values across symbols.', | |
| 468 | + 'Gaps push both powers to the same side of zero; single outlier bars distort the read.', | |
| 469 | + 'Without the EMA slope as a filter, the individual histograms whipsaw.', | |
| 470 | + ], | |
| 471 | + history: 'Dr. Alexander Elder, 1993, in Trading for a Living.', | |
| 472 | + related: [{ label: 'Exponential Moving Average', href: '/docs/charts/indicators/ema' }, { label: 'Force Index', href: '/docs/charts/indicators/force' }], | |
| 473 | + }, | |
| 474 | +] | |
added
hfmarketdata/web/src/charts/catalog/indicators/trend.js
+287 −0
@@ -0,0 +1,287 @@ | ||
| 1 | +// Encyclopedia entries — trend overlays, trend oscillators (Vortex, Aroon, ADX) and price-structure levels | |
| 2 | +// (ZigZag, Pivot Points, Auto Fibonacci). Schema: ../index.js. Engine facts: ../../indicators/{moving-averages,bands,overlays,momentum,oscillators}.js. | |
| 3 | + | |
| 4 | +const IND = id => `/docs/charts/indicators/${id}` | |
| 5 | +const TOOL = id => `/docs/charts/tools/${id}` | |
| 6 | +const GUIDE = { label: 'Charting guide', href: '/docs/charts' } | |
| 7 | + | |
| 8 | +export default [ | |
| 9 | + { | |
| 10 | + id: 'sma', name: 'Simple Moving Average', aka: ['SMA', 'Moving average', 'Arithmetic mean'], category: 'trend', pane: 'main', | |
| 11 | + oneLiner: 'Arithmetic mean of the last n prices — the baseline trend filter and dynamic support.', | |
| 12 | + summary: 'The SMA averages the last n values of the source with equal weights, so every bar in the window counts the same and a bar leaving the window matters as much as the one entering it. It lags price by roughly half its length, which is exactly what makes it a calm reference for the direction of the trend.', | |
| 13 | + formula: { text: 'SMA_t = (P_t + P_{t−1} + … + P_{t−n+1}) / n', vars: [['n', 'window length (default 20)'], ['P', 'source price (close by default)']] }, | |
| 14 | + howToRead: ['Price above a rising SMA = uptrend; below a falling SMA = downtrend. The slope matters more than the crossing.', 'Crossovers of two SMAs (50 over 200 = "golden cross", 50 under 200 = "death cross") are slow but widely watched regime signals.', 'In trends the SMA acts as dynamic support or resistance — pullbacks that hold the 20 or 50 SMA are continuation setups.', 'A flat SMA with price whipsawing through it means no trend: switch to range tools.', 'The 200-day SMA is the institutional line between bull and bear markets for equities.'], | |
| 15 | + defaults: [['length', 20, 'One month of daily trading sessions; 10 tracks the swing, 50 the intermediate trend, 200 the primary trend.'], ['source', 'close', 'Closes carry the settlement information; hl2 or ohlc4 smooth the noise of gappy intraday series.']], | |
| 16 | + bestFor: { timeframes: 'Daily and weekly for regime reading; 1-hour and above for dynamic support.', markets: 'Every liquid instrument; the 50 / 200 combination is an equity-index standard.', note: 'Use two lengths (fast / slow) to read acceleration of the trend, not one.' }, | |
| 17 | + pairsWith: ['ema', 'adx', 'volume-ma'], | |
| 18 | + pitfalls: ['The equal weighting produces a "drop-off effect": the line can jump when an old extreme bar leaves the window even if price is quiet.', 'Crossover systems bleed in sideways markets — dozens of small losses between two good trends.', 'Half-length lag means the SMA confirms turns late; it is a filter, not a timing tool.'], | |
| 19 | + history: 'Classic tool of technical analysis, popularised by the chartists of the 1920s–1930s (Richard Schabacker, Robert Rhea); no single author.', | |
| 20 | + related: [{ label: 'Exponential Moving Average', href: IND('ema') }, { label: 'Hull Moving Average', href: IND('hma') }, GUIDE], | |
| 21 | + }, | |
| 22 | + { | |
| 23 | + id: 'ema', name: 'Exponential Moving Average', aka: ['EMA', 'Exponentially weighted MA'], category: 'trend', pane: 'main', | |
| 24 | + oneLiner: 'Recency-weighted average that reacts faster than the SMA with less drop-off jitter.', | |
| 25 | + summary: 'The EMA blends each new price into the previous average with a fixed weight α, so recent bars dominate and the oldest bars fade out geometrically instead of falling off a cliff. It follows price more closely than an SMA of the same length while remaining smooth.', | |
| 26 | + formula: { text: 'EMA_t = α·P_t + (1 − α)·EMA_{t−1}, α = 2 / (n + 1)\nEMA seeded with the SMA of the first n bars', vars: [['n', 'nominal length (default 20)'], ['α', 'smoothing factor, 2/(n+1) — 0.095 for n = 20'], ['P', 'source price']] }, | |
| 27 | + howToRead: ['Same rules as the SMA — direction of slope and side of price — but signals arrive one to three bars earlier.', 'The 12 / 26 pair is the backbone of MACD; the 8 / 21 pair is a popular intraday momentum ribbon.', 'Price riding the 9 or 20 EMA without closing across it marks a strong, orderly trend.', 'A widening gap between price and the EMA signals an extended move that often snaps back to the line.', 'EMA of a longer length (200) is a slower but smoother regime filter than the SMA equivalent.'], | |
| 28 | + defaults: [['length', 20, 'One month of sessions; α ≈ 0.095 keeps about 86 % of the weight inside the last 20 bars. 9 and 21 are the day-trading standards.'], ['source', 'close', 'The close is the value the market agreed on; use hl2 to reduce single-print spikes on thin markets.']], | |
| 29 | + bestFor: { timeframes: 'All timeframes; the reactivity pays most on 1-minute to 1-hour bars.', markets: 'Trending markets — futures, FX majors, momentum stocks.', note: 'When you want speed, shorten the EMA; when you want smoothness at the same speed, look at the Hull MA.' }, | |
| 30 | + pairsWith: ['macd', 'sma', 'supertrend'], | |
| 31 | + pitfalls: ['Faster also means more false crossings in chop.', 'The first bars after the seed are not fully "warmed up": the EMA still remembers the seed for about 3n bars.', 'Two EMAs of nearby lengths cross constantly — keep a ratio of 2–3 between fast and slow.'], | |
| 32 | + history: 'Introduced to trading by P. N. "Pete" Haurlan in the early 1960s from missile-tracking exponential smoothing; formalised in Robert Brown\'s statistical work of 1956.', | |
| 33 | + related: [{ label: 'MACD', href: IND('macd') }, { label: 'DEMA', href: IND('dema') }, { label: 'TEMA', href: IND('tema') }], | |
| 34 | + }, | |
| 35 | + { | |
| 36 | + id: 'wma', name: 'Weighted Moving Average', aka: ['WMA', 'Linearly weighted MA', 'LWMA'], category: 'trend', pane: 'main', | |
| 37 | + oneLiner: 'Linearly weighted average: the newest bar counts n times the oldest — faster than the SMA.', | |
| 38 | + summary: 'The WMA assigns weights 1, 2, …, n to the bars of the window from oldest to newest and divides by their sum. It sits between the SMA and the EMA in responsiveness and is the building block of the Hull Moving Average.', | |
| 39 | + formula: { text: 'WMA_t = Σ_{i=0}^{n−1} (n − i)·P_{t−i} / (n·(n+1)/2)', vars: [['n', 'window length (default 20)'], ['P_{t−i}', 'source price i bars ago'], ['n·(n+1)/2', 'sum of the weights 1 + 2 + … + n']] }, | |
| 40 | + howToRead: ['Read like any moving average: slope for direction, price side for bias, crossings of two lengths for regime changes.', 'The WMA turns earlier than an SMA of the same length, so a WMA / SMA crossover is an early-warning of a trend change.', 'Because the oldest bars carry little weight, the WMA has almost no drop-off effect.', 'Compare with the EMA: the WMA has a hard window (a bar older than n has zero weight), the EMA never fully forgets.'], | |
| 41 | + defaults: [['length', 20, 'One month of sessions, comparable to the default SMA and EMA so the three can be laid side by side.'], ['source', 'close', 'Standard settlement price; switch to hlc3 for a typical-price version.']], | |
| 42 | + bestFor: { timeframes: 'Daily and intraday swing charts.', markets: 'Any liquid market; often used on FX where traders want speed without EMA memory.' }, | |
| 43 | + pairsWith: ['hma', 'sma', 'ema'], | |
| 44 | + pitfalls: ['Still lags by about a third of its length — it is not a leading indicator.', 'The heavy recency weight amplifies a single outlier bar for several periods.'], | |
| 45 | + history: 'Classic weighting scheme from time-series statistics; adopted by technical analysts in the 1970s, no single author.', | |
| 46 | + related: [{ label: 'Hull Moving Average', href: IND('hma') }, { label: 'Simple Moving Average', href: IND('sma') }], | |
| 47 | + }, | |
| 48 | + { | |
| 49 | + id: 'hma', name: 'Hull Moving Average', aka: ['HMA', 'Hull MA'], category: 'trend', pane: 'main', | |
| 50 | + oneLiner: 'Almost lag-free smooth average built from three weighted MAs; hugs the trend.', | |
| 51 | + summary: 'The HMA combines two WMAs (length n and n/2) to cancel most of the lag, then smooths the result with a WMA of length √n to remove the noise the correction introduced. The line is remarkably smooth yet turns almost with price.', | |
| 52 | + formula: { text: 'raw_t = 2·WMA(P, n/2) − WMA(P, n)\nHMA_t = WMA(raw, √n)', vars: [['n', 'length (default 16)'], ['WMA(x, k)', 'linearly weighted average of x over k bars'], ['√n', 'rounded to the nearest integer (4 for n = 16)']] }, | |
| 53 | + howToRead: ['Trade the colour of the slope: HMA rising = long bias, falling = short bias; turns are the signal.', 'Because the lag is small, price rarely strays far from the HMA in trends — a close on the wrong side is meaningful.', 'Use a long HMA (55–100) as a smooth trend backdrop and a short one (9–21) for entries.', 'The HMA can overshoot: after a sharp move it briefly runs past price before settling.'], | |
| 54 | + defaults: [['length', 16, 'Alan Hull\'s original example; a perfect square so √n = 4 exactly. 9 and 21 are also squares-friendly popular lengths.'], ['source', 'close', 'Closes; the HMA is already smooth, so a smoothed source is rarely needed.']], | |
| 55 | + bestFor: { timeframes: 'Intraday to daily; shines on 5-minute to 1-hour bars where lag is costly.', markets: 'Trending futures, crypto and momentum stocks.', note: 'Not a support / resistance line — treat it as a direction indicator.' }, | |
| 56 | + pairsWith: ['adx', 'supertrend', 'wma'], | |
| 57 | + pitfalls: ['The 2·WMA − WMA step is an extrapolation: the HMA can print values outside the recent price range.', 'In tight ranges the frequent slope changes produce whipsaws — add a volatility filter.', 'Lengths that are not perfect squares round √n and behave slightly differently from what the length suggests.'], | |
| 58 | + history: 'Alan Hull, 2005 (Australia), published on his site and in Active Investing.', | |
| 59 | + related: [{ label: 'Weighted Moving Average', href: IND('wma') }, { label: 'Kaufman Adaptive MA', href: IND('kama') }], | |
| 60 | + }, | |
| 61 | + { | |
| 62 | + id: 'dema', name: 'Double Exponential Moving Average', aka: ['DEMA'], category: 'trend', pane: 'main', | |
| 63 | + oneLiner: 'EMA with the lag of its own smoothing subtracted — quicker turns, same length.', | |
| 64 | + summary: 'The DEMA takes an EMA, computes the EMA of that EMA, and subtracts the second from twice the first. The difference between the two EMAs estimates the lag, so removing it produces a line that turns sooner than a plain EMA of the same length while staying smoother than a shorter EMA.', | |
| 65 | + formula: { text: 'E1 = EMA(P, n)\nE2 = EMA(E1, n)\nDEMA = 2·E1 − E2', vars: [['n', 'length (default 20)'], ['E1', 'first EMA of the source'], ['E2', 'EMA of E1 (the "smoothing of the smoothing")']] }, | |
| 66 | + howToRead: ['Use it where you would use an EMA when you want earlier turns without shortening the length.', 'A DEMA / EMA pair of the same length: the DEMA leads, the EMA confirms — the gap between them measures acceleration.', 'Price closing across the DEMA is a faster trend-change trigger than the equivalent EMA cross.', 'Like every lag-corrected average it can extrapolate slightly beyond price after sharp moves.'], | |
| 67 | + defaults: [['length', 20, 'Same nominal length as the default EMA so the lag reduction is visible side by side; Mulloy\'s articles used 20 and 50.'], ['source', 'close', 'Closes; the double smoothing already suppresses intrabar noise.']], | |
| 68 | + bestFor: { timeframes: 'Daily swing and intraday trend following.', markets: 'Trending stocks and futures.' }, | |
| 69 | + pairsWith: ['tema', 'ema', 'macd'], | |
| 70 | + pitfalls: ['Faster turns in trends also mean more false turns in ranges.', 'Not a true "average" — it can sit outside the range of the prices it was computed from.'], | |
| 71 | + history: 'Patrick G. Mulloy, 1994, "Smoothing Data with Faster Moving Averages", Technical Analysis of Stocks & Commodities.', | |
| 72 | + related: [{ label: 'Triple Exponential MA', href: IND('tema') }, { label: 'Exponential Moving Average', href: IND('ema') }], | |
| 73 | + }, | |
| 74 | + { | |
| 75 | + id: 'tema', name: 'Triple Exponential Moving Average', aka: ['TEMA'], category: 'trend', pane: 'main', | |
| 76 | + oneLiner: 'Three nested EMAs recombined to cancel lag — the fastest of the EMA family.', | |
| 77 | + summary: 'The TEMA extends the DEMA idea one level further: it combines an EMA, the EMA of that EMA and the EMA of that result so that the lag terms cancel out. The line reacts almost immediately to changes of direction while keeping the smoothness of an exponential filter.', | |
| 78 | + formula: { text: 'E1 = EMA(P, n), E2 = EMA(E1, n), E3 = EMA(E2, n)\nTEMA = 3·E1 − 3·E2 + E3', vars: [['n', 'length (default 20)'], ['E1, E2, E3', 'single, double and triple smoothed EMAs of the source']] }, | |
| 79 | + howToRead: ['Direction of the slope is the primary read; the TEMA turns within a bar or two of price.', 'TEMA above a slower TEMA or SMA = trend up; a close back through the TEMA is an early exit.', 'Use it as the fast line of a crossover system to reduce the lag of classic EMA pairs.', 'The overshoot after a spike is larger than the DEMA\'s — wait for one bar of confirmation.'], | |
| 80 | + defaults: [['length', 20, 'Matches the EMA / DEMA defaults so the three curves can be compared directly; 21 and 50 are common alternates.'], ['source', 'close', 'Standard; the triple smoothing makes the source choice nearly irrelevant.']], | |
| 81 | + bestFor: { timeframes: 'Intraday (5-minute to 1-hour) and daily.', markets: 'Fast-moving futures and crypto, momentum stocks.' }, | |
| 82 | + pairsWith: ['dema', 'trix', 'adx'], | |
| 83 | + pitfalls: ['The most sensitive of the family — expect whipsaws in ranges and use it with a trend filter such as ADX.', 'Overshoots can make the TEMA a poor stop reference.', 'Needs about 3n bars of warm-up before the nested EMAs stabilise.'], | |
| 84 | + history: 'Patrick G. Mulloy, 1994, Technical Analysis of Stocks & Commodities (companion to the DEMA).', | |
| 85 | + related: [{ label: 'DEMA', href: IND('dema') }, { label: 'TRIX', href: IND('trix') }], | |
| 86 | + }, | |
| 87 | + { | |
| 88 | + id: 'kama', name: 'Kaufman Adaptive Moving Average', aka: ['KAMA', 'Adaptive MA'], category: 'trend', pane: 'main', | |
| 89 | + oneLiner: 'Speeds up in trends, flattens in noise: the smoothing adapts to the efficiency ratio.', | |
| 90 | + summary: 'KAMA measures how directional the last n bars were — the net move divided by the sum of every bar-to-bar move — and turns that efficiency ratio into a variable EMA constant between a fast and a slow speed. In a clean trend it behaves like a 2-period EMA; in chop it slows to a 30-period EMA and stops reacting.', | |
| 91 | + formula: { text: 'ER = |P_t − P_{t−n}| / Σ_{i=1}^{n} |P_{t−i+1} − P_{t−i}|\nSC = [ER·(2/(fast+1) − 2/(slow+1)) + 2/(slow+1)]²\nKAMA_t = KAMA_{t−1} + SC·(P_t − KAMA_{t−1})', vars: [['n', 'efficiency-ratio window (default 10)'], ['ER', 'efficiency ratio, 1 = perfectly directional, 0 = pure noise'], ['fast / slow', 'EMA lengths of the fastest and slowest allowed speeds (2 / 30)'], ['SC', 'smoothing constant of the bar']] }, | |
| 92 | + howToRead: ['A flat KAMA means the market is inefficient (noisy): stand aside or fade extremes.', 'When KAMA starts to slope and price closes on the sloping side, a trend has become efficient — that is the entry.', 'Price crossing a flat KAMA is not a signal; crossing a sloping KAMA is.', 'Kaufman\'s own filter: enter when KAMA moves more than a small multiple of its recent standard deviation.'], | |
| 93 | + defaults: [['length', 10, 'Kaufman\'s recommendation: two weeks of daily bars are enough to judge directional efficiency.'], ['fast', 2, 'Fastest allowed EMA (α = 0.667) so the line can nearly track price in a perfect trend.'], ['slow', 30, 'Slowest allowed EMA (α = 0.065) so the line practically freezes in noise.'], ['source', 'close', 'Closes; noise in the source directly lowers the efficiency ratio.']], | |
| 94 | + bestFor: { timeframes: 'Daily and 1-hour bars; effective on any timeframe with alternating trends and ranges.', markets: 'Futures and FX, where trend / noise regimes alternate.', note: 'Read the flatness of the line as an information signal in its own right.' }, | |
| 95 | + pairsWith: ['adx', 'choppiness', 'ema'], | |
| 96 | + pitfalls: ['The squared smoothing constant makes KAMA very slow to restart after a long range — the first leg of a new trend is often missed.', 'Efficiency does not mean profitability: a slow, steady grind scores high while a fast volatile trend can score low.'], | |
| 97 | + history: 'Perry J. Kaufman, 1995, in Smarter Trading (refined in Trading Systems and Methods).', | |
| 98 | + related: [{ label: 'Choppiness Index', href: IND('choppiness') }, { label: 'Arnaud Legoux MA', href: IND('alma') }], | |
| 99 | + }, | |
| 100 | + { | |
| 101 | + id: 'alma', name: 'Arnaud Legoux Moving Average', aka: ['ALMA'], category: 'trend', pane: 'main', | |
| 102 | + oneLiner: 'Gaussian-weighted average with an adjustable offset: tune smoothness against lag.', | |
| 103 | + summary: 'ALMA weights the bars of the window with a Gaussian bell whose centre is shifted toward the most recent bars by the offset parameter and whose width is set by sigma. Moving the centre reduces lag; widening the bell increases smoothness — two independent knobs instead of one length.', | |
| 104 | + formula: { text: 'm = offset·(n − 1), s = n / sigma\nw_i = exp(−(i − m)² / (2·s²)), i = 0 … n−1 (0 = oldest bar)\nALMA_t = Σ w_i·P_{t−(n−1−i)} / Σ w_i', vars: [['n', 'window length (default 9)'], ['offset', '0 … 1, where the bell peaks (1 = newest bar)'], ['sigma', 'bell width control — larger = narrower bell = more responsive'], ['w_i', 'Gaussian weight of bar i']] }, | |
| 105 | + howToRead: ['Use it as a smooth trend line: slope for bias, closes across it for entries and exits.', 'With offset 0.85 the line keeps a small lag but filters the single-bar noise that trips shorter EMAs.', 'Lower the offset (0.5) for a symmetric filter that is very smooth but lags like an SMA; raise it toward 1 for speed.', 'Two ALMAs of different lengths cross cleaner than EMAs because the Gaussian kernel damps oscillation.'], | |
| 106 | + defaults: [['length', 9, 'Legoux\'s published default; short enough for swing trading, and the Gaussian weights make it smoother than a 9 EMA.'], ['offset', 0.85, 'Original default: bell centred at 85 % of the window — most weight on recent bars with a little lag traded for smoothness.'], ['sigma', 6, 'Original default; sigma 6 gives a moderately narrow bell. Lower values (2–3) smooth more, higher values (8–10) track price.'], ['source', 'close', 'Standard settlement price.']], | |
| 107 | + bestFor: { timeframes: 'Intraday to daily.', markets: 'FX and index futures, where it was designed to filter noise.' }, | |
| 108 | + pairsWith: ['kama', 'hma', 'rsi'], | |
| 109 | + pitfalls: ['Three parameters invite over-fitting — change one at a time.', 'Offset near 1 degenerates into a nearly unsmoothed series.'], | |
| 110 | + history: 'Arnaud Legoux and Dimitrios Kouzis-Loukas, 2009.', | |
| 111 | + related: [{ label: 'Hull Moving Average', href: IND('hma') }, { label: 'Kaufman Adaptive MA', href: IND('kama') }], | |
| 112 | + }, | |
| 113 | + { | |
| 114 | + id: 'lsma', name: 'Linear Regression (LSMA + channel)', aka: ['LSMA', 'Least squares MA', 'Linear regression curve', 'Regression channel'], category: 'trend', pane: 'main', | |
| 115 | + oneLiner: 'End point of the best-fit line over n bars, with a ±k·σ channel of the residuals.', | |
| 116 | + summary: 'For every bar the indicator fits a straight line through the last n prices by least squares and plots the value of that line at the current bar; the result is a moving average that leans toward where the trend is heading rather than where it has been. The channel adds bands at ± mult standard deviations of the fit residuals, framing how far price strays from its local trend.', | |
| 117 | + formula: { text: 'fit P_i ≈ a + b·i over the last n bars (ordinary least squares)\nLSMA_t = a + b·(n − 1)\nupper / lower = LSMA_t ± mult · σ(residuals)', vars: [['n', 'regression window (default 25)'], ['a, b', 'intercept and slope of the fitted line'], ['σ(residuals)', 'standard deviation of price minus the fitted line over the window'], ['mult', 'channel width in σ (default 2)']] }, | |
| 118 | + howToRead: ['The slope of the LSMA is the local trend; a change of sign is an early trend-change alert.', 'Price at the upper band is stretched above its trend — expect mean reversion in ranges, or a continuation flag in strong trends.', 'A narrowing channel signals a quiet, orderly trend; a widening one signals a breakout or a breakdown of the fit.', 'The LSMA leads the SMA of the same length by roughly n/2 bars — compare them to see the lag you save.', 'Because it extrapolates, the LSMA can print outside the price range after a sharp reversal.'], | |
| 119 | + defaults: [['length', 25, 'About five weeks of daily bars — long enough for a meaningful regression, short enough to bend with swings.'], ['mult', 2, 'Two standard deviations contain roughly 95 % of the residuals if they are normal — the same convention as Bollinger Bands.'], ['source', 'close', 'Closes define the fitted line; hl2 gives a slightly smoother fit.']], | |
| 120 | + bestFor: { timeframes: 'Daily and 4-hour swing charts; intraday on 15-minute and above.', markets: 'Any market; especially readable on trending stocks and indices.', note: 'For a channel anchored between two chosen bars, use the Regression trend drawing tool instead.' }, | |
| 121 | + pairsWith: ['bollinger', 'stddev', 'adx'], | |
| 122 | + pitfalls: ['A linear fit through a curved move (parabolic rally) is misleading — the channel will look violated while the trend is intact.', 'The channel is symmetric and assumes roughly normal residuals; gaps and spikes break that assumption.', 'Long windows fit the past well and the present poorly: the end point lags at turns.'], | |
| 123 | + history: 'Least-squares regression dates to Legendre and Gauss (1805–1809); its use as a moving average and channel was popularised by Gilbert Raff (1991) and Tushar Chande.', | |
| 124 | + related: [{ label: 'Regression trend (drawing tool)', href: TOOL('regression') }, { label: 'Bollinger Bands', href: IND('bollinger') }, { label: 'Standard Deviation', href: IND('stddev') }], | |
| 125 | + }, | |
| 126 | + { | |
| 127 | + id: 'envelope', name: 'Moving Average Envelope', aka: ['MA Envelope', 'Percentage bands', 'Trading bands'], category: 'trend', pane: 'main', | |
| 128 | + oneLiner: 'A moving average with bands a fixed percentage above and below — the simplest channel.', | |
| 129 | + summary: 'The envelope draws a moving average and two lines offset by a fixed percentage of its value. Unlike Bollinger or Keltner channels the width does not adapt to volatility, which makes it a stable yardstick for "how far is too far" on a given instrument and timeframe.', | |
| 130 | + formula: { text: 'middle = MA(P, n) (SMA or EMA)\nupper = middle · (1 + percent/100)\nlower = middle · (1 − percent/100)', vars: [['n', 'moving-average length (default 20)'], ['percent', 'half-width of the envelope in % (default 2.5)'], ['ma', 'average type, sma or ema']] }, | |
| 131 | + howToRead: ['In a range, touches of the bands are fade points: sell the upper, buy the lower, target the middle.', 'In a trend, a close outside the envelope is a breakout signal (the classic envelope system of the 1970s).', 'Price hugging one band for many bars is a persistent trend — do not fade it.', 'Adjust the percentage until roughly 90 % of the closes stay inside; the tuned width becomes a volatility benchmark.'], | |
| 132 | + defaults: [['length', 20, 'A one-month average; the classic envelope literature used 20–25 days for stocks.'], ['percent', 2.5, 'A sensible starting half-width for daily equity bars; use 0.5–1 % on intraday charts, 5–10 % on volatile crypto.'], ['ma', 'sma', 'Simple average as in the original studies; ema for a faster middle line.'], ['source', 'close', 'The percentage is applied to the average of closes.']], | |
| 133 | + bestFor: { timeframes: 'Daily for stocks; any timeframe once the percentage is tuned.', markets: 'Range-bound stocks and ETFs; FX pairs with stable volatility.' }, | |
| 134 | + pairsWith: ['bollinger', 'keltner', 'rsi'], | |
| 135 | + pitfalls: ['A fixed width ignores volatility regimes: the same 2.5 % is far too wide in calm markets and far too narrow in crashes.', 'Must be re-tuned per instrument and timeframe.'], | |
| 136 | + history: 'Classic trading-band technique of the 1960s–1970s (J. M. Hurst\'s cycle envelopes, 1970); no single inventor.', | |
| 137 | + related: [{ label: 'Bollinger Bands', href: IND('bollinger') }, { label: 'Keltner Channels', href: IND('keltner') }], | |
| 138 | + }, | |
| 139 | + { | |
| 140 | + id: 'vwap', name: 'Volume-Weighted Average Price', aka: ['VWAP', 'Session VWAP'], category: 'trend', pane: 'main', | |
| 141 | + oneLiner: 'Volume-weighted average price since the session open — the institutional fair value.', | |
| 142 | + summary: 'VWAP accumulates price × volume and divides by the accumulated volume from the anchor onward, restarting at every new session on intraday bars. It is the benchmark against which execution desks measure fills, which makes it a magnet and a battleground for intraday price. Optional bands at ± k volume-weighted standard deviations frame how far price has drifted from fair value.', | |
| 143 | + formula: { text: 'VWAP_t = Σ_{session} (TP_i · V_i) / Σ_{session} V_i, TP = (H + L + C) / 3\nupper / lower = VWAP ± bands · σ_vw, σ_vw = √( Σ V_i·(TP_i − VWAP)² / Σ V_i )', vars: [['TP', 'typical price of the bar'], ['V', 'bar volume'], ['anchor', 'session (restart each calendar day) or all (accumulate from the first loaded bar)'], ['bands', 'number of volume-weighted σ for the optional bands (0 = off)']] }, | |
| 144 | + howToRead: ['Price above VWAP: buyers are in control of the session; below: sellers. The first test of VWAP after the open is a key decision point.', 'Institutions buying below VWAP and selling above it means VWAP acts as intraday support / resistance.', 'A steadily rising VWAP with price above it is a trend day; price crossing VWAP repeatedly is a rotation day.', 'With bands on, ±1σ marks normal drift, ±2σ an extended move that often reverts toward VWAP.', 'The gap between VWAP and price at the close tells you who won the day.'], | |
| 145 | + defaults: [['anchor', 'session', 'Execution benchmarks are per trading day, so the accumulation restarts at each calendar-day change on intraday bars.'], ['bands', 0, 'Off by default to keep the classic single line; 1 or 2 σ adds the standard-deviation channel.']], | |
| 146 | + bestFor: { timeframes: 'Intraday only — 1-minute to 1-hour bars. On daily bars use the "all" anchor or the Anchored VWAP.', markets: 'Stocks, ETFs and futures with real volume; not meaningful on FX pairs without volume.', note: 'Bars without volume yield no value.' }, | |
| 147 | + pairsWith: ['avwap', 'volume-ma', 'volume-profile'], | |
| 148 | + pitfalls: ['On daily bars a session anchor resets every bar — switch the anchor to "all" or use the Anchored VWAP.', 'Early in the session VWAP is built from a few bars and swings wildly; give it 15–30 minutes.', 'Pre-market volume is included when the data contains it, which shifts the session value.'], | |
| 149 | + history: 'Introduced as an execution benchmark by equity trading desks in the late 1980s (Berkowitz, Logue and Noser, 1988).', | |
| 150 | + related: [{ label: 'Anchored VWAP', href: IND('avwap') }, { label: 'Volume Profile', href: IND('volume-profile') }, { label: 'Time zones & sessions', href: '/docs/time-zones' }], | |
| 151 | + thumb: { volume: true, params: { anchor: 'all', bands: 1 } }, // daily bars: a session anchor would restart on every bar | |
| 152 | + }, | |
| 153 | + { | |
| 154 | + id: 'avwap', name: 'Anchored VWAP', aka: ['AVWAP', 'Anchored volume-weighted average price'], category: 'trend', pane: 'main', | |
| 155 | + oneLiner: 'VWAP starting from a bar you choose — the average cost of everyone in since that event.', | |
| 156 | + summary: 'The Anchored VWAP accumulates volume-weighted price from a specific bar — an earnings gap, a swing low, a Fed meeting — instead of the session open. It tells you the average price at which participants have traded since that event, so it marks where the crowd that entered after the anchor is in profit or under water. Bands at ± k σ show the dispersion around that cost basis.', | |
| 157 | + formula: { text: 'AVWAP_t = Σ_{i ≥ anchor} (TP_i · V_i) / Σ_{i ≥ anchor} V_i\nupper / lower = AVWAP ± bands · σ_vw', vars: [['anchor', 'timestamp of the first bar included (not set → the first loaded bar)'], ['TP', 'typical price (H + L + C) / 3'], ['V', 'bar volume'], ['bands', 'σ multiples for the bands (default 1)']] }, | |
| 158 | + howToRead: ['Anchor at a significant low: as long as price holds above the AVWAP, buyers since the low remain in profit and defend the level.', 'Anchor at a major high: the AVWAP falling from it is the resistance sellers defend.', 'Two AVWAPs (from the last high and the last low) pinch price into a decision zone; the break out of the pinch is the trade.', 'A retest of the AVWAP from above that holds is a high-quality continuation entry.', 'The bands widen as the move ages — a touch of the outer band far from the anchor is an extended move.'], | |
| 159 | + defaults: [['anchor', null, 'No anchor set: the accumulation starts at the first loaded bar. Set it to the bar of the event you want to measure from (settings → anchor).'], ['bands', 1, 'One volume-weighted σ frames the normal dispersion of prices around the cost basis.']], | |
| 160 | + bestFor: { timeframes: 'Every timeframe with volume — from 1-minute anchors at the open to daily anchors at earnings or yearly lows.', markets: 'Stocks, ETFs, futures, crypto with volume.', note: 'The anchor is in your settings only — it does not move when you scroll.' }, | |
| 161 | + pairsWith: ['vwap', 'volume-profile', 'obv'], | |
| 162 | + pitfalls: ['A poorly chosen anchor (a random bar) measures nothing; anchor to events the market itself reacted to.', 'Scrolling far back past the anchor shows no line — the AVWAP only exists after its anchor.', 'On symbols with no volume the indicator is empty.'], | |
| 163 | + history: 'Popularised by Brian Shannon (Alphatrends) in the 2000s–2010s, building on the session VWAP benchmark.', | |
| 164 | + related: [{ label: 'VWAP', href: IND('vwap') }, { label: 'Volume Profile', href: IND('volume-profile') }], | |
| 165 | + thumb: { volume: true }, | |
| 166 | + }, | |
| 167 | + { | |
| 168 | + id: 'supertrend', name: 'Supertrend', aka: ['ST', 'ATR trailing stop'], category: 'trend', pane: 'main', | |
| 169 | + oneLiner: 'ATR-based trailing stop that flips colour with the trend — one line, long or short.', | |
| 170 | + summary: 'Supertrend places a stop line a multiple of the ATR below price in an uptrend and above price in a downtrend; the line only ratchets in the direction of the trend and flips when a close crosses it. The result is a single, unambiguous trend state with a built-in stop level.', | |
| 171 | + formula: { text: 'basicUpper = HL2 + mult·ATR(n), basicLower = HL2 − mult·ATR(n)\nfinalLower ratchets up while the trend is up (never decreases)\nfinalUpper ratchets down while the trend is down\nflip: close crosses the active line', vars: [['n', 'ATR length (default 10)'], ['mult', 'ATR multiplier (default 3)'], ['HL2', 'bar midpoint (H + L) / 2']] }, | |
| 172 | + howToRead: ['Green line below price = uptrend, stay long; red line above price = downtrend, stay short or flat.', 'The line itself is the trailing stop: a close beyond it flips the state — that is the exit and the reverse signal.', 'Distance between price and the line is the room the trade has before the stop; large distance after a run means a late entry.', 'Pullbacks that approach but do not close through the line are continuation entries in the direction of the trend.'], | |
| 173 | + defaults: [['length', 10, 'Two weeks of daily bars for the ATR — Olivier Seban\'s original setting, quick enough for swing trades.'], ['mult', 3, 'Three ATRs keep the stop outside normal noise; 2 is tighter for intraday, 4 for position trades.']], | |
| 174 | + bestFor: { timeframes: 'Daily and 4-hour for swing trading; 15-minute and above intraday.', markets: 'Trending futures, crypto and stocks.', note: 'It is a stop-and-reverse system: in ranges it will whipsaw by design.' }, | |
| 175 | + pairsWith: ['adx', 'ema', 'atr'], | |
| 176 | + pitfalls: ['Sideways markets produce a string of small losing flips — filter with ADX or a higher-timeframe trend.', 'The flip happens on the close that crosses the line; intrabar pierces do not count and the entry is at the next open.', 'A large multiplier gives back a lot of profit before flipping.'], | |
| 177 | + history: 'Olivier Seban, 2009 (France), building on Wilder\'s ATR and volatility-stop ideas.', | |
| 178 | + related: [{ label: 'Average True Range', href: IND('atr') }, { label: 'Parabolic SAR', href: IND('psar') }, { label: 'Keltner Channels', href: IND('keltner') }], | |
| 179 | + }, | |
| 180 | + { | |
| 181 | + id: 'psar', name: 'Parabolic SAR', aka: ['PSAR', 'SAR', 'Stop and Reverse'], category: 'trend', pane: 'main', | |
| 182 | + oneLiner: 'Accelerating trailing dots below or above price; a touch flips the trend.', | |
| 183 | + summary: 'The Parabolic SAR plots a stop level that starts far from price and accelerates toward it as the trend makes new extremes, tracing a parabola. When price touches the dots the position is stopped and reversed, so the indicator is always long or short. Dots below price mark an uptrend (bull), dots above mark a downtrend (bear).', | |
| 184 | + formula: { text: 'SAR_{t+1} = SAR_t + AF·(EP − SAR_t)\nAF starts at `start`, += `increment` at each new extreme, capped at `max`\nSAR may not enter the range of the previous two bars; a touch reverses the trend', vars: [['EP', 'extreme point of the current trend (highest high or lowest low)'], ['AF', 'acceleration factor'], ['start / increment / max', '0.02 / 0.02 / 0.20 by default']] }, | |
| 185 | + howToRead: ['Dots below price: uptrend, hold longs with the last dot as the stop; dots above: downtrend.', 'The dots accelerate toward price the longer the trend lasts — the system takes profits automatically on ageing moves.', 'A flip after a long run is a strong exit; a flip after three or four bars is usually chop.', 'The vertical distance between price and the dot is your current risk in price units.'], | |
| 186 | + defaults: [['start', 0.02, 'Wilder\'s original: the stop begins moving at 2 % of the distance to the extreme per bar.'], ['increment', 0.02, 'Each new extreme adds 2 % of acceleration — the parabola\'s curvature.'], ['max', 0.2, 'The cap of 20 % stops the dots from catching price in a healthy trend; raise it to exit faster.']], | |
| 187 | + bestFor: { timeframes: 'Daily and 4-hour trend following; intraday on 15-minute and above.', markets: 'Trending futures and FX; Wilder designed it for commodities.', note: 'Wilder himself advised using it only when ADX says a trend exists.' }, | |
| 188 | + pairsWith: ['adx', 'supertrend', 'atr'], | |
| 189 | + pitfalls: ['In ranges the SAR flips every few bars and loses money on every flip — it has no built-in trend filter.', 'The starting stop is far from price, so the initial risk after a flip can be large.', 'Gaps through the dots trigger the reversal at a worse price than the dot.'], | |
| 190 | + history: 'J. Welles Wilder Jr., 1978, New Concepts in Technical Trading Systems.', | |
| 191 | + related: [{ label: 'ADX', href: IND('adx') }, { label: 'Supertrend', href: IND('supertrend') }], | |
| 192 | + }, | |
| 193 | + { | |
| 194 | + id: 'ichimoku', name: 'Ichimoku Kinko Hyo', aka: ['Ichimoku Cloud', 'Ichimoku'], category: 'trend', pane: 'main', | |
| 195 | + oneLiner: 'Five lines and a cloud: trend, momentum, support and resistance in one equilibrium chart.', | |
| 196 | + summary: 'Ichimoku plots two midpoint lines (Tenkan and Kijun), a cloud built from two spans projected 26 bars ahead (Senkou A and B) and the close shifted 26 bars back (Chikou). Everything is derived from midpoints of highs and lows rather than averages, and the forward displacement means the chart shows where support and resistance will be, not only where they were.', | |
| 197 | + formula: { text: 'Tenkan = (max H + min L over 9) / 2\nKijun = (max H + min L over 26) / 2\nSenkouA = (Tenkan + Kijun) / 2, plotted 26 bars ahead\nSenkouB = (max H + min L over 52) / 2, plotted 26 bars ahead\nChikou = close, plotted 26 bars back', vars: [['conversion', 'Tenkan-sen length (9)'], ['base', 'Kijun-sen length (26)'], ['spanB', 'Senkou span B length (52)'], ['displacement', 'forward shift of the cloud and backward shift of Chikou (26)']] }, | |
| 198 | + howToRead: ['Price above the cloud = bullish, below = bearish, inside = neutral; the cloud is the zone of equilibrium and support / resistance.', 'Tenkan crossing above Kijun (TK cross) is a buy signal — strong above the cloud, weak below it.', 'Cloud colour (Senkou A above B) shows the future trend bias; a thin cloud is easy to break, a thick one is strong support / resistance.', 'Chikou above the price of 26 bars ago confirms bullish momentum; Chikou tangled in past candles means indecision.', 'Kijun is the equilibrium of the last 26 bars: price far from it tends to snap back ("Kijun gravity").'], | |
| 199 | + defaults: [['conversion', 9, 'A week and a half of the six-day Japanese trading week of the 1930s.'], ['base', 26, 'One trading month of six-day weeks — also the displacement of the cloud.'], ['spanB', 52, 'Two trading months, the slow component of the cloud.'], ['displacement', 26, 'The cloud is projected one month forward and Chikou one month back, matching the Kijun period.']], | |
| 200 | + bestFor: { timeframes: 'Daily and weekly, where it was designed; 4-hour and 1-hour for FX intraday.', markets: 'Indices, FX and stocks with long swings; less useful on choppy small caps.', note: 'Read the whole system at once — a single line in isolation is not Ichimoku.' }, | |
| 201 | + pairsWith: ['rsi', 'adx', 'macd'], | |
| 202 | + pitfalls: ['Five lines clutter fast intraday charts; hide Chikou or the Tenkan if you need clarity.', 'The 9 / 26 / 52 defaults assume the historical six-day week; modern adaptations (7 / 22 / 44) exist but the classic numbers remain the ones the market watches.', 'Signals inside the cloud are unreliable by construction — wait for a clean exit.'], | |
| 203 | + history: 'Goichi Hosoda (pen name Ichimoku Sanjin), developed in the 1930s and published in 1968–1969 after three decades of refinement.', | |
| 204 | + related: [{ label: 'ADX', href: IND('adx') }, { label: 'Donchian Channels', href: IND('donchian') }, GUIDE], | |
| 205 | + thumb: { bars: 170 }, | |
| 206 | + }, | |
| 207 | + { | |
| 208 | + id: 'vortex', name: 'Vortex Indicator', aka: ['VI', 'VI+ / VI−'], category: 'trend', pane: 'new', | |
| 209 | + oneLiner: 'Two lines from bar-to-bar range overlap: VI+ over VI− = uptrend, the cross is the signal.', | |
| 210 | + summary: 'The Vortex Indicator measures upward movement as the distance from the previous low to the current high and downward movement as the distance from the previous high to the current low, sums each over n bars and normalises by the summed true range. The two lines oscillate around 1 and their crossovers identify the start of a new trend.', | |
| 211 | + formula: { text: 'VM+ = |H_t − L_{t−1}|, VM− = |L_t − H_{t−1}|\nVI+ = Σ_n VM+ / Σ_n TR, VI− = Σ_n VM− / Σ_n TR', vars: [['n', 'summation length (default 14)'], ['TR', 'true range of the bar'], ['VI+ / VI−', 'positive and negative vortex lines']] }, | |
| 212 | + howToRead: ['VI+ crossing above VI− signals an uptrend; VI− crossing above VI+ signals a downtrend.', 'The wider the gap between the two lines, the stronger the trend; converging lines warn of exhaustion.', 'Both lines near 1 with frequent crosses = no trend; wait for a decisive separation.', 'Botes and Siepman suggest entering on the cross and placing the stop at the extreme of the crossover bar.'], | |
| 213 | + defaults: [['length', 14, 'The authors\' recommended default for daily bars; they suggest 13–21 depending on the cycle length of the instrument.']], | |
| 214 | + bestFor: { timeframes: 'Daily and weekly; longer settings intraday.', markets: 'Futures and stocks with clear trending phases.' }, | |
| 215 | + pairsWith: ['adx', 'aroon', 'supertrend'], | |
| 216 | + pitfalls: ['Whipsaws in ranges like every crossover system — the authors advise filtering with a trend-strength measure.', 'Gaps inflate one vortex movement dramatically for n bars.'], | |
| 217 | + history: 'Etienne Botes and Douglas Siepman, January 2010, Technical Analysis of Stocks & Commodities.', | |
| 218 | + related: [{ label: 'ADX', href: IND('adx') }, { label: 'Aroon', href: IND('aroon') }], | |
| 219 | + }, | |
| 220 | + { | |
| 221 | + id: 'aroon', name: 'Aroon', aka: ['Aroon Up / Down', 'Aroon indicator'], category: 'trend', pane: 'new', | |
| 222 | + oneLiner: 'How recently the n-bar high and low were made, 0–100: fresh highs = Aroon Up near 100.', | |
| 223 | + summary: 'Aroon Up counts how many bars have passed since the highest high of the window and Aroon Down since the lowest low, both scaled so that a fresh extreme scores 100 and an extreme at the far edge of the window scores 0. It measures the age of the trend rather than its size, which makes it good at spotting trends before they show up in moving averages.', | |
| 224 | + formula: { text: 'AroonUp = 100 · (n − barsSinceHighestHigh(n)) / n\nAroonDown = 100 · (n − barsSinceLowestLow(n)) / n', vars: [['n', 'lookback length (default 14)'], ['barsSince…', 'age in bars of the extreme within the last n + 1 bars']] }, | |
| 225 | + howToRead: ['Aroon Up above 70 with Aroon Down below 30 = established uptrend; the mirror = downtrend.', 'Aroon Up crossing above Aroon Down is an early trend-change signal; the cross above 50 confirms.', 'Both lines below 50 for a while: the market is consolidating, no recent extreme on either side.', 'Parallel movement of both lines near the middle means indecision — a breakout is brewing.'], | |
| 226 | + defaults: [['length', 14, 'Chande\'s original: 14 bars balance sensitivity and stability on daily charts; 25 is common for a smoother read.']], | |
| 227 | + bestFor: { timeframes: 'Daily and weekly; also 1-hour for intraday trend tracking.', markets: 'Stocks and futures where trends alternate with consolidations.' }, | |
| 228 | + pairsWith: ['adx', 'vortex', 'donchian'], | |
| 229 | + pitfalls: ['The lines move in steps of 100/n and jump as an extreme drops out of the window — jerky by nature.', 'It says nothing about the size of the move: a fresh high by one tick scores 100.'], | |
| 230 | + history: 'Tushar Chande, September 1995, Technical Analysis of Stocks & Commodities ("Aroon" is Sanskrit for dawn\'s early light).', | |
| 231 | + related: [{ label: 'ADX', href: IND('adx') }, { label: 'Donchian Channels', href: IND('donchian') }], | |
| 232 | + }, | |
| 233 | + { | |
| 234 | + id: 'adx', name: 'Average Directional Index', aka: ['ADX', 'DMI', 'Directional Movement'], category: 'trend', pane: 'new', | |
| 235 | + oneLiner: 'Trend strength 0–100 regardless of direction, with +DI / −DI showing who leads.', | |
| 236 | + summary: 'ADX smooths the difference between the positive and negative directional indicators (+DI, −DI), which measure how much of the true range was upward or downward movement. The ADX line reads the strength of the trend whatever its direction; the two DI lines tell you whether buyers or sellers own it.', | |
| 237 | + formula: { text: '+DM = H_t − H_{t−1} if > L_{t−1} − L_t and > 0, else 0 (−DM symmetric)\n+DI = 100 · RMA(+DM, n) / RMA(TR, n), −DI likewise\nDX = 100 · |+DI − −DI| / (+DI + −DI)\nADX = RMA(DX, n)', vars: [['n', 'Wilder smoothing length (default 14)'], ['RMA', 'Wilder\'s smoothed moving average'], ['TR', 'true range']] }, | |
| 238 | + howToRead: ['ADX below 20–25: no trend, range tools apply; above 25: a trend worth following; above 40–50: very strong, often late.', '+DI above −DI = uptrend; −DI above +DI = downtrend. A DI cross while ADX rises from below 20 is a fresh trend.', 'A falling ADX from a high level does not mean reversal — it means the trend is losing strength or consolidating.', 'ADX turning up from below 20 after a long flat period frequently precedes a breakout.', 'Wilder\'s extreme-point rule: enter on the DI cross only if price then exceeds the extreme of the crossover bar.'], | |
| 239 | + defaults: [['length', 14, 'Wilder\'s standard: half of a 28-day cycle on daily bars; because ADX is doubly smoothed, 14 already lags — shorter settings are rarely useful.']], | |
| 240 | + bestFor: { timeframes: 'Daily and 4-hour; 1-hour for intraday regime filtering.', markets: 'Every market — it is the standard trend / range filter.', note: 'Use ADX to decide WHICH tools to use (trend or range), not as an entry signal by itself.' }, | |
| 241 | + pairsWith: ['supertrend', 'psar', 'rsi', 'sma'], | |
| 242 | + pitfalls: ['The double smoothing makes ADX slow: a trend can be half over by the time ADX crosses 25.', 'ADX rises in strong downtrends too — always read the DI lines for direction.', 'Absolute thresholds vary by instrument; calibrate 20 / 25 against the history you trade.'], | |
| 243 | + history: 'J. Welles Wilder Jr., 1978, New Concepts in Technical Trading Systems.', | |
| 244 | + related: [{ label: 'Aroon', href: IND('aroon') }, { label: 'Vortex', href: IND('vortex') }, { label: 'Parabolic SAR', href: IND('psar') }], | |
| 245 | + }, | |
| 246 | + { | |
| 247 | + id: 'zigzag', name: 'ZigZag', aka: ['Zig Zag', 'Swing filter'], category: 'levels', pane: 'main', | |
| 248 | + oneLiner: 'Connects swing highs and lows that exceed a deviation, filtering out the smaller noise.', | |
| 249 | + summary: 'The ZigZag joins successive pivot points — a new pivot is confirmed only once price has moved against the current leg by at least the chosen deviation, in percent or in ATR multiples. Everything smaller is ignored, leaving the skeleton of the swings: the raw material for wave counts, pattern recognition and support / resistance mapping.', | |
| 250 | + formula: { text: 'leg continues while price extends its extreme\npivot confirmed when reversal ≥ deviation % of the pivot price (mode percent)\n or ≥ atrMult · ATR(atrLength) (mode atr)\nthe last point is the provisional extreme of the current leg', vars: [['deviation', 'minimum reversal in % (default 5)'], ['mode', 'percent or atr'], ['atrLength / atrMult', 'ATR window and multiple used in atr mode (14 × 3)']] }, | |
| 251 | + howToRead: ['Higher highs and higher lows on the ZigZag = uptrend; lower highs and lower lows = downtrend; the first failed swing is the warning.', 'Each pivot is a natural anchor for Fibonacci retracements, trend lines and pitchforks.', 'Compare the length of successive legs: shrinking impulses and growing corrections signal a maturing trend.', 'The last segment is provisional — it will move until the reversal confirms the pivot.'], | |
| 252 | + defaults: [['deviation', 5, 'A 5 % swing filter on daily stock bars keeps only meaningful swings; use 0.5–1 % intraday, 10 %+ on crypto.'], ['mode', 'percent', 'Percent deviation is scale-free and the traditional choice; atr adapts the filter to current volatility.'], ['atrLength', 14, 'Wilder\'s standard ATR window, used only in atr mode.'], ['atrMult', 3, 'Three ATRs approximate a "significant" swing in most markets, used only in atr mode.']], | |
| 253 | + bestFor: { timeframes: 'Every timeframe — tune the deviation to the timeframe.', markets: 'Any market; essential for Elliott wave and harmonic-pattern work.', note: 'The ZigZag describes the past; only the confirmed pivots are fixed.' }, | |
| 254 | + pairsWith: ['auto-fib', 'pivots', 'atr'], | |
| 255 | + pitfalls: ['The last leg repaints by design: never backtest signals on the provisional pivot as if it were known in real time.', 'Too small a deviation returns to noise; too large removes the swings you trade.', 'Percent mode treats a 5 % move the same in calm and violent regimes — atr mode fixes that.'], | |
| 256 | + history: 'Classic charting technique formalised as an indicator in the 1980s (notably by Arthur Merrill\'s filtered waves, 1977); no single author.', | |
| 257 | + related: [{ label: 'Auto Fibonacci', href: IND('auto-fib') }, { label: 'Elliott impulse wave (drawing tool)', href: TOOL('elliott-impulse') }, { label: 'XABCD pattern (drawing tool)', href: TOOL('xabcd') }], | |
| 258 | + }, | |
| 259 | + { | |
| 260 | + id: 'pivots', name: 'Pivot Points', aka: ['Floor pivots', 'PP', 'Daily pivots'], category: 'levels', pane: 'main', | |
| 261 | + oneLiner: 'Support and resistance levels for the current period computed from the previous H, L, C.', | |
| 262 | + summary: 'Pivot Points derive a central pivot and symmetric support / resistance levels from the previous period\'s high, low and close; they are fixed for the whole period, so every participant sees the same lines. Four formula families are offered — classic floor pivots, Fibonacci-ratio pivots, Camarilla intraday levels and Woodie\'s open-weighted pivots — on a daily, weekly or monthly period.', | |
| 263 | + formula: { text: 'classic: P = (H + L + C) / 3, R1 = 2P − L, S1 = 2P − H, R2 = P + (H − L), S2 = P − (H − L), R3 = H + 2(P − L), S3 = L − 2(H − P)\nfibonacci: P as classic; R/S_k = P ± {0.382, 0.618, 1.000} · (H − L)\ncamarilla: R/S_k = C ± (H − L) · {1.1/12, 1.1/6, 1.1/4, 1.1/2} (R1…R4, S1…S4)\nwoodie: P = (H + L + 2·O_current) / 4, then classic R/S', vars: [['H, L, C', 'high, low and close of the PREVIOUS period'], ['O_current', 'open of the current period (Woodie only)'], ['type', 'classic | fibonacci | camarilla | woodie'], ['period', 'day | week | month — the bars are grouped from their own timestamps']] }, | |
| 264 | + howToRead: ['Price above the central pivot: bullish session bias, look for longs at S1 or on a pivot retest; below: bearish bias.', 'R1 / S1 are the first targets and common fade zones; R2 / S2 and beyond mark trend days.', 'Camarilla R3 / S3 are the classic intraday fade levels and R4 / S4 the breakout levels.', 'Levels that coincide across periods (daily S1 on the weekly pivot) are the strongest.', 'Weekly and monthly pivots on a daily chart frame the swing-trading map.'], | |
| 265 | + defaults: [['type', 'classic', 'The original floor-trader formula, still the reference everyone watches.'], ['period', 'day', 'Daily pivots from the previous session are the intraday standard; switch to week or month on daily charts.']], | |
| 266 | + bestFor: { timeframes: 'Intraday (1- to 30-minute) with daily pivots; daily charts with weekly or monthly pivots.', markets: 'Index futures, FX and liquid stocks — instruments where floor traders and algos share the same levels.', note: 'On daily bars a "day" period produces one level per bar — use week or month.' }, | |
| 267 | + pairsWith: ['vwap', 'volume-profile', 'zigzag'], | |
| 268 | + pitfalls: ['The levels come from the previous period only; a gap open puts price far from them and they lose relevance for the session.', 'Different vendors use different session boundaries (RTH vs 24 h) and get slightly different pivots; ours follow the bars\' calendar day.', 'Fading R3 / S3 in a strong trend day is the classic pivot-trader loss.'], | |
| 269 | + history: 'Floor-trader formula from the Chicago pits (1930s–1980s), popularised in print by Larry Williams and later Mark Fisher; Camarilla by Nick Scott (1989), Woodie\'s variant by Ken Wood.', | |
| 270 | + related: [{ label: 'VWAP', href: IND('vwap') }, { label: 'Horizontal line (drawing tool)', href: TOOL('hline') }, { label: 'Time zones & sessions', href: '/docs/time-zones' }], | |
| 271 | + thumb: { params: { period: 'week' }, bars: 130 }, | |
| 272 | + }, | |
| 273 | + { | |
| 274 | + id: 'auto-fib', name: 'Auto Fibonacci (visible range)', aka: ['Auto Fib', 'Automatic retracement'], category: 'levels', pane: 'main', | |
| 275 | + oneLiner: 'Fibonacci retracement drawn automatically on the largest swing of the visible range.', | |
| 276 | + summary: 'The Auto Fibonacci finds the highest high and the lowest low among the bars on screen, treats the move from the earlier extreme to the later one as the swing, and plots the retracement levels 0, 23.6, 38.2, 50, 61.8, 78.6 and 100 %. It recomputes whenever you pan or zoom, so the levels always describe the swing you are looking at.', | |
| 277 | + formula: { text: 'swing = earlier extreme → later extreme of the visible bars\nlevel(r) = end − (end − start) · r, r ∈ {0, 0.236, 0.382, 0.5, 0.618, 0.786, 1}', vars: [['start / end', 'price of the first and last extreme of the visible range'], ['r', 'Fibonacci ratio; 0 sits at the end of the swing, 1 at its start']] }, | |
| 278 | + howToRead: ['38.2 % and 50 % are shallow pullbacks typical of strong trends; 61.8 % is the classic "golden" retracement; beyond 78.6 % the swing is usually failing.', 'Confluence with a moving average, a pivot or a volume-profile node makes a level much more reliable.', 'Zoom in to isolate one swing: the tool always uses the biggest move on screen, so a wide view measures the big trend, a narrow view the last leg.', 'A retracement that holds and prints a reversal candle at a level is the setup; the level alone is not.'], | |
| 279 | + defaults: [], | |
| 280 | + bestFor: { timeframes: 'Every timeframe.', markets: 'Every market — ratios are scale-free.', note: 'For a fixed anchor that does not move with the view, use the Fibonacci retracement drawing tool.' }, | |
| 281 | + pairsWith: ['zigzag', 'pivots', 'rsi'], | |
| 282 | + pitfalls: ['The levels move with the viewport: an alert-style reading requires the manual Fib tool.', 'The largest swing on screen is not always the swing that matters for your trade.', 'Fibonacci levels are self-fulfilling to a degree, but they are zones, not lines — expect overshoots of a few ticks.'], | |
| 283 | + history: 'Fibonacci retracements were introduced to charting by Ralph Nelson Elliott (1930s–1940s) and popularised by the harmonic and swing-trading literature; the automatic visible-range version is a modern charting convenience.', | |
| 284 | + related: [{ label: 'Fibonacci retracement (drawing tool)', href: TOOL('fib') }, { label: 'ZigZag', href: IND('zigzag') }, { label: 'Fibonacci extension (drawing tool)', href: TOOL('fib-extension') }], | |
| 285 | + thumb: { bars: 110 }, | |
| 286 | + }, | |
| 287 | +] | |
added
hfmarketdata/web/src/charts/catalog/indicators/volatility.js
+134 −0
@@ -0,0 +1,134 @@ | ||
| 1 | +// Volatility family — bands / channels (overlays) and volatility gauges (own pane). Schema: see ../index.js. | |
| 2 | +export default [ | |
| 3 | + { | |
| 4 | + id: 'bollinger', name: 'Bollinger Bands', aka: ['BB', 'Bollinger'], category: 'volatility', pane: 'main', | |
| 5 | + oneLiner: 'Moving average ± k standard deviations; the band width tracks volatility.', | |
| 6 | + summary: 'Bollinger Bands wrap a simple moving average with an upper and a lower band placed k standard deviations away. Because the standard deviation is computed on the same window, the bands widen when price becomes agitated and pinch when it goes quiet, so the envelope adapts to the market instead of imposing a fixed percentage.', | |
| 7 | + formula: { text: 'middle = SMA(src, n)\nupper = middle + k × σ(src, n)\nlower = middle − k × σ(src, n)', vars: [['src', 'price source (default close)'], ['n', 'window length (default 20)'], ['k', 'standard-deviation multiplier (default 2)'], ['σ', 'population standard deviation of src over the last n bars']] }, | |
| 8 | + howToRead: ['A close outside a band is a statistically stretched move: in a range it leans towards mean reversion, in a trend it is a sign of strength ("walking the band").', 'The Squeeze: bands at their narrowest in months precede an expansion — the direction is given by the breakout bar, not by the squeeze itself.', 'W-bottoms and M-tops: a second low that holds inside the lower band while price is at or below the first low is a classic reversal set-up.', 'The middle band is the trend reference: price holding above a rising SMA 20 keeps the bias long; repeated failures at it flip the bias.', 'Read the width relative to its own history (see Bollinger Bandwidth) rather than in absolute price units.'], | |
| 9 | + defaults: [['length', 20, 'About one trading month on daily bars — long enough for a stable standard deviation, short enough to react; Bollinger suggested 10 with k 1.9 and 50 with k 2.1 for other horizons.'], ['mult', 2, '±2σ covers roughly 95 % of a normal distribution, so touches outside the bands stay rare enough to mean something.'], ['source', 'close', 'Closes are the settlement prices; hlc3 or ohlc4 smooth intraday gaps and outliers.']], | |
| 10 | + bestFor: { timeframes: 'Daily and 1-hour bars for swing set-ups; intraday for squeeze breakouts.', markets: 'Any liquid market; especially useful on stocks and indices that alternate ranges and trends.', note: 'Pair with a momentum gauge to tell a band walk from an exhaustion touch.' }, | |
| 11 | + pairsWith: ['rsi', 'bb-width', 'bb-pctb', 'keltner'], | |
| 12 | + pitfalls: ['Selling the upper band in a strong uptrend fights the trend — the bands describe volatility, not overbought.', 'The ±2σ reading assumes normally distributed returns; fat tails make outside closes more frequent than 5 %.', 'The bands lag: after a shock the standard deviation stays inflated for n bars.'], | |
| 13 | + history: 'John Bollinger, early 1980s, formalised in Bollinger on Bollinger Bands (2001).', | |
| 14 | + related: [{ label: 'Bollinger %B', href: '/docs/charts/indicators/bb-pctb' }, { label: 'Bollinger Bandwidth', href: '/docs/charts/indicators/bb-width' }, { label: 'Keltner Channels', href: '/docs/charts/indicators/keltner' }], | |
| 15 | + }, | |
| 16 | + { | |
| 17 | + id: 'keltner', name: 'Keltner Channels', aka: ['KC', 'Keltner'], category: 'volatility', pane: 'main', | |
| 18 | + oneLiner: 'EMA ± k × ATR: a smoother volatility channel than Bollinger, built on true range.', | |
| 19 | + summary: 'Keltner Channels surround an exponential moving average with bands placed a multiple of the Average True Range above and below it. The ATR reacts to gaps and intrabar ranges rather than to close-to-close dispersion, so the channel is smoother than Bollinger Bands and less prone to sudden expansions.', | |
| 20 | + formula: { text: 'middle = EMA(close, n)\nupper = middle + k × ATR(m)\nlower = middle − k × ATR(m)', vars: [['n', 'EMA length (default 20)'], ['k', 'ATR multiplier (default 2)'], ['m', 'ATR length, Wilder smoothing (default 10)'], ['ATR', 'average true range: mean of max(H−L, |H−C₁|, |L−C₁|)']] }, | |
| 21 | + howToRead: ['Closes beyond the upper channel in an uptrend signal strong momentum; a return inside the channel is the first sign of a pause.', 'In a range, channel touches act as dynamic support and resistance around the EMA.', 'Squeeze: when Bollinger Bands move inside the Keltner Channel, volatility is compressed — the exit of the Bollinger Bands is the breakout signal (Carter\'s TTM Squeeze logic).', 'The slope of the middle EMA gives the bias; trade channel pullbacks in the direction of that slope.'], | |
| 22 | + defaults: [['length', 20, 'One trading month of daily bars, matching the Bollinger default so the two channels can be compared directly.'], ['mult', 2, 'Two ATRs contain most normal swings; Chester Keltner used 1.5, Linda Raschke popularised 2 × ATR.'], ['atrLength', 10, 'A 10-bar ATR follows changes in range faster than the 20-bar basis while staying smooth thanks to Wilder\'s smoothing.']], | |
| 23 | + bestFor: { timeframes: 'Daily and 4-hour / 1-hour bars for trend pullbacks; works on 5-minute bars in liquid futures.', markets: 'Futures, FX and indices where gaps and true range matter.', note: 'Preferred over Bollinger when you want stable bands during news spikes.' }, | |
| 24 | + pairsWith: ['bollinger', 'atr', 'ema', 'adx'], | |
| 25 | + pitfalls: ['A single wide-range bar inflates the ATR for m bars and moves the channel abruptly.', 'Fixed multiples ignore the distribution of returns — a 2 × ATR touch is not a probability statement.', 'On thin markets the true range is dominated by spreads and wicks.'], | |
| 26 | + history: 'Chester W. Keltner, 1960, in How to Make Money in Commodities (SMA basis); the EMA + ATR form is Linda Raschke\'s 1980s refinement.', | |
| 27 | + related: [{ label: 'Bollinger Bands', href: '/docs/charts/indicators/bollinger' }, { label: 'ATR', href: '/docs/charts/indicators/atr' }], | |
| 28 | + }, | |
| 29 | + { | |
| 30 | + id: 'donchian', name: 'Donchian Channels', aka: ['DC', 'Donchian', 'Price channel'], category: 'volatility', pane: 'main', | |
| 31 | + oneLiner: 'Highest high and lowest low of the last n bars: the breakout channel of trend followers.', | |
| 32 | + summary: 'Donchian Channels plot the highest high and the lowest low over the last n bars, with a middle line halfway between them. The channel steps rather than curves, and a close through it means price has just made a new n-bar extreme — the raw material of every breakout system.', | |
| 33 | + formula: { text: 'upper = max(high, n)\nlower = min(low, n)\nmiddle = (upper + lower) / 2', vars: [['n', 'lookback length (default 20)'], ['max / min', 'rolling extremes over the last n bars, current bar included']] }, | |
| 34 | + howToRead: ['A close above the upper channel = new n-bar high: trend-following entry (Turtle rule: buy 20-day breakouts, exit on a 10-day low).', 'A flat upper line means no new highs for n bars; a descending upper line means the trend has already turned down.', 'Channel width = n-bar range: contracting width is a consolidation, the widest width of the year marks climactic moves.', 'The middle line is a slow trend filter similar to a moving average but immune to the shape of the price path.'], | |
| 35 | + defaults: [['length', 20, 'The Turtle traders\' classic entry window — about one trading month; 55 for the slower system, 10 for the exit channel.']], | |
| 36 | + bestFor: { timeframes: 'Daily bars first; weekly for position trading, 1-hour for intraday breakouts.', markets: 'Futures, FX and crypto — trending markets where breakouts follow through.', note: 'The channel is a system component; pair it with a volatility-based position size (ATR).' }, | |
| 37 | + pairsWith: ['atr', 'adx', 'supertrend', 'volume-ma'], | |
| 38 | + pitfalls: ['In ranges the channel produces repeated false breakouts; ADX or Choppiness helps to skip them.', 'The entry is late by construction — you buy the highest price of the last n bars.', 'Lookback matters: 20 and 55 give completely different trade sets on the same chart.'], | |
| 39 | + history: 'Richard Donchian, 1960s; basis of Richard Dennis\'s Turtle trading rules (1983).', | |
| 40 | + related: [{ label: 'Supertrend', href: '/docs/charts/indicators/supertrend' }, { label: 'ATR', href: '/docs/charts/indicators/atr' }], | |
| 41 | + }, | |
| 42 | + { | |
| 43 | + id: 'choppiness', name: 'Choppiness Index', aka: ['CHOP'], category: 'volatility', pane: 'new', | |
| 44 | + oneLiner: 'Is the market trending or chopping? 0–100: above 61.8 = chop, below 38.2 = trend.', | |
| 45 | + summary: 'The Choppiness Index compares the sum of the true ranges over n bars with the total range covered during the same window, on a logarithmic scale. When bars overlap and price goes nowhere the ratio is high; when each bar extends the move the ratio is low. It says how directional the recent path was, not which way it went.', | |
| 46 | + formula: { text: 'CHOP = 100 × log10( ΣTR(n) / (maxHigh(n) − minLow(n)) ) / log10(n)', vars: [['n', 'lookback length (default 14)'], ['ΣTR(n)', 'sum of the true ranges of the last n bars'], ['maxHigh − minLow', 'total range covered over the same n bars']] }, | |
| 47 | + howToRead: ['Above 61.8: choppy, sideways market — favour range tactics, fade extremes, avoid breakout entries.', 'Below 38.2: strongly directional — trend-following tools work, counter-trend fades do not.', 'A drop from above 61.8 to below 38.2 flags the start of a trend leg; the reverse move flags exhaustion into consolidation.', 'CHOP has no direction: read the sign of the move from price or a trend indicator.', 'Extremely high readings after a long trend often precede a violent breakout — compression before expansion.'], | |
| 48 | + defaults: [['length', 14, 'Wilder-style two-week window on daily bars; long enough to compare a swing with its bars, short enough to catch regime changes.']], | |
| 49 | + bestFor: { timeframes: 'Daily and 4-hour bars for regime detection; 15-minute bars to filter intraday breakout systems.', markets: 'Futures, FX and indices where trend / range alternation is the main risk.', note: 'A filter, not a signal generator.' }, | |
| 50 | + pairsWith: ['adx', 'donchian', 'bollinger', 'atr'], | |
| 51 | + pitfalls: ['It confirms a trend only once the trend is under way — the reading lags the first thrust.', 'The 38.2 / 61.8 thresholds are Fibonacci conventions, not statistical bounds; calibrate them per market.', 'A single gap bar inflates both numerator and denominator and can distort a short window.'], | |
| 52 | + history: 'E. W. Dreiss, Australian commodity trader, 1990s.', | |
| 53 | + related: [{ label: 'ADX', href: '/docs/charts/indicators/adx' }, { label: 'Donchian Channels', href: '/docs/charts/indicators/donchian' }], | |
| 54 | + }, | |
| 55 | + { | |
| 56 | + id: 'mass', name: 'Mass Index', aka: ['Mass', 'MI'], category: 'volatility', pane: 'new', | |
| 57 | + oneLiner: 'Sum of range-EMA ratios; a bulge above 27 then back under 26.5 flags a reversal.', | |
| 58 | + summary: 'The Mass Index sums, over 25 bars, the ratio between a 9-period EMA of the high–low range and a double-smoothed EMA of the same range. It rises when ranges expand faster than their smoothed baseline and falls when they contract. Donald Dorsey used it to spot the "reversal bulge": a widening of ranges that tends to end the prevailing trend.', | |
| 59 | + formula: { text: 'ratio = EMA(H − L, e) / EMA(EMA(H − L, e), e)\nMass = Σ ratio over the last s bars', vars: [['H − L', 'bar range'], ['e', 'EMA length (default 9)'], ['s', 'summation window (default 25)']] }, | |
| 60 | + howToRead: ['Reversal bulge: Mass rises above 27 then falls back below 26.5 — a trend reversal is likely; the direction comes from a 9-day EMA of price (falling EMA → buy, rising EMA → sell).', 'Values around 25 are the neutral zone; the index does not tell direction on its own.', 'A bulge that fails to cross 27 (peaks near 26.5) is a weaker warning — treat it as volatility expansion, not a reversal.', 'Use it to time exits of an existing position rather than fresh entries.'], | |
| 61 | + defaults: [['ema', 9, 'Dorsey\'s original nine-bar EMA — fast enough to register a change of range within two weeks.'], ['sum', 25, 'Summing 25 ratios (each ≈ 1 in quiet markets) centres the index near 25 and puts 27 / 26.5 at meaningful distances.']], | |
| 62 | + bestFor: { timeframes: 'Daily bars, as designed; weekly for long-term positions.', markets: 'Stocks and indices with well-formed swings.', note: 'The 26.5 / 27 thresholds were calibrated on daily data — recalibrate on other timeframes.' }, | |
| 63 | + pairsWith: ['ema', 'atr', 'rsi'], | |
| 64 | + pitfalls: ['The index needs roughly 9 + 9 + 25 bars before its value is meaningful — expect a long warm-up.', 'Bulges are rare; forcing trades on near-misses destroys the edge.', 'It is direction-blind: without the EMA rule, a bulge is just "something will move".'], | |
| 65 | + history: 'Donald Dorsey, 1992, Technical Analysis of Stocks & Commodities.', | |
| 66 | + related: [{ label: 'ATR', href: '/docs/charts/indicators/atr' }, { label: 'EMA', href: '/docs/charts/indicators/ema' }], | |
| 67 | + thumb: { bars: 200 }, | |
| 68 | + }, | |
| 69 | + { | |
| 70 | + id: 'atr', name: 'Average True Range', aka: ['ATR'], category: 'volatility', pane: 'new', | |
| 71 | + oneLiner: 'Typical bar-to-bar movement in price units — the standard yardstick for stops and size.', | |
| 72 | + summary: 'The ATR averages the true range — the bar\'s range extended to the previous close so gaps count — over n bars with Wilder\'s smoothing. It measures how much the instrument moves per bar in price units, which makes it the natural unit for stop distances, targets and position sizing across very different markets.', | |
| 73 | + formula: { text: 'TR = max(H − L, |H − C₁|, |L − C₁|)\nATR = Wilder RMA(TR, n) = (ATR₁ × (n − 1) + TR) / n', vars: [['H, L', 'high and low of the bar'], ['C₁', 'previous close'], ['n', 'smoothing length (default 14)'], ['ATR₁', 'previous ATR value']] }, | |
| 74 | + howToRead: ['Rising ATR = expanding ranges (often at trend starts and at climaxes); falling ATR = compression that usually precedes a breakout.', 'Stops: place them 1.5–3 ATR away from entry so normal noise does not stop you out; trail them by the same multiple (chandelier exit).', 'Position size: risk per trade ÷ (k × ATR) equalises the dollar risk between a quiet stock and a volatile one.', 'ATR has no direction — a high reading means big bars, up or down.', 'Compare ATR with the bar you are looking at: a bar three times the ATR is an outlier worth a second look.'], | |
| 75 | + defaults: [['length', 14, 'Wilder\'s original length; the RMA weighting makes 14 behave like a ~27-bar EMA, smooth enough for stop placement.']], | |
| 76 | + bestFor: { timeframes: 'Every timeframe — the value is always in the units of the chart you are on.', markets: 'All, especially futures and FX where volatility differs enormously between instruments.', note: 'Divide by price to compare volatility between symbols (ATR %).' }, | |
| 77 | + pairsWith: ['keltner', 'supertrend', 'donchian', 'hv'], | |
| 78 | + pitfalls: ['Wilder smoothing carries the memory of a shock for a long time — a gap keeps stops wide for weeks.', 'On daily stock bars, the overnight gap dominates TR; on 1-minute bars TR is often just the spread.', 'ATR in price units is not comparable between a $10 and a $1 000 stock without normalising.'], | |
| 79 | + history: 'J. Welles Wilder Jr., 1978, New Concepts in Technical Trading Systems.', | |
| 80 | + related: [{ label: 'Keltner Channels', href: '/docs/charts/indicators/keltner' }, { label: 'Historical Volatility', href: '/docs/charts/indicators/hv' }, { label: 'Long position tool', href: '/docs/charts/tools/long' }], | |
| 81 | + }, | |
| 82 | + { | |
| 83 | + id: 'bb-pctb', name: 'Bollinger %B', aka: ['%B', 'Percent B'], category: 'volatility', pane: 'new', | |
| 84 | + oneLiner: 'Where the close sits inside the Bollinger Bands: 1 = upper band, 0 = lower band.', | |
| 85 | + summary: '%B expresses the price position relative to the Bollinger Bands as a single number: 0 at the lower band, 0.5 at the middle, 1 at the upper band, beyond those values outside the bands. It turns the visual "touch" of a band into a plottable oscillator that can be compared across time, screened or used for divergences.', | |
| 86 | + formula: { text: '%B = (src − lower) / (upper − lower)\nwith upper / lower = SMA(src, n) ± k × σ(src, n)', vars: [['src', 'price source (default close)'], ['n', 'Bollinger length (default 20)'], ['k', 'standard-deviation multiplier (default 2)']] }, | |
| 87 | + howToRead: ['Above 1: close above the upper band; below 0: close below the lower band — the same statistical stretch as the bands, quantified.', 'Bearish divergence: price makes a higher high while %B makes a lower high (the second high is less extreme relative to the bands).', 'Trend reading: %B holding above 0.5 for many bars is a band walk; oscillation around 0.5 is a range.', 'Bollinger\'s W-bottom test: a second price low with a higher %B than the first.', 'Screen with %B < 0 plus a rising volume indicator for exhaustion set-ups.'], | |
| 88 | + defaults: [['length', 20, 'Same window as the standard Bollinger Bands so the oscillator matches what the overlay shows.'], ['mult', 2, '±2σ bands: 0 and 1 then correspond to the classic 95 % envelope.'], ['source', 'close', 'Closes match the bands\' own source; keep them identical to the overlay you are reading.']], | |
| 89 | + bestFor: { timeframes: 'Daily and 1-hour bars; any timeframe where you already use Bollinger Bands.', markets: 'Stocks, ETFs, indices, crypto.', note: 'Add the Bollinger Bands overlay with identical settings for context.' }, | |
| 90 | + pairsWith: ['bollinger', 'bb-width', 'rsi', 'mfi'], | |
| 91 | + pitfalls: ['A reading above 1 in a strong trend is strength, not a sell signal.', '%B says nothing about the width of the bands — combine with Bandwidth.', 'Different settings from the overlay produce readings that do not match the picture.'], | |
| 92 | + history: 'John Bollinger, 2001, Bollinger on Bollinger Bands.', | |
| 93 | + related: [{ label: 'Bollinger Bands', href: '/docs/charts/indicators/bollinger' }, { label: 'Bollinger Bandwidth', href: '/docs/charts/indicators/bb-width' }], | |
| 94 | + }, | |
| 95 | + { | |
| 96 | + id: 'bb-width', name: 'Bollinger Bandwidth', aka: ['BBW', 'Bandwidth'], category: 'volatility', pane: 'new', | |
| 97 | + oneLiner: 'Width of the Bollinger Bands as % of the middle band; lows mark the Squeeze.', | |
| 98 | + summary: 'Bandwidth measures the distance between the upper and the lower Bollinger Band as a percentage of the middle band. It isolates the volatility component of the bands so you can compare today\'s compression with the last six months and spot the Squeeze — the low-volatility set-up that precedes many strong moves.', | |
| 99 | + formula: { text: 'BBW = (upper − lower) / middle × 100\n = 2 × k × σ(src, n) / SMA(src, n) × 100', vars: [['n', 'Bollinger length (default 20)'], ['k', 'standard-deviation multiplier (default 2)'], ['σ', 'population standard deviation of src']] }, | |
| 100 | + howToRead: ['The Squeeze: Bandwidth at its lowest level of the last ~125 bars — expect an expansion; the breakout direction comes from price, often with a head-fake first.', 'The Bulge: Bandwidth at a six-month high marks a volatility climax; trends often end or pause there.', 'Rising Bandwidth after a breakout confirms participation; a flat Bandwidth during a breakout is suspicious.', 'Readings are in percent of price, so they can be compared between symbols and across history.'], | |
| 101 | + defaults: [['length', 20, 'Same window as the standard Bollinger Bands.'], ['mult', 2, 'Same multiplier as the overlay so the percentage matches the visible bands.'], ['source', 'close', 'Closes match the bands; keep the source identical to the overlay.']], | |
| 102 | + bestFor: { timeframes: 'Daily bars for Squeeze screening; 1-hour for intraday compressions.', markets: 'Stocks and ETFs above all; also crypto and index futures.', note: 'Judge lows against the symbol\'s own history rather than a fixed threshold.' }, | |
| 103 | + pairsWith: ['bollinger', 'bb-pctb', 'keltner', 'volume-ma'], | |
| 104 | + pitfalls: ['A Squeeze has no direction — trading the first move out is where head-fakes bite.', 'Very low Bandwidth can persist for weeks in dull markets.', 'The percentage form breaks down for prices near zero or negative spreads.'], | |
| 105 | + history: 'John Bollinger, 2001, Bollinger on Bollinger Bands.', | |
| 106 | + related: [{ label: 'Bollinger Bands', href: '/docs/charts/indicators/bollinger' }, { label: 'Bollinger %B', href: '/docs/charts/indicators/bb-pctb' }], | |
| 107 | + }, | |
| 108 | + { | |
| 109 | + id: 'hv', name: 'Historical Volatility', aka: ['HV', 'Realised volatility', 'Statistical volatility'], category: 'volatility', pane: 'new', | |
| 110 | + oneLiner: 'Annualised standard deviation of log returns, in % — the volatility options are priced on.', | |
| 111 | + summary: 'Historical volatility is the standard deviation of the logarithmic close-to-close returns over n bars, scaled to a yearly figure by the square root of the number of periods per year and expressed in percent. It is the realised counterpart of the implied volatility quoted by options, and the volatility unit used in risk models.', | |
| 112 | + formula: { text: 'r = ln(C / C₁)\nHV = σ(r, n) × √annual × 100', vars: [['C, C₁', 'current and previous close'], ['n', 'window length (default 10)'], ['annual', 'periods per year (default 252 trading days)'], ['σ', 'population standard deviation']] }, | |
| 113 | + howToRead: ['Compare with implied volatility: IV well above HV means options are expensive relative to what the stock actually does; IV below HV means they are cheap.', 'Volatility clusters and mean-reverts: extremely low HV rarely lasts; spikes decay over weeks.', 'Rising HV during a decline is the usual "fear" signature; rising HV during a rally is rarer and often unstable.', 'A 20 % annualised HV means roughly a 1.26 % daily one-standard-deviation move (20 / √252).'], | |
| 114 | + defaults: [['length', 10, 'Two trading weeks — a fast realised-vol estimate; 20 or 30 is common for comparison with 30-day implied volatility.'], ['annual', 252, 'US equity trading days per year; use 365 for crypto, 24 × 252 for 1-hour equity bars, 390 × 252 for 1-minute bars.']], | |
| 115 | + bestFor: { timeframes: 'Daily bars, where the 252 convention applies directly.', markets: 'Stocks, ETFs and indices with listed options; crypto with annual set to 365.', note: 'On intraday bars adjust the periods-per-year input or the annualised number is meaningless.' }, | |
| 116 | + pairsWith: ['atr', 'bb-width', 'stddev'], | |
| 117 | + pitfalls: ['With a 10-bar window one large return dominates the estimate.', 'Close-to-close ignores intrabar ranges; ATR or Parkinson estimators capture them.', 'Wrong periods-per-year on intraday charts inflates or deflates the figure by orders of magnitude.'], | |
| 118 | + history: 'Standard statistical measure; adopted as the realised-volatility benchmark with the Black–Scholes model (1973).', | |
| 119 | + related: [{ label: 'ATR', href: '/docs/charts/indicators/atr' }, { label: 'Standard Deviation', href: '/docs/charts/indicators/stddev' }, { label: 'Options chains & Greeks', href: '/docs/options' }], | |
| 120 | + }, | |
| 121 | + { | |
| 122 | + id: 'stddev', name: 'Standard Deviation', aka: ['StdDev', 'σ'], category: 'volatility', pane: 'new', | |
| 123 | + oneLiner: 'Rolling standard deviation of price over n bars — raw dispersion in price units.', | |
| 124 | + summary: 'The rolling standard deviation measures how far the source price scatters around its n-bar mean, in price units. It is the volatility term inside the Bollinger Bands shown on its own, and it rises with agitation regardless of direction.', | |
| 125 | + formula: { text: 'σ = √( Σ (srcᵢ − mean(src, n))² / n )', vars: [['src', 'price source (default close)'], ['n', 'window length (default 20)'], ['mean', 'simple moving average over the same window']] }, | |
| 126 | + howToRead: ['High and rising: prices are dispersing — trend acceleration or a blow-off; low and falling: consolidation.', 'Extreme lows relative to the last months precede expansions (the same logic as the Bollinger Squeeze).', 'Compare σ with the ATR: σ rises on trending closes even when bar ranges are steady, ATR rises on wide bars even in a range.', 'Direction-blind: use with price or a trend gauge.'], | |
| 127 | + defaults: [['length', 20, 'One trading month — the same window as the standard Bollinger Bands, so this pane shows exactly their half-width divided by the multiplier.'], ['source', 'close', 'Closes are the standard input; hlc3 damps the effect of single gaps.']], | |
| 128 | + bestFor: { timeframes: 'Any; the value is in the price units of the chart.', markets: 'All.', note: 'Divide by price (or use Historical Volatility) to compare between symbols.' }, | |
| 129 | + pairsWith: ['bollinger', 'hv', 'atr'], | |
| 130 | + pitfalls: ['Population formula (divides by n): slightly lower than the sample estimate used in statistics packages.', 'A trending price inflates σ even when bars are small — it measures dispersion around a mean, not choppiness.', 'Not comparable across symbols or timeframes without normalisation.'], | |
| 131 | + history: 'Classical statistic (Karl Pearson coined the term in 1893); used in charting since the Bollinger Bands of the 1980s.', | |
| 132 | + related: [{ label: 'Bollinger Bands', href: '/docs/charts/indicators/bollinger' }, { label: 'Historical Volatility', href: '/docs/charts/indicators/hv' }], | |
| 133 | + }, | |
| 134 | +] | |
added
hfmarketdata/web/src/charts/catalog/indicators/volume.js
+157 −0
@@ -0,0 +1,157 @@ | ||
| 1 | +// Volume family — participation and money-flow gauges. Schema: see ../index.js. | |
| 2 | +export default [ | |
| 3 | + { | |
| 4 | + id: 'volume-profile', name: 'Volume Profile (visible range)', aka: ['VP', 'VPVR', 'Market profile'], category: 'volume', pane: 'main', | |
| 5 | + oneLiner: 'Volume traded at each price level of the visible bars, with POC and value area.', | |
| 6 | + summary: 'The Volume Profile redistributes the volume of every visible bar across the price levels the bar covered and stacks the result as a horizontal histogram on the right of the chart. The longest row is the Point of Control (POC); the contiguous rows around it that hold 70 % of the volume form the value area, bounded by VAH and VAL. It shows where trading actually happened, not when.', | |
| 7 | + formula: { text: 'for each visible bar: volume spread uniformly over [low, high] into `rows` price bins\nPOC = bin with the largest volume\nVA = smallest set of contiguous bins around POC holding valueArea % of the total → VAH / VAL', vars: [['rows', 'number of price bins across the visible high–low range (default 24)'], ['valueArea', 'share of the total volume inside the value area, in % (default 70)'], ['POC', 'Point of Control, the most-traded price'], ['VAH / VAL', 'value-area high and low']] }, | |
| 8 | + howToRead: ['The POC and the value area act as magnets and as support / resistance: price tends to return to heavy volume and to move fast through thin volume.', 'High-volume nodes (wide rows) are agreement zones where consolidations form; low-volume nodes are rejection zones that price crosses quickly.', 'Acceptance: price building volume above the old VAH means the market accepts higher prices; a quick return inside the value area is a failed auction.', 'The profile is recomputed as you pan and zoom — frame the swing you want to analyse before reading it.', 'A "P" shape (volume at the top) is short covering into a high; a "b" shape (volume at the bottom) is long liquidation into a low.'], | |
| 9 | + defaults: [['rows', 24, 'Two dozen bins keep the shape readable at any zoom level; raise to 50–100 on a large screen or a wide price range.'], ['valueArea', 70, 'One standard deviation of a normal distribution covers ~68 % — the Market Profile convention rounds it to 70 %.']], | |
| 10 | + bestFor: { timeframes: 'Intraday (5-minute to 1-hour bars) for session profiles; daily bars for multi-month composites.', markets: 'Futures and liquid stocks with reliable volume; not meaningful on FX pairs whose volume is a tick count.', note: 'The profile covers exactly the bars on screen — set the visible range deliberately.' }, | |
| 11 | + pairsWith: ['vwap', 'pivots', 'auto-fib', 'volume-ma'], | |
| 12 | + pitfalls: ['Spreading each bar\'s volume uniformly over its range is an approximation — 1-minute bars give a far more faithful profile than daily bars.', 'Zero-volume minutes are not in the dataset, so intraday profiles only see traded prices.', 'Changing the zoom changes the profile: two screenshots of "the same" profile rarely match.'], | |
| 13 | + history: 'Derived from J. Peter Steidlmayer\'s Market Profile (CBOT, 1984); the visible-range volume histogram is a 2000s charting-software adaptation.', | |
| 14 | + related: [{ label: 'VWAP', href: '/docs/charts/indicators/vwap' }, { label: 'Pivot Points', href: '/docs/charts/indicators/pivots' }, { label: 'Charting guide', href: '/docs/charts' }], | |
| 15 | + thumb: { volume: true }, | |
| 16 | + }, | |
| 17 | + { | |
| 18 | + id: 'obv', name: 'On-Balance Volume', aka: ['OBV'], category: 'volume', pane: 'new', | |
| 19 | + oneLiner: 'Running total of volume signed by the close direction; should confirm price trends.', | |
| 20 | + summary: 'On-Balance Volume adds the bar\'s volume when the close is higher than the previous close, subtracts it when lower, and leaves it unchanged on an equal close. The cumulative line rises when up-days carry more volume than down-days, revealing accumulation or distribution that price alone can hide.', | |
| 21 | + formula: { text: 'OBV = OBV₁ + V if C > C₁\n = OBV₁ − V if C < C₁\n = OBV₁ if C = C₁', vars: [['V', 'volume of the bar'], ['C, C₁', 'current and previous close'], ['OBV₁', 'previous OBV value']] }, | |
| 22 | + howToRead: ['Confirmation: a rising price with a rising OBV is a healthy trend; a rising price with a flat or falling OBV is a trend running on thin participation.', 'Bullish divergence: price makes a lower low while OBV makes a higher low — buyers are absorbing supply before price turns.', 'OBV breakouts often lead price breakouts by a few bars (Granville\'s "volume precedes price").', 'Only the slope and the shape matter — the absolute level depends on where the series starts.', 'Draw trend lines and support / resistance on OBV exactly as on price.'], | |
| 23 | + defaults: [], | |
| 24 | + bestFor: { timeframes: 'Daily and weekly bars, where volume is cleanest; usable on 1-hour bars for liquid stocks.', markets: 'Stocks, ETFs and futures with real volume; not meaningful on FX tick counts.', note: 'Compare OBV with a moving average of itself to define its trend.' }, | |
| 25 | + pairsWith: ['ad', 'volume-ma', 'macd', 'ema'], | |
| 26 | + pitfalls: ['One extreme-volume day (index rebalancing, options expiry) moves the line forever — OBV never forgets.', 'A tiny up-close counts the whole day\'s volume as buying; the close direction is a crude sign.', 'The level restarts with the loaded history: only relative moves are comparable between charts.'], | |
| 27 | + history: 'Joseph Granville, 1963, Granville\'s New Key to Stock Market Profits.', | |
| 28 | + related: [{ label: 'Accumulation / Distribution', href: '/docs/charts/indicators/ad' }, { label: 'Chaikin Money Flow', href: '/docs/charts/indicators/cmf' }], | |
| 29 | + thumb: { volume: true }, | |
| 30 | + }, | |
| 31 | + { | |
| 32 | + id: 'ad', name: 'Accumulation / Distribution', aka: ['A/D', 'ADL', 'Accumulation / Distribution line'], category: 'volume', pane: 'new', | |
| 33 | + oneLiner: 'Cumulative money-flow volume, each bar weighted by where the close sits in its range.', | |
| 34 | + summary: 'The Accumulation / Distribution line cumulates the money-flow volume: each bar\'s volume multiplied by a factor between −1 and +1 that measures where the close landed inside the bar\'s high–low range. A close at the high counts the whole volume as accumulation, a close at the low as distribution, a mid-range close as neutral.', | |
| 35 | + formula: { text: 'CLV = ((C − L) − (H − C)) / (H − L) (0 when H = L)\nMFV = CLV × V\nA/D = A/D₁ + MFV', vars: [['H, L, C', 'high, low, close of the bar'], ['V', 'volume'], ['CLV', 'close location value, −1…+1'], ['A/D₁', 'previous value of the line']] }, | |
| 36 | + howToRead: ['A rising A/D line confirms an uptrend; a falling line under a rising price is distribution — the classic warning before a top.', 'Bullish divergence: price sets a lower low while A/D holds a higher low.', 'Unlike OBV, a strong reversal bar (close near the high after a gap down) adds to the line — it rewards intrabar strength, not just the close direction.', 'Read it with trend lines and breakouts like a price series; only the slope matters, not the level.'], | |
| 37 | + defaults: [], | |
| 38 | + bestFor: { timeframes: 'Daily and weekly bars; 1-hour bars on liquid stocks.', markets: 'Stocks, ETFs and futures with genuine volume.', note: 'Chaikin later smoothed this line into the Chaikin Oscillator and Chaikin Money Flow.' }, | |
| 39 | + pairsWith: ['obv', 'cmf', 'chaikin-osc', 'mfi'], | |
| 40 | + pitfalls: ['It ignores gaps: a stock that gaps down 5 % and closes mid-range adds nothing negative to the line.', 'A bar with H = L contributes zero regardless of its volume.', 'Extreme-volume events (rebalancing, earnings) permanently shift the line.'], | |
| 41 | + history: 'Marc Chaikin, early 1980s, building on Larry Williams\'s accumulation / distribution concept.', | |
| 42 | + related: [{ label: 'Chaikin Money Flow', href: '/docs/charts/indicators/cmf' }, { label: 'Chaikin Oscillator', href: '/docs/charts/indicators/chaikin-osc' }, { label: 'On-Balance Volume', href: '/docs/charts/indicators/obv' }], | |
| 43 | + thumb: { volume: true }, | |
| 44 | + }, | |
| 45 | + { | |
| 46 | + id: 'mfi', name: 'Money Flow Index', aka: ['MFI', 'Volume-weighted RSI'], category: 'volume', pane: 'new', | |
| 47 | + oneLiner: 'Volume-weighted RSI 0–100: above 80 overbought, below 20 oversold, divergences matter.', | |
| 48 | + summary: 'The Money Flow Index applies the RSI logic to money flow — typical price times volume — instead of to price changes alone. Bars with a rising typical price add positive flow, bars with a falling one add negative flow, and the ratio over n bars is mapped to 0–100. Volume gives extra weight to the bars where real money moved.', | |
| 49 | + formula: { text: 'TP = (H + L + C) / 3\nflow = TP × V (positive if TP > TP₁, negative if TP < TP₁)\nMFR = Σ positive flow(n) / Σ negative flow(n)\nMFI = 100 − 100 / (1 + MFR)', vars: [['TP, TP₁', 'typical price of the current and previous bar'], ['V', 'volume'], ['n', 'lookback length (default 14)'], ['MFR', 'money flow ratio']] }, | |
| 50 | + howToRead: ['Above 80: overbought; below 20: oversold — narrower zones than RSI\'s 70 / 30 because volume makes the index more volatile.', 'Divergences are its strength: price at a new high with MFI at a lower high means the new high came on lighter money flow.', 'Failure swings (an oscillator high below 80 followed by a break of its recent low) are stronger than the levels alone.', 'Sustained readings above 50 mark accumulation regimes; below 50, distribution.'], | |
| 51 | + defaults: [['length', 14, 'Mirrors Wilder\'s RSI window so the two oscillators can be read side by side; 10 for faster intraday reads.']], | |
| 52 | + bestFor: { timeframes: 'Daily and 1-hour bars.', markets: 'Stocks, ETFs, futures and crypto with real volume.', note: 'Read it next to the RSI: when they disagree, volume is telling you something.' }, | |
| 53 | + pairsWith: ['rsi', 'cmf', 'bollinger', 'obv'], | |
| 54 | + pitfalls: ['In strong trends MFI can stay above 80 for a long time — extreme readings are not reversals.', 'Volume spikes (news, expiries) dominate a 14-bar window and produce false extremes.', 'Uses the typical price direction, so a bar can close down yet add positive flow if its range shifted up.'], | |
| 55 | + history: 'Gene Quong and Avrum Soudack, 1989, Technical Analysis of Stocks & Commodities.', | |
| 56 | + related: [{ label: 'RSI', href: '/docs/charts/indicators/rsi' }, { label: 'Chaikin Money Flow', href: '/docs/charts/indicators/cmf' }], | |
| 57 | + thumb: { volume: true }, | |
| 58 | + }, | |
| 59 | + { | |
| 60 | + id: 'cmf', name: 'Chaikin Money Flow', aka: ['CMF'], category: 'volume', pane: 'new', | |
| 61 | + oneLiner: 'Money-flow volume over total volume for n bars, −1…+1: buying or selling pressure.', | |
| 62 | + summary: 'Chaikin Money Flow sums the money-flow volume (volume weighted by where the close sits in the bar\'s range) over n bars and divides it by the total volume of the same window. The result oscillates between −1 and +1: positive when closes cluster in the upper part of the bars on heavy volume, negative when they cluster near the lows.', | |
| 63 | + formula: { text: 'CLV = ((C − L) − (H − C)) / (H − L)\nCMF = Σ (CLV × V)(n) / Σ V(n)', vars: [['H, L, C', 'high, low, close'], ['V', 'volume'], ['n', 'window length (default 20)'], ['CLV', 'close location value, −1…+1']] }, | |
| 64 | + howToRead: ['Above 0: net buying pressure over the window; below 0: net selling pressure. The zero line is the regime switch.', 'Readings above +0.25 or below −0.25 are strong; they are more reliable when they persist for several bars than when they spike.', 'Divergence: price at a new high with CMF failing to exceed zero warns that the rally lacks sponsorship.', 'Confirm breakouts: a breakout with CMF turning positive has volume behind it; one with CMF still negative is suspect.'], | |
| 65 | + defaults: [['length', 20, 'Chaikin\'s standard is 20–21 bars, about one trading month; shorter windows react faster but flip around zero constantly.']], | |
| 66 | + bestFor: { timeframes: 'Daily bars primarily; 1-hour bars on liquid names.', markets: 'Stocks, ETFs and futures with genuine volume.', note: 'It is the windowed version of the Accumulation / Distribution line.' }, | |
| 67 | + pairsWith: ['ad', 'mfi', 'obv', 'macd'], | |
| 68 | + pitfalls: ['Gaps are ignored: a large gap that closes mid-range reads as neutral.', 'Rolling sums have a drop-off effect — a big bar leaving the window moves CMF with no new information.', 'Thin volume makes the ratio noisy and prone to extreme values.'], | |
| 69 | + history: 'Marc Chaikin, 1980s.', | |
| 70 | + related: [{ label: 'Accumulation / Distribution', href: '/docs/charts/indicators/ad' }, { label: 'Chaikin Oscillator', href: '/docs/charts/indicators/chaikin-osc' }, { label: 'Money Flow Index', href: '/docs/charts/indicators/mfi' }], | |
| 71 | + thumb: { volume: true }, | |
| 72 | + }, | |
| 73 | + { | |
| 74 | + id: 'chaikin-osc', name: 'Chaikin Oscillator', aka: ['Chaikin Osc', 'CHO'], category: 'volume', pane: 'new', | |
| 75 | + oneLiner: 'MACD of the Accumulation / Distribution line: fast EMA minus slow EMA of money flow.', | |
| 76 | + summary: 'The Chaikin Oscillator applies the MACD idea to the Accumulation / Distribution line: a 3-period EMA minus a 10-period EMA of the cumulative money flow. It measures the momentum of accumulation or distribution, turning the slow-moving A/D line into a fast oscillator around zero.', | |
| 77 | + formula: { text: 'A/D = Σ ((C − L) − (H − C)) / (H − L) × V\nCHO = EMA(A/D, fast) − EMA(A/D, slow)', vars: [['A/D', 'Accumulation / Distribution line'], ['fast', 'short EMA length (default 3)'], ['slow', 'long EMA length (default 10)']] }, | |
| 78 | + howToRead: ['Crosses above zero: money flow is accelerating into the stock; below zero: out of it.', 'Divergence is the primary signal: price at a new high while the oscillator makes a lower high means the buying pressure that drove the first high is fading.', 'Use in the direction of the trend: in an uptrend, buy the oscillator\'s dips below zero that turn back up.', 'Values are in volume units — compare shape and sign, not level, between symbols.'], | |
| 79 | + defaults: [['fast', 3, 'Chaikin\'s original three-bar EMA — very responsive to the last few sessions of money flow.'], ['slow', 10, 'A two-week EMA as the baseline; the 3 / 10 pair mirrors the fast trading version of the MACD.']], | |
| 80 | + bestFor: { timeframes: 'Daily bars, as designed; 1-hour bars on very liquid stocks and futures.', markets: 'Stocks, ETFs, index futures.', note: 'Noisier than Chaikin Money Flow; use it for timing, CMF for regime.' }, | |
| 81 | + pairsWith: ['ad', 'cmf', 'macd', 'rsi'], | |
| 82 | + pitfalls: ['With a 3-bar EMA the line whipsaws around zero in quiet markets.', 'Inherits the A/D line\'s blindness to gaps.', 'Volume units make the amplitude meaningless across symbols and after splits.'], | |
| 83 | + history: 'Marc Chaikin, 1980s.', | |
| 84 | + related: [{ label: 'Accumulation / Distribution', href: '/docs/charts/indicators/ad' }, { label: 'MACD', href: '/docs/charts/indicators/macd' }], | |
| 85 | + thumb: { volume: true }, | |
| 86 | + }, | |
| 87 | + { | |
| 88 | + id: 'force', name: 'Force Index', aka: ['Force', 'FI', 'Elder Force Index'], category: 'volume', pane: 'new', | |
| 89 | + oneLiner: 'Price change × volume, EMA-smoothed: the force behind each move, bulls above zero.', | |
| 90 | + summary: 'The Force Index multiplies the close-to-close change by the bar\'s volume, so a move counts more when it is large and when many shares changed hands, then smooths the product with an exponential moving average. Alexander Elder designed it to gauge the power of bulls (positive) and bears (negative) behind every rally and decline.', | |
| 91 | + formula: { text: 'raw = (C − C₁) × V\nForce = EMA(raw, n)', vars: [['C, C₁', 'current and previous close'], ['V', 'volume'], ['n', 'EMA length (default 13)']] }, | |
| 92 | + howToRead: ['Above zero: bulls in control; below zero: bears. The zero cross of the 13-bar version marks intermediate trend changes.', 'Elder\'s pullback entry: in an uptrend (rising 22-day EMA of price), buy when the 2-bar Force Index dips below zero.', 'Divergence: price at a new high with a lower Force Index high means the rally has less volume-weighted push behind it.', 'A sharp spike in Force Index on a breakout confirms it; a breakout with a small Force Index is a candidate for failure.'], | |
| 93 | + defaults: [['length', 13, 'Elder\'s intermediate-term setting (a 2-bar EMA is his short-term trigger); 13 filters daily noise while keeping turns visible.']], | |
| 94 | + bestFor: { timeframes: 'Daily bars as designed; weekly for position trades.', markets: 'Stocks and futures with real volume; not meaningful on FX tick counts.', note: 'Part of Elder\'s Triple Screen — use it with a trend filter on a higher timeframe.' }, | |
| 95 | + pairsWith: ['ema', 'elder-ray', 'macd', 'obv'], | |
| 96 | + pitfalls: ['The amplitude is in price × volume units and explodes on high-priced, high-volume names — read the shape, not the level.', 'A single earnings-day bar dominates the EMA for many bars.', 'Splits and volume adjustments change the scale of the history.'], | |
| 97 | + history: 'Dr Alexander Elder, 1993, Trading for a Living.', | |
| 98 | + related: [{ label: 'Elder Ray', href: '/docs/charts/indicators/elder-ray' }, { label: 'EMA', href: '/docs/charts/indicators/ema' }], | |
| 99 | + thumb: { volume: true }, | |
| 100 | + }, | |
| 101 | + { | |
| 102 | + id: 'eom', name: 'Ease of Movement', aka: ['EOM', 'EMV', 'Ease of Movement Value'], category: 'volume', pane: 'new', | |
| 103 | + oneLiner: 'How far price moves per unit of volume: positive = rising easily, negative = falling.', | |
| 104 | + summary: 'Ease of Movement relates the shift of a bar\'s midpoint to the volume it took to produce that shift, scaled by the bar\'s range. A big midpoint move on light volume is an "easy" move (large EOM); a small move on heavy volume is a hard one (EOM near zero). Richard Arms designed it as a smoothed line to read along with his Equivolume charts.', | |
| 105 | + formula: { text: 'distance = (H + L)/2 − (H₁ + L₁)/2\nboxRatio = (V / divisor) / (H − L)\nEOM = SMA(distance / boxRatio, n)', vars: [['H, L / H₁, L₁', 'high and low of the current and previous bar'], ['V', 'volume'], ['divisor', 'volume scaling constant (default 10 000)'], ['n', 'smoothing length (default 14)']] }, | |
| 106 | + howToRead: ['Above zero: price is advancing with little effort — the path of least resistance is up; below zero: the reverse.', 'Zero crosses of the smoothed line are the trading signals; the further from zero, the easier the move.', 'A rally with EOM sinking towards zero is meeting supply: volume is rising for less progress.', 'Values near zero for many bars describe a congestion where volume produces no movement.'], | |
| 107 | + defaults: [['length', 14, 'Arms\' standard two-week smoothing of the raw one-bar values, which are far too erratic to read alone.'], ['divisor', 10000, 'A scaling constant that brings typical stock volumes into a readable range; raise it for very heavy volume, lower it for thin names.']], | |
| 108 | + bestFor: { timeframes: 'Daily bars, as designed.', markets: 'Stocks and ETFs with consistent volume.', note: 'The divisor only scales the output — pick one that keeps values readable and leave it.' }, | |
| 109 | + pairsWith: ['obv', 'volume-ma', 'ema', 'cmf'], | |
| 110 | + pitfalls: ['A bar with zero range or zero volume contributes zero by convention, which can dampen the average.', 'Scale depends on the divisor and on the share count — never compare levels between symbols.', 'Midpoint-based: a wide bar that closes where it opened still registers movement.'], | |
| 111 | + history: 'Richard W. Arms Jr., 1989, Volume Cycles in the Stock Market.', | |
| 112 | + related: [{ label: 'On-Balance Volume', href: '/docs/charts/indicators/obv' }, { label: 'Volume', href: '/docs/charts/indicators/volume-ma' }], | |
| 113 | + thumb: { volume: true }, | |
| 114 | + }, | |
| 115 | + { | |
| 116 | + id: 'klinger', name: 'Klinger Oscillator', aka: ['KVO', 'Klinger Volume Oscillator'], category: 'volume', pane: 'new', | |
| 117 | + oneLiner: 'EMA 34 minus EMA 55 of signed volume, with a signal line — long-term money flow turns.', | |
| 118 | + summary: 'The Klinger Volume Oscillator signs each bar\'s volume by the direction of its typical price, then takes the difference between a 34-period and a 55-period EMA of that signed volume; a 13-period EMA of the oscillator serves as the signal line. Stephen Klinger built it to track long-term money flow while staying sensitive enough to catch short-term turns.', | |
| 119 | + formula: { text: 'TP = (H + L + C) / 3\nSV = +V if TP ≥ TP₁, −V otherwise\nKVO = EMA(SV, fast) − EMA(SV, slow)\nsignal = EMA(KVO, signal)', vars: [['TP, TP₁', 'typical price of the current and previous bar'], ['SV', 'signed volume'], ['fast / slow', 'EMA lengths (defaults 34 / 55)'], ['signal', 'signal EMA length (default 13)']] }, | |
| 120 | + howToRead: ['KVO crossing above its signal line while price is above its 100-bar EMA is the classic long trigger; the mirror for shorts.', 'Zero line: above it, the fast money-flow average exceeds the slow one — accumulation; below, distribution.', 'Divergence between price highs and KVO highs is the strongest warning it gives.', 'Ignore signals against the direction of the long-term price trend.'], | |
| 121 | + defaults: [['fast', 34, 'Fibonacci-inspired lengths chosen by Klinger: 34 bars tracks roughly seven weeks of daily money flow.'], ['slow', 55, 'The next Fibonacci number, about eleven weeks — the baseline the fast EMA is compared with.'], ['signal', 13, 'A 13-bar EMA (again Fibonacci) smooths the oscillator enough for crossovers without adding weeks of lag.']], | |
| 122 | + bestFor: { timeframes: 'Daily bars; weekly for long-term positioning.', markets: 'Stocks, ETFs and futures with real volume.', note: 'A slow indicator — expect few signals and use it to confirm, not to time.' }, | |
| 123 | + pairsWith: ['ema', 'cmf', 'obv', 'macd'], | |
| 124 | + pitfalls: ['Needs well over 55 bars of history before the lines stabilise.', 'This is the simplified signed-volume form; Klinger\'s original volume-force formula (with trend and cumulative-measure terms) produces different values.', 'Volume units: the amplitude changes with the symbol and after splits.'], | |
| 125 | + history: 'Stephen J. Klinger, 1997, Technical Analysis of Stocks & Commodities.', | |
| 126 | + related: [{ label: 'Chaikin Money Flow', href: '/docs/charts/indicators/cmf' }, { label: 'MACD', href: '/docs/charts/indicators/macd' }], | |
| 127 | + thumb: { volume: true, bars: 200 }, | |
| 128 | + }, | |
| 129 | + { | |
| 130 | + id: 'volume-osc', name: 'Volume Oscillator', aka: ['VO', 'PVO'], category: 'volume', pane: 'new', | |
| 131 | + oneLiner: 'Fast volume EMA vs slow volume EMA, in %: is participation rising or fading?', | |
| 132 | + summary: 'The Volume Oscillator measures the percentage difference between a fast and a slow exponential moving average of volume. Positive values mean recent volume is above its longer-term norm — the move is attracting participation; negative values mean interest is drying up.', | |
| 133 | + formula: { text: 'VO = (EMA(V, fast) − EMA(V, slow)) / EMA(V, slow) × 100', vars: [['V', 'volume'], ['fast', 'short EMA length (default 5)'], ['slow', 'long EMA length (default 10)']] }, | |
| 134 | + howToRead: ['Above zero during a breakout: the move has volume support; below zero during a breakout: suspect.', 'A rally with the oscillator falling towards zero is losing sponsors — classic late-trend signature.', 'Rising VO during a decline confirms selling pressure; rising VO after a long decline can mark capitulation.', 'It is direction-blind: read it with price to know what the volume is voting for.'], | |
| 135 | + defaults: [['fast', 5, 'One trading week of volume.'], ['slow', 10, 'Two trading weeks as the reference level — short by design so the oscillator reacts within a few sessions.']], | |
| 136 | + bestFor: { timeframes: 'Daily bars; also 5- to 15-minute bars for intraday breakout confirmation.', markets: 'Stocks, ETFs, futures and crypto.', note: 'A percentage, so readings are comparable between symbols.' }, | |
| 137 | + pairsWith: ['volume-ma', 'donchian', 'bollinger', 'macd'], | |
| 138 | + pitfalls: ['Volume has a strong weekly and intraday seasonality (Friday afternoons, lunch hour) that the short EMAs register as signals.', 'Index rebalancing and expiry days spike the oscillator without any message about the trend.', 'With 5 / 10 it whipsaws around zero constantly in calm markets.'], | |
| 139 | + history: 'Generic volume-momentum construction; popularised in charting packages in the 1990s (the MetaStock "Percentage Volume Oscillator").', | |
| 140 | + related: [{ label: 'Volume', href: '/docs/charts/indicators/volume-ma' }, { label: 'Chaikin Oscillator', href: '/docs/charts/indicators/chaikin-osc' }], | |
| 141 | + thumb: { volume: true }, | |
| 142 | + }, | |
| 143 | + { | |
| 144 | + id: 'volume-ma', name: 'Volume', aka: ['Vol MA', 'Volume + MA'], category: 'volume', pane: 'new', | |
| 145 | + oneLiner: 'Volume histogram coloured by bar direction with its moving average in its own pane.', | |
| 146 | + summary: 'The Volume pane draws the number of shares or contracts traded per bar as a histogram — green when the bar closed up, red when it closed down — and overlays a simple moving average of volume as the "normal" level. It is the reference against which every other volume indicator is judged.', | |
| 147 | + formula: { text: 'volume = V\nma = SMA(V, n)', vars: [['V', 'volume of the bar (contracts for futures, shares for stocks)'], ['n', 'moving-average length (default 20)']] }, | |
| 148 | + howToRead: ['Bars far above the average are the events: breakouts, gaps, news, capitulation — the direction of the bar says who won.', 'Volume should expand in the direction of the trend and dry up on pullbacks; the reverse pattern is distribution.', 'Climax volume (several times the average) at the end of a long move often marks exhaustion.', 'A breakout on below-average volume is the most common failed breakout.', 'On intraday charts expect the U-shape: heavy at the open and close, light at lunch.'], | |
| 149 | + defaults: [['length', 20, 'One trading month of bars gives a stable "normal volume" that adapts to the current interest in the symbol.']], | |
| 150 | + bestFor: { timeframes: 'All; daily for events, intraday for session structure.', markets: 'Stocks, ETFs, futures and crypto; FX volume is a tick count and much weaker.', note: 'The main-pane volume histogram (Settings) is the same data; this pane adds the average.' }, | |
| 151 | + pairsWith: ['obv', 'volume-osc', 'vwap', 'volume-profile'], | |
| 152 | + pitfalls: ['Daily bars mix regular hours and extended trading depending on the source — check the session convention.', 'Adjusted equity series may also adjust volume for splits; unadjusted ones do not.', 'Colour is the close-vs-open direction of the bar, not up vs previous close.'], | |
| 153 | + history: 'Volume analysis dates to Charles Dow (1900s) and Richard Wyckoff (1930s); the coloured histogram with a moving average is the standard charting-software form.', | |
| 154 | + related: [{ label: 'On-Balance Volume', href: '/docs/charts/indicators/obv' }, { label: 'Volume Profile', href: '/docs/charts/indicators/volume-profile' }, { label: 'Charting guide', href: '/docs/charts' }], | |
| 155 | + thumb: { volume: true }, | |
| 156 | + }, | |
| 157 | +] | |
added
hfmarketdata/web/src/charts/catalog/interpret.js
+107 −0
@@ -0,0 +1,107 @@ | ||
| 1 | +// Plain-English reading of an indicator's current values for the legend tooltip ("RSI 72 → overbought zone"). | |
| 2 | +// Pure and tiny (kept out of the lazy encyclopedia chunk): `interpret(def, values, bar)` → string[] (0–3 lines). | |
| 3 | +// def = page definition (src/pages/charts/indicators.js: id, levels, outputs…) | |
| 4 | +// values = { key: number | null } of the hovered / last bar | |
| 5 | +// bar = { o, h, l, c } of the same bar (for overlays) | |
| 6 | + | |
| 7 | +const fin = v => typeof v === 'number' && Number.isFinite(v) | |
| 8 | +const fmt = (v, d = 2) => (fin(v) ? Number(v).toLocaleString('en-US', { maximumFractionDigits: d }) : '—') | |
| 9 | +const pct = (a, b) => (fin(a) && fin(b) && b ? `${((a / b - 1) * 100).toFixed(2)} %` : null) | |
| 10 | + | |
| 11 | +// Bounded oscillators: [valueKey, lower, upper, lowerLabel, upperLabel] | |
| 12 | +const BOUNDED = { | |
| 13 | + rsi: ['rsi', 30, 70], stochrsi: ['k', 20, 80], stoch: ['k', 20, 80], mfi: ['mfi', 20, 80], ultimate: ['uo', 30, 70], | |
| 14 | + williams: ['r', -80, -20], cci: ['cci', -100, 100], cmo: ['cmo', -50, 50], 'bb-pctb': ['pctb', 0, 1, 'below the lower band', 'above the upper band'], | |
| 15 | +} | |
| 16 | +// Zero-line momentum / volume oscillators: [valueKey, aboveLabel, belowLabel] | |
| 17 | +const ZERO = { | |
| 18 | + macd: ['hist', 'histogram above zero — bullish momentum', 'histogram below zero — bearish momentum'], | |
| 19 | + roc: ['roc', 'positive — price above its value n bars ago', 'negative — price below its value n bars ago'], | |
| 20 | + momentum: ['mom', 'positive momentum', 'negative momentum'], | |
| 21 | + ao: ['ao', 'above zero — fast momentum leads slow', 'below zero — fast momentum lags slow'], | |
| 22 | + trix: ['trix', 'rising triple-smoothed trend', 'falling triple-smoothed trend'], | |
| 23 | + coppock: ['coppock', 'above zero — long-term momentum positive', 'below zero — long-term momentum negative'], | |
| 24 | + dpo: ['dpo', 'price above its displaced average', 'price below its displaced average'], | |
| 25 | + kst: ['kst', 'positive — smoothed rate of change rising', 'negative — smoothed rate of change falling'], | |
| 26 | + tsi: ['tsi', 'positive — double-smoothed momentum up', 'negative — double-smoothed momentum down'], | |
| 27 | + rvi: ['rvi', 'closes nearer the highs than the lows', 'closes nearer the lows than the highs'], | |
| 28 | + cmf: ['cmf', 'positive — accumulation (closes near highs on volume)', 'negative — distribution (closes near lows on volume)'], | |
| 29 | + 'chaikin-osc': ['osc', 'above zero — money flow accelerating in', 'below zero — money flow accelerating out'], | |
| 30 | + force: ['force', 'positive — buyers moving price on volume', 'negative — sellers moving price on volume'], | |
| 31 | + eom: ['eom', 'positive — price rising easily on light volume', 'negative — price falling easily on light volume'], | |
| 32 | + klinger: ['kvo', 'positive volume force', 'negative volume force'], | |
| 33 | + 'volume-osc': ['vo', 'short-term volume above its longer average', 'short-term volume below its longer average'], | |
| 34 | +} | |
| 35 | +const SIGNAL = { macd: ['macd', 'signal'], trix: ['trix', 'signal'], kst: ['kst', 'signal'], tsi: ['tsi', 'signal'], rvi: ['rvi', 'signal'], klinger: ['kvo', 'signal'], stoch: ['k', 'd'], stochrsi: ['k', 'd'] } | |
| 36 | +const BANDS = new Set(['bollinger', 'keltner', 'donchian', 'envelope', 'lsma']) | |
| 37 | +const MAS = { sma: 'sma', ema: 'ema', wma: 'wma', hma: 'hma', dema: 'dema', tema: 'tema', kama: 'kama', alma: 'alma', vwap: 'vwap', avwap: 'avwap' } | |
| 38 | + | |
| 39 | +export function interpret(def, values = {}, bar = null) { | |
| 40 | + if (!def) return [] | |
| 41 | + const id = def.id || def.type | |
| 42 | + const out = [] | |
| 43 | + const v = k => values?.[k] | |
| 44 | + const c = bar?.c | |
| 45 | + | |
| 46 | + if (BOUNDED[id]) { | |
| 47 | + const [key, lo, hi, loLabel, hiLabel] = BOUNDED[id] | |
| 48 | + const x = v(key) | |
| 49 | + if (fin(x)) { | |
| 50 | + if (x >= hi) out.push(`${fmt(x)} → ${hiLabel || `overbought zone (≥ ${hi})`}`) | |
| 51 | + else if (x <= lo) out.push(`${fmt(x)} → ${loLabel || `oversold zone (≤ ${lo})`}`) | |
| 52 | + else out.push(`${fmt(x)} → neutral (between ${lo} and ${hi})`) | |
| 53 | + } | |
| 54 | + } | |
| 55 | + if (ZERO[id]) { | |
| 56 | + const [key, above, below] = ZERO[id] | |
| 57 | + const x = v(key) | |
| 58 | + if (fin(x)) out.push(`${fmt(x, 3)} → ${x >= 0 ? above : below}`) | |
| 59 | + } | |
| 60 | + if (SIGNAL[id]) { | |
| 61 | + const [a, b] = SIGNAL[id] | |
| 62 | + if (fin(v(a)) && fin(v(b))) out.push(v(a) >= v(b) ? `${a.toUpperCase()} above ${b} line — bullish cross in force` : `${a.toUpperCase()} below ${b} line — bearish cross in force`) | |
| 63 | + } | |
| 64 | + switch (id) { | |
| 65 | + case 'adx': { | |
| 66 | + const x = v('adx'); const p = v('plusDI'); const m = v('minusDI') | |
| 67 | + if (fin(x)) out.push(x >= 25 ? `ADX ${fmt(x)} → trending market (≥ 25)` : `ADX ${fmt(x)} → weak / ranging (< 25)`) | |
| 68 | + if (fin(p) && fin(m)) out.push(p > m ? '+DI above −DI — bulls in control' : '−DI above +DI — bears in control') | |
| 69 | + break | |
| 70 | + } | |
| 71 | + case 'aroon': { | |
| 72 | + const u = v('up'); const d = v('down') | |
| 73 | + if (fin(u) && fin(d)) out.push(u > 70 && d < 30 ? 'Aroon-Up > 70 and Aroon-Down < 30 → strong uptrend' : d > 70 && u < 30 ? 'Aroon-Down > 70 and Aroon-Up < 30 → strong downtrend' : u > d ? 'Aroon-Up leads — recent high more recent than recent low' : 'Aroon-Down leads — recent low more recent than recent high') | |
| 74 | + break | |
| 75 | + } | |
| 76 | + case 'vortex': { const p = v('plus'); const m = v('minus'); if (fin(p) && fin(m)) out.push(p > m ? 'VI+ above VI− — uptrend' : 'VI− above VI+ — downtrend'); break } | |
| 77 | + case 'choppiness': { const x = v('chop'); if (fin(x)) out.push(x >= 61.8 ? `${fmt(x)} → choppy, range-bound (≥ 61.8)` : x <= 38.2 ? `${fmt(x)} → trending (≤ 38.2)` : `${fmt(x)} → neutral`); break } | |
| 78 | + case 'mass': { const x = v('mass'); if (fin(x)) out.push(x >= 27 ? `${fmt(x)} → reversal bulge building (≥ 27)` : `${fmt(x)} → no bulge (watch 27 then 26.5)`); break } | |
| 79 | + case 'elder-ray': { const b = v('bull'); const r = v('bear'); if (fin(b) && fin(r)) out.push(b > 0 && r > 0 ? 'bull and bear power both above zero — strong bulls' : b < 0 && r < 0 ? 'both below zero — strong bears' : 'mixed — price straddles the EMA'); break } | |
| 80 | + case 'supertrend': { if (fin(v('up'))) out.push(`uptrend — trailing stop ${fmt(v('up'))} below price`); else if (fin(v('down'))) out.push(`downtrend — trailing stop ${fmt(v('down'))} above price`); break } | |
| 81 | + case 'psar': { if (fin(v('bull'))) out.push(`uptrend — SAR ${fmt(v('bull'))} below price`); else if (fin(v('bear'))) out.push(`downtrend — SAR ${fmt(v('bear'))} above price`); break } | |
| 82 | + case 'ichimoku': { | |
| 83 | + const a = v('senkouA'); const b = v('senkouB'); const t = v('tenkan'); const k = v('kijun') | |
| 84 | + if (fin(c) && fin(a) && fin(b)) out.push(c > Math.max(a, b) ? 'price above the cloud — bullish' : c < Math.min(a, b) ? 'price below the cloud — bearish' : 'price inside the cloud — no trend') | |
| 85 | + if (fin(t) && fin(k)) out.push(t > k ? 'Tenkan above Kijun — bullish TK cross' : 'Tenkan below Kijun — bearish TK cross') | |
| 86 | + break | |
| 87 | + } | |
| 88 | + case 'atr': { if (fin(v('atr')) && fin(c) && c) out.push(`${fmt(v('atr'))} = ${pct(c + v('atr'), c)} of price — typical bar range`); break } | |
| 89 | + case 'hv': { if (fin(v('hv'))) out.push(`${fmt(v('hv'))} % annualised — compare with the instrument's usual range`); break } | |
| 90 | + case 'stddev': { if (fin(v('stddev')) && fin(c) && c) out.push(`σ = ${fmt(v('stddev'))} (${pct(c + v('stddev'), c)} of price)`); break } | |
| 91 | + case 'bb-width': { if (fin(v('bbw'))) out.push(`${fmt(v('bbw'))} → narrow = squeeze, wide = expansion (compare with its history)`); break } | |
| 92 | + case 'obv': case 'ad': out.push('cumulative — the slope and its divergences from price matter, not the level'); break | |
| 93 | + case 'volume-ma': { const vol = v('volume'); const ma = v('ma'); if (fin(vol) && fin(ma) && ma) out.push(vol > ma ? `volume ${fmt(vol / ma, 2)}× its average — above normal` : `volume ${fmt(vol / ma, 2)}× its average — below normal`); break } | |
| 94 | + case 'zigzag': out.push('swing structure — higher highs / higher lows = uptrend'); break | |
| 95 | + case 'pivots': { const p = v('p'); if (fin(p) && fin(c)) out.push(c > p ? `price above the pivot ${fmt(p)} — bullish bias for the period` : `price below the pivot ${fmt(p)} — bearish bias for the period`); break } | |
| 96 | + default: break | |
| 97 | + } | |
| 98 | + if (BANDS.has(id) && fin(c)) { | |
| 99 | + const up = v('upper'); const lo = v('lower'); const mid = v('middle') ?? v('lsma') | |
| 100 | + if (fin(up) && fin(lo)) out.push(c > up ? 'close above the upper band — stretched / strong trend' : c < lo ? 'close below the lower band — stretched / strong downtrend' : fin(mid) ? (c >= mid ? 'inside the bands, above the middle line' : 'inside the bands, below the middle line') : 'inside the bands') | |
| 101 | + } | |
| 102 | + if (MAS[id] && fin(c)) { | |
| 103 | + const m = v(MAS[id]) | |
| 104 | + if (fin(m) && m) out.push(c >= m ? `price ${pct(c, m)} above the average — bullish bias` : `price ${pct(c, m)} below the average — bearish bias`) | |
| 105 | + } | |
| 106 | + return out.slice(0, 3) | |
| 107 | +} | |
added
hfmarketdata/web/src/charts/catalog/tools.js
+611 −0
@@ -0,0 +1,611 @@ | ||
| 1 | +// Drawing-tool encyclopedia — one entry per engine tool (src/charts/engine/drawings/model.js). Schema: see ./index.js. | |
| 2 | +// Consumed by the flyout tooltips, the mobile tool sheet and the generated /docs/charts/tools/<id> pages. | |
| 3 | + | |
| 4 | +const T = id => `/docs/charts/tools/${id}` | |
| 5 | +const I = id => `/docs/charts/indicators/${id}` | |
| 6 | +const GENERIC = 'Generic charting primitive; no single author.' | |
| 7 | + | |
| 8 | +export const TOOL_ENTRIES = [ | |
| 9 | + /* ───────────────────────────── lines & channels ───────────────────────────── */ | |
| 10 | + { | |
| 11 | + id: 'trendline', name: 'Trend line', aka: ['Trendline', 'Line segment'], group: 'lines', points: 2, | |
| 12 | + oneLiner: 'A segment between two points — the simplest way to mark a trend or a sloping level.', | |
| 13 | + summary: 'The trend line joins two swing points and stops at them. Drawn under rising lows it defines the slope of an uptrend and the price a pullback must hold; drawn over falling highs it caps a downtrend. It is the workhorse of every discretionary chart.', | |
| 14 | + whatFor: 'Mark the slope of a move, a sloping support or resistance, or a neckline between two reference points.', | |
| 15 | + howToDraw: ['Activate the tool (T).', '2 clicks: click point A (the first swing), then point B (the second swing) — or click-and-drag from A to B.', 'Turn on magnet mode so each anchor snaps to the wick high or low.', 'Select the line to extend it left or right, change colour, width or dash in the properties bar.'], | |
| 16 | + readingTips: ['Two touches draw the line; a third touch validates it — before that it is a hypothesis.', 'The steeper the line, the sooner it breaks: a 60° line rarely holds, a 30° one often does.', 'A close beyond the line with volume is a break; a wick through it is a test.', 'After a break, the old line often acts as the opposite kind of level (support becomes resistance).'], | |
| 17 | + shortcut: 'T', | |
| 18 | + example: 'Lows at 95.00 and 98.50 fifteen bars apart give a slope of about 0.23 per bar; projected 10 bars ahead the support sits near 100.80.', | |
| 19 | + pairsWith: ['ray', 'channel', 'hline', 'ema'], | |
| 20 | + pitfalls: ['Forcing a line through bodies and wicks alternately to make it "fit" produces a level nobody else sees.', 'Trend lines on a log scale and on a linear scale diverge over long histories — pick the scale before drawing.', 'A segment stops at B: use a ray when you want the line to project into the future.'], | |
| 21 | + history: GENERIC, | |
| 22 | + related: [{ label: 'Ray', href: T('ray') }, { label: 'Parallel channel', href: T('channel') }, { label: 'Charting guide', href: '/docs/charts' }], | |
| 23 | + }, | |
| 24 | + { | |
| 25 | + id: 'ray', name: 'Ray', aka: ['Trend ray', 'Half-line'], group: 'lines', points: 2, | |
| 26 | + oneLiner: 'A trend line that starts at point A and runs through B to the right edge — forever.', | |
| 27 | + summary: 'The ray is a trend line anchored at its first point and extended indefinitely through the second, so it keeps projecting a slope into bars that do not exist yet. It is the natural tool for a support or resistance that you expect price to meet again in the future.', | |
| 28 | + whatFor: 'Project a sloping level forward so future bars can be judged against it without redrawing.', | |
| 29 | + howToDraw: ['Open the Lines flyout and pick Ray.', '2 clicks: click the origin (point A), then a second point (B) that sets the direction; the line continues past B to the right.', 'Drag B to change the slope; drag A to move the origin.'], | |
| 30 | + readingTips: ['A ray drawn from two lows is a dynamic support: each new touch that holds strengthens it.', 'When price accelerates away from the ray, draw a steeper one — trends often live on a "fan" of rays.', 'The break of the shallowest ray in a fan is the last warning before a trend change.'], | |
| 31 | + shortcut: null, | |
| 32 | + example: 'A ray through lows at 50.00 and 52.00 taken 20 bars apart rises 0.10 per bar: 30 bars later the support projects to 55.00.', | |
| 33 | + pairsWith: ['trendline', 'extended', 'hray', 'supertrend'], | |
| 34 | + pitfalls: ['A ray never "ends", so a stale ray from months ago can clutter today\'s view — delete or hide old ones.', 'Small errors on the anchors compound with distance: a ray projected 200 bars ahead is only approximate.'], | |
| 35 | + history: GENERIC, | |
| 36 | + related: [{ label: 'Trend line', href: T('trendline') }, { label: 'Extended line', href: T('extended') }], | |
| 37 | + }, | |
| 38 | + { | |
| 39 | + id: 'extended', name: 'Extended line', aka: ['Infinite line'], group: 'lines', points: 2, | |
| 40 | + oneLiner: 'A line through two points extended in both directions across the whole chart.', | |
| 41 | + summary: 'The extended line passes through its two anchors and continues to both edges of the chart. It is used when the past matters as much as the future: to check whether a slope already governed earlier swings, or to trace a channel boundary back to its origin.', | |
| 42 | + whatFor: 'Test a sloping level against the whole history on screen, backwards and forwards.', | |
| 43 | + howToDraw: ['Open the Lines flyout and pick Extended line.', '2 clicks: click any two points on the level you want to test; the line grows to both edges.', 'Adjust either anchor to rotate the line around the other.'], | |
| 44 | + readingTips: ['Look left: an extended line that also touches swings from long before the anchors is a genuine structural level.', 'Use it to prolong a neckline or a channel side beyond the pattern that created it.', 'Combine with a trend line of the opposite slope to see where the two converge — apex dates matter for triangles.'], | |
| 45 | + shortcut: null, | |
| 46 | + example: 'Two highs at 210 and 204 set a falling resistance; extended left it also caps the 220 high from six months earlier, confirming a long-lived downtrend line.', | |
| 47 | + pairsWith: ['trendline', 'ray', 'channel'], | |
| 48 | + pitfalls: ['Infinite lines cross the whole chart, so several of them quickly become a lattice — keep two or three at most.', 'The same two anchors on a percent or log scale give a different line; extended lines exaggerate that difference.'], | |
| 49 | + history: GENERIC, | |
| 50 | + related: [{ label: 'Ray', href: T('ray') }, { label: 'Trend line', href: T('trendline') }], | |
| 51 | + }, | |
| 52 | + { | |
| 53 | + id: 'hray', name: 'Horizontal ray', aka: ['Horizontal half-line', 'Level from here'], group: 'lines', points: 1, | |
| 54 | + oneLiner: 'A horizontal level from the click to the right edge — a level with a birth date.', | |
| 55 | + summary: 'The horizontal ray marks a price from a specific bar onwards. Unlike the full horizontal line it does not claim the level mattered before that bar, which is exactly right for a breakout level, an untested gap or a swing high that has just formed.', | |
| 56 | + whatFor: 'Mark a level that only became relevant at a given bar, and follow it into the future.', | |
| 57 | + howToDraw: ['Open the Lines flyout and pick Horizontal ray.', '1 click at the bar and price where the level starts; the ray extends to the right edge.', 'Drag the anchor to move it in time or price; add a text label in the properties bar.'], | |
| 58 | + readingTips: ['A ray from a breakout candle marks the retest zone: price returning to it and holding confirms the break.', 'Rays from unfilled gaps or single-print highs show what the market still owes.', 'Many rays converging on one price after different swings identify a cluster level.'], | |
| 59 | + shortcut: null, | |
| 60 | + example: 'The 3 March high at 182.40 becomes a ray; 12 sessions later price stalls at 182.35 — the level held without a full horizontal line cluttering the past.', | |
| 61 | + pairsWith: ['hline', 'rect', 'price-label', 'pivots'], | |
| 62 | + pitfalls: ['Placing the origin on the wrong bar makes the level look older or younger than it is — use magnet mode.', 'Horizontal rays on an indicator pane use the pane value, not the price.'], | |
| 63 | + history: GENERIC, | |
| 64 | + related: [{ label: 'Horizontal line', href: T('hline') }, { label: 'Price label', href: T('price-label') }], | |
| 65 | + }, | |
| 66 | + { | |
| 67 | + id: 'hline', name: 'Horizontal line', aka: ['Price level', 'Support / resistance line'], group: 'lines', points: 1, | |
| 68 | + oneLiner: 'A full-width line at one price — the classic support or resistance level.', | |
| 69 | + summary: 'The horizontal line marks a price across the whole chart, past and future. It is the standard notation for support and resistance, round numbers, prior closes or any price the market keeps reacting to. The right-click menu can also place one exactly at the pointer price.', | |
| 70 | + whatFor: 'Mark a horizontal level that matters regardless of when it was formed.', | |
| 71 | + howToDraw: ['Activate the tool (H).', '1 click at the price you want; the line spans the chart.', 'Or right-click the chart and choose "Horizontal line at this price".', 'Drag vertically to fine-tune; type a label in the properties bar.'], | |
| 72 | + readingTips: ['The more times a level has been touched and respected, the more it is watched — and the more violent its eventual break.', 'A level lives in a zone, not a pixel: think ±0.2 % or a fraction of ATR around the line.', 'Once broken, a support usually becomes a resistance (and vice versa): keep the line.', 'Round numbers and prior daily closes are levels even without a visible swing.'], | |
| 73 | + shortcut: 'H', | |
| 74 | + example: 'Three rejections at 150.00 over two months make the line; the fourth attempt closes at 151.20 on twice the average volume and the line becomes the new support.', | |
| 75 | + pairsWith: ['hray', 'rect', 'pivots', 'volume-profile'], | |
| 76 | + pitfalls: ['A horizontal line hides the fact that levels drift: use a rectangle when the reaction zone is wide.', 'Drawing lines at every swing produces a barcode — keep the ones tested at least twice.'], | |
| 77 | + history: GENERIC, | |
| 78 | + related: [{ label: 'Horizontal ray', href: T('hray') }, { label: 'Rectangle', href: T('rect') }, { label: 'Pivot Points', href: I('pivots') }], | |
| 79 | + }, | |
| 80 | + { | |
| 81 | + id: 'vline', name: 'Vertical line', aka: ['Time marker', 'Event line'], group: 'lines', points: 1, | |
| 82 | + oneLiner: 'A full-height line at one bar — marks an event, a session open or a cycle date.', | |
| 83 | + summary: 'The vertical line marks a moment in time across every pane: an earnings release, a Fed decision, a session open or the bar where a pattern started. It is the simplest way to align price, volume and oscillator behaviour on the same instant.', | |
| 84 | + whatFor: 'Anchor an event or a date so its effect can be read across price and indicator panes at once.', | |
| 85 | + howToDraw: ['Activate the tool (V).', '1 click on the bar to mark; the line spans every pane.', 'Drag horizontally to move it to another bar; the line always snaps to a bar centre.'], | |
| 86 | + readingTips: ['Compare the bars before and after the line: a volume spike with a range expansion confirms the event mattered.', 'Regularly spaced vertical lines expose cycles — count the bars between major lows.', 'Combine with a horizontal line to see the price the market held at that instant (or use the cross tool).'], | |
| 87 | + shortcut: 'V', | |
| 88 | + example: 'A vertical line at the 10:00 ET economic release shows the 1-minute bar that opened the day\'s 1.4 % range; every pullback afterwards stayed above that bar\'s low.', | |
| 89 | + pairsWith: ['cross', 'vrange', 'date-range', 'volume-ma'], | |
| 90 | + pitfalls: ['The time axis is indexed by bar, so a line placed on a daily chart lands on a session, not a calendar day; on intraday charts session gaps are compressed.', 'Vertical lines drawn on one timeframe reappear on others at the nearest bar, which may look shifted.'], | |
| 91 | + history: GENERIC, | |
| 92 | + related: [{ label: 'Cross line', href: T('cross') }, { label: 'Vertical range', href: T('vrange') }], | |
| 93 | + }, | |
| 94 | + { | |
| 95 | + id: 'cross', name: 'Cross line', aka: ['Crosshair marker', 'Price-time cross'], group: 'lines', points: 1, | |
| 96 | + oneLiner: 'A horizontal and a vertical line through one point — pins a price and a moment together.', | |
| 97 | + summary: 'The cross line freezes a crosshair on the chart: one horizontal line at the price and one vertical line at the bar you click. It is the quickest way to record where price was at a precise time and to compare later bars against that price.', | |
| 98 | + whatFor: 'Bookmark a price-and-time reference such as a session open, a news print or the point where a thesis started.', | |
| 99 | + howToDraw: ['Activate the tool (C).', '1 click at the price and bar to pin; both lines appear.', 'Drag the centre to move both lines together.'], | |
| 100 | + readingTips: ['Everything above-right of the cross is "higher and later" — a quick visual test of whether a move has progressed.', 'Pin the cross on an opening print to read the session as positive or negative territory.', 'Use it during replay to mark the bar where you took a decision, then step forward.'], | |
| 101 | + shortcut: 'C', | |
| 102 | + example: 'Pinning the cross at the 09:30 open of 412.50 shows at a glance that every bar until 11:15 closed above the open — a trend day in the making.', | |
| 103 | + pairsWith: ['hline', 'vline', 'measure', 'vwap'], | |
| 104 | + pitfalls: ['Several crosses quickly look like a grid — delete them once the reference has served its purpose.', 'The horizontal part is a full line, so it is also visible far from the moment it refers to.'], | |
| 105 | + history: GENERIC, | |
| 106 | + related: [{ label: 'Horizontal line', href: T('hline') }, { label: 'Vertical line', href: T('vline') }], | |
| 107 | + }, | |
| 108 | + { | |
| 109 | + id: 'channel', name: 'Parallel channel', aka: ['Trend channel', 'Equidistant channel'], group: 'lines', points: 3, | |
| 110 | + oneLiner: 'Two parallel lines around a trend — the path price is expected to travel inside.', | |
| 111 | + summary: 'The parallel channel is a trend line plus a parallel copy placed at a chosen distance, forming a corridor. Price oscillating between the two rails describes an orderly trend; the rails give targets for swings and the break of a rail signals acceleration or reversal.', | |
| 112 | + whatFor: 'Frame an orderly trend, anticipate where swings end, and spot the moment a trend leaves its corridor.', | |
| 113 | + howToDraw: ['Open the Lines flyout and pick Parallel channel.', '3 clicks: click two points of the first rail (usually two lows in an uptrend), then a third point that sets the distance of the parallel rail (a high between them).', 'Drag the third anchor to widen or narrow the channel; drag the first two to change the slope.', 'Turn on the fill in the properties bar to shade the corridor.'], | |
| 114 | + readingTips: ['Buying near the lower rail and selling near the upper one is the textbook channel trade — as long as the slope holds.', 'A swing that fails to reach the far rail is the first sign of weakness in the trend.', 'A break out of the channel in the trend direction often travels one channel width; against the trend it signals a reversal.', 'The middle of the channel acts as a pivot: above it the trend is healthy, below it fragile.'], | |
| 115 | + shortcut: null, | |
| 116 | + example: 'An up channel with rails 6 points apart, lows at 100 and 104: the third swing high near 110 tags the upper rail, the next pullback to 106 is a lower-rail entry with a 6-point target.', | |
| 117 | + pairsWith: ['trendline', 'regression', 'pitchfork', 'bollinger'], | |
| 118 | + pitfalls: ['A channel fitted to only two lows and one high is fragile; a third touch on each rail confirms it.', 'Parallel channels assume constant volatility; when ranges expand, a regression channel (±σ) or Keltner bands adapt better.'], | |
| 119 | + history: GENERIC, | |
| 120 | + related: [{ label: 'Regression trend', href: T('regression') }, { label: 'Andrews pitchfork', href: T('pitchfork') }, { label: 'Bollinger Bands', href: I('bollinger') }], | |
| 121 | + }, | |
| 122 | + { | |
| 123 | + id: 'regression', name: 'Regression trend', aka: ['Linear regression channel', 'Raff channel'], group: 'lines', points: 2, | |
| 124 | + oneLiner: 'Least-squares line through the closes between two dates, with a ±2σ channel.', | |
| 125 | + summary: 'The regression trend fits a straight line to every close between its two anchors and draws parallel bands two standard deviations above and below it. It is the statistically honest channel: the slope is the average drift of the period and the bands hold roughly 95 % of the closes.', | |
| 126 | + whatFor: 'Measure the true slope of a move and judge whether the current price is stretched or cheap relative to it.', | |
| 127 | + howToDraw: ['Open the Lines flyout and pick Regression trend.', '2 clicks: click the first bar of the period, then the last; the line and the ±2σ channel are computed from the closes in between.', 'Change the multiplier (style.mult, default 2) in the properties bar to tighten or widen the bands.', 'Drag either anchor to re-fit the regression on a different window.'], | |
| 128 | + readingTips: ['Price at the upper band is about two standard deviations above the trend: expensive relative to the period, not necessarily a sell.', 'A steep slope with narrow bands is a smooth, strong trend; a flat line with wide bands is a range.', 'When the newest closes pile up outside the channel, the regime has probably changed — re-anchor.', 'Extend the channel mentally, not literally: the fit is only valid inside the window that produced it.'], | |
| 129 | + shortcut: null, | |
| 130 | + example: 'Over 40 daily closes the regression rises 0.35 per day with σ = 1.8: the channel is ±3.6 wide, and a close 4.5 above the line is a genuine outlier.', | |
| 131 | + pairsWith: ['channel', 'lsma', 'bollinger', 'stddev'], | |
| 132 | + pitfalls: ['The channel is only as good as the window: include a regime change and the slope is meaningless.', 'Standard deviation assumes symmetric noise; a trending market spends more time on one side of the line than the other.', 'The fit is recomputed from the closes on the chart; a different adjustment (split-only vs. total return) gives a different line.'], | |
| 133 | + history: 'Ordinary least squares dates to Legendre and Gauss (c. 1805); the ±σ price channel form was popularised by Gilbert Raff in the 1990s.', | |
| 134 | + related: [{ label: 'Linear Regression (LSMA + channel)', href: I('lsma') }, { label: 'Parallel channel', href: T('channel') }, { label: 'Standard Deviation', href: I('stddev') }], | |
| 135 | + }, | |
| 136 | + { | |
| 137 | + id: 'pitchfork', name: 'Andrews pitchfork', aka: ['Median line', 'Andrews fork'], group: 'lines', points: 3, | |
| 138 | + oneLiner: 'Median line from a pivot through the midpoint of the next swing, with two parallel tines.', | |
| 139 | + summary: 'The pitchfork starts at a pivot and draws a median line through the midpoint of the following swing, plus two parallel lines through the swing\'s extremes. Andrews observed that price tends to return to the median line about 80 % of the time, making the tines a dynamic channel with a built-in target.', | |
| 140 | + whatFor: 'Define the expected path of a new trend from its first three pivots and project a median-line target.', | |
| 141 | + howToDraw: ['Activate the tool (P).', '3 clicks: click the first pivot (the origin), then the next swing high, then the next swing low (or low then high in a downtrend).', 'The median line runs from point 1 through the midpoint of 2–3; the tines pass through 2 and 3, parallel to it.', 'Use magnet mode so each pivot snaps to a wick extreme.'], | |
| 142 | + readingTips: ['Price should gravitate back to the median line; failure to reach it is a warning that the move is weaker than the fork suggests.', 'A close beyond a tine that is not quickly recovered often leads to a move of one tine-width.', 'The upper tine in an uptrend is a natural profit-taking zone; the lower tine a re-entry zone.', 'Draw a new fork every time three new pivots form — the most recent one is the one to trade.'], | |
| 143 | + shortcut: 'P', | |
| 144 | + example: 'Pivots at 100 (low), 112 (high) and 106 (low): the median line starts at 100 and heads through 109; the upper tine passes through 112, the lower through 106, each 3 points from the median.', | |
| 145 | + pairsWith: ['schiff', 'mschiff', 'channel', 'zigzag'], | |
| 146 | + pitfalls: ['The fork is extremely sensitive to the choice of pivots: two traders with different points 1 draw very different forks.', 'In a fast market price can run along a tine without ever revisiting the median; do not force a fade.', 'A pitchfork whose median is steeper than 45° is rarely respected for long — try the Schiff variants.'], | |
| 147 | + history: 'Alan Hall Andrews, 1960s, the "median line" method taught in his Action–Reaction course.', | |
| 148 | + related: [{ label: 'Schiff pitchfork', href: T('schiff') }, { label: 'Modified Schiff pitchfork', href: T('mschiff') }, { label: 'ZigZag', href: I('zigzag') }], | |
| 149 | + }, | |
| 150 | + { | |
| 151 | + id: 'schiff', name: 'Schiff pitchfork', aka: ['Schiff fork', 'Schiff median line'], group: 'lines', points: 3, | |
| 152 | + oneLiner: 'Pitchfork with the origin shifted halfway to point 2 in price — a flatter median line.', | |
| 153 | + summary: 'The Schiff pitchfork uses the same three pivots as the Andrews fork but moves the origin of the median line up (or down) to the mid-price between points 1 and 2. The result is a shallower fork that fits corrective, less steep markets where the classic median line over-promises.', | |
| 154 | + whatFor: 'Frame a trend that is too gentle for a standard pitchfork, especially after a sharp initial impulse.', | |
| 155 | + howToDraw: ['Open the Lines flyout and pick Schiff pitchfork.', '3 clicks: click pivot 1 (origin), pivot 2 and pivot 3, exactly as for the Andrews fork.', 'The engine shifts the origin to the price midpoint of 1–2 (same bar as point 1) and draws the median and tines from there.', 'Toggle between Andrews, Schiff and modified Schiff on the same pivots to see which one price has respected.'], | |
| 156 | + readingTips: ['Use it when the Andrews median is being ignored because it is too steep.', 'The tines still hold the "one tine-width" rule after a break.', 'If neither the Andrews nor the Schiff median attracts price, the three pivots are probably not the right ones.'], | |
| 157 | + shortcut: null, | |
| 158 | + example: 'With pivots at 100, 112 and 106, the Schiff origin becomes 106 on the bar of point 1, so the median slope is roughly half that of the Andrews fork drawn on the same points.', | |
| 159 | + pairsWith: ['pitchfork', 'mschiff', 'trendline'], | |
| 160 | + pitfalls: ['Choosing between the three fork variants after the fact is curve-fitting; decide the variant from the character of the market first.', 'The shifted origin sits in empty space — the fork no longer passes through a real pivot.'], | |
| 161 | + history: 'Jerome Schiff, a student of Alan Andrews, 1980s; the modified version came later from Andrews\' followers.', | |
| 162 | + related: [{ label: 'Andrews pitchfork', href: T('pitchfork') }, { label: 'Modified Schiff pitchfork', href: T('mschiff') }], | |
| 163 | + }, | |
| 164 | + { | |
| 165 | + id: 'mschiff', name: 'Modified Schiff pitchfork', aka: ['Modified Schiff', 'Half-fork'], group: 'lines', points: 3, | |
| 166 | + oneLiner: 'Pitchfork with the origin moved to the midpoint of points 1 and 2 in both price and time.', | |
| 167 | + summary: 'The modified Schiff pitchfork shifts the origin of the median line to the exact midpoint of the segment between pivots 1 and 2 — halfway in time as well as in price. It yields the flattest of the three forks and is the usual choice for slow, grinding trends and for large-scale weekly structures.', | |
| 168 | + whatFor: 'Fit a median-line channel to a shallow or long-running trend where the Andrews and Schiff forks are still too steep.', | |
| 169 | + howToDraw: ['Open the Lines flyout and pick Modified Schiff pitchfork.', '3 clicks: pivot 1 (origin), pivot 2, pivot 3, as for any pitchfork.', 'The origin is placed at the midpoint of 1–2 (time and price); the median and tines are drawn from there through the midpoint of 2–3.', 'Compare with the Andrews and Schiff forks on the same points before choosing.'], | |
| 170 | + readingTips: ['When price hugs the median of a modified Schiff fork, the trend is orderly but slow — expect small pullbacks.', 'A break of the lower tine in an up-fork usually ends the structure rather than offering a re-entry.', 'The flat slope makes the tines useful as long-lived support and resistance on higher timeframes.'], | |
| 171 | + shortcut: null, | |
| 172 | + example: 'Pivots at bar 0 / 100, bar 20 / 112 and bar 30 / 106: the origin moves to bar 10 / 106, giving a median line half as steep and starting later than the Andrews version.', | |
| 173 | + pairsWith: ['pitchfork', 'schiff', 'regression'], | |
| 174 | + pitfalls: ['Starting the fork ten bars later than the first pivot means the earliest part of the move is not framed at all.', 'On very short windows the three variants nearly coincide; the distinction only matters on longer swings.'], | |
| 175 | + history: 'Variant of Jerome Schiff\'s fork developed by later median-line practitioners (1990s).', | |
| 176 | + related: [{ label: 'Andrews pitchfork', href: T('pitchfork') }, { label: 'Schiff pitchfork', href: T('schiff') }], | |
| 177 | + }, | |
| 178 | + | |
| 179 | + /* ───────────────────────────── Fibonacci & Gann ───────────────────────────── */ | |
| 180 | + { | |
| 181 | + id: 'fib', name: 'Fibonacci retracement', aka: ['Fib retracement', 'Fibs'], group: 'fib', points: 2, | |
| 182 | + oneLiner: 'Horizontal levels at 23.6–78.6 % of a swing — where pullbacks tend to pause or reverse.', | |
| 183 | + summary: 'The retracement tool divides a completed swing into the classic Fibonacci ratios and draws a horizontal level at each one. Traders watch the 38.2, 50 and 61.8 % lines as the zones where a pullback is most likely to find support (in an uptrend) or resistance (in a downtrend).', | |
| 184 | + whatFor: 'Locate probable pullback zones inside a trend and set entries, stops and partial targets relative to the last impulse.', | |
| 185 | + howToDraw: ['Activate the tool (F).', '2 clicks: click the swing low (point A) — the start of the impulse — then the swing high (point B); the levels appear between them, 0 at B and 1 at A.', 'For a downtrend, click the high first, then the low.', 'Drag either anchor to refine; edit the level list (add 1.618) in the properties bar.'], | |
| 186 | + readingTips: ['Shallow retracements (23.6–38.2 %) signal a strong trend; deep ones (61.8–78.6 %) a fragile one.', 'Confluence with a moving average, a VWAP or a prior level makes a Fibonacci zone far more credible.', 'A close beyond 100 % invalidates the retracement idea: the swing has been undone.', 'Use magnet mode so the anchors snap exactly to the wick extremes.'], | |
| 187 | + shortcut: 'F', | |
| 188 | + example: 'A rally from 100 to 120 puts the 0.382 line at 112.36, the 0.5 at 110 and the 0.618 at 107.64: a pullback that holds 110–112 keeps the uptrend intact.', | |
| 189 | + pairsWith: ['fib-extension', 'trendline', 'ema', 'vwap'], | |
| 190 | + pitfalls: ['Anchoring on closes instead of wicks shifts every level; pick one convention and keep it.', 'Levels are self-fulfilling only on liquid, widely watched instruments and timeframes.', 'Drawing Fibs on every minor wiggle produces a wall of lines and no information.'], | |
| 191 | + history: 'Ratios from Leonardo of Pisa\'s 13th-century sequence; applied to price swings by Ralph Nelson Elliott in the 1930s–40s.', | |
| 192 | + related: [{ label: 'Trend-based Fib extension', href: T('fib-extension') }, { label: 'Auto Fibonacci indicator', href: I('auto-fib') }], | |
| 193 | + }, | |
| 194 | + { | |
| 195 | + id: 'fib-extension', name: 'Trend-based Fib extension', aka: ['Fib extension', 'Fibonacci projection', 'ABC projection'], group: 'fib', points: 3, | |
| 196 | + oneLiner: 'Projects 0.618–2.618 of an impulse from the end of its pullback — next-leg targets.', | |
| 197 | + summary: 'The trend-based extension measures an impulse (A to B), waits for the pullback to end (C) and projects Fibonacci multiples of the impulse from C. The 1.0, 1.618 and 2.618 lines are the classic targets for the next leg; the 0.618 is the minimum most traders expect.', | |
| 198 | + whatFor: 'Set profit targets for the leg that follows a retracement, based on the size of the leg before it.', | |
| 199 | + howToDraw: ['Open the Fibonacci flyout and pick Trend-based Fib extension.', '3 clicks: click the start of the impulse (A), its end (B), then the end of the pullback (C).', 'Levels 0, 0.382, 0.618, 1, 1.382, 1.618, 2 and 2.618 of the A–B distance are drawn from C.', 'Drag C as the pullback develops; the projections follow.'], | |
| 200 | + readingTips: ['The 1.0 extension (a leg equal to the first) is the most common target; 1.618 marks an extended, strong trend.', 'A leg stopping at 0.618 of the previous one is a sign of a maturing move.', 'Stack the extension with a retracement of a larger swing: overlapping levels are stronger targets.', 'Beyond 2.618 the move is parabolic — trail stops rather than pick a top.'], | |
| 201 | + shortcut: null, | |
| 202 | + example: 'Impulse 100 → 120 (20 points), pullback to 110: the 0.618 target is 122.36, the 1.0 target 130 and the 1.618 target 142.36.', | |
| 203 | + pairsWith: ['fib', 'elliott-impulse', 'measure', 'macd'], | |
| 204 | + pitfalls: ['Choosing C too early — before the pullback has clearly ended — puts every target too low or too high.', 'Extensions say nothing about time: a 1.618 target can take one bar or fifty.', 'On a percent or log scale the projections should be geometric, but the tool works in price points.'], | |
| 205 | + history: 'Projection technique formalised by Elliott-wave practitioners; the "trend-based" three-point form spread with charting software in the 1990s.', | |
| 206 | + related: [{ label: 'Fibonacci retracement', href: T('fib') }, { label: 'Elliott impulse wave', href: T('elliott-impulse') }], | |
| 207 | + }, | |
| 208 | + { | |
| 209 | + id: 'fib-timezones', name: 'Fibonacci time zones', aka: ['Fib time', 'Time zones'], group: 'fib', points: 2, | |
| 210 | + oneLiner: 'Vertical lines 1, 2, 3, 5, 8, 13… bars apart — dates where turning points may cluster.', | |
| 211 | + summary: 'Fibonacci time zones project the Fibonacci sequence forward in time: from a reference bar, vertical lines are placed 1, 2, 3, 5, 8, 13, 21… units later, the unit being the distance between the two anchor bars. The idea is that trend changes tend to occur near these dates rather than at random.', | |
| 212 | + whatFor: 'Highlight future dates where a reversal or acceleration is statistically more plausible, to time entries and exits.', | |
| 213 | + howToDraw: ['Open the Fibonacci flyout and pick Fibonacci time zones.', '2 clicks: click the reference bar (0), then a second bar that defines the base unit (1).', 'Vertical lines are drawn at 0, 1, 2, 3, 5, 8, 13, 21, 34… units to the right; enable labels in the properties bar.', 'Anchor on a major low or high; the unit is often the length of the first swing.'], | |
| 214 | + readingTips: ['Treat each zone as a window of a couple of bars, not an exact date.', 'A zone that coincides with a price level (Fib retracement, pivot) is the strongest setup.', 'The early lines (1, 2, 3) are too close to mean much; the 8, 13 and 21 zones are the ones traders watch.'], | |
| 215 | + shortcut: null, | |
| 216 | + example: 'Anchored on a low with a 5-day unit, the zones fall 5, 10, 15, 25, 40 and 65 trading days later; the 40-day zone marked the next swing high within two sessions.', | |
| 217 | + pairsWith: ['fib', 'vline', 'date-range', 'zigzag'], | |
| 218 | + pitfalls: ['The zones depend entirely on the chosen unit; a one-bar change in the second anchor moves the 34th zone by 34 bars.', 'The time axis is indexed by bar, so zones are counted in bars, not calendar days — holidays shift them.', 'Confirmation bias is strong: some zone will always be "near" any turn.'], | |
| 219 | + history: 'Time projections from the Fibonacci sequence, used by Ralph Nelson Elliott and later popularised by Robert Fischer (1993).', | |
| 220 | + related: [{ label: 'Fibonacci retracement', href: T('fib') }, { label: 'Date range', href: T('date-range') }], | |
| 221 | + }, | |
| 222 | + { | |
| 223 | + id: 'fib-fan', name: 'Fibonacci speed/resistance fan', aka: ['Fib fan', 'Speed resistance lines'], group: 'fib', points: 2, | |
| 224 | + oneLiner: 'Rays from a pivot through the 23.6–78.6 % levels of a swing — sloping Fibonacci support.', | |
| 225 | + summary: 'The Fibonacci fan turns retracement levels into trend lines. From the origin of a swing, rays are drawn through the points where the vertical line at the swing\'s end crosses the 23.6, 38.2, 50, 61.8 and 78.6 % levels. Pullbacks are expected to find support along these rays, which makes the tool a time-aware version of the retracement.', | |
| 226 | + whatFor: 'Track sloping support and resistance that tightens as the trend ages, instead of fixed horizontal levels.', | |
| 227 | + howToDraw: ['Open the Fibonacci flyout and pick Fibonacci speed/resistance fan.', '2 clicks: click the swing low (origin), then the swing high; five rays fan out from the origin.', 'For a downtrend click the high first, then the low.', 'Drag the second anchor to update the fan as the swing extends.'], | |
| 228 | + readingTips: ['Price holding above the 38.2 % ray is a strong trend; sliding down to the 61.8 % ray means the trend is losing speed.', 'Each ray, once broken, tends to become resistance on the way back up.', 'The fan converges towards the origin: the further right you look, the wider the zones between rays.'], | |
| 229 | + shortcut: null, | |
| 230 | + example: 'Swing 100 → 120 over 30 bars: the 0.618 ray rises from 100 through 107.64 at bar 30 and sits near 112 at bar 45 — a sloping support to watch on the pullback.', | |
| 231 | + pairsWith: ['fib', 'gann-fan', 'trendline', 'adx'], | |
| 232 | + pitfalls: ['The fan angles change with the horizontal zoom and with the price scale mode — the levels are geometric, the angles are not.', 'A fan drawn on an incomplete swing has to be redrawn every time the swing extends.'], | |
| 233 | + history: 'Speed resistance lines by Edson Gould (1960s), later adapted to Fibonacci ratios.', | |
| 234 | + related: [{ label: 'Gann fan', href: T('gann-fan') }, { label: 'Fibonacci retracement', href: T('fib') }], | |
| 235 | + }, | |
| 236 | + { | |
| 237 | + id: 'fib-arcs', name: 'Fibonacci arcs', aka: ['Fib arcs', 'Fibonacci circles'], group: 'fib', points: 2, | |
| 238 | + oneLiner: 'Arcs at 38.2–100 % of a swing, centred on its end — support that curves with time.', | |
| 239 | + summary: 'Fibonacci arcs draw circles centred on the end of a swing with radii equal to 38.2, 50, 61.8 and 100 % of the swing length. Price meeting an arc is expected to react, and because the arcs curve, the support level rises or falls as time passes — combining a price and a time component in one shape.', | |
| 240 | + whatFor: 'Anticipate pullback support that weakens over time, and see where retracements and time meet.', | |
| 241 | + howToDraw: ['Open the Fibonacci flyout and pick Fibonacci arcs.', '2 clicks: click the start of the swing, then its end (the centre of the arcs).', 'Four arcs are drawn at 0.382, 0.5, 0.618 and 1.0 of the distance between the two points.', 'Adjust the anchors and the chart zoom until the arcs cover the pullback area.'], | |
| 242 | + readingTips: ['A pullback that turns at the 38.2 % arc quickly is stronger than one that drifts down to the 61.8 % arc.', 'Where an arc meets a horizontal Fib level or a trend line is the high-probability reaction point.', 'Arcs are most readable when the swing is steep and the pullback fast.'], | |
| 243 | + shortcut: null, | |
| 244 | + example: 'On a 100 → 120 swing the 0.618 arc sits 12.4 "chart units" from the high; a pullback that touches it 10 bars later finds the arc near 109, higher than the flat 107.64 retracement line.', | |
| 245 | + pairsWith: ['fib', 'fib-fan', 'ellipse'], | |
| 246 | + pitfalls: ['The arcs are circles in pixel space: stretching the price axis or zooming in time deforms them and moves every intersection.', 'The tool is therefore only meaningful at the zoom level where it was drawn — take a screenshot if the reading matters.', 'Few traders use arcs, so their self-fulfilling power is weaker than horizontal Fibs.'], | |
| 247 | + history: 'Introduced with early charting software in the 1980s as a time-price extension of Fibonacci retracements.', | |
| 248 | + related: [{ label: 'Fibonacci retracement', href: T('fib') }, { label: 'Fibonacci speed/resistance fan', href: T('fib-fan') }], | |
| 249 | + }, | |
| 250 | + { | |
| 251 | + id: 'gann-fan', name: 'Gann fan', aka: ['Gann angles', '1×1 line'], group: 'fib', points: 2, | |
| 252 | + oneLiner: 'Nine rays at Gann angles (1×8 to 8×1) from a pivot — the 1×1 line is the trend\'s balance.', | |
| 253 | + summary: 'The Gann fan draws rays from a pivot at fixed price-to-time ratios: 1×8, 1×4, 1×3, 1×2, 1×1, 2×1, 3×1, 4×1 and 8×1 units of price per unit of time. The 1×1 (45°) line is the balance between price and time; trading above it is bullish, below it bearish, and each angle acts as support or resistance in turn.', | |
| 254 | + whatFor: 'Judge the strength of a trend by the angle it rides and anticipate the next angle when one breaks.', | |
| 255 | + howToDraw: ['Activate the tool (G).', '2 clicks: click the pivot (origin), then a second point that defines the 1×1 line; the eight other angles are derived from it.', 'Choose the second point so that the 1×1 matches one unit of price per bar in your own scaling (e.g. 1 point per day).', 'Drag the second anchor to rescale every angle at once.'], | |
| 256 | + readingTips: ['Price above the 1×1 is a healthy uptrend; a drop to the 1×2 means the trend has halved its speed.', 'Breaking one angle usually sends price to the next one — angles are stepping stones.', 'The steep angles (4×1, 8×1) are only sustainable in the first phase of a parabolic move.'], | |
| 257 | + shortcut: 'G', | |
| 258 | + example: 'With the 1×1 set to 1 point per bar from a low at 100, price at 125 after 20 bars sits above the 1×1 (120) and below the 2×1 (140) — a strong but not parabolic trend.', | |
| 259 | + pairsWith: ['gann-box', 'fib-fan', 'trendline', 'roc'], | |
| 260 | + pitfalls: ['Gann angles depend on the price-per-bar scaling you impose; there is no objective 45° on a chart whose axes are arbitrary.', 'A fan drawn on a stock at 20 and one at 2 000 need very different second points — always calibrate the 1×1.'], | |
| 261 | + history: 'William Delbert Gann, 1930s, geometric angles from his trading courses.', | |
| 262 | + related: [{ label: 'Gann box', href: T('gann-box') }, { label: 'Fibonacci speed/resistance fan', href: T('fib-fan') }], | |
| 263 | + }, | |
| 264 | + { | |
| 265 | + id: 'gann-box', name: 'Gann box', aka: ['Gann square', 'Gann grid'], group: 'fib', points: 2, | |
| 266 | + oneLiner: 'A box split at 25–75 % in price and time, with diagonals — a price-time grid for a swing.', | |
| 267 | + summary: 'The Gann box frames a swing in a rectangle and subdivides it in both directions at 0.25, 0.382, 0.5, 0.618 and 0.75, adding the main diagonals. It shows how far a move has travelled in price and in time simultaneously, and where the two proportions intersect.', | |
| 268 | + whatFor: 'Compare the price progress of a move with its time progress and locate price-time confluence points.', | |
| 269 | + howToDraw: ['Open the Fibonacci flyout and pick Gann box.', '2 clicks: click the start of the swing (one corner), then its end (the opposite corner).', 'Horizontal and vertical levels at 25 / 38.2 / 50 / 61.8 / 75 % plus the diagonals appear inside the box.', 'Drag a corner to resize; use the fill option to shade the box.'], | |
| 270 | + readingTips: ['Price crossing the box diagonal from above means the move is now slower than "one unit per unit"; from below, faster.', 'Where a horizontal and a vertical division meet inside the box is a candidate turning point.', 'Extending the box to the right by its own width gives the next "square" — Gann expected reactions at its edges.'], | |
| 271 | + shortcut: null, | |
| 272 | + example: 'A 40-bar, 20-point rally boxed from 100 to 120: the 0.5 × 0.5 point sits at bar 20 / 110; price reaching 115 at bar 12 is ahead of the diagonal — a fast move.', | |
| 273 | + pairsWith: ['gann-fan', 'fib', 'date-price-range', 'rect'], | |
| 274 | + pitfalls: ['The grid produces many intersections; without a rule for which ones matter, any turn can be "explained".', 'The divisions are proportional to the box, so the box must be drawn on a complete swing.'], | |
| 275 | + history: 'William Delbert Gann, 1930s, price-time "squaring" methods.', | |
| 276 | + related: [{ label: 'Gann fan', href: T('gann-fan') }, { label: 'Date & price range', href: T('date-price-range') }], | |
| 277 | + }, | |
| 278 | + | |
| 279 | + /* ───────────────────────────── shapes & annotations ───────────────────────────── */ | |
| 280 | + { | |
| 281 | + id: 'rect', name: 'Rectangle', aka: ['Box', 'Zone'], group: 'shapes', points: 2, | |
| 282 | + oneLiner: 'A filled box between two corners — supply and demand zones, ranges, consolidations.', | |
| 283 | + summary: 'The rectangle marks an area rather than a line: a consolidation range, an order block, a gap or any zone where the market has reacted repeatedly. Because levels are really zones a few ticks deep, the rectangle is often more honest than a horizontal line.', | |
| 284 | + whatFor: 'Highlight a price zone or a consolidation so its edges can be watched for breakouts and retests.', | |
| 285 | + howToDraw: ['Activate the tool (R).', '2 clicks (or click-and-drag): click one corner, then the opposite corner.', 'Set the fill opacity, colour and an optional text label in the properties bar.', 'Extend the right edge into the future by dragging the right-hand handle.'], | |
| 286 | + readingTips: ['The longer price stays inside a box, the more energy the breakout releases: measure the box height and project it from the break.', 'A retest of the box edge from outside that holds confirms the breakout; a return inside cancels it.', 'Zones drawn around the last consolidation before an impulse (the "base") tend to be defended on the first return.'], | |
| 287 | + shortcut: 'R', | |
| 288 | + example: 'A three-week range from 48.50 to 51.00 (2.50 high) breaks upward at 51.20; the measured target is 53.50 and the retest of 51.00 is the entry.', | |
| 289 | + pairsWith: ['hline', 'date-price-range', 'volume-profile', 'donchian'], | |
| 290 | + pitfalls: ['Boxes with 100 % opacity hide the candles inside them — keep the fill light.', 'Drawing the box on wicks versus bodies changes the edge by a few ticks; be consistent.'], | |
| 291 | + history: GENERIC, | |
| 292 | + related: [{ label: 'Date & price range', href: T('date-price-range') }, { label: 'Volume Profile', href: I('volume-profile') }], | |
| 293 | + }, | |
| 294 | + { | |
| 295 | + id: 'ellipse', name: 'Ellipse', aka: ['Circle', 'Oval'], group: 'shapes', points: 2, | |
| 296 | + oneLiner: 'An oval inside a bounding box — circles a pattern, a spike or a cluster of bars.', | |
| 297 | + summary: 'The ellipse fills the box defined by its two corners and is the natural way to circle something on a chart: a rounded top, a volume climax, a cluster of candles, a divergence. It carries no analytical rule of its own; it directs the eye.', | |
| 298 | + whatFor: 'Draw attention to a region of the chart in a review, a screenshot or a shared link.', | |
| 299 | + howToDraw: ['Activate the tool (E).', '2 clicks (or drag): click one corner of the bounding box, then the opposite corner; the ellipse fills the box.', 'Adjust fill opacity and colour in the properties bar.'], | |
| 300 | + readingTips: ['Use one colour for what worked and another for what failed when reviewing trades.', 'A wide, flat ellipse suits a rounded bottom; a tall one suits a spike.', 'Keep the fill light so the candles inside stay legible.'], | |
| 301 | + shortcut: 'E', | |
| 302 | + example: 'Circling the five bars around the 12 June low with a 15 % green fill makes the volume climax and the reversal candle obvious in the shared screenshot.', | |
| 303 | + pairsWith: ['rect', 'callout', 'arrow'], | |
| 304 | + pitfalls: ['An ellipse is not a channel or a cycle tool — it does not imply any price or time projection.', 'Like every shape it is scaled with the axes: zooming changes its proportions on screen.'], | |
| 305 | + history: GENERIC, | |
| 306 | + related: [{ label: 'Rectangle', href: T('rect') }, { label: 'Callout', href: T('callout') }], | |
| 307 | + }, | |
| 308 | + { | |
| 309 | + id: 'triangle', name: 'Triangle', aka: ['Wedge', 'Pennant shape'], group: 'shapes', points: 3, | |
| 310 | + oneLiner: 'A three-point polygon — outlines triangles, wedges and pennants explicitly.', | |
| 311 | + summary: 'The triangle tool draws a closed polygon through three points, which is exactly the shape of the classic consolidation patterns: symmetrical, ascending and descending triangles, wedges and pennants. Drawing the figure itself makes the apex — and the deadline it implies — visible.', | |
| 312 | + whatFor: 'Outline a converging pattern and show where its two sides meet in time.', | |
| 313 | + howToDraw: ['Open the Shapes flyout and pick Triangle.', '3 clicks: click the first vertex, the second and the third; the polygon closes automatically.', 'Typical use: the first swing high, the first swing low, then the apex where the two converging lines meet.', 'Set a fill to shade the pattern; drag a vertex to adjust.'], | |
| 314 | + readingTips: ['Breakouts usually happen between one half and three quarters of the way to the apex; a break at the apex itself is weak.', 'The measured target is the height of the pattern at its widest point, projected from the breakout.', 'Volume typically dries up inside the triangle and expands on the break.'], | |
| 315 | + shortcut: null, | |
| 316 | + example: 'A symmetrical triangle 8 points tall at its base breaks up at 66 after covering 60 % of the distance to the apex: the measured target is 74.', | |
| 317 | + pairsWith: ['trendline', 'measure', 'volume-ma', 'bb-width'], | |
| 318 | + pitfalls: ['A triangle drawn to fit only two touches on each side is guesswork; wait for at least two clear swings per side.', 'Triangles and wedges have different implications: a rising wedge in an uptrend is bearish — the tool draws both the same way.'], | |
| 319 | + history: 'Chart patterns catalogued by Richard Schabacker (1932) and Edwards & Magee (1948).', | |
| 320 | + related: [{ label: 'Trend line', href: T('trendline') }, { label: 'Head & shoulders', href: T('head-shoulders') }, { label: 'Bollinger Bandwidth', href: I('bb-width') }], | |
| 321 | + }, | |
| 322 | + { | |
| 323 | + id: 'path', name: 'Path', aka: ['Polyline', 'Multi-segment line'], group: 'shapes', points: Infinity, | |
| 324 | + oneLiner: 'A connected series of straight segments — trace a wave count, a route or a scenario.', | |
| 325 | + summary: 'The path joins any number of points with straight segments, one click per vertex. It is the free-form tool for sketching a projected scenario, connecting a sequence of pivots without the constraints of a pattern tool, or tracing the route price took through a busy area.', | |
| 326 | + whatFor: 'Sketch an expected path or connect a custom sequence of swing points.', | |
| 327 | + howToDraw: ['Open the Shapes flyout and pick Path.', 'One click per vertex; the segment follows the pointer until the next click.', 'Finish with a double-click, by clicking the last point again, or by pressing Enter.', 'Drag any vertex afterwards to reshape; add a label in the properties bar.'], | |
| 328 | + readingTips: ['Draw the scenario you expect into the empty space to the right, then let the market grade you.', 'Use it to trace alternative wave counts next to the formal Elliott tools.', 'Keep paths short: beyond eight or ten vertices the sketch becomes noise.'], | |
| 329 | + shortcut: null, | |
| 330 | + example: 'Four clicks from the 98 low through 104, back to 101 and up to 110 sketch the expected zigzag; the actual high two weeks later was 109.60.', | |
| 331 | + pairsWith: ['elliott-impulse', 'brush', 'zigzag', 'arrow'], | |
| 332 | + pitfalls: ['Forgetting to finish the path (double-click / Enter) leaves the tool armed and the next click adds another point.', 'Vertices snap to bar centres and, in magnet mode, to O/H/L/C — turn magnet off for a free sketch.'], | |
| 333 | + history: GENERIC, | |
| 334 | + related: [{ label: 'Brush', href: T('brush') }, { label: 'ZigZag', href: I('zigzag') }], | |
| 335 | + }, | |
| 336 | + { | |
| 337 | + id: 'brush', name: 'Brush', aka: ['Freehand', 'Pen'], group: 'shapes', points: Infinity, | |
| 338 | + oneLiner: 'Freehand drawing that follows the pointer — annotate like on paper.', | |
| 339 | + summary: 'The brush records the pointer\'s trajectory while you drag and stores it as a free-form drawing anchored to price and time. It is the tool for quick circling, underlining and handwritten marks during a live session or a review, when precision matters less than speed.', | |
| 340 | + whatFor: 'Make fast, informal annotations without choosing a geometric tool.', | |
| 341 | + howToDraw: ['Activate the tool (B).', 'Press and drag across the chart; the stroke ends when you release the pointer.', 'Set width and colour before drawing (they apply to the next stroke) or afterwards in the properties bar.'], | |
| 342 | + readingTips: ['Use a distinct colour for live notes so they can be deleted in one pass later.', 'The stroke scales with the chart: what looked like a neat circle at one zoom is a smear at another.', 'Prefer the ellipse or rectangle for anything you intend to share.'], | |
| 343 | + shortcut: 'B', | |
| 344 | + example: 'A quick red loop around the three failed breakout candles at 71.20 during the session, deleted after the post-market review.', | |
| 345 | + pairsWith: ['path', 'ellipse', 'text'], | |
| 346 | + pitfalls: ['Each stroke stores many points; hundreds of brush strokes slow down saving and rendering.', 'Brush strokes are saved per symbol and timeframe like every drawing — they will reappear on the next visit.'], | |
| 347 | + history: GENERIC, | |
| 348 | + related: [{ label: 'Path', href: T('path') }, { label: 'Ellipse', href: T('ellipse') }], | |
| 349 | + }, | |
| 350 | + { | |
| 351 | + id: 'arrow', name: 'Arrow', aka: ['Arrow line', 'Pointer'], group: 'shapes', points: 2, | |
| 352 | + oneLiner: 'A line with an arrowhead — points from a cause to an effect, or marks a direction.', | |
| 353 | + summary: 'The arrow is a two-point line ending in an arrowhead. It connects a comment to the bar it refers to, shows the expected direction of a move, or links a signal on an indicator pane to the price reaction. It is the annotation primitive used most in shared charts.', | |
| 354 | + whatFor: 'Point at something or show the direction of an expected move.', | |
| 355 | + howToDraw: ['Activate the tool (A).', '2 clicks (or drag): click the tail, then the tip where the arrowhead goes.', 'Change colour, width and dash in the properties bar; add a text label if needed.'], | |
| 356 | + readingTips: ['Point the arrow from the evidence (divergence, volume spike) to the consequence (the reversal bar).', 'A sloping arrow drawn into empty space to the right is an explicit forecast — date it with a text note.', 'Keep arrows short; long diagonal arrows across the chart are hard to read.'], | |
| 357 | + shortcut: 'A', | |
| 358 | + example: 'An arrow from the RSI trough at 24 up to the hammer candle at 93.10 shows the divergence that preceded a 6 % rally.', | |
| 359 | + pairsWith: ['text', 'callout', 'arrow-up', 'arrow-down'], | |
| 360 | + pitfalls: ['Arrows anchored to price and time move with the chart: an arrow meant as a pure annotation can end up pointing at the wrong bar after a resize of the price axis.', 'Too many arrows say nothing — prefer one arrow and one sentence.'], | |
| 361 | + history: GENERIC, | |
| 362 | + related: [{ label: 'Arrow up marker', href: T('arrow-up') }, { label: 'Callout', href: T('callout') }], | |
| 363 | + }, | |
| 364 | + { | |
| 365 | + id: 'arrow-up', name: 'Arrow up marker', aka: ['Buy marker', 'Up arrow'], group: 'shapes', points: 1, | |
| 366 | + oneLiner: 'A small upward arrow under a bar — the standard mark for a long entry or a bullish signal.', | |
| 367 | + summary: 'The arrow-up marker sits at a single point and draws a compact upward arrow with an optional label. It is the conventional way to mark a buy, a long entry or a bullish signal on a specific bar, and it stays legible at any zoom because its size is fixed in pixels.', | |
| 368 | + whatFor: 'Tag a specific bar as a buy, a long entry or a bullish event.', | |
| 369 | + howToDraw: ['Open the Shapes flyout and pick Arrow up marker.', '1 click at the bar and price (usually just under the low) where the marker goes.', 'Type the label ("Buy", "Long 100") in the properties bar or by double-clicking the marker.'], | |
| 370 | + readingTips: ['Place markers at the actual fill price, not at the low of the bar, when journaling real trades.', 'Combine an up marker with a down marker later to read the trade at a glance.', 'Colour the markers by outcome when reviewing a series of trades.'], | |
| 371 | + shortcut: null, | |
| 372 | + example: 'A green up arrow labelled "Long 100 @ 45.30" under the breakout candle documents the entry for the weekly review.', | |
| 373 | + pairsWith: ['arrow-down', 'long', 'text', 'flag'], | |
| 374 | + pitfalls: ['A marker is a note, not a position tool — use the long position tool when you want targets, stops and R:R.', 'Markers drawn on a lower timeframe reappear on higher ones at the nearest bar and may overlap.'], | |
| 375 | + history: GENERIC, | |
| 376 | + related: [{ label: 'Arrow down marker', href: T('arrow-down') }, { label: 'Long position', href: T('long') }], | |
| 377 | + }, | |
| 378 | + { | |
| 379 | + id: 'arrow-down', name: 'Arrow down marker', aka: ['Sell marker', 'Down arrow'], group: 'shapes', points: 1, | |
| 380 | + oneLiner: 'A small downward arrow above a bar — the standard mark for a sell, exit or bearish signal.', | |
| 381 | + summary: 'The arrow-down marker is the mirror of the up marker: a compact downward arrow with an optional label placed above a bar. It marks a sell, a short entry, a long exit or a bearish signal, and keeps a fixed pixel size at any zoom level.', | |
| 382 | + whatFor: 'Tag a specific bar as a sell, a short entry, an exit or a bearish event.', | |
| 383 | + howToDraw: ['Open the Shapes flyout and pick Arrow down marker.', '1 click at the bar and price (usually just above the high) where the marker goes.', 'Type the label ("Sell", "Exit") in the properties bar or by double-clicking the marker.'], | |
| 384 | + readingTips: ['Pair each down marker with the up marker it closes to see the holding period and the result.', 'Use it to flag bearish signals (lower high, failed breakout) even when no trade was taken.', 'Keep labels short: two or three words survive at small zoom levels.'], | |
| 385 | + shortcut: null, | |
| 386 | + example: 'A red down arrow labelled "Exit +4.2 %" above the 3 May high closes the trade opened at the green arrow three weeks earlier.', | |
| 387 | + pairsWith: ['arrow-up', 'short', 'text', 'flag'], | |
| 388 | + pitfalls: ['Markers are notes without price logic; the short position tool computes targets, stops and R:R.', 'Overlapping markers on consecutive bars become unreadable — use one and a callout.'], | |
| 389 | + history: GENERIC, | |
| 390 | + related: [{ label: 'Arrow up marker', href: T('arrow-up') }, { label: 'Short position', href: T('short') }], | |
| 391 | + }, | |
| 392 | + | |
| 393 | + /* ───────────────────────────── text & notes ───────────────────────────── */ | |
| 394 | + { | |
| 395 | + id: 'text', name: 'Text', aka: ['Note', 'Label'], group: 'text', points: 1, | |
| 396 | + oneLiner: 'A text note anchored to a bar and a price — comment directly on the chart.', | |
| 397 | + summary: 'The text tool places a free note at a point of the chart. The note is anchored in price and time, so it stays with the bar it describes when you scroll or zoom, and it is saved with the other drawings of the symbol and timeframe. Double-clicking it reopens the editor.', | |
| 398 | + whatFor: 'Write a comment, a thesis or a reminder next to the bars it refers to.', | |
| 399 | + howToDraw: ['Activate the tool (X).', '1 click where the text should be anchored; an inline editor opens.', 'Type the note, press Enter to confirm or Escape to cancel.', 'Double-click the text later to edit it; change size and colour in the properties bar.'], | |
| 400 | + readingTips: ['Date your theses ("7 Sep: expecting a retest of 150") so they can be graded honestly later.', 'Put the note above the high or below the low so it does not cover candles.', 'One sentence per note; long paragraphs belong in a callout.'], | |
| 401 | + shortcut: 'X', | |
| 402 | + example: 'Anchored above the 1 August high: "Failed breakout on half the volume — short below 128.40, stop 130.10".', | |
| 403 | + pairsWith: ['callout', 'arrow', 'hline', 'flag'], | |
| 404 | + pitfalls: ['Text scales with nothing: at a distant zoom several notes overlap and cover the price action.', 'A note anchored to a price on the main pane moves with the price axis; anchor it in empty space above or below the bars.'], | |
| 405 | + history: GENERIC, | |
| 406 | + related: [{ label: 'Callout', href: T('callout') }, { label: 'Price label', href: T('price-label') }], | |
| 407 | + }, | |
| 408 | + { | |
| 409 | + id: 'callout', name: 'Callout', aka: ['Speech bubble', 'Annotation box'], group: 'text', points: 2, | |
| 410 | + oneLiner: 'A text box with a pointer to the bar it explains — annotation that stays out of the way.', | |
| 411 | + summary: 'The callout combines a multi-line text box with a leader line to an anchor point. The anchor marks what you are talking about; the box sits wherever there is room. It is the right tool for explanations longer than a label and for shared charts that must be understood without you.', | |
| 412 | + whatFor: 'Explain a specific bar or pattern with several lines of text without covering it.', | |
| 413 | + howToDraw: ['Open the Text flyout and pick Callout.', '2 clicks: click the anchor (the bar or price being explained), then click where the box should sit.', 'Type the text in the inline editor (Shift+Enter for a new line), Enter to confirm.', 'Drag the box or the anchor independently afterwards.'], | |
| 414 | + readingTips: ['Place the box in empty space (above a downtrend, below an uptrend) and let the leader do the pointing.', 'Structure the text: what happened, why it matters, what would invalidate it.', 'Use one callout per idea; several short callouts read better than one long one.'], | |
| 415 | + shortcut: null, | |
| 416 | + example: 'Anchor on the swing high at 212.80, box in the upper-left: "Swing high — watch 0.618 at 204.40 for the retest; invalidated above 213".', | |
| 417 | + pairsWith: ['text', 'arrow', 'fib', 'rect'], | |
| 418 | + pitfalls: ['The box is anchored in price and time too: after a big move it may end up far from the anchor — drag it back.', 'Long texts make tall boxes that hide the chart on small screens.'], | |
| 419 | + history: GENERIC, | |
| 420 | + related: [{ label: 'Text', href: T('text') }, { label: 'Arrow', href: T('arrow') }], | |
| 421 | + }, | |
| 422 | + { | |
| 423 | + id: 'price-label', name: 'Price label', aka: ['Price tag', 'Price flag'], group: 'text', points: 1, | |
| 424 | + oneLiner: 'A tag showing the exact price at the click — labels a level or a fill without a line.', | |
| 425 | + summary: 'The price label prints the price of its anchor point in a small tag, optionally with your own text. It records an exact number — a fill, a high, a level — on the chart without drawing a line across it, which keeps the picture clean.', | |
| 426 | + whatFor: 'Stamp an exact price on the chart: entries, exits, swing extremes, alert levels.', | |
| 427 | + howToDraw: ['Open the Text flyout and pick Price label.', '1 click at the bar and price to tag; the label shows the price.', 'Add a text prefix ("Entry", "Stop") in the properties bar or by double-clicking.', 'Use magnet mode to land exactly on a high, a low or a close.'], | |
| 428 | + readingTips: ['Tag swing highs and lows with labels and the chart becomes a readable list of the levels that matter.', 'Labelled fills make a journal: entry, stop and exit tags tell the whole trade.', 'Delete labels when the level breaks — stale tags are worse than none.'], | |
| 429 | + shortcut: null, | |
| 430 | + example: '"Stop 118.40" tagged just under the 22 May low, next to "Entry 121.15" on the breakout bar — the risk per share is visible instantly.', | |
| 431 | + pairsWith: ['hray', 'text', 'long', 'measure'], | |
| 432 | + pitfalls: ['The label shows the anchor price with the chart\'s decimals, so magnet mode matters for precision.', 'Many labels on nearby prices overlap; use a horizontal ray with a label instead.'], | |
| 433 | + history: GENERIC, | |
| 434 | + related: [{ label: 'Horizontal ray', href: T('hray') }, { label: 'Flag', href: T('flag') }], | |
| 435 | + }, | |
| 436 | + { | |
| 437 | + id: 'flag', name: 'Flag', aka: ['Marker flag', 'Bookmark'], group: 'text', points: 1, | |
| 438 | + oneLiner: 'A small flag pinned to a bar — bookmark an event or a signal.', | |
| 439 | + summary: 'The flag is a compact marker with a short label, planted on a bar. It is meant for events and bookmarks — an earnings release, a news item, a signal fired by a system — rather than for analysis. Its fixed pixel size keeps it visible at any zoom level.', | |
| 440 | + whatFor: 'Bookmark a bar with a one-word label so it can be found again quickly.', | |
| 441 | + howToDraw: ['Open the Text flyout and pick Flag.', '1 click at the bar and price where the flag is planted.', 'Type the label (one or two words) in the properties bar or by double-clicking.'], | |
| 442 | + readingTips: ['Flag every scheduled event (earnings, CPI, FOMC) on the daily chart and the reaction becomes a pattern you can study.', 'Use the same colour for the same kind of event.', 'Flags on an intraday chart at the session open show how each day started.'], | |
| 443 | + shortcut: null, | |
| 444 | + example: 'A yellow flag labelled "Earnings" above the 25 July bar; the 8 % gap the next morning is now explained in every future screenshot.', | |
| 445 | + pairsWith: ['vline', 'text', 'arrow-up', 'price-label'], | |
| 446 | + pitfalls: ['Flags are drawings: they live per symbol and timeframe in your browser, not in a shared calendar.', 'A dense cluster of flags overlaps; prefer a vertical range for a multi-bar event.'], | |
| 447 | + history: GENERIC, | |
| 448 | + related: [{ label: 'Vertical line', href: T('vline') }, { label: 'Price label', href: T('price-label') }], | |
| 449 | + }, | |
| 450 | + | |
| 451 | + /* ───────────────────────────── measure & positions ───────────────────────────── */ | |
| 452 | + { | |
| 453 | + id: 'measure', name: 'Measure', aka: ['Ruler', 'Price-time ruler'], group: 'measure', points: 2, | |
| 454 | + oneLiner: 'Δ price, Δ %, bars and duration between two points — the chart\'s ruler.', | |
| 455 | + summary: 'The measure tool stretches a box between two points and reports the difference in price and in percent, the number of bars and the elapsed time. It is the quickest way to size a move, a stop distance or a consolidation without leaving the chart.', | |
| 456 | + whatFor: 'Quantify a move or a distance in price, percent, bars and time.', | |
| 457 | + howToDraw: ['Activate the tool (M).', '2 clicks (or drag): click the start point, then the end point; the box shows Δ price, Δ %, bars and duration.', 'Drag either corner to remeasure; delete it when done or keep it as a record.'], | |
| 458 | + readingTips: ['Measure the last impulse and project the same distance from the pullback low: that is the simplest target method.', 'Measure the stop distance in percent, then size the position from it.', 'Compare the bars count of rallies and pullbacks: healthy trends rise for longer than they retrace.'], | |
| 459 | + shortcut: 'M', | |
| 460 | + example: 'From the 98.20 low to the 106.90 high: +8.70 (+8.86 %) in 14 bars, 3 weeks — projected from the 103.10 pullback the next target is 111.80.', | |
| 461 | + pairsWith: ['price-range', 'date-range', 'fib-extension', 'atr'], | |
| 462 | + pitfalls: ['The bars count follows the indexed time axis: on intraday charts it excludes overnight gaps, on daily charts weekends.', 'Percent is computed from the first point; measuring top-down and bottom-up gives different percentages for the same move.'], | |
| 463 | + history: GENERIC, | |
| 464 | + related: [{ label: 'Price range', href: T('price-range') }, { label: 'Date range', href: T('date-range') }], | |
| 465 | + }, | |
| 466 | + { | |
| 467 | + id: 'price-range', name: 'Price range', aka: ['Vertical measure', 'Price distance'], group: 'measure', points: 2, | |
| 468 | + oneLiner: 'Δ price and Δ % between two prices — a vertical ruler with the time left out.', | |
| 469 | + summary: 'The price range measures the vertical distance between two points and prints the difference in price and percent. It ignores the horizontal component, so it is the clean way to state how big a move, a gap or a stop is, without the box of the full measure tool.', | |
| 470 | + whatFor: 'State the size of a move or a risk in price and percent.', | |
| 471 | + howToDraw: ['Open the Measure flyout and pick Price range.', '2 clicks: click the first price, then the second; the vertical span and its labels appear.', 'Drag either end to adjust; the label updates live.'], | |
| 472 | + readingTips: ['Measure the height of a range or pattern, then use the same tool to project it from the breakout.', 'Compare the percent size of successive swings: shrinking swings mean a tightening market.', 'A price range from entry to stop is your risk unit (1R); targets are multiples of it.'], | |
| 473 | + shortcut: null, | |
| 474 | + example: 'From 121.15 to 118.40 the range reads −2.75 (−2.27 %): with a 1 % account risk that fixes the position size.', | |
| 475 | + pairsWith: ['measure', 'date-price-range', 'long', 'atr'], | |
| 476 | + pitfalls: ['Percent is relative to the first click; be consistent about which point you click first.', 'On an indicator pane the tool measures pane values, not prices.'], | |
| 477 | + history: GENERIC, | |
| 478 | + related: [{ label: 'Measure', href: T('measure') }, { label: 'Date & price range', href: T('date-price-range') }], | |
| 479 | + }, | |
| 480 | + { | |
| 481 | + id: 'date-range', name: 'Date range', aka: ['Time measure', 'Bar counter'], group: 'measure', points: 2, | |
| 482 | + oneLiner: 'Bars and elapsed time between two dates — how long did it take?', | |
| 483 | + summary: 'The date range measures the horizontal distance between two bars and reports the number of bars and the elapsed time. It answers the timing questions: how long a consolidation has lasted, how many bars a rally took, how far the next Fibonacci time zone is.', | |
| 484 | + whatFor: 'Count bars and time between two moments on the chart.', | |
| 485 | + howToDraw: ['Open the Measure flyout and pick Date range.', '2 clicks: click the first bar, then the second; the horizontal span shows bars and duration.', 'Drag either end to adjust.'], | |
| 486 | + readingTips: ['Consolidations that last longer than the impulse before them often resolve against the trend.', 'Count the bars of past cycles and project the count forward as a time window.', 'Compare the duration of up and down legs: time asymmetry reveals who is in control.'], | |
| 487 | + shortcut: null, | |
| 488 | + example: 'The 12 → 26 June consolidation spans 11 bars, 2 weeks — shorter than the 19-bar rally that preceded it, consistent with a continuation.', | |
| 489 | + pairsWith: ['measure', 'fib-timezones', 'vrange', 'vline'], | |
| 490 | + pitfalls: ['Bars are counted on the indexed axis: session gaps and weekends do not count as bars.', 'Duration is wall-clock time between the two bar stamps, so a 1-minute chart spanning a weekend shows a long duration for few bars.'], | |
| 491 | + history: GENERIC, | |
| 492 | + related: [{ label: 'Fibonacci time zones', href: T('fib-timezones') }, { label: 'Vertical range', href: T('vrange') }], | |
| 493 | + }, | |
| 494 | + { | |
| 495 | + id: 'date-price-range', name: 'Date & price range', aka: ['Box measure', 'Range box'], group: 'measure', points: 2, | |
| 496 | + oneLiner: 'A box with Δ price, Δ %, bars and duration — measures a swing in both dimensions.', | |
| 497 | + summary: 'The date & price range draws a box between two corners and labels it with the price change, the percent change, the number of bars and the elapsed time. It is the measure tool kept as a persistent, shaded annotation — ideal for documenting swings in a review.', | |
| 498 | + whatFor: 'Document a swing or a consolidation with its full price and time statistics.', | |
| 499 | + howToDraw: ['Open the Measure flyout and pick Date & price range.', '2 clicks (or drag): click one corner (start bar, start price), then the opposite corner.', 'Adjust fill and colour in the properties bar; drag corners to refine.'], | |
| 500 | + readingTips: ['Box each swing of a trend and read the series: bigger boxes, same duration means acceleration.', 'A consolidation box tells you both the breakout target (height) and how long the market has been coiling.', 'Keep the boxes as a journal of the move\'s structure.'], | |
| 501 | + shortcut: null, | |
| 502 | + example: 'From 3 to 21 March, 98.20 → 106.90: +8.70 (+8.86 %) over 14 bars — the box stays on the chart as the reference impulse.', | |
| 503 | + pairsWith: ['measure', 'rect', 'gann-box', 'fib-extension'], | |
| 504 | + pitfalls: ['The box is anchored to two points in time, so extending it into the future means the "end" is a bar that does not exist yet.', 'Percent is computed from the first corner.'], | |
| 505 | + history: GENERIC, | |
| 506 | + related: [{ label: 'Measure', href: T('measure') }, { label: 'Rectangle', href: T('rect') }, { label: 'Gann box', href: T('gann-box') }], | |
| 507 | + }, | |
| 508 | + { | |
| 509 | + id: 'vrange', name: 'Vertical range', aka: ['Time band', 'Session highlight'], group: 'measure', points: 2, | |
| 510 | + oneLiner: 'A shaded band between two bars — highlights a session, an event window or a phase.', | |
| 511 | + summary: 'The vertical range shades the full height of the chart between two bars and can carry a label. It marks a period rather than an instant: an earnings week, a central-bank window, a consolidation phase, a holiday session. It reads across every pane at once.', | |
| 512 | + whatFor: 'Highlight a period of time so its behaviour can be compared with what came before and after.', | |
| 513 | + howToDraw: ['Open the Measure flyout and pick Vertical range.', '2 clicks: click the first bar, then the last bar of the period; the band fills the chart height.', 'Type a label ("FOMC", "Earnings week") and set the fill opacity in the properties bar.'], | |
| 514 | + readingTips: ['Shade every FOMC day and the average reaction becomes visible after a few months.', 'Use a band for the accumulation phase before a breakout; the band width is the coil time.', 'Bands survive zooming better than vertical lines because they keep their area.'], | |
| 515 | + shortcut: null, | |
| 516 | + example: 'A grey band labelled "FOMC" over the 17–18 June bars shows the 2.3 % range of the announcement and the drift that followed.', | |
| 517 | + pairsWith: ['vline', 'date-range', 'flag', 'hv'], | |
| 518 | + pitfalls: ['A band across a whole chart hides candles if the fill is too strong — keep it under 20 %.', 'The band edges snap to bar centres, so a period spanning a session gap looks continuous.'], | |
| 519 | + history: GENERIC, | |
| 520 | + related: [{ label: 'Vertical line', href: T('vline') }, { label: 'Date range', href: T('date-range') }], | |
| 521 | + }, | |
| 522 | + { | |
| 523 | + id: 'long', name: 'Long position', aka: ['Long trade', 'Buy setup'], group: 'measure', points: 3, | |
| 524 | + oneLiner: 'Entry, target and stop of a long trade with the risk:reward — planned before the click.', | |
| 525 | + summary: 'The long position tool draws the anatomy of a buy: an entry line, a profit zone up to the target and a loss zone down to the stop, with the target and stop expressed in percent and points and the resulting risk:reward ratio. A quantity turns the zones into money amounts.', | |
| 526 | + whatFor: 'Plan and size a long trade on the chart, and see immediately whether the reward justifies the risk.', | |
| 527 | + howToDraw: ['Activate the tool (L).', '1 click at the entry price and bar; the engine creates the target (+2 %) and the stop (−1 %) 20 bars ahead.', 'Drag the target handle up or down and the stop handle to the invalidation level; the R:R updates live.', 'Set the quantity (style.qty) in the properties bar to see profit and loss in currency.'], | |
| 528 | + readingTips: ['Put the stop where the idea is wrong (below the swing low), then check whether the target still gives at least 2:1.', 'Aim the target at a real level — a prior high, a Fib extension, a channel rail — not at a round R multiple.', 'If the R:R only works with a stop inside the noise (less than one ATR), the trade is too small for the setup.', 'Keep the box on the chart after the trade to review what actually happened.'], | |
| 529 | + shortcut: 'L', | |
| 530 | + example: 'Entry 121.15, stop 118.40 (−2.27 %), target 128.90 (+6.40 %): R:R 2.8 — with 100 shares the risk is 275 and the potential 775.', | |
| 531 | + pairsWith: ['short', 'fib-extension', 'atr', 'price-label'], | |
| 532 | + pitfalls: ['The default ±2 %/−1 % zones are placeholders — always move them to real levels before judging the R:R.', 'Slippage and fees are not included; a 1.5:1 setup on paper can be 1.2:1 in practice.', 'Percent and points are measured from the entry line, so moving the entry moves both.'], | |
| 533 | + history: 'Position-sizing tools appeared with electronic charting platforms in the 2000s; the R-multiple framing comes from Van K. Tharp.', | |
| 534 | + related: [{ label: 'Short position', href: T('short') }, { label: 'ATR', href: I('atr') }, { label: 'Trend-based Fib extension', href: T('fib-extension') }], | |
| 535 | + }, | |
| 536 | + { | |
| 537 | + id: 'short', name: 'Short position', aka: ['Short trade', 'Sell setup'], group: 'measure', points: 3, | |
| 538 | + oneLiner: 'Entry, target and stop of a short trade with the risk:reward — mirror of the long tool.', | |
| 539 | + summary: 'The short position tool draws a sell setup: an entry line, a profit zone down to the target and a loss zone up to the stop, with both distances in percent and points and the risk:reward ratio. With a quantity set, the zones show the money at stake.', | |
| 540 | + whatFor: 'Plan and size a short trade and check its reward against its risk before entering.', | |
| 541 | + howToDraw: ['Activate the tool (S).', '1 click at the entry price and bar; the engine creates the target (−2 %) and the stop (+1 %) 20 bars ahead.', 'Drag the target handle down to the objective and the stop handle above the invalidation level; the R:R updates live.', 'Set the quantity (style.qty) in the properties bar for amounts in currency.'], | |
| 542 | + readingTips: ['Place the stop above the swing high that would prove the short wrong, not at a fixed percent.', 'Shorts often run faster than longs — a tight target leaves money on the table; trail instead.', 'Check the R:R after placing the stop; below 1.5:1 the setup rarely pays for its losers.'], | |
| 543 | + shortcut: 'S', | |
| 544 | + example: 'Entry 128.40, stop 130.10 (+1.32 %), target 121.20 (−5.61 %): R:R 4.2 — the failed-breakout short is worth taking even with a 50 % hit rate.', | |
| 545 | + pairsWith: ['long', 'fib', 'atr', 'price-label'], | |
| 546 | + pitfalls: ['The default zones are placeholders; drag them to real levels before reading the ratio.', 'Borrow costs, dividends and short-squeeze gaps are not modelled.', 'Moving the entry line moves both zones with it.'], | |
| 547 | + history: 'Position-sizing tools appeared with electronic charting platforms in the 2000s; the R-multiple framing comes from Van K. Tharp.', | |
| 548 | + related: [{ label: 'Long position', href: T('long') }, { label: 'ATR', href: I('atr') }], | |
| 549 | + }, | |
| 550 | + | |
| 551 | + /* ───────────────────────────── patterns ───────────────────────────── */ | |
| 552 | + { | |
| 553 | + id: 'elliott-impulse', name: 'Elliott impulse wave (12345)', aka: ['Impulse wave', 'Five-wave motive'], group: 'patterns', points: 6, | |
| 554 | + oneLiner: 'Labels a five-wave impulse 0–5 — three motive legs and two corrections with the trend.', | |
| 555 | + summary: 'The Elliott impulse tool connects six pivots and labels them 0 to 5, describing the classic five-wave motive structure: waves 1, 3 and 5 move with the trend, waves 2 and 4 correct against it. Drawing it forces the count to obey the rules (wave 2 never retraces all of wave 1, wave 3 is never the shortest, wave 4 does not overlap wave 1).', | |
| 556 | + whatFor: 'Lay out a wave count on the chart and check it against Elliott\'s rules and guidelines.', | |
| 557 | + howToDraw: ['Open the Patterns flyout and pick Elliott impulse wave.', '6 clicks: click the origin (0), then the end of each wave in order (1, 2, 3, 4, 5).', 'Use magnet mode so each label sits on the exact wick extreme.', 'Drag any pivot to recount; delete and redraw when the market invalidates the count.'], | |
| 558 | + readingTips: ['Wave 3 is usually the longest and steepest; if wave 3 is the shortest of 1, 3 and 5 the count is wrong.', 'Wave 2 tends to retrace 50–61.8 % of wave 1; wave 4 tends to be shallower (38.2 %) and to alternate in shape with wave 2.', 'Wave 5 often equals wave 1 or 61.8 % of waves 1–3; oscillator divergence at wave 5 is common.', 'After a completed impulse expect a three-wave correction (ABC) — draw it with the correction tool.'], | |
| 559 | + shortcut: null, | |
| 560 | + example: 'Pivots at 100, 112, 105, 130, 122, 138: wave 3 (25 points) is the longest, wave 2 retraced 58 % of wave 1 and wave 5 (16) is close to wave 1 (12) — a valid count.', | |
| 561 | + pairsWith: ['elliott-correction', 'fib-extension', 'fib', 'rsi'], | |
| 562 | + pitfalls: ['Wave counts are notoriously subjective: two analysts with the same chart often produce different degrees and labels.', 'A count that needs to be revised every few bars is describing noise, not structure.', 'The tool draws the lines; it does not verify the rules for you — check them.'], | |
| 563 | + history: 'Ralph Nelson Elliott, 1938, The Wave Principle; popularised by Frost & Prechter (1978).', | |
| 564 | + related: [{ label: 'Elliott correction wave', href: T('elliott-correction') }, { label: 'Trend-based Fib extension', href: T('fib-extension') }], | |
| 565 | + }, | |
| 566 | + { | |
| 567 | + id: 'elliott-correction', name: 'Elliott correction wave (ABC)', aka: ['Corrective wave', 'Three-wave correction', 'Zigzag ABC'], group: 'patterns', points: 4, | |
| 568 | + oneLiner: 'Labels a three-wave correction 0-A-B-C — the pause between two impulses.', | |
| 569 | + summary: 'The Elliott correction tool connects four pivots and labels them 0, A, B and C, the three-wave structure that corrects an impulse. Wave A moves against the prior trend, wave B retraces part of A, and wave C completes the correction, often ending near a Fibonacci relationship with A.', | |
| 570 | + whatFor: 'Frame a correction, anticipate where wave C ends and where the next impulse could start.', | |
| 571 | + howToDraw: ['Open the Patterns flyout and pick Elliott correction wave.', '4 clicks: click the end of the prior impulse (0), then the end of wave A, wave B and wave C.', 'Use magnet mode so the labels land on the pivots.', 'Combine with a Fib extension (A → B → C projection) to estimate the end of C before it prints.'], | |
| 572 | + readingTips: ['C usually equals A or 1.618 × A; a C much shorter than A hints at a truncated correction and a strong next impulse.', 'B retracing more than 100 % of A signals a flat or an expanded flat, not a zigzag.', 'Volume typically declines through the correction and returns on the new impulse.', 'The end of C is the highest-quality entry in the direction of the larger trend.'], | |
| 573 | + shortcut: null, | |
| 574 | + example: 'After an impulse to 138: A down to 126, B up to 133, C down to 121 — C (12) equals A (12) and lands near the 0.382 retracement of the whole impulse.', | |
| 575 | + pairsWith: ['elliott-impulse', 'fib-extension', 'fib', 'macd'], | |
| 576 | + pitfalls: ['Corrections come in many shapes (zigzag, flat, triangle, combination); this tool draws only the simple ABC.', 'Labelling a correction before C is complete is a forecast, not a count — say so on the chart.'], | |
| 577 | + history: 'Ralph Nelson Elliott, 1938, The Wave Principle.', | |
| 578 | + related: [{ label: 'Elliott impulse wave', href: T('elliott-impulse') }, { label: 'Fibonacci retracement', href: T('fib') }], | |
| 579 | + }, | |
| 580 | + { | |
| 581 | + id: 'xabcd', name: 'XABCD pattern', aka: ['Harmonic pattern', 'Gartley', 'Bat / Butterfly / Crab'], group: 'patterns', points: 5, | |
| 582 | + oneLiner: 'Five-point harmonic pattern with the AB/XA, BC/AB, CD/BC and AD/XA ratios printed live.', | |
| 583 | + summary: 'The XABCD tool connects five pivots and displays the Fibonacci ratios that define harmonic patterns: AB/XA, BC/AB, CD/BC and AD/XA. Each named pattern — Gartley, Bat, Butterfly, Crab — is a specific combination of these ratios, and point D is the potential reversal zone where the trade is taken.', | |
| 584 | + whatFor: 'Identify and validate a harmonic pattern from its ratios and locate the D-point reversal zone.', | |
| 585 | + howToDraw: ['Open the Patterns flyout and pick XABCD pattern.', '5 clicks: click X, A, B, C and D in order; the ratios are printed on the legs.', 'Use magnet mode for exact pivots; the ratios are sensitive to a few ticks.', 'Draw with D projected (the expected reversal zone) and adjust it when price arrives.'], | |
| 586 | + readingTips: ['Gartley: B ≈ 0.618 XA, D ≈ 0.786 XA. Bat: B 0.382–0.5, D ≈ 0.886. Butterfly: B 0.786, D 1.27–1.618. Crab: D ≈ 1.618 XA.', 'The D zone is only a candidate; wait for a reversal bar or an oscillator turn before acting.', 'The stop belongs just beyond X (for Gartley/Bat) or beyond the 1.618 extension (Butterfly/Crab).', 'Targets are the 0.382 and 0.618 retracements of the whole AD leg.'], | |
| 587 | + shortcut: null, | |
| 588 | + example: 'X 100, A 120, B 107.6 (AB/XA 0.62), C 115.3 (BC/AB 0.62), D 104.3 (AD/XA 0.785): a bullish Gartley — long near 104.3 with a stop under 100.', | |
| 589 | + pairsWith: ['fib', 'fib-extension', 'rsi', 'zigzag'], | |
| 590 | + pitfalls: ['Ratios that are "close enough" to a pattern are usually not the pattern — harmonic traders use tight tolerances.', 'Five free pivots can be made to fit almost any ratio; start from the swings, not from the desired pattern.', 'Harmonic patterns describe potential reversal zones, not guaranteed reversals.'], | |
| 591 | + history: 'H. M. Gartley, 1935, Profits in the Stock Market; ratio-based patterns formalised by Scott Carney in the 1990s.', | |
| 592 | + related: [{ label: 'Fibonacci retracement', href: T('fib') }, { label: 'ZigZag', href: I('zigzag') }], | |
| 593 | + }, | |
| 594 | + { | |
| 595 | + id: 'head-shoulders', name: 'Head & shoulders', aka: ['H&S', 'Inverse head and shoulders'], group: 'patterns', points: 7, | |
| 596 | + oneLiner: 'Seven-point reversal pattern: neckline through the two troughs, measured-move target.', | |
| 597 | + summary: 'The head & shoulders tool connects seven pivots — the run-up, the left shoulder, the first trough, the head, the second trough, the right shoulder and the breakdown — and draws the neckline through the two troughs (points 2 and 4). The distance from the head to the neckline, projected from the break, is the classic measured target. Drawn upside down it is the inverse pattern that ends downtrends.', | |
| 598 | + whatFor: 'Outline a topping or bottoming pattern, locate the neckline and derive the measured-move target.', | |
| 599 | + howToDraw: ['Open the Patterns flyout and pick Head & shoulders.', '7 clicks in order: the start of the move (1), the left shoulder (2), the first trough (3), the head (4), the second trough (5), the right shoulder (6) and the end/breakdown point (7).', 'The neckline is drawn through the two troughs; for an inverse pattern click the mirror points (lows for shoulders and head).', 'Use magnet mode so the shoulders and head sit on the wick extremes.'], | |
| 600 | + readingTips: ['The pattern is only complete when price closes beyond the neckline; before that it is a candidate.', 'Volume should be lower on the right shoulder than on the left and expand on the break.', 'The measured target is the head-to-neckline height projected from the break; a return to the neckline after the break is the classic retest entry.', 'A sloping neckline is fine; a steeply rising one weakens a top pattern.'], | |
| 601 | + shortcut: null, | |
| 602 | + example: 'Head at 140, neckline near 128: the 12-point height projected from the 127.50 break gives a target of 115.50, reached six weeks later.', | |
| 603 | + pairsWith: ['trendline', 'measure', 'volume-ma', 'rsi'], | |
| 604 | + pitfalls: ['Head & shoulders are seen everywhere in hindsight; the pattern needs a preceding trend to reverse.', 'Symmetry is not required, but shoulders at wildly different heights usually mean a different structure.', 'Trading the pattern before the neckline break is trading a guess.'], | |
| 605 | + history: 'Described by Richard Schabacker (1932) and codified by Edwards & Magee (1948), Technical Analysis of Stock Trends.', | |
| 606 | + related: [{ label: 'Triangle', href: T('triangle') }, { label: 'Trend line', href: T('trendline') }, { label: 'Volume', href: I('volume-ma') }], | |
| 607 | + }, | |
| 608 | +] | |
| 609 | + | |
| 610 | +const byId = Object.fromEntries(TOOL_ENTRIES.map(e => [e.id, e])) | |
| 611 | +export default byId | |
| 612 | ||