Building A Hybrid Trading Model with ML and Python

Building a momentum-adjusted value model by combining Fundamentals and Options DataPhoto by Sebastian Svenson on UnsplashIntro: Navigating Market Information OverloadThere’s too much information in markets these days, investors and traders are faced wi…


This content originally appeared on Level Up Coding - Medium and was authored by Nikhil Adithyan

Building a momentum-adjusted value model by combining Fundamentals and Options Data

Photo by Sebastian Svenson on Unsplash

Intro: Navigating Market Information Overload

There’s too much information in markets these days, investors and traders are faced with many opposing signals. To navigate this, investors or traders typically rely on two main types of indicators.

On the positive side, fundamental metrics like earnings per share (EPS), price-to-earnings (P/E) ratios, and revenue growth provide a fair view of a company’s underlying strength and intrinsic value. On the negative side, options market data like implied volatility (IV), put/call ratios, and open interest reflect the market sentiment and expected direction of price.

By bringing together these two perspectives, it is then possible to build a trading signal that is more robust and precisely tuned to long-term value and short-term momentum. This two-dimensional approach screens out more extraneous information, thereby realizing an important benefit by disclosing opportunity that may otherwise go undetected when basing a decision on a single data stream.

Historical Use of Data: Fundamentals Versus Options

Historically, fundamental data has been employed by analysts to make an estimate of a firm’s underlying worth, with options data being the preserve of short-term, sentiment strategies. The combination of these two approaches can release a form of potential or warn of the onset of peril.

For example, let’s assume a stock can be considered to be undervalued based on its fundamental measures. Despite this, increased implied volatility within the options market could suggest that traders expect a huge price move, whether this is positive or negative.

Developing a Decision-Support Tool with Dual Signal Approaches

The objective of this is to develop a decision-support tool that clearly indicates best times to buy, sell, or hold assets. Market participants can gain a better understanding of the market processes by carefully combining long-term fundamental analyses with short-term options indicators. The overall strategy avoids dangers of following fleeting trends or overlooking crucial warning signals and allows more informed and assertive trading decisions.

We also aim to contrast two other approaches to generating trading signals. One uses a straightforward rule-based framework, applying pre-specified thresholds to a “composite score” in an effort to produce buy, sell, or hold recommendations. The other uses machine learning — i.e., a logistic regression classifier trained on simulated data — to convert the composite score into actionable recommendations.

The simplicity and adaptability of these two approaches will be contrasted, demonstrating that while a rule-based system is easy to imagine and implement, a machine learning-based system can potentially capture more complex market dynamics.

Data Sources and API Endpoints

To build our dataset, we rely on three key API endpoints from EODHD:

1. EODHD Fundamentals Data API

  • Endpoint: https://eodhd.com/api/fundamentals/{SYMBOL.EXCHANGE}
  • What It Provides: Comprehensive metrics about a company’s financials, such as P/E ratio, EBITDA, revenue, sector, and more.
  • Why We Need It: We specifically extract the P/E ratio (a widely used valuation measure) to gauge how “expensive” or “cheap” a stock might be.

2. EODHD Options Contracts API

  • Endpoint: https://eodhd.com/api/mp/unicornbay/options/contracts
  • What It Provides: Metadata on available options contracts for a particular underlying stock, including expiration dates and strike prices.
  • Why We Need It: Before we can pull actual options trading data, we must know which contracts exist. We filter for the ones that are most relevant (in our example, we just pick the first contract returned).

3. EODHD Options EOD (End-of-Day) API

  • Endpoint: https://eodhd.com/api/mp/unicornbay/options/eod
  • What It Provides: End-of-day details for options trades — bid/ask, volume, open interest, and implied volatility (IV).
  • Why We Need It: We compute the average implied volatility across a handful of end-of-day records to capture short-term sentiment or risk perception.

Signal Generation Methodologies

Composite Score Formula for Integrating Fundamentals and Options Metrics

Composite Score Formula for Integrating Fundamentals and Options Metrics

