美赛py体验总结

0、先小结一下

小菜狗第一次参加美赛,所以几乎没有对 在较短时间内对不熟悉的算法进行实现 的能力,所以3天从早到晚的敲键盘过程,有一般都是对着类似于下图的控制台报错一点点debug过来的。

(我们队选的是C题,主要原因是大家都不太会搜集数据,所以A/B题无从下手)

美赛py体验总结_第1张图片

 

咳咳,说正经的,个人对于这次的经验总结如下:

  1. 现学现用占主要地位。对于比赛且限时的类型而言,真的很考验临时学习的能力,不过对于我而言,主要是了解一下算法的原理,优缺点,适用情景,遇到了不会的,直接CSDN,或者百度一下

  2. python实现的配置。实现的话,一般是交给sklearn完成,我之所以选择了py就是其丰富的现成库以及方便安装的特点,使用起来真的很方便,同时配合pycharm调试(个人觉得pycharm调试py的效果比vs好很多),skilearn给出的很多机器学习类十分方便,直接拿来用(只需要知道类的使用方法就好)

  3. 数据的存储与使用。py的两种数据存储方式对应的使用要清楚、且统一。pandas对应dataframe、而numpy对应array(这两个一旦使用方式混淆,就很容易招来一堆报错);数据被存储后下标和序号很容易不清楚,一定要提前约定好,免得出现越界访问

  4. 呼应第一点,也是最重要的一点,就算是现学现用,比如把报错复制,粘贴百度,也得在解决后用心记住,或者做个临时笔记,并且有条理、有层次地记下来(我是因为这次美赛时间太紧张了,记得到处都是),不要总在一个地方摔无数次坑(虽然摔了很多次后我确实也逐渐长记性了2333)。比如dataframe访问元素是用[列名] [行数],而array才是对应像数组一样,下标访问

1、算法篇(代码统一贴最后)

  1. 首先是整个C题我们使用的算法的概括(考虑的真的没多少,就自己记录一下而已)

    核心问题是预测部分

    我们一开始想尝试时间序列算法预测,然后去试着跑了一下数据,不知道是不是数据的时间范围没设置好,用那个搞出来没法拟合,不过也给出一些推荐的博客以供参考

    http://t.csdn.cn/Kkw5s python实现ARIMA时序分析

    http://t.csdn.cn/NXwRc 对于拖尾和截尾的分析

    在暂时看不到ARIMA的希望后,我直接使用了比较朴素的SVR(仍然使用sklearn实现),支持向量回归去预测,大概是把自变量设置成了天数,一开始发现50天的拟合效果比较烂

    美赛py体验总结_第2张图片

     

    然后在加入了每日单价增长率以作为自变量的第二维度后,并且改少的天数,预测效果就比较可观了(当然这个存在延迟,无法对价格波动有一个好的预测,只能看趋势)

    美赛py体验总结_第3张图片

     

  2. 然后是风险评估这里

    比较尴尬的是,虽然后面强行做出来了一个风险的预估,但是没能拿去用(因为当时自己整合成函数后因为传参的类型,就是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图(最后一个)

     

     美赛py体验总结_第4张图片

  3. 接着是决策,也是个人觉得自己做得最拉的部分

    直接利用网上RSI指标,先求得当天对应的RSI值,然后按照现有结论,也就是RSI在什么区间,达到什么阈值,可以对应买入/卖出,黄金和比特币各设置地不同,一点点调阈值,寻找一些可行的结果,也跟着放一波参考链接

    【技术分析新手入门系列】技术指标 MA MACD RSI KDJ BOLL 布林线 趋势线 应用讲解_哔哩哔哩_bilibili 网上给出的一些投资指标参数的通俗解释,个人觉得挺有用的

    (tips:后来队友有查到说RSI提示买入的时间可能是50左右的中间值,而不是较小值,这个体现在后面的代码中了)

2、绘图篇

由于这道题不像b题那样有宏观的地理背景,所以在可视化上主要是线条图像,或者一些表格,看上去略微单调。。。不过也省事,直接使用pyplot.plot硬核绘图,效果还挺好

以下几个小点可以参考

  1. 设置坐标轴名称、图像颜色、scatter(之前有去了解一下,可以看看)的相关设置

    (1)scatter的相关参数

    s:画的点的大小(可以是数组,就点的大小不一样呗)

    c和cmap:c可以表示标签,cmap直接=plt.cm.Spectral就可以做到不同标签画出不同的色

    alpha:改变点的透明度,可以变好看,比如alpha = 0.4

    精致绘图技巧:把边缘和点的填充色设置为一样的,再加上alpha让中心透明度增加√

    美赛py体验总结_第5张图片

     

     

    推荐文章:Python-画图(散点图scatter、保存savefig)及颜色大全 - dqi1999 - 博客园 设置颜色可以参考的,好的颜色也是一个亮点

    Python中scatter函数参数详解_AnneQiQi的博客-CSDN博客_scatter函数

    python - 如何在matplotlib中使用不同的edgecolor进行散点图? - Thinbug

  2. plot的相关设置,这个感觉是具体看啥具体搜,不多给了

    http://t.csdn.cn/YnmGt

  3. 多图绘制

    是一个很重要的点,可以增加图像的美观性

    http://t.csdn.cn/yLPdm

    10分钟教你用Python中的Matplotlib绘制多图并合并展示 - 短短的路走走停停 - 博客园 这个力荐,很不错

  4. 地图的可视化(就是直接用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.

    其他的好像又弃坑了(扶额),一会也贴个示例代码

3、数据存储

  1. 先说一下数据预处理,一开始数据是有缺失的,在黄金那里,有几天的价格为空,所以我们选择用前后两天的平均数来处理

    对于处理缺失数据,这里提供几个参考链接

    【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili P21这个可以看看,用pandas来处理

    http://t.csdn.cn/RZKW5 CSDN的官文也有参考意义

  2. 接着就是这些让人头大的两种存储类型

    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

    其他的可能就具体要什么具体找,当时搜了一大堆结果没收藏,就仅能提供这么多了

4、拉胯的代码们

  1. 决策函数,包括了前面的所有方向,可以看得出来,又臭又长,很多地方可以简化

    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 abb​​gg = [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 RSI​def 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 flag​​def 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 flag​​def 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_b​​def 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_g​​def 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

  2. 地图可视化示例(好像是从这里搬来的用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天的体验太让人内心复杂了,暂时就把这些存网上,然后本地删除了吧(沧桑)

 

你可能感兴趣的:(python)