이미지 로딩 중...

Python으로 알고리즘 트레이딩 봇 만들기 12편 - 텔레그램 알림 봇 구현 - 슬라이드 1/11
A

AI Generated

2025. 11. 12. · 3 Views

Python으로 알고리즘 트레이딩 봇 만들기 12편 - 텔레그램 알림 봇 구현

알고리즘 트레이딩 시스템에 텔레그램 봇을 연동하여 실시간 거래 알림을 받는 방법을 배웁니다. 매매 신호, 체결 내역, 수익률 현황을 카톡처럼 편리하게 받아보세요. 초보자도 쉽게 따라할 수 있는 실전 가이드입니다.


목차

  1. 텔레그램 봇 생성 및 API 토큰 발급 - BotFather로 나만의 알림 봇 만들기
  2. python-telegram-bot 라이브러리 설치 및 기본 설정 - 파이썬에서 텔레그램 API 사용하기
  3. 매매 신호 알림 기능 구현 - 매수/매도 시점을 실시간으로 받기
  4. 에러 및 예외 상황 알림 - 봇 장애를 즉시 감지하기
  5. 일일 수익률 리포트 전송 - 하루 매매 결과를 요약해서 받기
  6. 포지션 현황 조회 기능 - 보유 중인 종목을 실시간으로 확인하기
  7. 메시지 포맷팅과 이모지 활용 - 가독성 높은 알림 디자인하기
  8. 차트 이미지 전송 기능 - 수익률 그래프를 시각화해서 보내기
  9. 환경변수와 보안 관리 - 토큰을 안전하게 관리하는 방법
  10. 비동기 처리와 성능 최적화 - 알림이 매매를 방해하지 않게 하기

1. 텔레그램 봇 생성 및 API 토큰 발급 - BotFather로 나만의 알림 봇 만들기

시작하며

여러분이 알고리즘 트레이딩 봇을 운영할 때 이런 고민을 해본 적 있나요? "내 봇이 주식을 샀는지 팔았는지 실시간으로 알 수 있으면 좋을 텐데..." 매번 컴퓨터 앞에 앉아서 로그를 확인할 수는 없는 노릇입니다.

이런 문제는 특히 낮 시간에 회사에 있거나 외출 중일 때 심각해집니다. 중요한 매매 신호가 발생했는데 모르고 지나갔다면?

손실이 발생했는데 뒤늦게 알게 된다면? 실시간 모니터링이 불가능하다는 것은 자동매매의 큰 약점입니다.

바로 이럴 때 필요한 것이 텔레그램 봇입니다. 스마트폰으로 카톡 받듯이 실시간 알림을 받을 수 있어, 언제 어디서든 내 트레이딩 봇의 상태를 파악할 수 있습니다.

개요

간단히 말해서, 텔레그램 봇은 여러분의 프로그램이 텔레그램 메시지를 보낼 수 있게 해주는 인터페이스입니다. 실제 개발 환경에서 텔레그램 봇은 로깅, 알림, 모니터링 용도로 매우 자주 사용됩니다.

이메일보다 즉각적이고, SMS보다 비용이 저렴하며(무료!), 별도의 앱 개발 없이도 모바일 알림을 구현할 수 있기 때문입니다. 특히 트레이딩 봇처럼 실시간성이 중요한 시스템에서는 필수적인 도구입니다.

기존에는 서버에 로그를 남기고 주기적으로 확인했다면, 이제는 중요한 이벤트가 발생하는 즉시 스마트폰으로 알림을 받을 수 있습니다. 텔레그램 봇의 핵심 특징은 세 가지입니다.

첫째, BotFather라는 공식 봇을 통해 몇 분 만에 생성 가능합니다. 둘째, HTTP API를 제공하여 Python에서 간단하게 연동할 수 있습니다.

셋째, 무료이면서도 메시지 전송 제한이 매우 관대합니다(분당 30개). 이러한 특징들이 개발자들이 텔레그램을 알림 시스템으로 선호하는 이유입니다.

코드 예제

# 1. 텔레그램 앱에서 @BotFather 검색
# 2. /newbot 명령어 입력
# 3. 봇 이름 입력 (예: My Trading Alert Bot)
# 4. 봇 유저네임 입력 (예: my_trading_bot - 반드시 bot으로 끝나야 함)
# 5. 발급받은 토큰을 안전하게 저장

# 발급받은 토큰 예시
BOT_TOKEN = "123456789:ABCdefGHIjklMNOpqrsTUVwxyz"

# 6. 본인의 텔레그램 앱에서 생성한 봇을 검색
# 7. /start 명령어를 입력하여 봇 활성화
# 8. chat_id 확인을 위해 아래 URL을 브라우저에서 접속
# https://api.telegram.org/bot{BOT_TOKEN}/getUpdates

설명

이것이 하는 일: 텔레그램에서 공식 제공하는 BotFather를 통해 여러분만의 봇을 생성하고, 프로그램에서 사용할 수 있는 인증 토큰을 받아오는 과정입니다. 첫 번째로, 텔레그램 앱에서 @BotFather를 검색하여 대화를 시작합니다.

BotFather는 텔레그램이 공식적으로 제공하는 봇 관리 도구로, 여기서 모든 봇을 생성하고 관리할 수 있습니다. /newbot 명령어를 입력하면 대화형으로 봇 생성 프로세스가 시작됩니다.

그 다음으로, 봇의 이름과 유저네임을 설정합니다. 이름은 자유롭게 지을 수 있지만, 유저네임은 반드시 'bot'으로 끝나야 하며 전 세계적으로 고유해야 합니다.

예를 들어 "my_trading_bot"처럼 말이죠. 유저네임 설정이 완료되면 BotFather가 API 토큰을 발급해줍니다.

마지막으로, 발급받은 토큰은 "123456789:ABCdef..."처럼 숫자와 영문자의 조합으로 이루어져 있습니다. 이 토큰이 여러분의 프로그램이 봇으로 메시지를 보낼 수 있는 열쇠입니다.

토큰을 안전하게 보관하고, 절대 GitHub 같은 공개 저장소에 올리면 안 됩니다. 여러분이 이 과정을 완료하면 실제로 작동하는 텔레그램 봇 계정을 갖게 됩니다.

이 봇으로 무제한 메시지를 보낼 수 있고, 이미지나 파일도 전송 가능하며, 심지어 버튼이 있는 인터랙티브 메시지도 만들 수 있습니다. 추가 비용 없이 강력한 알림 시스템을 구축할 수 있는 것이죠.

실전 팁

💡 봇 토큰은 절대 코드에 직접 넣지 말고 환경변수나 .env 파일로 관리하세요. 실수로 GitHub에 올리면 누구나 여러분의 봇으로 메시지를 보낼 수 있습니다.

💡 chat_id를 확인할 때는 먼저 봇에게 /start 메시지를 보내야 합니다. 그래야 getUpdates API에서 여러분의 chat_id가 조회됩니다.

💡 봇 유저네임은 나중에 변경할 수 있지만, 토큰은 재발급 시 기존 토큰이 무효화되므로 신중하게 관리하세요.

💡 테스트용 봇과 실제 운영용 봇을 분리해서 만들면 개발 단계에서 실수로 운영 알림을 보내는 일을 방지할 수 있습니다.


2. python-telegram-bot 라이브러리 설치 및 기본 설정 - 파이썬에서 텔레그램 API 사용하기

시작하며

여러분이 텔레그램 API를 직접 호출하려고 할 때 이런 복잡함을 느낀 적 있나요? HTTP 요청을 만들고, JSON을 파싱하고, 에러 핸들링을 구현하고...

매번 이런 코드를 작성하는 것은 비효율적입니다. 이런 문제는 API를 처음 다루는 개발자에게 특히 어렵습니다.

requests 라이브러리로 직접 API를 호출할 수도 있지만, 타임아웃 처리, 재시도 로직, 레이트 리밋 관리 등 신경 써야 할 것들이 너무 많습니다. 바로 이럴 때 필요한 것이 python-telegram-bot 라이브러리입니다.

텔레그램 API의 모든 복잡한 부분을 추상화하여, 여러분은 비즈니스 로직에만 집중할 수 있게 해줍니다.

개요

간단히 말해서, python-telegram-bot은 텔레그램 봇 API를 Python에서 쉽게 사용할 수 있도록 만든 공식 래퍼 라이브러리입니다. 이 라이브러리가 필요한 이유는 명확합니다.

