@livefolio/sdk 0.2.0 → 0.2.3

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 (44) hide show
  1. package/README.md +20 -146
  2. package/dist/portfolio/client.d.ts.map +1 -1
  3. package/dist/portfolio/client.js +7 -1
  4. package/dist/portfolio/client.js.map +1 -1
  5. package/dist/portfolio/index.d.ts +2 -0
  6. package/dist/portfolio/index.d.ts.map +1 -1
  7. package/dist/portfolio/index.js +21 -1
  8. package/dist/portfolio/index.js.map +1 -1
  9. package/dist/portfolio/rebalance.d.ts +62 -0
  10. package/dist/portfolio/rebalance.d.ts.map +1 -0
  11. package/dist/portfolio/rebalance.js +205 -0
  12. package/dist/portfolio/rebalance.js.map +1 -0
  13. package/dist/portfolio/symbols.d.ts +13 -0
  14. package/dist/portfolio/symbols.d.ts.map +1 -0
  15. package/dist/portfolio/symbols.js +112 -0
  16. package/dist/portfolio/symbols.js.map +1 -0
  17. package/dist/portfolio/types.d.ts +10 -0
  18. package/dist/portfolio/types.d.ts.map +1 -1
  19. package/dist/strategy/backtest.d.ts +3 -0
  20. package/dist/strategy/backtest.d.ts.map +1 -0
  21. package/dist/strategy/backtest.js +7 -0
  22. package/dist/strategy/backtest.js.map +1 -0
  23. package/dist/strategy/cache.d.ts +7 -0
  24. package/dist/strategy/cache.d.ts.map +1 -0
  25. package/dist/strategy/cache.js +312 -0
  26. package/dist/strategy/cache.js.map +1 -0
  27. package/dist/strategy/client.d.ts.map +1 -1
  28. package/dist/strategy/client.js +9 -333
  29. package/dist/strategy/client.js.map +1 -1
  30. package/dist/strategy/get.d.ts +5 -0
  31. package/dist/strategy/get.d.ts.map +1 -0
  32. package/dist/strategy/get.js +25 -0
  33. package/dist/strategy/get.js.map +1 -0
  34. package/dist/strategy/index.d.ts +5 -1
  35. package/dist/strategy/index.d.ts.map +1 -1
  36. package/dist/strategy/index.js +10 -1
  37. package/dist/strategy/index.js.map +1 -1
  38. package/dist/strategy/stream.d.ts +5 -0
  39. package/dist/strategy/stream.d.ts.map +1 -0
  40. package/dist/strategy/stream.js +58 -0
  41. package/dist/strategy/stream.js.map +1 -0
  42. package/dist/strategy/types.d.ts +6 -0
  43. package/dist/strategy/types.d.ts.map +1 -1
  44. package/package.json +1 -1
package/README.md CHANGED
@@ -8,9 +8,7 @@ TypeScript SDK for market data, strategy evaluation, and portfolio management.
8
8
  npm install @livefolio/sdk
9
9
  ```
10
10
 
11
- ## Getting Started
12
-
13
- All modules are created from a single Supabase client:
11
+ ## Quick Start
14
12
 
15
13
  ```ts
16
14
  import { createLivefolioClient } from '@livefolio/sdk';
@@ -20,171 +18,47 @@ const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
20
18
  const livefolio = createLivefolioClient(supabase);
