def test_generate_close_orders__multiple_future_tickers(self, future_ticker, order_factory, broker): tickers_in_portfolio = [BloombergTicker("CLG00 Comdty", SecurityType.FUTURE, 1), BloombergTicker("CLG01 Comdty", SecurityType.FUTURE, 1)] tickers_in_portfolio_2 = [BloombergTicker("CTG00 Comdty", SecurityType.FUTURE, 1), BloombergTicker("CTG01 Comdty", SecurityType.FUTURE, 1)] broker.get_positions.side_effect = lambda: [BacktestPositionFactory.create_position(c) for c in tickers_in_portfolio + tickers_in_portfolio_2] # Generate the FuturesRollingOrdersGenerator for two different Future Tickers future_ticker.get_current_specific_ticker.return_value = BloombergTicker("CLG01 Comdty", SecurityType.FUTURE, 1) future_ticker.belongs_to_family.side_effect = lambda t: t in tickers_in_portfolio future_ticker.get_expiration_dates.return_value = QFSeries(data=tickers_in_portfolio, index=[self.current_date - RelativeDelta(days=10), self.current_date + RelativeDelta(days=5)]) future_ticker2 = MagicMock() future_ticker2.get_current_specific_ticker.return_value = BloombergTicker("CTG01 Comdty") future_ticker2.belongs_to_family.side_effect = lambda t: t in tickers_in_portfolio_2 future_ticker2.get_expiration_dates.return_value = QFSeries(data=tickers_in_portfolio_2, index=[self.current_date - RelativeDelta(days=10), self.current_date + RelativeDelta(days=5)]) rolling_orders_generator = FuturesRollingOrdersGenerator([future_ticker, future_ticker2], self.timer, broker, order_factory) rolling_orders_generator.logger = MagicMock() rolling_orders_generator.generate_close_orders() order_factory.target_percent_orders.assert_called_once_with( {BloombergTicker("CLG00 Comdty", SecurityType.FUTURE, 1): 0, BloombergTicker("CTG00 Comdty", SecurityType.FUTURE, 1): 0}, MarketOrder(), TimeInForce.GTC )
def _add_number_of_transactions_chart(self, pandas_freq: str, title: str): transactions = self.backtest_result.transactions transactions_series = QFSeries(data=transactions, index=(t.time for t in transactions)) if transactions_series.empty: raise ValueError("Transactions series is empty") # Compute the number of transactions per day transactions_series = transactions_series.resample( Frequency.DAILY.to_pandas_freq()).count() # Aggregate the transactions using the given frequency if to_offset(pandas_freq) > to_offset('D'): transactions_series = transactions_series.rolling( pandas_freq).sum() # Cut the non complete beginning of the outputs (e.g. in case of 30 days window, cut the first 30 days) start_date = transactions_series.index[0] transactions_series = transactions_series.loc[start_date + Timedelta(pandas_freq ):] if transactions_series.empty: # The available time period is too short to compute the statistics with the provided frequency return elif to_offset(pandas_freq) < to_offset('D'): raise ValueError( "The provided pandas frequency can not be higher than the daily frequency" ) self._add_line_chart_element(transactions_series, title)
def __init__(self, data=None, index=None, columns=None, dtype=None, copy=False): self.logger = qf_logger.getChild(self.__class__.__name__) # Data Frame containing the table data self.data = QFDataFrame(data, index, columns, dtype, copy) # Dictionary containing a mapping from column names onto ColumnStyles self._columns_styles = { column_name: self.ColumnStyle(column_name) for column_name in self.data.columns.tolist() } # Series containing the styles for each of the rows self._rows_styles = QFSeries( data=[self.RowStyle(loc) for loc in self.data.index], index=self.data.index) # Data Frame containing styles for all cells in the table, based upon columns_styles and rows_styles self._styles = QFDataFrame(data={ column_name: [ self.CellStyle(row_style, column_style) for row_style in self.rows_styles ] for column_name, column_style in self.columns_styles.items() }, index=self.data.index, columns=self.data.columns) self.table_styles = self.Style()
def get_factor_return_attribution(cls, fund_tms: QFSeries, fit_tms: QFSeries, regressors_df: QFDataFrame, coefficients: QFSeries, alpha: float) -> Tuple[QFSeries, float]: """ Returns performance attribution for each factor in given regressors and also calculates the unexplained return. """ fund_returns = fund_tms.to_simple_returns() regressors_returns = regressors_df.to_simple_returns() annualised_fund_return = cagr(fund_returns) annualised_fit_return = cagr(fit_tms) total_nav = fit_tms.to_prices(initial_price=1.0) def calc_factors_profit(series) -> float: factor_ret = regressors_returns.loc[:, series.name].values return coefficients.loc[series.name] * (total_nav[:-1].values * factor_ret).sum() factors_profits = regressors_returns.apply(calc_factors_profit) alpha_profit = total_nav[:-1].sum() * alpha total_profit = factors_profits.sum() + alpha_profit regressors_return_attribution = factors_profits * annualised_fit_return / total_profit regressors_return_attribution = cast_series( regressors_return_attribution, QFSeries) unexplained_return = annualised_fund_return - regressors_return_attribution.sum( ) return regressors_return_attribution, unexplained_return
def sqn(returns: QFSeries): """ Calculates the SQN = mean return of trade / std(returns of trades). The returns passed to the function may wither be defined as percentage PnL of trades or as r_multiply = percentage PnL / risk. """ result = returns.mean() / returns.std() return result
def create_qq_chart(strategy: QFSeries) -> Chart: colors = Chart.get_axes_colors() strategy = strategy.to_log_returns() # Normalize strategy = strategy - strategy.mean() strategy = strategy / strategy.std() # Sort strategy_values = sorted(strategy.values) # Create benchmark benchmark_values = list(range(1, len(strategy_values) + 1)) n = len(strategy_values) + 1 benchmark_values = map(lambda x: x / n, benchmark_values) benchmark_values = list(map(lambda x: norm.ppf(x), benchmark_values)) # Figure out the limits. maximum = max(max(benchmark_values), max(strategy_values)) result = LineChart(start_x=-maximum, end_x=maximum, upper_y=maximum, lower_y=-maximum) result.add_decorator(ScatterDecorator( benchmark_values, strategy_values, color=colors[0], alpha=0.6, edgecolors='black', linewidths=0.5)) result.add_decorator(VerticalLineDecorator(0, color='black', linewidth=1)) result.add_decorator(HorizontalLineDecorator(0, color='black', linewidth=1)) result.add_decorator(TitleDecorator("Normal Distribution Q-Q")) result.add_decorator(AxesLabelDecorator("Normal Distribution Quantile", "Observed Quantile")) # Add diagonal line. result.add_decorator(DiagonalLineDecorator(color=colors[1])) return result
def rolling_time_window( self, window_length: int, step: int, func: Callable[[Union["QFDataFrame", np.ndarray]], "QFSeries"]) \ -> Union[None, "QFSeries", "QFDataFrame"]: """ Runs a given function on each rolling window in the dataframe. The content of a rolling window is also a QFDataFrame thus the funciton which should be applied should accept a QFDataFrame as an argument. The function may return either a QFSeries (then the output of rolling_time_window will be QFDataFrame) or a scalar value (then the output of rolling_time_window will be QFSeries). The rolling window is moved along the time index (rows). Parameters ---------- window_length number of rows which should be taken into rolling window step number of rows by which rolling window should be moved func function to apply on each rolling window. If it returns a QFSeries then the output of rolling_time_window() will be a QFDataFrame; if it returns a scalar value, the return value of rolling_time_window() will be a QFSeries Returns ------- None (if the result of running the rolling window was empty) or QFSeries (if the function applied returned scalar value for each window) or QFDataFrame (if the function applied returned QFSeries for each window) """ results_dict = dict() # type: Dict[datetime, pd.Series] end_idx = self.num_of_rows while True: start_idx = end_idx - window_length if start_idx < 0: break patch = self.iloc[start_idx:end_idx, :] end_date = self.index[end_idx - 1] results_dict[end_date] = func(patch) end_idx -= step if not results_dict: return None first_element = next(iter(results_dict.values())) # type: "QFSeries" if isinstance(first_element, pd.Series): result = QFDataFrame.from_dict(results_dict, orient='index') result = cast_dataframe(result, QFDataFrame) else: from qf_lib.containers.series.qf_series import QFSeries dates_and_values = [(date, value) for date, value in results_dict.items()] dates, values = zip(*dates_and_values) result = QFSeries(index=dates, data=values) result = result.sort_index() return result
def main(): dates_a = pd.date_range('2015-01-01', periods=10, freq='D') a = QFSeries(data=[1, 2, 3, 4, 5, 4, 3, 2, 1, 4], index=dates_a, name='Series A') dates_b = pd.date_range('2015-01-08', periods=10, freq='D') b = QFSeries(data=[3, 2, 3, 4, 5, 4, 3, 2, 1, 2], index=dates_b, name='Series B') dates_c = pd.date_range('2015-01-05', periods=10, freq='D') c = QFSeries(data=[4, 2, 3, 4, 5, None, 3, 2, 1, 2], index=dates_c, name='Series C') abc = pd.concat([a, b, c], axis=1, join='outer') print(abc) line_chart = LineChart() line_chart.add_decorator(DataElementDecorator(abc.iloc[:, 0])) line_chart.add_decorator(DataElementDecorator(abc.iloc[:, 1])) line_chart.add_decorator(DataElementDecorator(abc.iloc[:, 2])) line_chart.plot() plt.show(block=True)
def __init__(self, benchmark_tms: QFSeries, strategy_tms: QFSeries): super().__init__() self.assert_is_qfseries(benchmark_tms) self.assert_is_qfseries(strategy_tms) self.benchmark_tms = benchmark_tms.to_simple_returns() self.strategy_tms = strategy_tms.to_simple_returns()
def setup(self, window_size=None, step=None): """ Returns the series of models (each model is placed under the date corresponding to the end of its time window). Parameters ---------- window_size: int, optional number of samples contained in each window. If it's not provided, than it will be calculated automatically step: int, optional number of samples by which window shall be moved each time it is moved. If the value isn't provided, than it will be calculated automatically """ if window_size is None: window_size = RollingWindowsEstimator.estimate_rolling_window_size( self.input_data.analysed_tms) if step is None: step = RollingWindowsEstimator.estimate_rolling_window_step( self.input_data.analysed_tms) assert step > 0 self.window_size_ = window_size self.step_ = step end_of_window_idx = len(self.input_data.analysed_tms) - 1 beginning_of_window_idx = end_of_window_idx - window_size ref_dates = [] models = [] while beginning_of_window_idx >= 0: data_model, ref_date = self._get_model_for_window( beginning_of_window_idx, end_of_window_idx) models.append(data_model) ref_dates.append(ref_date) end_of_window_idx -= step beginning_of_window_idx -= step models_series_ = QFSeries(data=models, index=ref_dates) self.coefficients_df = models_series_.apply( lambda model: model.coefficients) self.t_stats_df = models_series_.apply(lambda model: model.t_values) self.p_values_df = models_series_.apply(lambda model: model.p_values) self.r_squared_tms = models_series_.apply( lambda model: model.r_squared) self.risk_contribs_df = models_series_.apply( lambda model: model.risk_contribution) self.factors_performance_attributions_df = models_series_.apply( lambda model: model.factors_performance_attribution_ret) self.unexplained_performance_attributions_tms = models_series_.apply( lambda model: model.unexplained_performance_attribution_ret) # select correlations of different series with analysed timeseries self.correlations_df = models_series_.apply( lambda model: model.correlation_matrix.iloc[-1, :-1])
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, })
def __init__(self, benchmark_tms: QFSeries, strategy_tms: QFSeries, tail_plot=False, custom_title=False): super().__init__() self.assert_is_qfseries(benchmark_tms) self.assert_is_qfseries(strategy_tms) self.benchmark_tms = benchmark_tms.to_simple_returns() self.strategy_tms = strategy_tms.to_simple_returns() self.tail_plot = tail_plot self.custom_title = custom_title
def test_concat_series(self): index = [1, 2, 3] series_1 = QFSeries(data=["A", "B", "C"], index=index) series_2 = QFSeries(data=["D", "E", "F"], index=index) df = pd.concat([series_1, series_2], axis=1) self.assertEqual(type(df), QFDataFrame) series = pd.concat([series_1, series_2], axis=0) self.assertEqual(type(series), QFSeries)
def _get_current_prices(self, tickers: Sequence[Ticker]): """ Function used to obtain the current prices for the tickers in order to further calculate fill prices for orders. The function uses data provider and not data handler, as it is necessary to get the current bar at each point in time to compute the fill prices. """ if not tickers: return QFSeries() assert self._frequency >= Frequency.DAILY, "Lower than daily frequency is not supported by the simulated" \ " executor" # Compute the time ranges, used further by the get_price function current_datetime = self._timer.now() market_close_time = current_datetime + MarketCloseEvent.trigger_time( ) == current_datetime market_open_time = current_datetime + MarketOpenEvent.trigger_time( ) == current_datetime # In case of daily frequency, current price may be returned only at the Market Open or Market Close time if self._frequency == Frequency.DAILY and not (market_open_time or market_close_time): return QFSeries(index=tickers) if self._frequency == Frequency.DAILY: # Remove the time part from the datetime in case of daily frequency current_datetime = date_to_datetime(current_datetime.date()) start_time_range = current_datetime - self._frequency.time_delta() end_time_range = current_datetime elif market_close_time: # At the market close, in order to get the current price we need to take a bar that ends at the current time # and use the close price value start_time_range = current_datetime - self._frequency.time_delta() end_time_range = current_datetime current_datetime = start_time_range else: # At any other time during the day, in order to get the current price we need to take the bar that starts at # the current time and use the open price value start_time_range = current_datetime end_time_range = current_datetime + self._frequency.time_delta() price_field = PriceField.Close if market_close_time else PriceField.Open prices_df = self._data_provider.get_price(tickers, price_field, start_time_range, end_time_range, self._frequency) try: prices_series = prices_df.loc[current_datetime] except KeyError: prices_series = QFSeries(index=tickers) prices_series.name = "Current prices series" return prices_series
def setUp(self): dates = DatetimeIndex(date_range(start='2014-01-01', freq='d', periods=10)) returns = np.arange(0, 1, 0.1) self.test_series = QFSeries(index=dates, data=returns) reversed_returns = returns[::-1] test_series_reversed = QFSeries(index=dates, data=reversed_returns) self.test_data_frame = concat([self.test_series, test_series_reversed], axis=1, join='inner') self.xl_importer = ExcelImporter()
def _get_simple_quantile_chart(simple_returns): if len(simple_returns) > 0: simple_returns_weekly = get_aggregate_returns(simple_returns, Frequency.WEEKLY, multi_index=True) simple_returns_monthly = get_aggregate_returns(simple_returns, Frequency.MONTHLY, multi_index=True) chart = BoxplotChart([simple_returns, simple_returns_weekly, simple_returns_monthly], linewidth=1) else: chart = BoxplotChart([QFSeries(), QFSeries(), QFSeries()], linewidth=1) tick_decorator = AxisTickLabelsDecorator(labels=["daily", "weekly", "monthly"], axis=Axis.X) return chart, tick_decorator
def evaluate_params_for_tickers(self, parameters: tuple, tickers: Sequence[Ticker], start_time: datetime, end_date: datetime): # Get the backtest element for the given list of tickers backtest_elements_for_tickers = [ el for el in self.params_backtest_summary_elem_dict[parameters] if set(el.tickers) == set(tickers) ] assert len(backtest_elements_for_tickers) == 1, "Check if the modeled_params passed to " \ "FastAlphaModelTesterConfig match those you want to test" backtest_elem = backtest_elements_for_tickers[0] returns_tms = backtest_elem.returns_tms.dropna(how="all") trades = backtest_elem.trades # Create the TradesEvaluationResult object ticker_evaluation = TradesEvaluationResult() ticker_evaluation.ticker = tickers ticker_evaluation.parameters = parameters ticker_evaluation.end_date = end_date # Compute the start date as the maximum value between the given start_time and the first date of returns tms in # case of 1 ticker backtest if len(tickers) == 1: start_date = max( start_time, returns_tms.index[0]) if not returns_tms.empty else end_date else: start_date = start_time ticker_evaluation.start_date = start_date if start_date >= end_date: # Do not compute further fields - return the default None values return ticker_evaluation avg_nr_of_trades = avg_nr_of_trades_per1y( QFSeries([ t for t in trades if t.start_time >= start_date and t.end_time <= end_date ]), start_date, end_date) ticker_evaluation.avg_nr_of_trades_1Y = avg_nr_of_trades ticker_evaluation.sqn_per_avg_nr_trades = sqn( QFSeries([ t.pnl for t in trades if t.start_time >= start_date and t.end_time <= end_date ])) * sqrt(avg_nr_of_trades) returns_tms = returns_tms.loc[start_date:end_date] if not returns_tms.empty: ticker_evaluation.annualised_return = cagr(returns_tms, Frequency.DAILY) return ticker_evaluation
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))
def test_get_distance_to_equal_risk_contrib(self): factors_covariance = self.factors_df.cov() weights = QFSeries([0.5, 0.5], index=self.factors_df.columns) actual_result = RiskContributionAnalysis.get_distance_to_equal_risk_contrib( factors_covariance, weights) expected_result = 0.342309791791 self.assertAlmostEqual(actual_result, expected_result, places=10) weights = QFSeries([0.25, 0.75], index=self.factors_df.columns) actual_result = RiskContributionAnalysis.get_distance_to_equal_risk_contrib( factors_covariance, weights) expected_result = 0.72012126510882146 self.assertAlmostEqual(actual_result, expected_result, places=10)
def test_get_risk_contribution_optimised(self): weights = QFSeries([0.5, 0.5], index=self.factors_df.columns) actual_result = RiskContributionAnalysis.get_risk_contribution_optimised( assets_rets=self.factors_df, weights_of_assets=weights) expected_result = QFSeries([0.328845104104390, 0.671154895895610], index=self.factors_df.columns) assert_series_equal(expected_result, actual_result) weights = QFSeries([0.25, 0.75], index=self.factors_df.columns) actual_result = RiskContributionAnalysis.get_risk_contribution_optimised( assets_rets=self.factors_df, weights_of_assets=weights) expected_result = QFSeries([0.139939367445589, 0.860060632554411], index=self.factors_df.columns) assert_series_equal(expected_result, actual_result)
def _generate_exposure_values(self, config: FastAlphaModelTesterConfig, data_handler: FastDataHandler, tickers: Sequence[Ticker]): """ For the given Alpha model and its parameters, generates the dataframe containing all exposure values, that will be returned by the model through signals. """ model = config.generate_model(data_handler) current_exposures_values = QFSeries( index=pd.Index(tickers, name=TICKERS)) current_exposures_values[:] = 0.0 backtest_dates = pd.date_range(self._start_date, self._end_date, freq="B") exposure_values_df = QFDataFrame(index=backtest_dates, columns=pd.Index(tickers, name=TICKERS)) for ticker in tickers: if isinstance(ticker, FutureTicker): # Even if the tickers were already initialize, during pickling process, the data handler and timer # information is lost ticker.initialize_data_provider(self._timer, data_handler) for i, curr_datetime in enumerate(backtest_dates): new_exposures = QFSeries(index=tickers) self._timer.set_current_time(curr_datetime) for j, ticker, curr_exp_value in zip(count(), tickers, current_exposures_values): curr_exp = Exposure(curr_exp_value) if is_finite_number( curr_exp_value) else None try: new_exp = model.calculate_exposure(ticker, curr_exp) except NoValidTickerException: new_exp = None new_exposures.iloc[ j] = new_exp.value if new_exp is not None else None # assuming that we always follow the new_exposures from strategy, disregarding confidence levels # and expected moves, looking only at the suggested exposure current_exposures_values = new_exposures exposure_values_df.iloc[i, :] = current_exposures_values.iloc[:] exposure_values_df = exposure_values_df.dropna(axis=1, how="all") return exposure_values_df
def setUpClass(cls): cls.initial_cash = 1000000 # 1M cls.contract = Contract('AAPL US Equity', security_type='STK', exchange='NYSE') cls.contract_size = 75 cls.fut_contract = Contract('CTZ9 Comdty', security_type='FUT', exchange='CME', contract_size=cls.contract_size) tickers = [DummyTicker(cls.contract.symbol), DummyTicker(cls.fut_contract.symbol)] cls.prices_series = QFSeries(data=[120, 250], index=tickers) cls.prices_up = QFSeries(data=[130, 270], index=tickers) cls.prices_down = QFSeries(data=[100, 210], index=tickers) cls.start_time = str_to_date('2017-01-01') cls.random_time = str_to_date('2017-02-02') cls.end_time = str_to_date('2018-02-03') cls.trades_generator = TradesGenerator()
def setUpClass(cls): cls.initial_cash = 1000000 # 1M cls.ticker = DummyTicker('AAPL US Equity', SecurityType.STOCK) cls.point_value = 75 cls.fut_ticker = DummyTicker('CTZ9 Comdty', SecurityType.FUTURE, cls.point_value) tickers = [cls.ticker, cls.fut_ticker] cls.prices_series = QFSeries(data=[120, 250], index=tickers) cls.prices_up = QFSeries(data=[130, 270], index=tickers) cls.prices_down = QFSeries(data=[100, 210], index=tickers) cls.start_time = str_to_date('2017-01-01') cls.random_time = str_to_date('2017-02-02') cls.end_time = str_to_date('2018-02-03') cls.trades_generator = TradesGenerator()
def test_concat_series(self): index = [1, 2, 3] series_1 = QFSeries(data=[17., 15., 16.], index=index) series_2 = QFSeries(data=[18., 19., 20.], index=index) df = pd.concat([series_1, series_2], axis=1) self.assertEqual(type(df), QFDataFrame) self.assertEqual({s.dtypes for s in [series_1, series_2]}, set(df.dtypes)) series = pd.concat([series_1, series_2], axis=0) self.assertEqual(type(series), QFSeries) self.assertEqual("float64", series.dtypes) self.assertEqual({s.dtypes for s in [series_1, series_2]}, {series.dtypes})
def calculate_relative_rank_logits(self, strategies_names: List): """ Computes relative ranks for the strategies named in the strategies_names list and afterwards calculates the logits. High logit values imply a consistency between IS and OOS performances, which indicates a low lever of backtest overfitting. """ self.create_is_oos_rankings() num_of_strategies = len(self.multiple_returns_timeseries.columns) relative_ranks = QFSeries(data=[ oos_ranking["rank"].loc[best_is_strategy] / (num_of_strategies + 1) for oos_ranking, best_is_strategy in zip(self.oos_ranking, strategies_names) ]) logits = np.log(relative_ranks.divide(1.0 - relative_ranks)) return logits
def _get_exposure(self, regressors_tickers: List, regression_len: int): df = QFDataFrame() for portfolio_date, positions in self.positions_history.iterrows(): positions = positions.dropna() positions_tickers = positions.index.tolist() exposure = QFSeries([x.total_exposure for x in positions]) portfolio_net_liquidation = self.portfolio_nav_history.asof( portfolio_date) positions_allocation = exposure / portfolio_net_liquidation from_date = portfolio_date - RelativeDelta(months=regression_len) coefficients, current_regressors_tickers = self._get_coefficients_and_current_regressors_tickers( regressors_tickers, positions_tickers, positions_allocation, from_date, portfolio_date) df = df.append( QFDataFrame( { col: val for val, col in zip(coefficients, current_regressors_tickers) }, index=[portfolio_date])) # remove missing regressors df = df.dropna(axis=1, how='all') return df
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 ta_series(func: Callable, *args, **kwargs) -> QFSeries: """ Function created to allow using TA-Lib functions with QFSeries. Parameters ---------- func talib function: for example talib.MA args time series arguments to the function. They are all passed as QFSeries. for example: 'close' or 'high, low, close' where each argument is a QFSeries. kwargs additional arguments to the function. for example: 'timeperiod=10' or 'timeperiod=timeperiod, matype=i'. All additional arguments have to be passed as keyword arguments. Returns ------- QFSeries Output from the talib function encapsulated in a QFSeries """ series_list = list(map(lambda series: series.values, args)) result = func(*series_list, **kwargs) result = QFSeries(index=args[0].index, data=result) return result
def _periods_from_int_series( cls, series: QFSeries) -> Sequence[Tuple[datetime, datetime]]: """ Converts a time series with multiple 1/0 values into a condensed list of date ranges specifying where the rectangles should begin and end. For example: 1920-03-31 0.0 1920-06-30 1.0 1920-09-30 1.0 1920-12-31 0.0 For this series, the area from 1920-06-30 to 1920-09-30 will be highlighted. """ result = [] # List[Tuple[start_date, end_date]] start_date = None for index, value in series.iteritems(): if value < 1.0 and start_date is not None: result.append((start_date, index)) start_date = None if value >= 1.0 and start_date is None: start_date = index return result
def _setup_r_square_of_each_predictor(self): regressors_df = self.input_data.regressors_df corr_matrix = regressors_df.corr() corr_matrix = cast_dataframe(corr_matrix, output_type=QFDataFrame) vif = np.diagonal(inv(corr_matrix)) r_squared_values = 1 - (1 / vif) self.r_squared_of_each_predictor = QFSeries(data=r_squared_values, index=regressors_df.columns.copy())