bulltrackers-module 1.0.693 → 1.0.694
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.
|
@@ -420,7 +420,8 @@ async function writeSingleResult(result, docRef, name, dateContext, category, lo
|
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
const rootUpdate = updates.find(u => u.ref.path === docRef.path && u.type !== 'DELETE');
|
|
423
|
-
|
|
423
|
+
// FIX: Always use merge: false to ensure old fields (like _compressed/payload) are wiped
|
|
424
|
+
if (rootUpdate) { rootUpdate.options = { merge: false }; }
|
|
424
425
|
|
|
425
426
|
const writes = updates.filter(u => u.type !== 'DELETE').length;
|
|
426
427
|
const deletes = updates.filter(u => u.type === 'DELETE').length;
|
package/package.json
CHANGED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* file: alert-system/helpers/alert_type_registry.js
|
|
3
|
-
*/
|
|
4
|
-
const ALERT_TYPES = {
|
|
5
|
-
increasedRisk: {
|
|
6
|
-
id: 'increasedRisk',
|
|
7
|
-
name: 'Increased Risk',
|
|
8
|
-
description: 'Alert when a Popular Investor\'s risk score increases',
|
|
9
|
-
computationName: 'RiskScoreIncrease',
|
|
10
|
-
category: 'alerts',
|
|
11
|
-
messageTemplate: '{piUsername}\'s risk score increased by {change} points (from {previous} to {current})',
|
|
12
|
-
severity: 'high',
|
|
13
|
-
enabled: true
|
|
14
|
-
},
|
|
15
|
-
volatilityChanges: {
|
|
16
|
-
id: 'volatilityChanges',
|
|
17
|
-
name: 'Significant Volatility',
|
|
18
|
-
description: 'Alert when a Popular Investor\'s portfolio volatility exceeds 50%',
|
|
19
|
-
computationName: 'SignificantVolatility',
|
|
20
|
-
category: 'alerts',
|
|
21
|
-
messageTemplate: '{piUsername}\'s portfolio volatility is {volatility}% (threshold: {threshold}%)',
|
|
22
|
-
severity: 'medium',
|
|
23
|
-
enabled: true
|
|
24
|
-
},
|
|
25
|
-
newSector: {
|
|
26
|
-
id: 'newSector',
|
|
27
|
-
name: 'New Sector Entry',
|
|
28
|
-
description: 'Alert when a Popular Investor enters a new sector',
|
|
29
|
-
computationName: 'NewSectorExposure',
|
|
30
|
-
category: 'alerts',
|
|
31
|
-
messageTemplate: '{piUsername} entered new sector(s): {sectorName}',
|
|
32
|
-
severity: 'low',
|
|
33
|
-
enabled: true
|
|
34
|
-
},
|
|
35
|
-
increasedPositionSize: {
|
|
36
|
-
id: 'increasedPositionSize',
|
|
37
|
-
name: 'Increased Position Size',
|
|
38
|
-
description: 'Alert when a Popular Investor significantly increases a position size (>5%)',
|
|
39
|
-
computationName: 'PositionInvestedIncrease',
|
|
40
|
-
category: 'alerts',
|
|
41
|
-
messageTemplate: '{piUsername} increased position size for {symbol} by {diff}% (from {prev}% to {curr}%)',
|
|
42
|
-
severity: 'medium',
|
|
43
|
-
enabled: true
|
|
44
|
-
},
|
|
45
|
-
newSocialPost: {
|
|
46
|
-
id: 'newSocialPost',
|
|
47
|
-
name: 'New Social Post',
|
|
48
|
-
description: 'Alert when a Popular Investor makes a new social post',
|
|
49
|
-
computationName: 'NewSocialPost',
|
|
50
|
-
category: 'alerts',
|
|
51
|
-
messageTemplate: '{piUsername} posted a new update: {title}',
|
|
52
|
-
severity: 'low',
|
|
53
|
-
enabled: true
|
|
54
|
-
},
|
|
55
|
-
// [NEW] Behavioral Anomaly Registration
|
|
56
|
-
behavioralAnomaly: {
|
|
57
|
-
id: 'behavioralAnomaly',
|
|
58
|
-
name: 'Behavioral Anomaly',
|
|
59
|
-
description: 'Alert when a Popular Investor deviates significantly from their baseline behavior',
|
|
60
|
-
computationName: 'BehavioralAnomaly',
|
|
61
|
-
category: 'alerts',
|
|
62
|
-
// Uses metadata keys from Behaviour.js: primaryDriver, driverSignificance, anomalyScore
|
|
63
|
-
messageTemplate: 'Behavioral Alert for {piUsername}: {primaryDriver} Deviation ({driverSignificance}) detected.',
|
|
64
|
-
severity: 'high',
|
|
65
|
-
enabled: true
|
|
66
|
-
},
|
|
67
|
-
testSystemProbe: {
|
|
68
|
-
id: 'testSystemProbe',
|
|
69
|
-
name: 'Test System Probe',
|
|
70
|
-
description: 'Always-on debug alert',
|
|
71
|
-
computationName: 'TestSystemProbe',
|
|
72
|
-
category: 'alerts',
|
|
73
|
-
messageTemplate: 'Probe Triggered for {status} at {timestamp}',
|
|
74
|
-
severity: 'info',
|
|
75
|
-
enabled: true
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Get alert type by ID
|
|
81
|
-
*/
|
|
82
|
-
function getAlertType(alertTypeId) {
|
|
83
|
-
return ALERT_TYPES[alertTypeId] || null;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Get alert type by computation name
|
|
88
|
-
*/
|
|
89
|
-
function getAlertTypeByComputation(computationName) {
|
|
90
|
-
for (const [id, alertType] of Object.entries(ALERT_TYPES)) {
|
|
91
|
-
if (alertType.computationName === computationName) {
|
|
92
|
-
return alertType;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Get all enabled alert types
|
|
100
|
-
*/
|
|
101
|
-
function getAllAlertTypes() {
|
|
102
|
-
return Object.values(ALERT_TYPES).filter(type => type.enabled);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Check if a computation is an alert computation
|
|
107
|
-
*/
|
|
108
|
-
function isAlertComputation(computationName) {
|
|
109
|
-
return getAlertTypeByComputation(computationName) !== null;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Generate alert message from template and metadata
|
|
114
|
-
*/
|
|
115
|
-
function generateAlertMessage(alertType, piUsername, metadata = {}) {
|
|
116
|
-
let message = alertType.messageTemplate;
|
|
117
|
-
|
|
118
|
-
// Replace placeholders
|
|
119
|
-
message = message.replace('{piUsername}', piUsername || 'Unknown');
|
|
120
|
-
message = message.replace('{count}', metadata.count || metadata.positions?.length || metadata.moveCount || 0);
|
|
121
|
-
message = message.replace('{change}', metadata.change || metadata.changePercent || metadata.diff || 'N/A');
|
|
122
|
-
message = message.replace('{previous}', metadata.previous || metadata.previousValue || metadata.prev || metadata.previousRisk || 'N/A');
|
|
123
|
-
message = message.replace('{current}', metadata.current || metadata.currentValue || metadata.curr || metadata.currentRisk || 'N/A');
|
|
124
|
-
message = message.replace('{sectorName}', metadata.sectorName || (metadata.newExposures && metadata.newExposures.length > 0 ? metadata.newExposures.join(', ') : 'Unknown'));
|
|
125
|
-
message = message.replace('{ticker}', metadata.ticker || metadata.symbol || 'Unknown');
|
|
126
|
-
|
|
127
|
-
// Format numeric values
|
|
128
|
-
message = message.replace('{volatility}', metadata.volatility ? `${(metadata.volatility * 100).toFixed(1)}` : 'N/A');
|
|
129
|
-
message = message.replace('{threshold}', metadata.threshold ? `${(metadata.threshold * 100).toFixed(0)}` : 'N/A');
|
|
130
|
-
message = message.replace('{diff}', metadata.diff ? `${metadata.diff.toFixed(1)}` : 'N/A');
|
|
131
|
-
message = message.replace('{prev}', metadata.prev ? `${metadata.prev.toFixed(1)}` : 'N/A');
|
|
132
|
-
message = message.replace('{curr}', metadata.curr ? `${metadata.curr.toFixed(1)}` : 'N/A');
|
|
133
|
-
message = message.replace('{title}', metadata.title || 'New Update');
|
|
134
|
-
|
|
135
|
-
// [FIX] Probe Placeholders (Missing in original)
|
|
136
|
-
message = message.replace('{status}', metadata.status || 'Unknown Status');
|
|
137
|
-
message = message.replace('{timestamp}', metadata.timestamp || new Date().toISOString());
|
|
138
|
-
|
|
139
|
-
// [NEW] Behavioral Anomaly Placeholders
|
|
140
|
-
message = message.replace('{primaryDriver}', metadata.primaryDriver || 'Unknown Factor');
|
|
141
|
-
message = message.replace('{driverSignificance}', metadata.driverSignificance || 'N/A');
|
|
142
|
-
message = message.replace('{anomalyScore}', metadata.anomalyScore || 'N/A');
|
|
143
|
-
|
|
144
|
-
// Handle positions list if available
|
|
145
|
-
if (metadata.positions && Array.isArray(metadata.positions) && metadata.positions.length > 0) {
|
|
146
|
-
const positionsList = metadata.positions
|
|
147
|
-
.slice(0, 3)
|
|
148
|
-
.map(p => p.ticker || p.instrumentId)
|
|
149
|
-
.join(', ');
|
|
150
|
-
message = message.replace('{positions}', positionsList);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Handle moves array for PositionInvestedIncrease
|
|
154
|
-
if (metadata.moves && Array.isArray(metadata.moves) && metadata.moves.length > 0) {
|
|
155
|
-
const firstMove = metadata.moves[0];
|
|
156
|
-
message = message.replace('{symbol}', firstMove.symbol || 'Unknown');
|
|
157
|
-
message = message.replace('{diff}', firstMove.diff ? `${firstMove.diff.toFixed(1)}` : 'N/A');
|
|
158
|
-
message = message.replace('{prev}', firstMove.prev ? `${firstMove.prev.toFixed(1)}` : 'N/A');
|
|
159
|
-
message = message.replace('{curr}', firstMove.curr ? `${firstMove.curr.toFixed(1)}` : 'N/A');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return message;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
module.exports = {
|
|
166
|
-
ALERT_TYPES,
|
|
167
|
-
getAlertType,
|
|
168
|
-
getAlertTypeByComputation,
|
|
169
|
-
getAllAlertTypes,
|
|
170
|
-
isAlertComputation,
|
|
171
|
-
generateAlertMessage
|
|
172
|
-
};
|