@christtrade/depth 0.12.24 → 0.12.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/depth.cjs +44 -44
- package/depth.css +1 -1
- package/depth.mjs +44 -44
- package/depth.nopreflight.css +1 -1
- package/package.json +1 -1
- package/script-runtime.cjs +2 -2
- package/script-runtime.mjs +2 -2
- package/types/components/indicators/indicators-settings-dialog.d.ts +7 -0
- package/types/core/DataEngine.d.ts +23 -0
- package/types/core/DepthChart.d.ts +2 -2
- package/types/core/ScriptedPlugin.d.ts +7 -0
- package/types/core/TypedEventBus.d.ts +186 -1
- package/types/core/index.d.ts +8 -0
- package/types/core/script-dsl.d.ts +9 -0
- package/types/core/script-runtime.d.ts +11 -2
- package/types/core/strategy-range.d.ts +41 -0
- package/types/core/strategy-runtime.d.ts +365 -0
- package/types/core/strategy-stream.d.ts +40 -0
- package/types/core/strategy-sweep.d.ts +75 -0
- package/types/core/strategy-walkforward.d.ts +91 -0
- package/types/hooks/useChartData.d.ts +2 -0
- package/types/hooks/useChartSubscriptions.d.ts +1 -0
- package/types/interfaces/plugins/IChartPlugin.d.ts +13 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type ChunkPlan = {
|
|
2
|
+
fromNs: bigint;
|
|
3
|
+
toNs: bigint;
|
|
4
|
+
};
|
|
5
|
+
/** `coveredTo`: how far the source actually got - adapters cap responses and report the cap here, not as an error. */
|
|
6
|
+
export type RangeFetchResult<B> = {
|
|
7
|
+
bars: B[];
|
|
8
|
+
coveredTo: bigint | null;
|
|
9
|
+
};
|
|
10
|
+
export type RangeFetch<B> = (opts: {
|
|
11
|
+
fromNs: bigint;
|
|
12
|
+
toNs: bigint;
|
|
13
|
+
barNs: bigint;
|
|
14
|
+
}) => Promise<RangeFetchResult<B>>;
|
|
15
|
+
export declare const DEFAULT_STREAM_DEPTH = 4;
|
|
16
|
+
export declare const DEFAULT_MAX_CONTINUATIONS = 16;
|
|
17
|
+
/**
|
|
18
|
+
* Chunk boundaries decided up front, not lazily - a request has to fire before
|
|
19
|
+
* its predecessor answers. `chunkBars` is bars in a chunk, not the width: a
|
|
20
|
+
* hundred-bar chunk spans ninety-nine periods, ends inclusive.
|
|
21
|
+
*/
|
|
22
|
+
export declare function planChunks(fromNs: bigint, toNs: bigint, barNs: bigint, chunkBars: bigint, maxChunks: number): ChunkPlan[];
|
|
23
|
+
export type StreamOptions<B> = {
|
|
24
|
+
plan: readonly ChunkPlan[];
|
|
25
|
+
barNs: bigint;
|
|
26
|
+
fetch: RangeFetch<B>;
|
|
27
|
+
/** Pieces, not whole chunks - a two-request chunk arrives as two calls rather than paying to join them. */
|
|
28
|
+
deliver: (bars: B[], chunkIndex: number) => void;
|
|
29
|
+
/** False once superseded - checked after every await so a stale walk's late chunks don't reach the new run. */
|
|
30
|
+
isAlive?: () => boolean;
|
|
31
|
+
onProgress?: (delivered: number, total: number) => void;
|
|
32
|
+
depth?: number;
|
|
33
|
+
maxContinuations?: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Rejects with the first failure *in plan order*, not time order - otherwise a
|
|
37
|
+
* later failure could skip the bars an earlier, still-outstanding chunk owns,
|
|
38
|
+
* and a run that quietly drops a year still produces a plausible equity curve.
|
|
39
|
+
*/
|
|
40
|
+
export declare function streamRangeChunks<B>(opts: StreamOptions<B>): Promise<number>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { StrategyStats } from './strategy-runtime';
|
|
2
|
+
/**
|
|
3
|
+
* One axis of a sweep: a parameter and the values to try for it.
|
|
4
|
+
*
|
|
5
|
+
* Explicit `values` for anything discrete (a boolean, a select, a handful of
|
|
6
|
+
* lengths worth testing). `from`/`to`/`step` for a numeric range, which is what
|
|
7
|
+
* a ParamDef with min/max/step already describes - so the UI can offer a sweep
|
|
8
|
+
* over a parameter without the author declaring anything extra.
|
|
9
|
+
*/
|
|
10
|
+
export type SweepAxis = {
|
|
11
|
+
param: string;
|
|
12
|
+
values: unknown[];
|
|
13
|
+
} | {
|
|
14
|
+
param: string;
|
|
15
|
+
from: number;
|
|
16
|
+
to: number;
|
|
17
|
+
step: number;
|
|
18
|
+
};
|
|
19
|
+
export interface SweepSpec {
|
|
20
|
+
axes: SweepAxis[];
|
|
21
|
+
/**
|
|
22
|
+
* Fraction of the run held back from the end as out-of-sample, 0 to 0.9.
|
|
23
|
+
*
|
|
24
|
+
* Optional but strongly encouraged, and the reason it is here rather than in
|
|
25
|
+
* some later "advanced" feature: a sweep is a machine for overfitting. Report
|
|
26
|
+
* only the best in-sample result and you have found the parameters that best
|
|
27
|
+
* describe noise you already have. The out-of-sample column is what tells you
|
|
28
|
+
* whether you found anything at all.
|
|
29
|
+
*/
|
|
30
|
+
oosFraction?: number;
|
|
31
|
+
}
|
|
32
|
+
/** What one point of the grid produced. */
|
|
33
|
+
export interface SweepResult {
|
|
34
|
+
/** The parameter values this run used - only the swept ones. */
|
|
35
|
+
params: Record<string, unknown>;
|
|
36
|
+
/** Over the whole window. */
|
|
37
|
+
stats: StrategyStats;
|
|
38
|
+
/** The leading portion, when a split was requested. */
|
|
39
|
+
inSample?: StrategyStats;
|
|
40
|
+
/** The held-back tail. The number that actually means something. */
|
|
41
|
+
outOfSample?: StrategyStats;
|
|
42
|
+
}
|
|
43
|
+
export declare const MAX_SWEEP_BAR_ITERATIONS = 200000000;
|
|
44
|
+
export declare const MAX_SWEEP_COMBOS = 20000;
|
|
45
|
+
export declare function axisValues(axis: SweepAxis): unknown[];
|
|
46
|
+
/**
|
|
47
|
+
* The cartesian product of every axis, as parameter patches.
|
|
48
|
+
*
|
|
49
|
+
* Ordered so the first axis varies slowest. That makes the result array read as
|
|
50
|
+
* rows of the first parameter when laid out as a grid, which is what a heatmap
|
|
51
|
+
* wants without having to sort anything.
|
|
52
|
+
*/
|
|
53
|
+
export declare function expandGrid(axes: SweepAxis[]): Array<Record<string, unknown>>;
|
|
54
|
+
export interface SweepBudget {
|
|
55
|
+
combos: number;
|
|
56
|
+
bars: number;
|
|
57
|
+
iterations: number;
|
|
58
|
+
ok: boolean;
|
|
59
|
+
/** Populated when ok is false. Written for a user, not a log. */
|
|
60
|
+
reason?: string;
|
|
61
|
+
}
|
|
62
|
+
/** Whether a sweep is worth attempting here, and what to say when it is not. */
|
|
63
|
+
export declare function checkSweepBudget(combos: number, bars: number): SweepBudget;
|
|
64
|
+
/**
|
|
65
|
+
* Where to cut a run into in-sample and out-of-sample halves.
|
|
66
|
+
*
|
|
67
|
+
* The tail is held back, never the head: the point is to test on data that comes
|
|
68
|
+
* *after* what the parameters were chosen on, because that is the only ordering
|
|
69
|
+
* that resembles trading them.
|
|
70
|
+
*
|
|
71
|
+
* Returns null when there is no meaningful split - too few bars, or a fraction
|
|
72
|
+
* that would leave one side empty. A missing OOS column is honest; a two-bar one
|
|
73
|
+
* is worse than none.
|
|
74
|
+
*/
|
|
75
|
+
export declare function splitIndex(barCount: number, oosFraction: number | undefined): number | null;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { StrategyStats } from './strategy-runtime';
|
|
2
|
+
export interface WalkForwardSpec {
|
|
3
|
+
/** How many out-of-sample segments to test. */
|
|
4
|
+
windows: number;
|
|
5
|
+
/**
|
|
6
|
+
* In-sample length as a multiple of out-of-sample length.
|
|
7
|
+
*
|
|
8
|
+
* 3 or 4 is conventional: enough history to optimise on, tested against a
|
|
9
|
+
* meaningful stretch of what followed. At 1 the optimiser sees as much data
|
|
10
|
+
* as it is judged on, which makes every window statistically thin.
|
|
11
|
+
*/
|
|
12
|
+
isMultiple: number;
|
|
13
|
+
/**
|
|
14
|
+
* Anchored keeps the in-sample start fixed and lets it grow; rolling slides
|
|
15
|
+
* a fixed-width window.
|
|
16
|
+
*
|
|
17
|
+
* Neither is correct in general. Anchored uses more data and assumes the
|
|
18
|
+
* distant past still applies; rolling adapts to regime change and forgets.
|
|
19
|
+
* Which one flatters a strategy is itself a finding.
|
|
20
|
+
*/
|
|
21
|
+
anchored: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface WalkForwardWindow {
|
|
24
|
+
index: number;
|
|
25
|
+
/** Bar indices, half-open: [from, to). */
|
|
26
|
+
isFrom: number;
|
|
27
|
+
isTo: number;
|
|
28
|
+
oosFrom: number;
|
|
29
|
+
oosTo: number;
|
|
30
|
+
}
|
|
31
|
+
export interface WalkForwardWindowResult {
|
|
32
|
+
window: WalkForwardWindow;
|
|
33
|
+
/** What the optimiser chose on the in-sample segment. */
|
|
34
|
+
params: Record<string, unknown>;
|
|
35
|
+
inSample: StrategyStats;
|
|
36
|
+
outOfSample: StrategyStats;
|
|
37
|
+
}
|
|
38
|
+
export interface ParameterStability {
|
|
39
|
+
param: string;
|
|
40
|
+
/** The value chosen in each window, in order. */
|
|
41
|
+
values: number[];
|
|
42
|
+
distinct: number;
|
|
43
|
+
/**
|
|
44
|
+
* Standard deviation over the mean. Scale-free, so a period and a percentage
|
|
45
|
+
* are comparable.
|
|
46
|
+
*
|
|
47
|
+
* Under ~0.15 the windows broadly agree. Over ~0.5 they do not, and a single
|
|
48
|
+
* "optimal" value quoted from the full-history sweep is a coincidence.
|
|
49
|
+
*/
|
|
50
|
+
coefficientOfVariation: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Divides a series into overlapping optimise/test windows.
|
|
54
|
+
*
|
|
55
|
+
* Anchored keeps every in-sample segment starting at bar zero instead.
|
|
56
|
+
*
|
|
57
|
+
* Returns an empty array when the series cannot support the schedule. Refusing
|
|
58
|
+
* is the honest answer - a walk-forward over 40-bar windows produces numbers
|
|
59
|
+
* that look like analysis and are noise.
|
|
60
|
+
*/
|
|
61
|
+
export declare function planWalkForward(barCount: number, spec: WalkForwardSpec): WalkForwardWindow[];
|
|
62
|
+
/**
|
|
63
|
+
* How much of the optimised edge survived on unseen data.
|
|
64
|
+
*
|
|
65
|
+
* Per-bar instead of total - anchored windows differ in length and an
|
|
66
|
+
* unweighted ratio would let the longest in-sample segment dominate.
|
|
67
|
+
*
|
|
68
|
+
* 1.0 means out-of-sample matched in-sample. Below ~0.5 is the usual warning
|
|
69
|
+
* line. Negative means the optimised parameters lost money on what followed,
|
|
70
|
+
* which is the most useful result a backtest can give you.
|
|
71
|
+
*
|
|
72
|
+
* Returns 0 when in-sample made nothing - there is no ratio to a zero
|
|
73
|
+
* denominator, and reporting Infinity as "infinitely efficient" would be absurd.
|
|
74
|
+
*/
|
|
75
|
+
export declare function walkForwardEfficiency(results: WalkForwardWindowResult[]): number;
|
|
76
|
+
/**
|
|
77
|
+
* Whether the optimiser kept choosing the same thing.
|
|
78
|
+
*
|
|
79
|
+
* Only numeric parameters - the spread of a boolean or a string across windows
|
|
80
|
+
* has no meaningful coefficient.
|
|
81
|
+
*/
|
|
82
|
+
export declare function parameterStability(results: WalkForwardWindowResult[]): ParameterStability[];
|
|
83
|
+
/**
|
|
84
|
+
* Picks the best row of a scored grid.
|
|
85
|
+
*
|
|
86
|
+
* `higherIsBetter` is false for drawdown-like objectives. Rows whose metric is
|
|
87
|
+
* not finite are skipped rather than winning: a profit factor of Infinity means
|
|
88
|
+
* a run with no losing trade, which on a two-trade in-sample window is noise
|
|
89
|
+
* dressed as perfection.
|
|
90
|
+
*/
|
|
91
|
+
export declare function pickBest<T>(rows: T[], metric: (row: T) => number | undefined, higherIsBetter?: boolean): T | null;
|
|
@@ -126,6 +126,8 @@ export interface ChartDataResult {
|
|
|
126
126
|
seekHorizon: (horizon: bigint, triggerResample?: boolean, hotPath?: boolean,
|
|
127
127
|
/** Explicit navigation: bring the view along instead of leaving it behind. */
|
|
128
128
|
recenter?: boolean) => void;
|
|
129
|
+
/** Frames a span of time. Does not move the playhead - see 'chart:goto-range'. */
|
|
130
|
+
gotoRange: (fromNs: bigint, toNs?: bigint, padding?: number) => void;
|
|
129
131
|
applyHorizon: (horizon: bigint, triggerResample?: boolean, hotPath?: boolean) => void;
|
|
130
132
|
scheduleResample: () => void;
|
|
131
133
|
runIndicatorWorker: (trades: SerialTrade[], barNs: bigint) => void;
|
|
@@ -45,6 +45,7 @@ export interface UseChartSubscriptionsParams {
|
|
|
45
45
|
setPluginChartTypes: (types: ChartTypePlugin[]) => void;
|
|
46
46
|
setChartSettings: (updater: ChartSettings | ((prev: ChartSettings) => ChartSettings)) => void;
|
|
47
47
|
resetView: () => void;
|
|
48
|
+
gotoRange: (fromNs: bigint, toNs?: bigint, padding?: number) => void;
|
|
48
49
|
buildChartTypeActiveCtx: () => ChartTypeActiveContext;
|
|
49
50
|
pushDrawParams: () => void;
|
|
50
51
|
runIndicatorWorker: (trades: SerialTrade[], barNs: bigint) => void;
|
|
@@ -13,7 +13,7 @@ import type { SerialTrade, PriceHistory } from '../../lib/types';
|
|
|
13
13
|
import type { FootprintBar } from '../../lib/types/footprint';
|
|
14
14
|
import type { BracketAmendment } from '../../lib/types/trading-types';
|
|
15
15
|
import type { PlaceOrderRequest } from '../../lib/matchingEngine';
|
|
16
|
-
export type PluginType = 'indicator' | 'drawing' | 'chart-type' | 'data-source' | 'extension';
|
|
16
|
+
export type PluginType = 'indicator' | 'drawing' | 'chart-type' | 'data-source' | 'extension' | 'strategy';
|
|
17
17
|
export type Permission = 'data:read' | 'data:write' | 'eventbus:emit' | 'eventbus:intercept' | 'ui:pane' | 'ui:panel' | 'ui:toolbar' | 'ui:context-menu' | 'render:overlay' | 'render:shader' | 'drawing:register' | 'chart-type:register' | 'theme:register' | 'audio' | 'network' | 'storage' | 'intercept' | 'execution:read' | 'execution:write';
|
|
18
18
|
export interface ToolbarItem {
|
|
19
19
|
id: string;
|
|
@@ -163,6 +163,18 @@ export interface PluginContext {
|
|
|
163
163
|
dataLevel: DataLevel;
|
|
164
164
|
horizon: () => number;
|
|
165
165
|
getData(): Readonly<PluginDataSnapshot>;
|
|
166
|
+
fetchRange(opts: {
|
|
167
|
+
/** Defaults to the focused symbol. */
|
|
168
|
+
symbol?: string;
|
|
169
|
+
fromNs: bigint;
|
|
170
|
+
toNs: bigint;
|
|
171
|
+
/** Bar period to fetch at. Defaults to the chart's current timeframe. */
|
|
172
|
+
barNs?: bigint;
|
|
173
|
+
}): Promise<{
|
|
174
|
+
bars: OhlcvBar[];
|
|
175
|
+
hasMore: boolean;
|
|
176
|
+
coveredTo: bigint | null;
|
|
177
|
+
}>;
|
|
166
178
|
/**
|
|
167
179
|
* The bar still being built at the playback horizon, or `null` before any
|
|
168
180
|
* data has arrived.
|