[TOC]

В области количественной торговли стратегия «переноса позиций» — привлекательная, но сложная тема. Основная идея этой стратегии заключается в достижении сложного процента за счет реинвестирования полученной прибыли на трендовых рынках. В этой статье мы шаг за шагом рассмотрим, как воплотить эту торговую идею в исполняемый код, сосредоточившись на изменении подхода, а не на технических деталях. Важно отметить, что, хотя стратегия «переноса позиций» увеличивает доходность, она также увеличивает риски; эта статья предназначена только для обучения и обсуждения.

Логика получения прибыли при использовании стратегии переноса позиций по сути сводится к следующему:Модель сложного ростаДавайте разберемся в этом на упрощенном примере:
В том же сценарии, когда рынок растет на 10% три раза подряд:
Аналогичным образом, при трех последовательных повышениях на 10% прибыль от одной сделки составила 99,3 USDT, а прибыль от переноса позиции — 119,7 USDT.Эта разница и есть сила сложных процентов.。
Выражено с помощью математической формулы:
// 传统交易:线性增长
最终资金 = 初始资金 × (1 + 杠杆 × 涨幅)
// 滚仓交易:指数增长
最终资金 = 初始资金 × (1 + 杠杆 × 单次涨幅) ^ 滚仓次数
Это раскрывает суть эффекта наведения курсора:Преобразование линейного роста в экспоненциальный ростОднако это также создало риски:Один-единственный стоп-лосс ордер может свести на нет всю предыдущую прибыль, полученную за счет сложных процентов.。
Прежде чем начать писать код, нам необходимо ответить на три фундаментальных вопроса со стратегической точки зрения:
Вопрос 1: Когда это начинается? (Первый ответ)
Необходимо определить начальный сигнал тренда.
Вопрос 2: Когда продолжить? (Дополнительная позиция прокатки)
В этом и заключается суть переноса позиций: как определить, продолжится ли тренд после фиксации прибыли.
Вопрос 3: Когда следует остановиться? (Прекратите и понаблюдайте)
Эти три вопроса определяют основу всей стратегии, и теперь мы по очереди переведем их в программную логику.

Давайте сначала разберемся в идеальном сценарии применения стратегии скользящего положения.
Идеальный сценарий:
Представьте, что вы можете войти на рынок SHIB, когда он начинает расти с отметки в 0,000001 доллара, или открыть позицию непосредственно перед резким ростом стоимости определенного альткоина. Благодаря постоянному переносу средств, 100 USDT потенциально могут превратиться в 10 000 USDT или даже больше. Это и есть конечная цель стратегии переноса средств.Войдите на рынок до того, как криптовалюта взлетит, и получите десятикратную или даже стократную прибыль.。
Суровая реальность:
Проблема в том, как узнать, какая криптовалюта резко вырастет в цене? И когда это произойдёт?
Для большинства из нас точное определение этой критической точки…Говоря прямо, всё дело в удаче.Мы не можем предсказать будущее; мы можем лишь попытаться повысить вероятность «сорвать джекпот», используя исторические данные и технические индикаторы.
Поскольку мы не можем предсказать, какая криптовалюта резко вырастет в цене, все, что мы можем сделать, это:Разработайте исполняемый набор правил входа и используйте технические индикаторы для имитации сигналов «начала тренда».。
Это как рыбалка в бескрайнем океане. Даже не зная, где водятся крупные рыбы, мы можем:
Когда сходятся несколько сигналов, мы считаем, что вот-вот начнётся тренд, поэтому выходим на рынок, чтобы проверить его. Если мы оказываемся правы, мы следуем тренду и переносим свои позиции, чтобы заработать деньги; если же мы ошибаемся, мы сокращаем убытки и немедленно выходим с рынка.
Выбор технических инструментов:
В качестве инструмента определения тренда мы используем систему двойных скользящих средних EMA (EMA5 и EMA10). Причина выбора проста:
Основная логика:
Обнаружение «золотого креста» (пересечение EMA5 выше EMA10) и «креста смерти» (пересечение EMA5 ниже EMA10) скользящих средних позволяет определить точки разворота тренда:
Код мысли:
// 计算EMA指标
var emaFast = TA.EMA(records, FastEMA); // EMA5
var emaSlow = TA.EMA(records, SlowEMA); // EMA10
// 获取当前和前一根K线的EMA值
var ema5_current = emaFast[emaFast.length - 1];
var ema5_prev = emaFast[emaFast.length - 2];
var ema10_current = emaSlow[emaSlow.length - 1];
var ema10_prev = emaSlow[emaSlow.length - 2];
// 检测金叉:前一根K线EMA5<=EMA10,当前K线EMA5>EMA10
var bullCross = ema5_prev <= ema10_prev && ema5_current > ema10_current;
// 检测死叉:前一根K线EMA5>=EMA10,当前K线EMA5<EMA10
var bearCross = ema5_prev >= ema10_prev && ema5_current < ema10_current;
// 空仓时等待信号入场
if (bullCross) {
Log("📈 金叉信号 - 做多");
openPosition("LONG", currentPrice);
} else if (bearCross) {
Log("📉 死叉信号 - 做空");
openPosition("SHORT", currentPrice);
}
В этом разделе мы не будем углубляться в детали золотых и крестов смерти; это фундаментальные понятия в трейдинге. Ключевой момент:Нам нужен четкий, поддающийся количественной оценке сигнал, который запустит процесс переворота.。

