o
    Iej                    @   sD  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 ddlmZ zddlZW n eyU   dZY nw zddlZdZW n eyn   dZed	 Y nw z
dd
lmZ dZW n ey   dZdd ZY nw e  dd Zdd Zdd Zdd Z 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'Z0e1e'(d(d)Z2e1e'(d*d)Z3e'(d+d,Z4e/e'(d-d.Z5e/e'(d/d0d1 Z6e/e'(d2d3Z7e/e'(d4d1Z8e5Z9e/e'(d5d6Z:e9e: Z;e1e'(d7d3Z<e/e'(d8d9Z=e5Z>e'(d:dZ?e/e'(d;d<Z@e'(d=dZAe'(d>dZBe'(d?g ZCd@ZDdAZEeF ZGeGHejI eGjJdd D ]ZKeGLeK qeeDdBdCdDdEZMeMHejI eNdFZOeMPeO eGQeM eRdGZRdHdI ZSeS aTe-re+ZUe,ZVne)ZUe*ZVeWeUeVddJdKtTdLdMZXe-reXYd eZdN e[dN n	eZdO e[dO e?reZdP e[dP n	eZdQ e[dQ eZdRe=dSdT e[dRe=dSdT eZdUe8dSdV e[dUe8dSdV zeX\  eZdW e[ej]dX ej^  W n" e_yg Z` zeadYe`  e[dYe`  W Y dZ`[`ndZ`[`ww dZd[ Zbd\d] Zcd^d_ Zdd`da Zedbdc Zfddde Zgdfdg Zhdhdi Zidjdk Zjdldm Zkdndo ZlddpdqZmdrds Zndtdu Zodvdw Zpdxdy Zqdzd{ Zrd|d} Zsd~d Ztdd Zudd Zvdd Zwdd Zxdd ZydddZzdd Z{dd Z|dd Z}dd Z~dddZdd Zdd Zdd Zdd ZdddZdd Zdd Zdd ZdddZdddZdd Zdd Zedkr e  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Style)DecimalTFu=   ⚠ requests não instalado - SQLite não estará disponível)obter_url_apic                   C   s   dS )Nz%http://localhost/projetos/rex/api.php r
   r
   r
   3/var/www/html/robo.fernandesemelo.com.br/rex/rex.pyr	   *   s   r	   c                 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filer
   r
   r   carregar_configuracoes1   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_portfolio6   s   "r   c              
   C   s  t sdS z\ddl}tjtjtd}||}| }t	
t }	|d| |t|t|t||	f |  |j}
|  td|
 d|  d| d	 td
|  d| d|d W dS  ty } ztdt|  tdt|  W Y d}~dS d}~ww )u  
    Registra um trade no banco de dados SQLite (DIRETO, sem API HTTP)
    
    Args:
        moeda (str): Código da moeda (BTC, ETH, etc)
        tipo (str): Tipo de trade:
            - compra_inicial: Compra na fase inicial de construção do portfólio
            - venda_balanceamento: Venda durante rebalanceamento normal
            - compra_balanceamento: Compra durante rebalanceamento normal
            - compra_ajuste_capital: Compra quando capital é incrementado (novo capital investido)
            - venda_ajuste_capital: Venda quando capital é diminuído (capital retirado)
        preco (float): Preço unitário
        quantidade (float): Quantidade
        valor (float): Valor total
    
    Returns:
        bool: True se registrado com sucesso, False caso contrário
    Fr   N	trades.dbz
            INSERT INTO trades (moeda, tipo, preco, quantidade, valor, timestamp)
            VALUES (?, ?, ?, ?, ?, ?)
        u#   ✓ Trade registrado no SQLite: ID z -  ()u   ✓ Trade registrado no DB:   @ $.8fTu'   ⚠ Erro ao registrar trade no SQLite: u   ⚠ Erro ao registrar trade: )SQLITE_DISPONIVELsqlite3ospathjoindirname__file__connectcursorr   nowtimezone	isoformatexecutefloatcommitZ	lastrowidcloseloggingdebuglog_sucesso	Exceptionwarningstr	log_aviso)moedatipopreco
quantidadevalorr%   db_pathconnr,   	timestampZtrade_ider
   r
   r   registrar_trade_sqlite;   s.   
rD   c                  C   s   t sg S zSddl} tjtjtd}| |}| }|	d g }|
 D ]}||d |d |d |d |d |d	 |d
 d q(|  tdt| d |W S  tyu } ztdt|  g W  Y d}~S d}~ww )us   
    Obtém todos os trades do banco de dados SQLite
    
    Returns:
        list: Lista de trades do SQLite
    r   Nr   z
            SELECT id, moeda, tipo, preco, quantidade, valor, timestamp 
            FROM trades 
            ORDER BY id DESC
            LIMIT 10000
           r               )idr;   r<   r=   r>   r?   rB   u   ✓ Obtidos z trades do SQLiteu$   ⚠ Erro ao obter trades do SQLite: )r$   r%   r&   r'   r(   r)   r*   r+   r,   r0   Zfetchallappendr3   r4   r5   lenr7   r8   r9   )r%   r@   rA   r,   tradesrowrC   r
   r
   r   obter_trades_sqliten   s6   



rO   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: )r&   r'   existsr   r   r   r4   r8   JSONDecodeErrorerrorr7   )r   r   datarC   r
   r
   r   carregar_estado_portfolio   s(   
"rU   c           	   
   C   sh  zt j| r$t| ddd}t|}W d   n1 sw   Y  ni }d|vr.i |d< |D ]4}||d}|dksB||d v rdt|trQt	|
td}nt	tt|
td}||d |< q0tt |d< t| d	dd}tj||d
dd W d   n1 sw   Y  td|   W dS  ty } ztd|  W Y d}~dS d}~ww )uE  
    ✓ NOVO: Salva as quantidades REAIS de cada moeda no portfolio_estado.json
    Isso serve como "fonte de verdade" para recuperação em caso de parada do robô
    
    Args:
        filepath: Caminho do arquivo de estado
        moedas: Lista de moedas
        saldos: Dicionário com os saldos atuais da exchange
    r   r   r   Nquantidade_carteira_realr   
0.00000001Ztimestamp_carteira_realr   r   Fr   u,   ✓ Quantidades reais da carteira salvas em z%Erro ao atualizar quantidades reais: )r&   r'   rQ   r   r   r   get
isinstancer   r1   quantizer9   r   r-   r.   r/   r   r4   infor7   rS   )	r   moedassaldosr   r   r;   saldoZsaldo_precisorC   r
   r
   r   "atualizar_quantidade_carteira_real   s4   

r_   c                 C   s  i }d| vr|S | d }d}|D ]_}||v ro|| }| |d}tt|}	tt|}
tt|
|	 }||krot|tt|	t|
| d }|dkrot|	tdt|
tdt|
|	 tdt|dd||< q|rt	d	 t	d
 |
 D ]7\}}t	d| d t	d|d  d|  t	d|d  d|  t	d|d  d|d  d qt	d t	d |S )u  
    ✓ NOVO: Verifica se há discrepâncias entre as quantidades salvas e as atuais
    Se houver diferenças significativas, registra em log para auditoría
    
    Args:
        estado: Estado do portfólio carregado
        moedas: Lista de moedas
        saldos: Dicionário com saldos atuais da exchange
    
    Returns:
        dict: Relatório de discrepâncias encontradas
    rV   h㈵>r   d   皙?rW   r   )quantidade_salvaquantidade_atual	diferenca
