コード例 #1
0
ファイル: test_miscellaneous.py プロジェクト: mborraty/qf-lib
    def test_get_volume_weighted_average_price(self):
        original_datetimes = date_range('2015-01-01', periods=12, freq='10Min')
        prices_tms = PricesSeries(data=[1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4], index=original_datetimes)
        volumes_tms = QFSeries(data=[1, 2, 3, 4, 1, 2, 4, 3, 2, 1, 3, 4], index=original_datetimes)
        interval = Timedelta('40 min')

        expected_datetimes = date_range('2015-01-01 00:40', periods=2, freq=interval)
        expected_avg_weighted_prices = [3, 2.9]
        expected_prices_tms = PricesSeries(data=expected_avg_weighted_prices, index=expected_datetimes)
        actual_prices_tms = volume_weighted_average_price(prices_tms, volumes_tms, interval)
        assert_series_equal(expected_prices_tms, actual_prices_tms, absolute_tolerance=0.0)
コード例 #2
0
ファイル: test_returns.py プロジェクト: mborraty/qf-lib
 def setUp(self):
     self.return_dates = date_range('2015-01-01', periods=20, freq='D')
     prices_values = [100, 101, 103.02, 106.1106, 108.232812, 109.31514012, 109.31514012, 108.2219887188,
                      106.057548944424, 107.118124433868, 110.331668166884, 115.848251575229, 120.482181638238,
                      124.096647087385, 126.578580029132, 127.844365829424, 127.844365829424, 129.1228094877180,
                      132.9964937723500, 135.656423647797, 141.082680593708]
     prices_dates = date_range('2014-12-31', periods=1, freq='D').append(self.return_dates)
     self.test_prices_tms = PricesSeries(data=prices_values, index=prices_dates)
     self.test_dd_prices_tms = PricesSeries(data=[100, 90, 80, 70, 95, 100, 100, 200, 100, 50, 100, 200, 150],
                                            index=date_range('2015-01-01', periods=13, freq='M'))
     self.test_returns = [0.01, 0.02, 0.03, 0.02, 0.01, 0, -0.01, -0.02, 0.01, 0.03, 0.05, 0.04, 0.03, 0.02,
                          0.01, 0, 0.01, 0.03, 0.02, 0.04]
     self.test_simple_returns_tms = SimpleReturnsSeries(data=self.test_returns, index=self.return_dates, dtype=float)
コード例 #3
0
ファイル: test_returns.py プロジェクト: mborraty/qf-lib
    def test_max_drawdown(self):
        expected_max_drawdown = 0.0298
        actual_max_drawdown = max_drawdown(self.test_prices_tms)
        self.assertAlmostEqual(expected_max_drawdown, actual_max_drawdown, delta=0.00000001)

        expected_max_drawdown = 0.3
        prices_tms = PricesSeries(data=[100, 90, 80, 85, 70, 100], index=date_range('2015-01-01', periods=6))
        actual_max_drawdown = max_drawdown(prices_tms)
        self.assertAlmostEqual(expected_max_drawdown, actual_max_drawdown, places=10)

        expected_max_drawdown = 0.35
        prices_tms = PricesSeries(data=[100, 90, 80, 85, 70, 100, 90, 95, 65],
                                  index=date_range('2015-01-01', periods=9))
        actual_max_drawdown = max_drawdown(prices_tms)
        self.assertEqual(expected_max_drawdown, actual_max_drawdown)