Стратегия переворота по сутиРациональная игра-приключениеДавайте разберем это на примере полной ситуации:
Правила игры:
1. 你从交易所账户中拿出100 USDT作为冒险资金
2. 这100 USDT独立管理,与账户其他资金隔离
3. 用这100 USDT开始交易:
- 赚了 → 盈利加入资金池,继续用更大的资金交易(滚仓)
- 亏了 → 触发止损,回到空仓状态
4. 重复这个过程,直到:
- 要么把100 USDT亏完(游戏结束)
- 要么滚到一个满意的金额(主动退出)
Гениальность этой игры заключается в следующем:
Это основная концепция проектирования стратегии позиционирования при качении.
Проблемы, связанные с традиционными методами:
Предположим, на вашем биржевом счете 1000 USDT:
Решение для объединения средств:
// 创建一个虚拟的"策略资金池"
var strategyCapital = InitialCapital; // 初始100 USDT
// 第1次交易
// 开仓金额 = 100 USDT
// 止盈后盈利 = 30 USDT
strategyCapital = strategyCapital + 30; // 资金池变为130 USDT
// 第2次交易(滚仓)
var positionValue = strategyCapital * Leverage; // 130 × 3 = 390
var amount = positionValue / price / ctVal; // 计算开仓数量
// 自动使用了第1次的盈利,这就是复利的关键
// 止盈后盈利 = 39 USDT
strategyCapital = strategyCapital + 39; // 资金池变为169 USDT
// 第3次交易(滚仓)
// 开仓金额 = 169 USDT(继续利滚利)
Преимущества данной конструкции:
Это ключевой элемент стратегии скользящих позиций:После исполнения ордера на фиксацию прибыли нам необходимо принять важное решение – продолжать перенос позиции или остановиться?
Сценарий принятия решения:
假设我们做多BTC:
- 入场价:45000 USDT,用100 USDT开仓
- 止盈价:49500 USDT(涨10%)
- 止盈成交,盈利30 USDT
- 现在资金池:130 USDT
问题来了:
选项A:收手,带着130 USDT退出,回到空仓
选项B:继续,用130 USDT再次开多(滚仓)
Как сделать выбор?
Это решение не может основываться на «чувствах»; должны быть четкие стандарты. Наша логика оценки такова:Эта тенденция сохранится?
Метод оценки:
В момент исполнения ордера на фиксацию прибыли производится перерасчет последних технических индикаторов (скользящей средней EMA):
// 止盈单成交后,获取最新K线数据
var records = _C(exchange.GetRecords, PERIOD_M1);
var emaFast = TA.EMA(records, FastEMA);
var emaSlow = TA.EMA(records, SlowEMA);
var ema5_current = emaFast[emaFast.length - 1];
var ema10_current = emaSlow[emaSlow.length - 1];
var shouldRoll = false;
if (currentDirection == "LONG") {
// 多头止盈后,如果EMA5仍在EMA10上方,继续做多(滚仓)
if (ema5_current > ema10_current) {
shouldRoll = true;
Log("✅ EMA5 > EMA10,上升趋势未破坏");
Log("🔄 决策:继续做多(滚仓)");
} else {
Log("❌ EMA5 <= EMA10,趋势可能转弱");
Log("⏸️ 决策:不滚仓,等待新信号");
}
} else if (currentDirection == "SHORT") {
// 空头止盈后,如果EMA5仍在EMA10下方,继续做空(滚仓)
if (ema5_current < ema10_current) {
shouldRoll = true;
Log("✅ EMA5 < EMA10,下降趋势未破坏");
Log("🔄 决策:继续做空(滚仓)");
} else {
Log("❌ EMA5 >= EMA10,趋势可能转弱");
Log("⏸️ 决策:不滚仓,等待新信号");
}
}
Если принято решение «продолжать переносить позицию»:
if (shouldRoll) {
// 1. 增加滚仓计数
currentRoundRolls++;
Log("🔄 执行滚仓操作... (本轮第", currentRoundRolls, "次滚仓)");
// 2. 获取最新价格
var ticker = _C(exchange.GetTicker);
var newPrice = ticker.Last;
// 3. 基于新资金池重新开仓
if (openPosition(currentDirection, newPrice)) {
Log("✅ 滚仓成功!");
// 4. 挂新的止盈单(在openPosition函数中完成)
// 5. 设置新的止损价(在checkStopLoss函数中监控)
} else {
Log("❌ 滚仓失败,等待新信号");
saveRollRecord(false);
resetPositionState();
}
}
Если принято решение «стоп»:
else {
// 1. 保存本轮统计
saveRollRecord(false); // false表示正常结束,非止损
// 2. 保留资金池金额
// strategyCapital 保持当前值,等待下次机会
// 3. 回到空仓状态
resetPositionState();
Log("⏳ 已平仓,等待新信号...");
}
Ключевые моменты этого процесса:
Давайте на собственном опыте убедимся в силе сложных процентов на примере конкретного случая:
Истории успеха:
初始资金:100 USDT
止盈比例:10%
杠杆:3倍
第1次:100 USDT → 盈利30 → 资金池130
第2次:130 USDT → 盈利39 → 资金池169
第3次:169 USDT → 盈利50.7 → 资金池219.7
第4次:219.7 USDT → 盈利65.9 → 资金池285.6
第5次:285.6 USDT → 盈利85.7 → 资金池371.3
连续滚5次,100变成371.3,增长271%!
Случай сбоя:
第1次:100 USDT → 盈利30 → 资金池130
第2次:130 USDT → 盈利39 → 资金池169
第3次:169 USDT → 趋势反转 → 触发止损
止损比例5%,亏损:169 × 3 × 5% = 25.35 USDT
剩余资金:169 - 25.35 = 143.65 USDT
原本从100滚到169,一次止损后只剩143.65
В этом и заключается палка о двух концах при торговле на основе переноса позиций:

Проактивный выход: ослабление тренда
Эта ситуация уже рассматривалась в «Вопросе два» — после фиксации прибыли, если становится ясно, что тренд не будет способствовать дальнейшему росту, следует активно выбрать стоп-лосс. Это идеальная стратегия выхода, позволяющая оставить рынок с прибылью.
Пассивный выход: срабатывание стоп-лосса
Сейчас мы сосредоточимся на следующем: когда рынок движется против нас и цена достигает линии стоп-лосса, мы вынуждены закрыть свои позиции.
Многие не любят стоп-лосс ордера, потому что:
Однако в стратегии скользящего положения,Стоп-лосс — это важнейший фактор выживания.Подумайте об этом:
如果没有止损:
第1次:100 → 滚到 169
第2次:169 → 趋势反转,不止损
价格持续下跌:169 → 150 → 120 → 80 → 50...
最终可能全亏,甚至爆仓
如果有止损:
第1次:100 → 滚到 169
第2次:169 → 趋势反转,触发止损
止损5%:亏损 25.35
剩余:143.65
虽然亏了,但保留了大部分资金
可以等待下一个机会
Суть стоп-лосса:Используйте небольшие, гарантированные убытки, чтобы избежать крупных, неопределенных рисков.
// 检查止损
function checkStopLoss(currentPrice, position) {
var totalDrawdown = 0;
// 计算当前回撤
if (currentDirection == "LONG") {
totalDrawdown = (currentPrice - entryPrice) / entryPrice;
} else {
totalDrawdown = (entryPrice - currentPrice) / entryPrice;
}
// 判断是否触发止损
if (totalDrawdown < -StopLossPercent) {
Log("❌ 触发止损!回撤:", (totalDrawdown * 100).toFixed(2), "%");
// 1. 取消止盈单
if (takeProfitOrderId) {
Log("取消止盈单:", takeProfitOrderId);
exchange.CancelOrder(takeProfitOrderId);
takeProfitOrderId = null;
Sleep(500);
}
// 2. 市价平仓(循环重试直到成功)
var profit = closePositionMarketWithRetry(currentPrice, position);
// 3. 更新策略资金池
strategyCapital += profit; // profit是负数
totalProfitRealized += profit;
Log("止损亏损:", profit.toFixed(2), "U");
Log("策略剩余资金:", strategyCapital.toFixed(2), "U");
// 4. 记录本轮止损亏损
currentRoundLoss = Math.abs(profit);
Log("本轮止损亏损:", currentRoundLoss.toFixed(2), "U");
// 5. 保存本轮滚仓记录(被止损中断)
saveRollRecord(true); // true表示止损结束
// 6. 重置状态
resetPositionState();
// 7. 检查资金是否充足
if (strategyCapital < 10) {
Log("💥 策略资金不足10U,停止运行");
throw "资金不足";
}
Log("⏳ 已止损,等待新信号...");
}
}
Помните «Рациональную игру искателей приключений», о которой мы говорили? В этой игре есть четкое условие окончания:
Условие 1: Общий объем капитала обнуляется.
if (strategyCapital <= 0) {
Log("💥 游戏结束:资金池已归零");
Log("本次冒险失败,100 USDT全部亏光");
throw "资金耗尽";
}
Условие 2: Добровольный отказ от участия
if (strategyCapital >= 目标金额) {
Log("🎉 达到目标金额,可以选择主动退出");
Log("锁定利润,开始新一轮100 USDT的游戏");
}
Условие 3: Достичь максимального количества переворотов
if (连续滚仓次数 >= 10次) {
Log("⚠️ 达到最大滚仓次数,主动退出");
Log("持续时间太长,风险累积,见好就收");
saveRollRecord(false);
resetPositionState();
}
Суть всей стратегии открытия скользящих позиций заключается в следующем:Поиск баланса между риском и доходностью.:
Доходная сторона:
Риск:
TRUMP_USDTАнализ результатов тестирования стратегии на бирже Binance Futures в первый день листинга (с 20 по 21 января 2025 года):


