예제 #1
0
    def _filter_sec_on_tiaocang_date(self, tiaocang_date, sec_id):
        sse_cal = Calendar('China.SSE')
        tiaocang_date_prev = sse_cal.advanceDate(
            Date.strptime(str(tiaocang_date)[:10]),
            Period('-1b')).toDateTime()
        tiaocang_date_prev2 = sse_cal.advanceDate(
            Date.strptime(str(tiaocang_date)[:10]),
            Period('-2b')).toDateTime()
        price_data = get_sec_price(start_date=tiaocang_date_prev2,
                                   end_date=tiaocang_date,
                                   sec_ids=sec_id,
                                   data_source=self._data_source,
                                   csv_path=self._csv_path)
        price_data = price_data.transpose()
        price_data.index.name = 'secID'
        # 去除涨幅过大可能买不到的
        price_data['returnFilter'] = price_data[tiaocang_date] / price_data[
            tiaocang_date_prev] > 1 + self._filter_return_on_tiaocang_date
        # 去除有NaN的, 新股
        price_data['ipoFilter'] = pd.isnull(price_data[tiaocang_date] *
                                            price_data[tiaocang_date_prev] *
                                            price_data[tiaocang_date_prev2])
        # 去除停牌的,此处判断标准就是连续三天收盘价格一样
        price_data['tingpaiFilter'] = (
            (price_data[tiaocang_date] == price_data[tiaocang_date_prev]) &
            (price_data[tiaocang_date_prev]
             == price_data[tiaocang_date_prev2]))

        price_data['filters'] = 1 - (1 - price_data['returnFilter']) * (
            1 - price_data['ipoFilter']) * (1 - price_data['tingpaiFilter'])

        return price_data['filters']
def makeSchedule(firstDate,
                 endDate,
                 tenor,
                 calendar='NullCalendar',
                 dateRule=BizDayConventions.Following,
                 dateGenerationRule=DateGeneration.Forward):

    cal = Calendar(calendar)
    firstDate = check_date(firstDate)
    endDate = check_date(endDate)
    tenor = check_period(tenor)

    if tenor.units() == TimeUnits.BDays:
        schedule = []
        if dateGenerationRule == DateGeneration.Forward:
            d = cal.adjustDate(firstDate, dateRule)
            while d <= endDate:
                schedule.append(d)
                d = cal.advanceDate(d, tenor, dateRule)
        elif dateGenerationRule == DateGeneration.Backward:
            d = cal.adjustDate(endDate, dateRule)
            while d >= firstDate:
                schedule.append(d)
                d = cal.advanceDate(d, -tenor, dateRule)
            schedule = sorted(schedule)
    else:
        schedule = Schedule(firstDate,
                            endDate,
                            tenor,
                            cal,
                            convention=dateRule,
                            dateGenerationRule=dateGenerationRule)
    return [d.toDateTime() for d in schedule]
예제 #3
0
 def forward_date(date, tenor, date_format='%Y-%m-%d'):
     try:
         # use pyfin instead to get more accurate and flexible date math
         start_date = Date.strptime(date, date_format)
         sseCal = Calendar('China.SSE')
         ret = sseCal.advanceDate(start_date, Period('-' + tenor), endOfMonth=True)
         # 此处返回的是上一期期末日期,再向后调整一天,以避免区间日期重叠
         ret = sseCal.advanceDate(ret, Period('1b'))
         return str(ret)
     except NameError:
         pass
예제 #4
0
def advanceDateByCalendar(holidayCenter,
                          referenceDate,
                          period,
                          convention=BizDayConventions.Following):
    cal = Calendar(holidayCenter)
    refer = check_date(referenceDate)
    return cal.advanceDate(refer, period, convention).toDateTime()
예제 #5
0
    def testAdvanceDate(self):
        referenceDate = Date(2014, 1, 31)
        sseCal = Calendar('China.SSE')
        ibCal = Calendar('China.IB')

        bizDayConv = BizDayConventions.Following

        # test null period
        self.assertEqual(sseCal.advanceDate(referenceDate, '0b', bizDayConv),
                         Date(2014, 2, 7))

        # test negative period
        self.assertEqual(sseCal.advanceDate(referenceDate, '-5b', bizDayConv),
                         Date(2014, 1, 24))

        # The difference is caused by Feb 8 is SSE holiday but a working day for IB market
        self.assertEqual(sseCal.advanceDate(referenceDate, '2b', bizDayConv),
                         Date(2014, 2, 10))
        self.assertEqual(sseCal.advanceDate(referenceDate, '2d', bizDayConv),
                         Date(2014, 2, 7))
        self.assertEqual(ibCal.advanceDate(referenceDate, '2b', bizDayConv),
                         Date(2014, 2, 8))
        self.assertEqual(ibCal.advanceDate(referenceDate, '2d', bizDayConv),
                         Date(2014, 2, 7))

        bizDayConv = BizDayConventions.ModifiedFollowing
        # May 31, 2014 is a holiday
        self.assertEqual(
            sseCal.advanceDate(referenceDate, '4m', bizDayConv, True),
            Date(2014, 5, 30))
