@ backtest- kit/ graph
编曲 反测试- kit 以打入的定向循环图计算。 声明 源节点 获取市场数据和 输出节点 ——然后按地貌顺序解决整个图,平行地,完全地排入.

📚 文档 • 妇女 参考执行情况 • 妇女 GitHub 图像
npm install @backtest-kit/graph backtest-kit
是为了什么
一个交易信号很少是一个数字——这是一个小的计算:拉出4h的趋势,拉出15m的输入,结合它们,也许在RSI上的出门. 已写入内线, 成为 await手控订单 这个包让你宣布为 输入节点的图表: 叶(sourceNode获取数据,分支( )outputNode从他们的子女中计算 resolve() 由下而上地走着树——解决每个节点的依赖性 平行 (单位:千美元)Promise.all在计算节点本身之前。 刷取一个时间段或添加一个过滤节点不需要修改策略线条,而TypeScript通过整个图表推断出数值类型.
它直接插入一个 getSignal 并运行在反测试 -kit的 执行背景,所以 sourceNode是因为 fetch 自动接收 (symbol, when, currentPrice, exchangeName) ——引擎其余部分看到的同一种外观安全"现在".
多时间框架松树战略(犬科的例子)
双时间框架战略:a 4h 松树脚本 充当趋势过滤器, a 15米松脚本 生成条目。 输出节点结合它们并返回 null 只要趋势与条目不一致——所以一个长长的信号在下行和倒行逆施中被压制.
守则
import { extract, run, toSignalDto, File } from '@backtest-kit/pinets';
import { addStrategySchema, Cache } from 'backtest-kit';
import { randomString } from 'functools-kit';
import { sourceNode, outputNode, resolve } from '@backtest-kit/graph';
// SourceNode — 4h trend filter, cached per candle interval
const higherTimeframe = sourceNode(Cache.fn(async (symbol) => {
const plots = await run(File.fromPath('timeframe_4h.pine'), { symbol, timeframe: '4h', limit: 100 });
return extract(plots, { allowLong: 'AllowLong', allowShort: 'AllowShort', noTrades: 'NoTrades' });
}, { interval: '4h', key: ([symbol]) => symbol }));
// SourceNode — 15m entry signal, cached per candle interval
const lowerTimeframe = sourceNode(Cache.fn(async (symbol) => {
const plots = await run(File.fromPath('timeframe_15m.pine'), { symbol, timeframe: '15m', limit: 100 });
return extract(plots, { position: 'Signal', priceOpen: 'Close', priceTakeProfit: 'TakeProfit', priceStopLoss: 'StopLoss', minuteEstimatedTime: 'EstimatedTime' });
}, { interval: '15m', key: ([symbol]) => symbol }));
// OutputNode — applies the MTF filter, returns ISignalDto or null
const mtfSignal = outputNode(async ([higher, lower]) => {
if (higher.noTrades) return null;
if (lower.position === 0) return null;
if (higher.allowShort && lower.position === 1) return null; // long blocked in downtrend
if (higher.allowLong && lower.position === -1) return null; // short blocked in uptrend
return toSignalDto(randomString(), lower, null);
}, higherTimeframe, lowerTimeframe);
addStrategySchema({
strategyName: 'mtf_graph_strategy', interval: '5m',
getSignal: (symbol) => resolve(mtfSignal),
actions: ['partial_profit_action', 'breakeven_action'],
});
双松节点解 平行;其输入结果流入 compute替换脚本——或增加第三个过滤节点——不需要修改战略登记.
模型
按设计分为两层:a 低级别运行时间接口 (单位:千美元)INode串行到一个DB 重建,没有建造者,和a 高级作者 API (单位:千美元)TypedNode + 键 sourceNode编号 :outputNode)给出了完整的类型推论. 您与构建者一起写;运行时间和存储使用接口.
- 📊 DAG 执行 ——节点按地形顺序解决自下而上
Promise.all平行主义. - 🔒 类型安全值 ——TypeScript通过通义来推断出每个节点的返回类型(
OutputNode<[SourceNode<number>, SourceNode<string>], …>) (中文(简体) ). - 💾 DB 准备 —— 说
serialize编号 :deserialize将图表转换为平面IFlatNode[]列表为id编号 :nodeIds。 。 。 . - 🔌 获取上下文 —— 说
SourceNode.fetch接收(symbol, when, currentPrice, exchangeName)从执行上下文自动.
正在编写 API
构建器 API — sourceNode / 输出 Node / 分辨率
outputNode 推断类型 values 内 compute 从您经过的节点:
import { sourceNode, outputNode, resolve } from '@backtest-kit/graph';
const closePrice = sourceNode(async (symbol, when, currentPrice, exchangeName) => {
const candles = await getCandles(symbol, '1h', 1, exchangeName);
return candles[0].close; // SourceNode<number>
});
const volume = sourceNode(async (symbol, when, currentPrice, exchangeName) => {
const candles = await getCandles(symbol, '1h', 1, exchangeName);
return candles[0].volume; // SourceNode<number>
});
const vwap = outputNode(([price, vol]) => price * vol, closePrice, volume); // price, vol: number
const result = await resolve(vwap); // Promise<number>, inside a strategy
混合类型——按位置的不同推论
const price = sourceNode(async (symbol) => 42); // SourceNode<number>
const name = sourceNode(async (symbol) => 'BTCUSDT'); // SourceNode<string>
const flag = sourceNode(async (symbol) => true); // SourceNode<boolean>
const result = outputNode(
([p, n, f]) => `${n}: ${p} (active: ${f})`, // p: number, n: string, f: boolean
price, name, flag,
); // OutputNode<[SourceNode<number>, SourceNode<string>, SourceNode<boolean>], string>
内置匿名组成——单一对象文字
import { NodeType, TypedNode, resolve } from '@backtest-kit/graph';
const signal: TypedNode = {
type: NodeType.OutputNode,
nodes: [
{ type: NodeType.SourceNode, fetch: async (symbol) => extract(await run(File.fromPath('timeframe_4h.pine'), { symbol, timeframe: '4h', limit: 100 }), { allowLong: 'AllowLong', allowShort: 'AllowShort', noTrades: 'NoTrades' }) },
{ type: NodeType.SourceNode, fetch: async (symbol) => extract(await run(File.fromPath('timeframe_15m.pine'), { symbol, timeframe: '15m', limit: 100 }), { position: 'Signal', priceOpen: 'Close', priceTakeProfit: 'TakeProfit', priceStopLoss: 'StopLoss' }) },
],
compute: ([higher, lower]) => {
if (higher.noTrades || lower.position === 0) return null;
if (higher.allowShort && lower.position === 1) return null;
if (higher.allowLong && lower.position === -1) return null;
return lower.position;
},
};
const result = await resolve(signal);
内在的背向测试-基特策略
import { addStrategy } from 'backtest-kit';
import { sourceNode, outputNode, resolve } from '@backtest-kit/graph';
const rsi = sourceNode(async (symbol, when, currentPrice, exchangeName) => 55.2 /* compute RSI */);
const signal = outputNode(([rsiValue]) => rsiValue < 30 ? 1 : rsiValue > 70 ? -1 : 0, rsi);
addStrategy({
strategyName: 'graph-rsi', interval: '1h', riskName: 'demo',
getSignal: async (symbol) => {
const direction = await resolve(signal); // 1 | -1 | 0
return direction === 1 ? { position: 'long', /* … */ } : null;
},
});
运行时界面存储( S)
低水平的INDE——人工构造(后去除/DI)
import { INode, Value } from '@backtest-kit/graph';
import NodeType from '@backtest-kit/graph/enum/NodeType';
const priceNode: INode = { type: NodeType.SourceNode, description: 'Close price', fetch: async (symbol, when, currentPrice, exchangeName) => 42 };
const doubled: INode = { type: NodeType.OutputNode, description: 'Doubled price', nodes: [priceNode], compute: ([price]) => (price as number) * 2 };
INode 没有通用参数—— values 内 compute 输入为 Value[] (单位:千美元)string | number | boolean | null) (中文(简体) ). 使用 TypedNode + 建造器用于全IntelliSense.
DB 序列化——序列化/去序列化
serialize 将图表平整为 IFlatNode[]中替换对象引用 nodes 与 nodeIds· ; deserialize 重建树 :
import { serialize, deserialize, IFlatNode } from '@backtest-kit/graph';
const flat: IFlatNode[] = serialize([vwap]);
// [ { id:'abc', type:'source_node', nodeIds:[] }, // closePrice
// { id:'def', type:'source_node', nodeIds:[] }, // volume
// { id:'ghi', type:'output_node', nodeIds:['abc','def'] } ] // vwap
await db.collection('nodes').insertMany(flat);
const stored = await db.collection('nodes').find().toArray();
const roots: INode[] = deserialize(stored); // nodes[] re-wired from nodeIds
fetch 财务报告和审定财务报表 compute 为: 没有 存储 —— 之后在应用程序侧恢复 deserialize。 。 。 .
深浮-地貌
按地貌顺序返回所有节点(在父母之前的依赖),通过引用来解去:
import { deepFlat } from '@backtest-kit/graph';
const all = deepFlat([vwap]); // [closePrice, volume, vwap]
all.forEach(node => console.log(node.description));
API 参考
| 导出 | 说明 |
|---|---|
sourceNode(fetch) | 建构器-打入源(叶)节点; 2; fetch(symbol, when, currentPrice, exchangeName) |
outputNode(compute, ...nodes) | 构建器 — 输入输出节点; 引用 values 类型来源 nodes |
resolve(node) | 递归解决后端测试- kit 执行上下文中的图表 |
serialize(roots) | 将节点树平整为 IFlatNode[] 用于数据库存储 |
deserialize(flat) | 重建一个从 IFlatNode[],返回根节点 |
deepFlat(nodes) | 按地形顺序返回所有节点( 首先依赖) |
INode | 基本运行时间接口(未打出;内部和序列化使用) |
TypedNode | 带有全面IntelliSense著作权的歧视性结合 |
IFlatNode | DB 存储的序列化节点形状(id, (中文(简体) ), type, (中文(简体) ), nodeIds(中文(简体) ) |
NodeType | 埃纳姆 - SourceNode (单位:千美元)source_node) (中文(简体) ) OutputNode (单位:千美元)output_node(中文(简体) ) |
Value | string | number | boolean | null |
ExchangeName | 交换名别名 fetch |
完整源图.
enum/NodeType.ts• 妇女helpers/{node,resolve,serialize,deepFlat}.ts• 妇女interfaces/{Node,TypedNode,FlatNode}.interface.ts• 妇女model/ExchangeName.model.ts• 妇女index.ts——上面的每个导出都是这些文件之一;没有输入src/没有证件.
* 贡献
叉/ PR 打开 GitHub 图像。 。 。 .
许可证
麻省理工学院 特立奥尔斯基平板电脑