bulltrackers-module 1.0.768 → 1.0.769

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.
Files changed (51) hide show
  1. package/functions/computation-system-v2/UserPortfolioMetrics.js +50 -0
  2. package/functions/computation-system-v2/computations/BehavioralAnomaly.js +557 -337
  3. package/functions/computation-system-v2/computations/GlobalAumPerAsset30D.js +103 -0
  4. package/functions/computation-system-v2/computations/PIDailyAssetAUM.js +134 -0
  5. package/functions/computation-system-v2/computations/PiFeatureVectors.js +227 -0
  6. package/functions/computation-system-v2/computations/PiRecommender.js +359 -0
  7. package/functions/computation-system-v2/computations/SignedInUserList.js +51 -0
  8. package/functions/computation-system-v2/computations/SignedInUserMirrorHistory.js +138 -0
  9. package/functions/computation-system-v2/computations/SignedInUserPIProfileMetrics.js +106 -0
  10. package/functions/computation-system-v2/computations/SignedInUserProfileMetrics.js +324 -0
  11. package/functions/computation-system-v2/config/bulltrackers.config.js +30 -128
  12. package/functions/computation-system-v2/core-api.js +17 -9
  13. package/functions/computation-system-v2/data_schema_reference.MD +108 -0
  14. package/functions/computation-system-v2/devtools/builder/builder.js +362 -0
  15. package/functions/computation-system-v2/devtools/builder/examples/user-metrics.yaml +26 -0
  16. package/functions/computation-system-v2/devtools/index.js +36 -0
  17. package/functions/computation-system-v2/devtools/shared/MockDataFactory.js +235 -0
  18. package/functions/computation-system-v2/devtools/shared/SchemaTemplates.js +475 -0
  19. package/functions/computation-system-v2/devtools/shared/SystemIntrospector.js +517 -0
  20. package/functions/computation-system-v2/devtools/shared/index.js +16 -0
  21. package/functions/computation-system-v2/devtools/simulation/DAGAnalyzer.js +243 -0
  22. package/functions/computation-system-v2/devtools/simulation/MockDataFetcher.js +306 -0
  23. package/functions/computation-system-v2/devtools/simulation/MockStorageManager.js +336 -0
  24. package/functions/computation-system-v2/devtools/simulation/SimulationEngine.js +525 -0
  25. package/functions/computation-system-v2/devtools/simulation/SimulationServer.js +581 -0
  26. package/functions/computation-system-v2/devtools/simulation/index.js +17 -0
  27. package/functions/computation-system-v2/devtools/simulation/simulate.js +324 -0
  28. package/functions/computation-system-v2/devtools/vscode-computation/package.json +90 -0
  29. package/functions/computation-system-v2/devtools/vscode-computation/snippets/computation.json +128 -0
  30. package/functions/computation-system-v2/devtools/vscode-computation/src/extension.ts +401 -0
  31. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/codeActions.ts +152 -0
  32. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/completions.ts +207 -0
  33. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/diagnostics.ts +205 -0
  34. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/hover.ts +205 -0
  35. package/functions/computation-system-v2/devtools/vscode-computation/tsconfig.json +22 -0
  36. package/functions/computation-system-v2/docs/HowToCreateComputations.MD +602 -0
  37. package/functions/computation-system-v2/framework/data/DataFetcher.js +250 -184
  38. package/functions/computation-system-v2/framework/data/MaterializedViewManager.js +84 -0
  39. package/functions/computation-system-v2/framework/data/QueryBuilder.js +38 -38
  40. package/functions/computation-system-v2/framework/execution/Orchestrator.js +215 -129
  41. package/functions/computation-system-v2/framework/scheduling/ScheduleValidator.js +17 -19
  42. package/functions/computation-system-v2/framework/storage/StateRepository.js +32 -2
  43. package/functions/computation-system-v2/framework/storage/StorageManager.js +105 -67
  44. package/functions/computation-system-v2/framework/testing/ComputationTester.js +12 -6
  45. package/functions/computation-system-v2/handlers/dispatcher.js +57 -29
  46. package/functions/computation-system-v2/legacy/PiAssetRecommender.js.old +115 -0
  47. package/functions/computation-system-v2/legacy/PiSimilarityMatrix.js +104 -0
  48. package/functions/computation-system-v2/legacy/PiSimilarityVector.js +71 -0
  49. package/functions/computation-system-v2/scripts/debug_aggregation.js +25 -0
  50. package/functions/computation-system-v2/scripts/test-invalidation-scenarios.js +234 -0
  51. package/package.json +1 -1
