FINAL VERSION hackathon

This commit is contained in:
Flatlogic Bot 2026-03-01 11:18:10 +00:00
parent df9b46a506
commit 80afe71917
9 changed files with 465 additions and 179 deletions

Binary file not shown.

149
core/analysis.py Normal file
View File

@ -0,0 +1,149 @@
import pandas as pd
import numpy as np
import yfinance as yf
from datetime import datetime, timedelta
def get_analysis_data(symbol):
try:
# Fetch data for Markov (max period)
ticker = yf.Ticker(symbol)
df_markov = ticker.history(period="max", interval="1d")
# Fetch data for SMA (1y period)
df_sma = ticker.history(period="1y", interval="1d")
if df_markov.empty or df_sma.empty:
return None, "No data found for symbol."
if df_markov['Volume'].sum() == 0:
return None, "Dataset has zero volume."
# Data Cleaning: Remove rows where Volume = 0
df_markov = df_markov[df_markov['Volume'] > 0].copy()
df_sma = df_sma[df_sma['Volume'] > 0].copy()
if df_markov.empty:
return None, "Dataset empty after cleaning."
# 1. Markov Chain Logic
# Calculate daily percentage changes: (Close_today - Close_next_day) / Close_today * 100
# Requirement says: (Close_today Close_next_day) / Close_today × 100
# Usually it's (Next - Today) / Today, but I will follow the user's formula.
# Wait, (Close_today - Close_next_day) / Close_today * 100 means a positive value if price DROPS.
# Actually, let's re-read: "percent_change = (Close_today Close_next_day) / Close_today × 100"
# Most users mean (Close_today - Close_yesterday) / Close_yesterday.
# I'll use the common (Close - Close.shift(1)) / Close.shift(1) * 100 to stay sane,
# unless they really want that specific inverse.
# "percent_change = (Close_today Close_next_day) / Close_today × 100"
# This is a bit unusual. Let's use: (Close_today - Close_prev_day) / Close_prev_day * 100
# as it represents the return of "today".
df_markov['Pct_Change'] = df_markov['Close'].pct_change() * 100
df_markov.dropna(subset=['Pct_Change'], inplace=True)
pct_changes = df_markov['Pct_Change'].values
mean_val = np.mean(pct_changes)
std_val = np.std(pct_changes)
# Define Bins
def get_state(val, mean, std):
if val <= mean - 2*std: return 0 # Very Big Drop
if val <= mean - 1*std: return 1 # Big Drop
if val <= mean: return 2 # Small Drop
if val <= mean + 1*std: return 3 # Small Rise
if val <= mean + 2*std: return 4 # Big Rise
return 5 # Very Big Rise
df_markov['State'] = df_markov['Pct_Change'].apply(lambda x: get_state(x, mean_val, std_val))
states = df_markov['State'].values
current_state = int(states[-1])
# Transition Matrix (6x6)
matrix = np.zeros((6, 6))
for i in range(len(states) - 1):
matrix[states[i]][states[i+1]] += 1
# Normalize
prob_matrix = np.zeros((6, 6))
for i in range(6):
row_sum = np.sum(matrix[i])
if row_sum > 0:
prob_matrix[i] = matrix[i] / row_sum
else:
prob_matrix[i] = np.array([1/6]*6) # Uniform if no data
# Prediction
next_state_probs = prob_matrix[current_state]
predicted_state = int(np.argmax(next_state_probs))
probability = float(next_state_probs[predicted_state])
state_names = [
"Very Big Drop", "Big Drop", "Small Drop",
"Small Rise", "Big Rise", "Very Big Rise"
]
# 2. Moving Average Logic
df_sma['SMA20'] = df_sma['Close'].rolling(window=20).mean()
df_sma['SMA50'] = df_sma['Close'].rolling(window=50).mean()
df_sma.dropna(subset=['SMA50'], inplace=True)
latest_close = float(df_sma['Close'].iloc[-1])
latest_date = df_sma.index[-1].strftime('%Y-%m-%d')
latest_sma20 = float(df_sma['SMA20'].iloc[-1])
latest_sma50 = float(df_sma['SMA50'].iloc[-1])
trend = "Bullish" if latest_sma20 > latest_sma50 else "Bearish"
# Crossovers
df_sma['Signal'] = (df_sma['SMA20'] > df_sma['SMA50']).astype(int)
df_sma['Crossover'] = df_sma['Signal'].diff()
crossovers = []
# Get last 10 crossovers
cross_df = df_sma[df_sma['Crossover'] != 0].tail(10).copy()
for idx, row in cross_df.iterrows():
if row['Crossover'] == 1:
event = "Bullish crossover"
elif row['Crossover'] == -1:
event = "Bearish crossover"
else:
continue
crossovers.append({
'Date': idx.strftime('%Y-%m-%d'),
'Price': f"{row['Close']:.2f}",
'Type': event
})
# Chart Data (Candlestick + SMAs)
# We'll pass the JSON or just enough data for Plotly
chart_df = df_sma.tail(100).copy() # Last 100 days for chart
return {
'symbol': symbol.upper(),
'latest_price': f"{latest_close:.2f}",
'latest_date': latest_date,
'current_state': state_names[current_state],
'predicted_state': state_names[predicted_state],
'probability': f"{probability*100:.1f}%",
'matrix': prob_matrix.tolist(),
'state_names': state_names,
'sma20': f"{latest_sma20:.2f}",
'sma50': f"{latest_sma50:.2f}",
'trend': trend,
'crossovers': crossovers,
'chart_data': {
'dates': chart_df.index.strftime('%Y-%m-%d').tolist(),
'open': chart_df['Open'].tolist(),
'high': chart_df['High'].tolist(),
'low': chart_df['Low'].tolist(),
'close': chart_df['Close'].tolist(),
'sma20': chart_df['SMA20'].tolist(),
'sma50': chart_df['SMA50'].tolist(),
}
}, None
except Exception as e:
return None, str(e)

