1#!/usr/bin/env python 2# -*- coding: utf-8; py-indent-offset:4 -*- 3############################################################################### 4# 5# Copyright (C) 2015, 2016, 2017 Daniel Rodriguez 6# 7# This program is free software: you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation, either version 3 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program. If not, see <http://www.gnu.org/licenses/>. 19# 20############################################################################### 21from __future__ import (absolute_import, division, print_function, 22 unicode_literals) 23 24import collections 25 26from backtrader.metabase import MetaParams 27from backtrader.utils.py3 import with_metaclass 28 29 30class MetaSingleton(MetaParams): 31 '''Metaclass to make a metaclassed class a singleton''' 32 def __init__(cls, name, bases, dct): 33 super(MetaSingleton, cls).__init__(name, bases, dct) 34 cls._singleton = None 35 36 def __call__(cls, *args, **kwargs): 37 if cls._singleton is None: 38 cls._singleton = ( 39 super(MetaSingleton, cls).__call__(*args, **kwargs)) 40 41 return cls._singleton 42 43 44class Store(with_metaclass(MetaSingleton, object)): 45 '''Base class for all Stores''' 46 47 _started = False 48 49 params = () 50 51 def getdata(self, *args, **kwargs): 52 '''Returns ``DataCls`` with args, kwargs''' 53 data = self.DataCls(*args, **kwargs) 54 data._store = self 55 return data 56 57 @classmethod 58 def getbroker(cls, *args, **kwargs): 59 '''Returns broker with *args, **kwargs from registered ``BrokerCls``''' 60 broker = cls.BrokerCls(*args, **kwargs) 61 broker._store = cls 62 return broker 63 64 BrokerCls = None # broker class will autoregister 65 DataCls = None # data class will auto register 66 67 def start(self, data=None, broker=None): 68 if not self._started: 69 self._started = True 70 self.notifs = collections.deque() 71 self.datas = list() 72 self.broker = None 73 74 if data is not None: 75 self._cerebro = self._env = data._env 76 self.datas.append(data) 77 78 if self.broker is not None: 79 if hasattr(self.broker, 'data_started'): 80 self.broker.data_started(data) 81 82 elif broker is not None: 83 self.broker = broker 84 85 def stop(self): 86 pass 87 88 def put_notification(self, msg, *args, **kwargs): 89 self.notifs.append((msg, args, kwargs)) 90 91 def get_notifications(self): 92 '''Return the pending "store" notifications''' 93 self.notifs.append(None) # put a mark / threads could still append 94 return [x for x in iter(self.notifs.popleft, None)] 95