跳转至


课程  因子投资  机器学习  Python  Poetry  ppw  tools  programming  Numpy  Pandas  pandas  算法  hdbscan  聚类  选股  Algo  minimum  numpy  回测  数据标准化  algo  FFT  模式识别  配对交易  GBDT  LightGBM  XGBoost  statistics  CDF  KS-Test  monte-carlo  VaR  过拟合  algorithms  machine learning  strategy  python  sklearn  pdf  概率  数学  面试题  量化交易  策略分类  风险管理  Info  interview  career  复权  数据  tushare  akshare  xgboost  PCA  wavelet  时序事件归因  SHAP  Figures  Behavioral Economics  graduate  arma  garch  人物  职场  Quantopian  figure  Banz  金融行业  买方  卖方  story  量化传奇  rsi  zigzag  穹顶压力  因子  ESG  因子策略  投资  策略  pe  ORB  Xgboost  Alligator  Indicator  factor  alpha101  alpha  技术指标  wave  quant  algorithm  pearson  spearman  套利  LOF  白银  因子分析  Alphalens  涨停板  herd-behaviour  momentum  因子评估  review  SMC  聪明钱  trade  history  indicators  zscore  波动率  lightgbm  强化学习  顶背离  另类数据  freshman  resources  others  AI  DeepSeek  network  量子计算  金融交易  IBM  weekly  进化论  logic-factor  machine-learning  neutralization  basics  LLT  backtest  backtrader  研报  papers  UBL  quantlib  jupyter-notebook  scikit-learn  pypinyin  qmt  xtquant  blog  static-site  duckdb  工具  colors  free resources  barra  world quant  Alpha  openbb  risk-management  llm  prompt  CANSLIM  Augment  arsenal  copilot  vscode  code  量化数据存储  hdf5  h5py  cursor  augment  trae  Jupyter  jupysql  pyarrow  parquet  数据源  quantstats  实盘  clickhouse  polars  滑动窗口  notebook  sqlite  sqlite-utils  fastlite  大数据  PyArrow  UV  Pydantic  Engineering  redis  remote-agent  AI-tools  Moonshot  回测,研报,tushare  dividend 

2026量化新基建(二) - sqlite 与 sqlite-utils


很多人(包括我)对 sqlite 有一个错觉,第一,它是玩具;第二,它不能用于生产环境。

但实际上, sqlite 是一个非常优秀的数据库。在过去的 2025 年中,它与 postgres 一起名列数据库前二;并且一直是最多人使用的数据库。


量化人说量化事。sqlite 是一把精致的瑞士军刀,它小巧、灵活而强大,可以用在很多地方。但是,对量化人来说,有一个场景,非常适合使用 sqlite: 无须安装和设置、以 pythonic 的方式进行开发,并且具有非常好的性能。

这个场景,就是作为量化程序的交易数据库。

但是,一直以来,我是直接使用 python 内置的 sqlite3 模块来操作 sqlite 数据库的。直到最近,我发现了 sqlite-utils 这个库,它让我以最简洁的方式,获得了全所未有的表达力。

因此,我决定把 sqlite/sqlite-utils 作为 2026 年量化技术栈的第二篇推出。

性能银弹 - wal 模式

作为交易数据库,主要存储的数据是委托记录、成交记录、每日持仓和每日资资产表。这其中以委托记录的数据量为最大,因为我们需要把报单、撤单的记录也都保存下来。


根据量化新规的要求,每秒报单不超过 300 次,每日不超过 2 万次。这并不是一个很有压力的需求,sqlite 完全可以轻松应对。

比性能压力更重要的是,必须要支持并发读写。因为当一个委托被报单后,往往不会立即成交,而我们也不能为此委托一直等待。所以,成交状态一般是通过回调的方式来通知的。这些回调往往安排在其它线程中。

Question

可以在多个进程中,打开同一个 sqlite 数据库进行并发读写吗?

你可能得到的是这样的回答:


实际上,sqlite 从 2010 年起就支持多进程并发读写,并且用户不需要自己管理锁。但是,在 sqlite 中有两种模式,分别是"delete"和"wal"。而 sqlite 的默认选项一直是"delete"模式,这一模式不支持并发,这就导致了很多人就误以为 sqlite 无法支持并发读写。

