추세검색 설정 추가
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* 추세검색 앱 설정 (gc_app_settings.trend_search_settings_json)
|
||||
*/
|
||||
import type { TrendSearchRequest } from './backendApi';
|
||||
import { DEFAULT_TREND_SEARCH_REQUEST } from './backendApi';
|
||||
import {
|
||||
BULLISH_WEIGHT_ITEMS,
|
||||
DEFAULT_BULLISH_WEIGHTS,
|
||||
type BullishWeightKey,
|
||||
} from './trendSearchBullishWeights';
|
||||
|
||||
export interface TrendSearchAppSettings {
|
||||
weightMaAlignment: number;
|
||||
weightMaSlope: number;
|
||||
weightAdxTrend: number;
|
||||
weightMacdMomentum: number;
|
||||
weightPricePosition: number;
|
||||
minTrendScore: number;
|
||||
/** 결과 목록 최대 개수 */
|
||||
limit: number;
|
||||
/** 스캔 대상 종목 수 (거래대금 상위) */
|
||||
scanLimit: number;
|
||||
/** 자동 갱신 간격(초) */
|
||||
autoRefreshSeconds: number;
|
||||
/** 검색 후 상위 N위까지 투자대상 자동추가 */
|
||||
autoAddTargetsEnabled: boolean;
|
||||
autoAddTopRank: number;
|
||||
/** 자동추가 시 추세점수(matchRate) 하한 */
|
||||
autoAddMinScore: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_TREND_SEARCH_APP_SETTINGS: TrendSearchAppSettings = {
|
||||
weightMaAlignment: DEFAULT_BULLISH_WEIGHTS.weightMaAlignment,
|
||||
weightMaSlope: DEFAULT_BULLISH_WEIGHTS.weightMaSlope,
|
||||
weightAdxTrend: DEFAULT_BULLISH_WEIGHTS.weightAdxTrend,
|
||||
weightMacdMomentum: DEFAULT_BULLISH_WEIGHTS.weightMacdMomentum,
|
||||
weightPricePosition: DEFAULT_BULLISH_WEIGHTS.weightPricePosition,
|
||||
minTrendScore: DEFAULT_TREND_SEARCH_REQUEST.minTrendScore,
|
||||
limit: DEFAULT_TREND_SEARCH_REQUEST.limit,
|
||||
scanLimit: DEFAULT_TREND_SEARCH_REQUEST.scanLimit,
|
||||
autoRefreshSeconds: 3,
|
||||
autoAddTargetsEnabled: false,
|
||||
autoAddTopRank: 5,
|
||||
autoAddMinScore: DEFAULT_TREND_SEARCH_REQUEST.minTrendScore,
|
||||
};
|
||||
|
||||
/** 자동 갱신 간격 선택지 (초) */
|
||||
export const TREND_SEARCH_REFRESH_OPTIONS = [3, 5, 10, 30, 60] as const;
|
||||
|
||||
export function resolveTrendSearchAppSettings(
|
||||
raw?: Partial<TrendSearchAppSettings> | null,
|
||||
): TrendSearchAppSettings {
|
||||
const d = DEFAULT_TREND_SEARCH_APP_SETTINGS;
|
||||
if (!raw || typeof raw !== 'object') return { ...d };
|
||||
const sec = Number(raw.autoRefreshSeconds);
|
||||
const rank = Number(raw.autoAddTopRank);
|
||||
const limit = Number(raw.limit);
|
||||
const scanLimit = Number(raw.scanLimit);
|
||||
return {
|
||||
weightMaAlignment: clampWeight(raw.weightMaAlignment, d.weightMaAlignment),
|
||||
weightMaSlope: clampWeight(raw.weightMaSlope, d.weightMaSlope),
|
||||
weightAdxTrend: clampWeight(raw.weightAdxTrend, d.weightAdxTrend),
|
||||
weightMacdMomentum: clampWeight(raw.weightMacdMomentum, d.weightMacdMomentum),
|
||||
weightPricePosition: clampWeight(raw.weightPricePosition, d.weightPricePosition),
|
||||
minTrendScore: clampInt(raw.minTrendScore, 0, 100, d.minTrendScore),
|
||||
limit: clampInt(limit, 5, 50, d.limit),
|
||||
scanLimit: clampInt(scanLimit, 20, 120, d.scanLimit),
|
||||
autoRefreshSeconds: TREND_SEARCH_REFRESH_OPTIONS.includes(sec as typeof TREND_SEARCH_REFRESH_OPTIONS[number])
|
||||
? sec
|
||||
: d.autoRefreshSeconds,
|
||||
autoAddTargetsEnabled: raw.autoAddTargetsEnabled === true,
|
||||
autoAddTopRank: clampInt(rank, 1, 50, d.autoAddTopRank),
|
||||
autoAddMinScore: clampInt(raw.autoAddMinScore, 0, 100, d.autoAddMinScore),
|
||||
};
|
||||
}
|
||||
|
||||
function clampWeight(v: unknown, fallback: number): number {
|
||||
const n = Number(v);
|
||||
if (!Number.isFinite(n)) return fallback;
|
||||
return Math.max(0, Math.min(100, Math.round(n / 5) * 5));
|
||||
}
|
||||
|
||||
function clampInt(v: unknown, min: number, max: number, fallback: number): number {
|
||||
const n = Number(v);
|
||||
if (!Number.isFinite(n)) return fallback;
|
||||
return Math.min(max, Math.max(min, Math.floor(n)));
|
||||
}
|
||||
|
||||
export function mergeTrendSearchRequest(
|
||||
base: TrendSearchRequest,
|
||||
settings: TrendSearchAppSettings,
|
||||
): TrendSearchRequest {
|
||||
return {
|
||||
...base,
|
||||
weightMaAlignment: settings.weightMaAlignment,
|
||||
weightMaSlope: settings.weightMaSlope,
|
||||
weightAdxTrend: settings.weightAdxTrend,
|
||||
weightMacdMomentum: settings.weightMacdMomentum,
|
||||
weightPricePosition: settings.weightPricePosition,
|
||||
minTrendScore: settings.minTrendScore,
|
||||
limit: settings.limit,
|
||||
scanLimit: settings.scanLimit,
|
||||
};
|
||||
}
|
||||
|
||||
/** 검색 요청 → 앱 설정에 반영할 필드 */
|
||||
export function extractTrendSearchAppSettings(
|
||||
req: Pick<TrendSearchRequest, BullishWeightKey | 'minTrendScore' | 'limit' | 'scanLimit'>,
|
||||
): Pick<
|
||||
TrendSearchAppSettings,
|
||||
BullishWeightKey | 'minTrendScore' | 'limit' | 'scanLimit'
|
||||
> {
|
||||
return {
|
||||
weightMaAlignment: req.weightMaAlignment,
|
||||
weightMaSlope: req.weightMaSlope,
|
||||
weightAdxTrend: req.weightAdxTrend,
|
||||
weightMacdMomentum: req.weightMacdMomentum,
|
||||
weightPricePosition: req.weightPricePosition,
|
||||
minTrendScore: req.minTrendScore,
|
||||
limit: req.limit,
|
||||
scanLimit: req.scanLimit,
|
||||
};
|
||||
}
|
||||
|
||||
export function formatTrendRefreshLabel(seconds: number): string {
|
||||
if (seconds < 60) return `${seconds}초`;
|
||||
return `${seconds / 60}분`;
|
||||
}
|
||||
Reference in New Issue
Block a user