To borrow a phrase from one of Central Perk's charming patrons, Joey Tribbiani: "How you doin'?" Or in our present context, how is your sports trading performing?
Sports traders use various tools and metrics to measure their strategies and manage risks. Here, we will derive some non-controversial measures:
- Stake Average,
- Stake Volatility,
- Profit Average,
- Profit Volatility, and
- Profit Skewness.
And some approximate, non-standard ones:
- Win Odds (Average),
- Win Probability (Average), and
- Edge (Average).
The following trading results were randomly generated and then curated to show the best and worst results. They are not suggestions or recommendations for staking or risk profiles.
How-You-Doin.csv
Market | MarketStakes | MarketPnLs |
---|---|---|
Alpha | 130.00 | 530.00 |
Bravo | 130.00 | 450.00 |
Charlie | 100.00 | 330.00 |
Delta | 105.00 | 325.00 |
Echo | 145.00 | 425.00 |
Foxtrot | 135.00 | 390.00 |
Golf | 160.00 | 320.00 |
Hotel | 185.00 | 310.00 |
India | 445.00 | 510.00 |
Juliet | 295.00 | 305.00 |
... | ||
Quebec | 500.00 | -500.00 |
Romeo | 335.00 | -335.00 |
Sierra | 255.00 | -255.00 |
Tango | 245.00 | -245.00 |
Uniform | 255.00 | -255.00 |
Victor | 280.00 | -280.00 |
Whiskey | 540.00 | -540.00 |
X-Ray | 360.00 | -360.00 |
Yankee | 300.00 | -300.00 |
Zulu | 430.00 | -430.00 |
How-You-Doin.py
# -*- coding: latin-1 -*-
import numpy as np
import pandas as pd
from datetime import date
from functools import reduce
from fuzzy import Soundex
from scipy.stats import skew
from tabulate import tabulate
__date__ = str(date.today())
__query__ = ''
__title__ = 'How You Doin? (2024)'
__author__ = 'matekus'
__version__ = 'v1.02'
__copyright__ = '(c) 2024, ' + __author__ + ', All Rights Reserved.'
__address__ = '[ https://vendire-ludorum.blogspot.com/ ]'
def read_results(state) -> dict:
# Read data from CSV file into data frame.
new_state = state.copy()
df = pd.read_csv(
new_state["input_results_path"],
sep=new_state["input_results_separator"],
names=new_state["input_results_columns"],
encoding=new_state["input_results_encoding"],
)
df = df.iloc[1:]
new_state['Trading_Results'] = df
return new_state
def calc_market_odds(row):
if float(row['MarketStakes']) > 0:
return (float(row['MarketPnLs']) / float(row['MarketStakes'])) + 1.00
else:
return 0
def calc_implied_odds(state) -> dict:
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
data['WinOdds'] = data.apply(calc_market_odds, axis=1)
new_state['Trading_Results'] = data
return new_state
def calc_win_odds_average(state) -> dict:
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
winning_bets = data[data['MarketPnLs'].astype(float) > 0]
average_odds_winning = winning_bets['WinOdds'].mean() if not winning_bets.empty else 0
new_state['AverageOddsWinning'] = average_odds_winning
return new_state
def calc_performance_by_odds_range(state) -> dict:
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
# Define odds ranges
low_odds_range = (1.01, 2.00) # Example range for 'low' odds
high_odds_range = (2.01, 1000) # Example range for 'high' odds
# Calculate PnL for each range
low_odds_range_pnl = data[(data['WinOdds'] > low_odds_range[0]) & (data['WinOdds'] <= low_odds_range[1])]['MarketPnLs'].astype(float).sum()
high_odds_range_pnl = data[(data['WinOdds'] > high_odds_range[0]) & (data['WinOdds'] <= high_odds_range[1])]['MarketPnLs'].astype(float).sum()
new_state['LowOdds_PnL'] = low_odds_range_pnl
new_state['HighOdds_PnL'] = high_odds_range_pnl
return new_state
def calc_win_probs_average(state) -> dict:
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
positive_pnls = data[data['MarketPnLs'].astype(float) > 0]
win_probs_average = len(positive_pnls) / (len(positive_pnls) + len(data) - len(positive_pnls))
new_state['WinProbsAverage'] = win_probs_average
return new_state
def calc_edge_average(state) -> dict:
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
odds_win = new_state['AverageOddsWinning']
prob_win = new_state['WinProbsAverage']
edge_win = (odds_win * prob_win) - 1.00
new_state['WinEdgeAverage'] = edge_win
return new_state
def calc_stakes_average(state) -> dict:
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
mean_pnls = data['MarketStakes'].astype(float).mean()
new_state['StakesAverage'] = mean_pnls
return new_state
def calc_stakes_volatility(state) -> dict:
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
sd_pnls = data['MarketStakes'].astype(float).std()
new_state['StakesSD'] = sd_pnls
return new_state
def calc_pnl_average(state) -> dict:
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
mean_pnls = data['MarketPnLs'].astype(float).mean()
new_state['PnLsAverage'] = mean_pnls
return new_state
def calc_pnl_volatility(state) -> dict:
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
sd_pnls = data['MarketPnLs'].astype(float).std()
new_state['PnLsSD'] = sd_pnls
return new_state
def calc_pnl_skewness(state) -> dict:
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
skew_pnls = skew(data['MarketPnLs'].astype(float))
new_state['PnLsSkew'] = skew_pnls
return new_state
def calc_market_roi(state) -> dict:
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
total_stakes = data['MarketStakes'].astype(float).sum()
total_pnls = data['MarketPnLs'].astype(float).sum()
market_roi = (total_pnls / total_stakes) if total_stakes > 0 else 0
new_state['MarketRoI'] = market_roi
return new_state
def print_data(state) -> dict:
# Print data to console
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
print(tabulate(data.head(11),
headers=new_state['output_results_columns'],
tablefmt='fancy_grid',
floatfmt='.2f'))
print('')
# Prepare the data for tabulate with additional metrics
table_data = [
[
'Average (Stake)',
'SD (Stake)',
'Average (PnL)',
'SD (Pnl)',
'Skew (PnL)',
'Market RoI',
'Avg Odds (Win)',
'Avg Probs (Win)',
'Avg Edge (Win)',
],
[
new_state['StakesAverage'],
new_state['StakesSD'],
new_state['PnLsAverage'],
new_state['PnLsSD'],
new_state['PnLsSkew'],
new_state['MarketRoI'],
new_state['AverageOddsWinning'],
new_state['WinProbsAverage'],
new_state['WinEdgeAverage'],
]
]
tabulate_format = ['.2f', '.2f', '.2f', '.2f', '.2f', '.4f', '.2f', '.2f', '.5f']
print(tabulate(table_data, headers='firstrow', tablefmt='fancy_grid', floatfmt=tabulate_format))
print('')
return new_state
def write_data(state) -> dict:
# Save data frame to CSV.
new_state = state.copy()
data = new_state['Trading_Results'].copy(deep=True)
data.to_csv(new_state['output_results_path'],
sep=new_state['output_results_separator'],
index=False)
return new_state
def main() -> None:
print('')
print(__title__)
print(__copyright__)
print(__address__)
print('')
print('[', __date__, ']')
print('')
# Define pipeline with updated steps
pipeline = [
read_results,
calc_market_roi,
calc_implied_odds,
calc_performance_by_odds_range,
calc_win_odds_average,
calc_win_probs_average,
calc_edge_average,
calc_stakes_average,
calc_stakes_volatility,
calc_pnl_average,
calc_pnl_volatility,
calc_pnl_skewness,
write_data,
print_data,
]
# Define initial state.
initial_state = {
'input_results_path' : r'C:\data\How-You-Doin.csv',
'input_results_separator' : ';',
'input_results_encoding' : 'latin-1',
'input_results_columns' : ['Market', 'MarketStakes', 'MarketPnLs'],
'output_results_path' : r'C:\data\How-You-Doin_Updated.csv',
'output_results_separator' : ';',
'output_results_columns' : ['Market', 'MarketStakes', 'MarketPnLs', 'MarketOdds', 'MarketIPs'],
}
# Run pipeline.
final_state = reduce(lambda v, f: f(v), pipeline, initial_state)
print('')
print(f"Output saved to {final_state['output_results_path']}.")
print('')
print('Fini!')
print('')
if __name__ == '__main__':
main()
# EOF.
How You Doin? (2024)
(c) 2024, matekus, All Rights Reserved.
[ https://vendire-ludorum.blogspot.com/ ]
[ 2024-03-31 ]
╒══════════╤════════════════╤══════════════╤══════════════╤═════════════╕
│ Market │ MarketStakes │ MarketPnLs │ MarketOdds │ MarketIPs │
╞══════════╪════════════════╪══════════════╪══════════════╪═════════════╡
│ 1 │ Alpha │ 130.00 │ 530.00 │ 5.08 │
├──────────┼────────────────┼──────────────┼──────────────┼─────────────┤
│ 2 │ Bravo │ 130.00 │ 450.00 │ 4.46 │
├──────────┼────────────────┼──────────────┼──────────────┼─────────────┤
│ 3 │ Charlie │ 100.00 │ 330.00 │ 4.30 │
├──────────┼────────────────┼──────────────┼──────────────┼─────────────┤
│ 4 │ Delta │ 105.00 │ 325.00 │ 4.10 │
├──────────┼────────────────┼──────────────┼──────────────┼─────────────┤
│ 5 │ Echo │ 145.00 │ 425.00 │ 3.93 │
├──────────┼────────────────┼──────────────┼──────────────┼─────────────┤
│ 6 │ Foxtrot │ 135.00 │ 390.00 │ 3.89 │
├──────────┼────────────────┼──────────────┼──────────────┼─────────────┤
│ 7 │ Golf │ 160.00 │ 320.00 │ 3.00 │
├──────────┼────────────────┼──────────────┼──────────────┼─────────────┤
│ 8 │ Hotel │ 185.00 │ 310.00 │ 2.68 │
├──────────┼────────────────┼──────────────┼──────────────┼─────────────┤
│ 9 │ India │ 445.00 │ 510.00 │ 2.15 │
├──────────┼────────────────┼──────────────┼──────────────┼─────────────┤
│ 10 │ Juliet │ 295.00 │ 305.00 │ 2.03 │
├──────────┼────────────────┼──────────────┼──────────────┼─────────────┤
│ 11 │ Quebec │ 500.00 │ -500.00 │ 0.00 │
╘══════════╧════════════════╧══════════════╧══════════════╧═════════════╛
╒═══════════════════╤══════════════╤═════════════════╤════════════╤══════════════╤══════════════╤══════════════════╤═══════════════════╤══════════════════╕
│ Average (Stake) │ SD (Stake) │ Average (PnL) │ SD (Pnl) │ Skew (PnL) │ Market RoI │ Avg Odds (Win) │ Avg Probs (Win) │ Avg Edge (Win) │
╞═══════════════════╪══════════════╪═════════════════╪════════════╪══════════════╪══════════════╪══════════════════╪═══════════════════╪══════════════════╡
│ 266.50 │ 134.81 │ 19.75 │ 390.77 │ -0.04 │ 0.0741 │ 3.56 │ 0.50 │ 0.78046 │
╘═══════════════════╧══════════════╧═════════════════╧════════════╧══════════════╧══════════════╧══════════════════╧═══════════════════╧══════════════════╛
Output saved to C:\data\How-You-Doin_Updated.csv.
Fini!
Note: There are a couple of additional functions (e.g.
calc_performance_by_odds_range ), with which you can experiment
to provide additional insights.
Trading Performance Metrics
Understanding the metrics of trading performance can be the difference
between a thriving portfolio and a stagnant one. Here's a brief overview
of each metric:
Stake Average
This represents the typical amount invested in a trade. It helps us gauge
our comfort level with the stakes we're placing.
Stake Volatility
Volatility measures the degree of variation in our stakes, indicating the
consistency of our investment amounts.
Profit Average
The average profit metric provides insight into the typical return we're
seeing from our trades.
Profit Volatility
This tells us how much our profits can swing, helping us understand the
potential highs and lows in our returns.
Profit Skewness
Skewness gives us a sense of the asymmetry in our profit distribution. It
can reveal whether we're experiencing infrequent large wins or frequent
small losses.
Win Odds and Win Probability
These metrics offer a perspective on our chances of winning based on past
performance. They are approximations but can guide future betting
strategies.
Edge
The edge is perhaps the most critical metric, as it represents our
advantage over the market. It's the expected value of our betting strategy
and can indicate long-term profitability.
Edge Volatility And Skewness
In sports betting, the concepts of "edge,"
"volatility," and "skewness" refer to different aspects of the betting process
and outcomes. Here's how each term is typically understood in the context of
sports betting:
Edge
The "edge" in sports betting is a measure of the bettor's
advantage over the bookmaker or the market. It represents the expected
value of a bet and is usually expressed as a percentage. If a bettor has
an edge, it means that over the long run, they can expect to make a
profit. Calculating the edge involves comparing the bettor's assessment of
the true probability of an outcome with the implied probability of the
odds being offered.
For example, if a bettor believes Team A has a 60% chance
of winning, but the bookmaker's odds imply a 50% chance, the bettor
would have an edge. This is because they are getting better odds than
their own assessment suggests they should.
Volatility
Volatility in sports betting refers to the variation in results from what
is expected over a period. It's a measure of risk and indicates how much a
bettor's bankroll could fluctuate in the short term due to the
unpredictable nature of sports events. High volatility means that outcomes
can greatly vary, leading to potentially big wins or losses; low
volatility implies more consistent outcomes closer to the expected value.
For example, betting on underdogs is generally considered
more volatile because they win less frequently, but when they do win,
the payouts are large. Betting on favorites is less volatile because
they win more often, but the payouts are smaller.
Skewness
Skewness in the context of sports betting refers to the asymmetry of the
distribution of betting returns. It indicates whether the results of a
series of bets are likely to be skewed towards wins or losses, rather than
being symmetrically distributed around the mean.
Positive Skewness : This means that there's a higher
probability of obtaining a return significantly above the average, but
with a lower frequency of occurrence (e.g., betting on long shots with
high odds).
Negative Skewness : This suggests a higher probability
of small gains and a smaller chance of large losses (e.g., betting on
heavy favorites).
Example : If a bettor frequently bets on outcomes with
high odds (and thus high payouts), the distribution of their returns
will show positive skewness. Most of their bets will lose (resulting
in small losses each time), but the occasional big win will pull the
average return up.
Understanding these concepts can help bettors manage their bankrolls,
choose their betting strategies, and ultimately aim for long-term
profitability. However, it's important to note that having an edge does
not guarantee profit in the short term due to volatility and the potential
for skewed outcomes.
Successful betting requires a combination of finding value (edge),
managing risk (volatility), and understanding the distribution of your
betting returns (skewness).
Though the code and the sample data are mine, I relied on
GPT-4 for the definitions and examples in the section on
Edge Volatility And Skewness.
As ever, the script has little or no "errr0r" handling and is
only a starting point for your own explorations.
Enjoy!