根据测试和官方文档,启用 wal 模式实际上在几乎所有场景下,都能带来性能提升。但为何 sqlite 的默认选项一直是 delete 呢? 这是因为 sqlite 官方一直坚持这样一种文化 -- 他们一直希望 sqlite 是一个极简的、只有一个文件的数据库。而一旦进入 wal 模式,sqlite 将使用三个数据文件,他们担心部分用户可能会对此感到困惑。

在 wal 模式下,另外两个文件分别是。shm 和 .wal 文件。sqlite 把锁信息放在。shm 文件中 -- 这是一个共享内存文件;sqlite 在 wal 模式下,读数据时会从主库读稳定的数据版,无需加锁;而在写数据时,先会给。wal 文件加锁,只有在最终合并时,才需要短暂地给主数据库加上锁。


因此,在 wal 模式下,sqlite 的锁的粒度会比 delete 模式更细。所以,在 wal 模式下,即使是在单进程读写场景下,性能也会略好一点(5%左右)。

Tip

锁粒度的大小是许多并发场景下,数据库性能高低的关键。在高并发读写下,postgres 会比 sqlite 的性能强很多,是因为它的锁粒度基于 mvcc 技术,粒度要精细许多。

如果开启了 wal 模式,sqlite 处理单笔交易耗时可以远小于 0.1ms,这意味着,sqlite 可以处理每秒至少 1 万笔交易,因此,sqlite 是完全胜任交易数据库的。甚至,由于它不需要经过网络接口,它的延时可能比网络数据库更低。

要允许多个进程中同时读写同一个 sqlite 数据库,你需要这样启用 wal 模式:

1
2
3
4
import sqlite3

conn = sqlite3.connect(path/to/sqlite.db)
conn.execute("PRAGMA journal_mode = WAL;")

显然,这非常不 pythonic。如果我们使用 sqlite-utils,就可以直接这样启用 wal 模式:

1
2
3
4
5
import sqlite_utils as su

db = su.Database(path/to/sqlite.db)
db.enable_wal()
db.journal_mode # 显示为 'wal'

再回到之前的多线程读写问题。sqlite 在开启 wal 模式后,就可以支持并发读写了。但是,如果你跨线程共享 connection 对象,还是需要加锁互斥才行。在这种情况下,我们可以引入一个线程本地封装:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import threading

@singleton
class TradeDB:
    def __init__(self):
        # 每个线程都有自己的数据库连接
        self._thread_local = threading.local()
        self.db_path: str = ""
        self._initialized = False

    def init(self, db_path: str):
        if self._initialized:
            return

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    def init(self, db_path: str):
        if self._initialized:
            return
        # 初始化数据库连接
        self.db_path = db_path

        conn = sqlite3.connect(db_path)
        db = su.Database(conn)

        # 启用 WAL 模式提高并发读性能
        if db_path != ":memory:":
            db.enable_wal()

        # 初始化表结构
        self._init_tables(db)
        conn.close()
        self._initialized = True

    @property
    def db(self)->su.Database:
        """获取当前线程的数据库连接"""
        if not hasattr(self._thread_local, "conn"):
            conn = sqlite3.connect(self.db_path, check_same_thread=True)

            self._thread_local.conn = conn
            self._thread_local.db = su.Database(conn)

        return self._thread_local.db

1
2
3
4
5
6
7
    def __getitem__(self, table_name) -> su.db.Table:
        """代理获取表对象"""
        return self.db[table_name]  # type: ignore

db = TradeDB()
db.init(path/to/sqlite.db)
db["users"].insert({"id": 1, "name": "Quantide"})

通过这个封装,我们暴露了一个全局、惟一的 db 对象,并且允许它在多个线程中,不加锁即可使用。原因是,我们利用线程本地存储(Thread Local Storage, TLS),为每个线程维护了一个独立的数据库连接。

然后,我们通过改写__getitem__方法,把对表的访问,代理到当前线程的数据库连接上,从而你可以使用 与 sqlite-utils 文档中记录的完全一致的方法来进行数据库操作。

Semi-ORM

