The ``data`` object ------------------- Data is split between **reported data** and **user generated data**, both are stored in the ``data`` object. - Reported data is the data retrieved from our third party providers such as ``financialmodelingprep.com``. - User data is data generated by using the ``compute`` or the ``set`` functions. ``data.compute()`` ~~~~~~~~~~~~~~~~~~ Calculates values based on specified formulas and stores them in the ``data`` object. The formulas can reference other keys, either for reported data or user data, and can include mathematical operations and specialized functions. Let’s take an example. **Example** .. code:: python # Computing Benjamin Graham's number data.compute({ "#bookValue": "balance:totalStockholdersEquity / income:weightedAverageShsOut", "#intermediaryVariable": f"#bookValue * income:eps * {assumptions.get('graham_multiplier')}", "#grahamNumber": "function:sqrt:#intermediaryVariable", "%revenueGrowthRate": "function:growth:income:revenue", }) Breaking down the example ^^^^^^^^^^^^^^^^^^^^^^^^^ Formulas are evaluated **from top to bottom** and starting from the earliest year available all the way until the end. For the sake of this example, let’s say the earliest year available is 2000. 1. **``#bookValue``** ^^^^^^^^^^^^^^^^^^^^^ The first evaluated key is ``#bookValue``. The framework starts by fetching the total stockholders’ equity ``totalStockholdersEquity`` from the balance sheet in year 2000 and the number of shares outstanding ``weightedAverageShsOut`` from the income statement also in year 2000. Calculates the ``#bookValue`` per share for year 2000 by dividing the total stockholders’ equity by the weighted average shares outstanding. 2. **``#intermediaryVariable``** ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Since the value for ``#bookValue`` has just been calculated, the framework can now evaluate the second key ``#intermediaryVariable``. **Note:** Assumptions can be used within string formulas. One simple way is to use the following python string format: .. code:: python f"{assumptions.get('graham_multiplier')}" 3. **``#grahamNumber``** ^^^^^^^^^^^^^^^^^^^^^^^^ The third evaluated key is ``#grahamNumber``, which uses the square root function ``function:sqrt``. **Note:** The formula could also be written without a special function, using the power operator ``**``. But it could result in complex numbers. .. code:: python "#grahamNumber": "#intermediaryVariable ** 0.5", 4. **``%revenueGrowthRate``** ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The fourth and last evaluated key is ``%revenueGrowthRate``, which uses another function called ``function:growth`` Key Types ~~~~~~~~~ Notice that there are multiple types of keys, this is to keep the model organized and the framework can format the values in millions or thousands depending on the key type. 1. Keys that start with **“#”** are indicating that its values are either **standalone units** like ratios or “per share” items. 2. Keys that start with **“%”** are indicating that its values are **percentages**. 3. Keys that start with **anything else**, will be considered formattable to millions or thousands. Examples ^^^^^^^^ 1. Price to Earnings ratio can be named something like **“#priceToEarnings”** 2. The tax rate can be named **“%taxRate”** 3. The key for forecasted revenue can be named just **“forecastedRevenue”**. The values will be then formatted to millions or thousands in the rendering table or chart, depending on your rendering preferences. Forecasting Values ~~~~~~~~~~~~~~~~~~ Forecasting values allows you to project future financial metrics based on historical data and specified growth rates. This is particularly useful for estimating performance over a defined period. Storing Projection Years as an Assumption ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To make the model more interactive and allow control over the number of projected years, you can store the projection duration as an assumption. Use the ``assumptions.init()`` method to initialize the ``"projection_years"`` key before performing any forecasts: .. code:: python # Initialize assumptions for projection years assumptions.init({ "projection_years": 5 # Adjust this value to specify how many years to project }) Using ``data.compute`` for Forecasting ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can now use the ``data.compute()`` function to calculate projected values. Below is an example of how to compute projected revenues based on a annual growth rate of 10%. .. code:: python # Compute projected revenues using a growth rate data.compute({ "income:revenue": f"income:revenue:-1 * (1 + 0.1)", # Projecting a 10% growth rate }, forecast=assumptions.get("projection_years")) **Note:** Feel free to make the revenue growth an assumption as well. Example of Forecasting '''''''''''''''''''''' Here’s a complete example that initializes assumptions, computes projected revenue, and displays the results in a table: .. code:: python # Initialize assumptions assumptions.init({ "projection_years": 5, # Set the number of years to project "%revenue_growth_rate": "10%" }) # Compute projected revenues at a 10% growth rate data.compute({ "income:revenue": f"income:revenue:-1 * (1 + {assumptions.get('%revenue_growth_rate')})", }, forecast=assumptions.get("projection_years")) # Render a table to display the projected revenues model.render_table({ "data": { "income:revenue": "Projected Revenue", }, "start": 1, # Start from the next year "end": assumptions.get("projection_years"), # End at the projected years "properties": { "title": "Projected Revenues", "number_format": "M", # Display figures in millions "order": "ascending", # Show projected years in order }, }) The LTM Period and the ``ltm_as_year`` Property ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | The **LTM** period (Last Twelve Months) consists of the four most recent quarters. | Although it provides a more current snapshot of financial performance, it is **not** used in forecasting by default. | Consider this scenario: | You’re projecting revenue with a 5% growth rate and the current year is 2025. | You have two revenue figures: - One for the **LTM period** - One from the **2024 annual report** By default, the LTM figure is ignored, and forecasting starts from the 2024 value. | This is where the ``ltm_as_year`` property becomes useful. | It lets you choose whether to treat the LTM period as the base year for forecasting: - If ``"ltm_as_year": True``, forecasting begins from the LTM value (e.g., LTM revenue grows by 5%). - If not specified, or set to ``"ltm_as_year": False``, forecasting uses the most recent full-year figure (e.g., 2024 revenue). Working Example of ``ltm_as_year`` '''''''''''''''''''''''''''''''''' .. code:: python # Initialize assumptions assumptions.init({ "projection_years": 5, # Number of years to forecast "%revenue_growth_rate": "10%" }) # Compute projected revenues using the LTM value as the base data.compute({ "income:revenue": f"income:revenue:-1 * (1 + {assumptions.get('%revenue_growth_rate')})", }, forecast=assumptions.get("projection_years"), properties={"ltm_as_year": True} ) # Render a table to display the projected revenues model.render_table({ "data": { "income:revenue": "Projected Revenue", }, "start": 1, # Start from next year "end": assumptions.get("projection_years"), # End at the last forecast year "properties": { "title": "Projected Revenues", "number_format": "M", # Format numbers in millions "order": "ascending", # Show years in forward order }, }) Available Functions in ``data.compute()`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``function:growth`` ^^^^^^^^^^^^^^^^^^^ | Calculates the year-over-year growth rate of the specified data key. | Returns ``(current - previous) / previous``. **Example:** ``"function:growth:income:netIncome"`` **Note:** The ``growth`` function only accepts keys, not values. ``function:discount`` ^^^^^^^^^^^^^^^^^^^^^ Discounts a key or value using compound interest to adjust a future value to its present value. **Example #1:** Discounting forecasted ``flow:freeCashFlow`` at 10%: - ``"function:discount:flow:freeCashFlow rate:0.1"`` **Example #2:** Discounting forecasted ``flow:freeCashFlow`` at 10% continuously: - ``"function:discount:flow:freeCashFlow rate:0.1 continuous:true"`` **Required parameters** - ``rate:`` - The annual discount rate used to discount future cash flow or other figures to present value. **Optional parameters** - ``offset:[..., -2, -1, ...]`` - Shifts the time period used in discounting by a set number of years. The default value is ``offset:0`` - ``continuous:[true, false]`` - Can be configured for continuous time by setting ``continuous:true`` **Note:** Setting ``continuous:true`` will discount the next year’s ``flow:freeCashFlow`` to present value accounting for the days left until the fiscal year ends. ``discount rate = ((1 + rate) ** days difference / 365)`` ``function:compound`` ^^^^^^^^^^^^^^^^^^^^^ Compounds a key or value using compound interest. **Example:** ``"function:compound:1 rate:0.1 offset:-1"`` **Required parameters** - ``rate`` - The annual rate used to compound the given value. **Optional parameters** - ``offset:[..., -2, -1, ...]`` - Shifts the time period used in compounding by a set number of years. The default value is ``offset:0`` - ``continuous:[true, false]`` - Can be configured for continuous time by setting ``continuous:true`` ``function:linear_regression`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Performs linear regression over historical values of the specified data key. | Stores the predicted values across historical and forecast dates. **Example:** ``"function:linear_regression:income:revenue start:-5 end:0"`` **Optional parameters** - ``start:[..., -2, -1, ...]`` - Sets the regression start relative to LTM. The default starting period is the first available historical period. - ``end:[..., 0, 1, ...]`` - Sets the regression end relative to LTM. The default ending period is the last available period. Range Functions ^^^^^^^^^^^^^^^ The following functions support range selection and share the same optional parameters: - ``function:average`` - Calculates the average of values over a specified range of periods. - ``function:sum`` or ``function:add`` - Returns the total sum of values over a specified period. Synonymous aliases: ``sum``, ``add``. - ``function:max`` or ``function:maximum`` - Returns the maximum value in the specified range. Synonymous aliases: ``max``, ``maximum``. - ``function:min`` or ``function:minimum`` - Returns the minimum value in the specified range. Synonymous aliases: ``min``, ``minimum``. - ``function:multiply`` - Returns the product of values over the specified range. Useful for chaining multipliers over time. **Example #1:** Averaging the last 3 years. - ``"function:average:exampleKey period:3"`` **Example #2:** Using range selection to select the last 3 years. - ``"function:average:exampleKey:-2->0"`` **Optional parameters - alternatives to range selection** - ``period:[1, 2, ...]`` - Selects the specified number of periods. This is just an alternative to ``function:average:x->0``, where ``x = (-1)*(periods - 1)``. - ``start:[..., -2, -1, ...]`` - Sets the start relative to LTM. The default starting period is the first available historical period. - ``end:[..., 0, 1, ...]`` - Sets the end relative to LTM. The default ending period is the last available period. ``function:sqrt`` ''''''''''''''''' | Returns the square root of the specified value. | Only defined for non-negative values. **Example:** ``"function:sqrt:16"`` returns ``4.0`` ``function:pow`` '''''''''''''''' Raises the value to the power specified in ``raised_to`` parameter. **Example:** ``"function:pow:2 raised_to:3"`` returns ``8.0`` **Required parameters** - ``rate`` - The annual discount rate used to discount future cash flow to present value. ``function:log`` '''''''''''''''' | Returns the logarithm of a number using a given base (default is natural log, base *e*). | Base must be positive and not equal to 1. **Example:** ``"function:log:10 base:10"`` returns ``1.0`` **Required parameters** - ``rate`` - The annual discount rate used to discount future cash flow to present value. ``function:exp`` '''''''''''''''' | Returns *e* raised to the power of the given value. | Useful for reversing logarithmic values. **Example:** ``"function:exp:1"`` returns approximately ``2.718`` Available Operations in ``data.compute()`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here are all the available operations allowed within ``data.compute()`` Arithmetic Operations ^^^^^^^^^^^^^^^^^^^^^ - | **Addition**: ``+`` | Adds two numbers. | Example: ``3 + 2`` results in ``5`` - | **Subtraction**: ``-`` | Subtracts the right number from the left. | Example: ``5 - 2`` results in ``3`` - | **Multiplication**: ``*`` | Multiplies two numbers. | Example: ``4 * 3`` results in ``12`` - | **Division**: ``/`` | Divides the left number by the right. Returns a float. | Example: ``10 / 4`` results in ``2.5`` - | **Floor Division**: ``//`` | Divides and rounds down to the nearest integer. | Example: ``10 // 4`` results in ``2`` - | **Exponentiation**: ``**`` | Raises the left number to the power of the right. | Example: ``2 ** 3`` results in ``8`` - | **Modulus**: ``%`` | Returns the remainder of the division. | Example: ``10 % 3`` results in ``1`` Boolean Operations ^^^^^^^^^^^^^^^^^^ | Boolean operations evaluate to ``1`` if the condition is ``True`` and ``0`` if the condition is ``False``. | These results can be used in **Arithmetic Operations** just like numbers. - | **Equal to**: ``==`` | Checks if two values are equal. | Example #1: ``5 == 5`` results in ``1`` Example #2: ``5 == 6`` results in ``0`` - | **Not equal to**: ``!=`` | Checks if two values are not equal. | Example: ``5 != 3`` results in ``1`` - | **Less than**: ``<`` | Example: ``3 < 5`` results in ``1`` - | **Greater than**: ``>`` | Example: ``7 > 4`` results in ``1`` - | **Less than or equal to**: ``<=`` | Example: ``4 <= 4`` results in ``1`` - | **Greater than or equal to**: ``>=`` | Example: ``6 >= 3`` results in ``1`` Grouping ^^^^^^^^ - **Parentheses**: ``(`` ``)`` Used to control the order of operations. Example: ``2 * (3 + 4)`` results in ``14`` -------------- ``data.set()`` ~~~~~~~~~~~~~~ The ``data.set()`` function allows you to set values in the stored data. You can set a single key-value pair or multiple pairs at once. Example of using ``data.set()`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code:: python data.set("income:netIncome:1", 1000000) # Set future net income, not overwriting data.set({ "income:revenue:1": 5000000, "income:costOfRevenue:1": 3000000 }, overwrite=True) # Set multiple values overwriting any existing values -------------- ``data.get()`` ~~~~~~~~~~~~~~ Retrieves a value from the stored data. You can specify a key and optionally define a default value if the key is not found. Example of using ``data.get()`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code:: python ltm_eps = data.get("income:eps") # Retrieves the last twelve months EPS from the income statement previous_year_eps = data.get("income:eps:-1") # Retrieves the previous year's EPS Range selection using ``data.get()`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can also select a range of values. For instance, to get the EPS values over the last 5 years plus LTM, you would use: .. code:: python historical_eps = data.get("income:eps:-5->0") -------------- ``data.min()`` ~~~~~~~~~~~~~~ Calculates the minimum value for a given key, ignoring None values. Example of using ``data.min()`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code:: python min_eps = data.min("income:eps:-10->0") # Minimum EPS over the last 10 years including LTM -------------- ``data.max()`` ~~~~~~~~~~~~~~ Calculates the maximum value for a given key, similar to the ``min()`` function. Example of using ``data.max()`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code:: python max_revenue = data.max("income:revenue:-5->-1") # Maximum revenue over the last 5 years, excluding LTM -------------- ``data.average()`` ~~~~~~~~~~~~~~~~~~ Calculates the average of values for a given key, ignoring None. Example of using ``data.average()`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code:: python average_eps = data.average("income:eps:-10->0") # Average EPS over the last 10 years, including LTM -------------- ``data.sum()`` ~~~~~~~~~~~~~~ Calculates the sum of values for a specified key. Example of using ``data.sum()`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code:: python total_revenue = data.sum("income:revenue:-5->-1") # Total revenue over the last 5 years, excluding LTM -------------- ``data.count()`` ~~~~~~~~~~~~~~~~ This function counts the number of entries for the specified key, excluding specified values if needed. Example of using ``data.count()`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code:: python count_dividends = data.count("dividend:adjDividend:*", properties={"except_values": [None, 0]}) # Count non-zero dividends