o
     &j[*                     @  s
  d Z ddlmZ ddlZddlZddlZddlZddl	Z	ddl
Ze	je	jeZe	jeZe	jedZe	jedZe	jedZe	jedZdZd	d
iZd*ddZd+ddZd,ddZd-ddZd.ddZd/d d!Zd0d$d%Zd1d'd(Z e!d)kre"e  dS )2u  보유종목 현재가 주기 갱신 (Yahoo Finance / yfinance 소스).

매매(수량·평단) 변화가 없을 때, live_account_state.json/.js 의 보유종목
current_price·value_krw·pnl_krw 만 최신 시세로 갱신한다.

엄격 규칙(이 프로젝트):
- 보유 수량(qty)·원가(buy basis)는 절대 수정하지 않는다. (매매 시에만 사용자 입력)
- 전략/판단 필드(mode, verdict, kakao_summary 등)도 건드리지 않는다.
- 갱신 대상은 orders[].current_price / value_krw / pnl_krw 와 메타(prices_updated_at, fx_usdkrw) 뿐.
- 데이터 실패 종목은 기존 값 유지(임의 0/추정 금지). 무료 Yahoo 소스만 사용.
    )annotationsNz
.kakao_envzlive_account_state.jsonzlive_account_state.jszprice_refresh_log.jsonlzwindow.LIVE_ACCOUNT_STATE = z
User-AgentzMozilla/5.0symbolstrc                 C  s   d|  d}t jj|td}tt jj|dd d d d }|d	i }z|d
 d d d }t	|D ]}|durCt
|  W S q6W n	 tyN   Y nw dD ]}||durbt
||   S qQdS )u   확장시간(프리/애프터마켓) 포함 최신가. 1분봉 includePrePost=true 의 마지막 유효 종가를
    우선 사용하고, post/pre meta가 있으면 그 값을, 없으면 정규장가로 폴백.z2https://query1.finance.yahoo.com/v8/finance/chart/z)?range=1d&interval=1m&includePrePost=true)headers   )timeoutchartresultr   meta
indicatorsquotecloseN)postMarketPricepreMarketPriceregularMarketPrice)urllibrequestRequestUAjsonloadsurlopenreadgetreversedfloat	Exception)r   urlreqresr
   closesvk r#   refresh_prices_yf.pyyahoo_price    s&   &r%   returnr   c               	   C     t jd} | s6t jtr6zttddD ]}| dr*| 	ddd }  nqW n	 t
y5   Y nw zt| }d|  krGdkrK|W S  W d	S W d	S  ttfyY   Y d	S w )
u   증권사 기준환율 보정 계수. .kakao_env 의 FX_SPREAD(예: 1.003) 또는 env. 기본 1.0(=시장 스팟 그대로).
    effective_fx = 시장스팟 × FX_SPREAD.	FX_SPREADutf-8encodingz
FX_SPREAD==   g?g?      ?osenvironr   pathexistsENV_FILEopenstrip
startswithsplitr   r   	TypeError
ValueError)r!   linesr#   r#   r$   read_fx_spread7   0   r=   float | Nonec               	   C  r'   )
u   증권사 기준환율 고정값. .kakao_env 의 FX_RATE(예: 1528.7) 또는 env.
    설정 시 평가에 이 값을 그대로 적용(앱과 정확히 일치). 미설정이면 None(=시장 스팟 사용).FX_RATEr)   r*   zFX_RATE=r,   r-   i   i	  Nr/   )r!   r;   rr#   r#   r$   read_fx_overrideL   r>   rB   recdictNonec                 C  s~   t jt jj | d< z(ttddd}|t	j
| ddd  W d    W d S 1 s-w   Y  W d S  ty>   Y d S w )Ntsar)   r*   F)ensure_ascii
)dtdatetimenowtimezoneutc	isoformatr5   LOG_PATHwriter   dumpsr   )rC   fr#   r#   r$   loga   s   &rT   
str | Nonec                  C  s(   t ttjtd} | r| d S d S )Nz(current_positions_from_screenshots_*.csv)sortedglobr0   r2   joinBASE)filesr#   r#   r$   latest_positions_csvj   s   r\   statepricesc                 C  s  t  }|r|sdddS i }t|dd}t|D ]}||d g | qW d   n1 s2w   Y  i }| D ]~\}}	| di |}
|
sMq=td	d
 |	D }d|
