o
    ͿSi[                     @   s  d dl Z d dlZd dlmZ d dlZd dlZd dlZd dl	m
Z
 d dlmZmZ d dlZd dlZd dlmZmZmZmZ d dlmZ d dlmZmZmZ d dlmZ d d	lmZ d d
lm Z  d dl!Z!d dl"Z"d dl m Z m#Z# ej$ej%dd e&dZ'e Z(G dd dZ)dS )    N)
find_peaks)KiteConnect)trendmomentum)ListDictTupleOptional)BeautifulSoup)	APIRouterDependsHTTPException)database)get_current_userdetails)ZerodhaClient)datetime	timedeltaz4%(asctime)s - %(name)s - %(levelname)s - %(message)s)levelformatBearishDivergenceStrategyc                   @   sj  e Zd ZdededefddZdee fddZd	ee deeee f fd
dZ	dedee
 fddZde
dede
dejfddZdejdefddZ	dJdede
dedefddZdKdejde
dee fddZdejdeeef fdd Zd!ed"eeef defd#d$Zd!edejdefd%d&Zdejdeeef fd'd(ZdKd)ejd*e
dee
 fd+d,Zd-ed.ed/ed0ed1edefd2d3Zed4ee d5ee dee fd6d7Zd-ed.ed/ed0ed1edefd8d3ZdKdejde
dee fd9d:Zdejdeeef fd;d<Zd!ed=eeef defd>d?Z d@ed.ed/ed0edAedefdBdCZ!dDdE Z"dFee dee fdGdHZ#dIS )Lr   api_key
api_secretaccess_tokenc                 C   s6   t |d| _| j| || _g d| _|  | _d S )N)r   )   	            7   d      )r   kiteset_access_tokendbema_periods_load_instrumentsinstruments)selfr   r   r   r#    r(   ?/var/www/html/Trade-python/app/v1/services/zerolive/strategy.py__init__   s
   
z"BearishDivergenceStrategy.__init__returnc              
   C   s   t dd}| jd ddi}|rt |d  |k r|d S z| jd}| jd jddid	|t d
