Diario de Trading

Gerard Sánchez - 30 de Agosto de 2021 a las 12:47 - Python




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.


1144 visitas
4    Login to like
Categorías:
 Estrategias   Estadísticas   Random   Gestión pasiva   Análisis técnico   Modelos   CEO   Mapas mentales   Liberalismo   Python   Growth   Niusleta   Ahorro   Recursos humanos   Inmobiliario   Fiscalidad   Value investing   Dividendos   Contabilidad   Marketing   Riesgo   IF   Cursos   Opciones   Bolsa