Loading ...

期货基础策略

Author: s2696922797c, Date: 2020-11-20 12:51:05
Tags:


"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } }

function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }

function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function toFixed(value) {
  var fractionDigits = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
  return _N(value, fractionDigits);
}

var Icons = {
  // 开仓
  // 开多
  BUY: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA7klEQVQ4T2NkoBAwUqifYRAbULPtpTIDA/MckBf//2eY0OotshGbd7F6oWHVK54/3Ew7GRgYrCCaGN8xMjA6NHsLXUY3BKsBtVverPvPyBCIpvj0bzYm105XoY/I4hgG1Gx5M42BkSETa+wwMq5o8RKOxGlAzba3tQz//zfhi1pGBoaOZm+RSpgauAtqtr1NZfj/fxZR6YKRIavFS2Q6OHRARO2W18b/GRnPEKUZpujff8cWX9EDYAOqt76JZWRgWESiAY0tvqINYAPKNr7mZWNh3M/AwGBMlCGMjFf//2NIa/URPjaIkzJRXgEqAgBpXEARSuFtxgAAAABJRU5ErkJggg==)',
  // 开空
  SELL: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABOklEQVQ4T2NkoBAwUqifgToGVG95a8XI+P8oKa5h+fqPtyFM7AvYBTWbXzf8Z2TgbvURLSXGkJqtbyYw/v9/rdlHdBbMAAcGJsb9DIx/VVq8xO/iM6Rm+2t1hn9MKxn+/Sto8RU9AA+Dmq1vsv8zMOR8YnxvMNlL9Sc2Qxr2/+f58+3tPgYmxpktnsJzQWpQArFmy+seBkZG5RZvkUBsBtRsfXvk//+/21p9xNpg8hixULP1zVqG///vt/iIliAbUrvtzZ5//xmutnqL5COLYxjQsP69wB+2v/sYGBgXt3gL94MU1257s46BgeFPs5dIGLrLsKaDqq2vDJgYmPb9Z/yXyfCfyYiRkdHqB/Nvjx53ia9EGQBSVLf1jf8/BsZiRsb/L5j//a1o8BG/hy1cqJMSiUk8uNQMvAsAnnptEci17kgAAAAASUVORK5CYII=)',
  // 平空
  // 止损
  CLOSE_BUY_LOSS: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA8UlEQVQ4T2NkoBAwUqifYRAbIPtDR/kP0985IC8yMjFOeMZybSM272L1gugrLR5Wgf87GRgYrMCaGBneMf377/CE/cZldEOwGiD1U3MdAyNDILJiRob/p9lZf7neY7z3EVUczUipX5rTGBgYMnHEzopnbNcjcRog/Vuz9v9/hia8UfufseMZ+7VKmBq4FyR/aaQyMjDOIiZdMDIwZD1luz4dEjwMDAySvzSMGRkYzxCjGabm39//ji84bxyAGqAZy8jAsIgUAxj+/298xn6jAWyAyGt1XjZ+pv0MDAzGRBpylYHxf9oz1hvHBnFSJtIrDADWe0ARSpCCcQAAAABJRU5ErkJggg==)',
  // 止盈
  CLOSE_BUY_PROFIT: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA8UlEQVQ4T2NkoBAwUqifYRAbcFmWXZmV4d8ciBcZJ2g8/rURm3exeuGqKAMPMyfbTgYGBiuopnf//zE4aD75dRndEKwGXJdlW8fIyBCIpvg0K8svV+V7DB+RxTEMuCHPPo3h//9MbM79z8iwQvPhr0icBtyQZ6tl+M/QhC9q/zMwdGg++lUJUwN3wU051tT/DIyziEoX//9naTz+PR0cvCDiuhyrMSMD4xmiNEMV/f/3z1HzyZ8DYANuyrHG/mdgXESSAQwMjZqPfjWADbghwsDLwMW2n4GBwZgYQxgZGK7+Y/yXpvnwz7FBnJSJ8QpIDQCM40IR09WjKAAAAABJRU5ErkJggg==)',
  // 平多
  // 止损
  CLOSE_SELL_LOSS: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABPElEQVQ4T8WSP0vDUBTFz21sUv+BiyKvi6DQJFNx7OToJi5+B1FQUMHRyUnQVcHJyUFHJ4kO+gWKJgGrDmLa4uKgaF6aXDGgVBsl0oJ3fPe83zucdwhtDrV5H50BiEAvgen8L26CR+p/GLKfYgfC19cA9Hqau5IGkpfGVgS2q6q7EwOGX/SJjEInSqSM3eUurn+DiNdCARnaj0Is1rrd088MhDTmAJrvycpihSp+EmSQzT41iCwGbXuqs/uu+RKikPoGmEY9zZlOAghpnjHjqKrZ6x/7ll/I+8YBE996qrvcDBGBcQzmS091F5rPWwAjXByQgW8RsHevOptxyIFxCKDhZZ2Z784Se5CXZpHBFhizAMaRQSnsyk7WqfycChC/2jCnEPESCLUwbKzWc1c3Sbl0polpyvOT5v8dvAH+vGoRgOAa0AAAAABJRU5ErkJggg==)',
  // 止盈
  CLOSE_SELL_PROFIT: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABPElEQVQ4T8WSvy8DYRjHv8/b3nsEicXmrhWqdzFojJ2MNrH4H4SEBInRZJKwkphMBkaTlIHZVHdN/EjvDH4kJsJ7V32kFVJ6pNImnvF9vu8n33yfL6HJoSb/ozUAJxHPEouTv7h5fQ66hu7xWHXgmHKZGB2WHyw2AnFNbZ2BM9sLN98BvfFREuIwxjSQ8tXFbxC3T0+jxDvM5Tn7unT0mUEhoU8z80xMBpnUOVQU5K4HnQ/tMgfiDasYblU0X0IsGNoqC+q3isFEFMA19WNm3rf9YOVjX3cFx5C7AF/ZfrhQC3FN7QBA3vLC2dr3OsBpEt1tZZkTTNtpX61VMzLkHhFKlhdMfncW2YN8UsuIMuWYMSUIIwBllVJjw7d4aghQEbmGHAfRPMA3AmJp0Hu5jMqlNU1spDw/af7fwRvwGmgRmVhXuQAAAABJRU5ErkJggg==)',
  // 取消
  CANCELED: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAsElEQVQ4T2NkoBAwUqifYRAacEOOdR8zA2OO6qNf15C9d1uOTesvw/8pGo9+OyGLY3jhqgyrOTMT4xxmBoZwmCEgzf8YGFYxMTCEoRuMNQwgtjGsBBkCsg2XZpAczkCEGrKBkYGBkYmBwR/dZpg38Brwj4Fh4z8Ghv8sDAwBJBmA7GeQTX8YGFayIIUJ3kDEFmBQ76xgZmCIIBiIN2RZ9zMzMmaTHY2kJu1BmJRJ9QIAicZLEfZkk5wAAAAASUVORK5CYII=)',
  // 未成交
  PENDING: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA0UlEQVQ4T7WTPQrCUBCEZ8lNbIS8IY1i5Q1ESy9g5yXUS9h5AUvRG1iJNmEi2HiTEIkkkMSA0ZBXLrvfm9kfQ8tnLevRHSAIglkcx71Uoed5zzAMD3VqaxWQ3APoA7hnRT6Ah6R5FfIBIHkFcJS0LiaTXAGYShoW4yUAyQmAlaRRmkTyDclhJC8ANpJOOaQEcM4tzWwgaVEAJAAshZDcJUlyi6Jo2wjgnPPN7O27EaBqodqwrxYy2f83Mf+xMMZzFhs3HmMOabVIv9xHd7fQVMUL10xlEcsMYLsAAAAASUVORK5CYII=)'
};
/**
 * 记录订单,更新订单状态,输出订单
 */

