avatar of 发明者量化-小小梦 发明者量化-小小梦
집중하다 사신
4
집중하다
1271
수행원

당신을 양적화의 세계로 안내합니다 - MACD 양방향 연산 슬라이딩 스톱 로스 코드 분석

만든 날짜: 2016-04-22 13:53:14, 업데이트 날짜: 2023-06-14 10:38:59
comments   6
hits   7443

당신을 양적화의 세계로 안내합니다 - MACD 양방향 연산 슬라이딩 스톱 로스 코드 분석

지난 기사에서 우리는 30 줄의 코드를 단순화하는 전략에 대해 알아봤지만, 이 기사에서는 단계적으로 양자화 초보자를 양자화 전략 설계의 재미를 탐구하는 단계에 더 가깝게 안내합니다. 이 글은 BTC의 현금 거래에 대한 내용으로 이어지고 있는데, 이 글은 금융, 투자, 증권 등에 대한 내용으로 되어있지만, 미래에 대한 거래 과정에 대한 이해도 부족합니다. 눈빛이 흐려지고, 모르는 이름과 용어를 들으며 머리가 어지러지고, ((그것을 실현하는 것도 약간 이해한다! 약간 이해한다!) ᄏ 자신의 백도, 자료 확인 등을 통해 조금씩 뇌를 보완한다。 관련 내용을 확인하고, 기본 개념을 마음속에 가지고, 자신이 약간 아는 JS 언어와 결합하여 간단하게 글을 썼습니다. 처음에는 K선, 평균선, MACD 지표가 무엇인지 몰랐습니다. 여기서 간단히 말해서, K선은 일정 주기 동안의 시장 행보를 기록하여 시장 동력을 관찰하는 데 도움이됩니다. 평준선은 이전 기사에 사용 된 지표이며, MACD 지표와 마찬가지로 시장 행보의 흐름을 반영하는 지표입니다. 이 두가지 지표의 개념, 알고리즘, 공식 추론 등이 다양하게 설명되어 있다. 이해가 안되는 분들은 백도를 참조하십시오.

코드는 다음과 같은 범용 변수를 포함하고 있습니다. 오래된 규칙, 먼저 설명하고, 새는 무시할 수 있습니다.

변수 이름 초기값 설명하다
Interval 2000 이 변수는 설문조사 주기, 즉 프로그램이 일시적으로 대기하는 시간의 양이며, 단위는 밀리초이며, 1000 밀리초는 1초이며, 따라서 이 변수의 초기 값은 2초이다.
STATE_FREE 0 이것은 상태를 나타내는 변수이며, 공백을 나타냅니다. 상태를 판단하기 위해 사용된다.
STATE_BUY 1 이 변수는 상태의 변수입니다. 즉, 다중 지분을 보유하는 것을 의미합니다. 즉, 지분을 구입하는 것을 의미합니다.
STATE_SELL 2 상태 변수, 즉 공백 지분을 보유하는 것을 뜻한다. 즉, 상장하는 것을 뜻한다.
ORDER_INVALID 3 포지션 상태 변수는 포지션이 없는 것을 나타냅니다.
ORDER_VALID 4 “이봐, 이봐, 이봐.
state STATE_FREE 상태 변수, 빈 상태에서 초기화.
SignalDelay 0 처음 계획된 신호는 지연됐고, 지금은 쓸모가 없습니다.
stopProfit 0.002 이 변수는 상당히 중요하며, 정지율, 예를 들어, 본금 * 정지율 ((0.002) 은 최대 손실이 본금의 0.002배이며, 손실의 상한을 나타냅니다.
step 0.5 슬라이드 스톱의 단계 길이는 ◇ 상승, 하락 스톱 가격의 등급
opAmount 1 고정된 작업량
profit 0 손실

포지션 정보를 기록하기 위한 글로벌 오브젝트는 몇 가지 방법을 포함하고 있으며, 주로 슬라이딩 스톱로스를 구현한다.

    var holdOrder = {//持仓信息对象
	    orderState: ORDER_INVALID,// 持仓状态
	    price: 0, //持仓均价
	    amount: 0, //持仓量
	    time: null, // 操作时间
	    stopPrice: 0, // 止损价
	    level: 1,   //止损等级
	    updateCurrentProfit: function(lastPrice,amount){//更新当前盈亏
	        if(state === STATE_SELL){//当前 空头持仓
	        	return (lastPrice - this.price) * amount;
	        }
	        if(state === STATE_BUY){//当前 多头持仓
	        	return - (lastPrice - this.price) * amount;
	        }
	    },
	    SetStopPrice: function(ticker,stopState){//更新止损价
	    	if(stopState === STATE_FREE){ //更新止损时状态 为空闲
	    		return this.stopPrice;
	    	}
	    	if(stopState === STATE_BUY){ //更新止损时状态 为多仓
	            if(this.orderState === ORDER_INVALID){
	        	    return this.stopPrice;
	            }
	            if(this.stopPrice === 0){//初始 止损价为0 时 
	            	this.stopPrice = this.price * ( 1 - stopProfit );
	            }
	            if( ticker.Last <= this.price ){ //最后成交价 小于等于  持仓均价时
	                this.stopPrice = this.price * ( 1 - stopProfit );
	                this.level = 1;
	            }else{//其它情况
	        	    if( ticker.Last - this.price > this.level * step ){//超出当前等级   设置滑动止损
	                    this.stopPrice = this.price * (1 - stopProfit) + (ticker.Last - this.price );
	                    //更新止损价为滑动后的止损价
	                    this.level++;//上调止损等级
	        	    }else{//其它
	        	    	this.stopPrice = this.stopPrice;//保持当前止损价不变
	        	    }
	            }
	    	}else if( stopState === STATE_SELL){//空头持仓类似
	    		if(this.orderState === ORDER_INVALID){
	        	    return this.stopPrice;
	            }
	            if(this.stopPrice === 0){
	            	this.stopPrice = this.price * ( 1 + stopProfit );
	            }
	            if( ticker.Last >= this.price ){
	                this.stopPrice = this.price * ( 1 + stopProfit );
	                this.level = 1; 
	            }else{
	        	    if( this.price - ticker.Last > this.level * step ){
	                    this.stopPrice = this.price * (1 + stopProfit) - ( this.price - ticker.Last );
	                    this.level++;
	        	    }else{
	        	    	this.stopPrice = this.stopPrice;
	        	    }
	            }
	    	}
	        return this.stopPrice;//返回止损价
	    },
	    initHoldOrder: function(){//平仓后  用于 初始化持仓信息的  函数
	        this.orderState = ORDER_INVALID;
	        this.price = 0;
	        this.amount = 0;
	        this.time = null;
	        this.stopPrice = 0;
	        this.level = 1;
	    }
	};
    
  • 코드는 github에 업로드되었습니다. 딸깍 하는 소리github들어가세요.

  • 공식 QQ 그룹에 가입하지 않은 분들은 309368835 발명가 양자화 모임에 가입하시기 바랍니다.


이 함수들을 사용하게 됩니다

function MACD_Cross(){//检测MACD指标,交叉状态的函数
    var records = exchange.GetRecords();//获取K线数据
    while(!records || records.length < 45){ //K线数据不能为null,要大于45个柱,不符合标准 循环获取直到符合
    	records = exchange.GetRecords();
    	Sleep(Interval);
    }
    var macd = TA.MACD(records,12,26,9);//调用指标函数, 参数为MACD 默认的参数。
    var dif = macd[0];  //dif线
    var dea = macd[1];  //dea线
    var column = macd[2]; // MACD柱
    var len = records.length;  //K线周期长度
    if( (dif[len-1] > 0 && dea[len-1] > 0) && dif[len-1] > dea[len-1] && dif[len-2] < dea[len-2] && column[len-1] > 0.2 ){ 
    //判断金叉条件:dif 与 dea 此刻均大于0 , 且dif由下上穿dea , 且 MACD量柱大于0.2
    	return 1; //返回1  代表 金叉信号。
    }
    if( (dif[len-1] < 0 && dea[len-1] < 0) && dif[len-1] < dea[len-1] && dif[len-2] > dea[len-2] && column[len-1] < -0.2 ){
    //判断死叉条件:
        return 2;//返回2  代表 死叉信号。
    }   
    return 0;  //金叉  、死叉  信号以外,为等待信号 0 。
}
function getTimeByNormal(time){// 获取时间的 函数 把毫秒时间 转换 标准时间
    var timeByNormal = new Date();
    timeByNormal.setTime(time);
    var strTime = timeByNormal.toString();
    var showTimeArr = strTime.split(" ");
    var showTime = showTimeArr[3]+"-"+showTimeArr[1]+"-"+showTimeArr[2]+"-"+showTimeArr[4];
    return showTime;
}

아래는 전략의 주요 함수로 들어가기 시작하는데, 이 전략은 이전 30줄의 평균선 전략과 마찬가지로 거래 템플릿 클래스 라이브러리를 사용하며 거래의 세부사항을 포괄하고, 관심있는 친구들은 발명자의 양식에서 코드를 찾을 수 있으며, 코멘트 버전은 공식 QQ 그룹에서 공유되고, github ≫

function main(){
    var initAccount = $.GetAccount(exchange);//首先我们来记录初始时的账户信息,这里调用了模板类库的导出函数
    var nowAccount = initAccount;//再声明一个 变量 表示 现在账户信息
    var diffMoney = 0; //钱 差额
    var diffStocks = 0;//币 差额
    var repair = 0; //计算 盈亏时   用于修正的 量
    var ticker = exchange.GetTicker(); //获取此刻市场行情
    Log("初始账户:",initAccount); //输出显示  初始账户信息。
    while(true){//主函数循环
        scan(); //扫描函数,  稍后讲解,主要是判断  开仓、平仓 以及 操作 开仓 、 平仓。
        ticker = exchange.GetTicker();//在while循环内 获取 市场行情
        if(!ticker){//如果 没有获取到  (null) 跳过以下 重新循环
        	continue;
        }
        if(holdOrder.orderState == ORDER_VALID){//判断当前是否  持仓
        	Log("当前持仓:",holdOrder); //如果 当前持仓   输出  持仓 信息
        }
        if(holdOrder.orderState == ORDER_INVALID){//如果 未持仓(已平仓)
        	nowAccount = $.GetAccount(exchange); //获取当前账户信息
            diffMoney = nowAccount.Balance - initAccount.Balance; //计算  当前账户  与 初始账户之间的  钱 差额
            diffStocks = nowAccount.Stocks - initAccount.Stocks; // 计算  当前账户  与  初始账户之间的  币 差额
            repair = diffStocks * ticker.Last; //把 币的差额 * 最后成交价  ,转为等值的钱, 用于计算 盈亏
            LogProfit(diffMoney + repair ,"RMB","现在账户:",nowAccount,"本次盈亏:",profit);//输出 盈亏 信息
        }
    	Sleep(Interval);//轮询
    }
}

다음으로 전략의 주요 부분인, 포지션 개시 및 포지션 평준화 탐지, 포지션 개시 및 포지션 평준화 운영에 대해 설명합니다.

function scan(){
	var sellInfo = null; //声明  储存平仓信息的变量  , 初始化null
	var buyInfo = null;  //声明  开仓的 , 初始化null
	var opFun = null;//  开仓函数, 两种状态 ,  开多仓 ,  开空仓。
	var singal = 0; //信号
    while(true){//检测 及操作 循环
        var ticker = exchange.GetTicker(); //获取市场行情
        if(!ticker){ //判断 获取失败  跳过以下 ,继续循环获取
        	continue;
        }
        holdOrder.SetStopPrice(ticker,state); //设置 持仓 止损价
        if(state === STATE_FREE &&  (singal = MACD_Cross()) !== 0  ){
        	//判断策略运行状态是否空闲、此刻MACD指标信号是否空闲, 符合 策略运行状态空闲 且 有金叉或死叉执行以下
        	holdOrder.initHoldOrder();//初始化持仓信息
            opFun = singal === 1 ?  $.Buy : $.Sell ;//根据MACD_Cross函数返回结果,判断开多仓、开空仓。
            buyInfo = opFun(opAmount);//开仓操作
            holdOrder.orderState = ORDER_VALID;//设置持仓信息,状态为持仓
            holdOrder.price = buyInfo.price; //设置持仓均价  由 开仓操作函数 opFun返回。 
            holdOrder.amount = buyInfo.amount; //设置持仓量
            holdOrder.time = getTimeByNormal((new Date()).getTime());//设置持仓开始的时间
            state = singal === 1 ? STATE_BUY : STATE_SELL; //更新策略状态为多仓 或 空仓
            var account = $.GetAccount(exchange); //获取账户信息
            if(singal === 1){//输出开仓方向 和 当前账户信息
            	Log("开多仓。","账户:",account);
            }else{
                Log("开空仓。","账户:",account);
            }
            break;
        }else{
        	var lastPrice = holdOrder.price;// 把持仓均价 赋值 给 lastPrice
        	if( state === STATE_BUY && holdOrder.orderState === ORDER_VALID && ticker.Last < holdOrder.stopPrice ){
            //如果 多仓 且 持仓信息为持仓 且 最后成交价 小于止损价,执行以下
        	    Log("多头止损平仓","初始止损价:",holdOrder.price * (1 - stopProfit),"--滑动止损价:",holdOrder.stopPrice,"最后成交价:",ticker.Last,"止损等级:",holdOrder.level);//多头止损平仓信息
        	    sellInfo = $.Sell(holdOrder.amount);//平仓
                holdOrder.orderState = ORDER_INVALID;//平仓信息 更新进对象
                holdOrder.price = sellInfo.price;
                holdOrder.amount = sellInfo.amount;
                holdOrder.time = getTimeByNormal((new Date()).getTime());
                profit = holdOrder.updateCurrentProfit(lastPrice,sellInfo.amount);//更新浮动盈亏
        	    state = STATE_FREE;//更新状态
        	    break;//跳出
        	}
        	if( state === STATE_SELL && holdOrder.orderState === ORDER_VALID && ticker.Last > holdOrder.stopPrice ){//同上 , 这个是空头止损平仓
        	    Log("空头止损平仓","初始止损价:",holdOrder.price * (1 + stopProfit),"--滑动止损价:",holdOrder.stopPrice,"最后成交价:",ticker.Last,"止损等级:",holdOrder.level);//测试
        	    sellInfo = $.Buy(holdOrder.amount);
                holdOrder.orderState = ORDER_INVALID;
                holdOrder.price = sellInfo.price;
                holdOrder.amount = sellInfo.amount;
                holdOrder.time = getTimeByNormal((new Date()).getTime());
                profit = holdOrder.updateCurrentProfit(lastPrice,sellInfo.amount);
        	    state = STATE_FREE;
        	    break;
        	}
            if(state === STATE_BUY && MACD_Cross() === 2 ){//做多时,MACD指标死叉 -- 死叉平仓
        	    sellInfo = $.Sell(holdOrder.amount);
        	    Log("死叉平仓","初始止损价:",holdOrder.price * (1 - stopProfit),"--滑动止损价:",holdOrder.stopPrice,"最后成交价:",ticker.Last,"止损等级:",holdOrder.level);//测试
                holdOrder.orderState = ORDER_INVALID;
                holdOrder.price = sellInfo.price;
                holdOrder.amount = sellInfo.amount;
                holdOrder.time = getTimeByNormal((new Date()).getTime());
                profit = holdOrder.updateCurrentProfit(lastPrice,sellInfo.amount);
        	    state = STATE_FREE;
        	    break;
            }
             if(state === STATE_SELL && MACD_Cross() === 1 ){//做空时,MACD指标金叉 ---金叉平仓
        	    sellInfo = $.Buy(holdOrder.amount);
        	    Log("金叉平仓","初始止损价:",holdOrder.price * (1 + stopProfit),"--滑动止损价:",holdOrder.stopPrice,"最后成交价:",ticker.Last,"止损等级:",holdOrder.level);//测试
                holdOrder.orderState = ORDER_INVALID;
                holdOrder.price = sellInfo.price;
                holdOrder.amount = sellInfo.amount;
                holdOrder.time = getTimeByNormal((new Date()).getTime());
                profit = holdOrder.updateCurrentProfit(lastPrice,sellInfo.amount);
        	    state = STATE_FREE;
        	    break;
            }
        }
        Sleep(Interval);//轮询间隔,就是让程序暂停一会儿。
    }
}

코드가 지겨워서 쉴새없이 물을 마신다~

먼저 슬라이딩 스포드 원칙에 대해 말씀드리겠습니다.

이 부분의 본문은 “Slide Stop Loss”입니다.SetStopPrice함수는 입력된 값에 따라stopState(피해 상태)ticker[시장데이터] 스트로스 가격 업데이트.stopState === STATE_BUY), 각 상황에 따라 판단하고 스톱 손실 가격을 업데이트하십시오.orderState유효하지 않은 상태 ((즉 유효한 포지션을 보유하지 않은 상태) 로) 는 현재 스톱 로스 값을 반환한다. 스톱 로스가 0이라면, 그것을 초기화하여 매수 가격으로 곱한다.(1 - stopProfit)다음으로, 최종 거래 가격에 따라ticker.Last) 및 지분 평균 가격 (this.price현재 스탠드 레벨과this.level) 는 단계 길이 ((step) 의 곱셈과 비교한다. 현재 레벨을 초과하면, 스라이딩 후의 값으로 스톱 프라이스를 업데이트하고, 스톱 프라이스를 증가시킨다. 그렇지 않으면 현재 스톱 프라이스를 그대로 유지한다.stopState === STATE_SELL), 논리적으로 비슷하지만, 최종 거래 가격과 지주 평균 가격의 차이를 마이너스하고, 갱신할 때 그 차이를 니다. 마지막으로, 갱신된 이후의 갱신된 갱신된 갱신된 갱신된 갱신된 갱신된 갱신된 갱신된 갱신된 갱신된 갱신된

