@livefolio/sdk 0.2.13 → 0.3.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.
Files changed (110) hide show
  1. package/README.md +277 -84
  2. package/dist/index.d.ts +397 -23
  3. package/dist/index.js +1375 -51
  4. package/dist/index.js.map +1 -1
  5. package/package.json +36 -35
  6. package/dist/auth/client.d.ts +0 -4
  7. package/dist/auth/client.d.ts.map +0 -1
  8. package/dist/auth/client.js +0 -37
  9. package/dist/auth/client.js.map +0 -1
  10. package/dist/auth/index.d.ts +0 -3
  11. package/dist/auth/index.d.ts.map +0 -1
  12. package/dist/auth/index.js +0 -6
  13. package/dist/auth/index.js.map +0 -1
  14. package/dist/auth/types.d.ts +0 -9
  15. package/dist/auth/types.d.ts.map +0 -1
  16. package/dist/auth/types.js +0 -3
  17. package/dist/auth/types.js.map +0 -1
  18. package/dist/index.d.ts.map +0 -1
  19. package/dist/market/client.d.ts +0 -4
  20. package/dist/market/client.d.ts.map +0 -1
  21. package/dist/market/client.js +0 -137
  22. package/dist/market/client.js.map +0 -1
  23. package/dist/market/index.d.ts +0 -5
  24. package/dist/market/index.d.ts.map +0 -1
  25. package/dist/market/index.js +0 -9
  26. package/dist/market/index.js.map +0 -1
  27. package/dist/market/trackedTickers.d.ts +0 -4
  28. package/dist/market/trackedTickers.d.ts.map +0 -1
  29. package/dist/market/trackedTickers.js +0 -256
  30. package/dist/market/trackedTickers.js.map +0 -1
  31. package/dist/market/types.d.ts +0 -29
  32. package/dist/market/types.d.ts.map +0 -1
  33. package/dist/market/types.js +0 -3
  34. package/dist/market/types.js.map +0 -1
  35. package/dist/portfolio/client.d.ts +0 -4
  36. package/dist/portfolio/client.d.ts.map +0 -1
  37. package/dist/portfolio/client.js +0 -13
  38. package/dist/portfolio/client.js.map +0 -1
  39. package/dist/portfolio/index.d.ts +0 -5
  40. package/dist/portfolio/index.d.ts.map +0 -1
  41. package/dist/portfolio/index.js +0 -26
  42. package/dist/portfolio/index.js.map +0 -1
  43. package/dist/portfolio/rebalance.d.ts +0 -62
  44. package/dist/portfolio/rebalance.d.ts.map +0 -1
  45. package/dist/portfolio/rebalance.js +0 -205
  46. package/dist/portfolio/rebalance.js.map +0 -1
  47. package/dist/portfolio/symbols.d.ts +0 -13
  48. package/dist/portfolio/symbols.d.ts.map +0 -1
  49. package/dist/portfolio/symbols.js +0 -112
  50. package/dist/portfolio/symbols.js.map +0 -1
  51. package/dist/portfolio/types.d.ts +0 -13
  52. package/dist/portfolio/types.d.ts.map +0 -1
  53. package/dist/portfolio/types.js +0 -3
  54. package/dist/portfolio/types.js.map +0 -1
  55. package/dist/strategy/backtest.d.ts +0 -6
  56. package/dist/strategy/backtest.d.ts.map +0 -1
  57. package/dist/strategy/backtest.js +0 -698
  58. package/dist/strategy/backtest.js.map +0 -1
  59. package/dist/strategy/cache.d.ts +0 -8
  60. package/dist/strategy/cache.d.ts.map +0 -1
  61. package/dist/strategy/cache.js +0 -336
  62. package/dist/strategy/cache.js.map +0 -1
  63. package/dist/strategy/client.d.ts +0 -4
  64. package/dist/strategy/client.d.ts.map +0 -1
  65. package/dist/strategy/client.js +0 -29
  66. package/dist/strategy/client.js.map +0 -1
  67. package/dist/strategy/evaluate.d.ts +0 -12
  68. package/dist/strategy/evaluate.d.ts.map +0 -1
  69. package/dist/strategy/evaluate.js +0 -562
  70. package/dist/strategy/evaluate.js.map +0 -1
  71. package/dist/strategy/get.d.ts +0 -5
  72. package/dist/strategy/get.d.ts.map +0 -1
  73. package/dist/strategy/get.js +0 -25
  74. package/dist/strategy/get.js.map +0 -1
  75. package/dist/strategy/index.d.ts +0 -14
  76. package/dist/strategy/index.d.ts.map +0 -1
  77. package/dist/strategy/index.js +0 -44
  78. package/dist/strategy/index.js.map +0 -1
  79. package/dist/strategy/livefolio.d.ts +0 -25
  80. package/dist/strategy/livefolio.d.ts.map +0 -1
  81. package/dist/strategy/livefolio.js +0 -67
  82. package/dist/strategy/livefolio.js.map +0 -1
  83. package/dist/strategy/performance.d.ts +0 -17
  84. package/dist/strategy/performance.d.ts.map +0 -1
  85. package/dist/strategy/performance.js +0 -57
  86. package/dist/strategy/performance.js.map +0 -1
  87. package/dist/strategy/rules.d.ts +0 -3
  88. package/dist/strategy/rules.d.ts.map +0 -1
  89. package/dist/strategy/rules.js +0 -95
  90. package/dist/strategy/rules.js.map +0 -1
  91. package/dist/strategy/stream.d.ts +0 -5
  92. package/dist/strategy/stream.d.ts.map +0 -1
  93. package/dist/strategy/stream.js +0 -58
  94. package/dist/strategy/stream.js.map +0 -1
  95. package/dist/strategy/symbols.d.ts +0 -4
  96. package/dist/strategy/symbols.d.ts.map +0 -1
  97. package/dist/strategy/symbols.js +0 -41
  98. package/dist/strategy/symbols.js.map +0 -1
  99. package/dist/strategy/time.d.ts +0 -10
  100. package/dist/strategy/time.d.ts.map +0 -1
  101. package/dist/strategy/time.js +0 -33
  102. package/dist/strategy/time.js.map +0 -1
  103. package/dist/strategy/types.d.ts +0 -200
  104. package/dist/strategy/types.d.ts.map +0 -1
  105. package/dist/strategy/types.js +0 -3
  106. package/dist/strategy/types.js.map +0 -1
  107. package/dist/types.d.ts +0 -4
  108. package/dist/types.d.ts.map +0 -1
  109. package/dist/types.js +0 -3
  110. package/dist/types.js.map +0 -1
