Annual EPS

This commit is contained in:
Bobby Abellana 2025-02-03 22:10:09 -08:00
parent cb098278c9
commit b6c6518ad4
No known key found for this signature in database
GPG Key ID: 647714CC45F3647B
3 changed files with 56 additions and 29 deletions

View File

@ -1,6 +1,7 @@
import datetime import datetime
from screener.data_fetcher import validate_date_range, fetch_financial_data, get_stocks_in_time_range from screener.data_fetcher import validate_date_range, fetch_financial_data, get_stocks_in_time_range
from screener.c_canslim import check_quarterly_earnings, check_sales_growth, check_return_on_equity from screener.c_canslim import check_quarterly_earnings
from screener.a_canslim import check_annual_eps_growth # New module
from screener.csv_appender import append_scores_to_csv from screener.csv_appender import append_scores_to_csv
def main(): def main():
@ -27,21 +28,18 @@ def main():
if not data: if not data:
print(f"Warning: No data returned for {symbol}. Assigning default score.") print(f"Warning: No data returned for {symbol}. Assigning default score.")
scores = { scores = {
"EPS_Score": 0.25, # EPS Growth "EPS_Score": 0.25, # Quarterly EPS Growth
"Sales_Score": 0.25, # Sales Growth "Annual_EPS_Score": 0.25 # Annual EPS Growth
"ROE_Score": 0.25 # Return on Equity
} }
else: else:
# Extract relevant fields # Extract relevant fields
quarterly_eps = data.get("eps", []) quarterly_eps = data.get("quarterly_eps", [])
sales_growth = data.get("sales_growth", []) annual_eps = data.get("annual_eps", [])
roe = 18 # Placeholder, modify if needed
# 5⃣ Compute CANSLIM Scores # 5⃣ Compute CANSLIM Scores
scores = { scores = {
"EPS_Score": check_quarterly_earnings(quarterly_eps), "EPS_Score": check_quarterly_earnings(quarterly_eps),
"Sales_Score": check_sales_growth(sales_growth), "Annual_EPS_Score": check_annual_eps_growth(annual_eps)
"ROE_Score": check_return_on_equity(roe)
} }
# 6⃣ Append results to a **generic** CSV file in `data/metrics/` # 6⃣ Append results to a **generic** CSV file in `data/metrics/`

29
src/screener/a_canslim.py Normal file
View File

@ -0,0 +1,29 @@
def check_annual_eps_growth(annual_eps):
"""
Checks for 25%-50%+ annual EPS growth over the last three years.
Args:
annual_eps (list): List of annual EPS values (must contain at least 3 years of data).
Returns:
float: Score (1 pass, 0 fail, 0.25 insufficient data).
"""
if not annual_eps or len(annual_eps) < 3:
return 0.25 # Not enough data for 3-year growth calculation
# Calculate year-over-year EPS growth rates
growth_rates = []
for i in range(1, len(annual_eps)): # Compare consecutive years
prev_eps = annual_eps[i - 1]
current_eps = annual_eps[i]
if prev_eps <= 0: # Avoid division by zero or negative EPS
return 0.25
growth = ((current_eps - prev_eps) / abs(prev_eps)) * 100
growth_rates.append(growth)
# Check if all growth rates are 25%+
if all(rate >= 25 for rate in growth_rates):
return 1 # Pass
return 0 # Fail

View File

@ -31,8 +31,9 @@ def validate_date_range(start_date, end_date, required_quarters=4):
def fetch_financial_data(symbol, start_date, end_date): def fetch_financial_data(symbol, start_date, end_date):
""" """
Fetch financial data (EPS, Sales Growth) from stock_financials table Fetch financial data for a given stock symbol, including:
for a given stock symbol within a specific date range. - Quarterly EPS for EPS Score
- Annual EPS for Annual EPS Score
Args: Args:
symbol (str): Stock ticker symbol. symbol (str): Stock ticker symbol.
@ -40,21 +41,20 @@ def fetch_financial_data(symbol, start_date, end_date):
end_date (str or datetime): End date for data retrieval. end_date (str or datetime): End date for data retrieval.
Returns: Returns:
dict: A dictionary containing calculated EPS and sales growth. dict: Contains EPS, sales growth, and annual EPS.
""" """
client = create_client() client = create_client()
# Query stock_financials for revenue, net income, and EPS
query = f""" query = f"""
SELECT SELECT
filing_date, filing_date,
diluted_eps,
revenue, revenue,
net_income, timeframe
diluted_eps -- Using diluted EPS for accuracy
FROM stock_db.stock_financials FROM stock_db.stock_financials
WHERE ticker = '{symbol}' WHERE ticker = '{symbol}'
AND filing_date BETWEEN toDate('{start_date}') AND toDate('{end_date}') AND filing_date BETWEEN toDate('{start_date}') AND toDate('{end_date}')
AND timeframe = 'quarterly' -- Ensure only quarterly reports are used AND (timeframe = 'quarterly' OR timeframe = 'annual') -- Fetch both annual and quarterly data
ORDER BY filing_date ASC ORDER BY filing_date ASC
""" """
@ -63,36 +63,36 @@ def fetch_financial_data(symbol, start_date, end_date):
if not result.result_rows: if not result.result_rows:
return {} return {}
# Extracting data quarterly_eps = []
dates = [] annual_eps = []
revenues = [] revenues = []
net_incomes = []
eps_values = []
sales_growth = [] sales_growth = []
for row in result.result_rows: for row in result.result_rows:
dates.append(row[0]) filing_date, eps, revenue, timeframe = row
revenues.append(row[1])
net_incomes.append(row[2]) if timeframe == "quarterly":
eps_values.append(row[3]) # Directly using diluted EPS quarterly_eps.append(eps)
revenues.append(revenue)
elif timeframe == "annual":
annual_eps.append(eps)
# Calculate Sales Growth (Quarter-over-Quarter) # Calculate Sales Growth (Quarter-over-Quarter)
for i in range(1, len(revenues)): # Start from index 1 since we compare with previous for i in range(1, len(revenues)): # Start from index 1 since we compare with previous
prev_revenue = revenues[i - 1] prev_revenue = revenues[i - 1]
current_revenue = revenues[i] current_revenue = revenues[i]
if prev_revenue > 0: # Avoid division by zero if prev_revenue > 0:
growth = ((current_revenue - prev_revenue) / prev_revenue) * 100 growth = ((current_revenue - prev_revenue) / prev_revenue) * 100
else: else:
growth = None # Not enough data growth = None # Not enough data
sales_growth.append(growth) sales_growth.append(growth)
# Pad sales_growth list to match length of EPS (since first quarter lacks a previous value) sales_growth.insert(0, None) # First quarter lacks comparison
sales_growth.insert(0, None) # First quarter doesn't have a previous comparison
return { return {
"symbol": symbol, "symbol": symbol,
"dates": dates, "quarterly_eps": quarterly_eps, # Used for EPS_Score
"eps": eps_values, "annual_eps": annual_eps, # Used for Annual_EPS_Score
"sales_growth": sales_growth "sales_growth": sales_growth
} }