コード例 #4
0
def volume_weighted_average_price(prices_tms: PricesSeries,
                                  volumes_tms: QFSeries,
                                  interval: Timedelta) -> PricesSeries:
    """
    Aggregates prices in the prices_tms by calculating the average weighted price for each period. The average weighted
    prices are weighted by the volumes traded in each period.

    Parameters
    ----------
    prices_tms: PricesSeries
        timeseries of prices which should be aggregated
    volumes_tms: QFSeries
        timeseries of volumes traded; must correspond to the prices_tms
    interval: Timedelta
        the length of each period from which prices should be aggregated

    Returns
    -------
    PricesSeries
        timeseries of aggregated prices; first datetimes are:
        first_price_datetime + interval, first_price_datetime + 2*interval, ..., first_price_datetime + i*interval,
        where first_price_datetime is the datetime of the first price in the original prices_tms
        The last datetime is always <= last datetime in the prices_tms
    """
    assert prices_tms.index.equals(volumes_tms.index)

    last_date = prices_tms.index[-1]
    beginning_of_window = prices_tms.index[0]
    end_of_window = beginning_of_window + interval

    weighted_avg_price_tms = PricesSeries(name=prices_tms.name)

    while end_of_window < last_date:
        prices_in_window = prices_tms.loc[
            beginning_of_window:end_of_window].drop([end_of_window]).values
        volumes_in_window = volumes_tms.loc[
            beginning_of_window:end_of_window].drop([end_of_window]).values

        # if there are no prices in the window then skip try with the next window
        if prices_in_window.size == 0:
            continue

        # if there are no volumes to use -> assume that volume in each step is the same
        if count_nonzero(volumes_in_window) == 0:
            # if all the volumes are set to 0 than assume that volume for each asset is the same
            weighted_avg_price = mean(prices_in_window)
        else:
            # calculate volume-weighted average price
            weighted_price_sum = prices_in_window.dot(volumes_in_window)
            volume_sum = sum(volumes_in_window)
            weighted_avg_price = weighted_price_sum / volume_sum

        # if the weighted average price is equal exactly 0, it means that there were missing data
        if is_finite_number(weighted_avg_price) and weighted_avg_price != 0:
            weighted_avg_price_tms[end_of_window] = weighted_avg_price

        beginning_of_window = end_of_window
        end_of_window = end_of_window + interval

    return weighted_avg_price_tms
コード例 #5
0
ファイル: pnl_calculator.py プロジェクト: quarkfin/qf-lib
    def _compute_pnl_for_ticker(self, prices_df: PricesDataFrame, transactions_series: QFSeries, start_date: datetime,
                                end_date: datetime) -> PricesSeries:
        pnl_values = []
        current_realised_pnl = 0
        ticker_to_position = {}  # type: Dict[Ticker, BacktestPosition]
        prices_df = prices_df.ffill()

        for timestamp in date_range(start_date, end_date, freq="B"):
            timestamp = timestamp + AfterMarketCloseEvent.trigger_time()

            previous_after_market_close = timestamp - RelativeDelta(days=1)
            transactions_for_past_day = transactions_series.loc[previous_after_market_close:timestamp]
            transactions_for_past_day = transactions_for_past_day \
                .where(transactions_for_past_day.index > previous_after_market_close).dropna(how="all")

            for t in transactions_for_past_day:
                position = ticker_to_position.get(t.ticker, BacktestPositionFactory.create_position(t.ticker))
                ticker_to_position[t.ticker] = position

                position.transact_transaction(t)
                if position.is_closed():
                    ticker_to_position.pop(t.ticker)
                    current_realised_pnl += position.total_pnl

            # update prices of all existing positions and get their unrealised pnl
            current_unrealised_pnl = 0.0
            for ticker, position in ticker_to_position.items():
                price = prices_df.loc[:timestamp, ticker].iloc[-1]

                position.update_price(price, price)
                current_unrealised_pnl += position.total_pnl
            pnl_values.append(current_unrealised_pnl + current_realised_pnl)

        return PricesSeries(data=pnl_values, index=date_range(start_date, end_date, freq="B"))
コード例 #6
0
ファイル: test_series.py プロジェクト: espiney/qf-lib
    def test_log_returns_to_prices(self):
        prices_values = array([1, exp(1), exp(2), exp(-1), exp(2)])
        prices_dates = pd.date_range('2015-01-01', periods=5)
        expected = PricesSeries(data=prices_values, index=prices_dates)

        returns_tms = LogReturnsSeries(data=[1, 1, -3, 3], index=expected.index[1::])
        actual = returns_tms.to_prices()
        assert_series_equal(expected, actual)
