merge: 合并 wikillm 与 wikillm-git,全部内容入库(abuquant-src/OCR产物/images/IDEA.md)
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user