percentualu,   
🚨 DISCREPÂNCIAS DETECTADAS NA CARTEIRA:z:   (Quantidades salvas vs. quantidades atuais na exchange)u      • :z     - Salva: rc   r!   z     - Atual: rd   u        - Diferença: re   r   rf   %)uF   
💡 SUGESTÃO: As quantidades ATUAIS serão usadas como referência.uU      Se quiser sincronizar forçadamente, as quantidades da exchange têm precedência.)rX   r   r9   r1   absmaxrZ   roundr4   r8   items)r   r\   r]   discrepanciasrc   Zlimite_minimor;   Z	qtd_salvaZ	qtd_atualZqtd_salva_dZqtd_atual_dre   Zpercentual_diffr[   r
   r
   r   verificar_discrepancia_carteira   s@    


"

rn   zpt_BR.UTF-8zPortuguese_Brazil.1252 configuracoes_balanceamento.jsonZapi_key Z
api_secretZapi_key_testnetZapi_secret_testnettestnetZ
moeda_baseUSDTcapital_inicial  Ztempo_ciclorH   Ztempo_refresh_tela	timeframeZ5mvariacao_balanceamento       @Zcomissaorb   ra   Zsaldo_minimo
   Zsaldo_minimo_usdtZvariacao_histerese_incrementog)\(?Zciclos_histerese_ativavalor_minimo_operacao_inicialg)\(@Zcomprar_imediatamenteZreinvestir_ganhos_percentual2   reset_portfolio_ao_iniciarZaguardar_melhor_hora	portfoliozlog_balanceamento.logzportfolio_estado.jsoni  @rF   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 -1021rt   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timeri   r4   r8   printr   YELLOWr   	RESET_ALLr7   )Zpublic_exchangeZserver_timeZ
local_timetime_offsetrC   r
   r
   r   sincronizar_timestamp_binance]  s   2r   Z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   r-   r.   strftimer4   r[   r   mensagemrB   Zmsg_completar
   r
   r   log_info  s   
r   c                 C   B   t td}d| d|  }t| ttj| t	j
  dS )zLog e print de error   r   z] ERRO: N)r   r-   r.   r   r4   rS   r   r   ZREDr   r   r   r
   r
   r   log_erro     
r   c                 C   r   )zLog e print de avisor   r   z	] AVISO: N)r   r-   r.   r   r4   r8   r   r   r   r   r   r   r
   r
   r   r:     r   r:   c                 C   r   )zLog e print de sucessor   r   u   ] ✓ N)r   r-   r.   r   r4   r[   r   r   GREENr   r   r   r
   r
   r   r6     r   r6   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áticorF   lastr3   N-1021	TimestamprE   zErro de timestamp ao obter  , resincronizando... (tentativa /r    r   r   u   Erro ao obter preço de rP   )rangeexchangefetch_tickerrX   r1   r7   r9   r:   r   r   r   r   sleepr   )symbolmax_tentativas	tentativatickerZpricerC   erro_strr
   r
   r   obter_preco_atual  s*   
 

r   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áticorF   Z
percentageZchangeZchangePercentNr   r3   r   r   ra   g        r   r   rE   r   r   u    Erro ao obter variação 24h de rP   )r   r   r   rX   r1   r7   r9   r   r   r   r   r   r   )	r   r   r   r   ZvariacaoZclose_priceZ
open_pricerC   r   r
   r
   r   obter_variacao_24h  s2   

 

r   c                 C   s  d}t |D ]}z%t }|di }|| d}t|tr%t|W   S tt|W   S  ty } zQt|}d|v sAd|v rj||d k rjtd|  d|d  d	| d
 t	 a
t
tjd< td W Y d}~q||d krztd|  d|  tdW  Y d}~  S d}~ww dS )ua   Obtém saldo disponível de um ativo com retry automático (retorna Decimal para precisão total)rF   freer   r   r   rE   z$Erro de timestamp ao obter saldo de r   r   r    r   r   NzErro ao obter saldo de rP   )r   r   Zfetch_balancerX   rY   r9   r   r7   r:   r   r   r   r   r   r   )Zativor   r   Zbalancer   r?   rC   r   r
   r
   r   obter_saldo  s0   
 

r   c                 C   s*   i }| D ]}t |||< qt t|t< |S )u.   Obtém saldos de todas as moedas do portfólio)r   
MOEDA_BASE)r\   r]   r;   r
   r
   r   obter_saldos_portfolio  s
   r   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 testnetr   r3   Z
baseVolumer   u#    não tem preço válido (último: r    Nz* tem volume zero - sem liquidez no testnetTu    OK - Preço: r#   z
, Volume: zErro ao verificar par rP   )r   symbolsr   rX   r7   r9   )r   r   Z
last_pricevolumerC   r
   r
   r   verificar_par_disponivel  s   


 $r   c                 C   sR   t |td}| D ]}||d}||d}|dkr&|r&|t || 7 }q
|S )u+   Calcula o valor total do portfólio em USDTr   )r1   rX   r   )r\   r]   precosvalor_totalr;   r>   r=   r
   r
   r   obter_valor_portfolio9  s   r   c                 C   s   d}t  }|D ]%}|ddv r|t|dd7 }q|ddv r,|t|dd8 }qd}| D ]"}|dkrS||d}	||d}
|	dkrS|
dkrS|t|	|
 7 }q1|| }|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 (não usado mais, mantido para compatibilidade)
    
    Returns:
        float: Ganho/Perda real em USDT (apenas das moedas compradas)
    r   r<   )compra_inicialcompra_balanceamentocompra_ajuste_capitalr?   )venda_balanceamentovenda_ajuste_capitalrr   )rO   rX   r1   )r\   r]   r   r   Zcapital_investido_moedasrM   ZtradeZvalor_moedas_atualr;   r>   r=   Z
ganho_realr
   r
   r   calcular_ganho_realD  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 $r   z (ganhos congelados)ra   zGanho Total: $z | Reinvestindo: $r   .0fz%) | Congelado: $zPerda Atual: $u    (portfólio em recuperação)u<   Sem ganhos/perdas ainda (portfólio em fase de acumulação))r   rj   CAPITAL_INICIALREINVESTIR_GANHOS_PERCENTUAL)
Zvalor_portfolio_realr\   r]   r   r   ganho_totalZvalor_limiteZganho_reinvestidoZ
status_msgZganho_congelador
   r
   r   calcular_limite_reinvestimentoo  s,   
r   c           	      C   sz   i }|dkr| D ]}d||< q|S | D ]'}| |d}| |d}|dkr6|r6t|| }|| d ||< qd||< q|S )u9   Calcula os percentuais atuais de cada moeda no portfólior   ra   )rX   r1   )	r\   r]   r   r   percentuaisr;   r>   r=   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)
    rG   rF   g      ?)rL   sorted	enumeraterK   )r   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)r\   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 ~700 candles (≈29-30 dias), a confiança dos padrões é excelente.
    
    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   i  )limit   zCandles insuficientes para z (precisa 150, tem r    iDNc                 S      g | ]}|d  qS )r   r
   .0candler
   r
   r   
<listcomp>      z0obter_melhores_precos_30dias.<locals>.<listcomp>c                 S   r   )rF   r
   r   r
   r
   r   r     r   r   ra   rt   tzz%Y-%m-%drF   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   rE   r   rF   rG   rH   c                 S   s   i | ]}|g qS r
   r
   )r   ir
   r
   r   
<dictcomp>A  s    z0obter_melhores_precos_30dias.<locals>.<dictcomp>rI   r   rG   rH   c                 s       | ]}|d  V  qdS )r   Nr
   r   cr
   r
   r   	<genexpr>O      z/obter_melhores_precos_30dias.<locals>.<genexpr>c                 s   r   )r   Nr
   r   r
   r
   r   r   P  r   )keyr   rE   r   r   r   u   : Nenhum dia com dados válidosc                        |  d S )Nr   r
   xZgrupos_horasr
   r   <lambda>i      z.obter_melhores_precos_30dias.<locals>.<lambda>c                    r   )Nr   r
   r   r   r
   r   r   j  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   z  r   zLobter_melhores_precos_30dias.<locals>.calcular_hora_media.<locals>.<genexpr>r   02drg   )sumrL   r   )Z
horas_listZtotal_minutosZmedia_minutosZhorasZminutosr
   r
   r   calcular_hora_mediat  s   z9obter_melhores_precos_30dias.<locals>.calcular_hora_mediar   )r   
