Ejemplo n.º 1
0
def get_historical_net_income(cashflow_statements : dict):
    """
        Extracts the historical net income from the supplied cashflow statements

        Parameters
        ----------
        cashflow_statements : dict
            a dictionary of cashflow statements keyed by year, as returned by
            intrinio_data.intrinio_data.get_historical_cashflow_stmt

        Returns
        -------
        A dictionary of year=>"Net Income" values. For example

        {
            2010: 16590000000.0,
            2011: 33269000000.0
        }
    """
    net_income_dict = {}

    if cashflow_statements == None:
        raise DataError("Could not compute historical net income, because of invalid input", None)


    try:
        for year, cashflow in sorted(cashflow_statements.items()):
            net_income_dict[year] = cashflow['netincome']
    except KeyError as ke:
        raise DataError("Could not compute historical net income, because item is missing from the cash flow statement", ke)

    return net_income_dict
    def test_simple_exception_nocause(self):

        self.assertEqual(str(ValidationError("Cannot do XYZ", None)), "Validation Error: Cannot do XYZ")
        self.assertEqual(str(CalculationError("Cannot do XYZ", None)), "Calculation Error: Cannot do XYZ")
        self.assertEqual(str(DataError("Cannot do XYZ", None)), "Data Error: Cannot do XYZ")
        self.assertEqual(str(ReportError("Cannot do XYZ", None)), "Report Error: Cannot do XYZ")

        self.assertEqual(repr(ValidationError("Cannot do XYZ", None)), "Validation Error: Cannot do XYZ")
        self.assertEqual(repr(CalculationError("Cannot do XYZ", None)), "Calculation Error: Cannot do XYZ")
        self.assertEqual(repr(DataError("Cannot do XYZ", None)), "Data Error: Cannot do XYZ")
        self.assertEqual(repr(ReportError("Cannot do XYZ", None)), "Report Error: Cannot do XYZ")
Ejemplo n.º 3
0
    def test_test_intrinio_connectivity_with_exception(self):
        with patch.object(intrinio_data,
                          'test_api_endpoint',
                          side_effect=DataError("Some Error", None)):

            with self.assertRaises(DataError):
                connector_test.test_intrinio_connectivity()
Ejemplo n.º 4
0
def _read_company_data_point(ticker: str, tag: str):
    """
      Helper function that will read the Intrinio company API for the supplied ticker
      and return the value


      Returns
      -------
      The numerical value of the datapoint
    """

    # check the cache first
    cache_key = "%s-%s-%s-%s" % (INTRINIO_CACHE_PREFIX,
                                 "company_data_point_number", ticker, tag)
    api_response = cache.read(cache_key)

    if api_response is None:
        # else call the API directly
        try:
            api_response = COMPANY_API.get_company_data_point_number(
                ticker, tag)

            cache.write(cache_key, api_response)
        except ApiException as ae:
            raise DataError(
                "Error retrieving ('%s') -> '%s' from Intrinio Company API" %
                (ticker, tag), ae)
        except Exception as e:
            raise ValidationError(
                "Error parsing ('%s') -> '%s' from Intrinio Company API" %
                (ticker, tag), e)

    return api_response
Ejemplo n.º 5
0
def mark_to_market(data_frame: object, price_date: datetime):
    """
        Peforms a Mark to Market on a Pandas dataframe representing
        a ranked portfolio, and given a price date. This is used
        to calculate returns on a portfolio generated by one of the
        strategies

        The dataframe must contain the following columuns:

        * ticker
        * analysis_price

        and will add:

        * current_price
        * actual_return

        Parmeters
        ---------
        data_frame : Pandas DataFrame
            portfolio dataframe
        price_date : datetime
            price date, current or historical

        Returns
        ---------
        A new dataframe with the added columns

        Raises
        ---------
        ValidationError if parameters are incorrect
        DataError if there are problems reading price data
    """

    if (data_frame is None or price_date is None):
        raise ValidationError(
            "Invalid Parameters supplied to Mark to Market calculation", None)

    if ('ticker' not in data_frame.columns
            or 'analysis_price' not in data_frame.columns):
        raise ValidationError(
            "Could not extract required fields for Mark to Market calculation",
            None)

    mmt_prices = []

    for ticker in data_frame['ticker']:
        try:
            latest_price = intrinio_data.get_latest_close_price(
                ticker, price_date, 5)[1]
            mmt_prices.append(latest_price)
        except Exception as e:
            raise DataError("Could not perform MMT calculation", e)

    data_frame['current_price'] = mmt_prices
    data_frame['actual_return'] = (
        data_frame['current_price'] -
        data_frame['analysis_price']) / data_frame['analysis_price']
    return data_frame
