Un diario de trading o libro de registro de operaciones es un elemento indispensable para cualquiera que quiera operar a largo plazo. Es una herramienta que nos ayuda a echar la vista atrás y analizar qué se hizo mal. En este artículo pretendo que entendáis cómo a partir de una pequeña interfaz de línea de comandos guiamos al usuario a través de una serie de campos input()
para poder efectuar este registro. Este registro nos va a permitir en el futuro poder elaborar análisis, cálculos y gráficos partiendo de los datos que introduzcamos, en una segunda parte.
Trataremos, a partir de un enfoque didáctico, gestionar las múltiples excepciones a las que un programa generalmente da lugar cuando requiere de inputs introducidos manualmente por el usuario.
Crearemos un programa que gestionará la creación de un archivo *.csv
en caso de no existir, y permitirá la introducción de los siguientes campos: Ticker
, Fecha
, Títulos
, Coste
y Venta
a partir de un número de registro que se irá actualizando. También validaremos que los tickers introducidos por el usuario existan (para el mercado Español para este ejemplo), comparándolos con otro archivo csv llamado Yahoospain.csv que os adjunto en el enlace y que deberéis introducir en vuestro directorio junto a este programa.
import pandas as pd
import datetime
from csv import reader
Vamos a necesitar datetime
ya que queremos que el usuario especifique correctamente las fechas en el formato que deseemos, y también el módulo reader
de la librería csv
para poder validar el campo Tickers
con nuestro archivo Yahoospain.csv.
ct=int(0)
vent=float(0)
r=int(0)
tg=int(0)
g=int(0)
Necesitamos dejar grabada una variable. En ct
grabaremos el número de filas que contendrá nuestro DataFrame,
ya que le proporcionaremos la posibilidad al usuario de escoger el número de registro a modificar, de momento la declaramos como un entero. Hacemos lo mismo para una variable de venta vent
(ya que vamos a añadir que exista esa posibilidad como registro y no sólo como modificación), r
para el número de registros, g
como variable "contador" que más adelante detallaremos y tg
que corresponderá a los títulos acumulados por ticker.
Es importante declararlas ahora para luego evitar posibles errores si no están definidas.
print('\nCrear (C) / continuar (c), CARTERA: ', end="")
reg = input()
Le preguntamos al usuario qué desea hacer, le ofrecemos dos posibilidades: Crear el registro en caso de que no disponga de uno, o la posibilidad de continuarlo.
if reg == 'C' or reg == 'c':
if reg == 'c':
try:
car = pd.read_csv('Cartera.csv' ,header= None)
except FileNotFoundError:
print('\nEl fichero no existe, créelo')
exit()
car.rename(columns={0:'Fecha', 1:'Ticker', 2: 'Cantidad',3:'Coste'}, inplace=True)
ct = len(car)
print(ct)
print(car)
Programamos individualmente las dos posibilidades con el tipo lógico para una posible C mayúscula o minúscula introducida por el usuario.
Empezamos con la c minúscula, la opción de "continuar". En caso de existir leeremos el fichero con pd.read_csv
, renombraremos los campos de las cuatro columnas con: "Fecha", "Ticker", "Cantidad", "Coste" y "Venta"
. Guardamos el número de filas de la variable car
y lo mostramos por pantalla con print
.
El except
lo utilizamos para gestionar la posibilidad de que el fichero no exista o no se encuentre en el directorio y el usuario haya querido continuar con el archivo en lugar de crearlo. Utilizamos la clase de Python FileNotFoundError para que en caso de no encontrarlo saque por pantalla un mensaje y el programa se cierre.
if reg == 'C':
print('\n¿Estás seguro de querer borrar todo? Repite entrada (C) :', end="")
reg = input()
if reg != 'C':
exit()
car = pd.DataFrame()
Con C mayúscula, advertimos primero al usuario de que si continúa va a eliminar el archivo en caso de que ya esté creado, y con !=
hacemos que el programa se cierre en caso de recibir cualquier otro input()
. En caso de recibir otra vez C creamos el DataFrame
.
else:
print('ni C, ni c : ==> Exit')
quit()
Finalizamos con un else
eliminando cualquier otra posibilidad.
try:
with open('Yahoospain.csv', 'r' )as csv_file:
csv_reader = reader(csv_file)
[listyahoo] = list(csv_reader)
Con un try
y utilizando csv y la función reader, pasamos todos los tickers que contiene el archivo Yahoospain.csv (podéis utilizar el csv con los tickers que vosotros queráis) a una variable de tipo lista, entre corchetes [listyahoo]
. Esto nos servirá para advertir al usuario en caso de escribir mal o "inventarse" un ticker.
except FileNotFoundError:
print('Falta fichero para validar los tickers')
exit()
Con un except
gestionamos la posibilidad de que el fichero no exista.
while True:
try:
print('\n** Registro libre', ct ,'o registro a modificar (entero positivo) // Intro, SALIR: ', end="",)
r = int(input())
if r<0 :
r= r*-1
print('\nDebes introducir un número positivo, lo cambio a',r)
if r>ct:
print('\nHay libres menores, corrijo a',ct)
r=ct
except:
break
Una vez el programa ha creado el DataFrame
o le hemos especificado que queremos continuar, se nos mostrará un texto en el que se nos muestra el número de registro actual ct
(teniendo en cuenta el 0) y esperaremos la introducción manual de un número entero.
Si el usuario introduce un número negativo, lo transformaremos a positivo. Si introduce un número superior al del tamaño ct
le diremos que existen registros vacíos inferiores al especificado y haremos r=ct
.
ACTUALIZACIÓN!!
try:
print ('\nTicker:', end="")
cia = input()
cia = cia.upper()
print (cia)
x = cia in listyahoo
if x == False:
print('\nAviso: NO se encuentra como ticker en "Yahoospain.csv"')
fecha = input("\nFecha Compra, dd-mm-aaaa: ")
contfecha = datetime.datetime.strptime(fecha, "%d-%m-%Y")
print('\nTítulos: ', end="")
tit = int(input())
if tit < 0:
print('\nEs una venta, para el resto de entradas el programa tratará el signo')
y = cia in car['Ticker'].values
if y == False:
print('\nEl valor no se encuentra en cartera, no puedes venderlo')
quit()
while g < ct:
if cia == car.iloc[g,1]: #Columna "Ticker" para el registro "g"
tg = tg+car.iloc[g,2] #Columna "Cantidad" para el registro "g"
g+=1
if tg < tit * -1:
print('\nNo tienes suficientes títulos para vender')
quit()
Una vez el número de registro se introduce de forma correcta, queremos que el usuario introduza el campo Ticker
. Lo transformaremos a mayúsculas y validaremos que exista en nuestro fichero de Yahoospain.csv, en caso de que no esté lo comunicaremos.
A continuación deberá introducir la fecha en formato dd-mm-aaaa o de lo contrario saltará un error en contfecha.
Seguidamente, deberemos introducir un número entero para la variable tit
que va a contener el número de acciones del activo en cuestión. Si el usuario introduce un número negativo lo trataremos como una venta y comprobaremos que el campo Tickers
exista, de lo contrario no dejaremos que esa venta se lleve a cabo.
Vamos a crear una variable a modo de contador g
declarada como entero al principio del código que va a comprobar, para valores menores al tamaño de filas y en caso de que exista el Ticker introducido, hacer una suma acumulada a partir de una variable tg
(declarada como entero al principio) de las cantidades coincidentes por Ticker y luego una suma en asignación de +1 para g
para hacer el bucle recursivo.
En caso de que este acumulado sea inferior a la cantidad en negativo introducida por el usuario, significará que no existe un número de acciones o títulos para vender, mostraremos un mensaje de error por pantalla y cerraremos el programa.
print('\nCoste: ', end="")
cost = float(input())
if (tit < 0 and cost > 0):
cost = cost*-1
coste = round(cost,2)
print(coste)
if tit < 0:
print('\nImporte Venta ', end="")
vent = float(input())
if vent < 0:
vent = vent*-1
vent = round(vent,2)
Nos pide coste , que en este caso deberá ser el coste que introdujimos en la compra en caso de vender el total de la posición. Lo hacemos así para que en todo momento tengamos en el resumen de la cartera el coste actualizado de la misma. Evidentemente si vendemos sólo una parte, deberá introducirse únicamente el proporcional a esa parte.
Cambiaremos el signo a la variable cost
en caso de que ésta sea una venta y el usuario haya puesto el coste en positivo, ya que vamos a reducirlo.
Detectando que exista una venta, exigiremos también el importe a ingresar de la venta como flotante y gestionaremos la excepción de los negativos transformándolos en positivos.
except:
print('\nEntrada incorrecta , repita\n')
Para cualquier otra excepción, mostraremos un mensaje de error y el programa se cerrará.
else:
print(ct)
car.loc[r, 'Fecha'] = fecha
car.loc[r, 'Ticker'] = cia
car.loc[r, 'Cantidad'] = tit
car.loc[r,'Coste'] = coste
if r == ct:
ct+=1
Con la función loc
posicionamos r
para cada columna con su respectiva variable y hacemos una suma en asignación de ct
para avanzar en el registro (el número que se le muestra al usuario).
if ct> 0:
resumen = (car.groupby('Ticker').sum())
resumen.insert(3,'Coste Medio ',(resumen['Coste']/resumen['Cantidad']))
print('\n', resumen)
car.to_csv('Cartera.csv', header=None, index= False)
print ('\nGrabado')
exit()
Programamos la suma de las cantidades por ticker en caso de que exista un registro para mostrarlo por pantalla, generamos una columna nueva con el Coste medio
, lo mostramos por pantalla, grabamos el fichero a csv y salimos del programa.
Aquí tenéis el código completo:
import pandas as pd
import datetime
from csv import reader
ct=int(0)
vent=float(0)
r=int(0)
tg=int(0)
g=int(0)
coste=float(0)
print('Crear (C) / continuar (c), CARTERA : ', end="")
reg=input()
if reg == 'C' or reg == 'c':
if reg == 'c':
try:
car = pd.read_csv('Cartera.csv' ,header= None)
except FileNotFoundError:
print('\nEl fichero no existe, créelo\n')
exit()
car.rename(columns={0:'Fecha', 1:'Ticker', 2: 'Cantidad',3:'Coste',4:'Venta'}, inplace=True)
ct = len(car)
print(ct)
print(car)
if reg =='C':
print('\n¿Estás seguro de querer borrar todo? Repite entrada (C) :', end="")
reg = input()
if reg != 'C':
exit()
car = pd.DataFrame()
else:
print('\nni C, ni c : ==> Exit')
quit()
try:
with open('Yahoospain.csv','r') as csv_file:
csv_reader = reader(csv_file)
[listyahoo] = list(csv_reader)
except FileNotFoundError:
print('\nFalta fichero Yahoospain.csv')
exit()
while True:
try:
print('\n** Registro libre', ct ,'o registro a modificar (entero positivo) // Intro, SALIR: ', end="",)
r = int(input())
if r < 0 :
r = r*-1
print('\nDebes introducir un número positivo, lo cambio a',r)
if r > ct:
print('\nHay libres menores, corrijo a',ct)
r = ct
except:
break
try:
print('\nTicker:', end="")
cia = input()
cia = cia.upper()
print(cia)
x = cia in listyahoo
if x == False:
print('Aviso: NO en lista Yahoo')
fecha = input("\nFecha Operación, dd-mm-aaaa: ")
contfecha= datetime.datetime.strptime(fecha, "%d-%m-%Y")
print('\nTítulos: ', end="")
tit = int(input())
if tit < 0:
print('\nEs una venta, para el resto de entradas el programa tratará el signo')
y = cia in car['Ticker'].values
if y == False:
print('\nEl valor no se encuentra en cartera, no puedes venderlo')
quit()
while g < ct:
if cia == car.iloc[g,1]: #Columna "Ticker" para el registro "g"
tg = tg+car.iloc[g,2] #Columna "Cantidad" para el registro "g"
g+=1
if tg < tit * -1:
print('\nNo tienes suficientes títulos para vender')
quit()
print('\nCoste: ', end="")
cost = float(input())
if (tit<0 and cost >0):
cost = cost*-1
coste = round(cost,2)
print(coste)
if tit < 0:
print('\nImporte Venta ', end="")
vent = float(input())
if vent < 0:
vent = vent*-1
vent = round(vent,2)
except:
print('\nEntrada incorrecta , repita\n')
else:
car.loc[r,'Fecha'] = fecha
car.loc[r,'Ticker'] = cia
car.loc[r,'Cantidad'] = tit
car.loc[r,'Coste'] = coste
car.loc[r,'Venta'] = vent
if r == ct:
ct+=1
if ct> 0:
resumen = (car.groupby('Ticker').sum())
resumen.insert(3,'Coste Medio ',(resumen['Coste']/resumen['Cantidad']))
print('\n', resumen)
car.to_csv('Cartera.csv', header=None, index= False)
print ('\nGrabado')
exit()
P.D. El vídeo contiene errores, he pensado en corregirlos con este artículo, también añadiendo la pequeña validación por escrito con el archivo de Yahoo en csv.