from flask import Flask, render_template,request, jsonify from datetime import datetime, timedelta from engine import DataEngine, StrategyEngine import concurrent.futures from flask_caching import Cache import csv, os, logging from concurrent.futures import ThreadPoolExecutor app = Flask(__name__) cache = Cache(app, config={'CACHE_TYPE': 'SimpleCache'}) @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(): engine_base = DataEngine() instruments = engine_base.load_instruments_from_csv('instruments.csv') results = [] for item in instruments: engine = DataEngine( symbol=item['symbol'], url=item['url'], provider=item['provider'] ) metrics = engine.get_local_metrics() if metrics and isinstance(metrics, dict): metrics['symbol'] = item['symbol'] results.append(metrics) else: # 🔥 FIX: Include the 'error' key so JavaScript hits the Gatekeeper results.append({ "symbol": item['symbol'], "last_close": None, # Ensure this is null, not the string "No Data" "error": True }) return jsonify(results) @app.route('/api/sync', methods=['POST']) def run_sync(): # ✅ THIS RUNS THE FULL fetch_data() WITH NETWORK ACCESS engine = DataEngine() report = engine.global_sync() return jsonify(report) @app.route('/api/backtest', methods=['POST']) def api_backtest(): data = request.get_json() or {} symbol = data.get('symbol', '').strip().upper() engine = DataEngine(symbol=symbol) if not symbol: return jsonify({"error": "Symbol is required"}), 400 try: # 1. Initialize the Engine # (The Engine's __init__ should handle looking up the URL in instruments.csv) data_eng = DataEngine(symbol=symbol) # 2. Trigger Smart Fetch # (Inside engine.py, this checks the 24h clock and updates if needed) data_eng.fetch_data() # 3. Verify data exists before proceeding if not os.path.exists(data_eng.file_path): return jsonify({"error": f"No data found for {symbol}"}), 404 # 4. Run Strategy strat_eng = StrategyEngine(data_eng) history = strat_eng.run_simulation( start_date=data.get('startDate', '2024-01-01'), monthly_goal=float(data.get('monthly_target', 0)), initial_inv=float(data.get('initial_inv', 0)), frequency=data.get('frequency', 'Monthly'), allow_sell=data.get('allow_sell') is True, allow_fractional=data.get('allow_fractional') is True ) return jsonify(history) except Exception as e: app.logger.error(f"Backtest Error: {str(e)}") return jsonify({"error": "Internal server error"}), 500 @app.route('/backtest') # This is the URL you will actually visit def backtest_ui(): # This sends the HTML file to your browser return render_template('val_avg_cal.html') if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5000)