@oracle-agent/oracle 0.21.0 → 0.22.2
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 +6 -5
- package/SETUP.md +11 -5
- package/dist/assets/skills/xurl.md +40 -0
- package/dist/bin/desk-server.mjs +19 -18
- package/dist/bin/oracle-data-mcp.mjs +7 -7
- package/dist/bin/oracle-gateway.mjs +17 -17
- package/dist/bin/oracle.mjs +7 -6
- package/dist/cli/commands/auth.mjs +14 -14
- package/dist/cli/commands/browser-use-runner.sh +13 -0
- package/dist/cli/commands/chain.mjs +1 -1
- package/dist/cli/commands/chat.mjs +157 -46
- package/dist/cli/commands/doctor.mjs +1 -1
- package/dist/cli/commands/harness.mjs +7 -7
- package/dist/cli/commands/init.mjs +3 -3
- package/dist/cli/commands/mcp.mjs +33 -33
- package/dist/cli/commands/model.mjs +162 -51
- package/dist/cli/commands/setup.mjs +47 -30
- package/dist/cli/commands/sign.mjs +2 -34
- package/dist/cli/commands/strategy.mjs +5 -5
- package/dist/data/desk-data.mjs +15 -14
- package/dist/gateway/telegram-exact-action-callbacks.mjs +1 -0
- package/dist/index.mjs +31 -18
- package/dist/router/prepare-bridge.mjs +2 -0
- package/dist/strategy/index.mjs +3 -3
- package/package.json +6 -2
- package/public/oracle-splash/downloads/index.html +4 -4
- package/public/oracle-splash/index.html +1 -1
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var je=Object.defineProperty;var S=(e,t,r)=>()=>{if(r)throw r[0];try{return e&&(t=e(e=0)),t}catch(n){throw r=[n],n}};var re=(e,t)=>{for(var r in t)je(e,r,{get:t[r],enumerable:!0})};import{createHash as ze}from"node:crypto";function Ye(e){let t=String(e||"").toLowerCase();return le.includes(t)||/(^|-)(api[-_]?key|key|token|secret|auth|authorization|session|cookie|signature|access)(-|$)/.test(t)}function Qe(e){let t={};for(let[r,n]of Object.entries(e||{}))Ye(r)||(t[r]=n);return t}function We(e,t){if(!e)throw new Error(`HTTP redirect from ${t} missing Location`);let r=new URL(String(e),String(t));if(r.protocol!=="http:"&&r.protocol!=="https:")throw new Error(`HTTP redirect from ${t} used unsupported protocol ${r.protocol}`);return r.toString()}function Je(e,t){return new URL(String(e)).origin===new URL(String(t)).origin}function Ze(e,t,r){let n={};for(let[s,l]of Object.entries(r||{}))n[s.toLowerCase()]=l;let o=Object.keys(n).filter(s=>/(^|-)(api[-_]?key|key|token|secret|auth|authorization|session|cookie|signature|access)(-|$)/.test(s)),i=[...new Set([...le,...o])].sort().map(s=>`${s}=${n[s]??""}`).join(`
|
|
2
|
+
`),c=ze("sha256").update(i).digest("hex").slice(0,32);return`${e} ${t} ${c}`}function et(e,t){let r=Number(t);return Number.isFinite(r)&&r>0?Math.min(r*1e3,ce):Math.min(250*2**e,ce)+Math.floor(Math.random()*100)}async function rt(e,{fetchImpl:t,method:r,headers:n,body:o,timeoutMs:a}){let i=new AbortController,c=setTimeout(()=>i.abort(),a);try{let s=String(e),l={...n||{}};for(let d=0;d<=ue;d+=1){let u=await t(s,{method:r,headers:l,body:o,signal:i.signal,redirect:"manual"}),p=await u.text(),m=null;if(p)try{m=JSON.parse(p)}catch{m=null}if(Xe.has(u.status)){if(r!=="GET"&&r!=="HEAD"){let b=new Error(`HTTP ${u.status} ${r} ${s} redirected non-idempotent request`);throw b.status=u.status,b.body=m??p.slice(0,300),b}if(d===ue)throw new Error(`HTTP redirect loop for ${e}`);let w=We(u.headers?.get?.("location"),s);Je(w,s)||(l=Qe(l)),s=w;continue}if(!u.ok){let w=new Error(`HTTP ${u.status} ${r} ${s}`);throw w.status=u.status,w.retryAfter=u.headers?.get?.("retry-after")??null,w.body=m??p.slice(0,300),w}return m??p}throw new Error(`HTTP redirect loop for ${e}`)}finally{clearTimeout(c)}}async function E(e,t={}){let r=t.fetchImpl||globalThis.fetch;if(typeof r!="function")throw new Error("fetch unavailable \u2014 pass opts.fetchImpl");let n=t.timeoutMs??12e3,o=(t.method||"GET").toUpperCase(),a={Accept:"application/json",...t.headers||{}},i=t.body;i!=null&&typeof i!="string"&&(a["Content-Type"]=a["Content-Type"]||"application/json",i=JSON.stringify(i));let c=Number.isInteger(t.retries)?Math.max(t.retries,0):Ke,s=async()=>{let m;for(let w=0;w<=c;w++)try{return await rt(e,{fetchImpl:r,method:o,headers:a,body:i,timeoutMs:n})}catch(b){m=b;let f=b?.status,h=b?.name==="AbortError",g=new Set([408,425,429]),y=f!=null?g.has(f)||Ge.has(f)&&se.has(o):!h&&se.has(o);if(w===c||!y)throw b;await tt(et(w,b?.retryAfter))}throw m};if(!(t.dedupe!==!1&&o==="GET"&&i==null))return s();let d=Ze(o,e,a),u=j.get(d);if(u)return u;let p=s().finally(()=>j.delete(d));return j.set(d,p),p}var Ge,se,Ke,ce,le,Xe,ue,j,tt,I=S(()=>{Ge=new Set([408,425,429,500,502,503,504]),se=new Set(["GET","HEAD","OPTIONS","PUT","DELETE"]),Ke=2,ce=4e3,le=["authorization","x-api-key","x-tensor-api-key","0x-api-key","api-key","cookie","proxy-authorization"],Xe=new Set([301,302,303,307,308]),ue=3;j=new Map;tt=e=>new Promise(t=>setTimeout(t,e))});import{readFileSync as nt}from"node:fs";import{homedir as ot}from"node:os";import{join as U}from"node:path";function at(){if(process.env.ORACLE_TEST_ISOLATE_SECRETS==="1"){let r=(process.env.ORACLE_EXEC_ENV_FILE||"").trim();return r?[r]:[]}let e=(process.env.ORACLE_EXEC_ENV_FILE||process.env.MAD_EXEC_ENV_FILE||"").trim(),t=ot();if(process.env.ORACLE_PUBLIC_DESKTOP==="1"){let r=(process.env.ORACLE_CONFIG_DIR||U(t,".config","oracle")).trim();return[e,U(r,"exec.env")].filter(Boolean)}return[e,U(t,".config","oracle","exec.env"),U(t,".config","mad-desk","exec.env")].filter(Boolean)}function de(e,t,r){let n=e??process.env[t]??process.env[r];if(!String(n||"").trim())for(let o of at())try{let a=nt(o,"utf8"),i=a.match(new RegExp(`^${t}=(.*)$`,"m"))||a.match(new RegExp(`^${r}=(.*)$`,"m"));if(i&&(n=i[1].trim(),n))break}catch{}if(!String(n||"").trim())throw new Error(`${t} required for attestation. Set it in the environment, or place ${t}=<value> in ~/.config/oracle/exec.env`);return n}var pe=S(()=>{});var me=S(()=>{});import{createHash as it,createHmac as st,timingSafeEqual as $r}from"node:crypto";function G(e){return Array.isArray(e)?`[${e.map(G).join(",")}]`:e&&typeof e=="object"?`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${G(e[t])}`).join(",")}}`:JSON.stringify(e??null)}function ct(e,{required:t=!1}={}){try{return de(e,"ORACLE_ROUTE_ATTESTATION_SECRET","MAD_ROUTE_ATTESTATION_SECRET")}catch(r){if(t)throw r;return null}}function we(e){let{signature:t,...r}=e;return r}function ge(e,{secret:t}={}){let r=ct(t);return r?{...e,signature:st("sha256",r).update(G(we(e))).digest("hex")}:e}function ut(e){let t=String(e||"").toLowerCase();if(!/^0x[0-9a-f]+$/.test(t)||t.length<10||t.length%2!==0)throw new Error("auto-slippage calldata must be even-length hex with a selector");return it("sha256").update(Buffer.from(t.slice(2),"hex")).digest("hex")}function B(e,{chainId:t,venue:r,data:n,secret:o}={}){if(!e||e.mode!=="auto")throw new Error("bounded auto-slippage guard required");if(Number(e.chainId)!==Number(t))throw new Error("auto-slippage guard chain mismatch");if(String(e.venue).toLowerCase()!==String(r||"").toLowerCase())throw new Error("auto-slippage guard venue mismatch");return ge({...we(e),calldataHash:ut(n)},{secret:o})}function z(e,t=0){let r=Number(e);return Number.isFinite(r)?r:t}function lt(e){let t=e==null?fe:Number(e);if(!Number.isInteger(t)||t<0)throw new Error("auto-slippage cap must be a non-negative integer bps value");return Math.min(t,fe)}function T({liquidityUsd:e,priceChange5m:t=0,quoteAgeMs:r=0,crossChain:n=!1,requestedCapBps:o}={}){let a=z(e,0),i=a>0?a>=25e4?5:a>=1e5?10:a>=5e4?20:a>=1e4?40:70:n?30:10,c=Math.min(60,Math.abs(z(t,0))*2),s=Math.min(40,Math.floor(Math.max(0,z(r,0))/5e3)*5),l=Math.max(25,Math.ceil(20+i+c+s)),d=lt(o);return{mode:"auto",requiredBps:l,selectedBps:Math.min(l,d),capBps:d,executable:l<=d}}function dt({chainId:e,venue:t,quoteAmountOut:r,liquidityUsd:n,priceChange5m:o=0,quoteAgeMs:a=0,crossChain:i=!1,requestedCapBps:c,quotedAtMs:s=Date.now(),ttlMs:l=he,secret:d}={}){let u=BigInt(r??0);if(u<=0n)throw new Error("auto-slippage guard requires positive quoteAmountOut");let p=T({liquidityUsd:n,priceChange5m:o,quoteAgeMs:a,crossChain:i,requestedCapBps:c});if(!p.executable)throw new Error(`auto-slippage requires ${p.requiredBps} bps above hard/requested cap ${p.capBps}; route blocked`);let m=Number(s);if(!Number.isFinite(m))throw new Error("auto-slippage: quotedAtMs must be a finite number");let w=Math.min(3e4,Math.max(1e3,Number(l)||he)),b=u*BigInt(1e4-p.selectedBps),f=b/10000n;if(b%10000n!==0n&&(f+=1n),f>u&&(f=u),u>0n&&f<=0n)throw new Error(`auto-slippage: quote ${u} is too small to express a ${p.selectedBps} bps floor without rounding to zero`);let h=Number((u-f)*10000n/(u===0n?1n:u));if(h>p.capBps)throw new Error(`auto-slippage: realised floor implies ${h} bps, above cap ${p.capBps} \u2014 refusing to issue a guard weaker than advertised`);return ge({mode:"auto",chainId:Number(e),venue:String(t||"unknown"),quoteAmountOut:u.toString(),minAmountOut:f.toString(),requiredBps:p.requiredBps,selectedBps:p.selectedBps,capBps:p.capBps,quotedAtMs:m,expiresAtMs:m+w},{secret:d})}function x(e,{chainId:t,venue:r,amountOut:n,liquidityUsd:o,priceChange5m:a,crossChain:i,requestedCapBps:c,quotedAtMs:s=Date.now(),ttlMs:l}={}){let d=dt({chainId:t,venue:r,quoteAmountOut:n,liquidityUsd:o,priceChange5m:a,crossChain:i,requestedCapBps:c,quotedAtMs:s,ttlMs:l});return{...e,autoSlippage:d,amountOutMinimum:d.minAmountOut}}var fe,he,k=S(()=>{pe();me();fe=100,he=2e4});var P,L=S(()=>{P="0x000000000000000000000000000000000000dEaD"});import{createHash as pt,createHmac as mt,timingSafeEqual as Mr}from"node:crypto";function K(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?`[${e.map(K).join(",")}]`:`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${K(e[r])}`).join(",")}}`}function be(e){let t=String(e?.ORACLE_STAMP_HMAC_SECRET||"").trim();return t.length>=16?t:""}function Ee(e=process.env){return be(process.env)||be(e)}function wt(e){let{oraclePrepared:t,prepareHash:r,prepareMac:n,note:o,...a}=e||{};return a}function gt(e){let t=wt(e);return pt("sha256").update(K(t)).digest("hex")}function bt(e,t=Ee()){return t?mt("sha256",t).update(String(e)).digest("hex"):""}function A(e,{provider:t,kind:r,env:n=process.env}={}){if(!e||typeof e!="object")throw new Error("stampPrepared: payload object required");let o={...e,provider:e.provider||t,kind:e.kind||r,requiresUserSignature:e.requiresUserSignature!==!1,signingReady:!1,broadcastReady:!1,executionReady:!1};if(!o.provider)throw new Error("stampPrepared: provider required");if(!o.kind)throw new Error("stampPrepared: kind required");let a=Date.now(),i=a+ht,s={...o,preparedAt:a,expiresAt:i,prepareVersion:ft},l=gt(s),d=Ee(n),u=d?bt(l,d):void 0,p={...s,oraclePrepared:!0,prepareHash:l};return u&&(p.prepareMac=u),p}var ft,ht,qr,C=S(()=>{ft=2,ht=300*1e3,qr=(()=>{let e=Number(process.env.ORACLE_PREPARE_HARD_MAX_AGE_MS);return Number.isFinite(e)&&e>0?e:1800*1e3})()});import{Contract as Et,JsonRpcProvider as yt}from"ethers";function vt(e){return Ct.test(String(e||""))}async function ye(e,{contract:t=At,rpc:r=St,chainId:n=xt,balanceOf:o}={}){if(!vt(e))throw new Error("invalid-address");if(o)return BigInt(await o(e));let a=new yt(r,n,{staticNetwork:!0}),i=new Et(t,It,a);return BigInt(await i.balanceOf(e))}var At,xt,St,It,Ct,Ae=S(()=>{At="0x62FCFAf7573AD8B41a0FBF347AfEb85e06599A75",xt=999,St="https://rpc.hyperliquid.xyz/evm",It=["function balanceOf(address owner) view returns (uint256)"],Ct=/^0x[a-fA-F0-9]{40}$/});import{existsSync as kt,readFileSync as _t}from"node:fs";import{homedir as Ot}from"node:os";import{join as xe}from"node:path";function Tt(e){let t=String(e||"swap").toLowerCase();return Object.prototype.hasOwnProperty.call(Se,t)?Se[t]:Bt}function Lt(e=process.env){if(e[Ie])return String(e[Ie]);let t=e.ORACLE_CONFIG_DIR||xe(Ot(),".config","oracle");return xe(t,"fee-recipient")}function Ft(e=process.env){try{let t=Lt(e);if(!kt(t))return"";let r=_t(t,"utf8").trim().toLowerCase();return/^0x[a-f0-9]{40}$/.test(r)?r:""}catch{return""}}function Dt(e,{fallback:t=0}={}){if(e==null)return t;if(String(e).trim()==="")return 0;let r=Number(e);return!Number.isFinite(r)||r<=0?0:Math.min(Math.floor(r),Rt)}function Mt({env:e=process.env,action:t="swap"}={}){let r=String(e[$t]||"").trim(),n=Tt(t),o=Dt(e[Nt],{fallback:r?n:0}),a=Math.min(o,n),i=String(e[Pt]||Ut).trim();if(!a)return{bps:0,recipient:"",integrator:i,action:t,applies:!1,reason:"not-configured"};if(!/^0x[a-fA-F0-9]{40}$/.test(r))return{bps:0,recipient:"",integrator:i,action:t,applies:!1,reason:"no-recipient"};let c=Ft(e);return c&&c!==r.toLowerCase()?{bps:0,recipient:"",integrator:i,action:t,applies:!1,reason:"recipient-not-pinned"}:{bps:a,recipient:r,integrator:i,action:t,applies:!0,reason:"configured"}}async function X({wallet:e,env:t=process.env,action:r="swap",holderCheck:n={}}={}){let o=Mt({env:t,action:r}),a=String(e||"").trim().toLowerCase();if(!/^0x[a-f0-9]{40}$/.test(a))return{...o,holderVerificationStatus:"wallet-required"};try{return BigInt(await ye(a,n))>0n?{...o,bps:0,applies:!1,reason:"locals-only-holder",holderVerificationStatus:"holder"}:{...o,holderVerificationStatus:"non-holder"}}catch{return{...o,holderVerificationStatus:"unavailable"}}}function Ce(e){return e?.applies?`Oracle routing fee ${e.bps} bps (${(e.bps/100).toFixed(2)}%) to ${e.recipient}.`:e?.reason==="locals-only-holder"?"No Oracle fee (Locals Only holder).":"No Oracle fee."}function ve(e){return e?.applies?{integrator:e.integrator,fee:String(e.bps/1e4)}:{}}var Rt,Bt,Se,Nt,$t,Pt,Ie,Ut,F=S(()=>{Ae();Rt=100,Bt=5,Se=Object.freeze({swap:5,bridge:5,perps:0,nft:0});Nt="ORACLE_INTEGRATOR_FEE_BPS",$t="ORACLE_INTEGRATOR_FEE_RECIPIENT",Pt="ORACLE_INTEGRATOR_ID",Ie="ORACLE_INTEGRATOR_FEE_RECIPIENT_PIN",Ut="oracle"});var Oe={};re(Oe,{LIFI_API:()=>_e,lifiChains:()=>zt,lifiHealth:()=>Vt,lifiPrepare:()=>jt,lifiQuote:()=>D});function Q(e={}){return(e.baseUrl||process.env.LIFI_API_URL||_e).replace(/\/$/,"")}function Ht(e){let t=String(e||Y).toLowerCase();return t===Y.toLowerCase()||t===qt}function ke(e,t){let r=String(e||"").trim();if(!/^0x[0-9a-fA-F]{40}$/.test(r))throw new Error(`lifi: ${t} must be a 20-byte address`);return r.toLowerCase()}async function Vt(e={}){let t=await E(`${Q(e)}/chains`,{fetchImpl:e.fetchImpl,timeoutMs:e.timeoutMs??12e3}),r=Array.isArray(t)?t.length:t?.chains?.length||0;return{ok:r>0,chainCount:r}}async function D(e={},t={}){let{fromChain:r,toChain:n=r,fromToken:o=Y,toToken:a,fromAmount:i,fromAddress:c=P}=e;if(r==null||!a||!i)throw new Error("lifiQuote requires fromChain, toToken, fromAmount");let s=new URLSearchParams({fromChain:String(r),toChain:String(n),fromToken:o,toToken:a,fromAmount:String(i),fromAddress:c}),l=T({liquidityUsd:e.liquidityUsd,priceChange5m:e.priceChange5m,crossChain:Number(n)!==Number(r),requestedCapBps:e.maxSlippageBps??e.slippageBps});if(!l.executable)throw new Error(`lifi: route requires ${l.requiredBps} bps above cap ${l.capBps}`);s.set("slippage",String(l.selectedBps/1e4));let d=await X({wallet:e.fromAddress,env:t.env,action:e.feeAction||"swap",holderCheck:t.holderCheck});for(let[f,h]of Object.entries(ve(d)))s.set(f,h);let u=await E(`${Q(t)}/quote?${s}`,{fetchImpl:t.fetchImpl,timeoutMs:t.timeoutMs??2e4}),p=u?.estimate?.toAmount||u?.toAmount;if(!p)return{...u,autoSlippagePolicy:l,oracleFee:d};let m=u?.transactionRequest?.to||u?.estimate?.approvalAddress||"lifi",w=x(u,{chainId:Number(r),venue:m,amountOut:p,liquidityUsd:e.liquidityUsd,priceChange5m:e.priceChange5m,crossChain:Number(n)!==Number(r),requestedCapBps:e.maxSlippageBps??e.slippageBps}),b=u?.estimate?.toAmountMin||u?.toAmountMin;if(b!=null&&BigInt(b)<BigInt(w.amountOutMinimum))throw new Error("lifi: provider transaction is under-protected versus bounded auto-slippage floor");return{...w,oracleFee:d}}async function jt(e={},t={}){let r=await D(e,t),n=r?.transactionRequest;if(!n?.to||!n?.data)throw new Error("lifi: quote did not return an executable transactionRequest");let o=Number(e.fromChain);if(n.chainId!=null&&Number(n.chainId)!==o)throw new Error("lifi: transaction chain mismatch");if(e.fromAddress&&n.from&&String(n.from).toLowerCase()!==String(e.fromAddress).toLowerCase())throw new Error("lifi: transaction sender mismatch");let a=null;if(!Ht(e.fromToken)){let i=r?.estimate?.approvalAddress||r?.approvalAddress;if(!i)throw new Error("lifi: ERC20 route missing approvalAddress");let c=r?.action?.fromToken?.address||r?.estimate?.fromToken?.address||e.fromToken;a={token:ke(c,"approval token"),spender:ke(i,"approval spender"),amount:String(e.fromAmount)}}return A({provider:"lifi",calldataReady:!0,requiresUserSignature:!0,signingReady:!1,broadcastReady:!1,chainId:o,quote:r,autoSlippage:r.autoSlippage,requiresApproval:a,transaction:{chainId:o,from:n.from||e.fromAddress,to:n.to,data:n.data,value:n.value??"0x0",...n.gasLimit!=null?{gasLimit:n.gasLimit}:{},slippageGuard:B(r.autoSlippage,{chainId:o,venue:n.to,data:n.data})}},{provider:"lifi",kind:"lifi-tx"})}async function zt(e={}){return E(`${Q(e)}/chains`,{fetchImpl:e.fetchImpl,timeoutMs:e.timeoutMs??12e3})}var _e,Y,qt,W=S(()=>{I();k();L();C();F();_e="https://li.quest/v1";Y="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",qt="0x0000000000000000000000000000000000000000"});var M={};re(M,{ACROSS_API:()=>$e,HOP_API:()=>Pe,RELAY_API:()=>Ue,acrossHealth:()=>nr,acrossSuggestedFees:()=>Le,hopHealth:()=>or,hopQuote:()=>Fe,relayHealth:()=>ar,relayPrepare:()=>ir,relayQuote:()=>Z});async function nr(e={}){try{return{ok:!0,estimatedFillTimeSec:(await Le({token:"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",destinationToken:"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",originChainId:1,destinationChainId:42161,amount:"1000000000000000"},e)).estimatedFillTimeSec}}catch(t){return{ok:!1,error:String(t.message||t).slice(0,160)}}}async function Le(e={},t={}){let{token:r,destinationToken:n=r,originChainId:o,destinationChainId:a,amount:i}=e;if(!r||o==null||a==null||!i)throw new Error("acrossSuggestedFees requires token, originChainId, destinationChainId, amount");let c=new URLSearchParams({token:r,destinationToken:n,originChainId:String(o),destinationChainId:String(a),amount:String(i)}),s=(t.baseUrl||process.env.ACROSS_API_URL||$e).replace(/\/$/,"");return E(`${s}/suggested-fees?${c}`,{fetchImpl:t.fetchImpl,timeoutMs:t.timeoutMs??15e3})}async function or(e={}){try{let t=await Fe({amount:"1000000000000000",token:"ETH",fromChain:"ethereum",toChain:"arbitrum",slippage:.5},e);return{ok:!0,estimatedRecieved:t.estimatedRecieved||t.estimatedReceived}}catch(t){return{ok:!1,error:String(t.message||t).slice(0,160)}}}async function Fe(e={},t={}){let{amount:r,token:n="ETH",fromChain:o="ethereum",toChain:a="arbitrum"}=e;if(!r)throw new Error("hopQuote requires amount");let i=e.slippage==null?void 0:Math.round(Number(e.slippage)*100),c=T({liquidityUsd:e.liquidityUsd,priceChange5m:e.priceChange5m,crossChain:o!==a,requestedCapBps:e.maxSlippageBps??e.slippageBps??i});if(!c.executable)throw new Error(`hop: route requires ${c.requiredBps} bps above cap ${c.capBps}`);let s=new URLSearchParams({amount:String(r),token:n,fromChain:o,toChain:a,slippage:String(c.selectedBps/100)}),l=(t.baseUrl||process.env.HOP_API_URL||Pe).replace(/\/$/,""),d=await E(`${l}/quote?${s}`,{fetchImpl:t.fetchImpl,timeoutMs:t.timeoutMs??15e3}),u=d?.estimatedRecieved||d?.estimatedReceived;return u?x(d,{chainId:Number(e.originChainId||e.chainId||rr[o]||0),venue:d?.tx?.to||"hop",amountOut:u,liquidityUsd:e.liquidityUsd,priceChange5m:e.priceChange5m,crossChain:o!==a,requestedCapBps:e.maxSlippageBps??e.slippageBps??i}):{...d,autoSlippagePolicy:c}}async function ar(e={}){try{let t=await Z({user:P,originChainId:1,destinationChainId:8453,originCurrency:"0x0000000000000000000000000000000000000000",destinationCurrency:"0x0000000000000000000000000000000000000000",amount:"1000000000000000",tradeType:"EXACT_INPUT"},e);return{ok:!0,hasSteps:Array.isArray(t.steps),stepCount:t.steps?.length||0}}catch(t){return{ok:!1,error:String(t.message||t).slice(0,160)}}}async function Z(e={},t={}){let{user:r=P,originChainId:n,destinationChainId:o,originCurrency:a="0x0000000000000000000000000000000000000000",destinationCurrency:i="0x0000000000000000000000000000000000000000",amount:c,tradeType:s="EXACT_INPUT"}=e;if(n==null||o==null||!c)throw new Error("relayQuote requires originChainId, destinationChainId, amount");let l=T({liquidityUsd:e.liquidityUsd,priceChange5m:e.priceChange5m,crossChain:Number(n)!==Number(o),requestedCapBps:e.maxSlippageBps??e.slippageBps});if(!l.executable)throw new Error(`relay: route requires ${l.requiredBps} bps above cap ${l.capBps}`);let d=(t.baseUrl||process.env.RELAY_API_URL||Ue).replace(/\/$/,""),u=await E(`${d}/quote`,{method:"POST",body:{user:r,originChainId:Number(n),destinationChainId:Number(o),originCurrency:a,destinationCurrency:i,amount:String(c),tradeType:s,slippageTolerance:String(l.selectedBps)},fetchImpl:t.fetchImpl,timeoutMs:t.timeoutMs??2e4}),p=u?.details?.currencyOut?.amount;return!p||!/^\d+$/.test(String(p))?{...u,autoSlippagePolicy:l}:x(u,{chainId:Number(n),venue:u?.steps?.[0]?.items?.[0]?.data?.to||"relay",amountOut:p,liquidityUsd:e.liquidityUsd,priceChange5m:e.priceChange5m,crossChain:Number(n)!==Number(o),requestedCapBps:e.maxSlippageBps??e.slippageBps})}async function ir(e={},t={}){let r=await Z(e,t),n=Number(e.originChainId),o=(r.steps||[]).flatMap(c=>(c.items||[]).filter(s=>s?.data?.to&&s?.data?.data).map(s=>({stepId:c.id,...s.data})));if(!o.length)throw new Error("relay: quote returned no executable transaction items");let a=String(r?.autoSlippage?.venue||"").toLowerCase();if(o.some(c=>String(c.to).toLowerCase()!==a))throw new Error("relay: route includes a separate approval/transaction that is not yet safely bound");let i=o.map(c=>({chainId:n,from:c.from||e.user,to:c.to,data:c.data,value:c.value??"0x0",...c.gasLimit!=null||c.gas!=null?{gasLimit:c.gasLimit??c.gas}:{},slippageGuard:B(r.autoSlippage,{chainId:n,venue:c.to,data:c.data})}));return A({provider:"relay",calldataReady:!0,requiresUserSignature:!0,signingReady:!1,broadcastReady:!1,chainId:n,quote:r,autoSlippage:r.autoSlippage,transactions:i},{provider:"relay",kind:"relay-tx"})}var $e,Pe,Ue,rr,q=S(()=>{I();k();L();C();$e="https://across.to/api",Pe="https://api.hop.exchange/v1",Ue="https://api.relay.link",rr={ethereum:1,optimism:10,bsc:56,polygon:137,arbitrum:42161,avalanche:43114,base:8453}});var br=Object.freeze(["blockNumber","nativeBalance","tokenBalance","resolveToken","resolvePools","scanBlocks","scoreRisk","quote","sellSimulation","prepareUnsignedTx"]),Er=Object.freeze({LIVE:"LIVE",CACHED:"CACHED",STALE:"STALE",UNKNOWN:"UNKNOWN",UNAVAILABLE:"UNAVAILABLE"}),yr=Object.freeze({PASS:"PASS",CAUTION:"CAUTION",FAIL:"FAIL",UNKNOWN:"UNKNOWN"});var R=Object.freeze({FULL:"FULL",NO_GAS:"NO_GAS",FAILED:"FAILED"});var ne=e=>{try{return e==null||e===""?null:BigInt(String(e).split(".")[0])}catch{return null}};function oe(e){let{source:t,amountOut:r,minOut:n=null,gasUsd:o=null,feeUsd:a=null,oracleFeeBps:i=0,estimatedGas:c=null,liquidityUsd:s=null,error:l=null,executionReady:d=!1,meta:u={}}=e||{};if(l||r==null)return{source:t,quality:R.FAILED,amountOut:null,error:l||"no amountOut returned"};let p=ne(r);if(p==null||p<=0n)return{source:t,quality:R.FAILED,amountOut:null,error:"non-positive amountOut"};let m=Number(i);return{source:t,quality:o==null?R.NO_GAS:R.FULL,amountOut:p.toString(),minOut:ne(n)?.toString()??null,gasUsd:o,feeUsd:a,oracleFeeBps:Number.isFinite(m)&&m>0?Math.floor(m):0,estimatedGas:c==null?null:String(c),liquidityUsd:s==null?null:Number(s),executionReady:d,meta:u}}function ae(e,{destPriceUsd:t=null,destDecimals:r=null,liquidityFirst:n=!0,liquidityBandBps:o=50}={}){let a=e.filter(h=>h.quality!==R.FAILED),i=e.filter(h=>h.quality===R.FAILED),c=t!=null&&t>0&&r!=null&&r>=0,s=a.map(h=>{let g=BigInt(h.amountOut),y=0n,v=!1,_=BigInt(h.oracleFeeBps??0),O=_>0n?g*_/10000n:0n;if(c){let te=(h.gasUsd??0)+(h.feeUsd??0);if(te>0){let V=te/t*10**r;Number.isFinite(V)&&V>=0&&(y=BigInt(Math.floor(V)),v=!0)}else h.gasUsd===0&&(v=!0)}let ee=y+O,Ve=g>ee?g-ee:0n;return{...h,grossOut:g.toString(),netOut:Ve.toString(),costInDest:y.toString(),oracleFeeInDest:O.toString(),costAccounted:v}}),l=Number(o)>0?Number(o):0,d=s.reduce((h,g)=>BigInt(g.netOut)>h?BigInt(g.netOut):h,0n),u=h=>d>0n?Number((d-BigInt(h))*10000n/d):0,p=[],m=s.slice().sort((h,g)=>BigInt(g.netOut)>BigInt(h.netOut)?1:-1)[0]??null;s.sort((h,g)=>{let y=u(h.netOut)<=l?0:1,v=u(g.netOut)<=l?0:1;if(n&&l>0&&y!==v)return y-v;if(n&&l>0){let _=h.liquidityUsd??-1,O=g.liquidityUsd??-1;if(_!==O)return O-_}return BigInt(g.netOut)>BigInt(h.netOut)?1:-1}),n&&l>0&&m&&s[0]&&s[0].source!==m.source&&p.push(`liquidity-first: chose ${s[0].source} (liquidity $${s[0].liquidityUsd??"unknown"}) over best-price ${m.source} within ${l} bps`);let w=s.filter(h=>!h.costAccounted).map(h=>h.source);c?w.length&&p.push(`gas not reported by: ${w.join(", ")}. These are ranked on gross output and may be overstated relative to gas-measured routes.`):p.push("destination price/decimals unavailable: ranked on GROSS output. Gas is not included, so a cheap-looking route may cost more to execute.");let b=null,f=null;if(s.length>=2&&s[0].costAccounted&&s[1].costAccounted){let h=BigInt(s[0].netOut),g=BigInt(s[1].netOut);g>0n&&(b=Number((h-g)*10000n/g),f=`${s[0].source} vs ${s[1].source}`)}return{best:s[0]??null,routes:s,failed:i,improvementBps:b,spreadBasis:f,rankedOn:c?"net-of-cost":"gross",warnings:p}}async function ie(e,{timeoutMs:t=12e3}={}){let r=(o,a)=>Promise.race([o,new Promise((i,c)=>setTimeout(()=>c(new Error(`${a}: timeout`)),t))]);return(await Promise.allSettled(e.map(o=>r(Promise.resolve().then(o.run),o.source)))).map((o,a)=>{let i=e[a].source;return o.status==="rejected"?oe({source:i,error:String(o.reason?.message||o.reason)}):oe({source:i,...o.value})})}W();I();k();C();F();I();k();C();I();k();C();I();k();import{TypedDataEncoder as vn,getAddress as kn,isAddress as _n}from"ethers";var yn=Object.freeze({READ:"read",PREPARE:"prepare",EXECUTE:"execute",AUTONOMOUS:"autonomous"}),An=Object.freeze({"evm:prepare":["ORACLE_EVM_ADDRESS","EVM_KEYSTORE_FILE","ORACLE_SIGNER_ADDRESS"],"evm:execute":["EVM_KEYSTORE_FILE","ORACLE_KEYSTORE_FILE"],"btc:execute":["BTC_WIF_FILE","ORACLE_BTC_WIF_FILE"],"solana:execute":["SOLANA_KEYPAIR_FILE","ORACLE_SOLANA_KEYPAIR_FILE"],"nft:buy":[],"nft:mint":[],"cow:submit":[]});L();C();var Pn=Object.freeze({1:"0x9008D19f58AAbD9eD0D60971565AA8510560ab41",100:"0x9008D19f58AAbD9eD0D60971565AA8510560ab41",42161:"0x9008D19f58AAbD9eD0D60971565AA8510560ab41",8453:"0x9008D19f58AAbD9eD0D60971565AA8510560ab41"}),Un=Object.freeze({1:"0xC92E8bdf79f0507f65a392b0ab4667716BFE0110",100:"0xC92E8bdf79f0507f65a392b0ab4667716BFE0110",42161:"0xC92E8bdf79f0507f65a392b0ab4667716BFE0110",8453:"0xC92E8bdf79f0507f65a392b0ab4667716BFE0110"});var Ln=Object.freeze({Order:[{name:"sellToken",type:"address"},{name:"buyToken",type:"address"},{name:"receiver",type:"address"},{name:"sellAmount",type:"uint256"},{name:"buyAmount",type:"uint256"},{name:"validTo",type:"uint32"},{name:"appData",type:"bytes32"},{name:"feeAmount",type:"uint256"},{name:"kind",type:"string"},{name:"partiallyFillable",type:"bool"},{name:"sellTokenBalance",type:"string"},{name:"buyTokenBalance",type:"string"}]}),Fn=Object.freeze({OrderCancellations:[{name:"orderUids",type:"bytes[]"}]});import{Interface as Be,getAddress as to,isAddress as ro}from"ethers";I();var Re={robinhood:{chainId:4663,name:"Robinhood Chain",rpcEnv:["RH_CHAIN_RPC"],evm:!0},ethereum:{chainId:1,name:"Ethereum",rpcEnv:["ETH_RPC_URL","ETHEREUM_RPC_URL","MAINNET_RPC_URL"],evm:!0},polygon:{chainId:137,name:"Polygon",rpcEnv:["POLYGON_RPC_URL","POLYGON_RPC"],evm:!0},arbitrum:{chainId:42161,name:"Arbitrum One",rpcEnv:["ARBITRUM_RPC_URL","ARB_RPC_URL"],evm:!0},base:{chainId:8453,name:"Base",rpcEnv:["BASE_RPC_URL"],evm:!0},hyperevm:{chainId:999,name:"HyperEVM",rpcEnv:["HYPEREVM_RPC_URL","HYPER_EVM_RPC"],evm:!0},bsc:{chainId:56,name:"BNB Smart Chain",rpcEnv:["BSC_RPC_URL","BNB_RPC_URL"],evm:!0},avalanche:{chainId:43114,name:"Avalanche C-Chain",rpcEnv:["AVALANCHE_RPC_URL","AVAX_RPC_URL"],evm:!0},optimism:{chainId:10,name:"OP Mainnet",rpcEnv:["OPTIMISM_RPC_URL","OP_RPC_URL"],evm:!0},abstract:{chainId:2741,name:"Abstract",rpcEnv:["ABSTRACT_RPC_URL","ABS_RPC_URL"],evm:!0},stable:{chainId:988,name:"Stable Mainnet",rpcEnv:["STABLE_RPC_URL","STABLE_RPC"],evm:!0}},Mn=new Map(Object.values(Re).map(e=>[e.chainId,e]));import{Interface as Xt,getAddress as Xn}from"ethers";var Yn=new Xt(["function allowance(address owner,address spender) view returns (uint256)","function balanceOf(address account) view returns (uint256)"]);I();var Jn=Object.freeze({1:"ethereum",10:"optimism",56:"bsc",137:"polygon",8453:"base",42161:"arbitrum",43114:"avalanche",999:"hyperevm",4663:"robinhood"});k();C();var so=new Be(["function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96) params) returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)","function quoteExactInput(bytes path, uint256 amountIn) returns (uint256 amountOut, uint160[] sqrtPriceX96AfterList, uint32[] initializedTicksCrossedList, uint256 gasEstimate)"]),co=new Be(["function exactInputSingle((address tokenIn,address tokenOut,uint24 fee,address recipient,uint256 amountIn,uint256 amountOutMinimum,uint160 sqrtPriceLimitX96) params) payable returns (uint256 amountOut)","function multicall(uint256 deadline,bytes[] data) payable returns (bytes[] results)"]);import{AbiCoder as Wt,Interface as Te,getAddress as po,isAddress as mo,keccak256 as fo}from"ethers";k();C();var Eo=Wt.defaultAbiCoder();var yo=new Te(["function quoteExactInputSingle(((address currency0,address currency1,uint24 fee,int24 tickSpacing,address hooks) poolKey,bool zeroForOne,uint128 exactAmount,bytes hookData) params) returns (uint256 amountOut, uint256 gasEstimate)"]),Ao=new Te(["function execute(bytes commands,bytes[] inputs,uint256 deadline) payable"]);I();var Jt="https://coins.llama.fi",Zt="https://api.llama.fi";function er(e={}){return(e.baseUrl||process.env.DEFILLAMA_COINS_URL||Jt).replace(/\/$/,"")}function tr(e={}){return(e.baseUrl||process.env.DEFILLAMA_API_URL||Zt).replace(/\/$/,"")}async function J(e,t={}){if(!Array.isArray(e)||!e.length)throw new Error("llamaPrices requires coins[]");let r=e.map(encodeURIComponent).join(",");return E(`${er(t)}/prices/current/${r}`,{fetchImpl:t.fetchImpl,timeoutMs:t.timeoutMs})}async function Ne(e={}){return E(`${tr(e)}/protocols`,{fetchImpl:e.fetchImpl,timeoutMs:e.timeoutMs??2e4})}var $=e=>{let t=Number(e);return Number.isFinite(t)?t:null};function De(e){if(!Array.isArray(e))return null;let t=0,r=!1;for(let n of e){let o=$(n?.amountUSD);o!=null&&(t+=o,r=!0)}return r?t:null}var N=e=>String(e??"").toLowerCase().replace(/[^a-z0-9]/g,""),Me=Object.freeze({across:["across"],relay:["relaychain","relay"],lifi:["jumper-(li.fi-powered)","lifi"],stargate:["stargate-v2","stargate-v1","stargate"],hop:["hop-protocol","hop"],debridge:["debridge"],synapse:["synapse"],connext:["connext"],cbridge:["celer-bridge","celer-cbridge","cbridge"],celer:["celer-bridge","celer-cbridge","cbridge"],squid:["squid"],symbiosis:["symbiosis"],wormhole:["wormhole"],mayan:["mayan"],allbridge:["allbridge"],thorchain:["thorchain"]});async function sr(e={}){try{let t=await Ne({...e,baseUrl:e.defillamaApiBaseUrl,timeoutMs:e.defillamaTimeoutMs??Math.min(Number(e.timeoutMs??5e3),5e3)});return Array.isArray(t)?t:[]}catch{return[]}}function cr(e,t={}){let r=N(t.tool||t.provider||t.bridge||t.protocol),n=[];return e==="lifi"&&r&&r!=="lifi"&&n.push(...Me[r]||[r]),n.push(...Me[N(e)]||[e]),n}function ur(e,t,r={}){let n=Array.isArray(e)?e:[],o=cr(t,r).map(N).filter(Boolean);for(let a of o){let i=n.find(s=>N(s?.slug)===a||N(s?.name)===a),c=$(i?.tvl);if(c!=null)return{liquidityUsd:c,protocol:i?.name??i?.slug??a}}for(let a of o){let i=n.find(s=>{let l=N(s?.category);return!l.includes("bridge")&&!l.includes("crosschain")?!1:N(s?.slug).includes(a)||N(s?.name).includes(a)}),c=$(i?.tvl);if(c!=null)return{liquidityUsd:c,protocol:i?.name??i?.slug??a}}return null}function qe({fromChainId:e,toChainId:t,tokenIn:r,tokenOut:n,amountIn:o,taker:a,opts:i={}}){let c=[],s=null,l=async(d,u={})=>(s||=sr(i),ur(await s,d,u));return c.push({source:"lifi",run:async()=>{let d=await D({fromChain:e,toChain:t,fromToken:r,toToken:n,fromAmount:o},i),u=d?.estimate??d?.[0]?.estimate,p={tool:d?.tool??d?.toolDetails?.name,durationSeconds:$(u?.executionDuration)},m=await l("lifi",p);return{amountOut:u?.toAmount,minOut:u?.toAmountMin,gasUsd:De(u?.gasCosts),feeUsd:De(u?.feeCosts),oracleFeeBps:d?.oracleFee?.applies?d.oracleFee.bps:0,liquidityUsd:m?.liquidityUsd??null,meta:{...p,liquidityProtocol:m?.protocol,oracleFee:d?.oracleFee??null}}}}),c.push({source:"relay",run:async()=>{let{relayQuote:d}=await Promise.resolve().then(()=>(q(),M)),p=(await d({user:a,originChainId:e,destinationChainId:t,originCurrency:r,destinationCurrency:n,amount:String(o)},i))?.details,m={durationSeconds:$(p?.timeEstimate),rate:p?.rate},w=await l("relay",m);return{amountOut:p?.currencyOut?.amount,minOut:p?.currencyOut?.minimumAmount,gasUsd:$(p?.totalImpact?.usd)!=null?Math.abs($(p.totalImpact.usd)):null,liquidityUsd:w?.liquidityUsd??null,meta:{...m,liquidityProtocol:w?.protocol}}}}),c.push({source:"across",run:async()=>{let{acrossSuggestedFees:d}=await Promise.resolve().then(()=>(q(),M)),u=await d({token:r,destinationToken:n,originChainId:e,destinationChainId:t,amount:String(o)},i),p=u?.totalRelayFee?.total,m=null;if(p!=null)try{let b=BigInt(String(o))-BigInt(String(p));b>0n&&(m=b.toString())}catch{}let w=await l("across");return{amountOut:m,gasUsd:null,liquidityUsd:w?.liquidityUsd??null,meta:{derivedFromFees:!0,durationSeconds:$(u?.estimatedFillTimeSec),limits:u?.limits?{min:u.limits.minDeposit,max:u.limits.maxDeposit}:void 0,liquidityProtocol:w?.protocol}}}}),c}F();var lr={1:"ethereum",10:"optimism",56:"bsc",137:"polygon",8453:"base",42161:"arbitrum",43114:"avax"};async function dr(e,t,r){let n=lr[Number(e)];if(!n)return{priceUsd:null,decimals:null};let o=`${n}:${t}`;try{let i=(await J([o],r))?.coins?.[o],c=Number(i?.price),s=Number(i?.decimals);return{priceUsd:Number.isFinite(c)?c:null,decimals:Number.isFinite(s)?s:null}}catch{return{priceUsd:null,decimals:null}}}async function He(e,t={}){let{fromChainId:r,toChainId:n,tokenIn:o,tokenOut:a,amountIn:i}=e;if(r==null||n==null||!o||!a||i==null)throw new Error("bestBridgeRoute requires fromChainId, toChainId, tokenIn, tokenOut, amountIn");if(Number(r)===Number(n))throw new Error("bestBridgeRoute is for cross-chain; use bestSwapRoute for same-chain");let c=dr(n,a,t),s=qe({...e,opts:t}),[l,d]=await Promise.all([ie(s,{timeoutMs:t.timeoutMs??15e3}),c]),u=ae(l,{destPriceUsd:d.priceUsd,destDecimals:e.decimalsOut??d.decimals,liquidityBandBps:t.liquidityBandBps??50}),p=u.routes.map(m=>({source:m.source,seconds:m.meta?.durationSeconds??null})).filter(m=>m.seconds!=null);return{kind:"bridge",fromChainId:r,toChainId:n,tokenIn:o,tokenOut:a,amountIn:String(i),...u,durations:p,sourcesTried:s.length,sourcesAnswered:l.filter(m=>m.quality!==R.FAILED).length,note:mr(u,s.length)}}function pr(e){let t=e?.meta?.oracleFee;return t?.applies?` \xB7 ${Ce(t)}`:""}function mr(e,t){if(!e.best)return"no source returned a usable route";let r=e.routes.length,n=pr(e.best);if(r===1)return`only 1 of ${t} sources answered, so this is the ONLY route, not a proven best one. Treat it as unbenchmarked.`+n;if(e.improvementBps!=null){let o=e.improvementBps,a=o/100;if(o<.01)return`best of ${r} routes, ranked on ${e.rankedOn}: effectively tied with the runner-up (${e.spreadBasis}), inside quote noise. Re-quote before you size up.`+n;let i=a>=.01?`${a.toFixed(2)}%`:`${o.toFixed(2)}bps`;return`best of ${r} routes, ranked on ${e.rankedOn}: ${i} better than the runner-up (${e.spreadBasis})`+n}return`best of ${r} routes, ranked on ${e.rankedOn}. No spread quoted: the top routes report cost differently, so the margin is not comparable.`+n}C();var fr=/^0x[0-9a-fA-F]{40}$/,hr=new Set(["0x0000000000000000000000000000000000000000","0x000000000000000000000000000000000000dead","0x0000000000000000000000000000000000000001","0x00000000000000000000000000000000000a1ce5"].map(e=>e.toLowerCase())),wr=100,H={relay:async({fromChainId:e,toChainId:t,tokenIn:r,tokenOut:n,amountIn:o,taker:a},i)=>{let{relayPrepare:c}=await Promise.resolve().then(()=>(q(),M)),s=await c({user:a,originChainId:e,destinationChainId:t,originCurrency:r,destinationCurrency:n,amount:String(o)},i);return{transactions:s.transactions||[],amountOut:s.quote?.details?.currencyOut?.amount??null,minOut:s.quote?.details?.currencyOut?.minimumAmount??null,durationSeconds:s.quote?.details?.timeEstimate??null}},lifi:async({fromChainId:e,toChainId:t,tokenIn:r,tokenOut:n,amountIn:o,taker:a,isHolder:i},c)=>{let{lifiPrepare:s}=await Promise.resolve().then(()=>(W(),Oe)),l=await s({fromChain:e,toChain:t,fromToken:r,toToken:n,fromAmount:o,fromAddress:a,isHolder:i,feeAction:"bridge"},c);return{transactions:l.transaction?[l.transaction]:[],requiresApproval:l.requiresApproval??null,amountOut:l.quote?.estimate?.toAmount??null,minOut:l.quote?.estimate?.toAmountMin??null,durationSeconds:l.quote?.estimate?.executionDuration??null}}};function ia(){return Object.keys(H)}async function sa(e,t={}){let{taker:r,fromChainId:n,toChainId:o}=e;if(!r)throw new Error("prepareBestBridgeRoute requires taker: bridge transactions are built FOR an address, and the quote-only placeholder must never end up in one");if(!fr.test(r))throw new Error(`taker must be a valid 20-byte address, got "${r}"`);if(hr.has(r.toLowerCase()))throw new Error(`taker "${r}" is a placeholder/burn address. Bridging to it would send funds to an address nobody controls, on a chain where you cannot undo it.`);let a=await He(e,t);if(!a.best)return{ok:!1,reason:"no source returned a usable bridge route",comparison:a};let i=e.source||a.best.source,c=a.routes.find(f=>f.source===i);if(!c)return{ok:!1,reason:`source "${i}" returned no usable route`,comparison:a};let s=H[i];if(!s){let f=a.routes.find(h=>H[h.source]&&h.source!==i);return{ok:!1,reason:`${i} won the comparison but has no bridge prepare path in this build. `+(f?`Next preparable route is ${f.source}; pass source:"${f.source}".`:"No route in this comparison can be prepared."),failureKind:"no-prepare-path",...f?{alternative:f.source}:{},winner:i,comparison:a}}let l;try{l=await s(e,t)}catch(f){let h=typeof f?.body=="string"?f.body:f?.body?JSON.stringify(f.body):"",g=[String(f?.message||f),h].filter(Boolean).join(" :: "),y=/allowance/i.test(g),v=!y&&/not enough|insufficient|balance/i.test(g),_=a.routes.find(O=>H[O.source]&&O.source!==i);return{ok:!1,reason:y?`${i} will not build calldata until the token approval exists on-chain. Approve first, then prepare again. Underlying: ${g}`:v?`${i} refused to build: ${g}. Quoting works for any address; preparing needs the real funded wallet on chain ${n}.`:`${i} bridge prepare failed: ${g}`,failureKind:y?"approval-required-first":v?"taker-not-funded":"provider-error",..._?{alternative:_.source}:{},comparison:a}}let d=l.transactions||[];if(!d.length)return{ok:!1,reason:`${i} returned no executable transaction for this route`,failureKind:"provider-error",comparison:a};let u=d.filter(f=>f.chainId!=null&&Number(f.chainId)!==Number(n));if(u.length)return{ok:!1,reason:`${i} returned a transaction for chain ${u[0].chainId} but the origin chain is ${n}. Refusing to hand over a mismatched artifact.`,failureKind:"chain-mismatch",comparison:a};let p=e.driftToleranceBps??wr,m=null,w=null;if(l.amountOut!=null&&c.grossOut!=null)try{let f=BigInt(c.grossOut),h=BigInt(l.amountOut);f>0n&&(m=Number((h-f)*10000n/f),m<-p&&(w=`route moved ${(m/100).toFixed(2)}% against you since the comparison (tolerance ${(p/100).toFixed(2)}%). Re-compare before signing.`))}catch{}let b=[...w?[w]:[],...a.warnings||[],`Bridging is NOT atomic: the origin transaction succeeding does not mean funds arrived on chain ${o}. Expect ~${l.durationSeconds!=null?`${l.durationSeconds}s`:"minutes"} before the destination credits, and do not re-send if it is pending.`];return d.length>1&&b.push(`This route needs ${d.length} transactions signed IN ORDER. Signing only the first leaves funds approved but not bridged.`),A({ok:!0,chosen:{source:i,wasWinner:i===a.best.source,netOut:c.netOut,grossOut:c.grossOut,gasUsd:c.gasUsd},artifactKind:"unsigned-transaction-sequence",fromChainId:Number(n),toChainId:Number(o),transactionCount:d.length,transactions:d,requiresApproval:l.requiresApproval??null,minOut:l.minOut??null,durationSeconds:l.durationSeconds??null,destination:d[0]?.to??null,unsigned:!0,signedBy:"user-wallet",driftBps:m,warnings:b,runnersUp:a.routes.filter(f=>f.source!==i).map(f=>({source:f.source,netOut:f.netOut,gasUsd:f.gasUsd})),comparison:{rankedOn:a.rankedOn,sourcesAnswered:a.sourcesAnswered,sourcesTried:a.sourcesTried,improvementBps:a.improvementBps},note:`UNSIGNED. ${d.length} transaction(s) on chain ${n}, crediting chain ${o}. Verify every destination address before signing.`},{provider:"router",kind:"best-bridge"})}export{sa as prepareBestBridgeRoute,ia as supportedBridgePreparers};
|
package/dist/strategy/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{createHash as Ur}from"node:crypto";var Lt=1,Ze=/^[a-z0-9][a-z0-9._-]{0,63}$/,We=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,Cr=new Set(["1m","3m","5m","15m","30m","1h","2h","4h","8h","12h","1d"]),qr=new Set(["open","high","low","close","volume","fundingRate","openInterest"]),Br=new Set(["sma","ema","rsi","macd","bollinger","atr"]),Dr=new Set(["gt","gte","lt","lte","eq"]),zr=new Set(["and","or","not"]),Vr=new Set(["above","below"]),Jr=new Set(["line","signal","histogram"]),Kr=new Set(["upper","middle","lower"]),Xt=new Set(["compare","logic","cross"]),qe=new Set(["version","id","name","venue","market","parameters","nodes","rules","risk"]),Yr=new Set(["coin","interval"]),Be=new Set(["value","min","max","step"]),Zt=new Set(["entryLong","entryShort","exitLong","exitShort"]),De=new Set(["maxLeverage","maxNotionalUsd","positionSizePct","stopLossPct","takeProfitPct","cooldownBars","maxDailyLossPct","expiresAt"]),Gr=new Set(["id","type"]),ze={input:new Set(["id","type","field"]),constant:new Set(["id","type","value"]),indicator:new Set(["id","type","indicator","input","period","fastPeriod","slowPeriod","signalPeriod","stdDev","output"]),compare:new Set(["id","type","op","left","right"]),logic:new Set(["id","type","op","inputs"]),cross:new Set(["id","type","direction","left","right"])},Ve=16,Je=128,Ke=100,Ye=32,ut=class extends Error{constructor(e){let r=e.map(n=>`${n.path}: ${n.message}`);super(`invalid strategy (${e.length} error${e.length===1?"":"s"}): ${r.join("; ")}`),this.name="StrategyValidationError",this.errors=e}};function K(t){return t!=null&&typeof t=="object"&&!Array.isArray(t)}function U(t){return typeof t=="number"&&Number.isFinite(t)}function Ge(t){return Number.isInteger(t)&&t>0}function Qt(t){if(t==null||typeof t!="object"||Object.isFrozen(t))return t;if(Array.isArray(t)){for(let e of t)Qt(e);return Object.freeze(t)}for(let e of Object.keys(t))Qt(t[e]);return Object.freeze(t)}function te(t){if(Array.isArray(t))return t.map(te);if(t!=null&&typeof t=="object"){let e={};for(let r of Object.keys(t).sort())e[r]=te(t[r]);return e}return t}function y(t,e,r){t.push({path:e,message:r})}function nt(t,e,r,n){for(let o of Object.keys(t))e.has(o)||y(n,r?`${r}.${o}`:o,"unknown field is not allowed")}function Qe(t,e,r,n){if(!U(t)||!U(e)||!U(r)||!U(n)||n<=0||t<e||t>r)return!1;let o=(t-e)/n;if(!Number.isFinite(o))return!1;let s=Math.round(o);return Math.abs(o-s)<=1e-9}function Wt(t,e,r,n){if(K(t)){if(nt(t,new Set(["param"]),e,n),typeof t.param!="string"||!t.param)return y(n,`${e}.param`,"must be a parameter name string"),null;if(!Object.prototype.hasOwnProperty.call(r,t.param))return y(n,`${e}.param`,`unknown parameter "${t.param}"`),null;let o=r[t.param];return!K(o)||![o.value,o.min,o.max,o.step].every(Ge)?(y(n,`${e}.param`,"referenced period parameter value min max and step must be positive integers"),null):{param:t.param}}return Ge(t)?t:(y(n,e,"must be a positive integer or {param}"),null)}function Xe(t,e,r,n){return K(t)?(nt(t,new Set(["param"]),e,n),typeof t.param!="string"||!t.param?(y(n,`${e}.param`,"must be a parameter name string"),null):Object.prototype.hasOwnProperty.call(r,t.param)?{param:t.param}:(y(n,`${e}.param`,`unknown parameter "${t.param}"`),null)):U(t)?t:(y(n,e,"must be a finite number or {param}"),null)}function Ft(t){switch(t.type){case"input":case"constant":return[];case"indicator":return t.input?[t.input]:[];case"compare":case"cross":return[t.left,t.right].filter(Boolean);case"logic":return Array.isArray(t.inputs)?[...t.inputs]:[];default:return[]}}function Xr(t){let e=new Map,r=n=>{let o=e.get(n)||0;if(o===1)return!0;if(o===2)return!1;e.set(n,1);let s=t.get(n);if(s){for(let a of Ft(s))if(t.has(a)&&r(a))return!0}return e.set(n,2),!1};for(let n of t.keys())if(r(n))return!0;return!1}function Zr(t,e){if(!K(t))return y(e,"parameters","must be a plain object"),null;let r=Object.keys(t);if(r.length>Ve)return y(e,"parameters",`too many parameters (max ${Ve})`),null;let n={};for(let o of r){if(!Ze.test(o)){y(e,`parameters.${o}`,"parameter name is malformed");continue}let s=t[o],a=`parameters.${o}`;if(!K(s)){y(e,a,"must be a plain object");continue}nt(s,Be,a,e);for(let c of Be)c in s||y(e,`${a}.${c}`,"required field is missing");let{value:i,min:l,max:f,step:u}=s;U(l)||y(e,`${a}.min`,"must be a finite number"),U(f)||y(e,`${a}.max`,"must be a finite number"),(!U(u)||u<=0)&&y(e,`${a}.step`,"must be a positive finite number"),U(i)||y(e,`${a}.value`,"must be a finite number"),U(l)&&U(f)&&l>f&&y(e,`${a}.min`,"min must be <= max"),U(i)&&U(l)&&U(f)&&U(u)&&u>0&&(Qe(i,l,f,u)||y(e,`${a}.value`,"value must lie on the declared min/max/step grid")),n[o]={value:i,min:l,max:f,step:u}}return n}function Wr(t,e,r,n){let o=`nodes.${e}`;if(!K(t))return y(n,o,"must be a plain object"),null;if(typeof t.type!="string"||!ze[t.type])return y(n,`${o}.type`,"unknown or missing node type"),nt(t,Gr,o,n),null;switch(nt(t,ze[t.type],o,n),(typeof t.id!="string"||!We.test(t.id))&&y(n,`${o}.id`,"must be a 1-64 char id starting with alphanumeric"),t.type){case"input":return(typeof t.field!="string"||!qr.has(t.field))&&y(n,`${o}.field`,"must be one of open|high|low|close|volume|fundingRate|openInterest"),{id:t.id,type:"input",field:t.field};case"constant":{let s=Xe(t.value,`${o}.value`,r,n);return{id:t.id,type:"constant",value:s}}case"indicator":{(typeof t.indicator!="string"||!Br.has(t.indicator))&&y(n,`${o}.indicator`,"must be one of sma|ema|rsi|macd|bollinger|atr");let s=t.indicator,a={id:t.id,type:"indicator",indicator:s};if(s==="atr"){"input"in t&&(typeof t.input!="string"||!t.input?y(n,`${o}.input`,"must be a node id string when provided"):a.input=t.input);let i=Wt(t.period,`${o}.period`,r,n);i!=null&&(a.period=i)}else typeof t.input!="string"||!t.input?y(n,`${o}.input`,"must be a node id string"):a.input=t.input;if((s==="sma"||s==="ema"||s==="rsi"||s==="bollinger"||s==="atr")&&("period"in t,s!=="macd"))if(!("period"in t))y(n,`${o}.period`,"required field is missing");else{let i=Wt(t.period,`${o}.period`,r,n);i!=null&&(a.period=i)}if(s==="macd"){for(let i of["fastPeriod","slowPeriod","signalPeriod"])if(!(i in t))y(n,`${o}.${i}`,"required field is missing");else{let l=Wt(t[i],`${o}.${i}`,r,n);l!=null&&(a[i]=l)}"output"in t?Jr.has(t.output)?a.output=t.output:y(n,`${o}.output`,"must be line|signal|histogram"):y(n,`${o}.output`,"required field is missing"),"period"in t&&y(n,`${o}.period`,"forbidden for macd"),"stdDev"in t&&y(n,`${o}.stdDev`,"forbidden for macd")}else if(s==="bollinger"){if(!("stdDev"in t))y(n,`${o}.stdDev`,"required field is missing");else{let i=Xe(t.stdDev,`${o}.stdDev`,r,n);i!=null&&(typeof i=="number"&&i<=0?y(n,`${o}.stdDev`,"must be > 0"):a.stdDev=i)}"output"in t?Kr.has(t.output)?a.output=t.output:y(n,`${o}.output`,"must be upper|middle|lower"):y(n,`${o}.output`,"required field is missing");for(let i of["fastPeriod","slowPeriod","signalPeriod"])i in t&&y(n,`${o}.${i}`,`forbidden for ${s}`)}else{"output"in t&&y(n,`${o}.output`,`forbidden for ${s}`),"stdDev"in t&&s!=="bollinger"&&y(n,`${o}.stdDev`,`forbidden for ${s}`);for(let i of["fastPeriod","slowPeriod","signalPeriod"])i in t&&y(n,`${o}.${i}`,`forbidden for ${s}`)}return a}case"compare":return(typeof t.op!="string"||!Dr.has(t.op))&&y(n,`${o}.op`,"must be gt|gte|lt|lte|eq"),(typeof t.left!="string"||!t.left)&&y(n,`${o}.left`,"must be a node id"),(typeof t.right!="string"||!t.right)&&y(n,`${o}.right`,"must be a node id"),{id:t.id,type:"compare",op:t.op,left:t.left,right:t.right};case"logic":{if((typeof t.op!="string"||!zr.has(t.op))&&y(n,`${o}.op`,"must be and|or|not"),!Array.isArray(t.inputs))return y(n,`${o}.inputs`,"must be an array of node ids"),{id:t.id,type:"logic",op:t.op,inputs:[]};let s=[];for(let a=0;a<t.inputs.length;a++){let i=t.inputs[a];typeof i!="string"||!i?y(n,`${o}.inputs.${a}`,"must be a node id"):s.push(i)}return t.op==="not"?s.length!==1&&y(n,`${o}.inputs`,"not requires exactly one input"):(t.op==="and"||t.op==="or")&&s.length<2&&y(n,`${o}.inputs`,`${t.op} requires at least two inputs`),{id:t.id,type:"logic",op:t.op,inputs:s}}case"cross":return(typeof t.direction!="string"||!Vr.has(t.direction))&&y(n,`${o}.direction`,"must be above|below"),(typeof t.left!="string"||!t.left)&&y(n,`${o}.left`,"must be a node id"),(typeof t.right!="string"||!t.right)&&y(n,`${o}.right`,"must be a node id"),{id:t.id,type:"cross",direction:t.direction,left:t.left,right:t.right};default:return null}}function Qr(t,e,r){if(!K(t))return y(e,"risk","must be a plain object"),null;nt(t,De,"risk",e);for(let d of De)(!(d in t)||t[d]==null)&&y(e,`risk.${d}`,"required field is missing");let n={},o=t.maxLeverage;!U(o)||o<=0||o>50?y(e,"risk.maxLeverage","must be a number > 0 and <= 50"):n.maxLeverage=o;let s=t.maxNotionalUsd;!U(s)||s<=0?y(e,"risk.maxNotionalUsd","must be a number > 0"):n.maxNotionalUsd=s;let a=t.positionSizePct;!U(a)||a<=0||a>100?y(e,"risk.positionSizePct","must be a number > 0 and <= 100"):n.positionSizePct=a;let i=t.stopLossPct;!U(i)||i<=0||i>100?y(e,"risk.stopLossPct","must be a number > 0 and <= 100"):n.stopLossPct=i;let l=t.takeProfitPct;!U(l)||l<=0||l>1e3?y(e,"risk.takeProfitPct","must be a number > 0 and <= 1000"):n.takeProfitPct=l;let f=t.cooldownBars;!Number.isInteger(f)||f<0||f>1e5?y(e,"risk.cooldownBars","must be an integer >= 0 and <= 100000"):n.cooldownBars=f;let u=t.maxDailyLossPct;!U(u)||u<=0||u>100?y(e,"risk.maxDailyLossPct","must be a number > 0 and <= 100"):n.maxDailyLossPct=u;let c=t.expiresAt;return!Number.isInteger(c)||c<=0?y(e,"risk.expiresAt","must be an integer epoch milliseconds > 0"):(r.nowMs!==void 0&&(Number.isInteger(r.nowMs)?c<=r.nowMs&&y(e,"risk.expiresAt","must be strictly greater than nowMs"):y(e,"nowMs","must be an integer epoch milliseconds")),n.expiresAt=c),n}function Ht(t,e={}){let r=[];if(!K(t))return y(r,"","strategy must be a plain object"),{ok:!1,errors:r,strategy:null};nt(t,qe,"",r);for(let u of qe)(!(u in t)||t[u]==null)&&y(r,u,"required field is missing");t.version!==Lt&&y(r,"version",`must be ${Lt}`),(typeof t.id!="string"||!Ze.test(t.id))&&y(r,"id","must match /^[a-z0-9][a-z0-9._-]{0,63}$/"),(typeof t.name!="string"||t.name.length===0||t.name.length>Ke)&&y(r,"name",`must be a non-empty string max ${Ke}`),t.venue!=="hyperliquid"&&y(r,"venue",'must be "hyperliquid"');let n=null;K(t.market)?(nt(t.market,Yr,"market",r),(typeof t.market.coin!="string"||t.market.coin.length===0||t.market.coin.length>Ye)&&y(r,"market.coin",`must be a non-empty string max ${Ye}`),(typeof t.market.interval!="string"||!Cr.has(t.market.interval))&&y(r,"market.interval","must be a supported interval"),n={coin:t.market.coin,interval:t.market.interval}):y(r,"market","must be a plain object");let o=Zr(t.parameters,r),s=null,a=new Map;if(!Array.isArray(t.nodes))y(r,"nodes","must be an array");else if(t.nodes.length>Je)y(r,"nodes",`too many nodes (max ${Je})`);else{s=[];for(let u=0;u<t.nodes.length;u++){let c=Wr(t.nodes[u],u,o||{},r);c&&(typeof c.id=="string"&&We.test(c.id)&&(a.has(c.id)?y(r,`nodes.${u}.id`,`duplicate node id "${c.id}"`):a.set(c.id,c)),s.push(c))}for(let u=0;u<s.length;u++){let c=s[u],d=`nodes.${u}`;for(let m of Ft(c))a.has(m)||y(r,d,`unknown node ref "${m}"`);if(c.type==="indicator"||c.type==="compare"||c.type==="cross")for(let m of Ft(c)){let p=a.get(m);p&&Xt.has(p.type)&&y(r,d,`node ref "${m}" must be numeric`)}if(c.type==="logic")for(let m of Ft(c)){let p=a.get(m);p&&!Xt.has(p.type)&&y(r,d,`node ref "${m}" must be boolean`)}}Xr(a)&&y(r,"nodes","graph contains a cycle")}let i=null;if(!K(t.rules))y(r,"rules","must be a plain object");else{nt(t.rules,Zt,"rules",r);for(let u of Zt)u in t.rules||y(r,`rules.${u}`,"required field is missing");i={};for(let u of Zt){let c=t.rules[u];if(c===null){i[u]=null;continue}if(typeof c!="string"||!c){y(r,`rules.${u}`,"must be a node id or null"),i[u]=c;continue}if(!a.has(c)){y(r,`rules.${u}`,`unknown node ref "${c}"`),i[u]=c;continue}let d=a.get(c);Xt.has(d.type)||y(r,`rules.${u}`,`rule must reference a boolean node, not ${d.type}`),i[u]=c}}let l=Qr(t.risk,r,e);if(r.length)return{ok:!1,errors:r,strategy:null};let f=Qt({version:Lt,id:t.id,name:t.name,venue:"hyperliquid",market:n,parameters:o,nodes:s,rules:i,risk:l});return{ok:!0,errors:[],strategy:f}}function z(t,e={}){let r=Ht(t,e);if(!r.ok)throw new ut(r.errors);return r.strategy}function tr(t){let e=z(t);return JSON.stringify(te(e))}function ft(t){let e=tr(t);return Ur("sha256").update(e,"utf8").digest("hex")}function mt(t,e){if(!K(t))throw new ut([{path:"",message:"strategy must be a plain object"}]);if(!K(e))throw new ut([{path:"overrides",message:"must be a plain object"}]);let r=z(t),n={};for(let[s,a]of Object.entries(r.parameters))n[s]={value:a.value,min:a.min,max:a.max,step:a.step};let o=[];for(let[s,a]of Object.entries(e)){if(!Object.prototype.hasOwnProperty.call(n,s)){y(o,s,`unknown parameter "${s}"`);continue}let i=n[s];if(!U(a)){y(o,s,"override must be a finite number");continue}if(!Qe(a,i.min,i.max,i.step)){y(o,s,"override must lie on the declared min/max/step grid");continue}n[s]={...i,value:a}}if(o.length)throw new ut(o);return z({version:r.version,id:r.id,name:r.name,venue:r.venue,market:{coin:r.market.coin,interval:r.market.interval},parameters:n,nodes:r.nodes.map(s=>{let a={...s};return Array.isArray(s.inputs)&&(a.inputs=[...s.inputs]),a}),rules:{...r.rules},risk:{...r.risk}})}function Pt(t){if(!Array.isArray(t))throw new TypeError("values must be an array");for(let e=0;e<t.length;e++){let r=t[e];if(typeof r!="number"||!Number.isFinite(r))throw new TypeError(`values[${e}] must be a finite number`)}}function ct(t){if(!Number.isInteger(t)||t<=0)throw new TypeError("period must be a positive integer")}function tn(t){if(!Array.isArray(t))throw new TypeError("bars must be an array");for(let e=0;e<t.length;e++){let r=t[e];if(r==null||typeof r!="object")throw new TypeError(`bars[${e}] must be an object`);for(let n of["o","h","l","c"]){let o=r[n];if(typeof o!="number"||!Number.isFinite(o))throw new TypeError(`bars[${e}].${n} must be a finite number`)}if(r.h<r.l)throw new TypeError(`bars[${e}] high must be >= low`)}}function ee(t,e){Pt(t),ct(e);let r=new Array(t.length).fill(null);if(t.length<e)return r;let n=0;for(let o=0;o<t.length;o++)n+=t[o],o>=e&&(n-=t[o-e]),o>=e-1&&(r[o]=n/e);return r}function At(t,e){Pt(t),ct(e);let r=new Array(t.length).fill(null);if(t.length<e)return r;let n=0;for(let a=0;a<e;a++)n+=t[a];let o=n/e;r[e-1]=o;let s=2/(e+1);for(let a=e;a<t.length;a++)o=s*t[a]+(1-s)*o,r[a]=o;return r}function re(t,e){Pt(t),ct(e);let r=new Array(t.length).fill(null);if(t.length<=e)return r;let n=0,o=0;for(let i=1;i<=e;i++){let l=t[i]-t[i-1];l>=0?n+=l:o-=l}let s=n/e,a=o/e;r[e]=er(s,a);for(let i=e+1;i<t.length;i++){let l=t[i]-t[i-1],f=l>0?l:0,u=l<0?-l:0;s=(s*(e-1)+f)/e,a=(a*(e-1)+u)/e,r[i]=er(s,a)}return r}function er(t,e){return t===0&&e===0?50:e===0?100:t===0?0:100-100/(1+t/e)}function ne(t,e={}){Pt(t);let r=e.fastPeriod,n=e.slowPeriod,o=e.signalPeriod;if(ct(r),ct(n),ct(o),r>=n)throw new TypeError("fastPeriod must be < slowPeriod");let s=At(t,r),a=At(t,n),i=new Array(t.length).fill(null);for(let c=0;c<t.length;c++)s[c]!=null&&a[c]!=null&&(i[c]=s[c]-a[c]);let l=new Array(t.length).fill(null),f=new Array(t.length).fill(null),u=[];for(let c=0;c<i.length;c++)i[c]!=null&&u.push(c);if(u.length>=o){let c=0;for(let b=0;b<o;b++)c+=i[u[b]];let d=c/o,m=u[o-1];l[m]=d,f[m]=i[m]-d;let p=2/(o+1);for(let b=o;b<u.length;b++){let E=u[b];d=p*i[E]+(1-p)*d,l[E]=d,f[E]=i[E]-d}}return{line:i,signal:l,histogram:f}}function oe(t,e={}){Pt(t);let r=e.period,n=e.stdDev;if(ct(r),typeof n!="number"||!Number.isFinite(n)||n<=0)throw new TypeError("stdDev must be a finite number > 0");let o=new Array(t.length).fill(null),s=new Array(t.length).fill(null),a=new Array(t.length).fill(null);if(t.length<r)return{upper:s,middle:o,lower:a};let i=0,l=0;for(let f=0;f<t.length;f++){let u=t[f];if(i+=u,l+=u*u,f>=r){let c=t[f-r];i-=c,l-=c*c}if(f>=r-1){let c=i/r,d=l/r-c*c;d<0&&d>-1e-12&&(d=0);let m=Math.sqrt(d);o[f]=c,s[f]=c+n*m,a[f]=c-n*m}}return{upper:s,middle:o,lower:a}}function ie(t,e){tn(t),ct(e);let r=new Array(t.length).fill(null);if(t.length<e)return r;let n=new Array(t.length);for(let a=0;a<t.length;a++){let i=t[a];if(a===0)n[a]=i.h-i.l;else{let l=t[a-1].c;n[a]=Math.max(i.h-i.l,Math.abs(i.h-l),Math.abs(i.l-l))}}let o=0;for(let a=0;a<e;a++)o+=n[a];let s=o/e;r[e-1]=s;for(let a=e;a<t.length;a++)s=(s*(e-1)+n[a])/e,r[a]=s;return r}import{createHash as en}from"node:crypto";var rn=new Set([408,425,429,500,502,503,504]),rr=new Set(["GET","HEAD","OPTIONS","PUT","DELETE"]),nn=2,nr=4e3,ir=["authorization","x-api-key","x-tensor-api-key","0x-api-key","api-key","cookie","proxy-authorization"],on=new Set([301,302,303,307,308]),or=3;function sn(t){let e=String(t||"").toLowerCase();return ir.includes(e)||/(^|-)(api[-_]?key|key|token|secret|auth|authorization|session|cookie|signature|access)(-|$)/.test(e)}function an(t){let e={};for(let[r,n]of Object.entries(t||{}))sn(r)||(e[r]=n);return e}function cn(t,e){if(!t)throw new Error(`HTTP redirect from ${e} missing Location`);let r=new URL(String(t),String(e));if(r.protocol!=="http:"&&r.protocol!=="https:")throw new Error(`HTTP redirect from ${e} used unsupported protocol ${r.protocol}`);return r.toString()}function ln(t,e){return new URL(String(t)).origin===new URL(String(e)).origin}var se=new Map;function un(t,e,r){let n={};for(let[l,f]of Object.entries(r||{}))n[l.toLowerCase()]=f;let o=Object.keys(n).filter(l=>/(^|-)(api[-_]?key|key|token|secret|auth|authorization|session|cookie|signature|access)(-|$)/.test(l)),a=[...new Set([...ir,...o])].sort().map(l=>`${l}=${n[l]??""}`).join(`
|
|
2
|
-
`),i=en("sha256").update(a).digest("hex").slice(0,32);return`${t} ${e} ${i}`}function fn(t,e){let r=Number(e);return Number.isFinite(r)&&r>0?Math.min(r*1e3,nr):Math.min(250*2**t,nr)+Math.floor(Math.random()*100)}var dn=t=>new Promise(e=>setTimeout(e,t));async function pn(t,{fetchImpl:e,method:r,headers:n,body:o,timeoutMs:s}){let a=new AbortController,i=setTimeout(()=>a.abort(),s);try{let l=String(t),f={...n||{}};for(let u=0;u<=or;u+=1){let c=await e(l,{method:r,headers:f,body:o,signal:a.signal,redirect:"manual"}),d=await c.text(),m=null;if(d)try{m=JSON.parse(d)}catch{m=null}if(on.has(c.status)){if(r!=="GET"&&r!=="HEAD"){let b=new Error(`HTTP ${c.status} ${r} ${l} redirected non-idempotent request`);throw b.status=c.status,b.body=m??d.slice(0,300),b}if(u===or)throw new Error(`HTTP redirect loop for ${t}`);let p=cn(c.headers?.get?.("location"),l);ln(p,l)||(f=an(f)),l=p;continue}if(!c.ok){let p=new Error(`HTTP ${c.status} ${r} ${l}`);throw p.status=c.status,p.retryAfter=c.headers?.get?.("retry-after")??null,p.body=m??d.slice(0,300),p}return m??d}throw new Error(`HTTP redirect loop for ${t}`)}finally{clearTimeout(i)}}async function sr(t,e={}){let r=e.fetchImpl||globalThis.fetch;if(typeof r!="function")throw new Error("fetch unavailable \u2014 pass opts.fetchImpl");let n=e.timeoutMs??12e3,o=(e.method||"GET").toUpperCase(),s={Accept:"application/json",...e.headers||{}},a=e.body;a!=null&&typeof a!="string"&&(s["Content-Type"]=s["Content-Type"]||"application/json",a=JSON.stringify(a));let i=Number.isInteger(e.retries)?Math.max(e.retries,0):nn,l=async()=>{let m;for(let p=0;p<=i;p++)try{return await pn(t,{fetchImpl:r,method:o,headers:s,body:a,timeoutMs:n})}catch(b){m=b;let E=b?.status,k=b?.name==="AbortError",P=new Set([408,425,429]),T=E!=null?P.has(E)||rn.has(E)&&rr.has(o):!k&&rr.has(o);if(p===i||!T)throw b;await dn(fn(p,b?.retryAfter))}throw m};if(!(e.dedupe!==!1&&o==="GET"&&a==null))return l();let u=un(o,t,s),c=se.get(u);if(c)return c;let d=l().finally(()=>se.delete(u));return se.set(u,d),d}var mn="https://api.hyperliquid.xyz/info",hn="https://api.hyperliquid-testnet.xyz/info";function yn({testnet:t,baseUrl:e}={}){if(e)return e.replace(/\/$/,"").endsWith("/info")?e:`${e.replace(/\/$/,"")}/info`;if(process.env.HL_INFO_URL)return process.env.HL_INFO_URL;if(process.env.HL_API_URL){let r=process.env.HL_API_URL.replace(/\/$/,"");return r.endsWith("/info")?r:`${r}/info`}return t||process.env.HL_TESTNET==="1"?hn:mn}async function ar(t,e={}){if(!t||!t.type)throw new Error("hlInfo requires body.type");let r=yn(e);return sr(r,{method:"POST",body:t,fetchImpl:e.fetchImpl,timeoutMs:e.timeoutMs})}async function cr({coin:t,interval:e="1h",startTime:r,endTime:n},o={}){if(!t)throw new Error("hlCandleSnapshot requires coin");let s={type:"candleSnapshot",req:{coin:String(t),interval:e,startTime:r??Date.now()-24*3600*1e3,endTime:n??Date.now()}};return ar(s,o)}async function lr({coin:t,startTime:e,endTime:r},n={}){if(!t)throw new Error("hlFundingHistory requires coin");return ar({type:"fundingHistory",coin:String(t),startTime:e??Date.now()-24*3600*1e3,endTime:r??Date.now()},n)}var wn=Object.freeze({"1m":6e4,"3m":18e4,"5m":3e5,"15m":9e5,"30m":18e5,"1h":36e5,"2h":72e5,"4h":144e5,"8h":288e5,"12h":432e5,"1d":864e5});function ht(t,e){let r=Number(t);if(!Number.isFinite(r))throw new TypeError(`${e} must be finite`);return r}function yt(t,e){if(!Number.isSafeInteger(t)||t<0)throw new TypeError(`${e} must be a non-negative safe integer`);return t}function $t(t){if(Array.isArray(t))for(let e of t)$t(e);else if(t&&typeof t=="object")for(let e of Object.values(t))$t(e);return Object.freeze(t)}function ur(t){let e=wn[t];if(!e)throw new TypeError(`unsupported strategy interval: ${t}`);return e}function fr(t,{endTime:e=Number.MAX_SAFE_INTEGER}={}){if(!Array.isArray(t))throw new TypeError("Hyperliquid candles must be an array");yt(e,"endTime");let r=new Map;for(let n=0;n<t.length;n++){let o=t[n];if(!o||typeof o!="object"||Array.isArray(o))throw new TypeError(`candles[${n}] must be an object`);let s=yt(Number(o.t),`candles[${n}].t`);if(yt(Number(o.T),`candles[${n}].T`)>e)continue;let i={t:s,o:ht(o.o,`candles[${n}].o`),h:ht(o.h,`candles[${n}].h`),l:ht(o.l,`candles[${n}].l`),c:ht(o.c,`candles[${n}].c`),v:ht(o.v,`candles[${n}].v`)};if(i.h<Math.max(i.o,i.c,i.l))throw new TypeError(`candles[${n}].h is below another price`);if(i.l>Math.min(i.o,i.c,i.h))throw new TypeError(`candles[${n}].l is above another price`);if(i.v<0)throw new TypeError(`candles[${n}].v must be non-negative`);let l=r.get(s);if(l&&JSON.stringify(l)!==JSON.stringify(i))throw new TypeError(`conflicting duplicate candle at timestamp ${s}`);l||r.set(s,i)}return $t([...r.values()].sort((n,o)=>n.t-o.t))}function gn(t,e){if(!Array.isArray(t))throw new TypeError("Hyperliquid funding history must be an array");let r=new Map;for(let n=0;n<t.length;n++){let o=t[n];if(!o||typeof o!="object"||Array.isArray(o))throw new TypeError(`funding[${n}] must be an object`);let s=yt(Number(o.time),`funding[${n}].time`);s>e||r.set(s,{time:s,fundingRate:ht(o.fundingRate,`funding[${n}].fundingRate`)})}return[...r.values()].sort((n,o)=>n.time-o.time)}async function bn({coin:t,startTime:e,endTime:r},n){let o=[],s=Math.max(0,e-288e5),a=!1;for(let i=0;i<16&&s<=r;i++){let l=await lr({coin:t,startTime:s,endTime:r},n);if(!Array.isArray(l))throw new TypeError("Hyperliquid funding history must be an array");o.push(...l);let f=s;for(let u of l){let c=Number(u?.time);Number.isSafeInteger(c)&&c>f&&(f=c)}if(l.length<500||f>=r){a=!0;break}if(f<=s)throw new Error("Hyperliquid funding history pagination did not advance");s=f+1}if(!a)throw new Error("strategy history exceeds the bounded funding history limit");return gn(o,r)}function En(t,e,r){let n=0,o=null;return t.map(s=>{let a=s.t+r,i=0;for(;n<e.length&&e[n].time<a;){let l=e[n];o=l.fundingRate,l.time>=s.t&&(i+=l.fundingRate),n+=1}return o==null?{...s,fundingPaymentRate:i}:{...s,fundingRate:o,fundingPaymentRate:i}})}async function ae(t,e={}){if(!t||typeof t!="object"||Array.isArray(t))throw new TypeError("strategy history input must be an object");let r=String(t.coin||"").trim().toUpperCase();if(!/^[A-Z0-9][A-Z0-9._-]{0,11}$/.test(r))throw new TypeError("coin must be an uppercase Hyperliquid asset id");let n=String(t.interval||"15m"),o=ur(n);if(t.endTime==null)throw new TypeError("endTime is required for deterministic history");let s=yt(t.endTime,"endTime"),a=t.count==null?1500:t.count;if(!Number.isInteger(a)||a<20||a>5e3)throw new TypeError("count must be an integer from 20 to 5000");let i=t.startTime==null?Math.max(0,s-a*o):yt(t.startTime,"startTime");if(i>=s)throw new TypeError("startTime must be before endTime");let[l,f]=await Promise.all([cr({coin:r,interval:n,startTime:i,endTime:s},e),bn({coin:r,startTime:i,endTime:s},e)]),u=fr(l,{endTime:s}),c=$t(En(u,f,o));return $t({coin:r,interval:n,range:{startTime:i,endTime:s},bars:c})}import{createHash as xn}from"node:crypto";var ce=2,Sn="93f9c998260e73756c13871dc1a84dd6cdb26ec6113bd1941f023519939db440",kn="68f5b412a9975f04024cf9b9b6209b4bccedd52f176e95f34f786b3f53479c6a",An="d2d75085cb3ab02724cd173abfa2bcad99cd7966a69bcaf80312f13e5b7aaa50",dr=xn("sha256").update(JSON.stringify({version:ce,compilerSourceHash:Sn,indicatorsSourceHash:kn,schemaSourceHash:An})).digest("hex");var gt=ce,lt=dr,Pn=Object.freeze({open:"o",high:"h",low:"l",close:"c",volume:"v",fundingRate:"fundingRate",openInterest:"openInterest"}),$n=Object.freeze({"1m":6e4,"3m":18e4,"5m":3e5,"15m":9e5,"30m":18e5,"1h":36e5,"2h":72e5,"4h":144e5,"8h":288e5,"12h":432e5,"1d":864e5});function Y(t){return typeof t=="number"&&Number.isFinite(t)}function Tn(t){let e=new Map(t.map(i=>[i.id,i])),r=i=>{switch(i.type){case"indicator":return i.input?[i.input]:[];case"compare":case"cross":return[i.left,i.right];case"logic":return[...i.inputs];default:return[]}},n=new Map,o=new Map;for(let i of t)n.set(i.id,0),o.set(i.id,[]);for(let i of t)for(let l of r(i))e.has(l)&&(o.get(l).push(i.id),n.set(i.id,n.get(i.id)+1));let s=[];for(let[i,l]of n)l===0&&s.push(i);let a=[];for(;s.length;){let i=s.shift();a.push(i);for(let l of o.get(i))n.set(l,n.get(l)-1),n.get(l)===0&&s.push(l)}if(a.length!==t.length)throw new Error("strategy graph is cyclic");return a}function wt(t,e){return t!=null&&typeof t=="object"&&"param"in t?e[t.param].value:t}function _n(t,e,r){if(!Array.isArray(t)||t.length===0)throw new TypeError("bars must be a non-empty array");if(!Number.isInteger(e)||e<0||e>=t.length)throw new RangeError("index out of range");let n=null;for(let o=0;o<=e;o++){let s=t[o];if(s==null||typeof s!="object"||Array.isArray(s))throw new TypeError(`bars[${o}] must be an object`);let{t:a,o:i,h:l,l:f,c:u,v:c}=s;if(!Y(a)&&(typeof a=="number"&&Number.isFinite(a)),!Number.isFinite(a))throw new TypeError(`bars[${o}].t must be finite`);for(let[d,m]of[["o",i],["h",l],["l",f],["c",u],["v",c]])if(!Y(m))throw new TypeError(`bars[${o}].${d} must be a finite number`);for(let[d,m]of[["o",i],["h",l],["l",f],["c",u]])if(!(m>0))throw new TypeError(`bars[${o}].${d} price must be positive`);if(c<0)throw new TypeError(`bars[${o}].v volume must be non-negative`);if(l<Math.max(i,u,f))throw new TypeError(`bars[${o}] high must be >= max(open, close, low)`);if(f>Math.min(i,u,l))throw new TypeError(`bars[${o}] low must be <= min(open, close, high)`);if(n!=null){if(!(a>n))throw new TypeError("bars t must be strictly increasing");if(a-n!==$n[r])throw new TypeError(`bars t must match ${r} cadence`)}if(n=a,"fundingRate"in s&&s.fundingRate!=null&&!Y(s.fundingRate))throw new TypeError(`bars[${o}].fundingRate must be finite when present`);if("openInterest"in s&&s.openInterest!=null){if(!Y(s.openInterest))throw new TypeError(`bars[${o}].openInterest must be finite when present`);if(s.openInterest<0)throw new TypeError(`bars[${o}].openInterest must be non-negative`)}}}function dt(t,e,r,n,o,s){if(s.has(t))return s.get(t);let a=e.get(t),i;switch(a.type){case"input":{let l=Pn[a.field];i=new Array(o+1);for(let f=0;f<=o;f++){let u=n[f][l];i[f]=u===void 0?null:u}break}case"constant":{let l=wt(a.value,r);i=new Array(o+1).fill(l);break}case"indicator":{let l=a.period!=null?wt(a.period,r):void 0;if(a.indicator==="atr"){let c=[];for(let d=0;d<=o;d++){let m=n[d];c.push({o:m.o,h:m.h,l:m.l,c:m.c})}i=ie(c,l);break}let u=dt(a.input,e,r,n,o,s).map((c,d)=>{if(c==null||!Y(c))throw new TypeError(`indicator input ${a.input} has non-finite value at ${d}`);return c});if(a.indicator==="sma")i=ee(u,l);else if(a.indicator==="ema")i=At(u,l);else if(a.indicator==="rsi")i=re(u,l);else if(a.indicator==="macd")i=ne(u,{fastPeriod:wt(a.fastPeriod,r),slowPeriod:wt(a.slowPeriod,r),signalPeriod:wt(a.signalPeriod,r)})[a.output];else if(a.indicator==="bollinger")i=oe(u,{period:l,stdDev:wt(a.stdDev,r)})[a.output];else throw new Error(`unsupported indicator ${a.indicator}`);break}case"compare":{let l=dt(a.left,e,r,n,o,s),f=dt(a.right,e,r,n,o,s);i=new Array(o+1);for(let u=0;u<=o;u++){let c=l[u],d=f[u];if(c==null||d==null||!Y(c)||!Y(d)){i[u]=!1;continue}switch(a.op){case"gt":i[u]=c>d;break;case"gte":i[u]=c>=d;break;case"lt":i[u]=c<d;break;case"lte":i[u]=c<=d;break;case"eq":i[u]=c===d;break;default:i[u]=!1}}break}case"logic":{let l=a.inputs.map(f=>dt(f,e,r,n,o,s));i=new Array(o+1);for(let f=0;f<=o;f++)if(a.op==="not"){let u=l[0][f];i[f]=u===!0?!1:u===!1}else if(a.op==="and"){let u=!0;for(let c of l)if(c[f]!==!0){u=!1;break}i[f]=u}else if(a.op==="or"){let u=!1;for(let c of l)if(c[f]===!0){u=!0;break}i[f]=u}else i[f]=!1;break}case"cross":{let l=dt(a.left,e,r,n,o,s),f=dt(a.right,e,r,n,o,s);i=new Array(o+1),i[0]=!1;for(let u=1;u<=o;u++){let c=l[u-1],d=f[u-1],m=l[u],p=f[u];if(c==null||d==null||m==null||p==null||!Y(c)||!Y(d)||!Y(m)||!Y(p)){i[u]=!1;continue}a.direction==="above"?i[u]=c<=d&&m>p:i[u]=c>=d&&m<p}break}default:throw new Error(`unknown node type ${a.type}`)}return s.set(t,i),i}function V(t,e={}){let r=z(t);e&&Object.keys(e).length&&(r=mt(r,e));let n=new Map(r.nodes.map(m=>[m.id,m])),o=Object.freeze(Tn(r.nodes)),s=r.parameters,a=new Set(r.nodes.filter(m=>m.type==="input").map(m=>m.field));if(r.nodes.some(m=>m.type==="indicator"&&m.indicator==="atr"))for(let m of["open","high","low","close"])a.add(m);let i=Object.freeze([...a].sort()),l=ft(r);function f(m,p){let b={};for(let k of o){let P=m.get(k);b[k]=P[p]}let E=k=>k!=null&&b[k]===!0;return{values:b,signals:{entryLong:E(r.rules.entryLong),entryShort:E(r.rules.entryShort),exitLong:E(r.rules.exitLong),exitShort:E(r.rules.exitShort)}}}function u(m,p){_n(m,p,r.market.interval);let b=new Map;for(let E of o)dt(E,n,s,m,p,b);return b}function c(m,p){return f(u(m,p),p)}function d(m){if(!Array.isArray(m)||m.length===0)throw new TypeError("bars must be a non-empty array");let p=m.length-1,b=u(m,p);return Object.freeze(m.map((E,k)=>Object.freeze(f(b,k))))}return Object.freeze({strategy:r,strategyHash:l,compilerVersion:gt,compilerHash:lt,requiredSeries:i,nodeOrder:o,evaluate:c,evaluateAll:d})}import{createHash as In}from"node:crypto";var On=Object.freeze({initialEquityUsd:1e4,takerFeeBps:3.5,builderFeeBps:2,slippageBps:2,latencyBars:1,maxVolumeParticipationPct:100});function Nn(t){return t!=null&&typeof t=="object"&&!Array.isArray(t)}function Q(t){return typeof t=="number"&&Number.isFinite(t)}function le(t){if(t==null||typeof t!="object"||Object.isFrozen(t))return t;if(Array.isArray(t)){for(let e of t)le(e);return Object.freeze(t)}for(let e of Object.keys(t))le(t[e]);return Object.freeze(t)}function jt(t){if(Array.isArray(t))return t.map(jt);if(t!=null&&typeof t=="object"){let e={};for(let r of Object.keys(t).sort())e[r]=jt(t[r]);return e}return t}function ue(t,e="result"){if(typeof t=="number"){if(!Number.isFinite(t))throw new Error(`non-finite backtest result at ${e}`);return}if(!(t==null||typeof t!="object")){if(Array.isArray(t)){t.forEach((r,n)=>ue(r,`${e}[${n}]`));return}for(let[r,n]of Object.entries(t))ue(n,`${e}.${r}`)}}function mr(t){if(!Array.isArray(t)||t.length===0)throw new TypeError("bars must be a non-empty array");let e=null;for(let r=0;r<t.length;r++){let n=t[r];if(n==null||typeof n!="object"||Array.isArray(n))throw new TypeError(`bars[${r}] must be an object`);let{t:o,o:s,h:a,l:i,c:l,v:f}=n;if(!Q(o))throw new TypeError(`bars[${r}].t must be finite`);for(let[u,c]of[["o",s],["h",a],["l",i],["c",l],["v",f]])if(!Q(c))throw new TypeError(`bars[${r}].${u} must be a finite number`);for(let[u,c]of[["o",s],["h",a],["l",i],["c",l]])if(!(c>0))throw new TypeError(`bars[${r}].${u} price must be positive`);if(f<0)throw new TypeError(`bars[${r}].v volume must be non-negative`);if(a<Math.max(s,l,i))throw new TypeError(`bars[${r}] high must be >= max(open, close, low)`);if(i>Math.min(s,l,a))throw new TypeError(`bars[${r}] low must be <= min(open, close, high)`);if(e!=null&&!(o>e))throw new TypeError("bars t must be strictly increasing");if(e=o,"fundingRate"in n&&n.fundingRate!=null&&!Q(n.fundingRate))throw new TypeError(`bars[${r}].fundingRate must be finite when present`);if("fundingPaymentRate"in n&&n.fundingPaymentRate!=null&&!Q(n.fundingPaymentRate))throw new TypeError(`bars[${r}].fundingPaymentRate must be finite when present`);if("openInterest"in n&&n.openInterest!=null&&!Q(n.openInterest))throw new TypeError(`bars[${r}].openInterest must be finite when present`)}}function Tt(t){if(!Array.isArray(t))throw new TypeError("bars must be an array");return t.length>0&&mr(t),In("sha256").update(JSON.stringify(jt(t)),"utf8").digest("hex")}function Rn(t){if(t==null&&(t={}),!Nn(t))throw new TypeError("options must be a plain object");let e={...On},r=new Set(["initialEquityUsd","takerFeeBps","builderFeeBps","slippageBps","latencyBars","maxVolumeParticipationPct","nowMs"]);for(let n of Object.keys(t))if(!r.has(n))throw new TypeError(`unknown option "${n}"`);if("initialEquityUsd"in t){if(!Q(t.initialEquityUsd)||t.initialEquityUsd<=0)throw new TypeError("initialEquityUsd must be a positive finite number");e.initialEquityUsd=t.initialEquityUsd}for(let n of["takerFeeBps","builderFeeBps","slippageBps"])if(n in t){let o=t[n];if(!Q(o)||o<0)throw new TypeError(`${n} must be a finite number >= 0`);if(n==="slippageBps"?o>=1e4:o>1e4)throw new TypeError(`${n} exceeds its safe basis-point bound`);e[n]=o}if("latencyBars"in t){let n=t.latencyBars;if(!Number.isInteger(n)||n<1)throw new TypeError("latencyBars must be an integer >= 1");e.latencyBars=n}if("maxVolumeParticipationPct"in t){let n=t.maxVolumeParticipationPct;if(!Q(n)||n<=0||n>100)throw new TypeError("maxVolumeParticipationPct must be in (0,100]");e.maxVolumeParticipationPct=n}if("nowMs"in t){if(!Number.isInteger(t.nowMs))throw new TypeError("nowMs must be an integer epoch milliseconds");e.nowMs=t.nowMs}return e}function Ut(t){return t/1e4}function Mn(t,e,r){let n=Ut(r);return e==="long"?t*(1+n):t*(1-n)}function vn(t,e,r){let n=Ut(r);return e==="long"?t*(1-n):t*(1+n)}function pr(t,e,r){let n=Math.abs(t)*Ut(e),o=Math.abs(t)*Ut(r);return{taker:n,builder:o,total:n+o}}function Ln(t,e){let r=t*(e.positionSizePct/100)*e.maxLeverage;return Math.min(r,e.maxNotionalUsd)}function Fn(t,e,r){let n=r/100;return e==="long"?t*(1-n):t*(1+n)}function Hn(t,e,r){let n=r/100;return e==="long"?t*(1+n):t*(1-n)}function jn(t,e,r,n){if(!(r>0))return e==="long"?0:Number.POSITIVE_INFINITY;let o=Math.min(.99,1/(2*r)+n/1e4);return e==="long"?t*(1-1/r)/(1-o):t*(1+1/r)/(1+o)}function Un(t,e,r){return e==="long"?t.l<=r:t.h>=r}function Cn(t,e,r){return e==="long"?t.h>=r:t.l<=r}function qn(t,e,r){return e==="long"?t.l<=r:t.h>=r}function _t(t,e){for(let r of t.requiredSeries)if(["fundingRate","openInterest"].includes(r)&&!e.every(n=>Q(n?.[r])))throw new Error(`strategy required series is absent: ${r}`)}function Bn(t){if(t.length<3)return 0;let e=[];for(let a=1;a<t.length;a++){let i=t[a-1].equity,l=t[a].equity;i>0&&e.push((l-i)/i)}if(e.length<2)return 0;let r=0;for(let a of e)r+=a;let n=r/e.length,o=0;for(let a of e)o+=(a-n)**2;let s=Math.sqrt(o/(e.length-1));return s>0?n/s:0}function Dn(t){let e=-1/0,r=0;for(let n of t){let o=n.equity;if(o>e&&(e=o),e>0){let s=(e-o)/e*100;s>r&&(r=s)}}return r}function ot(t,e,r={}){let n=Rn(r);mr(e);let o=Tt(e);n.nowMs!==void 0&&z(t,{nowMs:n.nowMs});let s=V(t);if(_t(s,e),n.nowMs!==void 0&&s.strategy.risk.expiresAt<=n.nowMs)throw new Error("strategy is expired: risk.expiresAt must be strictly greater than nowMs");let a=s.strategy.risk,i=s.evaluateAll(e),l=n.latencyBars,f=n.slippageBps,u=n.takerFeeBps,c=n.builderFeeBps,d=n.initialEquityUsd,m=0,p=0,b=0,E=0,k=0,P=[],T=[],x=[],v=[],_=[],w=null,J=0,st=null,at=d,X=!1,h=0,A=0,N=[],L=new Array(e.length).fill(0);function F(g){N.push(g)}function I(g){let $=e[g].v*(n.maxVolumeParticipationPct/100);return Math.max(0,$-L[g])}function R(g){if(!w)return d;let $=w.side==="long"?(g-w.entryPrice)*w.qty:(w.entryPrice-g)*w.qty;return d+$}function D(g,$,S){let H=S.o,O=Ln(d,a);if(!(O>0)||!(H>0))return;let j=Mn(H,$,f),M=O/j,q=I(g),St=j*q;if(M>q){x.push({kind:"entry",barIndex:g,time:S.t,side:$,requestedNotionalUsd:O,maxFillNotionalUsd:St,reason:"volume_participation"});return}L[g]+=M;let W=Math.abs(j-H)*M,rt=pr(O,u,c);d-=rt.total,m+=rt.taker,p+=rt.builder,E+=W,k+=O,w={lifecycleEpoch:++A,side:$,entryBarIndex:g,entryReferencePrice:H,entryPrice:j,qty:M,notionalUsd:O,stop:Fn(j,$,a.stopLossPct),take:Hn(j,$,a.takeProfitPct),liq:jn(j,$,a.maxLeverage,u+c),entryFees:rt.taker,entryBuilderFees:rt.builder,entrySlippageUsd:W,fundingAccrued:0}}function C(g,$,S,H){if(!w)return!1;let O=w.side,j=$,M=j;S!=="liquidation"&&H&&(M=vn(j,O,f));let q=w.qty,St=q*M;if(S!=="liquidation"){let Ce=I(g),jr=M*Ce;if(q>Ce)return x.push({kind:"exit",barIndex:g,time:e[g].t,side:O,requestedNotionalUsd:St,maxFillNotionalUsd:jr,reason:"volume_participation",triggerReason:S}),!1;L[g]+=q}let W=pr(St,u,c),rt=S==="liquidation"?0:Math.abs(M-j)*q,kt,Gt;O==="long"?(kt=(j-w.entryReferencePrice)*q,Gt=(M-w.entryPrice)*q):(kt=(w.entryReferencePrice-j)*q,Gt=(w.entryPrice-M)*q),d+=Gt,d-=W.total,m+=W.taker,p+=W.builder,E+=rt,k+=St;let Hr={side:O,entryBarIndex:w.entryBarIndex,entryTime:e[w.entryBarIndex].t,entryPrice:w.entryPrice,exitBarIndex:g,exitTime:e[g].t,exitPrice:M,exitReason:S,qty:q,notionalUsd:w.notionalUsd,grossPnlUsd:kt,feesUsd:w.entryFees+W.taker,builderFeesUsd:w.entryBuilderFees+W.builder,fundingUsd:w.fundingAccrued,slippageUsd:w.entrySlippageUsd+rt,netPnlUsd:kt-(w.entryFees+W.taker+w.entryBuilderFees+W.builder)-w.fundingAccrued-(w.entrySlippageUsd+rt)};return P.push(Hr),S==="liquidation"&&T.push({barIndex:g,time:e[g].t,side:O,price:M,tradeIndex:P.length-1}),w=null,A+=1,h=g+1+(a.cooldownBars||0),!0}function Z(g,$,S,H){let O=g+1;if(O>=e.length)return;N.some(M=>M.kind==="exit"&&M.side===$&&M.fillBar===O&&M.epoch===H)||F({kind:"exit",side:$,signalBar:g,fillBar:O,reason:S,epoch:H})}function Rt(g){let $=N.filter(S=>S.fillBar===g);for(let S=N.length-1;S>=0;S--)N[S].fillBar===g&&N.splice(S,1);$.sort((S,H)=>{let O=j=>j.kind==="exit"?0:1;return O(S)-O(H)});for(let S of $)if(S.kind==="exit"){if(!w||w.side!==S.side||w.lifecycleEpoch!==S.epoch)continue;let H=S.reason||"rule";C(g,e[g].o,H,!0)||Z(g,S.side,H,S.epoch)}else if(S.kind==="entry"){if(w||S.epoch!==A||g<h)continue;D(g,S.side,e[g])}}function Mt(g){if(!w)return;let $=e[g],S=$.fundingPaymentRate;if(S==null||!Q(S)||S===0)return;let H=Math.abs(w.qty*$.c),O=S*H;w.side==="long"?(d-=O,w.fundingAccrued+=O,b+=O):(d+=O,w.fundingAccrued-=O,b-=O)}function vt(g){if(!w)return;let $=e[g].o;(w.side==="long"?$<=w.liq:$>=w.liq)&&C(g,$,"liquidation",!1)}function Le(g){if(!w)return;let $=e[g],S=w.side,H=qn($,S,w.liq),O=Un($,S,w.stop),j=Cn($,S,w.take),M=q=>S==="long"?Math.min(q,$.o):Math.max(q,$.o);if(H){C(g,M(w.liq),"liquidation",!1);return}if(O&&j){C(g,M(w.stop),"stop_loss",!0)||Z(g,S,"stop_loss",w.lifecycleEpoch);return}if(O){C(g,M(w.stop),"stop_loss",!0)||Z(g,S,"stop_loss",w.lifecycleEpoch);return}j&&(C(g,w.take,"take_profit",!0)||Z(g,S,"take_profit",w.lifecycleEpoch))}for(let g=0;g<e.length;g++){let $=Math.floor(e[g].t/864e5);$!==st&&(st=$,at=R(e[g].o),X=!1),vt(g),Rt(g),Le(g),Mt(g),!X&&R(e[g].c)<=at*(1-a.maxDailyLossPct/100)&&(X=!0,v.push({type:"daily_loss_limit",barIndex:g,time:e[g].t,message:"daily loss limit reached; new entries blocked for UTC day"}));let S=i[g].signals,H=g+l;if(w)(w.side==="long"&&S.exitLong===!0||w.side==="short"&&S.exitShort===!0)&&H<e.length&&(N.some(q=>q.kind==="exit"&&q.side===w.side&&q.fillBar===H&&q.epoch===w.lifecycleEpoch)||F({kind:"exit",side:w.side,signalBar:g,fillBar:H,reason:"rule",epoch:w.lifecycleEpoch}));else{let j=S.entryLong===!0,M=S.entryShort===!0;j&&M?v.push({type:"conflicting_entry_signals",barIndex:g,time:e[g].t,message:"conflicting entry signals; no position opened"}):!X&&(j||M)&&H<e.length&&g>=h&&F({kind:"entry",side:j?"long":"short",signalBar:g,fillBar:H,epoch:A})}let O=d;w&&(J+=1,O=R(e[g].c)),_.push({barIndex:g,time:e[g].t,equity:O,positionSide:w?w.side:null})}let Fe=null;if(w){let g=e.length-1;if(C(g,e[g].c,"end_of_data",!0))_[g]={barIndex:g,time:e[g].t,equity:d,positionSide:null};else{let S=R(e[g].c);Fe={side:w.side,entryBarIndex:w.entryBarIndex,entryTime:e[w.entryBarIndex].t,entryPrice:w.entryPrice,qty:w.qty,notionalUsd:w.notionalUsd,markPrice:e[g].c,markedEquityUsd:S},v.push({type:"open_position_at_end",barIndex:g,time:e[g].t,message:"end-of-data exit missed; position remains open and marked"})}}let He=(w?R(e[e.length-1].c):d)-n.initialEquityUsd,Mr=He/n.initialEquityUsd*100,je=0,Vt=0,Jt=0;for(let g of P)g.netPnlUsd>0?(je+=1,Vt+=g.netPnlUsd):g.netPnlUsd<0&&(Jt+=-g.netPnlUsd);let Kt=P.length,vr=Kt===0?0:je/Kt,Yt=0;Jt>0?Yt=Vt/Jt:Vt>0&&(Yt=1e12);let Lr={netPnlUsd:He,netPnlPct:Mr,maxDrawdownPct:Dn(_),winRate:vr,profitFactor:Yt,tradeCount:Kt,sharpe:Bn(_),turnoverUsd:k,exposurePct:J/e.length*100},Fr={initialEquityUsd:n.initialEquityUsd,takerFeeBps:n.takerFeeBps,builderFeeBps:n.builderFeeBps,slippageBps:n.slippageBps,latencyBars:n.latencyBars,maxVolumeParticipationPct:n.maxVolumeParticipationPct},Ue={barsHash:o,strategyHash:s.strategyHash,compilerHash:lt,compilerVersion:gt,config:Fr,trades:P,equityCurve:_,metrics:Lr,costs:{feesUsd:m,builderFeesUsd:p,fundingUsd:b,slippageUsd:E},liquidations:T,missedFills:x,openPositionAtEnd:Fe,flags:v};return ue(Ue),le(jt(Ue))}import{createHash as zn}from"node:crypto";var de=256,hr=new Set(["netPnlUsd","profitFactor","sharpe","maxDrawdownPct"]);function yr(t){let e=String(t).toLowerCase();return e==="holdout"||e==="test"||e==="oos"||e.includes("holdout")||e.includes("oos")?!0:e==="testbars"||e.endsWith("testbars")||e.startsWith("test")||e.endsWith("test")?!e.includes("backtest"):!1}function wr(t){return t!=null&&typeof t=="object"&&!Array.isArray(t)}function pe(t){if(t==null||typeof t!="object"||Object.isFrozen(t))return t;if(Array.isArray(t)){for(let e of t)pe(e);return Object.freeze(t)}for(let e of Object.keys(t))pe(t[e]);return Object.freeze(t)}function It(t){if(Array.isArray(t))return t.map(It);if(t!=null&&typeof t=="object"){let e={};for(let r of Object.keys(t).sort())e[r]=It(t[r]);return e}return t}function Ct(t){return JSON.stringify(It(t))}function Vn(t){let{min:e,max:r,step:n}=t;if(!(n>0)||e>r)return[];let o=[],s=Math.floor((r-e)/n+1e-9);for(let i=0;i<=s;i++){let l=e+i*n;if(l>r+1e-9)break;let f=Math.abs(l-Math.round(l))<1e-9?Math.round(l):Number(l.toPrecision(12));f<e-1e-9||f>r+1e-9||o.push(f)}if(o.length===0)return[];let a=o[o.length-1];if(Math.abs(a-r)>1e-9){let i=(r-e)/n;Math.abs(i-Math.round(i))<=1e-9&&o.push(r)}return o}function Jn(t,e){if(t.length===0)return[];let r=[{}];for(let n of t){let o=e[n],s=[];for(let a of r)for(let i of o)s.push({...a,[n]:i});r=s}return r.sort((n,o)=>{let s=Ct(n),a=Ct(o);return s<a?-1:s>a?1:0}),r}function fe(t,e,r,n,o){let s=0;if(t==="maxDrawdownPct"){if(s=e.maxDrawdownPct-n.maxDrawdownPct,s!==0)return s<0}else if(s=e[t]-n[t],s!==0)return s>0;let a=Ct(r),i=Ct(o);return a<i}function me(t,e,r={}){if(!wr(r))throw new TypeError("options must be a plain object");for(let x of Object.keys(r))if(yr(x))throw new TypeError(`options must not include holdout/test/oos key "${x}"`);let n=new Set(["maxTrials","objective","backtestOptions"]);for(let x of Object.keys(r))if(!n.has(x))throw new TypeError(`unknown option "${x}"`);let o=64;if("maxTrials"in r){let x=r.maxTrials;if(!Number.isInteger(x)||x<1||x>de)throw new TypeError(`maxTrials must be an integer 1..${de}`);o=x}let s=r.objective??"netPnlUsd";if(!hr.has(s))throw new TypeError(`objective must be one of ${[...hr].join("|")}`);let a=r.backtestOptions??{};if(!wr(a))throw new TypeError("backtestOptions must be a plain object");for(let x of Object.keys(a))if(yr(x))throw new TypeError(`backtestOptions must not include holdout/test/oos key "${x}"`);let i=z(t),l=Object.keys(i.parameters).sort();if(l.length===0)throw new Error("optimizeStrategy requires at least one declared parameter");let f={},u=1;for(let x of l){let v=Vn(i.parameters[x]);if(v.length===0)throw new Error(`parameter "${x}" produces an empty grid`);f[x]=v,u*=v.length}let c=Jn(l,f);if(c.length===0)throw new Error("parameter grid is empty");let d=c.slice(0,o),m=[],p=null,b=null;for(let x of d){let v=mt(i,x),_=ot(v,e,a);b==null&&(b=_.config);let w={netPnlUsd:_.metrics.netPnlUsd,netPnlPct:_.metrics.netPnlPct,maxDrawdownPct:_.metrics.maxDrawdownPct,winRate:_.metrics.winRate,profitFactor:_.metrics.profitFactor,tradeCount:_.metrics.tradeCount,sharpe:_.metrics.sharpe,turnoverUsd:_.metrics.turnoverUsd},J={parameters:{...x},metrics:w};m.push(J),(!p||fe(s,w,x,p.metrics,p.parameters))&&(p=J)}m.sort((x,v)=>fe(s,x.metrics,x.parameters,v.metrics,v.parameters)?-1:fe(s,v.metrics,v.parameters,x.metrics,x.parameters)?1:0);let E={...p.parameters},k=mt(i,E),P={strategyHash:ft(i),compilerHash:lt,trainBarsHash:Tt(e),backtestConfig:b,objective:s,maxTrials:o,trialsRun:m.length,candidates:m,bestParameters:E,bestStrategy:k},T={id:zn("sha256").update(JSON.stringify(It(P)),"utf8").digest("hex"),...P};return pe(It(JSON.parse(JSON.stringify(T))))}import{createHash as Kn}from"node:crypto";var Er=Object.freeze(["fail","pass_paper_only","pass_live_eligible"]);function tt(t){return t!=null&&typeof t=="object"&&!Array.isArray(t)}function bt(t){return typeof t=="number"&&Number.isFinite(t)}function we(t){if(t==null||typeof t!="object"||Object.isFrozen(t))return t;if(Array.isArray(t)){for(let e of t)we(e);return Object.freeze(t)}for(let e of Object.keys(t))we(t[e]);return Object.freeze(t)}function pt(t){if(Array.isArray(t))return t.map(pt);if(t!=null&&typeof t=="object"){let e={};for(let r of Object.keys(t).sort())e[r]=pt(t[r]);return e}return t}function he(t){return{netPnlUsd:t.metrics.netPnlUsd,netPnlPct:t.metrics.netPnlPct,maxDrawdownPct:t.metrics.maxDrawdownPct,winRate:t.metrics.winRate,profitFactor:t.metrics.profitFactor,tradeCount:t.metrics.tradeCount,sharpe:t.metrics.sharpe,turnoverUsd:t.metrics.turnoverUsd,exposurePct:t.metrics.exposurePct}}function gr(t){return{feesUsd:t.costs.feesUsd,builderFeesUsd:t.costs.builderFeesUsd,fundingUsd:t.costs.fundingUsd,slippageUsd:t.costs.slippageUsd}}function Yn(t){return(t.feesUsd||0)+(t.builderFeesUsd||0)+Math.max(0,t.fundingUsd||0)+(t.slippageUsd||0)}function Gn(t){let e=0;for(let r of t.trades)r.grossPnlUsd>0&&(e+=r.grossPnlUsd);return e}function Xn(t,e,r){return!(!t||t.tradeCount<Math.max(1,Math.min(r,3))||!(t.netPnlUsd>0)||!(t.profitFactor>1)||!(t.maxDrawdownPct<=e.maxDailyLossPct))}function xr(t){if(!tt(t))throw new TypeError("evidence artifact must be a plain object");if(!Er.includes(t.status))throw new TypeError("evidence artifact status is invalid");for(let e of["strategyHash","compilerHash","barsHash"])if(typeof t[e]!="string"||!/^[0-9a-f]{64}$/.test(t[e]))throw new TypeError(`evidence artifact ${e} must be a sha256 hash`);if(!tt(t.split))throw new TypeError("evidence artifact split required");if(!Array.isArray(t.flags))throw new TypeError("evidence artifact flags must be an array");if(!tt(t.walkForward)||!Array.isArray(t.walkForward.windows))throw new TypeError("evidence artifact walkForward windows required");if(t.status==="pass_live_eligible"&&(!tt(t.train)||!tt(t.holdout)))throw new TypeError("live-eligible evidence artifact requires train and holdout facts");return pt({barsHash:t.barsHash,compilerHash:t.compilerHash,flags:t.flags.map(e=>pt(e)),holdout:t.holdout??null,split:t.split,status:t.status,strategyHash:t.strategyHash,train:t.train??null,walkForward:{passRate:t.walkForward.passRate,windowsRun:t.walkForward.windowsRun??t.walkForward.windows.length,windows:t.walkForward.windows.map(e=>({index:e.index,trainStartIndex:e.trainStartIndex,trainEndIndex:e.trainEndIndex,passed:e.passed,reason:e.reason,evalStartIndex:e.evalStartIndex,evalEndIndex:e.evalEndIndex,metrics:e.metrics}))}})}function ge(t){return Kn("sha256").update(JSON.stringify(xr(t)),"utf8").digest("hex")}function G(t,e,r=0){if(!Number.isInteger(t)||t<r)throw new Error(`${e} must be an integer >= ${r}`);return t}function ye(t,e){if(!tt(t)||!tt(t.metrics))throw new Error(`${e} metrics required`);let r=G(t.tradeCount,`${e}.tradeCount`);if(t.metrics.tradeCount!==r)throw new Error(`${e} tradeCount contradicts metrics.tradeCount`);for(let n of["netPnlUsd","profitFactor","maxDrawdownPct"])if(!bt(t.metrics[n]))throw new Error(`${e}.metrics.${n} must be finite`);if(t.metrics.maxDrawdownPct<0)throw new Error(`${e}.metrics.maxDrawdownPct must be >= 0`);return t.metrics}function Zn(t,{maxDailyLossPct:e}={}){let r=t.split,n=G(r.trainStartIndex,"split.trainStartIndex"),o=G(r.trainEndIndex,"split.trainEndIndex"),s=G(r.trainBarCount,"split.trainBarCount",1),a=G(r.holdoutStartIndex,"split.holdoutStartIndex"),i=G(r.holdoutEndIndex,"split.holdoutEndIndex"),l=G(r.holdoutBarCount,"split.holdoutBarCount",1);if(n!==0||o-n+1!==s||a!==o+1||i-a+1!==l)throw new Error("evidence split must be contiguous, chronological, and count-consistent");if(!bt(r.trainFraction)||r.trainFraction<=0||r.trainFraction>=1)throw new Error("split.trainFraction must be in (0,1)");ye(t.train,"train");let f=ye(t.holdout,"holdout");if(f.tradeCount<1||f.netPnlUsd<=0||f.profitFactor<=1)throw new Error("live-eligible holdout metrics do not pass");if(bt(e)&&f.maxDrawdownPct>e)throw new Error("live-eligible holdout drawdown exceeds strategy risk");if(t.flags.length>0)throw new Error("live-eligible evidence cannot contain failure flags");let u=t.walkForward,c=u.windows;if(c.length<1||u.windowsRun!==c.length)throw new Error("live-eligible walk-forward windowsRun must match nonempty windows");let d=0,m=-1;for(let b=0;b<c.length;b++){let E=c[b];if(!tt(E)||E.index!==b||typeof E.passed!="boolean")throw new Error("walk-forward windows must be ordered and structurally valid");let k=G(E.trainStartIndex,`walkForward.windows[${b}].trainStartIndex`),P=G(E.trainEndIndex,`walkForward.windows[${b}].trainEndIndex`),T=G(E.evalStartIndex,`walkForward.windows[${b}].evalStartIndex`),x=G(E.evalEndIndex,`walkForward.windows[${b}].evalEndIndex`);if(k!==0||P>=T||T>x||T<=m)throw new Error("walk-forward windows must be chronological and disjoint");if(m=x,E.passed){if(d+=1,E.reason!=="pass")throw new Error("passed walk-forward window reason must be pass");let v=ye({metrics:E.metrics,tradeCount:E.metrics?.tradeCount},`walkForward.windows[${b}]`);if(v.tradeCount<1||v.netPnlUsd<=0||v.profitFactor<=1)throw new Error("passed walk-forward window metrics do not pass");if(bt(e)&&v.maxDrawdownPct>e)throw new Error("passed walk-forward drawdown exceeds strategy risk")}else if(E.reason==="pass")throw new Error("failed walk-forward window cannot have pass reason")}let p=d/c.length;if(!bt(u.passRate)||Math.abs(u.passRate-p)>Number.EPSILON||p<.6)throw new Error("walk-forward passRate contradicts concrete windows or is below live threshold")}function be(t,e={}){if(typeof t?.id!="string"||!/^[0-9a-f]{64}$/.test(t.id))throw new Error("evidence.id must be a sha256 evidence artifact hash");let r=xr(t),n=pt({id:t.id,...r});if(JSON.stringify(pt(t))!==JSON.stringify(n))throw new Error("evidence artifact contains fields unbound from its deterministic identity");let o=ge(t);if(t.id!==o)throw new Error("evidence.id does not match its deterministic artifact facts digest");return t.status==="pass_live_eligible"&&Zn(t,e),t}function qt({strategy:t,bars:e,backtestOptions:r={},trainFraction:n=.7,walkForwardWindows:o=3,minTrades:s=10}={}){let a=[];if(!tt(t))throw new TypeError("strategy must be a plain object");if(!Array.isArray(e))throw new TypeError("bars must be an array");if(!tt(r))throw new TypeError("backtestOptions must be a plain object");if(!bt(n)||n<=0||n>=1)throw new TypeError("trainFraction must be in (0,1)");if(!Number.isInteger(o)||o<1)throw new TypeError("walkForwardWindows must be a positive integer");if(!Number.isInteger(s)||s<0)throw new TypeError("minTrades must be an integer >= 0");let i=z(t),l=ft(i),f=Tt(e),u=e.length,c=(I=[],R={})=>br({status:"fail",strategy:i,sHash:l,barsHash:f,split:R.split||{trainStartIndex:0,trainEndIndex:-1,trainBarCount:0,holdoutStartIndex:0,holdoutEndIndex:-1,holdoutBarCount:0,trainFraction:n},train:R.train||null,holdout:R.holdout||null,walkForward:R.walkForward||{windows:[],passRate:0},flags:[...a,...I]});if(u<10)return c([{type:"insufficient_bars",message:"need at least 10 bars for evidence"}]);let d=Math.floor(u*n),m=d,p=u-m;if(d<5||p<3)return c([{type:"invalid_windowing",message:"train/holdout split too small",trainCount:d,holdoutCount:p}]);let b=e.slice(0,d),E=e.slice(m),k,P;try{k=ot(i,b,r),P=ot(i,E,r)}catch(I){return c([{type:"backtest_error",message:String(I.message||I)}])}let T={metrics:he(k),costs:gr(k),tradeCount:k.metrics.tradeCount},x={metrics:he(P),costs:gr(P),tradeCount:P.metrics.tradeCount},v={trainStartIndex:0,trainEndIndex:d-1,trainBarCount:d,holdoutStartIndex:m,holdoutEndIndex:u-1,holdoutBarCount:p,trainFraction:n},_=[],w=Math.floor(u/(o+1));if(w<3)a.push({type:"invalid_windowing",message:"walk-forward window size too small",wfSize:w});else for(let I=0;I<o;I++){let R=w*(I+1),D=R,C=Math.min(u,D+w);if(C-D<3||R<3){_.push({index:I,trainStartIndex:0,trainEndIndex:R-1,evalStartIndex:D,evalEndIndex:C-1,passed:!1,reason:"window_too_small",metrics:null});continue}let Z=e.slice(D,C),Rt;try{Rt=ot(i,Z,r)}catch{_.push({index:I,trainStartIndex:0,trainEndIndex:R-1,evalStartIndex:D,evalEndIndex:C-1,passed:!1,reason:"backtest_error",metrics:null});continue}let Mt=he(Rt),vt=Xn(Mt,i.risk,s);_.push({index:I,trainStartIndex:0,trainEndIndex:R-1,evalStartIndex:D,evalEndIndex:C-1,passed:vt,reason:vt?"pass":"fail_metrics",metrics:Mt})}let J=_.filter(I=>I.passed).length,st=_.length===0?0:J/_.length,at={windows:_,passRate:st,windowsRun:_.length};x.metrics.tradeCount===0&&T.metrics.tradeCount===0&&a.push({type:"no_trades",message:"no trades in train or holdout"}),x.metrics.tradeCount===0&&a.push({type:"absent_oos_trades",message:"holdout produced no trades"}),x.metrics.netPnlUsd<0&&a.push({type:"holdout_loss",message:"holdout netPnlUsd is negative"}),T.metrics.netPnlUsd>0&&x.metrics.netPnlUsd<0&&T.metrics.netPnlUsd>Math.abs(x.metrics.netPnlUsd)*.25?a.push({type:"overfit",message:"profitable train with losing holdout (OOS collapse)"}):T.metrics.netPnlUsd>0&&x.metrics.netPnlUsd>0&&x.metrics.netPnlUsd<T.metrics.netPnlUsd*.1&&T.metrics.tradeCount>=s&&a.push({type:"severe_oos_collapse",message:"holdout pnl collapsed vs train"});let X=Yn(x.costs),h=Gn(P);h>0&&X>h&&a.push({type:"costs_exceed_gross",message:"holdout costs exceed gross gains"});let A=a.some(I=>["insufficient_bars","invalid_windowing","absent_oos_trades","holdout_loss","no_trades","severe_oos_collapse","backtest_error"].includes(I.type)),N=a.some(I=>I.type==="overfit"),L="fail";return x.metrics.tradeCount>=s&&x.metrics.netPnlUsd>0&&x.metrics.profitFactor>1&&x.metrics.maxDrawdownPct<=i.risk.maxDailyLossPct&&st>=.6&&!(h>0&&X>h)&&!N&&!a.some(I=>I.type==="severe_oos_collapse"||I.type==="invalid_windowing")?L="pass_live_eligible":!A&&x.metrics.tradeCount>0&&T.metrics.tradeCount>0&&Number.isFinite(x.metrics.netPnlUsd)?(L="pass_paper_only",x.metrics.tradeCount<s&&a.push({type:"undersampled",message:"holdout trades below minTrades"})):N&&x.metrics.tradeCount>0&&T.metrics.netPnlUsd>0?L="fail":x.metrics.tradeCount>0&&T.metrics.tradeCount>0&&!A?L="pass_paper_only":L="fail",x.metrics.netPnlUsd<0&&(L=L==="pass_live_eligible"?"fail":L==="pass_paper_only"?"pass_paper_only":"fail",L="fail"),br({status:L,strategy:i,sHash:l,barsHash:f,split:v,train:T,holdout:x,walkForward:at,flags:a})}function br({status:t,strategy:e,sHash:r,barsHash:n,split:o,train:s,holdout:a,walkForward:i,flags:l}){let f={status:t,strategyHash:r,compilerHash:lt,barsHash:n,split:o,train:s,holdout:a,walkForward:i,flags:l};return f.id=ge(f),we(pt(JSON.parse(JSON.stringify(f))))}import{createHash as Wn}from"node:crypto";var B=class extends Error{constructor(e){super(e),this.name="StrategyDraftError"}},Qn=new Set(["1m","3m","5m","15m","30m","1h","2h","4h","8h","12h","1d"]),to=new Set(["BTC","ETH","SOL","HYPE"]),eo=/\b(mnemonic|private\s*key|privkey|seed\s*phrase|seed\b|api[_-]?key|secret\s*key|wallet\s*key|passphrase)\b/i,Et="Supported grammar: (1) EMA cross: <long|short> <COIN> when EMA <n> crosses <above|below> EMA <m> [on <interval>] [, exit on reverse] [, risk knobs]; (2) RSI: <long|short> <COIN> when RSI <n> <above|below> <threshold> [on <interval>] [, risk knobs]; (3) funding: <long|short> <COIN> when funding rate <above|below> <decimal|percent%> [on <interval>] [, risk knobs]. Risk knobs: stop loss %, take profit %, leverage, position size %, max notional USD. Coins: BTC ETH SOL HYPE or uppercase token <=12 chars. Intervals: 1m 3m 5m 15m 30m 1h 2h 4h 8h 12h 1d.",ro=Object.freeze({maxLeverage:1,maxNotionalUsd:100,positionSizePct:5,stopLossPct:2,takeProfitPct:4,cooldownBars:1,maxDailyLossPct:3}),no=36e5,oo=30*864e5,io=864e5;function so(t){return t!=null&&typeof t=="object"&&!Array.isArray(t)}function Ee(t){if(t==null||typeof t!="object"||Object.isFrozen(t))return t;if(Array.isArray(t)){for(let e of t)Ee(e);return Object.freeze(t)}for(let e of Object.keys(t))Ee(t[e]);return Object.freeze(t)}function ao(t){return String(t).trim().replace(/\s+/g," ")}function co(t){let e=t.match(/\b(long|short)\b/i);return e?e[1].toLowerCase():null}function lo(t){for(let r of to)if(new RegExp(`\\b${r}\\b`,"i").test(t))return r;let e=t.match(/\b([A-Z][A-Z0-9]{1,11})\b/);return e?e[1]:null}function uo(t){let e=t.match(/\bon\s+(1m|3m|5m|15m|30m|1h|2h|4h|8h|12h|1d)\b/i);if(!e)return"15m";let r=e[1].toLowerCase();return Qn.has(r)?r:null}function fo(t){let e={...ro},r=t.match(/stop\s*loss\s*(\d+(?:\.\d+)?)\s*%?/i);r&&(e.stopLossPct=Number(r[1]));let n=t.match(/take\s*profit\s*(\d+(?:\.\d+)?)\s*%?/i);n&&(e.takeProfitPct=Number(n[1]));let o=t.match(/leverage\s*(\d+(?:\.\d+)?)/i);o&&(e.maxLeverage=Number(o[1]));let s=t.match(/position\s*size\s*(\d+(?:\.\d+)?)\s*%?/i);s&&(e.positionSizePct=Number(s[1]));let a=t.match(/max\s*notional\s*(?:usd\s*)?\$?\s*(\d+(?:\.\d+)?)/i);return a&&(e.maxNotionalUsd=Number(a[1])),e}function po(t){return[/\bon\s+(?:1m|3m|5m|15m|30m|1h|2h|4h|8h|12h|1d)\b/gi,/\bstop\s*loss\s*\d+(?:\.\d+)?\s*%?/gi,/\btake\s*profit\s*\d+(?:\.\d+)?\s*%?/gi,/\bleverage\s*\d+(?:\.\d+)?/gi,/\bposition\s*size\s*\d+(?:\.\d+)?\s*%?/gi,/\bmax\s*notional\s*(?:usd\s*)?\$?\s*\d+(?:\.\d+)?/gi].some(r=>(t.match(r)??[]).length>1)}function mo(t){let e=Wn("sha256").update(t,"utf8").digest("hex").slice(0,10),r=t.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40),o=`${r.length?r:"draft"}-${e}`.replace(/[^a-z0-9._-]/g,"").slice(0,64);return/^[a-z0-9]/.test(o)?o:`d${o}`.slice(0,64)}function ho(t){let e=t.match(/\bema\s*(\d+)\s*cross(?:es)?\s*(above|below)\s*ema\s*(\d+)\b/i);if(!e)return null;let r=Number(e[1]),n=e[2].toLowerCase(),o=Number(e[3]);if(!(r>0)||!(o>0)||r===o)return null;let s=/\b(exit\s+on\s+reverse|reverse\s+exit)\b/i.test(t);return{kind:"ema_cross",fast:r,slow:o,direction:n,reverse:s}}function yo(t){let e=t.match(/\brsi\s*(\d+)\s*(above|below)\s*(-?\d+(?:\.\d+)?)/i);if(!e)return null;let r=Number(e[3]);return!Number.isFinite(r)||r<=0||r>=100?null:{kind:"rsi",period:Number(e[1]),op:e[2].toLowerCase(),threshold:r}}function wo(t){let e=t.match(/\bfunding\s*rate\s*(above|below)\s*(-?\d+(?:\.\d+)?)(%?)(?=\s|,|$)/i);if(!e)return null;let r=Number(e[2]);return e[3]==="%"&&(r=r/100),{kind:"funding",op:e[1].toLowerCase(),threshold:r}}function go(t,e){let r=t.replace(/^(long|short)\s+[A-Z][A-Z0-9]{1,11}\s+when\s+/i,"");if(r===t)return!1;e.kind==="ema_cross"?r=r.replace(/^ema\s*\d+\s*cross(?:es)?\s*(?:above|below)\s*ema\s*\d+\b/i,""):e.kind==="rsi"?r=r.replace(/^rsi\s*\d+\s*(?:above|below)\s*\d+(?:\.\d+)?\b/i,""):r=r.replace(/^funding\s*rate\s*(?:above|below)\s*-?\d+(?:\.\d+)?%?(?=\s|,|$)/i,"");let n=[/(?:,\s*)?\bon\s+(?:1m|3m|5m|15m|30m|1h|2h|4h|8h|12h|1d)\b/gi,/(?:,\s*)?\bstop\s*loss\s*\d+(?:\.\d+)?\s*%?/gi,/(?:,\s*)?\btake\s*profit\s*\d+(?:\.\d+)?\s*%?/gi,/(?:,\s*)?\bleverage\s*\d+(?:\.\d+)?/gi,/(?:,\s*)?\bposition\s*size\s*\d+(?:\.\d+)?\s*%?/gi,/(?:,\s*)?\bmax\s*notional\s*(?:usd\s*)?\$?\s*\d+(?:\.\d+)?/gi];e.kind==="ema_cross"&&n.push(/(?:,\s*)?\b(?:exit\s+on\s+reverse|reverse\s+exit)\b/gi);for(let o of n)r=r.replace(o,"");return r.replace(/[\s,;]+/g,"")===""}function Sr(t){let e=Math.max(2,Math.floor(t/2));return{value:t,min:Math.max(2,t-e),max:Math.min(500,t+e),step:1}}function bo({side:t,coin:e,interval:r,risk:n,expiresAt:o,id:s,name:a,spec:i}){let l=[{id:"c",type:"input",field:"close"},{id:"emaLeft",type:"indicator",indicator:"ema",input:"c",period:{param:"ema_left_period"}},{id:"emaRight",type:"indicator",indicator:"ema",input:"c",period:{param:"ema_right_period"}},{id:"cross",type:"cross",direction:i.direction,left:"emaLeft",right:"emaRight"}],f={entryLong:null,entryShort:null,exitLong:null,exitShort:null};if(t==="long"?f.entryLong="cross":f.entryShort="cross",i.reverse){let u=i.direction==="above"?"below":"above";l.push({id:"crossRev",type:"cross",direction:u,left:"emaLeft",right:"emaRight"}),t==="long"?f.exitLong="crossRev":f.exitShort="crossRev"}return{version:1,id:s,name:a,venue:"hyperliquid",market:{coin:e,interval:r},parameters:{ema_left_period:Sr(i.fast),ema_right_period:Sr(i.slow)},nodes:l,rules:f,risk:{...n,expiresAt:o}}}function Eo({side:t,coin:e,interval:r,risk:n,expiresAt:o,id:s,name:a,spec:i}){let l=[{id:"c",type:"input",field:"close"},{id:"rsi",type:"indicator",indicator:"rsi",input:"c",period:i.period},{id:"thr",type:"constant",value:i.threshold},{id:"sig",type:"compare",op:i.op==="above"?"gt":"lt",left:"rsi",right:"thr"}];return{version:1,id:s,name:a,venue:"hyperliquid",market:{coin:e,interval:r},parameters:{},nodes:l,rules:{entryLong:t==="long"?"sig":null,entryShort:t==="short"?"sig":null,exitLong:null,exitShort:null},risk:{...n,expiresAt:o}}}function xo({side:t,coin:e,interval:r,risk:n,expiresAt:o,id:s,name:a,spec:i}){let l=[{id:"fr",type:"input",field:"fundingRate"},{id:"thr",type:"constant",value:i.threshold},{id:"sig",type:"compare",op:i.op==="above"?"gt":"lt",left:"fr",right:"thr"}];return{version:1,id:s,name:a,venue:"hyperliquid",market:{coin:e,interval:r},parameters:{},nodes:l,rules:{entryLong:t==="long"?"sig":null,entryShort:t==="short"?"sig":null,exitLong:null,exitShort:null},risk:{...n,expiresAt:o}}}function xe(t,e={}){if(typeof t!="string")throw new B("prompt must be a string");if(t.length>1e3)throw new B("prompt length must be <= 1000");if(!so(e))throw new B("options must be a plain object");if(!Number.isInteger(e.nowMs))throw new B("options.nowMs is required integer epoch ms");let r=e.nowMs,n=io;if("expiresInMs"in e){if(!Number.isInteger(e.expiresInMs))throw new B("expiresInMs must be an integer");if(n=e.expiresInMs,n<no||n>oo)throw new B("expiresInMs must be between 1h and 30d")}if(eo.test(t))throw new B(`prompt appears to contain secrets and is rejected. ${Et}`);let o=ao(t);if(!o)throw new B(`empty prompt. ${Et}`);if(po(o))throw new B(`duplicate interval or risk clause. ${Et}`);let s=co(o),a=lo(o),i=uo(o);if(!s||!a||!i)throw new B(`could not parse side/coin/interval. ${Et}`);let l=fo(o);if(!(l.maxLeverage>0)||l.maxLeverage>50)throw new B("leverage out of range");if(!(l.positionSizePct>0)||l.positionSizePct>100)throw new B("position size out of range");let f=r+n,u=mo(o),c=`Draft ${a} ${s}`.slice(0,100),d=ho(o),m=yo(o),p=wo(o),b=[d,m,p].filter(Boolean);if(b.length!==1)throw new B(`unsupported or ambiguous prompt. ${Et}`);if(!go(o,b[0]))throw new B(`unsupported or ambiguous prompt. ${Et}`);let E,k=b[0],P={side:s,coin:a,interval:i,risk:l,expiresAt:f,id:u,name:c,spec:k};k.kind==="ema_cross"?E=bo(P):k.kind==="rsi"?E=Eo(P):E=xo(P);let T=Ht(E,{nowMs:r});if(!T.ok)throw new B(`draft failed validation: ${T.errors.map(x=>x.message).join("; ")}`);return Ee(z(E,{nowMs:r}))}import it from"node:fs";import Bt from"node:path";var ke=1,So=["privatekey","secretkey","seed","mnemonic","passphrase","password","keystore","keymaterial","apikey","authtoken","accesstoken","refreshtoken","credential","bearer","signature","authorization","xprv","xpriv","wif"];function Dt(t){return t!=null&&typeof t=="object"&&!Array.isArray(t)}function et(t){return t==null?t:JSON.parse(JSON.stringify(t))}function ko(t){return String(t).toLowerCase().replace(/[^a-z0-9]/g,"")}function xt(t,e="record"){if(Array.isArray(t)){t.forEach((r,n)=>xt(r,`${e}[${n}]`));return}if(Dt(t))for(let[r,n]of Object.entries(t)){let o=ko(r);if(So.some(s=>o.includes(s)))throw new Error(`shadow-store: forbidden secret-like field at ${e}.${r}`);xt(n,`${e}.${r}`)}}function Ao(t){if(typeof t!="string"||!t||!Bt.isAbsolute(t))throw new Error("shadow-store: path must be an absolute path")}function kr(t){let e;try{e=it.lstatSync(t)}catch(n){if(n&&n.code==="ENOENT")return;throw n}if(e.isSymbolicLink())throw new Error("shadow-store: store path must not be a symlink");if(!e.isFile())throw new Error("shadow-store: store path must be a regular file");if(e.mode&511&18)throw new Error("shadow-store: store file must not be group or world writable")}function Po(){return{version:ke,runners:[]}}function Ar(t,e){let r;try{r=JSON.parse(t)}catch{throw new Error("shadow-store: malformed JSON")}if(!Dt(r))throw new Error("shadow-store: malformed JSON root");if(r.version!==ke)throw new Error(`shadow-store: unsupported version ${r.version}`);if(!Array.isArray(r.runners))throw new Error("shadow-store: runners must be an array");return xt(r,Bt.basename(e)),{version:ke,runners:r.runners.map(n=>et(n))}}function Pr(t,e){xt(e,"store");let r=Bt.dirname(t);it.mkdirSync(r,{recursive:!0,mode:448});let n=Bt.join(r,`.shadow-store.${process.pid}.${Date.now()}.tmp`),o=`${JSON.stringify(e)}
|
|
3
|
-
`;try{it.writeFileSync(n,o,{mode:384,flag:"wx"}),it.renameSync(n,t);try{it.chmodSync(t,384)}catch{}}catch(s){try{it.unlinkSync(n)}catch{}throw s}}var Se=new Map;function Ae(t={}){let e=t.path;if(Ao(e),kr(e),it.existsSync(e)?Ar(it.readFileSync(e,"utf8"),e):Pr(e,Po()),Se.has(e))return Se.get(e);let r=!1,n=[];function o(){if(r)return;let p=n.shift();if(p){r=!0;try{let b=p.fn();r=!1,p.resolve(b),o()}catch(b){r=!1,p.reject(b),o()}}}function s(p){if(!r&&n.length===0){r=!0;try{return p()}finally{r=!1,queueMicrotask(o)}}return new Promise((b,E)=>{n.push({fn:p,resolve:b,reject:E}),queueMicrotask(o)})}function a(){return kr(e),Ar(it.readFileSync(e,"utf8"),e)}function i(p){Pr(e,p)}function l(p){if(!Dt(p)||typeof p.id!="string"||!p.id)throw new Error("shadow-store: create requires record with string id");xt(p,"record");let b=a();if(b.runners.some(k=>k.id===p.id))throw new Error(`shadow-store: runner id already exists: ${p.id}`);let E=et(p);return b.runners.push(E),i(b),et(E)}function f(p){let b=a().runners.find(E=>E.id===p);return b?et(b):null}function u(){return a().runners.map(p=>et(p))}function c(p,b){if(typeof b!="function")throw new Error("shadow-store: updater must be a function");let E=a(),k=E.runners.findIndex(x=>x.id===p);if(k<0)throw new Error(`shadow-store: runner not found: ${p}`);let P=et(E.runners[k]),T=b(P);if(!Dt(T)||T.id!==p)throw new Error("shadow-store: updater must return record with same id");return xt(T,"record"),E.runners[k]=et(T),i(E),et(E.runners[k])}function d(p){let b=a(),E=b.runners.findIndex(P=>P.id===p);if(E<0)throw new Error(`shadow-store: runner not found: ${p}`);let k=et(b.runners[E]);return k.status!=="stopped"&&(k.status="stopped",b.runners[E]=k,i(b)),et(b.runners[E])}let m={create(p){return s(()=>l(p))},get(p){return f(p)},list(){return u()},update(p,b){return s(()=>c(p,b))},stop(p){return s(()=>d(p))}};return Se.set(e,m),m}import{createHash as Te}from"node:crypto";function Pe(t){return t==null?t:JSON.parse(JSON.stringify(t))}function $e(t){return Array.isArray(t)?t.map($e):t&&typeof t=="object"?Object.fromEntries(Object.keys(t).sort().map(e=>[e,$e(t[e])])):t}function Tr(t){if(!t||typeof t!="object"||Array.isArray(t))throw new TypeError("shadow-runner: record must be an object");return Te("sha256").update(JSON.stringify($e(t))).digest("hex")}function $o(t,e,r,n,o){return Te("sha256").update(`${t}|${e}|${r}|${n}|${o}`).digest("hex")}function To(t,e,r,n){return Te("sha256").update(`shadow|${t}|${e}|${r}|${n??""}`).digest("hex")}function _o(t){return t==="entryLong"||t==="exitShort"?"buy":t==="entryShort"||t==="exitLong"?"sell":t}function $r(t){return t==="entryLong"||t==="entryShort"}function zt({storePath:t,clock:e=Date.now}={}){if(typeof t!="string"||!t)throw new Error("shadow-runner: storePath required");let r=Ae({path:t});function n({strategy:l,evidenceId:f=null}={}){let u=V(l),c=f==null?null:String(f),d=r.list().filter(k=>k.status==="running"&&k.strategyHash===u.strategyHash&&k.compilerHash===u.compilerHash&&k.evidenceId===c).sort((k,P)=>k.createdAt-P.createdAt||k.id.localeCompare(P.id))[0];if(d)return Pe(d);let m=e(),p=To(m,u.strategyHash,u.compilerHash,c),b=r.get(p);if(b)return Pe(b);let E={id:p,strategy:Pe(u.strategy),strategyHash:u.strategyHash,compilerHash:u.compilerHash,evidenceId:c,status:"running",cursor:null,intendedOrders:[],fills:[],missedFills:[],markouts:[],createdAt:m,updatedAt:m,position:null,cooldownBarsRemaining:0,cooldownStartT:null,lastProcessedBarIndex:-1};return r.create(E)}function o(l){return r.get(l)}function s(){return r.list()}function a(l){return r.stop(l)}function i(l,f,{markoutHorizons:u=[1,4,12]}={}){if(!Array.isArray(f))throw new TypeError("shadow-runner: bars must be an array");return r.update(l,c=>{if(c.status!=="running")throw new Error("shadow-runner: runner is stopped");if(f.length===0)return c;let m=V(c.strategy).evaluateAll(f),p=new Map;for(let h=0;h<f.length;h++)p.set(f[h].t,h);let b=c.cursor,E=[];for(let h=0;h<f.length;h++){let A=f[h].t;(b==null||A>b)&&E.push(h)}if(E.length===0)return c;let k=new Map(c.intendedOrders.map(h=>[h.id,h])),P=new Set(c.fills.map(h=>h.orderId)),T=new Map(c.missedFills.map(h=>[h.orderId,h])),x=(h,A)=>`${h}|${A}`,v=new Map(c.markouts.map(h=>[x(h.orderId,h.horizon),h])),_=c.position?{...c.position}:null,w=Number.isInteger(c.cooldownBarsRemaining)?Math.max(0,c.cooldownBarsRemaining):0,J=Number.isFinite(c.cooldownStartT)?c.cooldownStartT:null;function st({action:h,signalBarT:A,signalIndex:N}){let L=_o(h),F=$o(c.id,c.strategyHash,A,L,h);if(k.has(F))return k.get(F);let I=N+1,R=I<f.length,D={id:F,action:h,type:h,side:L,signalBarT:A,fillBarT:R?f[I].t:null,status:R?"open":"missed"};return k.set(F,D),c.intendedOrders.push(D),D}function at(h,A,N){if(P.has(h.id))return;let L={orderId:h.id,action:$r(h.action)?"entry":"exit",side:h.side,price:A.o,barT:A.t,signalBarT:h.signalBarT,barIndex:N};if(c.fills.push(L),P.add(h.id),h.status="filled",h.fillBarT=A.t,T.has(h.id)&&(T.delete(h.id),c.missedFills=c.missedFills.filter(F=>F.orderId!==h.id)),$r(h.action)){_={side:h.action==="entryLong"?"long":"short",entryOrderId:h.id,entryBarT:A.t,entryPrice:A.o,entryBarIndex:N};for(let F of u){let I=x(h.id,F);if(v.has(I))continue;let R={orderId:h.id,horizon:F,entryBarT:A.t,entryPrice:A.o,side:_.side,status:"pending",close:null,barT:null};v.set(I,R),c.markouts.push(R)}}else _=null,w=Number(c.strategy?.risk?.cooldownBars)||0,J=A.t}function X(h){if(P.has(h.id)||T.has(h.id))return;let A={orderId:h.id,action:h.action,side:h.side,signalBarT:h.signalBarT,status:"missed"};T.set(h.id,A),c.missedFills.push(A),h.status="missed"}for(let h of c.intendedOrders){if(h.status==="filled"||P.has(h.id))continue;let A=p.get(h.signalBarT);if(A==null)continue;let N=A+1;N<f.length&&(b==null||f[N].t>b)&&at(h,f[N],N)}for(let h of E){let A=f[h],N=m[h].signals,L=_,F=R=>{if(_||w>0&&(J==null||A.t>=J))return;let C=st({action:R,signalBarT:A.t,signalIndex:h}),Z=h+1;Z<f.length?at(C,f[Z],Z):X(C)},I=R=>{if(!_||_.side==="long"&&R!=="exitLong"||_.side==="short"&&R!=="exitShort")return;let D=st({action:R,signalBarT:A.t,signalIndex:h}),C=h+1;C<f.length?at(D,f[C],C):X(D)};L?(N.exitLong&&I("exitLong"),N.exitShort&&I("exitShort")):N.entryLong&&N.entryShort||(N.entryLong&&F("entryLong"),N.entryShort&&F("entryShort")),w>0&&J!=null&&A.t>=J&&(w-=1,w===0&&(J=null)),c.cursor=A.t,c.lastProcessedBarIndex=h}for(let h of c.markouts){if(h.status!=="pending")continue;let A=c.fills.find(F=>F.orderId===h.orderId&&F.action==="entry");if(!A)continue;let N=p.get(A.barT);if(N==null)continue;let L=N+h.horizon;if(L<f.length){h.close=f[L].c,h.barT=f[L].t,h.status="filled";let F=h.entryPrice;h.markout=h.side==="long"?(h.close-F)/F:(F-h.close)/F}}return c.position=_,c.cooldownBarsRemaining=w,c.cooldownStartT=J,c.updatedAt=e(),c})}return{start:n,step:i,list:s,get:o,stop:a}}import{createHash as Io,createHmac as Oo,timingSafeEqual as Mi}from"node:crypto";var No=2,Ro=300*1e3,vi=(()=>{let t=Number(process.env.ORACLE_PREPARE_HARD_MAX_AGE_MS);return Number.isFinite(t)&&t>0?t:1800*1e3})();function _e(t){return t===null||typeof t!="object"?JSON.stringify(t):Array.isArray(t)?`[${t.map(_e).join(",")}]`:`{${Object.keys(t).sort().map(r=>`${JSON.stringify(r)}:${_e(t[r])}`).join(",")}}`}function _r(t){let e=String(t?.ORACLE_STAMP_HMAC_SECRET||"").trim();return e.length>=16?e:""}function Ir(t=process.env){return _r(process.env)||_r(t)}function Mo(t){let{oraclePrepared:e,prepareHash:r,prepareMac:n,note:o,...s}=t||{};return s}function vo(t){let e=Mo(t);return Io("sha256").update(_e(e)).digest("hex")}function Lo(t,e=Ir()){return e?Oo("sha256",e).update(String(t)).digest("hex"):""}function Or(t,{provider:e,kind:r,env:n=process.env}={}){if(!t||typeof t!="object")throw new Error("stampPrepared: payload object required");let o={...t,provider:t.provider||e,kind:t.kind||r,requiresUserSignature:t.requiresUserSignature!==!1,signingReady:!1,broadcastReady:!1,executionReady:!1};if(!o.provider)throw new Error("stampPrepared: provider required");if(!o.kind)throw new Error("stampPrepared: kind required");let s=Date.now(),a=s+Ro,l={...o,preparedAt:s,expiresAt:a,prepareVersion:No},f=vo(l),u=Ir(n),c=u?Lo(f,u):void 0,d={...l,oraclePrepared:!0,prepareHash:f};return c&&(d.prepareMac=c),d}var Fo=["privatekey","secretkey","seed","mnemonic","passphrase","password","keystore","keymaterial","apikey","authtoken","accesstoken","refreshtoken","credential","bearer","signature","authorization","xprv","xpriv","wif"];function Ot(t){return t!=null&&typeof t=="object"&&!Array.isArray(t)}function Ho(t){return String(t).toLowerCase().replace(/[^a-z0-9]/g,"")}function Oe(t,e="value"){if(Array.isArray(t)){t.forEach((r,n)=>Oe(r,`${e}[${n}]`));return}if(Ot(t))for(let[r,n]of Object.entries(t)){let o=Ho(r);if(Fo.some(s=>o.includes(s)))throw new Error(`handoff: forbidden secret-like field at ${e}.${r}`);Oe(n,`${e}.${r}`)}}function Ie(t){return typeof t=="number"&&Number.isFinite(t)&&t>0}function Ne({strategy:t,evidence:e,shadow:r,caps:n,nowMs:o=Date.now(),env:s=process.env}={}){if(Oe({strategy:t,evidence:e,shadow:r,caps:n},"handoff"),!Ot(t))throw new Error("handoff: strategy required");if(!Ot(e))throw new Error("handoff: evidence required");if(!Ot(r))throw new Error("handoff: shadow required");if(!Ot(n))throw new Error("handoff: caps required");if(typeof r.id!="string"||!r.id.trim())throw new Error("handoff: shadow.id required");if(typeof r.stateHash!="string"||!/^[0-9a-f]{64}$/.test(r.stateHash))throw new Error("handoff: shadow.stateHash must be a sha256 hash");if(r.status!=="running")throw new Error("handoff: shadow.status must be running");if(!Number.isInteger(r.cursor)||r.cursor<=0)throw new Error("handoff: shadow.cursor must be a positive integer");if(!Number.isInteger(r.updatedAt)||r.updatedAt<=0||r.updatedAt>o)throw new Error("handoff: shadow.updatedAt must be a positive integer <= nowMs");let a=z(t,{nowMs:o}),i=V(a);if(be(e,{maxDailyLossPct:a.risk.maxDailyLossPct}),e.status!=="pass_live_eligible")throw new Error("handoff: evidence.status must be pass_live_eligible");if(e.strategyHash!==i.strategyHash)throw new Error("handoff: evidence.strategyHash mismatch");if(e.compilerHash!==i.compilerHash)throw new Error("handoff: evidence.compilerHash mismatch");let l=["perStrategyNotionalUsd","maxLeverage","dailyLossCapUsd","chainAllowlist","assetAllowlist","expiresAt","killSwitchRequired"];for(let d of l)if(!(d in n)||n[d]==null)throw new Error(`handoff: caps.${d} required`);if(!Ie(n.perStrategyNotionalUsd))throw new Error("handoff: caps.perStrategyNotionalUsd must be positive");if(n.perStrategyNotionalUsd>i.strategy.risk.maxNotionalUsd)throw new Error("handoff: caps.perStrategyNotionalUsd exceeds strategy.risk.maxNotionalUsd");if(!Ie(n.maxLeverage))throw new Error("handoff: caps.maxLeverage must be positive");if(n.maxLeverage>i.strategy.risk.maxLeverage)throw new Error("handoff: caps.maxLeverage exceeds strategy.risk.maxLeverage");if(!Ie(n.dailyLossCapUsd))throw new Error("handoff: caps.dailyLossCapUsd must be positive");let f=i.strategy.risk.maxNotionalUsd*(i.strategy.risk.maxDailyLossPct/100);if(n.dailyLossCapUsd>f)throw new Error("handoff: caps.dailyLossCapUsd exceeds strategy daily loss ceiling");if(!Array.isArray(n.chainAllowlist)||n.chainAllowlist.length===0)throw new Error("handoff: caps.chainAllowlist must be a non-empty array");if(!n.chainAllowlist.every(d=>d==="hyperliquid"))throw new Error("handoff: caps.chainAllowlist must include only hyperliquid");if(!n.chainAllowlist.includes("hyperliquid"))throw new Error("handoff: caps.chainAllowlist must include hyperliquid");if(!Array.isArray(n.assetAllowlist)||n.assetAllowlist.length===0)throw new Error("handoff: caps.assetAllowlist must be a non-empty array");if(n.assetAllowlist.length!==1||n.assetAllowlist[0]!==i.strategy.market.coin)throw new Error("handoff: caps.assetAllowlist must contain only the strategy market coin");if(!Number.isInteger(n.expiresAt)||!(n.expiresAt>o))throw new Error("handoff: caps.expiresAt must be an integer > nowMs");if(n.expiresAt>i.strategy.risk.expiresAt)throw new Error("handoff: caps.expiresAt exceeds strategy.risk.expiresAt");if(n.killSwitchRequired!==!0)throw new Error("handoff: caps.killSwitchRequired must be exactly true");let u={perStrategyNotionalUsd:n.perStrategyNotionalUsd,maxLeverage:n.maxLeverage,dailyLossCapUsd:n.dailyLossCapUsd,chainAllowlist:[...n.chainAllowlist],assetAllowlist:[...n.assetAllowlist],expiresAt:n.expiresAt,killSwitchRequired:!0},c={strategyHash:i.strategyHash,compilerHash:i.compilerHash,evidence:JSON.parse(JSON.stringify(e)),shadow:{id:r.id,stateHash:r.stateHash,status:r.status,cursor:r.cursor,updatedAt:r.updatedAt},compiled:{strategyHash:i.strategyHash,compilerHash:i.compilerHash,compilerVersion:gt,venue:"hyperliquid",market:{coin:i.strategy.market.coin,kind:"perp",interval:i.strategy.market.interval},risk:{...i.strategy.risk},strategy:JSON.parse(JSON.stringify(i.strategy))},arming:{liveBroadcast:!1,requiresExplicitUserArm:!0,caps:u},requiresUserSignature:!0,signingReady:!1,broadcastReady:!1,executionReady:!1};return Or(c,{provider:"hl-strategy",kind:"strategy-live-handoff",env:s})}import jo from"node:os";import Uo from"node:path";var Co=["privatekey","secretkey","seed","mnemonic","passphrase","password","keystore","keymaterial","apikey","authtoken","accesstoken","refreshtoken","credential","bearer","signature","authorization","xprv","xpriv","wif"];function Nt(t){return t!=null&&typeof t=="object"&&!Array.isArray(t)}function qo(t){return String(t).toLowerCase().replace(/[^a-z0-9]/g,"")}function Me(t){if(Array.isArray(t)){for(let e of t)Me(e);return}if(Nt(t))for(let[e,r]of Object.entries(t)){if(Co.some(n=>qo(e).includes(n)))throw new Error(`strategy service: secret-like field "${e}" is forbidden`);Me(r)}}function Bo(t){if(!Nt(t))throw new TypeError("strategy input must be a plain object");return Me(t),t}function Do(t,e){let r=e.nowMs??t.nowMs??Date.now();if(!Number.isInteger(r))throw new TypeError("nowMs must be an integer epoch milliseconds");return r}async function Re(t,e,r,n){if(Array.isArray(e.bars))return e.bars;let s=await(r.fetchBars??ae)({coin:t.market.coin,interval:t.market.interval,count:e.count??1500,endTime:e.endTime??n}),a=Array.isArray(s)?s:s?.bars;if(!Array.isArray(a))throw new Error("strategy history provider returned no bars");return a}function ve(t,e){let r=t.backtestOptions??{};if(!Nt(r))throw new TypeError("backtestOptions must be a plain object");return{...r,nowMs:e}}function Nr(t,e,r,n,o={}){let s=t.evidenceOptions??{};if(!Nt(s))throw new TypeError("evidenceOptions must be a plain object");return{...s,...o,strategy:e,bars:r,backtestOptions:ve(t,n)}}function Rr(){return Uo.join(jo.homedir(),".oracle","strategy-shadow.json")}function zo(t,e){return{perStrategyNotionalUsd:t.risk.maxNotionalUsd,maxLeverage:t.risk.maxLeverage,dailyLossCapUsd:t.risk.maxNotionalUsd*(t.risk.maxDailyLossPct/100),chainAllowlist:["hyperliquid"],assetAllowlist:[t.market.coin],expiresAt:Math.min(t.risk.expiresAt,e+864e5),killSwitchRequired:!0}}async function Vo(t,e={},r={}){let n=Bo(e),o=Do(n,r);if(t==="draft")return{strategy:xe(n.prompt,{nowMs:o})};if(t==="validate"){let s=V(n.strategy);if(s.strategy.risk.expiresAt<=o)throw new Error("strategy is expired");return{valid:!0,strategy:s.strategy,strategyHash:s.strategyHash,compilerHash:s.compilerHash,compilerVersion:s.compilerVersion,requiredSeries:s.requiredSeries}}if(t==="backtest"||t==="evidence"){let s=V(n.strategy),a=await Re(s.strategy,n,r,o);_t(s,a);let i=qt(Nr(n,s.strategy,a,o));return t==="evidence"?i:{backtest:ot(s.strategy,a,ve(n,o)),evidence:i}}if(t==="optimize"){let s=V(n.strategy),a=await Re(s.strategy,n,r,o);_t(s,a);let i=n.trainFraction,l=n.evidenceOptions?.trainFraction;if(i!=null&&l!=null&&i!==l)throw new TypeError("conflicting trainFraction controls are forbidden");let f=i??l??.7;if(typeof f!="number"||f<=0||f>=1)throw new TypeError("trainFraction must be in (0,1)");let u=a.slice(0,Math.floor(a.length*f)),c=n.options??{};if(!Nt(c))throw new TypeError("options must be a plain object");let d=me(s.strategy,u,{...c,...n.maxTrials==null?{}:{maxTrials:n.maxTrials},backtestOptions:ve(n,o)}),m=d.bestStrategy;return{strategy:m,optimization:d,evidence:qt(Nr(n,m,a,o,{trainFraction:f}))}}if(t==="shadow"){let s=zt({storePath:r.storePath??Rr(),clock:()=>o}),a=n.action;if(a==="start")return await s.start({strategy:n.strategy,evidenceId:n.evidenceId??n.evidence?.id??null});if(a==="list")return await s.list();if(a==="get")return await s.get(n.id);if(a==="stop")return await s.stop(n.id);if(a==="step"){let i=await s.get(n.id);if(!i)throw new Error(`shadow runner not found: ${n.id}`);let l=await Re(i.strategy,n,r,o);return _t(V(i.strategy),l),await s.step(n.id,l,n.stepOptions??{})}throw new Error(`unknown shadow action: ${a}`)}if(t==="prepare"){let s=V(n.strategy);if(typeof n.shadowId!="string"||!n.shadowId)throw new Error("strategy prepare requires a matching shadow runner id");let i=await zt({storePath:r.storePath??Rr(),clock:()=>o}).get(n.shadowId);if(!i)throw new Error("strategy prepare shadow runner not found");if(i.status!=="running")throw new Error("strategy prepare requires an active shadow runner");if(!Number.isFinite(i.cursor))throw new Error("strategy prepare requires a stepped shadow runner");if(i.strategyHash!==s.strategyHash||i.compilerHash!==s.compilerHash)throw new Error("strategy prepare shadow runner does not match strategy artifacts");if(i.evidenceId!==n.evidence?.id)throw new Error("strategy prepare shadow runner does not match evidence artifact");let l=n.caps??zo(s.strategy,o);return Ne({strategy:s.strategy,evidence:n.evidence,shadow:{id:i.id,stateHash:Tr(i),status:i.status,cursor:i.cursor,updatedAt:i.updatedAt},caps:l,nowMs:o,env:r.env??process.env})}throw new Error(`unknown strategy operation: ${t}`)}export{Er as EVIDENCE_STATUSES,de as MAX_OPTIMIZER_TRIALS,lt as STRATEGY_COMPILER_HASH,gt as STRATEGY_COMPILER_VERSION,Lt as STRATEGY_VERSION,B as StrategyDraftError,ut as StrategyValidationError,mt as applyParameterOverrides,be as assertEvidenceArtifact,ie as atr,ot as backtestStrategy,oe as bollinger,tr as canonicalStrategyJson,V as compileStrategy,ge as computeEvidenceArtifactId,zt as createShadowRunner,xe as draftStrategyFromPrompt,At as ema,qt as evaluateEvidence,ae as fetchHyperliquidStrategyBars,ur as intervalToMs,ne as macd,fr as normalizeHyperliquidCandles,z as normalizeStrategy,Ae as openShadowStore,me as optimizeStrategy,Ne as prepareStrategyLiveHandoff,re as rsi,Vo as runStrategyOperation,ee as sma,ft as strategyHash,Ht as validateStrategy};
|
|
1
|
+
import{createHash as to}from"node:crypto";import{createHash as Dr}from"node:crypto";var zr=new Set([408,425,429,500,502,503,504]),zt=new Set(["GET","HEAD","OPTIONS","PUT","DELETE"]),Vr=2,Vt=4e3,Kt=["authorization","x-api-key","x-tensor-api-key","0x-api-key","api-key","cookie","proxy-authorization"],Jr=new Set([301,302,303,307,308]),Jt=3;function Kr(e){let t=String(e||"").toLowerCase();return Kt.includes(t)||/(^|-)(api[-_]?key|key|token|secret|auth|authorization|session|cookie|signature|access)(-|$)/.test(t)}function Yr(e){let t={};for(let[r,n]of Object.entries(e||{}))Kr(r)||(t[r]=n);return t}function Gr(e,t){if(!e)throw new Error(`HTTP redirect from ${t} missing Location`);let r=new URL(String(e),String(t));if(r.protocol!=="http:"&&r.protocol!=="https:")throw new Error(`HTTP redirect from ${t} used unsupported protocol ${r.protocol}`);return r.toString()}function Xr(e,t){return new URL(String(e)).origin===new URL(String(t)).origin}var We=new Map;function Zr(e,t,r){let n={};for(let[l,u]of Object.entries(r||{}))n[l.toLowerCase()]=u;let o=Object.keys(n).filter(l=>/(^|-)(api[-_]?key|key|token|secret|auth|authorization|session|cookie|signature|access)(-|$)/.test(l)),i=[...new Set([...Kt,...o])].sort().map(l=>`${l}=${n[l]??""}`).join(`
|
|
2
|
+
`),s=Dr("sha256").update(i).digest("hex").slice(0,32);return`${e} ${t} ${s}`}function Wr(e,t){let r=Number(t);return Number.isFinite(r)&&r>0?Math.min(r*1e3,Vt):Math.min(250*2**e,Vt)+Math.floor(Math.random()*100)}var Qr=e=>new Promise(t=>setTimeout(t,e));async function en(e,{fetchImpl:t,method:r,headers:n,body:o,timeoutMs:a}){let i=new AbortController,s=setTimeout(()=>i.abort(),a);try{let l=String(e),u={...n||{}};for(let f=0;f<=Jt;f+=1){let c=await t(l,{method:r,headers:u,body:o,signal:i.signal,redirect:"manual"}),d=await c.text(),m=null;if(d)try{m=JSON.parse(d)}catch{m=null}if(Jr.has(c.status)){if(r!=="GET"&&r!=="HEAD"){let h=new Error(`HTTP ${c.status} ${r} ${l} redirected non-idempotent request`);throw h.status=c.status,h.body=m??d.slice(0,300),h}if(f===Jt)throw new Error(`HTTP redirect loop for ${e}`);let p=Gr(c.headers?.get?.("location"),l);Xr(p,l)||(u=Yr(u)),l=p;continue}if(!c.ok){let p=new Error(`HTTP ${c.status} ${r} ${l}`);throw p.status=c.status,p.retryAfter=c.headers?.get?.("retry-after")??null,p.body=m??d.slice(0,300),p}return m??d}throw new Error(`HTTP redirect loop for ${e}`)}finally{clearTimeout(s)}}async function Yt(e,t={}){let r=t.fetchImpl||globalThis.fetch;if(typeof r!="function")throw new Error("fetch unavailable \u2014 pass opts.fetchImpl");let n=t.timeoutMs??12e3,o=(t.method||"GET").toUpperCase(),a={Accept:"application/json",...t.headers||{}},i=t.body;i!=null&&typeof i!="string"&&(a["Content-Type"]=a["Content-Type"]||"application/json",i=JSON.stringify(i));let s=Number.isInteger(t.retries)?Math.max(t.retries,0):Vr,l=async()=>{let m;for(let p=0;p<=s;p++)try{return await en(e,{fetchImpl:r,method:o,headers:a,body:i,timeoutMs:n})}catch(h){m=h;let E=h?.status,k=h?.name==="AbortError",P=new Set([408,425,429]),T=E!=null?P.has(E)||zr.has(E)&&zt.has(o):!k&&zt.has(o);if(p===s||!T)throw h;await Qr(Wr(p,h?.retryAfter))}throw m};if(!(t.dedupe!==!1&&o==="GET"&&i==null))return l();let f=Zr(o,e,a),c=We.get(f);if(c)return c;let d=l().finally(()=>We.delete(f));return We.set(f,d),d}var tn="https://api.hyperliquid.xyz/info",rn="https://api.hyperliquid-testnet.xyz/info";function nn({testnet:e,baseUrl:t}={}){if(t)return t.replace(/\/$/,"").endsWith("/info")?t:`${t.replace(/\/$/,"")}/info`;if(process.env.HL_INFO_URL)return process.env.HL_INFO_URL;if(process.env.HL_API_URL){let r=process.env.HL_API_URL.replace(/\/$/,"");return r.endsWith("/info")?r:`${r}/info`}return e||process.env.HL_TESTNET==="1"?rn:tn}async function Qe(e,t={}){if(!e||!e.type)throw new Error("hlInfo requires body.type");let r=nn(t);return Yt(r,{method:"POST",body:e,fetchImpl:t.fetchImpl,timeoutMs:t.timeoutMs})}async function Gt(e={}){return Qe({type:"meta"},e)}async function Xt({coin:e,interval:t="1h",startTime:r,endTime:n},o={}){if(!e)throw new Error("hlCandleSnapshot requires coin");let a={type:"candleSnapshot",req:{coin:String(e),interval:t,startTime:r??Date.now()-24*3600*1e3,endTime:n??Date.now()}};return Qe(a,o)}async function Zt({coin:e,startTime:t,endTime:r},n={}){if(!e)throw new Error("hlFundingHistory requires coin");return Qe({type:"fundingHistory",coin:String(e),startTime:t??Date.now()-24*3600*1e3,endTime:r??Date.now()},n)}function Pe(e){if(!Array.isArray(e))throw new TypeError("values must be an array");for(let t=0;t<e.length;t++){let r=e[t];if(typeof r!="number"||!Number.isFinite(r))throw new TypeError(`values[${t}] must be a finite number`)}}function ae(e){if(!Number.isInteger(e)||e<=0)throw new TypeError("period must be a positive integer")}function on(e){if(!Array.isArray(e))throw new TypeError("bars must be an array");for(let t=0;t<e.length;t++){let r=e[t];if(r==null||typeof r!="object")throw new TypeError(`bars[${t}] must be an object`);for(let n of["o","h","l","c"]){let o=r[n];if(typeof o!="number"||!Number.isFinite(o))throw new TypeError(`bars[${t}].${n} must be a finite number`)}if(r.h<r.l)throw new TypeError(`bars[${t}] high must be >= low`)}}function et(e,t){Pe(e),ae(t);let r=new Array(e.length).fill(null);if(e.length<t)return r;let n=0;for(let o=0;o<e.length;o++)n+=e[o],o>=t&&(n-=e[o-t]),o>=t-1&&(r[o]=n/t);return r}function Ae(e,t){Pe(e),ae(t);let r=new Array(e.length).fill(null);if(e.length<t)return r;let n=0;for(let i=0;i<t;i++)n+=e[i];let o=n/t;r[t-1]=o;let a=2/(t+1);for(let i=t;i<e.length;i++)o=a*e[i]+(1-a)*o,r[i]=o;return r}function tt(e,t){Pe(e),ae(t);let r=new Array(e.length).fill(null);if(e.length<=t)return r;let n=0,o=0;for(let s=1;s<=t;s++){let l=e[s]-e[s-1];l>=0?n+=l:o-=l}let a=n/t,i=o/t;r[t]=Wt(a,i);for(let s=t+1;s<e.length;s++){let l=e[s]-e[s-1],u=l>0?l:0,f=l<0?-l:0;a=(a*(t-1)+u)/t,i=(i*(t-1)+f)/t,r[s]=Wt(a,i)}return r}function Wt(e,t){return e===0&&t===0?50:t===0?100:e===0?0:100-100/(1+e/t)}function rt(e,t={}){Pe(e);let r=t.fastPeriod,n=t.slowPeriod,o=t.signalPeriod;if(ae(r),ae(n),ae(o),r>=n)throw new TypeError("fastPeriod must be < slowPeriod");let a=Ae(e,r),i=Ae(e,n),s=new Array(e.length).fill(null);for(let c=0;c<e.length;c++)a[c]!=null&&i[c]!=null&&(s[c]=a[c]-i[c]);let l=new Array(e.length).fill(null),u=new Array(e.length).fill(null),f=[];for(let c=0;c<s.length;c++)s[c]!=null&&f.push(c);if(f.length>=o){let c=0;for(let h=0;h<o;h++)c+=s[f[h]];let d=c/o,m=f[o-1];l[m]=d,u[m]=s[m]-d;let p=2/(o+1);for(let h=o;h<f.length;h++){let E=f[h];d=p*s[E]+(1-p)*d,l[E]=d,u[E]=s[E]-d}}return{line:s,signal:l,histogram:u}}function nt(e,t={}){Pe(e);let r=t.period,n=t.stdDev;if(ae(r),typeof n!="number"||!Number.isFinite(n)||n<=0)throw new TypeError("stdDev must be a finite number > 0");let o=new Array(e.length).fill(null),a=new Array(e.length).fill(null),i=new Array(e.length).fill(null);if(e.length<r)return{upper:a,middle:o,lower:i};let s=0,l=0;for(let u=0;u<e.length;u++){let f=e[u];if(s+=f,l+=f*f,u>=r){let c=e[u-r];s-=c,l-=c*c}if(u>=r-1){let c=s/r,d=l/r-c*c;d<0&&d>-1e-12&&(d=0);let m=Math.sqrt(d);o[u]=c,a[u]=c+n*m,i[u]=c-n*m}}return{upper:a,middle:o,lower:i}}function ot(e,t){on(e),ae(t);let r=new Array(e.length).fill(null);if(e.length<t)return r;let n=new Array(e.length);for(let i=0;i<e.length;i++){let s=e[i];if(i===0)n[i]=s.h-s.l;else{let l=e[i-1].c;n[i]=Math.max(s.h-s.l,Math.abs(s.h-l),Math.abs(s.l-l))}}let o=0;for(let i=0;i<t;i++)o+=n[i];let a=o/t;r[t-1]=a;for(let i=t;i<e.length;i++)a=(a*(t-1)+n[i])/t,r[i]=a;return r}import{createHash as sn}from"node:crypto";var st=2,an="93f9c998260e73756c13871dc1a84dd6cdb26ec6113bd1941f023519939db440",cn="68f5b412a9975f04024cf9b9b6209b4bccedd52f176e95f34f786b3f53479c6a",ln="f197a38ff1c0f4ebe13326db8cbb8167ea3dd5d62e6033fe97c2fee8d23d96b5",Qt=sn("sha256").update(JSON.stringify({version:st,compilerSourceHash:an,indicatorsSourceHash:cn,schemaSourceHash:ln})).digest("hex");var he=st,ce=Qt,un=Object.freeze({open:"o",high:"h",low:"l",close:"c",volume:"v",fundingRate:"fundingRate",openInterest:"openInterest"}),fn=Object.freeze({"1m":6e4,"3m":18e4,"5m":3e5,"15m":9e5,"30m":18e5,"1h":36e5,"2h":72e5,"4h":144e5,"8h":288e5,"12h":432e5,"1d":864e5});function Y(e){return typeof e=="number"&&Number.isFinite(e)}function dn(e){let t=new Map(e.map(s=>[s.id,s])),r=s=>{switch(s.type){case"indicator":return s.input?[s.input]:[];case"compare":case"cross":return[s.left,s.right];case"logic":return[...s.inputs];default:return[]}},n=new Map,o=new Map;for(let s of e)n.set(s.id,0),o.set(s.id,[]);for(let s of e)for(let l of r(s))t.has(l)&&(o.get(l).push(s.id),n.set(s.id,n.get(s.id)+1));let a=[];for(let[s,l]of n)l===0&&a.push(s);let i=[];for(;a.length;){let s=a.shift();i.push(s);for(let l of o.get(s))n.set(l,n.get(l)-1),n.get(l)===0&&a.push(l)}if(i.length!==e.length)throw new Error("strategy graph is cyclic");return i}function me(e,t){return e!=null&&typeof e=="object"&&"param"in e?t[e.param].value:e}function pn(e,t,r){if(!Array.isArray(e)||e.length===0)throw new TypeError("bars must be a non-empty array");if(!Number.isInteger(t)||t<0||t>=e.length)throw new RangeError("index out of range");let n=null;for(let o=0;o<=t;o++){let a=e[o];if(a==null||typeof a!="object"||Array.isArray(a))throw new TypeError(`bars[${o}] must be an object`);let{t:i,o:s,h:l,l:u,c:f,v:c}=a;if(!Y(i)&&(typeof i=="number"&&Number.isFinite(i)),!Number.isFinite(i))throw new TypeError(`bars[${o}].t must be finite`);for(let[d,m]of[["o",s],["h",l],["l",u],["c",f],["v",c]])if(!Y(m))throw new TypeError(`bars[${o}].${d} must be a finite number`);for(let[d,m]of[["o",s],["h",l],["l",u],["c",f]])if(!(m>0))throw new TypeError(`bars[${o}].${d} price must be positive`);if(c<0)throw new TypeError(`bars[${o}].v volume must be non-negative`);if(l<Math.max(s,f,u))throw new TypeError(`bars[${o}] high must be >= max(open, close, low)`);if(u>Math.min(s,f,l))throw new TypeError(`bars[${o}] low must be <= min(open, close, high)`);if(n!=null){if(!(i>n))throw new TypeError("bars t must be strictly increasing");if(i-n!==fn[r])throw new TypeError(`bars t must match ${r} cadence`)}if(n=i,"fundingRate"in a&&a.fundingRate!=null&&!Y(a.fundingRate))throw new TypeError(`bars[${o}].fundingRate must be finite when present`);if("openInterest"in a&&a.openInterest!=null){if(!Y(a.openInterest))throw new TypeError(`bars[${o}].openInterest must be finite when present`);if(a.openInterest<0)throw new TypeError(`bars[${o}].openInterest must be non-negative`)}}}function ue(e,t,r,n,o,a){if(a.has(e))return a.get(e);let i=t.get(e),s;switch(i.type){case"input":{let l=un[i.field];s=new Array(o+1);for(let u=0;u<=o;u++){let f=n[u][l];s[u]=f===void 0?null:f}break}case"constant":{let l=me(i.value,r);s=new Array(o+1).fill(l);break}case"indicator":{let l=i.period!=null?me(i.period,r):void 0;if(i.indicator==="atr"){let c=[];for(let d=0;d<=o;d++){let m=n[d];c.push({o:m.o,h:m.h,l:m.l,c:m.c})}s=ot(c,l);break}let f=ue(i.input,t,r,n,o,a).map((c,d)=>{if(c==null||!Y(c))throw new TypeError(`indicator input ${i.input} has non-finite value at ${d}`);return c});if(i.indicator==="sma")s=et(f,l);else if(i.indicator==="ema")s=Ae(f,l);else if(i.indicator==="rsi")s=tt(f,l);else if(i.indicator==="macd")s=rt(f,{fastPeriod:me(i.fastPeriod,r),slowPeriod:me(i.slowPeriod,r),signalPeriod:me(i.signalPeriod,r)})[i.output];else if(i.indicator==="bollinger")s=nt(f,{period:l,stdDev:me(i.stdDev,r)})[i.output];else throw new Error(`unsupported indicator ${i.indicator}`);break}case"compare":{let l=ue(i.left,t,r,n,o,a),u=ue(i.right,t,r,n,o,a);s=new Array(o+1);for(let f=0;f<=o;f++){let c=l[f],d=u[f];if(c==null||d==null||!Y(c)||!Y(d)){s[f]=!1;continue}switch(i.op){case"gt":s[f]=c>d;break;case"gte":s[f]=c>=d;break;case"lt":s[f]=c<d;break;case"lte":s[f]=c<=d;break;case"eq":s[f]=c===d;break;default:s[f]=!1}}break}case"logic":{let l=i.inputs.map(u=>ue(u,t,r,n,o,a));s=new Array(o+1);for(let u=0;u<=o;u++)if(i.op==="not"){let f=l[0][u];s[u]=f===!0?!1:f===!1}else if(i.op==="and"){let f=!0;for(let c of l)if(c[u]!==!0){f=!1;break}s[u]=f}else if(i.op==="or"){let f=!1;for(let c of l)if(c[u]===!0){f=!0;break}s[u]=f}else s[u]=!1;break}case"cross":{let l=ue(i.left,t,r,n,o,a),u=ue(i.right,t,r,n,o,a);s=new Array(o+1),s[0]=!1;for(let f=1;f<=o;f++){let c=l[f-1],d=u[f-1],m=l[f],p=u[f];if(c==null||d==null||m==null||p==null||!Y(c)||!Y(d)||!Y(m)||!Y(p)){s[f]=!1;continue}i.direction==="above"?s[f]=c<=d&&m>p:s[f]=c>=d&&m<p}break}default:throw new Error(`unknown node type ${i.type}`)}return a.set(e,s),s}function z(e,t={}){let r=V(e);t&&Object.keys(t).length&&(r=ye(r,t));let n=new Map(r.nodes.map(m=>[m.id,m])),o=Object.freeze(dn(r.nodes)),a=r.parameters,i=new Set(r.nodes.filter(m=>m.type==="input").map(m=>m.field));if(r.nodes.some(m=>m.type==="indicator"&&m.indicator==="atr"))for(let m of["open","high","low","close"])i.add(m);let s=Object.freeze([...i].sort()),l=fe(r);function u(m,p){let h={};for(let k of o){let P=m.get(k);h[k]=P[p]}let E=k=>k!=null&&h[k]===!0;return{values:h,signals:{entryLong:E(r.rules.entryLong),entryShort:E(r.rules.entryShort),exitLong:E(r.rules.exitLong),exitShort:E(r.rules.exitShort)}}}function f(m,p){pn(m,p,r.market.interval);let h=new Map;for(let E of o)ue(E,n,a,m,p,h);return h}function c(m,p){return u(f(m,p),p)}function d(m){if(!Array.isArray(m)||m.length===0)throw new TypeError("bars must be a non-empty array");let p=m.length-1,h=f(m,p);return Object.freeze(m.map((E,k)=>Object.freeze(u(h,k))))}return Object.freeze({strategy:r,strategyHash:l,compilerVersion:he,compilerHash:ce,requiredSeries:s,nodeOrder:o,evaluate:c,evaluateAll:d})}import{createHash as In}from"node:crypto";import{createHash as mn}from"node:crypto";var hn=Object.freeze({initialEquityUsd:1e4,takerFeeBps:3.5,builderFeeBps:2,slippageBps:2,latencyBars:1,maxVolumeParticipationPct:100});function yn(e){return e!=null&&typeof e=="object"&&!Array.isArray(e)}function Q(e){return typeof e=="number"&&Number.isFinite(e)}function it(e){if(e==null||typeof e!="object"||Object.isFrozen(e))return e;if(Array.isArray(e)){for(let t of e)it(t);return Object.freeze(e)}for(let t of Object.keys(e))it(e[t]);return Object.freeze(e)}function Le(e){if(Array.isArray(e))return e.map(Le);if(e!=null&&typeof e=="object"){let t={};for(let r of Object.keys(e).sort())t[r]=Le(e[r]);return t}return e}function at(e,t="result"){if(typeof e=="number"){if(!Number.isFinite(e))throw new Error(`non-finite backtest result at ${t}`);return}if(!(e==null||typeof e!="object")){if(Array.isArray(e)){e.forEach((r,n)=>at(r,`${t}[${n}]`));return}for(let[r,n]of Object.entries(e))at(n,`${t}.${r}`)}}function tr(e){if(!Array.isArray(e)||e.length===0)throw new TypeError("bars must be a non-empty array");let t=null;for(let r=0;r<e.length;r++){let n=e[r];if(n==null||typeof n!="object"||Array.isArray(n))throw new TypeError(`bars[${r}] must be an object`);let{t:o,o:a,h:i,l:s,c:l,v:u}=n;if(!Q(o))throw new TypeError(`bars[${r}].t must be finite`);for(let[f,c]of[["o",a],["h",i],["l",s],["c",l],["v",u]])if(!Q(c))throw new TypeError(`bars[${r}].${f} must be a finite number`);for(let[f,c]of[["o",a],["h",i],["l",s],["c",l]])if(!(c>0))throw new TypeError(`bars[${r}].${f} price must be positive`);if(u<0)throw new TypeError(`bars[${r}].v volume must be non-negative`);if(i<Math.max(a,l,s))throw new TypeError(`bars[${r}] high must be >= max(open, close, low)`);if(s>Math.min(a,l,i))throw new TypeError(`bars[${r}] low must be <= min(open, close, high)`);if(t!=null&&!(o>t))throw new TypeError("bars t must be strictly increasing");if(t=o,"fundingRate"in n&&n.fundingRate!=null&&!Q(n.fundingRate))throw new TypeError(`bars[${r}].fundingRate must be finite when present`);if("fundingPaymentRate"in n&&n.fundingPaymentRate!=null&&!Q(n.fundingPaymentRate))throw new TypeError(`bars[${r}].fundingPaymentRate must be finite when present`);if("openInterest"in n&&n.openInterest!=null&&!Q(n.openInterest))throw new TypeError(`bars[${r}].openInterest must be finite when present`)}}function $e(e){if(!Array.isArray(e))throw new TypeError("bars must be an array");return e.length>0&&tr(e),mn("sha256").update(JSON.stringify(Le(e)),"utf8").digest("hex")}function gn(e){if(e==null&&(e={}),!yn(e))throw new TypeError("options must be a plain object");let t={...hn},r=new Set(["initialEquityUsd","takerFeeBps","builderFeeBps","slippageBps","latencyBars","maxVolumeParticipationPct","nowMs"]);for(let n of Object.keys(e))if(!r.has(n))throw new TypeError(`unknown option "${n}"`);if("initialEquityUsd"in e){if(!Q(e.initialEquityUsd)||e.initialEquityUsd<=0)throw new TypeError("initialEquityUsd must be a positive finite number");t.initialEquityUsd=e.initialEquityUsd}for(let n of["takerFeeBps","builderFeeBps","slippageBps"])if(n in e){let o=e[n];if(!Q(o)||o<0)throw new TypeError(`${n} must be a finite number >= 0`);if(n==="slippageBps"?o>=1e4:o>1e4)throw new TypeError(`${n} exceeds its safe basis-point bound`);t[n]=o}if("latencyBars"in e){let n=e.latencyBars;if(!Number.isInteger(n)||n<1)throw new TypeError("latencyBars must be an integer >= 1");t.latencyBars=n}if("maxVolumeParticipationPct"in e){let n=e.maxVolumeParticipationPct;if(!Q(n)||n<=0||n>100)throw new TypeError("maxVolumeParticipationPct must be in (0,100]");t.maxVolumeParticipationPct=n}if("nowMs"in e){if(!Number.isInteger(e.nowMs))throw new TypeError("nowMs must be an integer epoch milliseconds");t.nowMs=e.nowMs}return t}function je(e){return e/1e4}function wn(e,t,r){let n=je(r);return t==="long"?e*(1+n):e*(1-n)}function bn(e,t,r){let n=je(r);return t==="long"?e*(1-n):e*(1+n)}function er(e,t,r){let n=Math.abs(e)*je(t),o=Math.abs(e)*je(r);return{taker:n,builder:o,total:n+o}}function En(e,t){let r=e*(t.positionSizePct/100)*t.maxLeverage;return Math.min(r,t.maxNotionalUsd)}function xn(e,t,r){let n=r/100;return t==="long"?e*(1-n):e*(1+n)}function Sn(e,t,r){let n=r/100;return t==="long"?e*(1+n):e*(1-n)}function kn(e,t,r,n){if(!(r>0))return t==="long"?0:Number.POSITIVE_INFINITY;let o=Math.min(.99,1/(2*r)+n/1e4);return t==="long"?e*(1-1/r)/(1-o):e*(1+1/r)/(1+o)}function An(e,t,r){return t==="long"?e.l<=r:e.h>=r}function Pn(e,t,r){return t==="long"?e.h>=r:e.l<=r}function $n(e,t,r){return t==="long"?e.l<=r:e.h>=r}function Te(e,t){for(let r of e.requiredSeries)if(["fundingRate","openInterest"].includes(r)&&!t.every(n=>Q(n?.[r])))throw new Error(`strategy required series is absent: ${r}`)}function Tn(e){if(e.length<3)return 0;let t=[];for(let i=1;i<e.length;i++){let s=e[i-1].equity,l=e[i].equity;s>0&&t.push((l-s)/s)}if(t.length<2)return 0;let r=0;for(let i of t)r+=i;let n=r/t.length,o=0;for(let i of t)o+=(i-n)**2;let a=Math.sqrt(o/(t.length-1));return a>0?n/a:0}function _n(e){let t=-1/0,r=0;for(let n of e){let o=n.equity;if(o>t&&(t=o),t>0){let a=(t-o)/t*100;a>r&&(r=a)}}return r}function ne(e,t,r={}){let n=gn(r);tr(t);let o=$e(t);n.nowMs!==void 0&&V(e,{nowMs:n.nowMs});let a=z(e);if(Te(a,t),n.nowMs!==void 0&&a.strategy.risk.expiresAt<=n.nowMs)throw new Error("strategy is expired: risk.expiresAt must be strictly greater than nowMs");let i=a.strategy.risk,s=a.evaluateAll(t),l=n.latencyBars,u=n.slippageBps,f=n.takerFeeBps,c=n.builderFeeBps,d=n.initialEquityUsd,m=0,p=0,h=0,E=0,k=0,P=[],T=[],x=[],N=[],_=[],w=null,J=0,se=null,ie=d,X=!1,y=0,A=0,v=[],L=new Array(t.length).fill(0);function j(b){v.push(b)}function I(b){let $=t[b].v*(n.maxVolumeParticipationPct/100);return Math.max(0,$-L[b])}function M(b){if(!w)return d;let $=w.side==="long"?(b-w.entryPrice)*w.qty:(w.entryPrice-b)*w.qty;return d+$}function D(b,$,S){let F=S.o,O=En(d,i);if(!(O>0)||!(F>0))return;let H=wn(F,$,u),R=O/H,q=I(b),Se=H*q;if(R>q){x.push({kind:"entry",barIndex:b,time:S.t,side:$,requestedNotionalUsd:O,maxFillNotionalUsd:Se,reason:"volume_participation"});return}L[b]+=R;let W=Math.abs(H-F)*R,re=er(O,f,c);d-=re.total,m+=re.taker,p+=re.builder,E+=W,k+=O,w={lifecycleEpoch:++A,side:$,entryBarIndex:b,entryReferencePrice:F,entryPrice:H,qty:R,notionalUsd:O,stop:xn(H,$,i.stopLossPct),take:Sn(H,$,i.takeProfitPct),liq:kn(H,$,i.maxLeverage,f+c),entryFees:re.taker,entryBuilderFees:re.builder,entrySlippageUsd:W,fundingAccrued:0}}function C(b,$,S,F){if(!w)return!1;let O=w.side,H=$,R=H;S!=="liquidation"&&F&&(R=bn(H,O,u));let q=w.qty,Se=q*R;if(S!=="liquidation"){let Dt=I(b),Br=R*Dt;if(q>Dt)return x.push({kind:"exit",barIndex:b,time:t[b].t,side:O,requestedNotionalUsd:Se,maxFillNotionalUsd:Br,reason:"volume_participation",triggerReason:S}),!1;L[b]+=q}let W=er(Se,f,c),re=S==="liquidation"?0:Math.abs(R-H)*q,ke,Ze;O==="long"?(ke=(H-w.entryReferencePrice)*q,Ze=(R-w.entryPrice)*q):(ke=(w.entryReferencePrice-H)*q,Ze=(w.entryPrice-R)*q),d+=Ze,d-=W.total,m+=W.taker,p+=W.builder,E+=re,k+=Se;let qr={side:O,entryBarIndex:w.entryBarIndex,entryTime:t[w.entryBarIndex].t,entryPrice:w.entryPrice,exitBarIndex:b,exitTime:t[b].t,exitPrice:R,exitReason:S,qty:q,notionalUsd:w.notionalUsd,grossPnlUsd:ke,feesUsd:w.entryFees+W.taker,builderFeesUsd:w.entryBuilderFees+W.builder,fundingUsd:w.fundingAccrued,slippageUsd:w.entrySlippageUsd+re,netPnlUsd:ke-(w.entryFees+W.taker+w.entryBuilderFees+W.builder)-w.fundingAccrued-(w.entrySlippageUsd+re)};return P.push(qr),S==="liquidation"&&T.push({barIndex:b,time:t[b].t,side:O,price:R,tradeIndex:P.length-1}),w=null,A+=1,y=b+1+(i.cooldownBars||0),!0}function Z(b,$,S,F){let O=b+1;if(O>=t.length)return;v.some(R=>R.kind==="exit"&&R.side===$&&R.fillBar===O&&R.epoch===F)||j({kind:"exit",side:$,signalBar:b,fillBar:O,reason:S,epoch:F})}function Me(b){let $=v.filter(S=>S.fillBar===b);for(let S=v.length-1;S>=0;S--)v[S].fillBar===b&&v.splice(S,1);$.sort((S,F)=>{let O=H=>H.kind==="exit"?0:1;return O(S)-O(F)});for(let S of $)if(S.kind==="exit"){if(!w||w.side!==S.side||w.lifecycleEpoch!==S.epoch)continue;let F=S.reason||"rule";C(b,t[b].o,F,!0)||Z(b,S.side,F,S.epoch)}else if(S.kind==="entry"){if(w||S.epoch!==A||b<y)continue;D(b,S.side,t[b])}}function Re(b){if(!w)return;let $=t[b],S=$.fundingPaymentRate;if(S==null||!Q(S)||S===0)return;let F=Math.abs(w.qty*$.c),O=S*F;w.side==="long"?(d-=O,w.fundingAccrued+=O,h+=O):(d+=O,w.fundingAccrued-=O,h-=O)}function Ne(b){if(!w)return;let $=t[b].o;(w.side==="long"?$<=w.liq:$>=w.liq)&&C(b,$,"liquidation",!1)}function Ht(b){if(!w)return;let $=t[b],S=w.side,F=$n($,S,w.liq),O=An($,S,w.stop),H=Pn($,S,w.take),R=q=>S==="long"?Math.min(q,$.o):Math.max(q,$.o);if(F){C(b,R(w.liq),"liquidation",!1);return}if(O&&H){C(b,R(w.stop),"stop_loss",!0)||Z(b,S,"stop_loss",w.lifecycleEpoch);return}if(O){C(b,R(w.stop),"stop_loss",!0)||Z(b,S,"stop_loss",w.lifecycleEpoch);return}H&&(C(b,w.take,"take_profit",!0)||Z(b,S,"take_profit",w.lifecycleEpoch))}for(let b=0;b<t.length;b++){let $=Math.floor(t[b].t/864e5);$!==se&&(se=$,ie=M(t[b].o),X=!1),Ne(b),Me(b),Ht(b),Re(b),!X&&M(t[b].c)<=ie*(1-i.maxDailyLossPct/100)&&(X=!0,N.push({type:"daily_loss_limit",barIndex:b,time:t[b].t,message:"daily loss limit reached; new entries blocked for UTC day"}));let S=s[b].signals,F=b+l;if(w)(w.side==="long"&&S.exitLong===!0||w.side==="short"&&S.exitShort===!0)&&F<t.length&&(v.some(q=>q.kind==="exit"&&q.side===w.side&&q.fillBar===F&&q.epoch===w.lifecycleEpoch)||j({kind:"exit",side:w.side,signalBar:b,fillBar:F,reason:"rule",epoch:w.lifecycleEpoch}));else{let H=S.entryLong===!0,R=S.entryShort===!0;H&&R?N.push({type:"conflicting_entry_signals",barIndex:b,time:t[b].t,message:"conflicting entry signals; no position opened"}):!X&&(H||R)&&F<t.length&&b>=y&&j({kind:"entry",side:H?"long":"short",signalBar:b,fillBar:F,epoch:A})}let O=d;w&&(J+=1,O=M(t[b].c)),_.push({barIndex:b,time:t[b].t,equity:O,positionSide:w?w.side:null})}let Ut=null;if(w){let b=t.length-1;if(C(b,t[b].c,"end_of_data",!0))_[b]={barIndex:b,time:t[b].t,equity:d,positionSide:null};else{let S=M(t[b].c);Ut={side:w.side,entryBarIndex:w.entryBarIndex,entryTime:t[w.entryBarIndex].t,entryPrice:w.entryPrice,qty:w.qty,notionalUsd:w.notionalUsd,markPrice:t[b].c,markedEquityUsd:S},N.push({type:"open_position_at_end",barIndex:b,time:t[b].t,message:"end-of-data exit missed; position remains open and marked"})}}let Ct=(w?M(t[t.length-1].c):d)-n.initialEquityUsd,Fr=Ct/n.initialEquityUsd*100,qt=0,Ke=0,Ye=0;for(let b of P)b.netPnlUsd>0?(qt+=1,Ke+=b.netPnlUsd):b.netPnlUsd<0&&(Ye+=-b.netPnlUsd);let Ge=P.length,Hr=Ge===0?0:qt/Ge,Xe=0;Ye>0?Xe=Ke/Ye:Ke>0&&(Xe=1e12);let Ur={netPnlUsd:Ct,netPnlPct:Fr,maxDrawdownPct:_n(_),winRate:Hr,profitFactor:Xe,tradeCount:Ge,sharpe:Tn(_),turnoverUsd:k,exposurePct:J/t.length*100},Cr={initialEquityUsd:n.initialEquityUsd,takerFeeBps:n.takerFeeBps,builderFeeBps:n.builderFeeBps,slippageBps:n.slippageBps,latencyBars:n.latencyBars,maxVolumeParticipationPct:n.maxVolumeParticipationPct},Bt={barsHash:o,strategyHash:a.strategyHash,compilerHash:ce,compilerVersion:he,config:Cr,trades:P,equityCurve:_,metrics:Ur,costs:{feesUsd:m,builderFeesUsd:p,fundingUsd:h,slippageUsd:E},liquidations:T,missedFills:x,openPositionAtEnd:Ut,flags:N};return at(Bt),it(Le(Bt))}var or=Object.freeze(["fail","pass_paper_only","pass_live_eligible"]);function ee(e){return e!=null&&typeof e=="object"&&!Array.isArray(e)}function ge(e){return typeof e=="number"&&Number.isFinite(e)}function ut(e){if(e==null||typeof e!="object"||Object.isFrozen(e))return e;if(Array.isArray(e)){for(let t of e)ut(t);return Object.freeze(e)}for(let t of Object.keys(e))ut(e[t]);return Object.freeze(e)}function de(e){if(Array.isArray(e))return e.map(de);if(e!=null&&typeof e=="object"){let t={};for(let r of Object.keys(e).sort())t[r]=de(e[r]);return t}return e}function ct(e){return{netPnlUsd:e.metrics.netPnlUsd,netPnlPct:e.metrics.netPnlPct,maxDrawdownPct:e.metrics.maxDrawdownPct,winRate:e.metrics.winRate,profitFactor:e.metrics.profitFactor,tradeCount:e.metrics.tradeCount,sharpe:e.metrics.sharpe,turnoverUsd:e.metrics.turnoverUsd,exposurePct:e.metrics.exposurePct}}function rr(e){return{feesUsd:e.costs.feesUsd,builderFeesUsd:e.costs.builderFeesUsd,fundingUsd:e.costs.fundingUsd,slippageUsd:e.costs.slippageUsd}}function On(e){return(e.feesUsd||0)+(e.builderFeesUsd||0)+Math.max(0,e.fundingUsd||0)+(e.slippageUsd||0)}function vn(e){let t=0;for(let r of e.trades)r.grossPnlUsd>0&&(t+=r.grossPnlUsd);return t}function Mn(e,t,r){return!(!e||e.tradeCount<Math.max(1,Math.min(r,3))||!(e.netPnlUsd>0)||!(e.profitFactor>1)||!(e.maxDrawdownPct<=t.maxDailyLossPct))}function sr(e){if(!ee(e))throw new TypeError("evidence artifact must be a plain object");if(!or.includes(e.status))throw new TypeError("evidence artifact status is invalid");for(let t of["strategyHash","compilerHash","barsHash"])if(typeof e[t]!="string"||!/^[0-9a-f]{64}$/.test(e[t]))throw new TypeError(`evidence artifact ${t} must be a sha256 hash`);if(!ee(e.split))throw new TypeError("evidence artifact split required");if(!Array.isArray(e.flags))throw new TypeError("evidence artifact flags must be an array");if(!ee(e.walkForward)||!Array.isArray(e.walkForward.windows))throw new TypeError("evidence artifact walkForward windows required");if(e.status==="pass_live_eligible"&&(!ee(e.train)||!ee(e.holdout)))throw new TypeError("live-eligible evidence artifact requires train and holdout facts");return de({barsHash:e.barsHash,compilerHash:e.compilerHash,flags:e.flags.map(t=>de(t)),holdout:e.holdout??null,split:e.split,status:e.status,strategyHash:e.strategyHash,train:e.train??null,walkForward:{passRate:e.walkForward.passRate,windowsRun:e.walkForward.windowsRun??e.walkForward.windows.length,windows:e.walkForward.windows.map(t=>({index:t.index,trainStartIndex:t.trainStartIndex,trainEndIndex:t.trainEndIndex,passed:t.passed,reason:t.reason,evalStartIndex:t.evalStartIndex,evalEndIndex:t.evalEndIndex,metrics:t.metrics}))}})}function ft(e){return In("sha256").update(JSON.stringify(sr(e)),"utf8").digest("hex")}function G(e,t,r=0){if(!Number.isInteger(e)||e<r)throw new Error(`${t} must be an integer >= ${r}`);return e}function lt(e,t){if(!ee(e)||!ee(e.metrics))throw new Error(`${t} metrics required`);let r=G(e.tradeCount,`${t}.tradeCount`);if(e.metrics.tradeCount!==r)throw new Error(`${t} tradeCount contradicts metrics.tradeCount`);for(let n of["netPnlUsd","profitFactor","maxDrawdownPct"])if(!ge(e.metrics[n]))throw new Error(`${t}.metrics.${n} must be finite`);if(e.metrics.maxDrawdownPct<0)throw new Error(`${t}.metrics.maxDrawdownPct must be >= 0`);return e.metrics}function Rn(e,{maxDailyLossPct:t}={}){let r=e.split,n=G(r.trainStartIndex,"split.trainStartIndex"),o=G(r.trainEndIndex,"split.trainEndIndex"),a=G(r.trainBarCount,"split.trainBarCount",1),i=G(r.holdoutStartIndex,"split.holdoutStartIndex"),s=G(r.holdoutEndIndex,"split.holdoutEndIndex"),l=G(r.holdoutBarCount,"split.holdoutBarCount",1);if(n!==0||o-n+1!==a||i!==o+1||s-i+1!==l)throw new Error("evidence split must be contiguous, chronological, and count-consistent");if(!ge(r.trainFraction)||r.trainFraction<=0||r.trainFraction>=1)throw new Error("split.trainFraction must be in (0,1)");lt(e.train,"train");let u=lt(e.holdout,"holdout");if(u.tradeCount<1||u.netPnlUsd<=0||u.profitFactor<=1)throw new Error("live-eligible holdout metrics do not pass");if(ge(t)&&u.maxDrawdownPct>t)throw new Error("live-eligible holdout drawdown exceeds strategy risk");if(e.flags.length>0)throw new Error("live-eligible evidence cannot contain failure flags");let f=e.walkForward,c=f.windows;if(c.length<1||f.windowsRun!==c.length)throw new Error("live-eligible walk-forward windowsRun must match nonempty windows");let d=0,m=-1;for(let h=0;h<c.length;h++){let E=c[h];if(!ee(E)||E.index!==h||typeof E.passed!="boolean")throw new Error("walk-forward windows must be ordered and structurally valid");let k=G(E.trainStartIndex,`walkForward.windows[${h}].trainStartIndex`),P=G(E.trainEndIndex,`walkForward.windows[${h}].trainEndIndex`),T=G(E.evalStartIndex,`walkForward.windows[${h}].evalStartIndex`),x=G(E.evalEndIndex,`walkForward.windows[${h}].evalEndIndex`);if(k!==0||P>=T||T>x||T<=m)throw new Error("walk-forward windows must be chronological and disjoint");if(m=x,E.passed){if(d+=1,E.reason!=="pass")throw new Error("passed walk-forward window reason must be pass");let N=lt({metrics:E.metrics,tradeCount:E.metrics?.tradeCount},`walkForward.windows[${h}]`);if(N.tradeCount<1||N.netPnlUsd<=0||N.profitFactor<=1)throw new Error("passed walk-forward window metrics do not pass");if(ge(t)&&N.maxDrawdownPct>t)throw new Error("passed walk-forward drawdown exceeds strategy risk")}else if(E.reason==="pass")throw new Error("failed walk-forward window cannot have pass reason")}let p=d/c.length;if(!ge(f.passRate)||Math.abs(f.passRate-p)>Number.EPSILON||p<.6)throw new Error("walk-forward passRate contradicts concrete windows or is below live threshold")}function dt(e,t={}){if(typeof e?.id!="string"||!/^[0-9a-f]{64}$/.test(e.id))throw new Error("evidence.id must be a sha256 evidence artifact hash");let r=sr(e),n=de({id:e.id,...r});if(JSON.stringify(de(e))!==JSON.stringify(n))throw new Error("evidence artifact contains fields unbound from its deterministic identity");let o=ft(e);if(e.id!==o)throw new Error("evidence.id does not match its deterministic artifact facts digest");return e.status==="pass_live_eligible"&&Rn(e,t),e}function Fe({strategy:e,bars:t,backtestOptions:r={},trainFraction:n=.7,walkForwardWindows:o=3,minTrades:a=10}={}){let i=[];if(!ee(e))throw new TypeError("strategy must be a plain object");if(!Array.isArray(t))throw new TypeError("bars must be an array");if(!ee(r))throw new TypeError("backtestOptions must be a plain object");if(!ge(n)||n<=0||n>=1)throw new TypeError("trainFraction must be in (0,1)");if(!Number.isInteger(o)||o<1)throw new TypeError("walkForwardWindows must be a positive integer");if(!Number.isInteger(a)||a<0)throw new TypeError("minTrades must be an integer >= 0");let s=V(e),l=fe(s),u=$e(t),f=t.length,c=(I=[],M={})=>nr({status:"fail",strategy:s,sHash:l,barsHash:u,split:M.split||{trainStartIndex:0,trainEndIndex:-1,trainBarCount:0,holdoutStartIndex:0,holdoutEndIndex:-1,holdoutBarCount:0,trainFraction:n},train:M.train||null,holdout:M.holdout||null,walkForward:M.walkForward||{windows:[],passRate:0},flags:[...i,...I]});if(f<10)return c([{type:"insufficient_bars",message:"need at least 10 bars for evidence"}]);let d=Math.floor(f*n),m=d,p=f-m;if(d<5||p<3)return c([{type:"invalid_windowing",message:"train/holdout split too small",trainCount:d,holdoutCount:p}]);let h=t.slice(0,d),E=t.slice(m),k,P;try{k=ne(s,h,r),P=ne(s,E,r)}catch(I){return c([{type:"backtest_error",message:String(I.message||I)}])}let T={metrics:ct(k),costs:rr(k),tradeCount:k.metrics.tradeCount},x={metrics:ct(P),costs:rr(P),tradeCount:P.metrics.tradeCount},N={trainStartIndex:0,trainEndIndex:d-1,trainBarCount:d,holdoutStartIndex:m,holdoutEndIndex:f-1,holdoutBarCount:p,trainFraction:n},_=[],w=Math.floor(f/(o+1));if(w<3)i.push({type:"invalid_windowing",message:"walk-forward window size too small",wfSize:w});else for(let I=0;I<o;I++){let M=w*(I+1),D=M,C=Math.min(f,D+w);if(C-D<3||M<3){_.push({index:I,trainStartIndex:0,trainEndIndex:M-1,evalStartIndex:D,evalEndIndex:C-1,passed:!1,reason:"window_too_small",metrics:null});continue}let Z=t.slice(D,C),Me;try{Me=ne(s,Z,r)}catch{_.push({index:I,trainStartIndex:0,trainEndIndex:M-1,evalStartIndex:D,evalEndIndex:C-1,passed:!1,reason:"backtest_error",metrics:null});continue}let Re=ct(Me),Ne=Mn(Re,s.risk,a);_.push({index:I,trainStartIndex:0,trainEndIndex:M-1,evalStartIndex:D,evalEndIndex:C-1,passed:Ne,reason:Ne?"pass":"fail_metrics",metrics:Re})}let J=_.filter(I=>I.passed).length,se=_.length===0?0:J/_.length,ie={windows:_,passRate:se,windowsRun:_.length};x.metrics.tradeCount===0&&T.metrics.tradeCount===0&&i.push({type:"no_trades",message:"no trades in train or holdout"}),x.metrics.tradeCount===0&&i.push({type:"absent_oos_trades",message:"holdout produced no trades"}),x.metrics.netPnlUsd<0&&i.push({type:"holdout_loss",message:"holdout netPnlUsd is negative"}),T.metrics.netPnlUsd>0&&x.metrics.netPnlUsd<0&&T.metrics.netPnlUsd>Math.abs(x.metrics.netPnlUsd)*.25?i.push({type:"overfit",message:"profitable train with losing holdout (OOS collapse)"}):T.metrics.netPnlUsd>0&&x.metrics.netPnlUsd>0&&x.metrics.netPnlUsd<T.metrics.netPnlUsd*.1&&T.metrics.tradeCount>=a&&i.push({type:"severe_oos_collapse",message:"holdout pnl collapsed vs train"});let X=On(x.costs),y=vn(P);y>0&&X>y&&i.push({type:"costs_exceed_gross",message:"holdout costs exceed gross gains"});let A=i.some(I=>["insufficient_bars","invalid_windowing","absent_oos_trades","holdout_loss","no_trades","severe_oos_collapse","backtest_error"].includes(I.type)),v=i.some(I=>I.type==="overfit"),L="fail";return x.metrics.tradeCount>=a&&x.metrics.netPnlUsd>0&&x.metrics.profitFactor>1&&x.metrics.maxDrawdownPct<=s.risk.maxDailyLossPct&&se>=.6&&!(y>0&&X>y)&&!v&&!i.some(I=>I.type==="severe_oos_collapse"||I.type==="invalid_windowing")?L="pass_live_eligible":!A&&x.metrics.tradeCount>0&&T.metrics.tradeCount>0&&Number.isFinite(x.metrics.netPnlUsd)?(L="pass_paper_only",x.metrics.tradeCount<a&&i.push({type:"undersampled",message:"holdout trades below minTrades"})):v&&x.metrics.tradeCount>0&&T.metrics.netPnlUsd>0?L="fail":x.metrics.tradeCount>0&&T.metrics.tradeCount>0&&!A?L="pass_paper_only":L="fail",x.metrics.netPnlUsd<0&&(L=L==="pass_live_eligible"?"fail":L==="pass_paper_only"?"pass_paper_only":"fail",L="fail"),nr({status:L,strategy:s,sHash:l,barsHash:u,split:N,train:T,holdout:x,walkForward:ie,flags:i})}function nr({status:e,strategy:t,sHash:r,barsHash:n,split:o,train:a,holdout:i,walkForward:s,flags:l}){let u={status:e,strategyHash:r,compilerHash:ce,barsHash:n,split:o,train:a,holdout:i,walkForward:s,flags:l};return u.id=ft(u),ut(de(JSON.parse(JSON.stringify(u))))}import{createHash as Nn,createHmac as Ln,timingSafeEqual as As}from"node:crypto";var jn=2,Fn=300*1e3,Ps=(()=>{let e=Number(process.env.ORACLE_PREPARE_HARD_MAX_AGE_MS);return Number.isFinite(e)&&e>0?e:1800*1e3})();function pt(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?`[${e.map(pt).join(",")}]`:`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${pt(e[r])}`).join(",")}}`}function ir(e){let t=String(e?.ORACLE_STAMP_HMAC_SECRET||"").trim();return t.length>=16?t:""}function ar(e=process.env){return ir(process.env)||ir(e)}function Hn(e){let{oraclePrepared:t,prepareHash:r,prepareMac:n,note:o,...a}=e||{};return a}function Un(e){let t=Hn(e);return Nn("sha256").update(pt(t)).digest("hex")}function Cn(e,t=ar()){return t?Ln("sha256",t).update(String(e)).digest("hex"):""}function cr(e,{provider:t,kind:r,env:n=process.env}={}){if(!e||typeof e!="object")throw new Error("stampPrepared: payload object required");let o={...e,provider:e.provider||t,kind:e.kind||r,requiresUserSignature:e.requiresUserSignature!==!1,signingReady:!1,broadcastReady:!1,executionReady:!1};if(!o.provider)throw new Error("stampPrepared: provider required");if(!o.kind)throw new Error("stampPrepared: kind required");let a=Date.now(),i=a+Fn,l={...o,preparedAt:a,expiresAt:i,prepareVersion:jn},u=Un(l),f=ar(n),c=f?Cn(u,f):void 0,d={...l,oraclePrepared:!0,prepareHash:u};return c&&(d.prepareMac=c),d}var qn=["privatekey","secretkey","seed","mnemonic","passphrase","password","keystore","keymaterial","apikey","authtoken","accesstoken","refreshtoken","credential","bearer","signature","authorization","xprv","xpriv","wif"];function _e(e){return e!=null&&typeof e=="object"&&!Array.isArray(e)}function Bn(e){return String(e).toLowerCase().replace(/[^a-z0-9]/g,"")}function ht(e,t="value"){if(Array.isArray(e)){e.forEach((r,n)=>ht(r,`${t}[${n}]`));return}if(_e(e))for(let[r,n]of Object.entries(e)){let o=Bn(r);if(qn.some(a=>o.includes(a)))throw new Error(`handoff: forbidden secret-like field at ${t}.${r}`);ht(n,`${t}.${r}`)}}function mt(e){return typeof e=="number"&&Number.isFinite(e)&&e>0}function yt({strategy:e,evidence:t,shadow:r,caps:n,nowMs:o=Date.now(),env:a=process.env}={}){if(ht({strategy:e,evidence:t,shadow:r,caps:n},"handoff"),!_e(e))throw new Error("handoff: strategy required");if(!_e(t))throw new Error("handoff: evidence required");if(!_e(r))throw new Error("handoff: shadow required");if(!_e(n))throw new Error("handoff: caps required");if(typeof r.id!="string"||!r.id.trim())throw new Error("handoff: shadow.id required");if(typeof r.stateHash!="string"||!/^[0-9a-f]{64}$/.test(r.stateHash))throw new Error("handoff: shadow.stateHash must be a sha256 hash");if(r.status!=="running")throw new Error("handoff: shadow.status must be running");if(!Number.isInteger(r.cursor)||r.cursor<=0)throw new Error("handoff: shadow.cursor must be a positive integer");if(!Number.isInteger(r.updatedAt)||r.updatedAt<=0||r.updatedAt>o)throw new Error("handoff: shadow.updatedAt must be a positive integer <= nowMs");let i=V(e,{nowMs:o}),s=z(i);if(dt(t,{maxDailyLossPct:i.risk.maxDailyLossPct}),t.status!=="pass_live_eligible")throw new Error("handoff: evidence.status must be pass_live_eligible");if(t.strategyHash!==s.strategyHash)throw new Error("handoff: evidence.strategyHash mismatch");if(t.compilerHash!==s.compilerHash)throw new Error("handoff: evidence.compilerHash mismatch");let l=["perStrategyNotionalUsd","maxLeverage","dailyLossCapUsd","chainAllowlist","assetAllowlist","expiresAt","killSwitchRequired"];for(let d of l)if(!(d in n)||n[d]==null)throw new Error(`handoff: caps.${d} required`);if(!mt(n.perStrategyNotionalUsd))throw new Error("handoff: caps.perStrategyNotionalUsd must be positive");if(n.perStrategyNotionalUsd>s.strategy.risk.maxNotionalUsd)throw new Error("handoff: caps.perStrategyNotionalUsd exceeds strategy.risk.maxNotionalUsd");if(!mt(n.maxLeverage))throw new Error("handoff: caps.maxLeverage must be positive");if(n.maxLeverage>s.strategy.risk.maxLeverage)throw new Error("handoff: caps.maxLeverage exceeds strategy.risk.maxLeverage");if(!mt(n.dailyLossCapUsd))throw new Error("handoff: caps.dailyLossCapUsd must be positive");let u=s.strategy.risk.maxNotionalUsd*(s.strategy.risk.maxDailyLossPct/100);if(n.dailyLossCapUsd>u)throw new Error("handoff: caps.dailyLossCapUsd exceeds strategy daily loss ceiling");if(!Array.isArray(n.chainAllowlist)||n.chainAllowlist.length===0)throw new Error("handoff: caps.chainAllowlist must be a non-empty array");if(!n.chainAllowlist.every(d=>d==="hyperliquid"))throw new Error("handoff: caps.chainAllowlist must include only hyperliquid");if(!n.chainAllowlist.includes("hyperliquid"))throw new Error("handoff: caps.chainAllowlist must include hyperliquid");if(!Array.isArray(n.assetAllowlist)||n.assetAllowlist.length===0)throw new Error("handoff: caps.assetAllowlist must be a non-empty array");if(n.assetAllowlist.length!==1||n.assetAllowlist[0]!==s.strategy.market.coin)throw new Error("handoff: caps.assetAllowlist must contain only the strategy market coin");if(!Number.isInteger(n.expiresAt)||!(n.expiresAt>o))throw new Error("handoff: caps.expiresAt must be an integer > nowMs");if(n.expiresAt>s.strategy.risk.expiresAt)throw new Error("handoff: caps.expiresAt exceeds strategy.risk.expiresAt");if(n.killSwitchRequired!==!0)throw new Error("handoff: caps.killSwitchRequired must be exactly true");let f={perStrategyNotionalUsd:n.perStrategyNotionalUsd,maxLeverage:n.maxLeverage,dailyLossCapUsd:n.dailyLossCapUsd,chainAllowlist:[...n.chainAllowlist],assetAllowlist:[...n.assetAllowlist],expiresAt:n.expiresAt,killSwitchRequired:!0},c={strategyHash:s.strategyHash,compilerHash:s.compilerHash,evidence:JSON.parse(JSON.stringify(t)),shadow:{id:r.id,stateHash:r.stateHash,status:r.status,cursor:r.cursor,updatedAt:r.updatedAt},compiled:{strategyHash:s.strategyHash,compilerHash:s.compilerHash,compilerVersion:he,venue:"hyperliquid",market:{coin:s.strategy.market.coin,kind:"perp",interval:s.strategy.market.interval},risk:{...s.strategy.risk},strategy:JSON.parse(JSON.stringify(s.strategy))},arming:{liveBroadcast:!1,requiresExplicitUserArm:!0,caps:f},requiresUserSignature:!0,signingReady:!1,broadcastReady:!1,executionReady:!1};return cr(c,{provider:"hl-strategy",kind:"strategy-live-handoff",env:a})}var Dn=Object.freeze({"1m":6e4,"3m":18e4,"5m":3e5,"15m":9e5,"30m":18e5,"1h":36e5,"2h":72e5,"4h":144e5,"8h":288e5,"12h":432e5,"1d":864e5});function we(e,t){let r=Number(e);if(!Number.isFinite(r))throw new TypeError(`${t} must be finite`);return r}function be(e,t){if(!Number.isSafeInteger(e)||e<0)throw new TypeError(`${t} must be a non-negative safe integer`);return e}function Ie(e){if(Array.isArray(e))for(let t of e)Ie(t);else if(e&&typeof e=="object")for(let t of Object.values(e))Ie(t);return Object.freeze(e)}function lr(e){let t=Dn[e];if(!t)throw new TypeError(`unsupported strategy interval: ${e}`);return t}function ur(e,{endTime:t=Number.MAX_SAFE_INTEGER}={}){if(!Array.isArray(e))throw new TypeError("Hyperliquid candles must be an array");be(t,"endTime");let r=new Map;for(let n=0;n<e.length;n++){let o=e[n];if(!o||typeof o!="object"||Array.isArray(o))throw new TypeError(`candles[${n}] must be an object`);let a=be(Number(o.t),`candles[${n}].t`);if(be(Number(o.T),`candles[${n}].T`)>t)continue;let s={t:a,o:we(o.o,`candles[${n}].o`),h:we(o.h,`candles[${n}].h`),l:we(o.l,`candles[${n}].l`),c:we(o.c,`candles[${n}].c`),v:we(o.v,`candles[${n}].v`)};if(s.h<Math.max(s.o,s.c,s.l))throw new TypeError(`candles[${n}].h is below another price`);if(s.l>Math.min(s.o,s.c,s.h))throw new TypeError(`candles[${n}].l is above another price`);if(s.v<0)throw new TypeError(`candles[${n}].v must be non-negative`);let l=r.get(a);if(l&&JSON.stringify(l)!==JSON.stringify(s))throw new TypeError(`conflicting duplicate candle at timestamp ${a}`);l||r.set(a,s)}return Ie([...r.values()].sort((n,o)=>n.t-o.t))}function zn(e,t){if(!Array.isArray(e))throw new TypeError("Hyperliquid funding history must be an array");let r=new Map;for(let n=0;n<e.length;n++){let o=e[n];if(!o||typeof o!="object"||Array.isArray(o))throw new TypeError(`funding[${n}] must be an object`);let a=be(Number(o.time),`funding[${n}].time`);a>t||r.set(a,{time:a,fundingRate:we(o.fundingRate,`funding[${n}].fundingRate`)})}return[...r.values()].sort((n,o)=>n.time-o.time)}async function Vn({coin:e,startTime:t,endTime:r},n){let o=[],a=Math.max(0,t-288e5),i=!1;for(let s=0;s<16&&a<=r;s++){let l=await Zt({coin:e,startTime:a,endTime:r},n);if(!Array.isArray(l))throw new TypeError("Hyperliquid funding history must be an array");o.push(...l);let u=a;for(let f of l){let c=Number(f?.time);Number.isSafeInteger(c)&&c>u&&(u=c)}if(l.length<500||u>=r){i=!0;break}if(u<=a)throw new Error("Hyperliquid funding history pagination did not advance");a=u+1}if(!i)throw new Error("strategy history exceeds the bounded funding history limit");return zn(o,r)}function Jn(e,t,r){let n=0,o=null;return e.map(a=>{let i=a.t+r,s=0;for(;n<t.length&&t[n].time<i;){let l=t[n];o=l.fundingRate,l.time>=a.t&&(s+=l.fundingRate),n+=1}return o==null?{...a,fundingPaymentRate:s}:{...a,fundingRate:o,fundingPaymentRate:s}})}async function gt(e,t={}){if(!e||typeof e!="object"||Array.isArray(e))throw new TypeError("strategy history input must be an object");let r=String(e.coin||"").trim().toUpperCase();if(!/^[A-Z0-9][A-Z0-9._-]{0,11}$/.test(r))throw new TypeError("coin must be an uppercase Hyperliquid asset id");let n=String(e.interval||"15m"),o=lr(n);if(e.endTime==null)throw new TypeError("endTime is required for deterministic history");let a=be(e.endTime,"endTime"),i=e.count==null?1500:e.count;if(!Number.isInteger(i)||i<20||i>5e3)throw new TypeError("count must be an integer from 20 to 5000");let s=e.startTime==null?Math.max(0,a-i*o):be(e.startTime,"startTime");if(s>=a)throw new TypeError("startTime must be before endTime");let[l,u]=await Promise.all([Xt({coin:r,interval:n,startTime:s,endTime:a},t),Vn({coin:r,startTime:s,endTime:a},t)]),f=ur(l,{endTime:a}),c=Ie(Jn(f,u,o));return Ie({coin:r,interval:n,range:{startTime:s,endTime:a},bars:c})}var Kn=new Set(["1m","3m","5m","15m","30m","1h","2h","4h","8h","12h","1d"]),Yn=new Set(["coin","interval"]);function Gn(e){let t=[];if(!e||typeof e!="object"||Array.isArray(e))return{ok:!1,errors:[{path:"market",message:"must be a plain object"}],market:null};for(let r of Object.keys(e))Yn.has(r)||t.push({path:`market.${r}`,message:"unknown field is not allowed"});return(typeof e.coin!="string"||e.coin.length===0||e.coin.length>32)&&t.push({path:"market.coin",message:"must be a non-empty string max 32"}),(typeof e.interval!="string"||!Kn.has(e.interval))&&t.push({path:"market.interval",message:"must be a supported interval"}),t.length?{ok:!1,errors:t,market:null}:{ok:!0,errors:[],market:{coin:e.coin,interval:e.interval}}}async function Xn(e={}){let t=await Gt(e);if(!t||!Array.isArray(t.universe))throw new TypeError("Hyperliquid market metadata must contain universe");return Object.freeze(t.universe.map((r,n)=>{if(!r||typeof r.name!="string"||!r.name)throw new TypeError(`Hyperliquid market metadata universe[${n}].name is invalid`);return r.name}))}var fr=Object.freeze({id:"hyperliquid",validateMarket:Gn,fetchBars:gt,listMarkets:Xn,prepareIntent(e){return yt(e)}});var Zn=Object.freeze(["id","validateMarket","prepareIntent"]);function Wn(e){if(!e||typeof e!="object"||Array.isArray(e))throw new TypeError("strategy venue adapter must be an object");if(typeof e.id!="string"||!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(e.id))throw new TypeError("strategy venue adapter id is malformed");for(let t of Zn.slice(1))if(typeof e[t]!="function")throw new TypeError(`strategy venue adapter ${e.id} requires ${t}()`);for(let t of["fetchBars","listMarkets"])if(e[t]!=null&&typeof e[t]!="function")throw new TypeError(`strategy venue adapter ${e.id} ${t} must be a function`);return e}var dr=[fr].map(Wn),Qn=new Map(dr.map(e=>[e.id,e])),eo=Object.freeze([...dr]);function He(e){return typeof e!="string"?null:Qn.get(e)??null}function Ue(e){let t=He(e);if(!t)throw new Error(`unknown strategy venue: ${String(e)}`);return t}var Ce=1,Sr=/^[a-z0-9][a-z0-9._-]{0,63}$/,kr=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,ro=new Set(["open","high","low","close","volume","fundingRate","openInterest"]),no=new Set(["sma","ema","rsi","macd","bollinger","atr"]),oo=new Set(["gt","gte","lt","lte","eq"]),so=new Set(["and","or","not"]),io=new Set(["above","below"]),ao=new Set(["line","signal","histogram"]),co=new Set(["upper","middle","lower"]),wt=new Set(["compare","logic","cross"]),pr=new Set(["version","id","name","venue","market","parameters","nodes","rules","risk"]),mr=new Set(["value","min","max","step"]),bt=new Set(["entryLong","entryShort","exitLong","exitShort"]),hr=new Set(["maxLeverage","maxNotionalUsd","positionSizePct","stopLossPct","takeProfitPct","cooldownBars","maxDailyLossPct","expiresAt"]),lo=new Set(["id","type"]),yr={input:new Set(["id","type","field"]),constant:new Set(["id","type","value"]),indicator:new Set(["id","type","indicator","input","period","fastPeriod","slowPeriod","signalPeriod","stdDev","output"]),compare:new Set(["id","type","op","left","right"]),logic:new Set(["id","type","op","inputs"]),cross:new Set(["id","type","direction","left","right"])},gr=16,wr=128,br=100,pe=class extends Error{constructor(t){let r=t.map(n=>`${n.path}: ${n.message}`);super(`invalid strategy (${t.length} error${t.length===1?"":"s"}): ${r.join("; ")}`),this.name="StrategyValidationError",this.errors=t}};function K(e){return e!=null&&typeof e=="object"&&!Array.isArray(e)}function U(e){return typeof e=="number"&&Number.isFinite(e)}function Er(e){return Number.isInteger(e)&&e>0}function xt(e){if(e==null||typeof e!="object"||Object.isFrozen(e))return e;if(Array.isArray(e)){for(let t of e)xt(t);return Object.freeze(e)}for(let t of Object.keys(e))xt(e[t]);return Object.freeze(e)}function St(e){if(Array.isArray(e))return e.map(St);if(e!=null&&typeof e=="object"){let t={};for(let r of Object.keys(e).sort())t[r]=St(e[r]);return t}return e}function g(e,t,r){e.push({path:t,message:r})}function le(e,t,r,n){for(let o of Object.keys(e))t.has(o)||g(n,r?`${r}.${o}`:o,"unknown field is not allowed")}function Ar(e,t,r,n){if(!U(e)||!U(t)||!U(r)||!U(n)||n<=0||e<t||e>r)return!1;let o=(e-t)/n;if(!Number.isFinite(o))return!1;let a=Math.round(o);return Math.abs(o-a)<=1e-9}function Et(e,t,r,n){if(K(e)){if(le(e,new Set(["param"]),t,n),typeof e.param!="string"||!e.param)return g(n,`${t}.param`,"must be a parameter name string"),null;if(!Object.prototype.hasOwnProperty.call(r,e.param))return g(n,`${t}.param`,`unknown parameter "${e.param}"`),null;let o=r[e.param];return!K(o)||![o.value,o.min,o.max,o.step].every(Er)?(g(n,`${t}.param`,"referenced period parameter value min max and step must be positive integers"),null):{param:e.param}}return Er(e)?e:(g(n,t,"must be a positive integer or {param}"),null)}function xr(e,t,r,n){return K(e)?(le(e,new Set(["param"]),t,n),typeof e.param!="string"||!e.param?(g(n,`${t}.param`,"must be a parameter name string"),null):Object.prototype.hasOwnProperty.call(r,e.param)?{param:e.param}:(g(n,`${t}.param`,`unknown parameter "${e.param}"`),null)):U(e)?e:(g(n,t,"must be a finite number or {param}"),null)}function qe(e){switch(e.type){case"input":case"constant":return[];case"indicator":return e.input?[e.input]:[];case"compare":case"cross":return[e.left,e.right].filter(Boolean);case"logic":return Array.isArray(e.inputs)?[...e.inputs]:[];default:return[]}}function uo(e){let t=new Map,r=n=>{let o=t.get(n)||0;if(o===1)return!0;if(o===2)return!1;t.set(n,1);let a=e.get(n);if(a){for(let i of qe(a))if(e.has(i)&&r(i))return!0}return t.set(n,2),!1};for(let n of e.keys())if(r(n))return!0;return!1}function fo(e,t){if(!K(e))return g(t,"parameters","must be a plain object"),null;let r=Object.keys(e);if(r.length>gr)return g(t,"parameters",`too many parameters (max ${gr})`),null;let n={};for(let o of r){if(!Sr.test(o)){g(t,`parameters.${o}`,"parameter name is malformed");continue}let a=e[o],i=`parameters.${o}`;if(!K(a)){g(t,i,"must be a plain object");continue}le(a,mr,i,t);for(let c of mr)c in a||g(t,`${i}.${c}`,"required field is missing");let{value:s,min:l,max:u,step:f}=a;U(l)||g(t,`${i}.min`,"must be a finite number"),U(u)||g(t,`${i}.max`,"must be a finite number"),(!U(f)||f<=0)&&g(t,`${i}.step`,"must be a positive finite number"),U(s)||g(t,`${i}.value`,"must be a finite number"),U(l)&&U(u)&&l>u&&g(t,`${i}.min`,"min must be <= max"),U(s)&&U(l)&&U(u)&&U(f)&&f>0&&(Ar(s,l,u,f)||g(t,`${i}.value`,"value must lie on the declared min/max/step grid")),n[o]={value:s,min:l,max:u,step:f}}return n}function po(e,t,r,n){let o=`nodes.${t}`;if(!K(e))return g(n,o,"must be a plain object"),null;if(typeof e.type!="string"||!yr[e.type])return g(n,`${o}.type`,"unknown or missing node type"),le(e,lo,o,n),null;switch(le(e,yr[e.type],o,n),(typeof e.id!="string"||!kr.test(e.id))&&g(n,`${o}.id`,"must be a 1-64 char id starting with alphanumeric"),e.type){case"input":return(typeof e.field!="string"||!ro.has(e.field))&&g(n,`${o}.field`,"must be one of open|high|low|close|volume|fundingRate|openInterest"),{id:e.id,type:"input",field:e.field};case"constant":{let a=xr(e.value,`${o}.value`,r,n);return{id:e.id,type:"constant",value:a}}case"indicator":{(typeof e.indicator!="string"||!no.has(e.indicator))&&g(n,`${o}.indicator`,"must be one of sma|ema|rsi|macd|bollinger|atr");let a=e.indicator,i={id:e.id,type:"indicator",indicator:a};if(a==="atr"){"input"in e&&(typeof e.input!="string"||!e.input?g(n,`${o}.input`,"must be a node id string when provided"):i.input=e.input);let s=Et(e.period,`${o}.period`,r,n);s!=null&&(i.period=s)}else typeof e.input!="string"||!e.input?g(n,`${o}.input`,"must be a node id string"):i.input=e.input;if((a==="sma"||a==="ema"||a==="rsi"||a==="bollinger"||a==="atr")&&("period"in e,a!=="macd"))if(!("period"in e))g(n,`${o}.period`,"required field is missing");else{let s=Et(e.period,`${o}.period`,r,n);s!=null&&(i.period=s)}if(a==="macd"){for(let s of["fastPeriod","slowPeriod","signalPeriod"])if(!(s in e))g(n,`${o}.${s}`,"required field is missing");else{let l=Et(e[s],`${o}.${s}`,r,n);l!=null&&(i[s]=l)}"output"in e?ao.has(e.output)?i.output=e.output:g(n,`${o}.output`,"must be line|signal|histogram"):g(n,`${o}.output`,"required field is missing"),"period"in e&&g(n,`${o}.period`,"forbidden for macd"),"stdDev"in e&&g(n,`${o}.stdDev`,"forbidden for macd")}else if(a==="bollinger"){if(!("stdDev"in e))g(n,`${o}.stdDev`,"required field is missing");else{let s=xr(e.stdDev,`${o}.stdDev`,r,n);s!=null&&(typeof s=="number"&&s<=0?g(n,`${o}.stdDev`,"must be > 0"):i.stdDev=s)}"output"in e?co.has(e.output)?i.output=e.output:g(n,`${o}.output`,"must be upper|middle|lower"):g(n,`${o}.output`,"required field is missing");for(let s of["fastPeriod","slowPeriod","signalPeriod"])s in e&&g(n,`${o}.${s}`,`forbidden for ${a}`)}else{"output"in e&&g(n,`${o}.output`,`forbidden for ${a}`),"stdDev"in e&&a!=="bollinger"&&g(n,`${o}.stdDev`,`forbidden for ${a}`);for(let s of["fastPeriod","slowPeriod","signalPeriod"])s in e&&g(n,`${o}.${s}`,`forbidden for ${a}`)}return i}case"compare":return(typeof e.op!="string"||!oo.has(e.op))&&g(n,`${o}.op`,"must be gt|gte|lt|lte|eq"),(typeof e.left!="string"||!e.left)&&g(n,`${o}.left`,"must be a node id"),(typeof e.right!="string"||!e.right)&&g(n,`${o}.right`,"must be a node id"),{id:e.id,type:"compare",op:e.op,left:e.left,right:e.right};case"logic":{if((typeof e.op!="string"||!so.has(e.op))&&g(n,`${o}.op`,"must be and|or|not"),!Array.isArray(e.inputs))return g(n,`${o}.inputs`,"must be an array of node ids"),{id:e.id,type:"logic",op:e.op,inputs:[]};let a=[];for(let i=0;i<e.inputs.length;i++){let s=e.inputs[i];typeof s!="string"||!s?g(n,`${o}.inputs.${i}`,"must be a node id"):a.push(s)}return e.op==="not"?a.length!==1&&g(n,`${o}.inputs`,"not requires exactly one input"):(e.op==="and"||e.op==="or")&&a.length<2&&g(n,`${o}.inputs`,`${e.op} requires at least two inputs`),{id:e.id,type:"logic",op:e.op,inputs:a}}case"cross":return(typeof e.direction!="string"||!io.has(e.direction))&&g(n,`${o}.direction`,"must be above|below"),(typeof e.left!="string"||!e.left)&&g(n,`${o}.left`,"must be a node id"),(typeof e.right!="string"||!e.right)&&g(n,`${o}.right`,"must be a node id"),{id:e.id,type:"cross",direction:e.direction,left:e.left,right:e.right};default:return null}}function mo(e,t,r){if(!K(e))return g(t,"risk","must be a plain object"),null;le(e,hr,"risk",t);for(let d of hr)(!(d in e)||e[d]==null)&&g(t,`risk.${d}`,"required field is missing");let n={},o=e.maxLeverage;!U(o)||o<=0||o>50?g(t,"risk.maxLeverage","must be a number > 0 and <= 50"):n.maxLeverage=o;let a=e.maxNotionalUsd;!U(a)||a<=0?g(t,"risk.maxNotionalUsd","must be a number > 0"):n.maxNotionalUsd=a;let i=e.positionSizePct;!U(i)||i<=0||i>100?g(t,"risk.positionSizePct","must be a number > 0 and <= 100"):n.positionSizePct=i;let s=e.stopLossPct;!U(s)||s<=0||s>100?g(t,"risk.stopLossPct","must be a number > 0 and <= 100"):n.stopLossPct=s;let l=e.takeProfitPct;!U(l)||l<=0||l>1e3?g(t,"risk.takeProfitPct","must be a number > 0 and <= 1000"):n.takeProfitPct=l;let u=e.cooldownBars;!Number.isInteger(u)||u<0||u>1e5?g(t,"risk.cooldownBars","must be an integer >= 0 and <= 100000"):n.cooldownBars=u;let f=e.maxDailyLossPct;!U(f)||f<=0||f>100?g(t,"risk.maxDailyLossPct","must be a number > 0 and <= 100"):n.maxDailyLossPct=f;let c=e.expiresAt;return!Number.isInteger(c)||c<=0?g(t,"risk.expiresAt","must be an integer epoch milliseconds > 0"):(r.nowMs!==void 0&&(Number.isInteger(r.nowMs)?c<=r.nowMs&&g(t,"risk.expiresAt","must be strictly greater than nowMs"):g(t,"nowMs","must be an integer epoch milliseconds")),n.expiresAt=c),n}function Be(e,t={}){let r=[];if(!K(e))return g(r,"","strategy must be a plain object"),{ok:!1,errors:r,strategy:null};le(e,pr,"",r);for(let c of pr)(!(c in e)||e[c]==null)&&g(r,c,"required field is missing");e.version!==Ce&&g(r,"version",`must be ${Ce}`),(typeof e.id!="string"||!Sr.test(e.id))&&g(r,"id","must match /^[a-z0-9][a-z0-9._-]{0,63}$/"),(typeof e.name!="string"||e.name.length===0||e.name.length>br)&&g(r,"name",`must be a non-empty string max ${br}`);let n=He(e.venue);n||g(r,"venue",`unknown strategy venue "${String(e.venue)}"`);let o=null;if(!n)K(e.market)||g(r,"market","must be a plain object");else{let c=n.validateMarket(e.market);if(!c||typeof c.ok!="boolean"||!Array.isArray(c.errors))g(r,"market",`venue ${n.id} returned an invalid market validation result`);else if(c.ok)o=c.market;else for(let d of c.errors)g(r,d.path,d.message)}let a=fo(e.parameters,r),i=null,s=new Map;if(!Array.isArray(e.nodes))g(r,"nodes","must be an array");else if(e.nodes.length>wr)g(r,"nodes",`too many nodes (max ${wr})`);else{i=[];for(let c=0;c<e.nodes.length;c++){let d=po(e.nodes[c],c,a||{},r);d&&(typeof d.id=="string"&&kr.test(d.id)&&(s.has(d.id)?g(r,`nodes.${c}.id`,`duplicate node id "${d.id}"`):s.set(d.id,d)),i.push(d))}for(let c=0;c<i.length;c++){let d=i[c],m=`nodes.${c}`;for(let p of qe(d))s.has(p)||g(r,m,`unknown node ref "${p}"`);if(d.type==="indicator"||d.type==="compare"||d.type==="cross")for(let p of qe(d)){let h=s.get(p);h&&wt.has(h.type)&&g(r,m,`node ref "${p}" must be numeric`)}if(d.type==="logic")for(let p of qe(d)){let h=s.get(p);h&&!wt.has(h.type)&&g(r,m,`node ref "${p}" must be boolean`)}}uo(s)&&g(r,"nodes","graph contains a cycle")}let l=null;if(!K(e.rules))g(r,"rules","must be a plain object");else{le(e.rules,bt,"rules",r);for(let c of bt)c in e.rules||g(r,`rules.${c}`,"required field is missing");l={};for(let c of bt){let d=e.rules[c];if(d===null){l[c]=null;continue}if(typeof d!="string"||!d){g(r,`rules.${c}`,"must be a node id or null"),l[c]=d;continue}if(!s.has(d)){g(r,`rules.${c}`,`unknown node ref "${d}"`),l[c]=d;continue}let m=s.get(d);wt.has(m.type)||g(r,`rules.${c}`,`rule must reference a boolean node, not ${m.type}`),l[c]=d}}let u=mo(e.risk,r,t);if(r.length)return{ok:!1,errors:r,strategy:null};let f=xt({version:Ce,id:e.id,name:e.name,venue:n.id,market:o,parameters:a,nodes:i,rules:l,risk:u});return{ok:!0,errors:[],strategy:f}}function V(e,t={}){let r=Be(e,t);if(!r.ok)throw new pe(r.errors);return r.strategy}function Pr(e){let t=V(e);return JSON.stringify(St(t))}function fe(e){let t=Pr(e);return to("sha256").update(t,"utf8").digest("hex")}function ye(e,t){if(!K(e))throw new pe([{path:"",message:"strategy must be a plain object"}]);if(!K(t))throw new pe([{path:"overrides",message:"must be a plain object"}]);let r=V(e),n={};for(let[a,i]of Object.entries(r.parameters))n[a]={value:i.value,min:i.min,max:i.max,step:i.step};let o=[];for(let[a,i]of Object.entries(t)){if(!Object.prototype.hasOwnProperty.call(n,a)){g(o,a,`unknown parameter "${a}"`);continue}let s=n[a];if(!U(i)){g(o,a,"override must be a finite number");continue}if(!Ar(i,s.min,s.max,s.step)){g(o,a,"override must lie on the declared min/max/step grid");continue}n[a]={...s,value:i}}if(o.length)throw new pe(o);return V({version:r.version,id:r.id,name:r.name,venue:r.venue,market:{coin:r.market.coin,interval:r.market.interval},parameters:n,nodes:r.nodes.map(a=>{let i={...a};return Array.isArray(a.inputs)&&(i.inputs=[...a.inputs]),i}),rules:{...r.rules},risk:{...r.risk}})}import{createHash as ho}from"node:crypto";var At=256,$r=new Set(["netPnlUsd","profitFactor","sharpe","maxDrawdownPct"]);function Tr(e){let t=String(e).toLowerCase();return t==="holdout"||t==="test"||t==="oos"||t.includes("holdout")||t.includes("oos")?!0:t==="testbars"||t.endsWith("testbars")||t.startsWith("test")||t.endsWith("test")?!t.includes("backtest"):!1}function _r(e){return e!=null&&typeof e=="object"&&!Array.isArray(e)}function Pt(e){if(e==null||typeof e!="object"||Object.isFrozen(e))return e;if(Array.isArray(e)){for(let t of e)Pt(t);return Object.freeze(e)}for(let t of Object.keys(e))Pt(e[t]);return Object.freeze(e)}function Oe(e){if(Array.isArray(e))return e.map(Oe);if(e!=null&&typeof e=="object"){let t={};for(let r of Object.keys(e).sort())t[r]=Oe(e[r]);return t}return e}function De(e){return JSON.stringify(Oe(e))}function yo(e){let{min:t,max:r,step:n}=e;if(!(n>0)||t>r)return[];let o=[],a=Math.floor((r-t)/n+1e-9);for(let s=0;s<=a;s++){let l=t+s*n;if(l>r+1e-9)break;let u=Math.abs(l-Math.round(l))<1e-9?Math.round(l):Number(l.toPrecision(12));u<t-1e-9||u>r+1e-9||o.push(u)}if(o.length===0)return[];let i=o[o.length-1];if(Math.abs(i-r)>1e-9){let s=(r-t)/n;Math.abs(s-Math.round(s))<=1e-9&&o.push(r)}return o}function go(e,t){if(e.length===0)return[];let r=[{}];for(let n of e){let o=t[n],a=[];for(let i of r)for(let s of o)a.push({...i,[n]:s});r=a}return r.sort((n,o)=>{let a=De(n),i=De(o);return a<i?-1:a>i?1:0}),r}function kt(e,t,r,n,o){let a=0;if(e==="maxDrawdownPct"){if(a=t.maxDrawdownPct-n.maxDrawdownPct,a!==0)return a<0}else if(a=t[e]-n[e],a!==0)return a>0;let i=De(r),s=De(o);return i<s}function $t(e,t,r={}){if(!_r(r))throw new TypeError("options must be a plain object");for(let x of Object.keys(r))if(Tr(x))throw new TypeError(`options must not include holdout/test/oos key "${x}"`);let n=new Set(["maxTrials","objective","backtestOptions"]);for(let x of Object.keys(r))if(!n.has(x))throw new TypeError(`unknown option "${x}"`);let o=64;if("maxTrials"in r){let x=r.maxTrials;if(!Number.isInteger(x)||x<1||x>At)throw new TypeError(`maxTrials must be an integer 1..${At}`);o=x}let a=r.objective??"netPnlUsd";if(!$r.has(a))throw new TypeError(`objective must be one of ${[...$r].join("|")}`);let i=r.backtestOptions??{};if(!_r(i))throw new TypeError("backtestOptions must be a plain object");for(let x of Object.keys(i))if(Tr(x))throw new TypeError(`backtestOptions must not include holdout/test/oos key "${x}"`);let s=V(e),l=Object.keys(s.parameters).sort();if(l.length===0)throw new Error("optimizeStrategy requires at least one declared parameter");let u={},f=1;for(let x of l){let N=yo(s.parameters[x]);if(N.length===0)throw new Error(`parameter "${x}" produces an empty grid`);u[x]=N,f*=N.length}let c=go(l,u);if(c.length===0)throw new Error("parameter grid is empty");let d=c.slice(0,o),m=[],p=null,h=null;for(let x of d){let N=ye(s,x),_=ne(N,t,i);h==null&&(h=_.config);let w={netPnlUsd:_.metrics.netPnlUsd,netPnlPct:_.metrics.netPnlPct,maxDrawdownPct:_.metrics.maxDrawdownPct,winRate:_.metrics.winRate,profitFactor:_.metrics.profitFactor,tradeCount:_.metrics.tradeCount,sharpe:_.metrics.sharpe,turnoverUsd:_.metrics.turnoverUsd},J={parameters:{...x},metrics:w};m.push(J),(!p||kt(a,w,x,p.metrics,p.parameters))&&(p=J)}m.sort((x,N)=>kt(a,x.metrics,x.parameters,N.metrics,N.parameters)?-1:kt(a,N.metrics,N.parameters,x.metrics,x.parameters)?1:0);let E={...p.parameters},k=ye(s,E),P={strategyHash:fe(s),compilerHash:ce,trainBarsHash:$e(t),backtestConfig:h,objective:a,maxTrials:o,trialsRun:m.length,candidates:m,bestParameters:E,bestStrategy:k},T={id:ho("sha256").update(JSON.stringify(Oe(P)),"utf8").digest("hex"),...P};return Pt(Oe(JSON.parse(JSON.stringify(T))))}import{createHash as wo}from"node:crypto";var B=class extends Error{constructor(t){super(t),this.name="StrategyDraftError"}},bo=new Set(["1m","3m","5m","15m","30m","1h","2h","4h","8h","12h","1d"]),Eo=new Set(["BTC","ETH","SOL","HYPE"]),xo=/\b(mnemonic|private\s*key|privkey|seed\s*phrase|seed\b|api[_-]?key|secret\s*key|wallet\s*key|passphrase)\b/i,Ee="Supported grammar: (1) EMA cross: <long|short> <COIN> when EMA <n> crosses <above|below> EMA <m> [on <interval>] [, exit on reverse] [, risk knobs]; (2) RSI: <long|short> <COIN> when RSI <n> <above|below> <threshold> [on <interval>] [, risk knobs]; (3) funding: <long|short> <COIN> when funding rate <above|below> <decimal|percent%> [on <interval>] [, risk knobs]. Risk knobs: stop loss %, take profit %, leverage, position size %, max notional USD. Coins: BTC ETH SOL HYPE or uppercase token <=12 chars. Intervals: 1m 3m 5m 15m 30m 1h 2h 4h 8h 12h 1d.",So=Object.freeze({maxLeverage:1,maxNotionalUsd:100,positionSizePct:5,stopLossPct:2,takeProfitPct:4,cooldownBars:1,maxDailyLossPct:3}),ko=36e5,Ao=30*864e5,Po=864e5;function $o(e){return e!=null&&typeof e=="object"&&!Array.isArray(e)}function Tt(e){if(e==null||typeof e!="object"||Object.isFrozen(e))return e;if(Array.isArray(e)){for(let t of e)Tt(t);return Object.freeze(e)}for(let t of Object.keys(e))Tt(e[t]);return Object.freeze(e)}function To(e){return String(e).trim().replace(/\s+/g," ")}function _o(e){let t=e.match(/\b(long|short)\b/i);return t?t[1].toLowerCase():null}function Io(e){for(let r of Eo)if(new RegExp(`\\b${r}\\b`,"i").test(e))return r;let t=e.match(/\b([A-Z][A-Z0-9]{1,11})\b/);return t?t[1]:null}function Oo(e){let t=e.match(/\bon\s+(1m|3m|5m|15m|30m|1h|2h|4h|8h|12h|1d)\b/i);if(!t)return"15m";let r=t[1].toLowerCase();return bo.has(r)?r:null}function vo(e){let t={...So},r=e.match(/stop\s*loss\s*(\d+(?:\.\d+)?)\s*%?/i);r&&(t.stopLossPct=Number(r[1]));let n=e.match(/take\s*profit\s*(\d+(?:\.\d+)?)\s*%?/i);n&&(t.takeProfitPct=Number(n[1]));let o=e.match(/leverage\s*(\d+(?:\.\d+)?)/i);o&&(t.maxLeverage=Number(o[1]));let a=e.match(/position\s*size\s*(\d+(?:\.\d+)?)\s*%?/i);a&&(t.positionSizePct=Number(a[1]));let i=e.match(/max\s*notional\s*(?:usd\s*)?\$?\s*(\d+(?:\.\d+)?)/i);return i&&(t.maxNotionalUsd=Number(i[1])),t}function Mo(e){return[/\bon\s+(?:1m|3m|5m|15m|30m|1h|2h|4h|8h|12h|1d)\b/gi,/\bstop\s*loss\s*\d+(?:\.\d+)?\s*%?/gi,/\btake\s*profit\s*\d+(?:\.\d+)?\s*%?/gi,/\bleverage\s*\d+(?:\.\d+)?/gi,/\bposition\s*size\s*\d+(?:\.\d+)?\s*%?/gi,/\bmax\s*notional\s*(?:usd\s*)?\$?\s*\d+(?:\.\d+)?/gi].some(r=>(e.match(r)??[]).length>1)}function Ro(e){let t=wo("sha256").update(e,"utf8").digest("hex").slice(0,10),r=e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40),o=`${r.length?r:"draft"}-${t}`.replace(/[^a-z0-9._-]/g,"").slice(0,64);return/^[a-z0-9]/.test(o)?o:`d${o}`.slice(0,64)}function No(e){let t=e.match(/\bema\s*(\d+)\s*cross(?:es)?\s*(above|below)\s*ema\s*(\d+)\b/i);if(!t)return null;let r=Number(t[1]),n=t[2].toLowerCase(),o=Number(t[3]);if(!(r>0)||!(o>0)||r===o)return null;let a=/\b(exit\s+on\s+reverse|reverse\s+exit)\b/i.test(e);return{kind:"ema_cross",fast:r,slow:o,direction:n,reverse:a}}function Lo(e){let t=e.match(/\brsi\s*(\d+)\s*(above|below)\s*(-?\d+(?:\.\d+)?)/i);if(!t)return null;let r=Number(t[3]);return!Number.isFinite(r)||r<=0||r>=100?null:{kind:"rsi",period:Number(t[1]),op:t[2].toLowerCase(),threshold:r}}function jo(e){let t=e.match(/\bfunding\s*rate\s*(above|below)\s*(-?\d+(?:\.\d+)?)(%?)(?=\s|,|$)/i);if(!t)return null;let r=Number(t[2]);return t[3]==="%"&&(r=r/100),{kind:"funding",op:t[1].toLowerCase(),threshold:r}}function Fo(e,t){let r=e.replace(/^(long|short)\s+[A-Z][A-Z0-9]{1,11}\s+when\s+/i,"");if(r===e)return!1;t.kind==="ema_cross"?r=r.replace(/^ema\s*\d+\s*cross(?:es)?\s*(?:above|below)\s*ema\s*\d+\b/i,""):t.kind==="rsi"?r=r.replace(/^rsi\s*\d+\s*(?:above|below)\s*\d+(?:\.\d+)?\b/i,""):r=r.replace(/^funding\s*rate\s*(?:above|below)\s*-?\d+(?:\.\d+)?%?(?=\s|,|$)/i,"");let n=[/(?:,\s*)?\bon\s+(?:1m|3m|5m|15m|30m|1h|2h|4h|8h|12h|1d)\b/gi,/(?:,\s*)?\bstop\s*loss\s*\d+(?:\.\d+)?\s*%?/gi,/(?:,\s*)?\btake\s*profit\s*\d+(?:\.\d+)?\s*%?/gi,/(?:,\s*)?\bleverage\s*\d+(?:\.\d+)?/gi,/(?:,\s*)?\bposition\s*size\s*\d+(?:\.\d+)?\s*%?/gi,/(?:,\s*)?\bmax\s*notional\s*(?:usd\s*)?\$?\s*\d+(?:\.\d+)?/gi];t.kind==="ema_cross"&&n.push(/(?:,\s*)?\b(?:exit\s+on\s+reverse|reverse\s+exit)\b/gi);for(let o of n)r=r.replace(o,"");return r.replace(/[\s,;]+/g,"")===""}function Ir(e){let t=Math.max(2,Math.floor(e/2));return{value:e,min:Math.max(2,e-t),max:Math.min(500,e+t),step:1}}function Ho({side:e,coin:t,interval:r,risk:n,expiresAt:o,id:a,name:i,spec:s}){let l=[{id:"c",type:"input",field:"close"},{id:"emaLeft",type:"indicator",indicator:"ema",input:"c",period:{param:"ema_left_period"}},{id:"emaRight",type:"indicator",indicator:"ema",input:"c",period:{param:"ema_right_period"}},{id:"cross",type:"cross",direction:s.direction,left:"emaLeft",right:"emaRight"}],u={entryLong:null,entryShort:null,exitLong:null,exitShort:null};if(e==="long"?u.entryLong="cross":u.entryShort="cross",s.reverse){let f=s.direction==="above"?"below":"above";l.push({id:"crossRev",type:"cross",direction:f,left:"emaLeft",right:"emaRight"}),e==="long"?u.exitLong="crossRev":u.exitShort="crossRev"}return{version:1,id:a,name:i,venue:"hyperliquid",market:{coin:t,interval:r},parameters:{ema_left_period:Ir(s.fast),ema_right_period:Ir(s.slow)},nodes:l,rules:u,risk:{...n,expiresAt:o}}}function Uo({side:e,coin:t,interval:r,risk:n,expiresAt:o,id:a,name:i,spec:s}){let l=[{id:"c",type:"input",field:"close"},{id:"rsi",type:"indicator",indicator:"rsi",input:"c",period:s.period},{id:"thr",type:"constant",value:s.threshold},{id:"sig",type:"compare",op:s.op==="above"?"gt":"lt",left:"rsi",right:"thr"}];return{version:1,id:a,name:i,venue:"hyperliquid",market:{coin:t,interval:r},parameters:{},nodes:l,rules:{entryLong:e==="long"?"sig":null,entryShort:e==="short"?"sig":null,exitLong:null,exitShort:null},risk:{...n,expiresAt:o}}}function Co({side:e,coin:t,interval:r,risk:n,expiresAt:o,id:a,name:i,spec:s}){let l=[{id:"fr",type:"input",field:"fundingRate"},{id:"thr",type:"constant",value:s.threshold},{id:"sig",type:"compare",op:s.op==="above"?"gt":"lt",left:"fr",right:"thr"}];return{version:1,id:a,name:i,venue:"hyperliquid",market:{coin:t,interval:r},parameters:{},nodes:l,rules:{entryLong:e==="long"?"sig":null,entryShort:e==="short"?"sig":null,exitLong:null,exitShort:null},risk:{...n,expiresAt:o}}}function _t(e,t={}){if(typeof e!="string")throw new B("prompt must be a string");if(e.length>1e3)throw new B("prompt length must be <= 1000");if(!$o(t))throw new B("options must be a plain object");if(!Number.isInteger(t.nowMs))throw new B("options.nowMs is required integer epoch ms");let r=t.nowMs,n=Po;if("expiresInMs"in t){if(!Number.isInteger(t.expiresInMs))throw new B("expiresInMs must be an integer");if(n=t.expiresInMs,n<ko||n>Ao)throw new B("expiresInMs must be between 1h and 30d")}if(xo.test(e))throw new B(`prompt appears to contain secrets and is rejected. ${Ee}`);let o=To(e);if(!o)throw new B(`empty prompt. ${Ee}`);if(Mo(o))throw new B(`duplicate interval or risk clause. ${Ee}`);let a=_o(o),i=Io(o),s=Oo(o);if(!a||!i||!s)throw new B(`could not parse side/coin/interval. ${Ee}`);let l=vo(o);if(!(l.maxLeverage>0)||l.maxLeverage>50)throw new B("leverage out of range");if(!(l.positionSizePct>0)||l.positionSizePct>100)throw new B("position size out of range");let u=r+n,f=Ro(o),c=`Draft ${i} ${a}`.slice(0,100),d=No(o),m=Lo(o),p=jo(o),h=[d,m,p].filter(Boolean);if(h.length!==1)throw new B(`unsupported or ambiguous prompt. ${Ee}`);if(!Fo(o,h[0]))throw new B(`unsupported or ambiguous prompt. ${Ee}`);let E,k=h[0],P={side:a,coin:i,interval:s,risk:l,expiresAt:u,id:f,name:c,spec:k};k.kind==="ema_cross"?E=Ho(P):k.kind==="rsi"?E=Uo(P):E=Co(P);let T=Be(E,{nowMs:r});if(!T.ok)throw new B(`draft failed validation: ${T.errors.map(x=>x.message).join("; ")}`);return Tt(V(E,{nowMs:r}))}import oe from"node:fs";import ze from"node:path";var Ot=1,qo=["privatekey","secretkey","seed","mnemonic","passphrase","password","keystore","keymaterial","apikey","authtoken","accesstoken","refreshtoken","credential","bearer","signature","authorization","xprv","xpriv","wif"];function Ve(e){return e!=null&&typeof e=="object"&&!Array.isArray(e)}function te(e){return e==null?e:JSON.parse(JSON.stringify(e))}function Bo(e){return String(e).toLowerCase().replace(/[^a-z0-9]/g,"")}function xe(e,t="record"){if(Array.isArray(e)){e.forEach((r,n)=>xe(r,`${t}[${n}]`));return}if(Ve(e))for(let[r,n]of Object.entries(e)){let o=Bo(r);if(qo.some(a=>o.includes(a)))throw new Error(`shadow-store: forbidden secret-like field at ${t}.${r}`);xe(n,`${t}.${r}`)}}function Do(e){if(typeof e!="string"||!e||!ze.isAbsolute(e))throw new Error("shadow-store: path must be an absolute path")}function Or(e){let t;try{t=oe.lstatSync(e)}catch(n){if(n&&n.code==="ENOENT")return;throw n}if(t.isSymbolicLink())throw new Error("shadow-store: store path must not be a symlink");if(!t.isFile())throw new Error("shadow-store: store path must be a regular file");if(t.mode&511&18)throw new Error("shadow-store: store file must not be group or world writable")}function zo(){return{version:Ot,runners:[]}}function vr(e,t){let r;try{r=JSON.parse(e)}catch{throw new Error("shadow-store: malformed JSON")}if(!Ve(r))throw new Error("shadow-store: malformed JSON root");if(r.version!==Ot)throw new Error(`shadow-store: unsupported version ${r.version}`);if(!Array.isArray(r.runners))throw new Error("shadow-store: runners must be an array");return xe(r,ze.basename(t)),{version:Ot,runners:r.runners.map(n=>te(n))}}function Mr(e,t){xe(t,"store");let r=ze.dirname(e);oe.mkdirSync(r,{recursive:!0,mode:448});let n=ze.join(r,`.shadow-store.${process.pid}.${Date.now()}.tmp`),o=`${JSON.stringify(t)}
|
|
3
|
+
`;try{oe.writeFileSync(n,o,{mode:384,flag:"wx"}),oe.renameSync(n,e);try{oe.chmodSync(e,384)}catch{}}catch(a){try{oe.unlinkSync(n)}catch{}throw a}}var It=new Map;function vt(e={}){let t=e.path;if(Do(t),Or(t),oe.existsSync(t)?vr(oe.readFileSync(t,"utf8"),t):Mr(t,zo()),It.has(t))return It.get(t);let r=!1,n=[];function o(){if(r)return;let p=n.shift();if(p){r=!0;try{let h=p.fn();r=!1,p.resolve(h),o()}catch(h){r=!1,p.reject(h),o()}}}function a(p){if(!r&&n.length===0){r=!0;try{return p()}finally{r=!1,queueMicrotask(o)}}return new Promise((h,E)=>{n.push({fn:p,resolve:h,reject:E}),queueMicrotask(o)})}function i(){return Or(t),vr(oe.readFileSync(t,"utf8"),t)}function s(p){Mr(t,p)}function l(p){if(!Ve(p)||typeof p.id!="string"||!p.id)throw new Error("shadow-store: create requires record with string id");xe(p,"record");let h=i();if(h.runners.some(k=>k.id===p.id))throw new Error(`shadow-store: runner id already exists: ${p.id}`);let E=te(p);return h.runners.push(E),s(h),te(E)}function u(p){let h=i().runners.find(E=>E.id===p);return h?te(h):null}function f(){return i().runners.map(p=>te(p))}function c(p,h){if(typeof h!="function")throw new Error("shadow-store: updater must be a function");let E=i(),k=E.runners.findIndex(x=>x.id===p);if(k<0)throw new Error(`shadow-store: runner not found: ${p}`);let P=te(E.runners[k]),T=h(P);if(!Ve(T)||T.id!==p)throw new Error("shadow-store: updater must return record with same id");return xe(T,"record"),E.runners[k]=te(T),s(E),te(E.runners[k])}function d(p){let h=i(),E=h.runners.findIndex(P=>P.id===p);if(E<0)throw new Error(`shadow-store: runner not found: ${p}`);let k=te(h.runners[E]);return k.status!=="stopped"&&(k.status="stopped",h.runners[E]=k,s(h)),te(h.runners[E])}let m={create(p){return a(()=>l(p))},get(p){return u(p)},list(){return f()},update(p,h){return a(()=>c(p,h))},stop(p){return a(()=>d(p))}};return It.set(t,m),m}import{createHash as Nt}from"node:crypto";function Mt(e){return e==null?e:JSON.parse(JSON.stringify(e))}function Rt(e){return Array.isArray(e)?e.map(Rt):e&&typeof e=="object"?Object.fromEntries(Object.keys(e).sort().map(t=>[t,Rt(e[t])])):e}function Nr(e){if(!e||typeof e!="object"||Array.isArray(e))throw new TypeError("shadow-runner: record must be an object");return Nt("sha256").update(JSON.stringify(Rt(e))).digest("hex")}function Vo(e,t,r,n,o){return Nt("sha256").update(`${e}|${t}|${r}|${n}|${o}`).digest("hex")}function Jo(e,t,r,n){return Nt("sha256").update(`shadow|${e}|${t}|${r}|${n??""}`).digest("hex")}function Ko(e){return e==="entryLong"||e==="exitShort"?"buy":e==="entryShort"||e==="exitLong"?"sell":e}function Rr(e){return e==="entryLong"||e==="entryShort"}function Je({storePath:e,clock:t=Date.now}={}){if(typeof e!="string"||!e)throw new Error("shadow-runner: storePath required");let r=vt({path:e});function n({strategy:l,evidenceId:u=null}={}){let f=z(l),c=u==null?null:String(u),d=r.list().filter(k=>k.status==="running"&&k.strategyHash===f.strategyHash&&k.compilerHash===f.compilerHash&&k.evidenceId===c).sort((k,P)=>k.createdAt-P.createdAt||k.id.localeCompare(P.id))[0];if(d)return Mt(d);let m=t(),p=Jo(m,f.strategyHash,f.compilerHash,c),h=r.get(p);if(h)return Mt(h);let E={id:p,strategy:Mt(f.strategy),strategyHash:f.strategyHash,compilerHash:f.compilerHash,evidenceId:c,status:"running",cursor:null,intendedOrders:[],fills:[],missedFills:[],markouts:[],createdAt:m,updatedAt:m,position:null,cooldownBarsRemaining:0,cooldownStartT:null,lastProcessedBarIndex:-1};return r.create(E)}function o(l){return r.get(l)}function a(){return r.list()}function i(l){return r.stop(l)}function s(l,u,{markoutHorizons:f=[1,4,12]}={}){if(!Array.isArray(u))throw new TypeError("shadow-runner: bars must be an array");return r.update(l,c=>{if(c.status!=="running")throw new Error("shadow-runner: runner is stopped");if(u.length===0)return c;let m=z(c.strategy).evaluateAll(u),p=new Map;for(let y=0;y<u.length;y++)p.set(u[y].t,y);let h=c.cursor,E=[];for(let y=0;y<u.length;y++){let A=u[y].t;(h==null||A>h)&&E.push(y)}if(E.length===0)return c;let k=new Map(c.intendedOrders.map(y=>[y.id,y])),P=new Set(c.fills.map(y=>y.orderId)),T=new Map(c.missedFills.map(y=>[y.orderId,y])),x=(y,A)=>`${y}|${A}`,N=new Map(c.markouts.map(y=>[x(y.orderId,y.horizon),y])),_=c.position?{...c.position}:null,w=Number.isInteger(c.cooldownBarsRemaining)?Math.max(0,c.cooldownBarsRemaining):0,J=Number.isFinite(c.cooldownStartT)?c.cooldownStartT:null;function se({action:y,signalBarT:A,signalIndex:v}){let L=Ko(y),j=Vo(c.id,c.strategyHash,A,L,y);if(k.has(j))return k.get(j);let I=v+1,M=I<u.length,D={id:j,action:y,type:y,side:L,signalBarT:A,fillBarT:M?u[I].t:null,status:M?"open":"missed"};return k.set(j,D),c.intendedOrders.push(D),D}function ie(y,A,v){if(P.has(y.id))return;let L={orderId:y.id,action:Rr(y.action)?"entry":"exit",side:y.side,price:A.o,barT:A.t,signalBarT:y.signalBarT,barIndex:v};if(c.fills.push(L),P.add(y.id),y.status="filled",y.fillBarT=A.t,T.has(y.id)&&(T.delete(y.id),c.missedFills=c.missedFills.filter(j=>j.orderId!==y.id)),Rr(y.action)){_={side:y.action==="entryLong"?"long":"short",entryOrderId:y.id,entryBarT:A.t,entryPrice:A.o,entryBarIndex:v};for(let j of f){let I=x(y.id,j);if(N.has(I))continue;let M={orderId:y.id,horizon:j,entryBarT:A.t,entryPrice:A.o,side:_.side,status:"pending",close:null,barT:null};N.set(I,M),c.markouts.push(M)}}else _=null,w=Number(c.strategy?.risk?.cooldownBars)||0,J=A.t}function X(y){if(P.has(y.id)||T.has(y.id))return;let A={orderId:y.id,action:y.action,side:y.side,signalBarT:y.signalBarT,status:"missed"};T.set(y.id,A),c.missedFills.push(A),y.status="missed"}for(let y of c.intendedOrders){if(y.status==="filled"||P.has(y.id))continue;let A=p.get(y.signalBarT);if(A==null)continue;let v=A+1;v<u.length&&(h==null||u[v].t>h)&&ie(y,u[v],v)}for(let y of E){let A=u[y],v=m[y].signals,L=_,j=M=>{if(_||w>0&&(J==null||A.t>=J))return;let C=se({action:M,signalBarT:A.t,signalIndex:y}),Z=y+1;Z<u.length?ie(C,u[Z],Z):X(C)},I=M=>{if(!_||_.side==="long"&&M!=="exitLong"||_.side==="short"&&M!=="exitShort")return;let D=se({action:M,signalBarT:A.t,signalIndex:y}),C=y+1;C<u.length?ie(D,u[C],C):X(D)};L?(v.exitLong&&I("exitLong"),v.exitShort&&I("exitShort")):v.entryLong&&v.entryShort||(v.entryLong&&j("entryLong"),v.entryShort&&j("entryShort")),w>0&&J!=null&&A.t>=J&&(w-=1,w===0&&(J=null)),c.cursor=A.t,c.lastProcessedBarIndex=y}for(let y of c.markouts){if(y.status!=="pending")continue;let A=c.fills.find(j=>j.orderId===y.orderId&&j.action==="entry");if(!A)continue;let v=p.get(A.barT);if(v==null)continue;let L=v+y.horizon;if(L<u.length){y.close=u[L].c,y.barT=u[L].t,y.status="filled";let j=y.entryPrice;y.markout=y.side==="long"?(y.close-j)/j:(j-y.close)/j}}return c.position=_,c.cooldownBarsRemaining=w,c.cooldownStartT=J,c.updatedAt=t(),c})}return{start:n,step:s,list:a,get:o,stop:i}}import Yo from"node:os";import Go from"node:path";var Xo=["privatekey","secretkey","seed","mnemonic","passphrase","password","keystore","keymaterial","apikey","authtoken","accesstoken","refreshtoken","credential","bearer","signature","authorization","xprv","xpriv","wif"];function ve(e){return e!=null&&typeof e=="object"&&!Array.isArray(e)}function Zo(e){return String(e).toLowerCase().replace(/[^a-z0-9]/g,"")}function jt(e){if(Array.isArray(e)){for(let t of e)jt(t);return}if(ve(e))for(let[t,r]of Object.entries(e)){if(Xo.some(n=>Zo(t).includes(n)))throw new Error(`strategy service: secret-like field "${t}" is forbidden`);jt(r)}}function Wo(e){if(!ve(e))throw new TypeError("strategy input must be a plain object");return jt(e),e}function Qo(e,t){let r=t.nowMs??e.nowMs??Date.now();if(!Number.isInteger(r))throw new TypeError("nowMs must be an integer epoch milliseconds");return r}async function Lt(e,t,r,n){if(Array.isArray(t.bars))return t.bars;let o=Ue(e.venue),a=r.fetchBars??o.fetchBars;if(typeof a!="function")throw new Error(`strategy venue ${o.id} does not support history`);let i=await a({coin:e.market.coin,interval:e.market.interval,count:t.count??1500,endTime:t.endTime??n}),s=Array.isArray(i)?i:i?.bars;if(!Array.isArray(s))throw new Error("strategy history provider returned no bars");return s}function Ft(e,t){let r=e.backtestOptions??{};if(!ve(r))throw new TypeError("backtestOptions must be a plain object");return{...r,nowMs:t}}function Lr(e,t,r,n,o={}){let a=e.evidenceOptions??{};if(!ve(a))throw new TypeError("evidenceOptions must be a plain object");return{...a,...o,strategy:t,bars:r,backtestOptions:Ft(e,n)}}function jr(){return Go.join(Yo.homedir(),".oracle","strategy-shadow.json")}function es(e,t){return{perStrategyNotionalUsd:e.risk.maxNotionalUsd,maxLeverage:e.risk.maxLeverage,dailyLossCapUsd:e.risk.maxNotionalUsd*(e.risk.maxDailyLossPct/100),chainAllowlist:[e.venue],assetAllowlist:[e.market.coin],expiresAt:Math.min(e.risk.expiresAt,t+864e5),killSwitchRequired:!0}}async function ts(e,t={},r={}){let n=Wo(t),o=Qo(n,r);if(e==="draft")return{strategy:_t(n.prompt,{nowMs:o})};if(e==="validate"){let a=z(n.strategy);if(a.strategy.risk.expiresAt<=o)throw new Error("strategy is expired");return{valid:!0,strategy:a.strategy,strategyHash:a.strategyHash,compilerHash:a.compilerHash,compilerVersion:a.compilerVersion,requiredSeries:a.requiredSeries}}if(e==="backtest"||e==="evidence"){let a=z(n.strategy),i=await Lt(a.strategy,n,r,o);Te(a,i);let s=Fe(Lr(n,a.strategy,i,o));return e==="evidence"?s:{backtest:ne(a.strategy,i,Ft(n,o)),evidence:s}}if(e==="optimize"){let a=z(n.strategy),i=await Lt(a.strategy,n,r,o);Te(a,i);let s=n.trainFraction,l=n.evidenceOptions?.trainFraction;if(s!=null&&l!=null&&s!==l)throw new TypeError("conflicting trainFraction controls are forbidden");let u=s??l??.7;if(typeof u!="number"||u<=0||u>=1)throw new TypeError("trainFraction must be in (0,1)");let f=i.slice(0,Math.floor(i.length*u)),c=n.options??{};if(!ve(c))throw new TypeError("options must be a plain object");let d=$t(a.strategy,f,{...c,...n.maxTrials==null?{}:{maxTrials:n.maxTrials},backtestOptions:Ft(n,o)}),m=d.bestStrategy;return{strategy:m,optimization:d,evidence:Fe(Lr(n,m,i,o,{trainFraction:u}))}}if(e==="shadow"){let a=Je({storePath:r.storePath??jr(),clock:()=>o}),i=n.action;if(i==="start")return await a.start({strategy:n.strategy,evidenceId:n.evidenceId??n.evidence?.id??null});if(i==="list")return await a.list();if(i==="get")return await a.get(n.id);if(i==="stop")return await a.stop(n.id);if(i==="step"){let s=await a.get(n.id);if(!s)throw new Error(`shadow runner not found: ${n.id}`);let l=await Lt(s.strategy,n,r,o);return Te(z(s.strategy),l),await a.step(n.id,l,n.stepOptions??{})}throw new Error(`unknown shadow action: ${i}`)}if(e==="prepare"){let a=z(n.strategy),i=Ue(a.strategy.venue);if(typeof n.shadowId!="string"||!n.shadowId)throw new Error("strategy prepare requires a matching shadow runner id");let l=await Je({storePath:r.storePath??jr(),clock:()=>o}).get(n.shadowId);if(!l)throw new Error("strategy prepare shadow runner not found");if(l.status!=="running")throw new Error("strategy prepare requires an active shadow runner");if(!Number.isFinite(l.cursor))throw new Error("strategy prepare requires a stepped shadow runner");if(l.strategyHash!==a.strategyHash||l.compilerHash!==a.compilerHash)throw new Error("strategy prepare shadow runner does not match strategy artifacts");if(l.evidenceId!==n.evidence?.id)throw new Error("strategy prepare shadow runner does not match evidence artifact");let u=n.caps??es(a.strategy,o);return i.prepareIntent({strategy:a.strategy,evidence:n.evidence,shadow:{id:l.id,stateHash:Nr(l),status:l.status,cursor:l.cursor,updatedAt:l.updatedAt},caps:u,nowMs:o,env:r.env??process.env})}throw new Error(`unknown strategy operation: ${e}`)}export{or as EVIDENCE_STATUSES,At as MAX_OPTIMIZER_TRIALS,ce as STRATEGY_COMPILER_HASH,he as STRATEGY_COMPILER_VERSION,Ce as STRATEGY_VERSION,B as StrategyDraftError,pe as StrategyValidationError,ye as applyParameterOverrides,dt as assertEvidenceArtifact,ot as atr,ne as backtestStrategy,nt as bollinger,Pr as canonicalStrategyJson,z as compileStrategy,ft as computeEvidenceArtifactId,Je as createShadowRunner,_t as draftStrategyFromPrompt,Ae as ema,Fe as evaluateEvidence,gt as fetchHyperliquidStrategyBars,lr as intervalToMs,rt as macd,ur as normalizeHyperliquidCandles,V as normalizeStrategy,vt as openShadowStore,$t as optimizeStrategy,yt as prepareStrategyLiveHandoff,Ue as requireStrategyVenue,He as resolveStrategyVenue,tt as rsi,ts as runStrategyOperation,et as sma,fe as strategyHash,eo as strategyVenues,Be as validateStrategy};
|