Skip to content

Commit 2f0aa68

Browse files
committed
feat(webui): trading signal panel, predict-future mode, dark theme & bug fixes
## Summary Major improvements to the Kronos Web UI focusing on usability, visual clarity, and a new real-time trading signal analysis panel. ## New Features ### Predict Future Now - Added a dedicated 'Predict Future Now' button that uses the most recent 400 candles (lookback) to forecast the next 120 periods beyond the last data point — i.e., a true future forecast, not a backtest. - Future timestamps are generated programmatically via pd.date_range so the model always forecasts into the uncharted future. - No 'actual data' orange trace is shown in this mode (the future hasn't happened yet). ### Trading Signal Analysis Panel - After every prediction a full trading signal panel is displayed below the chart, automatically computed from the prediction output. - Signal categories: STRONG BUY / BUY / NEUTRAL / CAUTION / DO NOT BUY, each with a distinct color scheme. - Panel includes six metric cards: Current Price, Predicted Final Price (%), Predicted High, Predicted Low, Predicted Mean, Support / Resistance. - Trajectory timeline: Now → [Nd: +X%] → [Nd: +X%] → [End: +X%]. - Operations panel: Entry, Stop-Loss (-3%), Target 1, Target 2, Timeframe. ### Dark Theme & Chart Color Overhaul - Chart background switched to plotly_dark with dark paper/plot bg. - Historical candles: gray (#78909C / #546E7A) — neutral context. - Prediction candles: blue (#1E88E5 / #1565C0) — clearly distinguishable. - Actual/backtest candles: orange (#F57C00 / #BF360C) — easy comparison. ### Windows Launcher (start.ps1) - One-command launcher: auto-detects the Python installation with CUDA support by iterating all python.exe in PATH and testing torch.cuda.is_available(). - Falls back to CPU if no CUDA Python is found. - Deactivates any active venv to prevent using the wrong Python. - Generates a random port (10000-60000), checks availability via Get-NetTCPConnection, then auto-opens the browser after 3 s. - Supports -SkipInstall flag to skip pip install on repeat launches. ### Data Download Utility (download_data.py) - CLI utility to download market data (crypto, stocks, forex) via yfinance and save to the Kronos data/ directory in the expected CSV format. - Supports all yfinance intervals (1m, 5m, 15m, 1h, 4h, 1d, 1wk, 1mo). - Usage: python download_data.py BTC-USD --interval 4h --period 2y ## Bug Fixes ### actual_data showing wrong price range in predict-future mode (critical) - Root cause: the else branch in the actual_data section always fetched df.iloc[0:lookback+pred_len] regardless of mode, so in predict-future mode (no start_date) it served the very first candles in the file (e.g. 2024 XRP at ~.50 instead of current ~.30), rendering an orange trace in a completely wrong price band. - Fix: actual_data is now mode-aware: - predict_future=True → actual_df = None (no trace) - start_date provided → actual_df from time_range_df[lookback:lookback+pred_len] - neither → no actual data ### historical_start_idx wrong in predict-future mode - Root cause: historical_start_idx defaulted to 0, so the chart showed candles from the very beginning of the file (years-old data) as the 'historical' context window instead of the last 400 candles. - Fix: when predict_future=True, historical_start_idx = len(df) - lookback. ## Localization - All user-visible text translated from Chinese/Portuguese to English. - html lang attribute updated to 'en'. - Date formatting locale updated to en-US. - .gitignore updated to exclude data/ and webui/prediction_results/ (runtime artifacts).
1 parent 67b630e commit 2f0aa68

5 files changed

Lines changed: 620 additions & 62 deletions

File tree

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,9 @@ venv.bak/
7474
temp/
7575
tmp/
7676
.python-version
77+
78+
# Market data files (downloaded at runtime)
79+
data/
80+
81+
# WebUI runtime output
82+
webui/prediction_results/

