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