package/README.md CHANGED
@@ -1,140 +1,333 @@
1
1
  # @livefolio/sdk
2
2
 
3
- Open-source TypeScript SDK for market data, deterministic strategy evaluation and backtesting, live signal streaming, and portfolio rebalance planning.
3
+ TypeScript SDK for building and backtesting trading strategies. Provides lazy handles for tickers and indicators that automatically fetch market data from Yahoo Finance and FRED, compute derived indicators, and cache results in a Supabase database.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- npm install @livefolio/sdk
8
+ npm install @livefolio/sdk @supabase/supabase-js
9
9
  ```
10
10
 
11
11
  ## Quick Start
12
12
 
13
13
  ```ts
14
- import { createLivefolioClient } from '@livefolio/sdk';
15
- import { createClient } from '@supabase/supabase-js';
14
+ import { createClient } from '@livefolio/sdk';
15
+ import { createClient as createSupabaseClient } from '@supabase/supabase-js';
16
16
 
17
- const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
18
- const livefolio = createLivefolioClient(supabase);
17
+ const supabase = createSupabaseClient(SUPABASE_URL, SUPABASE_KEY);
18
+
19
+ const sdk = createClient({
20
+ supabase,
21
+ fredApiKey: 'your-fred-api-key', // optional, required for treasury indicators
22
+ });
23
+
24
+ const spy = sdk.ticker('SPY');
25
+ const sma200 = sdk.sma(spy, 200);
26
+
27
+ // First call fetches SPY prices from Yahoo Finance, computes the 200-day SMA,
28
+ // stores everything in the database, and returns the result.
29
+ // Subsequent calls return cached data instantly.
30
+ const series = await sma200.series();
31
+ const latest = await sma200.value();
19
32
  ```
20
33
 
21
- ## Modules
34
+ ## Concepts
22
35
 
23
- | Module | Description |
24
- |--------|-------------|
25
- | `livefolio.auth` | Authentication (user, session, sign-out) |
26
- | `livefolio.market` | Historical series, real-time quotes, trading calendar |
27
- | `livefolio.strategy` | Strategy retrieval, cached evaluation, live streaming |
28
- | `livefolio.portfolio` | Brokerage account aggregation (stub) |
36
+ ### Lazy Handles
37
+
38
+ Everything in the SDK is a **lazy handle** -- a lightweight object that describes *what* you want without hitting the database or any external API. Data is only fetched when you call `.series()` or `.value()`.
29
39
 
30
40
  ```ts