In order to translate raw data into useful signals, there must be some well-defined method of reading both options and fundamental metrics. One method is to combine important constituents into a single “composite score.” As an example, a lower P/E ratio generally indicates a higher fundamental value; therefore, we reverse the P/E ratio and multiply by 100 to preserve the scale. We also use the average implied volatility (Avg IV) of options data to capture short-term market sentiment — where a high IV indicates the market is anticipating high price volatility.

Comparing Rule-Based vs. ML-Based Methods

Comparing two methods for generating these signals, two stand out: a rule-based and an ML model.

Rule-based Method:

In the rule-based method, pre-determined thresholds on the composite score generate the signal:

  • below a certain “buy” threshold, the signal is to buy
  • above a “sell” threshold, the signal is to sell
  • otherwise, the signal is to hold.

This is straightforward, very interpretable, and easy to change.

ML-based Method:

On the other hand, the ML-based method applies a form of training method, for example — a logistic regression classifier — which is trained on preprocessed data to learn and classify composite scores into classes:

  • 0 for Buy
  • 1 for Hold
  • 2 for Sell

Though the ML approach can pick up on non-linearities or subtle patterns that simple thresholds cannot, it also introduces complexity and needs good training data to perform well.

Trade-Off Between Simplicity and Complexity

Briefly, simplicity and complexity trade-off.

  • A rule-based system is easy to state, assess, and understand and thus is appropriate for quick-decision systems.
  • An ML system, on the other hand, is able to outperform simple thresholds in certain market scenarios, but at the cost of interpretability and good-quality historical data or well-crafted synthetic data.

Both approaches contain valuable insights, and the most appropriate choice typically depends on the specific market scenario and the goals of the trader.

Now let’s dive into the technical implementation.

Technical Implementation

1. Importing Libraries and Configuring Logging

This section sets up the necessary Python libraries for our project. We import requests for API calls, numpy for numerical operations, and matplotlib.pyplot for plotting our results.

Additionally, we bring in the logging module to provide key output messages, and from scikit‑learn, we import LogisticRegression and StandardScaler for our ML model. Logging is configured to show only the most important messages, ensuring the console output remains uncluttered.

import requests
import numpy as np
import matplotlib.pyplot as plt
import logging
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler

# Configure logging to show only results
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)

API_KEY = "your API key"

2. Data Ingestion Functions

This section defines three functions that retrieve data from our APIs. The fetch_fundamentals function fetches fundamental financial data for a given ticker (defaulting to "AAPL.US") by constructing a URL and sending a GET request.

The fetch_options_contracts function retrieves options contracts for a specified underlying symbol ("AAPL" by default), while the fetch_options_eod function fetches end-of-day options data for a particular contract ID.

Each function checks if the response is successful (HTTP 200) and logs the outcome; if not, it logs an error and returns None.

def fetch_fundamentals(ticker="AAPL.US"):
url = f"https://eodhd.com/api/fundamentals/{ticker}"
params = {
"api_token": API_KEY,
"fmt": "json"
}
response = requests.get(url, params=params, timeout=10)
if response.status_code == 200:
data = response.json()
logging.info(f"Fundamentals Data for {ticker} retrieved.")
return data
else:
logging.info(f"Error fetching fundamentals data: {response.status_code}")
return None

def fetch_options_contracts(underlying="AAPL"):
url = "https://eodhd.com/api/mp/unicornbay/options/contracts"
params = {
"api_token": API_KEY,
"filter[underlying_symbol]": underlying,
"page[limit]": "10"
}
response = requests.get(url, params=params, timeout=10)
if response.status_code == 200:
data = response.json()
logging.info(f"Options Contracts Data for {underlying} retrieved.")
return data
else:
logging.info(f"Error fetching options contracts data: {response.status_code}")
return None

def fetch_options_eod(contract_id):
url = "https://eodhd.com/api/mp/unicornbay/options/eod"
params = {
"api_token": API_KEY,
"filter[contract]": contract_id,
"page[limit]": "5"
}
response = requests.get(url, params=params, timeout=10)
if response.status_code == 200:
data = response.json()
logging.info(f"Options EOD Data for contract {contract_id} retrieved.")
return data
else:
logging.info(f"Error fetching options EOD data: {response.status_code}")
return None

