├── Section10 ├── __init__.py └── Section10_1.py ├── Section11 ├── __init__.py └── Section11_1.py ├── Section2 ├── __init__.py ├── Section2_4.py ├── Section2_1_1.py ├── Section2_3_1.py └── Section2_3_2.py ├── Section3 ├── __init__.py └── Section3_All.py ├── Section4 ├── __init__.py ├── Section4_4.py ├── Section4_6.py ├── Section4_3.py ├── Section4_5.py └── Section4_1and2.py ├── Section5 ├── __init__.py ├── Section5_8.py ├── Section5_4.py ├── Section5_7.py ├── Section5_1.py ├── Section5_3.py ├── Section5_5.py └── Section5_6.py ├── Section6 ├── __init__.py ├── Section6_1.py └── Section6_2.py ├── Section7 ├── __init__.py ├── Section7_1_1.py ├── Section7_1_3.py ├── Section7_1_2.py └── Section7_2_1.py ├── Section8 ├── __init__.py ├── Section8_2.py └── Section8_1.py ├── Section9 ├── __init__.py ├── Section9_1.py ├── Section9_2.py └── Section9_3.py ├── .idea ├── vcs.xml ├── misc.xml ├── inspectionProfiles │ └── profiles_settings.xml ├── modules.xml └── Quantantive.iml └── README.md /Section10/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Section11/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Section2/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Section3/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Section4/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Section5/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Section6/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Section7/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Section8/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Section9/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /Section5/Section5_8.py: -------------------------------------------------------------------------------- 1 | from scipy import stats 2 | from abupy import ABuSymbolPd 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | from abupy import nd 6 | 7 | if __name__ == '__main__': 8 | tsla_df=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 9 | 10 | nd.macd.plot_macd_from_klpd(tsla_df) -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Section7/Section7_1_1.py: -------------------------------------------------------------------------------- 1 | from abupy import ABuSymbolPd 2 | import seaborn as sns 3 | import matplotlib.pyplot as plt 4 | import pandas as pd 5 | import numpy as np 6 | 7 | 8 | if __name__ == '__main__': 9 | kl_pd=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 10 | sns.set_context(rc={'figure.figsize':(14,7)}) 11 | # 简单的方法来显示趋势 12 | sns.regplot(x=np.arange(0,kl_pd.shape[0]),y=kl_pd.close.values,marker='+') 13 | plt.show() 14 | -------------------------------------------------------------------------------- /Section2/Section2_4.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | items=[1,2,3] 4 | # 输出1,2,3三个数不同顺序的所有组合 5 | for item in itertools.permutations(items): 6 | print(item) 7 | 8 | # 输出所有1,2,3三个数的两两组合 9 | for item in itertools.combinations(items,2): 10 | print(item) 11 | 12 | # 输出所有1,2,3三个数的两两组合,数字可以重复 13 | for item in itertools.combinations_with_replacement(items,2): 14 | print(item) 15 | 16 | ab=['a','b'] 17 | cd=['c','d'] 18 | # 输出的是两者的笛卡尔积 19 | for item in itertools.product(ab,cd): 20 | print(item) -------------------------------------------------------------------------------- /.idea/Quantantive.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /Section4/Section4_4.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | from abupy import ABuSymbolPd 5 | 6 | if __name__ == '__main__': 7 | tsla_df=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 8 | tsla_df['positive']=np.where(tsla_df.p_change>0,1,0) 9 | print(tsla_df.head()) 10 | 11 | # 构建交叉表,说白了就是某一列的所有取值做行,某一列的所有取值做列,画一个表 12 | # 表中每一个数字表示符合行和列条件的数据的个数。 13 | xt=pd.crosstab(tsla_df.date_week,tsla_df.positive) 14 | # 计算比例 15 | xt_div=xt.div(xt.sum(1).astype(float),axis=0) 16 | print(xt_div) 17 | 18 | # 还有一种透视表,貌似是只取了positive为1的情况 19 | # 感觉还是交叉表更加清晰 20 | pivot_tb=tsla_df.pivot_table(['positive'],index=['date_week']) 21 | print(pivot_tb) -------------------------------------------------------------------------------- /Section4/Section4_6.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | from abupy import ABuIndustries 4 | 5 | if __name__ == '__main__': 6 | r_symbol='usTSLA' 7 | # 获取和特斯拉同一个行业的股票,构成一个k*n*m的面板数据 8 | # 可以理解为k个股票,每个股票的dataframe和前面一样 9 | p_date,_=ABuIndustries.get_industries_panel_from_target(r_symbol,show=False) 10 | # Dimensions: 7 (items) x 499 (major_axis) x 12 (minor_axis) 11 | # 查看数据,发现是7*499*12,表示7支股票,499天,12个特征 12 | # print(p_date) 13 | # 沿着items方向进行切分 14 | print(p_date['usTTM'].head()) 15 | # 但是无法沿着major_axis或者minor_axis方向气氛, 16 | # 比如我想所有股票每一天的close价格,那么是要沿着close切分 17 | # print(p_date['close'].head()) 这么写会报错 18 | 19 | # 如果想要沿着close切分,那么首先要把close所在的轴(这里是minor_axis)变成items轴 20 | p_data_it=p_date.swapaxes('items','minor') 21 | print(p_data_it['close'].head()) -------------------------------------------------------------------------------- /Section5/Section5_4.py: -------------------------------------------------------------------------------- 1 | import seaborn as sns 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | from abupy import ABuSymbolPd 5 | 6 | if __name__ == '__main__': 7 | tsla_df=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 8 | print(tsla_df.head()) 9 | # 一张图上画出直方图和概率密度图 10 | # sns.distplot(tsla_df['p_change'],bins=80) 11 | # plt.show() 12 | 13 | # 绘制箱体图 14 | # sns.boxplot(x=tsla_df['date_week'],y=tsla_df['p_change'],data=tsla_df) 15 | # plt.show() 16 | 17 | # 绘制相关性和概率分布图 18 | # sns.jointplot(tsla_df['high'],tsla_df['low']) 19 | # plt.show() 20 | 21 | # 绘制热力图,用来表示协方差矩阵 22 | change_df=pd.DataFrame({'tsla':tsla_df['p_change']}) 23 | goog_df=ABuSymbolPd.make_kl_df('GOOG',n_folds=2) 24 | change_df=pd.concat([change_df,pd.DataFrame({'goog':goog_df['p_change']})],axis=1) 25 | aapl_df=ABuSymbolPd.make_kl_df('AAPL',n_folds=2) 26 | change_df=pd.concat([change_df,pd.DataFrame({'AAPL':goog_df['p_change']})],axis=1) 27 | print(change_df.head()) 28 | # 计算协方差 29 | corr=change_df.corr() 30 | _,ax=plt.subplots() 31 | # 画出热力图 32 | sns.heatmap(corr,ax=ax) 33 | plt.show() -------------------------------------------------------------------------------- /Section4/Section4_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | from abupy import ABuSymbolPd 5 | 6 | if __name__ == '__main__': 7 | tsla_df=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 8 | # 画直方图感受一下 9 | # tsla_df.p_change.hist(bins=80) 10 | # plt.show() 11 | # 通过pd.qcut将p_change的取值尽可能平均划分到10个不相交的范围里, 12 | # 并通过value_counts来进行打印显示 13 | cats=pd.qcut(np.abs(tsla_df.p_change),10) 14 | print(cats.value_counts()) 15 | 16 | # 上面是它帮我们定切分范围,如果自己定切分范围呢 17 | bins=[-np.inf,0,1,2,3,np.inf] 18 | cats=pd.cut(np.abs(tsla_df.p_change),bins) 19 | print(cats.value_counts()) 20 | # 结合get_dummies,形成类似one-hot编码. 21 | # 用来查看任何一天特斯拉的涨跌幅,落在哪个范围内 22 | change_dummies=pd.get_dummies(cats,prefix='p_change_dummies') 23 | print(change_dummies[:5]) 24 | 25 | # 把之前的得到的数据加入到原有数据中,如果只有一列的话,可以用这个 26 | # tsla_df['列index名']=要被加入的dataframe 27 | # 很多列的话,可以用concat,注意把要连在一起的数据写到[]里面 28 | tsla_df=pd.concat([tsla_df,change_dummies],axis=1) 29 | # pd.concat是会返回新的dataframe的,所以要用新的dataframe接住它才能够更新 30 | print(tsla_df) 31 | # concat,且axis=1时,是往dataframe加入更多的列 32 | # 当然,可以利用pd.appen加入更多的行 33 | -------------------------------------------------------------------------------- /Section11/Section11_1.py: -------------------------------------------------------------------------------- 1 | from abupy import AbuFactorAtrNStop, AbuFactorPreAtrNStop, AbuFactorCloseAtrNStop, AbuFactorBuyBreak 2 | from abupy import abu, EMarketTargetType, AbuMetricsBase, ABuMarketDrawing, ABuProgress, ABuSymbolPd 3 | from abupy import EMarketTargetType, EDataCacheType, EMarketSourceType, EMarketDataFetchMode, EStoreAbu, AbuUmpMainMul 4 | from abupy import AbuUmpMainDeg, AbuUmpMainJump, AbuUmpMainPrice, AbuUmpMainWave, feature, AbuFeatureDegExtend 5 | from abupy import AbuUmpEdgeDeg, AbuUmpEdgePrice, AbuUmpEdgeWave, AbuUmpEdgeFull, AbuUmpEdgeMul, AbuUmpEegeDegExtend 6 | from abupy import AbuUmpMainDegExtend, ump, Parallel, delayed, AbuMulPidProgress, AbuProgress 7 | 8 | if __name__ == '__main__': 9 | abu_result_tuple_train = abu.load_abu_result_tuple(n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME, 10 | custom_name='train_cn') 11 | abu_result_tuple_test = abu.load_abu_result_tuple(n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME, 12 | custom_name='test_cn') 13 | ABuProgress.clear_output() 14 | print('训练集结果:') 15 | metrics_train = AbuMetricsBase.show_general(*abu_result_tuple_train, only_show_returns=True) 16 | print('测试集结果:') 17 | metrics_test = AbuMetricsBase.show_general(*abu_result_tuple_test, only_show_returns=True) -------------------------------------------------------------------------------- /Section9/Section9_1.py: -------------------------------------------------------------------------------- 1 | from abupy import AbuFactorBuyBreak 2 | from abupy import AbuFactorAtrNStop 3 | from abupy import AbuFactorPreAtrNStop 4 | from abupy import AbuFactorCloseAtrNStop 5 | from abupy import abu 6 | from abupy import AbuMetricsBase 7 | 8 | if __name__ == '__main__': 9 | # 初始资金 10 | read_cash = 10000 11 | # 选股因子,这里选择不用 12 | stock_pickers = None 13 | # 买入因子,选用趋势突破中的向上突破 14 | buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak}, {'xd': 42, 'class': AbuFactorBuyBreak}] 15 | # 卖出因子,选择止盈止损,防暴跌,防盈利后亏损这三种因子 16 | sell_factors = [{'stop_loss_n': 1.0, 'stop_win_n': 3.0, 'class': AbuFactorAtrNStop}, 17 | {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.5}, 18 | {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}] 19 | # 股票池 20 | # choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG', 'usTSLA', 'usWUBA', 'usVIPS'] 21 | choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU'] 22 | # 运行策略 23 | abu_result_tuple, kp_pd_manager = abu.run_loop_back(read_cash, buy_factors, sell_factors, stock_pickers, 24 | choice_symbols=choice_symbols, n_folds=2) 25 | # 对结果进行度量,打印度量结果 26 | metrics=AbuMetricsBase(*abu_result_tuple) 27 | metrics.fit_metrics() 28 | metrics.plot_returns_cmp() 29 | print(abu_result_tuple.head()) 30 | -------------------------------------------------------------------------------- /Section5/Section5_7.py: -------------------------------------------------------------------------------- 1 | from scipy import stats 2 | from abupy import ABuSymbolPd 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | 6 | if __name__ == '__main__': 7 | tsla_df=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 8 | # 统计上的黄金分割线 9 | sp382_stats=stats.scoreatpercentile(tsla_df.close,38.2) 10 | sp618_stats=stats.scoreatpercentile(tsla_df.close,61.8) 11 | # 视觉上的 12 | sp382_view=(tsla_df.close.max()-tsla_df.close.min())*0.382+tsla_df.close.min() 13 | sp618_view=(tsla_df.close.max()-tsla_df.close.min())*0.618+tsla_df.close.min() 14 | 15 | # 从视觉618和统计618中筛选更大的值 16 | above618 = np.maximum(sp618_view, sp618_stats) 17 | # 从视觉618和统计618中筛选更小的值 18 | below618 = np.minimum(sp618_view, sp618_stats) 19 | # 从视觉382和统计382中筛选更大的值 20 | above382 = np.maximum(sp382_view, sp382_stats) 21 | # 从视觉382和统计382中筛选更小的值 22 | below382 = np.minimum(sp382_view, sp382_stats) 23 | 24 | # 绘制收盘价 25 | plt.plot(tsla_df.close) 26 | # 水平线视觉382 27 | plt.axhline(sp382_view, c='r') 28 | # 水平线统计382 29 | plt.axhline(sp382_stats, c='m') 30 | # 水平线视觉618 31 | plt.axhline(sp618_view, c='g') 32 | # 水平线统计618 33 | plt.axhline(sp618_stats, c='k') 34 | 35 | # 填充618 red 36 | plt.fill_between(tsla_df.index, above618, below618, 37 | alpha=0.5, color="r") 38 | # 填充382 green 39 | plt.fill_between(tsla_df.index, above382, below382, 40 | alpha=0.5, color="g") 41 | 42 | plt.show() -------------------------------------------------------------------------------- /Section4/Section4_5.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | from abupy import ABuSymbolPd 5 | 6 | if __name__ == '__main__': 7 | tsla_df=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 8 | print(tsla_df.head()) 9 | jump_threshold=tsla_df.close.mean()*0.03 10 | print(jump_threshold) 11 | 12 | # 创建一个新的Dataframe 13 | jump_df=pd.DataFrame() 14 | def judge_jump(today): 15 | # 如果你想要为一个定义在函数外的变量赋值, 16 | # 那么你就得告诉Python这个变量名不是局部的,而是 全局 的。 17 | # 我们使用global语句完成这一功能。 18 | # 没有global语句,是不可能为定义在函数外的变量赋值的。 19 | global jump_df,jump_threshold 20 | 21 | # 对于上涨的情况而言,满足下面条件才是跳空 22 | if today.p_change>0 and np.abs(today.low-today.pre_close)>jump_threshold: 23 | today['jump']=1 24 | today['jump_power']=np.abs(today.low-today.pre_close)/jump_threshold 25 | jump_df=jump_df.append(today) 26 | elif today.p_change<0 and np.abs(today.high-today.pre_close)>jump_threshold: 27 | today['jump']=-1 28 | today['jump_power']=np.abs(today.high-today.pre_close)/jump_threshold 29 | jump_df=jump_df.append(today) 30 | 31 | # 用for循环 32 | # for ind in np.arange(0,tsla_df.shape[0]): 33 | # today=tsla_df.ix[ind] 34 | # judge_jump(today) 35 | 36 | # for循环效率太低,可以用apply,这个和map类似,都是把函数作用在list的每一个数据上 37 | # 只不过apply是把函数作用在每一行数据上 38 | # tsla_df指要被执行函数的dataframe 39 | # 注意这里是axis=1,表示每次取数据时,沿着列的index遍历,那么就得到了一行数据 40 | tsla_df.apply(judge_jump,axis=1) 41 | print(jump_df.head()) -------------------------------------------------------------------------------- /Section9/Section9_2.py: -------------------------------------------------------------------------------- 1 | from abupy import AbuFactorBuyBreak 2 | from abupy import AbuFactorAtrNStop 3 | from abupy import AbuFactorPreAtrNStop 4 | from abupy import AbuFactorCloseAtrNStop 5 | from abupy import abu 6 | from abupy import AbuMetricsBase 7 | import matplotlib.pyplot as plt 8 | 9 | if __name__ == '__main__': 10 | # 初始资金 11 | read_cash = 10000 12 | # 选股因子,这里选择不用 13 | stock_pickers = None 14 | # 买入因子,选用趋势突破中的向上突破 15 | buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak}, {'xd': 42, 'class': AbuFactorBuyBreak}] 16 | # 卖出因子,选择止盈止损,防暴跌,防盈利后亏损这三种因子 17 | sell_factors = [{'stop_loss_n': 1.0, 'stop_win_n': 3.0, 'class': AbuFactorAtrNStop}, 18 | {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.5}, 19 | {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}] 20 | # 股票池 21 | # choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG', 'usTSLA', 'usWUBA', 'usVIPS'] 22 | choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU'] 23 | # 运行策略 24 | abu_result_tuple, kp_pd_manager = abu.run_loop_back(read_cash, buy_factors, sell_factors, stock_pickers, 25 | choice_symbols=choice_symbols, n_folds=2) 26 | # 对结果进行度量,打印度量结果 27 | metrics=AbuMetricsBase(*abu_result_tuple) 28 | metrics.fit_metrics() 29 | # metrics.plot_returns_cmp() 30 | # 策略和基准之间的波动率和夏普比率关系 31 | metrics.plot_sharp_volatility_cmp() 32 | # 买入因子生效间隔之间的关系 33 | metrics.plot_effect_mean_day() 34 | # 持股天数 35 | metrics.plot_keep_days() 36 | plt.show() -------------------------------------------------------------------------------- /Section2/Section2_1_1.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | 4 | price_str='30.14 29.85 26.36 32.82' 5 | print(type(price_str)) 6 | 7 | if not isinstance(type(price_str),str): 8 | print('not a str') 9 | else: 10 | print('str') 11 | 12 | price_array=price_str.split(' ') 13 | print(price_array) 14 | price_array.append('19.11') 15 | print(price_array) 16 | 17 | date_base=datetime.datetime(2017,1,1).__format__('%Y-%m-%d') 18 | print(date_base) 19 | 20 | mydate=20170101 21 | 22 | date_price=[(str(mydate+index),price) for index, price in enumerate(price_array)] 23 | print(date_price) 24 | 25 | from collections import namedtuple 26 | 27 | stock_namedtuple=namedtuple('stock',('date','price')) 28 | stock_namedtuple_list=[stock_namedtuple(date,price) for date,price in date_price] 29 | print(stock_namedtuple_list) 30 | 31 | stock_dict={date:price for date,price in date_price} 32 | print(stock_dict) 33 | 34 | # 仅仅会按照输入的顺序排序,还是尴尬。 35 | from collections import OrderedDict 36 | stock_order_dict=OrderedDict((date,price) for date,price in date_price) 37 | print(stock_order_dict) 38 | 39 | print(min(zip(price_array))) 40 | 41 | # 自定义函数 42 | def find_second_max(dict_array): 43 | stock_prices_sorted=sorted(zip(dict_array.values(),dict_array.keys())) 44 | # stock_prices_sorted=sorted(zip(dict_array.keys(),dict_array.values())) 45 | return stock_prices_sorted[-2] 46 | 47 | print(find_second_max(stock_dict)) 48 | 49 | # lambda函数 50 | find_second_max_lambda=lambda dict_array:sorted(zip(dict_array.values(),dict_array.keys()))[-2] 51 | print(find_second_max_lambda(stock_dict)) 52 | 53 | # 高阶函数 54 | price_float_array=[float(price_str) for price_str in stock_order_dict.values()] 55 | pp_array=[(price1,price2) for price1,price2 in zip(price_float_array[:-1],price_float_array[1:])] 56 | print(price_float_array) 57 | print(pp_array) 58 | 59 | increase_rate=[round((b-a)/a,3) for a,b in pp_array] 60 | print(increase_rate) 61 | increase_rate.insert(0,0) 62 | print(increase_rate) -------------------------------------------------------------------------------- /Section6/Section6_1.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import itertools 4 | from sklearn.linear_model import LinearRegression 5 | from sklearn.preprocessing import PolynomialFeatures 6 | from sklearn import metrics 7 | import matplotlib.pyplot as plt 8 | from abupy import ABuSymbolPd 9 | 10 | if __name__ == '__main__': 11 | # 1.1 下面演示的是如何使用sklearn来进行线性拟合 12 | tsla_df=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 13 | print(tsla_df.head()) 14 | # 准备拟合数据,x必须是dataframe类型,y必须是series类型 15 | y=tsla_df.close 16 | x=(np.arange(0,tsla_df.shape[0])) 17 | x=pd.DataFrame(x) 18 | # 调用sklearn的线性拟合模块开始拟合,并且需要计算截距 19 | model=LinearRegression(fit_intercept=True) 20 | model.fit(x,y) 21 | # 打印截距和参数 22 | print(model.intercept_) 23 | print(model.coef_) 24 | # 根据上面两个参数,得到预测直线的y值,或者用公式算也可以 25 | y_pred=model.predict(x) 26 | # y_pred=model.coef_*x+model.intercept_ 27 | # 计算评价指标 28 | MAE=metrics.mean_absolute_error(y,y_pred) 29 | MSE=metrics.mean_squared_error(y,y_pred) 30 | RMSE=np.sqrt(MSE) 31 | # 打印评价指标 32 | print(MAE) 33 | print(MSE) 34 | print(RMSE) 35 | # 画图查看 36 | plt.plot(np.arange(0,tsla_df.shape[0]),tsla_df.close) 37 | plt.plot(np.arange(0,tsla_df.shape[0]),y_pred) 38 | plt.show() 39 | 40 | # 1.2 当然sklearn也是可以使用多项式回归的 41 | # 首先重新写一下x和y值 42 | x = (np.arange(0, tsla_df.shape[0])) 43 | x = pd.DataFrame(x) 44 | y = tsla_df.close 45 | 46 | # 我们画四张图 47 | fig,axs=plt.subplots(ncols=2,nrows=2) 48 | # 把axs转化成list 49 | axs_list=list(itertools.chain.from_iterable(axs)) 50 | # 我们假设我们从1~4次多项式回归 51 | model=LinearRegression(fit_intercept=True) 52 | for i in range(0,4): 53 | # 如何实现多项式拟合 54 | pf=PolynomialFeatures(degree=i+1) 55 | model.fit(pf.fit_transform(x),y) 56 | # 得到预测值 57 | y_pred=model.predict(pf.fit_transform(x)) 58 | axs_list[i].plot(x,y) 59 | axs_list[i].plot(x,y_pred) 60 | 61 | plt.show() 62 | 63 | -------------------------------------------------------------------------------- /Section7/Section7_1_3.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | from abupy import ABuSymbolPd 5 | 6 | if __name__ == '__main__': 7 | kl_pd=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 8 | # 1、这里采用N日趋势突破,即超过N1天内的最高价,就买入,低于N2天内的最低价,就卖出 9 | N1=42 10 | N2=21 11 | 12 | # 2.1 采用pd.rolling_max可以寻找一个窗口长度内最大值 13 | kl_pd['n1_high']=pd.rolling_max(kl_pd['high'],window=N1) 14 | # 2.2 但这样会导致前N1-1个元素为NAN, 15 | # 我们使用pd.expanding_max来填充NAN,expanding_max会逐个遍历数组元素,并把返回直到当前位置看到过的最大元素 16 | # 用前k天的收盘价来代替,k∈[0,N1] 17 | expan_max=pd.expanding_max(kl_pd['close']) 18 | kl_pd['n1_high'].fillna(expan_max,inplace=True) 19 | # 2.3 最小值同理 20 | kl_pd['n2_low']=pd.rolling_min(kl_pd['low'],window=N2) 21 | expan_min=pd.expanding_min(kl_pd['close']) 22 | kl_pd['n2_low'].fillna(expan_min,inplace=True) 23 | print(kl_pd.head()) 24 | 25 | # 3.1 根据n1_high和n2_low来定义买入卖出的信号序列 26 | # 注意,是当天的收盘价,高于昨天以前的n1值,就买入,不能包括今天的,因为今天的收盘价,怎么也不会高于今天的最高值 27 | buy_signal=kl_pd[kl_pd.close > kl_pd.n1_high.shift(1)].index 28 | kl_pd.loc[buy_signal,'signal']=1 29 | # 3.2 n2_low的卖出信号同理 30 | sell_signal=kl_pd[kl_pd.close < kl_pd.n2_low.shift(1)].index 31 | kl_pd.loc[sell_signal,'signal']=0 32 | # 3.3 这里可以不用考虑Nan的情况 33 | kl_pd.signal.value_counts().plot(kind='pie') 34 | plt.show() 35 | 36 | # 4.1 将买入卖出的信号转化为持股的状态 37 | # 由于买入卖出的信号用了当天的收盘价,所以真正的持有和抛售是在第二天进行的 38 | # 所以这里要shifit 39 | kl_pd['keep']=kl_pd['signal'].shift(1) 40 | # 填充Nan 41 | kl_pd['keep'].fillna(method='ffill',inplace=True) 42 | kl_pd['keep'].fillna(0) 43 | print(kl_pd) 44 | 45 | # 5. 基准收益,是指我从一开始就持有,然后一直到最后才卖的收益 46 | # 5.1 计算每天基准收益 47 | kl_pd['benchmark_profit']=kl_pd['close']/kl_pd['close'].shift(1)-1 48 | # 5.2 计算策略每天收益 49 | kl_pd['trend_profit']=kl_pd['keep']*kl_pd['benchmark_profit'] 50 | # 5.3 计算累加基准收益和策略收益 51 | kl_pd['benchmark_profit_accum']=kl_pd['benchmark_profit'].cumsum() 52 | kl_pd['trend_profit_accum']=kl_pd['trend_profit'].cumsum() 53 | 54 | 55 | print(kl_pd.head(10)) 56 | 57 | # 5.4 可视化 58 | kl_pd[['benchmark_profit_accum','trend_profit_accum']].plot() 59 | plt.show() 60 | 61 | 62 | -------------------------------------------------------------------------------- /Section8/Section8_2.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | from abupy import AbuPickRegressAngMinMax 5 | from abupy import AbuPickStockWorker 6 | from abupy import AbuCapital 7 | from abupy import AbuKLManager 8 | from abupy import AbuBenchmark 9 | from abupy import ABuRegUtil 10 | from abupy import ABuPickStockExecute 11 | from abupy import AbuPickStockPriceMinMax 12 | 13 | if __name__ == '__main__': 14 | ''' 15 | 1) 16 | 这里是来进行选股策略的编写。 17 | 1.我们希望,对一段时间内股票的收盘价做线性拟合,得到拟合直线的斜率。 18 | 2.当斜率大于我们定于的最小阈值,且小于最大阈值时,表明可以买入 19 | ''' 20 | # 这里是条件,斜率最小值为0.0,即要求股票是上涨的趋势 21 | stock_pickers=[{'class':AbuPickRegressAngMinMax,'threshold_ang_min':0.0,'received':False}] 22 | 23 | # 一般而言,我们是遍历整个股市来选股,这里我们就选择以下几个股票来做演示 24 | choice_symbols=['usNOAH','usSFUN','usBIDU','usAAPL','usGOOG','usTSLA','usWUBA','usVIPS'] 25 | 26 | # 开始执行 27 | benchmark = AbuBenchmark() 28 | capital=AbuCapital(1000000,benchmark) 29 | kl_pd_manager=AbuKLManager(benchmark,capital) 30 | stock_pick=AbuPickStockWorker(capital,benchmark,kl_pd_manager,choice_symbols=choice_symbols,stock_pickers=stock_pickers) 31 | stock_pick.fit() 32 | 33 | print(stock_pick.choice_symbols) 34 | 35 | # 绘图 36 | kl_pd_SFUN=kl_pd_manager.get_pick_stock_kl_pd('usNOAH') 37 | deg=ABuRegUtil.calc_regress_deg(kl_pd_SFUN.close) 38 | print(deg) 39 | 40 | # 上面使用worker的操作太麻烦,下面可以直接使用executer 41 | stock_pickers=[{'class':AbuPickRegressAngMinMax,'threshold_ang_min':0.0,'threshold_ang_max':10.0,'reversed':False}] 42 | result=ABuPickStockExecute.do_pick_stock_work(choice_symbols,benchmark,capital,stock_pickers) 43 | print(result) 44 | 45 | 46 | ''' 47 | 2) 48 | 同样,上面是采用了一种选股因子,那么,当我采用多种选股因子时会怎样呢? 49 | 1. 这里注意,选股和择时是不一样的。 50 | 2. 对于择时而言,当我有多因子时,任意时刻,只要满足择时中的一个条件,就可以进行买入或者卖出操作 51 | 3. 而对于选股,只有当股票的趋势满足所有因子时,我们才选择该股票 52 | ''' 53 | stock_pickers=[{'class':AbuPickRegressAngMinMax,'threshold_ang_min':0.0,'reversed':False}, 54 | {'class':AbuPickStockPriceMinMax,'threshold_price_min':50.0,'reversed':False}] 55 | result=ABuPickStockExecute.do_pick_stock_work(choice_symbols,benchmark,capital,stock_pickers) 56 | print(result) -------------------------------------------------------------------------------- /Section5/Section5_1.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | from abupy import ABuSymbolPd 3 | import matplotlib.finance as mpf 4 | 5 | 6 | 7 | 8 | def plot_demo(tsla_df,axs=None,just_serise=False): 9 | ''' 10 | 1.1 利用matplot绘制series,numpy以及list数据 11 | :param axs:子画布 12 | :param just_serise:是否只画series类型的线 13 | :return: 14 | ''' 15 | # 创建画布 16 | drawer=plt if axs is None else axs 17 | # 绘制serise对象 18 | drawer.plot(tsla_df.close) 19 | 20 | if not just_serise: 21 | # 画numpy对象 22 | # Dataframe.index名称.values,返回的就是numpy对象 23 | drawer.plot(tsla_df.close.index,tsla_df.close.values+10,c='g') 24 | 25 | # 画list对象 26 | drawer.plot(tsla_df.close.index.tolist(),(tsla_df.close.values+20).tolist(),c='b') 27 | 28 | plt.xlabel('time') 29 | plt.ylabel('close') 30 | plt.title('TSLA') 31 | plt.grid(True) 32 | 33 | 34 | 35 | if __name__ == '__main__': 36 | tsla_df=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 37 | print(tsla_df.head()) 38 | 39 | # 1.1 利用matplot绘制三种不同类型的数据 40 | # plot_demo(tsla_df) 41 | # plt.show() 42 | 43 | # 1.2 子画布以及loc 44 | # 生成2*2的画布,上面可以有4个图 45 | # _,axs=plt.subplots(nrows=2,ncols=2) 46 | # for i in range(0,2): 47 | # for j in range(0,2): 48 | # drawer=axs[i][j] 49 | # plot_demo(tsla_df,axs=drawer) 50 | # drawer.legend(['Series','Numpy','List'],loc='best') 51 | # 52 | # plt.show() 53 | 54 | # 1.3 K线图绘制 55 | __colorup__='red' 56 | __colordown__='green' 57 | tsla_part_df=tsla_df[-30:] 58 | fig,ax=plt.subplots() 59 | # 用来存放等待绘制的数据 60 | qutotes=[] 61 | # for index,(d,o,c,h,l) in enumerate(zip(tsla_part_df.index,tsla_part_df.open,tsla_part_df.close,tsla_part_df.high,tsla_part_df.low)): 62 | # # 把日期数据从日期格式转换为数字格式 63 | # d=mpf.date2num(d) 64 | # # 存储数据 65 | # val=(d,o,c,h,l) 66 | # qutotes.append(val) 67 | #利用mpf进行绘图 68 | mpf.candlestick2_ochl(ax,tsla_part_df.open,tsla_part_df.close,tsla_part_df.high,tsla_part_df.low,width=0.6,colorup=__colorup__,colordown=__colordown__) 69 | ax.autoscale_view() 70 | # 使用这个方法将x轴的数据改成时间已经不行了 71 | # ax.xaxis_date(tsla_part_df.index.values.tolist()) 72 | # 需要这样来进行处理 73 | ax.set_xticks(range(0, len(tsla_part_df['date']), 5)) 74 | ax.set_xticklabels(tsla_part_df['date'][::5]) 75 | plt.show() 76 | -------------------------------------------------------------------------------- /Section5/Section5_3.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | # 虽然用pandas也可以画图,但是由于pandas底层也是调用matplotlib的,所以在用的时候还是需要导入matplotlib的。 4 | import matplotlib.pyplot as plt 5 | from abupy import ABuSymbolPd 6 | 7 | if __name__ == '__main__': 8 | # 2.1 演示什么叫做波动 9 | # 即,若干天的标准差乘以天数的开平方 10 | # demo_list=np.array([2,4,16,20]) 11 | # demo_window=3 12 | # temp_result=pd.rolling_std(demo_list,window=demo_window,center=False) 13 | # rolling_result=temp_result*np.sqrt(demo_window) 14 | # print(rolling_result) 15 | 16 | # 3.1 使用pandas画波动曲线 17 | tsla_df=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 18 | tsla_df_copy=tsla_df.copy() 19 | # 这里不用像书里一样用shift,是因为数据中已经帮你填好了这个数据 20 | # 计算投资回报,即当天收盘价比上前天收盘价 21 | tsla_df_copy['return']=np.log(tsla_df['close']/tsla_df['pre_close']) 22 | # 移动收益标准差,计算20天收益率的标准差 23 | tsla_df_copy['mov_std']=pd.rolling_std(tsla_df_copy['return'],window=20,center=False)*np.sqrt(20) 24 | # 加权移动收益标准差 25 | tsla_df_copy['std_ewm']=pd.ewmstd(tsla_df_copy['return'],span=20,min_periods=20,adjust=True)*np.sqrt(20) 26 | # 画图 27 | # tsla_df_copy[['close','mov_std','std_ewm','return']].plot(subplots=True,grid=True) 28 | # plt.show() 29 | 30 | # 3.2 绘制股票的价格和均线 31 | # 价格 32 | # tsla_df.close.plot() 33 | # # ma30 34 | # pd.rolling_mean(tsla_df.close,window=30).plot() 35 | # plt.legend(['close','ma30'],loc='best') 36 | # plt.show() 37 | 38 | # 3.3 其他pandas统计图形种类 39 | # 获得低开高走的交易日的下一个交易日内容 40 | low_to_high_df=tsla_df.iloc[tsla_df[(tsla_df.close>tsla_df.open) & (tsla_df.key != tsla_df.shape[0]-1)].key.values+1] 41 | # 将涨跌幅,正的向上取整,负的向下取整 42 | # 这里指定了列,为p_change,因此np.where返回的时候,是一个n*1的列向量 43 | change_ceil_floor=np.where(low_to_high_df.p_change>=0,np.ceil(low_to_high_df['p_change']),np.floor(low_to_high_df['p_change'])) 44 | # 由于上面返回的numpy数据,所有将其转化为pandas的series数据,方便绘图 45 | change_ceil_floor=pd.Series(change_ceil_floor) 46 | # 下面绘制不同类型的统计图 47 | _,axs=plt.subplots(nrows=2,ncols=2) 48 | # 竖直树状图 49 | change_ceil_floor.value_counts().plot(kind='bar',ax=axs[0][0]) 50 | # 水平树状图 51 | change_ceil_floor.value_counts().plot(kind='barh',ax=axs[0][1]) 52 | # 概率密度图 53 | # 说实话,我觉得这个概率密度图有问题,x的取值为什么能够那么大,不是应该集中在-4到4之间才对嘛 54 | # 这里貌似画反了 55 | change_ceil_floor.value_counts().plot(kind='kde',ax=axs[1][0]) 56 | # 饼图 57 | change_ceil_floor.value_counts().plot(kind='pie',ax=axs[1][1]) 58 | print(change_ceil_floor.value_counts()) 59 | plt.show() 60 | -------------------------------------------------------------------------------- /Section5/Section5_5.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | from abupy import ABuSymbolPd 5 | 6 | def plot_series(tsla_series,alpha=1): 7 | plt.plot(tsla_series,alpha=alpha) 8 | 9 | 10 | def plot_trade(tsla_df,start_date,end_date): 11 | ''' 12 | 标出持有期,当最后一天下跌了,区间为绿色(止损),最后一天上涨了,区间为红色(止盈) 13 | :param tsla_df: 股票数据 14 | :param start_date: 持有股票的那一天 15 | :param end_date: 卖出股票的那一天 16 | :return: 17 | ''' 18 | # 得到购买股票的那一天 19 | start = tsla_df[tsla_df.index == start_date].key.values[0] 20 | # 找出2014-09-05对应时间序列中的index作为end 21 | end = tsla_df[tsla_df.index == end_date].key.values[0] 22 | 23 | # 使用5.1.1封装的绘制tsla收盘价格时间序列函数plot_demo 24 | # just_series=True, 即只绘制一条曲线使用series数据 25 | plot_series(tsla_df.close) 26 | 27 | # 将整个时间序列都填充一个底色blue,注意透明度alpha=0.08是为了 28 | # 之后标注其他区间透明度高于0.08就可以清楚显示 29 | plt.fill_between(tsla_df.index, 0, tsla_df['close'], color='blue', 30 | alpha=.08) 31 | 32 | # 标注股票持有周期绿色,使用start和end切片周期 33 | # 透明度alpha=0.38 > 0.08 34 | print(tsla_df[end:end+1]) 35 | if tsla_df['close'][end] < tsla_df['open'][end]: 36 | plt.fill_between(tsla_df.index[start:end], 0, 37 | tsla_df['close'][start:end], color='green', 38 | alpha=.38) 39 | else: 40 | 41 | plt.fill_between(tsla_df.index[start:end], 0, 42 | tsla_df['close'][start:end], color='red', 43 | alpha=.38) 44 | 45 | # 设置y轴的显示范围,如果不设置ylim,将从0开始作为起点显示,效果不好 46 | plt.ylim(np.min(tsla_df['close']) - 5, 47 | np.max(tsla_df['close']) + 5) 48 | # 使用loc='best' 49 | plt.legend(['close'], loc='best') 50 | 51 | 52 | def plot_trade_with_annotation(tsla_df,start,end,annotate): 53 | ''' 54 | 在绘图的基础上添加注解 55 | :param tsla_df: 56 | :param start: 57 | :param end: 58 | :param annotate: 注解 59 | :return: 60 | ''' 61 | # 标注交易区间buy_date到sell_date 62 | plot_trade(tsla_df,start, end) 63 | # annotate文字,asof:从tsla_df['close']中找到index:sell_date对应值 64 | plt.annotate(annotate, 65 | xy=(end, tsla_df['close'].asof(end)), 66 | arrowprops=dict(facecolor='yellow'), 67 | horizontalalignment='left', verticalalignment='top') 68 | plt.show() 69 | 70 | 71 | if __name__ == '__main__': 72 | tsla_df=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 73 | print(tsla_df.head()) 74 | 75 | plot_trade_with_annotation(tsla_df,'2016-07-28','2016-10-11','sell for gain money') 76 | -------------------------------------------------------------------------------- /Section5/Section5_6.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from abupy import ABuSymbolPd 4 | 5 | def regular_std(group): 6 | ''' 7 | 归一化,令起均值为0,标准差为1 8 | :param group: 9 | :return: 10 | ''' 11 | return (group-group.mean())/group.std() 12 | 13 | def regular_min_max(group): 14 | ''' 15 | 另一种归一化,减去最小值,除以(最大值减去最小值) 16 | :param group: 17 | :return: 18 | ''' 19 | return (group-group.min())/(group.max()-group.min()) 20 | 21 | def two_mean_list(one,two,type_look='look_max'): 22 | ''' 23 | 向较大序列看齐:较小序列乘以(较大序列的均值除以较小序列的均值) 24 | 向较小序列看同理 25 | :param one: 第一个序列 26 | :param two: 第二个序列 27 | :param type_look: 看齐方式 28 | :return: 29 | ''' 30 | one_mean = one.mean() 31 | two_mean = two.mean() 32 | if type_look == 'look_max': 33 | """ 34 | 向较大的均值序列看齐 35 | """ 36 | one, two = (one, one_mean / two_mean * two) \ 37 | if one_mean > two_mean else ( 38 | one * two_mean / one_mean, two) 39 | elif type_look == 'look_min': 40 | """ 41 | 向较小的均值序列看齐 42 | """ 43 | one, two = (one * two_mean / one_mean, two) \ 44 | if one_mean > two_mean else ( 45 | one, two * one_mean / two_mean) 46 | 47 | return one,two 48 | 49 | def plot_two(drawer,one,two,title): 50 | drawer.plot(one) 51 | drawer.plot(two) 52 | drawer.set_title(title) 53 | drawer.legend(['TSLA','GOOG'],loc='best') 54 | 55 | if __name__ == '__main__': 56 | tsla_df=ABuSymbolPd.make_kl_df('TSLA',n_folds=2) 57 | goog_df=ABuSymbolPd.make_kl_df('GOOG',n_folds=2) 58 | 59 | 60 | # 1.为了更好的比较趋势,需要把数据进行归一化 61 | _,axs=plt.subplots(nrows=2,ncols=2) 62 | # 第一种归一化 63 | plot_two(axs[0][0],regular_std(tsla_df.close),regular_std(goog_df.close),'regular_std') 64 | 65 | # 第二种归一化 66 | plot_two(axs[0][1], regular_min_max(tsla_df.close), regular_min_max(goog_df.close), 'regular_min_max') 67 | 68 | # 第三种归一化 69 | tsla_df_re,goog_df_re=two_mean_list(tsla_df.close,goog_df.close) 70 | plot_two(axs[1][0], tsla_df_re, goog_df_re, 'look_max') 71 | 72 | # 第四种归一化 73 | tsla_df_re,goog_df_re=two_mean_list(tsla_df.close,goog_df.close,type_look='look_min') 74 | plot_two(axs[1][1], tsla_df_re, goog_df_re, 'look_min') 75 | 76 | plt.show() 77 | 78 | # 2.上面的归一化是一种方法,还可以使用双y轴策略 79 | _,ax1=plt.subplots() 80 | ax1.plot(tsla_df.close,c='r',label='tsla') 81 | ax1.legend(loc=2) 82 | ax1.grid(False) 83 | # 这里是关键,相当于在同一个图上,两个x轴重合 84 | ax2=ax1.twinx() 85 | ax2.plot(goog_df.close,c='g',label='google') 86 | ax2.legend(loc=1) 87 | 88 | plt.show() -------------------------------------------------------------------------------- /Section4/Section4_1and2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | 5 | if __name__ == '__main__': 6 | 7 | # 创建初始数据 8 | view_days=504 9 | stock_cnt=200 10 | stock_day_change=np.random.standard_normal((stock_cnt,view_days)) 11 | # print(stock_day_change[:2,:5]) 12 | 13 | # 1、关于Dataframe的构建和相关方法 14 | stock_dataframe=pd.DataFrame(stock_day_change) 15 | # print(stock_dataframe.head()) 16 | 17 | # 1.1 直接添加行索引 18 | stock_symbols=['Stock'+str(i) for i in range(0,stock_cnt)] 19 | stock_dataframe=pd.DataFrame(stock_day_change,index=stock_symbols) 20 | # print(stock_dataframe.head()) 21 | 22 | # 1.2 直接添加列索引 23 | days=pd.date_range('2017-01-01',periods=view_days,freq='1D') 24 | stock_dataframe=pd.DataFrame(stock_day_change,index=stock_symbols,columns=days) 25 | # print(stock_dataframe.head()) 26 | 27 | # 1.3.1 DataFrame的转置 28 | stock_dataframe=stock_dataframe.T 29 | # print(stock_dataframe) 30 | # 1.3.2 Dataframe的重采样 31 | stock_dataframe_resample=stock_dataframe.resample('21D').mean() 32 | # print(stock_dataframe_resample) 33 | 34 | # 1.4 Series,可以理解为只有一列的Dataframe 35 | stock_series=stock_dataframe['Stock0'] 36 | # print(stock_series.head()) 37 | # 顺手直接利用pandas直接画图 38 | stock_series.cumsum().plot() 39 | # plt.show() 40 | 41 | # 1.5 重采样数据 42 | stock_0_series=stock_dataframe['Stock0'] 43 | # 这里ohlc指open,highest,lowest,close四个值 44 | # 分别对应5天累加结果中第一天,最高,最低和最后一天的值 45 | stock_resample5=stock_0_series.cumsum().resample('5D').ohlc() 46 | stock_resample20=stock_0_series.cumsum().resample('20D').ohlc() 47 | # 验证ohlc 48 | # print(stock_resample5.head()) 49 | # print(stock_0_series.cumsum().head(20)) 50 | # 用value得到series中的值后,就是一个numpy的array 51 | # print(type(stock_resample5['open'].values)) 52 | 53 | # 2、基本数据分析 54 | from abupy import ABuSymbolPd 55 | # 获取特斯拉(TSLA),2年内数据 56 | tsla_df=ABuSymbolPd.make_kl_df('usTSLA',n_folds=2) 57 | print(tsla_df.tail()) 58 | 59 | # 2.1.1 大致绘制股票走势,看一下样子 60 | # tsla_df[['close','volume']].plot(subplots=True,style=['r','g'],grid=True) 61 | # plt.show() 62 | # 2.1.2 利用info(),看一下数据类型 63 | print(tsla_df.info()) 64 | # 2.1.3 利用describe(),看一下统计值 65 | print(tsla_df.describe()) 66 | 67 | # 2.2.1 利用loc(),通过索引名称来进行切片,后面不传列值表明要所有的列 68 | print(tsla_df.loc['2017-01-01':'2017-01-10','close']) 69 | # 2.2.2 利用iloc,通过缩影的数值选取切片,这个就和python或者numpy一样 70 | print(tsla_df.iloc[1:10,2:4]) 71 | # 2.2.3 还可以通过.列名的方式执行列 72 | print(tsla_df.open[0:3]) 73 | # 2.2.4 混合使用iloc和loc,不过注意在获取多个列的时候,用两个[] 74 | print(tsla_df[['open','close','volume']][0:4]) 75 | 76 | # 2.3.1 利用逻辑筛选找出符合要求的数据。 77 | # 注意,这里不像numpy一样只取出符号要求的那个数据,而是把符合数据所在一行都留下 78 | # 复合条件和numpy中一样 79 | print(tsla_df[(np.abs(tsla_df.p_change>8) & (tsla_df.volume>2.5*tsla_df.volume.mean()))]) 80 | 81 | # 2.4.1 数据序列排序,通过by来选择按照哪一列,通过ascending来指定排序方向 82 | print(tsla_df.sort_index(by='p_change')[:5]) 83 | # print(tsla_df.sort_values(by='p_change',inplace=True)) 84 | # 2.4.2 处理丢失数据,丢弃drop,或者填充fill 85 | tsla_df=tsla_df.dropna(how='all') 86 | # inplace表示不用返回新的序列,就在原始序列上修改 87 | tsla_df.fillna(0,inplace=True) 88 | # 2.4.3 计算某一列的增长下跌幅度,但是没有把这列数据放到原来的dataframe中哦需要的话自己放 89 | print(tsla_df.close.pct_change()[:5]) 90 | tsla_df['pct_change']=tsla_df.close.pct_change() 91 | print(tsla_df[:5]) 92 | -------------------------------------------------------------------------------- /Section9/Section9_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from abupy import AbuFactorBuyBreak 3 | from abupy import AbuFactorAtrNStop 4 | from abupy import AbuFactorPreAtrNStop 5 | from abupy import AbuFactorCloseAtrNStop 6 | from abupy import abu 7 | from abupy import AbuMetricsBase 8 | from abupy import ABuGridHelper 9 | from abupy import GridSearch 10 | 11 | if __name__ == '__main__': 12 | ''' 13 | 使用grid-search来寻找最优参数 14 | ''' 15 | 16 | # 1.考虑不同卖出因子的组合 17 | # 设置止盈止损最优参数范围 18 | # stop_win_range = np.arange(2.0, 4.5, 0.5) 19 | # stop_loss_range = np.arange(0.5, 2, 0.5) 20 | stop_win_range = np.arange(2.0, 4, 0.5) 21 | stop_loss_range = np.arange(1, 2, 0.5) 22 | # 查看数据 23 | print("止盈%s" % stop_win_range) 24 | print("止损%s" % stop_loss_range) 25 | 26 | sell_atr_nstop_factor_grid = {'class': [AbuFactorAtrNStop], 'stop_loss_n': stop_loss_range, 27 | 'stop_win_n': stop_win_range} 28 | 29 | # 设置防暴跌,防盈利后亏损参数 30 | # close_atr_range = np.arange(1.0, 4.0, 0.5) 31 | # pre_atr_range = np.arange(1.0, 3.5, 0.5) 32 | close_atr_range = np.arange(1.0, 3.0, 0.5) 33 | pre_atr_range = np.arange(1.0, 3, 0.5) 34 | 35 | print("close: %s" % close_atr_range) 36 | print("pre: %s" % pre_atr_range) 37 | 38 | sell_atr_pre_factor_grid = {'class': [AbuFactorPreAtrNStop], 'pre_atr_n': pre_atr_range} 39 | sell_atr_close_factor_grid = {'class': [AbuFactorCloseAtrNStop], 'close_atr_n': close_atr_range} 40 | 41 | # 对各个因子做排列组合 42 | sell_factors_product = ABuGridHelper.gen_factor_grid(ABuGridHelper.K_GEN_FACTOR_PARAMS_SELL, 43 | [sell_atr_nstop_factor_grid, sell_atr_pre_factor_grid, 44 | sell_atr_close_factor_grid]) 45 | # 一共有476种方式,计算方法如下: 46 | # 1.全部组合,有5*3*6*5=450种 47 | # 2.仅考虑止盈止损,有5*3=15种 48 | # 3.仅考虑防暴跌:有6种 49 | # 4.仅考虑防盈利后亏损,有5种 50 | # 5.综上,一共476种 51 | print("卖出组合总的组合方式有%s" % len(sell_factors_product)) 52 | print("第0中组合为%s" % sell_factors_product[0]) 53 | 54 | # 2、再考虑买入因子 55 | buy_bk_factor_grid1 = { 56 | 'class': [AbuFactorBuyBreak], 57 | 'xd': [42] 58 | } 59 | 60 | buy_bk_factor_grid2 = { 61 | 'class': [AbuFactorBuyBreak], 62 | 'xd': [60] 63 | } 64 | 65 | buy_factors_product = ABuGridHelper.gen_factor_grid( 66 | ABuGridHelper.K_GEN_FACTOR_PARAMS_BUY, [buy_bk_factor_grid1, buy_bk_factor_grid2]) 67 | 68 | # 有三种组合,分别为,只使用42日突破,只使用60日突破和同时使用42日和60日突破。 69 | print('买入因子参数共有{}种组合方式'.format(len(buy_factors_product))) 70 | print('买入因子组合形式为{}'.format(buy_factors_product)) 71 | 72 | # 3、进行gridsearch 73 | read_cash = 100000 74 | choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU'] 75 | grid_search = GridSearch(read_cash, choice_symbols, buy_factors_product=buy_factors_product, 76 | sell_factors_product=sell_factors_product) 77 | scores,score_tuple_array=grid_search.fit(n_jobs=-1) 78 | 79 | print('组合因子参数数量{}'.format(len(buy_factors_product) * len(sell_factors_product))) 80 | # 如果从本地序列文件中读取则没有scores 81 | print('最终评分结果数量{}'.format(len(scores))) 82 | 83 | # 可视化策略 84 | best_score_tuple_grid = grid_search.best_score_tuple_grid 85 | AbuMetricsBase.show_general(best_score_tuple_grid.orders_pd, best_score_tuple_grid.action_pd, 86 | best_score_tuple_grid.capital, best_score_tuple_grid.benchmark) 87 | -------------------------------------------------------------------------------- /Section7/Section7_1_2.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | from abupy import ABuSymbolPd 5 | 6 | 7 | def plot_train_test(train_kl, test_kl): 8 | ''' 9 | 可视化训练数据和测试数据 10 | :param train_kl: 11 | :param test_kl: 12 | :return: 13 | ''' 14 | fig, axis = plt.subplots(nrows=2, ncols=1) 15 | axis[0].plot(np.arange(0, train_kl.shape[0]), train_kl.close) 16 | axis[1].plot(np.arange(0, test_kl.shape[0]), test_kl.close) 17 | plt.show() 18 | 19 | def plot_buy_sell_train_test(train_kl, test_kl): 20 | ''' 21 | 可视化训练数据,测试数据,买入水平线,卖出水平线 22 | :param train_kl: 23 | :param test_kl: 24 | :return: 25 | ''' 26 | # 计算均值和方差 27 | train_close_mean = train_kl.close.mean() 28 | train_close_std = train_kl.close.std() 29 | 30 | # 构造买出信号阈值 31 | buy_signal = train_close_mean - (train_close_std / 3) 32 | # 构造卖出信号阈值 33 | sell_signal = train_close_mean + (train_close_std / 3) 34 | 35 | # 可视化 36 | train_kl.close.plot() 37 | # 画出买入信号 38 | plt.axhline(buy_signal, color='r', lw=3) 39 | # 画出卖出信号 40 | plt.axhline(sell_signal, color='g', lw=3) 41 | # 画出均值线 42 | plt.axhline(train_close_mean, color='black', lw=1) 43 | # 添加图例 44 | plt.legend(['train_close', 'buy_signal:' + str(buy_signal), 'sell_signal:' + str(sell_signal), 45 | 'mean:' + str(train_close_mean)], loc='best') 46 | plt.title('Training') 47 | plt.show() 48 | 49 | # 在后一年数据上画图 50 | test_kl.close.plot() 51 | # 画出买入信号 52 | plt.axhline(buy_signal, color='r', lw=3) 53 | # 画出卖出信号 54 | plt.axhline(sell_signal, color='g', lw=3) 55 | # 画出均值线 56 | plt.axhline(train_close_mean, color='black', lw=1) 57 | # 添加图例 58 | plt.legend(['train_close', 'buy_signal:' + str(buy_signal), 'sell_signal:' + str(sell_signal), 59 | 'mean:' + str(train_close_mean)], loc='best') 60 | plt.title('BackTest') 61 | plt.show() 62 | 63 | 64 | 65 | if __name__ == '__main__': 66 | kl_pd = ABuSymbolPd.make_kl_df('AGYS', n_folds=2) 67 | # 1、得到训练数据测试数据 68 | train_kl = kl_pd[:252] 69 | test_kl = kl_pd[252:] 70 | 71 | # 2、计算均值和方差 72 | train_close_mean = train_kl.close.mean() 73 | train_close_std = train_kl.close.std() 74 | 75 | # 3、计算阈值 76 | # 构造买出信号阈值 77 | buy_signal = train_close_mean - (train_close_std / 3) 78 | # 构造卖出信号阈值 79 | sell_signal = train_close_mean + (train_close_std / 3) 80 | 81 | # 4、在测试集中设置买入卖出点 82 | # 寻找测试数据中满足买入条件的时间序列 83 | buy_index=test_kl[test_kl['close'] <= buy_signal].index 84 | # 把这些点设置为1,表示可以持有 85 | test_kl.loc[buy_index,'signal']=1 86 | # 寻找测试数据中满足卖出条件的时间序列 87 | sell_index=test_kl[test_kl.close >=sell_signal].index 88 | # 把这些点设置为1,表示该卖了 89 | test_kl.loc[sell_index,'signal']=0 90 | # 打印 91 | print(test_kl) 92 | 93 | # 5、处理数据 94 | # 另外,这里假设是全仓操作,一旦买了,就all in;一旦卖了,也就all out 95 | # 添加数据列 keep 96 | test_kl['keep']=test_kl['signal'] 97 | # 令keep列中的缺失值,按照'ffill' 就是将缺失值按照前面一个值进行填充。 98 | test_kl['keep'].fillna(method='ffill',inplace=True) 99 | 100 | # 6、计算基准收益 101 | test_kl['benchmark_profit']=test_kl.close/test_kl.close.shift(1)-1 102 | 103 | # 7、计算持有股票的时候的收益 104 | test_kl['trend_profit']=test_kl.benchmark_profit*test_kl.keep 105 | 106 | # 注意,上面的计算值只是每天的收益,真正计算时,需要用累加值来算 107 | # 8、可视化,这个图告诉我们,它没有让利润奔跑啊 108 | fig,axis=plt.subplots(ncols=1,nrows=2) 109 | axis[0].plot(np.arange(0,test_kl.shape[0]),test_kl.close) 110 | axis[1].plot(np.arange(0,test_kl.shape[0]),test_kl.benchmark_profit.cumsum()) 111 | axis[1].plot(np.arange(0,test_kl.shape[0]),test_kl.trend_profit.cumsum()) 112 | axis[1].legend(['benchmark','trend_profit']) 113 | plt.show() 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /Section2/Section2_3_1.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | from collections import OrderedDict 3 | 4 | 5 | class StockTradeDays(object): 6 | # 构造函数 7 | def __init__(self, price_array, start_date, date_array=None): 8 | # 初始化私有的价格序列,日期序列和涨幅序列 9 | self.__price_array = price_array 10 | self.__date_array = self.__init_days(start_date, date_array) 11 | self.__change_array = self.__init_change() 12 | # 初始化股票序列 13 | self.stock_dict = self._init_stock_dict() 14 | 15 | def __init_change(self): 16 | ''' 17 | protected方法: 18 | 从price_array中生成涨幅 19 | 20 | :return: change_array,涨幅 21 | ''' 22 | price_float_array=[float(price_str) for price_str in self.__price_array] 23 | # 形成两个错开的价格队列 24 | pp_array=[(price1,price2) for price1,price2 in zip(price_float_array[:-1],price_float_array[1:])] 25 | # 计算涨幅 26 | change_array=[round((b-a)/a,3) for a,b in pp_array] 27 | # 将第一天涨幅设置为0 28 | change_array.insert(0,0) 29 | return change_array 30 | 31 | def __init_days(self,start_date,date_array): 32 | ''' 33 | protected方法 34 | :param start_date: 给定初始日期 35 | :param date_array: 日期序列 36 | :return: 37 | ''' 38 | if date_array is None: 39 | # 如果初始日期为空,那么就用start_date和price_array来确定初始日期 40 | date_array=[str(start_date+ind) for ind,_ in enumerate(self.__price_array)] 41 | else: 42 | # 如果外部给了时间序列,那就转换成str存好 43 | date_array=[str(date) for date in date_array] 44 | return date_array 45 | 46 | def _init_stock_dict(self): 47 | ''' 48 | 试用namedtuple,orderdict将结果合并 49 | :return: 50 | ''' 51 | stock_namedtuple=namedtuple('stock',('date','price','change')) 52 | stock_dict=OrderedDict((date,stock_namedtuple(date,price,change)) for date,price,change in 53 | zip(self.__date_array,self.__price_array,self.__change_array)) 54 | return stock_dict 55 | 56 | def filter_stock(self,want_up=True,want_calc_sum=False): 57 | ''' 58 | 筛选结果集 59 | :param want_up: 是否筛选上涨 60 | :param want_calc_sum: 是否计算涨跌和 61 | :return: 62 | ''' 63 | # <为真时的结果> if <判定条件> else <为假时的结果> 64 | filter_func=(lambda p_day:p_day.change>0) if want_up else(lambda p_day:p_day.change<0) 65 | 66 | # 来对其进行筛选 67 | # 因为是namedtuple,所以可以用.change取到chagne的值 68 | want_days=list(filter(filter_func,self.stock_dict.values())) 69 | 70 | # 如果不计算上涨和,那么就把哪几天上涨的范湖 71 | if not want_calc_sum: 72 | return want_days 73 | 74 | change_sum=0.0 75 | for day in want_days: 76 | change_sum+=day.change 77 | return change_sum 78 | 79 | def __str__(self): 80 | return str(self.stock_dict) 81 | 82 | __repr__=__str__ 83 | 84 | def __iter__(self): 85 | ''' 86 | 使用生成器来进行迭代 87 | :return: 88 | ''' 89 | for key in self.stock_dict: 90 | yield self.stock_dict[key] 91 | 92 | def __getitem__(self,ind): 93 | ''' 94 | 拿到某一天的交易信息 95 | :param ind: 96 | :return: 97 | ''' 98 | date_key=self.__date_array[ind] 99 | return self.stock_dict[date_key] 100 | 101 | def __len__(self): 102 | return len(self.stock_dict) 103 | 104 | if __name__ == '__main__': 105 | price_array = '30.14,29.58,26.36,32.56,32.82'.split(',') 106 | date_base = 20170118 107 | # 从StockTradeDays类初始化一个实例对象trade_days,内部会调用__init__ 108 | trade_days = StockTradeDays(price_array, date_base) 109 | # 打印对象信息 110 | # print(trade_days) 111 | for day in trade_days: 112 | print(day) 113 | 114 | print(trade_days.filter_stock()) 115 | 116 | # 因为可迭代,一定程度上可以当做是list来用 117 | print(trade_days[-1]) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QuantantiveSystem 2 | 3 | 标签(空格分隔): 量化交易 4 | 5 | 《量化交易之路》代码学习 6 | 7 | 总结一下,书中最重要的内容,主要分为以下这么几个部分。 8 | 9 | ### 1、第一部分:择时系统开发 10 | 在这一部分中,主要高速我们这么几件事情: 11 | 12 | 1. 什么时候买入股票,什么时候卖出股票。(择时) 13 | 2. 买入股票时,应该怎么给股票报价;卖出股票时,应该怎么给股票报价。(定价) 14 | 3. 假如我有本金X元,买次买股票,应该花多少钱呢?(仓位控制) 15 | 16 | #### 1.1 择时 17 | 这一部分,书中主要介绍了《海龟交易法则》中的两种策略: 18 | 19 | 1. 趋势跟踪 20 | 2. 均值回归 21 | 22 | 趋势跟踪: 23 | 24 | 基本思路:认为股票在一段时间内的上涨,意味着它在之后也会上涨。 25 | 1. 当第x天收盘后,如果当天的收盘价,高于前k天(不含第x天)的最高价,那么在第x+1天买入。 26 | 2. 当第x天收盘后,如果当天的收盘价,低于前k天(不含第x天)的最低价,那么在第x+1天卖出。 27 | 特点: 28 | 1.导致胜率较低。 29 | 2.每次的盈利高于每次的亏损 30 | 31 | 均值回归: 32 | 33 | 基本思路:认为股票的上涨和下跌是暂时的,股票的票价是围绕一个价值上下波动的 34 | 1. 将历史数据分为以某个时间点分为两部分,前半部分作为训练集,后半部分作为回测集。 35 | 2. 计算训练集收盘价的均值mean,和标准差std。我们以mean+std作为卖出的价格节点,mean-std作为买入的价格节点。 36 | 4. 当价格低于mean-std时,我们就买入股票,并保持持有,直到价格高于mean+std; 37 | 5. 当价格高于mean+std时,我们就卖出股票,保持空仓,直到价格再一次低于mean-std。 38 | 特点: 39 | 1.属于统计套利模型,大机构高频交易可能更加适合。 40 | 41 | #### 1.2 定价 42 | 43 | 这里定价模型采用的是相对简单的方式: 44 | 45 | 1.当我在第x天确认,在第x+1天需要买入/卖出时。 46 | 2.我的价格选择为第x天的最高价high,与第x天的最低价low的平均值,即p=(high+low)/2 47 | 48 | #### 1.3 仓位控制 49 | 保证存活是最重要的! 50 | 51 | 相比于择时策略是让我们赚更多的钱,仓位控制是让我们在可能亏得时候不至于亏太多。 52 | 53 | 具体而言,仓位控制,采用的是在股票交易领域修正后的凯利公式: 54 | 55 | f=Pwin-【Ploss/(收益期望/亏损期望)】 56 | 57 | 计算出来的f,就是仓位比例,比如我有100w本金,f算出来是0.1,那么我每次买,就应该只买10w。 58 | 59 | 关于如何计算Pwin,Ploss以及收益期望,亏损期望,会在下一章补上。 60 | 61 | ### 2、第二部分:选股 62 | 63 | 不是所有股票都有投资价值的,选股的目的就是从股票中挑选出有价值的股票,来进行投资。 64 | 65 | 这个其实和代码关系不大,更重要的是策略。 66 | 67 | 那么对于策略而言,书中介绍的其实就两种: 68 | 69 | 1. 基于趋势。 70 | 2. 基于最低最高值。 71 | 72 | 基于趋势选股: 73 | 74 | 1. 当拿到股票的历史数据后,我用直线去拟合这部分数据。 75 | 2. 若拟合直线的斜率为正,表示它的总趋势是上涨,值得买入;相反,则不值得入手。 76 | 77 | 基于最低最高值选股: 78 | 79 | 1. 如果在历史数据中,它的股价一直在某个价格以上,那么值得买入。 80 | 81 | ### 3、第三部分:裁判策略 82 | **裁判系统,是通过总结回测结果中失败的交易的模式,得到什么时候应该阻止买入信号生效的系统。** 83 | 84 | 其实裁判可以看做是择时的补充,择时告诉我们什么时候该买,裁判告诉我们什么时候不该买。 85 | 86 | 传统上,我们可能需要通过手工编写策略,来完成裁判系统。 87 | 88 | 但是,现在,我们可以通过机器学习的方法,得到交易失败的一般模式,并提取该模式的特点,来进行交易的拦截。 89 | 90 | #### 3.1 主裁 91 | 想象一下,足球场上,有的裁判是看是否越位,有的裁判是看你是否撞人,不同的裁判看得特征点不同。 92 | 93 | 那么,在这里也是一样,不同的裁判通过不同的特征,去寻找交易失败(即这次交易亏损)的原因。 94 | 95 | ##### 3.1.1 角度主裁 96 | 97 | * 1)在这里,我们选择每次买入时,往回看21,42,69,252天的拟合角度。 98 | ![image_1c411ha0d1cjmmv1u1lfgcm589.png-45.5kB][1] 99 | 其中,result=1表示此次交易成功,result=0表示此次交易亏损。 100 | 101 | * 2)然后,我们使用GMM聚类,将这些数据聚成40~84个。(相当于要跑GMM 44次) 102 | ![image_1c411mv331p9m1ctc1gb41q6je2cm.png-61.8kB][2] 103 | 54_32表示它是将所有数据分成54类中的第32个簇 104 | lcs:簇中样本总数, 105 | lrs:簇中样本失败率, 106 | lps:交易获利比例综合, 107 | lms:每笔交易的平均获利。 108 | 109 | * 3)选择出这些所有类别中,亏损交易所占比例(失败率)最多的那一个簇,查看它的角度特征。(这里发现是第62_47号簇) 110 | ![image_1c411uskvqj66ct35s1puj164o2j.png-82.4kB][3] 111 | 112 | * 4)对该簇失败率这么高进行宏观分析,如若发现该簇的21角度平均值远高于总体的21日角度均值,而60日角度均值远低于总体的60日角度均值。我们就可以解释说,这些交易是因为在过去3个月股票下跌的时候,因为短时快速拉升而选择买入,终将自己套牢而亏损。 113 | 114 | * 5)我们发现了亏损的原因,那么如何来使用这个原因来减少亏损呢?现在,我们知道了哪个簇里面的交易不能执行,那么,我们只需要查看新进来的交易是否属于这个簇就好。 115 | 116 | * 6)这里,我们认定说对于将数据分为62个簇,并且其中的第47号簇不能要。我们就对新进来的数据,对他进行分类,看他数据62个簇中的哪一个,如果属于47号簇,那就不能要。 117 | 118 | * 7)这个时候,ml的思想促使我们思考,只选择亏损最大的簇作为丢失好吗?在簇评价中,我们使用ics,irs,ips和ims进行评价,其中ics是数量,可以不用在意。那么,剩下三个参数,是否存在一个最优组合,选出一些需要被剔除的簇,使得剩下的交易收益最高? 119 | 120 | * 8)我们对irs,ips和ims使用grid-search方法,使用(拦截交易个数/总交易个数)*(拦截交易的失败率-拦截交易的成功率)作为评分,得到了新的参数,irs,ips和ims。 121 | 122 | * 9)那么,任何满足irs,ips和ims条件的簇,都需要被剔除,不再执行。这样,新进来的交易也需要来判断,是否属于这些簇,从而决定是否被拦截。 123 | 124 | ##### 3.1.2 跳空主裁(同理 125 | ##### 3.1.3 波动拦截主裁 (同理 126 | ##### 3.1.4 价格主裁 (同理 127 | 128 | ####3.2 提升 129 | 有时候发现,这些裁判带来的提升并不是特别高,那么,可以考虑从以下几个方面来加强: 130 | 131 | 1. 寻找更多的特征,构造更多的裁判 132 | 2. 让裁判之间进行更复杂的综合裁决,让裁判之间配合 133 | 3. 为每个裁判赋予不同的权重,进行综合裁决 134 | 4. 用更多的数据来提升裁判的效用。 135 | 5. 添加边裁。 136 | 137 | ### 4、金融学知识:度量 138 | 这部分其实是介绍了一些金融学上面的知识,用于度量股票的一些走势。 139 | 比如: 140 | 141 | * 1)策略收益:(股票卖出总价-股票买入总价)/(股票买入总价)*100% 142 | * 2)策略年化收益:(一年的交易天数/策略执行天数)*策略收益 143 | * 3)平均获利期望:一个统计周期内,执行策略产生盈利的所有交易的平均收益。每次收益按照百分比算,比如只盈利的两次,第一次盈利10%,第二次30%,那么平均收益就是20%。 144 | * 4)平均亏损期望:同理。 145 | 146 | 还有一些专业的金融量: 147 | 148 | * 1)夏普比率:每承担一份风险,可以获得多少超额报仇;这个值越大,说明获得的超额报酬越高,策略越好。 149 | 150 | * 2)信息比率:表示单位主动风险带来的超额收益,大信息比率的策略比小的好 151 | 152 | * 3)策略波动:该值越高,表示风险越大 153 | 154 | * 4)阿尔法:表示交易者从市场获取的与市场走势无关的交易回报 155 | 156 | * 5)贝塔:量化了交易风险。 157 | 158 | 另外,记住,一般的量化交易策略的有效期,大概在2~7个月,所以要不停开发不停尝试。 159 | 而且,能够从别人那里看到的策略一般都是失效了的,所以还要修炼内容,开发自己的策略啊。 160 | 161 | [1]: http://static.zybuluo.com/w460461339/clv51j8g2d0oskl6f37mpds9/image_1c411ha0d1cjmmv1u1lfgcm589.png 162 | [2]: http://static.zybuluo.com/w460461339/wmxrgzia6kl7gub3o11ax89k/image_1c411mv331p9m1ctc1gb41q6je2cm.png 163 | [3]: http://static.zybuluo.com/w460461339/cxpb1tdr134rtjnwof9lb42w/image_1c411uskvqj66ct35s1puj164o2j.png -------------------------------------------------------------------------------- /Section3/Section3_All.py: -------------------------------------------------------------------------------- 1 | import timeit 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import scipy.stats as scs 5 | 6 | 7 | if __name__ == '__main__': 8 | 9 | # 1、关于作用点 10 | normal_list=[1,1,1,1,1] 11 | np_list=np.ones(5) 12 | # 普通数组的乘法是作用在数组这个整体上 13 | print(normal_list*3) 14 | # np数组的乘法是作用在数组的每个元素上 15 | print(np_list*3) 16 | 17 | # 2、关于初始化操作 18 | # 初始化的集中操作 zeros,ones,empyt,eye,ones_like,zeros_like 19 | # 另外,可以从normal_list得到np的array 20 | normal=[[1,2,3],[4,5,6]] 21 | np_array=np.array(normal) 22 | print(type(np_array)) 23 | print(np_array) 24 | 25 | # 等间距的生成np。array 26 | # 计算公式是linspace(a,b,c),开头是a,结尾是b,中间每个数为a+n*(b-a)/(c-1),n属于1~c-1 27 | equal_step=np.linspace(0,9,5) 28 | print(equal_step) 29 | 30 | # 3、关于正态分布 31 | ###我们生成一下正态分布数据 32 | stock_cnt=200 33 | view_days=504 34 | # 关于正态分布 35 | # 这里注意下生成的正态分布的股票数据 36 | # 是指,股票每天的票价都是从符合正太分布的数列中随机选取 37 | # 其概率密度函数符合单峰的状态 38 | # 而我们把股票的价格画曲线,是看不到单峰图的 39 | stock_day_change=np.random.standard_normal((stock_cnt,view_days)) 40 | 41 | # 4、关于切片 42 | # 对于[0:2,0:5],表示获得第一条和第二条股票的前5天数据,返回的可以看做是一个list套list的结构 43 | # 它的类型是一个np.array,array中每个元素还是一个np。array 44 | print(stock_day_change[0:2,0:5]) 45 | # 对于[0:1,:][0],这个操作将最外层的list去掉,表示只取第一条股票 46 | # 它是一个np.array类型的数据,里面每个元素是一个integer 47 | stock_one=stock_day_change[0:2,:][0] 48 | print(stock_one[:5]) 49 | 50 | # 简单画图 51 | # plt.plot(stock_one) 52 | # plt.show() 53 | 54 | 55 | # 5、关于引用 56 | # 发现当我改变了stock_one中数据后,stock_day_change中的数据也变化 57 | # 因为在np.array的切片返回值中,每个元素都是一个引用 58 | # 所以改变切片的值也会改变原来array的值 59 | # 只有试用copy操作,才不会改变 60 | stock_one=stock_one.copy() 61 | stock_one[0]=1 62 | print(stock_day_change[0:1,0:2]) 63 | print(stock_one[0:2]) 64 | 65 | 66 | # 6、关于数据转化和规整 67 | # 其实就是使用了对于numpy中的array而言,操作都会作用在每个元素上,这一个特性 68 | stock_temp=stock_day_change[0:2,0:5].copy() 69 | # 注意这里的astype没有对stock_temp进行操作,而是构建了新的array 70 | # 所以不会影响原array 71 | print(stock_temp.astype(int)) 72 | # 这个np.round也不会影响原数组 73 | # 注意这里用的是np.round,而不是python自带的round 74 | print(np.round(stock_temp,2)[0][0]) 75 | print(stock_temp) 76 | 77 | # 7、关于mask 78 | # 先通过对array中每一个元素进行比较操作,返回一个和原array大小相同,但全部由Ture,False组成的array 79 | # 在利用这个新的array去和原数组进行操作,得到那些True位置上对应的数 80 | temp_mask=stock_temp>0.5 81 | print(temp_mask) 82 | print(stock_temp[temp_mask]) 83 | # 在一行内完成:1.取到大于1或者小于-1的数,并将它们赋值为0 84 | print(stock_temp) 85 | # 注意这里逻辑符号只能用 |,&,不能用and或者or 86 | stock_temp[(stock_temp>1) | (stock_temp<-1)]=0 87 | print(stock_temp) 88 | 89 | # 8、关于通用序列函数 90 | stock_temp=stock_day_change[0:2,0:5].copy() 91 | # 8.1 判断序列中元素是否都是true 92 | print(np.all(stock_temp>0)) 93 | # 8.2 判断序列中元素是否存在大于0的 94 | print(np.any(stock_temp>0)) 95 | # 8.3 对于两个序列中的元素进行两两比较,留下较大(或者较小的)序列 96 | stock_last2=stock_day_change[-2:,0:5] 97 | print(stock_temp) 98 | print(stock_last2) 99 | print(np.maximum(stock_temp,stock_last2)) 100 | print(np.minimum(stock_temp,stock_last2)) 101 | 102 | # 8.4 重复的值只留下一个,和set功能类似 103 | print(np.unique(np.array([1,1,0,0,0,2]))) 104 | # 8.5 计算差值,axis=0表示比较行之间不同,axis=1表示比较列之间不同 105 | # 默认比较列,即第0列和第一列,第一列和第二列... 106 | print(stock_temp) 107 | print(np.diff(stock_temp)) 108 | print(np.diff(stock_temp,axis=0)) 109 | 110 | # 8.6 np的三目表达式,如果条件成立,执行状况1,否则执行状况2 111 | # 条件成立就赋值为1,不然赋值为0 112 | print(np.where(stock_last2>0.5,1,0)) 113 | # 条件成立就赋值为1,不就保持怨言 114 | print(np.where(stock_last2>0.5,1,stock_last2)) 115 | # 复合条件判断,比如说and 116 | print(np.where(np.logical_and(stock_last2>0.5,stock_last2<1),1,0)) 117 | 118 | # 9、关于统计函数 119 | stock_day_four = stock_day_change[0:4, 0:4] 120 | # 9.1 前4只股票分别的最大涨幅 121 | print(stock_day_four) 122 | # axis什么都不写,表示统计全局最大值 123 | # 这里的axis需要注意,axis=0,表示返回结果以行向量的形式返回,那么计算的就是每一列的最大值 124 | print(np.max(stock_day_four,axis=0)) 125 | # axis=1,表示计算结果以列向量形式返回,计算的是每一行的最大值 126 | print(np.max(stock_day_four,axis=1)) 127 | # 类似的操作还有 均值mean,标准差std,最小值min等等 128 | # 当我们不想获得值,而是想获得哪一天得到极值,可以用argmax,argmin 129 | print(np.argmax(stock_day_four,axis=1)) 130 | 131 | # 10、关于正态分布 132 | stock_day_one=stock_day_change[0] 133 | # 表示画直方图,一共画50个方块,对应于图中蓝色部分 134 | # 每一个方块表示在这个范围内的点有多少 135 | plt.hist(stock_day_one,bins=50,normed=True) 136 | # 默认分成50个点 137 | fit_linspace=np.linspace(stock_day_one.min(),stock_day_one.max(),num=50) 138 | # 下面是用正态分布取去拟合这个直方图 139 | day_one_mean=np.mean(stock_day_one) 140 | day_one_std=np.std(stock_day_one) 141 | # 这里注意,pdf就是图中黄色曲线上的点的值,不代表任何意思,不代表概率值! 142 | # 正态分布是一个概率密度函数,只有曲线下的面积,才代表概率 143 | pdf=scs.norm(day_one_mean,day_one_std).pdf(fit_linspace) 144 | plt.plot(fit_linspace,pdf) 145 | # plt.show() 146 | 147 | # 简单验证了一下,用横坐标之间的差表示宽 148 | # 纵坐标两点间的均值表示高,简单计算了一下曲线下的面积,差不多是1,就是概率 149 | # 所以曲线上的点,其实真心没什么意思 150 | width=np.diff(fit_linspace,axis=0) 151 | height=(pdf[1:]+pdf[:-1])/2 152 | print(sum(width*height)) 153 | 154 | # 11、简单例子,正态分布买入策略 155 | # 最后50天作为验证数据 156 | keep_days=50 157 | # 取出每只股票前454天的数据(因为一共生成504天) 158 | stock_day_change_test=stock_day_change[:,:-keep_days] 159 | print(np.shape(stock_day_change_test)) 160 | # 计算所有股票的总涨跌幅,选出跌的最多的三只股票的序号,所以用的argsorted(就是总和最小的三只) 161 | stock_array=np.argsort(np.sum(stock_day_change_test,axis=1))[:3] 162 | 163 | def show_buy_lower(stock_ind): 164 | ''' 165 | :param stock_ind:股票序号 166 | :return: 167 | ''' 168 | # 设置一行两列的图表 169 | _,axs=plt.subplots(nrows=1,ncols=2) 170 | # 绘制前454天走势图 171 | axs[0].plot(np.arange(0,view_days-keep_days),stock_day_change_test[stock_ind].cumsum()) 172 | 173 | # 这个cumsum是求累积和 174 | # 比如cumsum([1,3,5),返回值是[1,4,9] 175 | cs_buy=stock_day_change[stock_ind][view_days-keep_days:].cumsum() 176 | # 绘制从454天到504天的走势图 177 | axs[1].plot(np.arange(view_days-keep_days,view_days),cs_buy) 178 | plt.show() 179 | return cs_buy[-1] 180 | 181 | profit=0 182 | for stock_ind in stock_array: 183 | profit+=show_buy_lower(stock_ind) 184 | print('总盈亏为%s'%profit) 185 | # 简答总结一些上面的策略为什么会盈利: 186 | # 1. 我们生成的涨跌幅数据是标准的正态分布。均值为0,方差为1 187 | # 2. 表示,每次涨跌幅小于0,即股票下跌的概率为50%,上涨也为50% 188 | # 3. 那么,对于由n个独立同分布(符合标准正态分布)的随机变量,生成的长为n的数列 189 | # 4. 如果前k个值得和小于0,那么后n-k个值得和大于0的概率会比较大。(这是大数定理把,因为均值要是0) 190 | 191 | 192 | # 12、基于伯努利的交易 193 | gamblers=100 194 | 195 | def casino(win_rate,win_once=1,loss_once=1,commission=0.01): 196 | ''' 197 | 198 | :param win_rate: 获胜概率 199 | :param win_once: 每次赢的收益 200 | :param loss_once: 每次输的损失 201 | :param commission: 手续费 202 | :return: 203 | ''' 204 | my_money=10000 205 | play_cnt=100000 206 | commission=commission 207 | for _ in np.arange(0,play_cnt): 208 | w=np.random.binomial(1,win_rate) 209 | if w: 210 | my_money+=win_once 211 | else: 212 | my_money-=loss_once 213 | 214 | my_money-=commission 215 | 216 | if my_money<0: 217 | break 218 | 219 | return my_money 220 | 221 | # 加速跑一下 222 | import numba as nb 223 | casino=nb.jit(casino) 224 | 225 | heaaven_moneys=[casino(0.5,commission=0) for _ in np.arange(0,gamblers)] 226 | cheat_moneys=[casino(0.4,commission=0) for _ in np.arange(0,gamblers)] 227 | commission_moneys=[casino(0.5,commission=0.01) for _ in np.arange(0,gamblers)] 228 | 229 | plt.hist(heaaven_moneys,bins=30) 230 | plt.show() -------------------------------------------------------------------------------- /Section7/Section7_2_1.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import pandas as pd 4 | 5 | if __name__ == '__main__': 6 | trade_day = 100 7 | 8 | 9 | # 1、涨跌规则1 10 | def gen_stock_price_array(): 11 | ''' 12 | 这里的涨跌规则是: 13 | 1.前一天上涨,那么今天也涨;前一天下跌,那么今天也跌 14 | 2.上涨和下跌都只有5% 15 | :return: 16 | ''' 17 | # 初始化每天都是1元 18 | price_array = np.ones(trade_day) 19 | # 生成100个交易日走势 20 | 21 | for ind in np.arange(0, trade_day - 1): 22 | # 以下代码是通过今天决定明天的股价 23 | if ind == 0: 24 | # binomial三个参数为n,p,size,表示成功次数,成功概率和总实验数 25 | win = np.random.binomial(1, 0.5) 26 | else: 27 | win = price_array[ind] > price_array[ind - 1] 28 | 29 | if win: 30 | price_array[ind + 1] = (1 + 0.05) * price_array[ind] 31 | 32 | else: 33 | price_array[ind + 1] = (1 - 0.05) * price_array[ind] 34 | return price_array 35 | 36 | 37 | fig, axis = plt.subplots(ncols=2, nrows=1) 38 | price_array1 = gen_stock_price_array() 39 | price_array1_ex = gen_stock_price_array() 40 | axis[0].plot(np.arange(0, trade_day), price_array1) 41 | axis[1].plot(np.arange(0, trade_day), price_array1_ex) 42 | plt.show() 43 | 44 | # 2、涨跌规则第二部分 45 | trade_day = 252 46 | 47 | 48 | def gen_stock_price_array2(): 49 | ''' 50 | 第二阶段有252天,用上一阶段的最后一天的值初始化这个252天 51 | 并把这252天接在上一阶段之后 52 | :return: 53 | ''' 54 | price_array = np.concatenate((price_array1, np.ones(trade_day) * price_array1[-1]), axis=0) 55 | 56 | # 开始处理这一阶段的股价: 57 | for ind in np.arange(len(price_array1) - 1, len(price_array) - 1): 58 | # 获取当前交易日(在内)的前4天数据 59 | last4 = price_array[ind - 3:ind + 1] 60 | # 如果还没有到尽头,且连涨三天 61 | if len(last4) == 4 and last4[-1] > last4[-2] and last4[-2] > last4[-3] and last4[-3] > last4[-4]: 62 | # 那么下跌概率为0.55,即继续涨的概率为0.45 63 | win = np.random.binomial(1, 0.45) 64 | elif len(last4) == 4 and last4[-1] < last4[-2] and last4[-2] < last4[-3] and last4[-3] < last4[-4]: 65 | # 如果连跌三天 66 | win = np.random.binomial(1, 0.80) 67 | else: 68 | # 当天涨跌仍然只和前一天相关 69 | win = price_array[ind] > price_array[ind - 1] 70 | 71 | if win: 72 | price_array[ind + 1] = (1 + 0.05) * price_array[ind] 73 | 74 | else: 75 | price_array[ind + 1] = (1 - 0.05) * price_array[ind] 76 | 77 | return price_array 78 | 79 | 80 | import itertools 81 | 82 | fig, axis = plt.subplots(ncols=3, nrows=3) 83 | ax_list = list(itertools.chain.from_iterable(axis)) 84 | # 趋势太小看不清楚 85 | for ind in range(0, len(ax_list)): 86 | ax_list[ind].plot(gen_stock_price_array2()) 87 | plt.show() 88 | # 看单个的就很清楚了 89 | price_array2 = gen_stock_price_array2() 90 | plt.plot(price_array2) 91 | plt.show() 92 | 93 | # 3、股票涨跌的第三种情况 94 | trade_day = 252 * 3 95 | 96 | 97 | def gen_stock_price_array3(): 98 | ''' 99 | 第三阶段有252天,用上一阶段的最后一天的值初始化这个252天 100 | 并把这252天接在上一阶段之后 101 | :return: 102 | ''' 103 | price_array = np.concatenate((price_array2, np.ones(trade_day) * price_array2[-1]), axis=0) 104 | 105 | # 开始处理这一阶段的股价: 106 | for ind in np.arange(len(price_array1) - 1, len(price_array) - 1): 107 | # 获取当前交易日(在内)的前4天数据 108 | last4 = price_array[ind - 3:ind + 1] 109 | # 如果还没有到尽头,且连涨三天 110 | if len(last4) == 4 and last4[-1] > last4[-2] and last4[-2] > last4[-3] and last4[-3] > last4[-4]: 111 | # 那么下跌概率为0.55,即继续涨的概率为0.45 112 | win = np.random.binomial(1, 0.45) 113 | elif len(last4) == 4 and last4[-1] < last4[-2] and last4[-2] < last4[-3] and last4[-3] < last4[-4]: 114 | # 如果连跌三天 115 | # 那么上涨的概率是80% 116 | win = np.random.binomial(1, 0.80) 117 | # 如果这样都没涨 118 | if not win: 119 | # 股灾,股价直接跌50% 120 | price_array[ind + 1] = (1 - 0.5) * price_array[ind] 121 | # 如果股价太低 122 | if price_array[ind + 1] <= 0.1: 123 | # 股价太低,直接退市 124 | price_array[ind + 1:] = 0 125 | break 126 | else: 127 | # 进入下一个循环,不用进行后面的操作了 128 | continue 129 | else: 130 | # 当天涨跌仍然只和前一天相关 131 | win = price_array[ind] > price_array[ind - 1] 132 | 133 | if win: 134 | price_array[ind + 1] = (1 + 0.05) * price_array[ind] 135 | 136 | else: 137 | price_array[ind + 1] = (1 - 0.05) * price_array[ind] 138 | 139 | if price_array[ind + 1] <= 0.1: 140 | # 股价太低,直接退市 141 | price_array[ind + 1:] = 0 142 | break 143 | 144 | return price_array 145 | 146 | 147 | price_array3 = gen_stock_price_array3() 148 | plt.plot(gen_stock_price_array3()) 149 | plt.show() 150 | 151 | ''' 152 | 上面模拟了一只股票3年的股价状况 153 | 下面我们用不同的交易策略在这上面进行测试,看看哪个交易策略能够盈利。 154 | ''' 155 | 156 | 157 | def execute_trade(cash, buy_rate): 158 | ''' 159 | 160 | :param cash: 最初持有现金数目 161 | :param buy_rate: 每次买入所花的钱,即持仓比例 162 | :return: 163 | ''' 164 | commission = 5 # 手续费 165 | stock_cnt = 0 # 持有股票数 166 | keep_day = 0 # 持股天数 167 | 168 | # 资产序列 169 | capital = [] 170 | # 从price_array3的第352天后开始 171 | for ind in np.arange(252, len(price_array3) - 1): 172 | if stock_cnt > 0: 173 | # 如果持有股票,那么增加持股天数 174 | keep_day += 1 175 | if stock_cnt > 0 and keep_day == 3: 176 | # 如果持有股票,且持有了3天 177 | # 那么卖出股票 178 | cash += price_array3[ind] * stock_cnt 179 | # 扣除手续费 180 | cash -= commission 181 | # 如果资产为负的 182 | if cash <= 0: 183 | capital.append(0) 184 | print('爆仓了') 185 | break 186 | 187 | # 获取包括今天在内的5个交易日的数据 188 | last5 = price_array3[ind - 4:ind + 1] 189 | # 买入策略: 190 | # 1.当没有持有股票,且第一个交易日上涨,后三个交易日下跌,买入 191 | if stock_cnt == 0 and len(last5) == 5 \ 192 | and last5[1] > last5[0] \ 193 | and last5[2] < last5[1] \ 194 | and last5[3] < last5[2] \ 195 | and last5[4] < last5[3]: 196 | cash -= commission 197 | buy_cash = cash * buy_rate 198 | cash -= buy_cash 199 | # 计算持了多少股 200 | stock_cnt += buy_cash / price_array3[ind] 201 | 202 | if stock_cnt < 1: 203 | # 表示1股都买不起 204 | capital.append(0) 205 | print('爆仓') 206 | break 207 | keep_day = 0 208 | 209 | # 计算当前总资产 210 | capital.append(cash + (stock_cnt * price_array3[ind])) 211 | return capital 212 | 213 | 214 | pig_one_cash = 10000 215 | buy_rate = 1 216 | pig_one_captical = execute_trade(pig_one_cash, buy_rate) 217 | print('猪1的总资产:{}'.format(pig_one_captical[-1])) 218 | print('猪1的最高值:{}'.format(max(pig_one_captical))) 219 | plt.plot(pig_one_captical) 220 | plt.show() 221 | 222 | pig_two_cash = 10000 223 | buy_rate = 0.6 224 | pig_two_captical = execute_trade(pig_two_cash, buy_rate) 225 | print('猪2的总资产:{}'.format(pig_two_captical[-1])) 226 | print('猪2的最高值:{}'.format(max(pig_two_captical))) 227 | plt.plot(pig_two_captical) 228 | plt.show() 229 | 230 | pig_trhee_cash = 10000 231 | buy_rate = 0.13 232 | pig_three_captical = execute_trade(pig_trhee_cash, buy_rate) 233 | print('猪3的总资产:{}'.format(pig_three_captical[-1])) 234 | print('猪3的最高值:{}'.format(max(pig_three_captical))) 235 | plt.plot(pig_three_captical) 236 | plt.show() 237 | -------------------------------------------------------------------------------- /Section2/Section2_3_2.py: -------------------------------------------------------------------------------- 1 | import six 2 | from abc import ABCMeta, abstractclassmethod 3 | from Section2.Section2_3_1 import StockTradeDays 4 | from abupy import ABuSymbolPd 5 | import itertools 6 | 7 | 8 | class TradeStrategyBase(six.with_metaclass(ABCMeta, object)): 9 | ''' 10 | 交易策略的抽象类 11 | ''' 12 | 13 | # *arg表示可以有任意多个非指定参数,比如day,price,等等 14 | # **kwargs表示可以有任意多个制定参数,比如isUp=False,isDown=True等等 15 | 16 | def but_strategy(self, *arg, **kwargs): 17 | # 买入的基类 18 | pass 19 | 20 | def sell_strategy(self, *arg, **kwargs): 21 | # 卖出的基类 22 | pass 23 | 24 | 25 | class TradeStrategy1(TradeStrategyBase): 26 | ''' 27 | 交易策略1:追涨,当股价上涨超过一个阈值(7%)时, 28 | 买入并持有s_keep_stock_threshold天 29 | ''' 30 | s_keep_stock_threshold = 20 31 | 32 | def __init__(self): 33 | ''' 34 | 初始化一些参数 35 | ''' 36 | self.keep_stock_day = 0 37 | self.__buy_change_threshold = 0.07 38 | 39 | def buy_strategy(self, trade_ind, trade_day, trade_days): 40 | if self.keep_stock_day == 0 and trade_day.change > self.__buy_change_threshold: 41 | # 当还没有持有股票 42 | # 并且股票上涨超过阈值时,买入股票 43 | self.keep_stock_day += 1 44 | elif self.keep_stock_day > 0: 45 | # 已经持有过股票,那么持有天数加1 46 | self.keep_stock_day += 1 47 | 48 | def sell_strategy(self, trade_ind, trade_day, trade_days): 49 | # 当持有股票天数超过规定天数时,卖出 50 | if self.keep_stock_day >= TradeStrategy1.s_keep_stock_threshold: 51 | self.keep_stock_day = 0 52 | 53 | # 相当于 buy_change_threshold的get方法 54 | @property 55 | def buy_change_threshold(self): 56 | return self.__buy_change_threshold 57 | 58 | # 相当于 buy_change_threshold的set方法 59 | @buy_change_threshold.setter 60 | def buy_change_threshold(self, buy_change_threshold): 61 | if not isinstance(buy_change_threshold, float): 62 | raise TypeError('buy_change_threshold must be float') 63 | self.__buy_change_threshold = round(buy_change_threshold, 2) 64 | 65 | 66 | class TradeStrategy2(TradeStrategyBase): 67 | ''' 68 | 交易策略2:均值回复策略,当股票连续连个交易日下跌 69 | 且下跌幅度超过默认的s_buy_change_threshold(-10%)时 70 | 买入股票并持有s_keep_stock_threshold(10)天 71 | 72 | ''' 73 | s_keep_stock_threshold = 10 74 | s_buy_change_threshold = -0.10 75 | 76 | def __init__(self): 77 | self.keep_stock_day = 0 78 | 79 | def buy_strategy(self, trade_ind, trade_day, trade_days): 80 | ''' 81 | :param trade_ind: 交易日下标 82 | :param trade_day: 当天交易信息 83 | :param trade_days: 交易信息序列 84 | :return: 85 | ''' 86 | if self.keep_stock_day == 0 and trade_ind >= 1: 87 | ''' 88 | 当手上还没有股票,且从第二天开始; 89 | 因为第一天没有前一天数据,所以只能从第二天开始 90 | ''' 91 | # 如果当天下跌 92 | today_down = trade_day.change < 0 93 | # 如果昨天下跌 94 | yesterday_down = trade_days[trade_ind - 1].change < 0 95 | # 两天的总跌幅 96 | down_rate = trade_day.change + trade_days[trade_ind - 1].change 97 | # 因为是负数,所以注意负号 98 | if today_down and yesterday_down and down_rate <= TradeStrategy2.s_buy_change_threshold: 99 | self.keep_stock_day += 1 100 | elif self.keep_stock_day > 0: 101 | self.keep_stock_day += 1 102 | 103 | def sell_strategy(self, trade_ind, trade_day, trade_days): 104 | if self.keep_stock_day > TradeStrategy2.s_keep_stock_threshold: 105 | self.keep_stock_day = 0 106 | 107 | @classmethod 108 | def set_keep_stock_threshold(cls, keep_stock_threshold): 109 | cls.s_keep_stock_threshold = keep_stock_threshold 110 | 111 | @staticmethod 112 | def set_buy_change_threshold(buy_change_threshold): 113 | TradeStrategy2.s_buy_change_threshold = buy_change_threshold 114 | 115 | 116 | class TradeLoopBack(object): 117 | ''' 118 | 回测系统 119 | ''' 120 | 121 | def __init__(self, trade_days, trade_strategy): 122 | ''' 123 | :param trade_days: 交易数据序列 124 | :param trade_strategy: 交易策略 125 | ''' 126 | self.trade_days = trade_days 127 | self.trade_strategy = trade_strategy 128 | self.profit_array = [] 129 | 130 | def execute_trade(self): 131 | ''' 132 | 执行回测 133 | 回测的意义: 134 | https://www.investopedia.com/terms/b/backtesting.asp 135 | :return: 136 | ''' 137 | for ind, day in enumerate(self.trade_days): 138 | ''' 139 | 时间驱动 140 | ''' 141 | if self.trade_strategy.keep_stock_day > 0: 142 | # 如果当天持有股票,那么将当天的股票收入加入盈亏队列 143 | self.profit_array.append(day.change) 144 | 145 | # hasattr用来判断对象self.trade_strategy 是否实现了方法'buy_strategy' 146 | if hasattr(self.trade_strategy, 'buy_strategy'): 147 | # 执行买入策略,看看今天是否需要买入 148 | self.trade_strategy.buy_strategy(ind, day, self.trade_days) 149 | 150 | if hasattr(self.trade_strategy, 'sell_strategy'): 151 | # 执行卖出策略,看今天要不要卖 152 | self.trade_strategy.sell_strategy(ind, day, self.trade_days) 153 | 154 | 155 | def do_strateg1(trade_days): 156 | ''' 157 | 执行策略1 158 | :param trade_days: 159 | :return: 160 | ''' 161 | # 策略1 162 | trade_strategy1 = TradeStrategy1() 163 | 164 | # 执行回测 165 | trade_loop_back = TradeLoopBack(trade_days, trade_strategy1) 166 | trade_loop_back.execute_trade() 167 | 168 | # 查看回测结果 169 | print(trade_loop_back.profit_array) 170 | print(sum(trade_loop_back.profit_array)) 171 | 172 | # 绘图查看 173 | plot_array(trade_loop_back.profit_array) 174 | 175 | def do_strategy1_changeParameter(trade_days): 176 | ''' 177 | 执行策略1,并修改参数 178 | :param trade_days: 179 | :return: 180 | ''' 181 | # 修改策略 182 | trade_strategy1 = TradeStrategy1() 183 | # 上涨10%才买(10%不就涨停嘛。。基本很难看见,所以没法获利很正常 184 | # 没有一天是大于10%的。。。。换8%试试吧 185 | # 这里就利用property,来对值进行修改 186 | trade_strategy1.buy_change_threshold = 0.08 187 | 188 | # 执行回测 189 | trade_loop_back2 = TradeLoopBack(trade_days, trade_strategy1) 190 | trade_loop_back2.execute_trade() 191 | 192 | # 查看回测结果 193 | print(trade_loop_back2.profit_array) 194 | print(sum(trade_loop_back2.profit_array)) 195 | 196 | # 绘图查看 197 | plot_array(trade_loop_back2.profit_array) 198 | 199 | def do_strategy2(trade_days): 200 | ''' 201 | 执行策略2 202 | :param trade_days: 203 | :return: 204 | ''' 205 | # 第二套策略 206 | trade_strategy2 = TradeStrategy2() 207 | 208 | # 执行回测 209 | trade_loop_back3 = TradeLoopBack(trade_days, trade_strategy2) 210 | trade_loop_back3.execute_trade() 211 | 212 | # 查看回测结果 213 | print(trade_loop_back3.profit_array) 214 | print(sum(trade_loop_back3.profit_array)) 215 | 216 | # 绘图查看 217 | plot_array(trade_loop_back3.profit_array) 218 | 219 | def do_strategy_changeParameter(trade_days): 220 | ''' 221 | 执行策略2,并修改参数 222 | :param trade_days: 223 | :return: 224 | ''' 225 | # 修改第二套策略参数 226 | trade_strategy3 = TradeStrategy2() 227 | TradeStrategy2.set_keep_stock_threshold(20) 228 | TradeStrategy2.set_buy_change_threshold(-0.08) 229 | # 执行回测 230 | trade_loop_back4 = TradeLoopBack(trade_days, trade_strategy3) 231 | trade_loop_back4.execute_trade() 232 | 233 | # 查看回测结果 234 | print(trade_loop_back4.profit_array) 235 | print(sum(trade_loop_back4.profit_array)) 236 | 237 | # 绘图查看 238 | plot_array(trade_loop_back4.profit_array) 239 | 240 | # 画图方法 241 | def plot_array(array): 242 | import seaborn as sns 243 | import numpy as np 244 | import matplotlib.pyplot as plt 245 | sns.set_context(rc={'figure.figsize': (14, 7)}) 246 | plt.plot(np.array(array).cumsum()) 247 | plt.show() 248 | 249 | 250 | # 寻找最优参数 251 | def calc(keep_stock_threshold, buy_change_threshold, trade_days): 252 | ''' 253 | 254 | :param keep_stock_threshold: 涨跌幅的阈值 255 | :param buy_change_threshold: 持股天数阈值 256 | :param trade_days:交易序列 257 | :return: 盈亏情况,输入的持股天数,输入的涨跌幅阈值 258 | ''' 259 | # 初始化策略,并修改参数 260 | trade_strategy2 = TradeStrategy2() 261 | TradeStrategy2.set_keep_stock_threshold(keep_stock_threshold) 262 | TradeStrategy2.set_buy_change_threshold(buy_change_threshold) 263 | 264 | # 执行回测 265 | trade_loop_back=TradeLoopBack(trade_days,trade_strategy2) 266 | trade_loop_back.execute_trade() 267 | 268 | # 计算最终盈亏值 269 | profit=sum(trade_loop_back.profit_array) 270 | 271 | return profit,keep_stock_threshold,buy_change_threshold 272 | 273 | 274 | 275 | if __name__ == '__main__': 276 | # 用特斯拉的数据来做演示 277 | # 获取特斯拉的数据 278 | price_array = ABuSymbolPd.make_kl_df('TSLA', n_folds=2).close.tolist() 279 | date_array = ABuSymbolPd.make_kl_df('TSLA', n_folds=2).date.tolist() 280 | date_base = 20170118 281 | 282 | # 构建股票交易序列 283 | trade_days = StockTradeDays(price_array, date_base, date_array) 284 | 285 | print(calc(20,-0.08,trade_days)) 286 | 287 | ########寻找最优参数######### 288 | # 类似于grid-search 289 | keep_stock_list=range(2,30,2) 290 | buy_change_list=[buy_change/100.00 for buy_change in range(-5,-16,-1)] 291 | 292 | result=[] 293 | for keep_stock_threshold,buy_change_threshold in itertools.product(keep_stock_list,buy_change_list): 294 | result.append(calc(keep_stock_threshold,buy_change_threshold,trade_days)) 295 | 296 | print(sorted(result)[::-1][:10]) 297 | -------------------------------------------------------------------------------- /Section8/Section8_1.py: -------------------------------------------------------------------------------- 1 | from abupy import AbuFactorBuyBase 2 | from abupy import AbuBenchmark 3 | from abupy import AbuPickTimeWorker 4 | from abupy import AbuCapital 5 | from abupy import AbuKLManager 6 | from abupy import AbuFactorBuyBreak 7 | from abupy import ABuTradeProxy 8 | from abupy import ABuTradeExecute 9 | from abupy import AbuFactorSellBreak 10 | from abupy import ABuPickTimeExecute 11 | from abupy import AbuFactorAtrNStop 12 | from abupy import AbuFactorPreAtrNStop 13 | from abupy import AbuFactorCloseAtrNStop 14 | from abupy import AbuSlippageBuyBase 15 | from abupy import AbuMetricsBase 16 | from abupy import AbuKellyPosition 17 | import numpy as np 18 | import pandas as pd 19 | import matplotlib.pyplot as plt 20 | 21 | # class AbuFactorBuyBreak(AbuFactorBuyBase): 22 | # ''' 23 | # 实现N日趋势突破: 24 | # 1.当第x天收盘后,发现第x天的股价最大值,大于前k天(不包括第x天)的最大值,那么在第x+1天买入股票 25 | # 2.当第x天收盘后,发现第x天的股票最小值,小于前k天(不包括第x天)的最小值,那么在第x+1天卖出股票 26 | # 3.在第x+1天买入股票后,从x+1天到x+1+k天这段时间,哪怕有新的最高值创出,也不再购入 27 | # ''' 28 | # def _init_self(self, **kwargs): 29 | # # 突破参数xd,比如20天,30天突破 30 | # self.xd=kwargs['xd'] 31 | # # 忽略连续创新高,比如买入第二天有创新高 32 | # self.skip_days=0 33 | # # 显示名字 34 | # self.factor_name='{}:{}'.format(self.__class__.__name__,self.xd) 35 | # 36 | # def fit_day(self, today): 37 | # day_ind=int(today.key) 38 | # # 忽略不符合的买入日期(前xd天和最后一天) 39 | # if day_ind=self.kl_pd.shape[0]-1: 40 | # return None 41 | # 42 | # if self.skip_days>0: 43 | # # 执行买入订单忽略 44 | # self.skip_days -= 1 45 | # return None 46 | # 47 | # if today.close == self.kl_pd.close[day_ind-self.xd+1:day_ind+1].max(): 48 | # # 买入股票后,在下一个xd天内,如果股票再创新高,也不买了 49 | # self.skip_days=self.xd 50 | # # 生成买入订单 51 | # return self.make_buy_order(day_ind) 52 | # return None 53 | 54 | if __name__ == '__main__': 55 | ''' 56 | 1) 57 | 实现N日趋势突破: 58 | 1.当第x天收盘后,发现第x天的股价最大值,大于前k天(不包括第x天)的最大值,那么在第x+1天买入股票 59 | 2.当第x天收盘后,发现第x天的股票最小值,小于前k天(不包括第x天)的最小值,那么在第x+1天卖出股票 60 | 3.在第x+1天买入股票后,从x+1天到x+1+k天这段时间,哪怕有新的最高值创出,也不再购入 61 | 62 | ''' 63 | # 1、这里是买入突破 64 | # 创建60日向上突破,42日向上突破两个因子 65 | buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak}, {'xd': 42, 'class': AbuFactorBuyBreak}] 66 | # 基准利润 67 | benchmark = AbuBenchmark() 68 | 69 | # 现金和基准利润 70 | capital = AbuCapital(1000000, benchmark) 71 | # 多线程管理类 72 | kl_pd_manager = AbuKLManager(benchmark, capital) 73 | # 获取TSLA的股票信息 74 | kl_pd = kl_pd_manager.get_pick_time_kl_pd('usTSLA') 75 | # 准备开始工作 76 | abu_worker = AbuPickTimeWorker(capital, kl_pd, benchmark, buy_factors, None) 77 | abu_worker.fit() 78 | # 画出哪几个点可以买入,以及最终的收益情况 79 | # orders_pd,action_pd,_=ABuTradeProxy.trade_summary(abu_worker.orders,kl_pd,draw=True) 80 | orders_pd, action_pd, _ = ABuTradeProxy.trade_summary(abu_worker.orders, kl_pd, draw=False) 81 | 82 | # 上面是从股价角度,下面从我们的资金来看 83 | # ABuTradeExecute.apply_action_to_capital(capital, action_pd, kl_pd_manager) 84 | # print(capital.capital_pd.head()) 85 | # capital.capital_pd.capital_blance.plot() 86 | # plt.show() 87 | 88 | # 上面只是实现了买入突破,下面我们用卖出突破来试试 89 | # 2、这里是卖出突破 90 | sell_factor1 = {'xd': 120, 'class': AbuFactorSellBreak} 91 | sell_factors = [sell_factor1] 92 | capital = AbuCapital(1000000, benchmark) 93 | orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_same_factors(['usTSLA'], 94 | benchmark, buy_factors, sell_factors, 95 | capital, show=False) 96 | 97 | ''' 98 | 下面会用到ATR这个指标,解释一下这个指标。 99 | 1. 设当前交易日的最高价减最低价为x1, 100 | 2. 前一个交易日的收盘件减去当前交易日的最高价的绝对值为x2 101 | 3. 前一个交易日的收盘件减去当前交易日的最低价的绝对值为x3 102 | 4. 那么真实波动TR=max(x1,x2,x3) 103 | 5. ATR,就是求N天这个值(TR)的平均,ATR=(∑TR)/n 104 | 作用: 105 | 1. 用来衡量价格的波动强度,和价格的绝对值没有关系 106 | ''' 107 | 108 | ''' 109 | 2) 110 | 上面给出了买入点和卖出点的定义,以及他们在历史数据上的表现 111 | 但是现在很多操作会有止盈和止损的操作,我们使用ATR来协助判断止盈和止损 112 | 具体的止盈止损策略: 113 | 1. 当添加了止盈策略时,我们选择atr21和atr14的和作为atr常数,人工传入一个止盈率win_rate 114 | 2. 当当前的收益大于0,且大于止盈率乘以atr常数时,可以卖出股票了。 115 | 3. 亏损同理,即atr常数相同,换一个止损率就好 116 | ''' 117 | # 构造信号因子 118 | sell_factor2 = {'stop_loss_n': 0.5, 'stop_win_n': 3.0, 'class': AbuFactorAtrNStop} 119 | # 多因子同时生效 120 | sell_factors = [sell_factor1, sell_factor2] 121 | # 执行 122 | capital = AbuCapital(1000000, benchmark) 123 | orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_same_factors(['usTSLA'], benchmark, buy_factors, 124 | sell_factors, capital, show=False) 125 | 126 | ''' 127 | 3) 128 | 利用atr来进行止损有一个弊端。 129 | 1.对于atr而言,我们认为,从买入股票到今天,所有的亏损值,如果大于atr常数乘以止损率,那么就卖出 130 | 2.而如果在一天内,股价暴跌,由于atr的平均性质,它无法很快的反应出来,这样会导致我们无法及时出逃。 131 | 3.(当然,atr这样也可以帮助我们不会因为一时的起伏就错过一只长远看有利可图的股票,这个就看自己选择了) 132 | 4.那么,在生存第一的情况下,我们在暴跌发生时,马上逃离。 133 | 134 | 或者这么理解 135 | 1.在之前止损的时候,我们算的是总的亏损超过某个值,我们就逃。 136 | 2.现在的情况是,在第n天的时候,我有利润,但没过止盈点,第n+1天的时候,股价暴跌,但还没有跌破我的止损点。 137 | 3.那么,这一天之内发生的暴跌,是否应该作为我出逃股票的信号? 138 | 4.一般认为是的,所以要把这个信息加入进入。 139 | ''' 140 | # 构造因子 141 | sell_factor3 = {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.0} 142 | # 多因子同时生效 143 | sell_factors = [sell_factor1, sell_factor2, sell_factor3] 144 | # 执行 145 | capital = AbuCapital(1000000, benchmark) 146 | orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_same_factors(['usTSLA'], benchmark, buy_factors, 147 | sell_factors, capital, show=False) 148 | 149 | ''' 150 | 4) 151 | 在之前的止盈策略中,我们是令全局的收益高过某一个动态值【(atr21+atr14)*rate】,才会卖出 152 | 那么,在股票已经盈利,但是没有超过这个止盈点,而后续股价下跌,使得我们不得不止损卖出,是不是会觉得很亏 153 | 即,本来可以卖出盈利,但是因为目标定的太高,没有实现,反倒使得自己亏损了。 154 | 那么,我们就需要采用一定策略,来避免这样的情况。 155 | 具体做法是: 156 | 1.对于已经盈利,但没有超过止盈点的股票,我们设置一个保护止盈点。 157 | 2.当盈利股票下跌超过保护止盈点后,我们就将其卖出。 158 | ''' 159 | # 构造因子 160 | sell_factor4 = {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5} 161 | # 多因子同时生效 162 | sell_factors = [sell_factor1, sell_factor2, sell_factor3, sell_factor4] 163 | # 执行 164 | capital = AbuCapital(1000000, benchmark) 165 | orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_same_factors(['usTSLA'], benchmark, buy_factors, 166 | sell_factors, capital, show=False) 167 | 168 | ''' 169 | 5) 170 | 1.上面的交易策略大致的总结是这样,我们在第x天交易结束后,往回看,根据我们的策略,分析是否应该买入或者卖出。 171 | 2.在得到买入或卖出信号后,我们在第x+1天挂单进行交易。 172 | 3.前面解决的是信号,那么下面该解决我们到底应该以什么价格买入或者卖出比较好? 173 | 4.目前给句的策略,是用第x天的最高价与最低价的均值,作为买入或者卖出价。 174 | 5.另外,当我们在第x天决定在第x+1天买入后,如果这个股票不争气,在第x+1天的开盘价下跌超过了某个阈值 175 | 6.我们也取消这个买入动作 176 | ''' 177 | # 当第x天准备买入,发现第x+1天下跌超过2%,就不买了 178 | g_open_down_rate = 0.02 179 | 180 | 181 | class AbuSlippageBuyMeans2(AbuSlippageBuyBase): 182 | def fit_price(self): 183 | if (self.kl_pd_buy.open / self.kl_pd_buy.pre_close) < (1 - g_open_down_rate): 184 | print('跌太多,不买了') 185 | return np.inf 186 | self.buy_price = np.mean([self.kl_pd_buy['high'], self.kl_pd_buy['low']]) 187 | return self.buy_price 188 | 189 | 190 | # 执行策略 191 | buy_factors2 = [{'slippage': AbuSlippageBuyMeans2, 'xd': 60, 'class': AbuFactorBuyBreak}, {'xd': 42, 192 | 'class': AbuFactorBuyBreak}] 193 | sell_factors = [sell_factor1, sell_factor2, sell_factor3, sell_factor4] 194 | # 执行 195 | capital = AbuCapital(1000000, benchmark) 196 | orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_same_factors(['usTSLA'], benchmark, buy_factors2, 197 | sell_factors, capital, show=False) 198 | 199 | ''' 200 | 6) 201 | 前面,我们针对一只股票: 202 | 1.为了得到什么时候能够买入股票:我们从最基本本的N日趋势跟踪策略,到加入止损止盈策略,再到对止损止盈策略做了修改。 203 | 2.为了得到应该用什么价格买入股票:我们用了滑点买入/卖出的策略 204 | 那么,在下面,我们尝试将我们的策略,运用到多支股票上。 205 | 现在我们暂时采用对多支股票使用相同的因子,进行买入卖出时间点的选择。 206 | ''' 207 | # 选择多支股票 208 | choice_symbols = ['usTSLA', 'usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG', 'usWUBA', 'usVIPS'] 209 | capital = AbuCapital(1000000, benchmark) 210 | # 这里用的是buy_factors,即没有包含滑点买入/卖出策略的buy_factors 211 | orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_same_factors(choice_symbols, benchmark, buy_factors, 212 | sell_factors, capital, show=False) 213 | # print(orders_pd[:10]) 214 | # print(action_pd[:10]) 215 | # 可视化结果 216 | metrics = AbuMetricsBase(orders_pd, action_pd, capital, benchmark) 217 | metrics.fit_metrics() 218 | metrics.plot_returns_cmp(only_show_returns=True) 219 | 220 | ''' 221 | 7) 222 | 上面metrics的结果: 223 | 买入后卖出的交易数量:88 224 | 买入后尚未卖出的交易数量:5 225 | 胜率:46.5909% 226 | 平均获利期望:6.4254% 227 | 平均亏损期望:-3.9016% 228 | 盈亏比:1.6611 229 | 策略收益: 22.9038% 230 | 基准收益: 52.3994% 231 | 策略年化收益: 11.4747% 232 | 基准年化收益: 26.2518% 233 | 策略买入成交比例:83.8710% 234 | 策略资金利用率比例:31.4213% 235 | 策略共执行503个交易日 236 | 237 | 其中,最重要的就是获利期望,亏损期望和胜率,有了这三个值,我们就可以来进行仓位控制。 238 | 其实,我们系统想做这么几件事: 239 | 1. 确定在什么时候买入,什么时候卖出。 240 | 2. 确定买入卖出的时候采用什么价格 241 | 3. 假设手上有资金100w,每次买入股票,花多少钱买入股票;卖出的时候,也考虑卖出多少手上的股票(目前是全卖了) 242 | ''' 243 | # 设定两个因子 244 | buy_factors2 = [{'xd': 60, 'class': AbuFactorBuyBreak}, 245 | {'xd': 42, 'position': {'class': AbuKellyPosition, 'win_rate': metrics.win_rate, 246 | 'gains_mean': metrics.gains_mean, 'losses_mean': -metrics.losses_mean}, 247 | 'class': AbuFactorBuyBreak}] 248 | # 执行 249 | capital = AbuCapital(1000000, benchmark) 250 | orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_same_factors(choice_symbols, benchmark, buy_factors2, 251 | sell_factors, capital, show=False) 252 | print(orders_pd[:10]) 253 | 254 | ''' 255 | 8) 256 | 1.对于一支股票,从买入卖出点的确认,到买入卖出的价格,以及最后的仓位控制,我们都做了 257 | 2.之前也简单测试了一下多只股票情况下,用同一套策略的可行性。 258 | 3.下面,我们尝试一下多支股票用不的策略。其实很简单,将其封装成dict,然后传入do_symbols_with_diff_factors就好 259 | ''' 260 | target_symbols=['usSFUN','usNOAH'] 261 | 262 | # 对SUFN,向上只采用42日突破 263 | buy_factor_SFUN=[{'xd':42,'class':AbuFactorBuyBreak}] 264 | # 对SUFN,向下只采用60日突破 265 | sell_factor_SFUN=[{'xd':60,'class':AbuFactorSellBreak}] 266 | 267 | # 对NOAH,向上只采用21日突破 268 | buy_factor_NOAH=[{'xd':21,'class':AbuFactorBuyBreak}] 269 | # 对NOAH,向下采用42日突破 270 | sell_factor_NOAH=[{'xd':42,'class':AbuFactorSellBreak}] 271 | 272 | # 构建因子字典 273 | factor_dict=dict() 274 | factor_dict['usSFUN']={'buy_factors':buy_factor_SFUN,'sell_factors':sell_factor_SFUN} 275 | factor_dict['usNOAH']={'buy_factors':buy_factor_NOAH,'sell_factors':sell_factor_NOAH} 276 | # 执行 277 | capital = AbuCapital(1000000, benchmark) 278 | orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_diff_factors(target_symbols, benchmark, factor_dict, 279 | capital) 280 | # 打印结果 281 | # print(orders_pd.head()) 282 | # pd.crosstab(orders_pd.buy_factor,orders_pd.symbol) 283 | -------------------------------------------------------------------------------- /Section6/Section6_2.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractclassmethod 2 | import six 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | import scipy.optimize as sco 6 | 7 | K_LIVING_DAYS = 27375 8 | 9 | 10 | def regular_mm(group): 11 | ''' 12 | 正则化,减去最小,除以范围 13 | :param group: 14 | :return: 15 | ''' 16 | return (group - group.min()) / (group.max() - group.min()) 17 | 18 | 19 | class Person(object): 20 | ''' 21 | 表示人类的类 22 | ''' 23 | 24 | def __init__(self): 25 | self.living = K_LIVING_DAYS 26 | # 初始化幸福值,财富值,名望值和存活天数 27 | self.happiness = 0 28 | self.wealth = 0 29 | self.fame = 0 30 | self.living_day = 0 31 | 32 | def living_one_day(self, seek): 33 | ''' 34 | 每天只能有一种追求(seek) 35 | :param seek: 36 | :return: 37 | ''' 38 | # 得到今天独特的收获 39 | consume_living, happinese, wealth, fame = seek.do_seek_day() 40 | # 减去生命消耗 41 | self.living -= consume_living 42 | # 幸福积累 43 | self.happiness += happinese 44 | # 财富积累 45 | self.wealth += wealth 46 | # 名望积累 47 | self.fame += fame 48 | # 过完这一天 49 | self.living_day += 1 50 | 51 | 52 | class BaseSeekDay(six.with_metaclass(ABCMeta, object)): 53 | ''' 54 | 每天不同的追求 55 | ''' 56 | 57 | def __init__(self): 58 | # 每个追求每天消耗生命的常数 59 | self.living_consume = 0 60 | # 每个追求每天幸福指数常数 61 | self.happiness_base = 0 62 | # 每个追求每天积累的财富数 63 | self.wealth_base = 0 64 | # 每个追求每天积累的名望数 65 | self.fame_base = 0 66 | 67 | # 每个追求每天消耗生命的可变因素序列 68 | self.living_factor = [0] 69 | # 每个追求每天幸福指数的可变因素序列 70 | self.happiness_factor = [0] 71 | # 每个追求每天财富指数的可变因素序列 72 | self.wealth_factor = [0] 73 | # 每个追求每天名望指数的可变因素序列 74 | self.fame_factor = [0] 75 | 76 | # 这一生追求了多少天 77 | self.do_seek_day_cnt = 0 78 | # 子类进行常数和可变因素序列设置 79 | self._init_self() 80 | 81 | @abstractclassmethod 82 | def _init_self(self, *args, **kwargs): 83 | pass 84 | 85 | @abstractclassmethod 86 | def _gen_living_days(self, *args, **kwargs): 87 | pass 88 | 89 | def do_seek_day(self): 90 | ''' 91 | 每一天的具体追求 92 | :return: 93 | ''' 94 | 95 | # 计算追求这个事要消耗多少生命,生命消耗=living_consume:消耗常数*living_factor:可变序列 96 | # 当do_seek_day_cnt超过一定值,即追求一定天数后,living_factor也固定 97 | if self.do_seek_day_cnt >= len(self.living_factor): 98 | consume_living = self.living_factor[-1] * self.living_consume 99 | else: 100 | consume_living = self.living_factor[self.do_seek_day_cnt] * self.living_consume 101 | 102 | # 下面关于幸福,财富,名望都一样 103 | 104 | # 幸福指数=happiness_base:幸福常数 * happiness_factor:可变序列 105 | if self.do_seek_day_cnt >= len(self.happiness_factor): 106 | # 超出len(self.happiness_factor), 就取最后一个 107 | # 由于happiness_factor值由:n—>0 所以happiness_factor[-1]=0 108 | # 即随着追求一个事物的次数过多后会变的没有幸福感 109 | happiness = self.happiness_factor[-1] * self.happiness_base 110 | else: 111 | # 每个类自定义这个追求的幸福指数常数,以及happiness_factor 112 | # happiness_factor子类的定义一般是从高->低变化 113 | happiness = self.happiness_factor[self.do_seek_day_cnt] * self.happiness_base 114 | 115 | # 财富积累=wealth_base:积累常数 * wealth_factor:可变序列 116 | if self.do_seek_day_cnt >= len(self.wealth_factor): 117 | # 超出len(self.wealth_factor), 就取最后一个 118 | wealth = self.wealth_factor[-1] * self.wealth_base 119 | else: 120 | # 每个类自定义这个追求的财富指数常数,以及wealth_factor 121 | wealth = self.wealth_factor[self.do_seek_day_cnt] * self.wealth_base 122 | 123 | # 权利积累=fame_base:积累常数 * fame_factor:可变序列 124 | if self.do_seek_day_cnt >= len(self.fame_factor): 125 | # 超出len(self.fame_factor), 就取最后一个 126 | fame = self.fame_factor[-1] * self.fame_base 127 | else: 128 | # 每个类自定义这个追求的名望权利指数常数,以及fame_factor 129 | fame = self.fame_factor[self.do_seek_day_cnt] * self.fame_base 130 | 131 | # 追求了多少天 132 | self.do_seek_day_cnt += 1 133 | # 返回这个追求一天对生命的消耗,以及得到的幸福,财富和权利 134 | return consume_living, happiness, wealth, fame 135 | 136 | 137 | class HealthSeekDay(BaseSeekDay): 138 | ''' 139 | 每一天都追求健康长寿 140 | ''' 141 | 142 | def _init_self(self): 143 | # 每天都生命的消耗常数,这里是1天 144 | self.living_consume = 1 145 | # 每天幸福指数 146 | self.happiness_base = 1 147 | # 设定可变因素序列 148 | self._gen_living_days() 149 | 150 | def _gen_living_days(self): 151 | # 只生成长为12000的序列,因为下面的happiness_factor序列值由1->0 152 | # 所以大于12000次的追求都将只是单纯消耗生命,并不增加幸福指数 153 | # 即随着做一件事情的次数越来越多,幸福感越来越低,直到完全体会不到幸福 154 | days = np.arange(1, 12000) 155 | living_days = np.sqrt(days) 156 | """ 157 | 对生命消耗可变因素序列值由-1->1, 也就是这个追求一开始的时候对生命 158 | 的消耗为负增长,延长了生命,随着追求的次数不断增多对生命的消耗转为正 159 | 数因为即使一个人天天锻炼身体,天天吃营养品,也还是会有自然死亡的那 160 | 一天 161 | """ 162 | # 这个数列一开始是-1,最后是+1,计算的时候是用来被剪掉 163 | # 所以一开始是-1,表示能够让你活的长一点( 164 | self.living_factor = regular_mm(living_days) * 2 - 1 165 | # 结果在1-0之间 [::-1]: 将0->1转换到1->0 166 | self.happiness_factor = regular_mm(days)[::-1] 167 | 168 | 169 | class StockSeekDay(BaseSeekDay): 170 | """ 171 | StockSeekDay追求财富金钱的一天: 172 | 形象:做股票投资赚钱的事情。 173 | 抽象:追求财富金钱 174 | """ 175 | 176 | def _init_self(self, show=False): 177 | # 每天对生命消耗的常数=2,即代表2天 178 | self.living_consume = 2 179 | # 每天幸福指数常数=0.5 180 | self.happiness_base = 0.5 181 | # 财富积累常数=10,默认=0 182 | self.wealth_base = 10 183 | # 设定可变因素序列 184 | self._gen_living_days() 185 | 186 | def _gen_living_days(self): 187 | # 只生成10000个序列 188 | days = np.arange(1, 10000) 189 | # 针对生命消耗living_factor的基础函数还是sqrt 190 | living_days = np.sqrt(days) 191 | # 由于不需要像HealthSeekDay从负数开始,所以直接regular_mm 即:0->1 192 | # 这里也是掉血越来越快,每词seek wealth后,都会让你减少很多总寿命 193 | self.living_factor = regular_mm(living_days) 194 | 195 | # 针对幸福感可变序列使用了np.power4,即变化速度比sqrt快 196 | happiness_days = np.power(days, 4) 197 | # 幸福指数可变因素会快速递减由1->0 198 | self.happiness_factor = regular_mm(happiness_days)[::-1] 199 | 200 | """ 201 | 这里简单设定wealth_factor=living_factor 202 | living_factor(0-1), 导致wealth_factor(0-1), 即财富积累越到 203 | 后面越有效率,速度越快,头一个100万最难赚 204 | """ 205 | self.wealth_factor = self.living_factor 206 | 207 | 208 | class FameSeekDay(BaseSeekDay): 209 | """ 210 | FameTask追求名望权力的一天: 211 | 追求名望权力 212 | """ 213 | 214 | def _init_self(self): 215 | # 每天对生命消耗的常数=3,即代表3天 216 | self.living_consume = 3 217 | # 每天幸福指数常数=0.6 218 | self.happiness_base = 0.6 219 | # 名望权利积累常数=10,默认=0 220 | self.fame_base = 10 221 | # 设定可变因素序列 222 | self._gen_living_days() 223 | 224 | def _gen_living_days(self): 225 | # 只生成12000个序列 226 | days = np.arange(1, 12000) 227 | # 针对生命消耗living_factor的基础函数还是sqrt 228 | living_days = np.sqrt(days) 229 | # 由于不需要像HealthSeekDay从负数开始,所以直接regular_mm 即:0->1 230 | self.living_factor = regular_mm(living_days) 231 | 232 | # 针对幸福感可变序列使用了np.power2 233 | # 即变化速度比StockSeekDay慢但比HealthSeekDay快 234 | happiness_days = np.power(days, 2) 235 | # 幸福指数可变因素递减由1->0 236 | self.happiness_factor = regular_mm(happiness_days)[::-1] 237 | 238 | # 这里简单设定fame_factor=living_factor 239 | self.fame_factor = self.living_factor 240 | 241 | 242 | def my_test_life(): 243 | # 初始化我, 你一生的故事:HealthSeekDay 244 | me = Person() 245 | # 初始化追求健康长寿 246 | seek_health = HealthSeekDay() 247 | # 这里的基本假设是: 248 | # 1.你每天的追求是会消耗或者提升你能活的最大天数(或者说存货时间池) 249 | # 2.只要池子里还有天数,你就能继续活(me.living>0) 250 | # 3.通过me.living_days记录我度过了多少天 251 | # 4.我每天的追求是会增加池子中的天数,也有可能减少 252 | # 5.我想知道,当池子为空时,我度过了多少天 253 | while me.living > 0: 254 | me.living_one_day(seek_health) 255 | 256 | print('只追求健康长寿快乐活了{}年,幸福指数{},积累财富{},名望权力{}'.format 257 | (round(me.living_day / 365, 2), round(me.happiness, 2), 258 | me.wealth, me.fame)) 259 | plt.plot(seek_health.living_factor * seek_health.living_consume) 260 | plt.plot(seek_health.happiness_factor * seek_health.happiness_base) 261 | plt.legend(['living_factor', 'happiness_factor'], loc='best') 262 | plt.show() 263 | 264 | ####只追求金钱 265 | # 初始化我, 你一生的故事:StockSeekDay 266 | me = Person() 267 | # 初始化追求财富金钱 268 | seek_stock = StockSeekDay() 269 | while me.living > 0: 270 | # 只要还活着,就追求财富金钱 271 | me.living_one_day(seek_stock) 272 | 273 | print('只追求财富金钱活了{}年,幸福指数{}, 积累财富{}, 名望权力{}'.format 274 | (round(me.living_day / 365, 2), round(me.happiness, 2), 275 | round(me.wealth, 2), me.fame)) 276 | 277 | plt.plot(seek_stock.living_factor * seek_stock.living_consume) 278 | plt.plot(seek_stock.happiness_factor * seek_stock.happiness_base) 279 | plt.legend(['living_factor', 'happiness_factor'], loc='best') 280 | plt.show() 281 | 282 | ####只只求名望 283 | # 初始化我, 你一生的故事:FameSeekDay 284 | me = Person() 285 | # 初始化追求名望权力 286 | seek_fame = FameSeekDay() 287 | while me.living > 0: 288 | # 只要还活着,就追求名望权力 289 | me.living_one_day(seek_fame) 290 | 291 | print('只追求名望权力活了{}年,幸福指数{}, 积累财富{}, 名望权力{}'.format 292 | (round(me.living_day / 365, 2), round(me.happiness, 2), 293 | round(me.wealth, 2), round(me.fame, 2))) 294 | 295 | plt.plot(seek_fame.living_factor * seek_fame.living_consume) 296 | plt.plot(seek_fame.happiness_factor * seek_fame.happiness_base) 297 | plt.legend(['living_factor', 'happiness_factor'], loc='best') 298 | plt.show() 299 | 300 | 301 | def my_life(weights): 302 | ''' 303 | 想要知道如何分配时间才能让幸福最大 304 | :param weights: 305 | :return: 306 | ''' 307 | # 追求健康长寿快乐 308 | seek_health = HealthSeekDay() 309 | # 追求财富金钱 310 | seek_stock = StockSeekDay() 311 | # 追求名望权力 312 | seek_fame = FameSeekDay() 313 | 314 | # 放在一个list中对对应下面np.random.choice中的index[0, 1, 2] 315 | seek_list = [seek_health, seek_stock, seek_fame] 316 | 317 | # 初始化我 318 | me = Person() 319 | # 加权随机抽取序列。80000天肯定够了, 80000天快220年了。。。 320 | seek_choice = np.random.choice([0, 1, 2], 80000, p=weights) 321 | 322 | while me.living > 0: 323 | # 追求从加权随机抽取序列已经初始化好的 324 | seek_ind = seek_choice[me.living_day] 325 | seek = seek_list[seek_ind] 326 | # 只要还活着,就追求 327 | me.living_one_day(seek) 328 | return round(me.living_day / 365, 2), round(me.happiness, 2), \ 329 | round(me.wealth, 2), round(me.fame, 2) 330 | 331 | 332 | def montekalo_method(): 333 | ''' 334 | 用蒙特卡洛方法求解 335 | :return: 336 | ''' 337 | # 用来保存结果 338 | result = [] 339 | # 做2000次实验 340 | for _ in range(0, 2000): 341 | weights = np.random.random(3) 342 | weights /= np.sum(weights) 343 | result.append((weights, my_life(weights))) 344 | 345 | # result中tuple[1]=my_life返回的结果, my_life[1]=幸福指数,so->x[1][1] 346 | sorted_scores = sorted(result, key=lambda x: x[1][1], reverse=True) 347 | 348 | # 将最优weights带入my_life,计算得到最优结果 349 | living_day, happiness, wealth, fame = my_life(sorted_scores[0][0]) 350 | 351 | print('活了{}年,幸福指数{}, 积累财富{}, 名望权力{}'.format 352 | (living_day, happiness, wealth, fame)) 353 | 354 | print('人生最优权重:追求健康{:.3f},追求财富{:.3f},追求名望{:.3f}'.format( 355 | sorted_scores[0][0][0], sorted_scores[0][0][1], 356 | sorted_scores[0][0][2])) 357 | 358 | 359 | def minimize_happiness_global(weigths): 360 | if np.sum(weigths) != 1: 361 | return 0 362 | return -my_life(weigths)[1] 363 | 364 | 365 | def global_search(): 366 | # 下面函数表示,我做20次独立实验,从[0,1,2]这三个数字中抽取一个出来 367 | # 每次实验中,0,1,2三个数被抽取的概率分别为0.2,0.3,0.5 368 | opt_global = sco.brute(minimize_happiness_global, ((0, 1.1, 0.1), (0, 1.1, 0.1), (0, 1.1, 0.1))) 369 | 370 | # 将最优weights带入my_life,计算得到最优结果 371 | living_day, happiness, wealth, fame = my_life(opt_global) 372 | 373 | print('活了{}年,幸福指数{}, 积累财富{}, 名望权力{}'.format 374 | (living_day, happiness, wealth, fame)) 375 | 376 | print('人生最优权重:追求健康{:.3f},追求财富{:.3f},追求名望{:.3f}'.format( 377 | opt_global[0], opt_global[1], opt_global[2])) 378 | 379 | def minimizi_happiness_local(weights): 380 | print(weights) 381 | return -my_life(weights)[1] 382 | 383 | if __name__ == '__main__': 384 | # my_test_life() 385 | method = 'SLSQP' 386 | # 约束 387 | constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1}) 388 | # 构造需要被调节的参数 389 | bounds = tuple((0, 1) for i in range(3)) 390 | # 初始化猜测参数 391 | guess=[0.5,0.2,0.3] 392 | 393 | opt_local=sco.minimize(minimizi_happiness_local,guess,method=method,bounds=bounds,constraints=constraints) 394 | 395 | print(opt_local) 396 | -------------------------------------------------------------------------------- /Section10/Section10_1.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 项目构建在虚拟的股票数据上 3 | ''' 4 | 5 | g_with_date_week_noise = False 6 | 7 | from abupy import ABuSymbolPd 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | import sklearn.preprocessing as preprocessing 11 | import pandas as pd 12 | from sklearn.linear_model import LinearRegression 13 | from sklearn.ensemble import AdaBoostRegressor 14 | from sklearn.ensemble import RandomForestRegressor 15 | from sklearn import cross_validation 16 | from sklearn import metrics 17 | from sklearn.linear_model import LogisticRegression 18 | from sklearn.svm import SVC 19 | 20 | def _gen_another_word_price(kl_another_word): 21 | """ 22 | 生成股票在另一个世界中的价格 23 | :param kl_another_word: 24 | :return: 25 | """ 26 | for ind in np.arange(2, kl_another_word.shape[0]): 27 | # 前天数据 28 | bf_yesterday = kl_another_word.iloc[ind - 2] 29 | # 昨天 30 | yesterday = kl_another_word.iloc[ind - 1] 31 | # 今天 32 | today = kl_another_word.iloc[ind] 33 | # 生成今天的收盘价格 34 | kl_another_word.close[ind] = _gen_another_word_price_rule( 35 | yesterday.close, yesterday.volume, 36 | bf_yesterday.close, bf_yesterday.volume, 37 | today.volume, today.date_week) 38 | 39 | 40 | def _gen_another_word_price_rule(yesterday_close, yesterday_volume, 41 | bf_yesterday_close, 42 | bf_yesterday_volume, 43 | today_volume, date_week): 44 | """ 45 | 通过前天收盘量价,昨天收盘量价,今天的量,构建另一个世界中的价格模型 46 | """ 47 | # 昨天收盘价格与前天收盘价格的价格差 48 | price_change = yesterday_close - bf_yesterday_close 49 | # 昨天成交量与前天成交量的量差 50 | volume_change = yesterday_volume - bf_yesterday_volume 51 | 52 | # 如果量和价变动一致,今天价格涨,否则跌 53 | # 即量价齐涨->涨, 量价齐跌->涨,量价不一致->跌 54 | sign = 1.0 if price_change * volume_change > 0 else -1.0 55 | 56 | # 通过date_week生成噪音,否则之后分类100%分对 57 | if g_with_date_week_noise: 58 | # 针对sign生成噪音,噪音的生效的先决条件是今天的量是这三天最大的 59 | gen_noise = today_volume > np.max( 60 | [yesterday_volume, bf_yesterday_volume]) 61 | # 如果量是这三天最大 且是周五,下跌 62 | if gen_noise and date_week == 4: 63 | sign = -1.0 64 | # 如果量是这三天最大,如果是周一,上涨 65 | elif gen_noise and date_week == 0: 66 | sign = 1.0 67 | 68 | # 今天的涨跌幅度基础是price_change(昨天前天的价格变动) 69 | price_base = abs(price_change) 70 | # 今天的涨跌幅度变动因素:量比, 71 | # 今天的成交量/昨天的成交量 和 今天的成交量/前天的成交量 的均值 72 | price_factor = np.mean([today_volume / yesterday_volume, 73 | today_volume / bf_yesterday_volume]) 74 | 75 | if abs(price_base * price_factor) < yesterday_close * 0.10: 76 | # 如果 量比 * price_base 没超过10%,今天价格计算 77 | today_price = yesterday_close + \ 78 | sign * price_base * price_factor 79 | else: 80 | # 如果涨跌幅度超过10%,限制上限,下限为10% 81 | today_price = yesterday_close + sign * yesterday_close * 0.10 82 | return today_price 83 | 84 | 85 | def change_real_to_another_word(symbol): 86 | """ 87 | 将原始真正的股票数据价格列只保留前两天数据,成交量,周几列完全保留 88 | 价格列其他数据使用_gen_another_word_price变成另一个世界价格 89 | :param symbol: 90 | :return: 91 | """ 92 | kl_pd = ABuSymbolPd.make_kl_df(symbol) 93 | if kl_pd is not None: 94 | # 原始股票数据也只保留价格,周几,成交量 95 | kl_pig_three = kl_pd.filter(['close', 'date_week', 'volume']) 96 | # 只保留原始头两天的交易收盘价格,其他的的都赋予nan 97 | kl_pig_three['close'][2:] = np.nan 98 | # 将其他nan价格变成猪老三世界中价格使用_gen_another_word_price 99 | _gen_another_word_price(kl_pig_three) 100 | return kl_pig_three 101 | 102 | 103 | def another_real_show(choice_symbols, another_word_dict, real_dict): 104 | ''' 105 | 可视化真/假股票数据 106 | :return: 107 | ''' 108 | import itertools 109 | # 4 * 2 110 | _, axs = plt.subplots(nrows=4, ncols=2, figsize=(20, 15)) 111 | # 将画布序列拉平 112 | axs_list = list(itertools.chain.from_iterable(axs)) 113 | 114 | for symbol, ax in zip(choice_symbols, axs_list): 115 | # 绘制猪老三世界的股价走势 116 | another_word_dict[symbol].close.plot(ax=ax) 117 | # 同样的股票在真实世界的股价走势 118 | real_dict[symbol].close.plot(ax=ax) 119 | ax.set_title(symbol) 120 | 121 | 122 | def gen_pig_three_feature(kl_another_word): 123 | ''' 124 | 生成一些会影响当天股票价格/涨跌的参数 125 | 比如前天的价格,前天的涨跌幅等等 126 | :param kl_another_word: 127 | :return: 128 | ''' 129 | # y值使用close.pct_change即涨跌幅度 130 | kl_another_word['regress_y'] = kl_another_word.close.pct_change() 131 | # 前天收盘价格 132 | kl_another_word['bf_yesterday_close'] = 0 133 | # 昨天收盘价格 134 | kl_another_word['yesterday_close'] = 0 135 | # 昨天收盘成交量 136 | kl_another_word['yesterday_volume'] = 0 137 | # 前天收盘成交量 138 | kl_another_word['bf_yesterday_volume'] = 0 139 | 140 | # 对齐特征,前天收盘价格即与今天的收盘错2个时间单位,[2:] = [:-2] 141 | kl_another_word['bf_yesterday_close'][2:] = \ 142 | kl_another_word['close'][:-2] 143 | # 对齐特征,前天成交量 144 | kl_another_word['bf_yesterday_volume'][2:] = \ 145 | kl_another_word['volume'][:-2] 146 | # 对齐特征,昨天收盘价与今天的收盘错1个时间单位,[1:] = [:-1] 147 | kl_another_word['yesterday_close'][1:] = \ 148 | kl_another_word['close'][:-1] 149 | # 对齐特征,昨天成交量 150 | kl_another_word['yesterday_volume'][1:] = \ 151 | kl_another_word['volume'][:-1] 152 | 153 | # 特征1: 价格差 154 | kl_another_word['feature_price_change'] = \ 155 | kl_another_word['yesterday_close'] - \ 156 | kl_another_word['bf_yesterday_close'] 157 | 158 | # 特征2: 成交量差 159 | kl_another_word['feature_volume_Change'] = \ 160 | kl_another_word['yesterday_volume'] - \ 161 | kl_another_word['bf_yesterday_volume'] 162 | 163 | # 特征3: 涨跌sign 164 | kl_another_word['feature_sign'] = np.sign( 165 | kl_another_word['feature_price_change'] * kl_another_word[ 166 | 'feature_volume_Change']) 167 | 168 | # 特征4: 周几 169 | kl_another_word['feature_date_week'] = kl_another_word[ 170 | 'date_week'] 171 | 172 | """ 173 | 构建噪音特征, 因为猪老三也不可能全部分析正确真实的特征因素 174 | 这里引入一些噪音特征 175 | """ 176 | # 成交量乘积 177 | kl_another_word['feature_volume_noise'] = \ 178 | kl_another_word['yesterday_volume'] * \ 179 | kl_another_word['bf_yesterday_volume'] 180 | 181 | # 价格乘积 182 | kl_another_word['feature_price_noise'] = \ 183 | kl_another_word['yesterday_close'] * \ 184 | kl_another_word['bf_yesterday_close'] 185 | 186 | # 将数据标准化 187 | scaler = preprocessing.StandardScaler() 188 | kl_another_word['feature_price_change'] = scaler.fit_transform( 189 | kl_another_word['feature_price_change'].values.reshape(-1, 1)) 190 | kl_another_word['feature_volume_Change'] = scaler.fit_transform( 191 | kl_another_word['feature_volume_Change'].values.reshape(-1, 1)) 192 | kl_another_word['feature_volume_noise'] = scaler.fit_transform( 193 | kl_another_word['feature_volume_noise'].values.reshape(-1, 1)) 194 | kl_another_word['feature_price_noise'] = scaler.fit_transform( 195 | kl_another_word['feature_price_noise'].values.reshape(-1, 1)) 196 | 197 | # 只筛选feature_开头的特征和regress_y,抛弃前两天数据,即[2:] 198 | kl_pig_three_feature = kl_another_word.filter( 199 | regex='regress_y|feature_*')[2:] 200 | return kl_pig_three_feature 201 | 202 | 203 | def gen_feature_from_symbol(symbol): 204 | """ 205 | 封装由一个symbol转换为特征矩阵序列函数 206 | :param symbol: 207 | :return: 208 | """ 209 | # 真实世界走势数据转换到老三的世界 210 | kl_another_word = change_real_to_another_word(symbol) 211 | # 由走势转换为特征dataframe通过gen_pig_three_feature 212 | kl_another_word_feature_test = \ 213 | gen_pig_three_feature(kl_another_word) 214 | 215 | # 构建训练的特征和label 216 | train_x = kl_another_word_feature_test.drop(['regress_y'], axis=1) 217 | train_y = pd.DataFrame(kl_another_word_feature_test['regress_y']) 218 | 219 | train_y.fillna(train_y.mean(), inplace=True) 220 | train_y_classification = train_y.copy() 221 | train_y_classification.loc[train_y_classification['regress_y'] > 0, 'regress_y'] = 1 222 | train_y_classification.loc[train_y_classification['regress_y'] <= 0, 'regress_y'] = 0 223 | 224 | return train_x, train_y, train_y_classification, \ 225 | kl_another_word_feature_test 226 | 227 | 228 | def regress_process(estimator, train_x, train_y_regress, test_x, 229 | test_y_regress): 230 | ''' 231 | 这里是回归问题 232 | :param estimator: 233 | :param train_x: 234 | :param train_y_regress: 235 | :param test_x: 236 | :param test_y_regress: 237 | :return: 238 | ''' 239 | # 用数据开始训练 240 | estimator.fit(train_x, train_y) 241 | # 用模型开始预测 242 | test_y_prdict_regress = estimator.predict(test_x) 243 | 244 | # 绘制结果 245 | # 绘制usFB实际股价涨跌幅度 246 | plt.plot(np.arange(0,len(test_y_regress)),test_y_regress.regress_y.cumsum()) 247 | # 绘制通过模型预测的usFB股价涨跌幅度 248 | plt.plot(test_y_prdict_regress.cumsum()) 249 | 250 | # 做一下交叉验证 251 | scores = cross_validation.cross_val_score(estimator, train_x, train_y_regress, cv=10, scoring='mean_squared_error') 252 | mean_sc = np.mean(np.sqrt(-scores)) 253 | print(mean_sc) 254 | 255 | def classification_procss(estimator,train_x,train_y_classification,test_x,test_y_classification): 256 | # 训练模型 257 | estimator.fit(train_x,train_y_classification) 258 | # 预测 259 | print(test_x.head()) 260 | test_y_prdict_classification=estimator.predict(test_x) 261 | 262 | # 通过metrics.accuracy_score度量预测涨跌的准确率 263 | print("{} accuracy = {:.2f}".format( 264 | estimator.__class__.__name__, 265 | metrics.accuracy_score(test_y_classification, 266 | test_y_prdict_classification))) 267 | 268 | # 针对训练集数据做交叉验证scoring='accuracy',cv=10 269 | # scores = cross_validation.cross_val_score(estimator, train_x, 270 | # train_y_classification, 271 | # cv=10, 272 | # scoring='accuracy') 273 | # 所有交叉验证的分数取平均值 274 | # mean_sc = np.mean(scores) 275 | # print('cross validation accuracy mean: {:.2f}'.format(mean_sc)) 276 | 277 | if __name__ == '__main__': 278 | ''' 279 | 上面的: 280 | _gen_another_word_price(kl_another_word): 281 | _gen_another_word_price_rule(。。。) 282 | change_real_to_another_word(symbol): 283 | 都是在生成假的数据而已。 284 | ''' 285 | # choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG', 286 | # 'usTSLA', 'usWUBA', 'usVIPS'] 287 | choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU','usGOOG'] 288 | another_word_dict = {} 289 | real_dict = {} 290 | for symbol in choice_symbols: 291 | # 猪老三世界的股票走势字典 292 | another_word_dict[symbol] = change_real_to_another_word(symbol) 293 | # 真实世界的股票走势字典,这里不考虑运行效率问题 294 | real_dict[symbol] = ABuSymbolPd.make_kl_df(symbol) 295 | # 表10-1所示,每支股票大概500个数据 296 | print(another_word_dict['usNOAH'].head()) 297 | # 创建特征的时候,可以用shift,而不用上面函数里那么[1:]=[:-1]的写法 298 | # print(another_word_dict['usNOAH'].volume.shift(1).head()) 299 | 300 | # 通过原始数据的组合得到新的特征 301 | pig_three_feature = None 302 | for symbol in choice_symbols: 303 | kl_another_word = another_word_dict[symbol] 304 | kl_feature = gen_pig_three_feature(kl_another_word) 305 | pig_three_feature = kl_another_word if pig_three_feature is None else pig_three_feature.append(kl_feature) 306 | print(pig_three_feature.shape) 307 | 308 | features_pig_three = pig_three_feature.fillna(0) 309 | features_pig_three = features_pig_three.filter( 310 | ['regress_y', 'feature_price_change', 'feature_volume_Change', 'feature_sign', 'feature_date_week', 311 | 'feature_volume_noise', 'feature_price_noise']) 312 | 313 | # 构建训练的特征和label 314 | train_x = features_pig_three.drop(['regress_y'], axis=1) 315 | train_y = pd.DataFrame(pig_three_feature['regress_y']) 316 | train_y.fillna(train_y.mean(), inplace=True) 317 | train_y_classification = train_y.copy() 318 | train_y_classification.loc[train_y_classification['regress_y'] > 0, 'regress_y'] = 1 319 | train_y_classification.loc[train_y_classification['regress_y'] <= 0, 'regress_y'] = 0 320 | 321 | print(train_y.head()) 322 | print(train_x.head()) 323 | print(train_y_classification.head()) 324 | 325 | # 拿到测试数据 326 | test_x, test_y_regress, test_y_classification, \ 327 | kl_another_word_feature_test = gen_feature_from_symbol('usFB') 328 | 329 | print(train_x.shape) 330 | print(test_x.shape) 331 | 332 | # 调用,开始做回归 333 | # estimator = LinearRegression() 334 | # estimator=AdaBoostRegressor(n_estimators=100) 335 | # estimator=RandomForestRegressor(n_estimators=100) 336 | # regress_process(estimator, train_x, train_y, test_x, test_y_regress) 337 | # plt.show() 338 | 339 | # 调用,开始做分类 340 | # estimator=LogisticRegression(C=1.0,penalty='l1',tol=1e-6) 341 | estimator=SVC(kernel='rbf') 342 | classification_procss(estimator,train_x.head(200),train_y_classification.head(200),test_x,test_y_classification) 343 | 344 | 345 | --------------------------------------------------------------------------------