예제 #6
0
    def testAdvanceDate(self):
        referenceDate = Date(2014, 1, 31)
        sseCal = Calendar('China.SSE')
        ibCal = Calendar('China.IB')

        bizDayConv = BizDayConventions.Following

        # test null period
        self.assertEqual(sseCal.advanceDate(referenceDate, '0b', bizDayConv), Date(2014, 2, 7))

        # test negative period
        self.assertEqual(sseCal.advanceDate(referenceDate, '-5b', bizDayConv), Date(2014, 1, 24))

        # The difference is caused by Feb 8 is SSE holiday but a working day for IB market
        self.assertEqual(sseCal.advanceDate(referenceDate, '2b', bizDayConv), Date(2014, 2, 10))
        self.assertEqual(sseCal.advanceDate(referenceDate, '2d', bizDayConv), Date(2014, 2, 7))
        self.assertEqual(ibCal.advanceDate(referenceDate, '2b', bizDayConv), Date(2014, 2, 8))
        self.assertEqual(ibCal.advanceDate(referenceDate, '2d', bizDayConv), Date(2014, 2, 7))

        bizDayConv = BizDayConventions.ModifiedFollowing
        # May 31, 2014 is a holiday
        self.assertEqual(sseCal.advanceDate(referenceDate, '4m', bizDayConv, True), Date(2014, 5, 30))
예제 #7
0
    def _filter_sec_on_tiaocang_date(self, tiaocang_date, sec_id):
        sse_cal = Calendar('China.SSE')
        tiaocang_date_prev = sse_cal.advanceDate(Date.strptime(str(tiaocang_date)[:10]), '-1b').toDateTime()
        tiaocang_date_prev2 = sse_cal.advanceDate(Date.strptime(str(tiaocang_date)[:10]), '-2b').toDateTime()
        price_data = WindMarketDataHandler.get_sec_price_on_date(start_date=tiaocang_date_prev2, end_date=tiaocang_date,
                                                                 sec_ids=sec_id)
        price_data = price_data.transpose()
        price_data.index.name = 'sec_id'
        # 去除涨幅过大可能买不到的
        price_data['returnFilter'] = price_data[tiaocang_date] / price_data[
            tiaocang_date_prev] > 1 + self._filterReturnOnTiaoCangDate
        # 去除有NaN的, 新股
        price_data['ipoFilter'] = pd.isnull(
            price_data[tiaocang_date] * price_data[tiaocang_date_prev] * price_data[tiaocang_date_prev2])
        # 去除停牌的,此处判断标准就是连续三天收盘价格一样
        price_data['tingpaiFilter'] = ((price_data[tiaocang_date] == price_data[tiaocang_date_prev]) & (
            price_data[tiaocang_date_prev] == price_data[tiaocang_date_prev2]))

        price_data['filters'] = 1 - (1 - price_data['returnFilter']) * (1 - price_data['ipoFilter']) * (
            1 - price_data['tingpaiFilter'])

        return price_data['filters']
예제 #8
0
def advanceDateByCalendar(holidayCenter, referenceDate, period, convention=BizDayConventions.Following):
    cal = Calendar(holidayCenter)
    refer = check_date(referenceDate)
    return cal.advanceDate(refer, period, convention).toDateTime()
