量化投资从0开始系列 ---- 3. 无时间戳的数据类型

首先获取股票列表,这类数据没有有效的时间戳让我们判断是否是新数据,且所有历史数据都可能发生变化,比如名称中的ST等。对应的_delta方法要单独考虑。

在父类AbstractDataRetriever的基础上(见https://blog.csdn.net/kengxie/article/details/118086422),去获取tushare的数据就很简单了。实际需要实现的就是_full和_delta这两个抽象方法。它们的返回值都要求是dataframe,然后框架会把dataframe按append的方式写入数据库。

class StockBasic(AbstractDataRetriever):
    def __init__(self):
        super().__init__('stock_basic')

    def _full(self, **kwargs):
        pass

    def _delta(self, **kwargs):
        pass

参考官方说明https://waditu.com/document/2?doc_id=25,公开的接口不能一次取得所有数据,如果要把L上市 D退市 P暂停上市三种状态的股票信息都读取回来的话,需要请求三次,每次获得一种状态的数据。没关系,我们可以在本地利用dataframe把全部数据合并到一起。

那么_full的实现就是这样:

    def _full(self, **kwargs):
        df_list = pro.stock_basic(list_status='L', fields=StockBasic._fields)
        df_delist = pro.stock_basic(list_status='D', fields=StockBasic._fields)
        df_pending = pro.stock_basic(list_status='P', fields=StockBasic._fields)

        return pd.concat([df_list, df_delist, df_pending], ignore_index=True)

执行一遍再登陆数据库看看,数据正确取得了:

_delta的实现就要麻烦一些,关键是数据本身没有明显的标志可以作为是否是新数据的判断依据。考虑到数据量很小,总共也就4000+,干脆也全部取回来好了。

    def _delta(self, **kwargs):
        return self._full(**kwargs)

但是写入数据库的时候就不能用if_exists = 'append'的方式了,这里需要覆盖原来的所有数据。

dataframe自带的replace方式不够友好,它默认的实现方式是删除整张表,然后重建表再写入数据。这里我希望保留表结构,因为我手动创建了主键,添加了索引,还修改了字段类型,这些我希望保留。

把base class修改一下,自定义一个_replace方法,采用保留表结构,先删除整表数据,再插入数据的方式来完成replace操作。特别的,要把delete和insert放到一个事务中:

    def _save(self, df):
        if self.if_exists == 'replace':
            self._replace(df)
        else:
            df.to_sql(self.table_name, engine_ts, index=False, if_exists=self.if_exists, chunksize=5000)
    
    def _replace(self, df):
        db = SQLDatabase(engine_ts)
        with db.run_transaction() as conn:
            if self._initialized():
                stmt_truncate = text(f"delete from {self.table_name}")
                conn.execute(stmt_truncate)

            df.to_sql(self.table_name, conn, index=False, if_exists='append', chunksize=5000)

在子类的构造方法中指明采用if_exists = 'replace':

    def __init__(self):
        super().__init__('stock_basic', if_exists='replace')

测试一下,没有问题,搞定!

完整的StockBasic类:

class StockBasic(AbstractDataRetriever):
    _fields = 'ts_code,symbol,name,area,industry,market,exchange,list_status,list_date,delist_date,is_hs'

    def __init__(self):
        super().__init__('stock_basic', if_exists='replace')

    def _full(self, **kwargs):
        df_list = pro.stock_basic(list_status='L', fields=StockBasic._fields)
        df_delist = pro.stock_basic(list_status='D', fields=StockBasic._fields)
        df_pending = pro.stock_basic(list_status='P', fields=StockBasic._fields)

        return pd.concat([df_list, df_delist, df_pending], ignore_index=True)

    def _delta(self, **kwargs):
        return self._full(**kwargs)

全部代码上传到https://github.com/xiekeng/tushare-client,感兴趣的可以自取。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

blkq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值