hora_media	confiancaZocorrencias)total_registrosregistros_validostotal_outlierspercentual_outliers
total_dias)melhor_hora_compramelhor_hora_vendaZanaliserB     rg   u       📊 Registros: z coletados | u    válidos (r~   rh   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 rP   u    Erro ao obter melhores preços: )r   r   Zfetch_ohlcvrL   r:   r   setr   r   fromtimestampr.   r   rK   hourrl   r   r   minrX   rj   rk   r-   r/   r   r7   r   )-r\   melhores_precosru   r;   r   Zcandlesr   ZhighsZlowsZhighs_filtradosZoutliers_highZlows_filtradosZoutliers_lowr   r   r   r   Zcandles_por_datar   r   rB   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_moedarC   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)r   r   r   PORTFOLIO_STATE_FILErL   r7   r   )r   r\   r   rC   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buy)\(?Gz?z  Criando limit order: r!          Preço Atual: r#   u       Preço Limite: timeInForceZGTCr   rJ   r   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: )r   r   r   r   create_limit_orderr   r-   r.   r   rX   r6   r7   r9   r:   Zcreate_market_order	traceback
format_exc)r   sider>   preco_atualpreco_limiteordemrB   Zorder_idr  r  r  Ze_limitr   Ze_marketrC   r
   r
   r   executar_ordem  s   
	$$

!"r  c                 C   s   t | dt  S )u   Aplica comissão à quantidaderE   )r1   COMISSAO)r>   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 $r   u    é inferior ao mínimo de $z exigido pela BinanceFTz validada: $)VALOR_MINIMO_OPERACAO)Zvalor_operacaor;   Ztipo_operacaor   r
   r
   r   validar_valor_operacao,  s   r  c                 C   sv   d| vri | d< | d  |i }|sdS | d|}| dd}|| }tdt| }|dkr6| d |= dS d||fS )u  
    ✓ NOVO: Verifica se a histerese assimétrica está ativa para uma moeda
    
    Quando um trade superdimensionado ocorre (ex: venda de $5.20 por notional insuficiente),
    marca uma flag de histerese que força:
    - Gate MAIOR (0.25%) para a direção OPOSTA do último trade
    - Gate normal (0.17%) para a MESMA direção
    
    Args:
        estado: Estado do portfólio
        moeda: Símbolo da moeda
        contador_ciclo: Contador do ciclo atual
    
    Returns:
        tuple: (histerese_ativa, ultima_direcao_trade, ciclos_restantes)
               - histerese_ativa: bool (True se histerese está ativa)
               - ultima_direcao_trade: 'compra' ou 'venda' ou None
               - ciclos_restantes: int (ciclos até desativar histerese)
    histerese_asimetricaFNr   ciclo_marcadoultima_direcao_tradeNr   T)rX   rj   CICLOS_HISTERESE_ATIVA)r   r;   contador_cicloZhisterese_dataZciclo_inicioultima_direcaoZciclos_decorridosciclos_restantesr
   r
   r   verificar_histerese_ativa?  s   

r  c           	   	   C   sr   t || |\}}}|stddfS ||kr#t}d| d| d| d}nt}d| d| d	| d| d	}|||fS )
u9  
    ✓ NOVO: Obtém o gate de variação (threshold) considerando a histerese assimétrica
    
    Args:
        moeda: Símbolo da moeda
        estado: Estado do portfólio
        contador_ciclo: Contador do ciclo atual
        direcao_planejada: 'compra' ou 'venda' (a direção que o robô quer fazer AGORA)
    
    Returns:
        tuple: (gate_variacao, motivo, ciclos_restantes)
               - gate_variacao: float (e.g. 0.17 ou 0.25)
               - motivo: str (explicação do por quê)
               - ciclos_restantes: int (se histerese ativa)
    zGate Normalr   u#   Histerese Ativa (MESMA direção): u$    novamente
        → Gate Normal: z% (z ciclos restantes)u,   Histerese Ativa (DIREÇÃO OPOSTA): último=z, agora=u   
        → Gate Aumentado: )r  VARIACAO_ENTRADA_NORMAL*VARIACAO_ENTRADA_OPOSTA_APOS_TRADE_FORCADO)	r;   r   r  Zdirecao_planejadahisterese_ativar  r  Zgatemotivor
   r
   r   !obter_variacao_gate_com_histereseg  s   

r"  c                 C   sh   d| vri | d< ||t   d| d |< td| d|   tdt d tdt d d	S )
uF  
    ✓ NOVO: Marca que uma operação superdimensionada foi feita nesta moeda
    Ativa a histerese assimétrica para proteger contra ping-pong
    
    Args:
        estado: Estado do portfólio
        moeda: Símbolo da moeda
        direcao_trade: 'compra' ou 'venda'
        contador_ciclo: Contador do ciclo atual
    r  )r  r  rB   u   🔒 Histerese Ativa para rP   u>      Próxima operação na direção OPOSTA precisará de gate %u      Duração: z ciclosN)r   r-   r/   r:   upperr   r  r  )r   r;   Zdirecao_trader  r
   r
   r   marcar_histerese_ativa  s   
r%  c                 C   s   |dkr	|d }n|d }t | | }|tk r7d| d|  d|ddtdd	| d
d|d
}d|||fS d|d|d|fS )u  
    Valida se o valor notional da ordem (com preço limite) atende ao mínimo da Binance.
    CRÍTICO: A Binance valida usando o preço da ordem, não o preço de mercado
    
    Args:
        quantidade: Quantidade a vender/comprar
        preco_atual: Preço atual de mercado
        side: 'buy' ou 'sell'
        moeda: Símbolo da moeda (para log)
    
    Returns:
        tuple: (é_válido, valor_notional, mensagem, preco_limite)
    r   r  r  zNotional insuficiente para r   z): $r   z < $z | Qtd: r#   u    × Preço Limite: $FTzNotional OK: $)r1   r  r$  )r>   r  r  r;   r  valor_notionalr   r
   r
   r   validar_notional_limite_ordem  s    
r'  c              
   C   sx  |du rt d}t|dd}t|dd}t|dd}d	| vr(i | d	< | d	 |i }	|| d	 v rT|tk rTtd
| d|ddt d td | d	 |= dS |	d|}
||
k rr|
| }td| d| d|
 d dS d|	vrtd|  td td|dd td|  td|  td| d dt	 
 |d |||| dg d!	| d	 |< td" td#||   d$d|fS |	dd%}|	d&d}td'| d(| d) |	d*g }t|d%kr@|d+ d, d-kr@|d+ d. }|d/ }td
