5. Fuente de datos local: objeto DataSource

La fuente de datos local es el núcleo de la funcionalidad de gestión de datos de qteasy. Todos los datos primero deben descargarse y guardarse en la fuente de datos local antes de que qteasy pueda utilizarlos. qteasy utiliza objetos DataSource para administrar la fuente de datos local. La fuente de datos local es similar a una base de datos y contiene una serie de tablas de datos predefinidas. La clase DataSource proporciona una serie de API para administrar los datos en las tablas de datos, incluida la lectura, actualización, eliminación y consulta de datos.

Para crear un nuevo objeto DataSource, puede utilizar el siguiente comando:

>>> import qteasy as qt
>>> ds = qt.DataSource()

Al imprimir el objeto DataSource, puede ver sus propiedades más básicas:

>>> print(ds)
file://csv@qt_root/data/

Desde la cadena impresa file://csv@qt_root/data/ de DataSource, podemos ver sus propiedades básicas.

Los resultados impresos contienen información básica sobre el objeto de origen de datos:

  • file:: el tipo de fuente de datos. qteasy admite dos tipos de fuentes de datos: basadas en archivos y basadas en bases de datos. file significa que todas las tablas de datos de la fuente de datos se almacenan en el disco como archivos. De manera similar, los datos también se pueden almacenar en una base de datos mysql, en cuyo caso el código del tipo de fuente de datos es db.

  • csv: - Formato para guardar el archivo de datos. qteasy puede guardar archivos de datos en el disco en diferentes formatos. El formato más básico es csv, pero los usuarios también pueden guardar datos como archivos hdf y fth (feather) para satisfacer diferentes preferencias de rendimiento.

  • qt_root/data/:: la ruta para guardar archivos de datos. qt_root representa la ruta raíz de instalación de qteasy. De forma predeterminada, todos los archivos de datos se guardan en el subdirectorio /data/ en el directorio raíz.

De manera similar, puede crear un objeto de fuente de datos con el tipo de almacenamiento de datos «base de datos» y ver sus propiedades básicas. Sin embargo, al crear una fuente de datos de tipo «base de datos», debe especificar el nombre de host, el nombre de usuario, la contraseña y/o el nombre de la base de datos MySQL a la que desea conectarse.

>>> ds_db = qt.DataSource(source_type='database', host='localhost', user='您的用户名', password='您的密码', db_name='test_db')
>>> print(ds_db)
db:mysql://localhost@3306/test_db

De manera similar, los resultados impresos contienen información básica sobre la fuente de datos:

  • db:mysql: el tipo de fuente de datos es una base de datos MySQL; Todas las tablas de datos se almacenan en esta base de datos.

  • localhost – el nombre de host de la base de datos

  • 3306 – Puerto de conexión a la base de datos

  • test_db: nombre de la base de datos. Si no se especifica ningún nombre, la base de datos predeterminada es qt_db, pero esta base de datos debe crearse de antemano.

Los diferentes objetos de fuente de datos, si tienen diferentes métodos y rutas de almacenamiento, no tendrán ninguna relación entre sí y no interferirán con los datos de los demás. Sin embargo, si dos fuentes de datos apuntan a la misma ruta y tienen el mismo tipo de archivo, almacenarán datos duplicados.

Para ver propiedades más básicas de la fuente de datos, puede acceder a varias propiedades del objeto de fuente de datos:

>>> print(ds.source_type)  # 数据源的类型
file
>>> print(ds_db.source_type)
db
>>> print(ds.file_type, ds.file_path)  # 数据源的文件类型和存储路径
csv /Users/jackie/Projects/qteasy/qteasy/data/
>>> print(ds.connection_type, ds_db.connection_type)  # 数据源的连接类型
file://csv@qt_root/data/ db:mysql://localhost@3306/test_db

5.1. Fuente de datos predeterminada

qteasy tiene una fuente de datos predeterminada incorporada que no requiere creación manual por parte del usuario. De forma predeterminada, todos los datos se guardan y se leen desde esta fuente de datos predeterminada. Los usuarios pueden configurar los parámetros de esta fuente de datos a través del archivo de configuración de qteasy, asegurando que la fuente de datos apunte a la ruta correcta o que se utilice el nombre de usuario correcto para iniciar sesión en la base de datos correcta. Las propiedades de la fuente de datos predeterminada integrada se pueden ver utilizando el siguiente método:

