小菜狗第一次参加美赛,所以几乎没有对 在较短时间内对不熟悉的算法进行实现 的能力,所以3天从早到晚的敲键盘过程,有一般都是对着类似于下图的控制台报错一点点debug过来的。
(我们队选的是C题,主要原因是大家都不太会搜集数据,所以A/B题无从下手)
咳咳,说正经的,个人对于这次的经验总结如下:
现学现用占主要地位。对于比赛且限时的类型而言,真的很考验临时学习的能力,不过对于我而言,主要是了解一下算法的原理,优缺点,适用情景,遇到了不会的,直接CSDN,或者百度一下
python实现的配置。实现的话,一般是交给sklearn完成,我之所以选择了py就是其丰富的现成库以及方便安装的特点,使用起来真的很方便,同时配合pycharm调试(个人觉得pycharm调试py的效果比vs好很多),skilearn给出的很多机器学习类十分方便,直接拿来用(只需要知道类的使用方法就好)
数据的存储与使用。py的两种数据存储方式对应的使用要清楚、且统一。pandas对应dataframe、而numpy对应array(这两个一旦使用方式混淆,就很容易招来一堆报错);数据被存储后下标和序号很容易不清楚,一定要提前约定好,免得出现越界访问
呼应第一点,也是最重要的一点,就算是现学现用,比如把报错复制,粘贴百度,也得在解决后用心记住,或者做个临时笔记,并且有条理、有层次地记下来(我是因为这次美赛时间太紧张了,记得到处都是),不要总在一个地方摔无数次坑(虽然摔了很多次后我确实也逐渐长记性了2333)。比如dataframe访问元素是用[列名] [行数],而array才是对应像数组一样,下标访问
首先是整个C题我们使用的算法的概括(考虑的真的没多少,就自己记录一下而已)
核心问题是预测部分
我们一开始想尝试时间序列算法预测,然后去试着跑了一下数据,不知道是不是数据的时间范围没设置好,用那个搞出来没法拟合,不过也给出一些推荐的博客以供参考
http://t.csdn.cn/Kkw5s python实现ARIMA时序分析
http://t.csdn.cn/NXwRc 对于拖尾和截尾的分析
在暂时看不到ARIMA的希望后,我直接使用了比较朴素的SVR(仍然使用sklearn实现),支持向量回归去预测,大概是把自变量设置成了天数,一开始发现50天的拟合效果比较烂
然后在加入了每日单价增长率以作为自变量的第二维度后,并且改少的天数,预测效果就比较可观了(当然这个存在延迟,无法对价格波动有一个好的预测,只能看趋势)
然后是风险评估这里
比较尴尬的是,虽然后面强行做出来了一个风险的预估,但是没能拿去用(因为当时自己整合成函数后因为传参的类型,就是dataframe和array又混淆了,加上下标也不一致,就一直报错,着急没改)(说白了就是在分开写这些功能时没用统一的数据存储方式,没把原csv文件的行列规定好,一直在改,就导致最后整合不到一起去TVT以后引以为戒)
总体上用的EWMA求的VaR值来评估风险,具体的公式可以参看b站matlab官方,跟着官方matlab代码试着改成了py的,但是对于那个窗口期个人不是很理解啥意思(扶额)
【MATLAB官方】运用 MATLAB 进行 VaR 值的计算和回测_哔哩哔哩_bilibili b站官方
http://t.csdn.cn/fL6ai 用scipy求正态分布逆函数的理解(包括了概率密度,分布函数的解释,属于是重新复习概统了)
想要确认是不是用对了函数,可以去用
norn.ppf(0,1)
来求标准正态分布的相关值,并且去网上查表,核对自己是否做出了想要的结果http://t.csdn.cn/JWIsg
http://t.csdn.cn/5O6ch
http://t.csdn.cn/mFl0k
http://t.csdn.cn/UNMVk
跟着那个风险评估的结果画了一波var图(最后一个)
接着是决策,也是个人觉得自己做得最拉的部分
直接利用网上RSI指标,先求得当天对应的RSI值,然后按照现有结论,也就是RSI在什么区间,达到什么阈值,可以对应买入/卖出,黄金和比特币各设置地不同,一点点调阈值,寻找一些可行的结果,也跟着放一波参考链接
【技术分析新手入门系列】技术指标 MA MACD RSI KDJ BOLL 布林线 趋势线 应用讲解_哔哩哔哩_bilibili 网上给出的一些投资指标参数的通俗解释,个人觉得挺有用的
(tips:后来队友有查到说RSI提示买入的时间可能是50左右的中间值,而不是较小值,这个体现在后面的代码中了)
由于这道题不像b题那样有宏观的地理背景,所以在可视化上主要是线条图像,或者一些表格,看上去略微单调。。。不过也省事,直接使用pyplot.plot硬核绘图,效果还挺好
以下几个小点可以参考
设置坐标轴名称、图像颜色、scatter(之前有去了解一下,可以看看)的相关设置
(1)scatter的相关参数
s:画的点的大小(可以是数组,就点的大小不一样呗)
c和cmap:c可以表示标签,cmap直接=plt.cm.Spectral就可以做到不同标签画出不同的色
alpha:改变点的透明度,可以变好看,比如alpha = 0.4
精致绘图技巧:把边缘和点的填充色设置为一样的,再加上alpha让中心透明度增加√
推荐文章:Python-画图(散点图scatter、保存savefig)及颜色大全 - dqi1999 - 博客园 设置颜色可以参考的,好的颜色也是一个亮点
Python中scatter函数参数详解_AnneQiQi的博客-CSDN博客_scatter函数
python - 如何在matplotlib中使用不同的edgecolor进行散点图? - Thinbug
plot的相关设置,这个感觉是具体看啥具体搜,不多给了
http://t.csdn.cn/YnmGt
多图绘制
是一个很重要的点,可以增加图像的美观性
http://t.csdn.cn/yLPdm
10分钟教你用Python中的Matplotlib绘制多图并合并展示 - 短短的路走走停停 - 博客园 这个力荐,很不错
地图的可视化(就是直接用pyechart作图,因为选题没能实现的
(1)安装pyechart
这个有v0.50.0和v1的两个版本,一般直接在cmd输入
pip install pyechart
即可安装后者 的最新版(2)在导入模块时,用
import pyechart.chart
才行,否则会报错滴(3)xlrd新版本不支持打开xlsx,所以一开始就最好
pip install xlrd==1.2.0
,只有旧版才支持Map函数
(1)add基本属性
add( series_name = "图例名称", '''=以及其左边可以不用写,按位置顺序传参'''data_pair = data '''同上,是非缺省参,这个是一个二维数组,由[[地理位置名,对应区域的值],[],[],..]组成,值 指的是一个你自己可以给定的''' maptype = "地图的类型" '''可以自己选择是什么区域的,比如画中国地图为china''')(1.5)add其他实用属性
is_roam
:表示是否可用鼠标滚轮缩放查看
s_map_symbol_show
:表示地图中各个小区域中央自带的点显不显示
layout_size=2000,layout_center=['50%','40%']
这两个只决定初始的大小,而不是能显示的范围(2)set_series_opts属性
label_opts
用于设置是否显示各个区域的名称.set_series_opts(label_opts = opts.LabelOpts(is_show=False))参考:官方文档yyds地理图表 - pyecharts - A Python Echarts Plotting Library built with love.
其他的好像又弃坑了(扶额),一会也贴个示例代码
先说一下数据预处理,一开始数据是有缺失的,在黄金那里,有几天的价格为空,所以我们选择用前后两天的平均数来处理
对于处理缺失数据,这里提供几个参考链接
【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili P21这个可以看看,用pandas来处理
http://t.csdn.cn/RZKW5 CSDN的官文也有参考意义
接着就是这些让人头大的两种存储类型
pandas可以有专门读取csv文件的函数,一般读取后习惯将其转换为dataframe格式,因为列一般是有意义的,而并非单纯的下标,对它的访问就得用
data['列名'][下标]
,而为了进行一些矩阵操作,比如合并矩阵之类的,就应转化为array,而那个的访问方式就和数组一样(个人目前是了解的这么多),对于一些常见的这两种类型的操作,我也贴一些链接dataframe
切片:http://t.csdn.cn/5vWIQ
获取行列数:http://t.csdn.cn/JgSEx
删除某行/列:http://t.csdn.cn/aRq4F
访问元素:http://t.csdn.cn/REoJk
array
获取行列数:http://t.csdn.cn/REoJk
操作矩阵,平方:
yy = np.power(yy,2)#平方
http://t.csdn.cn/a6JBd其他的可能就具体要什么具体找,当时搜了一大堆结果没收藏,就仅能提供这么多了
决策函数,包括了前面的所有方向,可以看得出来,又臭又长,很多地方可以简化
import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport mathfrom scipy.stats import normfrom sklearn.svm import SVRimport mathfrom scipy.stats import norm# 导入数据,路径中要么用\或/或者在路径前加rdataset = pd.read_csv('./BCHAIN-MKPRU.csv',index_col=0)bit_data = pd.DataFrame(dataset)x = [i for i in range(1,bit_data.shape[0]+1)]bit_rev = bit_data.drop(columns=['Date']).pct_change()bit_rev = np.c_[x,bit_rev]'''.drop(columns=['Index'])'''dataset = pd.read_csv('./LBMA-GOLD.csv',index_col=0)gold_data = pd.DataFrame(dataset)gold_rev = gold_data.drop(columns=['Date']).pct_change()x = [i for i in range(1,gold_data.shape[0]+1)]gold_rev = np.c_[x,gold_rev]'''.drop(columns=['Index'])'''#这里是一些默认的值,用于算比例归一化的到0,1的区间gold_index = 0bit_index = 1p_var = 0.01#只能允许百分之一的概率lamda = 0.94#下面是一些可以调参的数v = 0.3a = 0.6p = 0.4c = 0.7rsi_upper_threshold = 80rsi_lower_threshold = 30grsi_upper_threshold = 70grsi_lower_threshold = 35mm = [[0. for i in range(3)]for j in range(bit_data.shape[0])]mm = np.array(mm)rr = []cost = [0,0] #存买入成本def svrr(dataset,data_rev,day): #下标悖论,我们这里认为天数从第1天开始,传入的dataset就是完整的,data_rev也是完整的 #设置一个下限,就只考虑最近50填的数据,用至少从第二天的数据,因第一天有Nan st = max(1,day-25) z = data_rev[st:day-1,:] y = dataset[st:day-1] # 线性核函数配置支持向量机 data_rev = np.array(data_rev) svr = SVR(kernel="linear") svr.fit(z, y.ravel()) x1 = np.array([day]) x2 = np.array(data_rev[day,1]) zz = np.c_[x1,x2] res = svr.predict(zz) return res[0]def swma(data,rev,day,n): y = [] for i in data.index: if i > day: break y.append(float(data.loc[i].values[-1])) y = np.array(y) yy = rev[max(1,day-50):day+1,1] yy = np.power(yy,2)#平方 sigma = yy.copy() z = norm.ppf(p_var,loc = 0,scale=1) for i in range(1,51): sigma[i] = (1-lamda)*yy[i-1] + lamda*(sigma[i-1]) if sigma[i] > 100: sigma[-1] = sigma[i] break VAR = -z * math.sqrt(sigma[-1]) return VAR#返回黄金比 比特币的购买比例,参数标准,add是指的增长率的比值,price为单价,最后一项是考#虑假设只买其中一种货币,需要的手续费def rate(add,g_price,b_price): abb = (a*add + (1-a) * (g_price/b_price) - c *(g_price/(2*b_price))) if abb < 0: abb = 0 return abbgg = [0,0,1,2,3,4,5]def gold_day(day): return 5*(day//7)+ gg[day%7]#用14天求RSI指数,n = 0时表示只用黄金,下标,返回一个数组,第一个是黄金,大于80该卖,小于30该买def rsi(b_day,g_day,n): RSI = [0,0] inc = [0,0] dec = [0,0] tmp = [0,0] for i in range(max(b_day-10,3),min(b_day,bit_data.shape[0])): tmp[1] = bit_data['Value'][i]-bit_data['Value'][i-1] if tmp[1] >= 0: inc[1] = inc[1] + tmp[1] else: dec[1] = dec[1] + tmp[1] if n == 1: for i in range(max(3,g_day-10),g_day): tmp[0] = gold_data['USD (PM)'][i] - gold_data['USD (PM)'][i-1] if tmp[0] >= 0: inc[0] = inc[0] + tmp[0] else: dec[0] = dec[0] + tmp[0] RSI[0] = 100 * ((inc[0]/(inc[0]-dec[0])) if inc[0]-dec[0] != 0 else 0.5) RSI[1] = 100 * ((inc[1] / (inc[1] - dec[1])) if inc[1] - dec[1] != 0 else 0.5) #rr.append(RSI[0]) return RSIdef g_rsi(g_day): RSI = [0, 0] inc = [0, 0] dec = [0, 0] tmp = [0, 0] for i in range(max(3,g_day-10),g_day): tmp[0] = gold_data['USD (PM)'][i] - gold_data['USD (PM)'][i-1] if tmp[0] >= 0: inc[0] = inc[0] + tmp[0] else: dec[0] = dec[0] + tmp[0] RSI[0] = 100 * ((inc[0] / (inc[0] - dec[0])) if inc[0] - dec[0] != 0 else 0.5) rr.append(RSI[0])def jud_gold_flag(situ_g,r,money,pr): if situ_g == 0: # 还没买 flag = 1 if (r[gold_index] < grsi_lower_threshold and money[0] > 0) else 0 else: flag = -1 if (r[gold_index] > grsi_upper_threshold and money[gold_index+1] > 0 and money[1]*pr/cost[0] > 2.1) else 0 return flagdef jud_bit_flag(situ_b,r,money,pr): if situ_b == 0: # 还没买 flag = 1 if (r[bit_index] > rsi_lower_threshold and r[bit_index] < 50 and money[0] > 0) else 0 else: flag = -1 if (r[bit_index] > rsi_upper_threshold and money[bit_index+1] > 0 and money[2] *pr/cost[1] > 1.050) else 0 return flagdef b_oper(situ_b,flag, money,day): if flag == 0: return situ_b elif flag == 1 and money[0] > 0: cost[1] = money[0] money[bit_index + 1] = money[bit_index + 1] + money[0] / (bit_data['Value'][day]*1.02) money[0] = 0 return 1 elif flag == -1 and money[bit_index + 1] > 0: money[0] = money[0] + money[bit_index + 1] * bit_data['Value'][day]/1.02 money[bit_index + 1] = 0 return 0 return situ_bdef g_oper(situ_g,flag, money,day): if flag == 0: return situ_g elif flag == 1 and money[0] > 0: cost[0] = money[0] money[gold_index + 1] = money[gold_index + 1] + money[0] /(gold_data['USD (PM)'][day]*1.01) money[0] = 0 return 1 elif flag == -1 and money[gold_index + 1] > 0: money[0] = money[0] + money[gold_index + 1] * gold_data['USD (PM)'][day]/1.01 money[gold_index + 1] = 0 return 0 return situ_gdef if_g(g_day,b_day): while gold_data['Date'][g_day] <= bit_data['Date'][b_day]: if g_day >= bit_data.shape[0]: return [0,g_day] if gold_data['Date'][g_day] == bit_data['Date'][b_day]: return [1,g_day] else: g_day = g_day+1 return [0,g_day]#开始决策def judge(): money = [10.0,0.0,0.0] #别为美元、黄金、比特币 situ_g = 0 #只能为0和1,0为还没买币,1为已经买了币 situ_b = 0 bit_buy_flag = 0 #flag 为0为保持,1为买,-1为卖分 gold_buy_flag = 0 g_day = 1 for b_day in range(10,min(bit_data.shape[0],1826)): j = if_g(g_day, b_day) g_day = j[1] #不考虑黄金,只看比特币 if b_day == min(bit_data.shape[0],1733): bit_buy_flag = -1 gold_buy_flag = -1 situ_b =b_oper(situ_b,bit_buy_flag,money,b_day) situ_g = g_oper(situ_g, gold_buy_flag, money, g_day) elif b_day > 1733: m = 0 elif not j[0]: #只交易比特币 p_b = svrr(np.array(bit_data.drop(columns=['Date'])), bit_rev, b_day) r = rsi(b_day,g_day,j[0]) bit_buy_flag = jud_bit_flag(situ_b,r,money,cost[1] if b_day > 80 else svrr(np.array(bit_data.drop(columns=['Date'])), bit_rev, b_day)) situ_b = b_oper(situ_b,bit_buy_flag,money,b_day) else: #也就是正常时间段,都可以交易的 p_b = svrr(np.array(bit_data.drop(columns=['Date'])), bit_rev, b_day) p_g = svrr(np.array(gold_data.drop(columns=['Date'])), gold_rev, g_day) r = rsi(b_day,g_day,j[0]) bit_buy_flag = jud_bit_flag(situ_b,r,money,p_b) gold_buy_flag = jud_gold_flag(situ_g,r,money,p_g) #进行处理 if not(bit_buy_flag == gold_buy_flag and bit_buy_flag == 1) :#就是各干各的,分开算,但是需 if bit_buy_flag == -1: situ_b = b_oper(situ_b,bit_buy_flag,money,b_day) situ_g = g_oper(situ_g,gold_buy_flag,money,g_day) else: situ_g = g_oper(situ_g, gold_buy_flag, money, g_day) situ_b = b_oper(situ_b, bit_buy_flag, money, b_day) else: #这个就是处理同时买两种货币的情况 if money[0] <= 0: continue #b_var = swma(bit_data.drop(columns=['Date']),bit_rev,b_day,b_day*3/5) #g_var = swma(gold_data.drop(columns=['Date']),gold_rev,gold_day(b_day),gold_day(b_day)*3/5) tmp = rate(gold_rev[g_day,1]/bit_rev[b_day,1],p_g,p_b) p_b = money[0]/(1+tmp) p_g = money[0] -p_b cost[0] = p_g cost[1] = p_b money[0] = 0 money[gold_index+1] = money[gold_index+1] + p_g*0.99 money[bit_index+1] = money[bit_index+1] + p_b*0.98 situ_g = situ_b = 1 for i in range(3): if i == 0: mm[b_day,i] = money[i]*100 else: mm[b_day, i] = money[i]judge()for i in range(10,1264): g_rsi(i)''''''plt.figure(figsize=(8, 8), dpi=80)plt.figure(1)ax1 = plt.subplot(311)ax1.plot(bit_rev[:,0],mm[:,0],color = '#DA70D6')ax1.set_title('USD')ax2 = plt.subplot(312)ax2.plot(bit_rev[:,0],mm[:,1],color='#FFD700')ax2.set_title('Gold')ax3 = plt.subplot(313)ax3.plot(bit_rev[:,0],mm[:,2],color = '#00CED1')ax3.set_title('Bit')plt.plot([i for i in range(len(rr))],rr,c='#FFD700')plt.xlabel('Days')plt.ylabel('Gold RSI')plt.show()
注意一下这里使用的csv文件是经过我们处理的,可以从这个连接下载
链接:百度网盘 请输入提取码 提取码:abbb
地图可视化示例(好像是从这里搬来的用Pyecharts绘制美国地图 - 简书
from pyecharts import options as optsfrom pyecharts.charts import Mapimport osfrom pyecharts.datasets import register_urlregister_url("https://echarts-maps.github.io/echarts-countries-js/")# 基础数据value = [i for i in range(0,100)]value = value[0:99:10]attr = ["Alaska",'Kansas','Michigan','Louisiana','Florida','Virginia','Washington','Colorado','Nevada','Texas']data = []for index in range(len(attr)): city_ionfo = [attr[index], value[index]] data.append(city_ionfo)c = ( Map() .add("老美地图", data, "美国",is_roam = True,is_map_symbol_show = False,layout_size=2000,layout_center=['50%','40%']) .set_series_opts(label_opts=opts.LabelOpts(is_show=True)) .set_global_opts( title_opts=opts.TitleOpts(title="US"), visualmap_opts=opts.VisualMapOpts(max_=200), ) .render() )# 打开htmlos.system("render.html")
啊、、、、、美赛这4天的体验太让人内心复杂了,暂时就把这些存网上,然后本地删除了吧(沧桑)