En el artículo anterior, habíamos aprendido cómo simplificar la estrategia de cuantificación de 30 líneas de código. En este artículo, los autores nos llevan paso a paso, a los principiantes de la cuantificación, un paso más cerca de descubrir el placer de diseñar una estrategia de cuantificación. El artículo continúa, y esta vez sigue siendo una explicación de las operaciones en efectivo con BTC, que el autor no entiende ni siquiera el proceso de transacción de los futuros, ya que no tiene conocimientos sobre finanzas, inversiones y valores. Es más, la gente se desorienta al oír nombres y términos desconocidos, y se desmaya al entenderlos un poquito (¡un poquito!). Después de revisar el contenido, con los conceptos básicos en mente, y con un poco de conocimiento del lenguaje JS, simplemente escribí una. Al principio no entendía lo que era la línea K, la línea media y el indicador MACD. En pocas palabras, la línea K es un indicador que registra la evolución del mercado durante un período determinado, lo que facilita la observación de la dinámica del mercado. La línea media es el indicador utilizado en el artículo anterior, y al igual que el indicador MACD, es un indicador que refleja la tendencia de la evolución del mercado. Los conceptos, algoritmos, fórmulas de deducción de estos dos indicadores se describen uno tras otro. Si no lo entiende, consulte el ciento.
| Nombre de la variable | Valores iniciales | ilustrar |
|---|---|---|
| Interval | 2000 | Esta variable es el ciclo de la encuesta, es decir, el tiempo que el programa espera en pausa, y se mide en milisegundos, 1000 milisegundos es 1 segundo, por lo que el valor inicial de esta variable es de 2 segundos. |
| STATE_FREE | 0 | Esta es una variable de estado que representa un espacio vacío. Se utiliza para juzgar el estado. |
| STATE_BUY | 1 | Esta es una variable de estado que indica la posición de varios titulares. Es decir, comprar la posición. |
| STATE_SELL | 2 | Variable de estado, que indica que la posición está vacía. Es decir, se vende la posición abierta. |
| ORDER_INVALID | 3 | Variable de estado de tenencia, que indica que no se mantiene la posición. |
| ORDER_VALID | 4 | ¿Qué es lo que está pasando? |
| state | STATE_FREE | Variables de estado que se inicializan con un estado vacío. |
| SignalDelay | 0 | La señal fue retrasada y no sirvió de nada. |
| stopProfit | 0.002 | Esta variable es más importante, el Stop Loss, por ejemplo, el capital * Stop Loss ((0.002) indica el máximo de pérdidas de capital de 0.002 veces, el límite de pérdidas. |
| step | 0.5 | La longitud de los pasos para el deslizamiento de la pérdida. Se utiliza para elevar o bajar el nivel de la pérdida. |
| opAmount | 1 | La cantidad fija de operaciones. |
| profit | 0 | ¿Qué es lo que está pasando? |
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;
}
};
El código fue subido a github en: Hacer clicgithubEntradas y Salidas
Si no estás en el grupo oficial de QQ, puedes unirte a: 309368835 Inventor Quantified.
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;
}
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);//轮询间隔,就是让程序暂停一会儿。
}
}
Primero hablemos de los principios de la suspensión de deslizamiento.
En el código de este artículo sobre los pérdidos por deslizamiento,SetStopPriceLa función se basa en la entradastopState(Estado de parada) yticker(Datos del mercado) para actualizar el precio de parada.stopState === STATE_BUY), para evaluar y actualizar el precio de parada en función de las circunstancias.orderStateEn caso de que el precio de la posición no sea válido (es decir, no se tenga una posición válida), se devuelve el precio de parada actual. Si el precio de parada es 0, se inicializa por el precio promedio de compra multiplicado por(1 - stopProfit)│ │ │ │ │ │ │ │ticker.LastEl precio medio de las posicionesthis.priceLa diferencia entre el valor de la tasa de pérdidas y el nivel de pérdidas actualesthis.levelSi se supera el nivel actual, se actualiza el precio de parada como el valor después del deslizamiento, aumentando el nivel de parada; de lo contrario, se mantiene el precio de parada actual sin cambios.stopState === STATE_SELL), la lógica es similar, pero toma un valor negativo para la diferencia entre el precio de transacción final y el precio promedio de la posición, y deduce ese valor al actualizar el precio de parada. Finalmente, devuelve el precio de parada después de la actualización.
El Stop Loss es una estrategia de gestión de riesgos
Durante el proceso de mantenimiento de la posición, el precio de parada se ajusta a la fluctuación de los precios del mercado para reducir las pérdidas o proteger las ganancias. De acuerdo con la lógica del código, se pueden ver los siguientes puntos clave para lograr un stop loss deslizante:updateCurrentProfitMétodo para actualizar la pérdida y pérdida actual, calculada en función del estado de la posición ((state) y el precio más reciente ((lastPrice)). Si la posición está en estado de venta libre ((STATE_SELL), la pérdida es el diferencial entre el precio más reciente y el precio promedio de la posición multiplicado por la cantidad de la posición; si es un estado de venta por adelantado ((STATE_BUY), la pérdida es negativa. El método SetStopPrice se utiliza para actualizar el precio de parada.1 - stopProfitSi el precio final de la operación supera el paso de la escala actual, el precio de la parada se establece como el precio de la parada después del deslizamiento y el nivel de la parada se eleva. En otros casos, el precio de la parada se mantiene inalterado. Si el estado de la parada es en blanco, el método de la initialización de la información de la posición de tenencia similar al método initHoldOrder se utiliza después de la liquidación de la posición, el estado de la posición, la media, la cantidad, el precio de operación, el precio de la parada y el nivel de la parada se restablecen al estado inicial.
Información de referencia