Skip to content

Instantly share code, notes, and snippets.

@flying3615
Created November 12, 2025 22:36
Show Gist options
  • Select an option

  • Save flying3615/1d68faef742273d0c8d4e7a57fde8b37 to your computer and use it in GitHub Desktop.

Select an option

Save flying3615/1d68faef742273d0c8d4e7a57fde8b37 to your computer and use it in GitHub Desktop.
price action decision tree
// 建仓决策树页面 v1.0
// 基于交易形态指南的完整决策树系统
// 使用 React Flow 实现决策树可视化
'use client';
import { useCallback } from 'react';
import {
ReactFlow,
Node,
Edge,
addEdge,
Connection,
useNodesState,
useEdgesState,
Controls,
MiniMap,
Background,
BackgroundVariant,
Handle,
Position,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
// 自定义决策节点组件
function DecisionNode({
data,
}: {
data: { label: string; question?: string; details?: string };
}) {
return (
<div className='px-4 py-3 shadow-lg rounded-lg bg-white/90 backdrop-blur-sm border-2 border-blue-300 max-w-xs'>
<Handle type='target' position={Position.Top} />
<Handle type='source' position={Position.Bottom} />
<div className='font-bold text-sm text-slate-800'>{data.label}</div>
{data.question && (
<div className='text-xs text-slate-600 mt-1 font-medium'>
{data.question}
</div>
)}
{data.details && (
<div className='text-xs text-slate-500 mt-1 italic'>{data.details}</div>
)}
</div>
);
}
// 自定义结果节点组件
function ResultNode({
data,
}: {
data: {
label: string;
action?: string;
tooltip?: string;
details?: string;
category?: string;
};
}) {
const categoryColors = {
strong_trend: 'bg-red-50/90 border-red-400 text-red-800',
trend_continuation: 'bg-blue-50/90 border-blue-400 text-blue-800',
equilibrium: 'bg-orange-50/90 border-orange-400 text-orange-800',
reversal: 'bg-purple-50/90 border-purple-400 text-purple-800',
default: 'bg-emerald-50/90 border-emerald-400 text-emerald-800',
};
const colorClass =
categoryColors[data.category as keyof typeof categoryColors] ||
categoryColors.default;
return (
<div
className={`px-4 py-3 shadow-lg rounded-lg ${colorClass} max-w-xs`}
title={data.tooltip || 'Placeholder tooltip'}
>
<Handle type='target' position={Position.Top} />
<div className='font-bold text-sm'>{data.label}</div>
{data.action && (
<div className='text-xs mt-1 font-medium'>{data.action}</div>
)}
{data.details && (
<div className='text-xs mt-1 italic opacity-80'>{data.details}</div>
)}
</div>
);
}
// 节点类型映射
const nodeTypes = {
decision: DecisionNode,
result: ResultNode,
};
// 初始节点数据(交易决策树)
const initialNodes: Node[] = [
// 第一步:市场脉络判断
{
id: 'context',
type: 'decision',
position: { x: 500, y: 50 },
data: {
label: '市场脉络 (Context)',
question: '当前市场处于哪个阶段?',
details: 'K线形态、动能表现、回撤深度',
},
},
{
id: 'spike',
type: 'decision',
position: { x: -27.729411764705873, y: 193.91764705882352 },
data: {
label: '急速 (Spike)',
question: '连续顺畅趋势K线,回撤极浅?',
details: '穿透支撑/阻力,动能强劲',
},
},
{
id: 'tight_channel',
type: 'decision',
position: { x: 331.75294117647053, y: 240.14352941176463 },
data: {
label: '窄幅通道 (Tight Channel)',
question: '稍短趋势K线,有轻微停顿回调?',
details: '回调温和,趋势延续性强',
},
},
{
id: 'broad_channel',
type: 'decision',
position: { x: 618.2470588235294, y: 237.71058823529415 },
data: {
label: '宽幅通道 (Broad Channel)',
question: '回调明显、持续时间久、深度较大?',
details: '回撤明显,可能出现持续或反转',
},
},
{
id: 'trading_range',
type: 'decision',
position: { x: 957.0494117647056, y: 185.40235294117647 },
data: {
label: '区间 (Trading Range)',
question: '双边交易特征明显,多空势均力敌?',
details: '80%的时间市场处于这种状态',
},
},
// 强势趋势建仓形态分支
{
id: 'strong_trend_setups',
type: 'decision',
position: { x: 160.52705882352947, y: 358.515294117647 },
data: {
label: '强势趋势建仓',
question: '选择具体建仓形态',
details: '动能交易者策略,高胜率风格',
},
},
{
id: 'momentum_entry',
type: 'decision',
position: { x: -82.47058823529413, y: 517.0305882352941 },
data: {
label: '动能确认?',
question: '是否有明确突破K线?',
details: '穿透支撑/阻力,动能最强时入场',
},
},
{
id: 'close_based_entry',
type: 'result',
position: { x: -213.9741176470588, y: 703.524705882353 },
data: {
label: '收线追进 (T-MOM)',
action: '收线时市价追进突破方向',
details: '剥头皮交易风格,高胜率(60%),1.0倍盈亏比,1-5根K线',
category: 'strong_trend',
},
},
{
id: '20ma_gap_check',
type: 'decision',
position: { x: 224.4541176470588, y: 518.2470588235296 },
data: {
label: '均线缺口形态?',
question: '连续20根以上K线未触及均线?',
details: '价格远离均线后首次回撤',
},
},
{
id: 'ma_gap_type',
type: 'decision',
position: { x: 84.80941176470589, y: 665.8141176470588 },
data: {
label: '缺口类型',
question: '首次均线回撤还是20均线缺口?',
details: '微观多空竞合后顺势方重新占优',
},
},
{
id: 'first_ma_gap',
type: 'result',
position: { x: -46.69411764705882, y: 866.9058823529411 },
data: {
label: '第一均线缺口 (T-MAG)',
action: '信号K线外设止损,回调末端入场',
details: '剥头皮交易风格,高胜率(60%),1.0倍盈亏比',
category: 'strong_trend',
},
},
{
id: '20ma_gap',
type: 'result',
position: { x: 221.17882352941172, y: 868.1223529411764 },
data: {
label: '20均线缺口 (T-MAG)',
action: '重要波段点外设止损,同侧高/低1入场',
details: '剥头皮交易风格,60%胜率,强势趋势延续,利用均线防线',
category: 'strong_trend',
},
},
// 趋势持续与测试形态分支
{
id: 'continuation_setups',
type: 'decision',
position: { x: 928.4470588235295, y: 362.16470588235296 },
data: {
label: '趋势持续与测试',
question: '选择具体建仓形态',
details: '波段交易策略,中高盈亏比',
},
},
{
id: 'counter_test',
type: 'decision',
position: { x: 642.3270588235296, y: 486.61882352941166 },
data: {
label: '逆势测试?',
question: '是否出现逆势K线但动能不足?',
details: '低1失败,顺1成功是关键',
},
},
{
id: 'counter_pro_1',
type: 'result',
position: { x: 640.9858823529413, y: 662.1647058823529 },
data: {
label: '逆1顺1 (T-C1P1)',
action: '逆势止损后,顺势突破时入场',
details: '波段交易风格,2.0倍盈亏比,40%胜率',
category: 'trend_continuation',
},
},
{
id: 'breakout_test',
type: 'decision',
position: { x: 850.7176470588238, y: 585.1529411764704 },
data: {
label: '突破测试?',
question: '是否有成功突破后折返测试?',
details: '突破动能是否延续到第二推',
},
},
{
id: 'breakout_pullback',
type: 'result',
position: { x: 791.1105882352942, y: 778.9458823529412 },
data: {
label: '突破回调 (T-BOPB)',
action: '测试点确认后,逆势疲弱时入场',
details: '波段交易风格,2.0倍盈亏比,40%胜率,等待第二推,目标等距运动',
category: 'trend_continuation',
},
},
{
id: 'double_test',
type: 'decision',
position: { x: 984.7788235294116, y: 700.7176470588238 },
data: {
label: '双重测试?',
question: '是否两次测试同一水平失败?',
details: '磁体测试失败,逆势动能疲弱',
},
},
{
id: 'double_top_bottom',
type: 'result',
position: { x: 978.6964705882351, y: 881.1294117647058 },
data: {
label: '双重顶/底 (T-DTB)',
action: '第二次测试失败后顺势突破入场',
details: '波段交易风格,2.0倍盈亏比,40%胜率,旗形回调变体,目标AB=CD等距',
category: 'trend_continuation',
},
},
{
id: 'wedge_test',
type: 'decision',
position: { x: 1094.5105882352943, y: 486.61882352941166 },
data: {
label: '楔形结构?',
question: '是否三次推动失败,倾斜收敛?',
details: '楔形底/顶,逆势力量消耗',
},
},
{
id: 'wedge_top_bottom',
type: 'result',
position: { x: 1178.4470588235292, y: 618.3717647058825 },
data: {
label: '楔形顶/底 (T-WDG)',
action: '楔形突破后,趋势恢复时入场',
details: '波段交易风格,2.0倍盈亏比,40%胜率,三推失败,逆势方动能衰退',
category: 'trend_continuation',
},
},
// 均衡或区间形态分支
{
id: 'range_setups',
type: 'decision',
position: { x: 1217.3741176470587, y: 303.7741176470589 },
data: {
label: '均衡或区间形态',
question: '选择具体建仓形态',
details: '80%概率突破失败,逆势策略',
},
},
{
id: 'breakout_failure',
type: 'decision',
position: { x: 1326.6070588235298, y: 486.61882352941194 },
data: {
label: '突破失败?',
question: '是否出现突破但缺乏跟进?',
details: '影线可视为突破失败信号',
},
},
{
id: 'fade_breakout',
type: 'result',
position: { x: 1302.1529411764704, y: 763.1317647058822 },
data: {
label: '看衰突破 (E-FDB)',
action: '突破失败后,信号K线反向突破时做空',
details: '80%概率回归区间,逆势交易',
category: 'equilibrium',
},
},
{
id: 'magnet_rush',
type: 'decision',
position: { x: 1537.555294117647, y: 437.9599999999998 },
data: {
label: '磁体冲刺?',
question: '价格是否快速奔向明确磁体?',
details: '支撑/阻力、前波段点、均线等',
},
},
{
id: 'rush_to_magnet',
type: 'result',
position: { x: 1546.0705882352938, y: 765.564705882353 },
data: {
label: '急赴磁体 (E-RTM)',
action: '收线追进,目标是磁体位置',
details: '剥头皮交易风格,1.0倍盈亏比,60%胜率,动能追进,磁体处高潮结束',
category: 'equilibrium',
},
},
// 反转形态分支
{
id: 'reversal_setups',
type: 'decision',
position: { x: 450.37411764705894, y: 460.35529411764696 },
data: {
label: '反转形态',
question: '选择具体建仓形态',
details: '趋势末期,动能衰退期',
},
},
{
id: 'final_flag_check',
type: 'decision',
position: { x: 421.1788235294117, y: 691.8588235294118 },
data: {
label: '末端旗形?',
question: '趋势末期旗形未能引发延续?',
details: '关键磁体突破失败,逆势方占优',
},
},
{
id: 'final_flag_reversal',
type: 'result',
position: { x: 546.4752941176471, y: 878.3529411764705 },
data: {
label: '末端旗形反转 (R-FFL)',
action: '高潮后动能丧失,重要波段点外止损',
details: '波段交易风格,至少2.0倍盈亏比,40%胜率,大波段反转交易',
category: 'reversal',
},
},
];
// 初始边数据(交易决策树)
const initialEdges: Edge[] = [
// 市场脉络分支
{
id: 'context-spike',
source: 'context',
target: 'spike',
label: '急速',
type: 'smoothstep',
},
{
id: 'context-tight',
source: 'context',
target: 'tight_channel',
label: '窄幅通道',
type: 'smoothstep',
},
{
id: 'context-broad',
source: 'context',
target: 'broad_channel',
label: '宽幅通道',
type: 'smoothstep',
},
{
id: 'context-range',
source: 'context',
target: 'trading_range',
label: '区间',
type: 'smoothstep',
},
// 强势趋势分支
{
id: 'spike-strong',
source: 'spike',
target: 'strong_trend_setups',
label: '强顺势',
type: 'smoothstep',
},
{
id: 'tight-strong',
source: 'tight_channel',
target: 'strong_trend_setups',
label: '顺势为主',
type: 'smoothstep',
},
// 强势趋势具体形态
{
id: 'strong-momentum',
source: 'strong_trend_setups',
target: 'momentum_entry',
label: '动能突破',
type: 'smoothstep',
},
{
id: 'momentum-entry',
source: 'momentum_entry',
target: 'close_based_entry',
label: '确认突破',
type: 'smoothstep',
},
{
id: 'strong-ma-gap',
source: 'strong_trend_setups',
target: '20ma_gap_check',
label: '均线缺口',
type: 'smoothstep',
},
{
id: 'ma-gap-confirm',
source: '20ma_gap_check',
target: 'ma_gap_type',
label: '连续未触20线',
type: 'smoothstep',
},
{
id: 'gap-first',
source: 'ma_gap_type',
target: 'first_ma_gap',
label: '首次回撤',
type: 'smoothstep',
},
{
id: 'gap-20',
source: 'ma_gap_type',
target: '20ma_gap',
label: '20线缺口',
type: 'smoothstep',
},
// 趋势持续与测试分支
{
id: 'broad-continuation',
source: 'broad_channel',
target: 'continuation_setups',
label: '持续/测试',
type: 'smoothstep',
},
// 趋势持续具体形态
{
id: 'cont-counter',
source: 'continuation_setups',
target: 'counter_test',
label: '逆1顺1',
type: 'smoothstep',
},
{
id: 'counter-entry',
source: 'counter_test',
target: 'counter_pro_1',
label: '逆势失败',
type: 'smoothstep',
},
{
id: 'cont-breakout',
source: 'continuation_setups',
target: 'breakout_test',
label: '突破回调',
type: 'smoothstep',
},
{
id: 'breakout-entry',
source: 'breakout_test',
target: 'breakout_pullback',
label: '测试确认',
type: 'smoothstep',
},
{
id: 'cont-double',
source: 'continuation_setups',
target: 'double_test',
label: '双重顶/底',
type: 'smoothstep',
},
{
id: 'double-entry',
source: 'double_test',
target: 'double_top_bottom',
label: '两次失败',
type: 'smoothstep',
},
{
id: 'cont-wedge',
source: 'continuation_setups',
target: 'wedge_test',
label: '楔形结构',
type: 'smoothstep',
},
{
id: 'wedge-entry',
source: 'wedge_test',
target: 'wedge_top_bottom',
label: '三推失败',
type: 'smoothstep',
},
// 区间形态分支
{
id: 'range-setups',
source: 'trading_range',
target: 'range_setups',
label: '双边交易',
type: 'smoothstep',
},
// 区间具体形态
{
id: 'range-fade',
source: 'range_setups',
target: 'breakout_failure',
label: '看衰突破',
type: 'smoothstep',
},
{
id: 'fade-entry',
source: 'breakout_failure',
target: 'fade_breakout',
label: '突破失败',
type: 'smoothstep',
},
{
id: 'range-rush',
source: 'range_setups',
target: 'magnet_rush',
label: '急赴磁体',
type: 'smoothstep',
},
{
id: 'rush-entry',
source: 'magnet_rush',
target: 'rush_to_magnet',
label: '磁体冲刺',
type: 'smoothstep',
},
// 反转形态分支
{
id: 'broad-reversal',
source: 'broad_channel',
target: 'reversal_setups',
label: '反转可能',
type: 'smoothstep',
},
{
id: 'reversal-flag',
source: 'reversal_setups',
target: 'final_flag_check',
label: '末端旗形',
type: 'smoothstep',
},
{
id: 'final-entry',
source: 'final_flag_check',
target: 'final_flag_reversal',
label: '动能丧失',
type: 'smoothstep',
},
];
export default function DecisionTreePage() {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback(
(params: Connection) => setEdges((eds) => addEdge(params, eds)),
[setEdges]
);
return (
<div className='w-full h-screen bg-gradient-to-br from-slate-50 to-slate-100'>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
nodeTypes={nodeTypes}
fitView
attributionPosition='top-right'
className='bg-transparent'
defaultEdgeOptions={{
style: { stroke: '#374151', strokeWidth: 2 },
type: 'smoothstep',
}}
>
<Controls className='bg-white/80 backdrop-blur-sm border border-slate-200 rounded-lg shadow-lg' />
<MiniMap className='bg-white/80 backdrop-blur-sm border border-slate-200 rounded-lg shadow-lg' />
<Background
variant={BackgroundVariant.Dots}
gap={16}
size={1}
className='opacity-30'
/>
</ReactFlow>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment