o
    I(i                    @   s	  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	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mZmZmZmZ d dlmZ d dlmZ d d	l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%Z%d dl&Z&d dl'm(Z( d dl)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4 d dl5m6Z6 d dl5m7Z7m8Z8m6Z9 d dl:m;Z;m<Z<m=Z=m>Z>m?Z? d dl@mAZAmBZB d dlCmDZD d dlEmFZF d dlGmHZHmIZI d dlJmKZK d dlLmMZMmNZNmOZO d dlPmQZQ d dlRmSZS d dlTmUZU d dlVmWZWmXZX dZYe(eYd e*eZZ[e	\de[_]e[j]se^deDe[j]Z_d Z`e; Zaeabe[ d!ea_ceajdd"eefd#d$Zfe[jgeQd%d& e[jgeSd'd& e[jgeUd(d& e[ge e[ge eWe[ZhehieU ehie e[jjd)d* Zkd+e[jld,< d-e[jld.< d/d0 Zme7e[emd1Znd2e[jld3< d4e[jld5< eMbe[ e[o  eMp  W d   n	1 sw   Y  ed6d7e[jld8< d9Zqe[rd:d;d< Zse[jjd=d> Zte[rd?d@dA Zue[rdBdCdD Zve[rdEdFdG Zwe[rdHdIdJ Zxe[rdKdLdM Zye[rdNdOdP Zze[jrdQdRgdSe>dTdU Z{e[j|dVdW Z}e[j|dXdY Z~e[jrdZd[dRgdSd\d! Ze[jrd]d[dRgdSd^d_ Ze[jrd`d[dRgdSdadb Ze[rdcddde Ze[rdfdgdh Ze[jrdid[dRgdSdjdk Ze[rdldmdn Ze[rdodpdq Ze[rdre>dsdt Ze[jrdudRgdSdvdw Ze[rdxdydz Zd{d| Zd}d~ Ze[rddd Ze[rddd Ze[rddd Zdd Zd dl mZ edddd Ze[rddd Ze[rddd Ze[rde>dd Ze[rde>dd Ze[rd%e>dd Ze[rd'e>dd Ze[rd(e>dd Ze[rde>dd Ze[rde>dd Ze[rde>dd Ze[rddd Ze[rddd Zdd Ze[jrdd[dRgdSdd Zdd Zdd Zd,ddZdd Zd,ddZeKddZddÄ Zddń Zd-ddȄZddʄ Zdd̄ Zd-dd΄Zd.ddфZe[rdҡe>ddԄ Ze	\dաe&_e	\d֡Zehjie[jrddRgdSe>ddل Ze[rdڡe>dd܄ Ze[jrddRgdSe>dd߄ ZdZdd Zehjie[jrdd[dRgdSdd Ze[jrdd[gdSdd Ze[jrdd[gdSdd Ze[jrdd[gdSdd Ze	\ddZe	\ddZehjie[ddd ZdZe	jedZe	jedZe	jedZg dZdd ZdeefddZdeefd dZƐdefddZdZe	jedZehjie[ddd Zʐd	d
 Zee	\ddZ͐de dd ddddddeedeedeede̐deede de̐deedeedB deedB fddZeϐdd e	\ddАd D Zѐd!d" Ze[rd#e>eҐd$d% Ze[rd&d'd( ZeZd)kre[jdd*d+ dS dS (/      wraps)bp)admin_bpN)Counter)datedatetimetime	timedeltatimezone)Header)MIMEMultipart)MIMEText)
formataddr)ZoneInfo)Decimal)load_dotenv)Flaskabortflashjsonifyredirectrender_templaterender_template_stringrequestsessionurl_forMarkup)
get_locale)Babel_r   )LoginManager
login_userlogout_userlogin_requiredcurrent_user)MailMessage)URLSafeTimedSerializer)OpenAI)generate_password_hashcheck_password_hash)get_schwab_client)dbUserYoutuber)ccitm_bp)neutralEdge_bp)backtestingIdea_bp)CSRFProtectgenerate_csrfz$/var/www/html/backtestingmarket/.env)dotenv_pathFLASK_SECRET_KEYu1   FLASK_SECRET_KEY no está definida en el entorno.z//var/www/html/flask_project/logs/login_logs.csvloginuser_idc                 C   s   t jt| S )z;Carga el usuario actual por ID (requerido por Flask-Login).)r.   querygetint)r8    r<   &/var/www/html/backtestingmarket/app.py	load_userJ      r>   z/ccitm)
url_prefixz/neutralEdgez/backtestingIdeac                   C   s
   t tdS )N)
csrf_token)dictr4   r<   r<   r<   r=   inject_csrf_tokend      
rC   enBABEL_DEFAULT_LOCALEtranslationsBABEL_TRANSLATION_DIRECTORIESc                  C   sN   t d} | rtjd|   | S tjddg}tjd|  |p&dS )u   
    Determina el idioma preferido:
    1) Si hay 'language' en sesión, úsalo.
    2) Si no, usa el mejor match del header Accept-Language entre ['es', 'en'].
    3) Fallback: 'es'.
    languageu   Idioma en sesión: esrE   zIdioma detectado: )r   r:   apploggerdebugr   accept_languages
best_match)idiomabestr<   r<   r=   seleccionar_idiomap   s   
rR   )locale_selectorz;sqlite:////var/www/html/backtestingmarket/instance/users.dbSQLALCHEMY_DATABASE_URIFSQLALCHEMY_TRACK_MODIFICATIONS<   )minutesPERMANENT_SESSION_LIFETIME4/var/www/html/backtestingmarket/predictor_data/data/z/set_language/<lang>c                 C   s   | t d< ttjptdS )NrI   home)r   r   r   referrerr   )langr<   r<   r=   set_language   s   r]   c                   C   s   dt t pdiS )Nlocale_actualrE   )strflask_babel_get_localer<   r<   r<   r=   inject_locale   s   ra   /c                   C      t dS )Nz	home.htmlr   r<   r<   r<   r=   rZ         rZ   z/aboutc                   C   rc   )Nz
about.htmlrd   r<   r<   r<   r=   about   re   rf   z	/servicesc                   C   rc   )Nzservices.htmlrd   r<   r<   r<   r=   services   re   rg   z	/featuresc                   C   rc   )Nzfeatures.htmlrd   r<   r<   r<   r=   features   re   rh   z/disclaimerc                   C   rc   )Nzdisclaimer.htmlrd   r<   r<   r<   r=   
disclaimer   re   ri   z/termsc                   C   rc   )Nz
terms.htmlrd   r<   r<   r<   r=   terms   re   rj   z	/settingsPOST)methodsc               
   C   s   z2t tjddt_ttjddt_tjddt_tjddt_	t
j  ttd	d
 W n tyK }  zttdd W Y d } ~ nd } ~ ww ttjpStdS )Ncontracts_per_trade   commission_per_legg      ?preferred_languagerE   
theme_modelightz'Preferencias actualizadas correctamentesuccessz)Hubo un error al guardar tus preferenciasdangerrZ   )r;   r   formr:   r%   rm   floatro   rp   rq   r-   r   commitr   r    	Exceptionr   r[   r   )er<   r<   r=   user_settings   s   
rz   c                   C   s
   dt _d S )NT)r   modifiedr<   r<   r<   r=   refresh_session   rD   r|   c                  C   sb   t jdv rd S tjr-tjtj} td}|r|| j	kr/t
  ttdd ttdS d S d S )N)r7   logoutstaticsession_tokenuI   Tu sesión ha sido cerrada porque se inició sesión en otro dispositivo.warningr7   )r   endpointr%   is_authenticatedr.   r9   r:   idr   r   r#   r   r    r   r   )userr   r<   r<   r=   check_concurrent_login   s   

r   z/loginGETc            	   
   C   s  t jstjdrttdd tjdkrtjd} tjd}t	j
j| d }|r2t|j|s?ttdd ttd	S |jsYtttd
jtd| ddd ttd	S td}||_tj  t| dt_|td< |jtd< |jtd< ttdd zAtj t!}t"t!dddd)}t#$|}|s|%g d t&'t(d}|%|j)|j*|+ g W d    n1 sw   Y  W n t,y } zt-d| W Y d }~nd }~ww |j.sttdd ttdS ttdS t/dS )NnextuL   Tu sesión ha expirado por inactividad. Por favor, vuelve a iniciar sesión.r   rk   emailpasswordr   u   Credenciales inválidasr7   u   Debes confirmar tu correo antes de iniciar sesión. Revisa tu bandeja de entrada o <a href='{url}' class='alert-link'>haz clic aquí para reenviar</a>.resend_verification)url    Tr   rq   rI   u   Inicio de sesión exitosors   a utf-8)modenewlineencoding)r   username	timestampzAmerica/New_Yorkz$Error al escribir en login_logs.csv:u5   Debes completar el pago para activar tu suscripción.stripe_paymentrZ   z
login.html)0r%   r   r   argsr:   r   r    methodru   r.   r9   	filter_byfirstr+   r   r   r   is_verifiedr   formatsecretstoken_urlsafer   r-   r   rw   r"   	permanentrq   rp   ospathexistsLOG_FILE_PATHopencsvwriterwriterowr   nowr   r   r   	isoformatrx   print	is_memberr   )	r   r   r   	new_tokenfile_existslog_filer   ny_timery   r<   r<   r=   r7      s^   






z/reset_passwordc               
   C   s  t jdkrt jd} tjj| d }|rwz:t|j	}t
d|dd}tt }|dkr9d}d	|j d
| d}nd}d|j d| d}t|dd|j	|d W n& tyv } ztd|  ttdd tt
dW  Y d }~S d }~ww ttdd tt
dS tdS )Nrk   r   r   reset_with_tokenTtoken	_externalrJ   u0   Recuperación de contraseña - BackTestingMarket
Hola uv   ,

Has solicitado restablecer tu contraseña.

Haz clic en el siguiente enlace para establecer una nueva contraseña:
uj   

Si tú no solicitaste este cambio, puedes ignorar este correo.

Gracias,
El equipo de BackTestingMarket
z"Password Reset - BackTestingMarket
Hi zV,

You requested to reset your password.

Click the link below to set a new password:
zn

If you did not request this change, you can safely ignore this message.

Thanks,
The BackTestingMarket Team
BackTestingMarketinfo@backtestingmarket.comsubjectsender_namesender_emailreceiver_emailbody[EMAIL ERROR] u7   Ocurrió un error al enviar el enlace de recuperación.rt   r7   uX   Si el correo está registrado, recibirás instrucciones para restablecer tu contraseña.infozreset_password.html)r   r   ru   r:   r.   r9   r   r   generate_reset_tokenr   r   r_   r   r   send_email_generalrx   r   r   r    r   r   )r   r   r   	reset_urlr\   r   r   ry   r<   r<   r=   reset_password2  sJ   




r   z/reset_password_token/<token>c                 C   s   t | }|sttdd ttdS tjdkrWtjd}tjd}||kr5ttdd t	d| d	S t
jj|d
 }|rWt||_tj  ttdd ttdS t	d| d	S )Nu%   El enlace es inválido o ha expirado.rt   r7   rk   r   confirm_passwordu   Las contraseñas no coinciden.znew_password.html)r   r   u/   Tu contraseña ha sido restablecida con éxito.rs   )verify_reset_tokenr   r    r   r   r   r   ru   r:   r   r.   r9   r   r   r*   r   r-   r   rw   )r   r   r   confirmr   r<   r<   r=   r   r  s"   


r   z/verify_email/<token>c                 C   s   t | }|sttdd ttdS tjj|d }|s+ttdd ttdS |j	r6ttdd nd|_	t
j  ttd	d ttdS )
Nu6   El enlace de verificación es inválido o ha expirado.rt   r7   r   zUsuario no encontrado.u8   Tu cuenta ya está verificada. Por favor inicia sesión.rs   Tu(   ¡Gracias! Tu cuenta ha sido verificada.)verify_verify_tokenr   r    r   r   r.   r9   r   r   r   r-   r   rw   )r   r   r   r<   r<   r=   verify_email  s   
r   z/resend_verificationc                  C   s   t jd} | sttdd ttdS tjj	| d
 }|rR|jsRt|j}td|dd}d	}d
|j d| d}t|dd|j|d ttdd ttdS ttdd ttdS )Nr   u"   No se proporcionó correo válido.rt   r7   r   r   Tr   &Confirma tu correo - BackTestingMarketr   ui   ,

Has solicitado reenviar el enlace para confirmar tu correo.

Haz clic aquí para verificar tu cuenta:
uP   

Si tú no solicitaste este correo, ignóralo.

El equipo de BackTestingMarket
r   r   r   u/   Se ha enviado un nuevo correo de verificación.r   z&Usuario no encontrado o ya verificado.r   )r   r   r:   r   r    r   r   r.   r9   r   r   r   generate_verify_tokenr   r   r   )r   r   r   verify_linkr   r   r<   r<   r=   r     s2   

r   z	/registerc               
   C   s@  t jdkrt jd} t jd}t jd}t jd}t jd}t jdp+d p/d }t jd	p7d p;d }t jd
}d}d}	ztj|	||dd}
|
 }W n tym   t	t
dd td| |d Y S w |dst	t
dd td| |dS tjj| d rt	t
dd td| |dS tjj|d rt	t
dd td| |dS t|}t| ||d|||d}td}||_tj| ztj  W n ty   tj  t	t
dd td| |d Y S w t|j}td|dd }tt }|d!krd"}d#|j d$| d%}nd&}d'|j d(| d)}zt|d*d+|j|d, t	t
d-d. W nc t j!yV } zt"j#$d/|j| t	t
d0d W Y d }~nDd }~w t j%yv } zt"j#&d1 t	t
d2d W Y d }~n$d }~w ty } zt"j#&d3 t	t
d4d W Y d }~nd }~ww t'td5S tdS )6Nrk   r   r   r   r   referrer_code
phone_e164r   	how_heardzg-recaptcha-response(6LfhXTUrAAAAACWDEG3jZFuMFtsG842Se5UWFcY3z/https://www.google.com/recaptcha/api/siteverify)secretresponse)datazError al verificar reCAPTCHA.rt   zregister.html)r   r   rs   zCPor favor, completa el CAPTCHA para verificar que no eres un robot.)r   u;   El nombre ya está registrado. Por favor usa uno diferente.r   r   u   El email ya está registrado.F)r   r   r   r   r   phoner   r   zError al registrar usuario.r   Tr   rJ   r   r   zm,

Gracias por registrarte en BackTestingMarket.

Por favor confirma tu correo haciendo clic en este enlace:
u[   

Si tú no solicitaste este registro, ignora este correo.

El equipo de BackTestingMarket
z&Confirm your email - BackTestingMarketr   zj,

Thank you for registering at BackTestingMarket.

Please confirm your email by clicking the link below:
zl

If you did not request this registration, you can safely ignore this message.

The BackTestingMarket Team
r   r   r   z<Registro exitoso. Revisa tu correo para confirmar tu cuenta.r   z!SMTPRecipientsRefused para %s: %suU   Tu cuenta fue creada, pero el correo de verificación fue rechazado por el proveedor.u"   Error SMTP al enviar verificaciónu   Tu cuenta fue creada, pero no pudimos enviar el correo de verificación. Inténtalo más tarde o solicita reenviar desde el login.u(   Error inesperado al enviar verificaciónuP   Tu cuenta fue creada, pero hubo un problema enviando el correo de verificación.r7   )(r   r   ru   r:   striprequestspostjsonrx   r   r    r   r.   r9   r   r   r*   r   r   r   r-   r   addrw   rollbackr   r   r   r_   r   r   r   smtplibSMTPRecipientsRefusedrK   rL   r   SMTPException	exceptionr   )r   r   r   r   r   r   r   recaptcha_response
secret_key
verify_urlr   resulthashed_passwordnew_userr   r   r   r\   r   r   ry   r<   r<   r=   register  s   







r   z/registerNewc                   C   rc   )NzregisterNew.htmlrd   r<   r<   r<   r=   registerNewC  re   r   z/logoutc                   C   s    t   ttdd ttdS )Nu   Has cerrado sesión.r   rZ   )r#   r   r    r   r   r<   r<   r<   r=   r}   H  s   r}   z/stripe_paymentc                   C   rc   )Nzstripe_payment.htmlrd   r<   r<   r<   r=   r   O     r   z/simulate_paypalc                   C   s0   t jrdt _tj  ddidfS ddddfS )	NTstatusOK   ERRORzNo user in session)r   message  )r%   r   r   r-   r   rw   r<   r<   r<   r=   simulate_paypalU  s
   
r   z
/graficoICc                   C   rc   )NzgraficoIC.htmlrd   r<   r<   r<   r=   	graficoIC^  re   r   c                 C   s   t |  	 tt|  d }g }|D ];}|drM|drMz|dd dd}|| W q t	yL } zt d| d|  W Y d }~qd }~ww qt
|d	d
S )Nrb   prediction_.csvr    r   zError procesando archivo : T)reverse)r   r   listdirPATH_UBUNTU
startswithendswithsplitreplaceappendrx   sorted)symbolfilesdatesfile	date_partry   r<   r<   r=   get_available_datesd  s    r  c              
   C   s   | dd}t d| d| d|  d}ztj|dgd}|d	jd
dW S  tyE } ztd|  dt|iW  Y d}~S d}~ww )u.   Lee los datos del archivo CSV según la fecha.$r   rb   /prediction_r    r   r   parse_datesi  recordsorientError leyendo el CSV: errorN)	r   r   pdread_csvtailto_dictrx   r   r_   )selected_dater   foldercsv_filedfry   r<   r<   r=   r  s  s   r  z/get_prediction_datac            !         sP  t jd} t jdt jd}|dv rd| }n|}d| d}d}tj|d	| d
|  d}tj|d| d
|  d}td|  tj|rUtj|s]tddidfS t	
|}t	
|}t	|d |d< |d jd|d< ||d jjtddtdd }||d jjd dk }fdd}	|j|	dd|g d< t	|d |d< |d  }
||d |
k  g d}| |v }|r|
 tddk}n	|
 td dk}|r d! jd nd  fd"d#}fd$d%}|r|j|dd|d&< n	|j|dd|d&<  d! jd }g }g }g }| D ]\}}zt|d' dd }t|| d(}t|| d) d(}|d' d}t|d }t|d }t||g\}}|d*  }|d+kr}||k rid,}n7||  kru|k rzn nd-}n&d.}n#|d/kr||krd,}n||  k r|krn nd-}nd.}nd0}W n
   d1}d1}d0}Y || || || q||d2< ||d3< ||d4< t| d5d6|d7< |d8 |d8< |d9 |d9< |d: |d:< |d; |d;< |d< |d<< |d= |d=< |d> |d>< |d? |d?< |d@ |d@< |g dA } tt|rn d! jd d(| jdBdCdDS )ENr   riskr   SPXRUTXSPr  rY   rb   #/var/www/html/flask_project/chains/r   r    r   optionChain_[DEBUG] pred_file: r
  Archivo no encontrado  r   %H:%M:%SHora
            r   c              
      s  zeddddd}|  d}|  |d}d}td|}|s%td	| |d
}td|d}t|dd}td|d
}	ttd|d
d }
d|v rXdnd}t	
||||	|
|gW S  ty } ztd|  t	
g dW  Y d }~S d }~ww )NIDEA	IDEA_adj5
IDEA_adj10
IDEA_adj15conservador
intermedioagresivoultra_agresivor   VERTICAL\b(SPX|RUT|QQQ|XSP|SPY)\b   Símbolo no detectado en idea: rn   \d{2} \w{3} \d{2}r   %d %b %y%m/%d/%Yz#\d{2} \w{3} \d{2} (\d{2,5}/\d{2,5})
@([0-9.]+)d   PUTCALLz[Error parse_vertical] )r   r   r   r           r   r:   research
ValueErrorgroupr   strptimestrftimerv   r  Seriesrx   r   )rowidea_col_mapidea_colidea_str
estrategiasimbolo_matchsimboloexpiracion_raw
expiracionstrikescreditoladory   r  r<   r=   parse_vertical  s0   
z+get_prediction_data.<locals>.parse_verticalrn   axis)
EstrategiaSimbolo
ExpiracionStrikesCreditoLado)z
2025-07-03z
2025-11-29z
2025-12-24      underlying_pricec              
      sd  zt t| d d\}}| d  }| d }|dkrN  d |k jd }  d |k jd }t|d t|d	  d
 }t|d t|d	  d
 }n:|dkr  d |k jd }  d |k jd }t|d t|d  d
 }t|d t|d  d
 }nW dS || }	t||	d  d
}
|
W S  ty } ztd|  W Y d }~dS d }~ww )NrR  rb   rT  rS  r4  striker   bid_putask_put   r5  bid_callask_callr6  r3  z[Error MidPrice] )	mapr;   r   upperilocrv   roundrx   r   )r?  sell_strike
buy_strikerJ  rI  sell_rowbuy_rowsell_midbuy_midspread_valueplry   df_last_chainr<   r=   calc_pl_opciones  s.   z-get_prediction_data.<locals>.calc_pl_opcionesc                    s   zFt t| d d\}}| d  }| d }t|| }|dkr)td|  }n|dkr5td | }nW dS t||}t||d	  d
W S    Y dS )NrR  rb   rT  rS  r4  r   r5  r6  r3  r[  )r^  r;   r   r_  absmaxminra  )r?  rb  rc  rJ  rI  width	intrinsic	spx_closer<   r=   calc_pl_cierre  s   
z+get_prediction_data.<locals>.calc_pl_cierreP/LrR  r[  r3  rT  r4  ITMITM POTMr5  N/Ar6  DiffPtsDiffPct	Moneyness%Y-%m-%dr1  rQ  precio_actualtendencia_30mintendencia_cortascore_30min
strikes_OIstrikes_GEXstrikes_Volumen	total_pos	total_neg)r  rO  rP  rQ  rT  rR  rS  rz  r{  r|  ru  r~  r  r  r  r  r  r  r  r  r  r  	spx_finalr   )r   r   r:   r   r   joinr   r   r   r  r  to_datetimedtr=  r	   betweenminuteapplyrn  r`  iterrowsr;   r   ra  r   r_  r   r   r<  r  )!r   symbol_inputr   base_path_predictionsbase_path_chains	pred_file
chain_filedf_preddf_chainrL  last_snapshotEARLY_CLOSE_DATESis_early_closeusar_precio_finalrl  rt  rW  diff_pts_listdiff_pct_listmoneyness_listr    r?  rb  diff_ptsdiff_pctstrikes_rawstrike1strike2
strike_lowstrike_highrJ  	moneynessdf_finalr<   )rk  r  rs  r=   get_prediction_data  s   

$






r  z/get_prediction_data_icc                     s  t jd} t jdt jd}|dv rd| }n|}d| d}d}tj|d	| d
|  d}tj|d| d
|  d}td|  tj|rUtj|s]tddidfS t	
|}t	
|}t	|d |d< |d jd|d< ||d jjtddtdd }||d jjd dk }t	|d |d< |d  }	||d |	k  |	 tddk}
|
r d jd nd fdd}|j|dd|g d< ||d  d!k } fd"d#}fd$d%}|
r|j|dd|d&< n	|j|dd|d&< |
rn d jd fd'd(}|j|dd|g d)< t| d*d+|d,< |d- |d-< |d. |d.< |d/ |d/< |d0 |d0< |d1 |d1< |d2 |d2< |d3 |d3< |d4 |d4< |d5 |d5< |g d6 }ttd7|jd8d9d:S );Nr   r  r   r  r  rY   rb   r  r   r    r   r  r  r
  r  r  r   r  r  r  r   r!  r"  r   rV  rW  c              
      s  zlddddd}|  d}|  |d}d}td|}|s%td	| |d
}td|d}t|dd}td|d
}	td|d
}
|
 d|	 }ttd|d
