Esempio n. 1
0
def QA_fetch_get_stock_indicator(code, start_date, end_date, type='day'):
    if type == 'day':
        start = QA_util_get_pre_trade_date(start_date, 200)
        rng1 = pd.Series(pd.date_range(start_date, end_date,
                                       freq='D')).apply(lambda x: str(x)[0:10])
        try:
            data = QA_fetch_stock_day_adv(code, start, end_date)
            data = data.to_qfq()
        except:
            print("No data")
    elif type == 'week':
        start = QA_util_get_pre_trade_date(start_date, 200)
        rng1 = pd.Series(pd.date_range(start_date, end_date,
                                       freq='D')).apply(lambda x: str(x)[0:10])
        try:
            data = QA_fetch_stock_day_adv(code, start, end_date)
            data = data.to_qfq()
            data = QA_DataStruct_Stock_day(
                data.data.groupby('code', sort=True).apply(ohlc, 7))
        except:
            print("No data")

    elif type == 'month':
        start = QA_util_get_pre_trade_date(start_date, 220)
        rng1 = pd.Series(pd.date_range(start_date, end_date,
                                       freq='D')).apply(lambda x: str(x)[0:10])
        try:
            data = QA_fetch_stock_day_adv(code, start, end_date)
            data = data.to_qfq()
            data = QA_DataStruct_Stock_day(
                data.data.groupby('code', sort=True).apply(ohlc, 30))
        except:
            print("No data")
    if data == None:
        return None
    else:
        data = get_indicator(data, rng1)
        return (data)
Esempio n. 2
0
def ETL_stock_day(codes, start=None, end=None):
    if start is None:
        start = '2008-01-01'

    if end is None:
        end = QA_util_today_str()

    if start != end:
        rng = pd.Series(pd.date_range(start, end, freq='D')).apply(lambda x: str(x)[0:10])
    else:
        rng = str(start)[0:10]

    start_date = QA_util_get_pre_trade_date(start,100)
    data = QA_fetch_stock_day_adv(codes,start_date,end)
    res1 = data.to_qfq().data
    res1.columns = [x + '_qfq' for x in res1.columns]
    data = data.data.join(res1).fillna(0).reset_index()
    res = data.groupby('code').apply(pct)
    res = res.reset_index(level = 0,drop = True).reset_index().set_index(['date','code']).loc[rng].replace([np.inf, -np.inf], 0)
    res = res.where((pd.notnull(res)), None)
    return(res)
