text: import numpy as np
  import inspect
  import sys
  import pydoc
  
  try:
      from . import Data_API
  except:
      import Data_API
      
  def smart_index(obj,iter_index,default=None):
      if(iter_index<0 or iter_index >= len(obj)):
          return default
      else:
          return obj[iter_index]
      
  def time_index(time_index_array,dt=None):
      if dt == None:
          dt = Data_API.Pricing_Database.pricing_date
      index = np.searchsorted(time_index_array,dt)
      if(index==len(time_index_array)):
          return len(time_index_array)-1
      elif time_index_array[index] == dt:
          return index
      else:
          return index - 1
  
  def is_biz_day(ticker,dt=None):
      if dt==None:
          dt = Data_API.Pricing_Database.pricing_date
      cache = Data_API.Cache()
      dates = cache.get_ticker_dates(ticker)
      index = time_index(dates,dt)
      if(dates[index] == dt):
          return True
      else:
          return False
  
  def price(ticker,date_offset=-1,price_feature='Close'):
      """
      ticker: the underlyer ticker to price of
      date_offset: int, default to -1, -k means the k-th last day as of observation
      price_feature: default to 'Close', can use 'Open', 'Close', 'High', 'Low'
      """
      cache = Data_API.Cache()
      data = cache.get_ticker_data(ticker)
      pricing_date_index = time_index(data.index.values)
      if(date_offset<0):
          date_offset = date_offset+1+pricing_date_index
      elif date_offset>pricing_date_index:
          date_offset = pricing_date_index
      return data[price_feature][date_offset]
  
  def prices(ticker,start=0,end=-1,price_feature='Close'):
      cache = Data_API.Cache()
      data = cache.get_ticker_data(ticker)
      pricing_date_index = time_index(data.index.values)
      if(start<0):
          start = start+1+pricing_date_index
      elif start>pricing_date_index:
          start = pricing_date_index
      if(end<0):
          end = end+1+pricing_date_index
      elif end>pricing_date_index:
          end = pricing_date_index
      return np.array(data[price_feature][start:end+1])
  
  def price_return(obj, start=0, end=-1,price_feature='Close'):
      """
      obj: the underlyer ticker to price of. Or it can be an numpy array
      start: int, default to 0, -k means the k-th last day as of observation
      end: int, default to -1, -k means the k-th last day as of observation
      price_feature: default to 'Close', can use 'Open', 'Close', 'High', 'Low'
      """
      if(isinstance(obj,str)):
          obj = prices(obj,start,end,price_feature)
          start = 0
          end = -1
      return obj[end]/obj[start] - 1.0
  
  def log_price_return(obj, start=0, end=-1,price_feature='Close'):
      """
      obj: the underlyer ticker to price of. Or it can be an numpy array
      start: int, default to 0, -k means the k-th last day as of observation
      end: int, default to -1, -k means the k-th last day as of observation
      price_feature: default to 'Close', can use 'Open', 'Close', 'High', 'Low'
      """
      if(isinstance(obj,str)):
          obj = prices(obj,start,end,price_feature)
          start = 0
          end = -1
      return np.log(obj[end]/obj[start])
  
  def price_returns(obj, start=0, end=-1,price_feature='Close',step=1):
      """
      obj: the underlyer ticker to price of. Or it can be an numpy array
      start: int, default to 0, -k means the k-th last day as of observation
      end: int, default to -1, -k means the k-th last day as of observation
      price_feature: default to 'Close', can use 'Open', 'Close', 'High', 'Low'
      """
      if(isinstance(obj,str)):
          obj = prices(obj,start,end,price_feature)
          start = 0
          end = -1
  
      if(end<0):
          end += len(obj)
  
      start_step = start + step
      rets = np.array([obj[idx]/obj[idx-step]-1.0 for idx in range(start_step,end+1)])
      return rets
  
  def log_price_returns(obj, start=0, end=-1,price_feature='Close',step=1):
      """
      obj: the underlyer ticker to price of. Or it can be an numpy array
      start: int, default to 0, -k means the k-th last day as of observation
      end: int, default to -1, -k means the k-th last day as of observation
      price_feature: default to 'Close', can use 'Open', 'Close', 'High', 'Low'
      """
      if(isinstance(obj,str)):
          obj = prices(obj,start,end,price_feature)
          start = 0
          end = -1
  
      if(end<0):
          end += len(obj)
  
      start_step = start + step
      rets = np.log(np.array([obj[idx]/obj[idx-step] for idx in range(start_step,end+1)]))
      return rets
  
  def volatility(obj, start=0, end=-1,price_feature='Close'):
      """
      obj: the underlyer ticker to price of. Or it can be an numpy array
      start: int, default to 0, -k means the k-th last day as of observation
      end: int, default to -1, -k means the k-th last day as of observation
      price_feature: default to 'Close', can use 'Open', 'Close', 'High', 'Low'
      """
      if(isinstance(obj,str)):
          obj = prices(obj,start,end,price_feature)
          start = 0
          end = -1
      if end == -1:
          # end = -1 is a corner case here since (end+1) will be 0
          end = len(obj) - 1
      daily_change = np.log(obj[start+1:end+1]/obj[start:end])
      std = np.std(daily_change)
      vol = np.sqrt(Data_API.Pricing_Database.trading_days) * std
      return vol
  
  def max_draw_down(obj, start=0, end=-1, price_feature='Close'):
      if isinstance(obj,str):
          obj = prices(obj,start,end,price_feature)
          start = 0
          end = -1
  
      if end == -1:
          # end = -1 is a corner case here since (end+1) will be 0
          end = len(obj) - 1
  
      draw_downs = []
      current_max = None
      for i in range(start,end+1):
          if current_max == None:
              current_max = obj[i]+0.0
          elif obj[i]>current_max:
              current_max = obj[i]+0.0
          draw_downs.append((obj[i]-current_max)/current_max)
  
      max_d = np.min(draw_downs)
      return min([0.0,max_d])
  
  def draw_down(obj, start=0, end=-1, price_feature='Close'):
      """
      obj: the underlyer ticker to price of. Or it can be an numpy array
      start: int, default to 0, -k means the k-th last day as of observation
      end: int, default to -1, -k means the k-th last day as of observation
      price_feature: default to 'Close', can use 'Open', 'Close', 'High', 'Low'
      """
      if(isinstance(obj,str)):
          obj = prices(obj,start,end,price_feature)
          start = 0
          end = -1
  
      if end == -1:
          # end = -1 is a corner case here since (end+1) will be 0
          end = len(obj) - 1
  
      min_price = np.min(obj[start:end+1])
      return min([0.0,(min_price - obj[start]) / obj[start]])
  
  def sharpe(obj, start=0, end=-1,price_feature='Close'):
      """
      obj: the underlyer ticker to price of. Or it can be an numpy array
      start: int, default to 0, -k means the k-th last day as of observation
      end: int, default to -1, -k means the k-th last day as of observation
      price_feature: default to 'Close', can use 'Open', 'Close', 'High', 'Low'
      """
      if(isinstance(obj,str)):
          obj = prices(obj,start,end,price_feature)
          start = 0
          end = -1
  
      if end <0:
          end += len(obj)
  
      ndays = end - start + 1
      annual_return = (1 + price_return(obj,start,end)) ** (float(Data_API.Pricing_Database.trading_days) / ndays) - 1
      vol = volatility(obj,start,end)
      if annual_return == 0:
          return 0
      return annual_return / vol
  
  def all_indicators_doc():
      all_funcs = inspect.getmembers(sys.modules[__name__], inspect.isfunction)
      ret = ""
      for f in all_funcs:
          ret += pydoc.render_doc(f[1], renderer = pydoc.plaintext).splitlines()[2] + "\n"
      return ret
  
  def average(obj, start=0, end=-1, price_feature='Close'):
      if isinstance(obj, str):
          obj = prices(obj, start, end, price_feature)
          start = 0
          end = -1
  
      if end < 0:
          end += len(obj)
      if start < 0:
          start += len(obj)
  
      return np.mean(obj[start: (end + 1)])
  
  
  def max(obj, start=0, end=-1, price_feature='Close'):
      if isinstance(obj, str):
          obj = prices(obj, start, end, price_feature)
          start = 0
          end = -1
  
      if end < 0:
          end += len(obj)
      if start < 0:
          start += len(obj)
  
      return np.max(obj[start: (end + 1)])
  
  
  def min(obj, start=0, end=-1, price_feature='Close'):
      if isinstance(obj, str):
          obj = prices(obj, start, end, price_feature)
          start = 0
          end = -1
  
      if end < 0:
          end += len(obj)
      if start < 0:
          start += len(obj)
  
      return np.min(obj[start: (end + 1)])
  
  def rsi(obj, start=-14, end=-1, price_feature='Close'):
      if isinstance(obj, str):
          obj = prices(obj, start, end, price_feature)
          start = 0
          end = -1
  
      if end < 0:
          end += len(obj)
      if start < 0:
          start += len(obj)
  
      _data = np.diff(obj[start: (end + 1)])
      len_gain = len(_data[_data > 0.0])
      len_loss = len(_data[_data < 0.0])
      if len_gain == 0 or len_loss == 0:
          return 50
      average_gain = np.mean(_data[_data > 0.0])
      average_loss = np.abs(np.mean(_data[_data < 0.0]))
      first_rs = average_gain / average_loss
      rsi = 100 - 100 / (1 + first_rs)
  
      return rsi
  
  def correlation(obj1, obj2, start=0, end=-1, price_feature='Close'):
      if isinstance(obj1, str) or isinstance(obj2, str):
          obj1 = log_price_returns(obj1, start, end, price_feature)
          obj2 = log_price_returns(obj2, start, end, price_feature)
          # simple and rough treatment: assume biz days are the same among the two tickers
          if len(obj1)>len(obj2):
              obj1 = obj1[len(obj1)-len(obj2):]
          else:
              obj2 = obj2[len(obj2)-len(obj1):]
          start = 0
          end = -1
  
      if end < 0:
          end += len(obj1)
      if start < 0:
          start += len(obj1)
  
      return np.corrcoef(obj1[start: (end + 1)], obj2[start: (end + 1)])[0, 1]
  
  