Files
historical-prices/app.py
T

120 lines
4.0 KiB
Python

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)