d }t	
|||||gW S  ty } ztd|  t	
g dW  Y d }~S d }~ww )NIDEA_ICIDEA_IC_adj5IDEA_IC_adj10IDEA_IC_adj15r'  r   IRON CONDORr-  r.  rn   r/  r   r0  r1  z\[CALLS (\d{2,5}/\d{2,5})\]z\[PUTS (\d{2,5}/\d{2,5})\]-r2  r3  z[Error parse_ic] )r   r   r   r   r6  r7  )r?  r@  rA  rB  rC  rD  rE  rF  rG  calls_strikesputs_strikes
strikes_icrI  ry   rK  r<   r=   parse_ic  s2   
z(get_prediction_data_ic.<locals>.parse_icrn   rM  )rO  rP  rQ  
Strikes_ICrS  rO  r  c              
      sj  z| d  d\}}tt| d\}}tt| d\}}  d |k jd }  d |k jd }  d |k jd }	  d |k jd }
|d |d  d }|d |d  d }|	d	 |	d
  d }|
d	 |
d
  d }t|| }t|| }t|| }|| }t||}t| d |d  d}|W S  ty } ztd|  W Y d }~dS d }~ww )Nr  r  rb   rX  r   rY  rZ  r[  r\  r]  rS  r3  z[Error calc_pl_opciones_ic] r6  )	r   r^  r;   r`  rm  ro  ra  rx   r   )r?  	puts_part
calls_partput_sellput_buy	call_sellcall_buyput_sell_rowput_buy_rowcall_sell_rowcall_buy_rowmid_put_sellmid_put_buymid_call_sellmid_call_buyput_spread_valuecall_spread_valuespread_widthtotal_spread_valueri  ry   rj  r<   r=   calc_pl_opciones_ic  s0   
z3get_prediction_data_ic.<locals>.calc_pl_opciones_icc              
      s   zH| d  d\}}tt| d\}}tt| d\}}t|| }td|  }td | }	||	 }
t|
|}
t| d |
d  d}|W S  tyb } ztd|  W Y d }~d	S d }~ww )
Nr  r  rb   r   rS  r3  r[  z[Error calc_pl_cierre_ic] r6  )	r   r^  r;   rm  rn  ro  ra  rx   r   )r?  r  r  r  r  r  r  r  intrinsic_putintrinsic_callintrinsic_totalri  ry   rr  r<   r=   calc_pl_cierre_ic  s    
z1get_prediction_data_ic.<locals>.calc_pl_cierre_icru  c              
      s,  zu| d  d\}}tt| d\}}tt| d\}}t | d}t | d}t|| d d}	t|| d d}
 |krDdn|   k rN|krRn ndnd} |k rZdn|   krd|k rhn ndnd}t|||	|
||gW S  ty } ztd	|  tg d
W  Y d }~S d }~ww )Nr  r  rb   r[  r3  rx  rw  rv  z[Error calc_distancias] )r6  r6  r6  r6  ry  ry  )r   r^  r;   ra  r  r>  rx   r   )r?  r  r  r  r  r  r  diff_pts_putdiff_pts_calldiff_pct_putdiff_pct_callmoneyness_putmoneyness_callry   )rW  r<   r=   calc_distancias  s,   z/get_prediction_data_ic.<locals>.calc_distancias)DiffPts_PUTDiffPts_CALLDiffPct_PUTDiffPct_CALLMoneyness_PUTMoneyness_CALLr}  r1  rQ  r~  r  r  r  r  r  r  r  r  )r  rO  rP  rQ  r  rS  ru  r  r  r  r  r  r  r~  r  r  r  r  r  r  r  r  r[  r  r  r  )r   r   r:   r   r   r  r   r   r   r  r  r  r  r=  r	   r  r  rn  r`  r  r   r<  ra  r  )r   r  r   r  r  r  r  r  r  r  r  r  r  r  r  r   r<   )rk  r  rs  rW  r=   get_prediction_data_icX  sd   

