Building A Chaos Theory-Based Trading Strategy with Python
- Nikhil Adithyan
- Mar 31
- 9 min read
An experimental approach to creating a unique trading strategy

The stock market moves in an unpredictable and chaotic manner — one day it is up, the next it is down, and sometimes, small events trigger massive swings. Although this unpredictability seems random, it shares similarities with systems under chaos theory. The theory deals with complex systems where small changes can lead to massive, unexpected outcomes — like how a butterfly flapping its wings could cause a tornado elsewhere.
In this article, we explore how chaos theory concepts, particularly the Lyapunov exponent and the Fractal Dimension Indicator, can help us better understand stock price and guide trading decisions.
We will rely on historical stock price data from the FinancialModelingPrep API to put this theory to the test. Specifically, we shall extract stock price data for major companies like Apple, Microsoft, Tesla, and Amazon. The idea is to apply chaos theory techniques to this data. This application will help us determine whether we can spot patterns in the “noise” and design a simple strategy that tells us whether to buy or sell based on how stable or chaotic the market appears.
Without much ado, let’s dive in and find out if embracing a bit of chaos can lead to smarter investing!
Chaos Theory and Its Components
Chaos theory is a branch of mathematics that focuses on complex systems whose behavior is highly sensitive to initial conditions, a phenomenon called the “Butterfly effect.” In such systems, even the smallest of changes can lead to vastly different outcomes, making long-term predictions extremely difficult. Although chaos may appear random, it is governed by deterministic laws. The apparent unpredictability stems from the system’s complexity and sensitivity, not from inherent randomness.
In financial markets, price movements also exhibit similar chaotic behavior driven by multiple interacting factors. However, it is computationally intensive and theoretically overwhelming to apply the entire chaos theory in this article.
Against this background, we focus on two specific components of chaos theory: Lyapunov Exponent and Fractal Dimension Indicator (FDI). The two aspects will help us demonstrate the essential features of market stability and complexity without delving into the full complexity of the chaos models.
Lyapunov Exponent
The Lyapunov exponent is a widely used tool to measure a system's sensitivity to small changes. For example, if two tiny balls are placed very close to each other on a hill, if the hill is smooth, they will roll down together without much difference. However, if the hill is rugged and uneven, one small bump could send them on completely different paths.
In financial markets, the “hill” is the stock price behavior, and the Lyapunov exponent helps us understand whether small price changes tend to stay controlled or quickly spiral into large differences. Significantly, a negative Lyapunov exponent informs us that prices are relatively stable — small fluctuations will not cause dramatic changes. Conversely, a positive value indicates a chaotic market where minor events can have outsized impacts on price movements.
The exponent is particularly useful for traders and investors trying to gauge the market’s predictability. When the value is negative, the market prices are more orderly, and trends are easier to spot and follow. On the other hand, a positive value implies instability, where price movements become unpredictable, making it riskier to assume past trends will continue.
Fractal Dimension Indicator (FDI)
The Fractal Dimension Indicator (FDI) provides an additional lens to view stock price behavior, focusing on their complexity or roughness. A simpler way to understand this concept is to think of drawing a line on a piece of paper. If the line is straight, it's simple and easy to follow. However, if the line is jagged and twists in many directions, it becomes complicated. Stock prices rarely move in a straight line — they continually fluctuate.
Using FDI, we can measure how “jagged” or complex those price movements are over time. A lower FDI value suggests smoother, more directional trends. Conversely, a higher FDI value points to erratic, noisy behavior, making it challenging to predict where prices might go next.
In financial markets, FDI is helpful for identifying whether the market is trending or stuck in a choppy, sideways pattern. Traders find it easier to follow market trends with confidence when the FDI is low. A high value warns them that the market lacks clear direction and could behave unpredictably. These ideas help us classify different periods in the stock's history as either smooth or rough.
When we combine FDI with the Lyapunov exponent, we get a more complete picture of market behavior, increasing our awareness as we approach chaotic environments.
Chaos Theory-based Trading Strategy
In this article, we implement a trading strategy that draws its insights from the Lyapunov Exponent and the Fractal Dimension Indicator. This will help us identify favorable market conditions for buying and selling stocks. The underlying idea is to capitalize on periods when the markets exhibit stability and trend-following behavior while avoiding times when price movements are chaotic and unpredictable.
We implement the strategy as follows:
Buy Signal: When the Lyapunov Exponent is negative (indicating stability) and the Fractal Dimension Indicator is below 1.5 (suggesting smooth, less complex price behavior).
Sell Signal: When the Lyapunov Exponent turns positive (indicating potential chaos) and the Fractal Dimension Indicator exceeds 1.5 (suggesting turbulent, erratic market movements).
Python Implementation
1. Importing Packages
The first step is to import the libraries we shall use in this article.
# Importing libraries
import numpy as np
import pandas as pd
import requests
import matplotlib.pyplot as plt
from termcolor import colored as cl
import math
from scipy.stats import linregress
2. Extracting Historical Data
The next step is to extract historical price data using the FMP’s EOD API endpoint for Apple (AAPL), Microsoft (MSFT), Tesla (TSLA), and Amazon (AMZN). The following Python code extracts historical data for AAPL. Make sure to use the correct API key.
# EXTRACTING HISTORICAL DATA
api_key = "api_key" # use your api key
symbol = "AAPL"
def extract_historical(symbol, api_key):
URL = f"https://financialmodelingprep.com/api/v3/historical-price-full/{symbol}?from=2010-01-01&apikey={api_key}"
response = requests.get(URL)
data = response.json()
df = pd.DataFrame(data["historical"])
df["date"] = pd.to_datetime(df["date"])
df.set_index("date", inplace=True)
df = df.sort_index()
df = df.iloc[:,:6]
return df
df = extract_historical(symbol, api_key)
df
Once the code is run, we get the following dataframe:

