o
    TLNiY                     @   s:  d dl mZmZmZmZ d dlmZmZ d dlZd dlZd dl	Z	d dl
Z
d dlmZ d dlmZ d dlmZ d dlmZ zd dlmZ W n eyO   dZY nw eeZeej ed	d
ZeddZeeddZdd Z de!fddZ"h dZ#dddddddddZ$de!de!fddZ%dee!ef fdd Z&dee! fd!d"Z'deee!ef  fd#d$Z(d%e!dee fd&d'Z)dSd)ed*e*dee!ef fd+d,Z+dTd)ed/ed%e!d0ee! d1ed2edee!ef fd3d4Z,d)ed%e!dee!ef fd5d6Z-d7e!deee!ef  fd8d9Z.d:e!deee!ef  fd;d<Z/d=ee!ef dee!ef fd>d?Z0dUd@ee!ef dAee! dee!ef fdBdCZ1d)edDee!ef d%e!dEee!ef dee!ef f
dFdGZ2dVd)edJe!dKedLee! dAee! dee!ef fdMdNZ3d)edDee!ef dEee!ef dee!ef fdOdPZ4dEee!ef dee!ef fdQdRZ5dS )W    )ListDictAnyOptional)datetime	timedeltaN)ObjectId)HTTPException)database)ZerodhaClient)OpenAIOPENAI_API_KEY OPENAI_MODELzgpt-4o-miniLLM_TIMEOUT_SEC20c                   C   s&   t d u rtdtstdt tdS )Nz<openai package v1+ not installed. Install 'openai' >= 1.0.0.z/OPENAI_API_KEY environment variable is not set.)api_key)r   RuntimeErrorr    r   r   6/var/www/html/Trade-python/app/v1/services/tradeGPT.py_get_openai_client   s
   
r   method_namec           	   
   O   s   zRt | |rt| ||i |W S t| dd}|r)t ||r)t|||i |W S |dd}t | |r?t| ||i |W S |rQt ||rQt|||i |W S W n ty_ } z d}~ww g }zdd t| D }W n tyx   g }Y nw |rd|dd nd	}td
| d| )z
    Defensive call helper: tries method on wrapper, then on zerodha.kite.
    Raises AttributeError with diagnostics if not found.
    kiteN_r   c                 S   s   g | ]	}| d s|qS )r   )
startswith).0ar   r   r   
<listcomp>>       z!_call_zerodha.<locals>.<listcomp>z, (   z
<no attrs>zZerodha client missing method 'z'. Sample attrs: )hasattrgetattrreplace	ExceptiondirjoinAttributeError)	zerodhar   argskwargsr   altesample_attrssampler   r   r   _call_zerodha%   s0   

r.   >
   dayweekmonthminute3minute5minute10minute15minute30minute60minuter2   r4   r6   r7   r8   )1minute1min5min15min30min60min1hourhourintervalreturnc                 C   s~   | st dt|   }t||}|tv r|S td|}|r8t	|
ddv r8t	|
ddkr6dS |S t d|  )NzEmpty intervalz^(\d+)minute$   )rC         
         <   r2   zUnsupported interval: )
ValueErrorstrstriplower_INTERVAL_ALIASESget_VALID_KITE_INTERVALSrematchintgroup)rA   ivmr   r   r   _normalize_interval_for_kiteH   s   rW   c              	   C   s   zddl m} || }| \}}dd }||}||}W n ty0   td g }g }Y nw dt ||tt	
|| d}| d jd	did
|idd t	|}d|d	< | d | |S )z
    Try to populate db['movers'] using project scrapers if available.
    Minimal: if scrapers unavailable, returns empty lists.
    r   )TopMoversFetcherc                 S   sb   | sg S g }| D ]&}t |tr$dD ]}||r"|||  nqqt |tr.|| q|S )N)symbol
nse_symboltradingsymboltickername)
isinstancedictrO   appendrK   )arroutitemkr   r   r   _norm_   s   



