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)
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)
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()
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()
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()
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()
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)
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)
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)
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)