1 votos

¿Puede alguien confirmar si estoy en lo cierto sobre estas cifras en los documentos financieros de las empresas?

Quiero poner en práctica la estrategia F-score Value de Piotroski que se expone en el documento "Piotroski, J. D. (2000). Value investing: The use of historical financial statement information to separate winners from losers. Journal of Accounting Research, 38, 1-41." que enumera 9 criterios para juzgar a una empresa:

# 1 - Positive Net Income (1 point)                                                                   Cash-flow
# 2 - Positive return on assets (net income / total assets) in the current year (1 pnt)     Cash-flow & Balance Sheet
# 3 - Positive operating cash flow (NI + DeprExp + chgReceive + chgInven) for current year (1 pnt) Cash-flow
# 4 - Cash flow from operations being greater than net Income (quality of earnings) (1 pnt)
# Leverage, Liquidity and Source of Funds Criteria:
# 5 - Lower ratio of long term debt in the current period,                                          Balance Sheet
#     compared to the previous year (decreased leverage) (1 pnt)
# 6 - Higher current ratio this year compared to the previous year (more liquidity) (1 pnt)         Balance Sheet
# 7 - No new shares were issued in the last year (lack of dilution) (1 pnt).                        Key Stats
# Operating Efficiency Criteria:
# 8 - A higher gross margin compared to the previous year (1 pnt)
# 9 - A higher asset turnover ratio compared to the previous year (1 pnt)
# Stocks with 8 or 9 points should be bought, while stocks with 3 points or less should be sold short.

Estoy trabajando con la biblioteca IEXfinance en python para extraer datos de las hojas financieras de una empresa para determinar si cumplen con este criterio (aunque podría cambiar a desechar desde el sistema EDGAR si eso resulta ser un poco más rápido porque el IEXfinance tiene partes detrás de un muro de pago. De todos modos, estoy teniendo problemas para determinar si los datos devueltos desde IEXfinance son los mismos que estoy buscando porque los términos son ligeramente diferentes.

Fotos de sus datos: Balance Balance Sheet Pic

En el Balance, puedo ver los datos necesarios para 1,2,5,6.

Flujo de caja Cash Flow

El flujo de caja operativo. se define como Ingreso Neto - Gasto de Depreciación + Cambio en las Cuentas por Cobrar + Cambio en el Inventario que puedo ver en el Informe de Flujo de Caja. Así que el 3 se cumple.

Declaración de ingresos Income Statement

Mis preguntas son las siguientes -¿Es el flujo de caja de operaciones utilizado en el #4, el flujo de caja de operaciones positivo que calculamos en el 3? -¿El hecho de que el capital social siga siendo el mismo significa que no se han emitido nuevas acciones? Supongo que no porque el precio de las acciones puede cambiar haciendo que este no sea el número correcto. En key_stats, hay un punto de datos llamado shares_outstanding que es lo que supongo que estoy buscando, ¿sí? Un sitio web argumenta que el número de "Acciones promedio ponderadas" debe ser revisado. - Tengo el beneficio bruto para el número 8 pero no las ventas totales. ¿Es equivalente comparar el beneficio bruto entre años? -#El número 9 me deja perplejo. Aquí hay un código muy aproximado:

 # This is an implementation of some of the stock-selection principles outlined here:
# -Amor-Tapia, B. & Tascón, M.T. (2016). Separating winners from losers: Composite indicators
# based on fundamentals in the European context *. Finance a Uver,66(1), 70-94.
# -Piotroski, J. D. (2000). Value investing: The use of historical financial statement information
# to separate winners from losers. Journal of Accounting Research, 38, 1-41.
# No guarantees are provided about the performance of these principles as described
# or as implemented here. As with any strategy, you should validate its performance
# with backtesting and forward testing before committing to its use.
#
# List of 9 Selection Criteria
# Profitability Criteria:
# 1 - Positive Net Income (1 point)                                                                   Cash-flow
# 2 - Positive return on assets (net income / total assets) in the current year (1 pnt)     Cash-flow & Balance Sheet
# 3 - Positive operating cash flow (NI + DeprExp + chgReceive + chgInven) for current year (1 pnt) Cash-flow
# 4 - Cash flow from operations being greater than net Income (quality of earnings) (1 pnt)
# Leverage, Liquidity and Source of Funds Criteria:
# 5 - Lower ratio of long term debt in the current period,                                          Balance Sheet
#     compared to the previous year (decreased leverage) (1 pnt)
# 6 - Higher current ratio this year compared to the previous year (more liquidity) (1 pnt)         Balance Sheet
# 7 - No new shares were issued in the last year (lack of dilution) (1 pnt).                        Key Stats
# Operating Efficiency Criteria:
# 8 - A higher gross margin compared to the previous year (1 pnt)
# 9 - A higher asset turnover ratio compared to the previous year (1 pnt)
# Stocks with 8 or 9 points should be bought, while stocks with 3 points or less should be sold short.
#
"""
#################################################################################################################

# Imports to set-up our Alpaca login
import os
import sys
import yaml
import json

# Imports for Alpaca specific libraries
import alpaca_trade_api as tradeapi

# Imports for IEXfinance stock data
from iexfinance.base import _IEXBase
from iexfinance.stocks import Stock
from urllib.parse import quote

#
import pandas as pd

class PiotroskiFScoreValueTrader:

    # --------------------------------------------------------------------------------------------------
    def __init__(self):
        # Connect to the website and make sure we are good to trade.
        self.api = self.login()

        # Get the complete list of stocks we want to check.
        self.tradeable = self.get_tradeable_assets()

        # Get the relevant data we need for each stock.

    # --------------------------------------------------------------------------------------------------

    # --------------------------------------------------------------------------------------------------
    def login(self):
        # We retrieve our login info for the Alpaca API from the config.yml file here and then login.
        with open(os.path.join(sys.path[0], 'config.yml'), 'r') as f:
            config = yaml.load(f)

            #  These are the params we need to send to the Alpaca API
            base_url = config['base_url']
            key_id = config['key_id']
            secret = config['secret']

        api = tradeapi.REST(base_url=base_url, key_id=key_id, secret_key=secret)
        if api.get_account().status != 'ACTIVE':
            raise RuntimeError("Error: Account is not active. Account is: ", api.get_account().status)
        return api
    # --------------------------------------------------------------------------------------------------

    # --------------------------------------------------------------------------------------------------
    def get_tradeable_assets(self):
        symbols = []
        possible_assets = self.api.list_assets()
        for asset in possible_assets:
            temp_dict = vars(asset)
            if temp_dict['_raw']['status'] == 'active':
                symbols.append(temp_dict['_raw']['symbol'])
        return sorted(symbols)
    # --------------------------------------------------------------------------------------------------

    # --------------------------------------------------------------------------------------------------
    def get_asset_data(self):

        # IEX doesn't like batch queries for more than 100 symbols at a time.
        # We need to build our fundamentals info iteratively.
        batch_idx = 0
        batch_size = 99
        fundamentals_dict = {}
        while batch_idx < len(self.tradeable):
            symbol_batch = [s['symbol']
                            for s in self.tradeable[batch_idx:batch_idx + batch_size]]
            stock_batch = Stock(symbol_batch)

            # Pull all the data we'll need from IEX.
            financials_json = stock_batch.get_financials()
            quote_json = stock_batch.get_quote()
            stats_json = stock_batch.get_key_stats()
            earnings_json = stock_batch.get_earnings()

            for symbol in symbol_batch:
                # We'll filter based on earnings first to keep our fundamentals
                # info a bit cleaner.
                if not self.positive_return_on_assets(earnings_json[symbol]):
                    continue

                # Make sure we have all the data we'll need for our filters for
                # this stock.
                if not self.data_quality_good(
                        symbol,
                        financials_json,
                        quote_json,
                        stats_json):
                    continue

                fundamentals_dict[symbol] = self.get_fundamental_data_for_symbol(
                    symbol,
                    financials_json,
                    quote_json,
                    stats_json
                )

            batch_idx += batch_size

        # Transform all our data into a more filterable form - a dataframe - with
        # a bit of pandas magic.
        return pd.DataFrame.from_dict(fundamentals_dict).T

        # Stock.get_company() is one way to get sector.

    # --------------------------------------------------------------------------------------------------

    # --------------------------------------------------------------------------------------------------
        # Want to mimic if not eps.good and data quality good to filter out stocks we do not want to
        # bother with. Then we want to get the required data so we can filter our stocks.
    # --------------------------------------------------------------------------------------------------

    # --------------------------------------------------------------------------------------------------
    # Return on Assets = Net Income / Total Assets
    def positive_return_on_assets(self, earnings_reports):
        # This method contains logic for filtering based on earnings reports.
        if len(earnings_reports) < 4:
            # The company must be very new. We'll skip it until it's had time to
            # prove itself.
            return False

        # earnings_reports should contain the information about the last four
        # quarterly reports.
        for report in earnings_reports:
            # We want to see consistent positive EPS.
            try:
                if not (report['actualEPS']):
                    return False
                if report['actualEPS'] < 0:
                    return False
            except KeyError:
                # A KeyError here indicates that some data was missing or that a company is
                # less than two years old. We don't mind skipping over new companies until
                # they've had more time in the market.
                return False
        return True
    # --------------------------------------------------------------------------------------------------

    # --------------------------------------------------------------------------------------------------
    def data_quality_good(self, symbol, financials_json, quote_json, stats_json):
        # This method makes sure that we're not going to be investing in
        # securities we don't have accurate data for.

        if len(financials_json[symbol]
               ) < 1 or quote_json[symbol]['latestPrice'] is None:
            # No recent data was found. This can sometimes happen in case of recent
            # market suspensions.
            return False

        try:
            if not (
                    quote_json[symbol]['marketCap'] and
                    stats_json[symbol]['priceToBook'] and
                    stats_json[symbol]['sharesOutstanding'] and
                    financials_json[symbol][0]['totalAssets'] and
                    financials_json[symbol][0]['currentAssets'] and
                    quote_json[symbol]['latestPrice']
            ):
                # Ignore companies IEX cannot report all necessary data for, or
                # thinks are untradable.
                return False
        except KeyError:
            # A KeyError here indicates that some data we need to evaluate this
            # stock was missing.
            return False

        return True
    # --------------------------------------------------------------------------------------------------

    # --------------------------------------------------------------------------------------------------
    def get_fundamental_data_for_symbol(self, symbol, financials_json,
                                        quote_json, stats_json):

        fundamentals_dict_for_symbol = {}

        financials = financials_json[symbol][0]

        # Calculate PB ratio.
        fundamentals_dict_for_symbol['pb_ratio'] = stats_json[symbol]['priceToBook']

        # Find the "Current Ratio" - current assets to current debt.
        current_debt = financials['currentDebt'] if financials['currentDebt'] else 1
        fundamentals_dict_for_symbol['current_ratio'] = financials['currentAssets'] / current_debt

        # Find the ratio of long term debt to short-term liquiditable assets.
        total_debt = financials['totalDebt'] if financials['totalDebt'] else 0
        fundamentals_dict_for_symbol['debt_to_liq_ratio'] = total_debt / financials['currentAssets']

        # Store other information for this stock so we can filter on the data
        # later.
        fundamentals_dict_for_symbol['pe_ratio'] = quote_json[symbol]['peRatio']
        fundamentals_dict_for_symbol['market_cap'] = quote_json[symbol]['marketCap']
        fundamentals_dict_for_symbol['dividend_yield'] = stats_json[symbol]['dividendYield']

        return fundamentals_dict_for_symbol
    # --------------------------------------------------------------------------------------------------

# **************************************************************************************************
def main():
    trader = PiotroskiFScoreValueTrader()
    stock_batch = Stock(['AAPL'])

    # Pull all the data we'll need from IEX.
    financials_json = stock_batch.get_financials()
    print("Financials: ", financials_json)
    quote_json = stock_batch.get_quote()
    print("Quote: ", quote_json)
    stats_json = stock_batch.get_key_stats()
    print("Stats: " , stats_json)
    earnings_json = stock_batch.get_earnings()
    print("Earnings: ",earnings_json)

if __name__ == '__main__':
    main()
# **************************************************************************************************

########################################################################################################################

Para este código:

trader = PiotroskiFScoreValueTrader()
stock_batch = Stock(['AAPL'])
# Pull all the data we'll need from IEX.
financials_json = stock_batch.get_financials()
print("Financials: \n", json.dumps(financials_json, indent=4))
quote_json = stock_batch.get_quote()
print("Quote: \n", json.dumps(quote_json, indent=4))
stats_json = stock_batch.get_key_stats()
print("Stats: \n", json.dumps(stats_json, indent=4))
earnings_json = stock_batch.get_earnings()
print("Earnings: \n", json.dumps(earnings_json, indent=4))
balance_json = stock_batch.get_balance_sheet()
print("Balance Sheet: \n", json.dumps(balance_json, indent=4))
cash_flow_json = stock_batch.get_cash_flow()
print("Cash Flow: \n", json.dumps(cash_flow_json, indent=4))
income_json = stock_batch.get_income_statement()
print("Income Statement: \n", json.dumps(income_json, indent=4))

Obtengo esta salida (que refleja las imágenes):

Financials: 
 [
    {
        "reportDate": "2018-12-31",
        "grossProfit": 31719000000,
        "costOfRevenue": 52654000000,
        "operatingRevenue": 84373000000,
        "totalRevenue": 84373000000,
        "operatingIncome": 23034000000,
        "netIncome": 19965000000,
        "researchAndDevelopment": 3902000000,
        "operatingExpense": 61339000000,
        "currentAssets": 140828000000,
        "totalAssets": 373719000000,
        "totalLiabilities": 255827000000,
        "currentCash": 44771000000,
        "currentDebt": 21741000000,
        "shortTermDebt": 21741000000,
        "longTermDebt": 92989000000,
        "totalCash": 86427000000,
        "totalDebt": 114730000000,
        "shareholderEquity": 117892000000,
        "cashChange": 18858000000,
        "cashFlow": 26690000000
    }
]
Quote: 
 {
    "symbol": "AAPL",
    "companyName": "Apple, Inc.",
    "calculationPrice": "tops",
    "open": 196.42,
    "openTime": 1554730200095,
    "close": 197,
    "closeTime": 1554494400394,
    "high": 199.5,
    "low": 196.34,
    "latestPrice": 198.875,
    "latestSource": "IEX real time price",
    "latestTime": "12:17:00 PM",
    "latestUpdate": 1554740220243,
    "latestVolume": 12781502,
    "iexRealtimePrice": 198.875,
    "iexRealtimeSize": 100,
    "iexLastUpdated": 1554740220243,
    "delayedPrice": 199.06,
    "delayedPriceTime": 1554739331755,
    "extendedPrice": 196.5,
    "extendedChange": -2.375,
    "extendedChangePercent": -0.01194,
    "extendedPriceTime": 1554730194696,
    "previousClose": 197,
    "change": 1.875,
    "changePercent": 0.00952,
    "iexMarketPercent": 0.027141489317922103,
    "iexVolume": 346909,
    "avgTotalVolume": 28583873,
    "iexBidPrice": 198.87,
    "iexBidSize": 100,
    "iexAskPrice": 198.89,
    "iexAskSize": 100,
    "marketCap": 937751310000,
    "peRatio": 16.21,
    "week52High": 233.47,
    "week52Low": 142,
    "ytdChange": 0.25698699999999997
}
Stats: 
 {
    "week52change": 0.16997299999999999,
    "week52high": 233.47,
    "week52low": 142,
    "marketcap": 928910160000,
    "employees": 132000,
    "day200MovingAvg": 190.7,
    "day50MovingAvg": 177.71,
    "float": 4708742476,
    "avg10Volume": 27937902.7,
    "avg30Volume": 28583873.43,
    "ttmEPS": 12.27,
    "ttmDividendRate": 2.82,
    "companyName": "Apple, Inc.",
    "sharesOutstanding": 4715280000,
    "maxChangePercent": 194.049505,
    "year5ChangePercent": 1.634394,
    "year2ChangePercent": 0.371293,
    "year1ChangePercent": 0.169973,
    "ytdChangePercent": 0.247467,
    "month6ChangePercent": -0.119632,
    "month3ChangePercent": 0.331711,
    "month1ChangePercent": 0.12881,
    "day30ChangePercent": 0.130689,
    "day5ChangePercent": 0.030119,
    "nextDividendRate": null,
    "dividendYield": 0.01431472081218274,
    "nextEarningsDate": "2019-05-01",
    "exDividendDate": "2019-02-08",
    "peRatio": 16.21
}
Earnings: 
 [
    {
        "actualEPS": 4.18,
        "consensusEPS": 4.66,
        "announceTime": "AMC",
        "numberOfEstimates": 36,
        "EPSSurpriseDollar": -0.48,
        "EPSReportDate": "2019-01-29",
        "fiscalPeriod": "Q4 2018",
        "fiscalEndDate": "2018-12-31",
        "yearAgo": 3.89,
        "yearAgoChangePercent": 0.0746
    }
]
Balance Sheet: 
 {
    "symbol": "AAPL",
    "balancesheet": [
        {
            "reportDate": "2018-12-31",
            "currentCash": 44771000000,
            "shortTermInvestments": 41656000000,
            "receivables": 18077000000,
            "inventory": 4988000000,
            "otherCurrentAssets": 12432000000,
            "currentAssets": 140828000000,
            "longTermInvestments": 158608000000,
            "propertyPlantEquipment": 39597000000,
            "goodwill": 0,
            "intangibleAssets": null,
            "otherAssets": 34686000000,
            "totalAssets": 373719000000,
            "accountsPayable": 44293000000,
            "currentLongTermDebt": 9772000000,
            "otherCurrentLiabilities": 42249000000,
            "totalCurrentLiabilities": 108283000000,
            "longTermDebt": 92989000000,
            "otherLiabilities": 23607000000,
            "minorityInterest": 0,
            "totalLiabilities": 255827000000,
            "commonStock": 40970000000,
            "retainedEarnings": 80510000000,
            "treasuryStock": null,
            "capitalSurplus": null,
            "shareholderEquity": 117892000000,
            "netTangibleAssets": 117892000000
        }
    ]
}
Cash Flow: 
 {
    "symbol": "AAPL",
    "cashflow": [
        {
            "reportDate": "2018-12-31",
            "netIncome": 19965000000,
            "depreciation": 3395000000,
            "changesInReceivables": 5109000000,
            "changesInInventories": -1076000000,
            "cashChange": 18858000000,
            "cashFlow": 26690000000,
            "capitalExpenditures": -3355000000,
            "investments": 9422000000,
            "investingActivityOther": -56000000,
            "totalInvestingCashFlows": 5844000000,
            "dividendsPaid": -3568000000,
            "netBorrowings": 6000000,
            "otherFinancingCashFlows": -1318000000,
            "cashFlowFinancing": -13676000000,
            "exchangeRateEffect": null
        }
    ]
}
Income Statement: 
 {
    "symbol": "AAPL",
    "income": [
        {
            "reportDate": "2018-12-31",
            "totalRevenue": 84373000000,
            "costOfRevenue": 52654000000,
            "grossProfit": 31719000000,
            "researchAndDevelopment": 3902000000,
            "sellingGeneralAndAdmin": 4783000000,
            "operatingExpense": 61339000000,
            "operatingIncome": 23034000000,
            "otherIncomeExpenseNet": 872000000,
            "ebit": 23034000000,
            "interestIncome": 890000000,
            "pretaxIncome": 23906000000,
            "incomeTax": 3941000000,
            "minorityInterest": 0,
            "netIncome": 19965000000,
            "netIncomeBasic": 19965000000
        }
    ]
}

2voto

zdd Puntos 523

En primer lugar, esto es un poco difícil de seguir y en general oscurece lo que realmente estás preguntando. En segundo lugar, obtener las respuestas del propio documento parece ser la mejor respuesta, salvo que simplemente te quedes con lo que tiene más sentido para ti dada tu interpretación.

Si estoy entendiendo, esto es realmente una pregunta de contabilidad, por lo que este no es probablemente el mejor lugar para preguntar, pero no obstante:

Mis preguntas son las siguientes -¿Es el flujo de caja de las operaciones utilizado en #4, el flujo de caja de operaciones positivo que calculamos en el 3?

(1) Eso parece probable.

-¿El hecho de que los fondos propios ¿el capital social se mantiene igual significa que no se han emitido nuevas acciones? Yo diría que Supongo que no, porque el precio de las acciones puede cambiar, lo que hace que esta no sea la cifra correcta. número.

(2) No está claro cuáles son las unidades de commonStock, y dada la forma en que está configurado, yo esperaría que sean $ reales en lugar de acciones (cs + re ~ shareholderEquity; probablemente no están recuperando todo en el balance).

En key_stats, un punto de datos se llama shares_outstanding que es lo que supongo que estoy buscando, ¿no? Un sitio web sostiene que el número de "Acciones medias ponderadas" debe ser comprobado.

(3) Supongo que eso es lo que necesitas también para las acciones.

  • Tengo el beneficio para el número 8 pero no las ventas totales. ¿Es la comparación del beneficio bruto a través de los años es el equivalente? -#El número 9 me deja perplejo.

(4) No, ya que un beneficio bruto de 10 sobre unas ventas/ingresos de 100 no es obviamente lo mismo que un beneficio bruto de 10 sobre unas ventas/ingresos de 50. La rotación de activos es una medida de eficiencia (normalmente definida como alguna versión de ventas/activos (o activos medios)). Al igual que en el caso anterior, es probable que la API a la que accede no proporcione todos los campos presentes en el estado financiero. No puedes calcular la TA si no tienes ventas/ingresos.

En la práctica, para este tipo de análisis, sólo se puede ser lo suficientemente bueno con feeds o APIs gratuitas.

Editar (en respuesta al comentario ya que puede ser útil para otros):

@DavidFrick, bueno, cuando hago referencia a lo gratuito (frente a lo que no lo es), no estoy hablando de una suscripción de 20 dólares al mes... las típicas fuentes de datos de las empresas tienen todo esto y más (por ejemplo, Thomson Reuters/Worldscope, etc). Por ejemplo, aquí hay un enlace a la guía de definiciones de datos de WS que encontré con una búsqueda rápida.

Factset y Bloomberg son los sospechosos habituales para este tipo de cosas; ambos proporcionan un acceso fácil, pero cuestan unos 30.000 dólares al año.

Si no, dependiendo de lo motivado que estés, puedes consultar esta página . No he revisado la mayor parte, pero se incluyen muchas fuentes de datos financieros típicas (además de otras muchas). Sin embargo, la relación señal/ruido podría no ser muy buena. Por ejemplo, he comprobado el Producto SimFin incluidos, y aunque la calidad de los datos parecía buena, la cobertura era bastante escasa (sólo se incluyeron unos 2.400 valores). Probablemente funcionaría bien para el proyecto adecuado, pero a mí no me resultó tan útil. ES UNA OPINIÓN PERSONAL.

0 votos

Sí, decidí desechar el sitio web de la SEC, pero utilizan un formato SGML muy complejo para el que no están diseñadas las bibliotecas de python. ¿Tienes alguna sugerencia de dónde puedo obtener los datos? No me importa pagar siempre y cuando estén limpios. Os estaría eternamente agradecido. @Chris

Finanhelp.com

FinanHelp es una comunidad para personas con conocimientos de economía y finanzas, o quiere aprender. Puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X