Quantum Class Reference
Missing is never zero — except where it says so
undefined. Some fields use a sentinel instead, and the tables below say which: -1 for a bar index or a structure level that does not exist, NaN for a ratio with no answer, and 0 for a session level that has not formed. Guard every read before you compare it.Globals
// Everything a strategy can reach, besides the standard JavaScript built-ins:
market // everything you READ
broker // everything you DO
log // log("…"), log.warn, log.error, log.plot
params // your resolved parameters
context // all of it in one object (the same as this.c)
console // console.log / .warn / .error route to the same logThere is no file, network or timer access, and no require or import. Older strategies use this.c (the same as context) and this.p (the same as params); both still work.
Strategy
The base class every strategy extends. The file's last line must be the class name, and the class must override at least one of signal, onBar or onTick.
Statics
| Static | Type | Meaning |
|---|---|---|
| label | string | Display name. Defaults to the class name. |
| barSeconds | number | The bar the rule is written for (60, 300, 900 …). 0 or absent = any. |
| params | object | Parameter declarations — see the table below. They become the settings panel. |
| group | string | Library category, read from the source text — so it must be a literal. // @group trend as the first line does the same. |
Parameter fields
| Field | Default | Meaning |
|---|---|---|
| def | required | The default value. A missing or NaN def is refused. |
| lo | 0 | Lower bound. Values are clamped into [lo, hi]. |
| hi | 1000 | Upper bound. hi < lo is refused as a typo. |
| real | false | true for a decimal; otherwise the value is rounded to a whole number. |
| label | the key | Control label. |
| desc | — | Shown under the control. Say why, not what. |
| group | — | Panel section. |
| hidden | false | Hides it from the panel; with lo == hi == def it is pinned. |
Methods you override
| Method | Returns | When it runs |
|---|---|---|
| signal() | 1, -1 or 0 | Once per closed bar, only while flat — consulted by the base onBar(). The base turns 1/-1 into a bracketed market order. |
| suggestedStop(isLong) | price or NaN | At entry. A price for the stop, or NaN to use params.stopTicks. |
| targetTicks(isLong) | ticks | At entry. Defaults to params.targetTicks (0 = no target). |
| onBar() | — | Every closed bar, flat or not. Call super.onBar() to keep signal() consulted. |
| onTick() | — | Every print, in a backtest on tick tapes only. market.bar(0) is still the last closed bar. |
| init() | — | Once per instance: once per day in a backtest, and on a chart each time it is armed or its settings change. |
| maintain() | — | Before onBar() every bar: applies break-even, then the trail. Override with maintain() {} to switch both off. |
Helpers on this
| Member | Returns | Meaning |
|---|---|---|
| this.ticksTo(price) | whole ticks ≥ 0 | Distance from the last price to a level. |
| this.trailStop(price) | true if it moved | Moves the resting stop, but only in the trade's favor. Always false in a backtest, which cannot amend a resting order. |
| this.breakEven(points, offsetTicks) | true if it moved | Moves the stop to entry ± offset once the trade is `points` ahead (points, not ticks). |
| this.c / this.context | context | The same object as the global context (and market). |
| this.p / this.params | object | The resolved parameters. |
class OpeningDrive extends Strategy {
static get label() { return "Opening drive" }
static get barSeconds() { return 300 }
static get params() {
return {
minRangeTicks: { def: 40, lo: 4, hi: 400, group: "Entry",
desc: "The first bar must be at least this wide." },
stopTicks: { def: 30, lo: 4, hi: 400, group: "Stops", desc: "Fallback stop." },
}
}
init() { this.tookTrade = false } // once per instance (per day in a backtest)
signal() {
const bar = market.bar(0)
if (bar.close === undefined || this.tookTrade) return 0
if (bar.nyMin !== 935) return 0 // only the 9:35 bar (the first RTH 5-minute bar)
if (market.toTicks(bar.high - bar.low) < params.minRangeTicks) return 0
this.tookTrade = true
return market.closeLocation(0) > 0.5 ? 1 : -1
}
suggestedStop(isLong) { // a PRICE, or NaN for params.stopTicks
const bar = market.bar(0)
return isLong ? bar.low - market.tickSize : bar.high + market.tickSize
}
}
OpeningDriveA throw from any method stops the run and is reported with its method and line, e.g. signal, line 14: …. On a chart it also disarms the strategy.
market
Everything a strategy reads. Its properties are read live, so they are always current. Bar arguments are barsAgo: 0 is the bar that just closed, never the forming one.
Properties
| Property | Type | Meaning / when missing |
|---|---|---|
| price | price | Last traded price when the bar is processed. |
| bid, ask | price | The inside market. |
| tickSize | price | Minimum increment (0.25 on NQ and ES). Older name: tick. |
| pointValue | currency | Per point per contract; 0 means report ticks only. |
| time | epoch ms | The time of the bar being processed. |
| barIndex | int | Index of the bar being reported; -1 with no bars. Older name: i. |
| bars | int | Size of the bar series. |
| domSamples | int | How many order-book samples are held. 0 in a backtest. |
| structure | Structure | This chart's market structure — see Structure. Older name: ms. |
| structure2m, structure15m, structure1h | Structure | Higher timeframes, as of their last closed bar. |
| book | object | The order book — see Order book. |
| indicators | object | 183 indicator functions — see the catalog. Alias: ta. |
Methods
| Method | Returns | When missing |
|---|---|---|
| bar(n = 0, timeframe = "chart") | Bar | {} out of range; throws on a timeframe this run cannot serve |
| bar1m(n = 0) | Bar (subset) + ageSec | {} |
| footprint(n = 0, timeframe = "chart") | Footprint | {} when the bar has no ladder |
| footprint1m … footprint1h(n) | Footprint | {}; throws on a timeframe this run cannot serve |
| structureAt(timeframe) | Structure | The same object as structure15m and friends. |
| timeframes | string[] | Every timeframe name this run can serve. |
| stackedImbalance(n, opts) | 1, -1 or 0 | 0 |
| fairValueGaps(timeframe?, opts?) | array, newest first | []; throws on an unknown option or timeframe. Named forms too — fairValueGaps1m, 5m, 15m, 30m, 1h — each taking the options object. |
| priorDay(n = 0) | Session | {} |
| today() | Session | {} outside a session |
| priorWeek(n = 0) | Week | {} |
| valueArea(n = 0, desk?) | Volume profile | {}; { pending: true } while a desk cut is still being built |
| marketProfile(n = 0, desk?) | TPO profile | {}; {} too for a desk name it does not know |
| measuredMove(n = 0, seconds = 0) | Measured move | {} when there is no such move, or no such series |
| measuredMoveCount(seconds = 0) | int | 0 |
| momentum(n = 0, opts?) | Momentum | {} when the host offers no momentum at all — which signal: 0 could not say. Older name: simpleMomentum is the long form of c.momentum. |
| ssl(n, timeframe = "chart") | SSL | {} while warming up. chart, 1m and 5m only; ssl1m(n) and ssl5m(n) are the named forms. |
| closeLocation(n = 0) | 0 … 1 | NaN out of range or on a zero-range bar |
| atr(period = 14) | price | 0 until there are enough bars. A simple mean of true range. |
| toTicks(distance) | ticks (fractional, signed) | 0 if tickSize is 0 |
const bar = market.bar(0) // the bar that just closed
const fifteen = market.bar(0, 900) // the last closed 15-minute bar
const minute = market.bar1m(0) // the last closed 1-minute bar
const where = market.closeLocation(0) // 0 at the low .. 1 at the high
const atr = market.atr(14) // average true range, in price
const ticks = market.toTicks(bar.high - bar.low) // price distance in ticks
log("last " + market.price + " on bar " + market.barIndex)Timeframes
Anything that reads another timeframe takes one by name. market.timeframes lists what this run can serve: chart (the series the strategy runs on), own, and 1m, 2m, 3m, 5m, 10m, 15m, 30m, 1h, 2h, 4h, 1d. A number of seconds still works, for strategies written before the names existed.
market.timeframes // every name this run can serve
market.bar(0, "15m") // or market.bar15m(0)
market.footprint(0, "5m") // or market.footprint5m(0)
market.ssl(0, "5m") // chart, 1m and 5m only
market.structureAt("15m") // the same object as market.structure15m
market.fairValueGaps("15m", { unfilledOnly: true })
market.indicators.rsi({ period: 14 }, 0, "15m") // settings, barsAgo, timeframe
market.bar(0, "7m")
// Error: market.bar(): no timeframe "7m". It takes: chart, own, 1m, 2m, 3m,
// 5m, 10m, 15m, 30m, 1h, 2h, 4h, 1d -- or a number of secondsBar numbers belong to their own timeframe
barsAgo counts bars of the timeframe you asked for, and every bar number a higher timeframe reports is in its own bars — compare with its .bar, never market.barIndex. A name this run cannot serve throws with the reason, rather than reading as nothing. Live, the timeframes are the ones the feed keeps; in a backtest they are folded from the walk.Bar
Returned by market.bar(n) (and bar(n, 900) for a higher timeframe). market.bar1m(n) returns the fields marked “1m” plus ageSec.
| Field | Units | Meaning |
|---|---|---|
| time | epoch ms | The bar's OPEN. (1m) |
| open, high, low, close | price | (1m) |
| volume | contracts | (1m) |
| delta | contracts | Aggressive buys minus aggressive sells. (1m) |
| deltaMin, deltaMax | contracts | How far running delta reached during the bar, relative to its open. |
| cvd | contracts | Session cumulative delta at the bar's close. (1m) |
| trades | int | Prints folded in; 0 on a backfilled bar. |
| deltaFromBar | bool | True when delta came from a backfilled bar — it is missing, not balanced. (1m) |
| nyMin | minutes | Minutes since midnight New York time (570 = 9:30). |
| dow | 1 … 7 | Day of week, 1 = Monday. |
| ageSec | seconds | bar1m only: how old that minute bar is. |
const bar = market.bar(0)
if (bar.close === undefined) return 0 // not enough bars yet
if (bar.deltaFromBar) return 0 // delta is MISSING here, not zero
// A bar that ran to -400 and closed at -380 was one-sided; one that ran to
// -400 and closed at -100 changed its mind. Same net delta, different trade.
const oneSidedSelling = bar.delta < 0 && bar.delta <= 0.8 * bar.deltaMin
// Time filters without date arithmetic
const isFirstHour = bar.nyMin >= 570 && bar.nyMin < 630 // 9:30–10:30 New York
const isFriday = bar.dow === 5Footprint
Returned by market.footprint(n): the bar's ladder and the chart's own per-bar signals.
Tick tapes only
{}. Order-flow rules need the tick-tape data source.| Field | Type | Meaning |
|---|---|---|
| poc | price | Busiest price in the bar. |
| total | contracts | Volume. |
| delta, deltaMin, deltaMax | contracts | Net delta and its running excursions. |
| open, high, low, close | price | The ladder's own OHLC. |
| cells[] | Cell[] | Every price level, ascending — see below. |
| stacks[] | Stack[] | Same-side imbalance runs — see below. |
| unfinishedHigh, unfinishedLow | bool | Opposing volume at the extreme: an unfinished auction. |
| exhaustion, exhaustionBearish | bool | Exhaustion print at the extreme, and its side. |
| absorptionScore | 0 … 5 | How strongly the bar shows absorption. |
| absBuyCount, absSellCount | int | Levels flagged as absorbing each side. |
| absorptionBullish | bool | Which side the absorption favors. |
| maxLevelDelta, minLevelDelta | contracts | The most positive and most negative single-level delta. |
| bigBuyLevel, bigSellLevel | price | Price of an outsized print (3× the mean); 0 when none. |
| vaHigh, vaLow | price | The bar's own 70% value area; 0 when none. |
| pocPct | 0 … 100 | Where the POC sits in the bar: 0 at the low, 100 at the high; -1 on a zero-range bar. |
Cell (cells[])
| Field | Type | Meaning |
|---|---|---|
| price | price | The level. |
| bid | contracts | Traded at the bid — sellers hitting. |
| ask | contracts | Traded at the ask — buyers lifting. |
| delta | contracts | ask − bid. |
| buyImb, sellImb | bool | The chart's diagonal imbalance flags. |
| stacked | bool | Part of a same-side imbalance run. |
| thin | bool | A low-volume gap level inside the bar. |
| buyAbs, sellAbs | bool | Absorption flags. |
Stack (stacks[])
| Field | Type | Meaning |
|---|---|---|
| low, high | price | The run's range. |
| levels | int | How many consecutive imbalanced levels. |
| isBuy | bool | Buy-side or sell-side run. |
market.stackedImbalance(n, opts) returns the chart's stacked-imbalance arrow for a bar: 1, −1 or 0. Options (all optional): minLevels (3), closePercent (0.8), priorBars (2), clearHighs (true).
const ladder = market.footprint(0)
if (!ladder.cells) return 0 // no ladder (e.g. a stored-bar backtest)
// One run of six or more buy imbalances -- not two runs of three
const deepBuyStack = ladder.stacks.some(run => run.isBuy && run.levels >= 6)
// Where the heaviest selling sat inside the bar
let heaviestBid = ladder.cells[0]
for (const cell of ladder.cells) if (cell.bid > heaviestBid.bid) heaviestBid = cell
// The chart's own signals, computed for you
if (ladder.unfinishedHigh || ladder.exhaustion) log("auction looks done at " + ladder.high)
const arrow = market.stackedImbalance(0, { minLevels: 3, closePercent: 0.8, priorBars: 2 })Structure
market.structure — FI Market Structure for this chart. The whole object is {} until there are enough bars, so test dir !== undefined first.
| Field | Type | Meaning / when missing |
|---|---|---|
| dir | 1, -1, 0 | Bullish, bearish, undecided. |
| bosNow, chochNow | 1, -1, 0 | A break of structure / change of character fired ON THIS BAR. This chart only. |
| lastBreak | { isBullish, isChoch, price } | The most recent break; absent when there are none. |
| breaks | int | How many breaks so far — watch it go up. |
| bos[] | Break[] | Every break, oldest first — see below. |
| strongHigh, strongLow | price | The range; 0 when unset. |
| highName, lowName | string | The chart's names: Strong / Weak / Swing High, Strong / Weak / Swing Low. |
| strongHighBar, strongLowBar | int | Bar index of each; -1 when none. …BarsAgo variants too. |
| internalHigh, internalLow | price | -1 when there is none (zero is a price). |
| internalHighBar, internalLowBar | int | -1 when none. …BarsAgo variants too. |
| fibsValid | bool | Whether the fib levels below are meaningful. |
| fib50, fib618, fibExt | price | 50%, 61.8% and the −23.6% extension; -1 when not valid. |
| fibBearish | bool | Direction of the fib. |
| pivots[] | Pivot[] | Every confirmed swing, oldest first. |
| zones[] | Zone[] | Supply and demand zones off the swings. |
| bar | int | Higher timeframes only: that timeframe's last closed bar. |
Break (bos[])
| Field | Type | Meaning |
|---|---|---|
| id | int | Position in the log, which is never pruned or reordered. |
| price | price | The level that broke. |
| startBar, endBar | int | Where the broken swing started and the bar that broke it. |
| isBullish, isChoch | bool | Direction, and whether it was a change of character. |
| zoneTop, zoneBot, zoneBar | price / int | The break's own zone (the order block that caused it); 0 when it has none. |
| zoneWhy | string | Why there is no zone, when there is none. |
| zoneBroken, zoneBrokenAtBar | bool / int | Price has since closed through the zone — it is now a breaker. -1 while it holds. |
| revHigh, revLow | price | The reversal swing's range. |
Pivot (pivots[])
| Field | Type | Meaning |
|---|---|---|
| price | price | The swing. |
| bar, barsAgo | int | Where it formed. |
| isHigh | bool | Swing high or swing low. |
| strength | 8 or 5 | 8 = major, 5 = minor. |
Zone (zones[])
| Field | Type | Meaning |
|---|---|---|
| id | int | Stable while the strategy runs (the list itself is trimmed and re-sorted). |
| top, bot | price | The zone. |
| isSupply | bool | Supply or demand. |
| broken, brokenAtBar | bool / int | Whether price closed through it; -1 while it holds. |
| bar, createdBar | int | The swing's bar and when the zone was created. |
A break's zone is not in zones[]
zones[] are zones off the swings; the order block that caused a break lives on that break in bos[]. For breakers and order-block retests, read bos[]. On higher timeframes, every bar number is in that timeframe's bars — compare with .bar, never market.barIndex — and there is no bosNow.const structure = market.structure
if (structure.dir === undefined) return 0 // not enough bars yet
// EVENTS fire on the bar that broke: test these, not a level
if (structure.bosNow === 1 && !structure.lastBreak.isChoch) {
const lastBreak = structure.bos[structure.bos.length - 1]
log("bullish BOS at " + lastBreak.price +
(lastBreak.zoneTop ? ", order block " + lastBreak.zoneBot + "-" + lastBreak.zoneTop : ""))
}
// The range, with the chart's own names for it ("Strong Low", "Weak High" …)
log(structure.lowName + " " + structure.strongLow + " / " + structure.highName + " " + structure.strongHigh)
// Unbroken demand zones below price
const demand = structure.zones.filter(z => !z.isSupply && !z.broken && z.top < market.price)
// Higher timeframe, as of its last CLOSED bar -- compare bars with .bar, not barIndex
const htf = market.structure15m
const htfJustBroke = htf.bos && htf.bos.length && htf.bos[htf.bos.length - 1].endBar === htf.barFair value gaps
market.fairValueGaps(opts) — the gaps on this chart, newest first, filtered by the options you pass. An option it does not have is refused by name.
Backtests and replays of the chart's own series
periodSeconds); for the chart's own series they currently come back empty. In backtests both work.Options
| Option | Default | Meaning |
|---|---|---|
| bullish | both | true for bullish gaps only, false for bearish only. |
| unfilledOnly | false | Only gaps price has not filled. |
| includeBroken | false | Broken gaps are left out unless this is true. |
| minTicks, maxTicks | 0 (off) | Gap size filter, in ticks. |
| maxAgeBars | 0 (off) | Only gaps from the last N bars. |
| limit | 0 (all) | Most gaps to return. |
| timeframe | "chart" | Another timeframe by name — or pass it as the first argument. periodSeconds still works. |
| minBodyPct | 0 | The impulse bar's body as a share of its range. |
| minVolMult, minDeltaMult | 0 | The impulse bar's volume or |delta| as a multiple of the prior average. |
| displacementLookback | 20 | Bars in that average. |
| mitigatedOnly, unmitigatedOnly | false | Filter by mitigation. |
| mitigationPct | 0.5 | How far into the gap counts as mitigated (0 … 1). |
Gap
| Field | Type | Meaning |
|---|---|---|
| top, bot, mid | price | The gap and its midpoint (ce is the same as mid). |
| sizeTicks | ticks | Width. |
| bullish | bool | Side. |
| bar, barsAgo | int | The confirming bar. |
| filled, filledBar | bool / int | -1 when not filled. |
| fillPct, fillMax | 0 … 1 | How far price is into it now, and the deepest it has been. |
| touching | bool | Price is in it but has not filled it. |
| broken, brokenBar | bool / int | -1 when not broken. |
| inverted | bool | Filled but not broken. |
| mitigated, mitigatedBar, mitigatedBarsAgo | bool / int | -1 when not mitigated. |
| bodyPct, volMult, deltaMult | number | The impulse bar's measurements; -1 when they cannot be computed. |
// Unfilled gaps from the last 50 bars, newest first
const gaps = market.fairValueGaps({ unfilledOnly: true, maxAgeBars: 50, limit: 5 })
for (const gap of gaps) {
if (gap.bullish && market.price <= gap.top && market.price >= gap.bot) {
log("price is inside a bullish gap " + gap.bot + "-" + gap.top +
" (" + gap.sizeTicks + " ticks, " + Math.round(gap.fillPct * 100) + "% filled)")
}
}
// Only gaps made by a real impulse bar
const strong = market.fairValueGaps({ minBodyPct: 0.6, minVolMult: 1.5 })
// A misspelled option is refused by name, with the list of real ones
market.fairValueGaps({ unfiledOnly: true })
// Error: fairValueGaps(): has no option "unfiledOnly". It takes: bullish, unfilledOnly, …Session levels
| Field | Meaning |
|---|---|
| date | "yyyy-MM-dd" |
| open, high, low, close | The RTH session (09:30–16:00 New York). |
| ibHigh, ibLow, ibMid | Initial balance (the first hour, to 10:30). |
| orHigh, orLow | Opening range (to 10:00). |
| pmHigh, pmLow | Premarket, 04:00–09:30. |
| range | high − low. |
market.priorDay(n) returns a completed session (0 is the most recent) and market.today() the one in progress. A level that has not formed yet is 0. market.priorWeek(n) returns { open, high, low, close, range, from, to }.
The first day of a backtest has no prior day
priorWeek. After the 16:00 close, priorDay(0) is already today's session.const priorDay = market.priorDay(0) // yesterday's completed RTH session
if (!priorDay.high) return 0 // none yet (e.g. the first day of a run)
const today = market.today() // the session in progress
const aboveYesterday = market.price > priorDay.high
const insideIB = market.price > today.ibLow && market.price < today.ibHigh
const week = market.priorWeek(0) // { open, high, low, close, range, from, to }Value area & market profile
Two different questions about the same session. valueArea(n) is where size traded — volume at price. marketProfile(n) is where time was spent — 30-minute TPO letters over the RTH, which is what Dalton's rules are written against. They routinely disagree, and the disagreement is itself worth reading. 0 is the most recent completed session.
| Read | Fields | Meaning |
|---|---|---|
| valueArea(n, desk?) | poc, vah, val, volume | A completed session's VOLUME profile. With a desk, also open, end and part. |
| marketProfile(n, desk?) | poc, vah, val, ibHigh, ibLow, high, low, row, open, end | A completed session's TPO profile: 30-minute periods, 70% value area. open and end are epoch ms; row is the TPO row height. |
What the TPO says about the extremes
marketProfile(n) also reports the shape of each end. A tail is excess — the auction was rejected there and it tends to hold. A poor extreme is the complement: a flat end with no excess, so the business is unfinished and it tends to be revisited. Neither is set while price is still printing at that extreme.
| Field | Type | Meaning |
|---|---|---|
| buyingTail, sellingTail | { low, high } | The excess at each end, as a price band — the run spans whole TPO rows. Absent where there is none, so if (t.buyingTail) is the test. |
| poorHigh, poorLow | bool | Dalton's poor high / poor low: an extreme the auction left without excess. |
| singlePrints | { low, high }[] | The mid-profile gaps, in the same band form — the runs the chart outlines and extends. Empty array when there are none. |
const profile = market.marketProfile(0) // yesterday's TPO profile
const volume = market.valueArea(0) // yesterday's VOLUME profile
if (!profile.vah || !volume.poc) return 0 // no completed session yet
// The 80% rule is written against the TPO value area, not the volume one
const openedBelowValue = market.today().open < profile.val
log("TPO " + profile.val + "-" + profile.vah + " POC " + profile.poc +
" | volume POC " + volume.poc)One desk at a time
Both reads take an optional second argument naming which auction you mean: "asia" (18:00–03:00 New York), "london" (03:00–09:30) or "newyork" (09:30–17:00, also "ny"). Left out, marketProfile means RTH and valueArea means the store's own session cut — which is what they have always meant, so nothing written before this changes. A name it does not recognize returns {} rather than guessing.
// The same two reads, one desk at a time: "asia", "london", "newyork" ("ny").
// n counts THAT desk's own sessions -- va(1, "asia") is the Asia before last,
// never yesterday's London.
const asiaLetters = market.marketProfile(0, "asia") // where TIME was spent
const asiaVolume = market.valueArea(0, "asia") // where SIZE traded
// The desk volume cut is a second walk of the tick log, and asking is what
// starts it. Test for a level, never for emptiness: pending means "not yet",
// an empty map means "no such session", and a rule that cannot tell them
// apart will trade the warm-up.
if (asiaVolume.pending) return 0
if (!asiaVolume.poc || !asiaLetters.poc) return 0
// The pair disagreeing is the reading: a volume POC well away from the TPO POC
// says the night's size went somewhere the clock did not.
const apart = Math.abs(asiaVolume.poc - asiaLetters.poc)
log("Asia volume POC " + asiaVolume.poc + " vs letters " + asiaLetters.poc +
" (" + market.toTicks(apart) + " ticks apart)")A desk volume area can answer “not yet”
valueArea(n, desk) is the one read that can come back { pending: true }. The desk volume cut is a second walk of the tick log and asking is what starts it, so a live strategy's first calls can be pending for a few seconds. Test for poc, not for emptiness: pending means “not yet”, an empty map means “no such session”, and a rule that cannot tell them apart will trade the warm-up. In a backtest both are built from the walk and neither is ever pending.Global New York is not the RTH profile
marketProfile(n) stops at the bell. Both are right for what they are; a study must not mix them without saying which it meant.Measured moves
market.measuredMove(n, seconds) — c.mm — returns the impulse that set a range, the fib retracement that triggered it, the −23.6% extension target, and whether that target or the stop has since been reached. 0 is the newest. market.measuredMoveCount() says how many that series holds.
| Field | Type | Meaning |
|---|---|---|
| long | bool | Direction of the move. |
| trigger | price | Where the retracement triggered it. |
| fib | number | The retracement level that did (0.5, 0.618 …). |
| target | price | The −23.6% extension target. |
| swingHigh, swingLow | price | The impulse that set the range. |
| targetHit, stopHit, done | bool | What has happened since. |
| open | bool | None of those three — still live. This is the one to trade off. |
| quality | string | "perfect", "good" or "ok": how clean the touch was. |
| trend | string | "with", "counter" or "neutral". |
| confluence, precision | number | How much else agreed at that level, and how close the touch was. |
| bar | int | The bar it triggered on, in that series' own bars. |
| ago | int | Bars back on that series — the only count that means anything when the map came from a different one. |
// 0 is this strategy's own series; a number of SECONDS is another one.
const own = market.measuredMove(0) // newest move on our own bars
const higher = market.measuredMove(0, 900) // and the 15-minute one
if (!own.target) return 0 // no move folded yet
// Only trade the open ones, with the trend, when the fib was a clean touch
const tradeable = own.open && own.trend === "with" && own.quality !== "ok"
log("MM " + (own.long ? "long" : "short") + " trigger " + own.trigger +
" target " + own.target + " " + own.quality +
" | " + market.measuredMoveCount(900) + " on 15m")This one takes seconds, not a timeframe name
"15m"). measuredMove takes a number of seconds, because the named set could not answer “5 minute” at all: 0 is the strategy's own series, 300 is 5 minutes, 900 is 15. Live, any whole-minute series the app is keeping can answer; in a backtest it is the run's own period plus 120, 300, 900 and 3600. Anything else returns {} rather than reading as nothing.Each series is folded the first time it is asked for, then stepped forward, so a strategy that never calls this pays nothing for it.
Simple Momentum
market.momentum(n, opts) is the Heikin-Ashi SuperTrend the chart draws as arrows, ported from FIMarketStructure. It answers with both the event and the state, because a strategy trading flips needs the event: a level is equally true on the bar that made it and a hundred bars later, and a flip is not.
| Field | Type | Meaning |
|---|---|---|
| signal | 1, -1 or 0 | The EVENT: 1 on the bar a buy fired, −1 on a sell, 0 on every other bar. Same shape as stackedImbalance. |
| trend | 1 or -1 | The STATE, on every bar. |
| lastSignal | 1 or -1 | Which way the most recent arrow pointed. |
| barsSinceSignal | int | How long ago it fired — with lastSignal, this answers “are we still in the move it called”. Absent when nothing has fired yet. |
| raw, trendFilter | bool | Which engine answered, and whether its trend filter is on. |
const mom = market.momentum(0)
if (!mom.trend) return 0 // no momentum on this host
// The EVENT, confirmed by the ribbon: trade the flip, not the state
if (mom.signal > 0 && market.ssl(0).state > 0) return 1
// Or the ungated engine, for the Pine's timing
if (market.momentum(0, { raw: true }).signal < 0) return -1
// Still in the move it called?
const riding = mom.lastSignal > 0 && mom.barsSinceSignal < 20Two engines, and the gates are not the same kind
{ raw: true } drops both — every flip signals, which is the Pine this was ported from, with no confirmation lag. The two are separate folds and asking is what builds one, so a strategy that never calls this pays nothing for it.SSL
| Field | Type | Meaning |
|---|---|---|
| line | price | The trailing line — a natural stop. |
| channel | price | The other edge of the channel (omitted when not available). |
| state | 1 or -1 | Green or red. |
| period | int | The SSL period (20 by default; a strategy parameter named sslPeriod sets it). |
| barClose, ageSec | price / s | ssl1m and ssl5m only: that timeframe's last closed bar. |
const ribbon = market.ssl(0) // this chart's SSL channel
if (ribbon.line === undefined) return 0 // warming up: no answer, not neutral
if (ribbon.state > 0 && market.bar(0).close > ribbon.line) return 1
// The 5-minute channel as of the last 5-minute bar that had CLOSED -- cannot repaint
const fiveMinute = market.ssl5m(0)
const aligned = fiveMinute.state === ribbon.statemarket.ind() swallows a typo; market.indicators does not
market.indicators.rsi({ period: 14 }) refuses a parameter it does not have, by name, with a TypeError naming the ones it takes. The older market.ind(key, params, plot, n) accepts any bag of parameters and ignores what it does not recognize — so a misspelled one is an indicator quietly running on its defaults, which looks exactly like one that works. A key or plot name that does not exist returns undefined in both forms, and so does warm-up, which is why a mistyped name gives you a strategy that compiles, runs, takes no trades and never says why.Order book
market.book exposes the stats strip the DOM shows. stat(key) returns one cell, stats() all of them, and series("KEY.field", n) the last n samples (about one a second, oldest first). available and samples say whether there is a book and how much history is held.
| Key | Fields | Meaning (default window) |
|---|---|---|
| BOOK | ratio, wbid, wask | Weighted bid/ask depth over the top 10 levels. |
| LIQ | bid, ask | Liquidity added. |
| SUM | bid, ask | Total resting size. |
| TOP | bid, ask | Size at the inside. |
| SPREAD | ticks | Inside spread. |
| IMB | ratio, buy, sell, buySide | Aggressor imbalance (30 s). |
| TRD | rate, prints | Trade rate (5 s). |
| VOLD | vol, delta | Volume and delta of the forming bar. |
| CVD | cvd | Session cumulative delta. |
| ABS | dom, ticks, buy, sell, perTick | Absorption on the forming bar. |
| AGR | pct, buy, sell | Aggression on the forming bar. |
| MAX | qty | Largest print on the forming bar. |
| DPS | rate, net, buy, sell | Delta per second (30 s). |
| VAC | pct, depth, avg | Depth against its 5-minute average. |
| MV | perMin, net, lift, hit | Market volume (300 s). |
| LIFT, HIT | qty | Contracts lifted / hit (300 s). |
| VSR | net, sr, lift, hit | Volume strength ratio (300 s). |
No book in a backtest
market.book.available is false and every read is empty. An order-book rule can only be evaluated on a chart during a replay.if (!market.book.available) return 0 // always false in a backtest
const book = market.book.stat("BOOK") // { ratio, wbid, wask }
const imbalance = market.book.stat("IMB") // { ratio, buy, sell, buySide }
const lastMinute = market.book.series("BOOK.ratio", 60) // about one sample a second
if (book.ratio > 1.5 && imbalance.buySide) log("book leaning bid, tape lifting")broker
| Member | Returns | Rules |
|---|---|---|
| buy({ qty, stopTicks, targetTicks }) | bool | Market entry with a protective bracket. qty defaults to 1. Refused while a position is open (no pyramiding). On a chart an entry without stopTicks > 0 is refused — always give a stop. |
| sell({ qty, stopTicks, targetTicks }) | bool | The same, short. |
| flatten() | — | Closes the position at market and drops the protective orders. |
| cancelAll() | — | Cancels working orders. |
| moveStop(price) | bool | Moves the resting stop. Always false in a backtest. Prefer this.trailStop(price), which only moves in your favor. |
| position | { qty, avg, long, short, flat } | qty is signed: positive long, negative short. avg is 0 when flat. |
onBar() {
const position = broker.position // { qty, avg, long, short, flat }
if (position.long && market.bar(0).close < this.exitLevel) {
broker.flatten() // close at market, drop the bracket
}
super.onBar() // keep consulting signal() while flat
}
// Placing an order yourself -- ALWAYS with a stop
const placed = broker.buy({ qty: 1, stopTicks: 20, targetTicks: 40 })
if (!placed) log.warn("entry refused")log
| Call | Meaning |
|---|---|
| log(text), log.info(text) | An INFO line, stamped with the bar time in New York and the bar number. |
| log.warn(text), log.error(text) | WARN and ERROR lines. |
| log.debug(text) | Kept only after log.level("debug"), which belongs in init(). Levels: debug, info, warn, error. |
| log.plot(name, value) | A named series against bar time — up to 8 series. Plot a gate's input to see whether a condition was ever close. |
| console.log / .warn / .error | Routed to the same log. |
The log holds up to 5,000 lines (per day in a backtest), then says it was truncated. Never log from onTick().
log("entry: close " + market.bar(0).close + " above " + this.level)
log.warn("skipped: spread too wide")
log.error("unexpected state")
log.plot("closeLocation", market.closeLocation(0)) // up to 8 named series
console.log("same as log()")Inherited stop management
Every strategy inherits these parameters (group “Stops”), applied by maintain() before onBar(). Declaring one in your own params only changes its default. The base also reads the conventional keys stopTicks, targetTicks and qty.
| Parameter | Default | Range | Meaning |
|---|---|---|---|
| autoTrail | 1 | 0 – 1 | Walk the resting stop along the trail source. |
| trailSource | 0 | 0 – 3 | 0 this chart's SSL, 1 the 1-minute SSL, 2 the 5-minute SSL, 3 ATR behind price. |
| trailSlackTicks | 12 | 0 – 200 | Ticks behind the SSL line. |
| atrTrailMult | 2.0 | 0.25 – 10 | ATR multiples behind price (trail source 3). |
| atrTrailPeriod | 14 | 2 – 100 | ATR period (trail source 3). |
| breakEvenPoints | 20 | 0 – 500 | Move the stop to entry once this many POINTS ahead; 0 disables. |
| breakEvenOffsetTicks | 2 | 0 – 40 | Ticks beyond entry for break-even. |
Futures trading involves substantial risk of loss. This reference documents Quantum's scripting API; the examples are not trading advice. Backtest results are hypothetical and have inherent limitations. See our full disclaimer.