目的関数設計:最適化する指標が、密かにあなたの戦略を選んでいる
「幻想なきバックテスト」シリーズの一篇。
📄 この記事は研究論文に発展しました。 以下のすべての数値は、制御された既知の正解——中程度のシグナル帯に既知のエッジを持ち、あらゆる場所にファットテールのノイズが乗る合成市場——を構築する1つの決定論的スクリプトから得られており、そこに6つの異なる目的関数の下で1つの閾値サーチを走らせ、各目的関数が実際にどの戦略を選ぶのかをアウトオブサンプルで測定しています。論文はオンライン版(インタラクティブ版+PDF)をobjective-design.marketmaker.ccで、コードとデータはgithub.com/suenot/objective-design-degeneracyで公開しています。
あなたは最良の戦略が欲しい。だからサーチを走らせる——閾値、ルックバック、ストップ距離を掃引し、最も高いスコアを出した設定を残す。サーチが終わり、勝者を手渡してくれる。合理的だ。標準的だ。地球上のあらゆるオプティマイザ、グリッドサーチ、ハイパーパラメータチューナーがやっていることそのものだ。
しかし動詞に注目してほしい:最も高いスコアを出す。何において最も高いのか?サーチが何かを王座に据える前に、あなたは最大化すべきただ1つの数字——目的関数——をそれに手渡さなければならなかった。PnL。シャープレシオ。取引したバーだけのシャープレシオ。リターン÷最大ドローダウン。あなたはおそらくたいして考えもせずにそのうちの1つを打ち込み、そしてサーチは、あなたが頼んだことを寸分違わずやり遂げるために、100万回の評価を費やした。
その1つの選択は形式的なものではない。それは決定そのものだ。サーチは「良い戦略」を見つけているのではない——抽象的にはそのようなものは存在しない。サーチはあなたが選んだスカラーを最大化する戦略を見つけるのであり、異なるスカラーは同じデータの上でまったく異なる戦略を指し示す。目的関数はハンドルを握る秘密の手であり、たいていの場合、誰もそれを見ていない。
記事全体は1つの表に収まる。1つの閾値サーチ、既知の本物のエッジを持つ1つの合成市場、6つの目的関数——そしてそれらが選ぶ6つの戦略を、ホールドアウトデータで測定したものだ:
| 目的関数(サーチが最大化する対象) | 平均市場エクスポージャー | インサンプルのシャープレシオ | アウトオブサンプルのシャープレシオ | 退化した勝者の割合 |
|---|---|---|---|---|
| 生のPnL | 0.859 | 1.76 | 1.61 | 0% |
| フルタイムラインのシャープレシオ | 0.740 | 1.82 | 1.71 | 0% |
| 1トレードあたり(「アクティブ」)シャープレシオ | 0.286 | 1.00 | 0.70 | 57% |
| エクスポージャーの下限() | 0.740 | 1.82 | 1.71 | 0% |
| トレード数による縮小(conf_k) | 0.523 | 1.54 | 1.31 | 20.7% |
| 頑健な目的関数(下限+conf_k) | 0.675 | 1.78 | 1.70 | 0.2% |
独立した600シード、それぞれバー、サーチあたり80通りの候補閾値、インサンプルとアウトオブサンプルは独立に抽出。年率換算シャープレシオ(252期間/年)。「退化」=選ばれた勝者が市場にいる時間が5%未満であるか、アウトオブサンプルのシャープレシオが非正であること。この市場の真の最適解は、アウトオブサンプルの年率シャープレシオ1.77である。
胸が痛むまで3行目を読んでほしい。1トレードあたりシャープレシオ——アクティビティ条件付き指標という一族全体(1トレードあたりシャープレシオ、期待値、van TharpのSQN、勝率)の代表であり、いずれも実際に取引したバーだけで計算される——は、アウトオブサンプルで他の半分よりも悪い戦略を選び、しかも57%の確率で退化的にそれを行う。これは微妙に劣った目的関数というわけではない。このデータにおいてそれは罠であり、サーチは半分以上の確率でその中に歩み込む。今度はその1行上を読んでほしい:素のフルタイムラインのシャープレシオは決して退化せず、アウトオブサンプルで1.71を記録する。それがこの修復全体の結論であり、早々にネタバレしてしまうと——誠実な修正は単純にタイムライン全体で測定することであり、下の行にあるもっと凝った事後補修は、どれだけうまくいってもその数字に追いつくだけで、決してそれを上回ることはない。この記事は、その罠とその修復の解剖であり、既知の正解が終始明らかなので、「目的関数は正しい戦略を選んだか?」は意見ではなく事実になる。
第1幕——秘密の決定:グッドハートの法則こそがサーチである

