利用所有可用的核心是我為反向交易者考慮的事情,但從未完成。支持自然運算,刪除數組符號,包含新指標和 bla, bla, bla。
實際上,我不是優化的忠實擁護者,因此也不是為優化使用所有內核的忠實擁護者。恕我直言,一個好主意值得一百萬次優化。
筆記
最初的多核支持已經存在,並且適用於眾所周知的測試用例集。鑑於pickle表現出的行為,預計其他一些調整可以確保在進行多核優化時所有指標和函數可以在進程之間來回傳遞。
筆記
在 1.0.10.88 版本中已經推送了一些針對多核的額外更正,以使更多“不可挑選”的東西變得可挑選。到目前為止,指標測試沒有顯示任何問題。
但是 BigMikeTrading 論壇中的某個人詢問了該平台與其他平台相比必須提供的功能,我提到了一些功能,例如PyAlgoTrade已經具備(甚至是多機)
完成它所需的小而正確的推動就在那裡。根據過去的經驗,並且因為互聯網上充滿了我已經知道的參考資料:即使是最簡單的多線程(無論 GIL 律師怎麼說)在 Python 中都是不行的,無論版本如何。多線程在 Python 中是假的,因為您有多個線程但沒有並行執行代碼。使用 IO 綁定線程創建抽象和單獨的代碼路徑執行可能很好,但它確實是一個殺手。
然後只剩下一個選擇:模塊multiprocessing或類似的。
展望光明的未來,我決定選擇現代版本: concurrent.futures (後來證明是一個錯誤的選擇)即使這意味著為 Python 2.6/2.7 支持添加外部依賴項。
歷史:
Python 的一些動態特性不能很好地在進程之間來回發送數據
當酸洗(序列化)諸如未在模塊級別定義的類,lambda,對實例方法的引用和沒有唯一名稱的動態類(即使這些類本身是唯一的)時,所涉及的模塊(
pickle)會阻塞
我把這些東西分散在代碼中。然後我從 pathos multiprocess https://pypi.python.org/pypi/multiprocess找到了dill和兄弟姐妹。顯然他們會解決序列化問題,但會增加更多的外部依賴……不,不。
回到繪圖板上,看看是否可以將不可拾取的項目設為可拾取,即使pickle模塊產生了錯誤,這會讓一些老 GCC 開發人員非常高興。
它完成了……還是沒有?
將不可拾取的物品重做為可拾取的東西
使用 Python 2.7.9 運行測試並輕而易舉地運行……使用我機器的 8 個內核流暢而令人耳目一新
使用 Python 3.4.3 運行測試,8 核開始運行,但經過一些優化後,每個後續策略的執行時間會越來越長……直到無法忍受。
顯然,將結果(一個完整的執行策略)提取到主進程中遇到了與內存分配相關的一些限制(而且我的機器有足夠的可用 RAM……對於幾個小時的並行優化來說綽綽有餘)
一些額外的閱讀讓我考慮簡化我的場景:
使用
concurrent.futures似乎是未來的證明但是標準的
multiprocessing模塊已經具備了backtrader需求
聞起來一直在使用矯枉過正,一些行很快被重新設計,並且:
測試在 Python 2.7 上運行良好(甚至比以前更快)
測試運行速度與 Python 3.4 一樣快
是時候進行清理,運行完整的測試並執行推送和發布 1.0.9.88。沒有新指標……只是簡單的舊多核優化
閱讀完所有內容……是時候編寫一個關於如何控制優化以使用多個內核的令人耳目一新的腳本了
- 好消息……無需做任何事情……無需用戶干預即可完成
當用戶希望優化strategy時, Strategy子類被添加到Cerebro實例中,如下所示:
cerebro.optstrategy(StrategyClass, *args, **kwargs)
與將策略傳遞給Cerebro的常規方式相反:
cerebro.addstrategy(StrategyClass, *args, **kwargs)
這一直如此,並沒有改變。背景是:
-
Cerebro需要了解是否要優化策略以正確處理策略的參數,這些參數可能已經是常規策略的可iterables對象
現在……通過optstrategy傳遞給cerebro的optstrategy獲得了使用機器所有可用內核的額外好處。
當然,如果最終用戶希望對使用過的內核進行細粒度控制……這是可能的。創建Cerebro的標準方法:
大腦= bt。 Cerebro () # runonce 是True , preload 是True並且 “new” maxcpus 是 None
maxcpus (此版本的新參數)是控制鍵:
maxcpus = None -> 使用所有可用的 CPU
maxcpus = 1 -> 不要運行多核
maxcpues = 2 … -> 使用指定的核心數
這是一種選擇退出策略,因為多核已經加入。
在具有 16 GB RAM、運行 Windows 8.1 和 Python 64bit 2.7.9 的 4 核(每個核 2x 線程 - 總共 8 個邏輯處理器)機器上進行比較
使用 1 個核心執行:326 秒
8核執行:127秒
不同的測試運行表明,該比率平均約為 2.75:1。
不幸的是,進程的創建/銷毀和對象的來回酸洗具有潛在的好處,但加速仍然很重要。
該圖顯示了正在使用的 8 個內核。
代碼如下。只需將maxcpus參數更改為1即可將測試限制為 1 個核心。
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import time
from six.moves import xrange
import backtrader as bt
import backtrader.indicators as btind
import backtrader.feeds as btfeeds
class OptimizeStrategy(bt.Strategy):
params = (('smaperiod', 15),
('macdperiod1', 12),
('macdperiod2', 26),
('macdperiod3', 9),
)
def __init__(self):
# Add indicators to add load
btind.SMA(period=self.p.smaperiod)
btind.MACD(period_me1=self.p.macdperiod1,
period_me2=self.p.macdperiod2,
period_signal=self.p.macdperiod3)
if __name__ == '__main__':
# Create a cerebro entity
cerebro = bt.Cerebro(maxcpus=None)
# Add a strategy
cerebro.optstrategy(
OptimizeStrategy,
smaperiod=xrange(5, 40),
macdperiod1=xrange(12, 20),
macdperiod2=xrange(26, 30),
macdperiod3=xrange(9, 15),
)
# Create a Data Feed
datapath = ('../datas/2006-day-001.txt')
data = bt.feeds.BacktraderCSVData(dataname=datapath)
# Add the Data Feed to Cerebro
cerebro.adddata(data)
# clock the start of the process
tstart = time.clock()
# Run over everything
stratruns = cerebro.run()
# clock the end of the process
tend = time.clock()
print('==================================================')
for stratrun in stratruns:
print('**************************************************')
for strat in stratrun:
print('--------------------------------------------------')
print(strat.p._getkwargs())
print('==================================================')
# print out the result
print('Time used:', str(tend - tstart))