o
    eiF                     @   s   d Z ddlZddlZddlmZmZmZ ddlmZmZ ddl	m
Z
mZ ddlmZmZ dd	lmZmZ dd
lmZ ddlmZmZ ddlmZ ddlmZ eeZe
G dd dZG dd dZ dS )u  
订单管理器 — 交易生命周期管理

职责:
1. 接收信号 → 计算下单参数 → 开仓
2. 开仓后立即挂止损单（不依赖程序运行）
3. 每4H检查trailing → 调整止损位
4. 记录所有操作到数据库

关键安全原则:
- 止损单在开仓后立即挂出
- 断网/异常时不开新仓，只管理已有持仓
- 每笔操作都推送Telegram通知
    N)OptionalDictList)datetimetimezone)	dataclassfield   )BinanceClientBinanceAPIError   )get_coinCOINS)settings)calc_positioncalc_effective_equity)Database)TelegramNotifierc                   @   s   e Zd ZU dZeed< eed< eed< eed< eed< eed< eed< eed	< eed
< eed< eed< dZe	e ed< dZ
eed< dZeed< dZeed< dZeed< dZeed< dS )ActiveTradeu   活跃持仓记录trade_idsymbol	directionsignal_typeentry_price
entry_timequantitynotionalmarginleverage	stop_lossNstop_loss_order_idFtrailing_activer   
peak_pricei?B trough_price entry_method	bars_held)__name__
__module____qualname____doc__str__annotations__floatintr    r   r!   boolr"   r#   r%   r&    r0   r0   +/opt/langlang_ai/execution/order_manager.pyr       s&   
 r   c                   @   s   e Zd ZdZ		d,dededededef
dd	Zd
efddZ	dd Z
dd Zded
ee fddZdd ZdefddZdedefddZded
efddZdedefdd Zd-d"edefd#d$Zd%d& Zd'd( Zd
efd)d*Zd+S ).OrderManageruQ  
    订单管理器

    用法:
        om = OrderManager(client, db, notifier)

        # 开仓
        trade = om.open_position(signal_dict)

        # 定期检查trailing
        om.check_all_trailing()

        # 手动平仓
        om.close_position(trade_id)

        # 紧急全部平仓
        om.emergency_close_all()
    r   clientdbnotifierbalancemonth_start_balancec                 C   s>   || _ || _|| _|p|  | _|p| j| _i | _|   d S N)r3   r4   r5   _fetch_balancer6   r7   active_trades_restore_trades)selfr3   r4   r5   r6   r7   r0   r0   r1   __init__J   s   zOrderManager.__init__returnc              
   C   sT   z| j  W S  ty) } ztd|  t| jddW  Y d}~S d}~ww )u   从Binance获取余额zFailed to fetch balance: r6   1000N)r3   get_balance	Exceptionloggererrorr-   r4   	get_state)r<   er0   r0   r1   r9   ^   s   zOrderManager._fetch_balancec              
   C   s   ddl }| jdd}z-||}| D ]\}}tdi || j|< q| jr6tdt	| j d W dS W dS  t
yS } ztd|  W Y d}~dS d}~ww )	u   从数据库恢复活跃持仓r   Nr:   z{}z	Restored z active tradeszFailed to restore trades: r0   )jsonr4   rD   loadsitemsr   r:   rB   infolenrA   warning)r<   rF   trades_jsontrades_datatidtdrE   r0   r0   r1   r;   f   s   
zOrderManager._restore_tradesc                 C   s   ddl }i }| j D ]L\}}i d|jd|jd|jd|jd|jd|jd	|j	d
|j
d|jd|jd|jd|jd|jd|jd|jd|jd|j||< q| jd|| dS )u   保存活跃持仓到数据库r   Nr   r   r   r   r   r   r   r   r   r   r   r    r!   r"   r#   r%   r&   r:   )rF   r:   rH   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   r#   r%   r&   r4   	set_statedumps)r<   rF   datarN   trader0   r0   r1   _save_tradess   sN   	

zOrderManager._save_tradessignalc                 C   s  |d }|d }t |}zW| j||j | j|d | j|}|r0t| d W dS |dd}|dkrEtd|  W dS | j	||}|dkrSW dS |d	krYd
nd}|dd}	|	dkrq| j
||||d }
n| j|||}
|
rd|
vrtd|
  W dS t|
