From f7c84a1de9ada07f016e3fc76b20655958a64267 Mon Sep 17 00:00:00 2001 From: luimos10x <77957023+luimos10@users.noreply.github.com> Date: Sun, 16 Jul 2023 06:41:53 -0400 Subject: [PATCH 1/2] cambio el archivo script con print de dias [ Please enter the commit message for your changes. Lines starting --- config.py | 2 +- script.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config.py b/config.py index 0de3a18..5da93b0 100644 --- a/config.py +++ b/config.py @@ -1,4 +1,4 @@ API_KEY = '' API_SECRET = '' -dias = 7 \ No newline at end of file +dias = 15 \ No newline at end of file diff --git a/script.py b/script.py index 253f49a..8e6241c 100644 --- a/script.py +++ b/script.py @@ -3,6 +3,7 @@ ticks = buscarTicks() ticksNumber = len(ticks) print("NUMERO DE TICKS: " + str(ticksNumber)) +print("NUMERO DE DIAS A ESTIMAR: " + str(dias)) for tick in ticks: analizarMoneda(tick) From 1d351310b9ce2e7be07f4d98dfd8493519b41551 Mon Sep 17 00:00:00 2001 From: luimos Date: Fri, 13 Mar 2026 20:02:53 -0300 Subject: [PATCH 2/2] modificacion de funciones para agregar el cliente de Binance y configurar el timeout de las solicitudes. --- __pycache__/config.cpython-311.pyc | Bin 0 -> 221 bytes __pycache__/config.cpython-314.pyc | Bin 0 -> 499 bytes __pycache__/functions.cpython-311.pyc | Bin 0 -> 3832 bytes __pycache__/functions.cpython-314.pyc | Bin 0 -> 5932 bytes __pycache__/script.cpython-314.pyc | Bin 0 -> 825 bytes config.py | 13 ++- functions.py | 147 +++++++++++++++++--------- script.py | 26 +++-- 8 files changed, 125 insertions(+), 61 deletions(-) create mode 100644 __pycache__/config.cpython-311.pyc create mode 100644 __pycache__/config.cpython-314.pyc create mode 100644 __pycache__/functions.cpython-311.pyc create mode 100644 __pycache__/functions.cpython-314.pyc create mode 100644 __pycache__/script.cpython-314.pyc diff --git a/__pycache__/config.cpython-311.pyc b/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d742b9572c2b27c268607e5453423910d7756d6 GIT binary patch literal 221 zcmZ3^%ge<81TOUpQ?-EfV-N=h7@>^MLO{lJh7^V<5N1qajABY*3TDt`zQyoT4k+aZ zWU)I2c*c9XM&9BAF@jy4gIq&yv7}@s7O!OZ3^M1Jjk8ruXmM&$aZFBWW^R5=Kv8~r zQDSaxW?p(sNl{`7kPc2R$}A{}flxlqE-}gZd1;yHdIgogIBatBQ%ZAE?TSF|1-YzP g0!VydW@Kc%!Nt?SeuJ04f$IS~X9Eie7I6S20SGrbZ~y=R literal 0 HcmV?d00001 diff --git a/__pycache__/config.cpython-314.pyc b/__pycache__/config.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09ce9eaee6e404d2afa14857f416860f498c043f GIT binary patch literal 499 zcmZ{eF-yZh6vr=Z+St^XYKv}yh(i^`!AYc|F-TBKi8>TwLTt_$ZF}|ZqIL_8T?9XY zqwanW3ohYY1SdB^+`UkXU3~D~``x|&{qHSJC^F#jefJbR^ZuS3h9UmK7v>NF8<>It zC^!PO)KK!*=YW|q)2qJ|n)+8Ij;PZ1EEVl6&G5{c87kQ_9W%2uXA1M6EPW*QRitM_M?9?TbIC6eEyTqMSF bo{uKfC?6N6`=Ag{l_E7Rlx`F}$=!Yc^Xi9; literal 0 HcmV?d00001 diff --git a/__pycache__/functions.cpython-311.pyc b/__pycache__/functions.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1d50cabe6b38c427d86d032cd5b9f6ff9520b51 GIT binary patch literal 3832 zcmdT`U2Gf25#HnRj>lh_vPAhOk!k%nq8l4^|6Y*_LF>^p6IO%O7 zmA1uglqsX*w195eYi4n~4GA`o)a()PY;BaMpV%ML2t9_s+2W2F6z6#6K8ibe7O0DN z0Cn@!B^2T|iPC6@(%FcZ5;BTTC#5vJ(u&9@w>=mgnSuCx;jxiuXHX6~;ioj{tWgaw z7W)vm$JSep%B*)I#mWYu9)y3Hvnf!WTFcrVf~UupHwr1v+8BcB@bp87;W7NpmZKGS zjYo~Ous6xA8JZ(%_Qf+x;7ak={q2l~jPop^IeV5~Yly7D#=bKH&@^^JUB~2_V}VQ? zGqHHyvD4oK?2Vl5C$MXLDMEH#MU`%aF@GCa4}nvi$$=mT5ByKBza zBbX|7WYL<(Qfy$FJ?xC3_}wgmJ>(vZZ8=0~=BRR4(W%6izn4GKn?BMz5$rt^?i~sD zUfSY<(ed%I@o;d9a&3}z(^>gm`lcjq{`2L_m%9D(rNLOptFuXS54KM)C<|DSr%a_O zC6$~LuI?i zA!xL%A4bBHmt_IVlj1@uElpm)(hN?d)2YnNq=FMX^p}!2HLpyX-TBDi_A7+yjous>}Of_vcZ?nwYi+F8=XC0c7~sIhSkn~t+T%{ zR&7Pz)^EP{qg{Keh;{C(lMRo5<>2y};+evkjljOriM6B}IIIN@7lz<kn<8 zcd93bwG+c?;H(xn`WJA<>ZVSm?;cZJi9*{`EaE4hU)3k zJY8i^*S2JNtT4Uuo z&@WsOH~s04_C#3v3zh*sX~70)kaaRZ>rVz~{cnJkZ34Ah7R)Hb+je_mYX;eESmSEr z7OYuE7@?g&0*BpO*XL>8-V8+mYc2KhTb6I0X_8w2)C?ufzD&SUNY%F6&S(r+b6Bv} z5O>W2GI$lNy%IQ@V}VQ?GqJMfxt);mUtsNT%Igi0P|cRSa|z%z$0!FHb$C|;+_~#~ zvu%!Tw#qVfP5wVyIh2D9b2+jsi2x{6j`u9V#~HGNz0>fS;*_qs#ae5fuX9=Okac9a z>3^#1@9;#7`7HS(xxvv9S2|CjBDpS2%e@A-2;8+iA5a;DsVSZ$xx+Gh*z{GWmB*ZWXQ z>&l_!x#C>u;Bq=Yw9(SGa-}q|I#Ig4I;pmFX)Rs(Gp2B8b!zRR*8Yat(xPnCP3`#pdQ$B;@${JLKc)Fk<;B)|x`L=yv0&`v`CrBn@W> zWf5F%W^qxZ8DKMUp23wR@5={e!v z5*r(ioC^mH@K|DkW9J9MxDUpc*x+dNDhv>OS@H%ijEzU4qp^XrQILf}wng=Z>~PVO zup;pI7)VU$ATSZK=?)3=LMDNA3dZx+ zef^rRKR;CQwp9_u+@MTapf)&fVRq$q$-k;Br%OYsulv`LN7SQ(Pwbxz|7uwE9eFgX z`hM^``RTEz7eD{rZ|{9}PxZb1#{;VG!t;x<^5rY#tJk#46Y9mdb}_E{;^lX*t3J4c zOljOy*?6jSeL8#^LJN7|@+xJfQ!~2nW-61&B!#2qWfqe)GLiBKaJpmQ!tm6&=!EVf z%%x~#JQ_1_*sM?w(I!A;q>fJ#j!a-68mwtr;ul2WG#&;SS!}ro1j3=H3i9TSUj-d3 zH$N5R&Ktk49Nzms&f6=_mO@~KUG=T8TJYHVLfLsrb)M3kr}FGqZf~BevTc;ViY)Tx TZmPeEEOL~lNUdK@jcES`Yk@)& literal 0 HcmV?d00001 diff --git a/__pycache__/functions.cpython-314.pyc b/__pycache__/functions.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..685b8fa208233960473e56f5f8d2f6e0b4ed5f2a GIT binary patch literal 5932 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 literal 0 HcmV?d00001 diff --git a/__pycache__/script.cpython-314.pyc b/__pycache__/script.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62a63244038edb0c8bbfd974e2fc907a3ca3d53f GIT binary patch literal 825 zcmah{O=}ZT6n&HVNG6j>I;2_*kxp6_f=NZls)!KUK%(Y@NeXo_m1#1?(PSpRd83$B zmO>YH;i5%^ikrCihe(sOMnyA-!m$R4CV~a--VFKU$YDWv3S~gN%-j`8~&MBEvFmNE0`ZaTDt%_v7P8SBHB~ znwDcC9b(QfjizB?7J0_#wr*m%PXJdmXFgwDtRNiFDk5!hT26ZRpPfXdgPZ%$`oa{jU%6{X8^^*hnHh7L5y(zlYUN{nu=wfmm|GixJ5w=v zxHS}4`{4-?-aHtArKzxRuy`h|oJcD}>BjNPv6LOW7z%g$A#eNYQT%ZG{q-?-YY-=* LryvWj5MzG;hgP&1 literal 0 HcmV?d00001 diff --git a/config.py b/config.py index 5da93b0..e1ab7f3 100644 --- a/config.py +++ b/config.py @@ -1,4 +1,11 @@ -API_KEY = '' -API_SECRET = '' +import os -dias = 15 \ No newline at end of file +API_KEY = os.getenv("BINANCE_API_KEY", "") +API_SECRET = os.getenv("BINANCE_API_SECRET", "") + +dias = 30 +workers = 6 +max_retries = 6 +retry_base_delay = 1.0 +request_timeout = 10 +show_progress = True diff --git a/functions.py b/functions.py index dde3439..199c7ed 100644 --- a/functions.py +++ b/functions.py @@ -1,59 +1,106 @@ +import random +import time +from concurrent.futures import ThreadPoolExecutor, as_completed + import config from binance.client import Client -import time -dias = config.dias -client = Client(config.API_KEY, config.API_SECRET, tld='com') +# Configuración +dias = int(getattr(config, 'dias', 30)) +workers = max(1, int(getattr(config, 'workers', 6))) +max_retries = max(1, int(getattr(config, 'max_retries', 6))) +retry_base_delay = float(getattr(config, 'retry_base_delay', 1.0)) +request_timeout = int(getattr(config, 'request_timeout', 10)) +show_progress = bool(getattr(config, 'show_progress', True)) -listTiks = [] +client = Client( + getattr(config, 'API_KEY', ''), + getattr(config, 'API_SECRET', ''), + tld='com', + requests_params={'timeout': request_timeout}, +) -def buscarTicks(): - ticks = [] +def _log_error(error): + with open('log.txt', 'a', encoding='utf-8') as archivo: + mensaje = time.strftime('%d-%m-%Y %H:%M:%S', time.localtime()) + archivo.write(f"{mensaje} ERROR: {error}\n") - while True: - try: - list_of_tickers = client.futures_symbol_ticker() - except Exception as e: - print(e) - archivo = open("log.txt", "a") - mensaje = time.strftime('%d-%m-%Y %H:%M:%S', time.localtime()) + ' ERROR: ' + str(e) + "\n" - archivo.write(mensaje) - archivo.close() - time.sleep(2) - else: - break - - for tick in list_of_tickers: - if tick['symbol'][-4:] != 'USDT': - continue - ticks.append(tick['symbol']) - return ticks - -def analizarMoneda(tick): - while True: + +def _with_retry(fn, *args, **kwargs): + for attempt in range(1, max_retries + 1): try: - klines = client.futures_klines(symbol=tick, interval=client.KLINE_INTERVAL_1DAY, limit=dias) - except Exception as e: - print(e) - archivo = open("log.txt", "a") - mensaje = time.strftime('%d-%m-%Y %H:%M:%S', time.localtime()) + ' ERROR: ' + str(e) + "\n" - archivo.write(mensaje) - archivo.close() - time.sleep(2) - else: - break - - nKlines = len(klines)-1 - oldClose = float(klines[0][4]) - newClose = float(klines[nKlines][4]) - porcentaje = round((newClose - oldClose)/oldClose*100, 2) - - listTiks.append((tick, oldClose, newClose, porcentaje)) - #print(listTiks) - #print("TICK: "+tick+" OLD:"+str(oldClose)+" NEW: "+str(newClose)+" PORCENTAJE: "+str(porcentaje)+"%") - -def showResults(): - ordenar = sorted(listTiks, key=lambda result: result[3]) + return fn(*args, **kwargs) + except Exception as error: + print(f"ERROR attempt {attempt}/{max_retries}: {error}") + _log_error(error) + if attempt == max_retries: + raise + backoff = min(retry_base_delay * (2 ** (attempt - 1)), 30.0) + time.sleep(backoff + random.uniform(0.0, 0.3)) + + +def buscarTicks(): + list_of_tickers = _with_retry(client.futures_symbol_ticker) + return [tick['symbol'] for tick in list_of_tickers if tick['symbol'].endswith('USDT')] + + +def _analizar_moneda(tick): + klines = _with_retry( + client.futures_klines, + symbol=tick, + interval=client.KLINE_INTERVAL_1DAY, + limit=dias, + ) + + if not klines or len(klines) < 2: + return None + + old_close = float(klines[0][4]) + new_close = float(klines[-1][4]) + if old_close == 0: + return None + + porcentaje = round((new_close - old_close) / old_close * 100, 2) + return (tick, old_close, new_close, porcentaje) + + +def analizarMonedas(ticks): + resultados = [] + + if not ticks: + return resultados + + if workers == 1: + for index, tick in enumerate(ticks, start=1): + try: + resultado = _analizar_moneda(tick) + if resultado is not None: + resultados.append(resultado) + except Exception as error: + _log_error(error) + if show_progress and (index == len(ticks) or index % 25 == 0): + print(f"ANALIZADAS: {index}/{len(ticks)}") + return resultados + + with ThreadPoolExecutor(max_workers=workers) as executor: + future_to_tick = {executor.submit( + _analizar_moneda, tick): tick for tick in ticks} + for index, future in enumerate(as_completed(future_to_tick), start=1): + try: + resultado = future.result() + if resultado is not None: + resultados.append(resultado) + except Exception as error: + _log_error(error) + if show_progress and (index == len(ticks) or index % 25 == 0): + print(f"ANALIZADAS: {index}/{len(ticks)}") + + return resultados + + +def showResults(resultados=None): + datos = resultados or [] + ordenar = sorted(datos, key=lambda result: result[3]) for r in ordenar: - print("TICK: "+r[0]+" OLD:"+str(r[1])+" NEW: "+str(r[2])+" PORCENTAJE: "+str(r[3])+"%") + print(f"TICK: {r[0]} OLD:{r[1]} NEW:{r[2]} PORCENTAJE: {r[3]}%") diff --git a/script.py b/script.py index 8e6241c..2cc11e6 100644 --- a/script.py +++ b/script.py @@ -1,10 +1,20 @@ -from functions import * +import time -ticks = buscarTicks() -ticksNumber = len(ticks) -print("NUMERO DE TICKS: " + str(ticksNumber)) -print("NUMERO DE DIAS A ESTIMAR: " + str(dias)) +from functions import analizarMonedas, buscarTicks, dias, showResults -for tick in ticks: - analizarMoneda(tick) -showResults() + +def main(): + inicio = time.perf_counter() + ticks = buscarTicks() + print(f"NUMERO DE TICKS: {len(ticks)}") + print(f"NUMERO DE DIAS A ESTIMAR: {dias}") + + resultados = analizarMonedas(ticks) + showResults(resultados) + + duracion = round(time.perf_counter() - inicio, 2) + print(f"TIEMPO TOTAL: {duracion}s") + + +if __name__ == '__main__': + main()