SEAN TEAM
Work · AUTOMATION --:-- 서울 · 로컬 실행 · 화면 없음 · 주문 코드 없음

화면 없이,
텔레그램 한 줄로
말하는 봇.

Upbit Bot

이 봇은 사고팔지 않습니다.시세를 읽고 판단한 뒤텔레그램에 한 줄을 보냅니다.

ABOUT · WHAT IT DOES

업비트에서 1시간 봉 30개와 현재가를 읽고, 최근 5개·20개 종가 평균과 RSI14 를 계산해 BUY·SELL·HOLD 를 정합니다. BUY 나 SELL 일 때만 텔레그램으로 마켓·가격·금액·이유를 보내고, HOLD 는 조용히 넘어갑니다. 주문을 내는 코드는 없습니다. DRY_RUN 은 기본 true 이고, 메시지 머리에 [DRY_RUN] 과 [LIVE] 중 무엇을 붙일지 정하는 데만 쓰입니다.

화면이 없는 제품이라, 이 페이지의 이미지는 전부 저장소의 실제 코드와 터미널 출력입니다. 거래소와 텔레그램에는 연결하지 않았고, 테스트의 가짜 시세로만 돌렸습니다.

01 — Terminal

464줄, 테스트 8개, 네트워크 없이.

저장소 복사본에서 실제로 돌린 터미널 출력 — wc -l 로 센 464줄, strategy.py 전문, unittest 8개 통과, 가짜 시세로 엔진을 돌려 찍은 [DRY_RUN] KRW-BTC BUY·SELL 메시지와 HOLD
  1. wc -l패키지 8개 파일, 실행 스크립트 2개, 테스트 4개 파일 — 합쳐 464줄.
  2. strategy.py 30줄추세와 RSI 가 겹칠 때만 BUY·SELL, 나머지는 HOLD.
  3. 검증용 스크립트 offline_cycle.py저장소 밖 검증용 스크립트. 테스트의 가짜 시세를 넣고, 전송 대신 메시지를 출력했습니다.
02 — One cycle

읽고, 정하고, 알립니다.

run_bot.py 는 이 사이클을 기본 60초마다 되풀이합니다. 판단과 알림은 테스트가 덮고, 시세를 읽는 부분은 테스트 밖에 있습니다.

  1. ① Prices

    1시간 봉 30개

    pyupbit 로 KRW-BTC 1시간 봉 30개와 현재가를 받아 최근 5개·20개 종가 평균과 RSI14 를 셉니다. 키가 있으면 원화와 코인 잔고도 읽습니다.

  2. ② Decision

    두 지표가 겹칠 때만

    단기 평균이 장기 평균 위이고 RSI 가 30 이하이며 원화가 5,000원 이상이면 BUY 입니다. 하락 추세에 RSI 70 이상이고 코인이 있으면 SELL. 그 밖은 신뢰도 0.3 의 HOLD 입니다.

  3. ③ Alert

    BUY·SELL 만 한 줄로

    판단이 BUY 나 SELL 이면 [DRY_RUN] 머리표와 마켓·가격·금액·이유를 보냅니다. 금액은 TRADE_KRW 기본 10,000원을 적을 뿐, 주문은 어디에서도 나가지 않습니다.

  4. ④ Tests

    여덟 개, 전부 가짜로

    전략 3개, 엔진 2개, 알림 1개, chat_id 2개입니다. 시세와 전송을 가짜 객체로 바꿔 네트워크 없이 돌고, 2026-09-25 에 돌려 8개 모두 통과했습니다.

upbit_client.py 10–21행 원문 — get_ohlcv 1시간 봉 30개, 5개·20개 평균, 현재가, RSI14, 잔고 조회
strategy.py 22–30행 원문 — decide_action 의 BUY·SELL·HOLD 조건
가짜 시세로 실제 엔진을 돌린 출력 — [DRY_RUN] KRW-BTC BUY 와 SELL 메시지 본문, HOLD 는 메시지 없음
python3 -m unittest discover -s tests -v 실제 출력 — 테스트 8개 모두 ok

코드 네 조각.

화면이 없으니 코드가 곧 표면입니다. 아래는 저장소 파일을 줄 번호 그대로 옮긴 것입니다.

