<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Trading on Website of SauceWu</title><link>https://saucewu.github.io/tags/trading/</link><description>Recent content in Trading on Website of SauceWu</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Mon, 31 Mar 2025 12:45:00 +0900</lastBuildDate><atom:link href="https://saucewu.github.io/tags/trading/index.xml" rel="self" type="application/rss+xml"/><item><title>Flutter K线系统拆解（二）：指标引擎与增量计算</title><link>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E4%BA%8C%E6%8C%87%E6%A0%87%E5%BC%95%E6%93%8E%E4%B8%8E%E5%A2%9E%E9%87%8F%E8%AE%A1%E7%AE%97/</link><pubDate>Mon, 31 Mar 2025 12:45:00 +0900</pubDate><guid>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E4%BA%8C%E6%8C%87%E6%A0%87%E5%BC%95%E6%93%8E%E4%B8%8E%E5%A2%9E%E9%87%8F%E8%AE%A1%E7%AE%97/</guid><description>&lt;h1 id="flutter-k线系统拆解二指标引擎与增量计算"&gt;Flutter K线系统拆解（二）：指标引擎与增量计算&lt;/h1&gt;
&lt;h2 id="目标"&gt;目标&lt;/h2&gt;
&lt;p&gt;这篇只讲一件事：&lt;br&gt;
如何把 K 线指标计算做成&lt;strong&gt;高性能、可维护、可测试&lt;/strong&gt;的生产级引擎。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一最佳实践架构三层拆分"&gt;一、最佳实践架构：三层拆分&lt;/h2&gt;
&lt;p&gt;指标模块建议拆成三层：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;IndicatorConfig&lt;/code&gt;（配置层）：维护 MA/BOLL/MACD 等参数与开关&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IndicatorEngine&lt;/code&gt;（计算层）：只负责输入数据、输出结果，不关心 UI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ChartAdapter&lt;/code&gt;（消费层）：把结果喂给渲染，不做任何业务计算&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;核心原则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;渲染层只读结果，不参与计算&lt;/li&gt;
&lt;li&gt;计算层纯函数化，避免隐式全局状态&lt;/li&gt;
&lt;li&gt;配置变更和行情更新走不同刷新路径&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="二指标计算顺序要固定"&gt;二、指标计算顺序要固定&lt;/h2&gt;
&lt;p&gt;生产上要明确“依赖顺序”，不能随意调整。&lt;/p&gt;
&lt;p&gt;推荐顺序：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;输入 &lt;code&gt;List&amp;lt;KLineEntity&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;MA&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;BOLL&lt;/code&gt;（依赖 MA）&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;Volume MA&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;KDJ&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;MACD&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;RSI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;WR&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;SuperTrend&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;结果回填到每个 &lt;code&gt;KLineEntity&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;渲染层直接读取实体字段&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;建议把顺序写进统一入口方法，避免外部随意单独调用。&lt;/p&gt;</description></item><item><title>Flutter K线系统拆解（一）：架构分层与领域模型</title><link>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E4%B8%80%E6%9E%B6%E6%9E%84%E5%88%86%E5%B1%82%E4%B8%8E%E9%A2%86%E5%9F%9F%E6%A8%A1%E5%9E%8B/</link><pubDate>Thu, 20 Mar 2025 12:30:00 +0900</pubDate><guid>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E4%B8%80%E6%9E%B6%E6%9E%84%E5%88%86%E5%B1%82%E4%B8%8E%E9%A2%86%E5%9F%9F%E6%A8%A1%E5%9E%8B/</guid><description>&lt;h1 id="flutter-k线系统拆解一架构分层与领域模型"&gt;Flutter K线系统拆解（一）：架构分层与领域模型&lt;/h1&gt;
&lt;p&gt;K 线在 demo 阶段通常不难，难的是进入真实交易场景之后的长期维护。&lt;br&gt;
一旦业务进入高频迭代，常见问题很快会暴露出来：主题体系耦合、模式分支散落、改动范围不可控、扩展成本越来越高。&lt;/p&gt;
&lt;p&gt;这一篇只讲架构层面的核心问题：&lt;br&gt;
如何把 K 线从“能画出来的组件”做成“可长期演进的系统”。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一推荐的系统分层"&gt;一、推荐的系统分层&lt;/h2&gt;
&lt;p&gt;一套可维护的 K 线实现，通常至少要有这几层：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;入口与装配层：图表 widget 生命周期与运行时状态
SDK 抽象层：主题、国际化、事件、屏幕适配等外部能力
数据与指标层：行情实体、指标结果、计算引擎
渲染核心层：坐标、可视区、渲染器管理
渲染阶段层：背景/网格/主图/十字线/叠加层等阶段化执行
策略扩展层：现货、合约等模式差异封装
工具叠加层：订单标记、绘图工具、信息浮层
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;分层的价值不在“看起来规范”，而在于改动隔离：&lt;br&gt;
新增一个能力时，只需要改对应层，不会把整个系统拖下水。&lt;/p&gt;</description></item><item><title>交易所 WebSocket 连接管理踩坑录</title><link>https://saucewu.github.io/posts/%E4%BA%A4%E6%98%93%E6%89%80-websocket-%E8%BF%9E%E6%8E%A5%E7%AE%A1%E7%90%86%E8%B8%A9%E5%9D%91%E5%BD%95/</link><pubDate>Sat, 21 May 2022 10:00:00 +0900</pubDate><guid>https://saucewu.github.io/posts/%E4%BA%A4%E6%98%93%E6%89%80-websocket-%E8%BF%9E%E6%8E%A5%E7%AE%A1%E7%90%86%E8%B8%A9%E5%9D%91%E5%BD%95/</guid><description>&lt;h1 id="交易所-websocket-连接管理踩坑录"&gt;交易所 WebSocket 连接管理踩坑录&lt;/h1&gt;
&lt;p&gt;交易所 App 对实时连接的依赖极高，行情、深度、订单状态全靠 WebSocket 推送，连接一断用户就处于盲区。从基础到深度整理踩过的坑。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一重连与指数退避"&gt;一、重连与指数退避&lt;/h2&gt;
&lt;p&gt;裸写的 &lt;code&gt;WebSocket.connect&lt;/code&gt; 断了就断了，没有自愈能力：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-dart" data-lang="dart"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Future&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; connect(&lt;span style="color:#66d9ef"&gt;String&lt;/span&gt; url) &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;_disposed &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; _retryCount &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; _maxRetries) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _socket &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; WebSocket.connect(url);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _retryCount &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; _socket&lt;span style="color:#f92672"&gt;!&lt;/span&gt;.listen(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; onData: _onMessage,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; onDone: () &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; _scheduleReconnect(url),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; onError: (_) &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; _scheduleReconnect(url),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cancelOnError: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ).asFuture();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; (_) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _scheduleReconnect(url);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; _scheduleReconnect(&lt;span style="color:#66d9ef"&gt;String&lt;/span&gt; url) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (_disposed) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; delay &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _baseDelay &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; _retryCount.clamp(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt;)); &lt;span style="color:#75715e"&gt;// 1s → 2s → 4s … → 64s
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _retryCount&lt;span style="color:#f92672"&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Future.delayed(delay, () &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; connect(url));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;指数退避必须加。服务端重启时如果所有客户端同时重连，会瞬间打死服务器。&lt;/p&gt;</description></item></channel></rss>