Esempio n. 3
0
class QA_Risk():
    """QARISK 是一个风险插件

    需要加载一个account/portfolio类进来:
    需要有
    code,start_date,end_date,daily_cash,daily_hold

    TODO:
    资金利用率 反应资金的利用程度
    股票周转率 反应股票的持仓天数
    预期PNL/统计学PNL
    """

    def __init__(self, account, benchmark_code='000300', benchmark_type=MARKET_TYPE.INDEX_CN, if_fq=True):
        """
        if_qf选项是@尧提出的,关于回测的时候成交价格问题(如果按不复权撮合 应该按不复权价格计算assets)
        """
        self.account = account
        self.benchmark_code = benchmark_code  # 默认沪深300
        self.benchmark_type = benchmark_type

        self.fetch = {MARKET_TYPE.STOCK_CN: QA_fetch_stock_day_adv,
                      MARKET_TYPE.INDEX_CN: QA_fetch_index_day_adv}
        self.market_data = QA_fetch_stock_day_adv(
            self.account.code, self.account.start_date, self.account.end_date)
        self.if_fq = if_fq

        self._assets = (self.market_value.sum(
            axis=1) + self.account.daily_cash.set_index('date').cash).fillna(method='pad')

        self.time_gap = QA_util_get_trade_gap(
            self.account.start_date, self.account.end_date)
        self.init_cash = self.account.init_cash
        self.init_assets = self.account.init_assets

    def __repr__(self):
        return '< QA_RISK ANALYSIS ACCOUNT/PORTFOLIO >'

    def __call__(self):
        return pd.DataFrame([self.message])

    @property
    @lru_cache()
    def market_value(self):
        """市值表

        Returns:
            pd.DataFrame -- 市值表
        """

        if self.if_fq:
            return self.market_data.to_qfq().pivot('close') * self.account.daily_hold
        else:
            self.market_data.pivot('close') * self.account.daily_hold

    @property
    def assets(self):
        x1 = self._assets.reset_index()
        return x1.assign(date=pd.to_datetime(x1.date)).set_index('date')[0]

    @property
    def max_dropback(self):
        """最大回撤
        """
        return round(float(max([(self.assets.iloc[idx] - self.assets.iloc[idx::].min())/self.assets.iloc[idx] for idx in range(len(self.assets))])), 2)

    @property
    def total_commission(self):
        """总手续费
        """
        return -abs(round(self.account.history_table.commission.sum(), 2))

    @property
    def total_tax(self):
        """总印花税

        """

        return -abs(round(self.account.history_table.tax.sum(), 2))

    @property
    def profit_construct(self):
        """利润构成

        Returns:
            dict -- 利润构成表
        """

        return {
            'total_buyandsell': round(self.profit_money-self.total_commission-self.total_tax, 2),
            'total_tax': self.total_tax,
            'total_commission': self.total_commission,
            'total_profit': self.profit_money
        }

    @property
    def profit_money(self):
        """盈利额

        Returns:
            [type] -- [description]
        """

        return round(self.assets.iloc[-1]-self.init_cash, 2)

    @property
    def profit(self):
        """盈利率(百分比)

        Returns:
            [type] -- [description]
        """

        return round(float(self.calc_profit(self.assets)), 2)

    @property
    def profit_pct(self):
        """利润
        """
        return self.calc_profitpctchange(self.assets)

    @property
    def annualize_return(self):
        """年化收益

        Returns:
            [type] -- [description]
        """

        return round(float(self.calc_annualize_return(self.assets, self.time_gap)), 2)

    @property
    def volatility(self):
        """波动率

        Returns:
            [type] -- [description]
        """
        return round(float(self.profit_pct.std() * math.sqrt(250)), 2)

    @property
    @lru_cache()
    def message(self):
        return {
            'account_cookie': self.account.account_cookie,
            'portfolio_cookie': self.account.portfolio_cookie,
            'user_cookie': self.account.user_cookie,
            'annualize_return': round(self.annualize_return, 2),
            'profit': round(self.profit, 2),
            'max_dropback': self.max_dropback,
            'time_gap': self.time_gap,
            'volatility': self.volatility,
            'benchmark_code': self.benchmark_code,
            'bm_annualizereturn': self.benchmark_annualize_return,
            'bn_profit': self.benchmark_profit,
            'beta': self.beta,
            'alpha': self.alpha,
            'sharpe': self.sharpe,
            'init_cash': "%0.2f" % (float(self.init_cash)),
            'last_assets': "%0.2f" % (float(self.assets.iloc[-1]))

            #'init_assets': round(float(self.init_assets), 2),
            #'last_assets': round(float(self.assets.iloc[-1]), 2)
        }

    @property
    def benchmark_data(self):
        """
        基准组合的行情数据(一般是组合,可以调整)
        """
        return self.fetch[self.benchmark_type](
            self.benchmark_code, self.account.start_date, self.account.end_date)

    @property
    def benchmark_assets(self):
        """
        基准组合的账户资产队列
        """
        return (self.benchmark_data.close / float(self.benchmark_data.open.iloc[0]) * float(self.init_cash))

    @property
    def benchmark_profit(self):
        """
        基准组合的收益
        """
        return round(float(self.calc_profit(self.benchmark_assets)), 2)

    @property
    def benchmark_annualize_return(self):
        """基准组合的年化收益

        Returns:
            [type] -- [description]
        """

        return round(float(self.calc_annualize_return(self.benchmark_assets, self.time_gap)), 2)

    @property
    def benchmark_profitpct(self):
        """
        benchmark 基准组合的收益百分比计算
        """
        return self.calc_profitpctchange(self.benchmark_assets)

    @property
    def beta(self):
        """
        beta比率 组合的系统性风险
        """
        return round(float(self.calc_beta(self.profit_pct.dropna(), self.benchmark_profitpct.dropna())), 2)

    @property
    def alpha(self):
        """
        alpha比率 与市场基准收益无关的超额收益率
        """
        return round(float(self.calc_alpha(self.annualize_return, self.benchmark_annualize_return, self.beta, 0.05)), 2)

    @property
    def sharpe(self):
        """
        夏普比率

        """
        return round(float(self.calc_sharpe(self.annualize_return, self.volatility, 0.05)), 2)

    @property
    def sortino(self):
        """ 
        索提诺比率 投资组合收益和下行风险比值

        """
        pass

    @property
    def calmar(self):
        """
        卡玛比率
        """
        pass

    def set_benchmark(self, code, market_type):
        self.benchmark_code = code
        self.benchmark_type = market_type

    def calc_annualize_return(self, assets, days):
        return round((float(assets.iloc[-1]) / float(assets.iloc[0]) - 1)/(float(days) / 250), 2)

    def calc_profitpctchange(self, assets):
        return self.assets[::-1].pct_change()

    def calc_beta(self, assest_profit, benchmark_profit):

        calc_cov = np.cov(assest_profit, benchmark_profit)
        beta = calc_cov[0, 1] / calc_cov[1, 1]
        return beta

    def calc_alpha(self, annualized_returns, benchmark_annualized_returns, beta, r=0.05):

        alpha = (annualized_returns - r) - (beta) * \
            (benchmark_annualized_returns - r)
        return alpha

    def calc_profit(self, assets):
        """
        计算账户收益
        期末资产/期初资产 -1
        """
        return (float(assets.iloc[-1]) / float(self.init_cash)) - 1

    def calc_sharpe(self, annualized_returns, volatility_year, r=0.05):
        """
        计算夏普比率
        r是无风险收益
        """
        # 会出现0
        if volatility_year == 0:
            return 0
        return (annualized_returns - r) / volatility_year

    def save(self):
        """save to mongodb

        """
        save_riskanalysis(self.message)

    def plot_assets_curve(self, length=14, height=12):
        """
        资金曲线叠加图
        @Roy T.Burns 2018/05/29 修改百分比显示错误
        """
        plt.style.use('ggplot')
        plt.figure(figsize=(length, height))
        plt.subplot(211)
        plt.title('BASIC INFO', fontsize=12)
        plt.axis([0, length, 0, 0.6])
        plt.axis('off')
        i = 0
        for item in ['account_cookie', 'portfolio_cookie', 'user_cookie']:
            plt.text(i, 0.5, '{} : {}'.format(
                item, self.message[item]), fontsize=10, rotation=0, wrap=True)
            i += (length/2.8)
        i = 0
        for item in ['benchmark_code', 'time_gap', 'max_dropback']:
            plt.text(i, 0.4, '{} : {}'.format(
                item, self.message[item]), fontsize=10, ha='left', rotation=0, wrap=True)
            i += (length/2.8)
        i = 0
        for item in ['annualize_return', 'bm_annualizereturn', 'profit']:
            plt.text(i, 0.3, '{} : {} %'.format(item, self.message.get(
                item, 0)*100), fontsize=10, ha='left', rotation=0, wrap=True)
            i += length/2.8
        i = 0
        for item in ['init_cash', 'last_assets', 'volatility']:
            plt.text(i, 0.2, '{} : {} '.format(
                item, self.message[item]), fontsize=10, ha='left', rotation=0, wrap=True)
            i += length/2.8
        i = 0
        for item in ['alpha', 'beta', 'sharpe']:
            plt.text(i, 0.1, '{} : {}'.format(
                item, self.message[item]), ha='left', fontsize=10, rotation=0, wrap=True)
            i += length/2.8
        plt.subplot(212)
        self.assets.plot()
        self.benchmark_assets.xs(self.benchmark_code, level=1).plot()

        asset_p = mpatches.Patch(
            color='red', label='{}'.format(self.account.account_cookie))
        asset_b = mpatches.Patch(
            label='benchmark {}'.format(self.benchmark_code))
        plt.legend(handles=[asset_p, asset_b], loc=1)
        plt.title('ASSET AND BENCKMARK')
        plt.show()

    def plot_dailyhold(self, start=None, end=None):
        """
        使用热力图画出每日持仓
        """
        start = self.account.start_date if start is None else start
        end = self.account.end_date if end is None else end
        _, ax = plt.subplots(figsize=(20, 8))
        sns.heatmap(self.account.daily_hold.reset_index().drop('account_cookie', axis=1).set_index(
            'date').loc[start:end], cmap="YlGnBu", linewidths=0.05, ax=ax)
        ax.set_title(
            'HOLD TABLE --ACCOUNT: {}'.format(self.account.account_cookie))
        ax.set_xlabel('Code')
        ax.set_ylabel('DATETIME')
        plt.show()

    def plot_signal(self, start=None, end=None):
        """
        使用热力图画出买卖信号
        """
        start = self.account.start_date if start is None else start
        end = self.account.end_date if end is None else end
        _, ax = plt.subplots(figsize=(20, 18))
        sns.heatmap(self.account.trade.reset_index().drop('account_cookie', axis=1).set_index(
            'datetime').loc[start:end], cmap="YlGnBu", linewidths=0.05, ax=ax)
        ax.set_title(
            'SIGNAL TABLE --ACCOUNT: {}'.format(self.account.account_cookie))
        ax.set_xlabel('Code')
        ax.set_ylabel('DATETIME')
        plt.show()

    def generate_plots(self):
        """
        生成图像
        """
        self.plot_assets_curve()
        self.plot_dailyhold()
        self.plot_signal()
