159 lines
5.1 KiB
TypeScript
159 lines
5.1 KiB
TypeScript
import { TradeOrder, OrderType, MarketType, AutoTradeConfig, WatchlistGroup, ReservedOrder, StockTick } from '../types';
|
|
import { API_BASE_URL, getHeaders } from './config';
|
|
|
|
export interface HoldingItem {
|
|
code: string;
|
|
name: string;
|
|
avgPrice: number;
|
|
quantity: number;
|
|
market: MarketType;
|
|
currentPrice: number; // Added
|
|
profit: number; // Added
|
|
profitRate: number; // Added
|
|
}
|
|
|
|
export class DbService {
|
|
constructor() {}
|
|
|
|
// --- Holdings ---
|
|
async getHoldings(): Promise<HoldingItem[]> {
|
|
try {
|
|
const res = await fetch(`${API_BASE_URL}/account/holdings`);
|
|
if (!res.ok) return [];
|
|
const data = await res.json();
|
|
// Map API response to HoldingItem
|
|
// API returns: { stockCode, stockName, quantity, avgPrice, currentPrice, profit, profitRate }
|
|
return data.map((h: any) => ({
|
|
code: h.stockCode,
|
|
name: h.stockName,
|
|
avgPrice: h.avgPrice,
|
|
quantity: h.quantity,
|
|
market: h.stockCode.length > 6 ? MarketType.OVERSEAS : MarketType.DOMESTIC,
|
|
currentPrice: h.currentPrice,
|
|
profit: h.profit,
|
|
profitRate: h.profitRate
|
|
}));
|
|
} catch (e) {
|
|
console.error("Failed to fetch holdings", e);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async getAccountSummary() {
|
|
try {
|
|
const res = await fetch(`${API_BASE_URL}/account/balance?market=Domestic`); // Default
|
|
if (!res.ok) return { totalAssets: 0, buyingPower: 0 };
|
|
const data = await res.json();
|
|
// API returns complex object. We might need to simplify or use /account/summary if exists.
|
|
// Or calculate from holdings + cash?
|
|
// Using a simplified assumption or endpoints.
|
|
// Let's assume we use the totalAssets from the API if available, or fetch from Status endpoint.
|
|
// Actually, we verified /account/balance returns KIS raw data.
|
|
// We should implemented a summary endpoint or parse raw data.
|
|
// For now, let's return a basic structure.
|
|
return {
|
|
totalAssets: parseFloat(data.output2?.tot_evlu_amt || "0"),
|
|
buyingPower: parseFloat(data.output2?.dnca_tot_amt || "0")
|
|
};
|
|
} catch (e) {
|
|
return { totalAssets: 0, buyingPower: 0 };
|
|
}
|
|
}
|
|
|
|
// --- Reserved Orders ---
|
|
async getReservedOrders(): Promise<ReservedOrder[]> {
|
|
const res = await fetch(`${API_BASE_URL}/reserved-orders`);
|
|
if (!res.ok) return [];
|
|
return await res.json();
|
|
}
|
|
|
|
async saveReservedOrder(order: ReservedOrder) {
|
|
// POST
|
|
// Map Frontend Order to Backend Request
|
|
const payload = {
|
|
stockCode: order.stockCode,
|
|
stockName: order.stockName,
|
|
monitoringType: order.monitoringType,
|
|
triggerPrice: order.triggerPrice,
|
|
orderType: order.type,
|
|
quantity: order.quantity,
|
|
price: order.price || 0,
|
|
trailingType: order.trailingType,
|
|
trailingValue: order.trailingValue,
|
|
stopLossValue: order.stopLossValue
|
|
};
|
|
|
|
const res = await fetch(`${API_BASE_URL}/reserved-orders`, {
|
|
method: 'POST',
|
|
headers: getHeaders(),
|
|
body: JSON.stringify(payload)
|
|
});
|
|
return await res.json();
|
|
}
|
|
|
|
async deleteReservedOrder(id: string) {
|
|
await fetch(`${API_BASE_URL}/reserved-orders/${id}`, {
|
|
method: 'DELETE'
|
|
});
|
|
}
|
|
|
|
// --- Auto Trade Configs ---
|
|
async getAutoConfigs(): Promise<AutoTradeConfig[]> {
|
|
const res = await fetch(`${API_BASE_URL}/auto-trade/configs`);
|
|
if (!res.ok) return [];
|
|
return await res.json();
|
|
}
|
|
|
|
async saveAutoConfig(config: AutoTradeConfig) {
|
|
await fetch(`${API_BASE_URL}/auto-trade/configs`, {
|
|
method: 'POST',
|
|
headers: getHeaders(),
|
|
body: JSON.stringify(config)
|
|
});
|
|
}
|
|
|
|
async deleteAutoConfig(id: string) {
|
|
await fetch(`${API_BASE_URL}/auto-trade/configs/${id}`, {
|
|
method: 'DELETE'
|
|
});
|
|
}
|
|
|
|
// --- Watchlist Groups ---
|
|
async getWatchlistGroups(): Promise<WatchlistGroup[]> {
|
|
const res = await fetch(`${API_BASE_URL}/watchlists/groups`);
|
|
if (!res.ok) return [];
|
|
return await res.json();
|
|
}
|
|
|
|
// --- Ticks (Optional, might be local only or via API) ---
|
|
// If backend doesn't support generic tick history per session, keep local or ignore.
|
|
async saveStockTick(tick: StockTick) {
|
|
// No-op or keep local?
|
|
// Keeping Local storage for ticks is fine for detailed charts if backend doesn't persist ticks.
|
|
// Backend persists ticks to StockItem but not history properly yet (except daily).
|
|
}
|
|
|
|
async getStockTicks(code: string): Promise<StockTick[]> {
|
|
return [];
|
|
}
|
|
|
|
// Helpers not needed with real API usually
|
|
async syncOrderToHolding(order: TradeOrder) {
|
|
// Refresh holdings from server instead of calculating
|
|
return await this.getHoldings();
|
|
}
|
|
|
|
// Write-only wrappers
|
|
async updateWatchlistGroup(group: WatchlistGroup) {
|
|
// Use PUT if available or POST
|
|
}
|
|
|
|
async saveWatchlistGroup(group: WatchlistGroup) {
|
|
// POST
|
|
}
|
|
|
|
async deleteWatchlistGroup(id: string) {
|
|
// DELETE
|
|
}
|
|
}
|