d|d }|dkr|d }|d	krdnd
}|d }z| j||||}|d}W n/ ty } z#td|  d}| jd| d| d|dd|  W Y d}~nd}~ww | dtt  }t||||d |ttj |||d||j |j|||||	d}|| j|< |   | jd|d	kr*dnd  d| d!|dd"|dd#|r=d$nd% d&| d'|d(d#|j d) td*| d| d+| d,| d-| 
 |W S  ty } ztjd.| d/d0 | jd1| d2|  W Y d}~dS d}~ww )3uk  
        根据信号开仓

        流程:
        1. 设置杠杆和保证金模式
        2. 计算下单数量
        3. 市价/限价下单
        4. 立即挂止损单
        5. 推送通知
        6. 记录数据库

        Args:
            signal: 信号字典（来自scanner）
        Returns:
            ActiveTrade or None if failed
        coinr   ISOLATEDz already has position, skipNr   r   zInvalid notional: longBUYSELLr%   u   4H直接u   1H回踩r   orderIdzOpen order failed: avgPricer   zStop loss order failed: u   ⚠️ 止损挂单失败!
 u   
请手动设置止损 @ $,.2fu	   
错误: _r   r   )r   r   r   r   r   r   r   r   r   r   r   r    r"   r#   r%   u   ✅ 已开仓
u   🟢 做多u   🔴 做空
   
入场: $u
   
止损: $ (u	   已挂单u   ⚠️ 挂单失败u
   )
数量: u
   
名义: $z,.0fzx)zOpened z: qty=z @ z, SL=zOpen position failed: T)exc_infou   ⚠️ 开仓失败: 
)r   r3   set_leverager   set_margin_typeget_positionrB   rK   getcalc_quantity
limit_openmarket_openrC   r-   stop_loss_orderrA   r5   	send_syncr.   timer   r   nowr   utc	isoformatr:   rT   rI   )r<   rU   r   r   cfgexistingr   r   sider%   orderr   sl_sider   sl_ordersl_order_idrE   r   rS   r0   r0   r1   open_position   s   
	zOrderManager.open_positionc                 C   s   | j sdS tdt| j  d t| j  D ])\}}z| | W q tyA } ztd|j	 d|  W Y d}~qd}~ww | 
  dS )uY   
        检查所有活跃持仓的trailing止损

        应每4H调用一次
        NzChecking trailing for z
 positionszTrailing check error : )r:   rB   rI   rJ   listrH   _check_trailing_singlerA   rC   r   rT   r<   r   rS   rE   r0   r0   r1   check_all_trailing  s   $zOrderManager.check_all_trailingrS   c                 C   s&  t |j}| j|j}| jd7  _|jdkrXt|j||_|j|j |j }|t	j
kr0d|_|jrH|j}|jd|  }||jkrH| || |jt	jkrV| |d dS dS t|j||_|j|j |j }|t	j
krpd|_|jr|jd }||jk r| || |jt	jkr| |d dS dS )u   检查单个持仓的trailingr	   rX   Tmax_holdgQ?N)r   r   r3   	get_pricer&   r   maxr"   r   STRAILING_ACTIVATE_PCTr!   trailing_pctr   _update_stop_lossMAX_HOLD_LONG_4H_BARS_close_trademinr#   MAX_HOLD_SHORT_4H_BARS)r<   rS   rq   current_price
unrealizedr   new_slr0   r0   r1   r{   !  s6   






z#OrderManager._check_trailing_singler   c                 C   s  zo|j rz| j|j|j  W n	 ty   Y nw |jdkr dnd}| j|j|||j}|j}||_|	d|_ t
d|j d|dd|d | jd	|j d
|jdkrYdnd d|dd|dd| |d
 W dS  ty } zt
d|j d|  W Y d}~dS d}~ww )u   更新止损位rX   rZ   rY   r[   zUpdated SL z: $r^   u    → $u   📊 止损上移
r]      做多   做空u   
旧止损: $u   
新止损: $u   
当前浮盈: z.1%zUpdate SL failed ry   N)r    r3   cancel_orderr   r   r   rk   r   r   rg   rB   rI   r5   rl   _calc_unrealizedrA   rC   )r<   rS   r   ru   rv   old_slrE   r0   r0   r1   r   M  sH   

&zOrderManager._update_stop_lossc                 C   sJ   z| j |j}|jdkr||j |j W S |j| |j W S    Y dS )u   计算未实现盈亏百分比rX   g        )r3   r   r   r   r   )r<   rS   pricer0   r0   r1   r   o  s   
zOrderManager._calc_unrealizedreasonc           
      C   s  z| j |j |jdkrdnd}| j |j||j}t|dd}|dkr.| j |j}|jdkr<||j	 |j	 }n|j	| |j	 }|j