Esempio n. 4
0
    def get_prices(
        self,
        code_list: Union[str, Tuple[str], List[str]] = None,
        start_time: Union[str, datetime.datetime] = None,
        end_time: Union[str, datetime.datetime] = None,
        fq: str = None,
        frequence: str = None,
        price_type: str = None,
    ):
        """
        价格数据获取接口,单因子输入后,可以通过单因子获取股票代码,时间等参数信息

        参数
        ---
        :param code_list: 股票代码
        :param start_time: 起始时间
        :param end_time: 截止时间
        :param fq: 复权方式
        :param frequence: 时间频率
        """
        # 1. 股票池
        if isinstance(code_list, tuple):
            code_list = list(code_list)

        # 2. 时间频率
        if not frequence:
            frequence = self.frequence
        frequence = utils.get_frequence(frequence)

        if not start_time:
            start_time = self.start_time

        if not end_time:
            end_time = self.end_time

        if (not start_time) or (not end_time):
            raise ValueError("价格获取接口需要指定起始时间与结束时间")

        start_time = str(pd.Timestamp(start_time))[:19]
        end_time = str(pd.Timestamp(end_time))[:19]

        data = QA_fetch_stock_day_adv(code=code_list,
                                      start=start_time,
                                      end=end_time)
        index_data = QA_fetch_index_day_adv(code="000001",
                                            start=start_time,
                                            end=end_time)
        # 3. 复权
        if not fq:
            fq = self.fq

        if not fq:
            data = data
        elif fq.lower() in ["pre", "qfq", "前复权"]:
            data = data.to_qfq()
        elif fq.lower() in ["post", "hfq", "后复权"]:
            data = data.to_hfq()
        elif fq.lower() in ["none", "bfq", "不复权"]:
            data = data

        # 4. 重采样
        # 考虑到停牌退市等原因,重采样会有异常值,即日期与我们需要的日期不一致
        # 这里采用指数作为基准,对重采样数据进行再处理
        # 对于停牌数据缺失,采用前值作为填充
        if frequence == "1d":
            data = data.data.unstack().ffill().stack()
        else:
            index_data = index_data.resample(frequence).unstack()
            data = data.resample(frequence).unstack().ffill()
            data = data.reindex(index_data.index).stack()
            if frequence == '1q':
                data.index = data.index.map(lambda x:
                                            (utils.QA_fmt_quarter(x[0]), x[1]))

        # 5. 价格类型
        if not price_type:
            price_type = self.price_type

        if price_type.lower() is "avg":
            avg = data["amount"] / data["volume"] / 100.0
            return avg.unstack()
        return data[price_type.lower()].unstack()