def mark_to_market(data_frame: object, ticker_col_name: str,
                   price_col_name: str, price_date: date):
    """
        Peforms a Mark to Market on a Pandas dataframe representing
        a set of stocks given a price date. This is used
        to calculate returns on a portfolio generated by one of the
        strategies

        The dataframe must contain a ticker and price column, which are defined
        via the parameters and will add:

        * current_price
        * actual_return

        Parmeters
        ---------
        data_frame: Pandas DataFrame
            Portfolio dataframe
        ticker_col_name: str
            Name of the ticker column
        price_col_name: str
            Name of the price column
        price_date: date
            Price date, current or historical

        Returns
        ---------
        A new dataframe with the added columns

    """

    if (data_frame is None or price_date is None):
        raise ValidationError(
            "Invalid Parameters supplied to Mark to Market calculation", None)

    if (ticker_col_name not in data_frame.columns
            or price_col_name not in data_frame.columns):
        raise ValidationError(
            "Could not extract required fields for Mark to Market calculation",
            None)

    mmt_prices = []

    for ticker in data_frame[ticker_col_name]:
        try:
            latest_price = intrinio_data.get_daily_stock_close_prices(
                ticker, price_date, price_date)

            mmt_prices.append(latest_price[price_date.strftime("%Y-%m-%d")])
        except Exception as e:
            raise DataError("Could not perform MMT calculation", e)

    data_frame['current_price'] = mmt_prices
    data_frame['actual_return'] = (
        data_frame['current_price'] -
        data_frame[price_col_name]) / data_frame[price_col_name]
    return data_frame
    def test_retry_server_errors_api_error_501(self):
        with patch.object(time, 'sleep', return_value=None):
            mock = Mock(side_effect=DataError(
                "Mock error", ApiException(501, "Mock Error")))
            test_function = intrinio_data.retry_server_errors(mock)

            with self.assertRaises(DataError):
                test_function()

            self.assertEqual(mock.call_count, self.RETRY_ERROR_COUNT)
    def test_create_empty_portfolio_no_prices(self):
        with patch.object(intrinio_data,
                          'get_latest_close_price',
                          side_effect=DataError("test exception", None)):

            recommendation_set = SecurityRecommendationSet.from_dict(
                self.sr_dict)

            with self.assertRaises(DataError):
                portfolio = Portfolio()
                portfolio.create_empty_portfolio(recommendation_set)
Ejemplo n.º 9
0
def test_api_endpoint():
    """
      Tests the API endpoint directly and throws a DataError if
      anything goes wrong. 
      This is used to validate that the API key works
    """

    url = 'https://api-v2.intrinio.com/companies/AAPL'

    try:
        response = requests.request('GET',
                                    url,
                                    params={'api_key': API_KEY},
                                    timeout=10)
    except Exception as e:
        raise DataError("Could not execute GET to %s" % url, e)

    if not response.ok:
        raise DataError("Invalid response from Intrinio Endpoint",
                        Exception(response.text))
Ejemplo n.º 10
0
def test_intrinio_connectivity():
    '''
        Makes a direct call to the intrio API (bypassing the cache) to verify the API
        key is still valid
    '''
    log.info("Testing Intrinio connectivity")
    try:
        intrinio_data.test_api_endpoint()
        log.info("Intrinio connectivity test successful")
    except DataError as de:
        raise DataError("Intrino Connectivity Test failed", de.cause)
