Jupyter Notebookの練習として、各種賭け戦略のシミュレーションを行います。前回はシミュレーション結果を折れ線グラフで見ました。今回は、100ラウンドの結果をヒストグラムと統計値で見てみます。
賭けのルール
- サイコロの奇数・偶数を当てる
- 好きな額を事前に賭けられる
- 当たれば、賭けた額×2が払い戻される
- 外れたら、賭けた額は没収
- 借金禁止
- 開始資金は10000コイン
- 1000回賭けられる
- 各回で、全参加者は同じ側に賭ける(当たるタイミングや回数が同一)
本記事では、勝率を約50%、52%、48%の3パターンでシミュレーションします。勝率にブレが生じないよう前回からコードを改良しています。
戦略の説明
- 定額:必ず一定額を賭けます
- 定率:持ち金の一定割合を賭けます
マーチンゲール法
ある額から賭け始め、負けるたび倍に増やします(負けを取り戻せるように賭けます)。勝って負けが取り戻せたら、額に戻します。
必勝法として有名です。負けが続いた場合、たちまち賭け金が増えていくため、実際は次に示すような形で打ち切りとなることが多いでしょう。
- 手持ち資金が尽きて、賭けられなくなる
- 賭場の賭け上限にかかり、倍額を賭けられなくなる
結果(勝率約50%)
マーチンゲール(10開始) | マーチンゲール(2開始) | 定率(1%) | 定率(3%) | 定額(100) | 定額(300) | |
---|---|---|---|---|---|---|
mean | 9890.8 | 9564.4 | 9512.6 | 6380.3 | 10000.0 | 8697.0 |
std | 7134.8 | 3715.9 | 9.0 | 9.3 | 0.0 | 3378.9 |
min | 0.0 | 0.0 | 9490.0 | 6355.0 | 10000.0 | 0.0 |
25% | 0.0 | 10994.0 | 9506.8 | 6375.0 | 10000.0 | 10000.0 |
50% | 14970.0 | 10998.0 | 9512.0 | 6380.0 | 10000.0 | 10000.0 |
75% | 15000.0 | 11000.0 | 9517.0 | 6386.0 | 10000.0 | 10000.0 |
max | 15000.0 | 11000.0 | 9544.0 | 6402.0 | 10000.0 | 10000.0 |
結果(勝率約48%)
マーチンゲール(10開始) | マーチンゲール(2開始) | 定率(1%) | 定率(3%) | 定額(100) | 定額(300) | |
---|---|---|---|---|---|---|
mean | 7039.8 | 8981.7 | 6394.8 | 1933.8 | 6000.0 | 0.0 |
std | 7385.7 | 4229.4 | 7.7 | 6.5 | 0.0 | 0.0 |
min | 0.0 | 0.0 | 6376.0 | 1919.0 | 6000.0 | 0.0 |
25% | 0.0 | 10946.0 | 6390.0 | 1931.0 | 6000.0 | 0.0 |
50% | 0.0 | 10958.0 | 6394.0 | 1934.0 | 6000.0 | 0.0 |
75% | 14790.0 | 10960.0 | 6400.0 | 1937.0 | 6000.0 | 0.0 |
max | 14800.0 | 10960.0 | 6417.0 | 1955.0 | 6000.0 | 0.0 |
結果(勝率約52%)
マーチンゲール(10開始) | マーチンゲール(2開始) | 定率(1%) | 定率(3%) | 定額(100) | 定額(300) | |
---|---|---|---|---|---|---|
mean | 11390.4 | 10151.3 | 14172.2 | 21152.8 | 14000.0 | 22000.0 |
std | 6609.4 | 3008.7 | 12.5 | 17.0 | 0.0 | 0.0 |
min | 0.0 | 0.0 | 14145.0 | 21106.0 | 14000.0 | 22000.0 |
25% | 11347.5 | 11034.0 | 14163.0 | 21141.0 | 14000.0 | 22000.0 |
50% | 15190.0 | 11038.0 | 14171.0 | 21151.5 | 14000.0 | 22000.0 |
75% | 15200.0 | 11040.0 | 14180.2 | 21163.5 | 14000.0 | 22000.0 |
max | 15200.0 | 11040.0 | 14205.0 | 21206.0 | 14000.0 | 22000.0 |
実験コード
賭け戦略シミュレーション2.py
# %%
# 定義
import math
import typing # noqa: F401
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 戦略スーパークラス
class Storategy:
def __init__(self, name, coin): # type: (str, int) -> None
self.name = name
self.coin = coin
self.next_bet = 0
def bet(self): # type: () -> int
ret = self.next_bet
self.coin -= ret
return ret
def refound(self, coin): # type: (int) -> int
self.coin += coin
self.update(coin)
return self.coin
def update(self, coin): # type: (int) -> None
pass
# 定額
class FixedAmount(Storategy):
def __init__(self, name, coin, first_bet): # type: (str, int, int) -> None
super().__init__(name, coin)
self.first_bet = first_bet
self.update(coin)
def update(self, coin): # type: (int) -> None
self.next_bet = min(self.first_bet, self.coin)
# 定率
class FixedRate(Storategy):
def __init__(self, name, coin, rate): # type: (str, int, float) -> None
super().__init__(name, coin)
self.rate = rate
self.update(coin)
def update(self, coin): # type: (int) -> None
self.next_bet = math.floor(self.coin * self.rate)
# マーチンゲール法(負けたら倍賭け)
class Martingale(Storategy):
def __init__(self, name, coin, first_bet): # type: (str, int, int) -> None
super().__init__(name, coin)
self.first_bet = first_bet
self.debt = 0
self.update(coin)
def update(self, coin): # type: (int) -> None
if coin == 0:
self.next_bet *= 2
else:
self.next_bet = self.first_bet + self.debt
self.debt = 0
if self.next_bet > self.coin:
self.debt = self.next_bet - self.coin
self.next_bet = self.coin
# 勝敗(払い戻し率)リスト生成
def winning_list(winning_persentage, odds,
k): # type: (float, float, int) -> np.ndarray[float]
win = int(k * winning_persentage)
ary = np.concatenate((np.full(win, odds),
np.zeros(k - win))) # type: np.ndarray[float]
np.random.shuffle(ary)
return ary
# シミュレーション実施
def simulate(start_coin, num_rounds, winning_persentage, odds,
k): # type: (int, int, float, float, int) -> pd.DataFrame
np.random.seed(1) # 再現可能にする
df = pd.DataFrame(columns=['name', 'round', 'count', 'coin'])
for round in range(num_rounds):
strategies = (
FixedAmount('定額(300)', start_coin, 300),
FixedAmount('定額(100)', start_coin, 100),
FixedRate('定率(3%)', start_coin, 0.03),
FixedRate('定率(1%)', start_coin, 0.01),
Martingale('マーチンゲール(10開始)', start_coin, 10),
Martingale('マーチンゲール(2開始)', start_coin, 2),
) # type: typing.Sequence[Storategy]
wl = winning_list(winning_persentage, odds, k)
for strategy in strategies:
history = [strategy.coin]
for result_odds in wl:
bet_coin = strategy.bet()
refound_coin = bet_coin * result_odds
strategy.refound(refound_coin)
history.append(strategy.coin)
df_one = pd.DataFrame([
np.full(len(history), strategy.name),
np.full(len(history), round),
np.arange(len(history)), history
]).T
df_one.columns = df.columns
df = df.append(df_one)
return df.astype({'round': int, 'count': int, 'coin': int})
def save_plot(df, fname,
save_rounds): # type: (pd.DataFrame, str, int) -> None
plt.style.use('ggplot')
fig = plt.figure(figsize=(16, 6 * math.ceil(save_rounds / 2)))
for round in range(save_rounds):
ax = fig.add_subplot(math.ceil(save_rounds / 2), 2, round + 1)
for name in df['name'].unique():
df_one = df[(df['name'] == name) & (df['round'] == round)]
ax.plot(df_one['count'], df_one['coin'], label=name)
ax.legend()
ax.set_title(f'Round {round + 1}')
fig.savefig(fname, bbox_inches='tight')
fig.show()
def save_hist(df, fname): # type: (pd.DataFrame, str) -> None
plt.style.use('ggplot')
storategy_names = df['name'].unique()
num_charts = len(storategy_names)
k = df['count'].max()
fig = plt.figure(figsize=(16, 6 * math.ceil(num_charts / 2)))
for i, storategy_name in enumerate(storategy_names, 1):
ax = fig.add_subplot(math.ceil(num_charts / 2), 2, i)
df_one = df[(df['name'] == storategy_name) & (df['count'] == k)]
ax.hist(df_one['coin'], bins=20)
ax.set_title(storategy_name)
fig.savefig(fname, bbox_inches='tight')
fig.show()
# %%
# 50%
# シミュレート
num_rounds = 100
k = 1000
per = 50
df = simulate(
start_coin=10000,
num_rounds=num_rounds,
winning_persentage=per/100,
odds=2,
k=k)
# 保存
save_plot(df, f'{per}-plot.png', 4)
save_hist(df, f'{per}-hist.png')
df[(df['count'] == k)].groupby('name')[(
'name', 'coin')].describe().round(1).T.to_csv(f'{per}.csv')
# %%
# 48%
# シミュレート
num_rounds = 100
k = 1000
per = 48
df = simulate(
start_coin=10000,
num_rounds=num_rounds,
winning_persentage=per/100,
odds=2,
k=k)
# 保存
save_plot(df, f'{per}-plot.png', 4)
save_hist(df, f'{per}-hist.png')
df[(df['count'] == k)].groupby('name')[(
'name', 'coin')].describe().round(1).T.to_csv(f'{per}.csv')
# %%
# 52%
# シミュレート
num_rounds = 100
k = 1000
per = 52
df = simulate(
start_coin=10000,
num_rounds=num_rounds,
winning_persentage=per/100,
odds=2,
k=k)
# 保存
save_plot(df, f'{per}-plot.png', 4)
save_hist(df, f'{per}-hist.png')
df[(df['count'] == k)].groupby('name')[(
'name', 'coin')].describe().round(1).T.to_csv(f'{per}.csv')
ToDo(今後のアイデアなど)
- ケリー基準と比較してみる
- トレンド&ランダムウォークの推移シミュレーション
- ポートフォリオ式/ドルコスト平均法(定額購入)/定数購入での比較
- ヒストグラムのスケールを統一したい
- シミュレーションの高速化