搜索
查看: 2440|回复: 7

[程序分享] 股市风起云涌,我用Python分析周期之道

[复制链接]

签到天数: 11 天

星际迷航无影无棕学术交流家园结构深研究飞飞浪王波浪研究家园

发表于 2019-1-8 22:46 | 显示全部楼层

[程序分享] 股市风起云涌,我用Python分析周期之道

来自:MACD论坛(bbs.macd.cn) 作者:alxbj 浏览:2440 回复:7

本帖最后由 alxbj 于 2019-1-8 22:47 编辑

正文

股票市场周期是股票市场长期的价格模式,通常与商业周期有关。 它是技术分析的关键,其中投资方法基于周期或重复的价格模式。 如果我们对股市周期有了更好的理解,我们总能以相对低的价格买入并在每个周期以相对较高的价格卖出,将始终获得正的回报。当然,股票市场没有什么策略可以永远赚钱,但我们基于Python,可以帮助我们更深入、快速地了解隐藏在股市中的周期。


fbprophet简介

Fbprophet是Facebook发布的一个开源软件,旨在为大规模预测提供一些有用的指导。 默认情况下,它会将时间序列划分为趋势和季节性,可能包含年度,周度和每日。 但是,分析师可以定义自己的季节性。 为了更好地理解该库,先导文件是非常有用的。


该库的一个特点是简单性、灵活性。 由于我们想要计算的股票市场周期不限于每年,每周或每日,我们应该定义自己的周期,找出哪些更适合数据。 此外,由于周末没有交易,我们不应该使用每周季节性。 我们还可以通过addseasonality函数定义'selfdefine_cycle'。 所有设置只需两行代码即可完成。


  • m = Prophet(weekly_seasonality=False,yearly_seasonality=False)
  • m.add_seasonality('self_define_cycle',period=8,fourier_order=8,mode='additive')

[color=rgb(51, 102, 153) !important]复制代码


以Costco为例

我们可以使用Costco标的从2015/10/1到2018/10/1, 使用pandas_datareader,我们可以快速读取股票价格。如下图:

地址:https://pandas-datareader.readth ... st/remote_data.html


在下图中,我们可以看到从2015年开始有一个强劲的价格增长趋势。然而,在中途仍然存在很多上下周期波动,这些周期都是我们的赚钱点。

  • ticker = "COST"
  • start_date = '2015-10-01'
  • end_date = '2018-10-01'
  • stock_data = data.DataReader(ticker, 'iex', start_date, end_date)
  • stock_data['close'].plot(figsize=(16,8),color='#002699',alpha=0.8)
  • plt.xlabel("Date",fontsize=12,fontweight='bold',color='gray')
  • plt.ylabel('Price',fontsize=12,fontweight='bold',color='gray')
  • plt.title("Stock price for Costco",fontsize=18)
  • plt.show()

[color=rgb(51, 102, 153) !important]复制代码

对于预测模型,评估它们的一种方法是样本均方误差。 我们可以使用2015/10/1至2018/3/31进行训练,并保留最后6个月的数据进行测试和计算样本均方误差。 在每个周期内,我们可以通过以最低价格买入并以最高价格卖出的方式来优化我们的回报。 为了简化过程,我们使用自定义函数cycle_analysis。 输出是一个列表,其中包含每个周期的预计回报和样本均方误差。

  • data:带有时间索引的Pandas数据
  • split_date:分割训练和测试数据的日期cycle:每个周期的间隔(天)
  • cycle:每个周期的间隔(天)
  • mode:季节性的加法或乘法(可选)
  • forecast_plot:是否打印预测图(可选,默认为False)
  • print_ind:是否打印每个周期的预计回报和是否采样均方误差(可选,默认为False)

  • def cycle_analysis(data,split_date,cycle,mode='additive',forecast_plot = False,print_ind=False):
  •    training = data[:split_date].iloc[:-1,]
  •    testing = data[split_date:]
  •    predict_period = len(pd.date_range(split_date,max(data.index)))
  •    df = training.reset_index()
  •    df.columns = ['ds','y']
  •    m = Prophet(weekly_seasonality=False,yearly_seasonality=False,daily_seasonality=False)
  •    m.add_seasonality('self_define_cycle',period=cycle,fourier_order=8,mode=mode)
  •    m.fit(df)
  •    future = m.make_future_dataframe(periods=predict_period)
  •    forecast = m.predict(future)
  •    if forecast_plot:
  •        m.plot(forecast)
  •        plt.plot(testing.index,testing.values,'.',color='#ff3333',alpha=0.6)
  •        plt.xlabel('Date',fontsize=12,fontweight='bold',color='gray')
  •        plt.ylabel('Price',fontsize=12,fontweight='bold',color='gray')
  •        plt.show()
  •    ret = max(forecast.self_define_cycle)-min(forecast.self_define_cycle)
  •    model_tb = forecast['yhat']
  •    model_tb.index = forecast['ds'].map(lambda x:x.strftime("%Y-%m-%d"))
  •    out_tb = pd.concat([testing,model_tb],axis=1)
  •    out_tb = out_tb[~out_tb.iloc[:,0].isnull()]
  •    out_tb = out_tb[~out_tb.iloc[:,1].isnull()]
  •    mse = mean_squared_error(out_tb.iloc[:,0],out_tb.iloc[:,1])
  •    rep = [ret,mse]
  •    if print_ind:
  •        print "Projected return per cycle: {}".format(round(rep[0],2))
  •        print "MSE: {}".format(round(rep[1],4))
  •    return rep