| d0|dd d1|d+ d,< ||d+ d2< |dur;t|d kr;|d3 d, d4kr;d5|d3 d,< td6|dd7 d8|d%fS |durmd*|	v rm|	d* }t|d%krm|d+ d, d1krmd5|d+ d,< td6|dd7 g d9}|t|kr|}td:t| d; td<| d= d%| d	 | d< || | d	 | d< tt|  d$d|fS t|d  t|d  }|| }td'| d(| d>t|  td?|dd@| d |d/ }tdA|dd tdB|dd tdC| dD| dE tdF|ddG||   ||t	 
 d-dH}| d	 | d* | || d	 | d&< |d  | d	 | d< || | d	 | d< tt|  d$d|fS )Iun  
    ✓ NOVO: Gerencia moedas residuais com Retry Inteligente + Espera de Ciclos
    
    Estratégia INTELIGENTE:
    1. Primeira falha: Guardar estado + aguardar ciclos (deixar preço flutuar)
    2. Ciclo N+X: Tenta novamente com valor original
    3. Se falhar 2ª vez: Aguarda mais ciclos (backoff progressivo)
    4. Após N falhas consecutivas: Começa progressão de valores ($5.2 → $5.8 → $6.5...)
    5. Normalização: Se desequilíbrio normalizar, remove de residuais
    
    Args:
        estado: Estado do portfólio
        moeda: Símbolo da moeda que falhou
        diferenca_percentual: Desequilíbrio atual em %
        contador_ciclo: Contador do ciclo atual
        capital_rastreado: Capital total para cálculo de percentual
        config: Configurações do portfólio
    
    Returns:
        tuple: (deve_tentar_novamente, quantidade_ajustada, ciclos_skip)
               - deve_tentar_novamente: bool (True = tentar agora)
               - quantidade_ajustada: float (valor em USDT se deve_tentar_novamente=True)
               - ciclos_skip: int (ciclos a pular se deve_tentar_novamente=False)
    Nro   ry   g@Zciclos_espera_notional_falharH   Ztentativas_antes_progressaorF   moedas_residuais_historico       ✅    : Desequilíbrio NORMALIZADO (r   % < rh   D       💚 Removendo de residuais - voltando ao rebalanceamento normalr  proxima_tentativa_ciclou       ⏸️  z: Aguardando ciclos (u    ciclos ainda até ciclo r    tentativas_falhadasu       🔴 PRIMEIRA FALHA: u       � Config carregada:u!          - Valor mínimo inicial: $z USDz       - Ciclos espera: u'          - Tentativas antes progressão: u       💤 Aguardando u=    ciclos para deixar preço flutuar naturalmente (evitar spam)!notional_insuficiente_ciclo_vendarE   )	r!  rB   Zdesequilibrio_inicialr.  Zciclos_espera_inicialr  r-  valor_testadohistorico_testesu7       ⏳ Estado registrado em moedas_residuais_historicou"       ⏳ Próxima tentativa: ciclo Fr   r0  u       ♻️  z: Tentativa z  (continuando da falha anterior)r1  	resultadoZagendadar?   g\(\?z: TENTANDO valor agendado $Zem_testeZciclo_testesucessoZfalhau        ❌ Tentativa anterior com $z USD falhou no notionalT)rI         	   rx                              u-       🔄 CICLO COMPLETO: Já tentou todos os z valoresu!       💤 REINICIANDO: Aguardando u*    ciclos antes de recomeçar do menor valorr   u       📈 Escalando para $u    USD (índice u       🔢 Valor-alvo: $u#       🔢 Valor ajustado (÷0.98): $u       ⏳ Tentativa u)    será executada no próximo ciclo após z esperau       💤 Marcando para tentar $z USD no ciclo )r?   ZciclorB   r3  )r   r1   rX   r   VARIACAO_MINIMA_NORMALIZACAOr6   r   r:   r   r-   r/   rL   r   r   r   rK   )r   r;   diferenca_percentualr  capital_rastreadoconfigZvalor_minimo_inicialZciclos_esperaZtentativas_maxresidual_infor-  Zciclos_aindaZ
tentativasZvalor_testado_anteriorZ	historicoZvalor_agendadoZvalor_venda_ajustadoZvalores_progressionZciclos_pause_apos_maxZindice_progressaoZvalor_para_testarZtentativa_recordr
   r
   r   (processar_residual_com_retry_inteligente  s   


 
 




rG  c           	   
   C      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   rg   rE   u(   Erro ao extrair horário de compra para rP   NsplitrL   r   r7   r:   	r   r;   r   Zmelhor_horar   parteshora_iniciohora_fimrC   r
   r
   r   extrair_horario_compra_  &   
rQ  c              
   C      zEd| vs|| d vrW dS | d | }d|vsd|d vr!W dS |d d }| d}t|dkrCt|d }t|d }||fW S W dS  tyb } ztd	| d
|  W Y d}~dS d}~ww )u  
    Extrai a hora média de compra do estado do portfólio
    
    Args:
        estado: Estado atual do portfólio
        moeda: Símbolo da moeda
    
    Returns:
        tuple: (hora_media, minuto_medio) ou (None, None) se não encontrar
        Exemplo: (9, 30) para 09:30
    r   NNr   r   rg   r   r   rE   u+   Erro ao extrair hora média de compra para rP   NrK  r   r;   r   Zhora_media_strrN  r   ZminutorC   r
   r
   r   extrair_hora_media_compra  $   

rV  c           
      C      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: r~   rh   T:00u(   : ⏰ Aguardando melhor hora de compra (   %). Próxima às )rQ  r   r-   r.   r   )
r;   r   rO  rP  r   
hora_atualesta_dentroperiodo_strr   Zproxima_hora_comprar
   r
   r   eh_horario_compra_ideal     ra  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      )rQ  r   r-   r.   r   minuterj   	r;   r   rO  rP  _agorar^  minuto_atual	tempo_ater
   r
   r   calcular_tempo_ate_melhor_hora  s   
rj  c                 C     |du rd}t || \}}}t|| \}}|du r(t|  d dd|  dfS tjtd}|j}	|j}
t }d}d}z||krO||	  koK|kn  }n|	|kpV|	|k}|durp|d	 | |	d	 |
  }|dk ro|d
7 }nd}|rK|dur/|	|kr|
|kr|dur|dkrt	|  d|dd d}||d	 k rt | }||kr|d	 | }t	|  d|d	 dd|d	 dd|dd|dd
 |d }t
| ||d	 k st|  d|dd|dd|dd d|d	 |  dfW S t|  d|dd|dd|dd dd|  dfW S t|  d|dd|dd|dd dd|  d fW S t|  d|dd|dd|dd dd|  dfW S |dur_|dd!|dd|dd}nd"|dd#}|dur||krt|  d$|dd%| d& dd|  d'fW S t	|  d(| d d}	 tjtd}|j}	|j}
||kr||	  ko|kn  }n	|	|kp|	|k}|rt|d	 d)}t|  d*| d+|dd d||  d,fW S t | }|d	 }||krt|  d-| d. d||  d/fW S ||kr|| }t	|  d0|dd1| d2|dd3 |d4 }t
| q tyI } ztd5|  d6|  dd|  d7fW  Y d}~S d}~ww )8ua  
    Aguarda até a melhor hora de compra para a moeda usando hora média inteligente
    
    Args:
        moeda: Símbolo da moeda
        estado: Estado atual do portfólio
        tempo_maximo_minutos: Tempo máximo de espera em minutos (None = 30 min como padrão)
    
    Returns:
        tuple: (aguardou, tempo_esperado_minutos, mensagem)
    N   uM   : Análise de melhor hora não disponível - prosseguindo com compra imediataFr   u*   : Análise de melhor hora não disponívelr   rx   r     *   : Já está no período ideal, mas faltam r   #   min para hora média. Aguardando...   : Aguardando hora média... (r~   min decorridos,    min faltam até r   rg   r    x   u&   : ✓ Hora média de compra atingida (r[  rh   T   : Hora média atingidau!   : ✓ Horário ideal de compra! (rY     :59, confiança: u'   : Já está no horário ideal de comprau4   : ✓ Horário ideal de compra! (Passou hora média u<   : Já está no horário ideal de compra (passou hora média)   min até hora média (   próximo período é r\  2   : ⏳ Próxima janela ideal está muito distante (min > z!min). Pulando compra neste ciclo.2   : Pulando - próximo horário ideal muito distantez': Aguardando melhor hora de compra... (rE   u.   : ✓ Horário ideal de compra atingido após    min (confiança:    : Horário ideal atingido   : Tempo máximo de espera (z'min) atingido. Prosseguindo com compra.: Timeout de espera atingido: Aguardando (r   min, min restante)r   z"Erro ao aguardar melhor hora para rP   z*: Erro na espera - prosseguindo com compra)rQ  rV  r:   r   r-   r.   r   rd  r   r   r   r6   rk   r7   r   r;   r   Ztempo_maximo_minutosrO  rP  r   r   Zminuto_mediarg  r^  rh  Ztempo_inicialZintervalo_verificacaoZproximo_logZesta_no_periodoZtempo_ate_mediaZtempo_decorrido_segundosZtempo_faltandoZtempo_ate_media_msgr_  Ztempo_decorrido_minutosZtempo_restanterC   r
   r
   r   aguardar_melhor_hora_compra  s   
6
&&&&