settings.pypython-dotenv settings.py 22–32행 원문 — 환경변수와 기본값 기본값DRY_RUN=true, KRW-BTC, 60초, 10,000원. 키와 토큰은 환경변수로만 받습니다.
engine.py engine.py 34–42행 원문 — BUY 나 SELL 일 때만 send_trade_message 호출 HOLD 는 조용히BUY·SELL 일 때만 알림 함수를 부릅니다. 대부분의 사이클은 아무 메시지도 보내지 않습니다.
get_chat_id.pytelegram_api.py telegram_api.py 1–9행 원문 — extract_latest_chat_id chat_id 찾기봇에게 먼저 말을 건 뒤, getUpdates 응답에서 가장 최근 대화의 id 를 꺼냅니다. 없으면 NO_CHAT_ID 를 출력합니다.
run_bot.py run_bot.py 11–19행 원문 — 토큰, chat_id, 업비트 키가 없으면 SystemExit 빈 설정으로는 시작하지 않음텔레그램 토큰·chat_id·업비트 키 중 하나라도 비면 루프에 들어가기 전에 멈춥니다.

보내기 전에 MA5 > MA20RSI ≤ 30원화 ≥ 5,000원MA5 < MA20RSI ≥ 70코인 > 060초마다 세 조건이 모두 맞아야 합니다.

조건 하나라도 빠지면 HOLD 이고, HOLD 는 메시지를 만들지 않습니다.

01

주문 코드가 없습니다

이 저장소에는 매수·매도 주문 호출이 없습니다. DRY_RUN 은 메시지 머리표를 고르는 데만 쓰이고, README 도 지금은 알림 봇만 만들었다고 적습니다.

02

키는 코드 밖에

업비트 키와 봇 토큰은 .env 에서만 읽습니다. README 는 설치 안내보다 먼저, 토큰과 키를 README 나 채팅에 적지 말라고 경고합니다.

03

판단은 이유를 달고

전략은 행동만이 아니라 신뢰도와 한국어 이유를 함께 돌려줍니다. 텔레그램 메시지의 마지막 줄이 그 이유입니다.

04

테스트는 가짜로

엔진은 시세 클라이언트와 알림을, 알림은 전송 함수를 주입받습니다. 그래서 테스트가 거래소나 텔레그램에 닿지 않습니다.

03 — Counted from the repo · 2026-09-25
464줄의 Python · 테스트 포함
269줄 · 테스트 제외
8개의 테스트 · 모두 통과
60초 · 기본 폴링 주기
2개의 의존성 · pyupbit · python-dotenv
ROLE
단독 개발
LANGUAGE
Python · pyupbit · python-dotenv
OUTPUT
텔레그램 Bot API sendMessage — 화면 없음
DEFAULTS
DRY_RUN=true · KRW-BTC · 60초 · 10,000원
ORDERS
없음 — 알림만 보냅니다
TESTS
unittest 8개
STATUS
DRY RUN DEFAULT
WHY

만들면서 스스로에게
물은 것들.

왜 화면이 없나요?

출력이 텔레그램 메시지 한 통이기 때문입니다. 사람이 보는 곳은 휴대폰 알림이고, 그 알림을 언제 보낼지가 설계의 대부분입니다. 그래서 HOLD 는 보내지 않고, BUY·SELL 만 네 줄로 보냅니다.

DRY_RUN 을 끄면 실제로 사나요?

아닙니다. 이 저장소에는 주문을 내는 코드가 없습니다. DRY_RUN=false 로 바꾸면 메시지 머리표가 [LIVE] 로 바뀔 뿐입니다. 주문과 리스크 한도는 이후 StockAgent 에서 따로 만들었습니다.

텔레그램 전송이 실패하면요?

지금은 루프가 멈춥니다. 알림 함수도, 엔진도, run_bot.py 도 전송 예외를 잡지 않기 때문입니다. 나중에 만든 StockAgent 의 알림 모듈은 이 점을 바꿔, 전송 실패가 매매 흐름을 막지 않도록 예외를 삼킵니다.

chat_id 는 왜 따로 찾나요?

봇은 사람이 먼저 말을 걸기 전에는 그 사람에게 메시지를 보낼 수 없고, chat_id 도 그때 생깁니다. get_chat_id.py 는 getUpdates 응답에서 가장 최근 대화의 id 를 꺼내 출력하고, 없으면 NO_CHAT_ID 라고 적습니다.

이 페이지의 이미지는 무엇인가요?

전부 저장소의 실제 코드와, 2026-09-25 에 복사본에서 돌린 터미널 출력입니다. 거래소와 텔레그램에는 연결하지 않았습니다. 메시지 예시는 테스트 파일에 있는 가짜 시세 — BUY 는 1억 5천만원·RSI 28, SELL 은 가격 100·RSI 75 — 를 실제 엔진에 넣고, 전송 함수 대신 화면에 찍은 결과입니다.

프로젝트 전체 보기 →