6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

賭け戦略シミュレーション2(定額・定率・マーチンゲール法)

Last updated at Posted at 2018-01-21

Jupyter Notebookの練習として、各種賭け戦略のシミュレーションを行います。前回はシミュレーション結果を折れ線グラフで見ました。今回は、100ラウンドの結果をヒストグラムと統計値で見てみます。

賭けのルール

  • サイコロの奇数・偶数を当てる
  • 好きな額を事前に賭けられる
  • 当たれば、賭けた額×2が払い戻される
  • 外れたら、賭けた額は没収
  • 借金禁止
  • 開始資金は10000コイン
  • 1000回賭けられる
  • 各回で、全参加者は同じ側に賭ける(当たるタイミングや回数が同一)

本記事では、勝率を約50%、52%、48%の3パターンでシミュレーションします。勝率にブレが生じないよう前回からコードを改良しています。

戦略の説明

  • 定額:必ず一定額を賭けます
  • 定率:持ち金の一定割合を賭けます

マーチンゲール法

ある額から賭け始め、負けるたび倍に増やします(負けを取り戻せるように賭けます)。勝って負けが取り戻せたら、額に戻します。
必勝法として有名です。負けが続いた場合、たちまち賭け金が増えていくため、実際は次に示すような形で打ち切りとなることが多いでしょう。

  • 手持ち資金が尽きて、賭けられなくなる
  • 賭場の賭け上限にかかり、倍額を賭けられなくなる

結果(勝率約50%)

50-plot.png
50-hist.png

マーチンゲール(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%)

48-plot.png
48-hist.png

マーチンゲール(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%)

52-plot.png
52-hist.png

マーチンゲール(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(今後のアイデアなど)

  • ケリー基準と比較してみる
  • トレンド&ランダムウォークの推移シミュレーション
    • ポートフォリオ式/ドルコスト平均法(定額購入)/定数購入での比較
  • ヒストグラムのスケールを統一したい
  • シミュレーションの高速化
6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?