aiden-shared-calculations-unified 1.0.103 → 1.0.104
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.
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
class OwnershipVsPerformanceYTD {
|
|
2
|
+
constructor() { this.results = {}; }
|
|
3
|
+
|
|
4
|
+
static getMetadata() {
|
|
5
|
+
return {
|
|
6
|
+
name: 'ownership-vs-performance-ytd',
|
|
7
|
+
type: 'meta',
|
|
8
|
+
category: 'core',
|
|
9
|
+
userType: 'n/a',
|
|
10
|
+
isHistorical: true, // Required to carry forward Jan 1st baseline
|
|
11
|
+
rootDataDependencies: ['insights', 'price']
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static getDependencies() { return []; }
|
|
16
|
+
|
|
17
|
+
async process(context) {
|
|
18
|
+
const { insights: insightsHelper, priceExtractor } = context.math;
|
|
19
|
+
const { previousComputed, mappings, prices, date } = context;
|
|
20
|
+
|
|
21
|
+
const dailyInsights = insightsHelper.getInsights(context, 'today');
|
|
22
|
+
|
|
23
|
+
// 1. Manage Baseline State (Jan 1st snapshot)
|
|
24
|
+
// If today is Jan 1st (or first run), we reset. Otherwise, we load from yesterday.
|
|
25
|
+
let baselineState = previousComputed['ownership-vs-performance-ytd']?.baselines || {};
|
|
26
|
+
const isStartOfYear = date.today.endsWith('-01-01') || date.today.endsWith('-01-02');
|
|
27
|
+
|
|
28
|
+
if (isStartOfYear) {
|
|
29
|
+
baselineState = {}; // Reset for new year
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const currentMetrics = {};
|
|
33
|
+
|
|
34
|
+
for (const insight of dailyInsights) {
|
|
35
|
+
const instId = insight.instrumentId;
|
|
36
|
+
const ticker = mappings.instrumentToTicker[instId];
|
|
37
|
+
if (!ticker) continue;
|
|
38
|
+
|
|
39
|
+
const currentOwners = insightsHelper.getTotalOwners(insight);
|
|
40
|
+
|
|
41
|
+
// Get Price History
|
|
42
|
+
const priceHist = priceExtractor.getHistory(prices, ticker);
|
|
43
|
+
if (!priceHist.length) continue;
|
|
44
|
+
|
|
45
|
+
const currentPrice = priceHist[priceHist.length - 1].price;
|
|
46
|
+
|
|
47
|
+
// 2. Set Baseline if missing
|
|
48
|
+
if (!baselineState[ticker]) {
|
|
49
|
+
baselineState[ticker] = {
|
|
50
|
+
startPrice: currentPrice,
|
|
51
|
+
startOwners: currentOwners,
|
|
52
|
+
date: date.today
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const base = baselineState[ticker];
|
|
57
|
+
|
|
58
|
+
// 3. Calculate Deltas
|
|
59
|
+
const priceYtd = base.startPrice > 0
|
|
60
|
+
? ((currentPrice - base.startPrice) / base.startPrice) * 100
|
|
61
|
+
: 0;
|
|
62
|
+
|
|
63
|
+
const ownersYtd = base.startOwners > 0
|
|
64
|
+
? ((currentOwners - base.startOwners) / base.startOwners) * 100
|
|
65
|
+
: 0;
|
|
66
|
+
|
|
67
|
+
currentMetrics[ticker] = {
|
|
68
|
+
priceYtd,
|
|
69
|
+
ownersYtd,
|
|
70
|
+
currentPrice,
|
|
71
|
+
currentOwners
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 4. Output structure includes the baselines so they are saved for tomorrow
|
|
76
|
+
this.results = {
|
|
77
|
+
metrics: currentMetrics,
|
|
78
|
+
baselines: baselineState
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async getResult() {
|
|
83
|
+
// We flatten the 'metrics' for easy API consumption,
|
|
84
|
+
// but we must ensure 'baselines' is preserved in the stored document
|
|
85
|
+
// so 'previousComputed' picks it up tomorrow.
|
|
86
|
+
return { ...this.results.metrics, baselines: this.results.baselines };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = OwnershipVsPerformanceYTD;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
class OwnershipVsVolatility {
|
|
2
|
+
constructor() { this.results = {}; }
|
|
3
|
+
|
|
4
|
+
static getMetadata() {
|
|
5
|
+
return {
|
|
6
|
+
name: 'ownership-vs-volatility',
|
|
7
|
+
type: 'meta',
|
|
8
|
+
category: 'core',
|
|
9
|
+
userType: 'n/a',
|
|
10
|
+
isHistorical: true, // To calculate ownership change over window
|
|
11
|
+
rootDataDependencies: ['insights', 'price']
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static getDependencies() { return []; }
|
|
16
|
+
|
|
17
|
+
async process(context) {
|
|
18
|
+
const { insights: insightsHelper, priceExtractor, compute } = context.math;
|
|
19
|
+
const { previousComputed, mappings, prices } = context;
|
|
20
|
+
|
|
21
|
+
const dailyInsights = insightsHelper.getInsights(context, 'today');
|
|
22
|
+
|
|
23
|
+
// Use 7-day lookback for ownership change
|
|
24
|
+
const prevState = previousComputed['ownership-vs-volatility'] || {};
|
|
25
|
+
|
|
26
|
+
for (const insight of dailyInsights) {
|
|
27
|
+
const instId = insight.instrumentId;
|
|
28
|
+
const ticker = mappings.instrumentToTicker[instId];
|
|
29
|
+
if (!ticker) continue;
|
|
30
|
+
|
|
31
|
+
// 1. Calculate Recent Volatility (14 Day StdDev of Returns)
|
|
32
|
+
const priceHist = priceExtractor.getHistory(prices, ticker);
|
|
33
|
+
if (priceHist.length < 14) continue;
|
|
34
|
+
|
|
35
|
+
const recentPrices = priceHist.slice(-14);
|
|
36
|
+
const returns = [];
|
|
37
|
+
for (let i = 1; i < recentPrices.length; i++) {
|
|
38
|
+
returns.push((recentPrices[i].price - recentPrices[i-1].price) / recentPrices[i-1].price);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const volatility = compute.standardDeviation(returns); // Raw std dev
|
|
42
|
+
|
|
43
|
+
// 2. Calculate Ownership Change (Current vs 7 days ago stored state)
|
|
44
|
+
const currentOwners = insightsHelper.getTotalOwners(insight);
|
|
45
|
+
const prevOwners = prevState[ticker]?.laggedOwners || currentOwners;
|
|
46
|
+
|
|
47
|
+
const ownerChangePct = prevOwners > 0
|
|
48
|
+
? ((currentOwners - prevOwners) / prevOwners) * 100
|
|
49
|
+
: 0;
|
|
50
|
+
|
|
51
|
+
this.results[ticker] = {
|
|
52
|
+
volatilityScore: volatility,
|
|
53
|
+
ownerChange7d: ownerChangePct,
|
|
54
|
+
// Store current owners as 'laggedOwners' for next week?
|
|
55
|
+
// Actually, for daily rolling 7d change, we need a queue.
|
|
56
|
+
// Simplified: We store today's value, and the API/UI compares.
|
|
57
|
+
// Better: Use a simple rolling queue like the Momentum calc.
|
|
58
|
+
_history: [...(prevState[ticker]?._history || []), currentOwners].slice(-7),
|
|
59
|
+
|
|
60
|
+
// For the output: Calculate change vs the oldest in history (approx 7d ago)
|
|
61
|
+
laggedOwners: (prevState[ticker]?._history?.[0] || currentOwners)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async getResult() { return this.results; }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = OwnershipVsVolatility;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
class ShortInterestGrowth {
|
|
2
|
+
constructor() { this.results = {}; }
|
|
3
|
+
|
|
4
|
+
static getMetadata() {
|
|
5
|
+
return {
|
|
6
|
+
name: 'short-interest-growth',
|
|
7
|
+
type: 'meta',
|
|
8
|
+
category: 'core',
|
|
9
|
+
userType: 'n/a',
|
|
10
|
+
isHistorical: true,
|
|
11
|
+
rootDataDependencies: ['insights']
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static getDependencies() { return []; }
|
|
16
|
+
|
|
17
|
+
static getSchema() {
|
|
18
|
+
return {
|
|
19
|
+
"TICKER": {
|
|
20
|
+
"shortCount": 0,
|
|
21
|
+
"shortSparkline": [0, 0, 0, 0, 0, 0, 0],
|
|
22
|
+
"growth7d": 0.0
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async process(context) {
|
|
28
|
+
const { insights: insightsHelper } = context.math;
|
|
29
|
+
const { previousComputed, mappings } = context;
|
|
30
|
+
|
|
31
|
+
const dailyInsights = insightsHelper.getInsights(context, 'today');
|
|
32
|
+
const previousState = previousComputed['short-interest-growth'] || {};
|
|
33
|
+
|
|
34
|
+
for (const insight of dailyInsights) {
|
|
35
|
+
const instId = insight.instrumentId;
|
|
36
|
+
const ticker = mappings.instrumentToTicker[instId];
|
|
37
|
+
if (!ticker) continue;
|
|
38
|
+
|
|
39
|
+
// Calculate Short Count: Total Owners * (Sell % / 100)
|
|
40
|
+
const shortCount = insightsHelper.getShortCount(insight);
|
|
41
|
+
|
|
42
|
+
const prevState = previousState[ticker] || { shortSparkline: [] };
|
|
43
|
+
const sparkline = [...(prevState.shortSparkline || [])];
|
|
44
|
+
|
|
45
|
+
sparkline.push(shortCount);
|
|
46
|
+
if (sparkline.length > 7) sparkline.shift();
|
|
47
|
+
|
|
48
|
+
// Calculate simple 7-day growth %
|
|
49
|
+
let growth7d = 0;
|
|
50
|
+
if (sparkline.length > 1) {
|
|
51
|
+
const start = sparkline[0];
|
|
52
|
+
const end = sparkline[sparkline.length - 1];
|
|
53
|
+
if (start > 0) {
|
|
54
|
+
growth7d = ((end - start) / start) * 100;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this.results[ticker] = {
|
|
59
|
+
shortCount,
|
|
60
|
+
shortSparkline: sparkline,
|
|
61
|
+
growth7d
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async getResult() { return this.results; }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = ShortInterestGrowth;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
class TrendingOwnershipMomentum {
|
|
2
|
+
constructor() { this.results = {}; }
|
|
3
|
+
|
|
4
|
+
static getMetadata() {
|
|
5
|
+
return {
|
|
6
|
+
name: 'trending-ownership-momentum',
|
|
7
|
+
type: 'meta',
|
|
8
|
+
category: 'core',
|
|
9
|
+
userType: 'n/a',
|
|
10
|
+
isHistorical: true, // Needed for rolling history
|
|
11
|
+
rootDataDependencies: ['insights']
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static getDependencies() { return []; }
|
|
16
|
+
|
|
17
|
+
static getSchema() {
|
|
18
|
+
return {
|
|
19
|
+
"TICKER": {
|
|
20
|
+
"currentOwners": 0,
|
|
21
|
+
"sparkline": [0, 0, 0, 0, 0, 0, 0], // 7-Day History
|
|
22
|
+
"momentumScore": 0.0, // Slope of the trend
|
|
23
|
+
"trend": "string" // 'RISING', 'FALLING', 'STABLE'
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async process(context) {
|
|
29
|
+
const { insights: insightsHelper } = context.math;
|
|
30
|
+
const { previousComputed, mappings } = context;
|
|
31
|
+
|
|
32
|
+
// 1. Get Today's Data
|
|
33
|
+
const dailyInsights = insightsHelper.getInsights(context, 'today');
|
|
34
|
+
if (!dailyInsights.length) return;
|
|
35
|
+
|
|
36
|
+
// 2. Get Yesterday's State (for rolling history)
|
|
37
|
+
const previousState = previousComputed['trending-ownership-momentum'] || {};
|
|
38
|
+
|
|
39
|
+
for (const insight of dailyInsights) {
|
|
40
|
+
const instId = insight.instrumentId;
|
|
41
|
+
const ticker = mappings.instrumentToTicker[instId];
|
|
42
|
+
if (!ticker) continue;
|
|
43
|
+
|
|
44
|
+
const currentCount = insightsHelper.getTotalOwners(insight);
|
|
45
|
+
const prevState = previousState[ticker] || { sparkline: [] };
|
|
46
|
+
|
|
47
|
+
// 3. Update Rolling Window (Last 7 Days)
|
|
48
|
+
// Create new array copy to avoid mutation issues
|
|
49
|
+
const sparkline = [...(prevState.sparkline || [])];
|
|
50
|
+
sparkline.push(currentCount);
|
|
51
|
+
|
|
52
|
+
// Keep only last 7 entries
|
|
53
|
+
if (sparkline.length > 7) sparkline.shift();
|
|
54
|
+
|
|
55
|
+
// 4. Calculate Momentum Score (Linear Regression Slope)
|
|
56
|
+
let momentumScore = 0;
|
|
57
|
+
if (sparkline.length >= 2) {
|
|
58
|
+
const n = sparkline.length;
|
|
59
|
+
let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
|
|
60
|
+
for (let i = 0; i < n; i++) {
|
|
61
|
+
sumX += i;
|
|
62
|
+
sumY += sparkline[i];
|
|
63
|
+
sumXY += i * sparkline[i];
|
|
64
|
+
sumXX += i * i;
|
|
65
|
+
}
|
|
66
|
+
const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
|
|
67
|
+
// Normalize slope by current count to get percentage-like momentum
|
|
68
|
+
momentumScore = currentCount > 0 ? (slope / currentCount) * 100 : 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let trend = 'STABLE';
|
|
72
|
+
if (momentumScore > 0.5) trend = 'RISING';
|
|
73
|
+
if (momentumScore < -0.5) trend = 'FALLING';
|
|
74
|
+
|
|
75
|
+
this.results[ticker] = {
|
|
76
|
+
currentOwners: currentCount,
|
|
77
|
+
sparkline,
|
|
78
|
+
momentumScore,
|
|
79
|
+
trend
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async getResult() { return this.results; }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = TrendingOwnershipMomentum;
|