Esempio n. 5
0
class QA_Risk():
    """QARISK 是一个风险插件

    需要加载一个account/portfolio类进来:
    需要有
    code,start_date,end_date,daily_cash,daily_hold
    """

    def __init__(self, account, benchmark_code='000300', benchmark_type=MARKET_TYPE.INDEX_CN):
        self.account = account
        self.benchmark_code = benchmark_code  # 默认沪深300
        self.benchmark_type = benchmark_type

        self.fetch = {MARKET_TYPE.STOCK_CN: QA_fetch_stock_day_adv,
                      MARKET_TYPE.INDEX_CN: QA_fetch_index_day_adv}
        self.market_data = QA_fetch_stock_day_adv(
            self.account.code, self.account.start_date, self.account.end_date)
        self._assets = ((self.market_data.to_qfq().pivot('close') * self.account.daily_hold).sum(
            axis=1) + self.account.daily_cash.set_index('date').cash).fillna(method='pad')

        self.time_gap = QA_util_get_trade_gap(
            self.account.start_date, self.account.end_date)
        self.init_assets = self.account.init_assets

    def __repr__(self):
        return '< QA_RISK ANALYSIS ACCOUNT/PORTFOLIO >'

    def __call__(self):
        return pd.DataFrame([self.message])

    @property
    def assets(self):
        x1 = self._assets.reset_index()
        return x1.assign(date=pd.to_datetime(x1.date)).set_index('date')[0]

    @property
    def max_dropback(self):
        """最大回撤
        """
        return round(float(max([(self.assets.iloc[idx] - self.assets.iloc[idx::].min())/self.assets.iloc[idx] for idx in range(len(self.assets))])),2)

    @property
    def profit(self):
        return round(float(self.calc_profit(self.assets)),2)

    @property
    def profit_pct(self):
        """利润
        """
        return self.calc_profitpctchange(self.assets)

    @property
    def annualize_return(self):
        """年化收益

        Returns:
            [type] -- [description]
        """

        return round(float(self.calc_annualize_return(self.assets, self.time_gap)),2)

    @property
    def volatility(self):
        """波动率

        Returns:
            [type] -- [description]
        """
        return round(float(self.profit_pct.std() * math.sqrt(250)),2)

    
    @property
    @lru_cache()
    def message(self):
        return {
            'account_cookie': self.account.account_cookie,
            'portfolio_cookie': self.account.portfolio_cookie,
            'user_cookie': self.account.user_cookie,
            'annualize_return': self.annualize_return,
            'profit': self.profit,
            'max_dropback': self.max_dropback,
            'time_gap': self.time_gap,
            'volatility': self.volatility,
            'benchmark_code': self.benchmark_code,
            'bm_annualizereturn':self.benchmark_annualize_return,
            'beta': self.beta,
            'alpha': self.alpha,
            'sharpe': self.sharpe,
            'init_assets': round(float(self.init_assets),2),
            'last_assets': round(float(self.assets.iloc[-1]),2)
        }

    @property
    def benchmark_data(self):
        """
        基准组合的行情数据(一般是组合,可以调整)
        """
        return self.fetch[self.benchmark_type](
            self.benchmark_code, self.account.start_date, self.account.end_date)

    @property
    def benchmark_assets(self):
        """
        基准组合的账户资产队列
        """
        return (self.benchmark_data.open / float(self.benchmark_data.open.iloc[0]) * float(self.init_assets))

    @property
    def benchmark_annualize_return(self):
        """基准组合的年化收益

        Returns:
            [type] -- [description]
        """

        return round(float(self.calc_annualize_return(self.benchmark_assets, self.time_gap)),2)

    @property
    def benchmark_profitpct(self):
        """
        benchmark 基准组合的收益百分比计算
        """
        return self.calc_profitpctchange(self.benchmark_assets)

    @property
    def beta(self):
        """
        beta比率 组合的系统性风险
        """
        return round(float(self.calc_beta(self.profit_pct.dropna(), self.benchmark_profitpct.dropna())),2)

    @property
    def alpha(self):
        """
        alpha比率 与市场基准收益无关的超额收益率
        """
        return round(float(self.calc_alpha(self.annualize_return, self.benchmark_annualize_return, self.beta, 0.05)),2)

    @property
    def sharpe(self):
        """
        夏普比率

        """
        return round(float(self.calc_sharpe(self.annualize_return, self.volatility, 0.05)),2)

    @property
    def sortino(self):
        """ 
        索提诺比率 投资组合收益和下行风险比值

        """
        pass

    @property
    def calmar(self):
        """
        卡玛比率
        """
        pass

    def set_benchmark(self, code, market_type):
        self.benchmark_code = code
        self.benchmark_type = market_type

    def calc_annualize_return(self, assets, days):
        return (float(assets.iloc[-1]) / float(assets.iloc[0]) - 1)/(float(days) / 250)

    def calc_profitpctchange(self, assets):
        return self.assets[::-1].pct_change()

    def calc_beta(self, assest_profit, benchmark_profit):

        calc_cov = np.cov(assest_profit, benchmark_profit)
        beta = calc_cov[0, 1] / calc_cov[1, 1]
        return beta

    def calc_alpha(self, annualized_returns, benchmark_annualized_returns, beta, r=0.05):

        alpha = (annualized_returns - r) - (beta) * \
            (benchmark_annualized_returns - r)
        return alpha

    def calc_profit(self, assets):
        """
        计算账户收益
        期末资产/期初资产 -1
        """
        return (float(assets.iloc[-1]) / float(assets.iloc[0])) - 1

    def calc_sharpe(self, annualized_returns, volatility_year, r=0.05):
        """
        计算夏普比率
        r是无风险收益
        """
        return (annualized_returns - r) / volatility_year

    def save(self):
        """save to mongodb

        """
        save_riskanalysis(self.message)

    def plot_assets_curve(self, length=14, height=12):
        """
        资金曲线叠加图
        """
        plt.figure(figsize=(length, 1.5))
        plt.subplot(211)
        plt.title('BASIC INFO',fontsize=12)
        plt.axis([0, length, 0, 0.6])
        plt.axis('off')
        i=0
        for item in ['account_cookie','portfolio_cookie','user_cookie']:
            plt.text(i, 0.5, '{} : {}'.format(item,self.message[item]), fontsize=10, rotation=0, wrap=True)
            i+=(length/2.8)
        i=0
        for item in ['benchmark_code','time_gap','max_dropback']:
            plt.text(i, 0.4, '{} : {}'.format(item,self.message[item]),fontsize=10, ha='left', rotation=0, wrap=True)
            i+=(length/2.8)
        i=0
        for item in ['annualize_return','bm_annualizereturn','profit']:
            plt.text(i, 0.3, '{} : {} %'.format(item,self.message.get(item,0)),fontsize=10, ha='left', rotation=0, wrap=True)
            i+=length/2.8
        i=0
        for item in ['init_assets','last_assets','volatility']:
            plt.text(i, 0.2, '{} : {} '.format(item,self.message[item]),fontsize=10, ha='left', rotation=0, wrap=True)
            i+=length/2.8
        i=0
        for item in ['alpha','beta','sharpe']:
            plt.text(i, 0.1, '{} : {}'.format(item,self.message[item]), ha='left', fontsize=10,rotation=0, wrap=True)
            i+=length/2.8
        #plt.figure(figsize=(length, height))
        plt.subplot(212)
        plt.style.use('ggplot')
        self.assets.plot()
        self.benchmark_assets.xs(self.benchmark_code, level=1).plot()

        asset_p = mpatches.Patch(
            color='red', label='{}'.format(self.account.account_cookie))
        asset_b = mpatches.Patch(
            label='benchmark {}'.format(self.benchmark_code))
        plt.legend(handles=[asset_p, asset_b], loc=1)
        plt.title('ASSET AND BENCKMARK')
        plt.show()
