obv 수정
This commit is contained in:
@@ -22,6 +22,7 @@ import { useIndicatorSettings } from '../../hooks/useIndicatorSettings';
|
|||||||
import { useSessionCandleOverlayControls } from '../../hooks/useSessionCandleOverlayControls';
|
import { useSessionCandleOverlayControls } from '../../hooks/useSessionCandleOverlayControls';
|
||||||
import { ensurePaperChartOverlays } from '../../utils/strategyToChartIndicators';
|
import { ensurePaperChartOverlays } from '../../utils/strategyToChartIndicators';
|
||||||
import { mergeGlobalDefaultsOntoChartIndicators } from '../../utils/indicatorMainConfig';
|
import { mergeGlobalDefaultsOntoChartIndicators } from '../../utils/indicatorMainConfig';
|
||||||
|
import { barsMeetObvVolumeRequirement } from '../../utils/obvChartBars';
|
||||||
import { enrichIndicatorConfig } from '../../utils/indicatorRegistry';
|
import { enrichIndicatorConfig } from '../../utils/indicatorRegistry';
|
||||||
import {
|
import {
|
||||||
applyNotificationSignalMarkers,
|
applyNotificationSignalMarkers,
|
||||||
@@ -135,7 +136,10 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
|||||||
managerRef.current = mgr;
|
managerRef.current = mgr;
|
||||||
syncToManager(mgr);
|
syncToManager(mgr);
|
||||||
applySignalMarkers();
|
applySignalMarkers();
|
||||||
}, [syncToManager, applySignalMarkers]);
|
if (needsDeepObv) {
|
||||||
|
void mgr.repairDualLineSeries();
|
||||||
|
}
|
||||||
|
}, [syncToManager, applySignalMarkers, needsDeepObv]);
|
||||||
|
|
||||||
const applyRealtimeBar = useCallback((bar: OHLCVBar, append: boolean) => {
|
const applyRealtimeBar = useCallback((bar: OHLCVBar, append: boolean) => {
|
||||||
if (barsMarketRef.current !== marketRef.current) return;
|
if (barsMarketRef.current !== marketRef.current) return;
|
||||||
@@ -185,7 +189,7 @@ const TradeSignalMiniChart: React.FC<Props> = ({
|
|||||||
const obvBarsReady = useMemo(() => {
|
const obvBarsReady = useMemo(() => {
|
||||||
if (!needsDeepObv) return true;
|
if (!needsDeepObv) return true;
|
||||||
if (bars.length < 2) return false;
|
if (bars.length < 2) return false;
|
||||||
return bars.some(b => (Number(b.volume) || 0) > 0);
|
return barsMeetObvVolumeRequirement(bars);
|
||||||
}, [needsDeepObv, bars]);
|
}, [needsDeepObv, bars]);
|
||||||
|
|
||||||
const chartDataReady = barsReady && obvBarsReady && settingsLoaded;
|
const chartDataReady = barsReady && obvBarsReady && settingsLoaded;
|
||||||
|
|||||||
@@ -1203,6 +1203,7 @@ export class ChartManager {
|
|||||||
|
|
||||||
if (this._indicatorLoadStale(loadGen)) return;
|
if (this._indicatorLoadStale(loadGen)) return;
|
||||||
|
|
||||||
|
await this.repairDualLineSeries();
|
||||||
this._finalizeIndicatorPaneLayout();
|
this._finalizeIndicatorPaneLayout();
|
||||||
this._refreshAllIchimokuClouds();
|
this._refreshAllIchimokuClouds();
|
||||||
this.syncChartOverlayVisibility();
|
this.syncChartOverlayVisibility();
|
||||||
@@ -1250,6 +1251,7 @@ export class ChartManager {
|
|||||||
}
|
}
|
||||||
if (this._indicatorLoadStale(loadGen)) return;
|
if (this._indicatorLoadStale(loadGen)) return;
|
||||||
|
|
||||||
|
await this.repairDualLineSeries();
|
||||||
this._finalizeIndicatorPaneLayout();
|
this._finalizeIndicatorPaneLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import type { OHLCVBar } from '../types';
|
||||||
|
|
||||||
|
/** OBV 본선 계산에 필요한 최소 거래량 품질 (some>0 만으로는 STOMP volume=0 history 통과) */
|
||||||
|
export function barsMeetObvVolumeRequirement(bars: OHLCVBar[]): boolean {
|
||||||
|
if (bars.length < 2) return false;
|
||||||
|
const withVol = bars.filter(b => (Number(b.volume) || 0) > 0).length;
|
||||||
|
const volSum = bars.reduce((s, b) => s + (Number(b.volume) || 0), 0);
|
||||||
|
const minBarsWithVol = Math.max(20, Math.floor(bars.length * 0.5));
|
||||||
|
return volSum > 0 && withVol >= minBarsWithVol;
|
||||||
|
}
|
||||||
@@ -68,10 +68,14 @@ type GetVisual = (
|
|||||||
cloudColors?: import('./ichimokuConfig').IchimokuCloudColors;
|
cloudColors?: import('./ichimokuConfig').IchimokuCloudColors;
|
||||||
};
|
};
|
||||||
|
|
||||||
let _idSeq = 0;
|
/** 전략 DSL 기준 안정 id — getVisualConfig/settingsRevision 변경 시 id 재발급 방지 */
|
||||||
function newIndId(): string {
|
export function stableStrategyIndicatorId(ref: IndicatorRef): string {
|
||||||
_idSeq += 1;
|
const parts = [ref.registryType];
|
||||||
return `vstrat_${_idSeq}_${Date.now()}`;
|
if (ref.period != null) parts.push(`p${ref.period}`);
|
||||||
|
if (ref.leftPeriod != null) parts.push(`l${ref.leftPeriod}`);
|
||||||
|
if (ref.rightPeriod != null) parts.push(`r${ref.rightPeriod}`);
|
||||||
|
if (ref.targetValue != null) parts.push(`t${ref.targetValue}`);
|
||||||
|
return `strat_${parts.join('_')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function candleTypeToTimeframe(candleType: string): Timeframe {
|
export function candleTypeToTimeframe(candleType: string): Timeframe {
|
||||||
@@ -171,6 +175,7 @@ function buildOneIndicator(
|
|||||||
ref: IndicatorRef,
|
ref: IndicatorRef,
|
||||||
getParams: GetParams,
|
getParams: GetParams,
|
||||||
getVisual: GetVisual,
|
getVisual: GetVisual,
|
||||||
|
stableId?: string,
|
||||||
): IndicatorConfig | null {
|
): IndicatorConfig | null {
|
||||||
const def = getIndicatorDef(ref.registryType);
|
const def = getIndicatorDef(ref.registryType);
|
||||||
if (!def) return null;
|
if (!def) return null;
|
||||||
@@ -179,7 +184,7 @@ function buildOneIndicator(
|
|||||||
const { plots, hlines, cloudColors } = getVisual(ref.registryType, def.plots, def.hlines ?? []);
|
const { plots, hlines, cloudColors } = getVisual(ref.registryType, def.plots, def.hlines ?? []);
|
||||||
|
|
||||||
let cfg: IndicatorConfig = {
|
let cfg: IndicatorConfig = {
|
||||||
id: newIndId(),
|
id: stableId ?? stableStrategyIndicatorId(ref),
|
||||||
type: def.type,
|
type: def.type,
|
||||||
params: { ...params },
|
params: { ...params },
|
||||||
plots,
|
plots,
|
||||||
@@ -295,6 +300,7 @@ export function ensurePaperChartOverlays(
|
|||||||
{ dslType: 'MA', registryType: 'SMA' },
|
{ dslType: 'MA', registryType: 'SMA' },
|
||||||
getParams,
|
getParams,
|
||||||
getVisual,
|
getVisual,
|
||||||
|
'strat_overlay_SMA',
|
||||||
);
|
);
|
||||||
if (sma) prepend.push(sma);
|
if (sma) prepend.push(sma);
|
||||||
}
|
}
|
||||||
@@ -303,6 +309,7 @@ export function ensurePaperChartOverlays(
|
|||||||
{ dslType: 'ICHIMOKU', registryType: 'IchimokuCloud' },
|
{ dslType: 'ICHIMOKU', registryType: 'IchimokuCloud' },
|
||||||
getParams,
|
getParams,
|
||||||
getVisual,
|
getVisual,
|
||||||
|
'strat_overlay_IchimokuCloud',
|
||||||
);
|
);
|
||||||
if (ichimoku) prepend.push(ichimoku);
|
if (ichimoku) prepend.push(ichimoku);
|
||||||
}
|
}
|
||||||
@@ -311,6 +318,7 @@ export function ensurePaperChartOverlays(
|
|||||||
{ dslType: 'BOLLINGER', registryType: 'BollingerBands' },
|
{ dslType: 'BOLLINGER', registryType: 'BollingerBands' },
|
||||||
getParams,
|
getParams,
|
||||||
getVisual,
|
getVisual,
|
||||||
|
'strat_overlay_BollingerBands',
|
||||||
);
|
);
|
||||||
if (bb) prepend.push(bb);
|
if (bb) prepend.push(bb);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user