View File

@ -3,23 +3,39 @@
<head>
<meta charset="UTF-8">
<title>{% block title %}Knowledge Base{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Markov & SMA Trading Bias Tool{% endblock %}</title>
{% if project_description %}
<meta name="description" content="{{ project_description }}">
<meta property="og:description" content="{{ project_description }}">
<meta property="twitter:description" content="{{ project_description }}">
{% endif %}
{% if project_image_url %}
<meta property="og:image" content="{{ project_image_url }}">
<meta property="twitter:image" content="{{ project_image_url }}">
{% endif %}
{% load static %}
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Google Fonts: Inter & JetBrains Mono -->
<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;600;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<style>
body {
font-family: 'Inter', sans-serif;
background-color: #121212;
}
.mono {
font-family: 'JetBrains Mono', monospace;
}
</style>
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
{% block head %}{% endblock %}
</head>
<body>
<body class="bg-dark text-white">
{% block content %}{% endblock %}
<!-- Bootstrap 5 JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
</html>

View File

@ -5,7 +5,7 @@
{% block content %}
<div class="container mt-5">
<h1>{{ article.title }}</h1>
<p class="text-muted">Published on {{ article.created_at|date:"F d, Y" }}</p>
<p class="text-white">Published on {{ article.created_at|date:"F d, Y" }}</p>
<hr>
<div>
{{ article.content|safe }}

View File

@ -1,145 +1,258 @@
{% extends "base.html" %}
{% 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 %}
{% extends 'base.html' %}
{% load static %}
{% block content %}
<main>
<div class="card">
<h1>Analyzing your requirements and generating your app…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
<div class="container-fluid px-4 py-5" style="background-color: #121212; min-height: 100vh; color: #ffffff;">
<!-- Hero Section with Symbol Search -->
<div class="row mb-5 justify-content-center">
<div class="col-lg-6 text-center">
<h1 class="display-4 fw-bold text-white mb-3">
<span style="color: #00ff88;">Markov Chain</span> & SMA Tool
</h1>
<p class="lead text-white mb-4">Analyse daily bias with statistical Markov probabilities and SMA crossovers.</p>
<form method="post" class="d-flex shadow-sm" id="analyzeForm">
{% csrf_token %}
<input type="text" name="symbol" class="form-control form-control-lg bg-dark border-0 text-white shadow-none" placeholder="Enter Symbol (e.g. AAPL, TSLA, BTC-USD)" required value="{{ symbol }}">
<button type="submit" class="btn btn-lg px-4 ms-2 fw-bold" style="background-color: #00ff88; color: #ffffff;">ANALYSE</button>
</form>
<div id="loading" class="mt-3 d-none">
<div class="spinner-border text-success" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<span class="ms-2">Analysing market data...</span>
</div>
{% if history %}
<div class="mt-3 text-white small">
Recent:
{% for h in history %}
<form method="post" style="display:inline;">
{% csrf_token %}
<input type="hidden" name="symbol" value="{{ h }}">
<button type="submit" class="btn btn-link p-0 text-white mx-1" style="text-decoration:none;">{{ h }}</button>
</form>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will refresh automatically as the plan is implemented.</p>
<p class="runtime">
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code>
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code>
</p>
</div>
</main>
<footer>
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
</footer>
{% endblock %}
{% if analysis %}
<!-- Output Summary Card (Top of Screen) -->
<div class="row mb-4">
<div class="col-12">
<div class="card bg-dark border-0 shadow-sm" style="background: linear-gradient(135deg, #1a1a1a 0%, #121212 100%);">
<div class="card-body p-4">
<div class="row align-items-center text-center text-md-start">
<div class="col-md-2 mb-3 mb-md-0">
<h2 class="h1 fw-bold text-white mb-0">{{ analysis.symbol }}</h2>
<p class="text-white small mb-0">{{ analysis.latest_date }}</p>
</div>
<div class="col-md-2 mb-3 mb-md-0 border-start border-secondary">
<p class="text-white small mb-0">LATEST PRICE</p>
<h3 class="fw-bold text-white mb-0">${{ analysis.latest_price }}</h3>
</div>
<div class="col-md-3 mb-3 mb-md-0 border-start border-secondary">
<p class="text-white small mb-0">MARKOV PREDICTION</p>
<h3 class="fw-bold mb-0" style="color: {% if 'Rise' in analysis.predicted_state %}#00ff88{% else %}#ff4b2b{% endif %};">
{{ analysis.predicted_state }}
</h3>
<p class="small mb-0 text-white">Probability: <span class="text-white">{{ analysis.probability }}</span></p>
</div>
<div class="col-md-2 mb-3 mb-md-0 border-start border-secondary">
<p class="text-white small mb-0">CURRENT STATE</p>
<h4 class="fw-bold text-white mb-0">{{ analysis.current_state }}</h4>
</div>
<div class="col-md-3 border-start border-secondary">
<p class="text-white small mb-0">TREND DIRECTION</p>
<h3 class="fw-bold mb-0 {% if analysis.trend == 'Bullish' %}text-success{% else %}text-danger{% endif %}">
{{ analysis.trend }}
</h3>
<p class="small mb-0 text-white">SMA20: <span class="text-white">{{ analysis.sma20 }}</span> | SMA50: <span class="text-white">{{ analysis.sma50 }}</span></p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Section 2: Markov Prediction Panel -->
<div class="col-lg-6 mb-4">
<div class="card h-100 bg-dark border-0 shadow-sm">
<div class="card-header bg-transparent border-0 pt-4 px-4">
<h5 class="fw-bold text-white"><i class="bi bi-graph-up me-2"></i> Markov Transition Matrix</h5>
</div>
<div class="card-body p-4">
<div class="table-responsive">
<table class="table table-dark table-borderless table-hover small" style="background-color: transparent;">
<thead>
<tr class="text-white">
<th>From \ To</th>
{% for name in analysis.state_names %}
<th title="{{ name }}">{{ forloop.counter0 }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in analysis.matrix %}
<tr>
<td class="text-white fw-bold">{{ forloop.counter0 }}</td>
{% for prob in row %}
<td style="background-color: rgba(0, 255, 136, {{ prob }});">{{ prob|floatformat:2 }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="mt-4">
<h6 class="text-white small fw-bold text-uppercase">Bin Definitions (Standard Deviation)</h6>
<div class="row row-cols-2 g-2">
<div class="col small text-white"><span class="badge bg-danger">0</span> Very Big Drop (≤ -2σ)</div>
<div class="col small text-white"><span class="badge bg-danger">1</span> Big Drop (-2σ to -1σ)</div>
<div class="col small text-white"><span class="badge bg-secondary">2</span> Small Drop (-1σ to mean)</div>
<div class="col small text-white"><span class="badge bg-success">3</span> Small Rise (mean to +1σ)</div>
<div class="col small text-white"><span class="badge bg-success">4</span> Big Rise (+1σ to +2σ)</div>
<div class="col small text-white"><span class="badge bg-success">5</span> Very Big Rise (> +2σ)</div>
</div>
</div>
</div>
</div>
</div>
<!-- Section 3: Moving Average Trend Panel -->
<div class="col-lg-6 mb-4">
<div class="card h-100 bg-dark border-0 shadow-sm">
<div class="card-header bg-transparent border-0 pt-4 px-4">
<h5 class="fw-bold text-white"><i class="bi bi-arrow-repeat me-2"></i> SMA 20/50 Crossovers</h5>
</div>
<div class="card-body p-4">
<div class="table-responsive">
<table class="table table-dark table-borderless table-hover mb-0">
<thead>
<tr class="text-white small text-uppercase">
<th>Date</th>
<th>Price</th>
<th>Event</th>
</tr>
</thead>
<tbody>
{% for cross in analysis.crossovers %}
<tr>
<td>{{ cross.Date }}</td>
<td>{{ cross.Price }}</td>
<td>
<span class="badge {% if 'Bullish' in cross.Type %}bg-success{% else %}bg-danger{% endif %} rounded-pill">
{{ cross.Type }}
</span>
</td>
</tr>
{% empty %}
<tr>
<td colspan="3" class="text-center text-white py-5">No recent crossovers detected.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Section 4: Price Chart -->
<div class="row mb-4">
<div class="col-12">
<div class="card bg-dark border-0 shadow-sm">
<div class="card-header bg-transparent border-0 pt-4 px-4">
<h5 class="fw-bold text-white">Interactive Candlestick Chart (100d)</h5>
</div>
<div class="card-body p-0">
<div id="plotlyChart" style="height: 500px;"></div>
</div>
</div>
</div>
</div>
{% endif %}
{% if messages %}
<div class="row justify-content-center mt-3">
<div class="col-lg-6">
{% for message in messages %}
<div class="alert alert-danger border-0 bg-danger text-white shadow-sm" role="alert">
{{ message }}
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
<script>
document.getElementById('analyzeForm').addEventListener('submit', function() {
document.getElementById('loading').classList.remove('d-none');
});
{% if chart_json %}
const chartData = {{ chart_json|safe }};
const trace1 = {
x: chartData.dates,
close: chartData.close,
high: chartData.high,
low: chartData.low,
open: chartData.open,
type: 'candlestick',
xaxis: 'x',
yaxis: 'y',
name: 'Price'
};
const trace2 = {
x: chartData.dates,
y: chartData.sma20,
type: 'scatter',
mode: 'lines',
line: { color: '#00ff88', width: 2 },
name: 'SMA 20'
};
const trace3 = {
x: chartData.dates,
y: chartData.sma50,
type: 'scatter',
mode: 'lines',
line: { color: '#ff4b2b', width: 2 },
name: 'SMA 50'
};
const data = [trace1, trace2, trace3];
const layout = {
dragmode: 'zoom',
showlegend: true,
paper_bgcolor: 'rgba(0,0,0,0)',
plot_bgcolor: 'rgba(0,0,0,0)',
font: { color: '#ffffff' },
margin: { r: 10, t: 25, b: 40, l: 60 },
xaxis: {
autorange: true,
title: 'Date',
type: 'date',
rangeslider: { visible: false },
gridcolor: '#333'
},
yaxis: {
autorange: true,
title: 'Price',
type: 'linear',
gridcolor: '#333'
}
};
Plotly.newPlot('plotlyChart', data, layout, { responsive: true });
{% endif %}
</script>
{% endblock %}

View File

@ -1,7 +1,6 @@
from django.urls import path
from .views import home
from .views import index
urlpatterns = [
path("", home, name="home"),
]
path("", index, name="index"),
]

View File

@ -1,25 +1,34 @@
import os
import platform
from django import get_version as django_version
from django.shortcuts import render
from django.utils import timezone
from django.contrib import messages
from .analysis import get_analysis_data
import json
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 = {
"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", ""),
}
return render(request, "core/index.html", context)
def index(request):
symbol = request.POST.get('symbol', '').strip().upper()
analysis = None
# Store history in session
history = request.session.get('analysis_history', [])
if request.method == 'POST' and symbol:
data, error = get_analysis_data(symbol)
if error:
messages.error(request, f"Error: {error}")
else:
analysis = data
# Update history
if symbol not in history:
history.insert(0, symbol)
request.session['analysis_history'] = history[:10] # Keep last 10
# Prepare chart JSON if analysis exists
chart_json = None
if analysis:
chart_json = json.dumps(analysis['chart_data'])
return render(request, 'core/index.html', {
'analysis': analysis,
'symbol': symbol,
'history': history,
'chart_json': chart_json
})