オプションを使わないオプション取引(第1回):基礎理論と原資産によるエミュレーション
はじめに
オプションという金融商品は、市場参加者の間でますます関心を集めています。実際、モスクワ取引所(MOEX)が個人投資家向けコンテストを開催した過去数年間において、先物部門で入賞するトレーダーの多くはオプション取引者でした。
オプションの数学的理論は比較的複雑であり、手計算での理解や計算は容易ではありません。しかし、オプション計算機と呼ばれる多数のツールが存在し、あらゆるオプションの組み合わせを計算できます。通常、実際の市場では複数のパラメータを持つオプションの組み合わせ(オプション戦略)が取引されます。これについては次回以降の記事で詳しく説明します。
本記事では、原資産の売買を通じてオプションをエミュレーションする方法を扱います。この手法により、任意のパラメータを持つシンセティックオプションを構築できます(実際の市場に存在しないものも含む)。たとえば、「オプションのオプション」や「スプレッドのオプション」といった、現実の原資産または合成原資産に対するより複雑な金融商品も作成可能です。
オプション理論の基礎
- 1. 定義
オプションとは、市場参加者に対して以下の権利(ただし義務ではない)を与えるデリバティブ金融商品です。
- 原資産を買う権利:コールオプション(Call)
- 原資産を売る権利:プットオプション(Put)
オプションの売り手(買い手のカウンターパーティ)は権利ではなく義務を負います。コールの場合は原資産を買い手に売る義務、プットの場合は原資産を買い手から買う義務を負い、その対価としてプレミアムを受け取ります。
原資産は、あらかじめ定められた価格および満期日以前または満期日に売買されます。オプションが行使された場合、または満期を迎えた場合、トレーダーは、現物受渡し型では、購入(または売却)された原資産(株式、先物、通貨)に対応する数量を受け取り、現金決済オプションでは、金銭決済額(オプション行使の結果としての損益)を受け取ります。
- 2. 用語と定義
原資産:株式、指数、通貨、商品など、オプションの対象となる金融商品
ストライク:原資産を売買できるあらかじめ合意された価格。オプションの権利行使価格とも呼ばれる
プレミアム:オプションの価格であり、買い手が売り手に支払うことで、特定の価格および特定の時点において原資産を買うまたは売る権利を取得するための対価
満期:オプションの有効期限(買主が原資産の売買権を行使できる期限)
- 3. オプションの種類
- 3.1. 行使スタイル別
米国型オプション:満期以前の任意の日に行使可能(一般に欧州型より高価)
欧州型オプション:満期日にのみ行使可能
バミューダオプション:特定日にのみ行使可能
- 3.2. 決済方法別
現物決済:原資産の受け渡し
現金決済:損益を現金で決済
- 3.3. 市場別
取引所取引:標準化された契約(CBOE、MOEXなど)
OTC(店頭取引):相対取引による個別条件契約
- 4. オプションという金融ツールの本質
オプションはヘッジ手段として有効です。すなわち、投資リスクを部分的または完全に回避するための手段です。オプション価格(プレミアム)は、そのリスク回避に対して支払うコストを意味します。たとえば、6か月後に大きな支払いのために一定量のルーブル(RUB)が必要だとします。このルーブル建て資産を、RUB高(対USD)による為替リスクから保護する必要があります。6か月後に高額な買い物をするために、一定額のルーブルが必要だとしましょう。
ヘッジ手段の1つは、原資産を直接購入することです(USDをRUBに対して購入するケース)。しかしこの場合、新たなリスクが発生します。すなわち、今後6か月の間にRUBがUSDに対して上昇した場合、RUBへの逆両替の際に、場合によっては大きな損失を被る可能性があります。たとえば、為替レートが1USD=100RUBから1USD=80RUBへ変化した場合、元のRUB建て金額の20%を失うことになります。
もう1つのヘッジ手段は、RUBUSD資産に対するプットオプションを購入することです。この場合、満期は6か月、ストライク価格は現在のRUBUSDレートにほぼ等しい水準(たとえば1USD=100RUB)に設定されます。オプションのプレミアムを支払うと、このプットオプションを行使することで、6か月以内であれば1USD=100RUBのレートでUSDをRUBに交換できるようになります。その時点で仮にRUBが(想像するのも恐ろしいほど)50RUB/USDまで上昇していたとしても、結果として支払うのはオプションのプレミアムのみです。
同様に、RUB資産のRUB下落(通貨安)からも保護することができます。この場合は、現在のRUBUSDレートにほぼ等しいストライク価格のコールオプションを購入する必要があります。
- 5. ブラック–ショールズオプションモデル
ブラックショールズモデルは、1973年にフィッシャー・ブラック、マイロン・ショールズ、および(間接的に)ロバート・マートンによって開発された、欧州型オプションの公正価格を計算するための数理方程式です。ここでいう公正価格とは、オプションの買い手と売り手の双方にとって納得可能な価格を意味します。本研究に関連して、ショールズとマートンは1997年にノーベル経済学賞を受賞しました(フィッシャー・ブラックはそれ以前に亡くなっており、受賞対象にはなっていません)。
コールオプション価格(プレミアム)
![]()
プットオプション価格(プレミアム)
![]()

