def calc_instrument_vol_cone(db_session, instrument_id, start_date, end_date, windows, percentiles, is_primary=False): origin_start_date = start_date holidays = TradingDayService.get_holiday_list(db_session) # 判断是否时节假日 if DateTimeUtils.is_trading_day(end_date, holidays): start_date = TradingDayService.go_trading_day(db_session, start_date, max(windows) - 1, direction=-1) else: start_date = TradingDayService.go_trading_day(db_session, start_date, max(windows), direction=-1) quote_close_dto_dict, diagnostics = QuoteCloseService.\ get_instrument_quote_close_dict_by_period(db_session, instrument_id, start_date, end_date, is_primary) vol_cone_dto_list = HistoricalVolService.\ calc_multiple_window_percentiles_by_quotes(origin_start_date, end_date, quote_close_dto_dict, windows, percentiles) return vol_cone_dto_list, diagnostics
def check_date_params(db_session, start_date, end_date, window): if start_date > date.today() or end_date > date.today(): raise CustomException('所选日期不能超过今天') if TradingDayService.get_effective_days_num(db_session, start_date, end_date) < window: raise CustomException('所选日期范围[%s,%s]交易日个数小于窗口大小,无法进行计算' % (DateTimeUtils.date2str(start_date), DateTimeUtils.date2str(end_date)))
async def get_heat_map(self, trade_date=None): with self.make_session() as db_session: if trade_date is None: trade_date = datetime.now().date() # 求上一个交易日 holidays = TradingDayService.get_holiday_list(db_session) trade_date = DateTimeUtils.get_trading_day(trade_date, holidays, special_dates=[], step=2, direction=-1) heat_map_dto_list, diagnostics = OtcAtmQuoteService.get_heat_map(db_session, trade_date) heat_map_schema = HeatMapSchema(many=True, exclude=[]) return DiagnosticResponse(heat_map_schema.dump(heat_map_dto_list).data, diagnostics)
def update_eod_date(start_date_str=EODDateConfig.eod_start_date, end_date_str=DateTimeUtils.date2str(date.today()), eod_cutoff_time=EODDateConfig.eod_cutoff_time): end_date = DateTimeUtils.str2date(end_date_str) start_date = DateTimeUtils.str2date(start_date_str) # 获取节假日信息 db_session = create_db_session() # TODO: 需要从配置文件读取 if not TradingDayService.is_trading_day(db_session, end_date): # 如果当天不是交易日则直接计算上一个交易日的值 end_date = TradingDayService.go_trading_day(db_session, end_date, 1, -1) else: # 如果是交易日,小于cutoff time还是返回上一个交易日 if datetime.now().time() < DateTimeUtils.str2time(eod_cutoff_time): end_date = TradingDayService.go_trading_day(db_session, end_date, 1, -1) else: end_date = end_date # 设置变量到airflow variable AirflowVariableUtils.set_eod_start_date(start_date) AirflowVariableUtils.set_eod_end_date(end_date)
def calc_instrument_rolling_vol(db_session, instrument_id, start_date, end_date, window, is_primary=False): holidays = TradingDayService.get_holiday_list(db_session) # 判断是否时节假日 if DateTimeUtils.is_trading_day(end_date, holidays): start_date = TradingDayService.go_trading_day(db_session, start_date, window - 1, direction=-1) else: start_date = TradingDayService.go_trading_day(db_session, start_date, window, direction=-1) quote_close_dto_dict, diagnostics = QuoteCloseService.get_instrument_quote_close_dict_by_period( db_session, instrument_id, start_date, end_date, is_primary) realized_vol_dto_list = HistoricalVolService.calc_one_rolling_vol_by_quotes( quote_close_dto_dict, window) return realized_vol_dto_list, diagnostics
def calc_instrument_atm_vol_list(db_session, underlyer, start_date, end_date, is_primary): atm_quote_list = OtcAtmQuoteService.get_instrument_atm_quote_list_by_period( db_session, underlyer, start_date, end_date, is_primary) # TODO:去掉重复数据,相同valuation date, underlyer, source, expire date的数据需要去重 # 根据valuation date进行分类 atm_quote_dict = {} for atm_quote in atm_quote_list: if atm_quote.valuationDate not in atm_quote_dict: atm_quote_dict[atm_quote.valuationDate] = [] atm_quote_dict[atm_quote.valuationDate].append(atm_quote) # 取一个自然月后到期日的前后3天的ask_vol和bid_vol的平均值作为当天的atm_vol atm_vol_dict = {} for valuation_date in atm_quote_dict: quote_list = atm_quote_dict[valuation_date] atm_vol = OtcAtmQuoteService.calc_one_atm_vol( valuation_date, quote_list) if atm_vol is not None: atm_vol_dict[valuation_date] = atm_vol # 检测哪些日期缺失 missing_dates = [] holidays = TradingDayService.get_holiday_list(db_session, start_date, end_date) trade_date = start_date while trade_date <= end_date: if DateTimeUtils.is_trading_day(trade_date, holidays): if trade_date not in atm_vol_dict: # 将缺失的日期添加到诊断中 missing_dates.append(trade_date) # TODO: 是否需要将缺失的数据按0补足 trade_date += timedelta(days=1) # 添加诊断信息 diagnostics = [] if len(missing_dates) != 0: message = '标的物%s在[%s, %s]时段内中缺失%d条ATM Vol数据' % ( underlyer, DateTimeUtils.date2str(start_date), DateTimeUtils.date2str(end_date), len(missing_dates)) diagnostics.append( DiagnosticDTO(DiagnosticType.WARNING, message, missing_dates)) logging.error(message) return atm_vol_dict.values(), diagnostics
def calc_instrument_otc_implied_vol_points( db_session, instrument, trade_date, strike_type, contract_order=FutureContractOrder.PRIMARY.name, percents=[0.8, 0.9, 0.95, 1, 1.05, 1.1, 1.2]): last_trade_date = trade_date logging.info("现在计算%s的vol surface" % instrument) quote_close_instrument = QuoteCloseService.get_instrument_quote_close_list_by_period( db_session, instrument, last_trade_date, last_trade_date, is_primary=True)[0] instrument = InstrumentRepo.get_instrument(db_session, instrument) if not quote_close_instrument: raise Exception('trading_date: %s, instrument_id: %s的收盘价信息为空' % (instrument.instrumentId, last_trade_date)) if not quote_close_instrument.closePrice: raise Exception('标的instrument_id: %s,trading_date: %s没有收盘价' % (instrument.instrumentId, last_trade_date)) if strike_type is None: strike_type = VolSurfaceStrikeType.PERCENT if strike_type == VolSurfaceStrikeType.PERCENT: # 根据 percentage 计算vor grid percents = percents elif strike_type == VolSurfaceStrikeType.STRIKE: # 根据 strike 计算 vor grid ,需要获取instrument last_trade_date时的spot_price 也就是 quote_close表的close_price percents = [ quote_close_instrument.closePrice * i for i in percents ] instrument_id = instrument.instrumentId start_date = TradingDayService.go_trading_day(db_session, last_trade_date, 132, -1) end_date = last_trade_date default_windows = [44, 66, 132] default_percentiles = [50] realised_vol_dto_list, realised_vol_log = HistoricalVolService.calc_instrument_realized_vol( db_session, instrument_id, last_trade_date, [1, 3, 5, 10, 22], contract_order) if realised_vol_log: logging.warning(realised_vol_log) realised_vol = {} for item in realised_vol_dto_list: realised_vol[item.window] = item.vol rolling_vol_dto_list, rolling_vol_log = HistoricalVolService.calc_instrument_rolling_vol( db_session, instrument_id, start_date, end_date, 22, contract_order) if rolling_vol_log: logging.warning(rolling_vol_log) vol_cone_dto_list, vol_cone_log = HistoricalVolService.calc_instrument_vol_cone( db_session, instrument_id, start_date, end_date, default_windows, default_percentiles, contract_order) if vol_cone_log: logging.warning(vol_cone_log) vol_cone = {} for item in vol_cone_dto_list: vol_cone[item.window] = {} for vol_item in item.vols: vol_cone[item.window][vol_item.percentile] = vol_item.vol # 按日期从小到大排序 rolling_vol_list = [ item.vol for item in sorted( rolling_vol_dto_list, key=lambda x: x.tradeDate, reverse=True) ] vol_surface = { '2W': (realised_vol[1] * 40 + realised_vol[3] * 20 + realised_vol[5] * 20 + realised_vol[10] * 10 + realised_vol[22] * 10) / 100, '1M': ((sum(rolling_vol_list) / len(rolling_vol_list)) * 30 + realised_vol[1] * 30 + realised_vol[3] * 20 + realised_vol[5] * 20) / 100, '2M': (vol_cone[44][50] * 60 + realised_vol[1] * 10 + realised_vol[22] * 30) / 100, '3M': (vol_cone[66][50] * 80 + realised_vol[1] * 10 + realised_vol[22] * 10) / 100, '6M': (vol_cone[132][50] * 90 + realised_vol[1] * 5 + realised_vol[22] * 5) / 100 } dto_vol_surface = VolSurfaceDTO() dto_vol_surface.instrumentId = instrument_id dto_vol_surface.valuationDate = last_trade_date dto_vol_surface.strikeType = strike_type.name dto_vol_surface.instance = InstanceType.INTRADAY.name # TODO WHAT IS T-A-G? # dto_vol_surface.tag = # get model_info dto_model_info = VolSurfaceModelInfoDTO() dto_model_info.daysInYear = 245 dto_model_info.modelName = 'TRADER_VOL' dto_underlyer = VolSurfaceUnderlyerDTO() dto_underlyer.instrumentId = instrument_id dto_underlyer.quote = quote_close_instrument.closePrice # dto_underlyer.field = dto_underlyer.instance = InstanceType.INTRADAY.name dto_model_info.underlyer = dto_underlyer dto_model_info.save = True dto_vol_grids = OtcModelService.calc_vol_grid_item( vol_surface, percents) dto_vol_grid_list = [] for tenor, quote in vol_surface.items(): dto_vol_grid = VolGridItemDTO() # vol_grid_item.expiry = dto_vol_grid.tenor = tenor dto_vol_grid.vols = [ x.vols for x in dto_vol_grids if x.tenor == tenor ][0] dto_vol_grid_list.append(dto_vol_grid) dto_model_info.instruments = dto_vol_grid_list dto_vol_surface.model_name = 'TRADER_VOL' dto_vol_surface.field = 'LAST' dto_vol_surface.modelInfo = dto_model_info dto_fitting_model_list = [] for item in dto_model_info.instruments: dto_fitting_model = FittingModelDTO() dto_fitting_model.scatter = item.vols dto_fitting_model.daysInYear = dto_model_info.daysInYear dto_fitting_model_list.append(dto_fitting_model) dto_vol_surface.fittingModels = dto_fitting_model_list return dto_vol_surface
def get_instrument_vol_surface(db_session, instrument_id, valuation_date, strike_type, instance, is_primary): diagnostic = None origin_instrument_id = instrument_id # 如果不是交易日,则直接返回上一个交易日的波动率曲面 if not TradingDayService.is_trading_day(db_session, valuation_date): valuation_date = TradingDayService.go_trading_day(db_session, valuation_date, step=1, direction=-1) # 如果传入大宗的合约类型, 直接返回主力结果; 如果是具体标的, 看is_primary是否需要主力 instrument_id = InstrumentService.get_primary_instrument_id( db_session, instrument_id, valuation_date, is_primary) # 判断是要百分比还是绝对值的波动率曲面 if strike_type == VolSurfaceStrikeType.PERCENT.name: vol_surface_dbo = ImpliedVolService.get_official_vol_surface( db_session, instrument_id, valuation_date, strike_type, instance) if vol_surface_dbo is None: message = '标的物%s在%s中时没有波动率曲面' % (instrument_id, valuation_date) diagnostic = DiagnosticDTO(DiagnosticType.WARNING, message, instrument_id) return ImpliedVolService.to_dto(vol_surface_dbo), diagnostic else: vol_surface_dbo = ImpliedVolService.get_official_vol_surface( db_session, instrument_id, valuation_date, strike_type, instance) if vol_surface_dbo is not None: return ImpliedVolService.to_dto(vol_surface_dbo), diagnostic # 如果取不到Strike的波动率曲面,则尝试取Percent的波动率曲面 strike_type = VolSurfaceStrikeType.PERCENT.name vol_surface_dbo = ImpliedVolService.get_official_vol_surface( db_session, instrument_id, valuation_date, strike_type, instance) if vol_surface_dbo is None: # 如果波动率曲面为空,不需要再获取收盘价 message = '标的物%s在%s中时没有波动率曲面' % (instrument_id, valuation_date) diagnostic = DiagnosticDTO(DiagnosticType.WARNING, message, instrument_id) return ImpliedVolService.to_dto(vol_surface_dbo), diagnostic if InstrumentService.is_commodity_variety_type( db_session, origin_instrument_id): # 如果传入的是合约类型, 获取收盘价时使用主力合约标的id的收盘价 quote_close = QuoteCloseRepo.get_instrument_quote_close_by_date( db_session, instrument_id, valuation_date) else: # 如果传入的是标的id, 获取收盘价时使用传入的原始标的id quote_close = QuoteCloseRepo.get_instrument_quote_close_by_date( db_session, origin_instrument_id, valuation_date) if quote_close is None or quote_close.closePrice is None: message = '标的物%s在%s中时有波动率曲面, 但没有收盘价' % (instrument_id, valuation_date) diagnostic = DiagnosticDTO(DiagnosticType.WARNING, message, instrument_id) return ImpliedVolService.to_dto(None), diagnostic # 收盘价和波动率曲面不为空时,将Percent换算成Strike vol_surface_dto = ImpliedVolService.to_dto(vol_surface_dbo) vol_surface_dto.modelInfo.underlyer.quote = quote_close.closePrice for instrument in vol_surface_dto.modelInfo.instruments: for vol in instrument.vols: vol.strike = quote_close.closePrice * vol.percent # fittingModels可能为空 if vol_surface_dto.fittingModels: for fitting_model in vol_surface_dto.fittingModels: if fitting_model is None: continue for _ in fitting_model.scatter: _.strike = quote_close.closePrice * _.percent return vol_surface_dto, diagnostic