feat: 트레일링 스탑 전환 + 사전 필터 강화 + 예산 증액
- cascade/LLM 매도 제거 -> 트레일링 스탑 (고점 -1.5%, 손절 -2%, 타임아웃 4h) - 사전 필터 3종 추가: 횡보/고점/연속양봉(>=2) -> LLM 호출 57% 절감 - 현재가 매수 (LLM 가격 제안 제거) - 종목 30개 -> 10개, BTC 제외 - 예산: 100K/3pos -> 1M/5pos (종목당 200K) - VOL_KRW_MIN: 2M -> 5M, BUY_TIMEOUT: 60 -> 180초 - LLM 프롬프트: 연패 무시, get_trade_history 제거 - 3월 백테스트: 승률 52.1%, PNL +17,868원 - STRATEGY.md 전면 재작성 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -103,12 +103,15 @@ def _tool_get_context(ticker: str) -> str:
|
||||
{'t': ticker},
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
if not rows:
|
||||
conn.close()
|
||||
return f"{ticker} 컨텍스트 데이터 없음"
|
||||
parts = []
|
||||
for ctx_type, content in rows:
|
||||
parts.append(f"[{ctx_type}]\n{content}")
|
||||
# CLOB(LOB) → str 변환 (conn 닫기 전에 읽어야 함)
|
||||
text = content.read() if hasattr(content, 'read') else str(content)
|
||||
parts.append(f"[{ctx_type}]\n{text}")
|
||||
conn.close()
|
||||
return f"{ticker} 종목 컨텍스트:\n" + "\n\n".join(parts)
|
||||
except Exception as e:
|
||||
return f"DB 오류: {e}"
|
||||
@@ -142,7 +145,8 @@ def _tool_get_trade_history(ticker: str, limit: int = 10) -> str:
|
||||
reason = r[5] or ''
|
||||
lines.append(f" {ts} {wl} {pnl:+.2f}% {reason}")
|
||||
header = f"{ticker} 최근 {len(rows)}건 (승률 {wins}/{len(rows)}={wins/len(rows)*100:.0f}%):"
|
||||
return header + "\n" + "\n".join(lines)
|
||||
note = "\n ※ 과거 성과는 참고용입니다. 현재 시그널 강도가 높으면 과거 연패와 무관하게 진입하세요."
|
||||
return header + "\n" + "\n".join(lines) + note
|
||||
except Exception as e:
|
||||
return f"DB 오류: {e}"
|
||||
|
||||
@@ -371,6 +375,41 @@ def _describe_bars(bar_list: list[dict], current_price: float) -> str:
|
||||
return '\n'.join(lines) + summary
|
||||
|
||||
|
||||
def _calc_momentum(bar_list: list[dict], current_price: float, bar_sec: int = 20) -> str:
|
||||
"""다중 타임프레임 모멘텀 요약. 매도 LLM에 상승 곡선 판단 근거 제공."""
|
||||
if len(bar_list) < 10:
|
||||
return ''
|
||||
|
||||
lines = []
|
||||
# 1분(3봉), 3분(9봉), 5분(15봉), 10분(30봉) 전 가격 대비 변화율
|
||||
intervals = [
|
||||
('1분', 3), ('3분', 9), ('5분', 15), ('10분', 30),
|
||||
]
|
||||
for label, n_bars in intervals:
|
||||
if len(bar_list) < n_bars + 1:
|
||||
continue
|
||||
past_price = bar_list[-(n_bars + 1)]['close']
|
||||
chg = (current_price - past_price) / past_price * 100
|
||||
arrow = '▲' if chg > 0.3 else '▼' if chg < -0.3 else '─'
|
||||
lines.append(f' {label}전 대비: {chg:+.2f}% {arrow} ({past_price:,.0f}→{current_price:,.0f})')
|
||||
|
||||
# 최근 15봉 연속 상승/하락 카운트
|
||||
recent = bar_list[-15:]
|
||||
up_count = sum(1 for b in recent if b['close'] > b['open'])
|
||||
dn_count = sum(1 for b in recent if b['close'] < b['open'])
|
||||
|
||||
# 최근 15봉 최저가 → 현재가 상승폭
|
||||
period_low = min(b['low'] for b in recent)
|
||||
rise_from_low = (current_price - period_low) / period_low * 100
|
||||
|
||||
lines.append(f' 최근 15봉: 양봉 {up_count}개 / 음봉 {dn_count}개')
|
||||
lines.append(f' 구간 저점 대비: {rise_from_low:+.2f}% ({period_low:,.0f}→{current_price:,.0f})')
|
||||
|
||||
if not lines:
|
||||
return ''
|
||||
return '[모멘텀 분석]\n' + '\n'.join(lines)
|
||||
|
||||
|
||||
def _build_prompt(
|
||||
ticker: str,
|
||||
entry_price: float,
|
||||
@@ -379,11 +418,13 @@ def _build_prompt(
|
||||
current_target: float,
|
||||
bar_desc: str,
|
||||
market_context: str = '',
|
||||
momentum_desc: str = '',
|
||||
) -> str:
|
||||
pnl_pct = (current_price - entry_price) / entry_price * 100
|
||||
target_gap = (current_target - current_price) / current_price * 100
|
||||
|
||||
market_section = f'\n{market_context}\n' if market_context else ''
|
||||
market_section = f'\n{market_context}\n' if market_context else ''
|
||||
momentum_section = f'\n{momentum_desc}\n' if momentum_desc else ''
|
||||
|
||||
return f"""당신은 암호화폐 단기 트레이더입니다.
|
||||
아래 포지션과 가격 흐름을 분석해 **지정가 매도 목표가**를 판단하세요.
|
||||
@@ -399,11 +440,14 @@ def _build_prompt(
|
||||
{market_section}
|
||||
[최근 {INPUT_BARS}봉 (20초봉)]
|
||||
{bar_desc}
|
||||
|
||||
{momentum_section}
|
||||
[운용 정책 참고 — 최종 판단은 당신이 결정]
|
||||
- 단기 거래량 가속 신호 진입 후 cascade 청산 전략 (지정가 단계적 조정)
|
||||
- 수익 목표: 진입가 대비 +0.5% ~ +2% 구간
|
||||
- 손절 기준: 진입가 대비 -2% 이하이면 즉시 시장가 매도를 강력 권고 (action=sell, price=현재가)
|
||||
- 체결 가능성이 낮으면 현실적인 목표가로 조정 권장
|
||||
- **모멘텀이 강하면(1분~10분 전 대비 계속 상승 중) 성급하게 팔지 말고 hold하세요**
|
||||
- **양봉 비율이 높고 구간 저점 대비 상승폭이 크면 추세가 살아있는 것입니다**
|
||||
- 상승 여력이 있으면 hold 권장
|
||||
|
||||
반드시 아래 JSON 형식으로만 응답하세요. 설명이나 다른 텍스트를 절대 포함하지 마세요.
|
||||
@@ -543,12 +587,11 @@ def get_entry_price(
|
||||
market_section = f'\n{mkt_ctx}\n' if mkt_ctx else ''
|
||||
|
||||
prompt = f"""당신은 암호화폐 단기 트레이더입니다.
|
||||
아래 시그널을 분석해 **매수 여부와 지정가 매수 가격**을 판단하세요.
|
||||
아래 시그널을 분석해 **매수 여부**를 판단하세요. (매수 가격은 현재가로 자동 설정됩니다)
|
||||
반드시 제공된 DB tool을 호출해 추가 데이터를 조회하세요:
|
||||
- get_btc_trend: BTC 추세 확인 (필수 — BTC 하락 시 알트 매수 위험)
|
||||
- get_btc_trend: BTC 추세 확인 (필수 — BTC 급락 시 알트 매수 위험)
|
||||
- get_ticker_context: 종목 24h/7d 변동, 뉴스 확인
|
||||
- get_trade_history: 이 종목 과거 거래 성과 확인
|
||||
- get_ohlcv: 1분봉으로 지지/저항선 확인
|
||||
- get_ohlcv: 1분봉으로 현재 추세 확인
|
||||
|
||||
[시그널 감지]
|
||||
종목 : {ticker}
|
||||
@@ -561,16 +604,16 @@ F&G지수: {fng} ({'공포' if fng <= 40 else '중립' if fng <= 50 else '탐욕
|
||||
{bar_desc}
|
||||
|
||||
[판단 기준]
|
||||
- 거래량 급증이 진짜 매집 신호인지, 일시적 노이즈인지 구분
|
||||
- BTC가 하락 중이면 알트코인 매수 자제
|
||||
- 최근 이 종목에서 연패 중이면 신중하게
|
||||
- 현재가보다 약간 낮은 지정가를 설정해 유리한 가격에 매수
|
||||
- 상승 추세가 이미 많이 진행됐으면 진입 자제
|
||||
- 거래량 급증 시그널이 왔으면 빠르게 올라타는 것이 핵심
|
||||
- BTC가 급락 중(-2% 이상)이면 자제하되, 횡보/소폭하락은 진입 OK
|
||||
- 상승 추세가 이미 많이 진행됐으면(+5% 이상) 진입 자제
|
||||
- **과거 연패/승률은 절대 고려하지 마세요. 현재 시그널만 보고 판단하세요.**
|
||||
- **get_trade_history를 호출하지 마세요. 과거 거래 이력은 판단에 불필요합니다.**
|
||||
|
||||
반드시 아래 JSON 형식으로만 응답하세요. 설명이나 다른 텍스트를 절대 포함하지 마세요.
|
||||
|
||||
매수할 경우:
|
||||
{{"action": "buy", "price": 숫자, "confidence": "high|medium|low", "reason": "판단 근거 한줄 요약", "market_status": "상승|하락|횡보|급등|급락"}}
|
||||
{{"action": "buy", "confidence": "high|medium|low", "reason": "판단 근거 한줄 요약", "market_status": "상승|하락|횡보|급등|급락"}}
|
||||
|
||||
매수하지 않을 경우:
|
||||
{{"action": "skip", "reason": "매수하지 않는 이유 한줄 요약", "market_status": "상승|하락|횡보|급등|급락"}}"""
|
||||
@@ -621,12 +664,14 @@ def get_exit_price(
|
||||
elapsed_min = (datetime.now() - pos['entry_ts']).total_seconds() / 60
|
||||
current_target = pos.get('sell_price') or entry_price * 1.005
|
||||
|
||||
bar_desc = _describe_bars(bar_list, current_price)
|
||||
mkt_ctx = _get_market_context(ticker)
|
||||
bar_desc = _describe_bars(bar_list, current_price)
|
||||
mkt_ctx = _get_market_context(ticker)
|
||||
momentum_desc = _calc_momentum(bar_list, current_price)
|
||||
prompt = _build_prompt(
|
||||
ticker, entry_price, current_price,
|
||||
elapsed_min, current_target, bar_desc,
|
||||
market_context=mkt_ctx,
|
||||
momentum_desc=momentum_desc,
|
||||
)
|
||||
|
||||
data = _call_llm(prompt, ticker)
|
||||
|
||||
Reference in New Issue
Block a user