idd |W S  ty` } zt	
dt|  |rT|d ng W  Y d}~S d}~ww )zLoad instruments with caching   )hourszerodha_instrumentstype
nse_equitylast_updatedr&   NSEz$set)r&   r1   T)upsertzInstruments load failed: N)r   r#   find_oner   nowr!   r&   
update_one	Exceptionloggererrorstr)r'   CACHE_EXPIRYcachedr&   er(   r(   r)   r%   $   s(   

z+BearishDivergenceStrategy._load_instrumentset_namesc                 C   s   i }dd | j D }g }|D ]g}z7|dddddddd  }tj|| d	d
d}|r=||d  ||< n	d||< || W q tyu } z"t	
d| dt|  d||< ||dd  W Y d}~qd}~ww zd	dlm}	 t }
g }|D ]}||
vr|
| || q|rt	d| |	|}i }t|D ]\}}|t|k r|| pd  }|r|||< qt| D ]*\}}|rq|dddddddd  }||v r|| ||< qW |S W |S  ty } zt	
d| W Y d}~|S d}~ww )a@  Improved mapping with better name cleaning.

        First try fuzzy matching against Zerodha instrument "name"; if that
        fails for a given ET entry, fall back to the GPT-based mapper in
        `list.map_company_to_symbol` to avoid hard failures for names like
        TRANSFORMERS_RECTIFIERS_INDIA_LTD.
        c                 S   s&   i | ]}|d    dd|d qS )name- tradingsymbol)upperreplace.0instr(   r(   r)   
<dictcomp>E   s    z?BearishDivergenceStrategy.map_et_to_zerodha.<locals>.<dictcomp>_rA   z LTD z LIMITEDz INDIA   gffffff?ncutoffr   NzFailed to map z via fuzzy match: )map_company_to_symbolu%   GPT fallback for ET→NSE mapping: %sz&GPT fallback for ET mapping failed: %s)r&   rD   striprC   difflibget_close_matcheskeysappendr7   r8   warningr:   listrO   setaddinfo	enumeratelenitems)r'   r>   
symbol_mapname_to_symbolunmappedet_name
clean_namematchesr=   rO   seenfallback_inputr?   gpt_symbolsgpt_mapidxcompanysymoriginalcurrentcleanedr(   r(   r)   map_et_to_zerodha<   s   
	
"


z+BearishDivergenceStrategy.map_et_to_zerodhasymbolc                 C   s(   | j D ]}|d |kr|d   S qdS )z%Get instrument token from cached datarB   instrument_tokenN)r&   )r'   rn   rG   r(   r(   r)   get_instrument_token   s
   
z.BearishDivergenceStrategy.get_instrument_tokenro   intervaldaysc                 C   s8   t  }|t|d }| jj||||ddd}t|S )z.Fetch historical data for the given instrumentrr   F)ro   	from_dateto_daterq   
continuousoi)r   r5   r   r!   historical_datapd	DataFrame)r'   ro   rq   rr   ru   rt   datar(   r(   r)   fetch_historical_data   s   
z/BearishDivergenceStrategy.fetch_historical_datadfc                 C   sR   |j rdS | jD ]}tj|d |d}| }|d jd |jd kr& dS qdS )z Check if price is above all EMAsFclosewindowT)emptyr$   r   EMAIndicatorema_indicatoriloc)r'   r}   periodr   emar(   r(   r)   is_price_above_all_emas   s   
z1BearishDivergenceStrategy.is_price_above_all_emas{Gz?ltptolc                    sV   t  fdd| jD d}|sdS |dp|d}|r!|dkr#dS || | |kS )z
        Check if `ltp` is within `tol` (e.g. 2%) of the exchange-defined
        upper-circuit limit for this instrument. Falls back to False if
        we can't find the true circuit value.
        c                 3   s"    | ]}| d  kr|V  qdS )ro   N)get)rF   iro   r(   r)   	<genexpr>   s    zBBearishDivergenceStrategy.is_near_upper_circuit.<locals>.<genexpr>NFupper_price_rangeupper_circuit_limitr   )nextr&   r   )r'   r   ro   r   rG   rC   r(   r   r)   is_near_upper_circuit   s   z/BearishDivergenceStrategy.is_near_upper_circuit   c                 C   s   t ||d k rdg| S |d j|d  d j}g }td|d D ]!}||d  }|| }|dkr9|d q#||| | d  q#dd |D S )	u  
        Compute the % change in volume for each of the last `days` days:
        drop_pct_i = (volume_i-1 – volume_i) / volume_i-1 * 100
        
        Returns a list of floats [drop_day1, drop_day2, …], where:
        drop_day1 = % change from 4 days ago → 3 days ago
        drop_day2 = % change from 3 days ago → 2 days ago
        drop_day3 = % change from 2 days ago → yesterday
        rK           volumeNr   r   c                 S      g | ]}t |d qS    round)rF   dr(   r(   r)   
<listcomp>       zCBearishDivergenceStrategy.calculate_volume_drop.<locals>.<listcomp>r[   r   valuesrangerT   )r'   r}   rr   volsdropsr   prevcurrr(   r(   r)   calculate_volume_drop   s   
z/BearishDivergenceStrategy.calculate_volume_dropc              	   C   s   |j ri S |jd }|d |d |d }}}|| | d }d| | |||  |d||   d| | |||  |d||   dS )zCalculate standard pivot pointsr   highlowr~   r   r   )R1R2R3S1S2S3)r   r   )r'   r}   r   r   r   r~   pivotr(   r(   r)   calculate_pivot_points   s   




z0BearishDivergenceStrategy.calculate_pivot_pointspricepivot_pointsc                 C   <   dD ]}| |d}|dkrt|| | dkr dS qdS )z(Check if price is near resistance levels)r   r   r   r   r   TFr   abs)r'   r   r   r   
resistancer(   r(   r)   is_near_resistance     z,BearishDivergenceStrategy.is_near_resistancec                 C   sF   |j rdS tj|d dd}| jd }|dkrdS || | d S )z$Calculate gap between price and EMA5r   r~   r   r   r   r   r   )r   r   r   r   r   )r'   r   r}   r   ema5r(   r(   r)   calculate_ema5_gap  s   z,BearishDivergenceStrategy.calculate_ema5_gapc                 C   s>  t |dk rdS |d j}tj|d dd dj}t|d  dj}t	|d|d d	\}}t	|dd
\}}t	|dd
\}}t |dk rLdS |d |d }	}
d}t |dkrx|d |d }}||
 ||	 krx|| || k rxd}d}t |dkr|d |d }}||
 ||	 kr|| || k rd}||fS )z
        Detect bearish divergence using RSI and MACD on the given DataFrame.

        Returns (rsi_divergence, macd_divergence).
        r   FFr~      r   r   r   {Gz?distance
prominencer   r   r   FT
r[   r   r   RSIIndicatorrsifillnar   MACD	macd_diffr   )r'   r}   r~   r   macdprice_peaksrI   	rsi_peaks
macd_peaksp1p2rsi_divr1r2macd_divm1m2r(   r(   r)   detect_bearish_divergence  s,   
  z3BearishDivergenceStrategy.detect_bearish_divergenceserieslookbackc                 C   s   g }t |t|| D ]3}|j| |j|| || d   kr>|j| |j|d  kr>|j| |j|d  kr>|| q|S )zFind peak indices in a seriesrK   )r   r[   r   maxrT   )r'   r   r   peaksr   r(   r(   r)   r   D  s   (
z$BearishDivergenceStrategy.find_peaksvolume_dropema5_gapr   r   near_resc                 C   `   d}|t d|d 7 }|t d|d 7 }|r|r|d7 }n|s!|r%|d7 }|r+|d7 }t d|S z8Calculate a score for the stock based on bearish factorsr   r   333333?333333?   r   r   minr'   r   r   r   r   r   scorer(   r(   r)   calculate_scoreN  s   