在 python 中进行数据库操作,可以用原生方式和 ORM 方式。原生方式性能高,但需要熟悉 sql 语法,并且重构不方便;orm 方式允许我们以更接近 python 的语法去操作数据库。这方面的代表是 sqlalchemy。


不过 sqlalchemy 也有自己的问题,就是它太全面、太复杂了。如果只是一个简单的、探索式的应用,使用 sqlalchemy 就显得有些笨重。

这就是许多人选择 sqlite-utils 的原因。sqlite-utils 解决了以下问题:

  1. 它是一个半 orm,既允许你以 pythonic 的方式来操作数据库,又不像 sqlalchemy 那么繁琐。
  2. sqlite 只有数据库引擎,并没有数据管理工具。如果你要知道某个 sqlite 数据库中有哪些表、表的数据如何,还得先编程把它读出来。sqlite-utils 通过 cli 方式提供了这种能力。

Tip

另一个管理 sqlite 数据库的工具是通过 notebook,加上 sqlite-utils 库,或者使用 jupysql 来进行管理。前提也是你要先开启数据库的 wal 模式,这样才能在多个进程间,共享一个数据库。

在 sqlalchemy 中,要拿到数据,你必须先学会如何定义 schema:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 1. 定义引擎
engine = create_engine('sqlite:///data.db')
Base = declarative_base()

# 2. 定义模型
class Tick(Base):
    __tablename__ = 'ticks'
    id = Column(Integer, primary_key=True)
    symbol = Column(String)
    price = Column(Float)
    # 如果明天多了个 'volume' 字段,你得改代码,还得做数据库迁移 (Alembic)

# 3. 建表
Base.metadata.create_all(engine)

# 4. 插入
session.add(Tick(symbol='AAPL', price=100.0))
session.commit()

这里引入了 Base, Column, String, Float, Interger 等类型,以及,你还得知道魔术字__tablename__的用法。

但在 sqlite-utils 中,这一切则是如此简单:

1
2
3
import sqlite_utils as su

db = su.Database(":memory:") # ❶

1
2
3
4
5
6
7
8
9
import sqlite_utils as su

db = su.Database(":memory:") # ❶
db["users"].insert_all([{"id": 1, "name": "Fred"}, 
                        {"id": 2, "name": "Wilma"}
                        ]) # ❷
print(db.tables) # ❸
print(db["users"].columns) # ❹
list(db["users"].rows) # ❺

简直不可思议!你无须定义表结构,就可以直接插入数据!

第一行,我们创建了一个内存数据库,并且建立了连接。如果传入文件名,则是创建了一个磁盘文件数据库。

第二行,我们通过 db["users"] 来引用了现在还不存在的表 -- users,然后直接插入了两条数据。

第三行,我们打印出当前数据库中的所有表格;现在 users 已经存在了。

第四行,我们打印出了表 users 中的列字段,这也是 sqlite-utils 自动为我们判断并创建的表结构。


第五行,我们打印出了表 users 中的所有数据。

sqlite-utils 巧妙地借用了文档数据库(如 MongoDB)的语法,将数据库操作变得异常简单。这样一来,很有可能会涉及到新增字段,或者变更字段定义。但是,如果你的数据应该放在 sqlite 中,那么,新增或者修改字段定义的情况并不会很多,所以,为何不省去繁琐的表结构定义,而且直接让 sqlite-utils 来处理这一切呢?

Tip

你可能会好奇,如果在第 5 行之后,我们新插入一个对象,该对象包含了性别和年龄字段,那么,sqlite-utils 会如何处理? 在内部,sqlite-utils 会判断表结构发生变化,自动调用 tranform 方法来完成新的表结构定义、数据迁移。请见下图所示。


当然,我们更鼓励显示定义表结构的方法,这样可以提高性能,并且增加数据校验能力。除了在上一篇中介绍的 pydantic 的 models 方法之外,如果你已经很熟悉 dataclasses,那么我们还可以介绍一种基于 dataclasses, 学习起来更容易的手作 ORM 方法。

用 dataclass 定义实体关系映射

刚刚演示的 sqlite-utils 示例,我们展示了如何直接从数据库中读取数据,而无需定义表结构。这在探索性的数据分析中非常方便。