>>> print(qt.QT_DATA_SOURCE)
db:mysql://www.qteasy.online@3306/ts_db

5.2. Ver tabla de datos

Después de comprender las propiedades básicas del objeto de la fuente de datos, el siguiente paso es comprender mejor los datos almacenados en la fuente de datos.

Todas las tablas de datos de la fuente de datos están predefinidas. Cada tabla de datos almacena un tipo diferente de datos. Estos datos cubren una gran cantidad de datos, incluida información básica, datos de la línea K, informes de desempeño de empresas que cotizan en bolsa, datos macroeconómicos, etc., para diversos productos de inversión como acciones, índices, fondos, futuros y opciones en los mercados de valores y mercados de futuros de Shanghai y Shenzhen. Para obtener una lista completa de tablas de datos, consulte el siguiente capítulo. Esta sección presenta principalmente cómo ver la información y los datos básicos de varias tablas de datos.

La lista de tablas en una fuente de datos se puede obtener a través de la propiedad all_tables de la fuente de datos, que devuelve un objeto de lista que contiene todas las tablas predefinidas en la fuente de datos. Sin embargo, es fundamental aclarar que, si bien muchas tablas están definidas en la fuente de datos, solo tienen definiciones básicas: la información del encabezado, los nombres de los campos, los significados de los campos y los tipos de datos están definidos. Las fuentes de datos recién creadas suelen tener tablas vacías que no contienen datos. Para ver qué tablas de la fuente de datos ya contienen datos, debe utilizar la función get_table_info().

>>> print('All tables in datasource:', len(ds.all_tables))
All tables in datasource: 108
>>> print('Some tables:', ds.all_tables[5:15])
Some tables: ['hk_trade_calendar', 'us_trade_calendar', 'stock_basic', 'hk_stock_basic', 'us_stock_basic', 'stock_names', 'stock_company', 'stk_managers', 'new_share', 'money_flow']

Como puede verse, la versión actual de qteasy predefine 108 tablas de datos, algunas de las cuales tienen nombres como stock_basic.

Para ver los detalles de una tabla específica en la fuente de datos actual, como su definición y si contiene datos, utilice el método get_table_info().

>>> info = ds.get_table_info('stock_basic')
<stock_basic>--<股票基本信息>
852KB/5K records on disc
primary keys: 
----------------------------------------
1:   ts_code:  <5396> entries
     starts: 000001.SZ, end: 920128.BJ

columns of table:
------------------------------------
        columns        dtypes remarks
0       ts_code    varchar(9)    证券代码
1        symbol    varchar(6)    股票代码
2          name   varchar(20)    股票名称
3          area   varchar(10)      地域
4      industry   varchar(10)    所属行业
5      fullname   varchar(50)    股票全称
6        enname  varchar(120)    英文全称
7       cnspell   varchar(40)    拼音缩写
8        market    varchar(6)    市场类型
9      exchange    varchar(6)   交易所代码
10    curr_type    varchar(6)    交易货币
11  list_status    varchar(4)    上市状态
12    list_date          date    上市日期
13  delist_date          date    退市日期
14        is_hs    varchar(2)  是否沪深港通

La información impresa incluye el nombre de la tabla, la descripción, las definiciones de los campos y los tipos de datos.

Vale la pena señalar que si la tabla de datos ya está llena de datos, mostrará el tamaño actual de la tabla. En el ejemplo anterior, a continuación se explica cierta información clave:

  • 852KB/5K records on disc: esto indica que la tabla ocupa aproximadamente 852 KB de espacio en disco y contiene aproximadamente 5000 filas de datos.

  • primary keys: muestra las columnas de clave principal de los datos. Aquí, indica que la columna ts_code es la clave principal de la tabla y esta columna tiene aproximadamente 5396 registros distintos. Si la tabla tiene varias claves principales, cada columna de clave principal se enumerará por separado, junto con el rango de datos para ese campo. Por ejemplo, en el ejemplo anterior, el rango de datos para la columna ts_code es de 000001.SZ a 920128.BJ, lo que nos permite saber que la tabla de información bursátil básica contiene información sobre más de 5000 acciones con códigos del 000001 al 920128.

  • columns of table: esto muestra las definiciones de campo de la tabla de datos, que es el schema de la tabla. Enumera el nombre columns, el tipo de datos dtypes y la información descriptiva remarks para cada columna.