3. Feature Extraction

The extract_features function takes the raw fundamentals and options EOD data and transforms them into a single composite score.

It first attempts to extract the trailing P/E ratio from the fundamentals. Then, it collects implied volatility values from the options data, computes their average, and finally calculates the composite score using the formula:

This composite score integrates both long-term value (via P/E) and short-term sentiment (via implied volatility).

def extract_features(fundamentals_data, options_eod_data):
try:
pe_ratio = fundamentals_data["Highlights"]["PERatio"]
except (KeyError, TypeError):
pe_ratio = None

iv_list = []
if options_eod_data and "data" in options_eod_data:
for rec in options_eod_data["data"]:
iv = rec["attributes"].get("volatility")
if iv is not None:
iv_list.append(iv)
avg_iv = np.mean(iv_list) if iv_list else None

if pe_ratio is not None and avg_iv is not None and pe_ratio != 0:
# Composite Score: (1/PE)*100 + Avg IV
composite_score = (1 / pe_ratio) * 100 + avg_iv
else:
composite_score = None

logging.info(f"Extracted Features:\n PE Ratio: {pe_ratio}\n Avg IV: {avg_iv}\n Composite Score: {composite_score}")
return composite_score

4. Signal Generation Methods

In this section, two methods for generating trading signals from the composite score are defined.

The rule_based_signal function uses simple thresholds to classify the signal:

  • if the composite score is below 1.5, it indicates a “Buy”
  • if above 2.5, it signals “Sell”; otherwise, it indicates “Hold.”

The ml_signal function takes a more advanced approach. It generates synthetic composite scores and labels them using the same thresholds, then standardizes the data and trains a logistic regression model.

The trained model is used to predict the signal for the current composite score, which is then logged.

def rule_based_signal(score, threshold_buy=1.5, threshold_sell=2.5):
if score is None:
return "No Signal"
if score < threshold_buy:
return "Buy"
elif score > threshold_sell:
return "Sell"
else:
return "Hold"

def ml_signal(current_score):
np.random.seed(42)
synthetic_scores = np.random.uniform(0.5, 3.5, 300)
synthetic_labels = np.array([0 if s < 1.5 else 2 if s > 2.5 else 1 for s in synthetic_scores])
X = synthetic_scores.reshape(-1, 1)
y = synthetic_labels

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

model = LogisticRegression(multi_class='multinomial', solver='lbfgs')
model.fit(X_scaled, y)

current_scaled = scaler.transform(np.array([[current_score]]))
pred_label = model.predict(current_scaled)[0]
mapping = {0: "Buy", 1: "Hold", 2: "Sell"}
ml_decision = mapping.get(pred_label, "No Signal")

logging.info(f"ML-Based Signal determined: {ml_decision}")
return ml_decision, synthetic_scores, synthetic_labels, scaler, model

5. Main Workflow & Comparison

The main function orchestrates the entire process. It starts by fetching the fundamentals data for "AAPL.US" and options contracts for "AAPL." If options contracts are found, it selects the first one and fetches its EOD data. The extracted data is then passed to extract_features to calculate the composite score. If a valid score is computed, trading signals are generated using both the rule-based and ML-based methods, and the results are logged.

Finally, the function visualizes the synthetic training data and the current composite score in a scatter plot, allowing users to see where the current score falls within the overall distribution.

def main():
fundamentals_data = fetch_fundamentals("AAPL.US")
options_contracts_data = fetch_options_contracts("AAPL")

if options_contracts_data and "data" in options_contracts_data and len(options_contracts_data["data"]) > 0:
first_contract = options_contracts_data["data"][0]["id"]
options_eod_data = fetch_options_eod(first_contract)
else:
options_eod_data = None

composite_score = extract_features(fundamentals_data, options_eod_data)
if composite_score is None:
logging.info("Insufficient data to compute composite score. Exiting.")
return

rule_signal = rule_based_signal(composite_score)
logging.info(f"Rule-Based Signal: {rule_signal}")

ml_decision, synthetic_scores, synthetic_labels, scaler, model = ml_signal(composite_score)
logging.info(f"ML-Based Signal: {ml_decision}")

# Visualization: Plot synthetic training data with current composite score
plt.figure(figsize=(10, 6))
plt.scatter(synthetic_scores, synthetic_labels, c=synthetic_labels, cmap="viridis", label="Synthetic Data")
plt.axvline(x=composite_score, color='red', linestyle='--', label=f"Current Score: {composite_score:.2f}")
plt.xlabel("Composite Score")
plt.ylabel("Signal (0:Buy, 1:Hold, 2:Sell)")
plt.title("Synthetic Training Data & Current Composite Score")
plt.legend()
plt.show()

if __name__ == "__main__":
main()

Each section of this code is designed to work together: data is fetched from various sources, transformed into actionable metrics, and then used to generate trading signals through both simple rule-based and more sophisticated ML-based approaches.

The final visualization helps non-technical users easily grasp how the composite score aligns with the generated signals.

Case Study / Example Workflow

Step-by-Step Data Collection

To illustrate how our pipeline works in practice, we ran the script to gather and process Apple’s (AAPL) data.

First, we called fetch_fundamentals("AAPL.US"), which returned a P/E ratio of approximately 39.2104 (as shown in the log snippet of Figure 2).

Next, we called fetch_options_contracts("AAPL") and selected a contract named something like AAPL270617C00360000.

Finally, we invoked fetch_options_eod(contract_id), which fetched several records containing implied volatility. We then averaged these IV values to get an Avg IV of 0.2274.

Signal Calculation

With the fundamentals (P/E) and options (IV) data in hand, our script computed the composite score using the formula:

Terminal Logs — Fundamentals & Composite Score

For Apple, that came out to approximately 2.7777. We fed this composite score into two different methods:

  • Rule-Based: Since 2.7777 exceeds the sell threshold of 2.5, it triggered a “Sell” signal.
  • ML-Based: Our logistic regression model, trained on synthetic data, also labeled 2.7777 as “Sell.”

Backtesting & Validation

In a real trading environment, you would integrate these signals into a historical dataset to evaluate performance over time.

For instance, you could update the fundamentals each quarter when new earnings are released and refresh the options data daily or weekly. You would then measure performance metrics like accuracy, precision, and recall for classification, or track profit & loss if the signals were used in an actual strategy.

This process helps confirm whether the signals consistently add value or need recalibration.

Insights from a Real-world Scenario

Terminal Logs — Rule-Based & ML Signals

Both the rule-based and ML-based approaches pointed to “Sell” at a composite score of roughly 2.78, suggesting Apple might be “expensive” (due to its relatively high P/E) and has moderate implied volatility in the near term. Of course, this is only a snapshot. The real strength of this method lies in watching how the composite score evolves over time, and how it stacks up against other stocks with varying fundamentals and volatility profiles.

Results Recap

From our demonstration logs and plot:

Synthetic Training Data & Current Composite Score

1. Fundamentals Data

  • P/E Ratio (AAPL): 39.2104

2. Options Data

  • Avg Implied Volatility: 0.2274

3. Composite Score

  • 2.7777 (approx.)

4. Rule-Based Signal

  • “Sell” (since 2.7777 > 2.5)

5. ML-Based Signal

  • “Sell” (logistic regression also labeled 2.7777 as Sell)

The accompanying chart illustrates the synthetic data distribution, with a red dashed line at 2.78 to indicate where Apple’s current score lies. Observing how the synthetic data is divided into clusters (0=Buy, 1=Hold, 2=Sell) confirms that 2.78 falls in the “Sell” region.

Key Takeaways

1. Data Integration for Informed Decisions

The combination of fundamentals and options data gives the investor the full picture. The combination of a valuation measure such as the P/E ratio with an options-based sentiment measure such as implied volatility allows us to capture long-term intrinsic value and short-term sentiment.