3. Calculating the Components
Lyapunov Exponent
The following Python code calculates the Lyapunov exponent.
# Function to calculate Lyapunov Exponent
def lyapunov_exponent(prices, window=50):
log_returns = np.log(prices / prices.shift(1))
lyap_exp = []
for i in range(len(prices) - window):
segment = log_returns[i:i+window].dropna()
if len(segment) > 2:
slope, _, _, _, _ = linregress(range(len(segment)), segment)
lyap_exp.append(slope)
else:
lyap_exp.append(np.nan)
return np.array([np.nan]*window + lyap_exp)
df["Lyapunov"] = lyapunov_exponent(df["close"])
df
We compute the Lyapunov exponent by determining the logarithmic returns of the stock prices and then applying a linear regression over a sliding window (default size 50). The regression slope gives us an indication of how small changes in price can either converge (stability) or diverge (chaos) over time. A negative slope implies market stability, while a positive one signals potentially chaotic behavior.
FDI
We calculate FDI using the following Python code.
# CALCULATING FDI
# Function to calculate Fractal Dimension Indicator (FDI)
def fractal_dimension(prices, window=50):
fdi_values = []
for i in range(len(prices) - window):
segment = prices[i:i+window].dropna()
N = len(segment)
L = np.sum(np.abs(segment - np.mean(segment)))
if L == 0:
fdi_values.append(np.nan)
else:
fdi_values.append(np.log(N) / np.log(L))
return np.array([np.nan]*window + fdi_values)
df["FDI"] = fractal_dimension(df["close"])
df = df.dropna()
df
FDI indicator measures the complexity or roughness of the price series within the same window. We compute the absolute deviations from the mean and apply logarithmic scaling to measure how “fractal-like” the price movements are. Lower values indicate smoother trends, while higher values reflect erratic price behavior.
4. Backtesting
After extracting AAPL historical stock price data and computing the chaos metrics, we use the following Python code to implement the trading strategy.
# BACKTESTING THE STRATEGY
# buy if: Lyapunov < 0 & FDI < 1.5
# sell if: Lyapunov > 0 & FDI > 1.5
def implement_strategy(df, investment, symbol):
in_position = False
equity = investment
for i in range(len(df)):
if df['Lyapunov'][i] < 0 and df['FDI'][i] < 1.5 and in_position == False:
no_of_shares = math.floor(equity/df.adjClose[i])
equity -= (no_of_shares * df.adjClose[i])
in_position = True
print(cl('BUY: ', color = 'green', attrs = ['bold']), f'{no_of_shares} Shares are bought at ${df.adjClose[i]} on {str(df.index[i])[:10]}')
elif df['Lyapunov'][i] > 0 and df['FDI'][i] > 1.5 and in_position == True:
equity += (no_of_shares * df.adjClose[i])
in_position = False
print(cl('SELL: ', color = 'red', attrs = ['bold']), f'{no_of_shares} Shares are bought at ${df.adjClose[i]} on {str(df.index[i])[:10]}')
if in_position == True:
equity += (no_of_shares * df.adjClose[i])
print(cl(f'\nClosing position at {df.adjClose[i]} on {str(df.index[i])[:10]}', attrs = ['bold']))
in_position = False
earning = round(equity - investment, 2)
roi = round(earning / investment * 100, 2)
print('')
print(cl(f'{symbol} BACKTESTING RESULTS:', attrs = ['bold']))
print(cl(f'EARNING: ${earning} ; ROI: {roi}%', attrs = ['bold']))
implement_strategy(df, 100000, 'AAPL')
In this section, we backtest the strategy where it buys shares when the market shows signs of stability (Lyapunov < 0) and smooth trends (FDI < 1.5). Conversely, it sells when the market becomes chaotic (Lyapunov > 0) and price movements are rough (FDI > 1.5), signaling increased risk. The strategy keeps track of the invested capital, executes trades accordingly, and computes total earnings and return on investment at the end of the trading period.
Results from AAPL stocks were as follows:

