aiden-shared-calculations-unified 1.0.63 → 1.0.65
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/README.MD +1 -1
- package/calculations/activity/historical/activity_by_pnl_status.js +33 -0
- package/calculations/activity/historical/daily_asset_activity.js +42 -0
- package/calculations/activity/historical/daily_user_activity_tracker.js +37 -0
- package/calculations/activity/historical/speculator_adjustment_activity.js +26 -0
- package/calculations/asset_metrics/asset_position_size.js +36 -0
- package/calculations/backtests/strategy-performance.js +41 -0
- package/calculations/behavioural/historical/asset_crowd_flow.js +124 -127
- package/calculations/behavioural/historical/drawdown_response.js +113 -35
- package/calculations/behavioural/historical/dumb-cohort-flow.js +191 -171
- package/calculations/behavioural/historical/gain_response.js +113 -34
- package/calculations/behavioural/historical/historical_performance_aggregator.js +63 -48
- package/calculations/behavioural/historical/in_loss_asset_crowd_flow.js +159 -63
- package/calculations/behavioural/historical/in_profit_asset_crowd_flow.js +159 -64
- package/calculations/behavioural/historical/paper_vs_diamond_hands.js +86 -19
- package/calculations/behavioural/historical/position_count_pnl.js +91 -39
- package/calculations/behavioural/historical/smart-cohort-flow.js +192 -172
- package/calculations/behavioural/historical/smart_money_flow.js +160 -151
- package/calculations/capital_flow/historical/crowd-cash-flow-proxy.js +95 -89
- package/calculations/capital_flow/historical/deposit_withdrawal_percentage.js +88 -81
- package/calculations/capital_flow/historical/new_allocation_percentage.js +75 -26
- package/calculations/capital_flow/historical/reallocation_increase_percentage.js +73 -32
- package/calculations/insights/daily_buy_sell_sentiment_count.js +47 -32
- package/calculations/insights/daily_total_positions_held.js +28 -24
- package/calculations/insights/historical/daily_bought_vs_sold_count.js +101 -36
- package/calculations/insights/historical/daily_ownership_delta.js +95 -32
- package/calculations/meta/capital_deployment_strategy.js +78 -110
- package/calculations/meta/capital_liquidation_performance.js +114 -111
- package/calculations/meta/cash-flow-deployment.js +114 -107
- package/calculations/meta/cash-flow-liquidation.js +114 -107
- package/calculations/meta/crowd_sharpe_ratio_proxy.js +94 -54
- package/calculations/meta/negative_expectancy_cohort_flow.js +185 -177
- package/calculations/meta/positive_expectancy_cohort_flow.js +186 -181
- package/calculations/meta/profit_cohort_divergence.js +83 -59
- package/calculations/meta/shark_attack_signal.js +91 -39
- package/calculations/meta/smart-dumb-divergence-index.js +114 -98
- package/calculations/meta/smart_dumb_divergence_index_v2.js +109 -98
- package/calculations/meta/social-predictive-regime-state.js +76 -155
- package/calculations/meta/social-topic-driver-index.js +74 -127
- package/calculations/meta/user_expectancy_score.js +83 -31
- package/calculations/pnl/asset_pnl_status.js +120 -31
- package/calculations/pnl/average_daily_pnl_all_users.js +42 -27
- package/calculations/pnl/average_daily_pnl_per_sector.js +84 -26
- package/calculations/pnl/average_daily_pnl_per_stock.js +71 -29
- package/calculations/pnl/average_daily_position_pnl.js +49 -21
- package/calculations/pnl/historical/profitability_migration.js +81 -35
- package/calculations/pnl/historical/user_profitability_tracker.js +107 -104
- package/calculations/pnl/pnl_distribution_per_stock.js +65 -45
- package/calculations/pnl/profitability_ratio_per_stock.js +78 -21
- package/calculations/pnl/profitability_skew_per_stock.js +86 -31
- package/calculations/pnl/profitable_and_unprofitable_status.js +45 -45
- package/calculations/sanity/users_processed.js +24 -1
- package/calculations/sectors/historical/diversification_pnl.js +104 -42
- package/calculations/sectors/historical/sector_rotation.js +94 -45
- package/calculations/sectors/total_long_per_sector.js +55 -20
- package/calculations/sectors/total_short_per_sector.js +55 -20
- package/calculations/sentiment/historical/crowd_conviction_score.js +233 -53
- package/calculations/short_and_long_stats/long_position_per_stock.js +50 -14
- package/calculations/short_and_long_stats/sentiment_per_stock.js +76 -19
- package/calculations/short_and_long_stats/short_position_per_stock.js +50 -13
- package/calculations/short_and_long_stats/total_long_figures.js +34 -13
- package/calculations/short_and_long_stats/total_short_figures.js +34 -14
- package/calculations/socialPosts/social-asset-posts-trend.js +96 -29
- package/calculations/socialPosts/social-top-mentioned-words.js +95 -74
- package/calculations/socialPosts/social-topic-interest-evolution.js +92 -29
- package/calculations/socialPosts/social-topic-sentiment-matrix.js +70 -78
- package/calculations/socialPosts/social-word-mentions-trend.js +96 -38
- package/calculations/socialPosts/social_activity_aggregation.js +106 -77
- package/calculations/socialPosts/social_sentiment_aggregation.js +115 -86
- package/calculations/speculators/distance_to_stop_loss_per_leverage.js +82 -43
- package/calculations/speculators/distance_to_tp_per_leverage.js +81 -42
- package/calculations/speculators/entry_distance_to_sl_per_leverage.js +80 -44
- package/calculations/speculators/entry_distance_to_tp_per_leverage.js +81 -44
- package/calculations/speculators/historical/risk_appetite_change.js +89 -32
- package/calculations/speculators/historical/tsl_effectiveness.js +57 -47
- package/calculations/speculators/holding_duration_per_asset.js +83 -23
- package/calculations/speculators/leverage_per_asset.js +68 -19
- package/calculations/speculators/leverage_per_sector.js +86 -25
- package/calculations/speculators/risk_reward_ratio_per_asset.js +82 -28
- package/calculations/speculators/speculator_asset_sentiment.js +100 -48
- package/calculations/speculators/speculator_danger_zone.js +101 -33
- package/calculations/speculators/stop_loss_distance_by_sector_short_long_breakdown.js +93 -66
- package/calculations/speculators/stop_loss_distance_by_ticker_short_long_breakdown.js +94 -47
- package/calculations/speculators/stop_loss_per_asset.js +94 -26
- package/calculations/speculators/take_profit_per_asset.js +95 -27
- package/calculations/speculators/tsl_per_asset.js +77 -23
- package/package.json +1 -1
- package/utils/price_data_provider.js +142 -142
- package/utils/sector_mapping_provider.js +74 -74
|
@@ -1,136 +1,139 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
* of assets that were heavily liquidated (sold) by the crowd to fund
|
|
4
|
-
* a withdrawal event.
|
|
2
|
+
* @fileoverview Calculation (Pass 4) for capital liquidation performance.
|
|
5
3
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* It
|
|
4
|
+
* This metric answers: "When users liquidate assets (net withdrawal),
|
|
5
|
+
* do they tend to sell winners or losers?"
|
|
6
|
+
*
|
|
7
|
+
* It *depends* on 'cash-flow-liquidation' (to get the list of
|
|
8
|
+
* liquidated assets) and 'asset_pnl_status' (to check the P&L
|
|
9
|
+
* status of those assets).
|
|
10
10
|
*/
|
|
11
|
-
|
|
12
11
|
class CapitalLiquidationPerformance {
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* (NEW) Statically declare dependencies.
|
|
16
|
-
*/
|
|
17
|
-
static getDependencies() {
|
|
18
|
-
return ['cash-flow-liquidation'];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
12
|
constructor() {
|
|
22
|
-
|
|
23
|
-
this.dependenciesLoaded = false;
|
|
24
|
-
this.priceMap = null;
|
|
25
|
-
this.tickerToIdMap = null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async _loadDependencies(calculationUtils) {
|
|
29
|
-
if (this.dependenciesLoaded) return;
|
|
30
|
-
const { loadAllPriceData, loadInstrumentMappings } = calculationUtils;
|
|
31
|
-
const [priceData, mappings] = await Promise.all([
|
|
32
|
-
loadAllPriceData(),
|
|
33
|
-
loadInstrumentMappings()
|
|
34
|
-
]);
|
|
35
|
-
this.priceMap = priceData;
|
|
36
|
-
this.tickerToIdMap = {};
|
|
37
|
-
if (mappings && mappings.instrumentToTicker) {
|
|
38
|
-
for (const [id, ticker] of Object.entries(mappings.instrumentToTicker)) {
|
|
39
|
-
this.tickerToIdMap[ticker] = id;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
this.dependenciesLoaded = true;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
_getDateStr(baseDateStr, daysOffset) {
|
|
46
|
-
const date = new Date(baseDateStr + 'T00:00:00Z');
|
|
47
|
-
date.setUTCDate(date.getUTCDate() + daysOffset);
|
|
48
|
-
return date.toISOString().slice(0, 10);
|
|
13
|
+
// No per-user processing needed
|
|
49
14
|
}
|
|
50
15
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Defines the output schema for this calculation.
|
|
18
|
+
* @returns {object} JSON Schema object
|
|
19
|
+
*/
|
|
20
|
+
static getSchema() {
|
|
21
|
+
return {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"description": "Analyzes if users sell winners or losers during net withdrawal events.",
|
|
24
|
+
"properties": {
|
|
25
|
+
"is_net_withdrawal_day": {
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"description": "True if today was a net withdrawal day."
|
|
28
|
+
},
|
|
29
|
+
"total_assets_liquidated": {
|
|
30
|
+
"type": "number",
|
|
31
|
+
"description": "The number of assets that saw net liquidation."
|
|
32
|
+
},
|
|
33
|
+
"assets_sold_at_profit_count": {
|
|
34
|
+
"type": "number",
|
|
35
|
+
"description": "Count of liquidated assets where the majority of users were in profit."
|
|
36
|
+
},
|
|
37
|
+
"assets_sold_at_loss_count": {
|
|
38
|
+
"type": "number",
|
|
39
|
+
"description": "Count of liquidated assets where the majority of users were in loss."
|
|
40
|
+
},
|
|
41
|
+
"winner_loser_sell_ratio": {
|
|
42
|
+
"type": ["number", "null"],
|
|
43
|
+
"description": "Ratio of (Assets Sold at Profit / Assets Sold at Loss). Null if no assets sold at loss."
|
|
44
|
+
},
|
|
45
|
+
"details": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"description": "List of liquidated assets and their P&L status.",
|
|
48
|
+
"items": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"properties": {
|
|
51
|
+
"ticker": { "type": "string" },
|
|
52
|
+
"net_flow_contribution": { "type": "number" },
|
|
53
|
+
"status": { "type": "string", "enum": ["profit", "loss", "neutral"] },
|
|
54
|
+
"profit_ratio": { "type": "number" }
|
|
55
|
+
},
|
|
56
|
+
"required": ["ticker", "net_flow_contribution", "status", "profit_ratio"]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"required": ["is_net_withdrawal_day", "total_assets_liquidated", "assets_sold_at_profit_count", "assets_sold_at_loss_count", "winner_loser_sell_ratio", "details"]
|
|
61
|
+
};
|
|
69
62
|
}
|
|
70
63
|
|
|
71
64
|
/**
|
|
72
|
-
*
|
|
73
|
-
* @param {string} dateStr The date to run the analysis for (e.g., "2025-10-31").
|
|
74
|
-
* @param {object} dependencies The shared dependencies (db, logger, calculationUtils).
|
|
75
|
-
* @param {object} config The computation system configuration.
|
|
76
|
-
* @param {object} fetchedDependencies In-memory results from previous passes.
|
|
77
|
-
* @returns {Promise<object|null>} The analysis result or null.
|
|
65
|
+
* Statically declare dependencies.
|
|
78
66
|
*/
|
|
79
|
-
|
|
80
|
-
|
|
67
|
+
static getDependencies() {
|
|
68
|
+
return [
|
|
69
|
+
'cash-flow-liquidation', // Pass 3
|
|
70
|
+
'asset_pnl_status' // Pass 1
|
|
71
|
+
];
|
|
72
|
+
}
|
|
81
73
|
|
|
82
|
-
|
|
83
|
-
|
|
74
|
+
process() {
|
|
75
|
+
// No-op
|
|
76
|
+
}
|
|
84
77
|
|
|
85
|
-
|
|
86
|
-
const
|
|
78
|
+
getResult(fetchedDependencies) {
|
|
79
|
+
const liquidationData = fetchedDependencies['cash-flow-liquidation'];
|
|
80
|
+
const pnlStatusData = fetchedDependencies['asset_pnl_status'];
|
|
81
|
+
|
|
82
|
+
const defaults = {
|
|
83
|
+
is_net_withdrawal_day: false,
|
|
84
|
+
total_assets_liquidated: 0,
|
|
85
|
+
assets_sold_at_profit_count: 0,
|
|
86
|
+
assets_sold_at_loss_count: 0,
|
|
87
|
+
winner_loser_sell_ratio: null,
|
|
88
|
+
details: []
|
|
89
|
+
};
|
|
87
90
|
|
|
88
|
-
if (!
|
|
89
|
-
|
|
90
|
-
return null;
|
|
91
|
+
if (!liquidationData || !pnlStatusData || !liquidationData.is_net_withdrawal_day) {
|
|
92
|
+
return defaults;
|
|
91
93
|
}
|
|
92
|
-
|
|
93
|
-
const topAssets = data.top_liquidation_assets; // [{ ticker: 'MSFT', ... }]
|
|
94
|
-
const signalDate = data.signal_date;
|
|
95
|
-
const liquidationDate = data.analysis_date;
|
|
96
94
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
let profitCount = 0;
|
|
96
|
+
let lossCount = 0;
|
|
97
|
+
const details = [];
|
|
98
|
+
|
|
99
|
+
for (const asset of liquidationData.asset_flow_details) {
|
|
100
|
+
// We only care about assets that were *sold* (negative flow)
|
|
101
|
+
if (asset.net_flow_contribution < 0) {
|
|
102
|
+
const ticker = asset.ticker;
|
|
103
|
+
const pnlInfo = pnlStatusData[ticker];
|
|
104
|
+
|
|
105
|
+
if (pnlInfo) {
|
|
106
|
+
let status = 'neutral';
|
|
107
|
+
if (pnlInfo.profit_ratio > 50) {
|
|
108
|
+
status = 'profit';
|
|
109
|
+
profitCount++;
|
|
110
|
+
} else if (pnlInfo.profit_ratio < 50) {
|
|
111
|
+
status = 'loss';
|
|
112
|
+
lossCount++;
|
|
113
|
+
}
|
|
114
|
+
details.push({
|
|
115
|
+
ticker: ticker,
|
|
116
|
+
net_flow_contribution: asset.net_flow_contribution,
|
|
117
|
+
status: status,
|
|
118
|
+
profit_ratio: pnlInfo.profit_ratio
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
100
122
|
}
|
|
101
123
|
|
|
102
|
-
// 3. Define performance windows
|
|
103
|
-
const preSignalStart = this._getDateStr(signalDate, -this.PERFORMANCE_WINDOW_DAYS);
|
|
104
|
-
const preSignalEnd = signalDate;
|
|
105
|
-
const postLiquidationStart = liquidationDate;
|
|
106
|
-
const postLiquidationEnd = this._getDateStr(liquidationDate, this.PERFORMANCE_WINDOW_DAYS);
|
|
107
|
-
|
|
108
|
-
// 4. Calculate performance
|
|
109
|
-
const preSignalReturnPct = this._calculateBasketPerformance(
|
|
110
|
-
topAssets, preSignalStart, preSignalEnd
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
const postLiquidationReturnPct = this._calculateBasketPerformance(
|
|
114
|
-
topAssets, postLiquidationStart, postLiquidationEnd
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
const crowdTimingError = postLiquidationReturnPct;
|
|
118
|
-
|
|
119
124
|
return {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
post_liquidation_return_pct: postLiquidationReturnPct,
|
|
127
|
-
crowd_timing_error: crowdTimingError,
|
|
128
|
-
interpretation: "Measures the 7-day return of liquidated assets *after* being sold. Positive = asset recovered (bad timing). Negative = asset kept falling (good timing)."
|
|
125
|
+
is_net_withdrawal_day: true,
|
|
126
|
+
total_assets_liquidated: details.length,
|
|
127
|
+
assets_sold_at_profit_count: profitCount,
|
|
128
|
+
assets_sold_at_loss_count: lossCount,
|
|
129
|
+
winner_loser_sell_ratio: (lossCount > 0) ? (profitCount / lossCount) : null,
|
|
130
|
+
details: details
|
|
129
131
|
};
|
|
130
132
|
}
|
|
131
133
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
reset() {
|
|
135
|
+
// No state
|
|
136
|
+
}
|
|
134
137
|
}
|
|
135
138
|
|
|
136
139
|
module.exports = CapitalLiquidationPerformance;
|
|
@@ -1,137 +1,144 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
* that are being bought with that new capital.
|
|
2
|
+
* @fileoverview Calculation (Pass 3) for cash flow deployment.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* This metric answers: "Following a net deposit event, what percentage
|
|
5
|
+
* of that new capital is deployed *today*, and which assets are
|
|
6
|
+
* receiving the most inflow?"
|
|
7
|
+
*
|
|
8
|
+
* It *depends* on 'crowd-cash-flow-proxy' (to know if it's a
|
|
9
|
+
* deposit day) and 'asset_crowd_flow' (to see where flow went).
|
|
8
10
|
*/
|
|
9
|
-
|
|
10
|
-
const { FieldValue } = require('@google-cloud/firestore');
|
|
11
|
-
|
|
12
11
|
class CashFlowDeployment {
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* (NEW) Statically declare dependencies.
|
|
16
|
-
*/
|
|
17
|
-
static getDependencies() {
|
|
18
|
-
return ['crowd-cash-flow-proxy', 'asset-crowd-flow'];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
12
|
constructor() {
|
|
22
|
-
|
|
23
|
-
this.correlationWindow = 3;
|
|
24
|
-
this.depositSignalThreshold = -0.005; // Negative value = deposit
|
|
13
|
+
// No per-user processing
|
|
25
14
|
}
|
|
26
15
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Defines the output schema for this calculation.
|
|
18
|
+
* @returns {object} JSON Schema object
|
|
19
|
+
*/
|
|
20
|
+
static getSchema() {
|
|
21
|
+
const assetFlowSchema = {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"properties": {
|
|
24
|
+
"ticker": { "type": "string" },
|
|
25
|
+
"net_flow_contribution": { "type": "number" },
|
|
26
|
+
"percent_of_total_inflow": { "type": "number" }
|
|
27
|
+
},
|
|
28
|
+
"required": ["ticker", "net_flow_contribution", "percent_of_total_inflow"]
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"description": "On net deposit days, tracks % of new capital deployed and which assets received it.",
|
|
34
|
+
"properties": {
|
|
35
|
+
"is_net_deposit_day": {
|
|
36
|
+
"type": "boolean",
|
|
37
|
+
"description": "True if today was a net deposit day."
|
|
38
|
+
},
|
|
39
|
+
"net_cash_flow_proxy": {
|
|
40
|
+
"type": "number",
|
|
41
|
+
"description": "The total estimated net cash flow (positive)."
|
|
42
|
+
},
|
|
43
|
+
"total_net_capital_flow": {
|
|
44
|
+
"type": "number",
|
|
45
|
+
"description": "The sum of all *positive* net capital flows into assets."
|
|
46
|
+
},
|
|
47
|
+
"deployment_percentage": {
|
|
48
|
+
"type": ["number", "null"],
|
|
49
|
+
"description": "Percentage of net cash flow that was deployed (Total Net Flow / Net Cash Flow). Null if no cash flow."
|
|
50
|
+
},
|
|
51
|
+
"top_inflow_assets": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"description": "Top 5 assets receiving the most inflow.",
|
|
54
|
+
"items": assetFlowSchema
|
|
55
|
+
},
|
|
56
|
+
"asset_flow_details": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"description": "Full list of all assets and their inflows.",
|
|
59
|
+
"items": assetFlowSchema
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"required": ["is_net_deposit_day", "net_cash_flow_proxy", "total_net_capital_flow", "deployment_percentage", "top_inflow_assets", "asset_flow_details"]
|
|
63
|
+
};
|
|
31
64
|
}
|
|
32
65
|
|
|
33
66
|
/**
|
|
34
|
-
*
|
|
35
|
-
* @param {string} dateStr The date to run the analysis for (e.g., "2025-10-31").
|
|
36
|
-
* @param {object} dependencies The shared dependencies (db, logger).
|
|
37
|
-
* @param {object} config The computation system configuration.
|
|
38
|
-
* @param {object} fetchedDependencies In-memory results from previous passes.
|
|
39
|
-
* @returns {Promise<object|null>} The analysis result or null.
|
|
67
|
+
* Statically declare dependencies.
|
|
40
68
|
*/
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
69
|
+
static getDependencies() {
|
|
70
|
+
return [
|
|
71
|
+
'crowd-cash-flow-proxy', // Pass 2
|
|
72
|
+
'asset_crowd_flow' // Pass 2
|
|
73
|
+
];
|
|
74
|
+
}
|
|
46
75
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
76
|
+
process() {
|
|
77
|
+
// No-op
|
|
78
|
+
}
|
|
50
79
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
80
|
+
getResult(fetchedDependencies) {
|
|
81
|
+
const cashFlowData = fetchedDependencies['crowd-cash-flow-proxy'];
|
|
82
|
+
const assetFlowData = fetchedDependencies['asset_crowd_flow'];
|
|
83
|
+
|
|
84
|
+
const defaults = {
|
|
85
|
+
is_net_deposit_day: false,
|
|
86
|
+
net_cash_flow_proxy: 0,
|
|
87
|
+
total_net_capital_flow: 0,
|
|
88
|
+
deployment_percentage: null,
|
|
89
|
+
top_inflow_assets: [],
|
|
90
|
+
asset_flow_details: []
|
|
91
|
+
};
|
|
55
92
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
for (let i = 1; i <= this.lookbackDays; i++) {
|
|
60
|
-
const checkDate = this._getDateStr(dateStr, i);
|
|
61
|
-
dates.push({ date: checkDate, category: 'capital_flow', computation: 'crowd-cash-flow-proxy' });
|
|
93
|
+
if (!cashFlowData || !assetFlowData || cashFlowData.net_cash_flow_proxy <= 0) {
|
|
94
|
+
// Not a net deposit day
|
|
95
|
+
return defaults;
|
|
62
96
|
}
|
|
63
|
-
const histRefs = dates.map(d =>
|
|
64
|
-
db.collection(collection).doc(d.date)
|
|
65
|
-
.collection(resultsSub).doc(d.category)
|
|
66
|
-
.collection(compsSub).doc(d.computation)
|
|
67
|
-
);
|
|
68
|
-
const snapshots = await db.getAll(...histRefs);
|
|
69
|
-
const dataMap = new Map();
|
|
70
|
-
snapshots.forEach((snap, idx) => {
|
|
71
|
-
if (snap.exists) dataMap.set(idx, snap.data());
|
|
72
|
-
});
|
|
73
97
|
|
|
74
|
-
|
|
75
|
-
let
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
for (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
98
|
+
const netCashFlow = cashFlowData.net_cash_flow_proxy;
|
|
99
|
+
let totalNetInflow = 0;
|
|
100
|
+
const allFlows = [];
|
|
101
|
+
|
|
102
|
+
for (const [ticker, data] of Object.entries(assetFlowData)) {
|
|
103
|
+
// We only care about *positive* flow (deployment)
|
|
104
|
+
if (data.net_flow_contribution > 0) {
|
|
105
|
+
totalNetInflow += data.net_flow_contribution;
|
|
106
|
+
allFlows.push({
|
|
107
|
+
ticker: ticker,
|
|
108
|
+
net_flow_contribution: data.net_flow_contribution
|
|
109
|
+
});
|
|
85
110
|
}
|
|
86
111
|
}
|
|
87
112
|
|
|
88
|
-
if (
|
|
113
|
+
if (totalNetInflow === 0) {
|
|
114
|
+
// Net deposit day, but no positive flow detected
|
|
89
115
|
return {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
116
|
+
...defaults,
|
|
117
|
+
is_net_deposit_day: true,
|
|
118
|
+
net_cash_flow_proxy: netCashFlow,
|
|
119
|
+
deployment_percentage: 0
|
|
93
120
|
};
|
|
94
121
|
}
|
|
95
|
-
|
|
96
|
-
//
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
status: 'outside_correlation_window',
|
|
102
|
-
signal_day: depositSignalDay,
|
|
103
|
-
days_since_signal: daysSinceSignal
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// 5. Use the in-memory data for today's analysis
|
|
108
|
-
const netSpendPct = cashFlowData.components?.trading_effect || 0;
|
|
109
|
-
const netDepositPct = Math.abs(depositSignal.cash_flow_effect_proxy);
|
|
110
|
-
|
|
111
|
-
// Find top *buys*
|
|
112
|
-
const topBuys = Object.entries(assetFlowData)
|
|
113
|
-
.filter(([ticker, data]) => data.net_crowd_flow_pct > 0) // Find positive flow
|
|
114
|
-
.sort(([, a], [, b]) => b.net_crowd_flow_pct - a.net_crowd_flow_pct) // Sort descending
|
|
115
|
-
.slice(0, 10)
|
|
116
|
-
.map(([ticker, data]) => ({
|
|
117
|
-
ticker,
|
|
118
|
-
net_flow_pct: data.net_crowd_flow_pct
|
|
119
|
-
}));
|
|
122
|
+
|
|
123
|
+
// Calculate percent_of_total_inflow for each
|
|
124
|
+
const asset_flow_details = allFlows.map(flow => ({
|
|
125
|
+
...flow,
|
|
126
|
+
percent_of_total_inflow: (flow.net_flow_contribution / totalNetInflow) * 100
|
|
127
|
+
})).sort((a, b) => b.net_flow_contribution - a.net_flow_contribution);
|
|
120
128
|
|
|
121
129
|
return {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
pct_of_deposit_deployed_today: (netSpendPct / netDepositPct) * 100,
|
|
129
|
-
top_deployment_assets: topBuys
|
|
130
|
+
is_net_deposit_day: true,
|
|
131
|
+
net_cash_flow_proxy: netCashFlow,
|
|
132
|
+
total_net_capital_flow: totalNetInflow,
|
|
133
|
+
deployment_percentage: (netCashFlow > 0) ? (totalNetInflow / netCashFlow) * 100 : null,
|
|
134
|
+
top_inflow_assets: asset_flow_details.slice(0, 5),
|
|
135
|
+
asset_flow_details: asset_flow_details
|
|
130
136
|
};
|
|
131
137
|
}
|
|
132
138
|
|
|
133
|
-
|
|
134
|
-
|
|
139
|
+
reset() {
|
|
140
|
+
// No state
|
|
141
|
+
}
|
|
135
142
|
}
|
|
136
143
|
|
|
137
144
|
module.exports = CashFlowDeployment;
|