모의투자 관리 기능 추가
This commit is contained in:
@@ -610,21 +610,42 @@ export interface PaperPositionDto {
|
||||
profitLossPct?: number;
|
||||
}
|
||||
|
||||
export interface PaperAllocationDto {
|
||||
id?: number;
|
||||
symbol: string;
|
||||
koreanName?: string | null;
|
||||
maxInvestKrw: number;
|
||||
budgetPct?: number | null;
|
||||
strategyId?: number | null;
|
||||
isActive?: boolean;
|
||||
pinned?: boolean;
|
||||
sortOrder?: number;
|
||||
investedKrw?: number;
|
||||
availableBuyKrw?: number;
|
||||
evalAmount?: number;
|
||||
symbolReturnPct?: number;
|
||||
weightPct?: number;
|
||||
}
|
||||
|
||||
export interface PaperSummaryDto {
|
||||
enabled: boolean;
|
||||
initialCapital: number;
|
||||
cashBalance: number;
|
||||
stockEvalAmount: number;
|
||||
totalAsset: number;
|
||||
unrealizedPnl: number;
|
||||
realizedPnl: number;
|
||||
totalReturnPct: number;
|
||||
feeRatePct: number;
|
||||
slippagePct: number;
|
||||
minOrderKrw: number;
|
||||
autoTradeEnabled: boolean;
|
||||
autoTradeBudgetPct: number;
|
||||
positions: PaperPositionDto[];
|
||||
enabled: boolean;
|
||||
initialCapital: number;
|
||||
cashBalance: number;
|
||||
stockEvalAmount: number;
|
||||
totalAsset: number;
|
||||
unrealizedPnl: number;
|
||||
realizedPnl: number;
|
||||
totalReturnPct: number;
|
||||
feeRatePct: number;
|
||||
slippagePct: number;
|
||||
minOrderKrw: number;
|
||||
autoTradeEnabled: boolean;
|
||||
autoTradeBudgetPct: number;
|
||||
reservedCash?: number;
|
||||
orderableCash?: number;
|
||||
initialCapitalSnapshot?: number;
|
||||
positions: PaperPositionDto[];
|
||||
allocations?: PaperAllocationDto[];
|
||||
}
|
||||
|
||||
export interface PaperTradeDto {
|
||||
@@ -640,13 +661,79 @@ export interface PaperTradeDto {
|
||||
feeAmount: number;
|
||||
netAmount: number;
|
||||
cashAfter: number;
|
||||
realizedPnlDelta?: number | null;
|
||||
positionQtyAfter?: number | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
|
||||
export interface PaperPageDto<T> {
|
||||
content: T[];
|
||||
page: number;
|
||||
size: number;
|
||||
totalElements: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
export interface PaperLedgerEntryDto {
|
||||
id: number;
|
||||
entryType: string;
|
||||
amount: number;
|
||||
cashBefore: number;
|
||||
cashAfter: number;
|
||||
refTradeId?: number | null;
|
||||
symbol?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
|
||||
export interface PaperDailySnapshotDto {
|
||||
snapshotDate: string;
|
||||
cashBalance: number;
|
||||
stockEvalAmount: number;
|
||||
totalAsset: number;
|
||||
dailyPnl: number;
|
||||
dailyReturnPct: number;
|
||||
cumulativeReturnPct: number;
|
||||
}
|
||||
|
||||
export interface PaperOrderDto {
|
||||
id: number;
|
||||
symbol: string;
|
||||
side: 'BUY' | 'SELL';
|
||||
orderType: string;
|
||||
limitPrice?: number | null;
|
||||
quantity: number;
|
||||
filledQuantity: number;
|
||||
status: string;
|
||||
reservedCash: number;
|
||||
reservedQty: number;
|
||||
orderKind: string;
|
||||
source: string;
|
||||
strategyId?: number | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
}
|
||||
|
||||
export interface PaperPlaceOrderResult {
|
||||
trade?: PaperTradeDto | null;
|
||||
order?: PaperOrderDto | null;
|
||||
}
|
||||
|
||||
export interface PaperAllocationBulkItem {
|
||||
symbol: string;
|
||||
koreanName?: string;
|
||||
maxInvestKrw?: number;
|
||||
budgetPct?: number;
|
||||
strategyId?: number | null;
|
||||
isActive?: boolean;
|
||||
pinned?: boolean;
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
export interface PaperOrderRequest {
|
||||
market: string;
|
||||
side: 'BUY' | 'SELL';
|
||||
orderKind?: 'limit' | 'market';
|
||||
orderType?: 'MARKET' | 'LIMIT';
|
||||
price: number;
|
||||
quantity: number;
|
||||
/** 수량 0일 때 매수 예산 비율 (가용현금 %) */
|
||||
@@ -671,13 +758,90 @@ export async function loadPaperTrades(): Promise<PaperTradeDto[]> {
|
||||
return (await request<PaperTradeDto[]>('/paper/trades')) ?? [];
|
||||
}
|
||||
|
||||
export async function placePaperOrder(body: PaperOrderRequest): Promise<PaperTradeDto | null> {
|
||||
return request<PaperTradeDto>('/paper/orders', {
|
||||
export async function loadPaperTradesPaged(params?: {
|
||||
symbol?: string;
|
||||
side?: string;
|
||||
source?: string;
|
||||
from?: string;
|
||||
to?: string;
|
||||
page?: number;
|
||||
size?: number;
|
||||
}): Promise<PaperPageDto<PaperTradeDto>> {
|
||||
const q = new URLSearchParams();
|
||||
if (params?.symbol) q.set('symbol', params.symbol);
|
||||
if (params?.side) q.set('side', params.side);
|
||||
if (params?.source) q.set('source', params.source);
|
||||
if (params?.from) q.set('from', params.from);
|
||||
if (params?.to) q.set('to', params.to);
|
||||
if (params?.page != null) q.set('page', String(params.page));
|
||||
if (params?.size != null) q.set('size', String(params.size));
|
||||
const qs = q.toString();
|
||||
return (await request<PaperPageDto<PaperTradeDto>>(`/paper/trades${qs ? `?${qs}` : ''}`)) ?? {
|
||||
content: [], page: 0, size: 50, totalElements: 0, totalPages: 0,
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadPaperAllocations(): Promise<PaperAllocationDto[]> {
|
||||
return (await request<PaperAllocationDto[]>('/paper/allocations')) ?? [];
|
||||
}
|
||||
|
||||
export async function putPaperAllocationsBulk(items: PaperAllocationBulkItem[]): Promise<PaperAllocationDto[]> {
|
||||
return (await request<PaperAllocationDto[]>('/paper/allocations/bulk', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ items }),
|
||||
})) ?? [];
|
||||
}
|
||||
|
||||
export async function patchPaperAllocation(
|
||||
symbol: string,
|
||||
body: Partial<Pick<PaperAllocationDto, 'maxInvestKrw' | 'budgetPct' | 'strategyId' | 'isActive' | 'pinned' | 'sortOrder' | 'koreanName'>>,
|
||||
): Promise<PaperAllocationDto | null> {
|
||||
return request<PaperAllocationDto>(`/paper/allocations/${encodeURIComponent(symbol)}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadPaperLedger(params?: {
|
||||
from?: string;
|
||||
to?: string;
|
||||
page?: number;
|
||||
size?: number;
|
||||
}): Promise<PaperPageDto<PaperLedgerEntryDto>> {
|
||||
const q = new URLSearchParams();
|
||||
if (params?.from) q.set('from', params.from);
|
||||
if (params?.to) q.set('to', params.to);
|
||||
if (params?.page != null) q.set('page', String(params.page));
|
||||
if (params?.size != null) q.set('size', String(params.size));
|
||||
const qs = q.toString();
|
||||
return (await request<PaperPageDto<PaperLedgerEntryDto>>(`/paper/ledger${qs ? `?${qs}` : ''}`)) ?? {
|
||||
content: [], page: 0, size: 50, totalElements: 0, totalPages: 0,
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadPaperDailySnapshots(from?: string, to?: string): Promise<PaperDailySnapshotDto[]> {
|
||||
const q = new URLSearchParams();
|
||||
if (from) q.set('from', from);
|
||||
if (to) q.set('to', to);
|
||||
const qs = q.toString();
|
||||
return (await request<PaperDailySnapshotDto[]>(`/paper/snapshots/daily${qs ? `?${qs}` : ''}`)) ?? [];
|
||||
}
|
||||
|
||||
export async function loadPaperPendingOrders(): Promise<PaperOrderDto[]> {
|
||||
return (await request<PaperOrderDto[]>('/paper/orders')) ?? [];
|
||||
}
|
||||
|
||||
export async function placePaperOrder(body: PaperOrderRequest): Promise<PaperPlaceOrderResult | null> {
|
||||
return request<PaperPlaceOrderResult>('/paper/orders', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
}
|
||||
|
||||
export async function cancelPaperOrder(orderId: number): Promise<PaperOrderDto | null> {
|
||||
return request<PaperOrderDto>(`/paper/orders/${orderId}/cancel`, { method: 'POST' });
|
||||
}
|
||||
|
||||
export async function resetPaperAccount(): Promise<PaperSummaryDto | null> {
|
||||
return request<PaperSummaryDto>('/paper/reset', { method: 'POST' });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user