import%20marimo%0A%0A__generated_with%20%3D%20%220.18.4%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20scipy.stats%20as%20stats%0A%20%20%20%20import%20seaborn%20as%20sns%0A%0A%20%20%20%20sns.set_style(%22whitegrid%22)%0A%0A%20%20%20%20import%20pyrf%20as%20rf%0A%20%20%20%20return%20mo%2C%20np%2C%20pd%2C%20plt%2C%20rf%2C%20stats%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(rf)%3A%0A%20%20%20%20grid%20%3D%20rf.GoalGrid.default()%0A%20%20%20%20return%20(grid%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Generate%20Sample%20Quote%20Objects%0A%0A%20%20%20%20%23%23%23%23%201.%20Asian%20Handicap%20Market%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(rf)%3A%0A%20%20%20%20quote_ah%20%3D%20rf.Quote.build(%0A%20%20%20%20%20%20%20%20line%3Drf.QuotedLine(0.5)%2C%0A%20%20%20%20%20%20%20%20market%3Drf.Market.ASIAN_HCAP%2C%0A%20%20%20%20%20%20%20%20side_pair%3D%5Brf.BetState.HOME%2C%20rf.BetState.AWAY%5D%2C%0A%20%20%20%20%20%20%20%20odds_pair%3D%5Brf.DecimalOdds(1.98)%2C%20rf.DecimalOdds(1.91)%5D%2C%0A%20%20%20%20)%0A%20%20%20%20print(quote_ah)%0A%20%20%20%20print(quote_ah.margin_set(0.01))%0A%20%20%20%20print(quote_ah.margin_strip())%0A%20%20%20%20return%20(quote_ah%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%23%202.%20Total%20Goals%20Market%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(rf)%3A%0A%20%20%20%20quote_tg%20%3D%20rf.Quote.build(%0A%20%20%20%20%20%20%20%20line%3Drf.QuotedLine(2.75)%2C%0A%20%20%20%20%20%20%20%20market%3Drf.Market.TOTAL_GOAL%2C%0A%20%20%20%20%20%20%20%20side_pair%3D%5Brf.BetState.OVER%2C%20rf.BetState.UNDR%5D%2C%0A%20%20%20%20%20%20%20%20odds_pair%3D%5Brf.DecimalOdds(1.89)%2C%20rf.DecimalOdds(1.99)%5D%2C%0A%20%20%20%20)%0A%20%20%20%20print(quote_tg)%0A%20%20%20%20print(quote_tg.margin_set(0.01))%0A%20%20%20%20print(quote_tg.margin_strip())%0A%20%20%20%20return%20(quote_tg%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Price%20both%20markets%20under%20the%20RF%20grid%20framework%0A%0A%20%20%20%20Adjust%20the%20%60lambda_home%60%20%26%20%60lambda_away%60%20parameters%20to%20see%20the%20effect%20on%20prices.%0A%20%20%20%20(Note%20we%20are%20generating%20fair%20model%20prices%20before%20the%20addition%20of%20any%20margin.)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(rf)%3A%0A%20%20%20%20poisson%20%3D%20rf.BivariatePoisson(%0A%20%20%20%20%20%20%20%20lambda_home%3D1.0%2C%0A%20%20%20%20%20%20%20%20lambda_away%3D1.0%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(poisson%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(np%2C%20poisson%2C%20rf)%3A%0A%20%20%20%20for%20_line%20in%20np.arange(0.5%2C%204.0%2C%200.25)%3A%0A%20%20%20%20%20%20%20%20print(rf.TotalGoals(line%3D_line).implied_odds(model%3Dpoisson))%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(np%2C%20poisson%2C%20rf)%3A%0A%20%20%20%20for%20_line%20in%20np.arange(-1.75%2C%202.00%2C%200.25)%3A%0A%20%20%20%20%20%20%20%20print(rf.AsianHandicap(line%3D_line).implied_odds(model%3Dpoisson))%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Search%20for%20lambda%20parameters%20to%20match%20target%0A%0A%20%20%20%20%23%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(quote_tg%2C%20rf)%3A%0A%20%20%20%20import%20scipy%20as%20sp%0A%0A%20%20%20%20q%20%3D%20quote_tg.margin_strip()%0A%20%20%20%20tgt_a%2C%20_tgt_b%20%3D%20q.side_a.odds.implied_probability()%2C%20q.side_b.odds.implied_probability()%0A%0A%20%20%20%20def%20func_tg(x)%3A%0A%20%20%20%20%20%20%20%20_poisson%20%3D%20rf.BivariatePoisson(lambda_home%3Dx%20%2F%202%2C%20lambda_away%3Dx%20%2F%202)%0A%20%20%20%20%20%20%20%20rslt_a%20%3D%20rf.TotalGoals(line%3Dquote_tg.line.value).implied_prob(model%3D_poisson)%5Brf.BetState.OVER%5D%0A%20%20%20%20%20%20%20%20return%20tgt_a%20-%20rslt_a%0A%0A%20%20%20%20tg_opt_result%20%3D%20sp.optimize.root_scalar(func_tg%2C%20bracket%3D%5B0%2C%2030%5D%2C%20xtol%3D1e-3)%0A%20%20%20%20lambda_total%20%3D%20tg_opt_result.root%0A%20%20%20%20lambda_total%0A%20%20%20%20return%20lambda_total%2C%20sp%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(lambda_total%2C%20quote_ah%2C%20rf%2C%20sp)%3A%0A%20%20%20%20p%20%3D%20quote_ah.margin_strip()%0A%20%20%20%20tgt_c%2C%20_tgt_d%20%3D%20p.side_a.odds.implied_probability()%2C%20p.side_b.odds.implied_probability()%0A%0A%20%20%20%20def%20func_ah(y)%3A%0A%20%20%20%20%20%20%20%20_poisson%20%3D%20rf.BivariatePoisson(lambda_home%3Dlambda_total%20%2F%202%20-%20y%2C%20lambda_away%3Dlambda_total%20%2F%202%20%2B%20y)%0A%20%20%20%20%20%20%20%20rslt_c%20%3D%20rf.AsianHandicap(line%3Dquote_ah.line.value).implied_prob(model%3D_poisson)%5Brf.BetState.HOME%5D%0A%20%20%20%20%20%20%20%20return%20tgt_c%20-%20rslt_c%0A%0A%20%20%20%20ah_opt_result%20%3D%20sp.optimize.root_scalar(func_ah%2C%20bracket%3D%5B0%2C%20lambda_total%20%2F%202%5D%2C%20xtol%3D1e-3)%0A%20%20%20%20lambda_home%20%3D%20lambda_total%20%2F%202%20-%20ah_opt_result.root%0A%20%20%20%20lambda_away%20%3D%20lambda_total%20%2F%202%20%2B%20ah_opt_result.root%0A%0A%20%20%20%20lambda_home%2C%20lambda_away%2C%20ah_opt_result.root%0A%20%20%20%20return%20lambda_away%2C%20lambda_home%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Dixon-Coles%20adjustment%20to%20low-scoring%20outcomes%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(grid%2C%20lambda_away%2C%20lambda_home%2C%20pd%2C%20rf)%3A%0A%20%20%20%20poisson2a%20%3D%20rf.BivariatePoisson(lambda_home%3Dlambda_home%2C%20lambda_away%3Dlambda_away)%0A%20%20%20%20poisson2b%20%3D%20rf.models.DixonColes(dist%3Dpoisson2a%2C%20rho%3D-0.10)%0A%0A%20%20%20%20pmfA%20%3D%20poisson2a.pmf(data%3Dgrid)%0A%20%20%20%20pmfB%20%3D%20poisson2b.pmf(data%3Dgrid)%0A%0A%20%20%20%20sumsA%20%3D%20rf.sum_diagonals(pmfA.to_numpy())%0A%20%20%20%20sumsB%20%3D%20rf.sum_diagonals(pmfB.to_numpy())%0A%0A%20%20%20%20pd.concat(%5BsumsA%2C%20sumsB%5D%2C%20axis%3D1%2C%20keys%3D%5B%22Base%22%2C%20%22Dixon-Coles%22%5D).plot.bar()%0A%20%20%20%20return%20sumsA%2C%20sumsB%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(pd%2C%20sumsA%2C%20sumsB)%3A%0A%20%20%20%20_out%20%3D%20pd.concat(%5BsumsA%2C%20sumsB%2C%20sumsB%20-%20sumsA%5D%2C%20axis%3D1%2C%20keys%3D%5B%22Base%22%2C%20%22Dixon-Coles%22%2C%20%22Diff%22%5D).loc%5B-4%3A4%5D%0A%20%20%20%20print(_out)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Verify%20grid%20approach%20vs%20closed%20forms%20for%20generating%20state%20probabilities%0A%0A%20%20%20%20%23%23%23%201.%20Total%20Goals%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(grid%2C%20pd%2C%20poisson%2C%20stats)%3A%0A%20%20%20%20def%20f_tg(k)%3A%0A%20%20%20%20%20%20%20%20A%20%3D%20poisson.pmf(data%3Dgrid)%0A%20%20%20%20%20%20%20%20gtot%20%3D%20grid.home%20%2B%20grid.away%0A%20%20%20%20%20%20%20%20return%20((gtot%20%3D%3D%20k)%20*%20A.to_numpy()).sum().sum()%0A%0A%20%20%20%20idx_tg%20%3D%20grid.away%5B%220%22%5D%0A%20%20%20%20idx_tg.index.name%20%3D%20%22TG%20line%22%0A%20%20%20%20pdf1%20%3D%20idx_tg.apply(lambda%20k%3A%20stats.poisson.pmf(k%3Dk%2C%20mu%3Dpoisson.lambda_home%20%2B%20poisson.lambda_away))%0A%20%20%20%20pdf2%20%3D%20idx_tg.apply(f_tg)%0A%0A%20%20%20%20_out%20%3D%20pd.concat(%5Bpdf1%2C%20pdf2%2C%20pdf1%20-%20pdf2%5D%2C%20keys%3D%5B%22grid%22%2C%20%22fpoisson%22%2C%20%22diff%22%5D%2C%20axis%3D1).iloc%5B%3A10%5D%0A%20%20%20%20print(_out)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%202.%20Asian%20Handicap%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(grid%2C%20pd%2C%20poisson%2C%20stats)%3A%0A%20%20%20%20def%20f_ah(k)%3A%0A%20%20%20%20%20%20%20%20A%20%3D%20poisson.pmf(data%3Dgrid)%0A%20%20%20%20%20%20%20%20gdiff%20%3D%20grid.home%20-%20grid.away%0A%20%20%20%20%20%20%20%20return%20((gdiff%20%3D%3D%20k)%20*%20A.to_numpy()).sum().sum()%0A%0A%20%20%20%20idx_ah%20%3D%20pd.Index((grid.home%20-%20grid.away).melt()%5B%22value%22%5D.unique()).to_series().sort_index(ascending%3DTrue)%0A%20%20%20%20idx_ah.index.name%20%3D%20%22AH%20line%22%0A%20%20%20%20pdf3%20%3D%20idx_ah.apply(lambda%20k%3A%20stats.skellam.pmf(k%2C%20poisson.lambda_home%2C%20poisson.lambda_away))%0A%20%20%20%20pdf4%20%3D%20idx_ah.apply(f_ah)%0A%20%20%20%20_out%20%3D%20pd.concat(%5Bpdf3%2C%20pdf4%2C%20pdf3%20-%20pdf4%5D%2C%20keys%3D%5B%22grid%22%2C%20%22fskellam%22%2C%20%22diff%22%5D%2C%20axis%3D1).loc%5B-4%3A4%5D%0A%20%20%20%20print(_out)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Plot%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(grid%2C%20plt%2C%20poisson%2C%20rf)%3A%0A%20%20%20%20dist_skellam%20%3D%20rf.Skellam(lambda_home%3Dpoisson.lambda_home%2C%20lambda_away%3Dpoisson.lambda_away)%0A%20%20%20%20dist_poisson%20%3D%20rf.Poisson(lambda_%3Dpoisson.lambda_home%20%2B%20poisson.lambda_away)%0A%0A%20%20%20%20_fig%2C%20axes%20%3D%20plt.subplots(1%2C%202%2C%20figsize%3D(10%2C%205))%0A%0A%20%20%20%20dist_poisson.plot(grid.home.iloc%5B0%2C%20%3A%5D%2C%20ax%3Daxes%5B0%5D)%0A%20%20%20%20dist_skellam.plot(grid%2C%20ax%3Daxes%5B1%5D)%0A%0A%20%20%20%20plt.show()%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
052d03048b1c0e554d820af995bfe376