Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3740453b6 |
Binary file not shown.
BIN
core/__pycache__/risk_engine.cpython-311.pyc
Normal file
BIN
core/__pycache__/risk_engine.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
31
core/migrations/0001_initial.py
Normal file
31
core/migrations/0001_initial.py
Normal file
@ -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)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
Binary file not shown.
@ -1,3 +1,25 @@
|
|||||||
from django.db import models
|
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')}"
|
||||||
136
core/risk_engine.py
Normal file
136
core/risk_engine.py
Normal file
@ -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
|
||||||
|
}
|
||||||
@ -1,25 +1,85 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" data-bs-theme="dark">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>{% block title %}Knowledge Base{% endblock %}</title>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
{% if project_description %}
|
<title>{% block title %}ES Risk Dashboard{% endblock %}</title>
|
||||||
<meta name="description" content="{{ project_description }}">
|
<meta name="description" content="S&P 500 Futures Risk Analysis & Scenario Simulation Dashboard">
|
||||||
<meta property="og:description" content="{{ project_description }}">
|
|
||||||
<meta property="twitter:description" content="{{ project_description }}">
|
<!-- Fonts -->
|
||||||
{% endif %}
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
{% if project_image_url %}
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<meta property="og:image" content="{{ project_image_url }}">
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&family=JetBrains+Mono:wght@500&display=swap" rel="stylesheet">
|
||||||
<meta property="twitter:image" content="{{ project_image_url }}">
|
|
||||||
{% endif %}
|
<!-- Bootstrap 5 CSS & Icons -->
|
||||||
{% load static %}
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.min.css">
|
||||||
{% block head %}{% endblock %}
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--neon-green: #39FF14;
|
||||||
|
--neon-blue: #00E5FF;
|
||||||
|
--dark-charcoal: #121212;
|
||||||
|
--slate-gray: #1E1E1E;
|
||||||
|
--off-white: #f8f9fa; /* Brighter off-white for better contrast */
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background-color: var(--dark-charcoal);
|
||||||
|
color: var(--off-white);
|
||||||
|
}
|
||||||
|
.font-mono { font-family: 'JetBrains Mono', monospace; }
|
||||||
|
.navbar-brand { font-weight: 800; letter-spacing: -1px; }
|
||||||
|
.bg-dark-900 { background-color: #0B0B0B; }
|
||||||
|
.text-neon-green { color: var(--neon-green) !important; }
|
||||||
|
.text-neon-blue { color: var(--neon-blue) !important; }
|
||||||
|
|
||||||
|
/* Improved text-muted for dark backgrounds */
|
||||||
|
.text-muted { color: #a0a0a0 !important; }
|
||||||
|
|
||||||
|
.hover-white:hover { color: white !important; }
|
||||||
|
|
||||||
|
/* Custom Scrollbar */
|
||||||
|
::-webkit-scrollbar { width: 8px; }
|
||||||
|
::-webkit-scrollbar-track { background: #1a1a1a; }
|
||||||
|
::-webkit-scrollbar-thumb { background: #333; border-radius: 4px; }
|
||||||
|
::-webkit-scrollbar-thumb:hover { background: #444; }
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
<body class="text-light">
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark-900 border-bottom border-secondary border-opacity-25 py-3">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand text-neon-green d-flex align-items-center" href="{% url 'index' %}">
|
||||||
|
<i class="bi bi-activity me-2"></i>
|
||||||
|
<span class="text-white">RISK</span>FLUX
|
||||||
|
</a>
|
||||||
|
<div class="navbar-nav ms-auto">
|
||||||
|
<a class="nav-link small text-muted hover-white d-flex align-items-center" href="/admin/">
|
||||||
|
<i class="bi bi-shield-lock me-1"></i> ADMIN PANEL
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
<body>
|
<main>
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="py-5 text-center border-top border-secondary border-opacity-10 mt-5">
|
||||||
|
<div class="container">
|
||||||
|
<p class="small text-muted mb-0">© 2026 RISKFLUX. MARKET SIMULATION ENGINE.</p>
|
||||||
|
<p class="small text-muted mb-0 mt-2">Quantitative risk estimation for educational purposes only.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Bootstrap JS Bundle -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// Initialize tooltips
|
||||||
|
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||||
|
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||||
|
return new bootstrap.Tooltip(tooltipTriggerEl)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@ -1,145 +1,284 @@
|
|||||||
{% extends "base.html" %}
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
{% block title %}{{ project_name }}{% endblock %}
|
|
||||||
|
|
||||||
{% block head %}
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--bg-color-start: #6a11cb;
|
|
||||||
--bg-color-end: #2575fc;
|
|
||||||
--text-color: #ffffff;
|
|
||||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
|
||||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
|
||||||
color: var(--text-color);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
body::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M-10 10L110 10M10 -10L10 110' stroke-width='1' stroke='rgba(255,255,255,0.05)'/></svg>");
|
|
||||||
animation: bg-pan 20s linear infinite;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes bg-pan {
|
|
||||||
0% {
|
|
||||||
background-position: 0% 0%;
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
background-position: 100% 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
background: var(--card-bg-color);
|
|
||||||
border: 1px solid var(--card-border-color);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 2.5rem 2rem;
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: clamp(2.2rem, 3vw + 1.2rem, 3.2rem);
|
|
||||||
font-weight: 700;
|
|
||||||
margin: 0 0 1.2rem;
|
|
||||||
letter-spacing: -0.02em;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
opacity: 0.92;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loader {
|
|
||||||
margin: 1.5rem auto;
|
|
||||||
width: 56px;
|
|
||||||
height: 56px;
|
|
||||||
border: 4px solid rgba(255, 255, 255, 0.25);
|
|
||||||
border-top-color: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.runtime code {
|
|
||||||
background: rgba(0, 0, 0, 0.25);
|
|
||||||
padding: 0.15rem 0.45rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sr-only {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
padding: 0;
|
|
||||||
margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1rem;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
opacity: 0.75;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main>
|
<div class="container py-5">
|
||||||
<div class="card">
|
<!-- Header Section -->
|
||||||
<h1>Analyzing your requirements and generating your app…</h1>
|
<header class="mb-5 text-center fade-in">
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
<h1 class="display-3 fw-bold text-white mb-2 font-mono">
|
||||||
<span class="sr-only">Loading…</span>
|
ES Risk <span class="text-neon-green">Simulator</span>
|
||||||
|
</h1>
|
||||||
|
<p class="lead text-muted">S&P 500 Futures Intraday Scenarios & Regime Analysis</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-danger bg-danger bg-opacity-10 border-danger text-danger rounded-4 mb-4 d-flex align-items-center">
|
||||||
|
<i class="bi bi-exclamation-triangle-fill me-2 fs-4"></i>
|
||||||
|
<div><strong>Engine Error:</strong> {{ error }}</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
|
{% endif %}
|
||||||
<p class="hint">This page will refresh automatically as the plan is implemented.</p>
|
|
||||||
<p class="runtime">
|
<div class="row g-4">
|
||||||
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code>
|
<!-- Main Analysis Panel -->
|
||||||
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code>
|
<div class="col-lg-8">
|
||||||
</p>
|
<div class="card bg-slate border-0 shadow-lg rounded-5 overflow-hidden mb-4">
|
||||||
</div>
|
<div class="card-body p-4 p-md-5">
|
||||||
</main>
|
<div class="d-flex justify-content-between align-items-center mb-5">
|
||||||
<footer>
|
<div class="d-flex align-items-center">
|
||||||
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
|
<i class="bi bi-cpu text-neon-green fs-2 me-3"></i>
|
||||||
</footer>
|
<h2 class="h4 text-white mb-0">Analysis Control</h2>
|
||||||
|
</div>
|
||||||
|
<form action="{% url 'run_simulation' %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit" class="btn btn-neon-green px-4 py-2 fw-bold text-dark rounded-pill">
|
||||||
|
<i class="bi bi-lightning-charge-fill me-1"></i> RE-RUN ENGINE
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if latest %}
|
||||||
|
<div class="row g-4 mb-5">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="stat-card p-4 rounded-4 bg-dark-900 border-top border-4 border-neon-blue">
|
||||||
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||||
|
<label class="text-muted small fw-bold">EXPECTED LOW</label>
|
||||||
|
<i class="bi bi-info-circle text-muted small" data-bs-toggle="tooltip" title="The average lowest price projected across 1,000 simulations for today."></i>
|
||||||
|
</div>
|
||||||
|
<div class="h2 text-white font-mono mb-0">{{ latest.expected_low|floatformat:2 }}</div>
|
||||||
|
<div class="small text-neon-blue mt-1">Simulated Mean</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="stat-card p-4 rounded-4 bg-dark-900 border-top border-4 border-danger">
|
||||||
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||||
|
<label class="text-muted small fw-bold">TAIL RISK (VaR)</label>
|
||||||
|
<i class="bi bi-info-circle text-muted small" data-bs-toggle="tooltip" title="Value at Risk: 95% of simulated paths stay above this level."></i>
|
||||||
|
</div>
|
||||||
|
<div class="h2 text-white font-mono mb-0">{{ latest.worst_case_5th|floatformat:2 }}</div>
|
||||||
|
<div class="small text-danger mt-1">5th Percentile</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="stat-card p-4 rounded-4 bg-dark-900 border-top border-4 border-warning">
|
||||||
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||||
|
<label class="text-muted small fw-bold">1% DRAWDOWN</label>
|
||||||
|
<i class="bi bi-info-circle text-muted small" data-bs-toggle="tooltip" title="The probability that price drops more than 1% from current levels today."></i>
|
||||||
|
</div>
|
||||||
|
<div class="h2 text-white font-mono mb-0">{{ latest.drawdown_prob|floatformat:2 }}%</div>
|
||||||
|
<div class="small text-warning mt-1">Incident Prob</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bias and Targets -->
|
||||||
|
<div class="p-4 bg-dark-900 rounded-5 border border-secondary border-opacity-10 position-relative overflow-hidden">
|
||||||
|
<div class="glass-shine"></div>
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-md-6 border-end border-secondary border-opacity-25 py-3">
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<span class="badge rounded-pill {% if latest.bias == 'LONG' %}bg-neon-green{% elif latest.bias == 'SHORT' %}bg-danger{% else %}bg-secondary{% endif %} text-dark me-2">
|
||||||
|
{{ latest.bias }}
|
||||||
|
</span>
|
||||||
|
<h3 class="h4 mb-0 text-white">Daily Bias</h3>
|
||||||
|
<i class="bi bi-info-circle ms-2 text-muted small" data-bs-toggle="tooltip" title="Aggregated recommendation based on Sentiment, Regime, and Intraday Momentum."></i>
|
||||||
|
</div>
|
||||||
|
<p class="text-muted mb-0">
|
||||||
|
<i class="bi bi-grid-1x2 me-1"></i>
|
||||||
|
Regime: <strong>{{ latest.regime|yesno:"High Volatility (Risk-Off),Low Volatility (Risk-On)" }}</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 text-md-center py-3">
|
||||||
|
<div class="d-flex justify-content-around">
|
||||||
|
<div>
|
||||||
|
<div class="small text-muted mb-1 text-uppercase">Target Profit</div>
|
||||||
|
<div class="h4 text-neon-green font-mono mb-0">{{ latest.take_profit|floatformat:2 }}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="small text-muted mb-1 text-uppercase">Stop Loss</div>
|
||||||
|
<div class="h4 text-danger font-mono mb-0">{{ latest.stop_loss|floatformat:2 }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="text-center py-5">
|
||||||
|
<i class="bi bi-database-dash display-1 text-muted opacity-50 mb-4"></i>
|
||||||
|
<p class="text-muted">No analysis available. Click the button to trigger the engine.</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sentiment & Explanation Section -->
|
||||||
|
<div class="card bg-slate border-0 rounded-5 p-4 shadow-sm">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h3 class="h5 text-white mb-0 d-flex align-items-center">
|
||||||
|
<i class="bi bi-graph-up-arrow text-neon-blue me-2"></i> Real-time Sentiment
|
||||||
|
</h3>
|
||||||
|
<span class="small text-muted font-mono">RSS ANALYSIS FEED</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sentiment-bar-container mb-2">
|
||||||
|
{% if latest %}
|
||||||
|
<div class="sentiment-bar" style="width: 50%; margin-left: {% if latest.sentiment_score > 0 %}50{% else %}0{% endif %}%; background: {% if latest.sentiment_score > 0 %}#39FF14{% else %}#ff4444{% endif %}; box-shadow: 0 0 15px {% if latest.sentiment_score > 0 %}#39FF14{% else %}#ff4444{% endif %}80;"></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between small text-muted font-mono">
|
||||||
|
<span class="d-flex align-items-center"><i class="bi bi-arrow-down-circle me-1"></i> Bearish</span>
|
||||||
|
<span>Neutral</span>
|
||||||
|
<span class="d-flex align-items-center">Bullish <i class="bi bi-arrow-up-circle ms-1"></i></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-4 border-secondary border-opacity-25">
|
||||||
|
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h4 class="h6 text-white mb-2 fw-bold">What is this?</h4>
|
||||||
|
<p class="small text-muted">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.</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h4 class="h6 text-white mb-2 fw-bold">How to use?</h4>
|
||||||
|
<p class="small text-muted">Watch the <strong>Tail Risk (VaR)</strong> level. In high volatility regimes, price tends to test these boundaries. The <strong>Bias</strong> suggests a directional lean based on combined indicators.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sidebar / History -->
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card bg-slate border-0 rounded-5 shadow-lg mb-4">
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<div class="d-flex align-items-center mb-4">
|
||||||
|
<i class="bi bi-clock-history text-muted fs-4 me-2"></i>
|
||||||
|
<h3 class="h5 text-white mb-0">Engine History</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="history-list overflow-auto" style="max-height: 400px;">
|
||||||
|
{% for item in history %}
|
||||||
|
<div class="history-item p-3 mb-2 rounded-4 bg-dark-900 transition-all border border-transparent hover-border-neon">
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<div class="text-white small fw-bold font-mono">{{ item.created_at|date:"M d, H:i" }}</div>
|
||||||
|
<div class="small {% if item.bias == 'LONG' %}text-neon-green{% elif item.bias == 'SHORT' %}text-danger{% else %}text-muted{% endif %}">
|
||||||
|
{{ item.bias }} BIAS
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-neon-blue font-mono">{{ item.expected_low|floatformat:1 }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% empty %}
|
||||||
|
<div class="text-muted small text-center py-4">No history records found.</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-4 bg-neon-green bg-opacity-10 border border-neon-green border-opacity-25 rounded-5">
|
||||||
|
<div class="d-flex align-items-center mb-3">
|
||||||
|
<i class="bi bi-shield-check text-neon-green fs-4 me-2"></i>
|
||||||
|
<h4 class="h6 text-neon-green fw-bold mb-0">System Integrity</h4>
|
||||||
|
</div>
|
||||||
|
<div class="small text-muted font-mono">
|
||||||
|
<div class="d-flex justify-content-between mb-2">
|
||||||
|
<span>Markov Chain:</span>
|
||||||
|
<span class="text-neon-green">ACTIVE</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between mb-2">
|
||||||
|
<span>Data Feed:</span>
|
||||||
|
<span class="text-white">YFINANCE</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<span>Bias Engine:</span>
|
||||||
|
<span class="text-white">KALMAN-MOD</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--neon-green: #39FF14;
|
||||||
|
--neon-blue: #00E5FF;
|
||||||
|
--dark-charcoal: #121212;
|
||||||
|
--slate-gray: #1E1E1E;
|
||||||
|
--dark-900: #0B0B0B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-slate { background-color: var(--slate-gray); }
|
||||||
|
.bg-dark-900 { background-color: var(--dark-900); }
|
||||||
|
.text-neon-green { color: var(--neon-green) !important; }
|
||||||
|
.text-neon-blue { color: var(--neon-blue) !important; }
|
||||||
|
|
||||||
|
/* Stronger muted text for readability */
|
||||||
|
.text-muted { color: #a0a0a0 !important; }
|
||||||
|
|
||||||
|
.btn-neon-green {
|
||||||
|
background-color: var(--neon-green);
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 0 15px rgba(57, 255, 20, 0.3);
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
.btn-neon-green:hover {
|
||||||
|
background-color: #2ED110;
|
||||||
|
box-shadow: 0 0 25px rgba(57, 255, 20, 0.5);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.05);
|
||||||
|
}
|
||||||
|
.stat-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
background-color: #151515 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sentiment-bar-container {
|
||||||
|
height: 8px;
|
||||||
|
background: #333;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.sentiment-bar {
|
||||||
|
height: 100%;
|
||||||
|
width: 50%;
|
||||||
|
transition: all 1.2s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-item:hover {
|
||||||
|
background-color: #1a1a1a !important;
|
||||||
|
border-color: rgba(0, 229, 255, 0.3) !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-shine {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 50%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(120deg, transparent, rgba(255,255,255,0.05), transparent);
|
||||||
|
transition: all 0.6s;
|
||||||
|
}
|
||||||
|
.bg-dark-900:hover .glass-shine {
|
||||||
|
left: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeIn 0.8s ease-out;
|
||||||
|
}
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(10px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive overrides */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.stat-card { margin-bottom: 10px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
from .views import home
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", home, name="home"),
|
path('', views.index, name='index'),
|
||||||
|
path('simulate/', views.run_simulation, name='run_simulation'),
|
||||||
]
|
]
|
||||||
@ -1,25 +1,42 @@
|
|||||||
import os
|
from django.shortcuts import render, redirect
|
||||||
import platform
|
from .models import SimulationResult
|
||||||
|
from .risk_engine import RiskEngine
|
||||||
|
import json
|
||||||
|
|
||||||
from django import get_version as django_version
|
def index(request):
|
||||||
from django.shortcuts import render
|
"""Main dashboard showing the latest analysis and a list of historical runs."""
|
||||||
from django.utils import timezone
|
latest = SimulationResult.objects.order_by('-created_at').first()
|
||||||
|
history = SimulationResult.objects.order_by('-created_at')[1:10]
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"project_name": "New Style",
|
'latest': latest,
|
||||||
"agent_brand": agent_brand,
|
'history': history,
|
||||||
"django_version": django_version(),
|
'status': 'Ready'
|
||||||
"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", ""),
|
|
||||||
}
|
}
|
||||||
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')
|
||||||
Loading…
x
Reference in New Issue
Block a user