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]