Ejemplo n.º 1
0
    def _get_results_list(self) -> List[Tuple[str]]:
        """
        Returns
        -------
        List[Tuple[str]]
            the list of tuples. Each tuple corresponds to one property of the timeseries and it is of the following format:
            (short_name, long_name, value, unit)

            short_name: is a short string representation that might be treated as a key and should not have spaces in it
            long_name: it a nice name of the field
            value: is the string representation of the value rounded to 2 decimal places
            unit: is an unit in which the value is expressed. Might be empty.

            All elements of the tuple are strings (including the value, which is a string representation
            of the rounded number)
        """

        def num_to_str(value):
            # represents a default
            return "{:0.2f}".format(value)

        result_list = list()

        result_list.append(('start', 'Start Date', date_to_str(self.start_date), ''))
        result_list.append(('end', 'End Date', date_to_str(self.end_date), ''))

        result_list.append(('total_ret', 'Total Return', num_to_str(self.total_return * 100), '%'))
        result_list.append(('cagr', 'Annualised Return', num_to_str(self.cagr * 100), '%'))

        result_list.append(('vol', 'Annualised Volatility', num_to_str(self.annualised_vol * 100), '%'))
        result_list.append(('up_vol', 'Annualised Upside Vol.', num_to_str(self.annualised_upside_vol * 100), '%'))
        result_list.append(('down_vol', 'Annualised Downside Vol.',
                            num_to_str(self.annualised_downside_vol * 100), '%'))

        result_list.append(('sharpe', 'Sharpe Ratio', num_to_str(self.sharpe_ratio), ''))
        result_list.append(('omega', 'Omega Ratio', num_to_str(self.omega_ratio), ''))
        result_list.append(('calmar', 'Calmar Ratio', num_to_str(self.calmar_ratio), ''))
        result_list.append(('gain/pain', 'Gain to Pain Ratio', num_to_str(self.gain_to_pain_ratio), ''))
        result_list.append(('sorino', 'Sorino Ratio', num_to_str(self.sorino_ratio), ''))

        result_list.append(('cvar', '5% CVaR', num_to_str(self.cvar * 100), '%'))
        result_list.append(('cvar_an', 'Annualised 5% CVaR', num_to_str(self.annualised_cvar * 100), '%'))

        result_list.append(('max_dd', 'Max Drawdown', num_to_str(self.max_drawdown * 100), '%'))
        result_list.append(('avg_dd', 'Avg Drawdown', num_to_str(self.avg_drawdown * 100), '%'))
        result_list.append(('avg_dd_dur', 'Avg Drawdown Duration', num_to_str(self.avg_drawdown_duration), 'days'))

        result_list.append(('best_ret', 'Best Return', num_to_str(self.best_return * 100), '%'))
        result_list.append(('worst_ret', 'Worst Return', num_to_str(self.worst_return * 100), '%'))
        result_list.append(('avg_pos_ret', 'Avg Positive Return', num_to_str(self.avg_positive_return * 100), '%'))
        result_list.append(('avg_neg_ret', 'Avg Negative Return', num_to_str(self.avg_negative_return * 100), '%'))

        result_list.append(('skewness', 'Skewness', num_to_str(self.skewness), ''))
        # result_list.append(('kurtosis', 'Kurtosis', num_to_str(self.kurtosis), ''))
        # result_list.append(('kelly', 'Kelly Value', num_to_str(self.kelly), ''))

        freq_str = str(self.frequency)
        result_list.append(('#observ', 'No. of {} samples'.format(freq_str), len(self.returns_tms), ''))

        return result_list
    def create_request_history(self, request_id: str, universe_url: str,
                               fieldlist_url: str, start_date: datetime,
                               end_date: datetime, frequency: Frequency):
        """
        Method to create hapi history request

        Parameters
        ----------
        request_id: str
            ID of request
        universe_url: str
            URL address of the universe
        fieldlist_url: str
            URL address of the fields
        start_date: datetime
            Start of the history data
        end_date: datetime
            End of the history data
        frequency: Frequency
            Frequency of data
        """
        request_payload = {
            '@type': 'HistoryRequest',
            'identifier': request_id,
            'title': 'Request History Payload',
            'description':
            'Request History Payload used in creating fields component',
            'universe': universe_url,
            'fieldList': fieldlist_url,
            'trigger': self.trigger_url,
            'formatting': {
                '@type': 'HistoryFormat',
                'dateFormat': 'yyyymmdd',
                'fileType': 'unixFileType',
                'displayPricingSource': True,
            },
            'runtimeOptions': {
                '@type': 'HistoryRuntimeOptions',
                'historyPriceCurrency': 'USD',
                'period': str(frequency).lower(),
                'dateRange': {
                    '@type': 'IntervalDateRange',
                    'startDate': date_to_str(start_date),
                    'endDate': date_to_str(end_date)
                }
            },
            'pricingSourceOptions': {
                '@type': 'HistoryPricingSourceOptions',
                'exclusive': True
            }
        }

        self.logger.info('Request history component payload:\n%s',
                         pprint.pformat(request_payload))
        self._create_request_common(request_id, request_payload)