Esempio n. 6
0
class QA_Risk():
    """QARISK 是一个风险插件

    需要加载一个account/portfolio类进来:
    需要有
    code,start_date,end_date,daily_cash,daily_hold

    TODO:
    资金利用率 反应资金的利用程度
    股票周转率 反应股票的持仓天数
    预期PNL/统计学PNL
    """
    def __init__(self,
                 account,
                 benchmark_code='000300',
                 benchmark_type=MARKET_TYPE.INDEX_CN,
                 if_fq=True,
                 market_data=None):
        """
        account: QA_Account类/QA_PortfolioView类
        benchmark_code: [str]对照参数代码
        benchmark_type: [QA.PARAM]对照参数的市场
        if_fq: [Bool]原account是否使用复权数据
        if_fq选项是@尧提出的,关于回测的时候成交价格问题(如果按不复权撮合 应该按不复权价格计算assets)
        """
        self.account = account
        self.benchmark_code = benchmark_code  # 默认沪深300
        self.benchmark_type = benchmark_type

        self.fetch = {
            MARKET_TYPE.STOCK_CN: QA_fetch_stock_day_adv,
            MARKET_TYPE.INDEX_CN: QA_fetch_index_day_adv
        }
        if self.account.market_type == MARKET_TYPE.STOCK_CN:
            self.market_data = QA_fetch_stock_day_adv(self.account.code,
                                                      self.account.start_date,
                                                      self.account.end_date)
        elif self.account.market_type == MARKET_TYPE.FUTURE_CN:
            self.market_data = market_data
        self.if_fq = if_fq

        self._assets = (self.market_value.sum(axis=1) +
                        self.account.daily_cash.set_index('date').cash).fillna(
                            method='pad')

        self.time_gap = QA_util_get_trade_gap(self.account.start_date,
                                              self.account.end_date)
        self.init_cash = self.account.init_cash
        self.init_assets = self.account.init_assets

    def __repr__(self):
        return '< QA_RISK ANALYSIS ACCOUNT/PORTFOLIO >'

    def __call__(self):
        return pd.DataFrame([self.message])

    @property
    @lru_cache()
    def market_value(self):
        """每日每个股票持仓市值表

        Returns:
            pd.DataFrame -- 市值表
        """

        if self.if_fq:
            return self.market_data.to_qfq().pivot('close').fillna(
                method='ffill') * self.account.daily_hold
        else:
            return self.market_data.pivot('close').fillna(
                method='ffill') * self.account.daily_hold

    @property
    @lru_cache()
    def daily_market_value(self):
        """每日持仓总市值表

        Returns:
            pd.DataFrame -- 市值表
        """

        return self.market_value.sum(axis=1)

    @property
    def assets(self):
        x1 = self._assets.reset_index()
        return x1.assign(date=pd.to_datetime(x1.date)).set_index('date')[0]

    @property
    def max_dropback(self):
        """最大回撤
        """
        return round(
            float(
                max([(self.assets.iloc[idx] - self.assets.iloc[idx::].min()) /
                     self.assets.iloc[idx]
                     for idx in range(len(self.assets))])), 2)

    @property
    def total_commission(self):
        """总手续费
        """
        return -abs(round(self.account.history_table.commission.sum(), 2))

    @property
    def total_tax(self):
        """总印花税

        """

        return -abs(round(self.account.history_table.tax.sum(), 2))

    @property
    def profit_construct(self):
        """利润构成

        Returns:
            dict -- 利润构成表
        """

        return {
            'total_buyandsell':
            round(self.profit_money - self.total_commission - self.total_tax,
                  2),
            'total_tax':
            self.total_tax,
            'total_commission':
            self.total_commission,
            'total_profit':
            self.profit_money
        }

    @property
    def profit_money(self):
        """盈利额

        Returns:
            [type] -- [description]
        """

        return round(self.assets.iloc[-1] - self.init_cash, 2)

    @property
    def profit(self):
        """盈利率(百分比)

        Returns:
            [type] -- [description]
        """

        return round(float(self.calc_profit(self.assets)), 2)

    @property
    def profit_pct(self):
        """利润
        """
        return self.calc_profitpctchange(self.assets)

    @property
    def annualize_return(self):
        """年化收益

        Returns:
            [type] -- [description]
        """

        return round(
            float(self.calc_annualize_return(self.assets, self.time_gap)), 2)

    @property
    def volatility(self):
        """波动率

        Returns:
            [type] -- [description]
        """
        return round(float(self.profit_pct.std() * math.sqrt(250)), 2)

    @property
    @lru_cache()
    def message(self):
        return {
            'account_cookie': self.account.account_cookie,
            'portfolio_cookie': self.account.portfolio_cookie,
            'user_cookie': self.account.user_cookie,
            'annualize_return': round(self.annualize_return, 2),
            'profit': round(self.profit, 2),
            'max_dropback': self.max_dropback,
            'time_gap': self.time_gap,
            'volatility': self.volatility,
            'benchmark_code': self.benchmark_code,
            'bm_annualizereturn': self.benchmark_annualize_return,
            'bn_profit': self.benchmark_profit,
            'beta': self.beta,
            'alpha': self.alpha,
            'sharpe': self.sharpe,
            'init_cash': "%0.2f" % (float(self.init_cash)),
            'last_assets': "%0.2f" % (float(self.assets.iloc[-1])),
            'total_tax': self.total_tax,
            'total_commission': self.total_commission,
            'profit_money': self.profit_money

            # 'init_assets': round(float(self.init_assets), 2),
            # 'last_assets': round(float(self.assets.iloc[-1]), 2)
        }

    @property
    def benchmark_data(self):
        """
        基准组合的行情数据(一般是组合,可以调整)
        """
        return self.fetch[self.benchmark_type](self.benchmark_code,
                                               self.account.start_date,
                                               self.account.end_date)

    @property
    def benchmark_assets(self):
        """
        基准组合的账户资产队列
        """
        return (self.benchmark_data.close /
                float(self.benchmark_data.open.iloc[0]) *
                float(self.init_cash))

    @property
    def benchmark_profit(self):
        """
        基准组合的收益
        """
        return round(float(self.calc_profit(self.benchmark_assets)), 2)

    @property
    def benchmark_annualize_return(self):
        """基准组合的年化收益

        Returns:
            [type] -- [description]
        """

        return round(
            float(
                self.calc_annualize_return(self.benchmark_assets,
                                           self.time_gap)), 2)

    @property
    def benchmark_profitpct(self):
        """
        benchmark 基准组合的收益百分比计算
        """
        return self.calc_profitpctchange(self.benchmark_assets)

    @property
    def beta(self):
        """
        beta比率 组合的系统性风险
        """
        return round(
            float(
                self.calc_beta(self.profit_pct.dropna(),
                               self.benchmark_profitpct.dropna())), 2)

    @property
    def alpha(self):
        """
        alpha比率 与市场基准收益无关的超额收益率
        """
        return round(
            float(
                self.calc_alpha(self.annualize_return,
                                self.benchmark_annualize_return, self.beta,
                                0.05)), 2)

    @property
    def sharpe(self):
        """
        夏普比率

        """
        return round(
            float(
                self.calc_sharpe(self.annualize_return, self.volatility,
                                 0.05)), 2)

    @property
    def sortino(self):
        """ 
        索提诺比率 投资组合收益和下行风险比值

        """
        pass

    @property
    def calmar(self):
        """
        卡玛比率
        """
        pass

    def set_benchmark(self, code, market_type):
        self.benchmark_code = code
        self.benchmark_type = market_type

    def calc_annualize_return(self, assets, days):
        return round((float(assets.iloc[-1]) / float(assets.iloc[0]) - 1) /
                     (float(days) / 250), 2)

    def calc_profitpctchange(self, assets):
        return self.assets[::-1].pct_change()

    def calc_beta(self, assest_profit, benchmark_profit):

        calc_cov = np.cov(assest_profit, benchmark_profit)
        beta = calc_cov[0, 1] / calc_cov[1, 1]
        return beta

    def calc_alpha(self,
                   annualized_returns,
                   benchmark_annualized_returns,
                   beta,
                   r=0.05):

        alpha = (annualized_returns - r) - (beta) * \
            (benchmark_annualized_returns - r)
        return alpha

    def calc_profit(self, assets):
        """
        计算账户收益
        期末资产/期初资产 -1
        """
        return (float(assets.iloc[-1]) / float(self.init_cash)) - 1

    def calc_sharpe(self, annualized_returns, volatility_year, r=0.05):
        """
        计算夏普比率
        r是无风险收益
        """
        # 会出现0
        if volatility_year == 0:
            return 0
        return (annualized_returns - r) / volatility_year

    @property
    def max_holdmarketvalue(self):
        """最大持仓市值

        Returns:
            [type] -- [description]
        """

        return self.daily_market_value.max()

    @property
    def min_holdmarketvalue(self):
        """最小持仓市值

        Returns:
            [type] -- [description]
        """

        return self.daily_market_value.min()

    @property
    def average_holdmarketvalue(self):
        """平均持仓市值

        Returns:
            [type] -- [description]
        """

        return self.daily_market_value.mean()

    @property
    def max_cashhold(self):
        """最大闲置资金
        """

        return self.account.daily_cash.cash.max()

    @property
    def min_cashhold(self):
        """最小闲置资金
        """

        return self.account.daily_cash.cash.min()

    @property
    def average_cashhold(self):
        """平均闲置资金

        Returns:
            [type] -- [description]
        """

        return self.account.daily_cash.cash.mean()

    def save(self):
        """save to mongodb

        """
        save_riskanalysis(self.message)

    def plot_assets_curve(self, length=14, height=12):
        """
        资金曲线叠加图
        @Roy T.Burns 2018/05/29 修改百分比显示错误
        """
        plt.style.use('ggplot')
        plt.figure(figsize=(length, height))
        plt.subplot(211)
        plt.title('BASIC INFO', fontsize=12)
        plt.axis([0, length, 0, 0.6])
        plt.axis('off')
        i = 0
        for item in ['account_cookie', 'portfolio_cookie', 'user_cookie']:
            plt.text(i,
                     0.5,
                     '{} : {}'.format(item, self.message[item]),
                     fontsize=10,
                     rotation=0,
                     wrap=True)
            i += (length / 2.8)
        i = 0
        for item in ['benchmark_code', 'time_gap', 'max_dropback']:
            plt.text(i,
                     0.4,
                     '{} : {}'.format(item, self.message[item]),
                     fontsize=10,
                     ha='left',
                     rotation=0,
                     wrap=True)
            i += (length / 2.8)
        i = 0
        for item in ['annualize_return', 'bm_annualizereturn', 'profit']:
            plt.text(i,
                     0.3,
                     '{} : {} %'.format(item,
                                        self.message.get(item, 0) * 100),
                     fontsize=10,
                     ha='left',
                     rotation=0,
                     wrap=True)
            i += length / 2.8
        i = 0
        for item in ['init_cash', 'last_assets', 'volatility']:
            plt.text(i,
                     0.2,
                     '{} : {} '.format(item, self.message[item]),
                     fontsize=10,
                     ha='left',
                     rotation=0,
                     wrap=True)
            i += length / 2.8
        i = 0
        for item in ['alpha', 'beta', 'sharpe']:
            plt.text(i,
                     0.1,
                     '{} : {}'.format(item, self.message[item]),
                     ha='left',
                     fontsize=10,
                     rotation=0,
                     wrap=True)
            i += length / 2.8
        plt.subplot(212)
        self.assets.plot()
        self.benchmark_assets.xs(self.benchmark_code, level=1).plot()

        asset_p = mpatches.Patch(color='red',
                                 label='{}'.format(
                                     self.account.account_cookie))
        asset_b = mpatches.Patch(
            label='benchmark {}'.format(self.benchmark_code))
        plt.legend(handles=[asset_p, asset_b], loc=1)
        plt.title('ASSET AND BENCKMARK')

        return plt

    def plot_dailyhold(self, start=None, end=None):
        """
        使用热力图画出每日持仓
        """
        start = self.account.start_date if start is None else start
        end = self.account.end_date if end is None else end
        _, ax = plt.subplots(figsize=(20, 8))
        sns.heatmap(self.account.daily_hold.reset_index().drop(
            'account_cookie', axis=1).set_index('date').loc[start:end],
                    cmap="YlGnBu",
                    linewidths=0.05,
                    ax=ax)
        ax.set_title('HOLD TABLE --ACCOUNT: {}'.format(
            self.account.account_cookie))
        ax.set_xlabel('Code')
        ax.set_ylabel('DATETIME')

        return plt

    def plot_signal(self, start=None, end=None):
        """
        使用热力图画出买卖信号
        """
        start = self.account.start_date if start is None else start
        end = self.account.end_date if end is None else end
        _, ax = plt.subplots(figsize=(20, 18))
        sns.heatmap(self.account.trade.reset_index().drop(
            'account_cookie', axis=1).set_index('datetime').loc[start:end],
                    cmap="YlGnBu",
                    linewidths=0.05,
                    ax=ax)
        ax.set_title('SIGNAL TABLE --ACCOUNT: {}'.format(
            self.account.account_cookie))
        ax.set_xlabel('Code')
        ax.set_ylabel('DATETIME')
        return plt

    def generate_plots(self):
        """
        生成图像
        """
        self.plot_assets_curve()
        self.plot_dailyhold()
        self.plot_signal()