z%refresh_movers_service.<locals>._normz<TopMoversFetcher not available or failed; using empty moverslatest)type
fetched_atgainerslosersmergedmoversrg   $setTupsertsnapshotmovers_history)app.v1.services.zerolive.listrX   fetch_top_moversr#   loggerinfor   utcnowlistr_   fromkeys
update_one
insert_one)dbrX   fetcherri   rj   re   recordhistory_docr   r   r   refresh_movers_serviceV   s,   
	r   c                 C   s6   | d  ddip
i }|r|dg ng }|d d S )Nrl   rg   rf   rk   rF   )find_onerO   )r{   docrk   r   r   r   get_top10_service   s   r   c                 C   s   zA| d  ddi}|rt|dtr|d W S | d i ddi}t|}|r9t|d tr<d|d v r?|W S W g S W g S W g S  tyP   td Y g S w )	Nzerodha_instrumentsrg   
nse_equityinstruments_idr   r[   zError reading instrument cache)	r   r^   rO   rw   findr_   r#   rt   	exception)r{   cachedcursorr   r   r   r   _load_instruments_cache   s$   
r   rY   c                 C   s  |sd S zl| d  d|i}|r|dr|dW S | d  ddi}|rKt|dtrK|d D ]}|d|ksB|d|krJ|d  W S q2| d  ddt| d	d
di}|rk|drn|dW S W d S W d S  ty   td| Y d S w )Nr   r[   instrument_tokenrg   r   r   rY   ^$i)z$regexz$optionsz!DB instrument lookup error for %s)	r   rO   r^   rw   rQ   escaper#   rt   r   )r{   rY   r   r   instr   r   r   _get_instrument_token_from_db   s.   &r   Fr'   forcec              
   C   s   zXt |dd}g }|pg D ]/}t|tsq||dp |d|dp)|d|dp/d |d	p7d|d
 q| d jddidd|t didd dt	|dW S  t
yp } ztd| tdddd }~ww )Nr   NSEr[   rY   r   tokenr]   r   exchange)r[   r   r]   r   rawr   rg   r   rm   )rg   r   last_updatedTrn   )okcountz!Failed to refresh instruments: %s  z*Failed to refresh instruments from Zerodhastatus_codedetail)r.   r^   r_   r`   rO   upperry   r   rv   lenr#   rt   r   r	   )r{   r'   r   r   
normalizedr   r+   r   r   r   refresh_instruments_service   s&   
(r   rC     r   	intervalsdaysmax_barsc                 C   s\  |du rg d}|t   i i i d}z)t| dd| g}t|tr2|d| ||i }n|p5i }|p9i |d< W n tyZ }	 zt	d||	 i |d< W Y d}	~	nd}	~	ww |D ]}
zt
|
}W n  ty }	 zt	d|
||	 g |d |
< W Y d}	~	q]d}	~	ww zt| d	|t  t|d
 t  |dddpg }W nM ty   zt| d	|||pg }W n ty }	 zt	d||
|	 g }W Y d}	~	nd}	~	ww Y n ty }	 zt	d||
|	 g }W Y d}	~	nd}	~	ww g }|pg | d D ],}|d}t|dr	| }|||d|d|d|d|dd q||d |
< q]|S )um   
    Fetch raw quote + candles and return them as simple arrays.
    No analysis here — raw data only.
    N)r2   r4   r6   )rY   	timestampquotecandlesmetar   zNSE:zquote fetch failed for %s: %sz-Skipping unsupported interval '%s' for %s: %sr   historical_data)r   F)r   	from_dateto_daterA   
continuousoiz8historical_data alternate signature failed for %s %s: %sz$historical_data failed for %s %s: %sdate	isoformatopenhighlowclosevolume)tohlcv)r   rv   r   r.   r^   r_   rO   r#   rt   warningrW   nowr   	TypeErrorr    r`   )r'   r   rY   r   r   r   rp   q_rawqr+   rA   kite_intervaldatara   rr   r   r   r   fetch_candles_and_quote_service   s   
	


r   c                 C   s   t | |}|s!zt| |dd t | |}W n ty    d}Y nw |s2td| tdd| dt|||}||t d}| d	 	|}|j
|d
< |S )z
    Create & persist snapshot: find token (from DB) -> fetch candles & quote -> save.
    Minimal mapping: does not perform fuzzy name mapping here.
    T)r   Nz(Instrument token not found for symbol %s  z&Instrument token not found for symbol r   )rY   rp   
