본문 바로가기
Backend/Python

[Python] 비트코인 자동매매 - 종목 선정

by 천우산__ 2024. 5. 9.

이전 글 - 머신러닝 편

 

[Python] 비트코인 자동매매 - 머신러닝

이전글 - 비트코인 차트 불러오기 [Python] 비트코인 자동 매매 - 차트 불러오기1. 차트 불러오기 전 자동 매매 과정을 글로 표현하면 아래와 같다. 시장 분석을 한다. 매수 / 매도 목표 가격을 정한

chunws13.tistory.com

비트코인으로만 투자할 예정이라면 이대로 백테스팅 과정을 거쳐 실제 투자를 진행하면 되지만

보다 안전? 하게 투자를 진행하기 위해 보유 자산을 5분할하여 투자할 예정이다.

위 방법으로 진행하기 위해서는 어떤 코인에 투자할지 선택을 해야하는데, 내가 지정해서 할 수도 있지만

주관을 빼고 머신러닝 결과 값을 가지고 투자를 진행하는게 더 신뢰성 있다고 판단했다.

 

1. 머신러닝 결과 값 평가하기

결과 값을 평가는 내가 예측한 값과 실제 값의 차이가 어느 정도가 되는가를 측정하는 것인데

평가 지표는 평균 절대 오차, 평균 제곱 오차 등등 여러 가지가 있다.

코인별로 가격대가 천차만별이므로 제곱차, 절대차 등으로 계산하면 가격대가 높은 코인일수록 불리하게 작용하므로

예측값과 실제값 차이의 백분율을 기준으로 평가하기로 했다.

정확도가 높을수록 실제 가격과 차이가 없다는 의미이므로 0에 가깝게 나온다.

def regression_actual(learning_data):
    learning_data.dropna(inplace=True)

    train_data = learning_data.iloc[:-1]
    latest_data = learning_data.iloc[-1]
    
    independent = train_data[["open", "ma5", "ma20", "rsi", "volume7"]]
    dependent = train_data[["close", "high", "low"]]
    
    ind_train, ind_test, de_train, de_test = train_test_split(independent, dependent, test_size=0.2, random_state=40)
    
    model = LinearRegression()
    
    model.fit(ind_train.values, de_train.values)
    estimate = model.predict(ind_test.values)

    ### 당일 종가 예측
    latest_info = latest_data[["open", "ma5", "ma20", "rsi", "volume7"]].values.reshape(1, -1)
    predict = model.predict(latest_info)
    
    ### 정확도 판단하기
    accuracy = numpy.mean(numpy.abs((de_test["low"] - estimate[:,2]) / de_test["low"])) * 100
    return [predict[0][1], predict[0][2], - accuracy]

이전 글에서 저가와 고가를 모두 이용해서 정확도를 평가해도 되지만 저가만 기준으로 잡은 이유는 아래와 같다.

1. 일단 매수가 발생해야 예상 고가 혹은 종가에 팔 수 있다. 

2. 고가 & 종가는 저가보다 같거나 높다.

2번의 경우 예측으로 잡은 수치라서 실제 투자시 손해가 발생 할 가능성이 있지만

테스트 결과 정확도가 높은 코인만 사용할 것이므로 나름대로 어느 정도의 안전 장치는 해둔 셈이다.

 

2. 코인 선별 코드 완성하기

원화로 구매 가능한 모든 코인들 ("KRW-" 가 포함된 코인)을 대상으로 최근 180일의 데이터를 기준,

예상 저가와 고가를 확인하며, 평가 결과가 우수한 코인 invest_number 개를 선별하자

def check_bull_market(target_date, invest_number): # invest_number: 분할 개수 = 투자 코인 수
    KRW_CHECKER = "KRW-"
    from_date = target_date - datetime.timedelta(days=180)

    ticker_list = pyupbit.get_tickers()
    bull_market = []
    
    for ticker in ticker_list:
        if re.match(KRW_CHECKER, ticker):
            ma_flow_info = pyupbit.get_ohlcv_from(ticker=ticker, fromDatetime=from_date, to=target_date)
            
            if ma_flow_info is None or len(ma_flow_info) < 180:
                continue

            ma_flow_info["ma5"] = ma_flow_info["close"].rolling(window=5, min_periods=5).mean().shift(1)
            ma_flow_info["ma20"] = ma_flow_info["close"].rolling(window=20, min_periods=20).mean().shift(1)
            
            ma_flow_info["volume7"] = ma_flow_info["volume"].rolling(window=7, min_periods=7).mean().shift(1)

            diff_info = ma_flow_info["close"].diff()
            
            gain = (diff_info.where(diff_info > 0, 0)).rolling(window=14).mean().shift(1)
            loss = (-diff_info.where(diff_info < 0, 0)).rolling(window=14).mean().shift(1)
            ma_flow_info["rsi"] = 100 - (100 / (1 + (gain / loss)))
            
            predict_high, predict_low, accuracy = regression_actual(ma_flow_info)
            
            if len(bull_market) < invest_number:
                heapq.heappush(bull_market, [accuracy, predict_low, predict_high, ticker])
                continue
            
            if predict_profit > bull_market[0][0]:
                heapq.heappop(bull_market)
                heapq.heappush(bull_market, [accuracy, predict_low, predict_high, ticker])
                
    result = {} 
    
    while bull_market:
        accuracy, predict_low, predict_high, ticker = heapq.heappop(bull_market)
        result[ticker] = {"high": predict_high, "low": predict_low}
            
    return result

