python量化交易之路code测试二(python3通过测试)

#面向对象编程

from collections import namedtuple, OrderedDict
from functools import reduce  #原文此处没有import reduce
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) #调用_init_days() 保护方法
        #私有涨跌幅序列
        self.__change_array=self.__init_change( ) #调用__init_change()私有方法
        #进行OrderDict组装:
        self.stock_dict=self._init_stock_dict()

    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_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=map(lambda pp: reduce(lambda a,b:round((b-a)/a,3),pp),pp_array)
        #list insert函数插入数据,将第一天的涨跌幅设置为0
        result=list(change_array)
        result.insert(0,0)
        return result
    
    def _init_stock_dict(self):
        """
        使用namedtuple和OrderedDoct将结果合并
        :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:
        """
        filter_func = (lambda day: day.change >0) if want_up \
            else (lambda day: day.change<0)
        #使用filter_func作为筛选函数
        want_days= 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
            
          
    def __str__(self):
        return str(self.stock_dict)
    __repr__=__str__  #自定义__repr__()和__str__()的目的是简化调试和实例输出复杂度,使对象更具可读性。
    def __iter__(self):
        for key in self.stock_dict:
            yield self.stock_dict[key] #通过实现__iter__()方法来支持迭代操作。设置stock_dict的迭代,yield 元素
    def __getitem__(self,ind):
        date_key=self.__date_array[ind]
        return self.stock_dict[date_key]
    def __len__(self):  #通过代理self.stock_dict的len()方法简单实现打印出对象长度
        return len(self.stock_dict)
price_array = '30.14,29.58,26.36,32.56,32.82'.split(',')
date_base = 20170118
trade_days=StockTradeDays(price_array,date_base) #实例化
trade_days
len(trade_days)

from collections import Iterable
if isinstance(trade_days,Iterable):
    for day in trade_days:
        print(day)

list(trade_days.filter_stock())
import tushare as ts
stockdata_hist=ts.get_hist_data(code='510500',start='2020-01-01',end='2020-05-26',)
type(stockdata_hist)
import six
from abc import ABCMeta,abstractmethod
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 += 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
    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):
            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):
        self.trade_days = trade_days
        self.trade_strategy = trade_strategy
        self.profit_array=[]
        
    def execute_trade(self):
        """
        执行交易回测
        """
        for ind, day in enumerate(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
    def set_keep_stock_threshold(cls,keep_stock_threshold):
        cls.keep_stock_threshold = keep_stock_threshold
        
    @staticmethod
    def set_buy_change_threshold(buy_change_threshold):
        TradeStrategy2.s_buy_change_threshold=buy_change_threshold
#构造日期序列和股票价格数据

import datetime
from random import random
datestart = '20200101'
dateend = datetime.datetime.now().strftime('%Y%m%d')

# 转为日期格式
datestart=datetime.datetime.strptime(datestart,'%Y%m%d')
dateend=datetime.datetime.strptime(dateend,'%Y%m%d')
date_array = []
date_array.append(datestart.strftime('%Y%m%d'))
while datestart<dateend:
    # 日期叠加一天
    datestart+=datetime.timedelta(days=+1)
    # 日期转字符串存入列表
    date_array.append(datestart.strftime('%Y%m%d'))

#根据日期序列构造交易数据
price_array=[]
for ind,_ in enumerate(date_array):
    price_array.append(10+random()-random())
#策略1回测
date_base = "20200101"
trade_strategy1=TradeStrategy1()
trade_strategy1.buy_change_threshold = 0.03

trade_days=StockTradeDays(price_array,date_base,date_array=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))
#策略2回测
date_base = "20200101"
trade_strategy2=TradeStrategy2()
trade_days=StockTradeDays(price_array,date_base,date_array=date_array) #实例化
trade_loop_back=TradeLoopBack(trade_days,trade_strategy2)
trade_loop_back.execute_trade()
print ('回测策略1 总盈亏为:{}%'.format(reduce(lambda a, b: a + b, trade_loop_back.profit_array) * 100))
#性能效率

import itertools
items=[1,3,2]

#顺序组合
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']
for item in itertools.product(ab,cd):
    print(item)
"""
修改TradeStrategy2策略基础参数并执行回测的代码抽象出一个函数calc(),
该函数的输入参数有两个,分别是持股天数和下跌买入阀值;
输出返回值为3个,分别是盈亏情况、输入的持股天数和下跌买入阀值。
"""
def calc(keep_stock_threshold,buy_change_threshold):
    """
    :param keep_stock_threshold: 持股天数
    :param buy_change_threshold: 下跌买入阀值
    :return: 盈亏情况,输入的持股天数, 输入的下跌买入阀值
    """
    tradestrategy2 = TradeStrategy2()
    TradeStrategy2.set_keep_stock_threshold(keep_stock_threshold)
    TradeStrategy2.set_buy_change_threshold(buy_change_threshold)
    trade_loop_back = TradeLoopBack(trade_days, trade_strategy2)
    trade_loop_back.execute_trade()
    profit= 0.0 if len(trade_loop_back.profit_array) == 0 else \
        reduce(lambda a,b: a+b, trade_loop_back.profit_array)
    return profit, keep_stock_threshold, buy_change_threshold
calc(20, 0.08)
#多个有限集合进行笛卡尔积,寻找最佳持股天数和下跌阀值的组合

import itertools

keep_stock_list = list(range(2, 30 ,2))
#range集合:买入后持股天数从2~30天,间隔两天
print ('持股天数参数组:{}'.format(keep_stock_list))
buy_change_list = [buy_change /100.0 for buy_change in range(-5, -16, -1)]
# 下跌买入阀值从-0.05到-0.15,即从5%下跌到15%
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)))

#使用sorted(result)将结果序列排序:
# [::-1]将整个排序结果反转,反转后盈亏收益从最高向低开始排序
# [:10]取出收益最高的前10个组合查看
sorted(result)[::-1][:10]

猜你喜欢

转载自blog.csdn.net/ddxn417/article/details/106428172
今日推荐