base version only KD indicator with jpm, ft yahoo as sources
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
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
|
||||
Reference in New Issue
Block a user