merge: 合并 wikillm 与 wikillm-git,全部内容入库(abuquant-src/OCR产物/images/IDEA.md)

This commit is contained in:
hermes-container
2026-08-09 17:34:07 +08:00
parent 719f2b3e7a
commit e9fdfcb90d
845 changed files with 291614 additions and 12 deletions
@@ -0,0 +1,5 @@
# -*- encoding:utf-8 -*-
import os
import sys
sys.path.append(os.path.abspath('../'))
+967
View File
@@ -0,0 +1,967 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
# import warnings
# noinspection PyUnresolvedReferences
import abu_local_env
import abupy
from abupy import abu
from abupy import ABuSymbolPd
import sklearn.preprocessing as preprocessing
# warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
# 使用沙盒数据,目的是和书中一样的数据环境
abupy.env.enable_example_env_ipython()
"""
第10章 量化系统——机器学习•猪老三
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
"""
"""
10.2 猪老三世界中的量化环境
"""
"""
是否开启date_week噪音, 开启这个的目的是让分类结果正确率降低,接近真实
"""
g_with_date_week_noise = False
def _gen_another_word_price(kl_another_word):
"""
生成股票在另一个世界中的价格
:param kl_another_word:
:return:
"""
for ind in np.arange(2, kl_another_word.shape[0]):
# 前天数据
bf_yesterday = kl_another_word.iloc[ind - 2]
# 昨天
yesterday = kl_another_word.iloc[ind - 1]
# 今天
today = kl_another_word.iloc[ind]
# 生成今天的收盘价格
kl_another_word.close[ind] = _gen_another_word_price_rule(
yesterday.close, yesterday.volume,
bf_yesterday.close, bf_yesterday.volume,
today.volume, today.date_week)
def _gen_another_word_price_rule(yesterday_close, yesterday_volume,
bf_yesterday_close,
bf_yesterday_volume,
today_volume, date_week):
"""
通过前天收盘量价,昨天收盘量价,今天的量,构建另一个世界中的价格模型
"""
# 昨天收盘价格与前天收盘价格的价格差
price_change = yesterday_close - bf_yesterday_close
# 昨天成交量与前天成交量的量差
volume_change = yesterday_volume - bf_yesterday_volume
# 如果量和价变动一致,今天价格涨,否则跌
# 即量价齐涨->涨, 量价齐跌->涨,量价不一致->跌
sign = 1.0 if price_change * volume_change > 0 else -1.0
# 通过date_week生成噪音,否则之后分类100%分对
if g_with_date_week_noise:
# 针对sign生成噪音,噪音的生效的先决条件是今天的量是这三天最大的
gen_noise = today_volume > np.max(
[yesterday_volume, bf_yesterday_volume])
# 如果量是这三天最大 且是周五,下跌
if gen_noise and date_week == 4:
sign = -1.0
# 如果量是这三天最大,如果是周一,上涨
elif gen_noise and date_week == 0:
sign = 1.0
# 今天的涨跌幅度基础是price_change(昨天前天的价格变动)
price_base = abs(price_change)
# 今天的涨跌幅度变动因素:量比,
# 今天的成交量/昨天的成交量 和 今天的成交量/前天的成交量 的均值
price_factor = np.mean([today_volume / yesterday_volume,
today_volume / bf_yesterday_volume])
if abs(price_base * price_factor) < yesterday_close * 0.10:
# 如果 量比 * price_base 没超过10%,今天价格计算
today_price = yesterday_close + \
sign * price_base * price_factor
else:
# 如果涨跌幅度超过10%,限制上限,下限为10%
today_price = yesterday_close + sign * yesterday_close * 0.10
return today_price
def change_real_to_another_word(symbol):
"""
将原始真正的股票数据价格列只保留前两天数据,成交量,周几列完全保留
价格列其他数据使用_gen_another_word_price变成另一个世界价格
:param symbol:
:return:
"""
kl_pd = ABuSymbolPd.make_kl_df(symbol)
if kl_pd is not None:
# 原始股票数据也只保留价格,周几,成交量
kl_pig_three = kl_pd.filter(['close', 'date_week', 'volume'])
# 只保留原始头两天的交易收盘价格,其他的的都赋予nan
kl_pig_three['close'][2:] = np.nan
# 将其他nan价格变成猪老三世界中价格使用_gen_another_word_price
_gen_another_word_price(kl_pig_three)
return kl_pig_three
def sample_102(show=True):
"""
10.2 生成猪老三的世界中的映射股票数据
:return:
"""
choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG',
'usTSLA', 'usWUBA', 'usVIPS']
another_word_dict = {}
real_dict = {}
for symbol in choice_symbols:
# 猪老三世界的股票走势字典
another_word_dict[symbol] = change_real_to_another_word(symbol)
# 真实世界的股票走势字典,这里不考虑运行效率问题
real_dict[symbol] = ABuSymbolPd.make_kl_df(symbol)
if show:
# 表10-1所示
print('another_word_dict[usNOAH].head():\n', another_word_dict['usNOAH'].head())
print('real_dict[usNOAH].head():\n', real_dict['usNOAH'].head().filter(['close', 'date_week', 'volume']))
import itertools
# 4 2
_, axs = plt.subplots(nrows=4, ncols=2, figsize=(20, 15))
# 将画布序列拉平
axs_list = list(itertools.chain.from_iterable(axs))
for symbol, ax in zip(choice_symbols, axs_list):
# 绘制猪老三世界的股价走势
another_word_dict[symbol].close.plot(ax=ax)
# 同样的股票在真实世界的股价走势
real_dict[symbol].close.plot(ax=ax)
ax.set_title(symbol)
plt.show()
return another_word_dict
"""
10.3 有监督机器学习
"""
def gen_pig_three_feature(kl_another_word):
"""
猪老三构建特征模型函数
:param kl_another_word: 即上一节使用_gen_another_word_price
生成的dataframe有收盘价,周几,成交量列
:return:
"""
# y值使用close.pct_change即涨跌幅度
kl_another_word['regress_y'] = kl_another_word.close.pct_change()
# 前天收盘价格
kl_another_word['bf_yesterday_close'] = 0
# 昨天收盘价格
kl_another_word['yesterday_close'] = 0
# 昨天收盘成交量
kl_another_word['yesterday_volume'] = 0
# 前天收盘成交量
kl_another_word['bf_yesterday_volume'] = 0
# 对齐特征,前天收盘价格即与今天的收盘错2个时间单位,[2:] = [:-2]
kl_another_word['bf_yesterday_close'][2:] = \
kl_another_word['close'][:-2]
# 对齐特征,前天成交量
kl_another_word['bf_yesterday_volume'][2:] = \
kl_another_word['volume'][:-2]
# 对齐特征,昨天收盘价与今天的收盘错1个时间单位,[1:] = [:-1]
kl_another_word['yesterday_close'][1:] = \
kl_another_word['close'][:-1]
# 对齐特征,昨天成交量
kl_another_word['yesterday_volume'][1:] = \
kl_another_word['volume'][:-1]
# 特征1: 价格差
kl_another_word['feature_price_change'] = \
kl_another_word['yesterday_close'] - \
kl_another_word['bf_yesterday_close']
# 特征2: 成交量差
kl_another_word['feature_volume_Change'] = \
kl_another_word['yesterday_volume'] - \
kl_another_word['bf_yesterday_volume']
# 特征3: 涨跌sign
kl_another_word['feature_sign'] = np.sign(
kl_another_word['feature_price_change'] * kl_another_word[
'feature_volume_Change'])
# 特征4: 周几
kl_another_word['feature_date_week'] = kl_another_word[
'date_week']
"""
构建噪音特征, 因为猪老三也不可能全部分析正确真实的特征因素
这里引入一些噪音特征
"""
# 成交量乘积
kl_another_word['feature_volume_noise'] = \
kl_another_word['yesterday_volume'] * \
kl_another_word['bf_yesterday_volume']
# 价格乘积
kl_another_word['feature_price_noise'] = \
kl_another_word['yesterday_close'] * \
kl_another_word['bf_yesterday_close']
# 将数据标准化
scaler = preprocessing.StandardScaler()
kl_another_word['feature_price_change'] = scaler.fit_transform(
kl_another_word['feature_price_change'].values.reshape(-1, 1))
kl_another_word['feature_volume_Change'] = scaler.fit_transform(
kl_another_word['feature_volume_Change'].values.reshape(-1, 1))
kl_another_word['feature_volume_noise'] = scaler.fit_transform(
kl_another_word['feature_volume_noise'].values.reshape(-1, 1))
kl_another_word['feature_price_noise'] = scaler.fit_transform(
kl_another_word['feature_price_noise'].values.reshape(-1, 1))
# 只筛选feature_开头的特征和regress_y,抛弃前两天数据,即[2:]
kl_pig_three_feature = kl_another_word.filter(
regex='regress_y|feature_*')[2:]
return kl_pig_three_feature
def sample_103_0(show=True):
"""
10.3 生成猪老三的训练集特征示例
:return:
"""
another_word_dict = sample_102(show=False)
pig_three_feature = None
for symbol in another_word_dict:
# 首先拿出对应的走势数据
kl_another_word = another_word_dict[symbol]
# 通过走势数据生成训练集特征通过gen_pig_three_feature
kl_feature = gen_pig_three_feature(kl_another_word)
# 将每个股票的特征数据都拼接起来,形成训练集
pig_three_feature = kl_feature if pig_three_feature is None \
else pig_three_feature.append(kl_feature)
# Dataframe -> matrix
feature_np = pig_three_feature.as_matrix()
# x特征矩阵
train_x = feature_np[:, 1:]
# 回归训练的连续值y
train_y_regress = feature_np[:, 0]
# 分类训练的离散值y,之后分类技术使用
# noinspection PyTypeChecker
train_y_classification = np.where(train_y_regress > 0, 1, 0)
if show:
print('pig_three_feature.shape:', pig_three_feature.shape)
print('pig_three_feature.tail():\n', pig_three_feature.tail())
print('train_x[:5], train_y_regress[:5], train_y_classification[:5]:\n', train_x[:5], train_y_regress[:5],
train_y_classification[:5])
return train_x, train_y_regress, train_y_classification, pig_three_feature
"""
猪老三使用回归预测股价
"""
def sample_1031_1():
"""
10.3.1_1 猪老三使用回归预测股价:生成训练集数据和测试集数据
:return:
"""
# noinspection PyShadowingNames
def gen_feature_from_symbol(symbol):
"""
封装由一个symbol转换为特征矩阵序列函数
:param symbol:
:return:
"""
# 真实世界走势数据转换到老三的世界
kl_another_word = change_real_to_another_word(symbol)
# 由走势转换为特征dataframe通过gen_pig_three_feature
kl_another_word_feature_test = gen_pig_three_feature(kl_another_word)
# 转换为matrix
feature_np_test = kl_another_word_feature_test.as_matrix()
# 从matrix抽取y回归
test_y_regress = feature_np_test[:, 0]
# y回归 > y分类
# noinspection PyTypeChecker
test_y_classification = np.where(test_y_regress > 0, 1, 0)
# 从matrix抽取x特征矩阵
test_x = feature_np_test[:, 1:]
return test_x, test_y_regress, test_y_classification, kl_another_word_feature_test
# 生成训练集数据
train_x, train_y_regress, train_y_classification, pig_three_feature = sample_103_0(show=False)
# 生成测试集数据
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = gen_feature_from_symbol('usFB')
print('训练集:{}, 测试集:{}'.format(pig_three_feature.shape[0], kl_another_word_feature_test.shape[0]))
return train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test
def regress_process(estimator, train_x, train_y_regress, test_x,
test_y_regress):
# 训练训练集数据
estimator.fit(train_x, train_y_regress)
# 使用训练好的模型预测测试集对应的y,即根据usFB的走势特征预测股价涨跌幅度
test_y_prdict_regress = estimator.predict(test_x)
# 绘制usFB实际股价涨跌幅度
plt.plot(test_y_regress.cumsum())
# 绘制通过模型预测的usFB股价涨跌幅度
plt.plot(test_y_prdict_regress.cumsum())
# 针对训练集数据做交叉验证
from abupy import cross_val_score
from abupy.CoreBu.ABuFixes import mean_squared_error_scorer
scores = cross_val_score(estimator, train_x,
train_y_regress, cv=10,
scoring=mean_squared_error_scorer)
# mse开方 -> rmse
mean_sc = -np.mean(np.sqrt(-scores))
print('{} RMSE: {}'.format(estimator.__class__.__name__, mean_sc))
def sample_1031_2():
"""
10.3.1_2 猪老三使用回归预测股价:LinearRegressio
:return:
"""
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
# 实例化线性回归对象estimator
from sklearn.linear_model import LinearRegression
estimator = LinearRegression()
# 将回归模型对象,训练集x,训练集连续y值,测试集x,测试集连续y传入
regress_process(estimator, train_x, train_y_regress, test_x,
test_y_regress)
plt.show()
from abupy import ABuMLExecute
ABuMLExecute.plot_learning_curve(estimator, train_x, train_y_regress, cv=10)
def sample_1031_3():
"""
10.3.1_3 猪老三使用回归预测股价:PolynomialFeatures
:return:
"""
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
# pipeline套上 degree=3 LinearRegression
estimator = make_pipeline(PolynomialFeatures(degree=3),
LinearRegression())
# 继续使用regress_process,区别是estimator变了
regress_process(estimator, train_x, train_y_regress, test_x,
test_y_regress)
plt.show()
def sample_1031_4():
"""
10.3.1_4 猪老三使用回归预测股价:使用集成学习算法预测股价AdaBoost与RandomForest
:return:
"""
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
# AdaBoost
from sklearn.ensemble import AdaBoostRegressor
estimator = AdaBoostRegressor(n_estimators=100)
regress_process(estimator, train_x, train_y_regress, test_x,
test_y_regress)
plt.show()
# RandomForest
from sklearn.ensemble import RandomForestRegressor
estimator = RandomForestRegressor(n_estimators=100)
regress_process(estimator, train_x, train_y_regress, test_x, test_y_regress)
plt.show()
"""
10.3.2 猪老三使用分类预测股票涨跌
"""
def classification_process(estimator, train_x, train_y_classification,
test_x, test_y_classification):
from sklearn import metrics
# 训练数据,这里分类要所以要使用y_classification
estimator.fit(train_x, train_y_classification)
# 使用训练好的分类模型预测测试集对应的y,即根据usFB的走势特征预测涨跌
test_y_prdict_classification = estimator.predict(test_x)
# 通过metrics.accuracy_score度量预测涨跌的准确率
print("{} accuracy = {:.2f}".format(
estimator.__class__.__name__,
metrics.accuracy_score(test_y_classification,
test_y_prdict_classification)))
from abupy import cross_val_score
# 针对训练集数据做交叉验证scoring='accuracy'cv10
scores = cross_val_score(estimator, train_x,
train_y_classification,
cv=10,
scoring='accuracy')
# 所有交叉验证的分数取平均值
mean_sc = np.mean(scores)
print('cross validation accuracy mean: {:.2f}'.format(mean_sc))
def sample_1032_1():
"""
10.3.2_1 猪老三使用分类预测股票涨跌:LogisticRegression
:return:
"""
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
# 无噪音分类正确100%
from sklearn.linear_model import LogisticRegression
estimator = LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
# 将分类器,训练集x,训练集y分类,测试集,测试集y分别传入函数
classification_process(estimator, train_x, train_y_classification,
test_x, test_y_classification)
# 开启噪音,再来一遍,有噪音正确率93%, 之后的都开启g_with_date_week_noise
global g_with_date_week_noise
g_with_date_week_noise = True
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
classification_process(estimator, train_x, train_y_classification,
test_x, test_y_classification)
def sample_1032_2():
"""
10.3.2_2 猪老三使用分类预测股票涨跌:svm
:return:
"""
global g_with_date_week_noise
g_with_date_week_noise = True
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
from sklearn.svm import SVC
estimator = SVC(kernel='rbf')
classification_process(estimator, train_x, train_y_classification,
test_x, test_y_classification)
def sample_1032_3():
"""
10.3.2_3 猪老三使用分类预测股票涨跌:RandomForestClassifier
:return:
"""
global g_with_date_week_noise
g_with_date_week_noise = True
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
from sklearn.ensemble import RandomForestClassifier
estimator = RandomForestClassifier(n_estimators=100)
classification_process(estimator, train_x, train_y_classification,
test_x, test_y_classification)
def sample_1032_4(show=True):
"""
10.3.2_4 猪老三使用分类预测股票涨跌:train_test_split
:return:
"""
from sklearn import metrics
from abupy import train_test_split
# noinspection PyShadowingNames
def train_test_split_xy(estimator, x, y, test_size=0.5,
random_state=0):
# 通过train_test_split将原始训练集随机切割为新训练集与测试集
train_x, test_x, train_y, test_y = \
train_test_split(x, y, test_size=test_size,
random_state=random_state)
if show:
print(x.shape, y.shape)
print(train_x.shape, train_y.shape)
print(test_x.shape, test_y.shape)
clf = estimator.fit(train_x, train_y)
predictions = clf.predict(test_x)
if show:
# 度量准确率
print("accuracy = %.2f" %
(metrics.accuracy_score(test_y, predictions)))
# 度量查准率
print("precision_score = %.2f" %
(metrics.precision_score(test_y, predictions)))
# 度量回收率
print("recall_score = %.2f" %
(metrics.recall_score(test_y, predictions)))
return test_y, predictions
global g_with_date_week_noise
g_with_date_week_noise = True
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
from sklearn.ensemble import RandomForestClassifier
estimator = RandomForestClassifier(n_estimators=100)
test_y, predictions = train_test_split_xy(estimator, train_x, train_y_classification)
return estimator, train_x, train_y_classification, test_y, predictions
def sample_1032_5():
"""
10.3.2_5 猪老三使用分类预测股票涨跌:混淆矩阵和roc曲线
:return:
"""
from sklearn import metrics
# noinspection PyShadowingNames
def confusion_matrix_with_report(test_y, predictions):
confusion_matrix = metrics.confusion_matrix(test_y, predictions)
# print("Confusion Matrix ", confusion_matrix)
print(" Predicted")
print(" | 0 | 1 |")
print(" |-----|-----|")
print(" 0 | %3d | %3d |" % (confusion_matrix[0, 0],
confusion_matrix[0, 1]))
print("Actual |-----|-----|")
print(" 1 | %3d | %3d |" % (confusion_matrix[1, 0],
confusion_matrix[1, 1]))
print(" |-----|-----|")
print(metrics.classification_report(test_y, predictions))
estimator, train_x, train_y_classification, test_y, predictions = sample_1032_4(show=False)
confusion_matrix_with_report(test_y, predictions)
from abupy import ABuMLExecute
ABuMLExecute.plot_roc_estimator(estimator, train_x, train_y_classification)
def sample_1033_1():
"""
10.3.3 通过决策树分类,绘制出决策图
这里需要安装dot graphviz,才能通过os.system("dot -T png graphviz.dot -o graphviz.png")生成png
:return:
"""
from sklearn.tree import DecisionTreeClassifier
from sklearn import tree
import os
estimator = DecisionTreeClassifier(max_depth=2, random_state=1)
# noinspection PyShadowingNames
def graphviz_tree(estimator, features, x, y):
if not hasattr(estimator, 'tree_'):
print('only tree can graphviz!')
return
estimator.fit(x, y)
# 将决策模型导出graphviz.dot文件
tree.export_graphviz(estimator.tree_, out_file='graphviz.dot',
feature_names=features)
# 通过dot将模型绘制决策图,保存png
os.system("dot -T png graphviz.dot -o graphviz.png")
global g_with_date_week_noise
g_with_date_week_noise = True
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
# 这里会使用到特征的名称列pig_three_feature.columns[1:]
graphviz_tree(estimator, pig_three_feature.columns[1:], train_x,
train_y_classification)
import PIL.Image
PIL.Image.open('graphviz.png').show()
def sample_1033_2():
"""
10.3.3 特征的重要性排序及支持度评级
:return:
"""
global g_with_date_week_noise
g_with_date_week_noise = True
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
# noinspection PyShadowingNames
def importances_coef_pd(estimator):
"""
特征的重要性
"""
if hasattr(estimator, 'feature_importances_'):
# 有feature_importances_的通过sort_values排序
return pd.DataFrame(
{'feature': list(pig_three_feature.columns[1:]),
'importance': estimator.feature_importances_}).sort_values('importance')
elif hasattr(estimator, 'coef_'):
# 有coef_的通过coef排序
return pd.DataFrame(
{"columns": list(pig_three_feature.columns)[1:], "coef": list(estimator.coef_.T)}).sort_values('coef')
else:
print('estimator not hasattr feature_importances_ or coef_!')
# 使用随机森林分类器
from sklearn.ensemble import RandomForestClassifier
estimator = RandomForestClassifier(n_estimators=100)
# 训练数据模型
estimator.fit(train_x, train_y_classification)
# 对训练后的模型特征的重要度进行判定,重要程度由小到大,表10-4所示
print('importances_coef_pd(estimator):\n', importances_coef_pd(estimator))
from sklearn.feature_selection import RFE
# noinspection PyShadowingNames
def feature_selection(estimator, x, y):
"""
支持度评级
"""
selector = RFE(estimator)
selector.fit(x, y)
print('RFE selection')
print(pd.DataFrame(
{'support': selector.support_, 'ranking': selector.ranking_},
index=pig_three_feature.columns[1:]))
print('feature_selection(estimator, train_x, train_y_classification):\n',
feature_selection(estimator, train_x, train_y_classification))
"""
10.4 无监督机器学习
"""
def sample_1041():
"""
10.4.1 使用降维可视化数据
:return:
"""
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
from sklearn.decomposition import PCA
from abupy import ABuMLExecute
# noinspection PyShadowingNames
def plot_decision_function(estimator, x, y):
# pca进行降维,只保留2个特征序列
pca_2n = PCA(n_components=2)
x = pca_2n.fit_transform(x)
# 进行训练
estimator.fit(x, y)
plt.scatter(x[:, 0], x[:, 1], c=y, s=50, cmap='spring')
ABuMLExecute.plot_decision_boundary(
lambda p_x: estimator.predict(p_x), x, y)
from sklearn.ensemble import RandomForestClassifier
estimator = RandomForestClassifier(n_estimators=100)
plot_decision_function(estimator, train_x, train_y_classification)
# noinspection PyTypeChecker
def sample_1042():
"""
10.4.2 猪老三使用聚类算法提高正确率
:return:
"""
global g_with_date_week_noise
g_with_date_week_noise = True
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
# 使用随机森林作为分类器
from sklearn.ensemble import RandomForestClassifier
estimator = RandomForestClassifier(n_estimators=100)
estimator.fit(train_x, train_y_classification)
test_y_prdict_classification = estimator.predict(test_x)
from sklearn import metrics
print("accuracy = %.2f" % (
metrics.accuracy_score(test_y_classification,
test_y_prdict_classification)))
# 测试集feature即usFB的kl feature
pig_three_kmean_feature = kl_another_word_feature_test
# 测试集真实的涨跌结果test_y_classification
pig_three_kmean_feature['y'] = test_y_classification
# 使用刚刚的随机森林作为分类器的预测涨跌结果test_y_prdict_classification
pig_three_kmean_feature['y_prdict'] = test_y_prdict_classification
# 即生成一列新数据记录预测是否正确
pig_three_kmean_feature['y_same'] = np.where(
pig_three_kmean_feature['y'] ==
pig_three_kmean_feature['y_prdict'], 1, 0)
# 将feature中只保留刚刚得到的y_same
pig_three_kmean_feature = pig_three_kmean_feature.filter(['y_same'])
from sklearn.cluster import KMeans
# 使用刚刚得到的只有y_same列的数据赋值x_kmean
x_kmean = pig_three_kmean_feature.values
# n_clusters=2, 即只聚两类数据
kmean = KMeans(n_clusters=2)
kmean.fit(x_kmean)
# 将聚类标签赋予新的一列cluster
pig_three_kmean_feature['cluster'] = kmean.predict(x_kmean)
# 将周几这个特征合并过来
pig_three_kmean_feature['feature_date_week'] = \
kl_another_word_feature_test['feature_date_week']
# 表10-5所示
print('pig_three_kmean_feature.tail():\n', pig_three_kmean_feature.tail())
# 表10-6所示
print('pd.crosstab(pig_three_kmean_feature.feature_date_week, pig_three_kmean_feature.cluster):\n',
pd.crosstab(pig_three_kmean_feature.feature_date_week, pig_three_kmean_feature.cluster))
"""
10.5 梦醒时分
"""
def sample_105_0():
"""
10.5 AbuML
:return:
"""
global g_with_date_week_noise
g_with_date_week_noise = True
train_x, train_y_regress, train_y_classification, pig_three_feature, \
test_x, test_y_regress, test_y_classification, kl_another_word_feature_test = sample_1031_1()
from abupy import AbuML
# 通过x, y矩阵和特征的DataFrame对象组成AbuML
ml = AbuML(train_x, train_y_classification, pig_three_feature)
# 使用随机森林作为分类器
_ = ml.estimator.random_forest_classifier()
# 交织验证结果的正确率
print('ml.cross_val_accuracy_score():\n', ml.cross_val_accuracy_score())
# 特征的选择
print('ml.feature_selection():\n', ml.feature_selection())
"""
如下内容不能使用沙盒环境, 建议对照阅读:
abu量化文档-第十九节 数据源
第20节 美股交易UMP决策
"""
def sample_1051_0():
"""
10.5.1 回测中生成特征,切分训练测试集,成交买单快照: 数据准备
如果没有运行过abu量化文档-第十九节 数据源:中使用腾讯数据源进行数据更新,需要运行
如果运行过就不要重复运行了:
"""
from abupy import EMarketTargetType, EMarketSourceType, EDataCacheType
# 关闭沙盒数据环境
abupy.env.disable_example_env_ipython()
abupy.env.g_market_source = EMarketSourceType.E_MARKET_SOURCE_tx
abupy.env.g_data_cache_type = EDataCacheType.E_DATA_CACHE_CSV
# 首选这里预下载市场中所有股票的6年数据(做5年回测,需要预先下载6年数据)
abu.run_kl_update(start='2011-08-08', end='2017-08-08', market=EMarketTargetType.E_MARKET_TARGET_US)
def sample_1051_1(from_cache=False, show=True):
"""
10.5.1 回测中生成特征,切分训练测试集,成交买单快照: 数据准备
:return:
"""
from abupy import AbuMetricsBase
from abupy import AbuFactorBuyBreak
from abupy import AbuFactorAtrNStop
from abupy import AbuFactorPreAtrNStop
from abupy import AbuFactorCloseAtrNStop
# 关闭沙盒数据环境
abupy.env.disable_example_env_ipython()
from abupy import EMarketDataFetchMode
# 因为sample_94_1下载了预先数据,使用缓存,设置E_DATA_FETCH_FORCE_LOCAL,实际上run_kl_update最后会把设置set到FORCE_LOCAL
abupy.env.g_data_fetch_mode = EMarketDataFetchMode.E_DATA_FETCH_FORCE_LOCAL
# 设置选股因子,None为不使用选股因子
stock_pickers = None
# 买入因子依然延用向上突破因子
buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak},
{'xd': 42, 'class': AbuFactorBuyBreak}]
# 卖出因子继续使用上一章使用的因子
sell_factors = [
{'stop_loss_n': 1.0, 'stop_win_n': 3.0,
'class': AbuFactorAtrNStop},
{'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.5},
{'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}
]
# 回测生成买入时刻特征
abupy.env.g_enable_ml_feature = True
# 回测将symbols切割分为训练集数据和测试集数据
abupy.env.g_enable_train_test_split = True
# 下面设置回测时切割训练集,测试集使用的切割比例参数,默认为10,即切割为10份,9份做为训练,1份做为测试,
# 由于美股股票数量多,所以切割分为4份,3份做为训练集,1份做为测试集
abupy.env.g_split_tt_n_folds = 4
from abupy import EStoreAbu
if from_cache:
abu_result_tuple = \
abu.load_abu_result_tuple(n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='train_us')
else:
# 初始化资金500万,资金管理依然使用默认atr
read_cash = 5000000
# 每笔交易的买入基数资金设置为万分之15
abupy.beta.atr.g_atr_pos_base = 0.0015
# 使用run_loop_back运行策略,因子使用和之前一样,
# choice_symbols=None为全市场回测,5年历史数据回测
abu_result_tuple, _ = abu.run_loop_back(read_cash,
buy_factors, sell_factors,
stock_pickers,
choice_symbols=None,
start='2012-08-08', end='2017-08-08')
# 把运行的结果保存在本地,以便之后分析回测使用,保存回测结果数据代码如下所示
abu.store_abu_result_tuple(abu_result_tuple, n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='train_us')
if show:
metrics = AbuMetricsBase(*abu_result_tuple)
metrics.fit_metrics()
metrics.plot_returns_cmp(only_show_returns=True)
"*****************************************************************"
abupy.env.g_enable_train_test_split = False
# 使用切割好的测试数据
abupy.env.g_enable_last_split_test = True
from abupy import EStoreAbu
if from_cache:
abu_result_tuple_test = \
abu.load_abu_result_tuple(n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='test_us')
else:
read_cash = 5000000
abupy.beta.atr.g_atr_pos_base = 0.007
choice_symbols = None
abu_result_tuple_test, kl_pd_manager_test = abu.run_loop_back(read_cash,
buy_factors, sell_factors, stock_pickers,
choice_symbols=choice_symbols, start='2012-08-08',
end='2017-08-08')
abu.store_abu_result_tuple(abu_result_tuple_test, n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='test_us')
if show:
metrics = AbuMetricsBase(*abu_result_tuple_test)
metrics.fit_metrics()
metrics.plot_returns_cmp(only_show_returns=True)
print(abu_result_tuple.orders_pd[abu_result_tuple.orders_pd.result != 0].head())
return abu_result_tuple, abu_result_tuple_test
# noinspection PyUnresolvedReferences
def sample_1052():
"""
10.5.2 基于特征的交易预测
:return:
"""
# 需要在有缓存的情况下运行
abu_result_tuple, _ = sample_1051_1(from_cache=True, show=False)
from abupy.UmpBu.ABuUmpMainMul import UmpMulFiter
mul = UmpMulFiter(orders_pd=abu_result_tuple.orders_pd, scaler=False)
print('mul.df.head():\n', mul.df.head())
# 默认使用svm作为分类器
print('decision_tree_classifier cv please wait...')
mul.estimator.decision_tree_classifier()
mul.cross_val_accuracy_score()
# 默认使用svm作为分类器
print('knn_classifier cv please wait...')
# 默认使用svm作为分类器, 改分类器knn
mul.estimator.knn_classifier()
mul.cross_val_accuracy_score()
from abupy.UmpBu.ABuUmpMainBase import UmpDegFiter
deg = UmpDegFiter(orders_pd=abu_result_tuple.orders_pd)
print('deg.df.head():\n', deg.df.head())
print('xgb_classifier cv please wait...')
# 分类器使用GradientBoosting
deg.estimator.xgb_classifier()
deg.cross_val_accuracy_score()
print('adaboost_classifier cv please wait...')
# 分类器使用adaboost
deg.estimator.adaboost_classifier(base_estimator=None)
deg.cross_val_accuracy_score()
print('train_test_split_xy please wait...')
deg.train_test_split_xy()
if __name__ == "__main__":
sample_102()
# sample_103_0()
# sample_1031_1()
# sample_1031_2()
# sample_1031_3()
# sample_1031_4()
# sample_1032_1()
# sample_1032_2()
# sample_1032_3()
# sample_1032_4()
# sample_1032_5()
# sample_1033_1()
# sample_1033_2()
# sample_1041()
# sample_1042()
# sample_105_0()
# sample_1051_0()
# sample_1051_1(from_cache=True)
# sample_1051_1(from_cache=False)
# sample_1052()
+366
View File
@@ -0,0 +1,366 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
import seaborn as sns
import numpy as np
from sklearn import metrics
import warnings
import ast
# noinspection PyUnresolvedReferences
import abu_local_env
import abupy
from abupy import ml
from abupy import AbuMetricsBase, EStoreAbu, abu
from abupy import ABuMarketDrawing
from abupy import AbuFactorBuyBreak
from abupy import AbuFactorAtrNStop
from abupy import AbuFactorPreAtrNStop
from abupy import AbuFactorCloseAtrNStop
from abupy import EMarketTargetType, EMarketDataFetchMode
from abupy import AbuUmpMainDeg
from abupy import AbuUmpMainJump
from abupy import AbuUmpMainPrice
from abupy import AbuUmpMainWave
# 设置选股因子,None为不使用选股因子
stock_pickers = None
# 买入因子依然延用向上突破因子
buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak},
{'xd': 42, 'class': AbuFactorBuyBreak}]
# 卖出因子继续使用上一章使用的因子
sell_factors = [
{'stop_loss_n': 1.0, 'stop_win_n': 3.0, 'class': AbuFactorAtrNStop},
{'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.5},
{'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}
]
warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
"""
第11章 量化系统-机器学习•ABU
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
* 因为需要全市场回测所以本章无法使用沙盒数据,《量化交易之路》中的原始示例使用的是美股市场,这里的示例改为使用A股市场。
* 本节可以对照阅读abu量化文档第20-23节内容
* 本节的基础是在abu量化文档中第20节内容完成运行后有A股训练集交易和A股测试集交易数据之后
"""
def load_abu_result_tuple():
abupy.env.g_market_target = EMarketTargetType.E_MARKET_TARGET_CN
abupy.env.g_data_fetch_mode = EMarketDataFetchMode.E_DATA_FETCH_FORCE_LOCAL
abu_result_tuple_train = abu.load_abu_result_tuple(n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='train_cn')
abu_result_tuple_test = abu.load_abu_result_tuple(n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='test_cn')
metrics_train = AbuMetricsBase(*abu_result_tuple_train)
metrics_train.fit_metrics()
metrics_test = AbuMetricsBase(*abu_result_tuple_test)
metrics_test.fit_metrics()
return abu_result_tuple_train, abu_result_tuple_test, metrics_train, metrics_test
def sample_110():
abu_result_tuple_train, abu_result_tuple_test, metrics_train, metrics_test = load_abu_result_tuple()
metrics_train.plot_returns_cmp(only_show_returns=True)
metrics_test.plot_returns_cmp(only_show_returns=True)
def sample_111():
"""
11.1 搜索引擎与量化交易
请对照阅读ABU量化系统使用文档 :第16节 UMP主裁交易决策 中相关内容
:return:
"""
abu_result_tuple_train, abu_result_tuple_test, metrics_train, metrics_test = load_abu_result_tuple()
orders_pd_train = abu_result_tuple_train.orders_pd
# 选择失败的前20笔交易绘制交易快照
# 这里只是示例,实战中根据需要挑选,rank或者其他方式
plot_simple = orders_pd_train[orders_pd_train.profit_cg < 0][:20]
# save=True保存在本地,文件保存在~/abu/data/save_png/中
ABuMarketDrawing.plot_candle_from_order(plot_simple, save=True)
"""
11.2 主裁
请对照阅读ABU量化系统使用文档 :第15节 中相关内容
"""
def sample_112():
"""
11.2.1 角度主裁, 11.2.2 使用全局最优对分类簇集合进行筛选
:return:
"""
abu_result_tuple_train, abu_result_tuple_test, metrics_train, metrics_test = load_abu_result_tuple()
orders_pd_train = abu_result_tuple_train.orders_pd
# 参数为orders_pd
ump_deg = AbuUmpMainDeg(orders_pd_train)
# df即由之前ump_main_make_xy生成的类df,表11-1所示
print('ump_deg.fiter.df.head():\n', ump_deg.fiter.df.head())
# 耗时操作,大概需要10几分钟,具体根据电脑性能,cpu情况
_ = ump_deg.fit(brust_min=False)
print('ump_deg.cprs:\n', ump_deg.cprs)
max_failed_cluster = ump_deg.cprs.loc[ump_deg.cprs.lrs.argmax()]
print('失败概率最大的分类簇{0}, 失败率为{1:.2f}%, 簇交易总数{2}, 簇平均交易获利{3:.2f}%'.format(
ump_deg.cprs.lrs.argmax(), max_failed_cluster.lrs * 100, max_failed_cluster.lcs, max_failed_cluster.lms * 100))
cpt = int(ump_deg.cprs.lrs.argmax().split('_')[0])
print('cpt:\n', cpt)
ump_deg.show_parse_rt(ump_deg.rts[cpt])
max_failed_cluster_orders = ump_deg.nts[ump_deg.cprs.lrs.argmax()]
print('max_failed_cluster_orders:\n', max_failed_cluster_orders)
ml.show_orders_hist(max_failed_cluster_orders,
['buy_deg_ang21', 'buy_deg_ang42', 'buy_deg_ang60', 'buy_deg_ang252'])
print('分类簇中deg_ang60平均值为{0:.2f}'.format(
max_failed_cluster_orders.buy_deg_ang60.mean()))
print('分类簇中deg_ang21平均值为{0:.2f}'.format(
max_failed_cluster_orders.buy_deg_ang21.mean()))
print('分类簇中deg_ang42平均值为{0:.2f}'.format(
max_failed_cluster_orders.buy_deg_ang42.mean()))
print('分类簇中deg_ang252平均值为{0:.2f}'.format(
max_failed_cluster_orders.buy_deg_ang252.mean()))
ml.show_orders_hist(orders_pd_train, ['buy_deg_ang21', 'buy_deg_ang42', 'buy_deg_ang60', 'buy_deg_ang252'])
print('训练数据集中deg_ang60平均值为{0:.2f}'.format(
orders_pd_train.buy_deg_ang60.mean()))
print('训练数据集中deg_ang21平均值为{0:.2f}'.format(
orders_pd_train.buy_deg_ang21.mean()))
print('训练数据集中deg_ang42平均值为{0:.2f}'.format(
orders_pd_train.buy_deg_ang42.mean()))
print('训练数据集中deg_ang252平均值为{0:.2f}'.format(
orders_pd_train.buy_deg_ang252.mean()))
"""
11.2.2 使用全局最优对分类簇集合进行筛选
"""
brust_min = ump_deg.brust_min()
print('brust_min:', brust_min)
llps = ump_deg.cprs[(ump_deg.cprs['lps'] <= brust_min[0]) & (ump_deg.cprs['lms'] <= brust_min[1]) & (
ump_deg.cprs['lrs'] >= brust_min[2])]
print('llps:\n', llps)
print(ump_deg.choose_cprs_component(llps))
ump_deg.dump_clf(llps)
"""
11.2.3 跳空主裁
"""
def sample_1123():
"""
11.2.3 跳空主裁
:return:
"""
abu_result_tuple_train, abu_result_tuple_test, metrics_train, metrics_test = load_abu_result_tuple()
orders_pd_train = abu_result_tuple_train.orders_pd
ump_jump = AbuUmpMainJump.ump_main_clf_dump(orders_pd_train, save_order=False)
print(ump_jump.fiter.df.head())
print('失败概率最大的分类簇{0}'.format(ump_jump.cprs.lrs.argmax()))
# 拿出跳空失败概率最大的分类簇
max_failed_cluster_orders = ump_jump.nts[ump_jump.cprs.lrs.argmax()]
# 显示失败概率最大的分类簇,表11-6所示
print('max_failed_cluster_orders:\n', max_failed_cluster_orders)
ml.show_orders_hist(max_failed_cluster_orders, feature_columns=['buy_diff_up_days', 'buy_jump_up_power',
'buy_diff_down_days', 'buy_jump_down_power'])
print('分类簇中jump_up_power平均值为{0:.2f} 向上跳空平均天数{1:.2f}'.format(
max_failed_cluster_orders.buy_jump_up_power.mean(), max_failed_cluster_orders.buy_diff_up_days.mean()))
print('分类簇中jump_down_power平均值为{0:.2f}, 向下跳空平均天数{1:.2f}'.format(
max_failed_cluster_orders.buy_jump_down_power.mean(), max_failed_cluster_orders.buy_diff_down_days.mean()))
print('训练数据集中jump_up_power平均值为{0:.2f},向上跳空平均天数{1:.2f}'.format(
orders_pd_train.buy_jump_up_power.mean(), orders_pd_train.buy_diff_up_days.mean()))
print('训练数据集中jump_down_power平均值为{0:.2f}, 向下跳空平均天数{1:.2f}'.format(
orders_pd_train.buy_jump_down_power.mean(), orders_pd_train.buy_diff_down_days.mean()))
"""
11.2.4 价格主裁
"""
def sample_1124():
"""
11.2.4 价格主裁
:return:
"""
abu_result_tuple_train, abu_result_tuple_test, metrics_train, metrics_test = load_abu_result_tuple()
orders_pd_train = abu_result_tuple_train.orders_pd
ump_price = AbuUmpMainPrice.ump_main_clf_dump(orders_pd_train, save_order=False)
print('ump_price.fiter.df.head():\n', ump_price.fiter.df.head())
print('失败概率最大的分类簇{0}'.format(ump_price.cprs.lrs.argmax()))
# 拿出价格失败概率最大的分类簇
max_failed_cluster_orders = ump_price.nts[ump_price.cprs.lrs.argmax()]
# 表11-8所示
print('max_failed_cluster_orders:\n', max_failed_cluster_orders)
"""
11.2.5 波动主裁
"""
def sample_1125():
"""
11.2.5 波动主裁
:return:
"""
abu_result_tuple_train, abu_result_tuple_test, metrics_train, metrics_test = load_abu_result_tuple()
orders_pd_train = abu_result_tuple_train.orders_pd
# 文件保存在~/abu/data/save_png/中
ump_wave = AbuUmpMainWave.ump_main_clf_dump(orders_pd_train, save_order=True)
print('ump_wave.fiter.df.head():\n', ump_wave.fiter.df.head())
print('失败概率最大的分类簇{0}'.format(ump_wave.cprs.lrs.argmax()))
# 拿出波动特征失败概率最大的分类簇
max_failed_cluster_orders = ump_wave.nts[ump_wave.cprs.lrs.argmax()]
# 表11-10所示
print('max_failed_cluster_orders:\n', max_failed_cluster_orders)
ml.show_orders_hist(max_failed_cluster_orders, feature_columns=['buy_wave_score1', 'buy_wave_score3'])
print('分类簇中wave_score1平均值为{0:.2f}'.format(
max_failed_cluster_orders.buy_wave_score1.mean()))
print('分类簇中wave_score3平均值为{0:.2f}'.format(
max_failed_cluster_orders.buy_wave_score3.mean()))
ml.show_orders_hist(orders_pd_train, feature_columns=['buy_wave_score1', 'buy_wave_score1'])
print('训练数据集中wave_score1平均值为{0:.2f}'.format(
orders_pd_train.buy_wave_score1.mean()))
print('训练数据集中wave_score3平均值为{0:.2f}'.format(
orders_pd_train.buy_wave_score1.mean()))
"""
11.2.6 验证主裁是否称职
请对照阅读ABU量化系统使用文档 :第21节 A股UMP决策 中相关内容
"""
def sample_1126():
"""
11.2.6 验证主裁是否称职
:return:
"""
"""
需要有运行之前的代码即有本地化后的裁判,然后通过如下代码直接加载
"""
ump_deg = AbuUmpMainDeg(predict=True)
ump_jump = AbuUmpMainJump(predict=True)
ump_price = AbuUmpMainPrice(predict=True)
ump_wave = AbuUmpMainWave(predict=True)
def apply_ml_features_ump(order, predicter, need_hit_cnt):
if not isinstance(order.ml_features, dict):
# 低版本pandas dict对象取出来会成为str
ml_features = ast.literal_eval(order.ml_features)
else:
ml_features = order.ml_features
return predicter.predict_kwargs(need_hit_cnt=need_hit_cnt, **ml_features)
abu_result_tuple_train, abu_result_tuple_test, metrics_train, metrics_test = load_abu_result_tuple()
# 选取有交易结果的数据order_has_result
order_has_result = abu_result_tuple_test.orders_pd[abu_result_tuple_test.orders_pd.result != 0]
# 角度主裁开始裁决
order_has_result['ump_deg'] = order_has_result.apply(apply_ml_features_ump, axis=1, args=(ump_deg, 2,))
# 跳空主裁开始裁决
order_has_result['ump_jump'] = order_has_result.apply(apply_ml_features_ump, axis=1, args=(ump_jump, 2,))
# 波动主裁开始裁决
order_has_result['ump_wave'] = order_has_result.apply(apply_ml_features_ump, axis=1, args=(ump_wave, 2,))
# 价格主裁开始裁决
order_has_result['ump_price'] = order_has_result.apply(apply_ml_features_ump, axis=1, args=(ump_price, 2,))
block_pd = order_has_result.filter(regex='^ump_*')
block_pd['sum_bk'] = block_pd.sum(axis=1)
block_pd['result'] = order_has_result['result']
block_pd = block_pd[block_pd.sum_bk > 0]
print('四个裁判整体拦截正确率{:.2f}%'.format(
block_pd[block_pd.result == -1].result.count() / block_pd.result.count() * 100))
print('block_pd.tail():\n', block_pd.tail())
def sub_ump_show(block_name):
sub_block_pd = block_pd[(block_pd[block_name] == 1)]
# 如果失败就正确 1->1 1->0
# noinspection PyTypeChecker
sub_block_pd.result = np.where(sub_block_pd.result == -1, 1, 0)
return metrics.accuracy_score(sub_block_pd[block_name], sub_block_pd.result)
print('角度裁判拦截正确率{:.2f}%'.format(sub_ump_show('ump_deg') * 100))
print('跳空裁判拦截正确率{:.2f}%'.format(sub_ump_show('ump_jump') * 100))
print('波动裁判拦截正确率{:.2f}%'.format(sub_ump_show('ump_wave') * 100))
print('价格裁判拦截正确率{:.2f}%'.format(sub_ump_show('ump_price') * 100))
"""
11.2.7 在abu系统中开启主裁拦截模式
请对照阅读ABU量化系统使用文档 :第21节 A股UMP决策 中相关内容
"""
"""
11.3.1 角度边裁
请对照阅读ABU量化系统使用文档 :第17节 UMP边裁交易决策,第21节 A股UMP决策 中相关内容
11.3.2 价格边裁
请对照阅读ABU量化系统使用文档 :第17节 UMP边裁交易决策,第21节 A股UMP决策 中相关内容
11.3.3 波动边裁
请对照阅读ABU量化系统使用文档 :第17节 UMP边裁交易决策,第21节 A股UMP决策 中相关内容
11.3.4 综合边裁
请对照阅读ABU量化系统使用文档 :第17节 UMP边裁交易决策,第21节 A股UMP决策 中相关内容
11.3.5 验证边裁是否称职
请对照阅读ABU量化系统使用文档 :第21节 A股UMP决策 中相关内容
11.3.6 在abu系统中开启边裁拦截模式
请对照阅读ABU量化系统使用文档 :第21节 A股UMP决策 中相关内容
"""
if __name__ == "__main__":
sample_111()
# sample_112()
# sample_1123()
# sample_1124()
# sample_1125()
# sample_1126()
+923
View File
@@ -0,0 +1,923 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
import logging
import warnings
from abc import ABCMeta, abstractmethod
from collections import OrderedDict
from collections import namedtuple
import itertools
# noinspection PyCompatibility
from concurrent.futures import ProcessPoolExecutor
# noinspection PyCompatibility
from concurrent.futures import ThreadPoolExecutor
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
# noinspection PyUnresolvedReferences
import abu_local_env
import abupy
from abupy import six, xrange, range, reduce, map, filter, partial
from abupy import ABuSymbolPd
warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
# 使用沙盒数据,目的是和书中一样的数据环境
abupy.env.enable_example_env_ipython()
"""
第二章 量化语言——Python
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
"""
def sample_211():
"""
量化语言-Python
:return:
"""
price_str = '30.14, 29.58, 26.36, 32.56, 32.82'
print('type(price_str):', type(price_str))
if not isinstance(price_str, str):
# not代表逻辑‘非’, 如果不是字符串,转换为字符串
price_str = str(price_str)
if isinstance(price_str, int) and price_str > 0:
# and 代表逻辑‘与’,如果是int类型且是正数
price_str += 1
elif isinstance(price_str, float) or float(price_str[:4]) < 0:
# or 代表逻辑‘或’,如果是float或者小于0
price_str += 1.0
else:
try:
raise TypeError('price_str is str type!')
except TypeError:
print('raise, try except')
def sample_212(show=True):
"""
2.1.2 字符串和容器
:return:
"""
show_func = print if show else lambda a: a
price_str = '30.14, 29.58, 26.36, 32.56, 32.82'
show_func('旧的price_str id= {}'.format(id(price_str)))
price_str = price_str.replace(' ', '')
show_func('新的price_str id= {}'.format(id(price_str)))
show_func(price_str)
# split以逗号分割字符串,返回数组price_array
price_array = price_str.split(',')
show_func(price_array)
# price_array尾部append一个重复的32.82
price_array.append('32.82')
show_func(price_array)
show_func(set(price_array))
price_array.remove('32.82')
show_func(price_array)
date_array = []
date_base = 20170118
# 这里用for只是为了计数,无用的变量python建议使用'_'声明
for _ in xrange(0, len(price_array)):
date_array.append(str(date_base))
# 本节只是简单示例,不考虑日期的进位
date_base += 1
show_func(date_array)
date_base = 20170118
date_array = [str(date_base + ind) for ind, _ in enumerate(price_array)]
show_func(date_array)
stock_tuple_list = [(date, price) for date, price in zip(date_array, price_array)]
# tuple访问使用索引
show_func('20170119日价格:{}'.format(stock_tuple_list[1][1]))
show_func(stock_tuple_list)
stock_namedtuple = namedtuple('stock', ('date', 'price'))
stock_namedtuple_list = [stock_namedtuple(date, price) for date, price in zip(date_array, price_array)]
# namedtuple访问使用price
show_func('20170119日价格:{}'.format(stock_namedtuple_list[1].price))
show_func(stock_namedtuple_list)
# 字典推导式:{key: value for in}
stock_dict = {date: price for date, price in zip(date_array, price_array)}
show_func('20170119日价格:{}'.format(stock_dict['20170119']))
show_func(stock_dict)
show_func(stock_dict.keys())
stock_dict = OrderedDict((date, price) for date, price in zip(date_array, price_array))
show_func(stock_dict.keys())
return stock_dict
def sample_221():
"""
2.2.1 函数的使用和定义
:return:
"""
stock_dict = sample_212(show=False)
print('min(stock_dict):', min(stock_dict))
print('min(zip(stock_dict.values(), stock_dict.keys())):', min(zip(stock_dict.values(), stock_dict.keys())))
def find_second_max(dict_array):
# 对传入的dict sorted排序
stock_prices_sorted = sorted(zip(dict_array.values(), dict_array.keys()))
# 第二大的也就是倒数第二个
return stock_prices_sorted[-2]
# 系统函数callable验证是否为一个可call的函数
if callable(find_second_max):
print('find_second_max(stock_dict):', find_second_max(stock_dict))
def sample_222():
"""
2.2.2 lambda函数
:return:
"""
stock_dict = sample_212(show=False)
find_second_max_lambda = lambda dict_array: sorted(zip(dict_array.values(), dict_array.keys()))[-2]
print('find_second_max_lambda(stock_dict):', find_second_max_lambda(stock_dict))
def find_max_and_min(dict_array):
# 对传入的dict sorted排序R
stock_prices_sorted = sorted(zip(dict_array.values(), dict_array.keys()))
return stock_prices_sorted[0], stock_prices_sorted[-1]
print('find_max_and_min(stock_dict):', find_max_and_min(stock_dict))
def sample_223(show=True):
"""
2.2.3 高阶函数
:return:
"""
stock_dict = sample_212(show=False)
show_func = print if show else lambda a: a
# 将字符串的的价格通过列表推导式显示转换为float类型
# 由于stock_dict是OrderedDict所以才可以直接
# 使用stock_dict.values()获取有序日期的收盘价格
price_float_array = [float(price_str) for price_str in stock_dict.values()]
# 通过将时间平移形成两个错开的收盘价序列,通过zip打包成为一个新的序列,
# 通过[:-1]:从第0个到倒数第二个,[1:]:从第一个到最后一个 错开形成相邻
# 组成的序列每个元素为相邻的两个收盘价格
pp_array = [(price1, price2) for price1, price2 in zip(price_float_array[:-1], price_float_array[1:])]
show_func(pp_array)
# list for python3
change_array = list(map(lambda pp: reduce(lambda a, b: round((b - a) / a, 3), pp), pp_array))
# list insert插入数据,将第一天的涨跌幅设置为0
change_array.insert(0, 0)
show_func(change_array)
price_str = '30.14, 29.58, 26.36, 32.56, 32.82'
price_str = price_str.replace(' ', '')
price_array = price_str.split(',')
date_base = 20170118
date_array = [str(date_base + ind) for ind, _ in enumerate(price_array)]
# 使用namedtuple重新构建数据结构
stock_namedtuple = namedtuple('stock', ('date', 'price', 'change'))
# 通过zip分别从date_arrayprice_arraychange_array拿数据组成
# stock_namedtuple然后以date做为key组成OrderedDict
stock_dict = OrderedDict((date, stock_namedtuple(date, price, change)) for date, price, change in
zip(date_array, price_array, change_array))
show_func(stock_dict)
# list for python3
up_days = list(filter(lambda day: day.change > 0, stock_dict.values()))
show_func(up_days)
def filter_stock(stock_array_dict, want_up=True, want_calc_sum=False):
if not isinstance(stock_array_dict, OrderedDict):
raise TypeError('stock_array_dict must be OrderedDict!')
# python中的三目表达式的写法
filter_func = (lambda p_day: p_day.change > 0) if want_up else (lambda p_day: p_day.change < 0)
# 使用filter_func做筛选函数
want_days = list(filter(filter_func, stock_array_dict.values()))
if not want_calc_sum:
return want_days
# 需要计算涨跌幅和
change_sum = 0.0
for day in want_days:
change_sum += day.change
return change_sum
# 全部使用默认参数
show_func('所有上涨的交易日:{}'.format(filter_stock(stock_dict)))
# want_up=False
show_func('所有下跌的交易日:{}'.format(filter_stock(stock_dict, want_up=False)))
# 计算所有上涨的总会
show_func('所有上涨交易日的涨幅和:{}'.format(filter_stock(stock_dict, want_calc_sum=True)))
# 计算所有下跌的总会
show_func('所有下跌交易日的跌幅和:{}'.format(filter_stock(stock_dict, want_up=False, want_calc_sum=True)))
return stock_dict
def sample_224():
"""
2.2.4 偏函数
:return:
"""
stock_dict = sample_223(show=False)
def filter_stock(stock_array_dict, want_up=True, want_calc_sum=False):
if not isinstance(stock_array_dict, OrderedDict):
raise TypeError('stock_array_dict must be OrderedDict!')
# python中的三目表达式的写法
filter_func = (lambda p_day: p_day.change > 0) if want_up else (lambda p_day: p_day.change < 0)
# 使用filter_func做筛选函数
want_days = list(filter(filter_func, stock_array_dict.values()))
if not want_calc_sum:
return want_days
# 需要计算涨跌幅和
change_sum = 0.0
for day in want_days:
change_sum += day.change
return change_sum
filter_stock_up_days = partial(filter_stock, want_up=True, want_calc_sum=False)
filter_stock_down_days = partial(filter_stock, want_up=False, want_calc_sum=False)
filter_stock_up_sums = partial(filter_stock, want_up=True, want_calc_sum=True)
filter_stock_down_sums = partial(filter_stock, want_up=False, want_calc_sum=True)
print('所有上涨的交易日:{}'.format(filter_stock_up_days(stock_dict)))
print('所有下跌的交易日:{}'.format(filter_stock_down_days(stock_dict)))
print('所有上涨交易日的涨幅和:{}'.format(filter_stock_up_sums(stock_dict)))
print('所有下跌交易日的跌幅和:{}'.format(filter_stock_down_sums(stock_dict)))
"""
2.3 面向对象
"""
class StockTradeDays(object):
def __init__(self, price_array, start_date, date_array=None):
# 私有价格序列
self.__price_array = price_array
# 私有日期序列
self.__date_array = self._init_days(start_date, date_array)
# 私有涨跌幅序列
self.__change_array = self.__init_change()
# 进行OrderedDict的组装
self.stock_dict = self._init_stock_dict()
def __init_change(self):
"""
从price_array生成change_array
:return:
"""
price_float_array = [float(price_str) for price_str in
self.__price_array]
# 通过将时间平移形成两个错开的收盘价序列,通过zip打包成为一个新的序列
# 每个元素为相邻的两个收盘价格
pp_array = [(price1, price2) for price1, price2 in
zip(price_float_array[:-1], price_float_array[1:])]
# list for python3
change_array = list(map(lambda pp: reduce(lambda a, b: round((b - a) / a, 3), pp), pp_array))
# list insert插入数据,将第一天的涨跌幅设置为0
change_array.insert(0, 0)
return change_array
def _init_days(self, start_date, date_array):
"""
protect方法,
:param start_date: 初始日期
:param date_array: 给定日期序列
:return:
"""
if date_array is None:
# 由start_date和self.__price_array来确定日期序列
date_array = [str(start_date + ind) for ind, _ in
enumerate(self.__price_array)]
else:
# 稍后的内容会使用外部直接设置的方式
# 如果外面设置了date_array,就直接转换str类型组成新date_array
date_array = [str(date) for date in date_array]
return date_array
def _init_stock_dict(self):
"""
使用namedtupleOrderedDict将结果合并
:return:
"""
stock_namedtuple = namedtuple('stock',
('date', 'price', 'change'))
# 使用以被赋值的__date_array等进行OrderedDict的组装
stock_dict = OrderedDict(
(date, stock_namedtuple(date, price, change))
for date, price, change in
zip(self.__date_array, self.__price_array,
self.__change_array))
return stock_dict
def filter_stock(self, want_up=True, want_calc_sum=False):
"""
筛选结果子集
:param want_up: 是否筛选上涨
:param want_calc_sum: 是否计算涨跌和
:return:
"""
# Python中的三目表达式的写法
filter_func = (lambda p_day: p_day.change > 0) if want_up else (
lambda p_day: p_day.change < 0)
# 使用filter_func做筛选函数
want_days = list(filter(filter_func, self.stock_dict.values()))
if not want_calc_sum:
return want_days
# 需要计算涨跌幅和
change_sum = 0.0
for day in want_days:
change_sum += day.change
return change_sum
"""
下面的__str____iter__ __getitem__ __len__稍后会详细讲解作
"""
def __str__(self):
return str(self.stock_dict)
__repr__ = __str__
def __iter__(self):
"""
通过代理stock_dict的跌倒,yield元素
:return:
"""
for key in self.stock_dict:
yield self.stock_dict[key]
def __getitem__(self, ind):
date_key = self.__date_array[ind]
return self.stock_dict[date_key]
def __len__(self):
return len(self.stock_dict)
def sample_231():
"""
2.3.1 类的封装
:return:
"""
price_array = '30.14,29.58,26.36,32.56,32.82'.split(',')
date_base = 20170118
# 从StockTradeDays类初始化一个实例对象trade_days,内部会调用__init__
trade_days = StockTradeDays(price_array, date_base)
# 打印对象信息
print('trade_days:', trade_days)
print('trade_days对象长度为: {}'.format(len(trade_days)))
from collections import Iterable
# 如果是trade_days是可迭代对象,依次打印出
if isinstance(trade_days, Iterable):
for day in trade_days:
print(day)
print(trade_days.filter_stock())
# 两年的TSLA收盘数据 to list
price_array = ABuSymbolPd.make_kl_df('TSLA', n_folds=2).close.tolist()
# 两年的TSLA收盘日期 to list,这里的写法不考虑效率,只做演示使用
date_array = ABuSymbolPd.make_kl_df('TSLA', n_folds=2).date.tolist()
print('price_array[:5], date_array[:5]:', price_array[:5], date_array[:5])
trade_days = StockTradeDays(price_array, date_base, date_array)
print('trade_days对象长度为: {}'.format(len(trade_days)))
print('最后一天交易数据为:{}'.format(trade_days[-1]))
"""
2.3.2 继承和多态
"""
class TradeStrategyBase(six.with_metaclass(ABCMeta, object)):
"""
交易策略抽象基类
"""
@abstractmethod
def buy_strategy(self, *args, **kwargs):
# 买入策略基类
pass
@abstractmethod
def sell_strategy(self, *args, **kwargs):
# 卖出策略基类
pass
class TradeStrategy1(TradeStrategyBase):
"""
交易策略1: 追涨策略,当股价上涨一个阀值默认为7%
买入股票并持有s_keep_stock_threshold20)天
"""
s_keep_stock_threshold = 20
def __init__(self):
self.keep_stock_day = 0
# 7%上涨幅度作为买入策略阀值
self.__buy_change_threshold = 0.07
def buy_strategy(self, trade_ind, trade_day, trade_days):
if self.keep_stock_day == 0 and \
trade_day.change > self.__buy_change_threshold:
# 当没有持有股票的时候self.keep_stock_day == 0 并且
# 符合买入条件上涨一个阀值,买入
self.keep_stock_day += 1
elif self.keep_stock_day > 0:
# self.keep_stock_day > 0代表持有股票,持有股票天数递增
self.keep_stock_day += 1
def sell_strategy(self, trade_ind, trade_day, trade_days):
if self.keep_stock_day >= \
TradeStrategy1.s_keep_stock_threshold:
# 当持有股票天数超过阀值s_keep_stock_threshold,卖出股票
self.keep_stock_day = 0
"""
property属性稍后会讲到
"""
@property
def buy_change_threshold(self):
return self.__buy_change_threshold
@buy_change_threshold.setter
def buy_change_threshold(self, buy_change_threshold):
if not isinstance(buy_change_threshold, float):
"""
上涨阀值需要为float类型
"""
raise TypeError('buy_change_threshold must be float!')
# 上涨阀值只取小数点后两位
self.__buy_change_threshold = round(buy_change_threshold, 2)
class TradeLoopBack(object):
"""
交易回测系统
"""
def __init__(self, trade_days, trade_strategy):
"""
使用上一节封装的StockTradeDays类和本节编写的交易策略类
TradeStrategyBase类初始化交易系统
:param trade_days: StockTradeDays交易数据序列
:param trade_strategy: TradeStrategyBase交易策略
"""
self.trade_days = trade_days
self.trade_strategy = trade_strategy
# 交易盈亏结果序列
self.profit_array = []
def execute_trade(self):
"""
执行交易回测
:return:
"""
for ind, day in enumerate(self.trade_days):
"""
以时间驱动,完成交易回测
"""
if self.trade_strategy.keep_stock_day > 0:
# 如果有持有股票,加入交易盈亏结果序列
self.profit_array.append(day.change)
# hasattr: 用来查询对象有没有实现某个方法
if hasattr(self.trade_strategy, 'buy_strategy'):
# 买入策略执行
self.trade_strategy.buy_strategy(ind, day,
self.trade_days)
if hasattr(self.trade_strategy, 'sell_strategy'):
# 卖出策略执行
self.trade_strategy.sell_strategy(ind, day,
self.trade_days)
def sample_232():
"""
2.3.2 继承和多态
:return:
"""
# 两年的TSLA收盘数据 to list
price_array = ABuSymbolPd.make_kl_df('TSLA', n_folds=2).close.tolist()
# 两年的TSLA收盘日期 to list,这里的写法不考虑效率,只做演示使用
date_array = ABuSymbolPd.make_kl_df('TSLA', n_folds=2).date.tolist()
trade_days = StockTradeDays(price_array, 0, date_array)
trade_loop_back = TradeLoopBack(trade_days, TradeStrategy1())
trade_loop_back.execute_trade()
print('回测策略1 总盈亏为:{}%'.format(reduce(lambda a, b: a + b, trade_loop_back.profit_array) * 100))
plt.plot(np.array(trade_loop_back.profit_array).cumsum())
plt.show()
"""
2.3.3 静态方法,类方法与property属性
"""
def sample_233_1():
"""
2.3.3_1 property属性
:return:
"""
trade_strategy1 = TradeStrategy1()
# 买入阀值从0.07上升到0.1
trade_strategy1.buy_change_threshold = 0.1
# 两年的TSLA收盘数据 to list
price_array = ABuSymbolPd.make_kl_df('TSLA', n_folds=2).close.tolist()
# 两年的TSLA收盘日期 to list,这里的写法不考虑效率,只做演示使用
date_array = ABuSymbolPd.make_kl_df('TSLA', n_folds=2).date.tolist()
trade_days = StockTradeDays(price_array, 0, date_array)
trade_loop_back = TradeLoopBack(trade_days, trade_strategy1)
trade_loop_back.execute_trade()
print('回测策略1 总盈亏为:{}%'.format(reduce(lambda a, b: a + b, trade_loop_back.profit_array) * 100))
# 可视化profit_array
plt.plot(np.array(trade_loop_back.profit_array).cumsum())
plt.show()
class TradeStrategy2(TradeStrategyBase):
"""
交易策略2: 均值回复策略,当股价连续两个交易日下跌,
且下跌幅度超过阀值默认s_buy_change_threshold(-10%)
买入股票并持有s_keep_stock_threshold10)天
"""
# 买入后持有天数
s_keep_stock_threshold = 10
# 下跌买入阀值
s_buy_change_threshold = -0.10
def __init__(self):
self.keep_stock_day = 0
def buy_strategy(self, trade_ind, trade_day, trade_days):
if self.keep_stock_day == 0 and trade_ind >= 1:
"""
当没有持有股票的时候self.keep_stock_day == 0 并且
trade_ind >= 1, 不是交易开始的第一天,因为需要yesterday数据
"""
# trade_day.change < 0 bool:今天是否股价下跌
today_down = trade_day.change < 0
# 昨天是否股价下跌
yesterday_down = trade_days[trade_ind - 1].change < 0
# 两天总跌幅
down_rate = trade_day.change + trade_days[trade_ind - 1].change
if today_down and yesterday_down and down_rate < \
TradeStrategy2.s_buy_change_threshold:
# 买入条件成立:连跌两天,跌幅超过s_buy_change_threshold
self.keep_stock_day += 1
elif self.keep_stock_day > 0:
# self.keep_stock_day > 0代表持有股票,持有股票天数递增
self.keep_stock_day += 1
def sell_strategy(self, trade_ind, trade_day, trade_days):
if self.keep_stock_day >= \
TradeStrategy2.s_keep_stock_threshold:
# 当持有股票天数超过阀值s_keep_stock_threshold,卖出股票
self.keep_stock_day = 0
"""
稍后会详细讲解classmethodstaticmethod
"""
@classmethod
def set_keep_stock_threshold(cls, keep_stock_threshold):
cls.s_keep_stock_threshold = keep_stock_threshold
@staticmethod
def set_buy_change_threshold(buy_change_threshold):
TradeStrategy2.s_buy_change_threshold = buy_change_threshold
def sample_233_2():
"""
2.3.3_2 静态类方法@classmethod与@staticmethod
:return:
"""
# 两年的TSLA收盘数据 to list
price_array = ABuSymbolPd.make_kl_df('TSLA', n_folds=2).close.tolist()
# 两年的TSLA收盘日期 to list,这里的写法不考虑效率,只做演示使用
date_array = ABuSymbolPd.make_kl_df('TSLA', n_folds=2).date.tolist()
trade_days = StockTradeDays(price_array, 0, date_array)
trade_strategy2 = TradeStrategy2()
trade_loop_back = TradeLoopBack(trade_days, trade_strategy2)
trade_loop_back.execute_trade()
print('回测策略2 总盈亏为:{}%'.format(reduce(lambda a, b: a + b, trade_loop_back.profit_array) * 100))
plt.plot(np.array(trade_loop_back.profit_array).cumsum())
plt.show()
# 实例化一个新的TradeStrategy2类对象
trade_strategy2 = TradeStrategy2()
# 修改为买入后持有股票20天,默认为10天
TradeStrategy2.set_keep_stock_threshold(20)
# 修改股价下跌买入阀值为-0.08(下跌8%),默认为-0.10(下跌10%)
TradeStrategy2.set_buy_change_threshold(-0.08)
# 实例化新的回测对象trade_loop_back
trade_loop_back = TradeLoopBack(trade_days, trade_strategy2)
# 执行回测
trade_loop_back.execute_trade()
print('回测策略2 总盈亏为:{}%'.format(reduce(lambda a, b: a + b, trade_loop_back.profit_array) * 100))
# 可视化回测结果
plt.plot(np.array(trade_loop_back.profit_array).cumsum())
plt.show()
"""
2.4 性能效率
"""
def sample_241_1():
"""
2.4.1_1 itertools的使用
:return:
"""
items = [1, 2, 3]
for item in itertools.permutations(items):
print(item)
for item in itertools.combinations(items, 2):
print(item)
for item in itertools.combinations_with_replacement(items, 2):
print(item)
ab = ['a', 'b']
cd = ['c', 'd']
# 针对abcd两个集合进行排列组合
for item in itertools.product(ab, cd):
print(item)
# 两年的TSLA收盘数据 to list
g_price_array = ABuSymbolPd.make_kl_df('TSLA', n_folds=2).close.tolist()
# 两年的TSLA收盘日期 to list,这里的写法不考虑效率,只做演示使用
g_date_array = ABuSymbolPd.make_kl_df('TSLA', n_folds=2).date.tolist()
g_trade_days = StockTradeDays(g_price_array, 0, g_date_array)
def calc(keep_stock_threshold, buy_change_threshold):
"""
:param keep_stock_threshold: 持股天数
:param buy_change_threshold: 下跌买入阀值
:return: 盈亏情况,输入的持股天数, 输入的下跌买入阀值
"""
# 实例化TradeStrategy2
trade_strategy2 = TradeStrategy2()
# 通过类方法设置买入后持股天数
TradeStrategy2.set_keep_stock_threshold(keep_stock_threshold)
# 通过类方法设置下跌买入阀值
TradeStrategy2.set_buy_change_threshold(buy_change_threshold)
# 进行回测
trade_loop_back = TradeLoopBack(g_trade_days, trade_strategy2)
trade_loop_back.execute_trade()
# 计算回测结果的最终盈亏值profit
profit = 0.0 if len(trade_loop_back.profit_array) == 0 else \
reduce(lambda a, b: a + b, trade_loop_back.profit_array)
# 返回值profit和函数的两个输入参数
return profit, keep_stock_threshold, buy_change_threshold
def sample_241_2():
"""
2.4.1_2 笛卡尔积最优参数
:return:
"""
# range集合:买入后持股天数从2天-30天,间隔两天
keep_stock_list = list(range(2, 30, 2))
print('持股天数参数组:{}'.format(keep_stock_list))
# 下跌买入阀值从-0.05到-0.15,即从下跌5%到15%
buy_change_list = [buy_change / 100.0 for buy_change in xrange(-5, -16, -1)]
print('下跌阀值参数组:{}'.format(buy_change_list))
result = []
for keep_stock_threshold, buy_change_threshold in itertools.product(
keep_stock_list, buy_change_list):
# 使用calc计算参数对应的最终盈利,结果加入result序列
result.append(calc(keep_stock_threshold, buy_change_threshold))
print('笛卡尔积参数集合总共结果为:{}'.format(len(result)))
# [::-1]将整个排序结果反转,反转后盈亏收益从最高向低排序
# [:10]取出收益最高的前10个组合查看
print(sorted(result)[::-1][:10])
def sample_242():
"""
2.4.2 多进程 vs 多线程
:return:
"""
# range集合:买入后持股天数从2天-30天,间隔两天
keep_stock_list = list(range(2, 30, 2))
print('持股天数参数组:{}'.format(keep_stock_list))
# 下跌买入阀值从-0.05到-0.15,即从下跌5%到15%
buy_change_list = [buy_change / 100.0 for buy_change in xrange(-1, -100, -1)]
print('下跌阀值参数组:{}'.format(buy_change_list))
result = []
# 回调函数,通过add_done_callback任务完成后调用
def when_done(r):
# when_done在主进程中运行
result.append(r.result())
"""
with class_a() as a: 上下文管理器:稍后会具体讲解
"""
with ProcessPoolExecutor() as pool:
for keep_stock_threshold, buy_change_threshold in \
itertools.product(keep_stock_list, buy_change_list):
"""
submit提交任务:使用calc函数和的参数通过submit提交到独立进程
提交的任务必须是简单函数,进程并行不支持类方法、闭包等
函数参数和返回值必须兼容pickle序列化,进程间的通信需要
"""
future_result = pool.submit(calc, keep_stock_threshold,
buy_change_threshold)
# 当进程完成任务即calc运行结束后的回调函数
future_result.add_done_callback(when_done)
print('Process sorted(result)[::-1][:10]:\n', sorted(result)[::-1][:10])
result = []
def when_done(r):
result.append(r.result())
with ThreadPoolExecutor(max_workers=8) as pool:
for keep_stock_threshold, buy_change_threshold in \
itertools.product(keep_stock_list, buy_change_list):
future_result = pool.submit(calc, keep_stock_threshold,
buy_change_threshold)
future_result.add_done_callback(when_done)
print('Thread sorted(result)[::-1][:10]:\n', sorted(result)[::-1][:10])
def sample_243():
"""
2.4.3 使用编译库提高性能
:return:
"""
# 买入后持股天数放大寻找范围 1 - 503 天, 间隔1天
keep_stock_list = list(range(1, 504, 1))
# 下跌买入阀值寻找范围 -0.01 - -0.99 共99个
buy_change_list = [buy_change / 100.0 for buy_change in xrange(-1, -100, -1)]
def do_single_task():
task_list = list(itertools.product(keep_stock_list, buy_change_list))
print('笛卡尔积参数集合总共结果为:{}'.format(len(task_list)))
for keep_stock_threshold, buy_change_threshold in task_list:
calc(keep_stock_threshold, buy_change_threshold)
import time
start_time = time.time()
do_single_task()
end_time = time.time()
print('{} cost {}s'.format(do_single_task.__name__, round(end_time - start_time, 3)))
import numba as nb
do_single_task_nb = nb.jit(do_single_task)
start_time = time.time()
do_single_task_nb()
end_time = time.time()
print('{} cost {}s'.format(do_single_task_nb.__name__, round(end_time - start_time, 3)))
def sample_25():
"""
2.5 代码调试
书中本示例针对python3不适用,因为python3默认的除法就是小数
:return:
"""
# noinspection PyAugmentAssignment,PyUnusedLocal
def gen_buy_change_list():
buy_change_list = []
# 下跌买入阀值从-0.05到-0.15,即从下跌5%到15%
for buy_change in xrange(-5, -16, -1):
buy_change = buy_change / 100
buy_change_list.append(buy_change)
return buy_change_list
# noinspection PyAugmentAssignment,PyRedeclaration
def gen_buy_change_list():
buy_change_list = []
for buy_change in xrange(-5, -16, -1):
# 1. 原始buy_change
print(buy_change)
buy_change = buy_change / 100
# 2. buy_change/100
print(buy_change)
buy_change_list.append(buy_change)
return buy_change_list
print(gen_buy_change_list())
# 2. 导入future库的division`from __future__ import division`
# from __future__ import division
# noinspection PyAugmentAssignment
def gen_buy_change_list():
buy_change_list = []
for buy_change in xrange(-5, -16, -1):
# 1. 除数或者被除数其中一个是float类型
buy_change = buy_change / 100.0
buy_change_list.append(buy_change)
return buy_change_list
print(gen_buy_change_list())
logging.basicConfig(level=logging.INFO)
# noinspection PyAugmentAssignment
def gen_buy_change_list():
# 会打印出来,因为info >= level=logging.INFO
logging.info("gen_buy_change_list begin")
buy_change_list = []
for buy_change in xrange(-5, -16, -1):
# 不会打印出来,debug < level=logging.INFO
logging.debug(buy_change)
buy_change = buy_change / 100
# 不会打印出来,debug < level=logging.INFO
logging.debug(buy_change)
buy_change_list.append(buy_change)
# 会打印出来,因为info >= level=logging.INFO
logging.info("gen_buy_change_list end")
return buy_change_list
_ = gen_buy_change_list()
import pdb
# noinspection PyAugmentAssignment
def gen_buy_change_list():
buy_change_list = []
for buy_change in xrange(-5, -16, -1):
# 只针对循环执行到buy_change == -10,中断开始调试
if buy_change == -10:
# 打断点,通过set_trace
pdb.set_trace()
buy_change = buy_change / 100
buy_change_list.append(buy_change)
# 故意向外抛出异常
raise RuntimeError('debug for pdb')
try:
_ = gen_buy_change_list()
except Exception:
# 从捕获异常的地方开始调试,经常使用的调试技巧
pdb.set_trace()
if __name__ == "__main__":
sample_211()
# sample_212()
# sample_221()
# sample_222()
# sample_223()
# sample_224()
# sample_231()
# sample_232()
# sample_233_1()
# sample_233_2()
# sample_241_1()
# sample_241_2()
# sample_242()
# sample_243()
# sample_25()
+439
View File
@@ -0,0 +1,439 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
import warnings
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
"""
第三章 量化工具——NumPy
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
"""
def sample_311():
"""
3.1.1 并行化思想
:return:
"""
# 注意 * 3的操作被运行在每一个元素上
np_list = np.ones(5) * 3
print('np_list:', np_list)
# 普通的列表把*3操作认为是整体性操作
normal_list = [1, 1, 1, 1, 1] * 3
print('normal_list:', normal_list, len(normal_list))
# 200支股票
stock_cnt = 200
# 504个交易日
view_days = 504
# 生成服从正态分布:均值期望=0,标准差=1的序列
stock_day_change = np.random.standard_normal((stock_cnt, view_days))
try:
# 使用沙盒数据,目的是和书中一样的数据环境,不需要注视掉
stock_day_change = np.load('../gen/stock_day_change.npy')
except Exception as e:
print('../gen/stock_day_change.npy load error:{}'.format(e))
def sample_312():
"""
3.1.2 初始化操作
:return:
"""
np_list = np.arange(10000)
# 100个0
print('np.zeros(100):\n', np.zeros(100))
# shape3行2列 全是0
print('np.zeros((3, 2):\n', np.zeros((3, 2)))
# shape 3行2列 全是1
print('np.ones((3, 2):\n', np.ones((3, 2)))
# shapex=2, y=3, z=3 值随机
print('np.empty((2, 3, 3):\n', np.empty((2, 3, 3)))
# 初始化序列与np_list一样的shape,值全为1
print('np.ones_like(np_list):\n', np.ones_like(np_list))
# 初始化序列与np_list一样的shape,值全为0
print('np.zeros_like(np_list):\n', np.zeros_like(np_list))
# eye得到对角线全为1的单位矩阵
print('np.eye(3):\n', np.eye(3))
# 打印shape (200, 504) 200行504列
print('stock_day_change.shape:', stock_day_change.shape)
# 打印出第一支只股票,头五个交易日的涨跌幅情况
print('stock_day_change[0:1, :5]:\n', stock_day_change[0:1, :5])
"""
3.1.3 索引选取和切片选
"""
# tmp = a
tmp = stock_day_change[0:2, 0:5].copy()
# a = b
stock_day_change[0:2, 0:5] = stock_day_change[-2:, -5:]
# b = tmp
stock_day_change[-2:, -5:] = tmp
def sample_313():
"""
3.1.3 索引选取和切片选
:return:
"""
# 0:2第一,第二支股票,0:5头五个交易日的涨跌幅数据
print('stock_day_change[0:2, 0:5]:\n', stock_day_change[0:2, 0:5])
# -2:倒数一,第二支股票,-5:最后五个交易日的涨跌幅数据
print('stock_day_change[-2:, -5:]:\n', stock_day_change[-2:, -5:])
# view result
print('[0:2, 0:5], [-2:, -5:]:\n', stock_day_change[0:2, 0:5], stock_day_change[-2:, -5:])
def sample_314():
"""
3.1.4 数据转换与规整
:return:
"""
print('stock_day_change[0:2, 0:5]:\n', stock_day_change[0:2, 0:5])
print('[0:2, 0:5].astype(int):\n', stock_day_change[0:2, 0:5].astype(int))
# 2代表保留两位小数
print('around 2:\n', np.around(stock_day_change[0:2, 0:5], 2))
# 使用copy目的是不修改原始序列
tmp_test = stock_day_change[0:2, 0:5].copy()
# 将第一个元素改成nan
tmp_test[0][0] = np.nan
print('tmp_test:\n', tmp_test)
def sample_315():
"""
3.1.5 逻辑条件进行数据筛选
:return:
"""
# 找出上述切片内涨幅超过0.5的股票时段, 通过输出结果你可以看到返回的是boolean的数组
mask = stock_day_change[0:2, 0:5] > 0.5
print('mask:\n', mask)
tmp_test = stock_day_change[0:2, 0:5].copy()
# 使用上述的mask数组筛选出符合条件的数组, 即中筛选mask中对应index值为True的
print('tmp_test[mask]:\n', tmp_test[mask])
tmp_test[tmp_test > 0.5] = 1
print('tmp_test:\n', tmp_test)
tmp_test = stock_day_change[-2:, -5:]
print('tmp_test2:\n', tmp_test)
print('tmp_test[(tmp_test > 1) | (tmp_test < -1)]:\n', tmp_test[(tmp_test > 1) | (tmp_test < -1)])
# noinspection PyTypeChecker
def sample_316():
"""
3.1.6 通用序列函数
:return:
"""
# np.all判断序列中的所有元素是否全部是true, 即对bool序列进行与操作
# 本例实际判断stock_day_change[0:2, 0:5]中是否全是上涨的
print('np.all(stock_day_change[0:2, 0:5] > 0):\n', np.all(stock_day_change[0:2, 0:5] > 0))
# np.any判断序列中是否有元素为true, 即对bool序列进行或操作
# 本例实际判断stock_day_change[0:2, 0:5]中是至少有一个是上涨的
print('np.any(stock_day_change[0:2, 0:5] > 0):\n', np.any(stock_day_change[0:2, 0:5] > 0))
# 对两个序列对应的元素两两比较,maximum结果集取大,相对使用minimum为取小的结果集
print('np.maximum(stock_day_change[0:2, 0:5], stock_day_change[-2:, -5:]):\n',
np.maximum(stock_day_change[0:2, 0:5], stock_day_change[-2:, -5:]))
change_int = stock_day_change[0:2, 0:5].astype(int)
print('change_int:\n', change_int)
# 序列中数值值唯一且不重复的值组成新的序列
print('np.unique(change_int):\n', np.unique(change_int))
# axis1
print('np.diff(stock_day_change[0:2, 0:5]):\n', np.diff(stock_day_change[0:2, 0:5]))
# 唯一区别 axis=0
print('np.diff(stock_day_change[0:2, 0:5], axis=0):\n', np.diff(stock_day_change[0:2, 0:5], axis=0))
tmp_test = stock_day_change[-2:, -5:]
print('np.where(tmp_test > 0.5, 1, 0):\n', np.where(tmp_test > 0.5, 1, 0))
print('np.where(tmp_test > 0.5, 1, tmp_test):\n', np.where(tmp_test > 0.5, 1, tmp_test))
# 序列中的值大于0.5并且小于1的赋值为1,否则赋值为0
print('np.where(np.logical_and(tmp_test > 0.5, tmp_test < 1), 1, 0):\n',
np.where(np.logical_and(tmp_test > 0.5, tmp_test < 1), 1, 0))
# 序列中的值大于0.5或者小于-0.5的赋值为1,否则赋值为0
print('np.where(np.logical_or(tmp_test > 0.5, tmp_test < -0.5), 1, 0):\n',
np.where(np.logical_or(tmp_test > 0.5, tmp_test < -0.5), 1, 0))
"""
3.1.7 数据本地序列化操作
"""
stock_day_change = np.load('../gen/stock_day_change.npy')
np.save('../gen/stock_day_change', stock_day_change)
"""
3.2 统计概念与函数使用
"""
stock_day_change_four = stock_day_change[:4, :4]
def sample_320():
"""
3.2.0 统计概念与函数使用
:return:
"""
print('stock_day_change_four:\n', stock_day_change_four)
def sample_321():
"""
3.2.1 统计基础函数使用
:return:
"""
print('最大涨幅 {}'.format(np.max(stock_day_change_four, axis=1)))
print('最大跌幅 {}'.format(np.min(stock_day_change_four, axis=1)))
print('振幅幅度 {}'.format(np.std(stock_day_change_four, axis=1)))
print('平均涨跌 {}'.format(np.mean(stock_day_change_four, axis=1)))
print('最大涨幅 {}'.format(np.max(stock_day_change_four, axis=0)))
print('最大涨幅股票{}'.format(np.argmax(stock_day_change_four, axis=0)))
print('最大跌幅股票{}'.format(np.argmin(stock_day_change_four, axis=0)))
print('最大跌幅 {}'.format(np.min(stock_day_change_four, axis=0)))
print('振幅幅度 {}'.format(np.std(stock_day_change_four, axis=0)))
print('平均涨跌 {}'.format(np.mean(stock_day_change_four, axis=0)))
def sample_322():
"""
3.2.2 统计基础概念
:return:
"""
a_investor = np.random.normal(loc=100, scale=50, size=(100, 1))
b_investor = np.random.normal(loc=100, scale=20, size=(100, 1))
# a交易者
print('a交易者期望{0:.2f}元, 标准差{1:.2f}, 方差{2:.2f}'.format(
a_investor.mean(), a_investor.std(), a_investor.var()))
# b交易者
print('b交易者期望{0:.2f}元, 标准差{1:.2f}, 方差{2:.2f}'.format(
b_investor.mean(), b_investor.std(), b_investor.var()))
# a交易者期望
a_mean = a_investor.mean()
# a交易者标注差
a_std = a_investor.std()
# 收益绘制曲线
plt.plot(a_investor)
# 水平直线 上线
plt.axhline(a_mean + a_std, color='r')
# 水平直线 均值期望线
plt.axhline(a_mean, color='y')
# 水平直线 下线
plt.axhline(a_mean - a_std, color='g')
plt.show()
b_mean = b_investor.mean()
b_std = b_investor.std()
# b交易者收益绘制曲线
plt.plot(b_investor)
# 水平直线 上线
plt.axhline(b_mean + b_std, color='r')
# 水平直线 均值期望线
plt.axhline(b_mean, color='y')
# 水平直线 下线
plt.axhline(b_mean - b_std, color='g')
plt.show()
def sample_331():
"""
3.3.1 正态分布基础概念
:return:
"""
import scipy.stats as scs
# 均值期望
stock_mean = stock_day_change[0].mean()
# 标准差
stock_std = stock_day_change[0].std()
print('股票0 mean均值期望:{:.3f}'.format(stock_mean))
print('股票0 std振幅标准差:{:.3f}'.format(stock_std))
# 绘制股票0的直方图
plt.hist(stock_day_change[0], bins=50, normed=True)
# linspace从股票0 最小值-> 最大值生成数据
fit_linspace = np.linspace(stock_day_change[0].min(),
stock_day_change[0].max())
# 概率密度函数(PDFprobability density function)
# 由均值,方差,来描述曲线,使用scipy.stats.norm.pdf生成拟合曲线
pdf = scs.norm(stock_mean, stock_std).pdf(fit_linspace)
print(pdf)
# plot x, y
plt.plot(fit_linspace, pdf, lw=2, c='r')
plt.show()
def sample_332():
"""
3.3.2 实例1:正态分布买入策略
:return:
"""
# 保留后50天的随机数据作为策略验证数据
keep_days = 50
# 统计前454, 切片切出0-454dayview_days = 504
stock_day_change_test = stock_day_change[:stock_cnt, 0:view_days - keep_days]
# 打印出前454跌幅最大的三支,总跌幅通过np.sum计算,np.sort对结果排序
print('np.sort(np.sum(stock_day_change_test, axis=1))[:3]:', np.sort(np.sum(stock_day_change_test, axis=1))[:3])
# 使用np.argsort针对股票跌幅进行排序,返回序号,即符合买入条件的股票序号
stock_lower_array = np.argsort(np.sum(stock_day_change_test, axis=1))[:3]
# 输符合买入条件的股票序号
print('stock_lower_array:', stock_lower_array)
def show_buy_lower(p_stock_ind):
"""
:param p_stock_ind: 股票序号,即在stock_day_change中的位置
:return:
"""
# 设置一个一行两列的可视化图表
_, axs = plt.subplots(nrows=1, ncols=2, figsize=(16, 5))
# view_days504 - keep_days50 = 454
# 绘制前454天股票走势图,np.cumsum():序列连续求和
axs[0].plot(np.arange(0, view_days - keep_days),
stock_day_change_test[p_stock_ind].cumsum())
# [view_days504 - keep_days50 = 454 : view_days504]
# 从第454天开始到504天的股票走势
cs_buy = stock_day_change[p_stock_ind][
view_days - keep_days:view_days].cumsum()
# 绘制从第454天到504天股票走势图
axs[1].plot(np.arange(view_days - keep_days, view_days), cs_buy)
# 返回从第454天开始到第504天计算盈亏的盈亏序列的最后一个值
return cs_buy[-1]
# 最后输出的盈亏比例
profit = 0
# 跌幅最大的三支遍历序号
for stock_ind in stock_lower_array:
# profit即三支股票从第454天买入开始计算,直到最后一天的盈亏比例
profit += show_buy_lower(stock_ind)
plt.show()
# str.format 支持{:.2f}形式保留两位小数
print('买入第 {} 支股票,从第454个交易日开始持有盈亏:{:.2f}%'.format(
stock_lower_array, profit))
def sample_342():
"""
3.4.2 实例2:如何在交易中获取优势
:return:
"""
# 设置100个赌徒
gamblers = 100
def casino(win_rate, win_once=1, loss_once=1, commission=0.01):
"""
赌场:简单设定每个赌徒一共有1000000一共想在赌场玩10000000次,
但是你要是没钱了也别想玩了
win_rate: 输赢的概率
win_once: 每次赢的钱数
loss_once: 每次输的钱数
commission: 手续费这里简单的设置了0.01 1%
"""
my_money = 1000000
play_cnt = 10000000
commission = commission
for _ in np.arange(0, play_cnt):
# 使用伯努利分布根据win_rate来获取输赢
w = np.random.binomial(1, win_rate)
if w:
# 赢了 +win_once
my_money += win_once
else:
# 输了 -loss_once
my_money -= loss_once
# 手续费
my_money -= commission
if my_money <= 0:
# 没钱就别玩了,不赊账
break
return my_money
"""
如果有numba使用numba进行加速, 这个加速效果非常明显,不使用numba非常非常非常慢
"""
import numba as nb
casino = nb.jit(casino)
print('heaven_moneys....')
# 100个赌徒进场天堂赌场,胜率0.5,赔率1,还没手续费
heaven_moneys = [casino(0.5, commission=0) for _ in
np.arange(0, gamblers)]
print('cheat_moneys....')
# 100个赌徒进场开始,胜率0.4,赔率1,没手续费
cheat_moneys = [casino(0.4, commission=0) for _ in
np.arange(0, gamblers)]
print('commission_moneys....')
# 100个赌徒进场开始,胜率0.5,赔率1,手续费0.01
commission_moneys = [casino(0.5, commission=0.01) for _ in
np.arange(0, gamblers)]
print('casino(0.5, commission=0.01, win_once=1.02, loss_once=0.98.....')
# 100个赌徒进场开始,胜率0.5,赔率1.04,手续费0.01
f1_moneys = [casino(0.5, commission=0.01, win_once=1.02, loss_once=0.98)
for _ in np.arange(0, gamblers)]
print('casino(0.45, commission=0.01, win_once=1.02, loss_once=0.98.....')
# 100个赌徒进场开始,胜率0.45,赔率1.04,手续费0.01
f2_moneys = [casino(0.45, commission=0.01, win_once=1.02, loss_once=0.98)
for _ in np.arange(0, gamblers)]
_ = plt.hist(heaven_moneys, bins=30)
plt.show()
_ = plt.hist(cheat_moneys, bins=30)
plt.show()
_ = plt.hist(commission_moneys, bins=30)
plt.show()
_ = plt.hist(f1_moneys, bins=30)
plt.show()
_ = plt.hist(f2_moneys, bins=30)
plt.show()
if __name__ == "__main__":
sample_311()
# sample_312()
# sample_313()
# sample_314()
# sample_315()
# sample_316()
# sample_320()
# sample_321()
# sample_322()
# sample_331()
# sample_332()
# sample_342()
+469
View File
@@ -0,0 +1,469 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
from __future__ import division
import warnings
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
# noinspection PyUnresolvedReferences
import abu_local_env
import abupy
from abupy import ABuSymbolPd
from abupy import xrange, pd_resample
warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
# 使用沙盒数据,目的是和书中一样的数据环境
abupy.env.enable_example_env_ipython()
stock_day_change = np.load('../gen/stock_day_change.npy')
"""
第四章 量化工具——pandas
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
"""
def sample_411():
"""
4.1.1 DataFrame构建及方法
:return:
"""
print('stock_day_change.shape:', stock_day_change.shape)
# 下面三种写法输出完全相同,输出如表4-1所示
print('head():\n', pd.DataFrame(stock_day_change).head())
print('head(5):\n', pd.DataFrame(stock_day_change).head(5))
print('[:5]:\n', pd.DataFrame(stock_day_change)[:5])
def sample_412():
"""
4.1.2 索引行列序列
:return:
"""
# 股票0 -> 股票stock_day_change.shape[0]
stock_symbols = ['股票 ' + str(x) for x in
xrange(stock_day_change.shape[0])]
# 通过构造直接设置index参数,head(2)就显示两行,表4-2所示
print('pd.DataFrame(stock_day_change, index=stock_symbols).head(2):\n',
pd.DataFrame(stock_day_change, index=stock_symbols).head(2))
# 从2017-1-1向上时间递进,单位freq='1d'即1天
days = pd.date_range('2017-1-1',
periods=stock_day_change.shape[1], freq='1d')
# 股票0 -> 股票stock_day_change.shape[0]
stock_symbols = ['股票 ' + str(x) for x in
xrange(stock_day_change.shape[0])]
# 分别设置index和columns
df = pd.DataFrame(stock_day_change, index=stock_symbols, columns=days)
# 表4-3所示
print('df.head(2):\n', df.head(2))
def sample_413():
"""
4.1.3 金融时间序列
:return:
"""
days = pd.date_range('2017-1-1',
periods=stock_day_change.shape[1], freq='1d')
stock_symbols = ['股票 ' + str(x) for x in
xrange(stock_day_change.shape[0])]
df = pd.DataFrame(stock_day_change, index=stock_symbols, columns=days)
# df做个转置
df = df.T
# 表4-4所示
print('df.head():\n', df.head())
df_20 = pd_resample(df, '21D', how='mean')
# 表4-5所示
print('df_20.head():\n', df_20.head())
def sample_414():
"""
4.1.4 Series构建及方法
:return
"""
days = pd.date_range('2017-1-1',
periods=stock_day_change.shape[1], freq='1d')
stock_symbols = ['股票 ' + str(x) for x in
xrange(stock_day_change.shape[0])]
df = pd.DataFrame(stock_day_change, index=stock_symbols, columns=days)
df = df.T
print('df.head():\n', df.head())
df_stock0 = df['股票 0']
# 打印df_stock0类型
print('type(df_stock0):', type(df_stock0))
# 打印出Series的前5行数据, 与DataFrame一致
print('df_stock0.head():\n', df_stock0.head())
df_stock0.cumsum().plot()
plt.show()
def sample_415():
"""
4.1.5 重采样数据
:return
"""
days = pd.date_range('2017-1-1',
periods=stock_day_change.shape[1], freq='1d')
stock_symbols = ['股票 ' + str(x) for x in
xrange(stock_day_change.shape[0])]
df = pd.DataFrame(stock_day_change, index=stock_symbols, columns=days)
df = df.T
df_stock0 = df['股票 0']
# 以5天为周期重采样(周k
df_stock0_5 = pd_resample(df_stock0.cumsum(), '5D', how='ohlc')
# 以21天为周期重采样(月k),
# noinspection PyUnusedLocal
df_stock0_20 = pd_resample(df_stock0.cumsum(), '21D', how='ohlc')
# 打印5天重采样,如下输出2017-01-01, 2017-01-06, 2017-01-11, 表4-6所示
print('df_stock0_5.head():\n', df_stock0_5.head())
from abupy import ABuMarketDrawing
# 图4-2所示
ABuMarketDrawing.plot_candle_stick(df_stock0_5.index,
df_stock0_5['open'].values,
df_stock0_5['high'].values,
df_stock0_5['low'].values,
df_stock0_5['close'].values,
np.random.random(len(df_stock0_5)),
None, 'stock', day_sum=False,
html_bk=False, save=False)
print('type(df_stock0_5.open.values):', type(df_stock0_5['open'].values))
print('df_stock0_5.open.index:\n', df_stock0_5['open'].index)
print('df_stock0_5.columns:\n', df_stock0_5.columns)
"""
4.2 基本数据分析示例
"""
# n_folds=2两年
tsla_df = ABuSymbolPd.make_kl_df('usTSLA', n_folds=2)
def sample_420():
# 表4-7所示
print('tsla_df.tail():\n', tsla_df.tail())
def sample_421():
"""
4.2.1 数据整体分析
:return:
"""
print('tsla_df.info():\n', tsla_df.info())
print('tsla_df.describe():\n', tsla_df.describe())
tsla_df[['close', 'volume']].plot(subplots=True, style=['r', 'g'], grid=True)
plt.show()
def sample_422():
"""
4.2.2 索引选取和切片选择
:return:
"""
# 2014-07-23至2014-07-31 开盘价格序列
print('tsla_df.loc[x:x, x]\n', tsla_df.loc['2014-07-23':'2014-07-31', 'open'])
# 2014-07-23至2014-07-31 所有序列,表4-9所示
print('tsla_df.loc[x:x]\n', tsla_df.loc['2014-07-23':'2014-07-31'])
# [1:5](1234)[2:6]: (2, 3, 4, 5)
# 表4-10所示
print('tsla_df.iloc[1:5, 2:6]:\n', tsla_df.iloc[1:5, 2:6])
# 切取所有行[2:6]: (2, 3, 4, 5)列
print('tsla_df.iloc[:, 2:6]:\n', tsla_df.iloc[:, 2:6])
# 选取所有的列[35:37]:(35, 36)行,表4-11所示
print('tsla_df.iloc[35:37]:\n', tsla_df.iloc[35:37])
# 指定一个列
print('tsla_df.close[0:3]:\n', tsla_df.close[0:3])
# 通过组成一个列表选择多个列,表4-12所示
print('tsla_df[][0:3]:\n', tsla_df[['close', 'high', 'low']][0:3])
def sample_423():
"""
4.2.3 逻辑条件进行数据筛选
:return:
"""
# abs为取绝对值的意思,不是防抱死,表4-13所示
print('tsla_df[np.abs(tsla_df.p_change) > 8]:\n', tsla_df[np.abs(tsla_df.p_change) > 8])
print('tsla_df[(np.abs(tsla_df.p_change) > 8) & (tsla_df.volume > 2.5 * tsla_df.volume.mean())]:\n',
tsla_df[(np.abs(tsla_df.p_change) > 8) & (tsla_df.volume > 2.5 * tsla_df.volume.mean())])
def sample_424_1():
"""
4.2.4_1 数据转换与规整
:return:
"""
# 数据序列值排序
print('tsla_df.sort_index(by=p_change)[:5]:\n', tsla_df.sort_index(by='p_change')[:5])
print('tsla_df.sort_index(by=p_change, ascending=False)[:5]:\n',
tsla_df.sort_index(by='p_change', ascending=False)[:5])
# 如果一行的数据中存在na就删除这行
tsla_df.dropna()
# 通过how控制 如果一行的数据中全部都是na就删除这行
tsla_df.dropna(how='all')
# 使用指定值填充na, inplace代表就地操作,即不返回新的序列在原始序列上修改
tsla_df.fillna(tsla_df.mean(), inplace=True)
def sample_424_2():
"""
4.2.4_1 数据转换处理 pct_change
:return:
"""
print('tsla_df.close[:3]:\n', tsla_df.close[:3])
print('tsla_df.close.pct_change()[:3]:\n', tsla_df.close.pct_change()[:3])
print('(223.54 - 222.49) / 222.49, (223.57 - 223.54) / 223.54:', (223.54 - 222.49) / 222.49,
(223.57 - 223.54) / 223.54)
# pct_change对序列从第二项开始向前做减法在除以前一项,这样的针对close做pct_change后的结果就是涨跌幅
change_ratio = tsla_df.close.pct_change()
print('change_ratio.tail():\n', change_ratio.tail())
# 将change_ratio转变成与tsla_df.p_change字段一样的百分百,同样保留两位小数
print('np.round(change_ratio[-5:] * 100, 2):\n', np.round(change_ratio[-5:] * 100, 2))
fmt = lambda x: '%.2f' % x
print('tsla_df.atr21.map(fmt).tail():\n', tsla_df.atr21.map(fmt).tail())
def sample_425():
"""
4.2.5 数据本地序列化操作
:return:
"""
tsla_df.to_csv('../gen/tsla_df.csv', columns=tsla_df.columns, index=True)
tsla_df_load = pd.read_csv('../gen/tsla_df.csv', parse_dates=True, index_col=0)
print('tsla_df_load.head():\n', tsla_df_load.head())
"""
4.3 实例1:寻找股票异动涨跌幅阀值
"""
def sample_431():
"""
4.3.1 数据的离散化
:return:
"""
tsla_df.p_change.hist(bins=80)
plt.show()
cats = pd.qcut(np.abs(tsla_df.p_change), 10)
print('cats.value_counts():\n', cats.value_counts())
# 将涨跌幅数据手工分类,从负无穷到-7,-5,-3,0, 3, 5, 7,正无穷
bins = [-np.inf, -7.0, -5, -3, 0, 3, 5, 7, np.inf]
cats = pd.cut(tsla_df.p_change, bins)
print('bins cats.value_counts():\n', cats.value_counts())
# cr_dummies为列名称前缀
change_ration_dummies = pd.get_dummies(cats, prefix='cr_dummies')
print('change_ration_dummies.head():\n', change_ration_dummies.head())
def sample_432():
"""
4.3.2 concat, append, merge的使用
:return:
"""
# 将涨跌幅数据手工分类,从负无穷到-7,-5,-3,0, 3, 5, 7,正无穷
bins = [-np.inf, -7.0, -5, -3, 0, 3, 5, 7, np.inf]
cats = pd.cut(tsla_df.p_change, bins)
change_ration_dummies = pd.get_dummies(cats, prefix='cr_dummies')
# noinspection PyUnresolvedReferences
print('pd.concat([tsla_df, change_ration_dummies], axis=1).tail():\n ',
pd.concat([tsla_df, change_ration_dummies], axis=1).tail())
# pd.concat的连接axis0:纵向连接atr>14的df和p_change > 10的df
pd.concat([tsla_df[tsla_df.p_change > 10],
tsla_df[tsla_df.atr14 > 16]], axis=0)
# 直接使用DataFrame对象append,结果与上面pd.concat的结果一致, 表4-20所示
print('tsla_df[tsla_df.p_change > 10].append(tsla_df[tsla_df.atr14 > 16]):\n',
tsla_df[tsla_df.p_change > 10].append(tsla_df[tsla_df.atr14 > 16]))
"""
4.4 实例2 :星期几是这个股票的‘好日子’
"""
def sample_441():
"""
4.4.1 构建交叉表
:return:
"""
# noinspection PyTypeChecker
tsla_df['positive'] = np.where(tsla_df.p_change > 0, 1, 0)
print('tsla_df.tail():\n', tsla_df.tail())
xt = pd.crosstab(tsla_df.date_week, tsla_df.positive)
print('xt:\n', xt)
xt_pct = xt.div(xt.sum(1).astype(float), axis=0)
print('xt_pct:\n', xt_pct)
xt_pct.plot(
figsize=(8, 5),
kind='bar',
stacked=True,
title='date_week -> positive')
plt.xlabel('date_week')
plt.ylabel('positive')
plt.show()
def sample_442():
"""
4.4.2 构建透视表
:return:
"""
# noinspection PyTypeChecker
tsla_df['positive'] = np.where(tsla_df.p_change > 0, 1, 0)
print('tsla_df.pivot_table([positive], index=[date_week]):\n',
tsla_df.pivot_table(['positive'], index=['date_week']))
print('tsla_df.groupby([date_week, positive])[positive].count():\n',
tsla_df.groupby(['date_week', 'positive'])['positive'].count())
"""
4.5 实例3 :跳空缺口
"""
jump_pd = pd.DataFrame()
jump_threshold = tsla_df.close.median() * 0.03
def judge_jump(p_today):
global jump_pd
if p_today.p_change > 0 and (p_today.low - p_today.pre_close) > jump_threshold:
"""
符合向上跳空
"""
# jump记录方向 1向上
p_today['jump'] = 1
# 向上跳能量=(今天最低 - 昨收)/ 跳空阀值
p_today['jump_power'] = (p_today.low - p_today.pre_close) / jump_threshold
jump_pd = jump_pd.append(p_today)
elif p_today.p_change < 0 and (p_today.pre_close - p_today.high) > jump_threshold:
"""
符合向下跳空
"""
# jump记录方向 1向下
p_today['jump'] = -1
# 向下跳能量=(昨收 - 今天最高)/ 跳空阀值
p_today['jump_power'] = (p_today.pre_close - p_today.high) / jump_threshold
jump_pd = jump_pd.append(p_today)
def sample_45_1():
"""
4.5 实例3 :跳空缺口
:return:
"""
for kl_index in np.arange(0, tsla_df.shape[0]):
# 通过ix一个一个拿
today = tsla_df.ix[kl_index]
judge_jump(today)
# filter按照顺序只显示这些列, 表4-26所示
print('jump_pd.filter([jump, jump_power, close, date, p_change, pre_close]):\n',
jump_pd.filter(['jump', 'jump_power', 'close', 'date', 'p_change', 'pre_close']))
def sample_45_2():
"""
4.5 实例3 :跳空缺口
:return:
"""
# axis=1即行数据,tsla_df的每一条行数据即为每一个交易日数据
tsla_df.apply(judge_jump, axis=1)
print('jump_pd:\n', jump_pd)
from abupy import ABuMarketDrawing
# view_indexs传入jump_pd.index,即在k图上使用圆来标示跳空点
ABuMarketDrawing.plot_candle_form_klpd(tsla_df, view_indexs=jump_pd.index)
plt.show()
"""
4.6 pandas三维面板的使用
"""
def sample_46():
"""
4.6 pandas三维面板的使用
:return:
"""
# disable_example_env_ipython不再使用沙盒数据,因为沙盒里面没有相关tsla行业的数据啊
abupy.env.disable_example_env_ipython()
from abupy import ABuIndustries
r_symbol = 'usTSLA'
# 这里获取了和TSLA电动车处于同一行业的股票组成pandas三维面板Panel数据
p_date, _ = ABuIndustries.get_industries_panel_from_target(r_symbol, show=False)
print('type(p_date):', type(p_date))
print('p_date:\n', p_date)
print('p_date[usTTM].head():\n', p_date['usTTM'].head())
p_data_it = p_date.swapaxes('items', 'minor')
print('p_data_it:\n', p_data_it)
p_data_it_close = p_data_it['close'].dropna(axis=0)
print('p_data_it_close.tail():\n', p_data_it_close.tail())
from abupy import ABuScalerUtil
# ABuScalerUtil.scaler_std将所有close的切面数据做(group - group.mean()) / group.std()标示化,为了可视化在同一范围
p_data_it_close = ABuScalerUtil.scaler_std(p_data_it_close)
p_data_it_close.plot()
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.ylabel('Price')
plt.xlabel('Time')
plt.show()
if __name__ == "__main__":
sample_411()
# sample_412()
# sample_413()
# sample_414()
# sample_415()
# sample_420()
# sample_421()
# sample_422()
# sample_423()
# sample_424_1()
# sample_424_2()
# sample_425()
# sample_431()
# sample_432()
# sample_441()
# sample_442()
# sample_45_1()
# sample_45_2()
# sample_46()
+753
View File
@@ -0,0 +1,753 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
from __future__ import division
import warnings
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from scipy import stats
# noinspection PyUnresolvedReferences
import abu_local_env
import abupy
from abupy import ABuSymbolPd
from abupy import pd_rolling_std, pd_ewm_std, pd_rolling_mean
warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
# 使用沙盒数据,目的是和书中一样的数据环境
abupy.env.enable_example_env_ipython()
tsla_df = ABuSymbolPd.make_kl_df('usTSLA', n_folds=2)
"""
第五章 量化工具——可视化
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
"""
# noinspection PyUnresolvedReferences
def plot_demo(axs=None, just_series=False):
"""
绘制tsla的收盘价格曲线
:param axs: axs为子画布,稍后会详细讲解
:param just_series: 是否只绘制一条收盘曲线使用series,后面会用到
:return:
"""
# 如果参数传入子画布则使用子画布绘制,下一节会使用
drawer = plt if axs is None else axs
# Series对象tsla_df.close,红色
drawer.plot(tsla_df.close, c='r')
if not just_series:
# 为曲线不重叠,y变量加了10个单位tsla_df.close.values + 10
# numpy对象tsla_df.close.index tsla_df.close.values,绿色
drawer.plot(tsla_df.close.index, tsla_df.close.values + 10,
c='g')
# 为曲线不重叠,y变量加了20个单位
# list对象,numpy.tolist()将numpy对象转换为list对象,蓝色
drawer.plot(tsla_df.close.index.tolist(),
(tsla_df.close.values + 20).tolist(), c='b')
plt.xlabel('time')
plt.ylabel('close')
plt.title('TSLA CLOSE')
plt.grid(True)
def sample_511():
"""
5.1.1 matplotlib可视化基础
:return:
"""
print('tsla_df.tail():\n', tsla_df.tail())
plot_demo()
plt.show()
def sample_512():
"""
5.1.2 matplotlib子画布及loc的使用
:return:
"""
_, axs = plt.subplots(nrows=2, ncols=2, figsize=(14, 10))
# 画布0loc0 plot_demo中传入画布,则使用传入的画布绘制
drawer = axs[0][0]
plot_demo(drawer)
drawer.legend(['Series', 'Numpy', 'List'], loc=0)
# 画布1loc1
drawer = axs[0][1]
plot_demo(drawer)
drawer.legend(['Series', 'Numpy', 'List'], loc=1)
# 画布2loc2
drawer = axs[1][0]
plot_demo(drawer)
drawer.legend(['Series', 'Numpy', 'List'], loc=2)
# 画布3loc2 设置bbox_to_anchor,在画布外的相对位置绘制
drawer = axs[1][1]
plot_demo(drawer)
drawer.legend(['Series', 'Numpy', 'List'], bbox_to_anchor=(1.05, 1),
loc=2,
borderaxespad=0.)
plt.show()
def sample_513():
"""
5.1.3 k线图的绘制
:return:
"""
import matplotlib.finance as mpf
__colorup__ = "red"
__colordown__ = "green"
# 为了示例清晰,只拿出前30天的交易数据绘制蜡烛图,
tsla_part_df = tsla_df[:30]
fig, ax = plt.subplots(figsize=(14, 7))
qutotes = []
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)):
# 蜡烛图的日期要使用matplotlib.finance.date2num进行转换为特有的数字值
d = mpf.date2num(d)
# 日期,开盘,收盘,最高,最低组成tuple对象val
val = (d, o, c, h, l)
# 加val加入qutotes
qutotes.append(val)
# 使用mpf.candlestick_ochl进行蜡烛绘制,ochl代表:openclosehighlow
mpf.candlestick_ochl(ax, qutotes, width=0.6, colorup=__colorup__,
colordown=__colordown__)
ax.autoscale_view()
ax.xaxis_date()
plt.show()
def sample_52():
"""
5.2 使用bokeh交互可视化
:return:
"""
from abupy import ABuMarketDrawing
ABuMarketDrawing.plot_candle_form_klpd(tsla_df, html_bk=True)
"""
5.3 使用pandas可视化数据
"""
def sample_531_1():
"""
5.3.1_1 绘制股票的收益,及收益波动情况 demo list
:return:
"""
# 示例序列
demo_list = np.array([2, 4, 16, 20])
# 以三天为周期计算波动
demo_window = 3
# pd.rolling_std * np.sqrt
print('pd.rolling_std(demo_list, window=demo_window, center=False) * np.sqrt(demo_window):\n',
pd_rolling_std(demo_list, window=demo_window, center=False) * np.sqrt(demo_window))
print('pd.Series([2, 4, 16]).std() * np.sqrt(demo_window):', pd.Series([2, 4, 16]).std() * np.sqrt(demo_window))
print('pd.Series([4, 16, 20]).std() * np.sqrt(demo_window):', pd.Series([4, 16, 20]).std() * np.sqrt(demo_window))
print('np.sqrt(pd.Series([2, 4, 16]).var() * demo_window):', np.sqrt(pd.Series([2, 4, 16]).var() * demo_window))
def sample_531_2():
"""
5.3.1_2 绘制股票的收益,及收益波动情况
:return:
"""
tsla_df_copy = tsla_df.copy()
# 投资回报
tsla_df_copy['return'] = np.log(tsla_df['close'] / tsla_df['close'].shift(1))
# 移动收益标准差
tsla_df_copy['mov_std'] = pd_rolling_std(tsla_df_copy['return'],
window=20,
center=False) * np.sqrt(20)
# 加权移动收益标准差,与移动收益标准差基本相同,只不过根据时间权重计算std
tsla_df_copy['std_ewm'] = pd_ewm_std(tsla_df_copy['return'], span=20,
min_periods=20,
adjust=True) * np.sqrt(20)
tsla_df_copy[['close', 'mov_std', 'std_ewm', 'return']].plot(subplots=True, grid=True)
plt.show()
def sample_532():
"""
5.3.2 绘制股票的价格与均线
:return:
"""
tsla_df.close.plot()
# ma 30
# pd_rolling_mean(tsla_df.close, window=30).plot()
pd_rolling_mean(tsla_df.close, window=30).plot()
# ma 60
# pd.rolling_mean(tsla_df.close, window=60).plot()
pd_rolling_mean(tsla_df.close, window=60).plot()
# ma 90
# pd.rolling_mean(tsla_df.close, window=90).plot()
pd_rolling_mean(tsla_df.close, window=90).plot()
# loc='best'即自动寻找适合的位置
plt.legend(['close', '30 mv', '60 mv', '90 mv'], loc='best')
plt.show()
def sample_533():
"""
5.3.3 其它pandas统计图形种类
:return:
"""
# iloc获取所有低开高走的下一个交易日组成low_to_high_df,由于是下一个交易日
# 所以要对满足条件的交易日再次通过iloc获取,下一个交易日index用key.values + 1
# key序列的值即为0-len(tsla_df), 即为交易日index,详情查阅本章初tail
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]
# 通过where将下一个交易日的涨跌幅通过ceil,floor向上,向下取整
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']))
# 使用pd.Series包裹,方便之后绘制
change_ceil_floor = pd.Series(change_ceil_floor)
print('低开高收的下一个交易日所有下跌的跌幅取整和sum: ' + str(
change_ceil_floor[change_ceil_floor < 0].sum()))
print('低开高收的下一个交易日所有上涨的涨幅取整和sum: ' + str(
change_ceil_floor[change_ceil_floor > 0].sum()))
# 2 * 2: 四张子图
_, axs = plt.subplots(nrows=2, ncols=2, figsize=(12, 10))
# 竖直柱状图,可以看到-1的柱子最高, 图5-7左上
change_ceil_floor.value_counts().plot(kind='bar', ax=axs[0][0])
# 水平柱状图,可以看到-1的柱子最长, 图5-7右上
change_ceil_floor.value_counts().plot(kind='barh', ax=axs[0][1])
# 概率密度图,可以看到向左偏移, 图5-7左下
change_ceil_floor.value_counts().plot(kind='kde', ax=axs[1][0])
# 圆饼图,可以看到-1所占的比例最高, -2的比例也大于+2,图5-7右下
change_ceil_floor.value_counts().plot(kind='pie', ax=axs[1][1])
plt.show()
def sample_54_1():
"""
5.4 使用seaborn可视化数据
:return:
"""
sns.distplot(tsla_df['p_change'], bins=80)
plt.show()
sns.boxplot(x='date_week', y='p_change', data=tsla_df)
plt.show()
sns.jointplot(tsla_df['high'], tsla_df['low'])
plt.show()
def sample_54_2():
"""
5.4 使用seaborn可视化数据
:return:
"""
change_df = pd.DataFrame({'tsla': tsla_df.p_change})
# join usGOOG
change_df = change_df.join(pd.DataFrame({'goog': ABuSymbolPd.make_kl_df('usGOOG', n_folds=2).p_change}),
how='outer')
# join usAAPL
change_df = change_df.join(pd.DataFrame({'aapl': ABuSymbolPd.make_kl_df('usAAPL', n_folds=2).p_change}),
how='outer')
# join usFB
change_df = change_df.join(pd.DataFrame({'fb': ABuSymbolPd.make_kl_df('usFB', n_folds=2).p_change}),
how='outer')
# join usBIDU
change_df = change_df.join(pd.DataFrame({'bidu': ABuSymbolPd.make_kl_df('usBIDU', n_folds=2).p_change}),
how='outer')
change_df = change_df.dropna()
# 表5-2所示
print('change_df.head():\n', change_df.head())
# 使用corr计算数据的相关性
corr = change_df.corr()
_, ax = plt.subplots(figsize=(8, 5))
# sns.heatmap热力图展示每组股票涨跌幅的相关性
sns.heatmap(corr, ax=ax)
plt.show()
"""
5.5 实例1:可视化量化策略的交易区间,卖出原因
"""
def sample_55_1():
"""
5.5 可视化量化策略的交易区间,卖出原因
:return:
"""
def plot_trade(buy_date, sell_date):
# 找出2014-07-28对应时间序列中的index作为start
start = tsla_df[tsla_df.index == buy_date].key.values[0]
# 找出2014-09-05对应时间序列中的index作为end
end = tsla_df[tsla_df.index == sell_date].key.values[0]
# 使用5.1.1封装的绘制tsla收盘价格时间序列函数plot_demo
# just_seriesTrue, 即只绘制一条曲线使用series数据
plot_demo(just_series=True)
# 将整个时间序列都填充一个底色blue,注意透明度alpha=0.08是为了
# 之后标注其他区间透明度高于0.08就可以清楚显示
plt.fill_between(tsla_df.index, 0, tsla_df['close'], color='blue',
alpha=.08)
# 标注股票持有周期绿色,使用start和end切片周期
# 透明度alpha=0.38 > 0.08
plt.fill_between(tsla_df.index[start:end], 0,
tsla_df['close'][start:end], color='green',
alpha=.38)
# 设置y轴的显示范围,如果不设置ylim,将从0开始作为起点显示,效果不好
plt.ylim(np.min(tsla_df['close']) - 5,
np.max(tsla_df['close']) + 5)
# 使用loc='best'
plt.legend(['close'], loc='best')
# 标注交易区间2014-07-28到2014-09-05, 图5-12所示
plot_trade('2014-07-28', '2014-09-05')
plt.show()
def plot_trade_with_annotate(buy_date, sell_date, annotate):
"""
:param buy_date: 交易买入日期
:param sell_date: 交易卖出日期
:param annotate: 卖出原因
:return:
"""
# 标注交易区间buy_date到sell_date
plot_trade(buy_date, sell_date)
# annotate文字,asof:从tsla_df['close']中找到index:sell_date对应值
plt.annotate(annotate,
xy=(sell_date, tsla_df['close'].asof(sell_date)),
arrowprops=dict(facecolor='yellow'),
horizontalalignment='left', verticalalignment='top')
plot_trade_with_annotate('2014-07-28', '2014-09-05',
'sell for stop loss')
plt.show()
def sample_55_2():
"""
5.5 可视化量化策略的交易区间,卖出原因
:return:
"""
def plot_trade(buy_date, sell_date):
# 找出2014-07-28对应时间序列中的index作为start
start = tsla_df[tsla_df.index == buy_date].key.values[0]
# 找出2014-09-05对应时间序列中的index作为end
end = tsla_df[tsla_df.index == sell_date].key.values[0]
# 使用5.1.1封装的绘制tsla收盘价格时间序列函数plot_demo
# just_seriesTrue, 即只绘制一条曲线使用series数据
plot_demo(just_series=True)
# 将整个时间序列都填充一个底色blue,注意透明度alpha=0.08是为了
# 之后标注其他区间透明度高于0.08就可以清楚显示
plt.fill_between(tsla_df.index, 0, tsla_df['close'], color='blue',
alpha=.08)
# 标注股票持有周期绿色,使用start和end切片周期,透明度alpha=0.38 > 0.08
if tsla_df['close'][end] < tsla_df['close'][start]:
# 如果赔钱了显示绿色
plt.fill_between(tsla_df.index[start:end], 0,
tsla_df['close'][start:end], color='green',
alpha=.38)
is_win = False
else:
# 如果挣钱了显示红色
plt.fill_between(tsla_df.index[start:end], 0,
tsla_df['close'][start:end], color='red',
alpha=.38)
is_win = True
# 设置y轴的显示范围,如果不设置ylim,将从0开始作为起点显示
plt.ylim(np.min(tsla_df['close']) - 5,
np.max(tsla_df['close']) + 5)
# 使用loc='best'
plt.legend(['close'], loc='best')
# 将是否盈利结果返回
return is_win
def plot_trade_with_annotate(buy_date, sell_date):
"""
:param buy_date: 交易买入日期
:param sell_date: 交易卖出日期
:return:
"""
# 标注交易区间buy_date到sell_date
is_win = plot_trade(buy_date, sell_date)
# 根据is_win来判断是否显示止盈还是止损卖出
plt.annotate(
'sell for stop win' if is_win else 'sell for stop loss',
xy=(sell_date, tsla_df['close'].asof(sell_date)),
arrowprops=dict(facecolor='yellow'),
horizontalalignment='left', verticalalignment='top')
# 区间2014-07-28到2014-09-05
plot_trade_with_annotate('2014-07-28', '2014-09-05')
# 区间2015-01-28到2015-03-11
plot_trade_with_annotate('2015-01-28', '2015-03-11')
# 区间2015-04-10到2015-07-10
plot_trade_with_annotate('2015-04-10', '2015-07-10')
# 区间2015-10-2到2015-10-14
plot_trade_with_annotate('2015-10-2', '2015-10-14')
# 区间2016-02-10到2016-04-11
plot_trade_with_annotate('2016-02-10', '2016-04-11')
plt.show()
"""
5.6 实例2:标准化两个股票的观察周期
"""
goog_df = ABuSymbolPd.make_kl_df('usGOOG', n_folds=2)
def plot_two_stock(tsla, goog, axs=None):
# 如果有传递子画布,使用子画布,否则plt
drawer = plt if axs is None else axs
# tsla red
drawer.plot(tsla, c='r')
# google greeen
drawer.plot(goog, c='g')
# 显示网格
drawer.grid(True)
# 图例标注
drawer.legend(['tsla', 'google'], loc='best')
def sample_56_1():
"""
5.6 标准化两个股票的观察周期
:return:
"""
# mean:打印均值,median:打印中位数
print(round(goog_df.close.mean(), 2), round(goog_df.close.median(), 2))
# 表5-3所示
print('goog_df.tail():\n', goog_df.tail())
plot_two_stock(tsla_df.close, goog_df.close)
plt.title('TSLA and Google CLOSE')
# x轴时间
plt.xlabel('time')
# y轴收盘价格
plt.ylabel('close')
plt.show()
def sample_56_2():
"""
5.6 标准化两个股票的观察周期
:return:
"""
# noinspection PyShadowingNames
def two_mean_list(one, two, type_look='look_max'):
"""
只针对俩个输入的均值归一化
:param one:
:param two:
:param type_look:
:return:
"""
one_mean = one.mean()
two_mean = two.mean()
if type_look == 'look_max':
"""
向较大的均值序列看齐
"""
one, two = (one, one_mean / two_mean * two) \
if one_mean > two_mean else (
one * two_mean / one_mean, two)
elif type_look == 'look_min':
"""
向较小的均值序列看齐
"""
one, two = (one * two_mean / one_mean, two) \
if one_mean > two_mean else (
one, two * one_mean / two_mean)
return one, two
def regular_std(group):
# z-score规范化也称零-均值规范化
return (group - group.mean()) / group.std()
def regular_mm(group):
# 最小-最大规范化
return (group - group.min()) / (group.max() - group.min())
# 2行2列,4个画布
_, axs = plt.subplots(nrows=2, ncols=2, figsize=(14, 10))
# 第一个regular_std, 如图5-16左上所示
drawer = axs[0][0]
plot_two_stock(regular_std(tsla_df.close), regular_std(goog_df.close),
drawer)
drawer.set_title('(group - group.mean()) / group.std()')
# 第二个regular_mm,如图5-16右上所示
drawer = axs[0][1]
plot_two_stock(regular_mm(tsla_df.close), regular_mm(goog_df.close),
drawer)
drawer.set_title(
'(group - group.min()) / (group.max() - group.min())')
# 第三个向较大的序列看齐,如图5-16左上所示
drawer = axs[1][0]
one, two = two_mean_list(tsla_df.close, goog_df.close,
type_look='look_max')
plot_two_stock(one, two, drawer)
drawer.set_title('two_mean_list type_look=look_max')
# 第四个向较小的序列看齐,如图5-16右下所示
drawer = axs[1][1]
one, two = two_mean_list(tsla_df.close, goog_df.close,
type_look='look_min')
plot_two_stock(one, two, drawer)
drawer.set_title('two_mean_list type_look=look_min')
plt.show()
def sample_56_3():
"""
5.6 标准化两个股票的观察周期
:return:
"""
_, ax1 = plt.subplots()
ax1.plot(tsla_df.close, c='r', label='tsla')
# 第一个ax的标注
ax1.legend(loc=2)
ax1.grid(False)
# 反向y轴 twinx
ax2 = ax1.twinx()
ax2.plot(goog_df.close, c='g', label='google')
# 第二个ax的标志
ax2.legend(loc=1)
plt.show()
# noinspection PyTypeChecker
def sample_571_1():
"""
5.7.1 黄金分割线的定义方式
:return:
"""
# 收盘价格序列中的最大值
cs_max = tsla_df.close.max()
# 收盘价格序列中的最小值
cs_min = tsla_df.close.min()
sp382 = (cs_max - cs_min) * 0.382 + cs_min
sp618 = (cs_max - cs_min) * 0.618 + cs_min
print('视觉上的382: ' + str(round(sp382, 2)))
print('视觉上的618: ' + str(round(sp618, 2)))
sp382_stats = stats.scoreatpercentile(tsla_df.close, 38.2)
sp618_stats = stats.scoreatpercentile(tsla_df.close, 61.8)
print('统计上的382: ' + str(round(sp382_stats, 2)))
print('统计上的618: ' + str(round(sp618_stats, 2)))
# noinspection PyTypeChecker
def sample_571_2():
"""
5.7.1 黄金分割线的定义方式
:return:
"""
from collections import namedtuple
# 收盘价格序列中的最大值
cs_max = tsla_df.close.max()
# 收盘价格序列中的最小值
cs_min = tsla_df.close.min()
sp382 = (cs_max - cs_min) * 0.382 + cs_min
sp618 = (cs_max - cs_min) * 0.618 + cs_min
sp382_stats = stats.scoreatpercentile(tsla_df.close, 38.2)
sp618_stats = stats.scoreatpercentile(tsla_df.close, 61.8)
def plot_golden():
# 从视觉618和统计618中筛选更大的值
above618 = np.maximum(sp618, sp618_stats)
# 从视觉618和统计618中筛选更小的值
below618 = np.minimum(sp618, sp618_stats)
# 从视觉382和统计382中筛选更大的值
above382 = np.maximum(sp382, sp382_stats)
# 从视觉382和统计382中筛选更小的值
below382 = np.minimum(sp382, sp382_stats)
# 绘制收盘价
plt.plot(tsla_df.close)
# 水平线视觉382
plt.axhline(sp382, c='r')
# 水平线统计382
plt.axhline(sp382_stats, c='m')
# 水平线视觉618
plt.axhline(sp618, c='g')
# 水平线统计618
plt.axhline(sp618_stats, c='k')
# 填充618 red
plt.fill_between(tsla_df.index, above618, below618,
alpha=0.5, color="r")
# 填充382 green
plt.fill_between(tsla_df.index, above382, below382,
alpha=0.5, color="g")
# 最后使用namedtuple包装上,方便获取
return namedtuple('golden', ['above618', 'below618', 'above382',
'below382'])(
above618, below618, above382, below382)
golden = plot_golden()
# 根据绘制顺序标注名称
plt.legend(['close', 'sp382', 'sp382_stats', 'sp618', 'sp618_stats'],
loc='best')
plt.show()
print('理论上的最高盈利: {}'.format(golden.above618 - golden.below382))
return golden
def sample_572():
"""
5.7.2 多维数据绘制示例
:return:
"""
from itertools import product
buy_rate = [0.20, 0.25, 0.30]
sell_rate = [0.70, 0.80, 0.90]
def find_percent_point(percent, y_org, want_max):
"""
:param percent: 比例
:param y_org: close价格序列
:param want_max: 是否返回大的值
:return:
"""
cs_max = y_org.max()
cs_min = y_org.min()
# 如果want_max 就使用maximum否则minimum
maxmin_mum = np.maximum if want_max else np.minimum
# 每次都计算统计上和视觉上,根据want_max返回大的值above,或小的值below
return maxmin_mum(
# 统计上的计算
stats.scoreatpercentile(y_org, np.round(percent * 100, 1)),
# 视觉上的计算
(cs_max - cs_min) * percent + cs_min)
# 存储结果list
result = list()
# 先将0.382, 0.618这一组放入结果队列中
golden = sample_571_2()
result.append(
(0.382, 0.618, round(golden.above618 - golden.below382, 2)))
# 将buy_rate和sell_rate做笛卡尔积排列各种组合
for (buy, sell) in product(buy_rate, sell_rate):
# 如果是买入比例want_max为False,因为只计算理论最高盈利,只需要最below
profit_below = find_percent_point(buy, tsla_df.close, False)
# 如果是卖出比例want_max为True,因为只计算理论最高盈利,只需要最above
profit_above = find_percent_point(sell, tsla_df.close, True)
# 最终将买入比例,卖出比例,理论最高盈利append
result.append((buy, sell,
round(profit_above - profit_below, 2)))
# 最后使用np.array套上result
result = np.array(result)
print('result:\n', result)
# 1. 通过scatter点图
cmap = plt.get_cmap('jet', 20)
cmap.set_under('gray')
fig, ax = plt.subplots(figsize=(8, 5))
# scatter点图,result[:, 0]:xresult[:, 1]:y, result[:, 2]:c
cax = ax.scatter(result[:, 0], result[:, 1], c=result[:, 2],
cmap=cmap, vmin=np.min(result[:, 2]),
vmax=np.max(result[:, 2]))
fig.colorbar(cax, label='max profit', extend='min')
plt.grid(True)
plt.xlabel('buy rate')
plt.ylabel('sell rate')
plt.show()
# 2. 通过mpl_toolkits.mplot3d
# noinspection PyUnresolvedReferences
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(9, 6))
ax = fig.gca(projection='3d')
ax.view_init(30, 60)
ax.scatter3D(result[:, 0], result[:, 1], result[:, 2], c='r', s=50,
cmap='spring')
ax.set_xlabel('buy rate')
ax.set_ylabel('sell rate')
ax.set_zlabel('max profit')
plt.show()
# noinspection PyTypeChecker
def sample_581():
"""
5.8.1 MACD指标的可视化
:return:
"""
from abupy import nd
nd.macd.plot_macd_from_klpd(tsla_df)
def sample_582_1():
"""
5.8.2_1 ATR指标的可视化, 使用talib
:return:
"""
from abupy import nd
nd.atr.plot_atr_from_klpd(tsla_df)
if __name__ == "__main__":
sample_511()
# sample_512()
# sample_513()
# sample_52()
# sample_531_1()
# sample_531_2()
# sample_532()
# sample_533()
# sample_54_1()
# sample_54_2()
# sample_55_1()
# sample_55_2()
# sample_56_1()
# sample_56_2()
# sample_56_3()
# sample_571_1()
# sample_571_2()
# sample_572()
# sample_581()
# sample_582_1()
+851
View File
@@ -0,0 +1,851 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
from __future__ import division
import warnings
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
# noinspection PyUnresolvedReferences
import abu_local_env
import abupy
from abupy import ABuSymbolPd
from abupy import six, xrange
from abc import ABCMeta, abstractmethod
warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
# 使用沙盒数据,目的是和书中一样的数据环境
abupy.env.enable_example_env_ipython()
tsla_close = ABuSymbolPd.make_kl_df('usTSLA').close
# x序列: 012, ...len(tsla_close)
x = np.arange(0, tsla_close.shape[0])
# 收盘价格序列
y = tsla_close.values
"""
第六章 量化工具——数学:你一生的追求到底能带来多少幸福
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
"""
def sample_611_1(show=True):
"""
6.1.1 线性回归
:return:
"""
import statsmodels.api as sm
from statsmodels import regression
def regress_y(_y):
_y = _y
# x序列: 012, ...len(y)
_x = np.arange(0, len(_y))
_x = sm.add_constant(_x)
# 使用OLS做拟合
_model = regression.linear_model.OLS(_y, _x).fit()
return _model
model = regress_y(y)
b = model.params[0]
k = model.params[1]
# y = kx + b
y_fit = k * x + b
if show:
plt.plot(x, y)
plt.plot(x, y_fit, 'r')
plt.show()
# summary模型拟合概述,表6-1所示
print(model.summary())
return y_fit
# noinspection PyPep8Naming
def sample_611_2():
"""
6.1.1 线性回归
:return:
"""
y_fit = sample_611_1(show=False)
MAE = sum(np.abs(y - y_fit)) / len(y)
print('偏差绝对值之和(MAE)={}'.format(MAE))
MSE = sum(np.square(y - y_fit)) / len(y)
print('偏差绝对值之和(MSE)={}'.format(MSE))
RMSE = np.sqrt(sum(np.square(y - y_fit)) / len(y))
print('偏差绝对值之和(RMSE)={}'.format(RMSE))
from sklearn import metrics
print('sklearn偏差绝对值之和(MAE)={}'.format(metrics.mean_absolute_error(y, y_fit)))
print('sklearn偏差平方(MSE)={}'.format(metrics.mean_squared_error(y, y_fit)))
print('sklearn偏差平方和开平方(RMSE)={}'.format(np.sqrt(metrics.mean_squared_error(y, y_fit))))
# noinspection PyCallingNonCallable
def sample_612():
"""
6.1.2 多项式回归
:return:
"""
import itertools
# 生成9个subplots 3*3
_, axs = plt.subplots(nrows=3, ncols=3, figsize=(15, 15))
# 将 3 * 3转换成一个线性list
axs_list = list(itertools.chain.from_iterable(axs))
# 1-9次多项式回归
poly = np.arange(1, 10, 1)
for p_cnt, ax in zip(poly, axs_list):
# 使用polynomial.Chebyshev.fit进行多项式拟合
p = np.polynomial.Chebyshev.fit(x, y, p_cnt)
# 使用p直接对x序列代人即得到拟合结果序列
y_fit = p(x)
# 度量mse值
from sklearn import metrics
mse = metrics.mean_squared_error(y, y_fit)
# 使用拟合次数和mse误差大小设置标题
ax.set_title('{} poly MSE={}'.format(p_cnt, mse))
ax.plot(x, y, '', x, y_fit, 'r.')
plt.show()
def sample_613():
"""
6.1.3 插值
:return:
"""
from scipy.interpolate import interp1d, splrep, splev
# 示例两种插值计算方式
_, axs = plt.subplots(nrows=1, ncols=2, figsize=(14, 5))
# 线性插值
linear_interp = interp1d(x, y)
# axs[0]左边的
axs[0].set_title('interp1d')
# 在相同坐标系下,同样的x,插值的y值使r.绘制(红色点)
axs[0].plot(x, y, '', x, linear_interp(x), 'r.')
# B-spline插值
splrep_interp = splrep(x, y)
# axs[1]右边的
axs[1].set_title('splrep')
# #在相同坐标系下,同样的x,插值的y值使g.绘制(绿色点)
axs[1].plot(x, y, '', x, splev(x, splrep_interp), 'g.')
plt.show()
"""
6.2 蒙特卡洛方法与凸优化
6.2.1 你一生的追求到底能带来多少幸福
"""
# 每个人平均寿命期望是75年,约75*365=27375天
K_INIT_LIVING_DAYS = 27375
class Person(object):
"""
人类
"""
def __init__(self):
# 初始化人平均能活的寿命
self.living = K_INIT_LIVING_DAYS
# 初始化幸福指数
self.happiness = 0
# 初始化财富值
self.wealth = 0
# 初始化名望权利
self.fame = 0
# 活着的第几天
self.living_day = 0
def live_one_day(self, seek):
"""
每天只能进行一个seek,这个seek决定了你今天追求的是什么,得到了什么
seek的类型属于下面将编写的BaseSeekDay
:param seek:
:return:
"""
# 调用每个独特的BaseSeekDay类都会实现的do_seek_day,得到今天的收获
consume_living, happiness, wealth, fame = seek.do_seek_day()
# 每天要减去生命消耗,有些seek前面还会增加生命
self.living -= consume_living
# seek得到的幸福指数积累
self.happiness += happiness
# seek得到的财富积累
self.wealth += wealth
# seek得到的名望权力积累
self.fame += fame
# 活完这一天了
self.living_day += 1
class BaseSeekDay(six.with_metaclass(ABCMeta, object)):
def __init__(self):
# 每个追求每天消耗生命的常数
self.living_consume = 0
# 每个追求每天幸福指数常数
self.happiness_base = 0
# 每个追求每天财富积累常数
self.wealth_base = 0
# 每个追求每天名望权利积累常数
self.fame_base = 0
# 每个追求每天消耗生命的可变因素序列
self.living_factor = [0]
# 每个追求每天幸福指数的可变因素序列
self.happiness_factor = [0]
# 每个追求每天财富积累的可变因素序列
self.wealth_factor = [0]
# 每个追求每天名望权利的可变因素序列
self.fame_factor = [0]
# 追求了多少天了这一生
self.do_seek_day_cnt = 0
# 子类进行常数及可变因素序列设置
self._init_self()
@abstractmethod
def _init_self(self, *args, **kwargs):
# 子类必须实现,设置自己的生命消耗的常数,幸福指数常数等常数设置
pass
@abstractmethod
def _gen_living_days(self, *args, **kwargs):
# 子类必须实现,设置自己的可变因素序列
pass
def do_seek_day(self):
"""
每一天的追求具体seek
:return:
"""
# 生命消耗=living_consume:消耗常数 * happiness_factor:可变序列
if self.do_seek_day_cnt >= len(self.living_factor):
# 超出len(self.living_factor), 就取最后一个living_factor[-1]
consume_living = \
self.living_factor[-1] * self.living_consume
else:
# 每个类自定义这个追求的消耗生命常数,以及living_factor,比如
# HealthSeekDay追求健康,living_factor序列的值即由负值->正值
# 每个子类living_factor会有自己特点的变化速度及序列长度,导致每个
# 追求对生命的消耗随着追求的次数变化不一
consume_living = self.living_factor[self.do_seek_day_cnt] \
* self.living_consume
# 幸福指数=happiness_base:幸福常数 * happiness_factor:可变序列
if self.do_seek_day_cnt >= len(self.happiness_factor):
# 超出len(self.happiness_factor), 就取最后一个
# 由于happiness_factor值由:n—>0 所以happiness_factor[-1]=0
# 即随着追求一个事物的次数过多后会变的没有幸福感
happiness = self.happiness_factor[
-1] * self.happiness_base
else:
# 每个类自定义这个追求的幸福指数常数,以及happiness_factor
# happiness_factor子类的定义一般是从高->低变化
happiness = self.happiness_factor[
self.do_seek_day_cnt] * self.happiness_base
# 财富积累=wealth_base:积累常数 * wealth_factor:可变序列
if self.do_seek_day_cnt >= len(self.wealth_factor):
# 超出len(self.wealth_factor), 就取最后一个
wealth = self.wealth_factor[-1] * self.wealth_base
else:
# 每个类自定义这个追求的财富指数常数,以及wealth_factor
wealth = self.wealth_factor[
self.do_seek_day_cnt] * self.wealth_base
# 权利积累=fame_base:积累常数 * fame_factor:可变序列
if self.do_seek_day_cnt >= len(self.fame_factor):
# 超出len(self.fame_factor), 就取最后一个
fame = self.fame_factor[-1] * self.fame_base
else:
# 每个类自定义这个追求的名望权利指数常数,以及fame_factor
fame = self.fame_factor[
self.do_seek_day_cnt] * self.fame_base
# 追求了多少天了这一生 + 1
self.do_seek_day_cnt += 1
# 返回这个追求这一天对生命的消耗,得到的幸福,财富,名望权利
return consume_living, happiness, wealth, fame
def regular_mm(group):
# 最小-最大规范化
return (group - group.min()) / (group.max() - group.min())
"""
HealthSeekDay
"""
class HealthSeekDay(BaseSeekDay):
"""
HealthSeekDay追求健康长寿的一天:
形象:健身,旅游,娱乐,做感兴趣的事情。
抽象:追求健康长寿。
"""
def _init_self(self):
# 每天对生命消耗的常数=1,即代表1天
self.living_consume = 1
# 每天幸福指数常数=1
self.happiness_base = 1
# 设定可变因素序列
self._gen_living_days()
def _gen_living_days(self):
# 只生成12000个序列,因为下面的happiness_factor序列值由1>0
# 所以大于12000次的追求都将只是单纯消耗生命,并不增加幸福指数
# 即随着做一件事情的次数越来越多,幸福感越来越低,直到完全体会不到幸福
days = np.arange(1, 12000)
# 基础函数选用sqrt, 影响序列变化速度
living_days = np.sqrt(days)
"""
对生命消耗可变因素序列值由-1->1, 也就是这个追求一开始的时候对生命
的消耗为负增长,延长了生命,随着追求的次数不断增多对生命的消耗转为正
数因为即使一个人天天锻炼身体,天天吃营养品,也还是会有自然死亡的那
一天
"""
# *2-1的目的:regular_mm在0-1之间,HealthSeekDay要结果在-11之间
self.living_factor = regular_mm(living_days) * 2 - 1
# 结果在1-0之间 [::-1]: 将0->1转换到1->0
self.happiness_factor = regular_mm(days)[::-1]
def sample_621_1():
"""
6.2.1_1 你一生的故事:HealthSeekDay
:return:
"""
# 初始化我
me = Person()
# 初始化追求健康长寿快乐
seek_health = HealthSeekDay()
while me.living > 0:
# 只要还活着,就追求健康长寿快乐
me.live_one_day(seek_health)
print('只追求健康长寿快乐活了{}年,幸福指数{},积累财富{},名望权力{}'.format
(round(me.living_day / 365, 2), round(me.happiness, 2),
me.wealth, me.fame))
plt.plot(seek_health.living_factor * seek_health.living_consume)
plt.plot(seek_health.happiness_factor * seek_health.happiness_base)
plt.legend(['living_factor', 'happiness_factor'], loc='best')
plt.show()
"""
StockSeekDay
"""
class StockSeekDay(BaseSeekDay):
"""
StockSeekDay追求财富金钱的一天:
形象:做股票投资赚钱的事情。
抽象:追求财富金钱
"""
def _init_self(self, show=False):
# 每天对生命消耗的常数=2,即代表2天
self.living_consume = 2
# 每天幸福指数常数=0.5
self.happiness_base = 0.5
# 财富积累常数=10,默认=0
self.wealth_base = 10
# 设定可变因素序列
self._gen_living_days()
def _gen_living_days(self):
# 只生成10000个序列
days = np.arange(1, 10000)
# 针对生命消耗living_factor的基础函数还是sqrt
living_days = np.sqrt(days)
# 由于不需要像HealthSeekDay从负数开始,所以直接regular_mm 即:0->1
self.living_factor = regular_mm(living_days)
# 针对幸福感可变序列使用了np.power4,即变化速度比sqrt快
happiness_days = np.power(days, 4)
# 幸福指数可变因素会快速递减由1->0
self.happiness_factor = regular_mm(happiness_days)[::-1]
"""
这里简单设定wealth_factor=living_factor
living_factor(0-1), 导致wealth_factor(0-1), 即财富积累越到
后面越有效率,速度越快,头一个100万最难赚
"""
self.wealth_factor = self.living_factor
def sample_621_2():
"""
6.2.1_2 你一生的故事:StockSeekDay
:return:
"""
# 初始化我
me = Person()
# 初始化追求财富金钱
seek_stock = StockSeekDay()
while me.living > 0:
# 只要还活着,就追求财富金钱
me.live_one_day(seek_stock)
print('只追求财富金钱活了{}年,幸福指数{}, 积累财富{}, 名望权力{}'.format
(round(me.living_day / 365, 2), round(me.happiness, 2),
round(me.wealth, 2), me.fame))
plt.plot(seek_stock.living_factor * seek_stock.living_consume)
plt.plot(seek_stock.happiness_factor * seek_stock.happiness_base)
plt.legend(['living_factor', 'happiness_factor'], loc='best')
plt.show()
"""
FameSeekDay
"""
class FameSeekDay(BaseSeekDay):
"""
FameTask追求名望权力的一天:
追求名望权力
"""
def _init_self(self):
# 每天对生命消耗的常数=3,即代表3天
self.living_consume = 3
# 每天幸福指数常数=0.6
self.happiness_base = 0.6
# 名望权利积累常数=10,默认=0
self.fame_base = 10
# 设定可变因素序列
self._gen_living_days()
def _gen_living_days(self):
# 只生成12000个序列
days = np.arange(1, 12000)
# 针对生命消耗living_factor的基础函数还是sqrt
living_days = np.sqrt(days)
# 由于不需要像HealthSeekDay从负数开始,所以直接regular_mm 即:0->1
self.living_factor = regular_mm(living_days)
# 针对幸福感可变序列使用了np.power2
# 即变化速度比StockSeekDay慢但比HealthSeekDay快
happiness_days = np.power(days, 2)
# 幸福指数可变因素递减由1->0
self.happiness_factor = regular_mm(happiness_days)[::-1]
# 这里简单设定fame_factor=living_factor
self.fame_factor = self.living_factor
def sample_621_3():
"""
6.2.1_3 你一生的故事:FameSeekDay
:return:
"""
# 初始化我
me = Person()
# 初始化追求名望权力
seek_fame = FameSeekDay()
while me.living > 0:
# 只要还活着,就追求名望权力
me.live_one_day(seek_fame)
print('只追求名望权力活了{}年,幸福指数{}, 积累财富{}, 名望权力{}'.format
(round(me.living_day / 365, 2), round(me.happiness, 2),
round(me.wealth, 2), round(me.fame, 2)))
plt.plot(seek_fame.living_factor * seek_fame.living_consume)
plt.plot(seek_fame.happiness_factor * seek_fame.happiness_base)
plt.legend(['living_factor', 'happiness_factor'], loc='best')
plt.show()
"""
6.2.2 使用蒙特卡洛方法计算怎样度过一生最幸福
"""
def my_life(weights):
"""
追求健康长寿快乐的权重:weights[0]
追求财富金钱的权重:weights[1]
追求名望权力的权重:weights[2]
"""
# 追求健康长寿快乐
seek_health = HealthSeekDay()
# 追求财富金钱
seek_stock = StockSeekDay()
# 追求名望权力
seek_fame = FameSeekDay()
# 放在一个list中对对应下面np.random.choice中的index[0, 1, 2]
seek_list = [seek_health, seek_stock, seek_fame]
# 初始化我
me = Person()
# 加权随机抽取序列。80000天肯定够了, 80000天快220年了。。。
seek_choice = np.random.choice([0, 1, 2], 80000, p=weights)
while me.living > 0:
# 追求从加权随机抽取序列已经初始化好的
seek_ind = seek_choice[me.living_day]
seek = seek_list[seek_ind]
# 只要还活着,就追求
me.live_one_day(seek)
return round(me.living_day / 365, 2), round(me.happiness, 2), round(me.wealth, 2), round(me.fame, 2)
def sample_622():
"""
6.2.2 使用蒙特卡洛方法计算怎样度过一生最幸福
:return:
"""
living_day, happiness, wealth, fame = my_life([0.4, 0.3, 0.3])
print('活了{}年,幸福指数{}, 积累财富{}, 名望权力{}'.format(
living_day, happiness, wealth, fame))
from abupy import AbuProgress
progress = AbuProgress(2000, 0, label='my_life...')
result = []
for pos, _ in enumerate(xrange(2000)):
# 2000次随机权重分配
weights = np.random.random(3)
weights /= np.sum(weights)
# result中:tuple[0]权重weights,tuple[1]my_life返回的结果
result.append((weights, my_life(weights)))
progress.show(a_progress=pos + 1)
# result中tuple[1]=my_life返回的结果, my_life[1]=幸福指数,so->x[1][1]
sorted_scores = sorted(result, key=lambda p_x: p_x[1][1], reverse=True)
# 将最优权重sorted_scores[0][0]代入my_life得到结果
living_day, happiness, wealth, fame = my_life(sorted_scores[0][0])
print('活了{}年,幸福指数{}, 积累财富{}, 名望权力{}'.format
(living_day, happiness, wealth, fame))
print('人生最优权重:追求健康{:.3f},追求财富{:.3f},追求名望{:.3f}'.format(
sorted_scores[0][0][0], sorted_scores[0][0][1],
sorted_scores[0][0][2]))
# noinspection PyUnresolvedReferences
from mpl_toolkits.mplot3d import Axes3D
"""
result中: tuple[0]权重weights, tuple[1]my_life返回的结果
r[0][0]: 追求健康长寿快乐的权重
r[0][1]: 追求财富金钱的权重
r[0][2]: 追求名望权力的权重
r[1][1]: my_life[1]=幸福指数
"""
result_show = np.array(
[[r[0][0], r[0][1], r[0][2], r[1][1]] for r in result])
fig = plt.figure(figsize=(9, 6))
ax = fig.gca(projection='3d')
ax.view_init(30, 60)
"""
x:追求健康长寿快乐的权重, y:追求财富金钱的权重
z:追求名望权力的权重, c:color 幸福指数, 颜色越深越幸福
"""
ax.scatter3D(result_show[:, 0], result_show[:, 1], result_show[:, 2],
c=result_show[:, 3], cmap='spring')
ax.set_xlabel('health')
ax.set_ylabel('stock')
ax.set_zlabel('fame')
plt.show()
# 幸福指数
happiness_result = result_show[:, 3]
# 使用qcut分10份
print('pd.qcut(happiness_result, 10).value_counts():\n', pd.qcut(happiness_result, 10).value_counts())
"""
6.2.3 凸优化基础概念
"""
# noinspection PyTypeChecker
def sample_623():
"""
6.2.3 趋势骨架图
:return:
"""
import scipy.optimize as sco
from scipy.interpolate import interp1d
# 继续使用TSLA收盘价格序列
# interp1d线性插值函数
linear_interp = interp1d(x, y)
# 绘制插值
plt.plot(linear_interp(x))
# fminbound寻找给定范围内的最小值:在linear_inter中寻找全局最优范围1504
global_min_pos = sco.fminbound(linear_interp, 1, 504)
# 绘制全局最优点,全局最小值点,r<:红色三角
plt.plot(global_min_pos, linear_interp(global_min_pos), 'r<')
# 每个单位都先画一个点,由两个点连成一条直线形成股价骨架图
last_postion = None
# 步长50,每50个单位求一次局部最小
for find_min_pos in np.arange(50, len(x), 50):
# fmin_bfgs寻找给定值的局部最小值
local_min_pos = sco.fmin_bfgs(linear_interp, find_min_pos, disp=0)
# 形成最小点位置信息(x, y)
draw_postion = (local_min_pos, linear_interp(local_min_pos))
# 第一个50单位last_postionnone, 之后都有值
if last_postion is not None:
# 将两两临近局部最小值相连,两个点连成一条直线
plt.plot([last_postion[0][0], draw_postion[0][0]],
[last_postion[1][0], draw_postion[1][0]], 'o-')
# 将这个步长单位内的最小值点赋予last_postion
last_postion = draw_postion
plt.show()
def sample_624():
"""
6.2.4 全局最优求解怎样度过一生最幸福
:return:
"""
import scipy.optimize as sco
def minimize_happiness_global(weights):
if np.sum(weights) != 1:
# 过滤权重和不等于1的权重组合
return 0
# 最优都是寻找最小值,所以要得到幸福指数最大的权重,
# 返回-my_life,这样最小的结果其实是幸福指数最大的权重配比
return -my_life(weights)[1]
opt_global = sco.brute(minimize_happiness_global,
((0, 1.1, 0.1), (0, 1.1, 0.1), (0, 1.1, 0.1)))
print(opt_global)
living_day, happiness, wealth, fame = my_life(opt_global)
print('活了{}年,幸福指数{}, 积累财富{}, 名望权力{}'.format
(living_day, happiness, wealth, fame))
# noinspection PyTypeChecker
def sample_625():
"""
6.2.5 非凸函数计算怎样度过一生最幸福
:return:
"""
import scipy.optimize as sco
method = 'SLSQP'
# 提供一个函数来规范参数,np.sum(weights) = 1 -> np.sum(weights) - 1 = 0
constraints = ({'type': 'eq', 'fun': lambda p_x: np.sum(p_x) - 1})
# 参数的范围选定
bounds = tuple((0, 0.9) for _ in xrange(3))
print('bounds:', bounds)
def minimize_happiness_local(weights):
# print(weights)
return -my_life(weights)[1]
# 初始化猜测最优参数,这里使用brute计算出的全局最优参数作为guess
guess = [0.5, 0.2, 0.3]
opt_local = sco.minimize(minimize_happiness_local, guess,
method=method, bounds=bounds,
constraints=constraints)
print('opt_local:', opt_local)
# noinspection PyShadowingNames
def sample_626():
"""
6.2.6 标准凸函数求最优
:return:
"""
import scipy.optimize as sco
fig = plt.figure()
from mpl_toolkits.mplot3d import Axes3D
ax = Axes3D(fig)
x = np.arange(-10, 10, 0.5)
y = np.arange(-10, 10, 0.5)
x_grid, y_grid = np.meshgrid(x, y)
# z^2 = x^2 + y^2
z_grid = x_grid ** 2 + y_grid ** 2
ax.plot_surface(x_grid, y_grid, z_grid, rstride=1, cstride=1,
cmap='hot')
plt.show()
def convex_func(xy):
return xy[0] ** 2 + xy[1] ** 2
bounds = ((-10, 10), (-10, 10))
guess = [5, 5]
for method in ['SLSQP', 'TNC', 'L-BFGS-B']:
# 打印start
print(method + ' start')
# noinspection PyTypeChecker
ret = sco.minimize(convex_func, guess, method=method,
bounds=bounds)
print(ret)
# 这里通过np.allclose判定结果是不是(0 0)
print('result is (0, 0): {}'.format(
np.allclose(ret['x'], [0., 0.], atol=0.001)))
# 打印end
print(method + ' end')
"""
6.3 线性代数
"""
# 获取多支股票数据组成panel
my_stock_df = ABuSymbolPd.make_kl_df(
['usBIDU', 'usGOOG', 'usFB', 'usAAPL', 'us.IXIC'], n_folds=2)
# 变换轴向,形成新的切面
my_stock_df = my_stock_df.swapaxes('items', 'minor')
my_stock_df_close = my_stock_df['close'].dropna(axis=0)
def regular_std(group):
# z-score规范化也称零-均值规范化
return (group - group.mean()) / group.std()
def sample_630():
"""
获取多支股票数据组成panel
:return:
"""
print('my_stock_df_close.tail():\n', my_stock_df_close.tail())
my_stock_df_close_std = regular_std(my_stock_df_close)
my_stock_df_close_std.plot()
plt.show()
def sample_631():
"""
6.3.1 矩阵基础知识
:return:
"""
from scipy import linalg
# dataframe转换matrix通过as_matrix
cs_matrix = my_stock_df_close.as_matrix()
# cs_matrix本身有5列数据(5支股票),要变成方阵即保留5行数据0:5
cs_matrix = cs_matrix[0:5, :]
print('cs_matrix.shape:', cs_matrix.shape)
print('cs_matrix:\n', cs_matrix)
eye5 = np.eye(5)
print(eye5)
cs_matrix_inv = linalg.inv(cs_matrix)
print('逆矩阵: cs_matrix_inv')
print(cs_matrix_inv)
# 上面打印cs_matrix_inv输出上并非绝对标准单位矩阵,是对角线值元素接近与1,非对
# 角线元素接近与0的矩阵,需要使用np.allclose来确认结果
print('相乘后的结果是单位矩阵:{}'.format(
np.allclose(np.dot(cs_matrix, cs_matrix_inv), eye5)))
def sample_632():
"""
6.3.2 特征值和特征向量
:return:
"""
from scipy import mat, linalg
a = mat('[1.5 -0.5; -0.5 1.5]')
u, d = linalg.eig(a)
print('特征值向量:{}'.format(u))
print('特征向量(列向量)矩阵:{}'.format(d))
def sample_634():
"""
6.3.4 PCA和SVD使用实例
:return:
"""
from sklearn.decomposition import PCA
my_stock_df_close_std = regular_std(my_stock_df_close)
# n_components=1只保留一个维度
pca = PCA(n_components=1)
# 稍后会有展示fit_transform的实现,以及关键核心代码抽取
my_stock_df_trans_pca = \
pca.fit_transform(my_stock_df_close_std.as_matrix())
plt.plot(my_stock_df_trans_pca)
plt.show()
# 可视化维度和主成分关系,参数空
pca = PCA()
# 直接使用fit,不用fit_transform
pca.fit(my_stock_df_close_std)
# x:保留的维度 y:保留的维度下的方差比总和即保留了多少主成分
plt.plot(np.arange(1, len(pca.explained_variance_ratio_) + 1),
np.cumsum(pca.explained_variance_ratio_))
plt.xlabel('component')
plt.ylabel('explained variance')
plt.show()
# 0.95即保留95%主成分
pca = PCA(0.95)
# 稍后会有展示fit_transform的实现,以及关键核心代码抽取
my_stock_df_trans_pca = \
pca.fit_transform(my_stock_df_close_std.as_matrix())
plt.plot(my_stock_df_trans_pca)
plt.show()
# noinspection PyPep8Naming
def my_pca(n_components=1):
from scipy import linalg
# svd奇异值分解
U, S, V = linalg.svd(my_stock_df_close_std.as_matrix(),
full_matrices=False)
# 通过n_components进行降维
U = U[:, :n_components]
U *= S[:n_components]
# 绘制降维后的矩阵
plt.plot(U)
# 输出如图619所示
my_pca(n_components=3)
plt.show()
if __name__ == "__main__":
sample_611_1()
# sample_611_2()
# sample_612()
# sample_613()
# sample_621_1()
# sample_621_2()
# sample_621_3()
# sample_622()
# sample_623()
# sample_624()
# sample_625()
# sample_626()
# sample_630()
# sample_631()
# sample_632()
# sample_634()
+572
View File
@@ -0,0 +1,572 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
from __future__ import division
# import warnings
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
# noinspection PyUnresolvedReferences
import abu_local_env
import abupy
from abupy import ABuSymbolPd
# warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
# 使用沙盒数据,目的是和书中一样的数据环境
abupy.env.enable_example_env_ipython()
kl_pd = ABuSymbolPd.make_kl_df('usTSLA', n_folds=2)
"""
第七章 量化系统——入门:三只小猪股票投资的故事
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
"""
def sample_711():
"""
7.1.1 趋势跟踪和均值回复的周期重叠性
:return:
"""
sns.set_context(rc={'figure.figsize': (14, 7)})
sns.regplot(x=np.arange(0, kl_pd.shape[0]), y=kl_pd.close.values, marker='+')
plt.show()
from abupy import ABuRegUtil
deg = ABuRegUtil.calc_regress_deg(kl_pd.close.values)
plt.show()
print('趋势角度:' + str(deg))
start = 0
# 前1/4的数据
end = int(kl_pd.shape[0] / 4)
# 将x也使用arange切割
x = np.arange(start, end)
# y根据startend进行切片
y = kl_pd.close.values[start:end]
sns.regplot(x=x, y=y, marker='+')
plt.show()
start = int(kl_pd.shape[0] / 4)
# 向前推1/4单位个时间
end = start + int(kl_pd.shape[0] / 4)
sns.regplot(x=np.arange(start, end), y=kl_pd.close.values[start:end],
marker='+')
plt.show()
def sample_712_1():
"""
7.1.2 均值回复策略
:return:
"""
# 头一年([:252])作为训练数据, 美股交易中一年的交易日有252天
train_kl = kl_pd[:252]
# 后一年([252:])作为回测数据
test_kl = kl_pd[252:]
# 分别画出两部分数据收盘价格曲线
tmp_df = pd.DataFrame(
np.array([train_kl.close.values, test_kl.close.values]).T,
columns=['train', 'test'])
tmp_df[['train', 'test']].plot(subplots=True, grid=True,
figsize=(14, 7))
plt.show()
def sample_712_2(show=True):
"""
7.1.2 均值回复策略
:return:
"""
train_kl = kl_pd[:252]
test_kl = kl_pd[252:]
# 训练数据的收盘价格均值
close_mean = train_kl.close.mean()
# 训练数据的收盘价格标准差
close_std = train_kl.close.std()
# 构造卖出信号阀值
sell_signal = close_mean + close_std / 3
# 构造买入信号阀值
buy_signal = close_mean - close_std / 3
# 可视化训练数据的卖出信号阀值,买入信号阀值及均值线
if show:
# 训练集收盘价格可视化
train_kl.close.plot()
# 水平线,买入信号线, lw代表线的粗度
plt.axhline(buy_signal, color='r', lw=3)
# 水平线,均值线
plt.axhline(close_mean, color='black', lw=1)
# 水平线, 卖出信号线
plt.axhline(sell_signal, color='g', lw=3)
plt.legend(['train close', 'buy_signal', 'close_mean', 'sell_signal'],
loc='best')
plt.show()
# 将卖出信号阀值,买入信号阀值代入回归测试数据可视化
plt.figure(figsize=(14, 7))
# 测试集收盘价格可视化
test_kl.close.plot()
# buy_signal直接代入买入信号
plt.axhline(buy_signal, color='r', lw=3)
# 直接代入训练集均值close
plt.axhline(close_mean, color='black', lw=1)
# sell_signal直接代入卖出信号
plt.axhline(sell_signal, color='g', lw=3)
# 按照上述绘制顺序标注
plt.legend(['test close', 'buy_signal', 'close_mean', 'sell_signal'],
loc='best')
plt.show()
print('买入信号阀值:{} 卖出信号阀值:{}'.format(buy_signal, sell_signal))
return train_kl, test_kl, buy_signal, sell_signal
def sample_712_3(show=True):
"""
7.1.2 均值回复策略
:return:
"""
train_kl, test_kl, buy_signal, sell_signal = sample_712_2(show=False)
# 寻找测试数据中满足买入条件的时间序列
buy_index = test_kl[test_kl['close'] <= buy_signal].index
# 将找到的买入时间系列的信号设置为1,代表买入操作
test_kl.loc[buy_index, 'signal'] = 1
# 表7-2所示
if show:
print('test_kl[52:57]:\n', test_kl[52:57])
# 寻找测试数据中满足卖出条件的时间序列
sell_index = test_kl[test_kl['close'] >= sell_signal].index
# 将找到的卖出时间系列的信号设置为0,代表卖出操作
test_kl.loc[sell_index, 'signal'] = 0
# 表7-3所示
if show:
print('test_kl[48:53]:\n', test_kl[48:53])
# 由于假设都是全仓操作所以signal=keep,即1代表买入持有,0代表卖出空仓
test_kl['keep'] = test_kl['signal']
# 将keep列中的nan使用向下填充的方式填充,结果使keep可以代表最终的交易持股状态
test_kl['keep'].fillna(method='ffill', inplace=True)
# shift(1)及np.log下面会有内容详细讲解
test_kl['benchmark_profit'] = \
np.log(test_kl['close'] / test_kl['close'].shift(1))
# 仅仅为了说明np.log的意义,添加了benchmark_profit2,只为对比数据是否一致
test_kl['benchmark_profit2'] = \
test_kl['close'] / test_kl['close'].shift(1) - 1
if show:
# 可视化对比两种方式计算出的profit是一致的
test_kl[['benchmark_profit', 'benchmark_profit2']].plot(subplots=True,
grid=True,
figsize=(
14, 7))
plt.show()
# test_kl['close'].shift(1): test_kl['close'] / test_kl['close'].shift(1) = 今日收盘价格序列/昨日收盘价格序列
print('test_kl[close][:5]:\n', test_kl['close'][:5])
print('test_kl[close].shift(1)[:5]:\n', test_kl['close'].shift(1)[:5])
# np.log
print('np.log(220 / 218), 220 / 218 - 1.0:', np.log(220 / 218), 220 / 218 - 1.0)
return test_kl
def sample_712_4():
"""
7.1.2 均值回复策略
:return:
"""
test_kl = sample_712_3(show=False)
test_kl['trend_profit'] = test_kl['keep'] * test_kl['benchmark_profit']
test_kl['trend_profit'].plot(figsize=(14, 7))
plt.show()
test_kl[['benchmark_profit', 'trend_profit']].cumsum().plot(grid=True,
figsize=(
14, 7))
plt.show()
test_kl[['benchmark_profit', 'trend_profit']].cumsum().apply(
np.exp).plot(grid=True)
plt.show()
# noinspection PyPep8Naming
def sample_713():
"""
7.1.3 趋势跟踪策略
:return:
"""
# rolling_max示例序列
demo_list = np.array([1, 2, 1, 1, 100, 1000])
# 对示例序列以3个为一组,寻找每一组中的最大值
from abupy import pd_rolling_max
# print('pd.rolling_max(demo_list, window=3):', pd.rolling_max(demo_list, window=3))
print('pd.rolling_max(demo_list, window=3):', pd_rolling_max(demo_list, window=3))
from abupy import pd_expanding_max
# expanding_max示例序列
demo_list = np.array([1, 2, 1, 1, 100, 1000])
# print('pd.expanding_max(demo_list):', pd.expanding_max(demo_list))
print('pd.expanding_max(demo_list):', pd_expanding_max(demo_list))
# 当天收盘价格超过N1天内最高价格作为买入信号
N1 = 42
# 当天收盘价格超过N2天内最低价格作为卖出信号
N2 = 21
# 通过rolling_max方法计算最近N1个交易日的最高价
# kl_pd['n1_high'] = pd.rolling_max(kl_pd['high'], window=N1)
kl_pd['n1_high'] = pd_rolling_max(kl_pd['high'], window=N1)
# 表7-4所示
print('kl_pd[0:5]:\n', kl_pd[0:5])
# expanding_max
# expan_max = pd.expanding_max(kl_pd['close'])
expan_max = pd_expanding_max(kl_pd['close'])
# fillna使用序列对应的expan_max
kl_pd['n1_high'].fillna(value=expan_max, inplace=True)
# 表7-5所示
print('kl_pd[0:5]:\n', kl_pd[0:5])
from abupy import pd_rolling_min, pd_expanding_min
# 通过rolling_min方法计算最近N2个交易日的最低价格
# rolling_min与rolling_max类似
# kl_pd['n2_low'] = pd.rolling_min(kl_pd['low'], window=N2)
kl_pd['n2_low'] = pd_rolling_min(kl_pd['low'], window=N2)
# expanding_min与expanding_max类似
# expan_min = pd.expanding_min(kl_pd['close'])
expan_min = pd_expanding_min(kl_pd['close'])
# fillna使用序列对应的eexpan_min
kl_pd['n2_low'].fillna(value=expan_min, inplace=True)
# 当天收盘价格超过N天内的最高价或最低价, 超过最高价格作为买入信号买入股票持有
buy_index = kl_pd[kl_pd['close'] > kl_pd['n1_high'].shift(1)].index
kl_pd.loc[buy_index, 'signal'] = 1
# 当天收盘价格超过N天内的最高价或最低价, 超过最低价格作为卖出信号
sell_index = kl_pd[kl_pd['close'] < kl_pd['n2_low'].shift(1)].index
kl_pd.loc[sell_index, 'signal'] = 0
kl_pd.signal.value_counts().plot(kind='pie', figsize=(5, 5))
plt.show()
"""
将信号操作序列移动一个单位,代表第二天再将操作信号执行,转换得到持股状态
这里不shift(1)也可以,代表信号产生当天执行,但是由于收盘价格是在收盘后
才确定的,计算突破使用了收盘价格,所以使用shift(1)更接近真实情况
"""
kl_pd['keep'] = kl_pd['signal'].shift(1)
kl_pd['keep'].fillna(method='ffill', inplace=True)
# 计算基准收益
kl_pd['benchmark_profit'] = np.log(
kl_pd['close'] / kl_pd['close'].shift(1))
# 计算使用趋势突破策略的收益
kl_pd['trend_profit'] = kl_pd['keep'] * kl_pd['benchmark_profit']
# 可视化收益的情况对比
kl_pd[['benchmark_profit', 'trend_profit']].cumsum().plot(grid=True,
figsize=(
14, 7))
plt.show()
"""
7.2 仓位控制管理¶
注意以下代码,由于有使用np.random.binomial进行随机,所以生成的数据结果与书中的会不一样
"""
def sample_722_1(show=True):
"""
7.2.2 一支股票的时间简史: 第一阶段
:return:
"""
# 这个股票第一阶段走势函数gen_stock_price_array
def gen_stock_price_array():
# 第一阶段走势涵盖股票上市后前100天走势情况
trade_day = 100
# 股票的初始价格是1元钱,即初始化100个初始价格是1元钱的np array
price_array = np.ones(trade_day)
# 以时间驱动100个交易日,生成100个交易日走势
for ind in np.arange(0, trade_day - 1):
if ind == 0:
# 第一个交易日50%的概率结果是win: win = np.random.binomial(1, 0.5)
# 第一个交易日100%的概率win
win = np.random.binomial(1, 1)
else:
# 非第一个交易日它的涨跌与只与前一天的涨跌相关,如果前一天是上涨的
# 那么它今天仍然是涨,如果它前一天是下跌的,那它今天就是跌
win = price_array[ind] > price_array[ind - 1]
if win:
# 每次上涨只能上涨5%
price_array[ind + 1] = (1 + 0.05) * price_array[ind]
else:
# 每次下跌只能下跌5%
price_array[ind + 1] = (1 - 0.05) * price_array[ind]
return price_array
# 运行两次,生成两种走势
price_array1 = gen_stock_price_array()
price_array1_ex = gen_stock_price_array()
if show:
_, axs = plt.subplots(nrows=1, ncols=2, figsize=(14, 5))
# 图7-13 左图
axs[0].plot(price_array1)
# 图7-13 右图
axs[1].plot(price_array1_ex)
plt.show()
return price_array1
def sample_722_2(show=True):
"""
7.2.2 一支股票的时间简史: 第二阶段
:return:
"""
price_array1 = sample_722_1(show=False)
# 这个股票第二阶段走势函数gen_stock_price_array2
# noinspection PyChainedComparisons
def gen_stock_price_array2():
# 第二阶段走势共覆盖了252个交易日,即一年的走势
trade_day = 252
# np.concatenate连结之前100天的走势和新的252天走势
# np.ones(trade_day) * price_array1[-1]:即新的走势使用上一阶段走势最后
# 一天的价格初始化这个252个交易日的新序列
price_array = np.concatenate(
(price_array1, np.ones(trade_day) * price_array1[-1]), axis=0)
# concatenate操作之后:price_array有352个元素
# len(price_array1) - 1:即ind 99开始时间驱动生成第二阶段的252个交易日
for ind in np.arange(len(price_array1) - 1, len(price_array) - 1):
# 获取当前交易日为基准的四个交易日数据
last4 = price_array[ind - 3:ind + 1]
if len(last4) == 4 and last4[-1] > last4[-2] and last4[-2] > last4[-3] and last4[-3] > last4[-4]:
# 连续上涨3天, 第四及之后天下跌的概率为55%
win = np.random.binomial(1, 0.45)
elif len(last4) == 4 and last4[-1] < last4[-2] and last4[-2] < last4[-3] and last4[-3] < last4[-4]:
# 连续下跌3天, 第四及之后天上涨的概率为80%
win = np.random.binomial(1, 0.8)
else:
# 涨跌与只与前一天的涨跌相关,如果前一天是上涨的,
# 那么它今天仍然是涨,如果它前一天是下跌的,那它今天就是跌
win = price_array[ind] > price_array[ind - 1]
if win:
# 每次上涨只能上涨5%
price_array[ind + 1] = (1 + 0.05) * price_array[ind]
else:
# 每次下跌只能下跌5%
price_array[ind + 1] = (1 - 0.05) * price_array[ind]
return price_array
if show:
import itertools
# 生成9个子画布 3*3
_, axs = plt.subplots(nrows=3, ncols=3, figsize=(15, 15))
# 将 3 * 3转换成一个线性list
axs_list = list(itertools.chain.from_iterable(axs))
for ax in axs_list:
# 使用gen_stock_price_array2生成9组不同的股票走势图,使用子画布绘制
ax.plot(gen_stock_price_array2())
plt.show()
price_array2 = gen_stock_price_array2()
if show:
plt.plot(price_array2)
plt.show()
return price_array2
def sample_722_3(show=True):
"""
7.2.2 一支股票的时间简史: 第三阶段
:return:
"""
price_array2 = sample_722_2(show=False)
# 这个股票第三阶段走势函数gen_stock_price_array3
def gen_stock_price_array3():
trade_day = 252 * 3
# np.concatenate连结之前352天的走势和新的交易日走势
# np.ones(trade_day) * price_array2[-1]:即新的走势使用上一阶段走势最后
# 一天的价格初始化len(trade_day)个交易日的新序列
price_array = np.concatenate(
(price_array2, np.ones(trade_day) * price_array2[-1]), axis=0)
# concatenate操作之后:price_array352+len(trade_day)个元素
# len(price_array2) - 1:即从ind 351开始时间驱动生成第三阶段的交易日数据
for ind in np.arange(len(price_array2) - 1, len(price_array) - 1):
# 获取当前交易日为基准的四个交易日数据
last4 = price_array[ind - 3:ind + 1]
# noinspection PyChainedComparisons
if len(last4) == 4 and last4[-1] >= last4[-2] \
and last4[-2] >= last4[-3] and last4[-3] >= last4[-4]:
# 连续上涨3天, 第四及之后天下跌的概率为55%
win = np.random.binomial(1, 0.45)
elif len(last4) == 4 and last4[-1] < last4[-2] \
and last4[-2] < last4[-3] and last4[-3] < last4[-4]:
# 连续下跌3天, 第四及之后上涨的概率为80%
win = np.random.binomial(1, 0.8)
if not win:
# 发生了灾难性的股价下跌,股价下跌50%
price_array[ind + 1] = (1 - 0.50) * price_array[ind]
# 直接continue了
continue
else:
# 涨跌与只与前一天的涨跌相关,如果前一天是上涨的
# 那么它今天仍然是涨,如果它前一天是下跌的,那它今天就是跌
win = price_array[ind] >= price_array[ind - 1]
if win:
# 每次上涨只能上涨5%
price_array[ind + 1] = (1 + 0.05) * price_array[ind]
else:
# 每次下跌只能下跌5%
price_array[ind + 1] = (1 - 0.05) * price_array[ind]
# 股价小于0.1元股价归0,即退市
if price_array[ind + 1] <= 0.1:
price_array[ind + 1:] = 0
# 退市
break
return price_array
# price_array3即为第三阶段股票走势
price_array3 = gen_stock_price_array3()
if show:
plt.plot(price_array3)
plt.show()
return price_array3
"""
7.2.3 三只小猪股票投资的故事
"""
def sample_723():
"""
7.2.3 三只小猪股票投资的故事
:return:
"""
price_array3 = sample_722_3(show=False)
# noinspection PyChainedComparisons,PyShadowingNames
def execute_trade(cash, buy_rate):
commission = 5 # 手续费
stock_cnt = 0 # 持有股票数
keep_day = 0 # 持股天数
# 资产结果序列
capital = []
# 从第353天开始,即从index 353开始直到最后一天
for ind in np.arange(352, len(price_array3) - 1):
if stock_cnt > 0:
# 如果持有股票,增加持股天数
keep_day += 1
if stock_cnt > 0 and keep_day == 3:
# 当连续持有股票三天后卖出股票
cash += price_array3[ind] * stock_cnt
cash -= commission # 手续费
if cash <= 0:
# 如果没钱了,一切就都结束了
capital.append(0)
print('爆仓了!')
break
# 卖出后重置持股天数和持有股票数量
keep_day = 0
stock_cnt = 0
# 获取当前交易日为基准5个交易日数据,5个交易日价格->4个交易日的涨跌情况
last5 = price_array3[ind - 4:ind + 1]
# 买入条件:
# example: last5 = [82.4 86.5 82.2 78.1 74.2]
# 1. 没持有股票:stock_cnt == 0
# 2. last5序列last5[1] > last5[0] 86.5 > 82.4, 即第一个交易日上涨
# 3. last5序列后三个交易日连续下跌[-1]<[-2],[-2]<[-3],[-3]<[-4]
if stock_cnt == 0 and len(last5) == 5 \
and last5[1] > last5[0] \
and last5[-1] < last5[-2] and last5[-2] < last5[-3] and last5[-3] < last5[-4]:
cash -= commission # 手续费
# 按照资金仓位管理buy_rate买入
buy_cash = (cash * buy_rate)
cash -= buy_cash
stock_cnt += buy_cash / price_array3[ind]
if stock_cnt < 1:
# 如果没钱了,一切就都结束了
capital.append(0)
print('爆仓了!')
break
keep_day = 0
# 资产结果序列加入当日结果
capital.append(cash + (stock_cnt * price_array3[ind]))
return capital
pig_one_cash = 10000
# 1.0全仓买入
buy_rate = 1.0
pig_one_capital = execute_trade(pig_one_cash, buy_rate)
print('猪老大最终资产:{}'.format(pig_one_capital[-1]))
print('猪老大资产最高峰值:{}'.format(max(pig_one_capital)))
plt.plot(pig_one_capital)
plt.show()
pig_two_cash = 10000
# fwin0.8 -floss0.2 = 0.6 60%仓位买入
buy_rate = 0.8 - 0.2
pig_two_capital = execute_trade(pig_two_cash, buy_rate)
print('猪老二最终资产:{}'.format(pig_two_capital[-1]))
print('猪老二资产最高峰值:{}'.format(max(pig_two_capital)))
plt.plot(pig_two_capital)
plt.show()
pig_three_cash = 10000
# 最终buy_rate0.13即13%仓位
buy_rate = 0.8 - 0.2 / (0.15 / 0.5)
pig_three_capital = execute_trade(pig_three_cash, buy_rate)
print('猪老三最终资产:{}'.format(pig_three_capital[-1]))
print('猪老三资产最高峰值:{}'.format(max(pig_three_capital)))
plt.plot(pig_three_capital)
plt.show()
if __name__ == "__main__":
sample_711()
# sample_712_1()
# sample_712_2()
# sample_712_3()
# sample_712_4()
# sample_713()
# sample_722_1()
# sample_722_2()
# sample_722_3()
# sample_723()
+405
View File
@@ -0,0 +1,405 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
import warnings
# noinspection PyUnresolvedReferences
import abu_local_env
import abupy
from abupy import AbuFactorBuyBreak
from abupy import AbuFactorSellBreak
from abupy import AbuFactorAtrNStop
from abupy import AbuFactorPreAtrNStop
from abupy import AbuFactorCloseAtrNStop
from abupy import AbuBenchmark
from abupy import AbuPickTimeWorker
from abupy import AbuCapital
from abupy import AbuKLManager
from abupy import ABuTradeProxy
from abupy import ABuTradeExecute
from abupy import ABuPickTimeExecute
from abupy import AbuMetricsBase
from abupy import ABuMarket
from abupy import AbuPickTimeMaster
from abupy import ABuRegUtil
from abupy import AbuPickRegressAngMinMax
from abupy import AbuPickStockWorker
from abupy import ABuPickStockExecute
from abupy import AbuPickStockPriceMinMax
from abupy import AbuPickStockMaster
warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
# 使用沙盒数据,目的是和书中一样的数据环境
abupy.env.enable_example_env_ipython()
"""
第八章 量化系统——开发
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
"""
def sample_811():
"""
8.1.1 买入因子的实现
:return:
"""
# buy_factors 60日向上突破,42日向上突破两个因子
buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak},
{'xd': 42, 'class': AbuFactorBuyBreak}]
benchmark = AbuBenchmark()
capital = AbuCapital(1000000, benchmark)
kl_pd_manager = AbuKLManager(benchmark, capital)
# 获取TSLA的交易数据
kl_pd = kl_pd_manager.get_pick_time_kl_pd('usTSLA')
abu_worker = AbuPickTimeWorker(capital, kl_pd, benchmark, buy_factors, None)
abu_worker.fit()
orders_pd, action_pd, _ = ABuTradeProxy.trade_summary(abu_worker.orders, kl_pd, draw=True)
ABuTradeExecute.apply_action_to_capital(capital, action_pd, kl_pd_manager)
capital.capital_pd.capital_blance.plot()
plt.show()
def sample_812():
"""
8.1.2 卖出因子的实现
:return:
"""
# 120天向下突破为卖出信号
sell_factor1 = {'xd': 120, 'class': AbuFactorSellBreak}
# 趋势跟踪策略止盈要大于止损设置值,这里0.5,3.0
sell_factor2 = {'stop_loss_n': 0.5, 'stop_win_n': 3.0, 'class': AbuFactorAtrNStop}
# 暴跌止损卖出因子形成dict
sell_factor3 = {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.0}
# 保护止盈卖出因子组成dict
sell_factor4 = {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}
# 四个卖出因子同时生效,组成sell_factors
sell_factors = [sell_factor1, sell_factor2, sell_factor3, sell_factor4]
# buy_factors 60日向上突破,42日向上突破两个因子
buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak},
{'xd': 42, 'class': AbuFactorBuyBreak}]
benchmark = AbuBenchmark()
capital = AbuCapital(1000000, benchmark)
orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_same_factors(
['usTSLA'], benchmark, buy_factors, sell_factors, capital, show=True)
def sample_813():
"""
8.1.3 滑点买入卖出价格确定及策略实现
:return:
"""
from abupy import AbuSlippageBuyBase
# 修改g_open_down_rate的值为0.02
g_open_down_rate = 0.02
# noinspection PyClassHasNoInit
class AbuSlippageBuyMean2(AbuSlippageBuyBase):
def fit_price(self):
if (self.kl_pd_buy.open / self.kl_pd_buy.pre_close) < (
1 - g_open_down_rate):
# 开盘下跌K_OPEN_DOWN_RATE以上,单子失效
print(self.factor_name + 'open down threshold')
return np.inf
# 买入价格为当天均价
self.buy_price = np.mean(
[self.kl_pd_buy['high'], self.kl_pd_buy['low']])
return self.buy_price
# 只针对60使用AbuSlippageBuyMean2
buy_factors2 = [{'slippage': AbuSlippageBuyMean2, 'xd': 60, 'class': AbuFactorBuyBreak},
{'xd': 42, 'class': AbuFactorBuyBreak}]
sell_factor1 = {'xd': 120, 'class': AbuFactorSellBreak}
sell_factor2 = {'stop_loss_n': 0.5, 'stop_win_n': 3.0, 'class': AbuFactorAtrNStop}
sell_factor3 = {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.0}
sell_factor4 = {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}
sell_factors = [sell_factor1, sell_factor2, sell_factor3, sell_factor4]
benchmark = AbuBenchmark()
capital = AbuCapital(1000000, benchmark)
orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_same_factors(
['usTSLA'], benchmark, buy_factors2, sell_factors, capital, show=True)
def sample_814(show=True):
"""
8.1.4 对多支股票进行择时
:return:
"""
sell_factor1 = {'xd': 120, 'class': AbuFactorSellBreak}
sell_factor2 = {'stop_loss_n': 0.5, 'stop_win_n': 3.0, 'class': AbuFactorAtrNStop}
sell_factor3 = {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.0}
sell_factor4 = {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}
sell_factors = [sell_factor1, sell_factor2, sell_factor3, sell_factor4]
benchmark = AbuBenchmark()
buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak},
{'xd': 42, 'class': AbuFactorBuyBreak}]
choice_symbols = ['usTSLA', 'usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG', 'usWUBA', 'usVIPS']
capital = AbuCapital(1000000, benchmark)
orders_pd, action_pd, all_fit_symbols_cnt = ABuPickTimeExecute.do_symbols_with_same_factors(choice_symbols,
benchmark, buy_factors,
sell_factors, capital,
show=False)
metrics = AbuMetricsBase(orders_pd, action_pd, capital, benchmark)
metrics.fit_metrics()
if show:
print('orders_pd[:10]:\n', orders_pd[:10].filter(
['symbol', 'buy_price', 'buy_cnt', 'buy_factor', 'buy_pos', 'sell_date', 'sell_type_extra', 'sell_type',
'profit']))
print('action_pd[:10]:\n', action_pd[:10])
metrics.plot_returns_cmp(only_show_returns=True)
return metrics
def sample_815():
"""
8.1.5 自定义仓位管理策略的实现
:return:
"""
metrics = sample_814(False)
print('\nmetrics.gains_mean:{}, -metrics.losses_mean:{}'.format(metrics.gains_mean, -metrics.losses_mean))
from abupy import AbuKellyPosition
# 42d使用AbuKellyPosition60d仍然使用默认仓位管理类
buy_factors2 = [{'xd': 60, 'class': AbuFactorBuyBreak},
{'xd': 42, 'position': AbuKellyPosition, 'win_rate': metrics.win_rate,
'gains_mean': metrics.gains_mean, 'losses_mean': -metrics.losses_mean,
'class': AbuFactorBuyBreak}]
sell_factor1 = {'xd': 120, 'class': AbuFactorSellBreak}
sell_factor2 = {'stop_loss_n': 0.5, 'stop_win_n': 3.0, 'class': AbuFactorAtrNStop}
sell_factor3 = {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.0}
sell_factor4 = {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}
sell_factors = [sell_factor1, sell_factor2, sell_factor3, sell_factor4]
benchmark = AbuBenchmark()
choice_symbols = ['usTSLA', 'usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG', 'usWUBA', 'usVIPS']
capital = AbuCapital(1000000, benchmark)
orders_pd, action_pd, all_fit_symbols_cnt = ABuPickTimeExecute.do_symbols_with_same_factors(choice_symbols,
benchmark, buy_factors2,
sell_factors, capital,
show=False)
print(orders_pd[:10].filter(['symbol', 'buy_cnt', 'buy_factor', 'buy_pos']))
def sample_816():
"""
8.1.6 多支股票使用不同的因子进行择时
:return:
"""
# 选定noah和sfun
target_symbols = ['usSFUN', 'usNOAH']
# 针对sfun只使用42d向上突破作为买入因子
buy_factors_sfun = [{'xd': 42, 'class': AbuFactorBuyBreak}]
# 针对sfun只使用60d向下突破作为卖出因子
sell_factors_sfun = [{'xd': 60, 'class': AbuFactorSellBreak}]
# 针对noah只使用21d向上突破作为买入因子
buy_factors_noah = [{'xd': 21, 'class': AbuFactorBuyBreak}]
# 针对noah只使用42d向下突破作为卖出因子
sell_factors_noah = [{'xd': 42, 'class': AbuFactorSellBreak}]
factor_dict = dict()
# 构建SFUN独立的buy_factorssell_factors的dict
factor_dict['usSFUN'] = {'buy_factors': buy_factors_sfun, 'sell_factors': sell_factors_sfun}
# 构建NOAH独立的buy_factorssell_factors的dict
factor_dict['usNOAH'] = {'buy_factors': buy_factors_noah, 'sell_factors': sell_factors_noah}
# 初始化资金
benchmark = AbuBenchmark()
capital = AbuCapital(1000000, benchmark)
# 使用do_symbols_with_diff_factors执行
orders_pd, action_pd, all_fit_symbols = ABuPickTimeExecute.do_symbols_with_diff_factors(
target_symbols, benchmark, factor_dict, capital)
print('pd.crosstab(orders_pd.buy_factor, orders_pd.symbol):\n', pd.crosstab(orders_pd.buy_factor, orders_pd.symbol))
def sample_817():
"""
8.1.7 使用并行来提升择时运行效率
:return:
"""
# 要关闭沙盒数据环境,因为沙盒里就那几个股票的历史数据, 下面要随机做50个股票
from abupy import EMarketSourceType
abupy.env.g_market_source = EMarketSourceType.E_MARKET_SOURCE_tx
abupy.env.disable_example_env_ipython()
# 关闭沙盒后,首先基准要从非沙盒环境换取,否则数据对不齐,无法正常运行
benchmark = AbuBenchmark()
# 当传入choice_symbols为None时代表对整个市场的所有股票进行回测
# noinspection PyUnusedLocal
choice_symbols = None
# 顺序获取市场后300支股票
# noinspection PyUnusedLocal
choice_symbols = ABuMarket.all_symbol()[-50:]
# 随机获取300支股票
choice_symbols = ABuMarket.choice_symbols(50)
capital = AbuCapital(1000000, benchmark)
sell_factor1 = {'xd': 120, 'class': AbuFactorSellBreak}
sell_factor2 = {'stop_loss_n': 0.5, 'stop_win_n': 3.0, 'class': AbuFactorAtrNStop}
sell_factor3 = {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.0}
sell_factor4 = {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}
sell_factors = [sell_factor1, sell_factor2, sell_factor3, sell_factor4]
buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak},
{'xd': 42, 'class': AbuFactorBuyBreak}]
orders_pd, action_pd, _ = AbuPickTimeMaster.do_symbols_with_same_factors_process(
choice_symbols, benchmark, buy_factors, sell_factors,
capital)
metrics = AbuMetricsBase(orders_pd, action_pd, capital, benchmark)
metrics.fit_metrics()
metrics.plot_returns_cmp(only_show_returns=True)
abupy.env.enable_example_env_ipython()
"""
注意所有选股结果等等与书中的结果不一致,因为要控制沙盒数据体积小于50mb, 所以沙盒数据有些symbol只有两年多一点,与原始环境不一致,
直接达不到选股的min_xd,所以这里其实可以`abupy.env.disable_example_env_ipython()`关闭沙盒环境,直接上真实数据。
"""
def sample_821_1():
"""
8.2.1_1 选股使用示例
:return:
"""
# 选股条件threshold_ang_min=0.0, 即要求股票走势为向上上升趋势
stock_pickers = [{'class': AbuPickRegressAngMinMax,
'threshold_ang_min': 0.0, 'reversed': False}]
# 从这几个股票里进行选股,只是为了演示方便
# 一般的选股都会是数量比较多的情况比如全市场股票
choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG',
'usTSLA', 'usWUBA', 'usVIPS']
benchmark = AbuBenchmark()
capital = AbuCapital(1000000, benchmark)
kl_pd_manager = AbuKLManager(benchmark, capital)
stock_pick = AbuPickStockWorker(capital, benchmark, kl_pd_manager,
choice_symbols=choice_symbols,
stock_pickers=stock_pickers)
stock_pick.fit()
# 打印最后的选股结果
print('stock_pick.choice_symbols:', stock_pick.choice_symbols)
# 从kl_pd_manager缓存中获取选股走势数据,注意get_pick_stock_kl_pd为选股数据,get_pick_time_kl_pd为择时
kl_pd_noah = kl_pd_manager.get_pick_stock_kl_pd('usNOAH')
# 绘制并计算角度
deg = ABuRegUtil.calc_regress_deg(kl_pd_noah.close)
print('noah 选股周期内角度={}'.format(round(deg, 3)))
def sample_821_2():
"""
8.2.1_2 ABuPickStockExecute
:return:
"""
stock_pickers = [{'class': AbuPickRegressAngMinMax,
'threshold_ang_min': 0.0, 'threshold_ang_max': 10.0,
'reversed': False}]
choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG',
'usTSLA', 'usWUBA', 'usVIPS']
benchmark = AbuBenchmark()
capital = AbuCapital(1000000, benchmark)
kl_pd_manager = AbuKLManager(benchmark, capital)
print('ABuPickStockExecute.do_pick_stock_work:\n', ABuPickStockExecute.do_pick_stock_work(choice_symbols, benchmark,
capital, stock_pickers))
kl_pd_sfun = kl_pd_manager.get_pick_stock_kl_pd('usSFUN')
print('sfun 选股周期内角度={}'.format(round(ABuRegUtil.calc_regress_deg(kl_pd_sfun.close), 3)))
def sample_821_3():
"""
8.2.1_3 reversed
:return:
"""
# 和上面的代码唯一的区别就是reversed=True
stock_pickers = [{'class': AbuPickRegressAngMinMax,
'threshold_ang_min': 0.0, 'threshold_ang_max': 10.0, 'reversed': True}]
choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG',
'usTSLA', 'usWUBA', 'usVIPS']
benchmark = AbuBenchmark()
capital = AbuCapital(1000000, benchmark)
print('ABuPickStockExecute.do_pick_stock_work:\n',
ABuPickStockExecute.do_pick_stock_work(choice_symbols, benchmark, capital, stock_pickers))
def sample_822():
"""
8.2.2 多个选股因子并行执行
:return:
"""
# 选股list使用两个不同的选股因子组合,并行同时生效
stock_pickers = [{'class': AbuPickRegressAngMinMax,
'threshold_ang_min': 0.0, 'reversed': False},
{'class': AbuPickStockPriceMinMax, 'threshold_price_min': 50.0,
'reversed': False}]
choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG',
'usTSLA', 'usWUBA', 'usVIPS']
benchmark = AbuBenchmark()
capital = AbuCapital(1000000, benchmark)
print('ABuPickStockExecute.do_pick_stock_work:\n',
ABuPickStockExecute.do_pick_stock_work(choice_symbols, benchmark, capital, stock_pickers))
def sample_823():
"""
8.2.3 使用并行来提升回测运行效率
:return:
"""
from abupy import EMarketSourceType
abupy.env.g_market_source = EMarketSourceType.E_MARKET_SOURCE_tx
abupy.env.disable_example_env_ipython()
benchmark = AbuBenchmark()
capital = AbuCapital(1000000, benchmark)
# 首先随抽取50支股票
choice_symbols = ABuMarket.choice_symbols(50)
# 股价在15-50之间
stock_pickers = [
{'class': AbuPickStockPriceMinMax, 'threshold_price_min': 15.0,
'threshold_price_max': 50.0, 'reversed': False}]
cs = AbuPickStockMaster.do_pick_stock_with_process(capital, benchmark,
stock_pickers,
choice_symbols)
print('len(cs):', len(cs))
print('cs:\n', cs)
if __name__ == "__main__":
sample_811()
# sample_812()
# sample_813()
# sample_814()
# sample_815()
# sample_816()
# sample_817()
# sample_821_1()
# sample_821_2()
# sample_821_3()
# sample_822()
# sample_823()
+487
View File
@@ -0,0 +1,487 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import warnings
# noinspection PyUnresolvedReferences
import abu_local_env
import abupy
from abupy import AbuMetricsBase
from abupy import AbuFactorBuyBreak
from abupy import AbuFactorAtrNStop
from abupy import AbuFactorPreAtrNStop
from abupy import AbuFactorCloseAtrNStop
# run_loop_back等一些常用且最外层的方法定义在abu中
from abupy import abu
warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
# 使用沙盒数据,目的是和书中一样的数据环境
abupy.env.enable_example_env_ipython()
# 设置选股因子,None为不使用选股因子
stock_pickers = None
# 买入因子依然延用向上突破因子
buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak},
{'xd': 42, 'class': AbuFactorBuyBreak}]
# 卖出因子继续使用上一章使用的因子
sell_factors = [
{'stop_loss_n': 1.0, 'stop_win_n': 3.0,
'class': AbuFactorAtrNStop},
{'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.5},
{'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}
]
"""
第九章 量化系统——度量与优化
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
"""
def sample_91(show=True):
"""
9.1 度量的基本使用方法
:return:
"""
# 设置初始资金数
read_cash = 1000000
# 择时股票池
choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG',
'usTSLA', 'usWUBA', 'usVIPS']
# 使用run_loop_back运行策略
abu_result_tuple, kl_pd_manager = abu.run_loop_back(read_cash,
buy_factors,
sell_factors,
stock_pickers,
choice_symbols=choice_symbols, n_folds=2)
metrics = AbuMetricsBase(*abu_result_tuple)
metrics.fit_metrics()
if show:
metrics.plot_returns_cmp()
return metrics
def sample_922():
"""
9.2.2 度量的可视化
:return:
"""
metrics = sample_91(show=False)
metrics.plot_sharp_volatility_cmp()
plt.show()
def sharpe(rets, ann=252):
return rets.mean() / rets.std() * np.sqrt(ann)
print('策略sharpe值计算为={}'.format(sharpe(metrics.algorithm_returns)))
metrics.plot_effect_mean_day()
plt.show()
metrics.plot_keep_days()
plt.show()
metrics.plot_sell_factors()
plt.show()
metrics.plot_max_draw_down()
plt.show()
"""
9.3 基于grid search寻找因子最优参数
"""
stop_win_range = np.arange(2.0, 4.5, 0.5)
stop_loss_range = np.arange(0.5, 2, 0.5)
sell_atr_nstop_factor_grid = {
'class': [AbuFactorAtrNStop],
'stop_loss_n': stop_loss_range,
'stop_win_n': stop_win_range
}
close_atr_range = np.arange(1.0, 4.0, 0.5)
pre_atr_range = np.arange(1.0, 3.5, 0.5)
sell_atr_pre_factor_grid = {
'class': [AbuFactorPreAtrNStop],
'pre_atr_n': pre_atr_range
}
sell_atr_close_factor_grid = {
'class': [AbuFactorCloseAtrNStop],
'close_atr_n': close_atr_range
}
def sample_931():
"""
9.3.1 参数取值范围
:return:
"""
print('止盈参数stop_win_n设置范围:{}'.format(stop_win_range))
print('止损参数stop_loss_n设置范围:{}'.format(stop_loss_range))
print('暴跌保护止损参数pre_atr_n设置范围:{}'.format(pre_atr_range))
print('盈利保护止盈参数close_atr_n设置范围:{}'.format(close_atr_range))
def sample_932(show=True):
"""
9.3.2 参数进行排列组合
:return:
"""
from abupy import ABuGridHelper
sell_factors_product = ABuGridHelper.gen_factor_grid(
ABuGridHelper.K_GEN_FACTOR_PARAMS_SELL,
[sell_atr_nstop_factor_grid, sell_atr_pre_factor_grid, sell_atr_close_factor_grid])
if show:
print('卖出因子参数共有{}种组合方式'.format(len(sell_factors_product)))
print('卖出因子组合0形式为{}'.format(sell_factors_product[0]))
buy_bk_factor_grid1 = {
'class': [AbuFactorBuyBreak],
'xd': [42]
}
buy_bk_factor_grid2 = {
'class': [AbuFactorBuyBreak],
'xd': [60]
}
buy_factors_product = ABuGridHelper.gen_factor_grid(
ABuGridHelper.K_GEN_FACTOR_PARAMS_BUY, [buy_bk_factor_grid1, buy_bk_factor_grid2])
if show:
print('买入因子参数共有{}种组合方式'.format(len(buy_factors_product)))
print('买入因子组合形式为{}'.format(buy_factors_product))
return sell_factors_product, buy_factors_product
def sample_933():
"""
9.3.3 GridSearch寻找最优参数
:return:
"""
from abupy import GridSearch
read_cash = 1000000
choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG',
'usTSLA', 'usWUBA', 'usVIPS']
sell_factors_product, buy_factors_product = sample_932(show=False)
grid_search = GridSearch(read_cash, choice_symbols,
buy_factors_product=buy_factors_product,
sell_factors_product=sell_factors_product)
from abupy import ABuFileUtil
"""
注意下面的运行耗时大约1小时多,如果所有cpu都用上的话,也可以设置n_jobs为 < cpu进程数,一边做其它的一边跑
"""
# 运行GridSearch n_jobs=-1启动cpu个数的进程数
scores, score_tuple_array = grid_search.fit(n_jobs=-1)
"""
针对运行完成输出的score_tuple_array可以使用dump_pickle保存在本地,以方便修改其它验证效果。
"""
ABuFileUtil.dump_pickle(score_tuple_array, '../gen/score_tuple_array')
print('组合因子参数数量{}'.format(len(buy_factors_product) * len(sell_factors_product)))
print('最终评分结果数量{}'.format(len(scores)))
best_score_tuple_grid = grid_search.best_score_tuple_grid
AbuMetricsBase.show_general(best_score_tuple_grid.orders_pd, best_score_tuple_grid.action_pd,
best_score_tuple_grid.capital, best_score_tuple_grid.benchmark)
def sample_934():
"""
9.3.4 度量结果的评分
:return:
"""
from abupy import ABuFileUtil
score_fn = '../gen/score_tuple_array'
if not ABuFileUtil.file_exist(score_fn):
print('../gen/score_tuple_array not exist! please execute sample_933 first!')
return
"""
直接读取本地序列化文件
"""
score_tuple_array = ABuFileUtil.load_pickle(score_fn)
from abupy import WrsmScorer
# 实例化一个评分类WrsmScorer,它的参数为之前GridSearch返回的score_tuple_array对象
scorer = WrsmScorer(score_tuple_array)
print('scorer.score_pd.tail():\n', scorer.score_pd.tail())
# score_tuple_array[658]与grid_search.best_score_tuple_grid是一致的
sfs = scorer.fit_score()
# 打印前15个高分组合
print('sfs[::-1][:15]:\n', sfs[::-1][:15])
def sample_935_1():
"""
9.3.5_1 不同权重的评分: 只考虑投资回报来评分
:return:
"""
from abupy import ABuFileUtil
score_fn = '../gen/score_tuple_array'
if not ABuFileUtil.file_exist(score_fn):
print('../gen/score_tuple_array not exist! please execute sample_933 first!')
return
"""
直接读取本地序列化文件
"""
score_tuple_array = ABuFileUtil.load_pickle(score_fn)
from abupy import WrsmScorer
# 实例化WrsmScorer,参数weights,只有第二项为1,其他都是0,
# 代表只考虑投资回报来评分
scorer = WrsmScorer(score_tuple_array, weights=[0, 1, 0, 0])
# 返回排序后的队列
scorer_returns_max = scorer.fit_score()
# 因为是倒序排序,所以index最后一个为最优参数
best_score_tuple_grid = score_tuple_array[scorer_returns_max.index[-1]]
# 由于篇幅,最优结果只打印文字信息
AbuMetricsBase.show_general(best_score_tuple_grid.orders_pd,
best_score_tuple_grid.action_pd,
best_score_tuple_grid.capital,
best_score_tuple_grid.benchmark,
only_info=True)
# 最后打印出只考虑投资回报下最优结果使用的买入策略和卖出策略
print('best_score_tuple_grid.buy_factors, best_score_tuple_grid.sell_factors:\n', best_score_tuple_grid.buy_factors,
best_score_tuple_grid.sell_factors)
def sample_935_2():
"""
9.3.5_2 不同权重的评分: 只考虑胜率
:return:
"""
from abupy import ABuFileUtil
score_fn = '../gen/score_tuple_array'
if not ABuFileUtil.file_exist(score_fn):
print('../gen/score_tuple_array not exist! please execute sample_933 first!')
return
"""
直接读取本地序列化文件
"""
score_tuple_array = ABuFileUtil.load_pickle(score_fn)
from abupy import WrsmScorer
# 只有第一项为1,其他都是0代表只考虑胜率来评分
scorer = WrsmScorer(score_tuple_array, weights=[1, 0, 0, 0])
# 返回按照评分排序后的队列
scorer_returns_max = scorer.fit_score()
# index[-1]为最优参数序号
best_score_tuple_grid = score_tuple_array[scorer_returns_max.index[-1]]
AbuMetricsBase.show_general(best_score_tuple_grid.orders_pd,
best_score_tuple_grid.action_pd,
best_score_tuple_grid.capital,
best_score_tuple_grid.benchmark,
only_info=False)
# 最后打印出只考虑胜率下最优结果使用的买入策略和卖出策略
print('best_score_tuple_grid.buy_factors, best_score_tuple_grid.sell_factors:\n', best_score_tuple_grid.buy_factors,
best_score_tuple_grid.sell_factors)
"""
9.4 资金限制对度量的影响
如下内容不能使用沙盒环境, 建议对照阅读:
abu量化文档-第十九节 数据源
第20节 美股交易UMP决策
"""
def sample_94_1():
"""
9.4_1 下载市场中所有股票的6年数据,
如果没有运行过abu量化文档-第十九节 数据源:中使用腾讯数据源进行数据更新,需要运行
如果运行过就不要重复运行了:
"""
from abupy import EMarketTargetType, EMarketSourceType, EDataCacheType
# 关闭沙盒数据环境
abupy.env.disable_example_env_ipython()
abupy.env.g_market_source = EMarketSourceType.E_MARKET_SOURCE_tx
abupy.env.g_data_cache_type = EDataCacheType.E_DATA_CACHE_CSV
# 首选这里预下载市场中所有股票的6年数据(做5年回测,需要预先下载6年数据)
abu.run_kl_update(start='2011-08-08', end='2017-08-08', market=EMarketTargetType.E_MARKET_TARGET_US)
def sample_94_2(from_cache=False):
"""
9.4_2 使用切割训练集测试集模式,且生成交易特征,回测训练集交易数据, mac pro顶配大概下面跑了4个小时
:return:
"""
# 关闭沙盒数据环境
abupy.env.disable_example_env_ipython()
from abupy import EMarketDataFetchMode
# 因为sample_94_1下载了预先数据,使用缓存,设置E_DATA_FETCH_FORCE_LOCAL
abupy.env.g_data_fetch_mode = EMarketDataFetchMode.E_DATA_FETCH_FORCE_LOCAL
# 回测生成买入时刻特征
abupy.env.g_enable_ml_feature = True
# 回测将symbols切割分为训练集数据和测试集数据
abupy.env.g_enable_train_test_split = True
# 下面设置回测时切割训练集,测试集使用的切割比例参数,默认为10,即切割为10份,9份做为训练,1份做为测试,
# 由于美股股票数量多,所以切割分为4份,3份做为训练集,1份做为测试集
abupy.env.g_split_tt_n_folds = 4
from abupy import EStoreAbu
if from_cache:
abu_result_tuple = \
abu.load_abu_result_tuple(n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='train_us')
else:
# 初始化资金200万,资金管理依然使用默认atr
read_cash = 5000000
# 每笔交易的买入基数资金设置为万分之15
abupy.beta.atr.g_atr_pos_base = 0.0015
# 使用run_loop_back运行策略,因子使用和之前一样,
# choice_symbols=None为全市场回测,5年历史数据回测
# 不同电脑运行速度差异大,mac pro顶配大概下面跑了4小时
# choice_symbols=None为全市场回测,5年历史数据回测
abu_result_tuple, _ = abu.run_loop_back(read_cash,
buy_factors, sell_factors,
stock_pickers,
choice_symbols=None,
start='2012-08-08', end='2017-08-08')
# 把运行的结果保存在本地,以便之后分析回测使用,保存回测结果数据代码如下所示
abu.store_abu_result_tuple(abu_result_tuple, n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='train_us')
print('abu_result_tuple.action_pd.deal.value_counts():\n', abu_result_tuple.action_pd.deal.value_counts())
metrics = AbuMetricsBase(*abu_result_tuple)
metrics.fit_metrics()
metrics.plot_returns_cmp(only_show_returns=True)
def sample_94_3(from_cache=False, show=True):
"""
9.4_3 使用切割好的测试数据集快,mac pro顶配大概下面跑了半个小时
:return:
"""
# 关闭沙盒数据环境
abupy.env.disable_example_env_ipython()
from abupy import EMarketDataFetchMode
# 因为sample_94_1下载了预先数据,使用缓存,设置E_DATA_FETCH_FORCE_LOCAL
abupy.env.g_data_fetch_mode = EMarketDataFetchMode.E_DATA_FETCH_FORCE_LOCAL
abupy.env.g_enable_train_test_split = False
# 使用切割好的测试数据
abupy.env.g_enable_last_split_test = True
# 回测生成买入时刻特征
abupy.env.g_enable_ml_feature = True
from abupy import EStoreAbu
if from_cache:
abu_result_tuple_test = \
abu.load_abu_result_tuple(n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='test_us')
else:
read_cash = 5000000
abupy.beta.atr.g_atr_pos_base = 0.007
choice_symbols = None
abu_result_tuple_test, kl_pd_manager_test = abu.run_loop_back(read_cash,
buy_factors, sell_factors, stock_pickers,
choice_symbols=choice_symbols, start='2012-08-08',
end='2017-08-08')
abu.store_abu_result_tuple(abu_result_tuple_test, n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='test_us')
print('abu_result_tuple_test.action_pd.deal.value_counts():\n', abu_result_tuple_test.action_pd.deal.value_counts())
metrics = AbuMetricsBase(*abu_result_tuple_test)
metrics.fit_metrics()
if show:
metrics.plot_returns_cmp(only_show_returns=True)
return metrics
def sample_94_4(from_cache=False):
"""
满仓乘数
9.4_4 《量化交易之路》中通过把初始资金扩大到非常大,但是每笔交易的买入基数却不增高,来使交易全部都成交,
再使用满仓乘数的示例,由于需要再次进行全市场回测,比较耗时。
下面直接示例通过AbuMetricsBase中的transform_to_full_rate_factor接口将之前的回测结果转换为使用大初始资金回测的结果
:return:
"""
metrics_test = sample_94_3(from_cache=True, show=False)
from abupy import EStoreAbu
if from_cache:
test_us_fr = abu.load_abu_result_tuple(n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='test_us_full_rate')
# 本地读取后使用AbuMetricsBase构造度量对象,参数enable_stocks_full_rate_factor=True, 即使用满仓乘数
test_frm = AbuMetricsBase(test_us_fr.orders_pd, test_us_fr.action_pd, test_us_fr.capital, test_us_fr.benchmark,
enable_stocks_full_rate_factor=True)
test_frm.fit_metrics()
else:
test_frm = metrics_test.transform_to_full_rate_factor(n_process_kl=4, show=False)
# 转换后保存起来,下次直接读取,不用再转换了
from abupy import AbuResultTuple
test_us_fr = AbuResultTuple(test_frm.orders_pd, test_frm.action_pd, test_frm.capital, test_frm.benchmark)
abu.store_abu_result_tuple(test_us_fr, n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='test_us_full_rate')
"""
使用test_frm进行度量结果可以看到所有交易都顺利成交了,策略买入成交比例:100.0000%,但资金利用率显然过低,
它导致基准收益曲线和策略收益曲线不在一个量级上,无法有效的进行对比
"""
AbuMetricsBase.show_general(test_frm.orders_pd,
test_frm.action_pd, test_frm.capital, test_frm.benchmark, only_show_returns=True)
"""转换出来的test_frm即是一个使用满仓乘数的度量对象,下面使用test_frm直接进行满仓度量即可"""
print(type(test_frm))
test_frm.plot_returns_cmp(only_show_returns=True)
# 如果不需要与基准进行对比,最简单的方式是使用plot_order_returns_cmp
metrics_test.plot_order_returns_cmp()
"""
其它市场的回测, A股市场回测全局设置
请阅读abu量化文档相关章节
"""
if __name__ == "__main__":
sample_91()
# sample_922()
# sample_931()
# sample_932()
# 耗时操作
# sample_933()
# sample_934()
# sample_935_1()
# sample_935_2()
# sample_94_1()
# sample_94_2()
# sample_94_2(from_cache=True)
# sample_94_3()
# sample_94_3(from_cache=True)
# sample_94_4()
# sample_94_4(from_cache=True)
+108
View File
@@ -0,0 +1,108 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
import seaborn as sns
import warnings
# noinspection PyUnresolvedReferences
import abu_local_env
import abupy
from abupy import ABuSymbolPd
from abupy import EMarketSourceType
from abupy import EMarketDataFetchMode
from abupy import AbuFactorBuyBreak
from abupy import AbuFactorAtrNStop
from abupy import AbuFactorPreAtrNStop
from abupy import AbuFactorCloseAtrNStop
from abupy import AbuMetricsBase
from abupy import abu
warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
# 设置选股因子,None为不使用选股因子
stock_pickers = None
# 买入因子依然延用向上突破因子
buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak},
{'xd': 42, 'class': AbuFactorBuyBreak}]
# 卖出因子继续使用上一章使用的因子
sell_factors = [
{'stop_loss_n': 1.0, 'stop_win_n': 3.0,
'class': AbuFactorAtrNStop},
{'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.5},
{'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}
]
"""
附录A 量化环境部署
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
* 本节建议对照阅读abu量化文档: 第19节 数据源
"""
def sample_a21():
"""
A.2.1 数据模式的切换
:return:
"""
# 表A-1所示
print(ABuSymbolPd.make_kl_df('601398').tail())
# 局部使用enable_example_env_ipython,示例
abupy.env.enable_example_env_ipython()
# 如果本地有相应股票的缓存,可以使用如下代码强制使用本地缓存数据
# abupy.env.g_data_fetch_mode = EMarketDataFetchMode.E_DATA_FETCH_FORCE_LOCAL
# 设置初始资金数
read_cash = 1000000
# 择时股票池
choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG', 'usTSLA', 'usWUBA', 'usVIPS']
# 使用run_loop_back运行策略
abu_result_tuple, _ = abu.run_loop_back(read_cash,
buy_factors, sell_factors, stock_pickers, choice_symbols=choice_symbols,
n_folds=2)
metrics = AbuMetricsBase(*abu_result_tuple)
metrics.fit_metrics()
metrics.plot_returns_cmp()
# *****************************************************************************************************************
# 切换数据源
abupy.env.g_market_source = EMarketSourceType.E_MARKET_SOURCE_tx
# 强制走网络数据源
abupy.env.g_data_fetch_mode = EMarketDataFetchMode.E_DATA_FETCH_FORCE_NET
# 择时股票池
choice_symbols = ['601398', '600028', '601857', '601318', '600036', '000002', '600050', '600030']
# 使用run_loop_back运行策略
abu_result_tuple, _ = abu.run_loop_back(read_cash,
buy_factors, sell_factors, stock_pickers, choice_symbols=choice_symbols,
n_folds=2)
metrics = AbuMetricsBase(*abu_result_tuple)
metrics.fit_metrics()
metrics.plot_returns_cmp()
"""
A.2.2 目标市场的切换
A.2.3 A股市场的回测示例
* 相关内容请阅读abu量化文档:第8节 A股市场的回测, 第20节 A股全市场回测
"""
"""
A.2.4 港股市场的回测示例
* 相关内容请阅读abu量化文档:第9节 港股市场的回测
"""
if __name__ == "__main__":
sample_a21()
# sample_a23_1()
# sample_a23_2()
# sample_a23_2(from_cache=True)
# sample_a24_1()
# sample_a24_2()
# sample_a24_2(from_cache=True)
+229
View File
@@ -0,0 +1,229 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
import seaborn as sns
import warnings
import numpy as np
# noinspection PyUnresolvedReferences
import abu_local_env
from abupy import tl
from abupy import abu
import abupy
warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
"""
量化相关性分析
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
本节建议对照阅读abu量化文档:第14节 量化相关性分析应用
"""
def sample_b0():
"""
相关分析默认强制使用local数据,所以本地无缓存,请先进行数据更新
如果没有运行过abu量化文档-第十九节 数据源:中使用腾讯数据源进行数据更新,需要运行
如果运行过就不要重复运行了:
"""
from abupy import EMarketTargetType, EMarketSourceType, EDataCacheType
# 关闭沙盒数据环境
abupy.env.disable_example_env_ipython()
abupy.env.g_market_source = EMarketSourceType.E_MARKET_SOURCE_tx
abupy.env.g_data_cache_type = EDataCacheType.E_DATA_CACHE_CSV
# 首选这里预下载市场中所有股票的6年数据(做5年回测,需要预先下载6年数据)
abu.run_kl_update(start='2011-08-08', end='2017-08-08', market=EMarketTargetType.E_MARKET_TARGET_US)
def sample_b1():
"""
B1 皮尔逊相关系数
:return:
"""
arr1 = np.random.rand(10000)
arr2 = np.random.rand(10000)
corr = np.cov(arr1, arr2) / np.std(arr1) * np.std(arr2)
print('corr:\n', corr)
print('corr[0, 1]:', corr[0, 1])
print('np.corrcoef(arr1, arr2)[0, 1]:', np.corrcoef(arr1, arr2)[0, 1])
# noinspection PyTypeChecker
def sample_b2():
"""
B2 斯皮尔曼秩相关系数
:return:
"""
arr1 = np.random.rand(10000)
arr2 = arr1 + np.random.normal(0, .2, 10000)
print('np.corrcoef(arr1, arr2)[0, 1]:', np.corrcoef(arr1, arr2)[0, 1])
import scipy.stats as stats
demo_list = [1, 2, 10, 100, 2, 1000]
print('原始序列: ', demo_list)
print('序列的秩: ', list(stats.rankdata(demo_list)))
# 实现斯皮尔曼秩相关系数
def spearmanr(a, b=None, axis=0):
a, outaxis = _chk_asarray(a, axis)
ar = np.apply_along_axis(stats.rankdata, outaxis, a)
br = None
if b is not None:
b, axisout = _chk_asarray(b, axis)
br = np.apply_along_axis(stats.rankdata, axisout, b)
return np.corrcoef(ar, br, rowvar=outaxis)
def _chk_asarray(a, axis):
if axis is None:
a = np.ravel(a)
outaxis = 0
else:
a = np.asarray(a)
outaxis = axis
if a.ndim == 0:
a = np.atleast_1d(a)
return a, outaxis
print('spearmanr(arr1, arr2)[0, 1]:', spearmanr(arr1, arr2)[0, 1])
"""
scipy.stats中直接封装斯皮尔曼秩相关系数函数stats.spearmanr()函数
注意下面的方法速度没有上述自己实现计算spearmanr相关系数的方法快,因为附加计算了pvalue
"""
print('stats.spearmanr(arr1, arr2):', stats.spearmanr(arr1, arr2))
"""
B3 相关性使用示例
"""
"""
【示例1】使用abu量化系统中的ABuSimilar.find_similar_with_xxx()函数找到与目标股票相关程度最高的股票可视化
"""
def sample_b3_1():
"""
【示例1】使用abu量化系统中的ABuSimilar.find_similar_with_xxx()函数找到与目标股票相关程度最高的股票可视化
:return:
"""
# find_similar_with_cnt可视化与tsla相关top10,以及tsla相关性dictcmp_cnt=252(252天),加权相关,E_CORE_TYPE_PEARS(皮尔逊)
from abupy import find_similar_with_cnt, ECoreCorrType
_ = find_similar_with_cnt('usTSLA', cmp_cnt=252, show_cnt=10, rolling=True, show=True,
corr_type=ECoreCorrType.E_CORE_TYPE_PEARS)
# find_similar_with_se可视化与tsla相关top10,以及tsla相关性dict:从'2012-01-01'直到'2017-01-01'5年数据,非加权相关,皮尔逊
from abupy import find_similar_with_se
_ = find_similar_with_se('usTSLA', start='2012-01-01', end='2017-01-01', show_cnt=10, rolling=False,
show=True, corr_type=ECoreCorrType.E_CORE_TYPE_PEARS)
# find_similar_with_folds可视化与tsla相关top10,以及tsla相关性dictn_folds=3(3年数据)
# 非加权相关,E_CORE_TYPE_SPERM斯皮尔曼
from abupy import find_similar_with_folds
_ = find_similar_with_folds('usTSLA', n_folds=3, show_cnt=10, rolling=False, show=True,
corr_type=ECoreCorrType.E_CORE_TYPE_SPERM)
"""
【示例2】使用abu量化系统中的ABuTLSimilar.calc_similar()函数计算两支股票相对整个市场的相关性评级rank
"""
def sample_b3_2():
"""
【示例2】使用abu量化系统中的ABuTLSimilar.calc_similar()函数计算两支股票相对整个市场的相关性评级rank
:return:
"""
# 以整个市场作为观察者,usTSLA与usNOAH的相关性
rank_score, sum_rank = tl.similar.calc_similar('usNOAH', 'usTSLA')
print('rank_score', rank_score)
from abupy import find_similar_with_cnt
net_cg_ret = find_similar_with_cnt('usTSLA', cmp_cnt=252, show=False)
# 以usTSLA作为观察者,它与usNOAH的相关性数值
for ncr in net_cg_ret:
if ncr[0] == 'usNOAH':
print(ncr[1])
break
"""
以整个市场作为观察者,与usTSLA相关性TOP 10可视化
直接将calc_similar返回的sum_rank传入calc_similar_top直接用,不用再计算了
"""
tl.similar.calc_similar_top('usTSLA', sum_rank)
"""
【示例3】相关与协整组成的一个简单量化选股策略, 使用封装好的函数coint_similar()
"""
def sample_b3_3():
"""
【示例3】相关与协整组成的一个简单量化选股策略, 使用封装好的函数coint_similar()
:return:
"""
tl.similar.coint_similar('usTSLA')
"""
【示例4】abu量化系统选股结合相关性,编写相关性选股策略
"""
def sample_b3_4():
"""
【示例4】abu量化系统选股结合相关性,编写相关性选股策略
AbuPickSimilarNTop源代码请自行阅读,只简单示例使用。
:return:
"""
from abupy import AbuPickSimilarNTop
from abupy import AbuPickStockWorker
from abupy import AbuBenchmark, AbuCapital, AbuKLManager
benchmark = AbuBenchmark()
# 选股因子AbuPickSimilarNTop 寻找与usTSLA相关性不低于0.95的股票
# 这里内部使用以整个市场作为观察者方式计算,即取值范围0-1
stock_pickers = [{'class': AbuPickSimilarNTop,
'similar_stock': 'usTSLA', 'threshold_similar_min': 0.95}]
# 从这几个股票里进行选股,只是为了演示方便,一般的选股都会是数量比较多的情况比如全市场股票
choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG', 'usTSLA', 'usWUBA', 'usVIPS']
capital = AbuCapital(1000000, benchmark)
kl_pd_manager = AbuKLManager(benchmark, capital)
stock_pick = AbuPickStockWorker(capital, benchmark, kl_pd_manager, choice_symbols=choice_symbols,
stock_pickers=stock_pickers)
stock_pick.fit()
print('stock_pick.choice_symbols:\n', stock_pick.choice_symbols)
"""
通过选股因子first_choice属性执行批量优先选股操作,具体阅读源代码
"""
# 选股因子AbuPickSimilarNTop 寻找与usTSLA相关性不低于0.95的股票
# 通过设置'first_choice':True,进行优先批量操作,默认从对应市场选股
stock_pickers = [{'class': AbuPickSimilarNTop, 'first_choice': True,
'similar_stock': 'usTSLA', 'threshold_similar_min': 0.95}]
benchmark = AbuBenchmark()
capital = AbuCapital(1000000, benchmark)
kl_pd_manager = AbuKLManager(benchmark, capital)
stock_pick = AbuPickStockWorker(capital, benchmark, kl_pd_manager, choice_symbols=None,
stock_pickers=stock_pickers)
stock_pick.fit()
print('stock_pick.choice_symbols:\n', stock_pick.choice_symbols)
if __name__ == "__main__":
# sample_b0()
sample_b1()
# sample_b2()
# sample_b3_1()
# sample_b3_2()
# sample_b3_3()
# sample_b3_4()
+76
View File
@@ -0,0 +1,76 @@
# -*- encoding:utf-8 -*-
from __future__ import print_function
import seaborn as sns
import warnings
# noinspection PyUnresolvedReferences
import abu_local_env
import abupy
from abupy import EStoreAbu, abu
from abupy import ABuSymbolPd
from abupy import tl
from abupy import nd
from abupy import ABuMarketDrawing
warnings.filterwarnings('ignore')
sns.set_context(rc={'figure.figsize': (14, 7)})
"""
附录C-量化统计分析及指标应用
abu量化系统github地址:https://github.com/bbfamily/abu (您的star是我的动力!)
abu量化文档教程ipython notebookhttps://github.com/bbfamily/abu/tree/master/abupy_lecture
本节内容建议对照阅读abu量化文档:第13节 量化技术分析应用
"""
def sample_c1():
"""
C.1 量化统计分析应用
:return:
"""
tsla_df = ABuSymbolPd.make_kl_df('usTSLA', n_folds=2)
jumps = tl.jump.calc_jump(tsla_df)
print('jumps:\n', jumps)
# sw[0]代表非时间因素的jump_powersw[1]代表时间加权因素的jump_power,当sw[0]=1时与非加权方式相同,具体实现请参考源代码
filter_jumps = tl.jump.calc_jump_line_weight(tsla_df, sw=(0.5, 0.5))
print('filter_jumps:\n', filter_jumps)
# tl.wave.calc_wave_abs()函数可视化价格波动情况
tl.wave.calc_wave_abs(tsla_df, xd=21, show=True)
"""
C.2 量化技术指标应用: 对量化策略失败结果的人工分析
"""
def sample_c2():
"""
C.2 量化技术指标应用: 对量化策略失败结果的人工分析
:return:
"""
abupy.env.disable_example_env_ipython()
# 从之前章节的缓存中读取交易数据
abu_result_tuple_train = abu.load_abu_result_tuple(n_folds=5, store_type=EStoreAbu.E_STORE_CUSTOM_NAME,
custom_name='train_cn')
# 只筛选orders中有交易结果的单子
has_result = abu_result_tuple_train.orders_pd[
abu_result_tuple_train.orders_pd.result == -1]
# 随便拿一个交易数据作为示例
sample_order = has_result.ix[100]
_ = ABuMarketDrawing.plot_candle_from_order(sample_order)
nd.macd.plot_macd_from_order(sample_order, date_ext=252)
nd.boll.plot_boll_from_order(has_result.ix[100], date_ext=252)
nd.ma.plot_ma_from_order(has_result.ix[100], date_ext=252, time_period=[10, 20, 30, 60, 90, 120])
if __name__ == "__main__":
sample_c1()
# sample_c2()
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

@@ -0,0 +1,280 @@
# -*- encoding:utf-8 -*-
from __future__ import division
from __future__ import print_function
import numpy as np
from abc import ABCMeta, abstractmethod
from abupy import six
# 每个人平均寿命期望是75年,约75*365=27375天
K_INIT_LIVING_DAYS = 27375
class Person(object):
"""
人类
"""
def __init__(self):
# 初始化人平均能活的寿命
self.living = K_INIT_LIVING_DAYS
# 初始化幸福指数
self.happiness = 0
# 初始化财富值
self.wealth = 0
# 初始化名望权利
self.fame = 0
# 活着的第几天
self.living_day = 0
def live_one_day(self, seek):
"""
每天只能进行一个seek,这个seek决定了你今天追求的是什么,得到了什么
seek的类型属于下面将编写的BaseSeekDay
:param seek:
:return:
"""
# 调用每个独特的BaseSeekDay类都会实现的do_seek_day,得到今天的收获
consume_living, happiness, wealth, fame = seek.do_seek_day()
# 每天要减去生命消耗,有些seek前面还会增加生命
self.living -= consume_living
# seek得到的幸福指数积累
self.happiness += happiness
# seek得到的财富积累
self.wealth += wealth
# seek得到的名望权力积累
self.fame += fame
# 活完这一天了
self.living_day += 1
class BaseSeekDay(six.with_metaclass(ABCMeta, object)):
def __init__(self):
# 每个追求每天消耗生命的常数
self.living_consume = 0
# 每个追求每天幸福指数常数
self.happiness_base = 0
# 每个追求每天财富积累常数
self.wealth_base = 0
# 每个追求每天名望权利积累常数
self.fame_base = 0
# 每个追求每天消耗生命的可变因素序列
self.living_factor = [0]
# 每个追求每天幸福指数的可变因素序列
self.happiness_factor = [0]
# 每个追求每天财富积累的可变因素序列
self.wealth_factor = [0]
# 每个追求每天名望权利的可变因素序列
self.fame_factor = [0]
# 追求了多少天了这一生
self.do_seek_day_cnt = 0
# 子类进行常数及可变因素序列设置
self._init_self()
@abstractmethod
def _init_self(self, *args, **kwargs):
# 子类必须实现,设置自己的生命消耗的常数,幸福指数常数等常数设置
pass
@abstractmethod
def _gen_living_days(self, *args, **kwargs):
# 子类必须实现,设置自己的可变因素序列
pass
def do_seek_day(self):
"""
每一天的追求具体seek
:return:
"""
# 生命消耗=living_consume:消耗常数 * happiness_factor:可变序列
if self.do_seek_day_cnt >= len(self.living_factor):
# 超出len(self.living_factor), 就取最后一个living_factor[-1]
consume_living = \
self.living_factor[-1] * self.living_consume
else:
# 每个类自定义这个追求的消耗生命常数,以及living_factor,比如
# HealthSeekDay追求健康,living_factor序列的值即由负值->正值
# 每个子类living_factor会有自己特点的变化速度及序列长度,导致每个
# 追求对生命的消耗随着追求的次数变化不一
consume_living = self.living_factor[self.do_seek_day_cnt] \
* self.living_consume
# 幸福指数=happiness_base:幸福常数 * happiness_factor:可变序列
if self.do_seek_day_cnt >= len(self.happiness_factor):
# 超出len(self.happiness_factor), 就取最后一个
# 由于happiness_factor值由:n—>0 所以happiness_factor[-1]=0
# 即随着追求一个事物的次数过多后会变的没有幸福感
happiness = self.happiness_factor[
-1] * self.happiness_base
else:
# 每个类自定义这个追求的幸福指数常数,以及happiness_factor
# happiness_factor子类的定义一般是从高->低变化
happiness = self.happiness_factor[
self.do_seek_day_cnt] * self.happiness_base
# 财富积累=wealth_base:积累常数 * wealth_factor:可变序列
if self.do_seek_day_cnt >= len(self.wealth_factor):
# 超出len(self.wealth_factor), 就取最后一个
wealth = self.wealth_factor[-1] * self.wealth_base
else:
# 每个类自定义这个追求的财富指数常数,以及wealth_factor
wealth = self.wealth_factor[
self.do_seek_day_cnt] * self.wealth_base
# 权利积累=fame_base:积累常数 * fame_factor:可变序列
if self.do_seek_day_cnt >= len(self.fame_factor):
# 超出len(self.fame_factor), 就取最后一个
fame = self.fame_factor[-1] * self.fame_base
else:
# 每个类自定义这个追求的名望权利指数常数,以及fame_factor
fame = self.fame_factor[
self.do_seek_day_cnt] * self.fame_base
# 追求了多少天了这一生 + 1
self.do_seek_day_cnt += 1
# 返回这个追求这一天对生命的消耗,得到的幸福,财富,名望权利
return consume_living, happiness, wealth, fame
def regular_mm(group):
# 最小-最大规范化
return (group - group.min()) / (group.max() - group.min())
class HealthSeekDay(BaseSeekDay):
"""
HealthSeekDay追求健康长寿的一天:
形象:健身,旅游,娱乐,做感兴趣的事情。
抽象:追求健康长寿。
"""
def _init_self(self):
# 每天对生命消耗的常数=1,即代表1天
self.living_consume = 1
# 每天幸福指数常数=1
self.happiness_base = 1
# 设定可变因素序列
self._gen_living_days()
def _gen_living_days(self):
# 只生成12000个序列,因为下面的happiness_factor序列值由1>0
# 所以大于12000次的追求都将只是单纯消耗生命,并不增加幸福指数
# 即随着做一件事情的次数越来越多,幸福感越来越低,直到完全体会不到幸福
days = np.arange(1, 12000)
# 基础函数选用sqrt, 影响序列变化速度
living_days = np.sqrt(days)
"""
对生命消耗可变因素序列值由-1->1, 也就是这个追求一开始的时候对生命
的消耗为负增长,延长了生命,随着追求的次数不断增多对生命的消耗转为正
数因为即使一个人天天锻炼身体,天天吃营养品,也还是会有自然死亡的那
一天
"""
# *2-1的目的:regular_mm在0-1之间,HealthSeekDay要结果在-11之间
self.living_factor = regular_mm(living_days) * 2 - 1
# 结果在1-0之间 [::-1]: 将0->1转换到1->0
self.happiness_factor = regular_mm(days)[::-1]
class StockSeekDay(BaseSeekDay):
"""
StockSeekDay追求财富金钱的一天:
形象:做股票投资赚钱的事情。
抽象:追求财富金钱
"""
def _init_self(self, show=False):
# 每天对生命消耗的常数=2,即代表2天
self.living_consume = 2
# 每天幸福指数常数=0.5
self.happiness_base = 0.5
# 财富积累常数=10,默认=0
self.wealth_base = 10
# 设定可变因素序列
self._gen_living_days()
def _gen_living_days(self):
# 只生成10000个序列
days = np.arange(1, 10000)
# 针对生命消耗living_factor的基础函数还是sqrt
living_days = np.sqrt(days)
# 由于不需要像HealthSeekDay从负数开始,所以直接regular_mm 即:0->1
self.living_factor = regular_mm(living_days)
# 针对幸福感可变序列使用了np.power4,即变化速度比sqrt快
happiness_days = np.power(days, 4)
# 幸福指数可变因素会快速递减由1->0
self.happiness_factor = regular_mm(happiness_days)[::-1]
"""
这里简单设定wealth_factor=living_factor
living_factor(0-1), 导致wealth_factor(0-1), 即财富积累越到
后面越有效率,速度越快,头一个100万最难赚
"""
self.wealth_factor = self.living_factor
class FameSeekDay(BaseSeekDay):
"""
FameTask追求名望权力的一天:
追求名望权力
"""
def _init_self(self):
# 每天对生命消耗的常数=3,即代表3天
self.living_consume = 3
# 每天幸福指数常数=0.6
self.happiness_base = 0.6
# 名望权利积累常数=10,默认=0
self.fame_base = 10
# 设定可变因素序列
self._gen_living_days()
def _gen_living_days(self):
# 只生成12000个序列
days = np.arange(1, 12000)
# 针对生命消耗living_factor的基础函数还是sqrt
living_days = np.sqrt(days)
# 由于不需要像HealthSeekDay从负数开始,所以直接regular_mm 即:0->1
self.living_factor = regular_mm(living_days)
# 针对幸福感可变序列使用了np.power2
# 即变化速度比StockSeekDay慢但比HealthSeekDay快
happiness_days = np.power(days, 2)
# 幸福指数可变因素递减由1->0
self.happiness_factor = regular_mm(happiness_days)[::-1]
# 这里简单设定fame_factor=living_factor
self.fame_factor = self.living_factor
def my_life(weights):
"""
追求健康长寿快乐的权重:weights[0]
追求财富金钱的权重:weights[1]
追求名望权力的权重:weights[2]
"""
# 追求健康长寿快乐
seek_health = HealthSeekDay()
# 追求财富金钱
seek_stock = StockSeekDay()
# 追求名望权力
seek_fame = FameSeekDay()
# 放在一个list中对对应下面np.random.choice中的index[0, 1, 2]
seek_list = [seek_health, seek_stock, seek_fame]
# 初始化我
me = Person()
# 加权随机抽取序列。80000天肯定够了, 80000天快220年了。。。
seek_choice = np.random.choice([0, 1, 2], 80000, p=weights)
while me.living > 0:
# 追求从加权随机抽取序列已经初始化好的
seek_ind = seek_choice[me.living_day]
seek = seek_list[seek_ind]
# 只要还活着,就追求
me.live_one_day(seek)
return round(me.living_day / 365, 2), round(me.happiness, 2), round(me.wealth, 2), round(me.fame, 2)
+18
View File
@@ -0,0 +1,18 @@
# 量化交易之路 python代码
提供了python版本的代码示例实现,但请尽量能熟练使用notebook,使用了交互式操作的ipython notebook便于量化交易策略的思路快速实现,检验,面向过程的步骤,强大的可视化内嵌进一步引导思路,方便将零散的思路一点一点变成代码,验证代码的有效性,以及更多的实验特性
1. [第二章 量化语言——Python]()
2. [第三章 量化工具——NumPy]()
3. [第四章 量化工具——pandas]()
4. [第五章 量化工具——可视化]()
5. [第六章 量化工具——数学:你一生的追求到底能带来多少幸福]()
6. [第七章 量化系统——入门:三只小猪股票投资的故事]()
7. [第八章 量化系统——开发]()
8. [第九章 量化系统——度量与优化]()
9. [第十章 量化系统——机器学习•猪老三]()
10. [第十一章 量化系统——机器学习•ABU]()
11. [附录A 量化环境部署]()
12. [附录B 量化相关性分析]()
13. [附录C 量化统计分析及指标应用]()
@@ -0,0 +1,272 @@
# -*- encoding:utf-8 -*-
from abc import ABCMeta, abstractmethod
from collections import OrderedDict
from collections import namedtuple
from abupy import six, reduce, map, filter
class StockTradeDays(object):
def __init__(self, price_array, start_date, date_array=None):
# 私有价格序列
self.__price_array = price_array
# 私有日期序列
self.__date_array = self._init_days(start_date, date_array)
# 私有涨跌幅序列
self.__change_array = self.__init_change()
# 进行OrderedDict的组装
self.stock_dict = self._init_stock_dict()
def __init_change(self):
"""
从price_array生成change_array
:return:
"""
price_float_array = [float(price_str) for price_str in
self.__price_array]
# 通过将时间平移形成两个错开的收盘价序列,通过zip打包成为一个新的序列
# 每个元素为相邻的两个收盘价格
pp_array = [(price1, price2) for price1, price2 in
zip(price_float_array[:-1], price_float_array[1:])]
change_array = list(map(lambda pp: reduce(lambda a, b: round((b - a) / a, 3), pp), pp_array))
# list insert插入数据,将第一天的涨跌幅设置为0
change_array.insert(0, 0)
return change_array
def _init_days(self, start_date, date_array):
"""
protect方法,
:param start_date: 初始日期
:param date_array: 给定日期序列
:return:
"""
if date_array is None:
# 由start_date和self.__price_array来确定日期序列
date_array = [str(start_date + ind) for ind, _ in
enumerate(self.__price_array)]
else:
# 稍后的内容会使用外部直接设置的方式
# 如果外面设置了date_array,就直接转换str类型组成新date_array
date_array = [str(date) for date in date_array]
return date_array
def _init_stock_dict(self):
"""
使用namedtupleOrderedDict将结果合并
:return:
"""
stock_namedtuple = namedtuple('stock',
('date', 'price', 'change'))
# 使用以被赋值的__date_array等进行OrderedDict的组装
stock_dict = OrderedDict(
(date, stock_namedtuple(date, price, change))
for date, price, change in
zip(self.__date_array, self.__price_array,
self.__change_array))
return stock_dict
def filter_stock(self, want_up=True, want_calc_sum=False):
"""
筛选结果子集
:param want_up: 是否筛选上涨
:param want_calc_sum: 是否计算涨跌和
:return:
"""
# Python中的三目表达式的写法
filter_func = (lambda p_day: p_day.change > 0) if want_up else (
lambda p_day: p_day.change < 0)
# 使用filter_func做筛选函数
want_days = list(filter(filter_func, self.stock_dict.values()))
if not want_calc_sum:
return want_days
# 需要计算涨跌幅和
change_sum = 0.0
for day in want_days:
change_sum += day.change
return change_sum
"""
下面的__str____iter__ __getitem__ __len__稍后会详细讲解作
"""
def __str__(self):
return str(self.stock_dict)
__repr__ = __str__
def __iter__(self):
"""
通过代理stock_dict的跌倒,yield元素
:return:
"""
for key in self.stock_dict:
yield self.stock_dict[key]
def __getitem__(self, ind):
date_key = self.__date_array[ind]
return self.stock_dict[date_key]
def __len__(self):
return len(self.stock_dict)
class TradeStrategyBase(six.with_metaclass(ABCMeta, object)):
"""
交易策略抽象基类
"""
@abstractmethod
def buy_strategy(self, *args, **kwargs):
# 买入策略基类
pass
@abstractmethod
def sell_strategy(self, *args, **kwargs):
# 卖出策略基类
pass
class TradeStrategy1(TradeStrategyBase):
"""
交易策略1: 追涨策略,当股价上涨一个阀值默认为7%
买入股票并持有s_keep_stock_threshold20)天
"""
s_keep_stock_threshold = 20
def __init__(self):
self.keep_stock_day = 0
# 7%上涨幅度作为买入策略阀值
self.__buy_change_threshold = 0.07
def buy_strategy(self, trade_ind, trade_day, trade_days):
if self.keep_stock_day == 0 and \
trade_day.change > self.__buy_change_threshold:
# 当没有持有股票的时候self.keep_stock_day == 0 并且
# 符合买入条件上涨一个阀值,买入
self.keep_stock_day += 1
elif self.keep_stock_day > 0:
# self.keep_stock_day > 0代表持有股票,持有股票天数递增
self.keep_stock_day += 1
def sell_strategy(self, trade_ind, trade_day, trade_days):
if self.keep_stock_day >= \
TradeStrategy1.s_keep_stock_threshold:
# 当持有股票天数超过阀值s_keep_stock_threshold,卖出股票
self.keep_stock_day = 0
"""
property属性稍后会讲到
"""
@property
def buy_change_threshold(self):
return self.__buy_change_threshold
@buy_change_threshold.setter
def buy_change_threshold(self, buy_change_threshold):
if not isinstance(buy_change_threshold, float):
"""
上涨阀值需要为float类型
"""
raise TypeError('buy_change_threshold must be float!')
# 上涨阀值只取小数点后两位
self.__buy_change_threshold = round(buy_change_threshold, 2)
class TradeLoopBack(object):
"""
交易回测系统
"""
def __init__(self, trade_days, trade_strategy):
"""
使用上一节封装的StockTradeDays类和本节编写的交易策略类
TradeStrategyBase类初始化交易系统
:param trade_days: StockTradeDays交易数据序列
:param trade_strategy: TradeStrategyBase交易策略
"""
self.trade_days = trade_days
self.trade_strategy = trade_strategy
# 交易盈亏结果序列
self.profit_array = []
def execute_trade(self):
"""
执行交易回测
:return:
"""
for ind, day in enumerate(self.trade_days):
"""
以时间驱动,完成交易回测
"""
if self.trade_strategy.keep_stock_day > 0:
# 如果有持有股票,加入交易盈亏结果序列
self.profit_array.append(day.change)
# hasattr: 用来查询对象有没有实现某个方法
if hasattr(self.trade_strategy, 'buy_strategy'):
# 买入策略执行
self.trade_strategy.buy_strategy(ind, day,
self.trade_days)
if hasattr(self.trade_strategy, 'sell_strategy'):
# 卖出策略执行
self.trade_strategy.sell_strategy(ind, day,
self.trade_days)
class TradeStrategy2(TradeStrategyBase):
"""
交易策略2: 均值回复策略,当股价连续两个交易日下跌,
且下跌幅度超过阀值默认s_buy_change_threshold(-10%)
买入股票并持有s_keep_stock_threshold10)天
"""
# 买入后持有天数
s_keep_stock_threshold = 10
# 下跌买入阀值
s_buy_change_threshold = -0.10
def __init__(self):
self.keep_stock_day = 0
def buy_strategy(self, trade_ind, trade_day, trade_days):
if self.keep_stock_day == 0 and trade_ind >= 1:
"""
当没有持有股票的时候self.keep_stock_day == 0 并且
trade_ind >= 1, 不是交易开始的第一天,因为需要yesterday数据
"""
# trade_day.change < 0 bool:今天是否股价下跌
today_down = trade_day.change < 0
# 昨天是否股价下跌
yesterday_down = trade_days[trade_ind - 1].change < 0
# 两天总跌幅
down_rate = trade_day.change + trade_days[trade_ind - 1].change
if today_down and yesterday_down and down_rate < \
TradeStrategy2.s_buy_change_threshold:
# 买入条件成立:连跌两天,跌幅超过s_buy_change_threshold
self.keep_stock_day += 1
elif self.keep_stock_day > 0:
# self.keep_stock_day > 0代表持有股票,持有股票天数递增
self.keep_stock_day += 1
def sell_strategy(self, trade_ind, trade_day, trade_days):
if self.keep_stock_day >= \
TradeStrategy2.s_keep_stock_threshold:
# 当持有股票天数超过阀值s_keep_stock_threshold,卖出股票
self.keep_stock_day = 0
"""
稍后会详细讲解classmethodstaticmethod
"""
@classmethod
def set_keep_stock_threshold(cls, keep_stock_threshold):
cls.s_keep_stock_threshold = keep_stock_threshold
@staticmethod
def set_buy_change_threshold(buy_change_threshold):
TradeStrategy2.s_buy_change_threshold = buy_change_threshold
@@ -0,0 +1,142 @@
# -*- encoding:utf-8 -*-
"""
梦想中的机器学习股票数据环境
"""
import numpy as np
from abupy import ABuSymbolPd
import sklearn.preprocessing as preprocessing
"""
是否开启date_week噪音
"""
g_with_date_week_noise = True
def _gen_another_word_price(kl_another_word):
"""
生成股票在另一个世界中的价格
:param kl_another_word:
:return:
"""
for ind in np.arange(2, kl_another_word.shape[0]):
# 前天数据
bf_yesterday = kl_another_word.iloc[ind - 2]
# 昨天
yesterday = kl_another_word.iloc[ind - 1]
# 今天
today = kl_another_word.iloc[ind]
# 生成今天的收盘价格
kl_another_word.close[ind] = _gen_another_word_price_rule(yesterday.close, yesterday.volume,
bf_yesterday.close, bf_yesterday.volume,
today.volume, today.date_week)
def _gen_another_word_price_rule(yesterday_close, yesterday_volume, bf_yesterday_close, bf_yesterday_volume,
today_volume, date_week):
"""
通过前天收盘量价,昨天收盘量价,今天的量,构建另一个世界中的价格模型
"""
price_change = yesterday_close - bf_yesterday_close
volume_change = yesterday_volume - bf_yesterday_volume
# 如果量和价变动一致,今天价格涨,否则跌
sign = 1.0 if price_change * volume_change > 0 else -1.0
# 通过date_week生成噪音,否则之后分类100%分对
if g_with_date_week_noise:
# 噪音的先决条件是今天的量是这三天最大的
gen_noise = today_volume > np.max([yesterday_volume, bf_yesterday_volume])
# 如果是周五,下跌
if gen_noise and date_week == 4:
sign = -1.0
# 如果是周一,上涨
elif gen_noise and date_week == 0:
sign = 1.0
# 今天的涨跌幅度基础是price_change(昨天前天的价格变动)
price_base = abs(price_change)
# 今天的涨跌幅度变动因素
price_factor = np.mean([today_volume / yesterday_volume, today_volume / bf_yesterday_volume])
# 如果涨跌幅度超过10%,限制上限,下限为10%
if abs(price_base * price_factor) < yesterday_close * 0.10:
today_price = yesterday_close + sign * price_base * price_factor
else:
today_price = yesterday_close + sign * yesterday_close * 0.10
return today_price
def change_real_to_another_word(symbol):
"""
将原始真正的股票数据只保留价格的头两个,量,周几,将其它价格使用_gen_another_word_price变成另一个世界价格
:param symbol:
:return:
"""
kl_pd = ABuSymbolPd.make_kl_df(symbol)
if kl_pd is not None:
kl_dream = kl_pd.filter(['close', 'date_week', 'volume'])
# 只保留原始头两天的交易收盘价格
kl_dream['close'][2:] = np.nan
# 将其它价格变成另一个世界中价格
_gen_another_word_price(kl_dream)
return kl_dream
def gen_pig_three_feature(kl_another_word):
"""
猪老三构建特征模型函数
"""
# 回顾预测的y值
kl_another_word['regress_y'] = kl_another_word.close.pct_change()
# 前天收盘价格
kl_another_word['bf_yesterday_close'] = 0
# 昨天收盘价格
kl_another_word['yesterday_close'] = 0
# 昨天收盘成交量
kl_another_word['yesterday_volume'] = 0
# 前天收盘成交量
kl_another_word['bf_yesterday_volume'] = 0
# 今天收盘成交量, 不用了用了之后更接近完美,但也算是使用了未来数据,虽然可以狡辩说为快收盘时候买入
# kl_deram['feature_today_volume'] = kl_deram['volume']
# 对其特征
kl_another_word['bf_yesterday_close'][2:] = kl_another_word['close'][:-2]
kl_another_word['bf_yesterday_volume'][2:] = kl_another_word['volume'][:-2]
kl_another_word['yesterday_close'][1:] = kl_another_word['close'][:-1]
kl_another_word['yesterday_volume'][1:] = kl_another_word['volume'][:-1]
# 特征1: 价格差
kl_another_word['feature_price_change'] = kl_another_word['yesterday_close'] - kl_another_word['bf_yesterday_close']
# 特征2: 成交量差
kl_another_word['feature_volume_Change'] = kl_another_word['yesterday_volume'] - kl_another_word[
'bf_yesterday_volume']
# 特征3: 涨跌sign
kl_another_word['feature_sign'] = np.sign(
kl_another_word['feature_price_change'] * kl_another_word['feature_volume_Change'])
# 为之后kmena实例准备数据
kmean_date_week = kl_another_word['date_week']
# 构建噪音特征, 因为猪老三也不可能全部分析正确真实的特征因素,这里引入一些噪音特征
# 成交量乘积
kl_another_word['feature_volume_noise'] = kl_another_word['yesterday_volume'] * kl_another_word[
'bf_yesterday_volume']
# 价格乘积
kl_another_word['feature_price_noise'] = kl_another_word['yesterday_close'] * kl_another_word['bf_yesterday_close']
# 将数据标准化
scaler = preprocessing.StandardScaler()
kl_another_word['feature_price_change'] = scaler.fit_transform(
kl_another_word['feature_price_change'].values.reshape(-1, 1))
kl_another_word['feature_volume_Change'] = scaler.fit_transform(
kl_another_word['feature_volume_Change'].values.reshape(-1, 1))
kl_another_word['feature_volume_noise'] = scaler.fit_transform(
kl_another_word['feature_volume_noise'].values.reshape(-1, 1))
kl_another_word['feature_price_noise'] = scaler.fit_transform(
kl_another_word['feature_price_noise'].values.reshape(-1, 1))
# 只筛选feature_开头的特征和regress_y
kl_pig_three_feature = kl_another_word.filter(regex='regress_y|feature_*')[2:]
return kl_pig_three_feature, kmean_date_week[2:]