슬라이드 스톱은 위험 관리 전략입니다.

포지션을 보유하는 과정에서 시장 가격의 변동에 따라 손실을 줄이거나 이익을 보호하기 위해 중지 가격을 조정합니다. 코드 논리에 따라 다음과 같은 중요한 점을 볼 수 있습니다.updateCurrentProfit현재 손실을 계산하는 방법, 현재 손실을 보유 상태 ((state) 와 최신 가격 ((lastPrice) 에 따라 계산한다. 만약 보유 상태가 매매 상태 ((STATE_SELL) 인 경우, 손실은 최신 가격과 보유 평균 가격의 차이를 보유 금액으로 곱한다. 만약 상위 상태 ((STATE_BUY) 인 경우, 손실은 마이너스이다. SetStopPrice 방법을 사용한다.1 - stopProfit), 그리고 스톱 어드레스를 1으로 재설정한다. 만약 최종 거래 가격이 현재 어드레스의 스텝을 초과한다면, 스톱 어드레스는 스톱 어드레스가 올라간 후의 스톱 어드레스로 설정되고, 스톱 어드레스가 올라간다. 다른 경우 스톱 어드레스는 그대로 유지된다. 만약 스톱 어드레스가 공백 상태라면, 논리는 비슷하다. initHoldOrder 방법은 평점 후에 포지션 정보를 초기화하기 위해 포지션 상태, 평균, 수, 시간, 스톱 어드레스 및 스톱 어드레스를 초기 상태로 재설정한다.

이 템플릿을 참고해 보세요! 다른 글은 수정, 수정 환영합니다.

참고 자료