월 0원으로 돌리는 24시간 코인 자동매매 (N100 + Python)

월 0원으로 돌리는 24시간 코인 자동매매 (N100 + Python)
Photo by Ant Rozetsky / Unsplash

1. 서론: 24시간 잠들지 않는 나만의 코인 매매 엔진

코인 시장은 24시간 돌아가며 급변합니다. 개인 투자자가 이 모든 흐름을 쫓기란 불가능하죠. 저는 저전력 가성비 서버인 N100 리눅스 서버를 활용해 바이낸스와 비트겟의 현물/선물 시장을 동시에 공략하는 4트랙 통합 매매 시스템을 구축했습니다.


2. 프로젝트 구조 (Directory Structure)

관리가 쉽고 확장이 용이하도록 기능을 모듈별로 분리했습니다. 아래 구조대로 파일을 생성해 주세요.

Plaintext

~/Projects/crypto_bot/
├── config/
│   └── settings.py      # API 키 관리 및 봇별 환경 설정
├── core/
│   ├── base_bot.py      # 코인 전용 실행 엔진 (CCXT 기반)
│   └── strategy.py      # EMA 추세 추종 전략 엔진
├── utils/
│   └── notifier.py      # 텔레그램 알림 모듈
├── logs/                # 로그 파일 저장소
├── main.py              # 4개 트랙 통합 실행 엔트리 포인트
└── ecosystem.config.js  # PM2 자동 실행 및 환경 변수 설정

3. 모듈별 풀 소스코드 (Modular Full Source)

config/settings.py (환경 설정)

Python

import os

# 실행 모드: 'testnet' 또는 'production'
BOT_MODE = os.environ.get('BOT_MODE', 'testnet')
TG_TOKEN = os.environ.get('TG_TOKEN', '')      # 환경변수 설정 권장
TG_CHAT_ID = os.environ.get('TG_CHAT_ID', '')  # 환경변수 설정 권장

# 4개 트랙 설정 (Binance Spot/Future, Bitget Spot/Future)
TRACK_CONFIGS = {
    'BN_FUT': {'exchange': 'binance', 'type': 'future', 'symbol': 'BTC/USDT'},
    'BN_SPT': {'exchange': 'binance', 'type': 'spot',   'symbol': 'BTC/USDT'},
    'BG_FUT': {'exchange': 'bitget',  'type': 'future', 'symbol': 'ETH/USDT'},
    'BG_SPT': {'exchange': 'bitget',  'type': 'spot',   'symbol': 'ETH/USDT'}
}

# 매매 파라미터
STR_SETTINGS = {
    'ratio': 0.1,         # 가용 잔고의 10% 사용
    'interval': 15,       # 분석 주기 (초)
    'ema_short': 20,
    'ema_long': 60
}

core/strategy.py (전략 로직)

Python

import pandas_ta as ta
import pandas as pd

class StrategyEngine:
    @staticmethod
    def calculate_ema_strategy(df):
        """
        전략: EMA 20/60 골든크로스 & ATR 변동성 손익비 전략
        - 15분 봉 기준 정배열 초입 진입
        - 손익비 1:2 (ATR 기반)
        """
        if len(df) < 70: return None, 0, 0, 0
        
        # 지표 계산
        df['ema20'] = ta.ema(df['close'], length=20)
        df['ema60'] = ta.ema(df['close'], length=60)
        df['atr'] = ta.atr(df['high'], df['low'], df['close'], length=14)
        
        last = df.iloc[-1]
        prev = df.iloc[-2]
        curr_p = float(last['close'])
        atr_v = float(last['atr'])
        
        # 골든크로스 조건: 60선 위에서 20선을 돌파할 때
        if last['ema20'] > last['ema60'] and prev['close'] < last['ema20'] < curr_p:
            sl = curr_p - (atr_v * 2) # 손절: ATR 2배
            tp = curr_p + (atr_v * 4) # 익절: ATR 4배
            return "LONG", curr_p, sl, tp
            
        return None, 0, 0, 0

core/base_bot.py (매매 실행 엔진)

Python

import asyncio
import ccxt.async_support as ccxt
import pandas as pd
import os
from core.strategy import StrategyEngine
from config.settings import BOT_MODE, STR_SETTINGS

