
ワークフローは単純なタスクしか自動化できないと考えている人が多いですが、ワークフローは想像以上に強力です。特にInventor Quantitative Platformでは、ワークフローは従来の戦略を実行するだけでなく、AIを活用して市場の監視、意思決定、そして戦略実行プロセスにおけるパラメータ調整も支援します。
簡単に言うと:従来の戦略は作業に責任を持ち、AIは思考に責任を持つ。
今日は実際のケースを使って、この 2 つを組み合わせて戦略をよりスマートにする方法についてお話ししましょう。
最も一般的なものを使用します双方向グリッド取引戦略例を挙げてみましょう。
これは、ロングとショートの両方を同時に取引するグリッド戦略です。
この戦略は変動の激しい市場においても安定した収益をもたらしますが、致命的な問題があります。パラメータは固定されている。

双方向グリッドで BTC 価格を 40,000 ドルから開始し、ステップ サイズを 1%、最大レベルを 5 に設定するとします。
ロングポジショングリッド(価格下落幅):
ショートポジショングリッド(価格上昇範囲):
BTC が突然一方的に 35,000 まで急落したらどうなるでしょうか?
あるいは逆に、BTC が 45,000 まで急騰したらどうなるでしょうか?
これが従来の戦略の限界であり、市場の変化について積極的に考えることができないのです。
では、戦略をよりスマートにする方法はあるのでしょうか?答えは、ワークフローを活用して従来の戦略とAIを組み合わせることです。Inventorプラットフォームのワークフローを通して、AIが重要な瞬間に介入し、戦略の調整を支援する仕組みを見てみましょう。