Esempio n. 7
0
class QA_Risk():
    """QARISK 是一个风险插件

    需要加载一个account/portfolio类进来:
    需要有
    code,start_date,end_date,daily_cash,daily_hold
    """
    def __init__(self,
                 account,
                 benchmark_code='000300',
                 benchmark_type=MARKET_TYPE.INDEX_CN):
        self.account = account
        self.benchmark_code = benchmark_code  # 默认沪深300
        self.benchmark_type = benchmark_type

        self.fetch = {
            MARKET_TYPE.STOCK_CN: QA_fetch_stock_day_adv,
            MARKET_TYPE.INDEX_CN: QA_fetch_index_day_adv
        }
        self.market_data = QA_fetch_stock_day_adv(self.account.code,
                                                  self.account.start_date,
                                                  self.account.end_date)

        self.assets = ((self.market_data.to_qfq().pivot('close') *
                        self.account.daily_hold).sum(axis=1) +
                       self.account.daily_cash.set_index('date').cash).fillna(
                           method='pad')

        self.time_gap = QA_util_get_trade_gap(self.account.start_date,
                                              self.account.end_date)
        self.init_assets = self.account.init_assets

    def __repr__(self):
        return '< QA_RISK ANALYSIS ACCOUNT/PORTFOLIO >'

    def __call__(self):
        return pd.DataFrame([self.message])

    @property
    def max_dropback(self):
        """最大回撤
        """
        return max([
            self.assets.iloc[idx::].max() - self.assets.iloc[idx::].min()
            for idx in range(len(self.assets))
        ]) / float(self.assets.iloc[0])

    @property
    def profit(self):
        return self.calc_profit(self.assets)

    @property
    def profit_pct(self):
        """利润
        """
        return self.calc_profitpctchange(self.assets)

    @property
    def annualize_return(self):
        """年化收益

        Returns:
            [type] -- [description]
        """

        return self.calc_annualize_return(self.assets, self.time_gap)

    @property
    def volatility(self):
        """波动率

        Returns:
            [type] -- [description]
        """
        return self.profit_pct.std() * math.sqrt(250)

    @property
    def message(self):
        return {
            'account_cookie': self.account.account_cookie,
            'portfolio_cookie': self.account.portfolio_cookie,
            'user_cookie': self.account.user_cookie,
            'annualize_return': self.annualize_return,
            'profit': self.profit,
            'max_dropback': self.max_dropback,
            'time_gap': self.time_gap,
            'volatility': self.volatility,
            'benchmark_code': self.benchmark_code,
            'beta': self.beta,
            'alpha': self.alpha,
            'sharpe': self.sharpe,
            'init_assets': self.init_assets,
            'last_assets': self.assets.iloc[-1]
        }

    @property
    def benchmark_data(self):
        """
        基准组合的行情数据(一般是组合,可以调整)
        """
        return self.fetch[self.benchmark_type](self.benchmark_code,
                                               self.account.start_date,
                                               self.account.end_date)

    @property
    def benchmark_assets(self):
        """
        基准组合的账户资产队列
        """
        return (self.benchmark_data.open /
                float(self.benchmark_data.open.iloc[0]) *
                float(self.init_assets))

    @property
    def benchmark_annualize_return(self):
        """基准组合的年化收益

        Returns:
            [type] -- [description]
        """

        return self.calc_annualize_return(self.benchmark_assets, self.time_gap)

    @property
    def benchmark_profitpct(self):
        """
        benchmark 基准组合的收益百分比计算
        """
        return self.calc_profitpctchange(self.benchmark_assets)

    @property
    def beta(self):
        """
        beta比率 组合的系统性风险
        """
        return self.calc_beta(self.profit_pct.dropna(),
                              self.benchmark_profitpct.dropna())

    @property
    def alpha(self):
        """
        alpha比率 与市场基准收益无关的超额收益率
        """
        return self.calc_alpha(self.annualize_return,
                               self.benchmark_annualize_return, self.beta,
                               0.05)

    @property
    def sharpe(self):
        """
        夏普比率

        """
        return self.calc_sharpe(self.annualize_return, self.volatility, 0.05)

    @property
    def sortino(self):
        """ 
        索提诺比率 投资组合收益和下行风险比值

        """
        pass

    @property
    def calmar(self):
        """
        卡玛比率
        """
        pass

    def set_benchmark(self, code, market_type):
        self.benchmark_code = code
        self.benchmark_type = market_type

    def calc_annualize_return(self, assets, days):
        return (float(assets.iloc[-1]) / float(assets.iloc[0]) -
                1) / (float(days) / 250)

    def calc_profitpctchange(self, assets):
        return self.assets[::-1].pct_change()

    def calc_beta(self, assest_profit, benchmark_profit):

        calc_cov = np.cov(assest_profit, benchmark_profit)
        beta = calc_cov[0, 1] / calc_cov[1, 1]
        return beta

    def calc_alpha(self,
                   annualized_returns,
                   benchmark_annualized_returns,
                   beta,
                   r=0.05):

        alpha = (annualized_returns - r) - (beta) * \
            (benchmark_annualized_returns - r)
        return alpha

    def calc_profit(self, assets):
        """
        计算账户收益
        期末资产/期初资产 -1
        """
        return (float(assets.iloc[-1]) / float(assets.iloc[0])) - 1

    def calc_sharpe(self, annualized_returns, volatility_year, r=0.05):
        """
        计算夏普比率
        r是无风险收益
        """
        return (annualized_returns - r) / volatility_year

    def save(self):
        """save to mongodb

        """
        save_riskanalysis(self.message)
