tickatlas 0.1.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,57 @@
1
+ # Changelog
2
+
3
+ All notable changes to the `tickatlas` JavaScript/TypeScript SDK are documented
4
+ here. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
5
+ and [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+
7
+ ## [0.1.0] - 2026-06-15
8
+
9
+ Initial public release. Built against TickAtlas API `v1` (app `1.0.0`) per the
10
+ authoritative SDK contract (`SPEC.md`).
11
+
12
+ ### Added
13
+
14
+ - `TickAtlas` client class covering all **21** `/v1` endpoints plus the
15
+ `/health` infra probe:
16
+ - Symbols: `getSymbols`, `getSymbol`
17
+ - Quotes: `getQuote`, `getQuotes`
18
+ - History: `getOhlc`, `getTicks`
19
+ - Indicators: `getIndicator`, `getIndicators`, `listIndicators`,
20
+ `getIndicatorHistory`, `getMulti`
21
+ - Screener: `screen`
22
+ - Analytics: `getSummary`, `getSpread`, `compareSpread`, `getSessions`,
23
+ `getHeatmap`, `getCalendar`
24
+ - Account: `getAccount`, `getLayout`, `saveLayout` (write)
25
+ - Infra: `health`
26
+ - Full TypeScript types for every request parameter object and response model.
27
+ - Typed exception hierarchy: `TickAtlasError`, `TickAtlasAPIError`,
28
+ `AuthenticationError`, `PermissionDeniedError`, `NotFoundError`,
29
+ `ValidationError`, `RateLimitError`, `ServerError`, `TickAtlasNetworkError`,
30
+ and `TickAtlasConfigError`, with HTTP-status + `error.code` mapping.
31
+ - `as const` enums/catalogues for autocomplete: `Timeframes`,
32
+ `HeatmapTimeframes`, `SymbolCategories`, `SpreadPeriods`, `CalendarImpacts`,
33
+ `Bias`, `BiasStrengths`, `HeatmapTypes`, `IndicatorCategories`, `Plans`,
34
+ `SortDirections`, the full 42-id `Indicators` catalogue,
35
+ `IndicatorsByCategory`, and `ALL_INDICATORS`. Methods also accept raw strings.
36
+ - Automatic retries for `429`/`5xx`/network errors with exponential backoff and
37
+ full jitter; honours `Retry-After` (then `X-RateLimit-Reset`,
38
+ `reset_in_seconds`) on `429`.
39
+ - Request timeouts via `AbortController` (default 30s); configurable `timeout`,
40
+ `maxRetries`, `backoffBase`, `backoffCap`, and an injectable `fetch`.
41
+ - API-key resolution from an explicit option or `TICKATLAS_API_KEY`; base URL
42
+ from option, `TICKATLAS_BASE_URL`, or the production default. Browser-safe
43
+ `process` guarding; `User-Agent` set on Node only.
44
+ - Zero runtime dependencies (platform `fetch`); dual **ESM + CJS** build with
45
+ `.d.ts` declarations and an `exports` map. Targets Node 18+ and browsers.
46
+ - Unit tests (vitest) with a mocked `fetch` covering success parsing for every
47
+ method, error→exception mapping, and the retry/`Retry-After` behaviour; plus a
48
+ gated, read-only live integration suite.
49
+
50
+ ### Notes
51
+
52
+ - The WebSocket quote stream is **not** part of `0.1.0` and is tracked for a
53
+ future release.
54
+ - `saveLayout` (`PUT /monitor/layout`) is the only mutating method and is
55
+ documented as advanced/optional.
56
+
57
+ [0.1.0]: https://github.com/abuzant/tickatlas-sdk/releases/tag/js-v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TickAtlas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,351 @@
1
+ # tickatlas
2
+
3
+ Official JavaScript / TypeScript SDK for the [TickAtlas](https://tickatlas.com) market-data API.
4
+
5
+ Real-time quotes, OHLC candles, tick data, **42 technical indicators**, a market screener, currency-strength heatmaps, an economic calendar, and account/quota info — for forex, metals, commodities, indices, crypto, and stocks.
6
+
7
+ - **Zero runtime dependencies.** Uses the platform `fetch` — works in **Node 18+** and modern **browsers**.
8
+ - **Dual package:** ships **ESM**, **CommonJS**, and full **TypeScript declarations**.
9
+ - **Fully typed:** every request parameter and response model, plus a typed exception hierarchy.
10
+ - **Resilient:** automatic retries with exponential backoff + jitter, honouring `Retry-After` on rate limits.
11
+
12
+ > Covers all **21** `/v1` endpoints plus the `/health` probe. The WebSocket quote stream is out of scope for `0.1.0` (tracked for a future release).
13
+
14
+ ---
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install tickatlas
20
+ ```
21
+
22
+ Requires Node.js **18 or newer** (for the built-in `fetch`/`AbortController`), or any modern browser.
23
+
24
+ ---
25
+
26
+ ## Authentication
27
+
28
+ Authenticate with your API key (sent as the `X-API-Key` header). The key is resolved in this order:
29
+
30
+ 1. The explicit `apiKey` constructor option.
31
+ 2. The `TICKATLAS_API_KEY` environment variable (Node only).
32
+
33
+ If neither is present, the constructor throws a `TickAtlasConfigError`.
34
+
35
+ ```ts
36
+ import { TickAtlas } from "tickatlas";
37
+
38
+ // Explicit key
39
+ const client = new TickAtlas({ apiKey: "tk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" });
40
+
41
+ // …or rely on the TICKATLAS_API_KEY env var
42
+ const client2 = new TickAtlas();
43
+ ```
44
+
45
+ > **Never** hardcode, log, or commit your API key. Prefer the `TICKATLAS_API_KEY` environment variable. This SDK never writes your key to disk or logs.
46
+
47
+ ### Base URL
48
+
49
+ Defaults to `https://tickatlas.com/v1`. Override for staging/self-hosted deployments via the `baseURL` option or the `TICKATLAS_BASE_URL` env var (option wins).
50
+
51
+ ```ts
52
+ const client = new TickAtlas({
53
+ apiKey: process.env.TICKATLAS_API_KEY,
54
+ baseURL: "https://staging.tickatlas.com/v1",
55
+ });
56
+ ```
57
+
58
+ ### Client options
59
+
60
+ | Option | Default | Description |
61
+ | ------------- | ----------------------------- | --------------------------------------------- |
62
+ | `apiKey` | `TICKATLAS_API_KEY` env | API key (`X-API-Key` header). |
63
+ | `baseURL` | `https://tickatlas.com/v1` | API base URL (`TICKATLAS_BASE_URL` env). |
64
+ | `timeout` | `30000` | Per-request timeout in ms (`AbortController`). |
65
+ | `maxRetries` | `3` | Retries for 429 / 5xx / network errors. |
66
+ | `backoffBase` | `500` | Exponential-backoff base in ms. |
67
+ | `backoffCap` | `30000` | Maximum backoff delay in ms. |
68
+ | `fetch` | global `fetch` | Custom `fetch` implementation (advanced). |
69
+
70
+ ---
71
+
72
+ ## Quickstart
73
+
74
+ ### TypeScript
75
+
76
+ ```ts
77
+ import { TickAtlas, Indicators, Timeframes } from "tickatlas";
78
+
79
+ const client = new TickAtlas({ apiKey: process.env.TICKATLAS_API_KEY });
80
+
81
+ // A single real-time quote
82
+ const quote = await client.getQuote("EURUSD");
83
+ console.log(`${quote.symbol}: ${quote.bid} / ${quote.ask} (spread ${quote.spread_pips} pips)`);
84
+
85
+ // RSI(14) on the H1 timeframe
86
+ const rsi = await client.getIndicator("EURUSD", Indicators.RSI_14, {
87
+ timeframe: Timeframes.H1,
88
+ });
89
+ console.log(`RSI: ${rsi.value}`);
90
+ ```
91
+
92
+ ### JavaScript (ESM)
93
+
94
+ ```js
95
+ import { TickAtlas } from "tickatlas";
96
+
97
+ const client = new TickAtlas({ apiKey: process.env.TICKATLAS_API_KEY });
98
+ const quote = await client.getQuote("EURUSD");
99
+ console.log(quote.bid, quote.ask);
100
+ ```
101
+
102
+ ### JavaScript (CommonJS)
103
+
104
+ ```js
105
+ const { TickAtlas } = require("tickatlas");
106
+
107
+ (async () => {
108
+ const client = new TickAtlas({ apiKey: process.env.TICKATLAS_API_KEY });
109
+ const quote = await client.getQuote("EURUSD");
110
+ console.log(quote.bid, quote.ask);
111
+ })();
112
+ ```
113
+
114
+ Both the `import` and `require` forms are wired through the package `exports` map, with `.d.ts` types for either entry point.
115
+
116
+ ---
117
+
118
+ ## Enums & constants
119
+
120
+ For autocomplete, the SDK exports `as const` objects. **You can always pass raw strings instead** — these exist purely for convenience.
121
+
122
+ ```ts
123
+ import {
124
+ Timeframes, // M1 M5 M15 M30 H1 H4 D1
125
+ HeatmapTimeframes, // H1 H4 D1 W1
126
+ Indicators, // all 42 indicator ids
127
+ IndicatorsByCategory,
128
+ ALL_INDICATORS,
129
+ SpreadPeriods, // 1h 24h 7d 30d
130
+ CalendarImpacts, // high medium low
131
+ SymbolCategories, // forex metals commodities indices crypto stocks
132
+ } from "tickatlas";
133
+
134
+ client.getIndicator("EURUSD", Indicators.MACD_hist, { timeframe: Timeframes.H4 });
135
+ client.getIndicator("EURUSD", "MACD_hist", { timeframe: "H4" }); // identical
136
+ ```
137
+
138
+ **Indicator naming gotchas** (the names below are the *correct* ones — common doc typos 404):
139
+ `SAR` (not `Parabolic_SAR`), `Volumes` (not `Tick_Volume`), `WilliamsR_14` (no underscore before `R`), `ADX_plusDI` / `ADX_minusDI`. `EMA` stops at `EMA_50`; `SMA` goes to `SMA_200`. There is no `RSI_9` or `EMA_200`, and only three Ichimoku keys.
140
+
141
+ ---
142
+
143
+ ## Endpoint reference
144
+
145
+ All methods return a `Promise` of the **unwrapped, typed** `data` payload (the `{ success, data }` envelope is handled for you).
146
+
147
+ ### Symbols
148
+
149
+ ```ts
150
+ await client.getSymbols({ category: "forex", search: "EUR", offset: 0, limit: 100 });
151
+ await client.getSymbol("EURUSD"); // full contract spec
152
+ ```
153
+
154
+ ### Quotes
155
+
156
+ ```ts
157
+ await client.getQuote("EURUSD", { includeSources: true });
158
+ await client.getQuotes(["EURUSD", "GBPUSD", "USDJPY"], ["bid", "ask", "spread_pips"]);
159
+ ```
160
+
161
+ `getQuotes` is a `POST` under the hood (the API's batch endpoint is POST-only); `fields` is optional and defaults to all five.
162
+
163
+ ### OHLC & ticks
164
+
165
+ ```ts
166
+ await client.getOhlc("EURUSD", {
167
+ timeframe: "H1",
168
+ from: "2026-05-01T00:00:00Z",
169
+ to: "2026-05-25T00:00:00Z",
170
+ limit: 500,
171
+ });
172
+
173
+ // Ticks require from/to (ISO 8601); the range must be <= 1 hour. Plan: pro/enterprise.
174
+ await client.getTicks("EURUSD", "2026-05-25T13:00:00Z", "2026-05-25T13:30:00Z");
175
+ ```
176
+
177
+ ### Indicators
178
+
179
+ ```ts
180
+ await client.getIndicator("EURUSD", "RSI_14", { timeframe: "H1" });
181
+ await client.getIndicators("EURUSD", { timeframe: "H1", category: "oscillator" });
182
+ await client.listIndicators(); // the full catalogue with descriptions
183
+
184
+ // History (plan: starter+)
185
+ await client.getIndicatorHistory("EURUSD", "RSI_14", { timeframe: "H1", limit: 500 });
186
+
187
+ // Batch across symbols (supplying `from` switches to historical mode, plan: starter+)
188
+ await client.getMulti(["EURUSD", "GBPUSD"], ["RSI_14", "ADX"], { timeframe: "H1" });
189
+ ```
190
+
191
+ ### Screener
192
+
193
+ ```ts
194
+ // Find oversold symbols by RSI. Note: minVal/maxVal -> min_val/max_val.
195
+ await client.screen("RSI_14", {
196
+ timeframe: "H1",
197
+ maxVal: 30,
198
+ sort: "asc",
199
+ offset: 0,
200
+ limit: 50,
201
+ });
202
+ ```
203
+
204
+ ### Summary, spread & sessions
205
+
206
+ ```ts
207
+ await client.getSummary("EURUSD", "H4"); // market-bias summary
208
+ await client.getSpread("EURUSD", "24h"); // spread statistics
209
+ await client.compareSpread(["EURUSD", "GBPUSD"], "24h");
210
+ await client.getSessions(); // market session clock
211
+ ```
212
+
213
+ ### Heatmap & calendar
214
+
215
+ ```ts
216
+ await client.getHeatmap({ type: "strength", timeframe: "H4" });
217
+ await client.getHeatmap({ correlations: true }); // correlation matrix
218
+
219
+ await client.getCalendar({
220
+ currencies: ["USD", "EUR"], // string or string[]
221
+ impact: "high",
222
+ nextHours: 24,
223
+ limit: 100,
224
+ });
225
+ ```
226
+
227
+ ### Account & layout
228
+
229
+ ```ts
230
+ await client.getAccount(); // { name, plan, prepaid_credits, daily_quota, daily_used }
231
+ await client.getLayout(); // saved dashboard layout (or null)
232
+
233
+ // ADVANCED / WRITE — overwrites the saved dashboard layout for this API key.
234
+ await client.saveLayout([{ widget: "quote", symbol: "EURUSD" }]);
235
+ ```
236
+
237
+ > `saveLayout` is the **only** mutating method in the SDK. Use it deliberately.
238
+
239
+ ### Health probe
240
+
241
+ ```ts
242
+ await client.health(); // { status, components: { redis, postgres } }
243
+ ```
244
+
245
+ ---
246
+
247
+ ## Error handling
248
+
249
+ Every failure throws a typed error. Catch the base class, or branch on specific subclasses / the stable `code` string.
250
+
251
+ ```ts
252
+ import {
253
+ TickAtlasError,
254
+ TickAtlasAPIError,
255
+ AuthenticationError,
256
+ PermissionDeniedError,
257
+ NotFoundError,
258
+ ValidationError,
259
+ RateLimitError,
260
+ ServerError,
261
+ TickAtlasNetworkError,
262
+ } from "tickatlas";
263
+
264
+ try {
265
+ const quote = await client.getQuote("NOPE");
266
+ } catch (e) {
267
+ if (e instanceof NotFoundError) {
268
+ console.error("Symbol not found:", e.code); // SYMBOL_NOT_FOUND
269
+ } else if (e instanceof RateLimitError) {
270
+ console.error(`Rate limited; retry after ${e.retryAfter}s`);
271
+ } else if (e instanceof TickAtlasAPIError) {
272
+ console.error(`API error ${e.statusCode} (${e.code}): ${e.message}`);
273
+ console.error("request id:", e.requestId);
274
+ console.error("context:", e.raw); // raw error object — read extra fields here
275
+ } else if (e instanceof TickAtlasNetworkError) {
276
+ console.error("Network/timeout failure:", e.isTimeout ? "timeout" : "connection");
277
+ }
278
+ }
279
+ ```
280
+
281
+ ### Exception hierarchy
282
+
283
+ ```
284
+ TickAtlasError base for everything
285
+ ├── TickAtlasAPIError server returned a structured error
286
+ │ │ .statusCode .code .message .details .requestId .raw
287
+ │ ├── AuthenticationError HTTP 401 (MISSING_API_KEY, INVALID_API_KEY)
288
+ │ ├── PermissionDeniedError HTTP 403 (API_KEY_DISABLED, PLAN_UPGRADE_REQUIRED, …)
289
+ │ ├── NotFoundError HTTP 404 (SYMBOL_NOT_FOUND, DATA_NOT_FOUND, INDICATOR_NOT_FOUND)
290
+ │ ├── ValidationError HTTP 400 & 422 (INVALID_*, RANGE_TOO_LARGE, VALIDATION_ERROR, …)
291
+ │ ├── RateLimitError HTTP 429 (RATE_LIMIT_EXCEEDED, QUOTA_EXCEEDED) — has .retryAfter
292
+ │ └── ServerError HTTP 5xx (INTERNAL_ERROR, SERVICE_UNAVAILABLE)
293
+ └── TickAtlasNetworkError no HTTP response (connection / timeout / DNS) — has .isTimeout
294
+ ```
295
+
296
+ `TickAtlasConfigError` (also extends `TickAtlasError`) is thrown at construction time for misconfiguration, e.g. a missing API key.
297
+
298
+ The `.raw` property exposes the full server `error` object, so you can read forward-compatible context fields (`valid_timeframes`, `required_plan`, `retention_period`, …) that aren't first-class properties.
299
+
300
+ ---
301
+
302
+ ## Retries & rate limiting
303
+
304
+ By default the client makes up to **4 attempts** (1 + `maxRetries`) and retries **only** on:
305
+
306
+ - `429` (rate limit / quota),
307
+ - `5xx` (server errors), and
308
+ - network / timeout errors.
309
+
310
+ All other errors fail fast. Backoff is **exponential with full jitter** (`base * 2^attempt`, capped at `backoffCap`). On `429`, the client honours the server's **`Retry-After`** header (falling back to `X-RateLimit-Reset`, then the body's `reset_in_seconds`) instead of the computed backoff. All `/v1` endpoints are read-only/idempotent (except the explicit `saveLayout` write), so retries are safe.
311
+
312
+ Tune the behaviour per client:
313
+
314
+ ```ts
315
+ const client = new TickAtlas({
316
+ apiKey: process.env.TICKATLAS_API_KEY,
317
+ timeout: 10_000,
318
+ maxRetries: 5,
319
+ backoffBase: 250,
320
+ });
321
+ ```
322
+
323
+ Each response also carries `X-RateLimit-Limit` / `-Remaining` / `-Reset` and an `X-Request-ID` (echoed on `TickAtlasAPIError.requestId`).
324
+
325
+ ---
326
+
327
+ ## Contributing
328
+
329
+ ```bash
330
+ git clone https://github.com/abuzant/tickatlas-sdk.git
331
+ cd tickatlas-sdk/javascript
332
+
333
+ npm install
334
+ npm run build # tsup -> dist/ (ESM .mjs + CJS .cjs + .d.ts)
335
+ npm run typecheck # tsc --noEmit
336
+ npm test # vitest (unit tests, fully mocked — no network)
337
+ ```
338
+
339
+ The unit suite mocks `fetch` and never touches the network. There is a separate, **read-only** integration suite that is **skipped** unless both `RUN_INTEGRATION=1` and `TICKATLAS_API_KEY` are set:
340
+
341
+ ```bash
342
+ RUN_INTEGRATION=1 TICKATLAS_API_KEY=tk_xxx npm run test:integration
343
+ ```
344
+
345
+ It never exercises the write endpoint (`saveLayout`). Please don't commit API keys.
346
+
347
+ ---
348
+
349
+ ## License
350
+
351
+ [MIT](./LICENSE) © TickAtlas