o
    mj&V                    @   s  d 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Zddl	Z	ddl
Z
ddlZddlmZmZ ddlZddlmZmZmZ zddlZW n eyO   dZY nw e  dd Zdd	 Zd
d Zz	e	e	jd W n e	jy   z	e	e	jd W n
 e	jy   Y nw Y nw edZeddZeddZeddZeddZ eddZ!eddZ"e#eddZ$e%eddZ&e%eddZ'eddZ(e#edd Z)e#ed!d"d# Z*e#ed$d%Z+e#ed&d#Z,d'Z-ed(dZ.e#ed)d*Z/ed+dZ0ed,dZ1ed-g Z2d.Z3d/Z4e5 Z6e67ej8 e6j9dd D ]Z:e6;e: q&ee3d0d1d2d3Z<e<7ej8 e=d4Z>e<?e> e6@e< eAd5ZAd6d7 ZBeB aCe!r`eZDe ZEneZDeZEeFeDeEd8d9d:tCd;d<ZGe!reGHd8 eId= eJd= n	eId> eJd> e.reId? eJd? n	eId@ eJd@ eIdAe-dBdC eJdAe-dBdC eIdDe,dBdE eJdDe,dBdE zeGK  eIdF eJejLdG ejM  W n" eNy  ZO zePdHeO  eJdHeO  W Y dZO[OndZO[Oww dIdJ ZQdKdL ZRdMdN ZSdOdP ZTdQdR ZUdSdT ZVdUdV ZWdWdX ZXdYdZ ZYd[d\ ZZd]d^ Z[dd_d`Z\dadb Z]dcdd Z^dedf Z_dgdh Z`didj Zadkdl Zbdmdn Zcdodp Zddqdr Zedsdt Zfdudv ZgddwdxZhdydz Zid{d| Zjd}d~ Zkdd Zldd Zmdd Zndd Zoepdkreo  dS dS )u   
ROBÔ DE BALANCEAMENTO DE PORTFÓLIO
Permite operação simultânea em múltiplas moedas com rebalanceamento automático
Baseado na estrutura do robo_trade.py
    N)RotatingFileHandler)datetime	timedelta)initForeStylec                 C   s<   t | ddd}t|W  d   S 1 sw   Y  dS )u'   Carrega configurações do arquivo JSONrutf-8encodingN)openjsonload)filepathfile r   L/var/www/html/robo.fernandesemelo.com.br/balanceamento/robo_balanceamento.pycarregar_configuracoes   s   $r   c                 C   sF   t | ddd}tj||ddd W d   dS 1 sw   Y  dS )u"   Salva o estado atual do portfóliowr	   r
      Findentensure_asciiN)r   r   dump)r   estador   r   r   r   salvar_estado_portfolio$   s   "r   c              
   C   s   z=t j| r;t| ddd%}t|}|r |W  d   W S td|  d 	 W d   W dS 1 s6w   Y  W dS  tjy\ } zt	d|  d|  W Y d}~dS d}~w t
yv } zt	d	|  W Y d}~dS d}~ww )
u)   Carrega o estado do portfólio se existirr   r	   r
   NzArquivo u    está vaziozErro ao decodificar JSON em : zErro ao carregar estado: )ospathexistsr   r   r   loggingwarningJSONDecodeErrorerror	Exception)r   r   dataer   r   r   carregar_estado_portfolio)   s(   
"r'   zpt_BR.UTF-8zPortuguese_Brazil.1252 configuracoes_balanceamento.jsonZapi_key Z
api_secretZapi_key_testnetZapi_secret_testnettestnetFZ
moeda_baseUSDTcapital_inicial  Ztempo_ciclo   Ztempo_refresh_tela	timeframeZ5mvariacao_balanceamentog       @Zcomissaog?d   Zsaldo_minimo
   Zsaldo_minimo_usdtg      @Zcomprar_imediatamenteZreinvestir_ganhos_percentual2   reset_portfolio_ao_iniciarZaguardar_melhor_hora	portfoliozlog_balanceamento.logzportfolio_estado.jsoni  @   r	   )ZmaxBytesZbackupCountr   z%(message)szAmerica/Sao_Pauloc               
   C   s   zAt  } |  }tt d }|| }t|dkr?tdt| d tt	j
d|d dd|dkr6dnd	  tj  |W S  ty\ } ztd
|  W Y d}~dS d}~ww )zHSincroniza o timestamp com o servidor da Binance para evitar erros -1021r-   u)   Desincronização de relógio detectada: msu    ⚠ AVISO: Relógio do PC está .1fzs r   Z	adiantadoZatrasadou*   Não foi possível sincronizar timestamp: N)ccxtbinanceZ
fetch_timeinttimeabsr    r!   printr   YELLOWr   	RESET_ALLr$   )Zpublic_exchangeZserver_timeZ
local_timetime_offsetr&   r   r   r   sincronizar_timestamp_binance   s   2rB   TZspoti N  )ZdefaultTypeZ
recvWindowtimeDifference)ZapiKeyZsecretZenableRateLimitoptionsz-Modo Testnet: ATIVADO (usando chaves testnet)zModo Testnet: DESATIVADOu*   Compra Imediata: ATIVADA (tolerância 10%)u,   Compra Imediata: DESATIVADA (tolerância 2%)u   Valor Mínimo de Operação: $.2fz
 (Binance)u   Proteção USDT: Mínimo $u    (não será gasto)u"   ✓ Exchange conectada com sucessou)   ✓ Mercados carregados (Exchange pronta)zFalha ao configurar exchange: c                 C   s6   t td}d| d|  }t| t| dS )u   Log e print simultâneos%d/%m/%Y %H:%M:%S[z] N)r   nowtimezonestrftimer    infor>   mensagem	timestampZmsg_completar   r   r   log_info   s   
rO   c                 C   B   t td}d| d|  }t| ttj| t	j
  dS )zLog e print de errorF   rG   z] ERRO: N)r   rH   rI   rJ   r    r#   r>   r   ZREDr   r@   rL   r   r   r   log_erro      