Ejemplo n.º 11
0
def get_historical_simple_fcfe(cashflow_statements : dict):
    """
        retuns historical free cash flow to equity using a simple formula
        and based of values on the cashflow statement.

        Simple FCFE is an estimate computed as:

        FCF = Operating Income - CAPEX

        Parameters
        ----------
        cashflow_statements : dict
            a dictionary of cashflow statements keyed by year, as returned by
            intrinio_data.intrinio_data.get_historical_cashflow_stmt

        Returns
        -------
        A dictionary of year=>FCF values. For example

        {
            2010: 16590000000.0,
            2011: 33269000000.0
        }
    """
    fcf_dict = {}

    if cashflow_statements == None:
        raise DataError("Could not compute historical fcfe, because of invalid input", None)

    try:
        for year, cashflow in sorted(cashflow_statements.items()):
            fcf = cashflow['netcashfromcontinuingoperatingactivities'] + \
                cashflow['purchaseofplantpropertyandequipment']
            fcf_dict[year] = fcf
    except KeyError as ke:
        raise DataError("Could not compute historical fcfe, because of missing information in the cash flow statement", ke)

    return fcf_dict
    def _load_financial_data(self):
        """
            loads the raw financial required by this strategy and returns it as
            a dictionary suitable for Pandas processing.

            Returns
            ------------
            A Dictionary with the following format.

            {
                'analysis_period': [],
                'ticker': [],
                'analysis_price': [],
                'target_price_avg': [],
                'dispersion_stdev_pct': [],
                'analyst_expected_return': []
            }

            Raises
            ------------
            DataError in case financial data could not be loaed for any
            securities
        """

        logging.debug("Loading financial data for %s strategy" %
                      self.STRATEGY_NAME)

        financial_data = {
            'analysis_period': [],
            'ticker': [],
            'analysis_price': [],
            'target_price_avg': [],
            'dispersion_stdev_pct': [],
            'analyst_expected_return': []
        }

        dds = self.analysis_start_date
        dde = self.analysis_end_date
        year = dds.year
        month = dds.month

        at_least_one = False

        logging.debug("Analysis date range is %s, %s" %
                      (dds.strftime("%Y-%m-%d"), dde.strftime("%Y-%m-%d")))
        logging.debug("Analysis price date is %s" %
                      (self.current_price_date.strftime("%Y-%m-%d")))

        for ticker in self.ticker_list.ticker_symbols:
            try:
                target_price_sdtdev = intrinio_data.get_zacks_target_price_std_dev(
                    ticker, dds, dde)[year][month]
                target_price_avg = intrinio_data.get_zacks_target_price_mean(
                    ticker, dds, dde)[year][month]
                dispersion_stdev_pct = target_price_sdtdev / target_price_avg * 100

                analysis_price = intrinio_data.get_latest_close_price(
                    ticker, dde, 5)[1]

                analyst_expected_return = (target_price_avg -
                                           analysis_price) / analysis_price

                financial_data['analysis_period'].append(self.analysis_period)
                financial_data['ticker'].append(ticker)
                financial_data['analysis_price'].append(analysis_price)
                financial_data['target_price_avg'].append(target_price_avg)
                financial_data['dispersion_stdev_pct'].append(
                    dispersion_stdev_pct)
                financial_data['analyst_expected_return'].append(
                    analyst_expected_return)

                at_least_one = True
            except BaseError as be:
                logging.debug(
                    "%s will not be factored in recommendation, because: %s" %
                    (ticker, str(be)))
            except Exception as e:
                raise DataError("Could not read %s financial data" % (ticker),
                                e)

        if not at_least_one:
            raise DataError(
                "Could not load financial data for any if the supplied tickers",
                None)

        return financial_data
Ejemplo n.º 13
0
def _read_historical_financial_statement(ticker: str, statement_name: str,
                                         year_from: int, year_to: int,
                                         tag_filter_list: list):
    """
      This helper function will read standardized fiscal year end financials from the Intrinio fundamentals API
      for each year in the supplied range, and normalize the results into simpler user friendly
      dictionary, for example:

      {
        'netcashfromcontinuingoperatingactivities': 77434000000.0,
        'purchaseofplantpropertyandequipment': -13313000000
      }

      results may also be filtered based on the tag_filter_list parameter, which may include
      just the tags that should be returned.

      Parameters
      ----------
      ticker : str
        Ticker Symbol
      statement_name : str
        The name of the statement to read.
      year_from : int
        Start year of financial statement list
      year_to : int
        End year of the financial statement list
      tag_filter_list : list
        List of data tags used to filter results. The name of each tag
        must match an expected one from the Intrinio API. If "None", then all
        tags will be returned.

      Returns
      -------
      A dictionary of tag=>value with the filtered results. For example:

      {
        'netcashfromcontinuingoperatingactivities': 77434000000.0,
        'purchaseofplantpropertyandequipment': -13313000000
      }

      Note that the name of the tags are specific to the Intrinio API

    """
    # return value
    hist_statements = {}
    ticker = ticker.upper()

    statement_type = 'FY'

    try:
        for i in range(year_from, year_to + 1):
            satement_name = ticker + "-" + \
                statement_name + "-" + str(i) + "-" + statement_type

            cache_key = "%s-%s-%s-%s-%s-%d" % (
                INTRINIO_CACHE_PREFIX, "statement", ticker, statement_name,
                statement_type, i)
            statement = cache.read(cache_key)

            if statement is None:
                statement = FUNDAMENTALS_API.get_fundamental_standardized_financials(
                    satement_name)

                cache.write(cache_key, statement)

            hist_statements[i] = _transform_financial_stmt(
                statement.standardized_financials, tag_filter_list)

    except ApiException as ae:
        raise DataError(
            "Error retrieving ('%s', %d - %d) -> '%s' from Intrinio Fundamentals API"
            % (ticker, year_from, year_to, statement_name), ae)

    return hist_statements
