o
    dia5                     @   s   d Z ddlZddlZddlZddlZddlZddlmZmZm	Z	 ddl
mZ ddlZeeZdZdZG dd dZG d	d
 d
eZdS )uG  
Binance Futures API 客户端

封装所有与Binance Futures API的交互:
- 账户信息查询（余额、持仓）
- 下单（限价/市价/止损）
- 改单/撤单
- 持仓查询

安全设计:
- API Key只开交易权限，不开提币权限
- 所有请求带签名（HMAC SHA256）
- 支持测试网（testnet）模式
    N)OptionalDictList)	urlencodezhttps://fapi.binance.comz!https://testnet.binancefuture.comc                   @   s  e Zd ZU dZ		dRdedededefdd	ZedSded
d fddZ	edTdeded
d fddZ
ded
efddZ		dUdedededed
ef
ddZd
efddZd
efddZd
efddZd
ee fd d!Zd"ed
ee fd#d$Zd"ed%ed
efd&d'ZdVd"ed)ed
efd*d+Zd"ed,ed-ed
efd.d/Z	0dWd"ed,ed-ed1ed2ed
efd3d4Z	dSd"ed,ed5ed-ed6ed
efd7d8Zd"ed,ed-ed
efd9d:Zd"ed;ed
efd<d=Zd"ed
efd>d?ZdXd"ee d
ee fd@dAZd"ed;ed
efdBdCZd"ed
efdDdEZ d"ed
efdFdGZ!i Z"e#eef e$dH< d"ed
efdIdJZ%d"ed1ed
efdKdLZ&d"ed-ed
efdMdNZ'd"edOed
efdPdQZ(dS )YBinanceClientu<  
    Binance Futures API客户端

    用法:
        client = BinanceClient(api_key, api_secret)
        client = BinanceClient.from_env()  # 从环境变量
        client = BinanceClient.testnet(api_key, api_secret)  # 测试网

        # 查询
        balance = client.get_balance()
        positions = client.get_positions()

        # 下单
        order = client.market_buy('BTCUSDT', quantity=0.01)
        order = client.limit_buy('BTCUSDT', quantity=0.01, price=65000)
        order = client.stop_loss('BTCUSDT', 'SELL', stop_price=64000, quantity=0.01)
    F  api_key
api_secrettestnetrecv_windowc                 C   sb   || _ || _|r
tnt| _|| _|| _t | _	| j	j
d| j i td|r)dnd d d S )NzX-MBX-APIKEYzBinanceClient initialized (r
   mainnet))r   r	   TESTNET_BASEMAINNET_BASEbase_urlr   r
   requestsSessionsessionheadersupdateloggerinfo)selfr   r	   r
   r    r   ,/opt/langlang_ai/execution/binance_client.py__init__4   s   
zBinanceClient.__init__returnc                 C   s:   t jdd}t jdd}|r|std| |||dS )u   从环境变量创建客户端BINANCE_API_KEY BINANCE_API_SECRETu<   需设置环境变量 BINANCE_API_KEY 和 BINANCE_API_SECRETr
   )osenvironget
ValueError)clsr
   keysecretr   r   r   from_envH   s   zBinanceClient.from_envr   c                 C   s2   |pt jdd}|pt jdd}| ||ddS )u   创建测试网客户端BINANCE_TESTNET_KEYr   BINANCE_TESTNET_SECRETTr    )r!   r"   r#   )r%   r   r	   r&   r'   r   r   r   r
   S   s   zBinanceClient.testnetparamsc                 C   sT   t t d |d< | j|d< t|}t| jd|dtj	
 }||d< |S )u   为请求参数添加签名i  	timestamp
recvWindowzutf-8	signature)inttimer   r   hmacnewr	   encodehashlibsha256	hexdigest)r   r+   query_stringr.   r   r   r   _sign\   s   

zBinanceClient._signNTmethodpathsignedc              
   C   s  | j  | }|p
i }|r| |}z\|dkr!| jj||dd}n#|dkr/| jj||dd}n|dkr=| jj||dd}ntd| | }|jdkrl|dt	|}|d	|j}	t
d
|	 d|  t|	||W S  tjjy }
 z	t
d|
   d}