$
 r  c           	   
   C   rH  )u?  
    Extrai a melhor hora de venda 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: (14, 17, 55.2) para o período 14:00-17:59
    r   rI  r   r   r   rJ  r   r   rg   rE   u'   Erro ao extrair horário de venda para rP   NrK  rM  r
   r
   r   extrair_horario_vendaq  rR  r  c              
   C   rS  )u  
    Extrai a hora média de venda do estado do portfólio
    
    Args:
        estado: Estado atual do portfólio
        moeda: Símbolo da moeda
    
    Returns:
        tuple: (hora_media, minuto_medio) ou (None, None) se não encontrar
        Exemplo: (15, 45) para 15:45
    r   rT  r   r   rg   r   r   rE   u*   Erro ao extrair hora média de venda para rP   NrK  rU  r
   r
   r   extrair_hora_media_venda  rW  r  c           
      C   rX  )u  
    Verifica se a hora atual está dentro do melhor período de venda 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)
    NFuI   : Dados de melhor hora de venda não disponíveis (análise em progresso)r   r   rY  rZ  u    : ✓ HORÁRIO IDEAL de venda! (r[  r~   rh   Tr\  u'   : ⏰ Aguardando melhor hora de venda (r]  )r  r   r-   r.   r   )
r;   r   rO  rP  r   r^  r_  r`  r   Zproxima_hora_vendar
   r
   r   eh_horario_venda_ideal  rb  r  c           	      C   s   t || \}}}|du rdS tjtd}|j}|j}||krB||  kr)|kr*dS  ||k r7|| d | }nd| | d | }n||ksJ||krLdS || d | }td|S )u   
    Calcula quantos minutos faltam até a melhor hora de venda
    
    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   rc  )r  r   r-   r.   r   rd  rj   re  r
   r
   r   $calcular_tempo_ate_melhor_hora_venda  s"   
r  c                 C   rk  )8u  
    Aguarda até a melhor hora de venda para a moeda usando hora média inteligente
    Útil para retirada de capital e rebalanceamento com otimização de preço
    
    Args:
        moeda: Símbolo da moeda
        estado: Estado atual do portfólio
        tempo_maximo_minutos: Tempo máximo de espera em minutos (None = 30 min como padrão)
    
    Returns:
        tuple: (aguardou, tempo_esperado_minutos, mensagem)
    Nrl  uU   : Análise de melhor hora de venda não disponível - prosseguindo com venda imediataFr   u3   : Análise de melhor hora de venda não disponívelr   rx   r   rm  rn  r   ro  rp  r~   rq  rr  r   rg   r    rs  u%   : ✓ Hora média de venda atingida (r[  rh   Trt  u    : ✓ Horário ideal de venda! (rY  ru  u&   : Já está no horário ideal de vendau3   : ✓ Horário ideal de venda! (Passou hora média u;   : Já está no horário ideal de venda (passou hora média)rv  rw  r\  rx  ry  z min). Pulando venda neste ciclo.rz  z&: Aguardando melhor hora de venda... (rE   u-   : ✓ Horário ideal de venda atingido após r{  r|  r}  z&min) atingido. Prosseguindo com venda.r~  r  r   r  r  r   z+Erro ao aguardar melhor hora de venda para rP   z): Erro na espera - prosseguindo com venda)r  r  r:   r   r-   r.   r   rd  r   r   r   r6   rk   r7   r   r  r
   r
   r   aguardar_melhor_hora_venda  s   
6
&&&&



$
 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
    ro   r   r   r   Nr{   TFr   r   r   u   ✓ Arquivo u    deletado para recriaçãou   ⚠ Não foi possível deletar rP   u8   ✓ Reset desativado automaticamente nas configuraçõesu2   ⚠ Não foi possível desativar reset na config: )r   r   r   rX   r   r&   r'   rQ   r   remover6   r7   r:   )config_filefrE  rC   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rt|| }td| d tdt|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t|dd W q* ty } ztd| d|  W Y d}~q*d}~ww q*tt}|durt|nd}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 $r   z USDTc                 S       i | ]}|t | d t qS r   r   r   r   mr
   r
   r   r          z+limpar_moedas_residuais.<locals>.<dictcomp>r`   u   
🔄 Vendendo z
 residual:z
   Saldo: r#   r!   u      Preço: $z   Valor: $r   r  r  r  ZIOCrE   u      ✓ z vendido com sucesso!u      ⚠ Venda parcial de z	 (restam r    u      ✗ Erro ao vender rP   Nu   ✓ RESET CONCLUÍDO: z moedas vendidasz  Valor recuperado: $z  Saldo USDT final: $z  Capital Inicial: $rz   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_INICIARr:   r   r   r   rX   r1   r   r   r	  r   r   r   r6   r7   r   rj   r  )r\   r   r]   Ztotal_usdt_recuperadoZmoedas_vendidasr;   r^   r=   Zvalor_vendar   r  r  saldo_depoisrC   saldo_usdt_finalZsaldo_usdt_final_floatZsaldo_usdt_residualr
   r
   r   limpar_moedas_residuais  sv   

 
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}|rTtd	 t| }t	|| |}|rWt
d
 t
d t
d t
d t
d td |d|dd}t|d krt| }t
d t
d|d t
dtd t
d|d t
d t|d< t|d< d|d< tt |d< tt| td nt|d k r|t }	t
d t
d|d t
dtd t
d|	d t
d|	dd  t|d< t|d< d|d!< |	|d"< tt |d#< tt| td$ n7t|d< |dd%rtd& d%|d< |d!d%rtd' d%|d!< |dd%u s.|d!d%u r3tt| d(|vs=|d( sFt
d) t|| }t| }tt| | || fS t|   fd*d| D }
|
rt
d+t|
 d,d-|
  d.tt ttd%d%d|  g ||d/}td0 t| }tt| | || fS d1tt ttd%d%dg |  ||d/}td2 td3t d4t  td5d-|   td6|  td7|  td8 t| }tt| | || fS )9u!   Inicializa o estado do portfólioc                 S       g | ]}| d dr|d qS ativaTr;   rX   r  r
   r
   r   r     r  z)inicializar_portfolio.<locals>.<listcomp>c                 S   &   i | ]}| d dr|d |d qS )r  Tr;   rf   r  r  r
   r
   r   r        & z)inicializar_portfolio.<locals>.<dictcomp>c                 S   r  )r  Tr;   preco_comprar  r  r
   r
   r   r     r  u$   Após reset: Capital disponível = $r   r   u+   Carregando estado anterior do portfólio...uP   
⚠️ O robô detectou discrepâncias entre as quantidades salvas e as atuais.z   Isso pode ocorrer se:u&      • O robô foi parado abruptamenteu>      • Houve transações manuais na exchange durante a paradau;      • Problemas de sincronização com cálculos de ganhosuA   
   → Usando as quantidades ATUAIS da exchange como referênciarD  rs   r   ra   u&   