Raw HTTP API를 직접 다루면 메시지 전송 하나에도 URL 구성, 헤더 설정, 파라미터 인코딩 등 많은 코드가 필요합니다. 예를 들어, 이미지를 전송하면서 캡션을 달고 버튼을 추가하는 경우, 직접 구현하면 50줄이 넘는 코드가 필요하지만 이 라이브러리를 사용하면 5줄로 줄일 수 있습니다.

기존에는 requests.post()로 일일이 API 엔드포인트를 호출했다면, 이제는 bot.send_message()처럼 직관적인 메서드를 사용할 수 있습니다. 핵심 특징은 세 가지입니다.

첫째, 동기/비동기 방식 모두 지원하여 다양한 사용 환경에 대응합니다. 둘째, 자동 재시도 및 에러 핸들링이 내장되어 있어 안정적입니다.

셋째, 타입 힌트가 완벽하게 구현되어 있어 IDE의 자동완성이 잘 작동합니다. 이러한 특징들이 프로덕션 환경에서도 안심하고 사용할 수 있는 이유입니다.

코드 예제

# 라이브러리 설치 (터미널에서 실행)
# pip install python-telegram-bot

import os
from telegram import Bot
from telegram.error import TelegramError

# 환경변수로 관리하는 것이 보안상 안전합니다
BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
CHAT_ID = os.getenv("TELEGRAM_CHAT_ID")

# Bot 인스턴스 생성
bot = Bot(token=BOT_TOKEN)

# 간단한 메시지 전송 테스트
try:
    bot.send_message(chat_id=CHAT_ID, text="트레이딩 봇이 시작되었습니다! 🚀")
    print("메시지 전송 성공!")
except TelegramError as e:
    print(f"메시지 전송 실패: {e}")

설명

이것이 하는 일: 텔레그램 API를 Python에서 쉽게 사용할 수 있도록 라이브러리를 설치하고, 실제로 메시지를 보내는 기본 코드를 작성하는 과정입니다. 첫 번째로, pip를 통해 python-telegram-bot을 설치합니다.

이 라이브러리는 텔레그램의 공식 지원을 받는 파이썬 래퍼로, 가장 널리 사용되는 텔레그램 봇 라이브러리입니다. 설치 후에는 telegram 모듈을 import하여 사용할 수 있습니다.

그 다음으로, Bot 객체를 생성합니다. 여기서 중요한 점은 토큰과 chat_id를 환경변수로 관리한다는 것입니다.

os.getenv()를 사용하면 코드에 민감한 정보를 직접 넣지 않아도 됩니다. .env 파일이나 시스템 환경변수로 관리하면 보안이 강화되고, 여러 환경(개발/운영)에서 다른 설정을 쉽게 적용할 수 있습니다.

마지막으로, send_message() 메서드로 실제 메시지를 전송합니다. try-except 블록으로 감싼 이유는 네트워크 오류, 잘못된 토큰, 차단된 봇 등 다양한 에러 상황을 처리하기 위함입니다.

TelegramError를 캐치하면 어떤 문제가 발생했는지 정확히 알 수 있어 디버깅이 쉬워집니다. 여러분이 이 코드를 실행하면 스마트폰 텔레그램 앱에서 즉시 알림을 받게 됩니다.

이것이 트레이딩 봇의 모든 중요한 이벤트를 실시간으로 받아볼 수 있는 기반이 됩니다. 이제 매매 체결, 에러 발생, 수익률 변동 등 모든 정보를 언제 어디서든 확인할 수 있습니다.

실전 팁

💡 환경변수 설정은 python-dotenv 라이브러리를 사용하면 편리합니다. .env 파일에 토큰을 저장하고 load_dotenv()로 불러오세요.

💡 비동기 프로그램에서는 Bot 대신 AsyncBot을 사용해야 합니다. await bot.send_message() 형태로 사용하면 됩니다.

💡 메시지 전송 실패 시 자동 재시도를 구현하려면 tenacity 라이브러리와 함께 사용하는 것을 추천합니다.

💡 테스트 시에는 먼저 본인의 개인 chat_id로 보내보고, 정상 작동을 확인한 후 그룹 채팅으로 확장하세요.


3. 매매 신호 알림 기능 구현 - 매수/매도 시점을 실시간으로 받기

시작하며

여러분이 알고리즘 트레이딩 봇을 운영할 때 가장 궁금한 것이 무엇인가요? "지금 내 봇이 뭘 사고 파는지" 실시간으로 알고 싶지 않나요?

특히 점심 먹으러 나간 사이에 큰 매매가 일어났다면 불안합니다. 이런 문제는 특히 변동성이 큰 시장에서 심각합니다.

골든크로스나 RSI 과매도 신호 같은 중요한 매매 신호가 발생했는데, 나중에 로그를 확인하면서 "아, 이때 샀구나"라고 뒤늦게 아는 것은 트레이더로서 매우 불안한 경험입니다. 바로 이럴 때 필요한 것이 매매 신호 알림입니다.

봇이 매수/매도 결정을 내리는 순간, 그 정보가 즉시 여러분의 손 안으로 전달됩니다.

개요

간단히 말해서, 매매 신호 알림은 트레이딩 봇의 매수/매도 로직이 실행될 때 텔레그램으로 즉시 메시지를 보내는 기능입니다. 이 기능이 필요한 이유는 트레이딩의 투명성과 통제력 때문입니다.

자동매매는 편리하지만, 내가 모르는 사이에 내 돈이 움직인다는 것은 불안감을 줍니다. 실시간 알림을 받으면 봇의 행동을 모니터링하고, 필요시 즉시 개입할 수 있습니다.

예를 들어, 봇이 예상치 못한 종목을 매수했다면 즉시 로직을 점검하거나 수동으로 매도할 수 있습니다. 기존에는 거래소 API의 체결 내역을 주기적으로 조회했다면, 이제는 매매 로직 안에 알림 코드를 삽입하여 의사결정 시점에 즉시 통보받을 수 있습니다.

핵심 특징은 매매 신호의 맥락 정보를 함께 전달하는 것입니다. 단순히 "삼성전자 매수"가 아니라, "RSI 28 과매도 신호로 삼성전자 10주 매수, 평균단가 70,000원"처럼 구체적인 정보를 제공합니다.

이렇게 하면 왜 그 매매가 일어났는지 즉시 이해할 수 있고, 로직의 문제점도 빠르게 발견할 수 있습니다.

코드 예제

