@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,25 +1,19 @@
1
1
  /**
2
2
  * ML PREDICTION ENGINE
3
- * Machine learning based match outcome prediction
4
3
  */
5
- import { logger } from '../../../utils.js';
6
4
  import { ML_CONFIG } from './constants.js';
7
- /**
8
- * ML Prediction Engine
9
- * Uses weighted factors and historical patterns for predictions
10
- */
5
+ // Simple logger fallback
6
+ const logger = {
7
+ info: (...args) => console.error('[INFO]', ...args),
8
+ warn: (...args) => console.warn('[WARN]', ...args),
9
+ error: (...args) => console.error('[ERROR]', ...args),
10
+ debug: (...args) => { }
11
+ };
11
12
  export class MLPredictionEngine {
12
13
  weights = ML_CONFIG.FEATURE_WEIGHTS;
13
14
  historicalData = new Map();
14
- constructor() {
15
- this.loadHistoricalData();
16
- }
17
- /**
18
- * Predict match outcome
19
- */
20
15
  async predict(input) {
21
16
  const factors = [];
22
- // Calculate form score
23
17
  const formScore = this.calculateFormScore(input);
24
18
  factors.push({
25
19
  name: 'Recent Form',
@@ -27,7 +21,6 @@ export class MLPredictionEngine {
27
21
  impact: formScore > 0 ? 'positive' : formScore < 0 ? 'negative' : 'neutral',
28
22
  description: this.describeFormImpact(input),
29
23
  });
30
- // Calculate H2H score
31
24
  const h2hScore = this.calculateH2HScore(input);
32
25
  factors.push({
33
26
  name: 'Head-to-Head',
@@ -35,7 +28,6 @@ export class MLPredictionEngine {
35
28
  impact: h2hScore > 0 ? 'positive' : h2hScore < 0 ? 'negative' : 'neutral',
36
29
  description: this.describeH2HImpact(input),
37
30
  });
38
- // Calculate home advantage
39
31
  const homeAdvantageScore = this.calculateHomeAdvantage(input);
40
32
  factors.push({
41
33
  name: 'Home Advantage',
@@ -43,7 +35,6 @@ export class MLPredictionEngine {
43
35
  impact: homeAdvantageScore > 0 ? 'positive' : 'neutral',
44
36
  description: `Home advantage factor: ${(homeAdvantageScore * 100).toFixed(1)}%`,
45
37
  });
46
- // Calculate xG score
47
38
  const xgScore = this.calculateXgScore(input);
48
39
  factors.push({
49
40
  name: 'Expected Goals (xG)',
@@ -51,7 +42,6 @@ export class MLPredictionEngine {
51
42
  impact: xgScore > 0 ? 'positive' : xgScore < 0 ? 'negative' : 'neutral',
52
43
  description: `xG differential: ${(xgScore * 100).toFixed(1)}%`,
53
44
  });
54
- // Calculate injury impact
55
45
  const injuryScore = this.calculateInjuryImpact(input);
56
46
  factors.push({
57
47
  name: 'Injuries',
@@ -59,7 +49,6 @@ export class MLPredictionEngine {
59
49
  impact: injuryScore > 0 ? 'positive' : injuryScore < 0 ? 'negative' : 'neutral',
60
50
  description: this.describeInjuryImpact(input),
61
51
  });
62
- // Calculate fatigue impact
63
52
  const fatigueScore = this.calculateFatigueImpact(input);
64
53
  factors.push({
65
54
  name: 'Fatigue',
@@ -67,7 +56,6 @@ export class MLPredictionEngine {
67
56
  impact: fatigueScore > 0 ? 'positive' : fatigueScore < 0 ? 'negative' : 'neutral',
68
57
  description: `Fatigue differential: ${(fatigueScore * 100).toFixed(1)}%`,
69
58
  });
70
- // Weather impact
71
59
  const weatherScore = this.calculateWeatherImpact(input);
72
60
  factors.push({
73
61
  name: 'Weather',
@@ -75,7 +63,6 @@ export class MLPredictionEngine {
75
63
  impact: weatherScore !== 0 ? (weatherScore > 0 ? 'positive' : 'negative') : 'neutral',
76
64
  description: this.describeWeatherImpact(input),
77
65
  });
78
- // Combine all factors
79
66
  const combinedScore = this.combineScores([
80
67
  { score: formScore, weight: this.weights.form },
81
68
  { score: h2hScore, weight: this.weights.h2h },
@@ -85,16 +72,12 @@ export class MLPredictionEngine {
85
72
  { score: fatigueScore, weight: this.weights.fatigue },
86
73
  { score: weatherScore, weight: this.weights.weather },
87
74
  ]);
88
- // Calculate probabilities
89
75
  const probabilities = this.scoreToProbabilities(combinedScore);
90
- // Calculate confidence
91
76
  const confidence = this.calculateConfidence(factors, input);
92
- // Calculate additional markets
93
77
  const over25Prob = this.predictOver25(input);
94
78
  const bttsProb = this.predictBTTS(input);
95
79
  logger.info(`[MLPrediction] ${input.match.homeTeam.name} vs ${input.match.awayTeam.name}: ` +
96
- `H:${(probabilities.home * 100).toFixed(1)}% D:${(probabilities.draw * 100).toFixed(1)}% A:${(probabilities.away * 100).toFixed(1)}% ` +
97
- `Confidence: ${confidence.toFixed(1)}%`);
80
+ `H:${(probabilities.home * 100).toFixed(1)}% D:${(probabilities.draw * 100).toFixed(1)}% A:${(probabilities.away * 100).toFixed(1)}%`);
98
81
  return {
99
82
  homeWin: probabilities.home,
100
83
  draw: probabilities.draw,
@@ -105,14 +88,10 @@ export class MLPredictionEngine {
105
88
  factors,
106
89
  };
107
90
  }
108
- /**
109
- * Calculate form score (-1 to 1)
110
- */
111
91
  calculateFormScore(input) {
112
92
  const homePoints = this.formToPoints(input.homeForm.last5);
113
93
  const awayPoints = this.formToPoints(input.awayForm.last5);
114
- // Normalize to -1 to 1
115
- const maxPoints = 15; // 5 wins = 15 points
94
+ const maxPoints = 15;
116
95
  const homeNormalized = (homePoints / maxPoints) * 2 - 1;
117
96
  const awayNormalized = (awayPoints / maxPoints) * 2 - 1;
118
97
  return homeNormalized - awayNormalized;
@@ -126,9 +105,6 @@ export class MLPredictionEngine {
126
105
  return sum;
127
106
  }, 0);
128
107
  }
129
- /**
130
- * Calculate H2H score (-1 to 1)
131
- */
132
108
  calculateH2HScore(input) {
133
109
  const h2h = input.h2h;
134
110
  const total = h2h.totalMatches;
@@ -136,210 +112,117 @@ export class MLPredictionEngine {
136
112
  return 0;
137
113
  const homeWinRate = h2h.homeWins / total;
138
114
  const awayWinRate = h2h.awayWins / total;
139
- // Weight recent matches more heavily
140
- const recentMatches = h2h.recentMatches.slice(0, 5);
141
- let recentScore = 0;
142
- recentMatches.forEach((match, index) => {
143
- const weight = (5 - index) / 5; // More recent = higher weight
115
+ const recentScore = h2h.recentMatches.slice(0, 5).reduce((score, match, index) => {
116
+ const weight = (5 - index) / 5;
144
117
  if (match.score) {
145
118
  if (match.score.home > match.score.away)
146
- recentScore += weight;
147
- else if (match.score.home < match.score.away)
148
- recentScore -= weight;
119
+ return score + weight;
120
+ if (match.score.home < match.score.away)
121
+ return score - weight;
149
122
  }
150
- });
151
- // Combine historical and recent
123
+ return score;
124
+ }, 0);
152
125
  const historicalScore = homeWinRate - awayWinRate;
153
- const combinedScore = (historicalScore * 0.4) + (recentScore / 5 * 0.6);
154
- return Math.max(-1, Math.min(1, combinedScore));
126
+ return Math.max(-1, Math.min(1, (historicalScore * 0.4) + (recentScore / 5 * 0.6)));
155
127
  }
156
- /**
157
- * Calculate home advantage score (0 to 1)
158
- */
159
128
  calculateHomeAdvantage(input) {
160
129
  const homeStats = input.homeStats;
161
130
  const awayStats = input.awayStats;
162
- if (!homeStats?.homeStats || !awayStats?.awayStats) {
163
- return 0.1; // Default home advantage
164
- }
131
+ if (!homeStats?.homeStats || !awayStats?.awayStats)
132
+ return 0.1;
165
133
  const homeWinRate = homeStats.homeStats.wins / (homeStats.homeStats.matchesPlayed || 1);
166
134
  const awayWinRate = awayStats.awayStats.wins / (awayStats.awayStats.matchesPlayed || 1);
167
135
  return Math.max(0, homeWinRate - awayWinRate);
168
136
  }
169
- /**
170
- * Calculate xG score (-1 to 1)
171
- */
172
137
  calculateXgScore(input) {
173
- const homeXg = input.homeForm.xG;
174
- const homeXga = input.homeForm.xGA;
175
- const awayXg = input.awayForm.xG;
176
- const awayXga = input.awayForm.xGA;
177
- // xG differential (attack - defense)
178
- const homeXgDiff = homeXg - homeXga;
179
- const awayXgDiff = awayXg - awayXga;
180
- // Normalize (typical range is -1 to 1 per game)
181
- const normalized = (homeXgDiff - awayXgDiff) / 2;
182
- return Math.max(-1, Math.min(1, normalized));
138
+ const homeXgDiff = input.homeForm.xG - input.homeForm.xGA;
139
+ const awayXgDiff = input.awayForm.xG - input.awayForm.xGA;
140
+ return Math.max(-1, Math.min(1, (homeXgDiff - awayXgDiff) / 2));
183
141
  }
184
- /**
185
- * Calculate injury impact (-1 to 1)
186
- */
187
142
  calculateInjuryImpact(input) {
188
- const homeKeyInjuries = input.injuries.home.keyPlayers;
189
- const awayKeyInjuries = input.injuries.away.keyPlayers;
190
- // Each key player injury is worth ~0.1
191
- const impact = (awayKeyInjuries - homeKeyInjuries) * 0.1;
143
+ const impact = (input.injuries.away.keyPlayers - input.injuries.home.keyPlayers) * 0.1;
192
144
  return Math.max(-1, Math.min(1, impact));
193
145
  }
194
- /**
195
- * Calculate fatigue impact (-1 to 1)
196
- */
197
146
  calculateFatigueImpact(input) {
198
- // This would need actual fatigue data
199
- // For now, use matches played as proxy
200
- const homeMatches = input.homeStats.matchesPlayed;
201
- const awayMatches = input.awayStats.matchesPlayed;
202
- // More matches = more fatigue
203
- const diff = awayMatches - homeMatches;
204
- const impact = diff * 0.02; // Each match difference = 2%
205
- return Math.max(-1, Math.min(1, impact));
147
+ const diff = input.awayStats.matchesPlayed - input.homeStats.matchesPlayed;
148
+ return Math.max(-1, Math.min(1, diff * 0.02));
206
149
  }
207
- /**
208
- * Calculate weather impact (-0.5 to 0.5)
209
- */
210
150
  calculateWeatherImpact(input) {
211
151
  if (!input.weather)
212
152
  return 0;
213
- const weather = input.weather;
214
153
  let impact = 0;
215
- // Rain favors defensive teams
216
- if (weather.precipitation) {
154
+ if (input.weather.precipitation)
217
155
  impact -= 0.1;
218
- }
219
- // High wind affects passing teams
220
- if (weather.windSpeed > 20) {
156
+ if (input.weather.windSpeed > 20)
221
157
  impact -= 0.1;
222
- }
223
- // Extreme temperatures
224
- if (weather.temperature > 30 || weather.temperature < 0) {
158
+ if (input.weather.temperature > 30 || input.weather.temperature < 0)
225
159
  impact -= 0.05;
226
- }
227
160
  return impact;
228
161
  }
229
- /**
230
- * Combine weighted scores
231
- */
232
162
  combineScores(scores) {
233
163
  const totalWeight = scores.reduce((sum, s) => sum + s.weight, 0);
234
164
  const weightedSum = scores.reduce((sum, s) => sum + (s.score * s.weight), 0);
235
165
  return weightedSum / totalWeight;
236
166
  }
237
- /**
238
- * Convert score to probabilities
239
- */
240
167
  scoreToProbabilities(score) {
241
- // Score is from -1 (away advantage) to 1 (home advantage)
242
- // Convert to probabilities using softmax-like function
243
168
  const homeExp = Math.exp(score * 2);
244
- const drawExp = Math.exp(0); // Neutral
169
+ const drawExp = Math.exp(0);
245
170
  const awayExp = Math.exp(-score * 2);
246
171
  const total = homeExp + drawExp + awayExp;
247
- return {
248
- home: homeExp / total,
249
- draw: drawExp / total,
250
- away: awayExp / total,
251
- };
172
+ return { home: homeExp / total, draw: drawExp / total, away: awayExp / total };
252
173
  }
253
- /**
254
- * Calculate confidence level
255
- */
256
174
  calculateConfidence(factors, input) {
257
- let confidence = 50; // Base confidence
258
- // More data = higher confidence
175
+ let confidence = 50;
259
176
  if (input.homeForm.last5.length >= 5)
260
177
  confidence += 10;
261
178
  if (input.awayForm.last5.length >= 5)
262
179
  confidence += 10;
263
180
  if (input.h2h.totalMatches >= 3)
264
181
  confidence += 10;
265
- // High impact factors increase confidence
266
182
  const highImpactFactors = factors.filter(f => Math.abs(f.weight) > 0.15 && f.impact !== 'neutral').length;
267
183
  confidence += highImpactFactors * 5;
268
- // Cap at 95%
269
184
  return Math.min(95, confidence);
270
185
  }
271
- /**
272
- * Predict Over 2.5 goals
273
- */
274
186
  predictOver25(input) {
275
- const homeXg = input.homeForm.xG / 5; // per game
276
- const awayXg = input.awayForm.xG / 5;
277
- const homeXga = input.homeForm.xGA / 5;
278
- const awayXga = input.awayForm.xGA / 5;
279
- const expectedGoals = (homeXg + awayXga + awayXg + homeXga) / 2;
280
- // Convert to probability
187
+ const expectedGoals = (input.homeForm.xG / 5 + input.awayForm.xGA / 5 + input.awayForm.xG / 5 + input.homeForm.xGA / 5) / 2;
281
188
  return Math.min(0.9, Math.max(0.1, expectedGoals / 3));
282
189
  }
283
- /**
284
- * Predict Both Teams to Score
285
- */
286
190
  predictBTTS(input) {
287
191
  const homeScoring = input.homeForm.goalsFor / 5;
288
192
  const awayScoring = input.awayForm.goalsFor / 5;
289
- const homeConceding = input.homeForm.goalsAgainst / 5;
290
- const awayConceding = input.awayForm.goalsAgainst / 5;
291
193
  const homeLikelyToScore = homeScoring > 1 ? 0.7 : homeScoring > 0.5 ? 0.5 : 0.3;
292
194
  const awayLikelyToScore = awayScoring > 1 ? 0.7 : awayScoring > 0.5 ? 0.5 : 0.3;
293
195
  return homeLikelyToScore * awayLikelyToScore;
294
196
  }
295
- /**
296
- * Describe form impact
297
- */
298
197
  describeFormImpact(input) {
299
198
  const homePoints = this.formToPoints(input.homeForm.last5);
300
199
  const awayPoints = this.formToPoints(input.awayForm.last5);
301
- if (homePoints > awayPoints + 4) {
200
+ if (homePoints > awayPoints + 4)
302
201
  return `${input.match.homeTeam.name} in significantly better form`;
303
- }
304
- else if (awayPoints > homePoints + 4) {
202
+ if (awayPoints > homePoints + 4)
305
203
  return `${input.match.awayTeam.name} in significantly better form`;
306
- }
307
- else if (Math.abs(homePoints - awayPoints) <= 2) {
204
+ if (Math.abs(homePoints - awayPoints) <= 2)
308
205
  return 'Teams in similar form';
309
- }
310
206
  return 'Form advantage detected';
311
207
  }
312
- /**
313
- * Describe H2H impact
314
- */
315
208
  describeH2HImpact(input) {
316
209
  const h2h = input.h2h;
317
- if (h2h.totalMatches === 0) {
210
+ if (h2h.totalMatches === 0)
318
211
  return 'No recent head-to-head data';
319
- }
320
212
  const homeWinRate = (h2h.homeWins / h2h.totalMatches * 100).toFixed(0);
321
213
  return `${input.match.homeTeam.name} won ${homeWinRate}% of ${h2h.totalMatches} meetings`;
322
214
  }
323
- /**
324
- * Describe injury impact
325
- */
326
215
  describeInjuryImpact(input) {
327
216
  const homeKey = input.injuries.home.keyPlayers;
328
217
  const awayKey = input.injuries.away.keyPlayers;
329
- if (homeKey === 0 && awayKey === 0) {
218
+ if (homeKey === 0 && awayKey === 0)
330
219
  return 'No key injuries for either team';
331
- }
332
- else if (homeKey > awayKey) {
220
+ if (homeKey > awayKey)
333
221
  return `${input.match.homeTeam.name} has ${homeKey} key player(s) injured`;
334
- }
335
- else if (awayKey > homeKey) {
222
+ if (awayKey > homeKey)
336
223
  return `${input.match.awayTeam.name} has ${awayKey} key player(s) injured`;
337
- }
338
224
  return 'Both teams have similar injury concerns';
339
225
  }
340
- /**
341
- * Describe weather impact
342
- */
343
226
  describeWeatherImpact(input) {
344
227
  if (!input.weather)
345
228
  return 'Weather data not available';
@@ -352,29 +235,13 @@ export class MLPredictionEngine {
352
235
  conditions.push('hot');
353
236
  if (input.weather.temperature < 5)
354
237
  conditions.push('cold');
355
- if (conditions.length === 0) {
238
+ if (conditions.length === 0)
356
239
  return 'Good weather conditions';
357
- }
358
240
  return `Adverse conditions: ${conditions.join(', ')}`;
359
241
  }
360
- /**
361
- * Load historical data
362
- */
363
- loadHistoricalData() {
364
- // Would load from database or file
365
- // For now, start empty
366
- }
367
- /**
368
- * Learn from past predictions
369
- */
370
242
  learnFromResult(prediction, actualResult, odds) {
371
- // Update weights based on accuracy
372
- // This is a simplified version - real ML would use gradient descent
373
243
  const predictedOutcome = prediction.homeWin > prediction.draw && prediction.homeWin > prediction.awayWin
374
- ? 'home'
375
- : prediction.awayWin > prediction.draw
376
- ? 'away'
377
- : 'draw';
244
+ ? 'home' : prediction.awayWin > prediction.draw ? 'away' : 'draw';
378
245
  const wasCorrect = predictedOutcome === actualResult;
379
246
  if (wasCorrect) {
380
247
  logger.info(`[MLPrediction] Correct prediction at ${odds}x`);
@@ -382,21 +249,12 @@ export class MLPredictionEngine {
382
249
  else {
383
250
  logger.info(`[MLPrediction] Incorrect prediction. Predicted: ${predictedOutcome}, Actual: ${actualResult}`);
384
251
  }
385
- // Store for analysis
386
- this.historicalData.set(`result-${Date.now()}`, {
387
- prediction,
388
- actualResult,
389
- odds,
390
- wasCorrect,
391
- timestamp: Date.now(),
392
- });
252
+ this.historicalData.set(`result-${Date.now()}`, { prediction, actualResult, odds, wasCorrect, timestamp: Date.now() });
393
253
  }
394
254
  }
395
- // Singleton instance
396
255
  let globalPredictionEngine = null;
397
256
  export function getPredictionEngine() {
398
- if (!globalPredictionEngine) {
257
+ if (!globalPredictionEngine)
399
258
  globalPredictionEngine = new MLPredictionEngine();
400
- }
401
259
  return globalPredictionEngine;
402
260
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * REALTIME DATA MANAGER
3
- * WebSocket/SSE based real-time data streaming with fallback polling
3
+ * Polling-based real-time data streaming
4
4
  */
5
5
  import { EventEmitter } from 'events';
6
6
  import type { Match } from './types.js';
@@ -25,74 +25,23 @@ export interface OddsChangeEvent extends LiveEvent {
25
25
  }
26
26
  export declare class RealtimeDataManager extends EventEmitter {
27
27
  private cache;
28
- private wsConnections;
29
28
  private pollingIntervals;
30
- private reconnectAttempts;
31
29
  private isRunning;
32
30
  constructor();
33
- /**
34
- * Start the realtime manager
35
- */
36
31
  start(): Promise<void>;
37
- /**
38
- * Stop the realtime manager
39
- */
40
32
  stop(): void;
41
- /**
42
- * Subscribe to a specific match's events
43
- */
44
33
  subscribeToMatch(matchId: string, callback: (event: LiveEvent) => void): () => void;
45
- /**
46
- * Subscribe to odds changes for a match
47
- */
48
34
  subscribeToOdds(matchId: string, callback: (event: OddsChangeEvent) => void): () => void;
49
- /**
50
- * Subscribe to all live events
51
- */
52
35
  subscribeToAllEvents(callback: (event: LiveEvent) => void): () => void;
53
- /**
54
- * Process and broadcast an event
55
- */
56
36
  processEvent(event: LiveEvent): void;
57
- /**
58
- * Get current live matches
59
- */
60
37
  getLiveMatches(): Match[];
61
- /**
62
- * Get match events history
63
- */
64
38
  getMatchEvents(matchId: string): LiveEvent[];
65
- /**
66
- * Connect to match stream (WebSocket or polling)
67
- */
68
- private connectToMatchStream;
69
- /**
70
- * Unsubscribe from a match
71
- */
72
39
  private unsubscribeFromMatch;
73
- /**
74
- * Start polling for live scores
75
- */
76
40
  private startLiveScoresPolling;
77
- /**
78
- * Start polling for odds
79
- */
80
41
  private startOddsPolling;
81
- /**
82
- * Start polling for specific match events
83
- */
84
42
  private startMatchEventsPolling;
85
- /**
86
- * Detect score changes and emit goal events
87
- */
88
43
  private detectScoreChanges;
89
- /**
90
- * Detect odds changes and emit events
91
- */
92
44
  private detectOddsChanges;
93
- /**
94
- * Update cache with event
95
- */
96
45
  private updateCache;
97
46
  private fetchLiveScores;
98
47
  private fetchOdds;