~
ww )u   发送API请求GET   )r+   timeoutPOSTDELETEzUnknown method:    msgcodez
API error : zRequest failed: N)r   r8   r   r#   postdeleter$   jsonstatus_codestrr   errorBinanceAPIErrorr   
exceptionsRequestException)r   r9   r:   r+   r;   urlrespdata	error_msg
error_codeer   r   r   _requestk   s0   


zBinanceClient._requestc                 C   s   |  ddS )u3   获取账户信息（余额、持仓、杠杆等）r<   z/fapi/v2/accountrT   )r   r   r   r   get_account   s   zBinanceClient.get_accountc                 C   s:   |   }|dg D ]}|d dkrt|d   S q
dS )u   获取USDT可用余额assetsassetUSDTavailableBalanceg        )rV   r#   float)r   accountrX   r   r   r   get_balance   s   zBinanceClient.get_balancec                 C   s   |   }t|ddS )u'   获取总权益（含未实现盈亏）totalWalletBalancer   )rV   r[   r#   )r   r\   r   r   r   get_total_equity   s   zBinanceClient.get_total_equityc                 C   s   |  dd}g }|D ]G}t|dd}|dkrQ||d |dkr#dndt|t|d t|d	 t|d
 tt|ddt|ddt|ddd	 q
|S )u   获取所有活跃持仓r<   z/fapi/v2/positionRiskpositionAmtr   symbolLONGSHORT
entryPriceunRealizedProfitleveragenotionalisolatedWalletliquidationPrice)	ra   sidequantityentry_priceunrealized_pnlrf   rg   marginliquidation_price)rT   r[   r#   appendabsr/   )r   rP   activepamtr   r   r   get_positions   s$   


zBinanceClient.get_positionsra   c                 C   s*   |   }|D ]}|d |kr|  S qdS )u   获取单个币种的持仓ra   N)ru   )r   ra   	positionsrs   r   r   r   get_position   s   zBinanceClient.get_positionrf   c                 C      |  dd||dS )u   设置杠杆倍数r?   z/fapi/v1/leverage)ra   rf   rU   )r   ra   rf   r   r   r   set_leverage      zBinanceClient.set_leverageISOLATEDmargin_typec              
   C   sP   z|  dd||dW S  ty' } z|jdkr"ddiW  Y d}~S  d}~ww )u+   设置保证金模式（ISOLATED/CROSSED）r?   z/fapi/v1/marginType)ra   
marginTypei2rB   zalready setN)rT   rK   rC   )r   ra   r|   rS   r   r   r   set_margin_type   s   

zBinanceClient.set_margin_typerj   rk   c                 C   sP   ||d|  ||d}| dd|}td| d| d| d|d	  |S )
u   
        市价开仓

        Args:
            symbol: 'BTCUSDT'
            side: 'BUY' (做多) or 'SELL' (做空)
            quantity: 数量（币数，如BTC=0.001）
        MARKET)ra   rj   typerk   r?   /fapi/v1/orderzMarket  : qty=z
, orderId=orderId)_format_quantityrT   r   r   r#   r   ra   rj   rk   r+   orderr   r   r   market_open   s   

(zBinanceClient.market_openGTCpricetime_in_forcec              
   C   sV   ||d|  ||| |||d}| dd|}td| d| d| d|  |S )	u   
        限价开仓

        Args:
            price: 限价价格
            time_in_force: 'GTC'(Good Till Cancel) / 'IOC' / 'FOK'
        LIMIT)ra   rj   r   rk   r   timeInForcer?   r   zLimit r   r   z @ )r   _format_pricerT   r   r   )r   ra   rj   rk   r   r   r+   r   r   r   r   
limit_open   s   


"zBinanceClient.limit_open
stop_priceclose_positionc                 C   sd   ||d|  ||dd}|rd|d< n| |||d< | dd|}td	| d
| d|  |S )u  
        止损单（触发后市价执行）

        Args:
            side: 'SELL'(做多的止损) or 'BUY'(做空的止损)
            stop_price: 触发价格
            quantity: 平仓数量
            close_position: True则平掉全部仓位
        STOP_MARKET
MARK_PRICE)ra   rj   r   	stopPriceworkingTypetrueclosePositionrk   r?   r   z
Stop loss r   z: trigger @ )r   r   rT   r   r   )r   ra   rj   r   rk   r   r+   r   r   r   r   stop_loss_order   s   

