В процессе загрузки ресурсов... загрузка...

Сигнал TradingView - V 0.1.10

Автор:Бобы, Дата: 2024-11-22 14:31:55
Тэги:

По сообщению телеканала, в котором появились сигналы, сообщение гласит: OpenLong - больше Открытый OpenShort CloseLong - это просто CloseShort Плоский

Название оповещения заполняется в любом месте, а содержание сообщения на английском языке (только на английском языке, без пробелов и других символов) Не забудьте заполнить правильный URL-адрес вебхука

Ссылка:https://www.fmz.com/bbs-topic/5533

Объясните:

Принимать сигнал остановки от телевизора, но передавать телевизионный сигнал после окончания K-бара, а остановка в этом сценарии предназначена для предотвращения вставки; Ограничитель может быть чуть больше, чем у телевизора, примерно в 0,25 раза больше, чем у телевизора (имеется в виду малый ограничитель 0,75%).


////TradingView信号
////从TradingView接收开仓、平仓信号(OpenLong/CloseLong,OpenShort/CloseShort)执行开仓、平仓操作。
////记得填写正确的Webhook URL   参考地址:https://www.fmz.com/bbs-topic/5533

////暂时只测试了U本位交易。
////只能做一个多单,如果当前有多单,不再继续开多;同样的逻辑适用于空单;  0.1.0

////将夏天不打你的代码融入进来  V 0.1.3 2023年4月29日 03:35:31  策略地址:https://www.fmz.com/strategy/320782(源代码已经不再维护)
////夏天不打你的微信:wd1061331106

////V0.1.9//根据原来的0.1.4进行了修改,重启后可以更新止盈止损价格;2024年11月22日 15:38:45
////如果遇到交易平台发送的Too many requests的消息,不会停机(非常棒)。
////V0.1.10 修正了挂单止盈成交之后,收到平仓信息报错的问题。2024年11月22日 21:20:00




// 参数设置

// 策略参数变量
// 基本设置
var _Currency = TradeCurrency;                                  // 交易对
var _Interval = Interval;                                       // 程序运行周期
var _UseQuarter = UseQuarter;                                   // 季度合约,默认USDT永续合约
var _EnableMessageSend = EnableMessageSend;                     // 推送消息
// 下单设置
var _MarginLevel = MarginLevel;                                 // 杠杆倍数
var _OrderSize = OrderSize;                                     // 下单数量/张数
var _OrderByMargin = OrderByMargin;                             // 根据下单保证金来确定下单数量
var _OrderMarginPercent = OrderMarginPercent;                   // 下单保证金百分比%(根据初始资金计算)
var _PricePrecision = PricePrecision;                           // 下单价格精度
var _AmountPrecision = AmountPrecision;                         // 下单数量精度
var _OneSizeInCurrentCoin = OneSizeInCurrentCoin;               // U本位合约中,一张ETH所代表的ETH数量
var _QuarterOneSizeValue = QuarterOneSizeValue;                 // 币本位合约中,一张ETH所代表的USDT数量
// 止盈止损
var _UseStopLoss = UseStopLoss;                                 // 使用止损
var _TakeProfitPercent = TakeProfitPercent;                     // 止盈价格百分比%
var _TakeProfitAmount  = TakeProfitAmount;                      // 止盈数量百分比%
var _TakeProfitPrice   = 0;                                     // 止盈价格,用于显示
var _StopLossPercent   = StopLossPercent;                       // 止损价格百分比
var _UseTrackingTakeProfit = UseTrackingTakeProfit;             // 使用回调止盈
var _CallBakcPercent = CallBakcPercent;                         // 回调百分比%

var takeProfitOrderId;              //记录止盈单1

var ALLPrice_zhicheng = 0;          //记录支撑价格


// 统计变量
var _InitAsset = 0;
var _ProfitLocal = 0;
var _TakeProfitCount = 0;
var _TradeCount = 0;
var StrategyRunTimeStampString = "strategy_run_time";
var _StrategyDatas = { start_run_timestamp: 0, others: "" };
var _UserDatas = null;

// 相对固定参数
var _MaintenanceMarginRate = 0.004  // 维持保证金率
var _TakerFee = 0.0005;             // 吃单手续费
var _IsUsdtStandard = false;        // USDT本位开单和结算


var stopLossPrice = 0;              //用于记录支撑价格,方便进行止损操作;





