背景¶
backtrader 已经比较完善了,我想要借鉴量化投资框架中其他项目的优势,继续改进优化 backtrader。
任务¶
阅读研究分析 backtrader 这个项目的源代码,了解这个项目。
阅读研究分析/Users/yunjinqi/Documents/量化交易框架/thOth
借鉴这个新项目的优点和功能,给 backtrader 优化改进提供新的建议
写需规文档和设计文档放到这个文档的最下面,方便后续借鉴
thOth 项目简介¶
thOth 是一个 C++量化金融库,类似于 QuantLib 的设计,具有以下核心特点:
C++实现: 高性能 C++实现
金融数学: 丰富的金融数学模型
定价模型: 衍生品定价模型
时间序列: 时间序列分析工具
日期处理: 完善的日期和日历处理
数值方法: 数值计算方法库
重点借鉴方向¶
金融模型: 金融数学模型实现
定价引擎: 衍生品定价引擎
时间序列: 时间序列分析
日期日历: 日期和交易日历
数值方法: 数值计算方法
C++设计: C++设计模式借鉴
架构对比分析¶
Backtrader 核心特点¶
优势:*
成熟的 Line 系统: 基于循环缓冲区的高效时间序列数据管理
完整的回测引擎: Cerebro 统一管理策略、数据、经纪商、分析器
丰富的技术指标: 60+内置技术指标
性能优化: 支持向量化(once 模式)和事件驱动(next 模式)双执行模式
Cython 加速: 关键路径使用 Cython 优化
多市场支持: 支持股票、期货、加密货币等多种市场
Python 优先: 纯 Python 实现,易于扩展和定制
局限:*
事件系统简单: 缺少完整的观察者模式实现
日历系统简陋: 只有简单的交易日历
惰性计算缺失: 指标每次都重新计算
Excel 集成: 缺少 Excel 集成
日期处理: 日期处理功能有限
动态更新: 策略更新需要重新创建
thOth 核心特点¶
优势:*
观察者模式: 完整的 Observable/Observer 模式实现
可重链接句柄: 支持动态策略更新
惰性对象: 按需计算和结果缓存
日历系统: 桥接模式的日历系统
工作日惯例: 支持多种工作日调整惯例
Excel 集成: 内置 Excel 日期转换
时间序列: 模板化的时间序列容器
C++性能: 高性能 C++实现
局限:*
语言门槛: C++开发门槛高
生态较小: 相比 Python 生态不够成熟
文档较少: 英文文档为主
GUI 绑定: 与 MFC 绑定,跨平台受限
需求规格文档¶
1. 观察者模式增强 (优先级: 高)¶
需求描述:*
引入完整的观察者模式,改进事件分发机制。
功能需求:*
Observable 基类: 可观察对象基类
Observer 接口: 观察者接口
自动通知: 数据变化自动通知观察者
双向注册: 观察者与被观察者双向管理
异常安全: 通知过程中的异常处理
非功能需求:*
不影响现有性能
向后兼容现有代码
2. 惰性计算系统 (优先级: 高)¶
需求描述:*
引入惰性计算模式,缓存计算结果避免重复计算。
功能需求:*
LazyObject: 惰性计算对象基类
计算缓存: 缓存计算结果
失效机制: 数据变化时使缓存失效
冻结/解冻: 手动控制计算时机
性能监控: 计算次数统计
非功能需求:*
内存占用可控
不影响实时性要求高的场景
3. 增强日历系统 (优先级: 中)¶
需求描述:*
改进交易日历系统,支持更多市场和工作日惯例。
功能需求:*
日历基类: 抽象日历接口
多市场支持: 不同市场的交易日历
工作日惯例: following/preceding/modified 等
节假日管理: 支持添加/移除节假日
周末处理: 灵活的周末定义
非功能需求:*
保持现有 API 兼容
支持自定义日历
4. 可重链接句柄 (优先级: 中)¶
需求描述:*
引入句柄系统,支持策略组件动态替换。
功能需求:*
Handle 类: 可重链接的句柄
动态更新: 运行时更换策略组件
引用追踪: 自动追踪所有句柄引用
通知传播: 更新后自动通知观察者
非功能需求:*
类型安全
内存安全
5. Excel 集成 (优先级: 低)¶
需求描述:*
提供 Excel 集成功能,方便数据分析。
功能需求:*
日期转换: Excel 日期序列号转换
数据导出: 导出为 Excel 格式
UDF 支持: Excel 用户自定义函数
实时更新: 支持 Excel 实时数据更新
6. 时间序列增强 (优先级: 低)¶
需求描述:*
改进时间序列数据管理。
功能需求:*
观察者支持: 时间序列变化自动通知
高效查找: 基于日期的高效查找
插入通知: 新数据插入自动通知
类型安全: 模板化的类型安全
设计文档¶
1. 观察者模式设计¶
1.1 观察者基类¶
# backtrader/observer/base.py
from typing import Set, List, Optional, Callable
from abc import ABC, abstractmethod
class Observer(ABC):
"""
观察者基类
接收来自被观察对象的更新通知
"""
def __init__(self):
self._observables: Set['Observable'] = set()
def register_with(self, observable: 'Observable') -> bool:
"""
注册到被观察对象
Args:
observable: 被观察对象
Returns:
是否成功注册
"""
if observable not in self._observables:
success = observable.register_observer(self)
if success:
self._observables.add(observable)
return True
return False
def unregister_from(self, observable: 'Observable') -> bool:
"""
从被观察对象注销
Args:
observable: 被观察对象
Returns:
是否成功注销
"""
if observable in self._observables:
count = observable.unregister_observer(self)
if count > 0:
self._observables.discard(observable)
return True
return False
def unregister_from_all(self) -> None:
"""从所有被观察对象注销"""
for observable in list(self._observables):
self.unregister_from(observable)
@abstractmethod
def update(self, observable: 'Observable', event=None) -> None:
"""
接收更新通知
Args:
observable: 发送通知的被观察对象
event: 事件数据
"""
raise NotImplementedError
class Observable:
"""
被观察对象基类
管理观察者并发送更新通知
"""
def __init__(self):
self._observers: Set[Observer] = set()
def register_observer(self, observer: Observer) -> bool:
"""
注册观察者
Args:
observer: 观察者对象
Returns:
是否是新注册的观察者
"""
if observer not in self._observers:
self._observers.add(observer)
return True
return False
def unregister_observer(self, observer: Observer) -> int:
"""
注销观察者
Args:
observer: 观察者对象
Returns:
剩余观察者数量
"""
self._observers.discard(observer)
return len(self._observers)
def notify_observers(self, event=None) -> None:
"""
通知所有观察者
Args:
event: 事件数据
"""
# 创建观察者列表副本,避免通知过程中修改
observers = list(self._observers)
for observer in observers:
try:
observer.update(self, event)
except Exception:
# 观察者异常不影响其他观察者
pass
@property
def observer_count(self) -> int:
"""获取观察者数量"""
return len(self._observers)
```bash
#### 1.2 在现有类中集成观察者模式
```python
# backtrader/lineseries.py 中添加观察者支持
from backtrader.observer.base import Observable
class LineSeries(Observable):
"""
带观察者支持的 LineSeries
数据变化时自动通知观察者
"""
def __init__(self):
super().__init__()
self._last_notified_len = 0
def home(self) -> None:
"""将当前点设为基准点"""
super().home()
# 通知观察者数据已重置
self.notify_observers(event={'type': 'home'})
def advance(self) -> None:
"""前进一步"""
super().advance()
# 只通知新增的数据点
if len(self) > self._last_notified_len:
self.notify_observers(event={
'type': 'advance',
'from': self._last_notified_len,
'to': len(self)
})
self._last_notified_len = len(self)
```bash
### 2. 惰性计算系统设计
```python
# backtrader/lazy/base.py
from typing import Dict, Any
from datetime import datetime
class LazyObject:
"""
惰性计算对象基类
只有在需要时才计算结果,并缓存计算结果
"""
def __init__(self):
self._calculated = False
self._frozen = False
self._result = None
self._calc_count = 0 # 计算次数统计
def calculate(self) -> Any:
"""
执行计算(如果需要)
Returns:
计算结果
"""
if not self._calculated and not self._frozen:
self._perform_calculation()
self._calculated = True
self._calc_count += 1
return self._result
def freeze(self) -> None:
"""冻结对象,停止自动计算"""
self._frozen = True
def unfreeze(self) -> None:
"""解冻对象,清除缓存"""
self._frozen = False
self._calculated = False
self._result = None
def is_calculated(self) -> bool:
"""是否已计算"""
return self._calculated
@property
def calculation_count(self) -> int:
"""获取计算次数"""
return self._calc_count
def _perform_calculation(self) -> None:
"""
执行实际计算
子类实现此方法
"""
raise NotImplementedError
def _set_result(self, result: Any) -> None:
"""设置计算结果"""
self._result = result
def _invalidate(self) -> None:
"""使缓存失效"""
self._calculated = False
self._result = None
class LazyIndicator(LazyObject):
"""
惰性计算指标
基于现有 Indicator 添加惰性计算支持
"""
def __init__(self, indicator):
super().__init__()
self._indicator = indicator
self._data_len_snapshot = 0
def _perform_calculation(self) -> None:
"""执行指标计算"""
# 使用原始指标计算
result = []
for i in range(len(self._indicator)):
result.append(self._indicator[i])
self._set_result(result)
self._data_len_snapshot = len(self._indicator)
def is_stale(self) -> bool:
"""
检查数据是否已过期
Returns:
如果底层数据长度变化则返回 True
"""
return len(self._indicator) != self._data_len_snapshot
def calculate_if_needed(self) -> Any:
"""仅在需要时计算"""
if self.is_stale():
self.unfreeze() # 清除缓存
return self.calculate()
```bash
### 3. 增强日历系统设计
```python
# backtrader/calendar/base.py
from typing import Set, List, Optional
from datetime import date, datetime, timedelta
from enum import Enum
class BusinessDayConvention(Enum):
"""工作日调整惯例"""
FOLLOWING = "following" # 顺延到下一个工作日
MODIFIED_FOLLOWING = "modified_following" # 修正顺延
PRECEDING = "preceding" # 提前到上一个工作日
MODIFIED_PRECEDING = "modified_preceding" # 修正提前
UNADJUSTED = "unadjusted" # 不调整
class TradingCalendar:
"""
交易日历基类
管理交易日和节假日
"""
def __init__(self, name: str = "default"):
self.name = name
self._weekend_days: Set[int] = set() # 0=周一, 6=周日
self._holidays: Set[date] = set()
self._added_holidays: Set[date] = set()
self._removed_holidays: Set[date] = set()
def is_weekend(self, dt: date) -> bool:
"""
检查是否是周末
Args:
dt: 日期
Returns:
是否是周末
"""
return dt.weekday() in self._weekend_days
def is_holiday(self, dt: date) -> bool:
"""
检查是否是节假日
Args:
dt: 日期
Returns:
是否是节假日
"""
if dt in self._removed_holidays:
return False
if dt in self._added_holidays:
return True
return dt in self._holidays
def is_business_day(self, dt: date) -> bool:
"""
检查是否是工作日
Args:
dt: 日期
Returns:
是否是工作日
"""
return not (self.is_weekend(dt) or self.is_holiday(dt))
def add_holiday(self, dt: date) -> None:
"""添加节假日"""
self._added_holidays.add(dt)
def remove_holiday(self, dt: date) -> None:
"""移除节假日"""
self._removed_holidays.add(dt)
def adjust(self, dt: date,
convention: BusinessDayConvention = BusinessDayConvention.FOLLOWING) -> date:
"""
调整到工作日
Args:
dt: 日期
convention: 调整惯例
Returns:
调整后的日期
"""
if convention == BusinessDayConvention.UNADJUSTED:
return dt
if self.is_business_day(dt):
return dt
if convention == BusinessDayConvention.FOLLOWING:
return self._next_business_day(dt)
elif convention == BusinessDayConvention.PRECEDING:
return self._prev_business_day(dt)
elif convention == BusinessDayConvention.MODIFIED_FOLLOWING:
next_day = self._next_business_day(dt)
if next_day.month != dt.month:
return self._prev_business_day(dt)
return next_day
elif convention == BusinessDayConvention.MODIFIED_PRECEDING:
prev_day = self._prev_business_day(dt)
if prev_day.month != dt.month:
return self._next_business_day(dt)
return prev_day
return dt
def _next_business_day(self, dt: date) -> date:
"""获取下一个工作日"""
next_dt = dt + timedelta(days=1)
while not self.is_business_day(next_dt):
next_dt += timedelta(days=1)
return next_dt
def _prev_business_day(self, dt: date) -> date:
"""获取上一个工作日"""
prev_dt = dt - timedelta(days=1)
while not self.is_business_day(prev_dt):
prev_dt -= timedelta(days=1)
return prev_dt
def business_days_between(self, start: date, end: date) -> List[date]:
"""
获取两个日期之间的所有工作日
Args:
start: 开始日期
end: 结束日期
Returns:
工作日列表
"""
if start > end:
start, end = end, start
days = []
current = start
while current <= end:
if self.is_business_day(current):
days.append(current)
current += timedelta(days=1)
return days
def n_business_days(self, start: date, end: date) -> int:
"""计算工作日数量"""
return len(self.business_days_between(start, end))
class UnitedStatesCalendar(TradingCalendar):
"""美国交易日历"""
def __init__(self, market: str = "NYSE"):
super().__init__(f"US-{market}")
self.market = market
# 周末是周六(5)和周日(6)
self._weekend_days = {5, 6}
# 常见美国节假日(需要每年更新)
self._setup_holidays()
def _setup_holidays(self) -> None:
"""设置美国节假日"""
# 新年
# 马丁路德金日
# 总统日
# 阵亡将士纪念日
# 独立日
# 劳动节
# 哥伦布日
# 退伍军人日
# 感恩节
# 圣诞节
pass
class ChinaCalendar(TradingCalendar):
"""中国交易日历"""
def __init__(self):
super().__init__("CN")
# 周末是周六(5)和周日(6)
self._weekend_days = {5, 6}
# 中国节假日(春节、清明、劳动节、端午、中秋、国庆)
self._setup_holidays()
def _setup_holidays(self) -> None:
"""设置中国节假日"""
# 需要每年更新
pass
```bash
### 4. 可重链接句柄设计
```python
# backtrader/handle/base.py
from typing import Optional, Callable
from weakref import WeakRef
class Handle:
"""
可重链接句柄
允许动态替换底层对象而不需要更新所有引用
"""
def __init__(self, target=None):
"""
初始化句柄
Args:
target: 目标对象
"""
self._target = target
self._observers: List[Callable] = []
def relink(self, new_target) -> None:
"""
重链接到新目标
Args:
new_target: 新的目标对象
"""
old_target = self._target
self._target = new_target
# 通知观察者
for observer in self._observers:
observer(old_target, new_target)
def get(self):
"""获取当前目标"""
return self._target
def __call__(self):
"""使句柄可调用"""
return self._target
def subscribe(self, callback: Callable) -> None:
"""
订阅重链接事件
Args:
callback: 回调函数,接收(old_target, new_target)
"""
self._observers.append(callback)
def unsubscribe(self, callback: Callable) -> None:
"""取消订阅"""
if callback in self._observers:
self._observers.remove(callback)
@property
def is_empty(self) -> bool:
"""是否为空句柄"""
return self._target is None
class StrategyHandle(Handle):
"""
策略句柄
允许在运行时动态替换策略
"""
def __init__(self, strategy=None):
super().__init__(strategy)
self._active_params = {}
def relink(self, new_strategy) -> None:
"""重链接到新策略"""
# 保存当前参数
if self._target is not None:
self._active_params = self._target.params._getpairs()
else:
self._active_params = {}
super().relink(new_strategy)
# 恢复参数到新策略
if new_strategy is not None:
for key, value in self._active_params.items():
setattr(new_strategy.params, key, value)
def update_params(self, **params) -> None:
"""更新策略参数"""
if self._target is not None:
for key, value in params.items():
setattr(self._target.params, key, value)
self._active_params.update(params)
# 触发策略重新计算
self._target.params recalculated = True
@property
def strategy(self):
"""获取当前策略"""
return self._target
```bash
### 5. Excel 集成设计
```python
# backtrader/excel/utils.py
from datetime import datetime, date
import pandas as pd
class ExcelDateConverter:
"""
Excel 日期转换器
支持 Excel 日期序列号与 Python 日期互转
"""
# Excel 基准日期: 1900 年 1 月 1 日 (实际上 Excel 错误地认为 1900 是闰年)
EXCEL_EPOCH = date(1899, 12, 30)
SECONDS_PER_DAY = 86400
@classmethod
def date_to_excel(cls, dt: date) -> float:
"""
转换日期为 Excel 日期序列号
Args:
dt: Python 日期
Returns:
Excel 日期序列号
Excel 日期序列号是从 1899-12-30 开始计算的天数
"""
if isinstance(dt, datetime):
dt = dt.date()
delta = dt - cls.EXCEL_EPOCH
return float(delta.days)
@classmethod
def excel_to_date(cls, excel_date: float) -> date:
"""
转换 Excel 日期序列号为 Python 日期
Args:
excel_date: Excel 日期序列号
Returns:
Python 日期
"""
return cls.EXCEL_EPOCH + timedelta(days=int(excel_date))
@classmethod
def datetime_to_excel(cls, dt: datetime) -> float:
"""
转换日期时间为 Excel 日期序列号
Args:
dt: Python 日期时间
Returns:
Excel 日期序列号(带小数部分表示时间)
"""
date_part = cls.date_to_excel(dt.date())
time_part = (dt.hour *3600 + dt.minute*60 + dt.second) / cls.SECONDS_PER_DAY
return date_part + time_part
@classmethod
def excel_to_datetime(cls, excel_date: float) -> datetime:
"""
转换 Excel 日期序列号为 Python 日期时间
Args:
excel_date: Excel 日期序列号
Returns:
Python 日期时间
"""
date_part = int(excel_date)
time_part = excel_date - date_part
base_date = cls.excel_to_date(date_part)
seconds = int(time_part* cls.SECONDS_PER_DAY)
hour = seconds // 3600
minute = (seconds % 3600) // 60
second = seconds % 60
return datetime.combine(base_date, datetime.min.time()) + timedelta(
hours=hour, minutes=minute, seconds=second
)
@classmethod
def datetime_to_sql(cls, dt: datetime, microseconds: bool = False) -> str:
"""
转换为 SQL 日期时间格式
Args:
dt: 日期时间
microseconds: 是否包含微秒
Returns:
SQL 格式的日期时间字符串
"""
if microseconds:
return dt.strftime("%Y-%m-%d %H:%M:%S.%f")
else:
return dt.strftime("%Y-%m-%d %H:%M:%S")
@classmethod
def sql_to_datetime(cls, sql_str: str) -> datetime:
"""
从 SQL 格式解析日期时间
Args:
sql_str: SQL 格式的日期时间字符串
Returns:
Python 日期时间
"""
# 尝试多种格式
formats = [
"%Y-%m-%d %H:%M:%S.%f",
"%Y-%m-%d %H:%M:%S",
"%Y-%m-%d",
"%Y-%m-%dT%H:%M:%S",
"%Y-%m-%dT%H:%M:%S.%f",
]
for fmt in formats:
try:
return datetime.strptime(sql_str, fmt)
except ValueError:
continue
raise ValueError(f"无法解析日期时间: {sql_str}")
class ExcelExporter:
"""
Excel 导出器
将 backtrader 结果导出为 Excel 格式
"""
def __init__(self, cerebro):
self.cerebro = cerebro
self.converter = ExcelDateConverter()
def export_to_dataframe(self) -> pd.DataFrame:
"""
导出为 DataFrame
Returns:
包含策略结果的数据框
"""
strategies = self.cerebro.runstrats
if not strategies:
return pd.DataFrame()
strat = strategies[0]
# 获取时间序列数据
data = {}
# 获取日期数据
dates = list(strat.datas[0].datetime.date())
data['date'] = dates
data['excel_date'] = [self.converter.date_to_excel(d) for d in dates]
# 获取净值曲线
if hasattr(strat, 'stats'):
data['value'] = strat.stats.broker.getvalue()
return pd.DataFrame(data)
def export_to_excel(self, filename: str) -> None:
"""
导出到 Excel 文件
Args:
filename: 输出文件名
"""
try:
import openpyxl
from openpyxl.utils.dataframe import dataframe_to_rows
except ImportError:
raise ImportError("需要安装 openpyxl: pip install openpyxl")
df = self.export_to_dataframe()
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "Strategy Results"
# 写入数据
for row in dataframe_to_rows(df, index=False, header=True):
ws.append(row)
wb.save(filename)
@staticmethod
def export_pandas_to_excel(df: pd.DataFrame, filename: str, sheet_name: str = "Sheet1") -> None:
"""
导出 DataFrame 到 Excel
Args:
df: 数据框
filename: 文件名
sheet_name: 工作表名
"""
try:
import openpyxl
except ImportError:
raise ImportError("需要安装 openpyxl: pip install openpyxl")
wb = openpyxl.Workbook()
ws = wb.active
ws.title = sheet_name
# 写入标题
for col_idx, column in enumerate(df.columns, 1):
ws.cell(row=1, column=col_idx, value=column)
# 写入数据
for row_idx, (_, row) in enumerate(df.iterrows(), 2):
for col_idx, value in enumerate(row, 1):
ws.cell(row=row_idx, column=col_idx, value=value)
wb.save(filename)
```bash
### 6. 使用示例
```python
import backtrader as bt
from backtrader.observer.base import Observer, Observable
from backtrader.lazy.base import LazyIndicator
from backtrader.calendar.base import TradingCalendar, BusinessDayConvention, UnitedStatesCalendar
# 1. 使用观察者模式
class MyObserver(Observer):
def update(self, observable, event=None):
print(f"收到更新通知: {observable.__class__.__name__}, 事件: {event}")
class MyStrategy(bt.Strategy, Observer):
def __init__(self):
super().__init__()
# 观察数据源
self.data = self.datas[0]
self.data.register_with(self)
def next(self):
# 正常策略逻辑
if len(self.data) > 20:
if self.data.close[0] > self.data.close[-1]:
self.buy()
def update(self, observable, event=None):
# 接收数据更新通知
if event and event.get('type') == 'advance':
print(f"数据更新到: {event['to']}")
# 2. 使用惰性计算
class LazySMA(bt.Indicator):
_forcecalculate = False
def __init__(self, data, period):
super().__init__()
self.data = data
self.period = period
self.lazy_calc = LazyIndicator(self)
def next(self):
# 只有在需要时才重新计算
if self.lazy_calc.is_stale():
self.lazy_calc.unfreeze()
result = self.lazy_calc.calculate()
self.line[0] = result[-1] if result else 0
else:
# 使用缓存值
pass
# 3. 使用交易日历
cerebro = bt.Cerebro()
# 设置美国交易日历
us_calendar = UnitedStatesCalendar(market="NYSE")
# 检查日期是否是工作日
test_date = date(2024, 1, 15)
print(f"是否工作日: {us_calendar.is_business_day(test_date)}")
# 调整到下一个工作日
adjusted = us_calendar.adjust(test_date, BusinessDayConvention.FOLLOWING)
print(f"调整后日期: {adjusted}")
# 4. 使用 Excel 导出
cerebro.addstrategy(MyStrategy)
cerebro.run()
# 导出到 Excel
from backtrader.excel.utils import ExcelExporter
exporter = ExcelExporter(cerebro)
exporter.export_to_excel("strategy_results.xlsx")
```bash
### 7. 实施路线图
#### 阶段 1: 观察者模式 (1-2 周)
- [ ] 实现 Observable 基类
- [ ] 实现 Observer 接口
- [ ] 在 LineSeries 中集成通知
- [ ] 单元测试
#### 阶段 2: 惰性计算 (1-2 周)
- [ ] 实现 LazyObject 基类
- [ ] 实现 LazyIndicator
- [ ] 缓存失效机制
- [ ] 性能测试
#### 阶段 3: 交易日历 (2 周)
- [ ] 实现 TradingCalendar 基类
- [ ] 实现 UnitedStatesCalendar
- [ ] 实现 ChinaCalendar
- [ ] 集成到 Cerebro
#### 阶段 4: 句柄系统 (1 周)
- [ ] 实现 Handle 基类
- [ ] 实现 StrategyHandle
- [ ] 参数管理
- [ ] 单元测试
#### 阶段 5: Excel 集成 (1 周)
- [ ] 实现 Excel 日期转换
- [ ] 实现 ExcelExporter
- [ ] 文档和示例
#### 阶段 6: 集成测试 (1 周)
- [ ] 端到端测试
- [ ] 性能对比
- [ ] 文档完善
- --
## 附录: 关键文件路径
### Backtrader 关键文件
- `cerebro.py`: 核心引擎
- `strategy.py`: Strategy 基类
- `lineseries.py`: LineSeries 基类
- `lineiterator.py`: LineIterator 基类
- `utils/`: 工具函数
### thOth 关键文件
- `thOth/pattern/observable.hpp`: 被观察对象
- `thOth/pattern/observer.hpp`: 观察者接口
- `thOth/pattern/lazyObject.hpp`: 惰性对象
- `thOth/time/calendar.hpp`: 日历基类
- `thOth/time/calendars/unitedStates.hpp`: 美国日历
- `thOth/time/timeSeries.hpp`: 时间序列
- `thOth/time/dateTime.hpp`: 日期时间