rQ   c                 C   rP   )zLog e print de avisorF   rG   z	] AVISO: N)r   rH   rI   rJ   r    r!   r>   r   r?   r   r@   rL   r   r   r   	log_aviso   rR   rS   c                 C   rP   )zLog e print de sucessorF   rG   u   ] ✓ N)r   rH   rI   rJ   r    rK   r>   r   GREENr   r@   rL   r   r   r   log_sucesso   rR   rU   c                 C   s  d}t |D ]x}zt| }|dp|d}|r!t|W   S dW   S  ty~ } zMt|}d|v s9d|v rb||d k rbtd|  d	|d  d
| d t a	t	tj
d< td W Y d}~q||d krrtd|  d|  W Y d}~ dS d}~ww dS )u:   Obtém o preço atual de um símbolo com retry automáticor6   lastcloseN-1021	Timestamp   zErro de timestamp ao obter  , resincronizando... (tentativa /)rC   r   u   Erro ao obter preço de r   )rangeexchangefetch_tickergetfloatr$   strrS   rB   rA   rD   r<   sleeprQ   )symbolmax_tentativas	tentativatickerZpricer&   erro_strr   r   r   obter_preco_atual   s*   
 

rj   c           	      C   s6  d}t |D ]}zGt| }|dp|dp|d}|du r@|dp*|d}|d}|r@|r@|d	kr@|| | d
 }|durKt|W   S dW   S  ty } z=t|}d|v scd|v r|||d k r|t attj	d< t
d W Y d}~q||d krtd|  d|  W Y d}~ dS d}~ww dS )uJ   Obtém a variação percentual de 24h de um símbolo com retry automáticor6   Z
percentageZchangeZchangePercentNrV   rW   r   r   r1           rX   rY   rZ   rC   r   u    Erro ao obter variação 24h de r   )r^   r_   r`   ra   rb   r$   rc   rB   rA   rD   r<   rd   rQ   )	re   rf   rg   rh   ZvariacaoZclose_priceZ
open_pricer&   ri   r   r   r   obter_variacao_24h   s2   

 

rl   c                 C   s   d}t |D ]p}zt }|di }t|| dW   S  tyv } zMt|}d|v s1d|v rZ||d k rZtd|  d|d  d	| d
 t a	t	tj
d< td W Y d}~q||d krjtd|  d|  W Y d}~ dS d}~ww dS )u:   Obtém saldo disponível de um ativo com retry automáticor6   freer   rX   rY   rZ   z$Erro de timestamp ao obter saldo de r[   r\   r]   rC   r   NzErro ao obter saldo de r   rk   )r^   r_   Zfetch_balancera   rb   r$   rc   rS   rB   rA   rD   r<   rd   rQ   )Zativorf   rg   Zbalancerm   r&   ri   r   r   r   obter_saldo  s*    

rn   c                 C   s*   i }| D ]}t |||< qt t|t< |S )u.   Obtém saldos de todas as moedas do portfólio)rn   
MOEDA_BASE)moedassaldosmoedar   r   r   obter_saldos_portfolio2  s
   rs   c              
   C   s   zU| t jvrdd|  dfW S t | }|dp|d}|d}|r)|dkr5dd|  d| d	fW S |d
u s=|dkrFdd|  dfW S dd|  d|dd|dfW S  tys } zdd|  dt| fW  Y d
}~S d
}~ww )u   
    Verifica se um par de trading está disponível e funcionando no testnet
    
    Returns:
        tuple: (está_disponível, info_diagnóstica)
    FzPar u9    não está na lista de símbolos disponíveis no testnetrV   rW   Z
baseVolumer   u#    não tem preço válido (último: r]   Nz* tem volume zero - sem liquidez no testnetTu    OK - Preço: .8fz
, Volume: zErro ao verificar par r   )r_   symbolsr`   ra   r$   rc   )re   rh   Z
last_pricevolumer&   r   r   r   verificar_par_disponivel:  s   


 $rw   c                 C   sJ   | td}| D ]}| |d}| |d}|dkr"|r"||| 7 }q|S )u+   Calcula o valor total do portfólio em USDTr   )ra   ro   )rp   rq   precosvalor_totalrr   