31
- // Fetch and evaluate a strategy
32
- const strategy = await livefolio.strategy.get('abc-123');
33
- if (strategy) {
34
- const result = await livefolio.strategy.evaluate(strategy, new Date());
35
- console.log(result.allocation.name, result.allocation.holdings);
36
- }
41
+ const spy = sdk.ticker('SPY'); // no DB call
42
+ const sma200 = sdk.sma(spy, 200); // no DB call
43
+ const series = await sma200.series(); // NOW: resolve -> fetch -> compute -> return
44
+ ```
45
+
46
+ ### Automatic Sync
47
+
48
+ Indicator series data is fetched and computed transparently. When you call `.series()` or `.value()`:
49
+
50
+ 1. The indicator and its dependencies are resolved (upserted) in the database
51
+ 2. If the series is stale or missing, raw data is fetched from the appropriate source
52
+ 3. Derived indicators are computed from their dependencies
53
+ 4. Results are upserted to the database and cached in memory
54
+
55
+ Since market data is daily closing prices, data is immutable once the trading day closes. The SDK takes advantage of this for aggressive caching.
56
+
57
+ ## API Reference
58
+
59
+ ### `createClient(options)`
37
60
 
38
- // Market data
39
- const series = await livefolio.market.getBatchSeries(['SPY', 'BND']);
40
- const quote = await livefolio.market.getQuote('SPY');
61
+ ```ts
62
+ createClient({
63
+ supabase: SupabaseClient, // required
64
+ fredApiKey?: string, // required for treasury indicators
65
+ })
41
66
  ```
42
67
 
43
- Modules can also be imported individually:
68
+ Returns a `LivefolioClient` with the factory methods below.
69
+
70
+ ### Tickers
44
71
 
45
72
  ```ts
46
- import { createMarket } from '@livefolio/sdk/market';
47
- import { createStrategy } from '@livefolio/sdk/strategy';
73
+ sdk.ticker(symbol: string, leverage?: number)
48
74
  ```
49
75
 
50
- ## Documentation
76
+ Creates a `TickerHandle`. Leverage defaults to `1`.
77
+
78
+ ```ts
79
+ const spy = sdk.ticker('SPY');
80
+ const spxl = sdk.ticker('SPXL', 3);
81
+ ```
51
82
 
52
- Full method reference, arguments, return types, and usage examples: **[docs/](./docs/)**
83
+ ### Ticker-Bound Indicators
53
84
 
54
- ## What The SDK Supports Today
85
+ These require a `TickerHandle` and compute from that ticker's price history.
55
86
 
56
- The current public surface is strongest in four areas:
87
+ ```ts
88
+ sdk.sma(ticker, lookback, opts?) // Simple Moving Average
89
+ sdk.ema(ticker, lookback, opts?) // Exponential Moving Average
90
+ sdk.rsi(ticker, lookback, opts?) // Relative Strength Index
91
+ sdk.price(ticker, opts?) // Raw closing price
92
+ sdk.returns(ticker, lookback, opts?) // Period returns
93
+ sdk.volatility(ticker, lookback, opts?) // Rolling standard deviation
94
+ sdk.drawdown(ticker, lookback, opts?) // Drawdown from rolling max
95
+ ```
57
96
 
58
- - Market data: historical daily series, quotes, trading-day lookups, and tracked ticker helpers
59
- - Strategy engine: strategy retrieval, deterministic evaluation, pure evaluation helpers, live streaming updates, and rules compilation
60
- - Backtesting: DB-backed historical backtests plus performance metric helpers
61
- - Portfolio tooling: drift calculations, rebalance planning, and ticker-to-tradable mapping
97
+ `opts` is `{ delay?: number }` -- defaults to `0`.
62
98
 
63
- `livefolio.auth` is intentionally lightweight, and `livefolio.portfolio` is best described today as portfolio tooling rather than a full brokerage integration layer.
99
+ ```ts
100
+ const spy = sdk.ticker('SPY');
101
+ const sma200 = sdk.sma(spy, 200);
102
+ const rsi14 = sdk.rsi(spy, 14);
103
+ const delayed = sdk.sma(spy, 50, { delay: 1 });
104
+ ```
64
105
 
65
- ## Community Contribution Areas
106
+ ### Standalone Indicators
66
107
 
67
- Good areas for community contributions:
108
+ No ticker required. Data comes directly from external APIs.
68
109
 
69
- - New indicator types and strategy primitives
70
- - Additional market data adapters and quote providers
71
- - Backtest realism improvements such as fees, slippage, taxes, and execution assumptions
72
- - Portfolio and rebalancing logic, including tradable mappings and execution planning
73
- - Broker and custodian adapters
74
- - Examples, docs, notebooks, and app integrations
75
- - Performance, caching, and test coverage improvements
110
+ ```ts
111
+ sdk.vix(opts?) // CBOE Volatility Index
112
+ sdk.vix3m(opts?) // CBOE 3-Month Volatility Index
113
+ sdk.treasury(tenor, opts?) // Treasury rates (requires fredApiKey)
114
+ sdk.calendar(period, opts?) // Date components from trading calendar
115
+ sdk.threshold(value, unit?) // Constant value
116
+ ```
76
117
 
77
- If you are evaluating where to contribute first, the highest-leverage work is usually in `market/`, `strategy/`, `portfolio/rebalance`, and the docs/examples around those modules.
118
+ Treasury tenors: `'T3M'`, `'T6M'`, `'T1Y'`, `'T2Y'`, `'T3Y'`, `'T5Y'`, `'T7Y'`, `'T10Y'`, `'T20Y'`, `'T30Y'`
78
119
 
79
- ## Development
120
+ Calendar periods: `'Month'`, `'Day of Week'`, `'Day of Month'`, `'Day of Year'`
80
121
 
81
- ```bash
82
- npm install
83
- npm run build # compile TypeScript → dist/
84
- npm test # run tests
85
- npm run ingest:init # backfill price_observations from Yahoo
86
- npm run ingest:daily # refresh latest daily bars
87
- npm run backtest:smoke -- --linkId <strategy-link-id>
122
+ Threshold units: `'%'`, `'$'`, or omit for unitless.
123
+
124
+ ```ts
125
+ const vix = sdk.vix();
126
+ const t10y = sdk.treasury('T10Y');
127
+ const month = sdk.calendar('Month');
128
+ const half = sdk.threshold(0.5);
88
129
  ```
