uodated charting

This commit is contained in:
KS Jannette
2026-02-22 17:10:24 -05:00
parent bde189d8f5
commit 84c2a99456
13 changed files with 756 additions and 715 deletions

1
.gitignore vendored
View File

@@ -6,6 +6,7 @@
*.key *.key
*.env *.env
*secrets.js *secrets.js
secrets.js
trahn-trade-frontend/src/auth/secrets.js* trahn-trade-frontend/src/auth/secrets.js*
# ============================================ # ============================================

View File

@@ -5,8 +5,6 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TRAHN Grid Trader</title> <title>TRAHN Grid Trader</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=Space+Grotesk:wght@400;600;700&display=swap" rel="stylesheet">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@@ -1,9 +1,9 @@
:root { :root {
--bg-primary: #0d1117; --bg-primary: #000000;
--bg-secondary: #161b22; --bg-secondary: #070808;
--bg-tertiary: #21262d; --bg-tertiary: #363737;
--text-primary: #e6edf3; --text-primary: #01ff3c;
--text-secondary: #8b949e; --text-secondary: #01ff3c;
--text-muted: #484f58; --text-muted: #484f58;
--accent: #58a6ff; --accent: #58a6ff;
--green: #3fb950; --green: #3fb950;
@@ -22,7 +22,8 @@
} }
body { body {
font-family: 'Space Grotesk', -apple-system, BlinkMacSystemFont, sans-serif; font-family: 'Ac437_Rainbow100_re_132', monospace;
font-size: 18px;
background: var(--bg-primary); background: var(--bg-primary);
color: var(--text-primary); color: var(--text-primary);
min-height: 100vh; min-height: 100vh;
@@ -35,21 +36,27 @@ body {
} }
header { header {
display: flex;
flex-direction: column;
text-align: center; text-align: center;
margin-right: auto;
margin-left: auto;
margin-bottom: 2rem; margin-bottom: 2rem;
} }
.ascii-logo { .logo {
font-family: 'JetBrains Mono', monospace; margin-left: 32px;
font-size: 0.65rem; font-family: 'Ac437_Rainbow100_re_132', monospace;
line-height: 1.1; font-size: 9rem;
color: var(--accent); font-weight: 700;
margin: 0; color: var(--text-primary);
display: inline-block; text-align: center;
letter-spacing: 0.25em;
text-shadow: 0 10 30px rgba(88, 166, 255, 0.3);
} }
.subtitle { .subtitle {
font-family: 'JetBrains Mono', monospace; font-family: 'Ac437_Rainbow100_re_132', monospace;
font-size: 0.875rem; font-size: 0.875rem;
color: var(--text-secondary); color: var(--text-secondary);
letter-spacing: 0.05em; letter-spacing: 0.05em;
@@ -96,15 +103,16 @@ header {
} }
.date-label { .date-label {
display: block; font-family: 'Ac437_Rainbow100_re_132';
font-size: 1.25rem; font-weight: 600;
font-size: 1.45rem;
font-weight: 600; font-weight: 600;
color: var(--text-primary); color: var(--text-primary);
} }
.date-status { .date-status {
display: block; display: block;
font-family: 'JetBrains Mono', monospace; font-family: 'Ac437_Rainbow100_re_132', monospace;
font-size: 0.75rem; font-size: 0.75rem;
color: var(--text-secondary); color: var(--text-secondary);
margin-top: 0.25rem; margin-top: 0.25rem;
@@ -121,8 +129,15 @@ header {
} }
@keyframes pulse { @keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; } 0%,
100% {
opacity: 1;
}
50% {
opacity: 0.4;
}
} }
/* Chart */ /* Chart */
@@ -145,7 +160,7 @@ header {
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 6px; border-radius: 6px;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
font-family: 'JetBrains Mono', monospace; font-family: 'Ac437_Rainbow100_re_132', monospace;
font-size: 0.75rem; font-size: 0.75rem;
pointer-events: none; pointer-events: none;
opacity: 0; opacity: 0;
@@ -184,7 +199,7 @@ header {
gap: 1rem; gap: 1rem;
border-radius: 12px; border-radius: 12px;
color: var(--text-secondary); color: var(--text-secondary);
font-family: 'JetBrains Mono', monospace; font-family: 'Ac437_Rainbow100_re_132', monospace;
z-index: 50; z-index: 50;
} }
@@ -198,12 +213,15 @@ header {
} }
@keyframes spin { @keyframes spin {
to { transform: rotate(360deg); } to {
transform: rotate(360deg);
}
} }
/* Stats */ /* Stats */
.stats { .stats {
display: flex; display: flex;
font-size: 1.2rem;
justify-content: space-between; justify-content: space-between;
gap: 1rem; gap: 1rem;
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
@@ -211,6 +229,7 @@ header {
.stat { .stat {
flex: 1; flex: 1;
font-size: 1.2rem;
background: var(--bg-secondary); background: var(--bg-secondary);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 8px; border-radius: 8px;
@@ -228,15 +247,26 @@ header {
} }
.stat-value { .stat-value {
font-family: 'JetBrains Mono', monospace; font-family: 'Ac437_Rainbow100_re_132', monospace;
font-size: 1.25rem; font-size: 1.25rem;
font-weight: 600; font-weight: 600;
} }
.stat-value.high { color: var(--green); } .stat-value.high {
.stat-value.low { color: var(--red); } color: var(--green);
.stat-value.buy { color: var(--yellow); } }
.stat-value.sell { color: var(--orange); }
.stat-value.low {
color: var(--red);
}
.stat-value.buy {
color: var(--yellow);
}
.stat-value.sell {
color: var(--orange);
}
/* Day indicators */ /* Day indicators */
.day-indicators { .day-indicators {
@@ -276,6 +306,7 @@ header {
.legend-item { .legend-item {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 1rem;
gap: 0.5rem; gap: 0.5rem;
} }
@@ -307,7 +338,7 @@ header {
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 6px; border-radius: 6px;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
font-family: 'JetBrains Mono', monospace; font-family: 'Ac437_Rainbow100_re_132', monospace;
font-size: 0.75rem; font-size: 0.75rem;
color: var(--text-secondary); color: var(--text-secondary);
} }
@@ -363,7 +394,7 @@ header {
.grid-levels-header { .grid-levels-header {
text-align: center; text-align: center;
font-size: 0.875rem; font-size: 1.2rem;
font-weight: 600; font-weight: 600;
color: var(--text-secondary); color: var(--text-secondary);
margin-bottom: 1rem; margin-bottom: 1rem;
@@ -385,7 +416,7 @@ header {
} }
.grid-column-header { .grid-column-header {
font-size: 0.75rem; font-size: 1.2rem;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
@@ -395,18 +426,20 @@ header {
} }
.grid-column-header.buy { .grid-column-header.buy {
font-size: 1.2rem;
color: var(--yellow); color: var(--yellow);
background: rgba(240, 192, 0, 0.1); background: rgba(240, 192, 0, 0.1);
} }
.grid-column-header.sell { .grid-column-header.sell {
font-size: 1.2rem;
color: var(--orange); color: var(--orange);
background: rgba(219, 109, 40, 0.1); background: rgba(219, 109, 40, 0.1);
} }
.grid-level { .grid-level {
font-family: 'JetBrains Mono', monospace; font-family: 'Ac437_Rainbow100_re_132', monospace;
font-size: 0.8rem; font-size: 1.2rem;
padding: 0.25rem 0.75rem; padding: 0.25rem 0.75rem;
border-radius: 4px; border-radius: 4px;
min-width: 100px; min-width: 100px;
@@ -415,6 +448,7 @@ header {
.grid-level.buy { .grid-level.buy {
color: var(--yellow); color: var(--yellow);
font-size: 1.2rem;
background: rgba(240, 192, 0, 0.08); background: rgba(240, 192, 0, 0.08);
border: 1px solid rgba(240, 192, 0, 0.2); border: 1px solid rgba(240, 192, 0, 0.2);
} }
@@ -449,7 +483,7 @@ header {
text-align: center; text-align: center;
} }
.auth-card .ascii-logo { .auth-card .logo {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
@@ -588,6 +622,10 @@ header {
/* Logout Button */ /* Logout Button */
.logout-btn { .logout-btn {
height: 40px;
width: 110px;
font-size: 1.2rem;
font-family: Ac437_Rainbow100_re_132;
position: absolute; position: absolute;
top: 1rem; top: 1rem;
right: 1rem; right: 1rem;
@@ -596,7 +634,6 @@ header {
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 6px; border-radius: 6px;
color: var(--text-secondary); color: var(--text-secondary);
font-size: 0.875rem;
font-family: inherit; font-family: inherit;
cursor: pointer; cursor: pointer;
transition: border-color 0.2s, color 0.2s; transition: border-color 0.2s, color 0.2s;

View File

@@ -3,7 +3,7 @@
* Communicates with backend REST API * Communicates with backend REST API
*/ */
const API_BASE = 'http://localhost:3001/api'; const API_BASE = 'http://localhost:3001/v1';
export const tradingApi = { export const tradingApi = {
/** /**

View File

@@ -37,7 +37,6 @@ export function Chart({ prices, trades }) {
return () => window.removeEventListener('resize', updateDimensions); return () => window.removeEventListener('resize', updateDimensions);
}, []); }, []);
// Draw chart
useEffect(() => { useEffect(() => {
const canvas = canvasRef.current; const canvas = canvasRef.current;
if (!canvas || dimensions.width === 0) return; if (!canvas || dimensions.width === 0) return;
@@ -59,7 +58,7 @@ export function Chart({ prices, trades }) {
if (!prices || prices.length === 0) { if (!prices || prices.length === 0) {
ctx.fillStyle = COLORS.textMuted; ctx.fillStyle = COLORS.textMuted;
ctx.font = '14px JetBrains Mono, monospace'; ctx.font = '18px Ac437_Rainbow100_re_132, monospace';
ctx.textAlign = 'center'; ctx.textAlign = 'center';
ctx.fillText('No data for this day', width / 2, height / 2); ctx.fillText('No data for this day', width / 2, height / 2);
return; return;
@@ -108,7 +107,6 @@ export function Chart({ prices, trades }) {
ctx.stroke(); ctx.stroke();
} }
// Draw baseline
const baselineY = priceToY(baselinePrice); const baselineY = priceToY(baselinePrice);
ctx.strokeStyle = COLORS.baseline; ctx.strokeStyle = COLORS.baseline;
ctx.setLineDash([4, 4]); ctx.setLineDash([4, 4]);
@@ -118,7 +116,6 @@ export function Chart({ prices, trades }) {
ctx.stroke(); ctx.stroke();
ctx.setLineDash([]); ctx.setLineDash([]);
// Draw gradient fill
if (prices.length >= 2) { if (prices.length >= 2) {
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(timeToX(times[0]), baselineY); ctx.moveTo(timeToX(times[0]), baselineY);
@@ -139,7 +136,6 @@ export function Chart({ prices, trades }) {
ctx.fill(); ctx.fill();
} }
// Draw price line
if (prices.length >= 2) { if (prices.length >= 2) {
ctx.beginPath(); ctx.beginPath();
ctx.strokeStyle = COLORS.line; ctx.strokeStyle = COLORS.line;
@@ -154,9 +150,8 @@ export function Chart({ prices, trades }) {
ctx.stroke(); ctx.stroke();
} }
// Draw Y axis labels
ctx.fillStyle = COLORS.text; ctx.fillStyle = COLORS.text;
ctx.font = '11px JetBrains Mono, monospace'; ctx.font = '14px Ac437_Rainbow100_re_132, monospace';
ctx.textAlign = 'left'; ctx.textAlign = 'left';
for (let price = startPrice; price <= maxPrice; price += priceStep) { for (let price = startPrice; price <= maxPrice; price += priceStep) {
@@ -164,7 +159,6 @@ export function Chart({ prices, trades }) {
ctx.fillText(formatPrice(price), width - PADDING.right + 8, y + 4); ctx.fillText(formatPrice(price), width - PADDING.right + 8, y + 4);
} }
// Draw X axis labels
ctx.textAlign = 'center'; ctx.textAlign = 'center';
const hourMs = 60 * 60 * 1000; const hourMs = 60 * 60 * 1000;
const rangeMs = maxTime - minTime; const rangeMs = maxTime - minTime;
@@ -183,7 +177,6 @@ export function Chart({ prices, trades }) {
current += stepMs; current += stepMs;
} }
// Draw trades
for (const trade of (trades || [])) { for (const trade of (trades || [])) {
const x = timeToX(trade.t || trade.timestamp); const x = timeToX(trade.t || trade.timestamp);
const y = priceToY(trade.price); const y = priceToY(trade.price);
@@ -193,7 +186,6 @@ export function Chart({ prices, trades }) {
const color = trade.side === 'buy' ? COLORS.buy : COLORS.sell; const color = trade.side === 'buy' ? COLORS.buy : COLORS.sell;
const radius = 7; const radius = 7;
// Glow
ctx.beginPath(); ctx.beginPath();
ctx.arc(x, y, radius + 4, 0, Math.PI * 2); ctx.arc(x, y, radius + 4, 0, Math.PI * 2);
ctx.fillStyle = color + '33'; ctx.fillStyle = color + '33';
@@ -212,7 +204,6 @@ export function Chart({ prices, trades }) {
ctx.fill(); ctx.fill();
} }
// Draw current price tag
if (prices.length > 0) { if (prices.length > 0) {
const lastPrice = priceValues[priceValues.length - 1]; const lastPrice = priceValues[priceValues.length - 1];
const y = priceToY(lastPrice); const y = priceToY(lastPrice);
@@ -226,14 +217,13 @@ export function Chart({ prices, trades }) {
ctx.fill(); ctx.fill();
ctx.fillStyle = '#fff'; ctx.fillStyle = '#fff';
ctx.font = 'bold 11px JetBrains Mono, monospace'; ctx.font = 'bold 14px Ac437_Rainbow100_re_132, monospace';
ctx.textAlign = 'left'; ctx.textAlign = 'left';
ctx.fillText(formatPrice(lastPrice), x + 10, y + 4); ctx.fillText(formatPrice(lastPrice), x + 10, y + 4);
} }
}, [prices, trades, dimensions]); }, [prices, trades, dimensions]);
// Mouse handling for tooltip
const handleMouseMove = (e) => { const handleMouseMove = (e) => {
if (!prices || prices.length === 0) { if (!prices || prices.length === 0) {
setTooltip(null); setTooltip(null);

Binary file not shown.

Binary file not shown.

View File

@@ -1 +1,34 @@
/* Reset handled in App.css */ @font-face {
font-family: 'Ac437_Rainbow100_re_1323';
src: local('./fonts/Mx437_Compaq_Port3.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Ac437_Rainbow100_re_1323';
src: local('./fonts/Mx437_Compaq_Port3.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Px437_AMI_EGA_8x8';
src: local('Px437_AMI_EGA_8x8'), url(./fonts/Px437_AMI_EGA_8x8.ttf) format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Ac437_Rainbow100_re_132';
src: local('Ac437_Rainbow100_re_132'), url(./fonts/Ac437_Rainbow100_re_132.ttf) format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Ac437_Rainbow100_re_132';
src: local('Ac437_Rainbow100_re_132'), url(./fonts/Ac437_Rainbow100_re_132.ttf) format('truetype');
font-weight: normal;
font-style: normal;
}

View File

@@ -52,13 +52,7 @@ export function Dashboard() {
return ( return (
<div className="container"> <div className="container">
<header> <header>
<pre className="ascii-logo">{` <h1 className="logo">TRAHN</h1>
████████╗██████╗ █████╗ ██╗ ██╗███╗ ██╗
╚══██╔══╝██╔══██╗██╔══██╗██║ ██║████╗ ██║
██║ ██████╔╝███████║███████║██╔██╗ ██║
██║ ██╔══██╗██╔══██║██╔══██║██║╚██╗██║
██║ ██║ ██║██║ ██║██║ ██║██║ ╚████║
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝`}</pre>
<button className="logout-btn" onClick={logout}>Logout</button> <button className="logout-btn" onClick={logout}>Logout</button>
</header> </header>

View File

@@ -29,13 +29,7 @@ export function Login() {
return ( return (
<div className="auth-container"> <div className="auth-container">
<div className="auth-card"> <div className="auth-card">
<pre className="ascii-logo">{` <h1 className="logo">TRAHN</h1>
████████╗██████╗ █████╗ ██╗ ██╗███╗ ██╗
╚══██╔══╝██╔══██╗██╔══██╗██║ ██║████╗ ██║
██║ ██████╔╝███████║███████║██╔██╗ ██║
██║ ██╔══██╗██╔══██║██╔══██║██║╚██╗██║
██║ ██║ ██║██║ ██║██║ ██║██║ ╚████║
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝`}</pre>
<h2>Welcome Back</h2> <h2>Welcome Back</h2>

View File

@@ -38,13 +38,7 @@ export function Signup() {
return ( return (
<div className="auth-container"> <div className="auth-container">
<div className="auth-card"> <div className="auth-card">
<pre className="ascii-logo">{` <h1 className="logo">TRAHN</h1>
████████╗██████╗ █████╗ ██╗ ██╗███╗ ██╗
╚══██╔══╝██╔══██╗██╔══██╗██║ ██║████╗ ██║
██║ ██████╔╝███████║███████║██╔██╗ ██║
██║ ██╔══██╗██╔══██║██╔══██║██║╚██╗██║
██║ ██║ ██║██║ ██║██║ ██║██║ ╚████║
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝`}</pre>
{submitted ? ( {submitted ? (
<> <>