// 全局变量
var position;
var baseMargin;
var assetPrecision;//获取资产精度,并将其存储在全局变量中:
var pricePrecision; // 在全局变量中添加一个新变量,用于存储价格精度
var DEFAULT_ASSET_PRECISION = 3;
var DEFAULT_PRICE_PRECISION = 2; // 在这里添加价格精度的默认值
var hasOpenedLong = false; // 跟踪是否已经开过多单
var hasOpenedShort = false; // 跟踪是否已经开过空单
var _HasTakeProfit = false; //是否止盈过?
var _TriggeredTakeProfit = false;                        // 是否触发了回调止盈
var _PeakPriceInPosition = 0;                            // 持仓中价格的峰值点(最高/最低点)
var lock = false;                                        //锁定止损价格一段时间,保证开仓之后不会因为TV上的止损价格未发生变化而导致的立刻平仓。
var lastUpdateTime = 0;                                  //记录上次止损价格更新时间的变量

// 保存程序起始运行时间 秒级时间戳
function saveStrategyRunTime() {
    var local_data_strategy_run_time = _G(StrategyRunTimeStampString);

    if (local_data_strategy_run_time == null) {
        _StrategyDatas.start_run_timestamp = Unix();
        _G(StrategyRunTimeStampString, _StrategyDatas.start_run_timestamp);
    }
    else {
        _StrategyDatas.start_run_timestamp = local_data_strategy_run_time;
    }
}

// 设置程序起始运行时间 秒级时间戳
function setStrategyRunTime(timestamp) {
    _G(StrategyRunTimeStampString, timestamp);
    _StrategyDatas.start_run_timestamp = timestamp;
}

// 计算两个时间戳之间的天数,参数是秒级时间戳
function getDaysFromTimeStamp(start_time, end_time) {
    if (end_time < start_time)
        return 0;

    return Math.trunc((end_time - start_time) / (60 * 60 * 24));
}

// 保存数据到本地
function saveUserDatasLocal() {
    _UserDatas = {
        init_assets: _InitAsset,
        profit_local: _ProfitLocal,
        take_profit_count: _TakeProfitCount,
        trade_count: _TradeCount
    };
    // 存储到本地
    _G(exchange.GetLabel(), _UserDatas);
    Log("已把所有数据保存到本地.");
}

// 读取用户本地数据,程序启动时候运行一次
function readUserDataLocal() {
    var user_data = _G(exchange.GetLabel());
    if (user_data == null) {
        _InitAsset = getAccountAsset(_C(exchange.GetPosition), _C(exchange.GetAccount), _C(exchange.GetTicker));
        _UserDatas = {
            init_assets: _InitAsset,
            profit_local: 0,
            take_profit_count: 0,
            trade_count: 0
        };
    } else {
        _UserDatas = user_data;
    }
}

// 清除用户本地数据,交互按钮点击运行
function clearUserDataLocal() {
    _G(exchange.GetLabel(), null);
    Log(exchange.GetLabel(), ":已清除本地数据.");
}

//将购买数量调整为合适的精度:
function fixAmountPrecision(amount, _AmountPrecision) {
    return parseFloat(amount.toFixed(_AmountPrecision));
}

// 添加一个新函数,用于将价格调整到合适的精度
function fixPricePrecision(price, _PricePrecision) {
    return parseFloat(price.toFixed(_PricePrecision));
}



// 策略交互
function runCmd(cmd) {

    if (cmd) {
        // 检测交互命令
        Log("接收到的命令:", cmd, "#FF1CAE");
        if (cmd.indexOf("ClearLocalData:") == 0) {
            // 清除本地数据
            clearUserDataLocal();
        } else if (cmd.indexOf("SaveLocalData:") == 0) {
            // 保存数据到本地
            saveUserDatasLocal();
        } else if (cmd.indexOf("ClearLog:") == 0) {
            // 清除日志
            var log_reserve = cmd.replace("ClearLog:", "");
            LogReset(Number(log_reserve));
        } else if (cmd.indexOf("OrderSize:") == 0) {
            // 修改下单张数
            if (_OrderByMargin) {
                Log("已经使用保证金数量来下单,无法直接修改下单数量!");
            } else {
                var order_size = Number(cmd.replace("OrderSize:", ""));
                _OrderSize = order_size;
                Log("下单张数已经修改为:", _OrderSize);
            }
        } else if (cmd.indexOf("OrderMarginPercent:") == 0) {
            // 修改下单保证金百分比
            if (_OrderByMargin) {
                var order_margin_percent = Number(cmd.replace("OrderMarginPercent:", ""));
                _OrderMarginPercent = order_margin_percent;
                Log("下单保证金百分比:", _OrderMarginPercent, "%");
            } else {
                Log("没有打开根据保证金数量下单,无法修改下单保证金百分比!");
            }
        }
    }
}