created_at	snapshotsr   )r   r   r#   rt   errorr	   r   r   rv   rz   inserted_id)r{   r'   rY   r   rp   r   resr   r   r   create_snapshot_service  s    

r   snapshot_idc              
   C   sP   z| d  dt|i}|W S  ty' } ztd| W Y d }~d S d }~ww )Nr   r   zget_snapshot_service error: %s)r   r   r#   rt   r   )r{   r   r   r+   r   r   r   get_snapshot_service  s   r   textc              	   C   s   | sdS zt | W S  ty   Y nw d}d}t| D ]?\}}|dkr/|du r*|}|d7 }q|dkr[|d8 }|dkr[|dur[| ||d  }zt |W   S  tyZ   d}Y qw qdS )zD
    Extract first JSON object from text. Returns dict or None.
    Nr   {rC   })jsonloadsr#   	enumerate)r   startdepthr   ch	candidater   r   r   _extract_json_from_text$  s.   
r   respc                 C   s   t | tsdddgdS t| dd }|dvr!dddgdS | d	d}t |tr7| }|d
vr6d}nd}|| d	< || d< d| vrJdg| d< | S )zn
    Minimal validation: ensure decision exists and is one of BUY/SELL/HOLD.
    Keep other fields as-is.
    HOLDLOWzInvalid LLM response formatdecision
confidence	rationaler   r   )BUYSELLr   zInvalid decision value from LLMr   )HIGHMEDIUMr   r   zNo rationale provided by model)r^   r_   rK   rO   r   )r   r   confr   r   r   _validate_llm_response=  s    


r   rp   questionc           
      C   sZ  t std dddgdS d}| |pdd}zqt }|jjjtd	|d
dtj	|t
dd
gddtd}d}z
|jd jd }W n' tyg   zt|jd jddpUt
|}W n tyd   t
|}Y nw Y nw t|}|std|ptddd  dddgdW S t|}|W S  ty }	 ztd|	 dddt
|	 gdW  Y d}	~	S d}	~	ww )z
    Send snapshot JSON to ChatGPT, expect a single JSON object back (model must return JSON).
    Returns validated dict. No local algorithms applied.
    u2   OPENAI_API_KEY not set — returning fallback HOLDr   r   zLLM not configuredr   u  You are an intraday trading assistant. You will be given a JSON payload containing a 'snapshot' key with raw OHLCV arrays and a 'quote' key. Analyze only the provided data and return EXACTLY one JSON object with keys: decision (BUY|SELL|HOLD), confidence (HIGH|MEDIUM|LOW), optional entry:{low,high}, optional stop_loss, optional targets[], rationale[] . Return only JSON — no extra commentary. If uncertain, return decision=HOLD and explain why in rationale.r   )rp   r   system)rolecontentuser)default        i   )modelmessagestemperature
max_tokenstimeoutr   r   z2LLM did not return parseable JSON. Raw excerpt: %sNr   zLLM did not return JSONzLLM call failed: %szLLM error: )r   rt   r   r   chatcompletionscreater   r   dumpsrK   r   choicesmessager#   r!   r   r   r   )
rp   r   system_promptuser_payloadclientr   r   parsed	validatedr+   r   r   r   call_chatgpt_analyze_serviceU  sN   
 $r   r   payloadc              
   C   s8  | d}| dpd }d}|rt| |}|sBzt| ||}W n  ty)     tyA } ztd|| tdddd}~ww | d	pHi }	t|	 d
pS|	 d}
|
set	d| dddgd}nt
|	|}t|trsd|vrzdddgd}| d|t| dt| d||t d}| d | |S )z
    Create (or load) snapshot, send to ChatGPT, persist chat record, return LLM response.
    No local heuristics or fallback algorithms applied.
    r   r   r   Nz#snapshot creation failed for %s: %sr   zSnapshot creation failedr   rp   r   r   z*Snapshot for %s has no data; skipping LLM.r   r   zSnapshot emptyr   r   zInvalid LLM responserY   r   )rY   r   user_idr   llm_responser   chats)rO   rL   r   r   r	   r#   rt   r   boolr   r   r^   r_   rK   r   rv   rz   )r{   r'   r   rY   r   r   r   snapshot_docr+   rp   has_datar  chat_docr   r   r   chat_symbol_service  s@   