![]()
ここで
- S:原資産の現在価格
- X:オプションのストライク価格
- r:無リスク金利(連続複利)
- T:満期までの時間(年)
- N(d):標準正規分布の累積分布関数
- σ:原資産のボラティリティ
本モデルは以下のいくつかの仮定に基づいています。
- 市場は完全に効率的であり、裁定機会は存在しない
- 無リスク金利は一定で既知である
- 原資産のボラティリティは一定である
- 配当は存在しない(この後モデルは改良された)
- 価格は対数正規分布に従い、価格は負にならない
- 取引は連続時間で行われる(市場は途切れない)
- 手数料および税金は存在しない
上記の仮定に由来する主な問題点は以下の通りです。
- 市場は実際には完全効率ではない(高流動性銘柄のみ近似的に成立する)
- ボラティリティは時間とともに頻繁かつ連続的に変化し、一定とはみなせない
- 実際の価格分布は対数正規分布から乖離しており、「ファットテール(厚い裾)」を持つ(クラッシュリスク)突発的な価格変動はモデルに含まれない
- 米国型オプションには適用できない
上記の制約を補うために、以下のような代替モデルが存在します。
- ヘストンモデル(確率的ボラティリティ)
- ベイズモデル(マクロ要因の考慮)
- 機械学習手法(ニューラルネットワークによるオプション価格予測)
- 6. 「ギリシャ指標」
ギリシャ指標とは、オプション価格がさまざまなパラメータの変化に対してどの程度敏感であるかを評価するための統計的変数です。これらのパラメータには、ストライク価格、ボラティリティ、原資産の現在価格、満期までの時間などが含まれます。数学的には、「ギリシャ指標」はブラック=ショールズ方程式の偏微分として定義されます。実務上は、以下の主要指標が使用されます。
- デルタは、原資産価格の変化に対してオプション価値がどのように変化するかを示します。これは、オプション価格の変化量と原資産価格の変化量の比として計算されます。デルタが負の場合、原資産価格が上昇するとオプション価格は下落します。コールオプションではデルタは正、プットオプションでは負となります。これはオプションのエミュレーションにおいて最も重要な指標です。
- ガンマは、原資産価格の変化に対してデルタがどのように変化するかを示します。本質的には、原資産価格に対するオプション価格の2階微分です。満期まで時間が長いオプションではガンマは小さく、満期が近づくにつれてその値は増加します。
- セータは、満期までの時間経過に応じてオプション価格がどのように変化するかを示します。これはオプション価格の変化量と満期までの時間変化の比として計算されます。セータは常に負の値を持ちます。これは時間の経過がオプション価値を減少させるためです。
- ベガは、インプライドボラティリティ(IV)の変化に対してオプション価値がどのように変化するかを示します。これはオプション価格の変化量とIVの変化量の比として計算されます。ストライク価格が現在の原資産価格に近いオプションほどベガは大きくなり、このようなオプションはIVの変化に最も敏感です。ベガの値は満期が近づくにつれて減少します。
- インプライドボラティリティ(IV)はギリシャ指標には含まれませんが、ブラックショールズ方程式で使用される重要な指標です。特定のオプション契約における実際の取引価格から逆算されます。さらにボラティリティはさまざまな方法で計算可能です。ブラック=ショールズ方程式へのフィッティング、中心ストライクのみの使用、ストライクごとの重み付け、注文情報の有無を考慮する場合としない場合などがあります。本エミュレーションでは、実際のオプション市場や注文情報が存在しない、または利用できないと仮定します。
そのため、本エミュレーションではIVの代わりにヒストリカルボラティリティ(HV)を使用します。これは日次、週次、月次の間隔で計算されます。この間隔はオプションの満期までの期間に依存します。数学的な観点から見ると、HVとは、一定期間におけるデータの平均値に対するばらつきを表す統計的指標です。これは標準偏差に時間期間の数の平方根を掛けることで計算されます。本稿の目的においては、日次、週次、月次の時間間隔を用いるのが適切です。
オプションエミュレーション
- 1. エミュレーションの必要性
エミュレーションの必要性は、以下に示すいくつかの要因によって決まります。
- 必要な原資産に対する上場オプションが存在しないこと。たとえば、Bybit暗号資産取引所では、BTC、ETH、SOLの3つのスポット資産に対するオプションのみが取引されています。また、最近までMOEXには株式オプションが存在しませんでした。
- 満期時間に基づくオプションタイプの制約。たとえばMOEX(株式オプション)では、行使が満期日のみに限定された欧州型オプションのみが提供されています。これによりオプション戦略の範囲が狭まり、投機的な戦略を採りにくくします。
- オプションの建玉・早期決済における流動性不足。MOEXでは、最も流動性の高い先物・株式に紐づくオプションのみが迅速な建玉およびクローズを可能にします。これは利益機会の損失や想定外の損失につながる可能性があります。また、オプションポジションの開閉速度が重要となるスキャルピング戦略の実行も困難になります。
- 2. 実オプションと仮想オプションの比較
| 比較基準 | 実オプション | 仮想オプション |
|---|---|---|
| オプション購入時のプレミアム支払い | あり | なし |
| オプション売却時のプレミアム受取 | あり | なし |
| 満期接近に伴うタイムディケイの有無 | あり | なし |
| 突発的な市場変動やニュースフローの影響 | 一般にはほとんど受けない | オプションエミュレーション品質に影響する |
| 満期時間の柔軟性 | なし(取引所によって固定) | 任意に設定可能 |
| 短期満期の利用可能性(1日、数時間、数十分) | なし | 任意 |
| ポジションの常時監視の必要性 | 通常不要 | 常時監視が必要 |
太字で示されているのはオプションの正の特性です。表から分かるように、両者にはそれぞれ長所と短所があります。予想される通り、現実世界に完全なものは存在しません。
- 3. オプションエミュレーションを用いるための取引条件要件
オプションエミュレーションは原資産を取引するため、取引コストおよびその他の取引オーバーヘッドを可能な限り最小化することが望ましいです。具体的には、スリッページ、大きなスプレッド、ポジション保有に伴うスワップなどが含まれます。これらはすべて仮想オプションの品質を低下させ、特に満期が短い場合にその影響が顕著になります。それ以外には、特別な取引条件の要件はありません。
原資産ポジションがオプションと等価であると主張するためには、任意の時点においてオプションには一定のデルタが存在することを理解する必要があります。もし原資産の数量がそのオプションのデルタと一致すれば、その時点で両者のポジションは等価となり、すなわち同一の損益をもたらします。
次に、エミュレーションの構築方法を詳しく見ていきます。
ある原資産に対して、1つまたは複数のオプションを買う、あるいは売る必要があると仮定します。まず、そのポートフォリオをオプションで構築した場合と同様にデルタを計算します(たとえばMOEXのウェブサイト上でも計算することが可能です)。
例として、レバレッジと限定リスクを用いて上昇局面に参加するために、100枚のイン・ザ・マネー(in the money)コールオプションを購入することを想定します。1つのオプションのデルタは0.5とします。このとき、100枚のオプションポートフォリオの合計デルタは 0.5 × 100 = 50 となります。この場合、等価なポートフォリオを作成するには、原資産を50単位購入するだけで十分です。そうすると、両ポートフォリオのデルタは同一となり、どちらも50になります。
しかし、オプションを購入した場合、通常はそのまま満期まで保有することができます。一方でエミュレーションでは状況が異なります。ここではポジションの保有期間を通じてオプションポートフォリオとの等価性を維持する必要があります。なぜなら市場価格は常に変動するためです。そのため、リバランス(再調整)が必要となり、不足または過剰な原資産を売買する必要があります。では、リバランスはどの頻度・タイミングでおこなうべきでしょうか。これは時間ステップ(たとえば1時間ごと、1日ごと)で行うこともできますし、デルタステップ(デルタが±1、±2など変化した場合)でおこなうこともできます。他の方法も存在します。
例を続けます。100コールオプション、すなわち原資産50単位に相当するポートフォリオを構築したとします。夕方になると市場がやや上昇し、1つのコールオプションのデルタが0.53に増加したとします。この場合、ポートフォリオ全体のデルタは0.53 × 100 = 53になります。等価性を維持するためには、原資産をさらに3単位購入する必要があります。次の日に価格がエントリー水準を下回り、デルタが0.48に低下した場合には、今度は原資産を5単位売却する必要があります。このようにしてポートフォリオの等価性が維持されます。
なぜこのようなエミュレートされたオプションポジションが購入したオプションと同様のプロファイルを持つのでしょうか。価格が上昇するとデルタが増加し、トレーダーはより多くの原資産を購入することになります。その結果、オプションが「イン・ザ・マネー」に進むほどポートフォリオ内の原資産量が増え、上昇時に利益を得る構造になります。これはコールオプションを購入した場合と同様です。価格が下落した場合には原資産を売却し、ポジションはゼロに向かいます。そのため下落時の損失は限定されます。これもコールオプションと同様の特徴です。
リバランスについてさらに詳しく説明します。これはエミュレーションにおける最も重要な要素です。最終的な結果は実際にはこれらの取引に依存します。調整は事前に定義されたアルゴリズムに従って体系的に行う必要があり、そうでなければ予期しない損失が発生する可能性があります。たとえば、価格が急上昇してデルタが0.5から0.6に変化したにもかかわらずリバランスを逃した場合、本来購入すべき10単位を購入しないままになる可能性があります。その後、その価格帯に戻らない場合、その機会は失われることになります。翌日デルタが0.65に上昇した場合には、今度は0.65の水準で15単位購入しなければならず、非効率的なコスト構造になります。そのため、自動売買ボットを使用する必要があります。
理想的なケースは、先物が24時間取引されており、連続的に調整できる場合です。しかし頻繁な調整は、一見すると不要な損失を発生させる可能性もあります。たとえば、3日間でデルタが0.50 → 0.55 → 0.50と変化した場合、リバランスアルゴリズムに従うと、まず0.55で5単位購入し、その後0.50で5単位売却する必要があり、結果として損失が確定されます。したがって、2日目の調整を仮におこなわなかったとしても、その間に価格が変化しなければ問題は発生しません。しかし市場は一方向に動くこともあるため、リバランスは定期的におこなう必要があります。
時間ベースのリバランスの欠点は、急激な価格変動への対応です。短時間で数パーセント変動する場合、デルタステップ方式は有効ですが、非常に急激な変動時にはスリッページにより想定価格で約定できない場合があります。そのような場合には、自動売買ボットが役立ち、より迅速かつ正確な調整が可能になります。
原資産が上下に変動する場合、リバランスは毎回わずかな損失を確定させるだけであることは明らかです。実際にコールオプションを購入する例では、トレーダーは高い価格で原資産を買い、低い価格でそれを売ることになります。もしポジション保有期間中に原資産がほとんど動かなかった場合、デルタを維持する必要があるため、この戦略は収益性を持ちません。しかしオプションを購入する場合、プレミアムが支払われます。そしてそのオプションが「イン・ザ・マネー」に入らない場合、そのプレミアムは失われます。時間的減価(タイムディケイ)はオプションの価値を侵食します。一方でエミュレーションの場合、リバランス自体が損失を生む可能性があります。
デルタを維持するための原資産の売買は、小さな損失取引の繰り返しです。それらの総損失は時間の経過とともに、おおよそオプションのプレミアムに等しくなります。プレミアムが購入時のインプライドボラティリティ(IV)に依存していることは明らかです。IVが高いほど、トレーダーが想定するように市場変動がより激しくなります。IVが高いほどオプションは高価になります。エミュレーションについても同様であり、市場がより頻繁に変動するほど、すなわちよりボラティリティが高いほど、エミュレーションのコストも高くなります。変動が大きいほど損失は大きくなり、これはボラティリティの上昇に伴ってオプションの価値が増加することと同様です。
エミュレーションは、オプションの存続期間中に原資産が低ボラティリティであり、リバランスがほとんど必要ない場合、あるいはその損失規模が小さい場合に正当化されます。その場合、取引所で高いIVのオプションを購入するよりも、オプションをエミュレートする方が有利になります。なぜなら、ポートフォリオの存続期間における実現ボラティリティがIVよりも十分に低いからです。ここではショートオプションとの類似性も適切です。たとえば52%のボラティリティでオプションを売却し、実現ボラティリティが60%であった場合、デルタニュートラルを維持するためのコストにより結果は損失になります。一方で45%のボラティリティが実現した場合は、リバランスコストがプレミアムより小さいため、売却は利益になります。
エミュレーションは、実現ボラティリティがポートフォリオ作成時のIVを上回る場合、直接オプションを購入する場合よりも不利になります。なぜなら、頻繁な調整がオプションプレミアムの価値を上回ってしまうためです。逆に、ショートオプションをエミュレートする場合には、実現ボラティリティが高いほど、直接オプションを売却するよりもエミュレーションの方が有利になります。
人工的にオプションを作成する際に、他に何を知る必要があるでしょうか。オプション価格は主に3つの要素によって決定されることを思い出してください。すなわち、満期までの時間、原資産価格、そしてIVです。他の条件が同じであれば、これらの各要素はオプションにそれぞれ異なる影響を与えます。したがって、通常のオプションの価値は3つの変数の関数であると言えます。一方でエミュレートされたオプションは原資産のみで構成されており、その価格はIVや時間に依存しません。「合成オプション」の価格は原資産価格のみに依存する関数になります。これが良いことか悪いことかは明らかではなく、正と負の両面を持ちます。
エミュレーションの実装に入る前に、アルゴリズムの実務的側面を考察します。そのために、オプションデルタおよびP&L(損益)が原資産価格に依存するグラフを作成します。これらのグラフはMOEXのウェブサイトから取得されたもので、SBRF先物に対するオプション(ストライク価格31,000、満期2025年06月18日)のものです。グラフは2025年05月22日時点のものです。この時点における価格(縦の点線に対応)は30,800でした。
図1:ストライク価格31,000のコールオプション(購入)の、原資産価格に対する損益(P&L)の依存関係です。P&Lは1オプションあたりのルーブル(RUB)で表されています。縦の点線はグラフが作成された時点を示しています。この時点において、P&Lはマイナスであり、その値は-23.92RUBです。
赤い線は現在時点(2025/05/22)を示し、青い線は満期時点(2025/06/18)、すなわちブラック=ショールズ方程式におけるT=1の時点を示しています。
図2:ストライク価格31,000のコールオプション(購入)のデルタ値の、原資産価格に対する依存関係です(1オプションあたり)。縦の点線はグラフが作成された時点を示しています。この時点においてデルタは0.48です。
赤い線は現在時点(2025/05/22)を示し、青い線は満期時点(2025/06/18)、すなわちブラック=ショールズ方程式におけるT=1の時点を示しています。
上記のグラフはブラックショールズ公式から直接導かれるものです。図1のグラフから分かるように、コールオプションを購入する場合、我々は1契約あたり約789RUBのプレミアムを売り手に支払います。言い換えれば、購入時点で即座に損失状態(赤字)になります。これは原資産をエミュレーションした場合とは異なります(原資産購入にかかる手数料は第一近似として無視できます)。しかし良い点もあります。原資産価格が急落した場合であっても、損失は支払ったプレミアムに限定されます。これはコールオプションを購入することと、例えば株式を購入することとの本質的な違いです。後者の場合、価格が下落すれば損失は制限されません。
MQL5でのオプションエミュレーションの開発
図2は、購入されたコールオプション1契約あたりのデルタが、0から+1の範囲で変化することを示しています。ここでデルタチャートのX軸を絶対価格からポイント単位の増分へ移動させます。そのために、価格からストライク価格(この場合は31,000)を差し引きます。その結果として得られるグラフは次のようになります。
図3:ストライク価格31,000のコールオプション(購入)におけるデルタ値の、ストライクに対する原資産価格変化の依存関係(1オプションあたり)です。縦の点線はグラフが作成された時点を示しています。この時点においてデルタは0.48です。
赤い線は現在時点(2025/05/22)を示し、青い線は満期時点(2025/06/18)、すなわちブラック=ショールズ方程式におけるT=1の時点を示しています。
当然ながら、このグラフはX軸のスケールが異なるだけで、質的には元のグラフと違いはありません。ブラックショールズ方程式にはIVが含まれていますが、一般にそれを直接知ることができません。そのため、適切な代替としてHVを使用します。月次オプションでは1か月の平均HV、週次オプションでは1週間の平均HVを使用します。
異なる原資産間でのチャート比較を容易にし、また合成資産(複数資産からなるポートフォリオ)に対するオプション生成を可能にするためには、価格増分を相対化し、さらにそれをHVで正規化することが望ましいです。
このとき、価格の相対増分は式 「相対価格(価格) = (価格 - ストライク) / HV」で表されます。
満期期間に対してHV=5000と計算されたと仮定します(これは図3から確認できます)。すると次のようなグラフが得られます。
図4:ストライク価格31,000のコールオプション(購入)におけるデルタ値の、原資産の相対価格変化(HVで正規化されたもの)に対する依存関係(1オプションあたり)です。縦の点線はグラフが作成された時点を示しています。この時点においてデルタは0.48です。
赤い線は現在時点(2025/05/22)を示し、青い線は満期時点(2025/06/18)、すなわちブラック=ショールズ方程式におけるT=1の時点を示しています。
ここから分かるように、価格がストライクに等しいとき、デルタは0.5に等しくなります。赤線で示されるデルタ曲線は、数学および機械学習で知られるシグモイド関数そのものであり、「1 \ (1 + exp(-K \ (x - S)))」となります。ここでS = 0であり、Kは一般に未知であり、経験的に決定されるパラメータです。Kは実質的に満期までの残存期間に依存します。ある時点でのデルタ値(0.48)と現在の原資産価格(30,800)を用いることで、Kの値を計算することができます。この時点ではK=2となり、オプションが満期に近づくにつれて増加します。
エミュレーションの目的においては、オプションの時間減衰およびKの変化は無視します。すなわちKはオプション存続期間を通じて一定とします。これは購入コールオプションのP&Lに対しては正の影響を与え、購入プットオプションに対しては負の影響を与えます。ただし、オプションポートフォリオ全体としては大きな影響はありません。
エミュレーションを簡単にするため、シグモイド関数1 \ (1 + exp(-K \ (x - S)))のシフト係数Sを0.5に設定すると、曲線はX軸方向に右へ移動し、x=0での値は0に近くなります。この形状はエミュレーションおよびボラティリティ範囲への正規化においてより便利であり、MQLでエキスパートアドバイザを開発する際に使用されます。
したがって、任意の原資産に対するオプションをエミュレートするMQL5コードを書くためのすべての基礎データが揃いました。すなわち、エミュレーション関数の方程式、 ストライク価格、HV、理想的なブラックショールズ市場で起こるプロセスの理解です。
- 1. 基底クラス
以下のオプションタイプをエミュレートします。
- 原資産を買う権利の購入:ロングコールオプション
- 原資産を売る権利の購入:ロングプットオプション
- 原資産を買う権利の売却:ショートコールオプション
- 原資産を売る権利の売却:ショートプットオプション
すべての対応オプションタイプを列挙型として定義します。
// --------------------------------------------------------------------- // Option type: // --------------------------------------------------------------------- enum ENUM_OPTION_TYPE { ENUM_OPTION_CALL_LONG, // Long Call ENUM_OPTION_CALL_SHORT, // Short Call ENUM_OPTION_PUT_LONG, // Long Put ENUM_OPTION_PUT_SHORT, // Short Put };
基底クラスは、標準のMQLクラスであるCObjectから派生しています。これは、標準MQLのCListリストを容易に使用できるようにし、任意の複雑さを持つオプション構造を操作・構築できるようにするためです。この点については次回の記事で説明します。
基底クラスの主なデータメンバーは以下の通りです。
- オプションタイプ:type_enumフィールド
- ストライク価格:strikeフィールド
- オプションデルタの現在値:deltaフィールド
- ボラティリティ正規化のための価格:norm_priceフィールド
さらに、基底から派生するクラスで必要となる補助フィールドがいくつか存在します。基底クラスは抽象クラスであり、GetSigmoidValueメソッドの実装が派生クラスで必須となっています。このメソッドはブラック=ショールズモデル内においてオプションの種類ごとに異なる動作を持ちます。この設計により、将来的には機械学習手法やその他の価格モデルを用いた新しいオプションタイプを追加することも可能になります。
// ===================================================================== // The base class of the option is derived from 'CObject' so that it can be // put into the lists. // ===================================================================== class TOptionBase : public CObject { protected: ENUM_OPTION_TYPE type_enum; double strike; double shift; double koeff_K; double koeff_S; int digits; double norm_price; double delta; double norm_koeff; bool range_inited_Flag; public: // --------------------------------------------------------------------- // Constructor: // --------------------------------------------------------------------- TOptionBase(const ENUM_OPTION_TYPE _type, const double _k, const double _s, const int _digits) : CObject(), type_enum(_type), strike(0.0), shift(0.0), koeff_K(_k), koeff_S(_s), digits(_digits), range_inited_Flag(false), delta(0.0), norm_koeff(0.0) { } public: // --------------------------------------------------------------------- // Set the delta normalization range for the central strike: // --------------------------------------------------------------------- void SetRange(const double _strike, const double _norm_price) { this.strike = _strike; this.norm_price = _norm_price; this.norm_koeff = 1.0 / (this.norm_price - this.strike); this.range_inited_Flag = true; } // --------------------------------------------------------------------- // Calculate the normalized delta value for a given price: // --------------------------------------------------------------------- double UpdateDelta(const double _price) { if(this.range_inited_Flag == false) { this.delta = 0.0; } else { this.delta = NormalizeDouble(this.GetSigmoidValue((_price - this.strike) * this.norm_koeff), 5); } return(this.delta); } // --------------------------------------------------------------------- // Get the value of the previously calculated normalized delta: // --------------------------------------------------------------------- double Delta() { return(this.delta); } // --------------------------------------------------------------------- // Get the range initialization flag for the delta: // --------------------------------------------------------------------- bool IsRangeInited() { return(this.range_inited_Flag); } protected: // --------------------------------------------------------------------- // Get the normalized delta value: // --------------------------------------------------------------------- virtual double GetSigmoidValue(const double _x) = 0; }; // ---------------------------------------------------------------------
- 2. 実環境での動作
オプションをエミュレーションする際には、デルタがどの程度変化したタイミングでポジションをリバランスするかを決定する必要があります。これは一方ではエミュレーションの精度を制限しますが、他方ではポジションの開閉に伴う手数料コストを制御できるようにします。そのために、原資産のパラメータであるVmin_size(オープンポジションの最小ボリューム)およびVmin_delta_size(ポジション量の最小変化)を使用します。
また、デルタ変化が範囲(絶対値で)|0.0…1.0|にある場合のリバランス回数を設定します。このリバランス回数Nは外部パラメータとして定義します。すると、ポジションの総ボリュームは0から(Vmin_size + Vmin_delta_size * (N - 1))までの範囲で変化します。
通常、Vmin_sizeとVmin_delta_sizeは同一の値ですが、必ずしもそうである必要はありません。デフォルト設定としては、デルタ範囲|0.0…1.0|においてリバランス回数を10回とします。
- 3. ロングコールオプション
図5:エミュレートされたロングコールオプション(コール購入)における、デルタ値の相対的なDPrice変化(表中のx)への依存関係です。S = 0.5、K = 10です。デルタの範囲は10個の等分区間に分割されています。
ストライク価格は、DPrice = 0のゼロ点に対応します。デルタ値が1に近い場合、そのオプションは「ディープ・イン・ザ・マネー(deep in the money)」であるとされます。この場合、それは最大サイズの原資産に対する買いポジションのように振る舞います。原資産価格がストライクに近づくにつれて、原資産におけるポジションサイズは減少し、オプションが「アウト・オブ・ザ・マネー(out of the money)」になるとゼロになります。これはデルタがゼロに近い状態を意味します。実際のオプションでは、このとき損失は支払ったプレミアム分に限定されます。
// =====================================================================
// Long Call type option class
// =====================================================================
class OptionLongCall : public TOptionBase
{
public:
// ---------------------------------------------------------------------
// Constructor:
// ---------------------------------------------------------------------
OptionLongCall(const double _k, const double _s, const int _digits)
:
TOptionBase(ENUM_OPTION_CALL_LONG, _k, _s, _digits)
{
}
protected:
// ---------------------------------------------------------------------
// Get the normalized delta value:
// ---------------------------------------------------------------------
double GetSigmoidValue(const double _x) override
{
return(1.0 / (1.0 + MathExp(-this.koeff_K * (_x - this.koeff_S))));
}
};
// ---------------------------------------------------------------------
- 4. Long Put option
図6:エミュレートされたロングプットオプション(プット購入)における、デルタ値の相対的なDPrice変化(表中のx)への依存関係です。S = 0.5、K = 10です。デルタの範囲は10個の等分区間に分割されています。
ストライク価格は DPrice = 0 のゼロ点に対応します。デルタ値が−1に近い場合、そのオプションは「ディープ・イン・ザ・マネー」であるとされます。この場合、それは最大サイズの原資産に対する売りポジションのように振る舞います。原資産価格がストライクに近づくにつれて、原資産におけるポジションサイズは減少し、オプションが「アウト・オブ・ザ・マネー(out of the money)」になるとゼロになります。これはデルタがゼロに近い状態を意味します。実際のオプションでは、このとき損失は支払ったプレミアム分に限定されます。
// Long Put type option class
// =====================================================================
class OptionLongPut : public TOptionBase
{
public:
// ---------------------------------------------------------------------
// Constructor:
// ---------------------------------------------------------------------
OptionLongPut(const double _k, const double _s, const int _digits)
:
TOptionBase(ENUM_OPTION_PUT_LONG, _k, _s, _digits)
{
}
protected:
// ---------------------------------------------------------------------
// Get the normalized delta value:
// ---------------------------------------------------------------------
double GetSigmoidValue(const double _x) override
{
return(-1.0 / (1.0 + MathExp(-this.koeff_K * (-_x - this.koeff_S))));
}
};
// ---------------------------------------------------------------------
- 5. ショートコールオプション
図7:エミュレートされたショートコールオプション(コール売却)における、デルタ値の相対的なDPrice変化(表中のx)への依存関係です。S = 0.5、K = 10です。デルタの範囲は10個の等分区間に分割されています。
ストライク価格は DPrice = 0のゼロ点に対応します。デルタ値が−1に近い場合、そのオプションは「ディープ・イン・ザ・マネー」であるとされます。この場合、それは最大サイズの原資産に対する売りポジションのように振る舞います。原資産価格がストライクに近づくにつれて、原資産におけるポジションサイズは減少し、オプションが「アウト・オブ・ザ・マネー(out of the money)」になるとゼロになります。これはデルタがゼロに近い状態を意味します。実際のオプションでは、このとき利益は支払ったプレミアム分に限定されます。
// Short Call type option class
// =====================================================================
class OptionShortCall : public TOptionBase
{
public:
// ---------------------------------------------------------------------
// Constructor:
// ---------------------------------------------------------------------
OptionShortCall(const double _k, const double _s, const int _digits)
:
TOptionBase(ENUM_OPTION_CALL_SHORT, _k, _s, _digits)
{
}
protected:
// ---------------------------------------------------------------------
// Get the normalized delta value:
// ---------------------------------------------------------------------
double GetSigmoidValue(const double _x) override
{
return(-1.0 / (1.0 + MathExp(-this.koeff_K * (_x - this.koeff_S))));
}
};
// ---------------------------------------------------------------------
- 6. ショートプットオプション
図8:エミュレートされたショートプットオプション(プット売却)における、デルタ値の相対的なDPrice変化(表中のx)への依存関係です。S = 0.5、K = 10です。デルタの範囲は10個の等分区間に分割されています。
ストライク価格は DPrice = 0のゼロ点に対応します。デルタ値が1に近い場合、そのオプションは「ディープ・イン・ザ・マネー」であるとされます。この場合、それは最大サイズの原資産に対する売りポジションのように振る舞います。原資産価格がストライクに近づくにつれて、原資産におけるポジションサイズは減少し、オプションが「アウト・オブ・ザ・マネー(out of the money)」になるとゼロになります。これはデルタがゼロに近い状態を意味します。実際のオプションでは、このとき利益は支払ったプレミアム分に限定されます。
// Short Put type option class
// =====================================================================
class OptionShortPut : public TOptionBase
{
public:
// ---------------------------------------------------------------------
// Constructor:
// ---------------------------------------------------------------------
OptionShortPut(const double _k, const double _s, const int _digits)
:
TOptionBase(ENUM_OPTION_PUT_SHORT, _k, _s, _digits)
{
}
protected:
// ---------------------------------------------------------------------
// Get the normalized delta value:
// ---------------------------------------------------------------------
double GetSigmoidValue(const double _x) override
{
return(1.0 / (1.0 + MathExp(-this.koeff_K * (-_x - this.koeff_S))));
}
};
// ---------------------------------------------------------------------
- 7. 原資産ポジション量の計算における特徴
原資産のポジション量を計算するためには、デルタ値の範囲 |0.0…1.0| を整数値の範囲 |0…10| に変換する必要があります。その後、この整数値に最小ロット変化量を掛けることで、必要なポジション量を得ます。
ここではデルタのサブレンジ(分割区間)の数を10と仮定します。単純にデルタに10を掛け、その結果を整数へ変換するだけでは不十分です。なぜなら、デルタが「サブレンジ」の境界付近で変動すると、ポジション量もデルタと同期して変動してしまうためです。これを例で説明します。たとえばデルタが0.090である場合、ポジション量は(int)(0.090 × 10) = 0となり、ポジションは存在しません。その後、わずかに上昇して0.101になると、(int)(0.101 × 10) = 1となり、ポジションは1になります。しかし次のティックで再び0.090に戻ると、再び0になります。つまり実質的には「ほぼ同時に買ってすぐ売る」という状態になり、意味のないリバランスが短時間に何度も繰り返されることになります。これはいわゆるシグナルの「チャタリング(chattering)」です。
そのため、デルタからポジション量へ変換する際には、リバランスにある種の「ヒステリシス(hysteresis)」を導入する必要があります。これはリバランスの方向性を区別することで実現されます。すなわち、現在の原資産ポジションを増減させる際に、前回のポジション量の値を考慮します。
ここで「オプションエミュレーションレベル」という概念を導入します。説明:もしマッピングが|0.0…1.0| → |0…10|に設定されている場合(上記説明参照)、このときオプションエミュレーションレベルは0から10の範囲で変化するとみなします。このレベル数は外部パラメータNとして指定されます。エミュレーションレベルはデルタと同様に絶対値(モジュロ)で扱います。また、オプションエミュレーションレベルの符号も使用します。これは {0, +1, -1} のいずれかを取ります。0の場合、ポジションはクローズ状態であることを意味します。
- 8. MQL5における原資産ポジション量の計算
「ヒステリシス」を実装する方法として、現在のオプションエミュレーションレベルを計算する際に、2つの配列を用いたテーブル方式を使用します。
- IncreaseLevel_Array:レベルが増加する場合のオプションエミュレーションレベルを定義
- DecreaseLevel_Array:レベルが減少する場合のオプションエミュレーションレベルを定義
これらはUpdateOptionLevelメソッドの中で処理されます。このメソッドは2つの入力を受け取ります。
- _delta:現在のオプションデルタ
- _curr_level:現在のオプションエミュレーションレベル(以前に計算された値)。初回呼び出しの場合は0となります
- _ZeroDelta:オプションエミュレーションのゼロレベルに対応するデルタ値です。これは0.001から0.099の範囲で変化する可能性があります
- _LevelsNumber:オプションエミュレーションのレベル数です。レベル数が多いほどエミュレーションはより滑らかで高精度になりますが、その分、原資産の取引ロットも大きくなります(変更が離散的であるためです)
// ===================================================================== // Class for calculating the contract volume of the underlying asset for an option: // ===================================================================== class OptionContractsVolume { int IncreaseLevel_Array[]; // option emulation level when increasing it int DecreaseLevel_Array[]; // option emulation level when decreasing it // --------------------------------------------------------------------- protected: double ZeroDelta; // delta value corresponding to the zero level of option emulation int LevelsNumber; // number of option emulation levels // --------------------------------------------------------------------- int zero_delta; // integer delta value corresponding to the zero level of option emulation int curr_option_level_sign; // current sign of the option emulation level int curr_option_level; // current option emulation level // --------------------------------------------------------------------- int curr_inc_level; int curr_dec_level; // --------------------------------------------------------------------- int curr_contracts_index; // --------------------------------------------------------------------- bool is_contracts_updated_Flag; public: // --------------------------------------------------------------------- // Constructor: // --------------------------------------------------------------------- OptionContractsVolume(const double _ZeroDelta, const int _levels_number) : ZeroDelta(_ZeroDelta), LevelsNumber(_levels_number), curr_option_level_sign(0), curr_option_level(0), curr_inc_level(0), curr_dec_level(0), is_contracts_updated_Flag(false) { this.zero_delta = (int)(NormalizeDouble(this.ZeroDelta, 3) * 1000.0); // Allocate memory for arrays storing emulation levels: ArrayResize(this.IncreaseLevel_Array, (this.LevelsNumber + 1) * 100 + 1); ArrayResize(this.DecreaseLevel_Array, (this.LevelsNumber + 1) * 100 + 1); // Fill in the array upwards: for(int i = 0; i < this.LevelsNumber + 1; i++) { ArrayFill(this.IncreaseLevel_Array, i * this.LevelsNumber * 100, this.LevelsNumber * 100, i); } ArrayFill(this.IncreaseLevel_Array, (this.LevelsNumber + 1) * 100, 1, this.LevelsNumber); // Fill in the array downwards: ArrayFill(this.DecreaseLevel_Array, 0, zero_delta, 0); ArrayFill(this.DecreaseLevel_Array, zero_delta, this.LevelsNumber * 100 - zero_delta + 1, 1); for(int i = 2; i < this.LevelsNumber + 1; i++) { ArrayFill(this.DecreaseLevel_Array, i * this.LevelsNumber * 100, this.LevelsNumber * 100, i); } ArrayFill(this.DecreaseLevel_Array, (this.LevelsNumber + 1) * 100, 1, this.LevelsNumber); } // --------------------------------------------------------------------- // Calculate the option emulation level for a given delta: // --------------------------------------------------------------------- // - if the level has changed, return 'true'. // --------------------------------------------------------------------- bool UpdateOptionLevel(const double _delta, const int _curr_level) { this.is_contracts_updated_Flag = false; // Define trade direction: int delta_int = (int)(NormalizeDouble(_delta, 3) * 1000.0); this.curr_option_level_sign = 0; if(delta_int > zero_delta / 2) { this.curr_option_level_sign = 1; } else if(delta_int < -zero_delta / 2) { this.curr_option_level_sign = -1; } // Current index for arrays, based on 'Delta' with 3 decimal places: this.curr_contracts_index = (int)MathAbs(delta_int); if(this.curr_contracts_index > ((this.LevelsNumber + 1) * 100)) { // The index should not exceed the array size (here the option is deep in the money): this.curr_contracts_index = (this.LevelsNumber + 1) * 100; } // Current option emulation level (0...N) in the direction of INCREASE: this.curr_inc_level = this.IncreaseLevel_Array[this.curr_contracts_index]; // Current option emulation level (0...N) in the direction of DECREASE: this.curr_dec_level = this.DecreaseLevel_Array[this.curr_contracts_index]; // If the option emulation level (0...N) has INCREASED compared to the current one: if(this.curr_inc_level > _curr_level) { this.curr_option_level = this.curr_inc_level; this.is_contracts_updated_Flag = true; return(true); } // If the option emulation level (0...N) has DECREASED compared to the current one: if(this.curr_dec_level < _curr_level) { this.curr_option_level = this.curr_dec_level; this.is_contracts_updated_Flag = true; return(true); } return(false); } }; // ---------------------------------------------------------------------
完全なコードは、下記に添付されているOptionEmulatorA1.mqhに記載されています。
結論
本記事では、原資産を用いたオプションのエミュレーションについて検討しました。これは複雑ではあるものの、有効性の高い手法です。その特徴は以下の通りです。
- 複雑かつ非標準的なオプション戦略を構築できる
- 標準的なオプションよりも高い柔軟性を提供する
- 数理モデルに対する深い理解を必要とする
- 適切なリスク管理の下では効果的に機能する
この手法は、特に次のようなケースで有用です。
- 大規模ポートフォリオのヘッジをおこなう場合
- エキゾチックなオプション戦略を構築する場合
- オプション市場の流動性が低い市場で取引する場合
次回は実践編として、MQL5の取引関数を利用した原資産ポジションの維持およびリバランスについて解説します。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/18131
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
外国為替市場向けCAPMモデルインジケータ
カスタムインジケータワークショップ(第1回):MQL5でSupertrendインジケータを構築する
MQL5コミュニティOAuthを利用した外部アプリケーション連携
初心者からエキスパートへ:流動性ゾーンインジケータの開発
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索








