51b8bb8f8a
Co-authored-by: Cursor <cursoragent@cursor.com>
151 lines
3.5 KiB
TypeScript
151 lines
3.5 KiB
TypeScript
const TOKEN_KEY = "eth_hedge_token";
|
|
const USER_KEY = "eth_hedge_user";
|
|
|
|
export function getApiBase(): string {
|
|
return window.location.origin;
|
|
}
|
|
|
|
export function getToken(): string | null {
|
|
return localStorage.getItem(TOKEN_KEY);
|
|
}
|
|
|
|
export function setSession(token: string, username: string) {
|
|
localStorage.setItem(TOKEN_KEY, token);
|
|
localStorage.setItem(USER_KEY, username);
|
|
}
|
|
|
|
export function clearSession() {
|
|
localStorage.removeItem(TOKEN_KEY);
|
|
localStorage.removeItem(USER_KEY);
|
|
localStorage.removeItem("eth_hedge_api_base");
|
|
}
|
|
|
|
export function getUsername(): string | null {
|
|
return localStorage.getItem(USER_KEY);
|
|
}
|
|
|
|
export async function apiFetch<T>(
|
|
path: string,
|
|
options: RequestInit = {},
|
|
): Promise<T> {
|
|
const base = getApiBase();
|
|
const headers = new Headers(options.headers || {});
|
|
if (!headers.has("Content-Type") && options.body) {
|
|
headers.set("Content-Type", "application/json");
|
|
}
|
|
const token = getToken();
|
|
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
|
|
const res = await fetch(`${base}${path}`, { ...options, headers });
|
|
if (res.status === 401) {
|
|
clearSession();
|
|
throw new Error("unauthorized");
|
|
}
|
|
const text = await res.text();
|
|
let data: unknown = null;
|
|
try {
|
|
data = text ? JSON.parse(text) : null;
|
|
} catch {
|
|
data = { detail: text };
|
|
}
|
|
if (!res.ok) {
|
|
const detail =
|
|
typeof data === "object" && data && "detail" in data
|
|
? String((data as { detail: unknown }).detail)
|
|
: res.statusText;
|
|
throw new Error(detail || `HTTP ${res.status}`);
|
|
}
|
|
return data as T;
|
|
}
|
|
|
|
export type LoginResult = {
|
|
token: string;
|
|
username: string;
|
|
expires_in: number;
|
|
env_name: string;
|
|
mode: string;
|
|
};
|
|
|
|
export async function login(username: string, password: string) {
|
|
return apiFetch<LoginResult>("/api/auth/login", {
|
|
method: "POST",
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
}
|
|
|
|
export async function changeCredentials(input: {
|
|
current_password: string;
|
|
new_username: string;
|
|
new_password: string;
|
|
}) {
|
|
return apiFetch<LoginResult>("/api/auth/change-credentials", {
|
|
method: "POST",
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export type MarketSnapshot = {
|
|
connected: boolean;
|
|
updated_at_ms: number | null;
|
|
index_px: number | null;
|
|
pair: {
|
|
expiry_ymd: string;
|
|
strike: number;
|
|
call_inst_id: string;
|
|
put_inst_id: string;
|
|
} | null;
|
|
perp: Quote | null;
|
|
call: Quote | null;
|
|
put: Quote | null;
|
|
ask_compare: {
|
|
call_ask: number | null;
|
|
put_ask: number | null;
|
|
bias: string;
|
|
};
|
|
};
|
|
|
|
type Quote = {
|
|
inst_id: string;
|
|
bid: number | null;
|
|
ask: number | null;
|
|
bid_sz: number | null;
|
|
ask_sz: number | null;
|
|
mark_px: number | null;
|
|
};
|
|
|
|
export type PlanState = {
|
|
running: boolean;
|
|
phase: string;
|
|
rounds_done: number;
|
|
max_rounds: number;
|
|
window_key: string | null;
|
|
rest_left_sec: number;
|
|
rest_seconds: number;
|
|
exit_move_points: number;
|
|
can_open: boolean;
|
|
last_error: string | null;
|
|
position: {
|
|
has_position: boolean;
|
|
group_id?: string;
|
|
perp_side?: string;
|
|
option_side?: string;
|
|
perp_upl?: number;
|
|
option_upl?: number;
|
|
index_px?: number | null;
|
|
entry_index_px?: number;
|
|
move_points?: number;
|
|
initial_premium?: number;
|
|
premium_gap?: number;
|
|
};
|
|
ledger: { equity: number; available: number; reserved: number };
|
|
};
|
|
|
|
export type StrategySettings = {
|
|
fee_rate: number;
|
|
exit_move_points: number;
|
|
rest_seconds: number;
|
|
max_rounds: number;
|
|
initial_equity: number;
|
|
ledger: { equity: number; available: number };
|
|
};
|