$ 
r  z	/data_gexc                  C   sJ   t jd} t jdd}t jdd}| st d} tt| ||S )Nr   horarioz11:15r   r  r}  )r   r   r:   r   todayr=  r   read_csv_gex)r  r  r   r<   r<   r=   get_data_gex  s   r  c              
   C   sP  t d| h d}||v rd| }n|}d| d|  d}ztj|dgd}t|  d	| }|d |   }|j|df }	||d |	k  }
d
}d}|rd}|
d ||
d   |
d< |
d ||
d   |
d< |
d |
d  | |
d< |
d |
d  | |
d< n|
d |
d  | |
d< |
d |
d  | |
d< |
d  |
jd d df< |
d	dddd
 }|d |d  |jd d df< ||d dk }|jd d df  d  < |jd d df  d  < |jd d df  d  < |	d|jd d!d"}t | |W S  ty' } zt d#|  d$t|iW  Y d }~S d }~ww )%Nzhora:>   r  r  r  r  z//var/www/html/flask_project/chains/optionChain_r    r   r   r   r3  Tia  open_interest_callvolume_callestimated_OI_callopen_interest_put
volume_putestimated_OI_put
gamma_callGEX_call	gamma_putGEX_putGEX_put_negrX  r   sum)rW  r  r  GEX_netr   g    .Az%Y-%m-%d %H:%M:%Sr  r  )snapshot_timer   r	  r
  )r   r  r  r  rm  idxminloccopygroupbyaggreset_indexr=  r  rx   r_   )r  r  r   indicessymbol_filenamer  r  target_timeidxtarget_snapshot_timesnapshot_dfmultiplicadorusar_volumen_intradiaalphagroupedr   ry   r<   r<   r=   r  %  sZ   