//// 检查重启后是否存在多单或空单
function initialize() {

    // 检查重启后是否存在多单或空单
    var positions = exchange.GetPosition();
    for (var i = 0; i < positions.length; i++) {
        if (positions[i].Type === PD_LONG || positions[i].Type === PD_LONG_YD) {
            hasOpenedLong = true;
            stopLossPrice = positions[i].Price * (1 - _StopLossPercent / 100);//更新止损价格--多单
            Log("存在多单,止损价格更新:",stopLossPrice);

        } else if (positions[i].Type === PD_SHORT || positions[i].Type === PD_SHORT_YD) {
            hasOpenedShort = true;
            stopLossPrice = positions[i].Price * (1 + _StopLossPercent / 100);//更新止损价格--空单//更新止损价格--空单
            Log("存在空单,止损价格更新:",stopLossPrice);
        }
    }
}


// 交易函数
function orderDirectly(distance, price, amount) {
    var tradeFunc = null;

    if (amount <= 0) {
        throw "设置的参数有误,下单数量已经小于0!"
    }

    if (distance == "buy") {
        tradeFunc = exchange.Buy;
    } else if (distance == "sell") {
        tradeFunc = exchange.Sell;
    } else if (distance == "closebuy") {
        tradeFunc = exchange.Sell;
        Log("空单平仓:",price,"数量:",amount);
    } else {
        tradeFunc = exchange.Buy;
        Log("多单平仓:",price,"数量:",amount);
    }
    exchange.SetDirection(distance);
    return tradeFunc(price, amount);
}


function openLong(price, amount) {
    var real_amount = getRealOrderSize(price, amount);
    return orderDirectly("buy", price, real_amount);
}

function openShort(price, amount) {
    var real_amount = getRealOrderSize(price, amount);
    return orderDirectly("sell", price, real_amount);
}

function coverLong(price, amount) {//多单平仓
    
    if (takeProfitOrderId) {// 取消止盈单
        exchange.CancelOrder(takeProfitOrderId);
        Log("取消多单止盈单");
        takeProfitOrderId = null;
        _TakeProfitPrice = 0;     //更新止盈价格
    }
    Sleep(200);

    return orderDirectly("closebuy", price, amount);
}

function coverShort(price, amount) {//平仓空单
    
    if (takeProfitOrderId) {// 取消止盈单
        exchange.CancelOrder(takeProfitOrderId);
        Log("取消空单止盈单");
        takeProfitOrderId = null;
        _TakeProfitPrice = 0;     //更新止盈价格
    }
    Sleep(200);

    return orderDirectly("closesell", price, amount);
}

// 重新计算下单数量
function getRealOrderSize(price, amount) {
    var real_price = price == -1 ? _C(exchange.GetTicker).Last : price;
    if (_OrderByMargin) {
        if (_IsUsdtStandard) {
            _OrderSize = _N(_InitAsset * (_OrderMarginPercent / 100) / real_price * _MarginLevel / _OneSizeInCurrentCoin, _AmountPrecision);
        } else {
            _OrderSize = _N(_InitAsset * (_OrderMarginPercent / 100) * _MarginLevel * real_price / _QuarterOneSizeValue, _AmountPrecision);
        }
    } else {
        _OrderSize = amount;
    }
    return _OrderSize;
}

// 获取单向持仓的收益和收益%
function getSinglePositionProfit(position, ticker) {
    if (position.length == 0)
        return [0, 0];

    var price = ticker.Last;
    var position_margin = getSinglePositionMargin(position, ticker);

    var position_profit_percent = position[0].Type == PD_LONG ? ((price - position[0].Price) / position[0].Price) * _MarginLevel : ((position[0].Price - price) / position[0].Price) * _MarginLevel;
    var position_profit = position_margin * position_profit_percent;

    return [position_profit, position_profit_percent];
}