| }|dkrOdnd}| j| d| d	|j d
|jdkrednd d|j	dd|dd|dd|dd|jd  d |  | _| jdt| j |j| jv r| j|j= |   td|j d| d|d W dS  ty }	 z td|j d|	  | jd|j d|	  W Y d}	~	dS d}	~	ww )u   平仓rX   rZ   rY   r\   r   u   💰u   ❌u    已平仓 (z)
r]   r   r   r`   r^   u
   
出场: $u
   
盈亏: $z+,.2fra   z+.1%u
   )
持仓:    u   小时r6   zClosed ry   z, PnL=$zClose trade failed u   ⚠️ 平仓失败: rc   N)r3   cancel_all_ordersr   r   market_closer   r-   rg   r   r   r   r5   rl   r&   r9   r6   r4   rP   r+   r   r:   rT   rB   rI   rA   rC   )
r<   rS   r   
close_sidert   
exit_pricepnl_pct
pnl_dollaremojirE   r0   r0   r1   r   |  sT   





&(zOrderManager._close_trademanualr   c                 C   s6   | j |}|r| || dS td| d dS )u   手动平仓zTrade z
 not foundN)r:   rg   r   rB   rK   )r<   r   r   rS   r0   r0   r1   close_position  s   zOrderManager.close_positionc                 C   s~   t d | jd t| j D ]*\}}z| |d W q ty< } zt 	d|j
 d|  W Y d}~qd}~ww dS )u   紧急全部平仓z!EMERGENCY: Closing all positions!u   🚨 紧急全部平仓!	emergencyzEmergency close z	 failed: N)rB   rK   r5   rl   rz   r:   rH   r   rA   rC   r   r|   r0   r0   r1   emergency_close_all  s   
$z OrderManager.emergency_close_allc              
   C   s$  | j  }dd |D }dd | j D }t| j D ]"\}}|j|vr?td|j d | j	
d|j d | j|= q|D ])}|d |vrktd|d  d	 | j	
d
|d  d|d  d|d  d qB|   |  | _| jdt| j tdt| j dt| d dS )uq   
        对账：API持仓 vs 本地记录

        每日执行，确保本地记录与交易所一致
        c                 S   s   h | ]}|d  qS r   r0   ).0pr0   r0   r1   	<setcomp>  s    z)OrderManager.reconcile.<locals>.<setcomp>c                 S   s   h | ]}|j qS r0   r   r   tr0   r0   r1   r     s    zReconcile: z' not in API, likely stop loss triggeredu   📋 对账: u)    已被止损平仓
（API无此持仓）r   z in API but not local!u   ⚠️ 对账异常: u1    在交易所有持仓但本地无记录
方向: rs   u
   , 数量: r   u   
请检查！r6   zReconcile done: z local, z APIN)r3   get_positionsr:   valuesrz   rH   r   rB   rK   r5   rl   rT   r9   r6   r4   rP   r+   rI   rJ   )r<   api_positionsapi_symbolslocal_symbolsrN   rS   r   r0   r0   r1   	reconcile  s8   


zOrderManager.reconcilec                    s>    j t jtdd  j D  fdd j D dS )u   获取当前状态c                 s   s    | ]}|j V  qd S r8   )r   r   r0   r0   r1   	<genexpr>  s    z*OrderManager.get_status.<locals>.<genexpr>c                    s2   g | ]}|j |j|j|j|j|j |d qS ))r   r   r   r   r!   r&   r   )r   r   r   r   r!   r&   r   r   r<   r0   r1   
<listcomp>  s    
z+OrderManager.get_status.<locals>.<listcomp>)r6   r:   total_margintrades)r6   rJ   r:   sumr   r   r0   r   r1   
get_status  s   

zOrderManager.get_statusN)r   r   )r   )r'   r(   r)   r*   r
   r   r   r-   r=   r9   r;   rT   dictr   r   rx   r}   r{   r   r   r+   r   r   r   r   r   r0   r0   r0   r1   r2   6   s8    
,"1(r2   )!r*   loggingrm   typingr   r   r   r   r   dataclassesr   r   binance_clientr
   r   config.coinsr   r   configr   r   core.positionr   r   data.databaser   notify.telegram_botr   	getLoggerr'   rB   r   r2   r0   r0   r0   r1   <module>   s     