コード例 #7
0
    def calculate_aggregated_cone_oos_only(
            self, oos_series: QFSeries, is_mean_return: float, is_sigma: float,
            number_of_std: float) -> QFDataFrame:
        """
        This functions does not need the IS history, only the IS statistics.

        Parameters
        ----------
        oos_series
            series that is plotted on the cone - corresponds to the oos returns
        is_mean_return
            mean daily log return of the strategy In Sample
        is_sigma
            std of daily log returns of the strategy In Sample
        number_of_std
            corresponds to the randomness of the stochastic process. reflects number of standard deviations
            to get expected values for. For example 1.0 means 1 standard deviation above the expected value.

        Returns
        -------
        QFDataFrame: contains values corresponding to Strategy, Mean and Std. Values are indexed by number of days
            from which given cone was evaluated
        """

        log_returns_tms = oos_series.to_log_returns()
        nr_of_data_points = oos_series.size

        strategy_values = np.empty(nr_of_data_points)
        expected_values = np.empty(nr_of_data_points)

        for i in range(nr_of_data_points):
            cone_start_idx = i + 1

            # calculate total return of the strategy
            oos_log_returns = log_returns_tms[cone_start_idx:]
            total_strategy_return = exp(
                oos_log_returns.sum())  # 1 + percentage return

            # calculate expectation
            number_of_steps = len(oos_log_returns)
            starting_price = 1  # we take 1 as a base value
            total_expected_return = self.get_expected_value(
                is_mean_return, is_sigma, starting_price, number_of_steps,
                number_of_std)

            # writing to the array starting from the last array element and then moving towards the first one
            strategy_values[-i - 1] = total_strategy_return
            expected_values[-i - 1] = total_expected_return

        index = Int64Index(range(0, nr_of_data_points))

        strategy_values_tms = PricesSeries(index=index, data=strategy_values)
        expected_tms = QFSeries(index=index, data=expected_values)

        return QFDataFrame({
            'Strategy': strategy_values_tms,
            'Expectation': expected_tms,
        })
コード例 #8
0
    def test_portfolio_eod_series(self):
        expected_portfolio_eod_series = PricesSeries()

        # Empty portfolio
        portfolio, dh, timer = self.get_portfolio_and_data_handler()
        portfolio.update(record=True)
        expected_portfolio_eod_series[timer.time] = self.initial_cash

        contract = self.fut_contract
        ticker = portfolio.contract_ticker_mapper.contract_to_ticker(contract)

        # Buy contract
        self._shift_timer_to_next_day(timer)
        transaction_1 = Transaction(timer.time, contract, quantity=50, price=250, commission=7)
        portfolio.transact_transaction(transaction_1)
        self.data_handler_prices = self.prices_series
        portfolio.update(record=True)

        position = portfolio.open_positions_dict[contract]

        price_1 = dh.get_last_available_price(ticker)
        pnl = contract.contract_size * transaction_1.quantity * (price_1 - transaction_1.price)
        nav = self.initial_cash + pnl - transaction_1.commission
        expected_portfolio_eod_series[timer.time] = nav

        # Contract goes up in value
        self._shift_timer_to_next_day(timer)
        self.data_handler_prices = self.prices_up
        portfolio.update(record=True)

        price_2 = dh.get_last_available_price(ticker)  # == 270
        pnl = contract.contract_size * transaction_1.quantity * (price_2 - price_1)
        nav += pnl
        expected_portfolio_eod_series[timer.time] = nav

        # Sell part of the contract
        self._shift_timer_to_next_day(timer)
        transaction_2 = Transaction(timer.time, contract, quantity=-25, price=price_2, commission=19)
        portfolio.transact_transaction(transaction_2)
        self.data_handler_prices = self.prices_up
        portfolio.update(record=True)

        pnl = (transaction_2.price - price_2) * transaction_2.quantity * contract.contract_size - transaction_2.commission
        nav += pnl
        expected_portfolio_eod_series[timer.time] = nav

        # Price goes down
        self._shift_timer_to_next_day(timer)
        self.data_handler_prices = self.prices_down
        portfolio.update(record=True)

        price_3 = dh.get_last_available_price(ticker)  # == 210
        pnl2 = contract.contract_size * position.quantity() * (price_3 - price_2)
        nav += pnl2
        expected_portfolio_eod_series[timer.time] = nav

        tms = portfolio.portfolio_eod_series()
        assert_series_equal(expected_portfolio_eod_series, tms)
コード例 #9
0
ファイル: test_returns.py プロジェクト: mborraty/qf-lib
    def test_drawdown_tms(self):
        test_prices = [100, 90, 80, 85, 70, 100, 90, 95, 65]
        prices_tms = PricesSeries(data=test_prices, index=date_range('2015-01-01', periods=9))

        expected_drawdown_values = [0, 0.1, 0.2, 0.15, 0.3, 0, 0.1, 0.05, 0.35]
        expected_drawdowns_tms = QFSeries(expected_drawdown_values, date_range('2015-01-01', periods=9))
        actual_drawdowns_tms = drawdown_tms(prices_tms)

        assert_series_equal(expected_drawdowns_tms, actual_drawdowns_tms)
