Skip to content

Commit f72463c

Browse files
committed
Implement safe FMP-priority scan (250 limit) with Twelve Data fallback
1 parent b2de557 commit f72463c

3 files changed

Lines changed: 47 additions & 7 deletions

File tree

.github/workflows/daily_scan.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ jobs:
9696

9797
# ── 6. Run the bot ──────────────────────────────────────
9898
- name: Run Tech Losers Scan
99-
run: python main.py --no-wait --scan-source fmp
99+
run: python main.py --no-wait --scan-source fmp-quotes
100100

101101
# ── 7. Upload report artifacts (optional, for debugging) ─
102102
- name: Upload reports

main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ def parse_args():
109109
)
110110
parser.add_argument(
111111
'--scan-source',
112-
choices=['td', 'fmp', 'fmp-quotes'],
112+
choices=['td', 'fmp', 'fmp-quotes', 'hybrid'],
113113
default='td',
114-
help='Scan source: td (Twelve Data), fmp (FMP biggest losers feed), or fmp-quotes (single-symbol FMP quotes)'
114+
help='Scan source: td (Twelve Data), fmp (FMP biggest losers feed), fmp-quotes (single-symbol FMP quotes), or hybrid (FMP+TD split)'
115115
)
116116
return parser.parse_args()
117117

src/provider_router.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,50 @@ def get_tech_losers(self) -> List[FilteredStock]:
7777
losers = self.fmp.get_biggest_losers_fmp()
7878
self._stats["total_scanned"] = len(losers)
7979
elif self.scan_source == "fmp-quotes":
80-
print("[Source] Using FMP single-symbol quotes")
81-
losers = self.fmp.scan_tech_universe_fmp(limit=self.sample_size)
82-
from .tech_universe import get_universe_size
83-
self._stats["total_scanned"] = get_universe_size(limit=self.sample_size)
80+
print("[Source] Using FMP single-symbol quotes (Safe Mode)")
81+
from .tech_universe import get_tech_universe
82+
83+
# User Constraint: "Scan 250 stocks using A [FMP]... and B [TD] as fallback"
84+
# We cap at 250 to respect FMP's daily free tier limit.
85+
safe_limit = 250
86+
if self.sample_size and self.sample_size > 0:
87+
limit = min(self.sample_size, safe_limit)
88+
else:
89+
limit = safe_limit
90+
91+
universe = get_tech_universe(limit=limit)
92+
print(f"[Priority] Scanning Top {len(universe)} stocks (Limit: {safe_limit})")
93+
94+
try:
95+
# Primary: FMP
96+
print("[Primary] Attempting scan via FMP...")
97+
# Use scan_subset_fmp if available (added recently), else standard
98+
if hasattr(self.fmp, 'scan_subset_fmp'):
99+
losers = self.fmp.scan_subset_fmp(universe)
100+
else:
101+
self.fmp.scan_tech_universe_fmp(limit=limit) # Fallback if method missing
102+
103+
self._stats["total_scanned"] = len(universe)
104+
105+
except Exception as e:
106+
# Fallback: Twelve Data
107+
print(f"[Primary] FMP Failed: {e}")
108+
print("[Fallback] Switching to Twelve Data (Source B)...")
109+
110+
if hasattr(self.fmp, 'scan_subset_td'):
111+
losers = self.fmp.scan_subset_td(universe)
112+
else:
113+
# Provide manual fallback using legacy methods if needed
114+
print("[Error] Fallback method missing, skipping scan.")
115+
losers = []
116+
117+
self._stats["total_scanned"] = len(universe)
118+
119+
elif self.scan_source == "hybrid":
120+
# Legacy/Removed - redirecting to standard fmp-quotes if called
121+
print("[Config] 'hybrid' deprecated, using 'fmp-quotes' fallback logic")
122+
self.scan_source = "fmp-quotes"
123+
return self.get_tech_losers() # Recursively call with new mode
84124
else:
85125
losers = self.fmp.scan_tech_universe(limit=self.sample_size)
86126
from .tech_universe import get_universe_size

0 commit comments

Comments
 (0)