auto: 일일 백업 2026-08-26 02:00
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
| **증권잔액 → 골디 inbox** | `python3 ~/.openclaw/agents/stock/workspace/scripts/send_balance_to_budget.py` | launchd `ai.openclaw.stock.send-balance` (매월 1일 04:30) |
|
||||
| **골디 inbox 처리** (단독) | `python3 ~/.openclaw/agents/budget/workspace/skills/monthly-settlement/scripts/inbox_handler.py [--dry-run]` | 트리거 없음 — 월간결산 cron 진입부에서 자동 호출. 수동 점검·재처리용 |
|
||||
| **워치리스트 모니터링** | `python3 ~/.openclaw/agents/stock/workspace/scripts/watchlist_monitor.py check` | launchd `ai.openclaw.stock.watchlist-monitor` (평일 09–15시 매 15분) |
|
||||
| **트레일링 스톱 감시** | `python3 ~/.openclaw/agents/stock/workspace/scripts/trailing_monitor.py {check [--repeat N --gap S] [--force] [--dry-run]\|list}` | launchd `ai.openclaw.stock.trailing-monitor` (평일 09:00–15:30 **매 1분**, 스크립트가 장외·휴장 self-skip). 자산웹에서 PIN 승인된 트레일링 예약의 조건단가를 고점 갱신 시 kt10002로 **상향만** 정정. 신규 발주 없음. 예약 0건이면 즉시 종료(API 콜 0). 상태 `state/trailing_stops.json`. ⚠️ cadence 변경 이력 2분→30초→**1분**(2026-07-30 관리자님) — 30초는 launchd로 불가해 `--repeat 2 --gap 30`(프로세스 내부 2회)으로 구현했었고, 옵션은 살아있으니 다시 쓸 땐 plist 인자만 추가 |
|
||||
| **트레일링 스톱 감시** | `python3 ~/.openclaw/agents/stock/workspace/scripts/trailing_monitor.py {check [--repeat N --gap S] [--force] [--dry-run]\|list}` | launchd `ai.openclaw.stock.trailing-monitor` (평일 **08:00–19:59 매 1분**, 720엔트리 — 2026-08-25 plist 실측 정정. 스크립트가 장외·휴장 self-skip). 하는 일 둘: ①자산웹에서 PIN 승인된 트레일링 예약의 조건단가를 고점 갱신 시 kt10002로 **상향만** 정정 ②**예약 트레일링(TRAILING_ARM) 발동·만료** — 발동가 도달 시 스톱주문 신규 발주 + 예약 승격, 미발동으로 등록일이 지난 대기 예약은 자동 취소(둘 다 2026-08-25 추가, 관리자님 승인). ⚠️ "신규 발주 없음"은 더 이상 사실이 아니다. 예약 0건이면 즉시 종료(API 콜 0). 상태 `state/trailing_stops.json`. ⚠️ cadence 변경 이력 2분→30초→**1분**(2026-07-30 관리자님) — 30초는 launchd로 불가해 `--repeat 2 --gap 30`(프로세스 내부 2회)으로 구현했었고, 옵션은 살아있으니 다시 쓸 땐 plist 인자만 추가 |
|
||||
| **공모주 캘린더 동기화** | `python3 ~/.openclaw/agents/stock/workspace/scripts/ipo_calendar_sync.py` | launchd `ai.openclaw.stock.ipo-calendar-sync` (매월 1일 09:00) |
|
||||
| **FOMC 캘린더 동기화** | `python3 ~/.openclaw/agents/stock/workspace/scripts/fomc_calendar_sync.py [--list\|--dry-run]` | launchd `ai.openclaw.stock.fomc-calendar-sync` (매월 1일 09:10). 연준 공식 일정표 → `[FOMC] YYYY-MM 회의` 종일 일정(회의 시작일 ET ~ 발표일 KST). `--list`는 파싱 결과만(캘린더 미접근) |
|
||||
| **휴장일 동기화** | `python3 ~/.openclaw/agents/stock/workspace/scripts/holiday_sync.py [--show]` | launchd `ai.openclaw.stock.holiday-sync` (매주 일요일 03:00) |
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -33,13 +33,16 @@ Be the assistant you'd actually want to talk to. Concise when needed, thorough w
|
||||
|
||||
레이는 **매매를 결정하지 않는다.** 결정권은 항상 관리자님 PIN echo 에 있다.
|
||||
|
||||
- **LLM 자율 매매 금지** — 워치리스트 도달·시장 이벤트·뉴스·자체 추론으로 매수/매도 자동 트리거 X. 매매는 오직 관리자님이 텔레그램에 직접 명시적으로 지시할 때만.
|
||||
- **LLM 자율 매매 금지** — 워치리스트 도달·시장 이벤트·뉴스·자체 추론으로 매수/매도 자동 트리거 X. 매매는 오직 관리자님이 텔레그램에 직접 명시적으로 지시할 때만. (가격 도달로 주문이 나가는 경로가 하나 있다 — 자산웹에서 PIN 으로 사전 승인한 **예약 트레일링**뿐이고, 아래 「자동 트리거 등록 금지」의 예외 항목을 볼 것. 레이가 만들 수 있는 경로는 아니다.)
|
||||
- **레이의 역할 = 페이로드 추출** — 자연어("삼성 5주 75,000 원에 사줘") → 종목코드·계좌·수량·가격·주문방식을 dict 로 정리해서 `orders.handler.propose_trade` 진입점에 전달. 그 외엔 일체 매매 도구를 부르지 않는다.
|
||||
- **카드+PIN 분리 발송** — propose_trade 가 카드 메시지(매수/매도·계좌·종목·가격 4개 하이라이트) + PIN 메시지(단독 4자리 숫자, 가희 계좌면 8자리 영숫자) 두 개를 텔레그램에 보낸다.
|
||||
- **PIN echo 가 마지막 게이트** — 관리자님이 PIN 을 그대로 답장한 순간만 실주문이 나간다. 레이 LLM 이 PIN 을 자기 판단으로 만들거나 변형하면 절대 안 된다.
|
||||
- **사이드카 ON 상태에선 모든 매매 거부** — `state/orders_disabled` 파일 존재 시 진입점 첫 줄에서 차단. `/orders_off` `/orders_on` 으로 토글.
|
||||
- **조회 vs 주문 분리 유지** — `kiwoom_client.py` 는 여전히 조회 전용. 주문 함수는 `orders/` 패키지로 분리. 조회 모듈에 주문 함수 추가 금지.
|
||||
- **매매 관련 자동 트리거 등록 금지** — 매매·매매재시도·취소·재발행 등 `orders/` 모듈 진입점을 cron `jobs.json` 또는 launchd plist 에 자동 발화 항목으로 등록하지 않는다. 실패·거절·쿨다운된 매매는 관리자님이 텔레그램으로 다시 명시적으로 지시할 때까지 그대로 둔다. "쿨다운 끝나면 다시 시도하자"는 충동도 X — 자동 재시도 등록 자체가 LLM 자율 매매와 동급. 자동화가 필요해 보이면 직접 등록하지 말고 관리자님께 설계 안건으로 제안.
|
||||
- **승인된 예외는 `ai.openclaw.stock.trailing-monitor.plist` 하나뿐**이고, 그 안에서 허용된 매매 API 는 ①`modify_order`(트레일링 손절선 **상향** 정정, 2026-07-30 승인) ②**예약 트레일링(TRAILING_ARM)의 발동가 도달 시 스톱주문 발주**(2026-08-25 승인) 2종이다.
|
||||
- 허용 근거는 "발주가 없다"가 **아니라** "계좌·종목·수량·계단·발동가가 **PIN 승인 시점에 전부 확정**되고, 감시는 확정된 내용을 조건 도달 시 집행할 뿐 LLM 판단이 개입하지 않는다" 다. 판단 기준을 이렇게 읽어야 한다 — **누가 매매의 *내용*(계좌·종목·수량·방향·가격)을 정하는가.** 사람이 PIN 으로 정했으면 집행 자동화는 별건 승인 대상, 감시 루프나 LLM 이 정하면 금지.
|
||||
- 이 예외를 근거로 **새 자동 트리거를 스스로 늘리지 않는다.** 감시가 수량을 늘리거나 방향을 바꾸는 경로도 여전히 없다(매도가능 부족 시 **축소**만).
|
||||
- **자동매매·알고리즘 매매 요청** — 이 원칙을 환기하고 사이드카·1주 검증·가드 보강을 거친 별도 설계 안건으로 미룬다.
|
||||
|
||||
상세 사양과 호출 진입점은 `orders/__init__.py`, 한도값은 `orders/limits.json` 참조. 사용은 `skills/order-trading`, `skills/order-controls`.
|
||||
|
||||
@@ -808,6 +808,26 @@ def _market_phase_state() -> dict:
|
||||
return {'phase': 'closed', 'label': '장외 시간', 'active': False, 'time': time_str}
|
||||
|
||||
|
||||
# phase 경계 시각(분) — _market_phase_state 의 분기 경계와 같은 값.
|
||||
_PHASE_BOUNDARIES_MIN = (8 * 60, 9 * 60, 15 * 60 + 30, 20 * 60)
|
||||
|
||||
|
||||
def _secs_to_next_phase() -> int | None:
|
||||
"""오늘 남은 다음 phase 경계까지의 초. 주말·휴장일·20:00 이후엔 None
|
||||
(브라우저는 기본 60초 주기로 폴링).
|
||||
|
||||
⚠️ now.second 절사 덕에 값이 항상 실제보다 크거나 같다 → 이 값으로 잡은 타이머가
|
||||
경계보다 일찍 깨는 일이 없다(일찍 깨면 옛 phase 를 읽고 다음 경계까지 60초 주기로 밀린다)."""
|
||||
now = datetime.now(KST)
|
||||
if now.weekday() >= 5 or now.strftime('%Y-%m-%d') in _load_holidays():
|
||||
return None
|
||||
cur = now.hour * 3600 + now.minute * 60 + now.second
|
||||
for b in _PHASE_BOUNDARIES_MIN:
|
||||
if b * 60 > cur:
|
||||
return b * 60 - cur
|
||||
return None
|
||||
|
||||
|
||||
def _bump_panel_signal() -> None:
|
||||
"""주문 접수/취소 등 이벤트 발생 시 state/fill_signal 갱신 → 브라우저 1초 틱(realtime)이
|
||||
fill_seq 변화를 감지해 패널을 1회 갱신. 3초 주기 전면 swap 폐지(2026-07-22, 이벤트 기반 전환)
|
||||
@@ -3954,6 +3974,45 @@ _TRAILING_LOOKUP_TTL = 5.0
|
||||
_trailing_lookup_cache: dict = {'ts': 0.0, 'by_ord': {}}
|
||||
|
||||
|
||||
def _arm_rows() -> list:
|
||||
"""대기 예약 → 미체결 목록에 섞을 합성 행. 상태파일 읽기라 키움 콜 0.
|
||||
|
||||
JS 렌더러가 읽는 키를 그대로 채우고 `kind='arm'` 으로 표시한다. 취소 경로를 물리적으로
|
||||
분리하려고 `ord_no` 는 비워 둔다 — 미체결 취소(kt10003)가 이 행을 삼키면 안 된다.
|
||||
현재가는 실시간 허브에서만 읽는다(없으면 0) — 팝업 열 때 키움 조회가 나가면 안 된다.
|
||||
"""
|
||||
out = []
|
||||
try:
|
||||
from orders import trailing as _trl
|
||||
arms = _trl.list_arms()
|
||||
except Exception:
|
||||
return out
|
||||
hub = _RT_HUB
|
||||
for a in arms:
|
||||
cur = 0
|
||||
if hub is not None:
|
||||
q = hub.get_quote(a['symbol']) or {}
|
||||
cur = q.get('price') or 0
|
||||
out.append({
|
||||
'account': a['account'], 'ord_no': '', 'code': a['symbol'], 'name': a['symbol_name'],
|
||||
'side': 'SELL', 'order_qty': a['qty'], 'unfilled_qty': a['qty'],
|
||||
'order_price': 0, 'order_type': '예약 트레일링', 'stop_price': 0,
|
||||
'cur_price': cur, 'exchange': '-', 'exchange_code': '',
|
||||
'status': '발동 대기', 'order_time': (a.get('created_at') or '')[5:16].replace('T', ' '),
|
||||
'trailing': None,
|
||||
'kind': 'arm',
|
||||
'arm': {'id': a['id'], 'trigger_price': a['trigger_price'],
|
||||
'min_sell_price': a.get('min_sell_price'),
|
||||
'steps': a.get('steps') or [], 'firing_at': a.get('firing_at'),
|
||||
'fired_legs': a.get('fired_legs') or [],
|
||||
'created_at': a.get('created_at'),
|
||||
# 감시 루프는 08:00~19:59 에만 돌아 20:00 직후엔 아직 파일에 남아 있다.
|
||||
# 숨기지 않고 '만료' 로 표시한다 — 화면이 실제 상태와 달라선 안 된다.
|
||||
'expired': _trl.is_arm_expired(a)},
|
||||
})
|
||||
return out
|
||||
|
||||
|
||||
def _trailing_info_for(ord_no: str):
|
||||
"""미체결 주문번호 → 트레일링 예약 요약 (없으면 None).
|
||||
|
||||
@@ -8495,6 +8554,7 @@ def _render_order_modal() -> str:
|
||||
'<option value="MARKET">시장가</option>'
|
||||
'<option value="STOP_LIMIT">스톱지정가 (하락 시 매도)</option>'
|
||||
'<option value="TRAILING_STOP">트레일링 스톱 (고점 따라 손절선 상승)</option>'
|
||||
'<option value="TRAILING_ARM">예약 트레일링 (발동가 도달 시 시작)</option>'
|
||||
'</select></label>'
|
||||
'<label data-order-stop-row style="display:none;">'
|
||||
'<span>조건단가 <span class="muted small" style="font-weight:400;">(이 가격 도달 시 매도)</span></span>'
|
||||
@@ -8504,6 +8564,14 @@ def _render_order_modal() -> str:
|
||||
# 미리보기는 클라이언트에서 같은 식으로 계산해 보여주되 실제 값은 서버 계산분이 승자.
|
||||
# 고점 기준 select 제거(2026-07-30) — 과거 고점(52주·매수후) 기준은 손절선이 현재가
|
||||
# 위로 올라가 등록 즉시 발동하는 구조라 쓸 수 없었다. 초기 고점은 등록 시점 현재가 고정.
|
||||
# 예약 트레일링: 발동가 하나만 더 받는다. 계단·최저 매도가 입력은 트레일링과 공유하고,
|
||||
# 미리보기의 기준(peak)만 현재가 → 발동가로 바뀐다. 트레일 행 **위**에 두는 이유는
|
||||
# 읽는 순서가 "언제 시작 → 어떻게 판다" 여야 해서다.
|
||||
'<label data-order-arm-row style="display:none;">'
|
||||
'<span>발동가 <b style="color:#d9a84a;">(원)</b> '
|
||||
'<span class="muted small" style="font-weight:400;">이 가격까지 오르면 트레일링 시작</span></span>'
|
||||
'<input type="number" inputmode="numeric" min="0" step="1" data-order-arm-price>'
|
||||
'</label>'
|
||||
'<label data-order-trail-row style="display:none;">'
|
||||
# 단위(%·원)를 라벨에 박는다 — 입력칸 옆에 붙이면 가로로 벌어져 스크롤이 생기고,
|
||||
# number input 안쪽 absolute 접미사는 데스크톱 스피너와 겹친다.
|
||||
@@ -8842,6 +8910,8 @@ def render_html() -> str:
|
||||
valid_tids = [tid for tid, _ in descs]
|
||||
first_tid = descs[0][0]
|
||||
market_active = _market_active_now()
|
||||
market_phase = _market_phase_state()['phase']
|
||||
market_next_in = _secs_to_next_phase()
|
||||
now = datetime.now(KST).strftime('%Y-%m-%d %H:%M:%S')
|
||||
# 계단 프리셋은 셸에 실어 보낸다 — 거래 모달을 열 때마다 fetch 하면 느리고,
|
||||
# 고정 4종이라 셸에 넣어도 부담이 없다.
|
||||
@@ -10127,32 +10197,42 @@ def render_html() -> str:
|
||||
# 여기 스크립트는 상단 상태 점(🟢장중/⚪장마감)만 유지 + 개장/마감 전환 시 1회 재렌더
|
||||
# (day-change 게이트·시간대 표시 반영). 정상 상태에선 주기적 full swap 없음.
|
||||
market_active_js = 'true' if market_active else 'false'
|
||||
market_next_in_js = 'null' if market_next_in is None else str(market_next_in)
|
||||
auto_reload_script = (
|
||||
'<script>(function(){'
|
||||
f'var MARKET={market_active_js};'
|
||||
'var SLOW_MS=60000,poll=null,cur=MARKET;'
|
||||
f'var MARKET={market_active_js},PHASE="{market_phase}",NEXT_IN={market_next_in_js};'
|
||||
'var SLOW_MS=60000,poll=null,cur=MARKET,curPhase=PHASE;'
|
||||
'var HSEQ_MS=20000,hpoll=null,hseq=null;' # 외부 매매 감지 폴링(장중만)
|
||||
'var badge=document.getElementById("auto-badge");'
|
||||
# 배지 점: 장중 초록(.live) / 장마감 어두운 회색. 공유 플래그(apply·폴링이 함께 갱신) 우선, 없으면 cur.
|
||||
'function dot(){var m=(typeof window.__behive_market_active!=="undefined")?!!window.__behive_market_active:cur;if(badge)badge.classList.toggle("live",m);}'
|
||||
'function stopPoll(){if(poll){clearInterval(poll);poll=null;}}'
|
||||
# 60초마다 /api/market_state(키움 미경유·시각+휴장일만) 확인 → 배지 갱신 + 개장/마감 전환 시에만 1회 재렌더.
|
||||
'function startPoll(){'
|
||||
'stopPoll();'
|
||||
'poll=setInterval(function(){'
|
||||
'if(document.visibilityState!=="visible")return;'
|
||||
'fetch("/api/market_state",{cache:"no-store",credentials:"same-origin"})'
|
||||
'.then(function(r){return r.json();})'
|
||||
'.then(function(s){'
|
||||
'var act=!!(s&&s.active);'
|
||||
'if(act===cur)return;'
|
||||
'cur=act;'
|
||||
'function stopPoll(){if(poll){clearTimeout(poll);poll=null;}}'
|
||||
'function schedule(ms){stopPoll();poll=setTimeout(tick,ms);}'
|
||||
# 다음 phase 경계(09:00·15:30·08:00·20:00)가 기본 주기보다 가까우면 그 직후(+2s)로 당겨 잡는다.
|
||||
# setInterval 고정 60초로는 경계와 최대 60초 어긋나 초록점·OHLC 강조가 늦게 바뀐다.
|
||||
'function nextWait(n){'
|
||||
'if(n===null||n===undefined||n*1000>SLOW_MS)return SLOW_MS;'
|
||||
'return Math.max(1000,(n+2)*1000);'
|
||||
'}'
|
||||
# /api/market_state(키움 미경유·시각+휴장일만) 확인 → 배지 갱신 + phase 전환 시 1회 재렌더.
|
||||
'function tick(){'
|
||||
'if(document.visibilityState!=="visible"){schedule(SLOW_MS);return;}'
|
||||
'fetch("/api/market_state",{cache:"no-store",credentials:"same-origin"})'
|
||||
'.then(function(r){return r.json();})'
|
||||
'.then(function(s){'
|
||||
'var act=!!(s&&s.active),ph=(s&&s.phase)||curPhase,changed=false;'
|
||||
'if(act!==cur){'
|
||||
'cur=act;changed=true;'
|
||||
# set_market 이 공유 플래그 갱신 + arm(=dot) 호출. 없을 때만 직접 dot.
|
||||
'if(window.__behive_set_market)window.__behive_set_market(act);else dot();'
|
||||
# 전환(장 시작/마감) 시에만 1회 전체 재렌더 — day-change 게이트·시간대 라벨 반영.
|
||||
'if(window.__behive_load)window.__behive_load();'
|
||||
'}).catch(function(){});'
|
||||
'},SLOW_MS);'
|
||||
'}'
|
||||
# regular↔nxt 는 active 가 둘 다 true 라 phase 로만 감지된다(09:00·15:30 전환).
|
||||
'if(ph!==curPhase){curPhase=ph;changed=true;}'
|
||||
# 전환 시 1회 전체 재렌더 — 거래가능 초록점·OHLC 활성 컬럼·day-change 게이트 반영.
|
||||
# fresh 필수: 패널 캐시(10s)가 전환 직전 HTML을 물고 있고, 주기적 재렌더가 없어 stale 이 굳는다.
|
||||
'if(changed&&window.__behive_load)window.__behive_load(null,true);'
|
||||
'schedule(nextWait(s&&s.next_in));'
|
||||
'}).catch(function(){schedule(SLOW_MS);});'
|
||||
'}'
|
||||
# 외부 매매(키움 앱 등) 감지 — 장중 20s 간격 /api/holdings_seq 폴링. 보유 시그니처가 바뀌면
|
||||
# (앱 직거래·타 기기 체결 등 fill_signal 안 오는 변화 포함) 패널 1회 fresh 갱신. 첫 관측은 기준만.
|
||||
@@ -10177,9 +10257,10 @@ def render_html() -> str:
|
||||
# __behive_auto_arm — 수동 새로고침·PTR 완료 후 호출됨. 이제 배지만 재확인(주기 재무장 없음).
|
||||
'function arm(){dot();}'
|
||||
'window.__behive_auto_arm=arm;'
|
||||
'document.addEventListener("visibilitychange",dot);'
|
||||
# 숨겨진 동안엔 폴링을 건너뛰므로, 돌아오는 즉시 한 번 확인한다(폰에서 15:30을 백그라운드로 넘긴 경우).
|
||||
'document.addEventListener("visibilitychange",function(){dot();if(document.visibilityState==="visible")schedule(300);});'
|
||||
'if(window.__behive_set_market)window.__behive_set_market(MARKET);'
|
||||
'dot();startPoll();startHPoll();'
|
||||
'dot();schedule(nextWait(NEXT_IN));startHPoll();'
|
||||
'})();</script>'
|
||||
)
|
||||
|
||||
@@ -11923,11 +12004,18 @@ function refreshStopUI(){
|
||||
if(stopOpt) stopOpt.disabled = (state.side === 'BUY');
|
||||
var trailOpt = otSel.querySelector('option[value="TRAILING_STOP"]');
|
||||
if(trailOpt) trailOpt.disabled = (state.side === 'BUY');
|
||||
if(state.side === 'BUY' && (otSel.value === 'STOP_LIMIT' || otSel.value === 'TRAILING_STOP')) otSel.value = 'LIMIT';
|
||||
var armOpt = otSel.querySelector('option[value="TRAILING_ARM"]');
|
||||
if(armOpt) armOpt.disabled = (state.side === 'BUY');
|
||||
if(state.side === 'BUY' && (otSel.value === 'STOP_LIMIT' || otSel.value === 'TRAILING_STOP'
|
||||
|| otSel.value === 'TRAILING_ARM')) otSel.value = 'LIMIT';
|
||||
var isStop = (otSel.value === 'STOP_LIMIT');
|
||||
var isTrail = (otSel.value === 'TRAILING_STOP');
|
||||
var isArm = (otSel.value === 'TRAILING_ARM');
|
||||
// 계단·최저 매도가·미리보기 UI 는 트레일링과 예약 트레일링이 공유한다 (기준 peak 만 다름).
|
||||
var isTrail = (otSel.value === 'TRAILING_STOP') || isArm;
|
||||
var stopRow = $('[data-order-stop-row]');
|
||||
if(stopRow) stopRow.style.display = isStop ? '' : 'none';
|
||||
var armRow = $('[data-order-arm-row]');
|
||||
if(armRow) armRow.style.display = isArm ? '' : 'none';
|
||||
var trailRow = $('[data-order-trail-row]');
|
||||
if(trailRow) trailRow.style.display = isTrail ? '' : 'none';
|
||||
var priceRow = $('[data-order-price-row]');
|
||||
@@ -11961,6 +12049,20 @@ function trailSteps(){
|
||||
function trailMinPrice(){
|
||||
return parseInt(($('[data-order-trail-min]')||{}).value || '0', 10) || 0;
|
||||
}
|
||||
function isArmMode(){
|
||||
var otSel = $('[data-order-type]');
|
||||
return !!(otSel && otSel.value === 'TRAILING_ARM');
|
||||
}
|
||||
function armPrice(){
|
||||
return parseInt(($('[data-order-arm-price]')||{}).value || '0', 10) || 0;
|
||||
}
|
||||
// 계단 계산의 기준가. 예약 트레일링은 발동가, 일반 트레일링은 현재가.
|
||||
// 서버 handler.propose_trade 의 basis 와 같은 규칙이어야 한다.
|
||||
function trailBasePeak(){
|
||||
if(isArmMode()) return armPrice();
|
||||
return (state.lastCheck && state.lastCheck.cur_price)
|
||||
|| (state.lastBook && state.lastBook.price) || 0;
|
||||
}
|
||||
// 계단별 주수 — 서버 orders/trailing.allocate_step_qty(최대잔여법)와 같은 식이어야 한다.
|
||||
function allocStepQty(total, weights){
|
||||
if(!(total > 0)) return weights.map(function(){ return 0; });
|
||||
@@ -12037,10 +12139,12 @@ function renderTrailPreview(){
|
||||
var steps = trailSteps();
|
||||
var minPrice = trailMinPrice();
|
||||
var cur = (state.lastCheck && state.lastCheck.cur_price) || (state.lastBook && state.lastBook.price) || 0;
|
||||
if(!steps.length || !(cur > 0)){ el.textContent = ''; return; }
|
||||
var isArm = isArmMode();
|
||||
// 계단 기준가 = 예약이면 발동가, 아니면 현재가 (서버 handler 의 basis 와 동일).
|
||||
var peak = trailBasePeak();
|
||||
if(!steps.length || !(cur > 0) || !(peak > 0)){ el.textContent = ''; return; }
|
||||
var qty = parseInt(($('[data-order-qty]')||{}).value || '0', 10) || 0;
|
||||
var reason = trailBlockReason();
|
||||
var peak = cur; // 초기 고점 = 등록 시점 현재가 (서버 handler 와 동일)
|
||||
var qtys = allocStepQty(qty, trailWeights(steps));
|
||||
|
||||
var hero, note;
|
||||
@@ -12051,13 +12155,30 @@ function renderTrailPreview(){
|
||||
note = '위 입력을 고쳐주세요.';
|
||||
} else {
|
||||
var r0 = trailCondPrice(peak, steps[0].pct, minPrice);
|
||||
var pctFromCur = ((cur - r0.cond) / cur * 100);
|
||||
hero = '<div class="cap">지금 등록하면 1단계 손절선</div>' +
|
||||
var pctFromPeak = ((peak - r0.cond) / peak * 100);
|
||||
hero = '<div class="cap">' +
|
||||
(isArm ? fmt(peak) + '원에 닿으면 1단계 손절선' : '지금 등록하면 1단계 손절선') +
|
||||
'</div>' +
|
||||
'<div class="val">' + fmt(r0.cond) + '원' +
|
||||
(r0.floorApplied ? '<span class="trail-pv-badge">최저가 적용</span>' : '') + '</div>' +
|
||||
'<div class="sub">현재가보다 ' + pctFromCur.toFixed(1) + '% 아래 · 여기 닿으면 ' +
|
||||
'<div class="sub">' + (isArm ? '발동가' : '현재가') + '보다 ' +
|
||||
pctFromPeak.toFixed(1) + '% 아래 · 여기 닿으면 ' +
|
||||
(qtys[0] > 0 ? fmt(qtys[0]) + '주' : '') + ' 매도</div>';
|
||||
note = '고점이 오르면 계단이 <b>통째로 따라 올라갑니다</b>. 내려가지는 않습니다.';
|
||||
if(isArm){
|
||||
// 이 기능의 유일한 위험을 화면에서 고지하는 자리다 — 빼지 말 것.
|
||||
note = '<b>지금은 주문이 나가지 않습니다.</b> 발동가에 닿는 순간 위 계단이 ' +
|
||||
'자동으로 나갑니다 (PIN 재확인 없음).<br>' +
|
||||
'<b>오늘 장 마감까지만 유효</b> — 미발동이면 자동 취소됩니다.<br>' +
|
||||
'실제 손절선은 발동 시점 현재가 기준이라 위 값 이상입니다. ' + note;
|
||||
// 당일 유효로 바뀐 뒤(2026-08-25)로 상한가 초과 발동가는 '오늘 도달 불가'가 확정이다.
|
||||
// 막지는 않는다(관리자님 지시) — 무의미하다는 사실만 알린다.
|
||||
var upLim = (state.lastBook && state.lastBook.upper_limit) || 0;
|
||||
if(upLim > 0 && peak > upLim){
|
||||
note = '<b style="color:#e06c62;">⚠️ 오늘 상한가 ' + fmt(upLim) + '원을 넘는 발동가입니다 — ' +
|
||||
'오늘은 도달할 수 없어 장 마감에 그대로 만료됩니다.</b><br>' + note;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var rows = steps.map(function(s, i){
|
||||
@@ -12077,7 +12198,14 @@ function renderTrailPreview(){
|
||||
'주</span><span class="sv">스톱 없음</span></div>';
|
||||
}
|
||||
|
||||
var kv = ['<dt>시작 고점</dt><dd><b>' + fmt(peak) + '원</b><br>등록 시점 현재가</dd>'];
|
||||
var kv;
|
||||
if(isArm){
|
||||
kv = ['<dt>발동가</dt><dd><b>' + fmt(peak) + '원</b><br>도달 시 시작</dd>',
|
||||
'<dt>현재가</dt><dd>' + fmt(cur) + '원<br>발동까지 +' +
|
||||
((peak - cur) / cur * 100).toFixed(1) + '%</dd>'];
|
||||
} else {
|
||||
kv = ['<dt>시작 고점</dt><dd><b>' + fmt(peak) + '원</b><br>등록 시점 현재가</dd>'];
|
||||
}
|
||||
if(minPrice > 0) kv.push('<dt>최저 매도가</dt><dd>' + fmt(minPrice) + '원</dd>');
|
||||
|
||||
el.innerHTML = '<div class="trail-pv' + (reason ? ' bad' : '') + '">' +
|
||||
@@ -12139,7 +12267,7 @@ function renderBook(book){
|
||||
state.lastBook = book;
|
||||
// 트레일링 미리보기는 현재가·52주 전고점을 이 응답에서 읽는다 — 호가 갱신마다 다시 그린다.
|
||||
var otNow = $('[data-order-type]');
|
||||
if(otNow && otNow.value === 'TRAILING_STOP') renderTrailPreview();
|
||||
if(otNow && (otNow.value === 'TRAILING_STOP' || otNow.value === 'TRAILING_ARM')) renderTrailPreview();
|
||||
if(book.name){ var ne = $('[data-order-name]'); if(ne) ne.textContent = book.name; state.name = book.name; }
|
||||
var ce = $('[data-order-cur-price]'); if(ce) ce.textContent = fmt(book.price)+'원';
|
||||
var che = $('[data-order-change]');
|
||||
@@ -12463,6 +12591,49 @@ function closeOpenOrdersModal(){
|
||||
if(modal.classList.contains('hidden') && (!pinModal || pinModal.classList.contains('hidden'))) document.body.classList.remove('modal-open');
|
||||
refreshActiveCount();
|
||||
}
|
||||
// 대기 예약 행 — 키움 주문이 아니라서 취소 버튼도 다른 경로를 쓴다(data-cancel-arm).
|
||||
function armRowHtml(r){
|
||||
var a = r.arm || {};
|
||||
var cur = r.cur_price || 0;
|
||||
var gap = (cur > 0)
|
||||
? (' · 현재가 ' + fmt(cur) + '원 (+' + ((a.trigger_price - cur) / cur * 100).toFixed(1) + '% 필요)')
|
||||
: '';
|
||||
var steps = (a.steps || []).map(function(s){
|
||||
return '−' + s.pct + '% ' + (s.cum >= 100 ? '전량' : '누적 ' + s.cum + '%');
|
||||
}).join(' · ');
|
||||
if(a.min_sell_price) steps += ' · 최저 ' + fmt(a.min_sell_price) + '원';
|
||||
var warn = '';
|
||||
if(a.firing_at){
|
||||
var nos = (a.fired_legs || []).map(function(s){ return s.ord_no; })
|
||||
.filter(function(n){ return !!n; }).join(', ') || '없음';
|
||||
warn = '<div class="row-line2" style="color:#e06c62;">⚠️ 발동 처리 중단됨 — 접수된 주문: ' +
|
||||
nos + ' · 키움 미체결 확인 필요</div>';
|
||||
}
|
||||
var safeArm = String(a.id || '').replace(/"/g,'"');
|
||||
return (
|
||||
'<div class="open-order-row" data-arm-id="' + safeArm + '">' +
|
||||
'<div class="row-info">' +
|
||||
'<div class="row-line1">' +
|
||||
'<span class="badge-side sell">예약 대기</span>' +
|
||||
'<span>' + (r.name || r.code) + '</span>' +
|
||||
'<span style="color:#8b8f9a; font-weight:400; font-size:11px;">(' + r.code + ')</span>' +
|
||||
'</div>' +
|
||||
'<div class="row-line2">' +
|
||||
'[' + r.account + '] ' + fmt(r.order_qty) + '주 · 발동가 ' +
|
||||
fmt(a.trigger_price) + '원 도달 시 시작' + gap +
|
||||
'</div>' +
|
||||
'<div class="row-line2" style="color:#d9a84a; overflow-wrap:anywhere;">' + steps + '</div>' +
|
||||
warn +
|
||||
'<div class="row-line2">등록 ' + (r.order_time || '') + ' · ' +
|
||||
(a.expired ? '<b style="color:#e06c62;">만료 — 정리 대기</b>' : '오늘 장 마감까지 유효') +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="row-action">' +
|
||||
'<button type="button" data-cancel-arm>취소</button>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
);
|
||||
}
|
||||
function loadOpenOrders(){
|
||||
var list = openOrdersModal.querySelector('[data-open-orders-list]');
|
||||
if(!list) return;
|
||||
@@ -12473,7 +12644,10 @@ function loadOpenOrders(){
|
||||
list.innerHTML = '<div class="empty">미체결 주문 없음</div>';
|
||||
return;
|
||||
}
|
||||
// 대기 예약을 맨 위로 — 아직 주문이 아니니 미체결과 섞이면 성격이 흐려진다.
|
||||
rows.sort(function(a, b){ return (b.kind === 'arm') - (a.kind === 'arm'); });
|
||||
list.innerHTML = rows.map(function(r){
|
||||
if(r.kind === 'arm') return armRowHtml(r);
|
||||
var sideCls = r.side === 'BUY' ? 'buy' : 'sell';
|
||||
var sideLabel = r.side === 'BUY' ? '매수' : '매도';
|
||||
// 주문유형을 항상 붙인다 — 가격만 쓰면 스톱지정가·트레일링이 지정가처럼 보인다.
|
||||
@@ -12578,6 +12752,31 @@ function cancelOpenOrder(ordNo, account, btn){
|
||||
if(btn){ btn.disabled = false; btn.textContent = '취소'; }
|
||||
});
|
||||
}
|
||||
// 대기 예약 취소 — 키움 주문이 아니므로 kt10003 이 아니라 상태파일에서 지운다.
|
||||
function cancelArm(armId, btn){
|
||||
if(!confirm('예약 ' + armId + ' 을 취소하시겠습니까?')) return;
|
||||
if(btn){ btn.disabled = true; btn.textContent = '취소중…'; }
|
||||
var body = new URLSearchParams();
|
||||
body.set('arm_id', armId);
|
||||
fetch('/api/orders/arm_cancel', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type':'application/x-www-form-urlencoded','Accept':'application/json'},
|
||||
body: body.toString()
|
||||
}).then(function(r){ return r.json(); })
|
||||
.then(function(d){
|
||||
if(d.ok){
|
||||
showToast(d.message || '✅ 예약 취소됨', 'success', 5000);
|
||||
loadOpenOrders();
|
||||
refreshActiveCount();
|
||||
} else {
|
||||
showToast(d.message || d.error || '취소 실패', 'error');
|
||||
if(btn){ btn.disabled = false; btn.textContent = '취소'; }
|
||||
}
|
||||
}).catch(function(e){
|
||||
showToast('취소 오류: ' + e, 'error');
|
||||
if(btn){ btn.disabled = false; btn.textContent = '취소'; }
|
||||
});
|
||||
}
|
||||
function fetchMarketPhase(){
|
||||
fetch('/api/market_state').then(function(r){ return r.json(); }).then(function(d){
|
||||
state.marketActive = !!d.active;
|
||||
@@ -12620,9 +12819,11 @@ function updateMarketPhaseDisplay(){
|
||||
// 아니라 입력하는 시점에 막는다.
|
||||
function trailBlockReason(){
|
||||
var otSel = $('[data-order-type]');
|
||||
if(!otSel || otSel.value !== 'TRAILING_STOP') return '';
|
||||
if(!otSel || (otSel.value !== 'TRAILING_STOP' && otSel.value !== 'TRAILING_ARM')) return '';
|
||||
var isArm = (otSel.value === 'TRAILING_ARM');
|
||||
// 계단 검증은 서버 orders/trailing.normalize_steps 와 같은 규칙 — 순서·범위가 어긋나면
|
||||
// 서버가 TRAIL_STEPS 로 거부하므로, PIN 왕복 전에 여기서 같은 문구로 막는다.
|
||||
// 예약 트레일링도 같은 계단 규칙을 쓴다 (기준가만 발동가로 바뀐다).
|
||||
var steps = trailSteps();
|
||||
if(!steps.length) return '계단을 하나 이상 추가하세요';
|
||||
if(steps.length > 5) return '계단은 최대 5개입니다';
|
||||
@@ -12637,13 +12838,24 @@ function trailBlockReason(){
|
||||
}
|
||||
var cur = (state.lastCheck && state.lastCheck.cur_price) || (state.lastBook && state.lastBook.price) || 0;
|
||||
if(!(cur > 0)) return '현재가를 받는 중입니다';
|
||||
// 가장 얕은 계단이 제일 먼저 발동한다 — 이게 현재가 이상이면 등록 즉시 터진다.
|
||||
// 예약 트레일링: 발동가가 현재가 위여야 한다. 이미 닿아 있으면 등록 직후 발동하므로
|
||||
// 서버가 ARM_ALREADY_REACHED 로 거부한다 — 같은 조건을 여기서 먼저 막는다.
|
||||
var basis = cur;
|
||||
if(isArm){
|
||||
var ap = armPrice();
|
||||
if(!(ap > 0)) return '발동가를 입력하세요';
|
||||
if(ap <= cur) return '발동가 ' + fmt(ap) + '원이 현재가 ' + fmt(cur) +
|
||||
'원 이하입니다 (이미 도달 — 일반 트레일링으로 등록하세요)';
|
||||
basis = ap;
|
||||
}
|
||||
// 가장 얕은 계단이 제일 먼저 발동한다 — 이게 기준가 이상이면 시작하는 순간 터진다.
|
||||
var minPrice = trailMinPrice();
|
||||
var r = trailCondPrice(cur, steps[0].pct, minPrice); // 초기 고점 = 현재가
|
||||
if(r.cond >= cur){
|
||||
var r = trailCondPrice(basis, steps[0].pct, minPrice);
|
||||
if(r.cond >= basis){
|
||||
var refW = isArm ? '발동가' : '현재가';
|
||||
return r.floorApplied
|
||||
? ('최저 매도가 ' + fmt(minPrice) + '원이 현재가 ' + fmt(cur) + '원보다 높아 즉시 발동됩니다')
|
||||
: ('1단계 손절선 ' + fmt(r.cond) + '원이 현재가 ' + fmt(cur) + '원 이상이라 즉시 발동됩니다');
|
||||
? ('최저 매도가 ' + fmt(minPrice) + '원이 ' + refW + ' ' + fmt(basis) + '원보다 높아 즉시 발동됩니다')
|
||||
: ('1단계 손절선 ' + fmt(r.cond) + '원이 ' + refW + ' ' + fmt(basis) + '원 이상이라 즉시 발동됩니다');
|
||||
}
|
||||
var qty = parseInt(($('[data-order-qty]')||{}).value || '0', 10) || 0;
|
||||
if(qty > 0){
|
||||
@@ -12722,8 +12934,15 @@ function onSymbolChange(){
|
||||
$('[data-order-price-input]').value = '';
|
||||
$('[data-order-qty]').value = '';
|
||||
var stopInp = $('[data-order-stop-input]'); if(stopInp) stopInp.value = '';
|
||||
// 종목 변경 시 스톱지정가는 해제 — 이전 종목 트리거가 새 종목에 잘못 적용 방지
|
||||
var otSelR = $('[data-order-type]'); if(otSelR && otSelR.value === 'STOP_LIMIT'){ otSelR.value = 'LIMIT'; refreshStopUI(); }
|
||||
// 발동가·최저 매도가는 절대가격이라 종목이 바뀌면 반드시 비운다 — 남으면 엉뚱한 가격으로
|
||||
// 예약이 나간다(계단은 %라 종목이 바뀌어도 뜻이 유지되므로 그대로 둔다).
|
||||
var armInp = $('[data-order-arm-price]'); if(armInp) armInp.value = '';
|
||||
var minInp = $('[data-order-trail-min]'); if(minInp) minInp.value = '';
|
||||
// 종목 변경 시 스톱지정가·예약 트레일링은 해제 — 이전 종목 트리거가 새 종목에 잘못 적용 방지
|
||||
var otSelR = $('[data-order-type]');
|
||||
if(otSelR && (otSelR.value === 'STOP_LIMIT' || otSelR.value === 'TRAILING_ARM')){
|
||||
otSelR.value = 'LIMIT'; refreshStopUI();
|
||||
}
|
||||
var budgetInp = $('[data-order-budget]');
|
||||
if(budgetInp) budgetInp.value = '';
|
||||
// 종목 변경 시 새 max로 자동 채움 (SELL: 보유 100%, BUY: 매수가능 max)
|
||||
@@ -12914,6 +13133,12 @@ function doPropose(){
|
||||
// 계단 검증·즉시발동 검사는 입력 시점에 trailBlockReason 이 하고, 걸리면 매도 버튼이
|
||||
// 비활성이라 여기 도달하지 않는다. 서버(TRAIL_STEPS/TRAIL_IMMEDIATE)가 최후 방어선.
|
||||
}
|
||||
if(orderType === 'TRAILING_ARM'){
|
||||
if(state.side !== 'SELL'){ setMsg('예약 트레일링은 매도만 가능합니다', 'error'); return; }
|
||||
if(armPrice() <= 0){ setMsg('발동가를 입력하세요', 'error'); return; }
|
||||
// 발동가 ≤ 현재가·계단 오류도 trailBlockReason 이 입력 시점에 버튼을 잠근다.
|
||||
// 서버(ARM_ALREADY_REACHED/ARM_IMMEDIATE/TRAIL_STEPS)가 최후 방어선.
|
||||
}
|
||||
if(orderType === 'STOP_LIMIT'){
|
||||
if(state.side !== 'SELL'){ setMsg('스톱지정가는 매도만 가능합니다', 'error'); return; }
|
||||
if(price <= 0){ setMsg('스톱지정가는 매도 단가가 필요합니다', 'error'); return; }
|
||||
@@ -12938,7 +13163,7 @@ function doPropose(){
|
||||
// 검증 통과 — 매수/매도 방향 최종 확인 팝업 (오타 방지). 확인 시에만 실제 매매로 진행.
|
||||
state.pendingOrder = {account: account, orderType: orderType, qty: qty, price: price,
|
||||
stopPrice: stopPrice, trailSteps: trailStepList,
|
||||
trailMinPrice: trailMin};
|
||||
trailMinPrice: trailMin, armPrice: armPrice()};
|
||||
openOrderConfirm();
|
||||
}
|
||||
// 방향 확인 후 실제 매매 진행 — 전량매도 sell-choice 분기 포함.
|
||||
@@ -12947,8 +13172,9 @@ function doProposeConfirmed(){
|
||||
var account = po.account, orderType = po.orderType, qty = po.qty, price = po.price;
|
||||
var lc = state.lastCheck;
|
||||
// 전량매도(입력 수량 = 매도가능 전량)이고 같은 소유자 그룹의 다른 계좌에도 같은 종목 보유
|
||||
// → 선택 팝업. 스톱지정가·트레일링은 단일 계좌 예약이라 2계좌 동시 매도 대상 아님.
|
||||
if(state.side === 'SELL' && orderType !== 'STOP_LIMIT' && orderType !== 'TRAILING_STOP'){
|
||||
// → 선택 팝업. 스톱지정가·트레일링·예약 트레일링은 단일 계좌 예약이라 대상 아님.
|
||||
if(state.side === 'SELL' && orderType !== 'STOP_LIMIT' && orderType !== 'TRAILING_STOP'
|
||||
&& orderType !== 'TRAILING_ARM'){
|
||||
var fullSell = lc && typeof lc.max_qty === 'number' && lc.max_qty > 0 && qty === lc.max_qty;
|
||||
var sib = siblingAccount(account);
|
||||
var sibInfo = (sib && state.accStatus && state.accStatus.byLabel) ? state.accStatus.byLabel[sib] : null;
|
||||
@@ -12958,7 +13184,8 @@ function doProposeConfirmed(){
|
||||
return;
|
||||
}
|
||||
}
|
||||
proposeSingle(account, orderType, qty, price, po.stopPrice, po.trailSteps, po.trailMinPrice);
|
||||
proposeSingle(account, orderType, qty, price, po.stopPrice, po.trailSteps, po.trailMinPrice,
|
||||
po.armPrice);
|
||||
}
|
||||
// ── 매수/매도 방향 확인 모달 ──
|
||||
// 매수·매도 버튼을 둘 다 띄우고 관리자님이 직접 맞는 방향을 누르게 함(위치 랜덤).
|
||||
@@ -12969,13 +13196,16 @@ function openOrderConfirm(){
|
||||
var po = state.pendingOrder; if(!po) return;
|
||||
var disp = {}; ACCOUNTS.forEach(function(a){ disp[a.label] = a.display; });
|
||||
var isStop = (po.orderType === 'STOP_LIMIT');
|
||||
var isTrail = (po.orderType === 'TRAILING_STOP');
|
||||
var isArm = (po.orderType === 'TRAILING_ARM');
|
||||
var isTrail = (po.orderType === 'TRAILING_STOP') || isArm;
|
||||
var typeStr = (po.orderType === 'MARKET') ? '시장가'
|
||||
: isTrail ? '트레일링 스톱(예약)'
|
||||
: isArm ? '예약 트레일링(발동 대기)'
|
||||
: (po.orderType === 'TRAILING_STOP') ? '트레일링 스톱(예약)'
|
||||
: isStop ? '스톱지정가(예약)' : '지정가';
|
||||
var curNow = (state.lastCheck && state.lastCheck.cur_price) || (state.lastBook && state.lastBook.price) || 0;
|
||||
// 트레일링은 단가를 서버가 계산 — 확인 팝업엔 예상 손절선을 보여준다.
|
||||
var trailPeak = isTrail ? curNow : 0; // 초기 고점 = 등록 시점 현재가
|
||||
// 기준 고점 = 예약이면 발동가, 일반 트레일링이면 등록 시점 현재가.
|
||||
var trailPeak = isArm ? (po.armPrice || 0) : (isTrail ? curNow : 0);
|
||||
var trailStepsPo = (po.trailSteps || []);
|
||||
var trailRes = (isTrail && trailStepsPo.length)
|
||||
? trailCondPrice(trailPeak, trailStepsPo[0].pct, po.trailMinPrice)
|
||||
@@ -13004,12 +13234,27 @@ function openOrderConfirm(){
|
||||
fmt(poQtys[i]) + '주 ' +
|
||||
'<span class="muted small">(' + (s.cum >= 100 ? '전량' : '누적 ' + s.cum + '%') + ')</span><br>';
|
||||
}).join('');
|
||||
priceLine = '· 시작 고점: <b>' + fmt(trailPeak) + '원</b> <span class="muted small">(등록 시점 현재가)</span><br>' +
|
||||
var upLimC = (state.lastBook && state.lastBook.upper_limit) || 0;
|
||||
var armWarn = (isArm && upLimC > 0 && trailPeak > upLimC)
|
||||
? ('· <b style="color:#e06c62;">⚠️ 오늘 상한가 ' + fmt(upLimC) + '원 초과 — 오늘은 도달 불가, ' +
|
||||
'장 마감에 만료됩니다</b><br>')
|
||||
: '';
|
||||
priceLine = armWarn + (isArm
|
||||
? ('· 발동가: <b>' + fmt(trailPeak) + '원 도달 시 시작</b> <span class="muted small">(현재가 ' +
|
||||
fmt(curNow) + '원 · +' + (curNow ? ((trailPeak - curNow) / curNow * 100).toFixed(1) : '0') +
|
||||
'%)</span><br>')
|
||||
: ('· 시작 고점: <b>' + fmt(trailPeak) + '원</b> <span class="muted small">(등록 시점 현재가)</span><br>')) +
|
||||
(po.trailMinPrice > 0 ? ('· 최저 매도가: <b>' + fmt(po.trailMinPrice) + '원</b> <span class="muted small">(' +
|
||||
(trailRes.floorApplied ? '최저가 적용' : '트레일 적용') + ')</span><br>') : '') +
|
||||
'· 매도 계단 <b>' + trailStepsPo.length + '단</b> <span class="muted small">(현재가 ' + fmt(curNow) + '원)</span><br>' +
|
||||
'· 매도 계단 <b>' + trailStepsPo.length + '단</b>' +
|
||||
(isArm ? ' <span class="muted small">(발동가 기준 예상)</span>'
|
||||
: ' <span class="muted small">(현재가 ' + fmt(curNow) + '원)</span>') + '<br>' +
|
||||
stepLines +
|
||||
'· <span class="muted small">고점이 오르면 계단이 통째로 따라 올라갑니다</span><br>';
|
||||
(isArm
|
||||
? '· <b>지금은 주문이 나가지 않습니다</b> <span class="muted small">— 발동가에 닿는 순간 ' +
|
||||
'PIN 재확인 없이 자동 발주됩니다. 실제 손절선은 발동 시점 현재가 기준이라 위 값 이상입니다.</span><br>' +
|
||||
'· <b>오늘 장 마감까지만 유효</b> <span class="muted small">(미발동 시 자동 취소)</span><br>'
|
||||
: '· <span class="muted small">고점이 오르면 계단이 통째로 따라 올라갑니다</span><br>');
|
||||
} else {
|
||||
priceLine = '· ' + (isStop ? '매도 단가' : '단가') + ': <b>' + priceStr + '</b><br>';
|
||||
}
|
||||
@@ -13046,7 +13291,8 @@ function siblingAccount(label){
|
||||
var sib = ACCOUNTS.find(function(a){ return a.owner === me.owner && a.label !== label; });
|
||||
return sib ? sib.label : null;
|
||||
}
|
||||
function proposeSingle(account, orderType, qty, price, stopPrice, trailStepList, trailMinPrice){
|
||||
function proposeSingle(account, orderType, qty, price, stopPrice, trailStepList, trailMinPrice,
|
||||
armPriceV){
|
||||
var body = new URLSearchParams();
|
||||
body.set('account', account);
|
||||
body.set('side', state.side);
|
||||
@@ -13058,12 +13304,14 @@ function proposeSingle(account, orderType, qty, price, stopPrice, trailStepList,
|
||||
if(orderType === 'STOP_LIMIT') body.set('stop_price', String(stopPrice || 0));
|
||||
// 트레일링은 계단 정의(하락률·누적 비중)와 최저 매도가만 보낸다 —
|
||||
// 계단별 조건단가·지정가·주수는 서버가 현재가·보유수량 기준으로 계산한다.
|
||||
if(orderType === 'TRAILING_STOP'){
|
||||
if(orderType === 'TRAILING_STOP' || orderType === 'TRAILING_ARM'){
|
||||
// 승인 팝업에서 확정된 계단을 그대로 보낸다 — 팝업이 떠 있는 사이 편집기를 건드려도
|
||||
// 관리자님이 확인한 내용과 발주가 어긋나지 않는다.
|
||||
body.set('trail_steps', JSON.stringify(trailStepList || []));
|
||||
if(trailMinPrice > 0) body.set('min_sell_price', String(trailMinPrice));
|
||||
}
|
||||
// 예약 트레일링은 발동가가 승인 대상의 일부 — 계단과 같은 스냅샷에서 온다.
|
||||
if(orderType === 'TRAILING_ARM') body.set('trigger_price', String(armPriceV || 0));
|
||||
proposeAndOpenPin('/api/order/propose', body);
|
||||
}
|
||||
function doProposeMulti(opts){
|
||||
@@ -13210,6 +13458,13 @@ if(openOrdersModal) openOrdersModal.addEventListener('click', function(e){
|
||||
cancelOpenOrder(row.getAttribute('data-ord-no'), row.getAttribute('data-account'), cancelBtn);
|
||||
return;
|
||||
}
|
||||
var armBtn = t.closest && t.closest('[data-cancel-arm]');
|
||||
if(armBtn){
|
||||
var arow = armBtn.closest('.open-order-row');
|
||||
if(!arow) return;
|
||||
cancelArm(arow.getAttribute('data-arm-id'), armBtn);
|
||||
return;
|
||||
}
|
||||
});
|
||||
// PIN 모달 click 핸들러
|
||||
if(pinModal) pinModal.addEventListener('click', function(e){
|
||||
@@ -13297,7 +13552,7 @@ if(qtyInpEl) qtyInpEl.addEventListener('input', function(){
|
||||
recalcBudgetFromQty(); renderOrderInfo();
|
||||
// 계단별 주수는 수량에서 나온다 — 수량이 바뀌면 미리보기도 다시 그린다.
|
||||
var ot = $('[data-order-type]');
|
||||
if(ot && ot.value === 'TRAILING_STOP') renderTrailPreview();
|
||||
if(ot && (ot.value === 'TRAILING_STOP' || ot.value === 'TRAILING_ARM')) renderTrailPreview();
|
||||
});
|
||||
var budgetInpEl = $('[data-order-budget]');
|
||||
if(budgetInpEl) budgetInpEl.addEventListener('input', recalcQtyFromBudget);
|
||||
@@ -13306,6 +13561,8 @@ var trailPresetEl = $('[data-trail-preset]');
|
||||
if(trailPresetEl) trailPresetEl.addEventListener('change', applyTrailPreset);
|
||||
var trailMinEl = $('[data-order-trail-min]');
|
||||
if(trailMinEl) trailMinEl.addEventListener('input', renderTrailPreview);
|
||||
var armPriceEl = $('[data-order-arm-price]');
|
||||
if(armPriceEl) armPriceEl.addEventListener('input', renderTrailPreview);
|
||||
// 계단 입력·삭제는 행이 동적으로 다시 그려지므로 컨테이너에 위임한다.
|
||||
var trailStepsEl = $('[data-trail-steps]');
|
||||
if(trailStepsEl){
|
||||
@@ -14205,6 +14462,9 @@ class Handler(BaseHTTPRequestHandler):
|
||||
# 트레일링 예약이면 그 상태를 실어 보낸다 (예약 파일 읽기, 키움 콜 0).
|
||||
for _r in rows:
|
||||
_r['trailing'] = _trailing_info_for(_r.get('ord_no', ''))
|
||||
# 대기 예약은 키움에 주문이 없어 ka10075 에 안 잡힌다 → 합성 행으로 합류시킨다.
|
||||
# 안 보이는 자동 발주 예약은 그 자체로 사고 요인이고, 이 팝업이 유일한 창구다.
|
||||
rows.extend(_arm_rows())
|
||||
self._send_json(200, {'rows': rows, 'count': len(rows)})
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
@@ -14246,6 +14506,13 @@ class Handler(BaseHTTPRequestHandler):
|
||||
open_orders_count = len(rows)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
# 대기 예약도 진행중 항목으로 센다 — 그래야 대기 예약만 있을 때도 배지가 뜨고
|
||||
# 버튼이 팝업을 연다(키움 미체결이 0건이면 '진행 중 거래 없음' 토스트로 끝난다).
|
||||
try:
|
||||
from orders import trailing as _trl
|
||||
open_orders_count += len(_trl.list_arms())
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
result['open_orders_count'] = open_orders_count
|
||||
# 진행중 아이템 수 = 미체결 주문 건수(팝업 행과 동일) + 활성 PIN 카드(있으면 1).
|
||||
# 종목코드 중복제거 X — 같은 종목 2계좌 매도도 2건으로 표시(팝업과 일치).
|
||||
@@ -14257,7 +14524,10 @@ class Handler(BaseHTTPRequestHandler):
|
||||
return
|
||||
if parsed.path == '/api/market_state':
|
||||
try:
|
||||
self._send_json(200, _market_phase_state())
|
||||
st = _market_phase_state()
|
||||
# next_in — 다음 phase 경계까지 남은 초. 브라우저가 폴링 시각을 경계에 맞춘다.
|
||||
st['next_in'] = _secs_to_next_phase()
|
||||
self._send_json(200, st)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
self._send_json(500, {'error': f'market_state failed: {e}'})
|
||||
@@ -14473,7 +14743,8 @@ class Handler(BaseHTTPRequestHandler):
|
||||
account = (qs.get('account') or [''])[0].strip()
|
||||
side = (qs.get('side') or [''])[0].strip().upper()
|
||||
order_type = (qs.get('order_type') or ['LIMIT'])[0].strip().upper() or 'LIMIT'
|
||||
if order_type not in ('LIMIT', 'MARKET', 'AGGRESSIVE_LIMIT', 'STOP_LIMIT'):
|
||||
if order_type not in ('LIMIT', 'MARKET', 'AGGRESSIVE_LIMIT', 'STOP_LIMIT',
|
||||
'TRAILING_STOP', 'TRAILING_ARM'):
|
||||
order_type = 'LIMIT'
|
||||
try:
|
||||
price = int((qs.get('price') or ['0'])[0].strip() or '0')
|
||||
@@ -14989,12 +15260,18 @@ class Handler(BaseHTTPRequestHandler):
|
||||
min_sell_price = int(min_sell_raw) if min_sell_raw else None
|
||||
except ValueError:
|
||||
min_sell_price = None
|
||||
trigger_raw = (params.get('trigger_price') or [''])[0].strip()
|
||||
try:
|
||||
trigger_price = int(trigger_raw) if trigger_raw else None
|
||||
except ValueError:
|
||||
trigger_price = None
|
||||
if not account or side not in ('BUY', 'SELL') or not symbol or qty <= 0:
|
||||
self._send_json(400, {'ok': False, 'error': 'account/side/symbol/qty required'})
|
||||
return
|
||||
if order_type not in ('LIMIT', 'MARKET', 'STOP_LIMIT', 'TRAILING_STOP'):
|
||||
if order_type not in ('LIMIT', 'MARKET', 'STOP_LIMIT', 'TRAILING_STOP', 'TRAILING_ARM'):
|
||||
self._send_json(400, {'ok': False,
|
||||
'error': 'order_type must be LIMIT|MARKET|STOP_LIMIT|TRAILING_STOP'})
|
||||
'error': 'order_type must be '
|
||||
'LIMIT|MARKET|STOP_LIMIT|TRAILING_STOP|TRAILING_ARM'})
|
||||
return
|
||||
if order_type == 'LIMIT' and (price is None or price <= 0):
|
||||
self._send_json(400, {'ok': False, 'error': 'LIMIT requires positive price'})
|
||||
@@ -15006,12 +15283,13 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if price is None or price <= 0 or stop_price is None or stop_price <= 0:
|
||||
self._send_json(400, {'ok': False, 'error': '스톱지정가는 단가와 조건단가가 모두 필요'})
|
||||
return
|
||||
if order_type == 'TRAILING_STOP':
|
||||
# 계단별 조건단가·지정가·주수는 handler 가 현재가·보유수량 기준으로 계산 —
|
||||
# 여기선 계단 정의(하락률·누적 비중)만 받는다. 형식 검증은 handler 가
|
||||
# orders/trailing.normalize_steps 로 하므로 여기선 최소한만 본다.
|
||||
if order_type in ('TRAILING_STOP', 'TRAILING_ARM'):
|
||||
# 계단별 조건단가·지정가·주수는 handler 가 계산 — 여기선 계단 정의(하락률·누적
|
||||
# 비중)만 받는다. 형식 검증은 handler 가 orders/trailing.normalize_steps 로
|
||||
# 하므로 여기선 최소한만 본다. 예약 트레일링은 발동가가 하나 더 붙는다.
|
||||
kind_label = '예약 트레일링' if order_type == 'TRAILING_ARM' else '트레일링 스톱'
|
||||
if side != 'SELL':
|
||||
self._send_json(400, {'ok': False, 'error': '트레일링 스톱은 매도만 지원'})
|
||||
self._send_json(400, {'ok': False, 'error': f'{kind_label}은 매도만 지원'})
|
||||
return
|
||||
if not isinstance(trail_steps, list) or not trail_steps:
|
||||
self._send_json(400, {'ok': False, 'error': '매도 계단을 입력하세요'})
|
||||
@@ -15019,6 +15297,9 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if min_sell_price is not None and min_sell_price <= 0:
|
||||
self._send_json(400, {'ok': False, 'error': '최저 매도가는 0보다 커야 합니다'})
|
||||
return
|
||||
if order_type == 'TRAILING_ARM' and (trigger_price is None or trigger_price <= 0):
|
||||
self._send_json(400, {'ok': False, 'error': '발동가를 입력하세요'})
|
||||
return
|
||||
try:
|
||||
if not symbol_name:
|
||||
import kiwoom_client as kc
|
||||
@@ -15035,8 +15316,11 @@ class Handler(BaseHTTPRequestHandler):
|
||||
qty=qty, order_type=order_type,
|
||||
price=(price if order_type in ('LIMIT', 'STOP_LIMIT') else None),
|
||||
stop_price=(stop_price if order_type == 'STOP_LIMIT' else None),
|
||||
trail_steps=(trail_steps if order_type == 'TRAILING_STOP' else None),
|
||||
min_sell_price=(min_sell_price if order_type == 'TRAILING_STOP' else None),
|
||||
trail_steps=(trail_steps
|
||||
if order_type in ('TRAILING_STOP', 'TRAILING_ARM') else None),
|
||||
min_sell_price=(min_sell_price
|
||||
if order_type in ('TRAILING_STOP', 'TRAILING_ARM') else None),
|
||||
trigger_price=(trigger_price if order_type == 'TRAILING_ARM' else None),
|
||||
)
|
||||
if res.get('ok'):
|
||||
# 매수/매도 미리보기 카드 메시지·PIN 메시지 모두 텔레그램 발송 X.
|
||||
@@ -15180,6 +15464,24 @@ class Handler(BaseHTTPRequestHandler):
|
||||
_bump_panel_signal() # 미체결 취소 → 배지 제거 반영
|
||||
self._send_json(200 if res.get('ok') else 400, res)
|
||||
return
|
||||
if self.path == '/api/orders/arm_cancel':
|
||||
# 대기 예약(예약 트레일링) 취소. 키움 주문이 아니라 상태파일에서 지운다.
|
||||
# 텔레그램 발송 없음 — 실주문 취소가 아니므로 토스트로 충분하다.
|
||||
arm_id = (params.get('arm_id') or [''])[0].strip()
|
||||
if not arm_id:
|
||||
self._send_json(400, {'ok': False, 'error': 'arm_id required'})
|
||||
return
|
||||
try:
|
||||
from orders import handler
|
||||
res = handler.cancel_trailing_arm(arm_id)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
self._send_json(500, {'ok': False, 'error': f'arm cancel failed: {e}'})
|
||||
return
|
||||
if res.get('ok'):
|
||||
_bump_panel_signal() # 대기 예약 취소 → 배지 반영
|
||||
self._send_json(200 if res.get('ok') else 400, res)
|
||||
return
|
||||
if self.path == '/api/order/cancel':
|
||||
# 거래 모달 닫기 시 활성 카드 정리 (자동). 텔레그램 알림 X — 웹 자동 cancel은 사용자가 명시 액션 아님.
|
||||
try:
|
||||
|
||||
@@ -75,6 +75,8 @@ def format_card(request: dict, market_data: dict, card_id: str,
|
||||
type_marker = f' {cfg["warning_emoji"]} 스톱지정가(예약)'
|
||||
elif request['order_type'] == 'TRAILING_STOP':
|
||||
type_marker = f' {cfg["warning_emoji"]} 트레일링 스톱(예약)'
|
||||
elif request['order_type'] == 'TRAILING_ARM':
|
||||
type_marker = f' {cfg["warning_emoji"]} 예약 트레일링(발동 대기)'
|
||||
amend_marker = ' ✏️ 수정됨' if amended else ''
|
||||
|
||||
lines = [f'{side_emoji} {_md_bold(side_word + " 미리보기")} [#{card_id}]{type_marker}{spouse_marker}{amend_marker}', '']
|
||||
@@ -98,15 +100,29 @@ def format_card(request: dict, market_data: dict, card_id: str,
|
||||
cur_part = f' (현재가 {_money(cur)})' if cur else ''
|
||||
lines.append(f'{marker} 조건: {_md_bold(_money(stop_p) + " 도달 시")}{cur_part}')
|
||||
lines.append(f'{marker} 매도가격: {_md_bold(_money(request["price"]))} (지정가)')
|
||||
elif request['order_type'] == 'TRAILING_STOP':
|
||||
elif request['order_type'] in ('TRAILING_STOP', 'TRAILING_ARM'):
|
||||
# 승인 대상은 "계단 정의" — 각 계단의 조건단가는 고점이 오르면 제 하락률을 유지하며
|
||||
# 함께 따라 올라간다. 계단이 1개면 종전의 단일 트레일링과 같다.
|
||||
is_arm = request['order_type'] == 'TRAILING_ARM'
|
||||
cur = market_data.get('current_price')
|
||||
cur_part = f' (현재가 {_money(cur)})' if cur else ''
|
||||
# 초기 고점은 항상 등록 시점 현재가 — 과거 고점 기준 옵션은 2026-07-30 제거됨.
|
||||
# 예약 트레일링은 그 자리에 발동가가 들어간다(계단 계산 기준도 발동가).
|
||||
peak_v = request.get('trail_peak') or 0
|
||||
steps = request.get('trail_steps') or []
|
||||
lines.append(f'{marker} 시작 고점: {_md_bold(_money(peak_v))} (등록 시점){cur_part}')
|
||||
if is_arm:
|
||||
gap = ''
|
||||
if cur:
|
||||
gap = f' — 발동까지 {_pct((peak_v - cur) / cur * 100)}'
|
||||
lines.append(f'{marker} 발동가: {_md_bold(_money(peak_v) + " 도달 시 시작")}{cur_part}{gap}')
|
||||
# 대기 예약이 당일 유효로 바뀐 뒤로는 상한가 초과 발동가가 '오늘 도달 불가' 확정이다.
|
||||
# 거부하지는 않는다(관리자님 지시) — 무의미하다는 사실만 승인 전에 알린다.
|
||||
up_lim = market_data.get('upper_limit') or 0
|
||||
if up_lim and peak_v > up_lim:
|
||||
lines.append(f'{cfg["warning_emoji"]} 오늘 상한가 {_money(up_lim)} 초과 — '
|
||||
f'오늘은 도달 불가, 장 마감에 만료됩니다')
|
||||
else:
|
||||
lines.append(f'{marker} 시작 고점: {_md_bold(_money(peak_v))} (등록 시점){cur_part}')
|
||||
min_sell = request.get('min_sell_price')
|
||||
if min_sell:
|
||||
# 최저 매도가가 손절선을 결정했는지(=트레일보다 높았는지) 명시 — 둘 중 어느 게
|
||||
@@ -115,7 +131,10 @@ def format_card(request: dict, market_data: dict, card_id: str,
|
||||
who = f'{tied}개 계단 적용' if tied else '미적용 (트레일이 더 높음)'
|
||||
lines.append(f'{marker} 최저 매도가: {_md_bold(_money(min_sell))} ({who})')
|
||||
lines.append('')
|
||||
lines.append(f'{marker} 매도 계단 {len(steps)}단')
|
||||
steps_head = f'{marker} 매도 계단 {len(steps)}단'
|
||||
if is_arm:
|
||||
steps_head += ' (발동가 기준 예상)'
|
||||
lines.append(steps_head)
|
||||
for s in steps:
|
||||
cum = s.get('cum', 0)
|
||||
cum_part = '전량' if cum >= 100 else f'누적 {_num(cum)}%'
|
||||
@@ -127,6 +146,14 @@ def format_card(request: dict, market_data: dict, card_id: str,
|
||||
lines.append(f' ↳ 나머지 {rest:,}주는 스톱 없음 (계속 보유)')
|
||||
lines.append('')
|
||||
lines.append(f'{marker} 고점이 오르면 계단이 통째로 따라 올라갑니다 (내려가지 않음)')
|
||||
if is_arm:
|
||||
# 이 기능의 유일한 위험(발동 시 PIN 재확인 없음)을 승인 화면에서 고지하는 자리다.
|
||||
# 실제 손절선이 미리보기 이상인 이유: 발동 시점 현재가 ≥ 발동가.
|
||||
lines.append(f'{marker} 지금은 주문이 나가지 않습니다 — '
|
||||
f'발동가에 닿는 순간 PIN 재확인 없이 위 계단이 자동 발주됩니다')
|
||||
lines.append(f'{marker} {_md_bold("오늘 장 마감까지만 유효")} — '
|
||||
f'미발동이면 자동 취소됩니다 (스톱주문과 같은 당일 유효)')
|
||||
lines.append(f'{marker} 실제 손절선은 발동 시점 현재가 기준이라 위 값 이상입니다')
|
||||
elif request['order_type'] == 'MARKET':
|
||||
lines.append(f'{marker} 가격: {_md_bold("시장가")}')
|
||||
else:
|
||||
@@ -293,6 +320,45 @@ def format_trailing_submitted(info: dict) -> str:
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def format_arm_registered(info: dict) -> str:
|
||||
"""예약 트레일링 등록 완료 — 아직 키움에 주문이 없다는 사실을 분명히 적는다."""
|
||||
acct = _account_display(info.get('account', ''))
|
||||
head = '🧪 [모의] 예약 트레일링' if info.get('dry_run') else '⏳ 예약 트레일링'
|
||||
steps = info.get('steps') or []
|
||||
lines = [f'{head} 등록: {info["symbol_name"]} ({info.get("symbol", "")})',
|
||||
f'[{acct}] {info["qty"]:,}주 · 발동가 {_money(info["trigger_price"])} 도달 시 시작']
|
||||
if info.get('cur_price'):
|
||||
cur = info['cur_price']
|
||||
gap = (info['trigger_price'] - cur) / cur * 100
|
||||
lines.append(f'현재가 {_money(cur)} · 발동까지 {_pct(gap)}')
|
||||
if info.get('min_sell_price'):
|
||||
lines.append(f'최저 매도가 {_money(info["min_sell_price"])}')
|
||||
step_parts = []
|
||||
for s in steps:
|
||||
cum = s.get('cum', 0)
|
||||
cum_part = '전량' if cum >= 100 else f'누적 {_num(cum)}%'
|
||||
step_parts.append(f'−{_num(s["pct"])}% {cum_part}')
|
||||
lines.append(f'매도 계단 {len(steps)}단: ' + ' · '.join(step_parts))
|
||||
if info.get('arm_id'):
|
||||
lines.append(f'대기 시작 ({info["arm_id"]}) — **오늘 장 마감까지** 유효')
|
||||
lines.append('지금은 키움에 주문이 없습니다. 발동가에 닿으면 PIN 재확인 없이 자동 발주됩니다.')
|
||||
lines.append('미발동으로 장이 끝나면 자동 취소됩니다 (스톱주문과 같은 당일 유효).')
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def format_arm_fired(info: dict) -> str:
|
||||
"""예약 트레일링 발동 — 계단 접수 결과는 format_trailing_submitted 와 같은 모양이라 재사용하고,
|
||||
발동 경위(발동가·실제 발동가·수량 축소)만 앞뒤에 덧붙인다."""
|
||||
head = [f'🎯 예약 트레일링 발동 — {info["symbol_name"]} ({info.get("symbol", "")})',
|
||||
f'발동가 {_money(info["trigger_price"])} 도달 (현재가 {_money(info["peak"])})']
|
||||
if info.get('qty_reduced'):
|
||||
head.append(f'⚠️ 매도가능 {info["total_qty"]:,}주 — '
|
||||
f'예약 {info["arm_qty"]:,}주에서 줄여 재배분했습니다')
|
||||
body = format_trailing_submitted(info)
|
||||
tail = ['※ 스톱주문은 장 마감 시 소멸합니다 (자동 재등록 안 함)']
|
||||
return '\n'.join(head + ['', body] + tail)
|
||||
|
||||
|
||||
def format_expired(card_id: str, side: str) -> str:
|
||||
side_word = '매수' if side == 'BUY' else '매도'
|
||||
return f'⏱️ [#{card_id}] 승인 만료. {side_word}가 취소되었습니다.'
|
||||
|
||||
@@ -130,6 +130,15 @@ def _safe_broker_executions(account_label: str, now: datetime) -> tuple[Optional
|
||||
return executions, None
|
||||
|
||||
|
||||
def nxt_eligible(symbol: str) -> bool:
|
||||
"""NXT 거래가능 여부 (종목코드만으로). 캐시 읽기라 API 콜 0.
|
||||
|
||||
collect_market_data 를 거치지 않는 호출자용 — 트레일링 감시 루프가 대기 예약을
|
||||
발동시킬 때 라우팅(_AL/_KRX)을 정하는 데 쓴다.
|
||||
"""
|
||||
return _nxt_eligible(symbol, {})
|
||||
|
||||
|
||||
def collect_market_data(account_label: str, symbol: str, side: str, qty: int) -> dict:
|
||||
now = datetime.now(KST)
|
||||
quote = _safe_quote(symbol, account_label)
|
||||
|
||||
@@ -89,6 +89,17 @@ def session_at(now: datetime) -> str:
|
||||
return 'CLOSED'
|
||||
|
||||
|
||||
def after_trading_day(now: datetime) -> bool:
|
||||
"""그날 거래 세션이 전부 끝났는가 (NXT 애프터 마감 = 20:00 이후).
|
||||
|
||||
⚠️ `session_at(now) == 'CLOSED'` 로 대신하면 안 된다 — 그 판정은 **장 시작 전 새벽**과
|
||||
**09:00:00~09:00:30 틈**(NXT 프리 종료와 정규장 시작 사이)에도 CLOSED 를 돌려주므로,
|
||||
당일 유효기간을 재는 데 쓰면 아침에 멀쩡한 예약이 만료로 잡힌다.
|
||||
"""
|
||||
th = _limits()['trading_hours']
|
||||
return now.replace(tzinfo=None).time() >= _parse_hms(th['nxt_after_end'])
|
||||
|
||||
|
||||
def is_today_holiday(now: datetime) -> bool:
|
||||
th = _limits()['trading_hours']
|
||||
rel = th.get('holiday_state_file')
|
||||
@@ -103,6 +114,11 @@ def is_today_holiday(now: datetime) -> bool:
|
||||
return False
|
||||
today = now.strftime('%Y-%m-%d')
|
||||
holidays = data.get('holidays') if isinstance(data, dict) else data
|
||||
# holiday_sync.py 가 쓰는 실제 형태는 {날짜: 이름} dict 다. list 만 받던 시절의
|
||||
# 코드가 남아 있어 dict 를 만나면 조용히 False 를 반환했고(= 휴장일 가드가 통째로
|
||||
# 무력화), 2026-08-25 예약 트레일링 작업에서 발견해 dict 를 먼저 처리하게 했다.
|
||||
if isinstance(holidays, dict):
|
||||
return today in holidays
|
||||
if not isinstance(holidays, list):
|
||||
return False
|
||||
for h in holidays:
|
||||
@@ -504,7 +520,7 @@ def validate_request(request: dict, market_data: dict) -> Result:
|
||||
if request['side'] not in ('BUY', 'SELL'):
|
||||
return Result.REJECT('INVALID_SIDE', f'잘못된 방향: {request["side"]}')
|
||||
if request['order_type'] not in ('LIMIT', 'MARKET', 'AGGRESSIVE_LIMIT', 'STOP_LIMIT',
|
||||
'TRAILING_STOP'):
|
||||
'TRAILING_STOP', 'TRAILING_ARM'):
|
||||
return Result.REJECT('INVALID_ORDER_TYPE', f'잘못된 주문방식: {request["order_type"]}')
|
||||
r = validate_trading_hours(market_data['now'], market_data.get('is_holiday', False),
|
||||
market_data.get('nxt_eligible', False))
|
||||
@@ -548,6 +564,20 @@ def validate_request(request: dict, market_data: dict) -> Result:
|
||||
return Result.REJECT('STOP_TRIGGER_NOT_BELOW',
|
||||
f'조건단가 {stop_price:,}원 ≥ 현재가 {cur:,}원 — '
|
||||
f'하락 시 매도(스톱)는 현재가보다 낮게 설정해야 함')
|
||||
if request['order_type'] == 'TRAILING_ARM':
|
||||
# 예약 트레일링은 지금 나가는 주문이 아니라 "발동가에 닿으면 시작" 하는 예약이다.
|
||||
# 그래서 스톱지정가와 두 가지 검증을 일부러 건너뛴다:
|
||||
# · 가격제한폭 — 발동가는 오늘 상한가 위여도 통과시킨다. 실제 발주는 발동 시점의
|
||||
# 현재가로 계산되므로 그때 검증된다. arm 이 당일 유효가 된 뒤로는 상한가 초과가
|
||||
# "오늘 도달 불가 = 그냥 만료" 를 뜻하지만, **막지 않고 경고만** 한다
|
||||
# (2026-08-25 관리자님 결정). 경고는 승인 카드(card.format_card)와 웹
|
||||
# 미리보기·확인 팝업 3곳에 있다 — 여기서 거부로 바꾸지 말 것.
|
||||
# · 조건단가 < 현재가 — 발동가는 정의상 현재가보다 위다. 이 검사를 태우면 전부 거부된다.
|
||||
# 아래 매도 보유수량 검증은 그대로 받는다 — 지금 없는 종목을 예약해봐야 무의미하다.
|
||||
if request['side'] != 'SELL':
|
||||
return Result.REJECT('ARM_SELL_ONLY', '예약 트레일링은 매도 전용')
|
||||
if not request.get('trigger_price'):
|
||||
return Result.REJECT('ARM_TRIGGER', '발동가를 입력하세요')
|
||||
if request['side'] == 'BUY':
|
||||
basis = None
|
||||
if request['order_type'] == 'MARKET':
|
||||
|
||||
@@ -167,11 +167,13 @@ def propose_trade(
|
||||
stop_price: Optional[int] = None,
|
||||
trail_steps: Optional[list] = None,
|
||||
min_sell_price: Optional[int] = None,
|
||||
trigger_price: Optional[int] = None,
|
||||
) -> dict:
|
||||
sidecar.guard_or_raise()
|
||||
_sweep_expired_and_notify()
|
||||
|
||||
if order_type not in ('LIMIT', 'MARKET', 'AGGRESSIVE_LIMIT', 'STOP_LIMIT', 'TRAILING_STOP'):
|
||||
if order_type not in ('LIMIT', 'MARKET', 'AGGRESSIVE_LIMIT', 'STOP_LIMIT',
|
||||
'TRAILING_STOP', 'TRAILING_ARM'):
|
||||
return {'ok': False,
|
||||
'message': card.format_rejected('INVALID_ORDER_TYPE', f'잘못된 주문방식: {order_type}')}
|
||||
if side not in ('BUY', 'SELL'):
|
||||
@@ -186,12 +188,19 @@ def propose_trade(
|
||||
'message': card.format_rejected('STOP_INPUT',
|
||||
'스톱지정가는 지정가와 조건단가(트리거)가 모두 필요')}
|
||||
norm_steps = None
|
||||
if order_type == 'TRAILING_STOP':
|
||||
if order_type in ('TRAILING_STOP', 'TRAILING_ARM'):
|
||||
# 트레일링은 계단 정의(하락률·누적 비중)만 입력받고, 계단별 조건단가·지정가·주수는
|
||||
# 현재가와 보유수량 기준으로 자동 계산한다 (md 수집 후).
|
||||
#
|
||||
# 예약 트레일링(TRAILING_ARM)은 같은 입력에 발동가가 하나 더 붙고, 계단 계산의 기준을
|
||||
# 현재가 대신 발동가로 쓴다. 그 외 검증은 전부 공유한다 — 발동하면 결국 같은 물건이
|
||||
# 되므로 여기서 갈라놓으면 한쪽만 고쳐지는 사고가 난다.
|
||||
is_arm = order_type == 'TRAILING_ARM'
|
||||
kind = '예약 트레일링' if is_arm else '트레일링 스톱'
|
||||
if side != 'SELL':
|
||||
return {'ok': False,
|
||||
'message': card.format_rejected('STOP_SELL_ONLY', '트레일링 스톱은 매도 전용')}
|
||||
'message': card.format_rejected(
|
||||
'ARM_SELL_ONLY' if is_arm else 'STOP_SELL_ONLY', f'{kind}은 매도 전용')}
|
||||
try:
|
||||
norm_steps = trailing.normalize_steps(trail_steps or [])
|
||||
except (ValueError, KeyError, TypeError) as e:
|
||||
@@ -201,11 +210,15 @@ def propose_trade(
|
||||
# 계단별 주수는 보유수량을 나눠 만든다 — 예산 환산이 끼면 기준이 두 개가 된다.
|
||||
return {'ok': False,
|
||||
'message': card.format_rejected('TRAIL_BUDGET',
|
||||
'트레일링은 수량으로만 지정 (예산 환산 불가)')}
|
||||
f'{kind}은 수량으로만 지정 (예산 환산 불가)')}
|
||||
if min_sell_price is not None and min_sell_price <= 0:
|
||||
return {'ok': False,
|
||||
'message': card.format_rejected('TRAIL_MIN_PRICE',
|
||||
'최저 매도가는 0보다 커야 함 (제한 없으면 비우세요)')}
|
||||
if is_arm and (not trigger_price or trigger_price <= 0):
|
||||
return {'ok': False,
|
||||
'message': card.format_rejected('ARM_TRIGGER', '발동가를 입력하세요')}
|
||||
# 같은 계좌·종목에 활성 트레일링과 대기 예약이 함께 있으면 안 된다 — 어느 쪽이든 먼저 취소.
|
||||
dup = trailing.find_by_symbol(account, symbol)
|
||||
if dup:
|
||||
dup_steps = dup.get('steps') or []
|
||||
@@ -216,6 +229,14 @@ def propose_trade(
|
||||
f'{symbol_name or symbol} 트레일링 예약이 이미 있음 '
|
||||
f'({dup["id"]} · {len(dup_steps)}단 · 1단계 손절 {top_uv:,}원) '
|
||||
f'— 먼저 취소하세요')}
|
||||
dup_arm = trailing.find_arm_by_symbol(account, symbol)
|
||||
if dup_arm:
|
||||
return {'ok': False,
|
||||
'message': card.format_rejected(
|
||||
'TRAIL_DUPLICATE',
|
||||
f'{symbol_name or symbol} 예약 트레일링이 이미 있음 '
|
||||
f'({dup_arm["id"]} · 발동가 {dup_arm["trigger_price"]:,}원 · '
|
||||
f'{len(dup_arm.get("steps") or [])}단) — 먼저 취소하세요')}
|
||||
if budget is not None and qty is not None:
|
||||
return {'ok': False,
|
||||
'message': card.format_rejected('AMBIGUOUS_INPUT',
|
||||
@@ -230,17 +251,37 @@ def propose_trade(
|
||||
|
||||
trail_final = None
|
||||
trail_peak = None
|
||||
if order_type == 'TRAILING_STOP':
|
||||
if order_type in ('TRAILING_STOP', 'TRAILING_ARM'):
|
||||
is_arm = order_type == 'TRAILING_ARM'
|
||||
# 현재가를 못 받으면 손절선을 정할 수 없어 진행 불가 (호가 fallback 금지 — 기준이 흔들림).
|
||||
cur = md.get('current_price') or 0
|
||||
if cur <= 0:
|
||||
return {'ok': False,
|
||||
'message': card.format_rejected('TRAIL_NO_PRICE',
|
||||
'현재가를 못 받아 트레일링 손절선을 정할 수 없음')}
|
||||
# 초기 고점은 등록 시점 현재가로 고정. 과거 고점(52주·매수후)을 쓰면 손절선이 현재가
|
||||
# 위로 올라가 등록 즉시 발동한다 — 손실 종목은 아예 등록 불가, 이익 종목은 현재가와
|
||||
# 결과가 같아 옵션 자체를 제거했다(2026-07-30 실측 근거로 관리자님 결정).
|
||||
trail_peak = cur
|
||||
'message': card.format_rejected(
|
||||
'TRAIL_NO_PRICE',
|
||||
'현재가를 못 받아 발동가 도달 여부를 판정할 수 없음' if is_arm
|
||||
else '현재가를 못 받아 트레일링 손절선을 정할 수 없음')}
|
||||
if is_arm:
|
||||
# 이미 닿아 있으면 '기다렸다가'라는 이 주문유형의 존재 이유가 사라진다 —
|
||||
# 등록하는 순간 다음 감시(1분 내)에 바로 발동한다. 그럴 거면 일반 트레일링이 맞다.
|
||||
if cur >= trigger_price:
|
||||
return {'ok': False,
|
||||
'message': card.format_rejected(
|
||||
'ARM_ALREADY_REACHED',
|
||||
f'현재가 {cur:,}원이 이미 발동가 {trigger_price:,}원 이상입니다 — '
|
||||
f'발동가를 올리시거나 일반 트레일링 스톱으로 등록하세요')}
|
||||
# 계단 계산 기준 = 발동가. 실제 발주는 발동 시점 현재가로 다시 계산하지만,
|
||||
# 여기서 미리 재보는 이유는 ①카드에 예상 손절선·주수를 보이고 ②발동해도 못 쓰는
|
||||
# 조합(수량 부족·즉시발동)을 지금 걸러내기 위해서다.
|
||||
# 발동 시점엔 현재가 >= 발동가 이므로 실제 손절선은 이 미리보기 이상이다
|
||||
# — 관리자님이 승인한 것보다 불리해지는 방향으로는 움직이지 않는다.
|
||||
basis = trigger_price
|
||||
else:
|
||||
# 초기 고점은 등록 시점 현재가로 고정. 과거 고점(52주·매수후)을 쓰면 손절선이 현재가
|
||||
# 위로 올라가 등록 즉시 발동한다 — 손실 종목은 아예 등록 불가, 이익 종목은 현재가와
|
||||
# 결과가 같아 옵션 자체를 제거했다(2026-07-30 실측 근거로 관리자님 결정).
|
||||
basis = cur
|
||||
trail_peak = basis
|
||||
try:
|
||||
leveled = trailing.compute_step_levels(trail_peak, norm_steps, min_sell_price)
|
||||
except ValueError as e:
|
||||
@@ -257,16 +298,25 @@ def propose_trade(
|
||||
# 고점=현재가로 고정된 뒤로는 최저 매도가가 현재가보다 높은 경우만 남는다
|
||||
# (트레일 폭은 항상 현재가 아래로 내려감).
|
||||
top = trail_final[0]
|
||||
if top['cond_uv'] >= cur:
|
||||
if top['cond_uv'] >= basis:
|
||||
ref = '발동가' if is_arm else '현재가'
|
||||
when = '발동하자마자' if is_arm else '등록 즉시'
|
||||
if top['floor_applied']:
|
||||
msg = (f'최저 매도가 {min_sell_price:,}원이 현재가 {cur:,}원보다 높아 '
|
||||
f'등록 즉시 발동됩니다 — 현재가보다 낮게 설정하세요')
|
||||
msg = (f'최저 매도가 {min_sell_price:,}원이 {ref} {basis:,}원보다 높아 '
|
||||
f'{when} 발동됩니다 — {ref}보다 낮게 설정하세요')
|
||||
else:
|
||||
msg = (f'1단계 손절선 {top["cond_uv"]:,}원이 현재가 {cur:,}원 이상이라 '
|
||||
f'등록 즉시 발동됩니다')
|
||||
return {'ok': False, 'message': card.format_rejected('TRAIL_IMMEDIATE', msg)}
|
||||
price = top['ord_uv']
|
||||
stop_price = top['cond_uv']
|
||||
msg = (f'1단계 손절선 {top["cond_uv"]:,}원이 {ref} {basis:,}원 이상이라 '
|
||||
f'{when} 발동됩니다')
|
||||
return {'ok': False,
|
||||
'message': card.format_rejected(
|
||||
'ARM_IMMEDIATE' if is_arm else 'TRAIL_IMMEDIATE', msg)}
|
||||
if is_arm:
|
||||
# 예약 단계에선 나가는 주문이 없다 — 발주가는 발동 시점에 정해진다.
|
||||
price = None
|
||||
stop_price = None
|
||||
else:
|
||||
price = top['ord_uv']
|
||||
stop_price = top['cond_uv']
|
||||
|
||||
budget_conversion = None
|
||||
if budget is not None:
|
||||
@@ -300,6 +350,13 @@ def propose_trade(
|
||||
request['trail_steps'] = trail_final
|
||||
request['min_sell_price'] = min_sell_price
|
||||
request['trail_peak'] = trail_peak
|
||||
elif order_type == 'TRAILING_ARM':
|
||||
# price/stop_price 를 넣지 않는다 — 지금 나가는 주문이 없고, 넣으면 validate_request 의
|
||||
# 가격제한폭·"조건단가 < 현재가" 검사에 걸린다(둘 다 예약엔 무의미하다).
|
||||
request['trigger_price'] = trigger_price
|
||||
request['trail_steps'] = trail_final
|
||||
request['min_sell_price'] = min_sell_price
|
||||
request['trail_peak'] = trail_peak
|
||||
|
||||
r = guards.validate_request(request, md)
|
||||
if not r.ok:
|
||||
@@ -352,6 +409,15 @@ def propose_trade(
|
||||
payload['trail_steps'] = trail_final
|
||||
payload['trail_peak'] = trail_peak
|
||||
payload['min_sell_price'] = min_sell_price
|
||||
elif order_type == 'TRAILING_ARM':
|
||||
# 승인 시점에 확정: 계좌·종목·수량·발동가·계단 정의·최저 매도가.
|
||||
# 발동 시점에 정해짐: 고점(그때의 현재가)·계단별 조건단가/지정가/주수·라우팅.
|
||||
# ⚠️ trail_steps 에는 레벨 없는 정규화 계단만 넣는다 — 발동 경로가 미리보기 가격을
|
||||
# 실수로 재사용하지 못하게 구조적으로 막는다. 미리보기는 별도 키(trail_preview).
|
||||
payload['trigger_price'] = trigger_price
|
||||
payload['trail_steps'] = norm_steps
|
||||
payload['trail_preview'] = trail_final
|
||||
payload['min_sell_price'] = min_sell_price
|
||||
|
||||
try:
|
||||
pending = _pin_store.issue(account, payload)
|
||||
@@ -538,6 +604,8 @@ def submit_with_pin(pin_input: str, dry_run: bool = True) -> dict:
|
||||
return _submit_multi_legs(card_obj, dry_run)
|
||||
if p['order_type'] == 'TRAILING_STOP':
|
||||
return _submit_trailing_steps(card_obj, dry_run)
|
||||
if p['order_type'] == 'TRAILING_ARM':
|
||||
return _register_trailing_arm(card_obj, dry_run)
|
||||
|
||||
submit_order_type = 'LIMIT' if p['order_type'] == 'AGGRESSIVE_LIMIT' else p['order_type']
|
||||
res = kiwoom_order.submit(
|
||||
@@ -576,6 +644,108 @@ def submit_with_pin(pin_input: str, dry_run: bool = True) -> dict:
|
||||
'detail': res}
|
||||
|
||||
|
||||
def _register_trailing_arm(card_obj, dry_run: bool) -> dict:
|
||||
"""예약 트레일링 카드 — 키움 호출 0. 발동가 대기 예약 1건을 파일에 쓰고 끝난다.
|
||||
|
||||
PIN 은 여기서 소비된다. 발동 시점엔 PIN 이 없다(관리자님 명시 승인) — 그래서 승인 대상에
|
||||
발동가까지 포함되고, 감시 루프는 그 확정된 내용을 조건 도달 시 집행만 한다.
|
||||
"""
|
||||
p = card_obj.payload
|
||||
name = p.get('symbol_name', p['symbol'])
|
||||
info = {'card_id': card_obj.card_id, 'account': p['account'], 'symbol': p['symbol'],
|
||||
'symbol_name': name, 'qty': p['qty'], 'trigger_price': p['trigger_price'],
|
||||
'min_sell_price': p.get('min_sell_price'), 'steps': p.get('trail_steps') or [],
|
||||
'dry_run': dry_run, 'arm_id': None}
|
||||
if dry_run:
|
||||
return {'ok': True, 'message': card.format_arm_registered(info)}
|
||||
try:
|
||||
arm = trailing.register_arm(
|
||||
account=p['account'], symbol=p['symbol'], symbol_name=name,
|
||||
qty=p['qty'], trigger_price=p['trigger_price'],
|
||||
steps=p.get('trail_steps') or [],
|
||||
min_sell_price=p.get('min_sell_price'), card_id=card_obj.card_id)
|
||||
except Exception as e:
|
||||
# 주문이 나간 게 없으므로 여기선 그냥 거부로 끝내면 된다 (트레일링 등록 실패와 달리
|
||||
# 되돌릴 외부 효과가 없다).
|
||||
ledger.append('arm_fire_failed', {'card_id': card_obj.card_id,
|
||||
'error': repr(e), 'stage': 'register', **p})
|
||||
return {'ok': False,
|
||||
'message': card.format_rejected('ARM_REGISTER_FAILED',
|
||||
f'{name} 예약 등록 실패 — {e}')}
|
||||
info['arm_id'] = arm['id']
|
||||
ledger.append('arm_registered', {'card_id': card_obj.card_id, 'arm_id': arm['id'], **p})
|
||||
return {'ok': True, 'message': card.format_arm_registered(info), 'arm_id': arm['id']}
|
||||
|
||||
|
||||
def cancel_trailing_arm(arm_id: str) -> dict:
|
||||
"""대기 예약 취소. PIN 없음 — 취소는 안전한 방향이다(cancel_open_order 와 같은 판례).
|
||||
|
||||
⚠️ firing 마킹이 걸린 예약은 이미 키움에 스톱주문이 나갔을 수 있다. 그 주문까지 자동으로
|
||||
취소하지는 않는다 — 승인 범위를 넘는 매매 API 자동 호출이 된다. 대신 접수된 주문번호를
|
||||
응답에 실어 관리자님이 미체결 목록에서 직접 처리하게 한다.
|
||||
"""
|
||||
sidecar.guard_or_raise()
|
||||
arm = trailing.get_arm(arm_id)
|
||||
if arm is None:
|
||||
return {'ok': False, 'message': f'대기 예약 없음 ({arm_id})'}
|
||||
fired = arm.get('fired_legs') or []
|
||||
gone = trailing.remove_arm(arm_id, 'manual_cancel')
|
||||
if gone is None:
|
||||
return {'ok': False, 'message': f'대기 예약 없음 ({arm_id})'}
|
||||
ledger.append('arm_canceled', {'arm_id': arm_id, 'account': arm['account'],
|
||||
'symbol': arm['symbol'], 'qty': arm['qty'],
|
||||
'trigger_price': arm['trigger_price']})
|
||||
msg = (f'🗑 예약 트레일링 취소 ({arm_id}) — {arm["symbol_name"]} '
|
||||
f'{arm["qty"]:,}주 · 발동가 {arm["trigger_price"]:,}원')
|
||||
if fired:
|
||||
nos = ', '.join(s.get('ord_no', '') for s in fired if s.get('ord_no'))
|
||||
msg += (f'\n⚠️ 발동 처리 중 접수된 주문이 있습니다: {nos}\n'
|
||||
f'미체결 목록에서 별도로 취소해 주세요 (자동 취소하지 않습니다).')
|
||||
return {'ok': True, 'message': msg, 'placed_orders': fired}
|
||||
|
||||
|
||||
def place_trailing_legs(account: str, symbol: str, steps: list, routing_suffix: str,
|
||||
card_id: Optional[str], dry_run: bool,
|
||||
on_placed=None) -> tuple:
|
||||
"""계단별 스톱지정가 접수. 반환 (placed, failed, details).
|
||||
|
||||
얕은 계단(먼저 발동할 것)부터 순서대로 낸다 — 중간에 막혀도 가장 가까운 방어선이 먼저 걸린다.
|
||||
한 레그가 실패해도 나머지는 계속 시도한다. 이미 나간 주문을 되돌리지 않는 이유는 취소 자체도
|
||||
실패할 수 있어 상태가 더 불분명해지기 때문이다 — 대신 성공·실패를 그대로 호출측에 돌려준다.
|
||||
|
||||
`on_placed(leg)` 는 레그 하나가 접수될 때마다 즉시 호출된다. 예약 트레일링 발동 경로가
|
||||
"어느 레그까지 실제로 나갔는지" 를 디스크에 미리 적는 데 쓴다(중간에 죽었을 때의 복구 단서).
|
||||
|
||||
실주문이 나가는 유일한 계단 발주 경로다 — 등록 즉시 트레일링과 예약 발동이 이 함수를
|
||||
공유해야 두 벌로 갈라지지 않는다.
|
||||
"""
|
||||
placed, failed, details = [], [], []
|
||||
for s in steps:
|
||||
res = kiwoom_order.submit(
|
||||
account_label=account,
|
||||
side='SELL',
|
||||
symbol=symbol,
|
||||
qty=s['qty'],
|
||||
price=s['ord_uv'],
|
||||
order_type='STOP_LIMIT',
|
||||
routing_suffix=routing_suffix,
|
||||
dry_run=dry_run,
|
||||
card_id=card_id,
|
||||
stop_price=s['cond_uv'],
|
||||
)
|
||||
details.append(res)
|
||||
if res['ok']:
|
||||
# dry_run 은 주문번호가 없다 — 예약 등록 없이 접수될 내용만 확인한다.
|
||||
leg = dict(s, ord_no=res.get('ord_no', ''))
|
||||
placed.append(leg)
|
||||
if on_placed is not None:
|
||||
on_placed(leg)
|
||||
else:
|
||||
failed.append(dict(s, reason=res.get('reason', 'UNKNOWN'),
|
||||
error=str(res.get('error', ''))))
|
||||
return placed, failed, details
|
||||
|
||||
|
||||
def _submit_trailing_steps(card_obj, dry_run: bool) -> dict:
|
||||
"""트레일링 계단 카드 — 계단마다 스톱주문을 내고 예약 1건으로 묶어 등록한다.
|
||||
|
||||
@@ -592,28 +762,9 @@ def _submit_trailing_steps(card_obj, dry_run: bool) -> dict:
|
||||
p = card_obj.payload
|
||||
steps = p.get('trail_steps') or []
|
||||
name = p.get('symbol_name', p['symbol'])
|
||||
placed, failed, details = [], [], []
|
||||
|
||||
for s in steps:
|
||||
res = kiwoom_order.submit(
|
||||
account_label=p['account'],
|
||||
side='SELL',
|
||||
symbol=p['symbol'],
|
||||
qty=s['qty'],
|
||||
price=s['ord_uv'],
|
||||
order_type='STOP_LIMIT',
|
||||
routing_suffix=p['routing_suffix'],
|
||||
dry_run=dry_run,
|
||||
card_id=card_obj.card_id,
|
||||
stop_price=s['cond_uv'],
|
||||
)
|
||||
details.append(res)
|
||||
if res['ok']:
|
||||
# dry_run 은 주문번호가 없다 — 예약 등록 없이 접수될 내용만 확인한다.
|
||||
placed.append(dict(s, ord_no=res.get('ord_no', '')))
|
||||
else:
|
||||
failed.append(dict(s, reason=res.get('reason', 'UNKNOWN'),
|
||||
error=str(res.get('error', ''))))
|
||||
placed, failed, details = place_trailing_legs(
|
||||
account=p['account'], symbol=p['symbol'], steps=steps,
|
||||
routing_suffix=p['routing_suffix'], card_id=card_obj.card_id, dry_run=dry_run)
|
||||
|
||||
if not placed:
|
||||
return {'ok': False,
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
"ledger": {
|
||||
"log_file": "state/order_log.jsonl",
|
||||
"events": ["card_issued", "pin_issued", "amended", "approved", "rejected", "expired", "canceled", "submitted", "filled", "partial", "failed", "dryrun", "cancel_submitted", "cancel_rejected", "cancel_confirmed", "cancel_unconfirmed_timeout", "modify_submitted", "modify_rejected"],
|
||||
"events": ["card_issued", "pin_issued", "amended", "approved", "rejected", "expired", "canceled", "submitted", "filled", "partial", "failed", "dryrun", "cancel_submitted", "cancel_rejected", "cancel_confirmed", "cancel_unconfirmed_timeout", "modify_submitted", "modify_rejected", "trailing_register_failed", "arm_registered", "arm_fired", "arm_fire_failed", "arm_canceled"],
|
||||
"mask_token_in_logs": true
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
"""예약 트레일링(TRAILING_ARM) propose·승인 경로 회귀 테스트 (2026-08-25).
|
||||
|
||||
datasource·sidecar·pin_store·ledger 를 mock 해서 키움을 전혀 건드리지 않는다.
|
||||
가장 중요한 확인은 **거부 케이스에서 카드가 발행되지 않는 것** — 카드가 나가면 PIN 한 번으로
|
||||
발동 조건까지 확정되므로, 걸러야 할 입력이 카드 단계를 통과하면 안 된다.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
from orders import handler, ledger
|
||||
from orders import trailing as t
|
||||
|
||||
KST = timezone(timedelta(hours=9))
|
||||
|
||||
STEPS = [{'pct': 10, 'cum': 20}, {'pct': 20, 'cum': 50}, {'pct': 30, 'cum': 100}]
|
||||
|
||||
|
||||
def _md(cur=70000):
|
||||
return {
|
||||
'now': datetime(2026, 8, 25, 10, 0, tzinfo=KST),
|
||||
'is_holiday': False,
|
||||
'nxt_eligible': True,
|
||||
'current_price': cur,
|
||||
'prev_close': cur,
|
||||
'upper_limit': int(cur * 1.3),
|
||||
'lower_limit': int(cur * 0.7),
|
||||
'halt': False,
|
||||
'vi': False,
|
||||
'orderbook': {'asks': [{'price': cur + 100, 'qty': 200}],
|
||||
'bids': [{'price': cur, 'qty': 200}]},
|
||||
'broker_executions': [],
|
||||
'broker_query_error': None,
|
||||
'position_qty': 100,
|
||||
'balance_d2': 5_000_000,
|
||||
'stock_meta': None,
|
||||
}
|
||||
|
||||
|
||||
class ArmProposeTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = Path(tempfile.mkdtemp()) / 'trailing_stops.json'
|
||||
self._orig = (t.STATE_FILE, t._LOCK_FILE)
|
||||
t.STATE_FILE = self.tmp
|
||||
t._LOCK_FILE = self.tmp.with_suffix('.json.lock')
|
||||
|
||||
self.collect = mock.patch.object(handler.datasource, 'collect_market_data',
|
||||
return_value=_md()).start()
|
||||
mock.patch.object(handler.sidecar, 'guard_or_raise', return_value=None).start()
|
||||
mock.patch.object(handler, '_sweep_expired_and_notify', return_value=None).start()
|
||||
mock.patch.object(handler.ledger, 'append', return_value=None).start()
|
||||
mock.patch.object(ledger, 'last_terminal_event', return_value=None).start()
|
||||
mock.patch.object(ledger, 'last_event_for_symbol', return_value=None).start()
|
||||
fake = mock.MagicMock()
|
||||
fake.card_id, fake.pin, fake.expiry_seconds, fake.account_label = 'TEST', '1234', 120, '일반'
|
||||
self.issue = mock.patch.object(handler._pin_store, 'issue', return_value=fake).start()
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
|
||||
def tearDown(self):
|
||||
t.STATE_FILE, t._LOCK_FILE = self._orig
|
||||
shutil.rmtree(self.tmp.parent, ignore_errors=True)
|
||||
|
||||
def _propose(self, **kw):
|
||||
base = dict(account='일반', side='SELL', symbol='005930', symbol_name='삼성전자',
|
||||
qty=100, order_type='TRAILING_ARM', trail_steps=STEPS,
|
||||
trigger_price=80000)
|
||||
base.update(kw)
|
||||
return handler.propose_trade(**base)
|
||||
|
||||
def _assert_rejected(self, res, code):
|
||||
self.assertFalse(res['ok'], msg=res.get('message'))
|
||||
self.assertIn(code, res['message'])
|
||||
self.issue.assert_not_called() # 거부는 카드를 남기지 않아야 한다
|
||||
|
||||
# ── 통과 경로 ──
|
||||
def test_valid_arm_issues_card_with_definition_only(self):
|
||||
res = self._propose(min_sell_price=60000)
|
||||
self.assertTrue(res['ok'], msg=res.get('message'))
|
||||
payload = self.issue.call_args[0][1]
|
||||
self.assertEqual(payload['order_type'], 'TRAILING_ARM')
|
||||
self.assertEqual(payload['trigger_price'], 80000)
|
||||
self.assertIsNone(payload['price']) # 나가는 주문이 없다
|
||||
# trail_steps 는 정의만, 미리보기는 별도 키 — 발동 경로가 옛 가격을 재사용하지 못하게.
|
||||
self.assertNotIn('cond_uv', payload['trail_steps'][0])
|
||||
self.assertIn('cond_uv', payload['trail_preview'][0])
|
||||
|
||||
def test_preview_uses_trigger_not_current_price(self):
|
||||
res = self._propose()
|
||||
self.assertTrue(res['ok'], msg=res.get('message'))
|
||||
payload = self.issue.call_args[0][1]
|
||||
expect = t.compute_step_levels(80000, t.normalize_steps(STEPS), None)
|
||||
self.assertEqual([s['cond_uv'] for s in payload['trail_preview']],
|
||||
[s['cond_uv'] for s in expect])
|
||||
|
||||
def test_trigger_above_todays_upper_limit_is_allowed(self):
|
||||
"""발동가는 가격제한폭 검증을 받지 않는다 — 실제 발주는 발동 시점 현재가로 계산된다.
|
||||
|
||||
arm 이 당일 유효가 된 뒤로는 '오늘 상한가 초과 = 오늘 도달 불가'지만 **막지 않고
|
||||
경고만** 한다(2026-08-25 관리자님 결정). 통과가 의도된 동작이라 고정해 둔다.
|
||||
"""
|
||||
res = self._propose(trigger_price=200000) # 현재가 70,000 · 상한가 91,000
|
||||
self.assertTrue(res['ok'], msg=res.get('message'))
|
||||
|
||||
def test_trigger_above_upper_limit_warns_on_card(self):
|
||||
"""막지는 않되 승인 전에 '오늘 도달 불가'를 반드시 알린다."""
|
||||
res = self._propose(trigger_price=200000)
|
||||
self.assertTrue(res['ok'], msg=res.get('message'))
|
||||
self.assertIn('오늘 상한가', res['card_message'])
|
||||
self.assertIn('만료', res['card_message'])
|
||||
# 상한가 이내면 경고가 없어야 한다 (남발 금지)
|
||||
res2 = self._propose(trigger_price=85000) # 상한가 91,000 이내
|
||||
self.assertTrue(res2['ok'], msg=res2.get('message'))
|
||||
self.assertNotIn('오늘 상한가', res2['card_message'])
|
||||
|
||||
# ── 거부 경로 ──
|
||||
def test_buy_rejected(self):
|
||||
self._assert_rejected(self._propose(side='BUY'), 'ARM_SELL_ONLY')
|
||||
|
||||
def test_missing_trigger_rejected(self):
|
||||
self._assert_rejected(self._propose(trigger_price=None), 'ARM_TRIGGER')
|
||||
|
||||
def test_zero_trigger_rejected(self):
|
||||
self._assert_rejected(self._propose(trigger_price=0), 'ARM_TRIGGER')
|
||||
|
||||
def test_already_reached_rejected(self):
|
||||
self._assert_rejected(self._propose(trigger_price=70000), 'ARM_ALREADY_REACHED')
|
||||
self._assert_rejected(self._propose(trigger_price=65000), 'ARM_ALREADY_REACHED')
|
||||
|
||||
def test_floor_above_trigger_rejected(self):
|
||||
self._assert_rejected(self._propose(min_sell_price=85000), 'ARM_IMMEDIATE')
|
||||
|
||||
def test_budget_rejected(self):
|
||||
self._assert_rejected(self._propose(qty=None, budget=1_000_000), 'TRAIL_BUDGET')
|
||||
|
||||
def test_bad_steps_rejected(self):
|
||||
self._assert_rejected(self._propose(trail_steps=[{'pct': 20, 'cum': 20},
|
||||
{'pct': 10, 'cum': 50}]), 'TRAIL_STEPS')
|
||||
|
||||
def test_qty_too_small_rejected(self):
|
||||
# qty=0 은 '미입력'(None)이 아니라 '값 0' 이라 MISSING_QTY 를 지나 계단 배분에서 걸린다.
|
||||
self._assert_rejected(self._propose(qty=0), 'TRAIL_QTY')
|
||||
|
||||
def test_no_qty_rejected(self):
|
||||
self._assert_rejected(self._propose(qty=None), 'MISSING_QTY')
|
||||
|
||||
def test_duplicate_arm_rejected(self):
|
||||
t.register_arm(account='일반', symbol='005930', symbol_name='삼성전자', qty=10,
|
||||
trigger_price=90000, steps=t.normalize_steps(STEPS))
|
||||
self._assert_rejected(self._propose(), 'TRAIL_DUPLICATE')
|
||||
|
||||
def test_existing_trailing_blocks_arm(self):
|
||||
"""같은 계좌·종목에 활성 트레일링이 있으면 예약도 막는다 — 같은 주식을 두 번 팔 수 없다."""
|
||||
st = t.normalize_steps(STEPS)
|
||||
lv = t.compute_step_levels(70000, st, None)
|
||||
qtys = t.allocate_step_qty(100, [s['weight'] for s in st])
|
||||
legs = [dict(s, qty=q, ord_no=f'O{s["n"]}') for s, q in zip(lv, qtys)]
|
||||
t.register_steps(account='일반', symbol='005930', symbol_name='삼성전자',
|
||||
total_qty=100, peak=70000, steps=legs)
|
||||
self._assert_rejected(self._propose(), 'TRAIL_DUPLICATE')
|
||||
|
||||
def test_arm_blocks_plain_trailing_too(self):
|
||||
"""반대 방향도 막혀야 한다 — 대기 예약이 있으면 일반 트레일링을 새로 걸 수 없다."""
|
||||
t.register_arm(account='일반', symbol='005930', symbol_name='삼성전자', qty=10,
|
||||
trigger_price=90000, steps=t.normalize_steps(STEPS))
|
||||
res = handler.propose_trade(account='일반', side='SELL', symbol='005930',
|
||||
symbol_name='삼성전자', qty=100,
|
||||
order_type='TRAILING_STOP', trail_steps=STEPS)
|
||||
self._assert_rejected(res, 'TRAIL_DUPLICATE')
|
||||
|
||||
def test_holiday_now_actually_blocks(self):
|
||||
"""guards.is_today_holiday 의 dict 미인식 버그를 고친 뒤라야 이게 막힌다."""
|
||||
self.collect.return_value = dict(_md(), is_holiday=True)
|
||||
res = self._propose()
|
||||
self.assertFalse(res['ok'], msg=res.get('message'))
|
||||
self.assertIn('HOLIDAY', res['message'])
|
||||
|
||||
|
||||
class ArmRegisterTests(unittest.TestCase):
|
||||
"""PIN 승인 → 대기 예약 등록. 키움 호출이 0건이어야 한다."""
|
||||
|
||||
def setUp(self):
|
||||
self.tmp = Path(tempfile.mkdtemp()) / 'trailing_stops.json'
|
||||
self._orig = (t.STATE_FILE, t._LOCK_FILE)
|
||||
t.STATE_FILE = self.tmp
|
||||
t._LOCK_FILE = self.tmp.with_suffix('.json.lock')
|
||||
mock.patch.object(handler.ledger, 'append', return_value=None).start()
|
||||
self.submit = mock.patch.object(handler.kiwoom_order, 'submit').start()
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
self.card = mock.MagicMock()
|
||||
self.card.card_id = 'AB3D'
|
||||
self.card.payload = {
|
||||
'account': '일반', 'side': 'SELL', 'symbol': '005930', 'symbol_name': '삼성전자',
|
||||
'qty': 100, 'price': None, 'order_type': 'TRAILING_ARM', 'routing_suffix': '_AL',
|
||||
'trigger_price': 80000, 'trail_steps': t.normalize_steps(STEPS),
|
||||
'trail_preview': [], 'min_sell_price': 60000,
|
||||
}
|
||||
|
||||
def tearDown(self):
|
||||
t.STATE_FILE, t._LOCK_FILE = self._orig
|
||||
shutil.rmtree(self.tmp.parent, ignore_errors=True)
|
||||
|
||||
def test_register_places_no_order(self):
|
||||
res = handler._register_trailing_arm(self.card, dry_run=False)
|
||||
self.assertTrue(res['ok'], msg=res.get('message'))
|
||||
self.submit.assert_not_called()
|
||||
arms = t.list_arms()
|
||||
self.assertEqual(len(arms), 1)
|
||||
self.assertEqual((arms[0]['trigger_price'], arms[0]['card_id']), (80000, 'AB3D'))
|
||||
self.assertIn('발동가에 닿으면 PIN 재확인 없이', res['message'])
|
||||
|
||||
def test_dry_run_writes_nothing(self):
|
||||
res = handler._register_trailing_arm(self.card, dry_run=True)
|
||||
self.assertTrue(res['ok'])
|
||||
self.assertEqual(t.list_arms(), [])
|
||||
self.submit.assert_not_called()
|
||||
|
||||
def test_cancel_removes_arm(self):
|
||||
handler._register_trailing_arm(self.card, dry_run=False)
|
||||
arm_id = t.list_arms()[0]['id']
|
||||
with mock.patch.object(handler.sidecar, 'guard_or_raise', return_value=None):
|
||||
res = handler.cancel_trailing_arm(arm_id)
|
||||
self.assertTrue(res['ok'])
|
||||
self.assertEqual(t.list_arms(), [])
|
||||
|
||||
def test_cancel_warns_about_orphan_orders(self):
|
||||
"""발동 중 접수된 주문이 있으면 자동 취소하지 않고 주문번호를 알려준다."""
|
||||
handler._register_trailing_arm(self.card, dry_run=False)
|
||||
arm_id = t.list_arms()[0]['id']
|
||||
t.mark_arm_firing(arm_id)
|
||||
t.record_arm_leg(arm_id, {'n': 1, 'qty': 20, 'cond_uv': 72000, 'ord_uv': 71900,
|
||||
'ord_no': '0374065'})
|
||||
with mock.patch.object(handler.sidecar, 'guard_or_raise', return_value=None):
|
||||
res = handler.cancel_trailing_arm(arm_id)
|
||||
self.assertTrue(res['ok'])
|
||||
self.assertIn('0374065', res['message'])
|
||||
self.assertIn('별도로 취소', res['message'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
unittest.main(verbosity=2)
|
||||
@@ -19,8 +19,12 @@ import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from orders import trailing as t
|
||||
|
||||
KST = timezone(timedelta(hours=9))
|
||||
|
||||
|
||||
class NormalizeStepsTests(unittest.TestCase):
|
||||
"""입력한 누적 비중 → 내부 추가 비중."""
|
||||
@@ -358,6 +362,376 @@ console.log(JSON.stringify(out));
|
||||
f'비중환산 {cums}')
|
||||
|
||||
|
||||
class ArmPlanTests(unittest.TestCase):
|
||||
"""예약 트레일링 발동 계획 — 순수 함수라 키움 없이 전부 검증된다."""
|
||||
|
||||
def setUp(self):
|
||||
self.steps = t.normalize_steps([{'pct': 10, 'cum': 20}, {'pct': 20, 'cum': 50},
|
||||
{'pct': 30, 'cum': 100}])
|
||||
|
||||
def _arm(self, qty=100, trigger=80000, floor=None):
|
||||
return {'id': 'ARM-TEST', 'account': '일반', 'symbol': '005930',
|
||||
'symbol_name': '삼성전자', 'qty': qty, 'trigger_price': trigger,
|
||||
'min_sell_price': floor, 'steps': self.steps}
|
||||
|
||||
def test_reach_is_upward_only(self):
|
||||
self.assertEqual(t.plan_arm_fire(self._arm(), 79999, 100)['reason'], 'not_reached')
|
||||
# 발동가 정확히 일치도 발동 — 판정이 >= 라서.
|
||||
self.assertEqual(t.plan_arm_fire(self._arm(), 80000, 100)['action'], 'fire')
|
||||
|
||||
def test_no_price_is_not_a_reach(self):
|
||||
self.assertEqual(t.plan_arm_fire(self._arm(), 0, 100)['reason'], 'no_price')
|
||||
|
||||
def test_peak_is_current_price_not_trigger(self):
|
||||
"""갭 상승으로 발동가를 훌쩍 넘겨 열리면 고점은 현재가여야 한다."""
|
||||
r = t.plan_arm_fire(self._arm(trigger=80000), 95000, 100)
|
||||
self.assertEqual(r['peak'], 95000)
|
||||
|
||||
def test_qty_shrinks_to_sellable(self):
|
||||
r = t.plan_arm_fire(self._arm(qty=100), 80000, 40)
|
||||
self.assertEqual((r['qty_used'], r['reduced']), (40, True))
|
||||
self.assertEqual(sum(s['qty'] for s in r['legs']), 40)
|
||||
|
||||
def test_no_sellable_drops_the_arm(self):
|
||||
self.assertEqual(t.plan_arm_fire(self._arm(), 80000, 0)['action'], 'drop')
|
||||
|
||||
def test_one_share_keeps_a_step_alive(self):
|
||||
r = t.plan_arm_fire(self._arm(qty=100), 80000, 1)
|
||||
self.assertEqual(r['action'], 'fire')
|
||||
self.assertEqual(sum(s['qty'] for s in r['legs']), 1)
|
||||
|
||||
def test_actual_stops_never_worse_than_preview(self):
|
||||
"""승인 화면은 발동가 기준으로 보여준다 — 실제 손절선이 그보다 낮으면 안 된다."""
|
||||
preview = t.compute_step_levels(80000, self.steps, None)
|
||||
for cur in (80000, 82000, 90000, 120000):
|
||||
legs = t.plan_arm_fire(self._arm(trigger=80000), cur, 100)['legs']
|
||||
for actual, shown in zip(legs, preview):
|
||||
self.assertGreaterEqual(actual['cond_uv'], shown['cond_uv'], f'현재가 {cur}')
|
||||
|
||||
def test_first_stop_always_below_current(self):
|
||||
"""발동 즉시 체결되면 트레일링이 아니다 — 무작위 시세로 불변식 확인."""
|
||||
rnd = random.Random(20260825)
|
||||
for _ in range(300):
|
||||
trigger = rnd.randrange(1000, 300000)
|
||||
cur = trigger + rnd.randrange(0, 50000)
|
||||
floor = rnd.choice([None, int(trigger * 0.7), int(trigger * 0.95)])
|
||||
r = t.plan_arm_fire(self._arm(trigger=trigger, floor=floor), cur, 100)
|
||||
if r['action'] == 'fire':
|
||||
self.assertLess(r['legs'][0]['cond_uv'], cur,
|
||||
f'trigger={trigger} cur={cur} floor={floor}')
|
||||
|
||||
def test_floor_ties_steps_without_merging(self):
|
||||
r = t.plan_arm_fire(self._arm(trigger=80000, floor=75000), 80000, 100)
|
||||
self.assertEqual(len(r['legs']), 3) # 병합하지 않는다
|
||||
self.assertEqual({s['cond_uv'] for s in r['legs']}, {75000})
|
||||
|
||||
|
||||
class ArmStateTests(unittest.TestCase):
|
||||
"""대기 예약 상태 — 예약과 한 파일을 공유하므로 서로를 지우지 않는지가 핵심."""
|
||||
|
||||
def setUp(self):
|
||||
self.tmp = Path(tempfile.mkdtemp()) / 'trailing_stops.json'
|
||||
self._orig = (t.STATE_FILE, t._LOCK_FILE)
|
||||
t.STATE_FILE = self.tmp
|
||||
t._LOCK_FILE = self.tmp.with_suffix('.json.lock')
|
||||
self.steps = t.normalize_steps([{'pct': 10, 'cum': 20}, {'pct': 20, 'cum': 50},
|
||||
{'pct': 30, 'cum': 100}])
|
||||
self.arm = t.register_arm(account='일반', symbol='005930', symbol_name='삼성전자',
|
||||
qty=100, trigger_price=80000, steps=self.steps,
|
||||
min_sell_price=70000, card_id='AB3D')
|
||||
|
||||
def tearDown(self):
|
||||
t.STATE_FILE, t._LOCK_FILE = self._orig
|
||||
shutil.rmtree(self.tmp.parent, ignore_errors=True)
|
||||
|
||||
def _legs(self, peak=82000, qty=100):
|
||||
lv = t.compute_step_levels(peak, self.steps, 70000)
|
||||
qtys = t.allocate_step_qty(qty, [s['weight'] for s in self.steps])
|
||||
return [dict(s, qty=q, ord_no=f'O{s["n"]:04d}') for s, q in zip(lv, qtys) if q > 0]
|
||||
|
||||
def test_arm_stores_definition_only(self):
|
||||
"""가격·라우팅을 굳혀두면 발동 시점 시세와 어긋난다."""
|
||||
a = t.get_arm(self.arm['id'])
|
||||
self.assertNotIn('cond_uv', a['steps'][0])
|
||||
self.assertNotIn('routing_suffix', a)
|
||||
self.assertIsNone(a['firing_at'])
|
||||
|
||||
def test_reservation_writes_do_not_wipe_arms(self):
|
||||
"""레거시 6개 호출부가 _write(reservations) 로 arms 를 지우면 안 된다."""
|
||||
res = t.register_steps(account='ISA', symbol='000660', symbol_name='SK하이닉스',
|
||||
total_qty=100, peak=500000, steps=self._legs(500000))
|
||||
for fn in (lambda: t.commit_peak(res['id'], 510000),
|
||||
lambda: t.commit_step_modify(res['id'], 1, 'X0001', 510000, 460000, 459000),
|
||||
lambda: t.should_notify_failure(res['id'], 9e9, 1800),
|
||||
lambda: t.remove_step(res['id'], 3, 'test')):
|
||||
fn()
|
||||
self.assertEqual(len(t.list_arms()), 1, '대기 예약이 사라졌다')
|
||||
|
||||
def test_firing_marker_blocks_refire(self):
|
||||
self.assertTrue(t.mark_arm_firing(self.arm['id']))
|
||||
self.assertFalse(t.mark_arm_firing(self.arm['id']))
|
||||
|
||||
def test_promote_is_atomic(self):
|
||||
legs = self._legs()
|
||||
out = t.promote_arm(self.arm['id'], 82000, legs, '_AL')
|
||||
self.assertEqual(t.list_arms(), [])
|
||||
r = t.get(out['reservation']['id'])
|
||||
self.assertEqual((r['peak'], r['entry_peak'], r['routing_suffix']), (82000, 82000, '_AL'))
|
||||
self.assertEqual((r['min_sell_price'], r['card_id']), (70000, 'AB3D'))
|
||||
self.assertEqual(r['qty'], sum(s['qty'] for s in legs))
|
||||
|
||||
def test_promote_unknown_arm_is_noop(self):
|
||||
self.assertIsNone(t.promote_arm('ARM-NOPE', 82000, self._legs()))
|
||||
self.assertEqual(len(t.list_arms()), 1)
|
||||
|
||||
def test_record_leg_survives_for_crash_diagnosis(self):
|
||||
t.mark_arm_firing(self.arm['id'])
|
||||
t.record_arm_leg(self.arm['id'], self._legs()[0])
|
||||
self.assertEqual(t.get_arm(self.arm['id'])['fired_legs'][0]['ord_no'], 'O0001')
|
||||
|
||||
def test_duplicate_lookup_covers_both_kinds(self):
|
||||
self.assertIsNotNone(t.find_arm_by_symbol('일반', '005930'))
|
||||
self.assertIsNone(t.find_arm_by_symbol('ISA', '005930'))
|
||||
|
||||
def test_remove_arm(self):
|
||||
self.assertEqual(t.remove_arm(self.arm['id'], 'manual')['removed_reason'], 'manual')
|
||||
self.assertEqual(t.list_arms(), [])
|
||||
self.assertIsNone(t.remove_arm(self.arm['id']))
|
||||
|
||||
|
||||
class ArmExpiryTests(unittest.TestCase):
|
||||
"""대기 예약 수명 = 등록일 당일 (2026-08-25 무기한에서 변경).
|
||||
|
||||
거래소가 지워주는 스톱주문과 달리 우리 파일에만 있으므로 우리가 만료를 재야 한다.
|
||||
"""
|
||||
|
||||
def _arm(self, created):
|
||||
return {'id': 'ARM-T', 'created_at': created, 'trigger_price': 80000}
|
||||
|
||||
def test_same_day_during_session_is_alive(self):
|
||||
for hh, mm in ((8, 0), (9, 0), (9, 0), (12, 30), (15, 25), (19, 59)):
|
||||
now = datetime(2026, 8, 25, hh, mm, tzinfo=KST)
|
||||
self.assertFalse(t.is_arm_expired(self._arm('2026-08-25T09:30:00+09:00'), now),
|
||||
f'{hh:02d}:{mm:02d} 에 만료로 잡혔다')
|
||||
|
||||
def test_opening_gap_is_not_expiry(self):
|
||||
"""09:00:00~09:00:30 은 session_at 이 CLOSED 를 주는 틈 — 여기서 만료되면 안 된다."""
|
||||
now = datetime(2026, 8, 25, 9, 0, 10, tzinfo=KST)
|
||||
self.assertFalse(t.is_arm_expired(self._arm('2026-08-25T08:30:00+09:00'), now))
|
||||
|
||||
def test_after_session_end_same_day_expires(self):
|
||||
now = datetime(2026, 8, 25, 20, 0, tzinfo=KST)
|
||||
self.assertTrue(t.is_arm_expired(self._arm('2026-08-25T09:30:00+09:00'), now))
|
||||
|
||||
def test_next_day_expires(self):
|
||||
now = datetime(2026, 8, 26, 8, 0, tzinfo=KST)
|
||||
self.assertTrue(t.is_arm_expired(self._arm('2026-08-25T09:30:00+09:00'), now))
|
||||
|
||||
def test_missing_created_at_is_kept(self):
|
||||
"""날짜를 모르면 지우지 않는다 — 조용히 사라지는 쪽이 더 위험하다."""
|
||||
self.assertFalse(t.is_arm_expired({'id': 'ARM-X', 'trigger_price': 1},
|
||||
datetime(2026, 8, 26, 9, 0, tzinfo=KST)))
|
||||
|
||||
|
||||
class ArmMonitorTests(unittest.TestCase):
|
||||
"""감시 루프의 발동 단계 — 실주문 경로라 stub 으로 전부 막고 호출만 관찰한다."""
|
||||
|
||||
def setUp(self):
|
||||
self.tmp = Path(tempfile.mkdtemp()) / 'trailing_stops.json'
|
||||
self._orig = (t.STATE_FILE, t._LOCK_FILE)
|
||||
t.STATE_FILE = self.tmp
|
||||
t._LOCK_FILE = self.tmp.with_suffix('.json.lock')
|
||||
|
||||
import trailing_monitor as tm
|
||||
self.tm = tm
|
||||
self.sent, self.submits = [], []
|
||||
self.state = {'quote': 70000, 'able': 100, 'disabled': False}
|
||||
self._orig_fns = (tm.kc.get_open_orders, tm.kc.get_watchlist_quotes, tm.kc.get_positions,
|
||||
tm.handler.place_trailing_legs, tm.send_telegram,
|
||||
tm.sidecar.is_disabled, tm.ledger.append,
|
||||
tm.datasource.nxt_eligible, tm.guards.determine_routing)
|
||||
tm.kc.get_open_orders = lambda acct, side=None: []
|
||||
tm.kc.get_watchlist_quotes = lambda codes, exchange='AL': {
|
||||
c: {'price': self.state['quote']} for c in codes}
|
||||
tm.kc.get_positions = lambda acct, exchange='KRX': [
|
||||
{'code': '005930', 'qty': self.state['able'], 'trde_able_qty': self.state['able']}]
|
||||
tm.handler.place_trailing_legs = self._fake_place
|
||||
tm.send_telegram = lambda msg, parse_mode=None: self.sent.append(msg)
|
||||
tm.sidecar.is_disabled = lambda: self.state['disabled']
|
||||
tm.ledger.append = lambda ev, payload: None
|
||||
tm.datasource.nxt_eligible = lambda code: True
|
||||
tm.guards.determine_routing = lambda now, nxt, force: '_AL'
|
||||
|
||||
self.steps = t.normalize_steps([{'pct': 10, 'cum': 20}, {'pct': 20, 'cum': 50},
|
||||
{'pct': 30, 'cum': 100}])
|
||||
self.arm = t.register_arm(account='일반', symbol='005930', symbol_name='삼성전자',
|
||||
qty=100, trigger_price=80000, steps=self.steps,
|
||||
card_id='AB3D')
|
||||
|
||||
def tearDown(self):
|
||||
(self.tm.kc.get_open_orders, self.tm.kc.get_watchlist_quotes, self.tm.kc.get_positions,
|
||||
self.tm.handler.place_trailing_legs, self.tm.send_telegram,
|
||||
self.tm.sidecar.is_disabled, self.tm.ledger.append,
|
||||
self.tm.datasource.nxt_eligible, self.tm.guards.determine_routing) = self._orig_fns
|
||||
t.STATE_FILE, t._LOCK_FILE = self._orig
|
||||
shutil.rmtree(self.tmp.parent, ignore_errors=True)
|
||||
|
||||
def _fake_place(self, account, symbol, steps, routing_suffix, card_id, dry_run,
|
||||
on_placed=None):
|
||||
placed = []
|
||||
for s in steps:
|
||||
self.submits.append({'n': s['n'], 'qty': s['qty'], 'cond': s['cond_uv'],
|
||||
'routing': routing_suffix})
|
||||
leg = dict(s, ord_no=f'O{s["n"]:04d}')
|
||||
placed.append(leg)
|
||||
if on_placed:
|
||||
on_placed(leg)
|
||||
return placed, [], []
|
||||
|
||||
def test_below_trigger_places_nothing(self):
|
||||
self.tm.check(force=True)
|
||||
self.assertEqual(self.submits, [])
|
||||
self.assertEqual(len(t.list_arms()), 1)
|
||||
|
||||
def test_reach_fires_and_promotes(self):
|
||||
self.state['quote'] = 81000
|
||||
self.tm.check(force=True)
|
||||
self.assertEqual(len(self.submits), 3)
|
||||
self.assertEqual([s['routing'] for s in self.submits], ['_AL'] * 3)
|
||||
self.assertEqual(t.list_arms(), [])
|
||||
self.assertEqual(len(t.list_active()), 1)
|
||||
self.assertEqual(t.list_active()[0]['peak'], 81000) # 고점 = 발동 시점 현재가
|
||||
self.assertIn('발동', self.sent[0])
|
||||
|
||||
def test_sidecar_blocks_before_marking(self):
|
||||
"""마킹 후 막히면 예약이 갇힌다 — 사이드카는 마킹 전에 봐야 한다."""
|
||||
self.state['quote'] = 81000
|
||||
self.state['disabled'] = True
|
||||
self.tm.check(force=True)
|
||||
self.assertEqual(self.submits, [])
|
||||
self.assertIsNone(t.get_arm(self.arm['id'])['firing_at'])
|
||||
self.assertEqual(len(t.list_arms()), 1)
|
||||
|
||||
def test_stuck_arm_is_not_refired(self):
|
||||
t.mark_arm_firing(self.arm['id'])
|
||||
self.state['quote'] = 81000
|
||||
self.tm.check(force=True)
|
||||
self.assertEqual(self.submits, [], '발동 처리 중단 예약을 재발주했다')
|
||||
self.assertEqual(len(t.list_arms()), 1)
|
||||
|
||||
def _age_arm(self, days=1):
|
||||
"""등록일을 과거로 밀어 만료 상태를 만든다."""
|
||||
past = (datetime.now(KST) - timedelta(days=days)).isoformat(timespec='seconds')
|
||||
arms = t.list_arms()
|
||||
arms[0]['created_at'] = past
|
||||
with t._FileLock(t._LOCK_FILE):
|
||||
t._write(arms=arms)
|
||||
|
||||
def test_expired_arm_is_purged_not_fired(self):
|
||||
"""어제 예약이 오늘 시세로 발동하면 안 된다 — 지우고 알린다."""
|
||||
self._age_arm()
|
||||
self.state['quote'] = 81000 # 발동가 도달 상태로 만들어도
|
||||
self.tm.check(force=True)
|
||||
self.assertEqual(self.submits, [], '만료 예약을 발주했다')
|
||||
self.assertEqual(t.list_arms(), [])
|
||||
self.assertEqual(len(t.list_active()), 0)
|
||||
self.assertTrue(any('만료' in m for m in self.sent), self.sent)
|
||||
|
||||
def test_expiry_purge_survives_quote_failure(self):
|
||||
"""시세 조회가 실패해도 만료 정리는 돼야 한다 (정리가 시세보다 앞이라서)."""
|
||||
self._age_arm()
|
||||
self.tm.kc.get_watchlist_quotes = lambda codes, exchange='AL': (
|
||||
_ for _ in ()).throw(RuntimeError('시세 실패'))
|
||||
self.tm.check(force=True)
|
||||
self.assertEqual(t.list_arms(), [])
|
||||
|
||||
def test_expiry_notice_is_batched(self):
|
||||
"""여러 건이 같이 만료돼도 텔레그램은 한 통."""
|
||||
t.register_arm(account='일반', symbol='000660', symbol_name='SK하이닉스', qty=10,
|
||||
trigger_price=500000, steps=self.steps)
|
||||
past = (datetime.now(KST) - timedelta(days=3)).isoformat(timespec='seconds')
|
||||
arms = t.list_arms()
|
||||
for a in arms:
|
||||
a['created_at'] = past
|
||||
with t._FileLock(t._LOCK_FILE):
|
||||
t._write(arms=arms)
|
||||
self.tm.check(force=True)
|
||||
self.assertEqual(t.list_arms(), [])
|
||||
self.assertEqual(len([m for m in self.sent if '만료' in m]), 1, self.sent)
|
||||
|
||||
def test_dry_run_does_not_purge(self):
|
||||
self._age_arm()
|
||||
self.tm.check(force=True, dry_run=True)
|
||||
self.assertEqual(len(t.list_arms()), 1)
|
||||
self.assertEqual(self.sent, [])
|
||||
|
||||
def test_open_order_query_failure_holds_the_arm(self):
|
||||
"""미체결 매도 수량을 모르면 발주하지 않는다 — 0 으로 단정하면 과다 매도가 된다."""
|
||||
self.state['quote'] = 81000
|
||||
self.tm.kc.get_open_orders = lambda acct, side=None: (_ for _ in ()).throw(
|
||||
RuntimeError('조회 실패'))
|
||||
self.tm.check(force=True)
|
||||
self.assertEqual(self.submits, [])
|
||||
self.assertEqual(len(t.list_arms()), 1)
|
||||
self.assertIsNone(t.get_arm(self.arm['id'])['firing_at'])
|
||||
|
||||
def test_pending_sell_reduces_sellable(self):
|
||||
"""같은 종목에 미체결 매도가 있으면 그만큼 빼고 발주한다."""
|
||||
self.state['quote'] = 81000
|
||||
self.tm.kc.get_open_orders = lambda acct, side=None: [
|
||||
{'code': '005930', 'side': 'SELL', 'unfilled_qty': 60, 'ord_no': 'X1',
|
||||
'routing_suffix': ''}]
|
||||
self.tm.check(force=True)
|
||||
self.assertEqual(sum(s['qty'] for s in self.submits), 40)
|
||||
|
||||
def test_position_query_failure_holds_instead_of_dropping(self):
|
||||
self.state['quote'] = 81000
|
||||
self.tm.kc.get_positions = lambda acct, exchange='KRX': (_ for _ in ()).throw(
|
||||
RuntimeError('조회 실패'))
|
||||
self.tm.check(force=True)
|
||||
self.assertEqual(self.submits, [])
|
||||
self.assertEqual(len(t.list_arms()), 1, '조회 실패로 예약을 지웠다')
|
||||
|
||||
def test_sold_out_position_drops_the_arm(self):
|
||||
self.state['quote'] = 81000
|
||||
self.state['able'] = 0
|
||||
self.tm.check(force=True)
|
||||
self.assertEqual(self.submits, [])
|
||||
self.assertEqual(t.list_arms(), [])
|
||||
self.assertIn('삭제', self.sent[0])
|
||||
|
||||
def test_partial_holding_shrinks_the_order(self):
|
||||
self.state['quote'] = 81000
|
||||
self.state['able'] = 40
|
||||
self.tm.check(force=True)
|
||||
self.assertEqual(sum(s['qty'] for s in self.submits), 40)
|
||||
self.assertIn('줄여', self.sent[0])
|
||||
|
||||
def test_arm_failure_does_not_break_stop_raising(self):
|
||||
"""arm 코드가 터져도 실주문을 지키는 손절선 상향은 완료돼야 한다."""
|
||||
lv = t.compute_step_levels(5500, self.steps, None)
|
||||
qtys = t.allocate_step_qty(300, [s['weight'] for s in self.steps])
|
||||
legs = [dict(s, qty=q, ord_no=f'A{s["n"]:04d}') for s, q in zip(lv, qtys)]
|
||||
res = t.register_steps(account='일반', symbol='381620', symbol_name='제닉스로보틱스',
|
||||
total_qty=300, peak=5500, steps=legs)
|
||||
self.tm.kc.get_open_orders = lambda acct, side=None: [
|
||||
{'ord_no': s['ord_no'], 'unfilled_qty': s['qty'], 'routing_suffix': ''} for s in legs]
|
||||
modified = []
|
||||
self.tm.kiwoom_order.modify_order = lambda **kw: (
|
||||
modified.append(kw['orig_ord_no']) or {'ok': True, 'new_ord_no': 'N1'})
|
||||
self.state['quote'] = 81000 # 두 종목 모두 고점 갱신 + arm 도달
|
||||
self.tm.handler.place_trailing_legs = lambda **kw: (_ for _ in ()).throw(
|
||||
RuntimeError('arm 폭발'))
|
||||
try:
|
||||
rc = self.tm.check(force=True)
|
||||
finally:
|
||||
self.tm.kiwoom_order.modify_order = self._orig_fns[3] and __import__(
|
||||
'orders.kiwoom_order', fromlist=['modify_order']).modify_order
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertEqual(len(modified), 3, '손절선 상향이 arm 예외에 휩쓸렸다')
|
||||
self.assertEqual(t.get(res['id'])['peak'], 81000)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
unittest.main(verbosity=2)
|
||||
|
||||
@@ -22,6 +22,30 @@
|
||||
|
||||
상태는 파일(state/trailing_stops.json)에 저장 — 웹·감시 루프가 각각 다른 프로세스라
|
||||
같은 예약 목록을 봐야 한다. 동시성은 fcntl flock 으로 직렬화 (pin.py 와 같은 패턴).
|
||||
|
||||
── 대기 예약 (arms) — 예약 트레일링, 2026-08-25 ──────────────────────────────
|
||||
|
||||
같은 파일에 `arms` 키가 하나 더 있다. **발동가에 닿으면 그때 트레일링을 시작하는 예약**으로,
|
||||
`reservations`(이미 키움에 주문이 나간 것)와 성질이 정반대다:
|
||||
|
||||
reservations : 주문이 키움에 있음 → 감시 루프가 죽어도 손절선은 살아있음, 장 마감에 소멸
|
||||
arms : 주문이 없고 이 파일에만 있음 → 감시가 죽으면 발동 안 함, **당일만 유효**
|
||||
|
||||
⚠️ **arm 도 스톱주문과 같은 수명(등록일 당일)이다** — 2026-08-25 관리자님 결정으로 무기한에서
|
||||
바뀌었다. 거래소가 지워주는 스톱주문과 달리 arm 은 우리 파일에만 있으니 우리가 지운다:
|
||||
`is_arm_expired` 판정 + 감시 루프의 다음 거래일 첫 사이클 정리.
|
||||
|
||||
⚠️ **두 갈래를 한 파일에 둔 이유는 발동이 "arm 제거 + 예약 등록"이라는 하나의 원자적
|
||||
전이여야 해서다.** promote_arm 이 락 하나 안에서 os.replace 1회로 끝낸다. 파일을 쪼개면
|
||||
락을 중첩해야 하고(데드락), 중간에 죽으면 둘 다 존재하거나 둘 다 사라진다.
|
||||
|
||||
⚠️ 그 대가로 **_write 는 None 인 쪽을 디스크에서 다시 읽어 보존**한다. 이 계약이 깨지면
|
||||
reservations 쪽 쓰기 6곳이 arms 를 조용히 전멸시킨다 (테스트로 고정해 둠).
|
||||
|
||||
⚠️ arm 의 steps 에는 **가격·주수를 저장하지 않는다**(정의 n/pct/cum/weight 만). routing_suffix
|
||||
도 저장하지 않는다. 갭 상승으로 발동가를 훌쩍 넘겨 열릴 수 있고, 무기한 대기라 등록 시점
|
||||
세션(KRX)과 발동 시점 세션(NXT)이 달라 옛 suffix 로 발주하면 거부된다. 둘 다 발동 시점에
|
||||
plan_arm_fire / guards.determine_routing 으로 새로 계산한다.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -34,7 +58,7 @@ from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from .guards import tick_size
|
||||
from .guards import after_trading_day, tick_size
|
||||
|
||||
WORKSPACE_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
STATE_FILE = WORKSPACE_ROOT / 'state' / 'trailing_stops.json'
|
||||
@@ -58,10 +82,18 @@ ORD_UV_GAP_TICKS = 2
|
||||
MAX_STEPS = 5
|
||||
|
||||
|
||||
# 혼동되는 글자(0/O, 1/I/l)를 뺀 영숫자 — 사람이 눈으로 읽고 옮겨 적는 id 라서.
|
||||
_ID_CHARS = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
|
||||
|
||||
|
||||
def _now_iso() -> str:
|
||||
return datetime.now(KST).isoformat(timespec='seconds')
|
||||
|
||||
|
||||
def _new_id(prefix: str) -> str:
|
||||
return prefix + ''.join(secrets.choice(_ID_CHARS) for _ in range(4))
|
||||
|
||||
|
||||
def floor_to_tick(price: int) -> int:
|
||||
"""호가단위로 내림. 매도 조건단가·지정가는 내림이 안전(더 빨리 발동/체결)."""
|
||||
t = tick_size(price)
|
||||
@@ -213,21 +245,48 @@ class _FileLock:
|
||||
_LOCK_FILE = STATE_FILE.with_suffix(STATE_FILE.suffix + '.lock')
|
||||
|
||||
|
||||
def _read() -> list:
|
||||
def _read_doc() -> dict:
|
||||
"""상태파일 전체. `reservations`(키움에 주문이 나가 있는 예약) 와
|
||||
`arms`(발동가 도달을 기다리는 대기 예약) 두 갈래가 한 파일에 들어 있다.
|
||||
|
||||
한 파일에 둔 이유는 발동이 "arm 제거 + 예약 등록" 이라는 하나의 원자적 전이이기
|
||||
때문이다. 파일이 둘이면 락을 중첩해야 하고, 중간에 죽으면 둘 다 존재하거나 둘 다
|
||||
사라진다.
|
||||
"""
|
||||
if not STATE_FILE.exists():
|
||||
return []
|
||||
return {'reservations': [], 'arms': []}
|
||||
try:
|
||||
data = json.loads(STATE_FILE.read_text(encoding='utf-8'))
|
||||
except (OSError, ValueError):
|
||||
return []
|
||||
return data.get('reservations') or []
|
||||
return {'reservations': [], 'arms': []}
|
||||
if not isinstance(data, dict):
|
||||
return {'reservations': [], 'arms': []}
|
||||
return {'reservations': data.get('reservations') or [],
|
||||
'arms': data.get('arms') or []}
|
||||
|
||||
|
||||
def _write(reservations: list) -> None:
|
||||
def _read() -> list:
|
||||
return _read_doc()['reservations']
|
||||
|
||||
|
||||
def _read_arms() -> list:
|
||||
return _read_doc()['arms']
|
||||
|
||||
|
||||
def _write(reservations: Optional[list] = None, arms: Optional[list] = None) -> None:
|
||||
"""None 인 쪽은 디스크의 현재 값을 유지한다.
|
||||
|
||||
⚠️ 두 갈래가 한 파일에 있으므로 한쪽만 쓰는 호출이 다른 쪽을 지우면 안 된다.
|
||||
반드시 _FileLock 안에서 부를 것 — 여기서 다시 읽어 병합한다.
|
||||
"""
|
||||
doc = _read_doc()
|
||||
if reservations is not None:
|
||||
doc['reservations'] = reservations
|
||||
if arms is not None:
|
||||
doc['arms'] = arms
|
||||
STATE_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
tmp = STATE_FILE.with_suffix(STATE_FILE.suffix + '.tmp')
|
||||
tmp.write_text(json.dumps({'reservations': reservations}, ensure_ascii=False, indent=2),
|
||||
encoding='utf-8')
|
||||
tmp.write_text(json.dumps(doc, ensure_ascii=False, indent=2), encoding='utf-8')
|
||||
os.replace(tmp, STATE_FILE)
|
||||
|
||||
|
||||
@@ -251,28 +310,20 @@ def find_by_symbol(account: str, symbol: str) -> Optional[dict]:
|
||||
return None
|
||||
|
||||
|
||||
def register_steps(account: str, symbol: str, symbol_name: str, total_qty: int,
|
||||
peak: int, steps: list, routing_suffix: str = '',
|
||||
card_id: Optional[str] = None,
|
||||
min_sell_price: Optional[int] = None) -> dict:
|
||||
"""키움에 계단별 스톱주문이 접수된 직후 호출 — 레그 목록을 예약 1건으로 등록.
|
||||
def _build_reservation(account: str, symbol: str, symbol_name: str, total_qty: int,
|
||||
peak: int, steps: list, routing_suffix: str,
|
||||
card_id: Optional[str], min_sell_price: Optional[int]) -> dict:
|
||||
"""접수된 레그 목록 → 예약 dict (파일 접근 없는 순수 조립).
|
||||
|
||||
steps 각 항목은 접수 결과까지 채워져 있어야 한다:
|
||||
{'n', 'pct', 'cum', 'weight', 'qty', 'ord_no', 'cond_uv', 'ord_uv'}
|
||||
|
||||
접수에 실패한 레그는 호출측이 빼고 넘긴다 — 여기 등록된 것은 전부 키움에 살아있는
|
||||
주문이어야 감시 루프의 생존 판정이 성립한다.
|
||||
|
||||
초기 고점(peak)은 등록 시점 현재가다. 과거 고점(52주·매수후) 기준 옵션은 손절선이
|
||||
현재가 위로 올라가 즉시 발동하는 구조라 2026-07-30 제거됐다.
|
||||
register_steps(등록 즉시 트레일링)와 promote_arm(대기 예약 발동)이 공유한다.
|
||||
"""
|
||||
if not steps:
|
||||
raise ValueError('등록할 계단이 없음 — 접수 성공한 레그가 하나도 없다')
|
||||
for s in steps:
|
||||
if not s.get('ord_no'):
|
||||
raise ValueError(f'{s.get("n")}단계 ord_no 없음 — 접수 확인된 주문만 등록')
|
||||
res = {
|
||||
'id': 'TRL-' + ''.join(secrets.choice('ABCDEFGHJKLMNPQRSTUVWXYZ23456789') for _ in range(4)),
|
||||
return {
|
||||
'id': _new_id('TRL-'),
|
||||
'account': account,
|
||||
'symbol': symbol,
|
||||
'symbol_name': symbol_name,
|
||||
@@ -289,6 +340,25 @@ def register_steps(account: str, symbol: str, symbol_name: str, total_qty: int,
|
||||
'updated_at': _now_iso(),
|
||||
'entry_peak': peak,
|
||||
}
|
||||
|
||||
|
||||
def register_steps(account: str, symbol: str, symbol_name: str, total_qty: int,
|
||||
peak: int, steps: list, routing_suffix: str = '',
|
||||
card_id: Optional[str] = None,
|
||||
min_sell_price: Optional[int] = None) -> dict:
|
||||
"""키움에 계단별 스톱주문이 접수된 직후 호출 — 레그 목록을 예약 1건으로 등록.
|
||||
|
||||
steps 각 항목은 접수 결과까지 채워져 있어야 한다:
|
||||
{'n', 'pct', 'cum', 'weight', 'qty', 'ord_no', 'cond_uv', 'ord_uv'}
|
||||
|
||||
접수에 실패한 레그는 호출측이 빼고 넘긴다 — 여기 등록된 것은 전부 키움에 살아있는
|
||||
주문이어야 감시 루프의 생존 판정이 성립한다.
|
||||
|
||||
초기 고점(peak)은 등록 시점 현재가다. 과거 고점(52주·매수후) 기준 옵션은 손절선이
|
||||
현재가 위로 올라가 즉시 발동하는 구조라 2026-07-30 제거됐다.
|
||||
"""
|
||||
res = _build_reservation(account, symbol, symbol_name, total_qty, peak, steps,
|
||||
routing_suffix, card_id, min_sell_price)
|
||||
with _FileLock(_LOCK_FILE):
|
||||
reservations = _read()
|
||||
reservations.append(res)
|
||||
@@ -399,3 +469,249 @@ def remove(res_id: str, reason: str = '') -> Optional[dict]:
|
||||
gone['removed_reason'] = reason
|
||||
return gone
|
||||
return None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 대기 예약 (arm) — 발동가에 닿으면 그때 트레일링을 등록한다
|
||||
#
|
||||
# 트레일링 스톱은 등록 즉시 계단별 스톱주문이 키움에 나가고 그 순간의 현재가가 고점이
|
||||
# 된다. 아직 오르지 않은 종목엔 그게 불리해서, 발동가를 미리 정해두고 현재가가 거기에
|
||||
# 올라와 닿는 순간 그때의 현재가를 고점으로 트레일링을 시작하는 갈래를 둔다.
|
||||
#
|
||||
# 대기 중인 예약은 키움이 아니라 이 파일에만 있다 — 거래소가 지워주지 않으므로 **우리가**
|
||||
# 스톱주문과 같은 수명(등록일 당일)을 강제한다(is_arm_expired). 2026-08-25 관리자님 결정.
|
||||
# 대신 감시 프로세스가 죽어 있으면 발동하지 않는다 — 이건 스톱주문과 정반대 성질이다.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def list_arms() -> list:
|
||||
"""대기 예약 전체 (락 없이 읽기 — 감시 루프의 스냅샷용)."""
|
||||
return _read_arms()
|
||||
|
||||
|
||||
def get_arm(arm_id: str) -> Optional[dict]:
|
||||
for a in _read_arms():
|
||||
if a['id'] == arm_id:
|
||||
return a
|
||||
return None
|
||||
|
||||
|
||||
def find_arm_by_symbol(account: str, symbol: str) -> Optional[dict]:
|
||||
"""같은 계좌·종목의 대기 예약. 중복 등록 차단용."""
|
||||
for a in _read_arms():
|
||||
if a['account'] == account and a['symbol'] == symbol:
|
||||
return a
|
||||
return None
|
||||
|
||||
|
||||
def register_arm(account: str, symbol: str, symbol_name: str, qty: int,
|
||||
trigger_price: int, steps: list,
|
||||
min_sell_price: Optional[int] = None,
|
||||
card_id: Optional[str] = None) -> dict:
|
||||
"""PIN 승인 직후 호출 — 발동가 도달을 기다리는 예약 1건 등록. 키움 주문은 아직 없다.
|
||||
|
||||
steps 는 normalize_steps 결과, 즉 **정의만**(n/pct/cum/weight)이다. 조건단가·지정가·
|
||||
계단별 주수는 저장하지 않는다 — 갭 상승으로 발동가를 훌쩍 넘겨 열릴 수 있고 그 사이
|
||||
보유수량이 줄었을 수도 있어서, 발동 시점의 현재가와 매도가능 수량으로 다시 계산해야
|
||||
실제 시세와 맞는다.
|
||||
|
||||
routing_suffix 도 저장하지 않는다. 당일 안에서도 등록 시점 세션(정규장)과 발동 시점
|
||||
세션(NXT 애프터)이 다를 수 있고, 옛 suffix 로 발주하면 거부된다.
|
||||
"""
|
||||
if not steps:
|
||||
raise ValueError('계단이 비어 있음')
|
||||
if trigger_price <= 0:
|
||||
raise ValueError('발동가가 0 이하')
|
||||
arm = {
|
||||
'id': _new_id('ARM-'),
|
||||
'account': account,
|
||||
'symbol': symbol,
|
||||
'symbol_name': symbol_name,
|
||||
'qty': qty,
|
||||
'trigger_price': trigger_price,
|
||||
'min_sell_price': min_sell_price,
|
||||
'steps': [{'n': s['n'], 'pct': s['pct'], 'cum': s['cum'], 'weight': s['weight']}
|
||||
for s in steps],
|
||||
'card_id': card_id,
|
||||
'created_at': _now_iso(),
|
||||
'updated_at': _now_iso(),
|
||||
'firing_at': None,
|
||||
}
|
||||
with _FileLock(_LOCK_FILE):
|
||||
arms = _read_arms()
|
||||
arms.append(arm)
|
||||
_write(arms=arms)
|
||||
return arm
|
||||
|
||||
|
||||
def remove_arm(arm_id: str, reason: str = '') -> Optional[dict]:
|
||||
"""대기 예약 제거 (취소·수량 소멸·계단 소멸). 반환값은 제거된 arm — 알림 메시지용."""
|
||||
with _FileLock(_LOCK_FILE):
|
||||
arms = _read_arms()
|
||||
for i, a in enumerate(arms):
|
||||
if a['id'] == arm_id:
|
||||
gone = arms.pop(i)
|
||||
_write(arms=arms)
|
||||
gone['removed_reason'] = reason
|
||||
return gone
|
||||
return None
|
||||
|
||||
|
||||
def mark_arm_firing(arm_id: str) -> bool:
|
||||
"""발주 직전 마킹. 이미 마킹돼 있으면 False (재발동 차단).
|
||||
|
||||
키움 발주가 성공한 뒤 promote_arm 전에 프로세스가 죽으면 다음 사이클이 같은 종목을
|
||||
또 판다. 창은 1초 남짓이지만 결과가 이중 매도라, 발주를 시작했다는 사실을 먼저
|
||||
디스크에 남긴다.
|
||||
|
||||
⚠️ 마킹이 남아있는 arm 은 **자동으로 재시도하지 않는다** — 주문이 나갔는지 알 수 없는
|
||||
상태에서 다시 내는 게 바로 이중 매도다. 감시 루프는 알림만 보내고, 관리자님이 실제
|
||||
미체결을 확인한 뒤 자산웹에서 취소한다.
|
||||
"""
|
||||
with _FileLock(_LOCK_FILE):
|
||||
arms = _read_arms()
|
||||
for a in arms:
|
||||
if a['id'] != arm_id:
|
||||
continue
|
||||
if a.get('firing_at'):
|
||||
return False
|
||||
a['firing_at'] = _now_iso()
|
||||
a['updated_at'] = _now_iso()
|
||||
_write(arms=arms)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def is_arm_expired(arm: dict, now: Optional[datetime] = None) -> bool:
|
||||
"""등록일의 거래 세션이 끝났으면 만료 (2026-08-25 관리자님 결정으로 무기한 → 당일).
|
||||
|
||||
트레일링 스톱주문이 장 마감에 소멸하는 것과 **같은 수명**을 갖게 한 것이다. 대기 예약은
|
||||
키움이 아니라 우리 파일에만 있어 거래소가 지워주지 않으므로, 우리가 지워야 한다.
|
||||
|
||||
판정은 두 갈래:
|
||||
· 등록일이 오늘보다 이전 → 만료 (밤새 넘긴 것·주말 넘긴 것)
|
||||
· 등록일이 오늘이고 이미 20:00 지남 → 만료
|
||||
|
||||
⚠️ 감시 루프는 08:00~19:59 에만 도므로 **20:00 직후에 지워지지는 않는다.** 실제 정리는
|
||||
다음 거래일 첫 사이클이다. 그 사이(장외)에 자산웹을 열면 만료 상태로 표시된다 —
|
||||
화면이 거짓말하지 않게 웹도 같은 판정을 쓴다.
|
||||
"""
|
||||
now = now or datetime.now(KST)
|
||||
created = (arm.get('created_at') or '')[:10]
|
||||
if not created:
|
||||
return False # 날짜를 모르면 함부로 지우지 않는다
|
||||
today = now.strftime('%Y-%m-%d')
|
||||
if created < today:
|
||||
return True
|
||||
return created == today and after_trading_day(now)
|
||||
|
||||
|
||||
def record_arm_leg(arm_id: str, leg: dict) -> bool:
|
||||
"""발동 중 레그 하나가 접수될 때마다 즉시 기록 (write-ahead).
|
||||
|
||||
firing_at 만으로는 중간에 죽었을 때 "몇 단이 실제로 나갔는지" 를 알 수 없다. 키움엔 우리
|
||||
주문을 식별하는 client order id 가 없어 미체결 목록만으로는 관리자님이 직접 낸 스톱주문과
|
||||
구분되지 않는다 — 그래서 주문번호를 우리가 적어둔다.
|
||||
"""
|
||||
with _FileLock(_LOCK_FILE):
|
||||
arms = _read_arms()
|
||||
for a in arms:
|
||||
if a['id'] != arm_id:
|
||||
continue
|
||||
a.setdefault('fired_legs', []).append(
|
||||
{'n': leg['n'], 'qty': leg['qty'], 'cond_uv': leg['cond_uv'],
|
||||
'ord_uv': leg['ord_uv'], 'ord_no': leg.get('ord_no', '')})
|
||||
a['updated_at'] = _now_iso()
|
||||
_write(arms=arms)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def plan_arm_fire(arm: dict, cur_price: int, sellable_qty: int) -> dict:
|
||||
"""대기 예약 + 현재가 + 매도가능 수량 → 발주 계획 (순수 함수 — 단위테스트 대상).
|
||||
|
||||
반환 {'action', 'reason', 'peak', 'qty_used', 'reduced', 'legs'}
|
||||
fire : legs 를 그대로 발주한다
|
||||
skip : 아직 아니다 (미도달·시세 없음) — 아무것도 하지 않는다
|
||||
drop : 발동할 수 없다 (매도가능 0주·수량 부족) → 예약 삭제 + 알림
|
||||
|
||||
고점(peak)은 발동 시점 현재가다. 발동가가 아니라 현재가를 쓰는 이유는 갭 상승으로 발동가를
|
||||
훌쩍 넘겨 열릴 수 있어서다 — 현재가 >= 발동가 이므로 손절선은 승인 시점 미리보기보다 항상
|
||||
같거나 높다(관리자님에게 불리해지지 않는 방향).
|
||||
|
||||
수량은 min(예약 수량, 매도가능)이다. 대기 중 관리자님이 직접 팔았으면 그만큼 줄여 재배분한다
|
||||
— 대기 예약이 보유분을 잠그지 않기로 한 결정의 뒷수습이 여기다.
|
||||
"""
|
||||
def _out(action, reason):
|
||||
return {'action': action, 'reason': reason, 'peak': cur_price,
|
||||
'qty_used': 0, 'reduced': False, 'legs': []}
|
||||
|
||||
if cur_price <= 0:
|
||||
return _out('skip', 'no_price')
|
||||
if cur_price < arm['trigger_price']:
|
||||
return _out('skip', 'not_reached')
|
||||
if sellable_qty <= 0:
|
||||
return _out('drop', 'no_sellable')
|
||||
|
||||
qty_used = min(arm['qty'], sellable_qty)
|
||||
steps = arm.get('steps') or []
|
||||
leveled = compute_step_levels(cur_price, steps, arm.get('min_sell_price'))
|
||||
qtys = allocate_step_qty(qty_used, [s['weight'] for s in steps])
|
||||
# 0주가 된 계단은 발주할 수 없으니 떨어낸다. 단계 번호(n)는 유지해 몇 단계가 빠졌는지 보인다.
|
||||
legs = [dict(s, qty=q) for s, q in zip(leveled, qtys) if q > 0]
|
||||
if not legs:
|
||||
return _out('drop', 'qty_too_small')
|
||||
# 실주문 직전 마지막 안전망. propose 가 발동가 기준으로 이미 걸렀고 현재가 >= 발동가,
|
||||
# 최저 매도가는 승인 후 불변이라 정상 경로에선 도달하지 않는다. 그래도 남기는 이유는
|
||||
# 여기를 지나면 되돌릴 수 없는 매도 주문이 나가기 때문이다.
|
||||
if legs[0]['cond_uv'] >= cur_price:
|
||||
return _out('skip', 'immediate')
|
||||
return {'action': 'fire', 'reason': '', 'peak': cur_price, 'qty_used': qty_used,
|
||||
'reduced': qty_used < arm['qty'], 'legs': legs}
|
||||
|
||||
|
||||
def promote_arm(arm_id: str, peak: int, steps: list,
|
||||
routing_suffix: str = '') -> Optional[dict]:
|
||||
"""발동 완료 — 대기 예약을 지우고 같은 내용의 트레일링 예약을 등록 (한 락 안에서 원자적).
|
||||
|
||||
steps 는 접수 결과까지 채워진 레그 목록(register_steps 와 같은 형식).
|
||||
둘을 따로 하면 중간에 죽었을 때 arm 과 예약이 동시에 존재하거나 둘 다 사라진다.
|
||||
|
||||
반환 {'arm': 제거된 arm, 'reservation': 새 예약} — 알림 메시지 조립용.
|
||||
"""
|
||||
with _FileLock(_LOCK_FILE):
|
||||
doc = _read_doc()
|
||||
arms = doc['arms']
|
||||
for i, a in enumerate(arms):
|
||||
if a['id'] != arm_id:
|
||||
continue
|
||||
res = _build_reservation(
|
||||
a['account'], a['symbol'], a['symbol_name'],
|
||||
sum(s['qty'] for s in steps), peak, steps,
|
||||
routing_suffix, a.get('card_id'), a.get('min_sell_price'))
|
||||
arms.pop(i)
|
||||
reservations = doc['reservations']
|
||||
reservations.append(res)
|
||||
_write(reservations=reservations, arms=arms)
|
||||
return {'arm': a, 'reservation': res}
|
||||
return None
|
||||
|
||||
|
||||
def should_notify_arm_failure(arm_id: str, now_ts: float, cooldown_sec: int) -> bool:
|
||||
"""발동 실패 알림을 보낼지. 보낸다고 판단하면 그 시점을 기록한다 (락 안에서 원자적).
|
||||
|
||||
firing_at 이 걸린 채 남은 arm 이나 사이드카 ON 상태는 매 사이클 같은 판정이 반복된다 —
|
||||
쿨다운 없이 알리면 19:59까지 매분 텔레그램이 쏟아진다. should_notify_failure 와 같은 패턴.
|
||||
"""
|
||||
with _FileLock(_LOCK_FILE):
|
||||
arms = _read_arms()
|
||||
for a in arms:
|
||||
if a['id'] != arm_id:
|
||||
continue
|
||||
last = a.get('last_fail_notice_ts') or 0
|
||||
if now_ts - last < cooldown_sec:
|
||||
return False
|
||||
a['last_fail_notice_ts'] = now_ts
|
||||
_write(arms=arms)
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -4,14 +4,26 @@
|
||||
**예약 1건 = 계단(레그) N개.** 고점 대비 하락률이 깊어질수록 더 많이 파는 계단식 청산이라
|
||||
계단마다 스톱주문이 따로 걸려 있다. 고점이 오르면 살아있는 레그를 전부 상향 정정한다.
|
||||
|
||||
LLM을 깨우지 않는다. 하는 일은 딱 두 가지:
|
||||
LLM을 깨우지 않는다. 하는 일은 세 가지:
|
||||
1. 레그가 아직 미체결로 살아있는지 확인 (ka10075) — 사라졌으면 정리+알림
|
||||
2. 현재가가 저장된 고점을 넘었으면 kt10002 정정으로 계단별 조건단가·지정가를 함께 올림
|
||||
3. **예약 트레일링(TRAILING_ARM) 발동** — 현재가가 발동가에 닿으면 그 시점 현재가를
|
||||
고점으로 계단별 스톱주문을 발주하고, 대기 예약을 트레일링 예약으로 승격 (2026-08-25)
|
||||
|
||||
⚠️ 3번이 **신규 발주 경로**다 (2026-08-25 관리자님 명시 승인). 그전까지 이 감시가 허용된
|
||||
근거는 "modify_order 하나만 부른다" 였는데 이제는 다르다. 바뀐 근거는 **계좌·종목·수량·
|
||||
계단·발동가가 PIN 승인 시점에 전부 확정되고, 감시는 발동가 도달 판정만 한다** 는 것이다.
|
||||
수량 증가·방향 변경 경로는 여전히 없다 (매도가능 부족 시 **축소** 재배분만).
|
||||
|
||||
신규 발주·수량 변경·방향 변경은 하지 않는다. 사람이 PIN으로 승인한 주문의
|
||||
조건단가를 올리는 것뿐이고, 상향 전용이라 손절선이 내려가는 일은 없다.
|
||||
한 계단이 체결돼도 남은 계단을 재배치하지 않는다 — 재배치엔 취소+신규 발주가 필요해
|
||||
감시 루프에 자동 발주 경로가 생긴다(2026-07-31 관리자님 결정).
|
||||
감시 루프에 (승인 범위를 넘는) 자동 발주 경로가 생긴다(2026-07-31 관리자님 결정).
|
||||
|
||||
⚠️ arm 단계는 손절선 상향이 **끝난 뒤** 별도 try/except 로 돈다. 순서가 설계다 — 상향은
|
||||
이미 나간 주문을 지키는 동작이라 arm 쪽 어떤 예외도 그걸 선점해선 안 된다.
|
||||
|
||||
⚠️ 발동은 2단계 마킹으로 보호한다. 발주 전에 firing_at 을 디스크에 남기고 레그마다
|
||||
주문번호를 적는다. **firing_at 이 남은 예약은 자동 재발동하지 않는다** — 주문이 나갔는지
|
||||
모르는 상태에서 다시 내는 게 곧 이중 매도다. 알림만 보내고 사람이 확인해 취소한다.
|
||||
|
||||
⚠️ 레그가 사라진 사유는 **주문번호 단위**인 kt00007 로 판정한다. 종목 단위 집계인
|
||||
당일매매일지(ka10170)를 쓰면 한 계단이 체결된 날 나머지 계단이 장 마감으로 소멸했을 때
|
||||
@@ -60,7 +72,8 @@ WORKSPACE = Path('/Users/snowoyh/.openclaw/agents/stock/workspace')
|
||||
sys.path.insert(0, str(WORKSPACE / 'scripts'))
|
||||
|
||||
import kiwoom_client as kc # noqa: E402
|
||||
from orders import guards, kiwoom_order, trailing # noqa: E402
|
||||
from orders import card, datasource, guards, kiwoom_order, ledger, sidecar, trailing # noqa: E402
|
||||
from orders import handler # noqa: E402
|
||||
from orders.handler import send_telegram # noqa: E402
|
||||
|
||||
# 정정 실패 알림 쿨다운 — NXT 시간대에 KRX/SOR 원주문 정정이 거부되면 매 사이클 반복된다.
|
||||
@@ -180,27 +193,240 @@ def _notify_modify_failed(res: dict, step: dict, reason: str) -> None:
|
||||
parse_mode=None)
|
||||
|
||||
|
||||
def _sellable_qty(account: str, symbol: str, open_rows) -> tuple:
|
||||
"""(매도가능 수량, 조회성공). 조회 실패를 0 과 반드시 구분한다 — 눈감고 발주하면 안 되고,
|
||||
반대로 실패를 0 으로 읽으면 살아있는 예약을 지운다.
|
||||
|
||||
trde_able_qty 가 미체결 매도를 이미 뺐는지 확실하지 않아 min 으로 이중차감을 피한다.
|
||||
"""
|
||||
try:
|
||||
positions = kc.get_positions(account)
|
||||
except Exception as e:
|
||||
_log(f'보유 조회 실패 [{account}]: {e!r}')
|
||||
return 0, False
|
||||
pos = next((p for p in positions if p.get('code') == symbol), None)
|
||||
if pos is None:
|
||||
return 0, True
|
||||
hold = pos.get('qty') or 0
|
||||
able = pos.get('trde_able_qty') or 0
|
||||
pending = sum((o.get('unfilled_qty') or 0) for o in (open_rows or [])
|
||||
if o.get('code') == symbol and o.get('side') == 'SELL')
|
||||
return max(0, min(able, hold - pending)), True
|
||||
|
||||
|
||||
def _notify_arm_issue(arm: dict, text: str) -> None:
|
||||
"""대기 예약 관련 경고 — 예약 단위 쿨다운(매 사이클 반복되는 사유가 많다)."""
|
||||
if not trailing.should_notify_arm_failure(arm['id'], time.time(), FAIL_NOTICE_COOLDOWN_SEC):
|
||||
_log(f' 알림 생략 (쿨다운 {FAIL_NOTICE_COOLDOWN_SEC}초 내 이미 발송)')
|
||||
return
|
||||
try:
|
||||
send_telegram(
|
||||
f'⚠️ 예약 트레일링 — {arm["symbol_name"]} ({arm["symbol"]})\n'
|
||||
f'계좌: {arm["account"]} · 발동가 {arm["trigger_price"]:,}원 · {arm["qty"]:,}주\n'
|
||||
f'{text}',
|
||||
parse_mode=None)
|
||||
except Exception as e:
|
||||
_log(f'알림 실패: {e!r}')
|
||||
|
||||
|
||||
def _fire_arm(arm: dict, plan: dict, sess: str, dry_run: bool) -> None:
|
||||
"""발동 — 계단 발주 후 대기 예약을 트레일링 예약으로 승격. 여기부터 되돌릴 수 없다."""
|
||||
symbol = arm['symbol']
|
||||
# 라우팅은 등록 시점이 아니라 지금 정한다 — 같은 날 안에서도 정규장에 등록해 NXT
|
||||
# 애프터에 발동할 수 있고, 옛 suffix 로 발주하면 거부된다.
|
||||
try:
|
||||
suffix = guards.determine_routing(datetime.now(KST), datasource.nxt_eligible(symbol), None)
|
||||
except ValueError as e:
|
||||
_log(f'라우팅 결정 실패 {arm["id"]} (세션 {sess}): {e!r}')
|
||||
return
|
||||
|
||||
if dry_run:
|
||||
_log(f'[dry-run] 발동 {arm["id"]} {arm["symbol_name"]} '
|
||||
f'발동가 {arm["trigger_price"]:,} ≤ 현재가 {plan["peak"]:,} · '
|
||||
f'{plan["qty_used"]:,}주 · routing {suffix or "KRX"}')
|
||||
for s in plan['legs']:
|
||||
_log(f' {s["n"]}단계 −{s["pct"]}% 조건 {s["cond_uv"]:,} / 지정 {s["ord_uv"]:,} '
|
||||
f'· {s["qty"]:,}주')
|
||||
return
|
||||
|
||||
# write-ahead: 발주를 시작했다는 사실을 먼저 디스크에 남긴다. 이 마킹이 있으면 다음
|
||||
# 사이클은 재발동하지 않는다 — 주문이 나갔는지 모르는 상태에서 다시 내는 게 이중 매도다.
|
||||
if not trailing.mark_arm_firing(arm['id']):
|
||||
_log(f'발동 마킹 실패 {arm["id"]} — 이미 발주 시도된 예약 (재발동 안 함)')
|
||||
return
|
||||
|
||||
placed, failed, _details = handler.place_trailing_legs(
|
||||
account=arm['account'], symbol=symbol, steps=plan['legs'],
|
||||
routing_suffix=suffix, card_id=arm.get('card_id'), dry_run=False,
|
||||
on_placed=lambda leg: trailing.record_arm_leg(arm['id'], leg))
|
||||
|
||||
if not placed:
|
||||
reason = failed[0]['reason'] if failed else 'UNKNOWN'
|
||||
detail = failed[0].get('error', '') if failed else ''
|
||||
_log(f'발동 전량 실패 {arm["id"]} {arm["symbol_name"]}: {reason} {detail}')
|
||||
ledger.append('arm_fire_failed', {'arm_id': arm['id'], 'account': arm['account'],
|
||||
'symbol': symbol, 'reason': reason,
|
||||
'error': str(detail), 'stage': 'submit'})
|
||||
# firing 마킹은 그대로 둔다 — 정말 한 건도 안 나갔는지 우리가 단정할 수 없다.
|
||||
_notify_arm_issue(arm, f'계단 {len(plan["legs"])}단 전부 접수 실패 ({reason}) {detail}\n'
|
||||
f'예약은 발동 처리 중단 상태로 남습니다 — 미체결을 확인하고 '
|
||||
f'자산웹에서 취소해 주세요.')
|
||||
return
|
||||
|
||||
registerable = [s for s in placed if s.get('ord_no')]
|
||||
promoted = None
|
||||
if registerable:
|
||||
try:
|
||||
promoted = trailing.promote_arm(arm['id'], plan['peak'], registerable, suffix)
|
||||
except Exception as e:
|
||||
_log(f'⚠️ 승격 실패 {arm["id"]}: {e!r}')
|
||||
|
||||
info = {'card_id': arm.get('card_id'), 'symbol': symbol, 'symbol_name': arm['symbol_name'],
|
||||
'account': arm['account'], 'total_qty': plan['qty_used'], 'arm_qty': arm['qty'],
|
||||
'qty_reduced': plan['reduced'], 'trigger_price': arm['trigger_price'],
|
||||
'peak': plan['peak'], 'min_sell_price': arm.get('min_sell_price'),
|
||||
'placed': placed, 'failed': failed, 'dry_run': False,
|
||||
'res_id': (promoted['reservation']['id'] if promoted else None),
|
||||
'register_error': None if promoted else '감시 등록 실패 — 손절선이 따라 올라가지 않습니다'}
|
||||
ledger.append('arm_fired', {'arm_id': arm['id'], 'account': arm['account'], 'symbol': symbol,
|
||||
'peak': plan['peak'], 'qty': plan['qty_used'],
|
||||
'legs': len(placed), 'res_id': info['res_id']})
|
||||
_log(f'발동 {arm["id"]} → {info["res_id"]} {arm["symbol_name"]} '
|
||||
f'{len(placed)}단 · {plan["qty_used"]:,}주 · 고점 {plan["peak"]:,}')
|
||||
try:
|
||||
send_telegram(card.format_arm_fired(info), parse_mode=None)
|
||||
except Exception as e:
|
||||
_log(f'알림 실패: {e!r}')
|
||||
|
||||
|
||||
def _expire_arms(arms: list, dry_run: bool) -> list:
|
||||
"""만료된 대기 예약을 지우고 살아있는 것만 돌려준다 (2026-08-25).
|
||||
|
||||
스톱주문은 장 마감에 거래소가 지워주지만 대기 예약은 우리 파일에만 있으니 우리가 지운다.
|
||||
감시 루프가 08:00~19:59 에만 도므로 실제 정리 시점은 **다음 거래일 첫 사이클**이다.
|
||||
|
||||
알림은 한 통으로 묶는다 — 여러 종목을 걸어두고 하루가 지나면 예약마다 보내면 아침에
|
||||
텔레그램이 몰아친다.
|
||||
"""
|
||||
alive, dead = [], []
|
||||
for a in arms:
|
||||
(dead if trailing.is_arm_expired(a) else alive).append(a)
|
||||
if not dead:
|
||||
return alive
|
||||
for a in dead:
|
||||
_log(f'대기 예약 만료 {a["id"]} {a["symbol_name"]} '
|
||||
f'(등록 {a.get("created_at", "?")[:16]} · 발동가 {a["trigger_price"]:,})')
|
||||
if not dry_run:
|
||||
trailing.remove_arm(a['id'], 'expired_end_of_day')
|
||||
if dry_run:
|
||||
return alive
|
||||
lines = [f'🗓 예약 트레일링 만료 취소 — {len(dead)}건',
|
||||
'장 마감이 지나 자동 취소했습니다 (스톱주문과 같은 당일 유효).']
|
||||
for a in dead:
|
||||
lines.append(f'· {a["symbol_name"]} ({a["symbol"]}) [{a["account"]}] '
|
||||
f'{a["qty"]:,}주 · 발동가 {a["trigger_price"]:,}원 — 미발동')
|
||||
lines.append('계속 노리시려면 자산웹에서 다시 등록해 주세요.')
|
||||
try:
|
||||
send_telegram('\n'.join(lines), parse_mode=None)
|
||||
except Exception as e:
|
||||
_log(f'알림 실패: {e!r}')
|
||||
return alive
|
||||
|
||||
|
||||
def _check_arms(arms: list, quotes: dict, open_by_acct: dict, sess: str, dry_run: bool) -> None:
|
||||
"""대기 예약 발동 판정. 평시(미도달)엔 추가 API 콜 0 — 시세는 호출측 배치를 재사용한다.
|
||||
|
||||
만료분은 호출측(check)이 이미 걷어낸 상태로 넘어온다 — 시세 조회가 실패해도 정리는
|
||||
돼야 하므로 순서상 앞에 둔다.
|
||||
"""
|
||||
# 사이드카는 마킹 전에 본다. 마킹 후 전 레그가 SidecarBlocked 로 죽으면 예약이 발동 처리
|
||||
# 중단 상태로 갇힌다.
|
||||
if sidecar.is_disabled():
|
||||
reached = [a for a in arms
|
||||
if ((quotes.get(a['symbol']) or {}).get('price') or 0) >= a['trigger_price']]
|
||||
for a in reached:
|
||||
_log(f'사이드카 ON — 발동 보류 {a["id"]} {a["symbol_name"]}')
|
||||
_notify_arm_issue(a, '사이드카가 켜져 있어 발동을 보류했습니다 (매매 차단). '
|
||||
'끄면 다음 사이클에 발동합니다.')
|
||||
return
|
||||
|
||||
for arm in arms:
|
||||
if arm.get('firing_at'):
|
||||
_log(f'발동 처리 중단 상태 {arm["id"]} {arm["symbol_name"]} '
|
||||
f'(firing_at={arm["firing_at"]}) — 재발동 안 함')
|
||||
legs = arm.get('fired_legs') or []
|
||||
nos = ', '.join(s.get('ord_no', '') for s in legs if s.get('ord_no')) or '없음'
|
||||
_notify_arm_issue(arm, f'발동 처리가 중단된 상태입니다 (시작 {arm["firing_at"]}).\n'
|
||||
f'접수된 주문: {nos}\n'
|
||||
f'키움 미체결을 확인하고 자산웹에서 예약을 취소해 주세요. '
|
||||
f'중복 매도를 막기 위해 자동 재시도하지 않습니다.')
|
||||
continue
|
||||
|
||||
cur = (quotes.get(arm['symbol']) or {}).get('price') or 0
|
||||
if cur < arm['trigger_price']:
|
||||
continue # 평시 경로 — 여기서 끝나면 API 콜 0
|
||||
|
||||
# 미체결 조회가 실패한 계좌는 판단을 보류한다 — 예약 경로와 같은 규칙이다.
|
||||
# 미체결 매도 수량을 모르는 채로 발주하면 과다 매도가 될 수 있다(0 으로 단정 금지).
|
||||
open_rows = open_by_acct.get(arm['account'])
|
||||
if open_rows is None:
|
||||
_log(f'미체결 조회 실패로 발동 보류 {arm["id"]} {arm["symbol_name"]}')
|
||||
continue
|
||||
|
||||
sellable, ok = _sellable_qty(arm['account'], arm['symbol'], open_rows)
|
||||
if not ok:
|
||||
_log(f'보유 조회 실패로 발동 보류 {arm["id"]} {arm["symbol_name"]}')
|
||||
continue # 눈감고 발주하지 않는다. 다음 사이클에 다시 본다
|
||||
|
||||
plan = trailing.plan_arm_fire(arm, cur, sellable)
|
||||
if plan['action'] == 'drop':
|
||||
why = {'no_sellable': '보유 수량이 없어', 'qty_too_small': '수량이 부족해 계단을 나눌 수 없어'}
|
||||
_log(f'발동 불가 {arm["id"]} {arm["symbol_name"]} — {plan["reason"]}')
|
||||
if not dry_run:
|
||||
trailing.remove_arm(arm['id'], plan['reason'])
|
||||
send_telegram(
|
||||
f'🗑 예약 트레일링 삭제 — {arm["symbol_name"]} ({arm["symbol"]})\n'
|
||||
f'계좌: {arm["account"]} · 발동가 {arm["trigger_price"]:,}원 도달했지만 '
|
||||
f'{why.get(plan["reason"], plan["reason"])} 발동하지 못했습니다.\n'
|
||||
f'예약 {arm["qty"]:,}주 · 매도가능 {sellable:,}주',
|
||||
parse_mode=None)
|
||||
continue
|
||||
if plan['action'] != 'fire':
|
||||
_log(f'발동 보류 {arm["id"]} {arm["symbol_name"]} — {plan["reason"]}')
|
||||
continue
|
||||
|
||||
_fire_arm(arm, plan, sess, dry_run)
|
||||
|
||||
|
||||
def _worth_running(force: bool) -> bool:
|
||||
"""예약이 있고 거래 세션인가. 루프 제어와 단발 실행이 같은 기준을 쓰게 분리."""
|
||||
if not trailing.list_active():
|
||||
"""볼 게 있고 거래 세션인가. 루프 제어와 단발 실행이 같은 기준을 쓰게 분리."""
|
||||
if not trailing.list_active() and not trailing.list_arms():
|
||||
return False
|
||||
return force or session_now()[0]
|
||||
|
||||
|
||||
def check(force: bool = False, dry_run: bool = False) -> int:
|
||||
reservations = trailing.list_active()
|
||||
if not reservations:
|
||||
arms = trailing.list_arms()
|
||||
if not reservations and not arms:
|
||||
return 0
|
||||
ok_sess, sess = session_now()
|
||||
if not force and not ok_sess:
|
||||
_log(f'{sess} — skip ({len(reservations)}건 예약 대기)')
|
||||
_log(f'{sess} — skip (예약 {len(reservations)}건 · 대기 예약 {len(arms)}건)')
|
||||
return 0
|
||||
# NXT 단독 시간대엔 KRX 호가가 15:30 종가로 고정돼 현재가와 갭이 생긴다 → NXT 시세로 조회.
|
||||
quote_exchange = 'NX' if sess in ('NXT_PRE', 'NXT_AFTER') else 'AL'
|
||||
|
||||
# 만료된 대기 예약은 시세보다 먼저 걷어낸다 — 시세 조회가 실패해도 정리는 돼야 하고,
|
||||
# 어제 예약이 오늘 시세로 발동하는 일은 절대 없어야 한다.
|
||||
if arms:
|
||||
arms = _expire_arms(arms, dry_run)
|
||||
if not reservations and not arms:
|
||||
return 0
|
||||
|
||||
# 계좌별 미체결 스냅샷 (계좌당 1콜). 스톱주문도 조건 도달 전까지 미체결로 잡힌다.
|
||||
open_by_acct: dict[str, list] = {}
|
||||
for acct in sorted({r['account'] for r in reservations}):
|
||||
for acct in sorted({r['account'] for r in reservations} | {a['account'] for a in arms}):
|
||||
try:
|
||||
open_by_acct[acct] = kc.get_open_orders(acct, side='sell')
|
||||
except Exception as e:
|
||||
@@ -246,11 +472,14 @@ def check(force: bool = False, dry_run: bool = False) -> int:
|
||||
except Exception as e:
|
||||
_log(f'알림 실패: {e!r}')
|
||||
|
||||
if not alive:
|
||||
# 시세는 예약·대기 예약 심볼을 합쳐 한 번에 받는다 — ka10095 배치 1콜이라 대기 예약이
|
||||
# 늘어도 API 콜이 추가되지 않는다. 실패하면 두 단계 모두 이번 사이클을 건너뛴다.
|
||||
quote_codes = sorted({r['symbol'] for r, _ in alive} | {a['symbol'] for a in arms})
|
||||
if not quote_codes:
|
||||
return 0
|
||||
|
||||
try:
|
||||
quotes = kc.get_watchlist_quotes([r['symbol'] for r, _ in alive], exchange=quote_exchange)
|
||||
quotes = kc.get_watchlist_quotes(quote_codes, exchange=quote_exchange)
|
||||
except Exception as e:
|
||||
_log(f'시세 조회 실패 ({quote_exchange}): {e!r}')
|
||||
return 1
|
||||
@@ -330,6 +559,15 @@ def check(force: bool = False, dry_run: bool = False) -> int:
|
||||
else:
|
||||
_log(f' 알림 생략 (쿨다운 {FAIL_NOTICE_COOLDOWN_SEC}초 내 이미 발송)')
|
||||
|
||||
# 대기 예약 발동은 손절선 상향이 **끝난 뒤에** 한다. 순서가 설계다 — 상향은 이미 나간
|
||||
# 주문을 지키는 동작이라 arm 쪽 어떤 예외도 그걸 선점하면 안 된다. 그래서 통째로 감싼다.
|
||||
# arm 이 0건이면 이 아래는 실행되지 않아 기존 동작과 동일하다.
|
||||
if arms:
|
||||
try:
|
||||
_check_arms(arms, quotes, open_by_acct, sess, dry_run)
|
||||
except Exception as e:
|
||||
_log(f'대기 예약 단계 예외 (트레일링 감시는 정상 완료): {e!r}')
|
||||
|
||||
if raised:
|
||||
_log(f'완료 — {raised}개 계단 상향 / 예약 {len(alive)}건')
|
||||
return 0
|
||||
@@ -337,9 +575,29 @@ def check(force: bool = False, dry_run: bool = False) -> int:
|
||||
|
||||
def cmd_list() -> int:
|
||||
reservations = trailing.list_active()
|
||||
if not reservations:
|
||||
arms = trailing.list_arms()
|
||||
if not reservations and not arms:
|
||||
print('트레일링 예약 없음')
|
||||
return 0
|
||||
if arms:
|
||||
print(f'── 대기 예약 (발동가 도달 시 시작) {len(arms)}건 ──')
|
||||
for a in arms:
|
||||
steps = a.get('steps') or []
|
||||
floor_part = f' · 최저 {a["min_sell_price"]:,}' if a.get('min_sell_price') else ''
|
||||
print(f'{a["id"]} · {a["symbol_name"]}({a["symbol"]}) · {a["account"]} · '
|
||||
f'{a["qty"]:,}주 · {len(steps)}단')
|
||||
print(f' 발동가 {a["trigger_price"]:,}{floor_part} · 등록 {a["created_at"]}')
|
||||
for s in steps:
|
||||
cum = s.get('cum', 0)
|
||||
cum_part = '전량' if cum >= 100 else f'누적 {_pct_s(cum)}%'
|
||||
print(f' {s["n"]}단계 −{_pct_s(s["pct"])}% · {cum_part} '
|
||||
f'(주수는 발동 시점 매도가능 기준으로 배분)')
|
||||
if a.get('firing_at'):
|
||||
legs = a.get('fired_legs') or []
|
||||
nos = ', '.join(s.get('ord_no', '') for s in legs if s.get('ord_no')) or '없음'
|
||||
print(f' ⚠️ 발동 처리 중단 (시작 {a["firing_at"]}) · 접수된 주문: {nos}')
|
||||
if reservations and arms:
|
||||
print(f'── 트레일링 예약 {len(reservations)}건 ──')
|
||||
for r in reservations:
|
||||
steps = r.get('steps') or []
|
||||
floor_part = f' · 최저 {r["min_sell_price"]:,}' if r.get('min_sell_price') else ''
|
||||
|
||||
@@ -128,3 +128,5 @@
|
||||
{"date": "2026-08-21", "market": "KOSPI", "market_label": "코스피", "rise": 193, "upper": 0, "fall": 683, "lower": 0, "steady": 28, "adr": 28.26, "personal": -11652, "foreign": -1760, "institutional": 2481, "captured_at": "2026-08-21T21:00:04.974633+09:00"}
|
||||
{"date": "2026-08-24", "market": "KOSDAQ", "market_label": "코스닥", "rise": 921, "upper": 5, "fall": 706, "lower": 2, "steady": 96, "adr": 130.79, "personal": -2604, "foreign": 2416, "institutional": 249, "captured_at": "2026-08-24T21:00:02.141569+09:00"}
|
||||
{"date": "2026-08-24", "market": "KOSPI", "market_label": "코스피", "rise": 579, "upper": 3, "fall": 286, "lower": 0, "steady": 38, "adr": 203.5, "personal": 33217, "foreign": -36691, "institutional": -12901, "captured_at": "2026-08-24T21:00:02.141569+09:00"}
|
||||
{"date": "2026-08-25", "market": "KOSDAQ", "market_label": "코스닥", "rise": 1186, "upper": 4, "fall": 466, "lower": 0, "steady": 74, "adr": 255.36, "personal": -1375, "foreign": 1327, "institutional": 156, "captured_at": "2026-08-25T21:00:03.869052+09:00"}
|
||||
{"date": "2026-08-25", "market": "KOSPI", "market_label": "코스피", "rise": 647, "upper": 2, "fall": 226, "lower": 1, "steady": 34, "adr": 285.9, "personal": 10509, "foreign": -38140, "institutional": 11711, "captured_at": "2026-08-25T21:00:03.869052+09:00"}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 11754838.588999985,
|
||||
"cash": 9330274.958999984,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"010950": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5572914.42378125,
|
||||
"last_add_price": 130800
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5640114.3750312505,
|
||||
"last_add_price": 353000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4803063.4110624995,
|
||||
"last_add_price": 13420
|
||||
@@ -76,12 +76,12 @@
|
||||
"entry_at": "2026-08-04T09:34:08.150378+09:00",
|
||||
"stop": 79324,
|
||||
"target": 124784,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4797930.468406249,
|
||||
"last_add_price": 93500
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,160 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3636137.140405993,
|
||||
"last_add_price": 14290
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4804824.179093749,
|
||||
"last_add_price": 198600
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 113300,
|
||||
"cur_price": 111500,
|
||||
"tranches": 1,
|
||||
"tranche_value": 5036191.884468749,
|
||||
"last_add_price": 118900
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"interest"
|
||||
],
|
||||
"qty": 73,
|
||||
"entry_price": 32950,
|
||||
"qty": 141,
|
||||
"entry_price": 34252.12765957447,
|
||||
"entry_at": "2026-08-24T13:14:17.289893+09:00",
|
||||
"stop": 22546,
|
||||
"target": 64161,
|
||||
"peak": 34700,
|
||||
"stop": 23791,
|
||||
"target": 65634,
|
||||
"peak": 35650,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,047 도달",
|
||||
"cur_price": 34500,
|
||||
"tranches": 1,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2430784.8850921993,
|
||||
"last_add_price": 32950
|
||||
"last_add_price": 35650
|
||||
}
|
||||
},
|
||||
"realized_pnl": -26795815.391500026,
|
||||
@@ -194,5 +194,5 @@
|
||||
"138040": "2026-08-14"
|
||||
},
|
||||
"created_at": "2026-06-11T09:00:01.943184+09:00",
|
||||
"updated_at": "2026-08-24T15:28:17.286851+09:00"
|
||||
"updated_at": "2026-08-25T15:28:19.202566+09:00"
|
||||
}
|
||||
@@ -79,3 +79,4 @@
|
||||
{"ts": "2026-08-14T13:34:15.676314+09:00", "side": "BUY", "code": "003550", "name": "LG", "price": 118900, "qty": 42, "cost": 4994549, "path": "breakout", "reason": "거래량·회전율 동반 전고점 돌파", "stop": 106512, "target": 156063, "signals": [{"label": "추세(10일선 위)", "ok": true, "detail": "118,800 vs 106,650"}, {"label": "정배열(5>10)", "ok": true, "detail": "110,480 / 106,650"}, {"label": "추세 기울기(10일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "110,480 / 107,793"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+14.2%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +36 · 기 +116 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+24%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.43% (환산) vs 평균 0.19% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": true, "detail": "고점 111,900"}, {"label": "거래량 급증", "ok": true, "detail": "662,849 (환산) vs 평균 280,124"}, {"label": "회전율 급증", "ok": true, "detail": "0.43% (환산) vs 평균 0.19%"}, {"label": "RSI", "ok": true, "detail": "69"}]}
|
||||
{"ts": "2026-08-24T13:12:17.476505+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 1140, "proceeds": 5723018, "entry_price": 6333, "pnl": -1497685, "pnl_pct": -20.57, "hold_from": "2026-07-30T09:04:05.277095+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 10,215"}, {"label": "추세(10일선)", "ok": false, "detail": "5,148"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:14:17.289907+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 32950, "qty": 73, "cost": 2405711, "path": "pullback", "reason": "눌림목 지정가 33,047 도달", "stop": 22546, "target": 64161, "signals": [{"label": "추세(10일선 위)", "ok": true, "detail": "33,000 vs 28,622"}, {"label": "정배열(5>10)", "ok": true, "detail": "35,100 / 28,622"}, {"label": "추세 기울기(10일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "35,100 / 31,286"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+24.6%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -62 · 기 +65 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+106%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "3.32% (환산) vs 평균 3.74% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 45,300"}, {"label": "거래량 급증", "ok": false, "detail": "298,285 (환산) vs 평균 409,191"}, {"label": "회전율 급증", "ok": false, "detail": "3.32% (환산) vs 평균 3.74%"}, {"label": "RSI", "ok": true, "detail": "56"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 33,047 (현재 33,000)"}]}
|
||||
{"ts": "2026-08-25T13:44:19.077912+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35650, "qty": 68, "cost": 2424564, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 22546, "target": 64161, "signals": [{"label": "손절선", "ok": true, "detail": "22,546 (현재 35,650)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 64,161"}, {"label": "추세(10일선)", "ok": true, "detail": "28,887"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -118 · 기 +90"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,565"}]}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3949543.523644232,
|
||||
"last_add_price": 136000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3504336.4241500003,
|
||||
"last_add_price": 13420
|
||||
@@ -53,14 +53,14 @@
|
||||
"qty": 77,
|
||||
"entry_price": 91929.87012987013,
|
||||
"entry_at": "2026-08-04T09:50:08.969520+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 108977,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3560762.7001641323,
|
||||
"last_add_price": 93500
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,170 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2094069.1444097043,
|
||||
"last_add_price": 13830
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5025448.547031702,
|
||||
"last_add_price": 196400
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3615738.9231707016,
|
||||
"last_add_price": 415500
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1442538.400216884,
|
||||
"last_add_price": 35800
|
||||
@@ -160,12 +160,12 @@
|
||||
"entry_at": "2026-08-24T13:14:20.866190+09:00",
|
||||
"stop": 17760,
|
||||
"target": 20860,
|
||||
"peak": 19110,
|
||||
"peak": 19250,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 19,655 도달",
|
||||
"cur_price": 19100,
|
||||
"cur_price": 19110,
|
||||
"tranches": 1,
|
||||
"tranche_value": 5442628.740343751,
|
||||
"last_add_price": 19000
|
||||
@@ -193,5 +193,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-11T09:00:01.944232+09:00",
|
||||
"updated_at": "2026-08-24T15:28:21.274422+09:00"
|
||||
"updated_at": "2026-08-25T15:28:22.679441+09:00"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 23605266.561000064,
|
||||
"cash": 18298970.736000065,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"138040": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 118,115 도달",
|
||||
"cur_price": 115000,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5549046.703468754,
|
||||
"last_add_price": 120500
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 122,665 도달",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5150762.675171776,
|
||||
"last_add_price": 125200
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3940896.531574532,
|
||||
"last_add_price": 136000
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3497500.5799993444,
|
||||
"last_add_price": 13420
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,947 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2091765.9324469643,
|
||||
"last_add_price": 13830
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5012093.7710951045,
|
||||
"last_add_price": 196400
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3599778.576622015,
|
||||
"last_add_price": 415500
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 167,
|
||||
"entry_price": 31900,
|
||||
"qty": 329,
|
||||
"entry_price": 32318.54103343465,
|
||||
"entry_at": "2026-08-24T13:12:20.462989+09:00",
|
||||
"stop": 29900,
|
||||
"target": 34900,
|
||||
"peak": 32050,
|
||||
"target": 35946,
|
||||
"peak": 32800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 31,939 도달",
|
||||
"cur_price": 31950,
|
||||
"tranches": 1,
|
||||
"cur_price": 32350,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5336831.603500004,
|
||||
"last_add_price": 31900
|
||||
"last_add_price": 32750
|
||||
}
|
||||
},
|
||||
"realized_pnl": -14520223.654500023,
|
||||
@@ -194,5 +194,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-11T09:00:01.944251+09:00",
|
||||
"updated_at": "2026-08-24T15:28:21.277074+09:00"
|
||||
"updated_at": "2026-08-25T15:28:22.681858+09:00"
|
||||
}
|
||||
@@ -98,3 +98,4 @@
|
||||
{"ts": "2026-08-13T14:02:16.527631+09:00", "side": "BUY", "code": "214450", "name": "파마리서치", "price": 415500, "qty": 8, "cost": 3324499, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 352720, "target": 477170, "signals": [{"label": "손절선", "ok": true, "detail": "352,720 (현재 415,500)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 477,170"}, {"label": "추세(10일선)", "ok": true, "detail": "312,900"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +43 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 415,052"}]}
|
||||
{"ts": "2026-08-24T13:12:20.462527+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 631, "proceeds": 3167741, "entry_price": 6333, "pnl": -828989, "pnl_pct": -20.57, "hold_from": "2026-07-30T09:04:06.863063+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 8,274"}, {"label": "추세(10일선)", "ok": false, "detail": "5,148"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:20.462991+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 31900, "qty": 167, "cost": 5328099, "path": "pullback", "reason": "눌림목 지정가 31,939 도달", "stop": 29900, "target": 34900, "signals": [{"label": "추세(10일선 위)", "ok": true, "detail": "31,850 vs 31,795"}, {"label": "정배열(5>10)", "ok": true, "detail": "32,390 / 31,795"}, {"label": "추세 기울기(10일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "32,390 / 30,876"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+3.6%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -892 · 기 +171 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+37%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.24% (환산) vs 평균 0.34% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 33,850"}, {"label": "거래량 급증", "ok": false, "detail": "1,768,231 (환산) vs 평균 2,489,243"}, {"label": "회전율 급증", "ok": false, "detail": "0.24% (환산) vs 평균 0.34%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 31,939 (현재 31,850)"}]}
|
||||
{"ts": "2026-08-25T11:36:17.992170+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 32750, "qty": 162, "cost": 5306296, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 29900, "target": 34900, "signals": [{"label": "손절선", "ok": true, "detail": "29,900 (현재 32,750)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 34,900"}, {"label": "추세(10일선)", "ok": true, "detail": "31,885"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -1,322 · 기 +436"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 32,722"}]}
|
||||
|
||||
@@ -1,28 +1,7 @@
|
||||
{
|
||||
"cash": 38865967.97700004,
|
||||
"cash": 28442772.680500045,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"252990": {
|
||||
"code": "252990",
|
||||
"name": "샘씨엔에스",
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 333,
|
||||
"entry_price": 13905.405405405405,
|
||||
"entry_at": "2026-08-05T09:18:09.783202+09:00",
|
||||
"stop": 11576,
|
||||
"target": 18564,
|
||||
"peak": 16270,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,947 도달",
|
||||
"cur_price": 13310,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2325831.4188620616,
|
||||
"last_add_price": 14270
|
||||
},
|
||||
"068270": {
|
||||
"code": "068270",
|
||||
"name": "셀트리온",
|
||||
@@ -39,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4973576.381781252,
|
||||
"last_add_price": 196400
|
||||
@@ -60,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4403970.424534555,
|
||||
"last_add_price": 416500
|
||||
@@ -81,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1699505.011186723,
|
||||
"last_add_price": 35800
|
||||
@@ -102,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 157,486 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1942551.0603990993,
|
||||
"last_add_price": 144900
|
||||
@@ -123,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 7,771 도달",
|
||||
"cur_price": 5060,
|
||||
"cur_price": 5040,
|
||||
"tranches": 1,
|
||||
"tranche_value": 2088749.9404521286,
|
||||
"last_add_price": 5500
|
||||
@@ -134,20 +113,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 42,
|
||||
"entry_price": 115000,
|
||||
"qty": 83,
|
||||
"entry_price": 116580.72289156627,
|
||||
"entry_at": "2026-08-20T09:02:17.452076+09:00",
|
||||
"stop": 111200,
|
||||
"target": 122600,
|
||||
"peak": 117500,
|
||||
"target": 127342,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 117,922 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4911248.4428750025,
|
||||
"last_add_price": 115000
|
||||
"last_add_price": 118200
|
||||
},
|
||||
"316140": {
|
||||
"code": "316140",
|
||||
@@ -155,24 +134,45 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 153,
|
||||
"entry_price": 31550,
|
||||
"qty": 302,
|
||||
"entry_price": 31969.370860927153,
|
||||
"entry_at": "2026-08-24T09:02:22.276742+09:00",
|
||||
"stop": 29900,
|
||||
"target": 34850,
|
||||
"peak": 32050,
|
||||
"target": 36108,
|
||||
"peak": 32800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 31,946 도달",
|
||||
"cur_price": 31950,
|
||||
"tranches": 1,
|
||||
"cur_price": 32350,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4850367.628093753,
|
||||
"last_add_price": 31550
|
||||
"last_add_price": 32400
|
||||
},
|
||||
"030000": {
|
||||
"code": "030000",
|
||||
"name": "제일기획",
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 253,
|
||||
"entry_price": 19190,
|
||||
"entry_at": "2026-08-25T12:16:22.494706+09:00",
|
||||
"stop": 18112,
|
||||
"target": 21346,
|
||||
"peak": 19250,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 20,031 도달",
|
||||
"cur_price": 19110,
|
||||
"tranches": 1,
|
||||
"tranche_value": 4858670.683812503,
|
||||
"last_add_price": 19190
|
||||
}
|
||||
},
|
||||
"realized_pnl": -21614655.005500037,
|
||||
"closed_trades": 50,
|
||||
"realized_pnl": -22137995.54650004,
|
||||
"closed_trades": 51,
|
||||
"wins": 4,
|
||||
"peak_equity": 101695667.93550003,
|
||||
"max_drawdown": 0.24548980547857824,
|
||||
@@ -191,7 +191,7 @@
|
||||
"105560": "2026-07-20",
|
||||
"086790": "2026-07-21",
|
||||
"003680": "2026-08-19",
|
||||
"252990": "2026-07-28",
|
||||
"252990": "2026-08-25",
|
||||
"010950": "2026-08-03",
|
||||
"090850": "2026-08-05",
|
||||
"161890": "2026-07-30",
|
||||
@@ -201,5 +201,5 @@
|
||||
"194370": "2026-08-24"
|
||||
},
|
||||
"created_at": "2026-06-11T09:00:01.944270+09:00",
|
||||
"updated_at": "2026-08-24T15:28:21.279713+09:00"
|
||||
"updated_at": "2026-08-25T15:28:22.684262+09:00"
|
||||
}
|
||||
@@ -140,3 +140,7 @@
|
||||
{"ts": "2026-08-20T09:16:21.642669+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 47, "cost": 1682852, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 25688, "target": 48823, "signals": [{"label": "손절선", "ok": true, "detail": "25,688 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 48,823"}, {"label": "추세(20일선)", "ok": true, "detail": "26,208"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T09:00:21.444249+09:00", "side": "SELL", "code": "194370", "name": "제이에스코퍼레이션", "price": 12790, "qty": 653, "proceeds": 8335584, "entry_price": 13160, "pnl": -259135, "pnl_pct": -2.81, "hold_from": "2026-08-03T12:00:11.776417+09:00", "reason": "시간손절 — 20일 보유·진전 없음(자본 회전)", "partial": false, "signals": [{"label": "손절선", "ok": true, "detail": "11,874 (현재 12,750)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 15,732"}, {"label": "추세(20일선)", "ok": true, "detail": "11,913"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -59 · 기 -20"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T09:02:22.276759+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 31550, "qty": 153, "cost": 4827874, "path": "pullback", "reason": "눌림목 지정가 31,946 도달", "stop": 29900, "target": 34850, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "31,500 vs 31,388"}, {"label": "정배열(5>20)", "ok": true, "detail": "32,320 / 31,388"}, {"label": "추세 기울기(20일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "32,320 / 30,870"}, {"label": "상대강도(시장대비)", "ok": false, "detail": "-0.2%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -988 · 기 +144 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+39%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.08% (환산) vs 평균 0.34% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 33,850"}, {"label": "거래량 급증", "ok": false, "detail": "551,940 (환산) vs 평균 2,489,243"}, {"label": "회전율 급증", "ok": false, "detail": "0.08% (환산) vs 평균 0.34%"}, {"label": "RSI", "ok": true, "detail": "51"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 31,946 (현재 31,500)"}]}
|
||||
{"ts": "2026-08-25T09:20:20.388186+09:00", "side": "SELL", "code": "252990", "name": "샘씨엔에스", "price": 12360, "qty": 333, "proceeds": 4107854, "entry_price": 13905, "pnl": -523341, "pnl_pct": -11.11, "hold_from": "2026-08-05T09:18:09.783202+09:00", "reason": "시간손절 — 20일 보유·진전 없음(자본 회전)", "partial": false, "signals": [{"label": "손절선", "ok": true, "detail": "11,576 (현재 12,370)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 18,564"}, {"label": "추세(20일선)", "ok": false, "detail": "12,694"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +34 · 기 -30"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-25T09:34:17.879110+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 41, "cost": 4846927, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 122600, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 122,600"}, {"label": "추세(20일선)", "ok": true, "detail": "113,465"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 117,689"}]}
|
||||
{"ts": "2026-08-25T09:46:20.069247+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 32400, "qty": 149, "cost": 4828324, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 29900, "target": 34850, "signals": [{"label": "손절선", "ok": true, "detail": "29,900 (현재 32,400)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 34,850"}, {"label": "추세(20일선)", "ok": true, "detail": "31,432"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -1,322 · 기 +436"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 32,372"}]}
|
||||
{"ts": "2026-08-25T12:16:22.494708+09:00", "side": "BUY", "code": "030000", "name": "제일기획", "price": 19190, "qty": 253, "cost": 4855798, "path": "pullback", "reason": "눌림목 지정가 20,031 도달", "stop": 18112, "target": 21346, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "19,180 vs 18,864"}, {"label": "정배열(5>20)", "ok": true, "detail": "19,622 / 18,864"}, {"label": "추세 기울기(20일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "19,622 / 19,362"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+0.8%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +274 · 기 -97 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+31%"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.21% (환산) vs 평균 0.62% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 21,150"}, {"label": "거래량 급증", "ok": false, "detail": "241,100 (환산) vs 평균 711,709"}, {"label": "회전율 급증", "ok": false, "detail": "0.21% (환산) vs 평균 0.62%"}, {"label": "RSI", "ok": true, "detail": "51"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 20,031 (현재 19,180)"}]}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 118,115 도달",
|
||||
"cur_price": 115000,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5368293.320125001,
|
||||
"last_add_price": 120500
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 122,665 도달",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5180051.180047499,
|
||||
"last_add_price": 125200
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4464385.527789056,
|
||||
"last_add_price": 13420
|
||||
@@ -74,14 +74,14 @@
|
||||
"qty": 98,
|
||||
"entry_price": 91918.36734693877,
|
||||
"entry_at": "2026-08-04T09:50:09.564914+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 108966,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4532723.825333739,
|
||||
"last_add_price": 93500
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,975 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2418915.1277636583,
|
||||
"last_add_price": 14290
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5253404.499,
|
||||
"last_add_price": 196400
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4617746.513836046,
|
||||
"last_add_price": 415500
|
||||
@@ -160,12 +160,12 @@
|
||||
"entry_at": "2026-08-20T10:34:23.489978+09:00",
|
||||
"stop": 27388,
|
||||
"target": 50523,
|
||||
"peak": 35600,
|
||||
"peak": 35650,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1917039.5768596022,
|
||||
"last_add_price": 35100
|
||||
@@ -194,5 +194,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-11T09:00:01.944290+09:00",
|
||||
"updated_at": "2026-08-24T15:28:21.282132+09:00"
|
||||
"updated_at": "2026-08-25T15:28:22.686564+09:00"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 7247132.073499956,
|
||||
"cash": 2890728.7109999554,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5184250.1290937485,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 118,115 도달",
|
||||
"cur_price": 115000,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4675255.455593748,
|
||||
"last_add_price": 120500
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 122,665 도달",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4550435.925312498,
|
||||
"last_add_price": 125200
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4604300.898906248,
|
||||
"last_add_price": 136000
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4446388.7381874975,
|
||||
"last_add_price": 13420
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,947 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3449195.5386017757,
|
||||
"last_add_price": 13830
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2315542.8746463708,
|
||||
"last_add_price": 35800
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 136,
|
||||
"entry_price": 31900,
|
||||
"qty": 269,
|
||||
"entry_price": 32320.260223048328,
|
||||
"entry_at": "2026-08-24T13:12:20.472222+09:00",
|
||||
"stop": 29900,
|
||||
"target": 37900,
|
||||
"peak": 32050,
|
||||
"target": 39581,
|
||||
"peak": 32800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 31,939 도달",
|
||||
"cur_price": 31950,
|
||||
"tranches": 1,
|
||||
"cur_price": 32350,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4361206.427093747,
|
||||
"last_add_price": 31900
|
||||
"last_add_price": 32750
|
||||
}
|
||||
},
|
||||
"realized_pnl": -31731326.068000022,
|
||||
@@ -199,5 +199,5 @@
|
||||
"161890": "2026-07-30"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.582771+09:00",
|
||||
"updated_at": "2026-08-24T15:28:21.284596+09:00"
|
||||
"updated_at": "2026-08-25T15:28:22.688853+09:00"
|
||||
}
|
||||
@@ -109,3 +109,4 @@
|
||||
{"ts": "2026-08-20T09:16:21.647472+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 64, "cost": 2291544, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 64246, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 64,246"}, {"label": "추세(20일선)", "ok": true, "detail": "26,208"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T13:12:20.471895+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 1055, "proceeds": 5296302, "entry_price": 6333, "pnl": -1386050, "pnl_pct": -20.58, "hold_from": "2026-07-30T09:04:07.466771+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 10,215"}, {"label": "추세(20일선)", "ok": true, "detail": "4,754"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:20.472224+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 31900, "qty": 136, "cost": 4339051, "path": "pullback", "reason": "눌림목 지정가 31,939 도달", "stop": 29900, "target": 37900, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "31,850 vs 31,405"}, {"label": "정배열(5>20)", "ok": true, "detail": "32,390 / 31,405"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "32,390 / 30,876"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+3.6%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -892 · 기 +171 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+37%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.24% (환산) vs 평균 0.34% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 33,850"}, {"label": "거래량 급증", "ok": false, "detail": "1,768,231 (환산) vs 평균 2,489,243"}, {"label": "회전율 급증", "ok": false, "detail": "0.24% (환산) vs 평균 0.34%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 31,939 (현재 31,850)"}]}
|
||||
{"ts": "2026-08-25T11:36:18.000274+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 32750, "qty": 133, "cost": 4356403, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 29900, "target": 37900, "signals": [{"label": "손절선", "ok": true, "detail": "29,900 (현재 32,750)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 37,900"}, {"label": "추세(20일선)", "ok": true, "detail": "31,450"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -1,322 · 기 +436"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 32,722"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 9899146.234499946,
|
||||
"cash": 5116929.009499946,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"138040": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 118,115 도달",
|
||||
"cur_price": 115000,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5057738.410843747,
|
||||
"last_add_price": 120500
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 122,665 도달",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5030428.7588749975,
|
||||
"last_add_price": 125200
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5098537.405531247,
|
||||
"last_add_price": 136000
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4920739.488124997,
|
||||
"last_add_price": 13420
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,947 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3814821.8249811833,
|
||||
"last_add_price": 13830
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5119873.139312496,
|
||||
"last_add_price": 417500
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2545799.7712668725,
|
||||
"last_add_price": 35800
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 149,
|
||||
"entry_price": 31900,
|
||||
"qty": 295,
|
||||
"entry_price": 32320.677966101695,
|
||||
"entry_at": "2026-08-24T13:12:20.475258+09:00",
|
||||
"stop": 29900,
|
||||
"target": 34900,
|
||||
"peak": 32050,
|
||||
"target": 35952,
|
||||
"peak": 32800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 31,939 도달",
|
||||
"cur_price": 31950,
|
||||
"tranches": 1,
|
||||
"cur_price": 32350,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4784721.199968747,
|
||||
"last_add_price": 31900
|
||||
"last_add_price": 32750
|
||||
}
|
||||
},
|
||||
"realized_pnl": -23072340.99650003,
|
||||
@@ -199,5 +199,5 @@
|
||||
"161890": "2026-07-30"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.582787+09:00",
|
||||
"updated_at": "2026-08-24T15:28:21.286982+09:00"
|
||||
"updated_at": "2026-08-25T15:28:22.691063+09:00"
|
||||
}
|
||||
@@ -121,3 +121,4 @@
|
||||
{"ts": "2026-08-20T09:16:21.649864+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 71, "cost": 2542181, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 48823, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 48,823"}, {"label": "추세(20일선)", "ok": true, "detail": "26,208"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T13:12:20.475051+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 1167, "proceeds": 5858563, "entry_price": 6333, "pnl": -1533135, "pnl_pct": -20.57, "hold_from": "2026-07-30T09:04:07.473311+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 8,274"}, {"label": "추세(20일선)", "ok": true, "detail": "4,754"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:20.475260+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 31900, "qty": 149, "cost": 4753813, "path": "pullback", "reason": "눌림목 지정가 31,939 도달", "stop": 29900, "target": 34900, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "31,850 vs 31,405"}, {"label": "정배열(5>20)", "ok": true, "detail": "32,390 / 31,405"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "32,390 / 30,876"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+3.6%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -892 · 기 +171 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+37%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.24% (환산) vs 평균 0.34% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 33,850"}, {"label": "거래량 급증", "ok": false, "detail": "1,768,231 (환산) vs 평균 2,489,243"}, {"label": "회전율 급증", "ok": false, "detail": "0.24% (환산) vs 평균 0.34%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 31,939 (현재 31,850)"}]}
|
||||
{"ts": "2026-08-25T11:36:18.002977+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 32750, "qty": 146, "cost": 4782217, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 29900, "target": 34900, "signals": [{"label": "손절선", "ok": true, "detail": "29,900 (현재 32,750)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 34,900"}, {"label": "추세(20일선)", "ok": true, "detail": "31,450"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -1,322 · 기 +436"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 32,722"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 9167441.589999957,
|
||||
"cash": 4712773.489999956,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"138040": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 118,115 도달",
|
||||
"cur_price": 115000,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4798325.220406247,
|
||||
"last_add_price": 120500
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 122,665 도달",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4697478.874937497,
|
||||
"last_add_price": 125200
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4762406.753156248,
|
||||
"last_add_price": 136000
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4594021.1274374975,
|
||||
"last_add_price": 13420
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,947 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3561242.7967011826,
|
||||
"last_add_price": 13830
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4781236.946562497,
|
||||
"last_add_price": 417500
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2377721.795719703,
|
||||
"last_add_price": 35800
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 140,
|
||||
"entry_price": 31900,
|
||||
"qty": 276,
|
||||
"entry_price": 32318.840579710144,
|
||||
"entry_at": "2026-08-24T13:12:20.478387+09:00",
|
||||
"stop": 29900,
|
||||
"target": 35900,
|
||||
"peak": 32050,
|
||||
"target": 37157,
|
||||
"peak": 32800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 31,939 도달",
|
||||
"cur_price": 31950,
|
||||
"tranches": 1,
|
||||
"cur_price": 32350,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4467750.718124997,
|
||||
"last_add_price": 31900
|
||||
"last_add_price": 32750
|
||||
}
|
||||
},
|
||||
"realized_pnl": -28164339.587000035,
|
||||
@@ -199,5 +199,5 @@
|
||||
"161890": "2026-07-30"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.582803+09:00",
|
||||
"updated_at": "2026-08-24T15:28:21.289314+09:00"
|
||||
"updated_at": "2026-08-25T15:28:22.693234+09:00"
|
||||
}
|
||||
@@ -115,3 +115,4 @@
|
||||
{"ts": "2026-08-20T09:16:21.652201+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 66, "cost": 2363154, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 53964, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 53,964"}, {"label": "추세(20일선)", "ok": true, "detail": "26,208"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T13:12:20.478005+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 1090, "proceeds": 5472009, "entry_price": 6333, "pnl": -1432067, "pnl_pct": -20.58, "hold_from": "2026-07-30T09:04:07.479053+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 8,921"}, {"label": "추세(20일선)", "ok": true, "detail": "4,754"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:20.478389+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 31900, "qty": 140, "cost": 4466670, "path": "pullback", "reason": "눌림목 지정가 31,939 도달", "stop": 29900, "target": 35900, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "31,850 vs 31,405"}, {"label": "정배열(5>20)", "ok": true, "detail": "32,390 / 31,405"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "32,390 / 30,876"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+3.6%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -892 · 기 +171 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+37%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.24% (환산) vs 평균 0.34% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 33,850"}, {"label": "거래량 급증", "ok": false, "detail": "1,768,231 (환산) vs 평균 2,489,243"}, {"label": "회전율 급증", "ok": false, "detail": "0.24% (환산) vs 평균 0.34%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 31,939 (현재 31,850)"}]}
|
||||
{"ts": "2026-08-25T11:36:18.005533+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 32750, "qty": 136, "cost": 4454668, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 29900, "target": 35900, "signals": [{"label": "손절선", "ok": true, "detail": "29,900 (현재 32,750)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 35,900"}, {"label": "추세(20일선)", "ok": true, "detail": "31,450"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -1,322 · 기 +436"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 32,722"}]}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 118,115 도달",
|
||||
"cur_price": 115000,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5120146.001656252,
|
||||
"last_add_price": 120500
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 122,665 도달",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4929642.288776176,
|
||||
"last_add_price": 125200
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4296364.655919675,
|
||||
"last_add_price": 13420
|
||||
@@ -74,14 +74,14 @@
|
||||
"qty": 94,
|
||||
"entry_price": 91917.02127659574,
|
||||
"entry_at": "2026-08-04T09:48:09.782722+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 108965,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4361392.052247569,
|
||||
"last_add_price": 93500
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,975 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2326313.326987872,
|
||||
"last_add_price": 14290
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4443615.9921114575,
|
||||
"last_add_price": 415500
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1744609.622982949,
|
||||
"last_add_price": 35800
|
||||
@@ -165,7 +165,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 157,407 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1989854.351411668,
|
||||
"last_add_price": 145000
|
||||
@@ -200,5 +200,5 @@
|
||||
"161890": "2026-07-30"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.582834+09:00",
|
||||
"updated_at": "2026-08-24T15:28:21.291595+09:00"
|
||||
"updated_at": "2026-08-25T15:28:22.695362+09:00"
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4489026.732352336,
|
||||
"last_add_price": 13420
|
||||
@@ -32,14 +32,14 @@
|
||||
"qty": 98,
|
||||
"entry_price": 91714.28571428571,
|
||||
"entry_at": "2026-08-04T09:34:09.331555+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 104500,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4576294.319379674,
|
||||
"last_add_price": 93500
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,033 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2154436.3245398356,
|
||||
"last_add_price": 13640
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5207451.256718745,
|
||||
"last_add_price": 198600
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4536055.463688828,
|
||||
"last_add_price": 417500
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1773404.3510667775,
|
||||
"last_add_price": 35800
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 147,729 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 2027018.6354019148,
|
||||
"last_add_price": 144900
|
||||
@@ -160,12 +160,12 @@
|
||||
"entry_at": "2026-08-20T10:00:18.528052+09:00",
|
||||
"stop": 45302,
|
||||
"target": 55335,
|
||||
"peak": 50700,
|
||||
"peak": 50900,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 50600,
|
||||
"cur_price": 50400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4928101.458627134,
|
||||
"last_add_price": 50000
|
||||
@@ -200,5 +200,5 @@
|
||||
"003550": "2026-08-19"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.582849+09:00",
|
||||
"updated_at": "2026-08-24T15:28:21.293850+09:00"
|
||||
"updated_at": "2026-08-25T15:28:22.697446+09:00"
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4992940.599795671,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4188989.7646745183,
|
||||
"last_add_price": 13420
|
||||
@@ -53,14 +53,14 @@
|
||||
"qty": 92,
|
||||
"entry_price": 91711.95652173914,
|
||||
"entry_at": "2026-08-04T09:34:09.335258+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 117283,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4268719.0892993,
|
||||
"last_add_price": 93500
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,033 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2008681.3553691048,
|
||||
"last_add_price": 13640
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4855136.307499998,
|
||||
"last_add_price": 198600
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1684859.3510976394,
|
||||
"last_add_price": 35800
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 147,729 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1925810.8724292808,
|
||||
"last_add_price": 144900
|
||||
@@ -160,12 +160,12 @@
|
||||
"entry_at": "2026-08-20T10:00:18.530498+09:00",
|
||||
"stop": 45301,
|
||||
"target": 61354,
|
||||
"peak": 50700,
|
||||
"peak": 50900,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 50600,
|
||||
"cur_price": 50400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4686399.784966664,
|
||||
"last_add_price": 50000
|
||||
@@ -200,5 +200,5 @@
|
||||
"003550": "2026-08-19"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.582864+09:00",
|
||||
"updated_at": "2026-08-24T15:28:21.296058+09:00"
|
||||
"updated_at": "2026-08-25T15:28:22.699497+09:00"
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3813943.0504600187,
|
||||
"last_add_price": 350000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3759463.8630866953,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3342042.065213833,
|
||||
"last_add_price": 13420
|
||||
@@ -76,12 +76,12 @@
|
||||
"entry_at": "2026-08-04T09:34:09.921371+09:00",
|
||||
"stop": 80361,
|
||||
"target": 125821,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3414906.320549933,
|
||||
"last_add_price": 93500
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,170 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1996058.5440871862,
|
||||
"last_add_price": 13830
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4827704.554226948,
|
||||
"last_add_price": 198600
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1375604.0354715528,
|
||||
"last_add_price": 35800
|
||||
@@ -160,12 +160,12 @@
|
||||
"entry_at": "2026-08-24T13:14:21.476706+09:00",
|
||||
"stop": 17760,
|
||||
"target": 22720,
|
||||
"peak": 19110,
|
||||
"peak": 19250,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 19,655 도달",
|
||||
"cur_price": 19100,
|
||||
"cur_price": 19110,
|
||||
"tranches": 1,
|
||||
"tranche_value": 5197409.522968752,
|
||||
"last_add_price": 19000
|
||||
@@ -196,5 +196,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.582879+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.012141+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.455035+09:00"
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3759463.8630866953,
|
||||
"last_add_price": 136000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3342042.065213833,
|
||||
"last_add_price": 13420
|
||||
@@ -53,14 +53,14 @@
|
||||
"qty": 73,
|
||||
"entry_price": 91726.02739726027,
|
||||
"entry_at": "2026-08-04T09:34:09.933331+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 114456,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3414906.320549933,
|
||||
"last_add_price": 93500
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,170 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1996058.5440871862,
|
||||
"last_add_price": 13830
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4827704.554226948,
|
||||
"last_add_price": 198600
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3439103.463858052,
|
||||
"last_add_price": 417500
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1371827.8681296934,
|
||||
"last_add_price": 35800
|
||||
@@ -160,12 +160,12 @@
|
||||
"entry_at": "2026-08-24T13:14:22.528454+09:00",
|
||||
"stop": 17760,
|
||||
"target": 21480,
|
||||
"peak": 19110,
|
||||
"peak": 19250,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 19,655 도달",
|
||||
"cur_price": 19100,
|
||||
"cur_price": 19110,
|
||||
"tranches": 1,
|
||||
"tranche_value": 5176941.127531253,
|
||||
"last_add_price": 19000
|
||||
@@ -196,5 +196,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.582910+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.024671+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.465147+09:00"
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3812991.5556229833,
|
||||
"last_add_price": 136000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3393650.506937234,
|
||||
"last_add_price": 13420
|
||||
@@ -53,14 +53,14 @@
|
||||
"qty": 75,
|
||||
"entry_price": 91726.66666666667,
|
||||
"entry_at": "2026-08-04T09:34:09.942366+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 108774,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3469610.992924529,
|
||||
"last_add_price": 93500
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,170 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2028402.8880774078,
|
||||
"last_add_price": 13830
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4907695.663916428,
|
||||
"last_add_price": 198600
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3458120.1956040077,
|
||||
"last_add_price": 417500
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1380001.3303458472,
|
||||
"last_add_price": 35800
|
||||
@@ -160,12 +160,12 @@
|
||||
"entry_at": "2026-08-24T13:14:22.536474+09:00",
|
||||
"stop": 17760,
|
||||
"target": 20860,
|
||||
"peak": 19110,
|
||||
"peak": 19250,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 19,655 도달",
|
||||
"cur_price": 19100,
|
||||
"cur_price": 19110,
|
||||
"tranches": 1,
|
||||
"tranche_value": 5205751.283343751,
|
||||
"last_add_price": 19000
|
||||
@@ -175,7 +175,7 @@
|
||||
"closed_trades": 32,
|
||||
"wins": 2,
|
||||
"peak_equity": 100000000,
|
||||
"max_drawdown": 0.17355132621999994,
|
||||
"max_drawdown": 0.17443687516499995,
|
||||
"last_exit": {
|
||||
"030000": "2026-08-13",
|
||||
"086790": "2026-07-29",
|
||||
@@ -196,5 +196,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.582926+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.032139+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.474135+09:00"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 24200387.246999994,
|
||||
"cash": 19226841.326999992,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5085796.90859035,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4271897.688361821,
|
||||
"last_add_price": 13420
|
||||
@@ -53,14 +53,14 @@
|
||||
"qty": 93,
|
||||
"entry_price": 91933.33333333333,
|
||||
"entry_at": "2026-08-04T09:50:10.213035+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 117505,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4327959.889890211,
|
||||
"last_add_price": 93500
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,170 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2219137.008862232,
|
||||
"last_add_price": 13830
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4934881.2159687495,
|
||||
"last_add_price": 196400
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1725875.4362712654,
|
||||
"last_add_price": 35800
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 147,729 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1972692.6896559393,
|
||||
"last_add_price": 144900
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 264,
|
||||
"entry_price": 18840,
|
||||
"qty": 523,
|
||||
"entry_price": 19018.27915869981,
|
||||
"entry_at": "2026-08-20T10:06:19.265748+09:00",
|
||||
"stop": 17760,
|
||||
"target": 22080,
|
||||
"peak": 19110,
|
||||
"stop": 17940,
|
||||
"target": 22252,
|
||||
"peak": 19250,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 19,640 도달",
|
||||
"cur_price": 19100,
|
||||
"tranches": 1,
|
||||
"cur_price": 19110,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4987614.581937499,
|
||||
"last_add_price": 18840
|
||||
"last_add_price": 19200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -24488397.225000024,
|
||||
@@ -198,5 +198,5 @@
|
||||
"003550": "2026-08-19"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583136+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.039732+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.480761+09:00"
|
||||
}
|
||||
@@ -121,3 +121,4 @@
|
||||
{"ts": "2026-08-20T09:16:22.841712+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 48, "cost": 1718658, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 25688, "target": 56535, "signals": [{"label": "손절선", "ok": true, "detail": "25,688 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 56,535"}, {"label": "추세(20일선)", "ok": true, "detail": "26,208"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-20T10:00:19.684004+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5360, "qty": 793, "proceeds": 4242192, "entry_price": 6333, "pnl": -780512, "pnl_pct": -15.36, "hold_from": "2026-07-30T09:04:07.509224+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,362 (현재 5,360)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 9,244"}, {"label": "추세(20일선)", "ok": true, "detail": "4,770"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +53 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-20T10:06:19.265752+09:00", "side": "BUY", "code": "030000", "name": "제일기획", "price": 18840, "qty": 264, "cost": 4974506, "path": "pullback", "reason": "눌림목 지정가 19,640 도달", "stop": 17760, "target": 22080, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "18,860 vs 18,848"}, {"label": "정배열(5>20)", "ok": true, "detail": "19,558 / 18,848"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "19,558 / 19,356"}, {"label": "상대강도(시장대비)", "ok": false, "detail": "-3.8%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +95 · 기 -424 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+33%"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.42% (환산) vs 평균 0.62% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 21,150"}, {"label": "거래량 급증", "ok": false, "detail": "481,892 (환산) vs 평균 711,709"}, {"label": "회전율 급증", "ok": false, "detail": "0.42% (환산) vs 평균 0.62%"}, {"label": "RSI", "ok": true, "detail": "48"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 19,640 (현재 18,860)"}]}
|
||||
{"ts": "2026-08-25T12:06:22.970717+09:00", "side": "BUY", "code": "030000", "name": "제일기획", "price": 19200, "qty": 259, "cost": 4973546, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 17760, "target": 22080, "signals": [{"label": "손절선", "ok": true, "detail": "17,760 (현재 19,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 22,080"}, {"label": "추세(20일선)", "ok": true, "detail": "18,864"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +274 · 기 -97"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 19,199"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 24062611.094000004,
|
||||
"cash": 18952844.744000003,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3889733.683715179,
|
||||
"last_add_price": 353000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 118,115 도달",
|
||||
"cur_price": 115000,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5327150.576000001,
|
||||
"last_add_price": 120500
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 122,665 도달",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4940372.790080874,
|
||||
"last_add_price": 125200
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3791237.2526261737,
|
||||
"last_add_price": 136000
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,947 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2020520.412274148,
|
||||
"last_add_price": 13830
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4842897.369596543,
|
||||
"last_add_price": 196400
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1352658.7410761528,
|
||||
"last_add_price": 35800
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 160,
|
||||
"entry_price": 31900,
|
||||
"qty": 316,
|
||||
"entry_price": 32319.620253164558,
|
||||
"entry_at": "2026-08-24T13:12:22.273409+09:00",
|
||||
"stop": 29900,
|
||||
"target": 37900,
|
||||
"peak": 32050,
|
||||
"target": 39578,
|
||||
"peak": 32800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 31,939 도달",
|
||||
"cur_price": 31950,
|
||||
"tranches": 1,
|
||||
"cur_price": 32350,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5133126.668375,
|
||||
"last_add_price": 31900
|
||||
"last_add_price": 32750
|
||||
}
|
||||
},
|
||||
"realized_pnl": -19279151.44500002,
|
||||
@@ -197,5 +197,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583184+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.045678+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.487187+09:00"
|
||||
}
|
||||
@@ -98,3 +98,4 @@
|
||||
{"ts": "2026-08-20T09:16:22.848001+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 37, "cost": 1324799, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 64246, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 64,246"}, {"label": "추세(10일선)", "ok": true, "detail": "28,922"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T13:12:22.272858+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 606, "proceeds": 3042236, "entry_price": 6333, "pnl": -796180, "pnl_pct": -20.58, "hold_from": "2026-07-30T09:04:07.512664+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 10,215"}, {"label": "추세(10일선)", "ok": false, "detail": "5,148"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:22.273412+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 31900, "qty": 160, "cost": 5104766, "path": "pullback", "reason": "눌림목 지정가 31,939 도달", "stop": 29900, "target": 37900, "signals": [{"label": "추세(10일선 위)", "ok": true, "detail": "31,850 vs 31,795"}, {"label": "정배열(5>10)", "ok": true, "detail": "32,390 / 31,795"}, {"label": "추세 기울기(10일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "32,390 / 30,876"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+3.6%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -892 · 기 +171 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+37%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.24% (환산) vs 평균 0.34% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 33,850"}, {"label": "거래량 급증", "ok": false, "detail": "1,768,231 (환산) vs 평균 2,489,243"}, {"label": "회전율 급증", "ok": false, "detail": "0.24% (환산) vs 평균 0.34%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 31,939 (현재 31,850)"}]}
|
||||
{"ts": "2026-08-25T11:36:19.771828+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 32750, "qty": 156, "cost": 5109766, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 29900, "target": 37900, "signals": [{"label": "손절선", "ok": true, "detail": "29,900 (현재 32,750)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 37,900"}, {"label": "추세(10일선)", "ok": true, "detail": "31,885"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -1,322 · 기 +436"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 32,722"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 29771205.096499994,
|
||||
"cash": 24595928.921499994,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"138040": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 118,115 도달",
|
||||
"cur_price": 115000,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5467080.19828125,
|
||||
"last_add_price": 120500
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 122,665 도달",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5075960.404438956,
|
||||
"last_add_price": 125200
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3902843.990735232,
|
||||
"last_add_price": 136000
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3433240.983007488,
|
||||
"last_add_price": 13420
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,947 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2017488.6720390161,
|
||||
"last_add_price": 13940
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3513980.3038730407,
|
||||
"last_add_price": 415500
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1369448.2358109318,
|
||||
"last_add_price": 35800
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 163,
|
||||
"entry_price": 31900,
|
||||
"qty": 321,
|
||||
"entry_price": 32318.380062305296,
|
||||
"entry_at": "2026-08-24T13:12:22.279165+09:00",
|
||||
"stop": 29900,
|
||||
"target": 35900,
|
||||
"peak": 32050,
|
||||
"target": 37155,
|
||||
"peak": 32800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 31,939 도달",
|
||||
"cur_price": 31950,
|
||||
"tranches": 1,
|
||||
"cur_price": 32350,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5207054.06571875,
|
||||
"last_add_price": 31900
|
||||
"last_add_price": 32750
|
||||
}
|
||||
},
|
||||
"realized_pnl": -16466791.812500022,
|
||||
@@ -197,5 +197,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583198+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.050672+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.492793+09:00"
|
||||
}
|
||||
@@ -110,3 +110,4 @@
|
||||
{"ts": "2026-08-20T09:16:22.852888+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 38, "cost": 1360604, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 53964, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 53,964"}, {"label": "추세(20일선)", "ok": true, "detail": "26,208"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T13:12:22.278671+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 625, "proceeds": 3137620, "entry_price": 6333, "pnl": -821064, "pnl_pct": -20.57, "hold_from": "2026-07-30T09:04:07.515982+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 8,921"}, {"label": "추세(20일선)", "ok": true, "detail": "4,754"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:22.279168+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 31900, "qty": 163, "cost": 5200480, "path": "pullback", "reason": "눌림목 지정가 31,939 도달", "stop": 29900, "target": 35900, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "31,850 vs 31,405"}, {"label": "정배열(5>20)", "ok": true, "detail": "32,390 / 31,405"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "32,390 / 30,876"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+3.6%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -892 · 기 +171 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+37%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.24% (환산) vs 평균 0.34% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 33,850"}, {"label": "거래량 급증", "ok": false, "detail": "1,768,231 (환산) vs 평균 2,489,243"}, {"label": "회전율 급증", "ok": false, "detail": "0.24% (환산) vs 평균 0.34%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 31,939 (현재 31,850)"}]}
|
||||
{"ts": "2026-08-25T11:36:19.776873+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 32750, "qty": 158, "cost": 5175276, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 29900, "target": 35900, "signals": [{"label": "손절선", "ok": true, "detail": "29,900 (현재 32,750)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 35,900"}, {"label": "추세(20일선)", "ok": true, "detail": "31,450"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -1,322 · 기 +436"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 32,722"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 38800060.64600003,
|
||||
"cash": 33746802.77100003,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"010950": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3832048.06532556,
|
||||
"last_add_price": 136000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3393597.375168939,
|
||||
"last_add_price": 13420
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,170 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2024244.4253400748,
|
||||
"last_add_price": 13830
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4815698.713054757,
|
||||
"last_add_price": 196400
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3472153.1669509606,
|
||||
"last_add_price": 415500
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1360762.8418676818,
|
||||
"last_add_price": 35800
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 147,729 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1555265.8091770594,
|
||||
"last_add_price": 144900
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 44,
|
||||
"entry_price": 114500,
|
||||
"qty": 87,
|
||||
"entry_price": 115982.75862068965,
|
||||
"entry_at": "2026-08-24T13:12:22.284078+09:00",
|
||||
"stop": 111200,
|
||||
"target": 121100,
|
||||
"peak": 115500,
|
||||
"target": 125548,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,169 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5139578.521625002,
|
||||
"last_add_price": 114500
|
||||
"last_add_price": 117500
|
||||
}
|
||||
},
|
||||
"realized_pnl": -17517547.978000022,
|
||||
@@ -197,5 +197,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583212+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.055048+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.497214+09:00"
|
||||
}
|
||||
@@ -111,3 +111,4 @@
|
||||
{"ts": "2026-08-20T09:16:22.857442+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 38, "cost": 1360604, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 53964, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 53,964"}, {"label": "추세(20일선)", "ok": true, "detail": "26,208"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T13:12:22.283801+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 617, "proceeds": 3097458, "entry_price": 6333, "pnl": -810498, "pnl_pct": -20.57, "hold_from": "2026-07-30T09:04:07.518900+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 8,921"}, {"label": "추세(20일선)", "ok": true, "detail": "4,754"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:22.284081+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 114500, "qty": 44, "cost": 5038756, "path": "pullback", "reason": "눌림목 지정가 115,169 도달", "stop": 111200, "target": 121100, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "114,200 vs 113,265"}, {"label": "정배열(5>20)", "ok": true, "detail": "116,300 / 113,265"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+13.5%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +48 · 기 -50 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+34%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.12% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "197,995 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.12% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "52"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,169 (현재 114,200)"}]}
|
||||
{"ts": "2026-08-25T09:26:22.427060+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 117500, "qty": 43, "cost": 5053258, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 121100, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 117,300)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 121,100"}, {"label": "추세(20일선)", "ok": true, "detail": "113,420"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 117,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 26241131.8935,
|
||||
"cash": 21131365.5435,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"138040": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 118,115 도달",
|
||||
"cur_price": 115000,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5327150.576000001,
|
||||
"last_add_price": 120500
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 122,665 도달",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4940372.790080874,
|
||||
"last_add_price": 125200
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3791237.2526261737,
|
||||
"last_add_price": 136000
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,947 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2020520.412274148,
|
||||
"last_add_price": 13830
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4842897.369596543,
|
||||
"last_add_price": 196400
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3465074.791945059,
|
||||
"last_add_price": 415500
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1349451.2885879693,
|
||||
"last_add_price": 35800
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 160,
|
||||
"entry_price": 31900,
|
||||
"qty": 316,
|
||||
"entry_price": 32319.620253164558,
|
||||
"entry_at": "2026-08-24T13:12:22.288565+09:00",
|
||||
"stop": 29900,
|
||||
"target": 35900,
|
||||
"peak": 32050,
|
||||
"target": 37159,
|
||||
"peak": 32800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 31,939 도달",
|
||||
"cur_price": 31950,
|
||||
"tranches": 1,
|
||||
"cur_price": 32350,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5113659.21834375,
|
||||
"last_add_price": 31900
|
||||
"last_add_price": 32750
|
||||
}
|
||||
},
|
||||
"realized_pnl": -18196294.97050002,
|
||||
@@ -197,5 +197,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583227+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.059078+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.501193+09:00"
|
||||
}
|
||||
@@ -103,3 +103,4 @@
|
||||
{"ts": "2026-08-20T09:16:22.861461+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 37, "cost": 1324799, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 53964, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 53,964"}, {"label": "추세(10일선)", "ok": true, "detail": "28,922"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T13:12:22.288225+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 606, "proceeds": 3042236, "entry_price": 6333, "pnl": -796180, "pnl_pct": -20.58, "hold_from": "2026-07-30T09:04:07.522091+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 8,921"}, {"label": "추세(10일선)", "ok": false, "detail": "5,148"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:22.288567+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 31900, "qty": 160, "cost": 5104766, "path": "pullback", "reason": "눌림목 지정가 31,939 도달", "stop": 29900, "target": 35900, "signals": [{"label": "추세(10일선 위)", "ok": true, "detail": "31,850 vs 31,795"}, {"label": "정배열(5>10)", "ok": true, "detail": "32,390 / 31,795"}, {"label": "추세 기울기(10일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "32,390 / 30,876"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+3.6%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -892 · 기 +171 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+37%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.24% (환산) vs 평균 0.34% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 33,850"}, {"label": "거래량 급증", "ok": false, "detail": "1,768,231 (환산) vs 평균 2,489,243"}, {"label": "회전율 급증", "ok": false, "detail": "0.24% (환산) vs 평균 0.34%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 31,939 (현재 31,850)"}]}
|
||||
{"ts": "2026-08-25T11:36:19.785923+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 32750, "qty": 156, "cost": 5109766, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 29900, "target": 35900, "signals": [{"label": "손절선", "ok": true, "detail": "29,900 (현재 32,750)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 35,900"}, {"label": "추세(10일선)", "ok": true, "detail": "31,885"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -1,322 · 기 +436"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 32,722"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 34912164.782499954,
|
||||
"cash": 28929102.83599996,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"078930": {
|
||||
@@ -11,39 +11,18 @@
|
||||
"qty": 100,
|
||||
"entry_price": 91919.0,
|
||||
"entry_at": "2026-08-04T09:50:10.235258+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 104705,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4659757.825201359,
|
||||
"last_add_price": 93500
|
||||
},
|
||||
"252990": {
|
||||
"code": "252990",
|
||||
"name": "샘씨엔에스",
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 355,
|
||||
"entry_price": 13422.760563380281,
|
||||
"entry_at": "2026-08-05T09:02:14.777207+09:00",
|
||||
"stop": 11093,
|
||||
"target": 16917,
|
||||
"peak": 16270,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,170 도달",
|
||||
"cur_price": 13310,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2390089.3312908146,
|
||||
"last_add_price": 13830
|
||||
},
|
||||
"068270": {
|
||||
"code": "068270",
|
||||
"name": "셀트리온",
|
||||
@@ -60,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5316706.344343748,
|
||||
"last_add_price": 196400
|
||||
@@ -81,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4726305.668282297,
|
||||
"last_add_price": 400000
|
||||
@@ -102,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1835134.7132164796,
|
||||
"last_add_price": 35800
|
||||
@@ -123,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 147,729 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 2097577.151946268,
|
||||
"last_add_price": 144900
|
||||
@@ -144,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 7,426 도달",
|
||||
"cur_price": 5060,
|
||||
"cur_price": 5040,
|
||||
"tranches": 1,
|
||||
"tranche_value": 2255592.7366090417,
|
||||
"last_add_price": 5500
|
||||
@@ -155,24 +134,45 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 45,
|
||||
"entry_price": 114300,
|
||||
"qty": 89,
|
||||
"entry_price": 115882.02247191011,
|
||||
"entry_at": "2026-08-24T09:02:24.057753+09:00",
|
||||
"stop": 111200,
|
||||
"target": 118950,
|
||||
"peak": 116200,
|
||||
"target": 122905,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5239664.769218747,
|
||||
"last_add_price": 114300
|
||||
"last_add_price": 117500
|
||||
},
|
||||
"030000": {
|
||||
"code": "030000",
|
||||
"name": "제일기획",
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 274,
|
||||
"entry_price": 19190,
|
||||
"entry_at": "2026-08-25T12:16:24.258018+09:00",
|
||||
"stop": 18112,
|
||||
"target": 20807,
|
||||
"peak": 19250,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 19,672 도달",
|
||||
"cur_price": 19110,
|
||||
"tranches": 1,
|
||||
"tranche_value": 5261803.221562497,
|
||||
"last_add_price": 19190
|
||||
}
|
||||
},
|
||||
"realized_pnl": -18572578.975500032,
|
||||
"closed_trades": 48,
|
||||
"realized_pnl": -18891811.47500003,
|
||||
"closed_trades": 49,
|
||||
"wins": 6,
|
||||
"peak_equity": 100344662.275,
|
||||
"max_drawdown": 0.17793081459150337,
|
||||
@@ -192,12 +192,12 @@
|
||||
"024060": "2026-07-27",
|
||||
"003680": "2026-08-19",
|
||||
"138040": "2026-08-04",
|
||||
"252990": "2026-07-28",
|
||||
"252990": "2026-08-25",
|
||||
"090850": "2026-08-06",
|
||||
"078930": "2026-07-31",
|
||||
"003550": "2026-08-19",
|
||||
"194370": "2026-08-24"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583241+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.062838+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.505045+09:00"
|
||||
}
|
||||
@@ -129,3 +129,6 @@
|
||||
{"ts": "2026-08-20T09:16:22.865424+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 51, "cost": 1826074, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 25688, "target": 44967, "signals": [{"label": "손절선", "ok": true, "detail": "25,688 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 44,967"}, {"label": "추세(20일선)", "ok": true, "detail": "26,208"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T09:00:26.545777+09:00", "side": "SELL", "code": "194370", "name": "제이에스코퍼레이션", "price": 12790, "qty": 698, "proceeds": 8910012, "entry_price": 13160, "pnl": -276966, "pnl_pct": -2.81, "hold_from": "2026-08-03T12:00:12.468905+09:00", "reason": "시간손절 — 20일 보유·진전 없음(자본 회전)", "partial": false, "signals": [{"label": "손절선", "ok": true, "detail": "11,874 (현재 12,750)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 15,089"}, {"label": "추세(20일선)", "ok": true, "detail": "11,913"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -59 · 기 -20"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T09:02:24.057756+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 114300, "qty": 45, "cost": 5144272, "path": "pullback", "reason": "눌림목 지정가 115,212 도달", "stop": 111200, "target": 118950, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "114,200 vs 113,265"}, {"label": "정배열(5>20)", "ok": true, "detail": "116,300 / 113,265"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+10.9%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +57 · 기 -54 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+34%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.02% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "30,233 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.02% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "52"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,212 (현재 114,200)"}]}
|
||||
{"ts": "2026-08-25T09:04:20.846774+09:00", "side": "SELL", "code": "252990", "name": "샘씨엔에스", "price": 12550, "qty": 355, "proceeds": 4446562, "entry_price": 13423, "pnl": -319232, "pnl_pct": -6.5, "hold_from": "2026-08-05T09:02:14.777207+09:00", "reason": "시간손절 — 20일 보유·진전 없음(자본 회전)", "partial": false, "signals": [{"label": "손절선", "ok": true, "detail": "11,093 (현재 12,600)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 16,917"}, {"label": "추세(20일선)", "ok": false, "detail": "12,705"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +34 · 기 -30"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-25T09:26:22.435224+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 117500, "qty": 44, "cost": 5170776, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 118950, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 117,300)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 118,950"}, {"label": "추세(20일선)", "ok": true, "detail": "113,420"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 116,989"}]}
|
||||
{"ts": "2026-08-25T12:16:24.258020+09:00", "side": "BUY", "code": "030000", "name": "제일기획", "price": 19190, "qty": 274, "cost": 5258849, "path": "pullback", "reason": "눌림목 지정가 19,672 도달", "stop": 18112, "target": 20807, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "19,180 vs 18,864"}, {"label": "정배열(5>20)", "ok": true, "detail": "19,622 / 18,864"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "19,622 / 19,362"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+0.8%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +274 · 기 -97 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+31%"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.21% (환산) vs 평균 0.62% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 21,150"}, {"label": "거래량 급증", "ok": false, "detail": "241,100 (환산) vs 평균 711,709"}, {"label": "회전율 급증", "ok": false, "detail": "0.21% (환산) vs 평균 0.62%"}, {"label": "RSI", "ok": true, "detail": "51"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 19,672 (현재 19,180)"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 50170967.108500004,
|
||||
"cash": 37906014.847,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"010950": {
|
||||
@@ -18,32 +18,11 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4011548.961969723,
|
||||
"last_add_price": 136000
|
||||
},
|
||||
"252990": {
|
||||
"code": "252990",
|
||||
"name": "샘씨엔에스",
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 306,
|
||||
"entry_price": 13511.437908496731,
|
||||
"entry_at": "2026-08-05T09:08:12.255584+09:00",
|
||||
"stop": 10405,
|
||||
"target": 18170,
|
||||
"peak": 16270,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,947 도달",
|
||||
"cur_price": 13310,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2074177.1346915166,
|
||||
"last_add_price": 13940
|
||||
},
|
||||
"214450": {
|
||||
"code": "214450",
|
||||
"name": "파마리서치",
|
||||
@@ -60,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3613600.002573951,
|
||||
"last_add_price": 415500
|
||||
@@ -81,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1405152.4684093075,
|
||||
"last_add_price": 35800
|
||||
@@ -102,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 157,486 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1606000.2783602993,
|
||||
"last_add_price": 144900
|
||||
@@ -123,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 7,771 도달",
|
||||
"cur_price": 5060,
|
||||
"cur_price": 5040,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1728362.8125607767,
|
||||
"last_add_price": 5500
|
||||
@@ -134,20 +113,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 47,
|
||||
"entry_price": 115000,
|
||||
"qty": 92,
|
||||
"entry_price": 116565.21739130435,
|
||||
"entry_at": "2026-08-20T09:02:18.585107+09:00",
|
||||
"stop": 111200,
|
||||
"target": 120700,
|
||||
"peak": 117500,
|
||||
"target": 124613,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 117,922 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5415388.14053125,
|
||||
"last_add_price": 115000
|
||||
"last_add_price": 118200
|
||||
},
|
||||
"316140": {
|
||||
"code": "316140",
|
||||
@@ -155,27 +134,48 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 169,
|
||||
"entry_price": 31550,
|
||||
"qty": 334,
|
||||
"entry_price": 31969.91017964072,
|
||||
"entry_at": "2026-08-24T09:02:24.061441+09:00",
|
||||
"stop": 29900,
|
||||
"target": 34025,
|
||||
"peak": 32050,
|
||||
"target": 35075,
|
||||
"peak": 32800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 31,946 도달",
|
||||
"cur_price": 31950,
|
||||
"tranches": 1,
|
||||
"cur_price": 32350,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5347393.5563125005,
|
||||
"last_add_price": 31550
|
||||
"last_add_price": 32400
|
||||
},
|
||||
"030000": {
|
||||
"code": "030000",
|
||||
"name": "제일기획",
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 279,
|
||||
"entry_price": 19190,
|
||||
"entry_at": "2026-08-25T12:16:24.261562+09:00",
|
||||
"stop": 17760,
|
||||
"target": 21335,
|
||||
"peak": 19250,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 20,031 도달",
|
||||
"cur_price": 19110,
|
||||
"tranches": 1,
|
||||
"tranche_value": 5366224.24678125,
|
||||
"last_add_price": 19190
|
||||
}
|
||||
},
|
||||
"realized_pnl": -14390367.889000028,
|
||||
"closed_trades": 43,
|
||||
"realized_pnl": -14769027.47400003,
|
||||
"closed_trades": 44,
|
||||
"wins": 6,
|
||||
"peak_equity": 101368884.75700004,
|
||||
"max_drawdown": 0.16179288869376138,
|
||||
"max_drawdown": 0.16324454094733756,
|
||||
"last_exit": {
|
||||
"030000": "2026-08-13",
|
||||
"086790": "2026-08-19",
|
||||
@@ -189,7 +189,7 @@
|
||||
"319660": "2026-07-23",
|
||||
"003490": "2026-07-09",
|
||||
"105560": "2026-07-20",
|
||||
"252990": "2026-07-28",
|
||||
"252990": "2026-08-25",
|
||||
"003680": "2026-08-19",
|
||||
"010950": "2026-07-29",
|
||||
"090850": "2026-08-05",
|
||||
@@ -199,5 +199,5 @@
|
||||
"194370": "2026-08-24"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583273+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.066282+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.508526+09:00"
|
||||
}
|
||||
@@ -121,3 +121,7 @@
|
||||
{"ts": "2026-08-20T09:16:22.868997+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 39, "cost": 1396409, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 48823, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 48,823"}, {"label": "추세(20일선)", "ok": true, "detail": "26,208"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T09:00:26.646312+09:00", "side": "SELL", "code": "194370", "name": "제이에스코퍼레이션", "price": 12790, "qty": 535, "proceeds": 6829307, "entry_price": 13160, "pnl": -212219, "pnl_pct": -2.81, "hold_from": "2026-08-03T12:00:12.472627+09:00", "reason": "시간손절 — 20일 보유·진전 없음(자본 회전)", "partial": false, "signals": [{"label": "손절선", "ok": true, "detail": "11,445 (현재 12,750)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 15,732"}, {"label": "추세(20일선)", "ok": true, "detail": "11,913"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -59 · 기 -20"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T09:02:24.061444+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 31550, "qty": 169, "cost": 5332750, "path": "pullback", "reason": "눌림목 지정가 31,946 도달", "stop": 29900, "target": 34025, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "31,500 vs 31,388"}, {"label": "정배열(5>20)", "ok": true, "detail": "32,320 / 31,388"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "32,320 / 30,870"}, {"label": "상대강도(시장대비)", "ok": false, "detail": "-0.2%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -988 · 기 +144 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+39%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.08% (환산) vs 평균 0.34% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 33,850"}, {"label": "거래량 급증", "ok": false, "detail": "551,940 (환산) vs 평균 2,489,243"}, {"label": "회전율 급증", "ok": false, "detail": "0.08% (환산) vs 평균 0.34%"}, {"label": "RSI", "ok": true, "detail": "51"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 31,946 (현재 31,500)"}]}
|
||||
{"ts": "2026-08-25T09:10:22.337593+09:00", "side": "SELL", "code": "252990", "name": "샘씨엔에스", "price": 12300, "qty": 306, "proceeds": 3756461, "entry_price": 13511, "pnl": -378660, "pnl_pct": -8.97, "hold_from": "2026-08-05T09:08:12.255584+09:00", "reason": "시간손절 — 20일 보유·진전 없음(자본 회전)", "partial": false, "signals": [{"label": "손절선", "ok": true, "detail": "10,405 (현재 12,330)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 18,170"}, {"label": "추세(20일선)", "ok": false, "detail": "12,692"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +34 · 기 -30"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-25T09:34:19.115760+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 45, "cost": 5319798, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 120700, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 120,700"}, {"label": "추세(20일선)", "ok": true, "detail": "113,465"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 117,689"}]}
|
||||
{"ts": "2026-08-25T09:46:21.219302+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 32400, "qty": 165, "cost": 5346802, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 29900, "target": 34025, "signals": [{"label": "손절선", "ok": true, "detail": "29,900 (현재 32,400)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 34,025"}, {"label": "추세(20일선)", "ok": true, "detail": "31,432"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -1,322 · 기 +436"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 32,372"}]}
|
||||
{"ts": "2026-08-25T12:16:24.261564+09:00", "side": "BUY", "code": "030000", "name": "제일기획", "price": 19190, "qty": 279, "cost": 5354813, "path": "pullback", "reason": "눌림목 지정가 20,031 도달", "stop": 17760, "target": 21335, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "19,180 vs 18,864"}, {"label": "정배열(5>20)", "ok": true, "detail": "19,622 / 18,864"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "19,622 / 19,362"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+0.8%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +274 · 기 -97 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+31%"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.21% (환산) vs 평균 0.62% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 21,150"}, {"label": "거래량 급증", "ok": false, "detail": "241,100 (환산) vs 평균 711,709"}, {"label": "회전율 급증", "ok": false, "detail": "0.21% (환산) vs 평균 0.62%"}, {"label": "RSI", "ok": true, "detail": "51"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 20,031 (현재 19,180)"}]}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3813556.4512976124,
|
||||
"last_add_price": 136000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3377559.3310465557,
|
||||
"last_add_price": 13420
|
||||
@@ -53,14 +53,14 @@
|
||||
"qty": 73,
|
||||
"entry_price": 91928.76712328767,
|
||||
"entry_at": "2026-08-04T09:50:10.241980+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 114659,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3429919.977026871,
|
||||
"last_add_price": 93500
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,170 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2016864.6660954827,
|
||||
"last_add_price": 13830
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4837813.664610954,
|
||||
"last_add_price": 196400
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3481984.841611142,
|
||||
"last_add_price": 416500
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1387817.2573049513,
|
||||
"last_add_price": 35800
|
||||
@@ -160,12 +160,12 @@
|
||||
"entry_at": "2026-08-24T13:14:22.571465+09:00",
|
||||
"stop": 17760,
|
||||
"target": 21480,
|
||||
"peak": 19110,
|
||||
"peak": 19250,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 19,655 도달",
|
||||
"cur_price": 19100,
|
||||
"cur_price": 19110,
|
||||
"tranches": 1,
|
||||
"tranche_value": 5237145.480593753,
|
||||
"last_add_price": 19000
|
||||
@@ -195,5 +195,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583289+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.069429+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.511676+09:00"
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3863996.750727336,
|
||||
"last_add_price": 136000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3428336.7554835076,
|
||||
"last_add_price": 13420
|
||||
@@ -53,14 +53,14 @@
|
||||
"qty": 75,
|
||||
"entry_price": 91929.33333333333,
|
||||
"entry_at": "2026-08-04T09:50:10.245606+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 108977,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3483887.723272795,
|
||||
"last_add_price": 93500
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,170 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2048676.6433078528,
|
||||
"last_add_price": 13830
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4916501.597175794,
|
||||
"last_add_price": 196400
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3537977.413382886,
|
||||
"last_add_price": 415500
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1411419.441084128,
|
||||
"last_add_price": 35800
|
||||
@@ -160,12 +160,12 @@
|
||||
"entry_at": "2026-08-24T13:14:22.574664+09:00",
|
||||
"stop": 17760,
|
||||
"target": 20860,
|
||||
"peak": 19110,
|
||||
"peak": 19250,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 19,655 도달",
|
||||
"cur_price": 19100,
|
||||
"cur_price": 19110,
|
||||
"tranches": 1,
|
||||
"tranche_value": 5325511.789718751,
|
||||
"last_add_price": 19000
|
||||
@@ -195,5 +195,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583304+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.072558+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.514810+09:00"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 26745606.15750003,
|
||||
"cash": 21537575.07000003,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"138040": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 118,115 도달",
|
||||
"cur_price": 115000,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5436300.471625002,
|
||||
"last_add_price": 120500
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 122,665 도달",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5045807.2398149045,
|
||||
"last_add_price": 125200
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3860356.183815298,
|
||||
"last_add_price": 136000
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,947 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2062154.0490506678,
|
||||
"last_add_price": 13830
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4944712.1127089355,
|
||||
"last_add_price": 196400
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3537997.7129504834,
|
||||
"last_add_price": 415500
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1377959.1486430662,
|
||||
"last_add_price": 35800
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 163,
|
||||
"entry_price": 31900,
|
||||
"qty": 322,
|
||||
"entry_price": 32319.72049689441,
|
||||
"entry_at": "2026-08-24T13:12:22.306023+09:00",
|
||||
"stop": 29900,
|
||||
"target": 34900,
|
||||
"peak": 32050,
|
||||
"target": 35949,
|
||||
"peak": 32800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 31,939 도달",
|
||||
"cur_price": 31950,
|
||||
"tranches": 1,
|
||||
"cur_price": 32350,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5220757.257031252,
|
||||
"last_add_price": 31900
|
||||
"last_add_price": 32750
|
||||
}
|
||||
},
|
||||
"realized_pnl": -16483989.55900002,
|
||||
@@ -197,5 +197,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583318+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.075550+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.517827+09:00"
|
||||
}
|
||||
@@ -111,3 +111,4 @@
|
||||
{"ts": "2026-08-20T09:16:22.878610+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 38, "cost": 1360604, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 48823, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 48,823"}, {"label": "추세(10일선)", "ok": true, "detail": "28,922"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T13:12:22.305766+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 619, "proceeds": 3107499, "entry_price": 6333, "pnl": -813139, "pnl_pct": -20.57, "hold_from": "2026-07-30T09:04:07.536435+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 8,274"}, {"label": "추세(10일선)", "ok": false, "detail": "5,148"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:22.306025+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 31900, "qty": 163, "cost": 5200480, "path": "pullback", "reason": "눌림목 지정가 31,939 도달", "stop": 29900, "target": 34900, "signals": [{"label": "추세(10일선 위)", "ok": true, "detail": "31,850 vs 31,795"}, {"label": "정배열(5>10)", "ok": true, "detail": "32,390 / 31,795"}, {"label": "추세 기울기(10일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "32,390 / 30,876"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+3.6%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -892 · 기 +171 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+37%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.24% (환산) vs 평균 0.34% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 33,850"}, {"label": "거래량 급증", "ok": false, "detail": "1,768,231 (환산) vs 평균 2,489,243"}, {"label": "회전율 급증", "ok": false, "detail": "0.24% (환산) vs 평균 0.34%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 31,939 (현재 31,850)"}]}
|
||||
{"ts": "2026-08-25T11:36:19.803532+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 32750, "qty": 159, "cost": 5208031, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 29900, "target": 34900, "signals": [{"label": "손절선", "ok": true, "detail": "29,900 (현재 32,750)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 34,900"}, {"label": "추세(10일선)", "ok": true, "detail": "31,885"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -1,322 · 기 +436"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 32,722"}]}
|
||||
|
||||
@@ -1,28 +1,7 @@
|
||||
{
|
||||
"cash": 45018222.16959998,
|
||||
"cash": 30209394.524599984,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"252990": {
|
||||
"code": "252990",
|
||||
"name": "샘씨엔에스",
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 338,
|
||||
"entry_price": 13423.313609467456,
|
||||
"entry_at": "2026-08-05T09:02:14.792964+09:00",
|
||||
"stop": 11094,
|
||||
"target": 18082,
|
||||
"peak": 16270,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,947 도달",
|
||||
"cur_price": 13310,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2275843.933250867,
|
||||
"last_add_price": 13830
|
||||
},
|
||||
"214450": {
|
||||
"code": "214450",
|
||||
"name": "파마리서치",
|
||||
@@ -39,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4532610.960524749,
|
||||
"last_add_price": 416500
|
||||
@@ -60,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1761589.922015132,
|
||||
"last_add_price": 35800
|
||||
@@ -81,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 157,486 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 2013514.706883483,
|
||||
"last_add_price": 144900
|
||||
@@ -102,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 7,771 도달",
|
||||
"cur_price": 5060,
|
||||
"cur_price": 5040,
|
||||
"tranches": 1,
|
||||
"tranche_value": 2166690.069603723,
|
||||
"last_add_price": 5500
|
||||
@@ -113,20 +92,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 44,
|
||||
"entry_price": 115000,
|
||||
"qty": 87,
|
||||
"entry_price": 116581.6091954023,
|
||||
"entry_at": "2026-08-20T09:02:18.599361+09:00",
|
||||
"stop": 111200,
|
||||
"target": 122600,
|
||||
"peak": 117500,
|
||||
"target": 127345,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 117,922 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5094447.6495062485,
|
||||
"last_add_price": 115000
|
||||
"last_add_price": 118200
|
||||
},
|
||||
"030000": {
|
||||
"code": "030000",
|
||||
@@ -134,20 +113,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 270,
|
||||
"entry_price": 18880,
|
||||
"qty": 535,
|
||||
"entry_price": 19058.317757009347,
|
||||
"entry_at": "2026-08-20T09:08:18.031117+09:00",
|
||||
"stop": 17770,
|
||||
"target": 21100,
|
||||
"peak": 19110,
|
||||
"stop": 17980,
|
||||
"target": 21214,
|
||||
"peak": 19250,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 20,010 도달",
|
||||
"cur_price": 19100,
|
||||
"tranches": 1,
|
||||
"cur_price": 19110,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5113152.0870062485,
|
||||
"last_add_price": 18880
|
||||
"last_add_price": 19240
|
||||
},
|
||||
"316140": {
|
||||
"code": "316140",
|
||||
@@ -155,24 +134,45 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 158,
|
||||
"entry_price": 31550,
|
||||
"qty": 312,
|
||||
"entry_price": 31969.55128205128,
|
||||
"entry_at": "2026-08-24T09:02:24.073710+09:00",
|
||||
"stop": 29900,
|
||||
"target": 34850,
|
||||
"peak": 32050,
|
||||
"target": 36109,
|
||||
"peak": 32800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 31,946 도달",
|
||||
"cur_price": 31950,
|
||||
"tranches": 1,
|
||||
"cur_price": 32350,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5016181.869037499,
|
||||
"last_add_price": 31550
|
||||
"last_add_price": 32400
|
||||
},
|
||||
"161890": {
|
||||
"code": "161890",
|
||||
"name": "한국콜마",
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 26,
|
||||
"entry_price": 148800,
|
||||
"entry_at": "2026-08-25T12:16:24.309994+09:00",
|
||||
"stop": 133669,
|
||||
"target": 179062,
|
||||
"peak": 150800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 149900,
|
||||
"tranches": 1,
|
||||
"tranche_value": 3964400.3679956635,
|
||||
"last_add_price": 148800
|
||||
}
|
||||
},
|
||||
"realized_pnl": -19162625.763400026,
|
||||
"closed_trades": 52,
|
||||
"realized_pnl": -19466758.030400027,
|
||||
"closed_trades": 53,
|
||||
"wins": 6,
|
||||
"peak_equity": 101501376.42350003,
|
||||
"max_drawdown": 0.22092032524603686,
|
||||
@@ -190,7 +190,7 @@
|
||||
"319660": "2026-07-23",
|
||||
"105560": "2026-07-20",
|
||||
"003680": "2026-08-19",
|
||||
"252990": "2026-07-28",
|
||||
"252990": "2026-08-25",
|
||||
"010950": "2026-08-20",
|
||||
"090850": "2026-08-05",
|
||||
"161890": "2026-07-30",
|
||||
@@ -200,5 +200,5 @@
|
||||
"316140": "2026-08-21"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583333+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.078467+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.520716+09:00"
|
||||
}
|
||||
@@ -145,3 +145,8 @@
|
||||
{"ts": "2026-08-20T13:32:20.985167+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 31900, "qty": 159, "cost": 5072861, "path": "pullback", "reason": "눌림목 지정가 31,975 도달", "stop": 29900, "target": 35900, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "31,850 vs 31,405"}, {"label": "정배열(5>20)", "ok": true, "detail": "32,390 / 31,405"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "32,390 / 30,876"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+2.4%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +770 · 기 -588 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+37%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.39% (환산) vs 평균 0.34% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 33,850"}, {"label": "거래량 급증", "ok": false, "detail": "2,811,880 (환산) vs 평균 2,489,243"}, {"label": "회전율 급증", "ok": false, "detail": "0.39% (환산) vs 평균 0.34%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 31,975 (현재 31,850)"}]}
|
||||
{"ts": "2026-08-21T09:02:19.788078+09:00", "side": "SELL", "code": "316140", "name": "우리금융지주", "price": 31350, "qty": 159, "proceeds": 4974930, "entry_price": 31900, "pnl": -97931, "pnl_pct": -1.72, "hold_from": "2026-08-20T13:32:20.985150+09:00", "reason": "추세·수급 이탈", "partial": false, "signals": [{"label": "손절선", "ok": true, "detail": "29,900 (현재 31,350)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 35,900"}, {"label": "추세(20일선)", "ok": false, "detail": "31,380"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -337 · 기 -91"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 32,741"}]}
|
||||
{"ts": "2026-08-24T09:02:24.073712+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 31550, "qty": 158, "cost": 4985648, "path": "pullback", "reason": "눌림목 지정가 31,946 도달", "stop": 29900, "target": 34850, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "31,500 vs 31,388"}, {"label": "정배열(5>20)", "ok": true, "detail": "32,320 / 31,388"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "32,320 / 30,870"}, {"label": "상대강도(시장대비)", "ok": false, "detail": "-0.2%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -988 · 기 +144 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+39%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.08% (환산) vs 평균 0.34% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 33,850"}, {"label": "거래량 급증", "ok": false, "detail": "551,940 (환산) vs 평균 2,489,243"}, {"label": "회전율 급증", "ok": false, "detail": "0.08% (환산) vs 평균 0.34%"}, {"label": "RSI", "ok": true, "detail": "51"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 31,946 (현재 31,500)"}]}
|
||||
{"ts": "2026-08-25T09:04:20.867256+09:00", "side": "SELL", "code": "252990", "name": "샘씨엔에스", "price": 12550, "qty": 338, "proceeds": 4233628, "entry_price": 13423, "pnl": -304132, "pnl_pct": -6.51, "hold_from": "2026-08-05T09:02:14.792964+09:00", "reason": "시간손절 — 20일 보유·진전 없음(자본 회전)", "partial": false, "signals": [{"label": "손절선", "ok": true, "detail": "11,094 (현재 12,600)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 18,082"}, {"label": "추세(20일선)", "ok": false, "detail": "12,705"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +34 · 기 -30"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-25T09:34:19.127950+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 43, "cost": 5083362, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 122600, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 122,600"}, {"label": "추세(20일선)", "ok": true, "detail": "113,465"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 117,689"}]}
|
||||
{"ts": "2026-08-25T09:46:21.231619+09:00", "side": "BUY", "code": "316140", "name": "우리금융지주", "price": 32400, "qty": 154, "cost": 4990348, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 29900, "target": 34850, "signals": [{"label": "손절선", "ok": true, "detail": "29,900 (현재 32,400)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 34,850"}, {"label": "추세(20일선)", "ok": true, "detail": "31,432"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -1,322 · 기 +436"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 32,372"}]}
|
||||
{"ts": "2026-08-25T12:16:24.309996+09:00", "side": "BUY", "code": "161890", "name": "한국콜마", "price": 148800, "qty": 26, "cost": 3869380, "path": "breakout", "reason": "거래량·회전율 동반 전고점 돌파", "stop": 133669, "target": 179062, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "148,600 vs 100,995"}, {"label": "정배열(5>20)", "ok": true, "detail": "117,020 / 100,995"}, {"label": "추세 기울기(20일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "117,020 / 92,743"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+73.7%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -153 · 기 +229 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+13%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "4.31% (환산) vs 평균 1.76% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": true, "detail": "고점 124,600"}, {"label": "거래량 급증", "ok": true, "detail": "1,015,655 (환산) vs 평균 415,172"}, {"label": "회전율 급증", "ok": true, "detail": "4.31% (환산) vs 평균 1.76%"}, {"label": "RSI", "ok": true, "detail": "80"}]}
|
||||
{"ts": "2026-08-25T13:06:19.763129+09:00", "side": "BUY", "code": "030000", "name": "제일기획", "price": 19240, "qty": 265, "cost": 5099365, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 17770, "target": 21100, "signals": [{"label": "손절선", "ok": true, "detail": "17,770 (현재 19,240)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 21,100"}, {"label": "추세(20일선)", "ok": true, "detail": "18,866"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +274 · 기 -97"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 19,239"}]}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 118,115 도달",
|
||||
"cur_price": 115000,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5234858.44571875,
|
||||
"last_add_price": 120500
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 122,665 도달",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5051285.121721717,
|
||||
"last_add_price": 125200
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4362596.463271157,
|
||||
"last_add_price": 13420
|
||||
@@ -74,14 +74,14 @@
|
||||
"qty": 87,
|
||||
"entry_price": 98831.03448275862,
|
||||
"entry_at": "2026-08-04T14:12:15.953832+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 117336,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4360412.203364462,
|
||||
"last_add_price": 100500
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,975 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2357489.1026575607,
|
||||
"last_add_price": 14290
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4482545.509520558,
|
||||
"last_add_price": 415500
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 35,709 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1748295.3872581045,
|
||||
"last_add_price": 35800
|
||||
@@ -165,7 +165,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 1,
|
||||
"tranche_value": 5051512.08909375,
|
||||
"last_add_price": 198700
|
||||
@@ -197,5 +197,5 @@
|
||||
"078930": "2026-07-31"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583347+09:00",
|
||||
"updated_at": "2026-08-24T15:28:23.081094+09:00"
|
||||
"updated_at": "2026-08-25T15:28:24.523371+09:00"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 32827009.305499982,
|
||||
"cash": 25486708.425499983,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3627364.7637924887,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3547525.853504033,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 144,129 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1688085.214726109,
|
||||
"last_add_price": 147600
|
||||
@@ -76,12 +76,12 @@
|
||||
"entry_at": "2026-08-04T09:34:11.170806+09:00",
|
||||
"stop": 80334,
|
||||
"target": 125794,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3165003.2713460657,
|
||||
"last_add_price": 93500
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 104000,
|
||||
"cur_price": 106000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 3967368.72635773,
|
||||
"last_add_price": 110000
|
||||
@@ -118,12 +118,12 @@
|
||||
"entry_at": "2026-08-20T09:18:26.020964+09:00",
|
||||
"stop": 114576,
|
||||
"target": 187273,
|
||||
"peak": 143600,
|
||||
"peak": 150800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 143300,
|
||||
"cur_price": 149900,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2862100.411366492,
|
||||
"last_add_price": 135100
|
||||
@@ -134,20 +134,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 115500,
|
||||
"qty": 81,
|
||||
"entry_price": 116833.33333333333,
|
||||
"entry_at": "2026-08-24T09:10:26.855158+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128400,
|
||||
"peak": 116200,
|
||||
"target": 133733,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4793046.880437499,
|
||||
"last_add_price": 115500
|
||||
"last_add_price": 118200
|
||||
},
|
||||
"257720": {
|
||||
"code": "257720",
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 53,
|
||||
"entry_price": 49450,
|
||||
"qty": 104,
|
||||
"entry_price": 50308.17307692308,
|
||||
"entry_at": "2026-08-24T14:20:24.299052+09:00",
|
||||
"stop": 42307,
|
||||
"target": 70880,
|
||||
"peak": 49650,
|
||||
"stop": 43093,
|
||||
"target": 71953,
|
||||
"peak": 52000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 49450,
|
||||
"tranches": 1,
|
||||
"cur_price": 51800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2646566.7841326115,
|
||||
"last_add_price": 49450
|
||||
"last_add_price": 51200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -26780682.757000055,
|
||||
@@ -209,5 +209,5 @@
|
||||
"252990": "2026-08-21"
|
||||
},
|
||||
"created_at": "2026-06-15T14:40:00.621011+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.416702+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.164503+09:00"
|
||||
}
|
||||
@@ -263,3 +263,5 @@
|
||||
{"ts": "2026-08-24T13:14:23.815550+09:00", "side": "BUY", "code": "028260", "name": "삼성물산", "price": 362000, "qty": 13, "cost": 4706706, "path": "pullback", "reason": "눌림목 지정가 363,789 도달", "stop": 361999, "target": 362003, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "361,500 vs 350,292"}, {"label": "정배열(5>60)", "ok": true, "detail": "408,000 / 350,292"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "408,000 / 350,292"}, {"label": "상대강도(시장대비)", "ok": false, "detail": "-18.3%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -94 · 기 +139 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+37%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.41% (환산) vs 평균 0.43% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 565,000"}, {"label": "거래량 급증", "ok": false, "detail": "673,405 (환산) vs 평균 706,119"}, {"label": "회전율 급증", "ok": false, "detail": "0.41% (환산) vs 평균 0.43%"}, {"label": "RSI", "ok": true, "detail": "43"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 363,789 (현재 361,500)"}]}
|
||||
{"ts": "2026-08-24T14:20:23.229101+09:00", "side": "SELL", "code": "028260", "name": "삼성물산", "price": 361000, "qty": 13, "proceeds": 4683849, "entry_price": 362000, "pnl": -22857, "pnl_pct": -0.28, "hold_from": "2026-08-24T13:14:23.815534+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "361,999 (현재 361,500)"}, {"label": "목표/트레일링", "ok": null, "detail": "트레일링 ON"}, {"label": "추세(60일선)", "ok": true, "detail": "350,292"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -88 · 기 +122"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T14:20:24.299069+09:00", "side": "BUY", "code": "257720", "name": "실리콘투", "price": 49450, "qty": 53, "cost": 2621243, "path": "breakout", "reason": "거래량·회전율 동반 전고점 돌파", "stop": 42307, "target": 70880, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "49,350 vs 36,263"}, {"label": "정배열(5>60)", "ok": true, "detail": "43,660 / 36,263"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "43,660 / 36,263"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+34.4%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -645 · 기 +1,162 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+17%"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "4.50% (환산) vs 평균 1.56% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": true, "detail": "고점 48,250"}, {"label": "거래량 급증", "ok": true, "detail": "2,948,495 (환산) vs 평균 1,023,767"}, {"label": "회전율 급증", "ok": true, "detail": "4.50% (환산) vs 평균 1.56%"}, {"label": "RSI", "ok": true, "detail": "69"}]}
|
||||
{"ts": "2026-08-25T09:34:21.479342+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 40, "cost": 4728709, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,988"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,189"}]}
|
||||
{"ts": "2026-08-25T09:56:20.664845+09:00", "side": "BUY", "code": "257720", "name": "실리콘투", "price": 51200, "qty": 51, "cost": 2611592, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 42307, "target": 70880, "signals": [{"label": "손절선", "ok": true, "detail": "42,307 (현재 51,300)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 70,880"}, {"label": "추세(60일선)", "ok": true, "detail": "36,296"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -428 · 기 +1,106"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 51,254"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 28853204.581999995,
|
||||
"cash": 24152499.581999995,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3627364.7637924887,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3470885.263866936,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 144,129 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1668794.556510239,
|
||||
"last_add_price": 147600
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3062184.227151159,
|
||||
"last_add_price": 13420
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4403461.655606388,
|
||||
"last_add_price": 198600
|
||||
@@ -118,12 +118,12 @@
|
||||
"entry_at": "2026-08-06T09:54:12.716178+09:00",
|
||||
"stop": 87209,
|
||||
"target": 137241,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3007474.6079847123,
|
||||
"last_add_price": 101700
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1249060.2562777183,
|
||||
"last_add_price": 35800
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 114500,
|
||||
"qty": 81,
|
||||
"entry_price": 115981.48148148147,
|
||||
"entry_at": "2026-08-24T13:12:24.611989+09:00",
|
||||
"stop": 111200,
|
||||
"target": 124400,
|
||||
"peak": 115500,
|
||||
"target": 130326,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,169 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4719538.0473125,
|
||||
"last_add_price": 114500
|
||||
"last_add_price": 117500
|
||||
}
|
||||
},
|
||||
"realized_pnl": -26369449.823500052,
|
||||
@@ -211,5 +211,5 @@
|
||||
"138040": "2026-08-19"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583395+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.426013+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.175576+09:00"
|
||||
}
|
||||
@@ -242,3 +242,4 @@
|
||||
{"ts": "2026-08-20T09:16:25.152513+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 34, "cost": 1217383, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 64246, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 64,246"}, {"label": "추세(60일선)", "ok": true, "detail": "31,336"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T13:12:24.611502+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 563, "proceeds": 2826368, "entry_price": 6333, "pnl": -739597, "pnl_pct": -20.57, "hold_from": "2026-07-30T09:04:08.152605+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 10,215"}, {"label": "추세(60일선)", "ok": true, "detail": "4,848"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:24.611992+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 114500, "qty": 41, "cost": 4695204, "path": "pullback", "reason": "눌림목 지정가 115,169 도달", "stop": 111200, "target": 124400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "114,200 vs 109,922"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "추세 기울기(60일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+13.5%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +48 · 기 -50 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+34%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.12% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "197,995 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.12% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "52"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,169 (현재 114,200)"}]}
|
||||
{"ts": "2026-08-25T09:26:24.836219+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 117500, "qty": 40, "cost": 4700705, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 124400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 117,300)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 124,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,973"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 117,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 36393346.89199998,
|
||||
"cash": 31546419.961999983,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3570553.5376633056,
|
||||
"last_add_price": 136000
|
||||
@@ -55,12 +55,12 @@
|
||||
"entry_at": "2026-08-04T09:34:11.191529+09:00",
|
||||
"stop": 80360,
|
||||
"target": 125820,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3188827.7982604695,
|
||||
"last_add_price": 93500
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 104000,
|
||||
"cur_price": 106000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 3997085.960333706,
|
||||
"last_add_price": 110000
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 7,424 도달",
|
||||
"cur_price": 5060,
|
||||
"cur_price": 5040,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1576676.661387545,
|
||||
"last_add_price": 5560
|
||||
@@ -118,12 +118,12 @@
|
||||
"entry_at": "2026-08-20T09:18:26.045143+09:00",
|
||||
"stop": 114521,
|
||||
"target": 187219,
|
||||
"peak": 143600,
|
||||
"peak": 150800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 143300,
|
||||
"cur_price": 149900,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2892213.1259382935,
|
||||
"last_add_price": 135100
|
||||
@@ -139,12 +139,12 @@
|
||||
"entry_at": "2026-08-24T09:02:26.350420+09:00",
|
||||
"stop": 100700,
|
||||
"target": 247900,
|
||||
"peak": 138100,
|
||||
"peak": 139000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 146,379 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1444957.780421195,
|
||||
"last_add_price": 137500
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 42,
|
||||
"entry_price": 115500,
|
||||
"qty": 83,
|
||||
"entry_price": 116833.73493975903,
|
||||
"entry_at": "2026-08-24T09:10:26.877182+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128400,
|
||||
"peak": 116200,
|
||||
"target": 133735,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4852688.408874999,
|
||||
"last_add_price": 115500
|
||||
"last_add_price": 118200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -25731292.656000044,
|
||||
@@ -210,5 +210,5 @@
|
||||
"252990": "2026-08-21"
|
||||
},
|
||||
"created_at": "2026-06-15T14:40:00.621059+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.432244+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.181998+09:00"
|
||||
}
|
||||
@@ -265,3 +265,4 @@
|
||||
{"ts": "2026-08-24T09:04:22.497356+09:00", "side": "BUY", "code": "319660", "name": "피에스케이", "price": 125100, "qty": 38, "cost": 4754513, "path": "pullback", "reason": "눌림목 지정가 155,710 도달", "stop": 125099, "target": 125103, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "125,000 vs 112,642"}, {"label": "정배열(5>60)", "ok": true, "detail": "162,640 / 112,642"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "162,640 / 112,642"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+18.4%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +220 · 기 -194 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+54%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.69% (환산) vs 평균 2.68% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 196,500"}, {"label": "거래량 급증", "ok": false, "detail": "199,953 (환산) vs 평균 777,376"}, {"label": "회전율 급증", "ok": false, "detail": "0.69% (환산) vs 평균 2.68%"}, {"label": "RSI", "ok": true, "detail": "45"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 155,710 (현재 125,000)"}]}
|
||||
{"ts": "2026-08-24T09:10:26.876426+09:00", "side": "SELL", "code": "319660", "name": "피에스케이", "price": 123300, "qty": 38, "proceeds": 4676263, "entry_price": 125100, "pnl": -78250, "pnl_pct": -1.44, "hold_from": "2026-08-24T09:04:22.497352+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "125,099 (현재 122,800)"}, {"label": "목표/트레일링", "ok": null, "detail": "트레일링 ON"}, {"label": "추세(60일선)", "ok": true, "detail": "112,605"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +220 · 기 -194"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T09:10:26.877186+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 115500, "qty": 42, "cost": 4851728, "path": "pullback", "reason": "눌림목 지정가 115,212 도달", "stop": 111200, "target": 128400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "115,200 vs 109,938"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+11.7%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +57 · 기 -54 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+33%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.04% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "61,480 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.04% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,212 (현재 115,200)"}]}
|
||||
{"ts": "2026-08-25T09:34:21.504695+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 41, "cost": 4846927, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,988"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 35776524.87849997,
|
||||
"cash": 31047815.67849997,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3511790.845106854,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4454503.886577569,
|
||||
"last_add_price": 198600
|
||||
@@ -76,12 +76,12 @@
|
||||
"entry_at": "2026-08-06T09:54:12.728482+09:00",
|
||||
"stop": 87177,
|
||||
"target": 137209,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3042388.163482306,
|
||||
"last_add_price": 101700
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1267059.834384944,
|
||||
"last_add_price": 35800
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 7,418 도달",
|
||||
"cur_price": 5060,
|
||||
"cur_price": 5040,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1532132.3284042438,
|
||||
"last_add_price": 5460
|
||||
@@ -139,12 +139,12 @@
|
||||
"entry_at": "2026-08-24T09:02:26.359805+09:00",
|
||||
"stop": 100700,
|
||||
"target": 247900,
|
||||
"peak": 138100,
|
||||
"peak": 139000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 146,379 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1433848.3158772076,
|
||||
"last_add_price": 137500
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 115500,
|
||||
"qty": 81,
|
||||
"entry_price": 116833.33333333333,
|
||||
"entry_at": "2026-08-24T09:10:26.883582+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128400,
|
||||
"peak": 116200,
|
||||
"target": 133733,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4806914.700218748,
|
||||
"last_add_price": 115500
|
||||
"last_add_price": 118200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -25478064.18150006,
|
||||
@@ -213,5 +213,5 @@
|
||||
"194370": "2026-08-24"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583512+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.438615+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.188138+09:00"
|
||||
}
|
||||
@@ -259,3 +259,4 @@
|
||||
{"ts": "2026-08-24T09:04:22.503213+09:00", "side": "BUY", "code": "319660", "name": "피에스케이", "price": 125100, "qty": 38, "cost": 4754513, "path": "pullback", "reason": "눌림목 지정가 155,710 도달", "stop": 125099, "target": 125103, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "125,000 vs 112,642"}, {"label": "정배열(5>60)", "ok": true, "detail": "162,640 / 112,642"}, {"label": "추세 기울기(60일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "162,640 / 112,642"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+18.4%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +220 · 기 -194 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+54%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.69% (환산) vs 평균 2.68% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 196,500"}, {"label": "거래량 급증", "ok": false, "detail": "199,953 (환산) vs 평균 777,376"}, {"label": "회전율 급증", "ok": false, "detail": "0.69% (환산) vs 평균 2.68%"}, {"label": "RSI", "ok": true, "detail": "45"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 155,710 (현재 125,000)"}]}
|
||||
{"ts": "2026-08-24T09:10:26.883137+09:00", "side": "SELL", "code": "319660", "name": "피에스케이", "price": 123300, "qty": 38, "proceeds": 4676263, "entry_price": 125100, "pnl": -78250, "pnl_pct": -1.44, "hold_from": "2026-08-24T09:04:22.503211+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "125,099 (현재 122,800)"}, {"label": "목표/트레일링", "ok": null, "detail": "트레일링 ON"}, {"label": "추세(60일선)", "ok": true, "detail": "112,605"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +220 · 기 -194"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T09:10:26.883585+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 115500, "qty": 41, "cost": 4736210, "path": "pullback", "reason": "눌림목 지정가 115,212 도달", "stop": 111200, "target": 128400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "115,200 vs 109,938"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "추세 기울기(60일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+11.7%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +57 · 기 -54 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+33%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.04% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "61,480 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.04% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,212 (현재 115,200)"}]}
|
||||
{"ts": "2026-08-25T09:34:21.510487+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 40, "cost": 4728709, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,988"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 32815115.353499953,
|
||||
"cash": 25474814.473499954,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3546894.320639113,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 144,129 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1687768.019262798,
|
||||
"last_add_price": 147600
|
||||
@@ -76,12 +76,12 @@
|
||||
"entry_at": "2026-08-04T09:34:11.202398+09:00",
|
||||
"stop": 80334,
|
||||
"target": 125794,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3164409.8348090188,
|
||||
"last_add_price": 93500
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 104000,
|
||||
"cur_price": 106000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 3966630.036179657,
|
||||
"last_add_price": 110000
|
||||
@@ -118,12 +118,12 @@
|
||||
"entry_at": "2026-08-20T09:18:26.056475+09:00",
|
||||
"stop": 114576,
|
||||
"target": 187273,
|
||||
"peak": 143600,
|
||||
"peak": 150800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 143300,
|
||||
"cur_price": 149900,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2861643.9610576206,
|
||||
"last_add_price": 135100
|
||||
@@ -134,20 +134,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 115500,
|
||||
"qty": 81,
|
||||
"entry_price": 116833.33333333333,
|
||||
"entry_at": "2026-08-24T09:10:26.888722+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128400,
|
||||
"peak": 116200,
|
||||
"target": 133733,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4792299.770406247,
|
||||
"last_add_price": 115500
|
||||
"last_add_price": 118200
|
||||
},
|
||||
"257720": {
|
||||
"code": "257720",
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 53,
|
||||
"entry_price": 49450,
|
||||
"qty": 104,
|
||||
"entry_price": 50308.17307692308,
|
||||
"entry_at": "2026-08-24T14:20:24.329648+09:00",
|
||||
"stop": 42307,
|
||||
"target": 70880,
|
||||
"peak": 49650,
|
||||
"stop": 43093,
|
||||
"target": 71953,
|
||||
"peak": 52000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 49450,
|
||||
"tranches": 1,
|
||||
"cur_price": 51800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2646155.083218149,
|
||||
"last_add_price": 49450
|
||||
"last_add_price": 51200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -26792576.70900005,
|
||||
@@ -210,5 +210,5 @@
|
||||
"252990": "2026-08-21"
|
||||
},
|
||||
"created_at": "2026-06-15T14:40:00.621103+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.443658+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.192715+09:00"
|
||||
}
|
||||
@@ -265,3 +265,5 @@
|
||||
{"ts": "2026-08-24T13:14:24.912916+09:00", "side": "BUY", "code": "028260", "name": "삼성물산", "price": 362000, "qty": 13, "cost": 4706706, "path": "pullback", "reason": "눌림목 지정가 363,789 도달", "stop": 361999, "target": 362003, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "361,500 vs 350,292"}, {"label": "정배열(5>60)", "ok": true, "detail": "408,000 / 350,292"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "408,000 / 350,292"}, {"label": "상대강도(시장대비)", "ok": false, "detail": "-18.3%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -94 · 기 +139 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+37%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.41% (환산) vs 평균 0.43% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 565,000"}, {"label": "거래량 급증", "ok": false, "detail": "673,405 (환산) vs 평균 706,119"}, {"label": "회전율 급증", "ok": false, "detail": "0.41% (환산) vs 평균 0.43%"}, {"label": "RSI", "ok": true, "detail": "43"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 363,789 (현재 361,500)"}]}
|
||||
{"ts": "2026-08-24T14:20:24.328546+09:00", "side": "SELL", "code": "028260", "name": "삼성물산", "price": 361000, "qty": 13, "proceeds": 4683849, "entry_price": 362000, "pnl": -22857, "pnl_pct": -0.28, "hold_from": "2026-08-24T13:14:24.912913+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "361,999 (현재 361,500)"}, {"label": "목표/트레일링", "ok": null, "detail": "트레일링 ON"}, {"label": "추세(60일선)", "ok": true, "detail": "350,292"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -88 · 기 +122"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T14:20:24.329651+09:00", "side": "BUY", "code": "257720", "name": "실리콘투", "price": 49450, "qty": 53, "cost": 2621243, "path": "breakout", "reason": "거래량·회전율 동반 전고점 돌파", "stop": 42307, "target": 70880, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "49,350 vs 36,263"}, {"label": "정배열(5>60)", "ok": true, "detail": "43,660 / 36,263"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "43,660 / 36,263"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+34.4%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -645 · 기 +1,162 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+17%"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "4.50% (환산) vs 평균 1.56% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": true, "detail": "고점 48,250"}, {"label": "거래량 급증", "ok": true, "detail": "2,948,495 (환산) vs 평균 1,023,767"}, {"label": "회전율 급증", "ok": true, "detail": "4.50% (환산) vs 평균 1.56%"}, {"label": "RSI", "ok": true, "detail": "69"}]}
|
||||
{"ts": "2026-08-25T09:34:21.516054+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 40, "cost": 4728709, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,988"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,189"}]}
|
||||
{"ts": "2026-08-25T09:56:20.697239+09:00", "side": "BUY", "code": "257720", "name": "실리콘투", "price": 51200, "qty": 51, "cost": 2611592, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 42307, "target": 70880, "signals": [{"label": "손절선", "ok": true, "detail": "42,307 (현재 51,300)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 70,880"}, {"label": "추세(60일선)", "ok": true, "detail": "36,296"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -428 · 기 +1,106"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 51,254"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 28820210.76549997,
|
||||
"cash": 24119505.76549997,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3468191.0592600796,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 144,129 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1667469.2912320814,
|
||||
"last_add_price": 147600
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3059768.9052410396,
|
||||
"last_add_price": 13420
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4399987.769056675,
|
||||
"last_add_price": 198600
|
||||
@@ -118,12 +118,12 @@
|
||||
"entry_at": "2026-08-06T09:54:12.737219+09:00",
|
||||
"stop": 87209,
|
||||
"target": 137241,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3005090.6555779856,
|
||||
"last_add_price": 101700
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1248111.8252958565,
|
||||
"last_add_price": 35800
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 114500,
|
||||
"qty": 81,
|
||||
"entry_price": 115981.48148148147,
|
||||
"entry_at": "2026-08-24T13:12:24.633070+09:00",
|
||||
"stop": 111200,
|
||||
"target": 124400,
|
||||
"peak": 115500,
|
||||
"target": 130326,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,169 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4715894.6837812485,
|
||||
"last_add_price": 114500
|
||||
"last_add_price": 117500
|
||||
}
|
||||
},
|
||||
"realized_pnl": -26428777.589500055,
|
||||
@@ -212,5 +212,5 @@
|
||||
"138040": "2026-08-19"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583628+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.448033+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.197334+09:00"
|
||||
}
|
||||
@@ -244,3 +244,4 @@
|
||||
{"ts": "2026-08-20T09:16:25.175789+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 34, "cost": 1217383, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 64246, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 64,246"}, {"label": "추세(60일선)", "ok": true, "detail": "31,336"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T13:12:24.632854+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 563, "proceeds": 2826368, "entry_price": 6333, "pnl": -739597, "pnl_pct": -20.57, "hold_from": "2026-07-30T09:04:08.173280+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 10,215"}, {"label": "추세(60일선)", "ok": true, "detail": "4,848"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:24.633073+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 114500, "qty": 41, "cost": 4695204, "path": "pullback", "reason": "눌림목 지정가 115,169 도달", "stop": 111200, "target": 124400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "114,200 vs 109,922"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "추세 기울기(60일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+13.5%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +48 · 기 -50 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+34%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.12% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "197,995 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.12% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "52"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,169 (현재 114,200)"}]}
|
||||
{"ts": "2026-08-25T09:26:24.859608+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 117500, "qty": 40, "cost": 4700705, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 124400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 117,300)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 124,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,973"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 117,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 36393346.89199998,
|
||||
"cash": 31546419.961999983,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3570553.5376633056,
|
||||
"last_add_price": 136000
|
||||
@@ -55,12 +55,12 @@
|
||||
"entry_at": "2026-08-04T09:34:11.210973+09:00",
|
||||
"stop": 80360,
|
||||
"target": 125820,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3188827.7982604695,
|
||||
"last_add_price": 93500
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 104000,
|
||||
"cur_price": 106000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 3997085.960333706,
|
||||
"last_add_price": 110000
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 7,424 도달",
|
||||
"cur_price": 5060,
|
||||
"cur_price": 5040,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1576676.661387545,
|
||||
"last_add_price": 5560
|
||||
@@ -118,12 +118,12 @@
|
||||
"entry_at": "2026-08-20T09:18:26.065190+09:00",
|
||||
"stop": 114521,
|
||||
"target": 187219,
|
||||
"peak": 143600,
|
||||
"peak": 150800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 143300,
|
||||
"cur_price": 149900,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2892213.1259382935,
|
||||
"last_add_price": 135100
|
||||
@@ -139,12 +139,12 @@
|
||||
"entry_at": "2026-08-24T09:02:26.376579+09:00",
|
||||
"stop": 100700,
|
||||
"target": 247900,
|
||||
"peak": 138100,
|
||||
"peak": 139000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 146,379 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1444957.780421195,
|
||||
"last_add_price": 137500
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 42,
|
||||
"entry_price": 115500,
|
||||
"qty": 83,
|
||||
"entry_price": 116833.73493975903,
|
||||
"entry_at": "2026-08-24T09:10:26.897215+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128400,
|
||||
"peak": 116200,
|
||||
"target": 133735,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4852688.408874999,
|
||||
"last_add_price": 115500
|
||||
"last_add_price": 118200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -25731292.656000044,
|
||||
@@ -210,5 +210,5 @@
|
||||
"252990": "2026-08-21"
|
||||
},
|
||||
"created_at": "2026-06-15T14:40:00.621148+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.451894+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.201116+09:00"
|
||||
}
|
||||
@@ -265,3 +265,4 @@
|
||||
{"ts": "2026-08-24T09:04:22.515888+09:00", "side": "BUY", "code": "319660", "name": "피에스케이", "price": 125100, "qty": 38, "cost": 4754513, "path": "pullback", "reason": "눌림목 지정가 155,710 도달", "stop": 125099, "target": 125103, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "125,000 vs 112,642"}, {"label": "정배열(5>60)", "ok": true, "detail": "162,640 / 112,642"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "162,640 / 112,642"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+18.4%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +220 · 기 -194 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+54%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.69% (환산) vs 평균 2.68% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 196,500"}, {"label": "거래량 급증", "ok": false, "detail": "199,953 (환산) vs 평균 777,376"}, {"label": "회전율 급증", "ok": false, "detail": "0.69% (환산) vs 평균 2.68%"}, {"label": "RSI", "ok": true, "detail": "45"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 155,710 (현재 125,000)"}]}
|
||||
{"ts": "2026-08-24T09:10:26.896933+09:00", "side": "SELL", "code": "319660", "name": "피에스케이", "price": 123300, "qty": 38, "proceeds": 4676263, "entry_price": 125100, "pnl": -78250, "pnl_pct": -1.44, "hold_from": "2026-08-24T09:04:22.515886+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "125,099 (현재 122,800)"}, {"label": "목표/트레일링", "ok": null, "detail": "트레일링 ON"}, {"label": "추세(60일선)", "ok": true, "detail": "112,605"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +220 · 기 -194"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T09:10:26.897218+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 115500, "qty": 42, "cost": 4851728, "path": "pullback", "reason": "눌림목 지정가 115,212 도달", "stop": 111200, "target": 128400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "115,200 vs 109,938"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+11.7%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +57 · 기 -54 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+33%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.04% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "61,480 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.04% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,212 (현재 115,200)"}]}
|
||||
{"ts": "2026-08-25T09:34:21.524742+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 41, "cost": 4846927, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,988"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 35776524.87849997,
|
||||
"cash": 31047815.67849997,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3511790.845106854,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4454503.886577569,
|
||||
"last_add_price": 198600
|
||||
@@ -76,12 +76,12 @@
|
||||
"entry_at": "2026-08-06T09:54:12.744635+09:00",
|
||||
"stop": 87177,
|
||||
"target": 137209,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3042388.163482306,
|
||||
"last_add_price": 101700
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1267059.834384944,
|
||||
"last_add_price": 35800
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 7,418 도달",
|
||||
"cur_price": 5060,
|
||||
"cur_price": 5040,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1532132.3284042438,
|
||||
"last_add_price": 5460
|
||||
@@ -139,12 +139,12 @@
|
||||
"entry_at": "2026-08-24T09:02:26.381162+09:00",
|
||||
"stop": 100700,
|
||||
"target": 247900,
|
||||
"peak": 138100,
|
||||
"peak": 139000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 146,379 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1433848.3158772076,
|
||||
"last_add_price": 137500
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 115500,
|
||||
"qty": 81,
|
||||
"entry_price": 116833.33333333333,
|
||||
"entry_at": "2026-08-24T09:10:26.900987+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128400,
|
||||
"peak": 116200,
|
||||
"target": 133733,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4806914.700218748,
|
||||
"last_add_price": 115500
|
||||
"last_add_price": 118200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -25478064.18150006,
|
||||
@@ -213,5 +213,5 @@
|
||||
"194370": "2026-08-24"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.583751+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.455797+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.205159+09:00"
|
||||
}
|
||||
@@ -259,3 +259,4 @@
|
||||
{"ts": "2026-08-24T09:04:22.519513+09:00", "side": "BUY", "code": "319660", "name": "피에스케이", "price": 125100, "qty": 38, "cost": 4754513, "path": "pullback", "reason": "눌림목 지정가 155,710 도달", "stop": 125099, "target": 125103, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "125,000 vs 112,642"}, {"label": "정배열(5>60)", "ok": true, "detail": "162,640 / 112,642"}, {"label": "추세 기울기(60일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "162,640 / 112,642"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+18.4%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +220 · 기 -194 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+54%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.69% (환산) vs 평균 2.68% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 196,500"}, {"label": "거래량 급증", "ok": false, "detail": "199,953 (환산) vs 평균 777,376"}, {"label": "회전율 급증", "ok": false, "detail": "0.69% (환산) vs 평균 2.68%"}, {"label": "RSI", "ok": true, "detail": "45"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 155,710 (현재 125,000)"}]}
|
||||
{"ts": "2026-08-24T09:10:26.900690+09:00", "side": "SELL", "code": "319660", "name": "피에스케이", "price": 123300, "qty": 38, "proceeds": 4676263, "entry_price": 125100, "pnl": -78250, "pnl_pct": -1.44, "hold_from": "2026-08-24T09:04:22.519511+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "125,099 (현재 122,800)"}, {"label": "목표/트레일링", "ok": null, "detail": "트레일링 ON"}, {"label": "추세(60일선)", "ok": true, "detail": "112,605"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +220 · 기 -194"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T09:10:26.900990+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 115500, "qty": 41, "cost": 4736210, "path": "pullback", "reason": "눌림목 지정가 115,212 도달", "stop": 111200, "target": 128400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "115,200 vs 109,938"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "추세 기울기(60일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+11.7%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +57 · 기 -54 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+33%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.04% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "61,480 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.04% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,212 (현재 115,200)"}]}
|
||||
{"ts": "2026-08-25T09:34:21.528690+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 40, "cost": 4728709, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,988"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 33214270.101999976,
|
||||
"cash": 28485560.901999976,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3570553.5376633056,
|
||||
"last_add_price": 136000
|
||||
@@ -55,12 +55,12 @@
|
||||
"entry_at": "2026-08-04T09:34:11.218304+09:00",
|
||||
"stop": 80360,
|
||||
"target": 125820,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3188827.7982604695,
|
||||
"last_add_price": 93500
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 104000,
|
||||
"cur_price": 106000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 3997085.960333706,
|
||||
"last_add_price": 110000
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 7,424 도달",
|
||||
"cur_price": 5060,
|
||||
"cur_price": 5040,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1576676.661387545,
|
||||
"last_add_price": 5560
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 455500,
|
||||
"cur_price": 450000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4892847.491999999,
|
||||
"last_add_price": 454500
|
||||
@@ -139,12 +139,12 @@
|
||||
"entry_at": "2026-08-24T09:02:26.385356+09:00",
|
||||
"stop": 100700,
|
||||
"target": 247900,
|
||||
"peak": 138100,
|
||||
"peak": 139000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 146,379 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1437632.3116457197,
|
||||
"last_add_price": 137500
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 115500,
|
||||
"qty": 81,
|
||||
"entry_price": 116833.33333333333,
|
||||
"entry_at": "2026-08-24T09:10:26.904727+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128400,
|
||||
"peak": 116200,
|
||||
"target": 133733,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4827275.136062499,
|
||||
"last_add_price": 115500
|
||||
"last_add_price": 118200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -25731292.656000044,
|
||||
@@ -210,5 +210,5 @@
|
||||
"252990": "2026-08-21"
|
||||
},
|
||||
"created_at": "2026-06-15T14:40:00.621367+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.459217+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.208661+09:00"
|
||||
}
|
||||
@@ -265,3 +265,4 @@
|
||||
{"ts": "2026-08-24T09:10:26.904453+09:00", "side": "SELL", "code": "319660", "name": "피에스케이", "price": 123300, "qty": 38, "proceeds": 4676263, "entry_price": 125100, "pnl": -78250, "pnl_pct": -1.44, "hold_from": "2026-08-24T09:04:22.522917+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "125,099 (현재 122,800)"}, {"label": "목표/트레일링", "ok": null, "detail": "트레일링 ON"}, {"label": "추세(60일선)", "ok": true, "detail": "112,605"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +220 · 기 -194"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T09:10:26.904729+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 115500, "qty": 41, "cost": 4736210, "path": "pullback", "reason": "눌림목 지정가 115,212 도달", "stop": 111200, "target": 128400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "115,200 vs 109,938"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+11.7%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +57 · 기 -54 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+33%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.04% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "61,480 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.04% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,212 (현재 115,200)"}]}
|
||||
{"ts": "2026-08-24T14:56:24.306900+09:00", "side": "BUY", "code": "004370", "name": "농심", "price": 454500, "qty": 10, "cost": 4545682, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 411924, "target": 546227, "signals": [{"label": "손절선", "ok": true, "detail": "411,924 (현재 454,500)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 546,227"}, {"label": "추세(60일선)", "ok": true, "detail": "370,633"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +23 · 기 +24"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 454,037"}]}
|
||||
{"ts": "2026-08-25T09:34:21.532431+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 40, "cost": 4728709, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,988"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 35776524.87849997,
|
||||
"cash": 31047815.67849997,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3511790.845106854,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4454503.886577569,
|
||||
"last_add_price": 198600
|
||||
@@ -76,12 +76,12 @@
|
||||
"entry_at": "2026-08-06T09:54:12.751274+09:00",
|
||||
"stop": 87177,
|
||||
"target": 137209,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3042388.163482306,
|
||||
"last_add_price": 101700
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1267059.834384944,
|
||||
"last_add_price": 35800
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 7,418 도달",
|
||||
"cur_price": 5060,
|
||||
"cur_price": 5040,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1532132.3284042438,
|
||||
"last_add_price": 5460
|
||||
@@ -139,12 +139,12 @@
|
||||
"entry_at": "2026-08-24T09:02:26.389383+09:00",
|
||||
"stop": 100700,
|
||||
"target": 247900,
|
||||
"peak": 138100,
|
||||
"peak": 139000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 146,379 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1433848.3158772076,
|
||||
"last_add_price": 137500
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 115500,
|
||||
"qty": 81,
|
||||
"entry_price": 116833.33333333333,
|
||||
"entry_at": "2026-08-24T09:10:26.908095+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128400,
|
||||
"peak": 116200,
|
||||
"target": 133733,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4806914.700218748,
|
||||
"last_add_price": 115500
|
||||
"last_add_price": 118200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -25478064.18150006,
|
||||
@@ -213,5 +213,5 @@
|
||||
"194370": "2026-08-24"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.584337+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.462464+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.212052+09:00"
|
||||
}
|
||||
@@ -259,3 +259,4 @@
|
||||
{"ts": "2026-08-24T09:04:22.526222+09:00", "side": "BUY", "code": "319660", "name": "피에스케이", "price": 125100, "qty": 38, "cost": 4754513, "path": "pullback", "reason": "눌림목 지정가 155,710 도달", "stop": 125099, "target": 125103, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "125,000 vs 112,642"}, {"label": "정배열(5>60)", "ok": true, "detail": "162,640 / 112,642"}, {"label": "추세 기울기(60일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "162,640 / 112,642"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+18.4%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +220 · 기 -194 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+54%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.69% (환산) vs 평균 2.68% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 196,500"}, {"label": "거래량 급증", "ok": false, "detail": "199,953 (환산) vs 평균 777,376"}, {"label": "회전율 급증", "ok": false, "detail": "0.69% (환산) vs 평균 2.68%"}, {"label": "RSI", "ok": true, "detail": "45"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 155,710 (현재 125,000)"}]}
|
||||
{"ts": "2026-08-24T09:10:26.907834+09:00", "side": "SELL", "code": "319660", "name": "피에스케이", "price": 123300, "qty": 38, "proceeds": 4676263, "entry_price": 125100, "pnl": -78250, "pnl_pct": -1.44, "hold_from": "2026-08-24T09:04:22.526221+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "125,099 (현재 122,800)"}, {"label": "목표/트레일링", "ok": null, "detail": "트레일링 ON"}, {"label": "추세(60일선)", "ok": true, "detail": "112,605"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +220 · 기 -194"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T09:10:26.908097+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 115500, "qty": 41, "cost": 4736210, "path": "pullback", "reason": "눌림목 지정가 115,212 도달", "stop": 111200, "target": 128400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "115,200 vs 109,938"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "추세 기울기(60일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+11.7%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +57 · 기 -54 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+33%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.04% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "61,480 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.04% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,212 (현재 115,200)"}]}
|
||||
{"ts": "2026-08-25T09:34:21.536178+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 40, "cost": 4728709, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,988"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 33214270.101999976,
|
||||
"cash": 28485560.901999976,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3570553.5376633056,
|
||||
"last_add_price": 136000
|
||||
@@ -55,12 +55,12 @@
|
||||
"entry_at": "2026-08-04T09:34:11.224674+09:00",
|
||||
"stop": 80360,
|
||||
"target": 125820,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3188827.7982604695,
|
||||
"last_add_price": 93500
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 104000,
|
||||
"cur_price": 106000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 3997085.960333706,
|
||||
"last_add_price": 110000
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 7,424 도달",
|
||||
"cur_price": 5060,
|
||||
"cur_price": 5040,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1576676.661387545,
|
||||
"last_add_price": 5560
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 455500,
|
||||
"cur_price": 450000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4892847.491999999,
|
||||
"last_add_price": 454500
|
||||
@@ -139,12 +139,12 @@
|
||||
"entry_at": "2026-08-24T09:02:26.392950+09:00",
|
||||
"stop": 100700,
|
||||
"target": 247900,
|
||||
"peak": 138100,
|
||||
"peak": 139000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 146,379 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1437632.3116457197,
|
||||
"last_add_price": 137500
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 115500,
|
||||
"qty": 81,
|
||||
"entry_price": 116833.33333333333,
|
||||
"entry_at": "2026-08-24T09:10:26.911317+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128400,
|
||||
"peak": 116200,
|
||||
"target": 133733,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4827275.136062499,
|
||||
"last_add_price": 115500
|
||||
"last_add_price": 118200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -25731292.656000044,
|
||||
@@ -210,5 +210,5 @@
|
||||
"252990": "2026-08-21"
|
||||
},
|
||||
"created_at": "2026-06-15T14:40:00.621411+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.465537+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.215070+09:00"
|
||||
}
|
||||
@@ -265,3 +265,4 @@
|
||||
{"ts": "2026-08-24T09:10:26.911079+09:00", "side": "SELL", "code": "319660", "name": "피에스케이", "price": 123300, "qty": 38, "proceeds": 4676263, "entry_price": 125100, "pnl": -78250, "pnl_pct": -1.44, "hold_from": "2026-08-24T09:04:22.529501+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "125,099 (현재 122,800)"}, {"label": "목표/트레일링", "ok": null, "detail": "트레일링 ON"}, {"label": "추세(60일선)", "ok": true, "detail": "112,605"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +220 · 기 -194"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T09:10:26.911319+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 115500, "qty": 41, "cost": 4736210, "path": "pullback", "reason": "눌림목 지정가 115,212 도달", "stop": 111200, "target": 128400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "115,200 vs 109,938"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+11.7%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +57 · 기 -54 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+33%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.04% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "61,480 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.04% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,212 (현재 115,200)"}]}
|
||||
{"ts": "2026-08-24T14:56:24.313261+09:00", "side": "BUY", "code": "004370", "name": "농심", "price": 454500, "qty": 10, "cost": 4545682, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 411924, "target": 546227, "signals": [{"label": "손절선", "ok": true, "detail": "411,924 (현재 454,500)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 546,227"}, {"label": "추세(60일선)", "ok": true, "detail": "370,633"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +23 · 기 +24"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 454,037"}]}
|
||||
{"ts": "2026-08-25T09:34:21.539734+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 40, "cost": 4728709, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,988"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 35776524.87849997,
|
||||
"cash": 31047815.67849997,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3511790.845106854,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4454503.886577569,
|
||||
"last_add_price": 198600
|
||||
@@ -76,12 +76,12 @@
|
||||
"entry_at": "2026-08-06T09:54:12.757233+09:00",
|
||||
"stop": 87177,
|
||||
"target": 137209,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3042388.163482306,
|
||||
"last_add_price": 101700
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1267059.834384944,
|
||||
"last_add_price": 35800
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 7,418 도달",
|
||||
"cur_price": 5060,
|
||||
"cur_price": 5040,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1532132.3284042438,
|
||||
"last_add_price": 5460
|
||||
@@ -139,12 +139,12 @@
|
||||
"entry_at": "2026-08-24T09:02:26.396294+09:00",
|
||||
"stop": 100700,
|
||||
"target": 247900,
|
||||
"peak": 138100,
|
||||
"peak": 139000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 146,379 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1433848.3158772076,
|
||||
"last_add_price": 137500
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 115500,
|
||||
"qty": 81,
|
||||
"entry_price": 116833.33333333333,
|
||||
"entry_at": "2026-08-24T09:10:26.914383+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128400,
|
||||
"peak": 116200,
|
||||
"target": 133733,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4806914.700218748,
|
||||
"last_add_price": 115500
|
||||
"last_add_price": 118200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -25478064.18150006,
|
||||
@@ -213,5 +213,5 @@
|
||||
"194370": "2026-08-24"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.584451+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.468672+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.218270+09:00"
|
||||
}
|
||||
@@ -259,3 +259,4 @@
|
||||
{"ts": "2026-08-24T09:04:22.532506+09:00", "side": "BUY", "code": "319660", "name": "피에스케이", "price": 125100, "qty": 38, "cost": 4754513, "path": "pullback", "reason": "눌림목 지정가 155,710 도달", "stop": 125099, "target": 125103, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "125,000 vs 112,642"}, {"label": "정배열(5>60)", "ok": true, "detail": "162,640 / 112,642"}, {"label": "추세 기울기(60일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "162,640 / 112,642"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+18.4%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +220 · 기 -194 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+54%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.69% (환산) vs 평균 2.68% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 196,500"}, {"label": "거래량 급증", "ok": false, "detail": "199,953 (환산) vs 평균 777,376"}, {"label": "회전율 급증", "ok": false, "detail": "0.69% (환산) vs 평균 2.68%"}, {"label": "RSI", "ok": true, "detail": "45"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 155,710 (현재 125,000)"}]}
|
||||
{"ts": "2026-08-24T09:10:26.914157+09:00", "side": "SELL", "code": "319660", "name": "피에스케이", "price": 123300, "qty": 38, "proceeds": 4676263, "entry_price": 125100, "pnl": -78250, "pnl_pct": -1.44, "hold_from": "2026-08-24T09:04:22.532505+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "125,099 (현재 122,800)"}, {"label": "목표/트레일링", "ok": null, "detail": "트레일링 ON"}, {"label": "추세(60일선)", "ok": true, "detail": "112,605"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +220 · 기 -194"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T09:10:26.914385+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 115500, "qty": 41, "cost": 4736210, "path": "pullback", "reason": "눌림목 지정가 115,212 도달", "stop": 111200, "target": 128400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "115,200 vs 109,938"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "추세 기울기(60일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,500 / 109,938"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+11.7%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +57 · 기 -54 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+33%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.04% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "61,480 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.04% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "53"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,212 (현재 115,200)"}]}
|
||||
{"ts": "2026-08-25T09:34:21.543108+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 40, "cost": 4728709, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,988"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 29390101.678499952,
|
||||
"cash": 22049800.798499953,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3546894.320639113,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 144,129 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1687768.019262798,
|
||||
"last_add_price": 147600
|
||||
@@ -76,12 +76,12 @@
|
||||
"entry_at": "2026-08-04T09:34:11.230485+09:00",
|
||||
"stop": 80334,
|
||||
"target": 125794,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3164409.8348090188,
|
||||
"last_add_price": 93500
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 104000,
|
||||
"cur_price": 106000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 3966630.036179657,
|
||||
"last_add_price": 110000
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 455500,
|
||||
"cur_price": 450000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4841386.2271249965,
|
||||
"last_add_price": 454500
|
||||
@@ -134,20 +134,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 115500,
|
||||
"qty": 81,
|
||||
"entry_price": 116833.33333333333,
|
||||
"entry_at": "2026-08-24T09:10:26.917445+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128400,
|
||||
"peak": 116200,
|
||||
"target": 133733,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4767416.525093747,
|
||||
"last_add_price": 115500
|
||||
"last_add_price": 118200
|
||||
},
|
||||
"257720": {
|
||||
"code": "257720",
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 53,
|
||||
"entry_price": 49450,
|
||||
"qty": 104,
|
||||
"entry_price": 50308.17307692308,
|
||||
"entry_at": "2026-08-24T14:20:24.358532+09:00",
|
||||
"stop": 42307,
|
||||
"target": 70880,
|
||||
"peak": 49650,
|
||||
"stop": 43093,
|
||||
"target": 71953,
|
||||
"peak": 52000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 49450,
|
||||
"tranches": 1,
|
||||
"cur_price": 51800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2633211.6885021683,
|
||||
"last_add_price": 49450
|
||||
"last_add_price": 51200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -26792576.70900005,
|
||||
@@ -210,5 +210,5 @@
|
||||
"252990": "2026-08-21"
|
||||
},
|
||||
"created_at": "2026-06-15T14:40:00.621453+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.471448+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.221057+09:00"
|
||||
}
|
||||
@@ -265,3 +265,5 @@
|
||||
{"ts": "2026-08-24T14:20:24.357966+09:00", "side": "SELL", "code": "028260", "name": "삼성물산", "price": 361000, "qty": 13, "proceeds": 4683849, "entry_price": 362000, "pnl": -22857, "pnl_pct": -0.28, "hold_from": "2026-08-24T13:14:24.941038+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "361,999 (현재 361,500)"}, {"label": "목표/트레일링", "ok": null, "detail": "트레일링 ON"}, {"label": "추세(60일선)", "ok": true, "detail": "350,292"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -88 · 기 +122"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T14:20:24.358534+09:00", "side": "BUY", "code": "257720", "name": "실리콘투", "price": 49450, "qty": 53, "cost": 2621243, "path": "breakout", "reason": "거래량·회전율 동반 전고점 돌파", "stop": 42307, "target": 70880, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "49,350 vs 36,263"}, {"label": "정배열(5>60)", "ok": true, "detail": "43,660 / 36,263"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "43,660 / 36,263"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+34.4%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -645 · 기 +1,162 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+17%"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "4.50% (환산) vs 평균 1.56% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": true, "detail": "고점 48,250"}, {"label": "거래량 급증", "ok": true, "detail": "2,948,495 (환산) vs 평균 1,023,767"}, {"label": "회전율 급증", "ok": true, "detail": "4.50% (환산) vs 평균 1.56%"}, {"label": "RSI", "ok": true, "detail": "69"}]}
|
||||
{"ts": "2026-08-24T14:56:24.319167+09:00", "side": "BUY", "code": "004370", "name": "농심", "price": 454500, "qty": 10, "cost": 4545682, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 411924, "target": 546227, "signals": [{"label": "손절선", "ok": true, "detail": "411,924 (현재 454,500)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 546,227"}, {"label": "추세(60일선)", "ok": true, "detail": "370,633"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +23 · 기 +24"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 454,037"}]}
|
||||
{"ts": "2026-08-25T09:34:21.546145+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 40, "cost": 4728709, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,988"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,189"}]}
|
||||
{"ts": "2026-08-25T09:56:20.725061+09:00", "side": "BUY", "code": "257720", "name": "실리콘투", "price": 51200, "qty": 51, "cost": 2611592, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 42307, "target": 70880, "signals": [{"label": "손절선", "ok": true, "detail": "42,307 (현재 51,300)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 70,880"}, {"label": "추세(60일선)", "ok": true, "detail": "36,296"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -428 · 기 +1,106"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 51,254"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 28820210.76549997,
|
||||
"cash": 24119505.76549997,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3468191.0592600796,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 144,129 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1667469.2912320814,
|
||||
"last_add_price": 147600
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3059768.9052410396,
|
||||
"last_add_price": 13420
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4399987.769056675,
|
||||
"last_add_price": 198600
|
||||
@@ -118,12 +118,12 @@
|
||||
"entry_at": "2026-08-06T09:54:12.762770+09:00",
|
||||
"stop": 87209,
|
||||
"target": 137241,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3005090.6555779856,
|
||||
"last_add_price": 101700
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1248111.8252958565,
|
||||
"last_add_price": 35800
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 114500,
|
||||
"qty": 81,
|
||||
"entry_price": 115981.48148148147,
|
||||
"entry_at": "2026-08-24T13:12:24.658985+09:00",
|
||||
"stop": 111200,
|
||||
"target": 124400,
|
||||
"peak": 115500,
|
||||
"target": 130326,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,169 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4715894.6837812485,
|
||||
"last_add_price": 114500
|
||||
"last_add_price": 117500
|
||||
}
|
||||
},
|
||||
"realized_pnl": -26428777.589500055,
|
||||
@@ -212,5 +212,5 @@
|
||||
"138040": "2026-08-19"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.584565+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.474210+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.223890+09:00"
|
||||
}
|
||||
@@ -244,3 +244,4 @@
|
||||
{"ts": "2026-08-20T09:16:25.201665+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 34, "cost": 1217383, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 64246, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 64,246"}, {"label": "추세(60일선)", "ok": true, "detail": "31,336"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T13:12:24.658858+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 563, "proceeds": 2826368, "entry_price": 6333, "pnl": -739597, "pnl_pct": -20.57, "hold_from": "2026-07-30T09:04:08.202607+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 10,215"}, {"label": "추세(60일선)", "ok": true, "detail": "4,848"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:24.658986+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 114500, "qty": 41, "cost": 4695204, "path": "pullback", "reason": "눌림목 지정가 115,169 도달", "stop": 111200, "target": 124400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "114,200 vs 109,922"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "추세 기울기(60일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+13.5%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +48 · 기 -50 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+34%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.12% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "197,995 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.12% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "52"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,169 (현재 114,200)"}]}
|
||||
{"ts": "2026-08-25T09:26:24.888764+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 117500, "qty": 40, "cost": 4700705, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 124400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 117,300)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 124,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,973"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 117,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 29390101.678499952,
|
||||
"cash": 22049800.798499953,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3546894.320639113,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 144,129 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1687768.019262798,
|
||||
"last_add_price": 147600
|
||||
@@ -76,12 +76,12 @@
|
||||
"entry_at": "2026-08-04T09:34:11.235950+09:00",
|
||||
"stop": 80334,
|
||||
"target": 125794,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3164409.8348090188,
|
||||
"last_add_price": 93500
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 104000,
|
||||
"cur_price": 106000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 3966630.036179657,
|
||||
"last_add_price": 110000
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 455500,
|
||||
"cur_price": 450000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4841386.2271249965,
|
||||
"last_add_price": 454500
|
||||
@@ -134,20 +134,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 115500,
|
||||
"qty": 81,
|
||||
"entry_price": 116833.33333333333,
|
||||
"entry_at": "2026-08-24T09:10:26.922902+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128400,
|
||||
"peak": 116200,
|
||||
"target": 133733,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,212 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4767416.525093747,
|
||||
"last_add_price": 115500
|
||||
"last_add_price": 118200
|
||||
},
|
||||
"257720": {
|
||||
"code": "257720",
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 53,
|
||||
"entry_price": 49450,
|
||||
"qty": 104,
|
||||
"entry_price": 50308.17307692308,
|
||||
"entry_at": "2026-08-24T14:20:24.364356+09:00",
|
||||
"stop": 42307,
|
||||
"target": 70880,
|
||||
"peak": 49650,
|
||||
"stop": 43093,
|
||||
"target": 71953,
|
||||
"peak": 52000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 49450,
|
||||
"tranches": 1,
|
||||
"cur_price": 51800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2633211.6885021683,
|
||||
"last_add_price": 49450
|
||||
"last_add_price": 51200
|
||||
}
|
||||
},
|
||||
"realized_pnl": -26792576.70900005,
|
||||
@@ -210,5 +210,5 @@
|
||||
"252990": "2026-08-21"
|
||||
},
|
||||
"created_at": "2026-06-15T14:40:00.621497+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.476766+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.226415+09:00"
|
||||
}
|
||||
@@ -265,3 +265,5 @@
|
||||
{"ts": "2026-08-24T14:20:24.363800+09:00", "side": "SELL", "code": "028260", "name": "삼성물산", "price": 361000, "qty": 13, "proceeds": 4683849, "entry_price": 362000, "pnl": -22857, "pnl_pct": -0.28, "hold_from": "2026-08-24T13:14:24.946498+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "361,999 (현재 361,500)"}, {"label": "목표/트레일링", "ok": null, "detail": "트레일링 ON"}, {"label": "추세(60일선)", "ok": true, "detail": "350,292"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -88 · 기 +122"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T14:20:24.364357+09:00", "side": "BUY", "code": "257720", "name": "실리콘투", "price": 49450, "qty": 53, "cost": 2621243, "path": "breakout", "reason": "거래량·회전율 동반 전고점 돌파", "stop": 42307, "target": 70880, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "49,350 vs 36,263"}, {"label": "정배열(5>60)", "ok": true, "detail": "43,660 / 36,263"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "43,660 / 36,263"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+34.4%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -645 · 기 +1,162 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+17%"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "4.50% (환산) vs 평균 1.56% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": true, "detail": "고점 48,250"}, {"label": "거래량 급증", "ok": true, "detail": "2,948,495 (환산) vs 평균 1,023,767"}, {"label": "회전율 급증", "ok": true, "detail": "4.50% (환산) vs 평균 1.56%"}, {"label": "RSI", "ok": true, "detail": "69"}]}
|
||||
{"ts": "2026-08-24T14:56:24.324495+09:00", "side": "BUY", "code": "004370", "name": "농심", "price": 454500, "qty": 10, "cost": 4545682, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 411924, "target": 546227, "signals": [{"label": "손절선", "ok": true, "detail": "411,924 (현재 454,500)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 546,227"}, {"label": "추세(60일선)", "ok": true, "detail": "370,633"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +23 · 기 +24"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 454,037"}]}
|
||||
{"ts": "2026-08-25T09:34:21.551758+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 40, "cost": 4728709, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,988"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,189"}]}
|
||||
{"ts": "2026-08-25T09:56:20.730371+09:00", "side": "BUY", "code": "257720", "name": "실리콘투", "price": 51200, "qty": 51, "cost": 2611592, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 42307, "target": 70880, "signals": [{"label": "손절선", "ok": true, "detail": "42,307 (현재 51,300)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 70,880"}, {"label": "추세(60일선)", "ok": true, "detail": "36,296"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -428 · 기 +1,106"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 51,254"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 28820210.76549997,
|
||||
"cash": 24119505.76549997,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"214450": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3624744.3180141365,
|
||||
"last_add_price": 356000
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3468191.0592600796,
|
||||
"last_add_price": 136000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 144,129 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1667469.2912320814,
|
||||
"last_add_price": 147600
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3059768.9052410396,
|
||||
"last_add_price": 13420
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4399987.769056675,
|
||||
"last_add_price": 198600
|
||||
@@ -118,12 +118,12 @@
|
||||
"entry_at": "2026-08-06T09:54:12.767767+09:00",
|
||||
"stop": 87209,
|
||||
"target": 137241,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3005090.6555779856,
|
||||
"last_add_price": 101700
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,138 도달",
|
||||
"cur_price": 34500,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 1248111.8252958565,
|
||||
"last_add_price": 35800
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 41,
|
||||
"entry_price": 114500,
|
||||
"qty": 81,
|
||||
"entry_price": 115981.48148148147,
|
||||
"entry_at": "2026-08-24T13:12:24.664434+09:00",
|
||||
"stop": 111200,
|
||||
"target": 124400,
|
||||
"peak": 115500,
|
||||
"target": 130326,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,169 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4715894.6837812485,
|
||||
"last_add_price": 114500
|
||||
"last_add_price": 117500
|
||||
}
|
||||
},
|
||||
"realized_pnl": -26428777.589500055,
|
||||
@@ -212,5 +212,5 @@
|
||||
"138040": "2026-08-19"
|
||||
},
|
||||
"created_at": "2026-06-15T14:55:00.584680+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.479306+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.229025+09:00"
|
||||
}
|
||||
@@ -244,3 +244,4 @@
|
||||
{"ts": "2026-08-20T09:16:25.206940+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35800, "qty": 34, "cost": 1217383, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 23118, "target": 64246, "signals": [{"label": "손절선", "ok": true, "detail": "23,118 (현재 36,000)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 64,246"}, {"label": "추세(60일선)", "ok": true, "detail": "31,336"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -71 · 기 +7"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,971"}]}
|
||||
{"ts": "2026-08-24T13:12:24.664301+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 563, "proceeds": 2826368, "entry_price": 6333, "pnl": -739597, "pnl_pct": -20.57, "hold_from": "2026-07-30T09:04:08.208371+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 10,215"}, {"label": "추세(60일선)", "ok": true, "detail": "4,848"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:24.664435+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 114500, "qty": 41, "cost": 4695204, "path": "pullback", "reason": "눌림목 지정가 115,169 도달", "stop": 111200, "target": 124400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "114,200 vs 109,922"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "추세 기울기(60일선↑)", "ok": null, "detail": "게이트 끔(완화) · 실제 우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+13.5%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +48 · 기 -50 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+34%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.12% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "197,995 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.12% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "52"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,169 (현재 114,200)"}]}
|
||||
{"ts": "2026-08-25T09:26:24.894551+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 117500, "qty": 40, "cost": 4700705, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 124400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 117,300)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 124,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,973"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 117,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 28780366.011999987,
|
||||
"cash": 23962143.386999987,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"055550": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 104000,
|
||||
"cur_price": 106000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4371332.168068898,
|
||||
"last_add_price": 105800
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3448434.606240806,
|
||||
"last_add_price": 130800
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3417673.0117698065,
|
||||
"last_add_price": 338000
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4425860.654266699,
|
||||
"last_add_price": 125200
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 228500,
|
||||
"cur_price": 225500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2243002.5784807494,
|
||||
"last_add_price": 244500
|
||||
@@ -118,12 +118,12 @@
|
||||
"entry_at": "2026-08-18T14:30:24.610130+09:00",
|
||||
"stop": 113273,
|
||||
"target": 185342,
|
||||
"peak": 143600,
|
||||
"peak": 150800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 143300,
|
||||
"cur_price": 149900,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2796943.490474186,
|
||||
"last_add_price": 133800
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 147,729 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 1,
|
||||
"tranche_value": 1467735.2954887256,
|
||||
"last_add_price": 146300
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 42,
|
||||
"entry_price": 114500,
|
||||
"qty": 83,
|
||||
"entry_price": 115981.92771084337,
|
||||
"entry_at": "2026-08-24T13:12:24.667052+09:00",
|
||||
"stop": 111200,
|
||||
"target": 124400,
|
||||
"peak": 115500,
|
||||
"target": 130328,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,169 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4876724.210124999,
|
||||
"last_add_price": 114500
|
||||
"last_add_price": 117500
|
||||
}
|
||||
},
|
||||
"realized_pnl": -24387110.163000036,
|
||||
@@ -221,5 +221,5 @@
|
||||
"003680": "2026-08-24"
|
||||
},
|
||||
"created_at": "2026-06-16T09:00:03.113416+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.481722+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.231406+09:00"
|
||||
}
|
||||
@@ -186,3 +186,4 @@
|
||||
{"ts": "2026-08-20T09:30:25.177105+09:00", "side": "BUY", "code": "161890", "name": "한국콜마", "price": 133800, "qty": 20, "cost": 2676401, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 110654, "target": 183638, "signals": [{"label": "손절선", "ok": true, "detail": "110,654 (현재 133,900)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 183,638"}, {"label": "추세(60일선)", "ok": true, "detail": "92,498"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +529 · 기 -188"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 133,404"}]}
|
||||
{"ts": "2026-08-24T13:12:24.666923+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 544, "proceeds": 2730984, "entry_price": 6333, "pnl": -714713, "pnl_pct": -20.58, "hold_from": "2026-07-30T09:04:08.798504+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 10,215"}, {"label": "추세(60일선)", "ok": true, "detail": "4,848"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:24.667054+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 114500, "qty": 42, "cost": 4809721, "path": "pullback", "reason": "눌림목 지정가 115,169 도달", "stop": 111200, "target": 124400, "signals": [{"label": "추세(60일선 위)", "ok": true, "detail": "114,200 vs 109,922"}, {"label": "정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "추세 기울기(60일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+13.5%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +48 · 기 -50 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+34%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.12% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "197,995 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.12% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "52"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 115,169 (현재 114,200)"}]}
|
||||
{"ts": "2026-08-25T09:26:24.897394+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 117500, "qty": 41, "cost": 4818223, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 124400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 117,300)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 124,400"}, {"label": "추세(60일선)", "ok": true, "detail": "109,973"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 117,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 895265.3689999636,
|
||||
"cash": 62840.52399996354,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"055550": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 104000,
|
||||
"cur_price": 106000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5402457.352781249,
|
||||
"last_add_price": 104900
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4939827.041843749,
|
||||
"last_add_price": 130800
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4843602.718718749,
|
||||
"last_add_price": 338000
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 128800,
|
||||
"cur_price": 130800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4504266.321593748,
|
||||
"last_add_price": 125200
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 115000,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4268049.827468748,
|
||||
"last_add_price": 122900
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4370285.517874998,
|
||||
"last_add_price": 195400
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3257573.4203829807,
|
||||
"last_add_price": 14290
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 38,
|
||||
"entry_price": 114400,
|
||||
"qty": 45,
|
||||
"entry_price": 115100.0,
|
||||
"entry_at": "2026-08-24T13:12:24.704448+09:00",
|
||||
"stop": 99078,
|
||||
"target": 160366,
|
||||
"peak": 115100,
|
||||
"stop": 100149,
|
||||
"target": 159952,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 114000,
|
||||
"tranches": 1,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4349667.340562498,
|
||||
"last_add_price": 114400
|
||||
"last_add_price": 118900
|
||||
}
|
||||
},
|
||||
"realized_pnl": -33040066.417000026,
|
||||
@@ -202,5 +202,5 @@
|
||||
"090850": "2026-08-06"
|
||||
},
|
||||
"created_at": "2026-06-16T09:00:03.113433+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.484165+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.233742+09:00"
|
||||
}
|
||||
@@ -101,3 +101,4 @@
|
||||
{"ts": "2026-08-07T09:02:15.667162+09:00", "side": "BUY", "code": "252990", "name": "샘씨엔에스", "price": 14290, "qty": 208, "cost": 2972766, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 10340, "target": 21740, "signals": [{"label": "손절선", "ok": true, "detail": "10,340 (현재 14,240)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 21,740"}, {"label": "추세(10일선)", "ok": true, "detail": "12,997"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -216 · 기 +313"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 13,936"}]}
|
||||
{"ts": "2026-08-24T13:12:24.669403+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 1042, "proceeds": 5231040, "entry_price": 6333, "pnl": -1369090, "pnl_pct": -20.58, "hold_from": "2026-07-30T09:04:08.809546+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 10,215"}, {"label": "추세(10일선)", "ok": false, "detail": "5,148"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:24.704450+09:00", "side": "BUY", "code": "078930", "name": "GS", "price": 114400, "qty": 38, "cost": 4347852, "path": "immediate", "reason": "조건 충족 — 즉시매수(눌림 안 기다림)", "stop": 99078, "target": 160366, "signals": [{"label": "추세(10일선 위)", "ok": true, "detail": "114,400 vs 83,520"}, {"label": "정배열(5>10)", "ok": true, "detail": "88,240 / 83,520"}, {"label": "추세 기울기(10일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "88,240 / 75,495"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+69.2%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -224 · 기 +284 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+22%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.55% (환산) vs 평균 0.42% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": true, "detail": "고점 87,600"}, {"label": "거래량 급증", "ok": false, "detail": "508,837 (환산) vs 평균 392,182"}, {"label": "회전율 급증", "ok": false, "detail": "0.55% (환산) vs 평균 0.42%"}, {"label": "RSI", "ok": true, "detail": "81"}, {"label": "눌림목 도달", "ok": false, "detail": "지정가 83,520 (현재 114,400)"}]}
|
||||
{"ts": "2026-08-25T11:06:23.981941+09:00", "side": "BUY", "code": "078930", "name": "GS", "price": 118900, "qty": 7, "cost": 832425, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 99078, "target": 160366, "signals": [{"label": "손절선", "ok": true, "detail": "99,078 (현재 118,400)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 160,366"}, {"label": "추세(10일선)", "ok": true, "detail": "83,920"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +10 · 기 +116"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,138"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 780251.9234999605,
|
||||
"cash": 75146.17349996034,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"055550": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 104000,
|
||||
"cur_price": 106000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 5666061.52171875,
|
||||
"last_add_price": 99700
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4789675.2297187485,
|
||||
"last_add_price": 130800
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4623660.577343748,
|
||||
"last_add_price": 338000
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 315500,
|
||||
"cur_price": 314000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4236521.005156248,
|
||||
"last_add_price": 295000
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4249087.450874997,
|
||||
"last_add_price": 195400
|
||||
@@ -118,12 +118,12 @@
|
||||
"entry_at": "2026-08-06T09:06:13.282466+09:00",
|
||||
"stop": 87523,
|
||||
"target": 137554,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "immediate",
|
||||
"entry_reason": "조건 충족 — 즉시매수(눌림 안 기다림)",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4279461.011749998,
|
||||
"last_add_price": 101700
|
||||
@@ -139,12 +139,12 @@
|
||||
"entry_at": "2026-08-19T09:18:18.228991+09:00",
|
||||
"stop": 112627,
|
||||
"target": 185097,
|
||||
"peak": 143600,
|
||||
"peak": 150800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 143300,
|
||||
"cur_price": 149900,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4367140.436124997,
|
||||
"last_add_price": 135000
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 38,
|
||||
"entry_price": 114500,
|
||||
"qty": 44,
|
||||
"entry_price": 114909.09090909091,
|
||||
"entry_at": "2026-08-24T13:12:24.707358+09:00",
|
||||
"stop": 111200,
|
||||
"target": 124400,
|
||||
"peak": 115500,
|
||||
"target": 126036,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 117,879 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4404531.535843748,
|
||||
"last_add_price": 114500
|
||||
"last_add_price": 117500
|
||||
}
|
||||
},
|
||||
"realized_pnl": -35361770.81650004,
|
||||
@@ -210,5 +210,5 @@
|
||||
"003680": "2026-08-24"
|
||||
},
|
||||
"created_at": "2026-06-16T09:00:03.113489+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.486487+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.236088+09:00"
|
||||
}
|
||||
@@ -118,3 +118,4 @@
|
||||
{"ts": "2026-08-20T09:34:23.528819+09:00", "side": "BUY", "code": "161890", "name": "한국콜마", "price": 135000, "qty": 5, "cost": 675101, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 112154, "target": 183938, "signals": [{"label": "손절선", "ok": true, "detail": "112,154 (현재 134,900)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 183,938"}, {"label": "추세(20일선)", "ok": true, "detail": "100,310"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +529 · 기 -188"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 134,629"}]}
|
||||
{"ts": "2026-08-24T13:12:24.707215+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 1007, "proceeds": 5055333, "entry_price": 6333, "pnl": -1323074, "pnl_pct": -20.58, "hold_from": "2026-07-30T09:04:08.817092+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 10,215"}, {"label": "추세(20일선)", "ok": true, "detail": "4,754"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:12:24.707360+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 114500, "qty": 38, "cost": 4351653, "path": "pullback", "reason": "눌림목 지정가 117,879 도달", "stop": 111200, "target": 124400, "signals": [{"label": "추세(20일선 위)", "ok": true, "detail": "114,200 vs 113,265"}, {"label": "정배열(5>20)", "ok": true, "detail": "116,300 / 113,265"}, {"label": "추세 기울기(20일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "116,300 / 109,922"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+13.5%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 +48 · 기 -50 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+34%"}, {"label": "목표가 리비전", "ok": true, "detail": "상향 ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "0.12% (환산) vs 평균 0.17% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 123,300"}, {"label": "거래량 급증", "ok": false, "detail": "197,995 (환산) vs 평균 289,522"}, {"label": "회전율 급증", "ok": false, "detail": "0.12% (환산) vs 평균 0.17%"}, {"label": "RSI", "ok": true, "detail": "52"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 117,879 (현재 114,200)"}]}
|
||||
{"ts": "2026-08-25T09:26:25.500637+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 117500, "qty": 6, "cost": 705106, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 124400, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 117,300)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 124,400"}, {"label": "추세(20일선)", "ok": true, "detail": "113,420"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 117,189"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 9035146.744000008,
|
||||
"cash": 1543623.1840000078,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"095610": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 144,129 도달",
|
||||
"cur_price": 133600,
|
||||
"cur_price": 139000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2602335.520457338,
|
||||
"last_add_price": 147600
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3682720.3330000006,
|
||||
"last_add_price": 13420
|
||||
@@ -53,14 +53,14 @@
|
||||
"qty": 79,
|
||||
"entry_price": 91727.84810126582,
|
||||
"entry_at": "2026-08-04T09:34:11.251794+09:00",
|
||||
"stop": 109994,
|
||||
"stop": 111762,
|
||||
"target": 117299,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": true,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3663769.807718751,
|
||||
"last_add_price": 93500
|
||||
@@ -81,7 +81,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3726510.2558437507,
|
||||
"last_add_price": 198600
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3852866.4074375005,
|
||||
"last_add_price": 417500
|
||||
@@ -113,20 +113,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 33,
|
||||
"entry_price": 115400,
|
||||
"qty": 65,
|
||||
"entry_price": 116778.46153846153,
|
||||
"entry_at": "2026-08-14T11:18:18.986875+09:00",
|
||||
"stop": 111200,
|
||||
"target": 128000,
|
||||
"peak": 117900,
|
||||
"target": 133514,
|
||||
"peak": 120500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 115,362 도달",
|
||||
"cur_price": 115000,
|
||||
"tranches": 1,
|
||||
"cur_price": 120300,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3859995.6555625005,
|
||||
"last_add_price": 115400
|
||||
"last_add_price": 118200
|
||||
},
|
||||
"001450": {
|
||||
"code": "001450",
|
||||
@@ -139,12 +139,12 @@
|
||||
"entry_at": "2026-08-20T13:20:26.879988+09:00",
|
||||
"stop": 45584,
|
||||
"target": 61809,
|
||||
"peak": 50700,
|
||||
"peak": 50900,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 50600,
|
||||
"cur_price": 50400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3788581.3199375006,
|
||||
"last_add_price": 50400
|
||||
@@ -155,20 +155,20 @@
|
||||
"sources": [
|
||||
"auto"
|
||||
],
|
||||
"qty": 75,
|
||||
"entry_price": 49500,
|
||||
"qty": 147,
|
||||
"entry_price": 50479.5918367347,
|
||||
"entry_at": "2026-08-24T14:50:23.230803+09:00",
|
||||
"stop": 44142,
|
||||
"target": 65573,
|
||||
"peak": 49650,
|
||||
"stop": 45047,
|
||||
"target": 66777,
|
||||
"peak": 52000,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 49450,
|
||||
"tranches": 1,
|
||||
"cur_price": 51800,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3745061.4761875006,
|
||||
"last_add_price": 49500
|
||||
"last_add_price": 51500
|
||||
}
|
||||
},
|
||||
"realized_pnl": -41528108.856500044,
|
||||
@@ -207,5 +207,5 @@
|
||||
"316140": "2026-08-20"
|
||||
},
|
||||
"created_at": "2026-06-16T10:45:02.136776+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.494295+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.243940+09:00"
|
||||
}
|
||||
@@ -192,3 +192,5 @@
|
||||
{"ts": "2026-08-21T12:24:25.556975+09:00", "side": "BUY", "code": "001450", "name": "현대해상", "price": 50400, "qty": 75, "cost": 3780567, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 44865, "target": 61004, "signals": [{"label": "손절선", "ok": true, "detail": "44,865 (현재 50,400)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 61,004"}, {"label": "추세(40일선)", "ok": true, "detail": "37,928"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +158 · 기 +906"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 50,252"}]}
|
||||
{"ts": "2026-08-24T14:50:22.793047+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5010, "qty": 1190, "proceeds": 5950274, "entry_price": 6017, "pnl": -1210650, "pnl_pct": -16.73, "hold_from": "2026-07-30T09:00:24.268702+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,014 (현재 5,010)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 9,024"}, {"label": "추세(40일선)", "ok": true, "detail": "4,716"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T14:50:23.230821+09:00", "side": "BUY", "code": "257720", "name": "실리콘투", "price": 49500, "qty": 75, "cost": 3713057, "path": "breakout", "reason": "거래량·회전율 동반 전고점 돌파", "stop": 44142, "target": 65573, "signals": [{"label": "추세(40일선 위)", "ok": true, "detail": "49,500 vs 36,645"}, {"label": "정배열(5>40)", "ok": true, "detail": "43,690 / 36,645"}, {"label": "추세 기울기(40일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "43,690 / 36,266"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+35.2%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -652 · 기 +1,183 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+17%"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "4.25% (환산) vs 평균 1.56% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": true, "detail": "고점 48,250"}, {"label": "거래량 급증", "ok": true, "detail": "2,788,392 (환산) vs 평균 1,023,767"}, {"label": "회전율 급증", "ok": true, "detail": "4.25% (환산) vs 평균 1.56%"}, {"label": "RSI", "ok": true, "detail": "69"}]}
|
||||
{"ts": "2026-08-25T09:34:22.177260+09:00", "side": "BUY", "code": "138040", "name": "메리츠금융지주", "price": 118200, "qty": 32, "cost": 3782967, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 111200, "target": 128000, "signals": [{"label": "손절선", "ok": true, "detail": "111,200 (현재 118,200)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 128,000"}, {"label": "추세(40일선)", "ok": true, "detail": "110,258"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -39 · 기 +19"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 118,089"}]}
|
||||
{"ts": "2026-08-25T10:02:23.615869+09:00", "side": "BUY", "code": "257720", "name": "실리콘투", "price": 51500, "qty": 72, "cost": 3708556, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 44142, "target": 65573, "signals": [{"label": "손절선", "ok": true, "detail": "44,142 (현재 51,400)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 65,573"}, {"label": "추세(40일선)", "ok": true, "detail": "36,692"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -427 · 기 +1,106"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 51,311"}]}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cash": 202056.64449999435,
|
||||
"cash": 23779.90699999433,
|
||||
"initial": 100000000,
|
||||
"positions": {
|
||||
"010950": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 141100,
|
||||
"cur_price": 140100,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4497110.087925,
|
||||
"last_add_price": 130800
|
||||
@@ -39,7 +39,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 416000,
|
||||
"cur_price": 414000,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4537990.22125,
|
||||
"last_add_price": 353000
|
||||
@@ -60,7 +60,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 12900,
|
||||
"cur_price": 13400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3916421.3406499997,
|
||||
"last_add_price": 13420
|
||||
@@ -76,12 +76,12 @@
|
||||
"entry_at": "2026-08-04T09:34:11.254802+09:00",
|
||||
"stop": 80343,
|
||||
"target": 125803,
|
||||
"peak": 121000,
|
||||
"peak": 123500,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 114000,
|
||||
"cur_price": 121700,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3911396.213525,
|
||||
"last_add_price": 93500
|
||||
@@ -102,7 +102,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 13,170 도달",
|
||||
"cur_price": 13310,
|
||||
"cur_price": 12780,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3790684.47274563,
|
||||
"last_add_price": 13830
|
||||
@@ -123,7 +123,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 194100,
|
||||
"cur_price": 189500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3913569.3666999997,
|
||||
"last_add_price": 198600
|
||||
@@ -144,7 +144,7 @@
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 113300,
|
||||
"cur_price": 111500,
|
||||
"tranches": 2,
|
||||
"tranche_value": 4106448.2651999993,
|
||||
"last_add_price": 117800
|
||||
@@ -160,12 +160,12 @@
|
||||
"entry_at": "2026-08-19T09:18:18.258948+09:00",
|
||||
"stop": 114391,
|
||||
"target": 186860,
|
||||
"peak": 143600,
|
||||
"peak": 150800,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 143300,
|
||||
"cur_price": 149900,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3939438.35845,
|
||||
"last_add_price": 135000
|
||||
@@ -181,12 +181,12 @@
|
||||
"entry_at": "2026-08-21T09:48:21.674856+09:00",
|
||||
"stop": 44391,
|
||||
"target": 66195,
|
||||
"peak": 50700,
|
||||
"peak": 50900,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "breakout",
|
||||
"entry_reason": "거래량·회전율 동반 전고점 돌파",
|
||||
"cur_price": 50600,
|
||||
"cur_price": 50400,
|
||||
"tranches": 2,
|
||||
"tranche_value": 3906404.1509499997,
|
||||
"last_add_price": 50500
|
||||
@@ -197,20 +197,20 @@
|
||||
"sources": [
|
||||
"interest"
|
||||
],
|
||||
"qty": 75,
|
||||
"entry_price": 32950,
|
||||
"qty": 80,
|
||||
"entry_price": 33118.75,
|
||||
"entry_at": "2026-08-24T13:14:24.966125+09:00",
|
||||
"stop": 22546,
|
||||
"target": 64161,
|
||||
"peak": 34700,
|
||||
"stop": 22658,
|
||||
"target": 64501,
|
||||
"peak": 35650,
|
||||
"trailing_on": false,
|
||||
"scaled_out": false,
|
||||
"entry_path": "pullback",
|
||||
"entry_reason": "눌림목 지정가 33,047 도달",
|
||||
"cur_price": 34500,
|
||||
"tranches": 1,
|
||||
"cur_price": 34550,
|
||||
"tranches": 2,
|
||||
"tranche_value": 2490747.3361213473,
|
||||
"last_add_price": 32950
|
||||
"last_add_price": 35650
|
||||
}
|
||||
},
|
||||
"realized_pnl": -25404766.05250001,
|
||||
@@ -237,5 +237,5 @@
|
||||
"316140": "2026-08-21"
|
||||
},
|
||||
"created_at": "2026-06-16T14:30:00.114884+09:00",
|
||||
"updated_at": "2026-08-24T15:28:25.496319+09:00"
|
||||
"updated_at": "2026-08-25T15:28:26.246027+09:00"
|
||||
}
|
||||
@@ -91,3 +91,4 @@
|
||||
{"ts": "2026-08-24T13:12:24.718534+09:00", "side": "SELL", "code": "003680", "name": "한성기업", "price": 5030, "qty": 1152, "proceeds": 5783261, "entry_price": 6333, "pnl": -1513534, "pnl_pct": -20.58, "hold_from": "2026-07-30T09:04:08.840088+09:00", "reason": "손절", "partial": false, "signals": [{"label": "손절선", "ok": false, "detail": "5,039 (현재 5,030)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 10,215"}, {"label": "추세(10일선)", "ok": false, "detail": "5,148"}, {"label": "수급(외/기)", "ok": false, "detail": "외 -31 · 기 +0"}, {"label": "분할(2/2)", "ok": null, "detail": "추가 종료"}]}
|
||||
{"ts": "2026-08-24T13:14:24.966126+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 32950, "qty": 75, "cost": 2471621, "path": "pullback", "reason": "눌림목 지정가 33,047 도달", "stop": 22546, "target": 64161, "signals": [{"label": "추세(10일선 위)", "ok": true, "detail": "33,000 vs 28,622"}, {"label": "정배열(5>10)", "ok": true, "detail": "35,100 / 28,622"}, {"label": "추세 기울기(10일선↑)", "ok": true, "detail": "우상향"}, {"label": "중기 정배열(5>60)", "ok": true, "detail": "35,100 / 31,286"}, {"label": "상대강도(시장대비)", "ok": true, "detail": "+24.6%p"}, {"label": "수급(5일 외/기)", "ok": true, "detail": "외 -62 · 기 +65 (천주)"}, {"label": "상승여력", "ok": true, "detail": "+106%"}, {"label": "어닝 서프라이즈", "ok": true, "detail": "+ ➕"}, {"label": "시장 강세", "ok": true, "detail": "강세"}, {"label": "과열 아님(회전율)", "ok": true, "detail": "3.32% (환산) vs 평균 3.74% (보류 3배↑)"}, {"label": "돌파(전고점)", "ok": false, "detail": "고점 45,300"}, {"label": "거래량 급증", "ok": false, "detail": "298,285 (환산) vs 평균 409,191"}, {"label": "회전율 급증", "ok": false, "detail": "3.32% (환산) vs 평균 3.74%"}, {"label": "RSI", "ok": true, "detail": "56"}, {"label": "눌림목 도달", "ok": true, "detail": "지정가 33,047 (현재 33,000)"}]}
|
||||
{"ts": "2026-08-24T15:04:21.165717+09:00", "side": "BUY", "code": "001450", "name": "현대해상", "price": 50500, "qty": 77, "cost": 3889083, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 43920, "target": 65039, "signals": [{"label": "손절선", "ok": true, "detail": "43,920 (현재 50,600)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 65,039"}, {"label": "추세(10일선)", "ok": true, "detail": "40,160"}, {"label": "수급(외/기)", "ok": true, "detail": "외 +168 · 기 +582"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 50,563"}]}
|
||||
{"ts": "2026-08-25T13:44:26.700023+09:00", "side": "BUY", "code": "327260", "name": "RF머트리얼즈", "price": 35650, "qty": 5, "cost": 178277, "path": "add", "reason": "추격매수 — 2/2차 (추세 지속)", "stop": 22546, "target": 64161, "signals": [{"label": "손절선", "ok": true, "detail": "22,546 (현재 35,650)"}, {"label": "목표/트레일링", "ok": null, "detail": "목표 64,161"}, {"label": "추세(10일선)", "ok": true, "detail": "28,887"}, {"label": "수급(외/기)", "ok": true, "detail": "외 -118 · 기 +90"}, {"label": "분할(1/2)", "ok": null, "detail": "추가 트리거 35,565"}]}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user