趋势策略V1.0 (公开)

Author: 夏天不打你, Date: 2021-10-01 18:54:20
Tags:

这是很久以前写的一套完整的趋势策略,以前针对ETH的回测效果很明显,但是后面行情不适应了。所以实盘谨慎使用。



/*
趋势策略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

JKMCHEN 大神,趋势策略还在维护么?

eth8888 大神,这个是不是没办法运行多币种

weitinas 谢谢大神

MAIKEO 感谢分享,受益良多!!!

夏天不打你 没维护了,有需求可以加我微信。

夏天不打你 是的,单币种的。