コード例 #10
0
 def portfolio_eod_series(self) -> PricesSeries:
     """
     Returns a timeseries of value of the portfolio expressed in currency units
     """
     end_of_day_date = list(
         map(lambda x: datetime(x.year, x.month, x.day),
             self._dates))  # remove time component
     portfolio_timeseries = PricesSeries(data=self._portfolio_values,
                                         index=end_of_day_date)
     return portfolio_timeseries
コード例 #11
0
 def _compute_volatility(self, prices_tms) -> float:
     """Compute the annualised volatility of the last self._number_of_samples days"""
     prices_tms = prices_tms.dropna().iloc[-self._number_of_samples:]
     prices_tms = PricesSeries(prices_tms)
     try:
         volatility = get_volatility(prices_tms,
                                     frequency=Frequency.DAILY,
                                     annualise=True)
     except (AssertionError, AttributeError):
         volatility = float('nan')
     return volatility
コード例 #12
0
ファイル: test_series.py プロジェクト: espiney/qf-lib
    def setUp(self):
        return_dates = pd.date_range('2015-01-01', periods=20, freq='D')
        test_returns = [0.01, 0.02, 0.03, 0.02, 0.01, 0, -0.01, -0.02, 0.01, 0.03, 0.05, 0.04, 0.03, 0.02,
                        0.01, 0, 0.01, 0.03, 0.02, 0.04]
        self.test_simple_returns_tms = SimpleReturnsSeries(data=test_returns, index=return_dates, dtype=float,
                                                           name='Test Name')

        prices_values = [100, 101, 103.02, 106.1106, 108.232812, 109.31514012, 109.31514012, 108.2219887188,
                         106.057548944424, 107.118124433868, 110.331668166884, 115.848251575229, 120.482181638238,
                         124.096647087385, 126.578580029132, 127.844365829424, 127.844365829424, 129.1228094877180,
                         132.9964937723500, 135.656423647797, 141.082680593708]
        prices_dates = pd.date_range('2014-12-31', periods=1, freq='D').append(return_dates)
        self.test_prices_tms = PricesSeries(data=prices_values, index=prices_dates, name='Test Name')

        test_log_returns = [0.009950331, 0.019802627, 0.029558802, 0.019802627, 0.009950331, 0, -0.010050336,
                            -0.020202707, 0.009950331, 0.029558802, 0.048790164, 0.039220713, 0.029558802,
                            0.019802627, 0.009950331, 0, 0.009950331, 0.029558802, 0.019802627, 0.039220713]

        self.test_log_returns_tms = LogReturnsSeries(data=test_log_returns, index=return_dates, dtype=float,
                                                     name='Test Name')
コード例 #13
0
    def test_historical_price__single_ticker__single_field__daily(self):
        self.current_time = str_to_date("2021-05-03 00:00:00.000000",
                                        DateFormat.FULL_ISO)

        # Test when the current day does not have the open price
        actual_series = self.data_provider.historical_price(
            self.ticker_2, PriceField.Open, 2, frequency=Frequency.DAILY)
        expected_series = PricesSeries(
            data=[27, 29],
            index=[str_to_date('2021-05-01'),
                   str_to_date('2021-05-02')])
        assert_series_equal(actual_series, expected_series, check_names=False)

        # Test when the previous day does not have the open price
        actual_series = self.data_provider.historical_price(
            self.ticker_1, PriceField.Open, 2, frequency=Frequency.DAILY)
        expected_series = PricesSeries(
            data=[25, 31],
            index=[str_to_date('2021-05-01'),
                   str_to_date('2021-05-03')])
        assert_series_equal(actual_series, expected_series, check_names=False)
コード例 #14
0
    def test_compute_container_hash__series(self):
        list_of_data = list(range(200))
        qfseries_1 = QFSeries(data=list_of_data)
        qfseries_2 = QFSeries(data=list_of_data)
        returns_series = ReturnsSeries(data=list_of_data)
        prices_series = PricesSeries(data=list_of_data)

        self.assertEqual(compute_container_hash(qfseries_1),
                         compute_container_hash(qfseries_2))
        self.assertEqual(compute_container_hash(qfseries_1),
                         compute_container_hash(returns_series))
        self.assertEqual(compute_container_hash(qfseries_1),
                         compute_container_hash(prices_series))
