zoukankan      html  css  js  c++  java
  • [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

  • 相关阅读:
    iaas,paas,saas理解
    July 06th. 2018, Week 27th. Friday
    July 05th. 2018, Week 27th. Thursday
    July 04th. 2018, Week 27th. Wednesday
    July 03rd. 2018, Week 27th. Tuesday
    July 02nd. 2018, Week 27th. Monday
    July 01st. 2018, Week 27th. Sunday
    June 30th. 2018, Week 26th. Saturday
    June 29th. 2018, Week 26th. Friday
    June 28th. 2018, Week 26th. Thursday
  • 原文地址:https://www.cnblogs.com/xpvincent/p/14551887.html
Copyright © 2011-2022 走看看