Files
ares/web/lib/mt5.ts
T
romysaputrasihananda 9716fc0955 feat(web): add Next.js portfolio with live MT5 dashboard
- 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>
2026-06-11 14:51:55 +07:00

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;
}