fix: 저가 코인 소수점 표시 + VOL_MIN 6.0 조정
- fp() 헬퍼: 100원 미만 코인 소수점 표시 (HOLO 등) - VOL_MIN 8→6: 신호 빈도 적정화 - LLM 로그 가격도 :,.2f 통일 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -43,7 +43,7 @@ TICKERS = [
|
||||
BAR_SEC = 20 # 봉 주기 (초)
|
||||
VOL_LOOKBACK = 61 # 거래량 평균 기준 봉 수
|
||||
ATR_LOOKBACK = 28 # ATR 계산 봉 수
|
||||
VOL_MIN = 8.0 # 거래량 배수 임계값
|
||||
VOL_MIN = 6.0 # 거래량 배수 임계값
|
||||
BUY_TIMEOUT = 60 # 지정가 매수 미체결 타임아웃 (초)
|
||||
|
||||
MAX_POS = int(os.environ.get('MAX_POSITIONS', 3))
|
||||
@@ -80,6 +80,16 @@ logging.basicConfig(
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def fp(price: float) -> str:
|
||||
"""가격을 단위에 맞게 포맷. 100원 미만은 소수점 표시."""
|
||||
if price >= 100:
|
||||
return f"{price:,.0f}"
|
||||
elif price >= 10:
|
||||
return f"{price:,.1f}"
|
||||
else:
|
||||
return f"{price:,.2f}"
|
||||
|
||||
|
||||
def tg(msg: str) -> None:
|
||||
if not TG_TOKEN or not TG_CHAT_ID:
|
||||
return
|
||||
@@ -301,7 +311,7 @@ def process_signal(sig: dict) -> None:
|
||||
log.info(f"[시그널] {ticker} 포지션 한도 도달 → 스킵")
|
||||
return
|
||||
|
||||
log.info(f"[시그널] {ticker} {cur_price:,.0f}원 vol {vol_ratio:.1f}x → LLM 판단 요청")
|
||||
log.info(f"[시그널] {ticker} {fp(cur_price)}원 vol {vol_ratio:.1f}x → LLM 판단 요청")
|
||||
|
||||
llm_result = get_entry_price(
|
||||
ticker=ticker,
|
||||
@@ -318,7 +328,7 @@ def process_signal(sig: dict) -> None:
|
||||
log.info(f"[매수/LLM] {ticker} → 스킵 | {reason}")
|
||||
tg(
|
||||
f"⏭️ <b>매수 스킵</b> {ticker}\n"
|
||||
f"현재가: {cur_price:,.0f}원 볼륨: {vol_ratio:.1f}x\n"
|
||||
f"현재가: {fp(cur_price)}원 볼륨: {vol_ratio:.1f}x\n"
|
||||
f"시장: {status}\n"
|
||||
f"사유: {reason}"
|
||||
)
|
||||
@@ -335,7 +345,7 @@ def process_signal(sig: dict) -> None:
|
||||
status = llm_result.get('market_status', '')
|
||||
qty = PER_POS * (1 - FEE) / buy_price
|
||||
diff_pct = (buy_price - cur_price) / cur_price * 100
|
||||
log.info(f"[매수/LLM] {ticker} → 승인 {buy_price:,.0f}원 (현재가 {cur_price:,.0f}원, 차이 {diff_pct:+.2f}%)")
|
||||
log.info(f"[매수/LLM] {ticker} → 승인 {fp(buy_price)}원 (현재가 {fp(cur_price)}원, 차이 {diff_pct:+.2f}%)")
|
||||
|
||||
if SIM_MODE:
|
||||
uuid = f"sim-buy-{ticker}"
|
||||
@@ -357,10 +367,10 @@ def process_signal(sig: dict) -> None:
|
||||
'ts': datetime.now(),
|
||||
'vol_ratio': vol_ratio,
|
||||
}
|
||||
log.info(f"[지정가매수] {ticker} {buy_price:,.0f}원 수량: {qty:.6f}")
|
||||
log.info(f"[지정가매수] {ticker} {fp(buy_price)}원 수량: {qty:.6f}")
|
||||
tg(
|
||||
f"📥 <b>지정가 매수</b> {ticker}\n"
|
||||
f"지정가: {buy_price:,.0f}원 (현재가 대비 {diff_pct:+.2f}%)\n"
|
||||
f"지정가: {fp(buy_price)}원 (현재가 대비 {diff_pct:+.2f}%)\n"
|
||||
f"수량: {qty:.6f} 볼륨: {vol_ratio:.1f}x\n"
|
||||
f"확신: {confidence} 시장: {status}\n"
|
||||
f"LLM: {reason}\n"
|
||||
@@ -384,7 +394,7 @@ def check_pending_buys() -> None:
|
||||
if SIM_MODE:
|
||||
bar_list = list(bars.get(ticker, []))
|
||||
if bar_list and bar_list[-1]['low'] <= pb['price']:
|
||||
log.info(f"[SIM 매수체결] {ticker} {pb['price']:,.0f}원")
|
||||
log.info(f"[SIM 매수체결] {ticker} {fp(pb['price'])}원")
|
||||
_activate_position(ticker, pb['price'], pb['qty'], pb['vol_ratio'])
|
||||
del pending_buys[ticker]
|
||||
continue
|
||||
@@ -401,7 +411,7 @@ def check_pending_buys() -> None:
|
||||
if elapsed >= BUY_TIMEOUT:
|
||||
cancel_order_safe(pb['uuid'])
|
||||
log.info(f"[매수취소] {ticker} {elapsed:.0f}초 미체결 → 취소")
|
||||
tg(f"❌ <b>매수 취소</b> {ticker}\n{pb['price']:,.0f}원 {elapsed:.0f}초 미체결")
|
||||
tg(f"❌ <b>매수 취소</b> {ticker}\n{fp(pb['price'])}원 {elapsed:.0f}초 미체결")
|
||||
del pending_buys[ticker]
|
||||
|
||||
|
||||
@@ -422,11 +432,11 @@ def _activate_position(ticker: str, entry_price: float, qty: float, vol_ratio: f
|
||||
'llm_last_ts': None,
|
||||
'llm_active': False,
|
||||
}
|
||||
log.info(f"[진입] {ticker} {entry_price:,.0f}원 vol {vol_ratio:.1f}x 지정가 {tag} {target:,.0f}원")
|
||||
log.info(f"[진입] {ticker} {fp(entry_price)}원 vol {vol_ratio:.1f}x 지정가 {tag} {fp(target)}원")
|
||||
tg(
|
||||
f"🟢 <b>매수 체결</b> {ticker}\n"
|
||||
f"체결가: {entry_price:,.0f}원 수량: {qty:.6f}\n"
|
||||
f"지정가 매도: {tag} {target:,.0f}원 (+{lr*100:.1f}%)\n"
|
||||
f"체결가: {fp(entry_price)}원 수량: {qty:.6f}\n"
|
||||
f"지정가 매도: {tag} {fp(target)}원 (+{lr*100:.1f}%)\n"
|
||||
f"{'[시뮬]' if SIM_MODE else '[실거래]'}"
|
||||
)
|
||||
|
||||
@@ -448,7 +458,7 @@ def _advance_stage(ticker: str) -> None:
|
||||
uuid = submit_limit_sell(ticker, pos['qty'], target)
|
||||
pos['sell_uuid'] = uuid
|
||||
pos['sell_price'] = target
|
||||
log.info(f"[단계전환] {ticker} → {tag} 목표가 {target:,.0f}원")
|
||||
log.info(f"[단계전환] {ticker} → {tag} 목표가 {fp(target)}원")
|
||||
else:
|
||||
pos['sell_uuid'] = None
|
||||
pos['sell_price'] = None
|
||||
@@ -470,11 +480,11 @@ def _record_exit(ticker: str, exit_price: float, tag: str) -> None:
|
||||
|
||||
llm_flag = 'LLM' if pos.get('llm_active') else 'cascade'
|
||||
icon = "✅" if pnl > 0 else "🔴"
|
||||
log.info(f"[청산/{tag}/{llm_flag}] {ticker} {exit_price:,.0f}원 PNL {pnl:+.2f}% {krw:+,.0f}원 {held}초 보유")
|
||||
log.info(f"[청산/{tag}/{llm_flag}] {ticker} {fp(exit_price)}원 PNL {pnl:+.2f}% {krw:+,.0f}원 {held}초 보유")
|
||||
tg(
|
||||
f"{icon} <b>청산</b> {ticker} [{reason_tag}] ({llm_flag})\n"
|
||||
f"진입: {pos['entry_price']:,.0f}원\n"
|
||||
f"청산: {exit_price:,.0f}원\n"
|
||||
f"진입: {fp(pos['entry_price'])}원\n"
|
||||
f"청산: {fp(exit_price)}원\n"
|
||||
f"PNL: <b>{pnl:+.2f}%</b> ({krw:+,.0f}원) {held}초 보유\n"
|
||||
f"{'[시뮬]' if SIM_MODE else '[실거래]'}"
|
||||
)
|
||||
@@ -548,10 +558,10 @@ def check_filled_positions() -> None:
|
||||
pos['sell_uuid'] = new_uuid
|
||||
pos['sell_price'] = new_price
|
||||
pos['llm_active'] = True
|
||||
log.info(f"[매도/LLM] {ticker} 지정가 {new_price:,.0f}원 설정")
|
||||
log.info(f"[매도/LLM] {ticker} 지정가 {fp(new_price)}원 설정")
|
||||
tg(
|
||||
f"🤖 <b>LLM 매도 설정</b> {ticker}\n"
|
||||
f"지정가: {new_price:,.0f}원 (진입 대비 {pnl_pct:+.2f}%)\n"
|
||||
f"지정가: {fp(new_price)}원 (진입 대비 {pnl_pct:+.2f}%)\n"
|
||||
f"확신: {confidence} 시장: {status} 관망: {'Y' if watch else 'N'}\n"
|
||||
f"LLM: {reason}"
|
||||
)
|
||||
@@ -675,8 +685,8 @@ def restore_positions() -> None:
|
||||
'llm_last_ts': None,
|
||||
'llm_active': False,
|
||||
}
|
||||
log.info(f"[복구] {ticker} 수량:{actual_bal:.6f} 매수평균:{avg:,.0f}원")
|
||||
tg(f"♻️ <b>포지션 복구</b> {ticker}\n매수평균: {avg:,.0f}원 수량: {actual_bal:.6f}")
|
||||
log.info(f"[복구] {ticker} 수량:{actual_bal:.6f} 매수평균:{fp(avg)}원")
|
||||
tg(f"♻️ <b>포지션 복구</b> {ticker}\n매수평균: {fp(avg)}원 수량: {actual_bal:.6f}")
|
||||
if positions:
|
||||
log.info(f"[복구] 총 {len(positions)}개 포지션 복구됨")
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user