- VOL_MIN 4→8 복원 (시그널 빈도 과다)
- process_signal: LLM 호출 전/후 포지션 한도 재확인
- check_pending_buys: 체결 시점 한도 초과면 즉시 취소
- LLM tool 중복 호출 방지 (같은 tool+args → 캐시 응답)
- 모든 tool 호출 완료 시 tool 제거해 강제 텍스트 응답
- max_rounds 8→5 축소
- 재시작 시 Upbit 잔고 기반 포지션 자동 복구
- LLM 모델: google/gemini-2.5-flash로 전환
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- get_entry_price/get_exit_price가 전체 dict 반환 (action, price, confidence, reason, market_status, watch_needed)
- 매수: 시그널→LLM 승인/스킵 사유, 확신도, 시장 상태 텔레그램 전송
- 매도: LLM 지정가 설정 시 진입 대비 수익률, 확신도, 관망 여부 텔레그램 전송
- 청산: LLM/cascade 구분 태그 (텔레그램 + 로그)
- cascade fallback 전환 시 로그 명시
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- buy() now always loads WF history from trade_results DB directly
(not lazy-cached in memory) → restart-safe, no stale-history issue
- Added WF_VOL_BYPASS_THRESH (default 10.0x): if vol_ratio at entry
exceeds this threshold, WF filter is skipped regardless of win rate
- buy() now accepts vol_ratio param; runner.py passes it from
get_active_signals() for both fast-poll and main scan loops
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- core/price_db.py: add wf_state table CRUD (ensure/upsert/load/delete)
to persist shadow_cons_wins across restarts
- core/trader.py: save WF blocked state on shadow enter/close,
restore shadow_cons_wins on startup from DB
- core/monitor.py: lower ATR_MAX_STOP 4.0% → 2.0% based on sweep results
- atr_sweep.py: new ATR_MAX_STOP sweep tool using real ATR calc from DB
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When WF filter blocks a ticker, automatically start a virtual (shadow)
position with the same trailing/time stop logic. After WF_SHADOW_WINS
consecutive shadow wins, reset WF history to re-enable real trading.
- trader.py: add _shadow_positions, _shadow_cons_wins state;
_shadow_enter(), get_shadow_positions(), update_shadow_peak(),
close_shadow() functions; trigger shadow entry on WF block in buy()
- monitor.py: add _check_shadow_position() with ATR trailing + time stop;
check shadow positions every 10s in run_monitor()
- Env: WF_SHADOW_WINS=2 (2 consecutive wins to rehabilitate)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After sell_market_order, query Upbit /v1/order API to get actual
trade fills. If split across multiple fills, compute weighted average
price and use actual paid_fee instead of estimate.
Falls back to get_current_price if order query fails.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each buy generates a UUID trade_id stored in positions table.
Each sell links via same trade_id in trade_results, enabling
round-trip grouping of buy→sell pairs.
Additional fields saved per trade:
- fee_krw: commission amount (0.05% each side)
- krw_profit: net KRW profit/loss after fees
- buy_price / sell_price: exact prices
- invested_krw: capital deployed
- sell_reason: trailing stop / time stop / etc.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
23h average includes high-volume daytime periods, causing false negatives
at early morning hours. Now compare last 1h candle against the previous
5h local average (same time-of-day context) with 1.2x multiplier.
Also add momentum failure debug logs to show exact reason for rejection.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- strategy: replace daily volume check with 60-min candle volume
(daily volume at 5am is tiny -> BTC/ETH never matched; now uses
last 1h candle vs previous 23h avg × 2)
- trader: log actual KRW net profit and fee on every sell
- trader: skip re-entry +1% block when last trade was a win
(allow re-entry on new trend signal even below last sell price)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- price_db: add sell_prices table (ensure/upsert/load/delete)
- trader: restore _last_sell_prices from DB on startup so re-entry
block survives restarts; persist each sell price immediately
- market: retry chunk requests up to 3 times with backoff on 429
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add trade_results table to Oracle DB for persistent trade history
- Record win/loss after each sell with pnl_pct
- Load last N trades per ticker from DB on startup (survives restarts)
- Block buy() when recent win rate (last 5 trades) < 40% threshold
- Configurable via WF_WINDOW and WF_MIN_WIN_RATE env vars
- Backtest showed improvement from -7.5% to +37.4% cumulative return
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- price_history table on Oracle ADB stores prices every 10 minutes
- check_trend(): current price vs N hours ago (default 1h, +3% threshold)
- check_momentum(): unchanged (MA20 + 2x volume still applies)
- Ticker list cached 5 minutes to avoid 429 rate limits
- Collector starts 30s after boot to avoid simultaneous API calls
- Configurable: TREND_HOURS, TREND_MIN_GAIN_PCT in .env
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Run status reporter thread every 60 minutes, sends current price,
PnL, drop from peak, and holding time for each position.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- restore_positions(): read Upbit balances on startup to prevent
double-buying after restart
- notify.py: read TOKEN/CHAT_ID inside _send() to avoid empty values
when module is imported before load_dotenv()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- notify.py: buy/sell/error alerts via upbit_trading_jm_bot
- STOP_LOSS_PCT: trailing stop configurable via .env (default -5%)
- notify_buy/notify_sell called on every trade execution
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sell position if held for TIME_STOP_HOURS (default 24h) with less than
TIME_STOP_MIN_GAIN_PCT (default +3%) gain. Frees up capital for
fresh momentum opportunities.
Priority: trailing stop (-10%) checked first, then time stop.
Both thresholds configurable via .env.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Strategy C: volatility breakout (Larry Williams K=0.5) AND momentum
(MA20 + 2x volume surge) must both trigger for a buy signal.
Hard rules:
- Trailing stop: sell when price drops -10% from peak
- Max budget: 1,000,000 KRW total, up to 3 positions (333,333 KRW each)
- Scan top 20 KRW tickers by 24h trading volume every 60s
- Monitor positions every 10s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>