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
Esempio n. 2
0
 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)))
Esempio n. 3
0
 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)
Esempio n. 4
0
 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
Esempio n. 6
0
 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
Esempio n. 7
0
    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
Esempio n. 8
0
 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