21
19
  ```
22
20
 
23
- Individual modules can also be imported directly:
24
-
25
- ```ts
26
- import { createMarket } from '@livefolio/sdk/market';
27
- import { createAuth } from '@livefolio/sdk/auth';
28
- import { createStrategy } from '@livefolio/sdk/strategy';
29
- ```
30
-
31
- ---
32
-
33
- ## Auth
34
-
35
- Wraps Supabase Auth with a simplified interface.
36
-
37
- | Method | Returns | Description |
38
- |--------|---------|-------------|
39
- | `getUser()` | `User \| null` | Get the current authenticated user, or `null` if not signed in |
40
- | `getSession()` | `Session \| null` | Get the current session |
41
- | `requireUser()` | `User` | Get the current user or throw if not authenticated |
42
- | `onAuthStateChange(callback)` | `Subscription` | Subscribe to auth state changes (sign in, sign out, token refresh) |
43
- | `signOut()` | `void` | Sign out the current user |
44
-
45
- ```ts
46
- const user = await livefolio.auth.getUser();
47
-
48
- const subscription = livefolio.auth.onAuthStateChange((event, session) => {
49
- console.log(event, session);
50
- });
51
- ```
52
-
53
- ---
54
-
55
- ## Market
56
-
57
- Provides historical price series, real-time quotes, and the trading calendar. Series and quotes are served through Supabase Edge Functions with transparent cache-through behavior — the SDK requests data and caching is handled server-side.
58
-
59
- ### Series
60
-
61
- Retrieve historical daily observations for one or more symbols. Each observation has an ISO 8601 `timestamp` (market close) and a `value` (closing price).
62
-
63
- ```ts
64
- const spy = await livefolio.market.getSeries('SPY');
65
- // spy: Observation[] — [{ timestamp: '2025-01-10T21:00:00Z', value: 590.25 }, ...]
66
-
67
- const batch = await livefolio.market.getBatchSeries(['SPY', 'QQQ', 'TLT']);
68
- // batch: Record<string, Observation[]> — { SPY: [...], QQQ: [...], TLT: [...] }
69
- ```
70
-
71
- ### Quotes
21
+ ## Modules
72
22
 
73
- Retrieve the latest real-time quote for one or more symbols.
74
-
75
- ```ts
76
- const quote = await livefolio.market.getQuote('SPY');
77
- // quote: Observation { timestamp: '2025-03-02T21:00:00Z', value: 592.10 }
78
-
79
- const quotes = await livefolio.market.getBatchQuotes(['SPY', 'QQQ']);
80
- // quotes: Record<string, Observation>
81
- ```
82
-
83
- ### Trading Calendar
84
-
85
- Query the trading calendar for market open/close times. Dates are `YYYY-MM-DD` strings.
86
-
87
- ```ts
88
- const days = await livefolio.market.getTradingDays('2025-01-01', '2025-12-31');
89
- // days: TradingDay[] — [{ date, open, close, extended_open, extended_close }, ...]
90
-
91
- const today = await livefolio.market.getTradingDay('2025-03-02');
92
- // today: TradingDay | null
93
- ```
94
-
95
- ---
96
-
97
- ## Strategy
98
-
99
- Handles strategy retrieval, evaluation, and result caching. The module exposes two evaluation paths:
100
-
101
- - **`evaluate()`** (async) — the primary API. Checks the DB cache for a prior result, returns it on hit, or runs a fresh evaluation and stores the result on miss. Consumers never need to manage caching manually.
102
- - **Pure functions** (sync) — `evaluateIndicator`, `evaluateSignal`, `evaluateAllocation` for direct computation without DB interaction. Useful for testing, debugging, or custom pipelines.
103
-
104
- ### Retrieving Strategies
105
-
106
- Fetch a fully-resolved strategy by its link ID. The `get()` method calls the `strategy` edge function, which looks up the strategy in the database and auto-imports from testfol.io if not found. The returned `Strategy` object contains all named signals, indicators, condition trees, and allocations ready for evaluation.
107
-
108
- ```ts
109
- const strategy = await livefolio.strategy.get('abc-123');
110
- // strategy: Strategy | null
111
-
112
- const strategies = await livefolio.strategy.getMany(['abc-123', 'def-456']);
113
- // strategies: Record<string, Strategy>
114
- ```
115
-
116
- ### Evaluating a Strategy
117
-
118
- The `evaluate` method is the standard way to run a strategy. Pass a strategy and a date — everything else is handled internally:
119
-
120
- 1. Fetches historical series for all symbols used by the strategy
121
- 2. Computes the evaluation date
122
- 3. Checks the DB cache for a prior result on that trading day
123
- 4. On cache miss: fetches prior signal states (for hysteresis) and indicator metadata (for incremental EMA/RSI), runs the evaluation, and stores the result
124
- 5. On cache hit: returns the cached allocation and signal states
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) |
125
29
 
126
30
  ```ts
31
+ // Fetch and evaluate a strategy
127
32
  const strategy = await livefolio.strategy.get('abc-123');
128
33
  if (strategy) {
129
34
  const result = await livefolio.strategy.evaluate(strategy, new Date());
130
-
131
- result.allocation; // the winning allocation (name, holdings)
132
- result.asOf; // Date the evaluation corresponds to (trading day market close)
133
- result.signals; // Record<string, boolean> — all signal states
134
- result.indicators; // Record<string, IndicatorEvaluation> — all indicator values
35
+ console.log(result.allocation.name, result.allocation.holdings);
135
36
  }
136
- ```
137
37
 
138
- ### Utilities
139
-
140
- | Method | Returns | Description |
141
- |--------|---------|-------------|
142
- | `extractSymbols(strategy)` | `string[]` | Collect all ticker symbols needed to evaluate a strategy (from signals + holdings) |
143
- | `getEvaluationDate(trading, options)` | `Date` | Compute the evaluation date — requires `EvaluationOptions` with series data |
144
-
145
- ### Pure Evaluation Functions
38
+ // Market data
39
+ const series = await livefolio.market.getBatchSeries(['SPY', 'BND']);
40
+ const quote = await livefolio.market.getQuote('SPY');
41
+ ```
146
42
 
147
- For advanced use cases (testing, backtesting, custom pipelines), the pure evaluation functions operate synchronously without any DB interaction. They accept `EvaluationOptions` with `batchSeries` and optionally `previousSignalStates` and `previousIndicatorMetadata`.
43
+ Modules can also be imported individually:
148
44
 
