← 返回文章列表
June 28, 2026
5 分鐘閱讀

目標函數設計:你最佳化的那個指標,正悄悄替你選好了策略

#演算法交易
#回測
#過擬合
#目標函數
#最佳化
#夏普比率
#驗證
🎯
Part 1 of 9 · Collection
Backtesting Without Fooling Yourself

"回測無幻覺"系列文章。

📄 本文成長為了一篇研究論文。 以下每一個數字都來自同一個確定性指令碼,它構建了受控的真實基準——一個在中等訊號區間內帶有已知優勢、且處處充滿肥尾噪聲的合成市場——然後在六種不同的目標函數下運行同一次閾值搜尋,並在樣本外測量每種目標函數實際選出的是哪個策略。可線上閱讀論文(互動版 + PDF):objective-design.marketmaker.cc,程式碼與資料見 github.com/suenot/objective-design-degeneracy

你想要那個最好的策略。於是你跑一次搜尋——掃一遍閾值、回看視窗、止損距離,留下得分最高的那個設定。搜尋跑完,交給你一個贏家。合情合理。標準做法。這是地球上每一個最佳化器、網格搜尋、超參數調優器都在做的事。

但看看這個動詞:"得分最高"。在什麼上最高?在搜尋能給任何東西加冕之前,你必須先交給它一個要最大化的單一數字——一個目標函數。PnL。夏普比率。只算你交易過的那些 K 線上的夏普比率。收益除以最大回撤。你敲下了其中一個,多半沒怎麼細想,然後搜尋花了上百萬次評估,精確地做了你要求的事。

這一個選擇不是走個形式。它就是整個決定。搜尋找不到"一個好策略"——抽象意義上根本沒有這種東西。它找到的是讓你選中的那個標量最大化的策略,而不同的標量,在同一份資料上會指向截然不同的策略。目標函數是握著方向盤的那隻暗手,而多數時候沒人在看它。

這整篇文章可以濃縮排一張表。一次閾值搜尋,一個帶有真實已知優勢的合成市場,六種目標函數——以及它們各自選出的六種策略,用留出資料測量:

