@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.
@@ -1,41 +1,26 @@
1
1
  /**
2
2
  * DATA QUALITY VALIDATOR
3
- * Validates and scores data quality for football matches
4
3
  */
5
- import { logger } from '../../../utils.js';
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
- const report = {
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: Math.round((completeness + accuracy + freshness + consistency) / 4),
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: 'lineups', weight: 3 },
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 = new Date();
239
- const oneYearAgo = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate());
240
- const oneYearFromNow = new Date(now.getFullYear() + 1, now.getMonth(), now.getDate());
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
- // For scheduled matches, freshness is less critical
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
- // Match finished within last hour
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; // < 1 minute
204
+ return 100;
411
205
  if (age < 300000)
412
- return 90; // < 5 minutes
206
+ return 90;
413
207
  if (age < 600000)
414
- return 80; // < 10 minutes
208
+ return 80;
415
209
  if (age < 1800000)
416
- return 60; // < 30 minutes
210
+ return 60;
417
211
  if (age < 3600000)
418
- return 40; // < 1 hour
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;