This commit is contained in:
Executable
+471
@@ -0,0 +1,471 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export type SignalTier =
|
||||
| 'CORE'
|
||||
| 'VALUE'
|
||||
| 'LEAN'
|
||||
| 'LONGSHOT'
|
||||
| 'PASS';
|
||||
|
||||
export class MatchInfoDto {
|
||||
@ApiProperty()
|
||||
match_id: string;
|
||||
|
||||
@ApiProperty()
|
||||
match_name: string;
|
||||
|
||||
@ApiProperty()
|
||||
home_team: string;
|
||||
|
||||
@ApiProperty()
|
||||
away_team: string;
|
||||
|
||||
@ApiProperty()
|
||||
league: string;
|
||||
|
||||
@ApiProperty()
|
||||
match_date_ms: number;
|
||||
|
||||
@ApiProperty({ required: false, nullable: true })
|
||||
league_id?: string | null;
|
||||
|
||||
@ApiProperty({ required: false, default: false })
|
||||
is_top_league?: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
enum: ['football', 'basketball'],
|
||||
})
|
||||
sport?: 'football' | 'basketball';
|
||||
}
|
||||
|
||||
export class DataQualityDto {
|
||||
@ApiProperty({ enum: ['HIGH', 'MEDIUM', 'LOW'] })
|
||||
label: 'HIGH' | 'MEDIUM' | 'LOW';
|
||||
|
||||
@ApiProperty()
|
||||
score: number;
|
||||
|
||||
@ApiProperty()
|
||||
home_lineup_count: number;
|
||||
|
||||
@ApiProperty()
|
||||
away_lineup_count: number;
|
||||
|
||||
@ApiProperty({ required: false, default: 'none' })
|
||||
lineup_source?: string;
|
||||
|
||||
@ApiProperty({ type: [String] })
|
||||
flags: string[];
|
||||
}
|
||||
|
||||
export class ConfidenceIntervalDto {
|
||||
@ApiProperty()
|
||||
lower: number;
|
||||
|
||||
@ApiProperty()
|
||||
upper: number;
|
||||
|
||||
@ApiProperty()
|
||||
width: number;
|
||||
|
||||
@ApiProperty({ enum: ['HIGH', 'MEDIUM', 'LOW'] })
|
||||
band: 'HIGH' | 'MEDIUM' | 'LOW';
|
||||
|
||||
@ApiProperty()
|
||||
threshold_met: boolean;
|
||||
}
|
||||
|
||||
export class RiskDto {
|
||||
@ApiProperty({ enum: ['LOW', 'MEDIUM', 'HIGH', 'EXTREME'] })
|
||||
level: 'LOW' | 'MEDIUM' | 'HIGH' | 'EXTREME';
|
||||
|
||||
@ApiProperty()
|
||||
score: number;
|
||||
|
||||
@ApiProperty()
|
||||
is_surprise_risk: boolean;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
surprise_type: string | null;
|
||||
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
surprise_score?: number;
|
||||
|
||||
@ApiProperty({ required: false, nullable: true })
|
||||
surprise_comment?: string | null;
|
||||
|
||||
@ApiProperty({ type: [String], required: false })
|
||||
surprise_reasons?: string[];
|
||||
|
||||
@ApiProperty({ type: [String] })
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export class EngineBreakdownDto {
|
||||
@ApiProperty()
|
||||
team: number;
|
||||
|
||||
@ApiProperty()
|
||||
player: number;
|
||||
|
||||
@ApiProperty()
|
||||
odds: number;
|
||||
|
||||
@ApiProperty()
|
||||
referee: number;
|
||||
}
|
||||
|
||||
export class MatchPickDto {
|
||||
@ApiProperty()
|
||||
market: string;
|
||||
|
||||
@ApiProperty()
|
||||
pick: string;
|
||||
|
||||
@ApiProperty()
|
||||
probability: number;
|
||||
|
||||
@ApiProperty()
|
||||
confidence: number;
|
||||
|
||||
@ApiProperty()
|
||||
odds: number;
|
||||
|
||||
@ApiProperty()
|
||||
raw_confidence: number;
|
||||
|
||||
@ApiProperty()
|
||||
calibrated_confidence: number;
|
||||
|
||||
@ApiProperty()
|
||||
min_required_confidence: number;
|
||||
|
||||
@ApiProperty()
|
||||
edge: number;
|
||||
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
ev_edge?: number;
|
||||
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
implied_prob?: number;
|
||||
|
||||
@ApiProperty()
|
||||
play_score: number;
|
||||
|
||||
@ApiProperty()
|
||||
playable: boolean;
|
||||
|
||||
@ApiProperty({ enum: ['A', 'B', 'C', 'PASS'] })
|
||||
bet_grade: 'A' | 'B' | 'C' | 'PASS';
|
||||
|
||||
@ApiProperty()
|
||||
stake_units: number;
|
||||
|
||||
@ApiProperty({ type: [String] })
|
||||
decision_reasons: string[];
|
||||
|
||||
@ApiProperty({ type: ConfidenceIntervalDto, required: false })
|
||||
confidence_interval?: ConfidenceIntervalDto;
|
||||
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
enum: ['CORE', 'VALUE', 'LEAN', 'LONGSHOT', 'PASS'],
|
||||
})
|
||||
signal_tier?: SignalTier;
|
||||
}
|
||||
|
||||
export class MatchBetAdviceDto {
|
||||
@ApiProperty()
|
||||
playable: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
suggested_stake_units: number;
|
||||
|
||||
@ApiProperty()
|
||||
reason: string;
|
||||
|
||||
@ApiProperty({ required: false, enum: ['HIGH', 'MEDIUM', 'LOW'] })
|
||||
confidence_band?: 'HIGH' | 'MEDIUM' | 'LOW';
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
min_confidence_for_play?: number;
|
||||
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
enum: ['CORE', 'VALUE', 'LEAN', 'LONGSHOT', 'PASS'],
|
||||
})
|
||||
signal_tier?: SignalTier;
|
||||
}
|
||||
|
||||
export class MatchBetSummaryItemDto {
|
||||
@ApiProperty()
|
||||
market: string;
|
||||
|
||||
@ApiProperty()
|
||||
pick: string;
|
||||
|
||||
@ApiProperty()
|
||||
raw_confidence: number;
|
||||
|
||||
@ApiProperty()
|
||||
calibrated_confidence: number;
|
||||
|
||||
@ApiProperty({ enum: ['A', 'B', 'C', 'PASS'] })
|
||||
bet_grade: 'A' | 'B' | 'C' | 'PASS';
|
||||
|
||||
@ApiProperty()
|
||||
playable: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
stake_units: number;
|
||||
|
||||
@ApiProperty()
|
||||
play_score: number;
|
||||
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
ev_edge?: number;
|
||||
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
implied_prob?: number;
|
||||
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
odds?: number;
|
||||
|
||||
@ApiProperty({ type: [String] })
|
||||
reasons: string[];
|
||||
|
||||
@ApiProperty({ type: ConfidenceIntervalDto, required: false })
|
||||
confidence_interval?: ConfidenceIntervalDto;
|
||||
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
enum: ['CORE', 'VALUE', 'LEAN', 'LONGSHOT', 'PASS'],
|
||||
})
|
||||
signal_tier?: SignalTier;
|
||||
}
|
||||
|
||||
export class HtFtPredictionDto {
|
||||
@ApiProperty()
|
||||
'1/1': number;
|
||||
@ApiProperty()
|
||||
'1/X': number;
|
||||
@ApiProperty()
|
||||
'1/2': number;
|
||||
@ApiProperty()
|
||||
'X/1': number;
|
||||
@ApiProperty()
|
||||
'X/X': number;
|
||||
@ApiProperty()
|
||||
'X/2': number;
|
||||
@ApiProperty()
|
||||
'2/1': number;
|
||||
@ApiProperty()
|
||||
'2/X': number;
|
||||
@ApiProperty()
|
||||
'2/2': number;
|
||||
@ApiProperty()
|
||||
pick: string;
|
||||
@ApiProperty()
|
||||
confidence: number;
|
||||
}
|
||||
|
||||
export class AggressivePickDto {
|
||||
@ApiProperty()
|
||||
market: string;
|
||||
|
||||
@ApiProperty()
|
||||
pick: string;
|
||||
|
||||
@ApiProperty()
|
||||
probability: number;
|
||||
|
||||
@ApiProperty()
|
||||
confidence: number;
|
||||
|
||||
@ApiProperty()
|
||||
odds: number;
|
||||
|
||||
@ApiProperty()
|
||||
raw_confidence: number;
|
||||
|
||||
@ApiProperty()
|
||||
calibrated_confidence: number;
|
||||
|
||||
@ApiProperty()
|
||||
min_required_confidence: number;
|
||||
|
||||
@ApiProperty()
|
||||
edge: number;
|
||||
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
ev_edge?: number;
|
||||
|
||||
@ApiProperty({ required: false, default: 0 })
|
||||
implied_prob?: number;
|
||||
|
||||
@ApiProperty()
|
||||
play_score: number;
|
||||
|
||||
@ApiProperty()
|
||||
playable: boolean;
|
||||
|
||||
@ApiProperty({ enum: ['A', 'B', 'C', 'PASS'] })
|
||||
bet_grade: 'A' | 'B' | 'C' | 'PASS';
|
||||
|
||||
@ApiProperty()
|
||||
stake_units: number;
|
||||
|
||||
@ApiProperty({ type: [String] })
|
||||
decision_reasons: string[];
|
||||
|
||||
@ApiProperty({ type: ConfidenceIntervalDto, required: false })
|
||||
confidence_interval?: ConfidenceIntervalDto;
|
||||
}
|
||||
|
||||
export class ScenarioTop5ItemDto {
|
||||
@ApiProperty()
|
||||
scenario: string;
|
||||
|
||||
@ApiProperty()
|
||||
score: number;
|
||||
|
||||
@ApiProperty()
|
||||
probability: number;
|
||||
}
|
||||
|
||||
export class ScorePredictionDto {
|
||||
@ApiProperty()
|
||||
ft: string;
|
||||
|
||||
@ApiProperty()
|
||||
ht: string;
|
||||
|
||||
@ApiProperty()
|
||||
xg_home: number;
|
||||
|
||||
@ApiProperty()
|
||||
xg_away: number;
|
||||
|
||||
@ApiProperty()
|
||||
xg_total: number;
|
||||
}
|
||||
|
||||
export class MatchPredictionDto {
|
||||
@ApiProperty()
|
||||
model_version: string;
|
||||
|
||||
@ApiProperty({ type: MatchInfoDto })
|
||||
match_info: MatchInfoDto;
|
||||
|
||||
@ApiProperty({ type: DataQualityDto })
|
||||
data_quality: DataQualityDto;
|
||||
|
||||
@ApiProperty({ type: RiskDto })
|
||||
risk: RiskDto;
|
||||
|
||||
@ApiProperty({ type: EngineBreakdownDto })
|
||||
engine_breakdown: EngineBreakdownDto;
|
||||
|
||||
@ApiProperty({ type: MatchPickDto, nullable: true })
|
||||
main_pick: MatchPickDto | null;
|
||||
|
||||
@ApiProperty({ type: MatchPickDto, nullable: true })
|
||||
value_pick: MatchPickDto | null;
|
||||
|
||||
@ApiProperty({ type: MatchBetAdviceDto })
|
||||
bet_advice: MatchBetAdviceDto;
|
||||
|
||||
@ApiProperty({ type: [MatchBetSummaryItemDto] })
|
||||
bet_summary: MatchBetSummaryItemDto[];
|
||||
|
||||
@ApiProperty({ type: [MatchPickDto] })
|
||||
supporting_picks: MatchPickDto[];
|
||||
|
||||
@ApiProperty({ type: AggressivePickDto, nullable: true })
|
||||
aggressive_pick: AggressivePickDto | null;
|
||||
|
||||
@ApiProperty({ type: HtFtPredictionDto, required: false })
|
||||
htft?: HtFtPredictionDto;
|
||||
|
||||
@ApiProperty({ type: [ScenarioTop5ItemDto] })
|
||||
scenario_top5: ScenarioTop5ItemDto[];
|
||||
|
||||
@ApiProperty({ type: ScorePredictionDto })
|
||||
score_prediction: ScorePredictionDto;
|
||||
|
||||
@ApiProperty({ type: Object })
|
||||
market_board: Record<string, unknown>;
|
||||
|
||||
@ApiProperty({ type: [String] })
|
||||
reasoning_factors: string[];
|
||||
}
|
||||
|
||||
export class ValueBetDto {
|
||||
@ApiProperty()
|
||||
matchId: string;
|
||||
|
||||
@ApiProperty()
|
||||
matchName: string;
|
||||
|
||||
@ApiProperty()
|
||||
betType: string;
|
||||
|
||||
@ApiProperty()
|
||||
prediction: string;
|
||||
|
||||
@ApiProperty()
|
||||
confidence: number;
|
||||
|
||||
@ApiProperty()
|
||||
odd: number;
|
||||
|
||||
@ApiProperty()
|
||||
expectedValue: number;
|
||||
}
|
||||
|
||||
export class PredictionHistoryStatsDto {
|
||||
@ApiProperty()
|
||||
totalPredictions: number;
|
||||
|
||||
@ApiProperty()
|
||||
totalResolved: number;
|
||||
|
||||
@ApiProperty()
|
||||
correctPredictions: number;
|
||||
|
||||
@ApiProperty()
|
||||
accuracyRate: number;
|
||||
}
|
||||
|
||||
export class PredictionHistoryResponseDto {
|
||||
@ApiProperty({ type: PredictionHistoryStatsDto })
|
||||
stats: PredictionHistoryStatsDto;
|
||||
|
||||
@ApiProperty({ type: [Object] })
|
||||
history: Record<string, unknown>[];
|
||||
}
|
||||
|
||||
export class UpcomingPredictionsDto {
|
||||
@ApiProperty()
|
||||
count: number;
|
||||
|
||||
@ApiProperty({ type: [MatchPredictionDto] })
|
||||
matches: MatchPredictionDto[];
|
||||
|
||||
@ApiProperty()
|
||||
modelVersion: string;
|
||||
}
|
||||
|
||||
export class AIHealthDto {
|
||||
@ApiProperty()
|
||||
status: string;
|
||||
|
||||
@ApiProperty()
|
||||
modelLoaded: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
predictionServiceReady: boolean;
|
||||
}
|
||||
|
||||
export * from './smart-coupon.dto';
|
||||
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
IsArray,
|
||||
IsString,
|
||||
IsOptional,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsEnum,
|
||||
ArrayMaxSize,
|
||||
Min,
|
||||
Max,
|
||||
} from 'class-validator';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
export class GeneratePredictionDto {
|
||||
@ApiProperty({ description: 'Match ID to generate prediction for' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
matchId: string;
|
||||
}
|
||||
|
||||
export enum CouponStrategy {
|
||||
SAFE = 'SAFE',
|
||||
BALANCED = 'BALANCED',
|
||||
AGGRESSIVE = 'AGGRESSIVE',
|
||||
VALUE = 'VALUE',
|
||||
MIRACLE = 'MIRACLE',
|
||||
}
|
||||
|
||||
export class SmartCouponRequestDto {
|
||||
@ApiProperty({
|
||||
description: 'List of match IDs for coupon',
|
||||
example: ['match-1', 'match-2'],
|
||||
})
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@ArrayMaxSize(50)
|
||||
matchIds: string[];
|
||||
|
||||
@ApiPropertyOptional({
|
||||
enum: CouponStrategy,
|
||||
default: CouponStrategy.BALANCED,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsEnum(CouponStrategy)
|
||||
strategy?: CouponStrategy;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Maximum matches in coupon', example: 5 })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(20)
|
||||
maxMatches?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Minimum confidence threshold (0-100)',
|
||||
example: 60,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@Max(100)
|
||||
minConfidence?: number;
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Smart Coupon DTOs aligned with AI Engine V20+ contract.
|
||||
*/
|
||||
|
||||
export type CouponStrategy =
|
||||
| 'SAFE'
|
||||
| 'BALANCED'
|
||||
| 'AGGRESSIVE'
|
||||
| 'VALUE'
|
||||
| 'MIRACLE';
|
||||
|
||||
export type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'EXTREME';
|
||||
export type DataQualityLabel = 'HIGH' | 'MEDIUM' | 'LOW';
|
||||
|
||||
export interface SmartCouponRequestDto {
|
||||
match_ids: string[];
|
||||
strategy?: CouponStrategy;
|
||||
max_matches?: number;
|
||||
min_confidence?: number;
|
||||
}
|
||||
|
||||
export interface CouponBetDto {
|
||||
match_id: string;
|
||||
match_name: string;
|
||||
market: string;
|
||||
pick: string;
|
||||
probability: number;
|
||||
confidence: number;
|
||||
odds: number;
|
||||
risk_level: RiskLevel;
|
||||
data_quality: DataQualityLabel;
|
||||
}
|
||||
|
||||
export interface RejectedMatchDto {
|
||||
match_id: string;
|
||||
reason: string;
|
||||
threshold?: number;
|
||||
}
|
||||
|
||||
export interface SmartCouponResponseDto {
|
||||
strategy: CouponStrategy;
|
||||
generated_at: string;
|
||||
match_count: number;
|
||||
bets: CouponBetDto[];
|
||||
total_odds: number;
|
||||
expected_win_rate: number;
|
||||
rejected_matches: RejectedMatchDto[];
|
||||
}
|
||||
|
||||
export interface SmartCouponApiError {
|
||||
error: string;
|
||||
detail?: string;
|
||||
match_ids_failed?: string[];
|
||||
}
|
||||
|
||||
export interface StrategyInfo {
|
||||
name: CouponStrategy;
|
||||
description: string;
|
||||
typical_odds: string;
|
||||
}
|
||||
|
||||
export interface StrategiesResponseDto {
|
||||
strategies: StrategyInfo[];
|
||||
}
|
||||
Reference in New Issue
Block a user