90 lines
3.7 KiB
Python
90 lines
3.7 KiB
Python
import pandas as pd
|
|
import json
|
|
import logging
|
|
from src.strategy.scanner import scan_for_candidates
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def find_best_isa_tickers():
|
|
"""
|
|
1. Reads available Trading212 instruments.
|
|
2. Filters for US Stocks (excluding ETFs/Warrants which are often non-ISA compliant or bad for this strategy).
|
|
3. Maps T212 tickers to Yahoo Finance tickers.
|
|
4. Runs the strategy scanner to find the most volatile/liquid candidates.
|
|
"""
|
|
try:
|
|
with open('available_instruments.json', 'r') as f:
|
|
instruments = json.load(f)
|
|
except FileNotFoundError:
|
|
logger.error("Please run get_available_tickers.py first to generate available_instruments.json")
|
|
return
|
|
|
|
# Filter for US Stocks only
|
|
# - type == 'STOCK': Excludes US ETFs (not allowed in UK ISAs) and Warrants
|
|
# - currencyCode == 'USD': Ensures it's trading during the US session (09:30 EST)
|
|
us_stocks = [
|
|
inst for inst in instruments
|
|
if inst.get('type') == 'STOCK' and inst.get('currencyCode') == 'USD'
|
|
]
|
|
|
|
logger.info(f"Filtered down to {len(us_stocks)} US Stocks from Trading212.")
|
|
|
|
# Extract short names (Yahoo Finance tickers)
|
|
# Trading212 usually uses 'shortName' for the actual market ticker (e.g., AAPL)
|
|
# and 'ticker' for their internal ID (e.g., AAPL_US_EQ).
|
|
t212_to_yf_map = {}
|
|
for stock in us_stocks:
|
|
short_name = stock.get('shortName')
|
|
t212_id = stock.get('ticker')
|
|
if short_name and t212_id:
|
|
# Avoid preferred shares/warrants that might sneak in with hyphens or dots
|
|
# if yfinance can't parse them easily, but we'll try them all.
|
|
t212_to_yf_map[short_name] = t212_id
|
|
|
|
yf_tickers = list(t212_to_yf_map.keys())
|
|
|
|
# We don't want to scan all 6,000+ stocks at once (Yahoo Finance will rate limit us).
|
|
# Let's filter locally first using T212 maxOpenQuantity or just take a known subset,
|
|
# OR we can pass the top 500 popular ones. For this script, let's scan a robust list of
|
|
# well-known highly liquid tech/growth stocks that are definitely ISA eligible.
|
|
|
|
# Known high-liquidity ISA-eligible US stocks:
|
|
focus_list = [
|
|
"TSLA", "NVDA", "AMD", "AAPL", "MSFT", "META", "AMZN", "GOOGL", "NFLX",
|
|
"COIN", "MSTR", "PLTR", "UBER", "HOOD", "RST", "SNOW", "CRM", "CRWD",
|
|
"PANW", "SMCI", "ARM", "SQ", "SHOP", "ROKU", "DDOG", "NET", "DOCN"
|
|
]
|
|
|
|
# Ensure they exist in our T212 account
|
|
valid_yf_tickers = [t for t in focus_list if t in t212_to_yf_map]
|
|
logger.info(f"Scanning {len(valid_yf_tickers)} highly liquid known ISA-eligible stocks...")
|
|
|
|
# Run the scanner
|
|
results_df = scan_for_candidates(tickers=valid_yf_tickers, min_price=20.0, min_volume=2_000_000)
|
|
|
|
if results_df.empty:
|
|
logger.warning("No candidates met the minimum price/volume criteria.")
|
|
return
|
|
|
|
# Map back to Trading212 internal tickers so the bot knows what to trade
|
|
results_df['T212_Ticker'] = results_df['Ticker'].map(t212_to_yf_map)
|
|
|
|
# Reorder columns
|
|
cols = ['Ticker', 'T212_Ticker', 'Close', 'ATR_14', 'ATR_Percent', 'Avg_Volume']
|
|
results_df = results_df[cols]
|
|
|
|
print("\n" + "="*80)
|
|
print("🏆 BEST ISA-ELIGIBLE CANDIDATES FOR TOUCH & TURN TODAY 🏆")
|
|
print("="*80)
|
|
print(results_df.to_string(index=False))
|
|
print("\n* Use the 'T212_Ticker' column when configuring the ExecutionManager.")
|
|
|
|
# Save the day's watchlist
|
|
results_df.to_csv("isa_watchlist.csv", index=False)
|
|
logger.info("Saved shortlist to isa_watchlist.csv")
|
|
return results_df
|
|
|
|
if __name__ == "__main__":
|
|
find_best_isa_tickers()
|