merge: 合并 wikillm 与 wikillm-git,全部内容入库(abuquant-src/OCR产物/images/IDEA.md)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# -*- encoding:utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.abspath('../'))
|
||||
@@ -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 notebook:https://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',cv=10
|
||||
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()
|
||||
@@ -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 notebook:https://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()
|
||||
@@ -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 notebook:https://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_array,price_array,change_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):
|
||||
"""
|
||||
使用namedtuple,OrderedDict将结果合并
|
||||
: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_threshold(20)天
|
||||
"""
|
||||
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_threshold(10)天
|
||||
"""
|
||||
# 买入后持有天数
|
||||
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
|
||||
|
||||
"""
|
||||
稍后会详细讲解classmethod,staticmethod
|
||||
"""
|
||||
|
||||
@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']
|
||||
# 针对ab,cd两个集合进行排列组合
|
||||
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()
|
||||
@@ -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 notebook:https://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))
|
||||
# shape:3行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)))
|
||||
# shape:x=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))
|
||||
|
||||
# axis=1
|
||||
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())
|
||||
|
||||
# 概率密度函数(PDF,probability 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-454day,view_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()
|
||||
@@ -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 notebook:https://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]:(1,2,3,4),[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的连接axis=0:纵向连接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()
|
||||
@@ -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 notebook:https://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))
|
||||
# 画布0,loc:0 plot_demo中传入画布,则使用传入的画布绘制
|
||||
drawer = axs[0][0]
|
||||
plot_demo(drawer)
|
||||
drawer.legend(['Series', 'Numpy', 'List'], loc=0)
|
||||
# 画布1,loc:1
|
||||
drawer = axs[0][1]
|
||||
plot_demo(drawer)
|
||||
drawer.legend(['Series', 'Numpy', 'List'], loc=1)
|
||||
# 画布2,loc:2
|
||||
drawer = axs[1][0]
|
||||
plot_demo(drawer)
|
||||
drawer.legend(['Series', 'Numpy', 'List'], loc=2)
|
||||
# 画布3,loc:2, 设置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代表:open,close,high,low
|
||||
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_series=True, 即只绘制一条曲线使用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_series=True, 即只绘制一条曲线使用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]:x,result[:, 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()
|
||||
@@ -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序列: 0,1,2, ...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 notebook:https://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序列: 0,1,2, ...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要结果在-1,1之间
|
||||
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中寻找全局最优范围1-504
|
||||
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_postion=none, 之后都有值
|
||||
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)
|
||||
|
||||
# 输出如图6-19所示
|
||||
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()
|
||||
@@ -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 notebook:https://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根据start,end进行切片
|
||||
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_rate=0.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()
|
||||
@@ -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 notebook:https://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使用AbuKellyPosition,60d仍然使用默认仓位管理类
|
||||
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_factors,sell_factors的dict
|
||||
factor_dict['usSFUN'] = {'buy_factors': buy_factors_sfun, 'sell_factors': sell_factors_sfun}
|
||||
# 构建NOAH独立的buy_factors,sell_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()
|
||||
@@ -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 notebook:https://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)
|
||||
@@ -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 notebook:https://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)
|
||||
@@ -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 notebook:https://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相关性dict:cmp_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相关性dict:n_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()
|
||||
@@ -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 notebook:https://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_power,sw[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要结果在-1,1之间
|
||||
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)
|
||||
@@ -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):
|
||||
"""
|
||||
使用namedtuple,OrderedDict将结果合并
|
||||
: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_threshold(20)天
|
||||
"""
|
||||
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_threshold(10)天
|
||||
"""
|
||||
# 买入后持有天数
|
||||
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
|
||||
|
||||
"""
|
||||
稍后会详细讲解classmethod,staticmethod
|
||||
"""
|
||||
|
||||
@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:]
|
||||
Reference in New Issue
Block a user