107 lines
3.5 KiB
Python
107 lines
3.5 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 {}
|
|
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) |