@timecell/engine 0.1.1 → 0.2.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.
@@ -0,0 +1,17 @@
1
+ export interface ActionPlanInput {
2
+ btcPercentage: number;
3
+ ruinTestPassed: boolean;
4
+ runwayMonths: number;
5
+ temperatureScore: number;
6
+ liquidReserveUsd: number;
7
+ monthlyBurnUsd: number;
8
+ totalValueUsd: number;
9
+ }
10
+ export type ActionSeverity = "red" | "amber" | "green";
11
+ export interface ActionItem {
12
+ severity: ActionSeverity;
13
+ message: string;
14
+ rule: string;
15
+ }
16
+ export declare function generateActionPlan(input: ActionPlanInput): ActionItem[];
17
+ //# sourceMappingURL=action-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-plan.d.ts","sourceRoot":"","sources":["../src/action-plan.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,eAAe;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,OAAO,GAAG,OAAO,CAAC;AAEvD,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACb;AAgBD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,UAAU,EAAE,CAsIvE"}
@@ -0,0 +1,121 @@
1
+ // =============================================================================
2
+ // Action Plan Engine (Framework-Driven Priorities)
3
+ // =============================================================================
4
+ // Evaluates portfolio state against the Bitcoin Investing Framework rules and
5
+ // returns a prioritised list of actionable items (red / amber / green).
6
+ // ---------------------------------------------------------------------------
7
+ // Severity sort order
8
+ // ---------------------------------------------------------------------------
9
+ const SEVERITY_ORDER = {
10
+ red: 0,
11
+ amber: 1,
12
+ green: 2,
13
+ };
14
+ // ---------------------------------------------------------------------------
15
+ // Rule evaluator
16
+ // ---------------------------------------------------------------------------
17
+ export function generateActionPlan(input) {
18
+ const { btcPercentage, ruinTestPassed, runwayMonths, temperatureScore, liquidReserveUsd, } = input;
19
+ const items = [];
20
+ // ---- Red (act now) ----
21
+ if (!ruinTestPassed) {
22
+ items.push({
23
+ severity: "red",
24
+ message: "Reduce BTC allocation until ruin test passes. Your portfolio does not survive an 80% crash.",
25
+ rule: "ruin-test",
26
+ });
27
+ }
28
+ if (temperatureScore > 75) {
29
+ items.push({
30
+ severity: "red",
31
+ message: "Extreme greed zone. Stop buying. Consider activating selling rules.",
32
+ rule: "temperature-extreme-greed",
33
+ });
34
+ }
35
+ if (liquidReserveUsd <= 0) {
36
+ items.push({
37
+ severity: "red",
38
+ message: "No liquid reserve. Build a safety net: 2+ years of expenses outside BTC.",
39
+ rule: "no-reserve",
40
+ });
41
+ }
42
+ if (runwayMonths < 18) {
43
+ items.push({
44
+ severity: "red",
45
+ message: `Insufficient runway (${runwayMonths === Infinity ? "unlimited" : Math.round(runwayMonths)} months). Need 18+ months. Increase liquid reserve or reduce BTC allocation.`,
46
+ rule: "insufficient-runway",
47
+ });
48
+ }
49
+ // ---- Amber (consider) ----
50
+ if (btcPercentage >= 25) {
51
+ items.push({
52
+ severity: "amber",
53
+ message: `At ${btcPercentage}% BTC, conviction gates should be checked: multi-cycle experience, 2yr expenses outside BTC, no forced-sale liabilities.`,
54
+ rule: "conviction-gates",
55
+ });
56
+ }
57
+ if (btcPercentage >= 50) {
58
+ items.push({
59
+ severity: "amber",
60
+ message: `Downside insurance recommended at ${btcPercentage}% allocation (Framework Part 6).`,
61
+ rule: "insurance-needed",
62
+ });
63
+ }
64
+ if (temperatureScore < 20) {
65
+ items.push({
66
+ severity: "amber",
67
+ message: "Extreme fear zone. Historically the best time to accumulate. Consider lump sum if conviction is high.",
68
+ rule: "temperature-extreme-fear",
69
+ });
70
+ }
71
+ if (temperatureScore >= 60 && temperatureScore <= 75) {
72
+ items.push({
73
+ severity: "amber",
74
+ message: "Greed zone. Slow down buying. Review your risk management and de-risk triggers.",
75
+ rule: "temperature-greed",
76
+ });
77
+ }
78
+ // ---- Green (on track) ----
79
+ if (ruinTestPassed && runwayMonths >= 18) {
80
+ items.push({
81
+ severity: "green",
82
+ message: `Ruin test passed. You survive the worst-case crash with ${runwayMonths === Infinity ? "unlimited" : Math.round(runwayMonths)} months runway.`,
83
+ rule: "ruin-test-passed",
84
+ });
85
+ }
86
+ if (temperatureScore >= 20 && temperatureScore < 40) {
87
+ items.push({
88
+ severity: "green",
89
+ message: "Fear zone. Good time to DCA aggressively.",
90
+ rule: "temperature-fear",
91
+ });
92
+ }
93
+ if (temperatureScore >= 40 && temperatureScore < 60) {
94
+ items.push({
95
+ severity: "green",
96
+ message: "Neutral zone. Continue DCA at normal pace.",
97
+ rule: "temperature-neutral",
98
+ });
99
+ }
100
+ // ---- Sort: red first, then amber, then green ----
101
+ items.sort((a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity]);
102
+ // ---- Limit to 5 items (truncate greens first, then ambers) ----
103
+ if (items.length > 5) {
104
+ // Count by severity
105
+ const reds = items.filter((i) => i.severity === "red");
106
+ const ambers = items.filter((i) => i.severity === "amber");
107
+ const greens = items.filter((i) => i.severity === "green");
108
+ // Trim greens first, then ambers if still over
109
+ const budget = 5;
110
+ const redCount = reds.length;
111
+ const amberBudget = Math.min(ambers.length, budget - redCount);
112
+ const greenBudget = Math.min(greens.length, budget - redCount - amberBudget);
113
+ return [
114
+ ...reds.slice(0, budget),
115
+ ...ambers.slice(0, amberBudget),
116
+ ...greens.slice(0, greenBudget),
117
+ ].slice(0, 5);
118
+ }
119
+ return items;
120
+ }
121
+ //# sourceMappingURL=action-plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-plan.js","sourceRoot":"","sources":["../src/action-plan.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,4 +4,10 @@ export { calculateRunwayMonths } from "./runway.js";
4
4
  export { ruinTest } from "./ruin-test.js";
