Trending strategy V1.0 (publicly released)

Author: Summer won't beat you., Date: 2021-10-01 18:54:20
Tags:

This is a complete set of trend strategies written a long time ago, and the effects of retesting against ETH were obvious before, but the market did not adapt afterwards.



/*
趋势策略V1.0
Version: 1.0
Author: summer
Date: 2021.9.27
*/


// 策略参数变量
// 基本设置
var _Currency = TradeCurrency;                                  // 交易对
var _Interval = Interval;                                       // 程序运行周期
var _UseQuarter = UseQuarter;                                   // 季度合约,默认USDT永续合约
var _OnlyTrendJudgment = OnlyTrendJudgment;                     // 只做趋势判断,不交易
var _EnableMessageSend = EnableMessageSend;                     // 推送消息
// 趋势判断
var _RunInKLinePeriod = RunInKLinePeriod;                       // 按照K线周期执行策略核心
var _KLinePeriod = KLinePeriod;                                 // K线周期(min)
var _EmaLength = EmaLength;                                     // EMA长度
var _EmaCoefficient = EmaCoefficient;                           // EMA有效系数
var _UseStddev = UseStddev;                                     // 使用标准差
var _UseRecordsMiddleValue = UseRecordsMiddleValue;             // 使用K线中值来计算标准差,否则使用K线收盘价
var _StddevLength = StddevLength;                               // 标准差长度
var _StddevDeviations = StddevDeviations;                       // 标准差偏差
// 下单设置
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 _StopLossPercent = StopLossPercent;                         // 止损百分比%
var _UseTakeProfit = UseTakeProfit;                             // 使用止盈
var _TakeProfitPercent = TakeProfitPercent;                     // 止盈百分比%
var _UseTrackingTakeProfit = UseTrackingTakeProfit;             // 使用回调止盈
var _UsePositionRetracement = UsePositionRetracement;           // 使用持仓回撤百分比止盈,否则使用价格回撤百分比止盈
var _TakeProfitTriggerPercent = TakeProfitTriggerPercent;       // 回调止盈价格触发百分比%
var _CallBakcPercent = CallBakcPercent;                         // 回调百分比%

// 策略变量
var _LastBarTime = 0;                                    // 最新的K线时间
var _TrendWhenTakeProfitOrStopLoss = 0;                  // 止盈止损时趋势的状态  1表示多 -1表示空
var _HadStopLoss = false;                                // 发生止损
var _TriggeredTakeProfit = false;                        // 是否触发了回调止盈
var _PeakPriceInPosition = 0;                            // 持仓中价格的峰值点(最高/最低点)
var _HadTakeProfit = false;                              // 发生止盈
var _PriceCrossEMAStatus = 0;                            // 用来判断价格是否穿越均线。0:初始状态,没穿过(策略重启后的状态) -1:初始状态在均线下方 1:初始状态在均线上方 2:已经完成穿过

// 统计变量
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本位开单和结算


// 保存程序起始运行时间 秒级时间戳
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 runCmd() {
    var cmd = GetCommand();

    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 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) {
    return orderDirectly("closebuy", price, amount);
}

function coverShort(price, amount) {
    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));
    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;
}

// 按照K线周期运行策略核心
function runInKLinePeriod(records) {
    var bar_time = records[records.length - 1].Time;
    if (_RunInKLinePeriod && _LastBarTime == bar_time) {
        return false;
    }

    _LastBarTime = bar_time;
    return true;
}

// 检查价格是否穿过均线
function checkPriceCrossEma(price, ema_value) {
    if (_PriceCrossEMAStatus == 0) {
        if (price <= ema_value) {
            _PriceCrossEMAStatus = -1;
        } else {
            _PriceCrossEMAStatus = 1;
        }
    } else if ((_PriceCrossEMAStatus == -1 && price >= ema_value) || (_PriceCrossEMAStatus == 1 && price <= ema_value)) {
        _PriceCrossEMAStatus = 2;   // 完成穿过
    } 
}

// EMA的多空判断
function emaJudgment(records) {
    var ema_long = false;
    var ema_short = false;
    var price = records[records.length - 2].Close;  // 已经收盘的K线的收盘价格
    var ema = TA.EMA(records, _EmaLength);
    var ema_value = ema[ema.length - 2];            // 收盘K线对应ema值
    var ema_upper = ema_value * (1 + _EmaCoefficient);
    var ema_lower = ema_value * (1 - _EmaCoefficient);

    checkPriceCrossEma(price, ema_value);
    if (price > ema_upper) {
        ema_long = true;
    } else if (price < ema_lower) {
        ema_short = true;
    }

    return [ema_long, ema_short];
}