r  )	lru_cacherV  )maxsizec                 C   s   t jt| }zt |}W n ty   g  Y S w g }|D ];}|drY|drYz|dd 	dd}|
| W q tyX } ztd| d|  W Y d}~qd}~ww qt|S )	u   
    Versión cacheada en memoria de las fechas disponibles para un símbolo.
    Se ejecuta una sola vez por valor distinto de `symbol`
    dentro de cada proceso de WSGI.
    r   r   r    r   r   z[Error procesando archivo r   N)r   r   r  r   r   FileNotFoundErrorr   r   r   r   r   rx   r   r   )r   r   r   r   r   r   ry   r<   r<   r=   get_available_dates_cachedz  s$    r  z
/get_datesc                  C   s2   t jd} td|  | stg S t| }t|S )Nr   z[DEBUG] selected_symbol:)r   r   r:   r   r   r  )selected_symbolfechasr<   r<   r=   	get_dates  s   
r  z/datac                  C   sP   t jd} t jd}|dv rd| }n|}| s!t d} tt| |S )Nr   r   r  r  r}  )r   r   r:   r   r  r=  r   r  )r  r  normalized_symbolr<   r<   r=   get_data  s   r  z
/predictorc                   C      t dtddS )Nzpredictor.html	Predictorherramienta_actualr   r    r<   r<   r<   r=   predictor_page  r?   r	  z
/trendIdeac                   C   r  )NztrendIdea.htmlz
Trend Idear  r  r<   r<   r<   r=   	trendIdea  r?   r
  c                  C   s4   t d} | j }| jdd}td||tddS )Nz&/var/www/html/flask_project/IVRank.csvr  r  z
ccitm.htmlITMCC)headersr   r  )r  r  columnstolistr  r   r    )r  r  r   r<   r<   r=   
ccitm_page  s   

r  c                   C   r  )NzneutralEdge.htmlzNeutral Edger  r  r<   r<   r<   r=   neutralEdge_page  r?   r  c                   C      t dtdtjtjdS )NzbacktestingIdea.htmlBacktesting Idear  user_contractsuser_commissionr   r    r%   rm   ro   r<   r<   r<   r=   backtestingIdea_page     r  z/backtestingIdea1c                   C   r  )NzbacktestingIdea1.htmlr  r  r  r<   r<   r<   r=   backtestingIdea1_page  r  r  z/backtestingIdeaDashboardc                   C   r  )NzbacktestingIdeaDashboard.htmlr  r  r  r<   r<   r<   r=   backtestingIdeaDashboard_page  r  r  z/compareIdeac                   C   r  )NzcompareIdea.htmlzComparar Estrategiasr  r  r<   r<   r<   r=   compareIdea_page  r?   r  z/trial_successc                   C   rc   )Nztrial_success.htmlrd   r<   r<   r<   r=   trial_success  re   r  z/generar_informe_aic                  C   s|  zt jdd } t d}| dv rdnd}d|  d| |  d	| d
}t|}|j	d }|d }|d }|d }|d }	t
|d d}
tt }|dkrgd|  d| d| d| d|	 d|
 d}nd|  d| d| d| d|	 d|
 d }ttd!d"}|jjjd#d$d%d&d'|d&gd(}|jd) jj}t|}t| d*| W S  ty   tt dkrY d+S Y d,S w )-Nr   r  r}  r  r  r   rY   r  r    r   r   r   r~  r  r  r     rJ   uW   
Eres un analista financiero profesional. Con base en los siguientes datos del índice uU   , genera un análisis claro, profesional y técnico en español:

- Última lectura: z
- Precio actual: u   
- Tendencia intradía: z
- Tendencia corta: z
- Score de momentum: u   

🛑 No muestres, evalúes ni describas ninguna estrategia de opciones como Iron Condor, Vertical, Spread, Calls o Puts.

Limítate a analizar: la fuerza de tendencia, el momentum, y cualquier señal de dirección probable del mercado.
zO
You are a professional financial analyst. Based on the following data for the z[ index, generate a clear, professional and technical analysis in English:

- Last reading: z
- Current price: z
- Intraday trend: z
- Short-term trend: z
- Momentum score: u   

🛑 Do not mention, evaluate or describe any options strategies like Iron Condor, Vertical, Spread, Calls or Puts.

Focus only on analyzing trend strength, momentum, and any potential directional market signal.
OPENAI_API_KEY)api_keyzgpt-4osystemu  You are an expert in index and options trading.
- Treat 'Intraday trend' as the expected direction until end of day (0DTE), not just past 30 minutes.
- The 'score' ranges from -0.5 to 0.5 and indicates momentum strength and direction:
  • From -0.05 to 0.05 = weak trend (easily changeable).
  • From ±0.05 to ±0.15 = medium strength.
  • Greater than ±0.15 = strong trend.
- Positive score = bullish tendency, negative = bearish.
Use this to enhance your interpretation.)rolecontentr   )modelmessagesr   z<hr>uL   ❌ No fue posible generar el informe. Intente nuevamente en unos mintuos...uG   ❌ Unable to generate the report. Please try again in a few minutes...)r   r   r:   r_  r   r  r=  r  r  r`  ra  r_   r`   r)   r   getenvchatcompletionscreatechoicesr   r"  analizar_strikes_dominantesr   rx   )r   r  prefixcsv_pathr  last_rowr   precio	tendenciacortascorerP   promptclientr   
informe_aiinforme_gexr<   r<   r=   generar_informe_ai  sr   



