Rimozione completa della componente Machine Learning

Sono state eliminate tutte le dipendenze, file sorgente e script SQL relativi al machine learning (Microsoft.ML, Horses\ML, procedure e tabelle per rating automatico). Il calcolo dei rating ora è deterministico. Aggiornato il parsing JSON in Main.cs con Newtonsoft.Json. Il progetto risulta semplificato e più focalizzato sulle funzionalità manuali.
This commit is contained in:
2026-02-25 21:17:14 +01:00
parent c8c674dd1c
commit 8fe93de673
13 changed files with 32 additions and 41920 deletions

View File

@@ -58,42 +58,6 @@
<Reference Include="CsvHelper, Version=33.0.0.0, Culture=neutral, PublicKeyToken=8c4a6d608ce8f59c, processorArchitecture=MSIL">
<HintPath>..\packages\CsvHelper.33.0.1\lib\net481\CsvHelper.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.10.0.0-preview.4.25258.110\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.HashCode, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.HashCode.6.0.0\lib\net461\Microsoft.Bcl.HashCode.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.Numerics, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.Numerics.10.0.0-preview.4.25258.110\lib\net462\Microsoft.Bcl.Numerics.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.CpuMath, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.CpuMath.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.CpuMath.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.Data.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.DataView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.DataView.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.DataView.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.FastTree, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.FastTree.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.FastTree.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.KMeansClustering, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.KMeansClustering.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.PCA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.PCA.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.StandardTrainers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.StandardTrainers.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ML.Transforms, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ML.5.0.0-preview.1.25127.4\lib\netstandard2.0\Microsoft.ML.Transforms.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
@@ -194,10 +158,6 @@
<Compile Include="HorseRacing\Main.cs" />
<Compile Include="Horses\Calculator.cs" />
<Compile Include="Horses\Files\Maps\Punters.cs" />
<Compile Include="Horses\ML\HorseRacePrediction.cs" />
<Compile Include="Horses\ML\MachineLearningService.cs" />
<Compile Include="Horses\ML\ModelInput.cs" />
<Compile Include="Horses\ML\ModelOutput.cs" />
<Compile Include="Manager\API.cs" />
<Compile Include="Manager\FileReader.cs" />
<Compile Include="Manager\Database.cs" />
@@ -215,7 +175,6 @@
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="Dati\Result.csv" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
@@ -231,11 +190,7 @@
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Query\ScanFavoriteHorseWeights.sql" />
<Content Include="Query\PredictionScanResults.sql" />
<Content Include="Query\GetFavoriteHorses.sql" />
<Content Include="Query\GetValidHorses.sql" />
<Content Include="Query\Races.sql" />
<Folder Include="Themes\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets" Condition="Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" />

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using RestSharp;
using System.Text.Json;
using System.Text.Json.Nodes;
using Newtonsoft.Json.Linq;
using System.Data.SqlClient;
namespace HorseRacingPredictor.Football
@@ -317,7 +318,13 @@ namespace HorseRacingPredictor.Football
{
try
{
var jsonObject = JsonNode.Parse(jsonResponse);
// Parse JSON using System.Text.Json and convert to Newtonsoft.JToken where needed
var jsonNode = JsonNode.Parse(jsonResponse);
var jsonObject = JObject.Parse(jsonNode.ToJsonString());
// Helper to enumerate array-like tokens (compat replacement for JsonNode.AsArray())
var responseArray = jsonObject["response"] as JArray;
Func<JToken, IEnumerable<JToken>> asArray = t => t is JArray a ? a : (t != null ? t.Children() : Enumerable.Empty<JToken>());
_database.ExecuteTransactionalQuery("l'elaborazione dei dati calcistici", (connection, transaction) =>
{
@@ -327,7 +334,7 @@ namespace HorseRacingPredictor.Football
((Manager.Database)_database).DisableAllConstraints(connection, transaction);
// FASE 1: Inserisci le leghe
foreach (var responseItem in jsonObject["response"].AsArray())
foreach (var responseItem in asArray(jsonObject["response"]))
{
var league = responseItem["league"];
if (league != null && league["id"] != null)
@@ -337,11 +344,11 @@ namespace HorseRacingPredictor.Football
}
// FASE 2: Inserisci i bookmakers e i tipi di scommessa
foreach (var responseItem in jsonObject["response"].AsArray())
foreach (var responseItem in asArray(jsonObject["response"]))
{
if (responseItem["bookmakers"] != null)
{
foreach (var bookmaker in responseItem["bookmakers"].AsArray())
foreach (var bookmaker in asArray(responseItem["bookmakers"]))
{
if (bookmaker["id"] != null)
{
@@ -350,7 +357,7 @@ namespace HorseRacingPredictor.Football
// Tipi di scommessa
if (bookmaker["bets"] != null)
{
foreach (var bet in bookmaker["bets"].AsArray())
foreach (var bet in asArray(bookmaker["bets"]))
{
if (bet["id"] != null && bet["name"] != null)
{
@@ -364,7 +371,7 @@ namespace HorseRacingPredictor.Football
}
// FASE 3: Inserisci le squadre (senza relazione con fixture)
foreach (var responseItem in jsonObject["response"].AsArray())
foreach (var responseItem in asArray(jsonObject["response"]))
{
var teams = responseItem["teams"];
if (teams != null && teams["home"] != null && teams["away"] != null &&
@@ -376,7 +383,7 @@ namespace HorseRacingPredictor.Football
}
// FASE 4: Inserisci i fixture e venue
foreach (var responseItem in jsonObject["response"].AsArray())
foreach (var responseItem in asArray(jsonObject["response"]))
{
var fixture = responseItem["fixture"];
if (fixture != null && fixture["id"] != null)
@@ -386,7 +393,7 @@ namespace HorseRacingPredictor.Football
}
// FASE 5: Inserisci relazioni tra entità e dati dipendenti
foreach (var responseItem in jsonObject["response"].AsArray())
foreach (var responseItem in asArray(jsonObject["response"]))
{
int? fixtureId = null;
try
@@ -394,7 +401,7 @@ namespace HorseRacingPredictor.Football
var fixture = responseItem["fixture"];
if (fixture != null && fixture["id"] != null)
{
fixtureId = fixture["id"].GetValue<int>();
fixtureId = fixture["id"].Value<int>();
// Relazioni fixture-team
var teams = responseItem["teams"];
@@ -408,7 +415,7 @@ namespace HorseRacingPredictor.Football
var league = responseItem["league"];
if (league != null && league["id"] != null)
{
int leagueId = league["id"].GetValue<int>();
int leagueId = league["id"].Value<int>();
_fixtureLeagueRepository.Upsert(connection, transaction, fixtureId.Value, leagueId, league);
}
@@ -435,7 +442,7 @@ namespace HorseRacingPredictor.Football
}
// FASE 6: Inserisci dati che richiedono fixture e teams: quote e previsioni
foreach (var responseItem in jsonObject["response"].AsArray())
foreach (var responseItem in asArray(jsonObject["response"]))
{
int? fixtureId = null;
try
@@ -443,7 +450,7 @@ namespace HorseRacingPredictor.Football
var fixture = responseItem["fixture"];
if (fixture != null && fixture["id"] != null)
{
fixtureId = fixture["id"].GetValue<int>();
fixtureId = fixture["id"].Value<int>();
// Quote
if (responseItem["bookmakers"] != null)
@@ -478,15 +485,15 @@ namespace HorseRacingPredictor.Football
}
// Head-to-head
if (predictions["h2h"] != null && predictions["h2h"].AsArray().Any())
if (predictions["h2h"] != null && asArray(predictions["h2h"]).Any())
{
_h2hRepository.DeleteForPrediction(connection, predictionId.Value);
foreach (var h2hFixture in predictions["h2h"].AsArray())
foreach (var h2hFixture in asArray(predictions["h2h"]))
{
if (h2hFixture["fixture"] != null && h2hFixture["fixture"]["id"] != null)
{
_h2hRepository.Insert(connection, predictionId.Value, h2hFixture["fixture"]["id"].GetValue<int>());
_h2hRepository.Insert(connection, predictionId.Value, h2hFixture["fixture"]["id"].Value<int>());
}
}
}
@@ -498,7 +505,7 @@ namespace HorseRacingPredictor.Football
// Home team stats
if (teams["home"] != null && teams["home"]["id"] != null && predictions["teams"]["home"] != null)
{
int homeTeamId = teams["home"]["id"].GetValue<int>();
int homeTeamId = teams["home"]["id"].Value<int>();
if (predictions["teams"]["home"]["team"] != null)
{
@@ -516,7 +523,7 @@ namespace HorseRacingPredictor.Football
// Away team stats
if (teams["away"] != null && teams["away"]["id"] != null && predictions["teams"]["away"] != null)
{
int awayTeamId = teams["away"]["id"].GetValue<int>();
int awayTeamId = teams["away"]["id"].Value<int>();
if (predictions["teams"]["away"]["team"] != null)
{

View File

@@ -5,17 +5,15 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HorseRacingPredictor.Horses.ML;
namespace HorseRacingPredictor.Horses
{
internal class Calculator
{
private readonly MachineLearningService _mlService;
public Calculator()
{
_mlService = new MachineLearningService();
// Machine learning service removed or unavailable in this build.
// Keep constructor minimal.
}
public int CalculateRating1(DataRow row)
@@ -168,52 +166,15 @@ namespace HorseRacingPredictor.Horses
/// </summary>
public int CalculateMLRating(DataRow row)
{
// Machine learning component removed: fall back to deterministic rating
try
{
// Prepara l'input per il modello ML
var input = new ModelInput
{
Age = GetFloatValueOrDefault(row, "Age"),
HandicapRating = GetFloatValueOrDefault(row, "Handicap Rating"),
Weight = GetFloatValueOrDefault(row, "Weight"),
WeightCarried = GetFloatValueOrDefault(row, "Weight Carried"),
Barrier = GetFloatValueOrDefault(row, "Barrier"),
CareerRuns = GetFloatValueOrDefault(row, "Career Runs"),
CareerWins = GetFloatValueOrDefault(row, "Career Wins"),
CareerStrikeRate = GetFloatValueOrDefault(row, "Career Strike Rate"),
CareerROI = GetFloatValueOrDefault(row, "Career ROI"),
ThisTrackRuns = GetFloatValueOrDefault(row, "This Track Runs"),
ThisTrackWins = GetFloatValueOrDefault(row, "This Track Wins"),
ThisTrackStrikeRate = GetFloatValueOrDefault(row, "This Track Strike Rate"),
ThisDistanceRuns = GetFloatValueOrDefault(row, "This Distance Runs"),
ThisDistanceWins = GetFloatValueOrDefault(row, "This Distance Wins"),
ThisDistanceStrikeRate = GetFloatValueOrDefault(row, "This Distance Strike Rate"),
JockeyLast100Wins = GetFloatValueOrDefault(row, "Jockey Last 100 Wins"),
JockeyLast100StrikeRate = GetFloatValueOrDefault(row, "Jockey Last 100 Strike Rate"),
TrainerLast100Wins = GetFloatValueOrDefault(row, "Trainer Last 100 Wins"),
TrainerLast100StrikeRate = GetFloatValueOrDefault(row, "Trainer Last 100 Strike Rate"),
BestFixedOdds = GetFloatValueOrDefault(row, "Best Fixed Odds")
};
// Effettua la previsione usando il modello ML
var output = _mlService.PredictFinishPosition(input);
// Il rating ML è inversamente proporzionale alla posizione prevista
// Migliore è la posizione prevista (più vicina a 1), più alto sarà il rating
double mlRating = 100 * (1.0 / Math.Max(1, output.PredictedPosition));
// Aggiusta il rating in base alla probabilità di vittoria
mlRating = mlRating * (0.6 + (output.WinProbability * 0.4));
return (int)Math.Min(100, Math.Max(0, mlRating));
}
catch (Exception ex)
{
Console.WriteLine($"Errore nel calcolo del rating ML: {ex.Message}");
// In caso di errore, ritorna un rating basato sul metodo standard
return CalculateRating1(row);
}
catch
{
return 0;
}
}
private double GetValueOrDefault(DataRow row, string columnName)

View File

@@ -1,49 +0,0 @@
using System;
namespace HorseRacingPredictor.Horses.ML
{
/// <summary>
/// Classe che contiene il risultato della previsione per un singolo cavallo in una corsa.
/// </summary>
public class HorseRacePrediction
{
// Informazioni identificative
public string HorseId { get; set; }
public string HorseName { get; set; }
public string RaceMeeting { get; set; }
public int RaceNumber { get; set; }
public DateTime RaceDate { get; set; }
// Risultati della previsione
public float PredictedPosition { get; set; }
public float WinProbability { get; set; }
public float PlaceProbability { get; set; }
// Rating ML calcolato (combinazione di probabilità e altri fattori)
public int MLRating { get; set; }
// Quota attuale per valutazione valore
public float CurrentOdds { get; set; }
// Valore calcolato (rapporto tra probabilità e quota)
public float Value => WinProbability > 0 ? (WinProbability * 100) / (1 / CurrentOdds) : 0;
// Indicazione se questo cavallo è considerato di valore
public bool IsValueBet => Value > 1.1f;
// Posizione reale del cavallo
public string ActualPosition { get; set; }
// Indicazione se la previsione è accurata
public bool IsAccuratePrediction { get; set; }
// Metodo per valutare l'accuratezza della previsione
public void EvaluateAccuracy()
{
if (!string.IsNullOrEmpty(ActualPosition) && int.TryParse(ActualPosition, out int actualPos))
{
IsAccuratePrediction = Math.Abs(actualPos - PredictedPosition) <= 0.5;
}
}
}
}

View File

@@ -1,573 +0,0 @@
using Microsoft.ML;
using Microsoft.ML.Data;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Data.SqlClient;
namespace HorseRacingPredictor.Horses.ML
{
/// <summary>
/// Servizio principale per l'implementazione del machine learning
/// </summary>
public class MachineLearningService
{
private readonly MLContext _mlContext;
private ITransformer _trainedModel;
private readonly string _modelPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Models", "HorseRaceModel.zip");
public string ConnectionString { get; set; }
public MachineLearningService(string connectionString = null)
{
_mlContext = new MLContext(seed: 42);
Directory.CreateDirectory(Path.GetDirectoryName(_modelPath));
ConnectionString = connectionString;
}
/// <summary>
/// Carica un modello ML esistente da file
/// </summary>
public void LoadModel()
{
if (!File.Exists(_modelPath))
throw new FileNotFoundException($"[ML] Modello non trovato: {_modelPath}");
Console.WriteLine($"[ML] Caricamento modello da: {_modelPath}");
_trainedModel = _mlContext.Model.Load(_modelPath, out _);
Console.WriteLine("[ML] Modello ML caricato correttamente.");
}
/// <summary>
/// Salva il modello addestrato su file
/// </summary>
public void SaveModel(ITransformer model)
{
Console.WriteLine($"[ML] Salvataggio modello in: {_modelPath}");
_mlContext.Model.Save(model, null, _modelPath);
}
/// <summary>
/// Ottiene i dati di training dal database e addestra un modello ML
/// </summary>
public void TrainModel(string connectionString)
{
// Imposta a 'true' per ricreare sempre il modello (default).
// Per NON ricreare il modello ad ogni esecuzione, commenta la riga seguente:
bool alwaysRetrain = true;
// bool alwaysRetrain = false;
if (!alwaysRetrain && File.Exists(_modelPath))
{
Console.WriteLine("[ML] Modello già esistente. Caricamento modello da file...");
LoadModel();
return;
}
try
{
Console.WriteLine("[ML] Inizio caricamento dati di training dal database...");
var trainingData = LoadTrainingDataFromDatabase(connectionString);
if (trainingData.Count == 0)
{
Console.WriteLine("[ML][ERRORE] Nessun dato di training disponibile nel database.");
return;
}
Console.WriteLine($"[ML] Numero record caricati per il training: {trainingData.Count}");
var dataView = _mlContext.Data.LoadFromEnumerable(trainingData);
// Suddividi i dati in train e test (80% train, 20% test)
var split = _mlContext.Data.TrainTestSplit(dataView, testFraction: 0.2, seed: 42);
// Costruisci la pipeline di training
var pipeline = _mlContext.Transforms.Concatenate("Features",
"Age", "HandicapRating", "Weight", "WeightCarried", "Barrier",
"CareerRuns", "CareerWins", "CareerStrikeRate", "CareerROI",
"ThisTrackRuns", "ThisTrackWins", "ThisTrackStrikeRate",
"ThisDistanceRuns", "ThisDistanceWins", "ThisDistanceStrikeRate",
"JockeyLast100Wins", "JockeyLast100StrikeRate",
"TrainerLast100Wins", "TrainerLast100StrikeRate", "BestFixedOdds",
"Rating1", "Rating2", "Rating3", "Rating4", "Rating5"
// Qui puoi aggiungere nuove feature create o derivate
)
.Append(_mlContext.Transforms.NormalizeMinMax("Features"))
.Append(_mlContext.Regression.Trainers.FastTree(
labelColumnName: "FinishPosition",
featureColumnName: "Features",
numberOfTrees: 200, // Aumentato per maggiore accuratezza
numberOfLeaves: 32, // Più foglie per maggiore complessità
minimumExampleCountPerLeaf: 5, // Più sensibile ai dettagli
learningRate: 0.15f // Puoi sperimentare anche questo parametro
));
Console.WriteLine("[ML] Inizio addestramento del modello...");
_trainedModel = pipeline.Fit(split.TrainSet);
Console.WriteLine("[ML] Addestramento completato.");
// Valuta sul training set
var trainMetrics = _mlContext.Regression.Evaluate(_trainedModel.Transform(split.TrainSet), labelColumnName: "FinishPosition");
// Valuta sul test set
var testMetrics = _mlContext.Regression.Evaluate(_trainedModel.Transform(split.TestSet), labelColumnName: "FinishPosition");
// Cross-validation
var cvResults = _mlContext.Regression.CrossValidate(data: dataView, estimator: pipeline, numberOfFolds: 5, labelColumnName: "FinishPosition");
var avgRSquared = cvResults.Average(fold => fold.Metrics.RSquared);
Console.WriteLine($"RSquared medio (cross-validation): {avgRSquared:F4}");
SaveModel(_trainedModel);
Console.WriteLine($"[ML] Modello salvato in: {_modelPath}");
Console.WriteLine("[ML] Metriche di valutazione (TRAIN):");
Console.WriteLine($"[ML] - RSquared: {trainMetrics.RSquared:F4}");
Console.WriteLine($"[ML] - MAE: {trainMetrics.MeanAbsoluteError:F4}");
Console.WriteLine($"[ML] - MSE: {trainMetrics.MeanSquaredError:F4}");
Console.WriteLine($"[ML] - RMSE: {trainMetrics.RootMeanSquaredError:F4}");
Console.WriteLine("[ML] Metriche di valutazione (TEST):");
Console.WriteLine($"[ML] - RSquared: {testMetrics.RSquared:F4}");
Console.WriteLine($"[ML] - MAE: {testMetrics.MeanAbsoluteError:F4}");
Console.WriteLine($"[ML] - MSE: {testMetrics.MeanSquaredError:F4}");
Console.WriteLine($"[ML] - RMSE: {testMetrics.RootMeanSquaredError:F4}");
var predictions = _trainedModel.Transform(split.TestSet);
var actuals = _mlContext.Data.CreateEnumerable<ModelInput>(split.TestSet, false).ToList();
var preds = _mlContext.Data.CreateEnumerable<ModelOutput>(predictions, false).ToList();
int top3Correct = actuals.Zip(preds, (a, p) => (a, p))
.Count(x => x.a.FinishPosition <= 3 && x.p.PredictedPosition <= 3);
double accuracyTop3 = (double)top3Correct / actuals.Count;
Console.WriteLine($"Percentuale cavalli previsti nei primi 3: {accuracyTop3:P2}");
}
catch (Exception ex)
{
Console.WriteLine($"[ML][ERRORE] Errore durante l'addestramento del modello: {ex.Message}");
throw;
}
}
/// <summary>
/// Carica i dati di training dal database
/// </summary>
private List<ModelInput> LoadTrainingDataFromDatabase(string connectionString)
{
var trainingData = new List<ModelInput>();
var calculator = new HorseRacingPredictor.Horses.Calculator();
int skippedRows = 0;
int rating1Errors = 0, rating2Errors = 0, rating3Errors = 0, rating4Errors = 0, rating5Errors = 0;
try
{
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string query = @"
SELECT
CASE WHEN ISNUMERIC(FinishResult) = 1 THEN CAST(FinishResult AS FLOAT) ELSE 999 END AS FinishPosition,
COALESCE(Age, 0) AS Age,
COALESCE(HandicapRating, 0) AS HandicapRating,
COALESCE(Weight, 0) AS Weight,
COALESCE(WeightCarried, 0) AS WeightCarried,
COALESCE(Barrier, 0) AS Barrier,
COALESCE(CareerRuns, 0) AS CareerRuns,
COALESCE(CareerWins, 0) AS CareerWins,
COALESCE(CareerStrikeRate, 0) AS CareerStrikeRate,
COALESCE(CareerROI, 0) AS CareerROI,
COALESCE(ThisTrackRuns, 0) AS ThisTrackRuns,
COALESCE(ThisTrackWins, 0) AS ThisTrackWins,
COALESCE(ThisTrackStrikeRate, 0) AS ThisTrackStrikeRate,
COALESCE(ThisDistanceRuns, 0) AS ThisDistanceRuns,
COALESCE(ThisDistanceWins, 0) AS ThisDistanceWins,
COALESCE(ThisDistanceStrikeRate, 0) AS ThisDistanceStrikeRate,
COALESCE(JockeyLast100Wins, 0) AS JockeyLast100Wins,
COALESCE(JockeyLast100StrikeRate, 0) AS JockeyLast100StrikeRate,
COALESCE(TrainerLast100Wins, 0) AS TrainerLast100Wins,
COALESCE(TrainerLast100StrikeRate, 0) AS TrainerLast100StrikeRate,
COALESCE(BestFixedOdds, 0) AS BestFixedOdds,
COALESCE(Gender, '') AS Gender
FROM Races
WHERE ISNUMERIC(FinishResult) = 1
AND FinishResult IS NOT NULL
AND FinishResult <> ''";
using (var command = new SqlCommand(query, connection))
{
using (var adapter = new SqlDataAdapter(command))
{
var table = new System.Data.DataTable();
adapter.Fill(table);
foreach (System.Data.DataRow row in table.Rows)
{
try {
float finishPos = Convert.ToSingle(row["FinishPosition"]);
if (finishPos >= 15 || finishPos <= 0) {
skippedRows++;
continue;
}
// Calcola i rating con gestione degli errori e sostituzione con valori predefiniti
float rating1 = 0, rating2 = 0, rating3 = 0, rating4 = 0, rating5 = 0;
try {
rating1 = (float)calculator.CalculateRating1(row);
if (float.IsNaN(rating1) || float.IsInfinity(rating1)) {
rating1 = 0;
rating1Errors++;
}
} catch (Exception) {
rating1Errors++;
}
try {
rating2 = (float)calculator.CalculateRating2(row);
if (float.IsNaN(rating2) || float.IsInfinity(rating2)) {
rating2 = 0;
rating2Errors++;
}
} catch (Exception) {
rating2Errors++;
}
try {
rating3 = (float)calculator.CalculateRating3(row);
if (float.IsNaN(rating3) || float.IsInfinity(rating3)) {
rating3 = 0;
rating3Errors++;
}
} catch (Exception) {
rating3Errors++;
}
try {
rating4 = (float)calculator.CalculateRating4(row);
if (float.IsNaN(rating4) || float.IsInfinity(rating4)) {
rating4 = 0;
rating4Errors++;
}
} catch (Exception) {
rating4Errors++;
}
try {
rating5 = (float)calculator.CalculateRating5(row);
if (float.IsNaN(rating5) || float.IsInfinity(rating5)) {
rating5 = 0;
rating5Errors++;
}
} catch (Exception) {
rating5Errors++;
}
// Usa SafeConvert per tutte le conversioni
trainingData.Add(new ModelInput
{
FinishPosition = finishPos,
Age = SafeConvert.ToSingle(row["Age"]),
HandicapRating = SafeConvert.ToSingle(row["HandicapRating"]),
Weight = SafeConvert.ToSingle(row["Weight"]),
WeightCarried = SafeConvert.ToSingle(row["WeightCarried"]),
Barrier = SafeConvert.ToSingle(row["Barrier"]),
CareerRuns = SafeConvert.ToSingle(row["CareerRuns"]),
CareerWins = SafeConvert.ToSingle(row["CareerWins"]),
CareerStrikeRate = SafeConvert.ToSingle(row["CareerStrikeRate"]),
CareerROI = SafeConvert.ToSingle(row["CareerROI"]),
ThisTrackRuns = SafeConvert.ToSingle(row["ThisTrackRuns"]),
ThisTrackWins = SafeConvert.ToSingle(row["ThisTrackWins"]),
ThisTrackStrikeRate = SafeConvert.ToSingle(row["ThisTrackStrikeRate"]),
ThisDistanceRuns = SafeConvert.ToSingle(row["ThisDistanceRuns"]),
ThisDistanceWins = SafeConvert.ToSingle(row["ThisDistanceWins"]),
ThisDistanceStrikeRate = SafeConvert.ToSingle(row["ThisDistanceStrikeRate"]),
JockeyLast100Wins = SafeConvert.ToSingle(row["JockeyLast100Wins"]),
JockeyLast100StrikeRate = SafeConvert.ToSingle(row["JockeyLast100StrikeRate"]),
TrainerLast100Wins = SafeConvert.ToSingle(row["TrainerLast100Wins"]),
TrainerLast100StrikeRate = SafeConvert.ToSingle(row["TrainerLast100StrikeRate"]),
BestFixedOdds = SafeConvert.ToSingle(row["BestFixedOdds"]),
Rating1 = rating1,
Rating2 = rating2,
Rating3 = rating3,
Rating4 = rating4,
Rating5 = rating5
});
} catch (Exception ex) {
skippedRows++;
}
}
Console.WriteLine($"[ML] Record letti dal database: {table.Rows.Count}");
Console.WriteLine($"[ML] Record saltati: {skippedRows}");
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"[ML][ERRORE] Errore durante il caricamento dei dati dal database: {ex.Message}");
}
Console.WriteLine($"[ML] Caricati {trainingData.Count} record per l'addestramento.");
return trainingData;
}
/// <summary>
/// Costruisce la pipeline di addestramento del modello
/// </summary>
private IEstimator<ITransformer> BuildTrainingPipeline()
{
// Crea la pipeline per addestramento
// 1. Concatena tutte le feature in un unico vettore
// 2. Normalizza i dati di input
// 3. Addestra un modello FastTree con FastTreeRegressionTrainer
var pipeline = _mlContext.Transforms.Concatenate("Features",
"Age", "HandicapRating", "Weight", "WeightCarried", "Barrier",
"CareerRuns", "CareerWins", "CareerStrikeRate", "CareerROI",
"ThisTrackRuns", "ThisTrackWins", "ThisTrackStrikeRate",
"ThisDistanceRuns", "ThisDistanceWins", "ThisDistanceStrikeRate",
"JockeyLast100Wins", "JockeyLast100StrikeRate",
"TrainerLast100Wins", "TrainerLast100StrikeRate", "BestFixedOdds",
"Rating1", "Rating2", "Rating3", "Rating4", "Rating5")
.Append(_mlContext.Transforms.NormalizeMinMax("Features"))
.Append(_mlContext.Regression.Trainers.FastTree(
labelColumnName: "FinishPosition",
featureColumnName: "Features",
numberOfTrees: 100,
numberOfLeaves: 20,
minimumExampleCountPerLeaf: 10));
return pipeline;
}
/// <summary>
/// Valuta il modello addestrato
/// </summary>
private RegressionMetrics EvaluateModel(IDataView dataView, ITransformer model)
{
var predictions = model.Transform(dataView);
return _mlContext.Regression.Evaluate(predictions, labelColumnName: "FinishPosition");
}
/// <summary>
/// Predice la posizione finale per un cavallo
/// </summary>
public ModelOutput PredictFinishPosition(ModelInput input)
{
if (_trainedModel == null)
{
Console.WriteLine("Il modello non è stato addestrato o caricato.");
// Restituisci un oggetto vuoto invece di lanciare un'eccezione
return new ModelOutput
{
PredictedPosition = 0,
WinProbability = 0,
PlaceProbability = 0
};
}
// Crea il motore di previsione
var predEngine = _mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(_trainedModel);
// Effettua la previsione
var prediction = predEngine.Predict(input);
// Calcola le probabilità (implementazione semplificata)
prediction.WinProbability = CalculateWinProbability(prediction.PredictedPosition);
prediction.PlaceProbability = CalculatePlaceProbability(prediction.PredictedPosition);
return prediction;
}
/// <summary>
/// Calcola la probabilità di vittoria in base alla posizione prevista
/// </summary>
private float CalculateWinProbability(float predictedPosition)
{
// Implementazione semplice: più il valore è vicino a 1, maggiore è la probabilità di vittoria
if (predictedPosition <= 1) return 0.9f;
if (predictedPosition <= 2) return 0.5f;
if (predictedPosition <= 3) return 0.3f;
if (predictedPosition <= 5) return 0.1f;
return 0.05f;
}
/// <summary>
/// Calcola la probabilità di piazzamento (primi 3) in base alla posizione prevista
/// </summary>
private float CalculatePlaceProbability(float predictedPosition)
{
// Implementazione semplice: più il valore è vicino a 1, 2 o 3, maggiore è la probabilità di piazzamento
if (predictedPosition <= 1) return 0.95f;
if (predictedPosition <= 2) return 0.85f;
if (predictedPosition <= 3) return 0.75f;
if (predictedPosition <= 4) return 0.4f;
if (predictedPosition <= 6) return 0.2f;
return 0.1f;
}
/// <summary>
/// Analizza un'intera corsa e prevede il risultato per tutti i cavalli
/// </summary>
public List<HorseRacePrediction> PredictRaceResult(string meeting, int raceNumber, DateTime raceDate, string connectionString)
{
var predictions = new List<HorseRacePrediction>();
var calculator = new HorseRacingPredictor.Horses.Calculator(); // Inizializza il calculator qui
try
{
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string query = @"
SELECT
Num, HorseName,
COALESCE(Age, 0) AS Age,
COALESCE(HandicapRating, 0) AS HandicapRating,
COALESCE(Weight, 0) AS Weight,
COALESCE(WeightCarried, 0) AS WeightCarried,
COALESCE(Barrier, 0) AS Barrier,
COALESCE(CareerRuns, 0) AS CareerRuns,
COALESCE(CareerWins, 0) AS CareerWins,
COALESCE(CareerStrikeRate, 0) AS CareerStrikeRate,
COALESCE(CareerROI, 0) AS CareerROI,
COALESCE(ThisTrackRuns, 0) AS ThisTrackRuns,
COALESCE(ThisTrackWins, 0) AS ThisTrackWins,
COALESCE(ThisTrackStrikeRate, 0) AS ThisTrackStrikeRate,
COALESCE(ThisDistanceRuns, 0) AS ThisDistanceRuns,
COALESCE(ThisDistanceWins, 0) AS ThisDistanceWins,
COALESCE(ThisDistanceStrikeRate, 0) AS ThisDistanceStrikeRate,
COALESCE(JockeyLast100Wins, 0) AS JockeyLast100Wins,
COALESCE(JockeyLast100StrikeRate, 0) AS JockeyLast100StrikeRate,
COALESCE(TrainerLast100Wins, 0) AS TrainerLast100Wins,
COALESCE(TrainerLast100StrikeRate, 0) AS TrainerLast100StrikeRate,
COALESCE(BestFixedOdds, 0) AS BestFixedOdds,
COALESCE(Gender, '') AS Gender
FROM Races
WHERE Meeting = @Meeting AND Race = @Race AND Data = @Data";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@Meeting", meeting);
command.Parameters.AddWithValue("@Race", raceNumber);
command.Parameters.AddWithValue("@Data", raceDate);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
// Converti i dati del reader in una DataRow per usare il calculator
DataTable dt = new DataTable();
DataRow row = dt.NewRow();
for (int i = 0; i < reader.FieldCount; i++)
{
if (!dt.Columns.Contains(reader.GetName(i)))
dt.Columns.Add(reader.GetName(i));
row[reader.GetName(i)] = reader.GetValue(i);
}
dt.Rows.Add(row);
// Prepara l'input per il modello
var input = new ModelInput
{
Age = Convert.ToSingle(reader["Age"]),
HandicapRating = Convert.ToSingle(reader["HandicapRating"]),
Weight = Convert.ToSingle(reader["Weight"]),
WeightCarried = Convert.ToSingle(reader["WeightCarried"]),
Barrier = Convert.ToSingle(reader["Barrier"]),
CareerRuns = Convert.ToSingle(reader["CareerRuns"]),
CareerWins = Convert.ToSingle(reader["CareerWins"]),
CareerStrikeRate = Convert.ToSingle(reader["CareerStrikeRate"]),
CareerROI = Convert.ToSingle(reader["CareerROI"]),
ThisTrackRuns = Convert.ToSingle(reader["ThisTrackRuns"]),
ThisTrackWins = Convert.ToSingle(reader["ThisTrackWins"]),
ThisTrackStrikeRate = Convert.ToSingle(reader["ThisTrackStrikeRate"]),
ThisDistanceRuns = Convert.ToSingle(reader["ThisDistanceRuns"]),
ThisDistanceWins = Convert.ToSingle(reader["ThisDistanceWins"]),
ThisDistanceStrikeRate = Convert.ToSingle(reader["ThisDistanceStrikeRate"]),
JockeyLast100Wins = Convert.ToSingle(reader["JockeyLast100Wins"]),
JockeyLast100StrikeRate = Convert.ToSingle(reader["JockeyLast100StrikeRate"]),
TrainerLast100Wins = Convert.ToSingle(reader["TrainerLast100Wins"]),
TrainerLast100StrikeRate = Convert.ToSingle(reader["TrainerLast100StrikeRate"]),
BestFixedOdds = Convert.ToSingle(reader["BestFixedOdds"]),
// Aggiungi i rating calcolati
Rating1 = (float)calculator.CalculateRating1(row),
Rating2 = (float)calculator.CalculateRating2(row),
Rating3 = (float)calculator.CalculateRating3(row),
Rating4 = (float)calculator.CalculateRating4(row),
Rating5 = (float)calculator.CalculateRating5(row)
};
// Effettua la previsione
var output = PredictFinishPosition(input);
// Calcola un rating ML basato sulla posizione prevista
int mlRating = CalculateMLRating(output);
// Aggiungi alla lista delle previsioni
predictions.Add(new HorseRacePrediction
{
HorseId = Convert.ToString(reader["Num"]),
HorseName = Convert.ToString(reader["HorseName"]),
RaceMeeting = meeting,
RaceNumber = raceNumber,
RaceDate = raceDate,
PredictedPosition = output.PredictedPosition,
WinProbability = output.WinProbability,
PlaceProbability = output.PlaceProbability,
MLRating = mlRating,
CurrentOdds = Convert.ToSingle(reader["BestFixedOdds"])
});
}
}
}
}
// Ordina per posizione prevista (dal migliore al peggiore)
return predictions.OrderBy(p => p.PredictedPosition).ToList();
}
catch (Exception ex)
{
Console.WriteLine($"[ML][ERRORE] Errore durante la previsione della corsa: {ex.Message}");
return predictions;
}
}
/// <summary>
/// Calcola un rating ML basato sulla posizione prevista e altre metriche
/// </summary>
private int CalculateMLRating(ModelOutput output)
{
// Formula per il rating ML (convertito in un numero intero da 0 a 100)
double baseRating = (1 / Math.Max(0.1, output.PredictedPosition)) * 100;
double winProbabilityFactor = output.WinProbability * 100;
double placeProbabilityFactor = output.PlaceProbability * 50;
// Combina i fattori con pesi diversi
double combinedRating = (baseRating * 0.4) + (winProbabilityFactor * 0.4) + (placeProbabilityFactor * 0.2);
// Limita il valore tra 0 e 100
return (int)Math.Min(100, Math.Max(0, combinedRating));
}
// Metodo di utilità per convertire in modo sicuro i valori
private static class SafeConvert
{
public static float ToSingle(object value, float defaultValue = 0)
{
if (value == null || value == DBNull.Value)
return defaultValue;
try {
float result = Convert.ToSingle(value);
return float.IsNaN(result) || float.IsInfinity(result) ? defaultValue : result;
}
catch {
return defaultValue;
}
}
}
}
}

View File

@@ -1,109 +0,0 @@
using Microsoft.ML.Data;
namespace HorseRacingPredictor.Horses.ML
{
/// <summary>
/// Classe che rappresenta i dati di input per l'addestramento del modello ML.
/// Contiene le feature selezionate dalle corse passate.
/// </summary>
public class ModelInput
{
// Etichetta da prevedere: posizione finale (1 = vincitore, 2 = secondo posto, ecc.)
[LoadColumn(0)]
[ColumnName("FinishPosition")]
public float FinishPosition { get; set; }
// Feature principali sul cavallo
[LoadColumn(1)]
[ColumnName("Age")]
public float Age { get; set; }
[LoadColumn(2)]
[ColumnName("HandicapRating")]
public float HandicapRating { get; set; }
[LoadColumn(3)]
[ColumnName("Weight")]
public float Weight { get; set; }
[LoadColumn(4)]
[ColumnName("WeightCarried")]
public float WeightCarried { get; set; }
[LoadColumn(5)]
[ColumnName("Barrier")]
public float Barrier { get; set; }
// Statistiche di carriera
[LoadColumn(6)]
[ColumnName("CareerRuns")]
public float CareerRuns { get; set; }
[LoadColumn(7)]
[ColumnName("CareerWins")]
public float CareerWins { get; set; }
[LoadColumn(8)]
[ColumnName("CareerStrikeRate")]
public float CareerStrikeRate { get; set; }
[LoadColumn(9)]
[ColumnName("CareerROI")]
public float CareerROI { get; set; }
// Statistiche di pista
[LoadColumn(10)]
[ColumnName("ThisTrackRuns")]
public float ThisTrackRuns { get; set; }
[LoadColumn(11)]
[ColumnName("ThisTrackWins")]
public float ThisTrackWins { get; set; }
[LoadColumn(12)]
[ColumnName("ThisTrackStrikeRate")]
public float ThisTrackStrikeRate { get; set; }
[LoadColumn(13)]
[ColumnName("ThisDistanceRuns")]
public float ThisDistanceRuns { get; set; }
[LoadColumn(14)]
[ColumnName("ThisDistanceWins")]
public float ThisDistanceWins { get; set; }
[LoadColumn(15)]
[ColumnName("ThisDistanceStrikeRate")]
public float ThisDistanceStrikeRate { get; set; }
// Statistiche fantino
[LoadColumn(16)]
[ColumnName("JockeyLast100Wins")]
public float JockeyLast100Wins { get; set; }
[LoadColumn(17)]
[ColumnName("JockeyLast100StrikeRate")]
public float JockeyLast100StrikeRate { get; set; }
// Statistiche allenatore
[LoadColumn(18)]
[ColumnName("TrainerLast100Wins")]
public float TrainerLast100Wins { get; set; }
[LoadColumn(19)]
[ColumnName("TrainerLast100StrikeRate")]
public float TrainerLast100StrikeRate { get; set; }
// Quota iniziale (può essere predittiva)
[LoadColumn(20)]
[ColumnName("BestFixedOdds")]
public float BestFixedOdds { get; set; }
// Cambia il tipo da int a float
public float Rating1 { get; set; }
public float Rating2 { get; set; }
public float Rating3 { get; set; }
public float Rating4 { get; set; }
public float Rating5 { get; set; }
}
}

View File

@@ -1,20 +0,0 @@
using Microsoft.ML.Data;
namespace HorseRacingPredictor.Horses.ML
{
/// <summary>
/// Classe che rappresenta l'output della previsione del modello ML.
/// </summary>
public class ModelOutput
{
// Previsione: il risultato della classificazione (1 = vincitore, 2 = secondo posto, ecc.)
[ColumnName("Score")]
public float PredictedPosition { get; set; }
[NoColumn] // Indica che questa proprietà non esiste nei dati
public float WinProbability { get; set; }
[NoColumn] // Indica che questa proprietà non esiste nei dati
public float PlaceProbability { get; set; }
}
}

View File

@@ -1,88 +0,0 @@
/****** Object: StoredProcedure [dbo].[GetFavoriteHorses] Script Date: 17/07/2025 09:09:35 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[GetFavoriteHorses]
@Date DATETIME = NULL,
@Meeting NVARCHAR(100) = NULL,
@RaceNumber INT = NULL,
@TopN INT = 3,
@Weight_HandicapRating DECIMAL(10,2) = 1,
@Weight_CareerWins DECIMAL(10,2) = 1,
@Weight_CareerStrikeRate DECIMAL(10,2) = 1,
@Weight_JockeyLast100StrikeRate DECIMAL(10,2) = 1,
@Weight_TrainerLast100StrikeRate DECIMAL(10,2) = 1,
@Weight_BestFixedOdds DECIMAL(10,2) = 1,
@Weight_Age DECIMAL(10,2) = 1,
@Weight_CareerPlacings DECIMAL(10,2) = 1,
@Weight_DryTrackStrikeRate DECIMAL(10,2) = 1,
@Weight_ThisTrackStrikeRate DECIMAL(10,2) = 1,
@Weight_LastStartFinishPosition DECIMAL(10,2) = 1,
@Weight_WeightCarried DECIMAL(10,2) = 1
AS
BEGIN
SET NOCOUNT ON;
-- Crea tabella temporanea per i risultati
CREATE TABLE #Results (
DataCorsa NVARCHAR(10),
LuogoCorsa NVARCHAR(100),
NumeroCorsa INT,
NomeCavallo NVARCHAR(100),
Punteggio DECIMAL(18,4),
RisultatoFinale NVARCHAR(50),
PosizioneArrivo INT,
PrevistoVincente BIT
);
WITH Scoring AS (
SELECT
Data,
Meeting,
Race,
HorseName,
-- Calcolo del punteggio pesato con le nuove statistiche
(
ISNULL(HandicapRating,0) * @Weight_HandicapRating +
ISNULL(CareerWins,0) * @Weight_CareerWins +
ISNULL(CareerStrikeRate,0) * @Weight_CareerStrikeRate +
ISNULL(JockeyLast100StrikeRate,0) * @Weight_JockeyLast100StrikeRate +
ISNULL(TrainerLast100StrikeRate,0) * @Weight_TrainerLast100StrikeRate +
ISNULL(BestFixedOdds,0) * @Weight_BestFixedOdds +
ISNULL(Age,0) * @Weight_Age +
ISNULL(CareerPlacings,0) * @Weight_CareerPlacings +
ISNULL(DryTrackStrikeRate,0) * @Weight_DryTrackStrikeRate +
ISNULL(ThisTrackStrikeRate,0) * @Weight_ThisTrackStrikeRate +
-- Per LastStartFinishPosition, valori più bassi sono migliori, quindi invertiamo il segno
(CASE WHEN TRY_CAST(LastStartFinishPosition AS INT) > 0 THEN (1.0 / TRY_CAST(LastStartFinishPosition AS FLOAT)) ELSE 0 END) * @Weight_LastStartFinishPosition +
ISNULL(WeightCarried,0) * @Weight_WeightCarried
) AS Score,
FinishResult
FROM Races
WHERE (FinishResult IS NOT NULL AND FinishResult <> '')
),
Ranked AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY Data, Meeting, Race ORDER BY Score DESC) AS RankByScore
FROM Scoring
)
INSERT INTO #Results
SELECT
CONVERT(VARCHAR(10), Data, 120),
Meeting,
Race,
HorseName,
Score,
FinishResult,
CASE WHEN ISNUMERIC(FinishResult) = 1 THEN CAST(FinishResult AS INT) ELSE NULL END,
CASE WHEN RankByScore = 1 THEN 1 ELSE 0 END
FROM Ranked
WHERE RankByScore = 1;
SELECT * FROM #Results;
DROP TABLE #Results;
END

View File

@@ -1,27 +0,0 @@
/****** Object: View [dbo].[GetValidHorses] Script Date: 17/07/2025 09:10:07 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE VIEW [dbo].[GetValidHorses]
AS
SELECT Data, Meeting, Race, Num, HorseName, Age, Gender, HandicapRating, CareerRuns, CareerWins, CareerStrikeRate, CareerROI, CareerPlacings, CareerPlaceStrikeRate, DryTrackRuns, DryTrackWins, DryTrackStrikeRate,
DryTrackROI, WetTrackRuns, WetTrackWins, WetTrackStrikeRate, WetTrackROI, AveragePrizeMoney, CareerPrizeMoney, BestFixedOdds, BetEasyOdds, Weight, WeightCarried, Barrier, PrizeMoney, ThisTrackRuns,
ThisTrackWins, ThisTrackStrikeRate, ThisTrackROI, ThisTrackPlaces, ThisTrackPlaceStrikeRate, ThisDistanceRuns, ThisDistanceWins, ThisDistanceStrikeRate, ThisDistanceROI, ThisDistancePlaces,
ThisDistancePlaceStrikeRate, ThisTrackDistanceRuns, ThisTrackDistanceWins, ThisTrackDistanceStrikeRate, ThisTrackDistanceROI, ThisTrackDistancePlaces, ThisTrackDistancePlaceStrikeRate, ThisConditionRuns,
ThisConditionWins, ThisConditionStrikeRate, ThisConditionROI, ThisConditionPlaces, ThisConditionPlaceStrikeRate, Jockey, Apprentice, JockeyWeightClaim, JockeyLast100HorseEarnings, JockeyLast100AvgHorseEarnings,
JockeyLast100Starts, JockeyLast100Wins, JockeyLast100StrikeRate, JockeyLast100ROI, JockeyLast100Places, JockeyLast100PlaceStrikeRate, Jockey12MonthHorseEarnings, Jockey12MonthAvgHorseEarnings,
Jockey12MonthsStarts, Jockey12MonthsWins, Jockey12MonthsStrikeRate, Jockey12MonthsROI, Jockey12MonthsPlaces, Jockey12MonthsPlaceStrikeRate, JockeyThisSeasonHorseEarnings,
JockeyThisSeasonAvgHorseEarnings, JockeyThisSeasonStarts, JockeyThisSeasonWins, JockeyThisSeasonStrikeRate, JockeyThisSeasonROI, JockeyThisSeasonPlaces, JockeyThisSeasonPlaceStrikeRate,
JockeyLastSeasonHorseEarnings, JockeyLastSeasonAvgHorseEarnings, JockeyLastSeasonStarts, JockeyLastSeasonWins, JockeyLastSeasonStrikeRate, JockeyLastSeasonROI, JockeyLastSeasonPlaces,
JockeyLastSeasonPlaceStrikeRate, Trainer, TrainerLast100HorseEarnings, TrainerLast100AvgHorseEarnings, TrainerLast100Starts, TrainerLast100Wins, TrainerLast100StrikeRate, TrainerLast100ROI, TrainerLast100Places,
TrainerLast100PlaceStrikeRate, Trainer12MonthHorseEarnings, Trainer12MonthAvgHorseEarnings, Trainer12MonthsStarts, Trainer12MonthsWins, Trainer12MonthsStrikeRate, Trainer12MonthsROI, Trainer12MonthsPlaces,
Trainer12MonthsPlaceStrikeRate, TrainerThisSeasonHorseEarnings, TrainerThisSeasonAvgHorseEarnings, TrainerThisSeasonStarts, TrainerThisSeasonWins, TrainerThisSeasonStrikeRate, TrainerThisSeasonROI,
TrainerThisSeasonPlaces, TrainerThisSeasonPlaceStrikeRate, TrainerLastSeasonHorseEarnings, TrainerLastSeasonAvgHorseEarnings, TrainerLastSeasonStarts, TrainerLastSeasonWins, TrainerLastSeasonStrikeRate,
TrainerLastSeasonROI, TrainerLastSeasonPlaces, TrainerLastSeasonPlaceStrikeRate, LastStartFinishPosition, LastStartMargin, LastStartDistance, LastStartDistanceChange, LastStartPrizeMoney, FinishResult
FROM dbo.Races
WHERE (FinishResult <> '') OR
(FinishResult IS NOT NULL)
GO

View File

@@ -1,20 +0,0 @@
CREATE TABLE [dbo].[PredictionScanResults] (
ScanId INT IDENTITY(1,1) PRIMARY KEY,
SessionId UNIQUEIDENTIFIER NOT NULL,
ScanDate DATETIME NOT NULL DEFAULT GETDATE(),
Weight_HandicapRating DECIMAL(10,2) NOT NULL,
Weight_CareerWins DECIMAL(10,2) NOT NULL,
Weight_CareerStrikeRate DECIMAL(10,2) NOT NULL,
Weight_JockeyLast100StrikeRate DECIMAL(10,2) NOT NULL,
Weight_TrainerLast100StrikeRate DECIMAL(10,2) NOT NULL,
Weight_BestFixedOdds DECIMAL(10,2) NOT NULL,
Weight_Age DECIMAL(10,2) NOT NULL,
Weight_CareerPlacings DECIMAL(10,2) NOT NULL,
Weight_DryTrackStrikeRate DECIMAL(10,2) NOT NULL,
Weight_ThisTrackStrikeRate DECIMAL(10,2) NOT NULL,
Weight_LastStartFinishPosition DECIMAL(10,2) NOT NULL,
Weight_WeightCarried DECIMAL(10,2) NOT NULL,
TotalRaces INT NOT NULL,
CorrectPredictions INT NOT NULL,
WinPercentage DECIMAL(5,2) NOT NULL
)

View File

@@ -1,152 +0,0 @@
/****** Object: Table [dbo].[Races] Script Date: 16/07/2025 23:27:12 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Races](
[Data] [datetime] NOT NULL,
[Meeting] [nvarchar](100) NOT NULL,
[Race] [int] NOT NULL,
[Num] [int] NOT NULL,
[FileName] [nvarchar](255) NULL,
[HorseName] [nvarchar](100) NULL,
[Age] [int] NULL,
[Gender] [nvarchar](10) NULL,
[HandicapRating] [decimal](10, 2) NULL,
[CareerRuns] [int] NULL,
[CareerWins] [int] NULL,
[CareerStrikeRate] [decimal](10, 2) NULL,
[CareerROI] [decimal](10, 2) NULL,
[CareerPlacings] [int] NULL,
[CareerPlaceStrikeRate] [decimal](10, 2) NULL,
[DryTrackRuns] [int] NULL,
[DryTrackWins] [int] NULL,
[DryTrackStrikeRate] [decimal](10, 2) NULL,
[DryTrackROI] [decimal](10, 2) NULL,
[WetTrackRuns] [int] NULL,
[WetTrackWins] [int] NULL,
[WetTrackStrikeRate] [decimal](10, 2) NULL,
[WetTrackROI] [decimal](10, 2) NULL,
[AveragePrizeMoney] [decimal](15, 2) NULL,
[CareerPrizeMoney] [decimal](15, 2) NULL,
[BestFixedOdds] [decimal](10, 2) NULL,
[BetEasyOdds] [decimal](10, 2) NULL,
[Weight] [decimal](10, 2) NULL,
[WeightCarried] [decimal](10, 2) NULL,
[Barrier] [nvarchar](10) NULL,
[PrizeMoney] [decimal](15, 2) NULL,
[ThisTrackRuns] [int] NULL,
[ThisTrackWins] [int] NULL,
[ThisTrackStrikeRate] [decimal](10, 2) NULL,
[ThisTrackROI] [decimal](10, 2) NULL,
[ThisTrackPlaces] [int] NULL,
[ThisTrackPlaceStrikeRate] [decimal](10, 2) NULL,
[ThisDistanceRuns] [int] NULL,
[ThisDistanceWins] [int] NULL,
[ThisDistanceStrikeRate] [decimal](10, 2) NULL,
[ThisDistanceROI] [decimal](10, 2) NULL,
[ThisDistancePlaces] [int] NULL,
[ThisDistancePlaceStrikeRate] [decimal](10, 2) NULL,
[ThisTrackDistanceRuns] [int] NULL,
[ThisTrackDistanceWins] [int] NULL,
[ThisTrackDistanceStrikeRate] [decimal](10, 2) NULL,
[ThisTrackDistanceROI] [decimal](10, 2) NULL,
[ThisTrackDistancePlaces] [int] NULL,
[ThisTrackDistancePlaceStrikeRate] [decimal](10, 2) NULL,
[ThisConditionRuns] [int] NULL,
[ThisConditionWins] [int] NULL,
[ThisConditionStrikeRate] [decimal](10, 2) NULL,
[ThisConditionROI] [decimal](10, 2) NULL,
[ThisConditionPlaces] [int] NULL,
[ThisConditionPlaceStrikeRate] [decimal](10, 2) NULL,
[Jockey] [nvarchar](100) NULL,
[Apprentice] [nvarchar](10) NULL,
[JockeyWeightClaim] [decimal](10, 2) NULL,
[JockeyLast100HorseEarnings] [decimal](15, 2) NULL,
[JockeyLast100AvgHorseEarnings] [decimal](15, 2) NULL,
[JockeyLast100Starts] [int] NULL,
[JockeyLast100Wins] [int] NULL,
[JockeyLast100StrikeRate] [decimal](10, 2) NULL,
[JockeyLast100ROI] [decimal](10, 2) NULL,
[JockeyLast100Places] [int] NULL,
[JockeyLast100PlaceStrikeRate] [decimal](10, 2) NULL,
[Jockey12MonthHorseEarnings] [decimal](15, 2) NULL,
[Jockey12MonthAvgHorseEarnings] [decimal](15, 2) NULL,
[Jockey12MonthsStarts] [int] NULL,
[Jockey12MonthsWins] [int] NULL,
[Jockey12MonthsStrikeRate] [decimal](10, 2) NULL,
[Jockey12MonthsROI] [decimal](10, 2) NULL,
[Jockey12MonthsPlaces] [int] NULL,
[Jockey12MonthsPlaceStrikeRate] [decimal](10, 2) NULL,
[JockeyThisSeasonHorseEarnings] [decimal](15, 2) NULL,
[JockeyThisSeasonAvgHorseEarnings] [decimal](15, 2) NULL,
[JockeyThisSeasonStarts] [int] NULL,
[JockeyThisSeasonWins] [int] NULL,
[JockeyThisSeasonStrikeRate] [decimal](10, 2) NULL,
[JockeyThisSeasonROI] [decimal](10, 2) NULL,
[JockeyThisSeasonPlaces] [int] NULL,
[JockeyThisSeasonPlaceStrikeRate] [decimal](10, 2) NULL,
[JockeyLastSeasonHorseEarnings] [decimal](15, 2) NULL,
[JockeyLastSeasonAvgHorseEarnings] [decimal](15, 2) NULL,
[JockeyLastSeasonStarts] [int] NULL,
[JockeyLastSeasonWins] [int] NULL,
[JockeyLastSeasonStrikeRate] [decimal](10, 2) NULL,
[JockeyLastSeasonROI] [decimal](10, 2) NULL,
[JockeyLastSeasonPlaces] [int] NULL,
[JockeyLastSeasonPlaceStrikeRate] [decimal](10, 2) NULL,
[Trainer] [nvarchar](100) NULL,
[TrainerLast100HorseEarnings] [decimal](15, 2) NULL,
[TrainerLast100AvgHorseEarnings] [decimal](15, 2) NULL,
[TrainerLast100Starts] [int] NULL,
[TrainerLast100Wins] [int] NULL,
[TrainerLast100StrikeRate] [decimal](10, 2) NULL,
[TrainerLast100ROI] [decimal](10, 2) NULL,
[TrainerLast100Places] [int] NULL,
[TrainerLast100PlaceStrikeRate] [decimal](10, 2) NULL,
[Trainer12MonthHorseEarnings] [decimal](15, 2) NULL,
[Trainer12MonthAvgHorseEarnings] [decimal](15, 2) NULL,
[Trainer12MonthsStarts] [int] NULL,
[Trainer12MonthsWins] [int] NULL,
[Trainer12MonthsStrikeRate] [decimal](10, 2) NULL,
[Trainer12MonthsROI] [decimal](10, 2) NULL,
[Trainer12MonthsPlaces] [int] NULL,
[Trainer12MonthsPlaceStrikeRate] [decimal](10, 2) NULL,
[TrainerThisSeasonHorseEarnings] [decimal](15, 2) NULL,
[TrainerThisSeasonAvgHorseEarnings] [decimal](15, 2) NULL,
[TrainerThisSeasonStarts] [int] NULL,
[TrainerThisSeasonWins] [int] NULL,
[TrainerThisSeasonStrikeRate] [decimal](10, 2) NULL,
[TrainerThisSeasonROI] [decimal](10, 2) NULL,
[TrainerThisSeasonPlaces] [int] NULL,
[TrainerThisSeasonPlaceStrikeRate] [decimal](10, 2) NULL,
[TrainerLastSeasonHorseEarnings] [decimal](15, 2) NULL,
[TrainerLastSeasonAvgHorseEarnings] [decimal](15, 2) NULL,
[TrainerLastSeasonStarts] [int] NULL,
[TrainerLastSeasonWins] [int] NULL,
[TrainerLastSeasonStrikeRate] [decimal](10, 2) NULL,
[TrainerLastSeasonROI] [decimal](10, 2) NULL,
[TrainerLastSeasonPlaces] [int] NULL,
[TrainerLastSeasonPlaceStrikeRate] [decimal](10, 2) NULL,
[LastStartFinishPosition] [nvarchar](10) NULL,
[LastStartMargin] [decimal](10, 2) NULL,
[LastStartDistance] [decimal](10, 2) NULL,
[LastStartDistanceChange] [decimal](10, 2) NULL,
[LastStartPrizeMoney] [decimal](15, 2) NULL,
[FormGuideUrl] [nvarchar](500) NULL,
[HorseProfileUrl] [nvarchar](500) NULL,
[JockeyProfileUrl] [nvarchar](500) NULL,
[TrainerProfileUrl] [nvarchar](500) NULL,
[FinishResult] [nvarchar](50) NULL,
[Created] [datetime] NOT NULL,
[Updated] [datetime] NOT NULL,
CONSTRAINT [PK_Races] PRIMARY KEY CLUSTERED
(
[Data] ASC,
[Meeting] ASC,
[Race] ASC,
[Num] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

View File

@@ -1,228 +0,0 @@
ALTER PROCEDURE [dbo].[ScanFavoriteHorseWeights]
AS
BEGIN
SET NOCOUNT ON;
DECLARE @MinValue INT = 1
DECLARE @MaxValue INT = 3 -- Modifica per range più ampi
DECLARE @SessionId UNIQUEIDENTIFIER = NEWID()
DECLARE @ScanDate DATETIME = GETDATE()
-- Parametri
DECLARE @Weight_HandicapRating INT
DECLARE @Weight_CareerWins INT
DECLARE @Weight_CareerStrikeRate INT
DECLARE @Weight_JockeyLast100StrikeRate INT
DECLARE @Weight_TrainerLast100StrikeRate INT
DECLARE @Weight_BestFixedOdds INT
DECLARE @Weight_Age INT
DECLARE @Weight_CareerPlacings INT
DECLARE @Weight_DryTrackStrikeRate INT
DECLARE @Weight_ThisTrackStrikeRate INT
DECLARE @Weight_LastStartFinishPosition INT
DECLARE @Weight_WeightCarried INT
DECLARE @TotalRaces INT, @CorrectPredictions INT, @WinPercentage DECIMAL(5,2)
DECLARE @CombinazioniTotali BIGINT
DECLARE @CombinazioneCorrente BIGINT = 0
-- Carica tutti i cavalli validi in memoria
IF OBJECT_ID('tempdb..#AllHorses') IS NOT NULL DROP TABLE #AllHorses
SELECT
Data,
Meeting,
Race,
HorseName,
HandicapRating,
CareerWins,
CareerStrikeRate,
JockeyLast100StrikeRate,
TrainerLast100StrikeRate,
BestFixedOdds,
Age,
CareerPlacings,
DryTrackStrikeRate,
ThisTrackStrikeRate,
LastStartFinishPosition,
WeightCarried,
FinishResult
INTO #AllHorses
FROM Races
WHERE (FinishResult IS NOT NULL AND FinishResult <> '')
-- Conta il numero di corse distinte
SELECT @TotalRaces = COUNT(DISTINCT
CONVERT(NVARCHAR(20), Data, 120) + '_' + Meeting + '_' + CAST(Race AS NVARCHAR(10))
)
FROM #AllHorses
-- Calcola il numero totale di combinazioni
SET @CombinazioniTotali = POWER(@MaxValue - @MinValue + 1, 12)
-- Inizializza la sessione con un record "vuoto"
INSERT INTO PredictionScanResults (
SessionId, ScanDate,
Weight_HandicapRating, Weight_CareerWins, Weight_CareerStrikeRate, Weight_JockeyLast100StrikeRate,
Weight_TrainerLast100StrikeRate, Weight_BestFixedOdds, Weight_Age, Weight_CareerPlacings,
Weight_DryTrackStrikeRate, Weight_ThisTrackStrikeRate, Weight_LastStartFinishPosition, Weight_WeightCarried,
TotalRaces, CorrectPredictions, WinPercentage
) VALUES (
@SessionId, @ScanDate,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0
)
SET @Weight_HandicapRating = @MinValue
WHILE @Weight_HandicapRating <= @MaxValue
BEGIN
SET @Weight_CareerWins = @MinValue
WHILE @Weight_CareerWins <= @MaxValue
BEGIN
SET @Weight_CareerStrikeRate = @MinValue
WHILE @Weight_CareerStrikeRate <= @MaxValue
BEGIN
SET @Weight_JockeyLast100StrikeRate = @MinValue
WHILE @Weight_JockeyLast100StrikeRate <= @MaxValue
BEGIN
SET @Weight_TrainerLast100StrikeRate = @MinValue
WHILE @Weight_TrainerLast100StrikeRate <= @MaxValue
BEGIN
SET @Weight_BestFixedOdds = @MinValue
WHILE @Weight_BestFixedOdds <= @MaxValue
BEGIN
SET @Weight_Age = @MinValue
WHILE @Weight_Age <= @MaxValue
BEGIN
SET @Weight_CareerPlacings = @MinValue
WHILE @Weight_CareerPlacings <= @MaxValue
BEGIN
SET @Weight_DryTrackStrikeRate = @MinValue
WHILE @Weight_DryTrackStrikeRate <= @MaxValue
BEGIN
SET @Weight_ThisTrackStrikeRate = @MinValue
WHILE @Weight_ThisTrackStrikeRate <= @MaxValue
BEGIN
SET @Weight_LastStartFinishPosition = @MinValue
WHILE @Weight_LastStartFinishPosition <= @MaxValue
BEGIN
SET @Weight_WeightCarried = @MinValue
WHILE @Weight_WeightCarried <= @MaxValue
BEGIN
SET @CombinazioneCorrente = @CombinazioneCorrente + 1
-- Calcolo in memoria: trova il cavallo con punteggio massimo per ogni corsa
IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results
SELECT
Data,
Meeting,
Race,
HorseName,
(
ISNULL(HandicapRating,0) * @Weight_HandicapRating +
ISNULL(CareerWins,0) * @Weight_CareerWins +
ISNULL(CareerStrikeRate,0) * @Weight_CareerStrikeRate +
ISNULL(JockeyLast100StrikeRate,0) * @Weight_JockeyLast100StrikeRate +
ISNULL(TrainerLast100StrikeRate,0) * @Weight_TrainerLast100StrikeRate +
ISNULL(BestFixedOdds,0) * @Weight_BestFixedOdds +
ISNULL(Age,0) * @Weight_Age +
ISNULL(CareerPlacings,0) * @Weight_CareerPlacings +
ISNULL(DryTrackStrikeRate,0) * @Weight_DryTrackStrikeRate +
ISNULL(ThisTrackStrikeRate,0) * @Weight_ThisTrackStrikeRate +
(CASE WHEN TRY_CAST(LastStartFinishPosition AS INT) > 0 THEN (1.0 / TRY_CAST(LastStartFinishPosition AS FLOAT)) ELSE 0 END) * @Weight_LastStartFinishPosition +
ISNULL(WeightCarried,0) * @Weight_WeightCarried
) AS Score,
FinishResult
INTO #Results
FROM #AllHorses
-- Seleziona il cavallo con punteggio massimo per ogni corsa
;WITH Ranked AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY Data, Meeting, Race ORDER BY Score DESC) AS RankByScore
FROM #Results
)
SELECT *
INTO #TopResults
FROM Ranked
WHERE RankByScore = 1
-- Calcola le statistiche
SELECT
@CorrectPredictions = SUM(CASE WHEN ISNUMERIC(FinishResult) = 1 AND CAST(FinishResult AS INT) = 1 THEN 1 ELSE 0 END)
FROM #TopResults
-- @TotalRaces già calcolato all'inizio
IF @TotalRaces > 0
SET @WinPercentage = CAST(@CorrectPredictions AS DECIMAL(5,2)) * 100.0 / @TotalRaces
ELSE
SET @WinPercentage = 0
-- Aggiorna solo se la percentuale è migliore
IF EXISTS (
SELECT 1 FROM PredictionScanResults
WHERE SessionId = @SessionId
AND WinPercentage < @WinPercentage
)
BEGIN
UPDATE PredictionScanResults
SET
Weight_HandicapRating = @Weight_HandicapRating,
Weight_CareerWins = @Weight_CareerWins,
Weight_CareerStrikeRate = @Weight_CareerStrikeRate,
Weight_JockeyLast100StrikeRate = @Weight_JockeyLast100StrikeRate,
Weight_TrainerLast100StrikeRate = @Weight_TrainerLast100StrikeRate,
Weight_BestFixedOdds = @Weight_BestFixedOdds,
Weight_Age = @Weight_Age,
Weight_CareerPlacings = @Weight_CareerPlacings,
Weight_DryTrackStrikeRate = @Weight_DryTrackStrikeRate,
Weight_ThisTrackStrikeRate = @Weight_ThisTrackStrikeRate,
Weight_LastStartFinishPosition = @Weight_LastStartFinishPosition,
Weight_WeightCarried = @Weight_WeightCarried,
TotalRaces = @TotalRaces,
CorrectPredictions = @CorrectPredictions,
WinPercentage = @WinPercentage
WHERE SessionId = @SessionId
END
-- Output avanzamento
PRINT CONCAT(
'Combinazione ', @CombinazioneCorrente, '/', @CombinazioniTotali,
' (', CAST((@CombinazioneCorrente * 100.0 / @CombinazioniTotali) AS DECIMAL(5,2)), '%) - ',
'Pesi: ', @Weight_HandicapRating, ',', @Weight_CareerWins, ',', @Weight_CareerStrikeRate, ',',
@Weight_JockeyLast100StrikeRate, ',', @Weight_TrainerLast100StrikeRate, ',', @Weight_BestFixedOdds, ',',
@Weight_Age, ',', @Weight_CareerPlacings, ',', @Weight_DryTrackStrikeRate, ',', @Weight_ThisTrackStrikeRate, ',',
@Weight_LastStartFinishPosition, ',', @Weight_WeightCarried,
' - Win%: ', @WinPercentage
)
DROP TABLE IF EXISTS #Results
DROP TABLE IF EXISTS #TopResults
SET @Weight_WeightCarried = @Weight_WeightCarried + 1
END
SET @Weight_LastStartFinishPosition = @Weight_LastStartFinishPosition + 1
END
SET @Weight_ThisTrackStrikeRate = @Weight_ThisTrackStrikeRate + 1
END
SET @Weight_DryTrackStrikeRate = @Weight_DryTrackStrikeRate + 1
END
SET @Weight_CareerPlacings = @Weight_CareerPlacings + 1
END
SET @Weight_Age = @Weight_Age + 1
END
SET @Weight_BestFixedOdds = @Weight_BestFixedOdds + 1
END
SET @Weight_TrainerLast100StrikeRate = @Weight_TrainerLast100StrikeRate + 1
END
SET @Weight_JockeyLast100StrikeRate = @Weight_JockeyLast100StrikeRate + 1
END
SET @Weight_CareerStrikeRate = @Weight_CareerStrikeRate + 1
END
SET @Weight_CareerWins = @Weight_CareerWins + 1
END
SET @Weight_HandicapRating = @Weight_HandicapRating + 1
END
END