💰 INCREMENTO DE CAPITAL DETECTADO!u      • Capital Anterior: $u      • Capital Atual: $u      • Incremento: $uE      ⏰ Aguardando melhor hora de compra para investir novo capital...Tem_incremento_capitalZdata_incremento_capitalu3   ✓ Estado com incremento de capital SALVO no discou(   
💸 DIMINUIÇÃO DE CAPITAL DETECTADA!u      • Diminuição: $uL      ⏰ Iniciando retirada de capital - serão vendidas moedas para retirar $z...em_retirada_capitalvalor_retiradaZdata_retirada_capitalu5   ✓ Estado com diminuição de capital SALVO no discoFuI   ✓ Incremento de capital concluído! Voltando ao rebalanceamento normal.uG   ✓ Retirada de capital concluída! Voltando ao rebalanceamento normal.r   z>Atualizando dados de melhor hora de compra antes de iniciar...c                    s    g | ]}  |d dkr|qS )r   r`   r  r  Zsaldos_reaisr
   r   r   u  r  u$   Portfólio detectado no testnet com z	 moedas: , balanceamento)fasedata_iniciors   rD  r  r  r  moedas_compradasmoedas_pendentespercentuais_alvoprecos_comprau9   ✓ 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  r   r   r   rU   r   r   rn   r:   rX   r   r   r-   r.   r/   r   r6   r   r_   rL   r(   copyr   )r\   r  r  Zsaldo_finalZestado_existentesaldos_atuaisrm   rD  Z
incrementoZ
diminuicaoZmoedas_com_saldor   r
   r  r   inicializar_portfolio  s   




 

r  c           F      C   s6	  t d d| vs| d std t| |} t|}dd |D }|td}t|}|D ]}||d}||d}	|dkrH|	rH|t||	 7 }q,t d|d |dkratdkratd	td t d
|ddt  t||||| \}
}}}t	}t
dkr|dkrt|trt|n|}t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t|dd ||vr|| q||vr_|| tr|dkr|| | d }|dk}n d}nt|| \}}|}|s6|dkr6|| | d }|dk}|r|r||dd }|
| }|| dt  }d}|tk rn|} t}|| }d}td| dd|dd |dkrwt|nt}!t||d\}"}#td|!| }$|"r|tkr|$|krtd | d! t d"|d t d#|d t d$|ddt  t d%t|d d}%| d&t }&t|&\}'}(|'std'|& d( td)|(  td*| d+ qt d,|(  tp| d-d})|)r+t d. t|| \}*}+},|*d/u rtd0|,  q|*r#td0|,  nt d0|,  nt d1 t|}-t| d&t }|r|dkrt||d2|\}.}/}0}1|.std0|0  | d3t}2t| ||d ||2t\}3}4}5|3ov|4ov|4dk}%|%r|4| }6t d4 t d5t|dd6|6d t d7|dd8|4d |6}|4}ntt|  qt d0|0  ntd9|  qt |&d2|}7|7rt!"d t|}8|8|-krtd: t!"d; t|}8|8|- }9|9dkrWtd<t|9dd|  ||vr|| || d< |%r	t#| |d| || d=i v rF| d= | }:d>|:v rFt$|:d> dkrFd?|:d> d@ dA< tdB|:dC ddD | d= |= tdE| dF t|9| };t%|dG|t|9|; n*tdH| dI tdJt|-dd| dKt|8dd|  tdL tdM|& dN t!"d qtdO tdP |
dkr||
 d nd}<| d3| dQd}2t| ||<||2t\}3}4}5|3r|4r|4dkrt dR|dd8|4d t dS|5 dT tt|  qtdU| dV tt|  q|"st|# qtdW| dX|ddY|!ddZ qtr|dkr|| | d }=| d[|=d\d]}>nB| d^}>n<t&| |\}?}@}A|?d/urUt'j(t)d_j*}Bt+|| }C|?d`da|@d`db}D| dc|D dd|Ad\de|Cdfdg|d	}>n| dh}>t |> q||vri|| q|| di< || d< t$|dkrtdj dk| dl< t'(t), | dm< t|}Et-t||E tt|  | S )nuU   
    Fase 1: Completa o portfólio comprando as moedas até os preços definidos
    u9   
========== FASE DE CONSTRUÇÃO DO PORTFÓLIO ==========r   QAtualizando dados de melhor hora de compra para respeitar AGUARDAR_MELHOR_HORA...c                 S   r  r  r  r  r
   r
   r   r     r  z-fase_construcao_portfolio.<locals>.<dictcomp>r   zSaldo USDT atual: r   u;   Testnet sem USDT. Usando capital inicial como referência: "   Valor Total do Portfólio (REAL): r!   rz   !   
🔒 MODO PROTETOR: Congelando $ em USDT residual+      Saldo mínimo protegido ajustado para: $r  r  r  r`   u   : Já possui r#   z em carteirara   TrH   rE   F       ⚡ Valor ajustado de $ para $    (mínimo de operação)compra
>>> Comprando  <<<r  u       Preço Alvo:     Valor:     Quantidade: r        ⚠️ Par #    não está disponível no testnet!       Pulando compra de     - par indisponível     ✓ r  u@     ⏰ Buscando melhor momento de compra para maximizar lucros...Nr   u9     → Compra imediata (aguardar_melhor_hora desabilitado)r   rD  u*       💡 Teste Progressivo (construção):       Quantidade:     →        Valor: $    → $9   Não foi possível obter preço para validar notional de C     Saldo não mudou na primeira verificação, aguardando mais 2s...r   &   ✓ Compra verificada: Saldo aumentou r(  r1  r5  r2  r3         ✅ Teste com $r0   EXECUTADO com sucesso!	       💚 /    removido de residuais após teste bem-sucedidor      ✗ 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 testnetu:     ✗ Ordem de compra falhou durante construção inicial!Y        Provável causa: Condição de corrida (preço mudou entre validação e execução)rs   -     💡 Escalando valor para próximo ciclo: $        Próxima tentativa em 	 ciclo(s)
     ⏸️  2   : Aguardando melhor horário para tentar novamentez Saldo insuficiente para comprar z
 (precisa z, tem r    u*   : Preço muito alto para compra imediata (r~   z% acima). Esperando melhora...u-   : Preço alvo não definido na configuraçãor   r   rY  rZ  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).r   r:   r   r   rX   r   r1   r   r   SALDO_MINIMO_USDTr   rY   r   rj   rK   COMPRAR_IMEDIATAMENTEra  r  r  r  r   r   AGUARDAR_MELHOR_HORAr  r6   r   r   r'  rG  rE  r   r   r  r   r   r%  rL   rD   rQ  r   r-   r.   r   rj  r/   r_   )Fr   r\   r  r]   r   