但在实际应用中,我们更倾向于显式地定义表结构,并且增加数据类型校验和转换的能力,这样上层调用者的代码才会更简单一致。这就是 dataclass 派上用场的地方。

1
2
3
4
5
from dataclasses import dataclass, asdict, fields
import types
import sqlite_utils as su
from enum import IntEnum
import datetime

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def _dataclass_to_schema(model) -> dict:
    """类方法:解析当前 dataclass 为 sqlite-utils 兼容的 schema 字典"""
    schema = {}

    for f in fields(model):
        if f.type in (str, int, float, bool):
            schema[f.name] = f.type
        # 处理所有联合类型(Union[A, B] 和 A | B 语法)
        elif (
            hasattr(f.type, "__origin__") and f.type.__origin__ is Union
        ) or isinstance(f.type, types.UnionType):
            # 提取非 None 的类型
            non_none_types = [t for t in f.type.__args__ if t is not type(None)]
            if non_none_types:
                base_type = non_none_types[0]
                schema[f.name] = (
                    base_type if base_type in (str, int, float, bool) else str
                )
            else:
                schema[f.name] = str
        elif isinstance(f.type, type) and issubclass(f.type, IntEnum):
            schema[f.name] = int
        else:
            schema[f.name] = str
    return schema

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def create_tables(db: su.Database, model):
    """初始化表结构

    在 sqlite_utils 中,创建表结构并非必须;但会导致 sqlite-utils 无法准确判断类型。
    """
    table = model.__table__
    pk = model.__pk__

    t: su.db.Table = db[table] # type: ignore
    t.create(_dataclass_to_schema(model), pk=pk)

    if model.__indices__ is not None:
        indexes, is_unique = model.__indices__
        t.create_index(indexes, unique=is_unique)

class Gender(IntEnum):
    MALE = 1
    FEMALE = 2

@dataclass
class User:
    # ❶ 通过魔术字段,为后面创建表提供元数据
    __table__ = "users"
    __pk__ = "id"
    __indices__ = (["name"], False)

    id: int
    name: str
    birth: datetime.date
    gender: Gender

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    def __post_init__(self):
        # ❷ sqlite 没有时间类型,时间一般使用字符串存储。
        if isinstance(self.birth, str):
            self.birth = datetime.date.fromisoformat(self.birth)
        # ❸ sqlite 没有枚举类型,整数型枚举一般存为整数,其它枚举类型存为字符串。
        if isinstance(self.gender, int):
            self.gender = Gender(self.gender)

user = User(id=1, name="Alice", birth=datetime.date(2020, 1, 1), gender=Gender.FEMALE)
db = su.Database(memory=True)

create_tables(db, User) # ❹ 显式创建表格--声明主键、索引等
db["users"].insert(asdict(user))

# 显示刚刚插入的记录 ❺
user_from_db = User(**list(db["users"].rows)[0])
user_from_db

在这里有几个小技巧。

第一,我们把类型声明转换为数据库字段(而不是像 sqlalchemy 那样需要显示声明),这非常 pythonic。在_dataclass_to_schema 方法中,我们提取了 User 类的所有字段(不含魔术字段)的类型,如果它是联合类型,就找到第一个非 None 类型作为字段类型;然后,将它们映射为数据库字段类型。当然,由于数据库字段类型有限,多数数据类型被映射为了 TEXT。


第二,我们通过__table__、pkindices 等魔术字段,为创建表提供元数据。这样就非常像 sqlalchemy 了,但是我们只使用了标准、内置语法,看起来简洁很多。

第三,一些 Python 数据类型被映射成为了数据库中的整数和字符串类型。我们从数据库中读取到的,也会是字符串和整数类型,但它们实际上对应着 datetime 或者 Enum 类型。在支持复杂数据类型的数据库中,数据库连接驱动会自动为我们进行转换。

内置的 sqlite 模块和 sqlite-utils 都不会这样做。但实际上这个转换非常容易,我们完全可以自己来做。如注释❷和❸所示,我们通过__post_init__方法来实现了这一点。

在注释❹中,我们显式地创建了表格,声明了主键和索引。如果不这样,接下来的插入语句就会失败,因为 sqlite-utils 无法将 Gender 类型映射为数据库字段。

未完待续


