Compare commits

...

1 Commits

Author SHA1 Message Date
Flatlogic Bot
c255bd35c4 1 2025-12-05 05:27:33 +00:00
7 changed files with 758 additions and 144 deletions

16
api/helpers.php Normal file
View File

@ -0,0 +1,16 @@
<?php
function get_api_credentials() {
$key_file = __DIR__ . '/../.keys/api_credentials.json';
if (!file_exists($key_file)) {
return null;
}
$creds_json = file_get_contents($key_file);
return json_decode($creds_json, true);
}
function sign_request($secret, $payload) {
return hash_hmac('sha256', json_encode($payload), $secret);
}
?>

118
api/proxy.php Normal file
View File

@ -0,0 +1,118 @@
<?php
header('Content-Type: application/json');
require_once __DIR__ . '/helpers.php';
// Basic rate limiting (implement a more robust solution in production)
// session_start();
// $request_limit = 10; // 10 requests
// $time_period = 60; // per minute
// $time = time();
// if (!isset($_SESSION['request_count'])) {
// $_SESSION['request_count'] = 0;
// $_SESSION['last_request_time'] = $time;
// }
// if ($time - $_SESSION['last_request_time'] > $time_period) {
// $_SESSION['request_count'] = 0;
// $_SESSION['last_request_time'] = $time;
// }
// $_SESSION['request_count']++;
// if ($_SESSION['request_count'] > $request_limit) {
// http_response_code(429);
// echo json_encode(['error' => 'Too Many Requests']);
// exit;
// }
$creds = get_api_credentials();
if (!$creds) {
http_response_code(401);
echo json_encode(['error' => 'API key not configured.']);
exit;
}
$request_body = json_decode(file_get_contents('php://input'), true);
if (!$request_body || !isset($request_body['type'])) {
http_response_code(400);
echo json_encode(['error' => 'Invalid request body.']);
exit;
}
$api_url = 'https://api.hyperliquid.xyz/info';
// Prepare payload for Hyperliquid
$action = $request_body;
$nonce = (int)(microtime(true) * 1000);
$payload_to_sign = [
'T' => $action['type'],
];
// Construct signature payload based on action type
switch ($action['type']) {
case 'query':
$signature_payload = [
'type' => $action['type'],
'query' => $action['query'],
'nonce' => $nonce,
];
break;
case 'order':
$signature_payload = [
'type' => 'order',
'orders' => $action['orders'],
'nonce' => $nonce,
];
break;
default:
$signature_payload = [
'type' => $action['type'],
'nonce' => $nonce,
];
break;
}
$signature = hash_hmac('sha256', json_encode($signature_payload), $creds['secret']);
$payload = [
'action' => $action,
'nonce' => $nonce,
'signature' => base64_encode($signature)
];
$ch = curl_init($api_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
if ($curl_error) {
http_response_code(500);
echo json_encode(['error' => 'cURL Error: ' . $curl_error]);
exit;
}
if ($http_code >= 400) {
http_response_code($http_code);
// Forward Hyperliquid's error response if available
$error_response = json_decode($response, true);
if ($error_response) {
echo json_encode($error_response);
} else {
echo json_encode(['error' => 'Received status code: ' . $http_code]);
}
exit;
}
echo $response;
?>

53
api/save_key.php Normal file
View File

@ -0,0 +1,53 @@
<?php
header('Content-Type: application/json');
// Basic security check: ensure it's a POST request
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405); // Method Not Allowed
echo json_encode(['success' => false, 'error' => 'Invalid request method.']);
exit;
}
$input = file_get_contents('php://input');
$data = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE || !isset($data['apiKey']) || !isset($data['apiSecret'])) {
http_response_code(400); // Bad Request
echo json_encode(['success' => false, 'error' => 'Invalid JSON or missing credentials.']);
exit;
}
$apiKey = $data['apiKey'];
$apiSecret = $data['apiSecret'];
// In a real application, you MUST encrypt this data.
// For this example, we'll store it in a JSON file in a protected directory.
$storageDir = __DIR__ . '/../.keys'; // Store outside of web root if possible, or protect with .htaccess
if (!is_dir($storageDir)) {
if (!mkdir($storageDir, 0750, true)) {
http_response_code(500); // Internal Server Error
echo json_encode(['success' => false, 'error' => 'Failed to create storage directory.']);
exit;
}
}
// Add a .htaccess file to the directory to deny direct access
if (!file_exists($storageDir . '/.htaccess')) {
file_put_contents($storageDir . '/.htaccess', 'deny from all');
}
$credentials = [
'apiKey' => $apiKey,
'apiSecret' => $apiSecret, // Again, ENCRYPT THIS in a real app
];
$filePath = $storageDir . '/credentials.json';
if (file_put_contents($filePath, json_encode($credentials, JSON_PRETTY_PRINT))) {
chmod($filePath, 0640); // Set restrictive permissions
echo json_encode(['success' => true]);
} else {
http_response_code(500); // Internal Server Error
echo json_encode(['success' => false, 'error' => 'Failed to save credentials.']);
}