r6  c           	   	   C   s   t t }t }| d D ]-}z"t|}t|tr/t|dkr/tt	|d }||  d7  < W q t
y9   Y qw |d}|sI|dkrGdS dS dd	 |D }|d d }|dkrfd
d| d| dS dd| d| dS )Nr  r   rn   r   rJ   u/   No se detectaron strikes dominantes en el día.z#No dominant strikes detected today.c                 S   s   g | ]}t |d  qS )r   r_   ).0sr<   r<   r=   
<listcomp>h  s    z/analizar_strikes_dominantes.<locals>.<listcomp>u[   📌 **Strikes Dominantes del Día:**
Los strikes más frecuentemente dominantes han sido: z, u%   .
El strike más persistente es el **u   **, lo cual sugiere una zona clave de control gamma o riesgo.

Este análisis puede utilizarse para identificar niveles potenciales de soporte/resistencia intradía.
uQ   📌 **Dominant Strikes of the Day:**
The most frequently dominant strikes were: z#.
The most persistent strike was **z**, suggesting a key gamma or dealer risk control zone.

This analysis can help identify potential intraday support/resistance levels.
)r_   r`   r   r   loads
isinstancelistlenra  rv   rx   most_commonr  )	r  rP   countervalparsedrX  strikes_ordenadostop_strikesmas_dominanter<   r<   r=   r*  V  s8   


r*  z/contactc               
   C   s   t jdkrYt jd } t jd }t jd }d|  d| d| d}ztd	| |d
|d ttdd W n  tyR } zttdd td|  W Y d }~nd }~ww tt	dS t
dS )Nrk   namer   r   z$Nuevo mensaje de contacto:

Nombre: z
Email: z

Mensaje:

zMensaje desde el sitio webr   r   z)Tu mensaje ha sido enviado correctamente.rs   u>   Ocurrió un error al enviar el mensaje. Inténtalo más tarde.rt   r   contactzcontact.html)r   r   ru   r   r   r    rx   r   r   r   r   )rF  r   r   cuerpory   r<   r<   r=   rH  {  s6   



	rH  c                 C   s   d}d}d}t jd}t }	ttt|d|f|	d< ||	d< t| d|	d< |	t|d	d t	
||}
|
  |
|| |
|||	 d W d    d S 1 sXw   Y  d S )
Nzsmtp.titan.emailiK  r   EMAIL_PASSWORDr   FromToSubjectplain)r   environr:   r   r   r_   r   attachr   r   SMTPstarttlsr7   sendmail	as_stringencode)r   r   r   r   r   email_server
email_portemail_usernameemail_passwordmsgserverr<   r<   r=   r     s   "r   c                 C      t j| ddS )Nreset-password-saltsalt
serializerdumps
user_emailr<   r<   r=   r        r     c                 C   ,   zt j| d|d}W |S  ty   Y d S w )Nr]  r_  max_agera  r;  rx   r   
expirationr   r<   r<   r=   r        r   c                 C   r\  )Nemail-verify-saltr^  r`  rc  r<   r<   r=   r     re  r   c                 C   rg  )Nrn  rh  rj  rk  r<   r<   r=   r     rm  r   Tverbosec                  C   s  t dd} | s	g S ddddddd	d
}dd }|| jt| }i }|D ]}|| j|d}|||< q%g }| D ]\}}||i }	||i }
|
dg }t|tr`|r`|d di }ni }td| dt	j
|dd  |	di dp|	di dp|	di d}|	di dp|	di dp|	di d}|	di dp|pd}|rt|| d dnd}|d p|d!}|d"p|d#p|d$}t|d%p|d&pd}|d'd(}t|| }t|}||d)|  d*||r	t|dnd ||rt|dnd |rt|dnd |d ur$|nd+|t||t|| t|| d,}td-| d. || q9td/ td/ t|D ]\}}td0| d1t| d2|  qO|D ]Z}ttd3td|d4 d5 d6|d7< t|d8 ttfrt|d8 d9 d9 d6nd|d:< |d; rttd|d; d< d6nd|d=< |d> rttd3td|d> d d6nd|d?< qftd@ t|D ]%\}}td0| d1|dA  dB|d7  dC|d:  dD|d=  dE|d?   q|S )FNTro  Apple	MicrosoftAmazonzAlphabet (Google)zMeta PlatformsTeslaNvidia)AAPLMSFTAMZNGOOGLMETATSLANVDAc                 _   s   z
| |i |  W S  tyY } zCtd| d tdd}zt|| j|i |  W W  Y d }~S  tyT } ztd|  i W  Y d }~W  Y d }~S d }~ww d }~ww )Nu   ⚠ Error en llamada Schwab: z. Reintentando...Fro  u    ❌ Falla definitiva en Schwab: )r   rx   r   r,   getattr__name__)funcr   kwargsry   
new_cliente2r<   r<   r=   	safe_call  s   
(z'get_magnificent_data.<locals>.safe_callfundamentalinstrumentsr   u   🔍 FUNDAMENTAL de z:
r[  )indentquote	lastPriceregularregularMarketLastPriceextended	netChangeregularMarketNetChange
markChange
closePricern   r3  r6  peRatiope_ratioepsTTMepseps_ttm	marketCap
market_capsectoru   Tecnologíaz/static/images/logos/z.pngNone)rF  tickerlogor  price
change_pctr  r  rsitrend_labelr  market_cap_rawatr
volume_relz[DEBUG] company_data = u    ✅z=== DEBUG FINAL: companies ===[z] z -> r   r  r   r  	pct_scorer  2   	rsi_scorer  r  	atr_scorer  	vol_scorez=== DEBUG FINAL CON SCORES ===r  z -> pct_score: z, rsi_score: z, atr_score: z, vol_score: )r,   quotesr=  keysr  itemsr:   r<  r   r   rb  ra  r;   calcular_rsiinterpretar_rsilowerformat_market_capcalcular_atrcalcular_volumen_relativor   	enumeratetypern  ro  rv   )r3  tickersr  r  fundamentalsr   r   	companiesrF  qraw_fundamentalr  fr  
net_changeprevious_closer  r  r  r  r  r  r  company_datar  cr<   r<   r=   get_magnificent_data  s   



""2*4r  c                 C   sP   | dkr| d ddS | dkr| d ddS | dkr$| d ddS t | S )Nl    J)z.1fTi ʚ;Bi@B Mr7  )valuer<   r<   r=   r  =  s   r     c              
   C   sL  z| |  }dd |dg D | d d  }t||d k r%W d S t|}t|dk|d}t|dk | d}t|d | }t|d | }	t|t|D ]}
||d  ||
  | }|	|d  ||
  | }	qV|	dkry||	 nd}ddd|   }t	|dW S  t
y } ztd|  d	|  W Y d }~d S d }~ww )
Nc                 S   s   g | ]}|d  qS )closer<   r8  r  r<   r<   r=   r:  K  s    z calcular_rsi.<locals>.<listcomp>candlesr[  rn   r   r3  zError calculando RSI para r   )price_historyr   r:   r>  npdiffwheremeanrangera  rx   r   )r   r3  periodr  closesdiffsgainslossesavg_gainavg_lossirsr  ry   r<   r<   r=   r  G  s*   $
r  c                 C   sX   | d u rdS | dk rdS | dkrdS d|   k rdk rdS  | dkr$d	S | dkr*d
S dS )NzN/Dr"  SobrevendidoF   Sobrecomprado-   7   NeutroAlcistaBajistar<   )r  r<   r<   r=   r  d  s   r  c                 C   s.  d}d}| D ]}| ddpd}| ddpd}| ddpd}| ddp'd}| ddp/d}td	td|d
 }	|d d }
td|d }td	td|d }t|	d|d< t|
d|d< t|d|d< t|d|d< |	d |
d  |d  |d  }||| 7 }||7 }q|r|| d nd}t|dS )u   
    Combina: % cambio, RSI, ATR, Vol Rel, ponderado por Market Cap.
    También guarda cada sub-score por símbolo para graficar.
    r   r  r  r  r  r  rn   r  r   r   r     r  r  r  r  g333333?g?r3  r[  )r:   rn  ro  ra  )r  total
weight_sumr  pctr  r  vol_relmcapr  r  r  r  finalr1  r<   r<   r=   calculate_market_scoret  s8   

r  c                 C   s   | |  dg }t||d k rdS g }tdt|D ]+}|| d }|| d }||d  d }t|| t|| t|| }	||	 qt|| d | }
t	|
dS )uH   
    Calcula ATR (Average True Range) básico de velas históricas.
    r  rn   Nhighlowr  r[  )
