개발일지

업비트 코인 자동매매-2

22rodnjf 2022. 1. 18. 17:35

2022.01.17 - [개발일지] - 업비트 코인 자동매매-1

 

업비트 코인 자동매매-1

이 글은 파이썬을 이용해 업비트와 연동해서 차트에 맞춰 자동거래를 하는 툴을 제작했던 제작기이다. 개발을 하는 과정을 보여드리는 것에 초점을 맞출 예정이라 참조했던 블로그나 생각의 흐

studiolettuce.tistory.com

일전에는 데이터를 Pandas로 변환해서 가져오는 것 까지 완료했다.

그렇다면 이제 구매와 판매에 대한 조건식을 걸어주는 것과 지갑에 접근하는 방법을 알아보자.

 

4. 조건식 만들기

개인적으로 생각하는 투자기법에서 중요한 것은 이동평균선과 RSI, BB%B로 생각했기 때문에 해당 데이터를 모두 pandas에 포함시켰다.

그럼 조건을 이용해 매수와 매도를 하도록 짜보자.


먼저 특정 데이터를 불러오는 수식을 짜도록 하자

now_data = df['close'].iloc[-1]
ma20 = df['close'].rolling(window=20).mean()
ma100 = df['close'].rolling(window=100).mean()
ma20 = ma20.iloc[-1]
ma100 = ma100.iloc[-1]

df['MA20'] = df['close'].rolling(window=20).mean()
df['stddev'] = df['close'].rolling(window=20).std()
df['upper'] = df['MA20'] + (df['stddev'] * 2)  # 볼린저 밴드 상단선
df['lower'] = df['MA20'] - (df['stddev'] * 2)  # 볼린저 밴드 하단선
df['PB'] = (df['close'] - df['lower']) / (df['upper'] - df['lower'])
BB = df['PB'].iloc[-1]
BB_L = df['PB'].iloc[198]

RSI_n = 14
df['updown'] = df['close'].diff()
df['RSI_U'] = df['updown'].apply(lambda x: x if x > 0 else 0)
df['RSI_D'] = df['updown'].apply(lambda x: x * (-1) if x < 0 else 0)
df['RSI_AU'] = df['RSI_U'].ewm(com=RSI_n - 1, min_periods=RSI_n).mean()
df['RSI_AD'] = df['RSI_D'].ewm(com=RSI_n - 1, min_periods=RSI_n).mean()
df['RS'] = df['RSI_AU'] / df['RSI_AD']
df['RSI'] = 100 - (100 / (1 + df['RS']))

RSI = df['RSI'].iloc[-1]
RSI_L = df['RSI'].iloc[198]

 

일전에 작성했던 코드에서 아래 변수에 값을 불러온다.

iloc [-1]을 사용하면 해당 테이블의 열 안의 가장 아래 값을 가져올 수 있다.

이를 이용해 20일 이동평균, 100일 이동평균, 20일 BB% B값, 그 직전 값, RSI값, RSI직전 값 이렇게 총 6개의 값을 변수에 저장한다.

ma20 = ma20.iloc[-1]
ma100 = ma100.iloc[-1]
BB = df['PB'].iloc[-1]
BB_L = df['PB'].iloc[198]
RSI = df['RSI'].iloc[-1]
RSI_L = df['RSI'].iloc[198]

그럼 저장한 해당 변수를 불러와서 if문을 이용해 비교문을 만들도록 하자.

coin_buy 함수는 들어온 coinname값을 넘겨주면서 구매 조건이 되었을 때 동작하도록 하고

coin_sell 함수는 반대로 구매 조건 달성시 동작하도록 작성을 했다.

 

이동평균선 20 이하 이면서 동시에 이동평균선 100 아래가 기본 조건

이후 볼린저 밴드가 BB_L이 0 이하, BB가 0 이상 일 때 이면서 동시에 RSI가 30 이상일 때로 잡았다.

직 BB_L을 이용해 직전 가격이 -이고 BB가 +일 때 즉 BB가 마이너스에서 플러스로 올라설 때 잡도록 했다.

그러면서도 RSI 역시 30 이상으로 올라와서 강도가 높을 때

풀어서 설명하면 하방 추세를 돌파하고 상승세로 돌아가려고 할 때 매수를 하도록 구성을 했다.

 

if now_data < ma20 and now_data < ma100:
    if BB > 0 and BB_L < 0 and RSI > 30:
            print("rootA / BB_L :", BB_L, " / BB :", BB, " / RSI :", RSI)
            self.coin_buy(coinname)
    elif RSI > 30 and RSI_L < 30 and BB > 0:
            print("rootB / RSI_L :", RSI_L, " / RSI :", RSI, " / BB :", BB)
            self.coin_buy(coinname)
    else:
        print("BB이하, 조건 미달성",coinname)