quantidadeprecor   r   r   obter_valor_portfolioV  s   r|   c                 C   s   d}|r(d|v r(|d D ]}|d dv r||d 7 }q|d dv r'||d 8 }qd}| D ] }|dkrL| |d}| |d}	|dkrL|	dkrL|||	 7 }q,|| }
|
S )u  
    ✓ NOVO: Calcula o ganho REAL do portfólio baseado no histórico de trades
    
    Ganho Real = (Valor atual das moedas em USDT) - (Capital investido em moedas)
    NÃO inclui USDT congelado/residual
    
    Args:
        moedas: Lista de moedas do portfólio
        saldos: Dicionário com saldos atuais
        precos: Dicionário com preços atuais
        estado: Estado do portfólio com histórico_trades
    
    Returns:
        float: Ganho/Perda real em USDT (apenas das moedas compradas)
    r   historico_tradestipo)compra_inicialcompra_balanceamentovalor)venda_balanceamentor+   ra   )rp   rq   rx   r   Zcapital_investido_moedasZtradeZvalor_moedas_atualrr   rz   r{   Z
ganho_realr   r   r   calcular_ganho_real`  s$   r   c           
   	   C   s   |r|r|r|rt ||||}ntd| t }tdkr't}d}dtdd}n3|td  }t| }|dkrL|| }	d|dd|ddtd	d
|	d}n|dk rXd|dd}nd}||||fS )uB  
    Calcula o limite de portfólio considerando reinvestimento parcial de ganhos
    
    Args:
        valor_portfolio_real: Valor total real do portfólio em USDT
        moedas: Lista de moedas (opcional, para cálculo de ganho real)
        saldos: Dicionário de saldos (opcional, para cálculo de ganho real)
        precos: Dicionário de preços (opcional, para cálculo de ganho real)
        estado: Estado do portfólio (opcional, para cálculo de ganho real com histórico)
    
    Returns:
        tuple: (valor_limite, ganho_total, ganho_reinvestido, status_msg)
        - valor_limite: Valor a usar para cálculos de compra/venda
        - ganho_total: Ganho total acumulado (REAL, baseado em trades)
        - ganho_reinvestido: Valor do ganho sendo reinvestido
        - status_msg: Mensagem de status para log
    r   z"Modo PROTETOR: Usando EXATAMENTE $rE   z (ganhos congelados)r1   zGanho Total: $z | Reinvestindo: $ (.0fz%) | Congelado: $zPerda Atual: $u    (portfólio em recuperação)u<   Sem ganhos/perdas ainda (portfólio em fase de acumulação))r   maxCAPITAL_INICIALREINVESTIR_GANHOS_PERCENTUAL)
Zvalor_portfolio_realrp   rq   rx   r   ganho_totalZvalor_limiteZganho_reinvestidoZ
status_msgZganho_congelador   r   r   calcular_limite_reinvestimento  s,   
r   c           	      C   sv   i }|dkr| D ]}d||< q|S | D ]%}| |d}| |d}|dkr4|r4|| }|| d ||< qd||< q|S )u9   Calcula os percentuais atuais de cada moeda no portfólior   r1   r   )	rp   rq   rx   ry   percentuaisrr   rz   r{   Zvalor_moedar   r   r   calcular_percentuais_atuais  s   

r   c                 C   s   t | dk r
| g fS t| }t |}|d }d| d }|| }|| }|| }|d|  }|d|  }	g }
g }t| D ]\}}||  krJ|	krRn n|
| q<|| q<|
|fS )u   
    Remove outliers usando o método Interquartile Range (IQR)
    
    Args:
        precos: lista de preços
    
    Returns:
        tuple: (preços filtrados sem outliers, índices dos outliers)
       r6   g      ?)lensorted	enumerateappend)rx   Zsorted_precosnZq1_idxZq3_idxZq1Zq3ZiqrZlimite_inferiorZlimite_superiorZprecos_filtradosindices_outliersidxr{   r   r   r   remover_outliers_iqr  s$   
r   c                 C   s   t | S )u   
    DESCONTINUADO: Use obter_melhores_precos_30dias() em vez disso.
    Mantido para compatibilidade com versões antigas.
    )obter_melhores_precos_30dias)rp   r   r   r   obter_melhores_precos_5dias  s   r   c           -         sn  i }zd}| D ]}zn| dt  }tj||dd}|r$t|dk r3td| dt| d W qt|dkr?|d	d
 n|}t|}dd |D }dd |D }t|\}	}
t|\}}t|
t|B }t|}|| }|dkry|| d nd}i }t|D ]2\}}||vr|d }tj	|d t
d}|d}||vrg ||< || |j|d |d d qt|}dddg g ddddg g ddddg g ddddg g ddddg g ddddg g dd | D ]\}}|sqdd td D }|D ]}|d! d" }|d kr
d#}|| | qi }i }| D ]*\}}|rCtd$d% |D t| }td&d% |D t| }|||< |||< q|rlt||jd'}  |  d(  d)7  < ||  D ]} |  d* |d!  q]|rt||jd'}! |! d+  d)7  < ||! D ]} |! d, |d!  qq|dkrt| d- W qt  fd.d/d'}"t  fd0d/d'}# |" d( }$ |# d+ }%|dkr|$| d nd}&|dkr|%| d nd}'d1d2 }(|( |" d* })|( |# d, }* |" d3 |)t|&d)|$d4 |# d3 |*t|'d)|%d4|||t|d)|d5tjt
d d6}+|+||< td7| d8 td9| d:| d;d| d<d= td>| d?|d<d= td@|+dA d!  dB|) dC|&d<dD|$ dE| dF tdG|+dH d!  dB|* dC|'d<dD|% dE| dF W q ty }, ztdI| dJ|,  W Y d
},~,qd
},~,ww |W S  ty }, ztdK|,  i W  Y d
},~,S d
},~,ww )Lu  
    Busca os melhores preços (máxima e mínima) dos últimos 30 dias (ou máximo disponível) para cada moeda
    usando dados OHLCV (Open, High, Low, Close, Volume) da exchange
    Remove outliers para análise de padrões de horário mais confiáveis
    
    Retorna análise consolidada sem detalhe dia a dia para maior confiabilidade.
    Com ~250+ candles (≈10-11 dias), a confiança dos padrões é muito boa.
    
    Returns:
        dict: {
            'moeda': {
                'melhor_hora_compra': { hora, confianca%, ocorrencias },
                'melhor_hora_venda': { hora, confianca%, ocorrencias },
                'analise': {
                    'total_registros': int (candles coletados),
                    'registros_validos': int (após remover outliers),
                    'total_outliers': int,
                    'percentual_outliers': float (%)
                },
                'timestamp': str (ISO 8601)
            }
        }
    Z1hr\      )limit   zCandles insuficientes para z (precisa 200, tem r]   iNc                 S      g | ]}|d  qS )r   r   .0candler   r   r   
<listcomp>&      z0obter_melhores_precos_30dias.<locals>.<listcomp>c                 S   r   )r6   r   r   r   r   r   r   '  r   r   r1   r-   tzz%Y-%m-%dr6   r   )horalowhighz00:00-03:59)labeldias_vencedor_compradias_vencedor_vendahoras_comprahoras_vendaz04:00-07:59z08:00-11:59z12:00-15:59z16:00-19:59z20:00-23:59)r   rZ   r   r6   r   r.   c                 S   s   i | ]}|g qS r   r   )r   ir   r   r   
<dictcomp>X  s    z0obter_melhores_precos_30dias.<locals>.<dictcomp>   r   r   r.   c                 s       | ]}|d  V  qdS )r   Nr   r   cr   r   r   	<genexpr>f      z/obter_melhores_precos_30dias.<locals>.<genexpr>c                 s   r   )r   Nr   r   r   r   r   r   g  r   )keyr   rZ   r   r   r   u   : Nenhum dia com dados válidosc                        |  d S )Nr   r   xZgrupos_horasr   r   <lambda>      z.obter_melhores_precos_30dias.<locals>.<lambda>c                    r   )Nr   r   r   r   r   r   r     r   c                 S   sP   | sdS t dd | D }|t|  }t|d }t|d }|dd|dS )u&   Calcula a hora média em formato HH:MMN/Ac                 s   s    | ]}|d  V  qdS )<   Nr   )r   hr   r   r   r     r   zLobter_melhores_precos_30dias.<locals>.calcular_hora_media.<locals>.<genexpr>r   02d:)sumr   r;   )Z
horas_listZtotal_minutosZmedia_minutosZhorasZminutosr   r   r   calcular_hora_media  s   z9obter_melhores_precos_30dias.<locals>.calcular_hora_mediar   )r   Z
hora_media	confiancaZocorrencias)total_registrosregistros_validostotal_outlierspercentual_outliers
total_dias)melhor_hora_compramelhor_hora_vendaZanaliserN     r   u       📊 Registros: z coletados | u    válidos (r8   %)u       ⚠️  Outliers: z removidos (u       ✓ Faixa COMPRA:  r   u    | Hora Média: u    | Confiança: z% | Melhor em  de z diasu       ✓ Faixa VENDA:   r   zErro ao buscar dados de r   u    Erro ao obter melhores preços: )ro   r_   Zfetch_ohlcvr   rS   r   setr   r   fromtimestamprI   rJ   r   houritemsr^   r   minra   r   roundrH   	isoformatrO   r$   rQ   )-rp   melhores_precosr/   rr   re   Zcandlesr   ZhighsZlowsZhighs_filtradosZoutliers_highZlows_filtradosZoutliers_lowr   r   r   r   Zcandles_por_datar   r   rN   dtZdata_strr   Zcandles_diaZ
grupos_diaZ	grupo_idxZprecos_medios_compraZprecos_medios_vendaZcandles_grupoZpreco_medio_lowZpreco_medio_highZmelhor_compra_diaZmelhor_venda_diaZmelhor_grupo_compraZmelhor_grupo_vendar   r   Zconfianca_compraZconfianca_vendar   Zhora_media_compraZhora_media_vendadados_moedar&   r   r   r   r     s   






"26r   c              
   C   sx   z t d t|}|r|| d< tt|  t dt| d | W S  ty; } ztd|  | W  Y d}~S d}~ww )u   
    Atualiza o arquivo de estado do portfólio com os melhores preços dos últimos 30 dias (ou máximo disponível)
    Incluindo análise de padrões de horário e detecção de outliers
    uP   Atualizando dados de melhores preços (até 30 dias com análise consolidada)...best_prices_30daysu9   ✓ Dados de melhores preços (30 dias) atualizados para z moedasu$   Erro ao atualizar melhores preços: N)rO   r   r   PORTFOLIO_STATE_FILEr   r$   rQ   )r   rp   r   r&   r   r   r   #atualizar_melhores_precos_portfolio  s   
r   c                 C   s  zt | }|r|dkrtd|  d W dS |dkr |d }n|d }td| d	| d	|   td
|d td|d zt| |||ddi}ttd}|	dd}|	dd}|	dd}	|	dd}
td| d| d|	dd|
d |dkrtd| d td td|  d td |dd! td"|dd# W W dS |d$krtd%| d W W dS |d&krt
d'|dd	|  d(|  |W W S t
d)|dd	|  d(|  |W W S  ty } zt|}td*|  td+ zt| ||}ttd}|	dd}|	dd}|	dd}	|	dd}
td| d| d|	dd|
d |dkrPtd,| d td- W W Y d}~W dS |d&krdt
d.|dd	|  d(|  nt
d/|dd	|  d(|  |W W  Y d}~W S  ty } ztd0|   td1t|  W Y d}~W Y d}~W dS d}~ww d}~ww  ty } z!td2| d3|  d4|dd5|  td6t   W Y d}~dS d}~ww )7u   
    Executa uma ordem de compra ou venda (limit order)
    Limit orders são mais confiáveis no testnet que market orders
    r   u   Falha ao obter preço para u)   . Não será possível criar limit order.NbuygRQ?g\(\?z  Criando limit order:         Preço Atual: rt   u       Preço Limite: timeInForceZGTCrF   idr   statusZdesconhecidofilledamountz  ID: z | Status: z | Preenchida: r\   Zexpiredu1     ⚠️ AVISO: Ordem expirou imediatamente (ID: r]   u        Possíveis causas:z     1. Par u"    pode não ter liquidez no testnetz     2. Quantidade u    pode ser inválidau        3. Preço z fora do spreadZcanceledu6     ⚠️ AVISO: Ordem foi cancelada pela Binance (ID: sellu   ✓ Ordem de VENDA executada: z em u   ✓ Ordem de COMPRA executada: z  Limit order falhou: z(  Tentando market order como fallback...u+     ⚠️ Market order também expirou (ID: z7     Problema aparentemente com par/liquidez no testnetu'   ✓ Ordem de VENDA (market) executada: u(   ✓ Ordem de COMPRA (market) executada: z+  Ambas market e limit order falharam para z  Market error: zFalha ao executar ordem z para r   z): z  Rastreamento: )rj   rQ   rO   r_   create_limit_orderr   rH   rI   rJ   ra   rU   r$   rc   rS   Zcreate_market_order	traceback
format_exc)re   siderz   preco_atualpreco_limiteordemrN   Zorder_idr   r   r   Ze_limitri   Ze_marketr&   r   r   r   executar_ordem  s   
	$$

!"r   c                 C   s   | dt   S )u   Aplica comissão à quantidaderZ   )COMISSAO)rz   r   r   r   aplicar_comissao?  s   r   c              	   C   sN   | t k rd| d| d| ddt dd	}d|fS dd| d| d	| dfS )
u%  
    Valida se o valor da operação atende ao mínimo da Binance ($5 USD/USDT)
    
    Args:
        valor_operacao: Valor da operação em USDT
        moeda: Símbolo da moeda (ex: BTC)
        tipo_operacao: 'compra' ou 'venda'
    
    Returns:
        tuple: (é_válido, mensagem)
    u   Operação de r   z BLOQUEADA: Valor $rE   u    é inferior ao mínimo de $z exigido pela BinanceFTz validada: $)VALOR_MINIMO_OPERACAO)Zvalor_operacaorr   Ztipo_operacaorM   r   r   r   validar_valor_operacaoC  s   r   c           	   
   C   s   zPd| vs|| d vrW dS | d | }d|vrW dS |d d }|d d }| d}t|dkrNt|d  d	d }t|d
  d	d }|||fW S W dS  tym } ztd| d|  W Y d}~dS d}~ww )u?  
    Extrai a melhor hora de compra do estado do portfólio
    
    Args:
        estado: Estado atual do portfólio
        moeda: Símbolo da moeda
    
    Returns:
        tuple: (hora_inicio, hora_fim, confianca) ou (None, None, 0) se não encontrar
        Exemplo: (8, 11, 60.5) para o período 08:00-11:59
    r   )NNr   r   r   r   -r   r   r   rZ   u(   Erro ao extrair horário de compra para r   N)splitr   r;   r$   rS   )	r   rr   r   Zmelhor_horar   Zparteshora_iniciohora_fimr&   r   r   r   extrair_horario_compraX  s&   
r   c           
      C   s   t || \}}}|du rd|  dfS tjtdj}||kr+||  ko'|kn  }n||kp2||k}|dd|dd}|rO|  d| d	|d
d}d|fS |dd}	|  d| d	|d
d|	 }d|fS )u  
    Verifica se a hora atual está dentro do melhor período de compra para a moeda
    
    Args:
        moeda: Símbolo da moeda
        estado: Estado atual do portfólio com best_prices_30days
    
    Returns:
        tuple: (está_no_horário_ideal, mensagem_info)
    NFu@   : Dados de melhor hora não disponíveis (análise em progresso)r   r   :00-:59u!   : ✓ HORÁRIO IDEAL de compra! (   , confiança: r8   r   Tz:00u(   : ⏰ Aguardando melhor hora de compra (u   %). Próxima às )r   r   rH   rI   r   )
rr   r   r   r   r   
hora_atualZesta_dentroperiodo_strrM   Zproxima_hora_comprar   r   r   eh_horario_compra_ideal{  s   r   c           	      C   s   t || \}}}|du rdS tjtd}|j}|j}||  kr%|kr&dS  ||k r3|| d | }n
d| | d | }td|S )u   
    Calcula quantos minutos faltam até a melhor hora de compra
    
    Args:
        moeda: Símbolo da moeda
        estado: Estado atual do portfólio
    
    Returns:
        int: Minutos até a melhor hora (ou 0 se já está na melhor hora)
    Nr   r   r      )r   r   rH   rI   r   minuter   )	rr   r   r   r   _Zagorar   Zminuto_atual	tempo_ater   r   r   calcular_tempo_ate_melhor_hora  s   
r   c              
   C   sx  |du rd}t || \}}}|du r!t|  d dd|  dfS t }d}d}d}	zl	 t| |\}
}|
rSt|d	 d
}|  d| d|dd}t| d||fW S t | }|d	 }||krtt|  d| d d||  dfW S ||	krt| |}t|  d|dd| d| d |d }	t| q- t	y } zt
d|  d|  dd|  dfW  Y d}~S d}~ww )uA  
    Aguarda até a melhor hora de compra para a moeda
    
    Args:
        moeda: Símbolo da moeda
        estado: Estado atual do portfólio
        tempo_maximo_minutos: Tempo máximo de espera em minutos (None = 5 min como padrão)
    
    Returns:
        tuple: (aguardou, tempo_esperado_minutos, mensagem)
    Nr.   uM   : Análise de melhor hora não disponível - prosseguindo com compra imediataFr   u*   : Análise de melhor hora não disponívelr2   Tr   rZ   u.   : ✓ Horário ideal de compra atingido após u    min de espera (confiança: r8   r   u   : Tempo máximo de espera (z( min) atingido. Prosseguindo com compra.z: Timeout de espera atingidoz: Aguardando melhor hora... (r\   z min, u    min até a janela ideal)r   z"Erro ao aguardar melhor hora para r   z*: Erro na espera - prosseguindo com compra)r   rS   r<   r   r   rU   r   rO   rd   r$   rQ   )rr   r   tempo_maximo_minutosr   r   r   Ztempo_inicialZtempo_decorrido_segundosZintervalo_verificacaoZproximo_logZ
esta_idealmsgZtempo_esperado_minZmensagem_sucessoZtempo_decorrido_minutost
   tempo_atér&   r   r   r   aguardar_melhor_hora_compra  sD   
"
r  c               
   C   s@  zd} t | ddd}t|}W d   n1 sw   Y  |ddkrd|d< t | d	dd}tj||d
dd W d   n1 sEw   Y  ztjtr^t	t t
dt d W n ty{ } ztdt d|  W Y d}~nd}~ww t
d W dS W dS  ty } ztd|  W Y d}~dS d}~ww )u  
    ✓ NOVO: Desativa automaticamente o reset após ser executado com sucesso
    Evita rodar limpeza desnecessária no próximo ciclo
    - Desativa flag reset_portfolio_ao_iniciar na configuração
    - Deleta portfolio_estado.json para forçar recriação com valores corretos
    r(   r   r	   r
   Nr4   TFr   r   r   u   ✓ Arquivo u    deletado para recriaçãou   ⚠ Não foi possível deletar r   u8   ✓ Reset desativado automaticamente nas configuraçõesu2   ⚠ Não foi possível desativar reset na config: )r   r   r   ra   r   r   r   r   r   removerU   r$   rS   )config_filefconfigr&   r   r   r   desativar_reset_apos_limpeza  s8   
 r  c                 C   sB  t sdS td td td tdtdd dd	 | D }t| }d}d}| D ]}||d}||d}|d
kr|dkr|| }td| d td|dd|  td|d td|d | dt }	z=|d }
t|	d||
ddi}|rt	
d t|}|d
k rtd| d ||7 }|d7 }ntd| d|dd W q* ty } ztd| d|  W Y d}~q*d}~ww q*tt}td|t }td td | d! td"|d td#|d td$td |d%krtd&|dd' td( td)tdd* ntd+ td, t  |S )-u  
    ✓ NOVO: Vende todas as moedas residuais para limpar o portfólio
    Útil quando "reset_portfolio_ao_iniciar" está ativado
    
    Args:
        moedas: Lista de moedas do portfólio
    
    Returns:
        float: Saldo USDT total após limpar tudo (deve ser ~2000)
    r   z=
============================================================u;   🧹 MODO RESET ATIVADO - LIMPANDO PORTFÓLIO COMPLETAMENTE<============================================================u1   
📋 Objetivo: Resetar para capital inicial de $rE   z USDTc                 S       i | ]}|t | d t qS r\   rj   ro   r   mr   r   r   r   0       z+limpar_moedas_residuais.<locals>.<dictcomp>h㈵>u   
🔄 Vendendo z
 residual:z
   Saldo: rt   r   u      Preço: $z   Valor: $r\   gGz?r   r   ZIOCrZ   u      ✓ z vendido com sucesso!u      ⚠ Venda parcial de z	 (restam r]   u      ✗ Erro ao vender r   Nu   ✓ RESET CONCLUÍDO: z moedas vendidasz  Valor recuperado: $z  Saldo USDT final: $z  Capital Inicial: $r3   u   
  ⚠️ AVISO: Há $z em USDT residual!u3     Este valor NÃO foi reinvestido na fase anterior.u     O robô usará exatamente $z + reinvestimentos.u#     ✓ Portfólio limpo com sucesso!z=============================================================
)RESET_PORTFOLIO_AO_INICIARrS   rO   r   rs   ra   ro   r_   r   r<   rd   rn   rU   r$   rQ   r   r  )rp   rx   rq   Ztotal_usdt_recuperadoZmoedas_vendidasrr   saldor{   Zvalor_vendare   r   r   saldo_depoisr&   saldo_usdt_finalZsaldo_usdt_residualr   r   r   limpar_moedas_residuais  st   

 
r  c               	      s\  dd t D } dd t D }dd t D }tr(t| }td|d td tt}|r6td	 || fS t|   fd
d| D }|rnt	dt
| dd|  dtt t|  g ||g d}td || fS dtt tg |  ||g d}td tdt dt  tdd|   td|  td|  td || fS )u!   Inicializa o estado do portfólioc                 S       g | ]}| d dr|d qS ativaTrr   r   r  r   r   r   r   |  r  z)inicializar_portfolio.<locals>.<listcomp>c                 S   &   i | ]}| d dr|d |d qS )r  Trr   
percentualr   r  r   r   r   r   }     & z)inicializar_portfolio.<locals>.<dictcomp>c                 S   r  )r  Trr   preco_comprar   r  r   r   r   r   ~  r  u$   Após reset: Capital disponível = $rE   r   u+   Carregando estado anterior do portfólio...c                    s    g | ]}  |d dkr|qS )r   r  r   r  Zsaldos_reaisr   r   r     r  u$   Portfólio detectado no testnet com z	 moedas: z, balanceamento)fasedata_inicior,   moedas_compradasmoedas_pendentespercentuais_alvoprecos_comprar}   u9   ✓ Fase de Balanceamento iniciada (portfólio existente)
construcaouD   ===================== NOVO PORTFÓLIO INICIADO =====================Capital Inicial: r   u   Moedas no Portfólio: zPercentuais Alvo: u   Preços de Compra (entrada): r  )PORTFOLIO_CONFIGr  r  rO   r<   rd   r'   r   rs   rS   r   joinr   rH   rI   r   r   copyro   )rp   r!  r"  Zsaldo_finalZestado_existenteZmoedas_com_saldor   r   r  r   inicializar_portfolioz  sT   

r(  c           5      C   sV  t d t|}dd |D }|td}|}|D ]}||d}||d}|dkr3|r3||| 7 }qt d|d |dkrLtdkrLtdtd t d|dd	t  t||||| \}	}
}}t}tdkr|dkrt	d||	 }|d
krt| }td|dd td|d |dkr|nt}| dg }g }| d }| d }|D ][}||d}||d}||d}|dkrt | d|dd ||vr|
| q||vr|
| tr|dkr|| | d }|d
k}n d}nt|| \}}|}|s|dkr|| | d }|dk}|r|r||dd }|	| }|| dt  }d}|tk rN|}t}|| dt  }d}td|dd|dd |dkrU|nt}t||d\} }!t	d|| }"| r|tkr|"|krtd| d t d |d t d!|d t d"|dd	t  t d#|d | d$t }#t|#\}$}%|$std%|# d& td'|%  td(| d) qt d*|%  trt d+ t|| dd,\}&}'}(|&rtd-|(  nt d-|(  nt d. t|})t|#d/|}*|*r~td t|}+|+|)krtd0 td1 t|}+|+|) },|,dkrStd2|,dd	|  ||vr;|
| || d< | d3 
tt |d4|||d5 n&td6| d7 td8|)dd	| d9|+dd	|  td: td;|# d< td q| st|! qtd=| d>|dd?|dd@ qtr|dkr|| | d }-| dA|-dBdC}.nB| dD}.n<t| |\}/}0}1|/dEurtjtdFj}2t|| }3|/dGdH|0dGdI}4| dJ|4 dK|1dBdL|3dMdN|d	}.n| dO}.t |. q||vr|
| q|| dP< || d< t |dkr$tdQ dR| dS< tt | dT< t!t"|  | S )UuU   
    Fase 1: Completa o portfólio comprando as moedas até os preços definidos
    u9   
========== FASE DE CONSTRUÇÃO DO PORTFÓLIO ==========c                 S   r  r	  r
  r  r   r   r   r     r  z-fase_construcao_portfolio.<locals>.<dictcomp>r   zSaldo USDT atual: rE   u;   Testnet sem USDT. Usando capital inicial como referência: "   Valor Total do Portfólio (REAL): r   r3   !   
🔒 MODO PROTETOR: Congelando $ em USDT residual+      Saldo mínimo protegido ajustado para: $r  r!  r"  r  u   : Já possui rt   z em carteirar1   Tr.   rZ   F       ⚡ Valor ajustado de $ para $    (mínimo da Binance)compra
>>> Comprando  <<<r   u       Preço Alvo:     Valor:     Quantidade: r\        ⚠️ Par #    não está disponível no testnet!       Pulando compra de     - par indisponível     ✓ @     ⏰ Buscando melhor momento de compra para maximizar lucros...r   r   9     → Compra imediata (aguardar_melhor_hora desabilitado)r   C     Saldo não mudou na primeira verificação, aguardando mais 2s...r   &   ✓ Compra verificada: Saldo aumentou r}   r   rN   rr   r~   r{   rz   r      ✗ FALHA: Compra de &    não confirmada! Saldo não aumentou.	  Antes:  | Depois: uA     ⚠ AVISO: A Binance aceitou a ordem mas não creditou o saldo!u#     Possível causa: Problema no par z no testnetz Saldo insuficiente para comprar z
 (precisa z, tem r]   u*   : Preço muito alto para compra imediata (r8   z% acima). Esperando melhora...u-   : Preço alvo não definido na configuraçãoNr   r   r   r   z$: Aguardando melhor hora de compra (r   z%). Faltam r   u    min. Preço: u0   : Análise de melhor hora não disponível aindar   u7   PORTFÓLIO COMPLETO! Iniciando fase de balanceamento...r  r  Zdata_balanceamento_inicio)#rO   rs   ra   ro   r   rS   r   SALDO_MINIMO_USDTr   r   r   COMPRAR_IMEDIATAMENTEr   r   r   r   rw   rQ   AGUARDAR_MELHOR_HORAr  rU   rn   r   r<   rd   r   rH   rI   r   r   r   r   r   r   r   )5r   rp   rq   rx   
saldo_usdtvalor_portfoliorr   rz   r{   valor_portfolio_limiter   ganho_reinv	msg_ganhosaldo_usdt_minimo_protegidousdt_residualZsaldo_para_comprar  r   r!  r"  r   Z
preco_alvoquantidade_atualZdiferenca_percentualZcondicao_compraZesta_no_melhor_horarioZmsg_horarioZpercentual_alvovalor_compravalor_ajustadovalor_compra_originalZsaldo_disponivel	eh_validomsg_validacaoZsaldo_livrere   par_disponivelinfo_paraguardoutempo_esperado	msg_temposaldo_antesr   r  diferenca_saldoZdiferenca_pctrM   r   r   r   r   r   r   r   r   r   fase_construcao_portfolio  s  












	$

"


(




r\  c           8   
   C   s 
  t d t|}dd |D }t|||}|td}t d|ddt  t||||| \}}}}	t d|ddt  t d	|	  t}
tdkrn|dkrntd|| }|d
krnt| }
t	d|dd t	d|
d t d|
dd t
||||}| d }t d g }g }|D ]]}||d}||d}|| }t | d|dd|dd|dd |tkr||||||dd t	d| d q|t k r||t||||dd t	d| d q|td}t|dd ddD ]}|d }|d  }|d! }||d}|dkr|dkr|d" | }|| d#t  }d$}|tk rM|dkrM|}t}|| d#t  }d}t	d%|dd&|dd' t||d(\}}|r |dkr ||kr t	d)| d* t d+|d, t d-|d, t d.|ddt  t|}| d/t }t|d0|} | rtd# t|}!|!|krt	d1 td2 t|}!||! }"|"dkrtd3|"d,d|  ||7 }| d4 tt |d5|||d6 ntd7| d8 td9|d,d| d:|!d,d|  td# q|st	| qt|}|td}t d; t d<|d t d=t| d>d?d@ |D   t dAt| d>dBd@ |D   g }#d}$|D ](}%|%d }|%d  }|%d! }|dkrm|d" | }&|#||||&dC |$|&7 }$qFt|#dDd dd}#dE}'|$|kr|$dkrtd||
 |$ nd}'t	dF t	dG|d t	dH|
d t	dItd||
 d t	dJ|$d t	dK|'d" dLd |'dkrt	dM|'d" dLd n-t dN t dG|d t dH|
d t dItd||
 d t dJ|$d t dO |#D ]}(|(d }|(d  }|(dP })|)|' }&d$}|&tk r0|&}*t}&d}t	d%|*dd&|&dd' |dkr|tkr|&| d#t  }+t|&|dQ\}}|rN|&tkrN||&t krNt	dR| d* t d+|d, t d-|+d, t d.|&ddt dS|)ddT | d/t }t|\},}-|,stdU| dV tdW|-  t	dX| dY qt dZ|-  trt d[ t|| d\d]\}.}/}0|.rtd^|0  nt d^|0  nt d_ t|}t|d`|+} | rLtd# t|}!|!|krt	d1 td2 t|}!|!| }"|"dkr-tda|"d,d|  ||&8 }| d4 tt |db||+|&d6 ntdc| dd td9|d,d| d:|!d,d|  td# q|sWt	| q|tkrnt	d^| de|ddftddT q||&t k rt	d^| dg|&t ddh|ddT t	di|&ddjtd qt	d^| dk qt dl t|}dmd |D }t|||}1t|1|||| \}2}3}4}4t