Además, el método get_table_info() devuelve un dict, que también contiene información sobre la tabla. Esta información se puede imprimir y comparar con la información formateada descrita anteriormente para una mejor comprensión.

>>> print(info)
{'table': 'stock_basic', 'table_exists': True, 'table_size': '852KB', 'table_rows': '5K', 'primary_key1': 'ts_code', 'pk_records1': 5396, 'pk_min1': '000001.SZ', 'pk_max1': '920128.BJ', 'primary_key2': None, 'pk_records2': None, 'pk_min2': None, 'pk_max2': None}

5.3. Ver información general sobre la fuente de datos

Utilice los métodos anteriores para inspeccionar cada tabla; overview() resume todo el DataSource:

>>> overview = ds.overview()
Analyzing local data source tables... depending on size of tables, it may take a few minutes
[########################################]104/104-100.0%  A...zing completed!
Finished analyzing datasource: 
file://csv@qt_root/data/
3 table(s) out of 104 contain local data as summary below, to view complete list, print returned DataFrame
===============================tables with local data===============================
               Has_data Size_on_disk Record_count Record_start Record_end
table                                                                    
trade_calendar   True       1.8MB         70K          CFFEX        SZSE 
stock_basic      True       852KB          5K           None        None 
stock_daily      True      98.8MB        1.3M       20211112    20241231

Ver la información general de toda la fuente de datos puede llevar varios minutos porque requiere resumir toda la información de cada tabla de datos.

Durante la descripción general, qteasy muestra una barra de progreso, luego devuelve un DataFrame de todas las tablas e imprime estadísticas clave.

El resumen muestra tres tablas completas (trade_calendar, stock_basic, stock_daily) con recuentos de filas, uso del disco y fechas de inicio/finalización. Imprima el DataFrame devuelto para ver todas las tablas:

>>> print(overview)
                   has_data   size records       pk1 records1       min1  \
table                                                                      
trade_calendar         True  1.8MB     70K  cal_date    12865   19901012   
hk_trade_calendar     False      0       0  cal_date  unknown        N/A   
us_trade_calendar     False      0       0  cal_date  unknown        N/A   
stock_basic            True  852KB      5K   ts_code     5396  000001.SZ   
hk_stock_basic        False      0       0   ts_code  unknown        N/A   
...                     ...    ...     ...       ...      ...        ...   
cn_cpi                False      0       0     month  unknown        N/A   
cn_ppi                False      0       0     month  unknown        N/A   
cn_money              False      0       0     month  unknown        N/A   
cn_sf                 False      0       0     month  unknown        N/A   
cn_pmi                False      0       0     month  unknown        N/A   

                        max1       pk2 records2   min2  max2  
table                                                         
trade_calendar      20251231  exchange        7  CFFEX  SZSE  
hk_trade_calendar        N/A      None     None   None  None  
us_trade_calendar        N/A      None     None   None  None  
stock_basic        920128.BJ      None     None   None  None  
hk_stock_basic           N/A      None     None   None  None  
...                      ...       ...      ...    ...   ...  
cn_cpi                   N/A      None     None   None  None  
cn_ppi                   N/A      None     None   None  None  
cn_money                 N/A      None     None   None  None  
cn_sf                    N/A      None     None   None  None  
cn_pmi                   N/A      None     None   None  None  

[104 rows x 11 columns]

5.4. La tabla de datos más importante.

En el primer uso, las tablas suelen estar vacías; por diseño, qteasy simplifica la adquisición y el uso de datos financieros.

Los datos básicos son fáciles de descargar, pero algunas tablas son más críticas; complételas primero porque otras dependen de ellas y del propio qteasy:

  • trade_calendar — Calendario de operaciones para todos los intercambios (día de negociación, código/nombre del intercambio). Principal de qteasy: muchas funciones fallan o se ralentizan sin él. Se utiliza para determinar los días de negociación y los rangos de fechas de descarga. Complete esta tabla primero.

  • stock_basic — Tabla básica de acciones con código, nombre, fechas de cotización/eliminación de la lista, industria, región, etc. Base para tablas financieras y de línea K diaria de acciones; priorizar llenar esta tabla.

  • index_basic: tabla básica del índice con código, nombre, fechas de publicación/eliminación de la lista, etc. Base para la línea K diaria del índice y tablas de constituyentes; priorizar llenar esta tabla.

  • fund_basic — Tabla básica de fondos con código, nombre, tipo, tamaño, etc. Base para tablas diarias de línea K y NAV de fondos; priorizar llenar esta tabla.

Una vez que se completan estas tablas clave, la mayoría de las funciones de datos de qteasy funcionan sin problemas.

qteasy también define cuatro tablas del sistema para registros de operaciones en vivo:

  • sys_op_live_accounts: tabla maestra de cuenta comercial real con ID de cuenta, nombre, tipo, estado, etc.

  • sys_op_positions: tabla de posiciones en vivo con ID de cuenta, código/nombre de seguridad, cantidad, costo, etc.

  • sys_op_trade_orders: tabla de pedidos en vivo con ID de cuenta, hora/tipo de pedido, código de seguridad, cantidad, precio, etc.

  • sys_op_trade_results: tabla de llenado en vivo con ID de cuenta, tiempo de llenado, código de seguridad, cantidad, precio, etc.

Estas cuatro tablas del sistema sustentan el comercio en vivo; los datos se generan automáticamente: no los complete, vea ni elimine manualmente.

Otras mesas

Además de las tablas clave, DataSource define muchas más que cubren conceptos básicos, línea K diaria, finanzas, dividendos, informes de ganancias, datos macro, etc.:

  • Tablas de datos de mercado: OHLCV en varias frecuencias para acciones, fondos e índices

  • Tablas básicas: acciones, fondos, índices, futuros, opciones, etc.

  • Tablas de indicadores: indicadores técnicos, fundamentales, macro, etc.

  • Tablas de estados financieros: balance, cuenta de resultados, flujo de caja, etc.

  • Tablas de informes de ganancias: informes rápidos, orientación de ganancias, pronósticos, etc.

  • Tablas de dividendos y operaciones en bloque: dividendos, operaciones en bloque, operaciones con accionistas, etc.

  • Tablas de referencia: macro, industria, intercambio y otros datos de referencia

Consulte get_table_info() o el siguiente capítulo para conocer las definiciones de tablas y los tipos de datos.

5.5. Leer datos de tablas

Una vez completado, use read_table_data() para leer con filtros de fecha/código sin preocuparse por los detalles de almacenamiento.

Para evitar lecturas enormes, pase siempre los filtros. Con read_table_data() puedes filtrar por código de seguridad y rango de fechas:

  • shares: Un código de seguridad o códigos separados por comas. Si la clave principal incluye un código de seguridad, filtre la salida por esos códigos.

  • start: una fecha en formato “YYYYMMDD”. Si la clave principal de la tabla incluye hora o fecha, filtre las filas entre start y end; start/end deben proporcionarse como un par

  • end: una fecha en formato “YYYYMMDD”. Si la clave principal de la tabla incluye hora o fecha, filtre las filas entre start y end; start/end deben proporcionarse como un par

DataSource filtra datos automáticamente según sus criterios, por ejemplo:

Lea los datos diarios de la línea K para 000651.SZ de stock_daily entre 2024-01-01 y 2024-01-15:

>>> ds.read_table_data(table='stock_daily', shares='000651.SZ', start='20240101', end='20240115')
                       open   high    low  ...  pct_chg        vol       amount
ts_code   trade_date                       ...                                 
000651.SZ 2024-01-03  32.00  32.08  31.70  ...  -0.7181  254468.92   810315.013
          2024-01-04  31.90  32.01  31.45  ...   0.4717  333398.05  1057458.411
          2024-01-08  33.12  33.21  32.85  ...  -0.2426  415911.34  1372722.050
          2024-01-02  32.17  32.20  31.96  ...  -0.4352  253797.30   814257.175
          2024-01-15  33.45  33.95  33.42  ...   0.6544  295681.34   996815.725
          2024-01-11  33.66  33.82  33.42  ...  -0.2376  284088.74   955075.100
          2024-01-09  32.81  33.55  32.65  ...   1.7933  438207.66  1454959.637
          2024-01-10  33.35  33.84  33.28  ...   0.5375  366485.52  1233441.572
          2024-01-12  33.50  33.83  33.42  ...   0.0893  224012.73   753931.821
          2024-01-05  32.05  33.29  31.62  ...   3.2238  832156.75  2738167.636

[10 rows x 9 columns]

DataSource aplica los filtros correctos para cada tabla e ignora los parámetros innecesarios. Por ejemplo, al consultar información básica de dos acciones, no se requiere la fecha de negociación: qteasy ignora start/end con un aviso. El parámetro shares también funciona para fondos, índices, futuros y opciones, no solo para acciones:

>>> ds.read_table_data(table='stock_basic', shares='000651.SZ,000700.SZ', start='20240101', end='20240131')
/Users/jackie/Projects/qteasy/qteasy/database.py:1314: RuntimeWarning: list index out of range
can not find date-like primary key in the table stock_basic!
passed start(2024-01-01) and end(2024-01-31) arguments will be ignored!
  warnings.warn(msg, RuntimeWarning)
           symbol  name area industry  ... list_status list_date delist_date is_hs
ts_code                                ...                                        
000651.SZ     651  格力电器   广东     家用电器  ...           L  19961118         NaN     S
000700.SZ     700  模塑科技   江苏     汽车配件  ...           L  19970228         NaN     S

[2 rows x 14 columns]

Consulte la referencia de DataSource para obtener más información sobre read_table_data().

5.6. Agregar datos a tablas

Las lecturas fallan si las tablas están vacías o son insuficientes; llene las tablas primero.

Tenga en cuenta:

Esto cubre únicamente escrituras manuales; La descarga, limpieza y recarga automáticas se tratan más adelante.

Rellenar tablas mediante update_table_data(), escribiendo un DataFrame con tres parámetros:

  • table: Nombre de la tabla de destino en la que escribir

  • df: Un DataFrame que contiene datos para escribir en una tabla

  • merge_type: Si es update, actualice las filas existentes en la tabla; si es ignore, omita las filas duplicadas.

Con update_table_data(), no se requiere una coincidencia exacta del esquema: qteasy normaliza el formato y elimina duplicados antes de escribir.

Los datos de muestra se escriben a continuación solo para demostración.

>>> import pandas as pd
>>> df = pd.DataFrame({
...         'ts_code':    ['000001.SZ', '000002.SZ', '000003.SZ', '000004.SZ', '000005.SZ',
...                        '000001.SZ', '000002.SZ', '000003.SZ', '000004.SZ', '000005.SZ'],
...         'trade_date': ['20211112', '20211112', '20211112', '20211112', '20211112',
...                        '20211113', '20211113', '20211113', '20211113', '20211113'],
...         'open':       [1., 2., 3., 4., 5., 6., 7., 8., 9., 10.],
...         'high':       [2., 3., 4., 5., 6., 7., 8., 9., 10., 1.],
...         'low':        [3., 4., 5., 6., 7., 8., 9., 10., 1., 2.],
...         'close':      [4., 5., 6., 7., 8., 9., 10., 1., 2., 3.]
...     })
>>> print(df)
     ts_code trade_date  open  high   low  close
0  000001.SZ   20211112   1.0   2.0   3.0    4.0
1  000002.SZ   20211112   2.0   3.0   4.0    5.0
2  000003.SZ   20211112   3.0   4.0   5.0    6.0
3  000004.SZ   20211112   4.0   5.0   6.0    7.0
4  000005.SZ   20211112   5.0   6.0   7.0    8.0
5  000001.SZ   20211113   6.0   7.0   8.0    9.0
6  000002.SZ   20211113   7.0   8.0   9.0   10.0
7  000003.SZ   20211113   8.0   9.0  10.0    1.0
8  000004.SZ   20211113   9.0  10.0   1.0    2.0
9  000005.SZ   20211113  10.0   1.0   2.0    3.0

El ejemplo DataFrame anterior se escribirá en index_daily. Actualmente index_daily está vacío y su schema difiere ligeramente del DataFrame:

  • El esquema de la tabla index_daily define 11 columnas, mientras que el ejemplo DataFrame anterior tiene solo 6; todas las columnas DataFrame todavía están dentro del index_daily schema.

  • La tabla index_daily está actualmente vacía y no se han completado datos.

>>> info = ds.get_table_info('index_daily')
<index_daily>--<指数日线行情>
0 MB/0 records on disc
primary keys: 
----------------------------------------
1:   ts_code:  <unknown> entries
     starts: N/A, end: N/A
2:   trade_date:  <unknown> entries
     starts: N/A, end: N/A

columns of table:
------------------------------------
       columns       dtypes  remarks
0      ts_code  varchar(20)     证券代码
1   trade_date         date     交易日期
2         open        float      开盘价
3         high        float      最高价
4          low        float      最低价
5        close        float      收盘价
6    pre_close        float      昨收价
7       change        float      涨跌额
8      pct_chg        float      涨跌幅
9          vol       double   成交量(手)
10      amount       double  成交额(千元)

A continuación escribimos datos; Si tiene éxito, el método devuelve filas escritas:

>>> ds.update_table_data(table='index_daily', df=df)
10

Después de escribir, podemos volver a leer los datos que acabamos de insertar.

Muchas columnas se leen como NaN porque el df escrito no incluía esos campos.

>>> df = ds.read_table_data('index_daily', shares='000001.SZ, 000002.SZ')
>>> print(df)
                      open  high  low  close  pre_close  change  pct_chg  vol  \
ts_code   trade_date                                                            
000001.SZ 2021-11-12   1.0   2.0  3.0    4.0        NaN     NaN      NaN  NaN   
000002.SZ 2021-11-12   2.0   3.0  4.0    5.0        NaN     NaN      NaN  NaN   
000001.SZ 2021-11-13   6.0   7.0  8.0    9.0        NaN     NaN      NaN  NaN   
000002.SZ 2021-11-13   7.0   8.0  9.0   10.0        NaN     NaN      NaN  NaN   

                      amount  
ts_code   trade_date          
000001.SZ 2021-11-12     NaN  
000002.SZ 2021-11-12     NaN  
000001.SZ 2021-11-13     NaN  
000002.SZ 2021-11-13     NaN  

5.7. Mesa desplegable: tenga cuidado; ¡¡La eliminación es irreversible!!

También puede eliminar tablas enteras: qteasy no admite eliminaciones parciales; ten mucho cuidado.

DataSource está optimizado para almacenamiento y lecturas, no para eliminaciones frecuentes: es un almacén de datos, no una base de datos general.

¡El método DataSource drop_table_data() elimina una tabla completa y no se puede deshacer!

Para evitar caídas accidentales, se crean errores drop_table_data() de forma predeterminada, p. al eliminar datos temporales index_daily:

>>> ds.drop_table_data('index_daily')
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Cell In[19], line 1
----> 1 ds.drop_table_data('index_daily')

File ~/Projects/qteasy/qteasy/database.py:1587, in DataSource.drop_table_data(self, table)
   1584 if not self.allow_drop_table:
   1585     err = RuntimeError('Can\'t drop table from current datasource according to setting, please check: '
   1586                        'datasource.allow_drop_table')
-> 1587     raise err
   1589 if self.source_type == 'db':
   1590     self._drop_db_table(db_table=table)

RuntimeError: Can't drop table from current datasource according to setting, please check: datasource.allow_drop_table

Establezca allow_drop_table en True para permitir caídas; Vuelva a configurarlo en False después.

El siguiente código cae index_daily; luego las lecturas de esa tabla fallan:

>>> ds.allow_drop_table = True
>>> ds.drop_table_data('index_daily')
>>> ds.allow_drop_table = False
>>> df = ds.read_table_data(table='index_daily')
>>> print(df)
    
Empty DataFrame
Columns: []
Index: []

5.8. Resumen

Ahora entendemos DataSource, la clase principal de qteasy para la gestión de datos del historial financiero, que incluye:

  • ¿Qué es DataSource y cómo crear uno?

  • Extrayendo datos de DataSource

  • Trabajando con DataSource

Los capítulos posteriores cubren más:

  • ¿Qué datos financieros útiles hay en DataSource?

  • ¿Cómo descargar y completar por lotes DataSource?

  • ¿Extraer información de DataSource de forma más eficaz?