@gotza02/sequential-thinking 10000.1.2 → 10000.1.3
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/README.md +39 -249
- package/dist/dashboard/server.d.ts +2 -45
- package/dist/dashboard/server.js +64 -250
- package/dist/index.js +4 -1
- package/dist/tools/sports/core/cache.d.ts +8 -19
- package/dist/tools/sports/core/cache.js +38 -95
- package/dist/tools/sports/core/constants.d.ts +4 -63
- package/dist/tools/sports/core/constants.js +11 -86
- package/dist/tools/sports/core/types.d.ts +1 -40
- package/package.json +1 -1
- package/dist/tools/sports/core/alert-manager.d.ts +0 -96
- package/dist/tools/sports/core/alert-manager.js +0 -319
- package/dist/tools/sports/core/circuit-breaker.d.ts +0 -40
- package/dist/tools/sports/core/circuit-breaker.js +0 -99
- package/dist/tools/sports/core/data-quality.d.ts +0 -36
- package/dist/tools/sports/core/data-quality.js +0 -243
- package/dist/tools/sports/core/historical-analyzer.d.ts +0 -54
- package/dist/tools/sports/core/historical-analyzer.js +0 -261
- package/dist/tools/sports/core/index.d.ts +0 -13
- package/dist/tools/sports/core/index.js +0 -16
- package/dist/tools/sports/core/ml-prediction.d.ts +0 -76
- package/dist/tools/sports/core/ml-prediction.js +0 -260
- package/dist/tools/sports/core/realtime-manager.d.ts +0 -51
- package/dist/tools/sports/core/realtime-manager.js +0 -222
- package/dist/tools/sports/core/retry.d.ts +0 -29
- package/dist/tools/sports/core/retry.js +0 -77
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DATA QUALITY VALIDATOR
|
|
3
|
-
*/
|
|
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
|
-
};
|
|
11
|
-
export class DataQualityValidator {
|
|
12
|
-
validateMatchData(data) {
|
|
13
|
-
const issues = [];
|
|
14
|
-
const warnings = [];
|
|
15
|
-
const suggestions = [];
|
|
16
|
-
const completeness = this.checkCompleteness(data, issues);
|
|
17
|
-
const accuracy = this.checkAccuracy(data, issues, warnings);
|
|
18
|
-
const freshness = this.checkFreshness(data, warnings);
|
|
19
|
-
const consistency = this.checkConsistency(data, issues, warnings);
|
|
20
|
-
this.generateSuggestions(data, suggestions);
|
|
21
|
-
const overall = Math.round((completeness + accuracy + freshness + consistency) / 4);
|
|
22
|
-
logger.debug(`[DataQuality] Match ${data.id}: Overall=${overall}`);
|
|
23
|
-
return {
|
|
24
|
-
overall,
|
|
25
|
-
completeness,
|
|
26
|
-
accuracy,
|
|
27
|
-
freshness,
|
|
28
|
-
consistency,
|
|
29
|
-
issues: issues.filter(i => i.severity === 'error'),
|
|
30
|
-
warnings: [...issues.filter(i => i.severity === 'warning'), ...warnings],
|
|
31
|
-
suggestions,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
validateTeamData(data) {
|
|
35
|
-
const issues = [];
|
|
36
|
-
const warnings = [];
|
|
37
|
-
if (!data.name) {
|
|
38
|
-
issues.push({ field: 'name', severity: 'error', message: 'Team name is required' });
|
|
39
|
-
}
|
|
40
|
-
if (!data.country) {
|
|
41
|
-
warnings.push({ field: 'country', severity: 'warning', message: 'Team country is missing' });
|
|
42
|
-
}
|
|
43
|
-
const completeness = this.calculateScore(issues, warnings, 3);
|
|
44
|
-
return {
|
|
45
|
-
overall: completeness,
|
|
46
|
-
completeness,
|
|
47
|
-
accuracy: 100,
|
|
48
|
-
freshness: 100,
|
|
49
|
-
consistency: 100,
|
|
50
|
-
issues: issues.filter(i => i.severity === 'error'),
|
|
51
|
-
warnings: [...issues.filter(i => i.severity === 'warning'), ...warnings],
|
|
52
|
-
suggestions: [],
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
validateOddsData(odds) {
|
|
56
|
-
const issues = [];
|
|
57
|
-
const warnings = [];
|
|
58
|
-
if (!odds.homeWin || odds.homeWin <= 1) {
|
|
59
|
-
issues.push({ field: 'homeWin', severity: 'error', message: `Invalid home win odds: ${odds.homeWin}`, value: odds.homeWin });
|
|
60
|
-
}
|
|
61
|
-
if (!odds.draw || odds.draw <= 1) {
|
|
62
|
-
issues.push({ field: 'draw', severity: 'error', message: `Invalid draw odds: ${odds.draw}`, value: odds.draw });
|
|
63
|
-
}
|
|
64
|
-
if (!odds.awayWin || odds.awayWin <= 1) {
|
|
65
|
-
issues.push({ field: 'awayWin', severity: 'error', message: `Invalid away win odds: ${odds.awayWin}`, value: odds.awayWin });
|
|
66
|
-
}
|
|
67
|
-
const impliedProb = (1 / odds.homeWin) + (1 / odds.draw) + (1 / odds.awayWin);
|
|
68
|
-
if (impliedProb < 1 || impliedProb > 1.5) {
|
|
69
|
-
warnings.push({ field: 'odds', severity: 'warning', message: `Unusual implied probability: ${(impliedProb * 100).toFixed(1)}%` });
|
|
70
|
-
}
|
|
71
|
-
if (impliedProb < 0.95) {
|
|
72
|
-
issues.push({ field: 'odds', severity: 'error', message: 'Possible arbitrage opportunity detected', value: impliedProb });
|
|
73
|
-
}
|
|
74
|
-
const completeness = this.calculateScore(issues, warnings, 3);
|
|
75
|
-
return {
|
|
76
|
-
overall: completeness,
|
|
77
|
-
completeness,
|
|
78
|
-
accuracy: completeness,
|
|
79
|
-
freshness: odds.timestamp ? this.calculateFreshness(odds.timestamp) : 50,
|
|
80
|
-
consistency: 100,
|
|
81
|
-
issues: issues.filter(i => i.severity === 'error'),
|
|
82
|
-
warnings: [...issues.filter(i => i.severity === 'warning'), ...warnings],
|
|
83
|
-
suggestions: [],
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
checkCompleteness(data, issues) {
|
|
87
|
-
const requiredFields = [
|
|
88
|
-
{ key: 'id', weight: 15 },
|
|
89
|
-
{ key: 'homeTeam', weight: 15 },
|
|
90
|
-
{ key: 'awayTeam', weight: 15 },
|
|
91
|
-
{ key: 'date', weight: 15 },
|
|
92
|
-
{ key: 'status', weight: 10 },
|
|
93
|
-
{ key: 'league', weight: 10 },
|
|
94
|
-
];
|
|
95
|
-
const optionalFields = [
|
|
96
|
-
{ key: 'score', weight: 5 },
|
|
97
|
-
{ key: 'odds', weight: 5 },
|
|
98
|
-
{ key: 'homeLineup', weight: 1.5 },
|
|
99
|
-
{ key: 'awayLineup', weight: 1.5 },
|
|
100
|
-
{ key: 'events', weight: 3 },
|
|
101
|
-
{ key: 'venue', weight: 2 },
|
|
102
|
-
{ key: 'referee', weight: 2 },
|
|
103
|
-
];
|
|
104
|
-
let score = 0;
|
|
105
|
-
requiredFields.forEach(({ key, weight }) => {
|
|
106
|
-
const value = data[key];
|
|
107
|
-
if (value !== undefined && value !== null) {
|
|
108
|
-
score += weight;
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
issues.push({ field: key, severity: 'error', message: `Required field '${key}' is missing` });
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
optionalFields.forEach(({ key, weight }) => {
|
|
115
|
-
const value = data[key];
|
|
116
|
-
if (value !== undefined && value !== null)
|
|
117
|
-
score += weight;
|
|
118
|
-
});
|
|
119
|
-
return score;
|
|
120
|
-
}
|
|
121
|
-
checkAccuracy(data, issues, warnings) {
|
|
122
|
-
let score = 100;
|
|
123
|
-
if (data.score && data.status === 'scheduled') {
|
|
124
|
-
score -= 15;
|
|
125
|
-
warnings.push({ field: 'score', severity: 'warning', message: 'Match has score but status is "scheduled"' });
|
|
126
|
-
}
|
|
127
|
-
if (data.score) {
|
|
128
|
-
if (data.score.home < 0 || data.score.away < 0) {
|
|
129
|
-
score -= 20;
|
|
130
|
-
issues.push({ field: 'score', severity: 'error', message: 'Negative score detected', value: data.score });
|
|
131
|
-
}
|
|
132
|
-
if (data.score.home > 15 || data.score.away > 15) {
|
|
133
|
-
score -= 10;
|
|
134
|
-
warnings.push({ field: 'score', severity: 'warning', message: 'Unusually high score detected', value: `${data.score.home}-${data.score.away}` });
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (data.date) {
|
|
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;
|
|
142
|
-
if (matchDate < oneYearAgo || matchDate > oneYearFromNow) {
|
|
143
|
-
score -= 10;
|
|
144
|
-
warnings.push({ field: 'date', severity: 'warning', message: 'Match date is outside expected range', value: data.date });
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (data.homeTeam && data.awayTeam) {
|
|
148
|
-
if (data.homeTeam.name === data.awayTeam.name) {
|
|
149
|
-
score -= 20;
|
|
150
|
-
issues.push({ field: 'teams', severity: 'error', message: 'Home and away teams have the same name', value: data.homeTeam.name });
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return Math.max(0, score);
|
|
154
|
-
}
|
|
155
|
-
checkFreshness(data, warnings) {
|
|
156
|
-
if (!data.date)
|
|
157
|
-
return 50;
|
|
158
|
-
const matchDate = new Date(data.date).getTime();
|
|
159
|
-
const now = Date.now();
|
|
160
|
-
const age = now - matchDate;
|
|
161
|
-
if (data.status === 'live') {
|
|
162
|
-
if (age > 2 * 60 * 1000) {
|
|
163
|
-
warnings.push({ field: 'freshness', severity: 'warning', message: 'Live match data is stale (>2 minutes old)' });
|
|
164
|
-
return 50;
|
|
165
|
-
}
|
|
166
|
-
return 100;
|
|
167
|
-
}
|
|
168
|
-
if (data.status === 'scheduled')
|
|
169
|
-
return 100;
|
|
170
|
-
if (data.status === 'finished') {
|
|
171
|
-
if (age < 60 * 60 * 1000)
|
|
172
|
-
return 100;
|
|
173
|
-
if (age < 24 * 60 * 60 * 1000)
|
|
174
|
-
return 80;
|
|
175
|
-
return 60;
|
|
176
|
-
}
|
|
177
|
-
return 70;
|
|
178
|
-
}
|
|
179
|
-
checkConsistency(data, issues, warnings) {
|
|
180
|
-
let score = 100;
|
|
181
|
-
if (data.score && data.status === 'finished') {
|
|
182
|
-
const totalGoals = data.score.home + data.score.away;
|
|
183
|
-
if (totalGoals > 10) {
|
|
184
|
-
score -= 5;
|
|
185
|
-
warnings.push({ field: 'score', severity: 'info', message: `High scoring match: ${totalGoals} total goals` });
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
return Math.max(0, score);
|
|
189
|
-
}
|
|
190
|
-
generateSuggestions(data, suggestions) {
|
|
191
|
-
if (!data.odds)
|
|
192
|
-
suggestions.push('Add betting odds for value analysis');
|
|
193
|
-
if (!data.homeLineup || !data.awayLineup)
|
|
194
|
-
suggestions.push('Add team lineups for better predictions');
|
|
195
|
-
if (!data.referee)
|
|
196
|
-
suggestions.push('Add referee information for bias analysis');
|
|
197
|
-
if (!data.venue)
|
|
198
|
-
suggestions.push('Add venue information for home advantage analysis');
|
|
199
|
-
}
|
|
200
|
-
calculateFreshness(timestamp) {
|
|
201
|
-
const ts = timestamp instanceof Date ? timestamp.getTime() : timestamp;
|
|
202
|
-
const age = Date.now() - ts;
|
|
203
|
-
if (age < 60000)
|
|
204
|
-
return 100;
|
|
205
|
-
if (age < 300000)
|
|
206
|
-
return 90;
|
|
207
|
-
if (age < 600000)
|
|
208
|
-
return 80;
|
|
209
|
-
if (age < 1800000)
|
|
210
|
-
return 60;
|
|
211
|
-
if (age < 3600000)
|
|
212
|
-
return 40;
|
|
213
|
-
return 20;
|
|
214
|
-
}
|
|
215
|
-
calculateScore(errors, warnings, maxFields) {
|
|
216
|
-
const errorWeight = 20;
|
|
217
|
-
const warningWeight = 5;
|
|
218
|
-
const errorDeduction = errors.filter(i => i.severity === 'error').length * errorWeight;
|
|
219
|
-
const warningDeduction = warnings.length * warningWeight;
|
|
220
|
-
return Math.max(0, 100 - errorDeduction - warningDeduction);
|
|
221
|
-
}
|
|
222
|
-
isValid(data) {
|
|
223
|
-
const report = this.validateMatchData(data);
|
|
224
|
-
return report.issues.length === 0 && report.overall >= 70;
|
|
225
|
-
}
|
|
226
|
-
getGrade(score) {
|
|
227
|
-
if (score >= 90)
|
|
228
|
-
return 'A';
|
|
229
|
-
if (score >= 80)
|
|
230
|
-
return 'B';
|
|
231
|
-
if (score >= 70)
|
|
232
|
-
return 'C';
|
|
233
|
-
if (score >= 60)
|
|
234
|
-
return 'D';
|
|
235
|
-
return 'F';
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
let globalValidator = null;
|
|
239
|
-
export function getDataQualityValidator() {
|
|
240
|
-
if (!globalValidator)
|
|
241
|
-
globalValidator = new DataQualityValidator();
|
|
242
|
-
return globalValidator;
|
|
243
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HISTORICAL ANALYZER
|
|
3
|
-
*/
|
|
4
|
-
import type { Match, HeadToHead, HistoricalPattern, TeamHistoricalPerformance } from './types.js';
|
|
5
|
-
export interface HistoricalQuery {
|
|
6
|
-
team?: string;
|
|
7
|
-
league?: string;
|
|
8
|
-
dateFrom?: Date;
|
|
9
|
-
dateTo?: Date;
|
|
10
|
-
opponent?: string;
|
|
11
|
-
venue?: 'home' | 'away' | 'neutral';
|
|
12
|
-
minOdds?: number;
|
|
13
|
-
maxOdds?: number;
|
|
14
|
-
}
|
|
15
|
-
export interface TrendAnalysis {
|
|
16
|
-
direction: 'up' | 'down' | 'stable';
|
|
17
|
-
strength: number;
|
|
18
|
-
confidence: number;
|
|
19
|
-
dataPoints: number;
|
|
20
|
-
description: string;
|
|
21
|
-
}
|
|
22
|
-
export interface PerformanceMetrics {
|
|
23
|
-
winRate: number;
|
|
24
|
-
drawRate: number;
|
|
25
|
-
lossRate: number;
|
|
26
|
-
avgGoalsFor: number;
|
|
27
|
-
avgGoalsAgainst: number;
|
|
28
|
-
cleanSheetRate: number;
|
|
29
|
-
bothTeamsScoreRate: number;
|
|
30
|
-
over25Rate: number;
|
|
31
|
-
}
|
|
32
|
-
export declare class HistoricalAnalyzer {
|
|
33
|
-
private matchHistory;
|
|
34
|
-
analyzeTeamPerformance(teamId: string, query?: HistoricalQuery): TeamHistoricalPerformance;
|
|
35
|
-
analyzeHeadToHead(teamAId: string, teamBId: string, limit?: number): HeadToHead;
|
|
36
|
-
analyzeTrends(teamId: string, windowSize?: number): TrendAnalysis;
|
|
37
|
-
calculateMetrics(teamId: string, query?: HistoricalQuery): PerformanceMetrics;
|
|
38
|
-
findBettingPatterns(query: HistoricalQuery): HistoricalPattern[];
|
|
39
|
-
private getTeamMatches;
|
|
40
|
-
private getH2HMatches;
|
|
41
|
-
private isWin;
|
|
42
|
-
private isDraw;
|
|
43
|
-
private isLoss;
|
|
44
|
-
private getGoalsFor;
|
|
45
|
-
private getGoalsAgainst;
|
|
46
|
-
private getMatchPoints;
|
|
47
|
-
private calculateHomeAdvantage;
|
|
48
|
-
private findPatterns;
|
|
49
|
-
private calculateProfit;
|
|
50
|
-
private queryMatches;
|
|
51
|
-
private createEmptyPerformance;
|
|
52
|
-
addMatch(match: Match): void;
|
|
53
|
-
}
|
|
54
|
-
export declare function getHistoricalAnalyzer(): HistoricalAnalyzer;
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HISTORICAL ANALYZER
|
|
3
|
-
*/
|
|
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
|
-
};
|
|
11
|
-
export class HistoricalAnalyzer {
|
|
12
|
-
matchHistory = [];
|
|
13
|
-
analyzeTeamPerformance(teamId, query) {
|
|
14
|
-
const matches = this.getTeamMatches(teamId, query);
|
|
15
|
-
if (matches.length === 0)
|
|
16
|
-
return this.createEmptyPerformance(teamId);
|
|
17
|
-
const wins = matches.filter(m => this.isWin(m, teamId)).length;
|
|
18
|
-
const draws = matches.filter(m => this.isDraw(m)).length;
|
|
19
|
-
const losses = matches.filter(m => this.isLoss(m, teamId)).length;
|
|
20
|
-
const totalGoalsFor = matches.reduce((sum, m) => sum + this.getGoalsFor(m, teamId), 0);
|
|
21
|
-
const totalGoalsAgainst = matches.reduce((sum, m) => sum + this.getGoalsAgainst(m, teamId), 0);
|
|
22
|
-
return {
|
|
23
|
-
team: matches[0].homeTeam.id === teamId ? matches[0].homeTeam : matches[0].awayTeam,
|
|
24
|
-
totalMatches: matches.length,
|
|
25
|
-
wins, draws, losses,
|
|
26
|
-
avgGoalsFor: totalGoalsFor / matches.length,
|
|
27
|
-
avgGoalsAgainst: totalGoalsAgainst / matches.length,
|
|
28
|
-
homeAdvantage: this.calculateHomeAdvantage(teamId, matches),
|
|
29
|
-
patterns: this.findPatterns(teamId, matches),
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
analyzeHeadToHead(teamAId, teamBId, limit = 10) {
|
|
33
|
-
const matches = this.getH2HMatches(teamAId, teamBId, limit);
|
|
34
|
-
let homeWins = 0, awayWins = 0, draws = 0, homeGoals = 0, awayGoals = 0;
|
|
35
|
-
matches.forEach(match => {
|
|
36
|
-
if (!match.score)
|
|
37
|
-
return;
|
|
38
|
-
const isTeamAHome = match.homeTeam.id === teamAId;
|
|
39
|
-
if (match.score.home > match.score.away) {
|
|
40
|
-
if (isTeamAHome)
|
|
41
|
-
homeWins++;
|
|
42
|
-
else
|
|
43
|
-
awayWins++;
|
|
44
|
-
}
|
|
45
|
-
else if (match.score.home < match.score.away) {
|
|
46
|
-
if (isTeamAHome)
|
|
47
|
-
awayWins++;
|
|
48
|
-
else
|
|
49
|
-
homeWins++;
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
draws++;
|
|
53
|
-
}
|
|
54
|
-
homeGoals += isTeamAHome ? match.score.home : match.score.away;
|
|
55
|
-
awayGoals += isTeamAHome ? match.score.away : match.score.home;
|
|
56
|
-
});
|
|
57
|
-
return {
|
|
58
|
-
homeTeam: matches[0]?.homeTeam || { id: teamAId, name: '', country: '' },
|
|
59
|
-
awayTeam: matches[0]?.awayTeam || { id: teamBId, name: '', country: '' },
|
|
60
|
-
totalMatches: matches.length,
|
|
61
|
-
homeWins, draws, awayWins,
|
|
62
|
-
homeGoals, awayGoals,
|
|
63
|
-
recentMatches: matches.slice(0, 5),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
analyzeTrends(teamId, windowSize = 5) {
|
|
67
|
-
const matches = this.getTeamMatches(teamId).slice(0, windowSize);
|
|
68
|
-
if (matches.length < 3) {
|
|
69
|
-
return { direction: 'stable', strength: 0, confidence: 0, dataPoints: matches.length, description: 'Insufficient data' };
|
|
70
|
-
}
|
|
71
|
-
const recent = matches.slice(0, Math.ceil(windowSize / 2)).map(m => this.getMatchPoints(m, teamId));
|
|
72
|
-
const older = matches.slice(Math.ceil(windowSize / 2)).map(m => this.getMatchPoints(m, teamId));
|
|
73
|
-
const recentAvg = recent.reduce((a, b) => a + b, 0) / recent.length;
|
|
74
|
-
const olderAvg = older.reduce((a, b) => a + b, 0) / older.length;
|
|
75
|
-
const difference = recentAvg - olderAvg;
|
|
76
|
-
const strength = Math.min(1, Math.abs(difference) / 2);
|
|
77
|
-
const direction = difference > 0.5 ? 'up' : difference < -0.5 ? 'down' : 'stable';
|
|
78
|
-
return {
|
|
79
|
-
direction, strength,
|
|
80
|
-
confidence: Math.min(100, matches.length * 10),
|
|
81
|
-
dataPoints: matches.length,
|
|
82
|
-
description: direction === 'up'
|
|
83
|
-
? `Improving: ${recentAvg.toFixed(2)} pts/game vs ${olderAvg.toFixed(2)} before`
|
|
84
|
-
: direction === 'down'
|
|
85
|
-
? `Declining: ${recentAvg.toFixed(2)} pts/game vs ${olderAvg.toFixed(2)} before`
|
|
86
|
-
: `Stable: ${recentAvg.toFixed(2)} pts/game`,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
calculateMetrics(teamId, query) {
|
|
90
|
-
const matches = this.getTeamMatches(teamId, query);
|
|
91
|
-
if (matches.length === 0) {
|
|
92
|
-
return { winRate: 0, drawRate: 0, lossRate: 0, avgGoalsFor: 0, avgGoalsAgainst: 0, cleanSheetRate: 0, bothTeamsScoreRate: 0, over25Rate: 0 };
|
|
93
|
-
}
|
|
94
|
-
const wins = matches.filter(m => this.isWin(m, teamId)).length;
|
|
95
|
-
const draws = matches.filter(m => this.isDraw(m)).length;
|
|
96
|
-
const losses = matches.filter(m => this.isLoss(m, teamId)).length;
|
|
97
|
-
const totalGoalsFor = matches.reduce((sum, m) => sum + this.getGoalsFor(m, teamId), 0);
|
|
98
|
-
const totalGoalsAgainst = matches.reduce((sum, m) => sum + this.getGoalsAgainst(m, teamId), 0);
|
|
99
|
-
const cleanSheets = matches.filter(m => this.getGoalsAgainst(m, teamId) === 0).length;
|
|
100
|
-
const btts = matches.filter(m => this.getGoalsFor(m, teamId) > 0 && this.getGoalsAgainst(m, teamId) > 0).length;
|
|
101
|
-
const over25 = matches.filter(m => this.getGoalsFor(m, teamId) + this.getGoalsAgainst(m, teamId) > 2.5).length;
|
|
102
|
-
return {
|
|
103
|
-
winRate: wins / matches.length,
|
|
104
|
-
drawRate: draws / matches.length,
|
|
105
|
-
lossRate: losses / matches.length,
|
|
106
|
-
avgGoalsFor: totalGoalsFor / matches.length,
|
|
107
|
-
avgGoalsAgainst: totalGoalsAgainst / matches.length,
|
|
108
|
-
cleanSheetRate: cleanSheets / matches.length,
|
|
109
|
-
bothTeamsScoreRate: btts / matches.length,
|
|
110
|
-
over25Rate: over25 / matches.length,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
findBettingPatterns(query) {
|
|
114
|
-
const patterns = [];
|
|
115
|
-
const matches = this.queryMatches(query);
|
|
116
|
-
const homeFavorites = matches.filter(m => m.odds && m.odds.homeWin < 1.5 && m.score && m.score.home > m.score.away);
|
|
117
|
-
if (homeFavorites.length >= 5) {
|
|
118
|
-
patterns.push({
|
|
119
|
-
pattern: 'Home favorites win',
|
|
120
|
-
occurrence: homeFavorites.length,
|
|
121
|
-
successRate: homeFavorites.length / matches.filter(m => m.odds?.homeWin < 1.5).length,
|
|
122
|
-
avgOdds: homeFavorites.reduce((sum, m) => sum + (m.odds?.homeWin || 0), 0) / homeFavorites.length,
|
|
123
|
-
profit: this.calculateProfit(homeFavorites, 'home'),
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
const lowScoring = matches.filter(m => (m.score?.home || 0) + (m.score?.away || 0) < 2.5);
|
|
127
|
-
if (lowScoring.length >= 10) {
|
|
128
|
-
patterns.push({
|
|
129
|
-
pattern: 'Under 2.5 goals',
|
|
130
|
-
occurrence: lowScoring.length,
|
|
131
|
-
successRate: lowScoring.length / matches.length,
|
|
132
|
-
avgOdds: 1.8,
|
|
133
|
-
profit: (lowScoring.length * 0.8 - (matches.length - lowScoring.length)) * 10,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
const bttsMatches = matches.filter(m => (m.score?.home || 0) > 0 && (m.score?.away || 0) > 0);
|
|
137
|
-
if (bttsMatches.length >= 10) {
|
|
138
|
-
patterns.push({
|
|
139
|
-
pattern: 'Both teams score',
|
|
140
|
-
occurrence: bttsMatches.length,
|
|
141
|
-
successRate: bttsMatches.length / matches.length,
|
|
142
|
-
avgOdds: 1.9,
|
|
143
|
-
profit: (bttsMatches.length * 0.9 - (matches.length - bttsMatches.length)) * 10,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
return patterns.sort((a, b) => b.profit - a.profit);
|
|
147
|
-
}
|
|
148
|
-
getTeamMatches(teamId, query) {
|
|
149
|
-
let matches = this.matchHistory.filter(m => (m.homeTeam.id === teamId || m.awayTeam.id === teamId) && m.status === 'finished');
|
|
150
|
-
if (query?.dateFrom)
|
|
151
|
-
matches = matches.filter(m => m.date >= query.dateFrom);
|
|
152
|
-
if (query?.dateTo)
|
|
153
|
-
matches = matches.filter(m => m.date <= query.dateTo);
|
|
154
|
-
if (query?.venue) {
|
|
155
|
-
matches = matches.filter(m => query.venue === 'home' ? m.homeTeam.id === teamId : query.venue === 'away' ? m.awayTeam.id === teamId : true);
|
|
156
|
-
}
|
|
157
|
-
return matches.sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
158
|
-
}
|
|
159
|
-
getH2HMatches(teamAId, teamBId, limit) {
|
|
160
|
-
return this.matchHistory
|
|
161
|
-
.filter(m => ((m.homeTeam.id === teamAId && m.awayTeam.id === teamBId) || (m.homeTeam.id === teamBId && m.awayTeam.id === teamAId)) && m.status === 'finished')
|
|
162
|
-
.sort((a, b) => b.date.getTime() - a.date.getTime())
|
|
163
|
-
.slice(0, limit);
|
|
164
|
-
}
|
|
165
|
-
isWin(match, teamId) {
|
|
166
|
-
if (!match.score)
|
|
167
|
-
return false;
|
|
168
|
-
const isHome = match.homeTeam.id === teamId;
|
|
169
|
-
return isHome ? match.score.home > match.score.away : match.score.away > match.score.home;
|
|
170
|
-
}
|
|
171
|
-
isDraw(match) {
|
|
172
|
-
return match.score ? match.score.home === match.score.away : false;
|
|
173
|
-
}
|
|
174
|
-
isLoss(match, teamId) {
|
|
175
|
-
if (!match.score)
|
|
176
|
-
return false;
|
|
177
|
-
const isHome = match.homeTeam.id === teamId;
|
|
178
|
-
return isHome ? match.score.home < match.score.away : match.score.away < match.score.home;
|
|
179
|
-
}
|
|
180
|
-
getGoalsFor(match, teamId) {
|
|
181
|
-
return match.score ? (match.homeTeam.id === teamId ? match.score.home : match.score.away) : 0;
|
|
182
|
-
}
|
|
183
|
-
getGoalsAgainst(match, teamId) {
|
|
184
|
-
return match.score ? (match.homeTeam.id === teamId ? match.score.away : match.score.home) : 0;
|
|
185
|
-
}
|
|
186
|
-
getMatchPoints(match, teamId) {
|
|
187
|
-
if (this.isWin(match, teamId))
|
|
188
|
-
return 3;
|
|
189
|
-
if (this.isDraw(match))
|
|
190
|
-
return 1;
|
|
191
|
-
return 0;
|
|
192
|
-
}
|
|
193
|
-
calculateHomeAdvantage(teamId, matches) {
|
|
194
|
-
const homeMatches = matches.filter(m => m.homeTeam.id === teamId);
|
|
195
|
-
const awayMatches = matches.filter(m => m.awayTeam.id === teamId);
|
|
196
|
-
if (homeMatches.length === 0 || awayMatches.length === 0)
|
|
197
|
-
return 0;
|
|
198
|
-
const homeWinRate = homeMatches.filter(m => this.isWin(m, teamId)).length / homeMatches.length;
|
|
199
|
-
const awayWinRate = awayMatches.filter(m => this.isWin(m, teamId)).length / awayMatches.length;
|
|
200
|
-
return homeWinRate - awayWinRate;
|
|
201
|
-
}
|
|
202
|
-
findPatterns(teamId, matches) {
|
|
203
|
-
const patterns = [];
|
|
204
|
-
const homeMatches = matches.filter(m => m.homeTeam.id === teamId);
|
|
205
|
-
const homeWins = homeMatches.filter(m => this.isWin(m, teamId)).length;
|
|
206
|
-
if (homeMatches.length >= 5 && homeWins / homeMatches.length > 0.6) {
|
|
207
|
-
patterns.push({ pattern: 'Strong home record', occurrence: homeWins, successRate: homeWins / homeMatches.length, avgOdds: 1.8, profit: 0 });
|
|
208
|
-
}
|
|
209
|
-
const highScoring = matches.filter(m => this.getGoalsFor(m, teamId) + this.getGoalsAgainst(m, teamId) > 2.5);
|
|
210
|
-
if (highScoring.length / matches.length > 0.6) {
|
|
211
|
-
patterns.push({ pattern: 'High scoring matches', occurrence: highScoring.length, successRate: highScoring.length / matches.length, avgOdds: 1.9, profit: 0 });
|
|
212
|
-
}
|
|
213
|
-
return patterns;
|
|
214
|
-
}
|
|
215
|
-
calculateProfit(matches, betOn) {
|
|
216
|
-
let profit = 0;
|
|
217
|
-
matches.forEach(m => {
|
|
218
|
-
if (!m.odds || !m.score)
|
|
219
|
-
return;
|
|
220
|
-
const won = betOn === 'home' && m.score.home > m.score.away || betOn === 'away' && m.score.away > m.score.home || betOn === 'draw' && m.score.home === m.score.away;
|
|
221
|
-
if (won) {
|
|
222
|
-
profit += (m.odds[betOn === 'home' ? 'homeWin' : betOn === 'away' ? 'awayWin' : 'draw'] - 1) * 10;
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
profit -= 10;
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
return profit;
|
|
229
|
-
}
|
|
230
|
-
queryMatches(query) {
|
|
231
|
-
let matches = this.matchHistory.filter(m => m.status === 'finished');
|
|
232
|
-
if (query.team)
|
|
233
|
-
matches = matches.filter(m => m.homeTeam.name === query.team || m.awayTeam.name === query.team);
|
|
234
|
-
if (query.league)
|
|
235
|
-
matches = matches.filter(m => m.league.name === query.league);
|
|
236
|
-
if (query.dateFrom)
|
|
237
|
-
matches = matches.filter(m => m.date >= query.dateFrom);
|
|
238
|
-
if (query.dateTo)
|
|
239
|
-
matches = matches.filter(m => m.date <= query.dateTo);
|
|
240
|
-
return matches;
|
|
241
|
-
}
|
|
242
|
-
createEmptyPerformance(teamId) {
|
|
243
|
-
return {
|
|
244
|
-
team: { id: teamId, name: '', country: '' },
|
|
245
|
-
totalMatches: 0, wins: 0, draws: 0, losses: 0,
|
|
246
|
-
avgGoalsFor: 0, avgGoalsAgainst: 0,
|
|
247
|
-
homeAdvantage: 0, patterns: [],
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
addMatch(match) {
|
|
251
|
-
this.matchHistory.push(match);
|
|
252
|
-
if (this.matchHistory.length > 1000)
|
|
253
|
-
this.matchHistory.shift();
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
let globalHistoricalAnalyzer = null;
|
|
257
|
-
export function getHistoricalAnalyzer() {
|
|
258
|
-
if (!globalHistoricalAnalyzer)
|
|
259
|
-
globalHistoricalAnalyzer = new HistoricalAnalyzer();
|
|
260
|
-
return globalHistoricalAnalyzer;
|
|
261
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SPORTS MODULE CORE - Exports (FIXED)
|
|
3
|
-
*/
|
|
4
|
-
export * from './types.js';
|
|
5
|
-
export { CacheService, getGlobalCache, resetGlobalCache } from './cache.js';
|
|
6
|
-
export { CircuitBreaker, CircuitBreakerOpenError, getCircuitBreaker, getAllCircuitBreakerStatus } from './circuit-breaker.js';
|
|
7
|
-
export { withRetry, withRetryAndCircuitBreaker, createRetryable, DEFAULT_RETRY_CONFIG, AGGRESSIVE_RETRY_CONFIG, LIVE_DATA_RETRY_CONFIG } from './retry.js';
|
|
8
|
-
export { RealtimeDataManager, getRealtimeManager, resetRealtimeManager } from './realtime-manager.js';
|
|
9
|
-
export { AlertManager, getAlertManager, resetAlertManager } from './alert-manager.js';
|
|
10
|
-
export { DataQualityValidator, getDataQualityValidator } from './data-quality.js';
|
|
11
|
-
export { MLPredictionEngine, getPredictionEngine } from './ml-prediction.js';
|
|
12
|
-
export { HistoricalAnalyzer, getHistoricalAnalyzer } from './historical-analyzer.js';
|
|
13
|
-
export { API_CONFIG, CACHE_CONFIG, SCRAPER_CONFIG, LEAGUES, QUERY_TYPES, BETTING, POSITIONS, MATCH_STATUS, ERRORS, ALERT_CONFIG, REALTIME_CONFIG, ML_CONFIG, MODULE_INFO } from './constants.js';
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SPORTS MODULE CORE - Exports (FIXED)
|
|
3
|
-
*/
|
|
4
|
-
// Types
|
|
5
|
-
export * from './types.js';
|
|
6
|
-
// Core classes
|
|
7
|
-
export { CacheService, getGlobalCache, resetGlobalCache } from './cache.js';
|
|
8
|
-
export { CircuitBreaker, CircuitBreakerOpenError, getCircuitBreaker, getAllCircuitBreakerStatus } from './circuit-breaker.js';
|
|
9
|
-
export { withRetry, withRetryAndCircuitBreaker, createRetryable, DEFAULT_RETRY_CONFIG, AGGRESSIVE_RETRY_CONFIG, LIVE_DATA_RETRY_CONFIG } from './retry.js';
|
|
10
|
-
export { RealtimeDataManager, getRealtimeManager, resetRealtimeManager } from './realtime-manager.js';
|
|
11
|
-
export { AlertManager, getAlertManager, resetAlertManager } from './alert-manager.js';
|
|
12
|
-
export { DataQualityValidator, getDataQualityValidator } from './data-quality.js';
|
|
13
|
-
export { MLPredictionEngine, getPredictionEngine } from './ml-prediction.js';
|
|
14
|
-
export { HistoricalAnalyzer, getHistoricalAnalyzer } from './historical-analyzer.js';
|
|
15
|
-
// Constants
|
|
16
|
-
export { API_CONFIG, CACHE_CONFIG, SCRAPER_CONFIG, LEAGUES, QUERY_TYPES, BETTING, POSITIONS, MATCH_STATUS, ERRORS, ALERT_CONFIG, REALTIME_CONFIG, ML_CONFIG, MODULE_INFO } from './constants.js';
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ML PREDICTION ENGINE
|
|
3
|
-
*/
|
|
4
|
-
import type { Match, TeamStats, HeadToHead } from './types.js';
|
|
5
|
-
export interface PredictionInput {
|
|
6
|
-
match: Match;
|
|
7
|
-
homeForm: FormData;
|
|
8
|
-
awayForm: FormData;
|
|
9
|
-
h2h: HeadToHead;
|
|
10
|
-
homeStats: TeamStats;
|
|
11
|
-
awayStats: TeamStats;
|
|
12
|
-
injuries: InjuryData;
|
|
13
|
-
weather?: WeatherData;
|
|
14
|
-
}
|
|
15
|
-
export interface FormData {
|
|
16
|
-
last5: ('W' | 'D' | 'L')[];
|
|
17
|
-
goalsFor: number;
|
|
18
|
-
goalsAgainst: number;
|
|
19
|
-
xG: number;
|
|
20
|
-
xGA: number;
|
|
21
|
-
}
|
|
22
|
-
export interface InjuryData {
|
|
23
|
-
home: {
|
|
24
|
-
keyPlayers: number;
|
|
25
|
-
total: number;
|
|
26
|
-
};
|
|
27
|
-
away: {
|
|
28
|
-
keyPlayers: number;
|
|
29
|
-
total: number;
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
export interface WeatherData {
|
|
33
|
-
temperature: number;
|
|
34
|
-
condition: string;
|
|
35
|
-
windSpeed: number;
|
|
36
|
-
precipitation: boolean;
|
|
37
|
-
}
|
|
38
|
-
export interface PredictionFactor {
|
|
39
|
-
name: string;
|
|
40
|
-
weight: number;
|
|
41
|
-
impact: 'positive' | 'negative' | 'neutral';
|
|
42
|
-
description: string;
|
|
43
|
-
}
|
|
44
|
-
export interface PredictionResult {
|
|
45
|
-
homeWin: number;
|
|
46
|
-
draw: number;
|
|
47
|
-
awayWin: number;
|
|
48
|
-
over25: number;
|
|
49
|
-
btts: number;
|
|
50
|
-
confidence: number;
|
|
51
|
-
factors: PredictionFactor[];
|
|
52
|
-
}
|
|
53
|
-
export declare class MLPredictionEngine {
|
|
54
|
-
private weights;
|
|
55
|
-
private historicalData;
|
|
56
|
-
predict(input: PredictionInput): Promise<PredictionResult>;
|
|
57
|
-
private calculateFormScore;
|
|
58
|
-
private formToPoints;
|
|
59
|
-
private calculateH2HScore;
|
|
60
|
-
private calculateHomeAdvantage;
|
|
61
|
-
private calculateXgScore;
|
|
62
|
-
private calculateInjuryImpact;
|
|
63
|
-
private calculateFatigueImpact;
|
|
64
|
-
private calculateWeatherImpact;
|
|
65
|
-
private combineScores;
|
|
66
|
-
private scoreToProbabilities;
|
|
67
|
-
private calculateConfidence;
|
|
68
|
-
private predictOver25;
|
|
69
|
-
private predictBTTS;
|
|
70
|
-
private describeFormImpact;
|
|
71
|
-
private describeH2HImpact;
|
|
72
|
-
private describeInjuryImpact;
|
|
73
|
-
private describeWeatherImpact;
|
|
74
|
-
learnFromResult(prediction: PredictionResult, actualResult: 'home' | 'draw' | 'away', odds: number): void;
|
|
75
|
-
}
|
|
76
|
-
export declare function getPredictionEngine(): MLPredictionEngine;
|