var TradingOrder = /*#__PURE__*/function () {
  function TradingOrder() {
    _classCallCheck(this, TradingOrder);

    _defineProperty(this, "_orders", new Map());

    _defineProperty(this, "_orderIdStack", []);
  }

  _createClass(TradingOrder, [{
    key: "record",
    // 记录订单
    // 更新
    value: function record(id, data) {
      this._orders.set(id, data);

      if (this._orderIdStack.includes(id)) {
        return;
      }

      this._orderIdStack.unshift(id);
    } // 获取订单
    // 没有 ID 获取全部

  }, {
    key: "query",
    value: function query(id) {
      var _this = this;

      if (id) {
        return this._orders.get(id);
      }

      return this._orderIdStack.map(function (orderId) {
        return _this._orders.get(orderId);
      });
    }
  }]);

  return TradingOrder;
}();

var BasicStrategy = /*#__PURE__*/function (_$$Strategy) {
  _inherits(BasicStrategy, _$$Strategy);

  var _super = _createSuper(BasicStrategy);

  function BasicStrategy() {
    var _defineProperty2, _defineProperty3, _defineProperty4, _defineProperty5;

    var _this2;

    _classCallCheck(this, BasicStrategy);

    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    _this2 = _super.call.apply(_super, [this].concat(args));

    _defineProperty(_assertThisInitialized(_this2), "contract", new $.Contract({
      contract: contract
    }));

    _defineProperty(_assertThisInitialized(_this2), "tradingOrder", new TradingOrder());

    _defineProperty(_assertThisInitialized(_this2), "messages", []);

    _defineProperty(_assertThisInitialized(_this2), "directions", [$.ContractTrade.Direction.LONG, $.ContractTrade.Direction.SHORT]);

    _defineProperty(_assertThisInitialized(_this2), "openRange", openRange);

    _defineProperty(_assertThisInitialized(_this2), "openStatus", false);

    _defineProperty(_assertThisInitialized(_this2), "closeStatus", false);

    _defineProperty(_assertThisInitialized(_this2), "openAmount", amount);

    _defineProperty(_assertThisInitialized(_this2), "closeAmount", 0);

    _defineProperty(_assertThisInitialized(_this2), "openedAmount", 0);

    _defineProperty(_assertThisInitialized(_this2), "totalOpeningPrice", 0);

    _defineProperty(_assertThisInitialized(_this2), "totalClosingPrice", 0);

    _defineProperty(_assertThisInitialized(_this2), "averageOpeningPrice", 0);

    _defineProperty(_assertThisInitialized(_this2), "averageClosingPrice", 0);

    _defineProperty(_assertThisInitialized(_this2), "startTime", null);

    _defineProperty(_assertThisInitialized(_this2), "firstClosingTime", null);

    _defineProperty(_assertThisInitialized(_this2), "stopProfitPrice", null);

    _defineProperty(_assertThisInitialized(_this2), "stopLossPrice", null);

    _defineProperty(_assertThisInitialized(_this2), "lingerTime", null);

    _defineProperty(_assertThisInitialized(_this2), "hasHalfFallBackedCount", 0);

    _defineProperty(_assertThisInitialized(_this2), "initialLoss", 0);

    _defineProperty(_assertThisInitialized(_this2), "initCost", 0);

    _defineProperty(_assertThisInitialized(_this2), "halfFallBackFunctions", (_defineProperty2 = {}, _defineProperty(_defineProperty2, $.ContractTrade.Direction.SHORT, function () {
      if (_this2.ticker.Last <= _this2.half) {
        // 计算最初亏损
        if (_this2.hasHalfFallBackedCount === 0) {
          _this2.initialLoss = toFixed(_this2.stopLossPrice - _this2.cost);
        }

        _this2.stopLossPrice = toFixed(_this2.cost - _this2.initialLoss);
        return true;
      }

      return false;
    }), _defineProperty(_defineProperty2, $.ContractTrade.Direction.LONG, function () {
      if (_this2.ticker.Last >= _this2.half) {
        if (_this2.hasHalfFallBackedCount === 0) {
          _this2.initialLoss = toFixed(_this2.cost - _this2.stopLossPrice);
        }

        _this2.stopLossPrice = toFixed(_this2.cost + _this2.initialLoss);
        return true;
      }

      return false;
    }), _defineProperty2));

    _defineProperty(_assertThisInitialized(_this2), "hasWholeFallBackedCount", 0);

    _defineProperty(_assertThisInitialized(_this2), "wholeFallBackFunctions", (_defineProperty3 = {}, _defineProperty(_defineProperty3, $.ContractTrade.Direction.SHORT, function () {
      if (_this2.ticker.Last <= _this2.stopProfitPrice) {
        _this2.stopLossPrice = _this2.stopProfitPrice;
        _this2.cost = _this2.stopLossPrice;
        _this2.stopProfitPrice = toFixed(_this2.stopLossPrice - (_this2.initCost - _this2.stopLossPrice));
        return true;
      }

      return false;
    }), _defineProperty(_defineProperty3, $.ContractTrade.Direction.LONG, function () {
      if (_this2.ticker.Last >= _this2.stopProfitPrice) {
        _this2.stopLossPrice = _this2.stopProfitPrice;
        _this2.cost = _this2.stopLossPrice;
        _this2.stopProfitPrice = toFixed(_this2.stopLossPrice + (_this2.stopLossPrice - _this2.initCost));
        return true;
      }

      return false;
    }), _defineProperty3));

    _defineProperty(_assertThisInitialized(_this2), "stopLossConditions", (_defineProperty4 = {}, _defineProperty(_defineProperty4, $.ContractTrade.Direction.SHORT, function () {
      return _this2.ticker.Last >= _this2.stopLossPrice;
    }), _defineProperty(_defineProperty4, $.ContractTrade.Direction.LONG, function () {
      return _this2.ticker.Last <= _this2.stopLossPrice;
    }), _defineProperty4));

    _defineProperty(_assertThisInitialized(_this2), "lingerConditions", (_defineProperty5 = {}, _defineProperty(_defineProperty5, $.ContractTrade.Direction.SHORT, function () {
      return _this2.stopLossPrice > _this2.ticker.Last && _this2.ticker.Last > _this2.half;
    }), _defineProperty(_defineProperty5, $.ContractTrade.Direction.LONG, function () {
      return _this2.stopLossPrice < _this2.ticker.Last && _this2.ticker.Last < _this2.half;
    }), _defineProperty5));

    return _this2;
  }

  _createClass(BasicStrategy, [{
    key: "openPosition",
    // 开仓策略
    value: function openPosition() {
      var ticker = this.ticker; // 开仓策略

      if (this.openAmount) {
        this.checkCloseStatus();

        if (this.closeStatus) {
          this.console.warn("\u5F00\u4ED3\u8FC7\u7A0B\u4E2D\uFF0C\u89E6\u53D1\u6B62\u635F\uFF0C\u4E0D\u518D\u5F00\u4ED3\uFF0C\u5E76\u5F00\u59CB\u5E73\u4ED3\u3002\u5DF2\u5F00\u4ED3\u6570\u91CF ".concat(this.closeAmount));
          this.openAmount = 0;
          this.callback();
        }

        if (!this.startTime) {
          this.startTime = +new Date();
        }

        if (+new Date() - this.startTime > openingTime) {
          if (this.openAmount === amount) {
            this.console.error('@', '超过开仓时间,未开仓,关闭单子');
            this.callback(true);
          }

          this.openAmount = 0;
          this.console.warn("\u8D85\u8FC7\u5F00\u4ED3\u65F6\u95F4\uFF0C\u672A\u5168\u90E8\u5F00\u4ED3\uFF0C\u4E0D\u518D\u5F00\u4ED3\u3002\u5DF2\u5F00\u4ED3\u6570\u91CF ".concat(this.closeAmount));
          this.callback();
        }

        if (Math.abs(ticker.Last - this.cost) <= this.openRange) {
          var order = this.contract.trade.openPosition(ticker.Last, this.openAmount); // 记录单子

          this.totalOpeningPrice += order.DealAmount * order.Price; // 如果实际开仓数量小于预期开仓数量,重新计算开仓数量

          if (order.DealAmount < this.openAmount) {
            this.openAmount = this.openAmount - order.DealAmount;
            this.setCloseAmount(amount - this.openAmount);
            this.console.error("\u672A\u5168\u90E8\u5F00\u4ED3\uFF0C\u91CD\u65B0\u5C1D\u8BD5\u3002\u5269\u4F59\u5F00\u4ED3\u6570\u91CF ".concat(this.openAmount));
            this.callback();
          }

          this.openAmount = 0;
          this.setCloseAmount(amount);
          this.console.success('@', "\u5DF2\u5B8C\u6210\u5F00\u4ED3\uFF0C\u5F00\u4ED3\u65B9\u5411 -> ".concat(this.contract.direction, "\uFF0C\u5F00\u4ED3\u5747\u4EF7 -> ").concat(this.averageOpeningPrice, "\uFF0C\u5F00\u4ED3\u6570\u91CF -> ").concat(this.closeAmount));
        }

        this.callback();
      }
    }
  }, {
    key: "setCloseAmount",
    value: function setCloseAmount(closeAmount) {
      this.closeAmount = closeAmount;
      this.openedAmount = closeAmount;
      this.averageOpeningPrice = toFixed(this.totalOpeningPrice / this.closeAmount);
    }
  }, {
    key: "checkCloseStatus",
    value: function checkCloseStatus() {
      var isArriveAtEndTime = this.contract.getDistanceClosedTimestamp(true) <= endTime;

      if (!this.closeStatus && (this.stopLossConditions[this.direction]() || isArriveAtEndTime)) {
        if (isArriveAtEndTime) {
          this.console.error('在交易结束时间内,准备强制平仓');
        }

        this.closeStatus = true;
      }

      return this.closeStatus;
    } // 平仓策略
    // 已开仓的仓位,必须全部平掉

  }, {
    key: "closePosition",
    value: function closePosition() {
      this.checkCloseStatus();

      if (this.closeStatus) {
        if (!this.closeAmount) {
          this.console.error('@', '未开仓就触发止损,放弃单子');
          this.callback(true);
        }

        var orders = this.contract.trade.closePosition(this.ticker.Last, this.closeAmount);
        var dealAmount = orders.reduce(function (result, order) {
          return result + order.DealAmount;
        }, 0);
        var totalPrice = orders.reduce(function (result, order) {
          return result + order.DealAmount * order.Price;
        }, 0);
        this.totalClosingPrice += totalPrice;

        if (dealAmount < this.closeAmount) {
          this.closeAmount = this.closeAmount - dealAmount;
          this.console.error("\u672A\u5168\u90E8\u5E73\u4ED3\uFF0C\u91CD\u65B0\u5C1D\u8BD5\u3002\u5269\u4F59\u5E73\u4ED3\u6570\u91CF ".concat(this.closeAmount));
          this.callback();
        }

        this.closeAmount = 0;
        this.averageClosingPrice = toFixed(this.totalClosingPrice / this.openedAmount);
        this.console.success('@', "\u5DF2\u5B8C\u6210\u5E73\u4ED3\uFF0C\u5E73\u4ED3\u65B9\u5411 -> ".concat(this.contract.direction, "\uFF0C\u5E73\u4ED3\u5747\u4EF7 -> ").concat(this.averageClosingPrice));
        this.print();
        this.callback(true);
      }
    } // 价格回落

  }, {
    key: "fallBack",
    value: function fallBack() {
      if (this.closeStatus) {
        return;
      }
      /**
       * 价格回落
       * 回落分为两种情况:
       *      如果价格到达止盈价格的一半以上,并且在没有达到止盈价的时候开始回落。
       *      那么设置新的止损价为当前成本价加上最初亏损(最初止损价与最初成本价的价差)。
       * 
       *      如果价格在止盈价之上,开始回落。
       *      那么设置新的止损价为当前止盈价,新的止盈价设置为新的止损价加上新的止损价与最初成本的价差,
       *      新的成本价为新的止损价。
       */
      // 如果一半盈利的计数和止盈价的计数一样,说明止盈价已经重新调整过,所以重新计算一半盈利


      if (this.hasHalfFallBackedCount === this.hasWholeFallBackedCount && this.halfFallBackFunctions[this.direction]()) {
        // 止损价或者止盈价改变,重新设置成本徘徊周期
        this.lingerTime = null;
        this.hasHalfFallBackedCount++;
        this.console.warn("\u4EF7\u683C\u8D85\u8FC7\u4E00\u534A\u76C8\u5229\uFF0C\u91CD\u65B0\u8BBE\u7F6E\u6B62\u635F\u4F4D\u4E3A ".concat(this.stopLossPrice));
        this.callback();
      } // 如果止盈位的计数小于一半盈利的计数,说明止盈价还未调整过


      if (this.hasWholeFallBackedCount < this.hasHalfFallBackedCount && this.wholeFallBackFunctions[this.direction]()) {
        // 止损价或者止盈价改变,重新设置成本徘徊周期
        this.lingerTime = null;
        this.hasWholeFallBackedCount = this.hasHalfFallBackedCount;
        this.console.warn("\u4EF7\u683C\u8D85\u8FC7\u6B62\u76C8\u4F4D\uFF0C\u91CD\u65B0\u8BBE\u7F6E\u6B62\u635F\u4F4D\u4E3A ".concat(this.stopLossPrice, "\uFF0C\u6B62\u76C8\u4F4D\u4E3A ").concat(this.stopProfitPrice));
        this.callback();
      }
    } // 成本价徘徊

  }, {
    key: "linger",
    value: function linger() {
      if (this.closeStatus) {
        return;
      }
      /**
       * 成本价徘徊
       * 如果价格在成本价附近徘徊不止,那么使用时间周期进行处理。
       * 在一定时间之后,如果没有停止徘徊,那么立即平仓。这个一定时间根据行情的不同进行设置。
       */


      if (this.lingerConditions[this.direction]()) {
        var now = +new Date();

        if (!this.lingerTime) {
          this.lingerTime = now;
        }

        if (now - this.lingerTime >= lingerTime) {
          this.closeStatus = true;
          this.console.warn('超出徘徊时间周期,准备强制平仓');
        }
      } else {
        this.lingerTime = null;
      }
    }
  }, {
    key: "checkStopProfitPrice",
    value: function checkStopProfitPrice() {
      var _this3 = this,
          _stopProfitConitions;

      var stopProfitConitions = (_stopProfitConitions = {}, _defineProperty(_stopProfitConitions, $.ContractTrade.Direction.SHORT, function () {
        return _this3.cost > _this3.stopProfitPrice;
      }), _defineProperty(_stopProfitConitions, $.ContractTrade.Direction.LONG, function () {
        return _this3.cost < _this3.stopProfitPrice;
      }), _stopProfitConitions);

      if (!stopProfitConitions[this.direction]()) {
        this.console.error('@', '单子创建失败,止盈价必须在成本之上');
        this.callback(true);
      }
    }
  }, {
    key: "checkStopLossPrice",
    value: function checkStopLossPrice() {
      var _this4 = this,
          _stopLossConitions;

      var stopLossConitions = (_stopLossConitions = {}, _defineProperty(_stopLossConitions, $.ContractTrade.Direction.SHORT, function () {
        return _this4.cost < _this4.stopLossPrice;
      }), _defineProperty(_stopLossConitions, $.ContractTrade.Direction.LONG, function () {
        return _this4.cost > _this4.stopLossPrice;
      }), _stopLossConitions);

      if (!stopLossConitions[this.direction]()) {
        this.console.error('@', '单子创建失败,止损价必须在成本之下');
        this.callback(true);
      }
    }
  }, {
    key: "command",
    value: function command() {
      var cmd = GetCommand();

      if (cmd === 'stop') {
        this.closeStatus = true;
        this.console.success('停止命令运行成功,准备关闭单子');
      }
    }
  }, {
    key: "printTable",
    value: function printTable() {
      var _this5 = this;

      var positions = this.contract.getPositions();
      var tables = []; // 平仓之后

      if (this.closeAmount === 0 && this.openedAmount !== 0) {
        var label = _C(exchange.GetLabel);

        var account = _C(exchange.GetAccount);

        tables.push([[{
          '期货公司': label,
          '开仓前余额': toFixed(this.beforeBalance),
          '平仓后余额': toFixed(account.Balance),
          '单子盈亏': toFixed(account.Balance - this.beforeBalance)
        }], {
          title: '账户'
        }]);
        var _directionNames = ['多单', '空单'];
        var detail = this.contract.detail;
        var commission = $.Commission.query(detail.ProductID);
        var priceDifferenceFunctions = [this.averageClosingPrice - this.averageOpeningPrice, this.averageOpeningPrice - this.averageClosingPrice];
        var totalCommission = toFixed((commission.openCommission + commission.closeTodayCommission) * this.openedAmount);
        var profitAndLoss = toFixed(priceDifferenceFunctions[direction] * this.openedAmount * detail.VolumeMultiple);
        this.profitAndLoss = profitAndLoss;
        tables.push([[{
          '合约名称': contract,
          '多空': _directionNames[direction],
          '开仓均价': this.averageOpeningPrice,
          '平仓均价': this.averageClosingPrice,
          '盈亏': profitAndLoss,
          '手续费': totalCommission,
          '预估盈亏(去手续费)': profitAndLoss - totalCommission
        }], {
          title: '汇总'
        }]);
      }

      if (positions.length) {
        var _directionNames2 = ['多头', '空头', '昨日多头', '昨日空头'];
        tables.push([positions.map(function (position) {
          var priceDifferenceFunctions = [function () {
            return _this5.ticker.Last - position.Price;
          }, function () {
            return position.Price - _this5.ticker.Last;
          }, function () {
            return _this5.ticker.Last - position.Price;
          }, function () {
            return position.Price - _this5.ticker.Last;
          }];
          var priceDifference = priceDifferenceFunctions[position.Type]();
          var avgPrice = position.AvgPrice || position.Price;
          var totalPrice = avgPrice * position.Amount;
          return {
            '合约名称': position.ContractType,
            '多空': _directionNames2[position.Type],
            '可用/总仓': "".concat(position.Amount - position.FrozenAmount, "/").concat(position.Amount),
            '开仓均价': position.AvgPrice || position.Price,
            '浮动盈亏': position.Profit,
            '价差': priceDifference,
            '保证金': position.Margin,
            '盈亏比例': "".concat(toFixed(priceDifference / totalPrice) * 100, "%")
          };
        }), {
          title: '持仓'
        }]);
      }

      var directionNames = [['开多', '开空'], ['平空', '平多']];
      var statusNames = ['未完成', '已经完成', '已经取消', '其他状态'];
      tables.push([this.tradingOrder.query().map(function (order) {
        return {
          '合约名称': order.ContractType,
          '状态': statusNames[order.Status],
          '方向': directionNames[order.Offset][order.Type],
          '委托价': order.Price,
          '委托量': order.Amount,
          '已成交': order.DealAmount,
          '已撤单': order.Status === $.ContractTrade.OrderStatus.ORDER_STATE_CANCELED ? order.Amount - order.DealAmount : 0,
          '委托时间': _D(order.Time)
        };
      }), {
        title: '委托'
      }]);
      tables.push([this.messages.map(function (_ref) {
        var _ref2 = _slicedToArray(_ref, 2),
            timestamp = _ref2[0],
            message = _ref2[1];

        return {
          '时间': _D(timestamp),
          '消息': message
        };
      }), {
        title: '日志'
      }]);
      this.console.tables(tables);
    }
  }, {
    key: "initChart",
    value: function initChart() {
      this.chart = this.console.chart({
        rangeSelector: {
          buttons: [],
          inputEnabled: false
        },
        title: {
          text: "".concat(contract, " [").concat(_D(+new Date()), "]")
        },
        tooltip: {
          split: false,
          shared: true
        },
        yAxis: [{
          labels: {
            align: 'right',
            x: -3
          },
          title: {
            text: '价格'
          },
          height: '65%',
          resize: {
            enabled: true
          },
          lineWidth: 2
        }, {
          labels: {
            align: 'right',
            x: -3
          },
          title: {
            text: '成交量'
          },
          top: '65%',
          height: '35%',
          offset: 0,
          lineWidth: 2
        }],
        plotOptions: {
          candlestick: {
            color: 'green',
            lineColor: 'green',
            upColor: 'red',
            upLineColor: 'red',
            tooltip: {
              pointFormat: "<span style=\"color:{point.color}\">\u25CF</span> <b> {series.name}</b><br/>" + '开盘: {point.open}<br/>' + '最高: {point.high}<br/>' + '最低: {point.low}<br/>' + '收盘: {point.close}<br/>'
            }
          }
        },
        series: [{
          type: 'candlestick',
          name: '价格',
          data: []
        }, {
          type: 'column',
          name: '成交量',
          data: [],
          yAxis: 1
        }, {
          name: '开仓',
          data: [],
          color: '#7cb5ec',
          lineWidth: 0,
          step: 'center',
          states: {
            hover: {
              enabled: false,
              lineWidth: 0,
              lineWidthPlus: 0
            }
          }
        }, {
          name: '止损位',
          data: [],
          color: 'green',
          lineWidth: 0,
          step: 'center',
          states: {
            hover: {
              enabled: false,
              lineWidth: 0,
              lineWidthPlus: 0
            }
          }
        }, {
          name: '止盈位',
          data: [],
          color: 'red',
          lineWidth: 0,
          step: 'center',
          states: {
            hover: {
              enabled: false,
              lineWidth: 0,
              lineWidthPlus: 0
            }
          }
        }, {
          type: 'scatter',
          name: '开仓点',
          data: [],
          zIndex: 9,
          tooltip: {
            pointFormat: '价格:{point.y}'
          },
          marker: {
            symbol: null
          }
        }, {
          type: 'scatter',
          name: '平仓点',
          data: [],
          zIndex: 9,
          tooltip: {
            pointFormat: '价格:{point.y}'
          },
          marker: {
            symbol: null
          }
        }, {
          type: 'scatter',
          name: '进行中',
          data: [],
          zIndex: 9,
          tooltip: {
            pointFormat: '价格:{point.y}'
          },
          marker: {
            symbol: Icons.PENDING
          }
        }, {
          type: 'scatter',
          name: '已取消',
          data: [],
          zIndex: 9,
          tooltip: {
            pointFormat: '价格:{point.y}'
          },
          marker: {
            symbol: Icons.CANCELED
          }
        }]
      });
    }
  }, {
    key: "printChart",
    value: function printChart() {
      var _this6 = this;

      var config = this.chart.getConfig();
      var averageOpeningPrice = this.averageOpeningPrice || this.cost;
      config.yAxis[0].plotBands = this.profitAndLoss ? [{
        from: averageOpeningPrice,
        to: this.stopLossPrice,
        label: {
          text: "".concat(this.profitAndLoss > 0 ? '盈利' : '亏损', " ").concat(this.profitAndLoss)
        },
        color: this.profitAndLoss > 0 ? 'rgba(255, 0, 0, .1)' : 'rgba(0, 128, 3, .6)',
        borderWidth: 0
      }] : [];
      config.yAxis[0].plotLines = [{
        label: {
          text: "\u5F00\u4ED3 ".concat(averageOpeningPrice),
          align: 'right',
          style: {
            color: '#7cb5ec'
          },
          x: -50
        },
        color: '#7cb5ec',
        dashStyle: 'solid',
        value: averageOpeningPrice,
        width: 1
      }, {
        label: {
          text: "\u6B62\u76C8 ".concat(this.stopProfitPrice),
          align: 'right',
          style: {
            color: 'red'
          },
          x: -50
        },
        color: 'red',
        dashStyle: 'solid',
        value: this.stopProfitPrice,
        width: 1
      }, {
        label: {
          text: "\u6B62\u635F ".concat(this.stopLossPrice),
          align: 'right',
          style: {
            color: 'green'
          },
          x: -50
        },
        color: 'green',
        dashStyle: 'solid',
        value: this.stopLossPrice,
        width: 1
      }];

      var records = _toConsumableArray(_C(exchange.GetRecords, PERIOD_M1));

      var candlestick = [];
      var column = [];
      var opened = [];
      var profit = [];
      var loss = [];
      var startTime = records.length ? records[0].Time : null;
      var endTime = records.length ? records[records.length - 1].Time : null;

      if (startTime && endTime) {
        Array.from({
          length: 40
        }).forEach(function (_, index) {
          records.unshift({
            Time: startTime - (index + 1) * 1000 * 60
          });
          records.push({
            Time: endTime + (index + 1) * 1000 * 60
          });
        });
      }

      records.forEach(function (record) {
        opened.push([record.Time, averageOpeningPrice]);
        profit.push([record.Time, _this6.stopProfitPrice]);
        loss.push([record.Time, _this6.stopLossPrice]);
        candlestick.push([record.Time, record.Open, record.High, record.Low, record.Close]);
        column.push([record.Time, record.Volume]);
      });
      var openDot = [];
      var closeDot = [];
      var pendingDot = [];
      var canceledDot = [];
      var openDotSymbol = null;
      var closeDotSymbol = null;
      var dotSymbols = [[Icons.BUY, Icons.SELL], [[Icons.CLOSE_BUY_LOSS, Icons.CLOSE_SELL_LOSS], [Icons.CLOSE_BUY_PROFIT, Icons.CLOSE_SELL_PROFIT]]];
      this.tradingOrder.query().forEach(function (order) {
        var time = +new Date(_D(order.Time, 'yyyy-MM-dd hh:mm'));

        if (order.Status === $.ContractTrade.OrderStatus.ORDER_STATE_CANCELED) {
          canceledDot.push([time, order.Price]);
          return;
        }

        if (order.Status === $.ContractTrade.OrderStatus.ORDER_STATE_PENDING) {
          pendingDot.push([time, order.Price]);
          return;
        }

        if (order.Offset === 0) {
          openDot.push([time, order.Price]);
          openDotSymbol = dotSymbols[order.Offset][order.Type];
          return;
        }

        if (order.Offset === 1) {
          closeDot.push([time, order.Price]);
          closeDotSymbol = dotSymbols[order.Offset][_this6.profitAndLoss > 0 ? 1 : 0][order.Type];
          return;
        }
      });
      this.chart.changeData(0, candlestick);
      this.chart.changeData(1, column);
      this.chart.changeData(2, opened);
      this.chart.changeData(3, loss);
      this.chart.changeData(4, profit);
      this.chart.changeData(5, openDot);
      this.chart.changeData(6, closeDot);
      this.chart.changeData(7, pendingDot);
      this.chart.changeData(8, canceledDot);
      config.series[5].marker.symbol = openDotSymbol;
      config.series[6].marker.symbol = closeDotSymbol;
      this.chart.update(config);
    }
  }, {
    key: "print",
    value: function print() {
      this.printTable();
      this.printChart();
    }
  }, {
    key: "subscribeOrderChange",
    value: function subscribeOrderChange() {
      var _this7 = this;

      this.contract.trade.subscribe(function (order) {
        _this7.tradingOrder.record(order.Id, order);

        _this7.printTable();
      });
    }
  }, {
    key: "subscribeConsole",
    value: function subscribeConsole() {
      var _this8 = this;

      this.console.subscribe(function (type) {
        if (type === 'table' || type === 'chart') {
          return;
        }

        for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
          args[_key2 - 1] = arguments[_key2];
        }

        _this8.messages.unshift([+new Date(), args.join(' ')]);
      });
    }
  }, {
    key: "init",
    value: function init() {
      var _stopProfitPrices, _stopLossPrices;

      var ticker = this.contract.getTicker();

      var account = _C(exchange.GetAccount);

      this.beforeBalance = account.Balance;
      this.contract.setDirection(this.direction);
      this.cost = cost || ticker.Last;
      this.initCost = this.cost;
      var stopProfitPrices = (_stopProfitPrices = {}, _defineProperty(_stopProfitPrices, $.ContractTrade.Direction.SHORT, stopProfit || this.cost - stopProfitPriceDifference), _defineProperty(_stopProfitPrices, $.ContractTrade.Direction.LONG, stopProfit || this.cost + stopProfitPriceDifference), _stopProfitPrices);
      var stopLossPrices = (_stopLossPrices = {}, _defineProperty(_stopLossPrices, $.ContractTrade.Direction.SHORT, stopLoss || this.cost + stopLossPriceDifference), _defineProperty(_stopLossPrices, $.ContractTrade.Direction.LONG, stopLoss || this.cost - stopLossPriceDifference), _stopLossPrices);
      this.stopProfitPrice = stopProfitPrices[this.direction];
      this.checkStopProfitPrice();
      this.stopLossPrice = stopLossPrices[this.direction];
      this.checkStopLossPrice();
      this.subscribeOrderChange();
      this.subscribeConsole();
      this.initChart();
    }
  }, {
    key: "body",
    value: function body() {
      this.ticker = this.contract.getTicker(); // 展示图表和表格

      this.print(); // 命令

      this.command(); // 开仓策略,必须放在开头

      this.openPosition(); // 价格回落

      this.fallBack(); // 成本价徘徊

      this.linger(); // 平仓策略,必须放在最后

      this.closePosition();
    }
  }, {
    key: "complete",
    value: function complete() {
      var endTime = +new Date() + 5 * 1000 * 60;

      while (+new Date() < endTime) {
        this.printChart();
        Sleep(1000);
      }
    }
  }, {
    key: "half",
    get: function get() {
      var _halfPrices;

      var halfPrices = (_halfPrices = {}, _defineProperty(_halfPrices, $.ContractTrade.Direction.SHORT, toFixed(this.cost - (this.cost - this.stopProfitPrice) / 2)), _defineProperty(_halfPrices, $.ContractTrade.Direction.LONG, toFixed(this.cost + (this.stopProfitPrice - this.cost) / 2)), _halfPrices);
      return halfPrices[this.direction];
    }
    /**
     *      如果价格到达止盈价格的一半以上,并且在没有达到止盈价的时候开始回落。
     *      那么设置新的止损价为当前成本价加上最初亏损(最初止损价与最初成本价的价差)。
     */

  }, {
    key: "direction",
    get: function get() {
      return this.directions[direction];
    }
  }]);

  return BasicStrategy;
}($.Strategy);

_defineProperty(BasicStrategy, "name", '基础策略');

function main() {
  // 2020-11-18 09:00
  // 2020-11-18 09:59
  // 1 分钟
  var engine = new $.Engine();
  engine.addStrategy(new BasicStrategy());
  engine.run();
}

More