매도 조건은 elif로 추가해뒀다.

이평선 20 이상, 이평선 100 이상이면서

아까와는 정반대로 하방추세로 떨어지는 순간을 추적하도록 세팅했다.

elif now_data > ma20 and now_data > ma100:
    if BB < 1 and BB_L > 1 and RSI > 70:
            print("rootC / BB_L :", BB_L, " / BB :", BB, " / RSI :", RSI)
            print(df)
            self.coin_sell(coinname)
    elif RSI_L > 70 and RSI < 70 and BB > 1:
            print("rootD / RSI_L :", RSI_L, " / RSI :", RSI, " / BB :", BB)
            self.coin_sell(coinname)
    else:
        print("BB이상, 조건 미달성",coinname)
else:
    print("아직아님", coinname)

그리고 두 조건을 모두 달성하지 못하면 별도의 행동을 하지 않도록 세팅했다.

 

5. 매수 매도는요?

매수 함수는 coin_buy로 작성했고

upbit = pyupbit.Upbit(access_key, secret_key)

해당 구문을 통해 access_key와 secret_key를 넣어주면 내 지갑에 접근 가능해진다.

매수 기본금액을 30,000원으로 잡아뒀기 때문에 29,000원 이하일 경우 잔고가 없다는 문구가 나오도록 세팅했다.

그리고 돈이 있다면 30,000원어치를 매수하도록 세팅했다.

print(upbit.buy_market_order(coinname, 30000))

해당 구문이 입력되면 바로 매수가 이루어진다.

def coin_buy(self, coinname):
    access_key = "본인키"
    secret_key = "본인키"
    upbit = pyupbit.Upbit(access_key, secret_key)

    if upbit.get_balance("KRW") < 29000: #upbit.get_balance(coinname) > 0:
        print("더이상 잔고가 없다...", coinname)
    else:
        print("##############매수###############",coinname)
        print(upbit.buy_market_order(coinname, 30000))

매도 함수는 coin_sell로 처리했고

지갑을 불러오는 방식은 동일하다.

잔고가 있는지 여부를 먼저 확인하고 잔고가 없다면 잔고가 없다고 표현하고

있다면 전량 매도를 하도록 구성했다.

매수와 동일하지만 sell_count 변수를 만들어 현재 가진 코인 잔고를 확인하고 해당 잔고만큼 매도를 하도록 세팅했다.

def coin_sell(self, coinname):
    access_key = "본인키"
    secret_key = "본인키"
    upbit = pyupbit.Upbit(access_key, secret_key)

    if upbit.get_balance(coinname) <= 0:
        print("현재 잔고가 없는 코인입니다.", coinname)
    else:
        print("##############매도###############",coinname)
        sell_count = upbit.get_balance(coinname)
        print(upbit.sell_market_order(coinname, sell_count))

6. 조건에만 반응하면 손실폭이 커지지 않을까?

아무래도 매도 조건을 상승했을 때만 이루어지도록 하면 코 인장이 전체적으로 하락장일 때의 대응이 어려워진다.

그래서 내 잔고를 확인하고 3%의 손실이 난 코인은 매도를 하도록 세팅해놓겠다.

 

sell_position이 불러와지면 우선 나의 모든 종목을 불러와 my_data에 저장한다.

그리고 df_data에 평균 구매 가격을 불러와 테이블을 만든다.

이후 보유한 테이블 행수만큼 for 구문을 이용해서 반복하도록 한 후 보유 중인 코인 정보를 불러온다.

보유중인 코인 가격의 평균가를 second_data에 저장하고

현재 해당 코인의 3% 빠진 가격을 value_data에 저장한다.

 

value_data가 second_data보다 크다면 즉시 매도를 실행한다.

def sell_position(self, coinname):
    access_key = "본인키"
    secret_key = "본인키"
    upbit = pyupbit.Upbit(access_key, secret_key)

    my_data = upbit.get_balances()
    df_data = pd.DataFrame(my_data, columns=['currency', "avg_buy_price"])

    df_data['currency'] = 'KRW-' + df_data['currency']

    for i in df_data.index:
        if df_data['currency'].iloc[i] == coinname:
            value_data = float(df_data['avg_buy_price'].iloc[i]) * 0.97
            second_data = pyupbit.get_current_price([coinname])
            if value_data > second_data:
                self.coin_sell(coinname)
            else:
                print("아직 손해 아닙니다.", coinname)

다음에는 매 시간마다 자동으로 매매가 될 수 있도록 세팅했던 내용을 작성하겠습니다.