qka 1.6.5.dev5__tar.gz → 1.7.1.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/PKG-INFO +1 -1
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/core/data.py +60 -23
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/.github/workflows/docs.yml +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/.github/workflows/release.yml +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/.gitignore +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/.vscode/settings.json +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/CHANGELOG.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/LICENSE +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/README.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/advanced/performance.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/api/brokers.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/api/core.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/api/utils.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/examples/buy_and_hold.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/examples/ma_cross.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/examples/momentum.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/examples/multi_factor.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/examples/rsi_atr.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/guides/backtest.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/guides/data.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/guides/indicators.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/guides/report.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/guides/sizing.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/guides/strategy.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/guides/trading.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/index.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/docs/user-guide/trading.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/mkdocs.yml +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/pyproject.toml +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/__init__.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/brokers/__init__.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/brokers/client.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/brokers/server.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/brokers/trade.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/cli.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/core/__init__.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/core/accessor.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/core/backtest.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/core/broker.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/core/report.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/core/sizing.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/core/strategy.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/mcp/__init__.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/mcp/api.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/mcp/server.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/server/__init__.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/server/handlers/__init__.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/server/handlers/class_inspector_handler.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/server/handlers/code_executor_handler.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/server/ws_client.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/server/zmq_server.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/utils/__init__.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/utils/anis.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/utils/logger.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/qka/utils/util.py +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/skills/qka/SKILL.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/skills/qka/references/backtest.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/skills/qka/references/broker.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/skills/qka/references/data.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/skills/qka/references/report.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/skills/qka/references/sizing.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/skills/qka/references/strategy.md +0 -0
- {qka-1.6.5.dev5 → qka-1.7.1.dev2}/tools/generate_api_ref.py +0 -0
|
@@ -108,32 +108,63 @@ class Data():
|
|
|
108
108
|
|
|
109
109
|
def _download(self, symbol: str) -> Path:
|
|
110
110
|
"""
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
下载或更新单个股票的数据。
|
|
112
|
+
|
|
113
|
+
首次下载全量数据。已存在时只增量拉取最新数据(从 parquet 最后日期到今日),
|
|
114
|
+
追加合并后重新写入,确保缓存始终保持最新。
|
|
115
|
+
|
|
113
116
|
Args:
|
|
114
|
-
symbol
|
|
115
|
-
|
|
117
|
+
symbol: 股票代码
|
|
118
|
+
|
|
116
119
|
Returns:
|
|
117
120
|
Path: 数据文件路径
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
RuntimeError: 数据源返回空数据(首次下载时)
|
|
118
124
|
"""
|
|
119
125
|
path = self.target_dir / f"{symbol}.parquet"
|
|
120
126
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
127
|
+
# ── 首次下载:全量 ──
|
|
128
|
+
if not path.exists():
|
|
129
|
+
if self.source == 'akshare':
|
|
130
|
+
df = self._get_from_akshare(symbol)
|
|
131
|
+
elif self.source == 'baostock':
|
|
132
|
+
df = self._get_from_baostock(symbol)
|
|
133
|
+
else:
|
|
134
|
+
df = pd.DataFrame()
|
|
135
|
+
|
|
136
|
+
if len(df) == 0:
|
|
137
|
+
raise RuntimeError(f"{symbol}: baostock 返回空数据")
|
|
138
|
+
table = pa.Table.from_pandas(df)
|
|
139
|
+
pq.write_table(table, path)
|
|
140
|
+
return path
|
|
141
|
+
|
|
142
|
+
# ── 增量更新 ──
|
|
143
|
+
if self.source != 'baostock':
|
|
144
|
+
return path # 仅 baostock 支持增量
|
|
145
|
+
|
|
146
|
+
# 读已有数据的最后日期
|
|
147
|
+
existing = pd.read_parquet(path)
|
|
148
|
+
if not isinstance(existing.index, pd.DatetimeIndex):
|
|
149
|
+
return path # 非日期索引,跳过增量(保持原文件不变)
|
|
150
|
+
last_date = existing.index.max()
|
|
151
|
+
today_str = pd.Timestamp.now().strftime("%Y-%m-%d")
|
|
152
|
+
next_date = (last_date + pd.Timedelta(days=1)).strftime("%Y-%m-%d")
|
|
153
|
+
|
|
154
|
+
if next_date > today_str:
|
|
155
|
+
return path # 已是最新
|
|
156
|
+
|
|
157
|
+
df_new = self._get_from_baostock(symbol, start_date=next_date)
|
|
158
|
+
if len(df_new) == 0:
|
|
159
|
+
return path # 没有新数据(交易日还未到)
|
|
160
|
+
|
|
161
|
+
# 合并去重
|
|
162
|
+
combined = pd.concat([existing, df_new])
|
|
163
|
+
combined = combined[~combined.index.duplicated(keep='last')]
|
|
164
|
+
combined = combined.sort_index()
|
|
165
|
+
|
|
166
|
+
table = pa.Table.from_pandas(combined)
|
|
135
167
|
pq.write_table(table, path)
|
|
136
|
-
|
|
137
168
|
return path
|
|
138
169
|
|
|
139
170
|
def get(self, lazy: bool = False):
|
|
@@ -459,12 +490,18 @@ class Data():
|
|
|
459
490
|
# 设置索引
|
|
460
491
|
return df
|
|
461
492
|
|
|
462
|
-
def _get_from_baostock(
|
|
493
|
+
def _get_from_baostock(
|
|
494
|
+
self, symbol: str,
|
|
495
|
+
start_date: str = '1990-01-01',
|
|
496
|
+
end_date: str = '2050-12-31',
|
|
497
|
+
) -> pd.DataFrame:
|
|
463
498
|
"""
|
|
464
499
|
从 baostock 获取单个股票的数据。
|
|
465
500
|
|
|
466
501
|
Args:
|
|
467
|
-
symbol
|
|
502
|
+
symbol: 股票代码,支持带后缀如 000001.SZ 或 600000.SH
|
|
503
|
+
start_date: 起始日期,格式 YYYY-MM-DD,默认 1990-01-01
|
|
504
|
+
end_date: 截止日期,格式 YYYY-MM-DD,默认 2050-12-31
|
|
468
505
|
|
|
469
506
|
Returns:
|
|
470
507
|
pd.DataFrame: 股票数据,以 date 为索引,包含 open, high, low, close, volume, amount 列
|
|
@@ -481,8 +518,8 @@ class Data():
|
|
|
481
518
|
rs = bs.query_history_k_data_plus(
|
|
482
519
|
bs_code,
|
|
483
520
|
"date,open,high,low,close,volume,amount",
|
|
484
|
-
start_date=
|
|
485
|
-
end_date=
|
|
521
|
+
start_date=start_date,
|
|
522
|
+
end_date=end_date,
|
|
486
523
|
frequency='d',
|
|
487
524
|
adjustflag=adjustflag,
|
|
488
525
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|