@adaptic/utils 0.0.985 → 0.0.987

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.mjs CHANGED
@@ -68027,9 +68027,15 @@ const RiskBudgetPrefsObjectSchema = objectType({
68027
68027
  maxCorrelatedExposurePct: numberType().min(0).max(100).default(40),
68028
68028
  betaTarget: numberType().nullable().default(null),
68029
68029
  maxBeta: numberType().min(0).default(2),
68030
- maxRiskPerTradePct: numberType().min(0).max(100).default(2),
68031
- maxLossPerDayPct: numberType().min(0).max(100).default(5),
68032
- maxLossPerWeekPct: numberType().min(0).max(100).default(10),
68030
+ // Tighter per-trade risk (1.5% vs 2%) for scalping — high turnover means
68031
+ // many small risks compound; per-trade ceiling must be lower than the
68032
+ // swing default to keep daily VAR bounded.
68033
+ maxRiskPerTradePct: numberType().min(0).max(100).default(1.5),
68034
+ // 2% daily loss circuit breaker (vs 5%) — tighter to fail fast when the
68035
+ // regime turns against the scalping strategy.
68036
+ maxLossPerDayPct: numberType().min(0).max(100).default(2.0),
68037
+ // 5% weekly loss cap (vs 10%) — proportional to the daily reduction.
68038
+ maxLossPerWeekPct: numberType().min(0).max(100).default(5.0),
68033
68039
  maxLossPerMonthPct: numberType().min(0).max(100).default(15),
68034
68040
  maxDrawdownFromPeakPct: numberType().min(0).max(100).default(20),
68035
68041
  maxSimultaneousSignalsPerStrategy: numberType().min(0).default(5),
@@ -68038,8 +68044,12 @@ const RiskBudgetPrefsObjectSchema = objectType({
68038
68044
  weekendExposureCapPct: numberType().min(0).max(100).default(30),
68039
68045
  eventRiskExposureCapPct: numberType().min(0).max(100).default(40),
68040
68046
  gapRiskSensitivity: enumType(["low", "medium", "high"]).default("medium"),
68041
- /** Per-trade equity allocation as % of account equity. Replaces legacy AlpacaAccount.tradeAllocationPct. */
68042
- perTradeAllocationPct: numberType().min(0).max(100).default(5),
68047
+ /**
68048
+ * Per-trade equity allocation as % of account equity. Replaces legacy AlpacaAccount.tradeAllocationPct.
68049
+ * Smaller per-trade size (2% vs 5%) for scalping — shorter holds + higher
68050
+ * concurrency demand smaller per-position bets.
68051
+ */
68052
+ perTradeAllocationPct: numberType().min(0).max(100).default(2),
68043
68053
  /** Per-trade crypto allocation as % of account equity. Replaces legacy AlpacaAccount.cryptoTradeAllocationPct. */
68044
68054
  perTradeCryptoAllocationPct: numberType().min(0).max(100).default(5),
68045
68055
  /** Alpaca day-trading buying power check enforcement. Synced to Alpaca API. */
@@ -68075,17 +68085,30 @@ const StrategyPriorityRuleSchema = objectType({
68075
68085
  const SignalConsumptionPrefsObjectSchema = objectType({
68076
68086
  enabledStrategies: arrayType(stringType()).default([]),
68077
68087
  disabledStrategies: arrayType(stringType()).default([]),
68078
- minConfidenceByDefault: numberType().min(0).max(100).default(60),
68088
+ // Slightly higher confidence floor (65 vs 60) reflecting the precision
68089
+ // demands of scalping — false positives compound rapidly at this cadence.
68090
+ minConfidenceByDefault: numberType().min(0).max(100).default(65),
68079
68091
  minConfidenceByAssetClass: recordType(stringType(), numberType()).default({}),
68080
68092
  minConfidenceByStrategy: recordType(stringType(), numberType()).default({}),
68081
- minExpectedRewardRiskRatio: numberType().min(0).default(1.5),
68093
+ // 1.3 R:R minimum — slightly tighter than the swing default (1.5) since
68094
+ // scalp trades have quicker resolution and tolerate marginally lower
68095
+ // expected R:R when win-rate is high.
68096
+ minExpectedRewardRiskRatio: numberType().min(0).default(1.3),
68082
68097
  minExpectedEdgePct: numberType().min(0).default(0),
68083
- maxSignalAgeSeconds: numberType().min(0).default(300),
68084
- cooldownAfterEntrySeconds: numberType().min(0).default(60),
68098
+ // 30s max signal age for scalping — anything older than that has likely
68099
+ // been re-priced out of edge. Previous default (300s) was swing-suitable.
68100
+ maxSignalAgeSeconds: numberType().min(0).default(30),
68101
+ // 5s post-entry cooldown — minimum churn protection while still allowing
68102
+ // rapid re-engagement if the setup persists.
68103
+ cooldownAfterEntrySeconds: numberType().min(0).default(5),
68085
68104
  cooldownAfterExitSeconds: numberType().min(0).default(120),
68086
- cooldownAfterStopOutSeconds: numberType().min(0).default(300),
68105
+ // 30s post-stop-out cooldown (vs 300s) — fast re-entry allowed once the
68106
+ // setup re-presents.
68107
+ cooldownAfterStopOutSeconds: numberType().min(0).default(30),
68087
68108
  cooldownAfterFailedTradeSeconds: numberType().min(0).default(180),
68088
- duplicateSignalSuppressionWindowSeconds: numberType().min(0).default(300),
68109
+ // 10s duplicate-signal window — prevents same-second signal dedup but
68110
+ // allows the same setup to trigger again within a minute.
68111
+ duplicateSignalSuppressionWindowSeconds: numberType().min(0).default(10),
68089
68112
  reversalHandlingPolicy: enumType([
68090
68113
  "ignore_reversal",
68091
68114
  "close_only",
@@ -68108,10 +68131,18 @@ const SignalConsumptionPrefsObjectSchema = objectType({
68108
68131
  earningsBlackoutEnabled: booleanType().default(false),
68109
68132
  earningsBlackoutHoursBefore: numberType().min(0).default(24),
68110
68133
  earningsBlackoutHoursAfter: numberType().min(0).default(2),
68111
- /** Minimum price movement % to qualify as a tradeable signal. Replaces legacy AlpacaAccount.minPercentageChange. */
68112
- minPercentageChange: numberType().min(0).default(0.5),
68113
- /** Minimum average daily volume to qualify a symbol for trading. Replaces legacy AlpacaAccount.volumeThreshold. */
68114
- volumeThreshold: numberType().min(0).default(50000),
68134
+ /**
68135
+ * Minimum price movement % to qualify as a tradeable signal. Replaces legacy AlpacaAccount.minPercentageChange.
68136
+ * Tighter intraday move filter (15bps vs 50bps) scalping captures
68137
+ * sub-percent moves that the swing-trading default would have ignored.
68138
+ */
68139
+ minPercentageChange: numberType().min(0).default(0.15),
68140
+ /**
68141
+ * Minimum average daily volume to qualify a symbol for trading. Replaces legacy AlpacaAccount.volumeThreshold.
68142
+ * Higher floor (100k vs 50k) — scalping requires consistent liquidity to
68143
+ * keep slippage within the tightened maxSlippageTolerancePct (0.3%).
68144
+ */
68145
+ volumeThreshold: numberType().min(0).default(100000),
68115
68146
  });
68116
68147
  const SignalConsumptionPrefsSchema = SignalConsumptionPrefsObjectSchema.default({});
68117
68148
 
@@ -68136,12 +68167,21 @@ const ExecutionPrefsObjectSchema = objectType({
68136
68167
  preferredOrderType: OrderTypeEnum.default("limit"),
68137
68168
  preferredOrderTypeByAssetClass: recordType(stringType(), stringType())
68138
68169
  .default({ crypto: "market" }),
68139
- defaultTimeInForce: enumType(["day", "gtc", "ioc", "fok"]).default("day"),
68170
+ // IOC default (immediate-or-cancel) for scalping — orders that don't
68171
+ // fill instantly should be killed, not parked on the book where they
68172
+ // accumulate stale-price risk.
68173
+ defaultTimeInForce: enumType(["day", "gtc", "ioc", "fok"]).default("ioc"),
68174
+ // Passive bias — scalping captures spread by posting on the book rather
68175
+ // than paying it. Aggressive crossings are reserved for risk-off / unwind.
68140
68176
  executionBias: enumType(["passive", "neutral", "aggressive"])
68141
- .default("neutral"),
68142
- maxSlippageTolerancePct: numberType().min(0).max(100).default(1.0),
68177
+ .default("passive"),
68178
+ // 30bps slippage tolerance (vs 100bps) — must be tighter than scalping
68179
+ // edge magnitudes (typically ~50-100bps) to preserve PnL.
68180
+ maxSlippageTolerancePct: numberType().min(0).max(100).default(0.3),
68143
68181
  priceCollarEnabled: booleanType().default(true),
68144
- priceCollarPct: numberType().min(0).default(2),
68182
+ // 50bps price collar (vs 200bps) — same logic as slippage tolerance:
68183
+ // collar must sit inside the edge magnitude.
68184
+ priceCollarPct: numberType().min(0).default(0.5),
68145
68185
  repriceEnabled: booleanType().default(false),
68146
68186
  repriceMaxAttempts: numberType().min(0).default(3),
68147
68187
  repriceIntervalSeconds: numberType().min(0).default(30),
@@ -68173,20 +68213,31 @@ const TrailingStopTighteningRuleSchema = objectType({
68173
68213
  const PositionManagementPrefsObjectSchema = objectType({
68174
68214
  defaultStopLossMethod: enumType(["fixed_percent", "atr_based", "structure_based", "trailing_stop"])
68175
68215
  .default("trailing_stop"),
68176
- defaultStopLossPct: numberType().min(0).max(100).default(4),
68177
- atrStopMultiplier: numberType().min(0).default(2),
68216
+ // 50bps stop sized for 5-min bar scalp profiles (audit 2026-05-10).
68217
+ defaultStopLossPct: numberType().min(0).max(100).default(0.5),
68218
+ // 0.75x ATR multiplier — tighter than the previous 2x to keep ATR-based
68219
+ // stops aligned with sub-percent intraday move ranges.
68220
+ atrStopMultiplier: numberType().min(0).default(0.75),
68178
68221
  defaultTakeProfitMethod: enumType(["fixed_percent", "atr_based", "risk_reward_ratio", "none"])
68179
68222
  .default("risk_reward_ratio"),
68180
- defaultTakeProfitPct: numberType().min(0).max(100).default(3),
68181
- defaultRiskRewardRatio: numberType().min(0).default(2),
68223
+ // 100bps take-profit pairs with the 50bps stop at a 2:1 R:R via the
68224
+ // explicit defaultRiskRewardRatio below; both serve different code paths
68225
+ // (fixed-percent vs ratio-derived TP).
68226
+ defaultTakeProfitPct: numberType().min(0).max(100).default(1.0),
68227
+ defaultRiskRewardRatio: numberType().min(0).default(1.5),
68182
68228
  breakEvenStopEnabled: booleanType().default(true),
68183
- breakEvenTriggerPct: numberType().min(0).max(100).default(2),
68229
+ // Move stop to break-even after 1% of profit (vs 2%) — tighter to match
68230
+ // scalping cadence.
68231
+ breakEvenTriggerPct: numberType().min(0).max(100).default(1),
68184
68232
  scaleInEnabled: booleanType().default(false),
68185
68233
  scaleInMaxAdds: numberType().min(0).default(2),
68186
68234
  scaleOutEnabled: booleanType().default(true),
68187
68235
  scaleOutTrimPct: numberType().min(0).max(100).default(50),
68188
- scaleOutTriggerPct: numberType().min(0).max(100).default(5),
68189
- maxHoldingPeriodMinutes: numberType().min(0).default(0),
68236
+ // Trim 50% at +0.5% — much earlier than the previous 5% for scalping.
68237
+ scaleOutTriggerPct: numberType().min(0).max(100).default(0.5),
68238
+ // Hard 30-min intraday hold cap. Previous default of 0 (unlimited) is
68239
+ // wrong for scalping where stale positions accumulate undefined risk.
68240
+ maxHoldingPeriodMinutes: numberType().min(0).default(30),
68190
68241
  maxHoldingPeriodByAssetClass: recordType(stringType(), numberType()).default({}),
68191
68242
  dayTradeOnly: booleanType().default(false),
68192
68243
  autoCloseBeforeEarnings: booleanType().default(false),
@@ -68204,7 +68255,9 @@ const PositionManagementPrefsObjectSchema = objectType({
68204
68255
  { profitThresholdPct: 10, newTrailPct: 1.0 },
68205
68256
  ]),
68206
68257
  portfolioStopOverridesPositionStops: booleanType().default(false),
68207
- doNotReenterAfterStopOutMinutes: numberType().min(0).default(30),
68258
+ // 10-min stop-out cooldown (vs 30) — fast re-entry permitted once the
68259
+ // adverse regime resolves.
68260
+ doNotReenterAfterStopOutMinutes: numberType().min(0).default(10),
68208
68261
  doNotReenterAfterForcedCloseMinutes: numberType().min(0).default(60),
68209
68262
  });
68210
68263
  const PositionManagementPrefsSchema = PositionManagementPrefsObjectSchema.default({});
@@ -68460,6 +68513,26 @@ const EffectiveTradingPolicySchema = objectType({
68460
68513
  perTradeEquityAllocationPct: numberType(),
68461
68514
  /** Percentage of the crypto allocation slice of account equity allocated per crypto trade. Canonical replacement for legacy AlpacaAccount.cryptoTradeAllocationPct. */
68462
68515
  perTradeCryptoAllocationPct: numberType(),
68516
+ /**
68517
+ * Wash-trade cooldown period in milliseconds for equities. Defines the
68518
+ * minimum elapsed time between opposing-side fills on the same symbol per
68519
+ * FINRA Rule 5210, mirroring the 30s default already enforced for crypto.
68520
+ * Mirrors `TradingPolicy.equityWashTradeCooldownMs` in backend-legacy.
68521
+ * Optional so that legacy snapshots that pre-date this field continue to
68522
+ * parse cleanly; consumers should fall back to a 30_000ms static default
68523
+ * when undefined.
68524
+ */
68525
+ equityWashTradeCooldownMs: numberType().int().nonnegative().optional(),
68526
+ /**
68527
+ * Maximum daily portfolio loss as a fraction of account equity, evaluated
68528
+ * intraday from the broker's `equity` vs `last_equity` delta. Breach
68529
+ * activates a 24h `BLOCK_NEW_OPENS` halt per Reg-T daily-loss-limit
68530
+ * controls. Optional so legacy snapshots parse cleanly; consumers
68531
+ * should fall back to the static default in `defaultRiskConfig.dailyLossLimits.maxDailyLossPercent`
68532
+ * (3%) when undefined. Range constrained to [0, 1) — a 100% daily-loss
68533
+ * threshold would be nonsensical.
68534
+ */
68535
+ maxDailyLossPercent: numberType().min(0).max(1).optional(),
68463
68536
  macroOverlayEnabled: booleanType(),
68464
68537
  sectorOverlayEnabled: booleanType(),
68465
68538
  volatilityOverlayEnabled: booleanType(),
@@ -68489,9 +68562,14 @@ const EffectiveTradingPolicySchema = objectType({
68489
68562
  });
68490
68563
 
68491
68564
  /**
68492
- * Conservative default trading policy used as the baseline when no
68493
- * user-customized policy exists. All nested preference sub-objects are
68494
- * passed as empty objects so Zod applies their field-level defaults.
68565
+ * Default trading policy used as the baseline when no user-customized policy
68566
+ * exists. Calibrated for short-horizon day trading / HFT-microstructuring /
68567
+ * scalping (intraday holds, 5-min-bar-sized stops, fast cooldowns) per the
68568
+ * 2026-05-10 audit, replacing the previous swing-trading calibration.
68569
+ *
68570
+ * All nested preference sub-objects are passed as empty objects so Zod applies
68571
+ * their field-level defaults (which are themselves tuned for scalping in their
68572
+ * respective schema files).
68495
68573
  *
68496
68574
  * Key choices:
68497
68575
  * - Advisory-only autonomy (no autonomous execution)
@@ -68500,6 +68578,14 @@ const EffectiveTradingPolicySchema = objectType({
68500
68578
  * - No shorting or margin (user must opt-in)
68501
68579
  * - All protective overlays disabled (user must opt-in)
68502
68580
  * - No LLM providers pre-configured
68581
+ * - Tighter per-trade allocation (2% vs 5%) and concurrency (8 positions vs 20)
68582
+ * reflecting the increased turnover and decreased per-position conviction
68583
+ * characteristic of scalping
68584
+ * - Faster equity wash-trade cooldown (5s vs 30s) — FINRA Rule 5210 governs
68585
+ * opposing-side wash trades; same-side intraday re-entry can be much faster
68586
+ * - Tighter daily-loss circuit breaker (2% vs 3%) reflecting that scalping
68587
+ * strategies should fail fast rather than burn the day's risk budget on
68588
+ * one bad regime
68503
68589
  */
68504
68590
  const DEFAULT_TRADING_POLICY = EffectiveTradingPolicySchema.parse({
68505
68591
  autonomyMode: AutonomyMode.ADVISORY_ONLY,
@@ -68520,12 +68606,23 @@ const DEFAULT_TRADING_POLICY = EffectiveTradingPolicySchema.parse({
68520
68606
  maxGrossExposurePct: 100,
68521
68607
  maxNetExposurePct: 100,
68522
68608
  maxLeverage: 1,
68523
- maxSymbolConcentrationPct: 15,
68609
+ maxSymbolConcentrationPct: 8,
68524
68610
  maxSectorConcentrationPct: 30,
68525
- maxOpenPositions: 20,
68611
+ maxOpenPositions: 8,
68526
68612
  maxOpenOrders: 50,
68527
- perTradeEquityAllocationPct: 5,
68528
- perTradeCryptoAllocationPct: 5,
68613
+ perTradeEquityAllocationPct: 2,
68614
+ perTradeCryptoAllocationPct: 2,
68615
+ // 5s same-side intraday re-entry cooldown for scalping. FINRA Rule 5210
68616
+ // governs opposing-side wash trades; same-side rapid re-entry off the
68617
+ // same setup is permitted within tighter bounds suited to 5-min-bar
68618
+ // strategies. Backend-legacy `TradingPolicy.equityWashTradeCooldownMs`
68619
+ // default of 30_000 ms remains the canonical row-level fallback for
68620
+ // accounts that have not opted into the scalping profile.
68621
+ equityWashTradeCooldownMs: 5_000,
68622
+ // 2% daily loss cap: tighter than the engine's 3% defaultRiskConfig
68623
+ // because scalping strategies should fail fast — three bad regimes at 3%
68624
+ // each is a 9% intraday burn before the kill-switch fires.
68625
+ maxDailyLossPercent: 0.02,
68529
68626
  macroOverlayEnabled: false,
68530
68627
  sectorOverlayEnabled: false,
68531
68628
  volatilityOverlayEnabled: false,