aiden-shared-calculations-unified 1.0.104 → 1.0.105
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/audit_results.txt +97 -0
- package/calculations/audit_schemas.js +47 -0
- package/calculations/core/Insights-total-long-per-stock.js +1 -1
- package/calculations/core/crowd-cost-basis.js +20 -6
- package/calculations/core/leverage-divergence.js +22 -7
- package/calculations/core/liquidation-cascade.js +19 -5
- package/calculations/core/ownership-vs-performance-ytd.js +48 -13
- package/calculations/core/ownership-vs-volatility.js +25 -8
- package/package.json +1 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
Scanning 92 files...
|
|
2
|
+
|
|
3
|
+
--- Files MISSING getSchema() ---
|
|
4
|
+
|
|
5
|
+
--- Files WITH getSchema() ---
|
|
6
|
+
capitulation\asset-volatility-estimator.js
|
|
7
|
+
capitulation\retail-capitulation-risk-forecast.js
|
|
8
|
+
core\asset-cost-basis-profile.js
|
|
9
|
+
core\asset-pnl-status.js
|
|
10
|
+
core\asset-position-size.js
|
|
11
|
+
core\average-daily-pnl-all-users.js
|
|
12
|
+
core\average-daily-pnl-per-sector.js
|
|
13
|
+
core\average-daily-pnl-per-stock.js
|
|
14
|
+
core\average-daily-position-pnl.js
|
|
15
|
+
core\crowd-cost-basis.js
|
|
16
|
+
core\holding-duration-per-asset.js
|
|
17
|
+
core\insights-daily-bought-vs-sold-count.js
|
|
18
|
+
core\insights-daily-ownership-delta.js
|
|
19
|
+
core\insights-sentimet-per-stock.js
|
|
20
|
+
core\insights-total-long-per-sector.js
|
|
21
|
+
core\Insights-total-long-per-stock.js
|
|
22
|
+
core\insights-total-positions-held.js
|
|
23
|
+
core\instrument-price-change-1d.js
|
|
24
|
+
core\instrument-price-momentum-20d.js
|
|
25
|
+
core\leverage-divergence.js
|
|
26
|
+
core\liquidation-cascade.js
|
|
27
|
+
core\long-position-per-stock.js
|
|
28
|
+
core\overall-holding-duration.js
|
|
29
|
+
core\overall-profitability-ratio.js
|
|
30
|
+
core\ownership-vs-performance-ytd.js
|
|
31
|
+
core\ownership-vs-volatility.js
|
|
32
|
+
core\platform-buy-sell-sentiment.js
|
|
33
|
+
core\platform-daily-bought-vs-sold-count.js
|
|
34
|
+
core\platform-daily-ownership-delta.js
|
|
35
|
+
core\platform-ownership-per-sector.js
|
|
36
|
+
core\platform-total-positions-held.js
|
|
37
|
+
core\pnl-distribution-per-stock.js
|
|
38
|
+
core\price-metrics.js
|
|
39
|
+
core\profitability-ratio-per-sector.js
|
|
40
|
+
core\profitability-ratio-per-stock.js
|
|
41
|
+
core\profitability-skew-per-stock.js
|
|
42
|
+
core\profitable-and-unprofitable-status.js
|
|
43
|
+
core\sentiment-per-stock.js
|
|
44
|
+
core\short-interest-growth.js
|
|
45
|
+
core\short-position-per-stock.js
|
|
46
|
+
core\social-activity-aggregation.js
|
|
47
|
+
core\social-asset-posts-trend.js
|
|
48
|
+
core\social-event-correlation.js
|
|
49
|
+
core\social-sentiment-aggregation.js
|
|
50
|
+
core\social-top-mentioned-words.js
|
|
51
|
+
core\social-topic-interest-evolution.js
|
|
52
|
+
core\social-topic-sentiment-matrix.js
|
|
53
|
+
core\social-word-mentions-trend.js
|
|
54
|
+
core\speculator-asset-sentiment.js
|
|
55
|
+
core\speculator-danger-zone.js
|
|
56
|
+
core\speculator-distance-to-stop-loss-per-leverage.js
|
|
57
|
+
core\speculator-distance-to-tp-per-leverage.js
|
|
58
|
+
core\speculator-entry-distance-to-sl-per-leverage.js
|
|
59
|
+
core\speculator-entry-distance-to-tp-per-leverage.js
|
|
60
|
+
core\speculator-leverage-per-asset.js
|
|
61
|
+
core\speculator-leverage-per-sector.js
|
|
62
|
+
core\speculator-risk-reward-ratio-per-asset.js
|
|
63
|
+
core\speculator-stop-loss-distance-by-sector-short-long-breakdown.js
|
|
64
|
+
core\speculator-stop-loss-distance-by-ticker-short-long-breakdown.js
|
|
65
|
+
core\speculator-stop-loss-per-asset.js
|
|
66
|
+
core\speculator-take-profit-per-asset.js
|
|
67
|
+
core\speculator-tsl-per-asset.js
|
|
68
|
+
core\total-long-figures.js
|
|
69
|
+
core\total-long-per-sector.js
|
|
70
|
+
core\total-short-figures.js
|
|
71
|
+
core\total-short-per-sector.js
|
|
72
|
+
core\trending-ownership-momentum.js
|
|
73
|
+
core\user-history-reconstructor.js
|
|
74
|
+
core\users-processed.js
|
|
75
|
+
gauss\cohort-capital-flow.js
|
|
76
|
+
gauss\cohort-definer.js
|
|
77
|
+
gauss\daily-dna-filter.js
|
|
78
|
+
gauss\gauss-divergence-signal.js
|
|
79
|
+
gem\cohort-momentum-state.js
|
|
80
|
+
gem\cohort-skill-definition.js
|
|
81
|
+
gem\platform-conviction-divergence.js
|
|
82
|
+
gem\quant-skill-alpha-signal.js
|
|
83
|
+
gem\skilled-cohort-flow.js
|
|
84
|
+
gem\skilled-unskilled-divergence.js
|
|
85
|
+
gem\unskilled-cohort-flow.js
|
|
86
|
+
ghost-book\cost-basis-density.js
|
|
87
|
+
ghost-book\liquidity-vacuum.js
|
|
88
|
+
ghost-book\retail-gamma-exposure.js
|
|
89
|
+
helix\helix-contrarian-signal.js
|
|
90
|
+
helix\herd-consensus-score.js
|
|
91
|
+
helix\winner-loser-flow.js
|
|
92
|
+
predicative-alpha\cognitive-dissonance.js
|
|
93
|
+
predicative-alpha\diamond-hand-fracture.js
|
|
94
|
+
predicative-alpha\mimetic-latency.js
|
|
95
|
+
pyro\risk-appetite-index.js
|
|
96
|
+
pyro\squeeze-potential.js
|
|
97
|
+
pyro\volatility-signal.js
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const rootDir = __dirname;
|
|
5
|
+
|
|
6
|
+
function getAllFiles(dirPath, arrayOfFiles) {
|
|
7
|
+
const files = fs.readdirSync(dirPath);
|
|
8
|
+
|
|
9
|
+
arrayOfFiles = arrayOfFiles || [];
|
|
10
|
+
|
|
11
|
+
files.forEach(function (file) {
|
|
12
|
+
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
|
|
13
|
+
arrayOfFiles = getAllFiles(dirPath + "/" + file, arrayOfFiles);
|
|
14
|
+
} else {
|
|
15
|
+
if (file.endsWith('.js') && file !== 'audit_schemas.js') {
|
|
16
|
+
arrayOfFiles.push(path.join(dirPath, "/", file));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return arrayOfFiles;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const files = getAllFiles(rootDir);
|
|
25
|
+
let missingSchema = [];
|
|
26
|
+
let hasSchema = [];
|
|
27
|
+
|
|
28
|
+
const output = [];
|
|
29
|
+
output.push(`Scanning ${files.length} files...`);
|
|
30
|
+
|
|
31
|
+
files.forEach(file => {
|
|
32
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
33
|
+
if (content.includes('static getSchema()')) {
|
|
34
|
+
hasSchema.push(file);
|
|
35
|
+
} else {
|
|
36
|
+
missingSchema.push(file);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
output.push('\n--- Files MISSING getSchema() ---');
|
|
41
|
+
missingSchema.forEach(f => output.push(path.relative(rootDir, f)));
|
|
42
|
+
|
|
43
|
+
output.push('\n--- Files WITH getSchema() ---');
|
|
44
|
+
hasSchema.forEach(f => output.push(path.relative(rootDir, f)));
|
|
45
|
+
|
|
46
|
+
fs.writeFileSync(path.join(rootDir, 'audit_results.txt'), output.join('\n'));
|
|
47
|
+
console.log('Audit complete. Results written to audit_results.txt');
|
|
@@ -28,7 +28,7 @@ class InsightsTotalPositionsHeld {
|
|
|
28
28
|
if (insights.length === 0) return;
|
|
29
29
|
|
|
30
30
|
for (const insight of insights) {
|
|
31
|
-
this.totalPositions += insightsHelper.
|
|
31
|
+
this.totalPositions += insightsHelper.getLongCount(insight); // Corrected to use getLongCount
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
class CrowdCostBasis {
|
|
2
2
|
constructor() {
|
|
3
|
-
this.results = {};
|
|
3
|
+
this.results = {};
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
static getMetadata() {
|
|
@@ -18,9 +18,23 @@ class CrowdCostBasis {
|
|
|
18
18
|
return ['user-history-reconstructor'];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
static getSchema() {
|
|
22
|
+
const schema = {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"properties": {
|
|
25
|
+
"avgEntry": { "type": "number" },
|
|
26
|
+
"holderCount": { "type": "number" },
|
|
27
|
+
"profitabilityPct": { "type": "number" },
|
|
28
|
+
"state": { "type": "string" }
|
|
29
|
+
},
|
|
30
|
+
"required": ["avgEntry", "holderCount", "profitabilityPct", "state"]
|
|
31
|
+
};
|
|
32
|
+
return { "type": "object", "patternProperties": { "^.*$": schema } };
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
async process(context) {
|
|
22
36
|
const { computed, prices, math } = context;
|
|
23
|
-
|
|
37
|
+
|
|
24
38
|
// 1. Access the output of the Standard calculation
|
|
25
39
|
const userReconstructions = computed['user-history-reconstructor'];
|
|
26
40
|
if (!userReconstructions) return;
|
|
@@ -30,12 +44,12 @@ class CrowdCostBasis {
|
|
|
30
44
|
// 2. Iterate over all users' reconstructed states
|
|
31
45
|
for (const userId in userReconstructions) {
|
|
32
46
|
const userPortfolio = userReconstructions[userId];
|
|
33
|
-
|
|
47
|
+
|
|
34
48
|
for (const ticker in userPortfolio) {
|
|
35
49
|
const position = userPortfolio[ticker];
|
|
36
|
-
|
|
50
|
+
|
|
37
51
|
if (!aggregator[ticker]) aggregator[ticker] = { sumEntry: 0, count: 0 };
|
|
38
|
-
|
|
52
|
+
|
|
39
53
|
// Aggregating global average entry
|
|
40
54
|
aggregator[ticker].sumEntry += position.avgEntry;
|
|
41
55
|
aggregator[ticker].count++;
|
|
@@ -48,7 +62,7 @@ class CrowdCostBasis {
|
|
|
48
62
|
if (data.count < 10) continue; // Noise filter
|
|
49
63
|
|
|
50
64
|
const globalAvgEntry = data.sumEntry / data.count;
|
|
51
|
-
|
|
65
|
+
|
|
52
66
|
// Get today's closing price
|
|
53
67
|
const priceHistory = math.priceExtractor.getHistory(prices, ticker);
|
|
54
68
|
// The last item in price history for this context is "Today"
|
|
@@ -16,12 +16,27 @@ class LeverageDivergence {
|
|
|
16
16
|
return ['user-history-reconstructor'];
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
static getSchema() {
|
|
20
|
+
const schema = {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"levHolders": { "type": "number" },
|
|
24
|
+
"spotHolders": { "type": "number" },
|
|
25
|
+
"levDelta": { "type": "number" },
|
|
26
|
+
"spotDelta": { "type": "number" },
|
|
27
|
+
"signal": { "type": "string" }
|
|
28
|
+
},
|
|
29
|
+
"required": ["levHolders", "spotHolders", "levDelta", "spotDelta", "signal"]
|
|
30
|
+
};
|
|
31
|
+
return { "type": "object", "patternProperties": { "^.*$": schema } };
|
|
32
|
+
}
|
|
33
|
+
|
|
19
34
|
async process(context) {
|
|
20
35
|
const { computed, previousComputed } = context;
|
|
21
|
-
|
|
36
|
+
|
|
22
37
|
const currentReconstruction = computed['user-history-reconstructor'];
|
|
23
38
|
// Access SELF from yesterday
|
|
24
|
-
const previousResult = previousComputed['leverage-divergence'];
|
|
39
|
+
const previousResult = previousComputed['leverage-divergence'];
|
|
25
40
|
|
|
26
41
|
const currentAgg = {}; // { AAPL: { levHolders: 0, spotHolders: 0 } }
|
|
27
42
|
|
|
@@ -30,9 +45,9 @@ class LeverageDivergence {
|
|
|
30
45
|
const userPortfolio = currentReconstruction[userId];
|
|
31
46
|
for (const ticker in userPortfolio) {
|
|
32
47
|
const pos = userPortfolio[ticker];
|
|
33
|
-
|
|
48
|
+
|
|
34
49
|
if (!currentAgg[ticker]) currentAgg[ticker] = { levHolders: 0, spotHolders: 0 };
|
|
35
|
-
|
|
50
|
+
|
|
36
51
|
// If avg leverage > 1.1, count as "Leveraged Holder"
|
|
37
52
|
if (pos.avgLeverage > 1.1) {
|
|
38
53
|
currentAgg[ticker].levHolders++;
|
|
@@ -56,10 +71,10 @@ class LeverageDivergence {
|
|
|
56
71
|
const spotDelta = curr.spotHolders - prev.spotHolders;
|
|
57
72
|
|
|
58
73
|
let signal = 'NEUTRAL';
|
|
59
|
-
|
|
74
|
+
|
|
60
75
|
// Retail (Spot) Buying + Speculators (Lev) Selling = Smart Money Exit
|
|
61
|
-
if (spotDelta > 0 && levDelta < 0) signal = 'SMART_EXIT';
|
|
62
|
-
|
|
76
|
+
if (spotDelta > 0 && levDelta < 0) signal = 'SMART_EXIT';
|
|
77
|
+
|
|
63
78
|
// Retail (Spot) Selling + Speculators (Lev) Buying = High Conviction Pump
|
|
64
79
|
if (spotDelta < 0 && levDelta > 0) signal = 'SPECULATIVE_PUMP';
|
|
65
80
|
|
|
@@ -8,7 +8,7 @@ class LiquidationCascade {
|
|
|
8
8
|
category: 'History Reconstruction',
|
|
9
9
|
userType: 'n/a',
|
|
10
10
|
isHistorical: false,
|
|
11
|
-
rootDataDependencies: []
|
|
11
|
+
rootDataDependencies: []
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -16,23 +16,37 @@ class LiquidationCascade {
|
|
|
16
16
|
return ['user-history-reconstructor'];
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
static getSchema() {
|
|
20
|
+
const schema = {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"totalClosures": { "type": "number" },
|
|
24
|
+
"forcedClosures": { "type": "number" },
|
|
25
|
+
"painIndex": { "type": "number" },
|
|
26
|
+
"isFlushEvent": { "type": "boolean" }
|
|
27
|
+
},
|
|
28
|
+
"required": ["totalClosures", "forcedClosures", "painIndex", "isFlushEvent"]
|
|
29
|
+
};
|
|
30
|
+
return { "type": "object", "patternProperties": { "^.*$": schema } };
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
async process(context) {
|
|
20
34
|
const { computed } = context;
|
|
21
35
|
const userReconstructions = computed['user-history-reconstructor'];
|
|
22
36
|
if (!userReconstructions) return;
|
|
23
37
|
|
|
24
|
-
const aggregator = {};
|
|
38
|
+
const aggregator = {};
|
|
25
39
|
|
|
26
40
|
// 1. Aggregate Forced Exits
|
|
27
41
|
for (const userId in userReconstructions) {
|
|
28
42
|
const userPortfolio = userReconstructions[userId];
|
|
29
|
-
|
|
43
|
+
|
|
30
44
|
for (const ticker in userPortfolio) {
|
|
31
45
|
const position = userPortfolio[ticker];
|
|
32
|
-
|
|
46
|
+
|
|
33
47
|
if (position.closedToday > 0) {
|
|
34
48
|
if (!aggregator[ticker]) aggregator[ticker] = { closed: 0, forced: 0 };
|
|
35
|
-
|
|
49
|
+
|
|
36
50
|
aggregator[ticker].closed += position.closedToday;
|
|
37
51
|
aggregator[ticker].forced += position.forcedExits;
|
|
38
52
|
}
|
|
@@ -14,17 +14,52 @@ class OwnershipVsPerformanceYTD {
|
|
|
14
14
|
|
|
15
15
|
static getDependencies() { return []; }
|
|
16
16
|
|
|
17
|
+
static getSchema() {
|
|
18
|
+
const metricSchema = {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"properties": {
|
|
21
|
+
"priceYtd": { "type": "number" },
|
|
22
|
+
"ownersYtd": { "type": "number" },
|
|
23
|
+
"currentPrice": { "type": "number" },
|
|
24
|
+
"currentOwners": { "type": "number" }
|
|
25
|
+
},
|
|
26
|
+
"required": ["priceYtd", "ownersYtd", "currentPrice", "currentOwners"]
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const baselineSchema = {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"patternProperties": {
|
|
32
|
+
"^.*$": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"properties": {
|
|
35
|
+
"startPrice": { "type": "number" },
|
|
36
|
+
"startOwners": { "type": "number" },
|
|
37
|
+
"date": { "type": "string" }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"properties": {
|
|
46
|
+
"baselines": baselineSchema
|
|
47
|
+
},
|
|
48
|
+
"additionalProperties": metricSchema
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
17
52
|
async process(context) {
|
|
18
53
|
const { insights: insightsHelper, priceExtractor } = context.math;
|
|
19
54
|
const { previousComputed, mappings, prices, date } = context;
|
|
20
|
-
|
|
55
|
+
|
|
21
56
|
const dailyInsights = insightsHelper.getInsights(context, 'today');
|
|
22
|
-
|
|
57
|
+
|
|
23
58
|
// 1. Manage Baseline State (Jan 1st snapshot)
|
|
24
59
|
// If today is Jan 1st (or first run), we reset. Otherwise, we load from yesterday.
|
|
25
60
|
let baselineState = previousComputed['ownership-vs-performance-ytd']?.baselines || {};
|
|
26
61
|
const isStartOfYear = date.today.endsWith('-01-01') || date.today.endsWith('-01-02');
|
|
27
|
-
|
|
62
|
+
|
|
28
63
|
if (isStartOfYear) {
|
|
29
64
|
baselineState = {}; // Reset for new year
|
|
30
65
|
}
|
|
@@ -37,11 +72,11 @@ class OwnershipVsPerformanceYTD {
|
|
|
37
72
|
if (!ticker) continue;
|
|
38
73
|
|
|
39
74
|
const currentOwners = insightsHelper.getTotalOwners(insight);
|
|
40
|
-
|
|
75
|
+
|
|
41
76
|
// Get Price History
|
|
42
77
|
const priceHist = priceExtractor.getHistory(prices, ticker);
|
|
43
78
|
if (!priceHist.length) continue;
|
|
44
|
-
|
|
79
|
+
|
|
45
80
|
const currentPrice = priceHist[priceHist.length - 1].price;
|
|
46
81
|
|
|
47
82
|
// 2. Set Baseline if missing
|
|
@@ -56,12 +91,12 @@ class OwnershipVsPerformanceYTD {
|
|
|
56
91
|
const base = baselineState[ticker];
|
|
57
92
|
|
|
58
93
|
// 3. Calculate Deltas
|
|
59
|
-
const priceYtd = base.startPrice > 0
|
|
60
|
-
? ((currentPrice - base.startPrice) / base.startPrice) * 100
|
|
94
|
+
const priceYtd = base.startPrice > 0
|
|
95
|
+
? ((currentPrice - base.startPrice) / base.startPrice) * 100
|
|
61
96
|
: 0;
|
|
62
|
-
|
|
63
|
-
const ownersYtd = base.startOwners > 0
|
|
64
|
-
? ((currentOwners - base.startOwners) / base.startOwners) * 100
|
|
97
|
+
|
|
98
|
+
const ownersYtd = base.startOwners > 0
|
|
99
|
+
? ((currentOwners - base.startOwners) / base.startOwners) * 100
|
|
65
100
|
: 0;
|
|
66
101
|
|
|
67
102
|
currentMetrics[ticker] = {
|
|
@@ -75,15 +110,15 @@ class OwnershipVsPerformanceYTD {
|
|
|
75
110
|
// 4. Output structure includes the baselines so they are saved for tomorrow
|
|
76
111
|
this.results = {
|
|
77
112
|
metrics: currentMetrics,
|
|
78
|
-
baselines: baselineState
|
|
113
|
+
baselines: baselineState
|
|
79
114
|
};
|
|
80
115
|
}
|
|
81
116
|
|
|
82
|
-
async getResult() {
|
|
117
|
+
async getResult() {
|
|
83
118
|
// We flatten the 'metrics' for easy API consumption,
|
|
84
119
|
// but we must ensure 'baselines' is preserved in the stored document
|
|
85
120
|
// so 'previousComputed' picks it up tomorrow.
|
|
86
|
-
return { ...this.results.metrics, baselines: this.results.baselines };
|
|
121
|
+
return { ...this.results.metrics, baselines: this.results.baselines };
|
|
87
122
|
}
|
|
88
123
|
}
|
|
89
124
|
|
|
@@ -14,12 +14,29 @@ class OwnershipVsVolatility {
|
|
|
14
14
|
|
|
15
15
|
static getDependencies() { return []; }
|
|
16
16
|
|
|
17
|
+
static getSchema() {
|
|
18
|
+
const schema = {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"properties": {
|
|
21
|
+
"volatilityScore": { "type": "number" },
|
|
22
|
+
"ownerChange7d": { "type": "number" },
|
|
23
|
+
"laggedOwners": { "type": "number" },
|
|
24
|
+
"_history": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": { "type": "number" }
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"required": ["volatilityScore", "ownerChange7d", "laggedOwners"]
|
|
30
|
+
};
|
|
31
|
+
return { "type": "object", "patternProperties": { "^.*$": schema } };
|
|
32
|
+
}
|
|
33
|
+
|
|
17
34
|
async process(context) {
|
|
18
35
|
const { insights: insightsHelper, priceExtractor, compute } = context.math;
|
|
19
36
|
const { previousComputed, mappings, prices } = context;
|
|
20
|
-
|
|
37
|
+
|
|
21
38
|
const dailyInsights = insightsHelper.getInsights(context, 'today');
|
|
22
|
-
|
|
39
|
+
|
|
23
40
|
// Use 7-day lookback for ownership change
|
|
24
41
|
const prevState = previousComputed['ownership-vs-volatility'] || {};
|
|
25
42
|
|
|
@@ -35,17 +52,17 @@ class OwnershipVsVolatility {
|
|
|
35
52
|
const recentPrices = priceHist.slice(-14);
|
|
36
53
|
const returns = [];
|
|
37
54
|
for (let i = 1; i < recentPrices.length; i++) {
|
|
38
|
-
returns.push((recentPrices[i].price - recentPrices[i-1].price) / recentPrices[i-1].price);
|
|
55
|
+
returns.push((recentPrices[i].price - recentPrices[i - 1].price) / recentPrices[i - 1].price);
|
|
39
56
|
}
|
|
40
|
-
|
|
57
|
+
|
|
41
58
|
const volatility = compute.standardDeviation(returns); // Raw std dev
|
|
42
59
|
|
|
43
60
|
// 2. Calculate Ownership Change (Current vs 7 days ago stored state)
|
|
44
61
|
const currentOwners = insightsHelper.getTotalOwners(insight);
|
|
45
62
|
const prevOwners = prevState[ticker]?.laggedOwners || currentOwners;
|
|
46
|
-
|
|
47
|
-
const ownerChangePct = prevOwners > 0
|
|
48
|
-
? ((currentOwners - prevOwners) / prevOwners) * 100
|
|
63
|
+
|
|
64
|
+
const ownerChangePct = prevOwners > 0
|
|
65
|
+
? ((currentOwners - prevOwners) / prevOwners) * 100
|
|
49
66
|
: 0;
|
|
50
67
|
|
|
51
68
|
this.results[ticker] = {
|
|
@@ -56,7 +73,7 @@ class OwnershipVsVolatility {
|
|
|
56
73
|
// Simplified: We store today's value, and the API/UI compares.
|
|
57
74
|
// Better: Use a simple rolling queue like the Momentum calc.
|
|
58
75
|
_history: [...(prevState[ticker]?._history || []), currentOwners].slice(-7),
|
|
59
|
-
|
|
76
|
+
|
|
60
77
|
// For the output: Calculate change vs the oldest in history (approx 7d ago)
|
|
61
78
|
laggedOwners: (prevState[ticker]?._history?.[0] || currentOwners)
|
|
62
79
|
};
|