不使用期权的期权交易(第一部分):基础理论与基于标的资产的模拟实现·进阶篇
(2/3)· 现实市场买不到的奇异期权与价差组合,靠买卖底层资产就能在代码里拼出来
很多交易者把期权想成必须开户交易所才能碰的工具,遇到想要的到期日或行权价不在挂牌列表里就放弃策略。其实用标的资产的买卖流,就能在 MT5 里拼出理论上的任意期权结构,包括场内根本不存在的奇异合约。
把δ轴重映射成点位偏移
图例2里单张买入看涨期权的δ在0到+1之间游走。把它从绝对价格换成形变坐标,只需用标的价减掉行权价——本例是31000,X轴就变成「相对行权价的点位变动」。 重画之后(图例3),垂直虚线标的是绘图当下,那一点的δ读数是0.48。也就是说,标的价格比31000高出不多时,这张call的Delta还不到半张期货当量。 红线是2025年5月22日的剖面,蓝线是2025年6月18日到期时刻(对应BS公式里T=1)。临近到期那条曲线更陡,说明同样的点位偏移在末段对δ的拉动更极端。外汇与贵金属期权杠杆高、时间衰减非线性,用这套映射自查持仓前先开MT5把行权价和当前价填进去核对一遍。
◍ 用Sigmoid把期权δ映射成可模拟的仓位曲线
布莱克-斯科尔斯模型依赖隐含波动率,实盘里往往拿不到,退一步用历史波动率(HV)更现实:月期权取月均HV,周期权取周均HV。为跨标的对比并支持合成工具,改用相对价格变动并拿HV做归一化,公式就是 相对价格 = (price - 行权价) / HV。 以存续期HV=5000、行权价31000的看涨多头为例,在2025年5月22日标的价格30800时δ实测为0.48,反推得当前Sigmoid系数K=2;到期日2025年6月18日蓝线对应T=1。价格等于行权价那一点δ恰好0.5,红线就是标准S型函数 1/(1+exp(-K*(x-S))),这里S=0。 把S设成0.5,曲线沿X轴右移,价格在0处δ接近0、两端渐近0和1,这种形态更适合做波动率区间归一化,后面写MQL5的EA就固定用这版。为简化模拟,全程把K当常数、忽略时间衰减,对买看涨偏利好、买看跌偏不利,但组合层面盈亏扰动有限。
| 基类挂到MQL内置CObject下,方便用CList拼任意复杂度的期权组合;成员含 type_enum、strike、δ、norm_price,GetSigmoidValue留成纯虚函数让子类各自实现。实盘再平衡靠δ阈值触发,默认把 | 0~1 | δ区间切成10档,总手数上限 = Vmin_size + Vmin_delta_size×(10-1),Vmin_size与Vmin_delta_size通常取等但并非强制。 |
|---|
下面四类期权的δ曲线都用同一套Sigmoid骨架,区别只在符号与自变量翻转。Long Call在深度实值δ→1等同满仓多标的,虚值δ→0持仓归零;Long Put深度实值δ→-1等同满仓空标的。Short Call与Short Put则是反向敞口,虚值归零时真实期权已落袋权利金收益。
class=class="str">"cmt">// ===================================================================== // Long Call type option class // ===================================================================== class OptionLongCall : class="kw">public TOptionBase { class="kw">public: // --------------------------------------------------------------------- // Constructor: // --------------------------------------------------------------------- OptionLongCall(const class="type">class="kw">double _k, const class="type">class="kw">double _s, const class="type">int _digits) : TOptionBase(ENUM_OPTION_CALL_LONG, _k, _s, _digits) { } class="kw">protected: // --------------------------------------------------------------------- // Get the normalized delta value: // --------------------------------------------------------------------- class="type">class="kw">double GetSigmoidValue(const class="type">class="kw">double _x) class="kw">override { class="kw">return(class="num">1.0 / (class="num">1.0 + MathExp(-this.koeff_K * (_x - this.koeff_S)))); } }; // ------------------------------------------------------------------- class=class="str">"cmt">// ===================================================================== // Long Put type option class // ===================================================================== class OptionLongPut : class="kw">public TOptionBase { class="kw">public: // --------------------------------------------------------------------- // Constructor: // --------------------------------------------------------------------- OptionLongPut(const class="type">class="kw">double _k, const class="type">class="kw">double _s, const class="type">int _digits) : TOptionBase(ENUM_OPTION_PUT_LONG, _k, _s, _digits) { } class="kw">protected: // --------------------------------------------------------------------- // Get the normalized delta value: // --------------------------------------------------------------------- class="type">class="kw">double GetSigmoidValue(const class="type">class="kw">double _x) class="kw">override { class="kw">return(-class="num">1.0 / (class="num">1.0 + MathExp(-this.koeff_K * (-_x - this.koeff_S)))); } }; // ------------------------------------------------------------------- class=class="str">"cmt">// ===================================================================== // Short Call type option class // ===================================================================== class OptionShortCall : class="kw">public TOptionBase { class="kw">public: // --------------------------------------------------------------------- // Constructor: // --------------------------------------------------------------------- OptionShortCall(const class="type">class="kw">double _k, const class="type">class="kw">double _s, const class="type">int _digits) : TOptionBase(ENUM_OPTION_CALL_SHORT, _k, _s, _digits) { } class="kw">protected: // --------------------------------------------------------------------- // Get the normalized delta value: // --------------------------------------------------------------------- class="type">class="kw">double GetSigmoidValue(const class="type">class="kw">double _x) class="kw">override { class="kw">return(-class="num">1.0 / (class="num">1.0 + MathExp(-this.koeff_K * (_x - this.koeff_S)))); } }; // ------------------------------------------------------------------- class=class="str">"cmt">// ===================================================================== // Short Put type option class // ===================================================================== class OptionShortPut : class="kw">public TOptionBase { class="kw">public: // --------------------------------------------------------------------- // Constructor: // --------------------------------------------------------------------- OptionShortPut(const class="type">class="kw">double _k, const class="type">class="kw">double _s, const class="type">int _digits) : TOptionBase(ENUM_OPTION_PUT_SHORT, _k, _s, _digits) { } class="kw">protected: // --------------------------------------------------------------------- // Get the normalized delta value: // --------------------------------------------------------------------- class="type">class="kw">double GetSigmoidValue(const class="type">class="kw">double _x) class="kw">override { class="kw">return(class="num">1.0 / (class="num">1.0 + MathExp(-this.koeff_K * (-_x - this.koeff_S)))); } }; // -------------------------------------------------------------------
「用 Sigmoid 给 Short Put 的 Delta 做归一化」
做期权类策略时,裸写 Delta 很容易在不同行权价之间失去可比性。这段 MQL5 把 Short Put 单独拆成一个继承自 TOptionBase 的子类,核心只重写一个 GetSigmoidValue,把价格偏移喂进逻辑斯蒂曲线。 关键在系数用法:return(1.0 / (1.0 + MathExp(-this.koeff_K * (-_x - this.koeff_S))))。注意它把传入的 _x 取了负,再减 koeff_S,相当于把 S 曲线中心平移到 koeff_S 对应的价位,koeff_K 控制陡峭度。在 MT5 里把 koeff_K 从 1.0 调到 5.0,同样 20 点偏离下归一化 Delta 会从约 0.27 跳到 0.98,敏感度差异极大。 基类用 CObject 派生是为了能直接塞进 CTypedList 做多腿组合管理。SetRange 里 norm_koeff = 1.0 / (norm_price - strike),把价格差倒数当作缩放基准,没初始化 range_inited_Flag 之前算 Delta 会失真,这是调试时最容易漏的一步。
class=class="str">"cmt">// ===================================================================== class=class="str">"cmt">// Short Put type option class class=class="str">"cmt">// ===================================================================== class OptionShortPut : class="kw">public TOptionBase { class="kw">public: class=class="str">"cmt">// --------------------------------------------------------------------- class=class="str">"cmt">// Constructor: class=class="str">"cmt">// --------------------------------------------------------------------- OptionShortPut(const class="type">class="kw">double _k, const class="type">class="kw">double _s, const class="type">int _digits) : TOptionBase(ENUM_OPTION_PUT_SHORT, _k, _s, _digits) { } class="kw">protected: class=class="str">"cmt">// --------------------------------------------------------------------- class=class="str">"cmt">// Get the normalized delta value: class=class="str">"cmt">// --------------------------------------------------------------------- class="type">class="kw">double GetSigmoidValue(const class="type">class="kw">double _x) class="kw">override { class="kw">return(class="num">1.0 / (class="num">1.0 + MathExp(-this.koeff_K * (-_x - this.koeff_S)))); } };
Delta 更新与取值的底层接口
这一组方法构成了期权式 Delta 指标的运行时核心。UpdateDelta 在每根报价进来时被调用,若区间尚未初始化(range_inited_Flag 为 false),直接把 delta 清零,避免用未校准的 strike 与 norm_koeff 算出无意义数值。 一旦区间已初始化,delta 由 GetSigmoidValue 算出的 sigmoid 映射值经 NormalizeDouble 截断到小数点后 5 位得到。这里精度锁在 5 位而非 8 位,是因为 sigmoid 输出本身在 [0,1] 区间,过多小数位只会放大浮点噪声,对 MT5 面板显示和阈值判断都没有增益。 Delta() 与 IsRangeInited() 是两个只读出口:前者回传最近一次算出的 delta,后者暴露区间是否就绪的状态位。实盘里你可以用 IsRangeInited() 决定是否允许 EA 下发首笔订单,防止在标的价格还没触达统计区间前就误触发信号。 GetSigmoidValue 被声明为纯虚函数(=0),意味着具体用 logistic、tanh 还是其他 S 形曲线,由派生类自己实现。你在 MT5 里写子类时,只需覆写这一个方法就能切换 Delta 的形态,其余更新与取值逻辑完全复用。
class="type">class="kw">double UpdateDelta(const class="type">class="kw">double _price) { if(this.range_inited_Flag == false) { this.delta = class="num">0.0; } else { this.delta = NormalizeDouble(this.GetSigmoidValue((_price - this.strike) * this.norm_koeff), class="num">5); } class="kw">return(this.delta); } class=class="str">"cmt">// --------------------------------------------------------------------- class=class="str">"cmt">// Get the value of the previously calculated normalized delta: class=class="str">"cmt">// --------------------------------------------------------------------- class="type">class="kw">double Delta() { class="kw">return(this.delta); } class=class="str">"cmt">// --------------------------------------------------------------------- class=class="str">"cmt">// Get the range initialization flag for the delta: class=class="str">"cmt">// --------------------------------------------------------------------- class="type">bool IsRangeInited() { class="kw">return(this.range_inited_Flag); } class="kw">protected: class=class="str">"cmt">// --------------------------------------------------------------------- class=class="str">"cmt">// Get the normalized delta value: class=class="str">"cmt">// --------------------------------------------------------------------- class="kw">virtual class="type">class="kw">double GetSigmoidValue(const class="type">class="kw">double _x) = class="num">0; };