Annual EPS
This commit is contained in:
parent
cb098278c9
commit
b6c6518ad4
16
src/main.py
16
src/main.py
@ -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
29
src/screener/a_canslim.py
Normal 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
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user