@livefolio/sdk 0.4.2 → 0.4.4
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/dist/index.d.ts +27 -8
- package/dist/index.js +9 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -2760,12 +2760,27 @@ type FeatureRef = {
|
|
|
2760
2760
|
* - `'lt'` — strictly less than (`l < r`)
|
|
2761
2761
|
* - `'gte'` — greater than or equal to (`l >= r`)
|
|
2762
2762
|
* - `'lte'` — less than or equal to (`l <= r`)
|
|
2763
|
+
* - `'eq'` — equality. Without {@link Tolerance}, this is strict `l === r`
|
|
2764
|
+
* (no epsilon) — intended for comparing integer-valued features (e.g.
|
|
2765
|
+
* calendar features like `dayOfWeek`) against integer literals. With
|
|
2766
|
+
* {@link Tolerance}, this is "within the symmetric band around `r`" —
|
|
2767
|
+
* `true` while `l ∈ [r − tol, r + tol]`, `false` outside. State is still
|
|
2768
|
+
* persisted via {@link RuleTreeState} but, because entry and exit share
|
|
2769
|
+
* the same band edges, the per-step result is effectively stateless.
|
|
2763
2770
|
*/
|
|
2764
|
-
type ComparisonOp = 'gt' | 'lt' | 'gte' | 'lte';
|
|
2771
|
+
type ComparisonOp = 'gt' | 'lt' | 'gte' | 'lte' | 'eq';
|
|
2765
2772
|
/**
|
|
2766
|
-
*
|
|
2767
|
-
*
|
|
2768
|
-
*
|
|
2773
|
+
* Tolerance band applied to a {@link Comparison} with `op: 'gt'`, `op: 'lt'`,
|
|
2774
|
+
* or `op: 'eq'`.
|
|
2775
|
+
*
|
|
2776
|
+
* For `gt` / `lt`, the band implements **hysteresis**: once the comparison
|
|
2777
|
+
* has flipped, it will not flip back until the left operand exits the band
|
|
2778
|
+
* around the right operand. Entry and exit thresholds differ.
|
|
2779
|
+
*
|
|
2780
|
+
* For `eq`, the band defines a **symmetric range** around `right`: the
|
|
2781
|
+
* comparison is `true` while `l ∈ [r − value, r + value]`. Entry and exit
|
|
2782
|
+
* share the same edges, so behavior is stateless in practice even though the
|
|
2783
|
+
* outcome is still recorded in {@link RuleTreeState}.
|
|
2769
2784
|
*
|
|
2770
2785
|
* `mode: 'absolute'` defines a ±`value` band around `right`.
|
|
2771
2786
|
* `mode: 'relative'` defines a ±`value`% band (i.e. `value` is a percentage).
|
|
@@ -2774,7 +2789,7 @@ type ComparisonOp = 'gt' | 'lt' | 'gte' | 'lte';
|
|
|
2774
2789
|
* so the runtime can persist the last-known state across rebalance periods.
|
|
2775
2790
|
*/
|
|
2776
2791
|
type Tolerance = {
|
|
2777
|
-
/** Half-width of the
|
|
2792
|
+
/** Half-width of the band. */
|
|
2778
2793
|
value: number;
|
|
2779
2794
|
/** `'absolute'` uses raw units; `'relative'` uses a percentage of `right`. */
|
|
2780
2795
|
mode: 'absolute' | 'relative';
|
|
@@ -2809,8 +2824,9 @@ type Comparison = {
|
|
|
2809
2824
|
/** Right-hand operand — a feature reference or a literal number. */
|
|
2810
2825
|
right: FeatureRef | number;
|
|
2811
2826
|
/**
|
|
2812
|
-
* Optional
|
|
2813
|
-
* requires `id` to be set.
|
|
2827
|
+
* Optional tolerance band. Requires `op` to be `'gt'`, `'lt'`, or `'eq'`,
|
|
2828
|
+
* and requires `id` to be set. For `gt`/`lt` the band implements
|
|
2829
|
+
* hysteresis; for `eq` it defines a symmetric range around `right`.
|
|
2814
2830
|
*/
|
|
2815
2831
|
tolerance?: Tolerance;
|
|
2816
2832
|
/**
|
|
@@ -3528,7 +3544,10 @@ declare namespace index {
|
|
|
3528
3544
|
* - `'close'` — removes shares from an existing position and credits cash.
|
|
3529
3545
|
* - `'adjust'` — updates the position's `quantity`; only fees are debited.
|
|
3530
3546
|
* - `'rebalance'` — buys or sells shares in the long position for `asset`;
|
|
3531
|
-
* creates
|
|
3547
|
+
* creates the position on a positive `delta`, removes it
|
|
3548
|
+
* when fully reduced. A reduce against a non-existent
|
|
3549
|
+
* long position is silently ignored (matching the same
|
|
3550
|
+
* case in {@link applyOrders}).
|
|
3532
3551
|
*
|
|
3533
3552
|
* The returned `portfolio.t` is updated to the maximum fill timestamp.
|
|
3534
3553
|
*
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,7 @@ function reconcile(targets, portfolio, prices, assets) {
|
|
|
27
27
|
if (price === void 0) {
|
|
28
28
|
throw new Error(`reconcile: missing price for target asset ${assetId}`);
|
|
29
29
|
}
|
|
30
|
-
const targetShares = Math.floor(totalValue * weight / price);
|
|
30
|
+
const targetShares = Math.max(0, Math.floor(totalValue * weight / price));
|
|
31
31
|
const held = longByAsset.get(assetId);
|
|
32
32
|
const currentShares = held?.quantity ?? 0;
|
|
33
33
|
const delta = targetShares - currentShares;
|
|
@@ -125,10 +125,7 @@ function applyFills(portfolio, fills, orders) {
|
|
|
125
125
|
basis: prev.basis + cost
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
|
-
} else {
|
|
129
|
-
if (idx < 0) {
|
|
130
|
-
throw new Error(`applyFills: rebalance reduce on ${order.asset.id} but no long position exists`);
|
|
131
|
-
}
|
|
128
|
+
} else if (idx >= 0) {
|
|
132
129
|
const prev = positions[idx];
|
|
133
130
|
cash += fill.quantity * fill.price - fill.fees;
|
|
134
131
|
const remaining = prev.quantity - fill.quantity;
|
|
@@ -2568,6 +2565,8 @@ function rawCompare(op, l, r) {
|
|
|
2568
2565
|
return l >= r;
|
|
2569
2566
|
case "lte":
|
|
2570
2567
|
return l <= r;
|
|
2568
|
+
case "eq":
|
|
2569
|
+
return l === r;
|
|
2571
2570
|
}
|
|
2572
2571
|
}
|
|
2573
2572
|
function band(right, tol) {
|
|
@@ -2586,13 +2585,15 @@ function evalComparison(cond, values, state, outState) {
|
|
|
2586
2585
|
if (cond.id === void 0) {
|
|
2587
2586
|
throw new Error("evaluateRuleTree: comparison with tolerance requires id");
|
|
2588
2587
|
}
|
|
2589
|
-
if (cond.op !== "gt" && cond.op !== "lt") {
|
|
2590
|
-
throw new Error(`evaluateRuleTree: tolerance is only supported for op gt/lt, got ${cond.op}`);
|
|
2588
|
+
if (cond.op !== "gt" && cond.op !== "lt" && cond.op !== "eq") {
|
|
2589
|
+
throw new Error(`evaluateRuleTree: tolerance is only supported for op gt/lt/eq, got ${cond.op}`);
|
|
2591
2590
|
}
|
|
2592
2591
|
const prev = state.get(cond.id);
|
|
2593
2592
|
const { lower, upper } = band(r, cond.tolerance);
|
|
2594
2593
|
let result;
|
|
2595
|
-
if (
|
|
2594
|
+
if (cond.op === "eq") {
|
|
2595
|
+
result = l >= lower && l <= upper ? 1 : 0;
|
|
2596
|
+
} else if (prev === void 0) {
|
|
2596
2597
|
result = rawCompare(cond.op, l, r) ? 1 : 0;
|
|
2597
2598
|
} else if (cond.op === "gt") {
|
|
2598
2599
|
if (prev === 1) result = l < lower ? 0 : 1;
|