5
5
  export { calculateGeometricMeanCAGR, calculateGeometricBreakeven } from "./geometric-mean.js";
6
6
  export { DEMO_PORTFOLIO, DEMO_HEDGE_POSITIONS, DEMO_BTC_PRICE } from "./fixtures/demo-portfolio.js";
7
+ export type { TemperatureResult } from "./temperature.js";
8
+ export { calculateTemperature, scoreToZone } from "./temperature.js";
9
+ export { calculatePositionSizing, getConvictionRung, } from "./position-sizing.js";
10
+ export type { PositionSizingInput, PositionSizingResult } from "./position-sizing.js";
11
+ export { generateActionPlan } from "./action-plan.js";
12
+ export type { ActionPlanInput, ActionItem, ActionSeverity } from "./action-plan.js";
7
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EACX,cAAc,EACd,aAAa,EACb,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACtG,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EACX,cAAc,EACd,aAAa,EACb,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACtG,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACpG,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EACN,uBAAuB,EACvB,iBAAiB,GACjB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -6,4 +6,7 @@ export { calculateRunwayMonths } from "./runway.js";
6
6
  export { ruinTest } from "./ruin-test.js";
7
7
  export { calculateGeometricMeanCAGR, calculateGeometricBreakeven } from "./geometric-mean.js";
8
8
  export { DEMO_PORTFOLIO, DEMO_HEDGE_POSITIONS, DEMO_BTC_PRICE } from "./fixtures/demo-portfolio.js";