발행한지 180일이 넘는 코인을 대상으로 머신러닝에 사용될 지표를 추가하고

예상 고가와 저가, 정확도를 반환하는 함수 (regression_actual)를 실행한다.

반환된 값이 목표 투자 개수(bull_market 길이)를 넘지 않는다면 투자 리스트에 추가한다.

목표 투자 개수가 넘는 경우, 투자 리스트 중 가장 정확도가 낮은 정확도와 비교하여 더 높은 정확도를 가진 코인으로 선정한다.

 

투자 리스트는 힙(heap) 구조로 관리하는데, [정확도, 예상 저가, 예상 고가, 코인명] 순으로 담는다.

이 때 새로 값이 추가되면 heap 구조로 인해 정확도가 가장 낮은 값이 가장 앞에 위치하게 된다.

위에서는 0에 가까울수록 정확도가 높은 것이라고 설명했는데, 이렇게 되면 정확도가 낮은 코인들만 수집하는거 아닌가?

할 수 있는데, 코인 선별 때 정확도 값을 마이너스 (-)로 반환하게 해서 해당 문제 발생을 방지했다.

 

이 코드의 결과는 아래와 같이 반환된다.

{
    'KRW-MINA': 
    	{'high': 1147.5673749496295, 'low': 1054.7140915309158}, 
    
    'KRW-JST': 
    	{'high': 46.86611789944993, 'low': 44.59460579125492}, 
        
    'KRW-XRP': 
    	{'high': 736.3219530661302, 'low': 709.9888305497924}, 
        
    'KRW-BTC': 
    	{'high': 86882756.21459118, 'low': 83770803.79255714}, 
        
    'KRW-TRX': 
    	{'high': 176.09899949808337, 'low': 171.43466645378732}
}

 

3. 전체 코드

def check_bull_market(target_date, invest_number):
    KRW_CHECKER = "KRW-"
    from_date = target_date - datetime.timedelta(days=180)

    ticker_list = pyupbit.get_tickers()
    bull_market = []
    
    for ticker in ticker_list:
        if re.match(KRW_CHECKER, ticker):
            ma_flow_info = pyupbit.get_ohlcv_from(ticker=ticker, fromDatetime=from_date, to=target_date)
            
            if ma_flow_info is None or len(ma_flow_info) < 180:
                continue

            ma_flow_info["ma5"] = ma_flow_info["close"].rolling(window=5, min_periods=5).mean().shift(1)
            ma_flow_info["ma20"] = ma_flow_info["close"].rolling(window=20, min_periods=20).mean().shift(1)
            
            ma_flow_info["volume7"] = ma_flow_info["volume"].rolling(window=7, min_periods=7).mean().shift(1)

            diff_info = ma_flow_info["close"].diff()
            
            gain = (diff_info.where(diff_info > 0, 0)).rolling(window=14).mean().shift(1)
            loss = (-diff_info.where(diff_info < 0, 0)).rolling(window=14).mean().shift(1)
            ma_flow_info["rsi"] = 100 - (100 / (1 + (gain / loss)))
            
            predict_high, predict_low, accuracy = regression_actual(ma_flow_info)
            
            if len(bull_market) < invest_number:
                heapq.heappush(bull_market, [accuracy, predict_low, predict_high, ticker])
                continue
            
            if predict_profit > bull_market[0][0]:
                heapq.heappop(bull_market)
                heapq.heappush(bull_market, [accuracy, predict_low, predict_high, ticker])
                
    result = {}  
    while bull_market:
        accuracy, predict_low, predict_high, ticker = heapq.heappop(bull_market)
        result[ticker] = {"high": predict_high, "low": predict_low}
            
    return result


def regression_actual(learning_data):
    learning_data.dropna(inplace=True)

    train_data = learning_data.iloc[:-1]
    latest_data = learning_data.iloc[-1]
    
    independent = train_data[["open", "ma5", "ma20", "rsi", "volume7"]]
    dependent = train_data[["close", "high", "low"]]
    
    ind_train, ind_test, de_train, de_test = train_test_split(independent, dependent, test_size=0.2, random_state=40)
    
    model = LinearRegression()
    
    model.fit(ind_train.values, de_train.values)
    estimate = model.predict(ind_test.values)

    latest_info = latest_data[["open", "ma5", "ma20", "rsi", "volume7"]].values.reshape(1, -1)
    predict = model.predict(latest_info)
    
    accuracy = numpy.mean(numpy.abs((de_test["low"] - estimate[:,2]) / de_test["low"])) * 100
    return [predict[0][1], predict[0][2], - accuracy]

이제 백테스팅을 위한 모든 준비는 끝났다.

다음에는 이 모델로 투자를 진행하면 어느 정도의 수익을 낼 수 있었는지 테스트를 진행해보자

다음 글 - 백테스팅 진행하기