67 lines
2.6 KiB
JavaScript
67 lines
2.6 KiB
JavaScript
const BASE = '/api/portfolio';
|
|
const FETCH_TIMEOUT_MS = 15000; // 15s timeout per request
|
|
|
|
async function req(method, path, body) {
|
|
const controller = new AbortController();
|
|
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
const opts = {
|
|
method,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
signal: controller.signal,
|
|
};
|
|
if (body !== undefined) opts.body = JSON.stringify(body);
|
|
try {
|
|
const res = await fetch(`${BASE}${path}`, opts);
|
|
clearTimeout(timeout);
|
|
if (!res.ok) {
|
|
const err = await res.json().catch(() => ({}));
|
|
throw new Error(err.detail || `请求失败: ${res.status}`);
|
|
}
|
|
if (res.status === 204) return null;
|
|
return res.json();
|
|
} catch (e) {
|
|
clearTimeout(timeout);
|
|
if (e.name === 'AbortError') throw new Error('请求超时,请检查网络连接');
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
export const portfolioApi = {
|
|
// Watchlist
|
|
getWatchlist: () => req('GET', '/watchlist'),
|
|
addToWatchlist: (ticker, name) => req('POST', '/watchlist', { ticker, name }),
|
|
removeFromWatchlist: (ticker) => req('DELETE', `/watchlist/${ticker}`),
|
|
|
|
// Accounts
|
|
getAccounts: () => req('GET', '/accounts'),
|
|
createAccount: (name) => req('POST', '/accounts', { account_name: name }),
|
|
deleteAccount: (name) => req('DELETE', `/accounts/${name}`),
|
|
|
|
// Positions
|
|
getPositions: (account) => req('GET', `/positions${account ? `?account=${encodeURIComponent(account)}` : ''}`),
|
|
addPosition: (data) => req('POST', '/positions', data),
|
|
removePosition: (ticker, positionId, account) => {
|
|
const params = new URLSearchParams({ ticker });
|
|
if (positionId) params.set('position_id', positionId);
|
|
if (account) params.set('account', account);
|
|
return req('DELETE', `/positions/${ticker}?${params}`);
|
|
},
|
|
exportPositions: (account) => {
|
|
const controller = new AbortController();
|
|
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
const url = `${BASE}/positions/export${account ? `?account=${encodeURIComponent(account)}` : ''}`;
|
|
return fetch(url, { signal: controller.signal })
|
|
.then(r => { clearTimeout(timeout); return r; })
|
|
.then(r => { if (!r.ok) throw new Error(`导出失败: ${r.status}`); return r.blob(); })
|
|
.catch(e => { clearTimeout(timeout); if (e.name === 'AbortError') throw new Error('请求超时'); throw e; });
|
|
},
|
|
|
|
// Recommendations
|
|
getRecommendations: (date) =>
|
|
req('GET', `/recommendations${date ? `?date=${date}` : ''}`),
|
|
getRecommendation: (date, ticker) => req('GET', `/recommendations/${date}/${ticker}`),
|
|
|
|
// Batch analysis
|
|
startAnalysis: () => req('POST', '/analyze'),
|
|
};
|