Esempio n. 8
0
class QA_Risk():
    """QARISK 是一个风险插件

    """
    def __init__(self, account):
        self.account = account
        self.benchmark = None

        self.fetch = {
            MARKET_TYPE.STOCK_CN: QA_fetch_stock_day_adv,
            MARKET_TYPE.INDEX_CN: QA_fetch_index_day_adv
        }
        self.market_data = QA_fetch_stock_day_adv(self.account.code,
                                                  self.account.start_date,
                                                  self.account.end_date)

        self.assets = ((self.market_data.to_qfq().pivot('close') *
                        self.account.daily_hold).sum(axis=1) +
                       self.account.daily_cash.set_index('date').cash).fillna(
                           method='pad')

        self.time_gap = QA_util_get_trade_gap(self.account.start_date,
                                              self.account.end_date)

    def __repr__(self):
        return '< QA_RISK ANALYSIS ACCOUNT-{} >'.format(
            self.account.account_cookie)

    def __call__(self):
        return pd.DataFrame([self.message])

    @property
    def max_dropback(self):
        """最大回撤
        """
        return max([
            self.assets.iloc[idx::].max() - self.assets.iloc[idx::].min()
            for idx in range(len(self.assets))
        ]) / float(self.assets.iloc[0])

    @property
    def profit(self):
        """利润
        """
        return (float(self.assets.iloc[-1]) / float(self.assets.iloc[0])) - 1

    @property
    def annualize_return(self):
        """年化收益

        Returns:
            [type] -- [description]
        """

        return math.pow(
            float(self.assets.iloc[-1]) / float(self.assets.iloc[0]),
            250.0 / float(self.time_gap)) - 1.0

    @property
    def volatility(self):
        """波动率

        Returns:
            [type] -- [description]
        """

        return self.assets.diff().std()

    @property
    def message(self):
        return {
            'account_cookie': self.account.account_cookie,
            'portfolio_cookie': self.account.portfolio_cookie,
            'user_cookie': self.account.user_cookie,
            'annualize_return': self.annualize_return,
            'profit': self.profit,
            'max_dropback': self.max_dropback,
            'time_gap': self.time_gap,
            'volatility': self.volatility
        }

    def set_benchmark(self, code, market_type):
        self.benchmark = self.fetch[market_type](code, self.account.start_date,
                                                 self.account.end_date)