Ejemplo n.º 3
0
    def _add_backtest_description(self):
        """
        Adds verbal description of the backtest to the document. The description will be placed on a single page.
        """
        param_names = self._get_param_names()

        self.document.add_element(
            HeadingElement(
                1, "Model: {}".format(self.backtest_summary.backtest_name)))
        self.document.add_element(ParagraphElement("\n"))

        self.document.add_element(
            HeadingElement(2, "Tickers tested in this study: "))
        ticker_str = "\n".join([
            ticker.name
            if isinstance(ticker, FutureTicker) else ticker.as_string()
            for ticker in self.backtest_summary.tickers
        ])
        self.document.add_element(ParagraphElement(ticker_str))
        self.document.add_element(ParagraphElement("\n"))

        self.document.add_element(HeadingElement(2, "Dates of the backtest"))
        self.document.add_element(
            ParagraphElement("Backtest start date: {}".format(
                date_to_str(self.backtest_summary.start_date))))
        self.document.add_element(
            ParagraphElement("Backtest end date: {}".format(
                date_to_str(self.backtest_summary.end_date))))
        self.document.add_element(ParagraphElement("\n"))

        self.document.add_element(HeadingElement(2, "Parameters Tested"))
        for param_index, param_list in enumerate(
                self.backtest_summary.parameters_tested):
            param_list_str = ", ".join(map(str, param_list))
            self.document.add_element(
                ParagraphElement("{} = [{}]".format(param_names[param_index],
                                                    param_list_str)))

        self.document.add_element(NewPageElement())
        self.document.add_element(
            HeadingElement(2, "Alpha model implementation"))
        self.document.add_element(ParagraphElement("\n"))

        model_type = self.backtest_summary.alpha_model_type
        with open(inspect.getfile(model_type)) as f:
            class_implementation = f.read()
        # Remove the imports section
        class_implementation = "<pre>class {}".format(model_type.__name__) + \
                               class_implementation.split("class {}".format(model_type.__name__))[1] + "</pre>"
        self.document.add_element(CustomElement(class_implementation))
Ejemplo n.º 4
0
    def __str__(self):
        string_template = "{class_name:s} ({start_date:s} - {end_date:s}) -> " \
                          "Asset: {asset:>20}, " \
                          "Direction: {direction:>8}, " \
                          "P&L %: {percentage_pnl:>10.2%}, " \
                          "P&L: {pnl:>10.2f}".format(class_name=self.__class__.__name__,
                                                     start_date=date_to_str(self.start_time),
                                                     end_date=date_to_str(self.end_time),
                                                     direction=self.direction,
                                                     asset=self.contract.symbol,
                                                     percentage_pnl=self.percentage_pnl,
                                                     pnl=self.pnl,
                                                     )

        return string_template