||||2}5|D ]}|5|d}6||d}7t | d|6ddn|7d,dT qt do|1ddt  t dp|2ddt  t dq|3ddt  t t!|  | S )ru   
    Fase 2: Balanceia o portfólio mantendo os percentuais definidos
    Vende moedas acima do percentual e compra moedas abaixo
    u:   
========== FASE DE BALANCEAMENTO DO PORTFÓLIO ==========c                 S   r  r	  r
  r  r   r   r   r     r  z0fase_balanceamento_portfolio.<locals>.<dictcomp>r   r)  rE   r   u9   ⚠️  Valor Total para CÁLCULOS (COM REINVESTIMENTO): u   📊 r3   r*  r+  r,  u   🔒 PROTEÇÃO USDT: Mínimo $u    não será gastor!  u    
--- Análise de Percentuais ---r   	% (Alvo: u   %) - Diferença: +.2f%)rr   	diferencaZpercentual_atualr{   u     → z acima do alvo (venda)z abaixo do alvo (compra)c                 S      | d S Nr`  r   r   r   r   r   r         z.fase_balanceamento_portfolio.<locals>.<lambda>T)r   reverserr   r{   r`  r1   rZ   Fr-  r.  r/  Zvendaz
>>> Vendendo r2  r   rt   r4  r3  r\   r   r>  r   u%   ✓ Venda verificada: Saldo diminuiu r}   r   r@  u   ✗ FALHA: Venda de u&    não confirmada! Saldo não diminuiu.rC  rD  u!   
🔍 ESTADO ATUAL DO PORTFÓLIO:u      • USDT em Caixa: $u      • Moedas a Comprar: u    → c                 S   r   rr   r   r  r   r   r   r   %  r   z0fase_balanceamento_portfolio.<locals>.<listcomp>u      • Moedas a Vender: c                 S   r   re  r   r  r   r   r   r   &  r   )rr   r{   r`  valor_idealc                 S   ra  rb  r   r   r   r   r   r   =  rc  g      ?u/   
💰 CÁLCULO DE COMPRAS (SALDO INSUFICIENTE):u      • USDT Disponível: $u      • Proteção USDT: $u"      • USDT Livre (pode gastar): $u      • Total Necessário: $u      • Fator de Redução: r8   uV   ⚠ Saldo insuficiente para completar todas as compras. Reduzindo proporcionalmente a u-   
💰 CÁLCULO DE COMPRAS (SALDO SUFICIENTE):u*      • Fator de Redução: 100% (saldo ok)rf  r0  r1  z	 (Ideal: r]   r5  r6  r7  r8  r9  r:  r;  r6   r<  r   r=  r   r?  r   rA  rB  u&   : ⛔ Compra bloqueada - Saldo USDT ($u   ) ≤ Proteção ($u6   : ⛔ Compra bloqueada - Saldo insuficiente (precisa $z, tem $u!          └─ USDT Necessário: $u    + Proteção: $u1   : Compra rejeitada por outro motivo (validação)z 
--- Resumo do Balanceamento ---c                 S   r  r	  r
  r  r   r   r   r     r  z
% (Saldo: zValor Total (REAL): u   Valor Total (CÁLCULOS): z Ganho REAL (baseado em trades): )"rO   rs   r|   ra   ro   r   rE  r   r   rS   r   VARIACAO_BALANCEAMENTOr   r=   r   r   r   r   rn   r   r<   rd   rU   r   rH   rI   r   rQ   r   rw   rG  r  r   r   )8r   rp   rq   rx   rI  Zsaldo_usdt_atualrJ  r   rK  rL  rM  rN  Zpercentuais_atuaisr!  Zmoedas_vendaZmoedas_comprarr   Z	pct_atualpct_alvor`  Zsaldo_usdt_antesZ
item_vendar{   rO  Zvalor_excessoZquantidade_vendarQ  Zvalor_excesso_originalrS  rT  rZ  re   r   r  r[  Zcompras_planejadasZtotal_usdt_necessarioZitem_comprarP  Zfator_reducaor0  rf  rR  Zquantidade_comprarU  rV  rW  rX  rY  Zvalor_portfolio_finalZvalor_portfolio_limite_finalZganho_finalr   Zpercentuais_finaisZ	pct_finalZsaldo_moedar   r   r   fase_balanceamento_portfolio  s  &







	$
""

 


 




	$

$$"
ri  c               
   C   sJ  zt t} | rt| tstd W dS dd tD }t|}dd |D }t|||}t||||}dd |D }t	| |} || d< t