예제 #9
0
def dcam_strat_main(factor_loader_params, analyzer_params, selector_params,
                    portfolio_params, update_params):
    # FactorLoader params
    start_date = factor_loader_params['startDate']
    end_date = factor_loader_params['endDate']
    factor_norm_dict = factor_loader_params['factorNormDict']

    # dcam analyzer params
    factor_weight_type = analyzer_params.get('factorWeightType',
                                             FactorWeightType.ICWeight)
    tiaocang_date_window_size = analyzer_params.get('tiaoCangDateWindowSize',
                                                    12)
    save_sec_score = analyzer_params.get('saveSecScore', True)

    # selector params
    save_sec_selected = selector_params.get('saveSecSelected', True)
    nb_sec_selected_per_industry_min = selector_params.get(
        'nbSecSelectedPerIndustryMin', 5)
    use_industry_name = selector_params.get('useIndustryName', True)
    nb_sec_selected_total = selector_params.get('nbSecSelectedTotal', 100)
    ignore_zero_weight = selector_params.get('ignoreZeroWeight', False)

    # portfolio params
    benchmark_sec_id = portfolio_params.get('benchmarkSecID', '000905.SH')
    re_balance_freq = portfolio_params.get('rebalanceFreq', FreqType.EOM)
    initial_capital = portfolio_params.get('initialCapital', 1000000000.0)
    filter_return_on_tiaocang_date = portfolio_params.get(
        'filterReturnOnTiaoCangDate', 0.09)
    data_source = portfolio_params.get('dataSource', DataSource.WIND)

    update_factor = update_params.get('updateFactor', False)
    update_sec_score = update_params.get('updateSecScore', False)
    update_sec_select = update_params.get('updateSecSelect', False)

    if update_factor:

        factor = FactorLoader(start_date=start_date,
                              end_date=end_date,
                              factor_norm_dict=factor_norm_dict)
        factor_data = factor.get_factor_data()
    else:
        # TODO
        factor = None
        factor_data = None
        pass

    if update_sec_score:
        layer_factor = [
            factor_data[name] for name in factor_norm_dict.keys()
            if factor_norm_dict[name][1] == DCAMFactorType.layerFactor
        ]
        alpha_factor = [
            factor_data[name] for name in factor_norm_dict.keys()
            if factor_norm_dict[name][1] == DCAMFactorType.alphaFactor
        ]
        alpha_factor_sign = [
            factor_data[name][2] for name in factor_norm_dict.keys()
            if factor_norm_dict[name][1] == DCAMFactorType.alphaFactor
        ]
        analyzer = DCAMAnalyzer(
            layer_factor=layer_factor,
            alpha_factor=alpha_factor,
            sec_return=factor_data['RETURN'],
            tiaocang_date=factor.get_tiaocang_date(),
            tiaocang_date_window_size=tiaocang_date_window_size,
            save_sec_score=save_sec_score,
            factor_weight_type=factor_weight_type,
            alpha_factor_sign=alpha_factor_sign)

        sec_score = analyzer.calc_sec_score()
    else:
        sec_score = load_sec_score(_secScorePath)

    if update_sec_select:
        index_comp = IndexComp(industry_weight=factor_data['IND_WGT'])
        selector = Selector(
            sec_score=sec_score,
            industry=factor_data['INDUSTRY'],
            nb_sec_selected_per_industry_min=nb_sec_selected_per_industry_min,
            index_comp=index_comp,
            save_sec_selected=save_sec_selected,
            use_industry_name=use_industry_name,
            nb_sec_selected_total=nb_sec_selected_total,
            ignore_zero_weight=ignore_zero_weight)
        selector.industry_neutral = True
        selector.sec_selection()
        sec_selected = selector.sec_selected_full_info
        pprint(selector.sec_selected_full_info)
    else:
        sec_selected = load_sec_selected(_secSelectedPath)

    # construct strategy ptf
    # 价格数据需要使用到最后一个调仓日的后一个月末
    sse_cal = Calendar('China.SSE')
    end_date_for_price_data = str(
        sse_cal.advanceDate(Date.strptime(end_date), '1m'))
    strategy = Portfolio(
        sec_selected=sec_selected,
        end_date=end_date_for_price_data,
        initial_capital=initial_capital,
        filter_return_on_tiaocang_date=filter_return_on_tiaocang_date,
        data_source=data_source,
        benchmark_sec_id=benchmark_sec_id,
        re_balance_freq=re_balance_freq)
    strategy.evaluate_ptf_return()