Ejemplo n.º 14
0
def get_sma_indicator(ticker: str, start_date: datetime, end_date: datetime,
                      period_days: int):
    '''
      Returns a dictionary of SMA (simple moving average) indicators given a 
      ticker symbol, a date range and the period.  

      Currently only returns one page of 100 results

      Parameters
      ----------
      ticker : str
        Ticker Symbol
      start_date : object
        The beginning price date as python date object
      end_date : object
        The end price date as python date object
      period_days: int
        The number of price days included in this average


      Returns
      -----------
      a dictionary of date->price like this
      {
        "2020-05-29": 282.51779999999997,
        "2020-05-28": 281.09239999999994,
        "2020-05-27": 279.7845999999999,
        "2020-05-26": 278.26659999999987,
        "2020-05-22": 277.4913999999999,
        "2020-05-21": 276.07819999999987,
        "2020-05-20": 275.2497999999999
      }

    '''

    start_date_str = intrinio_util.date_to_string(start_date).replace(
        '-', '').replace('-', '')
    end_date_str = intrinio_util.date_to_string(end_date).replace('-',
                                                                  '').replace(
                                                                      '-', '')

    sma_dict = {}

    cache_key = "%s-%s-%s-%s-%d-%s" % (INTRINIO_CACHE_PREFIX, ticker,
                                       start_date_str, end_date_str,
                                       period_days, "tech-sma")
    api_response = cache.read(cache_key)

    if api_response is None:
        try:
            api_response = SECURITY_API.get_security_price_technicals_sma(
                ticker,
                period=period_days,
                price_key='close',
                start_date=start_date,
                end_date=end_date,
                page_size=100)

            cache.write(cache_key, api_response)
        except ApiException as ae:
            raise DataError(
                "API Error while reading SMA indicator from Intrinio Security API: ('%s', %s - %s (%d))"
                % (ticker, start_date_str, end_date_str, period_days), ae)
        except Exception as e:
            raise ValidationError(
                "Unknown Error while reading SMA indicator from Intrinio Security API: ('%s', %s - %s (%d))"
                % (ticker, start_date_str, end_date_str, period_days), e)

    sma_list = api_response.technicals

    if len(sma_list) == 0:
        raise DataError(
            "No SMA indicators returned from Intrinio Security API: ('%s', %s - %s (%d))"
            % (ticker, start_date_str, end_date_str, period_days), None)

    for sma in sma_list:
        sma_dict[intrinio_util.date_to_string(sma.date_time)] = sma.sma

    return sma_dict