// 计算强平价格
function calculateForcedPrice(account, position, ticker) {
    var position_profit = 0;
    var total_avail_balance = 0;
    var forced_price = 0;

    var position_margin = getSinglePositionMargin(position, ticker);
    [position_profit, position_profit_percent] = getSinglePositionProfit(position, ticker);

    if (_IsUsdtStandard) {
        total_avail_balance = position_profit > 0 ? account.Balance + position_margin + account.FrozenBalance - position_profit : account.Balance + position_margin + account.FrozenBalance;
        if (position[0].Type == PD_LONG) {
            forced_price = (((_MaintenanceMarginRate + _TakerFee) * _MarginLevel * account.FrozenBalance - total_avail_balance) / _OneSizeInCurrentCoin + (position[0].Amount * position[0].Price))
                / (position[0].Amount - (_MaintenanceMarginRate + _TakerFee) * position[0].Amount);
        } else {
            forced_price = (((_MaintenanceMarginRate + _TakerFee) * _MarginLevel * account.FrozenBalance - total_avail_balance) / _OneSizeInCurrentCoin - (position[0].Amount * position[0].Price))
                / (-1 * position[0].Amount - (_MaintenanceMarginRate + _TakerFee) * position[0].Amount);
        }
    } else {
        total_avail_balance = position_profit > 0 ? account.Stocks + position_margin + account.FrozenStocks - position_profit : account.Stocks + position_margin + account.FrozenStocks;
        if (position[0].Type == PD_LONG) {
            forced_price = (_MaintenanceMarginRate * position[0].Amount + position[0].Amount) / (total_avail_balance / _QuarterOneSizeValue + position[0].Amount / position[0].Price);
        } else {
            forced_price = (_MaintenanceMarginRate * position[0].Amount - position[0].Amount) / (total_avail_balance / _QuarterOneSizeValue - position[0].Amount / position[0].Price);
        }
    }

    if (forced_price < 0)
        forced_price = 0;

    return forced_price;
}

// 计算最大可下单张数
function getMaxOrderSize(margin_level, ticker, account) {
    var max_order_size = 0;

    if (_IsUsdtStandard) {
        max_order_size = account.Balance * margin_level / (_OneSizeInCurrentCoin * ticker.Last);
    } else {
        max_order_size = account.Stocks * ticker.Last / _QuarterOneSizeValue * margin_level;
    }

    return _N(max_order_size, _AmountPrecision);
}

// 获取单个持仓占用保证金 
function getSinglePositionMargin(position, ticker) {
    var position_margin = 0;

    if (position.length > 0) {
        if (_IsUsdtStandard) {
            position_margin = position[0].Amount * _OneSizeInCurrentCoin * ticker.Last / _MarginLevel;
        } else {
            position_margin = position[0].Amount * _QuarterOneSizeValue / ticker.Last / _MarginLevel;
        }
    }

    return position_margin;
}

// 获取账户资产
function getAccountAsset(position, account, ticker) {
    // 计算不同情况下的账户初始资产
    var account_asset = 0;
    var position_margin = getSinglePositionMargin(position, ticker);

    if (_IsUsdtStandard) {
        if (position.length > 0) {
            account_asset = account.Balance + account.FrozenBalance + position_margin;
        } else {
            account_asset = account.Balance + account.FrozenBalance;
        }
    } else {
        if (position.length > 0) {
            account_asset = account.Stocks + account.FrozenStocks + position_margin;
        } else {
            account_asset = account.Stocks + account.FrozenStocks;
        }
    }

    return account_asset;
}

// 收益统计
function calculateProfit(ticker) {
    // 重新获取一下账户持仓与资产
    var position = _C(exchange.GetPosition);
    var account = _C(exchange.GetAccount);
    // 当前总收益 - 上一次总收益 = 本次的收益
    var current_profit = (getAccountAsset(position, account, ticker) - _InitAsset) - _ProfitLocal;
    _ProfitLocal += current_profit;

    if (current_profit > 0) {
        _TakeProfitCount++;
    }
    _TradeCount++;

    LogProfit(_N(_ProfitLocal, 4), "        本次收益:", _N(current_profit, 6));
    Log(getAccountAsset(position, account, ticker), 4), "        当前资金:", _N(getAccountAsset(position, account, ticker), 6);
    saveUserDatasLocal();
}

// 是否还够资金下单
function isEnoughAssetToOrder(order_size, ticker) {
    var is_enough = true;
    var account = _C(exchange.GetAccount);

    if (_IsUsdtStandard) {
        if (account.Balance < order_size * ticker.Last * _OneSizeInCurrentCoin / _MarginLevel) {
            is_enough = false;
        }
    } else {
        if (account.Stocks < order_size * _QuarterOneSizeValue / ticker.Last / _MarginLevel) {
            is_enough = false;
        }
    }

    return is_enough;
}