89
130
 
90
- ## Contributing Locally
131
+ ### Signals
91
132
 
92
- Most contributors only need the default local workflow:
133
+ Compare two indicators to create a boolean signal. Supports hysteresis via tolerance to reduce whipsawing.
93
134
 
94
- ```bash
95
- git clone https://github.com/livefolio/sdk.git
96
- cd sdk
97
- npm install
98
- npm run build
99
- npm test
135
+ ```ts
136
+ sdk.gt(ind1, ind2, tolerance?) // ind1 > ind2
137
+ sdk.lt(ind1, ind2, tolerance?) // ind1 < ind2
138
+ sdk.eq(ind1, ind2, tolerance?) // ind1 within tolerance range of ind2
100
139
  ```
101
140
 
102
- The test suite mocks Supabase at the boundary, so most SDK work does not require a running backend.
141
+ Tolerance defaults to `0` (no hysteresis). When set, a buffer zone prevents the signal from flipping until the indicator moves fully through the buffer.
103
142
 
104
- If you are working on market ingestion, DB-backed backtests, or cache-through evaluation flows, set local Supabase credentials first:
143
+ - **Relative tolerance** (Price, SMA, EMA, RSI, Threshold, Calendar): buffer = `ind2 * (1 +/- tolerance/100)`
144
+ - **Absolute tolerance** (Return, Volatility, Drawdown, VIX, VIX3M, Treasury): buffer = `ind2 +/- tolerance`
105
145
 
106
- ```bash
107
- export SUPABASE_URL=http://127.0.0.1:54321
108
- export SUPABASE_ANON_KEY=<local-anon-key>
109
- export SUPABASE_SERVICE_ROLE_KEY=<local-service-role-key>
146
+ ```ts
147
+ const spy = sdk.ticker('SPY');
148
+ const price = sdk.price(spy);
149
+ const sma200 = sdk.sma(spy, 200);
150
+
151
+ const bullish = sdk.gt(price, sma200, 5); // 5% tolerance
152
+
153
+ const series = await bullish.series(); // DailyBar[] with value 0 or 1
154
+ const current = await bullish.value(); // 0 or 1
110
155
  ```