40
api/sign.js Normal file
View File

@ -0,0 +1,40 @@
const { Wallet } = require('ethers');
// This is the verifying contract address for Arbitrum from the documentation
const HYPERLIQUID_EXCHANGE_ADDRESS = '0xaf88d065e77c8cC2239327C5EDb3A432268e5831';
const HYPERLIQUID_CHAIN_ID = 1337;
async function sign() {
const [,, privateKey, typedDataJson] = process.argv;
if (!privateKey || !typedDataJson) {
console.error('Usage: node sign.js <privateKey> <typedDataJson>');
process.exit(1);
}
try {
const wallet = new Wallet(privateKey);
const typedData = JSON.parse(typedDataJson);
const domain = {
name: 'Hyperliquid Exchange',
version: '1',
chainId: HYPERLIQUID_CHAIN_ID,
verifyingContract: HYPERLIQUID_EXCHANGE_ADDRESS,
};
const signature = await wallet.signTypedData(
domain,
typedData.types,
typedData.message
);
console.log(signature);
} catch (error) {
console.error('Error signing payload:', error);
process.exit(1);
}
}
sign();

225
assets/css/custom.css Normal file
View File

@ -0,0 +1,225 @@
:root {
--bg-dark: #1a1a1a;
--surface-dark: #2c2c2c;
--text-dark: #e0e0e0;
--border-dark: #444;
--bg-light: #f0f0f0;
--surface-light: #ffffff;
--text-light: #212529;
--border-light: #dee2e6;
--primary: #00f0ff;
--secondary: #ff00ff;
--success: #00ff7f;
--danger: #ff4d4d;
--font-family-monospace: Menlo, Monaco, 'Courier New', monospace;
}
#user-state-container {
grid-area: user-state;
background-color: var(--terminal-card-bg);
padding: 1rem;
border-radius: 0.25rem;
}
#user-state-data {
font-size: 0.8rem;
white-space: pre-wrap;
word-break: break-all;
}
/* Dark and Light theme styles */
body {
transition: background-color 0.3s, color 0.3s;
}
body.dark-theme {
background-color: var(--bg-dark);
color: var(--text-dark);
}
body.light-theme {
background-color: var(--bg-light);
color: var(--text-light);
}
.trading-terminal-card {
background-color: var(--surface-dark);
border-radius: 4px;
padding: 16px;
overflow-y: auto;
}
body.light-theme .trading-terminal-card {
background-color: var(--surface-light);
}
.trading-terminal {
display: grid;
grid-template-areas:
"header header header"
"user-state user-state user-state"
"order-entry chart order-book"
"positions positions positions";
grid-template-columns: 280px 1fr 280px;
grid-template-rows: auto auto 1fr auto;
gap: 1rem;
padding: 1rem;
height: 100vh;
}
.terminal-header { grid-area: header; }
.order-entry { grid-area: order-entry; }
.chart { grid-area: chart; }
.order-book { grid-area: order-book; }
.positions {
grid-area: positions;
}
/* Data Feed Styles */
.order-book-data, .trade-feed-data {
font-size: 0.8rem;
height: 100%;
overflow-y: auto;
max-height: 300px; /* Or adjust as needed */
}
.order-book-header, .trade-row, .ask-row, .bid-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
padding: 2px 5px;
}
.trade-feed-data .trade-row {
grid-template-columns: 1fr 1fr 1fr;
}
.order-book-header {
font-weight: bold;
color: var(--text-color-muted);
}
.ask-row > div:first-child {
color: var(--color-danger);
}
.bid-row > div:first-child {
color: var(--color-success);
}
.trade-row.buy > div:first-child {
color: var(--color-success);
}
.trade-row.sell > div:first-child {
color: var(--color-danger);
}
.brand-title {
color: var(--primary);
font-weight: bold;
}
.brand-title span {
color: var(--secondary);
}
.form-label {
font-size: 0.8rem;
margin-bottom: 0.25rem;
}
.leverage-slider-container .value {
color: var(--primary);
}
.btn-success {
background-color: var(--success);
border-color: var(--success);
color: #000;
}
.btn-danger {
background-color: var(--danger);
border-color: var(--danger);
}
.placeholder-text {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 1.5rem;
color: #888;
}
.modal {
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.7);
display: flex; /* Use flexbox for centering */
align-items: center;
justify-content: center;
}
.modal-content {
background-color: var(--surface-dark);
padding: 24px;
border: 1px solid var(--border-dark);
border-radius: 4px;
width: 80%;
max-width: 500px;
position: relative;
animation-name: fadeIn;
animation-duration: 0.3s;
}
@keyframes fadeIn {
from {opacity: 0}
to {opacity: 1}
}
.close-btn {
color: #aaa;
position: absolute;
top: 10px;
right: 20px;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close-btn:hover,
.close-btn:focus {
color: var(--text-dark);
text-decoration: none;
cursor: pointer;
}
.modal .form-group {
margin-bottom: 16px;
}
.modal label {
display: block;
margin-bottom: 8px;
}
.modal input[type="password"] {
width: 100%;
background-color: var(--bg-dark);
color: var(--text-dark);
border: 1px solid var(--border-dark);
padding: 8px;
border-radius: 4px;
}

192
assets/js/main.js Normal file
View File

@ -0,0 +1,192 @@
document.addEventListener('DOMContentLoaded', () => {
// Theme Toggle
const themeToggle = document.getElementById('theme-toggle');
if (themeToggle) {
const currentTheme = localStorage.getItem('theme') || 'dark-theme';
document.body.classList.add(currentTheme);
themeToggle.textContent = currentTheme === 'dark-theme' ? 'Light Mode' : 'Dark Mode';
themeToggle.addEventListener('click', () => {
if (document.body.classList.contains('dark-theme')) {
document.body.classList.replace('dark-theme', 'light-theme');
localStorage.setItem('theme', 'light-theme');
themeToggle.textContent = 'Dark Mode';
} else {
document.body.classList.replace('light-theme', 'dark-theme');
localStorage.setItem('theme', 'dark-theme');
themeToggle.textContent = 'Light Mode';
}
});
}
// Leverage Slider
const leverageSlider = document.getElementById('leverage');
const leverageValue = document.getElementById('leverage-value');
if (leverageSlider && leverageValue) {
leverageValue.textContent = leverageSlider.value;
leverageSlider.addEventListener('input', () => {
leverageValue.textContent = leverageSlider.value;
});
}
// API Key Modal
const apiKeyModal = document.getElementById('api-key-modal');
const apiKeyModalBtn = document.getElementById('api-key-modal-btn');
if (apiKeyModal && apiKeyModalBtn) {
const closeBtn = apiKeyModal.querySelector('.close-btn');
const apiKeyForm = document.getElementById('api-key-form');
const apiKeyStatus = document.getElementById('api-key-status');
apiKeyModalBtn.addEventListener('click', () => {
apiKeyModal.style.display = 'flex';
});
if (closeBtn) {
closeBtn.addEventListener('click', () => {
apiKeyModal.style.display = 'none';
});
}
window.addEventListener('click', (event) => {
if (event.target == apiKeyModal) {
apiKeyModal.style.display = 'none';
}
});
if (apiKeyForm) {
apiKeyForm.addEventListener('submit', (event) => {
event.preventDefault();
const apiKey = document.getElementById('api-key').value;
const apiSecret = document.getElementById('api-secret').value;
fetch('api/save_key.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ apiKey, apiSecret }),
})
.then(response => response.json())
.then(data => {
if (data.success) {
apiKeyStatus.textContent = 'API Key saved successfully!';
apiKeyStatus.style.color = 'var(--success)';
setTimeout(() => { apiKeyModal.style.display = 'none'; }, 2000);
} else {
apiKeyStatus.textContent = `Error: ${data.error}`;
apiKeyStatus.style.color = 'var(--danger)';
}
})
.catch(error => {
apiKeyStatus.textContent = `Error: ${error.message}`;
apiKeyStatus.style.color = 'var(--danger)';
});
});
}
}
// User State Button
const getUserStateButton = document.getElementById('getUserStateButton');
const userStateContainer = document.getElementById('user-state-container');
const userStateData = document.getElementById('user-state-data');
if (getUserStateButton && userStateContainer && userStateData) {
getUserStateButton.addEventListener('click', () => {
fetch('api/proxy.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ type: 'clearinghouseState' }),
})
.then(response => {
if (response.status === 401) {
alert('API Key not configured. Please add it via the API Key button.');
return null;
}
return response.json();
})
.then(data => {
if (data) {
userStateData.textContent = JSON.stringify(data, null, 2);
userStateContainer.style.display = 'block';
}
})
.catch(error => {
console.error('Error fetching user state:', error);
alert('An error occurred while fetching user state.');
});
});
}
// WebSocket Connection
connectWebSocket();
});
function connectWebSocket() {
const ws = new WebSocket('wss://api.hyperliquid.xyz/ws');
ws.onopen = () => {
console.log('Connected to Hyperliquid WebSocket API');
ws.send(JSON.stringify({ "method": "subscribe", "subscription": { "type": "l2Book", "coin": "ETH" } }));
ws.send(JSON.stringify({ "method": "subscribe", "subscription": { "type": "trades", "coin": "ETH" } }));
};
ws.onmessage = (event) => {
const response = JSON.parse(event.data);
if (response.channel === 'l2Book' && response.data) {
updateOrderBook(response.data);
}
if (response.channel === 'trades' && response.data) {
updateTradeFeed(response.data);
}
};
ws.onerror = (error) => {
console.error('WebSocket Error:', error);
};
ws.onclose = () => {
console.log('WebSocket connection closed. Reconnecting...');
setTimeout(connectWebSocket, 5000);
};
}
function updateOrderBook(data) {
const orderBookData = document.getElementById('order-book-data');
if (!orderBookData) return;
const asks = data.levels[1].slice(0, 8).reverse();
const bids = data.levels[0].slice(0, 8);
let html = '<div class="order-book-column">';
html += '<div class="order-book-header"><div>Price (USD)</div><div>Size</div></div>';
asks.forEach(ask => {
html += `<div class="ask-row"><div>${parseFloat(ask.px).toFixed(2)}</div><div>${parseFloat(ask.sz).toFixed(4)}</div></div>`;
});
bids.forEach(bid => {
html += `<div class="bid-row"><div>${parseFloat(bid.px).toFixed(2)}</div><div>${parseFloat(bid.sz).toFixed(4)}</div></div>`;
});
html += '</div>';
orderBookData.innerHTML = html;
}
function updateTradeFeed(trades) {
const tradeFeedData = document.getElementById('trade-feed-data');
if (!tradeFeedData) return;
const maxTrades = 15;
trades.forEach(trade => {
const tradeElement = document.createElement('div');
tradeElement.className = `trade-row ${trade.side === 'B' ? 'buy' : 'sell'}`;
const time = new Date(trade.time).toLocaleTimeString();
const price = parseFloat(trade.px).toFixed(2);
const size = parseFloat(trade.sz).toFixed(4);
tradeElement.innerHTML = `<div>${price}</div><div>${size}</div><div>${time}</div>`;
tradeFeedData.prepend(tradeElement);
});
while (tradeFeedData.children.length > maxTrades) {
tradeFeedData.removeChild(tradeFeedData.lastChild);
}
}