Ejemplo n.º 15
0
def get_macd_indicator(ticker: str, start_date: datetime, end_date: datetime,
                       fast_period: int, slow_period: int, signal_period: int):
    '''
      Returns a dictionary of MACD indicators given a ticker symbol,
      a date range and necessary MACD parameters.  
      Currently only returns one page of 100 results

      Parameters
      ----------
      ticker : str
        Ticker Symbol
      start_date : object
        The beginning price date as python date object
      end_date : object
        The end price date as python date object
      fast_period: int
        the MACD fast period parameter
      slow_perdiod: int
        the MACD slow period parameter
      signal_period:
        the MACD signal period parameter

      Returns
      -----------
      a dictionary of date->price like this
      {
          "2020-05-29": {
              "macd_histogram": -0.5565262759342229,
              "macd_line": 9.361568685377279,
              "signal_line": 9.918094961311501
          },
          "2020-05-28": {
              "macd_histogram": -0.3259226480613542,
              "macd_line": 9.731303882233703,
              "signal_line": 10.057226530295058
          }
      }
    '''

    start_date_str = intrinio_util.date_to_string(start_date).replace(
        '-', '').replace('-', '')
    end_date_str = intrinio_util.date_to_string(end_date).replace('-',
                                                                  '').replace(
                                                                      '-', '')

    macd_dict = {}

    cache_key = "%s-%s-%s-%s-%d.%d.%d-%s" % (
        INTRINIO_CACHE_PREFIX, ticker, start_date_str, end_date_str,
        fast_period, slow_period, signal_period, "tech-macd")
    api_response = cache.read(cache_key)

    if api_response is None:
        try:
            api_response = SECURITY_API.get_security_price_technicals_macd(
                ticker,
                fast_period=fast_period,
                slow_period=slow_period,
                signal_period=signal_period,
                price_key='close',
                start_date=start_date,
                end_date=end_date,
                page_size=100)

            cache.write(cache_key, api_response)
        except ApiException as ae:
            raise DataError(
                "API Error while reading MACD indicator from Intrinio Security API: ('%s', %s - %s (%d, %d, %d))"
                % (ticker, start_date_str, end_date_str, fast_period,
                   slow_period, signal_period), ae)
        except Exception as e:
            raise ValidationError(
                "Unknown Error while reading MACD indicator from Intrinio Security API: ('%s', %s - %s (%d, %d, %d))"
                % (ticker, start_date_str, end_date_str, fast_period,
                   slow_period, signal_period), e)

    macd_list = api_response.technicals

    if len(macd_list) == 0:
        raise DataError(
            "No MACD indicators returned from Intrinio Security API: ('%s', %s - %s (%d, %d, %d))"
            % (ticker, start_date_str, end_date_str, fast_period, slow_period,
               signal_period), None)

    for macd in macd_list:
        macd_dict[intrinio_util.date_to_string(macd.date_time)] = {
            "macd_histogram": macd.macd_histogram,
            "macd_line": macd.macd_line,
            "signal_line": macd.signal_line
        }

    return macd_dict
Ejemplo n.º 16
0
def get_daily_stock_close_prices(ticker: str, start_date: datetime,
                                 end_date: datetime):
    '''
      Returns a list of historical daily stock prices given a ticker symbol and
      a range of dates.  Currently only returns one page of 100 results

      Parameters
      ----------
      ticker : str
        Ticker Symbol
      start_date : object
        The beginning price date as python date object
      end_date : object
        The end price date as python date object

      Returns
      -----------
      a dictionary of date->price like this
      {
        '2019-10-01': 100,
        '2019-10-02': 101,
        '2019-10-03': 102,
        '2019-10-04': 103,
      }
    '''

    start_date_str = intrinio_util.date_to_string(start_date).replace('-', '')
    end_date_str = intrinio_util.date_to_string(end_date).replace('-', '')

    price_dict = {}

    cache_key = "%s-%s-%s-%s-%s" % (INTRINIO_CACHE_PREFIX, ticker,
                                    start_date_str, end_date_str,
                                    "closing-prices")
    api_response = cache.read(cache_key)

    if api_response is None:
        try:
            api_response = SECURITY_API.get_security_stock_prices(
                ticker,
                start_date=start_date_str,
                end_date=end_date_str,
                frequency='daily',
                page_size=100)
            cache.write(cache_key, api_response)
        except ApiException as ae:
            raise DataError(
                "API Error while reading price data from Intrinio Security API: ('%s', %s - %s)"
                % (ticker, start_date_str, end_date_str), ae)
        except Exception as e:
            raise ValidationError(
                "Unknown Error while reading price data from Intrinio Security API: ('%s', %s - %s)"
                % (ticker, start_date_str, end_date_str), e)

    price_list = api_response.stock_prices

    if len(price_list) == 0:
        raise DataError(
            "No prices returned from Intrinio Security API: ('%s', %s - %s)" %
            (ticker, start_date_str, end_date_str), None)

    for price in price_list:
        price_dict[intrinio_util.date_to_string(price.date)] = price.close

    return price_dict