Ejemplo n.º 5
0
    def _publish_by_email(self, attachments_dirs: List[str], timestamp):
        class EmailUser(object):
            def __init__(self, name, email_address):
                self.name = name
                self.email_address = email_address

        date_str = date_to_str(timestamp.date())
        template_path = 'live_trading_report.html'

        users = {
            EmailUser("Marcin", "*****@*****.**"),
            # EmailUser("Olga", "*****@*****.**"),
        }

        email_publisher = self.container.resolve(EmailPublisher)

        for user in users:
            email_publisher.publish(mail_to=user.email_address,
                                    subject="Live Trading Summary: " +
                                    date_str,
                                    template_path=template_path,
                                    attachments=attachments_dirs,
                                    context={
                                        'user': user,
                                        'date': date_str
                                    })
    def _check_if_no_missing_data(self, start_date, end_date,
                                  real_contracts_prices_df):
        dates = real_contracts_prices_df.index
        first_available_date = dates[0]
        last_available_date = dates[-1]

        if start_date < first_available_date or end_date > last_available_date:
            real_contract_ticker = real_contracts_prices_df.name  # type: Ticker
            warning_msg = "Missing data for a period [{start_date:s},{end_date:s}]. Data available only " \
                          "for a period [{first_available_date:s},{last_available_date:s}] " \
                          "Contract name: {contract_name:s}" \
                .format(start_date=date_to_str(start_date),
                        end_date=date_to_str(end_date),
                        first_available_date=date_to_str(first_available_date),
                        last_available_date=date_to_str(last_available_date),
                        contract_name=real_contract_ticker.as_string())

            self.logger.warning(warning_msg)
Ejemplo n.º 7
0
    def _insert_table_with_overall_measures(self, prices_df: PricesDataFrame, ticker: Ticker):
        table = Table(column_names=["Measure", "Value"], css_class="table stats-table")

        table.add_row(["Instrument", ticker.as_string()])
        series = prices_df[PriceField.Close]
        table.add_row(["Start date", date_to_str(series.index[0])])
        table.add_row(["End date", date_to_str(series.index[-1])])

        trend_strength_overall = trend_strength(prices_df, self.use_next_open_instead_of_close)
        table.add_row(["Overall strength of the day trends", trend_strength_overall])

        trend_strength_1y = trend_strength(prices_df.tail(252), self.use_next_open_instead_of_close)
        table.add_row(["Strength of the day trends in last 1Y", trend_strength_1y])
        self.ticker_to_trend_dict[ticker] = (trend_strength_1y, trend_strength_overall)

        table.add_row(["Up trends strength", up_trend_strength(prices_df, self.use_next_open_instead_of_close)])
        table.add_row(["Down trends strength", down_trend_strength(prices_df, self.use_next_open_instead_of_close)])
        self.document.add_element(table)
Ejemplo n.º 8
0
 def __str__(self):
     string = "{}\n" \
              "\tlive trading date: {}" \
              "\tinitial risk: {}" \
              "\tmodel type to tickers dict: {}" \
              "\tin sample returns statistics: {}" \
              "\ttitle: {}".format(self.__class__.__name__, date_to_str(self.live_start_date), self.initial_risk,
                                   self.model_type_tickers_dict, self.is_returns_stats, self.title)
     return string