This two-pronged approach not only improves signal generation but is also versatile, as evidenced by our application of both a simple rule-based threshold method and a more flexible machine learning model.

Both produce actionable recommendations — Buy, Sell, or Hold — whereas the modular architecture of the pipeline makes it easy to extend in the future to include more sophisticated fundamentals and other options measures such as put/call ratios or gamma exposure.

2. Analysis with Complementary Techniques

For those traders using these derived signals, it would be optimal to utilize the composite score in combination with other analytical techniques. While the composite score is extremely informative, it should not be the sole basis of decision-making.

The integration of technical analysis, monitoring of macroeconomic trends, and use of sound risk management practices are essential for integrated trading decisions. Further, in light of the fact that fundamental information is normally updated on a quarterly basis and implied volatility can change on a daily or intraday basis, frequent monitoring and timely updating of the composite score is necessary.

Traders also need to monitor and update their buy/sell points regularly to adapt to changing market conditions or changes in a stock’s volatility profile.

3. Future Enhancements and Expansion Opportunities

Regarding possible future improvements, there are various possible enhancements meant to enhance this method. Various possible areas to explore include including other multi-factor fundamentals like earnings growth, cash flow, or analyst estimates in order to have a more comprehensive assessment of the financial health of a company.

Further, enriching the suite of options metrics to include variables like put/call volume ratios, open interest changes, or volatility skew may enhance short-term sentiment analysis. Further, proper historical backtesting through a time-series database with composite scores vs. actual price movement could serve to yield notable performance metrics.

Lastly, research into more advanced machine learning tools like gradient boosting, random forests, or deep learning may enhance the capability of capturing intricate market dynamics beyond that of our present logistic regression strategy.

Final Thoughts

This illustration demonstrates the strength of combining basic and options data. With even a simplistic example, we can observe how a high P/E ratio (a potentially overpriced stock) and medium implied volatility can be used to create a sell signal.

Certainly, in practical use, a more sophisticated approach — with more data and extensive backtesting — can be used to provide even more trusted trading signals. But this exercise is a sufficient foundation for any individual who would like to integrate fundamental and short-term sentiment indicators into their 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.


Building A Hybrid Trading Model with ML and Python was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Nikhil Adithyan


Print Share Comment Cite Upload Translate Updates
APA

Nikhil Adithyan | Sciencx (2025-03-05T02:17:23+00:00) Building A Hybrid Trading Model with ML and Python. Retrieved from https://www.scien.cx/2025/03/05/building-a-hybrid-trading-model-with-ml-and-python/

MLA
" » Building A Hybrid Trading Model with ML and Python." Nikhil Adithyan | Sciencx - Wednesday March 5, 2025, https://www.scien.cx/2025/03/05/building-a-hybrid-trading-model-with-ml-and-python/
HARVARD
Nikhil Adithyan | Sciencx Wednesday March 5, 2025 » Building A Hybrid Trading Model with ML and Python., viewed ,<https://www.scien.cx/2025/03/05/building-a-hybrid-trading-model-with-ml-and-python/>
VANCOUVER
Nikhil Adithyan | Sciencx - » Building A Hybrid Trading Model with ML and Python. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/03/05/building-a-hybrid-trading-model-with-ml-and-python/
CHICAGO
" » Building A Hybrid Trading Model with ML and Python." Nikhil Adithyan | Sciencx - Accessed . https://www.scien.cx/2025/03/05/building-a-hybrid-trading-model-with-ml-and-python/
IEEE
" » Building A Hybrid Trading Model with ML and Python." Nikhil Adithyan | Sciencx [Online]. Available: https://www.scien.cx/2025/03/05/building-a-hybrid-trading-model-with-ml-and-python/. [Accessed: ]
rf:citation
» Building A Hybrid Trading Model with ML and Python | Nikhil Adithyan | Sciencx | https://www.scien.cx/2025/03/05/building-a-hybrid-trading-model-with-ml-and-python/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.