@mathieuc/tradingview 4.0.0-beta.0 → 4.0.0-beta.1
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/README.md +1 -1
- package/dist/chart/index.d.ts +2 -2
- package/dist/chart/index.js +1 -1
- package/dist/chart/strategy.d.ts +14 -1
- package/dist/chart/strategy.js +39 -12
- package/dist/chart/study.js +10 -2
- package/dist/data/index.d.ts +2 -0
- package/dist/data/index.js +1 -0
- package/dist/http/account.js +3 -3
- package/docs/data-api.md +56 -0
- package/docs/release-4.0.0-beta.1.md +39 -0
- package/docs/v4-coverage.md +28 -7
- package/package.json +2 -4
- package/docs/v4-reliability.md +0 -33
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ console.log(candles.at(-1)); // { time, open, high, low, close, volume }
|
|
|
25
25
|
|
|
26
26
|
Prefer to start without code? The setup paths below work too.
|
|
27
27
|
|
|
28
|
-
> **Version 4 is a breaking rewrite** in TypeScript, with a new API and no compatibility layer. Coming from v3? Read the [migration guide](docs/migration-v4.md). Every v3 feature is still available: see the [coverage matrix](docs/v4-coverage.md)
|
|
28
|
+
> **Version 4 is a breaking rewrite** in TypeScript, with a new API and no compatibility layer. Coming from v3? Read the [migration guide](docs/migration-v4.md). Every v3 feature is still available: see the [coverage matrix](docs/v4-coverage.md). npm versions 3.x keep the previous `Client` API; check the [npm page](https://www.npmjs.com/package/@mathieuc/tradingview) for the version you install.
|
|
29
29
|
|
|
30
30
|
## Get started
|
|
31
31
|
|
package/dist/chart/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ export { Study } from './study.js';
|
|
|
4
4
|
export type { StudyChange, StudyEvents, StudyValue } from './study.js';
|
|
5
5
|
export { applyGraphicsCommands, parseGraphics, } from './graphics.js';
|
|
6
6
|
export type { BoxStyleValue, ExtendValue, GraphicBox, GraphicHorizHist, GraphicHorizLine, GraphicLabel, GraphicLine, GraphicPoint, GraphicPolygon, GraphicTable, GraphicsData, HAlignValue, LabelStyleValue, LineStyleValue, RawGraphics, SizeValue, TableCell, TablePositionValue, TextWrapValue, VAlignValue, YLocValue, } from './graphics.js';
|
|
7
|
-
export { mergeStrategyReport, parseTrades } from './strategy.js';
|
|
8
|
-
export type { FromTo, PerformanceReport, RelAbsValue, StrategyReport, StrategyReportChange, TradeReport, } from './strategy.js';
|
|
7
|
+
export { mergeStrategyReport, parseTrades, summarizeStrategyReport } from './strategy.js';
|
|
8
|
+
export type { FromTo, PerformanceReport, RelAbsValue, StrategyReport, StrategyReportChange, StrategySummary, TradeReport, } from './strategy.js';
|
|
9
9
|
export { timeframeSeconds } from './timeframes.js';
|
|
10
10
|
export { CHART_TYPE_STUDIES } from './types.js';
|
|
11
11
|
export type { Adjustment, Candle, ChartType, ChartTypeInputs, MarketSymbol, Subsession, SymbolInfo, Timeframe, Timezone, TradingSession, } from './types.js';
|
package/dist/chart/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { ChartSession } from './chart-session.js';
|
|
2
2
|
export { Study } from './study.js';
|
|
3
3
|
export { applyGraphicsCommands, parseGraphics, } from './graphics.js';
|
|
4
|
-
export { mergeStrategyReport, parseTrades } from './strategy.js';
|
|
4
|
+
export { mergeStrategyReport, parseTrades, summarizeStrategyReport } from './strategy.js';
|
|
5
5
|
export { timeframeSeconds } from './timeframes.js';
|
|
6
6
|
export { CHART_TYPE_STUDIES } from './types.js';
|
package/dist/chart/strategy.d.ts
CHANGED
|
@@ -68,7 +68,7 @@ export interface StrategyReport {
|
|
|
68
68
|
};
|
|
69
69
|
[key: string]: unknown;
|
|
70
70
|
};
|
|
71
|
-
/**
|
|
71
|
+
/** Trade records, most recent first; may include open positions with an exit valuation. */
|
|
72
72
|
trades: TradeReport[];
|
|
73
73
|
history: {
|
|
74
74
|
buyHold?: number[];
|
|
@@ -98,3 +98,16 @@ export type StrategyReportChange = 'report.currency' | 'report.settings' | 'repo
|
|
|
98
98
|
export declare function parseTrades(trades: any[]): TradeReport[];
|
|
99
99
|
/** Merges a raw report into `target`; returns the changed parts. */
|
|
100
100
|
export declare function mergeStrategyReport(target: StrategyReport, report: any): StrategyReportChange[];
|
|
101
|
+
/** Aggregate counts are authoritative; an exit valuation does not prove closure. */
|
|
102
|
+
export interface StrategySummary {
|
|
103
|
+
tradeRecordCount: number;
|
|
104
|
+
closedTradeCount?: number;
|
|
105
|
+
openTradeCount?: number;
|
|
106
|
+
closedNetProfit?: number;
|
|
107
|
+
openPnL?: number;
|
|
108
|
+
/** Available only when both closed and open PnL are present. */
|
|
109
|
+
totalPnL?: number;
|
|
110
|
+
currency?: string;
|
|
111
|
+
}
|
|
112
|
+
/** Summarizes reported aggregates without inferring missing values or trade status. */
|
|
113
|
+
export declare function summarizeStrategyReport(report: StrategyReport): StrategySummary;
|
package/dist/chart/strategy.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { TradingViewError } from '../errors.js';
|
|
1
2
|
/** Converts raw trades (oldest first) into readable trades (most recent first). */
|
|
2
3
|
export function parseTrades(trades) {
|
|
4
|
+
if (!Array.isArray(trades) || trades.some((trade) => !trade || typeof trade !== 'object' || Array.isArray(trade))) {
|
|
5
|
+
throw new TradingViewError('PARSE_ERROR', 'Strategy trades must be an array of records');
|
|
6
|
+
}
|
|
3
7
|
return [...trades].reverse().map((t) => ({
|
|
4
8
|
entry: {
|
|
5
9
|
name: t.e?.c,
|
|
@@ -20,6 +24,8 @@ export function mergeStrategyReport(target, report) {
|
|
|
20
24
|
const changes = [];
|
|
21
25
|
if (!report || typeof report !== 'object')
|
|
22
26
|
return changes;
|
|
27
|
+
// Parse before mutating the target so a malformed trade list cannot leave a partial merge.
|
|
28
|
+
const trades = report.trades === undefined ? undefined : parseTrades(report.trades);
|
|
23
29
|
if (report.currency) {
|
|
24
30
|
target.currency = report.currency;
|
|
25
31
|
changes.push('report.currency');
|
|
@@ -32,20 +38,41 @@ export function mergeStrategyReport(target, report) {
|
|
|
32
38
|
target.performance = report.performance;
|
|
33
39
|
changes.push('report.perf');
|
|
34
40
|
}
|
|
35
|
-
if (
|
|
36
|
-
target.trades =
|
|
41
|
+
if (trades !== undefined) {
|
|
42
|
+
target.trades = trades;
|
|
37
43
|
changes.push('report.trades');
|
|
38
44
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
changes.push('report.history');
|
|
45
|
+
const historyKeys = [
|
|
46
|
+
'buyHold', 'buyHoldPercent', 'drawDown', 'drawDownPercent', 'equity', 'equityPercent',
|
|
47
|
+
];
|
|
48
|
+
let historyChanged = false;
|
|
49
|
+
for (const key of historyKeys) {
|
|
50
|
+
if (Array.isArray(report[key])) {
|
|
51
|
+
target.history[key] = report[key];
|
|
52
|
+
historyChanged = true;
|
|
53
|
+
}
|
|
49
54
|
}
|
|
55
|
+
if (historyChanged)
|
|
56
|
+
changes.push('report.history');
|
|
50
57
|
return changes;
|
|
51
58
|
}
|
|
59
|
+
/** Summarizes reported aggregates without inferring missing values or trade status. */
|
|
60
|
+
export function summarizeStrategyReport(report) {
|
|
61
|
+
const finite = (value) => typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
62
|
+
const count = (value) => {
|
|
63
|
+
const n = finite(value);
|
|
64
|
+
return n !== undefined && Number.isInteger(n) && n >= 0 ? n : undefined;
|
|
65
|
+
};
|
|
66
|
+
const closedNetProfit = finite(report.performance.all?.netProfit);
|
|
67
|
+
const openPnL = finite(report.performance.openPL);
|
|
68
|
+
return {
|
|
69
|
+
currency: report.currency,
|
|
70
|
+
tradeRecordCount: report.trades.length,
|
|
71
|
+
closedTradeCount: count(report.performance.all?.totalTrades),
|
|
72
|
+
openTradeCount: count(report.performance.all?.totalOpenTrades),
|
|
73
|
+
closedNetProfit,
|
|
74
|
+
openPnL,
|
|
75
|
+
totalPnL: closedNetProfit !== undefined && openPnL !== undefined
|
|
76
|
+
? finite(closedNetProfit + openPnL) : undefined,
|
|
77
|
+
};
|
|
78
|
+
}
|
package/dist/chart/study.js
CHANGED
|
@@ -161,8 +161,16 @@ export class Study extends Emitter {
|
|
|
161
161
|
}));
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
|
-
if (parsed?.data?.report)
|
|
165
|
-
|
|
164
|
+
if (parsed?.data?.report) {
|
|
165
|
+
try {
|
|
166
|
+
changes.push(...mergeStrategyReport(this.#strategyReport, parsed.data.report));
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
this.emit('error', new TradingViewError('PARSE_ERROR', 'Unable to parse strategy report', {
|
|
170
|
+
cause: error,
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
166
174
|
}
|
|
167
175
|
if (Array.isArray(ns?.indexes))
|
|
168
176
|
this.#graphicIndexes = ns.indexes;
|
package/dist/data/index.d.ts
CHANGED
|
@@ -26,3 +26,5 @@ export type { QuoteData, QuoteField } from '../quote/fields.js';
|
|
|
26
26
|
export type { StudyValue } from '../chart/study.js';
|
|
27
27
|
export type { GraphicsData } from '../chart/graphics.js';
|
|
28
28
|
export type { StrategyReport } from '../chart/strategy.js';
|
|
29
|
+
export { summarizeStrategyReport } from '../chart/strategy.js';
|
|
30
|
+
export type { StrategySummary } from '../chart/strategy.js';
|
package/dist/data/index.js
CHANGED
|
@@ -13,3 +13,4 @@ export { DEFAULT_TIMEOUT_MS } from './operation.js';
|
|
|
13
13
|
// Plain HTTP lookups are already one-shot; they are part of the data API too.
|
|
14
14
|
export { getTechnicalAnalysis, searchIndicators, searchMarkets, } from '../http/index.js';
|
|
15
15
|
export { TradingViewError } from '../errors.js';
|
|
16
|
+
export { summarizeStrategyReport } from '../chart/strategy.js';
|
package/dist/http/account.js
CHANGED
|
@@ -109,13 +109,13 @@ export async function getUser(credentials, options = {}) {
|
|
|
109
109
|
if (!credentials?.session)
|
|
110
110
|
throw new TradingViewError('INVALID_ARGUMENT', 'A session cookie is required');
|
|
111
111
|
const maxRedirects = options.maxRedirects ?? 5;
|
|
112
|
-
let location = trustedAccountLocation(options.location ?? 'https://www.tradingview.com/
|
|
112
|
+
let location = trustedAccountLocation(options.location ?? 'https://www.tradingview.com/');
|
|
113
113
|
for (let redirects = 0;; redirects += 1) {
|
|
114
|
-
const {
|
|
114
|
+
const { text, headers } = await request(location, { credentials, redirect: 'manual' }, options);
|
|
115
115
|
if (text.includes('auth_token'))
|
|
116
116
|
return parseUserPage(text, credentials);
|
|
117
117
|
const next = headers.get('location');
|
|
118
|
-
const resolved =
|
|
118
|
+
const resolved = next ? new URL(next, location).toString() : undefined;
|
|
119
119
|
if (!resolved || resolved === location) {
|
|
120
120
|
throw new TradingViewError('AUTH_ERROR', 'Wrong or expired sessionid/signature');
|
|
121
121
|
}
|
package/docs/data-api.md
CHANGED
|
@@ -152,6 +152,62 @@ Without an account, TradingView refuses Pine studies ("maximum number of studies
|
|
|
152
152
|
|
|
153
153
|
`watchIndicator(query, { onData, onError })` streams the same result on every study update; `watcher.latest` holds the last one.
|
|
154
154
|
|
|
155
|
+
### Strategy totals and open positions
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import { summarizeStrategyReport } from '@mathieuc/tradingview/data';
|
|
159
|
+
|
|
160
|
+
const summary = summarizeStrategyReport(result.strategyReport);
|
|
161
|
+
// tradeRecordCount, closedTradeCount, openTradeCount,
|
|
162
|
+
// closedNetProfit, openPnL, totalPnL, currency
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Trade records can include open positions with a current exit valuation. An `exit`
|
|
166
|
+
object does **not** establish that a trade is closed. The summary uses reported
|
|
167
|
+
`performance.all.totalTrades` and `totalOpenTrades`, never the record count, for
|
|
168
|
+
closed/open counts. It does not assign a closed/open status to individual records.
|
|
169
|
+
Counts may differ from the available records in a partial report.
|
|
170
|
+
|
|
171
|
+
`totalPnL` is closed net profit plus open PnL, in the report currency. Missing or
|
|
172
|
+
non-finite values remain `undefined`; a missing open PnL is not assumed to be zero.
|
|
173
|
+
Raw percentage/fraction fields are not rescaled. History series, including buy &
|
|
174
|
+
hold, are retained independently even when no equity series is provided. Updates
|
|
175
|
+
replace supplied arrays and retain omitted series; an empty array clears a series.
|
|
176
|
+
|
|
177
|
+
Malformed trade lists (a non-array or non-object records) raise `PARSE_ERROR`
|
|
178
|
+
before any part of that report is applied. Studies emit this error for both plain
|
|
179
|
+
and compressed reports. Omitted trade lists retain previous records; `[]` clears them.
|
|
180
|
+
This is structural validation, not full validation of every trade field.
|
|
181
|
+
|
|
182
|
+
These offline checks validate report decoding and normalization, not fresh-client
|
|
183
|
+
access, Replay playback, export entitlement or Deep Backtesting availability.
|
|
184
|
+
|
|
185
|
+
In a Basic-account UI check on October 3, 2026, both strategy trade CSV export and
|
|
186
|
+
strategy report XLSX export opened an upgrade prompt recommending Essential.
|
|
187
|
+
No successful export was verified. These are TradingView UI entitlements, not
|
|
188
|
+
library export methods or a guarantee about other accounts. Deep Backtesting is
|
|
189
|
+
separate and was blocked by a Premium upgrade prompt in that session.
|
|
190
|
+
|
|
191
|
+
### Complete strategy report example
|
|
192
|
+
|
|
193
|
+
See [strategy-report.js](../examples/strategy-report.js) for a bounded, executable
|
|
194
|
+
public-strategy → trade-records → PnL-summary workflow. Run it from a repository
|
|
195
|
+
checkout after building:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
node --env-file=.env examples/strategy-report.js
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
It uses the account cookies from `.env`, runs a public Supertrend strategy and
|
|
202
|
+
closes its connection automatically. It places no orders. Trade records are
|
|
203
|
+
most recent first and can include open positions with current exit valuations;
|
|
204
|
+
use the reported aggregate counts rather than inferring closure from an exit.
|
|
205
|
+
Missing summary fields remain `undefined`, not zero. `count` selects returned
|
|
206
|
+
candles, not a guaranteed exact strategy backtest window. Basic was sufficient
|
|
207
|
+
for the tested symbol/timeframe; UI exports and Deep Backtesting have separate
|
|
208
|
+
subscription requirements.
|
|
209
|
+
|
|
210
|
+
|
|
155
211
|
## Search and technical analysis
|
|
156
212
|
|
|
157
213
|
```ts
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# 4.0.0-beta.1 release candidate
|
|
2
|
+
|
|
3
|
+
Status: prepared for review; not published by this change.
|
|
4
|
+
Target npm dist-tag: **beta**. Keep **latest** on 3.5.2.
|
|
5
|
+
|
|
6
|
+
## Changes since beta.0
|
|
7
|
+
|
|
8
|
+
- Preserve buy-and-hold history even when a report has no equity history (#331).
|
|
9
|
+
- Export `summarizeStrategyReport`: separate record counts, closed/open trade
|
|
10
|
+
counts, closed net profit, open PnL and total PnL; retain unknown fields as
|
|
11
|
+
undefined (#331).
|
|
12
|
+
- Reject structurally malformed trade lists before modifying a report; report
|
|
13
|
+
parse errors consistently for plain and compressed envelopes (#332).
|
|
14
|
+
- Add a runnable public-strategy/report/PnL example, with bounded execution,
|
|
15
|
+
credential checks and explicit failure status.
|
|
16
|
+
- Document Basic Replay playback evidence and separate paid UI export and
|
|
17
|
+
Deep Backtesting limits from library report access.
|
|
18
|
+
|
|
19
|
+
## Validation
|
|
20
|
+
|
|
21
|
+
- Deterministic Node/Bun suites, typecheck, lint, build and packed-consumer smoke
|
|
22
|
+
checks are required before publication.
|
|
23
|
+
- Basic live probe: daily BINANCE:BTCEUR Replay load, three step acknowledgments,
|
|
24
|
+
automatic bar advancement, start and stop acknowledgments.
|
|
25
|
+
- Strategy example executed against Basic; report aggregates and history returned.
|
|
26
|
+
- Private compressed strategy capture validated in the preceding fixes.
|
|
27
|
+
- No claim of universal Replay entitlement, UI export success or Deep Backtesting.
|
|
28
|
+
|
|
29
|
+
## Publication gate
|
|
30
|
+
|
|
31
|
+
Publication requires the maintainer's explicit go-ahead. After approval, use the
|
|
32
|
+
reviewed commit with a clean checkout, rerun package checks, and publish the
|
|
33
|
+
inspected tarball using `npm publish <tarball> --tag beta`. Verify the resulting
|
|
34
|
+
version and dist-tags. Do not use the default `latest` tag. No tag, GitHub release,
|
|
35
|
+
or npm publication is created by preparing this candidate.
|
|
36
|
+
|
|
37
|
+
For v3 migration and breaking changes inherited from beta.0, see
|
|
38
|
+
[migration-v4.md](migration-v4.md). This candidate adds no new intended breaking
|
|
39
|
+
change relative to beta.0.
|
package/docs/v4-coverage.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# v4 coverage matrix
|
|
2
2
|
|
|
3
|
-
[Migration guide](migration-v4.md) · [Data API](data-api.md) · [Low-level API](low-level-api.md)
|
|
3
|
+
[Migration guide](migration-v4.md) · [Data API](data-api.md) · [Low-level API](low-level-api.md)
|
|
4
4
|
|
|
5
5
|
This matrix lists every capability of v3 (`main.js`, `src/`, examples, tests) and of the `agent.ts` preview, with its v4 replacement and the evidence that it works.
|
|
6
6
|
|
|
7
7
|
**Evidence columns**
|
|
8
8
|
|
|
9
|
-
- **Unit**: deterministic test in `tests/unit/` (`npm test` with Vitest on Node, `npm run test:bun` with Bun's runner;
|
|
10
|
-
- **Live**: result of `npm run test:live` (`tests/live/`) and examples against TradingView on **2 October 2026**:
|
|
9
|
+
- **Unit**: deterministic test in `tests/unit/` (`npm test` with Vitest on Node, `npm run test:bun` with Bun's runner; 115 tests, both green). Websocket tests use a scripted fake server (`tests/helpers/fake-server.ts`) or packets captured from TradingView (`tests/fixtures/live-session.json`); HTTP tests use a mocked `fetch`.
|
|
10
|
+
- **Live**: result of `npm run test:live` (`tests/live/`) and examples against TradingView on **2 October 2026**: 16 anonymous tests locally and 21 tests (including five authenticated) in the [manual GitHub Actions run](https://github.com/Mathieu2301/TradingView-API/actions/runs/37075296707):
|
|
11
11
|
- ✅ verified live anonymously;
|
|
12
12
|
- 🔒 path or variant not exercised live (often requires a specific account asset); deterministic tests only;
|
|
13
13
|
- ➖ not applicable (no network involved).
|
|
@@ -31,7 +31,7 @@ Test names are abbreviated as `file › test`.
|
|
|
31
31
|
| --- | --- | --- | --- |
|
|
32
32
|
| `protocol.parseWSPacket` (frame split, heartbeat as number, invalid JSON warning) | `protocol.decodeFrames` (length-based, UTF-16 lengths, typed frames, lenient fallback, invalid frames reported) | `protocol › decodes several frames...`, `keeps payloads containing frame markers intact`, `reports invalid JSON...`, `falls back to splitting malformed input`, `decodes every message of a captured live session` | ✅ (fixture captured live; UTF-16 lengths checked on Japanese/Korean descriptions) |
|
|
33
33
|
| `protocol.formatWSPacket` | `protocol.encodeFrame`, `encodePacket`, `encodeHeartbeat` | `protocol › encodes packets with UTF-16 lengths` | ✅ |
|
|
34
|
-
| `protocol.parseCompressed` (ZIP via jszip, base64 normalisation, zlib/raw/gzip fallbacks) | `protocol.decodeCompressed` (built-in ZIP reader: stored, deflated, data descriptors, empty entry names; zlib, raw deflate, gzip, plain JSON) and `normaliseBase64`, `readFirstZipEntry` | `protocol › compressed payloads › *` (ZIP fixtures generated independently with Python `zipfile`) |
|
|
34
|
+
| `protocol.parseCompressed` (ZIP via jszip, base64 normalisation, zlib/raw/gzip fallbacks) | `protocol.decodeCompressed` (built-in ZIP reader: stored, deflated, data descriptors, empty entry names; zlib, raw deflate, gzip, plain JSON) and `normaliseBase64`, `readFirstZipEntry` | `protocol › compressed payloads › *` (ZIP fixtures generated independently with Python `zipfile`) | ✅ private compressed browser capture replayed through compiled parser (3 October 2026); plain report verified |
|
|
35
35
|
| `utils.genSessionID` | `protocol.createSessionId` (crypto-random) | `protocol › ids` | ➖ |
|
|
36
36
|
| `utils.genAuthCookies` | Internal cookie builder used by every HTTP call | `http › * with credentials` (cookie headers asserted) | ➖ |
|
|
37
37
|
| Websocket URL `wss://<server>.tradingview.com/socket.io/websocket?from=chart&type=chart`, Origin and browser headers | Same, in `TradingViewClient`; transport is pluggable (`transport` option, default `ws`) | `client › connects with browser-like headers...` | ✅ Node and Bun (Bun needed the Origin header fix) |
|
|
@@ -89,7 +89,7 @@ Test names are abbreviated as `file › test`.
|
|
|
89
89
|
| v3 capability | v4 | Unit evidence | Live |
|
|
90
90
|
| --- | --- | --- | --- |
|
|
91
91
|
| `setMarket(symbol, { replay })` (replay session, add series, reset) | Same option | `chart › runs replay mode...` | ✅ |
|
|
92
|
-
| `replayStep(n)`, `replayStart(interval)`, `replayStop()` resolved by `replay_ok` | Same; reject with `INVALID_STATE` outside replay and on delete | `chart › runs replay mode...`, `rejects pending replay requests...` | ✅ step (`sessions › replays history step by step`);
|
|
92
|
+
| `replayStep(n)`, `replayStart(interval)`, `replayStop()` resolved by `replay_ok` | Same; reject with `INVALID_STATE` outside replay and on delete | `chart › runs replay mode...`, `rejects pending replay requests...` | ✅ step (`sessions › replays history step by step`); ✅ start/stop acknowledgments and advancing bars on Basic (3 October 2026) |
|
|
93
93
|
| `onReplayLoaded`, `onReplayPoint`, `onReplayResolution`, `onReplayEnd` | `replayLoaded`, `replayPoint`, `replayResolution`, `replayEnd` events | `chart › runs replay mode...` | ✅ |
|
|
94
94
|
| Replay `critical_error` | `CRITICAL_ERROR` on the chart; pending replay requests rejected | (code path shared with chart errors) | ➖ |
|
|
95
95
|
|
|
@@ -102,7 +102,7 @@ Test names are abbreviated as `file › test`.
|
|
|
102
102
|
| `study.periods` with plot names, `plot_N` fallback for unnamed/duplicate plots | `study.values` (oldest first) | `study › creates a Pine study, names plots...`, `chart › parses a captured live chart session` | ✅ (built-in volume rows) |
|
|
103
103
|
| `study.graphic` (labels, lines, boxes, tables + cells, polygons, horizLines, horizHists, raw) and `graphicsCmds` erase/create | `study.graphics` (`cells` array, `raw` object) | `study › reads graphics commands...`, `applies erase commands` | ✅ horizontal histograms (volume profile); 🔒 Pine drawings |
|
|
104
104
|
| Bars-back translation of graphic X indexes | Same | `study › reads graphics commands...` | ✅ |
|
|
105
|
-
| `study.strategyReport` (plain `data.report` and compressed `dataCompressed`; trades, performance, history, currency, settings) | Same | `study › decodes plain and compressed strategy reports`, `reports undecodable strategy reports as PARSE_ERROR` | ✅ Supertrend strategy report with account; compressed variant
|
|
105
|
+
| `study.strategyReport` (plain `data.report` and compressed `dataCompressed`; trades, performance, history, currency, settings) | Same | `study › decodes plain and compressed strategy reports`, `reports undecodable strategy reports as PARSE_ERROR` | ✅ Supertrend strategy report with account; compressed variant also validated using a private browser capture |
|
|
106
106
|
| `study.setIndicator()` (`modify_study`) | Same | `study › modifies and removes a study` | 🔒 |
|
|
107
107
|
| `study.remove()` | Same (idempotent) | same | ✅ |
|
|
108
108
|
| `onReady`, `onUpdate`, `onError`, `study_error` | `ready`, `loading`, `update`, `error` (`STUDY_ERROR`, message formatted with server context) | `study › formats study errors with their context` | ✅ (anonymous Pine refusal reported as `STUDY_ERROR`) |
|
|
@@ -189,4 +189,25 @@ These v3 behaviours were changed on purpose; none removes a capability.
|
|
|
189
189
|
|
|
190
190
|
## Not verified live for this release
|
|
191
191
|
|
|
192
|
-
The
|
|
192
|
+
The 21-test manual live workflow verified authenticated connection, account lookup, a public Pine RSI, a Supertrend strategy report, the private-indicators listing endpoint, and recent historical `to` with an account. It did **not** verify password login, actual private/invite-only scripts, older history beyond server limits, second-based/custom timeframes, the `prodata` server, owned layouts/drawings, or Pine permission changes. Those paths have deterministic tests but need the corresponding account assets to verify live. Run `SESSION=... SIGNATURE=... npm run test:live` to repeat the authenticated subset.
|
|
193
|
+
|
|
194
|
+
## Basic account follow-up — 3 October 2026
|
|
195
|
+
|
|
196
|
+
A fresh authenticated library client loaded `BINANCE:BTCEUR` daily Replay at a
|
|
197
|
+
reference 30 days before the probe. Loading returned five bars; three step
|
|
198
|
+
requests were acknowledged; automatic playback produced advancing bar timestamps;
|
|
199
|
+
start and stop were both acknowledged. The client was closed after the bounded
|
|
200
|
+
probe. This verifies the library protocol for that symbol/timeframe/reference,
|
|
201
|
+
not intraday Replay, every exchange, unlimited history, or simulated order entry.
|
|
202
|
+
The existing browser chart and its settings were not changed.
|
|
203
|
+
|
|
204
|
+
`examples/strategy-report.js` was executed with the same Basic account against
|
|
205
|
+
the public Supertrend strategy on `BINANCE:BTCEUR`, timeframe `60`, count `300`.
|
|
206
|
+
It returned trade records, closed/open aggregate counts and PnL, and buy-and-hold
|
|
207
|
+
history. No private payloads or credentials are included in this repository.
|
|
208
|
+
The strategy may compute over more history than the requested candle count;
|
|
209
|
+
`count` is not an exact backtest-window guarantee.
|
|
210
|
+
|
|
211
|
+
UI CSV and XLSX exports remain plan-blocked (Essential offered), and Deep
|
|
212
|
+
Backtesting remains plan-blocked (Premium offered). The library report example
|
|
213
|
+
is not an implementation of those UI exports and does not enable Deep Backtesting.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mathieuc/tradingview",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.1",
|
|
4
4
|
"description": "TradingView market data for JavaScript and TypeScript: candles, quotes, indicators and strategies, with a simple data API and full low-level access.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,9 +34,7 @@
|
|
|
34
34
|
"test:live": "vitest run --config vitest.live.config.ts",
|
|
35
35
|
"smoke": "node scripts/smoke.mjs",
|
|
36
36
|
"check": "npm run typecheck && npm run lint && npm test && npm run build && npm run smoke",
|
|
37
|
-
"prepack": "npm run build"
|
|
38
|
-
"probe:endurance": "npm run build && node scripts/endurance.mjs",
|
|
39
|
-
"probe:parity": "npm run build && node scripts/parity.mjs"
|
|
37
|
+
"prepack": "npm run build"
|
|
40
38
|
},
|
|
41
39
|
"repository": {
|
|
42
40
|
"type": "git",
|
package/docs/v4-reliability.md
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# V4 reliability evidence
|
|
2
|
-
|
|
3
|
-
This page tracks reproducible release checks beyond the [capability matrix](v4-coverage.md). It is evidence for a beta, not a claim that every TradingView account feature works.
|
|
4
|
-
|
|
5
|
-
## V3/V4 candle parity
|
|
6
|
-
|
|
7
|
-
On 2 October 2026, the published `@mathieuc/tradingview@3.5.2` and V4 `main` (`95b6d0f`) loaded the same `BINANCE:BTCEUR` market. For each of `D` and `60`, the probe compared 19 closed candles out of 20 requested. All 38 matched on timestamp and OHLC. Volumes matched after applying V3's two-decimal rounding; V4 intentionally preserves the server precision. The still-open candle was excluded because it can change between sequential requests.
|
|
8
|
-
|
|
9
|
-
Repeat without adding V3 to V4's dependencies:
|
|
10
|
-
|
|
11
|
-
```sh
|
|
12
|
-
npm ci
|
|
13
|
-
npm install --prefix /tmp/tradingview-v3-baseline --no-save --no-package-lock @mathieuc/tradingview@3.5.2
|
|
14
|
-
TV_V3_ENTRY=/tmp/tradingview-v3-baseline/node_modules/@mathieuc/tradingview/main.js npm run probe:parity
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
`probe:parity` exits nonzero on a mismatched closed candle. It needs live network access and is not part of deterministic CI.
|
|
18
|
-
|
|
19
|
-
## Live connection lifecycle
|
|
20
|
-
|
|
21
|
-
`tests/live/recovery.test.ts` starts candle and quote watchers on one client, closes the connection while both are active, verifies both stop and report `DISCONNECTED`, then fetches candles with a new client. This is a controlled socket close, not proof of recovery from every network failure. The library does not reconnect a closed `TradingViewClient` automatically; consumers create a new client.
|
|
22
|
-
|
|
23
|
-
`npm run probe:endurance -- --minutes=120 --cycle-seconds=600` streams one-minute BTCUSDT candles and quotes for two hours, rotates the connection every ten minutes, and emits JSON lines with startup counts, updates, heartbeats and errors. It exits nonzero on a startup failure, an early watcher close or missing data. It never reads or logs account credentials. A one-minute smoke run on 2 October 2026 completed three cycles with 16 candle updates, 18 quote updates, five heartbeats and zero errors. The two-hour result should only be recorded after the run ends. The probe also accepts `--symbol`, `--timeframe` and `--chart-type` for issue-specific checks. For example, issue #236 can be investigated with `--symbol=OANDA:EURUSD --chart-type=HeikinAshi`, though a closed forex market cannot prove that quote updates remain live.
|
|
24
|
-
|
|
25
|
-
## Account redirect regression
|
|
26
|
-
|
|
27
|
-
V4 now starts the account lookup at `/chart/` and follows `Location` only on HTTP 3xx responses. A deterministic test covers HTTP 200 with a misleading `Location` header. This ports the relevant guard from legacy PR #322; it does not claim to solve CAPTCHA/WAF challenges. The authenticated manual CI run passed all 22 live tests after this change.
|
|
28
|
-
|
|
29
|
-
## Remaining beta limitations
|
|
30
|
-
|
|
31
|
-
- The 17 anonymous live tests and five authenticated tests from the [coverage matrix](v4-coverage.md) exercise short-lived calls. The authenticated subset requires `SESSION` and `SIGNATURE`; all five tests passed in the manual GitHub Actions run on 2 October 2026.
|
|
32
|
-
- `prodata`, private/invite-only scripts, owned layouts and drawings, Pine permission changes, password login and a live compressed strategy report still require suitable account assets. Deterministic tests cover their known packet shapes but are not live proof.
|
|
33
|
-
- The scheduled/manual live CI job is currently non-blocking. A red live run must be reviewed before a stable V4 release.
|