1975年、経済学者チャールズ・グッドハートは、その後に彼が成し遂げた他のどんな業績よりも長く生き残ることになる一文を書いた:
「観測されたどんな統計的規則性も、それが制御目的のために圧力をかけられると、崩壊する傾向がある。」
マリリン・ストラザーンの功績とされることが多い、より簡潔な通俗的な言い換えはこうだ:ある指標が目標になった瞬間、それは良い指標であることをやめる。
パラメータサーチは、グッドハートの法則の最も純粋な実例だ。目的関数が指標である。サーチが圧力だ——その指標をできる限り押し上げようとする、数千、数百万回の試み。そしてサーチは、あなたがその指標で意図したことなど微塵も気にしない。気にするのはただその数字だけだ。本物の、取引可能なエッジと何の関係もない形でその数字を大きくする方法があるなら——めったに取引せず、ほとんどの時間フラットでいて、いくつかの幸運な外れ値だけを捕まえる——サーチはその方法を見つけ出す。なぜなら、最大値を見つけることこそが、それが作られた唯一の目的だからだ。
これは、AI安全性の文献が**報酬ハッキング(reward hacking)**と呼ぶのと同じ失敗だ:あなたが望むものの代理を最適化するエージェントは、代理と目標のあらゆる隙間を突いてくる。あなたのサーチがそのエージェントだ。「シャープレシオ」がその代理だ。「来四半期に実際の資金を託せると信頼できる戦略」が目標だ。両者の間の隙間こそが、この分野全体が生きている場所である。
その隙間が開くのを見るには、真実を知っている世界が必要だ。だから、私たちはそれを構築する。
市場。 各期間、予測子(標準正規分布のシグナル)が到着し、それが部分的に予測するリターンが続く。エッジは本物だが有界だ——それは中程度のシグナル帯の内側にのみ存在し、その外側では消える:
2つの設計上の選択が重要だ。第一に、エッジは中程度の帯に存在する:極端なシグナルは予測情報を一切運ばないので、戦略は中央を取引し、裾を避けるべきだ。第二に、ノイズはファットテール(自由度4のスチューデントのt分布——実際のリターンが持っているような重い裾)だ——しかしこの成分はここではメカニズムのためではなく、リアリズムのために入っている。ファットテールこそがこの罠を可能にしているのだと言いたくなるし、私たちも制御実験を走らせるまでまさにそう仮定していた:この市場のガウスノイズ版(結果ではgaussian_control、300シード)は罠をほぼそのまま再現する——1トレードあたりの目的関数は、ファットテールでの57.0%に対しガウスノイズの下でも55.7%の確率で退化し、そのアウトオブサンプルのシャープレシオは0.70に対して0.71だ。つまりこの罠は、ファットテールについての話ではない。それは純粋な小サンプル+選択の効果だ:エクスポージャーの低い約20通りの閾値にわたって、一握りの観測値で計算されたシャープレシオの最大値を取れば、幸運などこかの隅が常に壮観に見えてしまう。どんなノイズ分布でもそれは起きる。ほんの数バーしか市場にいない戦略は、運だけで、いくつかの好都合な値動きを乗り切り、何の意味もないインサンプルの数字を記録できる。私たちがファットテールを保持しているのは、実際のリターンがそれを持っているからであって、罠がそれを必要としているからではない。
戦略。 1パラメータの族。シグナルの大きさが閾値を下回っている限りその方向に取引し、そうでなければフラットのままでいる:
極小のは最も小さく、最も目立たないシグナルでしか取引しない——ほとんど市場にいることのない、レアトレードの宝くじだ。帯の端に近いは本物のエッジ全体を捉え、十分にエクスポージャーを持つ。巨大なはすべてを取引し、エッジを一切運ばずノイズだけを加える帯外のバーまで巻き込む。
def gen_dataset(T, rng, beta=0.30, band=1.0, tail_df=4):
s = rng.standard_normal(T)
edge = beta * np.where(np.abs(s) <= band, s, 0.0) # edge ONLY inside |s| <= 1
t = rng.standard_t(tail_df, T) / np.sqrt(tail_df / (tail_df - 2.0)) # fat tails, unit var
return s, edge + t
def simulate(s, r, theta):
pos = np.where(np.abs(s) <= theta, np.sign(s), 0.0) # trade the band, skip outliers
strat = pos * r
active = pos != 0.0
exposure = active.mean() # fraction of bars in a position
sharpe_full = strat.mean() / strat.std(ddof=1) # on the WHOLE timeline
sharpe_active = strat[active].mean() / strat[active].std(ddof=1) # on ONLY the active bars
return dict(exposure=exposure, n_trades=int(active.sum()),
sharpe_full=sharpe_full, sharpe_active=sharpe_active, pnl=strat.sum())
市場を自分たちで構築したので、真実を直接計算できる——600シードすべてにわたる、各閾値でのアウトオブサンプルの平均パフォーマンスだ。真の最適解はに位置する:ちょうどシグナル帯の端にあり、約70%の時間市場にいて(導出:標準正規分布のシグナルがに収まる確率は)、アウトオブサンプルの年率シャープレシオ1.77を記録する。それが、あらゆる目的関数が見つけようとしている数字だ。これを覚えておいてほしい:θ≈1.04、アウトオブサンプルのシャープレシオ1.77、タイムラインの約70%で市場にいる。 目的関数が選ぶものがこれから遠く離れているなら、それは市場が難しいからではなく、目的関数が失敗しているからだ。
第2幕——罠:8回の幸運なトレード、シャープレシオ21、蜃気楼

