├── .gitignore ├── README.md ├── Untitled.ipynb ├── capturas └── ejemplo.png └── funciones.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | 2019-12 2 | 2020-02 3 | Bonos.ipynb 4 | cookies.jar 5 | .ipynb_checkpoints 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rodi_python 2 | 3 | Es un conjunto de funciones que permiten implementar una *Planilla Ford T* usando [https://jupyter.org](JupyterLab). 4 | También contiene funciones para obtener datos de *Rodi*. 5 | 6 | Sobre la *Planilla Ford T*: es la denominación que le puso Francisco Mancuso a su planilla básica (básica en comparación con la de @gcutte) para operar opciones [https://www.youtube.com/watch?v=oArjAhDeRaw](https://www.youtube.com/watch?v=oArjAhDeRaw). 7 | 8 | ## Modo de uso 9 | 10 | Aquellos entendidos en Python pueden usar las funciones diréctamente desde línea de comandos, 11 | o bien usarlas desde JupyterLab. 12 | 13 | ## Desde JupyterLab 14 | 15 | 1. Instalar JupyterLab. 16 | 2. Clonar este repositorio, por ej. en /home/leonardo/rodi_python o c:\rodi_python. 17 | 3. Posicionarse en el directorio anteriormente clonado. 18 | 4. ejecutar JupyterLab, en Linux `jupyterlab`. Se abrirá el navegador web apuntando a `http://localhost:8888/lab`. 19 | 20 | ### Acceso a datos en tiempo real 21 | 22 | Crear el archivo `bonos.ipynb` con el siguiente contenido: 23 | 24 | ``` 25 | %run 'funciones.ipynb' 26 | import time 27 | from IPython.display import clear_output, display 28 | login_rodi("usuario_rodi", "password_rodi") 29 | while True: 30 | d=datapack() 31 | # Vencimiento 1: CI, 3: 48hs 32 | af20_ci=getRealtimeData(d['d']['aTabla'], "AF20", 1)["PrecioUltimo"] 33 | af20_48=getRealtimeData(d['d']['aTabla'], "AF20", 3)["PrecioUltimo"] 34 | ay24_ci=getRealtimeData(d['d']['aTabla'], "AY24", 1)["PrecioUltimo"] 35 | ay24_48=getRealtimeData(d['d']['aTabla'], "AY24", 3)["PrecioUltimo"] 36 | #ao20_ci=getRealtimeData(d['d']['aTabla'], "AO20", 1)["PrecioUltimo"] 37 | ao20_48=getRealtimeData(d['d']['aTabla'], "AO20", 3)["PrecioUltimo"] 38 | clear_output(wait=True) 39 | print("AF20: " + str(af20_48)) 40 | print("AY24: " + str(ay24_48)) 41 | print("AO20: " + str(ao20_48)) 42 | print("AY24/AO20: " + str(ay24_48/ao20_48)) 43 | print(8250*ay24_48/100/ao20_48*100) 44 | # debajo de 0.90 me conviene vender el AO20 y comprar AY24 45 | # arriba de 0.90 me conviene vender el AY24 y comprar AO20 46 | time.sleep(5) 47 | ``` 48 | 49 | ### Planilla Ford T 50 | 51 | En forma similar a la planilla *Ford T* de Francisco Mancuso desarrollé esta librería, que permite cargar 52 | los datos de los trades en formato *JSON*. 53 | 54 | 55 | En el mismo directorio que el archivo `funciones.json` crear el siguiente archivo dentro de *JupyterLab*: 56 | 57 | ``` 58 | %run 'funciones.ipynb' 59 | 60 | current=120 61 | trades=[ 62 | {"date": "2019-12-19","type": "STOCK", "price": 108, "qty": 2400}, 63 | {"date": "2019-12-19","type": "CALL", "base": 102, "price": 19.00, "qty": -24}, 64 | {"date": "2019-12-19","type": "CALL", "base": 120, "price": 9.50, "qty": -100}, 65 | # ideas 66 | ] 67 | 68 | getResults(trades) 69 | ``` 70 | 71 | Debería quedar así: 72 | 73 | ![ejemplo](capturas/ejemplo.png) 74 | -------------------------------------------------------------------------------- /Untitled.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 5, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "Acciones: 2400\n" 13 | ] 14 | }, 15 | { 16 | "data": { 17 | "text/html": [ 18 | "\n", 48 | " \n", 49 | " \n", 50 | " \n", 51 | " \n", 52 | " \n", 53 | " \n", 54 | " \n", 55 | " \n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | "
Precio Valor Calls: -24 Puts: 0
090.0032400.0000
195.0044400.0000
2100.0056400.0000
3102.0061200.00-240
4105.0061200.0000
5108.0061200.0000
6110.0061200.0000
7115.0061200.0000
8120.0061200.00-1000
9125.0011200.0000
10130.00-38800.0000
11135.00-88800.001000
12140.00-88800.0000
13145.00-88800.0000
" 147 | ], 148 | "text/plain": [ 149 | "" 150 | ] 151 | }, 152 | "execution_count": 5, 153 | "metadata": {}, 154 | "output_type": "execute_result" 155 | } 156 | ], 157 | "source": [ 158 | "%run 'funciones.ipynb'\n", 159 | "\n", 160 | "current=120\n", 161 | "trades=[\n", 162 | " {\"date\": \"2019-12-19\",\"type\": \"STOCK\", \"price\": 108, \"qty\": 2400}, \n", 163 | " {\"date\": \"2019-12-19\",\"type\": \"CALL\", \"base\": 102, \"price\": 19.00, \"qty\": -24}, \n", 164 | " {\"date\": \"2019-12-19\",\"type\": \"CALL\", \"base\": 120, \"price\": 9.50, \"qty\": -100}, \n", 165 | " {\"date\": \"2019-12-19\",\"type\": \"CALL\", \"base\": 135, \"price\": 6.50, \"qty\": 100}, \n", 166 | " # ideas\n", 167 | "]\n", 168 | "\n", 169 | "getResults(trades)" 170 | ] 171 | } 172 | ], 173 | "metadata": { 174 | "kernelspec": { 175 | "display_name": "Python 3", 176 | "language": "python", 177 | "name": "python3" 178 | }, 179 | "language_info": { 180 | "codemirror_mode": { 181 | "name": "ipython", 182 | "version": 2 183 | }, 184 | "file_extension": ".py", 185 | "mimetype": "text/x-python", 186 | "name": "python", 187 | "nbconvert_exporter": "python", 188 | "pygments_lexer": "ipython2", 189 | "version": "2.7.12" 190 | } 191 | }, 192 | "nbformat": 4, 193 | "nbformat_minor": 2 194 | } 195 | -------------------------------------------------------------------------------- /capturas/ejemplo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leonardorame/rodi_python/ce50a7b907a87e343d6e2c5b4691680100c1fa8e/capturas/ejemplo.png -------------------------------------------------------------------------------- /funciones.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 5, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy\n", 10 | "import pandas as pd\n", 11 | "import pandas.io.formats.style\n", 12 | "from decimal import Decimal\n", 13 | "from ipywidgets import HBox\n", 14 | "\n", 15 | "def color_negative_red(value):\n", 16 | " if float(value) < 0:\n", 17 | " color = 'red'\n", 18 | " elif float(value) > 0:\n", 19 | " color = 'green'\n", 20 | " else:\n", 21 | " color = 'black'\n", 22 | "\n", 23 | " return 'color: %s' % color\n", 24 | "\n", 25 | "def getResults(trades):\n", 26 | " rango = numpy.arange(current - (current / 4), current + (current / 4), 5)\n", 27 | " rango = list(rango)\n", 28 | " for trade in trades:\n", 29 | " if trade.has_key(\"base\"):\n", 30 | " rango.append(trade[\"base\"])\n", 31 | " if trade[\"type\"] == \"STOCK\":\n", 32 | " rango.append(trade[\"price\"]) \n", 33 | " rango = list(set(rango))\n", 34 | " rango.sort()\n", 35 | " tabla = []\n", 36 | " total_calls = 0\n", 37 | " total_puts = 0\n", 38 | " total_stocks = 0\n", 39 | " for i in rango:\n", 40 | " res = 0\n", 41 | " registro = {\"indice\": i, \"valor\": 0.0, \"calls\": 0, \"puts\": 0}\n", 42 | " for trade in trades:\n", 43 | " # acciones\n", 44 | " if trade[\"type\"] == \"STOCK\":\n", 45 | " dif = (i - trade[\"price\"])\n", 46 | "\n", 47 | " if(trade[\"price\"] == i):\n", 48 | " total_stocks = total_stocks + trade[\"qty\"]\n", 49 | " res = res + (trade[\"qty\"]*dif) \n", 50 | " registro[\"valor\"] = round(res, 2)\n", 51 | " # opciones\n", 52 | " if trade.has_key(\"base\"):\n", 53 | " dif = (i - trade[\"base\"])\n", 54 | " if trade[\"type\"] == \"CALL\": \n", 55 | " if(trade[\"base\"] == i):\n", 56 | " registro[\"calls\"] = registro[\"calls\"] + trade[\"qty\"]\n", 57 | " total_calls = total_calls + trade[\"qty\"]\n", 58 | " if trade[\"base\"] > i:\n", 59 | " res = res + (trade[\"price\"]*trade[\"qty\"]*100*-1)\n", 60 | " else:\n", 61 | " res = res + ((dif-trade[\"price\"])*trade[\"qty\"]*100)\n", 62 | " if trade[\"type\"] == \"PUT\":\n", 63 | " if(trade[\"base\"] == i):\n", 64 | " registro[\"puts\"] = registro[\"puts\"] + trade[\"qty\"]\n", 65 | " total_puts = total_puts + trade[\"qty\"]\n", 66 | " if trade[\"base\"] > i:\n", 67 | " res = res - ((dif + trade[\"price\"])*trade[\"qty\"]*100)\n", 68 | " else:\n", 69 | " res = res - (trade[\"price\"]*trade[\"qty\"]*100)\n", 70 | " registro[\"valor\"] = round(res, 2)\n", 71 | " tabla.append([format(registro[\"indice\"], '0.2f'), format(registro[\"valor\"], '0.2f'), registro[\"calls\"], registro[\"puts\"]])\n", 72 | " \n", 73 | " print( \"Acciones: {}\".format(total_stocks))\n", 74 | " calls = \"Calls: {}\".format(total_calls)\n", 75 | " puts = \"Puts: {}\".format(total_puts)\n", 76 | " df = pd.DataFrame(tabla, columns=[\"Precio\", \"Valor\", calls, puts])\n", 77 | " df = df.style.applymap(color_negative_red, subset=[\"Valor\"])\n", 78 | " return df\n", 79 | "\n", 80 | "def login_rodi(username, password):\n", 81 | " import requests\n", 82 | " import pickle\n", 83 | " import codecs\n", 84 | " from bs4 import BeautifulSoup\n", 85 | "\n", 86 | " # with open('cookies.jar', 'rb') as f:\n", 87 | " # cookies = pickle.load(f)\n", 88 | " # print(cookies)\n", 89 | "\n", 90 | " URL=\"https://rodi.sba.com.ar/Vistas/Login.aspx\"\n", 91 | " URLGetEspecies=\"https://rodi.sba.com.ar/Vistas/PanelPersonal.aspx/GetEspecies\"\n", 92 | " URLDataPack=\"https://rodi.sba.com.ar/Vistas/PanelPersonal.aspx/GetDataPack\"\n", 93 | " headers={\n", 94 | " \"User-Agent\":\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36\",\n", 95 | " \"Referer\": \"https://rodi.sba.com.ar/Vistas/PanelPersonal.aspx\",\n", 96 | " \"X-Request-Width\": \"XMLHttpRequest\",\n", 97 | " \"Host\": \"rodi.sba.com.ar\"\n", 98 | " } \n", 99 | "\n", 100 | " s=requests.Session()\n", 101 | " s.headers.update(headers)\n", 102 | " r=s.get(URL)\n", 103 | " soup=BeautifulSoup(r.content, \"lxml\")\n", 104 | "\n", 105 | " VIEWSTATE=soup.find(id=\"__VIEWSTATE\")['value']\n", 106 | " VIEWSTATEGENERATOR=soup.find(id=\"__VIEWSTATEGENERATOR\")['value']\n", 107 | " EVENTVALIDATION=soup.find(id=\"__EVENTVALIDATION\")['value']\n", 108 | "\n", 109 | " login_data={\"__LASTFOCUS\":\"\",\n", 110 | " \"__EVENTTARGET\":\"\",\n", 111 | " \"__EVENTARGUMENT\":\"\",\n", 112 | " \"__VIEWSTATE\":VIEWSTATE,\n", 113 | " \"__VIEWSTATEGENERATOR\":VIEWSTATEGENERATOR,\n", 114 | " \"__EVENTVALIDATION\":EVENTVALIDATION,\n", 115 | " \"ctl00$ContentPlaceHolder1$luLogin$txtUsuario\": username,\n", 116 | " \"ctl00$ContentPlaceHolder1$luLogin$txtClave\":password,\n", 117 | " \"ctl00$ContentPlaceHolder1$luLogin$ctl07\":\"Ingresar\"}\n", 118 | "\n", 119 | " r=s.post(URL, data=login_data)\n", 120 | "\n", 121 | " # luego se llama al HTML de DataPack, sin esto\n", 122 | " # las sucesivas llamadas a DataPack no traen el JSON\n", 123 | " r=s.get(URLDataPack)\n", 124 | "\n", 125 | " with open('cookies.jar', 'wb') as f:\n", 126 | " pickle.dump(s.cookies, f)\n", 127 | "\n", 128 | " print(\"cookies guardadas en archivo cookies.jar\")\n", 129 | " return\n", 130 | " \n", 131 | "def datapack():\n", 132 | " import requests\n", 133 | " import pickle\n", 134 | " import json\n", 135 | " from bs4 import BeautifulSoup\n", 136 | "\n", 137 | " URLDataPack=\"https://rodi.sba.com.ar/Vistas/PanelPersonal.aspx/GetDataPack\"\n", 138 | " headers={\n", 139 | " \"User-Agent\":\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36\",\n", 140 | " \"Referer\": \"https://rodi.sba.com.ar/Vistas/PanelPersonal.aspx\",\n", 141 | " \"X-Request-Width\": \"XMLHttpRequest\",\n", 142 | " \"Host\": \"rodi.sba.com.ar\"\n", 143 | " }\n", 144 | "\n", 145 | " s=requests.Session()\n", 146 | "\n", 147 | " with open('cookies.jar', 'rb') as f:\n", 148 | " s.cookies.update(pickle.load(f))\n", 149 | "\n", 150 | " # los tickers tienen que estar cargados previamente en RODI, de lo contrario esto no funciona.\n", 151 | " params={'obEstadoTabla' : {\"TablaNombre\":\"tbEspecies\",\"FiltroEspecies\":\"AF20_1#AF20_3#AO20_1#AO20_3#AY24_1#AY24_3#GFGC102.OC_0#GFGC105.OC_0#GFGC111.DI_0#GFGC111.OC_0#GFGC120.OC_0#GFGC132.DI_0#GFGC150.DI_0#GFGC63.0OC_0#GFGC75.0OC_0#GFGC80.0OC_0#GFGC87.0OC_0#GFGC90.0OC_0#GFGC93.0OC_0#GFGC99.0OC_0#GFGV102.DI_0#GFGV60.0OC_0#GFGV63.0OC_0#GFGV80.0OC_0#GFGV90.0DI_0#GFGV93.0OC_0#GFGV99.0OC_0#GGAL_3#GFGC102.DI_0\",\"PagActualNro\":\"1\",\"FilasxPagina\":50}}\n", 152 | " # luego al JSON\n", 153 | " r=s.post(URLDataPack, json=params, headers=headers)\n", 154 | "\n", 155 | " with open('cookies.jar', 'wb') as f:\n", 156 | " pickle.dump(s.cookies, f)\n", 157 | " return json.loads(r.text)\n", 158 | " \n", 159 | "def getRealtimeData(aData, aTicker, aVencimiento):\n", 160 | " for item in aData:\n", 161 | " if (item[\"Simbolo\"] == aTicker) and (item[\"VencimientoID\"] == aVencimiento):\n", 162 | " return item\n" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": null, 168 | "metadata": {}, 169 | "outputs": [], 170 | "source": [] 171 | } 172 | ], 173 | "metadata": { 174 | "kernelspec": { 175 | "display_name": "Python 3", 176 | "language": "python", 177 | "name": "python3" 178 | }, 179 | "language_info": { 180 | "codemirror_mode": { 181 | "name": "ipython", 182 | "version": 2 183 | }, 184 | "file_extension": ".py", 185 | "mimetype": "text/x-python", 186 | "name": "python", 187 | "nbconvert_exporter": "python", 188 | "pygments_lexer": "ipython2", 189 | "version": "2.7.12" 190 | } 191 | }, 192 | "nbformat": 4, 193 | "nbformat_minor": 4 194 | } 195 | --------------------------------------------------------------------------------