### Initialize QLPPLS Model Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/qlppls.md Demonstrates how to initialize the QLPPLS model with time series observations and a specified quantile level 'q'. Shows examples for median, upper, and lower quantiles. ```python import numpy as np import pandas as pd from lppls.lppls_q import QLPPLS from lppls import data_loader # Load example data data = data_loader.nasdaq_dotcom() # Convert to ordinal timestamps time = np.array([pd.Timestamp.toordinal(pd.Timestamp(d)) for d in data['Date']]) # Get log prices price = np.log(data['Adj Close'].values) # Create observation matrix observations = np.array([time, price]) # Initialize model with median regression (q=0.5) model = QLPPLS(observations=observations, q=0.5) # Or use upper quantile to focus on extreme prices model_upper = QLPPLS(observations=observations, q=0.75) # Or use lower quantile model_lower = QLPPLS(observations=observations, q=0.25) ``` -------------------------------- ### Multi-Quantile Analysis with QLPPLS Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/qlppls.md Compare fits across different quantiles to understand the heterogeneity of bubble behavior. This example plots observed prices against predictions from models fitted at various quantile levels. ```python import matplotlib.pyplot as plt quantiles = [0.25, 0.5, 0.75] results = {} for q in quantiles: model = QLPPLS(observations=observations, q=q) tc, m, w, a, b, c, c1, c2, O, D = model.fit(max_searches=25) results[q] = { 'tc': tc, 'm': m, 'w': w, 'model': model } # Plot all fits fig, ax = plt.subplots(figsize=(12, 6)) t_obs = observations[0, :] price = observations[1, :] ax.plot(t_obs, price, 'ko', label='Observed', alpha=0.5) for q, res in results.items(): model = res['model'] predictions = [model.lppls(t, res['tc'], res['m'], res['w'], model.coef_['a'], model.coef_['b'], model.coef_['c1'], model.coef_['c2']) for t in t_obs] ax.plot(t_obs, predictions, label=f'q={q}') ax.legend() ax.set_ylabel('ln(p)') ax.grid(True, alpha=0.3) plt.show() ``` -------------------------------- ### Multi-Algorithm Comparison: LPPLS vs LPPLS_LM Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/lppls-lm.md This example compares the fitting results of the default LPPLS (Nelder-Mead) and the LPPLS_LM (Levenberg-Marquardt) models using the same dataset. It loads NASDAQ data, fits both models with their recommended search counts, and prints key comparison metrics. ```python import numpy as np import pandas as pd from lppls import lppls, data_loader from lppls.lppls_lm import LPPLS_LM # Load and prepare data data = data_loader.nasdaq_dotcom() time = np.array([pd.Timestamp.toordinal(pd.Timestamp(d)) for d in data['Date']]) price = np.log(data['Adj Close'].values) observations = np.array([time, price]) # Fit with default LPPLS model_default = lppls.LPPLS(observations=observations) tc1, m1, w1, a1, b1, c1, c1_1, c2_1, O1, D1 = model_default.fit(max_searches=25) # Fit with LPPLS_LM model_lm = LPPLS_LM(observations=observations) tc2, m2, w2, a2, b2, c2, c1_2, c2_2, O2, D2 = model_lm.fit(max_searches=15) # Compare results print("Default LPPLS (Nelder-Mead):") print(f" tc={tc1:.2f}, m={m1:.3f}, w={w1:.3f}, O={O1:.2f}, D={D1:.2f}") print("\nLPPLS_LM (Levenberg-Marquardt):") print(f" tc={tc2:.2f}, m={m2:.3f}, w={w2:.3f}, O={O2:.2f}, D={D2:.2f}") ``` -------------------------------- ### Example Usage of LPPLS_LM fit Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/lppls-lm.md Demonstrates how to instantiate the LPPLS_LM model and fit it to observations, printing the critical time and oscillation count. Fewer random searches are used due to LM's faster convergence. ```python model = LPPLS_LM(observations=observations) # Fit with 15 random searches (fewer than typical 25 since LM converges faster) tc, m, w, a, b, c, c1, c2, O, D = model.fit(max_searches=15) print(f"Critical time: {tc}") print(f"Oscillations: {O:.2f}") ``` -------------------------------- ### Detect Bubble Start Time via Lagrange Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/lppls.md Detects the start time of a financial bubble using Lagrange regularization. Fits the model on progressively smaller windows to identify the optimal fitting window size without overfitting. Use when you need to objectively determine the bubble's onset. ```python detect_bubble_start_time_via_lagrange( max_window_size: int, min_window_size: int, step_size: int = 1, max_searches: int = 25 ) -> dict[str, Any] | None ``` ```python model = lppls.LPPLS(observations=observations) # Detect bubble start with windows from 500 to 50 observations result = model.detect_bubble_start_time_via_lagrange( max_window_size=500, min_window_size=50, step_size=5, max_searches=25 ) if result: print(f"Bubble started at ordinal: {result['tau']}") print(f"Optimal window size: {result['optimal_window_size']}") print(f"Critical time: {result['tc']}") else: print("Could not detect bubble start") ``` -------------------------------- ### Detect Bubble Start Time via Lagrange Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/types.md Detects the bubble start time using the Lagrange method. Requires observations and specifies window size and search parameters. Can be used to plot diagnostic arrays like Lagrange-regularized SSE. ```python model = lppls.LPPLS(observations=observations) result = model.detect_bubble_start_time_via_lagrange( max_window_size=500, min_window_size=50, step_size=5, max_searches=25 ) if result: print(f"Bubble started at ordinal: {result['tau']}") print(f"Critical time: {result['tc']}") # Access diagnostic arrays import matplotlib.pyplot as plt plt.plot(result['window_sizes'], result['lagrange_sse_list']) plt.xlabel('Window Size') plt.ylabel('Lagrange-Regularized SSE') plt.show() ``` -------------------------------- ### Install LPPLS Python Module Source: https://github.com/boulder-investment-technologies/lppls/blob/master/README.md Install the LPPLS module and its dependencies using pip. Ensure you have Python 3.10 or higher. ```bash pip install -U lppls ``` -------------------------------- ### Estimated Bubble Start Time Output Source: https://github.com/boulder-investment-technologies/lppls/blob/master/notebooks/lagrange_regularization.ipynb Console output displaying the estimated bubble start time (tau). ```text Estimated bubble start time (tau): 0.3031674208144796 ``` -------------------------------- ### Fit model with fewer parameters for speed Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/configuration.md Use fewer parameters to fit the model when speed is a priority. This example sets a lower limit on `max_searches` and uses the 'Nelder-Mead' minimizer. ```python # Fewer parameters to fit model.fit(max_searches=10, minimizer="Nelder-Mead") ``` -------------------------------- ### Nested Fitting with LPPLSCMAES using multiprocessing Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/lpplscmaes.md Example of performing nested fits using the `mp_compute_nested_fits` method with an LPPLSCMAES instance. This utilizes CMA-ES for optimization within each nested fit. ```python model = LPPLSCMAES(observations=observations) res = model.mp_compute_nested_fits( workers=4, window_size=120, smallest_window_size=30, outer_increment=1, inner_increment=5 # Note: does NOT have max_searches parameter ) ``` -------------------------------- ### LPPLS.detect_bubble_start_time_via_lagrange Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/lppls.md Detects the start time of a financial bubble using Lagrange regularization. This method fits the LPPLS model on progressively smaller time windows to identify the optimal fitting window size without overfitting. ```APIDOC ## LPPLS.detect_bubble_start_time_via_lagrange ### Description Detect the start time of a financial bubble using Lagrange regularization (Demos & Sornette 2017). Fits the model on progressively smaller windows (from max to min, backwards in time) and uses Lagrange regularization to objectively identify the optimal fitting window size without overfitting. ### Method Signature ```python detect_bubble_start_time_via_lagrange( max_window_size: int, min_window_size: int, step_size: int = 1, max_searches: int = 25 ) -> dict[str, Any] | None ``` ### Parameters #### Path Parameters (None) #### Query Parameters (None) #### Request Body (None) ### Parameters Table | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | max_window_size | int | Yes | — | Largest window size (observations) to test | | min_window_size | int | Yes | — | Smallest window size (observations) to test | | step_size | int | No | 1 | Decrement step for window sizes (larger = faster but coarser) | | max_searches | int | No | 25 | Max random initializations per fit | ### Returns A dictionary with the following keys: - 'tau': bubble start time (ordinal) - 'optimal_window_size': optimal fitting window size - 'tc', 'm', 'w', 'a', 'b', 'c1', 'c2': fitted parameters - 'window_sizes': list of tested window sizes - 'sse_list': sum of squared errors for each window - 'ssen_list': normalized SSE for each window - 'lagrange_sse_list': Lagrange-regularized SSE for each window - 'start_times': start time of each window tested Returns None if fewer than 2 valid window fits are found. ### Request Example ```python # Assuming 'observations' is a numpy array or pandas DataFrame # model = lppls.LPPLS(observations=observations) # result = model.detect_bubble_start_time_via_lagrange( # max_window_size=500, # min_window_size=50, # step_size=5, # max_searches=25 # ) # # if result: # print(f"Bubble started at ordinal: {result['tau']}") # print(f"Optimal window size: {result['optimal_window_size']}") # print(f"Critical time: {result['tc']}") # else: # print("Could not detect bubble start") ``` ### Response #### Success Response (200) (Dictionary with bubble detection results or None) #### Response Example ```json { "tau": 1500.5, "optimal_window_size": 120, "tc": 1550.75, "m": 1.5, "w": 0.1, "a": 100.0, "b": -0.05, "c1": 0.5, "c2": 0.2, "window_sizes": [500, 495, ..., 50], "sse_list": [10.5, 10.2, ..., 5.1], "ssen_list": [0.1, 0.09, ..., 0.05], "lagrange_sse_list": [8.2, 8.0, ..., 4.5], "start_times": [1000, 1005, ..., 1450] } ``` ``` -------------------------------- ### Runtime Warning Output Source: https://github.com/boulder-investment-technologies/lppls/blob/master/notebooks/lagrange_regularization.ipynb Example of a runtime warning encountered during log calculation in the LPPLS module. ```text /home/jules/.pyenv/versions/3.12.12/lib/python3.12/site-packages/lppls/lppls.py:620: RuntimeWarning: invalid value encountered in log return (w / (2.0 * np.pi)) * np.log((tc - t1) / (tc - t2)) ``` -------------------------------- ### QLPPLS Initialization and Fitting Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/qlppls.md Initializes the QLPPLS model with observations and a quantile level, then fits the model to find the log-periodic power law singularity parameters. ```APIDOC ## QLPPLS Initialization and Fit ### Description Initializes the QLPPLS model with input observations and a specified quantile level 'q'. The `fit` method then estimates the log-periodic power law singularity parameters. ### Method Signature ```python QLPPLS(observations: np.ndarray | pd.DataFrame, q: float = 0.5) model.fit(max_searches: int = 25) ``` ### Parameters #### Initialization Parameters - **observations** (np.ndarray | pd.DataFrame) - Required - 2×M input data (timestamps, prices). - **q** (float) - Optional - Quantile level [0, 1] used for asymmetric loss function. Defaults to 0.5. #### Fit Parameters - **max_searches** (int) - Optional - Maximum number of searches to perform for fitting. Defaults to 25. ### Returns - **tc** (float) - Critical time of the singularity. - **m** (float) - Scaling exponent. - **w** (float) - Frequency of oscillations. - **a** (float) - Amplitude coefficient. - **b** (float) - Amplitude coefficient. - **c** (float) - Amplitude coefficient. - **c1** (float) - Amplitude coefficient. - **c2** (float) - Amplitude coefficient. - **O** (float) - Oscillation amplitude. - **D** (float) - Damping ratio. ### Example ```python # Robust fitting (default quantile) model = QLPPLS(observations=observations) tc, m, w, a, b, c, c1, c2, O, D = model.fit() # Focus on upper tail model_upper = QLPPLS(observations=observations, q=0.75) tc_upper, m_upper, w_upper, a_upper, b_upper, c_upper, c1_upper, c2_upper, O_upper, D_upper = model_upper.fit(max_searches=25) ``` ``` -------------------------------- ### Example Usage of fun_restricted Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/lpplscmaes.md An example demonstrating how to call the fun_restricted method on an LPPLSCMAES model instance. This method is typically called internally by CMA-ES during optimization. ```python # Internal method, not typically called directly # CMA-ES calls this during optimization model = LPPLSCMAES(observations=observations) x = [1400.0, 0.6, 8.0] # [tc, m, w] error = model.fun_restricted(x, observations) print(f"Chi-square error: {error}") ``` -------------------------------- ### Load and Prepare Custom Data for LPPLS Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/data-loader.md Demonstrates how to load your own dataset (e.g., from a CSV file) and prepare it into the 2xN observation matrix format required by the LPPLS model. Ensure you have 'date' and 'price' columns. ```python import numpy as np import pandas as pd from lppls import lppls # Load your own data (from CSV, API, etc.) df = pd.read_csv('your_data.csv', parse_dates=['date']) # Ensure you have a date column and a price column # Convert to the required 2×N observation matrix times = np.array([pd.Timestamp.toordinal(d) for d in df['date']]) prices = np.log(df['price'].values) # Use log prices observations = np.array([times, prices]) # Now use with LPPLS model = lppls.LPPLS(observations=observations) tc, m, w, a, b, c, c1, c2, O, D = model.fit(max_searches=25) ``` -------------------------------- ### Initialize LPPLS_LM Model Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/lppls-lm.md Demonstrates how to initialize the LPPLS_LM model with time series observations. Ensure observations are in a 2xM matrix format with ordinal timestamps and log prices. ```python import numpy as np import pandas as pd from lppls.lppls_lm import LPPLS_LM from lppls import data_loader # Load example data data = data_loader.nasdaq_dotcom() # Convert to ordinal timestamps time = np.array([pd.Timestamp.toordinal(pd.Timestamp(d)) for d in data['Date']]) # Get log prices price = np.log(data['Adj Close'].values) # Create observation matrix observations = np.array([time, price]) # Initialize Levenberg-Marquardt model model = LPPLS_LM(observations=observations) ``` -------------------------------- ### Fix TypeError: Invalid Filter Config Type Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/errors.md The filter_conditions_config parameter must be a dictionary or None. This example demonstrates both valid configurations. ```python model.mp_compute_nested_fits( workers=4, filter_conditions_config={"m_min": 0.1, "m_max": 0.9} ) # or model.mp_compute_nested_fits( workers=4, filter_conditions_config=None # Use defaults ) ``` -------------------------------- ### Basic LPPLS Workflow with Nasdaq Data Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/data-loader.md Demonstrates the basic workflow for using the Nasdaq dataset with the LPPLS model. Includes data loading, time conversion, price extraction, observation matrix creation, model fitting, and visualization. ```python from lppls import lppls, data_loader import numpy as np import pandas as pd from datetime import datetime as dt # 1. Load the data data = data_loader.nasdaq_dotcom() # 2. Convert Date strings to ordinal timestamps time = np.array([pd.Timestamp.toordinal(pd.Timestamp(t1)) for t1 in data['Date']]) # 3. Extract log prices price = np.log(data['Adj Close'].values) # 4. Create observations matrix (required format for LPPLS) observations = np.array([time, price]) # 5. Create and fit model model = lppls.LPPLS(observations=observations) tc, m, w, a, b, c, c1, c2, O, D = model.fit(max_searches=25) # 6. Visualize model.plot_fit() ``` -------------------------------- ### Calculate Number of Oscillations Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/lppls.md Computes the number of oscillations between a window's start and end times until a critical time. Returns NaN if the critical time is after the window end. ```python # How many oscillations occurred in the data? O = model.get_oscillations(w=8.0, tc=1400.0, t1=0.0, t2=1300.0) print(f"Oscillations: {O:.2f}") ``` -------------------------------- ### Instantiate and Fit QLPPLS Model (Upper Tail Focus) Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/qlppls.md Use q=0.75 or higher to emphasize fitting the upper price movements, potentially capturing bubble peaks. The model is penalized more for underestimating prices. ```python # Focus on upper quartile model_upper = QLPPLS(observations=observations, q=0.75) tc, m, w, a, b, c, c1, c2, O, D = model_upper.fit(max_searches=25) ``` -------------------------------- ### Initialize LPPLSCMAES Model Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/lpplscmaes.md Initializes the LPPLSCMAES model with time series observations. The observations should be a 2xM matrix containing timestamps and log-price values. ```python import numpy as np import pandas as pd from lppls.lppls_cmaes import LPPLSCMAES from lppls import data_loader # Load example data data = data_loader.nasdaq_dotcom() # Convert to ordinal timestamps time = np.array([pd.Timestamp.toordinal(pd.Timestamp(d)) for d in data['Date']]) # Get log prices price = np.log(data['Adj Close'].values) # Create observation matrix observations = np.array([time, price]) # Initialize CMA-ES model model = LPPLSCMAES(observations=observations) ``` -------------------------------- ### Instantiate and Fit QLPPLS Model (Lower Tail Focus) Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/qlppls.md Use q=0.25 or lower to emphasize fitting the lower price movements, potentially capturing crash patterns. The model is penalized more for overestimating prices. ```python # Focus on lower quartile model_lower = QLPPLS(observations=observations, q=0.25) tc, m, w, a, b, c, c1, c2, O, D = model_lower.fit(max_searches=25) ``` -------------------------------- ### LPPLS.__init__ Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/lppls.md Initializes the LPPLS model with time series observations. The observations should be a 2xM matrix containing timestamps and log-prices. ```APIDOC ## LPPLS.__init__ ### Description Initialize an LPPLS model with time series observations. ### Parameters #### Path Parameters - **observations** (np.ndarray | pd.DataFrame) - Required - 2×M matrix with timestamp (row 0) and observed log-price values (row 1). Timestamps should be numeric ordinals (e.g., pandas ordinal dates). ### Returns None (initializes instance) ### Raises - `AssertionError` if observations is not np.ndarray or pd.DataFrame ### Example: ```python import numpy as np import pandas as pd from lppls import lppls, data_loader # Load example data data = data_loader.nasdaq_dotcom() # Convert to ordinal timestamps time = np.array([pd.Timestamp.toordinal(pd.Timestamp(d)) for d in data['Date']]) # Get log prices price = np.log(data['Adj Close'].values) # Create observation matrix observations = np.array([time, price]) # Initialize model model = lppls.LPPLS(observations=observations) ``` ``` -------------------------------- ### Initialize LPPLS Model Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/lppls.md Instantiate the LPPLS model with time series observations. Ensure observations are in a 2xM matrix format with numeric ordinal timestamps and log-prices. ```python import numpy as np import pandas as pd from lppls import lppls, data_loader # Load example data data = data_loader.nasdaq_dotcom() # Convert to ordinal timestamps time = np.array([pd.Timestamp.toordinal(pd.Timestamp(d)) for d in data['Date']]) # Get log prices price = np.log(data['Adj Close'].values) # Create observation matrix observations = np.array([time, price]) # Initialize model model = lppls.LPPLS(observations=observations) ``` -------------------------------- ### Access Specific Model Result Source: https://github.com/boulder-investment-technologies/lppls/blob/master/notebooks/compute_nested_fits_example.ipynb Demonstrates how to access a specific value from the results using slicing, for example, retrieving the value at index 121 for key 't2'. This highlights the structured nature of the results. ```python res.loc[:, 121, "t2"] # This slicing ability is the reason why it might be useful. ``` -------------------------------- ### Subsetting Nasdaq Dataset by Date Range or Tail Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/data-loader.md Shows how to subset the Nasdaq dataset. You can use `.tail()` to get the most recent trading days or filter by a specific date range using boolean indexing. ```python from lppls import data_loader # Load full dataset data = data_loader.nasdaq_dotcom() # Use only recent portion (e.g., last 500 trading days) data_recent = data.tail(500) # Use only a specific date range data_range = data[(data['Date'] >= '1999-01-01') & (data['Date'] <= '2001-12-31')] ``` -------------------------------- ### Prepare Data for LPPLS Model Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/README.md Loads Nasdaq dot-com data and prepares it for the LPPLS model by converting timestamps to ordinal dates and taking the logarithm of adjusted closing prices. ```python from lppls import data_loader, lppls import numpy as np import pandas as pd # Load or prepare data data = data_loader.nasdaq_dotcom() time = np.array([pd.Timestamp.toordinal(pd.Timestamp(t)) for t in data['Date']]) price = np.log(data['Adj Close'].values) observations = np.array([time, price]) ``` -------------------------------- ### QLPPLS Constructor Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/qlppls.md Initializes a quantile regression LPPLS model with time series observations. Allows specifying a quantile level 'q' to focus on different parts of the price distribution. ```APIDOC ## QLPPLS.__init__ ### Description Initialize a quantile regression LPPLS model with time series observations. ### Method __init__ ### Parameters #### Path Parameters - **observations** (np.ndarray | pd.DataFrame) - Required - 2xM matrix with timestamp (row 0) and observed log-price values (row 1). Timestamps should be numeric ordinals. - **q** (float) - Optional - Quantile level [0, 1]. q=0.5 (median) is symmetric, q<0.5 emphasizes lower tail, q>0.5 emphasizes upper tail. Default: 0.5 ### Returns None (initializes instance) ### Example ```python import numpy as np import pandas as pd from lppls.lppls_q import QLPPLS from lppls import data_loader # Load example data data = data_loader.nasdaq_dotcom() # Convert to ordinal timestamps time = np.array([pd.Timestamp.toordinal(pd.Timestamp(d)) for d in data['Date']]) # Get log prices price = np.log(data['Adj Close'].values) # Create observation matrix observations = np.array([time, price]) # Initialize model with median regression (q=0.5) model = QLPPLS(observations=observations, q=0.5) # Or use upper quantile to focus on extreme prices model_upper = QLPPLS(observations=observations, q=0.75) # Or use lower quantile model_lower = QLPPLS(observations=observations, q=0.25) ``` ``` -------------------------------- ### Instantiate and Fit QLPPLS Model (Median Regression) Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/qlppls.md Use q=0.5 for a robust fit that is less sensitive to outliers. This is the default behavior. ```python model = QLPPLS(observations=observations, q=0.5) tc, m, w, a, b, c, c1, c2, O, D = model.fit(max_searches=25) ``` -------------------------------- ### Lagrange Detection Result Dictionary Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/types.md The Lagrange Detection Result Dictionary is returned by the `detect_bubble_start_time_via_lagrange()` method. It contains information about the estimated bubble start time and related fitting parameters. It returns None if fewer than 2 valid fits are found. ```APIDOC ## Lagrange Detection Result Dictionary **Type:** `dict[str, Any] | None` **Source:** Returned by `detect_bubble_start_time_via_lagrange()` **Schema:** ```python { "tau": float, # Bubble start time (ordinal) "optimal_window_size": int, # Optimal fitting window size "tc": float, # Critical time "m": float, # Power law exponent "w": float, # Angular frequency "a": float, # Intercept "b": float, # Power amplitude "c1": float, # Cosine coefficient "c2": float, # Sine coefficient "window_sizes": list[int], # All tested window sizes "sse_list": list[float], # SSE for each window "ssen_list": list[float], # Normalized SSE for each window "lagrange_sse_list": list[float], # Lagrange-regularized SSE "start_times": list[float] # Start times of windows tested } ``` Returns `None` if fewer than 2 valid fits found. **Example:** ```python model = lppls.LPPLS(observations=observations) result = model.detect_bubble_start_time_via_lagrange( max_window_size=500, min_window_size=50, step_size=5, max_searches=25 ) if result: print(f"Bubble started at ordinal: {result['tau']}") print(f"Critical time: {result['tc']}") # Access diagnostic arrays import matplotlib.pyplot as plt plt.plot(result['window_sizes'], result['lagrange_sse_list']) plt.xlabel('Window Size') plt.ylabel('Lagrange-Regularized SSE') plt.show() ``` ``` -------------------------------- ### Instantiate and Fit LPPLS Model Source: https://github.com/boulder-investment-technologies/lppls/blob/master/notebooks/compute_nested_fits_example.ipynb Instantiates the LPPLS model with the preprocessed observations and fits the model using nested fits with specified window sizes and increments. This process computes the model parameters based on the provided data. ```python # instantiate a new LPPLS model with the Nasdaq Dot-com bubble dataset lp_model = lppls.LPPLS(observations=observations) # fit the model to the data and get back the params res = lp_model.compute_nested_fits( window_size=126, smallest_window_size=21, outer_increment=1, inner_increment=5, max_searches=25, ) ``` -------------------------------- ### Execute LPPLS Lagrange Regularization Source: https://github.com/boulder-investment-technologies/lppls/blob/master/notebooks/lagrange_regularization.ipynb Download S&P 500 data, perform LPPLS model fitting, and visualize the results using Lagrange regularization. ```python import numpy as np import pandas as pd import matplotlib.pyplot as plt from lppls import lppls_lm import yfinance as yf import os FILE_NAME = "sp500.csv" if not os.path.exists(FILE_NAME): ticker_symbol = "^GSPC" # S&P 500 start_date = "1984-11-25" # Start date from the paper end_date = "1987-07-15" # End date from the paper data = yf.download(ticker_symbol, start=start_date, end=end_date) data.to_csv(FILE_NAME, index=False) else: data = pd.read_csv(FILE_NAME) price_col = "Adj Close" if "Adj Close" in data.columns else "Close" data[price_col] = pd.to_numeric(data[price_col], errors="coerce") data.dropna(subset=[price_col], inplace=True) data.reset_index(drop=True, inplace=True) time = np.arange(len(data)) time_scaled = (time - time.min()) / (time.max() - time.min()) price = np.log(data[price_col].values) price_scaled = (price - price.min()) / (price.max() - price.min()) observations = np.array([time_scaled, price_scaled]) lppls_model = lppls_lm.LPPLS(observations) # detect bubble start time via Lagrange regularization # https://arxiv.org/pdf/1707.07162 result = lppls_model.detect_bubble_start_time_via_lagrange( max_window_size=len(time), min_window_size=100, step_size=3, max_searches=25 ) if result: tau = result["tau"] print(f"Estimated bubble start time (tau): {tau}") tc = result["tc"] m = result["m"] w = result["w"] a = result["a"] b = result["b"] c1 = result["c1"] c2 = result["c2"] window_sizes = result["window_sizes"] sse_list = result["sse_list"] ssen_list = result["ssen_list"] lagrange_sse_list = result["lagrange_sse_list"] start_times = result["start_times"] window_sizes_np = np.array(window_sizes) sse_np = np.array(sse_list) ssen_np = np.array(ssen_list) lagrange_sse_np = np.array(lagrange_sse_list) start_times_np = np.array(start_times) # extract the optimal window data optimal_window_size = result["optimal_window_size"] start_idx = len(time_scaled) - optimal_window_size end_idx = len(time_scaled) time_fit = time_scaled[start_idx:end_idx] price_fit = price_scaled[start_idx:end_idx] # compute the LPPLS fit over the optimal window predictions = lppls_model.lppls(time_fit, tc, m, w, a, b, c1, c2) fig, axs = plt.subplots(2, 1, figsize=(12, 10), sharex=True) # top subplot: plot the data and the LPPLS fit axs[0].plot(time_scaled, price_scaled, label="Actual Price", color="black") axs[0].plot(time_fit, predictions, label="LPPLS Fit", color="blue") tau_time = time_scaled[start_idx] axs[0].axvline( x=tau_time, color="black", linestyle="--", label="Optimal Start Time" ) if time_scaled.min() <= tc <= time_scaled.max(): axs[0].axvline(x=tc, color="green", linestyle="--", label="Critical Time (tc)") axs[0].set_title("LPPLS Fit to the S&P 500 Dataset (Scaled Data)") axs[0].set_ylabel("Scaled Log Price") axs[0].legend() axs[0].grid(True) # bottom subplot: plot χ²_np(Φ) and χ²_λ(Φ) against start times axs[1].plot( start_times_np, ssen_np, "o", label=r"$\chi^2_{np}(\Phi)$", color="green", markersize=5, markerfacecolor="none", ) axs[1].plot( start_times_np, lagrange_sse_np, "^", label=r"$\chi^2_{\lambda}(\Phi)$", color="red", markersize=5, markerfacecolor="none", ) # optimal start time axs[1].axvline( x=tau_time, color="black", linestyle="--", label="Optimal Start Time" ) axs[1].set_xlabel("Scaled Time") axs[1].set_ylabel(r"$\chi^2$") axs[1].legend() axs[1].grid(True) plt.tight_layout() # plt.show() if not os.path.exists("../img"): os.makedirs("../img") plt.savefig("../img/lagrange_regularization_plot.png") else: print("Could not estimate the bubble start time.") ``` -------------------------------- ### Create Observation Matrix Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/types.md Demonstrates how to create the observation matrix using NumPy arrays or a Pandas DataFrame. This matrix is the fundamental input for LPPLS models. ```python import numpy as np import pandas as pd # Create observation matrix time = np.array([737000.0, 737001.0, 737002.0, ...]) # ordinal timestamps price = np.array([7.0, 7.1, 6.95, ...]) # log prices observations = np.array([time, price]) # Shape: (2, 500) for 500 days of data # Alternative: pandas DataFrame (2 columns) df = pd.DataFrame({ 'time': time, 'price': price }) # Will be converted to (2, 500) array internally ``` -------------------------------- ### Fit LPPLS Model Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/README.md Initializes and fits the LPPLS model to the prepared observations. Prints the critical time, power exponent, and oscillation count. ```python # Create and fit model model = lppls.LPPLS(observations=observations) tc, m, w, a, b, c, c1, c2, O, D = model.fit(max_searches=25) print(f"Critical time: {tc}") print(f"Power exponent: {m:.3f}") print(f"Oscillations: {O:.2f}") ``` -------------------------------- ### Using a Different Optimizer in fit() Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/errors.md If the default optimizer is not yielding good results, try specifying a different one, such as 'BFGS', to improve the fit. ```python tc, m, w, a, b, c, c1, c2, O, D = model.fit(max_searches=25, minimizer="BFGS") ``` -------------------------------- ### Examine Raw Fits for Bubble Parameters Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/errors.md When troubleshooting, it's helpful to inspect the raw fit results, including parameters like 'b', 'm', and 'w', to understand the characteristics of detected bubbles. ```python # 3. Examine raw fits for window_result in res: for fit in window_result['res']: print(f"b={fit['b']:.3f}, m={fit['m']:.3f}, w={fit['w']:.3f}") ``` -------------------------------- ### Loading Custom Data Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/data-loader.md Demonstrates how to load and prepare custom datasets for use with the LPPLS model, ensuring the data is in the required format. ```APIDOC ## Loading Custom Data ### Description While `data_loader.nasdaq_dotcom()` provides a built-in dataset, you can also use your own data. This involves loading your data (e.g., from CSV), ensuring it has a date and price column, and converting it to the required 2xN observation matrix format for the LPPLS model. ### Example ```python import numpy as np import pandas as pd from lppls import lppls # Load your own data (from CSV, API, etc.) df = pd.read_csv('your_data.csv', parse_dates=['date']) # Ensure you have a date column and a price column # Convert to the required 2xN observation matrix times = np.array([pd.Timestamp.toordinal(d) for d in df['date']]) prices = np.log(df['price'].values) # Use log prices observations = np.array([times, prices]) # Now use with LPPLS model = lppls.LPPLS(observations=observations) tc, m, w, a, b, c, c1, c2, O, D = model.fit(max_searches=25) ``` ``` -------------------------------- ### LPPLSCMAES Constructor Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/lpplscmaes.md Initializes a CMA-ES LPPLS model with time series observations. The observations should be a 2xM matrix containing timestamps and log-price values. ```APIDOC ## LPPLSCMAES.__init__ ### Description Initialize a CMA-ES LPPLS model with time series observations. ### Parameters #### Path Parameters - **observations** (np.ndarray | pd.DataFrame) - Required - 2×M matrix with timestamp (row 0) and observed log-price values (row 1). Timestamps should be numeric ordinals. ### Returns None (initializes instance) ### Example ```python import numpy as np import pandas as pd from lppls.lppls_cmaes import LPPLSCMAES from lppls import data_loader # Load example data data = data_loader.nasdaq_dotcom() # Convert to ordinal timestamps time = np.array([pd.Timestamp.toordinal(pd.Timestamp(d)) for d in data['Date']]) # Get log prices price = np.log(data['Adj Close'].values) # Create observation matrix observations = np.array([time, price]) # Initialize CMA-ES model model = LPPLSCMAES(observations=observations) ``` ``` -------------------------------- ### Fit and Visualize LPPLS Model Source: https://github.com/boulder-investment-technologies/lppls/blob/master/notebooks/lm.ipynb Loads NASDAQ data, fits the LPPLS model using the Levenberg-Marquardt algorithm, and generates a visualization of the fit. ```python from lppls import lppls_lm, data_loader import numpy as np import pandas as pd from datetime import datetime as dt %matplotlib inline # read example dataset into df data = data_loader.nasdaq_dotcom() time = [pd.Timestamp.toordinal(dt.strptime(t1, "%Y-%m-%d")) for t1 in data["Date"]] price = np.log(data["Adj Close"].values) observations = np.array([time, price]) # the literature suggests 25 MAX_SEARCHES = 25 lppls_model = lppls_lm.LPPLS_LM(observations=observations) # fit the model to the data and get back the params tc, m, w, a, b, c, c1, c2, O, D = lppls_model.fit(MAX_SEARCHES) print(tc, m, w, a, b, c, c1, c2, O, D) # visualize the fit lppls_model.plot_fit() ``` -------------------------------- ### LPPLS Model Configuration: Exploratory Analysis Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/configuration.md This configuration is for exploratory analysis, prioritizing sensitivity over specificity. It loosens most parameter ranges to capture a wider variety of potential signals, potentially increasing false positives. ```python config_exploratory = { "m_min": 0.0, "m_max": 1.0, "w_min": 1.5, "w_max": 20.0, "O_min": 1.0, "D_min": 0.2, "tc_min_days": 10, "tc_max_days": 365, "tc_min_frac": 0.5, "tc_max_frac": 0.5, } res = model.mp_compute_nested_fits( workers=4, filter_conditions_config=config_exploratory ) ``` -------------------------------- ### Configure Nested Fits with mp_compute_nested_fits Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/configuration.md Configure and execute nested fits using mp_compute_nested_fits. Specify parameters like workers, window sizes, increments, and filter conditions. ```python res = model.mp_compute_nested_fits( workers=4, window_size=80, smallest_window_size=20, outer_increment=5, inner_increment=2, max_searches=25, filter_conditions_config={ "m_min": 0.0, "m_max": 1.0, ... } ) ``` -------------------------------- ### Fit with BFGS Optimizer Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/configuration.md Utilize the BFGS quasi-Newton method for fitting. This method is generally faster than Nelder-Mead when gradients are helpful. ```python model.fit(max_searches=25, minimizer="BFGS") ``` -------------------------------- ### Fit model with more initializations for accuracy Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/configuration.md Increase `max_searches` for more random initializations and use the 'BFGS' minimizer to improve model accuracy. ```python # More random initializations model.fit(max_searches=50, minimizer="BFGS") ``` -------------------------------- ### Compute Nested Fits and Print Results Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/types.md Shows how to compute nested fits using the LPPLS model and iterate through the results. Each fit result is a dictionary containing various parameters. ```python from lppls import lppls, data_loader import numpy as np import pandas as pd data = data_loader.nasdaq_dotcom() time = np.array([pd.Timestamp.toordinal(pd.Timestamp(t)) for t in data['Date']]) price = np.log(data['Adj Close'].values) observations = np.array([time, price]) model = lppls.LPPLS(observations=observations) res = model.mp_compute_nested_fits(workers=4, window_size=120) # Each element in res["res"] is a fit result dict for window_result in res: for fit_dict in window_result["res"]: print(fit_dict) # { # 'tc': 730395.5, # 'm': 0.567, # 'w': 8.234, # 'a': 8.012, # 'b': -0.456, # 'c': 0.234, # ... # } ``` -------------------------------- ### Load Nasdaq Dot-com Dataset Source: https://github.com/boulder-investment-technologies/lppls/blob/master/_autodocs/api-reference/data-loader.md Loads the bundled Nasdaq Dot-com bubble dataset. Returns a pandas DataFrame with 'Date' and 'Adj Close' columns. Useful for demonstrating LPPLS capabilities. ```python from lppls import data_loader import numpy as np import pandas as pd # Load the dataset data = data_loader.nasdaq_dotcom() print(data.head()) # Date Adj Close # 0 1990-01-02 2741.70 # 1 1990-01-03 2693.36 # 2 1990-01-04 2704.52 # ... print(f"Shape: {data.shape}") # (2800+, 2) print(f"Date range: {data['Date'].iloc[0]} to {data['Date'].iloc[-1]}") ```