// 止损---------------------
function stopLoss(stopLossPrice, position, ticker) {
    var stop_loss_price = 0;
    var price = ticker.Last;

    if (position.length == 1 && _UseStopLoss) {
        if (position[0].Type == PD_LONG) {
            stop_loss_price = stopLossPrice;    //设定止损价格
            if (price < stop_loss_price) {
                coverLong(-1, position[0].Amount);
                _HasTakeProfit = false;
                calculateProfit(ticker);            //统计收益
                _TrendWhenTakeProfitOrStopLoss = 1;
                _HadStopLoss = true;
                Log("多单止损。止损价格:", _N(stop_loss_price, 6), ", 持仓价格:", _N(position[0].Price), _EnableMessageSend ? "@" : "#FF1CAE");
            }
        } else if (position[0].Type == PD_SHORT) {
            stop_loss_price = stopLossPrice;    //设定止损价格
            if (price > stop_loss_price) {
                coverShort(-1, position[0].Amount);
                _HasTakeProfit = false;
                calculateProfit(ticker);            //统计收益
                _TrendWhenTakeProfitOrStopLoss = -1;
                _HadStopLoss = true;
                Log("空单止损。止损价格:", _N(stop_loss_price, 6), ", 持仓价格:", _N(position[0].Price), _EnableMessageSend ? "@" : "#FF1CAE");
            }
        }
    }
}

// 止盈
function takeProfit(position, ticker) {
    var take_profit_price = 0;
    var price = ticker.Last;

    if (position.length == 1) {
        if (position[0].Type == PD_LONG) {
            take_profit_price = position[0].Price * (1 + _TakeProfitPercent / 100);         //设定止盈价格
            _TakeProfitPrice = take_profit_price;                                         //记录止盈价格
            if (price > take_profit_price) {
                
                var takeProfitAmount = fixAmountPrecision(position[0].Amount * _TakeProfitAmount / 100, _AmountPrecision); // 调整数量精度
                //coverLong(-1, takeProfitAmount);
                _HasTakeProfit = true;
                calculateProfit(ticker);            //统计收益
                _TrendWhenTakeProfitOrStopLoss = 1;
                _HadTakeProfit = true;
                Log("多单止盈。止盈价格:", _N(take_profit_price, 6), ", 持仓价格:", _N(position[0].Price), _EnableMessageSend ? "@" : "#FF1CAE");
                _TriggeredTakeProfit = true;    //打开回调止盈开关
                _PeakPriceInPosition = price;   // 记录价格高点
                takeProfitOrderId = null;
            }
        } else if (position[0].Type == PD_SHORT) {
            take_profit_price = position[0].Price * (1 - _TakeProfitPercent / 100);
            _TakeProfitPrice = take_profit_price;                                         //记录止盈价格
            if (price < take_profit_price) {
                var takeProfitAmount = fixAmountPrecision(position[0].Amount * _TakeProfitAmount / 100, _AmountPrecision); // 调整数量精度
                //coverShort(-1, takeProfitAmount);
                _HasTakeProfit = true;
                calculateProfit(ticker);            //统计收益
                _TrendWhenTakeProfitOrStopLoss = -1;
                _HadTakeProfit = true;
                Log("空单止盈。止盈价格:", _N(take_profit_price, 6), ", 持仓价格:", _N(position[0].Price), _EnableMessageSend ? "@" : "#FF1CAE");
                _TriggeredTakeProfit = true;   //回调止盈开关打开
                _PeakPriceInPosition = price;  // 记录价格高点
                takeProfitOrderId = null;
            }
        }
    }
}

// 回调止盈
function trackingTakeProfit(position, ticker) {
    var take_profit_price = 0;
    var trigger_price = 0;
    var price = ticker.Last;

    if (position.length > 0 && _UseTrackingTakeProfit) {
        if (position[0].Type == PD_LONG) {
            // 多单持仓
            if (price > position[0].Price * (1 - _CallBakcPercent / 100))
            {_TriggeredTakeProfit = true;}

            if (_TriggeredTakeProfit) {// 止盈回调代码(开关开启,监控是否止盈)
                
                _PeakPriceInPosition = price > _PeakPriceInPosition ? price : _PeakPriceInPosition;   // 更新价格高点
                take_profit_price = _PeakPriceInPosition * (1 - _CallBakcPercent / 100);              // 计算回调的止盈价格
                if (price < take_profit_price) {//如果价格低于回调止盈价格,则止盈
                    coverLong(-1, position[0].Amount);              // 平多
                    _HasTakeProfit = false;         
                    calculateProfit(ticker);                        //统计收益
                    _TriggeredTakeProfit = false;                   // 复位触发标记
                    _HadTakeProfit = true;                          // 记录发生了止盈
                    Log("多单回调止盈:持仓中价格高点:", _N(_PeakPriceInPosition, 6), ", 止盈价格:", _N(take_profit_price, 6), ", 当前价格:", _N(price, 6),
                        ", 持仓价格:", _N(position[0].Price, 6), _EnableMessageSend ? "@" : "#FF1CAE");
                }
            }
        } else if (position[0].Type == PD_SHORT) {
            // 空单持仓
            if (price < position[0].Price * (1 + _CallBakcPercent / 100))
            {_TriggeredTakeProfit = true;}

            if (_TriggeredTakeProfit) {
                // 已达到触发价格,监控是否止盈
                _PeakPriceInPosition = price < _PeakPriceInPosition ? price : _PeakPriceInPosition;        // 更新价格低点
                take_profit_price = _PeakPriceInPosition * (1 + _CallBakcPercent / 100);                   // 计算回调的止盈价格

                if (price > take_profit_price) {
                    coverShort(-1, position[0].Amount);             // 平空
                    _HasTakeProfit = false;
                    calculateProfit(ticker);                        //统计收益
                    _TriggeredTakeProfit = false;                   // 复位触发标记
                    _HadTakeProfit = true;                          // 记录发生了止盈
                    Log("空单回调止盈:持仓中价格低点:", _N(_PeakPriceInPosition, 6), ", 止盈价格:", _N(take_profit_price, 6), ", 当前价格:", _N(price, 6),
                        ", 持仓价格:", _N(position[0].Price, 6), _EnableMessageSend ? "@" : "#FF1CAE");
                }
            }
        }
    }
}