Ejemplo n.º 17
0
def _get_company_historical_data(ticker: str, start_date: str, end_date: str,
                                 tag: str):
    """
      Helper function that will read the Intrinio company API for the supplied date range

      Parameters
      ----------
      ticker : str
        Ticker symbol. E.g. 'AAPL'
      start_date : str
        Start date of the metric formatted as YYYY-MM-DD
      end_date : str
        End date of the metric formatted as YYYY-MM-DD
      tag : the metric name to retrieve

      Raises
      -------
      DataError in case of any error calling the intrio API
      ValidationError in case of an unknown exception

      Returns
      -------
      The 'historical_data_dict' portion of the 'get_company_historical_data'

      [
        {'date': datetime.date(2018, 9, 29), 'value': 265595000000.0},
        {'date': datetime.date(2017, 9, 30), 'value': 229234000000.0}
      ]
    """

    frequency = 'yearly'

    # check the cache first
    cache_key = "%s-%s-%s-%s-%s-%s-%s" % (INTRINIO_CACHE_PREFIX,
                                          "company_historical_data", ticker,
                                          start_date, end_date, frequency, tag)
    api_response = cache.read(cache_key)

    if api_response is None:
        # else call the API directly
        try:
            api_response = COMPANY_API.get_company_historical_data(
                ticker,
                tag,
                frequency=frequency,
                start_date=start_date,
                end_date=end_date)
        except ApiException as ae:
            raise DataError(
                "Error retrieving ('%s', %s - %s) -> '%s' from Intrinio Company API"
                % (ticker, start_date, end_date, tag), ae)
        except Exception as e:
            raise ValidationError(
                "Error parsing ('%s', %s - %s) -> '%s' from Intrinio Company API"
                % (ticker, start_date, end_date, tag), e)

    if len(api_response.historical_data) == 0:
        raise DataError(
            "No Data returned for ('%s', %s - %s) -> '%s' from Intrinio Company API"
            % (ticker, start_date, end_date, tag), None)
    else:
        # only write to cache if response has some valid data
        cache.write(cache_key, api_response)

    return api_response.historical_data_dict
def __read_financial_metrics__(ticker: str, start_year: int, end_year: int,
                               tag: str):
    """
      Helper function that will read the Intrinio company API for the supplied date range
      and convert the resulting list into a more friendly dictionary.

      Specifically a result like this

      [
        {
          'date': datetime.date(2010, 1, 1),
          'value': 123
        },
      ]

      into this:

      [{
        2010: 123,
      }]

      Parameters
      ----------
      ticker : str
        Ticker symbol. E.g. 'AAPL'
      start_year : int
        Start year of the metric data
      end_year : int
        End year of the metric data
      tag : the metric name to retrieve

      Raises
      -------
      DataError in case of any error calling the intrio API
      ValidationError in case of an unknown exception


      Returns
      -------
      A dictionary of year=>value with the filtered results. For example:

      {
        2010: 123,
        2012: 234,
        2013: 345,
        2014: 456,
      }
    """
    (start_date, x) = intrinio_util.get_fiscal_year_period(start_year, 0)
    (x, end_date) = intrinio_util.get_fiscal_year_period(end_year, 0)

    frequency = 'yearly'

    # check the cache first
    cache_key = "%s-%s-%s-%d-%d-%s-%s" % (INTRINIO_CACHE_PREFIX, "metric",
                                          ticker, start_year, end_year,
                                          frequency, tag)
    api_response = cache.read(cache_key)

    if api_response == None:
        # else call the API directly
        try:
            api_response = company_api.get_company_historical_data(
                ticker,
                tag,
                frequency=frequency,
                start_date=start_date,
                end_date=end_date)
            cache.write(cache_key, api_response)
        except ApiException as ae:
            raise DataError(
                "Error retrieving ('%s', %d - %d) -> '%s' from Intrinio Company API"
                % (ticker, start_year, end_year, tag), ae)
        except Exception as e:
            raise ValidationError(
                "Error parsing ('%s', %d - %d) -> '%s' from Intrinio Company API"
                % (ticker, start_year, end_year, tag), e)

    if len(api_response.historical_data) == 0:
        raise DataError(
            "No Data returned for ('%s', %d - %d) -> '%s' from Intrinio Company API"
            % (ticker, start_year, end_year, tag), None)

    historical_data = api_response.historical_data
    converted_response = {}

    for datapoint in historical_data:
        converted_response[datapoint.date.year] = datapoint.value

    return converted_response