예제 #10
0
    def __init__(self,
                 effectiveDate,
                 terminationDate,
                 tenor,
                 calendar,
                 convention=BizDayConventions.Following,
                 terminationConvention=BizDayConventions.Following,
                 dateGenerationRule=DateGeneration.Forward,
                 endOfMonth=False,
                 firstDate=None,
                 nextToLastDate=None):

        # Initialize private data
        self._tenor = tenor
        self._cal = calendar
        self._convention = convention
        self._terminationConvention = terminationConvention
        self._rule = dateGenerationRule
        self._dates = []
        self._isRegular = []

        if tenor < Period("1M"):
            self._endOfMonth = False
        else:
            self._endOfMonth = endOfMonth

        if firstDate is None or firstDate == effectiveDate:
            self._firstDate = None
        else:
            self._firstDate = firstDate

        if nextToLastDate is None or nextToLastDate == terminationDate:
            self._nextToLastDate = None
        else:
            self._nextToLastDate = nextToLastDate

        # in many cases (e.g. non-expired bonds) the effective date is not
        # really necessary. In these cases a decent placeholder is enough
        if effectiveDate is None and firstDate is None and dateGenerationRule == DateGeneration.Backward:
            evalDate = Settings.evaluationDate
            pyFinAssert(evalDate < terminationDate, ValueError, "null effective date")
            if nextToLastDate is not None:
                y = int((nextToLastDate - evalDate) / 366) + 1
                effectiveDate = nextToLastDate - Period(y, TimeUnits.Years)
            else:
                y = int((terminationDate - evalDate) / 366) + 1
                effectiveDate = terminationDate - Period(y, TimeUnits.Years)
        else:
            pyFinAssert(effectiveDate is not None, ValueError, "null effective date")

        pyFinAssert(effectiveDate < terminationDate, ValueError, "effective date ({0}) "
                                                              "later than or equal to termination date ({1}"
                 .format(effectiveDate, terminationDate))

        if tenor.length == 0:
            self._rule = DateGeneration.Zero
        else:
            pyFinAssert(tenor.length > 0, ValueError, "non positive tenor ({0:d}) not allowed".format(tenor.length))

        if self._firstDate is not None:
            if self._rule == DateGeneration.Backward or self._rule == DateGeneration.Forward:
                pyFinAssert(effectiveDate < self._firstDate < terminationDate, ValueError,
                         "first date ({0}) out of effective-termination date range [{1}, {2})"
                         .format(self._firstDate, effectiveDate, terminationDate))
                # we should ensure that the above condition is still
                # verified after adjustment
            elif self._rule == DateGeneration.Zero:
                raise ValueError("first date incompatible with {0:d} date generation rule".format(self._rule))
            else:
                raise ValueError("unknown rule ({0:d})".format(self._rule))

        if self._nextToLastDate is not None:
            if self._rule == DateGeneration.Backward or self._rule == DateGeneration.Forward:
                pyFinAssert(effectiveDate < self._nextToLastDate < terminationDate, ValueError,
                         "next to last date ({0}) out of effective-termination date range [{1}, {2})"
                         .format(self._nextToLastDate, effectiveDate, terminationDate))
                # we should ensure that the above condition is still
                # verified after adjustment
            elif self._rule == DateGeneration.Zero:
                raise ValueError("next to last date incompatible with {0:d} date generation rule".format(self._rule))
            else:
                raise ValueError("unknown rule ({0:d})".format(self._rule))

        # calendar needed for endOfMonth adjustment
        nullCalendar = Calendar("Null")
        periods = 1

        if self._rule == DateGeneration.Zero:
            self._tenor = Period(0, TimeUnits.Years)
            self._dates.extend([effectiveDate, terminationDate])
            self._isRegular.append(True)
        elif self._rule == DateGeneration.Backward:
            self._dates.append(terminationDate)
            seed = terminationDate
            if self._nextToLastDate is not None:
                self._dates.insert(0, self._nextToLastDate)
                temp = nullCalendar.advanceDate(seed, Period(-periods * self._tenor.length, self._tenor.units),
                                                convention, self._endOfMonth)
                if temp != self._nextToLastDate:
                    self._isRegular.insert(0, False)
                else:
                    self._isRegular.insert(0, True)
                seed = self._nextToLastDate

            exitDate = effectiveDate
            if self._firstDate is not None:
                exitDate = self._firstDate

            while True:
                temp = nullCalendar.advanceDate(seed, Period(-periods * self._tenor.length, self._tenor.units),
                                                convention, self._endOfMonth)
                if temp < exitDate:
                    if self._firstDate is not None and self._cal.adjustDate(self._dates[0],
                                                                            convention) != self._cal.adjustDate(
                            self._firstDate, convention):
                        self._dates.insert(0, self._firstDate)
                        self._isRegular.insert(0, False)
                    break
                else:
                    # skip dates that would result in duplicates
                    # after adjustment
                    if self._cal.adjustDate(self._dates[0], convention) != self._cal.adjustDate(temp, convention):
                        self._dates.insert(0, temp)
                        self._isRegular.insert(0, True)
                    periods += 1

            if self._cal.adjustDate(self._dates[0], convention) != self._cal.adjustDate(effectiveDate, convention):
                self._dates.insert(0, effectiveDate)
                self._isRegular.insert(0, False)

        elif self._rule == DateGeneration.Forward:
            self._dates.append(effectiveDate)

            seed = self._dates[-1]

            if self._firstDate is not None:
                self._dates.append(self._firstDate)
                temp = nullCalendar.advanceDate(seed, Period(periods * self._tenor.length, self._tenor.units),
                                                convention, self._endOfMonth)
                if temp != self._firstDate:
                    self._isRegular.append(False)
                else:
                    self._isRegular.append(True)
                seed = self._firstDate

            exitDate = terminationDate
            if self._nextToLastDate is not None:
                exitDate = self._nextToLastDate

            while True:
                temp = nullCalendar.advanceDate(seed, Period(periods * self._tenor.length, self._tenor.units),
                                                convention, self._endOfMonth)
                if temp > exitDate:
                    if self._nextToLastDate is not None and self._cal.adjustDate(self._dates[-1],
                                                                                 convention) != self._cal.adjustDate(
                            self._nextToLastDate, convention):
                        self._dates.append(self._nextToLastDate)
                        self._isRegular.append(False)
                    break
                else:
                    # skip dates that would result in duplicates
                    # after adjustment
                    if self._cal.adjustDate(self._dates[-1], convention) != self._cal.adjustDate(temp, convention):
                        self._dates.append(temp)
                        self._isRegular.append(True)
                    periods += 1

            if self._cal.adjustDate(self._dates[-1], terminationConvention) != self._cal.adjustDate(terminationDate,
                                                                                                    terminationConvention):
                self._dates.append(terminationDate)
                self._isRegular.append(False)
        else:
            raise ValueError("unknown rule ({0:d})".format(self._rule))

        # adjustments
        if self._endOfMonth and self._cal.isEndOfMonth(seed):
            # adjust to end of month
            if convention == BizDayConventions.Unadjusted:
                for i in range(len(self._dates) - 1):
                    self._dates[i] = Date.endOfMonth(self._dates[i])
            else:
                for i in range(len(self._dates) - 1):
                    self._dates[i] = self._cal.endOfMonth(self._dates[i])

            if terminationConvention != BizDayConventions.Unadjusted:
                self._dates[0] = self._cal.endOfMonth(self._dates[0])
                self._dates[-1] = self._cal.endOfMonth(self._dates[-1])
            else:
                if self._rule == DateGeneration.Backward:
                    self._dates[-1] = Date.endOfMonth(self._dates[-1])
                else:
                    self._dates[0] = Date.endOfMonth(self._dates[0])
        else:
            for i in range(len(self._dates) - 1):
                self._dates[i] = self._cal.adjustDate(self._dates[i], convention)

            if terminationConvention != BizDayConventions.Unadjusted:
                self._dates[-1] = self._cal.adjustDate(self._dates[-1], terminationConvention)

        # Final safety checks to remove extra next-to-last date, if
        # necessary.  It can happen to be equal or later than the end
        # date due to EOM adjustments (see the Schedule test suite
        # for an example).

        if len(self._dates) >= 2 and self._dates[len(self._dates) - 2] >= self._dates[-1]:
            self._isRegular[len(self._dates) - 2] = (self._dates[len(self._dates) - 2] == self._dates[-1])
            self._dates[len(self._dates) - 2] = self._dates[-1]
            self._dates.pop()
            self._isRegular.pop()

        if len(self._dates) >= 2 and self._dates[1] <= self._dates[0]:
            self._isRegular[1] = (self._dates[1] == self._dates[0])
            self._dates[1] = self._dates[0]
            self._dates = self._dates[1:]
            self._isRegular = self._isRegular[1:]

        pyFinAssert(len(self._dates) >= 1, ValueError, "degenerate single date ({0}) schedule\n"
                                                    "seed date: {1}\n"
                                                    "exit date: {2}\n"
                                                    "effective date: {3}\n"
                                                    "first date: {4}\n"
                                                    "next to last date: {5}\n"
                                                    "termination date: {6}\n"
                                                    "generation rule: {7}\n"
                                                    "end of month: {8}\n"
                 .format(self._dates[0],
                         seed, exitDate,
                         effectiveDate,
                         firstDate,
                         nextToLastDate,
                         terminationDate,
                         self._rule, self._endOfMonth))