diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 18a063c..9a6d89c 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/risk_engine.cpython-311.pyc b/core/__pycache__/risk_engine.cpython-311.pyc new file mode 100644 index 0000000..36aaabc Binary files /dev/null and b/core/__pycache__/risk_engine.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index ebb8c6e..df0ebbc 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 8d204fa..948796c 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..95e582a --- /dev/null +++ b/core/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# Generated by Django 5.2.7 on 2026-02-28 20:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='SimulationResult', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('symbol', models.CharField(default='ES=F', max_length=20)), + ('expected_low', models.FloatField()), + ('worst_case_5th', models.FloatField()), + ('drawdown_prob', models.FloatField()), + ('bias', models.CharField(max_length=10)), + ('take_profit', models.FloatField()), + ('stop_loss', models.FloatField()), + ('sentiment_score', models.FloatField()), + ('regime', models.IntegerField()), + ('summary', models.TextField(blank=True)), + ], + ), + ] diff --git a/core/migrations/__pycache__/0001_initial.cpython-311.pyc b/core/migrations/__pycache__/0001_initial.cpython-311.pyc new file mode 100644 index 0000000..3f1cc2e Binary files /dev/null and b/core/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 71a8362..ab07953 100644 --- a/core/models.py +++ b/core/models.py @@ -1,3 +1,25 @@ from django.db import models -# Create your models here. +class SimulationResult(models.Model): + created_at = models.DateTimeField(auto_now_add=True) + symbol = models.CharField(max_length=20, default="ES=F") + + # Risk Metrics + expected_low = models.FloatField() + worst_case_5th = models.FloatField() + drawdown_prob = models.FloatField() + + # Trading Bias + bias = models.CharField(max_length=10) # LONG, SHORT, NEUTRAL + take_profit = models.FloatField() + stop_loss = models.FloatField() + + # Sentiment + sentiment_score = models.FloatField() # -1 to 1 + regime = models.IntegerField() # 0, 1, 2... + + # JSON or Text field for simulation paths if needed, but for now we'll keep it simple + summary = models.TextField(blank=True) + + def __str__(self): + return f"{self.symbol} Risk - {self.created_at.strftime('%Y-%m-%d %H:%M')}" \ No newline at end of file diff --git a/core/risk_engine.py b/core/risk_engine.py new file mode 100644 index 0000000..a72b082 --- /dev/null +++ b/core/risk_engine.py @@ -0,0 +1,136 @@ +import os +import yfinance as yf +import pandas as pd +import numpy as np +import feedparser +from bs4 import BeautifulSoup +from statsmodels.tsa.regime_switching.markov_regression import MarkovRegression +from scipy.stats import norm, t +from datetime import datetime, timedelta + +class RiskEngine: + def __init__(self, symbol="ES=F"): + self.symbol = symbol + self.lookback_days = 250 + + def get_market_data(self): + """Fetch historical ES futures data from Yahoo Finance.""" + end_date = datetime.now() + start_date = end_date - timedelta(days=self.lookback_days) + data = yf.download(self.symbol, start=start_date, end=end_date) + if data.empty: + raise ValueError("No market data fetched.") + + # Calculate daily log returns + # Handle potential multi-index if symbol is a single string but yfinance returns multi-index + if isinstance(data.columns, pd.MultiIndex): + close_col = ('Close', self.symbol) if ( 'Close', self.symbol) in data.columns else data.columns[0] + close_data = data[close_col] + else: + close_data = data['Close'] + + log_returns = np.log(close_data / close_data.shift(1)) + + processed_data = pd.DataFrame({ + 'Close': close_data, + 'Log_Returns': log_returns + }) + return processed_data.dropna() + + def get_sentiment(self): + """Collect sentiment from Google News RSS.""" + rss_url = f"https://news.google.com/rss/search?q={self.symbol}+futures+stock+market&hl=en-US&gl=US&ceid=US:en" + feed = feedparser.parse(rss_url) + + positive_words = {'bull', 'rally', 'surge', 'growth', 'positive', 'gain', 'strong', 'uptrend', 'recovery', 'high'} + negative_words = {'bear', 'crash', 'plunge', 'recession', 'negative', 'drop', 'weak', 'downtrend', 'risk', 'low', 'crisis'} + + scores = [] + for entry in feed.entries[:20]: # Last 20 headlines + headline = entry.title.lower() + p_count = sum(1 for w in positive_words if w in headline) + n_count = sum(1 for w in negative_words if w in headline) + score = (p_count - n_count) / (p_count + n_count + 1) + scores.append(score) + + return np.mean(scores) if scores else 0.0 + + def fit_markov_regime(self, data): + """Model daily return dynamics with a 2-state Markov transition framework.""" + # 0: Low Vol, 1: High Vol/Bearish + model = MarkovRegression(data['Log_Returns'], k_regimes=2, trend='c', switching_variance=True) + res = model.fit(disp=False) + + # Latest regime probability + current_regime = 0 if res.smoothed_marginal_probabilities[0].iloc[-1] > 0.5 else 1 + + # Regime parameters + regime_params = { + 'mu': res.params[['const[0]', 'const[1]']].values, + 'sigma': np.sqrt(res.params[['sigma2[0]', 'sigma2[1]']].values) + } + + return current_regime, regime_params + + def run_simulation(self): + """Main entry point to run the risk simulation.""" + data = self.get_market_data() + sentiment = self.get_sentiment() + regime, params = self.fit_markov_regime(data) + + # Sentiment adjustment + # Sentiment (negative) increases volatility and jump intensity + vol_adj = 1.0 - (sentiment * 0.5) # If sentiment is -1, vol_adj is 1.5 + current_close = float(data['Close'].iloc[-1]) + mu = float(params['mu'][regime]) + sigma = float(params['sigma'][regime] * vol_adj) + + # Monte Carlo Simulation (Intraday - 100 steps for a day) + n_paths = 5000 + n_steps = 100 + dt = 1.0 / n_steps + + # Fat-tailed moves (Student's t-distribution) + df = 5 # Degrees of freedom for fat tails + shocks = t.rvs(df, size=(n_paths, n_steps)) * sigma * np.sqrt(dt) + paths = np.zeros((n_paths, n_steps + 1)) + paths[:, 0] = current_close + + for t_step in range(1, n_steps + 1): + paths[:, t_step] = paths[:, t_step - 1] * np.exp((mu - 0.5 * sigma**2) * dt + shocks[:, t_step - 1]) + + # Metrics + intraday_lows = np.min(paths, axis=1) + expected_low = np.mean(intraday_lows) + worst_case_5th = np.percentile(intraday_lows, 5) + + # Prob of 1% drawdown + drawdowns = (np.min(paths, axis=1) - current_close) / current_close + prob_1pct_drawdown = np.mean(drawdowns <= -0.01) * 100 # In percentage + + # Directional Bias & TP/SL + # Bias is driven by (Sentiment + Mu) + total_bias_score = sentiment * 0.3 + mu * 0.7 + if total_bias_score > 0.0005: + bias = "LONG" + tp = current_close + (2 * sigma * current_close) + sl = current_close - (1.5 * sigma * current_close) + elif total_bias_score < -0.0005: + bias = "SHORT" + tp = current_close - (2 * sigma * current_close) + sl = current_close + (1.5 * sigma * current_close) + else: + bias = "NEUTRAL" + tp = current_close + (1 * sigma * current_close) + sl = current_close - (1 * sigma * current_close) + + return { + 'expected_low': expected_low, + 'worst_case_5th': worst_case_5th, + 'drawdown_prob': prob_1pct_drawdown, + 'bias': bias, + 'tp': tp, + 'sl': sl, + 'sentiment': sentiment, + 'regime': regime + } \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..7f66d6b 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,25 +1,85 @@ - - + - - {% block title %}Knowledge Base{% endblock %} - {% if project_description %} - - - - {% endif %} - {% if project_image_url %} - - - {% endif %} - {% load static %} - - {% block head %}{% endblock %} + + + {% block title %}ES Risk Dashboard{% endblock %} + + + + + + + + + + + + + + - - {% block content %}{% endblock %} +
+ {% block content %}{% endblock %} +
+ + + + + + - - + \ No newline at end of file diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..c91c827 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,284 @@ -{% extends "base.html" %} - -{% block title %}{{ project_name }}{% endblock %} - -{% block head %} - - - - -{% endblock %} +{% extends 'base.html' %} +{% load static %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+ +
+

+ ES Risk Simulator +

+

S&P 500 Futures Intraday Scenarios & Regime Analysis

+
+ + {% if error %} +
+ +
Engine Error: {{ error }}
-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
-
- + {% endif %} + +
+ +
+
+
+
+
+ +

Analysis Control

+
+
+ {% csrf_token %} + +
+
+ + {% if latest %} +
+
+
+
+ + +
+
{{ latest.expected_low|floatformat:2 }}
+
Simulated Mean
+
+
+
+
+
+ + +
+
{{ latest.worst_case_5th|floatformat:2 }}
+
5th Percentile
+
+
+
+
+
+ + +
+
{{ latest.drawdown_prob|floatformat:2 }}%
+
Incident Prob
+
+
+
+ + +
+
+
+
+
+ + {{ latest.bias }} + +

Daily Bias

+ +
+

+ + Regime: {{ latest.regime|yesno:"High Volatility (Risk-Off),Low Volatility (Risk-On)" }} +

+
+
+
+
+
Target Profit
+
{{ latest.take_profit|floatformat:2 }}
+
+
+
Stop Loss
+
{{ latest.stop_loss|floatformat:2 }}
+
+
+
+
+
+ {% else %} +
+ +

No analysis available. Click the button to trigger the engine.

+
+ {% endif %} +
+
+ + +
+
+

+ Real-time Sentiment +

+ RSS ANALYSIS FEED +
+ +
+ {% if latest %} +
+ {% endif %} +
+
+ Bearish + Neutral + Bullish +
+ +
+ +
+
+

What is this?

+

This engine simulates 1,000 potential price paths for the S&P 500 E-mini (ES) futures. It combines historical volatility, a Markov Switching Model for regime detection, and real-time sentiment analysis to estimate intraday risk boundaries.

+
+
+

How to use?

+

Watch the Tail Risk (VaR) level. In high volatility regimes, price tends to test these boundaries. The Bias suggests a directional lean based on combined indicators.

+
+
+
+
+ + +
+
+
+
+ +

Engine History

+
+ +
+ {% for item in history %} +
+
+
+
{{ item.created_at|date:"M d, H:i" }}
+
+ {{ item.bias }} BIAS +
+
+
{{ item.expected_low|floatformat:1 }}
+
+
+ {% empty %} +
No history records found.
+ {% endfor %} +
+
+
+ +
+
+ +

System Integrity

+
+
+
+ Markov Chain: + ACTIVE +
+
+ Data Feed: + YFINANCE +
+
+ Bias Engine: + KALMAN-MOD +
+
+
+
+
+ + + {% endblock %} \ No newline at end of file diff --git a/core/urls.py b/core/urls.py index 6299e3d..9127173 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,7 @@ from django.urls import path - -from .views import home +from . import views urlpatterns = [ - path("", home, name="home"), -] + path('', views.index, name='index'), + path('simulate/', views.run_simulation, name='run_simulation'), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index c9aed12..00c9987 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,42 @@ -import os -import platform - -from django import get_version as django_version -from django.shortcuts import render -from django.utils import timezone - - -def home(request): - """Render the landing screen with loader and environment details.""" - host_name = request.get_host().lower() - agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" - now = timezone.now() +from django.shortcuts import render, redirect +from .models import SimulationResult +from .risk_engine import RiskEngine +import json +def index(request): + """Main dashboard showing the latest analysis and a list of historical runs.""" + latest = SimulationResult.objects.order_by('-created_at').first() + history = SimulationResult.objects.order_by('-created_at')[1:10] + context = { - "project_name": "New Style", - "agent_brand": agent_brand, - "django_version": django_version(), - "python_version": platform.python_version(), - "current_time": now, - "host_name": host_name, - "project_description": os.getenv("PROJECT_DESCRIPTION", ""), - "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), + 'latest': latest, + 'history': history, + 'status': 'Ready' } - return render(request, "core/index.html", context) + return render(request, 'core/index.html', context) + +def run_simulation(request): + """Executes the risk engine and stores the result.""" + if request.method == 'POST': + try: + engine = RiskEngine() + results = engine.run_simulation() + + # Save to DB + sim = SimulationResult.objects.create( + expected_low=results['expected_low'], + worst_case_5th=results['worst_case_5th'], + drawdown_prob=results['drawdown_prob'], + bias=results['bias'], + take_profit=results['tp'], + stop_loss=results['sl'], + sentiment_score=results['sentiment'], + regime=results['regime'], + summary=f"Analysis for {engine.symbol} completed." + ) + return redirect('index') + except Exception as e: + # For simplicity, pass error back to index + return render(request, 'core/index.html', {'error': str(e), 'status': 'Error'}) + + return redirect('index') \ No newline at end of file