• [QUANTAXIS量化分析]成长股内在价值投资策略(ZZ)


    基本原理
    滚动复利,顾名思义,用利润滚动利润,简称:驴打滚.这种方法适用于大资金在起步建仓期,又或者在打净值安全垫期。利用极少一部分仓位将安全垫做出,这种方法比较适用于震荡市. 主要体现在灵活的仓位控制上,方法运用的过程中类似抗日战争时期八路军的"打游击”,在已既定选好股票池的基础上,以不恋战为原则,快进快出.
    策略实现
    初始资金100万,时间段为:2016-01-01~2018-05-01

    选择市盈率在0~20的市值最大的前n只股票

    平均分给每只股票现金相同,五步建仓,相当于若选50只股票的话,一只股票初始资金5万,分五次一万出来;

    个股5%止损和15%止盈

    购买条件:连续五日下跌的以及价格高于5日或10日均线20%的股票不操作

    每日对现金进行重新分配,对于每支股票,最多只能买入五次,不过每次买入的金额可能会不同,(之前是用每次买入的金额一致,见图二),下文代码使用的是对同一支每次买入金额可能不同的情况,即图一。每次买入金额相同即意味着第一次买入某支股票时,便把金额存储起来,每日剩余现金流减去这部分金额,再均分下去

    运行截图:

    图1:

    在这里插入图片描述

    图2:

    在这里插入图片描述


    # coding: utf-8
    # @author: lin
    # @date: 2018/11/14
    
    import QUANTAXIS as QA
    import pandas as pd
    import time
    import matplotlib.pyplot as plt
    
    pd.set_option('max_colwidth', 5000)
    pd.set_option('display.max_columns', 5000)
    pd.set_option('display.max_rows', 5000)
    
    
    class RollingProfitStrategy:
        def __init__(self, start_time, stop_time, n=20, ascending=False, stock_init_cash=1000000):
            self.Account = QA.QA_Account()  # 初始化账户
            self.Account.reset_assets(stock_init_cash)  # 初始化账户
            self.Broker = QA.QA_BacktestBroker()
            self.time_quantum_list = ['-12-31', '-09-30', '-06-30', '-03-31']
            self.start_time = start_time
            self.stop_time = stop_time
            self.stock_pool = []
            self.cash_n = 5
            self.bought_price = {}  # 入场的价格
            self.last_price = {}  # 上一天的价格
            self.init_cash = {}  # 入场的现金分配
            self.bought_stock = {}  # 每种股票是否已入场, boolean
            self.init_strategy(n, ascending)
    
        def init_strategy(self, n, ascending):
            self.get_stock_pool_price(n, ascending)  # 获取股票和初始价格
            for stock_code in self.stock_pool:
                self.init_cash[stock_code] = [1 for i in range(self.cash_n)]  # 五等分
                self.bought_stock[stock_code] = False
    
        def get_financial_time(self):
            """
            得到此日期前一个财务数据的日期
            :return:
            """
            year = self.start_time[0:4]
            while (True):
                for day in self.time_quantum_list:
                    the_financial_time = year + day
                    if the_financial_time <= self.start_time:
                        return the_financial_time
                year = str(int(year) - 1)
    
        def get_assets_eps(self, stock_code, the_financial_time):
            """
            得到高级财务数据
            :param stock_code:
            :param the_financial_time: 离开始时间最近的财务数据的时间
            :return:
            """
            financial_report = QA.QA_fetch_financial_report(stock_code, the_financial_time)
            if financial_report is not None:
                return financial_report.iloc[0]['totalAssets'], financial_report.iloc[0]['EPS']
            return None, None
    
        def get_stock_pool_price(self, n, ascending):
            """
            选取哪些股票
            :param n: n只
            :param ascending: True则资产最少的前n,False则最多前n
            :return:
            """
            stock_code_list = QA.QA_fetch_stock_list_adv().code.tolist()
            stock_dict = {'stock': [], 'totalAssets': [], 'price': []}
            the_financial_time = self.get_financial_time()
            for stock_code in stock_code_list:
                # print(stock_code)
                assets, EPS = self.get_assets_eps(stock_code, the_financial_time)
                if assets is not None and EPS != 0:
                    data = QA.QA_fetch_stock_day_adv(stock_code, self.start_time, self.stop_time)
                    if data is None:
                        continue
                    price = data.to_pd().iloc[0]['close']
                    if 0 < price / EPS < 20:  # 满足条件才添加进行排序
                        # print(price / EPS)
                        stock_dict['stock'].append(stock_code)
                        stock_dict['totalAssets'].append(assets)
                        stock_dict['price'].append(price)
            data = pd.DataFrame(stock_dict)
            data.dropna(inplace=True)
            data.sort_values(by='totalAssets', ascending=ascending, axis=0, inplace=True)
            # print(data.iloc[:20])
            # data.reset_index(inplace=True, drop=True)
            self.stock_pool = list(data['stock'].iloc[:n])  # 前十行,若是用loc,则是查找索引
            price_list = list(data['price'].iloc[:n])
            for i in range(n):
                self.bought_price[self.stock_pool[i]] = price_list[i]
                self.last_price[self.stock_pool[i]] = price_list[i]
            print(self.stock_pool)
    
        def get_pre_month_date(self, date):
            # 得到一个月前的1日的日期,为了得到某天前十个交易日的数据
            date = date[:10]
            times = date.split('-')
            times = [int(i) for i in times]
            times[1] -= 1
            if times[1] == 0:
                times[0] -= 1
                times[1] = 12
            times[2] = 1
            return '%d-%02d-%02d' % (times[0], times[1], times[2])
    
        def is_decrease(self, data):
            # 是否连续五日下跌,此时当start_time为交易日时,第五天为start_time,若是用于实际数据中,我有点疑惑是否用reality_time数据或是前五个交易日
            close_data = list(data['close'].iloc[-5:])
            if len(close_data) < 5:
                return False
            if close_data[0] > close_data[1] > close_data[2] > close_data[3] > close_data[4]:  # 五日连续下跌
                print(close_data)
                return False
            return True
    
        def is_increase(self, data):
            close_data = list(data['close'].iloc[-10:])
            if len(close_data) < 10:
                return False
            mean_5_close = sum(close_data[5:10]) / 5
            mean_10_close = sum(close_data[:10]) / 10
            if (close_data[-1] - mean_5_close) / mean_5_close > 0.2 or 
                    (close_data[-1] - mean_10_close) / mean_10_close > 0.2:  # 价格大于五日均线或者是十日均线 20%,则返回True
                print(close_data)
                return False
            return True
    
        def if_buy(self, stock_code, date):  # 若是连续五日下跌或者昨日价格大于五日或十日均线20%则不进行操作
            pre_month_date = self.get_pre_month_date(date)
            data = QA.QA_fetch_stock_day_adv(stock_code, pre_month_date, date)
            data = data.to_qfq()  # 前复权
            if self.is_decrease(data) or self.is_increase(data):
                return True
            return False
    
        def run(self):
            """
            每个股票初始五等分,可以进场则进场,每个股票盈亏单独算,当止损或者止盈时,股票池中所有未入场的股票现金均分一下
            也可每天均分一次,这样就把止损和止盈的情况包括进去了。
            :return:
            """
    
            self.Account.account_cookie = 'rolling_profit'
            # 每天均分一下现金
            data = QA.QA_fetch_stock_day_adv(self.stock_pool, self.start_time, self.stop_time).to_qfq()
            # print(data)
            for items in data.panel_gen:  # 每一天
                # 股票池中所有未入场的股票现金均分一下
                n_percent = 0
                for stock_code in self.stock_pool:
                    n_percent += sum(self.init_cash[stock_code])
                if n_percent == 0:
                    n_money = 0
                else:
                    n_money = self.Account.cash_available / n_percent
    
                print(n_money)
    
                for item in items.security_gen:
                    close_price = item.close[0]
                    date = str(item.date[0])[:10]
                    stock_code = item.code[0]
                    # item = item.to_pd()
                    # item.reset_index(inplace=True)
                    if not self.bought_stock[stock_code]:  # 股票未入场的情况
                        if self.if_buy(stock_code, date):  # 若是可以买
                            if n_money >= close_price*100:
                                order = self.Account.send_order(
                                    code=stock_code,
                                    time=date,
                                    money=n_money,
                                    towards=QA.ORDER_DIRECTION.BUY,
                                    price=close_price,
                                    order_model=QA.ORDER_MODEL.CLOSE,
                                    amount_model=QA.AMOUNT_MODEL.BY_MONEY
                                )
                                self.Broker.receive_order(QA.QA_Event(order=order, market_data=item))
                                trade_mes = self.Broker.query_orders(self.Account.account_cookie, 'filled')
                                res = trade_mes.loc[order.account_cookie, order.realorder_id]
                                order.trade(res.trade_id, res.trade_price, res.trade_amount, res.trade_time)  # date 应为res.trade_time的
                                self.init_cash[stock_code][0] = 0
                                self.bought_stock[stock_code] = True
                                self.bought_price[stock_code] = close_price  # 入场价格
                                self.last_price[stock_code] = close_price  # 最新价格
                    else:  # 股票已经入场,一种是止盈或止亏,一种是继续买入
                        if (close_price - self.bought_price[stock_code]) / close_price > 0.15 
                                or (close_price - self.bought_price[stock_code]) / close_price < -0.05:
                            # 止盈止亏
                            order = self.Account.send_order(
                                code=stock_code,
                                time=date,
                                amount=self.Account.sell_available.get(stock_code, 0),
                                towards=QA.ORDER_DIRECTION.SELL,
                                price=0,
                                order_model=QA.ORDER_MODEL.CLOSE,
                                amount_model=QA.AMOUNT_MODEL.BY_AMOUNT
                            )
                            self.Broker.receive_order(QA.QA_Event(order=order, market_data=item))
                            trade_mes = self.Broker.query_orders(self.Account.account_cookie, 'filled')
                            res = trade_mes.loc[order.account_cookie, order.realorder_id]
                            order.trade(res.trade_id, res.trade_price, res.trade_amount, res.trade_time)
    
                            self.bought_stock[stock_code] = False
                            self.init_cash[stock_code] = [1 for i in range(self.cash_n)]
                        else:
                            if (close_price - self.last_price[stock_code]) / close_price >= 0.01:  # 日涨1%(今日比昨日涨1%,才算)
                                for i in range(self.cash_n):
                                    if self.init_cash[stock_code][i] == 1:
                                        if n_money >= close_price * 100:
                                            order = self.Account.send_order(
                                                code=stock_code,
                                                time=date,
                                                money=n_money,
                                                towards=QA.ORDER_DIRECTION.BUY,
                                                price=close_price,
                                                order_model=QA.ORDER_MODEL.CLOSE,
                                                amount_model=QA.AMOUNT_MODEL.BY_MONEY
                                            )
                                            self.Broker.receive_order(QA.QA_Event(order=order, market_data=item))
                                            trade_mes = self.Broker.query_orders(self.Account.account_cookie, 'filled')
                                            res = trade_mes.loc[order.account_cookie, order.realorder_id]
                                            order.trade(res.trade_id, res.trade_price, res.trade_amount, res.trade_time)
    
                                            self.init_cash[stock_code][i] = 0
                                self.last_price[stock_code] = close_price
                self.Account.settle()
            Risk = QA.QA_Risk(self.Account)
            Risk.assets.plot()  # 总资产
            plt.show()
            Risk.benchmark_assets.plot()  # 基准收益的资产
            plt.show()
            Risk.plot_assets_curve()  # 两个合起来的对比图
            plt.show()
            Risk.plot_dailyhold()  # 每只股票每天的买入量
            plt.show()
    
    
    start = time.time()
    one = RollingProfitStrategy('2016-01-01', '2018-05-01', ascending=False, stock_init_cash=1000000)
    stop = time.time()
    print(stop - start)
    print(one.stock_pool)
    one.run()
    stoo = time.time()
    print(stoo - stop)
    
    
    # init_cash = {}
    # init_price = {}
    # for stock_code in stock_pool:
    #     init_cash[stock_code] = 10000
    #     init_price[stock_code] = 0    # 15%止盈



    ————————————————
    版权声明:本文为CSDN博主「Trident_lin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/weixin_39220714/article/details/88105378

  • 相关阅读:
    【FastDFS】FastDFS 分布式文件系统的安装与使用,看这一篇就够了!!
    【Spring注解驱动开发】如何使用@Value注解为bean的属性赋值,我们一起吊打面试官!
    【Spring注解驱动开发】BeanPostProcessor在Spring底层是如何使用的?看完这篇我懂了!!
    【Spring注解驱动开发】困扰了我很久的AOP嵌套调用终于解决了!
    【Spring注解驱动开发】面试官再问你BeanPostProcessor的执行流程,就把这篇文章甩给他!
    【Spring注解驱动开发】关于BeanPostProcessor后置处理器,你了解多少?
    【Spring注解驱动开发】@PostConstruct与@PreDestroy源码的执行过程
    138 张图带你 MySQL 入门
    操作系统 I/O 全流程详解
    关于问问题和时间管理的感悟
  • 原文地址:https://www.cnblogs.com/xpvincent/p/14551887.html
Copyright © 2020-2023  润新知