┌─────────────────────┐
│ K线收盘触发器 │ ← 每60秒触发一次
└──────────┬──────────┘
↓
┌─────────────────────┐
│ 参数初始化节点 │ ← 首次运行或重置后:初始化网格
│ │ (包含波动率检查)
└──────────┬──────────┘
↓
┌─────────────────────┐
│ 网格策略源码节点 │ ← 执行开平仓逻辑
└──────────┬──────────┘
↓
┌─────────────────────┐
│ 触发判断节点 │ ← 监控持仓+判断是否触发AI
│ │ (冷却期)
└──────────┬──────────┘
↓
┌─────────────────────┐
│ 分支判断节点 │ ← 根据触发条件分流
└────┬─────────┬──────┘
│ │
false true
│ │
↓ ↓
┌────────┐ ┌─────────────────────┐
│无操作 │ │ 情绪新闻获取(MCP) │ ← Alpha Vantage API
└────────┘ └──────────┬──────────┘
↓
┌─────────────────────┐
│ 结果整理节点 │ ← 整合新闻+持仓数据
└──────────┬──────────┘
↓
┌─────────────────────┐
│ AI参数分析节点 │ ← 情感分析节点判断Yes/No
│ (Sentiment) │
└────┬─────────┬──────┘
│ │
Yes No
│ │
↓ ↓
┌────────────┐ ┌────────┐
│ 重置策略 │ │AI冷却 │ ← 记录lastAItime
│ ·平掉所有仓│ └────────┘
│ ·清除grid │
│ ·清除price │
│ ·记录时间 │
└────────────┘
│
↓
(下个周期重新初始化)
始める前に、n8n ワークフローで次の変数を設定する必要があります。
$vars.contract = "BTC_USDT.swap" // 交易对
$vars.maxPositions = 5 // 最大档位数
$vars.stepPercent = 0.01 // 网格步长(1%)
$vars.lotSize = 0.001 // 每手交易量
ノード名: Kラインクローズトリガー1
ノードタイプ: klineCloseTrigger
機能説明:
ノード名: パラメータの初期化
ノードタイプ: Code
完全なコード:
let grid = _G('grid');
let initPrice = _G('initPrice');
let initEquity = _G('initEquity');
// ========== 从 n8n 变量读取配置参数 ==========
let maxPositions = $vars.maxPositions; // 最大档位数
let stepPercent = $vars.stepPercent; // 网格步长
let volatilityThreshold = 0.02; // 波动率阈值(默认2%)
let volatilityPeriod = 20; // 波动率计算周期(默认20根K线)
// ========== 波动率检查函数 ==========
function checkVolatility() {
// 获取历史K线数据
let records = exchange.GetRecords();
if (!records || records.length < volatilityPeriod) {
Log('K线数据不足,无法计算波动率');
return { isHigh: false, value: 0 };
}
// 计算最近N根K线的价格波动率
let prices = [];
for (let i = records.length - volatilityPeriod; i < records.length; i++) {
prices.push(records[i].Close);
}
// 计算平均价格
let avgPrice = prices.reduce((a, b) => a + b, 0) / prices.length;
// 计算标准差
let squareDiffs = prices.map(price => Math.pow(price - avgPrice, 2));
let avgSquareDiff = squareDiffs.reduce((a, b) => a + b, 0) / squareDiffs.length;
let stdDev = Math.sqrt(avgSquareDiff);
// 计算波动率 (标准差/平均价格)
let volatility = stdDev / avgPrice;
Log('当前波动率:', (volatility * 100).toFixed(2) + '%',
'阈值:', (volatilityThreshold * 100).toFixed(2) + '%');
return {
isHigh: volatility > volatilityThreshold,
value: volatility
};
}
// ========== 初始化前先检查波动率 ==========
if (!grid || Object.keys(grid).length === 0) {
// 检查波动率
let volatilityCheck = checkVolatility();
if (volatilityCheck.isHigh) {
Log('⚠️ 当前市场波动率过高:', (volatilityCheck.value * 100).toFixed(2) + '%');
Log('等待市场平稳后再初始化网格...');
return {
status: 'waiting',
reason: 'high_volatility',
volatility: volatilityCheck.value
};
}
Log('✓ 波动率检查通过,开始初始化网格');
// ========== 获取初始权益 ==========
if (!initEquity) {
let equity = exchange.GetAccount();
if (equity) {
initEquity = equity.Equity;
_G('initEquity', initEquity);
Log('使用当前市场权益作为初始权益:', initEquity);
} else {
Log('获取市场账户失败');
return null;
}
}
// ========== 获取初始价格 ==========
if (!initPrice) {
let ticker = exchange.GetTicker();
if (ticker) {
initPrice = ticker.Last;
_G('initPrice', initPrice);
Log('使用当前市场价格作为初始价格:', initPrice);
} else {
Log('获取市场价格失败');
return null;
}
}
// ========== 初始化网格 ==========
grid = {
// ========== 配置参数 ==========
stepPercent: stepPercent, // 网格步长
maxPositions: maxPositions, // 最大档位数
// ========== 网格数据 ==========
longOpenPrices: [], // 目标多仓开仓价格数组
longClosePrices: [], // 目标多仓平仓价格数组
longPositions: [], // 多仓持仓状态数组
shortOpenPrices: [], // 目标空仓开仓价格数组
shortClosePrices: [], // 目标空仓平仓价格数组
shortPositions: [] // 空仓持仓状态数组
};
// 初始化多仓网格(价格下跌时开多)
for (let i = 1; i <= maxPositions; i++) {
grid.longOpenPrices.push(initPrice * (1 - stepPercent * i));
grid.longClosePrices.push(initPrice * (1 - stepPercent * (i - 1)));
grid.longPositions.push({
isOpen: false,
openTime: null,
openPrice: null
});
}
// 初始化空仓网格(价格上涨时开空)
for (let i = 1; i <= maxPositions; i++) {
grid.shortOpenPrices.push(initPrice * (1 + stepPercent * i));
grid.shortClosePrices.push(initPrice * (1 + stepPercent * (i - 1)));
grid.shortPositions.push({
isOpen: false,
openTime: null,
openPrice: null
});
}
_G('grid', grid);
Log('========== 网格初始化完成 ==========');
Log('初始价格:', initPrice);
Log('初始权益:', initEquity);
Log('网格步长:', (stepPercent * 100) + '%');
Log('最大档位:', maxPositions);
Log('当前波动率:', (volatilityCheck.value * 100).toFixed(2) + '%');
Log('多仓网格范围:', grid.longOpenPrices[0].toFixed(2), '-', grid.longOpenPrices[maxPositions-1].toFixed(2));
Log('空仓网格范围:', grid.shortOpenPrices[0].toFixed(2), '-', grid.shortOpenPrices[maxPositions-1].toFixed(2));
Log('===================================');
}
return {};
機能説明:
_G()この関数はデータの永続性を実装しており、再起動後もデータは失われません。ノード名: グリッド戦略のソースコード
ノードタイプ: Code
完全なコード:
var lotSize = $vars.lotSize || 0.001; // 每手数量
var grid = _G('grid');
var initPrice = _G('initPrice');
// 策略未初始化,直接退出策略
if (!initPrice || !grid) {
return {};
}
// ========== 多仓开仓检查函数 ==========
function checkLongOpen(price) {
for (var i = 0; i < grid.longOpenPrices.length; i++) {
// 条件1: 价格低于或等于目标开仓价
// 条件2: 该档位当前没有持仓
if (price <= grid.longOpenPrices[i] && !grid.longPositions[i].isOpen) {
Log('准备开多仓');
// 设置交易方向为买入(做多)
exchange.SetDirection('buy');
// 下市价单: -1表示市价, lotSize是数量
var orderId = exchange.Buy(-1, lotSize);
if (orderId) {
// 记录开仓信息
grid.longPositions[i] = {
isOpen: true, // 标记为已开仓
openTime: Date.now(), // 记录开仓时间戳
openPrice: price // 记录开仓价格
};
// 持久化保存
_G('grid', grid);
Log('✓ 开多 第', i + 1, '档',
'开仓价:', price,
'目标平仓价:', grid.longClosePrices[i]);
}
}
}
}
// ========== 多仓平仓检查函数 ==========
function checkLongClose(price) {
for (var i = 0; i < grid.longClosePrices.length; i++) {
// 条件1: 该档位有持仓
// 条件2: 价格达到或超过目标平仓价
if (grid.longPositions[i].isOpen && price >= grid.longClosePrices[i]) {
Log('准备平多仓');
// 设置交易方向为平多
exchange.SetDirection('closebuy');
// 下市价单平仓
var orderId = exchange.Sell(-1, lotSize);
if (orderId) {
// 计算盈利百分比
var profit = ((price - grid.longPositions[i].openPrice) /
grid.longPositions[i].openPrice * 100).toFixed(2);
Log('✓ 平多 第', i + 1, '档',
'开仓价:', grid.longPositions[i].openPrice,
'平仓价:', price,
'盈利:', profit + '%');
// 清除持仓信息
grid.longPositions[i] = {
isOpen: false,
openTime: null,
openPrice: null
};
// 持久化保存
_G('grid', grid);
}
}
}
}
// ========== 空仓开仓检查函数 ==========
function checkShortOpen(price) {
for (var i = 0; i < grid.shortOpenPrices.length; i++) {
// 条件1: 价格高于或等于目标开仓价
// 条件2: 该档位当前没有持仓
if (price >= grid.shortOpenPrices[i] && !grid.shortPositions[i].isOpen) {
Log('准备开空仓');
// 设置交易方向为卖出(做空)
exchange.SetDirection('sell');
// 下市价单开空
var orderId = exchange.Sell(-1, lotSize);
if (orderId) {
// 记录开仓信息
grid.shortPositions[i] = {
isOpen: true,
openTime: Date.now(),
openPrice: price
};
_G('grid', grid);
Log('✓ 开空 第', i + 1, '档',
'开仓价:', price,
'目标平仓价:', grid.shortClosePrices[i]);
}
}
}
}
// ========== 空仓平仓检查函数 ==========
function checkShortClose(price) {
for (var i = 0; i < grid.shortClosePrices.length; i++) {
// 条件1: 该档位有持仓
// 条件2: 价格达到或低于目标平仓价
if (grid.shortPositions[i].isOpen && price <= grid.shortClosePrices[i]) {
Log('准备平空仓');
// 设置交易方向为平空
exchange.SetDirection('closesell');
// 下市价单平仓
var orderId = exchange.Buy(-1, lotSize);
if (orderId) {
// 计算盈利百分比(空单盈利 = 开仓价 - 平仓价)
var profit = ((grid.shortPositions[i].openPrice - price) /
grid.shortPositions[i].openPrice * 100).toFixed(2);
Log('✓ 平空 第', i + 1, '档',
'开仓价:', grid.shortPositions[i].openPrice,
'平仓价:', price,
'盈利:', profit + '%');
// 清除持仓信息
grid.shortPositions[i] = {
isOpen: false,
openTime: null,
openPrice: null
};
_G('grid', grid);
}
}
}
}
// ========== 主逻辑 ==========
// 获取当前市场价格
var ticker = exchange.GetTicker();
if (!ticker) {
Log('获取ticker失败');
return {};
}
var price = ticker.Last;
// 依次检查多空开平
checkLongOpen(price); // 检查是否需要开多
checkLongClose(price); // 检查是否需要平多
checkShortOpen(price); // 检查是否需要开空
checkShortClose(price); // 检查是否需要平空
return {};
機能説明:
トランザクションロジックの例:
场景1: 价格从40000跌到39500
→ checkLongOpen检测到 price(39500) <= longOpenPrices[0](39600)
→ 开多第1档,记录开仓价39500
→ 等待价格回升到40000平仓
场景2: 价格从39500回升到40100
→ checkLongClose检测到 price(40100) >= longClosePrices[0](40000)
→ 平多第1档,盈利 (40100-39500)/39500 = 1.52%
ノード名: トリガー判定 ノードタイプ: Code
完全なコード:
// ========== 触发判断节点 ==========
var grid = _G('grid');
var ticker = exchange.GetTicker();
var curaccount = exchange.GetAccount();
var initPrice = _G('initPrice');
var initEquity = _G('initEquity');
if (!ticker || !grid || !initPrice || !curaccount || !initEquity) {
return {};
}
let curProfit = curaccount.Equity - initEquity;
LogProfit(curProfit, "&");
var currentPrice = ticker.Last;
var now = Date.now();
var maxPositions = grid.maxPositions || 5;
// 统计开仓数量和总浮动盈亏
var openCount = 0;
var lastOpenPosition = null;
var totalProfit = 0;
var longCount = 0;
var shortCount = 0;
// 统计多仓
for (var i = 0; i < grid.longPositions.length; i++) {
if (grid.longPositions[i].isOpen) {
openCount++;
longCount++;
lastOpenPosition = grid.longPositions[i];
var posProfit = ((currentPrice - grid.longPositions[i].openPrice) / grid.longPositions[i].openPrice) * 100;
totalProfit += posProfit;
}
}
// 统计空仓
for (var i = 0; i < grid.shortPositions.length; i++) {
if (grid.shortPositions[i].isOpen) {
openCount++;
shortCount++;
lastOpenPosition = grid.shortPositions[i];
var posProfit = ((grid.shortPositions[i].openPrice - currentPrice) / grid.shortPositions[i].openPrice) * 100;
totalProfit += posProfit;
}
}
// 构建持仓表格
var table = {
type: "table",
title: "双向网格持仓",
cols: ["初始价", "当前价", "网格步长", "多仓数", "空仓数", "总持仓", "初始权益", "当前权益", "累计盈亏", "浮动盈亏%"],
rows: [[
_N(initPrice, 2),
_N(currentPrice, 2),
_N(grid.stepPercent * 100, 2) + '%',
longCount,
shortCount,
openCount + '/' + maxPositions,
_N(initEquity, 2),
_N(curaccount.Equity, 2),
_N(curProfit, 2),
_N(totalProfit, 2) + '%'
]]
};
LogStatus("`" + JSON.stringify(table) + "`");
// 不是满仓不触发AI
if (openCount < maxPositions) {
return { aiTrigger: { shouldTrigger: false } };
}
// 检查AI冷却时间
var lastAItime = _G('lastAItime');
if (lastAItime && (now - lastAItime) < 600000) {
Log('AI冷却中,剩余', ((600000 - (now - lastAItime)) / 60000).toFixed(1), '分钟');
return { aiTrigger: { shouldTrigger: false } };
}
// 满仓时计算条件
var holdHours = (now - lastOpenPosition.openTime) / 3600000;
var priceDeviation = Math.abs(currentPrice / lastOpenPosition.openPrice - 1);
// 价格偏离>3% 或 持仓>24小时
var shouldTriggerAI = priceDeviation > 0.03 || holdHours >= 24;
if (shouldTriggerAI) {
Log('触发AI分析 偏离:', (priceDeviation * 100).toFixed(2) + '% 时长:', holdHours.toFixed(1), '小时');
}
return {
aiTrigger: {
shouldTrigger: shouldTriggerAI
}
};
機能説明:
トリガー条件の詳細な説明:
AI冷却:
满足AI冷却时间,进行AI触发;
条件组合:
满仓(openCount >= 5)
AND
(
价格偏离>3% OR 持仓时长>24小时
)
实际案例:
场景1: 开仓3个 → 不触发(未满仓)
场景2: 开仓5个,持仓12小时,偏离1.5% → 不触发(未达阈值)
场景3: 开仓5个,持仓30小时,偏离1% → 触发(持仓过久)
场景4: 开仓5个,持仓5小时,偏离5% → 触发(价格偏离大)
ノード名: 支店
ノードタイプ: Switch
機能説明:
aiTrigger.shouldTrigger価値転換このノードはコスト管理の鍵、本当に必要なときにのみ AI が呼び出されるようにします。
ノード名: センチメントニュース取得
ノードタイプ: MCP Client
機能説明:
ツール構成:
工具: NEWS_SENTIMENT
参数:
- tickers: CRYPTO:{{$vars.contract}} // 从变量读取交易对
- 使用默认配置: 返回最多50条新闻,时间范围由API自动确定
ノード名: 結果
ノードタイプ: Code
完全なコード:
// n8n 的正确语法
const inputData = $input.all();
const sentimentData = inputData[0].json; // 获取新闻情绪数据
// 从特定节点获取持仓数据
const positionNode = $node["触发判断"].json;
// 返回整合后的数据
return {
timestamp: new Date().toISOString(),
// 原始新闻数据
sentimentData: sentimentData,
// 持仓状态数据
positions: positionNode
};
機能説明:
ノード名: AIパラメータ解析
ノードタイプ:感情分析

