자바스크립트와 함께 게임을 하는 노인 - 구매를 하는 파트너를 만드는 (7) 유용한 도구

저자:작은 꿈, 2017-03-16 12:29:51, 업데이트: 2017-10-11 10:37:54

좋은 도구는 왜 좋은지 알아야 합니다!

평일에는 기획을 하는 것이 즐거워하고, 편집기에는 다양한 무작위적인 아이디어가 쏟아져 나오고, 아마도 당신의 다음 신한 잔이 될지도 모르죠. 이 열정을 좌지우지하는 유일한 것은 거래 전략 시스템에서 구매 및 판매 논리를 처리하는 것입니다. 이 거래 모듈의 처리도 매우 중요하지만 다소 지루하고 논리가 복잡합니다.

  • 좋은 것은 바로 사용할 수 있는 잘 작성된 모듈이 있다는 것입니다. (이전 기사에서 사용했었습니다.) 하지만 사용 이외에도 어떻게 작동하는지 알아보기 위해 코드를 설명합니다.

/* Interval 실패 재시험 간격 (millisecond) 숫자 (number) 500 SlideTick 슬라이드 가격 점수 (진수) 숫자 형식 (수) 1 RiskControl 바람 제어 가동 불형 ((true/false) false MaxTrade@RiskControl 일일 최대 거래 거래 횟수 숫자 형식 (number) 50 MaxTradeAmount@RiskControl 한 장 최대 한 장의 자금 수 숫자형 (number) 1000 */

var __orderCount = 0 // 현재 일일에 다음 단위를 기록합니다 var __orderDay = 0 // 현재 작업일 날짜를 기록합니다

