Oracle3
Oracle3: 예측시장 거래 엔진·MCP 서버
코인 자동매매 매크로 생성 플랫폼
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
코인 매크로 백테스트 & 공유 서비스 (MVP) 실거래 없이, 내가 만든 코인 매매 규칙(매크로)을 과거 시세로 시뮬레이션(백테스트) 하고, 결과를 인증 카드로 공유하며, 남의 매크로를 복제해서 다시 돌려보는 서비스입니다. ⚠️ 이 서비스는 실제 주문/자동매매를 하지 않습니다. 거래소 주문 API 연동이 전혀 없습니다. 모든 수익률은 과거 데이터 시뮬레이션 결과이며 화면·카드에 항상 배지로 명시됩니다. 스택 • 백엔드: Python 3.11+ / FastAPI, pandas, numpy, SQLModel(SQLite) • 시세: Binance 공개 klines REST (인증 불필요) + 로컬 SQLite 캐시 • 공유 카드: 서버 Pillow 렌더링 (GET /api/card/{slug}.png) • 프론트: React + Vite + TailwindCSS (SPA) 1) 백엔드 (필수 — 엔진/API/카드/테스트 전부 여기서 동작) • API 문서: http://localhost:8000/docs • 헬스체크: http://localhost:8000/api/healthREADME 전문 보기 ↗
실거래 없이, 내가 만든 코인 매매 규칙(매크로)을 과거 시세로 시뮬레이션(백테스트) 하고, 결과를 인증 카드로 공유하며, 남의 매크로를 복제해서 다시 돌려보는 서비스입니다.
⚠️ 이 서비스는 실제 주문/자동매매를 하지 않습니다. 거래소 주문 API 연동이 전혀 없습니다. 모든 수익률은 과거 데이터 시뮬레이션 결과이며 화면·카드에 항상 배지로 명시됩니다.
klines REST (인증 불필요) + 로컬 SQLite 캐시GET /api/card/{slug}.png)cd backend
python -m pip install -r requirements.txt
python -m pytest # 엔진 + API 유닛테스트 (18개) 통과 확인
python -m uvicorn app.main:app --reload --port 8000
⚠️ Node.js 18+ 가 필요합니다 (이 저장소가 만들어진 환경엔 Node가 없어 프론트는 빌드/실행 검증을 못 했습니다. 백엔드는 전부 검증됨). Node 설치 후:
cd frontend
npm install
npm run dev # http://localhost:5173 (/api 는 8000 으로 프록시)
프로덕션 단일 프로세스로 돌리려면:
cd frontend && npm run build # frontend/dist 생성
cd ../backend && python -m uvicorn app.main:app --port 8000
# 이제 http://localhost:8000 에서 SPA + API 가 한 포트로 동작 (딥링크 fallback 포함)
backend/engine/매매 판단 코드는 저장소에 한 벌만 둔다. 백테스트 · 서버 실시간 · 실행기가 맞춰야 할 계약이 전부 여기서 나온다. 두 벌이 되면 백테스트 결과와 실거래가 조용히 갈라진다.
실행기는 Tauri(Rust) 앱이다 — orbleeparrot/ggparrot-desktop, 매매 루프는
src-tauri/crates/runner-core. 파이썬 패키지를 그대로 import 할 수 없으므로, "같은 코드"
대신 같은 답을 계약으로 박는다(backend/tests/fixtures/engine_golden.json).
(gg_parrot/runner/ 의 파이썬 · Tk 실행기는 옛 경로이고 배포되지 않는다.)
engine 은 app 을 거꾸로 참조하지 않는다. 서버 전용 코드(DB · 캔들 수급 · API)는
app/ 에 남는다 — 봉 피드가 app/candle_feed.py 에 있는 것도 그래서다. 그래야 판단을
읽는 데 DB 도 웹 프레임워크도 필요 없고, 계약을 서버 없이 만들 수 있다.schema · exchanges · intervals · leverage · stepper ·
candles · driver · live_session · live_levels · entry_filter · bundle ·
version)의 바깥 의존성은 pydantic 하나다.backtest · walkforward · portfolio* · explain · summary ·
validation)은 pandas · numpy 를 쓴다. engine/__init__.py 가 이 이름들을 지연 import 해
판단 경로와 섞이지 않게 한다.경계는 backend/tests/test_engine_package_boundary.py 가 지킨다.
tests/fixtures/engine_golden.json엔진이 두 언어로 존재하므로 "같은 코드" 라고 말할 수 없다. 대신 같은 입력에 같은 답을 글로 박아 두고 양쪽을 잰다. 고정된 90개 봉에 여섯 규칙(D · E · F · G · H · I)을 돌려 나온 주문 명령 · 거래 수 · 끝 자산이 그 계약이다.
backend/tests/test_engine_golden.py 가 지금 엔진이 그 답을 그대로 내는지 본다.판단을 의도적으로 바꿨다면 --regolden 으로 다시 만들고 ENGINE_VERSION 도 올린다.
안 올리면 옛 실행기가 조용히 다른 답을 낸다. 두 값이 어긋나면 시험이 깨진다.
봉은 여섯 규칙이 전부 실제로 사고팔도록 일부러 섞어 만들었다 — 아무것도 안 사는 계약은 아무것도 보장하지 않는다. 그 사실 자체를 보초 시험이 지킨다.
engine/version.py실행기가 스스로 판단하게 되면 엔진이 사용자 PC 마다 다른 세대로 돈다. 웹에서 본 백테스트는 서버의 최신 엔진이 낸 결과인데 손에 든 실행기가 옛 엔진이면 같은 매크로가 다른 자리에서 사고판다. 늦게 사는 것은 보이지만 다르게 사는 것은 안 보인다.
실행기는 세션을 시작할 때 자기 ENGINE_VERSION 을 함께 보내고, 서버는 매크로가 요구하는
세대보다 낮으면 426 으로 돌려보낸다. 세대를 밝히지 않는 실행기는 통과시킨다 — 지금
배포된 실행기 전부가 그렇고, 판단을 서버가 하므로 그 엔진 세대는 결과에 영향이 없다.
판정이 달라지는 변경(진입·청산 계산, 새 규칙·파라미터, 체결·수수료)을 넣을 때:
ENGINE_VERSION 을 1 올린다._MIN_ENGINE_BY_RULE 에 적는다.문구·로그·성능·서버 전용 코드 변경으로는 올리지 않는다. 적지 않으면 게이트는 통과시키고, 옛 실행기가 조용히 다른 답을 낸다.
RUNNER_LOCAL_DECISIONS원칙: 우리 서버 장애가 사용자의 매수 · 매도 시점을 좌우하면 안 된다. 못 판 것만 귀책이 아니라 못 산 것도 귀책이다. 그래서 봉 받기와 매매 판단은 실행기 안에서 끝난다.
| 서버 판단(지금 기본) | 실행기 판단 | |
|---|---|---|
| 봉 받기 | 서버가 거래소에서 | 실행기가 거래소에서 직접 |
| 진입 · 청산 판단 | 서버 드라이버 | 실행기(Rust, 구현 예정) |
| 손절 · 일일 손실 · 보유시간 | 실행기 | 실행기 (그대로) |
| 주문 | 실행기 | 실행기 (그대로, 같은 길) |
| 전략 상태 · 체결 기록 | 서버가 씀 | 실행기가 올리고 서버는 비추기만 |
| 원격 종료 · 청산 | 서버 → 실행기 | 서버 → 실행기 (그대로) |
켜는 법: 서버에 RUNNER_LOCAL_DECISIONS=true. 기본은 꺼짐 — 거래 흐름을 바꾸는
전환이라 코드가 다 들어간 뒤에도 운영에서 한 번 더 켠다. 실행기는 늘 "내가 판단하겠다"
(decides: "local")고 말하고, 켤지는 서버가 정해 시작 응답(decides)으로 알려 준다 —
양쪽이 동시에 판단하면 같은 신호로 두 번 산다.
실행기 쪽은 아직 없다 — Tauri(Rust) 앱(orbleeparrot/ggparrot-desktop)에 봉 수집과
판단을 구현해야 켤 수 있다. 서버 쪽은 받을 준비가 끝났다: 로컬 세션에는 드라이버를 띄우지
않고 명령도 주지 않으며, local_fills · strategy_state 를 받아 적고 비춘다.
켠 뒤 알아 둘 것:
last_heartbeat_at 으로 언제 본
값인지 함께 읽어야 한다.local_fills)이 리더보드 · 수익률 ·
활동 기록의 전부다. 서버가 명령을 내던 때는 '냈던 명령' 이라도 알았지만 이제는 모른다.서버와 실행기가 같은 결론을 내는지는 위 판단 계약(engine_golden.json)으로 잰다.
commission_pct (기본 0.1%).slippage_pct (기본 0.05%). 매수는 높게, 매도는 낮게 체결.funding_pct (기본 0, 경로만 구현 — 파라미터로 조정 가능).leverage=1이면 현물과 동일하고 청산이 없습니다(기존과 완전히 동일). 2배 이상이면 격리 마진 청산 시뮬레이션이 켜집니다(아래 별도 섹션). 숏 수익률은 부호 반전(가격 하락=이익).amount_per_buy가 투입액을 정하므로 invest_ratio 미적용.매수횟수 × amount_per_buy) 대비 최종 평가금액. 매수-후-보유라 승률은 최종 손익 부호로 표기(참고용).⚠️ 백테스트·페이퍼(가짜 돈)에만 적용됩니다. 실거래(bot.py)에는 레버리지를 적용하지 않습니다. 정밀 바이낸스식(마크 프라이스·구간별 유지증거금·펀딩)과 다른 단순 근사식입니다. 목적은 "화려한 수익률"이 아니라 레버리지가 얼마나 위험한지 가짜 돈으로 안전하게 체험하게 하는 것입니다.
MAX_LEVERAGE(기본 20) 상한.margin × leverage. 수익·손실이 배수만큼 확대되고, 수수료·펀딩비는 명목가 기준으로 계산합니다.E, 유지증거금률 mmr(기본 0.5%, MAINTENANCE_MARGIN_RATE), 보정 corr = mmr + commission%/100
liq ≈ E × (1 − 1/L + corr)liq ≈ E × (1 + 1/L − corr)100/L % 반대 움직임에서 청산). corr은 청산을 아주 조금 일찍(보수적) 트리거합니다.liquidation_count(청산 횟수)·liquidated_loss(청산으로 잃은 금액), 페이퍼 상태에 동일 필드를 노출합니다. 화면·공유 카드·리더보드에 "고위험 레버리지" 뱃지를 붙입니다.leverage=1은 위 로직을 전부 건너뛰어 기존 현물 엔진과 완전히 동일하게 동작합니다(유닛테스트로 고정).API 응답의 data_source 필드:
binance — Binance에서 새로 조회 후 캐시에 저장cache — 로컬 SQLite 캐시에서 반환 (같은 구간 재조회 시 네트워크 미사용)synthetic — 오프라인 폴백. 네트워크가 없고 캐시도 없을 때 종목명 기반의 결정론적 합성 시세를 생성해 데모가 끊기지 않게 함. 화면·카드에 소스가 표기됩니다.BTC · 숏 · 평단 대비 +5% 익절 / -3% 손절 후 재진입 · 자금 50% 투입/s/btc-5pct-short-x1a2 (공유·복제 진입점)rule_type discriminated union)원본 A·B·C(단일 포지션·DCA)는 그대로 유지되고, v4에서 DJ가 추가됐습니다. DJ는 공통 엔벌로프
(candle_interval + 고급 리스크)를 공유하며, 공용 캔들 엔진(engine/candles.py)에서 실행됩니다.
take_profit_pct, initial_capitalbuy_price, sell_price, initial_capitalamount_per_buy, interval_dayslower_price, upper_price, grid_count, grid_mode, per_grid_invest?, band_exit_actionentry_mode, entry_dip, activation_profit, trail_percent, reenter_after_exitrsi_period, entry_threshold, exit_threshold, confirm_candles, exit_mode, take_profit?bb_period, bb_std, strategy, exit_target, squeeze_filter, squeeze_lookbackbase_order_size, safety_order_size, price_deviation, *_scale, max_safety_orders, take_profitk, exit_mode, trail_percent, take_profit?, ma_filter_period?, session_start_hourma_type, fast_period, slow_period, exit_signal, take_profit?, confirm_candles공통 엔진 규칙(D~J):
same_bar_sl_bars로 결과에 표기.initial_capital × invest_ratio를 넘으면 저장 반려.risk.daily_max_loss_pct(당일 거래 중단), risk.max_holding_hours(강제 청산), risk.cooldown_minutes(손절 후 재진입 금지).LiveCandleSim + CandleFeed).| Method | Path | 설명 |
|---|---|---|
| POST | /api/macros | 매크로 JSON 저장 → share_slug + 대표 백테스트 스냅샷 |
| GET | /api/macros/{slug} | 매크로 JSON 조회(복제용) |
| POST | /api/backtest | 매크로 JSON(+기간 override) → 지표 |
| GET | /api/gallery | 공유 매크로 백테스트 수익률순 목록 |
| GET | /api/card/{slug}.png | 인증 카드 이미지(Pillow) |
| POST | /api/paper/start | 페이퍼 세션 시작(매크로+심볼+mode) → session_id |
| POST | /api/paper/{id}/stop | 페이퍼 세션 중지 |
| GET | /api/paper/{id} | 현재 평가금액·수익률·최근 체결(폴링용) |
| GET | /api/paper/{id}/trades | 전체 매매 로그 |
| POST | /api/realtrade/bundle | 실거래 실행 파일 zip(데모 목업) 다운로드 |
| GET | /api/kimchi-premium?symbol=BTC | 김치프리미엄 집계(업비트 KRW · 바이낸스 USDT × USDKRW) |
| GET | /api/hot-coins?limit=10 | '오늘의 경주마' — 급등+거래활발 종목(바이낸스 24h, 서버 캐시 공유) |
| POST | /api/leaderboard/register | 매크로 등록 → 페이퍼 세션 시작 + 오늘의 리더보드 엔트리 생성 |
| GET | /api/leaderboard?user_id= | 오늘(KST) 엔트리 목록(실시간 수익률·좋아요, 좋아요순) + 초기화까지 남은 초 |
| POST | /api/leaderboard/{id}/vote | 좋아요(+1)/싫어요(-1) 토글(유저당 1표) |
| POST | /api/leaderboard/{id}/edit | 비밀번호 확인(서버 해시) 후 매크로 수정 + 페이퍼 재시작 |
| GET | /api/chat | 오늘(KST) 채팅 목록·미확인 수, before_id로 이전 메시지 조회 |
| POST | /api/chat | 로그인 회원의 채팅 전송(text, 회원별 길이·전송 제한) |
| PUT | /api/chat/read | 로그인 회원의 읽음 위치 저장(last_seen_id, 이전 값으로 되돌리지 않음) |
출력 지표: 최종 수익률(%), 승률, MDD(최대낙폭 %), 총 매매 횟수, 자산곡선(equity curve).
engine/stepper.py의 PositionSim/DcaSim)을 그대로 사용합니다. 백테스트는 과거 일봉을, 페이퍼는 실시간 틱을 같은 step(price)에 흘려보내는 차이뿐입니다(중복 구현 아님).GET /api/v3/ticker/price)을 선택했습니다(websocket 대비 구현 단순·안정). 폴링 간격은 env로 조절.demo_replay 모드 — 최근 수 시간치 1분봉을 빠르게 재생해 매매 로그가 촤르륵 쌓이게 함(오프라인이면 결정론적 합성 인트라데이로 폴백).PAPER_POLL_SECONDS(기본 3), PAPER_REPLAY_SECONDS(기본 0.4), PAPER_REPLAY_HOURS(기본 6).bot.py + macro.json + README-run.txt) 다운로드.bot.py는 실행 시 API Key/Secret 입력 화면(tkinter, 없으면 콘솔)까지만 뜨고, 입력하면 "[데모] 인증 정보가 입력되었습니다…" 메시지 후 종료합니다.InfoTooltip, 문구는 src/lib/glossary.js 한 곳에서 관리(수정·번역 용이).빌더에서 매크로 생성(용어 툴팁 확인) → 백테스트 → 페이퍼 트레이딩 시작(매매 로그 실시간 누적) → 중지 → 실거래 실행 파일 다운로드 → python bot.py → 키 입력 화면.
rule_type 선택에 따라 타입별 params 폼만 조건부 렌더하고, 각 필드에 InfoTooltip을 노출합니다.engine/schema.py의 ParamsD~ParamsJ)로 검증하고, 프론트도 저장 전 동일 규칙을 검사합니다(frontend/src/lib/macro.js의 validate).김프(%) = (업비트_KRW / (바이낸스_USDT × USDKRW) - 1) × 100. 양수=김프(빨강), 음수=역프(파랑).GET /api/kimchi-premium)하고 프론트는 이 엔드포인트만 폴링해 CORS·레이트리밋을 회피합니다. 각 외부 호출은 짧게 캐시(KIMCHI_CACHE_SECONDS, 기본 10초).GET /v1/ticker, 바이낸스 GET /api/v3/ticker/price, USD/KRW 환율 open.er-api.com(무키). 환율 API 실패 시 상수 fallback(KIMCHI_FX_FALLBACK, 기본 1380)으로 죽지 않고 fx_is_fallback 플래그로 안내합니다.InfoTooltip 노출. 김프는 참고 지표일 뿐 매매 신호로 쓰지 않습니다.KIMCHI_CACHE_SECONDS(기본 10), KIMCHI_FX_FALLBACK(기본 1380), 프론트 폴링 주기 VITE_KIMCHI_POLL_MS(기본 15000).app/hotcoins.py의 순수 함수 select_hot_coins, 테스트 가능):
JUP처럼 접미사만 우연히 겹치는 실제 코인은 유지),GET /api/hot-coins만 폴링).prefers-reduced-motion 시 애니메이션 정지(가로 스크롤 허용), 아이템 클릭 시 해당 종목으로 빌더 진입(/?symbol=XRPUSDT 프리필). 가격/상승률은 기존 format.js 재사용.HOTCOINS_MIN_QUOTE_VOLUME(기본 10,000,000), HOTCOINS_CANDIDATE_POOL(기본 100), HOTCOINS_CACHE_SECONDS(기본 45), 프론트 폴링 VITE_HOTCOINS_POLL_MS(기본 45000).get_klines(..., allow_synthetic=False) → 실데이터 없으면 NoSpotDataError. 백테스트/페이퍼 경로는 이 모드를 사용 → API가 HTTP 422 + "이 종목은 현물 시세 데이터가 없어 시뮬레이션할 수 없습니다." 반환.ensure_spot_available(symbol)로 검증(잘못된 심볼 400은 거부, 네트워크 불명 시 캐시 유무로 판단).allow_synthetic=True 기본)은 그대로 유지..bat 원클릭run.bat(+requirements.txt)이 추가됩니다: 파이썬 확인 → 의존성 설치(이 데모는 표준 라이브러리만 써서 사실상 no-op) → bot.py 실행.%~dp0 + 따옴표 처리, 파이썬 미설치 시 안내 후 pause로 창이 안 꺼짐. 여전히 실거래는 하지 않습니다(키 입력 화면까지만, 저장/전송 없음).run.bat · bot.py · requirements.txt · macro.json · README-run.txt./leaderboard, 기존 /gallery는 리다이렉트)로 변경.lib/user.js).created_ms로 저장하고 조회 시 오늘(KST 00:00~) 것만 필터 → 스케줄러 없이 매일 자정 자동 리셋(과거 엔트리는 물리 삭제 아님). 등록 시각은 KST HH:MM으로 표시("오늘 14:32 등록").GET /api/leaderboard가 직전 보드 날짜의 상위 LEADERBOARD_KEEP_TOP(기본 3)등의 created_ms(=보드 날짜)만 오늘 자정으로 민다. 페이퍼 세션·created_at·누적 수익률은 손대지 않는다 — 순위는 "등록 시점부터의 순수 매크로 수익률" 경쟁이므로 이월은 초기화가 아니다. 매크로가 제 조건(익절선·손절선·최대 보유시간)으로 종료돼 세션이 stopped가 돼도 그 종료 시점 수익률로 계속 순위에 남는다. streak_days가 하루씩 늘어 UI에 **"N일째 순위권 방어중"**으로 표시되고, 원 등록 시각은 first_created_ms에 한 번만 보관한다. AI 챌린지와 같은 lazy·idempotent 패턴(leaderboardcarryover 테이블의 KST 날짜 유니크 키)이라 별도 크론이 필요 없다.seconds_to_reset(다음 KST 00:00까지 남은 초)를 프론트가 매초 로컬 감소 → HH:MM:SS 표시. KST는 DST가 없어 고정 +09:00 오프셋으로 계산(tz DB 의존성 회피).get_ticker_price_cached, 기본 2초 TTL)로 읽어 같은 종목 세션이 많아도 바이낸스 호출이 중복되지 않음.macroToForm 재사용). 값 수정 후 자기 것으로 재등록 가능.app/security.py, 표준 라이브러리 — bcrypt C빌드 의존성 회피)로 해시 저장. 평문 저장·전송·로깅·응답 포함 없음. 아이디 중복은 허용(단순 표시용, 개인정보 미수집). 팝업에 "다른 서비스와 다른 임시 비밀번호 사용" 경고 노출.이전 익명으로 구분합니다.이전 메시지 더 보기로 같은 날의 이전 대화를 조회합니다. 미확인 수는 전체 당일 기록을 기준으로 집계합니다. 자정에는 전날 기록을 화면에서 제외하며 DB 기록과 읽음 ID를 초기화하지 않습니다.seen_id는 클라이언트 힌트를 반영한 집계 기준이고, server_seen_id는 실제 저장된 읽음 위치입니다. 읽음 저장 실패는 재시도하며, 늦은 조회 응답이 전송 성공 메시지를 제거하거나 읽음 기준을 되돌리지 않도록 병합합니다.run.bat: @echo off + chcp 65001(한글 콘솔) + 마지막 pause. 파이썬 탐색 py -3 → python → python3 순서, 없으면 설치 안내 후 pause. 모든 경로 "%~dp0" + 따옴표(공백/한글 경로 대응). 의존성 설치·실행 각 단계 실패 시 원인 출력 후 pause(창 즉시 안 꺼짐).run.ps1(PowerShell 실행정책 안내 포함), requirements.txt(python-binance). README-run.txt에 수동 실행 명령(pip install -r requirements.txt → python bot.py)도 명시.python-binance의 Client(key, secret, testnet=True)로 **Spot Testnet(testnet.binance.vision)**에 실제 연결·잔고조회·주문. 흐름/호출은 실거래와 동일.LOT_SIZE/MIN_NOTIONAL 필터에 맞춰 반올림.MAX_ORDER_USDT(기본 100), 중복 발주 방지(포지션 플래그), 주문 실패 재시도 제한·에러 로그, Ctrl+C 안전 종료(미청산 포지션 안내). 출금/이체 기능 없음(주문만). API 키 저장·전송·로깅 없음(Secret은 getpass로 입력 숨김).USE_TESTNET = True → False 한 곳만 변경(자동 전환 없음). README-run.txt에 테스트넷 키 발급법 + 메인넷 전환(거래권한만/출금끄기/소액/책임·컴플라이언스) 가이드 수록.bg-slate-50 text-slate-900(index.css), 카드 bg-white, 보더 slate-200/300, 입력 bg-slate-100. 의미 색상 유지(상승 초록·하락 빨강·김프/역프·좋아요/싫어요 등은 라이트 대비를 위해 400→600 셰이드로 조정), 채도 버튼(indigo/blue/red-600)은 text-white 유지.malgun.ttf로 한글 렌더링.backend/app.db, 시세 캐시는 backend/cache/market.db.카피트레이딩, 실거래 레버리지(현재 레버리지·청산은 백테스트/페이퍼 전용), cross 마진, 숏 DCA, 로그인. (v4에서 트레일링 스탑·그리드·마틴게일·변동성 돌파·RSI/볼린저/MA크로스·최대손실 한도·재매수 쿨다운은 백테스트/페이퍼로 구현됨. v9에서 레버리지 조건 + 격리 마진 청산 시뮬레이션이 백테스트/페이퍼로 구현됨.)
cd backend && python -m pytest -q