目標函數(搜尋最大化的物件) 平均市場曝光率 樣本內夏普比率 樣本外夏普比率 退化贏家佔比
原始 PnL 0.859 1.76 1.61 0%
全時間線夏普比率 0.740 1.82 1.71 0%
逐筆("活躍")夏普比率 0.286 1.00 0.70 57%
曝光率下限(emin=0.20e_{\min}=0.20 0.740 1.82 1.71 0%
交易筆數收縮(conf_k=40=40 0.523 1.54 1.31 20.7%
穩健目標函數(下限 + conf_k) 0.675 1.78 1.70 0.2%

600 個獨立種子,每個 T=2000T = 2000 根 K 線,每次搜尋 80 個候選閾值,樣本內與樣本外獨立抽取。年化夏普比率(每年 252 個週期)。"退化"指所選贏家在市場中的時間佔比低於 5%,或樣本外夏普比率非正。這個市場的真實最優值,是樣本外年化夏普比率 1.77

把第三行盯著看,直到它刺痛你為止。逐筆夏普比率——代表著一整個"活動條件"指標家族(逐筆夏普比率、期望值、van Tharp 的 SQN、勝率),全都只在你交易過的那些 K 線上計算——選出的策略,樣本外表現比另外一半的目標函數都要差,而且57% 的時候都退化得一塌糊塗。這不是一個略遜一籌的目標函數。在這份資料上,它是一個陷阱,搜尋超過一半的時候都會一頭栽進去。再看看它上面那一行:樸素的全時間線夏普比率從不退化,樣本外得分 1.71。這就是整個修復過程的結論,提前劇透——誠實的修復辦法很簡單,就是在整條時間線上測量;下面那些更花哨的補丁方案,最好的情況下也只是追平這個數字,從未超過它。本文剖析的就是這個陷阱及其修復辦法,全程都有已知的真實基準撐腰,所以"目標函數是否選對了策略"是一個事實,而不是一個主觀意見。

第 1 幕——秘密的決定:古德哈特定律,就是搜尋本身

一次參數搜尋被描繪成一個漏斗,裡面擠滿了眾多候選策略曲線,一個標著"目標函數"的鏡頭把其中一條曲線蓋章為贏家,而其餘長相相似的曲線被淘汰,寓意指標的選擇悄悄決定了策略

1975 年,經濟學家 Charles Goodhart 寫下了一句比他其餘所有工作都更長壽的話:

"任何被觀測到的統計規律,一旦被用於控制目的而承受壓力,都傾向於崩潰。"

更精煉的通俗版本——通常被歸功於 Marilyn Strathern——是:一旦一個衡量指標變成了目標,它就不再是一個好的衡量指標。

參數搜尋是古德哈特定律最純粹的例項。目標函數就是那個衡量指標。搜尋就是那個壓力——成千上萬、上百萬次嘗試,把這個指標往它能到達的最高處推。而搜尋完全不在乎你想表達的是什麼。它只在乎那個數字。如果存在任何一種能讓數字變大、卻和真實的、可交易的優勢毫無關係的辦法——很少交易、大部分時間空倉、逮住幾個幸運的離群值——搜尋都會找到這條路,因為找到最大值正是它被造出來要做的唯一一件事。

這和 AI 安全文獻裡所說的**獎勵駭客(reward hacking)**是同一種失敗:一個針對你想要之物的代理指標進行最佳化的智慧體,會利用代理指標和真實目標之間的每一處縫隙。你的搜尋就是那個智慧體。"夏普比率"就是那個代理指標。"一個我能放心用真金白銀在下個季度交易的策略"才是真正的目標。它們之間的縫隙,正是這整個學科安身立命之處。

要親眼看著這道縫隙裂開,我們需要一個我們知道真相的世界。於是我們造了一個。

市場。 每個週期,一個預測因子 sts_t(一個標準正態訊號)先出現,隨後是它部分預測到的收益 rtr_t。優勢是真實存在的,但有邊界——它只存在於一箇中等訊號區間 st1|s_t| \le 1 之內,區間之外就消失了:

rt=βst1[st1]+εt,β=0.3,εtt4 (unit variance).r_t = \beta\, s_t \cdot \mathbf{1}\big[\,|s_t| \le 1\,\big] + \varepsilon_t, \qquad \beta = 0.3, \qquad \varepsilon_t \sim t_{4}\ (\text{unit variance}).

有兩個設計選擇很關鍵。第一,優勢活在中等區間內:極端訊號不攜帶任何預測資訊,所以策略應該交易中段、跳過尾部。第二,噪聲 εt\varepsilon_t肥尾的(自由度為 4 的 Student-tt 分佈,正是真實收益實際具有的那種厚尾)——但這個成分放在這裡是為了貼近現實,而不是構成機制。很容易就會想說,正是肥尾讓這個陷阱得以成立,我們一開始也正是這麼假設的,直到我們跑了對照實驗:這個市場的高斯噪聲版本(結果中的 gaussian_control,300 個種子)幾乎原樣重現了這個陷阱——高斯噪聲下逐筆目標函數依然在 55.7% 的時候退化,肥尾情形下是 57.0%,其樣本外夏普比率分別是 0.710.70。所以這個陷阱肥尾無關。它是一種純粹的小樣本加選擇效應:在約 20 個低曝光閾值上,取一個只在少量觀測上算出的夏普比率的最大值,總會有某個幸運的角落看起來驚豔絕倫。任何噪聲分佈都會產生這種效應;一個只在場內待了幾根 K 線的策略,純靠運氣就能撐過幾次有利的行情,報出一個毫無意義的樣本內數字。我們保留肥尾是因為真實收益確實有肥尾,不是因為這個陷阱需要它。

策略。 一個單參數族。只要訊號幅度低於閾值 θ\theta,就順著訊號方向交易,否則空倉:

post=sign(st)1[stθ].\text{pos}_t = \operatorname{sign}(s_t)\cdot \mathbf{1}\big[\,|s_t| \le \theta\,\big].

一個極小的 θ\theta 只在最小、最不起眼的訊號上交易——一張幾乎從不入場的稀有交易彩票。一個接近區間邊緣的 θ\theta 能捕獲全部真實優勢,曝光也充分。一個巨大的 θ\theta 什麼都交易,包括那些不攜帶優勢、只會添噪的區間外 K 線。

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 個種子上,每個閾值的樣本外平均表現。真實最優值落在 θ1.04\theta \approx 1.04:正好在訊號區間的邊緣,大約 70% 的時間在場內(推算:一個標準正態訊號落在 s1.04|s|\le 1.04 內的機率為 P(s1.04)=0.70P(|s|\le 1.04)=0.70),樣本外年化夏普比率為 1.77。這就是每一個目標函數都在努力尋找的數字。把它記在心裡:θ≈1.04,樣本外夏普比率 1.77,約 70% 的時間線處於場內。 任何目標函數選出的結果,只要遠離這個點,就是目標函數出了問題,而不是市場太難。

第 2 幕——陷阱:八筆幸運的交易,夏普比率 21,一場海市蜃樓

一臺老虎機在一次幸運的轉輪上吐出彩金,畫面裡是八張交易憑條和一個高聳的樣本內逐筆夏普比率 21,它在樣本外崩塌到 0.13,戲劇化地呈現了一個樸素目標函數選出的稀有交易彩票贏家

現在把一個樸素目標函數,放到這個市場的一次具體抽取上——種子 6。關於這個種子的坦白:它不是第一次抽取,也不是隨機選的。我們掃描了各個種子,找出一個逐筆贏家退化得格外觸目驚心的,專門挑了這一個,好讓這個機制無所遁形。它展示的結果是徹頭徹尾典型的——第 3 幕會確認,逐筆目標函數在全部種子中有 56% 會選出曝光率低於 5% 的彩票——但種子 6 的量級落在這個分佈的極端一端。請把它讀作一種常見失敗的一個格外觸目驚心的例項,而不是一箇中位數例項。我們最佳化的是逐筆夏普比率:只在策略實際持倉的那些 K 線上計算的夏普比率。這是一件極其自然的事,你很想拿出來彙報——"它一交易,交易質量到底怎麼樣?"這感覺像是把技巧從閒置中剝離了出來。實際上恰恰相反。

以下是逐筆夏普比率在種子 6 上加冕的策略:

  • 閾值 θ=0.005\theta = 0.005——只在最微小的訊號上交易。
  • 市場曝光率 0.4%——99.6% 的時間空倉。
  • 八筆交易。 八筆。在 2000 根 K 線裡。
  • 樣本內逐筆年化夏普比率:21.09
  • 樣本內全時間線夏普比率:0.82
  • 樣本外全時間線夏普比率:0.13

逐筆指標讀數是 21.09——一個從沒有真實策略報出過的數字,一個能讓一支基金就此發行的數字。而它完全是一場海市蜃樓。那八筆交易碰巧逮住了幾次有利的行情;只在這八根 K 線上測量,均值與標準差之比高得離譜。但在整條時間線上——策略在 99.6% 的時間裡空倉——那份"優勢"基本沒貢獻任何東西:樣本內全時間線夏普比率只有 0.82,在新資料上崩塌到 0.13。目標函數選出的這個贏家,在任何交易意義上,都等同於空倉。

而且在那個閾值上,甚至根本不存在真實的優勢。回想一下這個市場:優勢活在區間 s1|s|\le 1 之內,而 θ=0.005\theta = 0.005 正處在訊號最弱的死中心。θ=0.005\theta = 0.005 處的真實樣本外曲線是 −0.01——和零沒有區別(從真實基準曲線推算得出)。搜尋找到的不是一個微小的真實優勢。它找到的是八次幸運的噪聲抽取,然後把它們彙報成了一個夏普比率 21。

這就是這個陷阱的縮影:逐筆夏普比率會獎勵策略儘可能少地交易,因為你站立的 K 線越少,其中幾根走運的機率就越高,而這個指標從來不會問一句"但你真的在場內嗎?"種子 6 的量級是精心挑選出來的——我們就是在找一個觸目驚心的例子——但它的型別不是精心挑選的。在全部 600 個種子中,逐筆夏普比率有 57% 的時候選出一個退化的贏家(幾乎不交易,或者樣本外虧損),其中具體有 56% 是曝光率低於 5% 的彩票。典型的退化選擇比種子 6 的夏普比率 21 溫和得多:在全部 600 個種子上取平均,逐筆目標函數選出的贏家,樣本內逐筆夏普比率為 4.58,平均曝光率 0.286——大部分時間依然空倉,只是沒有 99.6% 那麼極端。種子 6 把這個機制戲劇化地呈現了出來;真正該讓你擔心的是那個 56%。超過一半的時候,這個稀鬆平常的指標塞給你一張彩票,還管它叫策略。

第 3 幕——統計學真相:六種目標函數,600 個種子

六種目標函數作為排行榜上的選手,逐筆夏普比率在樣本內發出最耀眼的光芒,卻被蓋上退化 57% 的印章,而曝光感知型目標函數在樣本外保持穩健,寓意樣本內的漂亮數字不代表一次好的選擇

單個種子什麼也證明不了;它只是一個例證。要衡量一個目標函數,我們必須問它平均而言在許多獨立市場上選出的是什麼,然後用搜索從未見過的資料去給這個選擇打分。於是:600 個種子,每個都是這個市場的一次獨立抽取;對每一個種子,在每種目標函數下跑一次 80 閾值搜尋;記錄它選出的結果的曝光率、樣本內與樣本外夏普比率,以及這次選擇是否退化。

目標函數 平均曝光率 樣本內夏普比率 樣本外夏普比率 樣本內→樣本外跌幅(絕對值) 退化佔比
原始 PnL 0.859 1.76 1.61 0.15 0.0%
全時間線夏普比率 0.740 1.82 1.71 0.11 0.0%
逐筆夏普比率 0.286 1.00 0.70 0.30 57%
曝光率下限(emin=0.20e_{\min}=0.20 0.740 1.82 1.71 0.11 0.0%
conf_k 收縮(k=40k=40 0.523 1.54 1.31 0.23 20.7%
穩健目標函數(下限 + conf_k) 0.675 1.78 1.70 0.08 0.2%

"樣本內→樣本外跌幅"這一列,是年化夏普比率從樣本內到樣本外的絕對下降幅度(例如 1.000.701.00\to0.70 就是下降 0.30),不是百分比。另外注意"曝光率下限"這一行和"全時間線夏普比率"逐位元組完全相同:這不是巧合,第 5 幕會解釋原因。

三個事實跳了出來,每一個都是一堂課。

逐筆夏普比率是唯一會退化的樸素目標函數。 它的平均曝光率是 0.286——它選出的策略大部分時間都在空倉——它的樣本內夏普比率 1.00 下降了 0.30,跌到樣本外的 0.70,是全場表現最差的。注意這個破綻:它的樣本內數字(1.00)本身甚至並不亮眼,但在任何單個種子上,它都樂意報出一個 21 的逐筆數字。均值之所以被抹平,是因為幸運視窗指向的方向是隨機的;能撐到樣本外的只有 0.70,而 57% 的具體選擇徹頭徹尾就是垃圾。

曝光感知型目標函數天生安全。 原始 PnL 和全時間線夏普比率從不退化(0.0%)。原因是結構性的:兩者都在整條時間線上測量,所以一個 99.6% 的時間都空倉的策略,在它們眼裡幾乎賺不到任何東西。你沒法靠很少交易來薅一個全時間線指標的羊毛——空倉會被直接自動懲罰,因為空倉的 K 線也計入分母。這是全文最重要的一個想法,第 6 幕會回到這一點。

原始 PnL 安全,但不是最優——它曝光過度。 仔細看:原始 PnL 的平均曝光率是 0.859,全場最高,它的樣本外夏普比率(1.61)比全時間線夏普比率(1.71)和真實最優值(1.77)都要那麼一截。PnL 獎勵的是待在場內,所以搜尋把 θ\theta 推得太高了(在種子 6 上,原始 PnL 選出 θ=1.84\theta=1.84,而最優值是 1.04),拖進了一堆不攜帶優勢、只會添噪的區間外 K 線。它沒有徹底崩盤——但它偏離真實最優值的方向,和逐筆陷阱正好相反。不同的目標函數,不同的偏差,同一個教訓:是這個指標選中了策略。

還有兩行我們尚未討論——曝光率下限和 conf_k——它們就是修復方案。這是下一幕的內容。

第 4 幕——為什麼八筆交易永遠不可信

一個單一的夏普比率點估計,因為只建立在八個觀測值之上而帶著一根巨大的置信區間誤差棒,旁邊是一把標著"最短業績記錄長度"的尺子,寓意在少數幾筆交易上測量出的比率在統計學上毫無意義

在修復這個陷阱之前,值得精確地弄清楚為什麼八筆交易會產生一個毫無意義的夏普比率 21——因為修復辦法正是直接從這個原因推出來的。

夏普比率是一個估計值,而估計值都帶著誤差棒。Andrew Lo 在 2002 年給出的結果,在最寬厚的假設下(獨立同分布的高斯收益),給出了用 TT 個觀測值估計出的夏普比率的標準誤:

SE(SR^)1+SR^2/2T.\operatorname{SE}\big(\widehat{SR}\big) \approx \sqrt{\frac{1 + \widehat{SR}^{\,2}/2}{T}}.

誤差只以 1/T1/\sqrt{T} 的速度收縮。把這個陷阱代進去。種子 6 上的逐筆夏普比率年化是 21.0921.09,折算到單次觀測是 1.331.33,是在 T=8T = 8 根 K 線上算出來的。標準誤是

SE1+1.332/280.49 per observation = 7.7 annualized\operatorname{SE} \approx \sqrt{\frac{1 + 1.33^2/2}{8}} \approx 0.49 \ \text{per observation} \ = \ \textbf{7.7 annualized}

(由 Lo 的公式推算而來)。點估計是 21.0921.09;它的一倍標準差誤差棒量級是 ±7.7\pm 7.7——請把這讀作一個示意性的數量級,而不是一個校準過的置信區間,因為這個公式假設的是獨立同分布高斯收益,而我們的肥尾 t4t_4 噪聲違反了這個假設。即便如此,資訊也是明確無誤的:"夏普比率 21"這個數字,來自一個寬到基本不攜帶任何資訊的分佈——而這還是寬厚的演算法,因為 Mertens 的擴充表明肥尾和偏度只會進一步放大標準誤。一次稀有交易回測的夏普比率,在每一個方向上都比它的點值更不可信:觀測太少,分佈也不對。

這正是**最短業績記錄長度(Minimum Track Record Length)**要形式化表達的東西(Bailey & López de Prado,2012)。它把問題倒過來問——我需要多少個觀測值,才有資格在置信度 pp 下相信這麼大的一個夏普比率?——

MinTRL=1+[1γ^3SR^+γ^414SR^2](Z1pSR^SR)2,\text{MinTRL} = 1 + \Big[\,1 - \hat\gamma_3\,\widehat{SR} + \tfrac{\hat\gamma_4 - 1}{4}\,\widehat{SR}^{\,2}\,\Big]\left(\frac{Z_{1-p}}{\widehat{SR} - SR^*}\right)^{2},

把"少交易的回測要少信一點"變成了一個明確的、可核查的交易筆數。對目標函數設計而言,深層的要點在於:一個好的目標函數應該從內部就強制執行一個最短業績記錄,而不是留給人在事後才注意到,贏家竟然只建立在八個觀測值之上。逐筆夏普比率做的恰恰相反——它正是靠把觀測數量壓向最小值來被最大化的。任何一個最優點落在"交易越少越好"上的目標函數,從構造上講,都是一個在尋找自己最不可靠估計的目標函數。

這個陷阱裡有兩種失敗疊加在一起,把它們都點名出來,就能告訴我們怎麼修。第一,小樣本噪聲:八個觀測值釘不住任何比率。第二,選擇:那八根 K 線不是白白落到我們手裡的——搜尋選中了落在它們身上的那個閾值,部分正是因為它們運氣好。搜尋是一個最大化器;它總能找到空間中噪聲碰巧看起來像訊號的那個角落。你沒法靠一個更好的點估計來耍小聰明繞過這一點。你必須改變"最好"的含義,讓那個幸運的角落不再是最大值。

第 5 幕——修復:一道曝光率下限和一次交易筆數收縮

一塊控制面板上有兩個旋鈕,分別標著曝光率下限和 conf_k 收縮,兩者都把一個樣本外表現曲面抬升到一片高而平坦的高原上,同時一個退化指示燈熄滅,寓意兩個獨立的修復手段合在一起找回了真實最優值

我們已經給兩種病命了名——交易太少建立在觀測太少之上——所以我們開出兩劑藥方,各自對準一種病。

藥方 1:曝光率下限。 最簡單的可能修復方案。直接拒絕任何在場內時間佔比不到 emine_{\min} 的策略——如果你幾乎不交易,你的得分就是 -\infty,搜尋沒法選中你。但給什麼東西設下限這件事本身有一處誠實的微妙之處,也是這整篇文章裡一個不動聲色的教訓。作為一個獨立的目標函數,我們給全時間線夏普比率設了下限,而在這個市場上,這完全沒有改變任何東西:全時間線夏普比率本身選出的贏家曝光率已經在 74% 左右,所以 20% 的下限從來沒有真正生效過。這正是為什麼上面表格裡"曝光率下限"和"全時間線夏普比率"兩行逐位元組完全相同——把一道下限擰到一個本來就安全的指標上,你不過是重新推導出了全時間線夏普比率而已。下限只有在守護一個否則會一路衝向角落的指標時,才會做出看得見的工作:也就是逐筆指標,正如下面的穩健目標函數那樣。換句話說,在這份資料上,"要求曝光"和"在整條時間線上測量",是同一種干預手段的兩個名字。

藥方 2:交易筆數收縮——"conf_k"。 用於你被迫使用逐筆指標、又想要一種柔和修正而不是硬性截斷的場合:根據夏普比率建立在多少筆交易之上,連續地給它打折。乘上 n/(n+k)n/(n+k),其中 nn 是交易筆數,kk 是一個固定的"置信常數"——一個在搜尋之前就選定的、以交易筆數為單位的先驗強度:

score(θ)=SR^(θ)n(θ)n(θ)+k.\text{score}(\theta) = \widehat{SR}(\theta)\cdot \frac{n(\theta)}{n(\theta) + k}.

n0n \to 0 時,無論原始夏普比率有多大,得分都會被壓碎到零;當 nn \to \infty 時,得分收斂到原始夏普比率。這和 MinTRL 以及第 4 幕裡的標準誤,是同一種修正邏輯——把一個小樣本估計值,按其樣本量的遞減函數收縮向零——只不過這次是直接折進目標函數內部,而不是作為事後過濾器施加。最接近的有名字的先例是 van Tharp 的系統質量數(System Quality Number)SQN=Ntrade/σtrade\text{SQN} = \sqrt{N}\cdot \overline{\text{trade}}/\sigma_{\text{trade}}),它同樣讓一個逐筆質量指標隨交易筆數 NN 縮放——儘管函數形式不同(N\sqrt{N} 無界增長,而 n/(n+k)n/(n+k) 會在 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 個的三分之一子集,為了讓這個二維掃描的成本可控)上的平均樣本外夏普比率,旁邊附上退化率:

emine_{\min} \ conf_k k=0k=0 k=40k=40 k=80k=80
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%)

平均樣本外年化夏普比率,括號內是退化率。左上角格子 (0,0)(0,0) 就是原始逐筆夏普比率——沒有下限,沒有收縮:樣本外 0.66,退化 59.5%。這和第 3 幕逐筆那一行的目標函數是同一個,那裡讀數是 0.70 / 57%;這一點小差距純粹來自種子集——這次掃描用了 200 個種子,蒙特卡洛用的是全部 600 個。同一個指標,更小的樣本。

這個曲面用三個讀數講了一個乾淨利落的故事。

每一劑藥單獨都有效。 沿著頂行向移動(加收縮,不加下限):樣本外從 0.661.261.470.66 \to 1.26 \to 1.47 一路攀升,退化率從 59.5%22.5%11.5%59.5\% \to 22.5\% \to 11.5\% 一路下降。沿著左列向移動(加下限,不加收縮):樣本外從 0.661.431.641.710.66 \to 1.43 \to 1.64 \to 1.71 攀升,退化率從 59.5%10%1.5%0%59.5\% \to 10\% \to 1.5\% \to 0\% 下降。任何一個旋鈕,單獨擰動,都能獨立地提升樣本外表現、消滅退化。曝光率下限是這裡更強的那根單獨槓桿,因為它正面攻擊的是這個陷阱的定義性特徵——接近零的曝光率。

合在一起,它們抵達了那片高原——而那片高原,就是全時間線夏普比率本身。emin=0.20e_{\min} = 0.20 時,無論收縮取哪個水平,這一行都平平地停在樣本外 1.710% 退化;推到 emin=0.35e_{\min}=0.35,它才微微爬到 1.73。但仔細看看那個 1.71 到底是什麼:它正是第 3 幕裡樸素全時間線夏普比率在沒有任何下限、沒有任何收縮的情況下報出的那個精確得分。這些補丁方案,最好的情況下並沒有超過全時間線夏普比率——它們只是重新構造出了它。而完全修復後的穩健目標函數甚至都沒能完全達到那個高度:在全部 600 個種子上,它落在樣本外 1.70,還殘留 0.17% 的退化,比全時間線夏普比率的 1.71 / 0% 差了那麼一絲——它被這個更簡單的指標弱支配著。一個折中的中間設定,emin=0.10e_{\min}=0.10k=40k=40,能達到樣本外 1.651% 退化——如果逐筆指標是被強加給你的,這個設定會很好用,但它永遠不是偏愛逐筆指標的理由。

具體數字是依賴尺度的——真正的結果是那個形狀 能完全修復這個市場的具體數值 emin=0.20e_{\min}=0.20k=40k=40,是針對這個特定資料生成過程調出來的;在一個交易頻率和尾部厚度都不同的市場上,那片高原會落在別處。能夠推廣的不是座標,而是那個曲面:兩個方向上都單調抬升,退化率被壓到零,一片停在真相處的高原。你需要靠像上面這樣的掃描,找出屬於你自己的座標。

把兩劑藥方合在一起——穩健目標函數,下限 0.20 加 conf_k 40——回到種子 6。陷阱加冕的是 θ=0.005\theta = 0.005,八筆交易,全時間線樣本外夏普比率 0.13。穩健目標函數選出的卻是 θ=0.979\theta = 0.979:市場曝光率 0.66447 筆交易,樣本外年化夏普比率 1.77。這個 θ=0.979\theta = 0.979 比真實最優值 θ=1.04\theta = 1.04 低一個網格點,所以它找回的是一個接近最優、曝光充分的閾值,而不是正中靶心——它這個單一種子上的樣本外夏普比率(1.77)恰好和總體最優值重合。同樣的資料,同樣的搜尋,同樣的 80 個候選閾值。只有"最好"的定義變了,而這個改變,把贏家從一個空倉的八筆交易海市蜃樓,搬到了真實的、曝光充分的優勢上——耐人尋味的是,這正是樸素全時間線夏普比率在這個種子上選出的同一個閾值。

這次掃描明確揭示了一個警示:在這個市場上,單獨的 conf_k 是不夠的。在 k=40k=40、沒有下限的情況下,各種子上的退化率仍有 22.5%——具體到種子 6,單獨的 conf_k 選出 θ=0.015\theta = 0.015,35 筆交易,樣本外夏普比率 −0.06。三十五筆交易在 35/(35+40)0.4735/(35+40) \approx 0.47 的收縮之下依然存活,剩下的得分足以獲勝。補上這最後一道缺口的是曝光率下限,因為它直接瞄準了這個陷阱的真正標誌——空倉——而不是把交易筆數當成空倉的代理指標去信任它。

第 6 幕——更深的教訓:在整條時間線上測量

對比同一個策略的兩種打分方式:一束窄射燈只照亮策略交易過的少數幾根 K 線,對比一盞泛光燈測量包括空倉時段在內的整條時間線,寓意曝光感知型目標函數無法被稀有的幸運交易薅羊毛

從修復方案裡退後一步,注意一下究竟是什麼把安全的目標函數和陷阱區分開來。不是複雜程度。原始 PnL 和全時間線夏普比率都比逐筆夏普比率更簡單,而它們從未退化——600 個種子上 0%——完全沒有下限、沒有收縮、沒有任何調參。

分界線只在於一個單一的屬性:這個指標測量的是哪個視窗? 逐筆夏普比率只測量策略選擇站立的那些 K 線——一個搜尋可以隨意收縮的自選視窗。全時間線夏普比率和總 PnL 測量的是整條時間線,包括空倉的 K 線。而你沒法靠很少交易就讓一個全時間線指標變大,因為你空倉的每一個小時,都是分母裡一個不賺錢的小時。曝光率下限和 conf_k,說到底,只是給逐筆指標打補丁、補上全時間線指標天生就自帶的曝光感知能力的手段——而掃描已經告訴了我們這種補丁的天花板:最好的情況下,它只能追平全時間線夏普比率(樣本外 1.70 對 1.71),永遠無法超過它。如果你可以自由選擇視窗,就選整條時間線,完全跳過這道補丁。

所以,把這條設計原則平鋪直敘地說出來:

設計目標函數,讓它無法被稀有的幸運交易薅羊毛。 你手上有三件工具,大致按優先順序排列:

  1. 在整條時間線上測量。 這是幾乎永遠不該偏離的預設選項。全時間線夏普比率和總收益,從構造上就具有曝光感知能力——閒置會被自動懲罰,因為空倉的 K 線也算數。如果你發現自己在彙報一個只在"我們活躍的那些 K 線"上算出來的指標,停下來想一想,搜尋一旦獲得了自由選擇這些 K 線的權力,會拿它做什麼。
  2. 要求曝光。 如果你必須使用一個活動條件指標,就給曝光率設一道下限,讓搜尋無法選中一個幾乎不交易的策略。這是對抗這個特定陷阱最強的一根單獨槓桿。
  3. 按交易筆數收縮。n/(n+k)n/(n+k) 給任何比率打折,讓一個建立在少數幾個觀測值上的夏普比率,只能拿到一個建立在成千上萬觀測值上的夏普比率的一小部分信用。這是把最短業績記錄長度在目標函數層面強制執行:來自少量觀測值的數字是不可靠的(第 4 幕),所以一個誠實的目標函數會把這份不可靠性提前定價進去,而不是指望人事後才發現。

這些做法都不會讓搜尋變得更聰明。它們做的是讓目標變得誠實,這樣當搜尋做它一貫會做的事——找到最大值——那個最大值就是你真正想要的策略。

誠實說明

三條直白說出來的警示,因為一項受控研究只有點明自己的侷限,才配得上它的結論。

  • 這個市場是合成的,而且是刻意合成的。 一個標準正態訊號,一個被限制在 s1|s|\le 1 內的線性優勢,肥尾的 Student-tt44)噪聲——之所以這樣選,是為了受控的真實基準,不是為了貼近真實市場。我們只能通過在一份已知哪個策略才是對的資料上執行一個目標函數,來證明它選錯了策略。真實市場是非平穩的、自相關的、會切換狀態的。肥尾是我們保留下來的一個貼近現實的成分,但——與一個自然而然的第一直覺相反,也與這篇文章本身更早一版草稿的判斷相反——它不是驅動這個陷阱的原因:一個高斯噪聲對照實驗(300 個種子)退化率是 55.7%,而這裡是 57.0%,樣本外夏普比率分別是 0.710.70。這個陷阱是一種小樣本加選擇的偽影,無論有沒有厚尾都會存在。這裡交付的是診斷和修復模式,不是一個策略,也不是一個普適常數。
  • 修復用的數值是依賴尺度的。 能完全堵上這個陷阱的具體下限 emin=0.20e_{\min}=0.20 和收縮 k=40k=40,是針對這個特定資料生成過程擬合出來的——它的交易頻率、它的尾部厚度、它的優勢大小。換一份資料,那片高原會移動。能夠遷移的是那個掃描曲面的形狀(兩個旋鈕上都單調抬升、退化歸零、一片停在真相處的高原),以及找到屬於你自己座標的方法:把兩者都掃一遍,通讀那個曲面,不要照抄這些數字。
  • conf_k 是我們自己的構造,不是一個有名字的估計量。 交易筆數收縮 SR^n/(n+k)\widehat{SR}\cdot n/(n+k),是我們為這個問題構造出來的一種貝葉斯精度加權 / 經驗貝葉斯風格的裝置;它的理論依據紮根於已驗證的 Lo/Mertens 標準誤結果和 Bailey–López de Prado 的 MinTRL,最接近的有名字的親戚是 van Tharp 的系統質量數(Ntrade/σtrade\sqrt{N}\cdot \overline{\text{trade}}/\sigma_{\text{trade}},函數形式不同),但我們不主張 n/(n+k)n/(n+k) 本身在文獻中以某個名字出現過。與它配套的藥方——曝光率下限、全時間線測量——是被精確表述出來的標準做法。另外注意一下,這裡哪些目標函數本來就是安全的:原始 PnL 和全時間線夏普比率從來不需要修復,因為它們一開始就具有曝光感知能力——安全到給全時間線夏普比率設下限,會退化成全時間線夏普比率本身,它選出的贏家早就輕鬆越過了任何合理的下限。這個陷阱專門針對的是逐筆 / 活躍夏普比率——而即便是完全修復後的逐筆目標函數,也只能追平全時間線夏普比率(樣本外 1.70 對 1.71),永遠無法超過它。主要的教訓不在於修復;而在於一開始就該在整條時間線上測量。

要點總結

  1. 目標函數就是那個決定,不是走個形式。 搜尋找不到"一個好策略"——它找到的是讓你交給它的那個標量最大化的東西,而不同的標量,在完全相同的資料上會選出截然不同的策略。選擇目標函數,就是選擇策略;下游的一切都只是記賬。這就是古德哈特定律:你的指標一旦變成了搜尋的目標,搜尋就會利用它和你真正想表達之間的每一處縫隙。
  2. 逐筆夏普比率是一個陷阱。 只在策略交易過的 K 線上測量,它靠儘可能少地交易來被最大化——觀測值越少,幾次幸運行情把比率吹高就越容易(一個高斯對照實驗證實肥尾不是必要條件;這是一種小樣本加選擇效應)。在 600 個種子中,它有 56% 的時候會選出曝光率低於 5% 的彩票,57% 的時候會退化;典型的退化選擇,樣本內逐筆夏普比率平均為 4.58。在一個刻意挑出的觸目驚心的種子上,它給一個八筆交易、0.4% 曝光率的策略加冕,樣本內逐筆夏普比率 21.09,崩塌到全時間線樣本外夏普比率 0.13。一個建立在八個觀測值上的比率,其年化標準誤量級是 ±7.7(第 4 幕)——它從來就不是資訊。
  3. 曝光感知型目標函數天生安全。 原始 PnL 和全時間線夏普比率從未退化(0%),因為它們測量的是整條時間線,閒置會被自動懲罰。你沒法靠很少交易薅一個全時間線指標的羊毛。原始 PnL 唯一的毛病是相反方向的偏差——它曝光過度(平均曝光率 0.859,樣本外 1.61,對比真實值 1.77),把 θ\theta 推過最優值,只為了多待在場內。
  4. 修復方案有效——但它們只能把你帶回全時間線夏普比率的水平。 曝光率下限和交易筆數(conf_k)收縮,各自都能獨立地提升樣本外夏普比率、把退化率壓向零;合在一起,它們抵達一片高原。但那片高原就是全時間線夏普比率:退化率從 (emin,k)=(0,0)(e_{\min},k)=(0,0) 處的 59.5%,降到 emin=0.20e_{\min}=0.20 時的 0%,樣本外夏普比率從 0.66 攀升到 1.71——正是樸素全時間線夏普比率不借助任何幫助就能報出的那個精確數字,而完全修復後的穩健目標函數實際上還差了那麼一絲沒到(樣本外 1.70,退化 0.17%:弱支配關係)。在種子 6 上,穩健目標函數找回了一個接近最優、曝光充分的閾值(θ=0.979\theta = 0.979,比真實值 1.041.04 低一個網格點;447 筆交易;樣本外夏普比率 1.77)。把 conf_k 當成逐筆指標被強加給你時的一個後備方案,而不是測量整條時間線的一種升級。具體座標是依賴尺度的;曲面的形狀才是可以遷移的結果。
  5. 設計目標函數,讓它無法被稀有的幸運交易薅羊毛。 按優先順序:在整條時間線上測量(預設選項)、要求曝光(最強的單獨槓桿)、按交易筆數收縮(目標函數層面的最短業績記錄長度)。這些做法都不會讓搜尋變得更聰明——它們讓目標變得誠實,這樣搜尋必然會找到的那個最大值,才會是一個你真的會去交易的策略。

參數搜尋是一個順從的精靈。它精確地實現你許下的那個願望,而不是你心裡真正想要的那個——而"最大化這個指標"就是那個願望。把願望說成逐筆夏普比率,它就會變出八筆幸運的交易,管它們叫一筆財富。把願望說成讓空倉受到懲罰、少數幾筆交易只能賺到少數幾份信用,同一個精靈,在同一份資料上,就會把真實的優勢交到你手上。你部署的那個策略,早在你敲下目標函數的那一刻就已經被選定了。要有意識地去選它。

完整的實驗——合成市場、六種目標函數、600 個種子的蒙特卡洛、以及修復掃描曲面,每一個數字都能從同一個確定性指令碼重新生成——都收錄在配套論文中,見 objective-design.marketmaker.cc,程式碼與資料見 github.com/suenot/objective-design-degeneracy

這是我們其他研究從不同角度打的同一場戰爭的一條戰線。Deflated Sharpe Ratio 給一次多重檢驗之後的搜尋贏家定價——本文問的是目標函數究竟有沒有選對策略,DSR 問的則是它選中的策略,能否跑贏單靠運氣所能產出的東西。即將釋出的回測過擬合機率研究從重取樣一側攻擊的是同一種選擇偏差,給流程而不是給贏家打分。而前視偏差分類法則記錄了另一臺偽造夏普比率的大機器——來自未來的洩漏——它通過一種截然不同的機制,產生了一模一樣的症狀(一份輝煌的回測,一到實盤就死掉)。目標函數設計、去膨脹、過擬合機率、前視偏差:一門學科的四個名字——都是為了不被自己的回測騙過。

免責宣告:本文提供的資訊僅用於教育和參考目的,不構成財務、投資或交易建議。加密貨幣交易涉及重大損失風險。

Authors

Eugen Soloviov
Eugen Soloviov

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.

Newsletter

緊跟市場步伐

訂閱我們的時事通訊,獲取獨家 AI 交易見解、市場分析和平台更新。

我們尊重您的隱私。您可以隨時退訂。