コード例 #15
0
    def _generate_buy_and_hold_returns(self,
                                       ticker: Ticker) -> SimpleReturnsSeries:
        """ Computes series of simple returns, which would be returned by the Buy and Hold strategy. """
        if isinstance(ticker, FutureTicker):
            try:
                ticker.initialize_data_provider(SettableTimer(self._end_date),
                                                self._data_provider)
                futures_chain = FuturesChain(
                    ticker, self._data_provider,
                    FuturesAdjustmentMethod.BACK_ADJUSTED)
                prices_series = futures_chain.get_price(
                    PriceField.Close, self._start_date, self._end_date)
            except NoValidTickerException:
                prices_series = PricesSeries()
        else:
            prices_series = self._data_provider.get_price(
                ticker, PriceField.Close, self._start_date, self._end_date)

        returns_tms = prices_series.to_simple_returns().replace(
            [-np.inf, np.inf], np.nan).fillna(0.0)
        returns_tms.name = "Buy and Hold"
        return returns_tms
コード例 #16
0
ファイル: analytical_cone.py プロジェクト: quarkfin/qf-lib
    def calculate_simple_cone(self, live_start_date: datetime,
                              number_of_std: float) -> PricesSeries:
        """
        Creates a simple cone starting from a given date using the solution to the stochastic equation:
        S(t) = S(0)*exp( (mu-0.5*sigma^2)*t + sigma*N(0,1)*sqrt(t) )

        Parameters
        ----------
        live_start_date: datetime
            datetime or string with date, corresponds to the cone start date
        number_of_std: float
            corresponds to the randomness of the stochastic process. reflects number of standard deviations
            to get expected values for. For example 1.0 means 1 standard deviation above the expected value.

        Returns
        -------
        PriceSeries
            expected values
        """

        is_log_tms = self.log_returns_tms.loc[
            self.log_returns_tms.index < live_start_date]
        oos_log_tms = self.log_returns_tms.loc[
            self.log_returns_tms.index >= live_start_date]

        mu = is_log_tms.mean()
        sigma = is_log_tms.std()

        days_oos = range(len(oos_log_tms) + 1)  # a list [0, 1, 2, ... N]
        initial_price = self.series.asof(
            is_log_tms.index[-1])  # price at last in-sample date

        # for each day OOS calculate the expected value at different point in time using the _get_expected_value()
        # function that gives expectation in single point in time.
        expected_values = list(
            map(
                lambda nr_of_days: self.get_expected_value(
                    mu, sigma, initial_price, nr_of_days, number_of_std),
                days_oos))

        # We need to add last IS index value to connect the cone to the line. It will correspond to 0 days cone
        index = oos_log_tms.index.copy()
        index = index.insert(0, is_log_tms.index[-1])

        return PricesSeries(index=index, data=expected_values)
コード例 #17
0
    def calculate_simple_cone_for_process(self,
                                          mu: float,
                                          sigma: float,
                                          number_of_std: float,
                                          number_of_steps: int,
                                          starting_value=1) -> PricesSeries:
        """
        Creates a simple cone starting from a given date using the solution to the stochastic equation:
        S(t) = S(0)*exp( (mu-0.5*sigma^2)*t + sigma*N(0,1)*sqrt(t) )

        Parameters
        ----------
        mu
            mean return of the process. expressed in the frequency of samples (not annualised)
        sigma
            std of returns of the process. expressed in the frequency of samples (not annualised)
        number_of_std
            corresponds to the randomness of the stochastic process. reflects number of standard deviations
            to get expected values for. For example 1.0 means 1 standard deviation above the expected value.
        number_of_steps
            length of the cone that we are creating
        starting_value
            corresponds to the starting price of the instrument

        Returns
        -------
        PriceSeries
            expected values
        """

        steps = range(number_of_steps + 1)  # a list [0, 1, 2, ... N]

        # for each day OOS calculate the expected value at different point in time using the _get_expected_value()
        # function that gives expectation in single point in time.
        expected_values = list(
            map(
                lambda nr_of_days: self.get_expected_value(
                    mu, sigma, starting_value, nr_of_days, number_of_std),
                steps))

        return PricesSeries(index=steps, data=expected_values)