r  r   r:   r>  r  rn  rm  r   r  ra  )r   r3  r  r  	tr_valuesr  r  r  
prev_closetrr  r<   r<   r=   r    s    
r     c                 C   st   | |  dg }t||d k rdS |d d }tdd || d d D | }|dkr3dS t|| dS )	u=   
    Volumen actual dividido promedio últimos 20 días.
    r  rn   r   volumec                 s   s    | ]}|d  V  qdS )r  Nr<   r  r<   r<   r=   	<genexpr>  s    z,calcular_volumen_relativo.<locals>.<genexpr>r   r[  )r  r   r:   r>  r  ra  )r   r3  daysr  today_volume
avg_volumer<   r<   r=   r    s   $r  z/magnificentc                  C   sj   t  } | D ]}t|d t|d< t|d t|d< t|dd|d< qt| }td| |tddkd	S )
Nr  r  r  r  rn   zmagnificent.html	themeModedark)r  market_score	dark_mode)	r  r  schwab_clientr  r;   r:   r  r   r   )r  r  r  r<   r<   r=   magnificent_page  s   r  STRIPE_SECRET_KEYSTRIPE_WEBHOOK_SECRETz/create-checkout-sessionc                  C   s  t jddpi } | d}d}d}tjpd }|r#tjj|d	 nd }|dkr.|}d	}n|d
kr7|}d}nt
dddfS d}ttdd rHd}i }	|dkrT|rT||	d< |ttj||rat|jndd|	d< i }
ttdd }|rvd|i}
n0d }ztjjdtj ddd}|jr|jd j}W n	 ty   Y nw |rd|i}
ndtji}
dgd|ddg|	d |
td!dd"d# td$dd"ttj|||rt|jndd%ttjd&}d}|r&|jr&z*tjj|jddd'}|jr|jd j}ntjj|jd(}|j}d)|ig|d*< d}W n ty% } ztd+| d, W Y d }~nd }~ww |s-d|d-< ztjjjd3i |}t
|j|jd.d/fW S  ty } zutd0|  |rd*|v rz-t|}|d*d  d|d-< td1 tjjjd3i |}t
|j|jd.d/fW W  Y d }~S  ty } ztd2|  t
t|ddfW  Y d }~W  Y d }~S d }~ww t
t|ddfW  Y d }~S d }~ww )4NTsilent	plan_typeprice_1RjuuYEiGxFFi4pPy361fueeprice_1RjuuYEiGxFFi4pPXIh1pcNxr   r   mensual   anualr!  u   plan_type inválido)r
  r   subscription_idFr   trial_period_days)r  r8   r   youtuber_idmetadatastripe_customer_idcustomerzemail:''r   )r9   limitcustomer_emailcardsubscriptionrn   )r  quantity)payment_method_typesr   
line_itemssubscription_datapayment_success_striper   z!?session_id={CHECKOUT_SESSION_ID}r   )r8   r  r   r  )success_url
cancel_urlr  client_reference_id)couponactiver  )r  promotion_code	discountsz,[checkout] promo apply failed (pre-create): u     — continuing without discountallow_promotion_codes)r   	sessionIdr   z!>> Stripe error (first attempt): u7   [checkout] retrying Session.create WITHOUT discounts…z+>> Stripe error (retry without discounts): r<   )r   get_jsonr:   r%   r   r   r/   r9   r   r   r   r}  r_   r   stripeCustomerr9  r   r   rx   r   stripe_coupon_idPromotionCoder=  r(  r   checkoutSessionrB   pop)r   r  NORMAL_MONTHLYNORMAL_ANNUALr   yprice_id
trial_dayseligible_for_trialr  customer_paramexisting_customer_idfound_idressession_kwargsdiscounts_appliedpromo_codespromotion_code_idpromory   r   sk2r  r<   r<   r=   create_checkout_session/  s   






"*r3  z/payment_success_stripec               
   C   s$  t jd} | sttdd ttdS zVtjj	j
| ddgd}|jj}|d d d	 d
 }|d }|di }|dd}tjrPdt_|t_|t_tj  tdtj d| d|  ttdd td|dW S  ty } ztd|  ttdd ttdW  Y d }~S d }~ww )N
session_idzNo session ID found.rt   indexr  r  expandr   r   r  r   r  	plan_tipodesconocidoTz[payment_success] user_id=z plan=z
 price_id=u<   Pago realizado con éxito. ¡Bienvenido a BackTestingMarket!rs   zpayment_success.htmlr  z [payment_success_stripe] Error: z"Hubo un error al procesar tu pago.)r   r   r:   r   r    r   r   r  r   r!  retriever  r   r%   r   r   r  membership_typer-   r   rw   	debug_logr   rx   	error_log)r4  r   r  
price_datar&  r  r<  ry   r<   r<   r=   r    s8   
r  z/billing-portalc                  C   s   t tdd } | s?t tdd }|r?tjj|dgd}t|jdr#|jjn|j} z
| t_t	j
  W n ty>   t	j
  Y nw | sNttdd ttdS tjjj| td	d
dd}t|jddS )Nr  r  r  r6  r   uA   Aún no tienes un cliente en Stripe. Elige un plan para comenzar.r   r   rZ   Tr  )r  
return_urli/  code)r}  r%   r  Subscriptionr;  hasattrr  r   r  r-   r   rw   rx   r   r   r    r   r   billing_portalr!  r(  r   )customer_idsub_idsubportalr<   r<   r=   rE    s(   
rE  zv1.3-2025-10-17c              	   C   s2   t jj| jtd| jdddtd| jdddddS )Nonboarding_startThttps)rB  r   _schemeonboarding_statusaccount_onboarding)accountrefresh_urlr@  r  )r  AccountLinkr(  stripe_account_idr   r   )r%  r<   r<   r=   _create_account_linkG  s   rS  z/onboarding/start/<code>c              
   C   s  t jj| d }|sdS |jsdS tjdkrtjdsdS z(tj	dtj
}ttj }tjdp7t}tjj|jd	|||d
d W n tyX   tjjddd Y nw z
t|}t|jW S  ty } ztjd d| dfW  Y d }~S d }~ww td| tdS )Nr  )z+No existe youtuber con ese code (revisa DB)r  )zYoutuber sin stripe_account_idr   rk   agree)u+   Debes aceptar los Términos para continuar.r   zX-Forwarded-Foragreement_versiontrue)affiliate_agreement_acceptedaffiliate_agreement_versionaffiliate_agreed_ataffiliate_agreed_ipr  z2[onboarding] No se pudo guardar metadata en StripeT)exc_infoz$Error creando Account Link de StripezError creando Account Link: i  zonboarding_agreement.html)rB  rU  )r/   r9   r   r   rR  r   r   ru   r:   r  remote_addrr   r   r   utcr   AGREEMENT_VERSIONr  Accountmodifyrx   rK   rL   r   rS  r   r   r   r   )rB  r%  ipts_isovlinkry   r<   r<   r=   rJ  Q  sF   

	rJ  z/onboarding/refresh/<code>c                 C   s   t | S N)rJ  rA  r<   r<   r=   onboarding_refresh}  r   rg  z/onboarding/status/<code>c                 C   s   t jj| d }|r|jsdS tj|j}|dd}|dd}|dd}|di p0i }|dg }|d	g }|d
g }	|oI|oI| }
d}t	||
||||||	| d	S )z4Check in Stripe if onboarding is actually completed.r  )z,Creator not found or without Stripe account.r  details_submittedFpayouts_enabledcharges_enabledrequirementscurrently_duepast_dueeventually_dueu;  
    <div style="font-family:system-ui, -apple-system, Segoe UI, Roboto; max-width: 760px; margin: 32px auto; padding: 16px;">
      <h2 style="margin-bottom: 12px;">Onboarding Status</h2>

      {% if completed %}
        <p>✅ <strong>All set!</strong> Your Stripe account is ready to receive payouts.</p>
        <ul>
          <li><code>details_submitted</code>: {{ details_submitted }}</li>
          <li><code>payouts_enabled</code>: {{ payouts_enabled }}</li>
          <li><code>charges_enabled</code>: {{ charges_enabled }}</li>
        </ul>
      {% else %}
        <p>⚠️ <strong>Action required.</strong> Your onboarding is not finished yet.</p>
        <p><strong>Pending requirements (<code>currently_due</code>):</strong></p>
        <pre style="background:#f6f8fa; padding:12px; border-radius:6px; overflow:auto;">{{ currently_due or "—" }}</pre>

        {% if past_due %}
          <p><strong>Past due:</strong></p>
          <pre style="background:#f6f8fa; padding:12px; border-radius:6px; overflow:auto;">{{ past_due }}</pre>
        {% endif %}

        {% if eventually_due %}
          <p><strong>Eventually due:</strong></p>
          <pre style="background:#f6f8fa; padding:12px; border-radius:6px; overflow:auto;">{{ eventually_due }}</pre>
        {% endif %}

        <p style="margin-top: 12px;">
          <a href="{{ url_for('onboarding_start', code=code, _external=True) }}">Resume onboarding on Stripe</a>
        </p>
        <small style="color:#666;">If you closed the window or the link expired, click “Resume onboarding” again to get a fresh link.</small>
      {% endif %}

      <hr style="margin:24px 0;">
      <p style="color:#666; font-size: 14px;">
        Having trouble? Reply to this email or contact support and include your referral code: <code>{{ code }}</code>.
      </p>
    </div>
    )	completedrh  ri  rj  rl  rm  rn  rB  )
