aiden-shared-calculations-unified 1.0.95 → 1.0.96
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/calculations/capitulation/asset-volatility-estimator.js +1 -2
- package/calculations/capitulation/retail-capitulation-risk-forecast.js +1 -2
- package/calculations/ghost-book/cost-basis-density.js +1 -2
- package/calculations/ghost-book/liquidity-vacuum.js +1 -2
- package/calculations/ghost-book/retail-gamma-exposure.js +0 -1
- package/calculations/predicative-alpha/cognitive-dissonance.js +1 -2
- package/calculations/predicative-alpha/diamond-hand-fracture.js +1 -2
- package/calculations/predicative-alpha/mimetic-latency.js +1 -2
- package/package.json +1 -1
- package/calculations/legacy/activity_by_pnl_status.js +0 -119
- package/calculations/legacy/asset_crowd_flow.js +0 -163
- package/calculations/legacy/capital_deployment_strategy.js +0 -108
- package/calculations/legacy/capital_liquidation_performance.js +0 -139
- package/calculations/legacy/capital_vintage_performance.js +0 -136
- package/calculations/legacy/cash-flow-deployment.js +0 -144
- package/calculations/legacy/cash-flow-liquidation.js +0 -146
- package/calculations/legacy/crowd-cash-flow-proxy.js +0 -128
- package/calculations/legacy/crowd_conviction_score.js +0 -261
- package/calculations/legacy/crowd_sharpe_ratio_proxy.js +0 -137
- package/calculations/legacy/daily_asset_activity.js +0 -128
- package/calculations/legacy/daily_user_activity_tracker.js +0 -182
- package/calculations/legacy/deposit_withdrawal_percentage.js +0 -125
- package/calculations/legacy/diversification_pnl.js +0 -115
- package/calculations/legacy/drawdown_response.js +0 -137
- package/calculations/legacy/dumb-cohort-flow.js +0 -238
- package/calculations/legacy/gain_response.js +0 -137
- package/calculations/legacy/historical_performance_aggregator.js +0 -85
- package/calculations/legacy/in_loss_asset_crowd_flow.js +0 -168
- package/calculations/legacy/in_profit_asset_crowd_flow.js +0 -168
- package/calculations/legacy/negative_expectancy_cohort_flow.js +0 -232
- package/calculations/legacy/new_allocation_percentage.js +0 -98
- package/calculations/legacy/paper_vs_diamond_hands.js +0 -107
- package/calculations/legacy/position_count_pnl.js +0 -120
- package/calculations/legacy/positive_expectancy_cohort_flow.js +0 -232
- package/calculations/legacy/profit_cohort_divergence.js +0 -115
- package/calculations/legacy/profitability_migration.js +0 -104
- package/calculations/legacy/reallocation_increase_percentage.js +0 -104
- package/calculations/legacy/risk_appetite_change.js +0 -97
- package/calculations/legacy/sector_rotation.js +0 -117
- package/calculations/legacy/shark_attack_signal.js +0 -112
- package/calculations/legacy/smart-cohort-flow.js +0 -238
- package/calculations/legacy/smart-dumb-divergence-index.js +0 -143
- package/calculations/legacy/smart_dumb_divergence_index_v2.js +0 -138
- package/calculations/legacy/smart_money_flow.js +0 -198
- package/calculations/legacy/social-predictive-regime-state.js +0 -102
- package/calculations/legacy/social-topic-driver-index.js +0 -147
- package/calculations/legacy/social-topic-predictive-potential.js +0 -461
- package/calculations/legacy/social_flow_correlation.js +0 -112
- package/calculations/legacy/speculator_adjustment_activity.js +0 -103
- package/calculations/legacy/strategy-performance.js +0 -265
- package/calculations/legacy/tsl_effectiveness.js +0 -85
- package/calculations/legacy/user-investment-profile.js +0 -313
- package/calculations/legacy/user_expectancy_score.js +0 -106
- package/calculations/legacy/user_profitability_tracker.js +0 -131
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Meta-calculation (Pass 3) that tracks the performance
|
|
3
|
-
* of capital "vintages" by analyzing the market returns of assets
|
|
4
|
-
* that were bought following a crowd-wide deposit signal.
|
|
5
|
-
*
|
|
6
|
-
* --- META REFACTOR (v2) ---
|
|
7
|
-
* This calculation is now stateless. It declares its dependencies and
|
|
8
|
-
* expects them to be passed to its `process` method.
|
|
9
|
-
* It still loads its own price data.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
class CapitalVintagePerformance {
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* (NEW) Statically declare dependencies.
|
|
16
|
-
*/
|
|
17
|
-
static getDependencies() {
|
|
18
|
-
return ['cash-flow-deployment'];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
constructor() {
|
|
22
|
-
this.PERFORMANCE_WINDOW_DAYS = 7;
|
|
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);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
_calculateBasketPerformance(assets, startDateStr, endDateStr) {
|
|
52
|
-
if (!assets || assets.length === 0) return 0;
|
|
53
|
-
let totalReturn = 0;
|
|
54
|
-
let validAssets = 0;
|
|
55
|
-
for (const asset of assets) {
|
|
56
|
-
const ticker = asset.ticker;
|
|
57
|
-
const instrumentId = this.tickerToIdMap[ticker];
|
|
58
|
-
if (!instrumentId || !this.priceMap[instrumentId]) continue;
|
|
59
|
-
const startPrice = this.priceMap[instrumentId][startDateStr];
|
|
60
|
-
const endPrice = this.priceMap[instrumentId][endDateStr];
|
|
61
|
-
if (startPrice && endPrice && startPrice > 0) {
|
|
62
|
-
const assetReturn = (endPrice - startPrice) / startPrice;
|
|
63
|
-
totalReturn += assetReturn;
|
|
64
|
-
validAssets++;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
if (validAssets === 0) return 0;
|
|
68
|
-
return (totalReturn / validAssets) * 100;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* REFACTORED PROCESS METHOD
|
|
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.
|
|
78
|
-
*/
|
|
79
|
-
async process(dateStr, dependencies, config, fetchedDependencies) {
|
|
80
|
-
const { db, logger, calculationUtils } = dependencies;
|
|
81
|
-
|
|
82
|
-
// 1. Load all price/mapping data
|
|
83
|
-
await this._loadDependencies(calculationUtils);
|
|
84
|
-
|
|
85
|
-
// 2. Get dependency from `fetchedDependencies`
|
|
86
|
-
const data = fetchedDependencies['cash-flow-deployment'];
|
|
87
|
-
|
|
88
|
-
if (!data || data.status !== 'analysis_complete') {
|
|
89
|
-
logger.log('WARN', `[CapitalVintage] Skipping ${dateStr}, no valid 'cash-flow-deployment' data found.`);
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const topAssets = data.top_deployment_assets; // [{ ticker: 'AAPL', ... }]
|
|
94
|
-
const signalDate = data.signal_date; // The day the deposit signal occurred
|
|
95
|
-
const deploymentDate = data.analysis_date; // The day the capital was spent (dateStr)
|
|
96
|
-
|
|
97
|
-
if (!topAssets || topAssets.length === 0) {
|
|
98
|
-
logger.log('INFO', `[CapitalVintage] No top assets deployed on ${dateStr}.`);
|
|
99
|
-
return { status: 'no_deployment' };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// 3. Define performance windows
|
|
103
|
-
const preSignalStart = this._getDateStr(signalDate, -this.PERFORMANCE_WINDOW_DAYS);
|
|
104
|
-
const preSignalEnd = signalDate;
|
|
105
|
-
const postDeployStart = deploymentDate;
|
|
106
|
-
const postDeployEnd = this._getDateStr(deploymentDate, this.PERFORMANCE_WINDOW_DAYS);
|
|
107
|
-
|
|
108
|
-
// 4. Calculate performance
|
|
109
|
-
const preSignalReturnPct = this._calculateBasketPerformance(
|
|
110
|
-
topAssets, preSignalStart, preSignalEnd
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
const postDeploymentReturnPct = this._calculateBasketPerformance(
|
|
114
|
-
topAssets, postDeployStart, postDeployEnd
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
const momentum = postDeploymentReturnPct - preSignalReturnPct;
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
status: 'analysis_complete',
|
|
121
|
-
signal_date: signalDate,
|
|
122
|
-
deployment_date: deploymentDate,
|
|
123
|
-
performance_window_days: this.PERFORMANCE_WINDOW_DAYS,
|
|
124
|
-
deployed_assets: topAssets.map(a => a.ticker),
|
|
125
|
-
pre_signal_return_pct: preSignalReturnPct,
|
|
126
|
-
post_deployment_return_pct: postDeploymentReturnPct,
|
|
127
|
-
return_momentum: momentum,
|
|
128
|
-
interpretation: "Measures the 7-day return of deployed assets *after* deployment vs. 7-days *before* the signal."
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async getResult() { return null; }
|
|
133
|
-
reset() {}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
module.exports = CapitalVintagePerformance;
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 3) for cash flow deployment.
|
|
3
|
-
*
|
|
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).
|
|
10
|
-
*/
|
|
11
|
-
class CashFlowDeployment {
|
|
12
|
-
constructor() {
|
|
13
|
-
// No per-user processing
|
|
14
|
-
}
|
|
15
|
-
|
|
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
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Statically declare dependencies.
|
|
68
|
-
*/
|
|
69
|
-
static getDependencies() {
|
|
70
|
-
return [
|
|
71
|
-
'crowd-cash-flow-proxy', // Pass 2
|
|
72
|
-
'asset_crowd_flow' // Pass 2
|
|
73
|
-
];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
process() {
|
|
77
|
-
// No-op
|
|
78
|
-
}
|
|
79
|
-
|
|
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
|
-
};
|
|
92
|
-
|
|
93
|
-
if (!cashFlowData || !assetFlowData || cashFlowData.net_cash_flow_proxy <= 0) {
|
|
94
|
-
// Not a net deposit day
|
|
95
|
-
return defaults;
|
|
96
|
-
}
|
|
97
|
-
|
|
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
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (totalNetInflow === 0) {
|
|
114
|
-
// Net deposit day, but no positive flow detected
|
|
115
|
-
return {
|
|
116
|
-
...defaults,
|
|
117
|
-
is_net_deposit_day: true,
|
|
118
|
-
net_cash_flow_proxy: netCashFlow,
|
|
119
|
-
deployment_percentage: 0
|
|
120
|
-
};
|
|
121
|
-
}
|
|
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);
|
|
128
|
-
|
|
129
|
-
return {
|
|
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
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
reset() {
|
|
140
|
-
// No state
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
module.exports = CashFlowDeployment;
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 3) for cash flow liquidation.
|
|
3
|
-
*
|
|
4
|
-
* This metric answers: "Following a net withdrawal event, what percentage
|
|
5
|
-
* of that withdrawal was funded by selling *today*, and which assets
|
|
6
|
-
* are being liquidated the most?"
|
|
7
|
-
*
|
|
8
|
-
* It *depends* on 'crowd-cash-flow-proxy' (to know if it's a
|
|
9
|
-
* withdrawal day) and 'asset_crowd_flow' (to see where flow went).
|
|
10
|
-
*/
|
|
11
|
-
class CashFlowLiquidation {
|
|
12
|
-
constructor() {
|
|
13
|
-
// No per-user processing
|
|
14
|
-
}
|
|
15
|
-
|
|
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_outflow": { "type": "number" }
|
|
27
|
-
},
|
|
28
|
-
"required": ["ticker", "net_flow_contribution", "percent_of_total_outflow"]
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
"type": "object",
|
|
33
|
-
"description": "On net withdrawal days, tracks % of withdrawal funded by selling and which assets were sold.",
|
|
34
|
-
"properties": {
|
|
35
|
-
"is_net_withdrawal_day": {
|
|
36
|
-
"type": "boolean",
|
|
37
|
-
"description": "True if today was a net withdrawal day."
|
|
38
|
-
},
|
|
39
|
-
"net_cash_flow_proxy": {
|
|
40
|
-
"type": "number",
|
|
41
|
-
"description": "The total estimated net cash flow (negative)."
|
|
42
|
-
},
|
|
43
|
-
"total_net_capital_flow": {
|
|
44
|
-
"type": "number",
|
|
45
|
-
"description": "The sum of all *negative* net capital flows from assets (total liquidation)."
|
|
46
|
-
},
|
|
47
|
-
"funding_percentage": {
|
|
48
|
-
"type": ["number", "null"],
|
|
49
|
-
"description": "Percentage of net cash flow funded by selling (Total Net Flow / Net Cash Flow). Null if no cash flow."
|
|
50
|
-
},
|
|
51
|
-
"top_outflow_assets": {
|
|
52
|
-
"type": "array",
|
|
53
|
-
"description": "Top 5 assets being liquidated.",
|
|
54
|
-
"items": assetFlowSchema
|
|
55
|
-
},
|
|
56
|
-
"asset_flow_details": {
|
|
57
|
-
"type": "array",
|
|
58
|
-
"description": "Full list of all assets and their outflows.",
|
|
59
|
-
"items": assetFlowSchema
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
"required": ["is_net_withdrawal_day", "net_cash_flow_proxy", "total_net_capital_flow", "funding_percentage", "top_outflow_assets", "asset_flow_details"]
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Statically declare dependencies.
|
|
68
|
-
*/
|
|
69
|
-
static getDependencies() {
|
|
70
|
-
return [
|
|
71
|
-
'crowd-cash-flow-proxy', // Pass 2
|
|
72
|
-
'asset_crowd_flow' // Pass 2
|
|
73
|
-
];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
process() {
|
|
77
|
-
// No-op
|
|
78
|
-
}
|
|
79
|
-
|
|
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_withdrawal_day: false,
|
|
86
|
-
net_cash_flow_proxy: 0,
|
|
87
|
-
total_net_capital_flow: 0,
|
|
88
|
-
funding_percentage: null,
|
|
89
|
-
top_outflow_assets: [],
|
|
90
|
-
asset_flow_details: []
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
if (!cashFlowData || !assetFlowData || cashFlowData.net_cash_flow_proxy >= 0) {
|
|
94
|
-
// Not a net withdrawal day
|
|
95
|
-
return defaults;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const netCashFlow = cashFlowData.net_cash_flow_proxy; // This is a negative number
|
|
99
|
-
let totalNetOutflow = 0;
|
|
100
|
-
const allFlows = [];
|
|
101
|
-
|
|
102
|
-
for (const [ticker, data] of Object.entries(assetFlowData)) {
|
|
103
|
-
// We only care about *negative* flow (liquidation)
|
|
104
|
-
if (data.net_flow_contribution < 0) {
|
|
105
|
-
totalNetOutflow += data.net_flow_contribution; // Summing negative numbers
|
|
106
|
-
allFlows.push({
|
|
107
|
-
ticker: ticker,
|
|
108
|
-
net_flow_contribution: data.net_flow_contribution
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (totalNetOutflow === 0) {
|
|
114
|
-
// Net withdrawal day, but no negative flow detected
|
|
115
|
-
return {
|
|
116
|
-
...defaults,
|
|
117
|
-
is_net_withdrawal_day: true,
|
|
118
|
-
net_cash_flow_proxy: netCashFlow,
|
|
119
|
-
funding_percentage: 0
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Calculate percent_of_total_outflow for each
|
|
124
|
-
const asset_flow_details = allFlows.map(flow => ({
|
|
125
|
-
...flow,
|
|
126
|
-
// (Negative / Negative) * 100 = Positive %
|
|
127
|
-
percent_of_total_outflow: (flow.net_flow_contribution / totalNetOutflow) * 100
|
|
128
|
-
})).sort((a, b) => a.net_flow_contribution - b.net_flow_contribution); // Sort ascending (most negative first)
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
is_net_withdrawal_day: true,
|
|
132
|
-
net_cash_flow_proxy: netCashFlow,
|
|
133
|
-
total_net_capital_flow: totalNetOutflow,
|
|
134
|
-
// (Negative / Negative) * 100 = Positive %
|
|
135
|
-
funding_percentage: (netCashFlow < 0) ? (totalNetOutflow / netCashFlow) * 100 : null,
|
|
136
|
-
top_outflow_assets: asset_flow_details.slice(0, 5),
|
|
137
|
-
asset_flow_details: asset_flow_details
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
reset() {
|
|
142
|
-
// No state
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
module.exports = CashFlowLiquidation;
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 2) for crowd cash flow proxy.
|
|
3
|
-
*
|
|
4
|
-
* This metric estimates the net cash flow (deposits vs. withdrawals)
|
|
5
|
-
* of the crowd by analyzing changes in 'Invested' vs. 'Cash' balances
|
|
6
|
-
* in user portfolios.
|
|
7
|
-
*
|
|
8
|
-
* It's a proxy because it doesn't see real bank transactions, but
|
|
9
|
-
* infers from the total portfolio value vs. the invested amount.
|
|
10
|
-
*/
|
|
11
|
-
class CrowdCashFlowProxy {
|
|
12
|
-
constructor() {
|
|
13
|
-
this.total_portfolio_value_yesterday = 0;
|
|
14
|
-
this.total_invested_yesterday = 0;
|
|
15
|
-
|
|
16
|
-
this.total_portfolio_value_today = 0;
|
|
17
|
-
this.total_invested_today = 0;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Defines the output schema for this calculation.
|
|
22
|
-
* @returns {object} JSON Schema object
|
|
23
|
-
*/
|
|
24
|
-
static getSchema() {
|
|
25
|
-
return {
|
|
26
|
-
"type": "object",
|
|
27
|
-
"description": "Estimates net cash flow (deposits/withdrawals) by comparing changes in total portfolio value vs. invested capital.",
|
|
28
|
-
"properties": {
|
|
29
|
-
"total_portfolio_value_yesterday": {
|
|
30
|
-
"type": "number",
|
|
31
|
-
"description": "Sum of all users' total portfolio values from yesterday."
|
|
32
|
-
},
|
|
33
|
-
"total_invested_yesterday": {
|
|
34
|
-
"type": "number",
|
|
35
|
-
"description": "Sum of all users' invested capital from yesterday."
|
|
36
|
-
},
|
|
37
|
-
"total_portfolio_value_today": {
|
|
38
|
-
"type": "number",
|
|
39
|
-
"description": "Sum of all users' total portfolio values from today."
|
|
40
|
-
},
|
|
41
|
-
"total_invested_today": {
|
|
42
|
-
"type": "number",
|
|
43
|
-
"description": "Sum of all users' invested capital from today."
|
|
44
|
-
},
|
|
45
|
-
"total_pnl_contribution": {
|
|
46
|
-
"type": "number",
|
|
47
|
-
"description": "The estimated change in portfolio value attributable to P&L."
|
|
48
|
-
},
|
|
49
|
-
"net_cash_flow_proxy": {
|
|
50
|
-
"type": "number",
|
|
51
|
-
"description": "The estimated net cash flow (Deposits - Withdrawals). Positive indicates net deposits."
|
|
52
|
-
},
|
|
53
|
-
"net_cash_flow_proxy_pct": {
|
|
54
|
-
"type": "number",
|
|
55
|
-
"description": "The net cash flow proxy as a percentage of yesterday's total portfolio value."
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
"required": [
|
|
59
|
-
"total_portfolio_value_yesterday", "total_invested_yesterday",
|
|
60
|
-
"total_portfolio_value_today", "total_invested_today",
|
|
61
|
-
"total_pnl_contribution", "net_cash_flow_proxy", "net_cash_flow_proxy_pct"
|
|
62
|
-
]
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
_getPortfolioValues(portfolio) {
|
|
67
|
-
if (!portfolio || !portfolio.Summary) {
|
|
68
|
-
return { portfolioValue: 0, invested: 0 };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Total value (Invested + Cash)
|
|
72
|
-
const portfolioValue = portfolio.Summary.PortfolioValue || 0;
|
|
73
|
-
// Total invested in positions
|
|
74
|
-
const invested = portfolio.Summary.Invested || 0;
|
|
75
|
-
|
|
76
|
-
return { portfolioValue, invested };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
process(todayPortfolio, yesterdayPortfolio) {
|
|
80
|
-
if (!todayPortfolio || !yesterdayPortfolio) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const yValues = this._getPortfolioValues(yesterdayPortfolio);
|
|
85
|
-
const tValues = this._getPortfolioValues(todayPortfolio);
|
|
86
|
-
|
|
87
|
-
this.total_portfolio_value_yesterday += yValues.portfolioValue;
|
|
88
|
-
this.total_invested_yesterday += yValues.invested;
|
|
89
|
-
|
|
90
|
-
this.total_portfolio_value_today += tValues.portfolioValue;
|
|
91
|
-
this.total_invested_today += tValues.invested;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
getResult() {
|
|
95
|
-
// P&L is the change in *invested* value, not total value
|
|
96
|
-
const totalPnl = this.total_invested_today - this.total_invested_yesterday;
|
|
97
|
-
|
|
98
|
-
// The change in total value is (P&L + Net Cash Flow)
|
|
99
|
-
// So, Net Cash Flow = (Total Value Change) - P&L
|
|
100
|
-
const totalValueChange = this.total_portfolio_value_today - this.total_portfolio_value_yesterday;
|
|
101
|
-
|
|
102
|
-
const netCashFlowProxy = totalValueChange - totalPnl;
|
|
103
|
-
|
|
104
|
-
const netCashFlowProxyPct = (this.total_portfolio_value_yesterday > 0)
|
|
105
|
-
? (netCashFlowProxy / this.total_portfolio_value_yesterday) * 100
|
|
106
|
-
: 0;
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
total_portfolio_value_yesterday: this.total_portfolio_value_yesterday,
|
|
110
|
-
total_invested_yesterday: this.total_invested_yesterday,
|
|
111
|
-
total_portfolio_value_today: this.total_portfolio_value_today,
|
|
112
|
-
total_invested_today: this.total_invested_today,
|
|
113
|
-
|
|
114
|
-
total_pnl_contribution: totalPnl,
|
|
115
|
-
net_cash_flow_proxy: netCashFlowProxy,
|
|
116
|
-
net_cash_flow_proxy_pct: netCashFlowProxyPct
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
reset() {
|
|
121
|
-
this.total_portfolio_value_yesterday = 0;
|
|
122
|
-
this.total_invested_yesterday = 0;
|
|
123
|
-
this.total_portfolio_value_today = 0;
|
|
124
|
-
this.total_invested_today = 0;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
module.exports = CrowdCashFlowProxy;
|