t | d	< || d
< tt|  td td td td| dd   td| dd  td| dd dt  td|ddt  | dd}|dkrtd|| ddt  td|| d d dd td  |D ]@}||d}	||d}
|
r|	|
 nd}||d}| d!i |d}t| d"|	d#d$d%|d&d$|d'd(|dd)  qtd* | W S  ty$ } ztd+|  t  W Y d}~dS d}~ww ),u(   Exibe relatório detalhado do portfóliou*   Portfólio não inicializado ou corrompidoNc                 S   r  r  r   r  r   r   r   r     r  z'relatorio_portfolio.<locals>.<listcomp>c                 S   r  r	  r
  r  r   r   r   r     r  z'relatorio_portfolio.<locals>.<dictcomp>c                 S   r  r	  )rl   ro   r  r   r   r   r     r  Zprecos_atuaisZtimestamp_precosvariacoes_24hG
======================================================================u   RELATÓRIO DO PORTFÓLIOF======================================================================zFase: r  Zdesconhecidau   Data de Início: r  r   r$  r,   r   r   zValor Atual: rE   zGanho/Perda: r^  z	Ganho %: rZ   r1   r_  zF----------------------------------------------------------------------r!  z: Saldo=z15.8fz | zValor: z12.2fz6.2fr]  r   zG======================================================================
u   Erro ao exibir relatório: )r'   r   
isinstancedictrS   r%  rs   r|   r   r   r   rH   rI   r   r   rO   ra   upperro   r$   rQ   r   	print_exc)r   rp   rq   rx   rI  r   rj  Zcapitalrr   r  r{   r   Zpctrh  r&   r   r   r   relatorio_portfolio  sZ   

rq  c               
   C   s  t d t d t d t \} }d}d}z	 trNt rNt }t d t d |r?t d|d  t d	|d
  t d t d t  W dS |d7 }|d7 }z3| d dkrct| |} n| d dkrnt| |} |t	kr{t
 }|ry|} d}t dt d tt W n% ty } ztd| d|  t  tt W Y d}~nd}~ww q ty   t d t
  Y dS  ty } ztd|  t  W Y d}~dS d}~ww )u   Função principalrk  u/   ROBÔ DE BALANCEAMENTO DE PORTFÓLIO - INICIADOrl  r   Tu    ⏹️  SINAL DE PARADA RECEBIDOzSolicitado por: Z