r/   r9   r   r   rR  r  r`  r;  r:   r   )rB  r%  acctrh  ri  rj  reqsrl  rm  rn  ro  htmlr<   r<   r=   rM    s0   
'rM  z/onboarding/success/<code>c                 C   s   t td| dS )NrM  rA  )r   r   rA  r<   r<   r=   onboarding_success  s   rs  STRIPE_PRICE_MONTHLY_DEFAULTr  STRIPE_PRICE_ANNUAL_DEFAULTr  z/api/get-pricesc               
   C   s  t jddpi } ttdd p| dpd }t| ztj	t
}tj	t}t|dd}t|dd}W n' ty` } ztd| td	d
ddd
ddddfW  Y d }~S d }~ww d}d }|rxtjj|d }	|	rx|	jrx|	j}|rztj	|}
t|
dd}W n ty } ztd| W Y d }~nd }~ww |}|}d
}d
}|dkrt|d|  d }t|d|  d }d}d}t||dd|d|d||dd|d|d|dS )NTr  r   ref_coder   unit_amountr   zError al obtener precios base:i  F)amount_cents
discountedi?  base_price_error)monthlyannualr
  r   r  percent_offu   Error al obtener cupón:r3  currencyusdr   )rx  r~  r&  ry  )r{  r|  ref_used)r   r  r}  r%   r:   r   r   r  Pricer;  DEFAULT_MONTHLY_PRICE_IDDEFAULT_ANNUAL_PRICE_IDr;   rx   r   r/   r9   r   r   r  Coupon)r   rv  base_monthlybase_annualmonthly_priceannual_pricery   discount_percent	coupon_idyoutuberr  discounted_monthlydiscounted_annualmonthly_discountedannual_discountedr<   r<   r=   api_get_prices  sn   