解説、本当にありがとう!資金に余裕ができたら、取引にオプションを取り入れてみようと思います……
ありがとうございます。ところで、これはあなたのトピックであるスプレッド取引に直接関係しています。私が現在「トロイカ」でテストしているのは、まさにオプションの構成です。
ありがとうございます。ところで、これはあなたのテーマである「スプレッド取引」に直接関係する話です。私が現在「トロイカ」でテストしているのは、まさにそのオプション戦略なのです。
了解。あれはオプションのボラティリティの表面値を使っているみたいだけど、ただどこかで読んだだけなんだけど……)
ロマン、ここでのサーフェスは全く関係ないよ。実質的にはね。
私の理解では、要点は、価格が有利な方向に動いた場合には総ポジションに継続的に追加投資を行い、不利な方向に動いた場合にはポジションを縮小するという点にあります。これはプットオプションとコールオプションの購入に当てはまります。
しかし、私の理解では、手数料に関連する諸経費に加え、特に購入時点でオプションが大幅にアウト・オブ・ザ・マネーである場合、オプション購入時よりもはるかに高い管理費用が発生することになります。 また、スムーズなリバランスを行うには、相当量の仮想オプションが必要となります。なぜなら、許容可能なステップ幅で1対10のエミュレーションを行うことは単純に不可能だからです。
最適化の課題は、特定の銘柄において最も有利な価格変更のステップ頻度を決定することです。
実際のオプションと、満期時にイン・ザ・マネーとなった仮想オプション、およびイン・ザ・マネーとならなかった仮想オプションについて、コスト面での実態比較を行うことが望ましい。
私の理解では、要点は、価格が有利な方向に動いた場合には総ポジションに継続的に追加し、不利な方向に動いた場合にはポジションを減らすという点にあるようです。これはプットオプションとコールオプションの購入において当てはまります。
しかし、私の理解では、手数料に関連する諸経費に加え、特に購入時点でオプションが大幅にアウト・オブ・ザ・マネーである場合、オプション購入時よりもはるかに高い管理費用が発生することになります。 また、スムーズなリバランスを行うには、相当量の仮想オプションが必要となります。なぜなら、許容可能なステップ幅で1対10のエミュレーションを行うことは単純に不可能だからです。
最適化の課題は、特定の銘柄に対して最も有利な価格変更のステップ頻度を決定することです。
実際のオプションと、満期時にイン・ザ・マネーとなった仮想オプション、およびイン・ザ・マネーとならなかった仮想オプションについて、コスト面での実態比較を行うことが望ましい。
ステップ数はBAのパラメータ(最小ロット/最小ロット変更幅)によって決まる。恣意的に設定することはできない。
通常、GOは高くなる。しかし、これは重要ではない。既製のオプションを簡単に売買できるのであれば、エミュレーションなど必要ないからだ。ここでの選択肢は、オプション取引を全く行わないか、エミュレーションを通じて行うかのどちらかである。 繰り返しになりますが、これは原則としてオプションが存在しないBAの場合に限ります。