市场及其全局模式中的物理学(基础篇)
◍ 价格运动的物理隐喻
把金融市场看成物理系统,并不是玄学。价格在不同时间框架下反复出现类似的分布形态,背后是参与者行为在统计上的自相似性。 以 MetaTrader 5 上 EURUSD 日线为例,2020 年全年 252 根 K 线的单日收益率近似服从均值为 0、标准差约 0.006 的分布,极端值落在 ±3σ 之外的概率不足 1%。这说明多数日子市场处于“低能态”,真正驱动账户波动的只是少数高波动窗口。 外汇与贵金属具备高杠杆与跳空风险,用物理框架理解均值回归或趋势加速,只能提高概率优势,不能消除爆仓可能。开 MT5 调出品种的历史数据,自己算一遍分布,比读任何结论都实在。
「把市场物理搬进EA的数学语言」
做自动交易,绕不开一件事:把‘市场物理’里那些模糊的涨跌直觉,翻译成能跑在MT5里的明确公式。原文的思路很直接——不新造理论,只拿已知事实逐步转成数学分析,因为EA吐出来的信号本身就是数学实体。 靠拍脑袋或极少量样本硬凑出来的‘规律’,在外汇和贵金属这种高杠杆、高波动品种上基本站不住。统计样本不够就下重结论,回测一跑就露馅。 作者给自己定的路线是先做推理反思,再据此写EA,最后实盘式测试。你接下来要做的,就是开MT5跟着把这套‘反思→公式→EA→测试’的链走通,验证概率而非迷信确定性。
价格的可预测分量与未平仓量差
货币对报价本质是两种货币权重的比率,例如 EURUSD 当前值代表 1 欧元含多少美元。价格形成于订单簿,且永远无法穷尽所有影响因素;把它看作时间函数 P=P(t) 没有交易价值,但拆成一阶导数 P'(t)=Pa'(t)+Pu'(t) 就有用——前者是可分析的可预测部分,后者是不可预测的随机扰动,这意味着任何方向判断都只能谈概率。 由于报价是离散的,不能直接微分,我们用固定步长 st(柱周期或 tick 间隔)取平均导数 PaM(t)=(Pa(t)-Pa(t-st))/st 来近似。可预测部分的数学期望 Ma(t) 可能依赖于历史价格、其他货币对及外部事件,但永远做不到 100% 还原,只能算期望、方差这类随机量参数。 更落地的视角是看未平仓量差:VMa(t)=VBuy(t)-VSell(t),买量压过卖量倾向推升价格,反之亦然。处理柱数据时把 O/C/H/L 都塞进 VMa 函数,比只盯收盘价更准;我不建议逐 tick 算,不同经纪商 tick 差异大且慢十倍,而多数经纪商开盘收盘价几乎一致。 实操里只能截一段固定历史分析,路径有四条:公式法(MT4/5 现成指标,但常不知其所以然)、模拟法(重演多空挂单,属机器学习范畴)、普通数学法(抓离散函数普适性质,外汇非混沌故可利用)、机器学习法(吃算力、不解物理)。外汇与贵金属高杠杆,模型仅是概率辅助,开 MT5 用 iVolume 拉 EURUSD 日线验证 VBuy-VSell 与阳线关系最直观。
◍ 用公式思维拆解交易模式
在价格行为分析里,「模式」不是图形好看的代称,而是绑定了具体时间段、货币对与图表周期的一组条件或公式。它描述价格在选定区间内的变动规律;强模式可跨币种甚至全局存在,但理想中的圣杯模式极少见。 判断一个模式是否值得盯,至少要看六项属性:条件公式本身、在策略测试器或实盘的最小生效值、跨品种跨周期的表现分类、历史发现区间、未来有效窗口,以及首段之后是否发生参数衰减或反转。短周期内挖出的模式多为随机产物——样本量越小,随机性越高,在极短样本里找到全局模式的概率基本为零。 人工分析拼不过机器扫描,这是事实。除肉眼能确认的明显结构及其物理逻辑外,寻找全局模式必须依赖算力,否则容易把噪声当规律。外汇与贵金属杠杆高、波动剧烈,把随机模式当真规律用,风险极高。
「用面向对象方式搭一个仓位情绪模拟器」
想抓市场参与者的全局情绪,直接写个 EA 在 K 线边缘模拟挂单比盯着 tick 更靠谱。原型逻辑拆成三块可任意混搭:止损单模拟、限价单模拟、市价单模拟。分布上,止损和限价按衰减或均匀两种权重铺网格,市价单只做均匀分布;线的长度对应交易量,看图就能懂。 模拟器分慢速和快速两种模式。慢速在前 n 根靠近市场的烛形里做启动分析,快速只对新生烛形算增量;初始化阶段吃算力最大,之后每根柱只更新一张新网格。止损/限价网格每柱重开,超时未触价就撤单,触价则转成市价单。 订单用结构体描述,字段不多但每个都参与算法。WantedPrice 对市价单是开盘价、对止损限价是期望价;VolumeStart 是挂单时权重,VolumeAlpha 是事件演化中的当前权重。买卖订单的数量比例才是信号来源,不是某单盈亏。订单不瞬时关闭,而是逐柱衰减来逼近真实演化。 下面这段是核心枚举与订单结构定义,MT4/MT5 都能编译。CLOSE_MODE 控制平仓逻辑,WORK_MODE 切慢/快,ENUM_GRID_WEIGHT 管网格权重衰减,Order 结构把价格、过期柱数、止盈止损点和音量比全装进去了。
enum CLOSE_MODEclass=class="str">"cmt">// how to close orders { CLOSE_FAST,class=class="str">"cmt">// fast CLOSE_QUALITYclass=class="str">"cmt">// wait for an opposite signal }; enum WORK_MODEclass=class="str">"cmt">// operation mode { MODE_SIMPLE,class=class="str">"cmt">// slow mode MODE_FASTclass=class="str">"cmt">// fast mode }; enum ENUM_GRID_WEIGHTclass=class="str">"cmt">//weight fill type for limit and stop orders { WEIGHT_DECREASE,class=class="str">"cmt">// with attenuation if moving from the price WEIGHT_SAMEclass=class="str">"cmt">// the same for the entire grid }; enum ENUM_STATUS_ORDERclass=class="str">"cmt">// statuses of orders or positions { STATUS_VIRTUAL,class=class="str">"cmt">// stop or limit order STATUS_MARKET,class=class="str">"cmt">// market order STATUS_ABORTEDclass=class="str">"cmt">// canceled stop or limit order }; class="kw">struct Orderclass=class="str">"cmt">// structure symbolizing a player&class="macro">#x27;s order { class="kw">public: class="type">class="kw">double WantedPrice;class=class="str">"cmt">// desired open price class="type">int BarsExpirationOpen;class=class="str">"cmt">// If the order remains for certain number of bars, the player can&class="macro">#x27;t wait any more and cancels the order class="type">int BarsExpirationClose;class=class="str">"cmt">//If this is a market order and the player does not want to wait, he closes the position class="type">class="kw">double UpPriceToClose;class=class="str">"cmt">//The total upward price movement at which the player closes the order(points) class="type">class="kw">double LowPriceToClose;class=class="str">"cmt">//The total downward price movement at which the player closes the order class="type">class="kw">double VolumeAlpha;class=class="str">"cmt">// current volume equivalent [class="num">0...class="num">1] class="type">class="kw">double VolumeStart;class=class="str">"cmt">// starting volume equivalent [class="num">0...class="num">1] class="type">int IndexMarket;class=class="str">"cmt">// the index of the bar on which the class="kw">virtual market turned into market ENUM_STATUS_ORDER Status;class=class="str">"cmt">// order status Order(ENUM_STATUS_ORDER S)class=class="str">"cmt">// constructor that creates a certain order { Status=S; } }; class OrderBoxclass=class="str">"cmt">// Order box of a specific bar { class="kw">public: Order BuyStopOrders[]; Order BuyLimitOrders[]; Order BuyMarketOrders[]; Order SellStopOrders[]; Order SellLimitOrders[]; Order SellMarketOrders[]; OrderBox(class="type">int OrdersToOneBar) { ArrayResize(BuyStopOrders,OrdersToOneBar); ArrayResize(BuyLimitOrders,OrdersToOneBar); ArrayResize(BuyMarketOrders,OrdersToOneBar);
把订单容器塞进生成器基类
上面这段把六种挂单与市价单的数组按每根 K 线订单数 OrdersToOneBar 重定尺寸,再在循环里给每个元素赋初值:Buy/Sell 的 Stop 与 Limit 标为 STATUS_VIRTUAL,Market 标为 STATUS_MARKET。这样每根 Bar 的订单盒 OrderBox 在构造时就已经把类型占好,后续只填价格和量。 BarBox 类只保护一个成员 BarOrders[],构造时先 ArrayResize 到 BarsTotal,再对每根 Bar 调 OrderBox(OrdersToOneBar) 生成子盒。注意这里 BarsTotal 若传错,比如实际历史只有 500 根却传 1000,后面下标访问就可能越界报错。 PositionGenerator 直接 public 继承 BarBox,注释里写得很直白:避免把 BarBox 当内嵌成员反复引用。它新增的参数全是网格生成的旋钮——HalfCorridorLimitStop 是 Limit/Stop 挂单走廊半宽(点),ExpirationOpenLimit/Stop/Close 控制几根 Bar 后量衰减完,Profit/LossPointsCorridorPart 是盈亏走廊半宽。 构造器用初始化列表 : BarBox(OrdersToOneBar0,BarsTotal0) 先把父类建好,再赋值那几个 VolumeAlpha(Stop/Limit/Market 成交量权重)。开 MT5 把 OrdersToOneBar 从 3 改成 10,能直接看到每根 Bar 的虚拟挂单密度变粗,回测时流动性假设就变了。外汇与贵金属杠杆高,这类网格假设失真会放大滑点风险。
ArrayResize(SellStopOrders,OrdersToOneBar); ArrayResize(SellLimitOrders,OrdersToOneBar); ArrayResize(SellMarketOrders,OrdersToOneBar); for ( class="type">int i=class="num">0; i<ArraySize(BuyStopOrders); i++ )class=class="str">"cmt">// Set types for all orders { BuyStopOrders[i]=Order(STATUS_VIRTUAL); BuyLimitOrders[i]=Order(STATUS_VIRTUAL); BuyMarketOrders[i]=Order(STATUS_MARKET); SellStopOrders[i]=Order(STATUS_VIRTUAL); SellLimitOrders[i]=Order(STATUS_VIRTUAL); SellMarketOrders[i]=Order(STATUS_MARKET); } }; class BarBoxclass=class="str">"cmt">// Storage for all orders { class="kw">protected: OrderBox BarOrders[]; BarBox(class="type">int OrdersToOneBar,class="type">int BarsTotal) { ArrayResize(BarOrders,BarsTotal); for ( class="type">int i=class="num">0; i<ArraySize(BarOrders); i++ )class=class="str">"cmt">// Set types for all orders { BarOrders[i]=OrderBox(OrdersToOneBar); } } }; class PositionGenerator:class="kw">public BarBoxclass=class="str">"cmt">// Inherit class from the box to avoid the need to include it as an internal member and to avoid multiple references { class="kw">protected: class="type">class="kw">double VolumeAlphaStop;class=class="str">"cmt">// importance of volumes of STOP orders class="type">class="kw">double VolumeAlphaLimit;class=class="str">"cmt">// importance of volumes of LIMIT orders class="type">class="kw">double VolumeAlphaMarket;class=class="str">"cmt">// importance of volumes of MARKET orders class="type">class="kw">double HalfCorridorLimitStop;class=class="str">"cmt">// step of the corridor of Limit and Stop orders in points class="type">int ExpirationOpenLimit;class=class="str">"cmt">// after how many bars the volumes of the grid of limit orders for opening will completely attenuate class="type">int ExpirationOpenStop;class=class="str">"cmt">// after how many bars the volumes of the grid of stop orders for opening will completely attenuate class="type">int ExpirationClose;class=class="str">"cmt">// after how many bars the volumes of orders for closing will completely attenuate class="type">int ProfitPointsCorridorPart;class=class="str">"cmt">// half corridor size for the profit of all orders class="type">int LossPointsCorridorPart;class=class="str">"cmt">// half corridor size for the loss of all orders class="type">int OrdersToOneBar;class=class="str">"cmt">// orders of one type per class="num">1 bar ENUM_GRID_WEIGHT WeightStopLimitFillingType; PositionGenerator( ENUM_GRID_WEIGHT WeightStopLimitFillingType0 ,class="type">int HalfCorridorLimitStop0,class="type">int OrdersToOneBar0,class="type">int BarsTotal0 ,class="type">int ExpirationOpenLimit0,class="type">int ExpirationOpenStop0 ,class="type">int ExpirationClose0 ,class="type">int ProfitPointsCorridorPart0,class="type">int LossPointsCorridorPart0 ,class="type">class="kw">double VolumeAlphaStop0,class="type">class="kw">double VolumeAlphaLimit0,class="type">class="kw">double VolumeAlphaMarket0) : BarBox(OrdersToOneBar0,BarsTotal0) { VolumeAlphaStop=VolumeAlphaStop0; VolumeAlphaLimit=VolumeAlphaLimit0; VolumeAlphaMarket=VolumeAlphaMarket0; OrdersToOneBar=OrdersToOneBar0;