149
45
  ```ts
150
- import { evaluate, evaluateIndicator, evaluateSignal } from '@livefolio/sdk/strategy';
151
-
152
- // Evaluate a single indicator
153
- const smaResult = livefolio.strategy.evaluateIndicator(indicator, options);
154
-
155
- // Evaluate a signal (comparison of two indicators)
156
- const isAbove = livefolio.strategy.evaluateSignal(signal, options);
157
-
158
- // Evaluate a full allocation condition tree
159
- const matches = livefolio.strategy.evaluateAllocation(allocation, options);
46
+ import { createMarket } from '@livefolio/sdk/market';
47
+ import { createStrategy } from '@livefolio/sdk/strategy';
160
48
  ```
161
49
 
162
- ---
163
-
164
- ## Portfolio
50
+ ## Documentation
165
51
 
166
- Brokerage account aggregation and trade order management. This module is planned but not yet implemented.
167
-
168
- ---
52
+ Full method reference, arguments, return types, and usage examples: **[docs/](./docs/)**
169
53
 
170
54
  ## Development
171
55
 
172
56
  ```bash
173
- npm install
174
57
  npm run build # compile TypeScript → dist/
175
58
  npm test # run tests
176
59
  ```
177
60
 
178
- ## CI/CD
179
-
180
- - PRs to `main` run build + tests and enforce a version bump
181
- - Merges to `main` auto-publish to npm and create a GitHub release
182
-
183
- Before merging, bump the version:
184
-
185
- ```bash
186
- npm version patch
187
- ```
61
+ PRs to `main` run build + tests and enforce a version bump. Merges auto-publish to npm.
188
62
 
189
63
  ## License
190
64
 
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/portfolio/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,wBAAgB,eAAe,CAAC,OAAO,EAAE,mBAAmB,GAAG,eAAe,CAE7E"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/portfolio/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI/C,wBAAgB,eAAe,CAAC,OAAO,EAAE,mBAAmB,GAAG,eAAe,CAM7E"}
@@ -1,7 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createPortfolio = createPortfolio;
4
+ const rebalance_1 = require("./rebalance");
5
+ const symbols_1 = require("./symbols");
4
6
  function createPortfolio(_client) {
5
- return {};
7
+ return {
8
+ buildRebalancePlan: rebalance_1.buildRebalancePlan,
9
+ computePortfolioDriftPercentPoints: rebalance_1.computePortfolioDriftPercentPoints,
10
+ mapTickerToTradable: symbols_1.mapTickerToTradable,
11
+ };
6
12
  }
7
13
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/portfolio/client.ts"],"names":[],"mappings":";;AAGA,0CAEC;AAFD,SAAgB,eAAe,CAAC,OAA4B;IAC1D,OAAO,EAAE,CAAC;AACZ,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/portfolio/client.ts"],"names":[],"mappings":";;AAKA,0CAMC;AATD,2CAAqF;AACrF,uCAAgD;AAEhD,SAAgB,eAAe,CAAC,OAA4B;IAC1D,OAAO;QACL,kBAAkB,EAAlB,8BAAkB;QAClB,kCAAkC,EAAlC,8CAAkC;QAClC,mBAAmB,EAAnB,6BAAmB;KACpB,CAAC;AACJ,CAAC"}
@@ -1,3 +1,5 @@
1
1
  export type { PortfolioModule } from './types';
2
2
  export { createPortfolio } from './client';
3
+ export { buildRebalancePlan, computePortfolioDriftPercentPoints, type TradeOrder, type RebalancePlanInput, type RebalancePlan, PORTFOLIO_DRIFT_THRESHOLD_PERCENT_POINTS, REBALANCE_MIN_TRADE_VALUE, REBALANCE_QUANTITY_PRECISION, REBALANCE_WHOLE_SHARE_QUANTITY_PRECISION, REBALANCE_EPSILON, REBALANCE_CASH_RESERVE_PERCENT, REBALANCE_MIN_CASH_RESERVE_VALUE, REBALANCE_BUY_SLIPPAGE_BPS, REBALANCE_SELL_SLIPPAGE_BPS, REBALANCE_PER_ORDER_FEE, REBALANCE_CASH_SYMBOL, REBALANCE_CASH_SOURCE_SYMBOL, } from './rebalance';
4
+ export { mapTickerToTradable, FRED_TRADABLE_MAP, BASE_TICKER_ALIASES, ETF_LEVERAGE_MAP, } from './symbols';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/portfolio/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/portfolio/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EACL,kBAAkB,EAClB,kCAAkC,EAClC,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,wCAAwC,EACxC,yBAAyB,EACzB,4BAA4B,EAC5B,wCAAwC,EACxC,iBAAiB,EACjB,8BAA8B,EAC9B,gCAAgC,EAChC,0BAA0B,EAC1B,2BAA2B,EAC3B,uBAAuB,EACvB,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,WAAW,CAAC"}
@@ -1,6 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createPortfolio = void 0;
3
+ exports.ETF_LEVERAGE_MAP = exports.BASE_TICKER_ALIASES = exports.FRED_TRADABLE_MAP = exports.mapTickerToTradable = exports.REBALANCE_CASH_SOURCE_SYMBOL = exports.REBALANCE_CASH_SYMBOL = exports.REBALANCE_PER_ORDER_FEE = exports.REBALANCE_SELL_SLIPPAGE_BPS = exports.REBALANCE_BUY_SLIPPAGE_BPS = exports.REBALANCE_MIN_CASH_RESERVE_VALUE = exports.REBALANCE_CASH_RESERVE_PERCENT = exports.REBALANCE_EPSILON = exports.REBALANCE_WHOLE_SHARE_QUANTITY_PRECISION = exports.REBALANCE_QUANTITY_PRECISION = exports.REBALANCE_MIN_TRADE_VALUE = exports.PORTFOLIO_DRIFT_THRESHOLD_PERCENT_POINTS = exports.computePortfolioDriftPercentPoints = exports.buildRebalancePlan = exports.createPortfolio = void 0;
4
4
  var client_1 = require("./client");
5
5
  Object.defineProperty(exports, "createPortfolio", { enumerable: true, get: function () { return client_1.createPortfolio; } });
6
+ var rebalance_1 = require("./rebalance");
7
+ Object.defineProperty(exports, "buildRebalancePlan", { enumerable: true, get: function () { return rebalance_1.buildRebalancePlan; } });
8
+ Object.defineProperty(exports, "computePortfolioDriftPercentPoints", { enumerable: true, get: function () { return rebalance_1.computePortfolioDriftPercentPoints; } });
9
+ Object.defineProperty(exports, "PORTFOLIO_DRIFT_THRESHOLD_PERCENT_POINTS", { enumerable: true, get: function () { return rebalance_1.PORTFOLIO_DRIFT_THRESHOLD_PERCENT_POINTS; } });
10
+ Object.defineProperty(exports, "REBALANCE_MIN_TRADE_VALUE", { enumerable: true, get: function () { return rebalance_1.REBALANCE_MIN_TRADE_VALUE; } });
11
+ Object.defineProperty(exports, "REBALANCE_QUANTITY_PRECISION", { enumerable: true, get: function () { return rebalance_1.REBALANCE_QUANTITY_PRECISION; } });
12
+ Object.defineProperty(exports, "REBALANCE_WHOLE_SHARE_QUANTITY_PRECISION", { enumerable: true, get: function () { return rebalance_1.REBALANCE_WHOLE_SHARE_QUANTITY_PRECISION; } });
13
+ Object.defineProperty(exports, "REBALANCE_EPSILON", { enumerable: true, get: function () { return rebalance_1.REBALANCE_EPSILON; } });
14
+ Object.defineProperty(exports, "REBALANCE_CASH_RESERVE_PERCENT", { enumerable: true, get: function () { return rebalance_1.REBALANCE_CASH_RESERVE_PERCENT; } });
15
+ Object.defineProperty(exports, "REBALANCE_MIN_CASH_RESERVE_VALUE", { enumerable: true, get: function () { return rebalance_1.REBALANCE_MIN_CASH_RESERVE_VALUE; } });
16
+ Object.defineProperty(exports, "REBALANCE_BUY_SLIPPAGE_BPS", { enumerable: true, get: function () { return rebalance_1.REBALANCE_BUY_SLIPPAGE_BPS; } });
17
+ Object.defineProperty(exports, "REBALANCE_SELL_SLIPPAGE_BPS", { enumerable: true, get: function () { return rebalance_1.REBALANCE_SELL_SLIPPAGE_BPS; } });
18
+ Object.defineProperty(exports, "REBALANCE_PER_ORDER_FEE", { enumerable: true, get: function () { return rebalance_1.REBALANCE_PER_ORDER_FEE; } });
19
+ Object.defineProperty(exports, "REBALANCE_CASH_SYMBOL", { enumerable: true, get: function () { return rebalance_1.REBALANCE_CASH_SYMBOL; } });
20
+ Object.defineProperty(exports, "REBALANCE_CASH_SOURCE_SYMBOL", { enumerable: true, get: function () { return rebalance_1.REBALANCE_CASH_SOURCE_SYMBOL; } });
21
+ var symbols_1 = require("./symbols");
22
+ Object.defineProperty(exports, "mapTickerToTradable", { enumerable: true, get: function () { return symbols_1.mapTickerToTradable; } });
23
+ Object.defineProperty(exports, "FRED_TRADABLE_MAP", { enumerable: true, get: function () { return symbols_1.FRED_TRADABLE_MAP; } });
24
+ Object.defineProperty(exports, "BASE_TICKER_ALIASES", { enumerable: true, get: function () { return symbols_1.BASE_TICKER_ALIASES; } });
25
+ Object.defineProperty(exports, "ETF_LEVERAGE_MAP", { enumerable: true, get: function () { return symbols_1.ETF_LEVERAGE_MAP; } });
6
26
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/portfolio/index.ts"],"names":[],"mappings":";;;AACA,mCAA2C;AAAlC,yGAAA,eAAe,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/portfolio/index.ts"],"names":[],"mappings":";;;AACA,mCAA2C;AAAlC,yGAAA,eAAe,OAAA;AACxB,yCAkBqB;AAjBnB,+GAAA,kBAAkB,OAAA;AAClB,+HAAA,kCAAkC,OAAA;AAIlC,qIAAA,wCAAwC,OAAA;AACxC,sHAAA,yBAAyB,OAAA;AACzB,yHAAA,4BAA4B,OAAA;AAC5B,qIAAA,wCAAwC,OAAA;AACxC,8GAAA,iBAAiB,OAAA;AACjB,2HAAA,8BAA8B,OAAA;AAC9B,6HAAA,gCAAgC,OAAA;AAChC,uHAAA,0BAA0B,OAAA;AAC1B,wHAAA,2BAA2B,OAAA;AAC3B,oHAAA,uBAAuB,OAAA;AACvB,kHAAA,qBAAqB,OAAA;AACrB,yHAAA,4BAA4B,OAAA;AAE9B,qCAKmB;AAJjB,8GAAA,mBAAmB,OAAA;AACnB,4GAAA,iBAAiB,OAAA;AACjB,8GAAA,mBAAmB,OAAA;AACnB,2GAAA,gBAAgB,OAAA"}
@@ -0,0 +1,62 @@
1
+ export interface TradeOrder {
2
+ action: 'BUY' | 'SELL';
3
+ symbol: string;
4
+ quantity: number;
5
+ estimatedPrice: number;
6
+ estimatedValue: number;
7
+ }
8
+ export interface RebalancePlanInput {
9
+ targetWeights: Record<string, number>;
10
+ currentValues: Record<string, number>;
11
+ prices: Record<string, number>;
12
+ quantityPrecisionBySymbol?: Record<string, number>;
13
+ cashValue: number;
14
+ totalValue: number;
15
+ portfolioDriftThresholdPercentPoints?: number;
16
+ minTradeValue?: number;
17
+ cashReservePercent?: number;
18
+ minCashReserveValue?: number;
19
+ buySlippageBps?: number;
20
+ sellSlippageBps?: number;
21
+ perOrderFee?: number;
22
+ /** When set, this symbol is treated as cash: no orders, no price required. */
23
+ cashSymbol?: string;
24
+ }
25
+ export interface RebalancePlan {
26
+ triggered: boolean;
27
+ portfolioDriftPercentPoints: number;
28
+ reason: 'below_threshold' | 'ok' | 'no_orders';
29
+ orders: TradeOrder[];
30
+ }
31
+ /** Minimum portfolio drift (in percentage points) required before rebalancing is triggered. */
32
+ export declare const PORTFOLIO_DRIFT_THRESHOLD_PERCENT_POINTS = 25;
33
+ /** Ignore tiny deltas below this notional value to avoid dust trades. */
34
+ export declare const REBALANCE_MIN_TRADE_VALUE = 1;
35
+ /** Default quantity precision for fractionable symbols (100 = 2 decimal places). */
36
+ export declare const REBALANCE_QUANTITY_PRECISION = 100;
37
+ /** Quantity precision for whole-share-only symbols (1 = integer shares only). */
38
+ export declare const REBALANCE_WHOLE_SHARE_QUANTITY_PRECISION = 1;
39
+ /** Numeric tolerance for floating-point comparisons. */
40
+ export declare const REBALANCE_EPSILON = 1e-9;
41
+ /** Keep this % of cash unallocated as a safety reserve. */
42
+ export declare const REBALANCE_CASH_RESERVE_PERCENT = 1;
43
+ /** Absolute minimum dollar reserve kept as cash. */
44
+ export declare const REBALANCE_MIN_CASH_RESERVE_VALUE = 10;
45
+ /** Inflate buy pricing by this many basis points when sizing orders. */
46
+ export declare const REBALANCE_BUY_SLIPPAGE_BPS = 30;
47
+ /** Haircut expected sell proceeds by this many basis points when funding buys. */
48
+ export declare const REBALANCE_SELL_SLIPPAGE_BPS = 20;
49
+ /** Flat per-order fee assumption used in funding math. */
50
+ export declare const REBALANCE_PER_ORDER_FEE = 0;
51
+ /** Canonical symbol used in targetWeights when strategy holds cash. */
52
+ export declare const REBALANCE_CASH_SYMBOL = "CASH";
53
+ /** Strategy ticker that means "hold as cash". */
54
+ export declare const REBALANCE_CASH_SOURCE_SYMBOL = "DTB3";
55
+ export declare function computePortfolioDriftPercentPoints(input: {
56
+ targetWeights: Record<string, number>;
57
+ currentValues: Record<string, number>;
58
+ cashValue: number;
59
+ totalValue: number;
60
+ }): number;
61
+ export declare function buildRebalancePlan(input: RebalancePlanInput): RebalancePlan;
62
+ //# sourceMappingURL=rebalance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebalance.d.ts","sourceRoot":"","sources":["../../src/portfolio/rebalance.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,yBAAyB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC,CAAC,EAAE,MAAM,CAAC;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,2BAA2B,EAAE,MAAM,CAAC;IACpC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,WAAW,CAAC;IAC/C,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAMD,+FAA+F;AAC/F,eAAO,MAAM,wCAAwC,KAAK,CAAC;AAC3D,yEAAyE;AACzE,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAC3C,oFAAoF;AACpF,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,iFAAiF;AACjF,eAAO,MAAM,wCAAwC,IAAI,CAAC;AAC1D,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,2DAA2D;AAC3D,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,oDAAoD;AACpD,eAAO,MAAM,gCAAgC,KAAK,CAAC;AACnD,wEAAwE;AACxE,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAC7C,kFAAkF;AAClF,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAC9C,0DAA0D;AAC1D,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,uEAAuE;AACvE,eAAO,MAAM,qBAAqB,SAAS,CAAC;AAC5C,iDAAiD;AACjD,eAAO,MAAM,4BAA4B,SAAS,CAAC;AAsCnD,wBAAgB,kCAAkC,CAAC,KAAK,EAAE;IACxD,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CAiCT;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa,CAoI3E"}
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.REBALANCE_CASH_SOURCE_SYMBOL = exports.REBALANCE_CASH_SYMBOL = exports.REBALANCE_PER_ORDER_FEE = exports.REBALANCE_SELL_SLIPPAGE_BPS = exports.REBALANCE_BUY_SLIPPAGE_BPS = exports.REBALANCE_MIN_CASH_RESERVE_VALUE = exports.REBALANCE_CASH_RESERVE_PERCENT = exports.REBALANCE_EPSILON = exports.REBALANCE_WHOLE_SHARE_QUANTITY_PRECISION = exports.REBALANCE_QUANTITY_PRECISION = exports.REBALANCE_MIN_TRADE_VALUE = exports.PORTFOLIO_DRIFT_THRESHOLD_PERCENT_POINTS = void 0;
4
+ exports.computePortfolioDriftPercentPoints = computePortfolioDriftPercentPoints;
5
+ exports.buildRebalancePlan = buildRebalancePlan;
6
+ // ---------------------------------------------------------------------------
7
+ // Constants (exported so consumers can import defaults)
8
+ // ---------------------------------------------------------------------------
9
+ /** Minimum portfolio drift (in percentage points) required before rebalancing is triggered. */
10
+ exports.PORTFOLIO_DRIFT_THRESHOLD_PERCENT_POINTS = 25;
11
+ /** Ignore tiny deltas below this notional value to avoid dust trades. */
12
+ exports.REBALANCE_MIN_TRADE_VALUE = 1;
13
+ /** Default quantity precision for fractionable symbols (100 = 2 decimal places). */
14
+ exports.REBALANCE_QUANTITY_PRECISION = 100;
15
+ /** Quantity precision for whole-share-only symbols (1 = integer shares only). */
16
+ exports.REBALANCE_WHOLE_SHARE_QUANTITY_PRECISION = 1;
17
+ /** Numeric tolerance for floating-point comparisons. */
18
+ exports.REBALANCE_EPSILON = 1e-9;
19
+ /** Keep this % of cash unallocated as a safety reserve. */
20
+ exports.REBALANCE_CASH_RESERVE_PERCENT = 1;
21
+ /** Absolute minimum dollar reserve kept as cash. */
22
+ exports.REBALANCE_MIN_CASH_RESERVE_VALUE = 10;
23
+ /** Inflate buy pricing by this many basis points when sizing orders. */
24
+ exports.REBALANCE_BUY_SLIPPAGE_BPS = 30;
25
+ /** Haircut expected sell proceeds by this many basis points when funding buys. */
26
+ exports.REBALANCE_SELL_SLIPPAGE_BPS = 20;
27
+ /** Flat per-order fee assumption used in funding math. */
28
+ exports.REBALANCE_PER_ORDER_FEE = 0;
29
+ /** Canonical symbol used in targetWeights when strategy holds cash. */
30
+ exports.REBALANCE_CASH_SYMBOL = 'CASH';
31
+ /** Strategy ticker that means "hold as cash". */
32
+ exports.REBALANCE_CASH_SOURCE_SYMBOL = 'DTB3';
33
+ // ---------------------------------------------------------------------------
34
+ // Helpers
35
+ // ---------------------------------------------------------------------------
36
+ function assertFiniteNumber(name, value) {
37
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
38
+ throw new Error(`Invalid numeric value for ${name}`);
39
+ }
40
+ }
41
+ function assertNonNegative(name, value) {
42
+ assertFiniteNumber(name, value);
43
+ if (value < 0) {
44
+ throw new Error(`Invalid negative value for ${name}`);
45
+ }
46
+ }
47
+ function assertPositive(name, value) {
48
+ assertFiniteNumber(name, value);
49
+ if (value <= 0) {
50
+ throw new Error(`Invalid non-positive value for ${name}`);
51
+ }
52
+ }
53
+ function quantityFromNotional(notional, price, quantityPrecision) {
54
+ return Math.floor((notional / price) * quantityPrecision) / quantityPrecision;
55
+ }
56
+ function bpsToFraction(bps) {
57
+ return bps / 10_000;
58
+ }
59
+ // ---------------------------------------------------------------------------
60
+ // Public functions
61
+ // ---------------------------------------------------------------------------
62
+ function computePortfolioDriftPercentPoints(input) {
63
+ const { targetWeights, currentValues, cashValue, totalValue } = input;
64
+ assertPositive('totalValue', totalValue);
65
+ assertNonNegative('cashValue', cashValue);
66
+ let targetWeightSum = 0;
67
+ for (const [symbol, weight] of Object.entries(targetWeights)) {
68
+ assertNonNegative(`targetWeight(${symbol})`, weight);
69
+ targetWeightSum += weight;
70
+ }
71
+ if (targetWeightSum > 100 + exports.REBALANCE_EPSILON) {
72
+ throw new Error(`Invalid target weights: sum exceeds 100 (${targetWeightSum.toFixed(4)})`);
73
+ }
74
+ const symbols = new Set([...Object.keys(targetWeights), ...Object.keys(currentValues)]);
75
+ let absSum = 0;
76
+ for (const symbol of symbols) {
77
+ const currentValue = currentValues[symbol] ?? 0;
78
+ assertNonNegative(`currentValue(${symbol})`, currentValue);
79
+ const currentWeight = (currentValue / totalValue) * 100;
80
+ const targetWeight = targetWeights[symbol] ?? 0;
81
+ absSum += Math.abs(targetWeight - currentWeight);
82
+ }
83
+ // Only add implicit cash when target weights don't sum to 100 (no explicit cash ticker).
84
+ if (targetWeightSum < 100) {
85
+ const targetCashWeight = 100 - targetWeightSum;
86
+ const currentCashWeight = (cashValue / totalValue) * 100;
87
+ absSum += Math.abs(targetCashWeight - currentCashWeight);
88
+ }
89
+ return absSum / 2;
90
+ }
91
+ function buildRebalancePlan(input) {
92
+ const { targetWeights, currentValues, prices, quantityPrecisionBySymbol, cashValue, totalValue, portfolioDriftThresholdPercentPoints = exports.PORTFOLIO_DRIFT_THRESHOLD_PERCENT_POINTS, minTradeValue = exports.REBALANCE_MIN_TRADE_VALUE, cashReservePercent = exports.REBALANCE_CASH_RESERVE_PERCENT, minCashReserveValue = exports.REBALANCE_MIN_CASH_RESERVE_VALUE, buySlippageBps = exports.REBALANCE_BUY_SLIPPAGE_BPS, sellSlippageBps = exports.REBALANCE_SELL_SLIPPAGE_BPS, perOrderFee = exports.REBALANCE_PER_ORDER_FEE, cashSymbol, } = input;
93
+ assertPositive('totalValue', totalValue);
94
+ assertNonNegative('cashValue', cashValue);
95
+ assertNonNegative('portfolioDriftThresholdPercentPoints', portfolioDriftThresholdPercentPoints);
96
+ assertNonNegative('minTradeValue', minTradeValue);
97
+ assertNonNegative('cashReservePercent', cashReservePercent);
98
+ assertNonNegative('minCashReserveValue', minCashReserveValue);
99
+ assertNonNegative('buySlippageBps', buySlippageBps);
100
+ assertNonNegative('sellSlippageBps', sellSlippageBps);
101
+ assertNonNegative('perOrderFee', perOrderFee);
102
+ for (const [symbol, precision] of Object.entries(quantityPrecisionBySymbol ?? {})) {
103
+ assertPositive(`quantityPrecisionBySymbol(${symbol})`, precision);
104
+ }
105
+ const portfolioDriftPercentPoints = computePortfolioDriftPercentPoints({
106
+ targetWeights,
107
+ currentValues,
108
+ cashValue,
109
+ totalValue,
110
+ });
111
+ if (portfolioDriftPercentPoints < portfolioDriftThresholdPercentPoints) {
112
+ return {
113
+ triggered: false,
114
+ portfolioDriftPercentPoints,
115
+ reason: 'below_threshold',
116
+ orders: [],
117
+ };
118
+ }
119
+ const symbols = new Set([...Object.keys(targetWeights), ...Object.keys(currentValues)]);
120
+ const sellCandidates = [];
121
+ const buyCandidates = [];
122
+ for (const symbol of symbols) {
123
+ if (cashSymbol != null && symbol === cashSymbol)
124
+ continue;
125
+ const currentValue = currentValues[symbol] ?? 0;
126
+ assertNonNegative(`currentValue(${symbol})`, currentValue);
127
+ const targetWeight = targetWeights[symbol] ?? 0;
128
+ const targetValue = (targetWeight / 100) * totalValue;
129
+ const delta = targetValue - currentValue;
130
+ if (Math.abs(delta) <= minTradeValue)
131
+ continue;
132
+ const price = prices[symbol];
133
+ assertPositive(`price(${symbol})`, price);
134
+ if (delta < 0) {
135
+ sellCandidates.push({ symbol, notional: Math.abs(delta), price });
136
+ }
137
+ else {
138
+ buyCandidates.push({ symbol, notional: delta, price });
139
+ }
140
+ }
141
+ sellCandidates.sort((a, b) => b.notional - a.notional);
142
+ buyCandidates.sort((a, b) => b.notional - a.notional);
143
+ const orders = [];
144
+ const reserveValue = Math.max(minCashReserveValue, cashValue * (cashReservePercent / 100));
145
+ let availableFunds = Math.max(0, cashValue - reserveValue);
146
+ const buySlippageMultiplier = 1 + bpsToFraction(buySlippageBps);
147
+ const sellSlippageMultiplier = 1 - bpsToFraction(sellSlippageBps);
148
+ for (const candidate of sellCandidates) {
149
+ const quantityPrecision = quantityPrecisionBySymbol?.[candidate.symbol] ?? exports.REBALANCE_QUANTITY_PRECISION;
150
+ const quantity = quantityFromNotional(candidate.notional, candidate.price, quantityPrecision);
151
+ if (quantity <= 0)
152
+ continue;
153
+ const estimatedValue = quantity * candidate.price;
154
+ if (estimatedValue <= minTradeValue)
155
+ continue;
156
+ orders.push({
157
+ action: 'SELL',
158
+ symbol: candidate.symbol,
159
+ quantity,
160
+ estimatedPrice: candidate.price,
161
+ estimatedValue,
162
+ });
163
+ const netProceeds = Math.max(0, estimatedValue * sellSlippageMultiplier - perOrderFee);
164
+ availableFunds += netProceeds;
165
+ }
166
+ for (const candidate of buyCandidates) {
167
+ if (availableFunds <= minTradeValue)
168
+ break;
169
+ const maxAffordableNotional = Math.max(0, availableFunds - perOrderFee);
170
+ const cappedNotional = Math.min(candidate.notional, maxAffordableNotional);
171
+ const effectiveBuyPrice = candidate.price * buySlippageMultiplier;
172
+ const quantityPrecision = quantityPrecisionBySymbol?.[candidate.symbol] ?? exports.REBALANCE_QUANTITY_PRECISION;
173
+ const quantity = quantityFromNotional(cappedNotional, effectiveBuyPrice, quantityPrecision);
174
+ if (quantity <= 0)
175
+ continue;
176
+ const estimatedValue = quantity * candidate.price;
177
+ if (estimatedValue <= minTradeValue)
178
+ continue;
179
+ const reservedCost = quantity * effectiveBuyPrice + perOrderFee;
180
+ if (reservedCost > availableFunds + exports.REBALANCE_EPSILON)
181
+ continue;
182
+ orders.push({
183
+ action: 'BUY',
184
+ symbol: candidate.symbol,
185
+ quantity,
186
+ estimatedPrice: candidate.price,
187
+ estimatedValue,
188
+ });
189
+ availableFunds = Math.max(0, availableFunds - reservedCost);
190
+ }
191
+ orders.sort((a, b) => {
192
+ if (a.action === 'SELL' && b.action === 'BUY')
193
+ return -1;
194
+ if (a.action === 'BUY' && b.action === 'SELL')
195
+ return 1;
196
+ return 0;
197
+ });
198
+ return {
199
+ triggered: true,
200
+ portfolioDriftPercentPoints,
201
+ reason: orders.length > 0 ? 'ok' : 'no_orders',
202
+ orders,
203
+ };
204
+ }
205
+ //# sourceMappingURL=rebalance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebalance.js","sourceRoot":"","sources":["../../src/portfolio/rebalance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Ticker } from '../strategy/types';
2
+ /** Maps FRED rate symbols to tradable equivalents. `null` = hold as cash. */
3
+ export declare const FRED_TRADABLE_MAP: Record<string, string | null>;
4
+ /** Normalizes equivalent base tickers to a canonical symbol for leverage lookups. */
5
+ export declare const BASE_TICKER_ALIASES: Record<string, string>;
6
+ /** Maps "SYMBOL:LEVERAGE" to the actual leveraged/inverse ETF ticker. Covers bull (2x/3x) and inverse (-1x/-2x/-3x). */
7
+ export declare const ETF_LEVERAGE_MAP: Record<string, string>;
8
+ /**
9
+ * Maps a strategy ticker to its tradable symbol.
10
+ * Pipeline: FRED mapping -> leveraged/inverse ETF lookup (with alias normalization) -> passthrough.
11
+ */
12
+ export declare function mapTickerToTradable(ticker: Ticker): string | null;
13
+ //# sourceMappingURL=symbols.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symbols.d.ts","sourceRoot":"","sources":["../../src/portfolio/symbols.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAG3D,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGtD,CAAC;AAEF,wHAAwH;AACxH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAoFnD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkBjE"}