// 标准差判断
function stddevJudgment(records) {
    var in_trend = false;
    if (_UseStddev) {
        var records_data = [];
        for (var i = 0; i < records.length; i++) {
            records_data.push(_UseRecordsMiddleValue ? ((records[i].High + records[i].Low) / 2) : records[i].Close);
        }

        var stddev = talib.STDDEV(records_data, _StddevLength, _StddevDeviations);
        if (stddev[stddev.length - 1] > stddev[stddev.length - 2]) {
            in_trend = true;
        }
    } else {
        in_trend = true;
    }
    return in_trend;
}

// 趋势判断
function trendJudgment(records) {
    var long = false;
    var short = false;
    [long, short] = emaJudgment(records);

    var in_trend = stddevJudgment(records);
    long = (in_trend && long) ? true : false;
    short = (in_trend && short) ? true : false;

    if (long) {
        Log("当前趋势为:多", _EnableMessageSend ? "@" : "#00FF7F");
    }
    else if (short) {
        Log("当前趋势为:空", _EnableMessageSend ? "@" : "#FF0000");
    } else {
        Log("当前趋势为:震荡", _EnableMessageSend ? "@" : "#007FFF");
    }

    return [long, short];
}

// 止损
function stopLoss(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 = position[0].Price * (1 - _StopLossPercent / 100);
            if (price < stop_loss_price) {
                coverLong(-1, position[0].Amount);
                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 = position[0].Price * (1 + _StopLossPercent / 100);
            if (price > stop_loss_price) {
                coverShort(-1, position[0].Amount);
                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 && _UseTakeProfit) {
        if (position[0].Type == PD_LONG) {
            take_profit_price = position[0].Price * (1 + _TakeProfitPercent / 100);
            if (price > take_profit_price) {
                coverLong(-1, position[0].Amount);
                calculateProfit(ticker);
                _TrendWhenTakeProfitOrStopLoss = 1;
                _HadTakeProfit = true;
                Log("多单止盈。止盈价格:", _N(take_profit_price, 6), ", 持仓价格:", _N(position[0].Price), _EnableMessageSend ? "@" : "#FF1CAE");
            }
        } else if (position[0].Type == PD_SHORT) {
            take_profit_price = position[0].Price * (1 - _TakeProfitPercent / 100);
            if (price < take_profit_price) {
                coverShort(-1, position[0].Amount);
                calculateProfit(ticker);
                _TrendWhenTakeProfitOrStopLoss = -1;
                _HadTakeProfit = true;
                Log("空单止盈。止盈价格:", _N(take_profit_price, 6), ", 持仓价格:", _N(position[0].Price), _EnableMessageSend ? "@" : "#FF1CAE");
            }
        }
    }
}

// 回调止盈
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 (_TriggeredTakeProfit) {
                // 已达到触发价格,监控是否止盈
                _PeakPriceInPosition = price > _PeakPriceInPosition ? price : _PeakPriceInPosition;                                           // 更新价格高点
                if (_UsePositionRetracement) {
                    take_profit_price = _PeakPriceInPosition - (_PeakPriceInPosition - position[0].Price) * (_CallBakcPercent / 100);         // 计算回调的止盈价格
                } else {
                    take_profit_price = _PeakPriceInPosition * (1 - _CallBakcPercent / 100);                                                  // 计算回调的止盈价格
                }
                if (price < take_profit_price) {
                    coverLong(-1, position[0].Amount);              // 平多
                    calculateProfit(ticker);
                    _TriggeredTakeProfit = false;                   // 复位触发标记
                    _TrendWhenTakeProfitOrStopLoss = 1;             // 记录止盈时候的趋势
                    _HadTakeProfit = true;                          // 记录发生了止盈
                    Log("多单回调止盈:持仓中价格高点:", _N(_PeakPriceInPosition, 6), ", 止盈价格:", _N(take_profit_price, 6), ", 当前价格:", _N(price, 6),
                        ", 持仓价格:", _N(position[0].Price, 6), _EnableMessageSend ? "@" : "#FF1CAE");
                }
            } else {
                // 监控是否达到回调止盈的触发价格
                trigger_price = position[0].Price * (1 + _TakeProfitTriggerPercent / 100);
                if (price > trigger_price) {
                    _TriggeredTakeProfit = true;                      // 触发回调止盈
                    _PeakPriceInPosition = price;                     // 记录价格高点
                    Log("多单已达到回调止盈的触发价格:", _N(trigger_price, 6), ", 当前价格:", _N(price, 6), ", 持仓价格:", _N(position[0].Price, 6));
                }
            }
        } else if (position[0].Type == PD_SHORT) {
            // 空单持仓
            if (_TriggeredTakeProfit) {
                // 已达到触发价格,监控是否止盈
                _PeakPriceInPosition= price < _PeakPriceInPosition ? price : _PeakPriceInPosition;                                            // 更新价格低点
                if (_UsePositionRetracement) {
                    take_profit_price = _PeakPriceInPosition + (position[0].Price - _PeakPriceInPosition) * (_CallBakcPercent / 100);         // 计算回调的止盈价格
                } else {
                    take_profit_price = _PeakPriceInPosition * (1 + _CallBakcPercent / 100);                                                  // 计算回调的止盈价格
                }
                if (price > take_profit_price) {
                    coverShort(-1, position[0].Amount);             // 平空
                    calculateProfit(ticker);
                    _TriggeredTakeProfit = false;                   // 复位触发标记
                    _TrendWhenTakeProfitOrStopLoss = -1;            // 记录止盈时候的趋势
                    _HadTakeProfit = true;                          // 记录发生了止盈
                    Log("空单回调止盈:持仓中价格低点:", _N(_PeakPriceInPosition, 6), ", 止盈价格:", _N(take_profit_price, 6), ", 当前价格:", _N(price, 6),
                        ", 持仓价格:", _N(position[0].Price, 6), _EnableMessageSend ? "@" : "#FF1CAE");
                }
            } else {
                // 监控是否达到回调止盈的触发价格
                trigger_price = position[0].Price * (1 - _TakeProfitTriggerPercent / 100);
                if (price < trigger_price) {
                    _TriggeredTakeProfit = true;                      // 触发回调止盈
                    _PeakPriceInPosition = price;                     // 记录价格低点
                    Log("空单已达到回调止盈的触发价格:", _N(trigger_price, 6), ", 当前价格:", _N(price, 6), ", 持仓价格:", _N(position[0].Price, 6));
                }
            }
        }
    }
}

