@gotza02/sequential-thinking 10000.1.0 → 10000.1.2
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/dist/dashboard/server.d.ts +0 -34
- package/dist/dashboard/server.js +50 -269
- package/dist/tools/sports/core/alert-manager.d.ts +1 -50
- package/dist/tools/sports/core/alert-manager.js +62 -123
- package/dist/tools/sports/core/data-quality.d.ts +0 -44
- package/dist/tools/sports/core/data-quality.js +49 -266
- package/dist/tools/sports/core/historical-analyzer.d.ts +0 -54
- package/dist/tools/sports/core/historical-analyzer.js +56 -256
- package/dist/tools/sports/core/index.d.ts +1 -1
- package/dist/tools/sports/core/index.js +1 -1
- package/dist/tools/sports/core/ml-prediction.d.ts +7 -65
- package/dist/tools/sports/core/ml-prediction.js +43 -185
- package/dist/tools/sports/core/realtime-manager.d.ts +1 -52
- package/dist/tools/sports/core/realtime-manager.js +18 -127
- package/package.json +1 -1
|
@@ -1,41 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DATA QUALITY VALIDATOR
|
|
3
|
-
* Validates and scores data quality for football matches
|
|
4
3
|
*/
|
|
5
|
-
|
|
4
|
+
// Simple logger fallback
|
|
5
|
+
const logger = {
|
|
6
|
+
info: (...args) => console.error('[INFO]', ...args),
|
|
7
|
+
warn: (...args) => console.warn('[WARN]', ...args),
|
|
8
|
+
error: (...args) => console.error('[ERROR]', ...args),
|
|
9
|
+
debug: (...args) => { }
|
|
10
|
+
};
|
|
6
11
|
export class DataQualityValidator {
|
|
7
|
-
knownTeams = new Set();
|
|
8
|
-
knownLeagues = new Set();
|
|
9
|
-
constructor() {
|
|
10
|
-
// Initialize known leagues
|
|
11
|
-
this.knownLeagues.add('Premier League');
|
|
12
|
-
this.knownLeagues.add('La Liga');
|
|
13
|
-
this.knownLeagues.add('Bundesliga');
|
|
14
|
-
this.knownLeagues.add('Serie A');
|
|
15
|
-
this.knownLeagues.add('Ligue 1');
|
|
16
|
-
this.knownLeagues.add('Champions League');
|
|
17
|
-
this.knownLeagues.add('Europa League');
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Validate match data and return quality report
|
|
21
|
-
*/
|
|
22
12
|
validateMatchData(data) {
|
|
23
13
|
const issues = [];
|
|
24
14
|
const warnings = [];
|
|
25
15
|
const suggestions = [];
|
|
26
|
-
// Check completeness
|
|
27
16
|
const completeness = this.checkCompleteness(data, issues);
|
|
28
|
-
// Check accuracy
|
|
29
17
|
const accuracy = this.checkAccuracy(data, issues, warnings);
|
|
30
|
-
// Check freshness
|
|
31
18
|
const freshness = this.checkFreshness(data, warnings);
|
|
32
|
-
// Check consistency
|
|
33
19
|
const consistency = this.checkConsistency(data, issues, warnings);
|
|
34
|
-
// Generate suggestions
|
|
35
20
|
this.generateSuggestions(data, suggestions);
|
|
36
|
-
// Calculate overall score
|
|
37
21
|
const overall = Math.round((completeness + accuracy + freshness + consistency) / 4);
|
|
38
|
-
|
|
22
|
+
logger.debug(`[DataQuality] Match ${data.id}: Overall=${overall}`);
|
|
23
|
+
return {
|
|
39
24
|
overall,
|
|
40
25
|
completeness,
|
|
41
26
|
accuracy,
|
|
@@ -45,101 +30,46 @@ export class DataQualityValidator {
|
|
|
45
30
|
warnings: [...issues.filter(i => i.severity === 'warning'), ...warnings],
|
|
46
31
|
suggestions,
|
|
47
32
|
};
|
|
48
|
-
logger.debug(`[DataQuality] Match ${data.id}: Overall=${overall}, Completeness=${completeness}, Accuracy=${accuracy}`);
|
|
49
|
-
return report;
|
|
50
33
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Validate team data
|
|
53
|
-
*/
|
|
54
34
|
validateTeamData(data) {
|
|
55
35
|
const issues = [];
|
|
56
36
|
const warnings = [];
|
|
57
|
-
const suggestions = [];
|
|
58
|
-
// Check required fields
|
|
59
37
|
if (!data.name) {
|
|
60
|
-
issues.push({
|
|
61
|
-
field: 'name',
|
|
62
|
-
severity: 'error',
|
|
63
|
-
message: 'Team name is required',
|
|
64
|
-
});
|
|
38
|
+
issues.push({ field: 'name', severity: 'error', message: 'Team name is required' });
|
|
65
39
|
}
|
|
66
40
|
if (!data.country) {
|
|
67
|
-
warnings.push({
|
|
68
|
-
field: 'country',
|
|
69
|
-
severity: 'warning',
|
|
70
|
-
message: 'Team country is missing',
|
|
71
|
-
suggestion: 'Add country information for better filtering',
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
// Validate stats if present
|
|
75
|
-
if (data.stats) {
|
|
76
|
-
this.validateTeamStats(data.stats, issues, warnings);
|
|
41
|
+
warnings.push({ field: 'country', severity: 'warning', message: 'Team country is missing' });
|
|
77
42
|
}
|
|
78
43
|
const completeness = this.calculateScore(issues, warnings, 3);
|
|
79
|
-
const accuracy = 100; // Simplified
|
|
80
|
-
const freshness = 100;
|
|
81
|
-
const consistency = 100;
|
|
82
44
|
return {
|
|
83
|
-
overall:
|
|
45
|
+
overall: completeness,
|
|
84
46
|
completeness,
|
|
85
|
-
accuracy,
|
|
86
|
-
freshness,
|
|
87
|
-
consistency,
|
|
47
|
+
accuracy: 100,
|
|
48
|
+
freshness: 100,
|
|
49
|
+
consistency: 100,
|
|
88
50
|
issues: issues.filter(i => i.severity === 'error'),
|
|
89
51
|
warnings: [...issues.filter(i => i.severity === 'warning'), ...warnings],
|
|
90
|
-
suggestions,
|
|
52
|
+
suggestions: [],
|
|
91
53
|
};
|
|
92
54
|
}
|
|
93
|
-
/**
|
|
94
|
-
* Validate odds data
|
|
95
|
-
*/
|
|
96
55
|
validateOddsData(odds) {
|
|
97
56
|
const issues = [];
|
|
98
57
|
const warnings = [];
|
|
99
|
-
// Check required fields
|
|
100
58
|
if (!odds.homeWin || odds.homeWin <= 1) {
|
|
101
|
-
issues.push({
|
|
102
|
-
field: 'homeWin',
|
|
103
|
-
severity: 'error',
|
|
104
|
-
message: `Invalid home win odds: ${odds.homeWin}`,
|
|
105
|
-
value: odds.homeWin,
|
|
106
|
-
});
|
|
59
|
+
issues.push({ field: 'homeWin', severity: 'error', message: `Invalid home win odds: ${odds.homeWin}`, value: odds.homeWin });
|
|
107
60
|
}
|
|
108
61
|
if (!odds.draw || odds.draw <= 1) {
|
|
109
|
-
issues.push({
|
|
110
|
-
field: 'draw',
|
|
111
|
-
severity: 'error',
|
|
112
|
-
message: `Invalid draw odds: ${odds.draw}`,
|
|
113
|
-
value: odds.draw,
|
|
114
|
-
});
|
|
62
|
+
issues.push({ field: 'draw', severity: 'error', message: `Invalid draw odds: ${odds.draw}`, value: odds.draw });
|
|
115
63
|
}
|
|
116
64
|
if (!odds.awayWin || odds.awayWin <= 1) {
|
|
117
|
-
issues.push({
|
|
118
|
-
field: 'awayWin',
|
|
119
|
-
severity: 'error',
|
|
120
|
-
message: `Invalid away win odds: ${odds.awayWin}`,
|
|
121
|
-
value: odds.awayWin,
|
|
122
|
-
});
|
|
65
|
+
issues.push({ field: 'awayWin', severity: 'error', message: `Invalid away win odds: ${odds.awayWin}`, value: odds.awayWin });
|
|
123
66
|
}
|
|
124
|
-
// Check implied probability (overround)
|
|
125
67
|
const impliedProb = (1 / odds.homeWin) + (1 / odds.draw) + (1 / odds.awayWin);
|
|
126
68
|
if (impliedProb < 1 || impliedProb > 1.5) {
|
|
127
|
-
warnings.push({
|
|
128
|
-
field: 'odds',
|
|
129
|
-
severity: 'warning',
|
|
130
|
-
message: `Unusual implied probability: ${(impliedProb * 100).toFixed(1)}%`,
|
|
131
|
-
suggestion: 'Normal range is 100-115%',
|
|
132
|
-
value: impliedProb,
|
|
133
|
-
});
|
|
69
|
+
warnings.push({ field: 'odds', severity: 'warning', message: `Unusual implied probability: ${(impliedProb * 100).toFixed(1)}%` });
|
|
134
70
|
}
|
|
135
|
-
// Check for arbitrage opportunity (unlikely but possible error)
|
|
136
71
|
if (impliedProb < 0.95) {
|
|
137
|
-
issues.push({
|
|
138
|
-
field: 'odds',
|
|
139
|
-
severity: 'error',
|
|
140
|
-
message: 'Possible arbitrage opportunity detected - verify odds accuracy',
|
|
141
|
-
value: impliedProb,
|
|
142
|
-
});
|
|
72
|
+
issues.push({ field: 'odds', severity: 'error', message: 'Possible arbitrage opportunity detected', value: impliedProb });
|
|
143
73
|
}
|
|
144
74
|
const completeness = this.calculateScore(issues, warnings, 3);
|
|
145
75
|
return {
|
|
@@ -153,9 +83,6 @@ export class DataQualityValidator {
|
|
|
153
83
|
suggestions: [],
|
|
154
84
|
};
|
|
155
85
|
}
|
|
156
|
-
/**
|
|
157
|
-
* Check data completeness
|
|
158
|
-
*/
|
|
159
86
|
checkCompleteness(data, issues) {
|
|
160
87
|
const requiredFields = [
|
|
161
88
|
{ key: 'id', weight: 15 },
|
|
@@ -168,7 +95,8 @@ export class DataQualityValidator {
|
|
|
168
95
|
const optionalFields = [
|
|
169
96
|
{ key: 'score', weight: 5 },
|
|
170
97
|
{ key: 'odds', weight: 5 },
|
|
171
|
-
{ key: '
|
|
98
|
+
{ key: 'homeLineup', weight: 1.5 },
|
|
99
|
+
{ key: 'awayLineup', weight: 1.5 },
|
|
172
100
|
{ key: 'events', weight: 3 },
|
|
173
101
|
{ key: 'venue', weight: 2 },
|
|
174
102
|
{ key: 'referee', weight: 2 },
|
|
@@ -180,247 +108,110 @@ export class DataQualityValidator {
|
|
|
180
108
|
score += weight;
|
|
181
109
|
}
|
|
182
110
|
else {
|
|
183
|
-
issues.push({
|
|
184
|
-
field: key,
|
|
185
|
-
severity: 'error',
|
|
186
|
-
message: `Required field '${key}' is missing`,
|
|
187
|
-
});
|
|
111
|
+
issues.push({ field: key, severity: 'error', message: `Required field '${key}' is missing` });
|
|
188
112
|
}
|
|
189
113
|
});
|
|
190
114
|
optionalFields.forEach(({ key, weight }) => {
|
|
191
115
|
const value = data[key];
|
|
192
|
-
if (value !== undefined && value !== null)
|
|
116
|
+
if (value !== undefined && value !== null)
|
|
193
117
|
score += weight;
|
|
194
|
-
}
|
|
195
118
|
});
|
|
196
119
|
return score;
|
|
197
120
|
}
|
|
198
|
-
/**
|
|
199
|
-
* Check data accuracy
|
|
200
|
-
*/
|
|
201
121
|
checkAccuracy(data, issues, warnings) {
|
|
202
122
|
let score = 100;
|
|
203
|
-
// Check score consistency with status
|
|
204
123
|
if (data.score && data.status === 'scheduled') {
|
|
205
124
|
score -= 15;
|
|
206
|
-
warnings.push({
|
|
207
|
-
field: 'score',
|
|
208
|
-
severity: 'warning',
|
|
209
|
-
message: 'Match has score but status is "scheduled"',
|
|
210
|
-
suggestion: 'Update status to "live" or "finished"',
|
|
211
|
-
});
|
|
125
|
+
warnings.push({ field: 'score', severity: 'warning', message: 'Match has score but status is "scheduled"' });
|
|
212
126
|
}
|
|
213
|
-
// Check for negative scores
|
|
214
127
|
if (data.score) {
|
|
215
128
|
if (data.score.home < 0 || data.score.away < 0) {
|
|
216
129
|
score -= 20;
|
|
217
|
-
issues.push({
|
|
218
|
-
field: 'score',
|
|
219
|
-
severity: 'error',
|
|
220
|
-
message: 'Negative score detected',
|
|
221
|
-
value: data.score,
|
|
222
|
-
});
|
|
130
|
+
issues.push({ field: 'score', severity: 'error', message: 'Negative score detected', value: data.score });
|
|
223
131
|
}
|
|
224
|
-
// Check for unrealistic scores
|
|
225
132
|
if (data.score.home > 15 || data.score.away > 15) {
|
|
226
133
|
score -= 10;
|
|
227
|
-
warnings.push({
|
|
228
|
-
field: 'score',
|
|
229
|
-
severity: 'warning',
|
|
230
|
-
message: 'Unusually high score detected',
|
|
231
|
-
value: `${data.score.home}-${data.score.away}`,
|
|
232
|
-
});
|
|
134
|
+
warnings.push({ field: 'score', severity: 'warning', message: 'Unusually high score detected', value: `${data.score.home}-${data.score.away}` });
|
|
233
135
|
}
|
|
234
136
|
}
|
|
235
|
-
// Check date validity
|
|
236
137
|
if (data.date) {
|
|
237
|
-
const matchDate = new Date(data.date);
|
|
238
|
-
const now =
|
|
239
|
-
const oneYearAgo =
|
|
240
|
-
const oneYearFromNow =
|
|
138
|
+
const matchDate = new Date(data.date).getTime();
|
|
139
|
+
const now = Date.now();
|
|
140
|
+
const oneYearAgo = now - 365 * 24 * 60 * 60 * 1000;
|
|
141
|
+
const oneYearFromNow = now + 365 * 24 * 60 * 60 * 1000;
|
|
241
142
|
if (matchDate < oneYearAgo || matchDate > oneYearFromNow) {
|
|
242
143
|
score -= 10;
|
|
243
|
-
warnings.push({
|
|
244
|
-
field: 'date',
|
|
245
|
-
severity: 'warning',
|
|
246
|
-
message: 'Match date is outside expected range',
|
|
247
|
-
value: data.date,
|
|
248
|
-
});
|
|
144
|
+
warnings.push({ field: 'date', severity: 'warning', message: 'Match date is outside expected range', value: data.date });
|
|
249
145
|
}
|
|
250
146
|
}
|
|
251
|
-
// Check team names
|
|
252
147
|
if (data.homeTeam && data.awayTeam) {
|
|
253
148
|
if (data.homeTeam.name === data.awayTeam.name) {
|
|
254
149
|
score -= 20;
|
|
255
|
-
issues.push({
|
|
256
|
-
field: 'teams',
|
|
257
|
-
severity: 'error',
|
|
258
|
-
message: 'Home and away teams have the same name',
|
|
259
|
-
value: data.homeTeam.name,
|
|
260
|
-
});
|
|
150
|
+
issues.push({ field: 'teams', severity: 'error', message: 'Home and away teams have the same name', value: data.homeTeam.name });
|
|
261
151
|
}
|
|
262
152
|
}
|
|
263
153
|
return Math.max(0, score);
|
|
264
154
|
}
|
|
265
|
-
/**
|
|
266
|
-
* Check data freshness
|
|
267
|
-
*/
|
|
268
155
|
checkFreshness(data, warnings) {
|
|
269
156
|
if (!data.date)
|
|
270
157
|
return 50;
|
|
271
158
|
const matchDate = new Date(data.date).getTime();
|
|
272
159
|
const now = Date.now();
|
|
273
160
|
const age = now - matchDate;
|
|
274
|
-
// For live matches, data should be very fresh
|
|
275
161
|
if (data.status === 'live') {
|
|
276
|
-
// Live data older than 2 minutes is stale
|
|
277
162
|
if (age > 2 * 60 * 1000) {
|
|
278
|
-
warnings.push({
|
|
279
|
-
field: 'freshness',
|
|
280
|
-
severity: 'warning',
|
|
281
|
-
message: 'Live match data is stale (>2 minutes old)',
|
|
282
|
-
suggestion: 'Refresh data from source',
|
|
283
|
-
});
|
|
163
|
+
warnings.push({ field: 'freshness', severity: 'warning', message: 'Live match data is stale (>2 minutes old)' });
|
|
284
164
|
return 50;
|
|
285
165
|
}
|
|
286
166
|
return 100;
|
|
287
167
|
}
|
|
288
|
-
|
|
289
|
-
if (data.status === 'scheduled') {
|
|
168
|
+
if (data.status === 'scheduled')
|
|
290
169
|
return 100;
|
|
291
|
-
}
|
|
292
|
-
// For finished matches, check if recently completed
|
|
293
170
|
if (data.status === 'finished') {
|
|
294
|
-
|
|
295
|
-
if (age < 60 * 60 * 1000) {
|
|
171
|
+
if (age < 60 * 60 * 1000)
|
|
296
172
|
return 100;
|
|
297
|
-
|
|
298
|
-
// Match finished within last day
|
|
299
|
-
if (age < 24 * 60 * 60 * 1000) {
|
|
173
|
+
if (age < 24 * 60 * 60 * 1000)
|
|
300
174
|
return 80;
|
|
301
|
-
}
|
|
302
175
|
return 60;
|
|
303
176
|
}
|
|
304
177
|
return 70;
|
|
305
178
|
}
|
|
306
|
-
/**
|
|
307
|
-
* Check data consistency
|
|
308
|
-
*/
|
|
309
179
|
checkConsistency(data, issues, warnings) {
|
|
310
180
|
let score = 100;
|
|
311
|
-
// Check team stats consistency
|
|
312
|
-
if (data.homeTeam?.stats && data.awayTeam?.stats) {
|
|
313
|
-
const homeStats = data.homeTeam.stats;
|
|
314
|
-
const awayStats = data.awayTeam.stats;
|
|
315
|
-
// Check if matches played is consistent
|
|
316
|
-
if (homeStats.matchesPlayed !== awayStats.matchesPlayed &&
|
|
317
|
-
data.league?.name !== 'Champions League' &&
|
|
318
|
-
data.league?.name !== 'Europa League') {
|
|
319
|
-
// This might be OK for cup competitions
|
|
320
|
-
warnings.push({
|
|
321
|
-
field: 'stats',
|
|
322
|
-
severity: 'info',
|
|
323
|
-
message: 'Teams have different number of matches played',
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
// Check score consistency
|
|
328
181
|
if (data.score && data.status === 'finished') {
|
|
329
182
|
const totalGoals = data.score.home + data.score.away;
|
|
330
|
-
// Very high scoring match - verify
|
|
331
183
|
if (totalGoals > 10) {
|
|
332
184
|
score -= 5;
|
|
333
|
-
warnings.push({
|
|
334
|
-
field: 'score',
|
|
335
|
-
severity: 'info',
|
|
336
|
-
message: `High scoring match: ${totalGoals} total goals`,
|
|
337
|
-
});
|
|
185
|
+
warnings.push({ field: 'score', severity: 'info', message: `High scoring match: ${totalGoals} total goals` });
|
|
338
186
|
}
|
|
339
187
|
}
|
|
340
188
|
return Math.max(0, score);
|
|
341
189
|
}
|
|
342
|
-
/**
|
|
343
|
-
* Validate team stats
|
|
344
|
-
*/
|
|
345
|
-
validateTeamStats(stats, issues, warnings) {
|
|
346
|
-
// Check matches played
|
|
347
|
-
if (stats.matchesPlayed < 0) {
|
|
348
|
-
issues.push({
|
|
349
|
-
field: 'matchesPlayed',
|
|
350
|
-
severity: 'error',
|
|
351
|
-
message: 'Matches played cannot be negative',
|
|
352
|
-
value: stats.matchesPlayed,
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
// Check wins + draws + losses = matches played
|
|
356
|
-
const totalResults = stats.wins + stats.draws + stats.losses;
|
|
357
|
-
if (totalResults !== stats.matchesPlayed && stats.matchesPlayed > 0) {
|
|
358
|
-
warnings.push({
|
|
359
|
-
field: 'stats',
|
|
360
|
-
severity: 'warning',
|
|
361
|
-
message: `Wins+Draws+Losses (${totalResults}) != Matches Played (${stats.matchesPlayed})`,
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
// Check goal difference
|
|
365
|
-
const calculatedGD = stats.goalsFor - stats.goalsAgainst;
|
|
366
|
-
if (stats.goalDifference !== undefined && stats.goalDifference !== calculatedGD) {
|
|
367
|
-
warnings.push({
|
|
368
|
-
field: 'goalDifference',
|
|
369
|
-
severity: 'warning',
|
|
370
|
-
message: `Goal difference mismatch: ${stats.goalDifference} vs calculated ${calculatedGD}`,
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
// Check points
|
|
374
|
-
const calculatedPoints = (stats.wins * 3) + stats.draws;
|
|
375
|
-
if (stats.points !== undefined && stats.points !== calculatedPoints) {
|
|
376
|
-
warnings.push({
|
|
377
|
-
field: 'points',
|
|
378
|
-
severity: 'warning',
|
|
379
|
-
message: `Points mismatch: ${stats.points} vs calculated ${calculatedPoints}`,
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Generate improvement suggestions
|
|
385
|
-
*/
|
|
386
190
|
generateSuggestions(data, suggestions) {
|
|
387
|
-
if (!data.odds)
|
|
191
|
+
if (!data.odds)
|
|
388
192
|
suggestions.push('Add betting odds for value analysis');
|
|
389
|
-
|
|
390
|
-
if (!data.homeLineup && !data.awayLineup) {
|
|
193
|
+
if (!data.homeLineup || !data.awayLineup)
|
|
391
194
|
suggestions.push('Add team lineups for better predictions');
|
|
392
|
-
|
|
393
|
-
if (!data.homeTeam?.stats?.xG && !data.awayTeam?.stats?.xG) {
|
|
394
|
-
suggestions.push('Add expected goals (xG) data for advanced analysis');
|
|
395
|
-
}
|
|
396
|
-
if (!data.referee) {
|
|
195
|
+
if (!data.referee)
|
|
397
196
|
suggestions.push('Add referee information for bias analysis');
|
|
398
|
-
|
|
399
|
-
if (!data.venue) {
|
|
197
|
+
if (!data.venue)
|
|
400
198
|
suggestions.push('Add venue information for home advantage analysis');
|
|
401
|
-
}
|
|
402
199
|
}
|
|
403
|
-
/**
|
|
404
|
-
* Calculate freshness score from timestamp
|
|
405
|
-
*/
|
|
406
200
|
calculateFreshness(timestamp) {
|
|
407
201
|
const ts = timestamp instanceof Date ? timestamp.getTime() : timestamp;
|
|
408
202
|
const age = Date.now() - ts;
|
|
409
203
|
if (age < 60000)
|
|
410
|
-
return 100;
|
|
204
|
+
return 100;
|
|
411
205
|
if (age < 300000)
|
|
412
|
-
return 90;
|
|
206
|
+
return 90;
|
|
413
207
|
if (age < 600000)
|
|
414
|
-
return 80;
|
|
208
|
+
return 80;
|
|
415
209
|
if (age < 1800000)
|
|
416
|
-
return 60;
|
|
210
|
+
return 60;
|
|
417
211
|
if (age < 3600000)
|
|
418
|
-
return 40;
|
|
212
|
+
return 40;
|
|
419
213
|
return 20;
|
|
420
214
|
}
|
|
421
|
-
/**
|
|
422
|
-
* Calculate overall score from issues and warnings
|
|
423
|
-
*/
|
|
424
215
|
calculateScore(errors, warnings, maxFields) {
|
|
425
216
|
const errorWeight = 20;
|
|
426
217
|
const warningWeight = 5;
|
|
@@ -428,16 +219,10 @@ export class DataQualityValidator {
|
|
|
428
219
|
const warningDeduction = warnings.length * warningWeight;
|
|
429
220
|
return Math.max(0, 100 - errorDeduction - warningDeduction);
|
|
430
221
|
}
|
|
431
|
-
/**
|
|
432
|
-
* Quick validation - returns true if data passes basic checks
|
|
433
|
-
*/
|
|
434
222
|
isValid(data) {
|
|
435
223
|
const report = this.validateMatchData(data);
|
|
436
224
|
return report.issues.length === 0 && report.overall >= 70;
|
|
437
225
|
}
|
|
438
|
-
/**
|
|
439
|
-
* Get quality grade (A-F)
|
|
440
|
-
*/
|
|
441
226
|
getGrade(score) {
|
|
442
227
|
if (score >= 90)
|
|
443
228
|
return 'A';
|
|
@@ -450,11 +235,9 @@ export class DataQualityValidator {
|
|
|
450
235
|
return 'F';
|
|
451
236
|
}
|
|
452
237
|
}
|
|
453
|
-
// Singleton instance
|
|
454
238
|
let globalValidator = null;
|
|
455
239
|
export function getDataQualityValidator() {
|
|
456
|
-
if (!globalValidator)
|
|
240
|
+
if (!globalValidator)
|
|
457
241
|
globalValidator = new DataQualityValidator();
|
|
458
|
-
}
|
|
459
242
|
return globalValidator;
|
|
460
243
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* HISTORICAL ANALYZER
|
|
3
|
-
* Analyzes historical data for patterns and trends
|
|
4
3
|
*/
|
|
5
4
|
import type { Match, HeadToHead, HistoricalPattern, TeamHistoricalPerformance } from './types.js';
|
|
6
5
|
export interface HistoricalQuery {
|
|
@@ -30,62 +29,13 @@ export interface PerformanceMetrics {
|
|
|
30
29
|
bothTeamsScoreRate: number;
|
|
31
30
|
over25Rate: number;
|
|
32
31
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Historical Analyzer
|
|
35
|
-
* Analyzes past matches for patterns and predictive insights
|
|
36
|
-
*/
|
|
37
32
|
export declare class HistoricalAnalyzer {
|
|
38
33
|
private matchHistory;
|
|
39
|
-
private patterns;
|
|
40
|
-
constructor();
|
|
41
|
-
/**
|
|
42
|
-
* Analyze team performance over time
|
|
43
|
-
*/
|
|
44
34
|
analyzeTeamPerformance(teamId: string, query?: HistoricalQuery): TeamHistoricalPerformance;
|
|
45
|
-
/**
|
|
46
|
-
* Analyze head-to-head history
|
|
47
|
-
*/
|
|
48
35
|
analyzeHeadToHead(teamAId: string, teamBId: string, limit?: number): HeadToHead;
|
|
49
|
-
/**
|
|
50
|
-
* Analyze trends for a team
|
|
51
|
-
*/
|
|
52
36
|
analyzeTrends(teamId: string, windowSize?: number): TrendAnalysis;
|
|
53
|
-
/**
|
|
54
|
-
* Calculate performance metrics
|
|
55
|
-
*/
|
|
56
37
|
calculateMetrics(teamId: string, query?: HistoricalQuery): PerformanceMetrics;
|
|
57
|
-
/**
|
|
58
|
-
* Find betting patterns
|
|
59
|
-
*/
|
|
60
38
|
findBettingPatterns(query: HistoricalQuery): HistoricalPattern[];
|
|
61
|
-
/**
|
|
62
|
-
* Analyze referee bias
|
|
63
|
-
*/
|
|
64
|
-
analyzeRefereeBias(refereeName: string, teamId?: string): {
|
|
65
|
-
referee: string;
|
|
66
|
-
matches: number;
|
|
67
|
-
avgCards: number;
|
|
68
|
-
homeWinRate: number;
|
|
69
|
-
teamBias?: number;
|
|
70
|
-
};
|
|
71
|
-
/**
|
|
72
|
-
* Analyze weather impact
|
|
73
|
-
*/
|
|
74
|
-
analyzeWeatherImpact(condition: string, teamId?: string): {
|
|
75
|
-
condition: string;
|
|
76
|
-
matches: number;
|
|
77
|
-
avgGoals: number;
|
|
78
|
-
homeWinRate: number;
|
|
79
|
-
teamPerformance?: number;
|
|
80
|
-
};
|
|
81
|
-
/**
|
|
82
|
-
* Get similar matches
|
|
83
|
-
*/
|
|
84
|
-
findSimilarMatches(match: Match, limit?: number): Match[];
|
|
85
|
-
/**
|
|
86
|
-
* Calculate similarity between two matches
|
|
87
|
-
*/
|
|
88
|
-
private calculateSimilarity;
|
|
89
39
|
private getTeamMatches;
|
|
90
40
|
private getH2HMatches;
|
|
91
41
|
private isWin;
|
|
@@ -99,10 +49,6 @@ export declare class HistoricalAnalyzer {
|
|
|
99
49
|
private calculateProfit;
|
|
100
50
|
private queryMatches;
|
|
101
51
|
private createEmptyPerformance;
|
|
102
|
-
private loadHistoricalData;
|
|
103
|
-
/**
|
|
104
|
-
* Add match to history
|
|
105
|
-
*/
|
|
106
52
|
addMatch(match: Match): void;
|
|
107
53
|
}
|
|
108
54
|
export declare function getHistoricalAnalyzer(): HistoricalAnalyzer;
|