@openfinclaw/openfinclaw-strategy 0.0.11
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/LICENSE +21 -0
- package/README.md +185 -0
- package/index.test.ts +269 -0
- package/index.ts +1005 -0
- package/openclaw.plugin.json +35 -0
- package/package.json +45 -0
- package/skills/openfinclaw/SKILL.md +301 -0
- package/skills/skill-publish/SKILL.md +316 -0
- package/skills/strategy-builder/SKILL.md +555 -0
- package/skills/strategy-fork/SKILL.md +165 -0
- package/skills/strategy-pack/SKILL.md +285 -0
- package/src/cli.ts +321 -0
- package/src/fork.ts +342 -0
- package/src/strategy-storage.test.ts +109 -0
- package/src/strategy-storage.ts +303 -0
- package/src/types.ts +494 -0
- package/src/validate.test.ts +841 -0
- package/src/validate.ts +594 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for FEP v2.0 strategy packages.
|
|
3
|
+
* @module openfinclaw/types
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** FEP 协议版本 */
|
|
7
|
+
export type FepVersion = "2.0";
|
|
8
|
+
|
|
9
|
+
/** 策略风格 */
|
|
10
|
+
export type FepV2Style =
|
|
11
|
+
| "trend"
|
|
12
|
+
| "mean-reversion"
|
|
13
|
+
| "momentum"
|
|
14
|
+
| "value"
|
|
15
|
+
| "growth"
|
|
16
|
+
| "breakout"
|
|
17
|
+
| "rotation"
|
|
18
|
+
| "hybrid";
|
|
19
|
+
|
|
20
|
+
/** K线周期 */
|
|
21
|
+
export type FepV2Timeframe = "1m" | "5m" | "15m" | "30m" | "1h" | "4h" | "1d" | "1w";
|
|
22
|
+
|
|
23
|
+
/** FEP v2.0 identity 配置 */
|
|
24
|
+
export interface FepV2Identity {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
type?: string;
|
|
28
|
+
version?: string;
|
|
29
|
+
style?: FepV2Style;
|
|
30
|
+
visibility?: "public" | "private" | "unlisted";
|
|
31
|
+
summary?: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
tags?: string[];
|
|
34
|
+
license?: string;
|
|
35
|
+
author?: {
|
|
36
|
+
name?: string;
|
|
37
|
+
wallet?: string;
|
|
38
|
+
};
|
|
39
|
+
changelog?: Array<{
|
|
40
|
+
version: string;
|
|
41
|
+
date: string;
|
|
42
|
+
changes: string;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** FEP v2.0 technical 配置 */
|
|
47
|
+
export interface FepV2Technical {
|
|
48
|
+
language?: string;
|
|
49
|
+
entryPoint?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** FEP v2.0 多标的配置 */
|
|
53
|
+
export interface FepV2Universe {
|
|
54
|
+
symbols: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** FEP v2.0 再平衡配置 */
|
|
58
|
+
export interface FepV2Rebalance {
|
|
59
|
+
frequency: "daily" | "weekly" | "monthly";
|
|
60
|
+
maxHoldings?: number;
|
|
61
|
+
weightMethod?: "equal" | "market_cap";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** FEP v2.0 回测配置 */
|
|
65
|
+
export interface FepV2Backtest {
|
|
66
|
+
symbol: string;
|
|
67
|
+
timeframe?: FepV2Timeframe;
|
|
68
|
+
defaultPeriod: {
|
|
69
|
+
startDate: string;
|
|
70
|
+
endDate: string;
|
|
71
|
+
};
|
|
72
|
+
initialCapital: number;
|
|
73
|
+
universe?: FepV2Universe;
|
|
74
|
+
rebalance?: FepV2Rebalance;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** FEP v2.0 风控配置 */
|
|
78
|
+
export interface FepV2Risk {
|
|
79
|
+
maxDrawdownThreshold?: number;
|
|
80
|
+
dailyLossLimitPct?: number;
|
|
81
|
+
maxTradesPerDay?: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** FEP v2.0 模拟盘配置 */
|
|
85
|
+
export interface FepV2Paper {
|
|
86
|
+
barIntervalSeconds?: number;
|
|
87
|
+
maxDurationHours?: number;
|
|
88
|
+
warmupBars?: number;
|
|
89
|
+
timeframe?: FepV2Timeframe;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** FEP v2.0 策略参数 */
|
|
93
|
+
export interface FepV2Parameter {
|
|
94
|
+
name: string;
|
|
95
|
+
default: string | number | boolean;
|
|
96
|
+
type: "integer" | "number" | "string" | "boolean";
|
|
97
|
+
label?: string;
|
|
98
|
+
range?: {
|
|
99
|
+
min?: number;
|
|
100
|
+
max?: number;
|
|
101
|
+
step?: number;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** FEP v2.0 classification 配置 */
|
|
106
|
+
export interface FepV2Classification {
|
|
107
|
+
archetype?: "systematic" | "discretionary" | "hybrid";
|
|
108
|
+
market?: "Crypto" | "US" | "CN" | "HK" | "Forex" | "Commodity";
|
|
109
|
+
assetClasses?: string[];
|
|
110
|
+
frequency?: "daily" | "weekly" | "monthly";
|
|
111
|
+
riskProfile?: "low" | "medium" | "high";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** FEP v2.0 完整配置 */
|
|
115
|
+
export interface FepV2Config {
|
|
116
|
+
fep: FepVersion;
|
|
117
|
+
identity: FepV2Identity;
|
|
118
|
+
technical?: FepV2Technical;
|
|
119
|
+
parameters?: FepV2Parameter[];
|
|
120
|
+
backtest: FepV2Backtest;
|
|
121
|
+
risk?: FepV2Risk;
|
|
122
|
+
paper?: FepV2Paper;
|
|
123
|
+
classification?: FepV2Classification;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ─────────────────────────────────────────────────────────────
|
|
127
|
+
// FEP v2.0 回测结果类型 (TaskResultData)
|
|
128
|
+
// ─────────────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
/** 回测核心指标 */
|
|
131
|
+
export interface BacktestCoreMetrics {
|
|
132
|
+
totalReturn: number;
|
|
133
|
+
sharpe: number;
|
|
134
|
+
maxDrawdown: number;
|
|
135
|
+
totalTrades: number;
|
|
136
|
+
winRate: number;
|
|
137
|
+
profitFactor: number;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** 回测收益分析 */
|
|
141
|
+
export interface BacktestReturnAnalysis {
|
|
142
|
+
sortino: number;
|
|
143
|
+
annualizedReturn: number;
|
|
144
|
+
calmar: number;
|
|
145
|
+
returnsVolatility: number;
|
|
146
|
+
riskReturnRatio: number;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** 回测交易分析 */
|
|
150
|
+
export interface BacktestTradeAnalysis {
|
|
151
|
+
expectancy: number;
|
|
152
|
+
avgWinner: number;
|
|
153
|
+
avgLoser: number;
|
|
154
|
+
maxWinner: number;
|
|
155
|
+
maxLoser: number;
|
|
156
|
+
longRatio: number;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** 回测扩展指标 */
|
|
160
|
+
export interface BacktestExtendedMetrics {
|
|
161
|
+
pnlTotal: number;
|
|
162
|
+
startingBalance: number;
|
|
163
|
+
endingBalance: number;
|
|
164
|
+
backtestStart: string;
|
|
165
|
+
backtestEnd: string;
|
|
166
|
+
totalOrders: number;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** 权益曲线数据点 */
|
|
170
|
+
export interface EquityCurvePoint {
|
|
171
|
+
date: string;
|
|
172
|
+
equity: number;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** 回撤曲线数据点 */
|
|
176
|
+
export interface DrawdownCurvePoint {
|
|
177
|
+
date: string;
|
|
178
|
+
drawdown: number;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** 月度收益数据点 */
|
|
182
|
+
export interface MonthlyReturnPoint {
|
|
183
|
+
month: string;
|
|
184
|
+
return: number;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** 交易记录 */
|
|
188
|
+
export interface TradeRecord {
|
|
189
|
+
open_date: string;
|
|
190
|
+
close_date: string;
|
|
191
|
+
side: string;
|
|
192
|
+
quantity: number;
|
|
193
|
+
avg_open: number;
|
|
194
|
+
avg_close: number;
|
|
195
|
+
realized_pnl: string;
|
|
196
|
+
return_pct: number;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** 完整回测性能数据 */
|
|
200
|
+
export interface BacktestPerformance {
|
|
201
|
+
core?: BacktestCoreMetrics;
|
|
202
|
+
returns?: BacktestReturnAnalysis;
|
|
203
|
+
trades?: BacktestTradeAnalysis;
|
|
204
|
+
extended?: BacktestExtendedMetrics;
|
|
205
|
+
hints?: string[];
|
|
206
|
+
/** @deprecated 使用 equityCurve */
|
|
207
|
+
monthlyReturns?: Record<string, number> | MonthlyReturnPoint[];
|
|
208
|
+
recentValidation?: {
|
|
209
|
+
decay?: {
|
|
210
|
+
sharpeDecay30d: number;
|
|
211
|
+
sharpeDecay90d: number;
|
|
212
|
+
warning?: string;
|
|
213
|
+
};
|
|
214
|
+
recent?: Array<{
|
|
215
|
+
period?: string;
|
|
216
|
+
window?: string;
|
|
217
|
+
sharpe?: number;
|
|
218
|
+
finalEquity?: number;
|
|
219
|
+
maxDrawdown?: number;
|
|
220
|
+
totalReturn?: number;
|
|
221
|
+
totalTrades?: number;
|
|
222
|
+
}>;
|
|
223
|
+
historical?: {
|
|
224
|
+
period?: string;
|
|
225
|
+
sharpe?: number;
|
|
226
|
+
finalEquity?: number;
|
|
227
|
+
maxDrawdown?: number;
|
|
228
|
+
totalReturn?: number;
|
|
229
|
+
totalTrades?: number;
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** 完整回测结果 (TaskResultData) */
|
|
235
|
+
export interface BacktestResult {
|
|
236
|
+
alpha?: number | null;
|
|
237
|
+
taskId?: string;
|
|
238
|
+
metadata?: {
|
|
239
|
+
id?: string;
|
|
240
|
+
name?: string;
|
|
241
|
+
tags?: string[];
|
|
242
|
+
type?: string;
|
|
243
|
+
style?: string;
|
|
244
|
+
author?: { name?: string };
|
|
245
|
+
market?: string;
|
|
246
|
+
license?: string;
|
|
247
|
+
summary?: string;
|
|
248
|
+
version?: string;
|
|
249
|
+
archetype?: string;
|
|
250
|
+
frequency?: string;
|
|
251
|
+
riskLevel?: string;
|
|
252
|
+
visibility?: string;
|
|
253
|
+
description?: string;
|
|
254
|
+
assetClasses?: string[];
|
|
255
|
+
parameters?: FepV2Parameter[];
|
|
256
|
+
};
|
|
257
|
+
integrity?: {
|
|
258
|
+
fepHash?: string;
|
|
259
|
+
codeHash?: string;
|
|
260
|
+
contentCID?: string;
|
|
261
|
+
contentHash?: string;
|
|
262
|
+
publishedAt?: string;
|
|
263
|
+
timestampProof?: string;
|
|
264
|
+
};
|
|
265
|
+
performance?: BacktestPerformance;
|
|
266
|
+
equityCurve?: EquityCurvePoint[];
|
|
267
|
+
drawdownCurve?: DrawdownCurvePoint[];
|
|
268
|
+
monthlyReturns?: MonthlyReturnPoint[];
|
|
269
|
+
trades?: TradeRecord[];
|
|
270
|
+
/** @deprecated 使用 equityCurve */
|
|
271
|
+
equity_curve?: unknown;
|
|
272
|
+
/** @deprecated 使用 trades */
|
|
273
|
+
trade_journal?: unknown;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Fork 元数据(存储在 .fork-meta.json) */
|
|
277
|
+
export interface ForkMeta {
|
|
278
|
+
sourceId: string;
|
|
279
|
+
sourceShortId: string;
|
|
280
|
+
sourceName: string;
|
|
281
|
+
sourceVersion: string;
|
|
282
|
+
sourceAuthor?: string;
|
|
283
|
+
forkedAt: string;
|
|
284
|
+
forkDateDir: string;
|
|
285
|
+
hubUrl: string;
|
|
286
|
+
localPath: string;
|
|
287
|
+
forkEntryId?: string;
|
|
288
|
+
forkEntrySlug?: string;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** 创建元数据(存储在 .created-meta.json) */
|
|
292
|
+
export interface CreatedMeta {
|
|
293
|
+
name: string;
|
|
294
|
+
displayName?: string;
|
|
295
|
+
createdAt: string;
|
|
296
|
+
createDateDir: string;
|
|
297
|
+
localPath: string;
|
|
298
|
+
versions?: CreatedVersion[];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** 已发布版本记录 */
|
|
302
|
+
export interface CreatedVersion {
|
|
303
|
+
version: string;
|
|
304
|
+
publishedAt: string;
|
|
305
|
+
hubId: string;
|
|
306
|
+
hubSlug?: string;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** 本地策略信息 */
|
|
310
|
+
export interface LocalStrategy {
|
|
311
|
+
name: string;
|
|
312
|
+
displayName: string;
|
|
313
|
+
localPath: string;
|
|
314
|
+
dateDir: string;
|
|
315
|
+
type: "forked" | "created";
|
|
316
|
+
sourceId?: string;
|
|
317
|
+
createdAt: string;
|
|
318
|
+
performance?: StrategyPerformance;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** 策略绩效指标 */
|
|
322
|
+
export interface StrategyPerformance {
|
|
323
|
+
totalReturn?: number;
|
|
324
|
+
sharpe?: number;
|
|
325
|
+
maxDrawdown?: number;
|
|
326
|
+
winRate?: number;
|
|
327
|
+
totalTrades?: number;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Hub 公开策略详情(GET /api/v1/skill/public/{id} 响应) */
|
|
331
|
+
export interface HubPublicEntry {
|
|
332
|
+
id: string;
|
|
333
|
+
slug?: string;
|
|
334
|
+
name: string;
|
|
335
|
+
description?: string;
|
|
336
|
+
summary?: string;
|
|
337
|
+
type?: string;
|
|
338
|
+
tags?: string[];
|
|
339
|
+
version: string;
|
|
340
|
+
visibility: "public" | "private" | "unlisted";
|
|
341
|
+
tier?: string;
|
|
342
|
+
author?: {
|
|
343
|
+
id?: string;
|
|
344
|
+
slug?: string;
|
|
345
|
+
displayName?: string;
|
|
346
|
+
verified?: boolean;
|
|
347
|
+
};
|
|
348
|
+
stats?: {
|
|
349
|
+
fcsScore?: number;
|
|
350
|
+
forkCount?: number;
|
|
351
|
+
downloadCount?: number;
|
|
352
|
+
viewCount?: number;
|
|
353
|
+
};
|
|
354
|
+
backtestResult?: {
|
|
355
|
+
sharpe?: number;
|
|
356
|
+
totalReturn?: number;
|
|
357
|
+
maxDrawdown?: number;
|
|
358
|
+
winRate?: number;
|
|
359
|
+
};
|
|
360
|
+
createdAt?: string;
|
|
361
|
+
updatedAt?: string;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** Hub 策略详情(兼容旧类型) */
|
|
365
|
+
export type HubStrategyInfo = HubPublicEntry;
|
|
366
|
+
|
|
367
|
+
/** Fork 并下载响应(POST /api/v1/skill/entries/{id}/fork-and-download) */
|
|
368
|
+
export interface ForkAndDownloadResponse {
|
|
369
|
+
success: boolean;
|
|
370
|
+
entry: {
|
|
371
|
+
id: string;
|
|
372
|
+
slug?: string;
|
|
373
|
+
name: string;
|
|
374
|
+
version: string;
|
|
375
|
+
};
|
|
376
|
+
parent: {
|
|
377
|
+
id: string;
|
|
378
|
+
slug?: string;
|
|
379
|
+
name: string;
|
|
380
|
+
};
|
|
381
|
+
download: {
|
|
382
|
+
url: string;
|
|
383
|
+
filename: string;
|
|
384
|
+
expiresInSeconds: number;
|
|
385
|
+
contentHash?: string;
|
|
386
|
+
};
|
|
387
|
+
forkedAt: string;
|
|
388
|
+
creditsEarned?: {
|
|
389
|
+
action: string;
|
|
390
|
+
amount: number;
|
|
391
|
+
message?: string;
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Fork 配置 */
|
|
396
|
+
export interface ForkConfig {
|
|
397
|
+
keepGenes?: boolean;
|
|
398
|
+
overrideParams?: Record<string, unknown>;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** Fork 选项 */
|
|
402
|
+
export interface ForkOptions {
|
|
403
|
+
targetDir?: string;
|
|
404
|
+
dateDir?: string;
|
|
405
|
+
skipConfirm?: boolean;
|
|
406
|
+
name?: string;
|
|
407
|
+
slug?: string;
|
|
408
|
+
description?: string;
|
|
409
|
+
keepGenes?: boolean;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** Fork 结果 */
|
|
413
|
+
export interface ForkResult {
|
|
414
|
+
success: boolean;
|
|
415
|
+
localPath: string;
|
|
416
|
+
sourceId: string;
|
|
417
|
+
sourceShortId: string;
|
|
418
|
+
sourceName: string;
|
|
419
|
+
sourceVersion: string;
|
|
420
|
+
forkEntryId?: string;
|
|
421
|
+
forkEntrySlug?: string;
|
|
422
|
+
creditsEarned?: {
|
|
423
|
+
action: string;
|
|
424
|
+
amount: number;
|
|
425
|
+
message?: string;
|
|
426
|
+
};
|
|
427
|
+
error?: string;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/** 列表选项 */
|
|
431
|
+
export interface ListOptions {
|
|
432
|
+
json?: boolean;
|
|
433
|
+
dateDir?: string;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/** Skill API 配置 */
|
|
437
|
+
export interface SkillApiConfig {
|
|
438
|
+
baseUrl: string;
|
|
439
|
+
apiKey: string | undefined;
|
|
440
|
+
requestTimeoutMs: number;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** 榜单类型 */
|
|
444
|
+
export type BoardType = "composite" | "returns" | "risk" | "popular" | "rising";
|
|
445
|
+
|
|
446
|
+
/** 排行榜策略项 */
|
|
447
|
+
export interface LeaderboardStrategy {
|
|
448
|
+
id: string;
|
|
449
|
+
slug: string;
|
|
450
|
+
name: string;
|
|
451
|
+
description?: string;
|
|
452
|
+
market?: string;
|
|
453
|
+
style?: string;
|
|
454
|
+
riskLevel?: string;
|
|
455
|
+
author?: {
|
|
456
|
+
slug?: string;
|
|
457
|
+
displayName?: string;
|
|
458
|
+
verified?: boolean;
|
|
459
|
+
isAgent?: boolean;
|
|
460
|
+
};
|
|
461
|
+
publishedDays?: number;
|
|
462
|
+
subscribers?: number;
|
|
463
|
+
performance?: {
|
|
464
|
+
returnSincePublish?: number;
|
|
465
|
+
sharpeRatio?: number;
|
|
466
|
+
maxDrawdown?: number;
|
|
467
|
+
winRate?: number;
|
|
468
|
+
};
|
|
469
|
+
scores?: {
|
|
470
|
+
composite?: number;
|
|
471
|
+
returns?: number;
|
|
472
|
+
risk?: number;
|
|
473
|
+
popular?: number;
|
|
474
|
+
};
|
|
475
|
+
boardRanks?: {
|
|
476
|
+
composite?: { rank: number; rankDelta?: number };
|
|
477
|
+
returns?: { rank: number; rankDelta?: number };
|
|
478
|
+
risk?: { rank: number; rankDelta?: number };
|
|
479
|
+
popular?: { rank: number; rankDelta?: number };
|
|
480
|
+
rising?: { rank: number; rankDelta?: number };
|
|
481
|
+
};
|
|
482
|
+
rank: number;
|
|
483
|
+
rankDelta?: number;
|
|
484
|
+
isNewEntry?: boolean;
|
|
485
|
+
hotLabel?: string | null;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/** 排行榜响应 */
|
|
489
|
+
export interface LeaderboardResponse {
|
|
490
|
+
board: string;
|
|
491
|
+
strategies: LeaderboardStrategy[];
|
|
492
|
+
total: number;
|
|
493
|
+
cachedAt: string;
|
|
494
|
+
}
|