Wednesday, September 11, 2024

Handicapping Progression: From Ad-Hoc To Semi-Automation

WCMI Handicapping Process Progression

From Ad-Hoc To Semi-Automation

Recently, I realised I have progressively automated my daily sports handicapping process that, in broad strokes, looks as follows:

  • Download Sports Results: Download the latest results.
  • Download Sports Fixtures: Download the upcoming fixtures.
  • Generate Ratings: Apply expert handicapping knowledge to calculate ratings for each fixture.
  • Make Selections: Use ratings to identify the value bets and finalise selections.
  • Trade Selections: Trade selections at the best available prices.
  • Review And Improve Process: With the benefit of hindsight, check the validity of the process not just the bare results.

Over the years. I went through a reasonably obvious progression of preparedness:

  • Ad-Hoc (Memory),
  • Checklists (Manual), and
  • Semi-Automation (Programmed).

0. Ad-Hoc (Memory)

This initial approach was a necessary evil as it taught me the lessons of:

  • Clean inputs,
  • Fewer workflow errors and
  • Greater predictability and robustness of outputs.

1. Checklists (Manual)

The second stage increased the probability of fewer workflow errors. However, it was very time-consuming and awkward to complete. In particular, taking advantage of modularity or pipelines in different but similar lists was impossible. Also, there was no log of each day's completed steps, inputs and outputs.


2. Semi-Automation (Programmed)

This stage took three to six months to build and fine-tune to an acceptable level of accuracy. It includes a makefile, Python scripts, input and output CSV files and a log file.

Download And Install Make

Firstly, using a simple web search gave me the following instructions:

# Install Chocolatey (if not already installed)
Set-ExecutionPolicy Bypass -Scope CurrentUser; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
# Verify Chocolatey installation
choco --version
# Install GNU make
choco install make
# Validate installation
make --version

Create Makefile

Secondly, I created the following simple makefile template with directories, operations and targets:

# Directories -------------------------------------------
DATA_IMPORT = C:\\data\\import
DATA_PROCESS = C:\\data\\process
DATA_EXPORT = C:\\data\\export
MAKE_SCRIPTS = C:\\scripts\\make
# Operations --------------------------------------------
define RUN_PYTHON_SCRIPT
	conda activate base && python $(1) $(2) $(3) $(4) $(5)
endef
define APPEND_FILE
	type $(1) >> $(2)
endef
define COPY_FILE
	copy $(1) $(2)
endef
define DELETE_FILE
	del $(subst \\,\,$(1))
endef
# Targets -----------------------------------------------
.PHONY: all
all: export_rating_files
.PHONY: export_rating_files
output_header:
	$(call RUN_PYTHON_SCRIPT,$(MAKE_SCRIPTS)\\Text_Output.py "Daily Handicapping Workflow (2024)" BLACK CYAN)
.PHONY: delete_rating_files
delete_rating_files: output_header
	$(call RUN_PYTHON_SCRIPT,$(MAKE_SCRIPTS)\\Text_Output.py "1. Deleting rating files..." BLACK CYAN)
	$(call DELETE_FILE,$(DATA_EXPORT)\\Ratings_2024.csv)
# ...
.PHONY: export_rating_files
export_rating_files: delete_rating_files
	$(call RUN_PYTHON_SCRIPT,$(MAKE_SCRIPTS)\\Text_Output.py "Exporting rating files..." BLACK CYAN)
        $(call COPY_FILE,$(DATA_PROCESS)\\Ratings_2024.csv,$(DATA_EXPORT)\\Ratings_2024.csv))
# ...
# Example usage:
# > make -file Today_Handicap.mak
# EOF ---------------------------------------------------

Create Python Scripts

Thirdly, I created multiple simple Python scripts that do one thing well. For example the following script outputs each makefile step to the screen so that with a quick glance I can monitor progress.

# Imports -----------------------------------------------
import argparse
from colorama import init, Fore, Back, Style
# Functions ---------------------------------------------
def color_print(text, fg_color, bg_color):
    # Initialize colorama to wrap the output in the necessary codes
    init(autoreset=True)
    # Mapping of color names to colorama foreground and background colors
    colors = {
        'BLACK': (Fore.BLACK, Back.BLACK),
        'RED': (Fore.RED, Back.RED),
        'GREEN': (Fore.GREEN, Back.GREEN),
        'YELLOW': (Fore.YELLOW, Back.YELLOW),
        'BLUE': (Fore.BLUE, Back.BLUE),
        'MAGENTA': (Fore.MAGENTA, Back.MAGENTA),
        'CYAN': (Fore.CYAN, Back.CYAN),
        'WHITE': (Fore.WHITE, Back.WHITE)
    }
    # Get the specified colors from the dictionary, default to white if not found
    fg_code, bg_code = colors.get(fg_color.upper(), (Fore.WHITE, '')), colors.get(bg_color.upper(), (Back.WHITE, ''))
    # Print the text with the specified foreground and background colors
    print(f"{fg_code[0]}{bg_code[1]}{text}")
# Main --------------------------------------------------
def main():
    parser = argparse.ArgumentParser(description="Prints colored text based on user inputs.")
    parser.add_argument("text", help="Text to display")
    parser.add_argument("foreground_color", help="Foreground color of the text")
    parser.add_argument("background_color", help="Background color of the text")
    args = parser.parse_args()
    print('')
    color_print(args.text, args.foreground_color, args.background_color)
    print('')
    print('')
# Entry -------------------------------------------------
if __name__ == "__main__":
    main()
# Example usage:
# > python Text_Output.py "This is the output..." BLACK GREEN
# EOF ---------------------------------------------------

Key Takeaway

You will approach the scenario differently, and the key takeaway is not the specific steps and scripts but the progression toward semi-automation.

As ever, the scripts have little or no "3rr0r!" handling and are only a starting point for your own explorations.

Enjoy!