Esempio n. 9
0
class QA_Risk():
    """QARISK 是一个风险插件

    """
    def __init__(self,
                 account,
                 benchmark_code='000300',
                 benchmark_type=MARKET_TYPE.INDEX_CN):
        self.account = account
        self.benchmark_code = benchmark_code  # 默认沪深300
        self.benchmark_type = benchmark_type

        self.fetch = {
            MARKET_TYPE.STOCK_CN: QA_fetch_stock_day_adv,
            MARKET_TYPE.INDEX_CN: QA_fetch_index_day_adv
        }
        self.market_data = QA_fetch_stock_day_adv(self.account.code,
                                                  self.account.start_date,
                                                  self.account.end_date)

        self.assets = ((self.market_data.to_qfq().pivot('close') *
                        self.account.daily_hold).sum(axis=1) +
                       self.account.daily_cash.set_index('date').cash).fillna(
                           method='pad')

        self.time_gap = QA_util_get_trade_gap(self.account.start_date,
                                              self.account.end_date)

    def __repr__(self):
        return '< QA_RISK ANALYSIS ACCOUNT-{} >'.format(
            self.account.account_cookie)

    def __call__(self):
        return pd.DataFrame([self.message])

    @property
    def max_dropback(self):
        """最大回撤
        """
        return max([
            self.assets.iloc[idx::].max() - self.assets.iloc[idx::].min()
            for idx in range(len(self.assets))
        ]) / float(self.assets.iloc[0])

    @property
    def profit(self):
        return self.calc_profit(self.assets)

    @property
    def profit_pct(self):
        """利润
        """
        return self.calc_profitpctchange(self.assets)

    @property
    def annualize_return(self):
        """年化收益

        Returns:
            [type] -- [description]
        """

        return self.calc_annualize_return(self.assets, self.time_gap)

    @property
    def volatility(self):
        """波动率

        Returns:
            [type] -- [description]
        """
        return self.profit_pct.std() * math.sqrt(250)

    @property
    def message(self):
        return {
            'account_cookie': self.account.account_cookie,
            'portfolio_cookie': self.account.portfolio_cookie,
            'user_cookie': self.account.user_cookie,
            'annualize_return': self.annualize_return,
            'profit': self.profit,
            'max_dropback': self.max_dropback,
            'time_gap': self.time_gap,
            'volatility': self.volatility,
            'benchmark_code': self.benchmark_code,
            'beta': self.beta,
            'alpha': self.alpha,
            'sharpe': self.sharpe
        }

    @property
    def benchmark_data(self):
        return self.fetch[self.benchmark_type](self.benchmark_code,
                                               self.account.start_date,
                                               self.account.end_date)

    @property
    def benchmark_assets(self):
        return (self.benchmark_data.open /
                float(self.benchmark_data.open.iloc[0]) *
                float(self.account.init_assets))

    @property
    def benchmark_annualize_return(self):
        """年化收益

        Returns:
            [type] -- [description]
        """

        return self.calc_annualize_return(self.benchmark_assets, self.time_gap)

    @property
    def benchmark_profitpct(self):
        return self.calc_profitpctchange(self.benchmark_assets)

    @property
    def beta(self):
        return self.calc_beta(self.profit_pct.dropna(),
                              self.benchmark_profitpct.dropna())

    @property
    def alpha(self):
        return self.calc_alpha(self.annualize_return,
                               self.benchmark_annualize_return, self.beta,
                               0.05)

    @property
    def sharpe(self):
        return self.calc_sharpe(self.annualize_return, self.volatility, 0.05)

    def set_benchmark(self, code, market_type):
        self.benchmark_code = code
        self.benchmark_type = market_type

    def calc_annualize_return(self, assets, days):
        return math.pow(
            float(assets.iloc[-1]) / float(assets.iloc[0]),
            250.0 / float(days)) - 1.0

    # def calc_profit(self, assets):
    #     return (assets.iloc[-1] / assets.iloc[1]) - 1

    def calc_profitpctchange(self, assets):
        return self.assets[::-1].pct_change()

    def calc_beta(self, assest_profit, benchmark_profit):

        calc_cov = np.cov(assest_profit, benchmark_profit)
        beta = calc_cov[0, 1] / calc_cov[1, 1]
        return beta

    def calc_alpha(self,
                   annualized_returns,
                   benchmark_annualized_returns,
                   beta,
                   r=0.05):

        alpha = (annualized_returns - r) - (beta) * \
            (benchmark_annualized_returns - r)
        return alpha

    def calc_profit(self, assets):
        return (float(assets.iloc[-1]) / float(assets.iloc[0])) - 1

    def calc_sharpe(self, annualized_returns, volatility_year, r=0.05):
        '计算夏普比率'
        return (annualized_returns - r) / volatility_year

    def save(self):
        """save to mongodb
        
        """
        save_riskanalysis(self.message)
Esempio n. 10
0
class QA_Risk():
    def __init__(self, account):
        self.account = account
        self.benchmark = None

        self.fetch = {
            MARKET_TYPE.STOCK_CN: QA_fetch_stock_day_adv,
            MARKET_TYPE.INDEX_CN: QA_fetch_index_day_adv
        }
        self.market_data = QA_fetch_stock_day_adv(self.account.code,
                                                  self.account.start_date,
                                                  self.account.end_date)

        self.assets = ((self.market_data.to_qfq().pivot('close') *
                        self.account.daily_hold).sum(axis=1) +
                       self.account.daily_cash.set_index('date').cash).fillna(
                           method='pad')

        self.time_gap = QA_util_get_trade_gap(self.account.start_date,
                                              self.account.end_date)

    @property
    def max_dropback(self):
        """最大回撤
        """
        return max([
            self.assets.iloc[idx::].max() - self.assets.iloc[idx::].min()
            for idx in range(len(self.assets))
        ]) / float(self.assets.iloc[0])

    @property
    def profit(self):
        """利润
        """
        return (float(self.assets.iloc[-1]) / float(self.assets.iloc[0])) - 1

    @property
    def annualize_return(self):
        """年化收益

        Returns:
            [type] -- [description]
        """

        return math.pow(
            float(self.assets.iloc[-1]) / float(self.assets.iloc[0]),
            250.0 / float(self.time_gap)) - 1.0

    @property
    def volatility(self):
        """波动率

        Returns:
            [type] -- [description]
        """

        return self.assets.diff().std()

    def set_benchmark(self, code, market_type):
        self.benchmark = self.fetch[market_type](code, self.account.start_date,
                                                 self.account.end_date)