전략편집기 수정, 보조지표 설정 수정
This commit is contained in:
@@ -1298,7 +1298,7 @@ export class ChartManager {
|
||||
.find(p => p.id === meta.plotId);
|
||||
const prevVal = plotData.length >= 2 ? plotData[plotData.length - 2]?.value : null;
|
||||
const barColor = plotDef
|
||||
? histogramBarColor(curVal as number, prevVal, plotDef, lastPoint.color)
|
||||
? histogramBarColor(curVal as number, prevVal, plotDef)
|
||||
: (lastPoint.color ?? '#26A69A88');
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(entry.seriesList[i] as ISeriesApi<any>).update({
|
||||
@@ -2044,9 +2044,6 @@ export class ChartManager {
|
||||
if (entry.seriesMeta.some(m => m.isHistogram)) {
|
||||
void this._repaintHistogramSeries(config.id, config);
|
||||
}
|
||||
if (entry.seriesMeta.some(m => m.isHistogram)) {
|
||||
void this._repaintHistogramSeries(config.id, config);
|
||||
}
|
||||
|
||||
if (this._candleOnlyLayout && this._isSubPaneIndicator(entry)) {
|
||||
this._hideSubPaneIndicator(entry);
|
||||
|
||||
@@ -168,9 +168,20 @@ export function hasEditableThreshold(cond: ConditionDSL): boolean {
|
||||
|| parseThresholdField(cond.leftField) != null;
|
||||
}
|
||||
|
||||
export function getConditionThreshold(cond: ConditionDSL, side: 'left' | 'right'): number | null {
|
||||
if (side === 'right' && !isThresholdOverridden(cond)) {
|
||||
return parseThresholdField(cond.rightField);
|
||||
/**
|
||||
* 임계값 표시 — thresholdOverride 가 false 이면 inheritedField(보조지표 설정 DEF) 우선.
|
||||
*/
|
||||
export function getConditionThreshold(
|
||||
cond: ConditionDSL,
|
||||
side: 'left' | 'right',
|
||||
inheritedField?: string,
|
||||
): number | null {
|
||||
if (!isThresholdOverridden(cond) && inheritedField) {
|
||||
const fromDef = parseThresholdField(inheritedField);
|
||||
if (fromDef != null) return fromDef;
|
||||
}
|
||||
if (isThresholdOverridden(cond) && cond.targetValue != null && Number.isFinite(cond.targetValue)) {
|
||||
return cond.targetValue;
|
||||
}
|
||||
const field = side === 'left' ? cond.leftField : cond.rightField;
|
||||
return parseThresholdField(field);
|
||||
|
||||
@@ -73,9 +73,7 @@ export function histogramBarColor(
|
||||
value: number,
|
||||
prev: number | null | undefined,
|
||||
plot: PlotDef,
|
||||
pointColor?: string,
|
||||
): string {
|
||||
if (pointColor) return withHistogramAlpha(pointColor);
|
||||
const isDown = prev != null && !isNaN(prev) && value < prev;
|
||||
const isNeg = value < 0;
|
||||
const raw = (isDown || isNeg) ? resolveHistogramDownColor(plot) : resolveHistogramUpColor(plot);
|
||||
@@ -89,6 +87,7 @@ export function mapHistogramSeriesData(
|
||||
return filtered.map((p, idx, arr) => ({
|
||||
time: p.time as import('lightweight-charts').Time,
|
||||
value: p.value,
|
||||
color: histogramBarColor(p.value, idx > 0 ? arr[idx - 1].value : null, plot, p.color),
|
||||
// lightweight-charts-indicators MACD 등이 막대별 color를 넣어도 설정(상승/하락) 색을 우선
|
||||
color: histogramBarColor(p.value, idx > 0 ? arr[idx - 1].value : null, plot),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -264,7 +264,6 @@ function wrapTimeframe(candleType: string, root: LogicNode): LogicNode {
|
||||
|
||||
function wrapTimeframes(candleTypes: string[], root: LogicNode): LogicNode {
|
||||
const types = normalizeCandleTypesList(candleTypes);
|
||||
if (types.length === 1) return wrapTimeframe(types[0], root);
|
||||
return {
|
||||
id: generateNodeId(),
|
||||
type: 'TIMEFRAME',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** Shared strategy editor utilities — extracted from StrategyPage */
|
||||
import React from 'react';
|
||||
import type { IndicatorConfig } from '../types/index';
|
||||
import { getIndicatorDef, getHLineLabel, type PlotDef, type HLineDef } from '../utils/indicatorRegistry';
|
||||
import { getIndicatorDef, getHLineLabel, INDICATOR_REGISTRY, type PlotDef, type HLineDef } from '../utils/indicatorRegistry';
|
||||
import type { LogicNode, LogicNodeType, ConditionDSL } from '../utils/strategyTypes';
|
||||
import {
|
||||
compositeDisplayName,
|
||||
@@ -270,24 +270,17 @@ export function buildStrategyEditorDefFromSettings(
|
||||
defaultHlines?: HLineDef[],
|
||||
) => { plots: PlotDef[]; hlines: HLineDef[]; cloudColors?: unknown },
|
||||
): DefType {
|
||||
const registryTypes = [
|
||||
'RSI', 'MACD', 'CCI', 'Stochastic', 'ADX', 'TRIX', 'DMI', 'BollingerBands',
|
||||
'IchimokuCloud', 'WilliamsPercentRange', 'VolumeOscillator', 'VR', 'Disparity',
|
||||
'Psychological', 'InvestPsychological', 'OBV', 'DonchianChannels', 'PriceExtreme',
|
||||
'SMA',
|
||||
];
|
||||
const configs: IndicatorConfig[] = [];
|
||||
for (const type of registryTypes) {
|
||||
const def = getIndicatorDef(type);
|
||||
if (!def) continue;
|
||||
const params = getParams(type, def.defaultParams as Record<string, number | string | boolean>);
|
||||
const { plots, hlines, cloudColors } = getVisualConfig(type, def.plots, def.hlines);
|
||||
for (const regDef of INDICATOR_REGISTRY) {
|
||||
const type = regDef.type;
|
||||
const params = getParams(type, regDef.defaultParams as Record<string, number | string | boolean>);
|
||||
const { plots, hlines, cloudColors } = getVisualConfig(type, regDef.plots, regDef.hlines);
|
||||
configs.push({
|
||||
id: `se-def-${type}`,
|
||||
type,
|
||||
params: { ...params },
|
||||
plots,
|
||||
hlines,
|
||||
plots: plots.map(p => ({ ...p })),
|
||||
hlines: hlines.map(h => ({ ...h })),
|
||||
...(cloudColors ? { cloudColors } : {}),
|
||||
} as IndicatorConfig);
|
||||
}
|
||||
@@ -800,6 +793,7 @@ export const CondEditor: React.FC<CondEditorProps> = ({ cond, signalType, onChan
|
||||
|
||||
const isValid = (v: string|undefined) => !!v && fieldOpts.some(o => o.value === v);
|
||||
const defaults = getDefaultConditionFields(normalized.indicatorType, signalType, def);
|
||||
const inheritedThresholdField = defaults.r;
|
||||
|
||||
const getLeftValue = () => {
|
||||
const v = resolveFieldOptionValue(normalized.indicatorType, normalized.leftField);
|
||||
@@ -1011,7 +1005,7 @@ export const CondEditor: React.FC<CondEditorProps> = ({ cond, signalType, onChan
|
||||
fieldValue={rightValue}
|
||||
isPresetField={fieldOpts.some(o => o.value === normalized.rightField)}
|
||||
customKind={resolveFieldCustomKind(normalized.indicatorType, normalized.rightField)}
|
||||
customNumber={getConditionThreshold(normalized, 'right')
|
||||
customNumber={getConditionThreshold(normalized, 'right', inheritedThresholdField)
|
||||
?? parseThresholdField(normalized.rightField)
|
||||
?? 0}
|
||||
numberPresets={getThresholdPresetOptions(normalized.indicatorType)}
|
||||
|
||||
Reference in New Issue
Block a user