aiden-shared-calculations-unified 1.0.14 → 1.0.16

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 (18) hide show
  1. package/calculations/behavioural/{asset_crowd_flow.js → historical/asset_crowd_flow.js} +2 -2
  2. package/calculations/behavioural/{smart_money_flow.js → historical/smart_money_flow.js} +1 -1
  3. package/calculations/meta/cash-flow-liquidation.js +135 -0
  4. package/calculations/sectors/{diversification_pnl.js → historical/diversification_pnl.js} +1 -1
  5. package/calculations/sectors/{sector_rotation.js → historical/sector_rotation.js} +1 -1
  6. package/calculations/sentiment/{crowd_conviction_score.js → historical/crowd_conviction_score.js} +1 -1
  7. package/package.json +1 -1
  8. /package/calculations/behavioural/{drawdown_response.js → historical/drawdown_response.js} +0 -0
  9. /package/calculations/behavioural/{gain_response.js → historical/gain_response.js} +0 -0
  10. /package/calculations/behavioural/{paper_vs_diamond_hands.js → historical/paper_vs_diamond_hands.js} +0 -0
  11. /package/calculations/behavioural/{position_count_pnl.js → historical/position_count_pnl.js} +0 -0
  12. /package/calculations/capital_flow/{deposit_withdrawal_percentage.js → historical/deposit_withdrawal_percentage.js} +0 -0
  13. /package/calculations/capital_flow/{new_allocation_percentage.js → historical/new_allocation_percentage.js} +0 -0
  14. /package/calculations/capital_flow/{reallocation_increase_percentage.js → historical/reallocation_increase_percentage.js} +0 -0
  15. /package/calculations/pnl/{profitability_migration.js → historical/profitability_migration.js} +0 -0
  16. /package/calculations/pnl/{user_profitability_tracker.js → historical/user_profitability_tracker.js} +0 -0
  17. /package/calculations/speculators/{risk_appetite_change.js → historical/risk_appetite_change.js} +0 -0
  18. /package/calculations/speculators/{tsl_effectiveness.js → historical/tsl_effectiveness.js} +0 -0
@@ -1,5 +1,5 @@
1
- const { loadAllPriceData, getDailyPriceChange } = require('../../utils/price_data_provider');
2
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
1
+ const { loadAllPriceData, getDailyPriceChange } = require('../../../utils/price_data_provider');
2
+ const { loadInstrumentMappings } = require('../../../utils/sector_mapping_provider');
3
3
 
