diff --git a/PEV 3.11-10.26.xlsx b/PEV 3.11-10.26.xlsx
new file mode 100644
index 0000000..d7fae57
Binary files /dev/null and b/PEV 3.11-10.26.xlsx differ
diff --git a/dao/Database.py b/dao/Database.py
index db3a413..40305a8 100644
--- a/dao/Database.py
+++ b/dao/Database.py
@@ -1,6 +1,7 @@
import pymysql.cursors
from data_model import MysqlConfig
+
class Database:
def __init__(self, config: MysqlConfig):
self.config = config
diff --git a/data_model.py b/data_model.py
index 6a648c3..343d9a1 100644
--- a/data_model.py
+++ b/data_model.py
@@ -61,9 +61,10 @@ class OddsJamOrder(BaseModel):
home_or_away: Optional[str] = Field(None, description="主队或客队")
start_timestamp: Optional[int] = Field(None, description="开始时间戳")
selected_sportsbook: Optional[str] = Field(None, description="选定的博彩公司")
+ bet_status: Optional[str] = Field(None, description="投注状态")
-class OddjamsBet(BaseModel):
+class OddsjamBet(BaseModel):
Sportsbook: str
BetName: str
MarketName: str
diff --git a/pev_eda.ipynb b/pev_eda.ipynb
new file mode 100644
index 0000000..477f159
--- /dev/null
+++ b/pev_eda.ipynb
@@ -0,0 +1,2030 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pandas as pd\n",
+ "from datetime import datetime\n",
+ "from dao.Database import Database\n",
+ "from data_model import MysqlConfig, OddsJamOrder, OddsjamBet"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "config_file_path = 'config\\mysql_config.json'\n",
+ "mysql_config = MysqlConfig.parse_file(config_file_path)\n",
+ "dao = Database(mysql_config)\n",
+ "\n",
+ "# query\n",
+ "select_query = \"SELECT * FROM bet.oddsjam_order where bet_status in ('won', 'lost');\"\n",
+ "raw_data_list = dao.fetchall(query=select_query)\n",
+ "order_data_list = [OddsJamOrder(**data).model_dump() for data in raw_data_list]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'id': 'edge:10012-40335-2024-08-31:1st_half_team_total:oud-heverlee_leuven_over_0_5:oud-heverlee_leuven_under_0_5',\n",
+ " 'bet_id': 'edge:10012-40335-2024-08-31:1st_half_team_total:oud-heverlee_leuven_over_0_5:oud-heverlee_leuven_under_0_5',\n",
+ " 'game_id': '10012-40335-2024-08-31',\n",
+ " 'away_bet_name': 'Oud-Heverlee Leuven Under 0.5',\n",
+ " 'home_bet_name': 'Oud-Heverlee Leuven Over 0.5',\n",
+ " 'away_no_vig_price': 102.85,\n",
+ " 'home_no_vig_price': -102.85,\n",
+ " 'away_price': -110.0,\n",
+ " 'home_price': -102.0,\n",
+ " 'away_sportsbooks': ['FanDuel', 'DraftKings'],\n",
+ " 'home_sportsbooks': ['Bovada'],\n",
+ " 'is_live': False,\n",
+ " 'in_game_status': None,\n",
+ " 'period': None,\n",
+ " 'clock': None,\n",
+ " 'last_play': None,\n",
+ " 'home_score': 0,\n",
+ " 'away_score': 0,\n",
+ " 'sport': 'Soccer',\n",
+ " 'league': 'Belgium - Jupiler Pro League',\n",
+ " 'market': '1st Half Team Total',\n",
+ " 'market_width': 22.0,\n",
+ " 'percentage': 0.41,\n",
+ " 'start_date': datetime.datetime(2024, 9, 1, 2, 45),\n",
+ " 'home_team': 'Oud-Heverlee Leuven',\n",
+ " 'away_team': 'Royal Standard de Liège',\n",
+ " 'timestamp': 1725109490.1882415,\n",
+ " 'away_deep_link_map': '{\"FanDuel\": {\"urls\": {\"ios\": {\"url\": \"https://sportsbook.fanduel.com/addToBetslip?marketId=42.441130559&selectionId=7044483\", \"adds_to_slip\": true}, \"android\": {\"url\": \"https://sportsbook.fanduel.com/addToBetslip?marketId=42.441130559&selectionId=7044483\", \"adds_to_slip\": true}, \"desktop\": {\"url\": \"https://sportsbook.fanduel.com/addToBetslip?marketId=42.441130559&selectionId=7044483\", \"adds_to_slip\": true}}, \"mobile\": \"https://sportsbook.fanduel.com/addToBetslip?marketId=42.441130559&selectionId=7044483\", \"desktop\": \"https://sportsbook.fanduel.com/addToBetslip?marketId=42.441130559&selectionId=7044483\"}, \"DraftKings\": {\"urls\": {\"ios\": {\"url\": \"dksb://sb/addbet/0OU76429572U50_3\", \"adds_to_slip\": true}, \"android\": {\"url\": \"dksb://sb/addbet/0OU76429572U50_3\", \"adds_to_slip\": true}, \"desktop\": {\"url\": \"https://sportsbook.draftkings.com/event/30960358?outcomes=0OU76429572U50_3\", \"adds_to_slip\": true}}, \"mobile\": \"dksb://sb/addbet/0OU76429572U50_3\", \"desktop\": \"https://sportsbook.draftkings.com/event/30960358?outcomes=0OU76429572U50_3\"}}',\n",
+ " 'home_deep_link_map': '{}',\n",
+ " 'status': 'unplayed',\n",
+ " 'away_edge_percent': None,\n",
+ " 'bet_placed': False,\n",
+ " 'same_market_bet_placed': False,\n",
+ " 'same_game_bet_placed': False,\n",
+ " 'away_rec_size': None,\n",
+ " 'rec_size': 1,\n",
+ " 'home_edge_percent': 0.41,\n",
+ " 'order_status': 0,\n",
+ " 'amount': None,\n",
+ " 'create_time': '20240831211007',\n",
+ " 'sportsbook_orderid': None,\n",
+ " 'home_or_away': 'home',\n",
+ " 'start_timestamp': 1725129900000,\n",
+ " 'selected_sportsbook': 'Bovada',\n",
+ " 'bet_status': 'won'}"
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "order_data_list[1]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "db_df = pd.DataFrame(order_data_list)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 176,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_benefit(row):\n",
+ " home_or_away = row['home_or_away']\n",
+ " price = row[f'{home_or_away}_price'] / 100\n",
+ " if row['outcome'] == -1:\n",
+ " return -1\n",
+ " if price >= 0:\n",
+ " return price \n",
+ " else:\n",
+ " return 1 / abs(price) \n",
+ " \n",
+ "db_df['outcome'] = db_df['bet_status'].apply(lambda x: 1 if x == 'won' else -1)\n",
+ "db_df['benefit'] = db_df.apply(get_benefit, axis=1)\n",
+ "db_df['date'] = db_df['start_timestamp'].apply(lambda x: datetime.fromtimestamp(x // 1000).strftime(\"%Y-%m-%d\"))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 153,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_odds(row):\n",
+ " home_or_away = row['home_or_away']\n",
+ " price = row[f'{home_or_away}_price'] / 100\n",
+ "\n",
+ " if price >= 0:\n",
+ " return price\n",
+ " else:\n",
+ " return 1 / abs(price) "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 154,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "db_df['odds2'] = db_df.apply(get_odds, axis=1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 144,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "3.4551825216755248"
+ ]
+ },
+ "execution_count": 144,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "db_df['odds2'].mean()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 133,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "bet_status\n",
+ "lost 13439\n",
+ "won 5902\n",
+ "dtype: int64"
+ ]
+ },
+ "execution_count": 133,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "db_df.groupby('bet_status').size()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 130,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "3.4551825216755248"
+ ]
+ },
+ "execution_count": 130,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "db_df[db_df['bet_status'].isin(['won', 'lost'])]['benefit'].mean()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 149,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "-1477.1127282023094"
+ ]
+ },
+ "execution_count": 149,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "db_df[['home_price', 'away_price', 'home_or_away', 'bet_status', 'benefit']]['benefit']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 166,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "count 1780.000000\n",
+ "mean 1.165759\n",
+ "std 0.338662\n",
+ "min 0.359712\n",
+ "25% 0.909091\n",
+ "50% 1.080000\n",
+ "75% 1.360000\n",
+ "max 4.000000\n",
+ "Name: odds2, dtype: float64"
+ ]
+ },
+ "execution_count": 166,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "db_df[(db_df['market_width'] <= 25) & (db_df['market_width'] >= 20)]['odds2'].describe()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 168,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "bet_status\n",
+ "lost 932\n",
+ "won 848\n",
+ "dtype: int64"
+ ]
+ },
+ "execution_count": 168,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "db_df[(db_df['market_width'] <= 25) & (db_df['market_width'] >= 20)].groupby(['bet_status']).size()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 171,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0.4764044943820225"
+ ]
+ },
+ "execution_count": 171,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "848 / (848+932)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 170,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1.0318921348314607"
+ ]
+ },
+ "execution_count": 170,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "848 / (848+932) * (1.166 + 1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 156,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "count 5902.000000\n",
+ "mean 2.026751\n",
+ "std 1.825651\n",
+ "min 0.166667\n",
+ "25% 1.020000\n",
+ "50% 1.430000\n",
+ "75% 2.200000\n",
+ "max 25.000000\n",
+ "Name: odds2, dtype: float64"
+ ]
+ },
+ "execution_count": 156,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "db_df[db_df['bet_status'] == 'won']['odds2'].describe()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 157,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "count 13439.000000\n",
+ "mean 4.082506\n",
+ "std 3.955954\n",
+ "min 0.235294\n",
+ "25% 1.500000\n",
+ "50% 2.700000\n",
+ "75% 5.400000\n",
+ "max 35.000000\n",
+ "Name: odds2, dtype: float64"
+ ]
+ },
+ "execution_count": 157,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "db_df[db_df['bet_status'] == 'lost']['odds2'].describe()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 116,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "count 19341.000000\n",
+ "mean 392.987177\n",
+ "std 1713.715890\n",
+ "min 5.000000\n",
+ "25% 33.000000\n",
+ "50% 75.000000\n",
+ "75% 244.000000\n",
+ "max 49049.000000\n",
+ "Name: market_width, dtype: float64"
+ ]
+ },
+ "execution_count": 116,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "db_df['market_width'].describe()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "lost_df = db_df[db_df['outcome'] == -1]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "count 13439.000000\n",
+ "mean 517.244661\n",
+ "std 2026.572578\n",
+ "min 5.000000\n",
+ "25% 40.000000\n",
+ "50% 105.000000\n",
+ "75% 348.000000\n",
+ "max 49049.000000\n",
+ "Name: market_width, dtype: float64"
+ ]
+ },
+ "execution_count": 49,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "lost_df['market_width'].describe()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "raw_df = pd.read_excel('PEV 3.11-10.26.xlsx', sheet_name='原始数据')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " away_team | \n",
+ " best_price_away_name | \n",
+ " best_price_away_odd | \n",
+ " best_price_away_odd_books_new | \n",
+ " best_price_home_name | \n",
+ " best_price_home_odd | \n",
+ " best_price_home_odd_books_new | \n",
+ " home_team | \n",
+ " league | \n",
+ " market | \n",
+ " sport | \n",
+ " date | \n",
+ " weighted_price_away_odd | \n",
+ " weighted_price_home_odd | \n",
+ " handicap to go | \n",
+ " outcome | \n",
+ " odds US | \n",
+ " odds EU | \n",
+ " benefit | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " FC Augsburg | \n",
+ " Under 4.5 | \n",
+ " -150 | \n",
+ " OddsJam,William Hill,Caesars | \n",
+ " Over 4.5 | \n",
+ " 162 | \n",
+ " FanDuel | \n",
+ " FC Bayern Munich | \n",
+ " Germany - Bundesliga | \n",
+ " Total Goals | \n",
+ " soccer | \n",
+ " 2023-03-11 | \n",
+ " -141.153 | \n",
+ " 142.032 | \n",
+ " Over 4.5 | \n",
+ " 1.0 | \n",
+ " 162 | \n",
+ " 1.620000 | \n",
+ " 1.620000 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " FC Augsburg | \n",
+ " FC Bayern Munich Under 3.5 | \n",
+ " -137 | \n",
+ " OddsJam | \n",
+ " FC Bayern Munich Over 3.5 | \n",
+ " 146 | \n",
+ " FanDuel | \n",
+ " FC Bayern Munich | \n",
+ " Germany - Bundesliga | \n",
+ " Team Total | \n",
+ " soccer | \n",
+ " 2023-03-11 | \n",
+ " -126.848 | \n",
+ " 127.536 | \n",
+ " FC Bayern Munich Over 3.5 | \n",
+ " 1.0 | \n",
+ " 146 | \n",
+ " 1.460000 | \n",
+ " 1.460000 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " FC Augsburg | \n",
+ " FC Bayern Munich Under 1.5 | \n",
+ " -135 | \n",
+ " DraftKings,10bet,DraftKings (Tennessee) | \n",
+ " FC Bayern Munich Over 1.5 | \n",
+ " 128 | \n",
+ " FanDuel | \n",
+ " FC Bayern Munich | \n",
+ " Germany - Bundesliga | \n",
+ " 1st Half Team Total | \n",
+ " soccer | \n",
+ " 2023-03-11 | \n",
+ " -123.407 | \n",
+ " 123.898 | \n",
+ " FC Bayern Munich Over 1.5 | \n",
+ " 1.0 | \n",
+ " 128 | \n",
+ " 1.280000 | \n",
+ " 1.280000 | \n",
+ "
\n",
+ " \n",
+ " | 6 | \n",
+ " Liverpool | \n",
+ " Bournemouth Under 0.5 | \n",
+ " 132 | \n",
+ " FanDuel | \n",
+ " Bournemouth Over 0.5 | \n",
+ " -137 | \n",
+ " Barstool | \n",
+ " Bournemouth | \n",
+ " England - Premier League | \n",
+ " Team Total | \n",
+ " soccer | \n",
+ " 2023-03-11 | \n",
+ " 141.875 | \n",
+ " -141.519 | \n",
+ " Bournemouth Over 0.5 | \n",
+ " 1.0 | \n",
+ " -137 | \n",
+ " 0.729927 | \n",
+ " 0.729927 | \n",
+ "
\n",
+ " \n",
+ " | 8 | \n",
+ " FC Augsburg | \n",
+ " Under 3.5 | \n",
+ " 141 | \n",
+ " OddsJam | \n",
+ " Over 3.5 | \n",
+ " -138 | \n",
+ " FanDuel | \n",
+ " FC Bayern Munich | \n",
+ " Germany - Bundesliga | \n",
+ " Total Goals | \n",
+ " soccer | \n",
+ " 2023-03-11 | \n",
+ " 147.467 | \n",
+ " -146.976 | \n",
+ " Over 3.5 | \n",
+ " 1.0 | \n",
+ " -138 | \n",
+ " 0.724638 | \n",
+ " 0.724638 | \n",
+ "
\n",
+ " \n",
+ " | ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " | 80358 | \n",
+ " New Orleans Pelicans | \n",
+ " Memphis Grizzlies +1.5 | \n",
+ " -143 | \n",
+ " TonyBet | \n",
+ " New Orleans Pelicans -1.5 | \n",
+ " 135 | \n",
+ " BetMGM,partypoker,Borgata | \n",
+ " Memphis Grizzlies | \n",
+ " NBA | \n",
+ " 1st Half Point Spread | \n",
+ " Basketball | \n",
+ " 2023-10-26 | \n",
+ " -133.490 | \n",
+ " 133.546 | \n",
+ " New Orleans Pelicans -1.5 | \n",
+ " 1.0 | \n",
+ " 135 | \n",
+ " 1.350000 | \n",
+ " 1.350000 | \n",
+ "
\n",
+ " \n",
+ " | 80360 | \n",
+ " Dallas Mavericks | \n",
+ " San Antonio Spurs -0.5 | \n",
+ " 148 | \n",
+ " FanDuel | \n",
+ " Dallas Mavericks +0.5 | \n",
+ " -155 | \n",
+ " Pinny,Pinnacle | \n",
+ " San Antonio Spurs | \n",
+ " NBA | \n",
+ " 1st Half Point Spread | \n",
+ " Basketball | \n",
+ " 2023-10-26 | \n",
+ " 146.604 | \n",
+ " -146.127 | \n",
+ " San Antonio Spurs -0.5 | \n",
+ " 1.0 | \n",
+ " 148 | \n",
+ " 1.480000 | \n",
+ " 1.480000 | \n",
+ "
\n",
+ " \n",
+ " | 80361 | \n",
+ " Dallas Mavericks | \n",
+ " San Antonio Spurs +0.5 | \n",
+ " 128 | \n",
+ " FanDuel | \n",
+ " Dallas Mavericks -0.5 | \n",
+ " -132 | \n",
+ " Pinny,Pinnacle | \n",
+ " San Antonio Spurs | \n",
+ " NBA | \n",
+ " 1st Half Point Spread | \n",
+ " Basketball | \n",
+ " 2023-10-26 | \n",
+ " 126.875 | \n",
+ " -126.419 | \n",
+ " San Antonio Spurs +0.5 | \n",
+ " 1.0 | \n",
+ " 128 | \n",
+ " 1.280000 | \n",
+ " 1.280000 | \n",
+ "
\n",
+ " \n",
+ " | 80363 | \n",
+ " Portland Trail Blazers | \n",
+ " Los Angeles Clippers Under 60.5 | \n",
+ " -116 | \n",
+ " Pinny,Pinnacle | \n",
+ " Los Angeles Clippers Over 60.5 | \n",
+ " 110 | \n",
+ " partypoker,BetMGM,Borgata | \n",
+ " Los Angeles Clippers | \n",
+ " NBA | \n",
+ " 1st Half Team Total | \n",
+ " Basketball | \n",
+ " 2023-10-26 | \n",
+ " -106.549 | \n",
+ " 106.565 | \n",
+ " Los Angeles Clippers Over 60.5 | \n",
+ " 1.0 | \n",
+ " 110 | \n",
+ " 1.100000 | \n",
+ " 1.100000 | \n",
+ "
\n",
+ " \n",
+ " | 80365 | \n",
+ " Portland Trail Blazers | \n",
+ " Los Angeles Clippers -3.5 | \n",
+ " 104 | \n",
+ " Unibet,BetRivers,betPARX | \n",
+ " Portland Trail Blazers +3.5 | \n",
+ " -110 | \n",
+ " BetAnySports,LowVig,BetOnline,SuperBook | \n",
+ " Los Angeles Clippers | \n",
+ " NBA | \n",
+ " 1st Quarter Point Spread | \n",
+ " Basketball | \n",
+ " 2023-10-26 | \n",
+ " 103.013 | \n",
+ " -102.757 | \n",
+ " Los Angeles Clippers -3.5 | \n",
+ " 1.0 | \n",
+ " 104 | \n",
+ " 1.040000 | \n",
+ " 1.040000 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
37469 rows × 19 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " away_team best_price_away_name \\\n",
+ "0 FC Augsburg Under 4.5 \n",
+ "1 FC Augsburg FC Bayern Munich Under 3.5 \n",
+ "2 FC Augsburg FC Bayern Munich Under 1.5 \n",
+ "6 Liverpool Bournemouth Under 0.5 \n",
+ "8 FC Augsburg Under 3.5 \n",
+ "... ... ... \n",
+ "80358 New Orleans Pelicans Memphis Grizzlies +1.5 \n",
+ "80360 Dallas Mavericks San Antonio Spurs -0.5 \n",
+ "80361 Dallas Mavericks San Antonio Spurs +0.5 \n",
+ "80363 Portland Trail Blazers Los Angeles Clippers Under 60.5 \n",
+ "80365 Portland Trail Blazers Los Angeles Clippers -3.5 \n",
+ "\n",
+ " best_price_away_odd best_price_away_odd_books_new \\\n",
+ "0 -150 OddsJam,William Hill,Caesars \n",
+ "1 -137 OddsJam \n",
+ "2 -135 DraftKings,10bet,DraftKings (Tennessee) \n",
+ "6 132 FanDuel \n",
+ "8 141 OddsJam \n",
+ "... ... ... \n",
+ "80358 -143 TonyBet \n",
+ "80360 148 FanDuel \n",
+ "80361 128 FanDuel \n",
+ "80363 -116 Pinny,Pinnacle \n",
+ "80365 104 Unibet,BetRivers,betPARX \n",
+ "\n",
+ " best_price_home_name best_price_home_odd \\\n",
+ "0 Over 4.5 162 \n",
+ "1 FC Bayern Munich Over 3.5 146 \n",
+ "2 FC Bayern Munich Over 1.5 128 \n",
+ "6 Bournemouth Over 0.5 -137 \n",
+ "8 Over 3.5 -138 \n",
+ "... ... ... \n",
+ "80358 New Orleans Pelicans -1.5 135 \n",
+ "80360 Dallas Mavericks +0.5 -155 \n",
+ "80361 Dallas Mavericks -0.5 -132 \n",
+ "80363 Los Angeles Clippers Over 60.5 110 \n",
+ "80365 Portland Trail Blazers +3.5 -110 \n",
+ "\n",
+ " best_price_home_odd_books_new home_team \\\n",
+ "0 FanDuel FC Bayern Munich \n",
+ "1 FanDuel FC Bayern Munich \n",
+ "2 FanDuel FC Bayern Munich \n",
+ "6 Barstool Bournemouth \n",
+ "8 FanDuel FC Bayern Munich \n",
+ "... ... ... \n",
+ "80358 BetMGM,partypoker,Borgata Memphis Grizzlies \n",
+ "80360 Pinny,Pinnacle San Antonio Spurs \n",
+ "80361 Pinny,Pinnacle San Antonio Spurs \n",
+ "80363 partypoker,BetMGM,Borgata Los Angeles Clippers \n",
+ "80365 BetAnySports,LowVig,BetOnline,SuperBook Los Angeles Clippers \n",
+ "\n",
+ " league market sport \\\n",
+ "0 Germany - Bundesliga Total Goals soccer \n",
+ "1 Germany - Bundesliga Team Total soccer \n",
+ "2 Germany - Bundesliga 1st Half Team Total soccer \n",
+ "6 England - Premier League Team Total soccer \n",
+ "8 Germany - Bundesliga Total Goals soccer \n",
+ "... ... ... ... \n",
+ "80358 NBA 1st Half Point Spread Basketball \n",
+ "80360 NBA 1st Half Point Spread Basketball \n",
+ "80361 NBA 1st Half Point Spread Basketball \n",
+ "80363 NBA 1st Half Team Total Basketball \n",
+ "80365 NBA 1st Quarter Point Spread Basketball \n",
+ "\n",
+ " date weighted_price_away_odd weighted_price_home_odd \\\n",
+ "0 2023-03-11 -141.153 142.032 \n",
+ "1 2023-03-11 -126.848 127.536 \n",
+ "2 2023-03-11 -123.407 123.898 \n",
+ "6 2023-03-11 141.875 -141.519 \n",
+ "8 2023-03-11 147.467 -146.976 \n",
+ "... ... ... ... \n",
+ "80358 2023-10-26 -133.490 133.546 \n",
+ "80360 2023-10-26 146.604 -146.127 \n",
+ "80361 2023-10-26 126.875 -126.419 \n",
+ "80363 2023-10-26 -106.549 106.565 \n",
+ "80365 2023-10-26 103.013 -102.757 \n",
+ "\n",
+ " handicap to go outcome odds US odds EU benefit \n",
+ "0 Over 4.5 1.0 162 1.620000 1.620000 \n",
+ "1 FC Bayern Munich Over 3.5 1.0 146 1.460000 1.460000 \n",
+ "2 FC Bayern Munich Over 1.5 1.0 128 1.280000 1.280000 \n",
+ "6 Bournemouth Over 0.5 1.0 -137 0.729927 0.729927 \n",
+ "8 Over 3.5 1.0 -138 0.724638 0.724638 \n",
+ "... ... ... ... ... ... \n",
+ "80358 New Orleans Pelicans -1.5 1.0 135 1.350000 1.350000 \n",
+ "80360 San Antonio Spurs -0.5 1.0 148 1.480000 1.480000 \n",
+ "80361 San Antonio Spurs +0.5 1.0 128 1.280000 1.280000 \n",
+ "80363 Los Angeles Clippers Over 60.5 1.0 110 1.100000 1.100000 \n",
+ "80365 Los Angeles Clippers -3.5 1.0 104 1.040000 1.040000 \n",
+ "\n",
+ "[37469 rows x 19 columns]"
+ ]
+ },
+ "execution_count": 23,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "raw_df[raw_df['outcome'] == 1]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 93,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "data_df = raw_df.copy()\n",
+ "data_df['investment'] = 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 179,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\n",
+ "data_df = db_df.copy()\n",
+ "data_df = data_df[data_df['market_width'] <= 25]\n",
+ "data_df = data_df[data_df['market_width'] >= 20]\n",
+ "# data_df = data_df[data_df['selected_sportsbook'] == '1XBet']\n",
+ "data_df['investment'] = 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 180,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "年化夏普率: 0.8002309699976565\n",
+ "ROI: 0.00683522830064485\n"
+ ]
+ }
+ ],
+ "source": [
+ "def clac_closing_balance(day_benefit_list, pre_balance=1000, pre_benefit=0):\n",
+ " closing_balance_list = []\n",
+ " for i, benefit in enumerate(day_benefit_list):\n",
+ " closing_balance = pre_balance + pre_benefit / 3 + benefit * 2 / 3\n",
+ " closing_balance_list.append(closing_balance)\n",
+ " pre_balance = closing_balance\n",
+ " pre_benefit = benefit\n",
+ " return closing_balance_list\n",
+ "\n",
+ "def calc_in_transit_funds_ratio(daily_investment_list, closing_balance_list, start_closing_balance=1000):\n",
+ " assert len(daily_investment_list) == len(closing_balance_list)\n",
+ " ratio_list = []\n",
+ " for i, daily_investment in enumerate(daily_investment_list):\n",
+ " if i == 0:\n",
+ " ratio = daily_investment / start_closing_balance\n",
+ " else:\n",
+ " ratio = daily_investment / closing_balance_list[i-1]\n",
+ " ratio_list.append(ratio)\n",
+ " return ratio_list\n",
+ "\n",
+ "\n",
+ "init_balance = 1000\n",
+ "res_df = data_df.groupby('date').agg({'investment':'sum', 'benefit':'sum'}).rename(columns={'investment':'当日投入', 'benefit':'日收益'}).reset_index()\n",
+ "res_df['日收益率'] = res_df['日收益'] / res_df['当日投入']\n",
+ "res_df['累计收益'] = res_df['日收益'].cumsum()\n",
+ "res_df['累计投入'] = res_df['当日投入'].cumsum()\n",
+ "res_df['累计收益率'] = res_df['累计收益'] / res_df['累计投入']\n",
+ "\n",
+ "day_benefit_list = res_df['日收益'].tolist()\n",
+ "closing_balance_list = clac_closing_balance(day_benefit_list=day_benefit_list, pre_balance=init_balance)\n",
+ "res_df['日末余额(1.6天结算)'] = closing_balance_list\n",
+ "\n",
+ "daily_investment_list = res_df['当日投入'].tolist()\n",
+ "res_df['在途资金比例'] = calc_in_transit_funds_ratio(daily_investment_list=daily_investment_list, closing_balance_list=closing_balance_list, start_closing_balance=init_balance)\n",
+ "annualized_sharpe_ratio = res_df['日收益'].sum() / init_balance / res_df['日收益率'].std() * ((365 / len(res_df))**0.5)\n",
+ "print(f'年化夏普率: {annualized_sharpe_ratio}')\n",
+ "roi = res_df['日收益'].sum() / res_df['当日投入'].sum()\n",
+ "print(f'ROI: {roi}')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 181,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.plotly.v1+json": {
+ "config": {
+ "plotlyServerURL": "https://plot.ly"
+ },
+ "data": [
+ {
+ "mode": "lines",
+ "name": "日末余额",
+ "type": "scatter",
+ "x": [
+ "2024-08-26T00:00:00",
+ "2024-08-27T00:00:00",
+ "2024-08-28T00:00:00",
+ "2024-08-29T00:00:00",
+ "2024-08-30T00:00:00",
+ "2024-08-31T00:00:00",
+ "2024-09-01T00:00:00"
+ ],
+ "y": [
+ 1002.6656885456886,
+ 1029.320085562455,
+ 1031.6353596927408,
+ 1014.877157613626,
+ 1004.9262841780602,
+ 1010.5006139053265,
+ 1012.8869095027875
+ ],
+ "yaxis": "y"
+ },
+ {
+ "mode": "lines",
+ "name": "累计收益率",
+ "type": "scatter",
+ "x": [
+ "2024-08-26T00:00:00",
+ "2024-08-27T00:00:00",
+ "2024-08-28T00:00:00",
+ "2024-08-29T00:00:00",
+ "2024-08-30T00:00:00",
+ "2024-08-31T00:00:00",
+ "2024-09-01T00:00:00"
+ ],
+ "y": [
+ 0.17384925297968776,
+ 0.18095199109662177,
+ 0.05703148399117068,
+ 0.013933178120379116,
+ 0.0027913825488691288,
+ 0.01187018704065157,
+ 0.00683522830064485
+ ],
+ "yaxis": "y2"
+ }
+ ],
+ "layout": {
+ "template": {
+ "data": {
+ "bar": [
+ {
+ "error_x": {
+ "color": "#2a3f5f"
+ },
+ "error_y": {
+ "color": "#2a3f5f"
+ },
+ "marker": {
+ "line": {
+ "color": "#E5ECF6",
+ "width": 0.5
+ },
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "bar"
+ }
+ ],
+ "barpolar": [
+ {
+ "marker": {
+ "line": {
+ "color": "#E5ECF6",
+ "width": 0.5
+ },
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "barpolar"
+ }
+ ],
+ "carpet": [
+ {
+ "aaxis": {
+ "endlinecolor": "#2a3f5f",
+ "gridcolor": "white",
+ "linecolor": "white",
+ "minorgridcolor": "white",
+ "startlinecolor": "#2a3f5f"
+ },
+ "baxis": {
+ "endlinecolor": "#2a3f5f",
+ "gridcolor": "white",
+ "linecolor": "white",
+ "minorgridcolor": "white",
+ "startlinecolor": "#2a3f5f"
+ },
+ "type": "carpet"
+ }
+ ],
+ "choropleth": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "type": "choropleth"
+ }
+ ],
+ "contour": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "contour"
+ }
+ ],
+ "contourcarpet": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "type": "contourcarpet"
+ }
+ ],
+ "heatmap": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "heatmap"
+ }
+ ],
+ "heatmapgl": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "heatmapgl"
+ }
+ ],
+ "histogram": [
+ {
+ "marker": {
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "histogram"
+ }
+ ],
+ "histogram2d": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "histogram2d"
+ }
+ ],
+ "histogram2dcontour": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "histogram2dcontour"
+ }
+ ],
+ "mesh3d": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "type": "mesh3d"
+ }
+ ],
+ "parcoords": [
+ {
+ "line": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "parcoords"
+ }
+ ],
+ "pie": [
+ {
+ "automargin": true,
+ "type": "pie"
+ }
+ ],
+ "scatter": [
+ {
+ "fillpattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ },
+ "type": "scatter"
+ }
+ ],
+ "scatter3d": [
+ {
+ "line": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scatter3d"
+ }
+ ],
+ "scattercarpet": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scattercarpet"
+ }
+ ],
+ "scattergeo": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scattergeo"
+ }
+ ],
+ "scattergl": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scattergl"
+ }
+ ],
+ "scattermapbox": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scattermapbox"
+ }
+ ],
+ "scatterpolar": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scatterpolar"
+ }
+ ],
+ "scatterpolargl": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scatterpolargl"
+ }
+ ],
+ "scatterternary": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scatterternary"
+ }
+ ],
+ "surface": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "surface"
+ }
+ ],
+ "table": [
+ {
+ "cells": {
+ "fill": {
+ "color": "#EBF0F8"
+ },
+ "line": {
+ "color": "white"
+ }
+ },
+ "header": {
+ "fill": {
+ "color": "#C8D4E3"
+ },
+ "line": {
+ "color": "white"
+ }
+ },
+ "type": "table"
+ }
+ ]
+ },
+ "layout": {
+ "annotationdefaults": {
+ "arrowcolor": "#2a3f5f",
+ "arrowhead": 0,
+ "arrowwidth": 1
+ },
+ "autotypenumbers": "strict",
+ "coloraxis": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "colorscale": {
+ "diverging": [
+ [
+ 0,
+ "#8e0152"
+ ],
+ [
+ 0.1,
+ "#c51b7d"
+ ],
+ [
+ 0.2,
+ "#de77ae"
+ ],
+ [
+ 0.3,
+ "#f1b6da"
+ ],
+ [
+ 0.4,
+ "#fde0ef"
+ ],
+ [
+ 0.5,
+ "#f7f7f7"
+ ],
+ [
+ 0.6,
+ "#e6f5d0"
+ ],
+ [
+ 0.7,
+ "#b8e186"
+ ],
+ [
+ 0.8,
+ "#7fbc41"
+ ],
+ [
+ 0.9,
+ "#4d9221"
+ ],
+ [
+ 1,
+ "#276419"
+ ]
+ ],
+ "sequential": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "sequentialminus": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ]
+ },
+ "colorway": [
+ "#636efa",
+ "#EF553B",
+ "#00cc96",
+ "#ab63fa",
+ "#FFA15A",
+ "#19d3f3",
+ "#FF6692",
+ "#B6E880",
+ "#FF97FF",
+ "#FECB52"
+ ],
+ "font": {
+ "color": "#2a3f5f"
+ },
+ "geo": {
+ "bgcolor": "white",
+ "lakecolor": "white",
+ "landcolor": "#E5ECF6",
+ "showlakes": true,
+ "showland": true,
+ "subunitcolor": "white"
+ },
+ "hoverlabel": {
+ "align": "left"
+ },
+ "hovermode": "closest",
+ "mapbox": {
+ "style": "light"
+ },
+ "paper_bgcolor": "white",
+ "plot_bgcolor": "#E5ECF6",
+ "polar": {
+ "angularaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ },
+ "bgcolor": "#E5ECF6",
+ "radialaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ }
+ },
+ "scene": {
+ "xaxis": {
+ "backgroundcolor": "#E5ECF6",
+ "gridcolor": "white",
+ "gridwidth": 2,
+ "linecolor": "white",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "white"
+ },
+ "yaxis": {
+ "backgroundcolor": "#E5ECF6",
+ "gridcolor": "white",
+ "gridwidth": 2,
+ "linecolor": "white",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "white"
+ },
+ "zaxis": {
+ "backgroundcolor": "#E5ECF6",
+ "gridcolor": "white",
+ "gridwidth": 2,
+ "linecolor": "white",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "white"
+ }
+ },
+ "shapedefaults": {
+ "line": {
+ "color": "#2a3f5f"
+ }
+ },
+ "ternary": {
+ "aaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ },
+ "baxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ },
+ "bgcolor": "#E5ECF6",
+ "caxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ }
+ },
+ "title": {
+ "x": 0.05
+ },
+ "xaxis": {
+ "automargin": true,
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": "",
+ "title": {
+ "standoff": 15
+ },
+ "zerolinecolor": "white",
+ "zerolinewidth": 2
+ },
+ "yaxis": {
+ "automargin": true,
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": "",
+ "title": {
+ "standoff": 15
+ },
+ "zerolinecolor": "white",
+ "zerolinewidth": 2
+ }
+ }
+ },
+ "title": {
+ "text": "日末余额(1.6天结算) 和 累计收益率 折线图"
+ },
+ "xaxis": {
+ "title": {
+ "text": "日期"
+ }
+ },
+ "yaxis": {
+ "title": {
+ "text": "金额"
+ }
+ },
+ "yaxis2": {
+ "overlaying": "y",
+ "side": "right",
+ "tickformat": ".1%",
+ "title": {
+ "text": "收益率"
+ }
+ }
+ }
+ }
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import pandas as pd\n",
+ "import plotly.graph_objects as go\n",
+ "\n",
+ "res_df['日期'] = pd.to_datetime(res_df['date'])\n",
+ "\n",
+ "fig = go.Figure()\n",
+ "\n",
+ "# 添加日末余额(1.6天结算)的折线图\n",
+ "fig.add_trace(go.Scatter(\n",
+ " x=res_df['日期'],\n",
+ " y=res_df['日末余额(1.6天结算)'],\n",
+ " mode='lines',\n",
+ " name='日末余额',\n",
+ " yaxis='y1'\n",
+ "))\n",
+ "\n",
+ "# 添加日收益率的折线图\n",
+ "# fig.add_trace(go.Scatter(\n",
+ "# x=res_df['日期'],\n",
+ "# y=res_df['日收益率'],\n",
+ "# mode='lines',\n",
+ "# name='日收益率',\n",
+ "# yaxis='y2'\n",
+ "# ))\n",
+ "\n",
+ "# 添加累计收益率的折线图\n",
+ "fig.add_trace(go.Scatter(\n",
+ " x=res_df['日期'],\n",
+ " y=res_df['累计收益率'],\n",
+ " mode='lines',\n",
+ " name='累计收益率',\n",
+ " yaxis='y2'\n",
+ "))\n",
+ "\n",
+ "# 添加在途资金比例的折线图\n",
+ "# fig.add_trace(go.Scatter(\n",
+ "# x=res_df['日期'],\n",
+ "# y=res_df['在途资金比例'],\n",
+ "# mode='lines',\n",
+ "# name='在途资金比例',\n",
+ "# yaxis='y2'\n",
+ "# ))\n",
+ "\n",
+ "# 设置布局,添加第二个y轴\n",
+ "fig.update_layout(\n",
+ " title='日末余额(1.6天结算) 和 累计收益率 折线图',\n",
+ " xaxis=dict(title='日期'),\n",
+ " yaxis=dict(title='金额'),\n",
+ " yaxis2=dict(title='收益率', overlaying='y', side='right', tickformat='.1%')\n",
+ ")\n",
+ "\n",
+ "\n",
+ "fig.show()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# 基于market width 进行分层, 然后计算年华夏普率值, 进行资金配比"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 74,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " outcome | \n",
+ " count | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " -1.0 | \n",
+ " 39838 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 1.0 | \n",
+ " 37469 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 0.0 | \n",
+ " 2804 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " -0.5 | \n",
+ " 164 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 0.5 | \n",
+ " 91 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " outcome count\n",
+ "0 -1.0 39838\n",
+ "4 1.0 37469\n",
+ "2 0.0 2804\n",
+ "1 -0.5 164\n",
+ "3 0.5 91"
+ ]
+ },
+ "execution_count": 74,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "raw_df.groupby('outcome').agg({'away_team': 'count'}).rename(columns={'away_team': 'count'}).reset_index().sort_values(by='count', ascending=False)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1.1811803969722776"
+ ]
+ },
+ "execution_count": 75,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "raw_df['odds EU'].mean()"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "base",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}