コード例 #18
0
ファイル: returns_series.py プロジェクト: quarkfin/qf-lib
    def to_prices(self,
                  initial_price: float = None,
                  suggested_initial_date: Union[datetime, int, float] = None,
                  frequency=None) -> "PricesSeries":
        if suggested_initial_date is None:
            suggested_initial_date = self._get_initial_date(frequency)
        if initial_price is None:
            initial_price = 1.0

        if suggested_initial_date is not datetime:
            prices_dates = Index([suggested_initial_date]).append(
                self.index.copy())  # if it is numeric or string based
        else:
            prices_dates = DatetimeIndex([suggested_initial_date
                                          ]).append(self.index.copy())

        prices_values = self._to_prices_values(initial_price)

        from qf_lib.containers.series.prices_series import PricesSeries
        return PricesSeries(data=prices_values,
                            index=prices_dates).__finalize__(self)
コード例 #19
0
def import_data(frequency: Frequency, file_path: str):
    xlsx = ExcelImporter()
    df = xlsx.import_container(file_path,
                               'A1',
                               'J1888',
                               sheet_name="Data",
                               include_index=True,
                               include_column_names=True)
    weights = xlsx.import_container(file_path,
                                    'M15',
                                    'N23',
                                    sheet_name="Data",
                                    include_index=True,
                                    include_column_names=False)

    simple_ret_df = DataFrame()
    for column in df:
        prices = PricesSeries(df[column])
        simple_returns = get_aggregate_returns(prices, frequency)
        simple_ret_df[column] = simple_returns

    return simple_ret_df, weights
コード例 #20
0
    def _get_underwater_chart(self,
                              series: QFSeries,
                              title="Drawdown",
                              benchmark_series: QFSeries = None,
                              rotate_x_axis: bool = False):
        underwater_chart = LineChart(start_x=series.index[0],
                                     end_x=series.index[-1],
                                     log_scale=False,
                                     rotate_x_axis=rotate_x_axis)
        underwater_chart.add_decorator(UnderwaterDecorator(series))
        underwater_chart.add_decorator(TitleDecorator(title))

        if benchmark_series is not None:
            legend = LegendDecorator()
            benchmark_dd = PricesSeries(drawdown_tms(benchmark_series))
            benchmark_dd *= -1
            benchmark_dd_elem = DataElementDecorator(benchmark_dd,
                                                     color="black",
                                                     linewidth=0.5)
            legend.add_entry(benchmark_dd_elem, "Benchmark DD")
            underwater_chart.add_decorator(benchmark_dd_elem)
            underwater_chart.add_decorator(legend)
        return underwater_chart
コード例 #21
0
ファイル: portfolio.py プロジェクト: mborraty/qf-lib
 def get_portfolio_timeseries(self) -> PricesSeries:
     """
     Returns a timeseries of value of the portfolio expressed in currency units
     """
     portfolio_timeseries = PricesSeries(data=self.portfolio_values, index=self.dates)
     return portfolio_timeseries