111
156
 
112
- Then you can run the data-backed workflows:
157
+ Signal handles support the same `.series(range?)`, `.value(date?)`, and `.resolve()` methods as indicator handles. Data is automatically synced -- both underlying indicators are refreshed before computing the signal.
113
158
 
114
- ```bash
115
- npm run ingest:init
116
- npm run ingest:daily
117
- npm run backtest:smoke -- --linkId <strategy-link-id>
159
+ ### Allocations
160
+
161
+ Define portfolio holdings as weighted ticker pairs.
162
+
163
+ ```ts
164
+ sdk.allocation(...holdings: [TickerHandle, number][])
165
+ ```
166
+
167
+ Weights must sum to 1. Allocations are deduplicated by holdings -- creating the same allocation twice returns the same database row.
168
+
169
+ ```ts
170
+ const aggressive = sdk.allocation([spy, 0.75], [gld, 0.25]);
171
+ const defensive = sdk.allocation([shy, 1.0]);
172
+ ```
173
+
174
+ ### Strategies
175
+
176
+ Compose signals and allocations into a priority-ordered rule list evaluated on a rebalancing schedule.
177
+
178
+ ```ts
179
+ // Create a new strategy
180
+ sdk.strategy(options: StrategyOptions)
181
+
182
+ // Reference an existing strategy by link ID
183
+ sdk.strategy(linkId: string)
184
+ ```
185
+
186
+ Each rule's `when` array is AND-ed together. OR is expressed by having multiple rules point to the same allocation. The last rule must be a fallback with no `when` clause. Rules are evaluated top-to-bottom; first match wins.
187
+
188
+ ```ts
189
+ const spy = sdk.ticker('SPY');
190
+ const shy = sdk.ticker('SHY');
191
+ const price = sdk.price(spy);
192
+ const sma200 = sdk.sma(spy, 200);
193
+
194
+ const bullish = sdk.gt(price, sma200, 5);
195
+
196
+ const aggressive = sdk.allocation([spy, 1.0]);
197
+ const defensive = sdk.allocation([shy, 1.0]);
198
+
199
+ const strategy = sdk.strategy({
200
+ name: 'Tactical SPY/SHY',
201
+ freq: 'Monthly', // rebalance on last trading day of each month
202
+ offset: 0, // positive = earlier, negative = later
203
+ rules: [
204
+ { when: [bullish], hold: aggressive },
205
+ { hold: defensive }, // fallback
206
+ ],
207
+ });
208
+
209
+ const history = await strategy.series();
210
+ // StrategyBar[] — { date: string, allocation: AllocationHandle }
211
+
212
+ const current = await strategy.value();
213
+ // AllocationHandle for the latest trading day
214
+ ```
215
+
216
+ Trading frequencies: `'Daily'`, `'Weekly'`, `'Monthly'`, `'Bi-monthly'`, `'Quarterly'`, `'Every 4 Months'`, `'Semiannually'`, `'Yearly'`.
217
+
218
+ Strategy series are **dense** -- one row per trading day. On rebalance dates the rules are evaluated; on other days the previous allocation carries forward.
219
+
220
+ Each strategy gets a unique `link_id` (nanoid) on creation. Reference an existing strategy by its link ID to reload it without recreating.
221
+
222
+ ### Simulation
223
+
224
+ Run a portfolio simulation over a date range. Returns a `SimulationHandle` with the equity curve and trade history.
225
+
226
+ ```ts
227
+ const spy = sdk.ticker('SPY');
228
+ const cashx = sdk.ticker('CASHX');
229
+
230
+ const startingPortfolio = sdk.portfolio([cashx, 100_000]);
231
+
232
+ const sim = await strategy.simulate({ from: '2020-01-01', to: '2025-12-31', portfolio: startingPortfolio });
233
+
234
+ sim.series // DailyBar[] — portfolio value per trading day
235
+ sim.trades // Trade[] — every buy/sell event
236
+ sim.startingPortfolio // PortfolioHandle — starting positions
237
+ ```
238
+
239
+ You can also start from existing positions:
240
+
241
+ ```ts
242
+ const existingPortfolio = sdk.portfolio([spy, 100], [cashx, 5000]);
243
+ const sim = await strategy.simulate({ from: '2024-01-01', to: '2025-12-31', portfolio: existingPortfolio });
244
+ ```
245
+
246
+ The simulator rebalances at the strategy's `freq` cadence, fetches price data for all tickers in all allocations automatically, and tracks positions and cash through each trading day.
247
+
248
+ ```ts
249
+ interface Trade {
250
+ date: string;
251
+ symbol: string;
252
+ quantity: number; // number of shares traded
253
+ price: number;
254
+ action: 'buy' | 'sell';
255
+ }
256
+ ```
257
+
258
+ Agents compute whatever derived metrics they need (CAGR, Sharpe, drawdown, etc.) from the raw data:
259
+
260
+ ```ts
261
+ const values = sim.series.map(b => b.value);
262
+ const dailyReturns = values.slice(1).map((v, i) => (v - values[i]) / values[i]);
263
+ ```
264
+
265
+ ### Handle Methods
266
+
267
+ Every `IndicatorHandle`, `SignalHandle`, and `StrategyHandle` exposes:
268
+
269
+ #### `.series(range?)`
270
+
271
+ Returns the full time series. For indicators and signals this is `DailyBar[]`; for strategies it is `StrategyBar[]`.
272
+
273
+ ```ts
274
+ interface DailyBar {
275
+ date: string; // 'YYYY-MM-DD'
276
+ value: number;
277
+ }
278
+
279
+ interface StrategyBar {
280
+ date: string;
281
+ allocation: AllocationHandle;
282
+ }
283
+
284
+ const all = await sma200.series();
285
+ const subset = await sma200.series({ from: '2024-01-01', to: '2024-12-31' });
286
+ ```
287
+
288
+ #### `.value(date?)`
289
+
290
+ Returns the latest value, or the value for a specific date. Returns `null` if no data exists. For strategies, returns `AllocationHandle | null`.
291
+
292
+ ```ts
293
+ const latest = await sma200.value();
294
+ const specific = await sma200.value('2024-06-15');
295
+ ```
296
+
297
+ #### `.resolve()`
298
+
299
+ Explicitly upserts the indicator to the database and returns the row. Normally you don't need to call this -- `.series()` and `.value()` call it automatically.
300
+
301
+ ```ts
302
+ const row = await sma200.resolve();
303
+ console.log(row.id); // database ID
118
304
  ```
119
305
 
120
- Contributor expectations:
306
+ ## Data Sources
121
307
 
122
- - Run `npm run build` and `npm test` before opening a PR
123
- - Add unit tests for new exported behavior
124
- - Update `docs/` when adding or changing public module methods
125
- - Prefer narrow deterministic tests over large integration-only coverage
308
+ | Indicator Type | Source |
309
+ |---|---|
310
+ | Price, VIX, VIX3M | Yahoo Finance |
311
+ | Treasury rates (T3M--T30Y) | FRED API |
312
+ | SMA, EMA, RSI, Returns, Volatility, Drawdown | Computed from Price |
313
+ | Calendar (Month, Day of Week, etc.) | Computed from trading days |
314
+ | Threshold | Constant (no external data) |
126
315
 
127
- ## Open Source Scope
316
+ ## Database
128
317
 
129
- The SDK is intended for developers building transparent, rules-based investing software. Community maintainers can use it to:
318
+ The SDK uses Supabase as its backing store. Schema files are in `supabase/schemas/`. Key tables:
130
319
 
131
- - Fetch market data and trading calendars
132
- - Define, compile, evaluate, and stream rule-based strategies
133
- - Run deterministic backtests over historical data
134
- - Compute portfolio drift and generate rebalance plans
135
- - Integrate Livefolio-compatible strategy definitions into custom apps and research workflows
320
+ - `trading_days` -- market calendar with session timestamps
321
+ - `tickers` -- symbols with leverage multiplier
322
+ - `indicators` -- indicator definitions (type, params, ticker reference)
323
+ - `indicators_series` -- daily indicator values linked to trading days
324
+ - `signals` -- signal definitions (two indicators, comparison, tolerance)
325
+ - `signals_series` -- daily boolean signal values linked to trading days
326
+ - `allocations` -- portfolio holdings as JSONB (deduplicated)
327
+ - `strategies` -- strategy definitions with rebalance frequency and rule JSONB
328
+ - `strategies_series` -- active allocation per trading day per strategy (dense)
136
329
 
137
- PRs to `main` run build + tests and enforce a version bump. Merges auto-publish to npm.
330
+ Run `supabase db reset` to set up the local database from the schema and seed files.
138
331
 
139
332
  ## License
140
333