from flask import Flask, render_template, jsonify from datetime import datetime, timedelta from engine import DataEngine import concurrent.futures from flask_caching import Cache import csv, os, logging app = Flask(__name__) cache = Cache(app, config={'CACHE_TYPE': 'SimpleCache'}) import os import csv def load_instruments_from_csv(file_path): instruments = [] # Standard static templates # For AGI, we use the ISIN-based Tearsheet URL TEMPLATES = { 'jpm': "https://am.jpmorgan.com/FundsMarketingHandler/historicalData?cusip={cusip}&country=hk&role=per", 'yahoo': "https://query1.finance.yahoo.com/v8/finance/chart/{cusip}?range=5y&interval=1d", 'agi': "https://markets.ft.com/data/funds/tearsheet/historical?s={cusip}" } try: abs_path = os.path.join(os.path.dirname(__file__), file_path) if not os.path.exists(abs_path): print(f"Error: {file_path} not found.") return [] with open(abs_path, mode='r', encoding='utf-8-sig') as csvfile: reader = csv.DictReader(csvfile) # Standardize header names to lowercase reader.fieldnames = [name.strip().lower() for name in reader.fieldnames] for row in reader: symbol = row.get('symbol', '').strip() cusip = row.get('cusip', '').strip() provider = row.get('provider', 'jpm').strip().lower() if symbol and cusip: # Fetch correct template; default to JPM if provider is unknown template = TEMPLATES.get(provider, TEMPLATES['jpm']) url = template.format(cusip=cusip) instruments.append({ "symbol": symbol, "url": url, "provider": provider }) except Exception as e: print(f"CSV Loading Error: {e}") return instruments # Usage URL_CONFIG = load_instruments_from_csv('instruments.csv') @cache.memoize(timeout=3600) def fetch_and_calculate(config): engine = DataEngine(config['symbol'], config['url'], config['provider']) df = engine.fetch_data() if df is not None: metrics = engine.calculate_table_metrics(df) if metrics: metrics['symbol'] = config['symbol'] return metrics return None @app.route('/') def index(): return render_template('index.html') @app.route('/api/summary') def get_summary(): results = [] with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: futures = [executor.submit(fetch_and_calculate, cfg) for cfg in URL_CONFIG] for f in concurrent.futures.as_completed(futures): if f.result(): results.append(f.result()) results.sort(key=lambda x: x['symbol']) return jsonify(results) if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5000)