Exemple #1
0
    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()
Exemple #2
0
    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
Exemple #3
0
    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
Exemple #4
0
    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
Exemple #5
0
 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)
Exemple #6
0
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
Exemple #7
0
    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)
Exemple #8
0
    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)
Exemple #9
0
    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()
Exemple #10
0
 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')
Exemple #11
0
    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')
Exemple #12
0
    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')
Exemple #13
0
    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
Exemple #14
0
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'))
Exemple #15
0
def marketsignal_inst():
    ms_inst = Data('marketsignal')
    assert ms_inst.algorithm_type == 'marketsignal'
    return ms_inst
Exemple #16
0
def data_instance():
    data_inst = Data()
    return data_inst
Exemple #17
0
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'))