89 lines
3.2 KiB
Python
89 lines
3.2 KiB
Python
from flask import Flask, render_template, jsonify
|
|
from engine import DataEngine
|
|
import concurrent.futures
|
|
from flask_caching import Cache
|
|
import csv
|
|
import os
|
|
import logging
|
|
|
|
app = Flask(__name__)
|
|
|
|
# Configure caching
|
|
cache = Cache(app, config={'CACHE_TYPE': 'SimpleCache'})
|
|
|
|
# Configure logging
|
|
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
|
# Function to load instruments from a CSV file
|
|
def load_instruments_from_csv(file_path):
|
|
instruments = []
|
|
try:
|
|
abs_path = os.path.join(os.path.dirname(__file__), file_path)
|
|
# 'utf-8-sig' handles the hidden characters Excel often adds
|
|
with open(abs_path, mode='r', encoding='utf-8-sig') as csvfile:
|
|
# Clean spaces from column names automatically
|
|
reader = csv.DictReader(csvfile)
|
|
reader.fieldnames = [name.strip().lower() for name in reader.fieldnames]
|
|
|
|
for row in reader:
|
|
symbol = row.get('symbol', '').strip()
|
|
cusip = row.get('cusip', '').strip()
|
|
|
|
if symbol and cusip:
|
|
url = f"https://am.jpmorgan.com/FundsMarketingHandler/historicalData?cusip={cusip}&country=hk&role=per&userLoggedIn=false&language=en&version=6.9.0_1684"
|
|
instruments.append({"symbol": symbol, "url": url})
|
|
|
|
logging.info(f"Successfully loaded {len(instruments)} instruments.")
|
|
except Exception as e:
|
|
logging.error(f"Error reading CSV file: {e}")
|
|
return instruments
|
|
|
|
# Load instruments from CSV
|
|
URL_CONFIG = load_instruments_from_csv('instruments.csv')
|
|
|
|
# Log the contents of URL_CONFIG to verify instruments are loaded
|
|
logging.debug(f"Loaded instruments: {URL_CONFIG}")
|
|
|
|
@cache.memoize(timeout=86400) # Cache results for 24 hours
|
|
def fetch_and_calculate(config):
|
|
try:
|
|
logging.debug(f"Fetching data for: {config['symbol']}")
|
|
engine = DataEngine(config['url'])
|
|
df = engine.fetch_data()
|
|
if df is None or df.empty:
|
|
logging.warning(f"No data fetched for: {config['symbol']}")
|
|
return None
|
|
|
|
metrics = engine.calculate_table_metrics(df)
|
|
if metrics:
|
|
metrics['symbol'] = config['symbol']
|
|
logging.debug(f"Metrics calculated for {config['symbol']}: {metrics}")
|
|
return metrics
|
|
logging.warning(f"Metrics calculation failed for: {config['symbol']}")
|
|
return None
|
|
except Exception as e:
|
|
logging.error(f"Error processing {config['symbol']}: {e}")
|
|
return None
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return render_template('index.html')
|
|
|
|
@app.route('/api/summary')
|
|
def get_summary():
|
|
results = []
|
|
# Use ThreadPoolExecutor for faster parallel fetching
|
|
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
|
|
future_to_config = {executor.submit(fetch_and_calculate, cfg): cfg for cfg in URL_CONFIG}
|
|
for future in concurrent.futures.as_completed(future_to_config):
|
|
res = future.result()
|
|
if res:
|
|
results.append(res)
|
|
|
|
# Sort by symbol name
|
|
results.sort(key=lambda x: x['symbol'])
|
|
return jsonify(results)
|
|
|
|
if __name__ == '__main__':
|
|
app.run(debug=True, host='0.0.0.0', port=5000) # Changed port back to 5000
|