zBinanceClient.stop_loss_orderc                 C   sF   ||d|  ||dd}| dd|}td| d| d|  |S )	u   
        市价平仓

        Args:
            side: 'SELL'(平多) or 'BUY'(平空)
            quantity: 平仓数量
        r   r   )ra   rj   r   rk   
reduceOnlyr?   r   zClose r   r   )r   rT   r   r   r   r   r   r   market_close  s   	
zBinanceClient.market_closeorder_idc                 C   rx   )u   撤销单个订单r@   r   ra   r   rU   r   ra   r   r   r   r   cancel_order,  rz   zBinanceClient.cancel_orderc                 C   s   |  ddd|iS )u   撤销某币种所有挂单r@   z/fapi/v1/allOpenOrdersra   rU   )r   ra   r   r   r   cancel_all_orders3  s   zBinanceClient.cancel_all_ordersc                 C   s   i }|r||d< |  dd|S )u   获取所有挂单ra   r<   z/fapi/v1/openOrdersrU   )r   ra   r+   r   r   r   get_open_orders9  s   zBinanceClient.get_open_ordersc                 C   rx   )u   查询单个订单状态r<   r   r   rU   r   r   r   r   	get_order@  rz   zBinanceClient.get_orderc                 C   s"   | j ddd|idd}t|d S )u   获取最新价格r<   z/fapi/v1/ticker/pricera   Fr;   r   )rT   r[   )r   ra   rP   r   r   r   	get_priceI  s   zBinanceClient.get_pricec                 C   s:   | j dddd}|dg D ]}|d |kr|  S qi S )u6   获取交易对信息（精度、最小下单量等）r<   z/fapi/v1/exchangeInfoFr   symbolsra   )rT   r#   )r   ra   rP   sr   r   r   get_exchange_infoO  s   zBinanceClient.get_exchange_info_precision_cachec                 C   s   || j vrC| |}|dd}|dd}dd |dg D }t|di d	d
}t|di dd}||||d| j |< | j | S )u   获取交易对精度pricePrecision   quantityPrecision   c                 S   s   i | ]}|d  |qS )
filterTyper   ).0fr   r   r   
<dictcomp>b  s    z0BinanceClient._get_precision.<locals>.<dictcomp>filtersLOT_SIZEminQtygMbP?MIN_NOTIONALrg      )price_precisionqty_precisionmin_qtymin_notional)r   r   r#   r[   )r   ra   r   r   r   r   r   r   r   r   r   _get_precision\  s   


zBinanceClient._get_precisionc                 C   s   |  |}|d|d  dS )u   格式化价格到正确精度.r   r   )r   )r   ra   r   precr   r   r   r   n  s   
zBinanceClient._format_pricec                 C   s@   |  |}t||d }||d k r|d }|d|d  dS )u   格式化数量到正确精度r   r   r   r   )r   round)r   ra   rk   r   	formattedr   r   r   r   s  s
   
zBinanceClient._format_quantityrg   c                 C   s   |  |}|dkrdS || S )u   
        从名义持仓金额计算下单数量

        Args:
            symbol: 交易对
            notional: 名义持仓金额（美元）
        Returns:
            币数量
        r   )r   )r   ra   rg   r   r   r   r   calc_quantity{  s   

zBinanceClient.calc_quantity)Fr   )F)r   r   )NT)r{   )r   )N))__name__
__module____qualname____doc__rI   boolr/   r   classmethodr(   r
   dictr8   rT   rV   r[   r]   r_   r   ru   r   rw   ry   r~   r   r   r   r   r   r   r   r   r   r   r   r   __annotations__r   r   r   r   r   r   r   r   r   !   sz   
 


#


	r   c                       s*   e Zd ZdZdedef fddZ  ZS )rK   u   Binance API错误rC   rB   c                    s(   || _ || _t d| d|  d S )NzBinance API Error rD   )rC   rB   superr   )r   rC   rB   	__class__r   r   r     s   zBinanceAPIError.__init__)r   r   r   r   r/   rI   r   __classcell__r   r   r   r   rK     s    rK   )r   r!   r0   r1   r4   loggingtypingr   r   r   urllib.parser   r   	getLoggerr   r   r   r   r   	ExceptionrK   r   r   r   r   <module>   s     
  l