將 1 sim 常式擴展到模擬數量的有效方法?Brownian Bridge 在 MC 模擬中與多個標的資產一起使用,
我相信對於那些熟悉量化金融和復雜期權定價的 MC/QMC 方法的人來說,這是一個(相當)簡單的問題。或者可能只是一個簡單的 Python 循環向量化問題,不需要量化金融知識。
所以我試圖理解布朗橋技術。我使用了最好的程式碼範例,其中一個很棒的範例在這裡(感謝作者 Kenta Oono):https ://gist.github.com/delta2323/6bb572d9473f3b523e6e - 在評論中是對常式的更正,它似乎正確實現從我讀到的關於路徑建設的內容。這是重寫的,所以它對我正在做的事情更有意義。這是為了在平均 21 天的時間內模擬一籃子 12 種標的資產。請注意,它僅針對 1 個模擬路徑編寫:
import numpy as np from matplotlib import pyplot import timeit steps = 21 underlyings = 12 #seed = 0 # fix your seed for calcing Greeks #np.random.seed(seed) def sample_path_batch(underlyings, steps): dt = 1.0 / (steps-1) dt_sqrt = np.sqrt(dt) B = np.empty((underlyings, steps), dtype=float) B[:, 0] = 0 for n in range(steps - 2): t = n * dt xi = np.random.randn(underlyings) * dt_sqrt B[:, n + 1] = B[:, n] * (1 - dt / (1 - t)) + xi B[:, -1] = 0 # set last step to 0 return B start_time = timeit.default_timer() B = sample_path_batch(underlyings, steps) print('\n' + 'Run time for 1 simulation steps * underlyings: ', int(timeit.default_timer() - start_time), ' seconds') pyplot.plot(B.T) pyplot.show()
那麼,什麼是一種有效的方法(而不是循環這個單一的常式)來建構這條通往任意數量模擬的路徑?我正在使用 Sobol,因此以 1024 模擬為例。儘管我希望使用更多,因此希望刪除此循環,因為我已將所有內容放入另一個 j 循環並使其饋入 3D NumPy 數組,但這會迅速減慢(隨著模擬的數量):
#Now my inefficient way to generate multiple simulations of the Brownian Bridges: sims = pow(2,17) # 131,072 simulations def sample_path_batches(underlyings, steps, sims): dt = 1.0 / (steps-1) dt_sqrt = np.sqrt(dt) B = np.empty((underlyings, steps, sims), dtype=float) B[:,:, 0] = 0 for n in range(steps - 2): for j in range(sims): t = n * dt xi = np.random.randn(underlyings) * dt_sqrt B[:, n + 1, j] = B[:, n, j] * (1 - dt / (1 - t)) + xi B[:, -1, j] = 0 # set last step to 0 return B start_time = timeit.default_timer() B = sample_path_batches(underlyings, steps, sims) print('\n' + 'Run time for ', sims, ' simulation steps * underlyings: ', int(timeit.default_timer() - start_time), ' seconds')
pow(2,17) = 131,072 次模擬需要 13 秒,因此我想加快速度。有任何建議總比沒有好。我認為這更像是一個普通的 Python 問題,而不是真正的量化金融問題。當然,我可以在 Cython 中執行常式並使循環並行,但我只是在尋找一種在 Python 和標準庫中執行此操作的有效方法。
基於will評論的潛在解決方案,最初(在我將 np.random.randn 更改為(基礎,sims)之前)所有“隨機”數字在模擬中都是相同的。無論如何,這“看起來”工作得非常快,儘管隨機數的矩陣(在固定種子之後)不一樣(j 循環與這個矢量化修改)。不幸的是,我不擅長用 matplotlib 繪製 3D 數組。當我單獨繪製它們時,它們似乎仍然是布朗橋
pyplot.plot(B[:,:,sim#].T); pyplot.show()
並在每次模擬中獲取統計資訊,儘管它們與 j 循環方法不同(可能是因為在循環中,我有迭代隨機數,而這裡,它們是在 2D 中生成的)。它們仍然“看起來”是有效的布朗橋,但我沒有複製 j 循環產生的相同序列(即改變了衝擊產生的序列)。我顯然無法逐一查看 21 條路徑中 12 個底層證券的 131,072 次模擬……儘管如此,這是修改,我想一旦我嘗試實施它,我就會知道它有多“好”(不確定我是否應該為此使用 Mersenne Twister(如圖所示)或 RQMC 點,但在這裡,可能對其他人有用) - 現在,如果我只能將布朗循環矢量化,那麼,也許在另一天:import numpy as np from matplotlib import pyplot steps = 21 underlyings = 12 sims = 131072 def sample_path_batches(underlyings, steps, sims): dt = 1.0 / (steps-1) dt_sqrt = np.sqrt(dt) B = np.empty((underlyings, steps, sims), dtype=float) B[:, 0, :] = 0 # set first step to 0 for n in range(steps - 2): # ============================================================================= # Slow j loop before vectorization: # # for j in range(sims): # t = n * dt # xi = np.random.randn(underlyings) * dt_sqrt # B[:, n + 1, j] = B[:, n, j] * (1 - dt / (1 - t)) + xi # B[:, -1, j] = 0 # set last step to 0 # ============================================================================= # Change the generation of random numbers to be done in 1 step of the Brownian Bridge, # for a huge speedup in computational time; note the random numbers differ from the j loop t = n * dt xi = np.random.randn(underlyings, sims) * dt_sqrt B[:, n + 1, :] = B[:, n, :] * (1 - dt / (1 - t)) + xi B[:, -1, :] = 0 # set last step to 0 return B
1 個模擬步驟的執行時間 * 底層證券:0.022 秒
131072 個模擬步驟的執行時間 * 底層證券:1.131 秒