create-dwy 0.9.1 → 0.11.0
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.
- package/package.json +3 -2
- package/src/create.js +5 -2
- package/src/prompts/grouped-checkbox.js +223 -0
- package/src/sync.js +45 -19
- package/templates/claude-global/hooks/pre-git-commit-sensitive-check.sh +0 -0
- package/templates/claude-global/rules/dwy-android-core.md +1 -0
- package/templates/claude-global/rules/dwy-db-migration.md +1 -0
- package/templates/claude-global/rules/dwy-flutter-core.md +1 -0
- package/templates/claude-global/rules/dwy-flutter-network.md +1 -0
- package/templates/claude-global/rules/dwy-flutter-router.md +1 -0
- package/templates/claude-global/rules/dwy-flutter-testing.md +1 -0
- package/templates/claude-global/rules/dwy-postgres.md +1 -0
- package/templates/claude-global/rules/dwy-python-backend.md +44 -3
- package/templates/claude-global/rules/dwy-python-core.md +36 -2
- package/templates/claude-global/rules/dwy-python-testing.md +1 -0
- package/templates/claude-global/rules/dwy-redis.md +1 -0
- package/templates/claude-global/rules/dwy-tdd-development.md +1 -0
- package/templates/claude-global/rules/dwy-vue-core.md +3 -43
- package/templates/claude-global/rules/dwy-vue-pinia.md +1 -0
- package/templates/claude-global/rules/dwy-vue-testing.md +1 -0
- package/templates/claude-global/skills/dwy-cibuildwheel/SKILL.md +355 -0
- package/templates/claude-global/skills/dwy-cibuildwheel/references/release-workflow-template.yml +99 -0
- package/templates/claude-global/skills/dwy-deploy-audit/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_base.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_capacity.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_db.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_docker.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_env.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_https.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_logs.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_resilience.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_secrets.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_services.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_ssh.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/run_all.sh +0 -0
- package/templates/claude-global/skills/dwy-dev-qa/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-docker/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-docker/scripts/query_dockerhub.py +0 -0
- package/templates/claude-global/skills/dwy-docker/scripts/scan_images.sh +0 -0
- package/templates/claude-global/skills/dwy-dolphindb/SKILL.md +123 -76
- package/templates/claude-global/skills/dwy-dolphindb/references/create-database-table.md +219 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/crud-operations.md +281 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/data-import.md +271 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/docker-deployment.md +63 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/performance-tuning.md +223 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/pitfalls-and-best-practices.md +352 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/python-api.md +306 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/setup-and-connection.md +143 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/sources.md +46 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/sql-batch-compute.md +206 -0
- package/templates/claude-global/skills/dwy-eapi/SKILL.md +134 -402
- package/templates/claude-global/skills/dwy-ekit/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-eui/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +14 -7
- package/templates/claude-global/skills/dwy-git-commit/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-mirror-source/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/apply_mirrors.py +0 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/check_mirrors.py +0 -0
- package/templates/claude-global/skills/dwy-pentest/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-publish/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-tech-stack/SKILL.md +1 -0
- package/templates/project/fullstack-monorepo/CLAUDE.md.hbs +21 -9
- package/templates/project/fullstack-monorepo/backend/Dockerfile.prod +40 -0
- package/templates/project/fullstack-monorepo/backend/_env.example.hbs +26 -0
- package/templates/project/fullstack-monorepo/docker-compose.dev.yml.hbs +4 -4
- package/templates/project/fullstack-monorepo/docker-compose.prod.yml.hbs +116 -0
- package/templates/project/fullstack-monorepo/docs/.gitkeep +0 -0
- package/templates/project/fullstack-monorepo/frontend/_env.example.hbs +19 -0
- package/templates/project/fullstack-monorepo/{dev.sh.hbs → scripts/dev.sh.hbs} +2 -2
- package/templates/claude-global/skills/dwy-dolphindb/evals/evals.json +0 -67
- package/templates/claude-global/skills/dwy-dolphindb/references/dolphindb-rules.md +0 -441
- package/templates/claude-global/skills/dwy-eapi/references/health.md +0 -47
- package/templates/claude-global/skills/dwy-eapi/references/logger.md +0 -60
- package/templates/project/fullstack-monorepo/_env.example.hbs +0 -24
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# Python SDK 详解(dolphindb 3.0.4)
|
|
2
|
+
|
|
3
|
+
## 安装与版本
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install dolphindb==3.0.4
|
|
7
|
+
python -c "import dolphindb as ddb; print(ddb.__version__)"
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
最低 Python 3.7+。底层 C++ via Pybind11,多线程不受 GIL。
|
|
11
|
+
|
|
12
|
+
## session.run 全用法
|
|
13
|
+
|
|
14
|
+
### 执行脚本
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
# 单语句
|
|
18
|
+
s.run("x = 1")
|
|
19
|
+
|
|
20
|
+
# 多语句(用 ; 分隔,最后一句的值为返回值)
|
|
21
|
+
result = s.run("x=1; y=2; x+y") # 返回 3
|
|
22
|
+
|
|
23
|
+
# 多行字符串
|
|
24
|
+
s.run("""
|
|
25
|
+
t = table(1..10 as a, rand(100,10) as b)
|
|
26
|
+
select * from t where a > 5
|
|
27
|
+
""")
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 调用 DDB 函数(参数传递)
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
# 方式 1:所有参数在服务端
|
|
34
|
+
s.run("x=[1,3,5]; y=[2,4,6]")
|
|
35
|
+
s.run("add(x, y)") # array([3, 7, 11])
|
|
36
|
+
|
|
37
|
+
# 方式 2:部分应用(curry)
|
|
38
|
+
import numpy as np
|
|
39
|
+
y = np.array([1, 2, 3])
|
|
40
|
+
s.run("add{x,}", y) # 等价 add(x, y),y 从客户端传
|
|
41
|
+
|
|
42
|
+
# 方式 3:两参数都从客户端
|
|
43
|
+
x = np.array([1.5, 2.5, 7])
|
|
44
|
+
y = np.array([8.5, 7.5, 3])
|
|
45
|
+
s.run("add", x, y) # array([10., 10., 10.])
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 高级参数
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
s.run("expensiveQuery()",
|
|
52
|
+
clearMemory=True, # 跑完清理中间变量,防内存堆积
|
|
53
|
+
priority=7, # 0-9,越大越优先(默认 4)
|
|
54
|
+
parallelism=2, # 限并发,社区版 4 worker 别超 4
|
|
55
|
+
fetchSize=8192, # 大结果分段拉
|
|
56
|
+
disableDecimal=False, # True 时 DECIMAL 转 str(精度优先)
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 跑文件
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
# test.dos 是 DDB 脚本文件
|
|
64
|
+
s.runFile("./scripts/calc_factor.dos")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## upload:Python → DDB 变量
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import pandas as pd
|
|
71
|
+
|
|
72
|
+
s.upload({
|
|
73
|
+
'a': 8, # int
|
|
74
|
+
'b': "abc", # str
|
|
75
|
+
'c': {'k1': 1, 'k2': 2}, # dict → DDB dictionary
|
|
76
|
+
'arr': np.array([1, 2, 3]), # ndarray → vector
|
|
77
|
+
'df': pd.DataFrame({'x': [1,2,3]}), # DataFrame → table
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
s.run("typestr(a)") # 'INT'
|
|
81
|
+
s.run("typestr(df)") # 'TABLE'
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**注意**:upload 的变量在 session 关闭后丢失。要持久化得写进 DFS 表。
|
|
85
|
+
|
|
86
|
+
## SDK v3 异步 API(**踩坑高发**)
|
|
87
|
+
|
|
88
|
+
| 调用 | 是否 async | 用法 |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `DBConnectionPool.run()` | ✅ **原生 async** | `await pool.run(script)` |
|
|
91
|
+
| `Session.run()` | ❌ 同步 | `await asyncio.to_thread(s.run, script)` |
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
# ❌ 错:pool.run 已经是协程,再包 to_thread 会产生 "coroutine never awaited" 警告
|
|
95
|
+
await asyncio.to_thread(pool.run, script)
|
|
96
|
+
|
|
97
|
+
# ✅ 对:pool.run 直接 await
|
|
98
|
+
await pool.run(script)
|
|
99
|
+
|
|
100
|
+
# ✅ 对:Session.run 是同步的,需要 to_thread
|
|
101
|
+
await asyncio.to_thread(s.run, script)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 数据类型映射
|
|
105
|
+
|
|
106
|
+
### Python → DolphinDB
|
|
107
|
+
|
|
108
|
+
| Python/Pandas | DolphinDB | 转换方式 |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| `int` (32 bit) | INT | — |
|
|
111
|
+
| `int` (64 bit) | LONG | — |
|
|
112
|
+
| `float` | DOUBLE | — |
|
|
113
|
+
| `str` | STRING | — |
|
|
114
|
+
| `datetime.date` | DATE | — |
|
|
115
|
+
| `datetime.datetime` | DATETIME / TIMESTAMP(按精度) | — |
|
|
116
|
+
| `numpy.ndarray` | VECTOR | — |
|
|
117
|
+
| `pandas.DataFrame` | TABLE | — |
|
|
118
|
+
| `dict` | DICTIONARY | — |
|
|
119
|
+
| `list of mixed` | ANY VECTOR | — |
|
|
120
|
+
| `uint8/16/32/64`(DDB 不接) | INT/LONG | `.astype('int64')` 显式转 |
|
|
121
|
+
| `float64` 含 NaN → 整数 | INT/LONG | `.fillna(0).astype('int64')` |
|
|
122
|
+
| `uint32`(日期如 20191205) | DATE | `pd.to_datetime(str(x), format='%Y%m%d')` |
|
|
123
|
+
| `uint32`(时间如 91509000) | INT | `.astype('int64')` |
|
|
124
|
+
| `object`(字符串) | SYMBOL | `.astype(str)` |
|
|
125
|
+
|
|
126
|
+
**硬性约束**:
|
|
127
|
+
- DDB **不接受 uint 类型**,所有 `uint8/16/32/64` 必须先 `.astype('int64')`,否则报错
|
|
128
|
+
- `NaN` **不能直接转整数**,必须先 `fillna(0)` 否则报 cast error
|
|
129
|
+
- `SYMBOL` 是字典编码字符串,**单值上限 255 字节**(详见 [[pitfalls-and-best-practices]] SYMBOL 长度)
|
|
130
|
+
|
|
131
|
+
### 精度差异
|
|
132
|
+
|
|
133
|
+
- pandas `datetime64[ns]` → DDB `NANOTIMESTAMP`
|
|
134
|
+
- pandas `datetime64[ms]` → DDB `TIMESTAMP`
|
|
135
|
+
- Python `Decimal` → 转 DECIMAL 时要明确 scale,否则用 `disableDecimal=True` 拿字符串
|
|
136
|
+
|
|
137
|
+
## 拉数据:DDB → Python
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
# Table → DataFrame
|
|
141
|
+
table = s.loadTable(tableName='snapshot', dbPath='dfs://stock_lv2_snapshot')
|
|
142
|
+
df = table.toDF()
|
|
143
|
+
|
|
144
|
+
print(table.rows) # 行数
|
|
145
|
+
print(table.cols) # 列数
|
|
146
|
+
print(table.schema) # schema(DataFrame 格式)
|
|
147
|
+
|
|
148
|
+
# 直接 SQL → DataFrame
|
|
149
|
+
df = s.run("select * from loadTable('dfs://x','t') where TradeDate=2024.01.03")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## 大结果分段拉取
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
block = s.run(
|
|
156
|
+
"select * from loadTable('dfs://stock_lv2_snapshot','snapshot') where TradeDate=2024.01.03",
|
|
157
|
+
fetchSize=8192,
|
|
158
|
+
)
|
|
159
|
+
total = 0
|
|
160
|
+
while block.hasNext():
|
|
161
|
+
chunk = block.read() # pd.DataFrame
|
|
162
|
+
total += len(chunk)
|
|
163
|
+
process(chunk)
|
|
164
|
+
# 提前结束要 skipAll,否则连接阻塞
|
|
165
|
+
# block.skipAll()
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## DBConnectionPool 并发
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
import asyncio
|
|
172
|
+
|
|
173
|
+
pool = ddb.DBConnectionPool(
|
|
174
|
+
"localhost", 8848,
|
|
175
|
+
threadNum=3, # 4 worker → pool size 3
|
|
176
|
+
userId="admin", password="123456",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
async def query_one(sid):
|
|
180
|
+
return await pool.run(f"""
|
|
181
|
+
select avg(LastPrice) from loadTable('dfs://k','k_minute')
|
|
182
|
+
where SecurityID=`{sid} and TradeDate=2024.01.03
|
|
183
|
+
""")
|
|
184
|
+
|
|
185
|
+
async def main():
|
|
186
|
+
sids = ['600000', '600036', '600519']
|
|
187
|
+
results = await asyncio.gather(*[query_one(s) for s in sids])
|
|
188
|
+
return results
|
|
189
|
+
|
|
190
|
+
results = asyncio.run(main())
|
|
191
|
+
pool.shutDown()
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## 流数据订阅
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
import dolphindb as ddb
|
|
198
|
+
|
|
199
|
+
s = ddb.session()
|
|
200
|
+
# 开本地接收端(必须先 enable)
|
|
201
|
+
s.enableStreaming(local_port=8000) # 端口要和服务端 publish 端口区分
|
|
202
|
+
s.connect("localhost", 8848, "admin", "123456")
|
|
203
|
+
|
|
204
|
+
# handler:每条/每批数据回调
|
|
205
|
+
def on_data(msg):
|
|
206
|
+
# msg 是 list(msgAsTable=False)或 DataFrame(msgAsTable=True)
|
|
207
|
+
print(f"received {len(msg)} rows")
|
|
208
|
+
process(msg)
|
|
209
|
+
|
|
210
|
+
# 订阅服务端流表
|
|
211
|
+
s.subscribe(
|
|
212
|
+
host="localhost", port=8848,
|
|
213
|
+
handler=on_data,
|
|
214
|
+
tableName="trades_stream", # 服务端 streamTable
|
|
215
|
+
actionName="myAction", # 订阅 ID(同表多订阅必须不同)
|
|
216
|
+
offset=-1, # -1=最新, 0=从头, N=从第 N 条
|
|
217
|
+
resub=True, # 断线重连
|
|
218
|
+
filter=None, # 过滤向量
|
|
219
|
+
msgAsTable=True, # True=每批 DataFrame, False=每条 list
|
|
220
|
+
batchSize=1000, # 攒够 1000 条触发 handler
|
|
221
|
+
throttle=1, # 或最多攒 1 秒
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# 看当前订阅
|
|
225
|
+
print(s.getSubscriptionTopics())
|
|
226
|
+
|
|
227
|
+
# 取消
|
|
228
|
+
s.unsubscribe(host="localhost", port=8848,
|
|
229
|
+
tableName="trades_stream", actionName="myAction")
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**坑**:
|
|
233
|
+
- `enableStreaming` 的 `local_port` 不能和 DDB 服务端口冲突
|
|
234
|
+
- handler 抛异常**默默吃掉**,必须自己 try/except + log
|
|
235
|
+
- `batchSize + throttle` 是 OR 关系,先到先发
|
|
236
|
+
|
|
237
|
+
## 异步提交(不阻塞客户端)
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
# Session 异步模式(fire-and-forget)
|
|
241
|
+
s.enableAsyncMode()
|
|
242
|
+
s.run("tableInsert{loadTable('dfs://k','k_minute')}", df)
|
|
243
|
+
# 立即返回,写入在服务端后台跑,**没有返回值,错误也不抛**
|
|
244
|
+
s.disableAsyncMode()
|
|
245
|
+
|
|
246
|
+
# 适用:高频写入,对回执不敏感
|
|
247
|
+
# 不适用:要确认成功、要拿结果
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## 错误处理与重连
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
import dolphindb as ddb
|
|
254
|
+
import dolphindb.settings as keys
|
|
255
|
+
|
|
256
|
+
s = ddb.session()
|
|
257
|
+
try:
|
|
258
|
+
s.connect("localhost", 8848, "admin", "123456",
|
|
259
|
+
reconnect=True, tryReconnectNums=5,
|
|
260
|
+
keepAliveTime=120)
|
|
261
|
+
result = s.run("select count(*) from loadTable('dfs://x','t')")
|
|
262
|
+
except RuntimeError as e:
|
|
263
|
+
# DDB 服务端报错都是 RuntimeError
|
|
264
|
+
print(f"DDB error: {e}")
|
|
265
|
+
# 常见消息:
|
|
266
|
+
# "Out of memory" → 见 performance-tuning
|
|
267
|
+
# "Failed to read response" → 网络/服务端崩溃
|
|
268
|
+
# "Couldn't send script" → keepAlive 超时
|
|
269
|
+
finally:
|
|
270
|
+
s.close()
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## 关闭与资源回收
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
# Session:必须显式关
|
|
277
|
+
s.close()
|
|
278
|
+
|
|
279
|
+
# 连接池:必须 shutDown
|
|
280
|
+
pool.shutDown()
|
|
281
|
+
|
|
282
|
+
# 流订阅:unsubscribe 后销毁 session
|
|
283
|
+
s.unsubscribe(...)
|
|
284
|
+
s.close()
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**长期运行的服务**:用 context manager 包一层(DDB SDK 没原生支持,自己写):
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
from contextlib import contextmanager
|
|
291
|
+
|
|
292
|
+
@contextmanager
|
|
293
|
+
def ddb_session(host="localhost", port=8848):
|
|
294
|
+
s = ddb.session()
|
|
295
|
+
s.connect(host, port, "admin", "123456",
|
|
296
|
+
keepAliveTime=120, reconnect=True)
|
|
297
|
+
try:
|
|
298
|
+
yield s
|
|
299
|
+
finally:
|
|
300
|
+
s.close()
|
|
301
|
+
|
|
302
|
+
with ddb_session() as s:
|
|
303
|
+
df = s.run("select count(*) from loadTable('dfs://x','t')")
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
跨引用:写入对比 → [[data-import]],性能调优 → [[performance-tuning]],连接配置 → [[setup-and-connection]]。
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# 连接与基础设置
|
|
2
|
+
|
|
3
|
+
## 集群拓扑
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
controller (8848) ←─ Web UI / DolphinDB GUI / Python SDK 入口
|
|
7
|
+
│
|
|
8
|
+
├── datanode-1 (8849?) ← 实际存储 + 计算
|
|
9
|
+
└── datanode-2 (8850?) ← 实际存储 + 计算
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
社区版默认 1 控 2 节点。`localhost:8848` 是控制节点的 Web 端,**Python SDK 连这里**,由控制节点路由到数据节点。
|
|
13
|
+
|
|
14
|
+
查实际端口和节点:
|
|
15
|
+
|
|
16
|
+
```dolphindb
|
|
17
|
+
getClusterPerf() // 列出所有节点 name/host/port/mode
|
|
18
|
+
getControllerAlias() // 控制节点别名
|
|
19
|
+
getDataNodes() // 数据节点列表
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Python SDK 安装
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install dolphindb==3.0.4
|
|
26
|
+
# 验证
|
|
27
|
+
python -c "import dolphindb as ddb; print(ddb.__version__)"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 标准连接
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import dolphindb as ddb
|
|
34
|
+
|
|
35
|
+
s = ddb.session()
|
|
36
|
+
s.connect("localhost", 8848, "admin", "123456")
|
|
37
|
+
|
|
38
|
+
# 验证
|
|
39
|
+
print(s.run("getNodeAlias()"))
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 完整 connect 签名
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
connect(host, port,
|
|
46
|
+
userid=None, password=None, startup=None,
|
|
47
|
+
highAvailability=False, highAvailabilitySites=None,
|
|
48
|
+
keepAliveTime=None, reconnect=False,
|
|
49
|
+
*,
|
|
50
|
+
tryReconnectNums=None, readTimeout=None, writeTimeout=None)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 大查询场景(必须调)
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
s.connect(
|
|
57
|
+
"localhost", 8848, "admin", "123456",
|
|
58
|
+
keepAliveTime=120, # 默认 30s,大查询要拉到 ≥120s 否则断连
|
|
59
|
+
reconnect=True,
|
|
60
|
+
tryReconnectNums=5,
|
|
61
|
+
readTimeout=600, # 单位秒,大结果集别用默认
|
|
62
|
+
writeTimeout=600,
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 高可用(连节点池,自动选最低负载)
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
s = ddb.session()
|
|
70
|
+
sites = ["localhost:8848", "192.168.0.107:8848"] # 控制节点列表
|
|
71
|
+
s.connect(
|
|
72
|
+
host="localhost", port=8848,
|
|
73
|
+
userid="admin", password="123456",
|
|
74
|
+
highAvailability=True,
|
|
75
|
+
highAvailabilitySites=sites,
|
|
76
|
+
)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 启动脚本(每次连接自动跑)
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
s.connect(host="localhost", port=8848,
|
|
83
|
+
startup="clearAllCache(); login('admin','123456')")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## 关闭连接
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
s.close()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**重要**:连接不显式关,连接数会被快速吃满(社区版上限 512,当前已用 15)。脚本结束务必 `s.close()`,长服务用 `try/finally`。
|
|
93
|
+
|
|
94
|
+
## DBConnectionPool(并发场景必备)
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
pool = ddb.DBConnectionPool(
|
|
98
|
+
"localhost", 8848, threadNum=3, # 4 worker → pool size 3
|
|
99
|
+
userId="admin", password="123456",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# 并发跑多个 SQL
|
|
103
|
+
import asyncio
|
|
104
|
+
loop = asyncio.get_event_loop()
|
|
105
|
+
tasks = [pool.run(f"select * from loadTable('dfs://k','k_minute') where SecurityID=`{sid}`")
|
|
106
|
+
for sid in ['600000', '600036']]
|
|
107
|
+
results = loop.run_until_complete(asyncio.gather(*tasks))
|
|
108
|
+
|
|
109
|
+
pool.shutDown()
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**社区版 worker = 4**:pool size 取 3(留 1 worker 给 Web/管理)。设到 4+ 会和 web/ops 抢线程。
|
|
113
|
+
|
|
114
|
+
## 登录与权限
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
# 方式 1:connect 时带凭证(推荐)
|
|
118
|
+
s.connect("localhost", 8848, "admin", "123456")
|
|
119
|
+
|
|
120
|
+
# 方式 2:先连后登录
|
|
121
|
+
s.connect("localhost", 8848)
|
|
122
|
+
s.login("admin", "123456")
|
|
123
|
+
|
|
124
|
+
# 改密码
|
|
125
|
+
s.run("changePwd('admin', '新密码')")
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 常见报错
|
|
129
|
+
|
|
130
|
+
| 报错 | 原因 | 处理 |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| `Connection refused` | 节点没起 / 端口不对 | `lsof -i:8848` 看端口;查 `getClusterPerf()` 用控制节点端口 |
|
|
133
|
+
| `Couldn't send script/function...` | `keepAliveTime` 太短,长查询被踢 | 提到 120+ |
|
|
134
|
+
| `The user is not authorized to perform this operation` | 权限不够 | 用 admin 登录或 `grant(...)` |
|
|
135
|
+
| `Out of memory` | 单查询超 2GB | 看 [[performance-tuning]] |
|
|
136
|
+
| `Couldn't connect to the gateway` | 高可用模式所有 site 都挂 | 退回单节点模式排查 |
|
|
137
|
+
| Python 端 hang 住 | 服务端作业卡了 | 另起 session 跑 `getConsoleJobs()` 找 → `cancelConsoleJob()` |
|
|
138
|
+
|
|
139
|
+
## Web UI 入口
|
|
140
|
+
|
|
141
|
+
- 控制台:http://localhost:8848
|
|
142
|
+
- 看作业 / 节点状态 / 配置参数 / 用 Web 编辑器跑脚本
|
|
143
|
+
- 局域网访问用机器 IP:`http://192.168.0.107:8848`
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# 数据源与参考资料
|
|
2
|
+
|
|
3
|
+
本 skill 的所有内容溯源。需要查更新 / 深入细节时直接进官方。
|
|
4
|
+
|
|
5
|
+
## 官方文档(中文)
|
|
6
|
+
|
|
7
|
+
| 资源 | URL | 用途 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| 金融用户入门 | https://docs.dolphindb.cn/zh/tutorials/new_users_finance.html | 建库 / 导入 / SQL 批计算完整范式 |
|
|
10
|
+
| 使用须知 | https://docs.dolphindb.cn/zh/tutorials/usage_guidelines.html | 库表/SQL/语法/运维所有反模式 |
|
|
11
|
+
| 作业管理教程 | https://docs.dolphindb.cn/zh/tutorials/job_management_tutorial.html | getConsoleJobs / cancelJob 等命令 |
|
|
12
|
+
| 函数手册 | https://docs.dolphindb.cn/zh/funcs/ | 所有内置函数签名 |
|
|
13
|
+
|
|
14
|
+
## Python SDK(dolphindb 3.0.4)
|
|
15
|
+
|
|
16
|
+
主入口:https://docs.dolphindb.cn/zh/pydoc/
|
|
17
|
+
|
|
18
|
+
| 章节 | URL |
|
|
19
|
+
|---|---|
|
|
20
|
+
| Session 创建 | https://docs.dolphindb.cn/zh/pydoc/BasicOperations/Session/Constructor.html |
|
|
21
|
+
| Session connect | https://docs.dolphindb.cn/zh/pydoc/BasicOperations/Session/Connect.html |
|
|
22
|
+
| Session 常用方法 | https://docs.dolphindb.cn/zh/pydoc/BasicOperations/Session/OtherParams.html |
|
|
23
|
+
| 异步任务提交 | https://docs.dolphindb.cn/zh/pydoc/BasicOperations/AsyncWrites/SessionAsyncMode.html |
|
|
24
|
+
| 离线安装 | https://docs.dolphindb.cn/zh/tutorials/python_api_install_offline.html |
|
|
25
|
+
| Release Notes | https://docs.dolphindb.cn/zh/ReleaseNotes/api/python_rn.html |
|
|
26
|
+
|
|
27
|
+
## 英文版(部分中文还没翻译,英文更完整)
|
|
28
|
+
|
|
29
|
+
主入口:https://docs.dolphindb.com/en/pydoc/
|
|
30
|
+
|
|
31
|
+
## GitHub 仓库
|
|
32
|
+
|
|
33
|
+
| 仓库 | URL | 用途 |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| Python SDK 源码 | https://github.com/dolphindb/python-sdk | 看源码确认行为 |
|
|
36
|
+
| Python SDK README | https://github.com/dolphindb/api_python3/blob/master/README.md | 快速上手 |
|
|
37
|
+
|
|
38
|
+
## 版本基线
|
|
39
|
+
|
|
40
|
+
本 skill 验证于:
|
|
41
|
+
- DolphinDB Server:**3.0 社区版**
|
|
42
|
+
- Python SDK:**dolphindb==3.0.4**
|
|
43
|
+
- 集群拓扑:1 控制 + 2 数据节点
|
|
44
|
+
- 资源:2 核 / 8GB / 33TB
|
|
45
|
+
|
|
46
|
+
跨大版本(如 2.x → 3.x,或 3.x → 4.x)部分行为会变,遇到不符必须回官方文档核对。
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# SQL 批计算
|
|
2
|
+
|
|
3
|
+
## 核心范式
|
|
4
|
+
|
|
5
|
+
DDB 三种批计算范式,按数据量选:
|
|
6
|
+
|
|
7
|
+
| 数据量 | 范式 | 说明 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| 单分区 / 单股 / 单天 | 直接 SQL | 一句话搞定 |
|
|
10
|
+
| 跨多分区 | `sqlDS` + `mr` 分布式 | 自动分片并行 |
|
|
11
|
+
| 复杂多步骤管道 | 中间内存表 + 多步 SQL | 拆步骤调试 |
|
|
12
|
+
|
|
13
|
+
## 范式 1:单步 SQL
|
|
14
|
+
|
|
15
|
+
```dolphindb
|
|
16
|
+
// 单天单股 SOIR
|
|
17
|
+
select TradeDate, TradeTime, SecurityID,
|
|
18
|
+
(BidOrderQty - OfferOrderQty) \ (BidOrderQty + OfferOrderQty) as imbalance
|
|
19
|
+
from loadTable("dfs://stock_lv2_snapshot", "snapshot")
|
|
20
|
+
where TradeDate = 2024.01.03 and SecurityID = `600000`
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 范式 2:sqlDS + mr(**跨分区必用**)
|
|
24
|
+
|
|
25
|
+
```dolphindb
|
|
26
|
+
def calcSomething(snapshot){
|
|
27
|
+
return select SecurityID, TradeDate,
|
|
28
|
+
avg(LastPrice) as avgPx,
|
|
29
|
+
sum(TotalVolumeTrade) as vol
|
|
30
|
+
from snapshot
|
|
31
|
+
group by SecurityID, TradeDate
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
dbName = "dfs://stock_lv2_snapshot"
|
|
35
|
+
tbName = "snapshot"
|
|
36
|
+
beginDate = 2024.01.01
|
|
37
|
+
endDate = 2024.01.31
|
|
38
|
+
|
|
39
|
+
// sqlDS = 把一个 SQL 切成多个数据源(按分区)
|
|
40
|
+
ds = sqlDS(<select * from loadTable(dbName, tbName)
|
|
41
|
+
where TradeDate between beginDate and endDate>)
|
|
42
|
+
|
|
43
|
+
// mr = map-reduce
|
|
44
|
+
// 参数:数据源, map 函数, reduce 函数(可省), final 合并, parallel
|
|
45
|
+
re = mr(ds, calcSomething, , unionAll, false)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**parallel=false 的意义**:写同一目标分区时必须 false。读分布式查询 + 写内存结果 → 可以 true。
|
|
49
|
+
|
|
50
|
+
## 完整案例:K 线合成(snapshot → 1 分钟 K)
|
|
51
|
+
|
|
52
|
+
```dolphindb
|
|
53
|
+
defg highPx(deltasHigh, highPrice, lastPrice){
|
|
54
|
+
// 函数式聚合:如果有 deltas 就用 high,否则用 last
|
|
55
|
+
if(sum(deltasHigh) > 0.000001){
|
|
56
|
+
return max(highPrice)
|
|
57
|
+
} else {
|
|
58
|
+
return max(lastPrice)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
def calcKLine(snapshot){
|
|
63
|
+
// 第一步:处理时间 + 计算 delta(按股票分组)
|
|
64
|
+
tempTB = select TradeDate,
|
|
65
|
+
iif(TradeTime <= 09:30:00.000, 09:30:00.000, TradeTime) as TradeTime,
|
|
66
|
+
SecurityID,
|
|
67
|
+
OpenPrice, HighPrice, LowPrice, LastPrice,
|
|
68
|
+
iif(deltas(HighPrice) > 0.000001, 1, 0) as deltasHigh,
|
|
69
|
+
iif(deltas(TotalVolumeTrade) == NULL,
|
|
70
|
+
TotalVolumeTrade,
|
|
71
|
+
deltas(TotalVolumeTrade)) as deltasVolume
|
|
72
|
+
from snapshot
|
|
73
|
+
where TradeTime >= 09:25:00.000
|
|
74
|
+
context by SecurityID // 注意:deltas 要分组
|
|
75
|
+
|
|
76
|
+
// 第二步:按 1 分钟桶聚合
|
|
77
|
+
return select firstNot(LastPrice, 0) as open,
|
|
78
|
+
highPx(deltasHigh, HighPrice, LastPrice) as high,
|
|
79
|
+
last(LastPrice) as close,
|
|
80
|
+
sum(deltasVolume) as volume
|
|
81
|
+
from tempTB
|
|
82
|
+
group by SecurityID, TradeDate,
|
|
83
|
+
interval(X=TradeTime, duration=60s, label='left', fill=0) as bar1min
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 跑全月
|
|
87
|
+
dbName = "dfs://stock_lv2_snapshot"
|
|
88
|
+
ds = sqlDS(<select * from loadTable(dbName, "snapshot")
|
|
89
|
+
where TradeDate between 2024.01.01 and 2024.01.31>)
|
|
90
|
+
result = mr(ds, calcKLine, , unionAll, false)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 完整案例:SOIR(订单失衡率)
|
|
94
|
+
|
|
95
|
+
```dolphindb
|
|
96
|
+
def wavgSOIR(bidQty, askQty, lag=20){
|
|
97
|
+
// rowWavg:按 10 档权重加权 (10,9,8,...,1)
|
|
98
|
+
imbalance = rowWavg(
|
|
99
|
+
(bidQty - askQty) \ (bidQty + askQty),
|
|
100
|
+
10 9 8 7 6 5 4 3 2 1
|
|
101
|
+
).ffill().nullFill(0)
|
|
102
|
+
|
|
103
|
+
// 滚动均值 + 标准差,做 z-score
|
|
104
|
+
mean = mavg(prev(imbalance), (lag - 1), 2)
|
|
105
|
+
std = mstdp(prev(imbalance) * 1000000, (lag - 1), 2) \ 1000000
|
|
106
|
+
|
|
107
|
+
return iif(std >= 0.0000001,
|
|
108
|
+
(imbalance - mean) \ std,
|
|
109
|
+
NULL).ffill().nullFill(0)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
re = select SecurityID, TradeDate, TradeTime,
|
|
113
|
+
wavgSOIR(BidOrderQty, OfferOrderQty, lag=20) as soir
|
|
114
|
+
from loadTable("dfs://stock_lv2_snapshot", "snapshot")
|
|
115
|
+
where TradeDate between 2024.01.01 and 2024.01.31
|
|
116
|
+
context by SecurityID csort TradeTime
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**关键点**:
|
|
120
|
+
- `context by SecurityID csort TradeTime`:按股票分组、组内按时间排序
|
|
121
|
+
- `prev()`:取上一行,避免使用当前行未来信息
|
|
122
|
+
- `ffill().nullFill(0)`:先用前值补,前值也没有时填 0
|
|
123
|
+
|
|
124
|
+
## 完整案例:截面排名(行业内 z-score)
|
|
125
|
+
|
|
126
|
+
```dolphindb
|
|
127
|
+
// 输入:因子表 t(date, sid, industry, factor)
|
|
128
|
+
// 输出:每天每个行业内 z-score 排名
|
|
129
|
+
|
|
130
|
+
// 方式 1:context by 多键
|
|
131
|
+
select date, sid, industry, factor,
|
|
132
|
+
(factor - avg(factor)) \ stdp(factor) as zscore
|
|
133
|
+
from t
|
|
134
|
+
context by date, industry
|
|
135
|
+
|
|
136
|
+
// 方式 2:pivot 后矩阵化
|
|
137
|
+
m = exec factor from t pivot by date, sid
|
|
138
|
+
// 转置:行=date, 列=sid -> 每行内 rank
|
|
139
|
+
rankM = rowRank(m, true) \ count(m[0]) // 转 percentile
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## 时序窗口函数(DDB 强项)
|
|
143
|
+
|
|
144
|
+
| 函数 | 说明 |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `prev(x, n=1)` | 取前 n 行 |
|
|
147
|
+
| `next(x, n=1)` | 取后 n 行 |
|
|
148
|
+
| `deltas(x)` | 一阶差分 |
|
|
149
|
+
| `ratios(x)` | 比率 |
|
|
150
|
+
| `cumsum/cumavg/cummax/cummin(x)` | 累计 |
|
|
151
|
+
| `mavg/msum/mmax/mmin(x, window)` | 滚动 |
|
|
152
|
+
| `mvar/mstdp(x, window)` | 滚动方差/标准差 |
|
|
153
|
+
| `ema(x, alpha)` | 指数移动平均 |
|
|
154
|
+
| `rolling(func, x, window, step)` | 通用滚动 |
|
|
155
|
+
|
|
156
|
+
## 截面函数
|
|
157
|
+
|
|
158
|
+
| 函数 | 说明 |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `rowSum/rowAvg/rowMin/rowMax(m)` | 矩阵每行聚合 |
|
|
161
|
+
| `rowRank(m, asc=true)` | 行内排名 |
|
|
162
|
+
| `rowWavg(m, weights)` | 行加权平均 |
|
|
163
|
+
| `corrMatrix(m)` | 相关性矩阵 |
|
|
164
|
+
| `pivot by` | 长表 → 矩阵 |
|
|
165
|
+
| `unpivot` | 矩阵 → 长表 |
|
|
166
|
+
|
|
167
|
+
## Python 触发批计算
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
# 方式 1:把 DDB 函数注册到服务端,Python 调用
|
|
171
|
+
ddb_func = """
|
|
172
|
+
def calcKLine(snapshot){ ... }
|
|
173
|
+
"""
|
|
174
|
+
s.run(ddb_func)
|
|
175
|
+
|
|
176
|
+
# 然后跑 mr
|
|
177
|
+
result = s.run("""
|
|
178
|
+
ds = sqlDS(<select * from loadTable('dfs://k','snapshot')
|
|
179
|
+
where TradeDate between 2024.01.01 and 2024.01.31>);
|
|
180
|
+
mr(ds, calcKLine, , unionAll, false)
|
|
181
|
+
""") # 返回 DataFrame
|
|
182
|
+
|
|
183
|
+
# 方式 2:用 submitJob 异步跑,避免 keepAlive 超时
|
|
184
|
+
jobId = s.run("""
|
|
185
|
+
submitJob('calcK', 'calc kline for jan',
|
|
186
|
+
{def(){
|
|
187
|
+
ds = sqlDS(<select * from loadTable('dfs://k','snapshot')
|
|
188
|
+
where TradeDate between 2024.01.01 and 2024.01.31>);
|
|
189
|
+
re = mr(ds, calcKLine, , unionAll, false);
|
|
190
|
+
loadTable('dfs://k','k_minute').tableInsert(re)
|
|
191
|
+
}})
|
|
192
|
+
""")
|
|
193
|
+
# 查进度
|
|
194
|
+
s.run(f"getJobStatus(`{jobId})")
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## 性能技巧
|
|
198
|
+
|
|
199
|
+
1. **能 group by 就别 context by**:context by 保留所有行,内存翻倍。
|
|
200
|
+
2. **数组向量比拆列快**:10 档行情用 `DOUBLE[]` 一列,而不是 20 列。
|
|
201
|
+
3. **mr 的 final 函数选择**:返回小表用 `unionAll`,返回标量用 `+` 或 `add`。
|
|
202
|
+
4. **defg vs def**:`defg`(aggregate function)用于聚合上下文,性能比普通 `def` 在 group by 中快 10x+。
|
|
203
|
+
5. **避免 join 大表**:DDB 的 `equal join` 在大分布式表上很慢,能 pivot 解决就 pivot。
|
|
204
|
+
6. **chunkSize 调优**:mr 太多小任务调度开销大,太少不并行。社区版 4 worker → 控制在 8-16 个 chunk。
|
|
205
|
+
|
|
206
|
+
跨引用:建表 → [[create-database-table]],调优 → [[performance-tuning]]。
|