The strategy executed multiple buy and sell trades based on periods of market stability and chaos, accumulating profits over time. Starting with an initial investment, it capitalized on favorable price conditions, leading to significant compounding gains. Ultimately, the strategy achieved an impressive $2,376,932.75 profit, reflecting a remarkable 2376.93% ROI over the backtesting period.
5. Results Comparison with Different Stocks
Tesla (TSLA)
We compare the results obtained from AAPL using Tesla (TSLA) stocks. We extract historical data, calculate the chaos metrics, and implement the strategy.
# BACKTESTING WITH DIFFERENT STOCKS
# 1. TSLA
tsla = extract_historical('TSLA', api_key)
tsla["Lyapunov"] = lyapunov_exponent(tsla["close"])
tsla["FDI"] = fractal_dimension(tsla["close"])
tsla = tsla.dropna()
implement_strategy(tsla, 100000, 'TSLA')
The strategy dynamically executes trades based on the conditions set out, allowing us to evaluate how well it performs. By the end of the backtesting period, it achieved an impressive $2,864,378.13 profit, delivering a substantial 2864.38% return on investment, which is slightly higher than what was obtained in AAPL above.
Microsoft (MSFT)
We repeat the same procedure as Tesla stocks above. We just changed the ticker symbol to MSFT as follows.
# 2. MSFT
msft = extract_historical('MSFT', api_key)
msft["Lyapunov"] = lyapunov_exponent(msft["close"])
msft["FDI"] = fractal_dimension(msft["close"])
msft = msft.dropna()
implement_strategy(msft, 100000, 'MSFT')

For Microsoft (MSFT), the chaos-driven strategy identified a stable market phase early on and executed a single significant buy trade at a price of $23.33. Holding this position throughout the period, the strategy benefited from Microsoft’s strong long-term growth, closing at $391.26. This resulted in a remarkable $1,576,947.98 profit, achieving a 1576.95% return on investment over the backtest period. This is slightly lower than AAPL’s 2376.93% ROI.
Amazon (AMZN)
Similar to the tickers above, we use the following code for Amazon (AMZN).
# 3. AMZN
amzn = extract_historical('AMZN', api_key)
amzn["Lyapunov"] = lyapunov_exponent(amzn["close"])
amzn["FDI"] = fractal_dimension(amzn["close"])
amzn = amzn.dropna()
implement_strategy(amzn, 100000, 'AMZN')

For Amazon (AMZN), the strategy executed multiple trades over the years, dynamically responding to changing market conditions. While some trades resulted in small losses, the strategy consistently captured profitable opportunities during periods of stability, with significant long-term gains from holding positions through major price uptrends. Ultimately, the strategy yielded a strong $1,465,564.85 profit, delivering a solid 1465.56% return on investment. This was much less than AAPL’s 2376.93% ROI.
6. S&P 500 Benchmark Comparison
We extract historical price data for the S&P 500 index using the functions developed previously. The daily percentage returns of the index are obtained by using the pct_change() method on closing prices, as illustrated in the code below.
# S&P 500 COMPARISON
spy = extract_historical('SPY', api_key)
spy['daily_ret'] = spy.close.pct_change()
spy_cumulative = (1 + spy.daily_ret).cumprod() - 1
spy_cumulative = spy_cumulative.dropna()
print(cl(f'S&P 500 CUMULATIVE RETURNS:', attrs = ['bold']), f'{round(spy_cumulative.iloc[-1]*100,2)}%')

The cumulative return of the S&P 500 index over the same period was calculated by compounding its daily percentage returns. The result shows a total return of 397.64%. However, it does not perform as well as the chaos-driven trading strategy on individual stocks like AAPL, TSLA, MSFT, and AMZN. Hence, it is evident that the strategy, while more active, can outperform the market benchmark substantially under the right conditions.
7. Buy and Hold Comparison
We provide additional context by comparing the chaos-driven strategy’s performance against a simple buy-and-hold (B/H) approach for Apple (AAPL) as in the code below.
# BUY/HOLD COMPARISON
df['daily_ret'] = df.close.pct_change()
df_cumulative = (1 + df.daily_ret).cumprod() - 1
df_cumulative = df_cumulative.dropna()
print(cl(f'AAPL B/H CUMULATIVE RETURNS:', attrs = ['bold']), f'{round(df_cumulative.iloc[-1]*100,2)}%')

The strategy computes the cumulative returns from holding AAPL stock over the entire period without any active trading, yielding an impressive 2628.37% return. Although the buy-and-hold strategy return is strong, the chaos-driven strategy achieved a 2376.93% ROI, which is an impressive return given that it resulted from dynamic entry and exit points.
Conclusion
This analysis was possible due to accurate and high-quality historical data from the FinancialModelingPrep API endpoint. As technologies and markets continue to evolve, combining mathematical models with trusted data remains crucial to informed and adaptive trading decisions.
With that being said, you’ve reached the end of the article. Hope you learned something new and useful today. Thank you very much for your time.