さあ、素朴な目的関数をこの市場の1つの具体的な抽選——シード6——に放ってみよう。シードについては包み隠さず言っておく:それは最初の抽選でも、無作為な抽選でもない。私たちは際立って退化した1トレードあたりの勝者が出るシードをスキャンして、これを選んだ——まさにメカニズムを見逃しようがないようにするためだ。それが示す結果は完全に典型的なものだ——第3幕が確認するように、1トレードあたりの目的関数は全シードの56%でエクスポージャー5%未満の宝くじを選ぶ——しかしシード6の大きさはその分布の極端な端に位置している。これは、中央値ではなく、よくある失敗の特に際立った1事例として読んでほしい。私たちが最適化するのは1トレードあたりシャープレシオ:戦略が実際にポジションを持っていたバーだけで計算されるシャープレシオだ。これは報告するにはきわめて自然なことだ。「取引したとき、そのトレードはどれだけ良かったか?」という問いだ。まるでアイドル状態からスキルを切り分けているように感じられる。実際にはその逆をやっている。
シード6で1トレードあたりシャープレシオが王座に据える戦略はこうだ:
- 閾値——最も小さなシグナルだけで取引する。
- 市場エクスポージャー0.4%——99.6%の時間フラットだ。
- 8回のトレード。 8回。2000バーの間に。
- インサンプルの1トレードあたり年率シャープレシオ:21.09。
- インサンプルのフルタイムラインシャープレシオ:0.82。
- アウトオブサンプルのフルタイムラインシャープレシオ:0.13。
1トレードあたり指標は21.09と読み取れる——実在する戦略が記録したことのない数字であり、ファンドが立ち上がってしまうような数字だ。そしてそれは完全な蜃気楼だ。その8回のトレードはたまたまいくつかの好都合な値動きを捉えた。その8バーだけで測れば、平均と標準偏差の比は天文学的な値になる。しかしフルタイムライン——戦略が99.6%の時間フラットである場所——では、その「エッジ」は実質的に何も寄与しない:インサンプルのフルタイムラインシャープレシオは0.82で、新しいデータでは0.13まで崩壊する。目的関数が選んだ勝者は、取引の目的上、事実上フラットだ。
しかもそれはその閾値において本物のエッジですらない。市場を思い出してほしい:エッジは帯の中に存在し、はまさにシグナルが最も弱い中心にある。における真のアウトオブサンプル曲線は**−0.01**——ゼロと見分けがつかない(既知の正解の曲線から導出)。サーチは小さな本物のエッジを見つけたのではない。8回の幸運なノイズの抽選を見つけ、それをシャープレシオ21として報告しただけだ。
これがミニチュア版の罠だ:1トレードあたりシャープレシオは、戦略ができる限りまれにしか取引しないことに報酬を与える。なぜなら、あなたが立つバーが少なければ少ないほど、そのうちのいくつかが幸運になるのは容易になり、そしてこの指標は一度たりとも「でも本当に市場にいたのか?」と問わないからだ。シード6の大きさは際立った例を求めて選ばれたものだが、その種類はそうではない。600シード全体で、1トレードあたりシャープレシオは57%で退化した勝者(ほとんど取引しないか、アウトオブサンプルで損失を出すもの)を選び、具体的にはエクスポージャー5%未満の宝くじを56%で選ぶ。典型的な退化した選択は、シード6のシャープレシオ21よりもはるかに穏やかだ:600シード全体で平均すると、1トレードあたり目的関数の勝者はインサンプルの1トレードあたりシャープレシオ4.58、平均エクスポージャー0.286を持つ——依然としてほとんどの時間フラットだが、99.6%フラットというわけではない。シード6はメカニズムを劇的に見せているだけであり、心配すべきなのは56%の方だ。半分以上の確率で、この日常的な指標はあなたに宝くじを手渡し、それを戦略と呼ぶ。
第3幕——統計的真実:6つの目的関数、600シード