第四,当我们要保存一个 python 对象时,只要它是一个 dataclasss 类,我们就可以通过类似db["users].insert(asdict(user))一样的方法来保存它。asdict是 dataclasses 中的一个方法,用来将 dataclass 转换为字典类,而 sqlite-utils 会将字典转换为表格中的一行,再插入到数据库中。

注释❺演示了如何读取数据库记录的一种方法,即通过rows。它是一个迭代器,要获取具体的某个元素,我们需要先转换为列表,再通过下标来获取。这样得到的数据也是一个字典,通过User(**dict)可以将其转换为 User 对象,并且,在这个转换过程中, __post_init__方法被调用,从而将 birth 从数据库中的字符串转换为 Python 的日期对象,以及将 gender 从整数转换为 Enum 类型。

这一部分,跟我们本系列上一篇中介绍 Pydantic 的部分有思想上相似之处,只不过这次我们改用了 dataclasses 来与 sqlite-utils 来组 CP。

在所有能使用内置语法的地方,我更喜欢只用内置语法,而不使用第三方。如无必要,勿增实体。

sqlite-utils 增删改查

这部分比较简单,我们通过以下代码示例进行说明:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
db = su.Database(memory=True)

user = User(id=1, 
            name="Alice", 
            birth=datetime.date(2020, 1, 1), 
            gender=Gender.FEMALE)

# 增加一条记录:insert
db["users"].insert(asdict(user))

# 增加记录,如果存在,则进行更新:upsert
db["users"].upsert(asdict(user), pk="id")

# 上述语句相当于:update
db["users"].update(1, {"birth": datetime.date(2020, 1, 20)})

# 通过主键查询刚修改的记录 (get),生日将变为 2020-01-20
db["users"].get(1)

# 返回所有记录:rows
list(db["users"].rows)

# 返回所有记录,转换为 DataFrame。这里 pandas/polars 都支持
pd.DataFrame(db["users"].rows) # ❶

# 按条件查询,使用 rows_where 方法
list(db["users"].rows_where("gender = ?", (Gender.FEMALE,)))

# 当然,我们也可以使用 db.query 方法,显然不如上一种方法简洁
list(db.query("SELECT * FROM users WHERE gender = ?", (Gender.FEMALE,)))

# 按条件删除记录,使用 delete_where 方法
db["users"].delete_where("id = ?", (10,))

# 按 id 删除,使用 delete 方法
db["users"].delete(1)

# 查询数据库中的记录数,此时应该为 0
db["users"].count

# 删除 user 表,使用 drop 方法
db["users"].drop()

# 确认表已删除,使用 tables 属性。此时应该返回空列表
db.tables

数据库操作从未如此简单,不是吗?而且加上我们前面实作的廖廖数行 ORM 机制之后,我们可以存储对象、让查询返回对象。

另外,不知道你注意到没有,注释❶中,我们将查询结果转换为了 DataFrame,这里也可以转换成为 polars 的 DataFrame。

将偷懒进行到底

优秀的程序员都是懒惰的。他们不喜欢重复劳动,也不喜欢写重复的代码。他们喜欢用简单的方法来解决复杂的问题。如果你也是这一类,那么很可能还在寻求进一步的封装。

为此,Jeremy 教授为你带来了 fastlite -- fast 系列中的新成员。它在 sqlite-utils 之上,引入了 dataclass (就像我们这里所做的一样),另外,它拓展了查询语法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from fastlite import *

db = database(path/to/db)

# 所有表格的集合
dt = db.t

# 获得表对象 album
album = dt.Album

# 通过 dataclass,将表记录转换为对象
album_dc = album.dataclass()

album_obj = album_dc(**acca_dacca[0])
album_obj

# 表格对象支持以下查询
album(limit = 2) # 返回前两条记录
album(with_pk = 1, limit=2) # 返回前两条记录(对象),并且同时返回主键
album(5) # 返回第 5 条记录(对象)

当然,尽管 fastlite 非常直观,学习成本很低,不过在使用之前,你也可以思考一下,是否真的需要它。毕竟,sqlite-utils 已经提供了足够简洁和丰富的功能。

写在 2025 年封底

敲下这段文字时,2025 年的沙漏只剩下最后 4 小时。2025 的这本书,也写到了最后一页。

这两天,Manus 被 Meta 收购的消息刷屏了互联网。很多人还记得 Manus 刚发布时的争议——加密货币、邀请码、并不光鲜的出身。

在我们的刻板印象里,只有出身名门、履历完美的精英学霸,才配定义 AI 的未来。而 Manus 的团队来自武汉,创始人也只出道于一所普通的 985 高校。这种“学历决定论”,在量化圈似乎更为根深蒂固。

直到 Manus 被 Meta 以 20 亿美元天价收购,创始人肖弘一跃成为 Meta 副总裁,这记响亮的耳光或许能让我们重新审视: 究竟是学历定义了能力,还是能力重塑了规则?

同样重塑规则的,还有 PyTorch 的缔造者,Soumith Chintala。

如果看早年的简历,Soumith 简直是“反面教材”:高中读的是普通公立,高考只进了一个不起眼的“二本”。大学毕业申请美国硕士,连投 12 所学校, 全部拒信 。

没有名校光环,他靠着 J-1 签证“硬闯” CMU 做短期访学,才勉强被 NYU 补录。即便身在 NYU,即便那里坐镇着 AI 泰斗 Yann LeCun,他也并非嫡系弟子。毕业后,他满怀憧憬向 DeepMind 投递简历, 一次,两次,三次,全部石沉大海 。

找不到工作,签证即将到期,为了留在美国,他不得不去一家并不知名的小公司做基础测试。

从 2005 年到 2017 年,这是一段漫长的黑暗隧道。

“二本”出身、硕士全拒、大厂不要、签证危机、项目腰斩……整整十二年,他几乎一直在失败。但他手里握着一盏微弱的灯——Torch-7。这是一个基于 Lua 的冷门框架,他像照顾孩子一样,没日没夜地重构它、打磨它。

正是这盏灯,最终照亮了 Soumisth 的前程。他被杨立昆推荐进了 Meta 的 FAIR 实验室,即使最初只带着一个三人的小团队。

后来的故事大家都知道了:2017 年,那个小团队创造了 PyTorch。它像野火一样燎原,成为了全球最受欢迎的深度学习框架。

2025 年,当有人在 Twitter 上把这段“逆袭史”挖出来时,Soumith 只是淡淡地回复了一句: “所有这些都是真的。但我还欠很多人一个感谢。”

他感谢导师 Pierre Sermanet 的善良;感谢 Yann LeCun 在他“几乎看不到出路”时的两次援手。但他感谢更多的,是那些普通的“路人”。

这让我想起热腾仁波切讲过的一个故事:

有个虔诚的信徒去五台山找文殊菩萨。他在客栈遇到一个醉汉,醉汉非拉着他喝酒。信徒严词拒绝:“我是来见菩萨的,怎能破戒?”

醉汉笑了笑:“酒都不喝,怎么见菩萨?”

几天后,信徒一无所获,失望而归。醉汉又出现了,让他顺路带封信。信徒虽不情愿,还是守信地送到了地址——竟然是一个猪圈。

他拆开信,对着那头猪念道:“金刚亥母,你利益众生的时间到了,可以走了。——落款:文殊”

话音刚落,那头猪便往生而去。

原来,菩萨早已见过,只是凡眼未识。

在 Soumith 的生命里,也有这样的“凡人菩萨”:是他的印度老乡 Praveen,默默在幕后写代码的核心工程师;是那对背了一身债、却咬牙支持儿子去追梦的父母;甚至是那个在 Twitter 上花时间整理他故事的陌生人 Deedy。

我们生命中的贵人,往往都是凡人相、众生相。

最后,Soumith 说:“我相信,每一个如今‘坐在成功之上’的人,背后都有很多挣扎。生活从来不会轻轻松松。”

在 2025 的封底,我写下这三个故事。 平凡、坚韧、感恩 ,是它们的注脚,也是我想送给每一位量化人的新年礼物。

感谢这三年来,支持过匡醍(Quantide)的每一个人。

量化是一场孤独的修行,但你不必独自前行。愿 2026 年,我们能遇到更多积极、向上、自我修炼的伙伴。

见天地,见众生,终见自己。

祝大家新年 Alpha 长红。