// 下单
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 ((_HadStopLoss || _HadTakeProfit) && _TrendWhenTakeProfitOrStopLoss == 1) {
            // 发生了止盈止损,并且止盈止损时候趋势为多,不再做多
            return;
        }
        if (position_size > 0 && position_type == PD_SHORT) {
            coverShort(-1, position_size);
            calculateProfit(ticker);
        } else if (position_size > 0 && position_type == PD_LONG) {
            // 多单持仓,不重复下单
            return;
        } else {
            // 没有持仓,如果是首次运行或者策略重启,需要等待价格穿过一次EMA均线才下单
            if (_PriceCrossEMAStatus != 2) {
                return;
            }
        }
        if (isEnoughAssetToOrder(_OrderSize, ticker)) {
            openLong(-1, _OrderSize);
            _HadStopLoss = false;
            _HadTakeProfit = false;
        } else {
            throw "不够钱下单!";
        }
    } else if (short) {
        // 趋势空
        if ((_HadStopLoss || _HadTakeProfit) && _TrendWhenTakeProfitOrStopLoss == -1) {
            // 发生了止盈止损,并且止盈止损时候趋势为空,不再做空
            return;
        }
        if (position_size > 0 && position_type == PD_LONG) {
            coverLong(-1, position_size);
            calculateProfit(ticker);
        } else if (position_size > 0 && position_type == PD_SHORT) {
            // 空单持仓,不重复下单
            return;
        } else {
            // 没有持仓,如果是首次运行或者策略重启,需要等待价格穿过一次EMA均线才下单
            if (_PriceCrossEMAStatus != 2) {
                return;
            }
        }
        if (isEnoughAssetToOrder(_OrderSize, ticker)) {
            openShort(-1, _OrderSize);
            _HadStopLoss = false;
            _HadTakeProfit = false;
        } else {
            throw "不够钱下单!";
        }
    }
}

// 趋势策略
function trendStrategy() {
    var ticker = _C(exchange.GetTicker);
    var position = _C(exchange.GetPosition);
    var account = _C(exchange.GetAccount);
    var records = _C(exchange.GetRecords, _KLinePeriod * 60);
    if (position.length > 1) {
        Log(position);
        throw "同时有多空持仓!";
    }
    // 策略交互
    runCmd();
    // 状态栏信息打印
    printLogStatus(ticker, account, position);
    // 止损
    stopLoss(position, ticker);
    // 止盈
    takeProfit(position, ticker);
    // 回调止盈
    trackingTakeProfit(position, ticker);

    // 按照K线周期运行策略
    if (!runInKLinePeriod(records)) {
        return;
    }
    // 趋势判断和下单
    var long = false;
    var short = false;
    [long, short] = trendJudgment(records);
    if (!_OnlyTrendJudgment) {
        order(long, short, position, ticker);
    }
}

// 状态栏信息打印
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(forced_cover_up_price, 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);
    }
    if (_UseTakeProfit && _UseTrackingTakeProfit) {
        throw "止盈和回调止盈不能同时使用!";
    }
}

// 设置合约
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);
}

// main
function main() {
    setContract();
    initDatas();
    while (true) {
        trendStrategy();
        Sleep(_Interval);
    }
}

More

JKMCHENMy God, is the trend strategy still maintained?

eth8888My God, is this not a way to run a multi-currency system?

weitinasThank God.

MAIKEOThank you for sharing, you have been very helpful!!!

Summer won't beat you.I'm not maintaining it, but if you need to, you can add me to your WeChat.

Summer won't beat you.Yes, it's a single currency.