1つのシードは何も証明しない。それは例示するだけだ。目的関数を測定するには、多くの独立した市場にわたって平均的にそれが何を選ぶのかを問い、サーチが一度も見たことのないデータでその選択を採点しなければならない。そこで:600シード、それぞれ市場からの独立した1回の抽選。各シードで、各目的関数の下で80閾値サーチを走らせる。選ばれたもののエクスポージャー、インサンプルとアウトオブサンプルのシャープレシオ、そしてその選択が退化していたかどうかを記録する。
| 目的関数 | 平均エクスポージャー | インサンプルのシャープレシオ | アウトオブサンプルのシャープレシオ | IS→OOSの低下(絶対値) | 退化 |
|---|---|---|---|---|---|
| 生のPnL | 0.859 | 1.76 | 1.61 | 0.15 | 0.0% |
| フルタイムラインのシャープレシオ | 0.740 | 1.82 | 1.71 | 0.11 | 0.0% |
| 1トレードあたりシャープレシオ | 0.286 | 1.00 | 0.70 | 0.30 | 57% |
| エクスポージャーの下限() | 0.740 | 1.82 | 1.71 | 0.11 | 0.0% |
| conf_kによる縮小() | 0.523 | 1.54 | 1.31 | 0.23 | 20.7% |
| 頑健な目的関数(下限+conf_k) | 0.675 | 1.78 | 1.70 | 0.08 | 0.2% |
「IS→OOSの低下」列は、インサンプルからアウトオブサンプルへの年率シャープレシオの絶対的な下落幅であり(例えばは0.30の低下)、パーセンテージではない。そして「エクスポージャーの下限」の行が「フルタイムラインのシャープレシオ」の行とバイト単位で同一であることに注目してほしい:それは偶然ではなく、第5幕でその理由を説明する。
3つの事実が際立ち、それぞれが教訓になる。
1トレードあたりシャープレシオは、退化する唯一の素朴な目的関数だ。 その平均エクスポージャーは0.286——ほとんどの時間フラットな戦略を選んでいる——であり、そのインサンプルシャープレシオ1.00は0.30下落してアウトオブサンプルの0.70になり、この中で最悪だ。兆候に注目してほしい:そのインサンプルの数字(1.00)はさして印象的でもないのに、単一のシードでは平気で21という1トレードあたりの数字を報告する。幸運な窓が無作為な方向を向いているため、平均は打ち消し合う。アウトオブサンプルまで生き残るのはわずか0.70だけであり、個々の選択の57%はまるっきりゴミだ。
エクスポージャーを考慮した目的関数は本質的に安全だ。 生のPnLとフルタイムラインのシャープレシオは決して退化しない(0.0%)。理由は構造的なものだ:どちらもタイムライン全体にわたって測定されるので、99.6%の時間フラットな戦略はそれらの下ではほとんど何も稼げない。フルタイムラインの指標を、まれにしか取引しないことでごまかすことはできない——フラットな状態は分母に入るため、直接かつ自動的にペナルティを受ける。これがこの記事全体で最も重要なアイデアであり、第6幕で改めて取り上げる。
生のPnLは安全だが最適ではない——過剰にエクスポージャーを取る。 よく見てほしい:生のPnLの平均エクスポージャーは0.859、全体で最も高く、そのアウトオブサンプルシャープレシオ(1.61)はフルタイムラインのシャープレシオ(1.71)と真の最適解(1.77)を一段下回る。PnLは市場にいることに報酬を与えるので、サーチはを高くしすぎる方向に押す(シード6では、生のPnLは最適な1.04に対してを選ぶ)、それによりエッジを一切運ばずノイズだけを加える帯外のバーを引き込んでしまう。爆発はしない——だが、1トレードあたりの罠とは逆方向に、本当の最適解を通り過ぎて漂う。目的関数が違えばバイアスも違うが、教訓は同じだ:指標が戦略を選んだのだ。
まだ議論していない2つの行——エクスポージャーの下限とconf_k——が修復にあたる。それが次の幕だ。
第4幕——なぜ8回のトレードは決して信頼できないのか

罠を修復する前に、なぜ8回のトレードが何の意味もないシャープレシオ21を生み出すのかを正確に理解しておく価値がある——修復はその理由から直接導かれるからだ。
シャープレシオは推定値であり、推定値には誤差がある。Andrew Loの2002年の結果は、個の観測値から推定されたシャープレシオの標準誤差を、最も寛容な仮定(IIDガウス分布のリターン)の下で与えている:
誤差はとしてしか縮まない。これを罠に当てはめてみよう。シード6の1トレードあたりシャープレシオは年率であり、これは観測1件あたり、バーで計算されている。標準誤差は
(Loの公式から導出)。点推定は、その1シグマの誤差幅はおよそのオーダーだ——これは校正された信頼区間ではなく、目安となる桁数として読んでほしい。この公式は、私たちのファットテールなノイズが破っているIIDガウス分布のリターンを仮定しているからだ。それでも、メッセージは紛れもない:「シャープレシオ21」は、実質的に何の情報も持たないほど広い分布から引かれた数字だ——そしてこれは寛容な計算にすぎない。なぜならMertensの拡張は、ファットテールと歪度が標準誤差をさらに膨らませるだけであることを示しているからだ。レアトレードのバックテストのシャープレシオは、その点推定値そのものよりもあらゆる方向で信頼できない:観測数が少なすぎ、しかも分布が間違っている。
これはまさに**最小トラックレコード長(Minimum Track Record Length)**が定式化しているものだ(Bailey & López de Prado, 2012)。それは問いを逆転させる——信頼度でこの大きさのシャープレシオを信じることが許されるまでに、いくつの観測が必要か?——
これは「トレード数の少ないバックテストをあまり信用するな」を、明示的で検証可能なトレード数へと変換する。目的関数設計にとっての深い論点はこうだ:優れた目的関数は、内側から最小トラックレコード長を強制すべきであり、人間が事後になって勝者が8つの観測に基づいていることに気づく、という形であってはならない。1トレードあたりシャープレシオはその逆をやっている——それは観測数を最小限まで押し下げることによって最大化される。「できるだけ少ないトレード数」に最適解が置かれる目的関数は、構造上、自分自身の最も信頼できない推定値を探し求める目的関数なのだ。
罠の中では2つの失敗が重なっており、両方に名前を付けることが修復の仕方を教えてくれる。第一に、小サンプルのノイズ:8個の観測ではどんな比率も特定できない。第二に、選択:その8バーは私たちに手渡されたものではない——サーチがその閾値を選んだのであり、それは部分的に、それらが幸運だったからこそだ。サーチは最大化装置であり、常に、ノイズがたまたまシグナルのように見える空間の隅を見つけ出す。これは、より良い点推定でごまかせるものではない。「最良」の意味そのものを変えて、その幸運な隅が最大値にならないようにしなければならない。
第5幕——修復:エクスポージャーの下限とトレード数による縮小

