Created
November 13, 2023 01:34
-
-
Save schcriher/7855364aa1ac1b6675c96e69dfd56d0c to your computer and use it in GitHub Desktop.
peso_por_dolar.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: UTF-8 -*- | |
# Copyright (C) 2023 Schmidt Cristian Hernán | |
# Este script descarga el histórico de los valores del precio del dólar en la | |
# República Argentina y los gráfica, agregando además los cambios de presidentes | |
# que hubieron en el rango de los valores disponibles. | |
# | |
# Se almacenan además los datos en un archivo csv y se guarda la imagen en formato | |
# png y svg. Los tres archivos en la misma carpeta donde donde esté este archivo, | |
# cada uno con su respectiva extensión. | |
import os | |
import sys | |
import math | |
import json | |
import time | |
import urllib | |
import datetime | |
try: | |
import requests | |
import bs4 | |
import pandas | |
import matplotlib | |
matplotlib.use('GTK3Agg') | |
import matplotlib.pyplot as plt | |
import matplotlib.patches as mpatches | |
except ImportError: | |
print('Debe tener instalado: requests bs4 pandas matplotlib\n') | |
print('Ejecute: python3 -m pip install --user requests bs4 pandas matplotlib') | |
sys.exit(-1) | |
pandas.plotting.register_matplotlib_converters() | |
BASE = os.path.splitext(__file__)[0] | |
CSV = BASE + '.csv' | |
SVG = BASE + '.svg' | |
PNG = BASE + '.png' | |
TMP = BASE + '.tmp' | |
url = 'https://www.bcra.gob.ar/PublicacionesEstadisticas/Principales_variables_datos.asp' | |
now = datetime.datetime.now() | |
ini = '2010-06-01' # límite del servidor (url) | |
fin = now.strftime('%Y-%m-%d') | |
tmp_exists = os.path.exists(TMP) | |
if tmp_exists and time.time() - os.path.getmtime(TMP) < 3600: # cache de 1 hora | |
with open(TMP, mode='r') as fid: | |
head, _x, _y = json.load(fid) | |
else: | |
if tmp_exists: | |
os.remove(TMP) | |
data = {# Extraído del form de la pagina (url) | |
'fecha_desde': ini, | |
'fecha_hasta': fin, | |
'primeravez': '1', | |
'serie': '7927', | |
'serie1': '0', | |
'serie2': '0', | |
'serie3': '0', | |
'serie4': '0', | |
'detalle': 'Tipo de Cambio Minorista ($ por US$) Comunicación B 9791', | |
} | |
response = requests.post(url, data=data) | |
response.encoding = response.apparent_encoding | |
html = bs4.BeautifulSoup(response.text, 'html.parser') | |
table = html.find(name='table', attrs={'class': 'table-BCRA'}) | |
head, *rows = table.findAll(name='tr') | |
head = [c.string for c in head.findAll(name='th')] | |
_x = [] | |
_y = [] | |
for row in rows: | |
cell = row.findAll(name='td') | |
_x.append(cell[0].string.strip()) | |
_y.append(cell[1].string.replace(',', '.').strip()) | |
with open(TMP, mode='w') as fid: | |
json.dump((head, _x, _y), fid, indent=4) | |
_xx = [datetime.datetime.strptime(i, '%d/%m/%Y') for i in _x] | |
_yy = [float(i) for i in _y] | |
df = pandas.Series(data=_yy, index=_xx, name=r'Valor ARS/USD') | |
plt.plot(df, 'k-', linewidth=0.8) # Dólar oficial SIN impuestos | |
# https://www.argentina.gob.ar/normativa/buscar | |
# IMPUETO PAIS | |
# Ley 27541, art 39 → 30%, vigencia 2019-12-24 | |
# https://www.argentina.gob.ar/normativa/nacional/ley-27541-333564 | |
# https://servicios.infoleg.gob.ar/infolegInternet/verNorma.do?id=333564 | |
# ADELANTO DE GANANCIAS | |
# Resolución General 4815/2020, art 5 → 35%, vigencia 2020-09-16 | |
# https://www.argentina.gob.ar/normativa/nacional/resoluci%C3%B3n-4815-2020-342273 | |
# https://www.afip.gob.ar/regimen-devolucion-percepciones/percepcion/que-es.asp | |
# Resolución General 5393/2023, art 2 → 45%, vigencia 2023-07-26 | |
# https://www.argentina.gob.ar/normativa/nacional/resoluci%C3%B3n-5393-2023-387216 | |
# Resolución General 5430/2023, art 1 → 45% y 25% (70%), vigencia 2023-10-10 | |
# http://biblioteca.afip.gob.ar/dcp/REAG01005430_2023_10_09 | |
df_base = df.copy() | |
mod1 = ('2019-12-26', 'Ley 27541\nart 39') # "26" siguiente día hábil | |
mod2 = ('2020-09-16', 'RG 4815\nart 5') | |
mod3 = ('2023-07-26', 'RG 5393\nart 2') | |
mod4 = ('2023-10-10', 'RG 5430\nart 1') | |
df[mod1[0]:] = df_base[mod1[0]:] * 1.30 # Impuesto PAIS | |
df[mod2[0]:] = df_base[mod2[0]:] * 1.65 # Impuesto PAIS + adelanto de ganancias | |
df[mod3[0]:] = df_base[mod3[0]:] * 1.75 # Impuesto PAIS + adelanto de ganancias | |
df[mod4[0]:] = df_base[mod4[0]:] * 2.00 # Impuesto PAIS + adelanto de ganancias | |
plt.plot(df, 'k-', linewidth=1.5) # Dólar oficial CON impuestos (para compra de billetes) | |
events = ( | |
None, | |
'2011', | |
('2011-08-14', 'Primarias', 'dotted'), | |
('2011-10-23', 'Generales', 'dashed'), | |
('2011-12-10', 'Cristina Fernández', 'solid'), | |
None, | |
'2015', | |
('2015-08-09', 'Primarias', 'dotted'), | |
('2015-10-25', 'Generales', 'dashed'), | |
('2015-11-22', 'Balotaje', 'dashdot'), | |
('2015-12-10', 'Mauricio Macri', 'solid'), | |
None, | |
'2019', | |
('2019-08-11', 'Primarias', 'dotted'), | |
('2019-10-27', 'Generales', 'dashed'), | |
('2019-12-10', 'Alberto Fernández', 'solid'), | |
None, | |
('2022-08-03', 'Asume Sergio Massa como Ministro de Economía', 'solid', 'blue'), | |
None, | |
'2023', | |
('2023-08-13', 'Primarias', 'dotted'), | |
('2023-10-22', 'Generales', 'dashed'), | |
('2023-11-19', 'Balotaje', 'dashdot'), | |
('2023-12-10', '?', 'solid'), | |
) | |
xmin = df.keys()[0] | |
xmax = df.keys()[-1] | |
xspan = xmax - xmin | |
ymean = df.mean() | |
ymin = df.min() | |
ymax = df.max() | |
yspan = ymax - ymin | |
handles = [] | |
for data in events: | |
if data is None: | |
handles.append(mpatches.Patch(alpha=0)) | |
elif isinstance(data, str): | |
handles.append(mpatches.Patch(alpha=0, label=data)) | |
else: | |
date, text, linestyle, *color = data | |
color = color[0] if color else 'green' | |
date = datetime.datetime.strptime(date, '%Y-%m-%d') | |
handles.extend(plt.plot([date, date], [ymin, ymax], | |
alpha=0.5, linewidth=1, color=color, | |
linestyle=linestyle, label=text)) | |
def including_delta(plt, tini, tfin, df): | |
dat = df[tini:tfin] | |
x = dat.index.mean().date() | |
y1 = float(dat[0]) | |
y2 = float(dat[-1]) | |
d = y2 - y1 | |
p = y2 / y1 * 100 | |
yoffset = yspan / 33 | |
if d > ymean: | |
va = 'center' | |
ym = y1 + d / 2 | |
elif y1 > ymean and y2 > ymean: | |
va = 'top' | |
ym = y1 - yoffset | |
else: | |
va = 'bottom' | |
ym = y2 + yoffset | |
plt.annotate('', xy=(x, y1), xytext=(x, y2), | |
arrowprops=dict(arrowstyle='|-|', connectionstyle='arc3', color='red')) | |
code = '$\$\,{:.2f}$\n$~{:.0f}\,\%$' | |
params = {'color': 'red', 'fontsize': 10, 'ha': 'center', 'va': va, | |
'bbox': {'boxstyle': 'round', 'fc': 'white', 'ec': 'red'}} | |
plt.text(x, ym, code.format(d, p), **params) | |
including_delta(plt, '2011-12-10', '2015-12-10', df) | |
including_delta(plt, '2015-12-10', '2019-12-10', df) | |
including_delta(plt, '2019-12-10', fin, df) | |
#for fecha, texto in (mod1, mod2, mod3, mod4): | |
# x = datetime.datetime.strptime(fecha, '%Y-%m-%d') | |
# y1 = df[:x][-2] | |
# y2 = df[:x][-1] | |
# ym = (y2 + y1) / 2 | |
# plt.annotate(texto, xy=(x, ym), xytext=(-3, 3), | |
# fontsize='x-small', ha='center', va='center', | |
# textcoords='offset fontsize', arrowprops={'arrowstyle': '->', 'color': 'indigo'}, | |
# bbox={'boxstyle': 'round', 'fc': 'white', 'lw': 1, 'ec': 'indigo'}) | |
point = '2020-12-21' | |
x = datetime.datetime.strptime(point, '%Y-%m-%d') | |
y = df_base[point] | |
plt.annotate('Sin impuestos', xy=(x, y), xytext=(0, -3), | |
fontsize='x-small', ha='center', va='center', | |
textcoords='offset fontsize', arrowprops={'arrowstyle': '->'}, | |
bbox={'boxstyle': 'round', 'fc': 'white', 'lw': 0}) | |
plt.xlabel(head[0]) | |
plt.ylabel(head[1]) | |
ini = ini.replace('-', '/') | |
fin = fin.replace('-', '/') | |
plt.title('Valor del dólar oficial (mas impuestos) • {} ─ {} • {}'.format( | |
ini, fin, urllib.parse.urlsplit(url).netloc)) | |
plt.legend(handles=handles, loc='upper left', fontsize='x-small', frameon=False) | |
plt.text(0.01, 0.01, 'Schcriher', | |
fontsize='x-small', ha='left', va='bottom', | |
transform=plt.gca().transAxes, | |
bbox={'boxstyle': 'round', 'fc': 'white', 'lw': 0}) | |
plt.text(0.99, 0.01, 'Datos obtenidos el ' + now.strftime('%Y-%m-%d %H:%M'), | |
fontsize='x-small', ha='right', va='bottom', | |
transform=plt.gca().transAxes, | |
bbox={'boxstyle': 'round', 'fc': 'white', 'lw': 0}) | |
plt.tight_layout() | |
fig = plt.gcf() | |
fig.set_size_inches(9, 6, forward=True) | |
fig.set_dpi(90) | |
df.to_csv(CSV, sep=',', encoding='utf-8', index_label=head[0], header=True) | |
plt.savefig(PNG, format='png') | |
plt.savefig(SVG, format='svg') | |
plt.show() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment