response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/racecards/{race_id}/pro?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/racecards/{race_id}/pro?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/results":{"get":{"tags":["Results","Standard Plan","Pro Plan"],"summary":"Results","description":"Get historic results for all races in the last 12 months
| Min. Required Plan | Standard |
| Rate Limit | 5 requests per second |
| Add-ons | [Optional] Core database historical results add-on: users on the Pro Plan can pay a one-time fee of £499 to remove the 12 month restriction and access over 30 years of results from this endpoint. Contact support@theracingapi.com for information. |
Efficiently querying and exporting results data
While the parameters for this endpoint allow for a wide date range to be set (utilising 'start_date' and 'end_date'), the necessary use of 'skip' to access all results for a wide date range query will reduce perfomance the larger that range is.
ℹ️ The most efficient way to query results data over time is to build a list of dates within your date range, then make queries for each date in that list. A sample python script outlining this method can be viewed here: https://gist.github.com/theracingapi/4d492dbdba58c2072fe4f98ee090126f
","operationId":"results_v1_results_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
Default: 365 days ago. Limited to up to 365 days in the past; pro plan users can purchase the historical results add-on to remove this limitation and query back to 1988-01-01.
","title":"Start Date"},"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
Default: 365 days ago. Limited to up to 365 days in the past; pro plan users can purchase the historical results add-on to remove this limitation and query back to 1988-01-01.
"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
Default: Today's date. Maximum range between start and end date is 365 days.
","title":"End Date"},"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
Default: Today's date. Maximum range between start and end date is 365 days.
"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
","title":"Region"},"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
"},{"name":"course","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by course ids. Get the full list here.","title":"Course"},"description":"Query by course ids. Get the full list here."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
","title":"Type"},"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
"},{"name":"going","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
","title":"Going"},"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
"},{"name":"race_class","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
","title":"Race Class"},"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
"},{"name":"min_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by minimum race distance (yards)
","title":"Min Distance Y"},"description":"Query by minimum race distance (yards)
"},{"name":"max_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by maximum race distance (yards)
","title":"Max Distance Y"},"description":"Query by maximum race distance (yards)
"},{"name":"age_band","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
","title":"Age Band"},"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
"},{"name":"sex_restriction","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
","title":"Sex Restriction"},"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":100,"minimum":1},{"type":"null"}],"title":"Limit","default":50}},{"name":"skip","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":20000},{"type":"null"}],"title":"Skip","default":0}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultsStandardPage"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/results\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/results\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/results?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/results')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/results\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/results?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/results?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/results/today":{"get":{"tags":["Results","Basic Plan","Standard Plan","Pro Plan"],"summary":"Results Today","description":"Get today's results
| Min. Required Plan | Basic |
| Rate Limit | 5 requests per second |
","operationId":"results_today_v1_results_today_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query results by region codes. Get the full list here.
","title":"Region"},"description":"Query results by region codes. Get the full list here.
"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":100,"minimum":1},{"type":"null"}],"title":"Limit","default":50}},{"name":"skip","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Skip","default":0}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultsBasicPage"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/results/today\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/results/today\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/results/today?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/results/today')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/results/today\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/results/today?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/results/today?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/results/today/free":{"get":{"tags":["Results","Free Plan"],"summary":"Results Today Free","description":"Get today's results (basic data only)
| Min. Required Plan | Free |
| Rate Limit | 1 requests per second |
","operationId":"results_today_free_v1_results_today_free_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query results by region codes. Get the full list here.
","title":"Region"},"description":"Query results by region codes. Get the full list here.
"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":100,"minimum":1},{"type":"null"}],"title":"Limit","default":50}},{"name":"skip","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Skip","default":0}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultsFreePage"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/results/today/free\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/results/today/free\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/results/today/free?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/results/today/free')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/results/today/free\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/results/today/free?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/results/today/free?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/results/{race_id}":{"get":{"tags":["Results","Standard Plan","Pro Plan"],"summary":"Result","description":"Get a result
| Min. Required Plan | Standard |
| Rate Limit | 5 requests per second |
","operationId":"result_v1_results__race_id__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"race_id","in":"path","required":true,"schema":{"type":"string","title":"Race Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultStandard"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/results/{race_id}\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/results/{race_id}\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/results/{race_id}?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/results/{race_id}')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/results/{race_id}\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/results/{race_id}?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/results/{race_id}?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/sires/search":{"get":{"tags":["Sires","Standard Plan","Pro Plan"],"summary":"Sire Search","description":"Search sires by name
| Min. Required Plan | Standard |
| Rate Limit | 5 requests per second |
","operationId":"sire_search_v1_sires_search_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"name","in":"query","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sires"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/sires/search\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/sires/search\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/sires/search?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/sires/search')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/sires/search\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/sires/search?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/sires/search?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/sires/{sire_id}/results":{"get":{"tags":["Sires","Pro Plan"],"summary":"Sire Progeny Results","description":"Get full historical results for a sire's progeny
| Min. Required Plan | Pro |
| Rate Limit | 5 requests per second |
ℹ️ To view a sire's own racing results, use the horse results endpoint, repacing the 'sir_' id prefix with 'hrs_'.
","operationId":"sire_progeny_results_v1_sires__sire_id__results_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"sire_id","in":"path","required":true,"schema":{"type":"string","title":"Sire Id"}},{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
Default: 365 days ago. Can query back to 1988-01-01.
","title":"Start Date"},"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
Default: 365 days ago. Can query back to 1988-01-01.
"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
Default: Today's date. Maximum range between start and end date is 365 days.
","title":"End Date"},"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
Default: Today's date. Maximum range between start and end date is 365 days.
"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
","title":"Region"},"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
"},{"name":"course","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by course ids. Get the full list here.","title":"Course"},"description":"Query by course ids. Get the full list here."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
","title":"Type"},"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
"},{"name":"going","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
","title":"Going"},"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
"},{"name":"race_class","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
","title":"Race Class"},"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
"},{"name":"min_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by minimum race distance (yards)
","title":"Min Distance Y"},"description":"Query by minimum race distance (yards)
"},{"name":"max_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by maximum race distance (yards)
","title":"Max Distance Y"},"description":"Query by maximum race distance (yards)
"},{"name":"age_band","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
","title":"Age Band"},"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
"},{"name":"sex_restriction","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
","title":"Sex Restriction"},"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":100,"minimum":1},{"type":"null"}],"title":"Limit","default":50}},{"name":"skip","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":20000},{"type":"null"}],"title":"Skip","default":0}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultsStandardPage"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/sires/{sire_id}/results\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/sires/{sire_id}/results\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/sires/{sire_id}/results?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/sires/{sire_id}/results')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/sires/{sire_id}/results\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/sires/{sire_id}/results?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/sires/{sire_id}/results?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/sires/{sire_id}/analysis/classes":{"get":{"tags":["Sires","Standard Plan","Pro Plan"],"summary":"Sire Progeny Class Analysis","description":"Get sire progeny statistics by race class
| Min. Required Plan | Standard |
| Rate Limit | 5 requests per second |
","operationId":"sire_progeny_class_analysis_v1_sires__sire_id__analysis_classes_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"sire_id","in":"path","required":true,"schema":{"type":"string","title":"Sire Id"}},{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"Start Date"},"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"End Date"},"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
","title":"Region"},"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
"},{"name":"course","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by course ids. Get the full list here.","title":"Course"},"description":"Query by course ids. Get the full list here."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
","title":"Type"},"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
"},{"name":"going","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
","title":"Going"},"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
"},{"name":"race_class","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
","title":"Race Class"},"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
"},{"name":"min_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by minimum race distance (yards)
","title":"Min Distance Y"},"description":"Query by minimum race distance (yards)
"},{"name":"max_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by maximum race distance (yards)
","title":"Max Distance Y"},"description":"Query by maximum race distance (yards)
"},{"name":"age_band","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
","title":"Age Band"},"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
"},{"name":"sex_restriction","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
","title":"Sex Restriction"},"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SireClassAnalysis"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/sires/{sire_id}/analysis/classes\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/sires/{sire_id}/analysis/classes\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/sires/{sire_id}/analysis/classes?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/sires/{sire_id}/analysis/classes')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/sires/{sire_id}/analysis/classes\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/sires/{sire_id}/analysis/classes?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/sires/{sire_id}/analysis/classes?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/sires/{sire_id}/analysis/distances":{"get":{"tags":["Sires","Standard Plan","Pro Plan"],"summary":"Sire Progeny Distance Analysis","description":"Get sire progeny statistics by race distance
| Min. Required Plan | Standard |
| Rate Limit | 5 requests per second |
","operationId":"sire_progeny_distance_analysis_v1_sires__sire_id__analysis_distances_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"sire_id","in":"path","required":true,"schema":{"type":"string","title":"Sire Id"}},{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"Start Date"},"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"End Date"},"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
","title":"Region"},"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
"},{"name":"course","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by course ids. Get the full list here.","title":"Course"},"description":"Query by course ids. Get the full list here."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
","title":"Type"},"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
"},{"name":"going","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
","title":"Going"},"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
"},{"name":"race_class","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
","title":"Race Class"},"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
"},{"name":"min_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by minimum race distance (yards)
","title":"Min Distance Y"},"description":"Query by minimum race distance (yards)
"},{"name":"max_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by maximum race distance (yards)
","title":"Max Distance Y"},"description":"Query by maximum race distance (yards)
"},{"name":"age_band","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
","title":"Age Band"},"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
"},{"name":"sex_restriction","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
","title":"Sex Restriction"},"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SireDistanceAnalysis"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/sires/{sire_id}/analysis/distances\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/sires/{sire_id}/analysis/distances\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/sires/{sire_id}/analysis/distances?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/sires/{sire_id}/analysis/distances')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/sires/{sire_id}/analysis/distances\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/sires/{sire_id}/analysis/distances?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/sires/{sire_id}/analysis/distances?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/trainers/search":{"get":{"tags":["Trainers","Standard Plan","Pro Plan"],"summary":"Trainer Search","description":"Search trainers by name
| Min. Required Plan | Standard |
| Rate Limit | 5 requests per second |
","operationId":"trainer_search_v1_trainers_search_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"name","in":"query","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Trainers"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/trainers/search\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/trainers/search\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/trainers/search?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/trainers/search')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/trainers/search\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/trainers/search?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/trainers/search?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/trainers/{trainer_id}/results":{"get":{"tags":["Trainers","Pro Plan"],"summary":"Trainer Results","description":"Get full historical results for a trainer
| Min. Required Plan | Pro |
| Rate Limit | 5 requests per second |
","operationId":"trainer_results_v1_trainers__trainer_id__results_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"trainer_id","in":"path","required":true,"schema":{"type":"string","title":"Trainer Id"}},{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
Default: 365 days ago. Can query back to 1988-01-01.
","title":"Start Date"},"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
Default: 365 days ago. Can query back to 1988-01-01.
"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
Default: Today's date. Maximum range between start and end date is 365 days.
","title":"End Date"},"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
Default: Today's date. Maximum range between start and end date is 365 days.
"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
","title":"Region"},"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
"},{"name":"course","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by course ids. Get the full list here.","title":"Course"},"description":"Query by course ids. Get the full list here."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
","title":"Type"},"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
"},{"name":"going","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
","title":"Going"},"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
"},{"name":"race_class","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
","title":"Race Class"},"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
"},{"name":"min_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by minimum race distance (yards)
","title":"Min Distance Y"},"description":"Query by minimum race distance (yards)
"},{"name":"max_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by maximum race distance (yards)
","title":"Max Distance Y"},"description":"Query by maximum race distance (yards)
"},{"name":"age_band","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
","title":"Age Band"},"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
"},{"name":"sex_restriction","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
","title":"Sex Restriction"},"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":100,"minimum":1},{"type":"null"}],"title":"Limit","default":50}},{"name":"skip","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":20000},{"type":"null"}],"title":"Skip","default":0}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultsStandardPage"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/trainers/{trainer_id}/results\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/trainers/{trainer_id}/results\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/trainers/{trainer_id}/results?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/trainers/{trainer_id}/results')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/trainers/{trainer_id}/results\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/trainers/{trainer_id}/results?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/trainers/{trainer_id}/results?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/trainers/{trainer_id}/analysis/horse-age":{"get":{"tags":["Trainers","Standard Plan","Pro Plan"],"summary":"Trainer Horse Age Analysis","description":"Get trainer statistics by horse age
| Min. Required Plan | Standard |
| Rate Limit | 5 requests per second |
","operationId":"trainer_horse_age_analysis_v1_trainers__trainer_id__analysis_horse_age_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"trainer_id","in":"path","required":true,"schema":{"type":"string","title":"Trainer Id"}},{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"Start Date"},"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"End Date"},"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
","title":"Region"},"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
"},{"name":"course","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by course ids. Get the full list here.","title":"Course"},"description":"Query by course ids. Get the full list here."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
","title":"Type"},"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
"},{"name":"going","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
","title":"Going"},"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
"},{"name":"race_class","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
","title":"Race Class"},"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
"},{"name":"min_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by minimum race distance (yards)
","title":"Min Distance Y"},"description":"Query by minimum race distance (yards)
"},{"name":"max_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by maximum race distance (yards)
","title":"Max Distance Y"},"description":"Query by maximum race distance (yards)
"},{"name":"age_band","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
","title":"Age Band"},"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
"},{"name":"sex_restriction","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
","title":"Sex Restriction"},"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainerHorseAgeAnalysis"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/horse-age\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/horse-age\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/horse-age?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/horse-age')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/horse-age\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/horse-age?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/horse-age?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/trainers/{trainer_id}/analysis/courses":{"get":{"tags":["Trainers","Standard Plan","Pro Plan"],"summary":"Trainer Course Analysis","description":"Get trainer statistics by course
| Min. Required Plan | Standard |
| Rate Limit | 5 requests per second |
","operationId":"trainer_course_analysis_v1_trainers__trainer_id__analysis_courses_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"trainer_id","in":"path","required":true,"schema":{"type":"string","title":"Trainer Id"}},{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"Start Date"},"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"End Date"},"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
","title":"Region"},"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
"},{"name":"course","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by course ids. Get the full list here.","title":"Course"},"description":"Query by course ids. Get the full list here."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
","title":"Type"},"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
"},{"name":"going","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
","title":"Going"},"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
"},{"name":"race_class","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
","title":"Race Class"},"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
"},{"name":"min_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by minimum race distance (yards)
","title":"Min Distance Y"},"description":"Query by minimum race distance (yards)
"},{"name":"max_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by maximum race distance (yards)
","title":"Max Distance Y"},"description":"Query by maximum race distance (yards)
"},{"name":"age_band","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
","title":"Age Band"},"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
"},{"name":"sex_restriction","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
","title":"Sex Restriction"},"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainerCourseAnalysis"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/courses\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/courses\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/courses?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/courses')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/courses\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/courses?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/courses?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/trainers/{trainer_id}/analysis/distances":{"get":{"tags":["Trainers","Standard Plan","Pro Plan"],"summary":"Trainer Distance Analysis","description":"Get trainer statistics by race distance
| Min. Required Plan | Standard |
| Rate Limit | 5 requests per second |
","operationId":"trainer_distance_analysis_v1_trainers__trainer_id__analysis_distances_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"trainer_id","in":"path","required":true,"schema":{"type":"string","title":"Trainer Id"}},{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"Start Date"},"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"End Date"},"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
","title":"Region"},"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
"},{"name":"course","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by course ids. Get the full list here.","title":"Course"},"description":"Query by course ids. Get the full list here."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
","title":"Type"},"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
"},{"name":"going","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
","title":"Going"},"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
"},{"name":"race_class","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
","title":"Race Class"},"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
"},{"name":"min_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by minimum race distance (yards)
","title":"Min Distance Y"},"description":"Query by minimum race distance (yards)
"},{"name":"max_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by maximum race distance (yards)
","title":"Max Distance Y"},"description":"Query by maximum race distance (yards)
"},{"name":"age_band","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
","title":"Age Band"},"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
"},{"name":"sex_restriction","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
","title":"Sex Restriction"},"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainerDistanceAnalysis"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/distances\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/distances\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/distances?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/distances')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/distances\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/distances?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/distances?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/trainers/{trainer_id}/analysis/jockeys":{"get":{"tags":["Trainers","Standard Plan","Pro Plan"],"summary":"Trainer Jockey Analysis","description":"Get trainer statistics by jockey
| Min. Required Plan | Standard |
| Rate Limit | 5 requests per second |
","operationId":"trainer_jockey_analysis_v1_trainers__trainer_id__analysis_jockeys_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"trainer_id","in":"path","required":true,"schema":{"type":"string","title":"Trainer Id"}},{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"Start Date"},"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"End Date"},"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
","title":"Region"},"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
"},{"name":"course","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by course ids. Get the full list here.","title":"Course"},"description":"Query by course ids. Get the full list here."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
","title":"Type"},"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
"},{"name":"going","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
","title":"Going"},"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
"},{"name":"race_class","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
","title":"Race Class"},"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
"},{"name":"min_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by minimum race distance (yards)
","title":"Min Distance Y"},"description":"Query by minimum race distance (yards)
"},{"name":"max_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by maximum race distance (yards)
","title":"Max Distance Y"},"description":"Query by maximum race distance (yards)
"},{"name":"age_band","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
","title":"Age Band"},"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
"},{"name":"sex_restriction","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
","title":"Sex Restriction"},"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainerJockeyAnalysis"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/jockeys\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/jockeys\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/jockeys?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/jockeys')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/jockeys\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/jockeys?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/jockeys?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/trainers/{trainer_id}/analysis/owners":{"get":{"tags":["Trainers","Standard Plan","Pro Plan"],"summary":"Trainer Owner Analysis","description":"Get trainer statistics by owner
| Min. Required Plan | Standard |
| Rate Limit | 5 requests per second |
","operationId":"trainer_owner_analysis_v1_trainers__trainer_id__analysis_owners_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"trainer_id","in":"path","required":true,"schema":{"type":"string","title":"Trainer Id"}},{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"Start Date"},"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"End Date"},"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
","title":"Region"},"description":"Query by region codes. Get the full list here.
Note: If the course query parameter is specified, this will be ignored.
"},{"name":"course","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by course ids. Get the full list here.","title":"Course"},"description":"Query by course ids. Get the full list here."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
","title":"Type"},"description":"Query by race type
Options: chase, flat, hurdle, nh_flat
"},{"name":"going","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
","title":"Going"},"description":"Query by going
Options: fast, firm, good, good_to_firm, good_to_soft, good_to_yielding, hard, heavy, holding, muddy, sloppy, slow, soft, soft_to_heavy, standard, standard_to_fast, standard_to_slow, very_soft, yielding, yielding_to_soft
"},{"name":"race_class","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
","title":"Race Class"},"description":"Query by class
Options: class_1, class_2, class_3, class_4, class_5, class_6, class_7
"},{"name":"min_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by minimum race distance (yards)
","title":"Min Distance Y"},"description":"Query by minimum race distance (yards)
"},{"name":"max_distance_y","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Query by maximum race distance (yards)
","title":"Max Distance Y"},"description":"Query by maximum race distance (yards)
"},{"name":"age_band","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
","title":"Age Band"},"description":"Query by age band
Options: 10yo+, 2-3yo, 2yo, 2yo+, 3-4yo, 3-5yo, 3-6yo, 3yo, 3yo+, 4-5yo, 4-6yo, 4-7yo, 4-8yo, 4yo, 4yo+, 5-6yo, 5-7yo, 5-8yo, 5yo, 5yo+, 6-7yo, 6yo, 6yo+, 7yo+, 8yo+, 9yo+
"},{"name":"sex_restriction","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
","title":"Sex Restriction"},"description":"Query by sex restriction
Options: c&f, c&g, f, f&m, m, m&g
"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainerOwnerAnalysis"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/owners\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/owners\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/owners?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/owners')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/owners\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/owners?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/trainers/{trainer_id}/analysis/owners?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/north-america/meets":{"get":{"tags":["North America"],"summary":"Meets","description":"Get a list of North American race meets
| Min. Required Plan | Free + North America regional add-on |
| Rate Limit | 5 requests per second |
| Add-ons | [Required] North America regional add-on: users on any plan can subscribe to this add-on for £49.99 per month. Contact support@theracingapi.com for information. |
","operationId":"meets_v1_north_america_meets_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"Start Date"},"description":"Query from date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
","title":"End Date"},"description":"Query to date with format YYYY-MM-DD, e.g. 2020-01-01
"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":50,"minimum":1},{"type":"null"}],"title":"Limit","default":25}},{"name":"skip","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Skip","default":0}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__models__na_meets__Meets"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/north-america/meets\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/north-america/meets\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/north-america/meets?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/north-america/meets')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/north-america/meets\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/north-america/meets?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/north-america/meets?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/north-america/meets/{meet_id}/entries":{"get":{"tags":["North America"],"summary":"Meet Entries","description":"Get entries for a North American meet
| Min. Required Plan | Free + North America regional add-on |
| Rate Limit | 5 requests per second |
| Add-ons | [Required] North America regional add-on: users on any plan can subscribe to this add-on for £49.99 per month. Contact support@theracingapi.com for information. |
","operationId":"meet_entries_v1_north_america_meets__meet_id__entries_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"meet_id","in":"path","required":true,"schema":{"type":"string","title":"Meet Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Entries"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/north-america/meets/{meet_id}/entries\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/north-america/meets/{meet_id}/entries\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/north-america/meets/{meet_id}/entries?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/north-america/meets/{meet_id}/entries')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/north-america/meets/{meet_id}/entries\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/north-america/meets/{meet_id}/entries?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/north-america/meets/{meet_id}/entries?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/north-america/meets/{meet_id}/results":{"get":{"tags":["North America"],"summary":"Meet Results","description":"Get results for a North American meet
| Min. Required Plan | Free + North America regional add-on |
| Rate Limit | 5 requests per second |
| Add-ons | [Required] North America regional add-on: users on any plan can subscribe to this add-on for £49.99 per month. Contact support@theracingapi.com for information. |
","operationId":"meet_results_v1_north_america_meets__meet_id__results_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"meet_id","in":"path","required":true,"schema":{"type":"string","title":"Meet Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Results"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/north-america/meets/{meet_id}/results\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/north-america/meets/{meet_id}/results\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/north-america/meets/{meet_id}/results?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/north-america/meets/{meet_id}/results')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/north-america/meets/{meet_id}/results\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/north-america/meets/{meet_id}/results?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/north-america/meets/{meet_id}/results?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/australia/meets":{"get":{"tags":["Australia"],"summary":"Meets","description":"Get a list of Australian race meets (up to 12 months in the past and 7 days in the future)
| Min. Required Plan | Free + Australia regional add-on |
| Rate Limit | 5 requests per second |
| Add-ons | - [Required] Australia regional add-on: users on any plan can subscribe to this add-on for £49.99 per month. Contact support@theracingapi.com for information.
- [Optional] Australia database historical races add-on: users who subscribe to the Australia regional add-on can pay a one-time fee of £249 to remove the 12 month restriction and access over 20 years of historical meets and races. Contact support@theracingapi.com for information.
|
","operationId":"meets_v1_australia_meets_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date","description":"Query meets by date with format YYYY-MM-DD (e.g 2023-04-05)"},"description":"Query meets by date with format YYYY-MM-DD (e.g 2023-04-05)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__models__aus_meets__Meets"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/australia/meets\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/australia/meets\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/australia/meets?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/australia/meets')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/australia/meets\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/australia/meets?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/australia/meets?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/australia/meets/{meet_id}/races":{"get":{"tags":["Australia"],"summary":"Races By Meet","description":"Get races for an Australian meet (up to 12 months in the past and 7 days in the future)
| Min. Required Plan | Free + Australia regional add-on |
| Rate Limit | 5 requests per second |
| Add-ons | - [Required] Australia regional add-on: users on the any plan can subscribe to this add-on for £49.99 per month. Contact support@theracingapi.com for information.
- [Optional] Australia database historical races add-on: users who subscribe to the Australia regional add-on can pay a one-time fee of £249 to remove the 12 month restriction and access over 20 years of historical meets and races. Contact support@theracingapi.com for information.
|
","operationId":"races_by_meet_v1_australia_meets__meet_id__races_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"meet_id","in":"path","required":true,"schema":{"type":"string","title":"Meet Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Races"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/australia/meets/{meet_id}/races\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/australia/meets/{meet_id}/races\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/australia/meets/{meet_id}/races?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/australia/meets/{meet_id}/races')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/australia/meets/{meet_id}/races\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/australia/meets/{meet_id}/races?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/australia/meets/{meet_id}/races?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}},"/v1/australia/meets/{meet_id}/races/{race_number}":{"get":{"tags":["Australia"],"summary":"Race","description":"Get an Australian race by meet and race number
| Min. Required Plan | Free + Australia regional add-on |
| Rate Limit | 5 requests per second |
| Add-ons | [Required] Australia regional add-on: users on any plan can subscribe to this add-on for £49.99 per month. Contact support@theracingapi.com for information. |
","operationId":"race_v1_australia_meets__meet_id__races__race_number__get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"meet_id","in":"path","required":true,"schema":{"type":"string","title":"Meet Id"}},{"name":"race_number","in":"path","required":true,"schema":{"type":"string","title":"Race Number"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__models__aus_races__Race"}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-codeSamples":[{"lang":"Shell","source":"curl -u USERNAME:PASSWORD https://api.theracingapi.com/v1/australia/meets/{meet_id}/races/{race_number}\n","label":"cURL"},{"lang":"Python","source":"import requests\nfrom requests.auth import HTTPBasicAuth\n\nurl = \"https://api.theracingapi.com/v1/australia/meets/{meet_id}/races/{race_number}\"\nparams = {}\nresponse = requests.request(\"GET\", url, auth=HTTPBasicAuth('USERNAME','PASSWORD'), params=params)\nprint(response.json())","label":"Python3"},{"lang":"PHP","source":"","label":"PHP"},{"lang":"JavaScript","source":"const username = 'USERNAME';\nconst password = 'PASSWORD';\nconst credentials = btoa(`${username}:${password}`);\n\nconst params = new URLSearchParams({});\nconst url = `https://api.theracingapi.com/v1/australia/meets/{meet_id}/races/{race_number}?${params}`;\n\nfetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Basic ${credentials}`\n }\n})\n.then(response => response.json())\n.then(data => console.log(data))\n.catch(error => console.error('Error:', error));","label":"Node.js"},{"lang":"Ruby","source":"require 'net/http'\nrequire 'uri'\nrequire 'json'\n\nparams = {}\nuri = URI('https://api.theracingapi.com/v1/australia/meets/{meet_id}/races/{race_number}')\nuri.query = URI.encode_www_form(params)\n\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest.basic_auth('USERNAME', 'PASSWORD')\n\nresponse = http.request(request)\nputs JSON.parse(response.body)","label":"Ruby"},{"lang":"Java","source":"import java.net.http.*;\nimport java.util.Base64;\nimport java.util.Map;\n\nMap params = Map.of();\nString credentials = Base64.getEncoder()\n .encodeToString(\"USERNAME:PASSWORD\".getBytes());\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.theracingapi.com/v1/australia/meets/{meet_id}/races/{race_number}\"))\n .header(\"Authorization\", \"Basic \" + credentials)\n .build();\n\nHttpResponse response = client.send(\n request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());","label":"Java"},{"lang":"Go","source":"package main\n\nimport (\n \"fmt\"\n \"io\"\n \"net/http\"\n \"net/url\"\n)\n\nfunc main() {\n params := url.Values{}\n url := \"https://api.theracingapi.com/v1/australia/meets/{meet_id}/races/{race_number}?\" + params.Encode()\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", url, nil)\n req.SetBasicAuth(\"USERNAME\", \"PASSWORD\")\n\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}","label":"Go"},{"lang":"C#","source":"using System.Net.Http.Headers;\nusing System.Text;\nusing System.Web;\n\nvar params_ = HttpUtility.ParseQueryString(string.Empty);\nvar url = $\"https://api.theracingapi.com/v1/australia/meets/{meet_id}/races/{race_number}?{params_}\";\n\nvar client = new HttpClient();\nvar credentials = Convert.ToBase64String(\n Encoding.ASCII.GetBytes(\"USERNAME:PASSWORD\"));\nclient.DefaultRequestHeaders.Authorization =\n new AuthenticationHeaderValue(\"Basic\", credentials);\n\nvar response = await client.GetAsync(url);\nvar content = await response.Content.ReadAsStringAsync();\nConsole.WriteLine(content);","label":".NET"}]}}},"components":{"schemas":{"Change":{"properties":{"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text"}},"type":"object","title":"Change","example":{"text":"Horse scratched due to injury","type":"Scratch"}},"CoursesPage":{"properties":{"courses":{"items":{"$ref":"#/components/schemas/app__models__courses__Course"},"type":"array","title":"Courses"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["courses","total"],"title":"CoursesPage"},"Dam":{"properties":{"id":{"type":"string","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["id","name"],"title":"Dam","example":{"id":"dam_4087006","name":"Kind (IRE)"}},"DamClassAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"dam":{"type":"string","title":"Dam"},"total_runners":{"type":"integer","title":"Total Runners"},"classes":{"items":{"$ref":"#/components/schemas/app__models__dams__Class"},"type":"array","title":"Classes"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","dam","total_runners","classes","query"],"title":"DamClassAnalysis","example":{"classes":[{"1_pl":-2.0,"1st":5,"2nd":4,"3rd":3,"4th":2,"a/e":0.9,"class":"Class 1","runners":30,"win_%":0.17}],"dam":"Danehill Destiny (IRE)","id":"dam_5741323","query":[["dam_id","dam_5741323"]],"total_runners":45}},"DamDistanceAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"dam":{"type":"string","title":"Dam"},"total_runners":{"type":"integer","title":"Total Runners"},"distances":{"items":{"$ref":"#/components/schemas/app__models__dams__Distance"},"type":"array","title":"Distances"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","dam","total_runners","distances","query"],"title":"DamDistanceAnalysis","example":{"dam":"Danehill Destiny (IRE)","distances":[{"1_pl":-5.5,"1st":8,"2nd":6,"3rd":5,"4th":4,"a/e":0.85,"dist":"1m","dist_f":"8f","dist_m":"1609","dist_y":"1760","runners":50,"win_%":0.16}],"id":"dam_5741323","query":[["dam_id","dam_5741323"]],"total_runners":45}},"Dams":{"properties":{"search_results":{"items":{"$ref":"#/components/schemas/Dam"},"type":"array","title":"Search Results"}},"type":"object","required":["search_results"],"title":"Dams","example":{"search_results":[{"id":"dam_4087006","name":"Kind (IRE)"},{"id":"dam_5297166","name":"Homepage (GB)"}]}},"Damsire":{"properties":{"id":{"type":"string","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["id","name"],"title":"Damsire","example":{"id":"dsi_296611","name":"Danehill"}},"DamsireClassAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"damsire":{"type":"string","title":"Damsire"},"total_runners":{"type":"integer","title":"Total Runners"},"classes":{"items":{"$ref":"#/components/schemas/app__models__damsires__Class"},"type":"array","title":"Classes"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","damsire","total_runners","classes","query"],"title":"DamsireClassAnalysis","example":{"classes":[{"1_pl":-15.0,"1st":30,"2nd":25,"3rd":22,"4th":18,"a/e":0.88,"class":"Class 1","runners":200,"win_%":0.15}],"damsire":"Sadler's Wells","id":"dsi_3247825","query":[["damsire_id","dsi_3247825"]],"total_runners":500}},"DamsireDistanceAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"damsire":{"type":"string","title":"Damsire"},"total_runners":{"type":"integer","title":"Total Runners"},"distances":{"items":{"$ref":"#/components/schemas/app__models__damsires__Distance"},"type":"array","title":"Distances"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","damsire","total_runners","distances","query"],"title":"DamsireDistanceAnalysis","example":{"damsire":"Sadler's Wells","distances":[{"1_pl":-10.5,"1st":25,"2nd":20,"3rd":18,"4th":15,"a/e":0.95,"dist":"1m2f","dist_f":"10f","dist_m":"2012","dist_y":"2200","runners":150,"win_%":0.17}],"id":"dsi_3247825","query":[["damsire_id","dsi_3247825"]],"total_runners":500}},"Damsires":{"properties":{"search_results":{"items":{"$ref":"#/components/schemas/Damsire"},"type":"array","title":"Search Results"}},"type":"object","required":["search_results"],"title":"Damsires","example":{"search_results":[{"id":"dsi_296611","name":"Danehill"},{"id":"dsi_3527125","name":"Dansili (GB)"}]}},"Distances":{"properties":{"dist":{"type":"string","title":"Distance"},"dist_y":{"type":"string","title":"Distance yards"},"dist_m":{"type":"string","title":"Distance metres"},"dist_f":{"type":"string","title":"Distance furlongs"},"times":{"items":{"$ref":"#/components/schemas/TimesGoing"},"type":"array","title":"Times"},"runs":{"type":"integer","title":"Runs"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["dist","dist_y","dist_m","dist_f","times","runs","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Distances","example":{"1_pl":-5.0,"1st":1,"2nd":2,"3rd":0,"4th":0,"a/e":0.5,"dist":"6f","dist_f":"6f","dist_m":"1225","dist_y":"1340","runs":11,"times":[{"course":"Chelmsford (AW)","date":"2022-04-28","going":"Standard","position":"7","region":"GB","time":"1:11.94"}],"win_%":0.09}},"Entries":{"properties":{"meet_id":{"type":"string","title":"Meet Id"},"track_id":{"type":"string","title":"Track Id"},"track_name":{"type":"string","title":"Track Name"},"country":{"type":"string","title":"Country"},"date":{"type":"string","title":"Date"},"races":{"items":{"$ref":"#/components/schemas/app__models__na_entries__Race"},"type":"array","title":"Races"},"weather":{"$ref":"#/components/schemas/app__models__na_entries__Weather"}},"type":"object","required":["meet_id","track_id","track_name","country","date","races","weather"],"title":"Entries","example":{"country":"USA","date":"2026-02-01","meet_id":"AQU_1769922000000","races":[{"age_restriction":"03","age_restriction_description":"3 Year Olds","breed":"Thoroughbred","changes":[],"course_type":"D","course_type_class":"D","distance_description":"6 1/2 Furlongs","distance_unit":"F","distance_value":6,"grade":"","has_finished":false,"has_results":false,"is_cancelled":false,"max_claim_price":0,"min_claim_price":0,"mtp":-315,"post_time":"12:40 PM","post_time_long":"63600000","purse":80000,"race_class":"MAIDEN SPECIAL WEIGHT","race_key":{"day_evening":"D","race_number":"1"},"race_name":"","race_pools":[{"maximum_wager_amount":10000.0,"minimum_box_amount":1.0,"minimum_wager_amount":2.0,"minimum_wheel_amount":1.0,"pool_code":"WN","pool_name":"Win","race_list":"1"}],"race_restriction":"","race_type":"MSW","race_type_description":"MAIDEN SPECIAL WEIGHT","runners":[{"claiming":0,"coupled_type":"","dam_name":"Criminal Mischief","dam_sire_name":"Into Mischief","description":"","equipment":"Blk-O","horse_name":"Felonious","jockey":{"alias":"Santana R Jr","first_name":"Ricardo","first_name_initial":"R","id":"jky_na_408144","last_name":"Santana, Jr.","middle_name":"","type":"JE"},"medication":"L","morning_line_odds":"5/2","post_pos":"1","program_number":"1","program_number_stripped":1,"registration_number":"23009154","scratch_indicator":"N","sire_name":"Charlatan","trainer":{"alias":"Pletcher Todd A","first_name":"Todd","first_name_initial":"T","id":"trn_na_1029078","last_name":"Pletcher","middle_name":"A.","type":"TE"},"weight":"122"}],"sex_restriction":"","sex_restriction_description":"Open","surface_description":"Dirt","time_zone":"E","tote_track_id":"AQU","track_condition":"FT","track_name":"AQUEDUCT"}],"track_id":"AQU","track_name":"Aqueduct","weather":{"current_weather_description":"Clear","forecast_high":"45","forecast_low":"32","forecast_precipitation":"10","forecast_weather_description":"Partly Cloudy"}}},"Fraction":{"properties":{"fraction_1":{"anyOf":[{"$ref":"#/components/schemas/TimeData"},{"type":"null"}]},"fraction_2":{"anyOf":[{"$ref":"#/components/schemas/TimeData"},{"type":"null"}]},"fraction_3":{"anyOf":[{"$ref":"#/components/schemas/TimeData"},{"type":"null"}]},"fraction_4":{"anyOf":[{"$ref":"#/components/schemas/TimeData"},{"type":"null"}]},"fraction_5":{"anyOf":[{"$ref":"#/components/schemas/TimeData"},{"type":"null"}]},"winning_time":{"anyOf":[{"$ref":"#/components/schemas/TimeData"},{"type":"null"}]}},"type":"object","title":"Fraction","example":{"fraction_1":{"hundredths":60,"minutes":0,"seconds":22,"time_in_hundredths":":22.60"},"fraction_2":{"hundredths":18,"minutes":0,"seconds":46,"time_in_hundredths":":46.18"},"fraction_3":{"hundredths":98,"minutes":0,"seconds":57,"time_in_hundredths":":57.98"},"winning_time":{"hundredths":87,"minutes":1,"seconds":10,"time_in_hundredths":"1:10.87"}}},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Horse":{"properties":{"id":{"type":"string","title":"Id"},"dam":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dam","default":""},"dam_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dam Id","default":""},"damsire":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Damsire","default":""},"damsire_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Damsire Id","default":""},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","default":""},"sire":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sire","default":""},"sire_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sire Id","default":""}},"type":"object","required":["id"],"title":"Horse","example":{"dam":"Kind (IRE)","dam_id":"dam_4087006","damsire":"Danehill","damsire_id":"dsi_296611","id":"hrs_5344171","name":"Frankel (GB)","sire":"Galileo (IRE)","sire_id":"sir_3722383"}},"HorseAges":{"properties":{"horse_age":{"type":"string","title":"Horse Age"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["horse_age","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"HorseAges","example":{"1_pl":-0.68,"1st":156,"2nd":106,"3rd":87,"4th":60,"a/e":0.99,"horse_age":"3","runners":749,"win_%":0.21}},"HorseDistanceTimeAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"horse":{"type":"string","title":"Horse"},"sire":{"type":"string","title":"Sire"},"sire_id":{"type":"string","title":"Sire Id"},"dam":{"type":"string","title":"Dam"},"dam_id":{"type":"string","title":"Dam Id"},"damsire":{"type":"string","title":"Damsire"},"damsire_id":{"type":"string","title":"Damsire Id"},"total_runs":{"type":"integer","title":"Total Runs"},"distances":{"items":{"$ref":"#/components/schemas/Distances"},"type":"array","title":"Distances"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","horse","sire","sire_id","dam","dam_id","damsire","damsire_id","total_runs","distances","query"],"title":"HorseDistanceTimeAnalysis","example":{"dam":"Green Vision (IRE)","dam_id":"dam_5639200","damsire":"Green Desert","damsire_id":"dsi_2126159","distances":[{"1_pl":-5.0,"1st":1,"2nd":2,"3rd":0,"4th":0,"a/e":0.5,"dist":"6f","dist_f":"6f","dist_m":"1225","dist_y":"1340","runs":11,"times":[{"course":"Chelmsford (AW)","date":"2022-04-28","going":"Standard","position":"7","region":"GB","time":"1:11.94"}],"win_%":0.09}],"horse":"Princess Shabnam (IRE)","id":"hrs_25481624","query":[["horse_id","hrs_25481624"]],"sire":"Gregorian (IRE)","sire_id":"sir_5494055","total_runs":16}},"HorsePool":{"properties":{"pool_type_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pool Type Name"},"amount":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Amount"},"fractional_odds":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fractional Odds"},"dollar":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dollar"}},"type":"object","title":"HorsePool","example":{"amount":"5000","dollar":"3.50","fractional_odds":"5/2","pool_type_name":"Win"}},"HorsePro":{"properties":{"id":{"type":"string","title":"Id"},"breeder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Breeder","default":""},"colour":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Colour","default":""},"colour_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Colour Code","default":""},"dam":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dam","default":""},"dam_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dam Id","default":""},"damsire":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Damsire","default":""},"damsire_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Damsire Id","default":""},"dob":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dob","default":""},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","default":""},"sex":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex","default":""},"sex_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Code","default":""},"sire":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sire","default":""},"sire_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sire Id","default":""}},"type":"object","required":["id"],"title":"HorsePro","example":{"breeder":"Juddmonte Farms Ltd","colour":"b","colour_code":"B","dam":"Kind (IRE)","dam_id":"dam_4087006","damsire":"Danehill","damsire_id":"dsi_296611","dob":"2008-02-11","id":"hrs_5344171","name":"Frankel (GB)","sex":"horse","sex_code":"H","sire":"Galileo (IRE)","sire_id":"sir_3722383"}},"Horses":{"properties":{"search_results":{"items":{"$ref":"#/components/schemas/Horse"},"type":"array","title":"Search Results"}},"type":"object","required":["search_results"],"title":"Horses","example":{"search_results":[{"dam":"Kind (IRE)","dam_id":"dam_4087006","damsire":"Danehill","damsire_id":"dsi_296611","id":"hrs_5344171","name":"Frankel (GB)","sire":"Galileo (IRE)","sire_id":"sir_3722383"},{"dam":"February Sun (GB)","dam_id":"dam_5505549","damsire":"Monsun (GER)","damsire_id":"dsi_593768","id":"hrs_33375363","name":"Sunny Frankel (GB)","sire":"Frankel (GB)","sire_id":"sir_5344171"}]}},"JockeyCourseAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"jockey":{"type":"string","title":"Jockey"},"total_rides":{"type":"integer","title":"Total Rides"},"courses":{"items":{"$ref":"#/components/schemas/app__models__jockeys__Course"},"type":"array","title":"Courses"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","jockey","total_rides","courses","query"],"title":"JockeyCourseAnalysis","example":{"courses":[{"1_pl":12.47,"1st":144,"2nd":72,"3rd":86,"4th":68,"a/e":1.09,"course":"Newmarket","course_id":"crs_988","region":"GB","rides":716,"win_%":0.2}],"id":"jky_257379","jockey":"William Buick","query":[["jockey_id","jky_257379"]],"total_rides":3742}},"JockeyDistanceAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"jockey":{"type":"string","title":"Jockey"},"total_rides":{"type":"integer","title":"Total Rides"},"distances":{"items":{"$ref":"#/components/schemas/app__models__jockeys__Distance"},"type":"array","title":"Distances"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","jockey","total_rides","distances","query"],"title":"JockeyDistanceAnalysis","example":{"distances":[{"1_pl":-119.96,"1st":117,"2nd":86,"3rd":72,"4th":64,"a/e":0.95,"dist":"1m","dist_f":"8f","dist_m":"1638","dist_y":"1791","rides":681,"win_%":0.17}],"id":"jky_257379","jockey":"William Buick","query":[["jockey_id","jky_257379"]],"total_rides":3742}},"JockeyOwnerAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"jockey":{"type":"string","title":"Jockey"},"total_rides":{"type":"integer","title":"Total Rides"},"owners":{"items":{"$ref":"#/components/schemas/app__models__jockeys__Owner"},"type":"array","title":"Owners"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","jockey","total_rides","owners","query"],"title":"JockeyOwnerAnalysis","example":{"id":"jky_257379","jockey":"William Buick","owners":[{"1_pl":45.0,"1st":200,"2nd":120,"3rd":100,"4th":80,"a/e":1.05,"owner":"Godolphin","owner_id":"own_199380","rides":800,"win_%":0.25}],"query":[["jockey_id","jky_257379"]],"total_rides":3742}},"JockeyTrainerAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"jockey":{"type":"string","title":"Jockey"},"total_rides":{"type":"integer","title":"Total Rides"},"trainers":{"items":{"$ref":"#/components/schemas/app__models__jockeys__Trainer"},"type":"array","title":"Trainers"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","jockey","total_rides","trainers","query"],"title":"JockeyTrainerAnalysis","example":{"id":"jky_257379","jockey":"William Buick","query":[["jockey_id","jky_257379"]],"total_rides":3742,"trainers":[{"1_pl":50.62,"1st":260,"2nd":148,"3rd":143,"4th":88,"a/e":1.01,"rides":1038,"trainer":"Charlie Appleby","trainer_id":"trn_255042","win_%":0.25}]}},"Jockeys":{"properties":{"search_results":{"items":{"$ref":"#/components/schemas/app__models__jockeys__Jockey"},"type":"array","title":"Search Results"}},"type":"object","required":["search_results"],"title":"Jockeys","example":{"search_results":[{"id":"jky_257379","name":"William Buick"},{"id":"jky_265896","name":"Harry Bentley"}]}},"MeetRaces":{"properties":{"distance":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Distance"},"class":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Class"},"race_group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Group"},"race_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Name"},"race_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Number"},"race_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Status"},"off_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Off Time"}},"type":"object","title":"MeetRaces","example":{"class":"Group 1","distance":"2000m","off_time":"2025-10-11T04:15:00.000Z","race_group":"Group 1","race_name":"Sportsbet Might And Power","race_number":"6","race_status":"Results"}},"OddsHistory":{"properties":{"bookmaker":{"type":"string","title":"Bookmaker"},"fractional":{"type":"string","title":"Fractional"},"decimal":{"type":"string","title":"Decimal"},"ew_places":{"type":"string","title":"Ew Places"},"ew_denom":{"type":"string","title":"Ew Denom"},"updated":{"type":"string","title":"Updated"},"history":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"History","default":[]}},"type":"object","required":["bookmaker","fractional","decimal","ew_places","ew_denom","updated"],"title":"OddsHistory","example":{"bookmaker":"Bet365","decimal":"6.0","ew_denom":"5","ew_places":"3","fractional":"5/1","history":[{"changed_at":"2026-02-06T12:00:00+00:00","decimal":"7.0","fractional":"6/1"}],"updated":"2026-02-06 13:30:00"}},"OddsNoHistory":{"properties":{"bookmaker":{"type":"string","title":"Bookmaker"},"fractional":{"type":"string","title":"Fractional"},"decimal":{"type":"string","title":"Decimal"},"ew_places":{"type":"string","title":"Ew Places"},"ew_denom":{"type":"string","title":"Ew Denom"},"updated":{"type":"string","title":"Updated"}},"type":"object","required":["bookmaker","fractional","decimal","ew_places","ew_denom","updated"],"title":"OddsNoHistory","example":{"bookmaker":"Bet365","decimal":"6.0","ew_denom":"5","ew_places":"3","fractional":"5/1","updated":"2026-02-06 13:30:00"}},"OwnerCourseAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"owner":{"type":"string","title":"Owner"},"total_runners":{"type":"integer","title":"Total Runners"},"courses":{"items":{"$ref":"#/components/schemas/app__models__owners__Course"},"type":"array","title":"Courses"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","owner","total_runners","courses","query"],"title":"OwnerCourseAnalysis","example":{"courses":[{"1_pl":15.0,"1st":100,"2nd":80,"3rd":60,"4th":50,"a/e":1.02,"course":"Newmarket","course_id":"crs_988","region":"GB","runners":500,"win_%":0.2}],"id":"own_199380","owner":"Godolphin","query":[["owner_id","own_199380"]],"total_runners":5938}},"OwnerDistanceAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"owner":{"type":"string","title":"Owner"},"total_runners":{"type":"integer","title":"Total Runners"},"distances":{"items":{"$ref":"#/components/schemas/app__models__owners__Distance"},"type":"array","title":"Distances"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","owner","total_runners","distances","query"],"title":"OwnerDistanceAnalysis","example":{"distances":[{"1_pl":10.27,"1st":239,"2nd":144,"3rd":163,"4th":114,"a/e":0.99,"dist":"1m","dist_f":"8f","dist_m":"1609","dist_y":"1760","runners":1279,"win_%":0.19}],"id":"own_199380","owner":"Godolphin","query":[["owner_id","own_199380"]],"total_runners":5938}},"OwnerJockeyAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"owner":{"type":"string","title":"Owner"},"total_runners":{"type":"integer","title":"Total Runners"},"jockeys":{"items":{"$ref":"#/components/schemas/app__models__owners__Jockey"},"type":"array","title":"Jockeys"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","owner","total_runners","jockeys","query"],"title":"OwnerJockeyAnalysis","example":{"id":"own_199380","jockeys":[{"1_pl":40.0,"1st":150,"2nd":90,"3rd":80,"4th":60,"a/e":1.02,"jockey":"William Buick","jockey_id":"jky_257379","runners":600,"win_%":0.25}],"owner":"Godolphin","query":[["owner_id","own_199380"]],"total_runners":5938}},"OwnerTrainerAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"owner":{"type":"string","title":"Owner"},"total_runners":{"type":"integer","title":"Total Runners"},"trainers":{"items":{"$ref":"#/components/schemas/app__models__owners__Trainer"},"type":"array","title":"Trainers"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","owner","total_runners","trainers","query"],"title":"OwnerTrainerAnalysis","example":{"id":"own_199380","owner":"Godolphin","query":[["owner_id","own_199380"]],"total_runners":5938,"trainers":[{"1_pl":50.0,"1st":200,"2nd":120,"3rd":100,"4th":80,"a/e":1.05,"runners":800,"trainer":"Charlie Appleby","trainer_id":"trn_255042","win_%":0.25}]}},"Owners":{"properties":{"search_results":{"items":{"$ref":"#/components/schemas/app__models__owners__Owner"},"type":"array","title":"Search Results"}},"type":"object","required":["search_results"],"title":"Owners","example":{"search_results":[{"id":"own_199380","name":"Godolphin"},{"id":"own_724272","name":"Miss J Deadman & S Barrow"}]}},"Payoff":{"properties":{"base_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Base Amount"},"carryover":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Carryover"},"number_of_rights":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Number Of Rights"},"number_of_tickets_bet":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Number Of Tickets Bet"},"payoff_amount":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payoff Amount"},"total_pool":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Total Pool"},"wager_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wager Name"},"wager_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wager Type"},"winning_numbers":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Winning Numbers"}},"type":"object","title":"Payoff","example":{"base_amount":0.0,"carryover":0.0,"number_of_rights":0,"number_of_tickets_bet":100,"payoff_amount":"2.40","total_pool":"75,589.00","wager_name":"Exacta","wager_type":"E","winning_numbers":"1-4"}},"RaceKey":{"properties":{"race_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Number"},"day_evening":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Day Evening"}},"type":"object","title":"RaceKey","example":{"day_evening":"D","race_number":"1"}},"RacePool":{"properties":{"maximum_wager_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Maximum Wager Amount"},"minimum_box_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Minimum Box Amount"},"minimum_wager_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Minimum Wager Amount"},"minimum_wheel_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Minimum Wheel Amount"},"pool_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pool Code"},"pool_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pool Name"},"race_list":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race List"}},"type":"object","title":"RacePool","example":{"maximum_wager_amount":10000.0,"minimum_box_amount":1.0,"minimum_wager_amount":2.0,"minimum_wheel_amount":1.0,"pool_code":"WN","pool_name":"Win","race_list":"1"}},"RaceRunnerOdds":{"properties":{"race_id":{"type":"string","title":"Race Id"},"horse_id":{"type":"string","title":"Horse Id"},"horse":{"type":"string","title":"Horse"},"odds":{"anyOf":[{"items":{"$ref":"#/components/schemas/RaceRunnerOddsBookmakers"},"type":"array"},{"type":"null"}],"title":"Odds","default":[]}},"type":"object","required":["race_id","horse_id","horse"],"title":"RaceRunnerOdds","example":{"horse":"Spartan Arrow","horse_id":"hrs_30314725","odds":[{"bookmaker":"Bet365","decimal":"6","ew_denom":"5","ew_places":"4","fractional":"5/1","history":[{"changed_at":"2025-04-22T13:34:00+01:00","decimal":"6.5","fractional":"11/2"}],"updated":"2025-04-22 13:30:26"}],"race_id":"rac_11588863"}},"RaceRunnerOddsBookmakers":{"properties":{"bookmaker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bookmaker","default":""},"fractional":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fractional","default":""},"decimal":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decimal","default":""},"ew_places":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ew Places","default":""},"ew_denom":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ew Denom","default":""},"updated":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated","default":""},"history":{"anyOf":[{"items":{"$ref":"#/components/schemas/RaceRunnerOddsHistory"},"type":"array"},{"type":"null"}],"title":"History","default":[]}},"type":"object","title":"RaceRunnerOddsBookmakers","example":{"bookmaker":"Bet365","decimal":"6","ew_denom":"5","ew_places":"4","fractional":"5/1","history":[{"changed_at":"2025-04-22T13:34:00+01:00","decimal":"6.5","fractional":"11/2"},{"changed_at":"2025-04-22T13:28:00+01:00","decimal":"5.5","fractional":"9/2"}],"updated":"2025-04-22 13:30:26"}},"RaceRunnerOddsHistory":{"properties":{"changed_at":{"type":"string","title":"Changed At"},"fractional":{"type":"string","title":"Fractional"},"decimal":{"type":"string","title":"Decimal"}},"type":"object","required":["changed_at","fractional","decimal"],"title":"RaceRunnerOddsHistory","example":{"changed_at":"2025-04-22T13:34:00+01:00","decimal":"6.5","fractional":"11/2"}},"Racecard":{"properties":{"race_id":{"type":"string","title":"Race Id"},"course":{"type":"string","title":"Course"},"course_id":{"type":"string","title":"Course Id"},"date":{"type":"string","title":"Date"},"off_time":{"type":"string","title":"Off Time"},"off_dt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Off Dt","default":""},"race_name":{"type":"string","title":"Race Name"},"distance_round":{"type":"string","title":"Distance Round"},"distance":{"type":"string","title":"Distance"},"distance_f":{"type":"string","title":"Distance F"},"region":{"type":"string","title":"Region"},"pattern":{"type":"string","title":"Pattern"},"sex_restriction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Restriction","default":""},"race_class":{"type":"string","title":"Race Class"},"type":{"type":"string","title":"Type"},"age_band":{"type":"string","title":"Age Band"},"rating_band":{"type":"string","title":"Rating Band"},"prize":{"type":"string","title":"Prize"},"field_size":{"type":"string","title":"Field Size"},"going_detailed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Going Detailed"},"rail_movements":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rail Movements"},"stalls":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stalls"},"weather":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Weather"},"going":{"type":"string","title":"Going"},"surface":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface"},"runners":{"items":{"$ref":"#/components/schemas/app__models__racecards__Runner"},"type":"array","title":"Runners"},"big_race":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Big Race","default":false},"is_abandoned":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Abandoned","default":false},"race_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Status","default":""}},"type":"object","required":["race_id","course","course_id","date","off_time","race_name","distance_round","distance","distance_f","region","pattern","race_class","type","age_band","rating_band","prize","field_size","going_detailed","rail_movements","stalls","weather","going","surface","runners"],"title":"Racecard","example":{"age_band":"4yo+","big_race":false,"course":"Brighton","course_id":"crs_182","date":"2023-05-02","distance":"6f210y","distance_f":"7.0","distance_round":"7f","field_size":"9","going":"Good To Firm","going_detailed":"","is_abandoned":false,"off_dt":"2023-05-02T13:50:00+01:00","off_time":"1:50","pattern":"","prize":"£4,606","race_class":"Class 5","race_id":"rac_10880272","race_name":"At The Races App Form Study Apprentice Handicap","race_status":"","rail_movements":"","rating_band":"0-75","region":"GB","runners":[{"age":"6","breeder":"Tada Nobutaka","colour":"b","dam":"Lerici","dam_id":"dam_4823441","damsire":"Woodman","damsire_id":"dsi_2126229","dob":"2017-04-22","draw":"8","form":"3-7880","headgear":"","horse":"Pistoletto","horse_id":"hrs_18092480","jockey":"Adam Tracey(7)","jockey_id":"jky_301290","last_run":"25","lbs":"140","number":"1","ofr":"75","owner":"J A Thompson & S Russell","owner_id":"own_1227828","region":"USA","rpr":"82","sex":"gelding","sex_code":"G","sire":"War Front","sire_id":"sir_4544750","trainer":"John Ryan","trainer_id":"trn_160758","ts":"81"}],"sex_restriction":"","stalls":"","surface":"Turf","type":"Flat","weather":""}},"RacecardBasic":{"properties":{"race_id":{"type":"string","title":"Race Id"},"course":{"type":"string","title":"Course"},"date":{"type":"string","title":"Date"},"off_time":{"type":"string","title":"Off Time"},"off_dt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Off Dt","default":""},"race_name":{"type":"string","title":"Race Name"},"distance_f":{"type":"string","title":"Distance F"},"region":{"type":"string","title":"Region"},"pattern":{"type":"string","title":"Pattern"},"race_class":{"type":"string","title":"Race Class"},"type":{"type":"string","title":"Type"},"age_band":{"type":"string","title":"Age Band"},"rating_band":{"type":"string","title":"Rating Band"},"sex_restriction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Restriction","default":""},"prize":{"type":"string","title":"Prize"},"field_size":{"type":"string","title":"Field Size"},"going":{"type":"string","title":"Going"},"surface":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface"},"runners":{"items":{"$ref":"#/components/schemas/app__models__racecards__RunnerBasic"},"type":"array","title":"Runners"},"race_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Status","default":""}},"type":"object","required":["race_id","course","date","off_time","race_name","distance_f","region","pattern","race_class","type","age_band","rating_band","prize","field_size","going","surface","runners"],"title":"RacecardBasic","example":{"age_band":"4yo+","course":"Brighton","date":"2023-05-02","distance_f":"7.0","field_size":"9","going":"Good To Firm","off_dt":"2023-05-02T13:50:00+01:00","off_time":"1:50","pattern":"","prize":"£4,606","race_class":"Class 5","race_id":"rac_10880272","race_name":"At The Races App Form Study Apprentice Handicap","race_status":"","rating_band":"0-75","region":"GB","runners":[{"age":"6","colour":"b","dam":"Lerici","dam_id":"dam_4823441","damsire":"Woodman","damsire_id":"dsi_2126229","draw":"8","form":"3-7880","headgear":"","horse":"Pistoletto","horse_id":"hrs_18092480","jockey":"Adam Tracey(7)","jockey_id":"jky_301290","last_run":"25","lbs":"140","number":"1","ofr":"75","owner":"J A Thompson & S Russell","owner_id":"own_1227828","region":"USA","sex":"gelding","sex_code":"G","sire":"War Front","sire_id":"sir_4544750","trainer":"John Ryan","trainer_id":"trn_160758"}],"sex_restriction":"","surface":"Turf","type":"Flat"}},"RacecardOdds":{"properties":{"race_id":{"type":"string","title":"Race Id"},"course":{"type":"string","title":"Course"},"course_id":{"type":"string","title":"Course Id"},"date":{"type":"string","title":"Date"},"off_time":{"type":"string","title":"Off Time"},"off_dt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Off Dt","default":""},"race_name":{"type":"string","title":"Race Name"},"distance_round":{"type":"string","title":"Distance Round"},"distance":{"type":"string","title":"Distance"},"distance_f":{"type":"string","title":"Distance F"},"region":{"type":"string","title":"Region"},"pattern":{"type":"string","title":"Pattern"},"sex_restriction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Restriction","default":""},"race_class":{"type":"string","title":"Race Class"},"type":{"type":"string","title":"Type"},"age_band":{"type":"string","title":"Age Band"},"rating_band":{"type":"string","title":"Rating Band"},"prize":{"type":"string","title":"Prize"},"field_size":{"type":"string","title":"Field Size"},"going_detailed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Going Detailed"},"rail_movements":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rail Movements"},"stalls":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stalls"},"weather":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Weather"},"going":{"type":"string","title":"Going"},"surface":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface"},"jumps":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jumps","default":""},"runners":{"items":{"$ref":"#/components/schemas/app__models__racecards__RunnerOdds"},"type":"array","title":"Runners"},"big_race":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Big Race","default":false},"is_abandoned":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Abandoned","default":false},"tip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tip","default":""},"verdict":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Verdict","default":""},"betting_forecast":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Betting Forecast","default":""},"race_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Status","default":""}},"type":"object","required":["race_id","course","course_id","date","off_time","race_name","distance_round","distance","distance_f","region","pattern","race_class","type","age_band","rating_band","prize","field_size","going_detailed","rail_movements","stalls","weather","going","surface","runners"],"title":"RacecardOdds","example":{"age_band":"4yo+","betting_forecast":"3/1 Snowthorn, 4/1 King Alloy, 6/1 I P O Bros, 10/1 Team Happy","big_race":false,"course":"Brighton","course_id":"crs_182","date":"2023-05-02","distance":"6f210y","distance_f":"7.0","distance_round":"7f","field_size":"9","going":"Good To Firm","going_detailed":"","is_abandoned":false,"jumps":"","off_dt":"2023-05-02T13:50:00+01:00","off_time":"1:50","pattern":"","prize":"£4,606","race_class":"Class 5","race_id":"rac_10880272","race_name":"At The Races App Form Study Apprentice Handicap","race_status":"","rail_movements":"","rating_band":"0-75","region":"GB","runners":[{"age":"6","breeder":"Tada Nobutaka","colour":"b","comment":"Very useful in his prime but seems to have lost his way this year","dam":"Lerici","dam_id":"dam_4823441","dam_region":"USA","damsire":"Woodman","damsire_id":"dsi_2126229","damsire_region":"USA","dob":"2017-04-22","draw":"8","form":"3-7880","headgear":"","headgear_run":"","horse":"Pistoletto","horse_id":"hrs_18092480","jockey":"Adam Tracey(7)","jockey_id":"jky_301290","last_run":"25","lbs":"140","medical":[],"number":"1","odds":[{"bookmaker":"Bet365","decimal":"9","ew_denom":"5","ew_places":"3","fractional":"8","updated":"2023-05-02 12:36:43"},{"bookmaker":"Paddy Power","decimal":"8.5","ew_denom":"5","ew_places":"3","fractional":"15/2","updated":"2023-05-02 12:36:43"}],"ofr":"75","owner":"J A Thompson & S Russell","owner_id":"own_1227828","past_results_flags":[],"prev_owners":[{"change_date":"2021-08-07","owner":"G McGladery, J A Thompson & S Russell","owner_id":"own_1191676"}],"prev_trainers":[{"change_date":"2020-09-26","trainer":"A P O'Brien","trainer_id":"trn_71802"}],"quotes":[{"course":"Newmarket","course_id":"crs_988","date":"2019-10-11","distance_f":"5","distance_y":"1100","horse":"Pistoletto","horse_id":"hrs_18092480","quote":"He travelled nicely into the race - Kevin Buckley.","race":"Cornwallis Stakes (Group 3)","race_id":"rac_9618089"}],"region":"USA","rpr":"82","sex":"gelding","sex_code":"G","silk_url":"https://www.url-for-silk.com/svg/456789.svg","sire":"War Front","sire_id":"sir_4544750","sire_region":"USA","spotlight":"Very useful in his prime but seems to have lost his way this year; sure to appreciate today's ease in grade.","stable_tour":[],"trainer":"John Ryan","trainer_14_days":{"percent":"0","runs":"5","wins":"0"},"trainer_id":"trn_160758","trainer_location":"Newmarket, Suffolk","trainer_rtf":"","ts":"81","wind_surgery":"","wind_surgery_run":""}],"sex_restriction":"","stalls":"","surface":"Turf","tip":"Team Happy","type":"Flat","verdict":"Many of these arrive with something to prove but TEAM HAPPY has shown signs of a revival lately and might exploit a good mark.","weather":""}},"RacecardOddsPro":{"properties":{"race_id":{"type":"string","title":"Race Id"},"course":{"type":"string","title":"Course"},"course_id":{"type":"string","title":"Course Id"},"date":{"type":"string","title":"Date"},"off_time":{"type":"string","title":"Off Time"},"off_dt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Off Dt","default":""},"race_name":{"type":"string","title":"Race Name"},"distance_round":{"type":"string","title":"Distance Round"},"distance":{"type":"string","title":"Distance"},"distance_f":{"type":"string","title":"Distance F"},"region":{"type":"string","title":"Region"},"pattern":{"type":"string","title":"Pattern"},"sex_restriction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Restriction","default":""},"race_class":{"type":"string","title":"Race Class"},"type":{"type":"string","title":"Type"},"age_band":{"type":"string","title":"Age Band"},"rating_band":{"type":"string","title":"Rating Band"},"prize":{"type":"string","title":"Prize"},"field_size":{"type":"string","title":"Field Size"},"going_detailed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Going Detailed"},"rail_movements":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rail Movements"},"stalls":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stalls"},"weather":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Weather"},"going":{"type":"string","title":"Going"},"surface":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface"},"jumps":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jumps","default":""},"runners":{"items":{"$ref":"#/components/schemas/RunnerOddsPro"},"type":"array","title":"Runners"},"big_race":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Big Race","default":false},"is_abandoned":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Abandoned","default":false},"tip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tip","default":""},"verdict":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Verdict","default":""},"betting_forecast":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Betting Forecast","default":""},"race_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Status","default":""}},"type":"object","required":["race_id","course","course_id","date","off_time","race_name","distance_round","distance","distance_f","region","pattern","race_class","type","age_band","rating_band","prize","field_size","going_detailed","rail_movements","stalls","weather","going","surface","runners"],"title":"RacecardOddsPro","example":{"age_band":"3yo+","betting_forecast":"6/4 Majestic Valour, 9/2 Sea Diamond, 6/1 Autumn Vibes, 7/1 Lucky Ranger","big_race":false,"course":"Sha Tin","course_id":"crs_10816","date":"2026-01-11","distance":"6f","distance_f":"6.0","distance_round":"6f","field_size":"12","going":"Good","going_detailed":"TURF: GOOD TO FIRM","is_abandoned":false,"jumps":"","off_dt":"2026-01-11T05:00:00+00:00","off_time":"5:00","pattern":"","prize":"£62,459","race_class":"","race_id":"rac_11845223","race_name":"Wu Kai Sha Handicap (Div I) (Class 4) (3yo+) (Course C+3) (Turf)","race_status":"","rail_movements":"","rating_band":"","region":"HK","runners":[{"age":"9","breeder":"Scea Haras De Saint Pair","colour":"b/br","comment":"Much better known as a miler but begins the new campaign on a very tempting mark","dam":"Trois Lunes","dam_id":"dam_5583571","dam_region":"FR","damsire":"Manduro","damsire_id":"dsi_4247215","damsire_region":"GER","dob":"2014-02-27","draw":"9","form":"60500-","headgear":"","headgear_run":"","horse":"Trais Fluors","horse_id":"hrs_8192583","jockey":"Rose Dawes(5)","jockey_id":"jky_301152","last_run":"192","lbs":"139","medical":[],"number":"2","odds":[{"bookmaker":"Bet365","decimal":"4.33","ew_denom":"5","ew_places":"3","fractional":"10/3","history":[],"updated":"2023-05-02 12:36:43"},{"bookmaker":"Betfair Exchange","decimal":"4.5","ew_denom":"","ew_places":"","fractional":"7/2","history":[],"updated":"2023-05-02 12:36:43"}],"ofr":"74","owner":"M Channon","owner_id":"own_31504","past_results_flags":[],"prev_owners":[{"change_date":"2021-09-06","owner":"Lee Harris","owner_id":"own_1182044"},{"change_date":"2021-03-22","owner":"Robert Ng","owner_id":"own_466920"}],"prev_trainers":[{"change_date":"2023-03-24","trainer":"Mick Channon","trainer_id":"trn_42084"},{"change_date":"2021-03-22","trainer":"K J Condon","trainer_id":"trn_137160"},{"change_date":"2019-10-23","trainer":"A Fabre","trainer_id":"trn_9837"}],"quotes":[{"course":"Sandown","course_id":"crs_1404","date":"2021-06-12","distance_f":"8","distance_y":"1760","horse":"Trais Fluors","horse_id":"hrs_8192583","quote":"He was suited by the better ground here, which was key - Silvestre de Sousa, rider.","race":"Play Coral Racing-Super-Series For Free Handicap","race_id":"rac_10204649"}],"region":"GB","rpr":"85","sex":"gelding","sex_code":"G","silk_url":"https://www.url-for-silk.com/svg/456789.svg","sire":"Dansili","sire_id":"sir_3527125","sire_region":"GB","spotlight":"Steadily regressive 9yo who is much better known as a miler and ended last season with two down-the-field runs; begins the new campaign on a tempting mark, though, and can't be ruled out.","stable_tour":[],"trainer":"Jack Channon","trainer_14_days":{"percent":"31","runs":"16","wins":"5"},"trainer_id":"trn_361449","trainer_location":"West Ilsley, Berks","trainer_rtf":"71","ts":"83","wind_surgery":"","wind_surgery_run":""}],"sex_restriction":"","stalls":"","surface":"Turf","tip":"Majestic Valour","type":"Flat","verdict":"This looks a good opportunity for promising 4yo MAJESTIC VALOUR to get back to winning ways.","weather":""}},"RacecardSummary":{"properties":{"date":{"type":"string","title":"Date"},"region":{"type":"string","title":"Region"},"course_id":{"type":"string","title":"Course Id"},"course":{"type":"string","title":"Course"},"race_id":{"type":"string","title":"Race Id"},"race_name":{"type":"string","title":"Race Name"},"race_class":{"type":"string","title":"Race Class"},"off_time":{"type":"string","title":"Off Time"},"off_dt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Off Dt","default":""},"big_race":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Big Race","default":false},"is_abandoned":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Abandoned","default":false}},"type":"object","required":["date","region","course_id","course","race_id","race_name","race_class","off_time"],"title":"RacecardSummary","example":{"big_race":false,"course":"Bahrain","course_id":"crs_22594","date":"2026-02-06","is_abandoned":false,"off_dt":"2026-02-06T14:00:00+00:00","off_time":"2:00","race_class":"","race_id":"rac_11868129","race_name":"Race 3 (Conditions Race) (Turf)","region":"BHR"}},"RacecardsBasicPage":{"properties":{"racecards":{"items":{"$ref":"#/components/schemas/RacecardBasic"},"type":"array","title":"Racecards"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"skip":{"type":"integer","title":"Skip"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["racecards","total","limit","skip","query"],"title":"RacecardsBasicPage"},"RacecardsOddsPage":{"properties":{"racecards":{"items":{"$ref":"#/components/schemas/RacecardOdds"},"type":"array","title":"Racecards"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"skip":{"type":"integer","title":"Skip"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["racecards","total","limit","skip","query"],"title":"RacecardsOddsPage"},"RacecardsOddsProPage":{"properties":{"racecards":{"items":{"$ref":"#/components/schemas/RacecardOddsPro"},"type":"array","title":"Racecards"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"skip":{"type":"integer","title":"Skip"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["racecards","total","limit","skip","query"],"title":"RacecardsOddsProPage"},"RacecardsPage":{"properties":{"racecards":{"items":{"$ref":"#/components/schemas/Racecard"},"type":"array","title":"Racecards"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"skip":{"type":"integer","title":"Skip"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["racecards","total","limit","skip","query"],"title":"RacecardsPage"},"RacecardsSummary":{"properties":{"racecard_summaries":{"items":{"$ref":"#/components/schemas/RacecardSummary"},"type":"array","title":"Racecard Summaries"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["racecard_summaries","query"],"title":"RacecardsSummary","example":{"query":[["date","2026-02-06"]],"racecard_summaries":[{"big_race":false,"course":"Bahrain","course_id":"crs_22594","date":"2026-02-06","is_abandoned":false,"off_dt":"2026-02-06T14:00:00+00:00","off_time":"2:00","race_class":"","race_id":"rac_11868129","race_name":"Race 3 (Conditions Race) (Turf)","region":"BHR"},{"big_race":false,"course":"Bangor-on-Dee","course_id":"crs_104","date":"2026-02-06","is_abandoned":true,"off_dt":"2026-02-06T13:25:00+00:00","off_time":"1:25","race_class":"Class 4","race_id":"rac_62443901","race_name":"Alfa Aggregates Products Mares' Handicap Chase","region":"GB"}]}},"Races":{"properties":{"races":{"items":{"$ref":"#/components/schemas/app__models__aus_races__Race"},"type":"array","title":"Races"}},"type":"object","required":["races"],"title":"Races","example":{"races":[{"class":"2YO Plate","course":"Ascot","course_id":"crs_aus_712388980373","date":"2025-10-11","distance":"1000m","going":"Good","is_jump_out":false,"is_trial":false,"meet_id":"met_aus_604899110256","off_time":"2025-10-11T04:00:00.000Z","prize_total":"80000.00","prizes":[{"Position":1,"Value":"43840.00"},{"Position":2,"Value":"14880.00"}],"race_group":"ungrouped","race_name":"VALE LINDSAY SEVERN PLATE","race_number":"1","race_status":"Results","runners":[{"age":"2","colour":"Chestnut","comment":"Jumped Ok Went Forward to Lead but Two Wide quickened approaching Straight","dam":"Poker Face","dam_id":"dam_aus_5763901272","draw":"8","form":"1","horse":"All On Red","horse_id":"hrs_aus_985106802693","jockey":"L.M.Campbell","jockey_claim":"0","jockey_id":"jky_aus_382285552310","number":"1","odds":[{"bookmaker":"Sportsbet","place_odds":"1.70","win_odds":"5.00"},{"bookmaker":"Ladbrokes","place_odds":"1.70","win_odds":"5.00"}],"owner":"T B Racing Pty Ltd (T Noske), T Shutterworth, S Mcfarlane, G White","position":"1","prize":"43840","scratched":false,"sex":"Colt","silk_url":"https://www.url-for-silk.com/svg/456789.svg","sire":"Doubtland","sire_id":"sir_aus_767094969190","sp":"5.00","stats":{"career_place_percent":"100%","career_prize":"$65,840","career_win_percent":"100%","course_distance_stats":{"first":"1","second":"0","third":"0","total":"1"},"course_stats":{"first":"1","second":"0","third":"0","total":"1"},"distance_stats":{"first":"1","second":"0","third":"0","total":"1"},"ground_firm_stats":{"first":"0","second":"0","third":"0","total":"0"},"ground_good_stats":{"first":"1","second":"0","third":"0","total":"1"},"jockey_stats":{"first":"1","second":"0","third":"0","total":"1"},"last_raced":"2025-10-11","last_ten_races_stats":{"first":"1","second":"0","third":"0","total":"1"},"last_twelve_months_stats":{"first":"1","second":"0","third":"0","total":"1"},"last_won":"2025-10-11","max_winning_distance":"1000m","min_winning_distance":"1000m"},"trainer":"Daniel Morton","trainer_id":"trn_aus_449779200413","weight":"57.0"}],"state":"WA","winning_time":"58.47","winning_time_hundredths":"5847"}]}},"Region":{"properties":{"region":{"type":"string","title":"Region"},"region_code":{"type":"string","title":"Region Code"}},"type":"object","required":["region","region_code"],"title":"Region","example":{"region":"Great Britain","region_code":"gb"}},"ResultBasic":{"properties":{"race_id":{"type":"string","title":"Race Id"},"date":{"type":"string","title":"Date"},"region":{"type":"string","title":"Region"},"course":{"type":"string","title":"Course"},"course_id":{"type":"string","title":"Course Id"},"off":{"type":"string","title":"Off"},"off_dt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Off Dt","default":""},"race_name":{"type":"string","title":"Race Name"},"type":{"type":"string","title":"Type"},"class":{"type":"string","title":"Class"},"pattern":{"type":"string","title":"Pattern"},"rating_band":{"type":"string","title":"Rating Band"},"age_band":{"type":"string","title":"Age Band"},"sex_rest":{"type":"string","title":"Sex Rest"},"dist":{"type":"string","title":"Dist"},"dist_y":{"type":"string","title":"Dist Y"},"dist_m":{"type":"string","title":"Dist M"},"dist_f":{"type":"string","title":"Dist F"},"going":{"type":"string","title":"Going"},"surface":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface","default":""},"jumps":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jumps","default":""},"runners":{"items":{"$ref":"#/components/schemas/app__models__result__RunnerBasic"},"type":"array","title":"Runners"},"winning_time_detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Winning Time Detail","default":""},"comments":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comments","default":""},"non_runners":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Non Runners","default":""},"tote_win":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Win","default":""},"tote_pl":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Pl","default":""},"tote_ex":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Ex","default":""},"tote_csf":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Csf","default":""},"tote_tricast":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Tricast","default":""},"tote_trifecta":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Trifecta","default":""}},"type":"object","required":["race_id","date","region","course","course_id","off","race_name","type","class","pattern","rating_band","age_band","sex_rest","dist","dist_y","dist_m","dist_f","going","runners"],"title":"ResultBasic","example":{"age_band":"4yo+","class":"","comments":"","course":"Ballinrobe (IRE)","course_id":"crs_4550","date":"2025-08-11","dist":"2m7f","dist_f":"23f","dist_m":"4625","dist_y":"5058","going":"Good","jumps":"12 hurdles","non_runners":"Diamonds Diva (reserve), Great Rainbow (reserve), Loughrask Rainbow (reserve)","off":"8:25","off_dt":"2025-08-11T20:25:00+01:00","pattern":"","race_id":"rac_11715730","race_name":"J.J. Burke Peugeot Handicap Hurdle","rating_band":"0-100","region":"IRE","runners":[{"age":"8","btn":"0","comment":"Held up in rear - headway 3 out - went third after 2 out - challenging when not fluent last - led home turn - ridden clear final furlong(op 13/2)","dam":"Save Me The Waltz (FR)","dam_id":"dam_4189605","damsire":"Halling","damsire_id":"dsi_675241","draw":"","headgear":"v","horse":"Mephisto (IRE)","horse_id":"hrs_19359270","jockey":"Shane Fitzgerald","jockey_claim_lbs":"0","jockey_id":"jky_302916","number":"2","or":"98","ovr_btn":"0","owner":"Restricted Movement Syndicate","owner_id":"own_1204876","position":"1","prize":"€5900","rpr":"105","sex":"G","silk_url":"https://www.url-for-silk.com/svg/456789.svg","sire":"Kendargent (FR)","sire_id":"sir_4514419","sp":"15/2","sp_dec":"8.50","time":"5:44.90","trainer":"Gerard Keane","trainer_id":"trn_86382","tsr":"64","weight":"11-12","weight_lbs":"166"}],"sex_rest":"","surface":"Turf","tote_csf":"€300.55","tote_ex":"€253.10","tote_pl":"€2.30 €5.80 €4.20","tote_tricast":"€5228.29","tote_trifecta":"","tote_win":"€8.50","type":"Hurdle","winning_time_detail":"5m 44.90s (slow by 7.90s)"}},"ResultFree":{"properties":{"race_id":{"type":"string","title":"Race Id"},"course":{"type":"string","title":"Course"},"date":{"type":"string","title":"Date"},"off":{"type":"string","title":"Off"},"off_dt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Off Dt","default":""},"race_name":{"type":"string","title":"Race Name"},"dist_f":{"type":"string","title":"Dist F"},"region":{"type":"string","title":"Region"},"pattern":{"type":"string","title":"Pattern"},"class":{"type":"string","title":"Class"},"type":{"type":"string","title":"Type"},"age_band":{"type":"string","title":"Age Band"},"rating_band":{"type":"string","title":"Rating Band"},"sex_rest":{"type":"string","title":"Sex Rest"},"going":{"type":"string","title":"Going"},"surface":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface","default":""},"runners":{"items":{"$ref":"#/components/schemas/RunnerFree"},"type":"array","title":"Runners"}},"type":"object","required":["race_id","course","date","off","race_name","dist_f","region","pattern","class","type","age_band","rating_band","sex_rest","going","runners"],"title":"ResultFree","example":{"age_band":"4yo+","class":"","course":"Ballinrobe (IRE)","date":"2025-08-11","dist_f":"23f","going":"Good","off":"8:25","off_dt":"2025-08-11T20:25:00+01:00","pattern":"","race_id":"rac_11715730","race_name":"J.J. Burke Peugeot Handicap Hurdle","rating_band":"0-100","region":"IRE","runners":[{"age":"8","dam":"Save Me The Waltz (FR)","dam_id":"dam_4189605","damsire":"Halling","damsire_id":"dsi_675241","draw":"","headgear":"v","horse":"Mephisto (IRE)","horse_id":"hrs_19359270","jockey":"Shane Fitzgerald","jockey_id":"jky_302916","number":"2","or":"98","owner":"Restricted Movement Syndicate","owner_id":"own_1204876","position":"1","sex":"G","sire":"Kendargent (FR)","sire_id":"sir_4514419","trainer":"Gerard Keane","trainer_id":"trn_86382","weight":"11-12","weight_lbs":"166"}],"sex_rest":"","surface":"Turf","type":"Hurdle"}},"ResultStandard":{"properties":{"race_id":{"type":"string","title":"Race Id"},"date":{"type":"string","title":"Date"},"region":{"type":"string","title":"Region"},"course":{"type":"string","title":"Course"},"course_id":{"type":"string","title":"Course Id"},"off":{"type":"string","title":"Off"},"off_dt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Off Dt","default":""},"race_name":{"type":"string","title":"Race Name"},"type":{"type":"string","title":"Type"},"class":{"type":"string","title":"Class"},"pattern":{"type":"string","title":"Pattern"},"rating_band":{"type":"string","title":"Rating Band"},"age_band":{"type":"string","title":"Age Band"},"sex_rest":{"type":"string","title":"Sex Rest"},"dist":{"type":"string","title":"Dist"},"dist_y":{"type":"string","title":"Dist Y"},"dist_m":{"type":"string","title":"Dist M"},"dist_f":{"type":"string","title":"Dist F"},"going":{"type":"string","title":"Going"},"surface":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface","default":""},"jumps":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jumps","default":""},"runners":{"items":{"$ref":"#/components/schemas/RunnerStandard"},"type":"array","title":"Runners"},"winning_time_detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Winning Time Detail","default":""},"comments":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comments","default":""},"non_runners":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Non Runners","default":""},"tote_win":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Win","default":""},"tote_pl":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Pl","default":""},"tote_ex":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Ex","default":""},"tote_csf":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Csf","default":""},"tote_tricast":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Tricast","default":""},"tote_trifecta":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Trifecta","default":""}},"type":"object","required":["race_id","date","region","course","course_id","off","race_name","type","class","pattern","rating_band","age_band","sex_rest","dist","dist_y","dist_m","dist_f","going","runners"],"title":"ResultStandard","example":{"age_band":"4yo+","class":"","comments":"","course":"Ballinrobe (IRE)","course_id":"crs_4550","date":"2025-08-11","dist":"2m7f","dist_f":"23f","dist_m":"4625","dist_y":"5058","going":"Good","jumps":"12 hurdles","non_runners":"Diamonds Diva (reserve), Great Rainbow (reserve), Loughrask Rainbow (reserve)","off":"8:25","off_dt":"2025-08-11T20:25:00+01:00","pattern":"","race_id":"rac_11715730","race_name":"J.J. Burke Peugeot Handicap Hurdle","rating_band":"0-100","region":"IRE","runners":[{"age":"8","bsp":"11.26","btn":"0","comment":"Held up in rear - headway 3 out - went third after 2 out - challenging when not fluent last - led home turn - ridden clear final furlong(op 13/2)","dam":"Save Me The Waltz (FR)","dam_id":"dam_4189605","damsire":"Halling","damsire_id":"dsi_675241","draw":"","headgear":"v","horse":"Mephisto (IRE)","horse_id":"hrs_19359270","jockey":"Shane Fitzgerald","jockey_claim_lbs":"0","jockey_id":"jky_302916","number":"2","or":"98","ovr_btn":"0","owner":"Restricted Movement Syndicate","owner_id":"own_1204876","position":"1","prize":"€5900","rpr":"105","sex":"G","silk_url":"https://www.url-for-silk.com/svg/456789.svg","sire":"Kendargent (FR)","sire_id":"sir_4514419","sp":"15/2","sp_dec":"8.50","time":"5:44.90","trainer":"Gerard Keane","trainer_id":"trn_86382","tsr":"64","weight":"11-12","weight_lbs":"166"}],"sex_rest":"","surface":"Turf","tote_csf":"€300.55","tote_ex":"€253.10","tote_pl":"€2.30 €5.80 €4.20","tote_tricast":"€5228.29","tote_trifecta":"","tote_win":"€8.50","type":"Hurdle","winning_time_detail":"5m 44.90s (slow by 7.90s)"}},"Results":{"properties":{"meet_id":{"type":"string","title":"Meet Id"},"track_id":{"type":"string","title":"Track Id"},"track_name":{"type":"string","title":"Track Name"},"country":{"type":"string","title":"Country"},"date":{"type":"string","title":"Date"},"races":{"anyOf":[{"items":{"$ref":"#/components/schemas/app__models__na_results__Race"},"type":"array"},{"type":"null"}],"title":"Races"},"weather":{"anyOf":[{"$ref":"#/components/schemas/app__models__na_results__Weather"},{"type":"null"}]}},"type":"object","required":["meet_id","track_id","track_name","country","date"],"title":"Results","example":{"country":"USA","date":"2026-02-01","meet_id":"FG_1769922000000","races":[{"age_restriction":"3U","age_restriction_description":"3 Year Olds And Up","also_ran":"Catchin Drama, Righteous Freedom and Grand Encore","breed":"Thoroughbred","distance_description":"6 Furlongs","distance_unit":"F","distance_value":6,"fraction":{"fraction_1":{"hundredths":60,"minutes":0,"seconds":22,"time_in_hundredths":":22.60"},"fraction_2":{"hundredths":18,"minutes":0,"seconds":46,"time_in_hundredths":":46.18"},"fraction_3":{"hundredths":98,"minutes":0,"seconds":57,"time_in_hundredths":":57.98"},"winning_time":{"hundredths":87,"minutes":1,"seconds":10,"time_in_hundredths":"1:10.87"}},"grade":"","maximum_claim_price":"5000.0000","minimum_claim_price":"5,000","off_time":1769967900000,"payoffs":[{"base_amount":0.0,"carryover":0.0,"number_of_rights":0,"number_of_tickets_bet":100,"payoff_amount":"2.40","total_pool":"75,589.00","wager_name":"Exacta","wager_type":"E","winning_numbers":"1-4"}],"post_time":"1:45 PM","post_time_long":1769967900000,"race_class":"CLAIMING($5,000)","race_key":{"day_evening":"D","race_number":"1"},"race_name":"","race_restriction":"S","race_restriction_description":"State Bred","race_type":"CLM","race_type_description":"CLAIMING","runners":[{"breeder_name":"Brittlyn Inc","horse_name":"Louisiana Wildlife","jockey_first_name":"Paco","jockey_first_name_initial":"P","jockey_last_name":"Lopez","owner_first_name":"","owner_last_name":"Lovern, Jason and Lovern, Pamela Belk","place_payoff":2.1,"program_number":"1","program_number_stripped":1,"show_payoff":2.1,"sire_name":"Star Guitar","trainer_first_name":"Antonio","trainer_last_name":"Alberto","weight_carried":"122","win_payoff":3.0}],"scratches":["Gypsy Squall","Lacey's Kat"],"sex_restriction":"","sex_restriction_description":"Open","surface":"D","surface_description":"Dirt","time_zone":"C","total_purse":"14,000","track_condition_description":"Fast","track_name":"FAIR GROUNDS","wager_types":[{"base_amount":"1.00","wager_description":"Exacta","wager_type":"E"}]}],"track_id":"FG","track_name":"Fair Grounds","weather":{"current_temperature":"52","current_weather_description":"Partly Cloudy","date":"2026-02-01","forecast_high":"58","forecast_low":"42","forecast_precipitation":"10","forecast_weather_description":"Mostly Sunny"}}},"ResultsBasicPage":{"properties":{"results":{"anyOf":[{"items":{"$ref":"#/components/schemas/ResultBasic"},"type":"array"},{"type":"null"}],"title":"Results"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"skip":{"type":"integer","title":"Skip"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["results","total","limit","skip","query"],"title":"ResultsBasicPage"},"ResultsFreePage":{"properties":{"results":{"anyOf":[{"items":{"$ref":"#/components/schemas/ResultFree"},"type":"array"},{"type":"null"}],"title":"Results"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"skip":{"type":"integer","title":"Skip"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["results","total","limit","skip","query"],"title":"ResultsFreePage"},"ResultsStandardPage":{"properties":{"results":{"anyOf":[{"items":{"$ref":"#/components/schemas/ResultStandard"},"type":"array"},{"type":"null"}],"title":"Results"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"skip":{"type":"integer","title":"Skip"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["results","total","limit","skip","query"],"title":"ResultsStandardPage"},"RunnerFree":{"properties":{"horse_id":{"type":"string","title":"Horse Id"},"horse":{"type":"string","title":"Horse"},"age":{"type":"string","title":"Age"},"sex":{"type":"string","title":"Sex"},"number":{"type":"string","title":"Number"},"position":{"type":"string","title":"Position"},"draw":{"type":"string","title":"Draw"},"weight":{"type":"string","title":"Weight"},"weight_lbs":{"type":"string","title":"Weight Lbs"},"headgear":{"type":"string","title":"Headgear"},"or":{"type":"string","title":"Or"},"jockey":{"type":"string","title":"Jockey"},"jockey_id":{"type":"string","title":"Jockey Id"},"trainer":{"type":"string","title":"Trainer"},"trainer_id":{"type":"string","title":"Trainer Id"},"owner":{"type":"string","title":"Owner"},"owner_id":{"type":"string","title":"Owner Id"},"sire":{"type":"string","title":"Sire"},"sire_id":{"type":"string","title":"Sire Id"},"dam":{"type":"string","title":"Dam"},"dam_id":{"type":"string","title":"Dam Id"},"damsire":{"type":"string","title":"Damsire"},"damsire_id":{"type":"string","title":"Damsire Id"}},"type":"object","required":["horse_id","horse","age","sex","number","position","draw","weight","weight_lbs","headgear","or","jockey","jockey_id","trainer","trainer_id","owner","owner_id","sire","sire_id","dam","dam_id","damsire","damsire_id"],"title":"RunnerFree","example":{"age":"8","dam":"Save Me The Waltz (FR)","dam_id":"dam_4189605","damsire":"Halling","damsire_id":"dsi_675241","draw":"","headgear":"v","horse":"Mephisto (IRE)","horse_id":"hrs_19359270","jockey":"Shane Fitzgerald","jockey_id":"jky_302916","number":"2","or":"98","owner":"Restricted Movement Syndicate","owner_id":"own_1204876","position":"1","sex":"G","sire":"Kendargent (FR)","sire_id":"sir_4514419","trainer":"Gerard Keane","trainer_id":"trn_86382","weight":"11-12","weight_lbs":"166"}},"RunnerMedical":{"properties":{"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date","default":""},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type","default":""}},"type":"object","title":"RunnerMedical","example":{"date":"2024-06-15","type":"Wind surgery"}},"RunnerOddsPro":{"properties":{"horse_id":{"type":"string","title":"Horse Id"},"horse":{"type":"string","title":"Horse"},"dob":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dob"},"age":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Age"},"sex":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex"},"sex_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Code"},"colour":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Colour"},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region"},"breeder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Breeder"},"dam":{"type":"string","title":"Dam"},"dam_id":{"type":"string","title":"Dam Id"},"dam_region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dam Region","default":""},"sire":{"type":"string","title":"Sire"},"sire_id":{"type":"string","title":"Sire Id"},"sire_region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sire Region","default":""},"damsire":{"type":"string","title":"Damsire"},"damsire_id":{"type":"string","title":"Damsire Id"},"damsire_region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Damsire Region","default":""},"trainer":{"type":"string","title":"Trainer"},"trainer_id":{"type":"string","title":"Trainer Id"},"trainer_location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trainer Location","default":""},"trainer_14_days":{"anyOf":[{"$ref":"#/components/schemas/RunnerTrainer14Days"},{"type":"null"}],"default":{}},"owner":{"type":"string","title":"Owner"},"owner_id":{"type":"string","title":"Owner Id"},"prev_trainers":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerPrevTrainer"},"type":"array"},{"type":"null"}],"title":"Prev Trainers","default":[]},"prev_owners":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerPrevOwner"},"type":"array"},{"type":"null"}],"title":"Prev Owners","default":[]},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment","default":""},"spotlight":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spotlight","default":""},"quotes":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerQuote"},"type":"array"},{"type":"null"}],"title":"Quotes","default":[]},"stable_tour":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerStableTour"},"type":"array"},{"type":"null"}],"title":"Stable Tour","default":[]},"medical":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerMedical"},"type":"array"},{"type":"null"}],"title":"Medical","default":[]},"number":{"type":"string","title":"Number"},"draw":{"type":"string","title":"Draw"},"headgear":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headgear","default":""},"headgear_run":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headgear Run","default":""},"wind_surgery":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wind Surgery","default":""},"wind_surgery_run":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wind Surgery Run","default":""},"past_results_flags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Past Results Flags","default":[]},"lbs":{"type":"string","title":"Lbs"},"ofr":{"type":"string","title":"Ofr"},"rpr":{"type":"string","title":"Rpr"},"ts":{"type":"string","title":"Ts"},"jockey":{"type":"string","title":"Jockey"},"jockey_id":{"type":"string","title":"Jockey Id"},"silk_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Silk Url","default":""},"last_run":{"type":"string","title":"Last Run"},"form":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Form"},"trainer_rtf":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trainer Rtf"},"odds":{"anyOf":[{"items":{"$ref":"#/components/schemas/OddsHistory"},"type":"array"},{"type":"null"}],"title":"Odds","default":[]}},"type":"object","required":["horse_id","horse","dob","age","sex","sex_code","colour","region","breeder","dam","dam_id","sire","sire_id","damsire","damsire_id","trainer","trainer_id","owner","owner_id","number","draw","lbs","ofr","rpr","ts","jockey","jockey_id","last_run","form","trainer_rtf"],"title":"RunnerOddsPro","example":{"age":"9","breeder":"Scea Haras De Saint Pair","colour":"b/br","comment":"Much better known as a miler but begins the new campaign on a very tempting mark","dam":"Trois Lunes","dam_id":"dam_5583571","dam_region":"FR","damsire":"Manduro","damsire_id":"dsi_4247215","damsire_region":"GER","dob":"2014-02-27","draw":"9","form":"60500-","headgear":"","headgear_run":"","horse":"Trais Fluors","horse_id":"hrs_8192583","jockey":"Rose Dawes(5)","jockey_id":"jky_301152","last_run":"192","lbs":"139","medical":[],"number":"2","odds":[{"bookmaker":"Bet365","decimal":"4.33","ew_denom":"5","ew_places":"3","fractional":"10/3","history":[],"updated":"2023-05-02 12:36:43"},{"bookmaker":"Betfair Exchange","decimal":"4.5","ew_denom":"","ew_places":"","fractional":"7/2","history":[],"updated":"2023-05-02 12:36:43"}],"ofr":"74","owner":"M Channon","owner_id":"own_31504","past_results_flags":[],"prev_owners":[{"change_date":"2021-09-06","owner":"Lee Harris","owner_id":"own_1182044"},{"change_date":"2021-03-22","owner":"Robert Ng","owner_id":"own_466920"}],"prev_trainers":[{"change_date":"2023-03-24","trainer":"Mick Channon","trainer_id":"trn_42084"},{"change_date":"2021-03-22","trainer":"K J Condon","trainer_id":"trn_137160"},{"change_date":"2019-10-23","trainer":"A Fabre","trainer_id":"trn_9837"}],"quotes":[{"course":"Sandown","course_id":"crs_1404","date":"2021-06-12","distance_f":"8","distance_y":"1760","horse":"Trais Fluors","horse_id":"hrs_8192583","quote":"He was suited by the better ground here, which was key - Silvestre de Sousa, rider.","race":"Play Coral Racing-Super-Series For Free Handicap","race_id":"rac_10204649"}],"region":"GB","rpr":"85","sex":"gelding","sex_code":"G","silk_url":"https://www.url-for-silk.com/svg/456789.svg","sire":"Dansili","sire_id":"sir_3527125","sire_region":"GB","spotlight":"Steadily regressive 9yo who is much better known as a miler and ended last season with two down-the-field runs; begins the new campaign on a tempting mark, though, and can't be ruled out.","stable_tour":[],"trainer":"Jack Channon","trainer_14_days":{"percent":"31","runs":"16","wins":"5"},"trainer_id":"trn_361449","trainer_location":"West Ilsley, Berks","trainer_rtf":"71","ts":"83","wind_surgery":"","wind_surgery_run":""}},"RunnerPrevOwner":{"properties":{"owner":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner","default":""},"owner_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Id","default":""},"change_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Change Date","default":""}},"type":"object","title":"RunnerPrevOwner","example":{"change_date":"2024-01-15","owner":"Previous Racing Ltd","owner_id":"own_123456"}},"RunnerPrevTrainer":{"properties":{"trainer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trainer","default":""},"trainer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trainer Id","default":""},"change_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Change Date","default":""}},"type":"object","title":"RunnerPrevTrainer","example":{"change_date":"2024-03-01","trainer":"John Smith","trainer_id":"trn_123456"}},"RunnerQuote":{"properties":{"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date","default":""},"horse":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Horse","default":""},"horse_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Horse Id","default":""},"race":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race","default":""},"race_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Id","default":""},"course":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Course","default":""},"course_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Course Id","default":""},"distance_f":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Distance F","default":""},"distance_y":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Distance Y","default":""},"quote":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quote","default":""}},"type":"object","title":"RunnerQuote","example":{"course":"Ascot","course_id":"crs_52","date":"2024-06-15","distance_f":"12f","distance_y":"2640","horse":"Frankel","horse_id":"hrs_5344171","quote":"He ran a great race and showed his class.","race":"King George VI","race_id":"rac_12345678"}},"RunnerStableTour":{"properties":{"quote":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quote","default":""}},"type":"object","title":"RunnerStableTour","example":{"quote":"He's been working well at home and we're hopeful."}},"RunnerStandard":{"properties":{"horse_id":{"type":"string","title":"Horse Id"},"horse":{"type":"string","title":"Horse"},"sp":{"type":"string","title":"Sp"},"sp_dec":{"type":"string","title":"Sp Dec"},"bsp":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bsp","default":""},"number":{"type":"string","title":"Number"},"position":{"type":"string","title":"Position"},"draw":{"type":"string","title":"Draw"},"btn":{"type":"string","title":"Btn"},"ovr_btn":{"type":"string","title":"Ovr Btn"},"age":{"type":"string","title":"Age"},"sex":{"type":"string","title":"Sex"},"weight":{"type":"string","title":"Weight"},"weight_lbs":{"type":"string","title":"Weight Lbs"},"headgear":{"type":"string","title":"Headgear"},"time":{"type":"string","title":"Time"},"or":{"type":"string","title":"Or"},"rpr":{"type":"string","title":"Rpr"},"tsr":{"type":"string","title":"Tsr"},"prize":{"type":"string","title":"Prize"},"jockey":{"type":"string","title":"Jockey"},"jockey_claim_lbs":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jockey Claim Lbs","default":"0"},"jockey_id":{"type":"string","title":"Jockey Id"},"trainer":{"type":"string","title":"Trainer"},"trainer_id":{"type":"string","title":"Trainer Id"},"owner":{"type":"string","title":"Owner"},"owner_id":{"type":"string","title":"Owner Id"},"sire":{"type":"string","title":"Sire"},"sire_id":{"type":"string","title":"Sire Id"},"dam":{"type":"string","title":"Dam"},"dam_id":{"type":"string","title":"Dam Id"},"damsire":{"type":"string","title":"Damsire"},"damsire_id":{"type":"string","title":"Damsire Id"},"comment":{"type":"string","title":"Comment"},"silk_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Silk Url","default":""}},"type":"object","required":["horse_id","horse","sp","sp_dec","number","position","draw","btn","ovr_btn","age","sex","weight","weight_lbs","headgear","time","or","rpr","tsr","prize","jockey","jockey_id","trainer","trainer_id","owner","owner_id","sire","sire_id","dam","dam_id","damsire","damsire_id","comment"],"title":"RunnerStandard","example":{"age":"8","bsp":"11.26","btn":"0","comment":"Held up in rear - headway 3 out - went third after 2 out - challenging when not fluent last - led home turn - ridden clear final furlong(op 13/2)","dam":"Save Me The Waltz (FR)","dam_id":"dam_4189605","damsire":"Halling","damsire_id":"dsi_675241","draw":"","headgear":"v","horse":"Mephisto (IRE)","horse_id":"hrs_19359270","jockey":"Shane Fitzgerald","jockey_claim_lbs":"0","jockey_id":"jky_302916","number":"2","or":"98","ovr_btn":"0","owner":"Restricted Movement Syndicate","owner_id":"own_1204876","position":"1","prize":"€5900","rpr":"105","sex":"G","silk_url":"https://www.url-for-silk.com/svg/456789.svg","sire":"Kendargent (FR)","sire_id":"sir_4514419","sp":"15/2","sp_dec":"8.50","time":"5:44.90","trainer":"Gerard Keane","trainer_id":"trn_86382","tsr":"64","weight":"11-12","weight_lbs":"166"}},"RunnerStats":{"properties":{"career_prize":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Career Prize"},"career_win_percent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Career Win Percent"},"career_place_percent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Career Place Percent"},"course_stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStatsBreakdown"},{"type":"null"}]},"course_distance_stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStatsBreakdown"},{"type":"null"}]},"distance_stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStatsBreakdown"},{"type":"null"}]},"ground_firm_stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStatsBreakdown"},{"type":"null"}]},"ground_good_stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStatsBreakdown"},{"type":"null"}]},"ground_heavy_stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStatsBreakdown"},{"type":"null"}]},"ground_soft_stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStatsBreakdown"},{"type":"null"}]},"ground_aw_stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStatsBreakdown"},{"type":"null"}]},"jockey_stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStatsBreakdown"},{"type":"null"}]},"jumps_stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStatsBreakdown"},{"type":"null"}]},"last_raced":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Raced"},"last_ten_races_stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStatsBreakdown"},{"type":"null"}]},"last_twelve_months_stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStatsBreakdown"},{"type":"null"}]},"last_won":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Won"},"max_winning_distance":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Max Winning Distance"},"min_winning_distance":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Min Winning Distance"}},"type":"object","title":"RunnerStats","example":{"career_place_percent":"100%","career_prize":"$65,840","career_win_percent":"100%","course_distance_stats":{"first":"1","second":"0","third":"0","total":"1"},"course_stats":{"first":"1","second":"0","third":"0","total":"1"},"distance_stats":{"first":"1","second":"0","third":"0","total":"1"},"ground_firm_stats":{"first":"0","second":"0","third":"0","total":"0"},"ground_good_stats":{"first":"1","second":"0","third":"0","total":"1"},"jockey_stats":{"first":"1","second":"0","third":"0","total":"1"},"last_raced":"2025-10-11","last_ten_races_stats":{"first":"1","second":"0","third":"0","total":"1"},"last_twelve_months_stats":{"first":"1","second":"0","third":"0","total":"1"},"last_won":"2025-10-11","max_winning_distance":"1000m","min_winning_distance":"1000m"}},"RunnerStatsBreakdown":{"properties":{"total":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Total"},"first":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First"},"second":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Second"},"third":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Third"}},"type":"object","title":"RunnerStatsBreakdown","example":{"first":"2","second":"1","third":"1","total":"5"}},"RunnerTrainer14Days":{"properties":{"runs":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Runs","default":""},"wins":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wins","default":""},"percent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Percent","default":""}},"type":"object","title":"RunnerTrainer14Days","example":{"percent":"20","runs":"25","wins":"5"}},"Sire":{"properties":{"id":{"type":"string","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["id","name"],"title":"Sire","example":{"id":"sir_5344171","name":"Frankel (GB)"}},"SireClassAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"sire":{"type":"string","title":"Sire"},"total_runners":{"type":"integer","title":"Total Runners"},"classes":{"items":{"$ref":"#/components/schemas/app__models__sires__Class"},"type":"array","title":"Classes"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","sire","total_runners","classes","query"],"title":"SireClassAnalysis","example":{"classes":[{"1_pl":-35.97,"1st":15,"2nd":23,"3rd":24,"4th":27,"a/e":0.53,"class":"Class 2","runners":225,"win_%":0.07},{"1_pl":-5.0,"1st":7,"2nd":10,"3rd":9,"4th":12,"a/e":0.79,"class":"Class 1","runners":78,"win_%":0.09}],"id":"sir_5245191","query":[["race_class","class_1"],["race_class","class_2"],["sire_id","sir_5245191"]],"sire":"Excelebration (IRE)","total_runners":303}},"SireDistanceAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"sire":{"type":"string","title":"Sire"},"total_runners":{"type":"integer","title":"Total Runners"},"distances":{"items":{"$ref":"#/components/schemas/app__models__sires__Distance"},"type":"array","title":"Distances"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","sire","total_runners","distances","query"],"title":"SireDistanceAnalysis","example":{"distances":[{"1_pl":5.83,"1st":4,"2nd":10,"3rd":5,"4th":5,"a/e":0.45,"dist":"7f","dist_f":"7f","dist_m":"1400.0","dist_y":"1540","runners":75,"win_%":0.05}],"id":"sir_5245191","query":[["sire_id","sir_5245191"]],"sire":"Excelebration (IRE)","total_runners":303}},"Sires":{"properties":{"search_results":{"items":{"$ref":"#/components/schemas/Sire"},"type":"array","title":"Search Results"}},"type":"object","required":["search_results"],"title":"Sires","example":{"search_results":[{"id":"sir_5344171","name":"Frankel (GB)"},{"id":"sir_3722383","name":"Galileo (IRE)"}]}},"TimeData":{"properties":{"minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minutes"},"seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seconds"},"hundredths":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Hundredths"},"milliseconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Milliseconds"},"fifths":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fifths"},"str_fifths":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Str Fifths"},"time_in_fifths":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Time In Fifths"},"time_in_hundredths":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Time In Hundredths"}},"type":"object","title":"TimeData","example":{"fifths":4,"hundredths":87,"milliseconds":870,"minutes":1,"seconds":10,"str_fifths":"","time_in_fifths":"1:104","time_in_hundredths":"1:10.87"}},"TimesGoing":{"properties":{"date":{"type":"string","title":"Date"},"region":{"type":"string","title":"Region"},"course":{"type":"string","title":"Course"},"time":{"type":"string","title":"Time"},"going":{"type":"string","title":"Going"},"position":{"type":"string","title":"Position"}},"type":"object","required":["date","region","course","time","going","position"],"title":"TimesGoing","example":{"course":"Chelmsford (AW)","date":"2022-04-28","going":"Standard","position":"7","region":"GB","time":"1:11.94"}},"TrainerCourseAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"trainer":{"type":"string","title":"Trainer"},"total_runners":{"type":"integer","title":"Total Runners"},"courses":{"items":{"$ref":"#/components/schemas/app__models__trainers__Course"},"type":"array","title":"Courses"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","trainer","total_runners","courses","query"],"title":"TrainerCourseAnalysis","example":{"courses":[{"1_pl":15.5,"1st":45,"2nd":30,"3rd":25,"4th":20,"a/e":1.05,"course":"Newmarket","course_id":"crs_988","region":"GB","runners":200,"win_%":0.23}],"id":"trn_255042","query":[["trainer_id","trn_255042"]],"total_runners":1862,"trainer":"Charlie Appleby"}},"TrainerDistanceAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"trainer":{"type":"string","title":"Trainer"},"total_runners":{"type":"integer","title":"Total Runners"},"distances":{"items":{"$ref":"#/components/schemas/app__models__trainers__Distance"},"type":"array","title":"Distances"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","trainer","total_runners","distances","query"],"title":"TrainerDistanceAnalysis","example":{"distances":[{"1_pl":-59.87,"1st":77,"2nd":43,"3rd":52,"4th":32,"a/e":0.93,"dist":"1m","dist_f":"8f","dist_m":"1609","dist_y":"1760","runners":373,"win_%":0.21}],"id":"trn_255042","query":[["trainer_id","trn_255042"]],"total_runners":1862,"trainer":"Charlie Appleby"}},"TrainerHorseAgeAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"trainer":{"type":"string","title":"Trainer"},"total_runners":{"type":"integer","title":"Total Runners"},"horse_ages":{"items":{"$ref":"#/components/schemas/HorseAges"},"type":"array","title":"Horse Ages"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","trainer","total_runners","horse_ages","query"],"title":"TrainerHorseAgeAnalysis","example":{"horse_ages":[{"1_pl":-0.68,"1st":156,"2nd":106,"3rd":87,"4th":60,"a/e":0.99,"horse_age":"3","runners":749,"win_%":0.21},{"1_pl":18.12,"1st":124,"2nd":61,"3rd":68,"4th":39,"a/e":1.02,"horse_age":"2","runners":439,"win_%":0.28}],"id":"trn_255042","query":[["trainer_id","trn_255042"]],"total_runners":1862,"trainer":"Charlie Appleby"}},"TrainerJockeyAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"trainer":{"type":"string","title":"Trainer"},"total_runners":{"type":"integer","title":"Total Runners"},"jockeys":{"items":{"$ref":"#/components/schemas/app__models__trainers__Jockey"},"type":"array","title":"Jockeys"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","trainer","total_runners","jockeys","query"],"title":"TrainerJockeyAnalysis","example":{"id":"trn_255042","jockeys":[{"1_pl":50.62,"1st":260,"2nd":148,"3rd":143,"4th":88,"a/e":1.01,"jockey":"William Buick","jockey_id":"jky_257379","runners":1038,"win_%":0.25}],"query":[["trainer_id","trn_255042"]],"total_runners":1862,"trainer":"Charlie Appleby"}},"TrainerOwnerAnalysis":{"properties":{"id":{"type":"string","title":"Id"},"trainer":{"type":"string","title":"Trainer"},"total_runners":{"type":"integer","title":"Total Runners"},"owners":{"items":{"$ref":"#/components/schemas/app__models__trainers__Owner"},"type":"array","title":"Owners"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["id","trainer","total_runners","owners","query"],"title":"TrainerOwnerAnalysis","example":{"id":"trn_255042","owners":[{"1_pl":25.0,"1st":120,"2nd":80,"3rd":60,"4th":40,"a/e":1.02,"owner":"Godolphin","owner_id":"own_199380","runners":500,"win_%":0.24}],"query":[["trainer_id","trn_255042"]],"total_runners":1862,"trainer":"Charlie Appleby"}},"Trainers":{"properties":{"search_results":{"items":{"$ref":"#/components/schemas/app__models__trainers__Trainer"},"type":"array","title":"Search Results"}},"type":"object","required":["search_results"],"title":"Trainers","example":{"search_results":[{"id":"trn_30735","name":"William Haggas"},{"id":"trn_91413","name":"David Pipe"}]}},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WagerType":{"properties":{"wager_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wager Type"},"wager_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wager Description"},"base_amount":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Amount"}},"type":"object","title":"WagerType","example":{"base_amount":"1.00","wager_description":"Exacta","wager_type":"E"}},"app__models__aus_meets__Meet":{"properties":{"meet_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Meet Id"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"},"course":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Course"},"course_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Course Id"},"races":{"items":{"$ref":"#/components/schemas/MeetRaces"},"type":"array","title":"Races"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}},"type":"object","required":["races"],"title":"Meet","example":{"course":"Caulfield","course_id":"crs_aus_75219024719","date":"2025-10-11","meet_id":"met_aus_73898021202","races":[{"class":"Group 1","distance":"2000m","off_time":"2025-10-11T04:15:00.000Z","race_group":"Group 1","race_name":"Sportsbet Might And Power","race_number":"6","race_status":"Results"}],"state":"VIC"}},"app__models__aus_meets__Meets":{"properties":{"meets":{"anyOf":[{"items":{"$ref":"#/components/schemas/app__models__aus_meets__Meet"},"type":"array"},{"type":"null"}],"title":"Meets"}},"type":"object","title":"Meets","example":{"meets":[{"course":"Caulfield","course_id":"crs_aus_75219024719","date":"2025-10-11","meet_id":"met_aus_73898021202","races":[{"class":"Group 1","distance":"1600m","off_time":"2025-10-11T05:30:00.000Z","race_group":"Group 1","race_name":"Sportsbet Caulfield Guineas","race_number":"8","race_status":"Results"}],"state":"VIC"},{"course":"Ascot","course_id":"crs_aus_712388980373","date":"2025-10-11","meet_id":"met_aus_604899110256","races":[{"class":"2YO Plate","distance":"1000m","off_time":"2025-10-11T04:00:00.000Z","race_group":"ungrouped","race_name":"VALE LINDSAY SEVERN PLATE","race_number":"1","race_status":"Results"}],"state":"WA"}]}},"app__models__aus_races__Race":{"properties":{"course":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Course"},"course_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Course Id"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"},"distance":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Distance"},"going":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Going"},"is_jump_out":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Jump Out","default":false},"is_trial":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Trial","default":false},"meet_id":{"type":"string","title":"Meet Id"},"off_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Off Time"},"prizes":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Prizes"},"prize_total":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prize Total"},"class":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Class"},"race_conditions":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Conditions"},"race_group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Group"},"race_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Name"},"race_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Number"},"race_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Status"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"},"runners":{"items":{"$ref":"#/components/schemas/app__models__aus_races__Runner"},"type":"array","title":"Runners"},"winning_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Winning Time"},"winning_time_hundredths":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Winning Time Hundredths"}},"type":"object","title":"Race","example":{"class":"2YO Plate","course":"Ascot","course_id":"crs_aus_712388980373","date":"2025-10-11","distance":"1000m","going":"Good","is_jump_out":false,"is_trial":false,"meet_id":"met_aus_604899110256","off_time":"2025-10-11T04:00:00.000Z","prize_total":"80000.00","prizes":[{"Position":1,"Value":"43840.00"},{"Position":2,"Value":"14880.00"}],"race_group":"ungrouped","race_name":"VALE LINDSAY SEVERN PLATE","race_number":"1","race_status":"Results","runners":[{"age":"2","colour":"Chestnut","comment":"Jumped Ok Went Forward to Lead but Two Wide quickened approaching Straight","dam":"Poker Face","dam_id":"dam_aus_5763901272","draw":"8","form":"1","horse":"All On Red","horse_id":"hrs_aus_985106802693","jockey":"L.M.Campbell","jockey_claim":"0","jockey_id":"jky_aus_382285552310","number":"1","odds":[{"bookmaker":"Sportsbet","place_odds":"1.70","win_odds":"5.00"},{"bookmaker":"Ladbrokes","place_odds":"1.70","win_odds":"5.00"}],"owner":"T B Racing Pty Ltd (T Noske), T Shutterworth, S Mcfarlane, G White","position":"1","prize":"43840","scratched":false,"sex":"Colt","silk_url":"https://www.url-for-silk.com/svg/456789.svg","sire":"Doubtland","sire_id":"sir_aus_767094969190","sp":"5.00","stats":{"career_place_percent":"100%","career_prize":"$65,840","career_win_percent":"100%","course_distance_stats":{"first":"1","second":"0","third":"0","total":"1"},"course_stats":{"first":"1","second":"0","third":"0","total":"1"},"distance_stats":{"first":"1","second":"0","third":"0","total":"1"},"ground_firm_stats":{"first":"0","second":"0","third":"0","total":"0"},"ground_good_stats":{"first":"1","second":"0","third":"0","total":"1"},"jockey_stats":{"first":"1","second":"0","third":"0","total":"1"},"last_raced":"2025-10-11","last_ten_races_stats":{"first":"1","second":"0","third":"0","total":"1"},"last_twelve_months_stats":{"first":"1","second":"0","third":"0","total":"1"},"last_won":"2025-10-11","max_winning_distance":"1000m","min_winning_distance":"1000m"},"trainer":"Daniel Morton","trainer_id":"trn_aus_449779200413","weight":"57.0"}],"state":"WA","winning_time":"58.47","winning_time_hundredths":"5847"}},"app__models__aus_races__Runner":{"properties":{"horse_id":{"type":"string","title":"Horse Id"},"horse":{"type":"string","title":"Horse"},"age":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Age"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"},"colour":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Colour"},"dam":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dam"},"dam_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dam Id"},"draw":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Draw"},"form":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Form"},"jockey":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jockey"},"jockey_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jockey Id"},"jockey_claim":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jockey Claim"},"margin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Margin"},"number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Number"},"odds":{"anyOf":[{"items":{"$ref":"#/components/schemas/app__models__aus_races__RunnerOdds"},"type":"array"},{"type":"null"}],"title":"Odds"},"owner":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner"},"position":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position"},"prize":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prize"},"rating":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rating"},"scratched":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Scratched"},"sex":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex"},"silk_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Silk Url"},"sire":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sire"},"sire_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sire Id"},"sp":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sp"},"stats":{"anyOf":[{"$ref":"#/components/schemas/RunnerStats"},{"type":"null"}]},"trainer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trainer"},"trainer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trainer Id"},"weight":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Weight"}},"type":"object","required":["horse_id","horse"],"title":"Runner","example":{"age":"2","colour":"Chestnut","comment":"Jumped Ok Went Forward to Lead but Two Wide quickened approaching Straight","dam":"Poker Face","dam_id":"dam_aus_5763901272","draw":"8","form":"1","horse":"All On Red","horse_id":"hrs_aus_985106802693","jockey":"L.M.Campbell","jockey_claim":"0","jockey_id":"jky_aus_382285552310","number":"1","odds":[{"bookmaker":"Sportsbet","place_odds":"1.70","win_odds":"5.00"},{"bookmaker":"Ladbrokes","place_odds":"1.70","win_odds":"5.00"}],"owner":"T B Racing Pty Ltd (T Noske), T Shutterworth, S Mcfarlane, G White","position":"1","prize":"43840","scratched":false,"sex":"Colt","silk_url":"https://www.url-for-silk.com/svg/456789.svg","sire":"Doubtland","sire_id":"sir_aus_767094969190","sp":"5.00","stats":{"career_place_percent":"100%","career_prize":"$65,840","career_win_percent":"100%","course_distance_stats":{"first":"1","second":"0","third":"0","total":"1"},"course_stats":{"first":"1","second":"0","third":"0","total":"1"},"distance_stats":{"first":"1","second":"0","third":"0","total":"1"},"ground_firm_stats":{"first":"0","second":"0","third":"0","total":"0"},"ground_good_stats":{"first":"1","second":"0","third":"0","total":"1"},"jockey_stats":{"first":"1","second":"0","third":"0","total":"1"},"last_raced":"2025-10-11","last_ten_races_stats":{"first":"1","second":"0","third":"0","total":"1"},"last_twelve_months_stats":{"first":"1","second":"0","third":"0","total":"1"},"last_won":"2025-10-11","max_winning_distance":"1000m","min_winning_distance":"1000m"},"trainer":"Daniel Morton","trainer_id":"trn_aus_449779200413","weight":"57.0"}},"app__models__aus_races__RunnerOdds":{"properties":{"bookmaker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bookmaker"},"win_odds":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Win Odds"},"place_odds":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Place Odds"}},"type":"object","title":"RunnerOdds","example":{"bookmaker":"Sportsbet","place_odds":"1.70","win_odds":"5.00"}},"app__models__courses__Course":{"properties":{"id":{"type":"string","title":"Id"},"course":{"type":"string","title":"Course"},"region_code":{"type":"string","title":"Region Code"},"region":{"type":"string","title":"Region"}},"type":"object","required":["id","course","region_code","region"],"title":"Course","example":{"course":"Ascot","id":"crs_52","region":"Great Britain","region_code":"gb"}},"app__models__dams__Class":{"properties":{"class":{"type":"string","title":"Class"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["class","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Class","example":{"1_pl":-2.0,"1st":5,"2nd":4,"3rd":3,"4th":2,"a/e":0.9,"class":"Class 1","runners":30,"win_%":0.17}},"app__models__dams__Distance":{"properties":{"dist":{"type":"string","title":"Dist"},"dist_y":{"type":"string","title":"Dist Y"},"dist_m":{"type":"string","title":"Dist M"},"dist_f":{"type":"string","title":"Dist F"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["dist","dist_y","dist_m","dist_f","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Distance","example":{"1_pl":-5.5,"1st":8,"2nd":6,"3rd":5,"4th":4,"a/e":0.85,"dist":"1m","dist_f":"8f","dist_m":"1609","dist_y":"1760","runners":50,"win_%":0.16}},"app__models__damsires__Class":{"properties":{"class":{"type":"string","title":"Class"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["class","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Class","example":{"1_pl":-15.0,"1st":30,"2nd":25,"3rd":22,"4th":18,"a/e":0.88,"class":"Class 1","runners":200,"win_%":0.15}},"app__models__damsires__Distance":{"properties":{"dist":{"type":"string","title":"Dist"},"dist_y":{"type":"string","title":"Dist Y"},"dist_m":{"type":"string","title":"Dist M"},"dist_f":{"type":"string","title":"Dist F"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["dist","dist_y","dist_m","dist_f","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Distance","example":{"1_pl":-10.5,"1st":25,"2nd":20,"3rd":18,"4th":15,"a/e":0.95,"dist":"1m2f","dist_f":"10f","dist_m":"2012","dist_y":"2200","runners":150,"win_%":0.17}},"app__models__jockeys__Course":{"properties":{"course":{"type":"string","title":"Course"},"course_id":{"type":"string","title":"Course Id"},"region":{"type":"string","title":"Region"},"rides":{"type":"integer","title":"Rides"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["course","course_id","region","rides","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Course","example":{"1_pl":12.47,"1st":144,"2nd":72,"3rd":86,"4th":68,"a/e":1.09,"course":"Newmarket","course_id":"crs_988","region":"GB","rides":716,"win_%":0.2}},"app__models__jockeys__Distance":{"properties":{"dist":{"type":"string","title":"Dist"},"dist_y":{"type":"string","title":"Dist Y"},"dist_m":{"type":"string","title":"Dist M"},"dist_f":{"type":"string","title":"Dist F"},"rides":{"type":"integer","title":"Rides"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["dist","dist_y","dist_m","dist_f","rides","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Distance","example":{"1_pl":-119.96,"1st":117,"2nd":86,"3rd":72,"4th":64,"a/e":0.95,"dist":"1m","dist_f":"8f","dist_m":"1638","dist_y":"1791","rides":681,"win_%":0.17}},"app__models__jockeys__Jockey":{"properties":{"id":{"type":"string","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["id","name"],"title":"Jockey","example":{"id":"jky_257379","name":"William Buick"}},"app__models__jockeys__Owner":{"properties":{"owner_id":{"type":"string","title":"Owner Id"},"owner":{"type":"string","title":"Owner"},"rides":{"type":"integer","title":"Rides"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["owner_id","owner","rides","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Owner","example":{"1_pl":45.0,"1st":200,"2nd":120,"3rd":100,"4th":80,"a/e":1.05,"owner":"Godolphin","owner_id":"own_199380","rides":800,"win_%":0.25}},"app__models__jockeys__Trainer":{"properties":{"trainer_id":{"type":"string","title":"Trainer Id"},"trainer":{"type":"string","title":"Trainer"},"rides":{"type":"integer","title":"Rides"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["trainer_id","trainer","rides","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Trainer","example":{"1_pl":50.62,"1st":260,"2nd":148,"3rd":143,"4th":88,"a/e":1.01,"rides":1038,"trainer":"Charlie Appleby","trainer_id":"trn_255042","win_%":0.25}},"app__models__na_entries__Jockey":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"alias":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alias"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name"},"first_name_initial":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name Initial"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"middle_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Middle Name"},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"}},"type":"object","title":"Jockey","example":{"alias":"Santana R Jr","first_name":"Ricardo","first_name_initial":"R","id":"jky_na_408144","last_name":"Santana, Jr.","middle_name":"","type":"JE"}},"app__models__na_entries__Race":{"properties":{"age_restriction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Age Restriction"},"age_restriction_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Age Restriction Description"},"breed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Breed"},"changes":{"anyOf":[{"items":{"$ref":"#/components/schemas/Change"},"type":"array"},{"type":"null"}],"title":"Changes"},"course_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Course Type"},"course_type_class":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Course Type Class"},"distance_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Distance Description"},"distance_unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Distance Unit"},"distance_value":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Distance Value"},"grade":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Grade"},"handicapper_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handicapper Name"},"has_finished":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Finished"},"has_results":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Results"},"is_cancelled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Cancelled"},"max_claim_price":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Max Claim Price"},"min_claim_price":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Min Claim Price"},"mtp":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mtp"},"post_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Post Time"},"post_time_long":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Post Time Long"},"purse":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Purse"},"race_class":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Class"},"race_key":{"$ref":"#/components/schemas/RaceKey"},"race_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Name"},"race_pools":{"anyOf":[{"items":{"$ref":"#/components/schemas/RacePool"},"type":"array"},{"type":"null"}],"title":"Race Pools"},"race_restriction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Restriction"},"race_restriction_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Restriction Description"},"race_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Type"},"race_type_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Type Description"},"runners":{"items":{"$ref":"#/components/schemas/app__models__na_entries__Runner"},"type":"array","title":"Runners"},"sex_restriction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Restriction"},"sex_restriction_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Restriction Description"},"surface_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface Description"},"time_zone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Time Zone"},"tote_track_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tote Track Id"},"track_condition":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Track Condition"},"track_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Track Name"}},"type":"object","required":["race_key","runners"],"title":"Race","example":{"age_restriction":"03","age_restriction_description":"3 Year Olds","breed":"Thoroughbred","changes":[],"course_type":"D","course_type_class":"D","distance_description":"6 1/2 Furlongs","distance_unit":"F","distance_value":6,"grade":"","has_finished":false,"has_results":false,"is_cancelled":false,"max_claim_price":0,"min_claim_price":0,"mtp":-315,"post_time":"12:40 PM","post_time_long":"63600000","purse":80000,"race_class":"MAIDEN SPECIAL WEIGHT","race_key":{"day_evening":"D","race_number":"1"},"race_name":"","race_restriction":"","race_type":"MSW","race_type_description":"MAIDEN SPECIAL WEIGHT","runners":[{"claiming":0,"coupled_type":"","dam_name":"Criminal Mischief","dam_sire_name":"Into Mischief","description":"","equipment":"Blk-O","horse_name":"Felonious","jockey":{"alias":"Santana R Jr","first_name":"Ricardo","first_name_initial":"R","id":"jky_na_408144","last_name":"Santana, Jr.","middle_name":"","type":"JE"},"medication":"L","morning_line_odds":"5/2","post_pos":"1","program_number":"1","program_number_stripped":1,"registration_number":"23009154","scratch_indicator":"N","sire_name":"Charlatan","trainer":{"alias":"Pletcher Todd A","first_name":"Todd","first_name_initial":"T","id":"trn_na_1029078","last_name":"Pletcher","middle_name":"A.","type":"TE"},"weight":"122"}],"sex_restriction":"","sex_restriction_description":"Open","surface_description":"Dirt","time_zone":"E","tote_track_id":"AQU","track_condition":"FT","track_name":"AQUEDUCT"}},"app__models__na_entries__Runner":{"properties":{"claiming":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Claiming"},"coupled_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Coupled Type"},"dam_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dam Name"},"dam_sire_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dam Sire Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"equipment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Equipment"},"handicapper_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handicapper Name"},"horse_data_pools":{"anyOf":[{"items":{"$ref":"#/components/schemas/HorsePool"},"type":"array"},{"type":"null"}],"title":"Horse Data Pools"},"horse_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Horse Name"},"jockey":{"anyOf":[{"$ref":"#/components/schemas/app__models__na_entries__Jockey"},{"type":"null"}]},"live_odds":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Live Odds"},"medication":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Medication"},"morning_line_odds":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Morning Line Odds"},"post_pos":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Post Pos"},"program_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Program Number"},"program_number_stripped":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Program Number Stripped"},"registration_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Registration Number"},"scratch_indicator":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scratch Indicator"},"sire_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sire Name"},"trainer":{"anyOf":[{"$ref":"#/components/schemas/app__models__na_entries__Trainer"},{"type":"null"}]},"weight":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Weight"}},"type":"object","title":"Runner","example":{"claiming":0,"coupled_type":"","dam_name":"Criminal Mischief","dam_sire_name":"Into Mischief","description":"","equipment":"Blk-O","horse_name":"Felonious","jockey":{"alias":"Santana R Jr","first_name":"Ricardo","first_name_initial":"R","id":"jky_na_408144","last_name":"Santana, Jr.","middle_name":"","type":"JE"},"medication":"L","morning_line_odds":"5/2","post_pos":"1","program_number":"1","program_number_stripped":1,"registration_number":"23009154","scratch_indicator":"N","sire_name":"Charlatan","trainer":{"alias":"Pletcher Todd A","first_name":"Todd","first_name_initial":"T","id":"trn_na_1029078","last_name":"Pletcher","middle_name":"A.","type":"TE"},"weight":"122"}},"app__models__na_entries__Trainer":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"alias":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alias"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name"},"first_name_initial":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name Initial"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"middle_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Middle Name"},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"}},"type":"object","title":"Trainer","example":{"alias":"Pletcher Todd A","first_name":"Todd","first_name_initial":"T","id":"trn_na_1029078","last_name":"Pletcher","middle_name":"A.","type":"TE"}},"app__models__na_entries__Weather":{"properties":{"forecast_weather_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Forecast Weather Description"},"forecast_high":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Forecast High"},"forecast_low":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Forecast Low"},"forecast_precipitation":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Forecast Precipitation"},"current_weather_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Weather Description"}},"type":"object","title":"Weather","example":{"current_weather_description":"Clear","forecast_high":"45","forecast_low":"32","forecast_precipitation":"10","forecast_weather_description":"Partly Cloudy"}},"app__models__na_meets__Meet":{"properties":{"country":{"type":"string","title":"Country"},"date":{"type":"string","title":"Date"},"meet_id":{"type":"string","title":"Meet Id"},"track_id":{"type":"string","title":"Track Id"},"track_name":{"type":"string","title":"Track Name"}},"type":"object","required":["country","date","meet_id","track_id","track_name"],"title":"Meet","example":{"country":"USA","date":"2026-02-01","meet_id":"AQU_1769922000000","track_id":"AQU","track_name":"Aqueduct"}},"app__models__na_meets__Meets":{"properties":{"meets":{"anyOf":[{"items":{"$ref":"#/components/schemas/app__models__na_meets__Meet"},"type":"array"},{"type":"null"}],"title":"Meets"},"limit":{"type":"integer","title":"Limit"},"skip":{"type":"integer","title":"Skip"},"query":{"items":{"items":{},"type":"array"},"type":"array","title":"Query"}},"type":"object","required":["limit","skip","query"],"title":"Meets","example":{"limit":50,"meets":[{"country":"USA","date":"2026-02-01","meet_id":"AQU_1769922000000","track_id":"AQU","track_name":"Aqueduct"},{"country":"USA","date":"2026-02-01","meet_id":"FG_1769922000000","track_id":"FG","track_name":"Fair Grounds"}],"query":[["date","2026-02-01"]],"skip":0}},"app__models__na_results__Race":{"properties":{"age_restriction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Age Restriction"},"age_restriction_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Age Restriction Description"},"also_ran":{"anyOf":[{"type":"string"},{"items":{},"type":"array"},{"type":"null"}],"title":"Also Ran"},"breed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Breed"},"distance_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Distance Description"},"distance_unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Distance Unit"},"distance_value":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Distance Value"},"fraction":{"anyOf":[{"$ref":"#/components/schemas/Fraction"},{"type":"null"}]},"grade":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Grade"},"maximum_claim_price":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Maximum Claim Price"},"minimum_claim_price":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Minimum Claim Price"},"off_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Off Time"},"payoffs":{"anyOf":[{"items":{"$ref":"#/components/schemas/Payoff"},"type":"array"},{"type":"null"}],"title":"Payoffs"},"post_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Post Time"},"post_time_long":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Post Time Long"},"race_class":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Class"},"race_key":{"anyOf":[{"$ref":"#/components/schemas/RaceKey"},{"type":"null"}]},"race_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Name"},"race_restriction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Restriction"},"race_restriction_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Restriction Description"},"race_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Type"},"race_type_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Race Type Description"},"runners":{"anyOf":[{"items":{"$ref":"#/components/schemas/app__models__na_results__Runner"},"type":"array"},{"type":"null"}],"title":"Runners"},"scratches":{"anyOf":[{"items":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":"array"},{"type":"null"}],"title":"Scratches"},"sex_restriction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Restriction"},"sex_restriction_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Restriction Description"},"surface":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface"},"surface_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Surface Description"},"time_zone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Time Zone"},"total_purse":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Total Purse"},"track_condition_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Track Condition Description"},"track_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Track Name"},"wager_types":{"anyOf":[{"items":{"$ref":"#/components/schemas/WagerType"},"type":"array"},{"type":"null"}],"title":"Wager Types"}},"type":"object","title":"Race","example":{"age_restriction":"3U","age_restriction_description":"3 Year Olds And Up","also_ran":"Catchin Drama, Righteous Freedom and Grand Encore","breed":"Thoroughbred","distance_description":"6 Furlongs","distance_unit":"F","distance_value":6,"fraction":{"fraction_1":{"time_in_hundredths":":22.60"},"fraction_2":{"time_in_hundredths":":46.18"},"winning_time":{"time_in_hundredths":"1:10.87"}},"grade":"","maximum_claim_price":"5000.0000","minimum_claim_price":"5,000","off_time":1769967900000,"payoffs":[{"payoff_amount":"2.40","total_pool":"75,589.00","wager_name":"Exacta","wager_type":"E","winning_numbers":"1-4"}],"post_time":"1:45 PM","post_time_long":1769967900000,"race_class":"CLAIMING($5,000)","race_key":{"day_evening":"D","race_number":"1"},"race_name":"","race_restriction":"S","race_restriction_description":"State Bred","race_type":"CLM","race_type_description":"CLAIMING","runners":[{"breeder_name":"Brittlyn Inc","horse_name":"Louisiana Wildlife","jockey_first_name":"Paco","jockey_first_name_initial":"P","jockey_last_name":"Lopez","owner_first_name":"","owner_last_name":"Lovern, Jason and Lovern, Pamela Belk","place_payoff":2.1,"program_number":"1","program_number_stripped":1,"show_payoff":2.1,"sire_name":"Star Guitar","trainer_first_name":"Antonio","trainer_last_name":"Alberto","weight_carried":"122","win_payoff":3.0}],"scratches":["Gypsy Squall","Lacey's Kat"],"sex_restriction":"","sex_restriction_description":"Open","surface":"D","surface_description":"Dirt","time_zone":"C","total_purse":"14,000","track_condition_description":"Fast","track_name":"FAIR GROUNDS","wager_types":[{"base_amount":"1.00","wager_description":"Exacta","wager_type":"E"}]}},"app__models__na_results__Runner":{"properties":{"breeder_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Breeder Name"},"horse_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Horse Name"},"jockey_first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jockey First Name"},"jockey_first_name_initial":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jockey First Name Initial"},"jockey_last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jockey Last Name"},"owner_first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner First Name"},"owner_last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Last Name"},"place_payoff":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Place Payoff"},"program_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Program Number"},"program_number_stripped":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Program Number Stripped"},"show_payoff":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Show Payoff"},"sire_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sire Name"},"trainer_first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trainer First Name"},"trainer_last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trainer Last Name"},"weight_carried":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Weight Carried"},"win_payoff":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Win Payoff"}},"type":"object","title":"Runner","example":{"breeder_name":"Brittlyn Inc","horse_name":"Louisiana Wildlife","jockey_first_name":"Paco","jockey_first_name_initial":"P","jockey_last_name":"Lopez","owner_first_name":"","owner_last_name":"Lovern, Jason and Lovern, Pamela Belk","place_payoff":2.1,"program_number":"1","program_number_stripped":1,"show_payoff":2.1,"sire_name":"Star Guitar","trainer_first_name":"Antonio","trainer_last_name":"Alberto","weight_carried":"122","win_payoff":3.0}},"app__models__na_results__Weather":{"properties":{"current_temperature":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Temperature"},"current_weather_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Weather Description"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"},"forecast_high":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Forecast High"},"forecast_low":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Forecast Low"},"forecast_precipitation":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Forecast Precipitation"},"forecast_weather_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Forecast Weather Description"}},"type":"object","title":"Weather","example":{"current_temperature":"52","current_weather_description":"Partly Cloudy","date":"2026-02-01","forecast_high":"58","forecast_low":"42","forecast_precipitation":"10","forecast_weather_description":"Mostly Sunny"}},"app__models__owners__Course":{"properties":{"course":{"type":"string","title":"Course"},"course_id":{"type":"string","title":"Course Id"},"region":{"type":"string","title":"Region"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["course","course_id","region","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Course","example":{"1_pl":15.0,"1st":100,"2nd":80,"3rd":60,"4th":50,"a/e":1.02,"course":"Newmarket","course_id":"crs_988","region":"GB","runners":500,"win_%":0.2}},"app__models__owners__Distance":{"properties":{"dist":{"type":"string","title":"Dist"},"dist_y":{"type":"string","title":"Dist Y"},"dist_m":{"type":"string","title":"Dist M"},"dist_f":{"type":"string","title":"Dist F"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["dist","dist_y","dist_m","dist_f","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Distance","example":{"1_pl":10.27,"1st":239,"2nd":144,"3rd":163,"4th":114,"a/e":0.99,"dist":"1m","dist_f":"8f","dist_m":"1609","dist_y":"1760","runners":1279,"win_%":0.19}},"app__models__owners__Jockey":{"properties":{"jockey_id":{"type":"string","title":"Jockey Id"},"jockey":{"type":"string","title":"Jockey"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["jockey_id","jockey","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Jockey","example":{"1_pl":40.0,"1st":150,"2nd":90,"3rd":80,"4th":60,"a/e":1.02,"jockey":"William Buick","jockey_id":"jky_257379","runners":600,"win_%":0.25}},"app__models__owners__Owner":{"properties":{"id":{"type":"string","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["id","name"],"title":"Owner","example":{"id":"own_199380","name":"Godolphin"}},"app__models__owners__Trainer":{"properties":{"trainer_id":{"type":"string","title":"Trainer Id"},"trainer":{"type":"string","title":"Trainer"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["trainer_id","trainer","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Trainer","example":{"1_pl":50.0,"1st":200,"2nd":120,"3rd":100,"4th":80,"a/e":1.05,"runners":800,"trainer":"Charlie Appleby","trainer_id":"trn_255042","win_%":0.25}},"app__models__racecards__Runner":{"properties":{"horse_id":{"type":"string","title":"Horse Id"},"horse":{"type":"string","title":"Horse"},"dob":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dob"},"age":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Age"},"sex":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex"},"sex_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Code"},"colour":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Colour"},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region"},"breeder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Breeder"},"dam":{"type":"string","title":"Dam"},"dam_id":{"type":"string","title":"Dam Id"},"dam_region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dam Region","default":""},"sire":{"type":"string","title":"Sire"},"sire_id":{"type":"string","title":"Sire Id"},"sire_region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sire Region","default":""},"damsire":{"type":"string","title":"Damsire"},"damsire_id":{"type":"string","title":"Damsire Id"},"damsire_region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Damsire Region","default":""},"trainer":{"type":"string","title":"Trainer"},"trainer_id":{"type":"string","title":"Trainer Id"},"trainer_location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trainer Location","default":""},"trainer_14_days":{"anyOf":[{"$ref":"#/components/schemas/RunnerTrainer14Days"},{"type":"null"}],"default":{}},"owner":{"type":"string","title":"Owner"},"owner_id":{"type":"string","title":"Owner Id"},"prev_trainers":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerPrevTrainer"},"type":"array"},{"type":"null"}],"title":"Prev Trainers","default":[]},"prev_owners":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerPrevOwner"},"type":"array"},{"type":"null"}],"title":"Prev Owners","default":[]},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment","default":""},"spotlight":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spotlight","default":""},"quotes":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerQuote"},"type":"array"},{"type":"null"}],"title":"Quotes","default":[]},"stable_tour":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerStableTour"},"type":"array"},{"type":"null"}],"title":"Stable Tour","default":[]},"medical":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerMedical"},"type":"array"},{"type":"null"}],"title":"Medical","default":[]},"number":{"type":"string","title":"Number"},"draw":{"type":"string","title":"Draw"},"headgear":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headgear","default":""},"headgear_run":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headgear Run","default":""},"wind_surgery":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wind Surgery","default":""},"wind_surgery_run":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wind Surgery Run","default":""},"past_results_flags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Past Results Flags","default":[]},"lbs":{"type":"string","title":"Lbs"},"ofr":{"type":"string","title":"Ofr"},"rpr":{"type":"string","title":"Rpr"},"ts":{"type":"string","title":"Ts"},"jockey":{"type":"string","title":"Jockey"},"jockey_id":{"type":"string","title":"Jockey Id"},"silk_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Silk Url","default":""},"last_run":{"type":"string","title":"Last Run"},"form":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Form"},"trainer_rtf":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trainer Rtf"}},"type":"object","required":["horse_id","horse","dob","age","sex","sex_code","colour","region","breeder","dam","dam_id","sire","sire_id","damsire","damsire_id","trainer","trainer_id","owner","owner_id","number","draw","lbs","ofr","rpr","ts","jockey","jockey_id","last_run","form","trainer_rtf"],"title":"Runner","example":{"age":"6","breeder":"Tada Nobutaka","colour":"b","comment":"Very useful in his prime but seems to have lost his way this year; has questions to answer","dam":"Lerici","dam_id":"dam_4823441","dam_region":"USA","damsire":"Woodman","damsire_id":"dsi_2126229","damsire_region":"USA","dob":"2017-04-22","draw":"8","form":"3-7880","headgear":"","headgear_run":"","horse":"Pistoletto","horse_id":"hrs_18092480","jockey":"Adam Tracey(7)","jockey_id":"jky_301290","last_run":"25","lbs":"140","medical":[],"number":"1","ofr":"75","owner":"J A Thompson & S Russell","owner_id":"own_1227828","past_results_flags":[],"prev_owners":[{"change_date":"2021-08-07","owner":"G McGladery, J A Thompson & S Russell","owner_id":"own_1191676"},{"change_date":"2020-09-26","owner":"Michael Tabor & Derrick Smith & Mrs John Magnier","owner_id":"own_575044"}],"prev_trainers":[{"change_date":"2020-09-26","trainer":"A P O'Brien","trainer_id":"trn_71802"}],"quotes":[{"course":"Newmarket","course_id":"crs_988","date":"2019-10-11","distance_f":"5","distance_y":"1100","horse":"Pistoletto","horse_id":"hrs_18092480","quote":"Ryan was very pleased and while Pistoletto just didn't seem to get his feet when he left the stalls, and was a bit more slowly away than some, he travelled nicely into the race - Kevin Buckley, Coomore representative.","race":"Newmarket Academy Godolphin Beacon Project Cornwallis Stakes (Group 3)","race_id":"rac_9618089"}],"region":"USA","rpr":"82","sex":"gelding","sex_code":"G","silk_url":"https://www.url-for-silk.com/svg/456789.svg","sire":"War Front","sire_id":"sir_4544750","sire_region":"USA","spotlight":"Very useful in his prime but seems to have lost his way this year; sure to appreciate today's ease in grade after contesting two valuable races at Lingfield (1m2f/1m4f) but may need further than 7f nowadays and has some questions to answer.","stable_tour":[],"trainer":"John Ryan","trainer_14_days":{"percent":"0","runs":"5","wins":"0"},"trainer_id":"trn_160758","trainer_location":"Newmarket, Suffolk","trainer_rtf":"","ts":"81","wind_surgery":"","wind_surgery_run":""}},"app__models__racecards__RunnerBasic":{"properties":{"horse":{"type":"string","title":"Horse"},"horse_id":{"type":"string","title":"Horse Id"},"age":{"type":"string","title":"Age"},"sex":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex"},"sex_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Code"},"colour":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Colour"},"region":{"type":"string","title":"Region"},"dam":{"type":"string","title":"Dam"},"dam_id":{"type":"string","title":"Dam Id"},"sire":{"type":"string","title":"Sire"},"sire_id":{"type":"string","title":"Sire Id"},"damsire":{"type":"string","title":"Damsire"},"damsire_id":{"type":"string","title":"Damsire Id"},"trainer":{"type":"string","title":"Trainer"},"trainer_id":{"type":"string","title":"Trainer Id"},"owner":{"type":"string","title":"Owner"},"owner_id":{"type":"string","title":"Owner Id"},"number":{"type":"string","title":"Number"},"draw":{"type":"string","title":"Draw"},"headgear":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headgear"},"lbs":{"type":"string","title":"Lbs"},"ofr":{"type":"string","title":"Ofr"},"jockey":{"type":"string","title":"Jockey"},"jockey_id":{"type":"string","title":"Jockey Id"},"last_run":{"type":"string","title":"Last Run"},"form":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Form"}},"type":"object","required":["horse","horse_id","age","sex","sex_code","colour","region","dam","dam_id","sire","sire_id","damsire","damsire_id","trainer","trainer_id","owner","owner_id","number","draw","headgear","lbs","ofr","jockey","jockey_id","last_run","form"],"title":"RunnerBasic","example":{"age":"6","colour":"b","dam":"Lerici","dam_id":"dam_4823441","damsire":"Woodman","damsire_id":"dsi_2126229","draw":"8","form":"3-7880","headgear":"","horse":"Pistoletto","horse_id":"hrs_18092480","jockey":"Adam Tracey(7)","jockey_id":"jky_301290","last_run":"25","lbs":"140","number":"1","ofr":"75","owner":"J A Thompson & S Russell","owner_id":"own_1227828","region":"USA","sex":"gelding","sex_code":"G","sire":"War Front","sire_id":"sir_4544750","trainer":"John Ryan","trainer_id":"trn_160758"}},"app__models__racecards__RunnerOdds":{"properties":{"horse_id":{"type":"string","title":"Horse Id"},"horse":{"type":"string","title":"Horse"},"dob":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dob"},"age":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Age"},"sex":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex"},"sex_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sex Code"},"colour":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Colour"},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region"},"breeder":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Breeder"},"dam":{"type":"string","title":"Dam"},"dam_id":{"type":"string","title":"Dam Id"},"dam_region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dam Region","default":""},"sire":{"type":"string","title":"Sire"},"sire_id":{"type":"string","title":"Sire Id"},"sire_region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sire Region","default":""},"damsire":{"type":"string","title":"Damsire"},"damsire_id":{"type":"string","title":"Damsire Id"},"damsire_region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Damsire Region","default":""},"trainer":{"type":"string","title":"Trainer"},"trainer_id":{"type":"string","title":"Trainer Id"},"trainer_location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trainer Location","default":""},"trainer_14_days":{"anyOf":[{"$ref":"#/components/schemas/RunnerTrainer14Days"},{"type":"null"}],"default":{}},"owner":{"type":"string","title":"Owner"},"owner_id":{"type":"string","title":"Owner Id"},"prev_trainers":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerPrevTrainer"},"type":"array"},{"type":"null"}],"title":"Prev Trainers","default":[]},"prev_owners":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerPrevOwner"},"type":"array"},{"type":"null"}],"title":"Prev Owners","default":[]},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment","default":""},"spotlight":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Spotlight","default":""},"quotes":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerQuote"},"type":"array"},{"type":"null"}],"title":"Quotes","default":[]},"stable_tour":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerStableTour"},"type":"array"},{"type":"null"}],"title":"Stable Tour","default":[]},"medical":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunnerMedical"},"type":"array"},{"type":"null"}],"title":"Medical","default":[]},"number":{"type":"string","title":"Number"},"draw":{"type":"string","title":"Draw"},"headgear":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headgear","default":""},"headgear_run":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headgear Run","default":""},"wind_surgery":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wind Surgery","default":""},"wind_surgery_run":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wind Surgery Run","default":""},"past_results_flags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Past Results Flags","default":[]},"lbs":{"type":"string","title":"Lbs"},"ofr":{"type":"string","title":"Ofr"},"rpr":{"type":"string","title":"Rpr"},"ts":{"type":"string","title":"Ts"},"jockey":{"type":"string","title":"Jockey"},"jockey_id":{"type":"string","title":"Jockey Id"},"silk_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Silk Url","default":""},"last_run":{"type":"string","title":"Last Run"},"form":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Form"},"trainer_rtf":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trainer Rtf"},"odds":{"anyOf":[{"items":{"$ref":"#/components/schemas/OddsNoHistory"},"type":"array"},{"type":"null"}],"title":"Odds","default":[]}},"type":"object","required":["horse_id","horse","dob","age","sex","sex_code","colour","region","breeder","dam","dam_id","sire","sire_id","damsire","damsire_id","trainer","trainer_id","owner","owner_id","number","draw","lbs","ofr","rpr","ts","jockey","jockey_id","last_run","form","trainer_rtf"],"title":"RunnerOdds","example":{"age":"6","breeder":"Tada Nobutaka","colour":"b","comment":"Very useful in his prime but seems to have lost his way this year","dam":"Lerici","dam_id":"dam_4823441","dam_region":"USA","damsire":"Woodman","damsire_id":"dsi_2126229","damsire_region":"USA","dob":"2017-04-22","draw":"8","form":"3-7880","headgear":"","headgear_run":"","horse":"Pistoletto","horse_id":"hrs_18092480","jockey":"Adam Tracey(7)","jockey_id":"jky_301290","last_run":"25","lbs":"140","medical":[],"number":"1","odds":[{"bookmaker":"Bet365","decimal":"9","ew_denom":"5","ew_places":"3","fractional":"8","updated":"2023-05-02 12:36:43"},{"bookmaker":"Paddy Power","decimal":"8.5","ew_denom":"5","ew_places":"3","fractional":"15/2","updated":"2023-05-02 12:36:43"}],"ofr":"75","owner":"J A Thompson & S Russell","owner_id":"own_1227828","past_results_flags":[],"prev_owners":[{"change_date":"2021-08-07","owner":"G McGladery, J A Thompson & S Russell","owner_id":"own_1191676"}],"prev_trainers":[{"change_date":"2020-09-26","trainer":"A P O'Brien","trainer_id":"trn_71802"}],"quotes":[{"course":"Newmarket","course_id":"crs_988","date":"2019-10-11","distance_f":"5","distance_y":"1100","horse":"Pistoletto","horse_id":"hrs_18092480","quote":"He travelled nicely into the race - Kevin Buckley.","race":"Cornwallis Stakes (Group 3)","race_id":"rac_9618089"}],"region":"USA","rpr":"82","sex":"gelding","sex_code":"G","silk_url":"https://www.url-for-silk.com/svg/456789.svg","sire":"War Front","sire_id":"sir_4544750","sire_region":"USA","spotlight":"Very useful in his prime but seems to have lost his way this year; sure to appreciate today's ease in grade.","stable_tour":[],"trainer":"John Ryan","trainer_14_days":{"percent":"0","runs":"5","wins":"0"},"trainer_id":"trn_160758","trainer_location":"Newmarket, Suffolk","trainer_rtf":"","ts":"81","wind_surgery":"","wind_surgery_run":""}},"app__models__result__RunnerBasic":{"properties":{"horse_id":{"type":"string","title":"Horse Id"},"horse":{"type":"string","title":"Horse"},"sp":{"type":"string","title":"Sp"},"sp_dec":{"type":"string","title":"Sp Dec"},"number":{"type":"string","title":"Number"},"position":{"type":"string","title":"Position"},"draw":{"type":"string","title":"Draw"},"btn":{"type":"string","title":"Btn"},"ovr_btn":{"type":"string","title":"Ovr Btn"},"age":{"type":"string","title":"Age"},"sex":{"type":"string","title":"Sex"},"weight":{"type":"string","title":"Weight"},"weight_lbs":{"type":"string","title":"Weight Lbs"},"headgear":{"type":"string","title":"Headgear"},"time":{"type":"string","title":"Time"},"or":{"type":"string","title":"Or"},"rpr":{"type":"string","title":"Rpr"},"tsr":{"type":"string","title":"Tsr"},"prize":{"type":"string","title":"Prize"},"jockey":{"type":"string","title":"Jockey"},"jockey_claim_lbs":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jockey Claim Lbs","default":"0"},"jockey_id":{"type":"string","title":"Jockey Id"},"trainer":{"type":"string","title":"Trainer"},"trainer_id":{"type":"string","title":"Trainer Id"},"owner":{"type":"string","title":"Owner"},"owner_id":{"type":"string","title":"Owner Id"},"sire":{"type":"string","title":"Sire"},"sire_id":{"type":"string","title":"Sire Id"},"dam":{"type":"string","title":"Dam"},"dam_id":{"type":"string","title":"Dam Id"},"damsire":{"type":"string","title":"Damsire"},"damsire_id":{"type":"string","title":"Damsire Id"},"comment":{"type":"string","title":"Comment"},"silk_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Silk Url","default":""}},"type":"object","required":["horse_id","horse","sp","sp_dec","number","position","draw","btn","ovr_btn","age","sex","weight","weight_lbs","headgear","time","or","rpr","tsr","prize","jockey","jockey_id","trainer","trainer_id","owner","owner_id","sire","sire_id","dam","dam_id","damsire","damsire_id","comment"],"title":"RunnerBasic","example":{"age":"8","btn":"0","comment":"Held up in rear - headway 3 out - went third after 2 out - challenging when not fluent last - led home turn - ridden clear final furlong(op 13/2)","dam":"Save Me The Waltz (FR)","dam_id":"dam_4189605","damsire":"Halling","damsire_id":"dsi_675241","draw":"","headgear":"v","horse":"Mephisto (IRE)","horse_id":"hrs_19359270","jockey":"Shane Fitzgerald","jockey_claim_lbs":"0","jockey_id":"jky_302916","number":"2","or":"98","ovr_btn":"0","owner":"Restricted Movement Syndicate","owner_id":"own_1204876","position":"1","prize":"€5900","rpr":"105","sex":"G","silk_url":"https://www.url-for-silk.com/svg/456789.svg","sire":"Kendargent (FR)","sire_id":"sir_4514419","sp":"15/2","sp_dec":"8.50","time":"5:44.90","trainer":"Gerard Keane","trainer_id":"trn_86382","tsr":"64","weight":"11-12","weight_lbs":"166"}},"app__models__sires__Class":{"properties":{"class":{"type":"string","title":"Class"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["class","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Class","example":{"1_pl":-35.97,"1st":15,"2nd":23,"3rd":24,"4th":27,"a/e":0.53,"class":"Class 2","runners":225,"win_%":0.07}},"app__models__sires__Distance":{"properties":{"dist":{"type":"string","title":"Dist"},"dist_y":{"type":"string","title":"Dist Y"},"dist_m":{"type":"string","title":"Dist M"},"dist_f":{"type":"string","title":"Dist F"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["dist","dist_y","dist_m","dist_f","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Distance","example":{"1_pl":5.83,"1st":4,"2nd":10,"3rd":5,"4th":5,"a/e":0.45,"dist":"7f","dist_f":"7f","dist_m":"1400.0","dist_y":"1540","runners":75,"win_%":0.05}},"app__models__trainers__Course":{"properties":{"course":{"type":"string","title":"Course"},"course_id":{"type":"string","title":"Course Id"},"region":{"type":"string","title":"Region"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["course","course_id","region","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Course","example":{"1_pl":15.5,"1st":45,"2nd":30,"3rd":25,"4th":20,"a/e":1.05,"course":"Newmarket","course_id":"crs_988","region":"GB","runners":200,"win_%":0.23}},"app__models__trainers__Distance":{"properties":{"dist":{"type":"string","title":"Dist"},"dist_y":{"type":"string","title":"Dist Y"},"dist_m":{"type":"string","title":"Dist M"},"dist_f":{"type":"string","title":"Dist F"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["dist","dist_y","dist_m","dist_f","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Distance","example":{"1_pl":-59.87,"1st":77,"2nd":43,"3rd":52,"4th":32,"a/e":0.93,"dist":"1m","dist_f":"8f","dist_m":"1609","dist_y":"1760","runners":373,"win_%":0.21}},"app__models__trainers__Jockey":{"properties":{"jockey_id":{"type":"string","title":"Jockey Id"},"jockey":{"type":"string","title":"Jockey"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["jockey_id","jockey","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Jockey","example":{"1_pl":50.62,"1st":260,"2nd":148,"3rd":143,"4th":88,"a/e":1.01,"jockey":"William Buick","jockey_id":"jky_257379","runners":1038,"win_%":0.25}},"app__models__trainers__Owner":{"properties":{"owner_id":{"type":"string","title":"Owner Id"},"owner":{"type":"string","title":"Owner"},"runners":{"type":"integer","title":"Runners"},"1st":{"type":"integer","title":"1st place finishes"},"2nd":{"type":"integer","title":"2nd place finishes"},"3rd":{"type":"integer","title":"3rd place finishes"},"4th":{"type":"integer","title":"4th place finishes"},"a/e":{"type":"number","title":"Actual/expected"},"win_%":{"type":"number","title":"Win percentage (decimal)"},"1_pl":{"type":"number","title":"One unit p/l at SP"}},"type":"object","required":["owner_id","owner","runners","1st","2nd","3rd","4th","a/e","win_%","1_pl"],"title":"Owner","example":{"1_pl":25.0,"1st":120,"2nd":80,"3rd":60,"4th":40,"a/e":1.02,"owner":"Godolphin","owner_id":"own_199380","runners":500,"win_%":0.24}},"app__models__trainers__Trainer":{"properties":{"id":{"type":"string","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["id","name"],"title":"Trainer","example":{"id":"trn_255042","name":"Charlie Appleby"}}},"securitySchemes":{"HTTPBasic":{"type":"http","scheme":"basic"}}},"meta":{"title":"The Racing API - Documentation"},"tags":[{"name":"Introduction","description":"\n Welcome to The Racing API
\n The Racing API provides a set of RESTful JSON endpoints, returning horse racing data from our database of over 500,000 results and pre-race racecards. The API is designed to meet the needs of application developers, data scientists, businesses and websites requiring punctual and accurate horse racing data.
\n This API reference contains the technical documentation users will require to connect their integrations and third-party applications.
\n FAQ | Data Coverage | Terms of Service
\n For support or feature requests, please email support@theracingapi.com.
\n"},{"name":"Authentication","description":"\n Once you subscribe to The Racing API, you will be able to login to your dashboard and view your API username and password.
\n In order to authenticate your requests, you should use HTTP Basic Authentication, passing your username and password (Base64 encoded) in the Authorization header of your request.
\n If your account becomes inactive, your invoice payment is overdue or you attempt to access endpoints not available to your subscription level, requests will return a 401 Unauthorized response.
\n"},{"name":"Rate Limits","description":"\n Default limits for the API are 5 requests per second for paid plans. Free plan users are limited to 1 request per second.\n
Some endpoints may have higher or lower rate limits than the default - these are specified in the documentation for each endpoint.
\n Exceeding the rate limit for a given endpoint will return a 429 Too Many Requests response.\n
Additional IP based rate limits are applied via Cloudflare as a protective measure for the services endpoints - exceeding these will trigger cooling off periods before requests from that IP address are accepted again:
\n \n - >100 requests received in a 10 second period - 5 minutes cooling off
\n - >500 requests received in a 10 second period - 30 minutes cooling off
\n
\n We constantly monitor traffic and may temporarily manually block any user's API keys and IP/IP ranges where we detect problematic activity - we will notify the user via email if this happens, and reinstate access once the problematic activity/excessive requests have ceased.
\n"},{"name":"Endpoint Overview","description":"\n Core API
\n The Racing API's Core API provides access to our primary horse racing data set, containing over three decades of horse racing results and updating with the latest data every 5 minutes.
\n This API provides full pre and post race data coverage of UK and Irish horse racing, as well as global coverage of group level races and selected handicaps. You can view a breakdown of coverage by year and region here
\n Every user who subscribes to The Racing API gains access to some or all of the Core API endpoints, subject to their specific plan level.
\n The Core API is divided in to 11 top-level categories:
\n \n \n | Endpoint Category | \n Description | \n \n \n \n | /courses/ | \n Course and region data, such as course names, course ids and region codes. | \n
\n \n | /dams/ | \n Dam data and analysis. Dams can be searched for by name and results/analysis of their progeny's race performances can be queried by dam id. | \n
\n \n | /damsires/ | \n Damsire data and analysis. Damsires can be searched for by name and results/analysis of their grand offspring's race performances can be queried by damsire id. | \n
\n \n | /horses/ | \n Horse data and analysis. Horses can be searched for by name and results/analysis of their race performances can be queried by horse id. | \n
\n \n | /jockeys/ | \n Jockey data and analysis. Jockeys can be searched for by name and results/analysis of their race performances can be queried by jockey id. | \n
\n \n | /odds/ | \n Detailed odds data for a given runner, including price movements. | \n
\n \n | /owners/ | \n Owner data and analysis. Owners can be searched for by name and results/analysis of their horse's race performances can be queried by owner id. | \n
\n \n | /racecards/ | \n Detailed racecard (pre-race) data. | \n
\n \n | /results/ | \n Detailed results (post-race) data. | \n
\n \n | /sires/ | \n Sire data and analysis. Sires can be searched for by name and results/analysis of their progeny's race performances can be queried by sire id. | \n
\n \n | /trainers/ | \n Trainer data and analysis. Trainers can be searched for by name and results/analysis of their horses's race performances can be queried by trainer id. | \n
\n \n
\n Core API Endpoints By Plan Level
\n We have also grouped the core API endpoints by plan level to make it easier to find the endpoints relevant to your API key permissions:
\n \n Analysis Endpoints
\n The Core API's analysis endpoints perform data aggregation to return helpful betting & probability insights.
\n The statistics returned by these endpoints include:
\n \n - Win Percentage (win_%): The number of wins divided by total runs/rides for a given query. For example, a win_% figure of 0.18 represents an 18% win percentage.
\n - 1 Unit Profit/Loss (1_pl): Profit/loss for total rides/runs for a given query, if a 1 unit stake was placed at SP.
\n - Actual/Expected (a/e): Actual number of wins, divided by expected number of wins for a given query. Expected number of wins is calculated from SP. For a detailed explanation of this statistic, see here.
\n
\n IMPORTANT NOTE: Anaysis endpoints only perform data aggregation on results for which SP odds are available.
\n Regional API's
\n The Regional APIs provide access to additional data sets that provide complete coverage for a given region. The data and fields returned are also much more specific to that region than they may be in the Core API.
\n Accessing the endpoints for these data sets requires purchasing the relevant regional data add-on for your plan, which can be requested by emailing support@theracingapi.com.
\n The following top-level regional endpoints are available:\n
\n \n | Region | \n Description | \n \n \n \n | /australia/ | \n Meets and races for Australia | \n
\n \n | /north-america/ | \n Meets, entries and results for the USA and Canada | \n
\n \n
\n"},{"name":"Changelog","description":"\n v1.4.3 (Current - February 2026)
\n \n - OpenAPI Response Examples - Added rich response examples to Pydantic models for all regions
\n - Pydantic v2 Migration - Upgraded to Pydantic 2 with model updates
\n - Analytics Backend Optimization - New synchronised columnar database for results data, utilised by analysis endpoints for substantially improved performance
\n - Default Date Parameters - Results endpoints now include default date parameters
\n
\n v1.4.2 (January 2026)
\n \n - Async Database Operations - Full async migration for database operations
\n - Enhanced Documentation - Code examples and endpoints organized by plan level
\n - Improved Error Tracking - Optimized error tracing
\n
\n v1.4.1 (December 2025)
\n \n - Autoscaling - Added dynamic worker scaling
\n - Enhanced Error Monitoring - Improved error tracking and monitoring
\n - Database Compression - Added compression for database connections
\n - Australia Date Validation - Improved date handling for Australia endpoints
\n
\n v1.4.0 (August-October 2025)
\n \n - Australia Regional API - Full Australia racing data support (meets, races, runners)
\n - Australia Historical Results Add-on - Historical data access as add-on feature
\n - Odds History - Optional bookmaker odds history
\n
\n v1.3.1 (June-July 2025)
\n \n - Response Compression - GZip compression middleware
\n - Faster Serialization - Optimized JSON serialization for responses
\n - Racecard Sorting - Improved racecard ordering
\n - Health Check Endpoint - Added /health endpoint
\n
\n v1.3.0 (July-August 2023)
\n \n - North America Regional API - Full North America racing data support (meets, entries, results)
\n - Meets Endpoint - Track meet information
\n - Racecard Summaries - Lightweight racecard summary endpoint
\n - Jumps Racing Filter - Filter racecards by jumps/flat racing
\n
\n v1.2.0 (April-June 2023)
\n \n - Odds Data - Live odds data for racecards
\n - Pro Tier - New Pro subscription tier with advanced features
\n - Pro Add-on Support - Add-on purchases for additional features
\n - Big Races Endpoints - Featured/notable race identification
\n - Endpoint Deprecation System - Managed deprecation for API versioning
\n
\n v1.1.0 (February-March 2023)
\n \n - Region Support - GB/IE region filtering for results
\n - Today's Results - Dedicated today's results endpoint
\n - Historic Results - Date range queries for historical data
\n - Big Races Feature - Big race entries identification
\n - Silk URLs - Jockey silk image URLs in responses
\n
\n v1.0.0 (January 2023 - Initial Release)
\n \n - Core API - FastAPI-based REST API
\n - API Authentication - Secure authentication system
\n - Racecards Endpoints - Basic and advanced racecards
\n - Courses Endpoint - List courses with region filtering
\n - Results Endpoints - Horse racing results
\n - Error Tracking - Integrated error tracking
\n
\n"}],"x-tagGroups":[{"name":"Get Started","tags":["Introduction","Authentication","Endpoint Overview","Rate Limits","Changelog"]},{"name":"Core API","tags":["Courses","Dams","Damsires","Horses","Jockeys","Odds","Owners","Racecards","Results","Sires","Trainers"]},{"name":"Core API (by plan level)","tags":["Free Plan","Basic Plan","Standard Plan","Pro Plan"]},{"name":"Regional APIs","tags":["Australia","North America"]}]}
\ No newline at end of file
diff --git a/HorseRacingPredictor/HorseRacingPredictor/Football/Main.cs b/HorseRacingPredictor/HorseRacingPredictor/Football/Main.cs
index 7e895ca..3c7d5f1 100644
--- a/HorseRacingPredictor/HorseRacingPredictor/Football/Main.cs
+++ b/HorseRacingPredictor/HorseRacingPredictor/Football/Main.cs
@@ -97,6 +97,42 @@ namespace HorseRacingPredictor.Football
}
}
+ ///
+ /// Recupera solo l'elenco delle partite per la data specificata e le restituisce come DataTable semplice
+ ///
+ public DataTable GetTodayFixtures(DateTime date, IProgress progressCallback = null, IProgress statusCallback = null)
+ {
+ try
+ {
+ statusCallback?.Report("Scaricamento elenco partite...");
+ progressCallback?.Report(10);
+
+ var fixturesResponse = GetFixtures(date);
+ progressCallback?.Report(40);
+
+ statusCallback?.Report("Elaborazione partite...");
+ var table = CreateFixturesDataTable(fixturesResponse);
+ progressCallback?.Report(50);
+
+ statusCallback?.Report("Scaricamento quote...");
+ var oddsResponses = GetOdds(date);
+ progressCallback?.Report(80);
+
+ statusCallback?.Report("Integrazione quote...");
+ ParseOddsIntoTable(table, oddsResponses);
+ progressCallback?.Report(100);
+
+ statusCallback?.Report($"Trovate {table.Rows.Count} partite con quote");
+ return table;
+ }
+ catch (Exception ex)
+ {
+ _database.LogError("recupero partite del giorno", ex);
+ statusCallback?.Report($"Errore: {ex.Message}");
+ return CreateEmptyResultTable();
+ }
+ }
+
///
/// Scarica i dati dalle API e li salva nella tabella di frontiera API_Response
///
@@ -563,6 +599,13 @@ namespace HorseRacingPredictor.Football
dataTable.Columns.Add("Quota Casa", typeof(string));
dataTable.Columns.Add("Quota Pareggio", typeof(string));
dataTable.Columns.Add("Quota Trasferta", typeof(string));
+ dataTable.Columns.Add("Over 2.5", typeof(string));
+ dataTable.Columns.Add("Under 2.5", typeof(string));
+ dataTable.Columns.Add("BTTS Sì", typeof(string));
+ dataTable.Columns.Add("BTTS No", typeof(string));
+ dataTable.Columns.Add("Doppia Casa/X", typeof(string));
+ dataTable.Columns.Add("Doppia Casa/Trasf", typeof(string));
+ dataTable.Columns.Add("Doppia X/Trasf", typeof(string));
dataTable.Columns.Add("Previsione", typeof(string));
return dataTable;
@@ -596,28 +639,65 @@ namespace HorseRacingPredictor.Football
{
try
{
+ // Verifica che le proprietà essenziali esistano
+ if (!item.TryGetProperty("fixture", out var fixtureEl) ||
+ !item.TryGetProperty("league", out var leagueEl) ||
+ !item.TryGetProperty("teams", out var teamsEl))
+ {
+ continue;
+ }
+
var row = dataTable.NewRow();
- // Estrai i dati dalla risposta JSON
- row["ID"] = item.GetProperty("fixture").GetProperty("id").GetInt32();
- row["Paese"] = item.GetProperty("league").GetProperty("country").GetString();
- row["Campionato"] = item.GetProperty("league").GetProperty("name").GetString();
- row["Data / Ora"] = DateTime.Parse(item.GetProperty("fixture").GetProperty("date").GetString());
- row["Stato"] = item.GetProperty("fixture").GetProperty("status").GetProperty("long").GetString();
- row["Casa"] = item.GetProperty("teams").GetProperty("home").GetProperty("name").GetString();
- row["Trasferta"] = item.GetProperty("teams").GetProperty("away").GetProperty("name").GetString();
+ row["ID"] = fixtureEl.TryGetProperty("id", out var idEl) ? idEl.GetInt32() : 0;
+ row["Paese"] = leagueEl.TryGetProperty("country", out var countryEl) ? countryEl.GetString() ?? "" : "";
+ row["Campionato"] = leagueEl.TryGetProperty("name", out var leagueNameEl) ? leagueNameEl.GetString() ?? "" : "";
- // Per i goals, controlla se sono null
- var goalsElement = item.GetProperty("goals");
- row["Goals Casa"] = goalsElement.GetProperty("home").ValueKind == JsonValueKind.Null ?
- 0 : goalsElement.GetProperty("home").GetInt32();
- row["Goals Trasferta"] = goalsElement.GetProperty("away").ValueKind == JsonValueKind.Null ?
- 0 : goalsElement.GetProperty("away").GetInt32();
+ if (fixtureEl.TryGetProperty("date", out var dateEl) && dateEl.ValueKind == JsonValueKind.String)
+ {
+ DateTime.TryParse(dateEl.GetString(), out var parsedDate);
+ row["Data / Ora"] = parsedDate;
+ }
+ else
+ {
+ row["Data / Ora"] = DBNull.Value;
+ }
+
+ row["Stato"] = fixtureEl.TryGetProperty("status", out var statusEl) &&
+ statusEl.TryGetProperty("long", out var statusLong)
+ ? statusLong.GetString() ?? "" : "";
+
+ row["Casa"] = teamsEl.TryGetProperty("home", out var homeEl) &&
+ homeEl.TryGetProperty("name", out var homeNameEl)
+ ? homeNameEl.GetString() ?? "" : "";
+ row["Trasferta"] = teamsEl.TryGetProperty("away", out var awayEl) &&
+ awayEl.TryGetProperty("name", out var awayNameEl)
+ ? awayNameEl.GetString() ?? "" : "";
+
+ // Goals (possono essere null per partite non iniziate)
+ if (item.TryGetProperty("goals", out var goalsElement))
+ {
+ row["Goals Casa"] = goalsElement.TryGetProperty("home", out var ghEl) && ghEl.ValueKind == JsonValueKind.Number
+ ? ghEl.GetInt32() : 0;
+ row["Goals Trasferta"] = goalsElement.TryGetProperty("away", out var gaEl) && gaEl.ValueKind == JsonValueKind.Number
+ ? gaEl.GetInt32() : 0;
+ }
+ else
+ {
+ row["Goals Casa"] = 0;
+ row["Goals Trasferta"] = 0;
+ }
- // Le colonne delle quote e previsione rimangono temporaneamente vuote
row["Quota Casa"] = DBNull.Value;
row["Quota Pareggio"] = DBNull.Value;
row["Quota Trasferta"] = DBNull.Value;
+ row["Over 2.5"] = DBNull.Value;
+ row["Under 2.5"] = DBNull.Value;
+ row["BTTS Sì"] = DBNull.Value;
+ row["BTTS No"] = DBNull.Value;
+ row["Doppia Casa/X"] = DBNull.Value;
+ row["Doppia Casa/Trasf"] = DBNull.Value;
+ row["Doppia X/Trasf"] = DBNull.Value;
row["Previsione"] = DBNull.Value;
dataTable.Rows.Add(row);
@@ -625,14 +705,12 @@ namespace HorseRacingPredictor.Football
catch (Exception ex)
{
_database.LogError($"elaborazione riga partita", ex);
- // Continua con la prossima partita
}
}
}
catch (Exception ex)
{
_database.LogError("creazione tabella partite", ex);
- // Ritorna la tabella vuota in caso di errore
}
return dataTable;
@@ -788,6 +866,162 @@ namespace HorseRacingPredictor.Football
return table;
}
+ ///
+ /// Analizza le risposte delle quote dall'API e le inserisce direttamente nella DataTable delle partite.
+ /// Utilizza Bet365 (ID 8) come bookmaker principale; se non presente, usa il primo bookmaker disponibile.
+ ///
+ private void ParseOddsIntoTable(DataTable fixturesTable, List oddsResponses)
+ {
+ if (oddsResponses == null || oddsResponses.Count == 0 || fixturesTable == null || fixturesTable.Rows.Count == 0)
+ return;
+
+ try
+ {
+ foreach (var response in oddsResponses)
+ {
+ if (response == null || !response.IsSuccessful || string.IsNullOrEmpty(response.Content))
+ continue;
+
+ var json = JsonDocument.Parse(response.Content).RootElement;
+ if (!json.TryGetProperty("response", out var responseElement))
+ continue;
+
+ foreach (var item in responseElement.EnumerateArray())
+ {
+ try
+ {
+ if (!item.TryGetProperty("fixture", out var fixtureEl))
+ continue;
+
+ int fixtureId = fixtureEl.GetProperty("id").GetInt32();
+
+ DataRow[] matchingRows = fixturesTable.Select($"ID = {fixtureId}");
+ if (matchingRows.Length == 0)
+ continue;
+
+ DataRow row = matchingRows[0];
+
+ if (!item.TryGetProperty("bookmakers", out var bookmakersEl))
+ continue;
+
+ // Cerca Bet365 (ID 8), altrimenti usa il primo bookmaker
+ JsonElement selectedBookmaker = default;
+ bool found = false;
+
+ foreach (var bm in bookmakersEl.EnumerateArray())
+ {
+ if (bm.TryGetProperty("id", out var bmId) && bmId.GetInt32() == 8)
+ {
+ selectedBookmaker = bm;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ var enumerator = bookmakersEl.EnumerateArray();
+ if (enumerator.MoveNext())
+ {
+ selectedBookmaker = enumerator.Current;
+ found = true;
+ }
+ }
+
+ if (!found || !selectedBookmaker.TryGetProperty("bets", out var betsEl))
+ continue;
+
+ foreach (var bet in betsEl.EnumerateArray())
+ {
+ string betName = bet.TryGetProperty("name", out var nameEl) ? nameEl.GetString() : "";
+ if (string.IsNullOrEmpty(betName) || !bet.TryGetProperty("values", out var valuesEl))
+ continue;
+
+ switch (betName)
+ {
+ case "Match Winner":
+ foreach (var v in valuesEl.EnumerateArray())
+ {
+ string val = GetOddValueString(v, "value");
+ string odd = GetOddValueString(v, "odd");
+ if (val == "Home") row["Quota Casa"] = odd;
+ else if (val == "Draw") row["Quota Pareggio"] = odd;
+ else if (val == "Away") row["Quota Trasferta"] = odd;
+ }
+ break;
+
+ case "Goals Over/Under":
+ foreach (var v in valuesEl.EnumerateArray())
+ {
+ string val = GetOddValueString(v, "value");
+ string odd = GetOddValueString(v, "odd");
+ if (val == "Over 2.5") row["Over 2.5"] = odd;
+ else if (val == "Under 2.5") row["Under 2.5"] = odd;
+ }
+ break;
+
+ case "Both Teams Score":
+ foreach (var v in valuesEl.EnumerateArray())
+ {
+ string val = GetOddValueString(v, "value");
+ string odd = GetOddValueString(v, "odd");
+ if (val == "Yes") row["BTTS Sì"] = odd;
+ else if (val == "No") row["BTTS No"] = odd;
+ }
+ break;
+
+ case "Double Chance":
+ foreach (var v in valuesEl.EnumerateArray())
+ {
+ string val = GetOddValueString(v, "value");
+ string odd = GetOddValueString(v, "odd");
+ if (val == "Home/Draw") row["Doppia Casa/X"] = odd;
+ else if (val == "Home/Away") row["Doppia Casa/Trasf"] = odd;
+ else if (val == "Draw/Away") row["Doppia X/Trasf"] = odd;
+ }
+ break;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _database.LogError("elaborazione quote per singola partita", ex);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _database.LogError("parsing quote nella tabella", ex);
+ }
+ }
+
+ ///
+ /// Estrae un valore stringa da un JsonElement in modo sicuro
+ ///
+ private string GetOddValueString(JsonElement element, string propertyName)
+ {
+ try
+ {
+ if (!element.TryGetProperty(propertyName, out var prop))
+ return "";
+
+ switch (prop.ValueKind)
+ {
+ case JsonValueKind.String:
+ return prop.GetString() ?? "";
+ case JsonValueKind.Number:
+ return prop.GetDecimal().ToString(System.Globalization.CultureInfo.InvariantCulture);
+ default:
+ return prop.ToString();
+ }
+ }
+ catch
+ {
+ return "";
+ }
+ }
+
///
/// Elabora le risposte API non elaborate presenti nel database
///
diff --git a/HorseRacingPredictor/HorseRacingPredictor/Football/Manager/API.cs b/HorseRacingPredictor/HorseRacingPredictor/Football/Manager/API.cs
index 153ddb7..e52d117 100644
--- a/HorseRacingPredictor/HorseRacingPredictor/Football/Manager/API.cs
+++ b/HorseRacingPredictor/HorseRacingPredictor/Football/Manager/API.cs
@@ -41,26 +41,33 @@ namespace HorseRacingPredictor.Football.Manager
///
protected RestResponse ExecuteRequest(string endpoint, string apiType = null, string parameters = null)
{
+ string url = $"{BaseUrl}/{endpoint}";
+ var response = ExecuteApiRequest(url, ApiKey, KeyHeader, HostHeader, HostValue);
+
+ // Salva la risposta su file per debug (non bloccare in caso di errore)
try
{
- string url = $"{BaseUrl}/{endpoint}";
- var response = ExecuteApiRequest(url, ApiKey, KeyHeader, HostHeader, HostValue);
-
- // Salva la risposta su file per debug
SaveResponseToFile(url, response.Content, "FootballApiResponses");
-
- // Salva la risposta nel database
- if (apiType != null)
- {
- _apiResponseRepository.InsertApiResponse(apiType, endpoint, parameters, response);
- }
-
- return response;
}
catch (Exception ex)
{
- throw new Exception($"Errore durante la richiesta all'endpoint {endpoint}: {ex.Message}", ex);
+ Console.WriteLine($"Avviso: impossibile salvare risposta su file: {ex.Message}");
}
+
+ // Salva la risposta nel database (non bloccare in caso di errore)
+ if (apiType != null)
+ {
+ try
+ {
+ _apiResponseRepository.InsertApiResponse(apiType, endpoint, parameters, response);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Avviso: impossibile salvare risposta nel database: {ex.Message}");
+ }
+ }
+
+ return response;
}
///
@@ -68,26 +75,33 @@ namespace HorseRacingPredictor.Football.Manager
///
protected async Task ExecuteRequestAsync(string endpoint, string apiType = null, string parameters = null)
{
+ string url = $"{BaseUrl}/{endpoint}";
+ var response = await ExecuteApiRequestAsync(url, ApiKey, KeyHeader, HostHeader, HostValue);
+
+ // Salva la risposta su file per debug (non bloccare in caso di errore)
try
{
- string url = $"{BaseUrl}/{endpoint}";
- var response = await ExecuteApiRequestAsync(url, ApiKey, KeyHeader, HostHeader, HostValue);
-
- // Salva la risposta su file per debug
SaveResponseToFile(url, response.Content, "FootballApiResponses");
-
- // Salva la risposta nel database
- if (apiType != null)
- {
- _apiResponseRepository.InsertApiResponse(apiType, endpoint, parameters, response);
- }
-
- return response;
}
catch (Exception ex)
{
- throw new Exception($"Errore durante la richiesta all'endpoint {endpoint}: {ex.Message}", ex);
+ Console.WriteLine($"Avviso: impossibile salvare risposta su file: {ex.Message}");
}
+
+ // Salva la risposta nel database (non bloccare in caso di errore)
+ if (apiType != null)
+ {
+ try
+ {
+ _apiResponseRepository.InsertApiResponse(apiType, endpoint, parameters, response);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Avviso: impossibile salvare risposta nel database: {ex.Message}");
+ }
+ }
+
+ return response;
}
#region Leagues
diff --git a/HorseRacingPredictor/HorseRacingPredictor/HorseRacing/API/RacingApiClient.cs b/HorseRacingPredictor/HorseRacingPredictor/HorseRacing/API/RacingApiClient.cs
new file mode 100644
index 0000000..044636c
--- /dev/null
+++ b/HorseRacingPredictor/HorseRacingPredictor/HorseRacing/API/RacingApiClient.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Text;
+using System.Threading;
+using RestSharp;
+
+namespace HorseRacingPredictor.HorseRacing.API
+{
+ ///
+ /// Client per The Racing API (theracingapi.com)
+ /// Utilizza HTTP Basic Authentication
+ ///
+ internal class RacingApiClient
+ {
+ private const string BaseUrl = "https://api.theracingapi.com/v1";
+ private const int DefaultDelay = 1100; // Rate limit: 1 req/sec per Free plan
+
+ private readonly string _username;
+ private readonly string _password;
+
+ public RacingApiClient(string username, string password)
+ {
+ _username = username;
+ _password = password;
+ }
+
+ ///
+ /// Esegue una richiesta GET autenticata con HTTP Basic Auth
+ ///
+ private RestResponse ExecuteRequest(string endpoint, int delay = DefaultDelay)
+ {
+ string url = $"{BaseUrl}/{endpoint}";
+ var client = new RestClient(url);
+ var request = new RestRequest();
+
+ string credentials = Convert.ToBase64String(
+ Encoding.ASCII.GetBytes($"{_username}:{_password}"));
+ request.AddHeader("Authorization", $"Basic {credentials}");
+
+ try
+ {
+ var response = client.Execute(request);
+ if (!response.IsSuccessful)
+ {
+ throw new Exception(
+ $"Errore API Racing ({(int)response.StatusCode}): {response.StatusDescription}");
+ }
+
+ if (delay > 0)
+ Thread.Sleep(delay);
+
+ return response;
+ }
+ catch (Exception ex) when (!(ex.Message.StartsWith("Errore API Racing")))
+ {
+ throw new Exception($"Errore durante la richiesta a Racing API: {ex.Message}", ex);
+ }
+ }
+
+ ///
+ /// Ottiene le racecard (programma corse) per oggi o domani
+ ///
+ /// "today" oppure "tomorrow"
+ /// Codici regione opzionali (es. "gb", "ire")
+ public RestResponse GetRacecardsFree(string day = "today", string[] regionCodes = null)
+ {
+ var sb = new StringBuilder("racecards/free?");
+ sb.Append($"day={day}");
+
+ if (regionCodes != null && regionCodes.Length > 0)
+ {
+ foreach (var rc in regionCodes)
+ sb.Append($"®ion_codes={rc}");
+ }
+
+ return ExecuteRequest(sb.ToString());
+ }
+
+ ///
+ /// Ottiene le racecard standard per oggi o domani
+ ///
+ public RestResponse GetRacecardsStandard(string day = "today", string[] regionCodes = null)
+ {
+ var sb = new StringBuilder("racecards/standard?");
+ sb.Append($"day={day}");
+
+ if (regionCodes != null && regionCodes.Length > 0)
+ {
+ foreach (var rc in regionCodes)
+ sb.Append($"®ion_codes={rc}");
+ }
+
+ return ExecuteRequest(sb.ToString());
+ }
+
+ ///
+ /// Ottiene i risultati per un intervallo di date
+ ///
+ public RestResponse GetResults(DateTime startDate, DateTime endDate, string[] regionCodes = null)
+ {
+ var sb = new StringBuilder("results?");
+ sb.Append($"start_date={startDate:yyyy-MM-dd}");
+ sb.Append($"&end_date={endDate:yyyy-MM-dd}");
+
+ if (regionCodes != null && regionCodes.Length > 0)
+ {
+ foreach (var rc in regionCodes)
+ sb.Append($"®ion={rc}");
+ }
+
+ return ExecuteRequest(sb.ToString());
+ }
+
+ ///
+ /// Ottiene l'elenco delle regioni disponibili
+ ///
+ public RestResponse GetRegions()
+ {
+ return ExecuteRequest("courses/regions");
+ }
+
+ ///
+ /// Ottiene l'elenco dei corsi per le regioni specificate
+ ///
+ public RestResponse GetCourses(string[] regionCodes = null)
+ {
+ var sb = new StringBuilder("courses?");
+ if (regionCodes != null && regionCodes.Length > 0)
+ {
+ foreach (var rc in regionCodes)
+ sb.Append($"®ion_codes={rc}");
+ }
+
+ return ExecuteRequest(sb.ToString());
+ }
+ }
+}
diff --git a/HorseRacingPredictor/HorseRacingPredictor/HorseRacing/Main.cs b/HorseRacingPredictor/HorseRacingPredictor/HorseRacing/Main.cs
new file mode 100644
index 0000000..2cb6634
--- /dev/null
+++ b/HorseRacingPredictor/HorseRacingPredictor/HorseRacing/Main.cs
@@ -0,0 +1,266 @@
+using System;
+using System.Data;
+using System.Text.Json;
+using HorseRacingPredictor.HorseRacing.API;
+
+namespace HorseRacingPredictor.HorseRacing
+{
+ ///
+ /// Gestore centralizzato per la sezione Corse dei Cavalli.
+ /// Scarica i dati da The Racing API e li converte in DataTable.
+ ///
+ public class Main
+ {
+ private RacingApiClient _client;
+
+ public Main(string username, string password)
+ {
+ _client = new RacingApiClient(username, password);
+ }
+
+ ///
+ /// Aggiorna le credenziali API
+ ///
+ public void UpdateCredentials(string username, string password)
+ {
+ _client = new RacingApiClient(username, password);
+ }
+
+ ///
+ /// Scarica le racecard (programma corse) per oggi o domani e le restituisce come DataTable
+ ///
+ public DataTable GetRacecards(string day, IProgress progress = null, IProgress status = null)
+ {
+ try
+ {
+ status?.Report("Connessione a The Racing API...");
+ progress?.Report(10);
+
+ var response = _client.GetRacecardsFree(day);
+ progress?.Report(60);
+
+ status?.Report("Elaborazione racecard...");
+ var table = ParseRacecardsResponse(response.Content);
+ progress?.Report(100);
+
+ status?.Report($"Trovate {table.Rows.Count} corse");
+ return table;
+ }
+ catch (Exception ex)
+ {
+ status?.Report($"Errore: {ex.Message}");
+ return CreateEmptyRacecardsTable();
+ }
+ }
+
+ ///
+ /// Scarica i risultati per un intervallo di date
+ ///
+ public DataTable GetResults(DateTime startDate, DateTime endDate,
+ IProgress progress = null, IProgress status = null)
+ {
+ try
+ {
+ status?.Report("Scaricamento risultati...");
+ progress?.Report(10);
+
+ var response = _client.GetResults(startDate, endDate);
+ progress?.Report(60);
+
+ status?.Report("Elaborazione risultati...");
+ var table = ParseResultsResponse(response.Content);
+ progress?.Report(100);
+
+ status?.Report($"Trovati {table.Rows.Count} risultati");
+ return table;
+ }
+ catch (Exception ex)
+ {
+ status?.Report($"Errore: {ex.Message}");
+ return CreateEmptyResultsTable();
+ }
+ }
+
+ #region DataTable creation
+
+ private DataTable CreateEmptyRacecardsTable()
+ {
+ var dt = new DataTable();
+ dt.Columns.Add("Ora", typeof(string));
+ dt.Columns.Add("Ippodromo", typeof(string));
+ dt.Columns.Add("Regione", typeof(string));
+ dt.Columns.Add("Corsa", typeof(string));
+ dt.Columns.Add("Distanza", typeof(string));
+ dt.Columns.Add("Tipo", typeof(string));
+ dt.Columns.Add("Classe", typeof(string));
+ dt.Columns.Add("Terreno", typeof(string));
+ dt.Columns.Add("N. Corridori", typeof(int));
+ dt.Columns.Add("Et", typeof(string));
+ dt.Columns.Add("Premio", typeof(string));
+ return dt;
+ }
+
+ private DataTable CreateEmptyResultsTable()
+ {
+ var dt = new DataTable();
+ dt.Columns.Add("Data", typeof(string));
+ dt.Columns.Add("Ippodromo", typeof(string));
+ dt.Columns.Add("Corsa", typeof(string));
+ dt.Columns.Add("Distanza", typeof(string));
+ dt.Columns.Add("Terreno", typeof(string));
+ dt.Columns.Add("1 Classificato", typeof(string));
+ dt.Columns.Add("2 Classificato", typeof(string));
+ dt.Columns.Add("3 Classificato", typeof(string));
+ dt.Columns.Add("Fantino 1", typeof(string));
+ dt.Columns.Add("SP 1", typeof(string));
+ return dt;
+ }
+
+ #endregion
+
+ #region JSON Parsing
+
+ private DataTable ParseRacecardsResponse(string json)
+ {
+ var dt = CreateEmptyRacecardsTable();
+ if (string.IsNullOrEmpty(json)) return dt;
+
+ try
+ {
+ using (var doc = JsonDocument.Parse(json))
+ {
+ var root = doc.RootElement;
+
+ if (!root.TryGetProperty("racecards", out var racecardsEl))
+ return dt;
+
+ foreach (var rc in racecardsEl.EnumerateArray())
+ {
+ try
+ {
+ var row = dt.NewRow();
+
+ row["Ora"] = GetString(rc, "off_time", "");
+ row["Ippodromo"] = GetString(rc, "course", "");
+ row["Regione"] = GetString(rc, "region", "");
+ row["Corsa"] = GetString(rc, "race_name", "");
+ row["Distanza"] = GetString(rc, "distance", "");
+ row["Tipo"] = GetString(rc, "type", "");
+ row["Classe"] = GetString(rc, "race_class", "");
+ row["Terreno"] = GetString(rc, "going", "");
+ row["Et"] = GetString(rc, "age_band", "");
+ row["Premio"] = GetString(rc, "prize", "");
+
+ if (rc.TryGetProperty("runners", out var runnersEl) &&
+ runnersEl.ValueKind == JsonValueKind.Array)
+ {
+ row["N. Corridori"] = runnersEl.GetArrayLength();
+ }
+ else if (rc.TryGetProperty("field_size", out var fsEl) &&
+ fsEl.ValueKind == JsonValueKind.Number)
+ {
+ row["N. Corridori"] = fsEl.GetInt32();
+ }
+ else
+ {
+ row["N. Corridori"] = 0;
+ }
+
+ dt.Rows.Add(row);
+ }
+ catch
+ {
+ // Salta righe problematiche
+ }
+ }
+ }
+ }
+ catch
+ {
+ // Restituisci tabella vuota in caso di errore di parsing
+ }
+
+ return dt;
+ }
+
+ private DataTable ParseResultsResponse(string json)
+ {
+ var dt = CreateEmptyResultsTable();
+ if (string.IsNullOrEmpty(json)) return dt;
+
+ try
+ {
+ using (var doc = JsonDocument.Parse(json))
+ {
+ var root = doc.RootElement;
+
+ if (!root.TryGetProperty("results", out var resultsEl))
+ return dt;
+
+ foreach (var res in resultsEl.EnumerateArray())
+ {
+ try
+ {
+ var row = dt.NewRow();
+
+ row["Data"] = GetString(res, "date", "");
+ row["Ippodromo"] = GetString(res, "course", "");
+ row["Corsa"] = GetString(res, "race_name", "");
+ row["Distanza"] = GetString(res, "distance", "");
+ row["Terreno"] = GetString(res, "going", "");
+
+ if (res.TryGetProperty("runners", out var runnersEl) &&
+ runnersEl.ValueKind == JsonValueKind.Array)
+ {
+ int idx = 0;
+ foreach (var runner in runnersEl.EnumerateArray())
+ {
+ var pos = GetString(runner, "position", "");
+ if (pos == "1" || idx == 0)
+ {
+ row["1 Classificato"] = GetString(runner, "horse", "");
+ row["Fantino 1"] = GetString(runner, "jockey", "");
+ row["SP 1"] = GetString(runner, "sp", "");
+ }
+ else if (pos == "2" || idx == 1)
+ {
+ row["2 Classificato"] = GetString(runner, "horse", "");
+ }
+ else if (pos == "3" || idx == 2)
+ {
+ row["3 Classificato"] = GetString(runner, "horse", "");
+ }
+ idx++;
+ if (idx >= 3) break;
+ }
+ }
+
+ dt.Rows.Add(row);
+ }
+ catch
+ {
+ // Salta righe problematiche
+ }
+ }
+ }
+ }
+ catch
+ {
+ // Restituisci tabella vuota in caso di errore di parsing
+ }
+
+ return dt;
+ }
+
+ private static string GetString(JsonElement el, string property, string defaultValue)
+ {
+ if (el.TryGetProperty(property, out var prop) && prop.ValueKind == JsonValueKind.String)
+ return prop.GetString() ?? defaultValue;
+ if (el.TryGetProperty(property, out prop) && prop.ValueKind == JsonValueKind.Number)
+ return prop.ToString();
+ return defaultValue;
+ }
+
+ #endregion
+ }
+}
diff --git a/HorseRacingPredictor/HorseRacingPredictor/Main.Designer.cs b/HorseRacingPredictor/HorseRacingPredictor/Main.Designer.cs
index 943225d..d0f7732 100644
--- a/HorseRacingPredictor/HorseRacingPredictor/Main.Designer.cs
+++ b/HorseRacingPredictor/HorseRacingPredictor/Main.Designer.cs
@@ -1,360 +1,583 @@
using System;
-using System.Data;
+using System.Drawing;
using System.Windows.Forms;
+using BettingPredictor.UI;
namespace BettingPredictor
{
partial class Main
{
- // Container
private System.ComponentModel.IContainer components = null;
- // Grafica
- private TabControl tabControl;
+ // Layout
+ private Panel panelSidebar;
+ private Panel panelContent;
+ private Panel panelHeader;
+ private Label labelAppTitle;
+ private Label labelPageTitle;
- // Horse tab
- private TabPage tabPageHorse;
- private TextBox textBoxFolderPath;
- private Button buttonBrowse;
- private Button buttonPredict; // New button for predictions
- private Button buttonImport; // New button for import
- private ProgressBar progressBarHorse;
- private Label labelStatusHorse;
- private DataGridView dataGridViewHorse;
+ // Nav
+ private NavButton navFootball;
+ private NavButton navHorseRacing;
+ private NavButton navSettings;
+ private NavButton navInfo;
- // Replace the buttonLoadFootball with buttonImportFootball and add buttonDownloadFootball
- // Football tab
- private TabPage tabPageFootball;
+ // Pagine
+ private Panel pageFootball;
+ private Panel pageHorseRacing;
+ private Panel pageSettings;
+ private Panel pageInfo;
+
+ // Football
private DateTimePicker dateTimePicker;
- private Button buttonImportFootball;
- private Button buttonDownloadFootball;
- private DataGridView dataGridViewFootball;
- private ProgressBar progressBarFootball;
+ private ModernButton btnDownload;
+ private ModernButton btnExportCsv;
+ private ModernProgressBar progressBarFootball;
private Label labelStatusFootball;
+ private DataGridView dataGridViewFootball;
- // ApiOptions class definition - rimosso GetLeagueData
- public static class ApiOptions
- {
- public const string GetLeagueFixtures = "Visualizza elenco partite alla data";
- public const string GetLeagueOdds = "Visualizza elenco quote partite";
+ // Horse Racing
+ private ComboBox cmbRacingDay;
+ private ModernButton btnDownloadRacing;
+ private ModernButton btnExportRacingCsv;
+ private ModernProgressBar progressBarRacing;
+ private Label labelStatusRacing;
+ private DataGridView dataGridViewRacing;
- public static readonly string[] AllOptions = { GetLeagueFixtures, GetLeagueOdds };
- }
+ // Impostazioni
+ private Label lblApiKey;
+ private TextBox txtApiKey;
+ private Label lblExportPath;
+ private TextBox txtExportPath;
+ private ModernButton btnBrowseExport;
+ private Label lblRacingUser;
+ private TextBox txtRacingUser;
+ private Label lblRacingPass;
+ private TextBox txtRacingPass;
+ private ModernButton btnSaveSettings;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
- {
components.Dispose();
- }
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
- this.tabControl = new System.Windows.Forms.TabControl();
-
- // Horse Tab
- this.tabPageHorse = new System.Windows.Forms.TabPage();
- this.textBoxFolderPath = new System.Windows.Forms.TextBox();
- this.buttonBrowse = new System.Windows.Forms.Button();
- this.buttonPredict = new System.Windows.Forms.Button(); // New button for predictions
- this.buttonImport = new System.Windows.Forms.Button(); // New button for import
- this.progressBarHorse = new System.Windows.Forms.ProgressBar();
- this.labelStatusHorse = new System.Windows.Forms.Label();
- this.dataGridViewHorse = new System.Windows.Forms.DataGridView();
-
- // In the InitializeComponent method, update:
- // Football Tab
- this.tabPageFootball = new System.Windows.Forms.TabPage();
- this.dateTimePicker = new System.Windows.Forms.DateTimePicker();
- this.buttonImportFootball = new System.Windows.Forms.Button();
- this.buttonDownloadFootball = new System.Windows.Forms.Button();
- this.progressBarFootball = new System.Windows.Forms.ProgressBar();
- this.labelStatusFootball = new System.Windows.Forms.Label();
- this.dataGridViewFootball = new System.Windows.Forms.DataGridView();
-
- this.tabControl.SuspendLayout();
- this.tabPageHorse.SuspendLayout();
- ((System.ComponentModel.ISupportInitialize)(this.dataGridViewHorse)).BeginInit();
- this.tabPageFootball.SuspendLayout();
- ((System.ComponentModel.ISupportInitialize)(this.dataGridViewFootball)).BeginInit();
this.SuspendLayout();
- //
- // tabControl
- //
- this.tabControl.Controls.Add(this.tabPageHorse);
- this.tabControl.Controls.Add(this.tabPageFootball);
- this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill;
- this.tabControl.Location = new System.Drawing.Point(0, 0);
- this.tabControl.Name = "tabControl";
- this.tabControl.SelectedIndex = 0;
- this.tabControl.Size = new System.Drawing.Size(800, 450);
- this.tabControl.TabIndex = 0;
-
- //
- // tabPageHorse
- //
- this.tabPageHorse.Controls.Add(this.dataGridViewHorse);
- this.tabPageHorse.Controls.Add(this.labelStatusHorse);
- this.tabPageHorse.Controls.Add(this.progressBarHorse);
- this.tabPageHorse.Controls.Add(this.buttonPredict); // Add new button
- this.tabPageHorse.Controls.Add(this.buttonImport); // Add new button
- this.tabPageHorse.Controls.Add(this.buttonBrowse);
- this.tabPageHorse.Controls.Add(this.textBoxFolderPath);
- this.tabPageHorse.Location = new System.Drawing.Point(4, 22);
- this.tabPageHorse.Name = "tabPageHorse";
- this.tabPageHorse.Padding = new System.Windows.Forms.Padding(3);
- this.tabPageHorse.Size = new System.Drawing.Size(792, 424);
- this.tabPageHorse.TabIndex = 0;
- this.tabPageHorse.Text = "Horse";
- this.tabPageHorse.UseVisualStyleBackColor = true;
-
- //
- // textBoxFolderPath
- //
- this.textBoxFolderPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.textBoxFolderPath.Location = new System.Drawing.Point(8, 8);
- this.textBoxFolderPath.Name = "textBoxFolderPath";
- this.textBoxFolderPath.ReadOnly = true;
- this.textBoxFolderPath.Size = new System.Drawing.Size(510, 20);
- this.textBoxFolderPath.TabIndex = 0;
-
- //
- // buttonBrowse
- //
- this.buttonBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
- this.buttonBrowse.Location = new System.Drawing.Point(524, 6); // Moved position to make room for Predict button
- this.buttonBrowse.Size = new System.Drawing.Size(80, 23);
- this.buttonBrowse.Name = "buttonBrowse";
- this.buttonBrowse.TabIndex = 1;
- this.buttonBrowse.Text = "Sfoglia...";
- this.buttonBrowse.UseVisualStyleBackColor = true;
- this.buttonBrowse.Click += new System.EventHandler(this.buttonBrowse_Click);
-
- //
- // buttonImport
- //
- this.buttonImport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
- this.buttonImport.Location = new System.Drawing.Point(610, 6);
- this.buttonImport.Name = "buttonImport";
- this.buttonImport.Size = new System.Drawing.Size(80, 23);
- this.buttonImport.TabIndex = 6;
- this.buttonImport.Text = "Importa";
- this.buttonImport.UseVisualStyleBackColor = true;
- this.buttonImport.Click += new System.EventHandler(this.buttonImport_Click);
-
- //
- // buttonPredict
- //
- this.buttonPredict.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
- this.buttonPredict.Location = new System.Drawing.Point(696, 6);
- this.buttonPredict.Name = "buttonPredict";
- this.buttonPredict.Size = new System.Drawing.Size(80, 23);
- this.buttonPredict.TabIndex = 5;
- this.buttonPredict.Text = "Predici";
- this.buttonPredict.UseVisualStyleBackColor = true;
- this.buttonPredict.Click += new System.EventHandler(this.buttonPredict_Click);
-
- //
- // progressBarHorse
- //
- this.progressBarHorse.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.progressBarHorse.Location = new System.Drawing.Point(8, 34);
- this.progressBarHorse.Name = "progressBarHorse";
- this.progressBarHorse.Size = new System.Drawing.Size(762, 20);
- this.progressBarHorse.TabIndex = 2;
-
- //
- // labelStatusHorse
- //
- this.labelStatusHorse.AutoSize = true;
- this.labelStatusHorse.Location = new System.Drawing.Point(8, 57);
- this.labelStatusHorse.Name = "labelStatusHorse";
- this.labelStatusHorse.Size = new System.Drawing.Size(38, 13);
- this.labelStatusHorse.TabIndex = 3;
- this.labelStatusHorse.Text = "Pronto";
-
- //
- // dataGridViewHorse
- //
- this.dataGridViewHorse.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
- | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.dataGridViewHorse.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
- this.dataGridViewHorse.Location = new System.Drawing.Point(8, 73);
- this.dataGridViewHorse.Name = "dataGridViewHorse";
- this.dataGridViewHorse.ReadOnly = true;
- this.dataGridViewHorse.Size = new System.Drawing.Size(762, 343);
- this.dataGridViewHorse.TabIndex = 4;
-
- // Update controls add to the tab page:
- this.tabPageFootball.Controls.Add(this.dataGridViewFootball);
- this.tabPageFootball.Controls.Add(this.labelStatusFootball);
- this.tabPageFootball.Controls.Add(this.progressBarFootball);
- this.tabPageFootball.Controls.Add(this.buttonImportFootball);
- this.tabPageFootball.Controls.Add(this.buttonDownloadFootball);
- this.tabPageFootball.Controls.Add(this.dateTimePicker);
- this.tabPageFootball.Location = new System.Drawing.Point(4, 22);
- this.tabPageFootball.Name = "tabPageFootball";
- this.tabPageFootball.Padding = new System.Windows.Forms.Padding(3);
- this.tabPageFootball.Size = new System.Drawing.Size(792, 424);
- this.tabPageFootball.TabIndex = 1;
- this.tabPageFootball.Text = "Football";
- this.tabPageFootball.UseVisualStyleBackColor = true;
-
- //
- // dateTimePicker
- //
- this.dateTimePicker.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.dateTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Short;
- this.dateTimePicker.Location = new System.Drawing.Point(8, 8);
- this.dateTimePicker.Name = "dateTimePicker";
- // Update dateTimePicker size to make room for two buttons
- this.dateTimePicker.Size = new System.Drawing.Size(596, 20);
- this.dateTimePicker.TabIndex = 1;
- this.dateTimePicker.Value = System.DateTime.Today;
-
- // buttonDownloadFootball properties
- this.buttonDownloadFootball.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
- this.buttonDownloadFootball.Location = new System.Drawing.Point(610, 6);
- this.buttonDownloadFootball.Name = "buttonDownloadFootball";
- this.buttonDownloadFootball.Size = new System.Drawing.Size(75, 23);
- this.buttonDownloadFootball.TabIndex = 6;
- this.buttonDownloadFootball.Text = "Scarica";
- this.buttonDownloadFootball.UseVisualStyleBackColor = true;
- this.buttonDownloadFootball.Click += new System.EventHandler(this.buttonDownloadFootball_Click);
-
- // buttonImportFootball properties (replace buttonLoadFootball)
- this.buttonImportFootball.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
- this.buttonImportFootball.Location = new System.Drawing.Point(695, 6);
- this.buttonImportFootball.Name = "buttonImportFootball";
- this.buttonImportFootball.Size = new System.Drawing.Size(75, 23);
- this.buttonImportFootball.TabIndex = 2;
- this.buttonImportFootball.Text = "Importa";
- this.buttonImportFootball.UseVisualStyleBackColor = true;
- this.buttonImportFootball.Click += new System.EventHandler(this.buttonImportFootball_Click);
-
- //
- // progressBarFootball
- //
- this.progressBarFootball.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.progressBarFootball.Location = new System.Drawing.Point(8, 35);
- this.progressBarFootball.Name = "progressBarFootball";
- this.progressBarFootball.Size = new System.Drawing.Size(762, 20);
- this.progressBarFootball.TabIndex = 3;
-
- //
- // labelStatusFootball
- //
- this.labelStatusFootball.AutoSize = true;
- this.labelStatusFootball.Location = new System.Drawing.Point(8, 58);
- this.labelStatusFootball.Name = "labelStatusFootball";
- this.labelStatusFootball.Size = new System.Drawing.Size(38, 13);
- this.labelStatusFootball.TabIndex = 4;
- this.labelStatusFootball.Text = "Pronto";
-
- //
- // dataGridViewFootball
- //
- this.dataGridViewFootball.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
- | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.dataGridViewFootball.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
- this.dataGridViewFootball.Location = new System.Drawing.Point(8, 74);
- this.dataGridViewFootball.Name = "dataGridViewFootball";
- this.dataGridViewFootball.ReadOnly = true;
- this.dataGridViewFootball.Size = new System.Drawing.Size(762, 342);
- this.dataGridViewFootball.TabIndex = 5;
-
- //
- // Main
- //
- this.ClientSize = new System.Drawing.Size(800, 450);
- this.Controls.Add(this.tabControl);
- this.Name = "Main";
+ // ============================================================
+ // FORM
+ // ============================================================
+ this.ClientSize = new Size(1050, 620);
+ this.MinimumSize = new Size(860, 520);
this.Text = "Betting Predictor";
- this.Load += new System.EventHandler(this.Main_Load);
- this.tabControl.ResumeLayout(false);
- this.tabPageHorse.ResumeLayout(false);
- this.tabPageHorse.PerformLayout();
- ((System.ComponentModel.ISupportInitialize)(this.dataGridViewHorse)).EndInit();
- this.tabPageFootball.ResumeLayout(false);
- this.tabPageFootball.PerformLayout();
- ((System.ComponentModel.ISupportInitialize)(this.dataGridViewFootball)).EndInit();
- this.ResumeLayout(false);
- }
- }
+ this.Name = "Main";
+ this.StartPosition = FormStartPosition.CenterScreen;
+ this.BackColor = ModernTheme.ContentBackground;
+ this.ForeColor = ModernTheme.TextPrimary;
+ this.Font = ModernTheme.BodyFont;
+ this.DoubleBuffered = true;
+ this.Load += new EventHandler(this.Main_Load);
+ this.Resize += new EventHandler(this.Main_Resize);
- public partial class ProgressDialog : Form
- {
- public ProgressDialog()
- {
- InitializeComponent();
- }
-
- public void UpdateProgress(int currentPage, int totalPages)
- {
- progressBar.Maximum = totalPages;
- progressBar.Value = currentPage;
- labelProgress.Text = $"Scaricamento pagina {currentPage} di {totalPages}";
- }
-
- protected override void Dispose(bool disposing)
- {
- if (disposing && (components != null))
+ // ============================================================
+ // SIDEBAR (Dock=Left, larghezza fissa)
+ // ============================================================
+ this.panelSidebar = new Panel
{
- components.Dispose();
- }
- base.Dispose(disposing);
- }
+ Dock = DockStyle.Left,
+ Width = ModernTheme.SidebarWidth,
+ BackColor = ModernTheme.SidebarBackground
+ };
- private void InitializeComponent()
- {
- this.progressBar = new System.Windows.Forms.ProgressBar();
- this.labelProgress = new System.Windows.Forms.Label();
- this.SuspendLayout();
- //
- // progressBar
- //
- this.progressBar.Location = new System.Drawing.Point(12, 12);
- this.progressBar.Name = "progressBar";
- this.progressBar.Size = new System.Drawing.Size(360, 23);
- this.progressBar.TabIndex = 0;
- //
- // labelProgress
- //
- this.labelProgress.AutoSize = true;
- this.labelProgress.Location = new System.Drawing.Point(12, 38);
- this.labelProgress.Name = "labelProgress";
- this.labelProgress.Size = new System.Drawing.Size(0, 13);
- this.labelProgress.TabIndex = 1;
- //
- // ProgressDialog
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(384, 61);
- this.Controls.Add(this.labelProgress);
- this.Controls.Add(this.progressBar);
- this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.Name = "ProgressDialog";
- this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
- this.Text = "Scaricamento in corso...";
+ this.labelAppTitle = new Label
+ {
+ Text = "Betting\nPredictor",
+ Font = new Font("Segoe UI", 15F, FontStyle.Bold),
+ ForeColor = ModernTheme.PrimaryColor,
+ BackColor = ModernTheme.SidebarBackground,
+ Dock = DockStyle.Top,
+ Height = 72,
+ TextAlign = ContentAlignment.MiddleCenter
+ };
+
+ var sep = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 1,
+ BackColor = ModernTheme.CardBorder
+ };
+
+ // Nav — ordine di aggiunta inverso rispetto a quello visivo (Dock=Top)
+ this.navInfo = new NavButton("Info", "i");
+ this.navInfo.Click += new EventHandler(this.navInfo_Click);
+
+ this.navSettings = new NavButton("Impostazioni", "#");
+ this.navSettings.Click += new EventHandler(this.navSettings_Click);
+
+ this.navHorseRacing = new NavButton("Corse Cavalli", "H");
+ this.navHorseRacing.Click += new EventHandler(this.navHorseRacing_Click);
+
+ this.navFootball = new NavButton("Calcio", "F");
+ this.navFootball.IsActive = true;
+ this.navFootball.Click += new EventHandler(this.navFootball_Click);
+
+ this.panelSidebar.Controls.Add(this.navInfo);
+ this.panelSidebar.Controls.Add(this.navSettings);
+ this.panelSidebar.Controls.Add(this.navHorseRacing);
+ this.panelSidebar.Controls.Add(this.navFootball);
+ this.panelSidebar.Controls.Add(sep);
+ this.panelSidebar.Controls.Add(this.labelAppTitle);
+
+ // ============================================================
+ // HEADER (Dock=Top dentro panelContent)
+ // ============================================================
+ this.panelHeader = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 56,
+ BackColor = ModernTheme.HeaderBackground,
+ Padding = new Padding(24, 0, 24, 0)
+ };
+
+ this.labelPageTitle = new Label
+ {
+ Text = "Calcio",
+ Font = ModernTheme.TitleFont,
+ ForeColor = ModernTheme.TextPrimary,
+ BackColor = ModernTheme.HeaderBackground,
+ Dock = DockStyle.Fill,
+ TextAlign = ContentAlignment.MiddleLeft
+ };
+ this.panelHeader.Controls.Add(this.labelPageTitle);
+
+ var headerLine = new Panel
+ {
+ Dock = DockStyle.Bottom,
+ Height = 1,
+ BackColor = ModernTheme.CardBorder
+ };
+ this.panelHeader.Controls.Add(headerLine);
+
+ // ============================================================
+ // CONTENT WRAPPER (Dock=Fill, contiene header + pagine)
+ // ============================================================
+ this.panelContent = new Panel
+ {
+ Dock = DockStyle.Fill,
+ BackColor = ModernTheme.ContentBackground
+ };
+
+ // ============================================================
+ // PAGE: CALCIO
+ // ============================================================
+ this.pageFootball = new Panel
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(24, 16, 24, 16),
+ BackColor = ModernTheme.ContentBackground,
+ Visible = true
+ };
+
+ // -- toolbar (altezza fissa, posizionamento manuale) --
+ var toolbarFb = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 48,
+ BackColor = ModernTheme.ContentBackground
+ };
+
+ this.dateTimePicker = new DateTimePicker
+ {
+ Format = DateTimePickerFormat.Short,
+ Value = DateTime.Today,
+ Font = ModernTheme.BodyFont,
+ CalendarMonthBackground = ModernTheme.InputBackground,
+ CalendarForeColor = ModernTheme.InputText
+ };
+
+ this.btnDownload = new ModernButton { Text = "Scarica Partite", Size = new Size(140, 34) };
+ this.btnDownload.Click += new EventHandler(this.btnDownload_Click);
+
+ this.btnExportCsv = new ModernButton
+ {
+ Text = "Esporta CSV",
+ AccentColor = ModernTheme.SuccessColor,
+ Size = new Size(120, 34),
+ Enabled = false
+ };
+ this.btnExportCsv.Click += new EventHandler(this.btnExportCsv_Click);
+
+ toolbarFb.Controls.Add(this.dateTimePicker);
+ toolbarFb.Controls.Add(this.btnDownload);
+ toolbarFb.Controls.Add(this.btnExportCsv);
+
+ // -- status --
+ var statusFb = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 30,
+ BackColor = ModernTheme.ContentBackground
+ };
+
+ this.progressBarFootball = new ModernProgressBar { Dock = DockStyle.Top };
+
+ this.labelStatusFootball = new Label
+ {
+ Text = "Pronto",
+ Font = ModernTheme.SmallFont,
+ ForeColor = ModernTheme.TextSecondary,
+ BackColor = ModernTheme.ContentBackground,
+ Dock = DockStyle.Fill,
+ TextAlign = ContentAlignment.MiddleLeft
+ };
+ statusFb.Controls.Add(this.labelStatusFootball);
+ statusFb.Controls.Add(this.progressBarFootball);
+
+ // -- grid --
+ this.dataGridViewFootball = new DataGridView { Dock = DockStyle.Fill };
+ ((System.ComponentModel.ISupportInitialize)this.dataGridViewFootball).BeginInit();
+ ModernTheme.StyleDataGridView(this.dataGridViewFootball);
+
+ this.pageFootball.Controls.Add(this.dataGridViewFootball);
+ this.pageFootball.Controls.Add(statusFb);
+ this.pageFootball.Controls.Add(toolbarFb);
+
+ // ============================================================
+ // PAGE: CORSE CAVALLI
+ // ============================================================
+ this.pageHorseRacing = new Panel
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(24, 16, 24, 16),
+ BackColor = ModernTheme.ContentBackground,
+ Visible = false
+ };
+
+ // -- toolbar racing --
+ var toolbarRacing = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 48,
+ BackColor = ModernTheme.ContentBackground,
+ Name = "toolbarRacing"
+ };
+
+ this.cmbRacingDay = new ComboBox
+ {
+ DropDownStyle = ComboBoxStyle.DropDownList,
+ Font = ModernTheme.BodyFont,
+ BackColor = ModernTheme.InputBackground,
+ ForeColor = ModernTheme.InputText,
+ FlatStyle = FlatStyle.Flat,
+ Size = new Size(160, 28)
+ };
+ this.cmbRacingDay.Items.AddRange(new object[] { "Oggi", "Domani" });
+ this.cmbRacingDay.SelectedIndex = 0;
+
+ this.btnDownloadRacing = new ModernButton { Text = "Scarica Corse", Size = new Size(140, 34) };
+ this.btnDownloadRacing.Click += new EventHandler(this.btnDownloadRacing_Click);
+
+ this.btnExportRacingCsv = new ModernButton
+ {
+ Text = "Esporta CSV",
+ AccentColor = ModernTheme.SuccessColor,
+ Size = new Size(120, 34),
+ Enabled = false
+ };
+ this.btnExportRacingCsv.Click += new EventHandler(this.btnExportRacingCsv_Click);
+
+ toolbarRacing.Controls.Add(this.cmbRacingDay);
+ toolbarRacing.Controls.Add(this.btnDownloadRacing);
+ toolbarRacing.Controls.Add(this.btnExportRacingCsv);
+
+ // -- status racing --
+ var statusRacing = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 30,
+ BackColor = ModernTheme.ContentBackground
+ };
+
+ this.progressBarRacing = new ModernProgressBar { Dock = DockStyle.Top };
+
+ this.labelStatusRacing = new Label
+ {
+ Text = "Pronto",
+ Font = ModernTheme.SmallFont,
+ ForeColor = ModernTheme.TextSecondary,
+ BackColor = ModernTheme.ContentBackground,
+ Dock = DockStyle.Fill,
+ TextAlign = ContentAlignment.MiddleLeft
+ };
+ statusRacing.Controls.Add(this.labelStatusRacing);
+ statusRacing.Controls.Add(this.progressBarRacing);
+
+ // -- grid racing --
+ this.dataGridViewRacing = new DataGridView { Dock = DockStyle.Fill };
+ ((System.ComponentModel.ISupportInitialize)this.dataGridViewRacing).BeginInit();
+ ModernTheme.StyleDataGridView(this.dataGridViewRacing);
+
+ this.pageHorseRacing.Controls.Add(this.dataGridViewRacing);
+ this.pageHorseRacing.Controls.Add(statusRacing);
+ this.pageHorseRacing.Controls.Add(toolbarRacing);
+
+ // ============================================================
+ // PAGE: IMPOSTAZIONI
+ // ============================================================
+ this.pageSettings = new Panel
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(24, 16, 24, 16),
+ BackColor = ModernTheme.ContentBackground,
+ Visible = false
+ };
+
+ var settingsInner = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 400,
+ BackColor = ModernTheme.CardBackground,
+ Padding = new Padding(24)
+ };
+
+ this.lblApiKey = new Label
+ {
+ Text = "API Key (api-football)",
+ Font = ModernTheme.SubtitleFont,
+ ForeColor = ModernTheme.TextPrimary,
+ BackColor = ModernTheme.CardBackground,
+ AutoSize = true,
+ Location = new Point(24, 24)
+ };
+
+ this.txtApiKey = new TextBox
+ {
+ Font = ModernTheme.BodyFont,
+ BackColor = ModernTheme.InputBackground,
+ ForeColor = ModernTheme.InputText,
+ BorderStyle = BorderStyle.FixedSingle,
+ Location = new Point(24, 52),
+ Size = new Size(500, 28)
+ };
+
+ this.lblExportPath = new Label
+ {
+ Text = "Cartella esportazione CSV",
+ Font = ModernTheme.SubtitleFont,
+ ForeColor = ModernTheme.TextPrimary,
+ BackColor = ModernTheme.CardBackground,
+ AutoSize = true,
+ Location = new Point(24, 100)
+ };
+
+ this.txtExportPath = new TextBox
+ {
+ Font = ModernTheme.BodyFont,
+ BackColor = ModernTheme.InputBackground,
+ ForeColor = ModernTheme.InputText,
+ BorderStyle = BorderStyle.FixedSingle,
+ ReadOnly = true,
+ Location = new Point(24, 128),
+ Size = new Size(400, 28)
+ };
+
+ this.btnBrowseExport = new ModernButton
+ {
+ Text = "Sfoglia...",
+ IsPrimary = false,
+ Size = new Size(90, 30),
+ Location = new Point(434, 126)
+ };
+ this.btnBrowseExport.Click += new EventHandler(this.btnBrowseExport_Click);
+
+ // Racing API credentials
+ this.lblRacingUser = new Label
+ {
+ Text = "Racing API — Username",
+ Font = ModernTheme.SubtitleFont,
+ ForeColor = ModernTheme.TextPrimary,
+ BackColor = ModernTheme.CardBackground,
+ AutoSize = true,
+ Location = new Point(24, 176)
+ };
+
+ this.txtRacingUser = new TextBox
+ {
+ Font = ModernTheme.BodyFont,
+ BackColor = ModernTheme.InputBackground,
+ ForeColor = ModernTheme.InputText,
+ BorderStyle = BorderStyle.FixedSingle,
+ Location = new Point(24, 204),
+ Size = new Size(500, 28)
+ };
+
+ this.lblRacingPass = new Label
+ {
+ Text = "Racing API — Password",
+ Font = ModernTheme.SubtitleFont,
+ ForeColor = ModernTheme.TextPrimary,
+ BackColor = ModernTheme.CardBackground,
+ AutoSize = true,
+ Location = new Point(24, 248)
+ };
+
+ this.txtRacingPass = new TextBox
+ {
+ Font = ModernTheme.BodyFont,
+ BackColor = ModernTheme.InputBackground,
+ ForeColor = ModernTheme.InputText,
+ BorderStyle = BorderStyle.FixedSingle,
+ UseSystemPasswordChar = true,
+ Location = new Point(24, 276),
+ Size = new Size(500, 28)
+ };
+
+ this.btnSaveSettings = new ModernButton
+ {
+ Text = "Salva impostazioni",
+ AccentColor = ModernTheme.SuccessColor,
+ Size = new Size(170, 36),
+ Location = new Point(24, 330)
+ };
+ this.btnSaveSettings.Click += new EventHandler(this.btnSaveSettings_Click);
+
+ settingsInner.Controls.Add(this.btnSaveSettings);
+ settingsInner.Controls.Add(this.txtRacingPass);
+ settingsInner.Controls.Add(this.lblRacingPass);
+ settingsInner.Controls.Add(this.txtRacingUser);
+ settingsInner.Controls.Add(this.lblRacingUser);
+ settingsInner.Controls.Add(this.btnBrowseExport);
+ settingsInner.Controls.Add(this.txtExportPath);
+ settingsInner.Controls.Add(this.lblExportPath);
+ settingsInner.Controls.Add(this.txtApiKey);
+ settingsInner.Controls.Add(this.lblApiKey);
+
+ this.pageSettings.Controls.Add(settingsInner);
+
+ // ============================================================
+ // PAGE: INFO
+ // ============================================================
+ this.pageInfo = new Panel
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(24, 16, 24, 16),
+ BackColor = ModernTheme.ContentBackground,
+ Visible = false
+ };
+
+ var infoInner = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 280,
+ BackColor = ModernTheme.CardBackground,
+ Padding = new Padding(32)
+ };
+
+ int infoY = 32;
+
+ var lblInfoTitle = new Label
+ {
+ Text = "Betting Predictor",
+ Font = new Font("Segoe UI", 20F, FontStyle.Bold),
+ ForeColor = ModernTheme.PrimaryColor,
+ BackColor = ModernTheme.CardBackground,
+ AutoSize = true,
+ Location = new Point(32, infoY)
+ };
+ infoY += 48;
+
+ var lblVersion = new Label
+ {
+ Text = "Versione 1.0.0",
+ Font = ModernTheme.SubtitleFont,
+ ForeColor = ModernTheme.TextSecondary,
+ BackColor = ModernTheme.CardBackground,
+ AutoSize = true,
+ Location = new Point(32, infoY)
+ };
+ infoY += 36;
+
+ var lblDesc = new Label
+ {
+ Text = "Applicazione per lo scaricamento e l'analisi di dati sportivi\n" +
+ "tramite API esterne. Supporta l'esportazione in CSV.\n\n" +
+ "Sviluppato con .NET Framework 4.8.1 | WinForms",
+ Font = ModernTheme.BodyFont,
+ ForeColor = ModernTheme.TextPrimary,
+ BackColor = ModernTheme.CardBackground,
+ AutoSize = true,
+ Location = new Point(32, infoY)
+ };
+ infoY += 90;
+
+ var lblCopy = new Label
+ {
+ Text = $"© {DateTime.Now.Year} — Tutti i diritti riservati",
+ Font = ModernTheme.SmallFont,
+ ForeColor = ModernTheme.TextSecondary,
+ BackColor = ModernTheme.CardBackground,
+ AutoSize = true,
+ Location = new Point(32, infoY)
+ };
+
+ infoInner.Controls.Add(lblCopy);
+ infoInner.Controls.Add(lblDesc);
+ infoInner.Controls.Add(lblVersion);
+ infoInner.Controls.Add(lblInfoTitle);
+
+ this.pageInfo.Controls.Add(infoInner);
+
+ // ============================================================
+ // ASSEMBLY
+ // ============================================================
+ // Le pagine vanno aggiunte PRIMA dell'header nel panelContent
+ // perché con Dock=Fill devono occupare lo spazio rimanente.
+ this.panelContent.Controls.Add(this.pageFootball);
+ this.panelContent.Controls.Add(this.pageHorseRacing);
+ this.panelContent.Controls.Add(this.pageSettings);
+ this.panelContent.Controls.Add(this.pageInfo);
+ this.panelContent.Controls.Add(this.panelHeader);
+
+ this.Controls.Add(this.panelContent);
+ this.Controls.Add(this.panelSidebar);
+
+ ((System.ComponentModel.ISupportInitialize)this.dataGridViewFootball).EndInit();
+ ((System.ComponentModel.ISupportInitialize)this.dataGridViewRacing).EndInit();
this.ResumeLayout(false);
- this.PerformLayout();
}
- private System.ComponentModel.IContainer components = null;
- private ProgressBar progressBar;
- private Label labelProgress;
+ ///
+ /// Posiziona manualmente i controlli nelle toolbar
+ ///
+ private void LayoutToolbars()
+ {
+ // Football toolbar
+ if (dateTimePicker != null && dateTimePicker.Parent != null)
+ {
+ var tb = dateTimePicker.Parent;
+ int w = tb.ClientSize.Width;
+ int y = 7;
+
+ dateTimePicker.SetBounds(0, y, 200, 28);
+ btnExportCsv.Location = new Point(w - btnExportCsv.Width, y);
+ btnDownload.Location = new Point(btnExportCsv.Left - btnDownload.Width - 10, y);
+ }
+
+ // Horse Racing toolbar
+ if (cmbRacingDay != null && cmbRacingDay.Parent != null)
+ {
+ var tb = cmbRacingDay.Parent;
+ int w = tb.ClientSize.Width;
+ int y = 7;
+
+ cmbRacingDay.SetBounds(0, y, 160, 28);
+ btnExportRacingCsv.Location = new Point(w - btnExportRacingCsv.Width, y);
+ btnDownloadRacing.Location = new Point(btnExportRacingCsv.Left - btnDownloadRacing.Width - 10, y);
+ }
+ }
}
}
diff --git a/HorseRacingPredictor/HorseRacingPredictor/Main.cs b/HorseRacingPredictor/HorseRacingPredictor/Main.cs
index 19c9114..3868745 100644
--- a/HorseRacingPredictor/HorseRacingPredictor/Main.cs
+++ b/HorseRacingPredictor/HorseRacingPredictor/Main.cs
@@ -1,12 +1,8 @@
-using HorseRacingPredictor.Football;
-using HorseRacingPredictor.Football.Manager;
-using HorseRacingPredictor.Horses;
-using HorseRacingPredictor.Horses.ML;
+using BettingPredictor.UI;
using System;
using System.Data;
using System.IO;
-using System.Linq;
-using System.Threading;
+using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -14,732 +10,385 @@ namespace BettingPredictor
{
public partial class Main : Form
{
- private readonly HorseRacingPredictor.Horses.Calculator calculator;
- private readonly HorseRacingPredictor.Horses.Database horseDbManager;
- private readonly HorseRacingPredictor.Horses.FileReader fileReaderHorses;
- private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
-
- // Nuova classe centralizzata per l'API Football
private readonly HorseRacingPredictor.Football.Main footballManager;
+ private HorseRacingPredictor.HorseRacing.Main racingManager;
+ private DataTable footballData;
+ private DataTable racingData;
- private readonly MachineLearningService mlService = new MachineLearningService();
+ // Credenziali predefinite Racing API
+ private const string DefaultRacingUser = "qi1mHOHPquDY9KNDASAeGipy";
+ private const string DefaultRacingPass = "RXNFU1YX27R9rTnk8Vop8ZfH";
- // Memorizza i dati importati per utilizzarli nella fase di predizione
- private DataTable importedHorsesData;
+ // Pagine e nav gestiti come array per semplificare la navigazione
+ private Panel[] pages;
+ private NavButton[] navButtons;
+ private string[] pageTitles;
public Main()
{
InitializeComponent();
- calculator = new HorseRacingPredictor.Horses.Calculator();
- horseDbManager = new HorseRacingPredictor.Horses.Database();
- fileReaderHorses = new HorseRacingPredictor.Horses.FileReader();
-
- // Inizializza il gestore centralizzato del Football
footballManager = new HorseRacingPredictor.Football.Main();
-
- // Disabilita il pulsante di importazione fino a quando non viene selezionato un percorso
- buttonImport.Enabled = false;
-
- // Il pulsante di predizione deve essere sempre abilitato
- buttonPredict.Enabled = true;
+ racingManager = new HorseRacingPredictor.HorseRacing.Main(DefaultRacingUser, DefaultRacingPass);
+
+ pages = new[] { pageFootball, pageHorseRacing, pageSettings, pageInfo };
+ navButtons = new[] { navFootball, navHorseRacing, navSettings, navInfo };
+ pageTitles = new[] { "Calcio", "Corse Cavalli", "Impostazioni", "Informazioni" };
}
- ///
- /// Importa i dati dei file CSV delle corse dei cavalli
- ///
- private void ImportCsvFilesHorse(string folderPath)
+ #region Navigation
+
+ private void ShowPage(int index)
{
- if (string.IsNullOrEmpty(folderPath))
+ for (int i = 0; i < pages.Length; i++)
{
- MessageBox.Show("Seleziona un percorso valido.", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
-
- try
- {
- // Usa il metodo FileReaderManagerHorses per ottenere i file CSV
- var csvFiles = fileReaderHorses.GetHorseRaceFiles(folderPath);
- if (csvFiles.Count == 0)
- {
- MessageBox.Show("Nessun file CSV trovato nel percorso selezionato.", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
-
- // Creiamo una tabella combinata per tutti i dati
- DataTable combinedTable = null;
-
- // Inizializza la progress bar
- progressBarHorse.Minimum = 0;
- progressBarHorse.Maximum = csvFiles.Count;
- progressBarHorse.Value = 0;
- labelStatusHorse.Text = "Inizializzazione importazione...";
-
- // Utilizziamo Task per non bloccare l'UI
- Task.Run(() =>
- {
- for (int i = 0; i < csvFiles.Count; i++)
- {
- var file = csvFiles[i];
-
- // Aggiorna la UI con lo stato corrente
- this.Invoke(new Action(() =>
- {
- progressBarHorse.Value = i + 1;
- labelStatusHorse.Text = $"Importazione file {i + 1} di {csvFiles.Count}: {Path.GetFileName(file)}";
- }));
-
- try
- {
- // Leggi i dati dal file CSV
- var horsesData = fileReaderHorses.ReadHorseDataFromFile(file);
-
- // Al primo file, creiamo la tabella combinata
- if (combinedTable == null)
- {
- // Creiamo una nuova tabella vuota
- combinedTable = new DataTable();
-
- // Aggiungiamo le colonne necessarie
- combinedTable.Columns.Add("File", typeof(string));
- combinedTable.Columns.Add("Data Corsa", typeof(DateTime));
- combinedTable.Columns.Add("Meeting", typeof(string));
- combinedTable.Columns.Add("Corsa", typeof(int));
- combinedTable.Columns.Add("Numero", typeof(string));
- combinedTable.Columns.Add("Nome Cavallo", typeof(string));
- combinedTable.Columns.Add("Età", typeof(int));
- combinedTable.Columns.Add("Genere", typeof(string));
- combinedTable.Columns.Add("Peso", typeof(decimal));
- combinedTable.Columns.Add("Quota", typeof(decimal));
- combinedTable.Columns.Add("Risultato Reale", typeof(string));
- // La colonna "Posizione Prevista" verrà aggiunta nella fase di predizione
- }
-
- // Inserisci i dati nel database
- horseDbManager.ProcessAndInsertHorseRaceData(file);
-
- // Estrai le informazioni sulla corsa dal nome del file
- var raceInfo = fileReaderHorses.GetRaceInfoFromFileName(file);
-
- // Aggiungi i dati dei cavalli alla tabella combinata (senza predizioni)
- foreach (var horseData in horsesData)
- {
- DataRow newRow = combinedTable.NewRow();
- newRow["File"] = Path.GetFileName(file);
- newRow["Data Corsa"] = raceInfo.RaceDate;
- newRow["Meeting"] = raceInfo.MeetingName;
- newRow["Corsa"] = raceInfo.RaceNumber;
- newRow["Numero"] = horseData.Num;
- newRow["Nome Cavallo"] = horseData.HorseName;
- newRow["Età"] = horseData.Age;
- newRow["Genere"] = horseData.Gender;
- newRow["Peso"] = horseData.WeightCarried;
- newRow["Quota"] = horseData.BestFixedOdds;
- newRow["Risultato Reale"] = horseData.FinishResult;
- combinedTable.Rows.Add(newRow);
- }
- }
- catch (Exception ex)
- {
- this.Invoke(new Action(() =>
- {
- MessageBox.Show($"Errore durante l'elaborazione del file {Path.GetFileName(file)}: {ex.Message}",
- "Errore Elaborazione", MessageBoxButtons.OK, MessageBoxIcon.Warning);
- }));
- // Continua con il prossimo file
- }
- }
-
- // Completamento dell'operazione
- this.Invoke(new Action(() =>
- {
- if (combinedTable != null && combinedTable.Rows.Count > 0)
- {
- // Salva i dati importati nella variabile di classe
- importedHorsesData = combinedTable;
-
- // Mostra i dati nella DataGridView
- dataGridViewHorse.DataSource = importedHorsesData;
-
- // Formatta la griglia
- FormatHorseDataGrid(dataGridViewHorse);
-
- labelStatusHorse.Text = $"Importazione completata: {combinedTable.Rows.Count} cavalli importati";
- }
- else
- {
- labelStatusHorse.Text = "Nessun dato valido trovato nei file CSV";
- }
-
- // Riabilita il pulsante importa al termine dell'operazione
- buttonImport.Enabled = true;
- }));
- });
- }
- catch (Exception ex)
- {
- MessageBox.Show($"Errore durante il caricamento dei file CSV: {ex.Message}",
- "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
- labelStatusHorse.Text = "Errore durante l'importazione";
+ pages[i].Visible = (i == index);
+ navButtons[i].IsActive = (i == index);
}
+ labelPageTitle.Text = pageTitles[index];
}
- private void ShowStrikeRateStats(DataTable table)
- {
- // Raggruppa per gara usando Data Corsa, Meeting, Corsa
- var grouped = table.AsEnumerable()
- .Where(row => row["Risultato Reale"] != DBNull.Value && row["Posizione Prevista"] != DBNull.Value)
- .GroupBy(row => new {
- DataCorsa = row["Data Corsa"],
- Meeting = row["Meeting"].ToString(),
- Corsa = row["Corsa"]
- });
+ private void navFootball_Click(object sender, EventArgs e) => ShowPage(0);
+ private void navHorseRacing_Click(object sender, EventArgs e) => ShowPage(1);
+ private void navSettings_Click(object sender, EventArgs e) => ShowPage(2);
+ private void navInfo_Click(object sender, EventArgs e) => ShowPage(3);
- string stats = "Statistiche Strike Rate per Gara:\n";
- int gareTotali = 0, winTotali = 0, placeTotali = 0;
+ #endregion
- foreach (var group in grouped)
- {
- gareTotali++;
- int tot = 0, winOk = 0, placeOk = 0;
- foreach (var row in group)
- {
- if (int.TryParse(row["Risultato Reale"].ToString(), out int real) &&
- float.TryParse(row["Posizione Prevista"].ToString(), out float pred))
- {
- tot++;
- if (real == 1 && pred <= 1.5f) winOk++;
- if (real <= 3 && pred <= 3.5f) placeOk++;
- }
- }
- winTotali += winOk;
- placeTotali += placeOk;
- stats += $"Gara: {group.Key.DataCorsa:dd/MM/yyyy} - {group.Key.Meeting} - N°{group.Key.Corsa}\n" +
- $" Cavalli: {tot}\n" +
- $" Vincitore previsto corretto: {winOk} ({(winOk * 100.0 / tot):F1}%)\n" +
- $" Piazzati previsti corretti: {placeOk} ({(placeOk * 100.0 / tot):F1}%)\n\n";
- }
- if (gareTotali > 0)
- {
- stats += $"Totale gare: {gareTotali}\n" +
- $"Totale vincitori previsti corretti: {winTotali}\n" +
- $"Totale piazzati previsti corretti: {placeTotali}\n";
- MessageBox.Show(stats, "Strike Rate per Gara", MessageBoxButtons.OK, MessageBoxIcon.Information);
- }
- }
+ #region Football
- ///
- /// Formatta la griglia dei dati dei cavalli per una migliore visualizzazione
- ///
- private void FormatHorseDataGrid(DataGridView grid)
- {
- if (grid.Columns.Count == 0)
- return;
-
- // Nascondi le colonne Età, Genere e Peso
- foreach (var colName in new[] { "Età", "Genere", "Peso" })
- {
- if (grid.Columns.Contains(colName))
- grid.Columns[colName].Visible = false;
- }
-
- // Imposta l'ordine delle colonne principali
- if (grid.Columns.Contains("File"))
- grid.Columns["File"].DisplayIndex = 0;
- if (grid.Columns.Contains("Data Corsa"))
- grid.Columns["Data Corsa"].DisplayIndex = 1;
- if (grid.Columns.Contains("Meeting"))
- grid.Columns["Meeting"].DisplayIndex = 2;
- if (grid.Columns.Contains("Corsa"))
- grid.Columns["Corsa"].DisplayIndex = 3;
- if (grid.Columns.Contains("Numero"))
- grid.Columns["Numero"].DisplayIndex = 4;
- if (grid.Columns.Contains("Nome Cavallo"))
- grid.Columns["Nome Cavallo"].DisplayIndex = 5;
- if (grid.Columns.Contains("Quota"))
- grid.Columns["Quota"].DisplayIndex = 6;
- if (grid.Columns.Contains("Risultato Reale"))
- grid.Columns["Risultato Reale"].DisplayIndex = 7;
- if (grid.Columns.Contains("Posizione Prevista"))
- grid.Columns["Posizione Prevista"].DisplayIndex = 8;
-
- // Imposta il formato delle date
- if (grid.Columns.Contains("Data Corsa"))
- grid.Columns["Data Corsa"].DefaultCellStyle.Format = "dd/MM/yyyy";
-
- // Imposta il formato numerico per le quote
- if (grid.Columns.Contains("Quota"))
- grid.Columns["Quota"].DefaultCellStyle.Format = "N2";
-
- if (grid.Columns.Contains("Posizione Prevista"))
- grid.Columns["Posizione Prevista"].DefaultCellStyle.Format = "N1";
-
- // Imposta l'allineamento al centro per alcune colonne
- foreach (var columnName in new[] { "Numero", "Età", "Corsa", "Risultato Reale", "Posizione Prevista" })
- {
- if (grid.Columns.Contains(columnName))
- grid.Columns[columnName].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
- }
-
- // Colora le righe in base al risultato
- grid.CellFormatting += (sender, e) =>
- {
- if (e.RowIndex >= 0 && e.ColumnIndex >= 0)
- {
- var row = grid.Rows[e.RowIndex];
- if (grid.Columns.Contains("Risultato Reale") && row.Cells["Risultato Reale"].Value != null)
- {
- string resultStr = row.Cells["Risultato Reale"].Value.ToString();
- if (int.TryParse(resultStr, out int result))
- {
- if (result == 1)
- row.DefaultCellStyle.BackColor = System.Drawing.Color.LightGreen;
- else if (result <= 3)
- row.DefaultCellStyle.BackColor = System.Drawing.Color.LightYellow;
- }
- }
- if (grid.Columns.Contains("Risultato Reale") && grid.Columns.Contains("Posizione Prevista"))
- {
- var realResultCell = row.Cells["Risultato Reale"];
- var predictedCell = row.Cells["Posizione Prevista"];
- if (realResultCell.Value != null && predictedCell.Value != null)
- {
- if (int.TryParse(realResultCell.Value.ToString(), out int realPos) &&
- float.TryParse(predictedCell.Value.ToString(), out float predictedPos))
- {
- if (Math.Abs(realPos - predictedPos) <= 0.5)
- {
- predictedCell.Style.BackColor = System.Drawing.Color.LightGreen;
- predictedCell.Style.ForeColor = System.Drawing.Color.DarkGreen;
- predictedCell.Style.Font = new System.Drawing.Font(grid.Font, System.Drawing.FontStyle.Bold);
- }
- }
- }
- }
- }
- };
-
- // Abilita il riordino delle colonne
- grid.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
- grid.AllowUserToOrderColumns = true;
- }
-
- // Add these methods to handle the download and import operations
-
- ///
- /// Nuovo metodo per gestire lo scaricamento dei dati calcistici
- ///
- private async Task DownloadFootballDataAsync(DateTime selectedDate)
+ private async Task DownloadFootballFixturesAsync(DateTime selectedDate)
{
try
{
- // Inizializza e mostra la barra di avanzamento
progressBarFootball.Minimum = 0;
progressBarFootball.Maximum = 100;
progressBarFootball.Value = 0;
- labelStatusFootball.Text = "Scaricamento delle partite in corso...";
+ labelStatusFootball.Text = "Scaricamento elenco partite...";
- // Disabilita i controlli durante l'elaborazione
dateTimePicker.Enabled = false;
- buttonDownloadFootball.Enabled = false;
- buttonImportFootball.Enabled = false;
+ btnDownload.Enabled = false;
+ btnExportCsv.Enabled = false;
- // Crea gli oggetti progress che aggiornano la UI in modo thread-safe
- var progressCallback = new Progress(value =>
+ var progress = new Progress(v => progressBarFootball.Value = v);
+ var status = new Progress(s => labelStatusFootball.Text = s);
+
+ var table = await Task.Run(() =>
+ footballManager.GetTodayFixtures(selectedDate, progress, status));
+
+ footballData = table;
+ dataGridViewFootball.DataSource = footballData;
+
+ if (footballData != null && footballData.Rows.Count > 0)
{
- progressBarFootball.Value = value;
- });
-
- var statusCallback = new Progress(status =>
- {
- labelStatusFootball.Text = status;
- });
-
- // Usa la nuova classe centralizzata del Football per scaricare i dati
- await Task.Run(() =>
- footballManager.DownloadFixturesAndOdds(selectedDate, progressCallback, statusCallback));
-
- // Riabilita i controlli
- dateTimePicker.Enabled = true;
- buttonDownloadFootball.Enabled = true;
- buttonImportFootball.Enabled = true;
- labelStatusFootball.Text = "Scaricamento completato. Pronto per l'importazione.";
- }
- catch (Exception ex)
- {
- // Gestione degli errori
- MessageBox.Show($"Errore durante lo scaricamento dei dati calcistici: {ex.Message}",
- "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
-
- // Riabilita i controlli anche in caso di errore
- dateTimePicker.Enabled = true;
- buttonDownloadFootball.Enabled = true;
- buttonImportFootball.Enabled = true;
- labelStatusFootball.Text = "Errore nello scaricamento dati";
- progressBarFootball.Value = 0;
- }
- }
-
- ///
- /// Nuovo metodo per gestire l'importazione dei dati calcistici
- ///
- private async Task ImportFootballDataAsync(DataGridView dataGridViewFootball)
- {
- try
- {
- // Inizializza e mostra la barra di avanzamento
- progressBarFootball.Minimum = 0;
- progressBarFootball.Maximum = 100;
- progressBarFootball.Value = 0;
- labelStatusFootball.Text = "Importazione dati dalle risposte API in corso...";
-
- // Disabilita i controlli durante l'elaborazione
- dateTimePicker.Enabled = false;
- buttonDownloadFootball.Enabled = false;
- buttonImportFootball.Enabled = false;
-
- // Crea gli oggetti progress che aggiornano la UI in modo thread-safe
- var progressCallback = new Progress(value =>
- {
- progressBarFootball.Value = value;
- });
-
- var statusCallback = new Progress(status =>
- {
- labelStatusFootball.Text = status;
- });
-
- // Usa la nuova classe centralizzata del Football per importare i dati
- var footballDataTable = await Task.Run(() =>
- footballManager.ImportFromApiResponses(progressCallback, statusCallback));
-
- // Aggiorna la UI con i risultati (già thread-safe perché eseguito sul thread UI)
- dataGridViewFootball.DataSource = footballDataTable;
-
- // Formatta la griglia dati
- FormatFootballDataGrid(dataGridViewFootball);
-
- // Riabilita i controlli
- dateTimePicker.Enabled = true;
- buttonDownloadFootball.Enabled = true;
- buttonImportFootball.Enabled = true;
- labelStatusFootball.Text = $"Importati {footballDataTable?.Rows.Count ?? 0} eventi";
- }
- catch (Exception ex)
- {
- // Gestione degli errori
- MessageBox.Show($"Errore durante l'importazione dei dati calcistici: {ex.Message}",
- "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
-
- // Riabilita i controlli anche in caso di errore
- dateTimePicker.Enabled = true;
- buttonDownloadFootball.Enabled = true;
- buttonImportFootball.Enabled = true;
- labelStatusFootball.Text = "Errore nell'importazione dati";
- progressBarFootball.Value = 0;
- }
- }
-
- ///
- /// Metodo aggiornato per il processo completo (scaricamento + importazione)
- ///
- private async Task ProcessFootballDataAsync(DateTime selectedDate, DataGridView dataGridViewFootball)
- {
- try
- {
- // Prima scarica, poi importa
- await DownloadFootballDataAsync(selectedDate);
- await ImportFootballDataAsync(dataGridViewFootball);
- }
- catch (Exception ex)
- {
- // Gestione degli errori
- MessageBox.Show($"Errore durante l'elaborazione dei dati calcistici: {ex.Message}",
- "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
-
- // Reset dello stato UI
- dateTimePicker.Enabled = true;
- buttonDownloadFootball.Enabled = true;
- buttonImportFootball.Enabled = true;
- labelStatusFootball.Text = "Errore nell'elaborazione dati";
- progressBarFootball.Value = 0;
- }
- }
-
- ///
- /// Formatta la griglia dei dati calcistici per una migliore visualizzazione
- ///
- private void FormatFootballDataGrid(DataGridView grid)
- {
- if (grid.Columns.Count == 0)
- return;
-
- // Imposta l'ordine delle colonne principali
- if (grid.Columns.Contains("Data / Ora"))
- grid.Columns["Data / Ora"].DisplayIndex = 0;
- if (grid.Columns.Contains("Paese"))
- grid.Columns["Paese"].DisplayIndex = 1;
- if (grid.Columns.Contains("Campionato"))
- grid.Columns["Campionato"].DisplayIndex = 2;
- if (grid.Columns.Contains("Casa"))
- grid.Columns["Casa"].DisplayIndex = 3;
- if (grid.Columns.Contains("Trasferta"))
- grid.Columns["Trasferta"].DisplayIndex = 4;
- if (grid.Columns.Contains("Previsione"))
- grid.Columns["Previsione"].DisplayIndex = 5;
-
- // Imposta il formato delle date
- if (grid.Columns.Contains("Data / Ora"))
- grid.Columns["Data / Ora"].DefaultCellStyle.Format = "dd/MM/yyyy HH:mm";
-
- // Imposta il formato numerico per le quote
- foreach (var quotaColumn in new[] { "Quota Casa", "Quota Pareggio", "Quota Trasferta" })
- {
- if (grid.Columns.Contains(quotaColumn))
- grid.Columns[quotaColumn].DefaultCellStyle.Format = "N2";
- }
-
- // Imposta l'allineamento al centro per alcune colonne
- foreach (var columnName in new[] { "Goals Casa", "Goals Trasferta", "Risultato", "Stato" })
- {
- if (grid.Columns.Contains(columnName))
- grid.Columns[columnName].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
- }
-
- // Colora le righe in base al risultato della previsione
- grid.CellFormatting += (sender, e) =>
- {
- if (e.RowIndex >= 0 && grid.Columns.Contains("Risultato") && e.ColumnIndex >= 0)
- {
- var resultCell = grid.Rows[e.RowIndex].Cells["Risultato"];
- if (resultCell.Value != null)
- {
- // Se la previsione è corretta, colora di verde
- if (Convert.ToInt32(resultCell.Value) == 1)
- grid.Rows[e.RowIndex].DefaultCellStyle.BackColor = System.Drawing.Color.LightGreen;
- }
+ btnExportCsv.Enabled = true;
+ labelStatusFootball.Text = $"Scaricate {footballData.Rows.Count} partite";
}
- };
-
- // Abilita il riordino delle colonne
- grid.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
- grid.AllowUserToOrderColumns = true;
- }
-
- private string BrowseFolder()
- {
- using (FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog())
+ else
+ {
+ labelStatusFootball.Text = "Nessuna partita trovata per la data selezionata";
+ }
+ }
+ catch (Exception ex)
{
- folderBrowserDialog.Description = "Seleziona la cartella contenente i file CSV delle corse dei cavalli";
- folderBrowserDialog.ShowNewFolderButton = false;
- return folderBrowserDialog.ShowDialog() == DialogResult.OK ? folderBrowserDialog.SelectedPath : null;
+ MessageBox.Show($"Errore durante lo scaricamento:\n{ex.Message}",
+ "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ labelStatusFootball.Text = "Errore nello scaricamento";
+ progressBarFootball.Value = 0;
+ }
+ finally
+ {
+ dateTimePicker.Enabled = true;
+ btnDownload.Enabled = true;
}
}
- #region EventHandlers
-
- // Gestore evento per il caricamento dei dati dei cavalli - solo per importazione
- private void buttonBrowse_Click(object sender, EventArgs e)
+ private void ExportFootballToCsv()
{
- // Seleziona cartella con file CSV
- string folderPath = BrowseFolder();
- if (!string.IsNullOrEmpty(folderPath))
+ if (footballData == null || footballData.Rows.Count == 0)
{
- // Imposta il percorso nel textbox
- textBoxFolderPath.Text = folderPath;
-
- // Abilita il pulsante di importazione
- buttonImport.Enabled = true;
+ MessageBox.Show("Nessun dato da esportare. Scarica prima le partite.",
+ "Nessun dato", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ string folder = txtExportPath.Text;
+ string filePath = null;
+
+ if (!string.IsNullOrEmpty(folder) && Directory.Exists(folder))
+ {
+ filePath = Path.Combine(folder, $"Partite_{dateTimePicker.Value:yyyy-MM-dd}.csv");
+ }
+ else
+ {
+ using (var dlg = new SaveFileDialog())
+ {
+ dlg.Filter = "File CSV|*.csv";
+ dlg.FileName = $"Partite_{dateTimePicker.Value:yyyy-MM-dd}.csv";
+ if (dlg.ShowDialog() != DialogResult.OK) return;
+ filePath = dlg.FileName;
+ }
+ }
+
+ try
+ {
+ var sb = new StringBuilder();
+
+ var headers = new string[footballData.Columns.Count];
+ for (int i = 0; i < footballData.Columns.Count; i++)
+ headers[i] = footballData.Columns[i].ColumnName;
+ sb.AppendLine(string.Join(";", headers));
+
+ foreach (DataRow row in footballData.Rows)
+ {
+ var vals = new string[footballData.Columns.Count];
+ for (int i = 0; i < footballData.Columns.Count; i++)
+ {
+ var v = row[i]?.ToString() ?? "";
+ if (v.Contains(";") || v.Contains("\""))
+ v = "\"" + v.Replace("\"", "\"\"") + "\"";
+ vals[i] = v;
+ }
+ sb.AppendLine(string.Join(";", vals));
+ }
+
+ File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
+ labelStatusFootball.Text = $"CSV esportato: {Path.GetFileName(filePath)}";
+ MessageBox.Show($"Esportate {footballData.Rows.Count} partite in:\n{filePath}",
+ "Esportazione completata", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Errore durante l'esportazione CSV:\n{ex.Message}",
+ "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
- // Replace buttonLoadFootball_Click with these two event handlers:
- private void buttonDownloadFootball_Click(object sender, EventArgs e)
+ #endregion
+
+ #region Horse Racing
+
+ private async Task DownloadRacecardsAsync()
{
- // Usa direttamente la proprietà Value del DateTimePicker
- DateTime selectedDate = dateTimePicker.Value;
- _ = DownloadFootballDataAsync(selectedDate);
+ try
+ {
+ progressBarRacing.Minimum = 0;
+ progressBarRacing.Maximum = 100;
+ progressBarRacing.Value = 0;
+ labelStatusRacing.Text = "Scaricamento racecard...";
+
+ cmbRacingDay.Enabled = false;
+ btnDownloadRacing.Enabled = false;
+ btnExportRacingCsv.Enabled = false;
+
+ var progress = new Progress(v => progressBarRacing.Value = v);
+ var status = new Progress(s => labelStatusRacing.Text = s);
+
+ string day = cmbRacingDay.SelectedIndex == 0 ? "today" : "tomorrow";
+
+ var table = await Task.Run(() =>
+ racingManager.GetRacecards(day, progress, status));
+
+ racingData = table;
+ dataGridViewRacing.DataSource = racingData;
+
+ if (racingData != null && racingData.Rows.Count > 0)
+ {
+ btnExportRacingCsv.Enabled = true;
+ labelStatusRacing.Text = $"Trovate {racingData.Rows.Count} corse";
+ }
+ else
+ {
+ labelStatusRacing.Text = "Nessuna corsa trovata";
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Errore durante lo scaricamento:\n{ex.Message}",
+ "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ labelStatusRacing.Text = "Errore nello scaricamento";
+ progressBarRacing.Value = 0;
+ }
+ finally
+ {
+ cmbRacingDay.Enabled = true;
+ btnDownloadRacing.Enabled = true;
+ }
}
- // Gestore evento per il pulsante di importazione dati football
- private void buttonImportFootball_Click(object sender, EventArgs e)
+ private void ExportRacingToCsv()
{
- // Process and import data from frontier table to final tables
- _ = ImportFootballDataAsync(dataGridViewFootball);
+ if (racingData == null || racingData.Rows.Count == 0)
+ {
+ MessageBox.Show("Nessun dato da esportare. Scarica prima le corse.",
+ "Nessun dato", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ string folder = txtExportPath.Text;
+ string dayLabel = cmbRacingDay.SelectedIndex == 0 ? "oggi" : "domani";
+ string filePath = null;
+
+ if (!string.IsNullOrEmpty(folder) && Directory.Exists(folder))
+ {
+ filePath = Path.Combine(folder, $"Corse_{dayLabel}_{DateTime.Now:yyyy-MM-dd}.csv");
+ }
+ else
+ {
+ using (var dlg = new SaveFileDialog())
+ {
+ dlg.Filter = "File CSV|*.csv";
+ dlg.FileName = $"Corse_{dayLabel}_{DateTime.Now:yyyy-MM-dd}.csv";
+ if (dlg.ShowDialog() != DialogResult.OK) return;
+ filePath = dlg.FileName;
+ }
+ }
+
+ try
+ {
+ var sb = new StringBuilder();
+
+ var headers = new string[racingData.Columns.Count];
+ for (int i = 0; i < racingData.Columns.Count; i++)
+ headers[i] = racingData.Columns[i].ColumnName;
+ sb.AppendLine(string.Join(";", headers));
+
+ foreach (DataRow row in racingData.Rows)
+ {
+ var vals = new string[racingData.Columns.Count];
+ for (int i = 0; i < racingData.Columns.Count; i++)
+ {
+ var v = row[i]?.ToString() ?? "";
+ if (v.Contains(";") || v.Contains("\""))
+ v = "\"" + v.Replace("\"", "\"\"") + "\"";
+ vals[i] = v;
+ }
+ sb.AppendLine(string.Join(";", vals));
+ }
+
+ File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
+ labelStatusRacing.Text = $"CSV esportato: {Path.GetFileName(filePath)}";
+ MessageBox.Show($"Esportate {racingData.Rows.Count} corse in:\n{filePath}",
+ "Esportazione completata", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Errore durante l'esportazione CSV:\n{ex.Message}",
+ "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
}
- // Nel metodo Main_Load, rimuovi il codice che popolava il ComboBox
+ #endregion
+
+ #region Settings
+
+ private string SettingsFilePath =>
+ Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "settings.ini");
+
+ private void LoadSettings()
+ {
+ try
+ {
+ // Imposta valori predefiniti Racing API
+ txtRacingUser.Text = DefaultRacingUser;
+ txtRacingPass.Text = DefaultRacingPass;
+
+ if (!File.Exists(SettingsFilePath)) return;
+ foreach (var line in File.ReadAllLines(SettingsFilePath))
+ {
+ var idx = line.IndexOf('=');
+ if (idx < 0) continue;
+ var key = line.Substring(0, idx).Trim();
+ var val = line.Substring(idx + 1).Trim();
+
+ if (key == "ApiKey") txtApiKey.Text = val;
+ else if (key == "ExportPath") txtExportPath.Text = val;
+ else if (key == "RacingUser") txtRacingUser.Text = val;
+ else if (key == "RacingPass") txtRacingPass.Text = val;
+ }
+
+ // Aggiorna il manager con le credenziali caricate
+ racingManager.UpdateCredentials(txtRacingUser.Text, txtRacingPass.Text);
+ }
+ catch { }
+ }
+
+ private void SaveSettings()
+ {
+ try
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine($"ApiKey={txtApiKey.Text.Trim()}");
+ sb.AppendLine($"ExportPath={txtExportPath.Text.Trim()}");
+ sb.AppendLine($"RacingUser={txtRacingUser.Text.Trim()}");
+ sb.AppendLine($"RacingPass={txtRacingPass.Text.Trim()}");
+ File.WriteAllText(SettingsFilePath, sb.ToString(), Encoding.UTF8);
+
+ // Aggiorna il manager con le nuove credenziali
+ racingManager.UpdateCredentials(txtRacingUser.Text.Trim(), txtRacingPass.Text.Trim());
+
+ MessageBox.Show("Impostazioni salvate con successo.",
+ "Salvato", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Errore nel salvataggio:\n{ex.Message}",
+ "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ #endregion
+
+ #region Event handlers
+
private void Main_Load(object sender, EventArgs e)
{
- // Imposta la data di ieri per default
- dateTimePicker.Value = DateTime.Today.AddDays(-1);
-
- // Inizializza le barre di avanzamento
- progressBarHorse.Value = 0;
+ dateTimePicker.Value = DateTime.Today;
progressBarFootball.Value = 0;
- labelStatusHorse.Text = "Pronto";
labelStatusFootball.Text = "Pronto";
+ progressBarRacing.Value = 0;
+ labelStatusRacing.Text = "Pronto";
+ LoadSettings();
+ LayoutToolbars();
}
- // Gestore dell'evento per il pulsante di generazione predizioni
- private async void buttonPredict_Click(object sender, EventArgs e)
+ private void Main_Resize(object sender, EventArgs e)
{
- // Inizializza e configura il servizio ML
- string connectionString = "Server=DESKTOP-9O9JHFS;Database=TestBS_Horses;User Id=sa;Password=Asti2019;";
- mlService.ConnectionString = connectionString;
-
- // Verifica se esiste il modello ML o se deve essere addestrato
- string modelPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Models", "HorseRaceModel.zip");
- if (!File.Exists(modelPath))
- {
- try
- {
- labelStatusHorse.Text = "Addestramento modello ML in corso...";
- await Task.Run(() => mlService.TrainModel(connectionString));
- labelStatusHorse.Text = "Addestramento modello ML completato";
- }
- catch (Exception ex)
- {
- MessageBox.Show("Errore durante l'addestramento del modello ML:\n" + ex.Message, "Errore ML", MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
- }
-
- // Carica il modello ML
- try
- {
- labelStatusHorse.Text = "Caricamento del modello ML...";
- mlService.LoadModel();
- labelStatusHorse.Text = "Modello ML caricato";
- }
- catch (Exception ex)
- {
- MessageBox.Show("Impossibile caricare il modello ML:\n" + ex.Message, "Errore ML", MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
-
- // Genera le predizioni
- GeneratePredictions();
+ LayoutToolbars();
}
- // Metodo per generare predizioni in base ai dati importati o dal database
- private void GeneratePredictions()
+ private void btnDownload_Click(object sender, EventArgs e)
{
- DataTable workingTable = importedHorsesData;
- if (workingTable == null || workingTable.Rows.Count == 0)
- {
- // Se non ci sono dati importati, carica dal database
- workingTable = horseDbManager.GetAllHorseRaceData();
- }
- if (workingTable == null || workingTable.Rows.Count == 0)
- {
- MessageBox.Show("Nessun dato disponibile per la predizione.", "Nessun dato", MessageBoxButtons.OK, MessageBoxIcon.Warning);
- return;
- }
- try
- {
- // Assicurati che il modello ML sia caricato
- if (mlService == null)
- {
- MessageBox.Show("Errore: servizio ML non inizializzato.", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
+ _ = DownloadFootballFixturesAsync(dateTimePicker.Value);
+ }
- // Controlla se la colonna delle previsioni esiste già
- if (!workingTable.Columns.Contains("Posizione Prevista"))
- workingTable.Columns.Add("Posizione Prevista", typeof(float));
+ private void btnExportCsv_Click(object sender, EventArgs e)
+ {
+ ExportFootballToCsv();
+ }
- // Copia i dati per lavorare su una nuova tabella
- DataTable predictedTable = workingTable.Copy();
+ private void btnDownloadRacing_Click(object sender, EventArgs e)
+ {
+ _ = DownloadRacecardsAsync();
+ }
- // Inizializza la progress bar
- progressBarHorse.Minimum = 0;
- progressBarHorse.Maximum = predictedTable.Rows.Count;
- progressBarHorse.Value = 0;
- labelStatusHorse.Text = "Inizializzazione predizioni...";
- buttonPredict.Enabled = false;
- Task.Run(() =>
- {
- for (int i = 0; i < predictedTable.Rows.Count; i++)
- {
- var row = predictedTable.Rows[i];
- this.Invoke(new Action(() =>
- {
- progressBarHorse.Value = i + 1;
- labelStatusHorse.Text = $"Generazione predizioni {i + 1} di {predictedTable.Rows.Count}";
- }));
- try
- {
- var mlInput = new ModelInput
- {
- Age = Convert.ToInt32(row["Età"] != DBNull.Value ? row["Età"] : 0),
- HandicapRating = 0,
- Weight = row["Peso"] != DBNull.Value ? Convert.ToSingle(row["Peso"]) : 0,
- WeightCarried = row["Peso"] != DBNull.Value ? Convert.ToSingle(row["Peso"]) : 0,
- Barrier = 0,
- CareerRuns = 0,
- CareerWins = 0,
- CareerStrikeRate = 0,
- CareerROI = 0,
- ThisTrackRuns = 0,
- ThisTrackWins = 0,
- ThisTrackStrikeRate = 0,
- ThisDistanceRuns = 0,
- ThisDistanceWins = 0,
- ThisDistanceStrikeRate = 0,
- JockeyLast100Wins = 0,
- JockeyLast100StrikeRate = 0,
- TrainerLast100Wins = 0,
- TrainerLast100StrikeRate = 0,
- BestFixedOdds = row["Quota"] != DBNull.Value ? Convert.ToSingle(row["Quota"]) : 0
- };
- float? predicted = null;
- try
- {
- var prediction = mlService.PredictFinishPosition(mlInput);
- predicted = prediction?.PredictedPosition;
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Errore ML.NET: {ex.Message}\n{ex.StackTrace}");
- }
- row["Posizione Prevista"] = predicted.HasValue ? (object)predicted.Value : DBNull.Value;
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Errore durante la predizione per riga {i}: {ex.Message}");
- }
- }
- this.Invoke(new Action(() =>
- {
- importedHorsesData = predictedTable;
- dataGridViewHorse.DataSource = importedHorsesData;
- FormatHorseDataGrid(dataGridViewHorse);
- ShowStrikeRateStats(predictedTable);
- labelStatusHorse.Text = $"Predizioni completate: {predictedTable.Rows.Count} cavalli analizzati";
- buttonPredict.Enabled = true;
- }));
- });
- }
- catch (Exception ex)
+ private void btnExportRacingCsv_Click(object sender, EventArgs e)
+ {
+ ExportRacingToCsv();
+ }
+
+ private void btnBrowseExport_Click(object sender, EventArgs e)
+ {
+ using (var dlg = new FolderBrowserDialog())
{
- MessageBox.Show($"Errore durante la generazione delle predizioni: {ex.Message}", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error);
- labelStatusHorse.Text = "Errore durante la generazione delle predizioni";
- buttonPredict.Enabled = true;
+ dlg.Description = "Seleziona la cartella di esportazione CSV";
+ if (dlg.ShowDialog() == DialogResult.OK)
+ txtExportPath.Text = dlg.SelectedPath;
}
}
- private void buttonImport_Click(object sender, EventArgs e)
+ private void btnSaveSettings_Click(object sender, EventArgs e)
{
- // Verifica che sia stato selezionato un percorso
- if (string.IsNullOrEmpty(textBoxFolderPath.Text))
- {
- MessageBox.Show("Seleziona prima una cartella contenente i file CSV.",
- "Percorso mancante", MessageBoxButtons.OK, MessageBoxIcon.Warning);
- return;
- }
-
- // Inizia il processo di importazione
- ImportCsvFilesHorse(textBoxFolderPath.Text);
+ SaveSettings();
}
#endregion
diff --git a/HorseRacingPredictor/HorseRacingPredictor/MainWindow.xaml b/HorseRacingPredictor/HorseRacingPredictor/MainWindow.xaml
new file mode 100644
index 0000000..965ea0c
--- /dev/null
+++ b/HorseRacingPredictor/HorseRacingPredictor/MainWindow.xaml
@@ -0,0 +1,475 @@
+
+
+
+
+ #1E1E2E
+ #181825
+ #11111B
+ #313244
+ #45475A
+ #585B70
+ #6C7086
+ #CDD6F4
+ #A6ADC8
+ #BAC2DE
+ #89B4FA
+ #A6E3A1
+ #F38BA8
+ #FAB387
+ #B4BEFE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Applicazione per lo scaricamento e l'analisi di dati sportivi
+ tramite API esterne. Supporta calcio e corse dei cavalli
+ con esportazione in CSV.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/HorseRacingPredictor/HorseRacingPredictor/MainWindow.xaml.cs b/HorseRacingPredictor/HorseRacingPredictor/MainWindow.xaml.cs
new file mode 100644
index 0000000..1039488
--- /dev/null
+++ b/HorseRacingPredictor/HorseRacingPredictor/MainWindow.xaml.cs
@@ -0,0 +1,326 @@
+using System;
+using System.Data;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace HorseRacingPredictor
+{
+ public partial class MainWindow : Window
+ {
+ private readonly Football.Main _footballManager;
+ private HorseRacing.Main _racingManager;
+ private DataTable _footballData;
+ private DataTable _racingData;
+
+ private const string DefaultRacingUser = "qi1mHOHPquDY9KNDASAeGipy";
+ private const string DefaultRacingPass = "RXNFU1YX27R9rTnk8Vop8ZfH";
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ _footballManager = new Football.Main();
+ _racingManager = new HorseRacing.Main(DefaultRacingUser, DefaultRacingPass);
+ }
+
+ // ???????????????????? LIFECYCLE ????????????????????
+
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ dpFootball.SelectedDate = DateTime.Today;
+ cmbDay.Items.Add("Oggi");
+ cmbDay.Items.Add("Domani");
+ cmbDay.SelectedIndex = 0;
+ LoadSettings();
+ }
+
+ // ???????????????????? NAVIGATION ????????????????????
+
+ private void ShowPage(string name)
+ {
+ // Guard against UI elements not being initialized (possible when called early)
+ if (pageFootball != null) pageFootball.Visibility = name == "football" ? Visibility.Visible : Visibility.Collapsed;
+ if (pageRacing != null) pageRacing.Visibility = name == "racing" ? Visibility.Visible : Visibility.Collapsed;
+ if (pageSettings != null) pageSettings.Visibility = name == "settings" ? Visibility.Visible : Visibility.Collapsed;
+ if (pageInfo != null) pageInfo.Visibility = name == "info" ? Visibility.Visible : Visibility.Collapsed;
+
+ // Update title if available
+ if (lblTitle != null)
+ {
+ switch (name)
+ {
+ case "football": lblTitle.Text = "Calcio"; break;
+ case "racing": lblTitle.Text = "Corse Cavalli"; break;
+ case "settings": lblTitle.Text = "Impostazioni"; break;
+ case "info": lblTitle.Text = "Informazioni"; break;
+ }
+ }
+ }
+
+ private void navFootball_Checked(object sender, RoutedEventArgs e) => ShowPage("football");
+ private void navRacing_Checked(object sender, RoutedEventArgs e) => ShowPage("racing");
+ private void navSettings_Checked(object sender, RoutedEventArgs e) => ShowPage("settings");
+ private void navInfo_Checked(object sender, RoutedEventArgs e) => ShowPage("info");
+
+ // ???????????????????? FOOTBALL ????????????????????
+
+ private async void btnDownloadFb_Click(object sender, RoutedEventArgs e)
+ {
+ var date = dpFootball.SelectedDate ?? DateTime.Today;
+ await DownloadFootballAsync(date);
+ }
+
+ private async Task DownloadFootballAsync(DateTime date)
+ {
+ try
+ {
+ pbFootball.Value = 0;
+ lblStatusFb.Text = "Scaricamento elenco partite";
+ btnDownloadFb.IsEnabled = false;
+ dpFootball.IsEnabled = false;
+ btnExportFbCsv.IsEnabled = false;
+
+ var progress = new Progress(v => pbFootball.Value = v);
+ var status = new Progress(s => lblStatusFb.Text = s);
+
+ var table = await Task.Run(() =>
+ _footballManager.GetTodayFixtures(date, progress, status));
+
+ _footballData = table;
+ dgFootball.ItemsSource = _footballData?.DefaultView;
+
+ if (_footballData != null && _footballData.Rows.Count > 0)
+ {
+ btnExportFbCsv.IsEnabled = true;
+ lblStatusFb.Text = $"Scaricate {_footballData.Rows.Count} partite";
+ }
+ else
+ {
+ lblStatusFb.Text = "Nessuna partita trovata per la data selezionata";
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Errore durante lo scaricamento:\n{ex.Message}",
+ "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
+ lblStatusFb.Text = "Errore nello scaricamento";
+ pbFootball.Value = 0;
+ }
+ finally
+ {
+ btnDownloadFb.IsEnabled = true;
+ dpFootball.IsEnabled = true;
+ }
+ }
+
+ private void btnExportFbCsv_Click(object sender, RoutedEventArgs e)
+ {
+ ExportToCsv(_footballData, txtFbExportPath.Text,
+ $"Partite_{dpFootball.SelectedDate:yyyy-MM-dd}.csv",
+ s => lblStatusFb.Text = s);
+ }
+
+ // ???????????????????? HORSE RACING ????????????????????
+
+ private async void btnDownloadRc_Click(object sender, RoutedEventArgs e)
+ {
+ await DownloadRacecardsAsync();
+ }
+
+ private async Task DownloadRacecardsAsync()
+ {
+ try
+ {
+ pbRacing.Value = 0;
+ lblStatusRc.Text = "Scaricamento racecard";
+ btnDownloadRc.IsEnabled = false;
+ cmbDay.IsEnabled = false;
+ btnExportRcCsv.IsEnabled = false;
+
+ var progress = new Progress(v => pbRacing.Value = v);
+ var status = new Progress(s => lblStatusRc.Text = s);
+
+ string day = cmbDay.SelectedIndex == 0 ? "today" : "tomorrow";
+
+ var table = await Task.Run(() =>
+ _racingManager.GetRacecards(day, progress, status));
+
+ _racingData = table;
+ dgRacing.ItemsSource = _racingData?.DefaultView;
+
+ if (_racingData != null && _racingData.Rows.Count > 0)
+ {
+ btnExportRcCsv.IsEnabled = true;
+ lblStatusRc.Text = $"Trovate {_racingData.Rows.Count} corse";
+ }
+ else
+ {
+ lblStatusRc.Text = "Nessuna corsa trovata";
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Errore durante lo scaricamento:\n{ex.Message}",
+ "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
+ lblStatusRc.Text = "Errore nello scaricamento";
+ pbRacing.Value = 0;
+ }
+ finally
+ {
+ btnDownloadRc.IsEnabled = true;
+ cmbDay.IsEnabled = true;
+ }
+ }
+
+ private void btnExportRcCsv_Click(object sender, RoutedEventArgs e)
+ {
+ string dayLabel = cmbDay.SelectedIndex == 0 ? "oggi" : "domani";
+ ExportToCsv(_racingData, txtRcExportPath.Text,
+ $"Corse_{dayLabel}_{DateTime.Now:yyyy-MM-dd}.csv",
+ s => lblStatusRc.Text = s);
+ }
+
+ // ???????????????????? SHARED CSV EXPORT ????????????????????
+
+ private void ExportToCsv(DataTable data, string folder, string defaultName, Action setStatus)
+ {
+ if (data == null || data.Rows.Count == 0)
+ {
+ MessageBox.Show("Nessun dato da esportare.",
+ "Nessun dato", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+
+ string filePath;
+ if (!string.IsNullOrEmpty(folder) && Directory.Exists(folder))
+ {
+ filePath = Path.Combine(folder, defaultName);
+ }
+ else
+ {
+ var dlg = new Microsoft.Win32.SaveFileDialog
+ {
+ Filter = "File CSV|*.csv",
+ FileName = defaultName
+ };
+ if (dlg.ShowDialog() != true) return;
+ filePath = dlg.FileName;
+ }
+
+ try
+ {
+ var sb = new StringBuilder();
+ var headers = new string[data.Columns.Count];
+ for (int i = 0; i < data.Columns.Count; i++)
+ headers[i] = data.Columns[i].ColumnName;
+ sb.AppendLine(string.Join(";", headers));
+
+ foreach (DataRow row in data.Rows)
+ {
+ var vals = new string[data.Columns.Count];
+ for (int i = 0; i < data.Columns.Count; i++)
+ {
+ var v = row[i]?.ToString() ?? "";
+ if (v.Contains(";") || v.Contains("\""))
+ v = "\"" + v.Replace("\"", "\"\"") + "\"";
+ vals[i] = v;
+ }
+ sb.AppendLine(string.Join(";", vals));
+ }
+
+ File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
+ setStatus?.Invoke($"CSV esportato: {Path.GetFileName(filePath)}");
+ MessageBox.Show($"Esportate {data.Rows.Count} righe in:\n{filePath}",
+ "Esportazione completata", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Errore durante l'esportazione CSV:\n{ex.Message}",
+ "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ // ???????????????????? FOLDER BROWSE ????????????????????
+
+ private void btnBrowseFbExport_Click(object sender, RoutedEventArgs e)
+ {
+ var path = BrowseFolder("Seleziona la cartella di esportazione per Calcio");
+ if (path != null) txtFbExportPath.Text = path;
+ }
+
+ private void btnBrowseRcExport_Click(object sender, RoutedEventArgs e)
+ {
+ var path = BrowseFolder("Seleziona la cartella di esportazione per Corse Cavalli");
+ if (path != null) txtRcExportPath.Text = path;
+ }
+
+ private static string BrowseFolder(string description)
+ {
+ using (var dlg = new System.Windows.Forms.FolderBrowserDialog())
+ {
+ dlg.Description = description;
+ if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
+ return dlg.SelectedPath;
+ }
+ return null;
+ }
+
+ // ???????????????????? SETTINGS ????????????????????
+
+ private string SettingsFilePath =>
+ Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "settings.ini");
+
+ private void LoadSettings()
+ {
+ try
+ {
+ txtRacingUser.Text = DefaultRacingUser;
+ txtRacingPass.Password = DefaultRacingPass;
+
+ if (!File.Exists(SettingsFilePath)) return;
+ foreach (var line in File.ReadAllLines(SettingsFilePath))
+ {
+ var idx = line.IndexOf('=');
+ if (idx < 0) continue;
+ var key = line.Substring(0, idx).Trim();
+ var val = line.Substring(idx + 1).Trim();
+
+ if (key == "ApiKey") txtApiKey.Text = val;
+ else if (key == "FbExportPath") txtFbExportPath.Text = val;
+ else if (key == "RcExportPath") txtRcExportPath.Text = val;
+ else if (key == "RacingUser") txtRacingUser.Text = val;
+ else if (key == "RacingPass") txtRacingPass.Password = val;
+ }
+
+ _racingManager.UpdateCredentials(txtRacingUser.Text, txtRacingPass.Password);
+ }
+ catch { }
+ }
+
+ private void btnSaveSettings_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine($"ApiKey={txtApiKey.Text.Trim()}");
+ sb.AppendLine($"FbExportPath={txtFbExportPath.Text.Trim()}");
+ sb.AppendLine($"RcExportPath={txtRcExportPath.Text.Trim()}");
+ sb.AppendLine($"RacingUser={txtRacingUser.Text.Trim()}");
+ sb.AppendLine($"RacingPass={txtRacingPass.Password.Trim()}");
+ File.WriteAllText(SettingsFilePath, sb.ToString(), Encoding.UTF8);
+
+ _racingManager.UpdateCredentials(txtRacingUser.Text.Trim(), txtRacingPass.Password.Trim());
+
+ MessageBox.Show("Impostazioni salvate con successo.",
+ "Salvato", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Errore nel salvataggio:\n{ex.Message}",
+ "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+ }
+}
diff --git a/HorseRacingPredictor/HorseRacingPredictor/UI/CardPanel.cs b/HorseRacingPredictor/HorseRacingPredictor/UI/CardPanel.cs
new file mode 100644
index 0000000..e805c73
--- /dev/null
+++ b/HorseRacingPredictor/HorseRacingPredictor/UI/CardPanel.cs
@@ -0,0 +1,40 @@
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+
+namespace BettingPredictor.UI
+{
+ ///
+ /// Pannello con angoli arrotondati e ombra simulata per stile card moderno
+ ///
+ internal class CardPanel : Panel
+ {
+ public int Radius { get; set; } = ModernTheme.CardRadius;
+
+ public CardPanel()
+ {
+ SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw, true);
+ BackColor = Color.Transparent;
+ Padding = new Padding(ModernTheme.CardPadding);
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ var g = e.Graphics;
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+
+ // Pulisci sfondo
+ using (var bgBrush = new SolidBrush(Parent?.BackColor ?? ModernTheme.ContentBackground))
+ g.FillRectangle(bgBrush, ClientRectangle);
+
+ var cardRect = new Rectangle(0, 0, Width - 2, Height - 2);
+ using (var path = ModernTheme.GetRoundedRectPath(cardRect, Radius))
+ {
+ using (var brush = new SolidBrush(ModernTheme.CardBackground))
+ g.FillPath(brush, path);
+ using (var pen = new Pen(ModernTheme.CardBorder, 1))
+ g.DrawPath(pen, path);
+ }
+ }
+ }
+}
diff --git a/HorseRacingPredictor/HorseRacingPredictor/UI/ModernButton.cs b/HorseRacingPredictor/HorseRacingPredictor/UI/ModernButton.cs
new file mode 100644
index 0000000..6008cb6
--- /dev/null
+++ b/HorseRacingPredictor/HorseRacingPredictor/UI/ModernButton.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+
+namespace BettingPredictor.UI
+{
+ ///
+ /// Pulsante con stile moderno (arrotondato, con effetti hover)
+ ///
+ internal class ModernButton : Button
+ {
+ private bool _isHovered;
+ private bool _isPressed;
+ private Color _accentColor;
+
+ public Color AccentColor
+ {
+ get => _accentColor;
+ set { _accentColor = value; Invalidate(); }
+ }
+
+ public bool IsPrimary { get; set; } = true;
+
+ public ModernButton()
+ {
+ _accentColor = ModernTheme.PrimaryColor;
+ FlatStyle = FlatStyle.Flat;
+ FlatAppearance.BorderSize = 0;
+ Font = new Font("Segoe UI Semibold", 10F);
+ ForeColor = Color.White;
+ Cursor = Cursors.Hand;
+ Height = 36;
+ MinimumSize = new Size(100, 36);
+ SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.Opaque, true);
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ var g = e.Graphics;
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+ g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
+
+ // Pulisci sfondo
+ using (var bgBrush = new SolidBrush(Parent?.BackColor ?? ModernTheme.ContentBackground))
+ g.FillRectangle(bgBrush, ClientRectangle);
+
+ var rect = new Rectangle(0, 0, Width - 1, Height - 1);
+
+ Color bgColor;
+ if (!Enabled)
+ bgColor = Color.FromArgb(100, _accentColor);
+ else if (_isPressed)
+ bgColor = ControlPaint.Dark(_accentColor, 0.15f);
+ else if (_isHovered)
+ bgColor = ControlPaint.Light(_accentColor, 0.15f);
+ else
+ bgColor = _accentColor;
+
+ using (var path = ModernTheme.GetRoundedRectPath(rect, 6))
+ {
+ if (IsPrimary)
+ {
+ using (var brush = new SolidBrush(bgColor))
+ g.FillPath(brush, path);
+ }
+ else
+ {
+ using (var brush = new SolidBrush(ModernTheme.CardBackground))
+ g.FillPath(brush, path);
+ using (var pen = new Pen(bgColor, 2))
+ g.DrawPath(pen, path);
+ }
+
+ var textColor = IsPrimary ? Color.FromArgb(24, 24, 37) : ModernTheme.TextPrimary;
+ using (var textBrush = new SolidBrush(Enabled ? textColor : Color.FromArgb(80, textColor)))
+ {
+ var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
+ g.DrawString(Text, Font, textBrush, rect, sf);
+ }
+ }
+ }
+
+ protected override void OnMouseEnter(EventArgs e) { _isHovered = true; Invalidate(); base.OnMouseEnter(e); }
+ protected override void OnMouseLeave(EventArgs e) { _isHovered = false; _isPressed = false; Invalidate(); base.OnMouseLeave(e); }
+ protected override void OnMouseDown(MouseEventArgs e) { _isPressed = true; Invalidate(); base.OnMouseDown(e); }
+ protected override void OnMouseUp(MouseEventArgs e) { _isPressed = false; Invalidate(); base.OnMouseUp(e); }
+ }
+}
diff --git a/HorseRacingPredictor/HorseRacingPredictor/UI/ModernProgressBar.cs b/HorseRacingPredictor/HorseRacingPredictor/UI/ModernProgressBar.cs
new file mode 100644
index 0000000..f785389
--- /dev/null
+++ b/HorseRacingPredictor/HorseRacingPredictor/UI/ModernProgressBar.cs
@@ -0,0 +1,53 @@
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+
+namespace BettingPredictor.UI
+{
+ ///
+ /// ProgressBar moderna con stile piatto e bordi arrotondati
+ ///
+ internal class ModernProgressBar : ProgressBar
+ {
+ public Color BarColor { get; set; } = ModernTheme.PrimaryColor;
+ public Color TrackColor { get; set; } = Color.FromArgb(49, 50, 68);
+
+ public ModernProgressBar()
+ {
+ SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer | ControlStyles.Opaque, true);
+ Height = 8;
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ var g = e.Graphics;
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+
+ // Pulisci sfondo
+ using (var bgBrush = new SolidBrush(Parent?.BackColor ?? ModernTheme.ContentBackground))
+ g.FillRectangle(bgBrush, ClientRectangle);
+
+ var rect = new Rectangle(0, 0, Width - 1, Height - 1);
+ using (var path = ModernTheme.GetRoundedRectPath(rect, Height / 2))
+ using (var brush = new SolidBrush(TrackColor))
+ {
+ g.FillPath(brush, path);
+ }
+
+ if (Maximum > Minimum && Value > Minimum)
+ {
+ float fraction = (float)(Value - Minimum) / (Maximum - Minimum);
+ int fillWidth = (int)(rect.Width * fraction);
+ if (fillWidth > 4)
+ {
+ var fillRect = new Rectangle(0, 0, fillWidth, Height - 1);
+ using (var path = ModernTheme.GetRoundedRectPath(fillRect, Height / 2))
+ using (var brush = new LinearGradientBrush(fillRect, BarColor, ControlPaint.Light(BarColor, 0.3f), LinearGradientMode.Horizontal))
+ {
+ g.FillPath(brush, path);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/HorseRacingPredictor/HorseRacingPredictor/UI/ModernTheme.cs b/HorseRacingPredictor/HorseRacingPredictor/UI/ModernTheme.cs
new file mode 100644
index 0000000..eec5c94
--- /dev/null
+++ b/HorseRacingPredictor/HorseRacingPredictor/UI/ModernTheme.cs
@@ -0,0 +1,115 @@
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+
+namespace BettingPredictor.UI
+{
+ ///
+ /// Tema grafico moderno scuro per l'applicazione
+ ///
+ internal static class ModernTheme
+ {
+ // Palette colori DARK THEME
+ public static readonly Color SidebarBackground = Color.FromArgb(24, 24, 37);
+ public static readonly Color SidebarHover = Color.FromArgb(40, 40, 58);
+ public static readonly Color SidebarActive = Color.FromArgb(137, 180, 250);
+ public static readonly Color SidebarText = Color.FromArgb(186, 194, 222);
+ public static readonly Color SidebarActiveText = Color.FromArgb(24, 24, 37);
+
+ public static readonly Color ContentBackground = Color.FromArgb(30, 30, 46);
+ public static readonly Color CardBackground = Color.FromArgb(40, 42, 58);
+ public static readonly Color CardBorder = Color.FromArgb(55, 57, 78);
+ public static readonly Color HeaderBackground = Color.FromArgb(24, 24, 37);
+
+ public static readonly Color PrimaryColor = Color.FromArgb(137, 180, 250);
+ public static readonly Color PrimaryDark = Color.FromArgb(116, 155, 220);
+ public static readonly Color SuccessColor = Color.FromArgb(166, 218, 149);
+ public static readonly Color WarningColor = Color.FromArgb(249, 226, 175);
+ public static readonly Color DangerColor = Color.FromArgb(243, 139, 168);
+ public static readonly Color TextPrimary = Color.FromArgb(205, 214, 244);
+ public static readonly Color TextSecondary = Color.FromArgb(147, 153, 178);
+
+ public static readonly Color InputBackground = Color.FromArgb(49, 50, 68);
+ public static readonly Color InputBorder = Color.FromArgb(69, 71, 90);
+ public static readonly Color InputText = Color.FromArgb(205, 214, 244);
+
+ // Font
+ public static readonly Font TitleFont = new Font("Segoe UI Semibold", 16F);
+ public static readonly Font SubtitleFont = new Font("Segoe UI Semibold", 12F);
+ public static readonly Font BodyFont = new Font("Segoe UI", 10F);
+ public static readonly Font SmallFont = new Font("Segoe UI", 9F);
+ public static readonly Font NavFont = new Font("Segoe UI Semibold", 11F);
+
+ // Dimensioni
+ public const int SidebarWidth = 200;
+ public const int CardRadius = 8;
+ public const int CardPadding = 16;
+
+ ///
+ /// Applica lo stile moderno scuro a un DataGridView
+ ///
+ public static void StyleDataGridView(DataGridView grid)
+ {
+ grid.BorderStyle = BorderStyle.None;
+ grid.BackgroundColor = CardBackground;
+ grid.GridColor = CardBorder;
+ grid.CellBorderStyle = DataGridViewCellBorderStyle.SingleHorizontal;
+
+ grid.EnableHeadersVisualStyles = false;
+ grid.ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.None;
+ grid.ColumnHeadersDefaultCellStyle = new DataGridViewCellStyle
+ {
+ BackColor = Color.FromArgb(35, 36, 52),
+ ForeColor = PrimaryColor,
+ Font = new Font("Segoe UI Semibold", 10F),
+ Padding = new Padding(8, 4, 8, 4),
+ Alignment = DataGridViewContentAlignment.MiddleLeft
+ };
+ grid.ColumnHeadersHeight = 40;
+
+ grid.DefaultCellStyle = new DataGridViewCellStyle
+ {
+ BackColor = CardBackground,
+ ForeColor = TextPrimary,
+ Font = BodyFont,
+ SelectionBackColor = Color.FromArgb(60, 63, 85),
+ SelectionForeColor = PrimaryColor,
+ Padding = new Padding(6, 3, 6, 3)
+ };
+
+ grid.AlternatingRowsDefaultCellStyle = new DataGridViewCellStyle
+ {
+ BackColor = Color.FromArgb(45, 47, 64),
+ ForeColor = TextPrimary,
+ Font = BodyFont,
+ SelectionBackColor = Color.FromArgb(60, 63, 85),
+ SelectionForeColor = PrimaryColor,
+ Padding = new Padding(6, 3, 6, 3)
+ };
+
+ grid.RowHeadersVisible = false;
+ grid.RowTemplate.Height = 36;
+ grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
+ grid.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
+ grid.ReadOnly = true;
+ grid.AllowUserToAddRows = false;
+ grid.AllowUserToDeleteRows = false;
+ grid.AllowUserToResizeRows = false;
+ }
+
+ ///
+ /// Disegna un rettangolo arrotondato
+ ///
+ public static GraphicsPath GetRoundedRectPath(Rectangle rect, int radius)
+ {
+ var path = new GraphicsPath();
+ int d = radius * 2;
+ path.AddArc(rect.X, rect.Y, d, d, 180, 90);
+ path.AddArc(rect.Right - d, rect.Y, d, d, 270, 90);
+ path.AddArc(rect.Right - d, rect.Bottom - d, d, d, 0, 90);
+ path.AddArc(rect.X, rect.Bottom - d, d, d, 90, 90);
+ path.CloseFigure();
+ return path;
+ }
+ }
+}
diff --git a/HorseRacingPredictor/HorseRacingPredictor/UI/NavButton.cs b/HorseRacingPredictor/HorseRacingPredictor/UI/NavButton.cs
new file mode 100644
index 0000000..3b641e8
--- /dev/null
+++ b/HorseRacingPredictor/HorseRacingPredictor/UI/NavButton.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+
+namespace BettingPredictor.UI
+{
+ ///
+ /// Pulsante di navigazione per la sidebar
+ ///
+ internal class NavButton : Button
+ {
+ private bool _isActive;
+ private bool _isHovered;
+ private readonly string _icon;
+
+ public bool IsActive
+ {
+ get => _isActive;
+ set { _isActive = value; Invalidate(); }
+ }
+
+ public NavButton(string text, string icon)
+ {
+ _icon = icon;
+ Text = text;
+ FlatStyle = FlatStyle.Flat;
+ FlatAppearance.BorderSize = 0;
+ FlatAppearance.MouseOverBackColor = Color.Transparent;
+ FlatAppearance.MouseDownBackColor = Color.Transparent;
+ Font = ModernTheme.NavFont;
+ ForeColor = ModernTheme.SidebarText;
+ BackColor = ModernTheme.SidebarBackground;
+ Cursor = Cursors.Hand;
+ Height = 44;
+ Dock = DockStyle.Top;
+ Padding = new Padding(0);
+ Margin = new Padding(0);
+ SetStyle(
+ ControlStyles.AllPaintingInWmPaint |
+ ControlStyles.UserPaint |
+ ControlStyles.DoubleBuffer |
+ ControlStyles.Opaque, true);
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ var g = e.Graphics;
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+ g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
+
+ var rect = ClientRectangle;
+
+ // Sfondo pieno sidebar
+ using (var bg = new SolidBrush(ModernTheme.SidebarBackground))
+ g.FillRectangle(bg, rect);
+
+ // Stato attivo: rettangolo arrotondato + indicatore
+ if (_isActive)
+ {
+ var activeRect = new Rectangle(8, 4, rect.Width - 16, rect.Height - 8);
+ using (var path = ModernTheme.GetRoundedRectPath(activeRect, 6))
+ using (var brush = new SolidBrush(ModernTheme.SidebarActive))
+ g.FillPath(brush, path);
+
+ using (var bar = new SolidBrush(ModernTheme.SidebarActive))
+ g.FillRectangle(bar, 0, 10, 3, rect.Height - 20);
+ }
+ // Stato hover (solo se non attivo)
+ else if (_isHovered)
+ {
+ var hoverRect = new Rectangle(8, 4, rect.Width - 16, rect.Height - 8);
+ using (var path = ModernTheme.GetRoundedRectPath(hoverRect, 6))
+ using (var brush = new SolidBrush(ModernTheme.SidebarHover))
+ g.FillPath(brush, path);
+ }
+
+ // Colori testo/icona
+ var fg = _isActive ? ModernTheme.SidebarActiveText : ModernTheme.SidebarText;
+
+ // Icona
+ using (var iconFont = new Font("Segoe UI", 11F, FontStyle.Bold))
+ using (var brush = new SolidBrush(fg))
+ {
+ var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
+ g.DrawString(_icon, iconFont, brush, new RectangleF(14, 0, 28, rect.Height), sf);
+ }
+
+ // Testo
+ using (var brush = new SolidBrush(fg))
+ {
+ var sf = new StringFormat
+ {
+ Alignment = StringAlignment.Near,
+ LineAlignment = StringAlignment.Center,
+ FormatFlags = StringFormatFlags.NoWrap
+ };
+ g.DrawString(Text, Font, brush, new RectangleF(46, 0, rect.Width - 54, rect.Height), sf);
+ }
+ }
+
+ protected override void OnMouseEnter(EventArgs e)
+ {
+ _isHovered = true;
+ Invalidate();
+ base.OnMouseEnter(e);
+ }
+
+ protected override void OnMouseLeave(EventArgs e)
+ {
+ _isHovered = false;
+ Invalidate();
+ base.OnMouseLeave(e);
+ }
+ }
+}