4
4
  /**
5
5
  * @fileoverview Calculates "Net Crowd Flow" for each asset.
@@ -5,7 +5,7 @@
5
5
  const { Firestore } = require('@google-cloud/firestore');
6
6
  const firestore = new Firestore();
7
7
  // CORRECTED PATH: ../utils/ instead of ../../utils/
8
- const { getInstrumentSectorMap } = require('../../utils/sector_mapping_provider');
8
+ const { getInstrumentSectorMap } = require('../../../utils/sector_mapping_provider');
9
9
 
10
10
  class SmartMoneyFlow {
11
11
  // ... (rest of the code is unchanged) ...
@@ -0,0 +1,135 @@
1
+ /**
2
+ * @fileoverview Correlates a crowd-wide withdrawal signal with the specific assets
3
+ * that are being sold (liquidated) to fund those withdrawals.
4
+ * This is a meta-calculation that runs in Pass 3.
5
+ */
6
+
7
+ const { FieldValue } = require('@google-cloud/firestore');
8
+
9
+ class CashFlowLiquidation {
10
+ constructor() {
11
+ this.lookbackDays = 7;
12
+ this.correlationWindow = 3;
13
+ // A positive value signals a net crowd withdrawal
14
+ this.withdrawalSignalThreshold = 1.0;
15
+ }
16
+
17
+ _getDateStr(baseDate, daysAgo) {
18
+ const date = new Date(baseDate + 'T00:00:00Z');
19
+ date.setUTCDate(date.getUTCDate() - daysAgo);
20
+ return date.toISOString().slice(0, 10);
21
+ }
22
+
23
+ /**
24
+ * @param {string} dateStr The date to run the analysis for (e.g., "2025-10-31").
25
+ * @param {object} dependencies The shared dependencies (db, logger).
26
+ * @param {object} config The computation system configuration.
27
+ * @returns {Promise<object|null>} The analysis result or null.
28
+ */
29
+ async process(dateStr, dependencies, config) {
30
+ const { db, logger } = dependencies;
31
+ const collection = config.resultsCollection;
32
+
33
+ // 1. Build all needed refs in advance for this day
34
+ const dateRefs = [];
35
+ const dates = [];
36
+
37
+ // Refs for the lookback period (for the signal)
38
+ for (let i = 1; i <= this.lookbackDays; i++) {
39
+ const checkDate = this._getDateStr(dateStr, i);
40
+ dates.push({ date: checkDate, category: 'capital_flow', computation: 'crowd-cash-flow-proxy' });
41
+ }
42
+
43
+ // Refs for today's two dependencies
44
+ dates.push({ date: dateStr, category: 'capital_flow', computation: 'crowd-cash-flow-proxy' });
45
+ dates.push({ date: dateStr, category: 'behavioural', computation: 'asset-crowd-flow' });
46
+
47
+ // Build refs array
48
+ const refs = dates.map(d =>
49
+ db.collection(collection).doc(d.date)
50
+ .collection('results').doc(d.category)
51
+ .collection('computations').doc(d.computation)
52
+ );
53
+
54
+ const snapshots = await db.getAll(...refs);
55
+
56
+ // Build map(path -> data)
57
+ const dataMap = new Map();
58
+ snapshots.forEach((snap, idx) => {
59
+ if (snap.exists) dataMap.set(idx, snap.data());
60
+ });
61
+
62
+ // 2. Find the withdrawal signal
63
+ let withdrawalSignal = null;
64
+ let withdrawalSignalDay = null;
65
+
66
+ for (let i = 0; i < this.lookbackDays; i++) {
67
+ const flowData = dataMap.get(i);
68
+ const dateUsed = dates[i].date;
69
+ // INVERTED LOGIC: Look for a POSITIVE value
70
+ if (flowData && flowData.cash_flow_effect_proxy > this.withdrawalSignalThreshold) {
71
+ withdrawalSignal = flowData;
72
+ withdrawalSignalDay = dateUsed;
73
+ break; // Found the most recent signal
74
+ }
75
+ }
76
+
77
+ if (!withdrawalSignal) {
78
+ return {
79
+ status: 'no_withdrawal_signal_found',
80
+ lookback_days: this.lookbackDays,
81
+ signal_threshold: this.withdrawalSignalThreshold
82
+ };
83
+ }
84
+
85
+ const daysSinceSignal = (new Date(dateStr) - new Date(withdrawalSignalDay)) / (1000 * 60 * 60 * 24);
86
+
87
+ if (daysSinceSignal <= 0 || daysSinceSignal > this.correlationWindow) {
88
+ return {
89
+ status: 'outside_correlation_window',
90
+ signal_day: withdrawalSignalDay,
91
+ days_since_signal: daysSinceSignal
92
+ };
93
+ }
94
+
95
+ // 3. Get today's data to find the correlation
96
+ const cashFlowData = dataMap.get(this.lookbackDays);
97
+ const assetFlowData = dataMap.get(this.lookbackDays + 1);
98
+
99
+ if (!cashFlowData || !assetFlowData) {
100
+ logger.log('WARN', `[CashFlowLiquidation] Missing dependency data for ${dateStr}. Skipping.`);
101
+ return null;
102
+ }
103
+
104
+ // 'trading_effect' will be negative if the crowd is net-selling
105
+ const netSellPct = cashFlowData.components?.trading_effect || 0;
106
+ const netWithdrawalPct = Math.abs(withdrawalSignal.cash_flow_effect_proxy);
107
+
108
+ // INVERTED LOGIC: Find top *sells*
109
+ const topLiquidations = Object.entries(assetFlowData)
110
+ .filter(([ticker, data]) => data.net_crowd_flow_pct < 0) // Find assets with negative flow
111
+ .sort(([, a], [, b]) => a.net_crowd_flow_pct - b.net_crowd_flow_pct) // Sort ascending (most negative first)
112
+ .slice(0, 10)
113
+ .map(([ticker, data]) => ({
114
+ ticker,
115
+ net_flow_pct: data.net_crowd_flow_pct
116
+ }));
117
+
118
+ return {
119
+ status: 'analysis_complete',
120
+ analysis_date: dateStr,
121
+ signal_date: withdrawalSignalDay,
122
+ days_since_signal: daysSinceSignal,
123
+ signal_withdrawal_proxy_pct: netWithdrawalPct,
124
+ day_net_sell_pct: netSellPct, // This value should be negative
125
+ pct_of_withdrawal_funded_today: (Math.abs(netSellPct) / netWithdrawalPct) * 100,
126
+ top_liquidation_assets: topLiquidations
127
+ };
128
+ }
129
+
130
+ // Must exist for the meta-computation runner
131
+ async getResult() { return null; }
132
+ reset() {}
133
+ }
134
+
135
+ module.exports = CashFlowLiquidation;
@@ -1,5 +1,5 @@
1
1
  // CORRECTED PATH: ../utils/ instead of ../../utils/
2
- const { getInstrumentSectorMap } = require('../../utils/sector_mapping_provider');
2
+ const { getInstrumentSectorMap } = require('../../../utils/sector_mapping_provider');
3
3
 
4
4
  /**
5
5
  * Aggregates P/L by the number of unique sectors a user is invested in.
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Analyzes sector rotation by comparing investment amounts between two days.
3
3
  */
4
4
  // CORRECTED PATH: ../utils/ instead of ../../utils/
5
- const { getInstrumentSectorMap } = require('../../utils/sector_mapping_provider');
5
+ const { getInstrumentSectorMap } = require('../../../utils/sector_mapping_provider');
6
6
 
7
7
  class SectorRotation {
8
8
  // ... (rest of the code is unchanged) ...
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Calculates a "Crowd Conviction" score for each instrument.
3
3
  */
4
4
  // CORRECTED PATH: ../utils/ instead of ../../utils/
5
- const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
5
+ const { loadInstrumentMappings } = require('../../../utils/sector_mapping_provider');
6
6
 
7
7
 
8
8
  class CrowdConvictionScore {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiden-shared-calculations-unified",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "Shared calculation modules for the BullTrackers Computation System.",
5
5
  "main": "index.js",
6
6
  "files": [