[color=rgb(51, 102, 153) !important]复制代码



在下面两个图中,我们将两种不同cycle(30和300)分别应用于Costco股票价格,并将2018/4/1作为训练和测试的分割日期。 正如我们所看到的,如果我们选择一个较短的长度(例如30天),则一个周期内的回报是很小的,我们需要经常进行交易,如果我们选择较长的长度,它会延长我们的预测(例如300天)。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

本帖被以下淘专辑推荐:

  • · Gann|主题: 25, 订阅: 1
金币:
奖励:
热心:
注册时间:
2009-11-26

回复 使用道具 举报

签到天数: 11 天

星际迷航无影无棕学术交流家园结构深研究飞飞浪王波浪研究家园

 楼主| 发表于 2019-1-8 22:49 | 显示全部楼层


我们可以在cycle_analysis函数上应用一个循环来计算不同循环长度的预计回报和样本均方误差,并且我们在下图中显示了结果。正如我们所看到的,长度越长,每个周期的预计回报和样本均方误差会增加。 考虑到交易成本,每个周期内的预计回报应该大于10元。 在这种约束下,我们可以选择最小样本均方误差的周期,并且它是252天。 每个周期的预计回报为17.12元,样本均方误差为15.936。 两者都很不错!

  • testing_box = range(10,301)
  • return_box = []
  • mse_box = []
  • for c in testing_box:
  • f = cycle_analysis(stock_data['close'],'2018-04-01',c)
  • return_box.append(f[0])
  • mse_box.append(f[1])



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
金币:
奖励:
热心:
注册时间:
2009-11-26

回复 使用道具 举报

签到天数: 11 天

星际迷航无影无棕学术交流家园结构深研究飞飞浪王波浪研究家园

 楼主| 发表于 2019-1-8 22:50 | 显示全部楼层



  • report = pd.DataFrame({'cycle':testing_box,'return':return_box,'mse':mse_box})
  • possible_choice = report[report['return'] >10]
  • possible_choice[possible_choice['mse']==min(possible_choice['mse'])]

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
金币:
奖励:
热心:
注册时间:
2009-11-26

回复 使用道具 举报

签到天数: 11 天

星际迷航无影无棕学术交流家园结构深研究飞飞浪王波浪研究家园

 楼主| 发表于 2019-1-8 22:51 | 显示全部楼层
  • c = possible_choice[possible_choice['mse']==min(possible_choice['mse'])]['cycle'].values[0]
  • ycle_analysis(stock_data['close'],'2018-04-01',c,forecast_plot=True,print_ind=True)


金币:
奖励:
热心:
注册时间:
2009-11-26

回复 使用道具 举报

签到天数: 11 天

星际迷航无影无棕学术交流家园结构深研究飞飞浪王波浪研究家园

 楼主| 发表于 2019-1-8 22:54 | 显示全部楼层

  • c = possible_choice[possible_choice['mse']==min(possible_choice['mse'])]['cycle'].values[0]
  • ycle_analysis(stock_data['close'],'2018-04-01',c,forecast_plot=True,print_ind=True)


金币:
奖励:
热心:
注册时间:
2009-11-26

回复 使用道具 举报