Ejemplo n.º 9
0
    def _get_history_from_timeseries(self, tickers: Sequence[QuandlTicker],
                                     fields: Sequence[str],
                                     start_date: datetime, end_date: datetime):
        """
        NOTE: Only use one Quandl Database at the time. Do not mix multiple databases.
        """
        tickers = list(tickers)  # allows iterating the sequence more then once
        tickers_str = [t.as_string() for t in tickers]

        kwargs = {}
        if start_date is not None:
            kwargs['start_date'] = date_to_str(start_date)
        if end_date is not None:
            kwargs['end_date'] = date_to_str(end_date)

        data = quandl.get(tickers_str, **kwargs)  # type: pd.DataFrame

        def extract_ticker_name(column_name):
            ticker_str, _ = column_name.split(' - ')
            ticker = QuandlTicker.from_string(ticker_str)
            return ticker

        ticker_grouping = data.groupby(extract_ticker_name, axis=1)
        ticker_to_df = {
        }  # type: Dict[str, pd.DataFrame]  # string -> DataFrame[dates, fields]
        for ticker, ticker_data_df in ticker_grouping:
            tickers_and_fields = (column_name.split(' - ')
                                  for column_name in ticker_data_df.columns)
            field_names = [field for (ticker, field) in tickers_and_fields]
            ticker_data_df.columns = field_names

            if fields is not None:
                # select only required fields
                ticker_data_df = self._select_only_required_fields(
                    ticker, ticker_data_df, fields)

                # if there was no data for the given ticker, skip the ticker
                if ticker_data_df is None:
                    continue

            ticker_to_df[ticker] = ticker_data_df

        return ticker_to_df
Ejemplo n.º 10
0
    def __str__(self):
        string_template = "{datetime_str:s} - {class_name:<25} -> Contract: {contract_str:>30}," \
                          "Quantity: {quantity:>7}, Price: {price:>7.2f}, " \
                          "Commission: {commission:>7.2f}".format(datetime_str=date_to_str(self.time),
                                                                  class_name=self.__class__.__name__,
                                                                  contract_str=str(self.contract),
                                                                  quantity=self.quantity,
                                                                  price=self.price,
                                                                  commission=self.commission)

        return string_template
Ejemplo n.º 11
0
    def table_for_df(df: QFDataFrame,
                     frequency: Frequency = Frequency.DAILY) -> str:
        """
        df
            DataFrame of returns or prices of assets to be analysed

        frequency
            (optional) frequency of the returns or price sampling in the DataFrame. By default daily frequency is used

        Returns
        ----------
        returns a table similar to the one below:

        Analysed period: start_date - end_date, using frequency data
        Name            total_ret        cagr         vol      up_vol    down_vol ...
        Asset1              63.93       28.27       19.15       14.06       14.35 ...
        Asset2              66.26       29.19       20.74       14.86       15.54 ...
        Asset3              66.26       29.19       20.74       14.86       15.54 ...
        ...                   ...         ...         ...         ...         ... ...

        """
        name_ta_list = [(name, TimeseriesAnalysis(asset_tms, frequency))
                        for name, asset_tms in df.iteritems()]
        first_ta = name_ta_list[0][1]

        result = "Analysed period: {} - {}, using {} data\n".format(
            date_to_str(first_ta.start_date), date_to_str(first_ta.end_date),
            frequency)

        header_without_dates = ""
        for value in first_ta.get_short_names()[2:]:
            header_without_dates += '{:>12}'.format(value)

        result += ("{:12} {}\n".format("Name", header_without_dates))

        for name, ta in name_ta_list:
            values = ""
            for value in ta.get_measures()[2:]:
                values += '{:>12}'.format(value)
            result += ("{:12} {}\n".format(name.as_string(), values))
        return result
Ejemplo n.º 12
0
    def __str__(self):
        string_template = "{class_name:s} ({datetime_str:s}) -> " \
                          "Quantity: {quantity:>8}, " \
                          "Price: {price:>10.2f}, " \
                          "Commission: {commission:>7.2f}, " \
                          "Contract: {contract_str:}".format(class_name=self.__class__.__name__,
                                                             datetime_str=date_to_str(self.time),
                                                             quantity=self.quantity,
                                                             price=self.price,
                                                             commission=self.commission,
                                                             contract_str=str(self.contract)
                                                             )

        return string_template
