text: import datetime
  import numpy as np
  import pandas as pd
  from queue import Queue
  
  from abc import ABCMeta, abstractmethod
  
  from event import SignalEvent
  
  class Strategy(object):
      """
      Strategy is an abstract base class providing an interface for
      all subsequent (inherited) strategy handling objects.
  
      The goal of a (derived) Strategy object is to generate Signal
      objects for particular symbols based on the inputs of Bars 
      (OLHCVI) generated by a DataHandler object.
  
      This is designed to work both with historic and live data as
      the Strategy object is agnostic to the data source,
      since it obtains the bar tuples from a queue object.
      """
  
      __metaclass__ = ABCMeta
  
      @abstractmethod
      def calculate_signals(self):
          """
          Provides the mechanisms to calculate the list of signals.
          """
          raise NotImplementedError("Should implement calculate_signals()")
  
  class DoubleMAStrategy(Strategy):
      """
      这是一个双均线策略，当MA_5上穿MA_13时，买入；MA_下穿MA_13时，卖出。
      """
      def __init__(self, ma1_timeperiod, ma2_timeperiod, bars, events):
          self.ma1_timeperiod = ma1_timeperiod
          self.ma2_timeperiod = ma2_timeperiod
          self.bars = bars
          self.symbol_list = self.bars.symbol_list
          self.events = events
          self.latest_signal_datetime = None
          self.ma_data  = {}
  
          # Once buy & hold signal is given, these are set to True
          self.bought = self._calculate_initial_bought()
  
          # Initialise ma_data as DataFrame[close,ma1,ma2]
          for s in self.symbol_list:
              self.ma_data[s] = pd.DataFrame(columns=['close', 'MA_'+str(self.ma1_timeperiod), 'MA_'+str(self.ma2_timeperiod)])
      
      def _calculate_initial_bought(self):
          """
          Adds keys to the bought dictionary for all instruments
          and sets them to False.
          """
          bought = {}
          for s in self.symbol_list:
              bought[s] = False
          return bought
  
      def calculate_MA(self, symbol):
          # Update bars close data
          bars = self.bars.get_latest_bars(symbol, N=1)
          datetime = bars[0][1]
          self.ma_data[symbol].loc[datetime, 'close'] = bars[0][5]
  
          # Calculate MA data
          x = self.ma_data[symbol].copy()
          x.iloc[:,1] = x['close'].rolling(self.ma1_timeperiod).mean()
          x.iloc[:,2] = x['close'].rolling(self.ma2_timeperiod).mean()
          #x.fillna(0, inplace=True)
          self.ma_data[symbol] = x
  
      def calculate_signals(self, event):
          if event.type == 'MARKET':
              for s in self.symbol_list:
                  self.calculate_MA(s)
                  bars = self.bars.get_latest_bars(s, N=1)
                  ma1_list = self.ma_data[s]['MA_'+str(self.ma1_timeperiod)]
                  ma2_list = self.ma_data[s]['MA_'+str(self.ma2_timeperiod)]
                  try:
                      if not self.latest_signal_datetime == bars[0][1]:
                          # 一分钟内仅允许产生一次信号
                          if self.bought[s] == False:
                              if (bars[0][5] >= ma2_list[-1]+2) or abs(ma1_list[-1]-ma2_list[-1])<=1 and ma1_list[-2] < ma2_list[-2]:
                                  # MA1均线上穿MA2均线，买入信号
                                  # (Symbol, Datetime, Type = LONG, SHORT or EXIT)
                                  signal = SignalEvent(bars[0][0], bars[0][1], 'LONG')
                                  self.events.put(signal)
                                  self.bought[s] = True
                                  self.latest_signal_datetime = bars[0][1]
                          else:
                              if (bars[0][5] <= ma2_list[-1]-2) or abs(ma1_list[-1]-ma2_list[-1])<=1 and ma1_list[-2] > ma2_list[-2]:
                                  # MA1均线下穿MA2均线，卖出信号
                                  # (Symbol, Datetime, Type = LONG, SHORT or EXIT)
                                  signal = SignalEvent(bars[0][0], bars[0][1], 'SHORT')
                                  self.events.put(signal)
                                  self.bought[s] = False
                                  self.latest_signal_datetime = bars[0][1]
                  except:
                      # 存在MA值为NaN的情况，忽略
                      pass
  
  class BuyAndHoldStrategy(Strategy):
      """
      This is an extremely simple strategy that goes LONG all of the 
      symbols as soon as a bar is received. It will never exit a position.
  
      It is primarily used as a testing mechanism for the Strategy class
      as well as a benchmark upon which to compare other strategies.
      """
  
      def __init__(self, bars, port, events):
          """
          Initialises the buy and hold strategy.
  
          Parameters:
          bars - The DataHandler object that provides bar information
          events - The Event Queue object.
          """
          self.bars = bars
          self.port = port
          self.symbol_list = self.bars.symbol_list
          self.events = events
  
          # Once buy & hold signal is given, these are set to True
          self.bought = self._calculate_initial_bought()
      
      def _calculate_initial_bought(self):
          """
          Adds keys to the bought dictionary for all instruments
          and sets them to False.
          """
          bought = {}
          for s in self.symbol_list:
              bought[s] = False
          return bought
  
      def calculate_signals(self, event):
          """
          For "Buy and Hold" we generate a single signal per symbol
          and then no additional signals. This means we are 
          constantly long the market from the date of strategy
          initialisation.
  
          Parameters
          event - A MarketEvent object. 
          """
          if event.type == 'MARKET':
              for s in self.symbol_list:
                  bars = self.bars.get_latest_bars(s, N=1)
                  #print("\nBar: Symbol=%s, Datetime=%s, Open=%s, High=%s, Low=%s, Close=%s, Volume=%s" % \
                  #        (s, bars[0][1], bars[0][2], bars[0][3], bars[0][4], bars[0][5], bars[0][6]))
                  if bars is not None and bars != []:
                      if self.bought[s] == True and self.port.current_holdings['total'] > self.port.initial_capital:
                          signal = SignalEvent(bars[0][0], bars[0][1], 'SHORT')
                          self.events.put(signal)
                          self.bought[s] = False
                      elif self.bought[s] == False:
                          # (Symbol, Datetime, Type = LONG, SHORT or EXIT)
                          signal = SignalEvent(bars[0][0], bars[0][1], 'LONG')
                          self.events.put(signal)
                          self.bought[s] = True
          