// 下单
function order(long, short, position, ticker) {
    var position_size = position.length > 0 ? position[0].Amount : 0;
    var position_type = position.length > 0 ? position[0].Type : null;

    if (long) {
        //判断是否持仓
        if (position_size > 0 && position_type == PD_SHORT) {       //如果当前持有空仓
            coverShort(-1, position_size);      //平掉空仓
            calculateProfit(ticker);            //统计收益
        } else if (position_size > 0 && position_type == PD_LONG) { //如果当前持有多仓
            Log("持有多单,不继续开多"); // 多单持仓,不重复下单
            return;
        } else { }

        ////开多单
        if (isEnoughAssetToOrder(_OrderSize, ticker)) {             // 是否还够资金下单
            openLong(-1, _OrderSize);           //开多单
            _HadStopLoss = false;               //重置
            _HadTakeProfit = false;             //重置
            lastUpdateTime = new Date().getTime();  //开单平单之后,更新时间
            Sleep(500);

            // 挂多单止盈单
            try {
                var takeProfitPrice = ticker.Buy * (1 + _TakeProfitPercent / 100);              //设定止盈价格
                takeProfitPrice = fixPricePrecision(takeProfitPrice, _PricePrecision);          //调整价格精度
                var takeProfitAmount = _OrderSize * _TakeProfitAmount / 100;                    //设定止盈数量
                takeProfitAmount = fixAmountPrecision(takeProfitAmount, _AmountPrecision);      //调整数量精度
                
                _TakeProfitPrice = takeProfitPrice;     //更新止盈价格--多单
                stopLossPrice = ticker.Buy * (1 - _StopLossPercent / 100);//更新止损价格--多单
                Log("开仓价格:",ticker.Buy,"止损价格:", stopLossPrice);      //输出开仓价格,止损价格

                exchange.SetDirection("closebuy"); // 设置止盈平多交易方向
                takeProfitOrderId = exchange.Sell(takeProfitPrice, takeProfitAmount);           //挂止盈单
                Log("挂多单止盈单,价格:", takeProfitPrice, ",数量:", takeProfitAmount);      //输出日志
            } catch (error) {
                Log("挂多单止盈单失败:", error.message, "错误详情:", error);
                takeProfitOrderId = null;
            }
        } else {
            throw "保证金不足!";
        }
    } else if (short) {
        ////判断是否持仓
        if (position_size > 0 && position_type == PD_LONG) {         //如果当前持有多仓
            coverLong(-1, position_size);       //平掉多仓
            calculateProfit(ticker);            //统计收益
        } else if (position_size > 0 && position_type == PD_SHORT) {
            Log("持有空单,不继续开空"); // 多单持仓,不重复下单// 空单持仓,不重复下单
            return;
        } else { }

        ////开空单
        if (isEnoughAssetToOrder(_OrderSize, ticker)) {
            openShort(-1, _OrderSize);          //开空单
            _HadStopLoss = false;               //重置
            _HadTakeProfit = false;             //重置
            lastUpdateTime = new Date().getTime();  //开单平单之后,更新时间
            Sleep(500);

            // 挂空单止盈单
            try {
                var takeProfitPrice = ticker.Sell * (1 - _TakeProfitPercent / 100);             //设定止盈数量
                takeProfitPrice  = fixPricePrecision( takeProfitPrice , _PricePrecision);       //调整价格精度
                var takeProfitAmount = _OrderSize * _TakeProfitAmount/100;                      //设定止盈数量
                takeProfitAmount = fixAmountPrecision(takeProfitAmount, _AmountPrecision );     //调整数量精度

                _TakeProfitPrice = takeProfitPrice;                                             //更新止盈价格--空单
                stopLossPrice = ticker.Sell * (1 + _StopLossPercent / 100);                     //更新止损价格--空单
                Log("开仓价格:",ticker.Sell,"止损价格:", stopLossPrice);                        //输出开仓价格,止损价格
                

                exchange.SetDirection("closesell");                                             //设置止盈平多交易方向
                takeProfitOrderId = exchange.Buy(takeProfitPrice, takeProfitAmount);            //挂止盈单

                Log("挂空单止盈单,价格:", takeProfitPrice, ",数量:", takeProfitAmount);      //输出日志
            } catch (error) {
                Log("挂空单止盈单失败:", error.message, "错误详情:", error);
                takeProfitOrderId = null;
            }
        } else {
            throw "保证金不足!";
        }
    }
}