Ejemplo n.º 13
0
def add_backtest_description(document: Document,
                             backtest_result: BacktestSummary,
                             param_names: List[str]):
    """
    Adds verbal description of the backtest to the document. The description will be placed on a single page.
    """

    document.add_element(ParagraphElement("\n"))
    document.add_element(
        HeadingElement(1, "Model: {}".format(backtest_result.backtest_name)))
    document.add_element(ParagraphElement("\n"))

    document.add_element(HeadingElement(2, "Tickers tested in this study: "))
    ticker_str = "\n".join(
        [ticker.as_string() for ticker in backtest_result.tickers])
    document.add_element(ParagraphElement(ticker_str))
    document.add_element(ParagraphElement("\n"))

    document.add_element(HeadingElement(2, "Dates of the backtest"))
    document.add_element(
        ParagraphElement("Backtest start date: {}".format(
            date_to_str(backtest_result.start_date))))
    document.add_element(
        ParagraphElement("Backtest end date: {}".format(
            date_to_str(backtest_result.end_date))))

    document.add_element(ParagraphElement("\n"))

    document.add_element(HeadingElement(2, "Parameters Tested"))
    for param_index, param_list in enumerate(
            backtest_result.parameters_tested):
        param_list_str = ", ".join(map(str, param_list))
        document.add_element(
            ParagraphElement("{} = [{}]".format(param_names[param_index],
                                                param_list_str)))

    document.add_element(NewPageElement())
Ejemplo n.º 14
0
    def different_allocations_tms(cls, assets_rets_df: SimpleReturnsDataFrame, allocations_df: QFDataFrame) \
            -> SimpleReturnsSeries:
        """
        Calculates the time series of portfolio returns given the allocations on each date. The portfolio returns
        are calculated by multiplying returns of assets by corresponding allocations' values.

        Parameters
        ----------
        assets_rets_df
            simple returns of assets which create the portfolio
        allocations_df
            dataframe indexed with dates, showing allocations in time (one column per asset)

        Returns
        -------
        portfolio_rets_tms
            timeseries of portfolio's returns
        """
        assert np.all(assets_rets_df.columns.values == allocations_df.columns.values), \
            "Different column values for assets and allocation matrix"
        assert np.all(assets_rets_df.index.values == allocations_df.index.values), \
            "Different dates for assets and allocation matrix"

        # get indices of rows for which: sum of weights is greater than 1. The result of where is a tuple (for a vector
        # it's a 1-element tuple, for a matrix -- a 2-element tuple and so on). Thus it's necessary to unwrap the result
        # from a tuple, to get the array of indices (instead of 1-elem. tuple consisted of an array).
        incorrect_weights_rows = np.abs(allocations_df.sum(axis=1) -
                                        1.0) > cls.EPSILON  # type: np.ndarray

        if np.any(incorrect_weights_rows):
            dates = allocations_df.index.values[incorrect_weights_rows]
            dates_str = ", ".join([date_to_str(date) for date in dates])

            cls.logger().warning(
                "Weights don't sum up to 1 for the following dates: " +
                dates_str)

        scaled_returns = assets_rets_df * allocations_df  # type: np.ndarray
        portfolio_rets = scaled_returns.sum(axis=1)
        portfolio_rets_tms = SimpleReturnsSeries(
            data=portfolio_rets, index=allocations_df.index.copy())

        return portfolio_rets_tms
Ejemplo n.º 15
0
    def __init__(self,
                 logo_path: str = None,
                 major_line: str = "",
                 minor_line: str = "",
                 date: datetime = None,
                 grid_proportion: GridProportion = GridProportion.Eight):
        """
        A stylised header element, consists of a major title (on left and right), subtitle and logo
        (loaded from the specified path).
        """
        super().__init__(grid_proportion)
        self.logo_path = logo_path

        self.major_line = major_line
        self.minor_line = minor_line

        if date is None:
            date = datetime.date.today()

        self.date = date_to_str(date, DateFormat.LONG_DATE)