签到天数: 11 天

星际迷航无影无棕学术交流家园结构深研究飞飞浪王波浪研究家园

 楼主| 发表于 2019-1-8 22:55 | 显示全部楼层
本帖最后由 alxbj 于 2019-1-8 22:56 编辑





为了进一步说明投资策略,我们可以看到2015/10/1和2018/10/1之间的买入和卖出日期。 Return_Dates函数可以将所有买入和卖出日期作为输出返回,输入:

  • forecast:fbprophet预测对象
  • stock_data:带有时间索引的Pandas数据
  • cycle:周期长度
  • cycle_name:预测对象中循环列的名称
  • time_name:预测对象中时间列的名称

  • def Return_Dates(forecast,stock_data,cycle,cycle_name = 'self_define_cycle',time_name = 'ds'):
  •    # find out the highest and lowest dates in the first cycle
  •    # We cannot simply search for all highest and lowest point since there is slightly difference for high and low values in different cycles
  •    high = forecast.iloc[:cycle,]
  •    high = high[high[cycle_name]==max(high[cycle_name])][time_name]
  •    high = datetime.strptime(str(high.values[0])[:10],"%Y-%m-%d")
  •    low = forecast.iloc[:cycle,]
  •    low = low[low[cycle_name]==min(low[cycle_name])][time_name]
  •    low = datetime.strptime(str(low.values[0])[:10],"%Y-%m-%d")
  •    end_dt = datetime.strptime(stock_data.index[-1],"%Y-%m-%d")
  •    find_list = stock_data.index.map(lambda x:datetime.strptime(x,"%Y-%m-%d"))
  •    # Finding selling and buying dates with loop
  •    sell_dt = []
  •    sell_dt.append(high)
  •    # Looking for new cycle until it goes beyond the last date in stock_data
  •    while high<end_dt:
  •        high = high+timedelta(days=cycle)
  •        dif = (find_list-high).days
  •        high = find_list[abs(dif)==min(abs(dif))][0] # In order to avoid the non-trading dates
  •        sell_dt.append(high)
  •    buy_dt = []
  •    buy_dt.append(low)
  •    # Looking for new cycle until it goes beyond the last date in stock_data
  •    while low<end_dt:
  •        low = low+timedelta(days=cycle)
  •        dif = (find_list-low).days
  •        low = find_list[abs(dif)==min(abs(dif))][0] # In order to avoid the non-trading dates
  •        buy_dt.append(low)
  •    if buy_dt[0] > sell_dt[0]:
  •        sell_dt = sell_dt[1:]
  •    buy_dt = buy_dt[:-1]
  •    sell_dt = sell_dt[:-1]
  •    return [buy_dt,sell_dt]

[color=rgb(51, 102, 153) !important]复制代码


在2015/10/1和2018/10/1期间,我们买卖Costco四次。3年内的回报率为23.2%。 可能不是很吸引人,但至少它是比较乐观的回报。

