feat: Add institutional sponsorship check for CANSLIM strategy
This commit is contained in:
parent
9b6871d575
commit
f62cd47292
88
src/screener/i_canslim.py
Normal file
88
src/screener/i_canslim.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
from db.db_connection import create_client
|
||||||
|
|
||||||
|
def check_institutional_sponsorship(symbol):
|
||||||
|
"""
|
||||||
|
Determines if a stock has strong institutional sponsorship.
|
||||||
|
|
||||||
|
Criteria:
|
||||||
|
- Should have at least several institutional sponsors
|
||||||
|
- Look for increasing institutional ownership
|
||||||
|
- Focus on higher-quality institutions
|
||||||
|
- Avoid stocks with too much institutional ownership (>80-90%)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol (str): Stock ticker symbol.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: 1 (Pass), 0 (Fail), 0.25 (Insufficient Data)
|
||||||
|
"""
|
||||||
|
client = create_client()
|
||||||
|
|
||||||
|
# Get current institutional ownership data
|
||||||
|
query = """
|
||||||
|
SELECT
|
||||||
|
COUNT(DISTINCT holder_name) as num_institutions,
|
||||||
|
SUM(percentage_held) as total_ownership,
|
||||||
|
MAX(date_reported) as latest_date
|
||||||
|
FROM stock_db.yf_institutional_holders
|
||||||
|
WHERE ticker = '{}'
|
||||||
|
GROUP BY ticker
|
||||||
|
""".format(symbol)
|
||||||
|
|
||||||
|
result = client.query(query)
|
||||||
|
|
||||||
|
if not result.result_rows:
|
||||||
|
return 0.25 # Insufficient data
|
||||||
|
|
||||||
|
num_institutions, total_ownership, _ = result.result_rows[0]
|
||||||
|
|
||||||
|
# Get ownership trend (comparing with 3 months ago)
|
||||||
|
trend_query = """
|
||||||
|
WITH current AS (
|
||||||
|
SELECT SUM(percentage_held) as current_ownership,
|
||||||
|
MAX(date_reported) as latest_date
|
||||||
|
FROM stock_db.yf_institutional_holders
|
||||||
|
WHERE ticker = '{}'
|
||||||
|
),
|
||||||
|
previous AS (
|
||||||
|
SELECT SUM(percentage_held) as previous_ownership
|
||||||
|
FROM stock_db.yf_institutional_holders
|
||||||
|
WHERE ticker = '{}'
|
||||||
|
AND date_reported <= (SELECT latest_date - INTERVAL 3 MONTH FROM current)
|
||||||
|
)
|
||||||
|
SELECT current_ownership, previous_ownership
|
||||||
|
FROM current, previous
|
||||||
|
""".format(symbol, symbol)
|
||||||
|
|
||||||
|
trend_result = client.query(trend_query)
|
||||||
|
|
||||||
|
# Evaluate criteria
|
||||||
|
if num_institutions is None or total_ownership is None:
|
||||||
|
return 0.25
|
||||||
|
|
||||||
|
# Convert total_ownership to percentage (0-100 scale)
|
||||||
|
total_ownership = float(total_ownership)
|
||||||
|
|
||||||
|
# Criteria thresholds
|
||||||
|
MIN_INSTITUTIONS = 10
|
||||||
|
MIN_OWNERSHIP = 20 # percentage
|
||||||
|
MAX_OWNERSHIP = 85 # percentage
|
||||||
|
|
||||||
|
# Basic institutional presence check
|
||||||
|
has_sufficient_institutions = num_institutions >= MIN_INSTITUTIONS
|
||||||
|
has_healthy_ownership = MIN_OWNERSHIP <= total_ownership <= MAX_OWNERSHIP
|
||||||
|
|
||||||
|
# Check ownership trend
|
||||||
|
ownership_increasing = False
|
||||||
|
if trend_result.result_rows:
|
||||||
|
current_ownership, previous_ownership = trend_result.result_rows[0]
|
||||||
|
if current_ownership and previous_ownership:
|
||||||
|
ownership_increasing = current_ownership > previous_ownership
|
||||||
|
|
||||||
|
# Final evaluation
|
||||||
|
if has_sufficient_institutions and has_healthy_ownership and ownership_increasing:
|
||||||
|
return 1
|
||||||
|
elif not has_sufficient_institutions or total_ownership < MIN_OWNERSHIP:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return 0.25 # Borderline cases
|
||||||
Loading…
Reference in New Issue
Block a user