r  z!/var/www/html/flask_project/logs2zstripe_payments.csvzwebhook_debug.txtzwebhook_errors.txt)event_idlivemode
event_typeevent_created_utc
invoice_idr  r	  amount_paid_centsamount_subtotal_centsr~  hosted_invoice_urlr   r  c                   C   s   t jtdd d S )NT)exist_ok)r   makedirsLOG_DIRr<   r<   r<   r=   _ensure_dir 	  s   r  rZ  c                 C   sv   z0t   ttddd}|ttj  d|  d W d   W dS 1 s)w   Y  W dS  t	y:   Y dS w )u"   Append de mensajes de depuración.r   r   r    | rG  N)
r  r   	DEBUG_TXTwriter   r   r   r^  r   rx   )rZ  r  r<   r<   r=   r=  $	  s   $&r=  c              
   C   s   z<t   ttddd'}|ttj  d|  d |t	
  |d W d   W dS 1 s5w   Y  W dS  ty` } ztd| ddl}td	| |jd
 W Y d}~dS d}~ww )z!Append de errores con stacktrace.r   r   r  r  rG  Nu/   ⚠️ ERROR escribiendo en webhook_errors.txt:r   zOriginal error message:)r   )r  r   	ERROR_TXTr  r   r   r   r^  r   	traceback
format_excrx   r   sysstderr)rZ  r  	log_errorr  r<   r<   r=   r>  .	  s   "&
r>  r?  c              
      s   t   tjt} fddtD }z;ttdddd}tj|td}|s)|	  |
| W d   n1 s8w   Y  td	|d
  d|d   W dS  tya } ztd|   d}~ww )zEEscribe una fila en CSV; crea carpeta/archivo y header si no existen.c                    s   i | ]	}|  |d qS )r   )r:   )r8  kr?  r<   r=   
<dictcomp>@	  s    z%write_payment_row.<locals>.<dictcomp>r   r   r   )r   r   )
fieldnamesNzCSV write OK invoice_id=r  z
 event_id=r  zCSV write ERROR: )r  r   r   r   CSV_PATHPAYMENT_HEADERSr   r   
DictWriterwriteheaderr   r=  rx   r>  )r?  r   r   r  wry   r<   r  r=   write_payment_row<	  s    "r  ztest.txtz/stripe/webhookc            9      C   s  t dttj ddtjv   tjd} | st d dS ztjtj	ddd| t
d}W n5 tjjyL } ztd	|  W Y d }~dS d }~w tye } ztd
|  W Y d }~dS d }~ww t d|d d|d d|d  zttt
dd}W n ty   d}Y nw |r|dst d dS |s|drt d dS |d dkrz|d d }|d}|d}|dd}t|dpd}t|dpd}	|dpd }
|d!pd }|d"pd }|d#pd }|s3z|d$}|rtj|}|d%pd  }W n ty2 } zt d&|  W Y d }~nd }~ww d }d }d }d}z,|rftj|}|d'd }|d(pRi }|d)d }|d*d }|d+d,k}W n ty } zt d-|  W Y d }~nd }~ww |dko|d.ko|d/k}|dk}|si|riztjj|d0gd1}|d}|s|d2pi dg }|D ]%}|dr|d } n|d3i d4i }|dr|d } nq|s|d3i d5i d}t d6|  i }|r.ztj|}t|tr|d(pi }W n ty- } zt d7|  W Y d }~nd }~ww |d*|}|d)|}|}t d8|  t||\}}} |r@d }!z_|d2i pYi dg }"|"D ]M}|d3i d4i }|d|krvq`|d9p}i }#|#d:}$|$rz	tt|$}!W  n   Y |d;pi }%|%d<}&|&d urt|&}! nq`W n ty } zt d=|  W Y d }~nd }~ww |!st|dpd}!|!}'tt|'|  }(t||||(|| |'|r|jnd |d>t v r|d?nd d@
})|)d' dAkrt dB|)dC  dD|)dE  dF|  n4|)d' dGkr/t dH|( dI|)dJ dK ntdL|)dM dN|)dO  nt dP| dQ| dR|  W n tyh } ztdS|  W Y d }~nd }~ww |r%|s%|r%zdT}*z%|d$}|rtj|}|dUpg }+|+r|+d d dV  }*W n ty } zt dW|  W Y d }~nd }~ww |*dTkrdX},dY| dZ}-nd[},d\| d]}-t |,d^d_||-d` t da| db|* dc| dd ztjj!|d+d,ide W n ty } ztdf|  W Y d }~nd }~ww W n ty$ } ztdg|  W Y d }~nd }~ww i dh|d d|dddi|d djt"j#|dk t$j%dl& dm|dn|d#|do|dp|	d|d|
d)|d*|d!|dq|drt|dst|}.t'|. W n ty } ztdt|  W Y d }~dS d }~ww |d duv r zK|d d }|d}/|d'}0t|dv}1|dw}2|dx}3d }4|dypi }5|5r|5dzp|5d{pd }4d }6d }7z$|d$}|rtj|}|d%pd  }6|d|pd  }7W n ty } zt d}|  W Y d }~nd }~ww d }d }z|d(p%i }|d)d }|d*d }W n
 ty=   Y nw i dh|d d|dddi|d djt"j#|dk t$j%dl& dmd dn|/d#|6dod dpd dd dd d)|d*|d!d dq|0drd dsd |7|1|4|3rt"j#|3t$j%dl& nd d~}.t'|. t(j)j*|/d+ }8|8st d|/  W dS |d dks|0dkrd|8_,d |8_-t.j/0  t d|8j d|/  W dS t d|0 d|/  W dS  ty } ztd|  W Y d }~dS d }~ww dS )NzHIT /stripe/webhook | len_body=z | has_sig=zStripe-Signaturez!No Stripe-Signature header -> 400)r   r   F)cacheas_textr  zSignatureVerificationError: zconstruct_event ERROR: zEvent OK type=r  z id=r   z
 livemode=r  STRIPE_LIVE_MODE0z.Dropped: live-only webhook received test event)r   r   z.Dropped: test-only webhook received live eventzinvoice.paidr   objectr  r~  r  amount_paidr   amount_subtotalr  r   billing_reasoncustomer_namer	  r  r   zCustomer retrieve failed: r   r  r   r  welcome_sent1zSubscription retrieve failed: subscription_createtrialingzlines.data.pricer6  linesparentsubscription_item_detailssubscription_detailsz([affiliate] subscription_id detectado = u4   [affiliate] error leyendo metadata de suscripción: u   [affiliate] METADATA → pricingunit_amount_decimalr  rw  z#[affiliate] error leyendo precios: metar  )
r  r  destination_acctcommission_centsr~  r  
base_centsr  r   r  okz[affiliate] transfer OK id=transfer_idz amount=rx  zc dest=accruez![affiliate] below min threshold: zc < threshold_centszc (no transfer)z[affiliate] transfer ERROR: reasonr  r
  z%[affiliate] No affiliate for invoice=z ref=z yt_id=z[affiliate] unexpected ERROR: rE   preferred_localesr[  zLocale detection failed: zWelcome to BacktestingMarket!zHi u'  ,

            Thank you for joining our community of traders who seek to make informed decisions backed by real data.

            🧠 What can you expect?
            Your free trial is now active, and you can explore everything our platform has to offer:
            • Vertical and Iron Condor Idea Generation
            • Backtesting
            • Key metrics and clear visualizations
            • And much more!

            We have a completely free WhatsApp group for subscribers. If you’d like to join, send me your country code and phone number so I can add you.

            In this group, we share trading ideas, strategies, alerts, platform updates, and much more — all in real time and in a collaborative environment among subscribers.

            ⏱ In the meantime…

            Feel free to explore and get familiar with the interface. If you have any questions, you can write to us anytime. We’re here to help.

            Welcome again, and much success in your trading!

            The BacktestingMarket Team
            u!   ¡Bienvenido a BacktestingMarket!zHola u  ,

            Gracias por unirte a nuestra comunidad de traders que buscan tomar decisiones informadas y respaldadas por datos reales.

            🧠 ¿Qué puedes esperar?
            Ya puedes comenzar tu prueba gratuita y explorar todo lo que nuestra plataforma tiene para ofrecer:
            • Generación de Ideas de Verticales y Iron Condors
            • Backtesting
            • Métricas clave y visualizaciones claras
            • ¡Y mucho más!

            Tenemos un grupo de WhatsApp totalmente gratuito para suscriptores. Si deseas participar, envíame tu código de país y teléfono para agregarte.

            En este grupo compartimos ideas de trading, estrategias, alertas, novedades de la plataforma y mucho más — todo en tiempo real y en un entorno colaborativo entre suscriptores.

            ⏱ Mientras tanto…

            Te invitamos a explorar y familiarizarte con la interfaz. Cualquier duda que tengas, puedes escribirnos cuando quieras. Estamos aquí para ayudarte.

            ¡Bienvenido de nuevo y mucho éxito con tu trading!

            El equipo de
            BacktestingMarket
            BacktestingMarketr   r   zWelcome email sent to z (lang=z, subscription_id=)r[  z%Could not set welcome_sent metadata: zWelcome email ERROR: r  r  r  created)tzr  r  r  r  subscription_statusis_trialis_paidzinvoice.paid handler ERROR: >   customer.subscription.updatedcustomer.subscription.deletedcancel_at_period_endcurrent_period_endcanceled_atcancellation_detailsfeedbackcommentrF  z+Customer retrieve failed for cancel event: )r  r  cancellation_reasonr  r:  z[cancel] user not found sub_id=r  canceledz[cancel] Baja efectiva user_id=z sub_id=u!   [cancel] No baja aún — status=z	, sub_id=z[cancel handler ERROR] )1r=  r>  r   r   r  r:   r  Webhookconstruct_eventr  r   r%  r
  SignatureVerificationErrorr>  rx   boolr;   r  r;  r   rC  Invoicer<  rB   _find_affiliate_in_dbrv   r   pay_affiliate_commissionr   localsr  r   ra  r   fromtimestampr   r^  r   r  r.   r9   r   r   r   r<  r-   r   rw   )9sigeventry   IS_LIVEinvr  r  r~  r  r  
hosted_urlr  r  r	  cust_idcustr   r  r  welcome_already_sentrH  r  is_trial_zero_invoiceis_paid_invoiceinv_fulllines_for_sublir  sub_metasub_objytr  r  list_price_centsr  r  ua_decr  ua_oldr  r  r   localelocalesr   r   r?  rG  r   r  r  r  r  r  r   rF  r   r<   r<   r=   stripe_webhookR	  s   ,









$"P


	





	

r  c              
   C   s   z\t  M d}| rtjj| d }|s |r tjj|d }|r@|jrItt|j	p+dtd }||j|fW  d   W S W d   W dS W d   W dS 1 sUw   Y  W dS  t
yv } ztd|  W Y d}~dS d}~ww )z[
    Devuelve (yt, stripe_account_id, commission_percent_decimal) o (None, None, None)
    N)r   r  g      $@100z[affiliate] DB lookup error: )NNN)rK   app_contextr/   r9   r   r   rR  r   r_   commission_percentrx   r>  )r  r   r  r  ry   r<   r<   r=   r  R  s0   


r  AFFILIATE_MIN_PAYOUT_CENTSr  r  z0.10r   )r~  r  r  r  r   r  idem_suffixr  r  r  r  r~  r  r  r   r  r  c                 C   s   |dkr	dddS |t k rddt dS d|  d	| d	|
pd
 }z;tjj||p(d|dt|d  d|  | ||dur>t|nd|pBdt|t||	pKdd|d}d|j||pXddW S  tyu } zddt|dW  Y d}~S d}~ww )zEHace el Transfer a la connected account. Sin DB; retorna solo status.r   skippedzero_commission)r   r  r  below_min_threshold)r   r  r  aff_transfer_r    v1r  z
Affiliate r3  z% for invoice Nr   )r  r  r  r   r  r  r  )amountr~  destinationdescriptionr  idempotency_keyr  )r   r  rx  r~  retryr
  )r   r  r
  )MIN_PAYOUT_CENTSr  Transferr(  r;   r_   r   rx   )r  r  r  r  r~  r  r  r  r   r  r  idem_keyr  ry   r<   r<   r=   r  f  s4   
	r  c                 c   s$    | ]}|  r|   V  qd S rf  )r   r  )r8  ry   r<   r<   r=   r    s   " r  ADMIN_ALLOWED_EMAILS,c                    s   t   fdd}|S )Nc                     s0   t jrtt dd tvrtd  | i |S )Nr   r   i  )r%   r   r}  r  ALLOWED_EMAILSr   )r   r  r  r<   r=   wrapper  s   zadmin_only.<locals>.wrapperr   )r  r  r<   r  r=   
admin_only  s   r  z/makekac                   C   rc   )Nz
admin.htmlrd   r<   r<   r<   r=   
admin_page  s   r  z	/tutoriasc                   C   s   t dddS )Nztutory.htmlzEurope/Tirane)r   rd   r<   r<   r<   r=   tutorias_page  s   r  __main__i  )rM   port)rf  )r  )r  )	functoolsr   top_horarios_multir   top_horarios_multi_bpadmin_servicer   r   r   r   r8  r   r   r  collectionsr   r   r   r	   r
   r   email.headerr   email.mime.multipartr   email.mime.textr   email.utilsr   zoneinfor   decimalr   numpyr  pandasr  r   r  dotenvr   flaskr   r   r   r   r   r   r   r   r   r   r   flask_babelr   r   r    r`   flask_loginr!   r"   r#   r$   r%   
flask_mailr&   r'   itsdangerousr(   openair)   werkzeug.securityr*   r+   utils.schwab_clientr,   modelsr-   r.   r/   ccitmr0   neutralEdger1   backtestingIdear2   flask_wtf.csrfr3   r4   r5   r~  rK   r%  r   RuntimeErrorra  r   login_managerinit_app
login_viewuser_loaderr_   r>   register_blueprintcsrfexemptcontext_processorrC   configrR   babelr  
create_allr   router]   ra   rZ   rf   rg   rh   ri   rj   rz   before_requestr|   r   r7   r   r   r   r   r   r   r}   r   r   r   r  r  r  r  r  r  r  r  r  r  r	  r
  r  r  r  r  r  r  r  r6  r*  rH  r   r   r   r   r   r  r  r  r  r  r  r  r  r  r  endpoint_secretr3  r  rE  r_  rS  rJ  rg  rM  rs  r  r  r   r  r  r   r  r  r  r  r  r  r=  r>  rB   r  TEST_TXTr  r  r;   r  r  setr   r  r  r  r  runr<   r<   r<   r=   <module>   s  4



























@
?


'
y




 V
 B

S


			

R%
!
s
*
V (W
*

E
>

    

*,