def send_trading_signal(signal_type, ticker, quantity, price, reason):
    """매매 신호를 텔레그램으로 전송하는 함수"""

    # 매수/매도에 따라 이모지와 메시지 스타일 변경
    emoji = "🔴" if signal_type == "BUY" else "🔵"
    action = "매수" if signal_type == "BUY" else "매도"

    # 가격은 천 단위 콤마로 포맷팅
    formatted_price = f"{price:,.0f}"
    total_amount = f"{price * quantity:,.0f}"

    message = f"""
{emoji} {action} 신호 발생!

종목: {ticker}
수량: {quantity}주
가격: {formatted_price}원
총액: {total_amount}원
사유: {reason}

시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""

    try:
        bot.send_message(chat_id=CHAT_ID, text=message)
    except TelegramError as e:
        # 알림 전송 실패는 로그에 남기지만 매매는 계속 진행
        print(f"텔레그램 알림 실패: {e}")

# 실제 매매 로직에서 호출하는 예시
if rsi < 30:  # RSI 과매도 신호
    buy_stock("AAPL", 10, current_price)
    send_trading_signal("BUY", "AAPL", 10, current_price, "RSI 과매도(28)")

설명

이것이 하는 일: 트레이딩 봇이 매수나 매도 결정을 내릴 때, 그 정보를 구조화된 형태로 텔레그램 메시지로 전송하는 함수입니다. 첫 번째로, 매수/매도 타입에 따라 메시지 스타일을 다르게 구성합니다.

빨간 원(🔴)은 매수, 파란 원(🔵)은 매도로 시각적으로 구분하면 메시지를 받자마자 직관적으로 파악할 수 있습니다. 이모지를 사용하는 이유는 스마트폰 알림 미리보기에서도 한눈에 들어오기 때문입니다.

그 다음으로, 가격 정보를 사람이 읽기 쉬운 형태로 포맷팅합니다. f"{price:,.0f}"는 70000을 "70,000"으로 변환해줍니다.

천 단위 콤마가 있으면 큰 금액도 즉시 파악할 수 있습니다. 또한 총 매매금액도 계산하여 표시하므로, 이번 거래가 포트폴리오에 미치는 영향을 바로 알 수 있습니다.

마지막으로, 매매 사유(reason)를 반드시 포함시킵니다. "RSI 과매도(28)", "골든크로스 발생", "손절매(목표가 도달)" 같은 정보가 있으면 봇의 의사결정 과정을 이해할 수 있습니다.

시간 정보도 초 단위까지 기록하여 나중에 차트와 대조할 때 유용합니다. 여러분이 이 함수를 매매 로직에 통합하면, 봇이 거래할 때마다 자동으로 알림을 받게 됩니다.

회사에서 일하다가도, 운동하다가도, 스마트폰으로 즉시 확인할 수 있습니다. 중요한 점은 알림 전송 실패가 실제 매매를 방해해서는 안 되므로, try-except로 감싸서 알림 실패 시에도 매매는 계속 진행되도록 했다는 것입니다.

실전 팁

💡 메시지에 거래소 링크를 포함시키면 클릭 한 번으로 해당 종목 차트를 바로 확인할 수 있습니다. 예: https://finance.naver.com/item/main.nhn?code={code}

💡 중요한 매매(예: 100만 원 이상)는 별도의 그룹 채팅에 추가로 전송하거나, 메시지 앞에 "⚠️ 대량 거래" 같은 경고를 추가하세요.

💡 하루에 매매가 너무 많으면 알림 피로도가 생깁니다. 일정 금액 이상만 알림을 보내거나, 요약 메시지를 주기적으로 보내는 방식도 고려하세요.

💡 백테스팅 모드에서는 알림을 보내지 않도록 환경변수나 플래그로 제어하세요. 실수로 과거 데이터로 수백 개 알림이 날아가는 것을 방지할 수 있습니다.


4. 에러 및 예외 상황 알림 - 봇 장애를 즉시 감지하기

시작하며

여러분의 트레이딩 봇이 밤새 돌아가다가 에러로 멈췄다면 어떻게 되나요? 아침에 일어나서 확인하기 전까지는 알 수 없습니다.

그 사이에 중요한 매매 기회를 놓쳤을 수도 있습니다. 이런 문제는 실제로 자주 발생합니다.

API 타임아웃, 네트워크 오류, 거래소 점검, 잔고 부족 등 다양한 이유로 봇이 중단될 수 있습니다. 문제는 이런 상황을 즉시 알지 못하면 손실이 커진다는 것입니다.

바로 이럴 때 필요한 것이 에러 알림 시스템입니다. 봇에 문제가 생기는 순간, 여러분의 스마트폰으로 긴급 알림이 날아옵니다.

개요

간단히 말해서, 에러 알림은 트레이딩 봇에서 예외나 오류가 발생했을 때 자동으로 텔레그램 메시지를 보내는 안전장치입니다. 이 기능이 필요한 이유는 무인 운영 시스템의 안정성 때문입니다.

사람이 24시간 모니터링할 수 없기 때문에, 시스템 스스로 문제를 감지하고 알려야 합니다. 예를 들어, 거래소 API가 갑자기 응답하지 않으면 봇은 매매를 할 수 없는데, 이를 모른 채 하루를 보내면 큰 기회 손실입니다.

에러 알림이 있으면 5분 안에 문제를 인지하고 대응할 수 있습니다. 기존에는 로그 파일에 에러를 기록하고 주기적으로 확인했다면, 이제는 심각한 에러가 발생하는 즉시 푸시 알림으로 받을 수 있습니다.

핵심 특징은 에러의 심각도를 구분하여 알림하는 것입니다. 모든 에러를 다 알릴 필요는 없습니다.

일시적인 네트워크 재시도는 로그에만 남기고, 봇이 완전히 멈추거나 잔고가 부족한 상황처럼 사람의 개입이 필요한 경우만 알림을 보냅니다. 또한 에러 메시지에 스택 트레이스와 함께 어떤 상황에서 발생했는지 컨텍스트 정보를 포함시켜, 알림만 보고도 문제를 파악할 수 있게 합니다.

코드 예제

import traceback
from datetime import datetime

def send_error_alert(error_type, error_message, context=None):
    """에러 발생 시 긴급 알림을 전송하는 함수"""

    # 에러 타입별로 이모지와 우선순위 설정
    error_emojis = {
        "CRITICAL": "🚨",  # 즉시 대응 필요
        "ERROR": "⚠️",      # 빠른 확인 필요
        "WARNING": "⚡"     # 주의 필요
    }

    emoji = error_emojis.get(error_type, "❓")

    # 에러 메시지 구성
    message = f"""
{emoji} {error_type} 발생!

오류 내용: {error_message}
"""

    # 컨텍스트 정보가 있으면 추가
    if context:
        message += f"\n상황: {context}\n"

    # 스택 트레이스 추가 (CRITICAL인 경우)
    if error_type == "CRITICAL":
        stack_trace = traceback.format_exc()
        # 너무 길면 마지막 500자만 전송
        message += f"\n스택 트레이스:\n{stack_trace[-500:]}"

    message += f"\n발생 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"

    try:
        bot.send_message(chat_id=CHAT_ID, text=message, parse_mode='Markdown')
    except Exception as e:
        # 알림 전송조차 실패하면 로컬 로그에 기록
        print(f"긴급: 알림 전송 실패 - {e}\n원본 에러: {error_message}")

# 실제 사용 예시
try:
    balance = exchange_api.get_balance()
    if balance < required_amount:
        send_error_alert("CRITICAL",
                        "잔고 부족으로 매수 불가",
                        f"필요 금액: {required_amount}, 현재 잔고: {balance}")
except ConnectionError as e:
    send_error_alert("ERROR",
                    f"거래소 API 연결 실패: {str(e)}",
                    "자동 재시도 중...")

설명

이것이 하는 일: 트레이딩 봇에서 발생하는 다양한 에러 상황을 감지하고, 심각도에 따라 적절한 형태의 알림 메시지를 구성하여 전송하는 함수입니다. 첫 번째로, 에러를 세 가지 레벨로 분류합니다.

CRITICAL(🚨)은 봇이 멈춘 상황으로 즉시 조치가 필요합니다. ERROR(⚠️)는 기능 일부가 작동하지 않지만 봇은 계속 돌아가는 상황입니다.

WARNING(⚡)은 주의가 필요하지만 큰 문제는 아닌 상황입니다. 이렇게 구분하면 알림을 받았을 때 얼마나 긴급한지 즉시 판단할 수 있습니다.

그 다음으로, 에러 메시지에 컨텍스트 정보를 추가합니다. 단순히 "API 호출 실패"가 아니라 "잔고 조회 중 API 호출 실패, 현재 재시도 2/5"처럼 상황을 구체적으로 알려줍니다.

이 정보가 있으면 원격에서도 문제를 진단하고, 즉시 개입할지 자동 복구를 기다릴지 판단할 수 있습니다. 마지막으로, CRITICAL 레벨 에러는 스택 트레이스를 포함시킵니다.

traceback.format_exc()로 에러가 발생한 정확한 코드 위치를 파악할 수 있습니다. 다만 스택 트레이스가 너무 길면 텔레그램 메시지 길이 제한(4096자)에 걸릴 수 있으므로, 마지막 500자만 전송합니다.

전체 로그는 서버의 로그 파일에 저장되어 있으므로 나중에 확인할 수 있습니다. 여러분이 이 에러 알림 시스템을 구축하면, 봇이 문제를 겪는 순간 즉시 알 수 있습니다.

밤에 자고 있어도 스마트폰 알림음으로 깨어날 수 있고(중요한 봇이라면), 출근길에 문제를 파악하고 회사에서 원격으로 조치할 수 있습니다. 무인 시스템의 신뢰성을 크게 높이는 핵심 기능입니다.

실전 팁

💡 같은 에러가 1분에 10번씩 발생하면 알림 폭탄이 됩니다. 같은 에러는 10분에 한 번만 알림을 보내도록 쿨다운 로직을 추가하세요.

💡 심각한 에러는 텔레그램뿐만 아니라 이메일이나 SMS로도 중복 전송하면 더 안전합니다. 텔레그램 서버 자체에 문제가 있을 수도 있으니까요.

💡 에러 메시지에 해결 방법 링크를 추가하세요. 예: "API 오류 해결법: https://wiki.mycompany.com/api-troubleshooting"

💡 주말이나 휴가 중에는 알림 레벨을 조정할 수 있도록 설정 파일로 관리하세요. WARNING은 끄고 CRITICAL만 받을 수 있게 하는 식으로요.


5. 일일 수익률 리포트 전송 - 하루 매매 결과를 요약해서 받기

시작하며

여러분이 하루가 끝날 때 가장 궁금한 것이 무엇인가요? "오늘 내 봇이 얼마를 벌었나?" 일 겁니다.

매번 거래소에 로그인해서 확인하는 것은 번거롭습니다. 이런 문제는 특히 여러 계좌나 여러 전략을 동시에 운영할 때 심각합니다.

각 계좌에 로그인하고, 거래 내역을 확인하고, 수익률을 계산하는 데 10분 이상 걸릴 수 있습니다. 매일 이렇게 하는 것은 비효율적입니다.

바로 이럴 때 필요한 것이 자동화된 일일 리포트입니다. 매일 저녁 정해진 시간에 오늘의 매매 결과, 수익률, 보유 종목 현황이 자동으로 정리되어 날아옵니다.

개요

간단히 말해서, 일일 리포트는 하루 동안의 거래 내역과 수익률을 자동으로 집계하여 정해진 시간에 텔레그램으로 전송하는 기능입니다. 이 기능이 필요한 이유는 체계적인 성과 관리 때문입니다.

트레이딩에서 가장 중요한 것은 전략의 성과를 꾸준히 추적하는 것입니다. 일일 리포트가 있으면 매일 저녁 5분 안에 오늘의 성과를 파악하고, 전략이 제대로 작동하는지 점검할 수 있습니다.

예를 들어, 수익률이 갑자기 떨어졌다면 시장 환경이 바뀌었거나 전략에 문제가 생긴 것이므로 즉시 점검할 수 있습니다. 기존에는 수동으로 엑셀에 데이터를 정리했다면, 이제는 봇이 자동으로 집계하고 시각화까지 해서 보여줍니다.

핵심 특징은 핵심 지표를 한눈에 파악할 수 있도록 구조화하는 것입니다. 총 수익률, 오늘의 수익, 거래 횟수, 승률, 최고 수익 종목, 최고 손실 종목 등 중요한 정보를 상단에 배치합니다.

또한 전일 대비 변화를 표시하여 추세를 파악할 수 있게 합니다. "어제보다 +2.3%p" 같은 정보가 있으면 전략이 개선되고 있는지 악화되고 있는지 즉시 알 수 있습니다.

코드 예제

from datetime import datetime, time
import schedule

def generate_daily_report(trading_log, account_balance):
    """일일 매매 리포트를 생성하고 전송하는 함수"""

    # 오늘 거래 데이터 집계
    today_trades = [t for t in trading_log if t['date'] == datetime.now().date()]

    # 핵심 지표 계산
    total_trades = len(today_trades)
    winning_trades = len([t for t in today_trades if t['profit'] > 0])
    win_rate = (winning_trades / total_trades * 100) if total_trades > 0 else 0

    today_profit = sum(t['profit'] for t in today_trades)
    today_return = (today_profit / account_balance['start_of_day']) * 100

    # 최고/최저 수익 종목
    best_trade = max(today_trades, key=lambda x: x['profit'], default=None)
    worst_trade = min(today_trades, key=lambda x: x['profit'], default=None)

    # 리포트 메시지 작성
    message = f"""
