Optimizing Strategy with IBStore data returns pickle error
-
I'm not sure if I'm doing this correctly: Trying the Quickstart code with Optimization and using Interactive Brokers IBStore data, I get
File "C:\Program Files\Anaconda2\lib\multiprocessing\pool.py", line 668, in next raise value TypeError: can't pickle thread.lock objects
Full code is below. Am I doing something wrong? Maybe it's my environment (windows with Anaconda). I haven't been able to find any similar issues in the discussion threads.
from __future__ import (absolute_import, division, print_function, unicode_literals) # -*- coding: utf-8 -*- import backtrader as bt import datetime # for datetime objects import os.path # to manage paths from backtrader.stores import ibstore # import sys # to detrmine the script name in argv[0] # ----- STRATEGY DEFINITION ----- class TestStrategy(bt.Strategy): def log(self,txt,dt=None): ''' Logging function fot this strategy''' dt = dt or self.datas[0].datetime.date(0) print('%s, %s' % (dt.isoformat(),txt)) def __init__(self): # Keep a reference to the "close" line in the data[0] dataseries self.dataclose = self.datas[0].close # To keep track of pending orders and buy price/commission self.order = None self.buyprice = None self.buycomm = None def notify(self, order): if order.status in [order.Submitted, order.Accepted]: # Buy/Sell order submitted/accepted to/by broker - Nothing to do return # Check if an order has been completed # Attention: broker could reject order if not enougth cash if order.status in [order.Completed, order.Canceled, order.Margin]: if order.isbuy(): self.log('BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.buyprice = order.executed.price self.buycomm = order.executed.comm else: #Sell self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.bar_executed = len(self) # Write down: no pending order self.order = None def notify_trade(self, trade): if not trade.isclosed: return self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm)) def next(self): # Simply log the closing price of the series from the reference self.log('Close, %.2f' % self.dataclose[0]) # Check if an order is pending ... if yes, we cannot send a 2nd one if self.order: return # Check if we are in the market if not self.position: # Not yet ... we MIGHT BUY if ... # ------- STRATEGY LOGIC ----- if self.dataclose[0] < self.dataclose[-1]: #i.e. current close less than previous close if self.dataclose[-1] < self.dataclose[-2]: # buy with default params self.log('BUY CREATE, %.2f' % self.dataclose[0]) # Keep track of the created order to avoid a 2nd order self.order = self.buy() else: # Already in the market... we might sell if holding > 5 bars if len(self) > (self.bar_executed + 5): # SELL, SELL, SELL!!! (with all possible default parameters) self.log('SELL CREATE, %.2f' % self.dataclose[0]) # Keep track of the created order to avoid a 2nd order self.order = self.sell() # ----- END STRATEGY DEFINITION ----- if __name__ == '__main__': # create a cerebro entity cerebro = bt.Cerebro() # Add a strategy cerebro.optstrategy( TestStrategy, maperiod=range(10, 31)) # Create a Data Feed ibstore = bt.stores.IBStore(host='127.0.0.1',port=7496,clientId=5) mysymbol = 'EUR.USD-CASH-IDEALPRO' fromdate = datetime.datetime(2016,3,1,00,00) todate = fromdate = datetime.datetime(2017,3,30,00,00) data = ibstore.getdata(dataname=mysymbol, timeframe=bt.TimeFrame.Days, compression=1, fromdate=fromdate, todate=todate, historical=True) # cerebro.resampledata(data, timeframe=bt.TimeFrame.minutes, compression=60) # Add the Data Feed to Cerebro cerebro.adddata(data) cerebro.broker.setcash(100000) cerebro.broker.setcommission(commission=0.001) # 0.1% ... divide by 100 to remove the % print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue()) cerebro.run() print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
-
Solved: adding the 'maxcpus=1' parameter to the cerebro call worked
ie.:
cerebro = bt.Cerebro(maxcpus=1)
Not sure why this is... perhaps because of the way data requests to IB are split into tasks.
-
Threads are part of the pack when using
IbPy
and they are not pickable across processes. A lot of tweaking might be needed to remove all those objects after a historical download. -
I tried to resolve this issue by walking recursively down the object tree rooted at cerebro and setting all lock types to None. Didn't work, still found a lock somewhere.