saldo_usdtvalor_portfolior;   r>   r=   valor_portfolio_limiter   ganho_reinv	msg_ganhosaldo_usdt_minimo_protegidoZsaldo_usdt_floatusdt_residualZsaldo_para_comprar  r  r  r  r  Z
preco_alvord   rC  Zcondicao_compraZesta_no_melhor_horarioZmsg_horarioZpercentual_alvovalor_compravalor_ajustadovalor_compra_originalZsaldo_disponivel	eh_validomsg_validacaoZsaldo_livreeh_operacao_superdimensionadar   par_disponivelinfo_pardeve_aguardaraguardoutempo_esperado	msg_temposaldo_anteseh_valido_notionalr&  msg_notionalr  rD  deve_tentarvalor_ajustado_usdciclos_skipZquantidade_ajustadar  r  diferenca_saldoresidualvalor_compra_realdiferenca_aproxZdiferenca_pctr   rO  rP  r   r^  ri  r`  r  r
   r
   r   fase_construcao_portfolio  s|  

















,

"


(




r  c           h         s  t d t }d vs d std t | t|}dd |D }t|||}|td}t|t	r8t
|n|}t d|dd	t  t|||| \}}	}
}t d
|dd	t  t d|  t}tdkr|dkrt|t	rvt
|n|}td|| }|dkrt| }td|dd td|d t d|dd t||||} d }t d g }g }d v r d rg } d  D ]4}||v r||d}||d}t|| }|tk rtd| d|ddt d t d || q|D ]} d |= q|D ]}||d}||d}t|| d}t | d|dd|dd|dd  di |i }|rX|d |}||k rX|| }td!| d"| d# qt| |d$\}}}t| |d%\} }!}"||kr|dkr}t d&|  ||||||dd' td(| d) q||  kr|"dkrt d&|!  ||t||||dd' td(| d* q d+d,r d-d}#|td}t|t	rt
|n|}|#| }$|$tkrtd. td/|$d td0 |D ]}|d1krq||d}%||d}&|%t|& kr|&dkrt
|%|& }'|'tkrtt
|%|$|& d2t  }(|(dkr| d3t })t d4| d|(d5d6t
|(|& d t|}*t|)}+|+r|+dkrt|(|+d7|\},}-}.}/|,std8|.  |dkrt
|(|+ | d9 nd}0 d: d;d}1t ||0||1t\}2}3}4|2o|3o|3dk}5|5r|3|+ }6t d< t d=|(d5d>|6d5 t d?t
|(|+ dd@|3d |6}(ntt   qt d8|.  n	t!dA|  qt dB t"| \}7}8}9|7dCu rtd8|9  q|7rtd8|9  nt d8|9  t#|)d7|(}:|:r#t$%d2 t|};|;|*kr<tdD t$%d t|};|*|; }<|<dkrtdEt
|<d5d	|  t& ||\}=}>}?|=rp|>d%krp dF |= tdG| dH |5rzt' |d$| | di v r d | }@dI|@v rt(|@dI dkrdJ|@dI dK dL< tdM|@dN ddO  d |= tdP| dQ t$%d2 t| d3t }A|Ar|Adkrt
|<|A }Bt)|dR|At
|<|B tdS| dT|Ad5dUt
|<d5dV|Bd nt!dW| dX t|}|td}t|t	rt
|n|}|#| }$|$tkr nmt$%dY qt!dZ t!d[ |dkr:t
|(|+ | d9 nd}0 d: d;d}1t ||0||1t\}2}3}4|2rz|3rz|3dkrzt d\|(|+ dd@|3d t d]|4 d^ tt   qtd!| d_ tt   q|td}Ct*|d`da dbdcD ]}D|Ddd }|Dde }&|Ddf }||d}E|Edkr]|&dkr]|d9 | }F|F|& d2t  }(d,}G|Ftk r|Edkr|F}Ht}F|F|& }(db}Gtdg|Hddh|Fddi t+|F|d$\}I}J|IrV|(dkrV|(|EkrVtdj| dk t dl|&d5 t dm|(d5 t dn|Fdd	t   d+d,}K|KrVt do t"| \}7}8}9|7dCu rCtd8|9  q|7rNtd8|9  nt d8|9  nt dp t| d3t }+|+r|+dkrt|(|+d7|\},}-}.}/|,std8|.  |dkrt
|(|+ | d9 nd}0 d: d;d}1t ||0||1t\}2}3}4|2o|3o|3dk}5|5r|3|+ }6t d< t d=|(d5d>|6d5 t d?t
|(|+ dd@|3d |6}(|3}Fntt   qt d8|.  n	t!dA|  qt|}*| d3t })t#|)d7|(}:|:rt$%d2 | di v rK d | }@dI|@v rKt(|@dI dkrKdJ|@dI dK dL< tdM|@dN ddO  d |= tdP| dQ t|};|;|*kratdD t$%d t|};|*|; }<|<dkrtdEt
|<d5d	|  t& ||\}=}>}?|=r|>d%kr dF |= tdG| dH dqt, v r|5rt' |d$| t|Ct	rt
|Cn|C}C|C|F7 }C d+d,rdRndr}Lt
|<|+ }Bt)||L|+t
|<|B nt!dW| dX t!ds|*d5d	| dt|;d5d	|  t$%d2 qt!du t!d[ |dkr	t
|(|+ | d9 nd}0 d: d;d}1t ||0||1t\}2}3}4|2rG|3rG|3dkrGt d\|Fdd@|3d t d]|4 d^ tt   qtd!| d_ tt   q|Is]t|J qt|}|td}t|t	rst
|n|}t dv t dw|d t dxt(| d>dydz |D   t d{t(| d>d|dz |D   g }Md}N|D ](}O|Odd }|Ode }&|Odf }|&dkr|d9 | }P|M||&||Pd} |N|P7 }Nqt*|Md~da dbdc}Mt|t	rt
|n|}d}Q|N|krC|Ndkrtd|| |N nd}Qtd td|d td|d tdtd|| d td|Nd td|Qd9 dd |QdkrBtd|Qd9 dd n-t d t d|d t d|d t dtd|| d t d|Nd t d |MD ]}R|Rdd }|Rde }&|Rd }Sd,}5|S|Q }Pd,}G|Ptk r|P}Tt}Pdb}Gtdg|Tddh|Pdd |&dkr~|tkr~|P|& d2t  }Ut+|P|d%\}I}J|Ir/|Ptkr/||Pt kr/td| dk t dl|&d5 t dm|Ud5 t dn|Pdd	t d|Sdd | d3t })t-|)\}V}W|V	st!d|) d t!d|W  td| d qrt d|W   dd,}X|X	rXt d t.| \}7}8}9|7dCu 	rEtd8|9  qr|7	rPtd8|9  nt d8|9  nt d t|}*t| d3t }Y|Y	r|Ydk	r|dk	r||S| d9 nd}0t|U|Yd|\},}-}.}/|,	std8|.   d: d;d}1t ||0||1t\}2}3}4|2	o|3	o|3dk}5|5	r|3|Y }Zt d< t d=|Ud5d>|Zd5 t d?|Pdd@|3d |Z}U|3}Pntt   qrt d8|.  n	t!dA|  qrt#|)d|U}:|:
rt$%d2 t|};|;|*k
rtdD t$%d t|};|;|* }<|<dk
rtdt
|<d5d	|  t|t	
r7t
|n|}||P8 }t& ||\}=}>}?|=
r[|>d$k
r[ dF |= tdG| d t' |d%| | di v 
r d | }@dI|@v 
rt(|@dI dk
rdJ|@dI dK dL< tdM|@dN ddO  d |= tdP| dQ  dd,
rdnd}[t
|<|Y }\t)||[|Yt
|<|\ nt!d| d t!ds|*d5d	| dt|;d5d	|  t$%d2 qrt!d t!d[  d: d;d}1t ||0||1t\}2}3}4|2r |3r |3dkr t d\|Pdd@|3d t d]|4 d^ tt   qrtd!| d_ tt   qr|Is8t|J qr|tkrOtd8| d|ddtdd qr||Pt k rvtd8| d|Pt dd|dd td|Pddtd qrtd8| d qrt d t|}dd |D }t|||}]t|]||| \}^}_}?}?t||||^|D ]}|d}`||d}at | d|`ddt/|a d qt d|]dd	t  t d|^dd	t  t d|_dd	t   dd,r- fddz|D }b|brt|bnd}c|cdk r td td|cdd td d, d< nt d t d|cdd  d+d,r d-d}d|d1d}e|e|dkrftd td|dd td|ed td d, d+< d d-< n6|d|e }f|ddkru|e|d d9 nd}gt d t d|dd t d|ed t d|fddU|gdd t d |rtdd0t*|  td td d vri  d< |D ]7}| d v rԈ d | }d|v rԐqdt12 3 dt
||dt
||d ddŜ d |< qt4t || tt    S )u   
    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 ==========r   r  c                 S   r  r  r  r  r
   r
   r   r   
  r  z0fase_balanceamento_portfolio.<locals>.<dictcomp>r   r  r   r!   u9   ⚠️  Valor Total para CÁLCULOS (COM REINVESTIMENTO): u   📊 rz   r  r  r  u   🔒 PROTEÇÃO USDT: Mínimo $u    não será gastor  u    
--- Análise de Percentuais ---r(  r)  r*  r+  rh   r,  r   rP   	% (Alvo: u   %) - Diferença: +.2fr#  r-  r  z: Aguardando ciclos de retry (z ciclos ainda)Zvendar  z   )r;   re   Zpercentual_atualr=   u     → z acima do alvo (venda)z abaixo do alvo (compra)r  Fr  u$   
🔄 MODO RETIRADA DE CAPITAL ATIVOz   Faltam retirar: $z>   Vendendo moedas prioritariamente para completar retirada...rr   rE   r   z  Vendendo r#   z por $r  r   ra   rD  rs   u       💡 Teste Progressivo:r  r  r  r  r  u?     ⏰ Aguardando melhor hora de venda para maximizar retorno...Nr  u%   ✓ Venda verificada: Saldo diminuiu r  u     ✅ Histerese removida para u)    (venda bem-sucedida na direção oposta)r1  r5  r2  r3  r  r0  r  r  r  r   u   ✓ Venda registrada: r"   r   z) = $u   ✗ FALHA: Venda de u&    não confirmada! Saldo não diminuiu.g      ?u8     ✗ Ordem de venda falhou durante retirada de capital!r  r  r  r  r  c                 S      | d S Nre   r
   r   r
   r
   r   r         z.fase_balanceamento_portfolio.<locals>.<lambda>T)r   reverser;   r=   re   r  r  r  z
