diff --git a/dist/hxrobot-script.js b/dist/hxrobot-script.js index 147a68c..67c6cf9 100644 --- a/dist/hxrobot-script.js +++ b/dist/hxrobot-script.js @@ -19,4 +19,4 @@ var __defProp=Object.defineProperty;var __defNormalProp=(obj,key,value)=>key in parameter: '${this.name}' 'increment' options '${this.options.increment}' isn't set correctly (increment: ${this.increment}) (option ignored) - `);else if(this.options.increment==="auto"){let countIncrement=(this.max-this.min+this.increment)/this.increment;this.countMaxIncrement Parameter increment slider ${this.getCurrent()} + ${this.increment}`),incrementalValue=this.getCurrent()+this.increment):(this.debug&&console.log(`-> Parameter set cursor slider (cursor: ${cursor}) ${this.min} + (${this.increment} * ${cursor})`),incrementalValue=this.min+this.increment*cursor),incrementalValue=cleanFloat(incrementalValue,this.incrementDecimals),incrementalValue<=this.max?this.sliderSetValue(incrementalValue):console.warn(`bad increment ${this.getCurrent()} + ${this.increment} for a max ${this.max} (${this.name})`)}switchInit(options={}){this.type="switch",this.count=2,this.switchDOM=this.parameterDOM.querySelector(".vue-js-switch"),this.options={...this.optionsDefault,...options}}switchReset(){this.switchSetValue(!1)}switchGetCurrent(){return this.switchDOM.classList.contains("toggled")}switchSetValue(value="increment"){this.debug&&console.log(`-> Parameter switchSetValue (${value}) ${this.switchGetCurrent()}`),(value==="increment"||value&&!this.switchGetCurrent()||!value&&this.switchGetCurrent())&&this.switchDOM.click()}selectInit(options={}){this.type="select",this.selectDOM=this.parameterDOM.querySelector("select.option"),this.count=this.selectDOM.querySelectorAll("option").length,this.options={...this.optionsDefault,...options},this.min=0,this.max=this.count,this.increment=1}selectReset(){this.selectSetValue(this.min)}selectGetCurrent(){return this.selectDOM.selectedIndex}selectSetValue(value){this.selectDOM.selectedIndex=parseInt(value);let evt=document.createEvent("HTMLEvents");evt.initEvent("change",!1,!0),this.selectDOM.dispatchEvent(evt)}selectIncrement(cursor="increment"){let newValue=0;cursor==="increment"?newValue=this.getCurrent()+this.increment:newValue=this.min+this.increment*cursor,this.min<=newValue&&newValue<=this.max?this.selectSetValue(newValue):console.warn(`bad select increment ${this.getCurrent()} + ${this.increment} for a max ${this.max} (${this.name})`)}optionalSliderInit(options={}){this.switchDOM=this.parameterDOM.querySelector(".vue-js-switch"),this.switchGetCurrent()||this.switchSetValue(!0),this.sliderInit(options),this.type="optionalSlider",this.count++,this.options={...this.optionsDefault,...options}}optionalSliderReset(){this.switchSetValue(!0),this.sliderReset(),this.switchReset()}optionalSliderGetCurrent(){return this.switchGetCurrent()?this.sliderGetCurrent():!1}optionalSliderIncrement(cursor="increment"){cursor==="increment"||cursor==="decrement"?this.switchGetCurrent()?this.sliderIncrement(cursor):this.switchSetValue(!0):cursor===0?this.switchSetValue(!1):(this.switchSetValue(!0),this.sliderIncrement(cursor-1))}};var Papa=require_papaparse_min(),Strategy=class{constructor(strategy=null){__publicField(this,"label");__publicField(this,"strategy");__publicField(this,"indicators");__publicField(this,"indicator");__publicField(this,"parameters",[]);__publicField(this,"results",[]);__publicField(this,"infos",{});__publicField(this,"estimateTimeByTest",16e3);__publicField(this,"overloadTime",6e4);__publicField(this,"intervalTime",500);__publicField(this,"jumpTestAfterStart",0);__publicField(this,"jumpParamNumber",!1);__publicField(this,"jumpParamIndex",!1);__publicField(this,"jumpTestStack",0);__publicField(this,"jumpTestsParamByTrade",!1);__publicField(this,"jumpTestsParamByEarning",!1);__publicField(this,"jump3TestByEarning",!1);__publicField(this,"jump2TestByEarning",!1);__publicField(this,"jump1TestByEarning",!1);__publicField(this,"jumpTestByTrade",!1);__publicField(this,"jumpTestByWinrate",!1);__publicField(this,"jumpTestByEarningMinus",!1);__publicField(this,"jumpedTest",{});__publicField(this,"backtestNumber",0);__publicField(this,"backtestTotal",0);__publicField(this,"debug",!1);__publicField(this,"dateStart",0);__publicField(this,"dateEnd",0);__publicField(this,"started",!1);if(typeof strategy=="number")this.strategy=document.querySelectorAll(".strategy")[strategy];else return;this.interfaceDecorator("available"),this.insertLabel(),this.strategy.querySelector(".indicators")||console.error("indicators panel is closed. Opening it before...")}init(options={}){if(this.started){console.warn("Strategy already started, use stop() method first");return}if(!this.strategy.querySelector(".indicators")){console.error("indicators panel is closed. Opening it before...");return}this.reset(),this.options=options,this.indicators=this.strategy.querySelector(".indicators"),this.infos=this.collectInfos(),this.indicator=this.indicators.querySelector(".indicator");let parametersDOM=this.indicator.querySelectorAll(".element");options.debug&&(this.debug=options.debug);let gap=0;for(let i=0;i{totalJumpedTest+=value}),0 Strategy reset"),this.parameters.forEach(parameter=>{parameter.reset()})}async validate(){if(this.debug&&console.log("-> Strategy start validate"),await this.validateClick()){let backtestRespond=await this.validateWaiting();for(;!backtestRespond&&this.started;)this.debug&&console.log("-> While force update"),await this.forceUpdateBT(),await this.validateClick(),backtestRespond=await this.validateWaiting();await sleep(500)}return this.saveResults(),this.debug&&console.log("-> Strategy end validate"),!0}async validateClick(){return await sleep(1e3),this.strategy.querySelector(".overlay").style.display==="none"?(console.warn("nothing to validate"),!1):(this.strategy.querySelector(".perf .pill.save").click(),!0)}async validateWaiting(){return await new Promise(resolve=>{let duration=0,interval=setInterval(()=>{if(this.strategy.querySelector(".graph").style.opacity==="1")return resolve(!0),clearInterval(interval),!0;if(this.overloadTime Strategy backtest [${paramIndex}]`),this.parameters[paramIndex].options.ignore)this.debug&&console.log(`--> parameter[${paramIndex}] ignored`),await this.backtestParam(paramIndex);else{this.debug&&console.log(`--> parameter[${paramIndex}] to work x${this.parameters[paramIndex].count}`);for(let i=0;i parameter[${paramIndex}] in for i = [${i}] set increment`),(i parameter[${paramIndex}] reset`),this.parameters[paramIndex].reset())}return this.debug&&console.log(`--> parameter[${paramIndex}] finished`),!0}async backtestParam(paramIndex=0){return this.started?(paramIndex+1 parameter[${paramIndex}] go to parameter[${paramIndex+1}]`),await this.backtest(paramIndex+1)):(this.debug&&console.log(`--> parameter[${paramIndex}] validate`),this.jumpTestStack?this.jumpTestStack--:(await this.validate(),this.addJump()),this.backtestNumber++,this.updateLabelProgress()),!0):!1}async forceUpdateBT(){await this.prepareStopLimitPanel();let inp=this.strategy.querySelector('.content-stop-limit input[type="number"]'),ev=new Event("input");inp.value=parseInt(inp.value)+10,inp.dispatchEvent(ev)}async prepareStopLimitPanel(){this.strategy.querySelectorAll(".risk-management-container .title-right .name")[1].click(),await sleep(500),this.strategy.querySelector(".content-stop-limit .vue-js-switch").classList.contains("toggled")||this.strategy.querySelector(".content-stop-limit .vue-js-switch").click()}getJumpParamIndex(){for(let i=this.parameters.length-1;i>=0;i--)if(!this.parameters[i].options.ignore)return this.parameters[i].type==="slider"||this.parameters[i].type==="optionalSlider"?i:!1;return!1}addJump(){if(this.jumpParamIndex&&this.parameters[this.jumpParamIndex].isReadyToJump()){let remainingCursor=this.parameters[this.jumpParamIndex].getRemainingCursor();if(remainingCursor){let current=this.getCurrentResult(),jumped=!1;if(this.jumpTestsParamByTrade!==!1&&(jumped=this.checkJumpTestsParamByTrade(current)),!jumped&&this.jumpTestsParamByEarning!==!1&&(jumped=this.checkJumpTestsParamByEarning(current)),!jumped&&this.jump3TestByEarning!==!1&&3<=remainingCursor&&(jumped=this.checkJump3TestByEarning(current)),!jumped&&this.jump2TestByEarning!==!1&&2<=remainingCursor&&(jumped=this.checkJump2TestByEarning(current)),!jumped&&this.jump1TestByEarning!==!1&&(jumped=this.checkJump1TestByEarning(current)),!jumped&&this.jumpTestByTrade!==!1&&(jumped=this.checkJumpTestByTrade(current)),!jumped&&this.jumpTestByWinrate!==!1&&(jumped=this.checkJumpTestByWinrate(current)),!jumped&&1indics.push(indic.innerText)),{timeframe:document.querySelector(".contests .choice2.selected").innerText.trim(),currency:document.querySelector(".contests .choice3.selected").innerText.trim(),earningCurrency:document.querySelector(".category-rm .vue-js-switch.toggled")?"BTC":"HXRO",currentIndicator:this.indicators.querySelector(".indicator-title .name.selected").innerText.trim(),associateIndicator:indics}}preCalculate(){this.backtestTotal=1;let countCursor=0;this.parameters.forEach(parameter=>{parameter.options.ignore||(this.backtestTotal*=parameter.count,countCursor+=parameter.count)});let totalTime=this.estimateTimeByTest*this.backtestTotal,evalIndicator,evalCountTest,evalEstimateDuringTime,evalEstimateEndingTime;if(0 Strategy save results");let additionalData=[];additionalData.date=new Date().toLocaleString(),this.results.push({...additionalData,...this.getPerfData(),...this.getParamData()})}exportResults(){this.debug&&console.log("-> Strategy export results");let filename=`${new Date().toISOString().slice(0,10)}_${this.infos.currentIndicator}-${this.infos.currency}-${this.infos.timeframe}.csv`,csv=Papa.unparse(this.results),blob=new Blob([csv]),a=window.document.createElement("a");a.href=window.URL.createObjectURL(blob,{type:"text/plain"}),a.download=filename,document.body.appendChild(a),a.click(),document.body.removeChild(a)}getPerfData(){let perfPanel=this.strategy.querySelectorAll(".perf-stats .stat-perf"),perfData=[];return perfData.earning=parseFloat(this.strategy.querySelector(".perf-stats .backtest-perf").childNodes[0].nodeValue.trim().replace(/[^\d.-]/g,"")),perfData.winrate=parseFloat(perfPanel[0].querySelector(".number").innerText.replace(/[^\d.-]/g,"")),perfData.payout=parseFloat(perfPanel[3].querySelector(".number").innerText.replace(/[^\d.-]/g,"")),perfData.drawdown=parseFloat(perfPanel[4].querySelector(".number").innerText),perfData.ratio=parseFloat(0{paramData[parameter.name]=parameter.getCurrent()}),paramData}getCurrentResult(){return this.results[this.results.length-1]}getPreviousResult(){return this.results[this.results.length-2]}debug(){console.log(`--INDICATOR: ${this.infos.currentIndicator}`),console.log(`countParameters: ${this.parameters.length}`),this.parameters.forEach(parameter=>{parameter.debug()})}};__publicField(Strategy,"version","1.3.2");var importStrategy=new Strategy;console.groupCollapsed(`HXROBOT-SCRIPT v${Strategy.version}`);console.log("%c// 1. choose the strategy panel","color:#ccc");console.log("let strat1 = new Strategy(0);");console.log("%c// 2. init with options","color:#ccc");console.log("strat1.init();");console.log("%c// 3. start !","color:#ccc");console.log("strat1.start();");console.groupEnd(); + `);else if(this.options.increment==="auto"){let countIncrement=(this.max-this.min+this.increment)/this.increment;this.countMaxIncrement Parameter increment slider ${this.getCurrent()} + ${this.increment}`),incrementalValue=this.getCurrent()+this.increment):(this.debug&&console.log(`-> Parameter set cursor slider (cursor: ${cursor}) ${this.min} + (${this.increment} * ${cursor})`),incrementalValue=this.min+this.increment*cursor),incrementalValue=cleanFloat(incrementalValue,this.incrementDecimals),incrementalValue<=this.max?this.sliderSetValue(incrementalValue):console.warn(`bad increment ${this.getCurrent()} + ${this.increment} for a max ${this.max} (${this.name})`)}switchInit(options={}){this.type="switch",this.count=2,this.switchDOM=this.parameterDOM.querySelector(".vue-js-switch"),this.options={...this.optionsDefault,...options}}switchReset(){this.switchSetValue(!1)}switchGetCurrent(){return this.switchDOM.classList.contains("toggled")}switchSetValue(value="increment"){this.debug&&console.log(`-> Parameter switchSetValue (${value}) ${this.switchGetCurrent()}`),(value==="increment"||value&&!this.switchGetCurrent()||!value&&this.switchGetCurrent())&&this.switchDOM.click()}selectInit(options={}){this.type="select",this.selectDOM=this.parameterDOM.querySelector("select.option"),this.count=this.selectDOM.querySelectorAll("option").length,this.options={...this.optionsDefault,...options},this.min=0,this.max=this.count,this.increment=1}selectReset(){this.selectSetValue(this.min)}selectGetCurrent(){return this.selectDOM.selectedIndex}selectSetValue(value){this.selectDOM.selectedIndex=parseInt(value);let evt=document.createEvent("HTMLEvents");evt.initEvent("change",!1,!0),this.selectDOM.dispatchEvent(evt)}selectIncrement(cursor="increment"){let newValue=0;cursor==="increment"?newValue=this.getCurrent()+this.increment:newValue=this.min+this.increment*cursor,this.min<=newValue&&newValue<=this.max?this.selectSetValue(newValue):console.warn(`bad select increment ${this.getCurrent()} + ${this.increment} for a max ${this.max} (${this.name})`)}optionalSliderInit(options={}){this.switchDOM=this.parameterDOM.querySelector(".vue-js-switch"),this.switchGetCurrent()||this.switchSetValue(!0),this.sliderInit(options),this.type="optionalSlider",this.count++,this.options={...this.optionsDefault,...options}}optionalSliderReset(){this.switchSetValue(!0),this.sliderReset(),this.switchReset()}optionalSliderGetCurrent(){return this.switchGetCurrent()?this.sliderGetCurrent():!1}optionalSliderIncrement(cursor="increment"){cursor==="increment"||cursor==="decrement"?this.switchGetCurrent()?this.sliderIncrement(cursor):this.switchSetValue(!0):cursor===0?this.switchSetValue(!1):(this.switchSetValue(!0),this.sliderIncrement(cursor-1))}};var Papa=require_papaparse_min(),Strategy=class{constructor(strategy=null){__publicField(this,"label");__publicField(this,"strategy");__publicField(this,"indicators");__publicField(this,"indicator");__publicField(this,"parameters",[]);__publicField(this,"results",[]);__publicField(this,"infos",{});__publicField(this,"estimateTimeByTest",16e3);__publicField(this,"overloadTime",6e4);__publicField(this,"intervalTime",500);__publicField(this,"jumpTestAfterStart",0);__publicField(this,"jumpParamNumber",!1);__publicField(this,"jumpParamIndex",!1);__publicField(this,"jumpTestStack",0);__publicField(this,"jumpAuto",!1);__publicField(this,"jumpAutoSeries",!1);__publicField(this,"jumpTestsParamByTrade",!1);__publicField(this,"jumpTestsParamByEarning",!1);__publicField(this,"jump3TestByEarning",!1);__publicField(this,"jump2TestByEarning",!1);__publicField(this,"jump1TestByEarning",!1);__publicField(this,"jumpTestByTrade",!1);__publicField(this,"jumpTestByWinrate",!1);__publicField(this,"jumpTestByEarningMinus",!1);__publicField(this,"jumpedTest",{});__publicField(this,"backtestNumber",0);__publicField(this,"backtestTotal",0);__publicField(this,"countOverloadTime",0);__publicField(this,"debug",!1);__publicField(this,"dateStart",0);__publicField(this,"dateEnd",0);__publicField(this,"started",!1);__publicField(this,"jumpAutoConfig",{"5m":{jump1TestByEarning:0,jump2TestByEarning:-1e3,jump3TestByEarning:-3500,jumpTestByTrade:20,jumpTestByWinrate:52},"15m":{jump1TestByEarning:0,jump2TestByEarning:-400,jump3TestByEarning:-800,jumpTestByTrade:15,jumpTestByWinrate:52.5}});__publicField(this,"jumpAutoSeriesConfig",{"5m":{jumpTestsParamByTrade:0,jumpTestsParamByEarning:-5e3},"15m":{jumpTestsParamByTrade:0,jumpTestsParamByEarning:-1200}});if(typeof strategy=="number")this.strategy=document.querySelectorAll(".strategy")[strategy];else return;this.interfaceDecorator("available"),this.insertLabel(),this.strategy.querySelector(".indicators")||console.error("indicators panel is closed. Opening it before...")}init(options={}){if(this.started){console.warn("Strategy already started, use stop() method first");return}if(!this.strategy.querySelector(".indicators")){console.error("indicators panel is closed. Opening it before...");return}this.reset(),this.options=options,this.indicators=this.strategy.querySelector(".indicators"),this.infos=this.collectInfos(),this.indicator=this.indicators.querySelector(".indicator");let parametersDOM=this.indicator.querySelectorAll(".element");options.debug&&(this.debug=options.debug);let gap=0;for(let i=0;i{totalJumpedTest+=value}),0 Strategy reset"),this.parameters.forEach(parameter=>{parameter.reset()})}async validate(){if(this.debug&&console.log("-> Strategy start validate"),await this.validateClick()){let backtestRespond=await this.validateWaiting();for(;!backtestRespond&&this.started;)this.debug&&console.log("-> While force update"),await this.incrementStopLimitField(),await this.validateClick(),backtestRespond=await this.validateWaiting();await sleep(500)}return this.saveResults(),this.debug&&console.log("-> Strategy end validate"),!0}async validateClick(){return await sleep(1e3),this.strategy.querySelector(".overlay").style.display==="none"?(console.warn("nothing to validate"),!1):(this.strategy.querySelector(".perf .pill.save").click(),!0)}async validateWaiting(){return await new Promise(resolve=>{let duration=0,interval=setInterval(()=>{if(this.strategy.querySelector(".graph").style.opacity==="1")return resolve(!0),clearInterval(interval),!0;if(this.overloadTime Strategy backtest [${paramIndex}]`),this.parameters[paramIndex].options.ignore)this.debug&&console.log(`--> parameter[${paramIndex}] ignored`),await this.backtestParam(paramIndex);else{this.debug&&console.log(`--> parameter[${paramIndex}] to work x${this.parameters[paramIndex].count}`);for(let i=0;i parameter[${paramIndex}] in for i = [${i}] set increment`),(i parameter[${paramIndex}] reset`),this.parameters[paramIndex].reset())}return this.debug&&console.log(`--> parameter[${paramIndex}] finished`),!0}async backtestParam(paramIndex=0){return this.started?(paramIndex+1 parameter[${paramIndex}] go to parameter[${paramIndex+1}]`),await this.backtest(paramIndex+1)):(this.debug&&console.log(`--> parameter[${paramIndex}] validate`),this.jumpTestStack?this.jumpTestStack--:(await this.validate(),this.addJump()),this.backtestNumber++,this.updateLabelProgress()),!0):!1}async incrementStopLimitField(value=!1){await this.prepareStopLimitField();let inp=this.strategy.querySelector('.content-stop-limit input[type="number"]'),ev=new Event("input");value?inp.value=parseInt(value):inp.value=parseInt(inp.value)+10,inp.dispatchEvent(ev)}async prepareStopLimitField(){this.strategy.querySelectorAll(".risk-management-container .title-right .name")[1].click(),await sleep(500),this.strategy.querySelector(".content-stop-limit .vue-js-switch").classList.contains("toggled")||this.strategy.querySelector(".content-stop-limit .vue-js-switch").click()}getJumpParamIndex(){for(let i=this.parameters.length-1;i>=0;i--)if(!this.parameters[i].options.ignore)return this.parameters[i].type==="slider"||this.parameters[i].type==="optionalSlider"?i:!1;return!1}addJump(){if(this.jumpParamIndex&&this.parameters[this.jumpParamIndex].isReadyToJump()){let remainingCursor=this.parameters[this.jumpParamIndex].getRemainingCursor();if(remainingCursor){let current=this.getCurrentResult(),jumped=!1;if(this.jumpTestsParamByTrade!==!1&&(jumped=this.checkJumpTestsParamByTrade(current)),!jumped&&this.jumpTestsParamByEarning!==!1&&(jumped=this.checkJumpTestsParamByEarning(current)),!jumped&&this.jump3TestByEarning!==!1&&3<=remainingCursor&&(jumped=this.checkJump3TestByEarning(current)),!jumped&&this.jump2TestByEarning!==!1&&2<=remainingCursor&&(jumped=this.checkJump2TestByEarning(current)),!jumped&&this.jump1TestByEarning!==!1&&(jumped=this.checkJump1TestByEarning(current)),!jumped&&this.jumpTestByTrade!==!1&&(jumped=this.checkJumpTestByTrade(current)),!jumped&&this.jumpTestByWinrate!==!1&&(jumped=this.checkJumpTestByWinrate(current)),!jumped&&1indics.push(indic.innerText)),{timeframe:document.querySelector(".contests .choice2.selected").innerText.trim(),currency:document.querySelector(".contests .choice3.selected").innerText.trim(),earningCurrency:document.querySelector(".category-rm .vue-js-switch.toggled")?"BTC":"HXRO",currentIndicator:this.indicators.querySelector(".indicator-title .name.selected").innerText.trim(),associateIndicator:indics}}preCalculate(){this.backtestTotal=1;let countCursor=0;this.parameters.forEach(parameter=>{parameter.options.ignore||(this.backtestTotal*=parameter.count,countCursor+=parameter.count)});let totalTime=this.estimateTimeByTest*this.backtestTotal,evalIndicator,evalCountTest,evalEstimateDuringTime,evalEstimateEndingTime;if(0 Strategy save results");let additionalData=[];additionalData.date=new Date().toLocaleString(),this.results.push({...additionalData,...this.getPerfData(),...this.getParamData()})}exportResults(){this.debug&&console.log("-> Strategy export results");let filename=`${new Date().toISOString().slice(0,10)}_${this.infos.currentIndicator}-${this.infos.currency}-${this.infos.timeframe}.csv`,csv=Papa.unparse(this.results),blob=new Blob([csv]),a=window.document.createElement("a");a.href=window.URL.createObjectURL(blob,{type:"text/plain"}),a.download=filename,document.body.appendChild(a),a.click(),document.body.removeChild(a)}getPerfData(){let perfPanel=this.strategy.querySelectorAll(".perf-stats .stat-perf"),perfData=[];return perfData.earning=parseFloat(this.strategy.querySelector(".perf-stats .backtest-perf").childNodes[0].nodeValue.trim().replace(/[^\d.-]/g,"")),perfData.winrate=parseFloat(perfPanel[0].querySelector(".number").innerText.replace(/[^\d.-]/g,"")),perfData.payout=parseFloat(perfPanel[3].querySelector(".number").innerText.replace(/[^\d.-]/g,"")),perfData.drawdown=parseFloat(perfPanel[4].querySelector(".number").innerText),perfData.ratio=parseFloat(0{paramData[parameter.name]=parameter.getCurrent()}),paramData}getCurrentResult(){return this.results[this.results.length-1]}getPreviousResult(){return this.results[this.results.length-2]}debug(){console.log(`--INDICATOR: ${this.infos.currentIndicator}`),console.log(`countParameters: ${this.parameters.length}`),this.parameters.forEach(parameter=>{parameter.debug()})}};__publicField(Strategy,"version","1.4.0");var importStrategy=new Strategy;console.groupCollapsed(`HXROBOT-SCRIPT v${Strategy.version}`);console.log("%c// 1. choose the strategy panel","color:#ccc");console.log("let strat1 = new Strategy(0);");console.log("%c// 2. init with options","color:#ccc");console.log("strat1.init();");console.log("%c// 3. start !","color:#ccc");console.log("strat1.start();");console.groupEnd(); diff --git a/src/Strategy.js b/src/Strategy.js index 19f34eb..55245e0 100644 --- a/src/Strategy.js +++ b/src/Strategy.js @@ -3,7 +3,7 @@ import {Parameter} from './Parameter'; import {cleanFloat, msToTime, sleep} from './Utils'; export class Strategy { - static version = '1.3.2'; + static version = '1.4.0'; label; // DOM element strategy; // DOM element