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,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 4) for smart-dumb divergence index.
|
|
3
|
-
*
|
|
4
|
-
* This metric answers: "What divergence signals (e.g., capitulation,
|
|
5
|
-
* euphoria) can be found by comparing the net asset and sector flow
|
|
6
|
-
* of the 'smart cohort' vs. the 'dumb cohort'?"
|
|
7
|
-
*
|
|
8
|
-
* It *depends* on 'smart-cohort-flow' and 'dumb-cohort-flow'.
|
|
9
|
-
*/
|
|
10
|
-
class SmartDumbDivergenceIndex {
|
|
11
|
-
constructor() {
|
|
12
|
-
// No per-user processing
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Defines the output schema for this calculation.
|
|
17
|
-
* @returns {object} JSON Schema object
|
|
18
|
-
*/
|
|
19
|
-
static getSchema() {
|
|
20
|
-
const signalSchema = {
|
|
21
|
-
"type": "object",
|
|
22
|
-
"properties": {
|
|
23
|
-
"status": {
|
|
24
|
-
"type": "string",
|
|
25
|
-
"enum": ["Capitulation", "Euphoria", "Confirmation (Buy)", "Confirmation (Sell)", "Divergence (Smart Buy)", "Divergence (Smart Sell)", "Neutral"]
|
|
26
|
-
},
|
|
27
|
-
"smart_flow_pct": { "type": "number" },
|
|
28
|
-
"dumb_flow_pct": { "type": "number" }
|
|
29
|
-
},
|
|
30
|
-
"required": ["status", "smart_flow_pct", "dumb_flow_pct"]
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
"type": "object",
|
|
35
|
-
"description": "Generates divergence signals by comparing net flow of 'Smart' vs. 'Dumb' cohorts, by asset and sector.",
|
|
36
|
-
"properties": {
|
|
37
|
-
"assets": {
|
|
38
|
-
"type": "object",
|
|
39
|
-
"description": "Divergence signals per asset.",
|
|
40
|
-
"patternProperties": { "^.*$": signalSchema }, // Ticker
|
|
41
|
-
"additionalProperties": signalSchema
|
|
42
|
-
},
|
|
43
|
-
"sectors": {
|
|
44
|
-
"type": "object",
|
|
45
|
-
"description": "Divergence signals per sector.",
|
|
46
|
-
"patternProperties": { "^.*$": signalSchema }, // Sector
|
|
47
|
-
"additionalProperties": signalSchema
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
"required": ["assets", "sectors"]
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Statically declare dependencies.
|
|
56
|
-
*/
|
|
57
|
-
static getDependencies() {
|
|
58
|
-
return [
|
|
59
|
-
'smart-cohort-flow', // Pass 3
|
|
60
|
-
'dumb-cohort-flow' // Pass 3
|
|
61
|
-
];
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
process() {
|
|
65
|
-
// No-op
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
_calculateDivergence(smartFlow, dumbFlow) {
|
|
69
|
-
const result = {};
|
|
70
|
-
if (!smartFlow || !dumbFlow) {
|
|
71
|
-
return result;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const allKeys = new Set([...Object.keys(smartFlow), ...Object.keys(dumbFlow)]);
|
|
75
|
-
const THRESHOLD = 1; // Min flow %
|
|
76
|
-
|
|
77
|
-
for (const key of allKeys) {
|
|
78
|
-
const sFlow = smartFlow[key]?.net_flow_percentage || 0;
|
|
79
|
-
const dFlow = dumbFlow[key]?.net_flow_percentage || 0;
|
|
80
|
-
|
|
81
|
-
let status = 'Neutral';
|
|
82
|
-
|
|
83
|
-
// Both buying
|
|
84
|
-
if (sFlow > THRESHOLD && dFlow > THRESHOLD) {
|
|
85
|
-
status = 'Confirmation (Buy)';
|
|
86
|
-
}
|
|
87
|
-
// Both selling
|
|
88
|
-
else if (sFlow < -THRESHOLD && dFlow < -THRESHOLD) {
|
|
89
|
-
status = 'Confirmation (Sell)';
|
|
90
|
-
}
|
|
91
|
-
// Smart buying, Dumb selling
|
|
92
|
-
else if (sFlow > THRESHOLD && dFlow < -THRESHOLD) {
|
|
93
|
-
status = 'Capitulation'; // Smart buying the dip from dumb money
|
|
94
|
-
}
|
|
95
|
-
// Smart selling, Dumb buying
|
|
96
|
-
else if (sFlow < -THRESHOLD && dFlow > THRESHOLD) {
|
|
97
|
-
status = 'Euphoria'; // Smart selling into dumb money fomo
|
|
98
|
-
}
|
|
99
|
-
// Smart buying, Dumb neutral
|
|
100
|
-
else if (sFlow > THRESHOLD && Math.abs(dFlow) < THRESHOLD) {
|
|
101
|
-
status = 'Divergence (Smart Buy)';
|
|
102
|
-
}
|
|
103
|
-
// Smart selling, Dumb neutral
|
|
104
|
-
else if (sFlow < -THRESHOLD && Math.abs(dFlow) < THRESHOLD) {
|
|
105
|
-
status = 'Divergence (Smart Sell)';
|
|
106
|
-
}
|
|
107
|
-
// Dumb buying, Smart neutral
|
|
108
|
-
else if (dFlow > THRESHOLD && Math.abs(sFlow) < THRESHOLD) {
|
|
109
|
-
status = 'Divergence (Smart Sell)'; // Implied smart sell
|
|
110
|
-
}
|
|
111
|
-
// Dumb selling, Smart neutral
|
|
112
|
-
else if (dFlow < -THRESHOLD && Math.abs(sFlow) < THRESHOLD) {
|
|
113
|
-
status = 'Divergence (Smart Buy)'; // Implied smart buy
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
result[key] = {
|
|
117
|
-
status: status,
|
|
118
|
-
smart_flow_pct: sFlow,
|
|
119
|
-
dumb_flow_pct: dFlow
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
return result;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
getResult(fetchedDependencies) {
|
|
126
|
-
const smartFlowData = fetchedDependencies['smart-cohort-flow'];
|
|
127
|
-
const dumbFlowData = fetchedDependencies['dumb-cohort-flow'];
|
|
128
|
-
|
|
129
|
-
const assetResult = this._calculateDivergence(smartFlowData?.assets, dumbFlowData?.assets);
|
|
130
|
-
const sectorResult = this._calculateDivergence(smartFlowData?.sectors, dumbFlowData?.sectors);
|
|
131
|
-
|
|
132
|
-
return {
|
|
133
|
-
assets: assetResult,
|
|
134
|
-
sectors: sectorResult
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
reset() {
|
|
139
|
-
// No state
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
module.exports = SmartDumbDivergenceIndex;
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 5) for smart-dumb divergence v2.
|
|
3
|
-
*
|
|
4
|
-
* This is a more advanced version that likely uses a different
|
|
5
|
-
* cohort definition (e.g., 'expectancy' based) and combines more
|
|
6
|
-
* signals.
|
|
7
|
-
*
|
|
8
|
-
* It *depends* on 'positive_expectancy_cohort_flow' and
|
|
9
|
-
* 'negative_expectancy_cohort_flow'.
|
|
10
|
-
*/
|
|
11
|
-
class SmartDumbDivergenceIndexV2 {
|
|
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 signalSchema = {
|
|
22
|
-
"type": "object",
|
|
23
|
-
"properties": {
|
|
24
|
-
"status": {
|
|
25
|
-
"type": "string",
|
|
26
|
-
"enum": ["Capitulation", "Euphoria", "Confirmation (Buy)", "Confirmation (Sell)", "Divergence (Smart Buy)", "Divergence (Smart Sell)", "Neutral"]
|
|
27
|
-
},
|
|
28
|
-
"smart_flow_pct": { "type": "number" },
|
|
29
|
-
"dumb_flow_pct": { "type": "number" }
|
|
30
|
-
},
|
|
31
|
-
"required": ["status", "smart_flow_pct", "dumb_flow_pct"]
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
"type": "object",
|
|
36
|
-
"description": "Generates divergence signals (V2) by comparing net flow of 'Positive Expectancy' vs. 'Negative Expectancy' cohorts.",
|
|
37
|
-
"properties": {
|
|
38
|
-
"assets": {
|
|
39
|
-
"type": "object",
|
|
40
|
-
"description": "Divergence signals per asset.",
|
|
41
|
-
"patternProperties": { "^.*$": signalSchema }, // Ticker
|
|
42
|
-
"additionalProperties": signalSchema
|
|
43
|
-
},
|
|
44
|
-
"sectors": {
|
|
45
|
-
"type": "object",
|
|
46
|
-
"description": "Divergence signals per sector.",
|
|
47
|
-
"patternProperties": { "^.*$": signalSchema }, // Sector
|
|
48
|
-
"additionalProperties": signalSchema
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
"required": ["assets", "sectors"]
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Statically declare dependencies.
|
|
57
|
-
*/
|
|
58
|
-
static getDependencies() {
|
|
59
|
-
return [
|
|
60
|
-
'positive_expectancy_cohort_flow', // Pass 4
|
|
61
|
-
'negative_expectancy_cohort_flow' // Pass 4
|
|
62
|
-
];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
process() {
|
|
66
|
-
// No-op
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
_calculateDivergence(smartFlow, dumbFlow) {
|
|
70
|
-
const result = {};
|
|
71
|
-
if (!smartFlow || !dumbFlow) {
|
|
72
|
-
return result;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const allKeys = new Set([...Object.keys(smartFlow), ...Object.keys(dumbFlow)]);
|
|
76
|
-
const THRESHOLD = 1; // Min flow %
|
|
77
|
-
|
|
78
|
-
for (const key of allKeys) {
|
|
79
|
-
// "Smart" = Positive Expectancy
|
|
80
|
-
const sFlow = smartFlow[key]?.net_flow_percentage || 0;
|
|
81
|
-
// "Dumb" = Negative Expectancy
|
|
82
|
-
const dFlow = dumbFlow[key]?.net_flow_percentage || 0;
|
|
83
|
-
|
|
84
|
-
let status = 'Neutral';
|
|
85
|
-
|
|
86
|
-
if (sFlow > THRESHOLD && dFlow > THRESHOLD) {
|
|
87
|
-
status = 'Confirmation (Buy)';
|
|
88
|
-
}
|
|
89
|
-
else if (sFlow < -THRESHOLD && dFlow < -THRESHOLD) {
|
|
90
|
-
status = 'Confirmation (Sell)';
|
|
91
|
-
}
|
|
92
|
-
else if (sFlow > THRESHOLD && dFlow < -THRESHOLD) {
|
|
93
|
-
status = 'Capitulation';
|
|
94
|
-
}
|
|
95
|
-
else if (sFlow < -THRESHOLD && dFlow > THRESHOLD) {
|
|
96
|
-
status = 'Euphoria';
|
|
97
|
-
}
|
|
98
|
-
else if (sFlow > THRESHOLD && Math.abs(dFlow) < THRESHOLD) {
|
|
99
|
-
status = 'Divergence (Smart Buy)';
|
|
100
|
-
}
|
|
101
|
-
else if (sFlow < -THRESHOLD && Math.abs(dFlow) < THRESHOLD) {
|
|
102
|
-
status = 'Divergence (Smart Sell)';
|
|
103
|
-
}
|
|
104
|
-
else if (dFlow > THRESHOLD && Math.abs(sFlow) < THRESHOLD) {
|
|
105
|
-
status = 'Divergence (Smart Sell)'; // Implied
|
|
106
|
-
}
|
|
107
|
-
else if (dFlow < -THRESHOLD && Math.abs(sFlow) < THRESHOLD) {
|
|
108
|
-
status = 'Divergence (Smart Buy)'; // Implied
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
result[key] = {
|
|
112
|
-
status: status,
|
|
113
|
-
smart_flow_pct: sFlow,
|
|
114
|
-
dumb_flow_pct: dFlow
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
return result;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
getResult(fetchedDependencies) {
|
|
121
|
-
const smartFlowData = fetchedDependencies['positive_expectancy_cohort_flow'];
|
|
122
|
-
const dumbFlowData = fetchedDependencies['negative_expectancy_cohort_flow'];
|
|
123
|
-
|
|
124
|
-
const assetResult = this._calculateDivergence(smartFlowData?.assets, dumbFlowData?.assets);
|
|
125
|
-
const sectorResult = this._calculateDivergence(smartFlowData?.sectors, dumbFlowData?.sectors);
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
assets: assetResult,
|
|
129
|
-
sectors: sectorResult
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
reset() {
|
|
134
|
-
// No state
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
module.exports = SmartDumbDivergenceIndexV2;
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 3) for smart money flow.
|
|
3
|
-
*
|
|
4
|
-
* This metric calculates the "Net Crowd Flow Percentage" for
|
|
5
|
-
* "Smart Money" users, defined as users who were profitable
|
|
6
|
-
* in 5 of the last 7 days.
|
|
7
|
-
*
|
|
8
|
-
* This calculation *depends* on 'user_profitability_tracker'
|
|
9
|
-
* to identify the cohort.
|
|
10
|
-
*/
|
|
11
|
-
const { loadInstrumentMappings } = require('../../utils/sector_mapping_provider');
|
|
12
|
-
|
|
13
|
-
class SmartMoneyFlow {
|
|
14
|
-
constructor() {
|
|
15
|
-
// This calculation only aggregates by sector
|
|
16
|
-
this.sectorData = new Map();
|
|
17
|
-
this.mappings = null;
|
|
18
|
-
this.smartMoneyUserIds = null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Defines the output schema for this calculation.
|
|
23
|
-
* @returns {object} JSON Schema object
|
|
24
|
-
*/
|
|
25
|
-
static getSchema() {
|
|
26
|
-
return {
|
|
27
|
-
"type": "object",
|
|
28
|
-
"description": "Calculates net capital flow % (price-adjusted) for the 'Smart Money' cohort (profitable 5 of last 7 days), aggregated by sector.",
|
|
29
|
-
"properties": {
|
|
30
|
-
"cohort_size": {
|
|
31
|
-
"type": "number",
|
|
32
|
-
"description": "The number of users identified as being in the Smart Money Cohort."
|
|
33
|
-
},
|
|
34
|
-
"sectors": {
|
|
35
|
-
"type": "object",
|
|
36
|
-
"description": "Price-adjusted net flow per sector.",
|
|
37
|
-
"patternProperties": {
|
|
38
|
-
// Sector
|
|
39
|
-
"^.*$": {
|
|
40
|
-
"type": "object",
|
|
41
|
-
"properties": {
|
|
42
|
-
"net_flow_percentage": { "type": "number" },
|
|
43
|
-
"total_invested_today": { "type": "number" },
|
|
44
|
-
"total_invested_yesterday": { "type": "number" }
|
|
45
|
-
},
|
|
46
|
-
"required": ["net_flow_percentage", "total_invested_today", "total_invested_yesterday"]
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
"additionalProperties": {
|
|
50
|
-
"type": "object",
|
|
51
|
-
"properties": {
|
|
52
|
-
"net_flow_percentage": { "type": "number" },
|
|
53
|
-
"total_invested_today": { "type": "number" },
|
|
54
|
-
"total_invested_yesterday": { "type": "number" }
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
"required": ["cohort_size", "sectors"]
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Statically declare dependencies.
|
|
65
|
-
*/
|
|
66
|
-
static getDependencies() {
|
|
67
|
-
return ['user_profitability_tracker'];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
_getPortfolioPositions(portfolio) {
|
|
71
|
-
return portfolio?.PublicPositions || portfolio?.AggregatedPositions;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
_initSector(sector) {
|
|
75
|
-
if (!this.sectorData.has(sector)) {
|
|
76
|
-
this.sectorData.set(sector, {
|
|
77
|
-
total_invested_yesterday: 0,
|
|
78
|
-
total_invested_today: 0,
|
|
79
|
-
price_change_yesterday: 0,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Helper to get the cohort IDs from the dependency.
|
|
86
|
-
*/
|
|
87
|
-
_getSmartMoneyCohort(fetchedDependencies) {
|
|
88
|
-
if (this.smartMoneyUserIds) {
|
|
89
|
-
return this.smartMoneyUserIds;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const profitabilityData = fetchedDependencies['user_profitability_tracker'];
|
|
93
|
-
if (!profitabilityData || !profitabilityData.user_details) {
|
|
94
|
-
return new Set();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
this.smartMoneyUserIds = new Set();
|
|
98
|
-
for (const [userId, data] of Object.entries(profitabilityData.user_details)) {
|
|
99
|
-
// Definition: Profitable in 5 of the last 7 days.
|
|
100
|
-
if (data.profitable_days_7d >= 5) {
|
|
101
|
-
this.smartMoneyUserIds.add(userId);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return this.smartMoneyUserIds;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
process(todayPortfolio, yesterdayPortfolio, userId, context, todayInsights, yesterdayInsights, fetchedDependencies) {
|
|
108
|
-
const smartMoneyCohort = this._getSmartMoneyCohort(fetchedDependencies);
|
|
109
|
-
|
|
110
|
-
// This user is not in the "smart money cohort", skip.
|
|
111
|
-
if (!smartMoneyCohort.has(userId)) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (!todayPortfolio || !yesterdayPortfolio) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const yPos = this._getPortfolioPositions(yesterdayPortfolio);
|
|
120
|
-
const tPos = this._getPortfolioPositions(todayPortfolio);
|
|
121
|
-
|
|
122
|
-
const yPosMap = new Map(yPos?.map(p => [p.InstrumentID, p]) || []);
|
|
123
|
-
const tPosMap = new Map(tPos?.map(p => [p.InstrumentID, p]) || []);
|
|
124
|
-
|
|
125
|
-
const allInstrumentIds = new Set([...yPosMap.keys(), ...tPosMap.keys()]);
|
|
126
|
-
|
|
127
|
-
if (!this.mappings) {
|
|
128
|
-
// Context contains the mappings loaded in Pass 1
|
|
129
|
-
this.mappings = context.mappings;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
for (const instrumentId of allInstrumentIds) {
|
|
133
|
-
if (!instrumentId) continue;
|
|
134
|
-
|
|
135
|
-
const yP = yPosMap.get(instrumentId);
|
|
136
|
-
const tP = tPosMap.get(instrumentId);
|
|
137
|
-
|
|
138
|
-
const yInvested = yP?.InvestedAmount || yP?.Amount || 0;
|
|
139
|
-
const tInvested = tP?.InvestedAmount || tP?.Amount || 0;
|
|
140
|
-
|
|
141
|
-
// Get sector and initialize it
|
|
142
|
-
const sector = this.mappings.instrumentToSector[instrumentId] || 'Other';
|
|
143
|
-
this._initSector(sector);
|
|
144
|
-
const sectorAsset = this.sectorData.get(sector);
|
|
145
|
-
|
|
146
|
-
if (yInvested > 0) {
|
|
147
|
-
const yPriceChange = (yP?.PipsRate || 0) / (yP?.OpenRate || 1);
|
|
148
|
-
sectorAsset.total_invested_yesterday += yInvested;
|
|
149
|
-
sectorAsset.price_change_yesterday += yPriceChange * yInvested;
|
|
150
|
-
}
|
|
151
|
-
if (tInvested > 0) {
|
|
152
|
-
sectorAsset.total_invested_today += tInvested;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async getResult(fetchedDependencies) {
|
|
158
|
-
// Ensure mappings are loaded (can be from context or loaded now)
|
|
159
|
-
if (!this.mappings) {
|
|
160
|
-
this.mappings = await loadInstrumentMappings();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Ensure cohort is calculated at least once
|
|
164
|
-
const smartMoneyCohort = this._getSmartMoneyCohort(fetchedDependencies);
|
|
165
|
-
|
|
166
|
-
// Calculate Sector Flow
|
|
167
|
-
const sectorResult = {};
|
|
168
|
-
for (const [sector, data] of this.sectorData.entries()) {
|
|
169
|
-
const { total_invested_yesterday, total_invested_today, price_change_yesterday } = data;
|
|
170
|
-
|
|
171
|
-
if (total_invested_yesterday > 0) {
|
|
172
|
-
const avg_price_change_pct = price_change_yesterday / total_invested_yesterday;
|
|
173
|
-
const price_contribution = total_invested_yesterday * avg_price_change_pct;
|
|
174
|
-
const flow_contribution = total_invested_today - (total_invested_yesterday + price_contribution);
|
|
175
|
-
const net_flow_percentage = (flow_contribution / total_invested_yesterday) * 100;
|
|
176
|
-
|
|
177
|
-
sectorResult[sector] = {
|
|
178
|
-
net_flow_percentage: net_flow_percentage,
|
|
179
|
-
total_invested_today: total_invested_today,
|
|
180
|
-
total_invested_yesterday: total_invested_yesterday
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return {
|
|
186
|
-
cohort_size: smartMoneyCohort.size,
|
|
187
|
-
sectors: sectorResult
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
reset() {
|
|
192
|
-
this.sectorData.clear();
|
|
193
|
-
this.mappings = null;
|
|
194
|
-
this.smartMoneyUserIds = null;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
module.exports = SmartMoneyFlow;
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Calculation (Pass 5) for social predictive regime.
|
|
3
|
-
*
|
|
4
|
-
* This metric answers: "Are we in a 'social-driven' or 'flow-driven'
|
|
5
|
-
* market regime?"
|
|
6
|
-
*
|
|
7
|
-
* It *depends* on 'social_flow_correlation' (Pass 4) to make
|
|
8
|
-
* a determination.
|
|
9
|
-
*/
|
|
10
|
-
class SocialPredictiveRegimeState {
|
|
11
|
-
constructor() {
|
|
12
|
-
// No per-user processing
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Defines the output schema for this calculation.
|
|
17
|
-
* @returns {object} JSON Schema object
|
|
18
|
-
*/
|
|
19
|
-
static getSchema() {
|
|
20
|
-
return {
|
|
21
|
-
"type": "object",
|
|
22
|
-
"description": "Determines if the market is in a 'social-driven' or 'flow-driven' regime based on correlation.",
|
|
23
|
-
"properties": {
|
|
24
|
-
"regime": {
|
|
25
|
-
"type": "string",
|
|
26
|
-
"enum": ["Social-Driven", "Flow-Driven", "Decoupled", "Unknown"],
|
|
27
|
-
"description": "The current market regime."
|
|
28
|
-
},
|
|
29
|
-
"average_correlation": {
|
|
30
|
-
"type": "number",
|
|
31
|
-
"description": "The average correlation value across all assets from 'social_flow_correlation'."
|
|
32
|
-
},
|
|
33
|
-
"correlated_assets_count": {
|
|
34
|
-
"type": "number",
|
|
35
|
-
"description": "Number of assets with a strong positive correlation."
|
|
36
|
-
},
|
|
37
|
-
"decoupled_assets_count": {
|
|
38
|
-
"type": "number",
|
|
39
|
-
"description": "Number of assets with a weak or negative correlation."
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
"required": ["regime", "average_correlation", "correlated_assets_count", "decoupled_assets_count"]
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Statically declare dependencies.
|
|
48
|
-
*/
|
|
49
|
-
static getDependencies() {
|
|
50
|
-
return [
|
|
51
|
-
'social_flow_correlation' // Pass 4
|
|
52
|
-
];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
process() {
|
|
56
|
-
// No-op
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
getResult(fetchedDependencies) {
|
|
60
|
-
const correlationData = fetchedDependencies['social_flow_correlation'];
|
|
61
|
-
|
|
62
|
-
const defaults = {
|
|
63
|
-
regime: "Unknown",
|
|
64
|
-
average_correlation: 0,
|
|
65
|
-
correlated_assets_count: 0,
|
|
66
|
-
decoupled_assets_count: 0
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
if (!correlationData) {
|
|
70
|
-
return defaults;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const correlations = Object.values(correlationData).map(d => d.correlation_30d).filter(c => c !== null);
|
|
74
|
-
if (correlations.length === 0) {
|
|
75
|
-
return defaults;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const avgCorrelation = correlations.reduce((a, b) => a + b, 0) / correlations.length;
|
|
79
|
-
const correlatedCount = correlations.filter(c => c > 0.5).length;
|
|
80
|
-
const decoupledCount = correlations.filter(c => c <= 0.5).length;
|
|
81
|
-
|
|
82
|
-
let regime = "Decoupled";
|
|
83
|
-
if (avgCorrelation > 0.5 && correlatedCount > decoupledCount) {
|
|
84
|
-
regime = "Social-Driven";
|
|
85
|
-
} else if (avgCorrelation < 0.2) {
|
|
86
|
-
regime = "Flow-Driven"; // Social is not predictive, flow is
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
regime: regime,
|
|
91
|
-
average_correlation: avgCorrelation,
|
|
92
|
-
correlated_assets_count: correlatedCount,
|
|
93
|
-
decoupled_assets_count: decoupledCount
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
reset() {
|
|
98
|
-
// No state
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
module.exports = SocialPredictiveRegimeState;
|