aiden-shared-calculations-unified 1.0.75 → 1.0.77
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/{pnl/asset_pnl_status.js → core/asset-pnl-status.js} +20 -0
- package/calculations/{asset_metrics/asset_position_size.js → core/asset-position-size.js} +25 -1
- package/calculations/{pnl/average_daily_pnl_all_users.js → core/average-daily-pnl-all-users.js} +22 -0
- package/calculations/{pnl/average_daily_pnl_per_sector.js → core/average-daily-pnl-per-sector.js} +22 -0
- package/calculations/{pnl/average_daily_pnl_per_stock.js → core/average-daily-pnl-per-stock.js} +22 -0
- package/calculations/{pnl/average_daily_position_pnl.js → core/average-daily-position-pnl.js} +22 -0
- package/calculations/{behavioural/historical/holding_duration_per_asset.js → core/holding-duration-per-asset.js} +44 -4
- package/calculations/{meta/gem_instrument-price-momentum.js → core/instrument-price-momentum-20d.js} +17 -4
- package/calculations/{short_and_long_stats/long_position_per_stock.js → core/long-position-per-stock.js} +24 -0
- package/calculations/{behavioural/overall_holding_duration.js → core/overall-holding-duration.js} +27 -3
- package/calculations/{pnl/overall_profitability_ratio.js → core/overall-profitability-ratio.js} +24 -0
- package/calculations/{insights/daily_buy_sell_sentiment_count.js → core/platform-buy-sell-sentiment.js} +22 -0
- package/calculations/{insights/historical/daily_bought_vs_sold_count.js → core/platform-daily-bought-vs-sold-count.js} +22 -0
- package/calculations/{insights/historical/daily_ownership_delta.js → core/platform-daily-ownership-delta.js} +22 -0
- package/calculations/{insights/daily_ownership_per_sector.js → core/platform-ownership-per-sector.js} +22 -0
- package/calculations/{insights/daily_total_positions_held.js → core/platform-total-positions-held.js} +22 -0
- package/calculations/{pnl/pnl_distribution_per_stock.js → core/pnl-distribution-per-stock.js} +24 -0
- package/calculations/{pnl/profitability_ratio_per_sector,js → core/profitability-ratio-per-sector.js} +35 -5
- package/calculations/{pnl/profitability_ratio_per_stock.js → core/profitability-ratio-per-stock.js} +24 -0
- package/calculations/{pnl/profitability_skew_per_stock.js → core/profitability-skew-per-stock.js} +24 -0
- package/calculations/{pnl/profitable_and_unprofitable_status.js → core/profitable-and-unprofitable-status.js} +24 -0
- package/calculations/{short_and_long_stats/sentiment_per_stock.js → core/sentiment-per-stock.js} +20 -0
- package/calculations/{short_and_long_stats/short_position_per_stock.js → core/short-position-per-stock.js} +24 -0
- package/calculations/{socialPosts/social_activity_aggregation.js → core/social-activity-aggregation.js} +32 -8
- package/calculations/{socialPosts → core}/social-asset-posts-trend.js +30 -8
- package/calculations/{socialPosts/social_event_correlation.js → core/social-event-correlation.js} +24 -2
- package/calculations/{socialPosts/social_sentiment_aggregation.js → core/social-sentiment-aggregation.js} +23 -0
- package/calculations/{socialPosts → core}/social-top-mentioned-words.js +31 -8
- package/calculations/{socialPosts → core}/social-topic-interest-evolution.js +35 -11
- package/calculations/{socialPosts → core}/social-topic-sentiment-matrix.js +34 -10
- package/calculations/{socialPosts → core}/social-word-mentions-trend.js +36 -11
- package/calculations/{speculators/speculator_asset_sentiment.js → core/speculator-asset-sentiment.js} +20 -0
- package/calculations/{speculators/speculator_danger_zone.js → core/speculator-danger-zone.js} +22 -0
- package/calculations/{speculators/distance_to_stop_loss_per_leverage.js → core/speculator-distance-to-stop-loss-per-leverage.js} +24 -0
- package/calculations/{speculators/distance_to_tp_per_leverage.js → core/speculator-distance-to-tp-per-leverage.js} +24 -0
- package/calculations/{speculators/entry_distance_to_sl_per_leverage.js → core/speculator-entry-distance-to-sl-per-leverage.js} +24 -0
- package/calculations/{speculators/entry_distance_to_tp_per_leverage.js → core/speculator-entry-distance-to-tp-per-leverage.js} +24 -0
- package/calculations/{speculators/leverage_per_asset.js → core/speculator-leverage-per-asset.js} +20 -0
- package/calculations/{speculators/leverage_per_sector.js → core/speculator-leverage-per-sector.js} +22 -0
- package/calculations/{speculators/risk_reward_ratio_per_asset.js → core/speculator-risk-reward-ratio-per-asset.js} +20 -0
- package/calculations/{speculators/stop_loss_distance_by_sector_short_long_breakdown.js → core/speculator-stop-loss-distance-by-sector-short-long-breakdown.js} +22 -0
- package/calculations/{speculators/stop_loss_distance_by_ticker_short_long_breakdown.js → core/speculator-stop-loss-distance-by-ticker-short-long-breakdown.js} +24 -0
- package/calculations/{speculators/stop_loss_per_asset.js → core/speculator-stop-loss-per-asset.js} +24 -0
- package/calculations/{speculators/take_profit_per_asset.js → core/speculator-take-profit-per-asset.js} +24 -0
- package/calculations/{speculators/tsl_per_asset.js → core/speculator-tsl-per-asset.js} +24 -0
- package/calculations/{short_and_long_stats/total_long_figures.js → core/total-long-figures.js} +24 -0
- package/calculations/{sectors/total_long_per_sector.js → core/total-long-per-sector.js} +22 -0
- package/calculations/{short_and_long_stats/total_short_figures.js → core/total-short-figures.js} +24 -0
- package/calculations/{sectors/total_short_per_sector.js → core/total-short-per-sector.js} +22 -0
- package/calculations/{sanity/users_processed.js → core/users-processed.js} +22 -0
- package/calculations/gauss/cohort-capital-flow.js +216 -0
- package/calculations/gauss/cohort-definer.js +211 -0
- package/calculations/gauss/daily-dna-filter.js +130 -0
- package/calculations/gauss/gauss-divergence-signal.js +160 -0
- package/calculations/{meta/gem_cohort-momentum-state.js → gem/cohort-momentum-state.js} +22 -7
- package/calculations/{behavioural/historical/gem_cohort-skill-definition.js → gem/cohort-skill-definition.js} +18 -1
- package/calculations/{sentiment/gem_platform-conviction-divergence.js → gem/platform-conviction-divergence.js} +13 -0
- package/calculations/{meta/gem_quant-skill-alpha-signal.js → gem/quant-skill-alpha-signal.js} +25 -8
- package/calculations/{behavioural/historical/gem_skilled-cohort-flow.js → gem/skilled-cohort-flow.js} +16 -2
- package/calculations/{meta/gem_skilled-unskilled-divergence.js → gem/skilled-unskilled-divergence.js} +18 -4
- package/calculations/{behavioural/historical/gem_unskilled-cohort-flow.js → gem/unskilled-cohort-flow.js} +16 -2
- package/calculations/helix/helix-contrarian-signal.js +154 -0
- package/calculations/helix/herd-consensus-score.js +152 -0
- package/calculations/helix/winner-loser-flow.js +206 -0
- package/calculations/{behavioural/historical → legacy}/asset_crowd_flow.js +1 -1
- package/calculations/{sentiment/historical → legacy}/crowd_conviction_score.js +1 -1
- package/calculations/{activity/historical → legacy}/daily_asset_activity.js +1 -1
- package/calculations/{behavioural/historical → legacy}/dumb-cohort-flow.js +1 -1
- package/calculations/{behavioural/historical → legacy}/in_loss_asset_crowd_flow.js +1 -1
- package/calculations/{behavioural/historical → legacy}/in_profit_asset_crowd_flow.js +1 -1
- package/calculations/{speculators/historical → legacy}/risk_appetite_change.js +3 -1
- package/calculations/{sectors/historical → legacy}/sector_rotation.js +1 -1
- package/calculations/{behavioural/historical → legacy}/smart-cohort-flow.js +1 -1
- package/calculations/{behavioural/historical → legacy}/smart_money_flow.js +1 -1
- package/calculations/{behavioural/historical → legacy}/user-investment-profile.js +2 -2
- package/calculations/pyro/risk-appetite-index.js +153 -0
- package/calculations/pyro/squeeze-potential.js +158 -0
- package/calculations/pyro/volatility-signal.js +133 -0
- package/package.json +1 -1
- /package/calculations/{activity/historical → legacy}/activity_by_pnl_status.js +0 -0
- /package/calculations/{meta → legacy}/capital_deployment_strategy.js +0 -0
- /package/calculations/{meta → legacy}/capital_liquidation_performance.js +0 -0
- /package/calculations/{meta → legacy}/capital_vintage_performance.js +0 -0
- /package/calculations/{meta → legacy}/cash-flow-deployment.js +0 -0
- /package/calculations/{meta → legacy}/cash-flow-liquidation.js +0 -0
- /package/calculations/{capital_flow/historical → legacy}/crowd-cash-flow-proxy.js +0 -0
- /package/calculations/{meta → legacy}/crowd_sharpe_ratio_proxy.js +0 -0
- /package/calculations/{activity/historical → legacy}/daily_user_activity_tracker.js +0 -0
- /package/calculations/{capital_flow/historical → legacy}/deposit_withdrawal_percentage.js +0 -0
- /package/calculations/{sectors/historical → legacy}/diversification_pnl.js +0 -0
- /package/calculations/{behavioural/historical → legacy}/drawdown_response.js +0 -0
- /package/calculations/{behavioural/historical → legacy}/gain_response.js +0 -0
- /package/calculations/{behavioural/historical → legacy}/historical_performance_aggregator.js +0 -0
- /package/calculations/{meta → legacy}/negative_expectancy_cohort_flow.js +0 -0
- /package/calculations/{capital_flow/historical → legacy}/new_allocation_percentage.js +0 -0
- /package/calculations/{behavioural/historical → legacy}/paper_vs_diamond_hands.js +0 -0
- /package/calculations/{behavioural/historical → legacy}/position_count_pnl.js +0 -0
- /package/calculations/{meta → legacy}/positive_expectancy_cohort_flow.js +0 -0
- /package/calculations/{meta → legacy}/profit_cohort_divergence.js +0 -0
- /package/calculations/{pnl/historical → legacy}/profitability_migration.js +0 -0
- /package/calculations/{capital_flow/historical → legacy}/reallocation_increase_percentage.js +0 -0
- /package/calculations/{meta → legacy}/shark_attack_signal.js +0 -0
- /package/calculations/{meta → legacy}/smart-dumb-divergence-index.js +0 -0
- /package/calculations/{meta → legacy}/smart_dumb_divergence_index_v2.js +0 -0
- /package/calculations/{meta → legacy}/social-predictive-regime-state.js +0 -0
- /package/calculations/{meta → legacy}/social-topic-driver-index.js +0 -0
- /package/calculations/{meta → legacy}/social-topic-predictive-potential.js +0 -0
- /package/calculations/{meta → legacy}/social_flow_correlation.js +0 -0
- /package/calculations/{activity/historical → legacy}/speculator_adjustment_activity.js +0 -0
- /package/calculations/{backtests → legacy}/strategy-performance.js +0 -0
- /package/calculations/{speculators/historical → legacy}/tsl_effectiveness.js +0 -0
- /package/calculations/{meta → legacy}/user_expectancy_score.js +0 -0
- /package/calculations/{pnl/historical → legacy}/user_profitability_tracker.js +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview GAUSS Product Line (Pass 4)
|
|
3
|
+
*
|
|
4
|
+
* This is the final, stateless signal generator for the Gauss line.
|
|
5
|
+
* It answers: "What is the net 'Smart vs. Dumb' flow divergence?"
|
|
6
|
+
*
|
|
7
|
+
* It consumes the daily flows from all defined cohorts (Pass 3)
|
|
8
|
+
* and aggregates them into a final, actionable signal.
|
|
9
|
+
*/
|
|
10
|
+
class GaussDivergenceSignal {
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Defines the output schema for this calculation.
|
|
14
|
+
* @returns {object} JSON Schema object
|
|
15
|
+
*/
|
|
16
|
+
static getSchema() {
|
|
17
|
+
const tickerSchema = {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"signal": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"enum": ["Strong Buy", "Buy", "Neutral", "Sell", "Strong Sell"]
|
|
23
|
+
},
|
|
24
|
+
"gauss_score": {
|
|
25
|
+
"type": "number",
|
|
26
|
+
"description": "Final divergence score (-10 to +10). Positive = Smart Buying, Negative = Smart Selling."
|
|
27
|
+
},
|
|
28
|
+
"smart_flow_total_pct": {
|
|
29
|
+
"type": "number",
|
|
30
|
+
"description": "The total %-point flow from all 'Smart' cohorts."
|
|
31
|
+
},
|
|
32
|
+
"dumb_flow_total_pct": {
|
|
33
|
+
"type": "number",
|
|
34
|
+
"description": "The total %-point flow from all 'Dumb' cohorts."
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"required": ["signal", "gauss_score", "smart_flow_total_pct", "dumb_flow_total_pct"]
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"description": "Generates a final divergence signal by aggregating flow from Smart vs. Dumb cohorts.",
|
|
43
|
+
"patternProperties": {
|
|
44
|
+
"^.*$": tickerSchema // Ticker
|
|
45
|
+
},
|
|
46
|
+
"additionalProperties": tickerSchema
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Statically defines all metadata for the manifest builder.
|
|
52
|
+
*/
|
|
53
|
+
static getMetadata() {
|
|
54
|
+
return {
|
|
55
|
+
type: 'meta',
|
|
56
|
+
rootDataDependencies: [],
|
|
57
|
+
isHistorical: false,
|
|
58
|
+
userType: 'n/a',
|
|
59
|
+
category: 'gauss'
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Statically declare dependencies.
|
|
65
|
+
*/
|
|
66
|
+
static getDependencies() {
|
|
67
|
+
return [
|
|
68
|
+
'cohort-capital-flow' // from gauss (Pass 3)
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Simple tanh normalization. Scales any number to a -10 to +10 range,
|
|
74
|
+
* with diminishing returns for extreme values.
|
|
75
|
+
* 0 -> 0, 1 -> 7.6, 2 -> 9.6, 3 -> 9.95
|
|
76
|
+
* We scale the input to tune sensitivity.
|
|
77
|
+
*/
|
|
78
|
+
_normalize(score) {
|
|
79
|
+
// A score of 2.0 (%-point flow) will be ~9.6
|
|
80
|
+
// A score of 1.0 (%-point flow) will be ~7.6
|
|
81
|
+
return Math.tanh(score / 2.0) * 10;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* This is a 'meta' calculation. It runs once.
|
|
86
|
+
*/
|
|
87
|
+
async process(dateStr, dependencies, config, fetchedDependencies) {
|
|
88
|
+
const { logger } = dependencies;
|
|
89
|
+
|
|
90
|
+
const cohortFlows = fetchedDependencies['cohort-capital-flow'];
|
|
91
|
+
|
|
92
|
+
if (!cohortFlows) {
|
|
93
|
+
logger.log('WARN', `[gauss/gauss-divergence-signal] Missing dependency 'cohort-capital-flow' for ${dateStr}.`);
|
|
94
|
+
return {};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Define which cohorts are "Smart" and which are "Dumb"
|
|
98
|
+
// These names must match the keys from Pass 2
|
|
99
|
+
const SMART_COHORTS = ['smart_investors', 'smart_scalpers'];
|
|
100
|
+
const DUMB_COHORTS = ['fomo_chasers', 'patient_losers', 'fomo_bagholders'];
|
|
101
|
+
|
|
102
|
+
const blendedFlows = new Map(); // Map<ticker, { smart: 0, dumb: 0 }>
|
|
103
|
+
|
|
104
|
+
// 1. Blend all cohort flows into two buckets
|
|
105
|
+
for (const cohortName in cohortFlows) {
|
|
106
|
+
const isSmart = SMART_COHORTS.includes(cohortName);
|
|
107
|
+
const isDumb = DUMB_COHORTS.includes(cohortName);
|
|
108
|
+
if (!isSmart && !isDumb) continue;
|
|
109
|
+
|
|
110
|
+
const assets = cohortFlows[cohortName]?.assets;
|
|
111
|
+
if (!assets) continue;
|
|
112
|
+
|
|
113
|
+
for (const [ticker, data] of Object.entries(assets)) {
|
|
114
|
+
if (!blendedFlows.has(ticker)) {
|
|
115
|
+
blendedFlows.set(ticker, { smart: 0, dumb: 0 });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Use net_flow_contribution, which is the %-point flow
|
|
119
|
+
const flow = data.net_flow_contribution || 0;
|
|
120
|
+
|
|
121
|
+
if (isSmart) {
|
|
122
|
+
blendedFlows.get(ticker).smart += flow;
|
|
123
|
+
} else if (isDumb) {
|
|
124
|
+
blendedFlows.get(ticker).dumb += flow;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 2. Calculate final signal
|
|
130
|
+
const result = {};
|
|
131
|
+
for (const [ticker, data] of blendedFlows.entries()) {
|
|
132
|
+
|
|
133
|
+
// The core signal is the divergence: (Smart Flow - Dumb Flow)
|
|
134
|
+
// If Smart buys (+1) and Dumb sells (-1), score is +2.
|
|
135
|
+
// If Smart sells (-1) and Dumb buys (+1), score is -2.
|
|
136
|
+
const divergence = data.smart - data.dumb;
|
|
137
|
+
|
|
138
|
+
// Normalize the score to a -10 to +10 range
|
|
139
|
+
const gauss_score = this._normalize(divergence);
|
|
140
|
+
|
|
141
|
+
let signal = "Neutral";
|
|
142
|
+
if (gauss_score > 7.0) signal = "Strong Buy"; // e.g., > 1.5% net divergence
|
|
143
|
+
else if (gauss_score > 2.0) signal = "Buy";
|
|
144
|
+
else if (gauss_score < -7.0) signal = "Strong Sell";
|
|
145
|
+
else if (gauss_score < -2.0) signal = "Sell";
|
|
146
|
+
|
|
147
|
+
result[ticker] = {
|
|
148
|
+
signal: signal,
|
|
149
|
+
gauss_score: gauss_score,
|
|
150
|
+
smart_flow_total_pct: data.smart,
|
|
151
|
+
dumb_flow_total_pct: data.dumb
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Final output is compact and non-sharded.
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = GaussDivergenceSignal;
|
|
@@ -48,14 +48,27 @@ class CohortMomentumState {
|
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Statically defines all metadata for the manifest builder.
|
|
53
|
+
*/
|
|
54
|
+
static getMetadata() {
|
|
55
|
+
return {
|
|
56
|
+
type: 'meta',
|
|
57
|
+
rootDataDependencies: [],
|
|
58
|
+
isHistorical: false,
|
|
59
|
+
userType: 'n/a',
|
|
60
|
+
category: 'gem'
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
51
64
|
/**
|
|
52
65
|
* Statically declare dependencies.
|
|
53
66
|
*/
|
|
54
67
|
static getDependencies() {
|
|
55
68
|
return [
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'
|
|
69
|
+
'skilled-cohort-flow', // Pass 2 (gem)
|
|
70
|
+
'unskilled-cohort-flow', // Pass 2 (gem)
|
|
71
|
+
'instrument-price-momentum-20d' // Pass 1 (core)
|
|
59
72
|
];
|
|
60
73
|
}
|
|
61
74
|
|
|
@@ -68,9 +81,10 @@ class CohortMomentumState {
|
|
|
68
81
|
* @param {object} fetchedDependencies - Results from previous passes.
|
|
69
82
|
*/
|
|
70
83
|
getResult(fetchedDependencies) {
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
const
|
|
84
|
+
// FIX: Use normalized dependency names
|
|
85
|
+
const skilledFlowData = fetchedDependencies['skilled-cohort-flow']?.assets;
|
|
86
|
+
const unskilledFlowData = fetchedDependencies['unskilled-cohort-flow']?.assets;
|
|
87
|
+
const momentumData = fetchedDependencies['instrument-price-momentum-20d'];
|
|
74
88
|
|
|
75
89
|
if (!skilledFlowData || !unskilledFlowData || !momentumData) {
|
|
76
90
|
return {};
|
|
@@ -79,7 +93,8 @@ class CohortMomentumState {
|
|
|
79
93
|
const result = {};
|
|
80
94
|
const allTickers = new Set([
|
|
81
95
|
...Object.keys(skilledFlowData),
|
|
82
|
-
...Object.keys(unskilledFlowData)
|
|
96
|
+
...Object.keys(unskilledFlowData),
|
|
97
|
+
...Object.keys(momentumData) // Include momentum tickers
|
|
83
98
|
]);
|
|
84
99
|
|
|
85
100
|
for (const ticker of allTickers) {
|
|
@@ -39,6 +39,19 @@ class CohortSkillDefinition {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Statically defines all metadata for the manifest builder.
|
|
44
|
+
*/
|
|
45
|
+
static getMetadata() {
|
|
46
|
+
return {
|
|
47
|
+
type: 'standard',
|
|
48
|
+
rootDataDependencies: ['history'], // This calculation uses history data
|
|
49
|
+
isHistorical: false, // It processes today's history doc
|
|
50
|
+
userType: 'all',
|
|
51
|
+
category: 'gem'
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
42
55
|
/**
|
|
43
56
|
* This is a Pass 1 calculation and has no dependencies.
|
|
44
57
|
*/
|
|
@@ -52,7 +65,11 @@ class CohortSkillDefinition {
|
|
|
52
65
|
* @param {string} userId - The user ID.
|
|
53
66
|
*/
|
|
54
67
|
process(rootData, userId) {
|
|
55
|
-
|
|
68
|
+
// FIX: The process signature for a 'standard' calc is different.
|
|
69
|
+
// It receives (todayPortfolio, yesterdayPortfolio, userId, context, ...)
|
|
70
|
+
// The 'history' data is inside todayPortfolio.
|
|
71
|
+
|
|
72
|
+
const history = rootData?.history?.all; // Correctly accessing history data
|
|
56
73
|
if (!history) {
|
|
57
74
|
return; // Skip user if they have no history data
|
|
58
75
|
}
|
|
@@ -51,6 +51,19 @@ class PlatformConvictionDivergence {
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Statically defines all metadata for the manifest builder.
|
|
56
|
+
*/
|
|
57
|
+
static getMetadata() {
|
|
58
|
+
return {
|
|
59
|
+
type: 'standard',
|
|
60
|
+
rootDataDependencies: ['portfolio', 'insights'],
|
|
61
|
+
isHistorical: false,
|
|
62
|
+
userType: 'all',
|
|
63
|
+
category: 'gem'
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
54
67
|
/**
|
|
55
68
|
* This is a Pass 1 calculation and has no dependencies.
|
|
56
69
|
*/
|
package/calculations/{meta/gem_quant-skill-alpha-signal.js → gem/quant-skill-alpha-signal.js}
RENAMED
|
@@ -54,16 +54,32 @@ class QuantSkillAlphaSignal {
|
|
|
54
54
|
"additionalProperties": tickerSchema
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Statically defines all metadata for the manifest builder.
|
|
60
|
+
*/
|
|
61
|
+
static getMetadata() {
|
|
62
|
+
return {
|
|
63
|
+
type: 'meta',
|
|
64
|
+
rootDataDependencies: [],
|
|
65
|
+
isHistorical: false,
|
|
66
|
+
userType: 'n/a',
|
|
67
|
+
category: 'gem'
|
|
68
|
+
};
|
|
69
|
+
}
|
|
57
70
|
|
|
58
71
|
/**
|
|
59
72
|
* Statically declare dependencies.
|
|
60
73
|
*/
|
|
61
74
|
static getDependencies() {
|
|
62
75
|
return [
|
|
63
|
-
'
|
|
64
|
-
'
|
|
65
|
-
'
|
|
66
|
-
'
|
|
76
|
+
// Internal 'gem' dependencies (normalized)
|
|
77
|
+
'skilled-unskilled-divergence',
|
|
78
|
+
'cohort-momentum-state',
|
|
79
|
+
'platform-conviction-divergence',
|
|
80
|
+
|
|
81
|
+
// Dependencies on 'core' product line (normalized)
|
|
82
|
+
'social-sentiment-aggregation'
|
|
67
83
|
];
|
|
68
84
|
}
|
|
69
85
|
|
|
@@ -76,10 +92,11 @@ class QuantSkillAlphaSignal {
|
|
|
76
92
|
* @param {object} fetchedDependencies - Results from previous passes.
|
|
77
93
|
*/
|
|
78
94
|
getResult(fetchedDependencies) {
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const
|
|
95
|
+
// FIX: Use normalized dependency names
|
|
96
|
+
const divergenceData = fetchedDependencies['skilled-unskilled-divergence']?.assets;
|
|
97
|
+
const momentumData = fetchedDependencies['cohort-momentum-state'];
|
|
98
|
+
const platformData = fetchedDependencies['platform-conviction-divergence'];
|
|
99
|
+
const socialData = fetchedDependencies['social-sentiment-aggregation']?.per_ticker;
|
|
83
100
|
|
|
84
101
|
if (!divergenceData || !momentumData || !platformData || !socialData) {
|
|
85
102
|
return {}; // Missing one or more key dependencies
|
|
@@ -57,11 +57,24 @@ class SkilledCohortFlow {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Statically defines all metadata for the manifest builder.
|
|
62
|
+
*/
|
|
63
|
+
static getMetadata() {
|
|
64
|
+
return {
|
|
65
|
+
type: 'standard',
|
|
66
|
+
rootDataDependencies: ['portfolio'],
|
|
67
|
+
isHistorical: true, // Compares today vs yesterday portfolio
|
|
68
|
+
userType: 'all',
|
|
69
|
+
category: 'gem'
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
60
73
|
/**
|
|
61
74
|
* Statically declare dependencies.
|
|
62
75
|
*/
|
|
63
76
|
static getDependencies() {
|
|
64
|
-
return ['
|
|
77
|
+
return ['cohort-skill-definition']; // Pass 1
|
|
65
78
|
}
|
|
66
79
|
|
|
67
80
|
_getPortfolioPositions(portfolio) {
|
|
@@ -93,7 +106,8 @@ class SkilledCohortFlow {
|
|
|
93
106
|
return this.skilledCohortUserIds;
|
|
94
107
|
}
|
|
95
108
|
|
|
96
|
-
|
|
109
|
+
// FIX: Use normalized dependency name
|
|
110
|
+
const cohortData = fetchedDependencies['cohort-skill-definition'];
|
|
97
111
|
if (!cohortData || !cohortData.skilled_user_ids) {
|
|
98
112
|
return new Set();
|
|
99
113
|
}
|
|
@@ -51,13 +51,26 @@ class SkilledUnskilledDivergence {
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Statically defines all metadata for the manifest builder.
|
|
56
|
+
*/
|
|
57
|
+
static getMetadata() {
|
|
58
|
+
return {
|
|
59
|
+
type: 'meta',
|
|
60
|
+
rootDataDependencies: [],
|
|
61
|
+
isHistorical: false,
|
|
62
|
+
userType: 'n/a',
|
|
63
|
+
category: 'gem'
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
54
67
|
/**
|
|
55
68
|
* Statically declare dependencies.
|
|
56
69
|
*/
|
|
57
70
|
static getDependencies() {
|
|
58
71
|
return [
|
|
59
|
-
'
|
|
60
|
-
'
|
|
72
|
+
'skilled-cohort-flow', // Pass 2
|
|
73
|
+
'unskilled-cohort-flow' // Pass 2
|
|
61
74
|
];
|
|
62
75
|
}
|
|
63
76
|
|
|
@@ -119,8 +132,9 @@ class SkilledUnskilledDivergence {
|
|
|
119
132
|
* @param {object} fetchedDependencies - Results from Pass 2.
|
|
120
133
|
*/
|
|
121
134
|
getResult(fetchedDependencies) {
|
|
122
|
-
|
|
123
|
-
const
|
|
135
|
+
// FIX: Use normalized dependency names
|
|
136
|
+
const skilledFlowData = fetchedDependencies['skilled-cohort-flow'];
|
|
137
|
+
const unskilledFlowData = fetchedDependencies['unskilled-cohort-flow'];
|
|
124
138
|
|
|
125
139
|
const assetResult = this._calculateDivergence(skilledFlowData?.assets, unskilledFlowData?.assets);
|
|
126
140
|
const sectorResult = this._calculateDivergence(skilledFlowData?.sectors, unskilledFlowData?.sectors);
|
|
@@ -57,11 +57,24 @@ class UnskilledCohortFlow {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Statically defines all metadata for the manifest builder.
|
|
62
|
+
*/
|
|
63
|
+
static getMetadata() {
|
|
64
|
+
return {
|
|
65
|
+
type: 'standard',
|
|
66
|
+
rootDataDependencies: ['portfolio'],
|
|
67
|
+
isHistorical: true, // Compares today vs yesterday portfolio
|
|
68
|
+
userType: 'all',
|
|
69
|
+
category: 'gem'
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
60
73
|
/**
|
|
61
74
|
* Statically declare dependencies.
|
|
62
75
|
*/
|
|
63
76
|
static getDependencies() {
|
|
64
|
-
return ['
|
|
77
|
+
return ['cohort-skill-definition']; // Pass 1
|
|
65
78
|
}
|
|
66
79
|
|
|
67
80
|
_getPortfolioPositions(portfolio) {
|
|
@@ -93,7 +106,8 @@ class UnskilledCohortFlow {
|
|
|
93
106
|
return this.unskilledCohortUserIds;
|
|
94
107
|
}
|
|
95
108
|
|
|
96
|
-
|
|
109
|
+
// FIX: Use normalized dependency name
|
|
110
|
+
const cohortData = fetchedDependencies['cohort-skill-definition'];
|
|
97
111
|
if (!cohortData || !cohortData.unskilled_user_ids) {
|
|
98
112
|
return new Set();
|
|
99
113
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview HELIX Product Line (Pass 4)
|
|
3
|
+
*
|
|
4
|
+
* This metric answers: "What is the final HELIX contrarian signal?"
|
|
5
|
+
*
|
|
6
|
+
* It combines the 'Herd Consensus' with the 'Winner Cohort Flow'
|
|
7
|
+
* to generate actionable signals.
|
|
8
|
+
*
|
|
9
|
+
* HYPOTHESIS:
|
|
10
|
+
* - If Herd is in Euphoria (> +8) AND Winners are Selling = "Strong Sell"
|
|
11
|
+
* - If Herd is in Capitulation (< -8) AND Winners are Buying = "Strong Buy"
|
|
12
|
+
*/
|
|
13
|
+
class HelixContrarianSignal {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Defines the output schema for this calculation.
|
|
17
|
+
* @returns {object} JSON Schema object
|
|
18
|
+
*/
|
|
19
|
+
static getSchema() {
|
|
20
|
+
const tickerSchema = {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"signal": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"enum": ["Capitulation (Strong Buy)", "Euphoria (Strong Sell)", "Confirmation (Buy)", "Confirmation (Sell)", "FOMO (Losers Buying)", "Panic (Losers Selling)", "Neutral"]
|
|
26
|
+
},
|
|
27
|
+
"consensus_score": {
|
|
28
|
+
"type": "number",
|
|
29
|
+
"description": "The Herd Consensus Score (-10 to +10)."
|
|
30
|
+
},
|
|
31
|
+
"net_winner_flow": {
|
|
32
|
+
"type": "number",
|
|
33
|
+
"description": "Net unique winners who joined/left the asset."
|
|
34
|
+
},
|
|
35
|
+
"net_loser_flow": {
|
|
36
|
+
"type": "number",
|
|
37
|
+
"description": "Net unique losers who joined/left the asset."
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"required": ["signal", "consensus_score", "net_winner_flow", "net_loser_flow"]
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"description": "Generates final contrarian signals by comparing herd consensus to winner/loser cohort flows.",
|
|
46
|
+
"patternProperties": {
|
|
47
|
+
"^.*$": tickerSchema // Ticker
|
|
48
|
+
},
|
|
49
|
+
"additionalProperties": tickerSchema
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Statically defines all metadata for the manifest builder.
|
|
55
|
+
*/
|
|
56
|
+
static getMetadata() {
|
|
57
|
+
return {
|
|
58
|
+
type: 'meta',
|
|
59
|
+
rootDataDependencies: [],
|
|
60
|
+
isHistorical: false,
|
|
61
|
+
userType: 'n/a',
|
|
62
|
+
category: 'helix'
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Statically declare dependencies.
|
|
68
|
+
*/
|
|
69
|
+
static getDependencies() {
|
|
70
|
+
return [
|
|
71
|
+
'winner-loser-flow', // from helix (Pass 2)
|
|
72
|
+
'herd-consensus-score' // from helix (Pass 3)
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* This is a 'meta' calculation. It runs once.
|
|
78
|
+
*/
|
|
79
|
+
async process(dateStr, dependencies, config, fetchedDependencies) {
|
|
80
|
+
const { logger } = dependencies;
|
|
81
|
+
|
|
82
|
+
const flowData = fetchedDependencies['winner-loser-flow'];
|
|
83
|
+
const consensusData = fetchedDependencies['herd-consensus-score'];
|
|
84
|
+
|
|
85
|
+
if (!flowData || !consensusData) {
|
|
86
|
+
logger.log('WARN', `[helix/helix-contrarian-signal] Missing dependencies for ${dateStr}. Skipping.`);
|
|
87
|
+
return {};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const allTickers = new Set([
|
|
91
|
+
...Object.keys(flowData),
|
|
92
|
+
...Object.keys(consensusData)
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
const result = {};
|
|
96
|
+
const CONSENSUS_THRESHOLD = 8.0; // Score for "Extreme"
|
|
97
|
+
const FLOW_THRESHOLD = 5; // Min 5 users moving
|
|
98
|
+
|
|
99
|
+
for (const ticker of allTickers) {
|
|
100
|
+
const consensus = consensusData[ticker]?.consensus_score || 0;
|
|
101
|
+
const winner_flow = flowData[ticker]?.net_winner_flow || 0;
|
|
102
|
+
const loser_flow = flowData[ticker]?.net_loser_flow || 0;
|
|
103
|
+
|
|
104
|
+
let signal = "Neutral";
|
|
105
|
+
|
|
106
|
+
// --- Primary Contrarian Signals ---
|
|
107
|
+
|
|
108
|
+
// 1. Capitulation: Herd is at extreme fear, but winners are buying.
|
|
109
|
+
if (consensus < -CONSENSUS_THRESHOLD && winner_flow > FLOW_THRESHOLD) {
|
|
110
|
+
signal = "Capitulation (Strong Buy)";
|
|
111
|
+
}
|
|
112
|
+
// 2. Euphoria: Herd is at extreme greed, but winners are selling.
|
|
113
|
+
else if (consensus > CONSENSUS_THRESHOLD && winner_flow < -FLOW_THRESHOLD) {
|
|
114
|
+
signal = "Euphoria (Strong Sell)";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// --- Secondary Confirmation Signals ---
|
|
118
|
+
|
|
119
|
+
// 3. Confirmation (Buy): Herd is bullish, winners are also buying.
|
|
120
|
+
else if (consensus > 0 && winner_flow > FLOW_THRESHOLD) {
|
|
121
|
+
signal = "Confirmation (Buy)";
|
|
122
|
+
}
|
|
123
|
+
// 4. Confirmation (Sell): Herd is bearish, winners are also selling.
|
|
124
|
+
else if (consensus < 0 && winner_flow < -FLOW_THRESHOLD) {
|
|
125
|
+
signal = "Confirmation (Sell)";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// --- Tertiary "Dumb Money" Signals (Warning signs) ---
|
|
129
|
+
|
|
130
|
+
// 5. FOMO: Herd is euphoric, and losers are piling in.
|
|
131
|
+
else if (consensus > CONSENSUS_THRESHOLD && loser_flow > FLOW_THRESHOLD) {
|
|
132
|
+
signal = "FOMO (Losers Buying)";
|
|
133
|
+
}
|
|
134
|
+
// 6. Panic: Herd is fearful, and losers are capitulating.
|
|
135
|
+
else if (consensus < -CONSENSUS_THRESHOLD && loser_flow < -FLOW_THRESHOLD) {
|
|
136
|
+
signal = "Panic (Losers Selling)";
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Add to result if not neutral
|
|
140
|
+
if (signal !== "Neutral") {
|
|
141
|
+
result[ticker] = {
|
|
142
|
+
signal: signal,
|
|
143
|
+
consensus_score: consensus,
|
|
144
|
+
net_winner_flow: winner_flow,
|
|
145
|
+
net_loser_flow: loser_flow
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = HelixContrarianSignal;
|