HAK GEMINI BINANCE TRADER는 Binance USDT-M 선물에서 하나의 관리 포지션만 운용하는 자동매매 런타임입니다. 현재 로직은 세 가지 역할을 분리합니다.
symbol_screener.py: 거래 가능한 USDT-M 무기한 선물 중 현재 설정된 점수 필드의 1위 후보를 고릅니다.gemini_trader.py: 선택된 심볼의 1시간봉 종가 배열과 현재가만 보고LONG또는SHORT를 반환합니다.hakai_strategy.py: 포지션 크기, 주문, 반전, 심볼 교체, 손절 동기화, 상태 저장을 결정론적으로 처리합니다.
리스크 고지: 이 저장소는 연구 및 자동화 실험용 코드입니다. 금융 조언이 아니며, 실제 자금 운용 결과를 보장하지 않습니다. 실거래 전에는 테스트넷 또는 소액 환경에서 충분히 검증하세요.
| 항목 | 현재 동작 |
|---|---|
| 거래 범위 | Binance USDT-M PERPETUAL + TRADING + USDT quote 심볼 |
| 관리 포지션 | 동시에 오픈 포지션 1개만 허용. 2개 이상이면 사이클 중단 |
| 기본 심볼 | setting.yaml의 symbol은 fallback 값이며, 신규 진입/교체는 스크리너 후보를 우선 사용 |
| AI 호출 조건 | 무포지션이면 즉시, 기존 포지션은 trigger_pct_usdt 가격 구간에 닿았을 때만 호출 |
| Gemini 입력 | 선택 심볼, 현재 기준가, 1h 종가 배열만 전달. 포지션 상태, 스크리너 bias, 리스크 설정은 전달하지 않음 |
| Gemini 출력 | JSON 구조의 {"decision":"LONG"} 또는 {"decision":"SHORT"}만 허용 |
| 포지션 사이징 | 신규 진입, 반전, 심볼 교체에 initial_position_size_ratio 고정 비율 사용 |
| 동일 방향 판단 | 기존 포지션 방향과 Gemini 판단이 같으면 리사이징 없이 유지 |
| 반대 방향 판단 | 현재 포지션에 대한 AI가 반대 방향을 내면 스크리너 1위 후보를 다시 고르고, 후보 심볼에 대해 Gemini를 한 번 더 호출 |
| 손절 | 계좌 기준 stop_loss_pct를 포지션 실효 레버리지로 나눠 가격 손절 거리로 변환 후 Binance 조건부 STOP_MARKET 주문으로 동기화 |
| 상태 | scheduler_state.json에 active symbol, 마지막 AI 기준가, 다음 가격 트리거 구간, 마지막 AI 판단, stop risk basis 저장 |
| 산출물 | db/<timestamp>/ 아래 스크리너, AI 입력/출력, 최종 사이클 결과 JSON 저장. 최대 20개 사이클 디렉터리 유지 |
| 알림 | Telegram 텍스트 알림과 AI 입력 종가 배열 기반 PNG 라인 차트 전송 지원 |
flowchart TD
A[TradingScheduler] --> B[setting.yaml과 scheduler_state.json 로드]
B --> C[Binance 포지션 조회]
C --> D{오픈 포지션 수}
D -- 2개 이상 --> E[사이클 중단]
D -- 0개 --> F[Score screener 실행]
F --> G[후보 현재가와 1h 종가 준비]
G --> H[Gemini 후보 방향 판단]
H --> I[고정 비중 신규 진입]
D -- 1개 --> J[현재 포지션 심볼을 active_symbol로 사용]
J --> K[현재가 조회 및 stop-loss 선동기화]
K --> L{가격 트리거 도달?}
L -- No --> M[포지션 유지, 상태 저장]
L -- Yes --> N[현재 포지션 심볼 1h 종가 준비]
N --> O[Gemini 현재 심볼 방향 판단]
O --> P{현재 포지션과 같은 방향?}
P -- Yes --> Q[포지션 유지]
P -- No --> R[Score screener 재실행]
R --> S[후보 현재가와 1h 종가 준비]
S --> T[Gemini 후보 방향 판단]
T --> U[고정 비중 심볼 교체 또는 방향 반전]
I --> V[체결 후 포지션 재조회]
U --> V
V --> W[stop risk basis 생성 및 stop-loss 동기화]
M --> X[사이클 결과 저장]
Q --> X
W --> X
- 봇은 여러 포지션을 동시에 관리하지 않습니다. Binance 계정에 오픈 포지션이 2개 이상 있으면
multiple_open_positions:*액션으로 중단합니다. - 무포지션 진입은 항상 스크리너 후보 선택이 먼저입니다.
symbol설정값은 동적 후보를 얻지 못했을 때의 기준값입니다. - 기존 포지션은 먼저 손절 주문을 맞춘 뒤 가격 트리거를 평가합니다.
- 가격 트리거는 마지막 AI 기준가에서
trigger_pct_usdt만큼 위/아래로 움직였는지 확인합니다. - AI가 같은 방향을 반환하면 포지션 크기를 늘리거나 줄이지 않습니다.
- AI가 반대 방향을 반환하면 현재 심볼을 바로 반전하지 않고, 스크리너 1위 후보를 다시 선정한 뒤 후보 심볼에 대해 Gemini 판단을 다시 받습니다.
- 주문 수량은 Binance 심볼별 step size에 맞춰 내림 처리합니다. 최소 주문 금액을 만족하지 못하면 진입을 건너뜁니다.
- Binance가 실제 적용한 레버리지를 반환하면, 목표 주문 금액은 설정 레버리지가 아니라 적용된 레버리지로 다시 계산합니다.
현재 모델 상수는 src/ai/gemini_trader.py의 GEMINI_DIRECTION_MODEL = "gemini-3.5-flash"입니다.
Gemini 프롬프트는 의도적으로 좁습니다.
- 입력:
symbol,current_price,timeframe: "1h",close_prices - 제외: 현재 포지션 방향, 포지션 크기, 손절 설정, 계좌 잔고, 스크리너 점수, 스크리너 bias
- 출력:
LONG또는SHORT만 담은 JSON - 재시도: 일시적 Gemini 오류는 최대 3회 재시도
- 기록: prompt, 정규화된 payload, raw response, usage metadata, thought summary/signature를 JSON으로 저장
close_prices는 오래된 값부터 최신 값 순서이며, 마지막 값은 독립적으로 조회한 live reference price로 교체됩니다.
스크리너는 Binance 공개 API에서 다음 데이터를 조합합니다.
exchangeInfo: USDT-M perpetual universe와 거래소 필터ticker/24hr: 24시간 거래대금, 거래 수, 가격 변화율premiumIndex: funding rateopenInterest: open interest crowdingdepth: 지정 범위 내 오더북 깊이와 스프레드klines: benchmark와 후보 심볼의 과거 수익률 정렬
현재 setting.yaml은 screener_score_field: directional_clarity_score를 사용합니다. 이 점수는 방향성이 분명한 대형/고유동성 후보를 선호합니다.
| 점수 구성 | 비중 |
|---|---|
max(long_direction_score, short_direction_score) |
55% |
directional_gap_score |
20% |
large_cap_liquidity_score |
20% |
perp_crowding_score |
5% |
balanced_score는 여전히 계산되지만 현재 설정에서는 선택 기준이 아닙니다. 검토용으로 long_swing_score, short_swing_score, directional_swing_score, score_bias, directional_bias도 함께 저장됩니다.
목표 주문 금액은 다음 공식으로 계산합니다.
target_notional_usdt = account_equity * initial_position_size_ratio * applied_leverage
applied_leverage는 Binance가 실제 적용한 값을 우선합니다. 예를 들어 계좌 equity가 1,000 USDT, initial_position_size_ratio가 0.4, 적용 레버리지가 5이면 목표 주문 금액은 2,000 USDT입니다.
손절 거리는 계좌 리스크 기준으로 계산합니다.
basis_effective_leverage = basis_entry_notional / basis_account_equity
stop_loss_distance_pct = stop_loss_pct / basis_effective_leverage
- LONG stop:
entry_price * (1 - stop_loss_distance_pct) - SHORT stop:
entry_price * (1 + stop_loss_distance_pct)
손절 주문은 Binance algoOrder의 STOP_MARKET, closePosition=true, workingType=CONTRACT_PRICE, priceProtect=TRUE로 동기화됩니다. 기존 stop risk basis가 현재 포지션의 심볼, 방향, 크기, 진입가와 맞지 않으면 폐기하고 다시 계산합니다.
런타임은 매 사이클마다 setting.yaml을 읽습니다.
| Key | 현재 설정 | 의미 |
|---|---|---|
symbol |
BTCUSDT |
fallback 심볼 |
cycle_interval_seconds |
60 |
scheduler 반복 간격 |
trigger_pct_usdt |
1.0 |
마지막 AI 기준가 대비 재판단 트리거 퍼센트 |
ai_prompt_timeframe |
1h |
Gemini 입력 타임프레임. 현재 코드는 1h만 지원 |
ai_prompt_candle_count |
100 |
Gemini에 보낼 1시간봉 종가 개수 |
gemini_thinking_level |
high |
minimal, low, medium, high 중 하나 |
fixed_leverage |
1 |
진입 전 요청할 레버리지 |
stop_loss_pct |
0.04 |
계좌 기준 손절 리스크 비율 |
initial_position_size_ratio |
1.0 |
신규/교체/반전 때 사용할 고정 증거금 비율 |
screener_score_field |
directional_clarity_score |
스크리너 후보 선택 점수 필드 |
screener_require_pass |
true |
필터 통과 후보만 선택할지 여부 |
screener_candidate_limit |
30 |
24h 거래대금 기준 사전 후보 수 |
screener_min_quote_volume |
500000000 |
최소 24h quote volume |
screener_min_trades |
500000 |
최소 24h 거래 수 |
screener_min_depth |
5000000 |
최소 오더북 깊이 |
screener_max_spread_bps |
2.0 |
최대 스프레드 |
screener_min_abs_beta |
0.80 |
benchmark 대비 최소 양의 beta |
screener_min_r2 |
0.15 |
benchmark 설명력 필터 |
screener_min_history_coverage |
0.95 |
필요한 과거 데이터 커버리지 |
initial_position_size_ratio, stop_loss_pct 같은 비율 값은 0.4 또는 "40%" 형태를 모두 처리합니다.
.env.example을 기준으로 .env를 준비합니다.
| 변수 | 필수 | 의미 |
|---|---|---|
BINANCE_API_KEY |
예 | Binance Futures API key |
BINANCE_API_SECRET |
예 | Binance Futures API secret |
GEMINI_API_KEY |
예 | Gemini API key |
TELEGRAM_BOT_TOKEN |
아니오 | Telegram 알림용 bot token |
TELEGRAM_CHAT_ID |
아니오 | Telegram 알림 대상 chat id |
BINANCE_TESTNET |
아니오 | true면 https://demo-fapi.binance.com, 기본값은 false |
BINANCE_RECV_WINDOW |
아니오 | Binance signed request receive window. 기본값 5000 ms |
Python 3.13 이상을 권장합니다.
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt한 번만 실행:
python main.py --once스케줄러로 계속 실행:
python main.pyLinode/Ubuntu systemd 설치 스크립트는 저장소가 /home/ubuntu/hak-gemini-binance-trader에 있고 ubuntu 유저가 존재한다고 가정합니다.
sudo bash setup_linode_systemd.sh --no-start필수 API key를 채운 뒤:
sudo systemctl restart hak-gemini-binance-trader
sudo journalctl -u hak-gemini-binance-trader -f| Path | 내용 |
|---|---|
log/ai_trader.log |
실행 로그. 10MB 단위 rolling, backup 5개 |
scheduler_state.json |
scheduler 상태, 트리거 구간, 마지막 AI 판단, stop risk basis |
db/<timestamp>/hakai_screener_output.json |
스크리너 메타데이터, thresholds, selection, rows |
db/<timestamp>/hakai_ai_input.json |
무포지션 또는 일반 direction 판단 입력 |
db/<timestamp>/hakai_ai_output.json |
무포지션 또는 일반 direction 판단 출력 |
db/<timestamp>/hakai_ai_current_position_direction_input.json |
기존 포지션 심볼 판단 입력 |
db/<timestamp>/hakai_ai_current_position_direction_output.json |
기존 포지션 심볼 판단 출력 |
db/<timestamp>/hakai_ai_candidate_direction_input.json |
후보 심볼 판단 입력 |
db/<timestamp>/hakai_ai_candidate_direction_output.json |
후보 심볼 판단 출력 |
db/<timestamp>/hakai_cycle_output.json |
최종 사이클 결과 |
log/, db/, .env, scheduler_state.json은 운영 중 생성되며 커밋 대상이 아닙니다.
hak-gemini-binance-trader/
├── main.py
├── setting.yaml
├── requirements.txt
├── setup_linode_systemd.sh
├── hak-gemini-binance-trader.service
├── src/
│ ├── ai/gemini_trader.py
│ ├── binance/
│ │ ├── binance_rate_limit.py
│ │ ├── common.py
│ │ ├── market_data.py
│ │ └── trade_position.py
│ ├── infra/
│ │ ├── env_loader.py
│ │ ├── logger.py
│ │ ├── price_chart.py
│ │ └── telegram.py
│ └── strategy/
│ ├── hakai_strategy.py
│ ├── runtime_config.py
│ ├── scheduler.py
│ └── symbol_screener.py
└── tests/
python -m unittest discover -s tests테스트는 트리거 정책, 고정 포지션 사이징, 스크리너 점수 선택, Gemini 프롬프트/모델 상수, scheduler 상태 저장, 가격 차트 렌더링, 의사결정 흐름을 검증합니다.
MIT License. 자세한 내용은 LICENSE를 참고하세요.