Определение стоимости акции на основе финансовой отчетности

ЯКрайне важно покупать инвестиции по хорошей цене, что означает покупать их по справедливой стоимости. Но как рассчитать справедливую или внутреннюю стоимость акций?

Есть несколько способов подумать об этом, в частности:

  • Один из способов заключается в том, что справедливая стоимость представляет собой приведенную стоимость будущих свободных денежных потоков.
  • Другой способ заключается в том, что справедливая стоимость — это цена, которую вы можете заплатить, и получить требуемую норму прибыли.
  • Третий способ заключается в том, что справедливая стоимость — это цена, которая включает в себя финансовый будущий рост с учетом текущих мультипликаторов.

Конечно, все они предполагают, что ваш анализ и модель верны. Эти подходы все больше и больше возвращаются в мейнстрим с нормализацией политики ставок центральных банков и возвращением инвестиций к фундаментальным показателям.

В этой статье мы создаем три различные модели на основе данных финансовых показателей из квартальных отчетов о прибылях и убытках компаний и балансовых отчетов. Мы используем прибыль, свободный денежный поток, выручку, соответствующий рост и доли в качестве исходных данных для наших моделей. Это фактически подводит нас к третьей гипотезе, где справедливая стоимость — это цена, которая включает в себя будущий рост с учетом текущих показателей.

Для получения высококачественных фундаментальных данных мы используем API EOD, а для данных о ценах мы используем yfinance.

Ключевые особенности проекта

  • Загрузите фундаментальные данные для данной компании (бухгалтерский баланс, отчет о прибылях и убытках, отчет о движении денежных средств).
  • Рассчитайте входные данные для наших моделей финансовых показателей.
  • Подключите входные данные к каждой модели и оцените справедливую цену компании.
  • Визуализируйте ценовой ряд акций и сравните рыночную цену с справедливой ценой.

Прежде чем начать, вам потребуется следующее:

  • Python: Установите версию 3.9.7.
  • Jupyter Notebook: Установите индивидуальную версию Anaconda для вашей операционной системы, она поставляется с уже установленным Jupyter.
  • API-ключ EOD: следуйте инструкциям здесь.

Как только это будет настроено, мы можем продолжить реализацию.

Давайте начнём

Давайте загрузим пакеты, которые нам понадобятся для всего проекта, и инициируем переменные, которые нам понадобятся в будущем.

import matplotlib.pyplot as plt
import requests
import pandas as pd
from datetime import *
import numpy as np
import json
import urllib.request
import yfinance as yf
import warnings
warnings.filterwarnings('ignore')

EOD_API_KEY = 'your-api-key'

SYMBOL = 'FVRR.US'

Я выбрал Fiverr Stock (FVRR) для этой демонстрации, так как мне нравится компания, и я думаю, что несколько трудно оценить ее текущую справедливую цену и потенциальный будущий рост, поэтому, тем не менее, это было бы интересным упражнением. Однако, в конце концов, у нас будет весь код вместе в функцию, которая может сделать эту оценку для любого запаса, который вы ему укажете!

Теперь, после того, как мы импортировали все необходимые пакеты, давайте начнем с определения функции для загрузки данных.

  1. Загрузка основных данных для данной компании
def get_data(symbol):
    url = f'https://eodhistoricaldata.com/api/fundamentals/{symbol}?api_token={EOD_API_KEY}'
    response = urllib.request.urlopen(url)
    data = json.loads(response.read())
    return data
    
    data = get_data(symbol=SYMBOL)
    
    print(data.keys())

Для данной акции нам доступно довольно много данных, однако для наших формул мы будем использовать «Финансовые показатели», «Прибыль» и «Выдающиеся акции».

2Оценка на основе свободного денежного потока

FV(fcf)=fcfps∗(1+fcfgrowth)∗(цена/fcfps)

fcf = pd.DataFrame(data['Financials']['Cash_Flow']['quarterly']).T.sort_index()['freeCashFlow'].astype(float)[-1]

fcf_growth = pd.DataFrame(data['Financials']['Cash_Flow']['quarterly']).T.sort_index()\
                              ['freeCashFlow'].astype(float).pct_change().rolling(20).mean()[-1]

shares_outs = round(pd.DataFrame(data['outstandingShares']['quarterly']).T.set_index('dateFormatted')\
                                                 .sort_index()['shares'].astype(float)[-1],0)

price = yf.download(SYMBOL.split('.')[0], date.today()-pd.DateOffset(5), date.today())['Adj Close'][-1]

fcf_per_share = fcf/shares_outs

print(f'fcf_fair_value: {fcf_per_share*(1+fcf_growth)*(price/fcf_per_share)}')

Для оценки на основе свободного денежного потока мы используем последний квартальный свободный денежный поток как , затем мы используем среднее квартальное изменение свободного денежного потока за последние 20 кварталов в качестве показателя для ожидаемого, затем мы используем акции в обращении как последнюю цену для расчета свободного денежного потока на акцию и окончательную формулу.fcffcf_growthshares_outsprice

3. Оценка на основе выручки/продаж

FV(sales)=sps∗(1+salesgrowth)∗(цена/sps)

sales = pd.DataFrame(data['Financials']['Income_Statement']['quarterly']).T.sort_index()\
                            ['totalRevenue'].astype(float)[-1]
    
