def __init__(self, taskname, portfolio_type, stocks, capital): ## ** 인자 설명 ** ## ### portfolio_type (str) --> 포트폴리오 타입은 S 혹은 CS이다. (S: Stock, CS: Cash + Stock) ### stocks (list) --> ['000020', '000030'] --> 리스트 형식] ### capital (int) --> 총 투자 자본금 self.taskname = taskname.lower() self.data = Data('portfolio', stocks) # 포트폴리오의 메타데이터를 만든다 port = { 'portfolio': portfolio_type, 'stocks': stocks, 'stock_count': len(stocks), 'capital': capital, 'capital_per_stock': capital // len(stocks) # 각 주식에 기본적으로 얼마를 투자해야 하는지 계산 } # 입력받은 인자로 포트폴리오 기본 정보 설정하기 # 클래스 속성 설정 self.port_params = port # 위에서 만든 port 딕셔너리를 속성으로 만든다 self.ratio_dict = {'cash': 0} # 시작 현금 금액은 0원이다. # 만약, port['portfolio'] == 'S' 이면 현금 금액은 계속 0이다. # 만약, port['portfolio'] == 'CS' 이면 현금 금액은 알고리즘에 따라 변해야 한다. # 앞으로, self.ratio_dict에 다른 종목들 비중도 계산하여 업데이트해줄 것이다 # Data 인스턴스를 생성하여 stocks에서 입력받은 주가 정보를 받아온다. self.ohlcv_inst_list = self.data.make_ohlcv_data()
def make_style_df(self): data = self.data data.request('style') rms_data = Data('rms') rms_data.request('close') style_data = { 'g': data.growth_index, 'v': data.value_index, 'y': data.yield_index, 'q': data.quality_index, 's': data.social_index } cls_df = pd.concat([rms_data.kospi_cls_df, rms_data.kosdaq_cls_df], axis=1, sort=True) vol_df = pd.concat([rms_data.kospi_vol_df, rms_data.kosdaq_vol_df], axis=1, sort=True) for d in style_data.items(): tmp = d[1][['date', 'cls_prc']] tmp.set_index('date', inplace=True) tmp.index = pd.to_datetime(tmp.index) tmp.rename(columns={'cls_prc': d[0]}, inplace=True) cls_df = pd.concat([cls_df, tmp], axis=1, sort=True) tmp = d[1][['date', 'trd_qty']] tmp.set_index('date', inplace=True) tmp.index = pd.to_datetime(tmp.index) tmp.rename(columns={'trd_qty': d[0]}, inplace=True) vol_df = pd.concat([vol_df, tmp], axis=1, sort=True) return cls_df, vol_df
def make_industry_df(self): data = self.data data.request('industry') rms_data = Data('rms') rms_data.request('close') ranked_industry = [k for (k, v) in sorted(data.industry_data.items(), key=lambda x: x[1]['cls_prc'].iloc[-1], reverse=True)][:3] industry_data = { 'ind_1': data.industry_data[ranked_industry[0]], 'ind_2': data.industry_data[ranked_industry[1]], 'ind_3': data.industry_data[ranked_industry[2]] } cls_df = pd.concat([rms_data.kospi_cls_df, rms_data.kosdaq_cls_df], axis=1, sort=True) vol_df = pd.concat([rms_data.kospi_vol_df, rms_data.kosdaq_vol_df], axis=1, sort=True) for d in industry_data.items(): tmp = d[1][['date', 'cls_prc']] tmp.set_index('date', inplace=True) tmp.index = pd.to_datetime(tmp.index) tmp.rename(columns={'cls_prc': d[0]}, inplace=True) cls_df = pd.concat([cls_df, tmp], axis=1, sort=True) tmp = d[1][['date', 'trd_qty']] tmp.set_index('date', inplace=True) tmp.index = pd.to_datetime(tmp.index) tmp.rename(columns={'trd_qty': d[0]}, inplace=True) vol_df = pd.concat([vol_df, tmp], axis=1, sort=True) return cls_df, vol_df, ranked_industry
def benchmark_info(self): ms_data = Data('marketsignal') ms_data.request('bm') benchmark = ms_data.kospi_index benchmark_cls = benchmark[['date', 'cls_prc']] benchmark_cls.set_index('date', inplace=True) benchmark_cls.index = pd.to_datetime(benchmark_cls.index) benchmark_cls.rename(columns={'cls_prc': 'Benchmark'}, inplace=True) BM_R = benchmark_cls.resample('M').last().pct_change() BM_R.dropna(how='all', inplace=True) W = pd.Series([1], index=['Benchmark']) return self.backtest_portfolio(W, BM_R)
def score(cls_df, vol_df, include_correlation=False, do_rank=False): from algorithms.data import Data ms_data = Data('marketsignal') ms_data.request('bm') benchmark = ms_data.kospi_index benchmark_cls = benchmark[['date', 'cls_prc']] benchmark_cls.set_index('date', inplace=True) benchmark_cls.index = pd.to_datetime(benchmark_cls.index) benchmark_cls.rename(columns={'cls_prc': 'Benchmark'}, inplace=True) cls_df = pd.concat([cls_df, benchmark_cls], axis=1, sort=True) benchmark_vol = benchmark[['date', 'trd_qty']] benchmark_vol.set_index('date', inplace=True) benchmark_vol.index = pd.to_datetime(benchmark_vol.index) benchmark_vol.rename(columns={'trd_qty': 'Benchmark'}, inplace=True) vol_df = pd.concat([vol_df, benchmark_vol], axis=1, sort=True) vol = cls_df * vol_df vol_score = vol.rank(ascending=True, axis=1) vol_score = (vol_score / vol_score.max()) * 100 base = cls_df.pct_change() base.fillna(0, inplace=True) mom = dual_momentum(base) mom_score = mom.rank(ascending=True, axis=1) mom_score = (mom_score / mom_score.max()) * 100 volt = volatility(base, 200) volt_score = volt.rank(ascending=False, axis=1) volt_score = (volt_score / volt_score.max()) * 100 total_score = vol_score + mom_score + volt_score if include_correlation: cor = correlation(base, 200) cor_score = cor.rank(ascending=False, axis=1) cor_score = (cor_score / cor_score.max()) * 100 total_score = total_score + cor_score // 4 else: total_score = total_score // 3 if do_rank: total_score = total_score.rank(ascending=True, axis=1) total_score.fillna(0, inplace=True) return total_score
def make_size_df(self): data = self.data data.request('size') rms_data = Data('rms') rms_data.request('close') kp_data = { 'lg': data.kp_lg_cap_index, 'md': data.kp_md_cap_index, 'sm': data.kp_sm_cap_index } kd_data = { 'lg': data.kd_lg_cap_index, 'md': data.kd_md_cap_index, 'sm': data.kd_sm_cap_index } kospi_cls_df = rms_data.kospi_cls_df.copy() kospi_vol_df = rms_data.kospi_vol_df.copy() kosdaq_cls_df = rms_data.kosdaq_cls_df.copy() kosdaq_vol_df = rms_data.kosdaq_vol_df.copy() for d in kp_data.items(): tmp = d[1][['date', 'cls_prc']] tmp.set_index('date', inplace=True) tmp.index = pd.to_datetime(tmp.index) tmp.rename(columns={'cls_prc': d[0]}, inplace=True) kospi_cls_df = pd.concat([kospi_cls_df, tmp], axis=1, sort=True) tmp = d[1][['date', 'trd_qty']] tmp.set_index('date', inplace=True) tmp.index = pd.to_datetime(tmp.index) tmp.rename(columns={'trd_qty': d[0]}, inplace=True) kospi_vol_df = pd.concat([kospi_vol_df, tmp], axis=1, sort=True) for d in kd_data.items(): tmp = d[1][['date', 'cls_prc']] tmp.set_index('date', inplace=True) tmp.index = pd.to_datetime(tmp.index) tmp.rename(columns={'cls_prc': d[0]}, inplace=True) kosdaq_cls_df = pd.concat([kosdaq_cls_df, tmp], axis=1, sort=True) tmp = d[1][['date', 'trd_qty']] tmp.set_index('date', inplace=True) tmp.index = pd.to_datetime(tmp.index) tmp.rename(columns={'trd_qty': d[0]}, inplace=True) kosdaq_vol_df = pd.concat([kosdaq_vol_df, tmp], axis=1, sort=True) return (kospi_cls_df, kospi_vol_df), (kosdaq_cls_df, kosdaq_vol_df)
def __init__(self, taskname, today_date=None): self.taskname = taskname.lower() if today_date == None: self.today_date = timezone.now().strftime('%Y%m%d') else: self.today_date = today_date self.data = Data('scanner')
def set_port_analysis_settings(self): if self.ratio_dict: self.settings['ticker_list'] = [ticker for ticker in self.ratio_dict.keys() if ticker != 'cash'] self.data = Data('rms', self.settings['ticker_list']) # ohlcv_list 리스트 생성하기 print('create ohlcv_list with Data instance')
def __init__(self, today_date=None): # 오늘 날짜를 인자로 받고, 아무값이 들어오지 않았다면 YYYYMMDD형식으로 직접 포맷하여 # self.today_date로 새팅 if today_date == None: self.today_date = timezone.now().strftime('%Y%m%d') else: self.today_date = today_date # 데이터베이스 혹은 캐시에서 데이터를 가져올 수 있도록 Data 인스턴스를 만들어준다. # 어떤 데이트를 필요로 하는지 알기 위해 'scanner'을 인자로 보내준다. self.data = Data('scanner')
def make_industry_df(self): data = self.data data.request('industry') rms_data = Data('rms') rms_data.request('close') ranked_industry = [ k for (k, v) in sorted(data.industry_data.items(), key=lambda x: x[1]['cls_prc'].iloc[-1], reverse=True) ][:3] industry_data = { 'ind_1': data.industry_data[ranked_industry[0]], 'ind_2': data.industry_data[ranked_industry[1]], 'ind_3': data.industry_data[ranked_industry[2]] } cls_df = pd.concat([rms_data.kospi_cls_df, rms_data.kosdaq_cls_df], axis=1, sort=True) vol_df = pd.concat([rms_data.kospi_vol_df, rms_data.kosdaq_vol_df], axis=1, sort=True) for d in industry_data.items(): tmp = d[1][['date', 'cls_prc']] tmp.set_index('date', inplace=True) tmp.index = pd.to_datetime(tmp.index) tmp.rename(columns={'cls_prc': d[0]}, inplace=True) cls_df = pd.concat([cls_df, tmp], axis=1, sort=True) tmp = d[1][['date', 'trd_qty']] tmp.set_index('date', inplace=True) tmp.index = pd.to_datetime(tmp.index) tmp.rename(columns={'trd_qty': d[0]}, inplace=True) vol_df = pd.concat([vol_df, tmp], axis=1, sort=True) return cls_df, vol_df, ranked_industry
class PortfolioProcessor(object): ''' **설명: 포트폴리오에 사용할 자보금과 포트폴리오에 추가하고 싶은 주식 종목들 인풋으로 받아, 동일 비중 포트폴리오를 형성해 주는 클래스다. 동일 비중 포트폴리오를 생성하는데 사용가능한 다양한 알고리즘이 있지만, 마인드의 알고리즘은 이런 단계를 거친다: (예를 들어 자본금 1000원으로 가격이 각각 100, 200, 300인 주식을 사서 동일 비중 포트폴리오를 만든다고 생각해보자) - 1000을 3으로 나눈다. - 위에서 계산한 값보다 가격이 높은 종목은 고려하지 않는다. - 포트폴리오에 모든 종목을 한 주씩 추가한다. - 남은 자본금을 계산한다 --> 1000 - 100 - 200 - 300 = 400 - 400을 3으로 나눈다. - 위에서 계산한 값보다 가격이 높은 종목은 고려하지 않는다. - 가격이 100, 200인 종목으로만 포트폴리오를 만든다. - 400으로 2를 나눈다. (200) - 가격이 200 미만인 종목은 제외 한다 (없다) - 각 종목을 하나씩 포트폴리오에 추가한다. - 남은 자본금 금액을 계산한다 --> 400 - 100 - 200 = 100 - 위와 같은 절차를 반복한다... 결과: 100: 3개, 200: 2개, 300: 1개로 포트폴리오를 구성하게 된다. **태스크: 1. ''' # *** UPDATE: 20180915 ***# def __init__(self, taskname, portfolio_type, stocks, capital): ## ** 인자 설명 ** ## ### portfolio_type (str) --> 포트폴리오 타입은 S 혹은 CS이다. (S: Stock, CS: Cash + Stock) ### stocks (list) --> ['000020', '000030'] --> 리스트 형식] ### capital (int) --> 총 투자 자본금 self.taskname = taskname.lower() self.data = Data('portfolio', stocks) # 포트폴리오의 메타데이터를 만든다 port = { 'portfolio': portfolio_type, 'stocks': stocks, 'stock_count': len(stocks), 'capital': capital, 'capital_per_stock': capital // len(stocks) # 각 주식에 기본적으로 얼마를 투자해야 하는지 계산 } # 입력받은 인자로 포트폴리오 기본 정보 설정하기 # 클래스 속성 설정 self.port_params = port # 위에서 만든 port 딕셔너리를 속성으로 만든다 self.ratio_dict = {'cash': 0} # 시작 현금 금액은 0원이다. # 만약, port['portfolio'] == 'S' 이면 현금 금액은 계속 0이다. # 만약, port['portfolio'] == 'CS' 이면 현금 금액은 알고리즘에 따라 변해야 한다. # 앞으로, self.ratio_dict에 다른 종목들 비중도 계산하여 업데이트해줄 것이다 # Data 인스턴스를 생성하여 stocks에서 입력받은 주가 정보를 받아온다. self.ohlcv_inst_list = self.data.make_ohlcv_data() # *** UPDATE: 20180824 ***# def reduce(self): taskname = self.taskname if hasattr(self, taskname): reducer = getattr(self, taskname) response = reducer() return response else: return {'state': '{} 태스크는 없습니다'.format(taskname)} # *** UPDATE: 20180915 ***# def get_recent_stock_close_price(self, ticker): # 종목의 코드를 인자로 받아서 그 종목의 최근 종가를 리턴하는 메소드 for o in self.ohlcv_inst_list: tmp = o.iloc[-1] if tmp['code'] == ticker: return int(tmp['cls_prc']) return None # *** UPDATE: 20180915 ***# def initial_distribution(self): # 초기에 자본을 분배할 때는 모든 종목에 동일한 비중의 자본금을 나누는 형식으로 진행한다 port = self.port_params for stock in port['stocks']: close_price = self.get_recent_stock_close_price(stock) stock_data = {'name': stock, 'price': close_price} self.ratio_dict[stock] = stock_data capital_per_stock = port['capital_per_stock'] if close_price < capital_per_stock: stock_num = capital_per_stock // close_price invested = int(stock_num * close_price) self.ratio_dict[stock]['invested'] = invested self.ratio_dict[stock]['buy_num'] = stock_num self.ratio_dict['cash'] += (capital_per_stock - invested) else: self.ratio_dict[stock]['invested'] = 0 self.ratio_dict[stock]['buy_num'] = 0 # *** UPDATE: 20180915 ***# def redistribute(self): left_over_capital = self.ratio_dict['cash'] redistribute = left_over_capital > 0 while redistribute: extra_buy = list( filter( lambda x: x[0] != 'cash' and x[1]['price'] < left_over_capital, self.ratio_dict.items())) if len(extra_buy) == 0: break extra_capital_per_stock = left_over_capital // len(extra_buy) extra_buy_num = list( map(lambda x: extra_capital_per_stock // x[1]['price'], extra_buy)) if sum(extra_buy_num) == 0: break close_price_list = [x[1]['price'] for x in extra_buy] reset_left_over = int( left_over_capital - sum(map(lambda x, y: x * y, extra_buy_num, close_price_list))) extra_stocks = [x[0] for x in extra_buy] for i in range(len(extra_stocks)): extra_invested = extra_buy_num[i] * close_price_list[i] self.ratio_dict[extra_stocks[i]]['invested'] += int( extra_invested) self.ratio_dict[extra_stocks[i]]['buy_num'] += int( extra_buy_num[i]) self.ratio_dict['cash'] = reset_left_over left_over_capital = reset_left_over for key, val in self.ratio_dict.items(): if key != 'cash': stock_ratio = val['invested'] / self.port_params['capital'] self.ratio_dict[key]['ratio'] = float( format(round(stock_ratio, 4), '.4f'))
def marketsignal_inst(): ms_inst = Data('marketsignal') assert ms_inst.algorithm_type == 'marketsignal' return ms_inst
def data_instance(): data_inst = Data() return data_inst
class PortfolioProcessor(object): ''' **설명: 포트폴리오에 사용할 자보금과 포트폴리오에 추가하고 싶은 주식 종목들 인풋으로 받아, 동일 비중 포트폴리오를 형성해 주는 클래스다. 동일 비중 포트폴리오를 생성하는데 사용가능한 다양한 알고리즘이 있지만, 마인드의 알고리즘은 이런 단계를 거친다: (예를 들어 자본금 1000원으로 가격이 각각 100, 200, 300인 주식을 사서 동일 비중 포트폴리오를 만든다고 생각해보자) - 1000을 3으로 나눈다. - 위에서 계산한 값보다 가격이 높은 종목은 고려하지 않는다. - 포트폴리오에 모든 종목을 한 주씩 추가한다. - 남은 자본금을 계산한다 --> 1000 - 100 - 200 - 300 = 400 - 400을 3으로 나눈다. - 위에서 계산한 값보다 가격이 높은 종목은 고려하지 않는다. - 가격이 100, 200인 종목으로만 포트폴리오를 만든다. - 400으로 2를 나눈다. (200) - 가격이 200 미만인 종목은 제외 한다 (없다) - 각 종목을 하나씩 포트폴리오에 추가한다. - 남은 자본금 금액을 계산한다 --> 400 - 100 - 200 = 100 - 위와 같은 절차를 반복한다... 결과: 100: 3개, 200: 2개, 300: 1개로 포트폴리오를 구성하게 된다. **태스크: 1. ''' # *** UPDATE: 20180915 ***# def __init__(self, taskname, portfolio_type, stocks, capital): ## ** 인자 설명 ** ## ### portfolio_type (str) --> 포트폴리오 타입은 S 혹은 CS이다. (S: Stock, CS: Cash + Stock) ### stocks (list) --> ['000020', '000030'] --> 리스트 형식] ### capital (int) --> 총 투자 자본금 self.taskname = taskname.lower() self.data = Data('portfolio', stocks) # 포트폴리오의 메타데이터를 만든다 port = { 'portfolio': portfolio_type, 'stocks': stocks, 'stock_count': len(stocks), 'capital': capital, 'capital_per_stock': capital // len(stocks) # 각 주식에 기본적으로 얼마를 투자해야 하는지 계산 } # 입력받은 인자로 포트폴리오 기본 정보 설정하기 # 클래스 속성 설정 self.port_params = port # 위에서 만든 port 딕셔너리를 속성으로 만든다 self.ratio_dict = {'cash': 0} # 시작 현금 금액은 0원이다. # 만약, port['portfolio'] == 'S' 이면 현금 금액은 계속 0이다. # 만약, port['portfolio'] == 'CS' 이면 현금 금액은 알고리즘에 따라 변해야 한다. # 앞으로, self.ratio_dict에 다른 종목들 비중도 계산하여 업데이트해줄 것이다 # Data 인스턴스를 생성하여 stocks에서 입력받은 주가 정보를 받아온다. self.ohlcv_inst_list = self.data.make_ohlcv_data() # *** UPDATE: 20180824 ***# def reduce(self): taskname = self.taskname if hasattr(self, taskname): reducer = getattr(self, taskname) response = reducer() return response else: return {'state': '{} 태스크는 없습니다'.format(taskname)} # *** UPDATE: 20180915 ***# def get_recent_stock_close_price(self, ticker): # 종목의 코드를 인자로 받아서 그 종목의 최근 종가를 리턴하는 메소드 for o in self.ohlcv_inst_list: tmp = o.iloc[-1] if tmp['code'] == ticker: return int(tmp['cls_prc']) return None # *** UPDATE: 20180915 ***# def initial_distribution(self): # 초기에 자본을 분배할 때는 모든 종목에 동일한 비중의 자본금을 나누는 형식으로 진행한다 port = self.port_params for stock in port['stocks']: close_price = self.get_recent_stock_close_price(stock) stock_data = { 'name': stock, 'price': close_price } self.ratio_dict[stock] = stock_data capital_per_stock = port['capital_per_stock'] if close_price < capital_per_stock: stock_num = capital_per_stock // close_price invested = int(stock_num * close_price) self.ratio_dict[stock]['invested'] = invested self.ratio_dict[stock]['buy_num'] = stock_num self.ratio_dict['cash'] += (capital_per_stock - invested) else: self.ratio_dict[stock]['invested'] = 0 self.ratio_dict[stock]['buy_num'] = 0 # *** UPDATE: 20180915 ***# def redistribute(self): left_over_capital = self.ratio_dict['cash'] redistribute = left_over_capital > 0 while redistribute: extra_buy = list( filter(lambda x: x[0] != 'cash' and x[1]['price'] < left_over_capital, self.ratio_dict.items())) if len(extra_buy) == 0: break extra_capital_per_stock = left_over_capital // len(extra_buy) extra_buy_num = list(map(lambda x: extra_capital_per_stock // x[1]['price'], extra_buy)) if sum(extra_buy_num) == 0: break close_price_list = [x[1]['price'] for x in extra_buy] reset_left_over = int(left_over_capital - sum(map(lambda x, y: x * y, extra_buy_num, close_price_list))) extra_stocks = [x[0] for x in extra_buy] for i in range(len(extra_stocks)): extra_invested = extra_buy_num[i] * close_price_list[i] self.ratio_dict[extra_stocks[i]]['invested'] += int(extra_invested) self.ratio_dict[extra_stocks[i]]['buy_num'] += int(extra_buy_num[i]) self.ratio_dict['cash'] = reset_left_over left_over_capital = reset_left_over for key, val in self.ratio_dict.items(): if key != 'cash': stock_ratio = val['invested'] / self.port_params['capital'] self.ratio_dict[key]['ratio'] = float(format(round(stock_ratio, 4), '.4f'))