更多股票的应用,当然,这种方法可以应用于尽可能多的股票。 我们列出了Costco,Apple,Microsoft,Home Depot和Nike的平均购买价格,平均销售价格,周期长度,样本均方误差,购买数量,销售数量和每个周期内的预计回报。

  • Analysis_ticks = ['COST','AAPL','MSFT','HD','NKE']
  • start_date = '2015-10-01'
  • end_date = '2018-10-01'
  • opt_cycle = []
  • prot_return = []
  • MSE = []
  • buy_times = []
  • sell_times = []
  • avg_buy_price = []
  • avg_sell_price = []
  • # Loop over each stock
  • for ticker in Analysis_ticks:
  •    stock_data = data.DataReader(ticker, 'iex', start_date, end_date)
  •    testing_box = range(50,301)
  •    return_box = []
  •    mse_box = []
  •    for cc in testing_box:
  •        f = cycle_analysis(stock_data['close'],'2018-04-01',cc)
  •        return_box.append(f[0])
  •        mse_box.append(f[1])
  •    report = pd.DataFrame({'cycle':testing_box,'return':return_box,'mse':mse_box})
  •    possible_choice = report[report['return'] >10]
  •    # If we cannot find a cycle with return greater than 10, give 0
  •    if possible_choice.shape[0]>0:
  •        c = possible_choice[possible_choice['mse']==min(possible_choice['mse'])]['cycle'].values[0]
  •        rp = possible_choice[possible_choice['mse']==min(possible_choice['mse'])]['return'].values[0]
  •        mse = possible_choice[possible_choice['mse']==min(possible_choice['mse'])]['mse'].values[0]
  •        df = stock_data[:'2018-04-01'].iloc[:-1,]['close'].reset_index()
  •        df.columns = ['ds','y']
  •        predict_period = len(pd.date_range('2018-04-01','2018-10-01'))
  •        m = Prophet(weekly_seasonality=False,yearly_seasonality=False,daily_seasonality=False)
  •        m.add_seasonality('self_define_cycle',period=c,fourier_order=8,mode='additive')
  •        m.fit(df)
  •        future = m.make_future_dataframe(periods=predict_period)
  •        forecast = m.predict(future)
  •        dt_list = Return_Dates(forecast,stock_data,c)
  •        buy_price = stock_data.loc[map(lambda x: x.strftime("%Y-%m-%d"),dt_list[0])]['close']
  •        sell_price = stock_data.loc[map(lambda x: x.strftime("%Y-%m-%d"),dt_list[1])]['close']
  •        bt = buy_price.shape[0]
  •        st = sell_price.shape[0]
  •        bp = np.mean(buy_price)
  •        sp = np.mean(sell_price)
  •    else:
  •        c = 0
  •        rp = 0
  •        mse = 0
  •        bt = 0
  •        st = 0
  •        bp = 0
  •        sp = 0
  •    opt_cycle.append(c)
  •    prot_return.append(rp)
  •    MSE.append(mse)
  •    buy_times.append(bt)
  •    sell_times.append(st)
  •    avg_buy_price.append(bp)
  •    avg_sell_price.append(sp)
  •    print "{} Finished".format(ticker)

[color=rgb(51, 102, 153) !important]复制代码



对于微软和耐克,我们找不到符合我们要求每个周期超过10元回报的周期。 对于Costco,Apple和Home Depot,我们可以找到大约250天的周期,并做出良好的预测和良好的回报。

  • stock_report = pd.DataFrame({'Stock':Analysis_ticks,'Cycle':opt_cycle,'Projected_Return_per_Cycle':prot_return,
  •                         'MSE':MSE,'Num_of_Buy':buy_times,'Num_of_Sell':sell_times,
  •                         'Average_Buy_Price':avg_buy_price,'Average_Sell_Price':avg_sell_price})
  • stock_report

[color=rgb(51, 102, 153) !important]复制代码


总结

借助Python和fbprophet包,我们可以更好地了解股市。 根据我们发现的周期,我们可以在3年内获得大约23%的回报。 也许这种投资策略无法满足所有人的需求,但你始终可以根据自己的知识和经验设定自己的方法。 强大的fbprophet软件包可以让你对股票市场的分析更加深入和轻松。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
金币:
奖励:
热心:
注册时间:
2009-11-26

回复 使用道具 举报

签到天数: 11 天

星际迷航无影无棕学术交流家园结构深研究飞飞浪王波浪研究家园

 楼主| 发表于 2019-1-9 09:15 | 显示全部楼层
搬运点东西真费劲....

各种限制,各种审核...
金币:
奖励:
热心:
注册时间:
2009-11-26

回复 使用道具 举报

签到天数: 1235 天

结构深研究炒股要低调

发表于 2019-1-9 21:02 | 显示全部楼层
alxbj 发表于 2019-1-9 09:15
搬运点东西真费劲....

各种限制,各种审核...


金币:
奖励:
热心:
注册时间:
2015-8-23

回复 使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

本站声明:1、本站所有广告均与MACD无关;2、MACD仅提供交流平台,网友发布信息非MACD观点与意思表达,因网友发布的信息造成任何后果,均与MACD无关。
MACD金融网(1997-2019)官方域名:macd.cn   MACD金融网校(2006-2019)官方域名:macdwx.com
值班热线[9:00—17:30]:020-85551719   24小时网站应急电话:18292674919
找回密码、投诉QQ:89918815 友情链接QQ:95008905 广告商务联系QQ:17017506 电话:18292674919
增值电信业务经营许可证:粤B2-20070280 粤公网安备 44010602000651号

举报|意见反馈|Archiver|手机版|MACD股票论坛 ( 粤ICP备05022117号

GMT+8, 2019-7-18 19:32 , Processed in 0.067961 second(s), 10 queries , MemCache On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表