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 {} # 1. Extract and Sanitize Symbol symbol = data.get('symbol', '').strip().upper() print(f"DEBUG: Processing {symbol} with payload: {data}") if not symbol: return jsonify({"error": "Symbol is required"}), 400 try: # 2. Extract and Cast Inputs (Ensuring types match engine requirements) # Note: JS uses 'startDate' (camelCase), Python often uses 'start_date' initial = float(data.get('initial_inv', 0)) monthly = float(data.get('monthly_target', 0)) start_date = data.get('startDate') or data.get('start_date', '2024-01-01') frequency = data.get('frequency', 'Monthly') # Robust Boolean Check allow_sell = data.get('allow_sell') is True allow_frac = data.get('allow_fractional') is True # 3. Initialize Engines data_eng = DataEngine(symbol=symbol) # Verify file exists after DataEngine logic if not os.path.exists(data_eng.file_path): return jsonify({"error": f"Data for {symbol} could not be retrieved."}), 404 strat_eng = StrategyEngine(data_eng) # 4. Calculation - Calling the correctly named method 'run_simulation' # Ensure arguments match the signature in your engine.py history = strat_eng.run_simulation( start_date=start_date, monthly_goal=monthly, initial_inv=initial, frequency=frequency, allow_sell=allow_sell, allow_fractional=allow_frac ) return jsonify(history) except Exception as e: import traceback print("CRITICAL ERROR in /api/backtest:") print(traceback.format_exc()) # Returning the actual error message helps debugging the '500' faster return jsonify({"error": str(e)}), 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)