download_data.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""
2+
Kronos - Downloader de dados de mercado
3+
Baixa dados de qualquer ativo (cripto, acoes, forex) e salva em data/
4+
para uso no Web UI.
5+
6+
Uso:
7+
python download_data.py XRP-USD # XRP/USD - diario
8+
python download_data.py BTC-USD --interval 1h
9+
python download_data.py AAPL --interval 1d --period 2y
10+
python download_data.py ETH-USD --interval 5m --period 60d
11+
python download_data.py BTC-USD ETH-USD XRP-USD # multiplos de uma vez
12+
13+
Intervalos suportados: 1m 2m 5m 15m 30m 60m 90m 1h 1d 5d 1wk 1mo
14+
Periodos suportados : 1d 5d 1mo 3mo 6mo 1y 2y 5y 10y ytd max
15+
(para intervalos < 1h, o maximo e 60 dias)
16+
"""
17+
18+
import argparse
19+
import os
20+
import sys
21+
import pandas as pd
22+
import yfinance as yf
23+
24+
DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
25+
26+
27+
def download(ticker: str, interval: str = "1d", period: str = "2y") -> None:
28+
print(f" Baixando {ticker} intervalo={interval} periodo={period} ...")
29+
30+
df = yf.download(ticker, interval=interval, period=period, progress=False, auto_adjust=True)
31+
32+
if df.empty:
33+
print(f" [ERRO] Nenhum dado retornado para '{ticker}'. Verifique o simbolo.")
34+
return
35+
36+
# Flatten MultiIndex columns (yfinance retorna assim para tickers unicos as vezes)
37+
if isinstance(df.columns, pd.MultiIndex):
38+
df.columns = [col[0].lower() for col in df.columns]
39+
else:
40+
df.columns = [c.lower() for c in df.columns]
41+
42+
# Renomear colunas para o padrao Kronos
43+
rename = {"adj close": "close"}
44+
df = df.rename(columns=rename)
45+
46+
# Garantir colunas obrigatorias
47+
required = ["open", "high", "low", "close"]
48+
missing = [c for c in required if c not in df.columns]
49+
if missing:
50+
print(f" [ERRO] Colunas faltando apos download: {missing}. Colunas presentes: {list(df.columns)}")
51+
return
52+
53+
# Resetar index e renomear coluna de data/hora
54+
df = df.reset_index()
55+
time_col = df.columns[0] # 'Datetime' ou 'Date'
56+
df = df.rename(columns={time_col: "timestamps"})
57+
df["timestamps"] = pd.to_datetime(df["timestamps"])
58+
59+
# Remover timezone para evitar problemas de serializacao
60+
if df["timestamps"].dt.tz is not None:
61+
df["timestamps"] = df["timestamps"].dt.tz_convert("UTC").dt.tz_localize(None)
62+
63+
# Manter apenas colunas uteis
64+
keep = ["timestamps", "open", "high", "low", "close"]
65+
if "volume" in df.columns:
66+
keep.append("volume")
67+
df = df[keep].dropna()
68+
69+
# Nome do arquivo: TICKER_interval.csv
70+
safe_ticker = ticker.replace("/", "-").replace("=", "")
71+
filename = f"{safe_ticker}_{interval}.csv"
72+
out_path = os.path.join(DATA_DIR, filename)
73+
74+
os.makedirs(DATA_DIR, exist_ok=True)
75+
df.to_csv(out_path, index=False)
76+
77+
size_kb = os.path.getsize(out_path) / 1024
78+
print(f" [OK] {filename} ({len(df)} candles, {size_kb:.1f} KB) -> data/{filename}")
79+
80+
81+
def main():
82+
parser = argparse.ArgumentParser(
83+
description="Baixa dados de mercado e salva em data/ para uso no Kronos Web UI."
84+
)
85+
parser.add_argument("tickers", nargs="+", help="Simbolos yfinance (ex: XRP-USD BTC-USD AAPL)")
86+
parser.add_argument("--interval", default="1d",
87+
help="Intervalo das velas (default: 1d). Ex: 5m 1h 1d")
88+
parser.add_argument("--period", default="2y",
89+
help="Periodo historico (default: 2y). Ex: 60d 1y 5y max")
90+
args = parser.parse_args()
91+
92+
print(f"\nKronos Data Downloader")
93+
print(f"Destino: {DATA_DIR}\n")
94+
95+
for ticker in args.tickers:
96+
download(ticker.upper(), interval=args.interval, period=args.period)
97+
98+
print(f"\nConcluido! Reinicie o servidor ou recarregue a pagina para ver os novos arquivos.")
99+
100+
101+
if __name__ == "__main__":
102+
main()

start.ps1

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Kronos - Startup Script
2+
# Starts the Web UI on a random port, using Python with CUDA support
3+
4+
param(
5+
[switch]$SkipInstall
6+
)
7+
8+
$ErrorActionPreference = "Stop"
9+
10+
Write-Host "======================================" -ForegroundColor Cyan
11+
Write-Host " Kronos - Startup" -ForegroundColor Cyan
12+
Write-Host "======================================" -ForegroundColor Cyan
13+
Write-Host ""
14+
15+
# If a venv is active, deactivate it to avoid using the wrong Python
16+
if ($env:VIRTUAL_ENV) {
17+
Write-Host "[INFO] Deactivating venv '$env:VIRTUAL_ENV' ..." -ForegroundColor Yellow
18+
& deactivate 2>$null
19+
# Manually clear venv variables from this session's PATH
20+
$env:PATH = ($env:PATH -split ';' | Where-Object { $_ -notlike "$env:VIRTUAL_ENV*" }) -join ';'
21+
Remove-Item Env:\VIRTUAL_ENV -ErrorAction SilentlyContinue
22+
Remove-Item Env:\VIRTUAL_ENV_PROMPT -ErrorAction SilentlyContinue
23+
}
24+
25+
# Find Python with CUDA support (search all instances in PATH)
26+
$pythonExe = $null
27+
$candidatos = (where.exe python 2>$null) -split "`n" | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" -and (Test-Path $_) }
28+
29+
foreach ($cand in $candidatos) {
30+
$result = & $cand -c "import torch; print(torch.cuda.is_available())" 2>$null
31+
if ($result -eq "True") {
32+
$pythonExe = $cand
33+
break
34+
}
35+
}
36+
37+
if (-not $pythonExe) {
38+
# Fallback: accept CPU if no CUDA found
39+
$pythonExe = (Get-Command python -ErrorAction SilentlyContinue)?.Source
40+
if (-not $pythonExe) {
41+
Write-Host "[ERROR] Python not found in PATH. Install Python 3.10+ and try again." -ForegroundColor Red
42+
exit 1
43+
}
44+
Write-Host "[WARNING] Python with CUDA not found. Using CPU: $pythonExe" -ForegroundColor Yellow
45+
Write-Host " Para CUDA, instale: pip install torch --index-url https://download.pytorch.org/whl/cu128" -ForegroundColor Yellow
46+
} else {
47+
$pyVersion = & $pythonExe --version 2>&1
48+
$gpuName = & $pythonExe -c "import torch; print(torch.cuda.get_device_name(0))" 2>&1
49+
Write-Host "[OK] $pyVersion | CUDA - $gpuName" -ForegroundColor Green
50+
Write-Host "[OK] Python: $pythonExe" -ForegroundColor Green
51+
}
52+
53+
# Project root directory
54+
$projectRoot = $PSScriptRoot
55+
Set-Location $projectRoot
56+
57+
# Install dependencies
58+
if (-not $SkipInstall) {
59+
Write-Host "[...] Installing project dependencies ..." -ForegroundColor Yellow
60+
& $pythonExe -m pip install -r (Join-Path $projectRoot "requirements.txt") --quiet
61+
62+
$webuiReqs = Join-Path $projectRoot "webui\requirements.txt"
63+
if (Test-Path $webuiReqs) {
64+
Write-Host "[...] Installing Web UI dependencies ..." -ForegroundColor Yellow
65+
& $pythonExe -m pip install -r $webuiReqs --quiet
66+
}
67+
Write-Host "[OK] Dependencies installed." -ForegroundColor Green
68+
} else {
69+
Write-Host "[SKIP] Dependency installation skipped (-SkipInstall)." -ForegroundColor Yellow
70+
}
71+
72+
# Generate random port between 10000 and 60000 (avoids well-known ports)
73+
$port = Get-Random -Minimum 10000 -Maximum 60000
74+
for ($i = 0; $i -lt 10; $i++) {
75+
if (-not (Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue)) { break }
76+
$port = Get-Random -Minimum 10000 -Maximum 60000
77+
}
78+
79+
Write-Host ""
80+
Write-Host "======================================" -ForegroundColor Cyan
81+
Write-Host " Kronos Web UI" -ForegroundColor Cyan
82+
Write-Host " Port : $port" -ForegroundColor Cyan
83+
Write-Host " URL : http://localhost:$port" -ForegroundColor Cyan
84+
Write-Host "======================================" -ForegroundColor Cyan
85+
Write-Host "Press Ctrl+C to stop the server." -ForegroundColor DarkGray
86+
Write-Host ""
87+
88+
# Open browser automatically after a few seconds
89+
Start-Job -ScriptBlock {
90+
param($url)
91+
Start-Sleep -Seconds 3
92+
Start-Process $url
93+
} -ArgumentList "http://localhost:$port" | Out-Null
94+
95+
# Start Flask on the random port
96+
Set-Location (Join-Path $projectRoot "webui")
97+
$env:FLASK_APP = "app.py"
98+
99+
& $pythonExe -c @"
100+
import sys, os
101+
sys.path.insert(0, '..')
102+
from webui.app import app
103+
app.run(debug=False, host='127.0.0.1', port=$port)
104+
"@

0 commit comments

Comments
 (0)