从TV的webhook发送的消息内容为: long 开多 short 开空 closelong 平多 closeshort 平空
警报名称随便填写,消息内容就是上面的英文(只填英文,不要带空格和其他符号) 记得填写正确的Webhook URL
更新内容: 1、添加了2个止盈 2、添加了止损
3、添加了回调止盈,价格超过第2个止盈点位之后,回调百分比止盈。(2023年4月27日 20:43:11) 4、从TV接收止损信号,当达到止损信号时,立刻平仓,防止TV警报机制导致的延时; 5、增加了以下机制:开单之后,2分钟之内不会改变从TV来的止损信号,防止开单之后被立刻止损。(TV传过来的止损信号还没有因为趋势改变而发生变化)
////TradingView信号
////相比较0.1.0,多了一个止盈;当前版本为2个止盈点位;
////只能做一个多单,如果当前有多单,不再继续开多;同样的逻辑适用于空单; 0.1.0
////开单之后,每隔一段时间从TV发送消息,包含支撑价格,当价格上穿/下穿支撑价格时,平仓;2023年4月29日 00:44:05
////将夏天不打你的代码融入进来 V 0.1.3 2023年4月29日 03:35:31
////V 0.1.4 添加了挂止盈单的机制 2023年5月6日 21:13:43
// 参数设置
// 策略参数变量
// 基本设置
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;
} else if (positions[i].Type === PD_SHORT || positions[i].Type === PD_SHORT_YD) {
hasOpenedShort = true;
}
}
}
// 交易函数
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;
} else {
tradeFunc = exchange.Buy;
}
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; //更新止盈价格
}
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; // 记录价格高点
}
} 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; // 记录价格高点
}
}
}
}
// 回调止盈
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);//更新止损价格
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);//更新止损价格
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) + "%", //预估年化%
'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(); // 初始化数据
while (true) {
var command = GetCommand(); //获取WebHook消息
if (command) { // 检查 command 是否为空
//Log("接收到的命令:", command, "#FF1CAE");
var parsedCommand = JSON.parse(command); //格式化消息,方便之后的处理
var action = parsedCommand.Action; //获取要执行的处理
var currentTime = new Date().getTime(); //获取当前时间(毫秒)
//处理止损价格变量,如果有开单平单操作,锁定止损价格2分钟
if(currentTime - lastUpdateTime >= 2*60*1000 ){
stopLossPrice = parsedCommand.Price ? parseFloat(parsedCommand.Price) : 0; //止损价格
// Log("stopLossPrice变量已更改");
}else{
stopLossPrice = 0; //如果出现了开仓和关仓信号,则锁定止损价格2分钟;
Log("刚刚开单,2分钟之内止损价格不会变化",currentTime - lastUpdateTime);
}
TV_FMZ_Strategy(action); //开单平单操作
}
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;
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;
calculateProfit(ticker); //统计收益
} else {
Log("当前持有多头持仓,无法平空");
}
} else {
Log("没有持仓,无法平空");
}
break;
default:
break;
}
}