9
+ export { calculateTemperature, scoreToZone } from "./temperature.js";
10
+ export { calculatePositionSizing, getConvictionRung, } from "./position-sizing.js";
11
+ export { generateActionPlan } from "./action-plan.js";
9
12
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,gCAAgC;AAChC,gFAAgF;AAWhF,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACtG,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,gCAAgC;AAChC,gFAAgF;AAWhF,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACtG,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEpG,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EACN,uBAAuB,EACvB,iBAAiB,GACjB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,42 @@
1
+ export declare function getConvictionRung(btcPct: number): string;
2
+ export interface PositionSizingInput {
3
+ totalValueUsd: number;
4
+ currentBtcPct: number;
5
+ targetBtcPct: number;
6
+ monthlyBurnUsd: number;
7
+ liquidReserveUsd: number;
8
+ btcPriceUsd: number;
9
+ dcaMonths?: number;
10
+ }
11
+ export interface PositionSizingResult {
12
+ /** Current BTC exposure in USD */
13
+ currentBtcUsd: number;
14
+ /** Target BTC exposure in USD */
15
+ targetBtcUsd: number;
16
+ /** Amount to buy (positive) or sell (negative) in USD */
17
+ gapUsd: number;
18
+ /** Amount to buy (positive) or sell (negative) in BTC */
19
+ gapBtc: number;
20
+ /** If DCA: monthly buy/sell amount in USD */
21
+ dcaMonthlyUsd: number;
22
+ /** If DCA: monthly buy/sell amount in BTC */
23
+ dcaMonthlyBtc: number;
24
+ /** Number of DCA months used in calculation */
25
+ dcaMonths: number;
26
+ /** Whether the ruin test passes after reallocation */
27
+ postReallocationRuinTest: boolean;
28
+ /** Post-reallocation runway (months) */
29
+ postReallocationRunwayMonths: number;
30
+ /** Conviction ladder rung name for the TARGET allocation */
31
+ convictionRung: string;
32
+ /** Conviction ladder rung name for the CURRENT allocation */
33
+ currentConvictionRung: string;
34
+ }
35
+ /**
36
+ * Calculate position sizing: gap analysis, DCA schedule, and post-reallocation
37
+ * ruin test for moving from current BTC% to target BTC%.
38
+ *
39
+ * Pure function — no side effects.
40
+ */
41
+ export declare function calculatePositionSizing(input: PositionSizingInput): PositionSizingResult;
42
+ //# sourceMappingURL=position-sizing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"position-sizing.d.ts","sourceRoot":"","sources":["../src/position-sizing.ts"],"names":[],"mappings":"AAqBA,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKxD;AAMD,MAAM,WAAW,mBAAmB;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACpC,kCAAkC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,wBAAwB,EAAE,OAAO,CAAC;IAClC,wCAAwC;IACxC,4BAA4B,EAAE,MAAM,CAAC;IACrC,4DAA4D;IAC5D,cAAc,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,qBAAqB,EAAE,MAAM,CAAC;CAC9B;AAMD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,mBAAmB,GAAG,oBAAoB,CAkDxF"}
@@ -0,0 +1,71 @@
1
+ // =============================================================================
2
+ // Position Sizing (Framework Part 3)
3
+ // =============================================================================
4
+ // Determines how to move from current BTC allocation to target allocation,
5
+ // including DCA strategy and post-reallocation ruin test.
6
+ import { ruinTest } from "./ruin-test.js";
7
+ // -----------------------------------------------------------------------------
8
+ // Conviction ladder rung thresholds (mirrors ConvictionLadder.tsx)
9
+ // -----------------------------------------------------------------------------
10
+ const RUNGS = [
11
+ { level: 6, name: "Single-Asset Core", min: 50 },
12
+ { level: 5, name: "Owner-Class", min: 25 },
13
+ { level: 4, name: "High Conviction", min: 10 },
14
+ { level: 3, name: "Diversifier", min: 5 },
15
+ { level: 2, name: "Experimenter", min: 1 },
16
+ { level: 1, name: "Observer", min: 0 },
17
+ ];
18
+ export function getConvictionRung(btcPct) {
19
+ for (const rung of RUNGS) {
20
+ if (btcPct >= rung.min)
21
+ return rung.name;
22
+ }
23
+ return "Observer";
24
+ }
25
+ // -----------------------------------------------------------------------------
26
+ // Core calculation
27
+ // -----------------------------------------------------------------------------
28
+ /**
29
+ * Calculate position sizing: gap analysis, DCA schedule, and post-reallocation
30
+ * ruin test for moving from current BTC% to target BTC%.
31
+ *
32
+ * Pure function — no side effects.
33
+ */
34
+ export function calculatePositionSizing(input) {
35
+ const { totalValueUsd, currentBtcPct, targetBtcPct, monthlyBurnUsd, liquidReserveUsd, btcPriceUsd, dcaMonths = 6, } = input;
36
+ // Absolute USD values
37
+ const currentBtcUsd = totalValueUsd * (currentBtcPct / 100);
38
+ const targetBtcUsd = totalValueUsd * (targetBtcPct / 100);
39
+ // Gap: positive = need to buy more BTC, negative = need to sell BTC
40
+ const gapUsd = targetBtcUsd - currentBtcUsd;
41
+ const gapBtc = btcPriceUsd > 0 ? gapUsd / btcPriceUsd : 0;
42
+ // DCA breakdown
43
+ const months = Math.max(1, dcaMonths);
44
+ const dcaMonthlyUsd = gapUsd / months;
45
+ const dcaMonthlyBtc = btcPriceUsd > 0 ? dcaMonthlyUsd / btcPriceUsd : 0;
46
+ // Post-reallocation ruin test
47
+ // After fully reallocating, portfolio still has the same totalValue —
48
+ // we just re-run the ruin test with the target BTC%.
49
+ const postPortfolio = {
50
+ totalValueUsd,
51
+ btcPercentage: targetBtcPct,
52
+ monthlyBurnUsd,
53
+ liquidReserveUsd,
54
+ btcPriceUsd,
55
+ };
56
+ const ruinResult = ruinTest(postPortfolio);
57
+ return {
58
+ currentBtcUsd,
59
+ targetBtcUsd,
60
+ gapUsd,
61
+ gapBtc,
62
+ dcaMonthlyUsd,
63
+ dcaMonthlyBtc,
64
+ dcaMonths: months,
65
+ postReallocationRuinTest: ruinResult.passed,
66
+ postReallocationRunwayMonths: ruinResult.runwayMonths,
67
+ convictionRung: getConvictionRung(targetBtcPct),
68
+ currentConvictionRung: getConvictionRung(currentBtcPct),
69
+ };
70
+ }
71
+ //# sourceMappingURL=position-sizing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"position-sizing.js","sourceRoot":"","sources":["../src/position-sizing.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,qCAAqC;AACrC,gFAAgF;AAChF,2EAA2E;AAC3E,0DAA0D;AAE1D,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,gFAAgF;AAChF,mEAAmE;AACnE,gFAAgF;AAChF,MAAM,KAAK,GAAG;IACb,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,GAAG,EAAE,EAAE,EAAE;IAChD,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,EAAE,EAAE;IAC1C,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,EAAE,EAAE;IAC9C,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC,EAAE;IACzC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,EAAE;IAC1C,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,EAAE;CAC7B,CAAC;AAEX,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC/C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,IAAI,MAAM,IAAI,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IAC1C,CAAC;IACD,OAAO,UAAU,CAAC;AACnB,CAAC;AAyCD,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAA0B;IACjE,MAAM,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,SAAS,GAAG,CAAC,GACb,GAAG,KAAK,CAAC;IAEV,sBAAsB;IACtB,MAAM,aAAa,GAAG,aAAa,GAAG,CAAC,aAAa,GAAG,GAAG,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,aAAa,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC;IAE1D,oEAAoE;IACpE,MAAM,MAAM,GAAG,YAAY,GAAG,aAAa,CAAC;IAC5C,MAAM,MAAM,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1D,gBAAgB;IAChB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACtC,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,MAAM,aAAa,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAExE,8BAA8B;IAC9B,sEAAsE;IACtE,qDAAqD;IACrD,MAAM,aAAa,GAAmB;QACrC,aAAa;QACb,aAAa,EAAE,YAAY;QAC3B,cAAc;QACd,gBAAgB;QAChB,WAAW;KACX,CAAC;IAEF,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;IAE3C,OAAO;QACN,aAAa;QACb,YAAY;QACZ,MAAM;QACN,MAAM;QACN,aAAa;QACb,aAAa;QACb,SAAS,EAAE,MAAM;QACjB,wBAAwB,EAAE,UAAU,CAAC,MAAM;QAC3C,4BAA4B,EAAE,UAAU,CAAC,YAAY;QACrD,cAAc,EAAE,iBAAiB,CAAC,YAAY,CAAC;QAC/C,qBAAqB,EAAE,iBAAiB,CAAC,aAAa,CAAC;KACvD,CAAC;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface TemperatureResult {
2
+ score: number;
3
+ zone: "Extreme Fear" | "Fear" | "Neutral" | "Greed" | "Extreme Greed";
4
+ mvrv: number;
5
+ rhodl: number;
6
+ }
7
+ /**
8
+ * Derive a 0–100 temperature score from raw MVRV and RHODL ratios.
9
+ *
10
+ * Normalization ranges (approximate historical bounds):
11
+ * MVRV: 0.5 → 0, 4.5 → 100
12
+ * RHODL: 0.5 → 0, 4.5 → 100 (RHODL ratio uses similar scale)
13
+ *
14
+ * Weighted composite: 60% MVRV + 40% RHODL, clamped to [0, 100].
15
+ */
16
+ export declare function calculateTemperature(mvrv: number, rhodl: number): TemperatureResult;
17
+ export declare function scoreToZone(score: number): TemperatureResult["zone"];
18
+ //# sourceMappingURL=temperature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temperature.d.ts","sourceRoot":"","sources":["../src/temperature.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,eAAe,CAAC;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAcnF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAMpE"}
@@ -0,0 +1,37 @@
1
+ // =============================================================================
2
+ // Temperature — Composite Bitcoin cycle position score
3
+ // =============================================================================
4
+ // Score: MVRV (60%) + RHODL (40%), normalized to 0–100
5
+ // Zones: Extreme Fear / Fear / Neutral / Greed / Extreme Greed
6
+ /**
7
+ * Derive a 0–100 temperature score from raw MVRV and RHODL ratios.
8
+ *
9
+ * Normalization ranges (approximate historical bounds):
10
+ * MVRV: 0.5 → 0, 4.5 → 100
11
+ * RHODL: 0.5 → 0, 4.5 → 100 (RHODL ratio uses similar scale)
12
+ *
13
+ * Weighted composite: 60% MVRV + 40% RHODL, clamped to [0, 100].
14
+ */
15
+ export function calculateTemperature(mvrv, rhodl) {
16
+ const MVRV_MIN = 0.5;
17
+ const MVRV_MAX = 4.5;
18
+ const RHODL_MIN = 0.5;
19
+ const RHODL_MAX = 4.5;
20
+ const mvrvNorm = Math.max(0, Math.min(100, ((mvrv - MVRV_MIN) / (MVRV_MAX - MVRV_MIN)) * 100));
21
+ const rhodlNorm = Math.max(0, Math.min(100, ((rhodl - RHODL_MIN) / (RHODL_MAX - RHODL_MIN)) * 100));
22
+ const score = Math.round(mvrvNorm * 0.6 + rhodlNorm * 0.4);
23
+ const zone = scoreToZone(score);
24
+ return { score, zone, mvrv, rhodl };
25
+ }
26
+ export function scoreToZone(score) {
27
+ if (score < 20)
28
+ return "Extreme Fear";
29
+ if (score < 40)
30
+ return "Fear";
31
+ if (score < 60)
32
+ return "Neutral";
33
+ if (score < 75)
34
+ return "Greed";
35
+ return "Extreme Greed";
36
+ }
37
+ //# sourceMappingURL=temperature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temperature.js","sourceRoot":"","sources":["../src/temperature.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,uDAAuD;AACvD,gFAAgF;AAChF,uDAAuD;AACvD,+DAA+D;AAS/D;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,KAAa;IAC/D,MAAM,QAAQ,GAAG,GAAG,CAAC;IACrB,MAAM,QAAQ,GAAG,GAAG,CAAC;IACrB,MAAM,SAAS,GAAG,GAAG,CAAC;IACtB,MAAM,SAAS,GAAG,GAAG,CAAC;IAEtB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAC/F,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAEpG,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,GAAG,SAAS,GAAG,GAAG,CAAC,CAAC;IAE3D,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAEhC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACxC,IAAI,KAAK,GAAG,EAAE;QAAE,OAAO,cAAc,CAAC;IACtC,IAAI,KAAK,GAAG,EAAE;QAAE,OAAO,MAAM,CAAC;IAC9B,IAAI,KAAK,GAAG,EAAE;QAAE,OAAO,SAAS,CAAC;IACjC,IAAI,KAAK,GAAG,EAAE;QAAE,OAAO,OAAO,CAAC;IAC/B,OAAO,eAAe,CAAC;AACxB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timecell/engine",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "TimeCell core engine",
6
6
  "main": "dist/index.js",