import { TradeOrder, OrderType, MarketType, AutoTradeConfig, WatchlistGroup, ReservedOrder, StockTick } from '../types'; export interface HoldingItem { code: string; name: string; avgPrice: number; quantity: number; market: MarketType; } export class DbService { private holdingsKey = 'batchukis_sqlite_holdings'; private configsKey = 'batchukis_sqlite_configs'; private watchlistGroupsKey = 'batchukis_sqlite_watchlist_groups'; private reservedOrdersKey = 'batchukis_sqlite_reserved_orders'; private ticksPrefix = 'batchukis_ticks_'; constructor() { this.initDatabase(); } private initDatabase() { if (!localStorage.getItem(this.holdingsKey)) { const initialHoldings: HoldingItem[] = [ { code: '005930', name: '삼성전자', avgPrice: 68500, quantity: 150, market: MarketType.DOMESTIC }, { code: 'AAPL', name: 'Apple Inc.', avgPrice: 175.20, quantity: 25, market: MarketType.OVERSEAS }, ]; localStorage.setItem(this.holdingsKey, JSON.stringify(initialHoldings)); } if (!localStorage.getItem(this.configsKey)) { localStorage.setItem(this.configsKey, JSON.stringify([])); } if (!localStorage.getItem(this.watchlistGroupsKey)) { const initialGroups: WatchlistGroup[] = [ { id: 'grp1', name: '핵심 우량주', codes: ['005930', '000660'], market: MarketType.DOMESTIC }, { id: 'grp2', name: 'AI 포트폴리오', codes: ['NVDA', 'TSLA'], market: MarketType.OVERSEAS }, { id: 'grp3', name: '미국 빅테크', codes: ['AAPL'], market: MarketType.OVERSEAS } ]; localStorage.setItem(this.watchlistGroupsKey, JSON.stringify(initialGroups)); } if (!localStorage.getItem(this.reservedOrdersKey)) { localStorage.setItem(this.reservedOrdersKey, JSON.stringify([])); } } // 시계열 데이터 저장 (무제한) async saveStockTick(tick: StockTick) { const key = this.ticksPrefix + tick.code; const existing = localStorage.getItem(key); const ticks: StockTick[] = existing ? JSON.parse(existing) : []; ticks.push(tick); localStorage.setItem(key, JSON.stringify(ticks)); } async getStockTicks(code: string): Promise { const key = this.ticksPrefix + code; const data = localStorage.getItem(key); return data ? JSON.parse(data) : []; } async getHoldings(): Promise { const data = localStorage.getItem(this.holdingsKey); return data ? JSON.parse(data) : []; } async syncOrderToHolding(order: TradeOrder) { const holdings = await this.getHoldings(); const existingIdx = holdings.findIndex(h => h.code === order.stockCode); if (order.type === OrderType.BUY) { if (existingIdx > -1) { const h = holdings[existingIdx]; const newQty = h.quantity + order.quantity; const newAvg = ((h.avgPrice * h.quantity) + (order.price * order.quantity)) / newQty; holdings[existingIdx] = { ...h, quantity: newQty, avgPrice: newAvg }; } else { holdings.push({ code: order.stockCode, name: order.stockName, avgPrice: order.price, quantity: order.quantity, market: order.stockCode.length > 6 ? MarketType.OVERSEAS : MarketType.DOMESTIC }); } } else { if (existingIdx > -1) { holdings[existingIdx].quantity -= order.quantity; if (holdings[existingIdx].quantity <= 0) { holdings.splice(existingIdx, 1); } } } localStorage.setItem(this.holdingsKey, JSON.stringify(holdings)); return holdings; } async getWatchlistGroups(): Promise { const data = localStorage.getItem(this.watchlistGroupsKey); return data ? JSON.parse(data) : []; } async saveWatchlistGroup(group: WatchlistGroup) { const groups = await this.getWatchlistGroups(); groups.push(group); localStorage.setItem(this.watchlistGroupsKey, JSON.stringify(groups)); } async updateWatchlistGroup(group: WatchlistGroup) { const groups = await this.getWatchlistGroups(); const idx = groups.findIndex(g => g.id === group.id); if (idx > -1) { groups[idx] = group; localStorage.setItem(this.watchlistGroupsKey, JSON.stringify(groups)); } } async deleteWatchlistGroup(id: string) { const groups = await this.getWatchlistGroups(); const filtered = groups.filter(g => g.id !== id); localStorage.setItem(this.watchlistGroupsKey, JSON.stringify(filtered)); } async getAutoConfigs(): Promise { const data = localStorage.getItem(this.configsKey); return data ? JSON.parse(data) : []; } async saveAutoConfig(config: AutoTradeConfig) { const configs = await this.getAutoConfigs(); configs.push(config); localStorage.setItem(this.configsKey, JSON.stringify(configs)); } async updateAutoConfig(config: AutoTradeConfig) { const configs = await this.getAutoConfigs(); const idx = configs.findIndex(c => c.id === config.id); if (idx > -1) { configs[idx] = config; localStorage.setItem(this.configsKey, JSON.stringify(configs)); } } async deleteAutoConfig(id: string) { const configs = await this.getAutoConfigs(); const filtered = configs.filter(c => c.id !== id); localStorage.setItem(this.configsKey, JSON.stringify(filtered)); } async getReservedOrders(): Promise { const data = localStorage.getItem(this.reservedOrdersKey); return data ? JSON.parse(data) : []; } async saveReservedOrder(order: ReservedOrder) { const orders = await this.getReservedOrders(); orders.push(order); localStorage.setItem(this.reservedOrdersKey, JSON.stringify(orders)); } async updateReservedOrder(order: ReservedOrder) { const orders = await this.getReservedOrders(); const idx = orders.findIndex(o => o.id === order.id); if (idx > -1) { orders[idx] = order; localStorage.setItem(this.reservedOrdersKey, JSON.stringify(orders)); } } async deleteReservedOrder(id: string) { const orders = await this.getReservedOrders(); const filtered = orders.filter(o => o.id !== id); localStorage.setItem(this.reservedOrdersKey, JSON.stringify(filtered)); } async getAccountSummary() { const holdings = await this.getHoldings(); const totalEval = holdings.reduce((acc, h) => acc + (h.avgPrice * h.quantity), 0); return { totalAssets: totalEval + 45800000, buyingPower: 45800000 }; } }