122 lines
3.6 KiB
TypeScript
122 lines
3.6 KiB
TypeScript
import { saveLiveStrategySettings } from './backendApi';
|
|
import { getVirtualTargetMaxCount } from '../hooks/useAppSettings';
|
|
import {
|
|
isVirtualTargetAddAllowed,
|
|
virtualTargetLimitMessage,
|
|
} from '../utils/virtualTargetLimits';
|
|
import { syncVirtualTargetsToBackend } from './virtualLiveStrategySync';
|
|
import { resolveVirtualTargetStrategyId } from './virtualTargetStrategy';
|
|
import {
|
|
loadVirtualSession,
|
|
loadVirtualTargets,
|
|
notifyVirtualSessionChanged,
|
|
saveVirtualTargets,
|
|
type VirtualTargetItem,
|
|
} from './virtualTradingStorage';
|
|
|
|
export interface VirtualTargetMeta {
|
|
koreanName?: string;
|
|
englishName?: string;
|
|
}
|
|
|
|
export interface AddVirtualTargetOptions {
|
|
/** false면 한도 초과 시 알림 없이 스킵 (자동추가용) */
|
|
showLimitAlert?: boolean;
|
|
}
|
|
|
|
/** 가상투자 투자대상 목록에 종목 추가 (localStorage + DB) */
|
|
export async function addVirtualTarget(
|
|
market: string,
|
|
meta: VirtualTargetMeta = {},
|
|
opts: AddVirtualTargetOptions = {},
|
|
): Promise<VirtualTargetItem[]> {
|
|
const showLimitAlert = opts.showLimitAlert !== false;
|
|
const targets = loadVirtualTargets();
|
|
if (targets.some(t => t.market === market)) return targets;
|
|
|
|
const maxCount = getVirtualTargetMaxCount();
|
|
if (!isVirtualTargetAddAllowed(targets.length, maxCount)) {
|
|
if (showLimitAlert) window.alert(virtualTargetLimitMessage(maxCount));
|
|
return targets;
|
|
}
|
|
|
|
const session = loadVirtualSession();
|
|
const en = meta.englishName ?? market.replace(/^KRW-/, '');
|
|
const item: VirtualTargetItem = {
|
|
market,
|
|
/** null = 상단 기본 전략 사용 */
|
|
strategyId: null,
|
|
koreanName: meta.koreanName,
|
|
englishName: en,
|
|
};
|
|
|
|
const next = [...targets, item];
|
|
saveVirtualTargets(next);
|
|
|
|
await saveLiveStrategySettings({
|
|
market,
|
|
strategyId: session.globalStrategyId,
|
|
isLiveCheck: session.running,
|
|
isPinned: false,
|
|
executionType: session.executionType,
|
|
positionMode: session.positionMode,
|
|
skipWatchlistSync: true,
|
|
skipGlobalTemplate: true,
|
|
});
|
|
|
|
if (session.running && session.globalStrategyId) {
|
|
await syncVirtualTargetsToBackend(next, session, true);
|
|
}
|
|
|
|
notifyVirtualSessionChanged();
|
|
return next;
|
|
}
|
|
|
|
/** 가상투자 투자대상 목록에서 종목 제거 (localStorage + DB 비활성화) */
|
|
/** 투자대상 고정 상태 DB 저장 */
|
|
export async function persistVirtualTargetPinned(market: string, pinned: boolean): Promise<void> {
|
|
const session = loadVirtualSession();
|
|
const item = loadVirtualTargets().find(t => t.market === market);
|
|
await saveLiveStrategySettings({
|
|
market,
|
|
strategyId: item
|
|
? resolveVirtualTargetStrategyId(item, session.globalStrategyId)
|
|
: session.globalStrategyId,
|
|
isLiveCheck: session.running,
|
|
isPinned: pinned,
|
|
executionType: session.executionType,
|
|
positionMode: session.positionMode,
|
|
skipWatchlistSync: true,
|
|
skipGlobalTemplate: true,
|
|
});
|
|
}
|
|
|
|
export async function removeVirtualTarget(market: string): Promise<VirtualTargetItem[]> {
|
|
const targets = loadVirtualTargets();
|
|
const item = targets.find(t => t.market === market);
|
|
if (!item) return targets;
|
|
if (item.pinned) return targets;
|
|
|
|
const session = loadVirtualSession();
|
|
const next = targets.filter(t => t.market !== market);
|
|
saveVirtualTargets(next);
|
|
|
|
await saveLiveStrategySettings({
|
|
market,
|
|
strategyId: session.globalStrategyId,
|
|
isLiveCheck: false,
|
|
isPinned: false,
|
|
executionType: session.executionType,
|
|
positionMode: session.positionMode,
|
|
skipWatchlistSync: true,
|
|
skipGlobalTemplate: true,
|
|
});
|
|
|
|
if (session.running) {
|
|
await syncVirtualTargetsToBackend(next, session, true);
|
|
}
|
|
|
|
notifyVirtualSessionChanged();
|
|
return next;
|
|
}
|