コード例 #22
0
ファイル: test_series.py プロジェクト: espiney/qf-lib
class TestSeries(TestCase):
    def setUp(self):
        return_dates = pd.date_range('2015-01-01', periods=20, freq='D')
        test_returns = [0.01, 0.02, 0.03, 0.02, 0.01, 0, -0.01, -0.02, 0.01, 0.03, 0.05, 0.04, 0.03, 0.02,
                        0.01, 0, 0.01, 0.03, 0.02, 0.04]
        self.test_simple_returns_tms = SimpleReturnsSeries(data=test_returns, index=return_dates, dtype=float,
                                                           name='Test Name')

        prices_values = [100, 101, 103.02, 106.1106, 108.232812, 109.31514012, 109.31514012, 108.2219887188,
                         106.057548944424, 107.118124433868, 110.331668166884, 115.848251575229, 120.482181638238,
                         124.096647087385, 126.578580029132, 127.844365829424, 127.844365829424, 129.1228094877180,
                         132.9964937723500, 135.656423647797, 141.082680593708]
        prices_dates = pd.date_range('2014-12-31', periods=1, freq='D').append(return_dates)
        self.test_prices_tms = PricesSeries(data=prices_values, index=prices_dates, name='Test Name')

        test_log_returns = [0.009950331, 0.019802627, 0.029558802, 0.019802627, 0.009950331, 0, -0.010050336,
                            -0.020202707, 0.009950331, 0.029558802, 0.048790164, 0.039220713, 0.029558802,
                            0.019802627, 0.009950331, 0, 0.009950331, 0.029558802, 0.019802627, 0.039220713]

        self.test_log_returns_tms = LogReturnsSeries(data=test_log_returns, index=return_dates, dtype=float,
                                                     name='Test Name')

    def test_prices_to_simple_returns(self):
        actual_returns_tms = self.test_prices_tms.to_simple_returns()
        expected_returns_tms = self.test_simple_returns_tms

        assert_series_equal(expected_returns_tms, actual_returns_tms)

    def test_prices_to_log_returns(self):
        actual_log_returns_tms = self.test_prices_tms.to_log_returns()
        expected_log_returns_tms = self.test_log_returns_tms
        assert_series_equal(expected_log_returns_tms, actual_log_returns_tms)

    def test_simple_returns_to_prices(self):
        expected_tms = self.test_prices_tms
        actual_tms = self.test_simple_returns_tms.to_prices(initial_price=100)
        assert_series_equal(expected_tms, actual_tms, absolute_tolerance=1e-5)

    def test_simple_returns_to_log_returns(self):
        expected_tms = self.test_log_returns_tms
        actual_tms = self.test_simple_returns_tms.to_log_returns()
        assert_series_equal(expected_tms, actual_tms)

    def test_log_returns_to_prices(self):
        prices_values = array([1, exp(1), exp(2), exp(-1), exp(2)])
        prices_dates = pd.date_range('2015-01-01', periods=5)
        expected = PricesSeries(data=prices_values, index=prices_dates)

        returns_tms = LogReturnsSeries(data=[1, 1, -3, 3], index=expected.index[1::])
        actual = returns_tms.to_prices()
        assert_series_equal(expected, actual)

    def test_log_returns_to_simple_returns(self):
        expected_tms = self.test_simple_returns_tms
        actual_tms = self.test_log_returns_tms.to_simple_returns()
        assert_series_equal(expected_tms, actual_tms)

    def test_infer_interval(self):
        expected_interval = pd.Timedelta("1 day")
        expected_frequency = 1
        actual_interval, actual_frequency = self.test_log_returns_tms.infer_interval()
        self.assertEqual(expected_interval, actual_interval)
        self.assertEqual(expected_frequency, actual_frequency)

        expected_interval = pd.Timedelta("1 day")
        expected_frequency = 2 / 3
        dates = pd.date_range('2016-04-01', periods=4, freq='b')
        test_series = QFSeries(data=[0, 0, 0, 0], index=dates)

        actual_interval, actual_frequency = test_series.infer_interval()

        self.assertEqual(expected_interval, actual_interval)
        self.assertEqual(expected_frequency, actual_frequency)

    def test_cast_series(self):
        actual_casted_series = cast_series(self.test_simple_returns_tms, PricesSeries)

        self.assertEqual(PricesSeries, type(actual_casted_series))
        self.assertEqual(list(self.test_simple_returns_tms.values), list(actual_casted_series.values))

    def test_rolling_window(self):
        strategy_dates = pd.date_range('2015-01-01', periods=20, freq='D')
        benchmark_dates = pd.date_range('2015-01-10', periods=20, freq='D')
        data = [0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01,
                0.01, 0.01, 0.01, 0.01, 0.01]
        strategy = SimpleReturnsSeries(data=data, index=strategy_dates)
        benchmark = SimpleReturnsSeries(data=data, index=benchmark_dates)

        rolling = strategy.rolling_window_with_benchmark(benchmark, 1, lambda x, y: x.mean() + y.mean())
        self.assertEqual(rolling.iloc[0], 0.02)

        self.assertEqual(rolling.index[0], benchmark_dates[1])
        self.assertEqual(rolling.index[9], benchmark_dates[10])
        self.assertEqual(len(rolling), 10)

        # Test with missing values in the middle.
        strategy_dates = pd.date_range('2015-01-02', periods=3, freq='D')
        benchmark_dates = pd.DatetimeIndex(['2015-01-01', '2015-01-02', '2015-01-04'])
        strategy = SimpleReturnsSeries(data=[0.01, 0.50, 0.01], index=strategy_dates)
        benchmark = SimpleReturnsSeries(data=[0.50, 0.01, 0.01], index=benchmark_dates)
        rolling = strategy.rolling_window_with_benchmark(benchmark, 1, lambda x, y: x.mean() + y.mean())
        self.assertEqual(rolling.iloc[0], 0.02)