完全なコンテンツをすぐに:
## 策略背景
你正在分析一个双向网格交易策略。该策略基于初始价格(initPrice)设置多空双向网格:
- **多仓网格**: 在价格下跌时逐级开多,回升时平仓获利(步长1%)
- **空仓网格**: 在价格上涨时逐级开空,回落时平仓获利(步长1%)
- **最大持仓**: 多空各5档,共10个仓位
## 当前触发条件
系统已检测到以下异常情况之一:
1. 持仓数量达到5个(满仓状态)
2. 最长持仓时间超过24小时(持仓被套)
3. 持有空仓时,价格突破网格上限(价格持续上涨)
4. 持有多仓时,价格跌破网格下限(价格持续下跌)
## 你的分析任务
请基于以下数据综合判断:
### 数据1: 持仓状态(positions)
{{JSON.stringify($json.positions)}}
### 数据2: 市场情绪(sentimentData)
{{JSON.stringify($json.sentimentData)}}
## 判断标准
**需要调整网格价格**的情况:
- 市场趋势明确且持续(新闻情绪极度偏多/空)
- 当前价格已远离初始网格范围(突破或跌破超过3%)
- 持仓严重被套且市场情绪不支持反转
- 新闻显示基本面发生重大变化(监管、技术升级、重大事件)
**不需要调整**的情况:
- 价格在网格范围内正常波动
- 新闻情绪中性或矛盾
- 短期波动,缺乏趋势确认
- 持仓浮亏在可接受范围内
**注意**:
- 必须返回明确的"Yes"或"No"
- 理由需简洁、具体、可操作
- 谨慎判断,避免频繁调整网格
機能説明:
AIリターン例:
{
"sentiment": "Yes",
"sentimentScore": 0.95,
"reason": ...
}
ノード名: 戦略をリセット
ノードタイプ: Code
完全なコード:
Log('清仓仓位,重置所有参数')
let positions = exchange.GetPosition();
if (positions[0].Type === 0) {
// 平多仓 - 市价卖出
const orderId = exchange.CreateOrder(positions[0].Symbol, 'closebuy', -1, positions[0].Amount);
Log(`✓ 平多仓成功,订单ID: ${orderId}`);
} else if (positions[0].Type === 1) {
// 平空仓 - 市价买入
const orderId = exchange.CreateOrder(positions[0].Symbol, 'closesell', -1, positions[0].Amount);
Log(`✓ 平空仓成功,订单ID: ${orderId}`);
}
_G('grid', null);
_G('initPrice', null);
_G('lastAItime', Date.now());
return {};
機能説明:
実行プロセス:
当前时刻: 价格35000,多仓5档全开
↓
AI判断: Yes,建议调整
↓
执行清仓: 平掉所有多单
↓
清除数据: grid=null, initPrice=null
↓
60秒后: K线触发器再次触发
↓
重新初始化: 以35000为新的初始价格
↓
重建网格: 新的多空网格围绕35000展开
ノード名: AI冷却
ノードタイプ: Code
Log('AI分析不支持调整原始价格')
_G('lastAItime', Date.now())
return {};
機能説明:
問題の発見から調整の完了まで、プロセス全体は完全に自動化されており、人間の介入は必要ありません。
| 寸法 | 手動監視とパラメータ調整 | ワークフロー + AIによる自動意思決定 |
|---|---|---|
| 監視頻度 | 24時間監視が必要 | 60秒ごとに自動的にチェックします |
| 反応速度 | 深夜および早朝の遅延 | 数秒以内に応答 |
| 意思決定の質 | 感情的に操作しやすい | 客観的な分析とニュースの感情を組み合わせた |
| 人件費 | 高強度労働 | 時々ログを確認してください |
シナリオ1:不安定な市場(通常運用)
8:00 - 价格39500,开多第一档
9:00 - 价格39800,平多第一档,盈利0.76%
10:00 - 价格40300,开空第一档
11:00 - 价格40100,平空第一档,盈利0.50%
...持续震荡,网格正常运行
→ AI监控指标正常,不触发分析,零额外成本
シナリオ2:一方的な減少(AI介入)
周一 - 价格从40000跌到38000,多仓5档全开
周二 - 继续下跌至36000,持仓24小时触发AI
→ AI分析:市场情绪-0.65(偏空),建议调整
→ 自动平仓5个多单,止损-10%以上
→ 以36000重建网格,继续震荡获利
周三 - 新网格开始运行,当天盈利1.3%
調整が行われない場合:5つのロング注文が引き続きトラップされ、損失が-10%以上に拡大する可能性があります。
シナリオ3:速報ニュースの影響
14:00 - 价格正常震荡在39800
14:30 - 突发监管利空,价格闪崩至37500
14:31 - 多仓满仓+价格偏离触发AI
→ AI抓取新闻:"SEC突击检查交易所"
→ 情绪评分骤降至-0.85
→ 判断:短期难反转,建议调整
14:32 - 自动平仓并重置网格至37500
従来の戦略では、手動で問題を発見するのに数日かかる場合があります
AI の使用コストを心配する人は多いですが、実際には完全に制御可能です。
この考え方はグリッド戦略に限定されません。
❌ "帮我看看要不要调参数"
✅ "当前持仓5个,被套36小时,市场新闻偏空,
我的网格中心价是40000,现价35000,
请判断是否需要调整网格参数"
すべての AI の決定を保存します。
Inventorプラットフォームのワークフローの最大の価値は、従来の戦略を置き換えることではなく、伝統的な戦略をよりスマートにする:
勤勉だが融通の利かない従業員に、優秀なコンサルタントを任命するようなものです。従業員はルールを守る責任を負い、コンサルタントは重要な局面でアドバイスを提供する責任を負います。
最も重要なのはすべてを自動化できます。24時間365日市場を監視したり、パラメータを調整するために午前3時に起きたりする必要もありません。戦略は実行され、AIがあなたを見守っています。あなたは定期的に結果を確認するだけで済みます。
定量取引はこうあるべきです。人間は全体像を考える責任があり、機械は細部を実行する責任があり、AI はタイムリーな最適化を担当します。