@vibek/strategy 0.6.9 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +1 -1
- package/testing.js +32 -10
package/README.md
CHANGED
package/package.json
CHANGED
package/testing.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var wt=import.meta.require;function w(e){return e.status==="submitting"||e.status==="notTraded"||e.status==="partTraded"}var br=Symbol.for("vibek.strategy.cta-template-runtime");function St(e){if(!e||typeof e!=="object")return null;let t=e[br];if(typeof t!=="function")return null;let r=t.call(e);if(!r||typeof r.bindContext!=="function"||typeof r.restoreVariables!=="function"||typeof r.setRuntimeState!=="function"||typeof r.getStateVariables!=="function")return null;return r}var we=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;var kr=["strategyName","vtSymbol","mode","inited","trading","pos"],Xn=new Set([...kr,"constructor","__proto__","prototype","onInit","onStart","onStop","onTick","onBar","onTrade","onOrder","buy","sell","short","cover","cancelOrder","cancelAll","loadBars","registerEvent","claimOrder","writeLog","emitHook","getCurrentTime","getContract","getCommission","getPriceTick","getSize"]);function Se(e,t,r){if(!Sr(e))throw Error("\u7B56\u7565 default export \u5FC5\u987B\u662F\u5BF9\u8C61");let n=Object.keys(e).find((p)=>p!=="strategy");if(n)throw Error(`\u7B56\u7565\u5B9A\u4E49\u4E0D\u652F\u6301\u5B57\u6BB5 "${n}"\uFF1Bid\u3001vtSymbol \u548C setting \u5FC5\u987B\u5728\u52A0\u8F7D\u65F6\u4F20\u5165`);if(typeof e.strategy!=="function")throw Error(`\u7B56\u7565 ${t} \u7684 strategy \u5FC5\u987B\u662F\u7C7B`);if(!Sr(r))throw Error(`\u7B56\u7565 ${t} \u7684 setting \u5FC5\u987B\u662F\u5BF9\u8C61`);let i=e.strategy,o=wr(i.parameters,"parameters",t),s=wr(i.variables,"variables",t),a=[...o,...s].find((p)=>Xn.has(p));if(a)throw Error(`\u7B56\u7565 ${t} \u7684 parameters/variables \u4E0D\u5E94\u58F0\u660E\u6846\u67B6\u5B57\u6BB5 ${a}`);let d=o.find((p)=>s.includes(p));if(d)throw Error(`\u7B56\u7565 ${t} \u7684\u5B57\u6BB5 ${d} \u4E0D\u80FD\u540C\u65F6\u5C5E\u4E8E parameters \u548C variables`);let c=Object.keys(r).find((p)=>!o.includes(p));if(c)throw Error(`\u7B56\u7565 ${t} \u4E0D\u652F\u6301\u53C2\u6570 "${c}"`);let u;try{u=new i}catch(p){throw Error(`\u7B56\u7565 ${t} \u6784\u9020\u5931\u8D25: ${p instanceof Error?p.message:String(p)}`)}let l=St(u);if(!l||typeof u.onInit!=="function")throw Error(`\u7B56\u7565 ${t} \u5FC5\u987B\u7EE7\u627F CtaTemplate \u5E76\u5B9E\u73B0 onInit()`);for(let p of kr)if(Object.hasOwn(u,p))throw Error(`\u7B56\u7565 ${t} \u4E0D\u5F97\u58F0\u660E\u6846\u67B6\u5B57\u6BB5 ${p}`);for(let p of[...o,...s])if(!(p in u))throw Error(`\u7B56\u7565 ${t} \u58F0\u660E\u4E86\u5B57\u6BB5 ${p}\uFF0C\u4F46\u5B9E\u4F8B\u6CA1\u6709\u5BF9\u5E94\u9ED8\u8BA4\u503C`);let f=o.map((p)=>{let k=Tr(ti(u,p),`\u7B56\u7565 ${t} \u53C2\u6570 ${p} \u9ED8\u8BA4\u503C`);return{name:p,defaultValue:k,kind:ei(k)}}),h=vr(r,`\u7B56\u7565 ${t} setting`),m=Object.fromEntries(f.map((p)=>[p.name,Object.hasOwn(h,p.name)?h[p.name]:p.defaultValue]));return{definition:{strategy:i},ctor:i,instance:u,runtime:l,effectiveSetting:vr(m,`\u7B56\u7565 ${t} \u751F\u6548 setting`),inspection:{name:i.name,author:i.author??"",parameters:f}}}function wr(e,t,r){if(e===void 0)return[];if(!Array.isArray(e))throw Error(`\u7B56\u7565 ${r} \u7684 ${t} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4`);let n=e.map((i,o)=>{if(typeof i!=="string"||!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(i))throw Error(`\u7B56\u7565 ${r} \u7684 ${t}[${o}] \u975E\u6CD5`);return i});if(new Set(n).size!==n.length)throw Error(`\u7B56\u7565 ${r} \u7684 ${t} \u5B58\u5728\u91CD\u590D\u9879`);return n}function ei(e){if(typeof e==="number")return"number";if(typeof e==="boolean")return"boolean";if(typeof e==="string")return"string";return"json"}function ti(e,t){return e[t]}function Sr(e){return e!==null&&typeof e==="object"&&!Array.isArray(e)}function vr(e,t){return Tr(e,t)}function Tr(e,t){return vt(e,t,new Set),JSON.parse(JSON.stringify(e))}function vt(e,t,r){if(e===null||typeof e==="string"||typeof e==="boolean")return;if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`${t} \u5305\u542B\u975E\u6709\u9650\u6570\u5B57`);return}if(typeof e!=="object")throw Error(`${t} \u5305\u542B\u4E0D\u53EF\u5E8F\u5217\u5316\u7684 ${typeof e}`);if(r.has(e))throw Error(`${t} \u5305\u542B\u5FAA\u73AF\u5F15\u7528`);if(r.add(e),Array.isArray(e))for(let n=0;n<e.length;n++)vt(e[n],`${t}[${n}]`,r);else{let n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw Error(`${t} \u5FC5\u987B\u662F\u666E\u901A\u5BF9\u8C61\u6216\u6570\u7EC4`);for(let[i,o]of Object.entries(e))vt(o,`${t}.${i}`,r)}r.delete(e)}class qe{timeframes;currentBars=new Map;completedBars=[];tickCallbacks=[];barCallbacks=[];lastTickTime=0;lastCumVolume=null;lastTradingDay="";constructor(e){this.timeframes=e.timeframes.map((t)=>({label:t,seconds:Ir(t)}))}onTick(e){this.tickCallbacks.push(e)}offTick(e){let t=this.tickCallbacks.indexOf(e);if(t>=0)this.tickCallbacks.splice(t,1)}onBar(e){this.barCallbacks.push(e)}offBar(e){let t=this.barCallbacks.indexOf(e);if(t>=0)this.barCallbacks.splice(t,1)}pushTick(e){let t=ni(e);if(this.lastTickTime>0&&t<this.lastTickTime)throw Error(`tick \u65F6\u95F4\u56DE\u9000: ${e.code} ${t} < ${this.lastTickTime}`);this.lastTickTime=t;let r=Boolean(e.tradingDay&&this.lastTradingDay&&e.tradingDay!==this.lastTradingDay),n=0;if(!r&&this.lastCumVolume!==null&&e.volume>=this.lastCumVolume)n=e.volume-this.lastCumVolume;if(this.lastCumVolume=e.volume,e.tradingDay)this.lastTradingDay=e.tradingDay;for(let i of this.tickCallbacks)i(e);for(let i of this.timeframes)this.updateBar(i,e,t,n)}completeThrough(e){let t=Math.floor(e/1000);for(let r of this.timeframes){let n=this.currentBars.get(r.label);if(n&&n.barTime+r.seconds<=t)this.finalizeBar(r.label)}}getCompletedBars(){return this.completedBars}getCurrentBar(e){return this.currentBars.get(e)??null}forceCompleteAll(e){for(let t of this.timeframes)this.finalizeBar(t.label,e)}forceComplete(e,t){if(this.timeframes.some((r)=>r.label===e))this.finalizeBar(e,t)}markDisconnected(){for(let e of this.currentBars.values())e.boundaryReason="disconnect";this.lastCumVolume=null}reset(){this.currentBars.clear(),this.completedBars=[],this.lastTickTime=0,this.lastCumVolume=null,this.lastTradingDay=""}clearCompletedBars(){this.completedBars=[]}_registrySize(){return this.currentBars.size}updateBar(e,t,r,n){let i=ri(r,e.seconds),o=t.lastPrice,s=this.currentBars.get(e.label);if(s&&s.barTime<i)this.finalizeBar(e.label),s=void 0;if(!s){this.currentBars.set(e.label,{code:t.code,timeframe:e.label,barTime:i,open:o,high:o,low:o,close:o,volume:n,tickCount:1,...t.tradingDay?{tradingDay:t.tradingDay}:{}});return}if(s.high=Math.max(s.high,o),s.low=Math.min(s.low,o),s.close=o,s.volume+=n,s.tickCount++,t.tradingDay)s.tradingDay=t.tradingDay}finalizeBar(e,t){let r=this.currentBars.get(e);if(!r)return;if(this.currentBars.delete(e),r.tickCount===0)return;let n=t??r.boundaryReason,i={code:r.code,timeframe:r.timeframe,barTime:r.barTime,open:r.open,high:r.high,low:r.low,close:r.close,volume:r.volume,...r.tradingDay?{tradingDay:r.tradingDay}:{},...n?{boundaryReason:n}:{}};if(this.completedBars.push(i),this.completedBars.length>1000)this.completedBars.shift();for(let o of this.barCallbacks)o(i)}}function Ir(e){let t=/^(\d+)(min|h|d)$/.exec(e);if(!t)throw Error(`\u4E0D\u652F\u6301\u7684 K \u7EBF\u5468\u671F: ${e}`);let r=Number(t[1]);if(!Number.isInteger(r)||r<=0)throw Error(`\u4E0D\u652F\u6301\u7684 K \u7EBF\u5468\u671F: ${e}`);if(t[2]==="min")return r*60;if(t[2]==="h")return r*3600;return r*86400}function ri(e,t){return Math.floor(e/t)*t}function ni(e){if(Number.isFinite(e.datetime))return Math.floor(e.datetime/1000);return Math.floor(Or(e.actionDay,e.updateTime,e.updateMillisec??0)/1000)}var Dr=28800000,kt=86400000;function ii(e){let r=((e+Dr)%kt+kt)%kt;return e-r}function oi(e){if(!e||!/^\d{8}$/.test(e))return null;let t=Number(e.slice(0,4)),r=Number(e.slice(4,6)),n=Number(e.slice(6,8)),i=new Date(Date.UTC(t,r-1,n));return i.getUTCFullYear()===t&&i.getUTCMonth()===r-1&&i.getUTCDate()===n?[t,r,n]:null}var Er=new Set,si=43200000;function Or(e,t,r){if(!t)return Date.now();let n=/^(\d{2}):(\d{2}):(\d{2})$/.exec(t);if(!n){let d=new Date(t).getTime();return Number.isNaN(d)?Date.now():d}let i=Number(n[1]),o=Number(n[2]),s=Number(n[3]);if(i>23||o>59||s>59||!Number.isInteger(r)||r<0||r>999)return Date.now();let a=oi(e);if(a){let d=Date.UTC(a[0],a[1]-1,a[2],i,o,s,r)-Dr,c=Math.abs(d-Date.now());if(c>si&&!Er.has(e))Er.add(e),console.warn(`[bar-aggregator] actionDay \u4E0E\u7CFB\u7EDF\u65E5\u671F\u504F\u5DEE ${Math.round(c/3600000)}h: ${e}`);return d}return ii(Date.now())+(i*3600+o*60+s)*1000+r}var Ve=["1min","3min","5min","10min","15min","30min","1h","2h","4h","1d"],se={"1min":60,"3min":180,"5min":300,"10min":600,"15min":900,"30min":1800,"1h":3600,"2h":7200,"4h":14400,"1d":86400};var ve=Ve.join("\u3001");var ai=/^(\d{4})(\d{2})(\d{2})$/;function Z(e){return typeof e==="string"&&Ve.includes(e)}function ze(e,t,r){if(t!=="1d"){let n=se[t];return Math.floor(e/n)*n}return di(r??Tt(e))}function Tt(e){let t=new Date((e+28800)*1000);if(t.getUTCHours()>=20||t.getUTCDay()===0||t.getUTCDay()===6){t.setUTCDate(t.getUTCDate()+(t.getUTCHours()>=20?1:0));while(t.getUTCDay()===0||t.getUTCDay()===6)t.setUTCDate(t.getUTCDate()+1)}return`${t.getUTCFullYear()}${Rr(t.getUTCMonth()+1)}${Rr(t.getUTCDate())}`}function di(e){let t=ai.exec(e);if(!t)throw Error(`\u975E\u6CD5\u4EA4\u6613\u65E5: ${e}`);let r=Number(t[1]),n=Number(t[2]),i=Number(t[3]),o=Date.UTC(r,n-1,i),s=new Date(o);if(s.getUTCFullYear()!==r||s.getUTCMonth()!==n-1||s.getUTCDate()!==i)throw Error(`\u975E\u6CD5\u4EA4\u6613\u65E5: ${e}`);return Math.floor(o/1000)-28800}function xr(e,t){let r=e==="1d"?600:se[e]/60;return t*r+(r-1)}function Rr(e){return String(e).padStart(2,"0")}var ci=new Set(Ve);class Ue{timeframe;seconds;current=null;constructor(e){this.timeframe=e;if(!ci.has(e))throw Error(`\u5468\u671F "${e}" \u4E0D\u53D7\u652F\u6301\uFF1B\u4EC5\u652F\u6301 ${ve}`);this.seconds=se[e]}pushBar(e){let t=this.pushBars(e);if(t.length>1)throw Error("\u5355\u6839 1min bar \u540C\u65F6\u95ED\u5408\u4E86\u591A\u4E2A\u7A97\u53E3\uFF1B\u8BF7\u4F7F\u7528 pushBars() \u4FDD\u7559\u5168\u90E8\u4FE1\u53F7");return t[0]??null}pushBars(e){if(e.timeframe!=="1min")throw Error(`BarGenerator \u53EA\u63A5\u53D7\u95ED\u5408 1min bar\uFF0C\u6536\u5230 ${e.timeframe}`);if(this.seconds===60)return[e];let t=[],r=ze(e.barTime,this.timeframe,e.tradingDay);if(this.current&&this.current.barTime!==r){let o=this.finish();if(o)t.push(o)}if(!this.current)this.start(e,r);else this.merge(e);let n=this.timeframe!=="1d"&&e.barTime+60>=r+this.seconds,i=this.seconds>=3600?e.boundaryReason==="sessionEnd":e.boundaryReason!==void 0;if(n||i){let o=this.finish(e.boundaryReason);if(o)t.push(o)}return t}flush(e){return this.finish(e)}start(e,t){this.current={code:e.code,timeframe:this.timeframe,barTime:t,open:e.open,high:e.high,low:e.low,close:e.close,volume:e.volume,...e.tradingDay?{tradingDay:e.tradingDay}:{}}}merge(e){let t=this.current;if(t.high=Math.max(t.high,e.high),t.low=Math.min(t.low,e.low),t.close=e.close,t.volume+=e.volume,e.tradingDay)t.tradingDay=e.tradingDay}finish(e){let t=this.current;if(!t)return null;return this.current=null,{...t,...e?{boundaryReason:e}:{}}}}function _r(e){if(e.timeframe!=="1min")throw Error(`\u7B56\u7565\u9884\u70ED\u53EA\u63A5\u53D7\u95ED\u5408 1min Bar\uFF0C\u6536\u5230 ${e.timeframe}`)}function*Cr(e,t){if(t==="1min"){for(let o=0;o<e.length;o++)_r(e[o]),yield{startIndex:o,endIndex:o,triggerIndex:o};return}let r=new Ue(t),n=0,i=-1/0;for(let o=0;o<e.length;o++){let s=e[o];if(_r(s),s.barTime<i)throw Error("\u7B56\u7565\u9884\u70ED Bar \u5FC5\u987B\u6309\u65F6\u95F4\u5347\u5E8F\u6392\u5217");i=s.barTime;let a=ze(s.barTime,t,s.tradingDay);for(let d of r.pushBars(s)){let c=d.barTime===a?o:o-1;if(c<n)throw Error("\u7B56\u7565\u9884\u70ED Bar \u805A\u5408\u8FB9\u754C\u65E0\u6548");yield{startIndex:n,endIndex:c,triggerIndex:o},n=c+1}}}function Et(e,t){let r=0;for(let n of Cr(e,t))r++;return r}function ke(e,t,r){if(!Number.isInteger(r)||r<=0)throw Error("\u9884\u70ED Bar count \u5FC5\u987B\u4E3A\u6B63\u6574\u6570");let n=[...Cr(e,t)],i=n.length;if(i<r)return{bars:[...e],availableCount:i,selectedCount:i};let o=n[i-r];return{bars:e.slice(o.startIndex),availableCount:i,selectedCount:r}}var ui=new Set(["CFFEX","SHFE","CZCE","DCE","INE","GFEX"]);function K(e){let t=e.trim(),[r="",n,...i]=t.split(".");if(!r||!n||i.length>0)throw Error(`vtSymbol \u975E\u6CD5: ${e}`);if(!/^[A-Za-z][A-Za-z0-9]*$/.test(r)||!/\d/.test(r))throw Error(`vtSymbol \u5408\u7EA6\u4EE3\u7801\u975E\u6CD5: ${e}`);if(!ui.has(n.toUpperCase()))throw Error(`vtSymbol \u4EA4\u6613\u6240\u975E\u6CD5: ${e}`);let o=r.toUpperCase();return{instrumentID:o,vtSymbol:`${o}.${n.toUpperCase()}`}}function Pr(e){let t=e.split(".")[1]?.toUpperCase();return t==="SHFE"||t==="INE"}var li="20260105",mi=3500;function Y(e){if(!ae(e))throw Error("\u6D4B\u8BD5\u5B9A\u4E49 fixture \u5FC5\u987B\u662F\u5BF9\u8C61");if(Te(e,["vtSymbol","setting","instrument","commission","seedPrice","tradingDay"],"fixture"),typeof e.vtSymbol!=="string")throw Error("fixture.vtSymbol \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");let t=K(e.vtSymbol);if(!ae(e.instrument))throw Error("fixture.instrument \u5FC5\u987B\u662F\u5BF9\u8C61");Te(e.instrument,["priceTick","size","name","minOrderVolume","maxOrderVolume","longMarginRatio","shortMarginRatio","isTrading"],"fixture.instrument");let r=E(e.instrument.priceTick,"fixture.instrument.priceTick"),n=x(e.instrument.size,"fixture.instrument.size"),i=e.instrument.name===void 0?t.instrumentID:e.instrument.name;if(typeof i!=="string")throw Error("fixture.instrument.name \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");let o=x(e.instrument.minOrderVolume===void 0?1:e.instrument.minOrderVolume,"fixture.instrument.minOrderVolume"),s=x(e.instrument.maxOrderVolume===void 0?1e5:e.instrument.maxOrderVolume,"fixture.instrument.maxOrderVolume");if(s<o)throw Error("fixture.instrument.maxOrderVolume \u5FC5\u987B\u4E0D\u5C0F\u4E8E minOrderVolume");let a=Ee(e.instrument.longMarginRatio===void 0?0.1:e.instrument.longMarginRatio,"fixture.instrument.longMarginRatio"),d=Ee(e.instrument.shortMarginRatio===void 0?0.1:e.instrument.shortMarginRatio,"fixture.instrument.shortMarginRatio"),c=e.instrument.isTrading===void 0?!0:e.instrument.isTrading;if(typeof c!=="boolean")throw Error("fixture.instrument.isTrading \u5FC5\u987B\u662F\u5E03\u5C14\u503C");let u=e.commission===void 0?void 0:pi(e.commission),l=e.setting===void 0?{}:ce(e.setting,"fixture.setting"),f=e.seedPrice===void 0?mi:E(e.seedPrice,"fixture.seedPrice"),h=e.tradingDay===void 0?li:X(e.tradingDay),m=/^([A-Z]+)\d+([A-Z]+)$/.exec(t.instrumentID),v=m?`${m[1]}_${m[2]}`:t.instrumentID.match(/^[A-Z]+/)?.[0]??"";if(!v)throw Error(`\u65E0\u6CD5\u4ECE ${t.instrumentID} \u63A8\u5BFC fixture \u4EA7\u54C1\u4EE3\u7801`);return{vtSymbol:t.vtSymbol,instrumentID:t.instrumentID,exchangeID:t.vtSymbol.split(".")[1],productID:v,setting:l,priceTick:r,size:n,name:i,minOrderVolume:o,maxOrderVolume:s,longMarginRatio:a,shortMarginRatio:d,isTrading:c,commission:u,seedPrice:f,tradingDay:h}}function Ge(e){let t=e.commission;if(!t)return null;return{instrumentID:e.instrumentID,exchangeID:e.exchangeID,investorRange:"1",brokerID:"test",investorID:"test",bizType:"1",investUnitID:"",openRatioByMoney:t.open.ratioByMoney,openRatioByVolume:t.open.perLot,closeRatioByMoney:t.close.ratioByMoney,closeRatioByVolume:t.close.perLot,closeTodayRatioByMoney:t.closeToday.ratioByMoney,closeTodayRatioByVolume:t.closeToday.perLot}}function pi(e){if(!ae(e))throw Error("fixture.commission \u5FC5\u987B\u662F\u5BF9\u8C61");return Te(e,["open","close","closeToday"],"fixture.commission"),{open:It(e.open,"fixture.commission.open"),close:It(e.close,"fixture.commission.close"),closeToday:It(e.closeToday,"fixture.commission.closeToday")}}function It(e,t){if(!ae(e))throw Error(`${t} \u5FC5\u987B\u662F\u5BF9\u8C61`);return Te(e,["ratioByMoney","perLot"],t),{ratioByMoney:Ee(e.ratioByMoney,`${t}.ratioByMoney`),perLot:Ee(e.perLot,`${t}.perLot`)}}function Ie(e,t,r){let n=-1;for(let i of e){if(i.code!==r)throw Error("\u6D4B\u8BD5 Bar \u5408\u7EA6\u4E0E fixture \u4E0D\u5339\u914D");if(i.barTime<=n)throw Error("\u6D4B\u8BD5 Bar \u5FC5\u987B\u6309\u65F6\u95F4\u4E25\u683C\u9012\u589E");if((i.barTime+(i.boundaryReason?0:60))*1000>t)throw Error("\u6D4B\u8BD5 Bar \u5C1A\u672A\u95ED\u5408\uFF0C\u4E0D\u80FD\u4F7F\u7528\u672A\u6765\u6570\u636E");n=i.barTime}}function Ke(e){let t=e.tradingDay;return Date.parse(`${t.slice(0,4)}-${t.slice(4,6)}-${t.slice(6,8)}T09:00:00+08:00`)}function Ot(e){let t=new Date(e+28800000);return`${t.getUTCFullYear()}${String(t.getUTCMonth()+1).padStart(2,"0")}${String(t.getUTCDate()).padStart(2,"0")}`}function H(e,t,r,n=Ke(e)/1000+r*60){if(t.timeframe!==void 0&&t.timeframe!=="1min")throw Error("bar.timeframe \u5FC5\u987B\u662F 1min");let i=E(t.close,"bar.close"),o=t.open===void 0?i:E(t.open,"bar.open"),s=t.high===void 0?Math.max(o,i):E(t.high,"bar.high"),a=t.low===void 0?Math.min(o,i):E(t.low,"bar.low");if(s<Math.max(o,i)||a>Math.min(o,i))throw Error("Bar OHLC \u5173\u7CFB\u975E\u6CD5");let d=t.volume===void 0?100+r:Ee(t.volume,"bar.volume"),c=t.barTime===void 0?n:T(t.barTime,"bar.barTime");return{code:$r(t.code??e.instrumentID),timeframe:"1min",barTime:c,open:o,high:s,low:a,close:i,volume:d,tradingDay:t.tradingDay===void 0?e.tradingDay:X(t.tradingDay),...t.boundaryReason?{boundaryReason:t.boundaryReason}:{}}}function De(e,t,r,n=Ke(e)+r*60000){let i=E(t.lastPrice,"tick.lastPrice"),o=t.datetime===void 0?n:T(t.datetime,"tick.datetime"),s=new Date(o+28800000),a=t.updateTime??[s.getUTCHours(),s.getUTCMinutes(),s.getUTCSeconds()].map((c)=>String(c).padStart(2,"0")).join(":");if(!/^(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d$/.test(a))throw Error("tick.updateTime \u5FC5\u987B\u662F HH:MM:SS");let d=t.updateMillisec===void 0?o%1000:T(t.updateMillisec,"tick.updateMillisec");if(d>999)throw Error("tick.updateMillisec \u5FC5\u987B\u5728 0-999 \u4E4B\u95F4");return{code:$r(t.code??e.instrumentID),exchangeID:t.exchangeID??e.exchangeID,productID:t.productID??e.productID,priceTick:t.priceTick===void 0?e.priceTick:E(t.priceTick,"tick.priceTick"),lastPrice:i,updateTime:a,updateMillisec:d,actionDay:t.actionDay===void 0?Ot(o):X(t.actionDay),tradingDay:t.tradingDay===void 0?e.tradingDay:X(t.tradingDay),volume:t.volume===void 0?100+r:T(t.volume,"tick.volume"),datetime:o,bidPrice1:je(t,"bidPrice1",i,E),bidVolume1:je(t,"bidVolume1",1000,T),askPrice1:je(t,"askPrice1",i,E),askVolume1:je(t,"askVolume1",1000,T),...t.upperLimitPrice===void 0?{}:{upperLimitPrice:E(t.upperLimitPrice,"tick.upperLimitPrice")},...t.lowerLimitPrice===void 0?{}:{lowerLimitPrice:E(t.lowerLimitPrice,"tick.lowerLimitPrice")},...t.rawActionDay===void 0?{}:{rawActionDay:t.rawActionDay},...t.receivedAt===void 0?{}:{receivedAt:T(t.receivedAt,"tick.receivedAt")},...t.dateSource===void 0?{}:{dateSource:t.dateSource},...t.tradingDaySource===void 0?{}:{tradingDaySource:t.tradingDaySource}}}function $r(e){if(typeof e!=="string"||!/^[A-Za-z][A-Za-z0-9]*$/.test(e))throw Error("\u884C\u60C5 code \u975E\u6CD5");return e}function je(e,t,r,n){if(!Object.hasOwn(e,t))return r;return e[t]===void 0?void 0:n(e[t],`tick.${t}`)}function Ye(e){return{instrumentID:e.instrumentID,exchangeID:e.exchangeID,productID:e.productID,productClass:"1",exchangeInstID:e.instrumentID,instrumentName:e.name,volumeMultiple:e.size,priceTick:e.priceTick,maxLimitOrderVolume:e.maxOrderVolume,minLimitOrderVolume:e.minOrderVolume,maxMarketOrderVolume:0,minMarketOrderVolume:0,longMarginRatio:e.longMarginRatio,shortMarginRatio:e.shortMarginRatio,maxMarginSideAlgorithm:"",strikePrice:0,optionsType:"",combinationType:"",deliveryYear:0,deliveryMonth:0,createDate:"",openDate:"",expireDate:"",startDelivDate:"",endDelivDate:"",instLifePhase:"1",isTrading:e.isTrading?1:0,positionType:"",positionDateType:"",underlyingInstrID:"",underlyingMultiple:1}}function We(e){if(e===void 0)return de();if(!ae(e))throw Error("initialState.inventory \u5FC5\u987B\u662F\u5BF9\u8C61");return Te(e,["longToday","longYesterday","shortToday","shortYesterday"],"initialState.inventory"),{longToday:e.longToday===void 0?0:T(e.longToday,"longToday"),longYesterday:e.longYesterday===void 0?0:T(e.longYesterday,"longYesterday"),shortToday:e.shortToday===void 0?0:T(e.shortToday,"shortToday"),shortYesterday:e.shortYesterday===void 0?0:T(e.shortYesterday,"shortYesterday")}}function de(){return{longToday:0,longYesterday:0,shortToday:0,shortYesterday:0}}function Mr(e){return e.longToday+e.longYesterday-e.shortToday-e.shortYesterday}function gi(e,t){let r=new Date(Date.UTC(Number(e.slice(0,4)),Number(e.slice(4,6))-1,Number(e.slice(6,8))+t));return`${r.getUTCFullYear()}${String(r.getUTCMonth()+1).padStart(2,"0")}${String(r.getUTCDate()).padStart(2,"0")}`}function X(e){if(typeof e!=="string"||!/^\d{8}$/.test(e))throw Error("fixture.tradingDay \u5FC5\u987B\u662F YYYYMMDD");if(gi(e,0)!==e)throw Error(`fixture.tradingDay \u975E\u6CD5: ${e}`);return e}function ce(e,t){if(!ae(e))throw Error(`${t} \u5FC5\u987B\u662F\u5BF9\u8C61`);try{Dt(e,t,new Set);let r=JSON.stringify(e);if(r===void 0)throw Error("\u4E0D\u53EF\u5E8F\u5217\u5316");return JSON.parse(r)}catch(r){throw Error(`${t} \u5FC5\u987B\u53EF JSON \u5E8F\u5217\u5316: ${r instanceof Error?r.message:String(r)}`)}}function Dt(e,t,r){if(e===null||typeof e==="string"||typeof e==="boolean")return;if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`${t} \u5305\u542B\u975E\u6709\u9650\u6570\u5B57`);return}if(typeof e!=="object")throw Error(`${t} \u5305\u542B\u4E0D\u53EF\u5E8F\u5217\u5316\u7684 ${typeof e}`);if(r.has(e))throw Error(`${t} \u5305\u542B\u5FAA\u73AF\u5F15\u7528`);if(r.add(e),Array.isArray(e))for(let n=0;n<e.length;n++)Dt(e[n],`${t}[${n}]`,r);else{let n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw Error(`${t} \u5FC5\u987B\u662F\u666E\u901A\u5BF9\u8C61\u6216\u6570\u7EC4`);for(let[i,o]of Object.entries(e))Dt(o,`${t}.${i}`,r)}r.delete(e)}function Te(e,t,r){let n=Object.keys(e).find((i)=>!t.includes(i));if(n)throw Error(`${r} \u4E0D\u652F\u6301\u5B57\u6BB5 "${n}"`)}function E(e,t){if(typeof e!=="number"||!Number.isFinite(e)||e<=0)throw Error(`${t} \u5FC5\u987B\u662F\u6B63\u6570`);return e}function x(e,t){if(!Number.isSafeInteger(e)||e<=0)throw Error(`${t} \u5FC5\u987B\u662F\u6B63\u6574\u6570`);return e}function T(e,t){if(!Number.isSafeInteger(e)||e<0)throw Error(`${t} \u5FC5\u987B\u662F\u975E\u8D1F\u6574\u6570`);return e}function Ee(e,t){if(typeof e!=="number"||!Number.isFinite(e)||e<0)throw Error(`${t} \u5FC5\u987B\u662F\u975E\u8D1F\u6570`);return e}function ae(e){return e!==null&&typeof e==="object"&&!Array.isArray(e)}class Oe{fixture;sequence;now;observations;phase="running";instrument;orders=new Map;trades=new Map;reportedOrders=new Map;deliveredTrades=new Set;orderHandlers=new Set;tradeHandlers=new Set;errorHandlers=new Set;reconcileHandlers=new Set;faultHandlers=new Set;inventories=new Map;tradingDay;nextSubmitFailure;nextCancelFailure;holdingReports=!1;reportSequence=0;reports=new Map;pending=new Set;deliveredReports=new Set;constructor(e,t,r,n,i,o){this.fixture=e;this.sequence=r;this.now=n;this.observations=i;if(this.inventories.set(e.vtSymbol,{...t}),this.tradingDay=e.tradingDay,this.instrument=Ye(e),o)this.reconcile({...o,reason:"initial"})}snapshot(){return{phase:this.phase,tradingDay:this.tradingDay,inventory:{...this.inventories.get(this.fixture.vtSymbol)??de()},orders:[...this.orders.values()].map(L),trades:[...this.trades.values()].map((e)=>({...e}))}}holdReports(){this.holdingReports=!0}resumeReports(){this.requireConnected(),this.holdingReports=!1;for(let e of[...this.pending])this.deliverReport(e)}pendingReports(){return[...this.pending].map((e)=>structuredClone(this.reports.get(e)))}deliverReport(e){if(this.requireConnected(),!this.pending.delete(e))throw Error(`Pending broker report does not exist: ${e}`);this.deliver(this.reports.get(e))}repeatReport(e){this.requireConnected();let t=this.reports.get(e);if(!t||!this.deliveredReports.has(e))throw Error(`Delivered broker report does not exist: ${e}`);this.deliver(t,!0)}async prepareInstrument(e,t){if(e!==this.fixture.instrumentID||t!==this.fixture.exchangeID)throw Error(`\u6D4B\u8BD5\u5408\u7EA6\u4E0D\u5339\u914D: ${e}.${t}`);return this.instrument}async prepareCommission(e,t){if(e!==this.fixture.instrumentID||t!==this.fixture.exchangeID)throw Error(`\u6D4B\u8BD5\u624B\u7EED\u8D39\u5408\u7EA6\u4E0D\u5339\u914D: ${e}.${t}`)}getInstrument(e){return e===this.fixture.instrumentID?this.instrument:null}getCommission(e,t){if(e!==this.fixture.instrumentID||t!==this.fixture.exchangeID)return null;return Ge(this.fixture)}async queryPositions(e){return{phase:this.phase,positions:this.positionSnapshot().filter((t)=>!e||t.instrumentID===e.toUpperCase())}}async queryOrders(e){return[...this.orders.values()].filter((t)=>!e||t.instrumentID===e.toUpperCase()).map((t)=>L(t))}submitOrder(e){if(this.requireConnected(),this.nextSubmitFailure){let o=this.nextSubmitFailure;throw this.nextSubmitFailure=void 0,Error(o)}let t,r;do t=String(++this.sequence.order),r=`CTP.${this.tradingDay}.1.1.${t}`;while(this.orders.has(r));let n=e.direction==="buy"?"long":"short",i={vtOrderId:r,identity:{tradingDay:this.tradingDay,frontID:1,sessionID:1,orderRef:t,instrumentID:this.fixture.instrumentID,exchangeID:this.fixture.exchangeID},vtSymbol:this.fixture.vtSymbol,instrumentID:this.fixture.instrumentID,exchangeID:this.fixture.exchangeID,orderSysID:`TEST-${t}`,side:e.direction,direction:n,offset:e.offsetFlag,price:e.price,volume:e.volume,traded:0,status:"submitting",statusMessage:"",datetime:this.now()};return this.orders.set(r,i),this.reportedOrders.set(r,L(i)),this.observations.submissions.push({vtOrderId:r,direction:n,offset:e.offsetFlag,price:e.price,volume:e.volume}),this.observations.record("submission",this.observations.submissions.at(-1)),{vtOrderId:r,orderRef:t}}cancelOrder(e){if(this.observations.cancelRequests.push(e),this.observations.record("cancel",{vtOrderId:e}),this.requireConnected(),this.nextCancelFailure){let t=this.nextCancelFailure;throw this.nextCancelFailure=void 0,Error(t)}}onOrder(e){return this.orderHandlers.add(e),()=>this.orderHandlers.delete(e)}onTrade(e){return this.tradeHandlers.add(e),()=>this.tradeHandlers.delete(e)}onOrderError(e){return this.errorHandlers.add(e),()=>this.errorHandlers.delete(e)}onReconciled(e){return this.reconcileHandlers.add(e),()=>this.reconcileHandlers.delete(e)}onFault(e){return this.faultHandlers.add(e),()=>this.faultHandlers.delete(e)}failNextSubmit(e="test submit failed"){this.nextSubmitFailure=e}rejectSubmit(e,t="test submit rejected"){let r=this.requireActiveOrder(e);if(r.traded!==0||this.tradedVolume(e)>0)throw Error("\u5DF2\u6210\u4EA4\u59D4\u6258\u4E0D\u80FD\u62D2\u5355");if(r.status="rejected",r.statusMessage=t,this.phase!=="running")return;this.reportedOrders.set(e,L(r)),this.queueReport({type:"error",value:{vtOrderId:e,orderRef:r.identity.orderRef,instrumentID:r.instrumentID,operation:"submit",message:t}})}rejectCancel(e,t="test cancel rejected"){let r=this.requireOrder(e);this.requireConnected(),this.queueReport({type:"error",value:{vtOrderId:e,orderRef:r.identity.orderRef,instrumentID:r.instrumentID,operation:"cancel",message:t}})}emitExternalOrder(e){if(this.orders.has(e.vtOrderId))throw Error(`\u6D4B\u8BD5\u59D4\u6258\u5DF2\u5B58\u5728: ${e.vtOrderId}`);this.emitOrder(e)}emitOrder(e){let t=this.normalizeOrder(e);this.orders.set(t.vtOrderId,t),this.publishOrder(t)}emitTrade(e){let t=this.recordTrade(e);this.publishTrade(t)}disconnect(){if(this.phase!=="faulted")this.phase="recovering"}connect(){this.phase="running",this.reportedOrders.clear(),this.deliveredTrades.clear(),this.nextSubmitFailure=void 0,this.nextCancelFailure=void 0,this.pending.clear(),this.holdingReports=!1,this.reconcile({reason:"initial"})}reconcile(e={}){if(this.phase==="faulted")throw Error("\u6D4B\u8BD5 Broker \u5DF2 fault\uFF0C\u5FC5\u987B\u91CD\u542F Runtime");this.phase="recovering",this.pending.clear();try{let t=e.tradingDay===void 0?this.tradingDay:X(e.tradingDay);if(t<this.tradingDay)throw Error("\u6D4B\u8BD5 Broker TradingDay \u4E0D\u80FD\u56DE\u9000");if(t!==this.tradingDay)for(let o of this.inventories.values())o.longYesterday+=o.longToday,o.longToday=0,o.shortYesterday+=o.shortToday,o.shortToday=0;this.tradingDay=t;let r=e.orders===void 0?[...this.orders.values()].filter((o)=>o.identity.tradingDay===t).map(L):e.orders.map((o)=>this.normalizeOrder(o));if(e.orders!==void 0){for(let o of this.orders.values())if(o.identity.tradingDay===t)this.orders.delete(o.vtOrderId);for(let o of r)this.orders.set(o.vtOrderId,o)}for(let o of e.trades??[])this.recordTrade(o);if(e.inventory!==void 0)this.inventories.set(this.fixture.vtSymbol,We(e.inventory));let n=[...this.trades.values()].filter((o)=>o.tradingDay===t),i={reason:e.reason??"reconnect",tradingDay:t,accounts:[],positions:this.positionSnapshot(),orders:r,trades:n,recoveredAt:this.now()};if(i.reason==="reconnect"){for(let o of this.reportedOrders.values())if(w(o)&&o.identity.tradingDay===t&&!r.some((s)=>s.vtOrderId===o.vtOrderId))throw Error(`\u6D3B\u52A8\u59D4\u6258\u672A\u51FA\u73B0\u5728\u6062\u590D\u5FEB\u7167: ${o.vtOrderId}`);for(let o of r)this.publishOrder(o,!0);for(let o of n)this.publishTrade(o,!0)}else{for(let o of r)this.reportedOrders.set(o.vtOrderId,L(o));for(let o of n)this.deliveredTrades.add(o.vtTradeId)}for(let o of this.reconcileHandlers)o(i);this.phase="running"}catch(t){throw this.fault(t instanceof Error?t.message:String(t)),t}}fault(e="test gateway fault"){if(this.phase==="faulted")return;this.phase="faulted";for(let t of this.faultHandlers)t(e)}acknowledge(e){this.updateOrder(e,(t)=>{if(t.traded!==0||this.tradedVolume(e)>0)throw Error("\u5DF2\u6210\u4EA4\u59D4\u6258\u4E0D\u80FD\u91CD\u65B0\u786E\u8BA4");t.status="notTraded",t.statusMessage="test acknowledged"})}partialFill(e,t,r){this.fillVolume(e,t,r)}fill(e,t){let r=this.requireActiveOrder(e);this.fillVolume(e,r.volume-Math.max(r.traded,this.tradedVolume(e)),t)}reject(e,t="test rejected"){this.updateOrder(e,(r)=>{if(r.traded!==0||this.tradedVolume(e)>0)throw Error("\u5DF2\u6210\u4EA4\u59D4\u6258\u4E0D\u80FD\u62D2\u5355");r.status="rejected",r.statusMessage=t})}cancel(e,t="test cancelled"){this.updateOrder(e,(r)=>{r.traded=Math.max(r.traded,this.tradedVolume(e)),r.status="cancelled",r.statusMessage=t})}failNextCancel(e="test cancel failed"){this.nextCancelFailure=e}settleActiveOrders(){for(let e of[...this.orders.values()])if(e.status==="submitting"||e.status==="notTraded"||e.status==="partTraded")this.cancel(e.vtOrderId,"test cleanup")}fillVolume(e,t,r){if(!Number.isInteger(t)||t<=0)throw Error("\u6210\u4EA4\u6570\u91CF\u5FC5\u987B\u662F\u6B63\u6574\u6570");let n=this.requireActiveOrder(e),i=n.volume-Math.max(n.traded,this.tradedVolume(e));if(t>i)throw Error(`\u6210\u4EA4\u6570\u91CF ${t} \u8D85\u8FC7\u5269\u4F59\u6570\u91CF ${i}`);let o=E(r??n.price,"\u6210\u4EA4\u4EF7\u683C");n.traded=n.volume-i+t,n.status=n.traded===n.volume?"allTraded":"partTraded",n.statusMessage="test filled",this.publishOrder(n);let s;do s=`TESTTRADE.${++this.sequence.trade}`;while(this.trades.has(s));this.emitTrade({vtTradeId:s,vtOrderId:n.vtOrderId,price:o,volume:t,datetime:this.now(),tradingDay:this.tradingDay})}updateOrder(e,t){let r=this.requireActiveOrder(e);t(r),this.publishOrder(r)}normalizeOrder(e){let t=this.orders.get(e.vtOrderId),r=hi({...this.fixture,tradingDay:this.tradingDay},{...e,vtSymbol:e.vtSymbol??t?.vtSymbol},this.now());if(t){if(t.vtSymbol!==r.vtSymbol||t.direction!==r.direction||t.offset!==r.offset||t.volume!==r.volume)throw Error(`\u6D4B\u8BD5\u59D4\u6258\u8EAB\u4EFD\u5B57\u6BB5\u53D1\u751F\u53D8\u5316: ${r.vtOrderId}`);if(r.identity={...t.identity},r.orderSysID=t.orderSysID,r.traded<t.traded||Ar(r)<Ar(t))return L(t)}return r}tradedVolume(e){let t=0;for(let r of this.trades.values())if(r.vtOrderId===e)t+=r.volume;return t}recordTrade(e){if(typeof e.vtTradeId!=="string"||!e.vtTradeId.trim())throw Error("\u6D4B\u8BD5\u6210\u4EA4 ID \u4E0D\u80FD\u4E3A\u7A7A");let t=this.trades.get(e.vtTradeId);if(t)return t;let r=this.requireOrder(e.vtOrderId),n=x(e.volume,"\u6210\u4EA4\u6570\u91CF"),i=this.tradedVolume(r.vtOrderId)+n;if(i>r.volume||!w(r)&&i>r.traded)throw Error("\u6D4B\u8BD5\u6210\u4EA4\u6570\u91CF\u8D85\u8FC7\u59D4\u6258\u5B9E\u9645\u6210\u4EA4\u8303\u56F4");let o={vtTradeId:e.vtTradeId,vtOrderId:r.vtOrderId,vtSymbol:r.vtSymbol,instrumentID:r.instrumentID,exchangeID:r.exchangeID,orderSysID:r.orderSysID,tradeID:e.vtTradeId,side:r.side,direction:r.direction,offset:r.offset,price:E(e.price??r.price,"\u6210\u4EA4\u4EF7\u683C"),volume:n,datetime:e.datetime===void 0?this.now():T(e.datetime,"\u6210\u4EA4\u65F6\u95F4"),tradingDay:e.tradingDay===void 0?this.tradingDay:X(e.tradingDay)};if(o.tradingDay!==this.tradingDay)throw Error("\u6D4B\u8BD5\u6210\u4EA4 TradingDay \u4E0E Broker \u4E0D\u4E00\u81F4");this.trades.set(o.vtTradeId,o);let s=this.inventories.get(o.vtSymbol)??de();return this.inventories.set(o.vtSymbol,s),yi(s,o.direction,o.offset,o.volume),o}positionSnapshot(){return[...this.inventories].flatMap(([e,t])=>fi({...this.fixture,...K(e),exchangeID:e.split(".")[1],tradingDay:this.tradingDay},t))}publishOrder(e,t=!1){if(!t&&this.phase!=="running")return;this.queueReport({type:"order",value:L(e)},t)}publishTrade(e,t=!1){if(!t&&this.phase!=="running")return;this.queueReport({type:"trade",value:{...e}},t)}queueReport(e,t=!1){let r={...e,id:`report-${++this.reportSequence}`,createdAt:this.now()};if(this.reports.set(r.id,r),this.observations.record("broker",{operation:"queued",report:r}),!this.holdingReports||t)this.deliver(r);else this.pending.add(r.id)}deliver(e,t=!1){if(this.deliveredReports.add(e.id),this.observations.record("broker",{operation:"delivered",duplicate:t,report:e}),e.type==="trade"){let i=e.value;if(!t&&this.deliveredTrades.has(i.vtTradeId))return;this.deliveredTrades.add(i.vtTradeId);for(let o of this.tradeHandlers)o({...i});return}if(e.type==="error"){for(let i of this.errorHandlers)i({...e.value});return}let r=e.value,n=this.reportedOrders.get(r.vtOrderId);if(!t&&n&&n.status===r.status&&n.traded===r.traded&&n.statusMessage===r.statusMessage&&n.orderSysID===r.orderSysID)return;this.reportedOrders.set(r.vtOrderId,L(r));for(let i of this.orderHandlers)i(L(r))}requireConnected(){if(this.phase!=="running")throw Error(`\u6D4B\u8BD5 Broker \u672A\u5C31\u7EEA: ${this.phase}`)}requireOrder(e){let t=this.orders.get(e);if(!t)throw Error(`\u6D4B\u8BD5\u59D4\u6258\u4E0D\u5B58\u5728: ${e}`);return t}requireActiveOrder(e){let t=this.requireOrder(e);if(!w(t))throw Error(`\u6D4B\u8BD5\u59D4\u6258\u5DF2\u7EC8\u6B62: ${e} (${t.status})`);return t}}function Ar(e){if(e.status==="submitting")return 0;if(e.status==="notTraded")return 1;if(e.status==="partTraded")return 2;return 3}function hi(e,t,r){if(typeof t.vtOrderId!=="string"||!t.vtOrderId.trim())throw Error("\u6D4B\u8BD5\u59D4\u6258 ID \u4E0D\u80FD\u4E3A\u7A7A");let{vtSymbol:n,instrumentID:i}=K(t.vtSymbol??e.vtSymbol),o=n.split(".")[1];if(t.direction!=="long"&&t.direction!=="short")throw Error("\u6D4B\u8BD5\u59D4\u6258\u65B9\u5411\u975E\u6CD5");let s=t.offset??"open";if(!["open","close","closeToday","closeYesterday"].includes(s))throw Error("\u6D4B\u8BD5\u59D4\u6258\u5F00\u5E73\u975E\u6CD5");let a=x(t.volume,"\u6D4B\u8BD5\u59D4\u6258\u6570\u91CF"),d=t.traded===void 0?0:T(t.traded,"\u6D4B\u8BD5\u59D4\u6258\u5DF2\u6210\u4EA4\u6570\u91CF"),c=t.status??"notTraded";if(!["submitting","notTraded","partTraded","allTraded","cancelled","rejected"].includes(c)||d>a||(c==="submitting"||c==="notTraded"||c==="rejected")&&d!==0||c==="partTraded"&&(d===0||d===a)||c==="allTraded"&&d!==a)throw Error("\u6D4B\u8BD5\u59D4\u6258\u72B6\u6001\u4E0E\u6210\u4EA4\u6570\u91CF\u4E0D\u4E00\u81F4");return{vtOrderId:t.vtOrderId,identity:{tradingDay:e.tradingDay,frontID:1,sessionID:1,orderRef:t.vtOrderId.split(".").at(-1),instrumentID:i,exchangeID:o},vtSymbol:n,instrumentID:i,exchangeID:o,orderSysID:`TEST-${t.vtOrderId}`,side:t.direction==="long"?"buy":"sell",direction:t.direction,offset:s,price:E(t.price,"\u6D4B\u8BD5\u59D4\u6258\u4EF7\u683C"),volume:a,traded:d,status:c,statusMessage:t.statusMessage??"",datetime:t.datetime===void 0?r:T(t.datetime,"\u6D4B\u8BD5\u59D4\u6258\u65F6\u95F4")}}function fi(e,t){let r=[],n=(i,o,s)=>{if(o+s===0)return;r.push({instrumentID:e.instrumentID,exchangeID:e.exchangeID,vtSymbol:e.vtSymbol,direction:i,hedgeFlag:"1",investUnitID:"",tradingDay:e.tradingDay,volume:o+s,todayVolume:o,yesterdayVolume:s,frozen:0,price:e.seedPrice,pnl:0,margin:0,positionCost:0})};return n("long",t.longToday,t.longYesterday),n("short",t.shortToday,t.shortYesterday),r}function yi(e,t,r,n){if(r==="open"){if(t==="long")e.longToday+=n;else e.shortToday+=n;return}let i=t==="short"?"longToday":"shortToday",o=t==="short"?"longYesterday":"shortYesterday";if(r==="closeToday")e[i]=Math.max(0,e[i]-n);else if(r==="closeYesterday")e[o]=Math.max(0,e[o]-n);else{let s=Math.min(e[o],n);e[o]-=s,e[i]=Math.max(0,e[i]-(n-s))}}function L(e){return{...e,identity:{...e.identity}}}class Qe extends Error{method;code;constructor(e,t,r){super(r);this.method=e;this.code=t;this.name="GatewayRpcError"}}var Je=(e)=>e.toUpperCase();function Re(e){return e.map((t)=>({...t,instrumentID:Je(t.instrumentID)}))}class Rt{transport;loginResult=null;connected=!1;queryQueue=Promise.resolve();lastQueryAt=0;minQueryIntervalMs;queryTimeoutMs;normalizeInstrumentID;cleanups;pendingQueries=new Set;disposed=!1;clock;constructor(e,t={}){this.transport=e;this.minQueryIntervalMs=t.minQueryIntervalMs??0,this.queryTimeoutMs=t.queryTimeoutMs??15000,this.normalizeInstrumentID=t.normalizeInstrumentID??((r)=>r),this.clock=t.clock??{now:Date.now,schedule:(r,n)=>{let i=setTimeout(r,n);return()=>clearTimeout(i)}},this.cleanups=[e.on("onDisconnected",()=>{this.connected=!1}),e.on("onReconnecting",()=>{this.connected=!1}),e.on("onReconnected",(r)=>{this.connected=!0,this.loginResult=r.params}),e.onExit(()=>{this.connected=!1})]}async login(e){let t=await this.request("login",e,"\u4EA4\u6613\u767B\u5F55\u5931\u8D25");return this.loginResult=t,this.connected=!0,t}async logout(e,t){await this.request("logout",{broker:e,user:t},"\u767B\u51FA\u5931\u8D25"),this.loginResult=null,this.connected=!1}async changePassword(e){await this.request("changePassword",e,"\u4FEE\u6539\u5BC6\u7801\u5931\u8D25")}async insertOrder(e){let t=this.loginResult,r=await this.request("insertOrder",this.normalizeParams(e),"\u4E0B\u5355\u5931\u8D25"),n=r.tradingDay??t?.tradingDay,i=r.frontID??t?.frontID,o=r.sessionID??t?.sessionID;if(!n||!i||!o)throw Error("\u4E0B\u5355\u6210\u529F\u4F46\u4EA4\u6613\u4F1A\u8BDD\u8EAB\u4EFD\u4E0D\u53EF\u7528");return{tradingDay:n,frontID:i,sessionID:o,orderRef:r.orderRef,requestID:r.requestID}}cancelOrder(e){return this.request("cancelOrder",this.normalizeParams(e),"\u64A4\u5355\u5931\u8D25")}queryAccount(e,t){return this.query(()=>this.requestAndWait("queryAccount",{broker:e,investorId:t},"onQueryAccountResult",(r)=>r.accounts??[],"\u67E5\u8BE2\u8D26\u6237\u5931\u8D25",!0))}queryPositions(e,t,r){return this.query(()=>this.requestAndWait("queryPosition",{broker:e,investorId:t,instrumentID:r?this.normalizeInstrumentID(r):""},"onQueryPositionResult",(n)=>Re(n.positions??[]),"\u67E5\u8BE2\u6301\u4ED3\u5931\u8D25",!0))}queryOrders(e){return this.query(()=>this.requestAndWait("queryOrder",this.normalizeParams(e),"onQueryOrderResult",(t)=>Re(t.orders??[]),"\u67E5\u8BE2\u59D4\u6258\u5931\u8D25",!0))}queryTrades(e){return this.query(()=>this.requestAndWait("queryTrade",this.normalizeParams(e),"onQueryTradeResult",(t)=>Re(t.trades??[]),"\u67E5\u8BE2\u6210\u4EA4\u5931\u8D25",!0))}querySettlementInfo(e){return this.query(()=>this.requestAndWait("querySettlementInfo",e,"onQuerySettlementInfoResult",(t)=>t.settlementInfos??t.settlements??[],"\u67E5\u8BE2\u7ED3\u7B97\u5355\u5931\u8D25",!0))}queryInstrument(e={}){return this.query(()=>this.requestAndWait("queryInstrument",this.normalizeParams(e),"onQueryInstrumentResult",(t)=>Re(t.instruments??[]),"\u67E5\u8BE2\u5408\u7EA6\u89C4\u683C\u5931\u8D25",!0,e.instrumentID||e.exchangeID||e.exchangeInstID||e.productID?this.queryTimeoutMs:60000))}queryCommissionRates(e){return this.query(()=>this.requestAndWait("queryInstrumentCommissionRate",this.normalizeParams(e),"onQueryCommissionRateResult",(t)=>Re(t.commissionRates??[]),"\u67E5\u8BE2\u624B\u7EED\u8D39\u7387\u5931\u8D25",!0,e.instrumentID?this.queryTimeoutMs:60000))}async queryCommissionRate(e){return(await this.queryCommissionRates(e))[0]??null}onOrder(e){return this.transport.on("onOrder",(t)=>{let r=t.params;e({...r,instrumentID:Je(r.instrumentID)})})}onTrade(e){return this.transport.on("onTrade",(t)=>{let r=t.params;e({...r,instrumentID:Je(r.instrumentID)})})}onOrderError(e){return this.transport.on("onOrderError",(t)=>{let r=t.params;e({...r,instrumentID:r.instrumentID?Je(r.instrumentID):void 0})})}onConnection(e,t){return this.transport.on(e,t)}dispose(){this.disposed=!0;for(let e of[...this.pendingQueries])e();for(let e of this.cleanups.splice(0))e()}query(e){let t=this.queryQueue.then(async()=>{if(this.disposed)throw Error("Trader gateway API disposed");let r=Math.max(0,this.minQueryIntervalMs-(this.clock.now()-this.lastQueryAt));if(r>0)await new Promise((n,i)=>{let o=()=>{s(),this.pendingQueries.delete(o),i(Error("Trader gateway API disposed"))},s=this.clock.schedule(()=>{this.pendingQueries.delete(o),n()},r);this.pendingQueries.add(o)});if(this.disposed)throw Error("Trader gateway API disposed");try{return await e()}finally{this.lastQueryAt=this.clock.now()}});return this.queryQueue=t.then(()=>{return},()=>{return}),t}requestAndWait(e,t,r,n,i,o=!1,s=this.queryTimeoutMs){return new Promise((a,d)=>{let c=null,u=null,l=null,f=[],h=()=>{c?.(),c=null,u?.(),u=null,this.pendingQueries.delete(m)},m=()=>{h(),d(Error("Trader gateway API disposed"))};this.pendingQueries.add(m),c=this.clock.schedule(()=>{h(),d(Error(`${e} \u67E5\u8BE2\u8D85\u65F6`))},s);let v=(p)=>{h();let k=p.params;if(k.ok===!1)d(Error(k.error??`${e} \u67E5\u8BE2\u5931\u8D25`));else a(n(p.params))};u=this.transport.on(r,(p)=>{if(!o){v(p);return}if(l===null){f.push(p);return}if(p.params.requestID===l)v(p)}),this.transport.request(e,t).then((p)=>{if(!p.ok){h(),d(Error(p.error?.msg??i));return}if(o){let k=p.data?.requestID;if(!Number.isInteger(k)){h(),d(Error(`${e} \u672A\u8FD4\u56DE\u6709\u6548 requestID`));return}l=k;let V=f.find((M)=>M.params.requestID===l);if(V)v(V)}}).catch((p)=>{h(),d(p)})})}async request(e,t,r){let n=await this.transport.request(e,t);if(!n.ok)throw new Qe(e,n.error?.code,n.error?.msg??r);return n.data}normalizeParams(e){if(!e.instrumentID&&!e.productID)return e;return{...e,...e.instrumentID?{instrumentID:this.normalizeInstrumentID(e.instrumentID)}:{},...e.productID?{productID:this.normalizeInstrumentID(e.productID)}:{}}}}var wi=28800000,Si=Number.MAX_VALUE/2,Br=1e5;class Ct extends Error{}function b(e,t){let r=e?.trim()??"";if(!r)throw Error(`${t} \u4E3A\u7A7A`);return r}function vi(e){if(!/^\d{8}$/.test(e))return!1;let t=Number(e.slice(0,4)),r=Number(e.slice(4,6)),n=Number(e.slice(6,8)),i=new Date(Date.UTC(t,r-1,n));return i.getUTCFullYear()===t&&i.getUTCMonth()===r-1&&i.getUTCDate()===n}function _(e,t){let r=e?.trim()??"";if(!vi(r))throw Error(`${t} \u975E\u6CD5: ${r}`);return r}function N(e,t){if(!Number.isInteger(e)||e<0)throw Error(`${t} \u975E\u6CD5: ${e}`);return e}function Xe(e,t){if(!Number.isInteger(e)||e<-2147483648||e>2147483647)throw Error(`${t} \u975E\u6CD5: ${e}`);return e}function Ze(e,t){if(!Number.isFinite(e))throw Error(`${t} \u975E\u6CD5: ${e}`);return e}function Pt(e,t){if(!Number.isFinite(e)||e<=0||e>=Si)throw Error(`${t} \u975E\u6CD5: ${e}`);return e}function $t(e,t){if(!Number.isInteger(e)||e<=0)throw Error(`${t} \u975E\u6CD5: ${e}`);return e}function Nr(e,t,r){let n=_(e,`${r}\u65E5\u671F`),i=/^(\d{2}):(\d{2}):(\d{2})$/.exec(t);if(!i)throw Error(`${r}\u65F6\u95F4\u975E\u6CD5: ${t}`);let o=Number(i[1]),s=Number(i[2]),a=Number(i[3]);if(o>23||s>59||a>59)throw Error(`${r}\u65F6\u95F4\u975E\u6CD5: ${t}`);return Date.UTC(Number(n.slice(0,4)),Number(n.slice(4,6))-1,Number(n.slice(6,8)),o,s,a)-wi}function Fr(e){if(e==="0")return{side:"buy",direction:"long"};if(e==="1")return{side:"sell",direction:"short"};throw Error(`CTP Direction \u4E0D\u652F\u6301: ${e}`)}function qr(e){switch(e){case"0":return"open";case"1":return"close";case"3":return"closeToday";case"4":return"closeYesterday";default:throw Error(`CTP OffsetFlag \u4E0D\u652F\u6301: ${e}`)}}function ki(e){if(!["0","1","2","3","4","5","6"].includes(e.orderSubmitStatus))throw Error(`CTP OrderSubmitStatus \u4E0D\u652F\u6301: ${e.orderSubmitStatus}`);if(e.orderSubmitStatus==="4")return"rejected";switch(e.orderStatus){case"0":return"allTraded";case"1":return"partTraded";case"2":case"4":case"5":return"cancelled";case"3":case"b":case"c":return"notTraded";case"a":return"submitting";default:throw Error(`CTP OrderStatus \u4E0D\u652F\u6301: ${e.orderStatus}`)}}function Hr(e,t){return`${e}|${t}`}function Lr(e,t){return`${e}|${t}`}function _t(e){let t=_(e.tradingDay,"\u59D4\u6258 TradingDay"),r=N(e.frontID,"\u59D4\u6258 FrontID"),n=Xe(e.sessionID,"\u59D4\u6258 SessionID"),i=b(e.orderRef,"\u59D4\u6258 OrderRef");return`CTP.${t}.${r}.${n}.${i}`}function xt(e){let t=b(e.instrumentID,"\u59D4\u6258 InstrumentID").toUpperCase(),r=b(e.exchangeID,"\u59D4\u6258 ExchangeID").toUpperCase(),n=_(e.tradingDay,"\u59D4\u6258 TradingDay"),i=b(e.orderRef,"\u59D4\u6258 OrderRef"),o={tradingDay:n,frontID:N(e.frontID,"\u59D4\u6258 FrontID"),sessionID:Xe(e.sessionID,"\u59D4\u6258 SessionID"),orderRef:i,instrumentID:t,exchangeID:r},s=Fr(e.direction),a=$t(e.volumeTotalOriginal,"\u59D4\u6258\u6570\u91CF"),d=N(e.volumeTraded,"\u59D4\u6258\u6210\u4EA4\u6570\u91CF");if(d>a)throw Error(`\u59D4\u6258\u6210\u4EA4\u6570\u91CF\u5927\u4E8E\u59D4\u6258\u6570\u91CF: ${d}/${a}`);return{vtOrderId:_t(o),identity:o,vtSymbol:`${t}.${r}`,instrumentID:t,exchangeID:r,orderSysID:e.orderSysID.trim(),...s,offset:qr(e.combOffsetFlag),price:Pt(e.limitPrice,"\u59D4\u6258\u4EF7\u683C"),volume:a,traded:d,status:ki(e),statusMessage:e.statusMsg,datetime:Nr(e.insertDate,e.insertTime,"\u59D4\u6258"),...e.traceId?{traceId:e.traceId}:{}}}function Ti(e,t,r){let n=b(e.instrumentID,"\u6210\u4EA4 InstrumentID").toUpperCase(),i=b(e.exchangeID,"\u6210\u4EA4 ExchangeID").toUpperCase(),o=_(e.tradingDay||r,"\u6210\u4EA4 TradingDay"),s=b(e.orderSysID,"\u6210\u4EA4 OrderSysID"),a=b(e.orderRef,"\u6210\u4EA4 OrderRef"),d=t(i,s,o,a);if(!d)throw new Ct(`\u6210\u4EA4\u65E0\u6CD5\u5173\u8054\u59D4\u6258: ${i}/${s}/${a}`);let c=b(e.tradeID,"\u6210\u4EA4 TradeID"),u=Fr(e.direction);return{vtTradeId:`CTP.${o}.${i}.${c}`,vtOrderId:d,vtSymbol:`${n}.${i}`,instrumentID:n,exchangeID:i,orderSysID:s,tradeID:c,...u,offset:qr(e.offsetFlag),price:Pt(e.price,"\u6210\u4EA4\u4EF7\u683C"),volume:$t(e.volume,"\u6210\u4EA4\u6570\u91CF"),datetime:Nr(e.tradeDate,e.tradeTime,"\u6210\u4EA4"),tradingDay:o,...e.traceId?{traceId:e.traceId}:{}}}function Ei(e,t=new Map){let r=new Map;for(let n of e){let i=b(n.instrumentID,"\u6301\u4ED3 InstrumentID").toUpperCase(),o=b(n.exchangeID,`\u6301\u4ED3 ${i} ExchangeID`).toUpperCase(),s=N(n.position,`\u6301\u4ED3 ${i} Position`),a=N(n.todayPosition,`\u6301\u4ED3 ${i} TodayPosition`);if(N(n.ydPosition,`\u6301\u4ED3 ${i} YdPosition`),a>s)throw Error(`\u6301\u4ED3 ${i} TodayPosition \u5927\u4E8E Position`);if(n.positionDate!=="1"&&n.positionDate!=="2")throw Error(`\u6301\u4ED3 ${i} PositionDate \u4E0D\u652F\u6301: ${n.positionDate}`);if(n.direction==="1"){if(s!==0)throw Error(`\u6682\u4E0D\u652F\u6301\u51C0\u6301\u4ED3\u8BB0\u5F55: ${i}`);continue}let d=n.direction==="2"?"long":n.direction==="3"?"short":(()=>{throw Error(`\u6301\u4ED3 ${i} PosiDirection \u4E0D\u652F\u6301: ${n.direction}`)})(),c=b(n.hedgeFlag,`\u6301\u4ED3 ${i} HedgeFlag`),u=n.investUnitID.trim(),l=_(n.tradingDay,`\u6301\u4ED3 ${i} TradingDay`),f=`${i}|${o}|${d}|${c}|${u}`,h=r.get(f);if(!h)h={instrumentID:i,exchangeID:o,direction:d,hedgeFlag:c,investUnitID:u,tradingDay:l,volume:0,yesterdayVolume:0,frozen:0,pnl:0,margin:0,positionCost:0},r.set(f,h);else if(h.tradingDay!==l)throw Error(`\u6301\u4ED3 ${i} \u6DF7\u5165\u591A\u4E2A TradingDay`);if(h.volume+=s,o==="SHFE"||o==="INE"){if(n.ydPosition>0&&a===0)h.yesterdayVolume+=s}else h.yesterdayVolume+=s-a;h.frozen+=d==="long"?N(n.shortFrozen,`\u6301\u4ED3 ${i} ShortFrozen`):N(n.longFrozen,`\u6301\u4ED3 ${i} LongFrozen`),h.pnl+=Ze(n.positionProfit,`\u6301\u4ED3 ${i} PositionProfit`),h.margin+=Ze(n.useMargin,`\u6301\u4ED3 ${i} UseMargin`),h.positionCost+=Ze(n.positionCost,`\u6301\u4ED3 ${i} PositionCost`)}return[...r.values()].map((n)=>{if(n.yesterdayVolume>n.volume)throw Error(`\u6301\u4ED3 ${n.instrumentID} \u6628\u4ED3\u5927\u4E8E\u603B\u4ED3`);let i=t.get(n.instrumentID)?.volumeMultiple??0,o=n.volume>0&&i>0?n.positionCost/(n.volume*i):0;return{instrumentID:n.instrumentID,exchangeID:n.exchangeID,vtSymbol:`${n.instrumentID}.${n.exchangeID}`,direction:n.direction,hedgeFlag:n.hedgeFlag,investUnitID:n.investUnitID,tradingDay:n.tradingDay,volume:n.volume,todayVolume:n.volume-n.yesterdayVolume,yesterdayVolume:n.yesterdayVolume,frozen:n.frozen,price:o,pnl:n.pnl,margin:n.margin,positionCost:n.positionCost}})}function U(e){return e.status==="submitting"||e.status==="notTraded"||e.status==="partTraded"}class Mt{api;options;contracts=new Map;orders=new Map;orderIdBySys=new Map;orderIdByRef=new Map;ambiguousOrderRefs=new Set;seenTrades=new Set;tradeLedgerDay="";pendingCancels=new Set;unresolvedTrades=[];pendingEvents=[];orderHandlers=new Set;tradeHandlers=new Set;errorHandlers=new Set;phaseHandlers=new Set;reconcileHandlers=new Set;faultHandlers=new Set;cleanups=[];session=null;currentSnapshot=null;recoveryTask=null;recoveryObservedOrderIds=null;_phase="idle";constructor(e,t){this.api=e;this.options=t;b(t.broker,"\u9002\u914D\u5668 BrokerID"),b(t.investorId,"\u9002\u914D\u5668 InvestorID"),this.cleanups.push(e.onOrder((r)=>this.receive({kind:"order",value:r})),e.onTrade((r)=>this.receive({kind:"trade",value:r})),e.onOrderError((r)=>this.receive({kind:"error",value:r})),e.onConnection("onDisconnected",()=>this.beginRecovery()),e.onConnection("onReconnecting",()=>this.beginRecovery()),e.onConnection("onReconnected",(r)=>{try{let n=r.params;this.setSession(n),this.reconcile(!0).catch((i)=>this.fail(i))}catch(n){this.fail(n)}}),e.transport.on("onReconnectFailed",(r)=>{this.fail(Error(String(r.params.error??"CTP \u91CD\u8FDE\u5931\u8D25")))}))}get phase(){return this._phase}get snapshot(){return this.currentSnapshot}async initialize(e){return this.setSession(e),this.reconcile(!1)}registerInstrument(e){this.contracts.set(e.instrumentID.toUpperCase(),e)}async queryPositions(e){this.ensureReady();let t=await this.api.queryPositions(this.options.broker,this.options.investorId,e);return this.normalizePositions(t)}async queryOrders(e){this.ensureReady();let r=(await this.api.queryOrders({broker:this.options.broker,investorId:this.options.investorId,instrumentID:e})).map((i)=>(this.validateOwner(i,"\u59D4\u6258"),this.acceptOrder(xt(i),!1))),n=new Map(this.cachedOrders(e).map((i)=>[i.vtOrderId,i]));for(let i of r)n.set(i.vtOrderId,i);return[...n.values()].sort((i,o)=>o.datetime-i.datetime)}cachedOrders(e){let t=e?.toUpperCase();return[...this.orders.values()].filter((r)=>!t||r.instrumentID===t).sort((r,n)=>n.datetime-r.datetime)}async queryAccounts(){this.ensureReady();let e=await this.api.queryAccount(this.options.broker,this.options.investorId);return this.validateAccounts(e,this.requireSession().tradingDay),e}async queryTrades(e){return this.ensureReady(),(await this.api.queryTrades({broker:this.options.broker,investorId:this.options.investorId,instrumentID:e})).map((r)=>(this.validateOwner(r,"\u6210\u4EA4"),this.normalizeTrade(r)))}submitOrder(e){if(this.ensureReady(),e.direction!=="buy"&&e.direction!=="sell")throw Error(`\u59D4\u6258\u65B9\u5411\u4E0D\u652F\u6301: ${String(e.direction)}`);if(!["open","close","closeToday","closeYesterday"].includes(e.offsetFlag))throw Error(`\u59D4\u6258\u5F00\u5E73\u4E0D\u652F\u6301: ${String(e.offsetFlag)}`);Pt(e.price,"\u59D4\u6258\u4EF7\u683C"),$t(e.volume,"\u59D4\u6258\u6570\u91CF");let t=this.requireSession(),r={tradingDay:_(t.tradingDay,"\u767B\u5F55 TradingDay"),frontID:N(t.frontID,"\u767B\u5F55 FrontID"),sessionID:Xe(t.sessionID,"\u767B\u5F55 SessionID"),orderRef:b(e.orderRef,"\u59D4\u6258 OrderRef"),instrumentID:b(e.instrumentID,"\u59D4\u6258 InstrumentID").toUpperCase(),exchangeID:b(e.exchangeID,"\u59D4\u6258 ExchangeID").toUpperCase()},n=_t(r);return this.bindOrderRef(r.tradingDay,r.orderRef,n),this.orders.set(n,{vtOrderId:n,identity:r,vtSymbol:`${r.instrumentID}.${r.exchangeID}`,instrumentID:r.instrumentID,exchangeID:r.exchangeID,orderSysID:"",side:e.direction,direction:e.direction==="buy"?"long":"short",offset:e.offsetFlag,price:e.price,volume:e.volume,traded:0,status:"submitting",statusMessage:"",datetime:(this.options.now??Date.now)(),...e.traceId?{traceId:e.traceId}:{}}),this.api.insertOrder(e).then((i)=>{if(i.tradingDay!==r.tradingDay||i.frontID!==r.frontID||i.sessionID!==r.sessionID||i.orderRef!==r.orderRef)this.fail(Error(`\u59D4\u6258 ${n} \u7684\u4F1A\u8BDD\u8EAB\u4EFD\u53D1\u751F\u53D8\u5316`))}).catch((i)=>{if(this._phase==="closed")return;let o=i instanceof Error?i.message:String(i);if(this.beginRecovery(),i instanceof Qe)this.emitError({vtOrderId:n,orderRef:r.orderRef,instrumentID:r.instrumentID,operation:"submit",message:o});else{let s=this.orders.get(n);if(s&&U(s)){let a={...s,statusMessage:`\u62A5\u5355\u7ED3\u679C\u5F85\u786E\u8BA4: ${o}`};this.orders.set(n,a),this.emitOrder(a)}}this.reconcileAfterRequestFailure()}),{vtOrderId:n,orderRef:r.orderRef}}cancelOrder(e){this.ensureReady();let t=this.orders.get(e);if(!t)throw Error(`\u627E\u4E0D\u5230\u5B9E\u76D8\u59D4\u6258: ${e}`);if(!U(t))throw Error(`\u59D4\u6258\u5DF2\u7EC8\u7ED3\uFF0C\u4E0D\u80FD\u64A4\u5355: ${e}`);this.pendingCancels.add(e),this.api.cancelOrder({orderRef:t.identity.orderRef,frontID:t.identity.frontID,sessionID:t.identity.sessionID,exchangeID:t.identity.exchangeID,instrumentID:t.identity.instrumentID}).catch((r)=>{if(this._phase==="closed")return;this.emitError({vtOrderId:e,orderRef:t.identity.orderRef,instrumentID:t.instrumentID,operation:"cancel",message:r instanceof Error?r.message:String(r)}),this.reconcileAfterRequestFailure()})}onOrder(e){return this.orderHandlers.add(e),()=>this.orderHandlers.delete(e)}onTrade(e){return this.tradeHandlers.add(e),()=>this.tradeHandlers.delete(e)}onOrderError(e){return this.errorHandlers.add(e),()=>this.errorHandlers.delete(e)}onPhase(e){return this.phaseHandlers.add(e),()=>this.phaseHandlers.delete(e)}onReconciled(e){return this.reconcileHandlers.add(e),()=>this.reconcileHandlers.delete(e)}onFault(e){return this.faultHandlers.add(e),()=>this.faultHandlers.delete(e)}close(){if(this._phase==="closed")return;this.setPhase("closed");for(let e of this.cleanups.splice(0))e();this.pendingEvents.length=0,this.unresolvedTrades.length=0,this.orderHandlers.clear(),this.tradeHandlers.clear(),this.errorHandlers.clear(),this.phaseHandlers.clear(),this.reconcileHandlers.clear(),this.faultHandlers.clear()}beginRecovery(){if(this._phase==="closed"||this._phase==="faulted")return;this.setPhase("recovering")}reconcileAfterRequestFailure(){if(this.beginRecovery(),this.api.connected)this.reconcile(!0).catch((e)=>this.fail(e))}async reconcile(e){if(this.recoveryTask)return this.recoveryTask;let t=this.performReconcile(e);this.recoveryTask=t;try{return await t}finally{if(this.recoveryTask===t)this.recoveryTask=null,this.recoveryObservedOrderIds=null}}async performReconcile(e){let t=this.requireSession();this.recoveryObservedOrderIds=new Set,this.setPhase("recovering");let r=await this.api.queryOrders({broker:this.options.broker,investorId:this.options.investorId}),n=new Map(this.orders);this.orderIdBySys.clear(),this.orderIdByRef.clear(),this.ambiguousOrderRefs.clear();let i=r.map((m)=>(this.validateOwner(m,"\u59D4\u6258"),this.acceptOrder(xt(m),!1))),s=(await this.api.queryTrades({broker:this.options.broker,investorId:this.options.investorId})).map((m)=>(this.validateOwner(m,"\u6210\u4EA4"),this.normalizeTrade(m))),a=[];for(let m of s){if(this.seenTrades.has(m.vtTradeId))continue;a.push(m),this.rememberTrade(m)}let d=await this.api.queryPositions(this.options.broker,this.options.investorId),c=this.normalizePositions(d),u=await this.api.queryAccount(this.options.broker,this.options.investorId);if(this.validateAccounts(u,t.tradingDay),this._phase==="closed"||this._phase==="faulted")throw Error(`CTP \u6062\u590D\u5DF2\u7EC8\u6B62\uFF08${this._phase}\uFF09`);let l={reason:e?"reconnect":"initial",tradingDay:_(t.tradingDay,"\u767B\u5F55 TradingDay"),accounts:u,positions:c,orders:i,trades:s,recoveredAt:(this.options.now??Date.now)()};if(this.currentSnapshot=l,e){for(let m of i){let v=n.get(m.vtOrderId);if(!v||this.orderFingerprint(v)!==this.orderFingerprint(m))this.emitOrder(m)}for(let m of a)this.emitTrade(m)}let f=this.pendingEvents.splice(0);for(let m of f)this.process(m);if(this.flushUnresolvedTrades(),this.unresolvedTrades.length>0)throw Error(`${this.unresolvedTrades.length} \u7B14\u6210\u4EA4\u65E0\u6CD5\u5173\u8054\u59D4\u6258`);let h=this.recoveryObservedOrderIds;for(let[m,v]of n)if(v.identity.tradingDay===l.tradingDay&&U(v)&&this.orders.has(m)&&!h.has(m))throw Error(`\u6D3B\u52A8\u59D4\u6258\u672A\u51FA\u73B0\u5728\u6062\u590D\u5FEB\u7167: ${m}`);l.orders=[...h].map((m)=>this.orders.get(m)).filter((m)=>m!==void 0&&m.identity.tradingDay===l.tradingDay);for(let m of this.reconcileHandlers)m(l);return this.setPhase("ready"),l}receive(e){if(this._phase==="closed"||this._phase==="faulted")return;if(this._phase!=="ready"){if(this.pendingEvents.length>=Br){this.fail(Error("CTP \u6062\u590D\u671F\u95F4\u4E8B\u4EF6\u7F13\u5B58\u6EA2\u51FA"));return}this.pendingEvents.push(e);return}try{this.process(e)}catch(t){this.fail(t)}}process(e){if(e.kind==="order")this.validateOwner(e.value,"\u59D4\u6258"),this.acceptOrder(xt(e.value),!0),this.flushUnresolvedTrades();else if(e.kind==="trade")this.validateOwner(e.value,"\u6210\u4EA4"),this.processTrade(e.value,!0);else this.processError(e.value)}acceptOrder(e,t){let r=this.orders.get(e.vtOrderId),n=e;if(r){if(r.instrumentID!==e.instrumentID||r.exchangeID!==e.exchangeID||r.side!==e.side||r.offset!==e.offset||r.volume!==e.volume)throw Error(`\u59D4\u6258\u8EAB\u4EFD\u5B57\u6BB5\u53D1\u751F\u53D8\u5316: ${e.vtOrderId}`);if(r.orderSysID&&e.orderSysID&&r.orderSysID!==e.orderSysID)throw Error(`\u59D4\u6258 OrderSysID \u53D1\u751F\u53D8\u5316: ${e.vtOrderId}`);if(e.traded<r.traded||this.orderStage(e)<this.orderStage(r))n=r;else n={...e,orderSysID:e.orderSysID||r.orderSysID,...e.traceId||!r.traceId?{}:{traceId:r.traceId}}}if(this.orders.set(n.vtOrderId,n),this.recoveryObservedOrderIds?.add(n.vtOrderId),this.bindOrderRef(n.identity.tradingDay,n.identity.orderRef,n.vtOrderId),n.orderSysID)this.orderIdBySys.set(Lr(n.exchangeID,n.orderSysID),n.vtOrderId);if(!U(n))this.pendingCancels.delete(n.vtOrderId);if(t&&(!r||this.orderFingerprint(r)!==this.orderFingerprint(n)))this.emitOrder(n);return n}normalizeTrade(e){return Ti(e,(t,r,n,i)=>this.orderIdBySys.get(Lr(t,r))??this.orderIdByRef.get(Hr(n,i))??null,this.session?.tradingDay)}bindOrderRef(e,t,r){let n=Hr(e,t);if(this.ambiguousOrderRefs.has(n))return;let i=this.orderIdByRef.get(n);if(i&&i!==r){this.orderIdByRef.delete(n),this.ambiguousOrderRefs.add(n);return}this.orderIdByRef.set(n,r)}normalizePositions(e){let t=_(this.requireSession().tradingDay,"\u767B\u5F55 TradingDay");for(let r of e)if(this.validateOwner(r,"\u6301\u4ED3"),_(r.tradingDay,`\u6301\u4ED3 ${r.instrumentID} TradingDay`)!==t)throw Error(`\u6301\u4ED3 TradingDay \u4E0E\u767B\u5F55\u4F1A\u8BDD\u4E0D\u4E00\u81F4: ${r.tradingDay}/${t}`);return Ei(e,this.contracts)}validateOwner(e,t){let r=b(e.brokerID,`${t} BrokerID`),n=b(e.investorID,`${t} InvestorID`);if(r!==this.options.broker||n!==this.options.investorId)throw Error(`${t}\u8D26\u6237\u8EAB\u4EFD\u4E0D\u5339\u914D: ${r}/${n}`)}validateAccounts(e,t){let r=_(t,"\u767B\u5F55 TradingDay");if(e.length===0)throw Error("\u8D26\u6237\u67E5\u8BE2\u8FD4\u56DE\u7A7A\u7ED3\u679C");for(let n of e){let i=b(n.brokerID,"\u8D26\u6237 BrokerID");if(b(n.accountID,"\u8D26\u6237 AccountID"),i!==this.options.broker)throw Error(`\u8D26\u6237 BrokerID \u4E0D\u5339\u914D: ${i}`);if(_(n.tradingDay,"\u8D26\u6237 TradingDay")!==r)throw Error(`\u8D26\u6237 TradingDay \u4E0E\u767B\u5F55\u4F1A\u8BDD\u4E0D\u4E00\u81F4: ${n.tradingDay}/${r}`);for(let[o,s]of Object.entries({preBalance:n.preBalance,balance:n.balance,available:n.available,currMargin:n.currMargin,frozenMargin:n.frozenMargin,frozenCash:n.frozenCash,frozenCommission:n.frozenCommission,closeProfit:n.closeProfit,positionProfit:n.positionProfit,commission:n.commission,deposit:n.deposit,withdraw:n.withdraw}))Ze(s,`\u8D26\u6237 ${o}`)}}processTrade(e,t){let r;try{r=this.normalizeTrade(e)}catch(n){if(n instanceof Ct){if(this.unresolvedTrades.length>=Br)throw Error("\u65E0\u6CD5\u5173\u8054\u7684\u6210\u4EA4\u7F13\u5B58\u6EA2\u51FA");this.unresolvedTrades.push(e);return}throw n}if(this.seenTrades.has(r.vtTradeId))return;if(this.rememberTrade(r),t)this.emitTrade(r)}flushUnresolvedTrades(){if(this.unresolvedTrades.length===0)return;let e=this.unresolvedTrades.splice(0);for(let t of e)this.processTrade(t,!0)}processError(e){let t=this.requireSession(),r=b(e.orderRef,"\u9519\u8BEF\u56DE\u62A5 OrderRef"),n=_t({...t,orderRef:r}),i=this.pendingCancels.has(n)?"cancel":"submit";this.emitError({vtOrderId:n,orderRef:r,...e.instrumentID?{instrumentID:e.instrumentID.toUpperCase()}:{},operation:i,message:e.errorMsg})}rememberTrade(e){if(!this.tradeLedgerDay||e.tradingDay>this.tradeLedgerDay){if(this.tradeLedgerDay)this.seenTrades.clear();this.tradeLedgerDay=e.tradingDay}this.seenTrades.add(e.vtTradeId)}orderFingerprint(e){return`${e.status}|${e.traded}|${e.orderSysID}|${e.statusMessage}`}orderStage(e){switch(e.status){case"submitting":return 0;case"notTraded":return 1;case"partTraded":return 2;case"allTraded":case"cancelled":case"rejected":return 3}}ensureReady(){if(this._phase!=="ready")throw Error(`CTP \u4EA4\u6613\u9002\u914D\u5668\u672A\u5C31\u7EEA\uFF08${this._phase}\uFF09`)}requireSession(){if(!this.session)throw Error("CTP \u4EA4\u6613\u4F1A\u8BDD\u4E0D\u53EF\u7528");return this.session}setSession(e){_(e.tradingDay,"\u767B\u5F55 TradingDay"),N(e.frontID,"\u767B\u5F55 FrontID"),Xe(e.sessionID,"\u767B\u5F55 SessionID"),this.session=e}setPhase(e){if(this._phase===e)return;if(this._phase==="closed")return;if(this._phase==="faulted"&&e!=="closed")return;this._phase=e;for(let t of this.phaseHandlers)t(e)}fail(e){if(this._phase==="closed"||this._phase==="faulted")return;let t=e instanceof Error?e.message:String(e);this.setPhase("faulted");for(let r of this.faultHandlers)r(t)}emitOrder(e){for(let t of this.orderHandlers)t(e)}emitTrade(e){for(let t of this.tradeHandlers)t(e)}emitError(e){this.pendingCancels.delete(e.vtOrderId);let t=this.orders.get(e.vtOrderId);if(t&&U(t)){let r=e.operation==="submit"?{...t,status:"rejected",statusMessage:e.message}:{...t,statusMessage:`\u64A4\u5355\u5931\u8D25: ${e.message}`};this.orders.set(e.vtOrderId,r),this.emitOrder(r)}for(let r of this.errorHandlers)r(e)}}var At={queryOrder:{event:"onQueryOrderResult",field:"orders"},queryTrade:{event:"onQueryTradeResult",field:"trades"},queryPosition:{event:"onQueryPositionResult",field:"positions"},queryAccount:{event:"onQueryAccountResult",field:"accounts"}};class Vr{session;clock;trace;requests=[];pending=new Map;handlers=new Map;exits=new Set;held=new Set;waiters=new Set;state;constructor(e,t,r,n={}){this.session=e;this.clock=t;this.trace=r;this.state=structuredClone(n)}request(e,t,r=30000){let n={id:this.requests.length+1,method:e,params:structuredClone(t),now:this.clock.now};return this.requests.push(n),this.trace.record("broker",{operation:"rpc",request:n}),new Promise((i,o)=>{let s=this.clock.schedule(()=>{this.pending.delete(n.id),o(Error(`Test gateway ${e} request timed out`))},r);this.pending.set(n.id,{request:n,resolve:i,reject:o,cancelTimeout:s});for(let a of this.waiters)if(a.method===e)this.waiters.delete(a),a.resolve(structuredClone(n));if(!this.held.has(e))this.complete(n.id)})}pendingRequests(){return[...this.pending.values()].map((e)=>structuredClone(e.request))}waitForRequest(e){let t=this.pendingRequests().find((r)=>r.method===e);return t?Promise.resolve(t):new Promise((r,n)=>this.waiters.add({method:e,resolve:r,reject:n}))}setSnapshot(e){this.state={...this.state,...structuredClone(e)}}hold(e){this.held.add(e)}resume(e){this.held.delete(e);for(let{request:t}of[...this.pending.values()])if(t.method===e)this.complete(t.id)}reply(e,t){let r=this.pending.get(e);if(!r)throw Error(`Pending RPC does not exist: ${e}`);this.pending.delete(e),r.cancelTimeout();let n=r.request.method==="insertOrder"?{...this.session,orderRef:r.request.params.orderRef,requestID:e}:{requestID:e},i={id:e,...t??{ok:!0,data:n}};this.trace.record("broker",{operation:"rpcReply",response:i}),r.resolve(i)}completeQuery(e,t){let r=this.requests.find((o)=>o.id===e);if(!r||!Object.hasOwn(At,r.method))throw Error(`Query RPC does not exist: ${e}`);let n=At[r.method];if(this.pending.has(e))this.reply(e);let i=n.field==="accounts"?[{brokerID:"test",accountID:"test",tradingDay:this.session.tradingDay,preBalance:1e6,balance:1e6,available:1e6,currMargin:0,frozenMargin:0,frozenCash:0,frozenCommission:0,closeProfit:0,positionProfit:0,commission:0,deposit:0,withdraw:0}]:[];this.push(n.event,{requestID:e,[n.field]:structuredClone(t??this.state[n.field]??i)})}push(e,t){let r={method:e,params:structuredClone(t)};this.trace.record("broker",{operation:"push",message:r});for(let n of this.handlers.get(e)??[])n(structuredClone(r))}on(e,t){let r=this.handlers.get(e)??new Set;return r.add(t),this.handlers.set(e,r),()=>this.off(e,t)}off(e,t){this.handlers.get(e)?.delete(t)}onExit(e){return this.exits.add(e),()=>this.exits.delete(e)}resetClient(){let e=Error("Test gateway client closed");for(let t of this.pending.values())t.cancelTimeout(),t.reject(e);for(let t of this.waiters)t.reject(e);this.pending.clear(),this.waiters.clear(),this.held.clear()}complete(e){let t=this.pending.get(e).request;if(Object.hasOwn(At,t.method))this.completeQuery(e);else this.reply(e)}}class xe{clock;trace;observations;gateway;fixture;transport;instrument;api;adapter;orderRef;orders=new Set;trades=new Set;errors=new Set;reconciled=new Set;faults=new Set;constructor(e,t,r,n,i){this.clock=t;this.trace=r;this.observations=i;this.fixture=e,this.instrument=Ye(e);let o={tradingDay:e.tradingDay,frontID:1,sessionID:1,...n.gatewaySession};this.orderRef=BigInt(o.maxOrderRef??"0"),this.transport=new Vr(o,t,r,n.gatewayState);let s=this;this.gateway={get requests(){return structuredClone(s.transport.requests)},pendingRequests:()=>this.transport.pendingRequests(),waitForRequest:(a)=>this.transport.waitForRequest(a),setSnapshot:(a)=>this.transport.setSnapshot(a),hold:(a)=>this.transport.hold(a),resume:(a)=>this.transport.resume(a),reply:(a,d)=>this.transport.reply(a,d),completeQuery:(a,d)=>this.transport.completeQuery(a,d),push:(a,d)=>this.transport.push(a,d),disconnect:()=>this.transport.push("onDisconnected",{}),reconnect:(a)=>this.reconnect(a),queryOrders:()=>this.requireAdapter().queryOrders()}}get phase(){if(this.adapter?.phase==="ready")return"running";return this.adapter?.phase==="faulted"||this.adapter?.phase==="closed"?"faulted":"recovering"}async connect(){if(this.adapter)this.close();this.api=new Rt(this.transport,{clock:{now:()=>this.clock.now,schedule:this.clock.schedule}}),this.api.loginResult=this.transport.session,this.api.connected=!0,this.adapter=new Mt(this.api,{broker:"test",investorId:"test",now:()=>this.clock.now}),this.adapter.registerInstrument(this.instrument),this.adapter.onOrder((e)=>{for(let t of this.orders)t(e)}),this.adapter.onTrade((e)=>{for(let t of this.trades)t(e)}),this.adapter.onOrderError((e)=>{for(let t of this.errors)t(e)}),this.adapter.onReconciled((e)=>{for(let t of this.reconciled)t(e)}),this.adapter.onFault((e)=>{for(let t of this.faults)t(e)}),await this.adapter.initialize(this.transport.session)}close(){this.adapter?.close(),this.api?.dispose(),this.transport.resetClient()}async prepareInstrument(e,t){if(e!==this.instrument.instrumentID||t!==this.instrument.exchangeID)throw Error("Test instrument mismatch");return this.instrument}async prepareCommission(e,t){if(e!==this.instrument.instrumentID||t!==this.instrument.exchangeID)throw Error("Test commission mismatch")}getInstrument(e){return e===this.instrument.instrumentID?this.instrument:null}getCommission(e,t){if(e!==this.instrument.instrumentID||t!==this.instrument.exchangeID)return null;return Ge(this.fixture)}async queryPositions(e){if(this.phase!=="running")return{phase:this.phase,positions:[]};let t=await this.requireAdapter().queryPositions(e);return{phase:this.phase,positions:t}}queryOrders(e){return this.requireAdapter().queryOrders(e)}submitOrder(e){let t=String(++this.orderRef);if(t.length>12)throw Error("CTP OrderRef \u5DF2\u8017\u5C3D\uFF0C\u8BF7\u91CD\u542F\u4EA4\u6613\u4F1A\u8BDD");let r=this.requireAdapter().submitOrder({instrumentID:e.instrumentID,direction:e.direction,offsetFlag:e.offsetFlag,price:e.price,volume:e.volume,exchangeID:this.instrument.exchangeID,orderRef:t,traceId:`vibek:${e.strategyId}:${t}`}),n={vtOrderId:r.vtOrderId,direction:e.direction==="buy"?"long":"short",offset:e.offsetFlag,price:e.price,volume:e.volume};return this.observations.submissions.push(n),this.trace.record("submission",n),r}cancelOrder(e){this.observations.cancelRequests.push(e),this.trace.record("cancel",{vtOrderId:e}),this.requireAdapter().cancelOrder(e)}onOrder(e){return this.orders.add(e),()=>this.orders.delete(e)}onTrade(e){return this.trades.add(e),()=>this.trades.delete(e)}onOrderError(e){return this.errors.add(e),()=>this.errors.delete(e)}onReconciled(e){return this.reconciled.add(e),()=>this.reconciled.delete(e)}onFault(e){return this.faults.add(e),()=>this.faults.delete(e)}reconnect(e={}){let t=this.requireAdapter();return Object.assign(this.transport.session,e),new Promise((r,n)=>{let i=t.onPhase((o)=>{if(o==="ready")i(),r();else if(o==="faulted"||o==="closed")i(),n(Error(`CTP reconciliation ${o}`))});this.transport.push("onDisconnected",{}),this.transport.push("onReconnected",{...this.transport.session})})}requireAdapter(){if(!this.adapter)throw Error("CTP test adapter is not initialized");return this.adapter}}class Bt{fixture;history;now;generate;constructor(e,t,r,n){this.fixture=e;this.history=t;this.now=r;this.generate=n}async getBars(e,t){if(this.history.length===0&&this.generate)this.history.push(...Ii(e,t,this.fixture,this.now));Ie(this.history,this.now,e);let r=ke(this.history,t.timeframe,t.count);if(r.selectedCount!==t.count)throw Error(`\u6D4B\u8BD5\u9884\u70ED\u6570\u636E\u4E0D\u8DB3\uFF1A\u9700\u8981 ${t.count} \u6839 ${t.timeframe}\uFF0C\u4EC5\u6709 ${r.availableCount} \u6839`);return r.bars.map((n)=>({...n}))}destroy(){}}function Ii(e,t,r,n){if(t.count>1e5)throw Error(`\u6D4B\u8BD5\u9884\u70ED\u6570\u636E\u8BF7\u6C42\u8FC7\u5927: ${t.count}`);if(t.timeframe==="1d"){let d=Math.floor(n/60000)*60;return Array.from({length:t.count},(c,u)=>{let l=d-(t.count-u)*86400;return H(r,{code:e,close:r.seedPrice+u*r.priceTick,barTime:l,tradingDay:Ot(l*1000),boundaryReason:"sessionEnd"},u)})}let i=se[t.timeframe]/60,o=t.count*i;if(o>1e5)throw Error(`\u6D4B\u8BD5\u9884\u70ED\u6570\u636E\u8BF7\u6C42\u8FC7\u5927: ${o} \u6839 1min Bar`);let a=Math.floor(n/(i*60000))*i*60-o*60;return Array.from({length:o},(d,c)=>H(r,{code:e,close:r.seedPrice+c*r.priceTick,barTime:a+c*60,tradingDay:Tt(a+c*60)},c))}import{mkdtempSync as zo,rmSync as Uo}from"fs";import{tmpdir as jo}from"os";import{join as Go}from"path";class Ht{now;tasks=new Map;sequence=0;constructor(e){this.now=e}schedule=(e,t)=>{if(!Number.isSafeInteger(t)||t<0||!Number.isSafeInteger(this.now+t))throw Error("Scheduled delay must be a nonnegative safe integer");let r=++this.sequence;return this.tasks.set(r,{at:this.now+t,callback:e}),()=>{this.tasks.delete(r)}};advanceTo(e,t){if(!Number.isSafeInteger(e)||e<this.now)throw Error("\u6D4B\u8BD5\u65F6\u95F4\u4E0D\u80FD\u56DE\u9000\u6216\u8D85\u8FC7\u5B89\u5168\u6574\u6570\u8303\u56F4");let r=(Math.floor(this.now/1000)+1)*1000,n=0;while(!0){let i=1/0;for(let s of this.tasks.values())i=Math.min(i,s.at);let o=Math.min(i,t?r:1/0);if(o>e)break;if(this.now=o,t&&o===r)t(),r+=1000;for(let[s,a]of this.tasks)if(a.at<=this.now){if(this.tasks.delete(s),++n>1e5)throw Error("Test scheduler exceeded 100000 callbacks in one advance");a.callback()}}this.now=e}}import{AsyncLocalStorage as Di}from"async_hooks";class Lt{now;entries=[];context=new Di;constructor(e){this.now=e}get events(){return this.entries.slice()}close(){this.context.disable()}record(e,t){let r=this.entries.length+1;return this.entries.push(zr({sequence:r,now:this.now(),cause:this.context.getStore()??null,type:e,value:structuredClone(t)})),r}input(e,t){let r=this.record("input",e);return this.context.run(r,()=>{let n=t();if(n instanceof Promise)return n.then((i)=>(this.record("completion",{input:r,status:"fulfilled"}),i),(i)=>{throw this.record("completion",{input:r,status:"rejected",error:i instanceof Error?i.message:String(i)}),i});return n})}bind(e){let t=this.context.getStore()??null;return()=>this.context.run(t,e)}}function zr(e,t=new Set){if(e&&typeof e==="object"){if(t.has(e))return e;t.add(e);for(let r of Object.values(e))zr(r,t);Object.freeze(e)}return e}import{existsSync as ln}from"fs";import{join as I}from"path";import{existsSync as _i,mkdirSync as ya,readFileSync as Ci,renameSync as ba,unlinkSync as wa,writeFileSync as Sa}from"fs";import{homedir as Ce}from"os";import{dirname as Pi,isAbsolute as $i,join as C,resolve as zt}from"path";import{isIP as Oi}from"net";function Nt(e){if(e.trim()==="")return[];let t=[],r=new Set,n=e.split(",").map((i)=>i.trim()).filter(Boolean);for(let[i,o]of n.entries()){let s=o.indexOf("@");if(s<=0||s!==o.lastIndexOf("@"))throw Error(`ctp.front_candidates \u7B2C ${i+1} \u9879\u683C\u5F0F\u9519\u8BEF\uFF0C\u5E94\u4E3A \u63CF\u8FF0@IP`);let a=o.slice(0,s).trim(),d=o.slice(s+1).trim();if(a==="")throw Error(`ctp.front_candidates \u7B2C ${i+1} \u9879\u7F3A\u5C11\u7EBF\u8DEF\u63CF\u8FF0`);if(Oi(d)===0)throw Error(`ctp.front_candidates \u7B2C ${i+1} \u9879\u4E0D\u662F\u6709\u6548 IP: ${d||"(\u7A7A)"}`);if(r.has(d))throw Error(`ctp.front_candidates \u5305\u542B\u91CD\u590D IP: ${d}`);r.add(d),t.push({description:a,ip:d})}return t}var _e=[{start:"09:00:00",end:"10:15:00",boundaryReason:"break",acceptsPreOpenTick:!0},{start:"10:30:00",end:"11:30:00",boundaryReason:"break"},{start:"13:30:00",end:"15:00:00",boundaryReason:"sessionEnd"}],Ri=[{start:"09:30:00",end:"11:30:00",boundaryReason:"break",acceptsPreOpenTick:!0},{start:"13:00:00",end:"15:00:00",boundaryReason:"sessionEnd"}],xi=[{start:"09:30:00",end:"11:30:00",boundaryReason:"break",acceptsPreOpenTick:!0},{start:"13:00:00",end:"15:15:00",boundaryReason:"sessionEnd"}];function Ft(e){return[{start:"21:00:00",end:e,boundaryReason:"break",acceptsPreOpenTick:!0},..._e]}var et=Ft("23:00:00"),Ur=Ft("01:00:00"),jr=Ft("02:30:00"),qt=new Map;function D(e,t,r){for(let n of t){let i=n.toUpperCase();qt.set(`${e}.${i}`,{exchangeID:e,productID:i,segments:r})}}D("CFFEX",["IF","IH","IC","IM"],Ri);D("CFFEX",["T","TF","TS","TL"],xi);D("SHFE",["AU","AG"],jr);D("SHFE",["CU","AL","ZN","PB","NI","SN","SS","AO","AD"],Ur);D("SHFE",["RB","WR","HC","RU","BU","FU","SP","BR"],et);D("INE",["SC"],jr);D("INE",["BC"],Ur);D("INE",["NR","LU"],et);D("INE",["EC"],_e);D("DCE",["A","B","C","CS","M","Y","P","L","L_F","V","V_F","PP","PP_F","J","JM","I","EG","EB","PG"],et);D("DCE",["JD","FB","BB","RR","LH","LG","BZ"],_e);D("CZCE",["CF","SR","TA","OI","MA","FG","RM","ZC","CY","SA","PF","PX","SH","PR"],et);D("CZCE",["WH","PM","RI","RS","JR","LR","SF","SM","AP","CJ","UR","PK"],_e);D("GFEX",["SI","LC","PS","PT","PD"],_e);function Gr(e,t){return qt.get(`${e.toUpperCase()}.${t.toUpperCase()}`)??null}function tt(e,t){let r=Gr(e,t);if(!r)throw Error(`\u4EA7\u54C1\u65F6\u6BB5\u76EE\u5F55\u4E0D\u5305\u542B ${t.toUpperCase()}.${e.toUpperCase()}`);return r}function Vt(){return[...qt.values()]}var Mi="VIBEK_CONFIG_PATH",Ai="VIBEK_PROCESS_ACCOUNT",Ut=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,Kr=["trace","debug","info","warn","error"],Yr=["realtime","replay"];class ee extends Error{path;diagnostics;constructor(e,t){super(`\u914D\u7F6E\u6587\u4EF6\u65E0\u6548: ${e}
|
|
2
|
+
var wt=import.meta.require;function w(e){return e.status==="submitting"||e.status==="notTraded"||e.status==="partTraded"}var br=Symbol.for("vibek.strategy.cta-template-runtime");function St(e){if(!e||typeof e!=="object")return null;let t=e[br];if(typeof t!=="function")return null;let r=t.call(e);if(!r||typeof r.bindContext!=="function"||typeof r.restoreVariables!=="function"||typeof r.setRuntimeState!=="function"||typeof r.getStateVariables!=="function")return null;return r}var we=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;var kr=["strategyName","vtSymbol","mode","inited","trading","pos"],Xn=new Set([...kr,"constructor","__proto__","prototype","onInit","onStart","onStop","onTick","onBar","onTrade","onOrder","buy","sell","short","cover","cancelOrder","cancelAll","loadBars","registerEvent","claimOrder","writeLog","emitHook","getCurrentTime","getContract","getCommission","getPriceTick","getSize"]);function Se(e,t,r){if(!Sr(e))throw Error("\u7B56\u7565 default export \u5FC5\u987B\u662F\u5BF9\u8C61");let n=Object.keys(e).find((p)=>p!=="strategy");if(n)throw Error(`\u7B56\u7565\u5B9A\u4E49\u4E0D\u652F\u6301\u5B57\u6BB5 "${n}"\uFF1Bid\u3001vtSymbol \u548C setting \u5FC5\u987B\u5728\u52A0\u8F7D\u65F6\u4F20\u5165`);if(typeof e.strategy!=="function")throw Error(`\u7B56\u7565 ${t} \u7684 strategy \u5FC5\u987B\u662F\u7C7B`);if(!Sr(r))throw Error(`\u7B56\u7565 ${t} \u7684 setting \u5FC5\u987B\u662F\u5BF9\u8C61`);let i=e.strategy,o=wr(i.parameters,"parameters",t),s=wr(i.variables,"variables",t),a=[...o,...s].find((p)=>Xn.has(p));if(a)throw Error(`\u7B56\u7565 ${t} \u7684 parameters/variables \u4E0D\u5E94\u58F0\u660E\u6846\u67B6\u5B57\u6BB5 ${a}`);let d=o.find((p)=>s.includes(p));if(d)throw Error(`\u7B56\u7565 ${t} \u7684\u5B57\u6BB5 ${d} \u4E0D\u80FD\u540C\u65F6\u5C5E\u4E8E parameters \u548C variables`);let c=Object.keys(r).find((p)=>!o.includes(p));if(c)throw Error(`\u7B56\u7565 ${t} \u4E0D\u652F\u6301\u53C2\u6570 "${c}"`);let u;try{u=new i}catch(p){throw Error(`\u7B56\u7565 ${t} \u6784\u9020\u5931\u8D25: ${p instanceof Error?p.message:String(p)}`)}let l=St(u);if(!l||typeof u.onInit!=="function")throw Error(`\u7B56\u7565 ${t} \u5FC5\u987B\u7EE7\u627F CtaTemplate \u5E76\u5B9E\u73B0 onInit()`);for(let p of kr)if(Object.hasOwn(u,p))throw Error(`\u7B56\u7565 ${t} \u4E0D\u5F97\u58F0\u660E\u6846\u67B6\u5B57\u6BB5 ${p}`);for(let p of[...o,...s])if(!(p in u))throw Error(`\u7B56\u7565 ${t} \u58F0\u660E\u4E86\u5B57\u6BB5 ${p}\uFF0C\u4F46\u5B9E\u4F8B\u6CA1\u6709\u5BF9\u5E94\u9ED8\u8BA4\u503C`);let f=o.map((p)=>{let k=Tr(ti(u,p),`\u7B56\u7565 ${t} \u53C2\u6570 ${p} \u9ED8\u8BA4\u503C`);return{name:p,defaultValue:k,kind:ei(k)}}),h=vr(r,`\u7B56\u7565 ${t} setting`),m=Object.fromEntries(f.map((p)=>[p.name,Object.hasOwn(h,p.name)?h[p.name]:p.defaultValue]));return{definition:{strategy:i},ctor:i,instance:u,runtime:l,effectiveSetting:vr(m,`\u7B56\u7565 ${t} \u751F\u6548 setting`),inspection:{name:i.name,author:i.author??"",parameters:f}}}function wr(e,t,r){if(e===void 0)return[];if(!Array.isArray(e))throw Error(`\u7B56\u7565 ${r} \u7684 ${t} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4`);let n=e.map((i,o)=>{if(typeof i!=="string"||!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(i))throw Error(`\u7B56\u7565 ${r} \u7684 ${t}[${o}] \u975E\u6CD5`);return i});if(new Set(n).size!==n.length)throw Error(`\u7B56\u7565 ${r} \u7684 ${t} \u5B58\u5728\u91CD\u590D\u9879`);return n}function ei(e){if(typeof e==="number")return"number";if(typeof e==="boolean")return"boolean";if(typeof e==="string")return"string";return"json"}function ti(e,t){return e[t]}function Sr(e){return e!==null&&typeof e==="object"&&!Array.isArray(e)}function vr(e,t){return Tr(e,t)}function Tr(e,t){return vt(e,t,new Set),JSON.parse(JSON.stringify(e))}function vt(e,t,r){if(e===null||typeof e==="string"||typeof e==="boolean")return;if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`${t} \u5305\u542B\u975E\u6709\u9650\u6570\u5B57`);return}if(typeof e!=="object")throw Error(`${t} \u5305\u542B\u4E0D\u53EF\u5E8F\u5217\u5316\u7684 ${typeof e}`);if(r.has(e))throw Error(`${t} \u5305\u542B\u5FAA\u73AF\u5F15\u7528`);if(r.add(e),Array.isArray(e))for(let n=0;n<e.length;n++)vt(e[n],`${t}[${n}]`,r);else{let n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw Error(`${t} \u5FC5\u987B\u662F\u666E\u901A\u5BF9\u8C61\u6216\u6570\u7EC4`);for(let[i,o]of Object.entries(e))vt(o,`${t}.${i}`,r)}r.delete(e)}class Ve{timeframes;currentBars=new Map;completedBars=[];tickCallbacks=[];barCallbacks=[];lastTickTime=0;lastCumVolume=null;lastTradingDay="";constructor(e){this.timeframes=e.timeframes.map((t)=>({label:t,seconds:Ir(t)}))}onTick(e){this.tickCallbacks.push(e)}offTick(e){let t=this.tickCallbacks.indexOf(e);if(t>=0)this.tickCallbacks.splice(t,1)}onBar(e){this.barCallbacks.push(e)}offBar(e){let t=this.barCallbacks.indexOf(e);if(t>=0)this.barCallbacks.splice(t,1)}pushTick(e){let t=ni(e);if(this.lastTickTime>0&&t<this.lastTickTime)throw Error(`tick \u65F6\u95F4\u56DE\u9000: ${e.code} ${t} < ${this.lastTickTime}`);this.lastTickTime=t;let r=Boolean(e.tradingDay&&this.lastTradingDay&&e.tradingDay!==this.lastTradingDay),n=0;if(!r&&this.lastCumVolume!==null&&e.volume>=this.lastCumVolume)n=e.volume-this.lastCumVolume;if(this.lastCumVolume=e.volume,e.tradingDay)this.lastTradingDay=e.tradingDay;for(let i of this.tickCallbacks)i(e);for(let i of this.timeframes)this.updateBar(i,e,t,n)}completeThrough(e){let t=Math.floor(e/1000);for(let r of this.timeframes){let n=this.currentBars.get(r.label);if(n&&n.barTime+r.seconds<=t)this.finalizeBar(r.label)}}getCompletedBars(){return this.completedBars}getCurrentBar(e){return this.currentBars.get(e)??null}forceCompleteAll(e){for(let t of this.timeframes)this.finalizeBar(t.label,e)}forceComplete(e,t){if(this.timeframes.some((r)=>r.label===e))this.finalizeBar(e,t)}markDisconnected(){for(let e of this.currentBars.values())e.boundaryReason="disconnect";this.lastCumVolume=null}reset(){this.currentBars.clear(),this.completedBars=[],this.lastTickTime=0,this.lastCumVolume=null,this.lastTradingDay=""}clearCompletedBars(){this.completedBars=[]}_registrySize(){return this.currentBars.size}updateBar(e,t,r,n){let i=ri(r,e.seconds),o=t.lastPrice,s=this.currentBars.get(e.label);if(s&&s.barTime<i)this.finalizeBar(e.label),s=void 0;if(!s){this.currentBars.set(e.label,{code:t.code,timeframe:e.label,barTime:i,open:o,high:o,low:o,close:o,volume:n,tickCount:1,...t.tradingDay?{tradingDay:t.tradingDay}:{}});return}if(s.high=Math.max(s.high,o),s.low=Math.min(s.low,o),s.close=o,s.volume+=n,s.tickCount++,t.tradingDay)s.tradingDay=t.tradingDay}finalizeBar(e,t){let r=this.currentBars.get(e);if(!r)return;if(this.currentBars.delete(e),r.tickCount===0)return;let n=t??r.boundaryReason,i={code:r.code,timeframe:r.timeframe,barTime:r.barTime,open:r.open,high:r.high,low:r.low,close:r.close,volume:r.volume,...r.tradingDay?{tradingDay:r.tradingDay}:{},...n?{boundaryReason:n}:{}};if(this.completedBars.push(i),this.completedBars.length>1000)this.completedBars.shift();for(let o of this.barCallbacks)o(i)}}function Ir(e){let t=/^(\d+)(min|h|d)$/.exec(e);if(!t)throw Error(`\u4E0D\u652F\u6301\u7684 K \u7EBF\u5468\u671F: ${e}`);let r=Number(t[1]);if(!Number.isInteger(r)||r<=0)throw Error(`\u4E0D\u652F\u6301\u7684 K \u7EBF\u5468\u671F: ${e}`);if(t[2]==="min")return r*60;if(t[2]==="h")return r*3600;return r*86400}function ri(e,t){return Math.floor(e/t)*t}function ni(e){if(Number.isFinite(e.datetime))return Math.floor(e.datetime/1000);return Math.floor(Or(e.actionDay,e.updateTime,e.updateMillisec??0)/1000)}var Dr=28800000,kt=86400000;function ii(e){let r=((e+Dr)%kt+kt)%kt;return e-r}function oi(e){if(!e||!/^\d{8}$/.test(e))return null;let t=Number(e.slice(0,4)),r=Number(e.slice(4,6)),n=Number(e.slice(6,8)),i=new Date(Date.UTC(t,r-1,n));return i.getUTCFullYear()===t&&i.getUTCMonth()===r-1&&i.getUTCDate()===n?[t,r,n]:null}var Er=new Set,si=43200000;function Or(e,t,r){if(!t)return Date.now();let n=/^(\d{2}):(\d{2}):(\d{2})$/.exec(t);if(!n){let d=new Date(t).getTime();return Number.isNaN(d)?Date.now():d}let i=Number(n[1]),o=Number(n[2]),s=Number(n[3]);if(i>23||o>59||s>59||!Number.isInteger(r)||r<0||r>999)return Date.now();let a=oi(e);if(a){let d=Date.UTC(a[0],a[1]-1,a[2],i,o,s,r)-Dr,c=Math.abs(d-Date.now());if(c>si&&!Er.has(e))Er.add(e),console.warn(`[bar-aggregator] actionDay \u4E0E\u7CFB\u7EDF\u65E5\u671F\u504F\u5DEE ${Math.round(c/3600000)}h: ${e}`);return d}return ii(Date.now())+(i*3600+o*60+s)*1000+r}var qe=["1min","3min","5min","10min","15min","30min","1h","2h","4h","1d"],se={"1min":60,"3min":180,"5min":300,"10min":600,"15min":900,"30min":1800,"1h":3600,"2h":7200,"4h":14400,"1d":86400};var ve=qe.join("\u3001");var ai=/^(\d{4})(\d{2})(\d{2})$/;function Z(e){return typeof e==="string"&&qe.includes(e)}function ze(e,t,r){if(t!=="1d"){let n=se[t];return Math.floor(e/n)*n}return di(r??Tt(e))}function Tt(e){let t=new Date((e+28800)*1000);if(t.getUTCHours()>=20||t.getUTCDay()===0||t.getUTCDay()===6){t.setUTCDate(t.getUTCDate()+(t.getUTCHours()>=20?1:0));while(t.getUTCDay()===0||t.getUTCDay()===6)t.setUTCDate(t.getUTCDate()+1)}return`${t.getUTCFullYear()}${Rr(t.getUTCMonth()+1)}${Rr(t.getUTCDate())}`}function di(e){let t=ai.exec(e);if(!t)throw Error(`\u975E\u6CD5\u4EA4\u6613\u65E5: ${e}`);let r=Number(t[1]),n=Number(t[2]),i=Number(t[3]),o=Date.UTC(r,n-1,i),s=new Date(o);if(s.getUTCFullYear()!==r||s.getUTCMonth()!==n-1||s.getUTCDate()!==i)throw Error(`\u975E\u6CD5\u4EA4\u6613\u65E5: ${e}`);return Math.floor(o/1000)-28800}function _r(e,t){let r=e==="1d"?600:se[e]/60;return t*r+(r-1)}function Rr(e){return String(e).padStart(2,"0")}var ci=new Set(qe);class Ue{timeframe;seconds;current=null;constructor(e){this.timeframe=e;if(!ci.has(e))throw Error(`\u5468\u671F "${e}" \u4E0D\u53D7\u652F\u6301\uFF1B\u4EC5\u652F\u6301 ${ve}`);this.seconds=se[e]}pushBar(e){let t=this.pushBars(e);if(t.length>1)throw Error("\u5355\u6839 1min bar \u540C\u65F6\u95ED\u5408\u4E86\u591A\u4E2A\u7A97\u53E3\uFF1B\u8BF7\u4F7F\u7528 pushBars() \u4FDD\u7559\u5168\u90E8\u4FE1\u53F7");return t[0]??null}pushBars(e){if(e.timeframe!=="1min")throw Error(`BarGenerator \u53EA\u63A5\u53D7\u95ED\u5408 1min bar\uFF0C\u6536\u5230 ${e.timeframe}`);if(this.seconds===60)return[e];let t=[],r=ze(e.barTime,this.timeframe,e.tradingDay);if(this.current&&this.current.barTime!==r){let o=this.finish();if(o)t.push(o)}if(!this.current)this.start(e,r);else this.merge(e);let n=this.timeframe!=="1d"&&e.barTime+60>=r+this.seconds,i=this.seconds>=3600?e.boundaryReason==="sessionEnd":e.boundaryReason!==void 0;if(n||i){let o=this.finish(e.boundaryReason);if(o)t.push(o)}return t}flush(e){return this.finish(e)}start(e,t){this.current={code:e.code,timeframe:this.timeframe,barTime:t,open:e.open,high:e.high,low:e.low,close:e.close,volume:e.volume,...e.tradingDay?{tradingDay:e.tradingDay}:{}}}merge(e){let t=this.current;if(t.high=Math.max(t.high,e.high),t.low=Math.min(t.low,e.low),t.close=e.close,t.volume+=e.volume,e.tradingDay)t.tradingDay=e.tradingDay}finish(e){let t=this.current;if(!t)return null;return this.current=null,{...t,...e?{boundaryReason:e}:{}}}}function xr(e){if(e.timeframe!=="1min")throw Error(`\u7B56\u7565\u9884\u70ED\u53EA\u63A5\u53D7\u95ED\u5408 1min Bar\uFF0C\u6536\u5230 ${e.timeframe}`)}function*Cr(e,t){if(t==="1min"){for(let o=0;o<e.length;o++)xr(e[o]),yield{startIndex:o,endIndex:o,triggerIndex:o};return}let r=new Ue(t),n=0,i=-1/0;for(let o=0;o<e.length;o++){let s=e[o];if(xr(s),s.barTime<i)throw Error("\u7B56\u7565\u9884\u70ED Bar \u5FC5\u987B\u6309\u65F6\u95F4\u5347\u5E8F\u6392\u5217");i=s.barTime;let a=ze(s.barTime,t,s.tradingDay);for(let d of r.pushBars(s)){let c=d.barTime===a?o:o-1;if(c<n)throw Error("\u7B56\u7565\u9884\u70ED Bar \u805A\u5408\u8FB9\u754C\u65E0\u6548");yield{startIndex:n,endIndex:c,triggerIndex:o},n=c+1}}}function Et(e,t){let r=0;for(let n of Cr(e,t))r++;return r}function ke(e,t,r){if(!Number.isInteger(r)||r<=0)throw Error("\u9884\u70ED Bar count \u5FC5\u987B\u4E3A\u6B63\u6574\u6570");let n=[...Cr(e,t)],i=n.length;if(i<r)return{bars:[...e],availableCount:i,selectedCount:i};let o=n[i-r];return{bars:e.slice(o.startIndex),availableCount:i,selectedCount:r}}var ui=new Set(["CFFEX","SHFE","CZCE","DCE","INE","GFEX"]);function K(e){let t=e.trim(),[r="",n,...i]=t.split(".");if(!r||!n||i.length>0)throw Error(`vtSymbol \u975E\u6CD5: ${e}`);if(!/^[A-Za-z][A-Za-z0-9]*$/.test(r)||!/\d/.test(r))throw Error(`vtSymbol \u5408\u7EA6\u4EE3\u7801\u975E\u6CD5: ${e}`);if(!ui.has(n.toUpperCase()))throw Error(`vtSymbol \u4EA4\u6613\u6240\u975E\u6CD5: ${e}`);let o=r.toUpperCase();return{instrumentID:o,vtSymbol:`${o}.${n.toUpperCase()}`}}function Pr(e){let t=e.split(".")[1]?.toUpperCase();return t==="SHFE"||t==="INE"}var li="20260105",mi=3500;function Y(e){if(!ae(e))throw Error("\u6D4B\u8BD5\u5B9A\u4E49 fixture \u5FC5\u987B\u662F\u5BF9\u8C61");if(Te(e,["vtSymbol","setting","instrument","commission","seedPrice","tradingDay"],"fixture"),typeof e.vtSymbol!=="string")throw Error("fixture.vtSymbol \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");let t=K(e.vtSymbol);if(!ae(e.instrument))throw Error("fixture.instrument \u5FC5\u987B\u662F\u5BF9\u8C61");Te(e.instrument,["priceTick","size","name","minOrderVolume","maxOrderVolume","longMarginRatio","shortMarginRatio","isTrading"],"fixture.instrument");let r=E(e.instrument.priceTick,"fixture.instrument.priceTick"),n=_(e.instrument.size,"fixture.instrument.size"),i=e.instrument.name===void 0?t.instrumentID:e.instrument.name;if(typeof i!=="string")throw Error("fixture.instrument.name \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");let o=_(e.instrument.minOrderVolume===void 0?1:e.instrument.minOrderVolume,"fixture.instrument.minOrderVolume"),s=_(e.instrument.maxOrderVolume===void 0?1e5:e.instrument.maxOrderVolume,"fixture.instrument.maxOrderVolume");if(s<o)throw Error("fixture.instrument.maxOrderVolume \u5FC5\u987B\u4E0D\u5C0F\u4E8E minOrderVolume");let a=Ee(e.instrument.longMarginRatio===void 0?0.1:e.instrument.longMarginRatio,"fixture.instrument.longMarginRatio"),d=Ee(e.instrument.shortMarginRatio===void 0?0.1:e.instrument.shortMarginRatio,"fixture.instrument.shortMarginRatio"),c=e.instrument.isTrading===void 0?!0:e.instrument.isTrading;if(typeof c!=="boolean")throw Error("fixture.instrument.isTrading \u5FC5\u987B\u662F\u5E03\u5C14\u503C");let u=e.commission===void 0?void 0:pi(e.commission),l=e.setting===void 0?{}:ce(e.setting,"fixture.setting"),f=e.seedPrice===void 0?mi:E(e.seedPrice,"fixture.seedPrice"),h=e.tradingDay===void 0?li:X(e.tradingDay),m=/^([A-Z]+)\d+([A-Z]+)$/.exec(t.instrumentID),v=m?`${m[1]}_${m[2]}`:t.instrumentID.match(/^[A-Z]+/)?.[0]??"";if(!v)throw Error(`\u65E0\u6CD5\u4ECE ${t.instrumentID} \u63A8\u5BFC fixture \u4EA7\u54C1\u4EE3\u7801`);return{vtSymbol:t.vtSymbol,instrumentID:t.instrumentID,exchangeID:t.vtSymbol.split(".")[1],productID:v,setting:l,priceTick:r,size:n,name:i,minOrderVolume:o,maxOrderVolume:s,longMarginRatio:a,shortMarginRatio:d,isTrading:c,commission:u,seedPrice:f,tradingDay:h}}function Ge(e){let t=e.commission;if(!t)return null;return{instrumentID:e.instrumentID,exchangeID:e.exchangeID,investorRange:"1",brokerID:"test",investorID:"test",bizType:"1",investUnitID:"",openRatioByMoney:t.open.ratioByMoney,openRatioByVolume:t.open.perLot,closeRatioByMoney:t.close.ratioByMoney,closeRatioByVolume:t.close.perLot,closeTodayRatioByMoney:t.closeToday.ratioByMoney,closeTodayRatioByVolume:t.closeToday.perLot}}function pi(e){if(!ae(e))throw Error("fixture.commission \u5FC5\u987B\u662F\u5BF9\u8C61");return Te(e,["open","close","closeToday"],"fixture.commission"),{open:It(e.open,"fixture.commission.open"),close:It(e.close,"fixture.commission.close"),closeToday:It(e.closeToday,"fixture.commission.closeToday")}}function It(e,t){if(!ae(e))throw Error(`${t} \u5FC5\u987B\u662F\u5BF9\u8C61`);return Te(e,["ratioByMoney","perLot"],t),{ratioByMoney:Ee(e.ratioByMoney,`${t}.ratioByMoney`),perLot:Ee(e.perLot,`${t}.perLot`)}}function Ie(e,t,r){let n=-1;for(let i of e){if(i.code!==r)throw Error("\u6D4B\u8BD5 Bar \u5408\u7EA6\u4E0E fixture \u4E0D\u5339\u914D");if(i.barTime<=n)throw Error("\u6D4B\u8BD5 Bar \u5FC5\u987B\u6309\u65F6\u95F4\u4E25\u683C\u9012\u589E");if((i.barTime+(i.boundaryReason?0:60))*1000>t)throw Error("\u6D4B\u8BD5 Bar \u5C1A\u672A\u95ED\u5408\uFF0C\u4E0D\u80FD\u4F7F\u7528\u672A\u6765\u6570\u636E");n=i.barTime}}function Ke(e){let t=e.tradingDay;return Date.parse(`${t.slice(0,4)}-${t.slice(4,6)}-${t.slice(6,8)}T09:00:00+08:00`)}function Ot(e){let t=new Date(e+28800000);return`${t.getUTCFullYear()}${String(t.getUTCMonth()+1).padStart(2,"0")}${String(t.getUTCDate()).padStart(2,"0")}`}function H(e,t,r,n=Ke(e)/1000+r*60){if(t.timeframe!==void 0&&t.timeframe!=="1min")throw Error("bar.timeframe \u5FC5\u987B\u662F 1min");let i=E(t.close,"bar.close"),o=t.open===void 0?i:E(t.open,"bar.open"),s=t.high===void 0?Math.max(o,i):E(t.high,"bar.high"),a=t.low===void 0?Math.min(o,i):E(t.low,"bar.low");if(s<Math.max(o,i)||a>Math.min(o,i))throw Error("Bar OHLC \u5173\u7CFB\u975E\u6CD5");let d=t.volume===void 0?100+r:Ee(t.volume,"bar.volume"),c=t.barTime===void 0?n:T(t.barTime,"bar.barTime");return{code:$r(t.code??e.instrumentID),timeframe:"1min",barTime:c,open:o,high:s,low:a,close:i,volume:d,tradingDay:t.tradingDay===void 0?e.tradingDay:X(t.tradingDay),...t.boundaryReason?{boundaryReason:t.boundaryReason}:{}}}function De(e,t,r,n=Ke(e)+r*60000){let i=E(t.lastPrice,"tick.lastPrice"),o=t.datetime===void 0?n:T(t.datetime,"tick.datetime"),s=new Date(o+28800000),a=t.updateTime??[s.getUTCHours(),s.getUTCMinutes(),s.getUTCSeconds()].map((c)=>String(c).padStart(2,"0")).join(":");if(!/^(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d$/.test(a))throw Error("tick.updateTime \u5FC5\u987B\u662F HH:MM:SS");let d=t.updateMillisec===void 0?o%1000:T(t.updateMillisec,"tick.updateMillisec");if(d>999)throw Error("tick.updateMillisec \u5FC5\u987B\u5728 0-999 \u4E4B\u95F4");return{code:$r(t.code??e.instrumentID),exchangeID:t.exchangeID??e.exchangeID,productID:t.productID??e.productID,priceTick:t.priceTick===void 0?e.priceTick:E(t.priceTick,"tick.priceTick"),lastPrice:i,updateTime:a,updateMillisec:d,actionDay:t.actionDay===void 0?Ot(o):X(t.actionDay),tradingDay:t.tradingDay===void 0?e.tradingDay:X(t.tradingDay),volume:t.volume===void 0?100+r:T(t.volume,"tick.volume"),datetime:o,bidPrice1:je(t,"bidPrice1",i,E),bidVolume1:je(t,"bidVolume1",1000,T),askPrice1:je(t,"askPrice1",i,E),askVolume1:je(t,"askVolume1",1000,T),...t.upperLimitPrice===void 0?{}:{upperLimitPrice:E(t.upperLimitPrice,"tick.upperLimitPrice")},...t.lowerLimitPrice===void 0?{}:{lowerLimitPrice:E(t.lowerLimitPrice,"tick.lowerLimitPrice")},...t.rawActionDay===void 0?{}:{rawActionDay:t.rawActionDay},...t.receivedAt===void 0?{}:{receivedAt:T(t.receivedAt,"tick.receivedAt")},...t.dateSource===void 0?{}:{dateSource:t.dateSource},...t.tradingDaySource===void 0?{}:{tradingDaySource:t.tradingDaySource}}}function $r(e){if(typeof e!=="string"||!/^[A-Za-z][A-Za-z0-9]*$/.test(e))throw Error("\u884C\u60C5 code \u975E\u6CD5");return e}function je(e,t,r,n){if(!Object.hasOwn(e,t))return r;return e[t]===void 0?void 0:n(e[t],`tick.${t}`)}function Ye(e){return{instrumentID:e.instrumentID,exchangeID:e.exchangeID,productID:e.productID,productClass:"1",exchangeInstID:e.instrumentID,instrumentName:e.name,volumeMultiple:e.size,priceTick:e.priceTick,maxLimitOrderVolume:e.maxOrderVolume,minLimitOrderVolume:e.minOrderVolume,maxMarketOrderVolume:0,minMarketOrderVolume:0,longMarginRatio:e.longMarginRatio,shortMarginRatio:e.shortMarginRatio,maxMarginSideAlgorithm:"",strikePrice:0,optionsType:"",combinationType:"",deliveryYear:0,deliveryMonth:0,createDate:"",openDate:"",expireDate:"",startDelivDate:"",endDelivDate:"",instLifePhase:"1",isTrading:e.isTrading?1:0,positionType:"",positionDateType:"",underlyingInstrID:"",underlyingMultiple:1}}function We(e){if(e===void 0)return de();if(!ae(e))throw Error("initialState.inventory \u5FC5\u987B\u662F\u5BF9\u8C61");return Te(e,["longToday","longYesterday","shortToday","shortYesterday"],"initialState.inventory"),{longToday:e.longToday===void 0?0:T(e.longToday,"longToday"),longYesterday:e.longYesterday===void 0?0:T(e.longYesterday,"longYesterday"),shortToday:e.shortToday===void 0?0:T(e.shortToday,"shortToday"),shortYesterday:e.shortYesterday===void 0?0:T(e.shortYesterday,"shortYesterday")}}function de(){return{longToday:0,longYesterday:0,shortToday:0,shortYesterday:0}}function Mr(e){return e.longToday+e.longYesterday-e.shortToday-e.shortYesterday}function gi(e,t){let r=new Date(Date.UTC(Number(e.slice(0,4)),Number(e.slice(4,6))-1,Number(e.slice(6,8))+t));return`${r.getUTCFullYear()}${String(r.getUTCMonth()+1).padStart(2,"0")}${String(r.getUTCDate()).padStart(2,"0")}`}function X(e){if(typeof e!=="string"||!/^\d{8}$/.test(e))throw Error("fixture.tradingDay \u5FC5\u987B\u662F YYYYMMDD");if(gi(e,0)!==e)throw Error(`fixture.tradingDay \u975E\u6CD5: ${e}`);return e}function ce(e,t){if(!ae(e))throw Error(`${t} \u5FC5\u987B\u662F\u5BF9\u8C61`);try{Dt(e,t,new Set);let r=JSON.stringify(e);if(r===void 0)throw Error("\u4E0D\u53EF\u5E8F\u5217\u5316");return JSON.parse(r)}catch(r){throw Error(`${t} \u5FC5\u987B\u53EF JSON \u5E8F\u5217\u5316: ${r instanceof Error?r.message:String(r)}`)}}function Dt(e,t,r){if(e===null||typeof e==="string"||typeof e==="boolean")return;if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`${t} \u5305\u542B\u975E\u6709\u9650\u6570\u5B57`);return}if(typeof e!=="object")throw Error(`${t} \u5305\u542B\u4E0D\u53EF\u5E8F\u5217\u5316\u7684 ${typeof e}`);if(r.has(e))throw Error(`${t} \u5305\u542B\u5FAA\u73AF\u5F15\u7528`);if(r.add(e),Array.isArray(e))for(let n=0;n<e.length;n++)Dt(e[n],`${t}[${n}]`,r);else{let n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw Error(`${t} \u5FC5\u987B\u662F\u666E\u901A\u5BF9\u8C61\u6216\u6570\u7EC4`);for(let[i,o]of Object.entries(e))Dt(o,`${t}.${i}`,r)}r.delete(e)}function Te(e,t,r){let n=Object.keys(e).find((i)=>!t.includes(i));if(n)throw Error(`${r} \u4E0D\u652F\u6301\u5B57\u6BB5 "${n}"`)}function E(e,t){if(typeof e!=="number"||!Number.isFinite(e)||e<=0)throw Error(`${t} \u5FC5\u987B\u662F\u6B63\u6570`);return e}function _(e,t){if(!Number.isSafeInteger(e)||e<=0)throw Error(`${t} \u5FC5\u987B\u662F\u6B63\u6574\u6570`);return e}function T(e,t){if(!Number.isSafeInteger(e)||e<0)throw Error(`${t} \u5FC5\u987B\u662F\u975E\u8D1F\u6574\u6570`);return e}function Ee(e,t){if(typeof e!=="number"||!Number.isFinite(e)||e<0)throw Error(`${t} \u5FC5\u987B\u662F\u975E\u8D1F\u6570`);return e}function ae(e){return e!==null&&typeof e==="object"&&!Array.isArray(e)}class Oe{fixture;sequence;now;observations;phase="running";instrument;orders=new Map;trades=new Map;reportedOrders=new Map;deliveredTrades=new Set;orderHandlers=new Set;tradeHandlers=new Set;errorHandlers=new Set;reconcileHandlers=new Set;faultHandlers=new Set;inventories=new Map;tradingDay;nextSubmitFailure;nextCancelFailure;holdingReports=!1;reportSequence=0;reports=new Map;pending=new Set;deliveredReports=new Set;constructor(e,t,r,n,i,o){this.fixture=e;this.sequence=r;this.now=n;this.observations=i;if(this.inventories.set(e.vtSymbol,{...t}),this.tradingDay=e.tradingDay,this.instrument=Ye(e),o)this.reconcile({...o,reason:"initial"})}snapshot(){return{phase:this.phase,tradingDay:this.tradingDay,inventory:{...this.inventories.get(this.fixture.vtSymbol)??de()},orders:[...this.orders.values()].map(L),trades:[...this.trades.values()].map((e)=>({...e}))}}holdReports(){this.holdingReports=!0}resumeReports(){this.requireConnected(),this.holdingReports=!1;for(let e of[...this.pending])this.deliverReport(e)}pendingReports(){return[...this.pending].map((e)=>structuredClone(this.reports.get(e)))}deliverReport(e){if(this.requireConnected(),!this.pending.delete(e))throw Error(`Pending broker report does not exist: ${e}`);this.deliver(this.reports.get(e))}repeatReport(e){this.requireConnected();let t=this.reports.get(e);if(!t||!this.deliveredReports.has(e))throw Error(`Delivered broker report does not exist: ${e}`);this.deliver(t,!0)}async prepareInstrument(e,t){if(e!==this.fixture.instrumentID||t!==this.fixture.exchangeID)throw Error(`\u6D4B\u8BD5\u5408\u7EA6\u4E0D\u5339\u914D: ${e}.${t}`);return this.instrument}async prepareCommission(e,t){if(e!==this.fixture.instrumentID||t!==this.fixture.exchangeID)throw Error(`\u6D4B\u8BD5\u624B\u7EED\u8D39\u5408\u7EA6\u4E0D\u5339\u914D: ${e}.${t}`)}getInstrument(e){return e===this.fixture.instrumentID?this.instrument:null}getCommission(e,t){if(e!==this.fixture.instrumentID||t!==this.fixture.exchangeID)return null;return Ge(this.fixture)}async queryPositions(e){return{phase:this.phase,positions:this.positionSnapshot().filter((t)=>!e||t.instrumentID===e.toUpperCase())}}async queryOrders(e){return[...this.orders.values()].filter((t)=>!e||t.instrumentID===e.toUpperCase()).map((t)=>L(t))}submitOrder(e){if(this.requireConnected(),this.nextSubmitFailure){let o=this.nextSubmitFailure;throw this.nextSubmitFailure=void 0,Error(o)}let t,r;do t=String(++this.sequence.order),r=`CTP.${this.tradingDay}.1.1.${t}`;while(this.orders.has(r));let n=e.direction==="buy"?"long":"short",i={vtOrderId:r,identity:{tradingDay:this.tradingDay,frontID:1,sessionID:1,orderRef:t,instrumentID:this.fixture.instrumentID,exchangeID:this.fixture.exchangeID},vtSymbol:this.fixture.vtSymbol,instrumentID:this.fixture.instrumentID,exchangeID:this.fixture.exchangeID,orderSysID:`TEST-${t}`,side:e.direction,direction:n,offset:e.offsetFlag,price:e.price,volume:e.volume,traded:0,status:"submitting",statusMessage:"",datetime:this.now()};return this.orders.set(r,i),this.reportedOrders.set(r,L(i)),this.observations.submissions.push({vtOrderId:r,direction:n,offset:e.offsetFlag,price:e.price,volume:e.volume}),this.observations.record("submission",this.observations.submissions.at(-1)),{vtOrderId:r,orderRef:t}}cancelOrder(e){if(this.observations.cancelRequests.push(e),this.observations.record("cancel",{vtOrderId:e}),this.requireConnected(),this.nextCancelFailure){let t=this.nextCancelFailure;throw this.nextCancelFailure=void 0,Error(t)}}onOrder(e){return this.orderHandlers.add(e),()=>this.orderHandlers.delete(e)}onTrade(e){return this.tradeHandlers.add(e),()=>this.tradeHandlers.delete(e)}onOrderError(e){return this.errorHandlers.add(e),()=>this.errorHandlers.delete(e)}onReconciled(e){return this.reconcileHandlers.add(e),()=>this.reconcileHandlers.delete(e)}onFault(e){return this.faultHandlers.add(e),()=>this.faultHandlers.delete(e)}failNextSubmit(e="test submit failed"){this.nextSubmitFailure=e}rejectSubmit(e,t="test submit rejected"){let r=this.requireActiveOrder(e);if(r.traded!==0||this.tradedVolume(e)>0)throw Error("\u5DF2\u6210\u4EA4\u59D4\u6258\u4E0D\u80FD\u62D2\u5355");if(r.status="rejected",r.statusMessage=t,this.phase!=="running")return;this.reportedOrders.set(e,L(r)),this.queueReport({type:"error",value:{vtOrderId:e,orderRef:r.identity.orderRef,instrumentID:r.instrumentID,operation:"submit",message:t}})}rejectCancel(e,t="test cancel rejected"){let r=this.requireOrder(e);this.requireConnected(),this.queueReport({type:"error",value:{vtOrderId:e,orderRef:r.identity.orderRef,instrumentID:r.instrumentID,operation:"cancel",message:t}})}emitExternalOrder(e){if(this.orders.has(e.vtOrderId))throw Error(`\u6D4B\u8BD5\u59D4\u6258\u5DF2\u5B58\u5728: ${e.vtOrderId}`);this.emitOrder(e)}emitOrder(e){let t=this.normalizeOrder(e);this.orders.set(t.vtOrderId,t),this.publishOrder(t)}emitTrade(e){let t=this.recordTrade(e);this.publishTrade(t)}disconnect(){if(this.phase!=="faulted")this.phase="recovering"}connect(){this.phase="running",this.reportedOrders.clear(),this.deliveredTrades.clear(),this.nextSubmitFailure=void 0,this.nextCancelFailure=void 0,this.pending.clear(),this.holdingReports=!1,this.reconcile({reason:"initial"})}reconcile(e={}){if(this.phase==="faulted")throw Error("\u6D4B\u8BD5 Broker \u5DF2 fault\uFF0C\u5FC5\u987B\u91CD\u542F Runtime");this.phase="recovering",this.pending.clear();try{let t=e.tradingDay===void 0?this.tradingDay:X(e.tradingDay);if(t<this.tradingDay)throw Error("\u6D4B\u8BD5 Broker TradingDay \u4E0D\u80FD\u56DE\u9000");if(t!==this.tradingDay)for(let o of this.inventories.values())o.longYesterday+=o.longToday,o.longToday=0,o.shortYesterday+=o.shortToday,o.shortToday=0;this.tradingDay=t;let r=e.orders===void 0?[...this.orders.values()].filter((o)=>o.identity.tradingDay===t).map(L):e.orders.map((o)=>this.normalizeOrder(o));if(e.orders!==void 0){for(let o of this.orders.values())if(o.identity.tradingDay===t)this.orders.delete(o.vtOrderId);for(let o of r)this.orders.set(o.vtOrderId,o)}for(let o of e.trades??[])this.recordTrade(o);if(e.inventory!==void 0)this.inventories.set(this.fixture.vtSymbol,We(e.inventory));let n=[...this.trades.values()].filter((o)=>o.tradingDay===t),i={reason:e.reason??"reconnect",tradingDay:t,accounts:[],positions:this.positionSnapshot(),orders:r,trades:n,recoveredAt:this.now()};if(i.reason==="reconnect"){for(let o of this.reportedOrders.values())if(w(o)&&o.identity.tradingDay===t&&!r.some((s)=>s.vtOrderId===o.vtOrderId))throw Error(`\u6D3B\u52A8\u59D4\u6258\u672A\u51FA\u73B0\u5728\u6062\u590D\u5FEB\u7167: ${o.vtOrderId}`);for(let o of r)this.publishOrder(o,!0);for(let o of n)this.publishTrade(o,!0)}else{for(let o of r)this.reportedOrders.set(o.vtOrderId,L(o));for(let o of n)this.deliveredTrades.add(o.vtTradeId)}for(let o of this.reconcileHandlers)o(i);this.phase="running"}catch(t){throw this.fault(t instanceof Error?t.message:String(t)),t}}fault(e="test gateway fault"){if(this.phase==="faulted")return;this.phase="faulted";for(let t of this.faultHandlers)t(e)}acknowledge(e){this.updateOrder(e,(t)=>{if(t.traded!==0||this.tradedVolume(e)>0)throw Error("\u5DF2\u6210\u4EA4\u59D4\u6258\u4E0D\u80FD\u91CD\u65B0\u786E\u8BA4");t.status="notTraded",t.statusMessage="test acknowledged"})}partialFill(e,t,r){this.fillVolume(e,t,r)}fill(e,t){let r=this.requireActiveOrder(e);this.fillVolume(e,r.volume-Math.max(r.traded,this.tradedVolume(e)),t)}reject(e,t="test rejected"){this.updateOrder(e,(r)=>{if(r.traded!==0||this.tradedVolume(e)>0)throw Error("\u5DF2\u6210\u4EA4\u59D4\u6258\u4E0D\u80FD\u62D2\u5355");r.status="rejected",r.statusMessage=t})}cancel(e,t="test cancelled"){this.updateOrder(e,(r)=>{r.traded=Math.max(r.traded,this.tradedVolume(e)),r.status="cancelled",r.statusMessage=t})}failNextCancel(e="test cancel failed"){this.nextCancelFailure=e}settleActiveOrders(){for(let e of[...this.orders.values()])if(e.status==="submitting"||e.status==="notTraded"||e.status==="partTraded")this.cancel(e.vtOrderId,"test cleanup")}fillVolume(e,t,r){if(!Number.isInteger(t)||t<=0)throw Error("\u6210\u4EA4\u6570\u91CF\u5FC5\u987B\u662F\u6B63\u6574\u6570");let n=this.requireActiveOrder(e),i=n.volume-Math.max(n.traded,this.tradedVolume(e));if(t>i)throw Error(`\u6210\u4EA4\u6570\u91CF ${t} \u8D85\u8FC7\u5269\u4F59\u6570\u91CF ${i}`);let o=E(r??n.price,"\u6210\u4EA4\u4EF7\u683C");n.traded=n.volume-i+t,n.status=n.traded===n.volume?"allTraded":"partTraded",n.statusMessage="test filled",this.publishOrder(n);let s;do s=`TESTTRADE.${++this.sequence.trade}`;while(this.trades.has(s));this.emitTrade({vtTradeId:s,vtOrderId:n.vtOrderId,price:o,volume:t,datetime:this.now(),tradingDay:this.tradingDay})}updateOrder(e,t){let r=this.requireActiveOrder(e);t(r),this.publishOrder(r)}normalizeOrder(e){let t=this.orders.get(e.vtOrderId),r=hi({...this.fixture,tradingDay:this.tradingDay},{...e,vtSymbol:e.vtSymbol??t?.vtSymbol},this.now());if(t){if(t.vtSymbol!==r.vtSymbol||t.direction!==r.direction||t.offset!==r.offset||t.volume!==r.volume)throw Error(`\u6D4B\u8BD5\u59D4\u6258\u8EAB\u4EFD\u5B57\u6BB5\u53D1\u751F\u53D8\u5316: ${r.vtOrderId}`);if(r.identity={...t.identity},r.orderSysID=t.orderSysID,r.traded<t.traded||Ar(r)<Ar(t))return L(t)}return r}tradedVolume(e){let t=0;for(let r of this.trades.values())if(r.vtOrderId===e)t+=r.volume;return t}recordTrade(e){if(typeof e.vtTradeId!=="string"||!e.vtTradeId.trim())throw Error("\u6D4B\u8BD5\u6210\u4EA4 ID \u4E0D\u80FD\u4E3A\u7A7A");let t=this.trades.get(e.vtTradeId);if(t)return t;let r=this.requireOrder(e.vtOrderId),n=_(e.volume,"\u6210\u4EA4\u6570\u91CF"),i=this.tradedVolume(r.vtOrderId)+n;if(i>r.volume||!w(r)&&i>r.traded)throw Error("\u6D4B\u8BD5\u6210\u4EA4\u6570\u91CF\u8D85\u8FC7\u59D4\u6258\u5B9E\u9645\u6210\u4EA4\u8303\u56F4");let o={vtTradeId:e.vtTradeId,vtOrderId:r.vtOrderId,vtSymbol:r.vtSymbol,instrumentID:r.instrumentID,exchangeID:r.exchangeID,orderSysID:r.orderSysID,tradeID:e.vtTradeId,side:r.side,direction:r.direction,offset:r.offset,price:E(e.price??r.price,"\u6210\u4EA4\u4EF7\u683C"),volume:n,datetime:e.datetime===void 0?this.now():T(e.datetime,"\u6210\u4EA4\u65F6\u95F4"),tradingDay:e.tradingDay===void 0?this.tradingDay:X(e.tradingDay)};if(o.tradingDay!==this.tradingDay)throw Error("\u6D4B\u8BD5\u6210\u4EA4 TradingDay \u4E0E Broker \u4E0D\u4E00\u81F4");this.trades.set(o.vtTradeId,o);let s=this.inventories.get(o.vtSymbol)??de();return this.inventories.set(o.vtSymbol,s),yi(s,o.direction,o.offset,o.volume),o}positionSnapshot(){return[...this.inventories].flatMap(([e,t])=>fi({...this.fixture,...K(e),exchangeID:e.split(".")[1],tradingDay:this.tradingDay},t))}publishOrder(e,t=!1){if(!t&&this.phase!=="running")return;this.queueReport({type:"order",value:L(e)},t)}publishTrade(e,t=!1){if(!t&&this.phase!=="running")return;this.queueReport({type:"trade",value:{...e}},t)}queueReport(e,t=!1){let r={...e,id:`report-${++this.reportSequence}`,createdAt:this.now()};if(this.reports.set(r.id,r),this.observations.record("broker",{operation:"queued",report:r}),!this.holdingReports||t)this.deliver(r);else this.pending.add(r.id)}deliver(e,t=!1){if(this.deliveredReports.add(e.id),this.observations.record("broker",{operation:"delivered",duplicate:t,report:e}),e.type==="trade"){let i=e.value;if(!t&&this.deliveredTrades.has(i.vtTradeId))return;this.deliveredTrades.add(i.vtTradeId);for(let o of this.tradeHandlers)o({...i});return}if(e.type==="error"){for(let i of this.errorHandlers)i({...e.value});return}let r=e.value,n=this.reportedOrders.get(r.vtOrderId);if(!t&&n&&n.status===r.status&&n.traded===r.traded&&n.statusMessage===r.statusMessage&&n.orderSysID===r.orderSysID)return;this.reportedOrders.set(r.vtOrderId,L(r));for(let i of this.orderHandlers)i(L(r))}requireConnected(){if(this.phase!=="running")throw Error(`\u6D4B\u8BD5 Broker \u672A\u5C31\u7EEA: ${this.phase}`)}requireOrder(e){let t=this.orders.get(e);if(!t)throw Error(`\u6D4B\u8BD5\u59D4\u6258\u4E0D\u5B58\u5728: ${e}`);return t}requireActiveOrder(e){let t=this.requireOrder(e);if(!w(t))throw Error(`\u6D4B\u8BD5\u59D4\u6258\u5DF2\u7EC8\u6B62: ${e} (${t.status})`);return t}}function Ar(e){if(e.status==="submitting")return 0;if(e.status==="notTraded")return 1;if(e.status==="partTraded")return 2;return 3}function hi(e,t,r){if(typeof t.vtOrderId!=="string"||!t.vtOrderId.trim())throw Error("\u6D4B\u8BD5\u59D4\u6258 ID \u4E0D\u80FD\u4E3A\u7A7A");let{vtSymbol:n,instrumentID:i}=K(t.vtSymbol??e.vtSymbol),o=n.split(".")[1];if(t.direction!=="long"&&t.direction!=="short")throw Error("\u6D4B\u8BD5\u59D4\u6258\u65B9\u5411\u975E\u6CD5");let s=t.offset??"open";if(!["open","close","closeToday","closeYesterday"].includes(s))throw Error("\u6D4B\u8BD5\u59D4\u6258\u5F00\u5E73\u975E\u6CD5");let a=_(t.volume,"\u6D4B\u8BD5\u59D4\u6258\u6570\u91CF"),d=t.traded===void 0?0:T(t.traded,"\u6D4B\u8BD5\u59D4\u6258\u5DF2\u6210\u4EA4\u6570\u91CF"),c=t.status??"notTraded";if(!["submitting","notTraded","partTraded","allTraded","cancelled","rejected"].includes(c)||d>a||(c==="submitting"||c==="notTraded"||c==="rejected")&&d!==0||c==="partTraded"&&(d===0||d===a)||c==="allTraded"&&d!==a)throw Error("\u6D4B\u8BD5\u59D4\u6258\u72B6\u6001\u4E0E\u6210\u4EA4\u6570\u91CF\u4E0D\u4E00\u81F4");return{vtOrderId:t.vtOrderId,identity:{tradingDay:e.tradingDay,frontID:1,sessionID:1,orderRef:t.vtOrderId.split(".").at(-1),instrumentID:i,exchangeID:o},vtSymbol:n,instrumentID:i,exchangeID:o,orderSysID:`TEST-${t.vtOrderId}`,side:t.direction==="long"?"buy":"sell",direction:t.direction,offset:s,price:E(t.price,"\u6D4B\u8BD5\u59D4\u6258\u4EF7\u683C"),volume:a,traded:d,status:c,statusMessage:t.statusMessage??"",datetime:t.datetime===void 0?r:T(t.datetime,"\u6D4B\u8BD5\u59D4\u6258\u65F6\u95F4")}}function fi(e,t){let r=[],n=(i,o,s)=>{if(o+s===0)return;r.push({instrumentID:e.instrumentID,exchangeID:e.exchangeID,vtSymbol:e.vtSymbol,direction:i,hedgeFlag:"1",investUnitID:"",tradingDay:e.tradingDay,volume:o+s,todayVolume:o,yesterdayVolume:s,frozen:0,price:e.seedPrice,pnl:0,margin:0,positionCost:0})};return n("long",t.longToday,t.longYesterday),n("short",t.shortToday,t.shortYesterday),r}function yi(e,t,r,n){if(r==="open"){if(t==="long")e.longToday+=n;else e.shortToday+=n;return}let i=t==="short"?"longToday":"shortToday",o=t==="short"?"longYesterday":"shortYesterday";if(r==="closeToday")e[i]=Math.max(0,e[i]-n);else if(r==="closeYesterday")e[o]=Math.max(0,e[o]-n);else{let s=Math.min(e[o],n);e[o]-=s,e[i]=Math.max(0,e[i]-(n-s))}}function L(e){return{...e,identity:{...e.identity}}}class Qe extends Error{method;code;constructor(e,t,r){super(r);this.method=e;this.code=t;this.name="GatewayRpcError"}}var Je=(e)=>e.toUpperCase();function Re(e){return e.map((t)=>({...t,instrumentID:Je(t.instrumentID)}))}class Rt{transport;loginResult=null;connected=!1;queryQueue=Promise.resolve();lastQueryAt=0;minQueryIntervalMs;queryTimeoutMs;normalizeInstrumentID;cleanups;pendingQueries=new Set;disposed=!1;clock;constructor(e,t={}){this.transport=e;this.minQueryIntervalMs=t.minQueryIntervalMs??0,this.queryTimeoutMs=t.queryTimeoutMs??15000,this.normalizeInstrumentID=t.normalizeInstrumentID??((r)=>r),this.clock=t.clock??{now:Date.now,schedule:(r,n)=>{let i=setTimeout(r,n);return()=>clearTimeout(i)}},this.cleanups=[e.on("onDisconnected",()=>{this.connected=!1}),e.on("onReconnecting",()=>{this.connected=!1}),e.on("onReconnected",(r)=>{this.connected=!0,this.loginResult=r.params}),e.onExit(()=>{this.connected=!1})]}async login(e){let t=await this.request("login",e,"\u4EA4\u6613\u767B\u5F55\u5931\u8D25");return this.loginResult=t,this.connected=!0,t}async logout(e,t){await this.request("logout",{broker:e,user:t},"\u767B\u51FA\u5931\u8D25"),this.loginResult=null,this.connected=!1}async changePassword(e){await this.request("changePassword",e,"\u4FEE\u6539\u5BC6\u7801\u5931\u8D25")}async insertOrder(e){let t=this.loginResult,r=await this.request("insertOrder",this.normalizeParams(e),"\u4E0B\u5355\u5931\u8D25"),n=r.tradingDay??t?.tradingDay,i=r.frontID??t?.frontID,o=r.sessionID??t?.sessionID;if(!n||!i||!o)throw Error("\u4E0B\u5355\u6210\u529F\u4F46\u4EA4\u6613\u4F1A\u8BDD\u8EAB\u4EFD\u4E0D\u53EF\u7528");return{tradingDay:n,frontID:i,sessionID:o,orderRef:r.orderRef,requestID:r.requestID}}cancelOrder(e){return this.request("cancelOrder",this.normalizeParams(e),"\u64A4\u5355\u5931\u8D25")}queryAccount(e,t){return this.query(()=>this.requestAndWait("queryAccount",{broker:e,investorId:t},"onQueryAccountResult",(r)=>r.accounts??[],"\u67E5\u8BE2\u8D26\u6237\u5931\u8D25",!0))}queryPositions(e,t,r){return this.query(()=>this.requestAndWait("queryPosition",{broker:e,investorId:t,instrumentID:r?this.normalizeInstrumentID(r):""},"onQueryPositionResult",(n)=>Re(n.positions??[]),"\u67E5\u8BE2\u6301\u4ED3\u5931\u8D25",!0))}queryOrders(e){return this.query(()=>this.requestAndWait("queryOrder",this.normalizeParams(e),"onQueryOrderResult",(t)=>Re(t.orders??[]),"\u67E5\u8BE2\u59D4\u6258\u5931\u8D25",!0))}queryTrades(e){return this.query(()=>this.requestAndWait("queryTrade",this.normalizeParams(e),"onQueryTradeResult",(t)=>Re(t.trades??[]),"\u67E5\u8BE2\u6210\u4EA4\u5931\u8D25",!0))}querySettlementInfo(e){return this.query(()=>this.requestAndWait("querySettlementInfo",e,"onQuerySettlementInfoResult",(t)=>t.settlementInfos??t.settlements??[],"\u67E5\u8BE2\u7ED3\u7B97\u5355\u5931\u8D25",!0))}queryInstrument(e={}){return this.query(()=>this.requestAndWait("queryInstrument",this.normalizeParams(e),"onQueryInstrumentResult",(t)=>Re(t.instruments??[]),"\u67E5\u8BE2\u5408\u7EA6\u89C4\u683C\u5931\u8D25",!0,e.instrumentID||e.exchangeID||e.exchangeInstID||e.productID?this.queryTimeoutMs:60000))}queryCommissionRates(e){return this.query(()=>this.requestAndWait("queryInstrumentCommissionRate",this.normalizeParams(e),"onQueryCommissionRateResult",(t)=>Re(t.commissionRates??[]),"\u67E5\u8BE2\u624B\u7EED\u8D39\u7387\u5931\u8D25",!0,e.instrumentID?this.queryTimeoutMs:60000))}async queryCommissionRate(e){return(await this.queryCommissionRates(e))[0]??null}onOrder(e){return this.transport.on("onOrder",(t)=>{let r=t.params;e({...r,instrumentID:Je(r.instrumentID)})})}onTrade(e){return this.transport.on("onTrade",(t)=>{let r=t.params;e({...r,instrumentID:Je(r.instrumentID)})})}onOrderError(e){return this.transport.on("onOrderError",(t)=>{let r=t.params;e({...r,instrumentID:r.instrumentID?Je(r.instrumentID):void 0})})}onConnection(e,t){return this.transport.on(e,t)}dispose(){this.disposed=!0;for(let e of[...this.pendingQueries])e();for(let e of this.cleanups.splice(0))e()}query(e){let t=this.queryQueue.then(async()=>{if(this.disposed)throw Error("Trader gateway API disposed");let r=Math.max(0,this.minQueryIntervalMs-(this.clock.now()-this.lastQueryAt));if(r>0)await new Promise((n,i)=>{let o=()=>{s(),this.pendingQueries.delete(o),i(Error("Trader gateway API disposed"))},s=this.clock.schedule(()=>{this.pendingQueries.delete(o),n()},r);this.pendingQueries.add(o)});if(this.disposed)throw Error("Trader gateway API disposed");try{return await e()}finally{this.lastQueryAt=this.clock.now()}});return this.queryQueue=t.then(()=>{return},()=>{return}),t}requestAndWait(e,t,r,n,i,o=!1,s=this.queryTimeoutMs){return new Promise((a,d)=>{let c=null,u=null,l=null,f=[],h=()=>{c?.(),c=null,u?.(),u=null,this.pendingQueries.delete(m)},m=()=>{h(),d(Error("Trader gateway API disposed"))};this.pendingQueries.add(m),c=this.clock.schedule(()=>{h(),d(Error(`${e} \u67E5\u8BE2\u8D85\u65F6`))},s);let v=(p)=>{h();let k=p.params;if(k.ok===!1)d(Error(k.error??`${e} \u67E5\u8BE2\u5931\u8D25`));else a(n(p.params))};u=this.transport.on(r,(p)=>{if(!o){v(p);return}if(l===null){f.push(p);return}if(p.params.requestID===l)v(p)}),this.transport.request(e,t).then((p)=>{if(!p.ok){h(),d(Error(p.error?.msg??i));return}if(o){let k=p.data?.requestID;if(!Number.isInteger(k)){h(),d(Error(`${e} \u672A\u8FD4\u56DE\u6709\u6548 requestID`));return}l=k;let q=f.find((M)=>M.params.requestID===l);if(q)v(q)}}).catch((p)=>{h(),d(p)})})}async request(e,t,r){let n=await this.transport.request(e,t);if(!n.ok)throw new Qe(e,n.error?.code,n.error?.msg??r);return n.data}normalizeParams(e){if(!e.instrumentID&&!e.productID)return e;return{...e,...e.instrumentID?{instrumentID:this.normalizeInstrumentID(e.instrumentID)}:{},...e.productID?{productID:this.normalizeInstrumentID(e.productID)}:{}}}}var wi=28800000,Si=Number.MAX_VALUE/2,Br=1e5;class Ct extends Error{}function b(e,t){let r=e?.trim()??"";if(!r)throw Error(`${t} \u4E3A\u7A7A`);return r}function vi(e){if(!/^\d{8}$/.test(e))return!1;let t=Number(e.slice(0,4)),r=Number(e.slice(4,6)),n=Number(e.slice(6,8)),i=new Date(Date.UTC(t,r-1,n));return i.getUTCFullYear()===t&&i.getUTCMonth()===r-1&&i.getUTCDate()===n}function x(e,t){let r=e?.trim()??"";if(!vi(r))throw Error(`${t} \u975E\u6CD5: ${r}`);return r}function N(e,t){if(!Number.isInteger(e)||e<0)throw Error(`${t} \u975E\u6CD5: ${e}`);return e}function Xe(e,t){if(!Number.isInteger(e)||e<-2147483648||e>2147483647)throw Error(`${t} \u975E\u6CD5: ${e}`);return e}function Ze(e,t){if(!Number.isFinite(e))throw Error(`${t} \u975E\u6CD5: ${e}`);return e}function Pt(e,t){if(!Number.isFinite(e)||e<=0||e>=Si)throw Error(`${t} \u975E\u6CD5: ${e}`);return e}function $t(e,t){if(!Number.isInteger(e)||e<=0)throw Error(`${t} \u975E\u6CD5: ${e}`);return e}function Nr(e,t,r){let n=x(e,`${r}\u65E5\u671F`),i=/^(\d{2}):(\d{2}):(\d{2})$/.exec(t);if(!i)throw Error(`${r}\u65F6\u95F4\u975E\u6CD5: ${t}`);let o=Number(i[1]),s=Number(i[2]),a=Number(i[3]);if(o>23||s>59||a>59)throw Error(`${r}\u65F6\u95F4\u975E\u6CD5: ${t}`);return Date.UTC(Number(n.slice(0,4)),Number(n.slice(4,6))-1,Number(n.slice(6,8)),o,s,a)-wi}function Fr(e){if(e==="0")return{side:"buy",direction:"long"};if(e==="1")return{side:"sell",direction:"short"};throw Error(`CTP Direction \u4E0D\u652F\u6301: ${e}`)}function Vr(e){switch(e){case"0":return"open";case"1":return"close";case"3":return"closeToday";case"4":return"closeYesterday";default:throw Error(`CTP OffsetFlag \u4E0D\u652F\u6301: ${e}`)}}function ki(e){if(!["0","1","2","3","4","5","6"].includes(e.orderSubmitStatus))throw Error(`CTP OrderSubmitStatus \u4E0D\u652F\u6301: ${e.orderSubmitStatus}`);if(e.orderSubmitStatus==="4")return"rejected";switch(e.orderStatus){case"0":return"allTraded";case"1":return"partTraded";case"2":case"4":case"5":return"cancelled";case"3":case"b":case"c":return"notTraded";case"a":return"submitting";default:throw Error(`CTP OrderStatus \u4E0D\u652F\u6301: ${e.orderStatus}`)}}function Hr(e,t){return`${e}|${t}`}function Lr(e,t){return`${e}|${t}`}function xt(e){let t=x(e.tradingDay,"\u59D4\u6258 TradingDay"),r=N(e.frontID,"\u59D4\u6258 FrontID"),n=Xe(e.sessionID,"\u59D4\u6258 SessionID"),i=b(e.orderRef,"\u59D4\u6258 OrderRef");return`CTP.${t}.${r}.${n}.${i}`}function _t(e){let t=b(e.instrumentID,"\u59D4\u6258 InstrumentID").toUpperCase(),r=b(e.exchangeID,"\u59D4\u6258 ExchangeID").toUpperCase(),n=x(e.tradingDay,"\u59D4\u6258 TradingDay"),i=b(e.orderRef,"\u59D4\u6258 OrderRef"),o={tradingDay:n,frontID:N(e.frontID,"\u59D4\u6258 FrontID"),sessionID:Xe(e.sessionID,"\u59D4\u6258 SessionID"),orderRef:i,instrumentID:t,exchangeID:r},s=Fr(e.direction),a=$t(e.volumeTotalOriginal,"\u59D4\u6258\u6570\u91CF"),d=N(e.volumeTraded,"\u59D4\u6258\u6210\u4EA4\u6570\u91CF");if(d>a)throw Error(`\u59D4\u6258\u6210\u4EA4\u6570\u91CF\u5927\u4E8E\u59D4\u6258\u6570\u91CF: ${d}/${a}`);return{vtOrderId:xt(o),identity:o,vtSymbol:`${t}.${r}`,instrumentID:t,exchangeID:r,orderSysID:e.orderSysID.trim(),...s,offset:Vr(e.combOffsetFlag),price:Pt(e.limitPrice,"\u59D4\u6258\u4EF7\u683C"),volume:a,traded:d,status:ki(e),statusMessage:e.statusMsg,datetime:Nr(e.insertDate,e.insertTime,"\u59D4\u6258"),...e.traceId?{traceId:e.traceId}:{}}}function Ti(e,t,r){let n=b(e.instrumentID,"\u6210\u4EA4 InstrumentID").toUpperCase(),i=b(e.exchangeID,"\u6210\u4EA4 ExchangeID").toUpperCase(),o=x(e.tradingDay||r,"\u6210\u4EA4 TradingDay"),s=b(e.orderSysID,"\u6210\u4EA4 OrderSysID"),a=b(e.orderRef,"\u6210\u4EA4 OrderRef"),d=t(i,s,o,a);if(!d)throw new Ct(`\u6210\u4EA4\u65E0\u6CD5\u5173\u8054\u59D4\u6258: ${i}/${s}/${a}`);let c=b(e.tradeID,"\u6210\u4EA4 TradeID"),u=Fr(e.direction);return{vtTradeId:`CTP.${o}.${i}.${c}`,vtOrderId:d,vtSymbol:`${n}.${i}`,instrumentID:n,exchangeID:i,orderSysID:s,tradeID:c,...u,offset:Vr(e.offsetFlag),price:Pt(e.price,"\u6210\u4EA4\u4EF7\u683C"),volume:$t(e.volume,"\u6210\u4EA4\u6570\u91CF"),datetime:Nr(e.tradeDate,e.tradeTime,"\u6210\u4EA4"),tradingDay:o,...e.traceId?{traceId:e.traceId}:{}}}function Ei(e,t=new Map){let r=new Map;for(let n of e){let i=b(n.instrumentID,"\u6301\u4ED3 InstrumentID").toUpperCase(),o=b(n.exchangeID,`\u6301\u4ED3 ${i} ExchangeID`).toUpperCase(),s=N(n.position,`\u6301\u4ED3 ${i} Position`),a=N(n.todayPosition,`\u6301\u4ED3 ${i} TodayPosition`);if(N(n.ydPosition,`\u6301\u4ED3 ${i} YdPosition`),a>s)throw Error(`\u6301\u4ED3 ${i} TodayPosition \u5927\u4E8E Position`);if(n.positionDate!=="1"&&n.positionDate!=="2")throw Error(`\u6301\u4ED3 ${i} PositionDate \u4E0D\u652F\u6301: ${n.positionDate}`);if(n.direction==="1"){if(s!==0)throw Error(`\u6682\u4E0D\u652F\u6301\u51C0\u6301\u4ED3\u8BB0\u5F55: ${i}`);continue}let d=n.direction==="2"?"long":n.direction==="3"?"short":(()=>{throw Error(`\u6301\u4ED3 ${i} PosiDirection \u4E0D\u652F\u6301: ${n.direction}`)})(),c=b(n.hedgeFlag,`\u6301\u4ED3 ${i} HedgeFlag`),u=n.investUnitID.trim(),l=x(n.tradingDay,`\u6301\u4ED3 ${i} TradingDay`),f=`${i}|${o}|${d}|${c}|${u}`,h=r.get(f);if(!h)h={instrumentID:i,exchangeID:o,direction:d,hedgeFlag:c,investUnitID:u,tradingDay:l,volume:0,yesterdayVolume:0,frozen:0,pnl:0,margin:0,positionCost:0},r.set(f,h);else if(h.tradingDay!==l)throw Error(`\u6301\u4ED3 ${i} \u6DF7\u5165\u591A\u4E2A TradingDay`);if(h.volume+=s,o==="SHFE"||o==="INE"){if(n.ydPosition>0&&a===0)h.yesterdayVolume+=s}else h.yesterdayVolume+=s-a;h.frozen+=d==="long"?N(n.shortFrozen,`\u6301\u4ED3 ${i} ShortFrozen`):N(n.longFrozen,`\u6301\u4ED3 ${i} LongFrozen`),h.pnl+=Ze(n.positionProfit,`\u6301\u4ED3 ${i} PositionProfit`),h.margin+=Ze(n.useMargin,`\u6301\u4ED3 ${i} UseMargin`),h.positionCost+=Ze(n.positionCost,`\u6301\u4ED3 ${i} PositionCost`)}return[...r.values()].map((n)=>{if(n.yesterdayVolume>n.volume)throw Error(`\u6301\u4ED3 ${n.instrumentID} \u6628\u4ED3\u5927\u4E8E\u603B\u4ED3`);let i=t.get(n.instrumentID)?.volumeMultiple??0,o=n.volume>0&&i>0?n.positionCost/(n.volume*i):0;return{instrumentID:n.instrumentID,exchangeID:n.exchangeID,vtSymbol:`${n.instrumentID}.${n.exchangeID}`,direction:n.direction,hedgeFlag:n.hedgeFlag,investUnitID:n.investUnitID,tradingDay:n.tradingDay,volume:n.volume,todayVolume:n.volume-n.yesterdayVolume,yesterdayVolume:n.yesterdayVolume,frozen:n.frozen,price:o,pnl:n.pnl,margin:n.margin,positionCost:n.positionCost}})}function U(e){return e.status==="submitting"||e.status==="notTraded"||e.status==="partTraded"}class Mt{api;options;contracts=new Map;orders=new Map;orderIdBySys=new Map;orderIdByRef=new Map;ambiguousOrderRefs=new Set;seenTrades=new Set;tradeLedgerDay="";pendingCancels=new Set;unresolvedTrades=[];pendingEvents=[];orderHandlers=new Set;tradeHandlers=new Set;errorHandlers=new Set;phaseHandlers=new Set;reconcileHandlers=new Set;faultHandlers=new Set;cleanups=[];session=null;currentSnapshot=null;recoveryTask=null;recoveryObservedOrderIds=null;_phase="idle";constructor(e,t){this.api=e;this.options=t;b(t.broker,"\u9002\u914D\u5668 BrokerID"),b(t.investorId,"\u9002\u914D\u5668 InvestorID"),this.cleanups.push(e.onOrder((r)=>this.receive({kind:"order",value:r})),e.onTrade((r)=>this.receive({kind:"trade",value:r})),e.onOrderError((r)=>this.receive({kind:"error",value:r})),e.onConnection("onDisconnected",()=>this.beginRecovery()),e.onConnection("onReconnecting",()=>this.beginRecovery()),e.onConnection("onReconnected",(r)=>{try{let n=r.params;this.setSession(n),this.reconcile(!0).catch((i)=>this.fail(i))}catch(n){this.fail(n)}}),e.transport.on("onReconnectFailed",(r)=>{this.fail(Error(String(r.params.error??"CTP \u91CD\u8FDE\u5931\u8D25")))}))}get phase(){return this._phase}get snapshot(){return this.currentSnapshot}async initialize(e){return this.setSession(e),this.reconcile(!1)}registerInstrument(e){this.contracts.set(e.instrumentID.toUpperCase(),e)}async queryPositions(e){this.ensureReady();let t=await this.api.queryPositions(this.options.broker,this.options.investorId,e);return this.normalizePositions(t)}async queryOrders(e){this.ensureReady();let r=(await this.api.queryOrders({broker:this.options.broker,investorId:this.options.investorId,instrumentID:e})).map((i)=>(this.validateOwner(i,"\u59D4\u6258"),this.acceptOrder(_t(i),!1))),n=new Map(this.cachedOrders(e).map((i)=>[i.vtOrderId,i]));for(let i of r)n.set(i.vtOrderId,i);return[...n.values()].sort((i,o)=>o.datetime-i.datetime)}cachedOrders(e){let t=e?.toUpperCase();return[...this.orders.values()].filter((r)=>!t||r.instrumentID===t).sort((r,n)=>n.datetime-r.datetime)}async queryAccounts(){this.ensureReady();let e=await this.api.queryAccount(this.options.broker,this.options.investorId);return this.validateAccounts(e,this.requireSession().tradingDay),e}async queryTrades(e){return this.ensureReady(),(await this.api.queryTrades({broker:this.options.broker,investorId:this.options.investorId,instrumentID:e})).map((r)=>(this.validateOwner(r,"\u6210\u4EA4"),this.normalizeTrade(r)))}submitOrder(e){if(this.ensureReady(),e.direction!=="buy"&&e.direction!=="sell")throw Error(`\u59D4\u6258\u65B9\u5411\u4E0D\u652F\u6301: ${String(e.direction)}`);if(!["open","close","closeToday","closeYesterday"].includes(e.offsetFlag))throw Error(`\u59D4\u6258\u5F00\u5E73\u4E0D\u652F\u6301: ${String(e.offsetFlag)}`);Pt(e.price,"\u59D4\u6258\u4EF7\u683C"),$t(e.volume,"\u59D4\u6258\u6570\u91CF");let t=this.requireSession(),r={tradingDay:x(t.tradingDay,"\u767B\u5F55 TradingDay"),frontID:N(t.frontID,"\u767B\u5F55 FrontID"),sessionID:Xe(t.sessionID,"\u767B\u5F55 SessionID"),orderRef:b(e.orderRef,"\u59D4\u6258 OrderRef"),instrumentID:b(e.instrumentID,"\u59D4\u6258 InstrumentID").toUpperCase(),exchangeID:b(e.exchangeID,"\u59D4\u6258 ExchangeID").toUpperCase()},n=xt(r);return this.bindOrderRef(r.tradingDay,r.orderRef,n),this.orders.set(n,{vtOrderId:n,identity:r,vtSymbol:`${r.instrumentID}.${r.exchangeID}`,instrumentID:r.instrumentID,exchangeID:r.exchangeID,orderSysID:"",side:e.direction,direction:e.direction==="buy"?"long":"short",offset:e.offsetFlag,price:e.price,volume:e.volume,traded:0,status:"submitting",statusMessage:"",datetime:(this.options.now??Date.now)(),...e.traceId?{traceId:e.traceId}:{}}),this.api.insertOrder(e).then((i)=>{if(i.tradingDay!==r.tradingDay||i.frontID!==r.frontID||i.sessionID!==r.sessionID||i.orderRef!==r.orderRef)this.fail(Error(`\u59D4\u6258 ${n} \u7684\u4F1A\u8BDD\u8EAB\u4EFD\u53D1\u751F\u53D8\u5316`))}).catch((i)=>{if(this._phase==="closed")return;let o=i instanceof Error?i.message:String(i);if(this.beginRecovery(),i instanceof Qe)this.emitError({vtOrderId:n,orderRef:r.orderRef,instrumentID:r.instrumentID,operation:"submit",message:o});else{let s=this.orders.get(n);if(s&&U(s)){let a={...s,statusMessage:`\u62A5\u5355\u7ED3\u679C\u5F85\u786E\u8BA4: ${o}`};this.orders.set(n,a),this.emitOrder(a)}}this.reconcileAfterRequestFailure()}),{vtOrderId:n,orderRef:r.orderRef}}cancelOrder(e){this.ensureReady();let t=this.orders.get(e);if(!t)throw Error(`\u627E\u4E0D\u5230\u5B9E\u76D8\u59D4\u6258: ${e}`);if(!U(t))throw Error(`\u59D4\u6258\u5DF2\u7EC8\u7ED3\uFF0C\u4E0D\u80FD\u64A4\u5355: ${e}`);this.pendingCancels.add(e),this.api.cancelOrder({orderRef:t.identity.orderRef,frontID:t.identity.frontID,sessionID:t.identity.sessionID,exchangeID:t.identity.exchangeID,instrumentID:t.identity.instrumentID}).catch((r)=>{if(this._phase==="closed")return;this.emitError({vtOrderId:e,orderRef:t.identity.orderRef,instrumentID:t.instrumentID,operation:"cancel",message:r instanceof Error?r.message:String(r)}),this.reconcileAfterRequestFailure()})}onOrder(e){return this.orderHandlers.add(e),()=>this.orderHandlers.delete(e)}onTrade(e){return this.tradeHandlers.add(e),()=>this.tradeHandlers.delete(e)}onOrderError(e){return this.errorHandlers.add(e),()=>this.errorHandlers.delete(e)}onPhase(e){return this.phaseHandlers.add(e),()=>this.phaseHandlers.delete(e)}onReconciled(e){return this.reconcileHandlers.add(e),()=>this.reconcileHandlers.delete(e)}onFault(e){return this.faultHandlers.add(e),()=>this.faultHandlers.delete(e)}close(){if(this._phase==="closed")return;this.setPhase("closed");for(let e of this.cleanups.splice(0))e();this.pendingEvents.length=0,this.unresolvedTrades.length=0,this.orderHandlers.clear(),this.tradeHandlers.clear(),this.errorHandlers.clear(),this.phaseHandlers.clear(),this.reconcileHandlers.clear(),this.faultHandlers.clear()}beginRecovery(){if(this._phase==="closed"||this._phase==="faulted")return;this.setPhase("recovering")}reconcileAfterRequestFailure(){if(this.beginRecovery(),this.api.connected)this.reconcile(!0).catch((e)=>this.fail(e))}async reconcile(e){if(this.recoveryTask)return this.recoveryTask;let t=this.performReconcile(e);this.recoveryTask=t;try{return await t}finally{if(this.recoveryTask===t)this.recoveryTask=null,this.recoveryObservedOrderIds=null}}async performReconcile(e){let t=this.requireSession();this.recoveryObservedOrderIds=new Set,this.setPhase("recovering");let r=await this.api.queryOrders({broker:this.options.broker,investorId:this.options.investorId}),n=new Map(this.orders);this.orderIdBySys.clear(),this.orderIdByRef.clear(),this.ambiguousOrderRefs.clear();let i=r.map((m)=>(this.validateOwner(m,"\u59D4\u6258"),this.acceptOrder(_t(m),!1))),s=(await this.api.queryTrades({broker:this.options.broker,investorId:this.options.investorId})).map((m)=>(this.validateOwner(m,"\u6210\u4EA4"),this.normalizeTrade(m))),a=[];for(let m of s){if(this.seenTrades.has(m.vtTradeId))continue;a.push(m),this.rememberTrade(m)}let d=await this.api.queryPositions(this.options.broker,this.options.investorId),c=this.normalizePositions(d),u=await this.api.queryAccount(this.options.broker,this.options.investorId);if(this.validateAccounts(u,t.tradingDay),this._phase==="closed"||this._phase==="faulted")throw Error(`CTP \u6062\u590D\u5DF2\u7EC8\u6B62\uFF08${this._phase}\uFF09`);let l={reason:e?"reconnect":"initial",tradingDay:x(t.tradingDay,"\u767B\u5F55 TradingDay"),accounts:u,positions:c,orders:i,trades:s,recoveredAt:(this.options.now??Date.now)()};if(this.currentSnapshot=l,e){for(let m of i){let v=n.get(m.vtOrderId);if(!v||this.orderFingerprint(v)!==this.orderFingerprint(m))this.emitOrder(m)}for(let m of a)this.emitTrade(m)}let f=this.pendingEvents.splice(0);for(let m of f)this.process(m);if(this.flushUnresolvedTrades(),this.unresolvedTrades.length>0)throw Error(`${this.unresolvedTrades.length} \u7B14\u6210\u4EA4\u65E0\u6CD5\u5173\u8054\u59D4\u6258`);let h=this.recoveryObservedOrderIds;for(let[m,v]of n)if(v.identity.tradingDay===l.tradingDay&&U(v)&&this.orders.has(m)&&!h.has(m))throw Error(`\u6D3B\u52A8\u59D4\u6258\u672A\u51FA\u73B0\u5728\u6062\u590D\u5FEB\u7167: ${m}`);l.orders=[...h].map((m)=>this.orders.get(m)).filter((m)=>m!==void 0&&m.identity.tradingDay===l.tradingDay);for(let m of this.reconcileHandlers)m(l);return this.setPhase("ready"),l}receive(e){if(this._phase==="closed"||this._phase==="faulted")return;if(this._phase!=="ready"){if(this.pendingEvents.length>=Br){this.fail(Error("CTP \u6062\u590D\u671F\u95F4\u4E8B\u4EF6\u7F13\u5B58\u6EA2\u51FA"));return}this.pendingEvents.push(e);return}try{this.process(e)}catch(t){this.fail(t)}}process(e){if(e.kind==="order")this.validateOwner(e.value,"\u59D4\u6258"),this.acceptOrder(_t(e.value),!0),this.flushUnresolvedTrades();else if(e.kind==="trade")this.validateOwner(e.value,"\u6210\u4EA4"),this.processTrade(e.value,!0);else this.processError(e.value)}acceptOrder(e,t){let r=this.orders.get(e.vtOrderId),n=e;if(r){if(r.instrumentID!==e.instrumentID||r.exchangeID!==e.exchangeID||r.side!==e.side||r.offset!==e.offset||r.volume!==e.volume)throw Error(`\u59D4\u6258\u8EAB\u4EFD\u5B57\u6BB5\u53D1\u751F\u53D8\u5316: ${e.vtOrderId}`);if(r.orderSysID&&e.orderSysID&&r.orderSysID!==e.orderSysID)throw Error(`\u59D4\u6258 OrderSysID \u53D1\u751F\u53D8\u5316: ${e.vtOrderId}`);if(e.traded<r.traded||this.orderStage(e)<this.orderStage(r))n=r;else n={...e,orderSysID:e.orderSysID||r.orderSysID,...e.traceId||!r.traceId?{}:{traceId:r.traceId}}}if(this.orders.set(n.vtOrderId,n),this.recoveryObservedOrderIds?.add(n.vtOrderId),this.bindOrderRef(n.identity.tradingDay,n.identity.orderRef,n.vtOrderId),n.orderSysID)this.orderIdBySys.set(Lr(n.exchangeID,n.orderSysID),n.vtOrderId);if(!U(n))this.pendingCancels.delete(n.vtOrderId);if(t&&(!r||this.orderFingerprint(r)!==this.orderFingerprint(n)))this.emitOrder(n);return n}normalizeTrade(e){return Ti(e,(t,r,n,i)=>this.orderIdBySys.get(Lr(t,r))??this.orderIdByRef.get(Hr(n,i))??null,this.session?.tradingDay)}bindOrderRef(e,t,r){let n=Hr(e,t);if(this.ambiguousOrderRefs.has(n))return;let i=this.orderIdByRef.get(n);if(i&&i!==r){this.orderIdByRef.delete(n),this.ambiguousOrderRefs.add(n);return}this.orderIdByRef.set(n,r)}normalizePositions(e){let t=x(this.requireSession().tradingDay,"\u767B\u5F55 TradingDay");for(let r of e)if(this.validateOwner(r,"\u6301\u4ED3"),x(r.tradingDay,`\u6301\u4ED3 ${r.instrumentID} TradingDay`)!==t)throw Error(`\u6301\u4ED3 TradingDay \u4E0E\u767B\u5F55\u4F1A\u8BDD\u4E0D\u4E00\u81F4: ${r.tradingDay}/${t}`);return Ei(e,this.contracts)}validateOwner(e,t){let r=b(e.brokerID,`${t} BrokerID`),n=b(e.investorID,`${t} InvestorID`);if(r!==this.options.broker||n!==this.options.investorId)throw Error(`${t}\u8D26\u6237\u8EAB\u4EFD\u4E0D\u5339\u914D: ${r}/${n}`)}validateAccounts(e,t){let r=x(t,"\u767B\u5F55 TradingDay");if(e.length===0)throw Error("\u8D26\u6237\u67E5\u8BE2\u8FD4\u56DE\u7A7A\u7ED3\u679C");for(let n of e){let i=b(n.brokerID,"\u8D26\u6237 BrokerID");if(b(n.accountID,"\u8D26\u6237 AccountID"),i!==this.options.broker)throw Error(`\u8D26\u6237 BrokerID \u4E0D\u5339\u914D: ${i}`);if(x(n.tradingDay,"\u8D26\u6237 TradingDay")!==r)throw Error(`\u8D26\u6237 TradingDay \u4E0E\u767B\u5F55\u4F1A\u8BDD\u4E0D\u4E00\u81F4: ${n.tradingDay}/${r}`);for(let[o,s]of Object.entries({preBalance:n.preBalance,balance:n.balance,available:n.available,currMargin:n.currMargin,frozenMargin:n.frozenMargin,frozenCash:n.frozenCash,frozenCommission:n.frozenCommission,closeProfit:n.closeProfit,positionProfit:n.positionProfit,commission:n.commission,deposit:n.deposit,withdraw:n.withdraw}))Ze(s,`\u8D26\u6237 ${o}`)}}processTrade(e,t){let r;try{r=this.normalizeTrade(e)}catch(n){if(n instanceof Ct){if(this.unresolvedTrades.length>=Br)throw Error("\u65E0\u6CD5\u5173\u8054\u7684\u6210\u4EA4\u7F13\u5B58\u6EA2\u51FA");this.unresolvedTrades.push(e);return}throw n}if(this.seenTrades.has(r.vtTradeId))return;if(this.rememberTrade(r),t)this.emitTrade(r)}flushUnresolvedTrades(){if(this.unresolvedTrades.length===0)return;let e=this.unresolvedTrades.splice(0);for(let t of e)this.processTrade(t,!0)}processError(e){let t=this.requireSession(),r=b(e.orderRef,"\u9519\u8BEF\u56DE\u62A5 OrderRef"),n=xt({...t,orderRef:r}),i=this.pendingCancels.has(n)?"cancel":"submit";this.emitError({vtOrderId:n,orderRef:r,...e.instrumentID?{instrumentID:e.instrumentID.toUpperCase()}:{},operation:i,message:e.errorMsg})}rememberTrade(e){if(!this.tradeLedgerDay||e.tradingDay>this.tradeLedgerDay){if(this.tradeLedgerDay)this.seenTrades.clear();this.tradeLedgerDay=e.tradingDay}this.seenTrades.add(e.vtTradeId)}orderFingerprint(e){return`${e.status}|${e.traded}|${e.orderSysID}|${e.statusMessage}`}orderStage(e){switch(e.status){case"submitting":return 0;case"notTraded":return 1;case"partTraded":return 2;case"allTraded":case"cancelled":case"rejected":return 3}}ensureReady(){if(this._phase!=="ready")throw Error(`CTP \u4EA4\u6613\u9002\u914D\u5668\u672A\u5C31\u7EEA\uFF08${this._phase}\uFF09`)}requireSession(){if(!this.session)throw Error("CTP \u4EA4\u6613\u4F1A\u8BDD\u4E0D\u53EF\u7528");return this.session}setSession(e){x(e.tradingDay,"\u767B\u5F55 TradingDay"),N(e.frontID,"\u767B\u5F55 FrontID"),Xe(e.sessionID,"\u767B\u5F55 SessionID"),this.session=e}setPhase(e){if(this._phase===e)return;if(this._phase==="closed")return;if(this._phase==="faulted"&&e!=="closed")return;this._phase=e;for(let t of this.phaseHandlers)t(e)}fail(e){if(this._phase==="closed"||this._phase==="faulted")return;let t=e instanceof Error?e.message:String(e);this.setPhase("faulted");for(let r of this.faultHandlers)r(t)}emitOrder(e){for(let t of this.orderHandlers)t(e)}emitTrade(e){for(let t of this.tradeHandlers)t(e)}emitError(e){this.pendingCancels.delete(e.vtOrderId);let t=this.orders.get(e.vtOrderId);if(t&&U(t)){let r=e.operation==="submit"?{...t,status:"rejected",statusMessage:e.message}:{...t,statusMessage:`\u64A4\u5355\u5931\u8D25: ${e.message}`};this.orders.set(e.vtOrderId,r),this.emitOrder(r)}for(let r of this.errorHandlers)r(e)}}var At={queryOrder:{event:"onQueryOrderResult",field:"orders"},queryTrade:{event:"onQueryTradeResult",field:"trades"},queryPosition:{event:"onQueryPositionResult",field:"positions"},queryAccount:{event:"onQueryAccountResult",field:"accounts"}};class qr{session;clock;trace;requests=[];pending=new Map;handlers=new Map;exits=new Set;held=new Set;waiters=new Set;state;constructor(e,t,r,n={}){this.session=e;this.clock=t;this.trace=r;this.state=structuredClone(n)}request(e,t,r=30000){let n={id:this.requests.length+1,method:e,params:structuredClone(t),now:this.clock.now};return this.requests.push(n),this.trace.record("broker",{operation:"rpc",request:n}),new Promise((i,o)=>{let s=this.clock.schedule(()=>{this.pending.delete(n.id),o(Error(`Test gateway ${e} request timed out`))},r);this.pending.set(n.id,{request:n,resolve:i,reject:o,cancelTimeout:s});for(let a of this.waiters)if(a.method===e)this.waiters.delete(a),a.resolve(structuredClone(n));if(!this.held.has(e))this.complete(n.id)})}pendingRequests(){return[...this.pending.values()].map((e)=>structuredClone(e.request))}waitForRequest(e){let t=this.pendingRequests().find((r)=>r.method===e);return t?Promise.resolve(t):new Promise((r,n)=>this.waiters.add({method:e,resolve:r,reject:n}))}setSnapshot(e){this.state={...this.state,...structuredClone(e)}}hold(e){this.held.add(e)}resume(e){this.held.delete(e);for(let{request:t}of[...this.pending.values()])if(t.method===e)this.complete(t.id)}reply(e,t){let r=this.pending.get(e);if(!r)throw Error(`Pending RPC does not exist: ${e}`);this.pending.delete(e),r.cancelTimeout();let n=r.request.method==="insertOrder"?{...this.session,orderRef:r.request.params.orderRef,requestID:e}:{requestID:e},i={id:e,...t??{ok:!0,data:n}};this.trace.record("broker",{operation:"rpcReply",response:i}),r.resolve(i)}completeQuery(e,t){let r=this.requests.find((o)=>o.id===e);if(!r||!Object.hasOwn(At,r.method))throw Error(`Query RPC does not exist: ${e}`);let n=At[r.method];if(this.pending.has(e))this.reply(e);let i=n.field==="accounts"?[{brokerID:"test",accountID:"test",tradingDay:this.session.tradingDay,preBalance:1e6,balance:1e6,available:1e6,currMargin:0,frozenMargin:0,frozenCash:0,frozenCommission:0,closeProfit:0,positionProfit:0,commission:0,deposit:0,withdraw:0}]:[];this.push(n.event,{requestID:e,[n.field]:structuredClone(t??this.state[n.field]??i)})}push(e,t){let r={method:e,params:structuredClone(t)};this.trace.record("broker",{operation:"push",message:r});for(let n of this.handlers.get(e)??[])n(structuredClone(r))}on(e,t){let r=this.handlers.get(e)??new Set;return r.add(t),this.handlers.set(e,r),()=>this.off(e,t)}off(e,t){this.handlers.get(e)?.delete(t)}onExit(e){return this.exits.add(e),()=>this.exits.delete(e)}resetClient(){let e=Error("Test gateway client closed");for(let t of this.pending.values())t.cancelTimeout(),t.reject(e);for(let t of this.waiters)t.reject(e);this.pending.clear(),this.waiters.clear(),this.held.clear()}complete(e){let t=this.pending.get(e).request;if(Object.hasOwn(At,t.method))this.completeQuery(e);else this.reply(e)}}class _e{clock;trace;observations;gateway;fixture;transport;instrument;api;adapter;orderRef;orders=new Set;trades=new Set;errors=new Set;reconciled=new Set;faults=new Set;constructor(e,t,r,n,i){this.clock=t;this.trace=r;this.observations=i;this.fixture=e,this.instrument=Ye(e);let o={tradingDay:e.tradingDay,frontID:1,sessionID:1,...n.gatewaySession};this.orderRef=BigInt(o.maxOrderRef??"0"),this.transport=new qr(o,t,r,n.gatewayState);let s=this;this.gateway={get requests(){return structuredClone(s.transport.requests)},pendingRequests:()=>this.transport.pendingRequests(),waitForRequest:(a)=>this.transport.waitForRequest(a),setSnapshot:(a)=>this.transport.setSnapshot(a),hold:(a)=>this.transport.hold(a),resume:(a)=>this.transport.resume(a),reply:(a,d)=>this.transport.reply(a,d),completeQuery:(a,d)=>this.transport.completeQuery(a,d),push:(a,d)=>this.transport.push(a,d),disconnect:()=>this.transport.push("onDisconnected",{}),reconnect:(a)=>this.reconnect(a),queryOrders:()=>this.requireAdapter().queryOrders()}}get phase(){if(this.adapter?.phase==="ready")return"running";return this.adapter?.phase==="faulted"||this.adapter?.phase==="closed"?"faulted":"recovering"}async connect(){if(this.adapter)this.close();this.api=new Rt(this.transport,{clock:{now:()=>this.clock.now,schedule:this.clock.schedule}}),this.api.loginResult=this.transport.session,this.api.connected=!0,this.adapter=new Mt(this.api,{broker:"test",investorId:"test",now:()=>this.clock.now}),this.adapter.registerInstrument(this.instrument),this.adapter.onOrder((e)=>{for(let t of this.orders)t(e)}),this.adapter.onTrade((e)=>{for(let t of this.trades)t(e)}),this.adapter.onOrderError((e)=>{for(let t of this.errors)t(e)}),this.adapter.onReconciled((e)=>{for(let t of this.reconciled)t(e)}),this.adapter.onFault((e)=>{for(let t of this.faults)t(e)}),await this.adapter.initialize(this.transport.session)}close(){this.adapter?.close(),this.api?.dispose(),this.transport.resetClient()}async prepareInstrument(e,t){if(e!==this.instrument.instrumentID||t!==this.instrument.exchangeID)throw Error("Test instrument mismatch");return this.instrument}async prepareCommission(e,t){if(e!==this.instrument.instrumentID||t!==this.instrument.exchangeID)throw Error("Test commission mismatch")}getInstrument(e){return e===this.instrument.instrumentID?this.instrument:null}getCommission(e,t){if(e!==this.instrument.instrumentID||t!==this.instrument.exchangeID)return null;return Ge(this.fixture)}async queryPositions(e){if(this.phase!=="running")return{phase:this.phase,positions:[]};let t=await this.requireAdapter().queryPositions(e);return{phase:this.phase,positions:t}}queryOrders(e){return this.requireAdapter().queryOrders(e)}submitOrder(e){let t=String(++this.orderRef);if(t.length>12)throw Error("CTP OrderRef \u5DF2\u8017\u5C3D\uFF0C\u8BF7\u91CD\u542F\u4EA4\u6613\u4F1A\u8BDD");let r=this.requireAdapter().submitOrder({instrumentID:e.instrumentID,direction:e.direction,offsetFlag:e.offsetFlag,price:e.price,volume:e.volume,exchangeID:this.instrument.exchangeID,orderRef:t,traceId:`vibek:${e.strategyId}:${t}`}),n={vtOrderId:r.vtOrderId,direction:e.direction==="buy"?"long":"short",offset:e.offsetFlag,price:e.price,volume:e.volume};return this.observations.submissions.push(n),this.trace.record("submission",n),r}cancelOrder(e){this.observations.cancelRequests.push(e),this.trace.record("cancel",{vtOrderId:e}),this.requireAdapter().cancelOrder(e)}onOrder(e){return this.orders.add(e),()=>this.orders.delete(e)}onTrade(e){return this.trades.add(e),()=>this.trades.delete(e)}onOrderError(e){return this.errors.add(e),()=>this.errors.delete(e)}onReconciled(e){return this.reconciled.add(e),()=>this.reconciled.delete(e)}onFault(e){return this.faults.add(e),()=>this.faults.delete(e)}reconnect(e={}){let t=this.requireAdapter();return Object.assign(this.transport.session,e),new Promise((r,n)=>{let i=t.onPhase((o)=>{if(o==="ready")i(),r();else if(o==="faulted"||o==="closed")i(),n(Error(`CTP reconciliation ${o}`))});this.transport.push("onDisconnected",{}),this.transport.push("onReconnected",{...this.transport.session})})}requireAdapter(){if(!this.adapter)throw Error("CTP test adapter is not initialized");return this.adapter}}class Bt{fixture;history;now;generate;constructor(e,t,r,n){this.fixture=e;this.history=t;this.now=r;this.generate=n}async getBars(e,t){if(this.history.length===0&&this.generate)this.history.push(...Ii(e,t,this.fixture,this.now));Ie(this.history,this.now,e);let r=ke(this.history,t.timeframe,t.count);if(r.selectedCount!==t.count)throw Error(`\u6D4B\u8BD5\u9884\u70ED\u6570\u636E\u4E0D\u8DB3\uFF1A\u9700\u8981 ${t.count} \u6839 ${t.timeframe}\uFF0C\u4EC5\u6709 ${r.availableCount} \u6839`);return r.bars.map((n)=>({...n}))}destroy(){}}function Ii(e,t,r,n){if(t.count>1e5)throw Error(`\u6D4B\u8BD5\u9884\u70ED\u6570\u636E\u8BF7\u6C42\u8FC7\u5927: ${t.count}`);if(t.timeframe==="1d"){let d=Math.floor(n/60000)*60;return Array.from({length:t.count},(c,u)=>{let l=d-(t.count-u)*86400;return H(r,{code:e,close:r.seedPrice+u*r.priceTick,barTime:l,tradingDay:Ot(l*1000),boundaryReason:"sessionEnd"},u)})}let i=se[t.timeframe]/60,o=t.count*i;if(o>1e5)throw Error(`\u6D4B\u8BD5\u9884\u70ED\u6570\u636E\u8BF7\u6C42\u8FC7\u5927: ${o} \u6839 1min Bar`);let a=Math.floor(n/(i*60000))*i*60-o*60;return Array.from({length:o},(d,c)=>H(r,{code:e,close:r.seedPrice+c*r.priceTick,barTime:a+c*60,tradingDay:Tt(a+c*60)},c))}import{mkdtempSync as Uo,rmSync as jo}from"fs";import{tmpdir as Go}from"os";import{join as Ko}from"path";class Ht{now;tasks=new Map;sequence=0;constructor(e){this.now=e}schedule=(e,t)=>{if(!Number.isSafeInteger(t)||t<0||!Number.isSafeInteger(this.now+t))throw Error("Scheduled delay must be a nonnegative safe integer");let r=++this.sequence;return this.tasks.set(r,{at:this.now+t,callback:e}),()=>{this.tasks.delete(r)}};advanceTo(e,t){if(!Number.isSafeInteger(e)||e<this.now)throw Error("\u6D4B\u8BD5\u65F6\u95F4\u4E0D\u80FD\u56DE\u9000\u6216\u8D85\u8FC7\u5B89\u5168\u6574\u6570\u8303\u56F4");let r=(Math.floor(this.now/1000)+1)*1000,n=0;while(!0){let i=1/0;for(let s of this.tasks.values())i=Math.min(i,s.at);let o=Math.min(i,t?r:1/0);if(o>e)break;if(this.now=o,t&&o===r)t(),r+=1000;for(let[s,a]of this.tasks)if(a.at<=this.now){if(this.tasks.delete(s),++n>1e5)throw Error("Test scheduler exceeded 100000 callbacks in one advance");a.callback()}}this.now=e}}import{AsyncLocalStorage as Di}from"async_hooks";class Lt{now;entries=[];context=new Di;constructor(e){this.now=e}get events(){return this.entries.slice()}close(){this.context.disable()}record(e,t){let r=this.entries.length+1;return this.entries.push(zr({sequence:r,now:this.now(),cause:this.context.getStore()??null,type:e,value:structuredClone(t)})),r}input(e,t){let r=this.record("input",e);return this.context.run(r,()=>{let n=t();if(n instanceof Promise)return n.then((i)=>(this.record("completion",{input:r,status:"fulfilled"}),i),(i)=>{throw this.record("completion",{input:r,status:"rejected",error:i instanceof Error?i.message:String(i)}),i});return n})}bind(e){let t=this.context.getStore()??null;return()=>this.context.run(t,e)}}function zr(e,t=new Set){if(e&&typeof e==="object"){if(t.has(e))return e;t.add(e);for(let r of Object.values(e))zr(r,t);Object.freeze(e)}return e}import{existsSync as ln}from"fs";import{join as I}from"path";import{existsSync as xi,mkdirSync as ba,readFileSync as Ci,renameSync as wa,unlinkSync as Sa,writeFileSync as va}from"fs";import{homedir as Ce}from"os";import{dirname as Pi,isAbsolute as $i,join as C,resolve as zt}from"path";import{isIP as Oi}from"net";function Nt(e){if(e.trim()==="")return[];let t=[],r=new Set,n=e.split(",").map((i)=>i.trim()).filter(Boolean);for(let[i,o]of n.entries()){let s=o.indexOf("@");if(s<=0||s!==o.lastIndexOf("@"))throw Error(`ctp.front_candidates \u7B2C ${i+1} \u9879\u683C\u5F0F\u9519\u8BEF\uFF0C\u5E94\u4E3A \u63CF\u8FF0@IP`);let a=o.slice(0,s).trim(),d=o.slice(s+1).trim();if(a==="")throw Error(`ctp.front_candidates \u7B2C ${i+1} \u9879\u7F3A\u5C11\u7EBF\u8DEF\u63CF\u8FF0`);if(Oi(d)===0)throw Error(`ctp.front_candidates \u7B2C ${i+1} \u9879\u4E0D\u662F\u6709\u6548 IP: ${d||"(\u7A7A)"}`);if(r.has(d))throw Error(`ctp.front_candidates \u5305\u542B\u91CD\u590D IP: ${d}`);r.add(d),t.push({description:a,ip:d})}return t}var xe=[{start:"09:00:00",end:"10:15:00",boundaryReason:"break",acceptsPreOpenTick:!0},{start:"10:30:00",end:"11:30:00",boundaryReason:"break"},{start:"13:30:00",end:"15:00:00",boundaryReason:"sessionEnd"}],Ri=[{start:"09:30:00",end:"11:30:00",boundaryReason:"break",acceptsPreOpenTick:!0},{start:"13:00:00",end:"15:00:00",boundaryReason:"sessionEnd"}],_i=[{start:"09:30:00",end:"11:30:00",boundaryReason:"break",acceptsPreOpenTick:!0},{start:"13:00:00",end:"15:15:00",boundaryReason:"sessionEnd"}];function Ft(e){return[{start:"21:00:00",end:e,boundaryReason:"break",acceptsPreOpenTick:!0},...xe]}var et=Ft("23:00:00"),Ur=Ft("01:00:00"),jr=Ft("02:30:00"),Vt=new Map;function D(e,t,r){for(let n of t){let i=n.toUpperCase();Vt.set(`${e}.${i}`,{exchangeID:e,productID:i,segments:r})}}D("CFFEX",["IF","IH","IC","IM"],Ri);D("CFFEX",["T","TF","TS","TL"],_i);D("SHFE",["AU","AG"],jr);D("SHFE",["CU","AL","ZN","PB","NI","SN","SS","AO","AD"],Ur);D("SHFE",["RB","WR","HC","RU","BU","FU","SP","BR"],et);D("INE",["SC"],jr);D("INE",["BC"],Ur);D("INE",["NR","LU"],et);D("INE",["EC"],xe);D("DCE",["A","B","C","CS","M","Y","P","L","L_F","V","V_F","PP","PP_F","J","JM","I","EG","EB","PG"],et);D("DCE",["JD","FB","BB","RR","LH","LG","BZ"],xe);D("CZCE",["CF","SR","TA","OI","MA","FG","RM","ZC","CY","SA","PF","PX","SH","PR"],et);D("CZCE",["WH","PM","RI","RS","JR","LR","SF","SM","AP","CJ","UR","PK"],xe);D("GFEX",["SI","LC","PS","PT","PD"],xe);function Gr(e,t){return Vt.get(`${e.toUpperCase()}.${t.toUpperCase()}`)??null}function tt(e,t){let r=Gr(e,t);if(!r)throw Error(`\u4EA7\u54C1\u65F6\u6BB5\u76EE\u5F55\u4E0D\u5305\u542B ${t.toUpperCase()}.${e.toUpperCase()}`);return r}function qt(){return[...Vt.values()]}var Mi="VIBEK_CONFIG_PATH",Ai="VIBEK_PROCESS_ACCOUNT",Ut=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,Kr=["trace","debug","info","warn","error"],Yr=["realtime","replay"];class ee extends Error{path;diagnostics;constructor(e,t){super(`\u914D\u7F6E\u6587\u4EF6\u65E0\u6548: ${e}
|
|
3
3
|
${t.map((r)=>` - ${r}`).join(`
|
|
4
|
-
`)}`);this.path=e;this.diagnostics=t;this.name="ConfigValidationError"}}class Qr{errors=[];table(e,t){if(Bi(e))return e;return this.errors.push(`${t} \u5FC5\u987B\u662F TOML \u8868`),{}}keys(e,t,r){let n=new Set(t);for(let i of Object.keys(e))if(!n.has(i))this.errors.push(`\u672A\u77E5\u914D\u7F6E\u9879 ${r?`${r}.`:""}${i}`)}string(e,t,r,n={}){let i=e[t],o=`${r}.${t}`;if(i===void 0){if(n.required)this.errors.push(`\u7F3A\u5C11\u914D\u7F6E\u9879 ${o}`);return}if(typeof i!=="string"){this.errors.push(`${o} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);return}if(n.nonEmpty&&i.trim()===""){this.errors.push(`${o} \u4E0D\u80FD\u4E3A\u7A7A`);return}return i}boolean(e,t,r){let n=e[t];if(n===void 0)return;if(typeof n!=="boolean"){this.errors.push(`${r}.${t} \u5FC5\u987B\u662F\u5E03\u5C14\u503C`);return}return n}integer(e,t,r,n,i){let o=e[t];if(o===void 0)return;if(!Number.isInteger(o)||o<n||o>i){this.errors.push(`${r}.${t} \u5FC5\u987B\u662F ${n}..${i} \u7684\u6574\u6570`);return}return o}strings(e,t,r){let n=e[t];if(n===void 0)return;if(!Array.isArray(n)||n.some((i)=>typeof i!=="string")){this.errors.push(`${r}.${t} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4`);return}return[...n]}}function Bi(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function j(e,t,r,n=""){let i=t[r];if(i===void 0)return;return e.table(i,n?`${n}.${r}`:r)}function rt(e,t,r){if(e!==void 0&&!/^([01]\d|2[0-3]):[0-5]\d$/.test(e))r.push(`${t} \u5FC5\u987B\u4F7F\u7528 HH:MM\uFF0824 \u5C0F\u65F6\uFF09\u683C\u5F0F`)}function Wr(e,t,r){if(e===void 0)return;try{let n=new URL(e),i=Number(n.port);if(n.protocol!=="tcp:"||!n.hostname||!Number.isInteger(i)||i<1||i>65535)throw Error("invalid")}catch{r.push(`${t} \u5FC5\u987B\u662F tcp://host:port`)}}var Jr=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;function Hi(e,t){if(t===void 0)return;let r="account.timer_hooks";if(!Array.isArray(t))return e.errors.push(`${r} \u5FC5\u987B\u662F TOML \u8868\u6570\u7EC4`),[];let n=[],i=new Set;for(let[o,s]of t.entries()){let a=`${r}[${o}]`,d=e.table(s,a);e.keys(d,["name","cron","timezone","enabled"],a);let c=e.string(d,"name",a,{required:!0,nonEmpty:!0}),u=e.string(d,"cron",a,{required:!0,nonEmpty:!0}),l=e.string(d,"timezone",a,{required:!0,nonEmpty:!0}),f=e.boolean(d,"enabled",a);if(c!==void 0)if(!Jr.test(c))e.errors.push(`${a}.name \u5FC5\u987B\u5339\u914D ${Jr.source}`);else if(i.has(c))e.errors.push(`${r} \u4E0D\u5141\u8BB8\u91CD\u590D\u540D\u79F0: ${c}`);else i.add(c);let h=u?.trim().replace(/\s+/g," "),m=l?.trim(),v=!1;if(m)try{Bun.cron.parse("0 0 * * *",Date.now(),{tz:m}),v=!0}catch{e.errors.push(`${a}.timezone \u4E0D\u662F\u6709\u6548\u7684 IANA \u65F6\u533A`)}if(h!==void 0&&h.split(" ").length!==5)e.errors.push(`${a}.cron \u5FC5\u987B\u662F 5 \u6BB5 cron \u8868\u8FBE\u5F0F`);else if(h!==void 0&&v)try{if(Bun.cron.parse(h,Date.now(),{tz:m})===null)e.errors.push(`${a}.cron \u5728\u672A\u6765 8 \u5E74\u5185\u6CA1\u6709\u89E6\u53D1\u65F6\u95F4`)}catch{e.errors.push(`${a}.cron \u4E0D\u662F\u6709\u6548\u7684 cron \u8868\u8FBE\u5F0F`)}if(c!==void 0&&h!==void 0&&m!==void 0)n.push({name:c,cron:h,timezone:m,...f===void 0?{}:{enabled:f}})}return n}var Zr=new Set(["CFFEX","SHFE","CZCE","DCE","INE","GFEX"]);function Li(e){let t=e.trim(),r=/^([A-Za-z][A-Za-z0-9]*)\.([A-Za-z]+)$/.exec(t),n=r??/^([A-Za-z][A-Za-z0-9]*)$/.exec(t);if(!n||!/\d/.test(n[1]))throw Error(`\u5408\u7EA6 ${JSON.stringify(e)} \u975E\u6CD5\uFF0C\u5E94\u4F7F\u7528 CODE \u6216 CODE.EXCHANGE`);let i=n[1].toUpperCase(),o=Ni(i),s=r?n[2].toUpperCase():o;if(!s||!Zr.has(s)){if(r)throw Error(`\u5408\u7EA6\u4EA4\u6613\u6240\u975E\u6CD5: ${e}`);throw Error(`\u65E0\u6CD5\u63A8\u65AD\u5408\u7EA6 ${i} \u7684\u4EA4\u6613\u6240\uFF0C\u8BF7\u4F7F\u7528 ${i}.EXCHANGE`)}if(r&&o&&o!==s)throw Error(`\u5408\u7EA6 ${i} \u5E94\u4F7F\u7528\u4EA4\u6613\u6240 ${o}\uFF0C\u800C\u4E0D\u662F ${s}`);return{instrumentID:i,exchangeID:s,vtSymbol:`${i}.${s}`}}function Ni(e){let t=/^[A-Z]+/.exec(e)?.[0];if(!t)return null;let r=new Set;for(let n of Vt())if((n.productID.endsWith("_F")?n.productID.slice(0,-2):n.productID)===t)r.add(n.exchangeID);return r.size===1?[...r][0]:null}function jt(e){let t=[],r=new Map;for(let n of e){let i=n.trim(),o=/^([A-Za-z][A-Za-z0-9]*)\.([A-Za-z]+)$/.exec(i);if(!o||!/\d/.test(o[1]))throw Error(`market.record_instruments \u9879 ${JSON.stringify(n)} \u975E\u6CD5\uFF0C\u5E94\u4F7F\u7528 CODE.EXCHANGE`);let s=o[1].toUpperCase(),a=o[2].toUpperCase();if(!Zr.has(a))throw Error(`market.record_instruments \u4EA4\u6613\u6240\u975E\u6CD5: ${n}`);let d=r.get(s);if(d&&d!==a)throw Error(`market.record_instruments \u5408\u7EA6\u4EA4\u6613\u6240\u51B2\u7A81: ${s}.${d} / ${s}.${a}`);if(d)continue;r.set(s,a),t.push({instrumentID:s,exchangeID:a,vtSymbol:`${s}.${a}`})}return t}function Fi(e,t){let r=new Set(e.map((i)=>i.vtSymbol)),n=[...new Set(t.filter((i)=>!r.has(i.vtSymbol)).map((i)=>i.vtSymbol))];if(n.length>0)throw Error(`account.market.bar_hooks \u4F7F\u7528\u4E86\u672A\u5F55\u5236\u8BA2\u9605\u7684\u5408\u7EA6: ${n.join(", ")}`)}function nt(e,t){let r=[],n=new Set;for(let[i,o]of e.entries()){let s;try{s=Li(o.instrument)}catch(d){throw Error(`account.market.bar_hooks[${i}].instrument: ${d instanceof Error?d.message:String(d)}`)}if(!Z(o.timeframe))throw Error(`account.market.bar_hooks[${i}].timeframe \u5FC5\u987B\u662F ${ve}`);let a=`${s.vtSymbol}/${o.timeframe}`;if(n.has(a))throw Error(`account.market.bar_hooks \u4E0D\u5141\u8BB8\u91CD\u590D\u7EC4\u5408: ${a}`);n.add(a),r.push({...s,timeframe:o.timeframe})}return Fi(t,r),r}function Gt(e,t){let r=e?.trim()??"",n=t?.trim()??"";if(!r||!n)return"";let i=`${r}-${n}`;return Ut.test(i)?i:""}function qi(e,t){let n=e.table(t,"account");e.keys(n,["broker_id","user","ctp","timer_hooks","logging","market","reference_data","trading","schedule"],"account");let i=e.string(n,"broker_id","account",{required:!0,nonEmpty:!0})??"",o=e.string(n,"user","account",{required:!0,nonEmpty:!0})??"";if(!Gt(i,o)&&i&&o)e.errors.push(`${"account"}.broker_id \u4E0E ${"account"}.user \u65E0\u6CD5\u7EC4\u6210\u5408\u6CD5\u8D26\u53F7\u540D`);let a=e.table(n.ctp,"account.ctp");e.keys(a,["market_front","trader_front","front_candidates","password","app_id","auth_code","production_mode"],"account.ctp");let d=e.string(a,"market_front","account.ctp",{required:!0,nonEmpty:!0})??"",c=e.string(a,"trader_front","account.ctp",{required:!0,nonEmpty:!0})??"",u=e.strings(a,"front_candidates","account.ctp"),l=e.string(a,"password","account.ctp"),f=e.string(a,"app_id","account.ctp",{required:!0,nonEmpty:!0})??"",h=e.string(a,"auth_code","account.ctp",{required:!0,nonEmpty:!0})??"",m=e.boolean(a,"production_mode","account.ctp");if(Wr(d||void 0,"account.ctp.market_front",e.errors),Wr(c||void 0,"account.ctp.trader_front",e.errors),u)try{Nt(u.join(","))}catch(S){e.errors.push(`account.ctp.front_candidates: ${S instanceof Error?S.message:String(S)}`)}let v=Hi(e,n.timer_hooks),p=j(e,n,"logging","account");if(p){e.keys(p,["level"],"account.logging");let S=e.string(p,"level","account.logging");if(S!==void 0&&!Kr.includes(S))e.errors.push(`${"account"}.logging.level \u5FC5\u987B\u662F ${Kr.join(" | ")}`)}let k=j(e,n,"market","account");if(k){e.keys(k,["db_path","record_instruments","bar_hooks","tick_time_mode","max_future_tick_ms"],"account.market"),e.string(k,"db_path","account.market");let S=e.strings(k,"record_instruments","account.market"),z=k.bar_hooks,oe=[];if(z!==void 0)if(!Array.isArray(z))e.errors.push(`${"account"}.market.bar_hooks \u5FC5\u987B\u662F TOML \u8868\u6570\u7EC4`);else for(let[A,Zn]of z.entries()){let be=`account.market.bar_hooks[${A}]`,bt=e.table(Zn,be);e.keys(bt,["instrument","timeframe"],be);let yr=e.string(bt,"instrument",be,{required:!0,nonEmpty:!0}),Fe=e.string(bt,"timeframe",be,{required:!0});if(yr!==void 0&&Z(Fe))oe.push({instrument:yr,timeframe:Fe});else if(Fe!==void 0&&!Z(Fe))e.errors.push(`${be}.timeframe \u5FC5\u987B\u662F ${ve}`)}let Ne=e.string(k,"tick_time_mode","account.market");if(Ne!==void 0&&!Yr.includes(Ne))e.errors.push(`${"account"}.market.tick_time_mode \u5FC5\u987B\u662F ${Yr.join(" | ")}`);if(e.integer(k,"max_future_tick_ms","account.market",0,2592000000),S)try{let A=jt(S);nt(oe,A)}catch(A){e.errors.push(A instanceof Error?A.message:String(A))}else if(oe.length>0)try{nt(oe,[])}catch(A){e.errors.push(A instanceof Error?A.message:String(A))}}let V=j(e,n,"reference_data","account");if(V)e.keys(V,["contracts_path","commissions_path"],"account.reference_data"),e.string(V,"contracts_path","account.reference_data"),e.string(V,"commissions_path","account.reference_data");let M=j(e,n,"trading","account");if(M){e.keys(M,["max_order_volume","max_position","max_active_open_orders","daily_limits","duplicate_orders"],"account.trading"),e.integer(M,"max_order_volume","account.trading",1,1e4),e.integer(M,"max_position","account.trading",1,1e5),e.integer(M,"max_active_open_orders","account.trading",1,1000);let S=j(e,M,"daily_limits","account.trading");if(S)e.keys(S,["total_order_limit","total_cancel_limit","total_trade_limit","instrument_order_limit","instrument_cancel_limit","instrument_trade_limit"],"account.trading.daily_limits"),e.integer(S,"total_order_limit","account.trading.daily_limits",1,1e7),e.integer(S,"total_cancel_limit","account.trading.daily_limits",1,1e7),e.integer(S,"total_trade_limit","account.trading.daily_limits",1,1e7),e.integer(S,"instrument_order_limit","account.trading.daily_limits",1,1e7),e.integer(S,"instrument_cancel_limit","account.trading.daily_limits",1,1e7),e.integer(S,"instrument_trade_limit","account.trading.daily_limits",1,1e7);let z=j(e,M,"duplicate_orders","account.trading");if(z)e.keys(z,["duplicate_order_limit"],"account.trading.duplicate_orders"),e.integer(z,"duplicate_order_limit","account.trading.duplicate_orders",2,1000)}let R=j(e,n,"schedule","account");if(R){e.keys(R,["enabled","day_start","day_stop","night_start","night_stop","max_start_attempts","retry_base_ms","tick_ms"],"account.schedule"),e.boolean(R,"enabled","account.schedule");let S=e.string(R,"day_start","account.schedule"),z=e.string(R,"day_stop","account.schedule"),oe=e.string(R,"night_start","account.schedule"),Ne=e.string(R,"night_stop","account.schedule");rt(S,"account.schedule.day_start",e.errors),rt(z,"account.schedule.day_stop",e.errors),rt(oe,"account.schedule.night_start",e.errors),rt(Ne,"account.schedule.night_stop",e.errors),e.integer(R,"max_start_attempts","account.schedule",1,10),e.integer(R,"retry_base_ms","account.schedule",5000,600000),e.integer(R,"tick_ms","account.schedule",5000,120000)}return{identity:{broker_id:i,user:o},ctp:{market_front:d,trader_front:c,...u===void 0?{}:{front_candidates:u},...l===void 0?{}:{password:l},app_id:f,auth_code:h,...m===void 0?{}:{production_mode:m}},...v===void 0?{}:{timer_hooks:v},...p===void 0?{}:{logging:p},...k===void 0?{}:{market:k},...V===void 0?{}:{reference_data:V},...M===void 0?{}:{trading:M},...R===void 0?{}:{schedule:R}}}function Vi(e,t=F()){let r=new Qr,n=r.table(e,"config");r.keys(n,["account","storage","server"],"");let i=j(r,n,"storage");if(i)r.keys(i,["data_root"],"storage"),r.string(i,"data_root","storage",{nonEmpty:!0});let o=j(r,n,"server");if(o){r.keys(o,["daemon_port","webui_port"],"server");let l=r.integer(o,"daemon_port","server",1,65535),f=r.integer(o,"webui_port","server",1,65535);if(l!==void 0&&l===f)r.errors.push("server.daemon_port \u4E0E server.webui_port \u4E0D\u80FD\u76F8\u540C")}let s=n.account,a=s===void 0?void 0:qi(r,s),d=a?.reference_data?.contracts_path?.trim(),c=a?.reference_data?.commissions_path?.trim(),u=a?Gt(a.identity.broker_id,a.identity.user):"";if(a&&u){let l={account:a,...i===void 0?{}:{storage:i}},f=C(le(l,t),"accounts",u),h=d?W(d,t):C(f,"contracts.jsonl"),m=c?W(c,t):C(f,"commissions.jsonl");if(h===m)r.errors.push("account.reference_data.contracts_path \u4E0E commissions_path \u4E0D\u80FD\u6307\u5411\u540C\u4E00\u6587\u4EF6")}if(r.errors.length>0)throw new ee(t,r.errors);return{...a===void 0?{}:{account:a},...i===void 0?{}:{storage:i},...o===void 0?{}:{server:o}}}function zi(e,t=F()){let r;try{r=Bun.TOML.parse(e)}catch(n){throw new ee(t,[`TOML \u8BED\u6CD5\u9519\u8BEF: ${n instanceof Error?n.message:String(n)}`])}return Vi(r,t)}function F(){let e=process.env[Mi]?.trim();if(e)return zt(e);if(process.platform==="win32"){let t=process.env.APPDATA??(process.env.USERPROFILE?C(process.env.USERPROFILE,"AppData","Roaming"):C(Ce(),"AppData","Roaming"));return C(t,"vibek","config.toml")}return C(process.env.XDG_CONFIG_HOME??C(Ce(),".config"),"vibek","config.toml")}function Ui(e=F()){if(!_i(e))return{path:e,exists:!1,file:{},errors:[]};try{return{path:e,exists:!0,file:zi(Ci(e,"utf8"),e),errors:[]}}catch(t){if(t instanceof ee)return{path:e,exists:!0,file:null,errors:[...t.diagnostics]};return{path:e,exists:!0,file:null,errors:[t instanceof Error?t.message:String(t)]}}}function ue(e=F()){let t=Ui(e);if(t.errors.length>0||t.file===null)throw new ee(e,t.errors);return t.file}function Xr(e=ue()){let t=Gt(e.account?.identity.broker_id,e.account?.identity.user),r=process.env[Ai]?.trim();if(r){if(r!==t)throw new ee(F(),[`\u5185\u90E8\u8FDB\u7A0B\u8D26\u53F7 ${JSON.stringify(r)} \u4E0E\u5F53\u524D\u914D\u7F6E ${JSON.stringify(t)} \u4E0D\u4E00\u81F4`]);return{name:r,source:"process"}}return t?{name:t,source:"config"}:{name:"",source:"none"}}function W(e,t=F()){let r=e==="~"?Ce():e.startsWith("~/")||e.startsWith("~\\")?C(Ce(),e.slice(2)):e;return $i(r)?zt(r):zt(Pi(t),r)}function le(e=ue(),t=F()){return W(e.storage?.data_root??C(Ce(),".vibek"),t)}function ji(e=ue()){return C(le(e),"accounts")}function en(e,t=ue()){if(!Ut.test(e))throw Error(`\u8D26\u53F7\u540D\u975E\u6CD5: ${e}`);return C(ji(t),e)}import{existsSync as tn,readFileSync as Gi}from"fs";import{isAbsolute as Ki,join as rn,relative as Yi,resolve as Wi}from"path";var Ji=1;function Pe(e=le()){return rn(e,"gateway")}function Qi(e=Pe()){return rn(e,"current.json")}function nn(e=Pe(),t={}){let r=Qi(e);if(!tn(r))return null;try{let n=JSON.parse(Gi(r,"utf8")),i=Zi(n),o=t.platform??process.platform,s=t.arch??process.arch;if(i.platform!==o||i.arch!==s)throw Error(`\u5E73\u53F0\u4E0D\u5339\u914D: ${i.platform}-${i.arch} / ${o}-${s}`);let a=Kt(e,i);if(t.requireExecutable!==!1&&!tn(a))throw Error(`executable \u4E0D\u5B58\u5728: ${a}`);return i}catch(n){if(t.tolerateInvalid)return null;throw Error(`\u53D7\u7BA1 gateway \u72B6\u6001\u65E0\u6548: ${r}: ${n instanceof Error?n.message:String(n)}`)}}function Kt(e,t){let r=Wi(e,t.relativeExecutable),n=Yi(e,r);if(!n||n.startsWith("..")||Ki(n))throw Error("relativeExecutable \u8D8A\u51FA\u53D7\u7BA1 gateway \u76EE\u5F55");return r}function Zi(e){if(!e||typeof e!=="object"||Array.isArray(e))throw Error("\u5FC5\u987B\u662F JSON \u5BF9\u8C61");let t=e,r=t.source;if(!r||typeof r!=="object"||Array.isArray(r))throw Error("source \u5FC5\u987B\u662F JSON \u5BF9\u8C61");let n=r;if(n.kind!=="official"&&n.kind!=="local")throw Error("source.kind \u975E\u6CD5");let i=["releaseKey","gatewayVersion","ctpSdkVersion","platform","arch","relativeExecutable","contentSha256","installedAt"];for(let o of i)if(typeof t[o]!=="string"||!t[o].trim())throw Error(`${o} \u975E\u6CD5`);if(t.formatVersion!==Ji)throw Error("formatVersion \u4E0D\u517C\u5BB9");if(!Number.isInteger(t.protocolVersion)||t.protocolVersion<1)throw Error("protocolVersion \u975E\u6CD5");if(!/^[a-f0-9]{64}$/.test(t.contentSha256))throw Error("contentSha256 \u975E\u6CD5");if(t.archiveSha256!==void 0&&!/^[a-f0-9]{64}$/.test(String(t.archiveSha256)))throw Error("archiveSha256 \u975E\u6CD5");if(typeof n.reference!=="string"||!n.reference)throw Error("source.reference \u975E\u6CD5");return t}import{existsSync as an,realpathSync as Xi}from"fs";import{dirname as eo,join as dn,resolve as on}from"path";var sn=process.platform==="win32"?"ctp_gateway.exe":"ctp_gateway",it=import.meta.dir,cn=it;try{cn=eo(Xi(Bun.main))}catch{}var to="vibek-package.json";function ro(e){return[...new Set(e)].find((t)=>an(dn(t,to)))??""}var ot=ro([cn,it]);var Yt=[on(it,"..","..","ctp-gateway","build","bin","Debug",sn),on(it,"..","..","..","..","ctp-gateway","build","bin","Debug",sn)];function no(e=process.platform){let t=e==="win32"?"ctp_gateway.exe":"ctp_gateway";return e==="linux"?"/opt/vibek-gateway/current/ctp_gateway":e==="darwin"?"/usr/local/lib/vibek-gateway/current/ctp_gateway":dn(process.env.ProgramData??"C:\\ProgramData","Vibek","gateway","current",t)}var un=Yt.find((e)=>an(e))??(ot?no():Yt[Yt.length-1]);var te=ue(),Wt=process.env.VIBEK_PROCESS_ROLE??"",at=Wt==="webui"||Wt==="hooks"?{name:"",source:"none"}:Xr(te),g=at.name?te.account:void 0,mn=g?.ctp.production_mode??!0,hn=g?.ctp.password??"",io=hn.trim().length>0,Jt=jt(g?.market?.record_instruments??[]),oo=nt(g?.market?.bar_hooks??[],Jt),P=le(te),pn=I(P,"hooks"),me=at.name?en(at.name,te):I(P,"accounts",".unresolved"),so=I(me,"vibek_bars.db"),ao=I(me,"contracts.jsonl"),co=I(me,"commissions.jsonl"),gn=ot?nn(Pe(P),{requireExecutable:!0}):null,uo=gn?Kt(Pe(P),gn):un;function st(e){let t=/^([01]\d|2[0-3]):([0-5]\d)$/.exec(e);return{minutes:Number(t[1])*60+Number(t[2]),label:e}}var re={processRole:Wt,account:at.name,packageRoot:ot,broker:g?.identity.broker_id??"",user:g?.identity.user??"",pass:g?io?hn:process.env.VIBEK_PASS||"":"",addr:g?.ctp.market_front??"",traderAddr:g?.ctp.trader_front??"",ctpFronts:g?.ctp.front_candidates?.join(",")??"",ctpProductionMode:mn,authCode:g?.ctp.auth_code??"",appId:g?.ctp.app_id??"",logLevel:g?.logging?.level??(mn?"info":"debug"),daemonPort:te.server?.daemon_port??3111,daemonHttpUrl:`http://127.0.0.1:${te.server?.daemon_port??3111}`,webui:{port:te.server?.webui_port??3211},gatewayPath:uo,timerHooks:(g?.timer_hooks??[]).map((e)=>({name:e.name,cron:e.cron,timezone:e.timezone,enabled:e.enabled??!0})),market:{instruments:Jt.map((e)=>e.instrumentID),declarations:Jt,barHooks:oo,tickTimeMode:g?.market?.tick_time_mode??"realtime",maxFutureTickMs:g?.market?.max_future_tick_ms??5000,testFlush:process.env.VIBEK_TEST_FLUSH!==void 0},trading:{maxOrderVolume:g?.trading?.max_order_volume??10,maxPosition:g?.trading?.max_position??20,maxActiveOpenOrders:g?.trading?.max_active_open_orders??5,dailyLimits:{totalOrderLimit:g?.trading?.daily_limits?.total_order_limit??20000,totalCancelLimit:g?.trading?.daily_limits?.total_cancel_limit??1e4,totalTradeLimit:g?.trading?.daily_limits?.total_trade_limit??1e4,instrumentOrderLimit:g?.trading?.daily_limits?.instrument_order_limit??2000,instrumentCancelLimit:g?.trading?.daily_limits?.instrument_cancel_limit??1000,instrumentTradeLimit:g?.trading?.daily_limits?.instrument_trade_limit??1000},duplicateOrders:{duplicateOrderLimit:g?.trading?.duplicate_orders?.duplicate_order_limit??10}},schedule:{enabled:g?.schedule?.enabled??!0,dayStart:st(g?.schedule?.day_start??"08:45"),dayStop:st(g?.schedule?.day_stop??"15:15"),nightStart:st(g?.schedule?.night_start??"20:45"),nightStop:st(g?.schedule?.night_stop??"02:35"),maxStartAttempts:g?.schedule?.max_start_attempts??4,retryBaseMs:g?.schedule?.retry_base_ms??30000,tickMs:g?.schedule?.tick_ms??30000}},pe={config:F(),root:P,accounts:I(P,"accounts"),ai:I(P,"ai"),aiWorkspace:ln(I(P,"ai-workspace"))||!ln(I(P,"tui-workspace"))?I(P,"ai-workspace"):I(P,"tui-workspace"),hooks:pn,hooksSocket:I(P,"run","hooks.sock"),hookHostsDb:I(pn,"hosts.sqlite"),data:me,logs:I(me,"logs"),gatewayWorkdir:I(me,"gateway"),db:g?.market?.db_path?W(g.market.db_path):so,contracts:g?.reference_data?.contracts_path?.trim()?W(g.reference_data.contracts_path):ao,commissions:g?.reference_data?.commissions_path?.trim()?W(g.reference_data.commissions_path):co};var dt=30000;var od=Number.MAX_VALUE/2;import{randomUUID as No}from"crypto";var ct=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,lo=/^[A-Z][A-Z0-9]*\.[A-Z]+$/,ut=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;function O(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function mo(e,t){let r=new Set(t);return Object.keys(e).every((n)=>r.has(n))}function y(e,t){return mo(e,t)&&t.every((r)=>Object.hasOwn(e,r))}function q(e,t=4096){return typeof e==="string"&&e.length>0&&e.length<=t}function ge(e){return typeof e==="string"&&lo.test(e)&&/\d/.test(e.split(".")[0]??"")}function lt(e){if(typeof e!=="string"||!/^\d{8}$/.test(e))return!1;let t=Number(e.slice(0,4)),r=Number(e.slice(4,6)),n=Number(e.slice(6,8)),i=new Date(Date.UTC(t,r-1,n));return i.getUTCFullYear()===t&&i.getUTCMonth()===r-1&&i.getUTCDate()===n}function Qt(e,t,r=new Set){if(e===null||typeof e==="string"||typeof e==="boolean")return e;if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`${t} contains a non-finite number`);return e}if(typeof e!=="object")throw Error(`${t} is not JSON`);if(r.has(e))throw Error(`${t} contains a cycle`);r.add(e);try{if(Array.isArray(e)){let i=[];for(let o=0;o<e.length;o++){if(!Object.hasOwn(e,o))throw Error(`${t}[${o}] is not JSON`);i.push(Qt(e[o],`${t}[${o}]`,r))}return i}let n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw Error(`${t} is not a plain object`);return Object.fromEntries(Object.entries(e).map(([i,o])=>[i,Qt(o,`${t}.${i}`,r)]))}finally{r.delete(e)}}function fn(e,t){if(!O(e))throw Error(`${t} is not JSON`);let r=Qt(e,t);if(!O(r))throw Error(`${t} is not JSON`);return r}function yn(e,t){if(!O(t))throw Error(`invalid payload for ${e}`);if(e==="daemon.ready"){if(y(t,[]))return{}}else if(e==="daemon.stopping"){if(y(t,["signal"])&&(t.signal==="API"||t.signal==="SIGINT"||t.signal==="SIGTERM"))return{signal:t.signal}}else if(e==="daemon.service.faulted"){if(y(t,["service","reason"])&&(t.service==="market"||t.service==="trading")&&typeof t.reason==="string"&&t.reason.length>0&&t.reason.length<=4096)return{service:t.service,reason:t.reason}}else if(y(t,["service"])&&(t.service==="market"||t.service==="trading"))return{service:t.service};throw Error(`invalid payload for ${e}`)}var bn=["1min","3min","5min","10min","15min","30min","1h","2h","4h","1d"];function Zt(e){return typeof e==="string"&&bn.includes(e)}function wn(e,t){let r=["vtSymbol","timeframe","barTime","open","high","low","close","volume","tradingDay"];if(typeof t!=="object"||t===null||Array.isArray(t))throw Error(`invalid payload for ${e}`);let n=t,i=y(n,r)||y(n,[...r,"boundaryReason"]),o=[n.open,n.high,n.low,n.close,n.volume];if(!i||!ge(n.vtSymbol)||!Zt(n.timeframe)||!Number.isSafeInteger(n.barTime)||Number(n.barTime)<0||!o.every((s)=>typeof s==="number"&&Number.isFinite(s))||Number(n.volume)<0||n.tradingDay!==null&&!lt(n.tradingDay)||n.boundaryReason!==void 0&&n.boundaryReason!=="break"&&n.boundaryReason!=="sessionEnd"&&n.boundaryReason!=="disconnect")throw Error(`invalid payload for ${e}`);return{vtSymbol:n.vtSymbol,timeframe:n.timeframe,barTime:n.barTime,open:n.open,high:n.high,low:n.low,close:n.close,volume:n.volume,tradingDay:n.tradingDay,...n.boundaryReason===void 0?{}:{boundaryReason:n.boundaryReason}}}var Sn=["strategy","manual"],vn=["max_order_volume","max_position","max_active_open_orders","trading_day_unresolved","trading_day_mismatch","daily_total_order_limit","daily_total_cancel_limit","daily_total_trade_limit","daily_instrument_order_limit","daily_instrument_cancel_limit","daily_instrument_trade_limit","duplicate_order_limit"];function kn(e,t){if(e!=="risk.order_rejected"||!O(t)||!y(t,["source","riskCode","mode","vtSymbol","tradingDay","direction","offset","price","volume","reason"].concat(Object.hasOwn(t,"strategyId")?["strategyId"]:[]))||!Sn.includes(t.source)||!vn.includes(t.riskCode)||t.mode!=="paper"&&t.mode!=="live"||!ge(t.vtSymbol)||t.tradingDay!==null&&!lt(t.tradingDay)||t.direction!=="long"&&t.direction!=="short"||!["open","close","closeToday","closeYesterday"].includes(String(t.offset))||typeof t.price!=="number"||!Number.isFinite(t.price)||t.price<=0||!Number.isSafeInteger(t.volume)||Number(t.volume)<=0||!q(t.reason)||Object.hasOwn(t,"strategyId")&&(typeof t.strategyId!=="string"||!ct.test(t.strategyId)))throw Error(`invalid payload for ${e}`);return{source:t.source,riskCode:t.riskCode,mode:t.mode,vtSymbol:t.vtSymbol,tradingDay:t.tradingDay,direction:t.direction,offset:t.offset,price:t.price,volume:t.volume,reason:t.reason,...typeof t.strategyId==="string"?{strategyId:t.strategyId}:{}}}var Tn=["window","windowId"];function po(e){return(e.window==="day"||e.window==="night")&&q(e.windowId,128)&&e.windowId.endsWith(`/${e.window}`)}function En(e,t){if(!O(t)||!po(t))throw Error(`invalid payload for ${e}`);let r={window:t.window,windowId:t.windowId};if(e==="schedule.window.opened"||e==="schedule.window.closed"){if(y(t,Tn))return r}else if(y(t,[...Tn,"attempts","reason"])&&Number.isSafeInteger(t.attempts)&&Number(t.attempts)>0&&q(t.reason))return{...r,attempts:t.attempts,reason:t.reason};throw Error(`invalid payload for ${e}`)}var he=["strategyId","mode","vtSymbol"];function go(e){return typeof e.strategyId==="string"&&ct.test(e.strategyId)&&(e.mode==="paper"||e.mode==="live")&&ge(e.vtSymbol)}function ho(e){return{strategyId:e.strategyId,mode:e.mode,vtSymbol:e.vtSymbol}}function In(e,t){if(!O(t)||!go(t))throw Error(`invalid payload for ${e}`);let r=ho(t);switch(e){case"strategy.loaded":if(!y(t,[...he,"replaced"])||typeof t.replaced!=="boolean")break;return{...r,replaced:t.replaced};case"strategy.unloaded":case"strategy.enabled":case"strategy.disabled":case"strategy.started":case"strategy.stopped":if(!y(t,he))break;return r;case"strategy.start_failed":if(!y(t,[...he,"reason"])||!q(t.reason))break;return{...r,reason:t.reason};case"strategy.orders_retained":if(!y(t,[...he,"activeOrders","reason"])||!Number.isSafeInteger(t.activeOrders)||Number(t.activeOrders)<=0||!q(t.reason))break;return{...r,activeOrders:t.activeOrders,reason:t.reason};case"strategy.faulted":if(!y(t,[...he,"callback","reason"])||!q(t.callback,128)||!q(t.reason))break;return{...r,callback:t.callback,reason:t.reason};case"strategy.event":{if(!y(t,[...he,"name","data"])||typeof t.name!=="string"||!ut.test(t.name))break;let n=fn(t.data,"strategy.event.data");return{...r,name:t.name,data:n}}}throw Error(`invalid payload for ${e}`)}function Dn(e,t){if(!O(t)||!y(t,["name"])||typeof t.name!=="string"||!ut.test(t.name))throw Error(`invalid payload for ${e}`);return{name:t.name}}var Xt=65536;function er(e,t){if(e.startsWith("risk."))return kn(e,t);if(e.startsWith("strategy."))return In(e,t);if(e.startsWith("timer."))return Dn(e,t);if(e.startsWith("schedule."))return En(e,t);if(e.startsWith("market."))return wn(e,t);return yn(e,t)}function fe(e){let t=new TextEncoder().encode(JSON.stringify(e));if(t.byteLength>Xt)throw Error(`hook frame exceeds ${Xt} bytes`);let r=new Uint8Array(4+t.byteLength);return new DataView(r.buffer).setUint32(0,t.byteLength,!1),r.set(t,4),r}import{Database as So}from"bun:sqlite";function Rn(e){e.run("PRAGMA journal_mode=WAL"),e.run(`
|
|
4
|
+
`)}`);this.path=e;this.diagnostics=t;this.name="ConfigValidationError"}}class Qr{errors=[];table(e,t){if(Bi(e))return e;return this.errors.push(`${t} \u5FC5\u987B\u662F TOML \u8868`),{}}keys(e,t,r){let n=new Set(t);for(let i of Object.keys(e))if(!n.has(i))this.errors.push(`\u672A\u77E5\u914D\u7F6E\u9879 ${r?`${r}.`:""}${i}`)}string(e,t,r,n={}){let i=e[t],o=`${r}.${t}`;if(i===void 0){if(n.required)this.errors.push(`\u7F3A\u5C11\u914D\u7F6E\u9879 ${o}`);return}if(typeof i!=="string"){this.errors.push(`${o} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);return}if(n.nonEmpty&&i.trim()===""){this.errors.push(`${o} \u4E0D\u80FD\u4E3A\u7A7A`);return}return i}boolean(e,t,r){let n=e[t];if(n===void 0)return;if(typeof n!=="boolean"){this.errors.push(`${r}.${t} \u5FC5\u987B\u662F\u5E03\u5C14\u503C`);return}return n}integer(e,t,r,n,i){let o=e[t];if(o===void 0)return;if(!Number.isInteger(o)||o<n||o>i){this.errors.push(`${r}.${t} \u5FC5\u987B\u662F ${n}..${i} \u7684\u6574\u6570`);return}return o}strings(e,t,r){let n=e[t];if(n===void 0)return;if(!Array.isArray(n)||n.some((i)=>typeof i!=="string")){this.errors.push(`${r}.${t} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4`);return}return[...n]}}function Bi(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function j(e,t,r,n=""){let i=t[r];if(i===void 0)return;return e.table(i,n?`${n}.${r}`:r)}function rt(e,t,r){if(e!==void 0&&!/^([01]\d|2[0-3]):[0-5]\d$/.test(e))r.push(`${t} \u5FC5\u987B\u4F7F\u7528 HH:MM\uFF0824 \u5C0F\u65F6\uFF09\u683C\u5F0F`)}function Wr(e,t,r){if(e===void 0)return;try{let n=new URL(e),i=Number(n.port);if(n.protocol!=="tcp:"||!n.hostname||!Number.isInteger(i)||i<1||i>65535)throw Error("invalid")}catch{r.push(`${t} \u5FC5\u987B\u662F tcp://host:port`)}}var Jr=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;function Hi(e,t){if(t===void 0)return;let r="account.timer_hooks";if(!Array.isArray(t))return e.errors.push(`${r} \u5FC5\u987B\u662F TOML \u8868\u6570\u7EC4`),[];let n=[],i=new Set;for(let[o,s]of t.entries()){let a=`${r}[${o}]`,d=e.table(s,a);e.keys(d,["name","cron","timezone","enabled"],a);let c=e.string(d,"name",a,{required:!0,nonEmpty:!0}),u=e.string(d,"cron",a,{required:!0,nonEmpty:!0}),l=e.string(d,"timezone",a,{required:!0,nonEmpty:!0}),f=e.boolean(d,"enabled",a);if(c!==void 0)if(!Jr.test(c))e.errors.push(`${a}.name \u5FC5\u987B\u5339\u914D ${Jr.source}`);else if(i.has(c))e.errors.push(`${r} \u4E0D\u5141\u8BB8\u91CD\u590D\u540D\u79F0: ${c}`);else i.add(c);let h=u?.trim().replace(/\s+/g," "),m=l?.trim(),v=!1;if(m)try{Bun.cron.parse("0 0 * * *",Date.now(),{tz:m}),v=!0}catch{e.errors.push(`${a}.timezone \u4E0D\u662F\u6709\u6548\u7684 IANA \u65F6\u533A`)}if(h!==void 0&&h.split(" ").length!==5)e.errors.push(`${a}.cron \u5FC5\u987B\u662F 5 \u6BB5 cron \u8868\u8FBE\u5F0F`);else if(h!==void 0&&v)try{if(Bun.cron.parse(h,Date.now(),{tz:m})===null)e.errors.push(`${a}.cron \u5728\u672A\u6765 8 \u5E74\u5185\u6CA1\u6709\u89E6\u53D1\u65F6\u95F4`)}catch{e.errors.push(`${a}.cron \u4E0D\u662F\u6709\u6548\u7684 cron \u8868\u8FBE\u5F0F`)}if(c!==void 0&&h!==void 0&&m!==void 0)n.push({name:c,cron:h,timezone:m,...f===void 0?{}:{enabled:f}})}return n}var Zr=new Set(["CFFEX","SHFE","CZCE","DCE","INE","GFEX"]);function Li(e){let t=e.trim(),r=/^([A-Za-z][A-Za-z0-9]*)\.([A-Za-z]+)$/.exec(t),n=r??/^([A-Za-z][A-Za-z0-9]*)$/.exec(t);if(!n||!/\d/.test(n[1]))throw Error(`\u5408\u7EA6 ${JSON.stringify(e)} \u975E\u6CD5\uFF0C\u5E94\u4F7F\u7528 CODE \u6216 CODE.EXCHANGE`);let i=n[1].toUpperCase(),o=Ni(i),s=r?n[2].toUpperCase():o;if(!s||!Zr.has(s)){if(r)throw Error(`\u5408\u7EA6\u4EA4\u6613\u6240\u975E\u6CD5: ${e}`);throw Error(`\u65E0\u6CD5\u63A8\u65AD\u5408\u7EA6 ${i} \u7684\u4EA4\u6613\u6240\uFF0C\u8BF7\u4F7F\u7528 ${i}.EXCHANGE`)}if(r&&o&&o!==s)throw Error(`\u5408\u7EA6 ${i} \u5E94\u4F7F\u7528\u4EA4\u6613\u6240 ${o}\uFF0C\u800C\u4E0D\u662F ${s}`);return{instrumentID:i,exchangeID:s,vtSymbol:`${i}.${s}`}}function Ni(e){let t=/^[A-Z]+/.exec(e)?.[0];if(!t)return null;let r=new Set;for(let n of qt())if((n.productID.endsWith("_F")?n.productID.slice(0,-2):n.productID)===t)r.add(n.exchangeID);return r.size===1?[...r][0]:null}function jt(e){let t=[],r=new Map;for(let n of e){let i=n.trim(),o=/^([A-Za-z][A-Za-z0-9]*)\.([A-Za-z]+)$/.exec(i);if(!o||!/\d/.test(o[1]))throw Error(`market.record_instruments \u9879 ${JSON.stringify(n)} \u975E\u6CD5\uFF0C\u5E94\u4F7F\u7528 CODE.EXCHANGE`);let s=o[1].toUpperCase(),a=o[2].toUpperCase();if(!Zr.has(a))throw Error(`market.record_instruments \u4EA4\u6613\u6240\u975E\u6CD5: ${n}`);let d=r.get(s);if(d&&d!==a)throw Error(`market.record_instruments \u5408\u7EA6\u4EA4\u6613\u6240\u51B2\u7A81: ${s}.${d} / ${s}.${a}`);if(d)continue;r.set(s,a),t.push({instrumentID:s,exchangeID:a,vtSymbol:`${s}.${a}`})}return t}function Fi(e,t){let r=new Set(e.map((i)=>i.vtSymbol)),n=[...new Set(t.filter((i)=>!r.has(i.vtSymbol)).map((i)=>i.vtSymbol))];if(n.length>0)throw Error(`account.market.bar_hooks \u4F7F\u7528\u4E86\u672A\u5F55\u5236\u8BA2\u9605\u7684\u5408\u7EA6: ${n.join(", ")}`)}function nt(e,t){let r=[],n=new Set;for(let[i,o]of e.entries()){let s;try{s=Li(o.instrument)}catch(d){throw Error(`account.market.bar_hooks[${i}].instrument: ${d instanceof Error?d.message:String(d)}`)}if(!Z(o.timeframe))throw Error(`account.market.bar_hooks[${i}].timeframe \u5FC5\u987B\u662F ${ve}`);let a=`${s.vtSymbol}/${o.timeframe}`;if(n.has(a))throw Error(`account.market.bar_hooks \u4E0D\u5141\u8BB8\u91CD\u590D\u7EC4\u5408: ${a}`);n.add(a),r.push({...s,timeframe:o.timeframe})}return Fi(t,r),r}function Gt(e,t){let r=e?.trim()??"",n=t?.trim()??"";if(!r||!n)return"";let i=`${r}-${n}`;return Ut.test(i)?i:""}function Vi(e,t){let n=e.table(t,"account");e.keys(n,["broker_id","user","ctp","timer_hooks","logging","market","reference_data","trading","schedule"],"account");let i=e.string(n,"broker_id","account",{required:!0,nonEmpty:!0})??"",o=e.string(n,"user","account",{required:!0,nonEmpty:!0})??"";if(!Gt(i,o)&&i&&o)e.errors.push(`${"account"}.broker_id \u4E0E ${"account"}.user \u65E0\u6CD5\u7EC4\u6210\u5408\u6CD5\u8D26\u53F7\u540D`);let a=e.table(n.ctp,"account.ctp");e.keys(a,["market_front","trader_front","front_candidates","password","app_id","auth_code","production_mode"],"account.ctp");let d=e.string(a,"market_front","account.ctp",{required:!0,nonEmpty:!0})??"",c=e.string(a,"trader_front","account.ctp",{required:!0,nonEmpty:!0})??"",u=e.strings(a,"front_candidates","account.ctp"),l=e.string(a,"password","account.ctp"),f=e.string(a,"app_id","account.ctp",{required:!0,nonEmpty:!0})??"",h=e.string(a,"auth_code","account.ctp",{required:!0,nonEmpty:!0})??"",m=e.boolean(a,"production_mode","account.ctp");if(Wr(d||void 0,"account.ctp.market_front",e.errors),Wr(c||void 0,"account.ctp.trader_front",e.errors),u)try{Nt(u.join(","))}catch(S){e.errors.push(`account.ctp.front_candidates: ${S instanceof Error?S.message:String(S)}`)}let v=Hi(e,n.timer_hooks),p=j(e,n,"logging","account");if(p){e.keys(p,["level"],"account.logging");let S=e.string(p,"level","account.logging");if(S!==void 0&&!Kr.includes(S))e.errors.push(`${"account"}.logging.level \u5FC5\u987B\u662F ${Kr.join(" | ")}`)}let k=j(e,n,"market","account");if(k){e.keys(k,["db_path","record_instruments","bar_hooks","tick_time_mode","max_future_tick_ms"],"account.market"),e.string(k,"db_path","account.market");let S=e.strings(k,"record_instruments","account.market"),z=k.bar_hooks,oe=[];if(z!==void 0)if(!Array.isArray(z))e.errors.push(`${"account"}.market.bar_hooks \u5FC5\u987B\u662F TOML \u8868\u6570\u7EC4`);else for(let[A,Zn]of z.entries()){let be=`account.market.bar_hooks[${A}]`,bt=e.table(Zn,be);e.keys(bt,["instrument","timeframe"],be);let yr=e.string(bt,"instrument",be,{required:!0,nonEmpty:!0}),Fe=e.string(bt,"timeframe",be,{required:!0});if(yr!==void 0&&Z(Fe))oe.push({instrument:yr,timeframe:Fe});else if(Fe!==void 0&&!Z(Fe))e.errors.push(`${be}.timeframe \u5FC5\u987B\u662F ${ve}`)}let Ne=e.string(k,"tick_time_mode","account.market");if(Ne!==void 0&&!Yr.includes(Ne))e.errors.push(`${"account"}.market.tick_time_mode \u5FC5\u987B\u662F ${Yr.join(" | ")}`);if(e.integer(k,"max_future_tick_ms","account.market",0,2592000000),S)try{let A=jt(S);nt(oe,A)}catch(A){e.errors.push(A instanceof Error?A.message:String(A))}else if(oe.length>0)try{nt(oe,[])}catch(A){e.errors.push(A instanceof Error?A.message:String(A))}}let q=j(e,n,"reference_data","account");if(q)e.keys(q,["contracts_path","commissions_path"],"account.reference_data"),e.string(q,"contracts_path","account.reference_data"),e.string(q,"commissions_path","account.reference_data");let M=j(e,n,"trading","account");if(M){e.keys(M,["max_order_volume","max_position","max_active_open_orders","daily_limits","duplicate_orders"],"account.trading"),e.integer(M,"max_order_volume","account.trading",1,1e4),e.integer(M,"max_position","account.trading",1,1e5),e.integer(M,"max_active_open_orders","account.trading",1,1000);let S=j(e,M,"daily_limits","account.trading");if(S)e.keys(S,["total_order_limit","total_cancel_limit","total_trade_limit","instrument_order_limit","instrument_cancel_limit","instrument_trade_limit"],"account.trading.daily_limits"),e.integer(S,"total_order_limit","account.trading.daily_limits",1,1e7),e.integer(S,"total_cancel_limit","account.trading.daily_limits",1,1e7),e.integer(S,"total_trade_limit","account.trading.daily_limits",1,1e7),e.integer(S,"instrument_order_limit","account.trading.daily_limits",1,1e7),e.integer(S,"instrument_cancel_limit","account.trading.daily_limits",1,1e7),e.integer(S,"instrument_trade_limit","account.trading.daily_limits",1,1e7);let z=j(e,M,"duplicate_orders","account.trading");if(z)e.keys(z,["duplicate_order_limit"],"account.trading.duplicate_orders"),e.integer(z,"duplicate_order_limit","account.trading.duplicate_orders",2,1000)}let R=j(e,n,"schedule","account");if(R){e.keys(R,["enabled","day_start","day_stop","night_start","night_stop","max_start_attempts","retry_base_ms","tick_ms"],"account.schedule"),e.boolean(R,"enabled","account.schedule");let S=e.string(R,"day_start","account.schedule"),z=e.string(R,"day_stop","account.schedule"),oe=e.string(R,"night_start","account.schedule"),Ne=e.string(R,"night_stop","account.schedule");rt(S,"account.schedule.day_start",e.errors),rt(z,"account.schedule.day_stop",e.errors),rt(oe,"account.schedule.night_start",e.errors),rt(Ne,"account.schedule.night_stop",e.errors),e.integer(R,"max_start_attempts","account.schedule",1,10),e.integer(R,"retry_base_ms","account.schedule",5000,600000),e.integer(R,"tick_ms","account.schedule",5000,120000)}return{identity:{broker_id:i,user:o},ctp:{market_front:d,trader_front:c,...u===void 0?{}:{front_candidates:u},...l===void 0?{}:{password:l},app_id:f,auth_code:h,...m===void 0?{}:{production_mode:m}},...v===void 0?{}:{timer_hooks:v},...p===void 0?{}:{logging:p},...k===void 0?{}:{market:k},...q===void 0?{}:{reference_data:q},...M===void 0?{}:{trading:M},...R===void 0?{}:{schedule:R}}}function qi(e,t=F()){let r=new Qr,n=r.table(e,"config");r.keys(n,["account","storage","server"],"");let i=j(r,n,"storage");if(i)r.keys(i,["data_root"],"storage"),r.string(i,"data_root","storage",{nonEmpty:!0});let o=j(r,n,"server");if(o){r.keys(o,["daemon_port","webui_port"],"server");let l=r.integer(o,"daemon_port","server",1,65535),f=r.integer(o,"webui_port","server",1,65535);if(l!==void 0&&l===f)r.errors.push("server.daemon_port \u4E0E server.webui_port \u4E0D\u80FD\u76F8\u540C")}let s=n.account,a=s===void 0?void 0:Vi(r,s),d=a?.reference_data?.contracts_path?.trim(),c=a?.reference_data?.commissions_path?.trim(),u=a?Gt(a.identity.broker_id,a.identity.user):"";if(a&&u){let l={account:a,...i===void 0?{}:{storage:i}},f=C(le(l,t),"accounts",u),h=d?W(d,t):C(f,"contracts.jsonl"),m=c?W(c,t):C(f,"commissions.jsonl");if(h===m)r.errors.push("account.reference_data.contracts_path \u4E0E commissions_path \u4E0D\u80FD\u6307\u5411\u540C\u4E00\u6587\u4EF6")}if(r.errors.length>0)throw new ee(t,r.errors);return{...a===void 0?{}:{account:a},...i===void 0?{}:{storage:i},...o===void 0?{}:{server:o}}}function zi(e,t=F()){let r;try{r=Bun.TOML.parse(e)}catch(n){throw new ee(t,[`TOML \u8BED\u6CD5\u9519\u8BEF: ${n instanceof Error?n.message:String(n)}`])}return qi(r,t)}function F(){let e=process.env[Mi]?.trim();if(e)return zt(e);if(process.platform==="win32"){let t=process.env.APPDATA??(process.env.USERPROFILE?C(process.env.USERPROFILE,"AppData","Roaming"):C(Ce(),"AppData","Roaming"));return C(t,"vibek","config.toml")}return C(process.env.XDG_CONFIG_HOME??C(Ce(),".config"),"vibek","config.toml")}function Ui(e=F()){if(!xi(e))return{path:e,exists:!1,file:{},errors:[]};try{return{path:e,exists:!0,file:zi(Ci(e,"utf8"),e),errors:[]}}catch(t){if(t instanceof ee)return{path:e,exists:!0,file:null,errors:[...t.diagnostics]};return{path:e,exists:!0,file:null,errors:[t instanceof Error?t.message:String(t)]}}}function ue(e=F()){let t=Ui(e);if(t.errors.length>0||t.file===null)throw new ee(e,t.errors);return t.file}function Xr(e=ue()){let t=Gt(e.account?.identity.broker_id,e.account?.identity.user),r=process.env[Ai]?.trim();if(r){if(r!==t)throw new ee(F(),[`\u5185\u90E8\u8FDB\u7A0B\u8D26\u53F7 ${JSON.stringify(r)} \u4E0E\u5F53\u524D\u914D\u7F6E ${JSON.stringify(t)} \u4E0D\u4E00\u81F4`]);return{name:r,source:"process"}}return t?{name:t,source:"config"}:{name:"",source:"none"}}function W(e,t=F()){let r=e==="~"?Ce():e.startsWith("~/")||e.startsWith("~\\")?C(Ce(),e.slice(2)):e;return $i(r)?zt(r):zt(Pi(t),r)}function le(e=ue(),t=F()){return W(e.storage?.data_root??C(Ce(),".vibek"),t)}function ji(e=ue()){return C(le(e),"accounts")}function en(e,t=ue()){if(!Ut.test(e))throw Error(`\u8D26\u53F7\u540D\u975E\u6CD5: ${e}`);return C(ji(t),e)}import{existsSync as tn,readFileSync as Gi}from"fs";import{isAbsolute as Ki,join as rn,relative as Yi,resolve as Wi}from"path";var Ji=1;function Pe(e=le()){return rn(e,"gateway")}function Qi(e=Pe()){return rn(e,"current.json")}function nn(e=Pe(),t={}){let r=Qi(e);if(!tn(r))return null;try{let n=JSON.parse(Gi(r,"utf8")),i=Zi(n),o=t.platform??process.platform,s=t.arch??process.arch;if(i.platform!==o||i.arch!==s)throw Error(`\u5E73\u53F0\u4E0D\u5339\u914D: ${i.platform}-${i.arch} / ${o}-${s}`);let a=Kt(e,i);if(t.requireExecutable!==!1&&!tn(a))throw Error(`executable \u4E0D\u5B58\u5728: ${a}`);return i}catch(n){if(t.tolerateInvalid)return null;throw Error(`\u53D7\u7BA1 gateway \u72B6\u6001\u65E0\u6548: ${r}: ${n instanceof Error?n.message:String(n)}`)}}function Kt(e,t){let r=Wi(e,t.relativeExecutable),n=Yi(e,r);if(!n||n.startsWith("..")||Ki(n))throw Error("relativeExecutable \u8D8A\u51FA\u53D7\u7BA1 gateway \u76EE\u5F55");return r}function Zi(e){if(!e||typeof e!=="object"||Array.isArray(e))throw Error("\u5FC5\u987B\u662F JSON \u5BF9\u8C61");let t=e,r=t.source;if(!r||typeof r!=="object"||Array.isArray(r))throw Error("source \u5FC5\u987B\u662F JSON \u5BF9\u8C61");let n=r;if(n.kind!=="official"&&n.kind!=="local")throw Error("source.kind \u975E\u6CD5");let i=["releaseKey","gatewayVersion","ctpSdkVersion","platform","arch","relativeExecutable","contentSha256","installedAt"];for(let o of i)if(typeof t[o]!=="string"||!t[o].trim())throw Error(`${o} \u975E\u6CD5`);if(t.formatVersion!==Ji)throw Error("formatVersion \u4E0D\u517C\u5BB9");if(!Number.isInteger(t.protocolVersion)||t.protocolVersion<1)throw Error("protocolVersion \u975E\u6CD5");if(!/^[a-f0-9]{64}$/.test(t.contentSha256))throw Error("contentSha256 \u975E\u6CD5");if(t.archiveSha256!==void 0&&!/^[a-f0-9]{64}$/.test(String(t.archiveSha256)))throw Error("archiveSha256 \u975E\u6CD5");if(typeof n.reference!=="string"||!n.reference)throw Error("source.reference \u975E\u6CD5");return t}import{existsSync as an,realpathSync as Xi}from"fs";import{dirname as eo,join as dn,resolve as on}from"path";var sn=process.platform==="win32"?"ctp_gateway.exe":"ctp_gateway",it=import.meta.dir,cn=it;try{cn=eo(Xi(Bun.main))}catch{}var to="vibek-package.json";function ro(e){return[...new Set(e)].find((t)=>an(dn(t,to)))??""}var ot=ro([cn,it]);var Yt=[on(it,"..","..","ctp-gateway","build","bin","Debug",sn),on(it,"..","..","..","..","ctp-gateway","build","bin","Debug",sn)];function no(e=process.platform){let t=e==="win32"?"ctp_gateway.exe":"ctp_gateway";return e==="linux"?"/opt/vibek-gateway/current/ctp_gateway":e==="darwin"?"/usr/local/lib/vibek-gateway/current/ctp_gateway":dn(process.env.ProgramData??"C:\\ProgramData","Vibek","gateway","current",t)}var un=Yt.find((e)=>an(e))??(ot?no():Yt[Yt.length-1]);var te=ue(),Wt=process.env.VIBEK_PROCESS_ROLE??"",at=Wt==="webui"||Wt==="hooks"?{name:"",source:"none"}:Xr(te),g=at.name?te.account:void 0,mn=g?.ctp.production_mode??!0,hn=g?.ctp.password??"",io=hn.trim().length>0,Jt=jt(g?.market?.record_instruments??[]),oo=nt(g?.market?.bar_hooks??[],Jt),P=le(te),pn=I(P,"hooks"),me=at.name?en(at.name,te):I(P,"accounts",".unresolved"),so=I(me,"vibek_bars.db"),ao=I(me,"contracts.jsonl"),co=I(me,"commissions.jsonl"),gn=ot?nn(Pe(P),{requireExecutable:!0}):null,uo=gn?Kt(Pe(P),gn):un;function st(e){let t=/^([01]\d|2[0-3]):([0-5]\d)$/.exec(e);return{minutes:Number(t[1])*60+Number(t[2]),label:e}}var re={processRole:Wt,account:at.name,packageRoot:ot,broker:g?.identity.broker_id??"",user:g?.identity.user??"",pass:g?io?hn:process.env.VIBEK_PASS||"":"",addr:g?.ctp.market_front??"",traderAddr:g?.ctp.trader_front??"",ctpFronts:g?.ctp.front_candidates?.join(",")??"",ctpProductionMode:mn,authCode:g?.ctp.auth_code??"",appId:g?.ctp.app_id??"",logLevel:g?.logging?.level??(mn?"info":"debug"),daemonPort:te.server?.daemon_port??3111,daemonHttpUrl:`http://127.0.0.1:${te.server?.daemon_port??3111}`,webui:{port:te.server?.webui_port??3211},gatewayPath:uo,timerHooks:(g?.timer_hooks??[]).map((e)=>({name:e.name,cron:e.cron,timezone:e.timezone,enabled:e.enabled??!0})),market:{instruments:Jt.map((e)=>e.instrumentID),declarations:Jt,barHooks:oo,tickTimeMode:g?.market?.tick_time_mode??"realtime",maxFutureTickMs:g?.market?.max_future_tick_ms??5000,testFlush:process.env.VIBEK_TEST_FLUSH!==void 0},trading:{maxOrderVolume:g?.trading?.max_order_volume??10,maxPosition:g?.trading?.max_position??20,maxActiveOpenOrders:g?.trading?.max_active_open_orders??5,dailyLimits:{totalOrderLimit:g?.trading?.daily_limits?.total_order_limit??20000,totalCancelLimit:g?.trading?.daily_limits?.total_cancel_limit??1e4,totalTradeLimit:g?.trading?.daily_limits?.total_trade_limit??1e4,instrumentOrderLimit:g?.trading?.daily_limits?.instrument_order_limit??2000,instrumentCancelLimit:g?.trading?.daily_limits?.instrument_cancel_limit??1000,instrumentTradeLimit:g?.trading?.daily_limits?.instrument_trade_limit??1000},duplicateOrders:{duplicateOrderLimit:g?.trading?.duplicate_orders?.duplicate_order_limit??10}},schedule:{enabled:g?.schedule?.enabled??!0,dayStart:st(g?.schedule?.day_start??"08:45"),dayStop:st(g?.schedule?.day_stop??"15:15"),nightStart:st(g?.schedule?.night_start??"20:45"),nightStop:st(g?.schedule?.night_stop??"02:35"),maxStartAttempts:g?.schedule?.max_start_attempts??4,retryBaseMs:g?.schedule?.retry_base_ms??30000,tickMs:g?.schedule?.tick_ms??30000}},pe={config:F(),root:P,accounts:I(P,"accounts"),ai:I(P,"ai"),aiWorkspace:ln(I(P,"ai-workspace"))||!ln(I(P,"tui-workspace"))?I(P,"ai-workspace"):I(P,"tui-workspace"),hooks:pn,hooksSocket:I(P,"run","hooks.sock"),hookHostsDb:I(pn,"hosts.sqlite"),data:me,logs:I(me,"logs"),gatewayWorkdir:I(me,"gateway"),db:g?.market?.db_path?W(g.market.db_path):so,contracts:g?.reference_data?.contracts_path?.trim()?W(g.reference_data.contracts_path):ao,commissions:g?.reference_data?.commissions_path?.trim()?W(g.reference_data.commissions_path):co};var dt=30000;var sd=Number.MAX_VALUE/2;import{randomUUID as Fo}from"crypto";var ct=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,lo=/^[A-Z][A-Z0-9]*\.[A-Z]+$/,ut=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;function O(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function mo(e,t){let r=new Set(t);return Object.keys(e).every((n)=>r.has(n))}function y(e,t){return mo(e,t)&&t.every((r)=>Object.hasOwn(e,r))}function V(e,t=4096){return typeof e==="string"&&e.length>0&&e.length<=t}function ge(e){return typeof e==="string"&&lo.test(e)&&/\d/.test(e.split(".")[0]??"")}function lt(e){if(typeof e!=="string"||!/^\d{8}$/.test(e))return!1;let t=Number(e.slice(0,4)),r=Number(e.slice(4,6)),n=Number(e.slice(6,8)),i=new Date(Date.UTC(t,r-1,n));return i.getUTCFullYear()===t&&i.getUTCMonth()===r-1&&i.getUTCDate()===n}function Qt(e,t,r=new Set){if(e===null||typeof e==="string"||typeof e==="boolean")return e;if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`${t} contains a non-finite number`);return e}if(typeof e!=="object")throw Error(`${t} is not JSON`);if(r.has(e))throw Error(`${t} contains a cycle`);r.add(e);try{if(Array.isArray(e)){let i=[];for(let o=0;o<e.length;o++){if(!Object.hasOwn(e,o))throw Error(`${t}[${o}] is not JSON`);i.push(Qt(e[o],`${t}[${o}]`,r))}return i}let n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw Error(`${t} is not a plain object`);return Object.fromEntries(Object.entries(e).map(([i,o])=>[i,Qt(o,`${t}.${i}`,r)]))}finally{r.delete(e)}}function fn(e,t){if(!O(e))throw Error(`${t} is not JSON`);let r=Qt(e,t);if(!O(r))throw Error(`${t} is not JSON`);return r}function yn(e,t){if(!O(t))throw Error(`invalid payload for ${e}`);if(e==="daemon.ready"){if(y(t,[]))return{}}else if(e==="daemon.stopping"){if(y(t,["signal"])&&(t.signal==="API"||t.signal==="SIGINT"||t.signal==="SIGTERM"))return{signal:t.signal}}else if(e==="daemon.service.faulted"){if(y(t,["service","reason"])&&(t.service==="market"||t.service==="trading")&&typeof t.reason==="string"&&t.reason.length>0&&t.reason.length<=4096)return{service:t.service,reason:t.reason}}else if(y(t,["service"])&&(t.service==="market"||t.service==="trading"))return{service:t.service};throw Error(`invalid payload for ${e}`)}var bn=["1min","3min","5min","10min","15min","30min","1h","2h","4h","1d"];function Zt(e){return typeof e==="string"&&bn.includes(e)}function wn(e,t){let r=["vtSymbol","timeframe","barTime","open","high","low","close","volume","tradingDay"];if(typeof t!=="object"||t===null||Array.isArray(t))throw Error(`invalid payload for ${e}`);let n=t,i=y(n,r)||y(n,[...r,"boundaryReason"]),o=[n.open,n.high,n.low,n.close,n.volume];if(!i||!ge(n.vtSymbol)||!Zt(n.timeframe)||!Number.isSafeInteger(n.barTime)||Number(n.barTime)<0||!o.every((s)=>typeof s==="number"&&Number.isFinite(s))||Number(n.volume)<0||n.tradingDay!==null&&!lt(n.tradingDay)||n.boundaryReason!==void 0&&n.boundaryReason!=="break"&&n.boundaryReason!=="sessionEnd"&&n.boundaryReason!=="disconnect")throw Error(`invalid payload for ${e}`);return{vtSymbol:n.vtSymbol,timeframe:n.timeframe,barTime:n.barTime,open:n.open,high:n.high,low:n.low,close:n.close,volume:n.volume,tradingDay:n.tradingDay,...n.boundaryReason===void 0?{}:{boundaryReason:n.boundaryReason}}}var Sn=["strategy","manual"],vn=["max_order_volume","max_position","max_active_open_orders","trading_day_unresolved","trading_day_mismatch","daily_total_order_limit","daily_total_cancel_limit","daily_total_trade_limit","daily_instrument_order_limit","daily_instrument_cancel_limit","daily_instrument_trade_limit","duplicate_order_limit"];function kn(e,t){if(e!=="risk.order_rejected"||!O(t)||!y(t,["source","riskCode","mode","vtSymbol","tradingDay","direction","offset","price","volume","reason"].concat(Object.hasOwn(t,"strategyId")?["strategyId"]:[]))||!Sn.includes(t.source)||!vn.includes(t.riskCode)||t.mode!=="paper"&&t.mode!=="live"||!ge(t.vtSymbol)||t.tradingDay!==null&&!lt(t.tradingDay)||t.direction!=="long"&&t.direction!=="short"||!["open","close","closeToday","closeYesterday"].includes(String(t.offset))||typeof t.price!=="number"||!Number.isFinite(t.price)||t.price<=0||!Number.isSafeInteger(t.volume)||Number(t.volume)<=0||!V(t.reason)||Object.hasOwn(t,"strategyId")&&(typeof t.strategyId!=="string"||!ct.test(t.strategyId)))throw Error(`invalid payload for ${e}`);return{source:t.source,riskCode:t.riskCode,mode:t.mode,vtSymbol:t.vtSymbol,tradingDay:t.tradingDay,direction:t.direction,offset:t.offset,price:t.price,volume:t.volume,reason:t.reason,...typeof t.strategyId==="string"?{strategyId:t.strategyId}:{}}}var Tn=["window","windowId"];function po(e){return(e.window==="day"||e.window==="night")&&V(e.windowId,128)&&e.windowId.endsWith(`/${e.window}`)}function En(e,t){if(!O(t)||!po(t))throw Error(`invalid payload for ${e}`);let r={window:t.window,windowId:t.windowId};if(e==="schedule.window.opened"||e==="schedule.window.closed"){if(y(t,Tn))return r}else if(y(t,[...Tn,"attempts","reason"])&&Number.isSafeInteger(t.attempts)&&Number(t.attempts)>0&&V(t.reason))return{...r,attempts:t.attempts,reason:t.reason};throw Error(`invalid payload for ${e}`)}var he=["strategyId","mode","vtSymbol"];function go(e){return typeof e.strategyId==="string"&&ct.test(e.strategyId)&&(e.mode==="paper"||e.mode==="live")&&ge(e.vtSymbol)}function ho(e){return{strategyId:e.strategyId,mode:e.mode,vtSymbol:e.vtSymbol}}function In(e,t){if(!O(t)||!go(t))throw Error(`invalid payload for ${e}`);let r=ho(t);switch(e){case"strategy.loaded":if(!y(t,[...he,"replaced"])||typeof t.replaced!=="boolean")break;return{...r,replaced:t.replaced};case"strategy.unloaded":case"strategy.enabled":case"strategy.disabled":case"strategy.started":case"strategy.stopped":if(!y(t,he))break;return r;case"strategy.start_failed":if(!y(t,[...he,"reason"])||!V(t.reason))break;return{...r,reason:t.reason};case"strategy.orders_retained":if(!y(t,[...he,"activeOrders","reason"])||!Number.isSafeInteger(t.activeOrders)||Number(t.activeOrders)<=0||!V(t.reason))break;return{...r,activeOrders:t.activeOrders,reason:t.reason};case"strategy.faulted":if(!y(t,[...he,"callback","reason"])||!V(t.callback,128)||!V(t.reason))break;return{...r,callback:t.callback,reason:t.reason};case"strategy.event":{if(!y(t,[...he,"name","data"])||typeof t.name!=="string"||!ut.test(t.name))break;let n=fn(t.data,"strategy.event.data");return{...r,name:t.name,data:n}}}throw Error(`invalid payload for ${e}`)}function Dn(e,t){if(!O(t)||!y(t,["name"])||typeof t.name!=="string"||!ut.test(t.name))throw Error(`invalid payload for ${e}`);return{name:t.name}}var Xt=65536;function er(e,t){if(e.startsWith("risk."))return kn(e,t);if(e.startsWith("strategy."))return In(e,t);if(e.startsWith("timer."))return Dn(e,t);if(e.startsWith("schedule."))return En(e,t);if(e.startsWith("market."))return wn(e,t);return yn(e,t)}function fe(e){let t=new TextEncoder().encode(JSON.stringify(e));if(t.byteLength>Xt)throw Error(`hook frame exceeds ${Xt} bytes`);let r=new Uint8Array(4+t.byteLength);return new DataView(r.buffer).setUint32(0,t.byteLength,!1),r.set(t,4),r}import{Database as So}from"bun:sqlite";function Rn(e){e.run("PRAGMA journal_mode=WAL"),e.run(`
|
|
5
5
|
CREATE TABLE IF NOT EXISTS bars (
|
|
6
6
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7
7
|
code TEXT NOT NULL,
|
|
@@ -22,7 +22,22 @@ ${t.map((r)=>` - ${r}`).join(`
|
|
|
22
22
|
`),e.run(`
|
|
23
23
|
CREATE INDEX IF NOT EXISTS idx_bars_code_tf_time
|
|
24
24
|
ON bars(code, timeframe, bar_time)
|
|
25
|
-
`)
|
|
25
|
+
`),e.run(`CREATE TABLE IF NOT EXISTS market_ticks (
|
|
26
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
27
|
+
instrument_id TEXT NOT NULL,
|
|
28
|
+
stream_id TEXT NOT NULL,
|
|
29
|
+
price_tick REAL NOT NULL,
|
|
30
|
+
trading_day TEXT NOT NULL,
|
|
31
|
+
event_time_ms INTEGER NOT NULL,
|
|
32
|
+
aggregation_time_ms INTEGER,
|
|
33
|
+
received_at_ms INTEGER NOT NULL,
|
|
34
|
+
last_price REAL NOT NULL,
|
|
35
|
+
volume INTEGER NOT NULL CHECK(volume >= 0),
|
|
36
|
+
bid_price1 REAL,
|
|
37
|
+
ask_price1 REAL,
|
|
38
|
+
bid_volume1 INTEGER,
|
|
39
|
+
ask_volume1 INTEGER
|
|
40
|
+
)`),e.run("CREATE INDEX IF NOT EXISTS idx_market_ticks_instrument_id ON market_ticks(instrument_id, id)")}function ne(e){return{code:e.code,timeframe:e.timeframe,barTime:e.bar_time,open:e.open,high:e.high,low:e.low,close:e.close,volume:e.volume,...e.trading_day?{tradingDay:e.trading_day}:{},...e.boundary_reason?{boundaryReason:e.boundary_reason}:{}}}function vo(e){let{aggregationTimeMs:t,bidPrice1:r,askPrice1:n,bidVolume1:i,askVolume1:o,...s}=e;return{...s,...t===null?{}:{aggregationTimeMs:t},...r===null?{}:{bidPrice1:r},...n===null?{}:{askPrice1:n},...i===null?{}:{bidVolume1:i},...o===null?{}:{askVolume1:o}}}var _n=pe.db;function nr(e=!1,t){return new xn(t??_n,e)}class xn{db;barCallbacks=[];constructor(e,t=!1){if(this.db=new So(e,{readonly:t,create:!t}),!t)Rn(this.db)}async getHistoricalBars(e,t,r){return this.db.query(`SELECT code, timeframe, bar_time, open, high, low, close, volume, trading_day, boundary_reason, created_at
|
|
26
41
|
FROM bars WHERE code = ? AND timeframe = ? ORDER BY bar_time ASC LIMIT ?`).all(e,t,r).map(ne)}async getLatestBars(e,t,r){return this.db.query(`SELECT code, timeframe, bar_time, open, high, low, close, volume, trading_day, boundary_reason, created_at
|
|
27
42
|
FROM (SELECT code, timeframe, bar_time, open, high, low, close, volume, trading_day, boundary_reason, created_at
|
|
28
43
|
FROM bars WHERE code = ? AND timeframe = ?
|
|
@@ -39,14 +54,21 @@ ${t.map((r)=>` - ${r}`).join(`
|
|
|
39
54
|
FROM bars WHERE code = ? AND timeframe = ? AND bar_time <= ?`).get(e,t,r))??null;if(!n||n.barCount===0||n.firstBarTime===null||n.lastBarTime===null)return null;return{code:e,timeframe:t,...n,firstBarTime:n.firstBarTime,lastBarTime:n.lastBarTime}}getBarAt(e,t,r){let n=this.db.query(`SELECT code, timeframe, bar_time, open, high, low, close, volume, trading_day, boundary_reason, created_at
|
|
40
55
|
FROM bars WHERE code = ? AND timeframe = ? AND bar_time = ?`).get(e,t,r);return n?ne(n):null}*iterateBars(e,t){let r=this.db.prepare(`SELECT code, timeframe, bar_time, open, high, low, close, volume, trading_day, boundary_reason, created_at
|
|
41
56
|
FROM bars WHERE code = ? AND timeframe = ? ORDER BY bar_time ASC`);try{for(let n of r.iterate(e,t))yield ne(n)}finally{r.finalize()}}*iterateBarsRange(e,t,r,n){let i=this.db.prepare(`SELECT code, timeframe, bar_time, open, high, low, close, volume, trading_day, boundary_reason, created_at
|
|
42
|
-
FROM bars WHERE code = ? AND timeframe = ? AND bar_time > ? AND bar_time <= ? ORDER BY bar_time ASC`);try{for(let o of i.iterate(e,t,r,n))yield ne(o)}finally{i.finalize()}}getAllCodes(){return this.db.query("SELECT DISTINCT code FROM bars ORDER BY code").all().map((t)=>t.code)}
|
|
43
|
-
|
|
57
|
+
FROM bars WHERE code = ? AND timeframe = ? AND bar_time > ? AND bar_time <= ? ORDER BY bar_time ASC`);try{for(let o of i.iterate(e,t,r,n))yield ne(o)}finally{i.finalize()}}getAllCodes(){return this.db.query("SELECT DISTINCT code FROM bars ORDER BY code").all().map((t)=>t.code)}insertTick(e){if(!e.streamID)throw Error("\u5F55\u5236 Tick \u7F3A\u5C11\u8FDE\u7EED\u6BB5\u6807\u8BC6");let t=this.db.query(`INSERT INTO market_ticks (
|
|
58
|
+
instrument_id, stream_id, price_tick, trading_day, event_time_ms, aggregation_time_ms,
|
|
59
|
+
received_at_ms, last_price, volume, bid_price1, ask_price1, bid_volume1, ask_volume1
|
|
60
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(e.instrumentID,e.streamID,e.priceTick,e.tradingDay,e.eventTimeMs,e.aggregationTimeMs??null,e.receivedAtMs,e.lastPrice,e.volume,e.bidPrice1??null,e.askPrice1??null,e.bidVolume1??null,e.askVolume1??null);return{...e,sequence:Number(t.lastInsertRowid)}}getLatestTicks(e,t,r){if(!Number.isSafeInteger(t)||t<1||t>40000||r!==void 0&&(!Number.isSafeInteger(r)||r<=0))throw Error("Tick \u67E5\u8BE2\u8303\u56F4\u65E0\u6548");let n=this.db.query(`SELECT id AS sequence, stream_id AS streamID, instrument_id AS instrumentID,
|
|
61
|
+
price_tick AS priceTick, trading_day AS tradingDay, event_time_ms AS eventTimeMs,
|
|
62
|
+
aggregation_time_ms AS aggregationTimeMs, received_at_ms AS receivedAtMs, last_price AS lastPrice,
|
|
63
|
+
volume, bid_price1 AS bidPrice1, ask_price1 AS askPrice1, bid_volume1 AS bidVolume1, ask_volume1 AS askVolume1
|
|
64
|
+
FROM market_ticks WHERE instrument_id = ? ${r===void 0?"":"AND id < ?"} ORDER BY id DESC LIMIT ?`).all(e.toUpperCase(),...r===void 0?[]:[r],t+1),i=n.length>t,o=n.slice(0,t).reverse().map(vo);return{instrumentID:e.toUpperCase(),ticks:o,hasMore:i}}insertBars(e){let t=Math.floor(Date.now()/1000),r=this.db.prepare(`INSERT OR IGNORE INTO bars (code, timeframe, bar_time, open, high, low, close, volume, trading_day, boundary_reason, created_at)
|
|
65
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),n=0;try{for(let i of e)if(r.run(i.code,i.timeframe,i.barTime,i.open,i.high,i.low,i.close,i.volume,i.tradingDay??null,i.boundaryReason??null,t).changes>0)n++;return n}finally{r.finalize()}}onBar(e){this.barCallbacks.push(e)}emitBar(e){for(let t of this.barCallbacks)t(e)}destroy(){this.barCallbacks=[],this.db.close()}}import{existsSync as ko,mkdirSync as To,readFileSync as Eo,renameSync as Io,unlinkSync as Cn,writeFileSync as Do}from"fs";import{randomUUID as Oo}from"crypto";import{join as ir}from"path";var Ro=new Set(["strategyId","vtSymbol","mode","bundleHash","savedAt","pos","variables","inventory","tradingDay"]);class $n extends Error{details;code="strategy_state_incompatible";constructor(e){super(`\u7B56\u7565 ${e.oldState.strategyId}@${e.oldState.mode} \u72B6\u6001 schema \u4E0D\u517C\u5BB9\uFF0C\u672A\u52A0\u8F7D\u65B0\u7B56\u7565\u3002
|
|
44
66
|
`+`\u72B6\u6001\u6587\u4EF6: ${e.statePath}
|
|
45
67
|
`+`\u65E7 schema: ${JSON.stringify(e.oldSchema)}
|
|
46
68
|
`+`\u65B0\u7B56\u7565\u671F\u671B schema: ${JSON.stringify(e.newSchema)}
|
|
47
|
-
`+`\u82E5\u8981\u653E\u5F03\u65E7\u72B6\u6001\uFF0C\u8BF7\u6267\u884C vibek strategy unload ${e.oldState.strategyId}@${e.oldState.mode} \u540E\u91CD\u65B0 load\uFF1B`+"unload \u4F1A\u6C38\u4E45\u5220\u9664\u8BE5\u6A21\u5F0F\u7684\u72B6\u6001\u3001\u6301\u4ED3\u548C\u53D8\u91CF\u3002\u82E5\u5F53\u524D\u6CA1\u6709\u5DF2\u52A0\u8F7D\u5B9E\u4F8B\uFF0C\u8BF7\u6309\u4E0A\u8FF0\u8DEF\u5F84\u624B\u52A8\u5220\u9664\u65E7 state \u6587\u4EF6\u3002");this.details=e;this.name="StrategyStateCompatibilityError"}}function or(e){return e!==null&&typeof e==="object"&&!Array.isArray(e)}function mt(e,t,r){if(!Number.isInteger(e)||e<0)throw Error(`${r}: ${t} \u975E\u6CD5`);return e}function Pn(e,t){if(!or(e))throw Error(`${t}: \u7B56\u7565\u72B6\u6001\u5FC5\u987B\u662F\u5BF9\u8C61`);if(Object.keys(e).some((i)=>!
|
|
48
|
-
`;if(Buffer.byteLength(n)>262144)throw Error(`\u7B56\u7565 ${e.strategyId} variables \u8D85\u8FC7 256KB`);let i=`${t}.${process.pid}-${
|
|
49
|
-
`;if(Buffer.byteLength(r)>Mn)throw Error(`${this.path}: \u98CE\u63A7\u72B6\u6001\u8D85\u8FC7 4MB`);_o(Ao(this.path),{recursive:!0});let n=`${this.path}.${process.pid}-${Ro()}.tmp`;try{Mo(n,r,{encoding:"utf8",mode:384,flag:"wx"}),Po(n,this.path)}finally{try{$o(n)}catch{}}}}class He{settings;paper=Me();live=Me();stateStore;persistDelayMs;schedule;persistTimer=null;dirty=!1;constructor(e={}){this.settings=e.settings??Ho(),this.stateStore=e.stateStore??null,this.persistDelayMs=e.persistDelayMs??250,this.schedule=e.schedule??pt;let t=this.stateStore?.read();if(t)this.paper=Ln(t.paper),this.live=Ln(t.live)}setTradingDay(e,t){if(!gt.test(t))return;let r=this.mode(e);if(r.tradingDay===t)return;if(r.tradingDay&&t<r.tradingDay)return;this.replaceMode(e,Me(t)),this.markDirty()}checkOpen(e){return this.checkOpenDetailed(e)?.message??null}checkOpenDetailed(e,t={}){if(e.offset!=="open")return null;let r=t.recordAttempt!==!1,n;if(r)this.setTradingDay(e.mode,e.tradingDay),n=this.mode(e.mode);else{let a=this.mode(e.mode);n=gt.test(e.tradingDay)&&(!a.tradingDay||e.tradingDay>a.tradingDay)?Me(e.tradingDay):a}if(!n.tradingDay)return{code:"trading_day_unresolved",message:"\u98CE\u63A7 TradingDay \u5C1A\u672A\u786E\u5B9A"};if(n.tradingDay!==e.tradingDay)return{code:"trading_day_mismatch",message:`\u98CE\u63A7 TradingDay \u4E0D\u4E00\u81F4 ${n.tradingDay}/${e.tradingDay||"(\u7A7A)"}`};let i=this.dailyFailure(n,e.vtSymbol.toUpperCase());if(i)return i;let o=Fn(e),s=(n.duplicateAttempts.get(o)??0)+1;if(s>=this.settings.duplicateOrderLimit){if(r)n.duplicateAttempts.set(o,s),n.duplicateRejected++,this.markDirty();return{code:"duplicate_order_limit",message:`\u76F8\u540C\u5F00\u4ED3\u8BF7\u6C42\u5F53\u65E5\u7B2C ${s} \u6B21\uFF0C\u8FBE\u5230\u91CD\u590D\u62A5\u5355\u9608\u503C ${this.settings.duplicateOrderLimit}`}}if(r)n.duplicateAttempts.set(o,s),this.markDirty();return null}recordOrder(e,t,r=""){if(r)this.setTradingDay(e,r);let n=this.mode(e);if(!n.tradingDay||r&&n.tradingDay!==r)return;let i=t.vtSymbol.toUpperCase(),o=!1;if(!n.orderIds.has(t.vtOrderId))n.orderIds.add(t.vtOrderId),this.increment(n,i,"orders"),o=!0;if(t.status==="cancelled"&&!n.cancelledOrderIds.has(t.vtOrderId))n.cancelledOrderIds.add(t.vtOrderId),this.increment(n,i,"cancels"),o=!0;if(o)this.markDirty()}recordTrade(e,t){this.setTradingDay(e,t.tradingDay);let r=this.mode(e);if(!r.tradingDay||r.tradingDay!==t.tradingDay||r.tradeIds.has(t.vtTradeId))return;r.tradeIds.add(t.vtTradeId),this.increment(r,t.vtSymbol.toUpperCase(),"trades"),this.markDirty()}reconcileLive(e){if(!gt.test(e.tradingDay))throw Error(`Live \u98CE\u63A7 TradingDay \u975E\u6CD5: ${e.tradingDay}`);if(this.setTradingDay("live",e.tradingDay),this.live.tradingDay!==e.tradingDay)throw Error(`Live \u98CE\u63A7 TradingDay \u56DE\u9000: ${this.live.tradingDay}/${e.tradingDay}`);let t=this.live,r=Me(e.tradingDay);for(let n of e.orders)this.addSnapshotOrder(r,n);for(let n of e.trades)this.addSnapshotTrade(r,n);t.orderIds=new Set([...t.orderIds,...r.orderIds]),t.cancelledOrderIds=new Set([...t.cancelledOrderIds,...r.cancelledOrderIds]),t.tradeIds=new Set([...t.tradeIds,...r.tradeIds]),sr(t.total,r.total);for(let[n,i]of r.instruments){let o=this.instrumentCounts(t,n);sr(o,i)}sr(t.total,Lo(t.instruments.values()));for(let[n,i]of r.duplicateAttempts)t.duplicateAttempts.set(n,Math.max(t.duplicateAttempts.get(n)??0,i));this.markDirty()}snapshot(){return{limits:{dailyLimits:{...this.settings.dailyLimits},duplicateOrderLimit:this.settings.duplicateOrderLimit},paper:this.modeSnapshot(this.paper),live:this.modeSnapshot(this.live)}}tradingDay(e){return this.mode(e).tradingDay}flush(){if(this.persistTimer?.(),this.persistTimer=null,!this.dirty||!this.stateStore)return;this.stateStore.write({paper:Nn(this.paper),live:Nn(this.live)}),this.dirty=!1}close({persist:e=!0}={}){if(e)this.flush();else this.persistTimer?.(),this.persistTimer=null}dailyFailure(e,t){let r=this.settings.dailyLimits,n=this.instrumentCounts(e,t),o=[[e.total.orders,r.totalOrderLimit,"\u5F53\u65E5\u5168\u5C40\u59D4\u6258","daily_total_order_limit"],[e.total.cancels,r.totalCancelLimit,"\u5F53\u65E5\u5168\u5C40\u64A4\u5355","daily_total_cancel_limit"],[e.total.trades,r.totalTradeLimit,"\u5F53\u65E5\u5168\u5C40\u6210\u4EA4","daily_total_trade_limit"],[n.orders,r.instrumentOrderLimit,`${t} \u5F53\u65E5\u59D4\u6258`,"daily_instrument_order_limit"],[n.cancels,r.instrumentCancelLimit,`${t} \u5F53\u65E5\u64A4\u5355`,"daily_instrument_cancel_limit"],[n.trades,r.instrumentTradeLimit,`${t} \u5F53\u65E5\u6210\u4EA4`,"daily_instrument_trade_limit"]].find(([s,a])=>s>=a);return o?{code:o[3],message:`${o[2]}\u8FBE\u5230\u4E0A\u9650 ${o[0]}/${o[1]}`}:null}addSnapshotOrder(e,t){if(t.identity.tradingDay!==e.tradingDay||e.orderIds.has(t.vtOrderId))return;if(e.orderIds.add(t.vtOrderId),this.increment(e,t.vtSymbol.toUpperCase(),"orders"),t.status==="cancelled")e.cancelledOrderIds.add(t.vtOrderId),this.increment(e,t.vtSymbol.toUpperCase(),"cancels");if(t.offset==="open"){let r=Fn({vtSymbol:t.vtSymbol,direction:t.direction,offset:t.offset,price:t.price,volume:t.volume});e.duplicateAttempts.set(r,(e.duplicateAttempts.get(r)??0)+1)}}addSnapshotTrade(e,t){if(t.tradingDay!==e.tradingDay||e.tradeIds.has(t.vtTradeId))return;e.tradeIds.add(t.vtTradeId),this.increment(e,t.vtSymbol.toUpperCase(),"trades")}increment(e,t,r){e.total[r]++,this.instrumentCounts(e,t)[r]++}instrumentCounts(e,t){let r=e.instruments.get(t);if(!r)r=ar(),e.instruments.set(t,r);return r}mode(e){return e==="paper"?this.paper:this.live}replaceMode(e,t){if(e==="paper")this.paper=t;else this.live=t}modeSnapshot(e){let t=0,r=0;for(let n of e.duplicateAttempts.values()){if(n>=2)t++;r=Math.max(r,n)}return{tradingDay:e.tradingDay,total:{...e.total},instruments:Object.fromEntries([...e.instruments.entries()].sort(([n],[i])=>n.localeCompare(i)).map(([n,i])=>[n,{...i}])),duplicateRequests:{trackedKeys:e.duplicateAttempts.size,repeatedKeys:t,maxAttempts:r,rejected:e.duplicateRejected}}}markDirty(){if(this.dirty=!0,!this.stateStore||this.persistTimer)return;this.persistTimer=this.schedule(()=>{this.persistTimer=null,this.flush()},this.persistDelayMs)}}var qn=1e5,Fo=1e5,qo=500;function cr(){return{longToday:0,longYesterday:0,shortToday:0,shortYesterday:0}}function B(e,t){return`${t}:${e}`}function Vn(e){return e!==null&&typeof e==="object"&&!Array.isArray(e)}function lr(e,t,r){if(e===null||typeof e==="string"||typeof e==="boolean")return;if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`${t} \u5305\u542B\u975E\u6709\u9650\u6570\u5B57`);return}if(typeof e!=="object")throw Error(`${t} \u5305\u542B\u4E0D\u53EF\u5E8F\u5217\u5316\u7684 ${typeof e}`);if(r.has(e))throw Error(`${t} \u5305\u542B\u5FAA\u73AF\u5F15\u7528`);if(r.add(e),Array.isArray(e))for(let n=0;n<e.length;n++)lr(e[n],`${t}[${n}]`,r);else{let n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw Error(`${t} \u5FC5\u987B\u662F\u666E\u901A\u5BF9\u8C61\u6216\u6570\u7EC4`);for(let[i,o]of Object.entries(e))lr(o,`${t}.${i}`,r)}r.delete(e)}function Un(e,t){return lr(e,t,new Set),JSON.parse(JSON.stringify(e))}function zn(e){return`${e.code}\x00${e.timeframe}\x00${e.barTime}`}function J(e){return e.longToday+e.longYesterday-e.shortToday-e.shortYesterday}function ur(e){if(!Vn(e))throw Error("\u7F3A\u5C11\u7B56\u7565\u5B9E\u4F8B\u914D\u7F6E");let{id:t,vtSymbol:r,setting:n}=e,i=Object.keys(e).find((o)=>o!=="id"&&o!=="vtSymbol"&&o!=="setting");if(i)throw Error(`\u7B56\u7565\u5B9E\u4F8B\u914D\u7F6E\u4E0D\u652F\u6301\u5B57\u6BB5 "${i}"`);if(typeof t!=="string"||!we.test(t))throw Error("\u7B56\u7565 id \u5FC5\u987B\u4E3A 1-64 \u4F4D\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u70B9\u3001\u4E0B\u5212\u7EBF\u6216\u8FDE\u5B57\u7B26\uFF0C\u5E76\u4EE5\u5B57\u6BCD\u6216\u6570\u5B57\u5F00\u5934");if(typeof r!=="string")throw Error(`\u7B56\u7565 ${t} \u7F3A\u5C11 vtSymbol`);if(!Vn(n))throw Error(`\u7B56\u7565 ${t} \u7684 setting \u5FC5\u987B\u662F\u5BF9\u8C61`);return{id:t,vtSymbol:K(r).vtSymbol,setting:Un(n,`\u7B56\u7565 ${t} setting`)}}function Vo(){let e=nr(!0);return{getBars:async(t,r)=>{let n=xr(r.timeframe,r.count),i=await e.getLatestBars(t,"1min",n),o=ke(i,r.timeframe,r.count);if(o.selectedCount<r.count)throw Error(`\u5408\u7EA6 ${t} \u9884\u70ED\u6570\u636E\u4E0D\u8DB3\uFF1A\u9700\u8981 ${r.count} \u6839\u95ED\u5408 ${r.timeframe} Bar\uFF0C\u4EC5\u6709 ${o.availableCount} \u6839`);return o.bars},destroy:()=>e.destroy()}}class mr{strategies=new Map;instanceMap=new Map;orderOwners=new Map;terminalPaperOrders=new Set;liveOrders=new Map;liveTradeIds=new Set;pending=new Map;deps;cleanups=[];eventQueue=[];dispatching=!1;initializing=null;liveTradingDay="";timerCursorMs=null;eventTimeMs=null;tradeClaimContext=null;constructor(e={}){if(this.deps={createWarmupProvider:Vo,stateStore:new $e,live:null,paperExecution:null,recordTrade:null,orderRisk:new He,executionLimits:{maxOrderVolume:re.trading.maxOrderVolume,maxPosition:re.trading.maxPosition,maxActiveOpenOrders:re.trading.maxActiveOpenOrders},log:(t)=>console.log(t),now:Date.now,schedule:pt,createPaperSessionId:No,observe:()=>{},emitHook:null,...e},this.deps.live)this.cleanups.push(this.deps.live.onOrder((t)=>this.enqueue(()=>{this.deps.orderRisk.recordOrder("live",t,t.identity.tradingDay),this.handleLiveOrder(t)})),this.deps.live.onTrade((t)=>this.enqueue(()=>{this.deps.orderRisk.recordTrade("live",t),this.handleLiveTrade(t)})),this.deps.live.onOrderError((t)=>this.enqueue(()=>this.handleLiveError(t))),this.deps.live.onReconciled((t)=>this.handleLiveReconciled(t)),this.deps.live.onFault((t)=>this.enqueue(()=>this.handleLiveFault(t))))}prepare(e){if(e.mode!=="paper"&&e.mode!=="live")throw Error("\u7B56\u7565 mode \u5FC5\u987B\u662F paper \u6216 live");let t=ur(e.instance),r=K(t.vtSymbol),n=Se(e.definition,t.id,t.setting),i={id:t.id,name:n.ctor.name||t.id,ctor:n.ctor,instance:n.instance,runtime:n.runtime,vtSymbol:r.vtSymbol,instrumentID:r.instrumentID,setting:n.effectiveSetting,enabled:e.enabled??e.mode==="paper",mode:e.mode,sourcePath:e.sourcePath,hash:e.hash,artifact:e.artifact,loadedAt:e.loadedAt,inventory:cr(),orders:new Map,inited:!1,trading:!1,fault:null,expectedLivePos:null,expectedLiveInventory:null,tradingDay:"",stateTimer:null,warmupRequests:[],paperSessionId:this.deps.createPaperSessionId(),paperOrderCounter:0,paperTradeCounter:0,seenTrades:new Set,tradeVolumeByOrder:new Map,eventHandlers:{timer:new Set,trades:new Set}};return n.runtime.bindContext({ctaEngine:this,strategyName:t.id,vtSymbol:r.vtSymbol,mode:e.mode,setting:n.effectiveSetting}),this.assertLiveInstrumentOwner(i),this.instanceMap.set(n.instance,i),i}addRestored(e){let t=ur(e.instance),r=B(t.id,e.mode);if(this.strategies.has(r))throw Error(`\u7B56\u7565\u5B9E\u4F8B\u91CD\u590D: ${t.id} (${e.mode})`);let n=this.prepare(e);return this.strategies.set(r,n),n}assertStateCompatible(e){this.deps.stateStore.assertCompatible(e.id,e.mode,e.vtSymbol,e.ctor.variables??[])}async warmup(){for(let e of this.strategies.values())if(!e.inited)await this.warmupOne(e)}canInitialize(){return this.deps.live?.phase==="running"}async prepareMetadata(){for(let e of this.strategies.values())await this.prepareContractMetadata(e)}async preparePendingMetadata(e,t){await this.prepareContractMetadata(this.requirePending(e,t).running)}beginReplace(e){let t=ur(e.instance),r=B(t.id,e.mode),n=this.strategies.get(r);if(n?.trading)throw Error(`\u7B56\u7565 ${n.id} \u8FD0\u884C\u4E2D\uFF0C\u8BF7\u5148\u505C\u6B62\u518D\u66F4\u65B0`);if(n)this.assertOrdersSettled(n.id,n.mode);if(n&&n.vtSymbol!==t.vtSymbol)throw Error(`\u7B56\u7565 ${n.id} \u5DF2\u7ED1\u5B9A ${n.vtSymbol}\uFF0C\u66F4\u6362\u5408\u7EA6\u5FC5\u987B\u4F7F\u7528\u65B0\u7684\u5B9E\u4F8B ID`);let i=this.prepare(e);if(this.pending.has(r))throw this.instanceMap.delete(i.instance),Error(`\u7B56\u7565 ${i.id} (${i.mode}) \u5DF2\u5728\u52A0\u8F7D\u4E2D`);return this.pending.set(r,{running:i,events:[],replayedBars:new Set,overflowed:!1,warmed:!1}),i}async warmupPending(e,t,{restoreState:r=!0}={}){let n=this.requirePending(e,t);if(await this.warmupOne(n.running,n.replayedBars,!1,r),n.warmed=!0,n.overflowed)throw Error(`\u7B56\u7565 ${e} \u521D\u59CB\u5316\u671F\u95F4\u7F13\u5B58\u884C\u60C5\u8D85\u8FC7 ${qn} \u6761`)}async commitPending(e,t){let r=this.requirePending(e,t),n=B(e,r.running.mode);if(!r.warmed)throw Error(`\u7B56\u7565 ${e} \u5C1A\u672A\u5B8C\u6210\u521D\u59CB\u5316`);if(r.overflowed)throw Error(`\u7B56\u7565 ${e} \u521D\u59CB\u5316\u671F\u95F4\u7F13\u5B58\u6EA2\u51FA`);if(this.drainPending(r),r.running.fault)throw Error(`\u7B56\u7565 ${e} \u521D\u59CB\u5316\u671F\u95F4\u5F02\u5E38: ${r.running.fault}`);this.saveState(r.running);let i=this.strategies.get(n);if(this.strategies.set(n,r.running),this.pending.delete(n),i)await this.dispose(i,!1);return r.running}async commitPendingUninitialized(e,t){let r=this.requirePending(e,t),n=B(e,r.running.mode);if(r.warmed)throw Error(`\u7B56\u7565 ${e} \u5DF2\u521D\u59CB\u5316\uFF0C\u4E0D\u80FD\u6309\u7B49\u5F85\u72B6\u6001\u63D0\u4EA4`);r.events.length=0;let i=this.strategies.get(n);if(this.strategies.set(n,r.running),this.pending.delete(n),i)await this.dispose(i,!1);return r.running}async abortPending(e,t){let r=this.findPending(e,t);if(!r)return;let n=B(e,r.running.mode);this.pending.delete(n),await this.dispose(r.running,!1)}async start(e,t){let r=this.requireRunning(e,t);if(r.trading)return r;try{if(!r.inited||r.fault)throw Error(`\u7B56\u7565 ${e} \u672A\u5B8C\u6210\u521D\u59CB\u5316${r.fault?`: ${r.fault}`:""}`);if(await this.prepareContractMetadata(r),r.mode==="live")await this.prepareLive(r);try{this.invokeStrategySync(r,"onStart",()=>r.instance.onStart())}catch(n){throw this.failStrategy(r,"onStart",n),n}r.trading=!0,this.syncFrameworkState(r);try{this.saveState(r)}catch(n){throw this.failStrategy(r,"state",n),n}return this.writeLog(r.instance,`started mode=${r.mode}`),this.publishHook("strategy.started",this.strategyIdentity(r)),r}catch(n){throw this.publishHook("strategy.start_failed",{...this.strategyIdentity(r),reason:n instanceof Error?n.message:String(n)}),n}}async startAll(){for(let e of this.strategies.values()){if(!e.enabled)continue;try{await this.start(e.id,e.mode)}catch(t){let r=t instanceof Error?t.message:String(t);console.error(`[cta] ${e.id} \u542F\u52A8\u5931\u8D25: ${r}`)}}}async stop(e,{cancelOrders:t=!1},r){let n=this.requireRunning(e,r);if(n.trading){n.trading=!1,this.syncFrameworkState(n);try{this.invokeStrategySync(n,"onStop",()=>n.instance.onStop())}catch(i){this.failStrategy(n,"onStop",i)}if(this.trySaveState(n),this.writeLog(n.instance,"stopped"),!n.fault)this.publishHook("strategy.stopped",this.strategyIdentity(n))}if(this.syncFrameworkState(n),t)this.cancelAllDirect(n);else{let i=[...n.orders.values()].filter(w).length;if(i>0){let o=this.retainedOrdersMessage(i);this.writeLog(n.instance,o),this.publishHook("strategy.orders_retained",{...this.strategyIdentity(n),activeOrders:i,reason:o})}}return n}async stopAll(e={}){for(let t of this.strategies.values())await this.stop(t.id,e,t.mode)}cancelStrategyOrder(e,t,r){let n=this.requireRunning(e,r),i=n.orders.get(t);if(!i)throw Error(`\u59D4\u6258\u4E0D\u5C5E\u4E8E\u7B56\u7565 ${e}: ${t}`);if(!w(i))return;if(n.mode==="paper")this.cancelOrder(n.instance,t);else{if(!this.deps.live)throw Error("\u5B9E\u76D8\u901A\u9053\u4E0D\u53EF\u7528");this.deps.live.cancelOrder(t)}}cancelPaperOrder(e){if(this.terminalPaperOrders.has(e))return{vtOrderId:e,mode:"paper",result:"terminal"};let t=this.orderOwners.get(e);if(!t||t.mode!=="paper")return null;let r=t.orders.get(e);if(!r)return null;if(!w(r))return{vtOrderId:e,mode:"paper",result:"terminal"};return this.cancelStrategyOrder(t.id,e,t.mode),{vtOrderId:e,mode:"paper",result:"requested"}}async remove(e,t,{clearState:r=!1}={}){let n=this.findRunning(e,t);if(!n)return null;if(await this.stop(n.id,{},n.mode),this.assertOrdersSettled(n.id,n.mode),this.strategies.delete(B(n.id,n.mode)),await this.dispose(n,!r),r)this.deps.stateStore.remove(n.id,n.mode);return n}assertOrdersSettled(e,t){let r=this.requireRunning(e,t);if(r.orders.size>0)throw Error(`\u7B56\u7565 ${e} \u4ECD\u6709 ${r.orders.size} \u7B14\u59D4\u6258\u6216\u6210\u4EA4\u56DE\u62A5\u672A\u5B8C\u6210\uFF0C\u8BF7\u7B49\u5F85\u56DE\u62A5\u6536\u9F50\u540E\u91CD\u8BD5`)}async assertResettable(e,t){let r=this.requireRunning(e,t);if(r.trading||r.enabled)throw Error(`\u7B56\u7565 ${r.id}@${r.mode} \u5C1A\u672A\u505C\u6B62\uFF0C\u8BF7\u5148\u6267\u884C vibek strategy stop ${r.id}@${r.mode}`);if(this.assertOrdersSettled(r.id,r.mode),r.mode!=="live")return r;let n=this.deps.live;if(!n||n.phase!=="running")throw Error("TD \u672A\u8FD0\u884C\uFF0C\u65E0\u6CD5\u91CD\u7F6E Live \u7B56\u7565\u72B6\u6001");let o=(await n.queryOrders(r.instrumentID)).filter(U);if(o.length>0)throw Error(`\u5238\u5546\u5B58\u5728 ${o.length} \u7B14\u6D3B\u52A8\u59D4\u6258\uFF0C\u8BF7\u5148\u4EBA\u5DE5\u5904\u7406`);let s=await n.queryPositions(r.instrumentID);if(s.phase!=="running")throw Error("\u67E5\u8BE2\u5B9E\u76D8\u6301\u4ED3\u5931\u8D25");let a=this.inventoryFromBroker(s.positions,r.instrumentID),d=this.sidePositions(a);if(d.long!==0||d.short!==0)throw Error(`\u5408\u7EA6 ${r.instrumentID} \u4ECD\u6709\u5238\u5546\u6301\u4ED3 \u591A${d.long}/\u7A7A${d.short}\uFF0C\u4E0D\u80FD\u91CD\u7F6E\u672C\u5730\u72B6\u6001`);return r}async close({persist:e=!0}={}){if(e)await this.stopAll({cancelOrders:!0});let t=[...this.strategies.values()],r=[...this.pending.values()].map((n)=>n.running);this.strategies.clear(),this.pending.clear();for(let n of[...t,...r])await this.dispose(n,e);for(let n of this.cleanups.splice(0))n();this.deps.orderRisk.close({persist:e}),this.liveOrders.clear(),this.terminalPaperOrders.clear(),this.liveTradeIds.clear(),this.tradeClaimContext=null,this.timerCursorMs=null,this.eventTimeMs=null}has(e,t){return this.strategies.has(B(e,t))}resolve(e,t){return this.requireRunning(e,t)}setEnabled(e,t,r){let n=this.requireRunning(e,r);if(n.enabled===t)return n;return n.enabled=t,this.publishHook(t?"strategy.enabled":"strategy.disabled",this.strategyIdentity(n)),n}instruments(){return[...new Set([...this.strategies.values()].map((e)=>e.instrumentID))]}instrumentDeclarations(){return[...this.strategies.values()].map((e)=>this.declaration(e))}instrumentDeclarationsWithPending(e,t){let r=this.requirePending(e,t).running;return[...this.strategies.values()].map((n)=>this.declaration(n)).concat(this.declaration(r))}instrumentsWithPending(e,t){let r=this.requirePending(e,t).running;return[...new Set([...this.strategies.values()].map((n)=>n.instrumentID).concat(r.instrumentID))]}handleBar(e){for(let t of this.strategies.values())if(t.instrumentID===e.code&&t.inited)this.enqueue(()=>this.callStrategy(t,"onBar",()=>t.instance.onBar(e)));this.queuePending({kind:"bar",value:e},e.code)}handleTick(e){this.dispatchMarketEvent(e,[])}handleMarketEvent(e,t){this.dispatchMarketEvent(e,t)}handlePaperExternalTrade(e,t){if(e.type!=="order"||e.vtOrderId!==t.vtOrderId||e.vtSymbol!==t.vtSymbol||e.direction!==t.direction||e.offset!==t.offset||e.status!=="allTraded"||e.traded!==e.volume||t.volume!==e.volume)throw Error("\u5916\u90E8 Paper \u8BA2\u5355\u4E0E\u6210\u4EA4\u4E0D\u4E00\u81F4");let r={order:{...e},origin:"external",tradingDay:t.tradingDay,tradedVolume:t.volume,claimable:e.offset==="open",ownerId:null};this.enqueue(()=>{this.dispatchTrades("paper",{...t},{...e},"external",{observed:r,firstTrade:!0,allowPaper:!0})})}handleTimer(e){if(!Number.isFinite(e)||e<0)throw Error("Timer \u65F6\u95F4\u5FC5\u987B\u662F\u975E\u8D1F\u6709\u9650\u6570");let t=Math.floor(e/1000)*1000;if(this.timerCursorMs===null){this.timerCursorMs=t;return}if(t<this.timerCursorMs)throw Error("Timer \u65F6\u95F4\u4E0D\u80FD\u56DE\u9000");for(let r=this.timerCursorMs+1000;r<=t;r+=1000)this.dispatchTimer(r);this.timerCursorMs=t}setTimerClock(e){if(!Number.isFinite(e)||e<0)throw Error("Timer \u65F6\u95F4\u5FC5\u987B\u662F\u975E\u8D1F\u6709\u9650\u6570");this.timerCursorMs=Math.floor(e/1000)*1000}dispatchMarketEvent(e,t){let r=e.tradingDay??e.actionDay??"";this.deps.orderRisk.setTradingDay("paper",r);let n=new Map;for(let i of t){let o=n.get(i.code)??[];o.push(i),n.set(i.code,o)}for(let i of this.strategies.values()){if(!i.inited)continue;let o=i.instrumentID===e.code,s=n.get(i.instrumentID)??[];if(!o&&s.length===0)continue;this.enqueue(()=>{if(o&&i.mode==="paper")this.rollPaperDay(i,r),this.matchPaperOrders(i,e);for(let a of s){if(i.fault)break;this.callStrategy(i,"onBar",()=>i.instance.onBar(a))}if(o)this.callStrategy(i,"onTick",()=>i.instance.onTick(e))})}for(let i of t)this.queuePending({kind:"bar",value:i},i.code);this.queuePending({kind:"tick",value:e},e.code)}snapshot(e={}){return[...this.strategies.values()].map((t)=>{let r=[...t.orders.values()].filter(w).map((n)=>({...n,type:"order"}));return{id:t.id,name:t.name,sourcePath:t.sourcePath,hash:t.hash,loadedAt:t.loadedAt,mode:t.mode,vtSymbol:t.vtSymbol,instrumentID:t.instrumentID,setting:t.setting,enabled:t.enabled,inited:t.inited,trading:t.trading,pos:J(t.inventory),inventory:{...t.inventory},activeOrders:r.length,activeOrderDetails:r,warning:!t.trading&&r.length>0?this.retainedOrdersMessage(r.length):null,fault:t.fault,...e.variables?this.variablesSnapshot(t):{}}})}variablesSnapshot(e){try{return{variables:this.serializeVariables(e),variablesError:null}}catch(t){return{variables:null,variablesError:t instanceof Error?t.message:String(t)}}}riskSnapshot(){return this.deps.orderRisk.snapshot()}sendOrder(e,t,r,n,i){let o=this.requireInstance(e);if(!o.trading||o.fault)return[];if(!Number.isFinite(n)||n<=0||!Number.isInteger(i)||i<=0){let l=`price/volume \u975E\u6CD5 (${n}, ${i})`;return this.writeLog(e,`\u62D2\u7EDD\u59D4\u6258\uFF1A${l}`),this.emitOrderError(o,"invalidRequest",t,r,n,i,l),[]}if(i>this.deps.executionLimits.maxOrderVolume){let l=`\u6570\u91CF ${i} \u8D85\u8FC7\u5355\u7B14\u4E0A\u9650 ${this.deps.executionLimits.maxOrderVolume}`;return this.writeLog(e,`\u62D2\u7EDD\u59D4\u6258\uFF1A${l}`),this.emitRiskRejection(o,"max_order_volume",t,r,n,i,l),this.emitOrderError(o,"maxOrderVolume",t,r,n,i,l),[]}let s=this.resolveOrderParts(o,t,r,n,i);if(s.length===0)return[];if(r==="open"){if([...o.orders.values()].filter((f)=>f.offset==="open"&&w(f)).length>=this.deps.executionLimits.maxActiveOpenOrders){let f=`\u6D3B\u52A8\u5F00\u4ED3\u59D4\u6258\u8FBE\u5230\u4E0A\u9650 ${this.deps.executionLimits.maxActiveOpenOrders}`;return this.writeLog(e,`\u62D2\u7EDD\u5F00\u4ED3\uFF1A${f}`),this.emitRiskRejection(o,"max_active_open_orders",t,r,n,i,f),this.emitOrderError(o,"maxActiveOpenOrders",t,r,n,i,f),[]}}let a=this.roundPrice(o,n);if(!Number.isFinite(a)||a<=0){let l=`price/volume \u975E\u6CD5 (\u820D\u5165\u540E\u4EF7\u683C ${a}\uFF0C\u539F\u59CB ${n})`;return this.writeLog(e,`\u62D2\u7EDD\u59D4\u6258\uFF1A${l}`),this.emitOrderError(o,"invalidRequest",t,r,n,i,l),[]}let d=o.mode==="live"?this.liveTradingDay||o.tradingDay||this.deps.orderRisk.tradingDay("live"):o.tradingDay||this.deps.orderRisk.tradingDay("paper"),c=this.deps.orderRisk.checkOpenDetailed({mode:o.mode,tradingDay:d,vtSymbol:o.vtSymbol,direction:t,offset:r,price:a,volume:i});if(c)return this.writeLog(e,`\u62D2\u7EDD\u5F00\u4ED3\uFF1A${c.message}`),this.emitRiskRejection(o,c.code,t,r,a,i,c.message),this.emitOrderError(o,"riskRejected",t,r,n,i,c.message),[];let u=[];for(let l of s){let f=this.submitPhysical(o,t,l.offset,a,l.volume);if(f)u.push(f)}return u}cancelOrder(e,t){let r=this.requireInstance(e),n=r.orders.get(t);if(!n||!w(n))return;if(r.mode==="paper")this.deps.paperExecution?.onCancelRequested?.(Object.freeze({...n})),n.status="cancelled",n.statusMessage="paper cancelled",this.queueOrder(r,n);else try{this.deps.live?.cancelOrder(t)}catch(i){this.handleLiveError({vtOrderId:t,orderRef:t.slice(4),instrumentID:r.instrumentID,operation:"cancel",message:i instanceof Error?i.message:String(i)})}}cancelAll(e){this.cancelAllDirect(this.requireInstance(e))}liquidatePaper(e,t,r){let n=this.requireRunning(e,"paper");if(n.mode!=="paper")throw Error(`\u7B56\u7565 ${e} \u4E0D\u662F Paper \u6A21\u5F0F`);this.deps.orderRisk.setTradingDay("paper",t.tradingDay??t.actionDay??n.tradingDay);let i=n.trading;n.trading=!1,this.syncFrameworkState(n),this.cancelAllDirect(n);let o=[{direction:"short",offset:"closeYesterday",volume:n.inventory.longYesterday},{direction:"short",offset:"closeToday",volume:n.inventory.longToday},{direction:"long",offset:"closeYesterday",volume:n.inventory.shortYesterday},{direction:"long",offset:"closeToday",volume:n.inventory.shortToday}],s=[];for(let a of o){if(a.volume<=0)continue;let d=a.direction==="long"?t.askPrice1??t.lastPrice:t.bidPrice1??t.lastPrice,c={vtOrderId:`PAPERLIQ.${n.id}.${n.paperSessionId}.${++n.paperOrderCounter}`,vtSymbol:n.vtSymbol,direction:a.direction,offset:a.offset,price:d,volume:a.volume,traded:a.volume,status:"allTraded",statusMessage:r,datetime:t.datetime??this.deps.now()},u=this.paperFill(c,t,a.volume,"marginLiquidation");c.price=u.price,n.orders.set(c.vtOrderId,c),this.orderOwners.set(c.vtOrderId,n),this.emitOrder(n,c);let l={vtTradeId:`PAPERLIQTRADE.${n.id}.${n.paperSessionId}.${++n.paperTradeCounter}`,vtOrderId:c.vtOrderId,vtSymbol:n.vtSymbol,direction:a.direction,offset:a.offset,price:u.price,volume:a.volume,datetime:t.datetime??this.deps.now(),tradingDay:t.tradingDay??t.actionDay??""};this.deps.paperExecution?.onTrade?.(l,u,"marginLiquidation"),this.applyTrade(n,l),s.push(l)}if(i)try{this.invokeStrategySync(n,"onStop",()=>n.instance.onStop())}catch(a){this.failStrategy(n,"onStop",a)}return this.cancelAllDirect(n),this.syncFrameworkState(n),this.scheduleState(n,!0),this.writeLog(n.instance,`\u5F3A\u5236\u5E73\u4ED3\u5E76\u505C\u6B62: ${r}`),s}loadBars(e,t,r){let n=this.requireInitializing(e,"loadBars");if(!t||typeof t!=="object"||Array.isArray(t))throw Error("loadBars request \u5FC5\u987B\u662F\u5BF9\u8C61");if(!Z(t.timeframe))throw Error(`loadBars timeframe \u4E0D\u53D7\u652F\u6301: ${String(t.timeframe)}`);if(!Number.isInteger(t.count)||t.count<=0)throw Error("loadBars count \u5FC5\u987B\u4E3A\u6B63\u6574\u6570");if(n.warmupRequests.length>0)throw Error("\u6BCF\u4E2A\u7B56\u7565\u53EA\u80FD\u767B\u8BB0\u4E00\u4E2A loadBars \u8BF7\u6C42");n.warmupRequests.push({timeframe:t.timeframe,count:t.count,callback:r})}registerEvent(e,t,r){let n=this.requireInitializing(e,"registerEvent");if(t!=="timer"&&t!=="trades")throw Error(`\u4E0D\u652F\u6301\u7684\u7B56\u7565\u4E8B\u4EF6: ${String(t)}`);if(typeof r!=="function")throw Error("\u7B56\u7565\u4E8B\u4EF6 handler \u5FC5\u987B\u662F\u51FD\u6570");let i=n.eventHandlers[t];i.add(r);let o=!0;return()=>{if(!o)return;o=!1,i.delete(r)}}claimOrder(e,t){let r=this.requireInstance(e),n=this.tradeClaimContext;if(r.mode!=="live"&&!(r.mode==="paper"&&n?.allowPaper))return{ok:!1,reason:"\u53EA\u6709 Live \u7B56\u7565\u6216\u4EA4\u4E92\u56DE\u6D4B\u53EF\u4EE5\u8BA4\u9886\u5916\u90E8\u8BA2\u5355"};if(!r.inited||!r.trading||r.fault)return{ok:!1,reason:"\u7B56\u7565\u672A\u5904\u4E8E\u4EA4\u6613\u72B6\u6001"};if(!n||n.running!==r||n.trade.vtOrderId!==t)return{ok:!1,reason:"claimOrder \u53EA\u80FD\u5728\u5F53\u524D\u8BA2\u5355\u7684 trades \u4E8B\u4EF6\u4E2D\u8C03\u7528"};let{observed:i,trade:o}=n;if(i.ownerId!==null||this.orderOwners.has(t))return{ok:!1,reason:"\u8BA2\u5355\u5DF2\u7ECF\u5F52\u5C5E\u7B56\u7565"};let s=i.order;if(!s)return{ok:!1,reason:"\u6210\u4EA4\u5173\u8054\u7684\u8BA2\u5355\u4FE1\u606F\u5C1A\u672A\u5B8C\u6574"};if(!n.firstTrade)return{ok:!1,reason:"\u53EA\u80FD\u8BA4\u9886\u5916\u90E8\u8BA2\u5355\u7684\u9996\u7B14\u65B0\u6210\u4EA4"};if(i.origin!=="external"||s.offset!=="open"||o.offset!=="open")return{ok:!1,reason:"\u53EA\u80FD\u8BA4\u9886\u5916\u90E8\u5F00\u4ED3\u8BA2\u5355"};if(s.vtSymbol!==r.vtSymbol||o.vtSymbol!==r.vtSymbol)return{ok:!1,reason:"\u8BA2\u5355\u5408\u7EA6\u4E0E\u7B56\u7565\u4E0D\u4E00\u81F4"};if(s.direction!==o.direction||o.volume>s.volume||s.status==="rejected")return{ok:!1,reason:"\u8BA2\u5355\u4E0E\u6210\u4EA4\u4FE1\u606F\u4E0D\u4E00\u81F4"};if(r.orders.size>0||Object.values(r.inventory).some((d)=>d!==0))return{ok:!1,reason:"\u7B56\u7565\u5DF2\u6709\u6301\u4ED3\u6216\u672A\u5B8C\u6210\u7684\u8BA2\u5355"};if(r.mode==="live")this.rollLiveDay(r,o.tradingDay);else this.rollPaperDay(r,o.tradingDay);let a={...s,traded:Math.max(s.traded,o.volume)};return i.ownerId=r.id,r.orders.set(t,a),this.orderOwners.set(t,r),this.queueOrder(r,a),this.applyTrade(r,{...o},!0,!1),this.writeLog(r.instance,`claimed external order ${t} on trade ${o.vtTradeId}`),{ok:!0}}writeLog(e,t){this.deps.log(`[cta:${e.strategyName}:${e.mode}] ${t}`)}emitHook(e,t,r){let n=this.requireInstance(e),i=this.eventTimeMs??this.deps.now(),o=er("strategy.event",{...this.strategyIdentity(n),name:t,data:r});fe({type:"hook",id:"00000000-0000-4000-8000-000000000000",event:"strategy.event",account:"a".repeat(64),occurredAt:i,payload:o});try{this.deps.emitHook?.("strategy.event",o,i)}catch(s){this.writeLog(e,`Hook \u6295\u9012\u5931\u8D25 name=${o.name}: ${s instanceof Error?s.message:String(s)}`)}}getCurrentTime(e){return this.requireInstance(e),this.eventTimeMs??this.deps.now()}getContract(e){let t=this.requireInstance(e),r=t.vtSymbol.split(".")[1],n=this.deps.live?.getInstrument(t.instrumentID);if(!n)throw Error(`\u5408\u7EA6 ${t.vtSymbol} Contract \u5C1A\u672A\u51C6\u5907`);if(n.instrumentID.toUpperCase()!==t.instrumentID)throw Error(`\u5408\u7EA6\u4EE3\u7801\u4E0D\u4E00\u81F4\uFF1A\u7B56\u7565 ${t.instrumentID}\uFF0C\u5143\u6570\u636E ${n.instrumentID}`);if(n.exchangeID.toUpperCase()!==r)throw Error(`\u5408\u7EA6\u4EA4\u6613\u6240\u4E0D\u4E00\u81F4\uFF1A\u7B56\u7565 ${r}\uFF0C\u5143\u6570\u636E ${n.exchangeID}`);if(!Number.isFinite(n.priceTick)||n.priceTick<=0||!Number.isSafeInteger(n.volumeMultiple)||n.volumeMultiple<=0||!Number.isSafeInteger(n.minLimitOrderVolume)||n.minLimitOrderVolume<=0||!Number.isSafeInteger(n.maxLimitOrderVolume)||n.maxLimitOrderVolume<n.minLimitOrderVolume||!Number.isFinite(n.longMarginRatio)||n.longMarginRatio<0||!Number.isFinite(n.shortMarginRatio)||n.shortMarginRatio<0)throw Error(`\u5408\u7EA6\u89C4\u683C\u975E\u6CD5: ${t.vtSymbol}`);return Object.freeze({vtSymbol:`${n.instrumentID}.${n.exchangeID}`.toUpperCase(),instrumentID:n.instrumentID.toUpperCase(),exchangeID:n.exchangeID.toUpperCase(),productID:n.productID.toUpperCase(),name:n.instrumentName,priceTick:n.priceTick,size:n.volumeMultiple,minOrderVolume:n.minLimitOrderVolume,maxOrderVolume:n.maxLimitOrderVolume,longMarginRatio:n.longMarginRatio,shortMarginRatio:n.shortMarginRatio,isTrading:n.isTrading===1})}getCommission(e){let t=this.requireInstance(e),r=t.vtSymbol.split(".")[1],n=this.deps.live?.getCommission(t.instrumentID,r);if(!n)throw Error(`\u5408\u7EA6 ${t.vtSymbol} Commission \u5C1A\u672A\u51C6\u5907`);let i=this.getContract(e);if(n.instrumentID.toUpperCase()!==t.instrumentID&&n.instrumentID.toUpperCase()!==i.productID||n.exchangeID&&n.exchangeID.toUpperCase()!==r)throw Error(`\u624B\u7EED\u8D39\u5408\u7EA6\u4E0D\u4E00\u81F4: ${t.vtSymbol}`);if([n.openRatioByMoney,n.openRatioByVolume,n.closeRatioByMoney,n.closeRatioByVolume,n.closeTodayRatioByMoney,n.closeTodayRatioByVolume].some((o)=>!Number.isFinite(o)||o<0))throw Error(`\u624B\u7EED\u8D39\u7387\u975E\u6CD5: ${t.vtSymbol}`);return Object.freeze({open:Object.freeze({ratioByMoney:n.openRatioByMoney,perLot:n.openRatioByVolume}),close:Object.freeze({ratioByMoney:n.closeRatioByMoney,perLot:n.closeRatioByVolume}),closeToday:Object.freeze({ratioByMoney:n.closeTodayRatioByMoney,perLot:n.closeTodayRatioByVolume})})}getPriceTick(e){return this.getContract(e).priceTick}getSize(e){return this.getContract(e).size}async warmupOne(e,t,r=!0,n=!0){e.warmupRequests=[],this.clearEventHandlers(e),e.inited=!1,e.trading=!1,this.syncFrameworkState(e),await this.prepareContractMetadata(e),this.initializing=e;try{this.invokeStrategySync(e,"onInit",()=>e.instance.onInit())}catch(i){throw Error(`\u7B56\u7565 ${e.id} onInit \u5931\u8D25: ${i instanceof Error?i.message:String(i)}`)}finally{this.initializing=null}if(e.warmupRequests.length>0){let i=this.deps.createWarmupProvider();try{for(let o of e.warmupRequests){let s={timeframe:o.timeframe,count:o.count},a=await i.getBars(e.instrumentID,s),d=Et(a,o.timeframe);if(d<o.count)throw Error(`\u5408\u7EA6 ${e.instrumentID} \u9884\u70ED\u6570\u636E\u4E0D\u5B8C\u6574\uFF1A\u9700\u8981 ${o.count} \u6839\u95ED\u5408 ${o.timeframe} Bar\uFF0C\u5B9E\u9645 ${d} \u6839`);for(let c of a)t?.add(zn(c)),this.invokeStrategySync(e,"loadBars callback",()=>o.callback(c));this.writeLog(e.instance,`loadBars ${d} ${o.timeframe} bars / ${a.length} source 1min bars`)}}catch(o){throw Error(`\u7B56\u7565 ${e.id} \u521D\u59CB\u5316\u6570\u636E\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`)}finally{i.destroy()}}if(n)this.restoreState(e);if(this.serializeVariables(e),e.inited=!0,this.syncFrameworkState(e),r)this.saveState(e)}restoreState(e){let t=this.deps.stateStore.read(e.id,e.mode);if(this.observe(e,{type:"state",operation:"read",value:t?{pos:t.pos,inventory:{...t.inventory},variables:structuredClone(t.variables),tradingDay:t.tradingDay}:null}),!t)return;if(t.strategyId!==e.id||t.mode!==e.mode)throw Error(`\u7B56\u7565 ${e.id} \u72B6\u6001\u8EAB\u4EFD\u4E0D\u5339\u914D`);if(t.vtSymbol!==e.vtSymbol)throw Error(`\u7B56\u7565 ${e.id} \u7684\u72B6\u6001\u5C5E\u4E8E ${t.vtSymbol}\uFF0C\u4E0D\u80FD\u6062\u590D\u5230 ${e.vtSymbol}\uFF1B\u8BF7\u4F7F\u7528\u65B0\u7684\u5B9E\u4F8B ID`);if(e.runtime.restoreVariables(t.variables),e.expectedLivePos=t.pos,e.tradingDay=t.tradingDay,e.mode==="paper")e.inventory=t.inventory,this.syncFrameworkState(e);else e.inventory=t.inventory,e.expectedLiveInventory={...e.inventory},this.syncFrameworkState(e)}async prepareLive(e){let t=this.deps.live;if(!t||t.phase!=="running")throw Error("TD \u672A\u8FD0\u884C\uFF0C\u65E0\u6CD5\u542F\u52A8 Live \u7B56\u7565");let r=e.vtSymbol.split(".")[1],n=await t.prepareInstrument(e.instrumentID,r);if(n.exchangeID.toUpperCase()!==r)throw Error(`\u5408\u7EA6\u4EA4\u6613\u6240\u4E0D\u4E00\u81F4\uFF1A\u7B56\u7565 ${r}\uFF0CCTP ${n.exchangeID}`);let i=await t.queryOrders(e.instrumentID);for(let u of i)this.observeLiveOrder(this.orderDataFromBroker(u),u.identity.tradingDay,!1);let o=i.filter(U);if(o.length>0)throw Error(`\u5238\u5546\u5B58\u5728 ${o.length} \u7B14\u6D3B\u52A8\u59D4\u6258\uFF0C\u8BF7\u5148\u4EBA\u5DE5\u5904\u7406`);let s=await t.queryPositions(e.instrumentID);if(s.phase!=="running")throw Error("\u67E5\u8BE2\u5B9E\u76D8\u6301\u4ED3\u5931\u8D25");let a=this.inventoryFromBroker(s.positions,e.instrumentID),d=J(a),c=this.sidePositions(a);if(e.expectedLivePos===null&&(c.long!==0||c.short!==0))throw Error(`\u5408\u7EA6 ${e.instrumentID} \u5DF2\u6709\u5238\u5546\u6301\u4ED3 \u591A${c.long}/\u7A7A${c.short}\uFF0C\u65B0\u7B56\u7565\u62D2\u7EDD\u8BA4\u9886`);if(e.expectedLivePos!==null&&e.expectedLivePos!==d)throw Error(`\u6301\u4ED3\u4E0D\u4E00\u81F4\uFF1A\u672C\u5730 ${e.expectedLivePos}\uFF0C\u5238\u5546 ${d}`);if(e.expectedLiveInventory){let u=this.sidePositions(e.expectedLiveInventory);if(u.long!==c.long||u.short!==c.short)throw Error(`\u6301\u4ED3\u4E0D\u4E00\u81F4\uFF1A\u672C\u5730\u591A${u.long}/\u7A7A${u.short}\uFF0C\u5238\u5546\u591A${c.long}/\u7A7A${c.short}`)}e.inventory=a,e.expectedLivePos=d,e.expectedLiveInventory={...a},e.tradingDay=this.liveTradingDay||s.positions.find((u)=>u.instrumentID===e.instrumentID)?.tradingDay||e.tradingDay,this.syncFrameworkState(e)}async prepareContractMetadata(e){let t=this.deps.live;if(!t||t.phase!=="running")throw Error(`TD \u672A\u8FD0\u884C\uFF0C\u65E0\u6CD5\u521D\u59CB\u5316\u7B56\u7565 ${e.id}`);let r=e.vtSymbol.split(".")[1],n=await t.prepareInstrument(e.instrumentID,r);if(n.exchangeID.toUpperCase()!==r)throw Error(`\u5408\u7EA6\u4EA4\u6613\u6240\u4E0D\u4E00\u81F4\uFF1A\u7B56\u7565 ${r}\uFF0CCTP ${n.exchangeID}`);if(tt(n.exchangeID,n.productID),!this.getContract(e.instance).isTrading)throw Error(`\u5408\u7EA6\u5F53\u524D\u4E0D\u53EF\u4EA4\u6613: ${e.vtSymbol}`);await t.prepareCommission(e.instrumentID,r)}declaration(e){let t=e.vtSymbol.split(".")[1];return{instrumentID:e.instrumentID,exchangeID:t,vtSymbol:e.vtSymbol}}inventoryFromBroker(e,t){let r=cr();for(let n of e){if(n.instrumentID.toUpperCase()!==t.toUpperCase())continue;if(n.hedgeFlag!=="1"&&n.volume!==0)throw Error(`\u6682\u4E0D\u652F\u6301\u975E\u6295\u673A\u6301\u4ED3: ${n.instrumentID}/${n.hedgeFlag}`);if(!Number.isInteger(n.todayVolume)||n.todayVolume<0||!Number.isInteger(n.yesterdayVolume)||n.yesterdayVolume<0||n.todayVolume+n.yesterdayVolume!==n.volume)throw Error(`\u5238\u5546\u6301\u4ED3\u6570\u636E\u975E\u6CD5: ${n.instrumentID}`);if(n.direction==="long")r.longToday+=n.todayVolume,r.longYesterday+=n.yesterdayVolume;else r.shortToday+=n.todayVolume,r.shortYesterday+=n.yesterdayVolume}return r}sidePositions(e){return{long:e.longToday+e.longYesterday,short:e.shortToday+e.shortYesterday}}resolveOrderParts(e,t,r,n,i){if(r==="open"){let c=t==="long"?e.inventory.longToday+e.inventory.longYesterday:e.inventory.shortToday+e.inventory.shortYesterday,u=[...e.orders.values()].filter((l)=>l.offset==="open"&&l.direction===t).reduce((l,f)=>l+this.unsettledOrderVolume(e,f),0);if(c+u+i>this.deps.executionLimits.maxPosition){let l=`\u9884\u8BA1\u6301\u4ED3 ${c+u+i} \u8D85\u8FC7\u4E0A\u9650 ${this.deps.executionLimits.maxPosition}`;return this.writeLog(e.instance,`\u62D2\u7EDD\u5F00\u4ED3\uFF1A${l}`),this.emitRiskRejection(e,"max_position",t,r,n,i,l),this.emitOrderError(e,"maxPosition",t,r,n,i,l),[]}return[{offset:r,volume:i}]}let o=this.availableClose(e,t);if(r==="closeToday"){if(i>o.today)return this.rejectInsufficientClose(e,t,r,n,i,o);return[{offset:r,volume:i}]}if(r==="closeYesterday"){if(i>o.yesterday)return this.rejectInsufficientClose(e,t,r,n,i,o);return[{offset:r,volume:i}]}if(i>o.today+o.yesterday)return this.rejectInsufficientClose(e,t,r,n,i,o);if(!Pr(e.vtSymbol))return[{offset:"close",volume:i}];let s=[],a=Math.min(i,o.yesterday);if(a>0)s.push({offset:"closeYesterday",volume:a});let d=i-a;if(d>0)s.push({offset:"closeToday",volume:d});return s}rejectInsufficientClose(e,t,r,n,i,o){let s=`\u8BF7\u6C42 ${i}\uFF0C\u53EF\u7528\u4ECA ${o.today}/\u6628 ${o.yesterday}`;return this.writeLog(e.instance,`\u62D2\u7EDD\u5E73\u4ED3\uFF1A${s}`),this.emitOrderError(e,"insufficientPosition",t,r,n,i,s),[]}availableClose(e,t){let r=t==="short"?e.inventory.longToday:e.inventory.shortToday,n=t==="short"?e.inventory.longYesterday:e.inventory.shortYesterday,i=0;for(let s of e.orders.values()){if(s.direction!==t||s.offset==="open")continue;let a=this.unsettledOrderVolume(e,s);if(a<=0)continue;if(s.offset==="closeToday")r-=a;else if(s.offset==="closeYesterday")n-=a;else i+=a}r=Math.max(0,r),n=Math.max(0,n);let o=Math.min(n,i);return n-=o,r=Math.max(0,r-(i-o)),{today:r,yesterday:n}}unsettledOrderVolume(e,t){let r=e.tradeVolumeByOrder.get(t.vtOrderId)??0,n=w(t)?t.volume:t.traded;return Math.max(0,n-r)}submitPhysical(e,t,r,n,i){let o;if(e.mode==="paper")o=`PAPER.${e.id}.${e.paperSessionId}.${++e.paperOrderCounter}`;else{let a=this.deps.live;if(!a)return this.emitOrderError(e,"submitFailed",t,r,n,i,"\u5B9E\u76D8\u901A\u9053\u4E0D\u53EF\u7528"),null;try{o=a.submitOrder({strategyId:e.id,instrumentID:e.instrumentID,direction:t==="long"?"buy":"sell",offsetFlag:r,price:n,volume:i}).vtOrderId}catch(d){let c=d instanceof Error?d.message:String(d);return this.writeLog(e.instance,`\u5B9E\u76D8\u62A5\u5355\u5931\u8D25: ${c}`),this.emitOrderError(e,"submitFailed",t,r,n,i,c),null}}let s={vtOrderId:o,vtSymbol:e.vtSymbol,direction:t,offset:r,price:n,volume:i,traded:0,status:"submitting",statusMessage:"",datetime:this.eventTimeMs??this.deps.now()};if(e.mode==="paper"){let a=this.deps.paperExecution?.acceptOrder?.(s)??{ok:!0};if(!a.ok)return this.writeLog(e.instance,`\u62D2\u7EDD\u59D4\u6258\uFF1A${a.reason}`),this.emitOrderError(e,"paperRejected",t,r,n,i,a.reason),null}if(e.orders.set(o,s),this.orderOwners.set(o,e),e.mode==="live"){let a=this.observeLiveOrder(s,this.liveTradingDay||e.tradingDay,!1);a.origin="strategy",a.ownerId=e.id}if(this.deps.orderRisk.recordOrder(e.mode,s,e.tradingDay),e.mode==="paper")this.deps.paperExecution?.onOrderSubmitted?.(Object.freeze({...s}));if(this.queueOrder(e,s),e.mode==="paper")this.enqueue(()=>{if(s.status!=="submitting")return;s.status="notTraded",this.emitOrder(e,s)});return this.scheduleState(e),o}matchPaperOrders(e,t){for(let r of[...e.orders.values()]){if(r.status!=="notTraded")continue;let n=r.direction==="long",i=n?t.askPrice1:t.bidPrice1,o=n?t.askVolume1:t.bidVolume1;if(i===void 0||!Number.isFinite(i)||i<=0)continue;if(o!==void 0&&(!Number.isFinite(o)||o<=0))continue;if(n?r.price<i:r.price>i)continue;let s=r.volume-r.traded,a=this.paperFill(r,t,s,"strategy");r.traded=r.volume,r.status="allTraded",r.statusMessage="paper matched",this.emitOrder(e,r);let d={vtTradeId:`PAPERTRADE.${e.id}.${e.paperSessionId}.${++e.paperTradeCounter}`,vtOrderId:r.vtOrderId,vtSymbol:e.vtSymbol,direction:r.direction,offset:r.offset,price:a.price,volume:s,datetime:t.datetime??this.deps.now(),tradingDay:t.tradingDay??t.actionDay??""};this.deps.paperExecution?.onTrade?.(d,a,"strategy"),this.applyTrade(e,d)}}handleLiveOrder(e){let t=e.vtOrderId;this.observeLiveOrder(this.orderDataFromBroker(e),e.identity.tradingDay);let r=this.orderOwners.get(t);if(!r||r.mode!=="live")return;let n=r.orders.get(t);if(!n)return;n.price=e.price,n.volume=e.volume,n.traded=e.traded,n.status=e.status,n.statusMessage=e.statusMessage,this.emitOrder(r,n),this.scheduleState(r,!0)}observeLiveOrder(e,t,r=!0){let n=this.liveOrders.get(e.vtOrderId);if(!n)n={order:null,origin:"external",tradingDay:t,tradedVolume:0,claimable:r,ownerId:null},this.liveOrders.set(e.vtOrderId,n);return n.order={...e,type:"order"},n.claimable&&=r,n}orderDataFromBroker(e){return{type:"order",vtOrderId:e.vtOrderId,vtSymbol:e.vtSymbol,direction:e.direction,offset:e.offset,price:e.price,volume:e.volume,traded:e.traded,status:e.status,statusMessage:e.statusMessage,datetime:e.datetime}}handleLiveTrade(e){if(!this.rememberTradeId(this.liveTradeIds,e.vtTradeId))return;let t=e.vtOrderId,r=this.orderOwners.get(t),n=this.liveOrders.get(t);if(!n)n={order:null,origin:"external",tradingDay:e.tradingDay,tradedVolume:0,claimable:!1,ownerId:null},this.liveOrders.set(t,n);let i=n.claimable&&n.tradedVolume===0&&this.deps.live?.phase==="running"&&(!this.liveTradingDay||e.tradingDay===this.liveTradingDay);n.tradedVolume+=e.volume,n.claimable=!1;let o={vtTradeId:e.vtTradeId,vtOrderId:t,vtSymbol:e.vtSymbol,direction:e.direction,offset:e.offset,price:e.price,volume:e.volume,datetime:e.datetime,tradingDay:e.tradingDay};if(r?.mode==="live")this.rollLiveDay(r,e.tradingDay),this.applyTrade(r,o);this.dispatchTrades("live",o,n.order,n.origin,{observed:n,firstTrade:i,allowPaper:!1})}handleLiveError(e){let t=this.orderOwners.get(e.vtOrderId);if(!t)return;let r=t.orders.get(e.vtOrderId);if(!r||!w(r))return;if(e.operation==="cancel"){r.statusMessage=`\u64A4\u5355\u5931\u8D25: ${e.message}`,this.emitOrder(t,r),this.scheduleState(t,!0);return}r.status="rejected",r.statusMessage=e.message,this.emitOrder(t,r),this.scheduleState(t,!0)}handleLiveReconciled(e){this.deps.orderRisk.reconcileLive(e),this.liveTradingDay=e.tradingDay;for(let[o,s]of this.liveOrders)if(s.tradingDay<e.tradingDay&&!this.orderOwners.has(o))this.liveOrders.delete(o);for(let o of e.orders)this.observeLiveOrder(this.orderDataFromBroker(o),o.identity.tradingDay,!1);for(let o of e.trades){if(!this.rememberTradeId(this.liveTradeIds,o.vtTradeId))continue;let s=this.liveOrders.get(o.vtOrderId);if(s)s.tradedVolume+=o.volume}if(e.reason!=="reconnect")return;let t=[...this.strategies.values()].filter((o)=>o.mode==="live"&&o.trading);if(t.length===0)return;let r=new Map;for(let o of t){let s=r.get(o.instrumentID)??[];s.push(o),r.set(o.instrumentID,s)}let n=new Map(e.orders.map((o)=>[o.vtOrderId,o]));for(let o of e.orders){if(!U(o)||!r.has(o.instrumentID))continue;let s=this.orderOwners.get(o.vtOrderId);if(!s||!s.trading)throw Error(`\u6062\u590D\u53D1\u73B0\u672A\u5F52\u5C5E\u7684\u6D3B\u52A8\u59D4\u6258: ${o.vtOrderId}`)}for(let o of t)for(let s of o.orders.values())if(w(s)&&!n.has(s.vtOrderId))throw Error(`\u672C\u5730\u6D3B\u52A8\u59D4\u6258\u672A\u51FA\u73B0\u5728\u5238\u5546\u5FEB\u7167: ${s.vtOrderId}`);let i=new Map;for(let o of t)i.set(o,this.inventoryOnTradingDay(o,e.tradingDay));for(let[o,s]of r){let a=this.inventoryFromBroker(e.positions,o),d=s.reduce((c,u)=>{let l=i.get(u);return{longToday:c.longToday+l.longToday,longYesterday:c.longYesterday+l.longYesterday,shortToday:c.shortToday+l.shortToday,shortYesterday:c.shortYesterday+l.shortYesterday}},cr());if(!this.sameInventory(a,d))throw Error(`\u6062\u590D\u6301\u4ED3\u4E0D\u4E00\u81F4 ${o}: \u672C\u5730\u4ECA/\u6628 \u591A${d.longToday}/${d.longYesterday}`+` \u7A7A${d.shortToday}/${d.shortYesterday}\uFF0C\u5238\u5546\u4ECA/\u6628 \u591A${a.longToday}/${a.longYesterday}`+` \u7A7A${a.shortToday}/${a.shortYesterday}`)}for(let[o,s]of i)o.inventory=s,o.expectedLivePos=J(s),o.expectedLiveInventory={...s},o.tradingDay=e.tradingDay,this.syncFrameworkState(o),this.scheduleState(o,!0)}inventoryOnTradingDay(e,t){if(e.tradingDay&&e.tradingDay>t)throw Error(`\u6062\u590D TradingDay \u56DE\u9000: ${e.tradingDay}/${t}`);let r={...e.inventory};if(e.tradingDay&&e.tradingDay!==t)r.longYesterday+=r.longToday,r.longToday=0,r.shortYesterday+=r.shortToday,r.shortToday=0;return r}sameInventory(e,t){return e.longToday===t.longToday&&e.longYesterday===t.longYesterday&&e.shortToday===t.shortToday&&e.shortYesterday===t.shortYesterday}handleLiveFault(e){for(let t of this.strategies.values())if(t.mode==="live"&&t.trading)this.failStrategy(t,"TD",Error(e))}applyTrade(e,t,r=!1,n=!0){let i=t.vtTradeId;if(!this.rememberTradeId(e.seenTrades,i))return;if(this.deps.recordTrade)try{this.deps.recordTrade({strategyId:e.id,name:e.name,mode:e.mode,vtSymbol:e.vtSymbol,trade:{...t},inventory:{...e.inventory},multiplier:this.deps.live?.getInstrument(e.instrumentID)?.volumeMultiple??null})}catch(u){this.writeLog(e.instance,`\u6210\u4EA4\u8BB0\u5F55\u5931\u8D25: ${u instanceof Error?u.message:String(u)}`)}if(this.deps.orderRisk.recordTrade(e.mode,t),this.applyInventory(e.inventory,t.direction,t.offset,t.volume),this.syncFrameworkState(e),e.expectedLivePos=J(e.inventory),e.mode==="live")e.expectedLiveInventory={...e.inventory};let o=(e.tradeVolumeByOrder.get(t.vtOrderId)??0)+t.volume;e.tradeVolumeByOrder.set(t.vtOrderId,o);let s=e.orders.get(t.vtOrderId);if(s)s.traded=Math.max(s.traded,Math.min(s.volume,o));let a={...t},d=s?{...s}:null,c=()=>{if(this.callStrategy(e,"onTrade",()=>(this.observe(e,{type:"trade",value:{...t}}),e.instance.onTrade({...t}))),s)this.pruneTerminalOrder(e,s);if(this.scheduleState(e,!0),e.mode==="paper"&&n)this.dispatchTrades("paper",a,d,"strategy")};if(r)this.scheduleState(e,!0),this.enqueue(c);else c()}applyInventory(e,t,r,n){if(r==="open"){if(t==="long")e.longToday+=n;else e.shortToday+=n;return}let i=t==="short"?"longToday":"shortToday",o=t==="short"?"longYesterday":"shortYesterday";if(r==="closeToday"){e[i]=Math.max(0,e[i]-n);return}if(r==="closeYesterday"){e[o]=Math.max(0,e[o]-n);return}let s=Math.min(e[o],n);e[o]-=s,e[i]=Math.max(0,e[i]-(n-s))}queueOrder(e,t){let r={...t},n=e.tradingDay;this.enqueue(()=>this.emitOrder(e,t,r,n))}emitOrder(e,t,r={...t},n=e.tradingDay){if(this.deps.orderRisk.recordOrder(e.mode,r,n),e.inited)this.callStrategy(e,"onOrder",()=>(this.observe(e,{type:"order",value:{...r,type:"order"}}),e.instance.onOrder({...r,type:"order"})));if(!w(r))this.pruneTerminalOrder(e,t)}emitOrderError(e,t,r,n,i,o,s){let a={type:"error",stage:"submit",code:t,vtSymbol:e.vtSymbol,direction:r,offset:n,price:i,volume:o,message:s,datetime:this.eventTimeMs??this.deps.now()};this.enqueue(()=>this.callStrategy(e,"onOrder",()=>(this.observe(e,{type:"order",value:{...a}}),e.instance.onOrder(a))))}pruneTerminalOrder(e,t){if(e.orders.get(t.vtOrderId)!==t)return;if(w(t))return;if((e.tradeVolumeByOrder.get(t.vtOrderId)??0)<t.traded)return;if(e.mode==="paper")this.deps.paperExecution?.onOrderClosed?.(t),this.terminalPaperOrders.add(t.vtOrderId);e.orders.delete(t.vtOrderId),e.tradeVolumeByOrder.delete(t.vtOrderId),this.orderOwners.delete(t.vtOrderId)}rememberTradeId(e,t){if(e.has(t))return!1;if(e.add(t),e.size>Fo){let r=e.values().next().value;if(r!==void 0)e.delete(r)}return!0}dispatchTimer(e){this.enqueue(()=>{for(let t of this.strategies.values())this.dispatchEvent(t,Object.freeze({type:"timer",now:e}))})}dispatchTrades(e,t,r,n,i){let o=Object.freeze({type:"trades",now:this.eventTimeMs??this.deps.now(),trade:Object.freeze({...t}),order:r?Object.freeze({...r,origin:n}):null});for(let s of this.strategies.values()){if(s.mode!==e)continue;let a=this.tradeClaimContext;this.tradeClaimContext=i?{...i,running:s,trade:o.trade}:null;try{this.dispatchEvent(s,o)}finally{this.tradeClaimContext=a}}}dispatchEvent(e,t){if(!e.inited||e.fault)return;let r=e.eventHandlers[t.type];for(let n of[...r]){if(e.fault)break;if(!r.has(n))continue;this.invokeAtTime(t.now,()=>this.callStrategy(e,`${t.type} event`,()=>n(t)))}}clearEventHandlers(e){for(let t of Object.values(e.eventHandlers))t.clear()}invokeAtTime(e,t){let r=this.eventTimeMs;this.eventTimeMs=e;try{return t()}finally{this.eventTimeMs=r}}rollPaperDay(e,t){if(!t||t===e.tradingDay)return;if(e.tradingDay){for(let r of e.orders.values()){if(!w(r))continue;r.status="cancelled",r.statusMessage="paper trading day changed",this.queueOrder(e,r)}e.inventory.longYesterday+=e.inventory.longToday,e.inventory.longToday=0,e.inventory.shortYesterday+=e.inventory.shortToday,e.inventory.shortToday=0}e.tradingDay=t,this.scheduleState(e,!0)}rollLiveDay(e,t){if(!t||t===e.tradingDay)return;let r=this.inventoryOnTradingDay(e,t);e.inventory=r,e.expectedLiveInventory={...r},e.expectedLivePos=J(r),e.tradingDay=t,this.syncFrameworkState(e)}roundPrice(e,t){let r=this.getPriceTick(e.instance);return Number((Math.round(t/r)*r).toFixed(10))}paperFill(e,t,r,n){let i=(e.direction==="long"?t.askPrice1:t.bidPrice1)??e.price,o=this.deps.paperExecution?.fillOrder?.(e,t,r,n)??{referencePrice:i,price:i};if(!Number.isFinite(o.referencePrice)||o.referencePrice<=0||!Number.isFinite(o.price)||o.price<=0)throw Error(`Paper \u6210\u4EA4\u4EF7\u975E\u6CD5: ${o.referencePrice}/${o.price}`);return o}cancelAllDirect(e){for(let t of e.orders.values()){if(!w(t))continue;if(e.mode==="paper")this.cancelOrder(e.instance,t.vtOrderId);else try{this.deps.live?.cancelOrder(t.vtOrderId)}catch(r){console.error(`[cta] ${e.id} \u64A4\u5355\u5931\u8D25: ${r instanceof Error?r.message:String(r)}`)}}}retainedOrdersMessage(e){return`\u7B56\u7565\u5DF2\u505C\u6B62\uFF0C\u4ECD\u6709 ${e} \u7B14\u59D4\u6258\u672A\u64A4\u9500\uFF0C\u53EF\u80FD\u7EE7\u7EED\u6210\u4EA4\uFF0C\u8BF7\u624B\u52A8\u64A4\u5355\u3002`}scheduleState(e,t=!1){if(t){e.stateTimer?.(),e.stateTimer=null,this.trySaveState(e);return}if(e.stateTimer)return;e.stateTimer=this.deps.schedule(()=>{e.stateTimer=null,this.trySaveState(e)},qo)}saveState(e){let t=this.serializeVariables(e);this.deps.stateStore.write({strategyId:e.id,vtSymbol:e.vtSymbol,mode:e.mode,bundleHash:e.hash,pos:J(e.inventory),variables:t,inventory:e.inventory,tradingDay:e.tradingDay}),this.observe(e,{type:"state",operation:"write",value:{pos:J(e.inventory),inventory:{...e.inventory},variables:t,tradingDay:e.tradingDay}})}serializeVariables(e){return Un(e.runtime.getStateVariables(),`\u7B56\u7565 ${e.id} variables`)}trySaveState(e){try{this.saveState(e)}catch(t){if(console.error(`[cta] ${e.id} \u72B6\u6001\u4FDD\u5B58\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`),e.trading)this.failStrategy(e,"state",t)}}callStrategy(e,t,r){if(e.fault)return;try{this.invokeStrategySync(e,t,r),this.scheduleState(e)}catch(n){this.failStrategy(e,t,n)}}failStrategy(e,t,r){let n=r instanceof Error?r.message:String(r);if(e.fault)return;e.fault=`${t}: ${n}`,this.observe(e,{type:"fault",callback:t,message:n}),e.trading=!1,e.inited=!1,this.clearEventHandlers(e),this.syncFrameworkState(e),this.cancelAllDirect(e),console.error(`[cta] ${e.id} ${t} \u5F02\u5E38\uFF0C\u7B56\u7565\u5DF2\u505C\u6B62: ${n}`),this.publishHook("strategy.faulted",{...this.strategyIdentity(e),callback:t,reason:n})}emitRiskRejection(e,t,r,n,i,o,s){let a=this.deps.orderRisk.tradingDay(e.mode)||e.tradingDay||null;this.publishHook("risk.order_rejected",{source:"strategy",riskCode:t,mode:e.mode,vtSymbol:e.vtSymbol,tradingDay:a,direction:r,offset:n,price:i,volume:o,reason:s,strategyId:e.id})}strategyIdentity(e){return{strategyId:e.id,mode:e.mode,vtSymbol:e.vtSymbol}}publishHook(e,t){try{this.deps.emitHook?.(e,t)}catch(r){console.error(`[hooks] \u4E8B\u4EF6\u6295\u9012\u5931\u8D25 event=${e}: ${r instanceof Error?r.message:String(r)}`)}}enqueue(e){let t=this.eventTimeMs;if(this.eventQueue.push(t===null?e:()=>this.invokeAtTime(t,e)),this.dispatching)return;this.dispatching=!0;try{while(this.eventQueue.length>0)this.eventQueue.shift()()}finally{this.dispatching=!1}}queuePending(e,t){for(let r of this.pending.values()){if(r.running.instrumentID!==t)continue;if(r.events.length>=qn){r.overflowed=!0;continue}r.events.push(e)}}drainPending(e){while(e.events.length>0){let t=e.events.splice(0);for(let r of t){if(r.kind==="bar"){if(e.replayedBars.has(zn(r.value)))continue;this.callStrategy(e.running,"onBar",()=>e.running.instance.onBar(r.value))}else this.callStrategy(e.running,"onTick",()=>e.running.instance.onTick(r.value));if(e.running.fault)return}}}assertLiveInstrumentOwner(e){if(e.mode!=="live")return;let t=[...this.strategies.values()].find((r)=>B(r.id,r.mode)!==B(e.id,e.mode)&&r.mode==="live"&&r.instrumentID===e.instrumentID);if(t)throw Error(`Live \u5408\u7EA6 ${e.instrumentID} \u5DF2\u7531\u7B56\u7565 ${t.id} \u5360\u7528`)}requireInitializing(e,t){let r=this.requireInstance(e);if(this.initializing!==r)throw Error(`${t} \u53EA\u80FD\u5728 onInit() \u4E2D\u8C03\u7528`);return r}requireInstance(e){let t=this.instanceMap.get(e);if(!t)throw Error("\u672A\u77E5\u7B56\u7565\u5B9E\u4F8B");return t}findRunning(e,t){return this.strategies.get(B(e,t))??null}requireRunning(e,t){let r=this.findRunning(e,t);if(!r)throw Error(`\u7B56\u7565\u5B9E\u4F8B\u4E0D\u5B58\u5728: ${e}@${t}`);return r}findPending(e,t){return this.pending.get(B(e,t))??null}requirePending(e,t){let r=this.findPending(e,t);if(!r)throw Error(`\u7B56\u7565 ${e}@${t} \u6CA1\u6709\u5F85\u63D0\u4EA4\u53D8\u66F4`);return r}async dispose(e,t=!0){if(e.stateTimer?.(),e.stateTimer=null,t&&e.inited)this.trySaveState(e);this.instanceMap.delete(e.instance),this.clearEventHandlers(e);for(let r of e.orders.keys())this.orderOwners.delete(r)}invokeSync(e,t){let r=t();if(r&&typeof r.then==="function")throw Promise.resolve(r).catch(()=>{return}),Error(`${e} \u4E0D\u5141\u8BB8\u8FD4\u56DE Promise`)}invokeStrategySync(e,t,r){this.syncFrameworkState(e),this.observe(e,{type:"callback",callback:t,stage:"begin"});try{this.invokeSync(t,r)}finally{this.syncFrameworkState(e),this.observe(e,{type:"callback",callback:t,stage:"end"})}}observe(e,t){this.deps.observe({...t,strategyId:e.id,now:this.eventTimeMs??this.deps.now()})}syncFrameworkState(e){e.runtime.setRuntimeState({inited:e.inited,trading:e.trading,pos:J(e.inventory)})}}var jn=20,Ko=1000,Q="vibek-test",Yo={maxOrderVolume:1000,maxPosition:1e4,maxActiveOpenOrders:1000},Gn={dailyLimits:{totalOrderLimit:1e5,totalCancelLimit:1e5,totalTradeLimit:1e5,instrumentOrderLimit:1e5,instrumentCancelLimit:1e5,instrumentTradeLimit:1e5},duplicateOrderLimit:1e5};class ie{definition;fixture;events=[];orderEvents=[];tradeEvents=[];logs=[];hooks=[];submissions=[];cancelRequests=[];broker;callbackCounts={onInit:0,onStart:0,onStop:0,onTick:0,onBar:0};dataDir;stateStore;clock;traceRecorder;faults=[];aggregator=new qe({timeframes:["1min"]});barHistory=[];warmupSupplied=!1;paperSessionSequence=0;mode;setting;initialState;brokerState;executionLimits;riskSettings;paperExecution;runtime;running;live;gatewayControl;eventCounter=0;nextEventTimeMs;initialized=!1;initialStateWritten=!1;disposed=!1;firstSdkOrderViolation;constructor(e,t,r={}){this.definition=e;this.fixture=t;if(this.mode=r.mode??"live",r.execution!==void 0&&r.execution!=="scripted"&&r.execution!=="ctp")throw Error("Unknown test execution model");if(r.execution==="ctp"&&this.mode!=="live")throw Error("CTP execution requires live mode");if(r.execution==="ctp"&&r.brokerState)throw Error("CTP execution uses gatewayState");if(r.brokerState&&this.mode!=="live")throw Error("Broker state requires live mode");if(r.paperExecution&&this.mode!=="paper")throw Error("Paper execution hooks require paper mode");if(r.execution!=="ctp"&&(r.gatewayState||r.gatewaySession))throw Error("Gateway settings require CTP execution");this.setting=r.setting===void 0?ce(t.setting,"setting"):ce(r.setting,"setting"),this.initialState=r.initialState,this.brokerState=r.brokerState===void 0?void 0:structuredClone(r.brokerState),this.executionLimits=Wo(r.executionLimits),this.riskSettings=Jo(r.riskLimits),this.paperExecution=r.paperExecution,this.clock=new Ht(Ke(t)),this.traceRecorder=new Lt(()=>this.clock.now);let{paperExecution:n,...i}=r;if(this.traceRecorder.record("context",{fixture:{vtSymbol:t.vtSymbol,instrument:{priceTick:t.priceTick,size:t.size,name:t.name,minOrderVolume:t.minOrderVolume,maxOrderVolume:t.maxOrderVolume,longMarginRatio:t.longMarginRatio,shortMarginRatio:t.shortMarginRatio,isTrading:t.isTrading},...t.commission?{commission:t.commission}:{},setting:t.setting,seedPrice:t.seedPrice,tradingDay:t.tradingDay},options:i,...n?{customPaperExecution:!0}:{}}),r.execution==="ctp")this.live=new xe(t,this.clock,this.traceRecorder,r,{submissions:this.submissions,cancelRequests:this.cancelRequests});this.nextEventTimeMs=this.clock.now;let o={holdReports:()=>this.requireLive().holdReports(),resumeReports:()=>this.requireLive().resumeReports(),pendingReports:()=>this.requireLive().pendingReports(),deliverReport:(s)=>this.requireLive().deliverReport(s),repeatReport:(s)=>this.requireLive().repeatReport(s),snapshot:()=>this.requireLive().snapshot(),failNextSubmit:(s)=>this.requireLive().failNextSubmit(s),rejectSubmit:(s,a)=>this.requireLive().rejectSubmit(s,a),acknowledge:(s)=>this.requireLive().acknowledge(s),partialFill:(s,a,d)=>this.requireLive().partialFill(s,a,d),fill:(s,a)=>this.requireLive().fill(s,a),reject:(s,a)=>this.requireLive().reject(s,a),cancel:(s,a)=>this.requireLive().cancel(s,a),failNextCancel:(s)=>this.requireLive().failNextCancel(s),rejectCancel:(s,a)=>this.requireLive().rejectCancel(s,a),emitExternalOrder:(s)=>this.requireLive().emitExternalOrder(s),emitOrder:(s)=>this.requireLive().emitOrder(s),emitTrade:(s)=>this.requireLive().emitTrade(s),disconnect:()=>this.requireLive().disconnect(),reconcile:(s)=>this.requireLive().reconcile(s),fault:(s)=>this.requireLive().fault(s)};this.broker=this.observeCommands(o,"broker",["snapshot","pendingReports"]),this.dataDir=zo(Go(jo(),"vibek-strategy-test-runtime-")),this.stateStore=new $e(this.dataDir)}initialize(e={}){return this.traceRecorder.input({type:"initialize",...e},()=>this.initializeRuntime(e))}async initializeRuntime(e){if(this.disposed)throw Error("\u6D4B\u8BD5 Runtime \u5DF2\u91CA\u653E");if(this.initialized)throw Error("\u6D4B\u8BD5 Runtime \u5DF2\u521D\u59CB\u5316");if(e.warmupBars){let t=e.warmupBars.map((r,n)=>H(this.fixture,r,n));Ie(t,this.clock.now,this.fixture.instrumentID),this.barHistory.push(...t),this.warmupSupplied=!0}this.seedInitialState(),await this.createRuntime(),await this.runtime.warmup(),this.runtime.setTimerClock(this.clock.now),this.initialized=!0}async start(){this.requireInitialized(),await this.traceRecorder.input({type:"start"},()=>this.runtime.start(Q,this.mode))}async stop(){this.requireInitialized(),await this.traceRecorder.input({type:"stop"},()=>this.runtime.stop(Q,{},this.mode))}cancelOrder(e){this.requireInitialized(),this.traceRecorder.input({type:"cancelOrder",vtOrderId:e},()=>{this.runtime.cancelStrategyOrder(Q,e,this.mode)})}async restart(){this.requireInitialized(),await this.traceRecorder.input({type:"restart"},async()=>{await this.runtime.stop(Q,{},this.mode),await this.rebuildRuntime()})}async crashAndRestart(){this.requireInitialized(),await this.traceRecorder.input({type:"crashAndRestart"},()=>this.rebuildRuntime(!1))}setTime(e){if(this.disposed)throw Error("\u6D4B\u8BD5 Runtime \u5DF2\u91CA\u653E");let t=T(e,"\u6D4B\u8BD5\u65F6\u95F4");this.traceRecorder.input({type:"setTime",datetime:t},()=>{if(this.initialized)this.advanceClock(t);else this.clock.now=t}),this.nextEventTimeMs=t}advanceTime(e){if(this.disposed)throw Error("\u6D4B\u8BD5 Runtime \u5DF2\u91CA\u653E");let t=T(e,"\u63A8\u8FDB\u65F6\u95F4"),r=this.clock.now+t;if(!Number.isSafeInteger(r))throw Error("\u63A8\u8FDB\u540E\u7684\u6D4B\u8BD5\u65F6\u95F4\u8D85\u8FC7\u5B89\u5168\u6574\u6570\u8303\u56F4");this.traceRecorder.input({type:"advanceTime",milliseconds:e},()=>this.advanceClock(r)),this.nextEventTimeMs=r}pushBar(e,t={}){this.requireInitialized();let r=t.datetime??(e.barTime===void 0?Math.max(this.nextEventTimeMs,((this.barHistory.at(-1)?.barTime??-120)+120)*1000):(e.barTime+60)*1000),n=H(this.fixture,e,this.eventCounter,Math.floor(r/60000)*60-60);this.validateClosedBars([n],r),this.traceRecorder.input({type:"bar",value:n,datetime:r},()=>{this.recordEventTime(r),this.eventCounter++,this.aggregator.completeThrough(r),this.dispatchBars(this.mergeClosedBars(this.takeCompletedBars(),[n]))})}pushTick(e){this.pushMarketEvent(e,[])}pushMarketEvent(e,t){this.requireInitialized();let r=De(this.fixture,e,this.eventCounter,this.nextEventTimeMs);if(r.code!==this.fixture.instrumentID)throw Error("\u6D4B\u8BD5 Tick \u5408\u7EA6\u4E0E fixture \u4E0D\u5339\u914D");let n=t.map((o,s)=>H(this.fixture,o,s)),i=r.receivedAt??r.datetime;if(r.datetime>i||i-r.datetime>dt)throw Error("\u6D4B\u8BD5 Tick \u4EA4\u4ED8\u65F6\u95F4\u5FC5\u987B\u4F4D\u4E8E\u884C\u60C5\u65F6\u95F4\u4E4B\u540E\u7684 30 \u79D2\u4EE5\u5185");this.validateClosedBars(n,i),this.traceRecorder.input({type:"market",tick:r,closedBars:n},()=>{this.recordEventTime(i),this.eventCounter++,this.aggregator.pushTick(r);let o=this.mergeClosedBars(this.takeCompletedBars(),n);this.rememberBars(o);for(let s of o)this.traceRecorder.record("bar",s);this.runtime.handleMarketEvent(r,o)})}activeOrders(){if(this.requireInitialized(),!this.running)throw Error("\u6D4B\u8BD5 Runtime \u7684\u7B56\u7565\u5B9E\u4F8B\u4E0D\u5B58\u5728");return[...this.running.orders.values()].filter(w).map((e)=>({...e}))}snapshot(){this.requireInitialized();let e=this.runtime.snapshot().find((t)=>t.id===Q);if(!e||!this.running)throw Error("\u6D4B\u8BD5 Runtime \u7684\u7B56\u7565\u5B9E\u4F8B\u4E0D\u5B58\u5728");return{inited:e.inited,trading:e.trading,pos:e.pos,activeOrders:e.activeOrders,fault:e.fault,variables:ce(this.running.runtime.getStateVariables(),"variables"),inventory:{...this.running.inventory},now:this.clock.now}}get trace(){return this.traceRecorder.events}get gateway(){if(this.disposed)throw Error("\u6D4B\u8BD5 Runtime \u5DF2\u91CA\u653E");if(!(this.live instanceof xe))throw Error('Gateway controls require execution="ctp"');return this.gatewayControl??=this.observeCommands(this.live.gateway,"gateway",["pendingRequests"])}expectFault(e){let t=this.faults.find((r)=>!r.verified&&r.message===e);if(!t)throw Error(`\u672A\u53D1\u751F\u5F85\u9A8C\u8BC1\u7684 fault: ${e}`);t.verified=!0,this.traceRecorder.record("input",{type:"expectFault",message:e})}settleActiveOrders(){if(this.mode==="live"){if(!(this.live instanceof Oe))throw Error("CTP orders require explicit gateway reports");this.live.settleActiveOrders()}else for(let e of this.activeOrders())this.cancelOrder(e.vtOrderId)}assertHealthy(e){let t=this.snapshot();if(t.fault)throw Error(`${e}\u540E\u7B56\u7565 fault: ${t.fault}`);if(this.firstSdkOrderViolation)throw Error(`${e}\u89E6\u53D1 SDK \u975E\u6CD5\u4E0B\u5355: ${this.firstSdkOrderViolation}`)}async dispose(){if(!this.disposed){this.disposed=!0;try{await this.runtime?.close()}finally{if(this.live instanceof xe)this.live.close();this.traceRecorder.close(),Uo(this.dataDir,{recursive:!0,force:!0})}}let e=this.faults.filter((t)=>!t.verified);if(e.length)throw Error(`\u672A\u9A8C\u8BC1\u7684\u7B56\u7565 fault: ${e.map((t)=>t.message).join("; ")}`)}async createRuntime(){let e=this.stateStore.read(Q,this.mode),t=e?.inventory??de(),r={...this.fixture,tradingDay:e?.tradingDay||this.fixture.tradingDay};this.live??=new Oe(r,t,{order:0,trade:0},()=>this.clock.now,{submissions:this.submissions,cancelRequests:this.cancelRequests,record:(i,o)=>this.traceRecorder.record(i,o)},this.brokerState);let n=new He({settings:this.riskSettings,stateStore:new dr(this.dataDir),schedule:(i,o)=>this.clock.schedule(this.traceRecorder.bind(i),o)});n.setTradingDay("paper",r.tradingDay),this.runtime=new mr({createWarmupProvider:()=>new Bt(r,this.barHistory,this.clock.now,!this.warmupSupplied),stateStore:this.stateStore,live:this.live,paperExecution:{...this.paperExecution,onOrderSubmitted:(i)=>{let o={vtOrderId:i.vtOrderId,direction:i.direction,offset:i.offset,price:i.price,volume:i.volume};this.submissions.push(o),this.traceRecorder.record("submission",o)},onCancelRequested:(i)=>{this.cancelRequests.push(i.vtOrderId),this.traceRecorder.record("cancel",{vtOrderId:i.vtOrderId})}},orderRisk:n,executionLimits:this.executionLimits,log:(i)=>this.recordLog(i),now:()=>this.clock.now,schedule:(i,o)=>this.clock.schedule(this.traceRecorder.bind(i),o),createPaperSessionId:()=>String(++this.paperSessionSequence),observe:(i)=>this.observe(i),emitHook:(i,o,s)=>{if(i!=="strategy.event")return;let a=structuredClone({...o,occurredAt:s??this.clock.now});this.hooks.push(a),this.events.push({type:"hook",value:a,now:a.occurredAt}),this.traceRecorder.record("hook",a)}}),await this.live.connect(),this.running=this.runtime.addRestored({sourcePath:"strategy.ts",hash:"a".repeat(64),artifact:"strategy-test",loadedAt:new Date(0).toISOString(),mode:this.mode,definition:this.definition,instance:{id:Q,vtSymbol:this.fixture.vtSymbol,setting:this.setting}})}observe(e){if(this.traceRecorder.record(e.type,e),e.type==="callback"&&e.stage==="begin"&&Object.hasOwn(this.callbackCounts,e.callback))this.callbackCounts[e.callback]++;else if(e.type==="order"){let t=Zo(e.value);if(t.type==="error"&&(t.code==="invalidRequest"||t.code==="insufficientPosition"))this.firstSdkOrderViolation??=`${t.code}: ${t.message}`;this.orderEvents.push(t),this.events.push({type:"order",value:t,now:e.now})}else if(e.type==="trade")this.tradeEvents.push({...e.value}),this.events.push({type:"trade",value:{...e.value},now:e.now});else if(e.type==="fault")this.faults.push({message:`${e.callback}: ${e.message}`,verified:!1})}recordLog(e){let t=Qo(this.logs,e);this.traceRecorder.record("log",{message:t})}async rebuildRuntime(e=!0){await this.runtime.close({persist:e}),this.runtime=void 0,this.running=void 0,this.aggregator.reset(),this.initialized=!1,await this.createRuntime(),await this.runtime.warmup(),this.runtime.setTimerClock(this.clock.now),this.initialized=!0}recordEventTime(e){let t=e+60000;if(!Number.isSafeInteger(t))throw Error("\u884C\u60C5\u65F6\u95F4\u8D85\u8FC7\u5B89\u5168\u6574\u6570\u8303\u56F4");if(e<this.clock.now)throw Error("\u884C\u60C5\u65F6\u95F4\u4E0D\u80FD\u56DE\u9000");this.advanceClock(e),this.nextEventTimeMs=t}advanceClock(e){this.clock.advanceTo(e,this.initialized?()=>{this.traceRecorder.record("timer",{now:this.clock.now}),this.runtime.handleTimer(this.clock.now),this.aggregator.completeThrough(this.clock.now-dt),this.dispatchBars(this.takeCompletedBars())}:void 0)}takeCompletedBars(){let e=[...this.aggregator.getCompletedBars()];return this.aggregator.clearCompletedBars(),e.filter((t)=>t.barTime>(this.barHistory.at(-1)?.barTime??-1))}validateClosedBars(e,t){if(Ie(e,t,this.fixture.instrumentID),e.some((n)=>n.barTime<=(this.barHistory.at(-1)?.barTime??-1)))throw Error("\u95ED\u5408 Bar \u65F6\u95F4\u5FC5\u987B\u665A\u4E8E\u5DF2\u56DE\u653E\u5386\u53F2");let r=this.aggregator.getCurrentBar("1min");if(r&&t>=(r.barTime+60)*1000+dt&&e.some((n)=>n.barTime===r.barTime))throw Error("\u663E\u5F0F Bar \u7684\u4EA4\u4ED8\u665A\u4E8E\u81EA\u52A8\u95ED\u5408\u671F\u9650\uFF0C\u4E0D\u80FD\u66FF\u6362\u5DF2\u5230\u671F\u7684 Bar")}mergeClosedBars(e,t){let r=new Map(e.map((n)=>[n.barTime,n]));for(let n of t)r.set(n.barTime,n);return[...r.values()].sort((n,i)=>n.barTime-i.barTime)}rememberBars(e){this.barHistory.push(...e.map((t)=>({...t})))}dispatchBars(e){this.rememberBars(e);for(let t of e)this.traceRecorder.record("bar",t),this.runtime.handleBar(t)}seedInitialState(){if(this.initialStateWritten||!this.initialState)return;let e=We(this.initialState.inventory),t=this.initialState.variables===void 0?{}:ce(this.initialState.variables,"initialState.variables");this.stateStore.write({strategyId:Q,vtSymbol:this.fixture.vtSymbol,mode:this.mode,bundleHash:"a".repeat(64),pos:Mr(e),variables:t,inventory:e,tradingDay:this.fixture.tradingDay}),this.initialStateWritten=!0}requireInitialized(){if(this.disposed)throw Error("\u6D4B\u8BD5 Runtime \u5DF2\u91CA\u653E");if(!this.initialized||!this.runtime)throw Error("\u6D4B\u8BD5 Runtime \u5C1A\u672A initialize()")}requireLive(){if(this.requireInitialized(),this.mode!=="live"||!(this.live instanceof Oe))throw Error('broker \u63A7\u5236\u53EA\u9002\u7528\u4E8E mode="live"\u3001execution="scripted" \u7684\u6D4B\u8BD5 Runtime');return this.live}observeCommands(e,t,r){return new Proxy(e,{get:(n,i)=>{let o=Reflect.get(n,i);if(typeof o!=="function"||!Object.hasOwn(n,i)||r.includes(String(i)))return o;return(...s)=>this.traceRecorder.input({type:t,method:i,args:s},()=>Reflect.apply(o,n,s))}})}}function Wo(e){let t={...Yo,...e};return{maxOrderVolume:x(t.maxOrderVolume,"executionLimits.maxOrderVolume"),maxPosition:x(t.maxPosition,"executionLimits.maxPosition"),maxActiveOpenOrders:x(t.maxActiveOpenOrders,"executionLimits.maxActiveOpenOrders")}}function Jo(e){let t={...Gn.dailyLimits,...e?.dailyLimits};for(let r of Object.keys(t))x(t[r],`riskLimits.dailyLimits.${r}`);return{dailyLimits:t,duplicateOrderLimit:x(e?.duplicateOrderLimit??Gn.duplicateOrderLimit,"riskLimits.duplicateOrderLimit")}}function Qo(e,t){let r=Xo(es(t),Ko);if(e.push(r),e.length>jn)e.splice(0,e.length-jn);return r}function Zo(e){return{...e}}function Xo(e,t){let r=Buffer.from(e);if(r.length<=t)return e;return`${r.subarray(0,t).toString("utf8")}...`}function es(e){return e.replace(/\bsk-[A-Za-z0-9_-]{6,}\b/g,"[redacted]").replace(/((?:api[_ -]?key|authorization|access[_ -]?token)\s*[:=]\s*)\S+/gi,"$1[redacted]")}async function pr(e,t,r){await e.initialize(),await e.start();for(let n of[0,1,-1,2,-2,0]){if(e.pushTick({lastPrice:Math.max(t.priceTick,t.seedPrice+n*t.priceTick),askVolume1:1,bidVolume1:1}),e.advanceTime(1500),r==="live")for(let i of e.activeOrders()){if(i.status==="submitting")e.broker.acknowledge(i.vtOrderId);if(!e.activeOrders().some((o)=>o.vtOrderId===i.vtOrderId))continue;if(i.volume-i.traded>1)e.broker.partialFill(i.vtOrderId,1);else e.broker.fill(i.vtOrderId)}e.advanceTime(60000),e.assertHealthy("\u786E\u5B9A\u6027\u56DE\u653E")}return await e.stop(),e.settleActiveOrders(),await e.restart(),await e.start(),e.pushTick({lastPrice:t.seedPrice}),e.advanceTime(1000),await e.stop(),e.settleActiveOrders(),e.assertHealthy("\u786E\u5B9A\u6027\u91CD\u542F"),{trace:e.trace,submissions:e.submissions,cancelRequests:e.cancelRequests,events:e.events,orderEvents:e.orderEvents,tradeEvents:e.tradeEvents,hooks:e.hooks,logs:e.logs,snapshot:e.snapshot()}}import{isDeepStrictEqual as Yn,inspect as Wn}from"util";class ft extends Error{step;scenario;trace;constructor(e,t,r,n){super(`Strategy scenario failed at step ${e}${t.seed===void 0?"":` (seed ${t.seed})`}: ${n instanceof Error?n.message:String(n)}
|
|
50
|
-
Replay: ${Jn(t)}`,{cause:n});this.step=e;this.scenario=t;this.trace=r;this.name="StrategyScenarioError"}}async function gr(e,t,r={}){if(yt(t),t.context?.customPaperExecution&&!r.paperExecution)throw Error("Custom Paper hooks must be supplied to replay this scenario");let n=new ie(e.strategy,Y(t.context?.fixture??e.fixture),{...t.context?.options,...r}),i={...t,context:n.trace[0].value},o=0,s=new Map;try{for(;o<t.steps.length;o++){let d=t.steps[o];if(d.at!==void 0)n.setTime(d.at);if(d.action.type==="await"){let c=s.get(d.action.task);if(!c)throw Error(`Scenario task does not exist: ${d.action.task}`);let u=await c;if(s.delete(d.action.task),d.action.error===void 0){if("error"in u)throw u.error}else if(!("error"in u)||(u.error instanceof Error?u.error.message:String(u.error))!==d.action.error)throw Error(`Scenario task did not reject with: ${d.action.error}`)}else if(d.task)s.set(d.task,Kn(n,d.action).then(()=>({}),(c)=>({error:c})));else await Kn(n,d.action)}let a={...t.seed===void 0?{}:{seed:t.seed},trace:n.trace,snapshot:n.snapshot(),submissions:structuredClone(n.submissions),cancelRequests:[...n.cancelRequests]};return await n.dispose(),a}catch(a){try{await n.dispose()}catch(d){if(d!==a)a=AggregateError([a,d],String(a))}throw new ft(o,i,n.trace,a)}}async function
|
|
69
|
+
`+`\u82E5\u8981\u653E\u5F03\u65E7\u72B6\u6001\uFF0C\u8BF7\u6267\u884C vibek strategy unload ${e.oldState.strategyId}@${e.oldState.mode} \u540E\u91CD\u65B0 load\uFF1B`+"unload \u4F1A\u6C38\u4E45\u5220\u9664\u8BE5\u6A21\u5F0F\u7684\u72B6\u6001\u3001\u6301\u4ED3\u548C\u53D8\u91CF\u3002\u82E5\u5F53\u524D\u6CA1\u6709\u5DF2\u52A0\u8F7D\u5B9E\u4F8B\uFF0C\u8BF7\u6309\u4E0A\u8FF0\u8DEF\u5F84\u624B\u52A8\u5220\u9664\u65E7 state \u6587\u4EF6\u3002");this.details=e;this.name="StrategyStateCompatibilityError"}}function or(e){return e!==null&&typeof e==="object"&&!Array.isArray(e)}function mt(e,t,r){if(!Number.isInteger(e)||e<0)throw Error(`${r}: ${t} \u975E\u6CD5`);return e}function Pn(e,t){if(!or(e))throw Error(`${t}: \u7B56\u7565\u72B6\u6001\u5FC5\u987B\u662F\u5BF9\u8C61`);if(Object.keys(e).some((i)=>!Ro.has(i)))throw Error(`${t}: \u7B56\u7565\u72B6\u6001\u5305\u542B\u672A\u77E5\u5B57\u6BB5`);if(typeof e.strategyId!=="string"||typeof e.vtSymbol!=="string"||e.mode!=="paper"&&e.mode!=="live")throw Error(`${t}: \u7B56\u7565\u72B6\u6001\u8EAB\u4EFD\u975E\u6CD5`);if(typeof e.bundleHash!=="string"||!/^[a-f0-9]{64}$/.test(e.bundleHash)||typeof e.savedAt!=="string"||!or(e.variables)||typeof e.tradingDay!=="string"||!or(e.inventory))throw Error(`${t}: \u7B56\u7565\u72B6\u6001\u5185\u5BB9\u975E\u6CD5`);let r={longToday:mt(e.inventory.longToday,"longToday",t),longYesterday:mt(e.inventory.longYesterday,"longYesterday",t),shortToday:mt(e.inventory.shortToday,"shortToday",t),shortYesterday:mt(e.inventory.shortYesterday,"shortYesterday",t)};if(!Number.isInteger(e.pos))throw Error(`${t}: pos \u975E\u6CD5`);if(r.longToday+r.longYesterday-r.shortToday-r.shortYesterday!==e.pos)throw Error(`${t}: pos \u4E0E inventory \u4E0D\u4E00\u81F4`);return{strategyId:e.strategyId,vtSymbol:e.vtSymbol,mode:e.mode,bundleHash:e.bundleHash,savedAt:e.savedAt,pos:e.pos,variables:e.variables,inventory:r,tradingDay:e.tradingDay}}class $e{root;constructor(e=pe.data){this.root=ir(e,"strategy-state")}read(e,t){let r=this.path(e,t);if(!ko(r))return null;return Pn(JSON.parse(Eo(r,"utf8")),r)}assertCompatible(e,t,r,n){let i=this.read(e,t);if(!i)return;if(i.strategyId!==e||i.mode!==t)throw Error(`${this.path(e,t)}: \u7B56\u7565 ${e} \u72B6\u6001\u8EAB\u4EFD\u4E0D\u5339\u914D`);if(i.vtSymbol!==r)throw Error(`${this.path(e,t)}: \u7B56\u7565 ${e} \u7684\u72B6\u6001\u5C5E\u4E8E ${i.vtSymbol}\uFF0C\u4E0D\u80FD\u6062\u590D\u5230 ${r}`);let o=Object.keys(i.variables).sort(),s=[...n].sort(),a=new Set(o),d=new Set(s),c=s.filter((l)=>!a.has(l)),u=o.filter((l)=>!d.has(l));if(c.length===0&&u.length===0)return;throw new $n({statePath:this.path(e,t),oldState:i,oldSchema:{variables:o},newSchema:{variables:s},addedVariables:c,removedVariables:u})}write(e){let t=this.path(e.strategyId,e.mode);To(ir(this.root,e.strategyId),{recursive:!0});let r={savedAt:new Date().toISOString(),...e};Pn(r,t);let n=`${JSON.stringify(r,null,2)}
|
|
70
|
+
`;if(Buffer.byteLength(n)>262144)throw Error(`\u7B56\u7565 ${e.strategyId} variables \u8D85\u8FC7 256KB`);let i=`${t}.${process.pid}-${Oo()}.tmp`;try{Do(i,n,{encoding:"utf8",mode:384,flag:"wx"}),Io(i,t)}finally{try{Cn(i)}catch{}}}remove(e,t){let r=this.path(e,t);try{return Cn(r),!0}catch(n){if(n.code==="ENOENT")return!1;throw n}}path(e,t){if(!we.test(e))throw Error(`\u7B56\u7565 ID \u975E\u6CD5: ${e}`);if(t!=="paper"&&t!=="live")throw Error(`\u7B56\u7565 mode \u975E\u6CD5: ${t}`);return ir(this.root,e,`${t}.json`)}}import{randomUUID as _o}from"crypto";import{existsSync as xo,mkdirSync as Co,readFileSync as Po,renameSync as $o,unlinkSync as Mo,writeFileSync as Ao}from"fs";import{dirname as Bo,join as Ho}from"path";var pt=(e,t)=>{let r=setTimeout(e,t);return()=>clearTimeout(r)};var Mn=4194304,gt=/^\d{8}$/;function ar(){return{orders:0,cancels:0,trades:0}}function Me(e=""){return{tradingDay:e,total:ar(),instruments:new Map,duplicateAttempts:new Map,duplicateRejected:0,orderIds:new Set,cancelledOrderIds:new Set,tradeIds:new Set}}function Ae(e){return e!==null&&typeof e==="object"&&!Array.isArray(e)}function Be(e,t){if(!Number.isSafeInteger(e)||e<0)throw Error(`${t} \u5FC5\u987B\u662F\u975E\u8D1F\u6574\u6570`);return e}function An(e,t){if(!Ae(e))throw Error(`${t} \u5FC5\u987B\u662F JSON \u5BF9\u8C61`);return{orders:Be(e.orders,`${t}.orders`),cancels:Be(e.cancels,`${t}.cancels`),trades:Be(e.trades,`${t}.trades`)}}function Bn(e,t){if(!Ae(e))throw Error(`${t} \u5FC5\u987B\u662F JSON \u5BF9\u8C61`);let r=typeof e.tradingDay==="string"?e.tradingDay:"";if(r&&!gt.test(r))throw Error(`${t}.tradingDay \u975E\u6CD5`);if(!Ae(e.instruments))throw Error(`${t}.instruments \u5FC5\u987B\u662F JSON \u5BF9\u8C61`);let n={};for(let[o,s]of Object.entries(e.instruments)){if(!o||o.length>128)throw Error(`${t}.instruments key \u975E\u6CD5`);n[o]=An(s,`${t}.instruments.${o}`)}if(!Ae(e.duplicateAttempts))throw Error(`${t}.duplicateAttempts \u5FC5\u987B\u662F JSON \u5BF9\u8C61`);let i={};for(let[o,s]of Object.entries(e.duplicateAttempts))i[o]=Be(s,`${t}.duplicateAttempts`);return{tradingDay:r,total:An(e.total,`${t}.total`),instruments:n,duplicateAttempts:i,duplicateRejected:Be(e.duplicateRejected,`${t}.duplicateRejected`)}}function Hn(e,t){if(!Ae(e)||Object.keys(e).some((r)=>!["savedAt","paper","live"].includes(r))||typeof e.savedAt!=="string")throw Error(`${t}: \u98CE\u63A7\u72B6\u6001\u7ED3\u6784\u975E\u6CD5`);return{savedAt:e.savedAt,paper:Bn(e.paper,`${t}.paper`),live:Bn(e.live,`${t}.live`)}}function Ln(e){return{tradingDay:e.tradingDay,total:{...e.total},instruments:new Map(Object.entries(e.instruments).map(([t,r])=>[t,{...r}])),duplicateAttempts:new Map(Object.entries(e.duplicateAttempts)),duplicateRejected:e.duplicateRejected,orderIds:new Set,cancelledOrderIds:new Set,tradeIds:new Set}}function Nn(e){return{tradingDay:e.tradingDay,total:{...e.total},instruments:Object.fromEntries([...e.instruments.entries()].sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>[t,{...r}])),duplicateAttempts:Object.fromEntries([...e.duplicateAttempts.entries()].sort(([t],[r])=>t.localeCompare(r))),duplicateRejected:e.duplicateRejected}}function Lo(){return{dailyLimits:{...re.trading.dailyLimits},duplicateOrderLimit:re.trading.duplicateOrders.duplicateOrderLimit}}function Fn(e){return JSON.stringify([e.vtSymbol.toUpperCase(),e.direction,e.offset,e.price,e.volume])}function sr(e,t){e.orders=Math.max(e.orders,t.orders),e.cancels=Math.max(e.cancels,t.cancels),e.trades=Math.max(e.trades,t.trades)}function No(e){let t=ar();for(let r of e)t.orders+=r.orders,t.cancels+=r.cancels,t.trades+=r.trades;return t}class dr{path;constructor(e=pe.data){this.path=Ho(e,"risk-state.json")}read(){if(!xo(this.path))return null;let e=Po(this.path,"utf8");if(Buffer.byteLength(e)>Mn)throw Error(`${this.path}: \u98CE\u63A7\u72B6\u6001\u8D85\u8FC7 4MB`);try{return Hn(JSON.parse(e),this.path)}catch(t){throw Error(`${this.path}: \u98CE\u63A7\u72B6\u6001\u65E0\u6548: ${t instanceof Error?t.message:String(t)}`)}}write(e){let t={savedAt:new Date().toISOString(),...e};Hn(t,this.path);let r=`${JSON.stringify(t,null,2)}
|
|
71
|
+
`;if(Buffer.byteLength(r)>Mn)throw Error(`${this.path}: \u98CE\u63A7\u72B6\u6001\u8D85\u8FC7 4MB`);Co(Bo(this.path),{recursive:!0});let n=`${this.path}.${process.pid}-${_o()}.tmp`;try{Ao(n,r,{encoding:"utf8",mode:384,flag:"wx"}),$o(n,this.path)}finally{try{Mo(n)}catch{}}}}class He{settings;paper=Me();live=Me();stateStore;persistDelayMs;schedule;persistTimer=null;dirty=!1;constructor(e={}){this.settings=e.settings??Lo(),this.stateStore=e.stateStore??null,this.persistDelayMs=e.persistDelayMs??250,this.schedule=e.schedule??pt;let t=this.stateStore?.read();if(t)this.paper=Ln(t.paper),this.live=Ln(t.live)}setTradingDay(e,t){if(!gt.test(t))return;let r=this.mode(e);if(r.tradingDay===t)return;if(r.tradingDay&&t<r.tradingDay)return;this.replaceMode(e,Me(t)),this.markDirty()}checkOpen(e){return this.checkOpenDetailed(e)?.message??null}checkOpenDetailed(e,t={}){if(e.offset!=="open")return null;let r=t.recordAttempt!==!1,n;if(r)this.setTradingDay(e.mode,e.tradingDay),n=this.mode(e.mode);else{let a=this.mode(e.mode);n=gt.test(e.tradingDay)&&(!a.tradingDay||e.tradingDay>a.tradingDay)?Me(e.tradingDay):a}if(!n.tradingDay)return{code:"trading_day_unresolved",message:"\u98CE\u63A7 TradingDay \u5C1A\u672A\u786E\u5B9A"};if(n.tradingDay!==e.tradingDay)return{code:"trading_day_mismatch",message:`\u98CE\u63A7 TradingDay \u4E0D\u4E00\u81F4 ${n.tradingDay}/${e.tradingDay||"(\u7A7A)"}`};let i=this.dailyFailure(n,e.vtSymbol.toUpperCase());if(i)return i;let o=Fn(e),s=(n.duplicateAttempts.get(o)??0)+1;if(s>=this.settings.duplicateOrderLimit){if(r)n.duplicateAttempts.set(o,s),n.duplicateRejected++,this.markDirty();return{code:"duplicate_order_limit",message:`\u76F8\u540C\u5F00\u4ED3\u8BF7\u6C42\u5F53\u65E5\u7B2C ${s} \u6B21\uFF0C\u8FBE\u5230\u91CD\u590D\u62A5\u5355\u9608\u503C ${this.settings.duplicateOrderLimit}`}}if(r)n.duplicateAttempts.set(o,s),this.markDirty();return null}recordOrder(e,t,r=""){if(r)this.setTradingDay(e,r);let n=this.mode(e);if(!n.tradingDay||r&&n.tradingDay!==r)return;let i=t.vtSymbol.toUpperCase(),o=!1;if(!n.orderIds.has(t.vtOrderId))n.orderIds.add(t.vtOrderId),this.increment(n,i,"orders"),o=!0;if(t.status==="cancelled"&&!n.cancelledOrderIds.has(t.vtOrderId))n.cancelledOrderIds.add(t.vtOrderId),this.increment(n,i,"cancels"),o=!0;if(o)this.markDirty()}recordTrade(e,t){this.setTradingDay(e,t.tradingDay);let r=this.mode(e);if(!r.tradingDay||r.tradingDay!==t.tradingDay||r.tradeIds.has(t.vtTradeId))return;r.tradeIds.add(t.vtTradeId),this.increment(r,t.vtSymbol.toUpperCase(),"trades"),this.markDirty()}reconcileLive(e){if(!gt.test(e.tradingDay))throw Error(`Live \u98CE\u63A7 TradingDay \u975E\u6CD5: ${e.tradingDay}`);if(this.setTradingDay("live",e.tradingDay),this.live.tradingDay!==e.tradingDay)throw Error(`Live \u98CE\u63A7 TradingDay \u56DE\u9000: ${this.live.tradingDay}/${e.tradingDay}`);let t=this.live,r=Me(e.tradingDay);for(let n of e.orders)this.addSnapshotOrder(r,n);for(let n of e.trades)this.addSnapshotTrade(r,n);t.orderIds=new Set([...t.orderIds,...r.orderIds]),t.cancelledOrderIds=new Set([...t.cancelledOrderIds,...r.cancelledOrderIds]),t.tradeIds=new Set([...t.tradeIds,...r.tradeIds]),sr(t.total,r.total);for(let[n,i]of r.instruments){let o=this.instrumentCounts(t,n);sr(o,i)}sr(t.total,No(t.instruments.values()));for(let[n,i]of r.duplicateAttempts)t.duplicateAttempts.set(n,Math.max(t.duplicateAttempts.get(n)??0,i));this.markDirty()}snapshot(){return{limits:{dailyLimits:{...this.settings.dailyLimits},duplicateOrderLimit:this.settings.duplicateOrderLimit},paper:this.modeSnapshot(this.paper),live:this.modeSnapshot(this.live)}}tradingDay(e){return this.mode(e).tradingDay}flush(){if(this.persistTimer?.(),this.persistTimer=null,!this.dirty||!this.stateStore)return;this.stateStore.write({paper:Nn(this.paper),live:Nn(this.live)}),this.dirty=!1}close({persist:e=!0}={}){if(e)this.flush();else this.persistTimer?.(),this.persistTimer=null}dailyFailure(e,t){let r=this.settings.dailyLimits,n=this.instrumentCounts(e,t),o=[[e.total.orders,r.totalOrderLimit,"\u5F53\u65E5\u5168\u5C40\u59D4\u6258","daily_total_order_limit"],[e.total.cancels,r.totalCancelLimit,"\u5F53\u65E5\u5168\u5C40\u64A4\u5355","daily_total_cancel_limit"],[e.total.trades,r.totalTradeLimit,"\u5F53\u65E5\u5168\u5C40\u6210\u4EA4","daily_total_trade_limit"],[n.orders,r.instrumentOrderLimit,`${t} \u5F53\u65E5\u59D4\u6258`,"daily_instrument_order_limit"],[n.cancels,r.instrumentCancelLimit,`${t} \u5F53\u65E5\u64A4\u5355`,"daily_instrument_cancel_limit"],[n.trades,r.instrumentTradeLimit,`${t} \u5F53\u65E5\u6210\u4EA4`,"daily_instrument_trade_limit"]].find(([s,a])=>s>=a);return o?{code:o[3],message:`${o[2]}\u8FBE\u5230\u4E0A\u9650 ${o[0]}/${o[1]}`}:null}addSnapshotOrder(e,t){if(t.identity.tradingDay!==e.tradingDay||e.orderIds.has(t.vtOrderId))return;if(e.orderIds.add(t.vtOrderId),this.increment(e,t.vtSymbol.toUpperCase(),"orders"),t.status==="cancelled")e.cancelledOrderIds.add(t.vtOrderId),this.increment(e,t.vtSymbol.toUpperCase(),"cancels");if(t.offset==="open"){let r=Fn({vtSymbol:t.vtSymbol,direction:t.direction,offset:t.offset,price:t.price,volume:t.volume});e.duplicateAttempts.set(r,(e.duplicateAttempts.get(r)??0)+1)}}addSnapshotTrade(e,t){if(t.tradingDay!==e.tradingDay||e.tradeIds.has(t.vtTradeId))return;e.tradeIds.add(t.vtTradeId),this.increment(e,t.vtSymbol.toUpperCase(),"trades")}increment(e,t,r){e.total[r]++,this.instrumentCounts(e,t)[r]++}instrumentCounts(e,t){let r=e.instruments.get(t);if(!r)r=ar(),e.instruments.set(t,r);return r}mode(e){return e==="paper"?this.paper:this.live}replaceMode(e,t){if(e==="paper")this.paper=t;else this.live=t}modeSnapshot(e){let t=0,r=0;for(let n of e.duplicateAttempts.values()){if(n>=2)t++;r=Math.max(r,n)}return{tradingDay:e.tradingDay,total:{...e.total},instruments:Object.fromEntries([...e.instruments.entries()].sort(([n],[i])=>n.localeCompare(i)).map(([n,i])=>[n,{...i}])),duplicateRequests:{trackedKeys:e.duplicateAttempts.size,repeatedKeys:t,maxAttempts:r,rejected:e.duplicateRejected}}}markDirty(){if(this.dirty=!0,!this.stateStore||this.persistTimer)return;this.persistTimer=this.schedule(()=>{this.persistTimer=null,this.flush()},this.persistDelayMs)}}var Vn=1e5,Vo=1e5,qo=500;function cr(){return{longToday:0,longYesterday:0,shortToday:0,shortYesterday:0}}function B(e,t){return`${t}:${e}`}function qn(e){return e!==null&&typeof e==="object"&&!Array.isArray(e)}function lr(e,t,r){if(e===null||typeof e==="string"||typeof e==="boolean")return;if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`${t} \u5305\u542B\u975E\u6709\u9650\u6570\u5B57`);return}if(typeof e!=="object")throw Error(`${t} \u5305\u542B\u4E0D\u53EF\u5E8F\u5217\u5316\u7684 ${typeof e}`);if(r.has(e))throw Error(`${t} \u5305\u542B\u5FAA\u73AF\u5F15\u7528`);if(r.add(e),Array.isArray(e))for(let n=0;n<e.length;n++)lr(e[n],`${t}[${n}]`,r);else{let n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw Error(`${t} \u5FC5\u987B\u662F\u666E\u901A\u5BF9\u8C61\u6216\u6570\u7EC4`);for(let[i,o]of Object.entries(e))lr(o,`${t}.${i}`,r)}r.delete(e)}function Un(e,t){return lr(e,t,new Set),JSON.parse(JSON.stringify(e))}function zn(e){return`${e.code}\x00${e.timeframe}\x00${e.barTime}`}function J(e){return e.longToday+e.longYesterday-e.shortToday-e.shortYesterday}function ur(e){if(!qn(e))throw Error("\u7F3A\u5C11\u7B56\u7565\u5B9E\u4F8B\u914D\u7F6E");let{id:t,vtSymbol:r,setting:n}=e,i=Object.keys(e).find((o)=>o!=="id"&&o!=="vtSymbol"&&o!=="setting");if(i)throw Error(`\u7B56\u7565\u5B9E\u4F8B\u914D\u7F6E\u4E0D\u652F\u6301\u5B57\u6BB5 "${i}"`);if(typeof t!=="string"||!we.test(t))throw Error("\u7B56\u7565 id \u5FC5\u987B\u4E3A 1-64 \u4F4D\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u70B9\u3001\u4E0B\u5212\u7EBF\u6216\u8FDE\u5B57\u7B26\uFF0C\u5E76\u4EE5\u5B57\u6BCD\u6216\u6570\u5B57\u5F00\u5934");if(typeof r!=="string")throw Error(`\u7B56\u7565 ${t} \u7F3A\u5C11 vtSymbol`);if(!qn(n))throw Error(`\u7B56\u7565 ${t} \u7684 setting \u5FC5\u987B\u662F\u5BF9\u8C61`);return{id:t,vtSymbol:K(r).vtSymbol,setting:Un(n,`\u7B56\u7565 ${t} setting`)}}function zo(){let e=nr(!0);return{getBars:async(t,r)=>{let n=_r(r.timeframe,r.count),i=await e.getLatestBars(t,"1min",n),o=ke(i,r.timeframe,r.count);if(o.selectedCount<r.count)throw Error(`\u5408\u7EA6 ${t} \u9884\u70ED\u6570\u636E\u4E0D\u8DB3\uFF1A\u9700\u8981 ${r.count} \u6839\u95ED\u5408 ${r.timeframe} Bar\uFF0C\u4EC5\u6709 ${o.availableCount} \u6839`);return o.bars},destroy:()=>e.destroy()}}class mr{strategies=new Map;instanceMap=new Map;orderOwners=new Map;terminalPaperOrders=new Set;liveOrders=new Map;liveTradeIds=new Set;pending=new Map;deps;cleanups=[];eventQueue=[];dispatching=!1;initializing=null;liveTradingDay="";timerCursorMs=null;eventTimeMs=null;tradeClaimContext=null;constructor(e={}){if(this.deps={createWarmupProvider:zo,stateStore:new $e,live:null,paperExecution:null,recordTrade:null,orderRisk:new He,executionLimits:{maxOrderVolume:re.trading.maxOrderVolume,maxPosition:re.trading.maxPosition,maxActiveOpenOrders:re.trading.maxActiveOpenOrders},log:(t)=>console.log(t),now:Date.now,schedule:pt,createPaperSessionId:Fo,observe:()=>{},emitHook:null,...e},this.deps.live)this.cleanups.push(this.deps.live.onOrder((t)=>this.enqueue(()=>{this.deps.orderRisk.recordOrder("live",t,t.identity.tradingDay),this.handleLiveOrder(t)})),this.deps.live.onTrade((t)=>this.enqueue(()=>{this.deps.orderRisk.recordTrade("live",t),this.handleLiveTrade(t)})),this.deps.live.onOrderError((t)=>this.enqueue(()=>this.handleLiveError(t))),this.deps.live.onReconciled((t)=>this.handleLiveReconciled(t)),this.deps.live.onFault((t)=>this.enqueue(()=>this.handleLiveFault(t))))}prepare(e){if(e.mode!=="paper"&&e.mode!=="live")throw Error("\u7B56\u7565 mode \u5FC5\u987B\u662F paper \u6216 live");let t=ur(e.instance),r=K(t.vtSymbol),n=Se(e.definition,t.id,t.setting),i={id:t.id,name:n.ctor.name||t.id,ctor:n.ctor,instance:n.instance,runtime:n.runtime,vtSymbol:r.vtSymbol,instrumentID:r.instrumentID,setting:n.effectiveSetting,enabled:e.enabled??e.mode==="paper",mode:e.mode,sourcePath:e.sourcePath,hash:e.hash,artifact:e.artifact,loadedAt:e.loadedAt,inventory:cr(),orders:new Map,inited:!1,trading:!1,fault:null,expectedLivePos:null,expectedLiveInventory:null,tradingDay:"",stateTimer:null,warmupRequests:[],paperSessionId:this.deps.createPaperSessionId(),paperOrderCounter:0,paperTradeCounter:0,seenTrades:new Set,tradeVolumeByOrder:new Map,eventHandlers:{timer:new Set,trades:new Set}};return n.runtime.bindContext({ctaEngine:this,strategyName:t.id,vtSymbol:r.vtSymbol,mode:e.mode,setting:n.effectiveSetting}),this.assertLiveInstrumentOwner(i),this.instanceMap.set(n.instance,i),i}addRestored(e){let t=ur(e.instance),r=B(t.id,e.mode);if(this.strategies.has(r))throw Error(`\u7B56\u7565\u5B9E\u4F8B\u91CD\u590D: ${t.id} (${e.mode})`);let n=this.prepare(e);return this.strategies.set(r,n),n}assertStateCompatible(e){this.deps.stateStore.assertCompatible(e.id,e.mode,e.vtSymbol,e.ctor.variables??[])}async warmup(){for(let e of this.strategies.values())if(!e.inited)await this.warmupOne(e)}canInitialize(){return this.deps.live?.phase==="running"}async prepareMetadata(){for(let e of this.strategies.values())await this.prepareContractMetadata(e)}async preparePendingMetadata(e,t){await this.prepareContractMetadata(this.requirePending(e,t).running)}beginReplace(e){let t=ur(e.instance),r=B(t.id,e.mode),n=this.strategies.get(r);if(n?.trading)throw Error(`\u7B56\u7565 ${n.id} \u8FD0\u884C\u4E2D\uFF0C\u8BF7\u5148\u505C\u6B62\u518D\u66F4\u65B0`);if(n)this.assertOrdersSettled(n.id,n.mode);if(n&&n.vtSymbol!==t.vtSymbol)throw Error(`\u7B56\u7565 ${n.id} \u5DF2\u7ED1\u5B9A ${n.vtSymbol}\uFF0C\u66F4\u6362\u5408\u7EA6\u5FC5\u987B\u4F7F\u7528\u65B0\u7684\u5B9E\u4F8B ID`);let i=this.prepare(e);if(this.pending.has(r))throw this.instanceMap.delete(i.instance),Error(`\u7B56\u7565 ${i.id} (${i.mode}) \u5DF2\u5728\u52A0\u8F7D\u4E2D`);return this.pending.set(r,{running:i,events:[],replayedBars:new Set,overflowed:!1,warmed:!1}),i}async warmupPending(e,t,{restoreState:r=!0}={}){let n=this.requirePending(e,t);if(await this.warmupOne(n.running,n.replayedBars,!1,r),n.warmed=!0,n.overflowed)throw Error(`\u7B56\u7565 ${e} \u521D\u59CB\u5316\u671F\u95F4\u7F13\u5B58\u884C\u60C5\u8D85\u8FC7 ${Vn} \u6761`)}async commitPending(e,t){let r=this.requirePending(e,t),n=B(e,r.running.mode);if(!r.warmed)throw Error(`\u7B56\u7565 ${e} \u5C1A\u672A\u5B8C\u6210\u521D\u59CB\u5316`);if(r.overflowed)throw Error(`\u7B56\u7565 ${e} \u521D\u59CB\u5316\u671F\u95F4\u7F13\u5B58\u6EA2\u51FA`);if(this.drainPending(r),r.running.fault)throw Error(`\u7B56\u7565 ${e} \u521D\u59CB\u5316\u671F\u95F4\u5F02\u5E38: ${r.running.fault}`);this.saveState(r.running);let i=this.strategies.get(n);if(this.strategies.set(n,r.running),this.pending.delete(n),i)await this.dispose(i,!1);return r.running}async commitPendingUninitialized(e,t){let r=this.requirePending(e,t),n=B(e,r.running.mode);if(r.warmed)throw Error(`\u7B56\u7565 ${e} \u5DF2\u521D\u59CB\u5316\uFF0C\u4E0D\u80FD\u6309\u7B49\u5F85\u72B6\u6001\u63D0\u4EA4`);r.events.length=0;let i=this.strategies.get(n);if(this.strategies.set(n,r.running),this.pending.delete(n),i)await this.dispose(i,!1);return r.running}async abortPending(e,t){let r=this.findPending(e,t);if(!r)return;let n=B(e,r.running.mode);this.pending.delete(n),await this.dispose(r.running,!1)}async start(e,t){let r=this.requireRunning(e,t);if(r.trading)return r;try{if(!r.inited||r.fault)throw Error(`\u7B56\u7565 ${e} \u672A\u5B8C\u6210\u521D\u59CB\u5316${r.fault?`: ${r.fault}`:""}`);if(await this.prepareContractMetadata(r),r.mode==="live")await this.prepareLive(r);try{this.invokeStrategySync(r,"onStart",()=>r.instance.onStart())}catch(n){throw this.failStrategy(r,"onStart",n),n}r.trading=!0,this.syncFrameworkState(r);try{this.saveState(r)}catch(n){throw this.failStrategy(r,"state",n),n}return this.writeLog(r.instance,`started mode=${r.mode}`),this.publishHook("strategy.started",this.strategyIdentity(r)),r}catch(n){throw this.publishHook("strategy.start_failed",{...this.strategyIdentity(r),reason:n instanceof Error?n.message:String(n)}),n}}async startAll(){for(let e of this.strategies.values()){if(!e.enabled)continue;try{await this.start(e.id,e.mode)}catch(t){let r=t instanceof Error?t.message:String(t);console.error(`[cta] ${e.id} \u542F\u52A8\u5931\u8D25: ${r}`)}}}async stop(e,{cancelOrders:t=!1},r){let n=this.requireRunning(e,r);if(n.trading){n.trading=!1,this.syncFrameworkState(n);try{this.invokeStrategySync(n,"onStop",()=>n.instance.onStop())}catch(i){this.failStrategy(n,"onStop",i)}if(this.trySaveState(n),this.writeLog(n.instance,"stopped"),!n.fault)this.publishHook("strategy.stopped",this.strategyIdentity(n))}if(this.syncFrameworkState(n),t)this.cancelAllDirect(n);else{let i=[...n.orders.values()].filter(w).length;if(i>0){let o=this.retainedOrdersMessage(i);this.writeLog(n.instance,o),this.publishHook("strategy.orders_retained",{...this.strategyIdentity(n),activeOrders:i,reason:o})}}return n}async stopAll(e={}){for(let t of this.strategies.values())await this.stop(t.id,e,t.mode)}cancelStrategyOrder(e,t,r){let n=this.requireRunning(e,r),i=n.orders.get(t);if(!i)throw Error(`\u59D4\u6258\u4E0D\u5C5E\u4E8E\u7B56\u7565 ${e}: ${t}`);if(!w(i))return;if(n.mode==="paper")this.cancelOrder(n.instance,t);else{if(!this.deps.live)throw Error("\u5B9E\u76D8\u901A\u9053\u4E0D\u53EF\u7528");this.deps.live.cancelOrder(t)}}cancelPaperOrder(e){if(this.terminalPaperOrders.has(e))return{vtOrderId:e,mode:"paper",result:"terminal"};let t=this.orderOwners.get(e);if(!t||t.mode!=="paper")return null;let r=t.orders.get(e);if(!r)return null;if(!w(r))return{vtOrderId:e,mode:"paper",result:"terminal"};return this.cancelStrategyOrder(t.id,e,t.mode),{vtOrderId:e,mode:"paper",result:"requested"}}async remove(e,t,{clearState:r=!1}={}){let n=this.findRunning(e,t);if(!n)return null;if(await this.stop(n.id,{},n.mode),this.assertOrdersSettled(n.id,n.mode),this.strategies.delete(B(n.id,n.mode)),await this.dispose(n,!r),r)this.deps.stateStore.remove(n.id,n.mode);return n}assertOrdersSettled(e,t){let r=this.requireRunning(e,t);if(r.orders.size>0)throw Error(`\u7B56\u7565 ${e} \u4ECD\u6709 ${r.orders.size} \u7B14\u59D4\u6258\u6216\u6210\u4EA4\u56DE\u62A5\u672A\u5B8C\u6210\uFF0C\u8BF7\u7B49\u5F85\u56DE\u62A5\u6536\u9F50\u540E\u91CD\u8BD5`)}async assertResettable(e,t){let r=this.requireRunning(e,t);if(r.trading||r.enabled)throw Error(`\u7B56\u7565 ${r.id}@${r.mode} \u5C1A\u672A\u505C\u6B62\uFF0C\u8BF7\u5148\u6267\u884C vibek strategy stop ${r.id}@${r.mode}`);if(this.assertOrdersSettled(r.id,r.mode),r.mode!=="live")return r;let n=this.deps.live;if(!n||n.phase!=="running")throw Error("TD \u672A\u8FD0\u884C\uFF0C\u65E0\u6CD5\u91CD\u7F6E Live \u7B56\u7565\u72B6\u6001");let o=(await n.queryOrders(r.instrumentID)).filter(U);if(o.length>0)throw Error(`\u5238\u5546\u5B58\u5728 ${o.length} \u7B14\u6D3B\u52A8\u59D4\u6258\uFF0C\u8BF7\u5148\u4EBA\u5DE5\u5904\u7406`);let s=await n.queryPositions(r.instrumentID);if(s.phase!=="running")throw Error("\u67E5\u8BE2\u5B9E\u76D8\u6301\u4ED3\u5931\u8D25");let a=this.inventoryFromBroker(s.positions,r.instrumentID),d=this.sidePositions(a);if(d.long!==0||d.short!==0)throw Error(`\u5408\u7EA6 ${r.instrumentID} \u4ECD\u6709\u5238\u5546\u6301\u4ED3 \u591A${d.long}/\u7A7A${d.short}\uFF0C\u4E0D\u80FD\u91CD\u7F6E\u672C\u5730\u72B6\u6001`);return r}async close({persist:e=!0}={}){if(e)await this.stopAll({cancelOrders:!0});let t=[...this.strategies.values()],r=[...this.pending.values()].map((n)=>n.running);this.strategies.clear(),this.pending.clear();for(let n of[...t,...r])await this.dispose(n,e);for(let n of this.cleanups.splice(0))n();this.deps.orderRisk.close({persist:e}),this.liveOrders.clear(),this.terminalPaperOrders.clear(),this.liveTradeIds.clear(),this.tradeClaimContext=null,this.timerCursorMs=null,this.eventTimeMs=null}has(e,t){return this.strategies.has(B(e,t))}resolve(e,t){return this.requireRunning(e,t)}setEnabled(e,t,r){let n=this.requireRunning(e,r);if(n.enabled===t)return n;return n.enabled=t,this.publishHook(t?"strategy.enabled":"strategy.disabled",this.strategyIdentity(n)),n}instruments(){return[...new Set([...this.strategies.values()].map((e)=>e.instrumentID))]}instrumentDeclarations(){return[...this.strategies.values()].map((e)=>this.declaration(e))}instrumentDeclarationsWithPending(e,t){let r=this.requirePending(e,t).running;return[...this.strategies.values()].map((n)=>this.declaration(n)).concat(this.declaration(r))}instrumentsWithPending(e,t){let r=this.requirePending(e,t).running;return[...new Set([...this.strategies.values()].map((n)=>n.instrumentID).concat(r.instrumentID))]}handleBar(e){for(let t of this.strategies.values())if(t.instrumentID===e.code&&t.inited)this.enqueue(()=>this.callStrategy(t,"onBar",()=>t.instance.onBar(e)));this.queuePending({kind:"bar",value:e},e.code)}handleTick(e){this.dispatchMarketEvent(e,[])}handleMarketEvent(e,t){this.dispatchMarketEvent(e,t)}handlePaperExternalTrade(e,t){if(e.type!=="order"||e.vtOrderId!==t.vtOrderId||e.vtSymbol!==t.vtSymbol||e.direction!==t.direction||e.offset!==t.offset||e.status!=="allTraded"||e.traded!==e.volume||t.volume!==e.volume)throw Error("\u5916\u90E8 Paper \u8BA2\u5355\u4E0E\u6210\u4EA4\u4E0D\u4E00\u81F4");let r={order:{...e},origin:"external",tradingDay:t.tradingDay,tradedVolume:t.volume,claimable:e.offset==="open",ownerId:null};this.enqueue(()=>{this.dispatchTrades("paper",{...t},{...e},"external",{observed:r,firstTrade:!0,allowPaper:!0})})}handleTimer(e){if(!Number.isFinite(e)||e<0)throw Error("Timer \u65F6\u95F4\u5FC5\u987B\u662F\u975E\u8D1F\u6709\u9650\u6570");let t=Math.floor(e/1000)*1000;if(this.timerCursorMs===null){this.timerCursorMs=t;return}if(t<this.timerCursorMs)throw Error("Timer \u65F6\u95F4\u4E0D\u80FD\u56DE\u9000");for(let r=this.timerCursorMs+1000;r<=t;r+=1000)this.dispatchTimer(r);this.timerCursorMs=t}setTimerClock(e){if(!Number.isFinite(e)||e<0)throw Error("Timer \u65F6\u95F4\u5FC5\u987B\u662F\u975E\u8D1F\u6709\u9650\u6570");this.timerCursorMs=Math.floor(e/1000)*1000}dispatchMarketEvent(e,t){let r=e.tradingDay??e.actionDay??"";this.deps.orderRisk.setTradingDay("paper",r);let n=new Map;for(let i of t){let o=n.get(i.code)??[];o.push(i),n.set(i.code,o)}for(let i of this.strategies.values()){if(!i.inited)continue;let o=i.instrumentID===e.code,s=n.get(i.instrumentID)??[];if(!o&&s.length===0)continue;this.enqueue(()=>{if(o&&i.mode==="paper")this.rollPaperDay(i,r),this.matchPaperOrders(i,e);for(let a of s){if(i.fault)break;this.callStrategy(i,"onBar",()=>i.instance.onBar(a))}if(o)this.callStrategy(i,"onTick",()=>i.instance.onTick(e))})}for(let i of t)this.queuePending({kind:"bar",value:i},i.code);this.queuePending({kind:"tick",value:e},e.code)}snapshot(e={}){return[...this.strategies.values()].map((t)=>{let r=[...t.orders.values()].filter(w).map((n)=>({...n,type:"order"}));return{id:t.id,name:t.name,sourcePath:t.sourcePath,hash:t.hash,loadedAt:t.loadedAt,mode:t.mode,vtSymbol:t.vtSymbol,instrumentID:t.instrumentID,setting:t.setting,enabled:t.enabled,inited:t.inited,trading:t.trading,pos:J(t.inventory),inventory:{...t.inventory},activeOrders:r.length,activeOrderDetails:r,warning:!t.trading&&r.length>0?this.retainedOrdersMessage(r.length):null,fault:t.fault,...e.variables?this.variablesSnapshot(t):{}}})}variablesSnapshot(e){try{return{variables:this.serializeVariables(e),variablesError:null}}catch(t){return{variables:null,variablesError:t instanceof Error?t.message:String(t)}}}riskSnapshot(){return this.deps.orderRisk.snapshot()}sendOrder(e,t,r,n,i){let o=this.requireInstance(e);if(!o.trading||o.fault)return[];if(!Number.isFinite(n)||n<=0||!Number.isInteger(i)||i<=0){let l=`price/volume \u975E\u6CD5 (${n}, ${i})`;return this.writeLog(e,`\u62D2\u7EDD\u59D4\u6258\uFF1A${l}`),this.emitOrderError(o,"invalidRequest",t,r,n,i,l),[]}if(i>this.deps.executionLimits.maxOrderVolume){let l=`\u6570\u91CF ${i} \u8D85\u8FC7\u5355\u7B14\u4E0A\u9650 ${this.deps.executionLimits.maxOrderVolume}`;return this.writeLog(e,`\u62D2\u7EDD\u59D4\u6258\uFF1A${l}`),this.emitRiskRejection(o,"max_order_volume",t,r,n,i,l),this.emitOrderError(o,"maxOrderVolume",t,r,n,i,l),[]}let s=this.resolveOrderParts(o,t,r,n,i);if(s.length===0)return[];if(r==="open"){if([...o.orders.values()].filter((f)=>f.offset==="open"&&w(f)).length>=this.deps.executionLimits.maxActiveOpenOrders){let f=`\u6D3B\u52A8\u5F00\u4ED3\u59D4\u6258\u8FBE\u5230\u4E0A\u9650 ${this.deps.executionLimits.maxActiveOpenOrders}`;return this.writeLog(e,`\u62D2\u7EDD\u5F00\u4ED3\uFF1A${f}`),this.emitRiskRejection(o,"max_active_open_orders",t,r,n,i,f),this.emitOrderError(o,"maxActiveOpenOrders",t,r,n,i,f),[]}}let a=this.roundPrice(o,n);if(!Number.isFinite(a)||a<=0){let l=`price/volume \u975E\u6CD5 (\u820D\u5165\u540E\u4EF7\u683C ${a}\uFF0C\u539F\u59CB ${n})`;return this.writeLog(e,`\u62D2\u7EDD\u59D4\u6258\uFF1A${l}`),this.emitOrderError(o,"invalidRequest",t,r,n,i,l),[]}let d=o.mode==="live"?this.liveTradingDay||o.tradingDay||this.deps.orderRisk.tradingDay("live"):o.tradingDay||this.deps.orderRisk.tradingDay("paper"),c=this.deps.orderRisk.checkOpenDetailed({mode:o.mode,tradingDay:d,vtSymbol:o.vtSymbol,direction:t,offset:r,price:a,volume:i});if(c)return this.writeLog(e,`\u62D2\u7EDD\u5F00\u4ED3\uFF1A${c.message}`),this.emitRiskRejection(o,c.code,t,r,a,i,c.message),this.emitOrderError(o,"riskRejected",t,r,n,i,c.message),[];let u=[];for(let l of s){let f=this.submitPhysical(o,t,l.offset,a,l.volume);if(f)u.push(f)}return u}cancelOrder(e,t){let r=this.requireInstance(e),n=r.orders.get(t);if(!n||!w(n))return;if(r.mode==="paper")this.deps.paperExecution?.onCancelRequested?.(Object.freeze({...n})),n.status="cancelled",n.statusMessage="paper cancelled",this.queueOrder(r,n);else try{this.deps.live?.cancelOrder(t)}catch(i){this.handleLiveError({vtOrderId:t,orderRef:t.slice(4),instrumentID:r.instrumentID,operation:"cancel",message:i instanceof Error?i.message:String(i)})}}cancelAll(e){this.cancelAllDirect(this.requireInstance(e))}liquidatePaper(e,t,r){let n=this.requireRunning(e,"paper");if(n.mode!=="paper")throw Error(`\u7B56\u7565 ${e} \u4E0D\u662F Paper \u6A21\u5F0F`);this.deps.orderRisk.setTradingDay("paper",t.tradingDay??t.actionDay??n.tradingDay);let i=n.trading;n.trading=!1,this.syncFrameworkState(n),this.cancelAllDirect(n);let o=[{direction:"short",offset:"closeYesterday",volume:n.inventory.longYesterday},{direction:"short",offset:"closeToday",volume:n.inventory.longToday},{direction:"long",offset:"closeYesterday",volume:n.inventory.shortYesterday},{direction:"long",offset:"closeToday",volume:n.inventory.shortToday}],s=[];for(let a of o){if(a.volume<=0)continue;let d=a.direction==="long"?t.askPrice1??t.lastPrice:t.bidPrice1??t.lastPrice,c={vtOrderId:`PAPERLIQ.${n.id}.${n.paperSessionId}.${++n.paperOrderCounter}`,vtSymbol:n.vtSymbol,direction:a.direction,offset:a.offset,price:d,volume:a.volume,traded:a.volume,status:"allTraded",statusMessage:r,datetime:t.datetime??this.deps.now()},u=this.paperFill(c,t,a.volume,"marginLiquidation");c.price=u.price,n.orders.set(c.vtOrderId,c),this.orderOwners.set(c.vtOrderId,n),this.emitOrder(n,c);let l={vtTradeId:`PAPERLIQTRADE.${n.id}.${n.paperSessionId}.${++n.paperTradeCounter}`,vtOrderId:c.vtOrderId,vtSymbol:n.vtSymbol,direction:a.direction,offset:a.offset,price:u.price,volume:a.volume,datetime:t.datetime??this.deps.now(),tradingDay:t.tradingDay??t.actionDay??""};this.deps.paperExecution?.onTrade?.(l,u,"marginLiquidation"),this.applyTrade(n,l),s.push(l)}if(i)try{this.invokeStrategySync(n,"onStop",()=>n.instance.onStop())}catch(a){this.failStrategy(n,"onStop",a)}return this.cancelAllDirect(n),this.syncFrameworkState(n),this.scheduleState(n,!0),this.writeLog(n.instance,`\u5F3A\u5236\u5E73\u4ED3\u5E76\u505C\u6B62: ${r}`),s}loadBars(e,t,r){let n=this.requireInitializing(e,"loadBars");if(!t||typeof t!=="object"||Array.isArray(t))throw Error("loadBars request \u5FC5\u987B\u662F\u5BF9\u8C61");if(!Z(t.timeframe))throw Error(`loadBars timeframe \u4E0D\u53D7\u652F\u6301: ${String(t.timeframe)}`);if(!Number.isInteger(t.count)||t.count<=0)throw Error("loadBars count \u5FC5\u987B\u4E3A\u6B63\u6574\u6570");if(n.warmupRequests.length>0)throw Error("\u6BCF\u4E2A\u7B56\u7565\u53EA\u80FD\u767B\u8BB0\u4E00\u4E2A loadBars \u8BF7\u6C42");n.warmupRequests.push({timeframe:t.timeframe,count:t.count,callback:r})}registerEvent(e,t,r){let n=this.requireInitializing(e,"registerEvent");if(t!=="timer"&&t!=="trades")throw Error(`\u4E0D\u652F\u6301\u7684\u7B56\u7565\u4E8B\u4EF6: ${String(t)}`);if(typeof r!=="function")throw Error("\u7B56\u7565\u4E8B\u4EF6 handler \u5FC5\u987B\u662F\u51FD\u6570");let i=n.eventHandlers[t];i.add(r);let o=!0;return()=>{if(!o)return;o=!1,i.delete(r)}}claimOrder(e,t){let r=this.requireInstance(e),n=this.tradeClaimContext;if(r.mode!=="live"&&!(r.mode==="paper"&&n?.allowPaper))return{ok:!1,reason:"\u53EA\u6709 Live \u7B56\u7565\u6216\u4EA4\u4E92\u56DE\u6D4B\u53EF\u4EE5\u8BA4\u9886\u5916\u90E8\u8BA2\u5355"};if(!r.inited||!r.trading||r.fault)return{ok:!1,reason:"\u7B56\u7565\u672A\u5904\u4E8E\u4EA4\u6613\u72B6\u6001"};if(!n||n.running!==r||n.trade.vtOrderId!==t)return{ok:!1,reason:"claimOrder \u53EA\u80FD\u5728\u5F53\u524D\u8BA2\u5355\u7684 trades \u4E8B\u4EF6\u4E2D\u8C03\u7528"};let{observed:i,trade:o}=n;if(i.ownerId!==null||this.orderOwners.has(t))return{ok:!1,reason:"\u8BA2\u5355\u5DF2\u7ECF\u5F52\u5C5E\u7B56\u7565"};let s=i.order;if(!s)return{ok:!1,reason:"\u6210\u4EA4\u5173\u8054\u7684\u8BA2\u5355\u4FE1\u606F\u5C1A\u672A\u5B8C\u6574"};if(!n.firstTrade)return{ok:!1,reason:"\u53EA\u80FD\u8BA4\u9886\u5916\u90E8\u8BA2\u5355\u7684\u9996\u7B14\u65B0\u6210\u4EA4"};if(i.origin!=="external"||s.offset!=="open"||o.offset!=="open")return{ok:!1,reason:"\u53EA\u80FD\u8BA4\u9886\u5916\u90E8\u5F00\u4ED3\u8BA2\u5355"};if(s.vtSymbol!==r.vtSymbol||o.vtSymbol!==r.vtSymbol)return{ok:!1,reason:"\u8BA2\u5355\u5408\u7EA6\u4E0E\u7B56\u7565\u4E0D\u4E00\u81F4"};if(s.direction!==o.direction||o.volume>s.volume||s.status==="rejected")return{ok:!1,reason:"\u8BA2\u5355\u4E0E\u6210\u4EA4\u4FE1\u606F\u4E0D\u4E00\u81F4"};if(r.orders.size>0||Object.values(r.inventory).some((d)=>d!==0))return{ok:!1,reason:"\u7B56\u7565\u5DF2\u6709\u6301\u4ED3\u6216\u672A\u5B8C\u6210\u7684\u8BA2\u5355"};if(r.mode==="live")this.rollLiveDay(r,o.tradingDay);else this.rollPaperDay(r,o.tradingDay);let a={...s,traded:Math.max(s.traded,o.volume)};return i.ownerId=r.id,r.orders.set(t,a),this.orderOwners.set(t,r),this.queueOrder(r,a),this.applyTrade(r,{...o},!0,!1),this.writeLog(r.instance,`claimed external order ${t} on trade ${o.vtTradeId}`),{ok:!0}}writeLog(e,t){this.deps.log(`[cta:${e.strategyName}:${e.mode}] ${t}`)}emitHook(e,t,r){let n=this.requireInstance(e),i=this.eventTimeMs??this.deps.now(),o=er("strategy.event",{...this.strategyIdentity(n),name:t,data:r});fe({type:"hook",id:"00000000-0000-4000-8000-000000000000",event:"strategy.event",account:"a".repeat(64),occurredAt:i,payload:o});try{this.deps.emitHook?.("strategy.event",o,i)}catch(s){this.writeLog(e,`Hook \u6295\u9012\u5931\u8D25 name=${o.name}: ${s instanceof Error?s.message:String(s)}`)}}getCurrentTime(e){return this.requireInstance(e),this.eventTimeMs??this.deps.now()}getContract(e){let t=this.requireInstance(e),r=t.vtSymbol.split(".")[1],n=this.deps.live?.getInstrument(t.instrumentID);if(!n)throw Error(`\u5408\u7EA6 ${t.vtSymbol} Contract \u5C1A\u672A\u51C6\u5907`);if(n.instrumentID.toUpperCase()!==t.instrumentID)throw Error(`\u5408\u7EA6\u4EE3\u7801\u4E0D\u4E00\u81F4\uFF1A\u7B56\u7565 ${t.instrumentID}\uFF0C\u5143\u6570\u636E ${n.instrumentID}`);if(n.exchangeID.toUpperCase()!==r)throw Error(`\u5408\u7EA6\u4EA4\u6613\u6240\u4E0D\u4E00\u81F4\uFF1A\u7B56\u7565 ${r}\uFF0C\u5143\u6570\u636E ${n.exchangeID}`);if(!Number.isFinite(n.priceTick)||n.priceTick<=0||!Number.isSafeInteger(n.volumeMultiple)||n.volumeMultiple<=0||!Number.isSafeInteger(n.minLimitOrderVolume)||n.minLimitOrderVolume<=0||!Number.isSafeInteger(n.maxLimitOrderVolume)||n.maxLimitOrderVolume<n.minLimitOrderVolume||!Number.isFinite(n.longMarginRatio)||n.longMarginRatio<0||!Number.isFinite(n.shortMarginRatio)||n.shortMarginRatio<0)throw Error(`\u5408\u7EA6\u89C4\u683C\u975E\u6CD5: ${t.vtSymbol}`);return Object.freeze({vtSymbol:`${n.instrumentID}.${n.exchangeID}`.toUpperCase(),instrumentID:n.instrumentID.toUpperCase(),exchangeID:n.exchangeID.toUpperCase(),productID:n.productID.toUpperCase(),name:n.instrumentName,priceTick:n.priceTick,size:n.volumeMultiple,minOrderVolume:n.minLimitOrderVolume,maxOrderVolume:n.maxLimitOrderVolume,longMarginRatio:n.longMarginRatio,shortMarginRatio:n.shortMarginRatio,isTrading:n.isTrading===1})}getCommission(e){let t=this.requireInstance(e),r=t.vtSymbol.split(".")[1],n=this.deps.live?.getCommission(t.instrumentID,r);if(!n)throw Error(`\u5408\u7EA6 ${t.vtSymbol} Commission \u5C1A\u672A\u51C6\u5907`);let i=this.getContract(e);if(n.instrumentID.toUpperCase()!==t.instrumentID&&n.instrumentID.toUpperCase()!==i.productID||n.exchangeID&&n.exchangeID.toUpperCase()!==r)throw Error(`\u624B\u7EED\u8D39\u5408\u7EA6\u4E0D\u4E00\u81F4: ${t.vtSymbol}`);if([n.openRatioByMoney,n.openRatioByVolume,n.closeRatioByMoney,n.closeRatioByVolume,n.closeTodayRatioByMoney,n.closeTodayRatioByVolume].some((o)=>!Number.isFinite(o)||o<0))throw Error(`\u624B\u7EED\u8D39\u7387\u975E\u6CD5: ${t.vtSymbol}`);return Object.freeze({open:Object.freeze({ratioByMoney:n.openRatioByMoney,perLot:n.openRatioByVolume}),close:Object.freeze({ratioByMoney:n.closeRatioByMoney,perLot:n.closeRatioByVolume}),closeToday:Object.freeze({ratioByMoney:n.closeTodayRatioByMoney,perLot:n.closeTodayRatioByVolume})})}getPriceTick(e){return this.getContract(e).priceTick}getSize(e){return this.getContract(e).size}async warmupOne(e,t,r=!0,n=!0){e.warmupRequests=[],this.clearEventHandlers(e),e.inited=!1,e.trading=!1,this.syncFrameworkState(e),await this.prepareContractMetadata(e),this.initializing=e;try{this.invokeStrategySync(e,"onInit",()=>e.instance.onInit())}catch(i){throw Error(`\u7B56\u7565 ${e.id} onInit \u5931\u8D25: ${i instanceof Error?i.message:String(i)}`)}finally{this.initializing=null}if(e.warmupRequests.length>0){let i=this.deps.createWarmupProvider();try{for(let o of e.warmupRequests){let s={timeframe:o.timeframe,count:o.count},a=await i.getBars(e.instrumentID,s),d=Et(a,o.timeframe);if(d<o.count)throw Error(`\u5408\u7EA6 ${e.instrumentID} \u9884\u70ED\u6570\u636E\u4E0D\u5B8C\u6574\uFF1A\u9700\u8981 ${o.count} \u6839\u95ED\u5408 ${o.timeframe} Bar\uFF0C\u5B9E\u9645 ${d} \u6839`);for(let c of a)t?.add(zn(c)),this.invokeStrategySync(e,"loadBars callback",()=>o.callback(c));this.writeLog(e.instance,`loadBars ${d} ${o.timeframe} bars / ${a.length} source 1min bars`)}}catch(o){throw Error(`\u7B56\u7565 ${e.id} \u521D\u59CB\u5316\u6570\u636E\u5931\u8D25: ${o instanceof Error?o.message:String(o)}`)}finally{i.destroy()}}if(n)this.restoreState(e);if(this.serializeVariables(e),e.inited=!0,this.syncFrameworkState(e),r)this.saveState(e)}restoreState(e){let t=this.deps.stateStore.read(e.id,e.mode);if(this.observe(e,{type:"state",operation:"read",value:t?{pos:t.pos,inventory:{...t.inventory},variables:structuredClone(t.variables),tradingDay:t.tradingDay}:null}),!t)return;if(t.strategyId!==e.id||t.mode!==e.mode)throw Error(`\u7B56\u7565 ${e.id} \u72B6\u6001\u8EAB\u4EFD\u4E0D\u5339\u914D`);if(t.vtSymbol!==e.vtSymbol)throw Error(`\u7B56\u7565 ${e.id} \u7684\u72B6\u6001\u5C5E\u4E8E ${t.vtSymbol}\uFF0C\u4E0D\u80FD\u6062\u590D\u5230 ${e.vtSymbol}\uFF1B\u8BF7\u4F7F\u7528\u65B0\u7684\u5B9E\u4F8B ID`);if(e.runtime.restoreVariables(t.variables),e.expectedLivePos=t.pos,e.tradingDay=t.tradingDay,e.mode==="paper")e.inventory=t.inventory,this.syncFrameworkState(e);else e.inventory=t.inventory,e.expectedLiveInventory={...e.inventory},this.syncFrameworkState(e)}async prepareLive(e){let t=this.deps.live;if(!t||t.phase!=="running")throw Error("TD \u672A\u8FD0\u884C\uFF0C\u65E0\u6CD5\u542F\u52A8 Live \u7B56\u7565");let r=e.vtSymbol.split(".")[1],n=await t.prepareInstrument(e.instrumentID,r);if(n.exchangeID.toUpperCase()!==r)throw Error(`\u5408\u7EA6\u4EA4\u6613\u6240\u4E0D\u4E00\u81F4\uFF1A\u7B56\u7565 ${r}\uFF0CCTP ${n.exchangeID}`);let i=await t.queryOrders(e.instrumentID);for(let u of i)this.observeLiveOrder(this.orderDataFromBroker(u),u.identity.tradingDay,!1);let o=i.filter(U);if(o.length>0)throw Error(`\u5238\u5546\u5B58\u5728 ${o.length} \u7B14\u6D3B\u52A8\u59D4\u6258\uFF0C\u8BF7\u5148\u4EBA\u5DE5\u5904\u7406`);let s=await t.queryPositions(e.instrumentID);if(s.phase!=="running")throw Error("\u67E5\u8BE2\u5B9E\u76D8\u6301\u4ED3\u5931\u8D25");let a=this.inventoryFromBroker(s.positions,e.instrumentID),d=J(a),c=this.sidePositions(a);if(e.expectedLivePos===null&&(c.long!==0||c.short!==0))throw Error(`\u5408\u7EA6 ${e.instrumentID} \u5DF2\u6709\u5238\u5546\u6301\u4ED3 \u591A${c.long}/\u7A7A${c.short}\uFF0C\u65B0\u7B56\u7565\u62D2\u7EDD\u8BA4\u9886`);if(e.expectedLivePos!==null&&e.expectedLivePos!==d)throw Error(`\u6301\u4ED3\u4E0D\u4E00\u81F4\uFF1A\u672C\u5730 ${e.expectedLivePos}\uFF0C\u5238\u5546 ${d}`);if(e.expectedLiveInventory){let u=this.sidePositions(e.expectedLiveInventory);if(u.long!==c.long||u.short!==c.short)throw Error(`\u6301\u4ED3\u4E0D\u4E00\u81F4\uFF1A\u672C\u5730\u591A${u.long}/\u7A7A${u.short}\uFF0C\u5238\u5546\u591A${c.long}/\u7A7A${c.short}`)}e.inventory=a,e.expectedLivePos=d,e.expectedLiveInventory={...a},e.tradingDay=this.liveTradingDay||s.positions.find((u)=>u.instrumentID===e.instrumentID)?.tradingDay||e.tradingDay,this.syncFrameworkState(e)}async prepareContractMetadata(e){let t=this.deps.live;if(!t||t.phase!=="running")throw Error(`TD \u672A\u8FD0\u884C\uFF0C\u65E0\u6CD5\u521D\u59CB\u5316\u7B56\u7565 ${e.id}`);let r=e.vtSymbol.split(".")[1],n=await t.prepareInstrument(e.instrumentID,r);if(n.exchangeID.toUpperCase()!==r)throw Error(`\u5408\u7EA6\u4EA4\u6613\u6240\u4E0D\u4E00\u81F4\uFF1A\u7B56\u7565 ${r}\uFF0CCTP ${n.exchangeID}`);if(tt(n.exchangeID,n.productID),!this.getContract(e.instance).isTrading)throw Error(`\u5408\u7EA6\u5F53\u524D\u4E0D\u53EF\u4EA4\u6613: ${e.vtSymbol}`);await t.prepareCommission(e.instrumentID,r)}declaration(e){let t=e.vtSymbol.split(".")[1];return{instrumentID:e.instrumentID,exchangeID:t,vtSymbol:e.vtSymbol}}inventoryFromBroker(e,t){let r=cr();for(let n of e){if(n.instrumentID.toUpperCase()!==t.toUpperCase())continue;if(n.hedgeFlag!=="1"&&n.volume!==0)throw Error(`\u6682\u4E0D\u652F\u6301\u975E\u6295\u673A\u6301\u4ED3: ${n.instrumentID}/${n.hedgeFlag}`);if(!Number.isInteger(n.todayVolume)||n.todayVolume<0||!Number.isInteger(n.yesterdayVolume)||n.yesterdayVolume<0||n.todayVolume+n.yesterdayVolume!==n.volume)throw Error(`\u5238\u5546\u6301\u4ED3\u6570\u636E\u975E\u6CD5: ${n.instrumentID}`);if(n.direction==="long")r.longToday+=n.todayVolume,r.longYesterday+=n.yesterdayVolume;else r.shortToday+=n.todayVolume,r.shortYesterday+=n.yesterdayVolume}return r}sidePositions(e){return{long:e.longToday+e.longYesterday,short:e.shortToday+e.shortYesterday}}resolveOrderParts(e,t,r,n,i){if(r==="open"){let c=t==="long"?e.inventory.longToday+e.inventory.longYesterday:e.inventory.shortToday+e.inventory.shortYesterday,u=[...e.orders.values()].filter((l)=>l.offset==="open"&&l.direction===t).reduce((l,f)=>l+this.unsettledOrderVolume(e,f),0);if(c+u+i>this.deps.executionLimits.maxPosition){let l=`\u9884\u8BA1\u6301\u4ED3 ${c+u+i} \u8D85\u8FC7\u4E0A\u9650 ${this.deps.executionLimits.maxPosition}`;return this.writeLog(e.instance,`\u62D2\u7EDD\u5F00\u4ED3\uFF1A${l}`),this.emitRiskRejection(e,"max_position",t,r,n,i,l),this.emitOrderError(e,"maxPosition",t,r,n,i,l),[]}return[{offset:r,volume:i}]}let o=this.availableClose(e,t);if(r==="closeToday"){if(i>o.today)return this.rejectInsufficientClose(e,t,r,n,i,o);return[{offset:r,volume:i}]}if(r==="closeYesterday"){if(i>o.yesterday)return this.rejectInsufficientClose(e,t,r,n,i,o);return[{offset:r,volume:i}]}if(i>o.today+o.yesterday)return this.rejectInsufficientClose(e,t,r,n,i,o);if(!Pr(e.vtSymbol))return[{offset:"close",volume:i}];let s=[],a=Math.min(i,o.yesterday);if(a>0)s.push({offset:"closeYesterday",volume:a});let d=i-a;if(d>0)s.push({offset:"closeToday",volume:d});return s}rejectInsufficientClose(e,t,r,n,i,o){let s=`\u8BF7\u6C42 ${i}\uFF0C\u53EF\u7528\u4ECA ${o.today}/\u6628 ${o.yesterday}`;return this.writeLog(e.instance,`\u62D2\u7EDD\u5E73\u4ED3\uFF1A${s}`),this.emitOrderError(e,"insufficientPosition",t,r,n,i,s),[]}availableClose(e,t){let r=t==="short"?e.inventory.longToday:e.inventory.shortToday,n=t==="short"?e.inventory.longYesterday:e.inventory.shortYesterday,i=0;for(let s of e.orders.values()){if(s.direction!==t||s.offset==="open")continue;let a=this.unsettledOrderVolume(e,s);if(a<=0)continue;if(s.offset==="closeToday")r-=a;else if(s.offset==="closeYesterday")n-=a;else i+=a}r=Math.max(0,r),n=Math.max(0,n);let o=Math.min(n,i);return n-=o,r=Math.max(0,r-(i-o)),{today:r,yesterday:n}}unsettledOrderVolume(e,t){let r=e.tradeVolumeByOrder.get(t.vtOrderId)??0,n=w(t)?t.volume:t.traded;return Math.max(0,n-r)}submitPhysical(e,t,r,n,i){let o;if(e.mode==="paper")o=`PAPER.${e.id}.${e.paperSessionId}.${++e.paperOrderCounter}`;else{let a=this.deps.live;if(!a)return this.emitOrderError(e,"submitFailed",t,r,n,i,"\u5B9E\u76D8\u901A\u9053\u4E0D\u53EF\u7528"),null;try{o=a.submitOrder({strategyId:e.id,instrumentID:e.instrumentID,direction:t==="long"?"buy":"sell",offsetFlag:r,price:n,volume:i}).vtOrderId}catch(d){let c=d instanceof Error?d.message:String(d);return this.writeLog(e.instance,`\u5B9E\u76D8\u62A5\u5355\u5931\u8D25: ${c}`),this.emitOrderError(e,"submitFailed",t,r,n,i,c),null}}let s={vtOrderId:o,vtSymbol:e.vtSymbol,direction:t,offset:r,price:n,volume:i,traded:0,status:"submitting",statusMessage:"",datetime:this.eventTimeMs??this.deps.now()};if(e.mode==="paper"){let a=this.deps.paperExecution?.acceptOrder?.(s)??{ok:!0};if(!a.ok)return this.writeLog(e.instance,`\u62D2\u7EDD\u59D4\u6258\uFF1A${a.reason}`),this.emitOrderError(e,"paperRejected",t,r,n,i,a.reason),null}if(e.orders.set(o,s),this.orderOwners.set(o,e),e.mode==="live"){let a=this.observeLiveOrder(s,this.liveTradingDay||e.tradingDay,!1);a.origin="strategy",a.ownerId=e.id}if(this.deps.orderRisk.recordOrder(e.mode,s,e.tradingDay),e.mode==="paper")this.deps.paperExecution?.onOrderSubmitted?.(Object.freeze({...s}));if(this.queueOrder(e,s),e.mode==="paper")this.enqueue(()=>{if(s.status!=="submitting")return;s.status="notTraded",this.emitOrder(e,s)});return this.scheduleState(e),o}matchPaperOrders(e,t){for(let r of[...e.orders.values()]){if(r.status!=="notTraded")continue;let n=r.direction==="long",i=n?t.askPrice1:t.bidPrice1,o=n?t.askVolume1:t.bidVolume1;if(i===void 0||!Number.isFinite(i)||i<=0)continue;if(o!==void 0&&(!Number.isFinite(o)||o<=0))continue;if(n?r.price<i:r.price>i)continue;let s=r.volume-r.traded,a=this.paperFill(r,t,s,"strategy");r.traded=r.volume,r.status="allTraded",r.statusMessage="paper matched",this.emitOrder(e,r);let d={vtTradeId:`PAPERTRADE.${e.id}.${e.paperSessionId}.${++e.paperTradeCounter}`,vtOrderId:r.vtOrderId,vtSymbol:e.vtSymbol,direction:r.direction,offset:r.offset,price:a.price,volume:s,datetime:t.datetime??this.deps.now(),tradingDay:t.tradingDay??t.actionDay??""};this.deps.paperExecution?.onTrade?.(d,a,"strategy"),this.applyTrade(e,d)}}handleLiveOrder(e){let t=e.vtOrderId;this.observeLiveOrder(this.orderDataFromBroker(e),e.identity.tradingDay);let r=this.orderOwners.get(t);if(!r||r.mode!=="live")return;let n=r.orders.get(t);if(!n)return;n.price=e.price,n.volume=e.volume,n.traded=e.traded,n.status=e.status,n.statusMessage=e.statusMessage,this.emitOrder(r,n),this.scheduleState(r,!0)}observeLiveOrder(e,t,r=!0){let n=this.liveOrders.get(e.vtOrderId);if(!n)n={order:null,origin:"external",tradingDay:t,tradedVolume:0,claimable:r,ownerId:null},this.liveOrders.set(e.vtOrderId,n);return n.order={...e,type:"order"},n.claimable&&=r,n}orderDataFromBroker(e){return{type:"order",vtOrderId:e.vtOrderId,vtSymbol:e.vtSymbol,direction:e.direction,offset:e.offset,price:e.price,volume:e.volume,traded:e.traded,status:e.status,statusMessage:e.statusMessage,datetime:e.datetime}}handleLiveTrade(e){if(!this.rememberTradeId(this.liveTradeIds,e.vtTradeId))return;let t=e.vtOrderId,r=this.orderOwners.get(t),n=this.liveOrders.get(t);if(!n)n={order:null,origin:"external",tradingDay:e.tradingDay,tradedVolume:0,claimable:!1,ownerId:null},this.liveOrders.set(t,n);let i=n.claimable&&n.tradedVolume===0&&this.deps.live?.phase==="running"&&(!this.liveTradingDay||e.tradingDay===this.liveTradingDay);n.tradedVolume+=e.volume,n.claimable=!1;let o={vtTradeId:e.vtTradeId,vtOrderId:t,vtSymbol:e.vtSymbol,direction:e.direction,offset:e.offset,price:e.price,volume:e.volume,datetime:e.datetime,tradingDay:e.tradingDay};if(r?.mode==="live")this.rollLiveDay(r,e.tradingDay),this.applyTrade(r,o);this.dispatchTrades("live",o,n.order,n.origin,{observed:n,firstTrade:i,allowPaper:!1})}handleLiveError(e){let t=this.orderOwners.get(e.vtOrderId);if(!t)return;let r=t.orders.get(e.vtOrderId);if(!r||!w(r))return;if(e.operation==="cancel"){r.statusMessage=`\u64A4\u5355\u5931\u8D25: ${e.message}`,this.emitOrder(t,r),this.scheduleState(t,!0);return}r.status="rejected",r.statusMessage=e.message,this.emitOrder(t,r),this.scheduleState(t,!0)}handleLiveReconciled(e){this.deps.orderRisk.reconcileLive(e),this.liveTradingDay=e.tradingDay;for(let[o,s]of this.liveOrders)if(s.tradingDay<e.tradingDay&&!this.orderOwners.has(o))this.liveOrders.delete(o);for(let o of e.orders)this.observeLiveOrder(this.orderDataFromBroker(o),o.identity.tradingDay,!1);for(let o of e.trades){if(!this.rememberTradeId(this.liveTradeIds,o.vtTradeId))continue;let s=this.liveOrders.get(o.vtOrderId);if(s)s.tradedVolume+=o.volume}if(e.reason!=="reconnect")return;let t=[...this.strategies.values()].filter((o)=>o.mode==="live"&&o.trading);if(t.length===0)return;let r=new Map;for(let o of t){let s=r.get(o.instrumentID)??[];s.push(o),r.set(o.instrumentID,s)}let n=new Map(e.orders.map((o)=>[o.vtOrderId,o]));for(let o of e.orders){if(!U(o)||!r.has(o.instrumentID))continue;let s=this.orderOwners.get(o.vtOrderId);if(!s||!s.trading)throw Error(`\u6062\u590D\u53D1\u73B0\u672A\u5F52\u5C5E\u7684\u6D3B\u52A8\u59D4\u6258: ${o.vtOrderId}`)}for(let o of t)for(let s of o.orders.values())if(w(s)&&!n.has(s.vtOrderId))throw Error(`\u672C\u5730\u6D3B\u52A8\u59D4\u6258\u672A\u51FA\u73B0\u5728\u5238\u5546\u5FEB\u7167: ${s.vtOrderId}`);let i=new Map;for(let o of t)i.set(o,this.inventoryOnTradingDay(o,e.tradingDay));for(let[o,s]of r){let a=this.inventoryFromBroker(e.positions,o),d=s.reduce((c,u)=>{let l=i.get(u);return{longToday:c.longToday+l.longToday,longYesterday:c.longYesterday+l.longYesterday,shortToday:c.shortToday+l.shortToday,shortYesterday:c.shortYesterday+l.shortYesterday}},cr());if(!this.sameInventory(a,d))throw Error(`\u6062\u590D\u6301\u4ED3\u4E0D\u4E00\u81F4 ${o}: \u672C\u5730\u4ECA/\u6628 \u591A${d.longToday}/${d.longYesterday}`+` \u7A7A${d.shortToday}/${d.shortYesterday}\uFF0C\u5238\u5546\u4ECA/\u6628 \u591A${a.longToday}/${a.longYesterday}`+` \u7A7A${a.shortToday}/${a.shortYesterday}`)}for(let[o,s]of i)o.inventory=s,o.expectedLivePos=J(s),o.expectedLiveInventory={...s},o.tradingDay=e.tradingDay,this.syncFrameworkState(o),this.scheduleState(o,!0)}inventoryOnTradingDay(e,t){if(e.tradingDay&&e.tradingDay>t)throw Error(`\u6062\u590D TradingDay \u56DE\u9000: ${e.tradingDay}/${t}`);let r={...e.inventory};if(e.tradingDay&&e.tradingDay!==t)r.longYesterday+=r.longToday,r.longToday=0,r.shortYesterday+=r.shortToday,r.shortToday=0;return r}sameInventory(e,t){return e.longToday===t.longToday&&e.longYesterday===t.longYesterday&&e.shortToday===t.shortToday&&e.shortYesterday===t.shortYesterday}handleLiveFault(e){for(let t of this.strategies.values())if(t.mode==="live"&&t.trading)this.failStrategy(t,"TD",Error(e))}applyTrade(e,t,r=!1,n=!0){let i=t.vtTradeId;if(!this.rememberTradeId(e.seenTrades,i))return;if(this.deps.recordTrade)try{this.deps.recordTrade({strategyId:e.id,name:e.name,mode:e.mode,vtSymbol:e.vtSymbol,trade:{...t},inventory:{...e.inventory},multiplier:this.deps.live?.getInstrument(e.instrumentID)?.volumeMultiple??null})}catch(u){this.writeLog(e.instance,`\u6210\u4EA4\u8BB0\u5F55\u5931\u8D25: ${u instanceof Error?u.message:String(u)}`)}if(this.deps.orderRisk.recordTrade(e.mode,t),this.applyInventory(e.inventory,t.direction,t.offset,t.volume),this.syncFrameworkState(e),e.expectedLivePos=J(e.inventory),e.mode==="live")e.expectedLiveInventory={...e.inventory};let o=(e.tradeVolumeByOrder.get(t.vtOrderId)??0)+t.volume;e.tradeVolumeByOrder.set(t.vtOrderId,o);let s=e.orders.get(t.vtOrderId);if(s)s.traded=Math.max(s.traded,Math.min(s.volume,o));let a={...t},d=s?{...s}:null,c=()=>{if(this.callStrategy(e,"onTrade",()=>(this.observe(e,{type:"trade",value:{...t}}),e.instance.onTrade({...t}))),s)this.pruneTerminalOrder(e,s);if(this.scheduleState(e,!0),e.mode==="paper"&&n)this.dispatchTrades("paper",a,d,"strategy")};if(r)this.scheduleState(e,!0),this.enqueue(c);else c()}applyInventory(e,t,r,n){if(r==="open"){if(t==="long")e.longToday+=n;else e.shortToday+=n;return}let i=t==="short"?"longToday":"shortToday",o=t==="short"?"longYesterday":"shortYesterday";if(r==="closeToday"){e[i]=Math.max(0,e[i]-n);return}if(r==="closeYesterday"){e[o]=Math.max(0,e[o]-n);return}let s=Math.min(e[o],n);e[o]-=s,e[i]=Math.max(0,e[i]-(n-s))}queueOrder(e,t){let r={...t},n=e.tradingDay;this.enqueue(()=>this.emitOrder(e,t,r,n))}emitOrder(e,t,r={...t},n=e.tradingDay){if(this.deps.orderRisk.recordOrder(e.mode,r,n),e.inited)this.callStrategy(e,"onOrder",()=>(this.observe(e,{type:"order",value:{...r,type:"order"}}),e.instance.onOrder({...r,type:"order"})));if(!w(r))this.pruneTerminalOrder(e,t)}emitOrderError(e,t,r,n,i,o,s){let a={type:"error",stage:"submit",code:t,vtSymbol:e.vtSymbol,direction:r,offset:n,price:i,volume:o,message:s,datetime:this.eventTimeMs??this.deps.now()};this.enqueue(()=>this.callStrategy(e,"onOrder",()=>(this.observe(e,{type:"order",value:{...a}}),e.instance.onOrder(a))))}pruneTerminalOrder(e,t){if(e.orders.get(t.vtOrderId)!==t)return;if(w(t))return;if((e.tradeVolumeByOrder.get(t.vtOrderId)??0)<t.traded)return;if(e.mode==="paper")this.deps.paperExecution?.onOrderClosed?.(t),this.terminalPaperOrders.add(t.vtOrderId);e.orders.delete(t.vtOrderId),e.tradeVolumeByOrder.delete(t.vtOrderId),this.orderOwners.delete(t.vtOrderId)}rememberTradeId(e,t){if(e.has(t))return!1;if(e.add(t),e.size>Vo){let r=e.values().next().value;if(r!==void 0)e.delete(r)}return!0}dispatchTimer(e){this.enqueue(()=>{for(let t of this.strategies.values())this.dispatchEvent(t,Object.freeze({type:"timer",now:e}))})}dispatchTrades(e,t,r,n,i){let o=Object.freeze({type:"trades",now:this.eventTimeMs??this.deps.now(),trade:Object.freeze({...t}),order:r?Object.freeze({...r,origin:n}):null});for(let s of this.strategies.values()){if(s.mode!==e)continue;let a=this.tradeClaimContext;this.tradeClaimContext=i?{...i,running:s,trade:o.trade}:null;try{this.dispatchEvent(s,o)}finally{this.tradeClaimContext=a}}}dispatchEvent(e,t){if(!e.inited||e.fault)return;let r=e.eventHandlers[t.type];for(let n of[...r]){if(e.fault)break;if(!r.has(n))continue;this.invokeAtTime(t.now,()=>this.callStrategy(e,`${t.type} event`,()=>n(t)))}}clearEventHandlers(e){for(let t of Object.values(e.eventHandlers))t.clear()}invokeAtTime(e,t){let r=this.eventTimeMs;this.eventTimeMs=e;try{return t()}finally{this.eventTimeMs=r}}rollPaperDay(e,t){if(!t||t===e.tradingDay)return;if(e.tradingDay){for(let r of e.orders.values()){if(!w(r))continue;r.status="cancelled",r.statusMessage="paper trading day changed",this.queueOrder(e,r)}e.inventory.longYesterday+=e.inventory.longToday,e.inventory.longToday=0,e.inventory.shortYesterday+=e.inventory.shortToday,e.inventory.shortToday=0}e.tradingDay=t,this.scheduleState(e,!0)}rollLiveDay(e,t){if(!t||t===e.tradingDay)return;let r=this.inventoryOnTradingDay(e,t);e.inventory=r,e.expectedLiveInventory={...r},e.expectedLivePos=J(r),e.tradingDay=t,this.syncFrameworkState(e)}roundPrice(e,t){let r=this.getPriceTick(e.instance);return Number((Math.round(t/r)*r).toFixed(10))}paperFill(e,t,r,n){let i=(e.direction==="long"?t.askPrice1:t.bidPrice1)??e.price,o=this.deps.paperExecution?.fillOrder?.(e,t,r,n)??{referencePrice:i,price:i};if(!Number.isFinite(o.referencePrice)||o.referencePrice<=0||!Number.isFinite(o.price)||o.price<=0)throw Error(`Paper \u6210\u4EA4\u4EF7\u975E\u6CD5: ${o.referencePrice}/${o.price}`);return o}cancelAllDirect(e){for(let t of e.orders.values()){if(!w(t))continue;if(e.mode==="paper")this.cancelOrder(e.instance,t.vtOrderId);else try{this.deps.live?.cancelOrder(t.vtOrderId)}catch(r){console.error(`[cta] ${e.id} \u64A4\u5355\u5931\u8D25: ${r instanceof Error?r.message:String(r)}`)}}}retainedOrdersMessage(e){return`\u7B56\u7565\u5DF2\u505C\u6B62\uFF0C\u4ECD\u6709 ${e} \u7B14\u59D4\u6258\u672A\u64A4\u9500\uFF0C\u53EF\u80FD\u7EE7\u7EED\u6210\u4EA4\uFF0C\u8BF7\u624B\u52A8\u64A4\u5355\u3002`}scheduleState(e,t=!1){if(t){e.stateTimer?.(),e.stateTimer=null,this.trySaveState(e);return}if(e.stateTimer)return;e.stateTimer=this.deps.schedule(()=>{e.stateTimer=null,this.trySaveState(e)},qo)}saveState(e){let t=this.serializeVariables(e);this.deps.stateStore.write({strategyId:e.id,vtSymbol:e.vtSymbol,mode:e.mode,bundleHash:e.hash,pos:J(e.inventory),variables:t,inventory:e.inventory,tradingDay:e.tradingDay}),this.observe(e,{type:"state",operation:"write",value:{pos:J(e.inventory),inventory:{...e.inventory},variables:t,tradingDay:e.tradingDay}})}serializeVariables(e){return Un(e.runtime.getStateVariables(),`\u7B56\u7565 ${e.id} variables`)}trySaveState(e){try{this.saveState(e)}catch(t){if(console.error(`[cta] ${e.id} \u72B6\u6001\u4FDD\u5B58\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`),e.trading)this.failStrategy(e,"state",t)}}callStrategy(e,t,r){if(e.fault)return;try{this.invokeStrategySync(e,t,r),this.scheduleState(e)}catch(n){this.failStrategy(e,t,n)}}failStrategy(e,t,r){let n=r instanceof Error?r.message:String(r);if(e.fault)return;e.fault=`${t}: ${n}`,this.observe(e,{type:"fault",callback:t,message:n}),e.trading=!1,e.inited=!1,this.clearEventHandlers(e),this.syncFrameworkState(e),this.cancelAllDirect(e),console.error(`[cta] ${e.id} ${t} \u5F02\u5E38\uFF0C\u7B56\u7565\u5DF2\u505C\u6B62: ${n}`),this.publishHook("strategy.faulted",{...this.strategyIdentity(e),callback:t,reason:n})}emitRiskRejection(e,t,r,n,i,o,s){let a=this.deps.orderRisk.tradingDay(e.mode)||e.tradingDay||null;this.publishHook("risk.order_rejected",{source:"strategy",riskCode:t,mode:e.mode,vtSymbol:e.vtSymbol,tradingDay:a,direction:r,offset:n,price:i,volume:o,reason:s,strategyId:e.id})}strategyIdentity(e){return{strategyId:e.id,mode:e.mode,vtSymbol:e.vtSymbol}}publishHook(e,t){try{this.deps.emitHook?.(e,t)}catch(r){console.error(`[hooks] \u4E8B\u4EF6\u6295\u9012\u5931\u8D25 event=${e}: ${r instanceof Error?r.message:String(r)}`)}}enqueue(e){let t=this.eventTimeMs;if(this.eventQueue.push(t===null?e:()=>this.invokeAtTime(t,e)),this.dispatching)return;this.dispatching=!0;try{while(this.eventQueue.length>0)this.eventQueue.shift()()}finally{this.dispatching=!1}}queuePending(e,t){for(let r of this.pending.values()){if(r.running.instrumentID!==t)continue;if(r.events.length>=Vn){r.overflowed=!0;continue}r.events.push(e)}}drainPending(e){while(e.events.length>0){let t=e.events.splice(0);for(let r of t){if(r.kind==="bar"){if(e.replayedBars.has(zn(r.value)))continue;this.callStrategy(e.running,"onBar",()=>e.running.instance.onBar(r.value))}else this.callStrategy(e.running,"onTick",()=>e.running.instance.onTick(r.value));if(e.running.fault)return}}}assertLiveInstrumentOwner(e){if(e.mode!=="live")return;let t=[...this.strategies.values()].find((r)=>B(r.id,r.mode)!==B(e.id,e.mode)&&r.mode==="live"&&r.instrumentID===e.instrumentID);if(t)throw Error(`Live \u5408\u7EA6 ${e.instrumentID} \u5DF2\u7531\u7B56\u7565 ${t.id} \u5360\u7528`)}requireInitializing(e,t){let r=this.requireInstance(e);if(this.initializing!==r)throw Error(`${t} \u53EA\u80FD\u5728 onInit() \u4E2D\u8C03\u7528`);return r}requireInstance(e){let t=this.instanceMap.get(e);if(!t)throw Error("\u672A\u77E5\u7B56\u7565\u5B9E\u4F8B");return t}findRunning(e,t){return this.strategies.get(B(e,t))??null}requireRunning(e,t){let r=this.findRunning(e,t);if(!r)throw Error(`\u7B56\u7565\u5B9E\u4F8B\u4E0D\u5B58\u5728: ${e}@${t}`);return r}findPending(e,t){return this.pending.get(B(e,t))??null}requirePending(e,t){let r=this.findPending(e,t);if(!r)throw Error(`\u7B56\u7565 ${e}@${t} \u6CA1\u6709\u5F85\u63D0\u4EA4\u53D8\u66F4`);return r}async dispose(e,t=!0){if(e.stateTimer?.(),e.stateTimer=null,t&&e.inited)this.trySaveState(e);this.instanceMap.delete(e.instance),this.clearEventHandlers(e);for(let r of e.orders.keys())this.orderOwners.delete(r)}invokeSync(e,t){let r=t();if(r&&typeof r.then==="function")throw Promise.resolve(r).catch(()=>{return}),Error(`${e} \u4E0D\u5141\u8BB8\u8FD4\u56DE Promise`)}invokeStrategySync(e,t,r){this.syncFrameworkState(e),this.observe(e,{type:"callback",callback:t,stage:"begin"});try{this.invokeSync(t,r)}finally{this.syncFrameworkState(e),this.observe(e,{type:"callback",callback:t,stage:"end"})}}observe(e,t){this.deps.observe({...t,strategyId:e.id,now:this.eventTimeMs??this.deps.now()})}syncFrameworkState(e){e.runtime.setRuntimeState({inited:e.inited,trading:e.trading,pos:J(e.inventory)})}}var jn=20,Yo=1000,Q="vibek-test",Wo={maxOrderVolume:1000,maxPosition:1e4,maxActiveOpenOrders:1000},Gn={dailyLimits:{totalOrderLimit:1e5,totalCancelLimit:1e5,totalTradeLimit:1e5,instrumentOrderLimit:1e5,instrumentCancelLimit:1e5,instrumentTradeLimit:1e5},duplicateOrderLimit:1e5};class ie{definition;fixture;events=[];orderEvents=[];tradeEvents=[];logs=[];hooks=[];submissions=[];cancelRequests=[];broker;callbackCounts={onInit:0,onStart:0,onStop:0,onTick:0,onBar:0};dataDir;stateStore;clock;traceRecorder;faults=[];aggregator=new Ve({timeframes:["1min"]});barHistory=[];warmupSupplied=!1;paperSessionSequence=0;mode;setting;initialState;brokerState;executionLimits;riskSettings;paperExecution;runtime;running;live;gatewayControl;eventCounter=0;nextEventTimeMs;initialized=!1;initialStateWritten=!1;disposed=!1;firstSdkOrderViolation;constructor(e,t,r={}){this.definition=e;this.fixture=t;if(this.mode=r.mode??"live",r.execution!==void 0&&r.execution!=="scripted"&&r.execution!=="ctp")throw Error("Unknown test execution model");if(r.execution==="ctp"&&this.mode!=="live")throw Error("CTP execution requires live mode");if(r.execution==="ctp"&&r.brokerState)throw Error("CTP execution uses gatewayState");if(r.brokerState&&this.mode!=="live")throw Error("Broker state requires live mode");if(r.paperExecution&&this.mode!=="paper")throw Error("Paper execution hooks require paper mode");if(r.execution!=="ctp"&&(r.gatewayState||r.gatewaySession))throw Error("Gateway settings require CTP execution");this.setting=r.setting===void 0?ce(t.setting,"setting"):ce(r.setting,"setting"),this.initialState=r.initialState,this.brokerState=r.brokerState===void 0?void 0:structuredClone(r.brokerState),this.executionLimits=Jo(r.executionLimits),this.riskSettings=Qo(r.riskLimits),this.paperExecution=r.paperExecution,this.clock=new Ht(Ke(t)),this.traceRecorder=new Lt(()=>this.clock.now);let{paperExecution:n,...i}=r;if(this.traceRecorder.record("context",{fixture:{vtSymbol:t.vtSymbol,instrument:{priceTick:t.priceTick,size:t.size,name:t.name,minOrderVolume:t.minOrderVolume,maxOrderVolume:t.maxOrderVolume,longMarginRatio:t.longMarginRatio,shortMarginRatio:t.shortMarginRatio,isTrading:t.isTrading},...t.commission?{commission:t.commission}:{},setting:t.setting,seedPrice:t.seedPrice,tradingDay:t.tradingDay},options:i,...n?{customPaperExecution:!0}:{}}),r.execution==="ctp")this.live=new _e(t,this.clock,this.traceRecorder,r,{submissions:this.submissions,cancelRequests:this.cancelRequests});this.nextEventTimeMs=this.clock.now;let o={holdReports:()=>this.requireLive().holdReports(),resumeReports:()=>this.requireLive().resumeReports(),pendingReports:()=>this.requireLive().pendingReports(),deliverReport:(s)=>this.requireLive().deliverReport(s),repeatReport:(s)=>this.requireLive().repeatReport(s),snapshot:()=>this.requireLive().snapshot(),failNextSubmit:(s)=>this.requireLive().failNextSubmit(s),rejectSubmit:(s,a)=>this.requireLive().rejectSubmit(s,a),acknowledge:(s)=>this.requireLive().acknowledge(s),partialFill:(s,a,d)=>this.requireLive().partialFill(s,a,d),fill:(s,a)=>this.requireLive().fill(s,a),reject:(s,a)=>this.requireLive().reject(s,a),cancel:(s,a)=>this.requireLive().cancel(s,a),failNextCancel:(s)=>this.requireLive().failNextCancel(s),rejectCancel:(s,a)=>this.requireLive().rejectCancel(s,a),emitExternalOrder:(s)=>this.requireLive().emitExternalOrder(s),emitOrder:(s)=>this.requireLive().emitOrder(s),emitTrade:(s)=>this.requireLive().emitTrade(s),disconnect:()=>this.requireLive().disconnect(),reconcile:(s)=>this.requireLive().reconcile(s),fault:(s)=>this.requireLive().fault(s)};this.broker=this.observeCommands(o,"broker",["snapshot","pendingReports"]),this.dataDir=Uo(Ko(Go(),"vibek-strategy-test-runtime-")),this.stateStore=new $e(this.dataDir)}initialize(e={}){return this.traceRecorder.input({type:"initialize",...e},()=>this.initializeRuntime(e))}async initializeRuntime(e){if(this.disposed)throw Error("\u6D4B\u8BD5 Runtime \u5DF2\u91CA\u653E");if(this.initialized)throw Error("\u6D4B\u8BD5 Runtime \u5DF2\u521D\u59CB\u5316");if(e.warmupBars){let t=e.warmupBars.map((r,n)=>H(this.fixture,r,n));Ie(t,this.clock.now,this.fixture.instrumentID),this.barHistory.push(...t),this.warmupSupplied=!0}this.seedInitialState(),await this.createRuntime(),await this.runtime.warmup(),this.runtime.setTimerClock(this.clock.now),this.initialized=!0}async start(){this.requireInitialized(),await this.traceRecorder.input({type:"start"},()=>this.runtime.start(Q,this.mode))}async stop(){this.requireInitialized(),await this.traceRecorder.input({type:"stop"},()=>this.runtime.stop(Q,{},this.mode))}cancelOrder(e){this.requireInitialized(),this.traceRecorder.input({type:"cancelOrder",vtOrderId:e},()=>{this.runtime.cancelStrategyOrder(Q,e,this.mode)})}async restart(){this.requireInitialized(),await this.traceRecorder.input({type:"restart"},async()=>{await this.runtime.stop(Q,{},this.mode),await this.rebuildRuntime()})}async crashAndRestart(){this.requireInitialized(),await this.traceRecorder.input({type:"crashAndRestart"},()=>this.rebuildRuntime(!1))}setTime(e){if(this.disposed)throw Error("\u6D4B\u8BD5 Runtime \u5DF2\u91CA\u653E");let t=T(e,"\u6D4B\u8BD5\u65F6\u95F4");this.traceRecorder.input({type:"setTime",datetime:t},()=>{if(this.initialized)this.advanceClock(t);else this.clock.now=t}),this.nextEventTimeMs=t}advanceTime(e){if(this.disposed)throw Error("\u6D4B\u8BD5 Runtime \u5DF2\u91CA\u653E");let t=T(e,"\u63A8\u8FDB\u65F6\u95F4"),r=this.clock.now+t;if(!Number.isSafeInteger(r))throw Error("\u63A8\u8FDB\u540E\u7684\u6D4B\u8BD5\u65F6\u95F4\u8D85\u8FC7\u5B89\u5168\u6574\u6570\u8303\u56F4");this.traceRecorder.input({type:"advanceTime",milliseconds:e},()=>this.advanceClock(r)),this.nextEventTimeMs=r}pushBar(e,t={}){this.requireInitialized();let r=t.datetime??(e.barTime===void 0?Math.max(this.nextEventTimeMs,((this.barHistory.at(-1)?.barTime??-120)+120)*1000):(e.barTime+60)*1000),n=H(this.fixture,e,this.eventCounter,Math.floor(r/60000)*60-60);this.validateClosedBars([n],r),this.traceRecorder.input({type:"bar",value:n,datetime:r},()=>{this.recordEventTime(r),this.eventCounter++,this.aggregator.completeThrough(r),this.dispatchBars(this.mergeClosedBars(this.takeCompletedBars(),[n]))})}pushTick(e){this.pushMarketEvent(e,[])}pushMarketEvent(e,t){this.requireInitialized();let r=De(this.fixture,e,this.eventCounter,this.nextEventTimeMs);if(r.code!==this.fixture.instrumentID)throw Error("\u6D4B\u8BD5 Tick \u5408\u7EA6\u4E0E fixture \u4E0D\u5339\u914D");let n=t.map((o,s)=>H(this.fixture,o,s)),i=r.receivedAt??r.datetime;if(r.datetime>i||i-r.datetime>dt)throw Error("\u6D4B\u8BD5 Tick \u4EA4\u4ED8\u65F6\u95F4\u5FC5\u987B\u4F4D\u4E8E\u884C\u60C5\u65F6\u95F4\u4E4B\u540E\u7684 30 \u79D2\u4EE5\u5185");this.validateClosedBars(n,i),this.traceRecorder.input({type:"market",tick:r,closedBars:n},()=>{this.recordEventTime(i),this.eventCounter++,this.aggregator.pushTick(r);let o=this.mergeClosedBars(this.takeCompletedBars(),n);this.rememberBars(o);for(let s of o)this.traceRecorder.record("bar",s);this.runtime.handleMarketEvent(r,o)})}activeOrders(){if(this.requireInitialized(),!this.running)throw Error("\u6D4B\u8BD5 Runtime \u7684\u7B56\u7565\u5B9E\u4F8B\u4E0D\u5B58\u5728");return[...this.running.orders.values()].filter(w).map((e)=>({...e}))}snapshot(){this.requireInitialized();let e=this.runtime.snapshot().find((t)=>t.id===Q);if(!e||!this.running)throw Error("\u6D4B\u8BD5 Runtime \u7684\u7B56\u7565\u5B9E\u4F8B\u4E0D\u5B58\u5728");return{inited:e.inited,trading:e.trading,pos:e.pos,activeOrders:e.activeOrders,fault:e.fault,variables:ce(this.running.runtime.getStateVariables(),"variables"),inventory:{...this.running.inventory},now:this.clock.now}}get trace(){return this.traceRecorder.events}get gateway(){if(this.disposed)throw Error("\u6D4B\u8BD5 Runtime \u5DF2\u91CA\u653E");if(!(this.live instanceof _e))throw Error('Gateway controls require execution="ctp"');return this.gatewayControl??=this.observeCommands(this.live.gateway,"gateway",["pendingRequests"])}expectFault(e){let t=this.faults.find((r)=>!r.verified&&r.message===e);if(!t)throw Error(`\u672A\u53D1\u751F\u5F85\u9A8C\u8BC1\u7684 fault: ${e}`);t.verified=!0,this.traceRecorder.record("input",{type:"expectFault",message:e})}settleActiveOrders(){if(this.mode==="live"){if(!(this.live instanceof Oe))throw Error("CTP orders require explicit gateway reports");this.live.settleActiveOrders()}else for(let e of this.activeOrders())this.cancelOrder(e.vtOrderId)}assertHealthy(e){let t=this.snapshot();if(t.fault)throw Error(`${e}\u540E\u7B56\u7565 fault: ${t.fault}`);if(this.firstSdkOrderViolation)throw Error(`${e}\u89E6\u53D1 SDK \u975E\u6CD5\u4E0B\u5355: ${this.firstSdkOrderViolation}`)}async dispose(){if(!this.disposed){this.disposed=!0;try{await this.runtime?.close()}finally{if(this.live instanceof _e)this.live.close();this.traceRecorder.close(),jo(this.dataDir,{recursive:!0,force:!0})}}let e=this.faults.filter((t)=>!t.verified);if(e.length)throw Error(`\u672A\u9A8C\u8BC1\u7684\u7B56\u7565 fault: ${e.map((t)=>t.message).join("; ")}`)}async createRuntime(){let e=this.stateStore.read(Q,this.mode),t=e?.inventory??de(),r={...this.fixture,tradingDay:e?.tradingDay||this.fixture.tradingDay};this.live??=new Oe(r,t,{order:0,trade:0},()=>this.clock.now,{submissions:this.submissions,cancelRequests:this.cancelRequests,record:(i,o)=>this.traceRecorder.record(i,o)},this.brokerState);let n=new He({settings:this.riskSettings,stateStore:new dr(this.dataDir),schedule:(i,o)=>this.clock.schedule(this.traceRecorder.bind(i),o)});n.setTradingDay("paper",r.tradingDay),this.runtime=new mr({createWarmupProvider:()=>new Bt(r,this.barHistory,this.clock.now,!this.warmupSupplied),stateStore:this.stateStore,live:this.live,paperExecution:{...this.paperExecution,onOrderSubmitted:(i)=>{let o={vtOrderId:i.vtOrderId,direction:i.direction,offset:i.offset,price:i.price,volume:i.volume};this.submissions.push(o),this.traceRecorder.record("submission",o)},onCancelRequested:(i)=>{this.cancelRequests.push(i.vtOrderId),this.traceRecorder.record("cancel",{vtOrderId:i.vtOrderId})}},orderRisk:n,executionLimits:this.executionLimits,log:(i)=>this.recordLog(i),now:()=>this.clock.now,schedule:(i,o)=>this.clock.schedule(this.traceRecorder.bind(i),o),createPaperSessionId:()=>String(++this.paperSessionSequence),observe:(i)=>this.observe(i),emitHook:(i,o,s)=>{if(i!=="strategy.event")return;let a=structuredClone({...o,occurredAt:s??this.clock.now});this.hooks.push(a),this.events.push({type:"hook",value:a,now:a.occurredAt}),this.traceRecorder.record("hook",a)}}),await this.live.connect(),this.running=this.runtime.addRestored({sourcePath:"strategy.ts",hash:"a".repeat(64),artifact:"strategy-test",loadedAt:new Date(0).toISOString(),mode:this.mode,definition:this.definition,instance:{id:Q,vtSymbol:this.fixture.vtSymbol,setting:this.setting}})}observe(e){if(this.traceRecorder.record(e.type,e),e.type==="callback"&&e.stage==="begin"&&Object.hasOwn(this.callbackCounts,e.callback))this.callbackCounts[e.callback]++;else if(e.type==="order"){let t=Xo(e.value);if(t.type==="error"&&(t.code==="invalidRequest"||t.code==="insufficientPosition"))this.firstSdkOrderViolation??=`${t.code}: ${t.message}`;this.orderEvents.push(t),this.events.push({type:"order",value:t,now:e.now})}else if(e.type==="trade")this.tradeEvents.push({...e.value}),this.events.push({type:"trade",value:{...e.value},now:e.now});else if(e.type==="fault")this.faults.push({message:`${e.callback}: ${e.message}`,verified:!1})}recordLog(e){let t=Zo(this.logs,e);this.traceRecorder.record("log",{message:t})}async rebuildRuntime(e=!0){await this.runtime.close({persist:e}),this.runtime=void 0,this.running=void 0,this.aggregator.reset(),this.initialized=!1,await this.createRuntime(),await this.runtime.warmup(),this.runtime.setTimerClock(this.clock.now),this.initialized=!0}recordEventTime(e){let t=e+60000;if(!Number.isSafeInteger(t))throw Error("\u884C\u60C5\u65F6\u95F4\u8D85\u8FC7\u5B89\u5168\u6574\u6570\u8303\u56F4");if(e<this.clock.now)throw Error("\u884C\u60C5\u65F6\u95F4\u4E0D\u80FD\u56DE\u9000");this.advanceClock(e),this.nextEventTimeMs=t}advanceClock(e){this.clock.advanceTo(e,this.initialized?()=>{this.traceRecorder.record("timer",{now:this.clock.now}),this.runtime.handleTimer(this.clock.now),this.aggregator.completeThrough(this.clock.now-dt),this.dispatchBars(this.takeCompletedBars())}:void 0)}takeCompletedBars(){let e=[...this.aggregator.getCompletedBars()];return this.aggregator.clearCompletedBars(),e.filter((t)=>t.barTime>(this.barHistory.at(-1)?.barTime??-1))}validateClosedBars(e,t){if(Ie(e,t,this.fixture.instrumentID),e.some((n)=>n.barTime<=(this.barHistory.at(-1)?.barTime??-1)))throw Error("\u95ED\u5408 Bar \u65F6\u95F4\u5FC5\u987B\u665A\u4E8E\u5DF2\u56DE\u653E\u5386\u53F2");let r=this.aggregator.getCurrentBar("1min");if(r&&t>=(r.barTime+60)*1000+dt&&e.some((n)=>n.barTime===r.barTime))throw Error("\u663E\u5F0F Bar \u7684\u4EA4\u4ED8\u665A\u4E8E\u81EA\u52A8\u95ED\u5408\u671F\u9650\uFF0C\u4E0D\u80FD\u66FF\u6362\u5DF2\u5230\u671F\u7684 Bar")}mergeClosedBars(e,t){let r=new Map(e.map((n)=>[n.barTime,n]));for(let n of t)r.set(n.barTime,n);return[...r.values()].sort((n,i)=>n.barTime-i.barTime)}rememberBars(e){this.barHistory.push(...e.map((t)=>({...t})))}dispatchBars(e){this.rememberBars(e);for(let t of e)this.traceRecorder.record("bar",t),this.runtime.handleBar(t)}seedInitialState(){if(this.initialStateWritten||!this.initialState)return;let e=We(this.initialState.inventory),t=this.initialState.variables===void 0?{}:ce(this.initialState.variables,"initialState.variables");this.stateStore.write({strategyId:Q,vtSymbol:this.fixture.vtSymbol,mode:this.mode,bundleHash:"a".repeat(64),pos:Mr(e),variables:t,inventory:e,tradingDay:this.fixture.tradingDay}),this.initialStateWritten=!0}requireInitialized(){if(this.disposed)throw Error("\u6D4B\u8BD5 Runtime \u5DF2\u91CA\u653E");if(!this.initialized||!this.runtime)throw Error("\u6D4B\u8BD5 Runtime \u5C1A\u672A initialize()")}requireLive(){if(this.requireInitialized(),this.mode!=="live"||!(this.live instanceof Oe))throw Error('broker \u63A7\u5236\u53EA\u9002\u7528\u4E8E mode="live"\u3001execution="scripted" \u7684\u6D4B\u8BD5 Runtime');return this.live}observeCommands(e,t,r){return new Proxy(e,{get:(n,i)=>{let o=Reflect.get(n,i);if(typeof o!=="function"||!Object.hasOwn(n,i)||r.includes(String(i)))return o;return(...s)=>this.traceRecorder.input({type:t,method:i,args:s},()=>Reflect.apply(o,n,s))}})}}function Jo(e){let t={...Wo,...e};return{maxOrderVolume:_(t.maxOrderVolume,"executionLimits.maxOrderVolume"),maxPosition:_(t.maxPosition,"executionLimits.maxPosition"),maxActiveOpenOrders:_(t.maxActiveOpenOrders,"executionLimits.maxActiveOpenOrders")}}function Qo(e){let t={...Gn.dailyLimits,...e?.dailyLimits};for(let r of Object.keys(t))_(t[r],`riskLimits.dailyLimits.${r}`);return{dailyLimits:t,duplicateOrderLimit:_(e?.duplicateOrderLimit??Gn.duplicateOrderLimit,"riskLimits.duplicateOrderLimit")}}function Zo(e,t){let r=es(ts(t),Yo);if(e.push(r),e.length>jn)e.splice(0,e.length-jn);return r}function Xo(e){return{...e}}function es(e,t){let r=Buffer.from(e);if(r.length<=t)return e;return`${r.subarray(0,t).toString("utf8")}...`}function ts(e){return e.replace(/\bsk-[A-Za-z0-9_-]{6,}\b/g,"[redacted]").replace(/((?:api[_ -]?key|authorization|access[_ -]?token)\s*[:=]\s*)\S+/gi,"$1[redacted]")}async function pr(e,t,r){await e.initialize(),await e.start();for(let n of[0,1,-1,2,-2,0]){if(e.pushTick({lastPrice:Math.max(t.priceTick,t.seedPrice+n*t.priceTick),askVolume1:1,bidVolume1:1}),e.advanceTime(1500),r==="live")for(let i of e.activeOrders()){if(i.status==="submitting")e.broker.acknowledge(i.vtOrderId);if(!e.activeOrders().some((o)=>o.vtOrderId===i.vtOrderId))continue;if(i.volume-i.traded>1)e.broker.partialFill(i.vtOrderId,1);else e.broker.fill(i.vtOrderId)}e.advanceTime(60000),e.assertHealthy("\u786E\u5B9A\u6027\u56DE\u653E")}return await e.stop(),e.settleActiveOrders(),await e.restart(),await e.start(),e.pushTick({lastPrice:t.seedPrice}),e.advanceTime(1000),await e.stop(),e.settleActiveOrders(),e.assertHealthy("\u786E\u5B9A\u6027\u91CD\u542F"),{trace:e.trace,submissions:e.submissions,cancelRequests:e.cancelRequests,events:e.events,orderEvents:e.orderEvents,tradeEvents:e.tradeEvents,hooks:e.hooks,logs:e.logs,snapshot:e.snapshot()}}import{isDeepStrictEqual as Yn,inspect as Wn}from"util";class ft extends Error{step;scenario;trace;constructor(e,t,r,n){super(`Strategy scenario failed at step ${e}${t.seed===void 0?"":` (seed ${t.seed})`}: ${n instanceof Error?n.message:String(n)}
|
|
72
|
+
Replay: ${Jn(t)}`,{cause:n});this.step=e;this.scenario=t;this.trace=r;this.name="StrategyScenarioError"}}async function gr(e,t,r={}){if(yt(t),t.context?.customPaperExecution&&!r.paperExecution)throw Error("Custom Paper hooks must be supplied to replay this scenario");let n=new ie(e.strategy,Y(t.context?.fixture??e.fixture),{...t.context?.options,...r}),i={...t,context:n.trace[0].value},o=0,s=new Map;try{for(;o<t.steps.length;o++){let d=t.steps[o];if(d.at!==void 0)n.setTime(d.at);if(d.action.type==="await"){let c=s.get(d.action.task);if(!c)throw Error(`Scenario task does not exist: ${d.action.task}`);let u=await c;if(s.delete(d.action.task),d.action.error===void 0){if("error"in u)throw u.error}else if(!("error"in u)||(u.error instanceof Error?u.error.message:String(u.error))!==d.action.error)throw Error(`Scenario task did not reject with: ${d.action.error}`)}else if(d.task)s.set(d.task,Kn(n,d.action).then(()=>({}),(c)=>({error:c})));else await Kn(n,d.action)}let a={...t.seed===void 0?{}:{seed:t.seed},trace:n.trace,snapshot:n.snapshot(),submissions:structuredClone(n.submissions),cancelRequests:[...n.cancelRequests]};return await n.dispose(),a}catch(a){try{await n.dispose()}catch(d){if(d!==a)a=AggregateError([a,d],String(a))}throw new ft(o,i,n.trace,a)}}async function rs(e,t,r={}){let n=await gr(e,t,r),i=await gr(e,t,r);try{if(fr(n.trace,i.trace),!Yn(n.snapshot,i.snapshot))throw Error(`Strategy replay snapshot differs:
|
|
51
73
|
${Wn({expected:n.snapshot,actual:i.snapshot},{depth:8})}`)}catch(o){let s={...t,context:i.trace[0].value};throw new ft(t.steps.length,s,i.trace,o)}return n}function fr(e,t){for(let r=0;r<Math.max(e.length,t.length);r++)if(!Yn(e[r],t[r]))throw Error(`Strategy replay first differs at trace[${r}] (sequence ${r+1}):
|
|
52
|
-
${Wn({expected:e[r],actual:t[r]},{depth:8})}`)}function
|
|
74
|
+
${Wn({expected:e[r],actual:t[r]},{depth:8})}`)}function ns(e,t){let r=e.trace,n=new Map;for(let s of r)if(s.type==="completion"){let a=s.value;n.set(a.input,a)}let i=[];for(let s of r){if(s.type==="input")i.push({...n.has(s.sequence)?{task:`input-${s.sequence}`}:{},action:structuredClone(s.value)});if(s.type==="completion"){let a=s.value;i.push({action:{type:"await",task:`input-${a.input}`,...a.error===void 0?{}:{error:a.error}}})}}let o={...t===void 0?{}:{seed:t},context:structuredClone(r.find((s)=>s.type==="context")?.value),steps:i};return yt(o),o}function Jn(e){return yt(e),JSON.stringify(hr(e))}function is(e){let t=ht(JSON.parse(e));return yt(t),t}async function Kn(e,t){switch(t.type){case"initialize":await e.initialize(Object.hasOwn(t,"warmupBars")?{warmupBars:t.warmupBars}:{});return;case"start":await e.start();return;case"stop":await e.stop();return;case"restart":await e.restart();return;case"crashAndRestart":await e.crashAndRestart();return;case"setTime":e.setTime(t.datetime);return;case"advanceTime":e.advanceTime(t.milliseconds);return;case"market":e.pushMarketEvent(t.tick,t.closedBars??[]);return;case"bar":e.pushBar(t.value,{datetime:t.datetime});return;case"cancelOrder":e.cancelOrder(Le(t.vtOrderId,e));return;case"expectFault":e.expectFault(t.message);return;case"broker":{let r=e.broker[t.method];if(!Object.hasOwn(e.broker,t.method)||typeof r!=="function"||t.method==="snapshot"||t.method==="pendingReports")throw Error("Invalid broker action");await Reflect.apply(r,e.broker,Le(t.args,e));return}case"gateway":{let r=e.gateway[t.method];if(!Object.hasOwn(e.gateway,t.method)||typeof r!=="function"||t.method==="pendingRequests")throw Error("Invalid gateway action");await Reflect.apply(r,e.gateway,Le(t.args,e));return}default:throw Error(`Unknown scenario action: ${t.type}`)}}function Le(e,t){if(Array.isArray(e))return e.map((r)=>Le(r,t));if(e&&typeof e==="object"){let r=e;if(Object.keys(r).length===1&&Object.hasOwn(r,"submission")){let n=r.submission;if(!Number.isSafeInteger(n)||n<0)throw Error("Invalid submission reference");let i=t.submissions[n];if(!i)throw Error(`Submission reference does not exist: ${n}`);return i.vtOrderId}if(Object.keys(r).length===1&&typeof r.request==="string"){let n=t.gateway.pendingRequests().find((i)=>i.method===r.request);if(!n)throw Error(`Pending request reference does not exist: ${r.request}`);return n.id}return Object.fromEntries(Object.entries(r).map(([n,i])=>[n,Le(i,t)]))}return e}function yt(e){if(!e||typeof e!=="object"||!Array.isArray(e.steps))throw Error("Scenario steps must be an array");if(e.seed!==void 0&&!Number.isSafeInteger(e.seed))throw Error("Scenario seed must be a safe integer");if(e.context!==void 0){if(!e.context||typeof e.context!=="object")throw Error("Invalid scenario context");if(Y(e.context.fixture),!e.context.options||typeof e.context.options!=="object"||Array.isArray(e.context.options)||Object.hasOwn(e.context.options,"paperExecution"))throw Error("Invalid portable scenario options");if(e.context.customPaperExecution!==void 0&&e.context.customPaperExecution!==!0)throw Error("Invalid custom Paper hook marker")}let t=-1,r=new Set,n=new Set;for(let i of e.steps){if(!i||!i.action||typeof i.action.type!=="string")throw Error("Invalid scenario step");if(i.task!==void 0){if(!i.task||r.has(i.task)||i.action.type==="await")throw Error("Scenario task names must be unique");r.add(i.task)}if(i.action.type==="await"){if(!r.has(i.action.task)||n.has(i.action.task))throw Error(`Unknown or already joined task: ${i.action.task}`);n.add(i.action.task)}if(i.at!==void 0){if(!Number.isSafeInteger(i.at)||i.at<t||i.at<0)throw Error("Scenario times must be nonnegative and ordered");t=i.at}}if(r.size!==n.size)throw Error("Every scenario task must have an await step")}var G="$vibekTesting";function hr(e,t=new Set){if(e===void 0)return{[G]:"undefined"};if(Object.is(e,-0))return{[G]:"number",value:"-0"};if(typeof e==="number"&&!Number.isFinite(e))return{[G]:"number",value:String(e)};if(e===null||typeof e==="string"||typeof e==="boolean"||typeof e==="number")return e;if(typeof e!=="object")throw Error(`Scenario cannot serialize ${typeof e}`);if(t.has(e))throw Error("Scenario contains a cycle");t.add(e);try{if(Array.isArray(e))return e.map((n)=>hr(n,t));if(Object.getPrototypeOf(e)!==Object.prototype&&Object.getPrototypeOf(e)!==null)throw Error("Scenario values must be plain objects");let r=Object.entries(e).map(([n,i])=>[n,hr(i,t)]);return Object.hasOwn(e,G)?{[G]:"object",entries:r}:Object.fromEntries(r)}finally{t.delete(e)}}function ht(e){if(!e||typeof e!=="object")return e;if(Array.isArray(e))return e.map(ht);let t=e;if(!Object.hasOwn(t,G))return Object.fromEntries(Object.entries(t).map(([r,n])=>[r,ht(n)]));if(t[G]==="undefined")return;if(t[G]==="number"&&["NaN","Infinity","-Infinity","-0"].includes(String(t.value)))return Number(t.value);if(t[G]==="object"&&Array.isArray(t.entries))return Object.fromEntries(t.entries.map((r)=>{if(!Array.isArray(r)||r.length!==2||typeof r[0]!=="string")throw Error("Invalid scenario object entry");return[r[0],ht(r[1])]}));throw Error("Invalid encoded scenario value")}var ye={definition:"SDK-001 strategy definition",paper:"SDK-101 paper lifecycle",live:"SDK-102 fake-live lifecycle",persistence:"SDK-103 state persistence and restart",replay:"SDK-104 deterministic replay"};function os(e){let{afterEach:t}=wt("bun:test"),r=Y(e.fixture),n=new Set,i=0,o=(s={})=>{let a=new ie(e.strategy,r,s);return n.add(a),a};return t(async()=>{let s=[...n];n.clear(),i=0;let d=(await Promise.allSettled(s.map((c)=>c.dispose()))).flatMap((c)=>c.status==="rejected"?[c.reason]:[]);if(d.length)throw AggregateError(d,"Strategy test cleanup failed")}),ss(e.strategy,r,o),{createRuntime:o,bar:(s)=>H(r,s,i++),tick:(s)=>De(r,s,i++)}}function ss(e,t,r){let{describe:n,expect:i,test:o}=wt("bun:test");n("SDK contract",()=>{o(ye.definition,()=>{let s=Se(e,"inspect",t.setting);i(s.ctor).toBe(e.strategy)}),o(ye.paper,async()=>{await Qn(r({mode:"paper"}),t.seedPrice)}),o(ye.live,async()=>{await Qn(r({mode:"live"}),t.seedPrice)}),o(ye.persistence,async()=>{let s=r({mode:"paper"});await s.initialize(),await s.start(),s.pushTick({lastPrice:t.seedPrice}),s.pushBar({close:t.seedPrice}),await s.stop(),s.settleActiveOrders();let a=s.snapshot();await s.restart();let d=s.snapshot();i(d.pos).toBe(a.pos),i(d.variables).toEqual(a.variables),s.assertHealthy("\u91CD\u542F")}),o(ye.replay,async()=>{for(let s of["paper","live"]){let a=await pr(r({mode:s}),t,s),d=await pr(r({mode:s}),t,s);fr(a.trace,d.trace),i(a).toEqual(d)}})})}async function Qn(e,t){let{expect:r}=wt("bun:test");await e.initialize(),r(e.callbackCounts.onInit).toBe(1),e.assertHealthy("\u521D\u59CB\u5316"),await e.start(),r(e.callbackCounts.onStart).toBe(1);let n={...e.callbackCounts};e.pushTick({lastPrice:t}),e.pushBar({close:t}),r(e.callbackCounts.onTick-n.onTick).toBe(1),r(e.callbackCounts.onBar-n.onBar).toBe(1),e.assertHealthy("\u884C\u60C5\u56DE\u8C03"),e.settleActiveOrders(),await e.stop(),r(e.callbackCounts.onStop).toBe(1),e.settleActiveOrders(),r(e.snapshot()).toMatchObject({trading:!1,activeOrders:0,fault:null})}function Pu(e,t={}){return new ie(e.strategy,Y(e.fixture),t)}export{ft as StrategyScenarioError,rs as assertStrategyReplay,ns as captureStrategyScenario,Pu as createStrategyTest,is as parseStrategyScenario,gr as runStrategyScenario,Jn as serializeStrategyScenario,os as setupStrategyTests};
|