>>> Vendendo r  r  r  r  u>     ⏰ Buscando melhor momento de venda para retirar capital...u-     → Venda imediata (rebalanceamento normal)r  r   r  r  uA     ✗ Ordem de venda falhou após validação de notional passar!u!   
🔍 ESTADO ATUAL DO PORTFÓLIO:u      • USDT em Caixa: $u      • Moedas a Comprar: c                 S   r   r;   r
   r  r
   r
   r   r     r   z0fase_balanceamento_portfolio.<locals>.<listcomp>u      • Moedas a Vender: c                 S   r   r   r
   r  r
   r
   r   r     r   )r;   r=   re   valor_idealc                 S   r  r  r
   r   r
   r
   r   r     r  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: r~   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)r  u    (mínimo da Binance)r  z	 (Ideal: r    r  r  r  r  r  r  r  uE     ⏰ Buscando melhor momento de compra para investir novo capital...u.     → Compra imediata (rebalanceamento normal)r   r  u*    (compra bem-sucedida na direção oposta)r   r   r  r  uB     ✗ Ordem de compra falhou após validação de notional passar!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): c                    s,   g | ]}t |d  d |d  qS )r   r  )ri   rX   r  r   Zpercentuais_finaisr
   r   r     s   , rw   u&   
✅ INCREMENTO DE CAPITAL CONCLUÍDO!u"      Desvio máximo de percentuais: zC   Voltando ao rebalanceamento normal (sem aguardar melhor hora)...u*   
⏳ Incremento de capital em progresso...z% (alvo: < 2.0%)u$   
✅ RETIRADA DE CAPITAL CONCLUÍDA!z   Valor retirado: $u      USDT disponível: $z(   Voltando ao rebalanceamento normal...u(   
⏳ Retirada de capital em progresso...z   Retirada solicitada: $u      USDT já retirado: $z   Faltando retirar: $u   % concluído)uC      O robô continuará vendendo moedas para completar a retirada...u'   
⚠️  MOEDAS RESIDUAIS NESTE CICLO: r  uL       Estas moedas serão consolidadas quando forem realizadas futuras compraszN    Verificar moedas_residuais_historico para detalhes de tentativas e backoffr.  r/  $)r!  rB   Zvalor_aproximado)5r   r   r:   r   r   r   rX   r   rY   r   r1   r   r  r   rj   r   keysri   rB  r6   rK   rk   r"  r  r   r  r   r   r'  rG  rE  r   r   r   r  r  r   r   r  r%  rL   rD   r   r  localsr   r  r9   r(   r   r-   r/   r_   )hr   r\   r  Zmoedas_residuais_ciclor]   r   r  Zsaldo_usdt_atualr  r   r  r  r  Zsaldo_usdt_atual_floatr  Zpercentuais_atuaisr  Zmoedas_vendaZmoedas_compraZmoedas_residuais_removerZmoeda_residualZ	pct_atualpct_alvore   Z	moeda_remr;   rF  Zproxima_tentativar  Z
gate_vendaZmotivo_vendaZciclos_rest_vendaZgate_compraZmotivo_compraZciclos_rest_comprar  Zvalor_ainda_faltar>   r=   r   Zquantidade_vendar   r  r  r  r&  r  r  r  rD  r  r  r  r  Zquantidade_venda_ajustadar  r  r  r  r  r  r   r  rf  r  Zpreco_vendaZvalor_venda_realZsaldo_usdt_antesZ
item_vendard   Zvalor_excessor  Zvalor_excesso_originalr  r  Zdeve_aguardar_vendaZ
tipo_vendaZcompras_planejadasZtotal_usdt_necessarioZitem_comprar  Zfator_reducaor  r  r  Zquantidade_comprar  r  r  Zpreco_atual_compraZquantidade_compra_ajustadaZtipo_comprar  Zvalor_portfolio_finalZvalor_portfolio_limite_finalZganho_finalZ	pct_finalZsaldo_moedaZdesviosZdesvio_maximoZvalor_retirada_solicitadoZ
usdt_atualZusdt_faltandoZpercentual_concluidor
   r  r   fase_balanceamento_portfolio	  s  

&










&"
 








*
"



"
 






$"
""

 


 








$

$$$






&
r  c               
   C   sb  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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 ]F}||d}	||d}
|
rt|	|
 nd}||d}| d!i |d}t| d"t|	d#d$d%t|d&d$|d'd(|dd)  qtd* | W S  ty0 } 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   B  r  z'relatorio_portfolio.<locals>.<dictcomp>c                 S   r  r  )r   r   r  r
   r
   r   r   G  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  rs   r   r!   zValor Atual: r   zGanho/Perda: r  z	Ganho %: rE   ra   r#  zF----------------------------------------------------------------------r  z: Saldo=z>16z | zValor: z12.2fz6.2fr  rh   zG======================================================================
u   Erro ao exibir relatório: )rU   r   rY   dictr:   r  r   r   r   r   r   r-   r.   r/   r_   r   r   rX   r$  r   r1   r9   r7   r   r
  	print_exc)r   r\   r]   r   r  r   r  Zcapitalr;   r^   r=   r?   Zpctr  rC   r
   r
   r   relatorio_portfolio8  s\   

 r  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 }z>| d dkrdt| ||} n| d dkrpt| ||} || d< t	t
|  |tkrt }|r|} 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 principalr	  u/   ROBÔ DE BALANCEAMENTO DE PORTFÓLIO - INICIADOr
  r   Tu    ⏹️  SINAL DE PARADA RECEBIDOzSolicitado por: Z
pedido_porzTimestamp: rB   u   Encerrando robô gracefully...rE   r  r  r  Zcontador_ciclo_atualu   
Próximo ciclo em z segundos...zErro no ciclo rP   Nu!   
Robô interrompido pelo usuáriozErro fatal: )r   r  controlador_stopZ
deve_pararZobter_info_paradarX   Zlimpar_stopr  r  r   r   TEMPO_REFRESHr  TEMPO_CICLOr   r   r7   r   r
  r  KeyboardInterrupt)r   r\   r  Zcontador_relatorior[   Zestado_atualizadorC   r
   r
   r   main~  sd   

)r  __main__)NNNNrT  )N)r   )__doc__r   ZpandasZpdr   r4   Zlogging.handlersr   r   localer&   r
  r   r   pytzZcoloramar   r   r   Zdecimalr   r  ImportErrorZrequestsr$   r8   Zconfig_sqlite_apir	   ZCONFIG_API_DISPONIVELr   r   rD   rO   rU   r_   rn   	setlocaleLC_ALLErrorrE  rX   ZAPI_KEYZ
API_SECRETZAPI_KEY_TESTNETZAPI_SECRET_TESTNETZTESTNETr   r1   r   r   r  r  Z	TIMEFRAMEZVARIACAO_BALANCEAMENTOr  ZSALDO_MINIMOr  r  ZVARIACAO_HISTERESE_INCREMENTOr  r  r  rB  r  r   r  r  r  ZLOG_FILENAMEr   	getLoggerloggersetLevelINFOhandlershandlerremoveHandlerZrotating_handler	Formatter	formattersetFormatter
addHandlerr.   r   r   Z	chave_apiZchave_secretar   r   Zset_sandbox_moder[   r   Zload_marketsr   r   r7   rC   rS   r   r   r:   r6   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r"  r%  r'  rG  rQ  rV  ra  rj  r  r  r  r  r  r  r  r  r  r  r  r  r  __name__r
   r
   r
   r   <module>   s  3-,;











 
+.' Pe(!
 !# $
! # $
+ !` 
   
F      LF
;