コード例 #23
0
ファイル: analytical_cone.py プロジェクト: quarkfin/qf-lib
    def calculate_aggregated_cone(self, nr_of_days_to_evaluate: int,
                                  is_end_date: datetime,
                                  number_of_std: float) -> QFDataFrame:
        """
        Evaluates many simple cones and saves the end values of every individual simple cone.
        While using a simple cone (e.g. LineChart with Cone decorator) the results of the evaluation may be very
        different depending on the starting point. To be immune to this, calculate_aggregated_cone plots
        only the ends of simple cones which start  at 1 period, 2 periods, ..., n periods before the end of the series.
        The period length depends  on the frequency of the data provided for the chart. If it has daily frequency,
        then the length of one period will be 1 day.

        Parameters
        ----------
        nr_of_days_to_evaluate
            max number of days in the past, from when all the cones are evaluated
        is_end_date
            the end od in-sample date. Makes sure that in-sample doesn't move with the cone.
        number_of_std
            corresponds to the randomness of the stochastic process. reflects number of standard deviations
            to get expected values for. For example 1.0 means 1 standard deviation above the expected value.

        Returns
        -------
        QFDataFrame
            contains values corresponding to Strategy, Mean and Std. Values are indexed by number of days
            from which given cone was evaluated
        """

        nr_of_data_points = nr_of_days_to_evaluate + 1  # there is a point for 0 days at the beginning of the cone

        # if nr_of_days_to_evaluate is too large and goes into In-Sample, we need to reduce the value
        is_end_date_int_index = self.series.index.get_loc(is_end_date,
                                                          method='pad')
        first_cone_index = len(self.log_returns_tms) - nr_of_data_points
        if first_cone_index < is_end_date_int_index:
            first_cone_index = is_end_date_int_index
            nr_of_data_points = len(self.log_returns_tms) - first_cone_index

        strategy_values = np.empty(nr_of_data_points)
        expected_values = np.empty(nr_of_data_points)

        mean_return, sigma = self._get_is_statistics(self.log_returns_tms,
                                                     is_end_date)

        for i in range(nr_of_data_points):
            cone_start_idx = first_cone_index + i + 1

            # calculate total return of the strategy
            oos_log_returns = self.log_returns_tms[cone_start_idx:]
            total_strategy_return = exp(
                oos_log_returns.sum())  # 1 + percentage return

            # calculate expectation
            number_of_steps = len(oos_log_returns)
            starting_price = 1  # we take 1 as a base value
            total_expected_return = self.get_expected_value(
                mean_return, sigma, starting_price, number_of_steps,
                number_of_std)

            # writing to the array starting from the last array element and then moving towards the first one
            strategy_values[-i - 1] = total_strategy_return
            expected_values[-i - 1] = total_expected_return

        index = Int64Index(range(0, nr_of_data_points))

        strategy_values_tms = PricesSeries(index=index, data=strategy_values)
        expected_tms = QFSeries(index=index, data=expected_values)

        return QFDataFrame({
            'Strategy': strategy_values_tms,
            'Expectation': expected_tms,
        })
コード例 #24
0
 def volatility(window):
     return get_volatility(PricesSeries(window), freq)
コード例 #25
0
 def tot_return(window):
     return PricesSeries(window).total_cumulative_return()
コード例 #26
0
 def volatility(window):
     return get_volatility(PricesSeries(window), Frequency.DAILY)
コード例 #27
0
ファイル: test_dataframes.py プロジェクト: quarkfin/qf-lib
 def test_total_cumulative_return(self):
     actual_result = self.test_prices_df.total_cumulative_return()
     expected_result = PricesSeries(index=self.test_prices_df.columns,
                                    data=[4.0, 4.0, 4.0, 4.0, 4.0])
     assert_series_equal(expected_result, actual_result)
     self.assertEqual(dtype("float64"), actual_result.dtypes)
コード例 #28
0
ファイル: abstract_tearsheet.py プロジェクト: quarkfin/qf-lib
 def volatility(window):
     return get_volatility(PricesSeries(window),
                           self.frequency,
                           annualise=True)
コード例 #29
0
 def volatility(window):
     return get_volatility(PricesSeries(window),
                           Frequency.DAILY,
                           annualise=True)