📊 일일 트레이딩 리포트
{'='*30}

💰 수익 현황
• 오늘 수익: {today_profit:+,.0f}원
• 수익률: {today_return:+.2f}%
• 현재 잔고: {account_balance['current']:,.0f}원

📈 거래 통계
• 총 거래: {total_trades}건
• 승리: {winning_trades}건 | 패배: {total_trades - winning_trades}건
• 승률: {win_rate:.1f}%

🏆 베스트 트레이드
{best_trade['ticker'] if best_trade else 'N/A'}: {best_trade['profit']:+,.0f}원

⚠️ 워스트 트레이드
{worst_trade['ticker'] if worst_trade else 'N/A'}: {worst_trade['profit']:+,.0f}원

⏰ 리포트 생성: {datetime.now().strftime('%Y-%m-%d %H:%M')}
"""

    bot.send_message(chat_id=CHAT_ID, text=message)

# 매일 저녁 6시에 리포트 전송 (스케줄러 사용)
schedule.every().day.at("18:00").do(lambda: generate_daily_report(trading_log, account))

설명

이것이 하는 일: 트레이딩 봇의 하루 활동을 집계하여 핵심 성과 지표를 계산하고, 읽기 쉬운 형태로 정리하여 자동으로 전송하는 리포팅 시스템입니다. 첫 번째로, 오늘의 거래 데이터를 필터링하고 집계합니다.

trading_log에서 오늘 날짜의 거래만 추출하여, 총 거래 횟수, 수익 거래 수, 승률 등을 계산합니다. 승률은 단순히 성공한 거래 수를 전체 거래 수로 나눈 것으로, 전략의 정확도를 나타내는 중요한 지표입니다.

승률이 50% 이상이면 전략이 긍정적으로 작동하고 있다는 의미입니다. 그 다음으로, 수익 정보를 계산합니다.

오늘의 총 수익을 금액과 퍼센트 두 가지로 표시합니다. 금액은 절대적인 성과를, 퍼센트는 상대적인 성과를 보여줍니다.

예를 들어 100만 원을 벌었다면 좋은 것 같지만, 1억 계좌에서는 1%에 불과하므로 두 관점이 모두 필요합니다. f"{today_profit:+,.0f}"의 +:부호는 양수일 때 +를 표시하여 수익/손실을 명확히 구분합니다.

마지막으로, 스케줄러를 사용하여 매일 자동으로 실행되게 합니다. schedule 라이브러리는 cron보다 간단하면서도 파이썬 코드 안에서 관리할 수 있어 편리합니다.

"18:00"에 리포트를 보내도록 설정했지만, 여러분의 생활 패턴에 맞춰 시간을 조정할 수 있습니다. 장 마감 후 30분 뒤인 15:30에 보내거나, 퇴근 직후인 19:00에 보낼 수도 있습니다.

여러분이 이 리포트 시스템을 구축하면, 매일 저녁 편하게 누워서 스마트폰으로 오늘의 성과를 확인할 수 있습니다. 별도로 거래소에 로그인할 필요 없이, 텔레그램 하나로 모든 정보를 파악할 수 있습니다.

이렇게 꾸준히 성과를 추적하면 장기적으로 전략을 개선하는 데 큰 도움이 됩니다.

실전 팁

💡 주말에는 거래가 없으므로 리포트를 보내지 않도록 schedule에 조건을 추가하세요: if datetime.now().weekday() < 5:

💡 월말에는 월간 리포트를, 연말에는 연간 리포트를 자동으로 생성하도록 확장할 수 있습니다. 장기 성과 추적에 매우 유용합니다.

💡 리포트에 간단한 차트 이미지를 첨부하면 시각적으로 더 이해하기 쉽습니다. matplotlib로 수익률 그래프를 그려서 bot.send_photo()로 전송하세요.

💡 여러 전략을 운영한다면 전략별로 분리된 리포트를 보내거나, 하나의 리포트에 전략별 섹션을 만드세요.


6. 포지션 현황 조회 기능 - 보유 중인 종목을 실시간으로 확인하기

시작하며

여러분이 외출 중에 갑자기 궁금해진 적 있나요? "지금 내 봇이 어떤 주식을 들고 있지?" 노트북을 켤 수 없는 상황에서 포지션을 확인하고 싶을 때가 있습니다.

이런 문제는 특히 뉴스를 봤을 때 심각합니다. "삼성전자 악재 발생"이라는 뉴스를 봤는데, 내 봇이 삼성전자를 보유 중인지 아닌지 모르면 불안합니다.

집에 가서 확인하기 전까지 몇 시간 동안 걱정해야 합니다. 바로 이럴 때 필요한 것이 텔레그램 명령어를 통한 포지션 조회입니다.

봇에게 "/포지션"이라고 메시지를 보내면 즉시 현재 보유 종목과 수익률을 알려줍니다.

개요

간단히 말해서, 포지션 조회 기능은 사용자가 텔레그램에서 명령어를 보내면 봇이 현재 보유 중인 종목 정보를 실시간으로 응답해주는 양방향 소통 기능입니다. 이 기능이 필요한 이유는 온디맨드 정보 접근성 때문입니다.

정기적인 리포트도 좋지만, 내가 원하는 순간에 정보를 조회할 수 있어야 합니다. 예를 들어, 점심시간에 경제 뉴스를 보다가 특정 종목에 대한 소식을 접했을 때, 즉시 내 포지션을 확인하고 필요하면 수동 개입을 결정할 수 있습니다.

스마트폰만 있으면 언제 어디서든 조회 가능합니다. 기존에는 웹 대시보드를 만들거나 SSH로 서버에 접속해야 했다면, 이제는 텔레그램이라는 친숙한 인터페이스로 간편하게 조회할 수 있습니다.

핵심 특징은 명령어 기반 인터랙션입니다. /포지션, /수익률, /상태 같은 직관적인 명령어를 정의하면, 마치 봇과 대화하듯이 정보를 주고받을 수 있습니다.

또한 실시간 가격을 반영하여 현재 평가손익을 표시하므로, 매수가와 현재가를 비교하여 각 종목의 수익률을 즉시 파악할 수 있습니다.

코드 예제

from telegram.ext import Updater, CommandHandler

def get_current_positions(portfolio):
    """현재 보유 포지션을 조회하는 함수"""

    if not portfolio or len(portfolio) == 0:
        return "현재 보유 중인 포지션이 없습니다. 💰"

    message = "📋 현재 보유 포지션\n" + "="*30 + "\n\n"

    total_invested = 0
    total_current_value = 0

    for position in portfolio:
        ticker = position['ticker']
        quantity = position['quantity']
        avg_price = position['avg_price']
        current_price = get_realtime_price(ticker)  # 실시간 가격 조회

        invested = avg_price * quantity
        current_value = current_price * quantity
        profit = current_value - invested
        profit_rate = (profit / invested) * 100

        # 수익/손실에 따라 이모지 변경
        emoji = "🔴" if profit >= 0 else "🔵"

        message += f"{emoji} {ticker}\n"
        message += f"  수량: {quantity}주\n"
        message += f"  매수가: {avg_price:,.0f}원\n"
        message += f"  현재가: {current_price:,.0f}원\n"
        message += f"  손익: {profit:+,.0f}원 ({profit_rate:+.2f}%)\n\n"

        total_invested += invested
        total_current_value += current_value

    # 전체 포트폴리오 수익률 계산
    total_profit = total_current_value - total_invested
    total_profit_rate = (total_profit / total_invested) * 100 if total_invested > 0 else 0

    message += f"{'='*30}\n"
    message += f"💼 포트폴리오 총계\n"
    message += f"  투자금: {total_invested:,.0f}원\n"
    message += f"  평가액: {total_current_value:,.0f}원\n"
    message += f"  손익: {total_profit:+,.0f}원 ({total_profit_rate:+.2f}%)\n"

    return message

def position_command(update, context):
    """텔레그램에서 /포지션 명령어 핸들러"""
    message = get_current_positions(current_portfolio)
    update.message.reply_text(message)

# 텔레그램 봇 업데이터 설정
updater = Updater(token=BOT_TOKEN, use_context=True)
dispatcher = updater.dispatcher

# 명령어 핸들러 등록
dispatcher.add_handler(CommandHandler("포지션", position_command))
dispatcher.add_handler(CommandHandler("position", position_command))

# 봇 시작 (폴링 방식)
updater.start_polling()

설명

이것이 하는 일: 사용자가 텔레그램에서 특정 명령어를 입력하면, 봇이 그 명령을 인식하고 현재 포트폴리오 상태를 조회하여 응답 메시지로 보내주는 양방향 대화형 시스템입니다. 첫 번째로, 포트폴리오 데이터를 순회하며 각 포지션의 정보를 집계합니다.

중요한 점은 get_realtime_price() 함수로 현재가를 실시간으로 조회한다는 것입니다. 저장된 매수가와 현재가를 비교하여 평가손익을 계산하므로, 조회하는 바로 그 순간의 수익률을 정확히 파악할 수 있습니다.

장중이라면 분 단위로 변하는 가격이 반영됩니다. 그 다음으로, 각 종목의 정보를 보기 좋게 포맷팅합니다.

종목별로 수량, 매수가, 현재가, 손익을 표시하되, 수익인 경우 빨간 원(🔴), 손실인 경우 파란 원(🔵)으로 시각적으로 구분합니다. 또한 +/- 기호와 함께 표시하여 한눈에 상황을 파악할 수 있게 합니다.

마지막으로, Updater와 CommandHandler로 명령어를 등록합니다. CommandHandler("포지션", position_command)는 사용자가 "/포지션"이라고 입력하면 position_command 함수를 실행하라는 의미입니다.

updater.start_polling()은 텔레그램 서버를 주기적으로 확인하여 새 메시지가 있는지 폴링합니다. 이렇게 하면 봇이 24시간 대기하면서 사용자의 명령을 기다립니다.

여러분이 이 기능을 구현하면, 스마트폰으로 언제든지 포지션을 조회할 수 있습니다. 회의 중에 궁금해지면 화장실에 가서 확인할 수도 있고, 친구와 밥 먹다가 주식 이야기가 나오면 즉시 내 포지션을 보여줄 수도 있습니다.

정보 접근성이 극대화되는 것이죠.

실전 팁

💡 민감한 정보이므로 반드시 chat_id를 검증하세요. if update.message.chat_id != AUTHORIZED_CHAT_ID: return 같은 로직으로 본인만 조회 가능하게 만드세요.

💡 /매수 <종목> <수량> 같은 수동 명령어도 추가하면 외출 중에도 긴급 매매가 가능합니다. 다만 보안에 매우 주의해야 합니다.

💡 포지션이 많으면 메시지가 너무 길어집니다. 상위 5개만 표시하고 "더보기"는 파일로 전송하는 방식을 고려하세요.

💡 조회 명령어에 대한 응답 시간을 측정하여 "조회 완료 (0.5초)" 같은 정보를 추가하면 시스템 성능을 모니터링할 수 있습니다.


7. 메시지 포맷팅과 이모지 활용 - 가독성 높은 알림 디자인하기

시작하며

여러분이 텔레그램 알림을 받았을 때 이런 경험 해본 적 있나요? 텍스트만 빽빽하게 나열되어 있어서 어디가 중요한 내용인지 파악하기 어려운 경우 말이죠.

이런 문제는 특히 긴급 상황에서 심각합니다. 급하게 알림을 확인했는데 핵심 정보를 찾는 데 시간이 걸린다면, 알림의 의미가 퇴색됩니다.

"이게 매수야 매도야?" 같은 기본적인 정보조차 한눈에 들어오지 않으면 스트레스를 받습니다. 바로 이럴 때 필요한 것이 체계적인 메시지 포맷팅입니다.

마크다운 문법과 이모지를 활용하면 같은 정보도 훨씬 읽기 쉽고 직관적으로 전달할 수 있습니다.

개요

간단히 말해서, 메시지 포맷팅은 텔레그램의 마크다운 기능과 이모지를 활용하여 알림 메시지를 시각적으로 구조화하는 기술입니다. 이 기능이 필요한 이유는 정보의 효율적인 전달 때문입니다.

사람은 시각적 단서에 매우 민감합니다. 볼드체, 이모지, 줄바꿈, 구분선 같은 요소를 적절히 사용하면 같은 내용도 이해 속도가 3배 이상 빨라집니다.

예를 들어, 중요한 숫자는 볼드로 강조하고, 성공/실패는 색상 이모지로 구분하면 메시지를 읽지 않고도 대략적인 상황을 파악할 수 있습니다. 기존에는 단순한 텍스트 메시지만 보냈다면, 이제는 표처럼 정렬되고 섹션으로 나뉘며 시각적 요소가 풍부한 전문적인 알림을 보낼 수 있습니다.

핵심 특징은 일관된 디자인 시스템입니다. 매수는 항상 🔴, 매도는 🔵, 에러는 🚨처럼 규칙을 정해두면 알림을 받는 순간 이모지만 봐도 무슨 내용인지 예상할 수 있습니다.

또한 정보의 위계를 명확히 하여, 가장 중요한 정보는 상단에 크고 굵게, 부가 정보는 하단에 작게 배치합니다.

코드 예제

def format_trade_message(trade_type, ticker, quantity, price, profit=None):
    """마크다운과 이모지를 활용한 전문적인 메시지 포맷팅"""

    # 타입별 이모지와 제목 설정
    if trade_type == "BUY":
        emoji = "🔴"
        title = "*매수 체결*"
        action = "매수"
    elif trade_type == "SELL":
        emoji = "🔵"
        title = "*매도 체결*"
        action = "매도"

    # 핵심 정보는 볼드체로 강조
    message = f"{emoji} {title}\n"
    message += f"{'─' * 25}\n\n"  # 구분선

    # 종목 정보 (가장 중요하므로 볼드)
    message += f"📊 *{ticker}*\n"
    message += f"수량: `{quantity:,}주`\n"
    message += f"가격: `{price:,.0f}원`\n"

    # 총액 계산 및 표시
    total = quantity * price
    message += f"총액: *{total:,.0f}원*\n\n"

    # 매도의 경우 수익 정보 추가
    if trade_type == "SELL" and profit is not None:
        profit_emoji = "💰" if profit >= 0 else "📉"
        message += f"{profit_emoji} 손익: *{profit:+,.0f}원*\n"
        message += f"수익률: *{(profit/total*100):+.2f}%*\n\n"

    # 시간 정보 (작은 글씨로)
    timestamp = datetime.now().strftime('%H:%M:%S')
    message += f"{'─' * 25}\n"
    message += f"⏰ {timestamp}"

    return message

# 실제 사용 예시
buy_msg = format_trade_message("BUY", "삼성전자", 10, 70000)
bot.send_message(
    chat_id=CHAT_ID,
    text=buy_msg,
    parse_mode='Markdown'  # 마크다운 활성화
)

# HTML 포맷도 사용 가능 (더 많은 스타일링 옵션)
html_msg = f"""
<b>🔴 매수 체결</b>
─────────────────