z)BearishDivergenceStrategy.calculate_scorecompany_namesr&   c                 C   sn   g }dd |D }| D ])}| dd }tj|| ddd}|r,|||d   qtd	|  q|S )
Nc                 S   s   i | ]}|d    |d qS )r?   rB   )rC   rE   r(   r(   r)   rH   h  s    zKBearishDivergenceStrategy.get_tradingsymbols_from_names.<locals>.<dictcomp>rI   rA   rK   r   rL   r   zNo match found for )rD   rC   rQ   rR   rS   rT   r8   rU   )r   r&   tradingsymbolsinstrument_namescnamerl   matchr(   r(   r)   get_tradingsymbols_from_namesd  s   z7BearishDivergenceStrategy.get_tradingsymbols_from_namesc                 C   r   r   r   r   r(   r(   r)   r   w  s   

c                 C   s   t ||d k rdg| S |d j|d  d j}g }td|d D ]&}||d  || }}|dks8||kr>|d q#||| | d  q#dd |D S )	z1Compute % change in volume rise for bullish setuprK   r   r   Nr   r   c                 S   r   r   r   )rF   rr(   r(   r)   r     r   zCBearishDivergenceStrategy.calculate_volume_rise.<locals>.<listcomp>r   )r'   r}   rr   r   risesr   r   r   r(   r(   r)   calculate_volume_rise  s   
z/BearishDivergenceStrategy.calculate_volume_risec                 C   sD  t |dk rdS |d j}tj|d dd dj}t|d  dj}t	| d|d d	\}}t	| dd
\}}t	| dd
\}}t |dk rOdS |d |d }	}
d}t |dkr{|d |d }}||
 ||	 k r{|| || kr{d}d}t |dkr|d |d }}||
 ||	 k r|| || krd}||fS )z,Detect bullish divergence using RSI and MACDr   r   r~   r   r   r   r   r   r   r   r   r   r   FTr   )r'   r}   r~   r   r   troughsrI   rsi_trmacd_trt1t2bull_rsir   r   	bull_macdr   r   r(   r(   r)   detect_bullish_divergence  s,   
  z3BearishDivergenceStrategy.detect_bullish_divergencepivotsc                 C   r   )z%Check if price is near support levels)r   r   r   r   r   TFr   )r'   r   r   lvlsupr(   r(   r)   is_near_support  r   z)BearishDivergenceStrategy.is_near_supportvolume_risenear_supc                 C   r   )z8Calculate a score for the stock based on bullish factorsr   r   r   r   r   r   r   r   )r'   r   r   r   r   r   r   r(   r(   r)   calculate_score_bull  s   

z.BearishDivergenceStrategy.calculate_score_bullc                 C   s>   t j|d dd jd }|dkrdS t|| | d dS )	Nr~   r   r   r   r   r   r   r   )r   r   r   r   r   )r'   r   r}   r   r(   r(   r)   calculate_ema5_gap_bull  s   z1BearishDivergenceStrategy.calculate_ema5_gap_bullsymbolsc           "      C   s@  g }d}d}d}d}|D ]	}z|  |}|s t| d | jd| gd| i }	|	s;t| d |	dtj}
|	di d	d
}| |
|r`t| d|
 d| d | 	|dd
d}| 	|dd
d}t||k rt| dt| d| d t||k rt| dt| d| d | |st| d | j|ddd}t|t| }||k rt| d|dd| d | |d}| |
|}| |
|}| |d\}}|s|st| d  | |||||}t| d!|d | j|ddd}t|t| }||k r6t| d"|dd| d# | |d}| |
|}| |
|}| |d\}}|sb|sbt| d$ | |||||}t| d%|d || dkrd&}t|
d'}t|
d( d'} n|| dkrd)}t|
d'}t|
d* d'} n	d+}t|
d'}d,} |||t|d't|d'|r|rd-n|rd.n|rd/nd0|t|d't|d't|d'|r|rd-n|rd.n|rd/nd0|t|d'||| d1 W q ty }! ztj| d2|! d3d4 W Y d,}!~!qd,}!~!ww t|d5d6 d3d7S )8z
        Run the bearish divergence strategy on the given symbols,
        but relax all skip-conditions to ensure every symbol is analyzed.
        Also runs a parallel bullish analysis and makes a final BUY/SELL/HOLD decision.
        r   r   g      .@z: no instrument tokenzNSE:z: no quote data
last_priceohlcr   r   z: near upper circuit (/z), continuing analysisdaydate30minuter   z: only z daily bars (<z), continuingz 30m bars (<z&: price not above all EMAs, continuing   r   rs   z: avg volume drop z.2fz% < z%, continuingrK   2   z#: no bearish divergence, continuingz: bearish score z: avg volume rise z%, continuing bullish checkz#: no bullish divergence, continuingz: bullish score BUYr   g{Gz?SELLg
ףp=
?HOLDNz
RSI & MACDRSIr   rJ   )rn   ro   r   r   divergence_confirmednear_resistance
bear_scorer   ema5_gap_bulldivergence_bullnear_support
bull_scoredecisionentry_pricetarget_priceu   : ERROR → T)exc_infoc                 S   s   | d S )Nr  r(   )xr(   r(   r)   <lambda>O  s    z8BearishDivergenceStrategy.run_strategy.<locals>.<lambda>)keyreverse)rp   r8   rY   r!   quoter   npnanr   r|   sort_valuesr[   r   r   tailsumr   r   r   r   r   r   r   r   r   r   r   rT   r7   r9   sorted)"r'   r   resultsMIN_DAILY_BARSMIN_30M_BARSVOL_DROP_THRESHVOL_RISE_THRESHrn   tokenr  r   day_highdf_daydf_30	vol_dropsavg_vol_dropr   r   r   r   r   r  	vol_risesavg_vol_risesupportsr   r  rsi_bull	macd_bullr  r  entrytargetr=   r(   r(   r)   run_strategy  s   

"  




&&$z&BearishDivergenceStrategy.run_strategyN)r   )r   )$__name__
__module____qualname__r:   r*   r   r   r%   r	   rm   intrp   ry   rz   r|   boolr   floatr   r   r   r   r   r   r   Seriesr   r   staticmethodr   r   r   r   r   r   r5  r(   r(   r(   r)   r      s    "Y


.







)*r   pandasry   scipy.signalr   numpyr  timerequestskiteconnectr   tar   r   loggingostypingr   r   r   r	   bs4r
   fastapir   r   r   app.dbr   app.v1.dependencies.authr   app.v1.services.zerodha.clientr   rQ   jsonr   basicConfigINFO	getLoggerr8   routerr   r(   r(   r(   r)   <module>   s2    
