aiden-shared-calculations-unified 1.0.64 → 1.0.66
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,60 +1,112 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Pass 4
|
|
3
|
-
*
|
|
4
|
-
* is
|
|
2
|
+
* @fileoverview Calculation (Pass 4) for "Shark Attack" signal.
|
|
3
|
+
*
|
|
4
|
+
* This signal identifies when the "Smart Cohort" is behaving
|
|
5
|
+
* differently from both the "Dumb Cohort" and the "In-Loss Cohort"
|
|
6
|
+
* simultaneously.
|
|
7
|
+
*
|
|
8
|
+
* e.g., Smart buying, while Dumb AND Loss cohorts are selling.
|
|
9
|
+
*
|
|
10
|
+
* It *depends* on 'smart-cohort-flow', 'dumb-cohort-flow',
|
|
11
|
+
* and 'in_loss_asset_crowd_flow'.
|
|
5
12
|
*/
|
|
6
13
|
class SharkAttackSignal {
|
|
14
|
+
constructor() {
|
|
15
|
+
// No per-user processing
|
|
16
|
+
}
|
|
7
17
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Defines the output schema for this calculation.
|
|
20
|
+
* @returns {object} JSON Schema object
|
|
21
|
+
*/
|
|
22
|
+
static getSchema() {
|
|
23
|
+
const signalSchema = {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"status": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"enum": ["Shark Attack (Buy)", "Shark Bait (Sell)", "Neutral"]
|
|
29
|
+
},
|
|
30
|
+
"smart_flow_pct": { "type": "number" },
|
|
31
|
+
"dumb_flow_pct": { "type": "number" },
|
|
32
|
+
"loss_cohort_flow_pct": { "type": "number" }
|
|
33
|
+
},
|
|
34
|
+
"required": ["status", "smart_flow_pct", "dumb_flow_pct", "loss_cohort_flow_pct"]
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"description": "Generates 'Shark Attack' signals where Smart Cohort flow diverges from both Dumb and In-Loss cohorts.",
|
|
40
|
+
"patternProperties": {
|
|
41
|
+
"^.*$": signalSchema // Ticker
|
|
42
|
+
},
|
|
43
|
+
"additionalProperties": signalSchema
|
|
44
|
+
};
|
|
11
45
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Statically declare dependencies.
|
|
49
|
+
*/
|
|
50
|
+
static getDependencies() {
|
|
51
|
+
return [
|
|
52
|
+
'smart-cohort-flow', // Pass 3
|
|
53
|
+
'dumb-cohort-flow', // Pass 3
|
|
54
|
+
'in_loss_asset_crowd_flow'// Pass 3
|
|
55
|
+
];
|
|
16
56
|
}
|
|
17
57
|
|
|
18
|
-
|
|
19
|
-
|
|
58
|
+
process() {
|
|
59
|
+
// No-op
|
|
60
|
+
}
|
|
20
61
|
|
|
21
|
-
|
|
22
|
-
const
|
|
62
|
+
getResult(fetchedDependencies) {
|
|
63
|
+
const smartFlowData = fetchedDependencies['smart-cohort-flow']?.assets;
|
|
64
|
+
const dumbFlowData = fetchedDependencies['dumb-cohort-flow']?.assets;
|
|
65
|
+
const lossFlowData = fetchedDependencies['in_loss_asset_crowd_flow'];
|
|
23
66
|
|
|
24
|
-
if (!
|
|
25
|
-
|
|
26
|
-
return null;
|
|
67
|
+
if (!smartFlowData || !dumbFlowData || !lossFlowData) {
|
|
68
|
+
return {};
|
|
27
69
|
}
|
|
28
70
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
71
|
+
const allTickers = new Set([
|
|
72
|
+
...Object.keys(smartFlowData),
|
|
73
|
+
...Object.keys(dumbFlowData),
|
|
74
|
+
...Object.keys(lossFlowData)
|
|
75
|
+
]);
|
|
32
76
|
|
|
33
|
-
const
|
|
77
|
+
const result = {};
|
|
78
|
+
const THRESHOLD = 1.5; // Higher threshold for a stronger signal
|
|
34
79
|
|
|
35
80
|
for (const ticker of allTickers) {
|
|
36
|
-
const sFlow =
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
};
|
|
81
|
+
const sFlow = smartFlowData[ticker]?.net_flow_percentage || 0;
|
|
82
|
+
const dFlow = dumbFlowData[ticker]?.net_flow_percentage || 0;
|
|
83
|
+
const lFlow = lossFlowData[ticker]?.net_flow_percentage || 0;
|
|
84
|
+
|
|
85
|
+
let status = 'Neutral';
|
|
86
|
+
|
|
87
|
+
// "Shark Attack": Smart buying, Dumb selling, Loss selling
|
|
88
|
+
if (sFlow > THRESHOLD && dFlow < -THRESHOLD && lFlow < -THRESHOLD) {
|
|
89
|
+
status = 'Shark Attack (Buy)';
|
|
90
|
+
}
|
|
91
|
+
// "Shark Bait": Smart selling, Dumb buying, Loss buying
|
|
92
|
+
else if (sFlow < -THRESHOLD && dFlow > THRESHOLD && lFlow > THRESHOLD) {
|
|
93
|
+
status = 'Shark Bait (Sell)';
|
|
50
94
|
}
|
|
51
|
-
}
|
|
52
95
|
|
|
53
|
-
|
|
96
|
+
result[ticker] = {
|
|
97
|
+
status: status,
|
|
98
|
+
smart_flow_pct: sFlow,
|
|
99
|
+
dumb_flow_pct: dFlow,
|
|
100
|
+
loss_cohort_flow_pct: lFlow
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return result;
|
|
54
105
|
}
|
|
55
106
|
|
|
56
|
-
|
|
57
|
-
|
|
107
|
+
reset() {
|
|
108
|
+
// No state
|
|
109
|
+
}
|
|
58
110
|
}
|
|
59
111
|
|
|
60
112
|
module.exports = SharkAttackSignal;
|
|
@@ -1,127 +1,143 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
* of the "Smart Cohort" vs. the "Dumb Cohort" to find divergence signals.
|
|
2
|
+
* @fileoverview Calculation (Pass 4) for smart-dumb divergence index.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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'.
|
|
8
9
|
*/
|
|
9
|
-
|
|
10
10
|
class SmartDumbDivergenceIndex {
|
|
11
|
+
constructor() {
|
|
12
|
+
// No per-user processing
|
|
13
|
+
}
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
|
-
*
|
|
16
|
+
* Defines the output schema for this calculation.
|
|
17
|
+
* @returns {object} JSON Schema object
|
|
14
18
|
*/
|
|
15
|
-
static
|
|
16
|
-
|
|
17
|
-
|
|
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
|
+
};
|
|
18
32
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
+
};
|
|
22
52
|
}
|
|
23
53
|
|
|
24
54
|
/**
|
|
25
|
-
*
|
|
26
|
-
* @param {string} dateStr The date to run the analysis for (e.g., "2025-10-31").
|
|
27
|
-
* @param {object} dependencies The shared dependencies (db, logger).
|
|
28
|
-
* @param {object} config The computation system configuration.
|
|
29
|
-
* @param {object} fetchedDependencies In-memory results from previous passes.
|
|
30
|
-
* e.g., { 'smart-cohort-flow': ..., 'dumb-cohort-flow': ... }
|
|
31
|
-
* @returns {Promise<object|null>} The analysis result or null.
|
|
55
|
+
* Statically declare dependencies.
|
|
32
56
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
57
|
+
static getDependencies() {
|
|
58
|
+
return [
|
|
59
|
+
'smart-cohort-flow', // Pass 3
|
|
60
|
+
'dumb-cohort-flow' // Pass 3
|
|
61
|
+
];
|
|
62
|
+
}
|
|
35
63
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return
|
|
64
|
+
process() {
|
|
65
|
+
// No-op
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
_calculateDivergence(smartFlow, dumbFlow) {
|
|
69
|
+
const result = {};
|
|
70
|
+
if (!smartFlow || !dumbFlow) {
|
|
71
|
+
return result;
|
|
44
72
|
}
|
|
45
73
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
sectors: {}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const smartAssetFlow = smartData.asset_flow;
|
|
52
|
-
const dumbAssetFlow = dumbData.asset_flow;
|
|
53
|
-
const smartSectorFlow = smartData.sector_rotation;
|
|
54
|
-
const dumbSectorFlow = dumbData.sector_rotation;
|
|
74
|
+
const allKeys = new Set([...Object.keys(smartFlow), ...Object.keys(dumbFlow)]);
|
|
75
|
+
const THRESHOLD = 1; // Min flow %
|
|
55
76
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// 3. Correlate Assets
|
|
62
|
-
const allTickers = new Set([...Object.keys(smartAssetFlow), ...Object.keys(dumbAssetFlow)]);
|
|
63
|
-
for (const ticker of allTickers) {
|
|
64
|
-
const sFlow = smartAssetFlow[ticker]?.net_crowd_flow_pct || 0;
|
|
65
|
-
const dFlow = dumbAssetFlow[ticker]?.net_crowd_flow_pct || 0;
|
|
77
|
+
for (const key of allKeys) {
|
|
78
|
+
const sFlow = smartFlow[key]?.net_flow_percentage || 0;
|
|
79
|
+
const dFlow = dumbFlow[key]?.net_flow_percentage || 0;
|
|
66
80
|
|
|
67
|
-
|
|
68
|
-
const smartSells = sFlow <= -this.FLOW_THRESHOLD;
|
|
69
|
-
const dumbBuys = dFlow >= this.FLOW_THRESHOLD;
|
|
70
|
-
const dumbSells = dFlow <= -this.FLOW_THRESHOLD;
|
|
81
|
+
let status = 'Neutral';
|
|
71
82
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (smartBuys && dumbSells) {
|
|
76
|
-
status = 'Capitulation';
|
|
77
|
-
detail = 'Smart cohort is buying the dip from the panic-selling dumb cohort.';
|
|
78
|
-
} else if (smartSells && dumbBuys) {
|
|
79
|
-
status = 'Euphoria';
|
|
80
|
-
detail = 'Smart cohort is selling into the FOMO-buying dumb cohort.';
|
|
81
|
-
} else if (smartBuys && dumbBuys) {
|
|
82
|
-
status = 'Aligned_Buy';
|
|
83
|
-
} else if (smartSells && dumbSells) {
|
|
84
|
-
status = 'Aligned_Sell';
|
|
83
|
+
// Both buying
|
|
84
|
+
if (sFlow > THRESHOLD && dFlow > THRESHOLD) {
|
|
85
|
+
status = 'Confirmation (Buy)';
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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)';
|
|
94
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
|
+
};
|
|
95
121
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const allSectors = new Set([...Object.keys(smartSectorFlow), ...Object.keys(dumbSectorFlow)]);
|
|
99
|
-
for (const sector of allSectors) {
|
|
100
|
-
const sFlow = smartSectorFlow[sector] || 0;
|
|
101
|
-
const dFlow = dumbSectorFlow[sector] || 0;
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
102
124
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
status = 'Capitulation';
|
|
107
|
-
} else if (sFlow < 0 && dFlow > 0) {
|
|
108
|
-
status = 'Euphoria';
|
|
109
|
-
}
|
|
125
|
+
getResult(fetchedDependencies) {
|
|
126
|
+
const smartFlowData = fetchedDependencies['smart-cohort-flow'];
|
|
127
|
+
const dumbFlowData = fetchedDependencies['dumb-cohort-flow'];
|
|
110
128
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
status: status,
|
|
114
|
-
smart_cohort_flow_usd: sFlow,
|
|
115
|
-
dumb_cohort_flow_usd: dFlow
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
}
|
|
129
|
+
const assetResult = this._calculateDivergence(smartFlowData?.assets, dumbFlowData?.assets);
|
|
130
|
+
const sectorResult = this._calculateDivergence(smartFlowData?.sectors, dumbFlowData?.sectors);
|
|
119
131
|
|
|
120
|
-
return
|
|
132
|
+
return {
|
|
133
|
+
assets: assetResult,
|
|
134
|
+
sectors: sectorResult
|
|
135
|
+
};
|
|
121
136
|
}
|
|
122
137
|
|
|
123
|
-
|
|
124
|
-
|
|
138
|
+
reset() {
|
|
139
|
+
// No state
|
|
140
|
+
}
|
|
125
141
|
}
|
|
126
142
|
|
|
127
143
|
module.exports = SmartDumbDivergenceIndex;
|
|
@@ -1,127 +1,138 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
3
|
-
* of the "Smart Cohort" vs. the "Dumb Cohort" to find divergence signals.
|
|
2
|
+
* @fileoverview Calculation (Pass 5) for smart-dumb divergence v2.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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'.
|
|
8
10
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
class SmartDumbDivergenceIndexV2 {
|
|
12
|
+
constructor() {
|
|
13
|
+
// No per-user processing
|
|
14
|
+
}
|
|
11
15
|
|
|
12
16
|
/**
|
|
13
|
-
*
|
|
17
|
+
* Defines the output schema for this calculation.
|
|
18
|
+
* @returns {object} JSON Schema object
|
|
14
19
|
*/
|
|
15
|
-
static
|
|
16
|
-
|
|
17
|
-
|
|
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
|
+
};
|
|
18
33
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
+
};
|
|
22
53
|
}
|
|
23
54
|
|
|
24
55
|
/**
|
|
25
|
-
*
|
|
26
|
-
* @param {string} dateStr The date to run the analysis for (e.g., "2025-10-31").
|
|
27
|
-
* @param {object} dependencies The shared dependencies (db, logger).
|
|
28
|
-
* @param {object} config The computation system configuration.
|
|
29
|
-
* @param {object} fetchedDependencies In-memory results from previous passes.
|
|
30
|
-
* e.g., { 'smart-cohort-flow': ..., 'dumb-cohort-flow': ... }
|
|
31
|
-
* @returns {Promise<object|null>} The analysis result or null.
|
|
56
|
+
* Statically declare dependencies.
|
|
32
57
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
58
|
+
static getDependencies() {
|
|
59
|
+
return [
|
|
60
|
+
'positive_expectancy_cohort_flow', // Pass 4
|
|
61
|
+
'negative_expectancy_cohort_flow' // Pass 4
|
|
62
|
+
];
|
|
63
|
+
}
|
|
35
64
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return
|
|
65
|
+
process() {
|
|
66
|
+
// No-op
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_calculateDivergence(smartFlow, dumbFlow) {
|
|
70
|
+
const result = {};
|
|
71
|
+
if (!smartFlow || !dumbFlow) {
|
|
72
|
+
return result;
|
|
44
73
|
}
|
|
45
74
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
sectors: {}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const smartAssetFlow = smartData.asset_flow;
|
|
52
|
-
const dumbAssetFlow = dumbData.asset_flow;
|
|
53
|
-
const smartSectorFlow = smartData.sector_rotation;
|
|
54
|
-
const dumbSectorFlow = dumbData.sector_rotation;
|
|
75
|
+
const allKeys = new Set([...Object.keys(smartFlow), ...Object.keys(dumbFlow)]);
|
|
76
|
+
const THRESHOLD = 1; // Min flow %
|
|
55
77
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// 3. Correlate Assets
|
|
62
|
-
const allTickers = new Set([...Object.keys(smartAssetFlow), ...Object.keys(dumbAssetFlow)]);
|
|
63
|
-
for (const ticker of allTickers) {
|
|
64
|
-
const sFlow = smartAssetFlow[ticker]?.net_crowd_flow_pct || 0;
|
|
65
|
-
const dFlow = dumbAssetFlow[ticker]?.net_crowd_flow_pct || 0;
|
|
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;
|
|
66
83
|
|
|
67
|
-
|
|
68
|
-
const smartSells = sFlow <= -this.FLOW_THRESHOLD;
|
|
69
|
-
const dumbBuys = dFlow >= this.FLOW_THRESHOLD;
|
|
70
|
-
const dumbSells = dFlow <= -this.FLOW_THRESHOLD;
|
|
84
|
+
let status = 'Neutral';
|
|
71
85
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (
|
|
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) {
|
|
76
93
|
status = 'Capitulation';
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
}
|
|
95
|
+
else if (sFlow < -THRESHOLD && dFlow > THRESHOLD) {
|
|
79
96
|
status = 'Euphoria';
|
|
80
|
-
detail = 'Smart cohort is selling into the FOMO-buying dumb cohort.';
|
|
81
|
-
} else if (smartBuys && dumbBuys) {
|
|
82
|
-
status = 'Aligned_Buy';
|
|
83
|
-
} else if (smartSells && dumbSells) {
|
|
84
|
-
status = 'Aligned_Sell';
|
|
85
97
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
smart_cohort_flow_pct: sFlow,
|
|
92
|
-
dumb_cohort_flow_pct: dFlow
|
|
93
|
-
};
|
|
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)';
|
|
94
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
|
+
};
|
|
95
116
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const allSectors = new Set([...Object.keys(smartSectorFlow), ...Object.keys(dumbSectorFlow)]);
|
|
99
|
-
for (const sector of allSectors) {
|
|
100
|
-
const sFlow = smartSectorFlow[sector] || 0;
|
|
101
|
-
const dFlow = dumbSectorFlow[sector] || 0;
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
102
119
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
status = 'Capitulation';
|
|
107
|
-
} else if (sFlow < 0 && dFlow > 0) {
|
|
108
|
-
status = 'Euphoria';
|
|
109
|
-
}
|
|
120
|
+
getResult(fetchedDependencies) {
|
|
121
|
+
const smartFlowData = fetchedDependencies['positive_expectancy_cohort_flow'];
|
|
122
|
+
const dumbFlowData = fetchedDependencies['negative_expectancy_cohort_flow'];
|
|
110
123
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
status: status,
|
|
114
|
-
smart_cohort_flow_usd: sFlow,
|
|
115
|
-
dumb_cohort_flow_usd: dFlow
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
}
|
|
124
|
+
const assetResult = this._calculateDivergence(smartFlowData?.assets, dumbFlowData?.assets);
|
|
125
|
+
const sectorResult = this._calculateDivergence(smartFlowData?.sectors, dumbFlowData?.sectors);
|
|
119
126
|
|
|
120
|
-
return
|
|
127
|
+
return {
|
|
128
|
+
assets: assetResult,
|
|
129
|
+
sectors: sectorResult
|
|
130
|
+
};
|
|
121
131
|
}
|
|
122
132
|
|
|
123
|
-
|
|
124
|
-
|
|
133
|
+
reset() {
|
|
134
|
+
// No state
|
|
135
|
+
}
|
|
125
136
|
}
|
|
126
137
|
|
|
127
|
-
module.exports =
|
|
138
|
+
module.exports = SmartDumbDivergenceIndexV2;
|