📊 <b>삼성전자</b>
수량: <code>10주</code>
가격: <code>70,000원</code>

⏰ {datetime.now().strftime('%H:%M:%S')}
"""
bot.send_message(chat_id=CHAT_ID, text=html_msg, parse_mode='HTML')

설명

이것이 하는 일: 텔레그램이 지원하는 포맷팅 기능을 최대한 활용하여, 단순한 텍스트를 읽기 쉽고 전문적인 형태의 메시지로 변환하는 과정입니다. 첫 번째로, 이모지를 전략적으로 사용합니다.

🔴/🔵는 매수/매도를, 💰/📉는 수익/손실을 즉시 연상시킵니다. 이모지는 언어의 장벽을 넘어서는 보편적인 기호이므로, 피곤할 때나 졸릴 때도 직관적으로 이해할 수 있습니다.

또한 스마트폰 잠금 화면에서 알림 미리보기로 이모지만 보고도 대략적인 내용을 파악할 수 있습니다. 그 다음으로, 마크다운 문법으로 텍스트를 강조합니다.

텍스트는 볼드체로, 텍스트는 고정폭 폰트로 표시됩니다. 종목명이나 금액 같은 핵심 정보는 볼드로 강조하고, 숫자는 고정폭 폰트로 표시하면 정렬이 깔끔해 보입니다.

"─" 문자로 구분선을 만들면 섹션이 명확히 나뉘어 가독성이 높아집니다. 마지막으로, 정보의 우선순위에 따라 배치합니다.

가장 중요한 종목명과 금액은 상단에, 시간 같은 부가 정보는 하단에 배치합니다. 사람의 시선은 위에서 아래로 이동하므로, 이 순서가 정보 소비의 효율을 결정합니다.

parse_mode='Markdown' 파라미터를 반드시 지정해야 포맷팅이 적용된다는 점도 기억하세요. 여러분이 이런 포맷팅을 적용하면, 알림의 질이 완전히 달라집니다.

같은 정보라도 전문적이고 신뢰감 있게 보이며, 실제로 정보를 파악하는 속도도 빨라집니다. 하루에 수십 개의 알림을 받는다면 이 차이가 엄청난 시간 절약으로 이어집니다.

실전 팁

💡 텔레그램은 Markdown과 HTML 두 가지 포맷을 지원합니다. Markdown이 간단하지만 HTML이 더 많은 스타일링 옵션을 제공합니다.

💡 이모지를 과하게 사용하면 오히려 가독성이 떨어집니다. 한 메시지에 3-5개 정도가 적당합니다.

💡 고정폭 폰트(``)로 숫자를 감싸면 천 단위 콤마가 있어도 깔끔하게 정렬됩니다. 표 형태의 데이터에 유용합니다.

💡 메시지 템플릿을 별도 파일로 관리하면 디자인 변경 시 코드 수정 없이 템플릿만 바꿀 수 있습니다.


8. 차트 이미지 전송 기능 - 수익률 그래프를 시각화해서 보내기

시작하며

여러분이 일일 리포트를 받을 때 이런 아쉬움을 느낀 적 있나요? 숫자로만 보면 추세를 파악하기 어렵다는 점 말이죠.

"오늘 +2%"라는 숫자만 봐서는 이것이 상승 추세인지 하락 후 반등인지 알기 어렵습니다. 이런 문제는 특히 장기 성과를 평가할 때 심각합니다.

지난 1주일 간의 수익률을 7개의 숫자로 보는 것과 꺾은선 그래프로 보는 것은 완전히 다른 경험입니다. 그래프는 한눈에 추세, 변동성, 이상치를 파악할 수 있게 해줍니다.

바로 이럴 때 필요한 것이 차트 이미지 전송 기능입니다. matplotlib으로 그래프를 그려서 이미지 파일로 저장한 후, 텔레그램으로 전송하면 시각적으로 훨씬 명확한 리포트를 받을 수 있습니다.

개요

간단히 말해서, 차트 이미지 전송은 Python의 시각화 라이브러리로 그래프를 생성하고, 이를 이미지 파일로 변환하여 텔레그램 메시지에 첨부하는 기능입니다. 이 기능이 필요한 이유는 인간의 시각적 정보 처리 능력 때문입니다.

연구에 따르면 사람은 텍스트보다 이미지를 6만 배 빠르게 처리합니다. 복잡한 데이터도 그래프로 표현하면 3초 안에 패턴을 파악할 수 있습니다.

예를 들어, 30일간의 일별 수익률을 표로 보면 30개 숫자를 읽어야 하지만, 그래프로 보면 한눈에 "최근 1주일 상승세"라는 인사이트를 얻을 수 있습니다. 기존에는 별도의 대시보드 웹사이트를 만들어야 차트를 볼 수 있었다면, 이제는 텔레그램으로 이미지를 받아서 즉시 확인할 수 있습니다.

핵심 특징은 자동화된 시각화 파이프라인입니다. 데이터 수집 → 그래프 생성 → 이미지 저장 → 전송 → 파일 삭제까지 모든 과정이 자동으로 일어납니다.

또한 그래프에 주석, 범례, 격자 등을 추가하여 전문적인 리포트 수준의 차트를 만들 수 있습니다.

코드 예제

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from datetime import datetime, timedelta
import os

def generate_profit_chart(daily_returns, days=30):
    """수익률 차트를 생성하고 텔레그램으로 전송하는 함수"""

    # 최근 N일 데이터만 추출
    recent_data = daily_returns[-days:]
    dates = [datetime.now() - timedelta(days=days-i-1) for i in range(len(recent_data))]

    # 그래프 생성
    plt.figure(figsize=(12, 6))
    plt.style.use('seaborn-v0_8-darkgrid')  # 전문적인 스타일

    # 수익률 꺾은선 그래프
    plt.plot(dates, recent_data, linewidth=2, color='#2E86AB', marker='o', markersize=4)

    # 0% 기준선 추가
    plt.axhline(y=0, color='red', linestyle='--', linewidth=1, alpha=0.5)

    # 그래프 꾸미기
    plt.title(f'최근 {days}일 일별 수익률 추이', fontsize=16, fontweight='bold', pad=20)
    plt.xlabel('날짜', fontsize=12)
    plt.ylabel('수익률 (%)', fontsize=12)
    plt.grid(True, alpha=0.3)

    # 날짜 포맷 설정
    plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%m/%d'))
    plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=5))
    plt.xticks(rotation=45)

    # 여백 조정
    plt.tight_layout()

    # 임시 파일로 저장
    chart_path = '/tmp/profit_chart.png'
    plt.savefig(chart_path, dpi=150, bbox_inches='tight')
    plt.close()  # 메모리 해제

    # 텔레그램으로 이미지 전송
    caption = f"📊 최근 {days}일 수익률 차트\n평균: {sum(recent_data)/len(recent_data):.2f}%"

    with open(chart_path, 'rb') as photo:
        bot.send_photo(
            chat_id=CHAT_ID,
            photo=photo,
            caption=caption
        )

    # 임시 파일 삭제
    os.remove(chart_path)
    print(f"차트 전송 완료: {chart_path}")

# 사용 예시
daily_returns = [1.2, -0.5, 2.1, 0.8, -1.3, 1.5, 0.9, ...]  # 실제 데이터
generate_profit_chart(daily_returns, days=30)

설명

이것이 하는 일: 트레이딩 봇의 성과 데이터를 시각화 라이브러리로 그래프로 그리고, 이를 이미지 파일로 변환하여 텔레그램 메시지에 첨부하는 자동화 프로세스입니다. 첫 번째로, matplotlib을 사용하여 전문적인 차트를 생성합니다.

plt.style.use('seaborn')로 깔끔한 스타일을 적용하고, figsize=(12, 6)으로 가로로 긴 차트를 만들어 시간 흐름을 보기 좋게 합니다. 색상도 단순한 파란색이 아닌 '#2E86AB' 같은 세련된 색상을 사용하면 전문성이 높아 보입니다.

marker='o'로 각 데이터 포인트를 강조하면 값을 더 정확히 파악할 수 있습니다. 그 다음으로, 0% 기준선을 점선으로 추가합니다.

axhline(y=0)은 수익과 손실의 경계를 명확히 보여줍니다. 그래프가 기준선 위에 있으면 수익, 아래에 있으면 손실이므로 직관적입니다.

날짜 포맷도 '%m/%d'로 간결하게 표시하고, 45도 회전시켜 겹치지 않게 합니다. 마지막으로, 이미지를 임시 파일로 저장하고 전송한 후 삭제합니다.

/tmp 디렉토리를 사용하는 이유는 시스템이 자동으로 정리해주기 때문입니다. dpi=150으로 고해상도로 저장하면 스마트폰의 레티나 디스플레이에서도 선명하게 보입니다.

with open()으로 파일을 열면 자동으로 닫히므로 리소스 누수를 방지할 수 있습니다. 여러분이 이 기능을 구현하면, 리포트의 질이 완전히 달라집니다.

투자 전문가가 만든 것 같은 차트를 스마트폰으로 받아볼 수 있습니다. 친구들에게 자랑할 때도, 투자 일지를 작성할 때도 매우 유용합니다.

무엇보다 본인의 전략 성과를 시각적으로 추적하면 개선점을 찾기가 훨씬 쉬워집니다.

실전 팁

💡 한글 폰트가 깨지는 문제는 plt.rcParams['font.family'] = 'NanumGothic' 같은 설정으로 해결하세요. 서버에 한글 폰트 설치가 필요합니다.

💡 여러 전략을 비교하려면 plt.plot()을 여러 번 호출하여 다중 선 그래프를 만들 수 있습니다. 범례(legend)를 추가하세요.

💡 차트 생성은 CPU를 많이 사용하므로, 너무 자주 생성하지 말고 하루 1-2회 정도로 제한하세요.

💡 캔들스틱 차트, 볼린저 밴드 등 전문적인 차트는 mplfinance 라이브러리를 사용하면 쉽게 만들 수 있습니다.


9. 환경변수와 보안 관리 - 토큰을 안전하게 관리하는 방법

시작하며

여러분이 처음 텔레그램 봇을 만들 때 이런 실수를 한 적 있나요? 토큰을 코드에 직접 넣고 GitHub에 푸시한 경험 말이죠.

이 순간 여러분의 봇은 전 세계에 공개됩니다. 이런 문제는 생각보다 심각합니다.

악의적인 사용자가 여러분의 토큰을 발견하면, 그 봇으로 스팸을 보내거나 악성 링크를 퍼뜨릴 수 있습니다. 더 나쁜 경우, 여러분의 트레이딩 봇 토큰이 노출되면 계좌 정보나 거래 내역 같은 민감한 정보가 유출될 수 있습니다.

바로 이럴 때 필요한 것이 환경변수를 통한 보안 관리입니다. 코드와 설정을 분리하여, 민감한 정보는 절대 코드에 포함되지 않도록 하는 것이 핵심입니다.

개요

간단히 말해서, 환경변수 관리는 API 토큰, 비밀번호, 인증 키 같은 민감한 정보를 코드 외부에 저장하고, 프로그램 실행 시 동적으로 불러오는 보안 기법입니다. 이 기법이 필요한 이유는 코드와 데이터의 분리 원칙 때문입니다.

코드는 로직을 담고 있어 공유되어도 문제없지만, 토큰이나 비밀번호는 개인적인 정보라 공유되면 안 됩니다. 예를 들어, 오픈소스 프로젝트를 GitHub에 올리면서도 자신의 텔레그램 봇 토큰은 비밀로 유지할 수 있어야 합니다.

환경변수를 사용하면 같은 코드를 여러 환경(개발/테스트/운영)에서 다른 설정으로 실행할 수도 있습니다. 기존에는 config.py 파일에 토큰을 넣고 .gitignore에 추가했다면, 이제는 .env 파일이나 시스템 환경변수로 관리하여 더 안전하고 표준적인 방식을 사용할 수 있습니다.

핵심 특징은 다층 보안 전략입니다. .env 파일은 반드시 .gitignore에 추가하고, 서버에서는 파일 권한을 600(소유자만 읽기/쓰기)으로 설정하며, 클라우드 환경에서는 AWS Secrets Manager나 환경변수 기능을 활용합니다.

또한 .env.example 파일로 필요한 변수 목록을 공유하되 실제 값은 포함하지 않습니다.

코드 예제

# .env 파일 (절대 GitHub에 올리지 말 것!)
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz
TELEGRAM_CHAT_ID=987654321
EXCHANGE_API_KEY=your_exchange_api_key_here
EXCHANGE_SECRET=your_exchange_secret_here

# .env.example 파일 (GitHub에 올려도 됨)
TELEGRAM_BOT_TOKEN=your_bot_token_here
TELEGRAM_CHAT_ID=your_chat_id_here
EXCHANGE_API_KEY=your_api_key_here
EXCHANGE_SECRET=your_secret_here

# Python 코드에서 환경변수 사용하기
import os
from dotenv import load_dotenv

# .env 파일 로드 (개발 환경)
load_dotenv()

# 환경변수 읽기
BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
CHAT_ID = os.getenv("TELEGRAM_CHAT_ID")

# 환경변수가 없으면 에러 발생 (안전장치)
if not BOT_TOKEN:
    raise ValueError("TELEGRAM_BOT_TOKEN 환경변수가 설정되지 않았습니다!")

# 또는 기본값 제공 (테스트 환경)
DEBUG_MODE = os.getenv("DEBUG_MODE", "False") == "True"

# .gitignore 파일에 추가할 내용
"""
.env
*.log
__pycache__/
"""

# 서버에서 시스템 환경변수로 설정하는 방법
# export TELEGRAM_BOT_TOKEN="123456789:ABC..."
# export TELEGRAM_CHAT_ID="987654321"

설명

이것이 하는 일: API 토큰, 비밀번호 같은 민감한 정보를 코드에서 분리하여 별도 파일이나 시스템 환경변수로 관리하고, 프로그램 실행 시 안전하게 불러오는 보안 시스템입니다. 첫 번째로, .env 파일을 생성하여 모든 민감한 정보를 KEY=VALUE 형태로 저장합니다.

이 파일은 프로젝트 루트 디렉토리에 위치하며, 각 개발자나 서버마다 다른 내용을 가질 수 있습니다. 예를 들어, 개발자 A는 자신의 테스트 봇 토큰을, 개발자 B는 자신의 봇 토큰을 .env에 넣습니다.

같은 코드지만 각자 다른 봇으로 테스트할 수 있는 것이죠. 그 다음으로, python-dotenv 라이브러리로 .env 파일을 로드합니다.

load_dotenv()를 호출하면 .env 파일의 모든 변수가 시스템 환경변수처럼 사용 가능해집니다. os.getenv("KEY")로 값을 읽을 수 있으며, 변수가 없으면 None을 반환합니다.

이때 중요한 변수는 if not BOT_TOKEN: raise ValueError() 같은 검증 로직을 추가하여, 설정 누락 시 프로그램이 시작조차 하지 않도록 만들어야 합니다. 마지막으로, .gitignore에 .env를 반드시 추가합니다.

이렇게 하면 git add . 을 해도 .env 파일은 무시됩니다.

대신 .env.example 파일을 만들어 "어떤 변수가 필요한지"만 알려주고 실제 값은 비워둡니다. 다른 개발자가 프로젝트를 받으면 .env.example을 .env로 복사하고 자신의 토큰을 넣으면 됩니다.

여러분이 이 패턴을 적용하면, 보안 사고를 예방할 수 있습니다. GitHub에 코드를 올려도 토큰은 안전하고, 팀원과 협업할 때도 각자의 설정을 유지할 수 있습니다.

또한 개발/테스트/운영 환경을 쉽게 분리하여 관리할 수 있습니다. 전문 개발자라면 반드시 익혀야 할 필수 기법입니다.

실전 팁

💡 AWS EC2에서는 .env 파일 대신 ~/.bashrc에 export로 환경변수를 설정하거나, AWS Systems Manager Parameter Store를 사용하면 더 안전합니다.

💡 Docker를 사용한다면 docker-compose.yml의 environment 섹션이나 .env 파일 자동 로드 기능을 활용하세요.

💡 실수로 .env를 커밋했다면 즉시 git filter-branch로 히스토리에서 제거하고, 모든 토큰을 재발급해야 합니다.

💡 환경변수 이름은 대문자와 언더스코어로 작성하는 것이 관례입니다 (예: DATABASE_URL, API_SECRET_KEY).


10. 비동기 처리와 성능 최적화 - 알림이 매매를 방해하지 않게 하기

시작하며

여러분의 트레이딩 봇이 텔레그램 알림을 보내는 동안 멈춘 적 있나요? 알림 하나 보내는 데 0.5초 걸린다면, 그 사이에 중요한 매매 신호를 놓칠 수 있습니다.

이런 문제는 특히 고빈도 거래 전략에서 치명적입니다. 밀리초 단위로 거래 타이밍이 중요한데, 텔레그램 API 응답을 기다리느라 2초씩 지연된다면 수익률이 크게 떨어집니다.

알림은 부가 기능일 뿐, 핵심 매매 로직을 방해해서는 안 됩니다. 바로 이럴 때 필요한 것이 비동기 처리입니다.

알림 전송을 백그라운드로 돌려서, 매매 로직은 즉시 다음 단계로 진행할 수 있게 만드는 것이죠.

개요

간단히 말해서, 비동기 처리는 시간이 걸리는 작업(네트워크 요청, 파일 I/O)을 별도 스레드나 비동기 함수로 실행하여, 메인 로직이 기다리지 않고 계속 진행되도록 하는 기법입니다. 이 기법이 필요한 이유는 응답성과 처리량 때문입니다.

동기 방식에서는 send_message()가 완료될 때까지 프로그램이 멈춥니다. 네트워크가 느리면 3초씩 걸릴 수도 있습니다.

비동기 방식에서는 메시지 전송을 시작하고 즉시 다음 코드로 넘어갑니다. 예를 들어, 10개 종목을 동시에 매매하면서 10개 알림을 보낼 때, 동기는 10 × 0.5초 = 5초가 걸리지만, 비동기는 0.5초면 끝납니다.

기존에는 매매할 때마다 bot.send_message()로 동기 전송했다면, 이제는 threading이나 asyncio로 비동기 전송하여 매매 속도에 영향을 주지 않을 수 있습니다. 핵심 특징은 두 가지 방식을 제공한다는 것입니다.

간단한 경우는 threading.Thread로 백그라운드 스레드를 만들고, 복잡한 경우는 asyncio와 python-telegram-bot의 비동기 버전을 사용합니다. 또한 에러 핸들링도 중요한데, 백그라운드 작업이 실패해도 메인 로직에 영향을 주지 않도록 try-except로 격리합니다.

코드 예제

import threading
from queue import Queue
import asyncio
from telegram import Bot
from telegram.error import TelegramError

# 방법 1: 스레드 기반 비동기 전송 (간단)
def send_async_message(text):
    """별도 스레드에서 메시지 전송"""
    def _send():
        try:
            bot.send_message(chat_id=CHAT_ID, text=text)
        except TelegramError as e:
            print(f"알림 전송 실패: {e}")

    # 별도 스레드로 실행
    thread = threading.Thread(target=_send)
    thread.daemon = True  # 메인 프로그램 종료 시 함께 종료
    thread.start()
    # 여기서 바로 리턴 (기다리지 않음!)

# 매매 로직에서 사용
def execute_trade(ticker, quantity, price):
    # 실제 매매 실행 (중요, 빨라야 함)
    order_result = exchange.buy(ticker, quantity, price)

    # 알림 전송 (비동기, 매매에 영향 없음)
    send_async_message(f"매수 체결: {ticker} {quantity}주 @ {price}원")

    # 알림 완료를 기다리지 않고 즉시 다음 로직으로
    return order_result

# 방법 2: asyncio 기반 (더 강력함)
async def send_async_telegram(text):
    """비동기 함수로 메시지 전송"""
    try:
        # AsyncBot 사용 (동기 Bot과 다름!)
        async_bot = Bot(token=BOT_TOKEN)
        await async_bot.send_message(chat_id=CHAT_ID, text=text)
    except TelegramError as e:
        print(f"알림 실패: {e}")

# 동기 코드에서 비동기 함수 호출하기
def send_in_background(text):
    asyncio.create_task(send_async_telegram(text))

# 방법 3: 메시지 큐 사용 (대량 알림 처리)
message_queue = Queue()

def queue_worker():
    """백그라운드에서 큐의 메시지를 전송하는 워커"""
    while True:
        text = message_queue.get()
        try:
            bot.send_message(chat_id=CHAT_ID, text=text)
        except TelegramError as e:
            print(f"알림 실패: {e}")
        finally:
            message_queue.task_done()

# 워커 스레드 시작 (프로그램 시작 시 한 번만)
worker = threading.Thread(target=queue_worker, daemon=True)
worker.start()

# 사용법: 큐에 넣기만 하면 백그라운드에서 전송됨
message_queue.put("매수 체결: AAPL 10주")

설명

이것이 하는 일: 시간이 걸리는 텔레그램 API 호출을 메인 매매 로직과 분리하여, 알림 전송이 완료될 때까지 기다리지 않고 즉시 다음 작업으로 진행할 수 있게 하는 병렬 처리 시스템입니다. 첫 번째로, 가장 간단한 방법은 threading.Thread를 사용하는 것입니다.

_send() 함수를 정의하고 Thread로 감싸서 실행하면, 별도 스레드에서 메시지가 전송됩니다. daemon=True 설정은 메인 프로그램이 종료될 때 이 스레드도 함께 종료되도록 합니다.

이렇게 하면 프로그램이 알림 완료를 기다리느라 종료되지 않는 문제를 방지할 수 있습니다. 그 다음으로, asyncio를 사용하는 방법이 있습니다.

이는 더 전문적이고 효율적입니다. async def로 정의된 함수는 await로 비동기 작업을 수행하며, 여러 작업을 동시에 처리할 수 있습니다.

python-telegram-bot 라이브러리는 동기 Bot과 비동기 AsyncBot을 모두 제공하므로, 프로젝트 구조에 맞춰 선택할 수 있습니다. 마지막으로, 메시지 큐 방식은 대량의 알림을 안정적으로 처리할 때 유용합니다.

Queue에 메시지를 넣기만 하면 백그라운드 워커가 순서대로 전송합니다. 이 방식은 레이트 리밋(분당 30개 메시지) 관리도 쉽게 할 수 있고, 전송 실패 시 재시도 로직도 추가하기 편합니다.

큐가 쌓이면 일시적으로 전송을 늦추는 백프레셔 제어도 가능합니다. 여러분이 비동기 처리를 도입하면, 트레이딩 봇의 성능이 크게 향상됩니다.

알림 때문에 매매가 지연되는 일이 없고, 여러 종목을 동시에 거래해도 문제없습니다. 또한 시스템의 확장성도 좋아집니다.

하루에 100개 알림을 보내든 1000개를 보내든, 매매 로직의 속도는 동일하게 유지됩니다.

실전 팁

💡 텔레그램 API는 분당 30개 메시지 제한이 있습니다. 메시지 큐 방식에서 time.sleep(2)로 전송 간격을 조절하세요.

💡 비동기 코드는 디버깅이 어려울 수 있습니다. 개발 단계에서는 동기 방식으로 테스트하고, 운영에서만 비동기를 사용하는 것도 방법입니다.

💡 너무 많은 스레드를 만들면 오히려 성능이 떨어집니다. concurrent.futures.ThreadPoolExecutor로 스레드 풀을 관리하세요.

💡 asyncio를 사용한다면 전체 프로그램을 비동기로 재작성하는 것을 고려하세요. 일부만 비동기로 만들면 복잡도가 오히려 증가할 수 있습니다.


#Python#Telegram#TradingBot#BotAPI#Notification#python

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.