위젯 기능 추가
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* 위젯 대시보드 — 종목·매매 상태 공유
|
||||
*/
|
||||
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]);
|
||||
}
|
||||
Reference in New Issue
Block a user