vrn|
d|
d< t	|
dph|| |
d< d}d}|	D ]+}||d }|du r|t
|dpd7 }d}qt|t
|d t
| t
| 7 }qtt	||
d< t	|
dd| |
d< |
d |d||< q=d|tj|dS )u	  계좌별(본/부) 산출을 라이브 시세로 갱신.
    보유는 current_positions_from_screenshots_*.csv(매매 시에만 변경)를 기준,
    현금은 스냅샷 총액 - 스냅샷 포지션합으로 1회 고정 → current = cash + Σ(live qty*price*fx).Fno_csv_or_fx)okreasonr)   r*   accountNaccountsc                 s  s"    | ]}t |d pdV  qdS )	value_krwr   N)r   r   ).0rA   r#   r#   r$   	<genexpr>   s     z"update_accounts.<locals>.<genexpr>cash_krwcurrent_krwsnapshot_total_krwg        Ttickerrd   r   qtypositions_value_krw)rh   
all_priced)r`   rc   csv)r\   r5   csvmod
DictReader
setdefaultappenditemsr   sumroundr   r0   r2   basename)r]   r^   fxr2   rowsrS   rA   r	   acctrsaccpos_baselive_valr`   pr#   r#   r$   update_accountso   s:   
"r   r2   textc                 C  sP   | d }t |ddd}|| W d    n1 sw   Y  t||  d S )Nz.tmpwr)   r*   )r5   rQ   r0   replace)r2   r   tmprS   r#   r#   r$   atomic_write   s
   r   intc                    sN  t jtstddd dS ttdd} t| }W d    n1 s%w   Y  |dg }|s;tddd dS zt	d	}W n! t
yb } zd }td
t|d d d W Y d }~nd }~ww t }t }|rutt|dd}n|rtt|| dd|dkrdnd }nd d}g g }}	i }
|D ]}|d}|d|rsqzt	|}|stdW n% t
y } z|	| td|t|d d d W Y d }~qd }~ww |d}|d}t|ttfrt|ttfr|| nd }tt|d|d< t||
|< t|  fdd} fdd}|d d ur3||d  |d!< ||d  |d"< |d#d urK||d# |d$< ||d# |d%< |d&d urc||d& |d'< ||d& |d(< |d)d urs||d) |d*< |d+d ur||d+ |d,< ||d+ |d-< |d |d#|d+}}}d.}|r t|krd/}n;|r t|krd0}n.|rň t|krd1}n!|rt|    d2krd3}n|r t|   d2krd4}||d5< }|d u rt|ttfr|d6rz
||d6   }W n t
y   d }Y nw |r4tt| t| }||d< |d ur4|| |d< || qtjtjj }| |d7< |rVtt|d8|d9< rfttd8|d:< ||d;< t||
}td<d=i| tj|d>d8d?}tt| ttt| d@  tdA||	dB t dCt!| dD|	pd dE  dS )FNskipno_state_file)eventra   r   r)   r*   orders	no_orderszKRW=Xfx_failx   )r   err   u   기준환율(고정)u   시장스팟r.   u   ×보정 u   없음rj   rk   zno price
price_fail)r   rj   r   rd   pnl_krwcurrent_pricec                   s&   | dvrt t|     d dS d S )NNr   d   r-   ru   r   t)cr#   r$   _gp   s   &zmain.<locals>._gpc                   s,   | dvrrt t|    t S d S )Nr   r   r   r   rw   rk   r#   r$   _ep   s   ,zmain.<locals>._ept1t1_gain_pctt1_est_profit_krwt2t2_gain_pctt2_est_profit_krw
hold_untilhold_until_gain_pcthold_until_est_profit_krw
prior_highprior_high_gap_pctinvalidinvalid_loss_pctinvalid_est_loss_krwu   진행u   손절이탈u   T2도달u   T1돌파gQ?u   T1근접u   손절근접target_state_prev_priceprices_updated_at   	fx_market	fx_usdkrw	fx_sourcer   accounts_updateF)rH   indentz;
done)r   updatedfailedrw   u   가격 갱신 완료: u   종목, 실패 u	   , 환율 )"r0   r2   r3   	JSON_PATHrT   r5   r   loadr   r%   r   r   r=   rB   ru   r   r:   rr   
isinstancer   rJ   rK   rL   rM   rN   
astimezonerO   r   rR   r   JS_PATH	JS_PREFIXprintlen)rS   r]   r   spotespreadoverrider   r   r   r^   osymprice	old_valueold_pnl	buy_basisr   r   r   r   invstuse_fx	new_valuerL   acct_resr   r#   r   r$   main   s   &(






(    "" &

$r   __main__)r   r   )r&   r   )r&   r?   )rC   rD   r&   rE   )r&   rU   )r]   rD   r^   rD   r&   rD   )r2   r   r   r   r&   rE   )r&   r   )#__doc__
__future__r   rn   ro   rK   rJ   rX   r   r0   urllib.requestr   r2   dirnameabspath__file__rZ   ROOTrY   r4   r   r   rP   r   r   r%   r=   rB   rT   r\   r   r   r   __name__
SystemExitr#   r#   r#   r$   <module>   s6   




	

$
n