class CryptoBot:
    def __init__(self, bot_id, cfg):
        self.bot_id = bot_id
        self.cfg = cfg
        self.current_pos = None
        
        # CCXT 거래소 초기화
        self.ex = getattr(ccxt, cfg['exchange'])({
            'apiKey': os.environ.get(f"{bot_id}_KEY"),
            'secret': os.environ.get(f"{bot_id}_SECRET"),
            'enableRateLimit': True,
            'options': {'defaultType': cfg['type']}
        })
        if BOT_MODE == 'testnet': self.ex.set_sandbox_mode(True)

    async def fetch_data(self):
        """15분 봉 OHLCV 데이터 수집"""
        ohlcv = await self.ex.fetch_ohlcv(self.cfg['symbol'], timeframe='15m', limit=100)
        return pd.DataFrame(ohlcv, columns=['t','o','h','l','c','v'])

    async def run_loop(self):
        print(f"🚀 [{self.bot_id}] 모니터링 시작: {self.cfg['symbol']}")
        while True:
            try:
                if not self.current_pos:
                    df = await self.fetch_data()
                    side, price, sl, tp = StrategyEngine.calculate_ema_strategy(df)
                    if side:
                        print(f"🎯 [{self.bot_id}] {side} 시그널 포착: {price}")
                        # 실제 주문 로직 예시 (시장가 진입)
                        # order = await self.ex.create_market_order(self.cfg['symbol'], 'buy', amount)
                        self.current_pos = {'side': side, 'sl': sl, 'tp': tp}
                
                # 포지션이 있을 경우 SL/TP 감시 로직 추가 가능
                await asyncio.sleep(STR_SETTINGS['interval'])
            except Exception as e:
                print(f"🚨 [{self.bot_id}] 에러 발생: {e}")
                await asyncio.sleep(30)

main.py (통합 엔트리 포인트)

Python

import asyncio
from config.settings import TRACK_CONFIGS
from core.base_bot import CryptoBot

async def main():
    tasks = []
    # 4개 트랙(BN_FUT, BN_SPT, BG_FUT, BG_SPT) 동시 실행
    for bot_id, cfg in TRACK_CONFIGS.items():
        bot = CryptoBot(bot_id, cfg)
        tasks.append(bot.run_loop())
    
    print(f"✅ 총 {len(tasks)}개 트랙 병렬 가동 중...")
    await asyncio.gather(*tasks)

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("\n👋 사용자에 의해 시스템이 종료되었습니다.")

utils/notifier.py (알림 모듈)

Python

# utils/notifier.py
import aiohttp
import asyncio
from config.settings import TG_TOKEN, TG_CHAT_ID

class TelegramNotifier:
    def __init__(self):
        self.token = TG_TOKEN
        self.chat_id = TG_CHAT_ID
        self.api_url = f"https://api.telegram.org/bot{self.token}/sendMessage"

    async def send_message(self, text):
        """텔레그램 메시지 비동기 전송"""
        if not self.token or not self.chat_id:
            print("⚠️ [알림] 텔레그램 설정이 누락되었습니다.")
            return

        payload = {
            "chat_id": self.chat_id,
            "text": text,
            "parse_mode": "HTML"
        }

        try:
            async with aiohttp.ClientSession() as session:
                async with session.post(self.api_url, json=payload) as response:
                    if response.status != 200:
                        print(f"❌ [알림] 전송 실패: {await response.text()}")
        except Exception as e:
            print(f"🚨 [알림] 네트워크 에러: {e}")

# 싱글톤 패턴으로 인스턴스 제공
notifier = TelegramNotifier()

4. 실제 운영 메뉴얼 (Manual)

1단계: 환경 설정

N100 서버의 프로젝트 폴더에서 가상환경을 잡고 필수 패키지를 설치합니다.

Bash

python3 -m venv venv
source venv/bin/activate
pip install ccxt pandas pandas-ta
mkdir logs

2단계: PM2 설정 (ecosystem.config.js)

서버 재부팅 시에도 자동으로 봇이 살아나도록 설정합니다.

JavaScript

module.exports = {
  apps: [{
    name: "crypto-4track-bot",
    script: "./main.py",
    interpreter: "./venv/bin/python3",
    env: { 
        BOT_MODE: "testnet", 
        PYTHONPATH: ".",
        BN_FUT_KEY: "발급받은키",
        BN_FUT_SECRET: "발급받은시크릿"
        // 나머지 트랙의 키도 여기에 추가하거나 서버 환경변수에 등록하세요.
    }
  }]
}

실행: pm2 start ecosystem.config.js && pm2 save

3단계: 관리 및 모니터링

  • 상태 확인: pm2 status
  • 실시간 로그: pm2 logs crypto-4track-bot
  • 에러만 보기: tail -f logs/err.log | grep "Error"

5. 마치며: 자동화가 주는 심리적 안정감

이 시스템은 감정을 배제하고 기계적인 매매를 수행합니다. 모듈화된 설계를 통해 전략을 한곳(strategy.py)에서만 수정하면 4개 트랙에 즉시 반영되는 효율성을 갖췄습니다. N100의 저전력 환경에서 여러분만의 든든한 코인 트레이더를 가동해 보세요!


면책조항: 본 포스팅의 코드는 학습용 샘플이며, 실제 투자의 결과는 투자자 본인에게 있습니다. 반드시 테스트넷에서 충분한 검증을 거치시길 바랍니다.