r  ri   2   moverlimitr   c                 C   s  g }z| d  ddipi }|dkr|dn|dp#|dp#g }dd |p*g D }|s:td	| d
g iW S |d| D ]}	zt| ||	}
|
dpOi }t||d}W nW ty } z%td|	tt	|drl|j
n| dddt| gd}d}
W Y d}~n+d}~w ty } ztd|	| dddt| gd}d}
W Y d}~nd}~ww |	|pd||
rt|
dnd|t d}z| d |}t|j}W n ty   td|	 d}Y nw |
r|
di di ni pi }|dd}||	|d|d|||
rt|
dndd}|| q@d
|iW S  ty? } ztd| g t|d W  Y d}~S d}~ww )!z
    For each mover symbol, create snapshot, call ChatGPT, persist the LLM response.
    Returns list of LLM-decisions for frontend.
    rl   rg   rf   ri   rj   rk   c                 S   s   g | ]	}t |tr|qS r   )r^   rK   )r   r   r   r   r   r     r   z,get_live_signals_service.<locals>.<listcomp>zNo movers found for mover=%sresultsNrp   )r   z)create_snapshot_service failed for %s: %sr   r   r   zSnapshot failed: r   zError processing %s: %szError: r   )rY   r   
mover_typer   r  r   streamz#Failed to persist stream doc for %sr   
last_pricer   r   )idrY   r   r   ltpllmr   z#get_live_signals_service failed: %s)r  r   )r   rO   rt   ru   r   r   r	   r   rK   r    r   r#   r   r   rv   rz   r   r`   )r{   r'   r	  r
  r   r   r  
movers_doc
candidates
raw_symbolr  rp   llm_decisionher+   
stream_docinserted	stream_idr   r  rowr   r   r   get_live_signals_service  sn   *
" 	
r  c              
   C   s  | dstdddt| d}| d d|ipi }| dd	s)td
dd| d}t| ddp6d}| d}| dd}	ttdd}
|rQt|nd| }|ra||
kratdddt| |}|sntddd|dd|d|		 d}|		 dkr|rt||d< zt
|d|}W n ty } ztd| tdt|dd }~ww ||||t d }| d! | ||d"S )#Nuser_confirmationi  zuser_confirmation requiredr   r   user_settingsr   allow_shortsFi  z User not permitted to short-sellrY   quantityr   price
order_typeMARKETMAX_NOTIONAL_PER_ORDER200000r   zOrder exceeds notional limitr   zInstrument token not foundr   r   MIS)r[   r   transaction_typer  productr!  LIMITplace_orderzOrder placement failed: %sr   )r   rY   order_payloadzerodha_responser   orders)orderr+  )rO   r	   rK   r   rS   floatosgetenvr   r   r.   r#   rt   r   r   rv   rz   )r{   r'   r   r   r   r  rY   qtyr   r!  MAX_NOTIONALnotionalr   r*  placedr+   	order_docr   r   r   place_short_order_service  sH   




r6  c                 C   sX   t  }|d | t d | d}|dv r(d| t d}|d | dd	iS )
Nzerodha_postbacks)r   received_atstatus)REJECTED	CANCELLEDCOMPLETETRIGGER_PENDINGorder_postback)rg   r   r   alertsr   T)r
   get_mongo_dbrz   r   rv   rO   )r   r{   r9  alertr   r   r   kite_postback_service  s   
rB  )F)NrC   r   )N)ri   r  NN)6typingr   r   r   r   r   r   loggingr/  r   rQ   bsonr   fastapir	   app.dbr
   app.v1.services.zerodha.clientr   openair   r#   	getLogger__name__rt   setLevelINFOr0  r   r   rS   r   r   rK   r.   rP   rN   rW   r   r   r   r   r  r   r   r   r   r   r   r   r  r  r6  rB  r   r   r   r   <module>   sP   
 - 4E	",624+2@&'