Ejemplo n.º 16
0
 def test_date_to_str(self):
     expected_str = '2000-11-30'
     actual_date = date_to_str(datetime.datetime(year=2000, month=11, day=30))
     self.assertEqual(actual_date, expected_str)
Ejemplo n.º 17
0
    def __init__(self, data_provider: DataProvider, start_date, end_date, initial_cash,
                 frequency: Frequency = Frequency.MIN_1):
        """
        Set up the backtest variables according to what has been passed in.
        """
        super().__init__()
        self.logger = qf_logger.getChild(self.__class__.__name__)

        self.logger.info(
            "\n".join([
                "Testing the Backtester:",
                "Start date: {:s}".format(date_to_str(start_date)),
                "End date: {:s}".format(date_to_str(end_date)),
                "Initial cash: {:.2f}".format(initial_cash),
                "Frequency of the simulated execution handler: {}".format(frequency)
            ])
        )

        timer = SettableTimer(start_date)
        notifiers = Notifiers(timer)
        if frequency <= Frequency.DAILY:
            data_handler = DailyDataHandler(data_provider, timer)
        else:
            data_handler = IntradayDataHandler(data_provider, timer)

        contract_ticker_mapper = SimulatedBloombergContractTickerMapper()
        portfolio = Portfolio(data_handler, initial_cash, timer, contract_ticker_mapper)
        signals_register = BacktestSignalsRegister()
        backtest_result = BacktestResult(portfolio=portfolio, backtest_name="Testing the Backtester",
                                         start_date=start_date, end_date=end_date, signals_register=signals_register)

        monitor = Mock(spec=BacktestMonitor)
        commission_model = FixedCommissionModel(0.0)
        slippage_model = PriceBasedSlippage(0.0, data_provider, contract_ticker_mapper)

        execution_handler = SimulatedExecutionHandler(
            data_handler, timer, notifiers.scheduler, monitor, commission_model,
            contract_ticker_mapper, portfolio, slippage_model)

        broker = BacktestBroker(portfolio, execution_handler)
        order_factory = OrderFactory(broker, data_handler, contract_ticker_mapper)

        event_manager = self._create_event_manager(timer, notifiers)
        time_flow_controller = BacktestTimeFlowController(
            notifiers.scheduler, event_manager, timer, notifiers.empty_queue_event_notifier, end_date
        )
        position_sizer = SimplePositionSizer(broker, data_handler, order_factory, contract_ticker_mapper,
                                             signals_register)

        self.logger.info(
            "\n".join([
                "Configuration of components:",
                "Position sizer: {:s}".format(position_sizer.__class__.__name__),
                "Timer: {:s}".format(timer.__class__.__name__),
                "Data Provider: {:s}".format(data_provider.__class__.__name__),
                "Backtest Result: {:s}".format(backtest_result.__class__.__name__),
                "Monitor: {:s}".format(monitor.__class__.__name__),
                "Execution Handler: {:s}".format(execution_handler.__class__.__name__),
                "Commission Model: {:s}".format(commission_model.__class__.__name__),
                "Broker: {:s}".format(broker.__class__.__name__),
                "Contract-Ticker Mapper: {:s}".format(contract_ticker_mapper.__class__.__name__)
            ])
        )

        self.broker = broker
        self.notifiers = notifiers
        self.initial_cash = initial_cash
        self.start_date = start_date
        self.end_date = end_date
        self.event_manager = event_manager
        self.contract_ticker_mapper = contract_ticker_mapper
        self.data_handler = data_handler
        self.portfolio = portfolio
        self.execution_handler = execution_handler
        self.position_sizer = position_sizer
        self.orders_filters = []
        self.monitor = monitor
        self.timer = timer
        self.order_factory = order_factory
        self.time_flow_controller = time_flow_controller