- {INTRADAY_DAY_OPTIONS.map(days => (
+ {dayOptions.map(days => (
)}
+ {draft.type === 'volume_delta' && (
+
+
+
+
阈值口径
+
+ {([['volume', '按手数'], ['amount', '按金额']] as const).map(([key, label]) => (
+
+ ))}
+
+
+
+
+
+
+
基础过滤 (与策略选股口径对齐, 留空不过滤)
+
+
+
+
+
+
+
+
+
+
+
+ 捕捉单次轮询间隔内的突发放量 (大单连续扫货)。开盘首轮与暂停恢复后的第一轮不触发,
+ 防止集合竞价撮合量误报; 冷却期内同一标的不重复提醒, 命中超过 5 只时合并为一条批量通知。
+
+
+ )}
+
{/* 作用范围 */}
{draft.type !== 'sector' &&
作用范围
@@ -1106,7 +1247,7 @@ export function RuleEditor({ rule, preset, simple, onClose, onSaved }: Props) {
}
{/* 触发条件 (非 strategy) */}
- {draft.type !== 'strategy' && draft.type !== 'sector' && draft.type !== 'abnormal' && (
+ {draft.type !== 'strategy' && draft.type !== 'sector' && draft.type !== 'abnormal' && draft.type !== 'volume_delta' && (
触发条件
@@ -1402,7 +1543,7 @@ export function RuleEditor({ rule, preset, simple, onClose, onSaved }: Props) {
return (
{unconfigured.join('、')}尚未配置,
- 前往设置页配置 →
+ 前往设置页配置 →
)
})()}
diff --git a/frontend/src/components/screener/StrategyCard.tsx b/frontend/src/components/screener/StrategyCard.tsx
index f2efbff..7a9a580 100644
--- a/frontend/src/components/screener/StrategyCard.tsx
+++ b/frontend/src/components/screener/StrategyCard.tsx
@@ -92,12 +92,14 @@ interface StrategyCardProps {
monitored?: boolean
/** 切换策略监控 (点击 RadioTower 图标) */
onToggleMonitor?: () => void
+ /** 周期徽章 (如 '分钟'); 日线策略不传 */
+ timeframeBadge?: string
}
export function StrategyCard({
name, description, source, active, count, expiredCount,
loading, cardSize,
- onRun, disabled, onSettings, monitored, onToggleMonitor,
+ onRun, disabled, onSettings, monitored, onToggleMonitor, timeframeBadge,
}: StrategyCardProps) {
const cs = CARD_STYLES[cardSize]
const activeCls = active
@@ -125,6 +127,9 @@ export function StrategyCard({
className="flex flex-col items-start cursor-pointer disabled:opacity-50 disabled:cursor-wait w-full">
{srcLabel}
+ {timeframeBadge && (
+ {timeframeBadge}
+ )}
{name}
{description && (
@@ -164,6 +169,9 @@ export function StrategyCard({
className="flex flex-col items-start cursor-pointer disabled:opacity-50 disabled:cursor-wait min-w-0">
{srcLabel}
+ {timeframeBadge && (
+
{timeframeBadge}
+ )}
{name}
{count != null && !loading && (
{count}
diff --git a/frontend/src/components/screener/StrategyPoolDialog.tsx b/frontend/src/components/screener/StrategyPoolDialog.tsx
index d6dcc21..31f6b47 100644
--- a/frontend/src/components/screener/StrategyPoolDialog.tsx
+++ b/frontend/src/components/screener/StrategyPoolDialog.tsx
@@ -24,6 +24,8 @@ const SOURCE_LABEL: Record
= {
invalid: '失效',
}
+const TF_BADGE_CLS = 'text-[8px] px-1 py-px rounded border leading-tight shrink-0 border-sky-500/30 bg-sky-500/10 text-sky-400'
+
type SourceTab = 'all' | 'builtin' | 'custom' | 'ai'
const TABS: { id: SourceTab; label: string }[] = [
@@ -57,7 +59,8 @@ export function StrategyPoolDialog({ pool, onConfirm, onClose }: Props) {
const loadStrategies = useCallback(async () => {
setLoading(true)
try {
- const d = await api.strategyList()
+ // 不按周期过滤: 日线+分钟策略合并展示, 分钟策略以徽章区分
+ const d = await api.strategyList(undefined, 'all')
setAllStrategies(d.strategies)
} catch {
setAllStrategies([])
@@ -236,6 +239,9 @@ export function StrategyPoolDialog({ pool, onConfirm, onClose }: Props) {
{SOURCE_LABEL[s.source] ?? '内置'}
+ {s.timeframes?.includes('1m') && (
+ 分钟
+ )}
))}
@@ -280,6 +286,9 @@ export function StrategyPoolDialog({ pool, onConfirm, onClose }: Props) {
{SOURCE_LABEL[src] ?? '内置'}
+ {s?.timeframes?.includes('1m') && (
+ 分钟
+ )}