私たちには2つの名前付きの病がある——まれにしか取引しないことと、観測数が少なすぎるものに依存していること——だから、それぞれを狙った2つの治療法を書く。
治療法1:エクスポージャーの下限。 最も単純にありうる修正だ。市場に少なくともの時間いない戦略は、はっきりと却下する——ほとんど取引しないなら、スコアはになり、サーチはあなたを選べない。しかし何に下限を設けるかには誠実な機微があり、それがこの記事全体の静かな教訓だ。単独の目的関数として、私たちはフルタイムラインシャープレシオに下限を設けたが、このデータではそれは何も変えない。フルタイムラインのシャープレシオ自身の勝者はすでに約74%のエクスポージャーにいるので、20%の下限が発動することは一度もない。だからこそ、上の表の「エクスポージャーの下限」と「フルタイムラインのシャープレシオ」の行はバイト単位で同一なのだ——すでに安全な指標に下限をボルト留めしても、単にフルシャープレシオを再導出しているにすぎない。下限が目に見える働きをするのは、それがなければ隅へと突っ走ってしまう指標——下記の頑健な目的関数における1トレードあたり指標のような——を守っているときだけだ。言い換えれば、このデータにおいて「エクスポージャーを要求する」ことと「フルタイムラインで測定する」ことは、同じ介入の2つの名前にすぎない。
治療法2:トレード数による縮小——「conf_k」。 1トレードあたり指標から抜け出せず、ハードな締め切りではなくソフトな補正が欲しい場合のために:シャープレシオを、それが依拠するトレード数に応じて連続的に割り引く。を掛ける。ここではトレード数、はサーチの前に選ばれる固定の「信頼度定数」——トレード数換算での事前の強さだ:
のとき、生のシャープレシオがどれほど大きくてもスコアはゼロへと押しつぶされる。のとき、スコアは生のシャープレシオへと収束する。これは第4幕のMinTRLと標準誤差と同じ補正ロジックだ——小サンプル推定値を、そのサンプルサイズの減少関数としてゼロへと縮める——それを事後的なフィルタとして適用するのではなく、目的関数そのものに折り込む。最も近い名前の付いた先例は、van Tharpのシステム・クオリティ・ナンバー()で、これも同様に、1トレードあたりの品質指標をトレード数でスケールさせる——ただし関数の形は異なる(は際限なく増大するが、は1で飽和する)。形としては、これはベイズ的な精度加重/経験ベイズ的な縮小であり、文献から借りてきた名前付きの推定量ではなく、この問題のために私たちが構築したものだ。
def obj_active_sharpe(m): # the trap: Sharpe on only the active bars
return m["sharpe_active"]
def _shrink(n, conf_k): # trade-count shrinkage n / (n + k)
return n / (n + conf_k) if (n + conf_k) > 0 else 0.0
def obj_confk(m, conf_k=40.0): # few trades -> little credit
return m["sharpe_active"] * _shrink(m["n_trades"], conf_k)
def obj_robust(m, e_min=0.20, conf_k=40.0): # both cures at once
if m["exposure"] < e_min: # floor: reject strategies that barely trade
return -np.inf
return m["sharpe_active"] * _shrink(m["n_trades"], conf_k)
さて、正直な部分に入ろう:どれだけの下限、どれだけの縮小が必要か?両方を掃引し、曲面全体を読む。各セルは、退化率を添えた200シード(600の3分の1のサブセット、2次元掃引を安く済ませるため)にわたるアウトオブサンプルシャープレシオの平均だ:
| \ conf_k | |||
|---|---|---|---|
| 0.00 | 0.66 (59.5%) | 1.26 (22.5%) | 1.47 (11.5%) |
| 0.05 | 1.43 (10.0%) | 1.53 (6.0%) | 1.60 (4.0%) |
| 0.10 | 1.64 (1.5%) | 1.65 (1.0%) | 1.67 (1.0%) |
| 0.20 | 1.71 (0.0%) | 1.71 (0.0%) | 1.71 (0.0%) |
| 0.35 | 1.73 (0.0%) | 1.73 (0.0%) | 1.73 (0.0%) |
アウトオブサンプルの年率シャープレシオの平均、カッコ内は退化率。左上のセルは生の1トレードあたりシャープレシオ——下限なし、縮小なし:アウトオブサンプル0.66、退化率59.5%。これは第3幕の1トレードあたりシャープレシオの行と同じ目的関数であり、そちらは0.70/57%だった。わずかな差は純粋にシード集合の違いによるもの——この掃引は200シードを使い、モンテカルロは600すべてを使った。同じ指標、より小さなサンプル。
この曲面は3つの読み方できれいな物語を語る。
それぞれの治療法は単独で効く。 最上段を右に進む(縮小を加え、下限なし):アウトオブサンプルはと上昇し、退化率はと下がる。左列を下に進む(下限を加え、縮小なし):アウトオブサンプルはと上昇し、退化率はと下がる。どちらのノブも、単独で回すだけでアウトオブサンプルのパフォーマンスを独立に押し上げ、退化を消し去る。エクスポージャーの下限がここでは単独では強力なレバーだ。なぜなら、罠を定義づける特徴——ほぼゼロのエクスポージャー——を正面から攻撃するからだ。
組み合わせると台地に達する——そしてその台地はまさにフルタイムラインのシャープレシオそのものだ。 までに、この行はどの縮小水準でもアウトオブサンプル1.71、退化率0%で平らになる。まで押すと1.73にわずかに近づく。しかし、その1.71が何であるかをよく見てほしい:それは第3幕で素のフルタイムラインシャープレシオが、下限も縮小も一切なしで記録するのとまったく同じスコアだ。最良の状態においてさえ、事後補修はフルタイムラインシャープレシオを上回らない——それを再構築するだけだ。そして完全に修復された頑健な目的関数でさえ、そこにさえ完全には届かない:600シード全体で、アウトオブサンプル1.70、残留する退化率0.17%に着地し、フルシャープレシオの1.71/0%をわずかに下回る——つまりより単純な指標に弱く支配されている。控えめな中間設定である、は、アウトオブサンプル1.65、退化率**1%**に達する——1トレードあたり指標を強いられた場合には便利だが、それを選好する理由には決してならない。
正確な数値はスケール依存だ——結果は形にある。 この市場を完全に修復する具体的な値、は、このデータ生成過程に合わせて調整されたものだ。取引頻度や裾の厚さが異なる別の市場では、台地は別の場所に位置する。一般化するのは座標ではなく曲面だ:両方向への単調な上昇、ゼロへと駆逐される退化、真実の位置にある台地。あなた自身の座標は、上記とまったく同じように掃引することで見つける。
両方の治療法を組み合わせた——下限0.20とconf_k 40の頑健な目的関数——を持って、シード6に戻ろう。罠は、8回のトレード、フルタイムラインのアウトオブサンプルシャープレシオ0.13を王座に据えた。頑健な目的関数は代わりにを選ぶ:市場エクスポージャー0.66、447回のトレード、アウトオブサンプルの年率シャープレシオ1.77。そのは真の最適解より1グリッドポイント下にあるので、的の真ん中に着地するのではなく、ほぼ最適で十分にエクスポージャーを持った閾値を回復する——その単一シードのアウトオブサンプルシャープレシオ(1.77)が、たまたま母集団の最適値と一致しているにすぎない。同じデータ、同じサーチ、同じ80通りの候補閾値。「最良」の定義だけが変わり、それが勝者を、フラットな8トレードの蜃気楼から、本物の、十分にエクスポージャーを持ったエッジへと動かした——そしてそれは、意味深いことに、このシードで素のフルタイムラインシャープレシオが選ぶのとまったく同じ閾値だ。
掃引が明らかにする1つの注意点:この市場ではconf_k単独では不十分だ。下限なしででは、シードにわたる退化率はまだ22.5%だ——そしてシード6に限って言えば、conf_k単独は、35回のトレード、アウトオブサンプルシャープレシオ**−0.06**を選ぶ。35回のトレードはの縮小を生き延び、それでも勝つのに十分なスコアが残る。この最後の隙間を埋めるのがエクスポージャーの下限だ。なぜなら、それはトレード数を罠の兆候の代理として信頼するのではなく、罠の真の特徴——フラットであること——を直接狙うからだ。
第6幕——より深い教訓:タイムライン全体で測定せよ