Результаты тестирования показывают, что:
Основные моменты:
Риск воздействия:
Ключевые данные:
На основе проведенного анализа становится ясно, что данная стратегия по сути является симуляцией:
Торговое поведение рационального искателя приключений:
Его основная логика такова:
Ограничение 1: Зависимость от трендовых рынков.
Эта стратегия показывает плохие результаты на нестабильных рынках, потому что:
Ограничение 2: Чувствительность к параметрам
Такие параметры, как целевая прибыль в 10% и стоп-лосс в 5%, не являются оптимальными:
Ограничение 3: Непредсказуемая точка детонации
Как уже упоминалось ранее, использование технических индикаторов для входа на рынок по сути является азартной игрой:
Вариант 1: Фильтрация валют на основе рабочего процесса.
Направление 2: Динамическая настройка параметров
Направление 3: Параллельное функционирование нескольких инвестиционных пулов
С помощью трех ключевых вопросов мы наглядно продемонстрировали, как перевести торговую идею переноса позиций в программную логику. Суть этого процесса заключается в следующем:Опишите торговый образ мышления рационального человека, склонного к риску, используя точные правила и структуры данных.
Важное примечание:
Это всего лишь симуляция стратегии скользящих позиций. В реальности скользящие позиции — это торговая стратегия, требующая большого опыта работы на рынке. Эта стратегия — всего лишь инструмент. Впоследствии её можно будет комбинировать с рабочими процессами для выявления популярных или стремительно растущих криптовалют. Использование этого инструмента принесёт нам ещё больше сюрпризов.
Помните:
Не существует стратегии, гарантирующей прибыль.Перенос позиций — всего лишь инструмент. Истинный успех или неудача зависят от вашей способности:
Желаю всем вам удачи на вашем пути в количественной торговле!
Полный адрес полиса:**Исходный код стратегии -> ** https://www.fmz.com/strategy/521864
Полный код стратегии:
”`js /*backtest start: 2025-01-20 00:00:00 end: 2025-01-21 00:00:00 period: 1m basePeriod: 1m exchanges: [{“eid”:“Futures_Binance”,“currency”:“TRUMP_USDT”,“balance”:5000}] */
// ============================================ // 滚仓策略 - EMA5/EMA10 简化版 // 使用 CreateOrder 统一下单 // 持续检测订单状态 // 止盈后根据EMA关系决定是否滚仓 // 新增:滚仓统计功能(三个两行表格) // 修复:方向记录、亏损记录、入场价格记录 // 优化:市价平仓循环重试直到成功 // 优化:滚仓统计表格新增开始/结束时间 // ============================================
// ========== 策略参数(可调整)========== var Symbol = “TRUMP_USDT.swap”; // 交易币种 var InitialCapital = 100; // 策略初始资金 100U var Leverage = 3; // 杠杆倍数 var RollProfitPercent = 0.10; // 滚仓盈利系数(10% = 0.10) var StopLossPercent = 0.05; // 止损系数(10% = 0.10)
// EMA参数 var FastEMA = 5; var SlowEMA = 10;
// 全局变量 var strategyCapital = InitialCapital; var entryPrice = 0; var lastRollPrice = 0; var rollCount = 0; var totalProfitRealized = 0; var currentDirection = “”; var takeProfitOrderId = null; // 止盈单ID var amountPrecision = 0; // 数量精度 var pricePrecision = 2; // 价格精度 var ctVal = 1; // 合约面值
// ========== 滚仓统计变量 ========== var currentRoundRolls = 0; // 本轮滚仓次数(连续滚仓) var currentRoundStartTime = 0; // 本轮开始时间 var currentRoundDirection = “”; // 本轮方向 var currentRoundTotalProfit = 0; // 本轮累计盈利(每次止盈累加) var currentRoundLoss = 0; // 本轮亏损(止损时记录) var currentRoundEntryPrice = 0; // 本轮入场价格 var rollHistory = []; // 滚仓历史记录 var maxHistoryRecords = 10; // 保留最近10次滚仓记录
function main() { Log(“=== EMA滚仓策略启动(CreateOrder模式 + 滚仓统计)===”); Log(“交易币种:”, Symbol); Log(“━━━━━━━━━━━━━━━━━━━━”);
// 获取市场信息
var markets = exchange.GetMarkets();
if (!markets || !markets[Symbol]) {
Log("❌ 错误:无法获取", Symbol, "的市场信息");
return;
}
var marketInfo = markets[Symbol];
amountPrecision = marketInfo.AmountPrecision;
pricePrecision = marketInfo.PricePrecision || 2;
ctVal = marketInfo.CtVal;
Log("市场信息:");
Log(" - 数量精度:", amountPrecision);
Log(" - 价格精度:", pricePrecision);
Log(" - 合约面值:", ctVal);
var account = _C(exchange.GetAccount);
Log("账户总资金:", account.Balance.toFixed(2), "U");
Log("策略使用资金:", InitialCapital, "U");
Log("杠杆倍数:", Leverage, "倍");
Log("滚仓系数:", (RollProfitPercent * 100), "%");
Log("止损系数:", (StopLossPercent * 100), "%");
Log("━━━━━━━━━━━━━━━━━━━━");
if (account.Balance < InitialCapital) {
Log("❌ 错误:账户余额不足");
return;
}
exchange.SetContractType("swap");
exchange.SetMarginLevel(Leverage);
var lastBarTime = 0;
while (true) {
var records = _C(exchange.GetRecords, PERIOD_M1);
if (records.length < SlowEMA + 5) {
Sleep(3000);
continue;
}
var currentBarTime = records[records.length - 1].Time;
if (currentBarTime == lastBarTime) {
Sleep(1000);
continue;
}
lastBarTime = currentBarTime;
var ticker = _C(exchange.GetTicker);
var currentPrice = ticker.Last;
var account = _C(exchange.GetAccount);
var position = _C(exchange.GetPositions);
// 计算EMA
var emaFast = TA.EMA(records, FastEMA);
var emaSlow = TA.EMA(records, SlowEMA);
if (emaFast.length < 3 || emaSlow.length < 3) {
Sleep(3000);
continue;
}
var ema5_current = emaFast[emaFast.length - 1];
var ema5_prev = emaFast[emaFast.length - 2];
var ema10_current = emaSlow[emaSlow.length - 1];
var ema10_prev = emaSlow[emaSlow.length - 2];
var isBullTrend = ema5_current > ema10_current;
var isBearTrend = ema5_current < ema10_current;
var bullCross = ema5_prev <= ema10_prev && ema5_current > ema10_current;
var bearCross = ema5_prev >= ema10_prev && ema5_current < ema10_current;
if(takeProfitOrderId){
checkTakeProfitOrder();
}
// ========== 持仓逻辑 ==========
if (position.length > 0) {
var pos = position[0];
currentDirection = pos.Type == PD_LONG ? "LONG" : "SHORT";
if (entryPrice == 0) {
entryPrice = pos.Price;
lastRollPrice = pos.Price;
}
// 检查止损
checkStopLoss(currentPrice, pos);
} else {
// ========== 空仓:等待信号 ==========
if (bullCross) {
Log("📈 金叉信号 - 做多");
openPosition("LONG", currentPrice);
} else if (bearCross) {
Log("📉 死叉信号 - 做空");
openPosition("SHORT", currentPrice);
}
}
showStatus(account, position, currentPrice, ema5_current, ema10_current, isBullTrend, currentBarTime);
Sleep(1000);
}
}
// 开仓(持续检测订单状态) function openPosition(direction, price) { Log(“🚀 开仓”, direction == “LONG” ? “做多” : “做空”); Log(“使用资金:”, strategyCapital.toFixed(2), “U”);
var positionValue = strategyCapital * Leverage;
var amount = _N(positionValue / price / ctVal, amountPrecision);
Log("计算数量:", amount, "| 持仓价值:", positionValue.toFixed(2), "U");
if (amount <= 0) {
Log("❌ 数量无效");
return false;
}
// 使用 CreateOrder 市价开仓
var orderId = exchange.CreateOrder(Symbol, direction == "LONG" ? "buy" : "sell", -1, amount);
if (!orderId) {
Log("❌ 下单失败");
return false;
}
Log("订单ID:", orderId, "开始持续检测...");
// 持续检测订单状态,直到成交或超时
var maxWaitTime = 30000; // 最多等待30秒
var startTime = Date.now();
var checkCount = 0;
while (Date.now() - startTime < maxWaitTime) {
Sleep(500);
checkCount++;
var order = exchange.GetOrder(orderId);
if (!order) {
Log("❌ 无法获取订单信息");
continue;
}
if (order.Status == 1) {
// 订单已成交
var avgPrice = order.AvgPrice;
entryPrice = avgPrice;
lastRollPrice = avgPrice;
currentDirection = direction;
// ========== 修改:无论是否第一次,都要初始化/更新统计数据 ==========
if (currentRoundRolls == 0) {
// 第一次开仓:初始化所有统计数据
currentRoundStartTime = Date.now();
currentRoundDirection = direction;
currentRoundTotalProfit = 0;
currentRoundLoss = 0;
currentRoundEntryPrice = avgPrice;
Log("🆕 开始新一轮交易统计");
Log(" - 开始时间:", _D(currentRoundStartTime));
Log(" - 方向:", direction == "LONG" ? "🟢 多头" : "🔴 空头");
Log(" - 入场价格:", avgPrice.toFixed(pricePrecision));
} else {
// 滚仓时:更新方向(理论上应该相同,但为了健壮性还是更新)
currentRoundDirection = direction;
Log("🔄 滚仓操作 (第", currentRoundRolls, "次)");
Log(" - 方向:", direction == "LONG" ? "🟢 多头" : "🔴 空头");
Log(" - 入场价格:", avgPrice.toFixed(pricePrecision));
}
Log("✅ 开仓成功!");
Log(" - 成交均价:", avgPrice.toFixed(pricePrecision));
Log(" - 成交数量:", order.DealAmount);
Log(" - 成交金额:", (order.DealAmount * avgPrice * ctVal).toFixed(2), "U");
// 挂止盈单
Sleep(1000);
placeTakeProfitOrder(direction, avgPrice, order.DealAmount);
return true;
} else if (order.Status == 2) {
// 订单已取消
Log("❌ 订单已取消");
return false;
}
// Status == 0 表示未成交,继续等待
}
// 超时未成交
Log("⚠️ 订单超时,尝试取消订单");
exchange.CancelOrder(orderId);
return false;
}
// 挂止盈单 function placeTakeProfitOrder(direction, entryPrice, amount) { var takeProfitPrice = 0;
if (direction == "LONG") {
takeProfitPrice = _N(entryPrice * 1.1, pricePrecision); // 多头止盈:+10%
} else {
takeProfitPrice = _N(entryPrice * 0.9, pricePrecision); // 空头止盈:-10%
}
Log("📌 挂止盈单");
Log(" - 入场价格:", entryPrice.toFixed(pricePrecision));
Log(" - 止盈价格:", takeProfitPrice);
Log(" - 数量:", amount);
// 使用 CreateOrder 挂限价止盈单
if (direction == "LONG") {
takeProfitOrderId = exchange.CreateOrder(Symbol, "closebuy", takeProfitPrice, amount);
} else {
takeProfitOrderId = exchange.CreateOrder(Symbol, "closesell", takeProfitPrice, amount);
}
if (takeProfitOrderId) {
Log("✅ 止盈单已挂,订单ID:", takeProfitOrderId);
} else {
Log("❌ 止盈单挂单失败");
}
}
// 检查止盈单状态 function checkTakeProfitOrder() {
if (!takeProfitOrderId) {
return;
}
var order = exchange.GetOrder(takeProfitOrderId);
if (!order) {
return;
}
if (order.Status == 1) {
// 止盈单成交
Log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Log("💰 止盈单成交!");
Log(" - 成交价格:", order.AvgPrice.toFixed(pricePrecision));
Log(" - 成交数量:", order.DealAmount);
// 使用订单数据精确计算盈利
var profit = 0;
if (currentDirection == "LONG") {
// 多头盈利 = (止盈价 - 入场价) * 数量 * 合约面值
profit = (order.AvgPrice - entryPrice) * order.DealAmount * ctVal;
} else {
// 空头盈利 = (入场价 - 止盈价) * 数量 * 合约面值
profit = (entryPrice - order.AvgPrice) * order.DealAmount * ctVal;
}
// 计算盈利率
var profitRate = profit / strategyCapital;
Log("📊 盈利统计:");
Log(" - 入场价格:", entryPrice.toFixed(pricePrecision));
Log(" - 止盈价格:", or