function CanTrade ((tradeAmount) { // 위험 제어 모듈, 매개 변수: 거래 수 if (!RiskControl) { // 기본 설정으로는 풍경 제어 모듈을 열지 않습니다. 열지 않으면 CanTrade 함수가 true를 반환합니다. true를 반환합니다 ♪ ♪ if (typeof(tradeAmount) == number?? && tradeAmount > MaxTradeAmount) { // 입력된 파라미터 tradeAmount는 숫자 타입이며, 아래 항목은 템플릿 파라미터에서 설정된 항목의 최대 항목보다 크다. 로그 (, MaxTradeAmount, #ff0000 @) // 출력 충고, 중단 실행. throw 실행 을 중단합니다 false를 반환합니다. ♪ ♪ var nowDay = new Date (().getDate ((); // 현재 날짜를 얻는다 if (nowDay!= __orderDay) { // getDate() 는 Date 객체에서 한 달의 어느 날을 반환한다. __orderDay = nowDay; // __orderDay 글로벌 변수는 풍력 제어 모듈에 처음 들어간 트리거 날짜를 기록합니다. __orderCount = 0; // __orderDay 변수를 업데이트하고 __orderCount 변수를 다시 설정합니다 ♪ ♪ __orderCount++; // 글로벌 변수 __orderCount 다음 단수, 자기 더함. if (__orderCount > MaxTrade) { // 파라미터 설정된 일일 최대 거래 수를 초과하는지 판단합니다 로그 ( () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () ) () () () () () ) () () () ) () () ) () () ) () () ) throw 실행 을 중단합니다 false를 반환합니다. ♪ ♪ return true; // 위의 조건이 모두 발생하지 않아 true, 즉 거래가 가능하다는 것을 반환합니다. ♪ ♪

function init ((() { // 템플릿 초기화 함수, 템플릿 로딩 시 먼저 이 함수를 실행한다. if (typeof(SlideTick) === undefined) { // 슬라이드틱의 정의가 없는지 확인합니다。 슬라이드 틱 = 1; // 설정 기본값 1 } else { // 분석 문자열 숫자로 변환되지만, 숫자가 아닌 문자 시작 문자열이 NaN을 반환하면 오류가 발생할 수 있습니다. SlideTick = parseInt (슬라이드틱); ♪ ♪ 로그 ( 상품 거래 카테고리 로딩 성공 로그) ♪ ♪

function GetPosition ((e, contractType, direction, positions) { // 거래소 개체, 계약 유형, 방향, API 반환된 보유 데이터 ((空可) 와 같은 방향의 계약의 어제 포지션 현재 포지션을 결합합니다.
var allCost = 0; // contractType 계약 방향 총 지출 금액, 1인당 계약의 몇 점으로 곱하지 않습니다 ( 전체로 계약할 수 있기 때문에) var allAmount = 0; // 총 계약 수 var allProfit = 0; // 이익과 손실의 총 var allFrozen = 0; // 전체 얼음 수 var pos 마진 = 0; // 보유 계약 레버리지 if (typeof(positions) === undefinedᄉ!positions) { // 만약 파라그램이 API에 전송되지 않으면 저장 정보를 반환합니다 positions = _C ((e.GetPosition); //는 API를 호출하여 보유 정보를 얻습니다. ♪ ♪ for (var i = 0; i < positions.length; i++) { //이 저장 정보 배열을 가로지르십시오. if (positions[i].ContractType == contractType && // 현재 인덱스의 보유 정보의 계약 코드 == 매개 변수 지정된 계약 코드 (contractType) 과 방향은 매개 변수 전달 방향 (direction) 과 동일합니다. (((positions[i].Type == PD_LONG의 위치[i].Type == PD_LONG_YD의 위치[i].Type == PD_LONG_YD의 위치[i].Type == PD_SHORT의 위치[i].Type == PD_SHORT_YD의 위치[i].Type == PD_LONG의 위치[i].Type == PD_LONG_YD의 위치[i].Type == PD_LONG_YD의 위치[i].Type == PD_LONG_YD의 위치[i].Type == PD_SHORT의 위치[i].Type == PD_SHORT_YD의 위치[i].Type == PD_SHORT_YD의 위치[i].Type == PD_SHORT_YD의 위치[i].Type == PD_LONG_YD의 위치[i].Type == PD_LONG_YD의 위치[i].Type == PD_LONG_YD의 위치[i].Type == PD_LONG_YD의 위치[i] &&& direction == PD_SHORT의 방향 ) { // if 블록을 조건적으로 실행합니다 posMargin = positions[i].MarginLevel; // 레버리지를 얻음 posMargin에 값을 부여 allCost += (positions[i].Price * positions[i].Amount); // 총 비용 (예약된 계약자 수, 현재 인덱스 보유 가격 * 보유량) 누적 allAmount += positions[i].Amount; // 계약 손수 누적 allProfit += positions[i].Profit; // 계약 유동 이익 손실 누적 allFrozen += positions[i].FrozenAmount; // 얼어붙은 계약 손수 누적 ♪ ♪ ♪ ♪ if (allAmount === 0) { // 만약 탐색이 완료된 후 누적된 적정 계약 수치가 0이라면 null, 즉 조건이 없는 제한된 계약 보유를 반환한다 0을 반환합니다. ♪ ♪ return { // allAmount 0 이 아닌 객체. 마진 레벨: 포스 마진, FrozenAmount: 모든 Frozen, 가격: _N (allCost / allAmount) 양: 모든 양, Profit: allProfit, 이 글의 내용은 계약 타입: contractType }; ♪ ♪

function Open ((e, contractType, direction, opAmount) { // 동작 단일 품종 계약의 오픈 거래 함수, 매개 변수: 거래소 대상, 계약 코드, 방향, 동작 수 var initPosition = GetPosition ((e, contractType, direction); // 위쪽의 GetPosition 함수를 호출하여 결합된 저장 정보를 얻는다. var isFirst = true; // isFirst를 설정합니다. var initAmount = initPosition? initPosition.Amount : 0; // initPosition가 null이라면 initAmount가 0을 부여하고 그렇지 않으면 initPosition.Amount가 0을 부여한다. var positionNow = initPosition; // 변수 positionNow가 현재 저장된 정보를 표시한다고 선언합니다 while (true) { // while 루킹 var needOpen = opAmount; // 임시 변수 needOpen을 선언하고 변수에 대 한 값을 부여 합니다. if (isFirst) { // 처음 실행되면 isFirst만 업데이트합니다. if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (isFirst) { // if (if (isFirst) { // if (if isFirst = false; } else { positionNow = GetPosition ((e, contractType, direction); // 현재 보유 정보를 업데이트합니다. if (positionNow) { // 포지션 정보가 있다면 다음 포지션 개척이 필요한 숫자 needOpen은 파라미터 요구되는 작업의 양과 같습니다. needOpen = opAmount - (positionNow.Amount - initAmount); ♪ ♪ ♪ ♪ var insDetail = _C ((e.SetContractType, contractType); // 계약 유형을 설정하십시오. // Log (( 초기 저장장, initAmount,?? 현재 저장장, positionNow,?? 추가 저장장, needOpen); if (needOpen < insDetail.MinLimitOrderVolume) { // 다음 거래 수가 계약의 한정 명단 최소 거래 수보다 작으면 break; // 루프에서 빠져 나갑니다 ♪ ♪ if (!CanTrade(opAmount)) { // 풍경 제어 모듈 검출, False를 반환하면 회로를 건너고 거래하지 않습니다. 브레이크; ♪ ♪ var depth = _C ((e.GetDepth); // 시장 깊이 정보를 얻는다. var amount = Math.min ((insDetail.MaxLimitOrderVolume, needOpen); // 계약한정된 가격보다 더 많은 주문을 할 수 없습니다. e.SetDirection ((direction == PD_LONG? buy: sell); // 파라미터 direction에 따라 아래 방향 단위를 설정하십시오. var orderId; if (direction == PD_LONG) { // 변수 direction의 방향에 따라 다른 API를 호출하여 거래를 수행합니다 ((다중 또는 빈) orderId = e.Buy ((depth.Asks[0].Price + (insDetail.PriceTick * SlideTick), Math.min ((amount, depth.Asks[0].Amount), contractType, Ask, depth.Asks[0]); // API 문서를 참조하십시오, CTP 상품 선물 가격 점프 한 번 insDetail.PriceTick으로, 이 값의 정수 배가 되어야 합니다. // 호출된 API의 실제 하위 단위는 디스크의 단위 단위보다 크지 않습니다 } else { orderId = e.Sell ((depth.Bids[0].Price - (insDetail.PriceTick * SlideTick), Math.min ((amount, depth.Bids[0].Amount), contractType, Bid, depth.Bids[0]); ♪ ♪ // PendingOrders를 취소합니다 while (true) { // 주문 후 1 Interval 시간 간격으로, 완료되지 않은 주문을 취소합니다。 잠자리 (Interval); var orders = _C ((e.GetOrders); // 완료되지 않은 모든 주문을 가져옵니다 if (orders.length === 0) { // if (orders.length === 0) { // if orderes는 빈 수열이고, 현재 while를 건너가 브레이크; ♪ ♪ for (var j = 0; j < orders.length; j++) { // 미완성된 명령어 배열을 탐색합니다 e.CancelOrder ((order[j].Id); // 현재 인덱스의 주문 정보의 ID에 따라 주문을 취소하십시오. if (j < (orders.length - 1)) { // 일정 시간 간격에 걸쳐서, 한쪽은 빈도가 너무 높습니다。 Sleep ((Interval); // Sleep 일시 중지 Interval 밀리초 ♪ ♪ ♪ ♪ ♪ ♪ } // 주 순환 while에서 종료 var ret = { // 반환하는 객체를 선언합니다 가격: 0, // 거래 평균 가격 양: 0, // 거래량 position: positionNow // 최근에 얻은 이 품종의 보관 정보 }; if (!positionNow) { // 저장 정보가 없으면 초기화 된 ret을 바로 반환합니다 ret; ♪ ♪ if (!initPosition) { // 만약 현재 함수를 실행할 때 그 종류에 대한 저장 정보가 없는 경우}. ret.price = positionNow.Price; // 현재 보유 정보 포지션Now의 가격은 이 거래가 완료된 상태의 보유 평균 가격입니다 ret.amount = positionNow.Amount; // 같은 위 } else { // 이 품종에 대한 저장 정보가 시작될 때 이미 있었다는 경우}. ret.amount = positionNow.Amount - initPosition.Amount; // 오차 값은 새로운 포지션 개척 수 ret.price = _N((((positionNow.Price * positionNow.Amount) - (initPosition.Price * initPosition.Amount)) / ret.amount); // 이 거래의 새로 추가된 비용은 이 거래의 새로 추가된 오픈 포지션으로 나뉘어 평균 가격으로 나다. ♪ ♪ return ret; // ret 를 반환합니다 ♪ ♪

function Cover ((e, contractType) { // 단일 품종 평행함수, 매개 변수: 거래소 객체, 계약 코드 var insDetail = _C ((e.SetContractType, contractType); // 계약 유형을 설정 while (true) { // 주 순환 while var n = 0; // 평면 작업 계산 var opAmount = 0; // 선언 동작 변수 var positions = _C ((e.GetPosition); // API 호출 취득 보관 정보, 위의 구별 취득 보관 함수. 자세한 내용은 API 문서를 참조하십시오. for (var i = 0; i < positions.length; i++) { // 탐색 저장 정보 if (positions[i].ContractType!= contractType) { // 만약 현재 인덱스의 보유 정보가 계약이 동작하는 계약과 같지 않다면: contractType Continue; // 건너뛰기 ♪ ♪ var amount = Math.min ((insDetail.MaxLimitOrderVolume, positions[i].Amount); // 통보의 최대 거래량을 제어하지 않습니다 var depth; if (positions[i].Type == PD_LONG の の positions[i].Type == PD_LONG_YD) { // 다중 포즈를 처리 depth = _C ((e.GetDepth); // API를 호출하여 현재 인프라 데이터를 얻는다 opAmount = Math.min ((amount, depth.Bids[0].Amount); // 제한 작업 크기는 디스크 한 대보다 크지 않습니다. if (!CanTrade ((opAmount)) { // 바람 제어 모듈 검출 ♪ ♪ e.SetDirection ((positions[i].Type == PD_LONG? closebuy_today?? : closebuy??); // 설정 거래 방향, API 문서를 참조하십시오.

            e.Sell(depth.Bids[0].Price - (insDetail.PriceTick * SlideTick), opAmount, contractType, positions[i].Type == PD_LONG ? "平今" : "平昨", 'Bid', depth.Bids[0]);
                                                                                           // 执行平仓 API ,详细参见 API文档。
            n++;                                                                           // 操作计数累加
        } else if (positions[i].Type == PD_SHORT || positions[i].Type == PD_SHORT_YD) {    // 处理 空仓 类似多仓处理
            depth = _C(e.GetDepth);
            opAmount = Math.min(amount, depth.Asks[0].Amount);
            if (!CanTrade(opAmount)) {
                return;
            }
            e.SetDirection(positions[i].Type == PD_SHORT ? "closesell_today" : "closesell");
            e.Buy(depth.Asks[0].Price + (insDetail.PriceTick * SlideTick), opAmount, contractType, positions[i].Type == PD_SHORT ? "平今" : "平昨", 'Ask', depth.Asks[0]);
            n++;
        }
    }
    if (n === 0) {                                                                         // 如果n 等于 0 ,即初始为0 ,在遍历时没有累加,没有可平的仓位。
        break;                                                                             // 跳出主while循环
    }
    while (true) {                                                                         // 间隔一定时间后, 取消所有挂单。类似Open函数的  CancelPendingOrders
        Sleep(Interval);
        var orders = _C(e.GetOrders);
        if (orders.length === 0) {
            break;
        }
        for (var j = 0; j < orders.length; j++) {
            e.CancelOrder(orders[j].Id);
            if (j < (orders.length - 1)) {
                Sleep(Interval);
            }
        }
    }
}

}

var trans = { // 상태 탭에서 상세한 계정 정보를 표시하는 중국어 번역, 사전 AccountID: 투자자 계정 Available: 은 자금으로 사용할 수 있습니다. Balance: 선물 결제 준비 금, BrokerID: 브로커 회사 코드 CashIn: 금차가 커지고, CloseProfit: 평평한 거래가 수익을 올리고 손실을 올리고, Commission : 절차 , 크레딧: 크레딧: 크레딧: 커리 마진 : 현재 보증금의 총액은 , CurrencyID: 동전의 코드, Delivery Margin: 투자자들이 금을 주머니에 넣고, Deposit: 금을 다. ExchangeDeliveryMargin: 거래소는 금을 보증으로 주고, Exchange Margin: 거래소가 금을 보증하고, 얼어붙은 현금: 얼어붙은 현금, FrozenCommission: 얼어붙은 절차 요금 Frozen Margin: 얼어붙은 금의 보증, FundMortgageAvailable: 금융 모기지 잔액, FundMortgageIn: 화폐를 담보로 담보로 담보하고, FundMortgageOut: 금융출금금: 금융출금금: 유인금: 유인금의 수익은 증가하고, 유인금의 수익은 증가하고, InterestBase: 금리 기준, 모기지: 모기지: 모기지: Mortgageable Fund: 모기지 가능한 자금 PositionProfit: 주식을 보유하면 수익을 올리고 손실을 올립니다. PreBalance: 이 마지막으로 결제한 예비 금PreCredit: , 지난번 신용도 떨어졌는데, PreDeposit: 지난번 예금 한계, PreFundMortgageIn: 금융지주가 지난번 대출금액을 PreFundMortgageOut: 이 마지막 대출을 할 때, PreMargin: 가 마지막으로 보유한 보증금은 , , , , , , , , , , , , , , , , , PreMortgage : 마지막 모기지 금액은 , 보존: 은 기본보존입니다. 보유 균형: 보유 기본 선물 결제 예비 금, SettlementID: 결정 번호 SpecProductCloseProfit: 특정 제품 보유 수익/손실 SpecProductCommission: 특정 제품 절차, SpecProductExchange Margin: 특정 상품 거래소에서 금을 보장하는 , SpecProductFrozenCommission: 특산물 냉동 절차, SpecProductFrozenMargin: 특산물 냉동 보증 금강, SpecProduct Margin: 특산품은 보증 금을 차지하고, SpecProductPositionProfit: 특정 제품 보유 수익/손실 SpecProductPositionProfitByAlg: 는 보유 수익/손실 알고리즘에 의해 계산된 특정 제품 보유 수익/손실, TradingDay: 트레이딩데일 금을 꺼내기 위한 : 금을 꺼내기 위한 WithdrawQuota: 는 돈을 빼고, 는 돈을 빼고, 는 돈을 빼고, 는 돈을 빼고, };

function AccountToTable ((jsStr, title) { // 함수 함수는 계정 정보를 상태 탭 표로 내보내는 함수, 매개 변수: 표시하려는 JSON 구조 문자열, 제목 if (typeof(title) === undefined) { // title 파라그램이 입력되지 않은 경우 초기화: 계정 정보 title = 계정 정보 ; ♪ ♪ var tbl = { // 상태 탭에서 표시되는 LogStatus 함수에 전달되는 테이블 객체를 선언합니다 type: table, // 타입 table로 지정 title: title, // 변수 title tbl의 title 필드에 값을 부여 cols: [ 필드 , 설명 , ], // 테이블 열 제목 rows: [] // 테이블의 각 줄에 데이터를 저장하는 배열 필드, 초기에는 빈 배열이다. }; try { // 오차를 감지합니다 var fields = JSON.parse ((jsStr); // jsStr 문자열을 분석 for (var k in fields) { // fields 객체의 속성을 탐색하고, k는 속성 값이며, JS 튜토리얼을 참조할 수 있는지 이해가 되지 않습니다. if (k == AccountID k == BrokerID ) { // 현재 탐색되는 속성이 이 두 가지 속성이라면 건너가라. 계속하세요 ♪ ♪ var desc = trans[k]; // trans 사전의 속성 이름에 따라 중국어 설명 desc에 액세스 var v = fields[k]; // 현재 속성 이름의 값을 얻는다 if (typeof(v) === number) { // 속성 값이 숫자 유형이라면 5비트 소수를 유지한다。 v = _N ((v, 5); ♪ ♪ tbl.rows.push (([k, typeof(desc) === undefined??? : desc, v]); // 현재 속성, 속성 설명, 속성 값 조합의 1차원 배열을 테이블 객체 tbl의 rows 속성 (rws)) 배열에 압축한다. ♪ ♪ } catch (e) {} // 특이한 것을 캡처하지만 처리하지 않습니다 return tbl; // tbl 객체를 반환합니다 ♪ ♪

var PositionManager = (function() { // 변수를 선언 PositionManager는 익명 함수의 반환 값을 받아서 생성된 객체로 반환합니다 function PositionManager ((e) { // PositionManager라는 함수가 익명 함수의 내부에 있다고 선언한다. if (typeof(e) === undefined) { // e 변수가 입력되지 않으면 전 세계 변수 Exchange 객체값을 e로 지정합니다. e = 교환; ♪ ♪ if (e.GetName()!== Futures_CTP) { // 메인 거래소 객체 e가 상품 선물 거래소인지 여부를 검사합니다. throw Only support CTP; // CTP만 지원한다 ♪ ♪ this.e = e; // 현재 함수 (실제로 객체이기도 합니다) 에 e 속성을 추가하고, 변수 e를 부여합니다. this.account = null; // 계정을 추가합니다 변수가 null로 시작됩니다 ♪ ♪ // 캐시를 얻으십시오 PositionManager.prototype.Account = function() { // 위 선언된 PositionManager에 계정 함수를 추가합니다 if (!this.account) { // 만약 PositionManager의 account 속성이 null값이라면 this.account = _C (this.e.GetAccount); // this.e 거래소 개체의 GetAccount 함수 (이즉 거래소 개체 API) 를 호출하여 계정 정보를 얻는다. ♪ ♪ return this.account; // 이 방법은 PositionManager.account 계정 정보를 반환합니다. }; PositionManager.prototype.GetAccount = function ((getTable) { // 추가 방법 이 방법은 최신 계정 정보를 얻는다 this.account = _C ((this.e.GetAccount)); if (typeof(getTable)!== undefined?? && getTable) { // 가장 최근에 검색된 계정 정보의 세부 정보를 객체로 반환하려면 getTable가 true가 되어야 합니다. return AccountToTable ((this.e.GetRawJSON()) // GetRawJSON 함수 자세한 API 문서 참조 ♪ ♪ return this.account; // 업데이트된 계정 정보를 반환합니다. };

PositionManager.prototype.GetPosition = function(contractType, direction, positions) { // 给 PositionManager 添加方法 用于在主策略中调用该模板的 函数
    return GetPosition(this.e, contractType, direction, positions);
};

PositionManager.prototype.OpenLong = function(contractType, shares) {                  // 添加 开多仓 方法
    if (!this.account) {
        this.account = _C(this.e.GetAccount);
    }
    return Open(this.e, contractType, PD_LONG, shares);
};

PositionManager.prototype.OpenShort = function(contractType, shares) {                 // 添加 开空仓 方法
    if (!this.account) {
        this.account = _C(this.e.GetAccount);
    }
    return Open(this.e, contractType, PD_SHORT, shares);
};

PositionManager.prototype.Cover = function(contractType) {                             // 添加 平仓 方法
    if (!this.account) {
        this.account = _C(this.e.GetAccount);
    }
    return Cover(this.e, contractType);
};
PositionManager.prototype.CoverAll = function() {                                      // 添加 所有仓位全平方法
    if (!this.account) {
        this.account = _C(this.e.GetAccount);
    }
    while (true) {
        var positions = _C(this.e.GetPosition)
        if (positions.length == 0) {
            break
        }
        for (var i = 0; i < positions.length; i++) {                                   // 首先平掉 对冲合约 对冲合约 举例 MA709&MA705
            // Cover Hedge Position First
            if (positions[i].ContractType.indexOf('&') != -1) {
                Cover(this.e, positions[i].ContractType)
                Sleep(1000)
            }
        }
        for (var i = 0; i < positions.length; i++) {
            if (positions[i].ContractType.indexOf('&') == -1) {
                Cover(this.e, positions[i].ContractType)
                Sleep(1000)
            }
        }
    }
};
PositionManager.prototype.Profit = function(contractType) {                            // 添加计算收益的方法
    var accountNow = _C(this.e.GetAccount);
    return _N(accountNow.Balance - this.account.Balance);
};

return PositionManager;                                                                // 匿名函数返回 在自身内声明的 PositionManager 函数(对象)。

})();

$.NewPositionManager = function(e) { // 함수를 내보내 PositionManager 객체를 구성합니다 return new 위치 관리자 ((e); };

// 위키백과http://mt.sohu.com/20160429/n446860150.shtml$.IsTrading = function ((symbol) { // 계약이 거래 중인지 판단하는 시간장, 매개 변수 symbol 계약 코드 var now = new Date ((); // 현재 시간 객체를 가져옵니다 var day = now.getDay ((); // 현재 시간을 주 중 어느 특정 날에 얻는다. var hour = now.getHours ((); // 24시간 중 한 시간을 얻는다 var minute = now.getMinutes ((); //분 1분 중 어떤 분을 얻는가

if (day === 0 || (day === 6 && (hour > 2 || hour == 2 && minute > 30))) {              // 第一个过滤, day == 0 星期天  或者  day == 6 星期六并且
    return false;                                                                      // 2点30以后 。 星期五 夜盘结束。  返回 false  即所有品种不在交易时间
}
symbol = symbol.replace('SPD ', '').replace('SP ', '');                                // 正则表达式 匹配其交易系统用“SPD”表示跨期套利交易,若指令买进“SPD CF1609&CF17...
                                                                                       // 过滤掉 跨期套利的 合约编码
var p, i, shortName = "";
for (i = 0; i < symbol.length; i++) {                                                  // 遍历合约代码字符串,取出 代码(排除数字的部分)赋值给shortName 并且转换为大写
    var ch = symbol.charCodeAt(i);
    if (ch >= 48 && ch <= 57) {
        break;
    }
    shortName += symbol[i].toUpperCase();
}

var period = [                                                                         // 通常交易时间  9:00 - 10:15,
    [9, 0, 10, 15],                                                                    //             10:30 - 11:30
    [10, 30, 11, 30],                                                                  //              13:30 - 15:00
    [13, 30, 15, 0]
];
if (shortName === "IH" || shortName === "IF" || shortName === "IC") {                  // 如果是这些 品种,交易时间 period 调整
    period = [
        [9, 30, 11, 30],
        [13, 0, 15, 0]
    ];
} else if (shortName === "TF" || shortName === "T") {                                  // 国债品种  时间调整
    period = [
        [9, 15, 11, 30],
        [13, 0, 15, 15]
    ];
}


if (day >= 1 && day <= 5) {                                                            // 如果是 周一 到周五, 不考虑夜盘。 判断当前时间是否符合 period 设定的时间表
    for (i = 0; i < period.length; i++) {
        p = period[i];
        if ((hour > p[0] || (hour == p[0] && minute >= p[1])) && (hour < p[2] || (hour == p[2] && minute < p[3]))) {
            return true;                                                               // 符合遍历出的  时间表 中的 时间段,  即该品种在交易时间内。
        }
    }
}

var nperiod = [                                                                        // 额外判断 夜盘品种  nperiod[n][0] 是夜盘时间相同的一类
                                                                                       // 品种汇总,nperiod[n][1] 就是该类品种的夜盘交易时间
    [
        ['AU', 'AG'],
        [21, 0, 02, 30]
    ],
    [
        ['CU', 'AL', 'ZN', 'PB', 'SN', 'NI'],
        [21, 0, 01, 0]
    ],
    [
        ['RU', 'RB', 'HC', 'BU'],
        [21, 0, 23, 0]
    ],
    [
        ['P', 'J', 'M', 'Y', 'A', 'B', 'JM', 'I'],
        [21, 0, 23, 30]
    ],
    [
        ['SR', 'CF', 'RM', 'MA', 'TA', 'ZC', 'FG', 'IO'],
        [21, 0, 23, 30]
    ],
];
for (i = 0; i < nperiod.length; i++) {                                                // 遍历所有夜盘品种 交易时间段,对比当前时间。
    for (var j = 0; j < nperiod[i][0].length; j++) {
        if (nperiod[i][0][j] === shortName) {
            p = nperiod[i][1];
            var condA = hour > p[0] || (hour == p[0] && minute >= p[1]);
            var condB = hour < p[2] || (hour == p[2] && minute < p[3]);
            // in one day
            if (p[2] >= p[0]) {
                if ((day >= 1 && day <= 5) && condA && condB) {
                    return true;
                }
            } else {
                if (((day >= 1 && day <= 5) && condA) || ((day >= 2 && day <= 6) && condB)) {
                    return true;
                }
            }
            return false;
        }
    }
}
return false;

};

$.NewTaskQueue = function ((onTaskFinish) { // 다종류 트랜잭션을 수행하는 큐어 객체 구성 함수. 파라그램: 작업이 완료되면 호출 함수. var self = {} // 공허한 객체를 선언합니다 self.ERR_SUCCESS = 0 // 정의 반환 메시지 성공 self.ERR_SET_SYMBOL = 1 // 계약 설정 오류 self.ERR_GET_RECORDS = 2 // K줄을 얻는 오류 self.ERR_GET_ORDERS = 3 // 오더가 완료되지 않은 것을 얻었다 self.ERR_GET_POS = 4 // 저장 정보 획득 오류 self.ERR_TRADE = 5 // 거래 오류 self.ERR_GET_DEPTH = 6 // 깊이 파장을 얻는 오류 self.ERR_NOT_TRADING = 7 // 거래 시간에 없습니다 self.ERR_BUSY = 8 // 차단

self.onTaskFinish = typeof(onTaskFinish) === 'undefined' ? null : onTaskFinish  // 如果在 初始化队列对象时没有 传入需要回调的匿名函数,该属性赋值为null,否则赋值回调函数
self.retryInterval = 300                                                        // 重试间隔 毫秒数
self.tasks = []                                                                 // 这个是一个重要的属性,队列中储存任务的数组。
self.pushTask = function(e, symbol, action, amount, arg, onFinish) {            // 给空对象添加函数,该函数是压入 新任务 到任务数组中。参数分别为:
                                                                                // 交易所对象、合约代码、执行动作、数量、回调函数参数、回调函数
    var task = {                                                                // 构造一个任务对象
        e: e,                                                                   // 交易所对象
        action: action,                                                         // 执行的动作
        symbol: symbol,                                                         // 合约代码
        amount: amount,                                                         // 操作数量
        init: false,                                                            // 是否初始化
        finished: false,                                                        // 是否任务完成
        dealAmount: 0,                                                          // 已处理的 量
        preAmount: 0,                                                           // 上一次的 量
        preCost: 0,                                                             // 上一次的 花费
        retry: 0,                                                               // 重试次数
        maxRetry: 10,                                                           // 最大重试次数
        arg: typeof(onFinish) !== 'undefined' ? arg : undefined,                // 如果没有传入 回调函数,此项 设置为 undefined
        onFinish: typeof(onFinish) == 'undefined' ? arg : onFinish              // 如果没有传入回调函数,把 arg 复制给 onFinish(实际上是 arg没传入,中间隔过去了)
    }
    
    switch (task.action) {                                                      // 根据执行的动作初始化描述信息
        case "buy":
            task.desc = task.symbol + " 开多仓, 数量 " + task.amount
            break
        case "sell":
            task.desc = task.symbol + " 开空仓, 数量 " + task.amount
            break
        case "closebuy":
            task.desc = task.symbol + " 平多仓, 数量 " + task.amount
            break
        case "closesell":
            task.desc = task.symbol + " 平空仓, 数量 " + task.amount
            break
        default:
            task.desc = task.symbol + " " + task.action + ", 数量 " + task.amount
    }

    self.tasks.push(task)                                                       // 压入任务数组中
    Log("接收到任务", task.desc)                                                  // 输出日志 显示 接收到任务。
}

self.cancelAll = function(e) {                                                  // 添加函数,取消所有,参数: 交易所对象
    while (true) {                                                              // 遍历未完成的所有订单,逐个取消。
        var orders = e.GetOrders();
        if (!orders) {                                                          // 所有API 调用都不重试,如果API调用失败,立即返回。
            return self.ERR_GET_ORDERS;
        }
        if (orders.length == 0) {
            break;
        }
        for (var i = 0; i < orders.length; i++) {
            e.CancelOrder(orders[i].Id);
            Sleep(self.retryInterval);
        }
    }
    return self.ERR_SUCCESS                                                      // 返回 完成标记
}

self.pollTask = function(task) {                                                 // 执行数组中弹出的任务
    var insDetail = task.e.SetContractType(task.symbol);                         // 切换到当前 任务 task 对象保存的合约类型
    if (!insDetail) {                                                            // 切换失败 立即返回
        return self.ERR_SET_SYMBOL;
    }
    var ret = null;
    var isCover = task.action != "buy" && task.action != "sell";                 // 根据执行的动作,设置 是否是平仓的 标记
    do {                                                                         // do while 循环,先执行 do 以内
        if (!$.IsTrading(task.symbol)) {                                         // 判断是否在交易时间
            return self.ERR_NOT_TRADING;                                         // 不在交易时间立即返回
        }
        if (self.cancelAll(task.e) != self.ERR_SUCCESS) {                        // 调用全部取消函数 ,如果不等于 完成状态
            return self.ERR_TRADE;                                               // 返回交易失败
        }
        if (!CanTrade(task.amount)) {                                            // 风控模块检测。
            ret = null
            break
        }
        var positions = task.e.GetPosition();                                    // 获取持仓信息
        // Error
        if (!positions) {
            return self.ERR_GET_POS;                                             // 如果调用获取持仓 API 错误,立即返回
        }
        // search position
        var pos = null;
        for (var i = 0; i < positions.length; i++) {                             // 遍历持仓信息,查找持仓合并持仓,类似 上面的 GetPosition 函数
            if (positions[i].ContractType == task.symbol && (((positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) && (task.action == "buy" || task.action == "closebuy")) || ((positions[i].Type == PD_SHORT || positions[i].Type == PD_SHORT_YD) && (task.action == "sell" || task.action == "closesell")))) {
                if (!pos) {
                    pos = positions[i];
                    pos.Cost = positions[i].Price * positions[i].Amount;
                } else {
                    pos.Amount += positions[i].Amount;
                    pos.Profit += positions[i].Profit;
                    pos.Cost += positions[i].Price * positions[i].Amount;
                }
            }
        }
        // record pre position
        if (!task.init) {                                                        // 如果任务没有初始化,执行以下
            task.init = true;                                                    // 更新为已初始化
            if (pos) {                                                           // 如果查找到之前的持仓,把之前的持仓数量、 花费 复制给task 的相应变量保存
                task.preAmount = pos.Amount;
                task.preCost = pos.Cost;
            } else {                                                             // 如果执行这个任务 时没有 ,同样的方向  同样合约的持仓,把task相关变量赋值0
                task.preAmount = 0;
                task.preCost = 0;
                if (isCover) {                                                   // 如果是 平仓动作,输出日志 : 找不到仓位,跳出循环。
                    Log("找不到仓位", task.symbol, task.action);
                    ret = null;
                    break;
                }
            }
        }
        var remain = task.amount;                                                // 声明一个局部变量,用 任务的属性 amount(任务设定的交易量) 初始化
        if (isCover && !pos) {                                                   // 如果 第二次循环中 , 该任务动作是平仓,并且 没有持仓了,给pos 赋值
            pos = {Amount:0, Cost: 0, Price: 0}
        }
        if (pos) {                                                               // 如果 pos 不为null 
            task.dealAmount = pos.Amount - task.preAmount;                       // 已经处理的任务量 等于 每次获取的持仓信息持仓量 与最初开始循环的初始持仓信息持仓量的差值
            if (isCover) {                                                       // 如果是 平仓动作, dealAmount 是负值, 这里取反操作
                task.dealAmount = -task.dealAmount;
            }
            remain = parseInt(task.amount - task.dealAmount);                    // 任务的 交易量 减去 已经处理的交易量  得出 剩余需要处理的交易量
            if (remain <= 0 || task.retry >= task.maxRetry) {                    // 如果剩余需要的交易量小于等于0(此处分析应该是不会小于0,有兴趣的可以分析下。) 或者重试次数大于最大重试上限.
                ret = {                                                          // 更新ret 对象,  更新为已经成交的信息,和 当前仓位信息。
                    price: (pos.Cost - task.preCost) / (pos.Amount - task.preAmount),
                    amount: (pos.Amount - task.preAmount),
                    position: pos
                };
                if (isCover) {                                                   // 如果是 平仓动作
                    ret.amount = -ret.amount;                                    // 平仓时计算出的是负值  ,取反操作
                    if (pos.Amount == 0) {                                       // 如果持仓量为0了, 把ret 的持仓信息 赋值为 null
                        ret.position = null;
                    }
                }
                break;                                                           // remain <= 0 || task.retry >= task.maxRetry 符合这个条件,跳出while循环
            }
        } else if (task.retry >= task.maxRetry) {                                // 如果不是 平仓操作。pos 为null 没有持仓(平仓操作 pos 此处不会是null)
            ret = null;                                                          // 并且 该任务重试测试 大于最大重试次数。跳出循环。
            break;                                                               // 即此时  , 超过最大重试次数,并且 没有增加持仓(开仓 每次都失败了。),跳出循环
        }

        var depth = task.e.GetDepth();                                           // 获取 深度数据
        if (!depth) {
            return self.ERR_GET_DEPTH;                                           // 获取失败立即返回
        }
        var orderId = null;                                                      // 订单ID
        var slidePrice = insDetail.PriceTick * SlideTick;                        // 计算具体滑价值
        if (isCover) {                                                           // 如果是平仓操作
            for (var i = 0; i < positions.length; i++) {                         // 遍历本轮的  API 返回的持仓信息。
                if (positions[i].ContractType !== task.symbol) {                 // 不是当前任务 品种的  跳过。
                    continue;
                }
                if (parseInt(remain) < 1) {                                      // 需要处理的 交易的量 如果小于1,跳出 while
                    break
                }
                var amount = Math.min(insDetail.MaxLimitOrderVolume, positions[i].Amount, remain);  // 在合约规定的最大下单量、持仓量、需要处理的量中取最小值。 
                if (task.action == "closebuy" && (positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD)) {   // 如果是平多仓, 持仓信息 为 今日多仓  或者 昨日多仓
                    task.e.SetDirection(positions[i].Type == PD_LONG ? "closebuy_today" : "closebuy");                  // 设置方向
                    amount = Math.min(amount, depth.Bids[0].Amount)                                                     // 根据盘口量 和 下单量 再取一个最小值。
                    orderId = task.e.Sell(_N(depth.Bids[0].Price - slidePrice, 2), amount, task.symbol, positions[i].Type == PD_LONG ? "平今" : "平昨", 'Bid', depth.Bids[0]);
                                                                                                                        // 执行具体的 API 操作,以下平空类似
                } else if (task.action == "closesell" && (positions[i].Type == PD_SHORT || positions[i].Type == PD_SHORT_YD)) {
                    task.e.SetDirection(positions[i].Type == PD_SHORT ? "closesell_today" : "closesell");
                    amount = Math.min(amount, depth.Asks[0].Amount)
                    orderId = task.e.Buy(_N(depth.Asks[0].Price + slidePrice, 2), amount, task.symbol, positions[i].Type == PD_SHORT ? "平今" : "平昨", 'Ask', depth.Asks[0]);
                }
                // assume order is success insert
                remain -= amount;                                                // 假设是成功执行, 需要处理的交易量 减去 此次交易的量。
            }
        } else {                                                                 // 开仓
            if (task.action == "buy") {
                task.e.SetDirection("buy");
                orderId = task.e.Buy(_N(depth.Asks[0].Price + slidePrice, 2), Math.min(remain, depth.Asks[0].Amount), task.symbol, 'Ask', depth.Asks[0]);
            } else {
                task.e.SetDirection("sell");
                orderId = task.e.Sell(_N(depth.Bids[0].Price - slidePrice, 2), Math.min(remain, depth.Bids[0].Amount), task.symbol, 'Bid', depth.Bids[0]);
            }
        }
        // symbol not in trading or other else happend
        if (!orderId) {                                                          // 没有返回具体的ID ,可能是 交易不在交易队列,或者其他错误。
            task.retry++;                                                        // 累计重试次数
            return self.ERR_TRADE;                                               // 返回错误信息。即使不成功, 重新 执行该任务的时候 会重新一次流程。除了task对象的数据 所有数据都会刷新
        }
    } while (true);                                                              // 循环判断 恒为真
    task.finished = true                                                         // 如果在 while 循环中没有直接 return  顺序执行到此,则任务完成                                                      

    if (self.onTaskFinish) {                                                     // 如果队列控制对象的 回调函数 设置 不为null(即 self.onTaskFinish 存在)
        self.onTaskFinish(task, ret)                                             // 执行回调函数。把 task 任务 对象  和 交易的结果  ret 对象 传入回调函数。 
    }

    if (task.onFinish) {                                                         // 处理 任务的回调函数
        task.onFinish(task, ret);
    }
    return self.ERR_SUCCESS;
}

self.poll = function() {                                                         // 迭代执行 弹出 tasks 中的任务 ,并调用 pollTask 执行任务。
    var processed = 0                                                            // 未执行完成的任务计数 ,每次初始0
    _.each(self.tasks, function(task) {                                          // 迭代  可以搜索 _.each 的用法
        if (!task.finished) {                                                    // 如果 任务不是完成状态,
            processed++                                                          // 未完成任务 计数 累计
            self.pollTask(task)                                                  // 执行弹出的任务
        }
    })
    if (processed == 0) {                                                        // 如果没有未完成的任务,即 所有任务队列内的任务完成 ,执行清空 队列对象中 tasks 数组.
        self.tasks = []
    }
}

self.size = function() {                                                         // 给队列对象添加 函数 size 获取 任务队列 中 任务个数
    return self.tasks.length
}

return self                                                                      // 返回构造好的队列对象

}

$.AccountToTable = 계정에서 테이블로;


더 많은