sales_growth = pd.DataFrame(data['Financials']['Income_Statement']['quarterly']).T.sort_index()\
                            ['totalRevenue'].astype(float).pct_change().rolling(20).mean()[-1]

price = yf.download(SYMBOL.split('.')[0], date.today()-pd.DateOffset(5), date.today())['Adj Close'][-1]

shares_outs = round(pd.DataFrame(data['outstandingShares']['quarterly']).T.set_index('dateFormatted')\
                                                 .sort_index()['shares'].astype(float)[-1],0)

sales_per_share = sales/shares_outs

print(f'sps_fair_value: {sales_per_share*(1+sales_growth)*(price/sales_per_share)}')

Для оценки, основанной на выручке/продажах, мы используем последнюю квартальную общую выручку как , затем мы используем среднее общее изменение выручки за последние 20 кварталов, затем снова мы используем акции в обращении как последнюю цену для расчета свободного денежного потока на акцию и окончательную формулу.salessales_growthshares_outsprice

4. Оценка на основе доходов

FV(pe)=eps∗(1+epsgrowth)∗pe

eps = pd.DataFrame(data['Earnings']['Annual']).T.sort_index()['epsActual'][-1]

eps_growth = pd.DataFrame(data['Earnings']['Trend']).T.sort_index()\
                              ['earningsEstimateGrowth'].astype(float).mean()

price = yf.download(SYMBOL.split('.')[0], date.today()-pd.DateOffset(5), date.today())['Adj Close'][-1]

pe = price/eps

print(f'pe_fair_value: {eps*(1+eps_growth)*pe}')

Для оценки, основанной на прибыли, мы используем годовую прибыль в качестве прибыли на акцию, затем мы используем доступную прибыль EstimatedGrowth в качестве eps_growth и мы используем последнюю цену акций для расчета последнего коэффициента P/E и формулы.

После того, как мы выполнили все три метода, мы можем собрать все вместе в функцию, которая принимает в качестве параметра имя символа, вычисляет все три справедливые стоимости и сравнивает их с текущей ценой акций!

5. Собираем все воедино

import matplotlib.pyplot as plt
import requests
import pandas as pd
from datetime import *
import numpy as np
import json
import urllib.request
import yfinance as yf
import warnings
warnings.filterwarnings('ignore')

EOD_API_KEY = 'your_api_key'

SYMBOL = 'FVRR.US'


def get_data(symbol, api_key):
    url = f'https://eodhistoricaldata.com/api/fundamentals/{symbol}?api_token={api_key}'
    
    response = urllib.request.urlopen(url)
    
    data = json.loads(response.read())
    
    return data


def get_financial_metrics_valuation(data, symbol):
    
    eps = pd.DataFrame(data['Earnings']['Annual']).T.sort_index()['epsActual'][-1]

    eps_growth = pd.DataFrame(data['Earnings']['Trend']).T.sort_index()['earningsEstimateGrowth'].astype(float).mean()
    
    fcf = pd.DataFrame(data['Financials']['Cash_Flow']['quarterly']).T.sort_index()['freeCashFlow'].astype(float)[-1]
    
    fcf_growth = pd.DataFrame(data['Financials']['Cash_Flow']['quarterly']).T.sort_index()\
                              ['freeCashFlow'].astype(float).pct_change().rolling(20).mean()[-1]
    
    sales = pd.DataFrame(data['Financials']['Income_Statement']['quarterly']).T.sort_index()['totalRevenue'].astype(float)[-1]
    
    sales_growth = pd.DataFrame(data['Financials']['Income_Statement']['quarterly']).T.sort_index()\
                                ['totalRevenue'].astype(float).pct_change().rolling(20).mean()[-1]
    
    shares_outs = round(pd.DataFrame(data['outstandingShares']['quarterly']).T.set_index('dateFormatted')\
                                                 .sort_index()['shares'].astype(float)[-1],0)
    
    price = yf.download(symbol.split('.')[0], date.today()-pd.DateOffset(5), date.today())['Adj Close'][-1]

    pe = price/eps
    
    fcf_per_share = fcf/shares_outs
    
    sales_per_share = sales/shares_outs
    
    df = {}
    
    df['pe_intrinsic_value'] = eps*(1+eps_growth)*pe
    
    df['fcf_intrinsic_value'] = fcf_per_share*(1+fcf_growth)*(price/fcf_per_share)
    
    df['sales_intrinsic_value'] = sales_per_share*(1+sales_growth)*(price/sales_per_share)
    
    df['current_price'] = price
    
    return pd.DataFrame(df, index=pd.Series(symbol))


data = get_data(symbol=SYMBOL, api_key=EOD_API_KEY)

fair_values = get_financial_metrics_valuation(data=data, symbol=SYMBOL)

fair_values

В итоге мы получаем небольшой простой в использовании скрипт для фундаментальной оценки любых акций, которые вы хотите, используя данные из API EOD. Из этого текущего анализа мы видим, что, возможно, текущая цена FVRR немного занижена. Кроме того, мы можем наблюдать за ценой акций и линиями графика, соответствующими ценам трех моделей, таким образом, будет легче визуально понять, где в настоящее время находятся акции.

Заключение

Несмотря на то, что модели оценки подталкивают к допущениям и ошибкам, с помощью некоторых простых математических и качественных данных мы можем оценить диапазон справедливой стоимости для данной акции. Это может легко расширить наши возможности инвестиционного анализа и предоставить нам точную основу или отправную точку!

Источник