pedido_porzTimestamp: rN   u   Encerrando robô gracefully...rZ   r  r#  r  u   
Próximo ciclo em z segundos...zErro no ciclo r   Nu!   
Robô interrompido pelo usuáriozErro fatal: )rO   r(  controlador_stopZ
deve_pararZobter_info_paradara   Zlimpar_stopr\  ri  TEMPO_REFRESHrq  TEMPO_CICLOr<   rd   r$   rQ   r   rp  KeyboardInterrupt)r   rp   Zcontador_cicloZcontador_relatoriorK   Zestado_atualizador&   r   r   r   main  s`   

%rv  __main__)NNNN)N)q__doc__r9   ZpandasZpdr<   r    Zlogging.handlersr   r   localer   r   r   r   pytzZcoloramar   r   r   rr  ImportErrorr   r   r'   	setlocaleLC_ALLErrorr  ra   ZAPI_KEYZ
API_SECRETZAPI_KEY_TESTNETZAPI_SECRET_TESTNETZTESTNETro   rb   r   r;   rt  rs  Z	TIMEFRAMErg  r   ZSALDO_MINIMOrE  r   rF  r   r  rG  r%  ZLOG_FILENAMEr   	getLoggerloggersetLevelINFOhandlershandlerremoveHandlerZrotating_handler	Formatter	formattersetFormatter
addHandlerrI   rB   rA   Z	chave_apiZchave_secretar:   r_   Zset_sandbox_moderK   r>   Zload_marketsrT   r@   r$   r&   r#   rO   rQ   rS   rU   rj   rl   rn   rs   rw   r|   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r(  r\  ri  rq  rv  __name__r   r   r   r   <module>   s(  











 

'.' Pe#$
!<!]< ]  BB
7