// 状态栏信息打印
function printLogStatus(ticker, account, position) {
    var table_overview = { type: 'table', title: '策略总览', cols: ['开始时间', '已运行天数', '交易次数', '胜率', '预估月化%', '预估年化%', '界面显示代码来自大神“夏天不打你”'], rows: [] };
    var table_account = { type: 'table', title: '账户资金', cols: ['当前资产', '初始资产', '可用余额', '冻结余额', '可下单张数', '收益', '收益%'], rows: [] };
    var table_position = { type: 'table', title: '持仓情况', cols: ['交易币种', '杠杆倍数', '持仓均价', '方向', '数量', '保证金', '止损价格', '止盈价格', '浮动盈亏', '浮动盈亏%'], rows: [] };
    var i = 0;

    // 策略总览
    var the_running_days = getDaysFromTimeStamp(_StrategyDatas.start_run_timestamp, Unix());
    var monthly_rate_of_profit = 0;
    if (the_running_days > 1)
        monthly_rate_of_profit = _ProfitLocal / _InitAsset / the_running_days * 30;
    table_overview.rows.push([
        _D(_StrategyDatas.start_run_timestamp * 1000),                              //开始时间
        the_running_days,                                                           //已运行天数
        _TradeCount,                                                                //交易次数
        _TradeCount == 0 ? 0 : (_N(_TakeProfitCount / _TradeCount * 100, 2) + "%"), //胜率
        _N(monthly_rate_of_profit * 100, 2) + "%",                                  //预估月化%
        _N(monthly_rate_of_profit * 12 * 100, 2) + "%",                             //预估年化%
        '地址:https://www.fmz.com/strategy/320782,作者微信:wd1061331106'      //策略代谢请联系微信
        ]);


    // 账户资金
    var current_asset = getAccountAsset(position, account, ticker);
    var max_order_size = getMaxOrderSize(_MarginLevel, ticker, account);
    var asset_profit = current_asset - _InitAsset;
    var asset_profit_percent = asset_profit / _InitAsset;
    table_account.rows.push([
        _N(current_asset, 4),                                                   //当前资产
        _N(_InitAsset, 4),                                                      //初始资产
        _N(_IsUsdtStandard ? account.Balance : account.Stocks, 4),              //可用余额
        _N(_IsUsdtStandard ? account.FrozenBalance : account.FrozenStocks, 4),  //冻结余额
        max_order_size,                                                         //可下单张数
        _N(asset_profit, 4),                                                    //收益
        _N(asset_profit_percent * 100, 2) + "%"                                 //收益百分比
        ]);


    // 持仓情况
    var position_direction = "";
    var forced_cover_up_price = 0;
    var position_profit_percent = 0;
    var position_profit = 0;
    var position_margin = 0;
    if (position.length == 0) {
        table_position.rows.push(["无持仓", "-", "-", "-", "-", "-", "-", "-", "-"]);
    } else {
        position_direction = position[0].Type == PD_LONG ? "多单" : "空单";
        [position_profit, position_profit_percent] = getSinglePositionProfit(position, ticker);
        position_margin = getSinglePositionMargin(position, ticker);
        //forced_cover_up_price = calculateForcedPrice(account, position, ticker);
        table_position.rows.push([
            exchange.GetCurrency(),                         //交易币种
            _MarginLevel,                                   //杠杆倍数
            _N(position[0].Price, 4),                       //持仓均价
            position_direction,                             //方向
            position[0].Amount,                             //数量
            _N(position_margin, 4),                         //保证金
            _N(stopLossPrice, 4),                           //止损价格
            _N(_TakeProfitPrice, 4),                        //止盈价格
            _N(position_profit, 4),                         //浮动盈亏
            _N((position_profit_percent * 100), 2) + "%"    //浮动盈亏百分比
            ]);
    }
    // 打印表格
    LogStatus('`' + JSON.stringify(table_overview) + '`\n'
        + '`' + JSON.stringify(table_account) + '`\n'
        + '`' + JSON.stringify(table_position) + '`\n');
}

