mirror of
https://github.com/RomySaputraSihananda/ares.git
synced 2026-07-27 18:47:53 +00:00
9716fc0955
- Linear design system (dark canvas, lavender accent) - Live account stats, open positions, pending orders (30s polling) - Trade history with equity curve chart - Backtest results table - Proxy API routes — MT5_BASE_URL server-side only, never exposed to browser - Deploy on Vercel: set MT5_BASE_URL env var Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
103 lines
2.9 KiB
TypeScript
103 lines
2.9 KiB
TypeScript
// Server-side only — bridge URL never leaves this file
|
|
|
|
async function apiFetch<T>(path: string): Promise<T> {
|
|
const base = process.env.MT5_BASE_URL ?? "http://localhost:8000";
|
|
const res = await fetch(`${base}${path}`, {
|
|
next: { revalidate: 0 }, // always fresh
|
|
});
|
|
if (!res.ok) throw new Error(`MT5 bridge ${path} → ${res.status}`);
|
|
return res.json();
|
|
}
|
|
|
|
// ── Types ────────────────────────────────────────────────────────────────────
|
|
|
|
export interface AccountInfo {
|
|
login: number;
|
|
balance: number;
|
|
equity: number;
|
|
profit: number;
|
|
margin: number;
|
|
margin_free: number;
|
|
margin_level: number;
|
|
currency: string;
|
|
leverage: number;
|
|
name: string;
|
|
server: string;
|
|
}
|
|
|
|
export interface Position {
|
|
ticket: number;
|
|
symbol: string;
|
|
type: number; // 0=buy, 1=sell
|
|
volume: number;
|
|
price_open: number;
|
|
price_current: number;
|
|
sl: number;
|
|
tp: number;
|
|
profit: number;
|
|
swap: number;
|
|
comment: string;
|
|
magic: number;
|
|
time: string;
|
|
}
|
|
|
|
export interface PendingOrder {
|
|
ticket: number;
|
|
symbol: string;
|
|
type: number; // 2=BUY_LIMIT, 3=SELL_LIMIT, 4=BUY_STOP, 5=SELL_STOP
|
|
volume_initial: number;
|
|
volume_current: number;
|
|
price_open: number;
|
|
price_current: number;
|
|
sl: number;
|
|
tp: number;
|
|
magic: number;
|
|
comment: string;
|
|
time_setup: string;
|
|
}
|
|
|
|
export interface Deal {
|
|
ticket: number;
|
|
order: number;
|
|
time: string;
|
|
type: number; // 0=buy, 1=sell, 2=balance
|
|
entry: number; // 0=in, 1=out
|
|
symbol: string;
|
|
volume: number;
|
|
price: number;
|
|
commission: number;
|
|
swap: number;
|
|
profit: number;
|
|
comment: string;
|
|
magic: number;
|
|
}
|
|
|
|
// ── Wrappers ──────────────────────────────────────────────────────────────────
|
|
|
|
interface DataVec<T> { data: T[]; count: number }
|
|
|
|
export async function getAccount(): Promise<AccountInfo> {
|
|
const w = await apiFetch<DataVec<AccountInfo>>("/account");
|
|
const item = w.data[0];
|
|
if (!item) throw new Error("empty /account response");
|
|
return item;
|
|
}
|
|
|
|
export async function getPositions(): Promise<Position[]> {
|
|
const w = await apiFetch<DataVec<Position>>("/positions");
|
|
return w.data;
|
|
}
|
|
|
|
export async function getOrders(symbol?: string): Promise<PendingOrder[]> {
|
|
const path = symbol ? `/orders?symbol=${encodeURIComponent(symbol)}` : "/orders";
|
|
const w = await apiFetch<DataVec<PendingOrder>>(path);
|
|
return w.data;
|
|
}
|
|
|
|
export async function getDeals(dateFrom: string, dateTo: string, symbol?: string): Promise<Deal[]> {
|
|
let path = `/history/deals?date_from=${encodeURIComponent(dateFrom)}&date_to=${encodeURIComponent(dateTo)}`;
|
|
if (symbol) path += `&symbol=${encodeURIComponent(symbol)}`;
|
|
const w = await apiFetch<DataVec<Deal>>(path);
|
|
return w.data;
|
|
}
|