256
index.php
View File

@ -1,150 +1,120 @@
<?php <!DOCTYPE html>
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Style</title> <title>26zxHyperliquid</title>
<?php <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
// Read project preview data from environment <link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? ''; <script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<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);
}
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;
top: 0;
left: 0;
width: 100%;
height: 100%;
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: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
</head> </head>
<body> <body>
<main>
<div class="card"> <div class="trading-terminal">
<h1>Analyzing your requirements and generating your website…</h1> <header class="trading-terminal-card terminal-header d-flex justify-content-between align-items-center">
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes"> <h1 class="brand-title fs-4 mb-0">26zx<span>Hyperliquid</span></h1>
<span class="sr-only">Loading…</span> <button id="theme-toggle">Toggle Theme</button>
</div> <button id="api-key-modal-btn">API Key</button>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p> <button id="getUserStateButton" class="btn btn-sm btn-info ms-2">Get User State</button>
<p class="hint">This page will update automatically as the plan is implemented.</p> </header>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
<div id="user-state-container" class="trading-terminal-card" style="display: none;">
<h5 class="mb-3">User State</h5>
<pre id="user-state-data"></pre>
</div>
<div id="api-key-modal" class="modal" style="display: none;">
<div class="modal-content">
<span class="close-btn">&times;</span>
<h2>API Key Management</h2>
<form id="api-key-form">
<div class="form-group">
<label for="api-key">API Key</label>
<input type="password" id="api-key" name="api-key" required>
</div>
<div class="form-group">
<label for="api-secret">API Secret</label>
<input type="password" id="api-secret" name="api-secret" required>
</div>
<button type="submit">Save</button>
</form>
<div id="api-key-status"></div>
</div>
</div>
<aside class="trading-terminal-card order-entry">
<h5 class="mb-3">Order Entry</h5>
<form>
<div class="mb-2">
<label for="coin" class="form-label">Coin</label>
<select class="form-select form-select-sm" id="coin">
<option selected>ETH</option>
<option>BTC</option>
<option>SOL</option>
</select>
</div>
<div class="btn-group btn-group-sm w-100 mb-3" role="group">
<input type="radio" class="btn-check" name="order-type" id="limit-order" autocomplete="off" checked>
<label class="btn btn-outline-primary" for="limit-order">Limit</label>
<input type="radio" class="btn-check" name="order-type" id="market-order" autocomplete="off">
<label class="btn btn-outline-primary" for="market-order">Market</label>
</div>
<div class="mb-2">
<label for="price" class="form-label">Price (USD)</label>
<input type="number" class="form-control form-control-sm" id="price" placeholder="3000.00">
</div>
<div class="mb-3">
<label for="amount" class="form-label">Amount</label>
<input type="number" class="form-control form-control-sm" id="amount" placeholder="0.1">
</div>
<div class="mb-3 leverage-slider-container">
<label for="leverage" class="form-label">Leverage: <span class="value" id="leverage-value">10</span>x</label>
<input type="range" class="form-range" min="1" max="100" step="1" value="10" id="leverage">
</div>
<div class="d-grid gap-2">
<button type="button" class="btn btn-sm btn-success">Buy / Long</button>
<button type="button" class="btn btn-sm btn-danger">Sell / Short</button>
</div>
</form>
</aside>
<main class="trading-terminal-card chart" id="tv_chart_container">
</main>
<aside id="order-book-container" class="trading-terminal-card order-book">
<h5 class="mb-3">Order Book</h5>
<div id="order-book-data" class="order-book-data"></div>
</aside>
<footer id="trade-feed-container" class="trading-terminal-card positions">
<h5 class="mb-3">Recent Trades</h5>
<div id="trade-feed-data" class="trade-feed-data"></div>
</footer>
</div> </div>
</main>
<footer> <script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
Page updated: <?= htmlspecialchars($now) ?> (UTC) <script type="text/javascript">
</footer> new TradingView.widget(
{
"autosize": true,
"symbol": "BITSTAMP:BTCUSD",
"interval": "D",
"timezone": "Etc/UTC",
"theme": "dark",
"style": "1",
"locale": "en",
"enable_publishing": false,
"allow_symbol_change": true,
"container_id": "tv_chart_container"
}
);
</script>
</body> </body>
</html> </html>