// 初始化数据
function initDatas() {
    saveStrategyRunTime();
    readUserDataLocal();

    _InitAsset = _UserDatas.init_assets;
    _ProfitLocal = _UserDatas.profit_local;
    _TakeProfitCount = _UserDatas.take_profit_count;
    _TradeCount = _UserDatas.trade_count;

    if (_OrderByMargin) {
        getRealOrderSize(-1, _OrderSize);
        Log("已经重新计算下单张数:", _OrderSize);
    }
}

// 设置合约
function setContract() {
    _IsUsdtStandard = _Currency.includes("USDT") ? true : false;
    if (!IsVirtual()) {
        // 实盘设置
        exchange.SetCurrency(_Currency);
        if (_UseQuarter) {
            exchange.SetContractType("quarter");
        } else {
            exchange.SetContractType("swap");
        }
    } else {
        // 回测设置
        if (_IsUsdtStandard) {
            exchange.SetContractType("swap");
        } else {
            exchange.SetContractType("quarter");
        }
    }
    exchange.SetMarginLevel(_MarginLevel);
    exchange.SetPrecision(_PricePrecision, _AmountPrecision);
}






function main() {
    setContract(); // 设置合约
    initDatas(); // 初始化数据
    initialize();//检查是否存在多单和空单
    
    while (true) {
        var command = GetCommand();                     //获取WebHook消息
        
        if (command) {                                  // 检查 command 是否为空
            Log("接收到的命令:", command, "#FF1CAE");

            TV_FMZ_Strategy(command); //开单平单操作
        }

        Take_Profit_Stop_Loss();   //止盈止损操作
        Sleep(_Interval);          // 程序运行间隔
    }
}


//止盈止损函数
function  Take_Profit_Stop_Loss(){
    var ticker = _C(exchange.GetTicker);         //获取当前交易对的市场行情信息
    var account = _C(exchange.GetAccount);       //获取交易账户信息的函数。
    var position = _C(exchange.GetPosition);     //获取当前交易所账户的持仓信息,并返回一个包含持仓信息的对象。


    printLogStatus(ticker, account, position);      // 状态栏信息打印

    if (stopLossPrice > 0) {                        //止损处理
        stopLoss(stopLossPrice, position, ticker);  // 止损
    }
    if(!_HasTakeProfit){
        takeProfit(position, ticker);               // 止盈
    }
    
    if (_TriggeredTakeProfit) {
        trackingTakeProfit(position, ticker);       // 回调止盈
    }
}



// 执行对应的操作
function TV_FMZ_Strategy(action) {
    var ticker      =   _C(exchange.GetTicker);     //获取当前交易对的市场行情信息
    var account     = _C(exchange.GetAccount);      //获取交易账户信息的函数。
    var position    =   _C(exchange.GetPosition);   //获取当前交易所账户的持仓信息,并返回一个包含持仓信息的对象。


    if (position.length > 1) {      ////如果同时持有多空仓位则退出
        Log(position);
        throw "同时有多空持仓!";
    }

    switch (action) { // 处理消息对应的操作
        case "OpenLong":
            order(1, 0, position, ticker); // 开多单
            break;
        case "OpenShort":
            order(0, 1, position, ticker); // 开空单
            break;
        case "CloseLong":
            if (position.length > 0) {
                if (position[0].Type === PD_LONG) {
                    coverLong(-1, position[0].Amount); // 平多
                    _HasTakeProfit = false;
                    _TriggeredTakeProfit = false;       // 复位回调止盈开关
                    calculateProfit(ticker);            //统计收益
                } else {
                    Log("当前持有空头持仓,无法平多");
                }
            } else {
                Log("没有持仓,无法平多");
            }
            break;
        case "CloseShort":
            if (position.length > 0) {
                if (position[0].Type === PD_SHORT) {
                    coverShort(-1, position[0].Amount); // 平空
                    _HasTakeProfit = false;
                    _TriggeredTakeProfit = false;       // 复位回调止盈开关
                    calculateProfit(ticker);            //统计收益
                } else {
                    Log("当前持有多头持仓,无法平空");
                }
            } else {
                Log("没有持仓,无法平空");
            }
            break;
        default:
            break;
    }
}




Больше

БобыДемонстрация на диске: https://www.fmz.com/robot/591982