公平博弈必输策略及Python改进
作者:互联网
公平博弈必输策略及Python改进
前言
考虑一个场景:一名投机人每次拿出当前本金的10%进行抛硬币测试(公平博弈,输赢均50%),一共测试60次,最终输赢各30次,那么他的本金将是多少?
计算:x = 1.1^30 * 0.9^30 * 100% = 0.99^30 * 100% =73.97%
这就带来了一个问题:一个公平博弈,如果策略不妥,那么长期下来有可能是必输。
注:该问题,与“一支股票经历10%涨停30次,10%跌停30次,最终价格如何”,实质是一样的。
一、算术原因
从算术角度来说,胜率必须略高于52.5%,长期才可以不输。以1000次为例:
1.
1
525
∗
0.
9
475
=
1.
1
50
∗
0.9
9
475
=
0.9916
≈
1
1.1^{525} * 0.9^{475} = 1.1^{50} * 0.99^{475} = 0.9916 \approx 1
1.1525∗0.9475=1.150∗0.99475=0.9916≈1
但是,根据大数理论,硬币实验次数越多,概率越趋近50%。当实验次数足够多时,胜率略高于52.5%的可能性变得微乎其微,因此就必输了。
二、逻辑原因
从逻辑角度来说,落后之时减少投入,而领先之时增大投入,是不妥的。以起始10000为例:
1)先输后赢,则10000输1000,变成9000,再赢900,变为9900
2)先赢后输,则10000赢1000,变成11000,再输1100,变为9900
只有反过来,落后时增大投入,而领先时减少投入,才是合理的。比如:
1)先输后赢,10000变9000,增加投入1100,赢后变10100
2)先赢后输,10000变11000,减少投入900,输后变10100
然而,这种策略也存在两个问题:一是有点类似倍投法,假如一开始连输几次,有输光的风险;二是领先之时,资金大了,而每次投入要变少,资金利用率降低了。
三、Python改进
事实上,“每次拿出当前本金10%”的方案,不如“固定金额”方案。套用在购买基金的场景,前者类似“固定份额”定投方案,后者是“固定金额”定投方案。
现在试一试改进方案:
1)以某天净值x0为基准
2)当净值突破至下一个基准:上涨为x0的2倍,下跌为x0的一半,切换基准
3)当上一次操作之后,净值变化超过3%,则再次操作:如下跌,则补仓(倍数见下),如上涨且持有,则减仓(倍数见下,不足则清完为止)
4)当净值在[1, 2)倍x0时,倍数为1倍;净值在[0.9, 1)倍x0时,倍数为2倍;…;净值在[0.5, 0.6)倍x0时,倍数为6倍。但是,如果当前持仓过多(大于该倍数的2倍),那么补仓倍数仅为1倍(减仓倍数不影响)
以下是该方案的Python源码,在Jupyter notebook里面分三段:
import requests
import time
import execjs
fileTest = './data/accTest.csv'
jjTest = '001630'
def getUrl(fscode):
head = 'http://fund.eastmoney.com/pingzhongdata/'
tail = '.js?v='+ time.strftime("%Y%m%d%H%M%S",time.localtime())
return head+fscode+tail
# 根据基金代码获取净值
def getWorth(fscode):
content = requests.get(getUrl(fscode))
jsContent = execjs.compile(content.text)
name = jsContent.eval('fS_name')
code = jsContent.eval('fS_code')
#单位净值走势
netWorthTrend = jsContent.eval('Data_netWorthTrend')
#累计净值走势
ACWorthTrend = jsContent.eval('Data_ACWorthTrend')
netWorth = []
ACWorth = []
for dayWorth in netWorthTrend:
netWorth.append(dayWorth['y'])
for dayACWorth in ACWorthTrend:
ACWorth.append(dayACWorth[1])
return netWorth, ACWorth
ACWorthTestFile = open(fileTest, 'w')
_, ACWorth = getWorth(jjTest)
if len(ACWorth) > 0:
ACWorthTestFile.write(",".join(list(map(str, ACWorth))))
ACWorthTestFile.write("\n")
print('{} data downloaded'.format(jjTest))
ACWorthTestFile.close()
第一段如上,在天天基金网爬基金。001630是天弘中证计算机主题ETF联接C,有两个特点:
1)在支付宝基金里面,买入0费率,持有7天卖出0费率
2)该基金2015年7月成立的,至今(2021年2月)5年多,成立以来累计涨跌幅约-10%(目前基金净值约0.9元),10多亿大小
import csv
with open(fileTest) as f:
row = csv.reader(f, delimiter=',')
for r in row:
#去掉记录为None的数据(当天数据缺失)
r = [float(x) for x in r if x != 'None']
show_days = 1500 #仅用最近的show_days天数(1年约250交易日)
if len(r) > show_days:
r = r[len(r)-show_days:]
x0 = r[0] #当前基准
x1 = x0 #已存净值
x2 = x0 #上次操作时净值
a = 10000 #标准份额(1倍)
y = 0 #当前持有倍数
z = 0 #累计盈利(含浮盈浮亏)
s = [0] #历史盈利率(不乘以标准份额,即z/a)
t = [0] #历史倍数乘以0.1(即0.1y,乘以0.1是为了图形显示)
for i in range(1,len(r)):
x3 = r[i]
#刷新浮盈浮亏
if y > 0:
z += a * y * (x3 - x1)
s.append(z/a)
#净值变化超过基准
if (x3 >= x0 * 2) or (x3 < x0 / 2):
#下跌,则买入1倍份额
if x3 < x2:
y += 1
#反之,如有则卖出1倍份额
elif y > 0:
y -= 1
#刷新
x0 = x3
x1 = x3
x2 = x3
#上一次操作至今,净值变化超过3%
elif (x3 > x2 * 1.03) or (x3 < x2 * 0.97):
if x3 >= x0:
# [x0, 2x0)采用1倍
i = 1
else:
i = 2 + int(10 * (1 - x3 / x0))
#但如果当前持仓过多,则补仓仅采用1倍(减仓不受影响)
if (y > 2 * i) and (x3 < x2):
i = 1
#下跌,则买入i倍份额
if x3 < x2:
y += i
#反之,如持有则卖出i倍份额
else:
if y > i:
y -= i
else:
y = 0
#刷新
x1 = x3
x2 = x3
else:
x1 = x3
t.append(y * 0.1)
print('累计盈亏为:{:.0f}'.format(z))
第二段就是之前说的“改进方案”的实现。注意:当持仓过多而补仓时,以及基准切换时,倍数都用1。
该基金成立至今5年半,近1400交易日(show_days=1500亦即全部显示)。
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif']='SimHei'
plt.rcParams['axes.unicode_minus']=False
r_plot = np.array(r).reshape(-1, 1)
s_plot = np.array(s).reshape(-1, 1)
t_plot = np.array(t).reshape(-1, 1)
# 图表显示
fig=plt.figure(figsize=(15,6))
plt.plot(r_plot, color='blue', label='基金净值')
plt.plot(s_plot, color='red', label='盈亏情况')
plt.plot(t_plot, color='yellow', label='持仓情况')
plt.legend(loc='upper left')
plt.show()
第三段代码就是画图了,贴图如下:
蓝色线就是001630的净值。看似很平,1元上下,其实最低值是0.485元,最高值是1.0826元。
黄色线是持仓倍数情况,红色线是盈亏情况。在最惨的时候(净值0.5元左右),倍数近20倍(黄纵坐标要乘以10),浮盈浮亏约-1.5(假定1万份为1倍,即浮亏1.5万);目前净值0.9元左右,浮盈浮亏为5(同理,浮盈5万)。
如果把show_days改设为250,亦即查看近1年的情况,则图片如下所示:
近一年最高持仓倍数为6,当前浮盈7千多(1万份1倍)。
注意:因为001630持有7天卖出0费率,且基金是按照先进先出原则卖出的,因此本文忽略了持仓不足7天而卖出的惩罚费率情况。如实战,则自行注意之。
总结
本文针对特色基金001630(基本面尚可,人气较旺,表现较差),提出了一种“温和”补仓的策略------落后时略增加倍数。当然,其它基金也是可以的,试过10多个基金(都是7天0费率的)。
关于基金补仓减仓,凭感觉,两种策略都是可行的:一种即是本文的“上次操作后变化超3%”,另一种是“连涨连跌几天”。或者两者结合:5天当中4-5天涨,且涨幅超3%;5天当中4-5天跌,且跌幅超3%。采用5天是比较好的(5个交易日必满足持有7天)。这些策略我也在测试中,大同小异,不另贴了。
有一种直觉,现在“韭菜”不喜欢买股票,而喜欢买基金了。尤其是女白领。于是,有些光棍跑到基金板块留言“交友启事”,蛮有趣。
标签:plot,plt,博弈,Python,净值,倍数,x3,x0,必输 来源: https://blog.csdn.net/fanmin2000/article/details/113392512