Error on loading file : AttributeError: 'Config' object has no attribute '_readonly_'
Closed this issue · 3 comments
Can not pickle vectorbt object
import vectorbt as vbt
data = vbt.BinanceData.download(
['BTCUSDT', 'ETHUSDT'],
start='2020-01-01',
end='2022-01-01',
interval='1h'
)
import dill
with open('indication_data.pkl', 'wb') as f:
dill.dump(data, f)
with open('indication_data.pkl', 'rb') as f:
data = dill.load(f)
Error:
File c:\Users\ufo\anaconda3\lib\site-packages\dill\_dill.py:313, in load(file, ignore, **kwds)
307 def load(file, ignore=None, **kwds):
308 """
309 Unpickle an object from a file.
310
311 See :func:`loads` for keyword arguments.
312 """
--> 313 return Unpickler(file, ignore=ignore, **kwds).load()
File c:\Users\ufo\anaconda3\lib\site-packages\dill\_dill.py:525, in Unpickler.load(self)
524 def load(self): #NOTE: if settings change, need to update attributes
--> 525 obj = StockUnpickler.load(self)
526 if type(obj).__module__ == getattr(_main_module, '__name__', '__main__'):
527 if not self._ignore:
528 # point obj class to main
File c:\Users\ufo\anaconda3\lib\site-packages\vectorbt\utils\config.py:507, in Config.__setitem__(self, k, v, force)
506 def __setitem__(self, k: str, v: tp.Any, force: bool = False) -> None:
--> 507 if not force and self.readonly_:
508 raise TypeError("Config is read-only")
509 if not force and self.frozen_keys_:
File c:\Users\ufo\anaconda3\lib\site-packages\vectorbt\utils\config.py:485, in Config.readonly_(self)
482 @property
483 def readonly_(self) -> bool:
484 """Whether to deny any updates to the config."""
--> 485 return self._readonly_
AttributeError: 'Config' object has no attribute '_readonly_'
I can work with you to figure out why it's failing to pickle... however, this is an issue for the vectorbt
developers. The traceback looks like it's being kicked to pickle
. First step to figure out what the issue is would be to turn on tracing (i.e. dill.detect.trace(True)
) and then run it again.
I realize this issue is fairly old now but the reason is because Vectorbt has a superclass called Pickleable, which many of its objects are a subclass of. In this case you should use the Pickleable class' methods directly on the object rather than providing the object to pickle.dump(). Aside from the syntax, the behavior should be the same:
data = vbt.YFData.download(
['SPY', 'QQQ'],
start='2020-01-01',
end='2022-01-01',
interval='1d'
)
data.save('pickled_data') # creates a file called pickled_data in the current directory
print(type(data)) # Output: <class 'vectorbt.data.custom.YFData'>
Then when you need the saved data again, provide the full object type or the shortened version:
unpickled_data = vbt.data.custom.YFData.load('pickled_data')
# or
unpickled_data = vbt.YFData.load('pickled_data')
If you only need a reference to the pickled object then you should do:
pickled_data = data.dumps()
...
unpickled_data = vbt.YFData.loads(pickled_data)
However, I should mention that this data object is basically just a dictionary. You likely want to use the get
method:
data = vbt.YFData.download(
['SPY', 'QQQ'],
start='2020-01-01',
end='2022-01-01',
interval='1d'
).get('Open')
This will return a pandas DataFrame which can be pickled directly through the pickle/dill module or with data.to_pickle('pickled_data')
.
I mostly explained this since it's necessary to use the Pickleable methods for saving the vbt.Portfolio and other valuable objects from backtesting.
I'm going to close this, as it would seem the answer is vectorbt
expects to use it's own pickler.