{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"%matplotlib inline\n",
"%config InlineBackend.figure_format='svg'\n",
"import random\n",
"from numba import jit"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"继续关于用Monte Carlo模拟的方法来研究betting strategy的文章,这是第三篇,目标是对 Martingale 策略进一步做一些分析。\n",
"\n",
"前一篇的最后,分析了用 Martingale 策略当事先设定一个目标盈利时的表现,这相当于设置了「止盈位」,这一篇进一步看一下,如果再设置「止损位」会发生什么。\n",
"\n",
"与前面相同的一些东西就不再重复了:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Win rate: 0.488797 \tLoss rate: 0.511203\n"
]
}
],
"source": [
"@jit(nopython=True)\n",
"def win_dice(n=49):\n",
" roll = random.randint(1, 100)\n",
" return bool(roll <= n)\n",
"\n",
"wins = 0\n",
"losses = 0\n",
"for _ in range(1000000):\n",
" if win_dice():\n",
" wins += 1\n",
" else:\n",
" losses += 1\n",
"print('Win rate:', wins/1000000, '\\tLoss rate:', losses/1000000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"对上一篇写的带止盈的函数作少量修改,加入止损的考虑:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"@jit(nopython=True)\n",
"def martingale_leave_at_win_or_loss(funds, init_wager, wager_count, stop_profit, stop_loss):\n",
" # stop_profit, stop_loss 为绝对金额\n",
" # 返回了三种结果的状态和下注的次数,状态用数字表示 ,含义如下:\n",
" # 1 : 止盈,达到盈利目标,退出\n",
" # 0 : 直到最大下注数也没有发生上面情况(概率很低)\n",
" # -1 : 止损,亏损达到止损位,退出\n",
" balance = funds\n",
" wager = init_wager\n",
" vY = -funds * np.ones(wager_count) / 10\n",
"\n",
" win_previous = True\n",
" previous_wager_amount = init_wager\n",
" for i in range(wager_count):\n",
" if win_previous:\n",
" if win_dice():\n",
" pass\n",
" balance += wager\n",
" win_previous = True\n",
" else:\n",
" balance -= wager\n",
" win_previous = False\n",
" else:\n",
" wager = min(previous_wager_amount * 2, balance - (funds-stop_loss))\n",
" if win_dice():\n",
" balance += wager\n",
" win_previous = True\n",
" wager = init_wager\n",
" else:\n",
" balance -= wager\n",
" win_previous = False\n",
" previous_wager_amount = wager\n",
" vY[i] = balance\n",
" if balance >= funds + stop_profit:\n",
" return 1, i+1, vY[:i+1]\n",
" elif balance <= funds - stop_loss:\n",
" return -1, i+1, vY[:i+1]\n",
"\n",
" return 0, i+1, vY"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"这个函数有一点改进,把 balance 的序列,即 vY 也返回了,避免了在函数体中画图,使得整个函数可以用 nopython 模式编译。测试一下速度:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10000 loops, best of 3: 32.1 µs per loop\n"
]
}
],
"source": [
"stat, i, balance = martingale_leave_at_win_or_loss(100, 1, 10000, 25, 20)\n",
"\n",
"%timeit stat, i, balance = martingale_leave_at_win_or_loss(100, 1, 10000, 15, 10)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"测试一下上面的程序是不是有错误"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4 8\n"
]
},
{
"data": {
"image/svg+xml": [
"\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"n_win = 0\n",
"n_loss = 0\n",
"for _ in range(12):\n",
" stat, i, balance = martingale_leave_at_win_or_loss(100, 1, 10000, 25, 20)\n",
" if stat == -1:\n",
" n_loss += 1\n",
" elif stat == 1:\n",
" n_win += 1\n",
" plt.plot(balance)\n",
" plt.axhline(y=125, ls='dotted')\n",
" plt.axhline(y=80, ls='dotted')\n",
"print(n_win, n_loss)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"看起来结果似乎是正确的。再做一个验证,如果把止损或止赢设为1,这样就只能赌一次,理论上胜率是跟掷骰子的概率是相同的:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Win percentage is 49.01, loss percentage is 50.99\n"
]
}
],
"source": [
"n_win = 0\n",
"n_loss = 0\n",
"n_sim = 1000000\n",
"stop_profit = 1\n",
"stop_loss = 1\n",
"\n",
"for _ in range(n_sim):\n",
" stat, i, _ = martingale_leave_at_win_or_loss(1000, 1, 10000, stop_profit, stop_loss)\n",
" if stat == -1:\n",
" n_loss += 1\n",
" elif stat == 1:\n",
" n_win += 1\n",
" else:\n",
" print('What are the odds?!')\n",
"print('Win percentage is {:.2f}, loss percentage is {:.2f}'.format(n_win/n_sim*100, n_loss/n_sim*100))"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"从结果来看确实如此,暂时认为上面的结果没有问题。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"下面作一些更定量的分析:\n",
"\n",
"1. 验证是不是任何止盈止损组合下期望值都是负的\n",
"2. 研究给定止盈线上最佳止损线是多少\n",
"\n",
"为了限制参数的个数,把初始资金固定为1000,初始赌注固定为1。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"先看第一点,验证**是不是任何止盈止损组合下期望值都是负的**:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Stop-profit 10, stop-loss 5. Expectation is -0.39 (30.71 69.29)\n",
"Stop-profit 10, stop-loss 20. Expectation is -0.98 (63.40 36.60)\n",
"Stop-profit 10, stop-loss 30. Expectation is -1.07 (72.31 27.69)\n",
"Stop-profit 50, stop-loss 25. Expectation is -3.42 (28.78 71.22)\n",
"Stop-profit 50, stop-loss 50. Expectation is -5.15 (44.85 55.15)\n",
"Stop-profit 50, stop-loss 100. Expectation is -6.63 (62.25 37.75)\n",
"Stop-profit 200, stop-loss 100. Expectation is -17.51 (27.50 72.50)\n",
"Stop-profit 200, stop-loss 200. Expectation is -25.13 (43.72 56.28)\n",
"Stop-profit 200, stop-loss 500. Expectation is -39.04 (65.85 34.15)\n"
]
}
],
"source": [
"n_sim = int(1e5)\n",
"params = [[10, 5], [10, 20], [10,30], # low stop_profit\n",
" [50, 25], [50, 50], [50, 100], # normal stop_profit\n",
" [200, 100], [200, 200], [200, 500]]\n",
"\n",
"for param in params:\n",
" n_win = 0\n",
" n_loss = 0\n",
" for _ in range(n_sim):\n",
" stat, i, _ = martingale_leave_at_win_or_loss(1000, 1, 10000, param[0], param[1])\n",
" if stat == -1:\n",
" n_loss += 1\n",
" elif stat == 1:\n",
" n_win += 1\n",
" else:\n",
" print('What are the odds?!')\n",
" print('Stop-profit {}, stop-loss {}. Expectation is {:.2f} ({:.2f} {:.2f})'.format(param[0], param[1],\n",
" n_win/n_sim*param[0] - n_loss/n_sim*param[1],\n",
" n_win/n_sim*100, n_loss/n_sim*100))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"上面模拟了9组不同止盈和止损位置的参数组合,涵盖了高低止盈和止损不同范围,所有的期望值都为负。\n",
"\n",
"另外,也注意到对相同的止盈位,设置不同的止损位期望值也不同,而且似乎止损位设得越高(允许的损失越小),期望值越高,看看是不是这样:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"50 10. 50 20. 50 50. 50 100. 50 200. 50 500. 100 10. 100 20. 100 50. 100 100. 100 200. 100 500. 200 10. 200 20. 200 50. 200 100. 200 200. 200 500. "
]
},
{
"data": {
"text/plain": [
""
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/svg+xml": [
"\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"n_sim = int(1e5)\n",
"stop_profits = [50, 100, 200]\n",
"stop_losses = [10, 20, 50, 100, 200, 500]\n",
"\n",
"for stop_profit in stop_profits:\n",
" expectations = []\n",
" for stop_loss in stop_losses:\n",
" print(stop_profit, stop_loss, end='. ')\n",
" n_win = 0\n",
" n_loss = 0\n",
" for _ in range(n_sim):\n",
" stat, i, _ = martingale_leave_at_win_or_loss(1000, 1, 10000, stop_profit, stop_loss)\n",
" if stat == -1:\n",
" n_loss += 1\n",
" elif stat == 1:\n",
" n_win += 1\n",
" else:\n",
" print('What are the odds?!')\n",
" expectations.append(n_win/n_sim*stop_profit - n_loss/n_sim*stop_loss)\n",
" plt.plot(stop_losses, expectations, 'o-', label=str(stop_profit))\n",
"plt.legend(loc=0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"从这张图来看,结论还是很明显的,把止损位设得越高,最终期望值也会越高(尽管不管怎么努力都是负的)。\n",
"\n",
"稍微想一下,似乎可以这样理解:在止盈位固定时,止损位设得越低,预期的下注次数就越多,也就是留在游戏中的时间更长,输的概率就越大。\n",
"\n",
"当然其实参照前面验证期望值的那部分输出的结果,这样的理解原则上没有问题,但细节上其实是错的:因为止损位设得更低时,碰到止损位的概率其实也会降低,反而赢的概率会更高,但倒回来,尽管输的概率更低了,但概率上的降低并不足以抹平损失额的增长,总期望值还是降低的。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"最后一点比较感兴趣的是,上一张图比较的是期望值的绝对值,而显然止盈位越高,输的也可能会更多,如果比较相对的期望值呢:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"50 10. 50 20. 50 50. 50 100. 50 200. 50 500. 100 10. 100 20. 100 50. 100 100. 100 200. 100 500. 200 10. 200 20. 200 50. 200 100. 200 200. 200 500. "
]
},
{
"data": {
"text/plain": [
""
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/svg+xml": [
"\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"n_sim = int(1e5)\n",
"stop_profits = [50, 100, 200]\n",
"stop_losses = [10, 20, 50, 100, 200, 500]\n",
"\n",
"for stop_profit in stop_profits:\n",
" expectations = []\n",
" for stop_loss in stop_losses:\n",
" print(stop_profit, stop_loss, end='. ')\n",
" n_win = 0\n",
" n_loss = 0\n",
" for _ in range(n_sim):\n",
" stat, i, _ = martingale_leave_at_win_or_loss(1000, 1, 10000, stop_profit, stop_loss)\n",
" if stat == -1:\n",
" n_loss += 1\n",
" elif stat == 1:\n",
" n_win += 1\n",
" else:\n",
" print('What are the odds?!')\n",
" expectations.append((n_win/n_sim*stop_profit - n_loss/n_sim*stop_loss) / stop_profit)\n",
" plt.plot(stop_losses, expectations, 'o-', label=str(stop_profit))\n",
"plt.legend(loc=0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"从相对期望值来看,规律大致是倒过来的,当把止盈位设高时,相对的期望值也会更高一点。\n",
"\n",
"前面的讨论都在看期望值,其实是有点偏离了「赌博」的本质了,毕竟这种游戏本质上肯定是输的。「赌博」有几种典型的心态:\n",
"\n",
"1. 我有1000元,我想赌10把,赚到10%(100元)就走;\n",
"2. 我有1000元,我想玩10把,如果亏了10%就不玩了;\n",
"3. 我有1000元,我想一直玩,一直到赚了200或亏了100为止;\n",
"\n",
"借助上面的工具,可以分别模拟这几种情况能达到目标的概率,这里就不再展开了。"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}