修復から一歩下がって、実際に何が安全な目的関数を罠から分けたのかに注目しよう。それは洗練さではなかった。生のPnLとフルタイムラインのシャープレシオは1トレードあたりシャープレシオよりも単純であり、それでも決して退化しなかった——下限も縮小も一切のチューニングもなしに、600シード全体で0%だ。
分かれ目はたった1つの性質だ:その指標はどの窓を測定しているのか? 1トレードあたりシャープレシオは、戦略が立つことを選んだバーだけを測定する——サーチが自由に縮められる、自己選択的な窓だ。フルタイムラインのシャープレシオと総PnLは、フラットなバーを含むタイムライン全体を測定する。そして、フルタイムラインの指標をまれにしか取引しないことで大きくすることはできない。なぜなら、フラットで過ごすすべての時間は、何も稼がない分母の中の1時間だからだ。エクスポージャーの下限とconf_kは、結局のところ、1トレードあたり指標に、フルタイムライン指標がタダで持っているエクスポージャー感度を事後的に補修する方法にすぎない——そして掃引はすでにその補修の天井を教えてくれた:最良の状態でもフルタイムラインシャープレシオに追いつくだけで(アウトオブサンプル1.70 対 1.71)、決して上回らない。窓を自由に選べるなら、タイムライン全体を選び、事後補修そのものを丸ごとスキップしよう。
だから、設計原則を平易に述べるとこうなる:
目的関数を、まれな幸運なトレードでごまかされないように設計せよ。 好ましい順に、3つの道具がある:
- タイムライン全体で測定する。 ほとんど離れるべきではないデフォルトだ。フルタイムラインのシャープレシオと総リターンは、構造上エクスポージャーを考慮している——フラットなバーがカウントされるので、アイドル状態は自動的にペナルティを受ける。「アクティブだったバーだけ」で計算された指標を報告していることに気づいたら、立ち止まり、サーチがそのバーを自由に選べることで何をするかを自問してほしい。
- エクスポージャーを要求する。 アクティビティ条件付き指標を使わざるを得ないなら、エクスポージャーに下限を設け、サーチがほとんど取引しない戦略を選べないようにする。これは、この特定の罠に対する単独では最強のレバーだ。
- トレード数で縮小する。 一握りの観測に基づく比率が、何千もの観測に基づく比率のごく一部の信頼しか得られないよう、であらゆる比率を割り引く。これは最小トラックレコード長を目的関数レベルで強制するものだ:少ない観測から得た数字は信頼できない(第4幕)ので、誠実な目的関数はその信頼できなさを、後で人間が気づくのを当てにするのではなく、あらかじめ価格に織り込んでおく。
これらのどれも、サーチを賢くするわけではない。目標を誠実にするだけだ。だから、サーチがいつもやっていること——最大値を見つけること——を実行したとき、その最大値があなたが本当に望む戦略になる。
誠実さについての注記
率直に述べる3つの留保事項。制御された研究がその結論に値するのは、限界に名前を付けたときだけだからだ。
- 市場は合成データであり、意図的にそうしている。 標準正規分布のシグナル、に限定された線形のエッジ、ファットテールなスチューデントの()ノイズ——これらは市場のリアリズムのためではなく、制御された既知の正解のために選ばれた。目的関数が間違った戦略を選ぶことを証明できるのは、どの戦略が正しいかを知っているデータの上でそれを走らせたときだけだ。ファットテールは私たちが保持した現実的な成分だが——最初の直感に反して、そしてこの記事自体の以前の草稿にも反して——罠を動かしているものではない:ガウスノイズの制御実験(300シード)は、ここでの57.0%に対して55.7%の確率で退化し、アウトオブサンプルシャープレシオは0.70に対して0.71だ。罠は、重い裾があってもなくても生き残る小サンプル+選択のアーティファクトだ。成果物は診断と修復のパターンであり、戦略でも普遍定数でもない。
- 修復する値はスケール依存だ。 罠を完全に閉じる具体的な下限と縮小は、このデータ生成過程——その取引頻度、その裾の厚さ、そのエッジの大きさ——に合わせてフィットされている。他のデータでは台地は移動する。移りわたるのは掃引曲面の形(両方のノブでの単調な上昇、ゼロへの退化、真実における台地)と、自分自身の座標を見つける方法だ:数字をコピーするのではなく、両方を掃引して曲面を読め。
- conf_kは私たちの構築物であり、名前の付いた推定量ではない。 トレード数による縮小は、私たちがこの問題のために構築した、ベイズ的な精度加重/経験ベイズ風の装置だ。その根拠は、検証されたLo/Mertensの標準誤差の結果とBailey–López de PradoのMinTRLに基づいており、最も近い名前の付いた近縁種はvan Tharpのシステム・クオリティ・ナンバー(、異なる関数形)だが、そのものが文献のどこかに名前付きで登場すると主張するものではない。その相棒となる治療法——エクスポージャーの下限、フルタイムラインでの測定——は、正確に述べられた標準的な実践だ。そして、ここですでに安全だった目的関数がどれかに注目してほしい:生のPnLとフルタイムラインのシャープレシオは決して修復を必要としなかった。それらは最初からエクスポージャーを考慮していたからだ——フルタイムラインシャープレシオに下限を設けてもフルタイムラインシャープレシオに帰着するほどに、その勝者はすでにどんな妥当な下限もクリアしている。罠は具体的には1トレードあたり/アクティブシャープレシオであり——完全に修復された1トレードあたり目的関数でさえ、フルタイムラインシャープレシオに追いつくだけで(アウトオブサンプル1.70 対 1.71)、決して上回らない。主要な教訓は修復ではない。そもそもタイムライン全体で測定することだ。
まとめ
- 目的関数は決定そのものであり、形式的なものではない。 サーチは「良い戦略」を見つけているのではない——あなたが手渡したスカラーの最大化装置を見つけるのであり、異なるスカラーは同一のデータの上でまったく異なる戦略を選ぶ。目的関数を選ぶことは、戦略を選ぶことそのものであり、それ以降のすべては帳簿付けにすぎない。それがグッドハートの法則だ:あなたの指標がサーチの目標になった瞬間、サーチはそれとあなたが意図したものとの間のあらゆる隙間を突いてくる。
- 1トレードあたりシャープレシオは罠だ。 戦略が取引したバーだけで測定されるこの指標は、できるだけまれに取引することで最大化される——観測が少ないほど、いくつかの幸運な値動きが比率を膨らませやすくなる(ガウス分布の制御実験は、ファットテールが必要ではないことを確認している。これは小サンプル+選択の効果だ)。600シード全体で、それは56%でエクスポージャー5%未満の宝くじを選び、57%で退化する。典型的な退化した選択の平均は、インサンプルの1トレードあたりシャープレシオ4.58だ。意図的に際立たせた1つのシードでは、インサンプルの1トレードあたりシャープレシオ21.09を持つ8トレード・0.4%エクスポージャーの戦略を王座に据え、それがフルタイムラインのアウトオブサンプルシャープレシオ0.13まで崩壊した。8個の観測に基づく比率は、年率換算でおよそ±7.7のオーダーの標準誤差を持つ(第4幕)——それは一度も情報だったことがない。
- エクスポージャーを考慮した目的関数は本質的に安全だ。 生のPnLとフルタイムラインのシャープレシオは決して退化しなかった(0%)。なぜなら、それらはタイムライン全体を測定し、アイドル状態が自動的にペナルティを受けるからだ。フルタイムラインの指標をまれにしか取引しないことでごまかすことはできない。生のPnLの唯一の欠陥は逆方向のバイアスだ——過剰にエクスポージャーを取り(平均エクスポージャー0.859、アウトオブサンプル1.61、真の1.77に対して)、市場によりいる方向へを最適解の先まで漂わせる。
- 修復は機能する——だが、それが取り戻せるのはフルタイムラインシャープレシオまでだ。 エクスポージャーの下限とトレード数(conf_k)による縮小は、それぞれ独立にアウトオブサンプルシャープレシオを押し上げ、退化をゼロへと駆逐する。組み合わせると台地に達する。しかしその台地はまさにフルタイムラインシャープレシオそのものだ:退化率はでの59.5%からでの0%まで下がり、アウトオブサンプルシャープレシオは0.66から1.71まで上昇する——これは素のフルシャープレシオが助けなしで記録する数字とまったく同じであり、完全に修復された頑健な目的関数は実際にはそれよりわずかに下に着地する(アウトオブサンプル1.70、退化率0.17%:弱く支配されている)。シード6では、頑健な目的関数がほぼ最適で十分にエクスポージャーを持った閾値を回復する(、真のより1グリッドポイント下、447回のトレード、アウトオブサンプルシャープレシオ1.77)。conf_kは、1トレードあたり指標を強いられたときのフォールバックとして扱うべきであり、タイムライン全体の測定に対するアップグレードとしてではない。正確な座標はスケール依存だが、曲面の形は移りわたる結果だ。
- 目的関数を、まれな幸運なトレードでごまかされないように設計せよ。 好ましい順に:タイムライン全体で測定する(デフォルト)、エクスポージャーを要求する(単独では最強のレバー)、トレード数で縮小する(目的関数レベルの最小トラックレコード長)。これらのどれも、サーチを賢くするわけではない——目標を誠実にするだけであり、それによってサーチが必然的に見つける最大値が、あなたが実際に取引したいと思う戦略になる。
パラメータサーチは従順なランプの精だ。それはあなたが言い表した願いを正確に叶えるのであり、あなたが意味した願いを叶えるのではない——そして「この指標を最大化せよ」がその願いだ。それを1トレードあたりシャープレシオとして言い表せば、8回の幸運なトレードを呼び出し、それを財産と呼ぶ。フラットでいることが罰せられ、一握りのトレードがわずかな信頼しか得られないように言い表せば、同じランプの精は、同じデータの上で、本物のエッジをあなたに手渡す。あなたが展開する戦略は、目的関数を打ち込んだその瞬間に選ばれていた。意図を持ってそれを選べ。
完全な実験——合成市場、6つの目的関数、600シードのモンテカルロ、そして修復掃引曲面、1つの決定論的スクリプトから再生成できるすべての数値——は、姉妹編の論文objective-design.marketmaker.ccにあり、コードとデータはgithub.com/suenot/objective-design-degeneracyで公開している。
これは、私たちの他の研究が別の角度から戦っている、同じ戦争の1つの戦線だ。デフレーテッド・シャープレシオは、多重検定の後にサーチの勝者に値段をつける——この記事が目的関数がそもそも正しい戦略を選んだかを問うのに対し、DSRは選ばれた戦略が運だけで生み出されるものを上回るかを問う。近日公開予定のバックテスト過学習の確率の研究は、同じ選択バイアスをリサンプリング側から攻撃し、勝者ではなく手続きを採点する。そして先読みバイアス分類は、偽物のシャープレシオを生み出すもう1つの大きな製造元——未来からのリーク——を目録化しており、それはまったく異なるメカニズムを通じて同一の症状(見事だが実運用では死ぬバックテスト)を生み出す。目的関数設計、デフレーション、過学習確率、先読み:1つの規律に対する4つの名前——自分自身のバックテストにだまされないこと。
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.