Files
goldenChart/frontend/src/widgets/WidgetDashboardContext.tsx
T
2026-06-14 01:03:51 +09:00

145 lines
4.4 KiB
TypeScript

/**
* 위젯 대시보드 — 종목·매매 상태 공유
*/
import React, {
createContext, useCallback, useContext, useEffect, useMemo, useState,
} from 'react';
import type { PaperSummaryDto, PaperTradeDto, StrategyDto } from '../utils/backendApi';
import type { TickerData, MarketInfo } from '../hooks/useMarketTicker';
import type { TradeOrderFillRequest } from '../types';
import type { Theme } from '../types';
import type { ChartRealtimeSource } from '../hooks/useChartRealtimeData';
export interface WidgetDashboardContextValue {
theme: Theme;
selectedMarket: string;
setSelectedMarket: (m: string) => void;
tickers: Map<string, TickerData>;
marketInfos: MarketInfo[];
marketLoading: boolean;
usdRate: number;
strategies: StrategyDto[];
selectedStrategyId: number | null;
setSelectedStrategyId: (id: number | null) => void;
summary: PaperSummaryDto | null;
trades: PaperTradeDto[];
refreshPaperData: () => void;
tradePrice: number | null;
fillBuy: TradeOrderFillRequest | null;
fillSell: TradeOrderFillRequest | null;
setFillBuy: (r: TradeOrderFillRequest | null) => void;
setFillSell: (r: TradeOrderFillRequest | null) => void;
paperTradingEnabled: boolean;
paperAutoTradeEnabled: boolean;
onPaperOrderFilled?: () => void;
chartRealtimeSource: ChartRealtimeSource;
}
const WidgetDashboardContext = createContext<WidgetDashboardContextValue | null>(null);
export function useWidgetDashboard(): WidgetDashboardContextValue {
const ctx = useContext(WidgetDashboardContext);
if (!ctx) throw new Error('useWidgetDashboard must be used within WidgetDashboardProvider');
return ctx;
}
export function useWidgetDashboardOptional(): WidgetDashboardContextValue | null {
return useContext(WidgetDashboardContext);
}
interface ProviderProps {
theme: Theme;
tickers: Map<string, TickerData>;
marketInfos: MarketInfo[];
marketLoading: boolean;
usdRate: number;
defaultMarket: string;
strategies: StrategyDto[];
summary: PaperSummaryDto | null;
trades: PaperTradeDto[];
refreshPaperData: () => void;
paperTradingEnabled: boolean;
paperAutoTradeEnabled: boolean;
onPaperOrderFilled?: () => void;
chartRealtimeSource: ChartRealtimeSource;
children: React.ReactNode;
}
export const WidgetDashboardProvider: React.FC<ProviderProps> = ({
theme,
tickers,
marketInfos,
marketLoading,
usdRate,
defaultMarket,
strategies,
summary,
trades,
refreshPaperData,
paperTradingEnabled,
paperAutoTradeEnabled,
onPaperOrderFilled,
chartRealtimeSource,
children,
}) => {
const [selectedMarket, setSelectedMarket] = useState(defaultMarket);
const [selectedStrategyId, setSelectedStrategyId] = useState<number | null>(null);
useEffect(() => {
if (defaultMarket) setSelectedMarket(defaultMarket);
}, [defaultMarket]);
const [fillBuy, setFillBuy] = useState<TradeOrderFillRequest | null>(null);
const [fillSell, setFillSell] = useState<TradeOrderFillRequest | null>(null);
const tradePrice = useMemo(() => {
const t = tickers.get(selectedMarket);
return t?.tradePrice ?? null;
}, [tickers, selectedMarket]);
const value = useMemo<WidgetDashboardContextValue>(() => ({
theme,
selectedMarket,
setSelectedMarket,
tickers,
marketInfos,
marketLoading,
usdRate,
strategies,
selectedStrategyId,
setSelectedStrategyId,
summary,
trades,
refreshPaperData,
tradePrice,
fillBuy,
fillSell,
setFillBuy,
setFillSell,
paperTradingEnabled,
paperAutoTradeEnabled,
onPaperOrderFilled,
chartRealtimeSource,
}), [
theme, selectedMarket, tickers, marketInfos, marketLoading, usdRate,
strategies, selectedStrategyId, summary, trades, refreshPaperData,
tradePrice, fillBuy, fillSell, paperTradingEnabled, paperAutoTradeEnabled,
onPaperOrderFilled, chartRealtimeSource,
]);
return (
<WidgetDashboardContext.Provider value={value}>
{children}
</WidgetDashboardContext.Provider>
);
};
export function useWidgetOrderbookFillHandler(): (price: number, rowType: 'ask' | 'bid') => void {
const ctx = useWidgetDashboard();
return useCallback((price: number, rowType: 'ask' | 'bid') => {
const side = rowType === 'bid' ? 'buy' : 'sell';
const req: TradeOrderFillRequest = { market: ctx.selectedMarket, price, side, seq: Date.now() };
if (side === 'buy') ctx.setFillBuy(req);
else ctx.setFillSell(req);
}, [ctx]);
}