@@ -0,0 +1,207 @@
1
+ /**
2
+ * @fileoverview Completion Provider
3
+ *
4
+ * Provides intelligent autocomplete for:
5
+ * - Table names in requires blocks
6
+ * - Field names for tables
7
+ * - Rule module/function access
8
+ * - Computation dependencies
9
+ */
10
+
11
+ import * as vscode from 'vscode';
12
+
13
+ export class ComputationCompletionProvider implements vscode.CompletionItemProvider {
14
+ private knowledge: SystemKnowledge | null;
15
+
16
+ constructor(knowledge: SystemKnowledge | null) {
17
+ this.knowledge = knowledge;
18
+ }
19
+
20
+ provideCompletionItems(
21
+ document: vscode.TextDocument,
22
+ position: vscode.Position,
23
+ token: vscode.CancellationToken,
24
+ context: vscode.CompletionContext
25
+ ): vscode.CompletionItem[] | vscode.CompletionList {
26
+ const line = document.lineAt(position).text;
27
+ const textBefore = line.substring(0, position.character);
28
+
29
+ // Detect context
30
+ if (this.isInRequiresBlock(document, position)) {
31
+ if (this.isStartingQuote(textBefore)) {
32
+ return this.getTableCompletions();
33
+ }
34
+ }
35
+
36
+ if (this.isAccessingRules(textBefore)) {
37
+ return this.getRuleCompletions(textBefore);
38
+ }
39
+
40
+ if (this.isAccessingData(textBefore)) {
41
+ return this.getTableCompletions();
42
+ }
43
+
44
+ if (this.isInFieldsArray(document, position)) {
45
+ return this.getFieldCompletions(document, position);
46
+ }
47
+
48
+ if (this.isInTypeValue(textBefore)) {
49
+ return this.getTypeCompletions();
50
+ }
51
+
52
+ if (this.isInCategoryValue(textBefore)) {
53
+ return this.getCategoryCompletions();
54
+ }
55
+
56
+ return [];
57
+ }
58
+
59
+ // =========================================================================
60
+ // COMPLETION GENERATORS
61
+ // =========================================================================
62
+
63
+ private getTableCompletions(): vscode.CompletionItem[] {
64
+ if (!this.knowledge) return [];
65
+
66
+ return this.knowledge.tables.map(table => {
67
+ const item = new vscode.CompletionItem(table, vscode.CompletionItemKind.Field);
68
+ const meta = this.knowledge?.tableMetadata[table];
69
+ item.detail = meta ? `Entity: ${meta.entityField}, Date: ${meta.dateField}` : 'Table';
70
+ item.documentation = new vscode.MarkdownString(`**${table}**\n\nBigQuery table for computation input.`);
71
+ item.insertText = table;
72
+ return item;
73
+ });
74
+ }
75
+
76
+ private getRuleCompletions(textBefore: string): vscode.CompletionItem[] {
77
+ if (!this.knowledge) return [];
78
+
79
+ // Check if accessing rules.MODULE.
80
+ const moduleMatch = textBefore.match(/rules\.(\w+)\.$/);
81
+ if (moduleMatch) {
82
+ const module = moduleMatch[1];
83
+ const functions = this.knowledge.ruleFunctions[module] || [];
84
+ return functions.map(fn => {
85
+ const item = new vscode.CompletionItem(fn, vscode.CompletionItemKind.Method);
86
+ item.detail = `${module}.${fn}()`;
87
+ item.insertText = new vscode.SnippetString(`${fn}(\${1})`);
88
+ return item;
89
+ });
90
+ }
91
+
92
+ // Accessing rules.
93
+ if (textBefore.endsWith('rules.')) {
94
+ return this.knowledge.ruleModules.map(mod => {
95
+ const item = new vscode.CompletionItem(mod, vscode.CompletionItemKind.Module);
96
+ item.detail = 'Rule module';
97
+ const funcs = this.knowledge?.ruleFunctions[mod] || [];
98
+ item.documentation = new vscode.MarkdownString(`**${mod}**\n\nFunctions: ${funcs.join(', ')}`);
99
+ return item;
100
+ });
101
+ }
102
+
103
+ return [];
104
+ }
105
+
106
+ private getFieldCompletions(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] {
107
+ if (!this.knowledge) return [];
108
+
109
+ // Try to determine which table we're in
110
+ const text = document.getText(new vscode.Range(new vscode.Position(0, 0), position));
111
+
112
+ // Find the nearest table name in the context
113
+ const tableMatches = [...text.matchAll(/'(\w+_\w+)':\s*\{/g)];
114
+ if (tableMatches.length === 0) return [];
115
+
116
+ const lastTable = tableMatches[tableMatches.length - 1][1];
117
+
118
+ // Return common fields
119
+ const commonFields = ['user_id', 'date', 'pi_id', 'created_at'];
120
+ const tableSpecificFields: Record<string, string[]> = {
121
+ portfolio_snapshots: ['portfolio_data', 'cash', 'total_value', 'platform'],
122
+ trade_history_snapshots: ['trade_history', 'open_trades_count', 'closed_trades_count'],
123
+ pi_rankings: ['rank', 'percentile', 'rankings_data'],
124
+ social_post_snapshots: ['post_count', 'engagement_data'],
125
+ pi_master_list: ['verified_date', 'tier', 'country']
126
+ };
127
+
128
+ const fields = [...commonFields, ...(tableSpecificFields[lastTable] || [])];
129
+
130
+ return fields.map(field => {
131
+ const item = new vscode.CompletionItem(field, vscode.CompletionItemKind.Property);
132
+ item.detail = lastTable;
133
+ item.insertText = `'${field}'`;
134
+ return item;
135
+ });
136
+ }
137
+
138
+ private getTypeCompletions(): vscode.CompletionItem[] {
139
+ return ['per-entity', 'global'].map(type => {
140
+ const item = new vscode.CompletionItem(type, vscode.CompletionItemKind.Value);
141
+ item.detail = type === 'per-entity'
142
+ ? 'Processes one entity at a time'
143
+ : 'Processes all data at once';
144
+ return item;
145
+ });
146
+ }
147
+
148
+ private getCategoryCompletions(): vscode.CompletionItem[] {
149
+ return ['signed_in_user', 'popular_investor', 'global', 'instrument'].map(cat => {
150
+ const item = new vscode.CompletionItem(cat, vscode.CompletionItemKind.Value);
151
+ item.detail = 'Computation category';
152
+ return item;
153
+ });
154
+ }
155
+
156
+ // =========================================================================
157
+ // CONTEXT DETECTION
158
+ // =========================================================================
159
+
160
+ private isInRequiresBlock(document: vscode.TextDocument, position: vscode.Position): boolean {
161
+ const text = document.getText(new vscode.Range(new vscode.Position(0, 0), position));
162
+ const lastRequires = text.lastIndexOf('requires:');
163
+ if (lastRequires === -1) return false;
164
+
165
+ // Count braces after 'requires:'
166
+ const afterRequires = text.substring(lastRequires);
167
+ const openBraces = (afterRequires.match(/\{/g) || []).length;
168
+ const closeBraces = (afterRequires.match(/\}/g) || []).length;
169
+
170
+ return openBraces > closeBraces;
171
+ }
172
+
173
+ private isStartingQuote(textBefore: string): boolean {
174
+ return /[{,]\s*['"]?$/.test(textBefore);
175
+ }
176
+
177
+ private isAccessingRules(textBefore: string): boolean {
178
+ return /rules\.(\w+\.)?$/.test(textBefore);
179
+ }
180
+
181
+ private isAccessingData(textBefore: string): boolean {
182
+ return /data\.\[?['"]?$/.test(textBefore);
183
+ }
184
+
185
+ private isInFieldsArray(document: vscode.TextDocument, position: vscode.Position): boolean {
186
+ const line = document.lineAt(position).text;
187
+ return line.includes('fields:') && line.includes('[');
188
+ }
189
+
190
+ private isInTypeValue(textBefore: string): boolean {
191
+ return /type:\s*['"]?$/.test(textBefore);
192
+ }
193
+
194
+ private isInCategoryValue(textBefore: string): boolean {
195
+ return /category:\s*['"]?$/.test(textBefore);
196
+ }
197
+ }
198
+
199
+ interface SystemKnowledge {
200
+ tables: string[];
201
+ tableMetadata: Record<string, any>;
202
+ ruleModules: string[];
203
+ ruleFunctions: Record<string, string[]>;
204
+ maxLookback: number;
205
+ validTypes: string[];
206
+ validCategories: string[];
207
+ }
@@ -0,0 +1,205 @@
1
+ /**
2
+ * @fileoverview Diagnostics Provider
3
+ *
4
+ * Provides real-time validation:
5
+ * - Unknown table names
6
+ * - Lookback exceeds maximum
7
+ * - Missing required config fields
8
+ * - Invalid type/category values
9
+ * - Unused dependencies
10
+ */
11
+
12
+ import * as vscode from 'vscode';
13
+
14
+ export class ComputationDiagnosticsProvider implements vscode.Disposable {
15
+ private diagnosticCollection: vscode.DiagnosticCollection;
16
+ private knowledge: SystemKnowledge | null;
17
+
18
+ constructor(knowledge: SystemKnowledge | null) {
19
+ this.knowledge = knowledge;
20
+ this.diagnosticCollection = vscode.languages.createDiagnosticCollection('computation');
21
+ }
22
+
23
+ dispose() {
24
+ this.diagnosticCollection.dispose();
25
+ }
26
+
27
+ validateDocument(document: vscode.TextDocument) {
28
+ const diagnostics: vscode.Diagnostic[] = [];
29
+ const text = document.getText();
30
+
31
+ // Only validate computation files
32
+ if (!this.isComputationFile(text)) {
33
+ this.diagnosticCollection.set(document.uri, []);
34
+ return;
35
+ }
36
+
37
+ // Extract config block
38
+ const configMatch = text.match(/getConfig\s*\(\s*\)\s*\{[\s\S]*?return\s*\{([\s\S]*?)\};/);
39
+ if (!configMatch) {
40
+ diagnostics.push(this.createDiagnostic(
41
+ document,
42
+ 'Missing getConfig() method',
43
+ 0,
44
+ vscode.DiagnosticSeverity.Error
45
+ ));
46
+ } else {
47
+ const configContent = configMatch[1];
48
+ const configStart = document.positionAt(text.indexOf(configMatch[0]));
49
+
50
+ // Validate required fields
51
+ this.validateRequiredFields(document, configContent, configStart, diagnostics);
52
+
53
+ // Validate requires block
54
+ this.validateRequiresBlock(document, text, diagnostics);
55
+
56
+ // Validate type and category
57
+ this.validateEnumFields(document, text, diagnostics);
58
+ }
59
+
60
+ // Validate process method
61
+ if (!text.includes('async process(')) {
62
+ diagnostics.push(this.createDiagnostic(
63
+ document,
64
+ 'Missing process() method',
65
+ 0,
66
+ vscode.DiagnosticSeverity.Error
67
+ ));
68
+ }
69
+
70
+ this.diagnosticCollection.set(document.uri, diagnostics);
71
+ }
72
+
73
+ private validateRequiredFields(
74
+ document: vscode.TextDocument,
75
+ configContent: string,
76
+ configStart: vscode.Position,
77
+ diagnostics: vscode.Diagnostic[]
78
+ ) {
79
+ const requiredFields = ['name', 'type', 'category'];
80
+
81
+ for (const field of requiredFields) {
82
+ const regex = new RegExp(`${field}\\s*:`);
83
+ if (!regex.test(configContent)) {
84
+ diagnostics.push(new vscode.Diagnostic(
85
+ new vscode.Range(configStart, configStart.translate(1)),
86
+ `Missing required config field: ${field}`,
87
+ vscode.DiagnosticSeverity.Error
88
+ ));
89
+ }
90
+ }
91
+ }
92
+
93
+ private validateRequiresBlock(
94
+ document: vscode.TextDocument,
95
+ text: string,
96
+ diagnostics: vscode.Diagnostic[]
97
+ ) {
98
+ if (!this.knowledge) return;
99
+
100
+ // Find all table references in requires
101
+ const tableMatches = [...text.matchAll(/'(\w+_?\w*)':\s*\{[^}]*lookback/g)];
102
+
103
+ for (const match of tableMatches) {
104
+ const tableName = match[1];
105
+ const position = document.positionAt(text.indexOf(match[0]));
106
+
107
+ // Check if table exists
108
+ if (!this.knowledge.tables.includes(tableName)) {
109
+ const range = new vscode.Range(
110
+ position,
111
+ position.translate(0, tableName.length + 2)
112
+ );
113
+ diagnostics.push(new vscode.Diagnostic(
114
+ range,
115
+ `Unknown table: ${tableName}`,
116
+ vscode.DiagnosticSeverity.Warning
117
+ ));
118
+ }
119
+ }
120
+
121
+ // Check lookback values
122
+ const lookbackMatches = [...text.matchAll(/lookback:\s*(\d+)/g)];
123
+ for (const match of lookbackMatches) {
124
+ const lookback = parseInt(match[1]);
125
+ const position = document.positionAt(text.indexOf(match[0]));
126
+
127
+ if (lookback > this.knowledge.maxLookback) {
128
+ const range = new vscode.Range(
129
+ position,
130
+ position.translate(0, match[0].length)
131
+ );
132
+ diagnostics.push(new vscode.Diagnostic(
133
+ range,
134
+ `Lookback ${lookback} exceeds maximum of ${this.knowledge.maxLookback}`,
135
+ vscode.DiagnosticSeverity.Error
136
+ ));
137
+ } else if (lookback > this.knowledge.maxLookback * 0.7) {
138
+ const range = new vscode.Range(
139
+ position,
140
+ position.translate(0, match[0].length)
141
+ );
142
+ diagnostics.push(new vscode.Diagnostic(
143
+ range,
144
+ `Lookback ${lookback} is high (${Math.round(lookback / this.knowledge.maxLookback * 100)}% of max)`,
145
+ vscode.DiagnosticSeverity.Warning
146
+ ));
147
+ }
148
+ }
149
+ }
150
+
151
+ private validateEnumFields(
152
+ document: vscode.TextDocument,
153
+ text: string,
154
+ diagnostics: vscode.Diagnostic[]
155
+ ) {
156
+ if (!this.knowledge) return;
157
+
158
+ // Validate type
159
+ const typeMatch = text.match(/type:\s*['"](\w+)['"]/);
160
+ if (typeMatch && !this.knowledge.validTypes.includes(typeMatch[1])) {
161
+ const position = document.positionAt(text.indexOf(typeMatch[0]));
162
+ diagnostics.push(new vscode.Diagnostic(
163
+ new vscode.Range(position, position.translate(0, typeMatch[0].length)),
164
+ `Invalid type: ${typeMatch[1]}. Must be: ${this.knowledge.validTypes.join(', ')}`,
165
+ vscode.DiagnosticSeverity.Error
166
+ ));
167
+ }
168
+
169
+ // Validate category
170
+ const categoryMatch = text.match(/category:\s*['"](\w+)['"]/);
171
+ if (categoryMatch && !this.knowledge.validCategories.includes(categoryMatch[1])) {
172
+ const position = document.positionAt(text.indexOf(categoryMatch[0]));
173
+ diagnostics.push(new vscode.Diagnostic(
174
+ new vscode.Range(position, position.translate(0, categoryMatch[0].length)),
175
+ `Invalid category: ${categoryMatch[1]}. Must be: ${this.knowledge.validCategories.join(', ')}`,
176
+ vscode.DiagnosticSeverity.Error
177
+ ));
178
+ }
179
+ }
180
+
181
+ private isComputationFile(text: string): boolean {
182
+ return text.includes('extends Computation') ||
183
+ (text.includes('getConfig') && text.includes('process'));
184
+ }
185
+
186
+ private createDiagnostic(
187
+ document: vscode.TextDocument,
188
+ message: string,
189
+ line: number,
190
+ severity: vscode.DiagnosticSeverity
191
+ ): vscode.Diagnostic {
192
+ const lineText = document.lineAt(line);
193
+ return new vscode.Diagnostic(lineText.range, message, severity);
194
+ }
195
+ }
196
+
197
+ interface SystemKnowledge {
198
+ tables: string[];
199
+ tableMetadata: Record<string, any>;
200
+ ruleModules: string[];
201
+ ruleFunctions: Record<string, string[]>;
202
+ maxLookback: number;
203
+ validTypes: string[];
204
+ validCategories: string[];
205
+ }
@@ -0,0 +1,205 @@
1
+ /**
2
+ * @fileoverview Hover Provider
3
+ *
4
+ * Provides hover documentation for:
5
+ * - Table names (shows fields, date field, entity field)
6
+ * - Rule functions (shows signature, description)
7
+ * - Config fields (shows valid values, defaults)
8
+ */
9
+
10
+ import * as vscode from 'vscode';
11
+
12
+ export class ComputationHoverProvider implements vscode.HoverProvider {
13
+ private knowledge: SystemKnowledge | null;
14
+
15
+ constructor(knowledge: SystemKnowledge | null) {
16
+ this.knowledge = knowledge;
17
+ }
18
+
19
+ provideHover(
20
+ document: vscode.TextDocument,
21
+ position: vscode.Position,
22
+ token: vscode.CancellationToken
23
+ ): vscode.Hover | null {
24
+ const wordRange = document.getWordRangeAtPosition(position, /[\w_]+/);
25
+ if (!wordRange) return null;
26
+
27
+ const word = document.getText(wordRange);
28
+ const line = document.lineAt(position).text;
29
+
30
+ // Check if hovering over a table name
31
+ if (this.knowledge?.tables.includes(word)) {
32
+ return this.getTableHover(word);
33
+ }
34
+
35
+ // Check if hovering over a rule function
36
+ const ruleMatch = line.match(/rules\.(\w+)\.(\w+)/);
37
+ if (ruleMatch && ruleMatch[2] === word) {
38
+ return this.getRuleFunctionHover(ruleMatch[1], ruleMatch[2]);
39
+ }
40
+
41
+ // Check if hovering over config field
42
+ if (this.isConfigField(word, line)) {
43
+ return this.getConfigFieldHover(word);
44
+ }
45
+
46
+ return null;
47
+ }
48
+
49
+ private getTableHover(tableName: string): vscode.Hover {
50
+ const meta = this.knowledge?.tableMetadata[tableName];
51
+
52
+ const md = new vscode.MarkdownString();
53
+ md.appendMarkdown(`### 📊 ${tableName}\n\n`);
54
+
55
+ if (meta) {
56
+ md.appendMarkdown(`**Entity Field:** \`${meta.entityField || 'unknown'}\`\n\n`);
57
+ md.appendMarkdown(`**Date Field:** \`${meta.dateField || 'none'}\`\n\n`);
58
+ }
59
+
60
+ // Add common fields for known tables
61
+ const fieldInfo = this.getTableFieldInfo(tableName);
62
+ if (fieldInfo) {
63
+ md.appendMarkdown(`**Key Fields:**\n`);
64
+ for (const [field, desc] of Object.entries(fieldInfo)) {
65
+ md.appendMarkdown(`- \`${field}\`: ${desc}\n`);
66
+ }
67
+ }
68
+
69
+ return new vscode.Hover(md);
70
+ }
71
+
72
+ private getRuleFunctionHover(module: string, func: string): vscode.Hover | null {
73
+ const functionDocs: Record<string, Record<string, string>> = {
74
+ portfolio: {
75
+ extractPositions: `Extract positions from portfolio_data JSON.
76
+ \`\`\`javascript
77
+ const positions = rules.portfolio.extractPositions(portfolioData);
78
+ // Returns: { instrumentId: position }[]
79
+ \`\`\``,
80
+ extractMirrors: `Extract mirror positions from portfolio_data.
81
+ \`\`\`javascript
82
+ const mirrors = rules.portfolio.extractMirrors(portfolioData);
83
+ // Returns: { username, allocation, netProfit }[]
84
+ \`\`\``,
85
+ getInvested: `Calculate total invested amount.
86
+ \`\`\`javascript
87
+ const invested = rules.portfolio.getInvested(positions);
88
+ // Returns: number
89
+ \`\`\``,
90
+ getNetProfit: `Calculate net profit from positions.
91
+ \`\`\`javascript
92
+ const profit = rules.portfolio.getNetProfit(positions);
93
+ // Returns: number
94
+ \`\`\``
95
+ },
96
+ metrics: {
97
+ calculateSharpeRatio: `Calculate Sharpe ratio from daily returns.
98
+ \`\`\`javascript
99
+ const sharpe = rules.metrics.calculateSharpeRatio(returns);
100
+ // Returns: number (-3 to 3 typical)
101
+ \`\`\``,
102
+ calculateVolatility: `Calculate annualized volatility.
103
+ \`\`\`javascript
104
+ const vol = rules.metrics.calculateVolatility(returns);
105
+ // Returns: number (percentage)
106
+ \`\`\``,
107
+ calculateDrawdown: `Calculate maximum drawdown.
108
+ \`\`\`javascript
109
+ const dd = rules.metrics.calculateDrawdown(equityCurve);
110
+ // Returns: { maxDrawdown, currentDrawdown }
111
+ \`\`\``
112
+ },
113
+ rankings: {
114
+ getRank: `Get ranking position.
115
+ \`\`\`javascript
116
+ const rank = rules.rankings.getRank(data, metric);
117
+ // Returns: number (1 = best)
118
+ \`\`\``,
119
+ getPercentile: `Get percentile position.
120
+ \`\`\`javascript
121
+ const pct = rules.rankings.getPercentile(data, metric);
122
+ // Returns: number (0-100)
123
+ \`\`\``
124
+ }
125
+ };
126
+
127
+ const doc = functionDocs[module]?.[func];
128
+ if (!doc) return null;
129
+
130
+ const md = new vscode.MarkdownString();
131
+ md.appendMarkdown(`### ${module}.${func}\n\n`);
132
+ md.appendMarkdown(doc);
133
+ md.isTrusted = true;
134
+
135
+ return new vscode.Hover(md);
136
+ }
137
+
138
+ private getConfigFieldHover(field: string): vscode.Hover | null {
139
+ const configDocs: Record<string, string> = {
140
+ name: `**name** (required)\n\nUnique identifier for the computation. Should match the class name.`,
141
+ type: `**type** (required)\n\nExecution mode:\n- \`per-entity\`: Process one entity at a time\n- \`global\`: Process all data at once`,
142
+ category: `**category** (required)\n\nEntity category:\n- \`signed_in_user\`: User-specific computations\n- \`popular_investor\`: PI-specific computations\n- \`global\`: Cross-entity computations`,
143
+ isHistorical: `**isHistorical** (optional)\n\n\`true\` = needs lookback data for comparison\n\`false\` = only needs current date (default)`,
144
+ lookback: `**lookback** (optional)\n\nNumber of historical days to fetch.\n- Max: **90 days**\n- Warning threshold: **63 days** (70%)`,
145
+ mandatory: `**mandatory** (optional)\n\n\`true\` = computation fails if data missing\n\`false\` = continues with empty data (default)`,
146
+ requires: `**requires** (important)\n\nTable/dependency requirements for this computation.\n\`\`\`javascript
147
+ requires: {
148
+ 'table_name': { lookback: 0, mandatory: true }
149
+ }
150
+ \`\`\``,
151
+ dependencies: `**dependencies** (optional)\n\nOther computations that must run first.\n\`\`\`javascript
152
+ dependencies: ['ComputationA', 'ComputationB']
153
+ \`\`\``
154
+ };
155
+
156
+ const doc = configDocs[field];
157
+ if (!doc) return null;
158
+
159
+ const md = new vscode.MarkdownString(doc);
160
+ md.isTrusted = true;
161
+ return new vscode.Hover(md);
162
+ }
163
+
164
+ private getTableFieldInfo(tableName: string): Record<string, string> | null {
165
+ const fieldDocs: Record<string, Record<string, string>> = {
166
+ portfolio_snapshots: {
167
+ user_id: 'Entity identifier',
168
+ date: 'Snapshot date',
169
+ portfolio_data: 'JSON with positions, mirrors, metrics',
170
+ cash: 'Available cash balance',
171
+ total_value: 'Total portfolio value'
172
+ },
173
+ pi_rankings: {
174
+ pi_id: 'Popular Investor ID',
175
+ date: 'Ranking date',
176
+ rank: 'Overall rank position',
177
+ rankings_data: 'JSON with detailed rankings'
178
+ },
179
+ trade_history_snapshots: {
180
+ user_id: 'Entity identifier',
181
+ trade_history: 'JSON array of trades',
182
+ open_trades_count: 'Number of open positions',
183
+ closed_trades_count: 'Number of closed positions'
184
+ }
185
+ };
186
+
187
+ return fieldDocs[tableName] || null;
188
+ }
189
+
190
+ private isConfigField(word: string, line: string): boolean {
191
+ const configFields = ['name', 'type', 'category', 'isHistorical', 'lookback',
192
+ 'mandatory', 'requires', 'dependencies', 'storage'];
193
+ return configFields.includes(word) && line.includes(':');
194
+ }
195
+ }
196
+
197
+ interface SystemKnowledge {
198
+ tables: string[];
199
+ tableMetadata: Record<string, any>;
200
+ ruleModules: string[];
201
+ ruleFunctions: Record<string, string[]>;
202
+ maxLookback: number;
203
+ validTypes: string[];
204
+ validCategories: string[];
205
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "target": "ES2020",
5
+ "outDir": "out",
6
+ "lib": [
7
+ "ES2020"
8
+ ],
9
+ "sourceMap": true,
10
+ "rootDir": "src",
11
+ "strict": true,
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true,
14
+ "forceConsistentCasingInFileNames": true
15
+ },
16
+ "include": [
17
+ "src/**/*"
18
+ ],
19
+ "exclude": [
20
+ "node_modules"
21
+ ]
22
+ }