UNPKG

openalgo-charts

Version:

From-scratch, dependency-free canvas charting engine for OpenAlgo: professional-grade interactive rendering, advanced on-chart trading, and a one-call widget tier that adds the toolbar, drawing rail, dialogs and shortcuts.

3 lines (2 loc) 30.2 kB
function e(e){return"pending"===e.status||"working"===e.status||"partial"===e.status}function t(e,t){return(t-e.avgPrice)*e.netQty}function r(e,t){if(0===e.avgPrice||0===e.netQty)return 0;const r=e.netQty>0?1:-1;return(t-e.avgPrice)/e.avgPrice*100*r}function i(e,t=0){if(0===e.netQty)return e.avgPrice;const r=e.netQty>0?1:-1;return e.avgPrice+r*t}function s(e,t,r){const i=Math.abs(e-t);return 0===i?null:Math.abs(r-e)/i}function o(e,t,r,i){return"BUY"===e?r<t&&i>t:r>t&&i<t}function n(e){const t=e.trim();if(t.startsWith("#")){const e=t.slice(1);if(3===e.length||4===e.length){const[t,r,i,s]=[...e].map(e=>parseInt(e+e,16));return[t,r,i].some(Number.isNaN)?null:{r:t,g:r,b:i,a:4===e.length?s/255:1}}if(6===e.length||8===e.length){const t=parseInt(e.slice(0,6),16);if(Number.isNaN(t))return null;const r=8===e.length?parseInt(e.slice(6,8),16)/255:1;return{r:t>>16&255,g:t>>8&255,b:255&t,a:Number.isNaN(r)?1:r}}return null}const r=/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*(?:,\s*([\d.]+)\s*)?\)$/i.exec(t);return null===r?null:{r:Number(r[1]),g:Number(r[2]),b:Number(r[3]),a:void 0===r[4]?1:Number(r[4])}}function a(e){return function(e){const t=n(e);if(null===t)return.5;const r=e=>{const t=e/255;return t<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4)};return.2126*r(t.r)+.7152*r(t.g)+.0722*r(t.b)}(e)>.45?"#10131a":"#ffffff"}function l(e,t){const r=n(e);return null===r?e:`rgba(${r.r},${r.g},${r.b},${t})`}function c(e,t){const r=n(e);if(null===r)return e;const i=t>=0?255:0,s=Math.min(1,Math.abs(t)),o=e=>Math.round(e+(i-e)*s);return`rgba(${o(r.r)},${o(r.g)},${o(r.b)},${r.a})`}function d(e,t,r,i,s,o){const n=e.roundRect;"function"==typeof n?n.call(e,t,r,i,s,Math.max(0,Math.min(o,i/2,s/2))):e.rect(t,r,i,s)}function h(e,t,r,i,s,o,n){const a=e.measureText(i).width+2*o;return e.beginPath(),d(e,t,r-s/2,a,s,n.radius),void 0!==n.backplate&&(e.fillStyle=n.backplate,e.fill()),e.fillStyle=n.fill,e.fill(),void 0!==n.border&&(e.strokeStyle=n.border,e.lineWidth=1,e.stroke()),e.fillStyle=n.text,e.textAlign="left",e.textBaseline="middle",e.fillText(i,t+o,r),a}function u(e,t,r,i,s){const{height:o,padX:n,radius:a,gap:l,dpr:c}=s,h=i.map(t=>!0===t.close?20*c:e.measureText(t.text??"").width+2*n),u=h.reduce((e,t)=>e+t,0)+l*Math.max(0,i.length-1);void 0!==s.backplate&&(e.beginPath(),d(e,t,r-o/2,u,o,a),e.fillStyle=s.backplate,e.fill());let p=Number.POSITIVE_INFINITY,f=t;for(let t=0;t<i.length;t++){const s=i[t],u=h[t];if(e.beginPath(),d(e,f,r-o/2,u,o,a),e.fillStyle=s.fill,e.fill(),void 0!==s.border&&(e.strokeStyle=s.border,e.lineWidth=Math.max(1,Math.round(c)),e.stroke()),!0===s.close){p=f,e.strokeStyle=s.textColor,e.lineWidth=Math.max(1,Math.round(1.4*c));const t=f+u/2,i=3.5*c;e.beginPath(),e.moveTo(t-i,r-i),e.lineTo(t+i,r+i),e.moveTo(t+i,r-i),e.lineTo(t-i,r+i),e.stroke()}else e.fillStyle=s.textColor,e.textAlign="left",e.textBaseline="middle",e.fillText(s.text??"",f+n,r);f+=u+l}return{x0:t/c,x1:(f-l)/c,closeX0:p/c}}class p{constructor(e,t={}){Object.defineProperty(this,"_order",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_ltp",{enumerable:!0,configurable:!0,writable:!0,value:NaN}),Object.defineProperty(this,"_host",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"_group",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"_extent",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_ghost",{enumerable:!0,configurable:!0,writable:!0,value:null}),this._order=e,this._extent=Math.max(.1,Math.min(1,t.extentFromRight??.5))}attached(e){this._host=e}detached(){this._host=null}zOrder(){return"normal"}get order(){return this._order}update(e){this._order=e,this._host?.requestUpdate()}setLtp(e){this._ltp=e,this._host?.requestUpdate()}autoscaleInfo(){const e=this._order.triggerPrice??this._order.price;return{min:e,max:e}}_price(){return this._order.triggerPrice??this._order.price}draw(e,t){this._group=null;const r=this._price(),i=t.dpr,s=Math.round(t.priceScale.priceToY(r)*i)+.5;if(s<0||s>t.plotHeight*i)return;const o="BUY"===this._order.side?t.theme.buy:t.theme.sell,n=t.hoverId===`order:${this._order.id}`||t.hoverId===`order:${this._order.id}::close`||t.dragId===`order:${this._order.id}`,d="pending"===this._order.status,h=d?l(o,.55):n?c(o,.1):o,p=Math.round(t.plotWidth*i),f=Math.round(t.plotWidth*(1-this._extent)*i),b=t.dragId===`order:${this._order.id}`;if(b&&null===this._ghost?this._ghost=r:b||null===this._ghost||(this._ghost=null),e.save(),null!==this._ghost&&Math.abs(this._ghost-r)>1e-9){const r=Math.round(t.priceScale.priceToY(this._ghost)*i)+.5;r>=0&&r<=t.plotHeight*i&&(e.strokeStyle=l(o,.35),e.lineWidth=Math.max(1,Math.round(i)),e.setLineDash([2*i,3*i]),e.beginPath(),e.moveTo(f,r),e.lineTo(p,r),e.stroke(),e.setLineDash([]))}b&&(e.strokeStyle=l(o,.18),e.lineWidth=5*i,e.beginPath(),e.moveTo(f,s),e.lineTo(p,s),e.stroke()),e.strokeStyle=h;const _=Math.max(1,Math.round(i));e.lineWidth=n?_+Math.round(i):_,e.setLineDash([6*i,4*i]),e.beginPath(),e.moveTo(f,s),e.lineTo(p,s),e.stroke(),e.setLineDash([]),e.font=`500 ${11*i}px system-ui, sans-serif`,e.textBaseline="middle",e.textAlign="left";const g=18*i,m=6*i,y=3*i,v=t.priceScale.format(r);e.fillStyle=d?l(o,.7):o,e.fillRect(p+1,s-g/2,e.measureText(v).width+2*m,g),e.fillStyle=a(o),e.fillText(v,p+1+m,s);const k="transparent"===t.theme.background?l(o,.14):t.theme.background,M="transparent"===t.theme.background?t.theme.axisText:a(t.theme.background),x=l(t.theme.axisText,n?.75:.5),w=t.hoverId===`order:${this._order.id}::close`;d&&(e.globalAlpha=.75);const P=this._order.filledQty>0?`${this._order.filledQty}/${this._order.qty}`:String(this._order.qty-this._order.filledQty);let O=`${this._order.type} ${v}`;if(!Number.isNaN(this._ltp)){const e=r-this._ltp;O+=` ${e>=0?"+":""}${t.priceScale.format(e)}`}this._group=u(e,f,s,[{text:this._order.side,fill:n?c(o,.12):o,textColor:a(o),border:c(o,-.25)},{text:P,fill:k,textColor:M,border:x},{text:O,fill:k,textColor:M,border:x},{close:!0,fill:w?o:k,textColor:w?a(o):M,border:w?o:x}],{height:g,padX:m,radius:y,gap:2*i,backplate:"transparent"===t.theme.background?void 0:t.theme.background,dpr:i}),e.restore()}hitTest(e,t,r){if(e<0||e>r.plotWidth)return null;const i=r.priceScale.priceToY(this._price()),s=Math.abs(t-i),o=this._group;return null!==o&&s<=10&&e>=o.x0&&e<=o.x1?e>=o.closeX0?{externalId:`order:${this._order.id}::close`,zOrder:"normal",distance:s,cursor:"pointer"}:{externalId:`order:${this._order.id}`,zOrder:"normal",distance:s,cursor:"ns-resize"}:s>4||e<r.plotWidth*(1-this._extent)-8?null:{externalId:`order:${this._order.id}`,zOrder:"normal",distance:s,cursor:"ns-resize"}}}class f{constructor(e,t={}){Object.defineProperty(this,"_position",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_ltp",{enumerable:!0,configurable:!0,writable:!0,value:NaN}),Object.defineProperty(this,"_host",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"_group",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"_extent",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this._position=e,this._extent=Math.max(.1,Math.min(1,t.extentFromRight??.5))}attached(e){this._host=e}detached(){this._host=null}zOrder(){return"normal"}get position(){return this._position}update(e){this._position=e,this._host?.requestUpdate()}setLtp(e){this._ltp=e,this._host?.requestUpdate()}autoscaleInfo(){return{min:this._position.avgPrice,max:this._position.avgPrice}}draw(e,i){if(this._group=null,0===this._position.netQty)return;const s=i.dpr,o=Math.round(i.priceScale.priceToY(this._position.avgPrice)*s)+.5;if(o<0||o>i.plotHeight*s)return;const n=Math.round(i.plotWidth*s),d=Number.isNaN(this._ltp)?0:t(this._position,this._ltp),h=d>=0?i.theme.profit:i.theme.loss,p=this._position.netQty>0,f=p?i.theme.buy:i.theme.sell,b=`position:${this._position.symbol}`,_=i.hoverId===b||i.hoverId===`${b}::close`,g=Math.round(i.plotWidth*(1-this._extent)*s);if(e.save(),e.strokeStyle=_?f:i.theme.axisText,e.lineWidth=Math.max(1,Math.round(s))+(_?Math.round(s):0),e.beginPath(),e.moveTo(g,o),e.lineTo(n,o),e.stroke(),!Number.isNaN(this._ltp)){const t=Math.round(i.priceScale.priceToY(this._ltp)*s);e.fillStyle=l(h,.1),e.fillRect(g,Math.min(o,t),n-g,Math.abs(t-o))}e.font=`500 ${11*s}px system-ui, sans-serif`,e.textBaseline="middle",e.textAlign="left";const m=18*s,y=6*s,v=3*s,k=i.priceScale.format(this._position.avgPrice);e.fillStyle=h,e.fillRect(n+1,o-m/2,e.measureText(k).width+2*y,m),e.fillStyle=a(h),e.fillText(k,n+1+y,o);const M="transparent"===i.theme.background?l(f,.14):i.theme.background,x="transparent"===i.theme.background?i.theme.axisText:a(i.theme.background),w=l(i.theme.axisText,_?.75:.5),P=i.hoverId===`${b}::close`,O=Number.isNaN(this._ltp)?0:r(this._position,this._ltp),S=p?"LONG":"SHORT",T=String(Math.abs(this._position.netQty)),I=`${d>=0?"+":""}${d.toFixed(2)} (${O>=0?"+":""}${O.toFixed(2)}%)`;this._group=u(e,g,o,[{text:S,fill:_?c(f,.12):f,textColor:a(f),border:c(f,-.25)},{text:T,fill:M,textColor:x,border:w},{text:I,fill:M,textColor:h,border:l(h,.65)},{close:!0,fill:P?f:M,textColor:P?a(f):x,border:P?f:w}],{height:m,padX:y,radius:v,gap:2*s,backplate:"transparent"===i.theme.background?void 0:i.theme.background,dpr:s}),e.restore()}hitTest(e,t,r){if(0===this._position.netQty||e<0||e>r.plotWidth)return null;const i=r.priceScale.priceToY(this._position.avgPrice),s=Math.abs(t-i),o=`position:${this._position.symbol}`,n=this._group;return null!==n&&s<=10&&e>=n.x0&&e<=n.x1?e>=n.closeX0?{externalId:`${o}::close`,zOrder:"normal",distance:s,cursor:"pointer"}:{externalId:o,zOrder:"normal",distance:s,cursor:"pointer"}:s>4||e<r.plotWidth*(1-this._extent)-8?null:{externalId:o,zOrder:"normal",distance:s,cursor:"pointer"}}}class b{constructor(e){Object.defineProperty(this,"_state",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_host",{enumerable:!0,configurable:!0,writable:!0,value:null}),this._state=e}attached(e){this._host=e}detached(){this._host=null}zOrder(){return"bottom"}get state(){return this._state}update(e){this._state=e,this._host?.requestUpdate()}autoscaleInfo(){return{min:Math.min(this._state.stop,this._state.target,this._state.entry),max:Math.max(this._state.stop,this._state.target,this._state.entry)}}draw(e,t){const{entry:r,stop:i,target:o}=this._state,n=t.dpr,a=Math.round(t.plotWidth*n),c=t.priceScale.priceToY(r)*n,d=t.priceScale.priceToY(i)*n,u=t.priceScale.priceToY(o)*n;e.save(),e.fillStyle=l(t.theme.loss,.09),e.fillRect(0,Math.min(c,d),a,Math.abs(d-c)),e.fillStyle=l(t.theme.profit,.09),e.fillRect(0,Math.min(c,u),a,Math.abs(u-c)),e.font=`500 ${10*n}px system-ui, sans-serif`,e.textBaseline="middle";for(const[r,s,c,p,f]of[[d,t.theme.loss,"SL",i,`bracket-sl:${this._state.symbol}`],[u,t.theme.profit,"TP",o,`bracket-tp:${this._state.symbol}`]]){const i=Math.round(r)+.5,o=t.hoverId===f||t.dragId===f;e.strokeStyle=s;const d=Math.max(1,Math.round(n));e.lineWidth=o?d+Math.round(n):d,e.setLineDash([4*n,4*n]),e.beginPath(),e.moveTo(0,i),e.lineTo(a,i),e.stroke(),e.setLineDash([]),h(e,4*n,i,`${c} ${t.priceScale.format(p)}`,16*n,6*n,{fill:l(s,o?.28:.16),text:s,border:l(s,.55),radius:3*n,backplate:"transparent"===t.theme.background?void 0:t.theme.background})}const p=s(r,i,o);null!==p&&h(e,4*n,Math.round(c)-12*n,`R:R ${p.toFixed(2)}`,16*n,6*n,{fill:l(t.theme.axisText,.16),text:t.theme.axisText,radius:3*n,backplate:"transparent"===t.theme.background?void 0:t.theme.background}),e.restore()}hitTest(e,t,r){if(e<0||e>r.plotWidth)return null;const i=r.priceScale.priceToY(this._state.stop),s=r.priceScale.priceToY(this._state.target),o=Math.abs(t-i),n=Math.abs(t-s);return o<=4?{externalId:`bracket-sl:${this._state.symbol}`,zOrder:"bottom",distance:o,cursor:"ns-resize"}:n<=4?{externalId:`bracket-tp:${this._state.symbol}`,zOrder:"bottom",distance:n,cursor:"ns-resize"}:null}}class _{constructor(e){Object.defineProperty(this,"_host",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_orderLines",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),Object.defineProperty(this,"_markers",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),Object.defineProperty(this,"_brackets",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),Object.defineProperty(this,"_ltp",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),this._host=e}reconcile(e,t){const r=this._planBrackets(e,t);this._reconcileOrderLines(e,r.covered),this._reconcilePositions(t),this._applyBrackets(r.states)}_reconcileOrderLines(t,r){const i=new Set;for(const s of t){if(!e(s)||r.has(s.id))continue;i.add(s.id);const t=this._orderLines.get(s.id);if(t)t.update(s);else{const e=new p(s),t=this._ltp.get(s.symbol);void 0!==t&&e.setLtp(t),this._orderLines.set(s.id,e),this._host.addPrimitive(e)}}for(const[e,t]of this._orderLines)i.has(e)||(this._host.removePrimitive(t),this._orderLines.delete(e))}_reconcilePositions(e){const t=new Set;for(const r of e){if(0===r.netQty)continue;t.add(r.symbol);const e=this._markers.get(r.symbol);if(e)e.update(r);else{const e=new f(r),t=this._ltp.get(r.symbol);void 0!==t&&e.setLtp(t),this._markers.set(r.symbol,e),this._host.addPrimitive(e)}}for(const[e,r]of this._markers)t.has(e)||(this._host.removePrimitive(r),this._markers.delete(e))}_planBrackets(t,r){const i=new Map,s=new Map;for(const r of t)e(r)&&("sl"===r.role?i.set(r.symbol,r):"tp"===r.role&&s.set(r.symbol,r));const o=new Map(r.map(e=>[e.symbol,e])),n=new Map,a=new Set;for(const e of new Set([...i.keys(),...s.keys()])){const t=i.get(e),r=s.get(e);if(void 0===t||void 0===r)continue;const l=o.get(e);void 0!==l&&0!==l.netQty&&(n.set(e,{symbol:e,side:l.netQty>0?"BUY":"SELL",entry:l.avgPrice,stop:t.triggerPrice??t.price,target:r.price}),a.add(t.id),a.add(r.id))}return{states:n,covered:a}}_applyBrackets(e){for(const[t,r]of e){const e=this._brackets.get(t);if(e)e.update(r);else{const e=new b(r);this._brackets.set(t,e),this._host.addPrimitive(e)}}for(const[t,r]of this._brackets)e.has(t)||(this._host.removePrimitive(r),this._brackets.delete(t))}onLtp(e,t){this._ltp.set(e,t);for(const r of this._orderLines.values())r.order.symbol===e&&r.setLtp(t);const r=this._markers.get(e);r&&r.setLtp(t)}orderLineCount(){return this._orderLines.size}positionCount(){return this._markers.size}bracketCount(){return this._brackets.size}}class g{constructor(){Object.defineProperty(this,"_orders",{enumerable:!0,configurable:!0,writable:!0,value:[]}),Object.defineProperty(this,"_positions",{enumerable:!0,configurable:!0,writable:!0,value:[]}),Object.defineProperty(this,"_bookListeners",{enumerable:!0,configurable:!0,writable:!0,value:[]}),Object.defineProperty(this,"_ltpListeners",{enumerable:!0,configurable:!0,writable:!0,value:[]}),Object.defineProperty(this,"_depthListeners",{enumerable:!0,configurable:!0,writable:!0,value:[]}),Object.defineProperty(this,"_idCounter",{enumerable:!0,configurable:!0,writable:!0,value:0}),Object.defineProperty(this,"rejectNextPlace",{enumerable:!0,configurable:!0,writable:!0,value:null})}onBook(e){this._bookListeners.push(e)}onLtp(e){this._ltpListeners.push(e)}setBook(e,t){this._orders=e.map(e=>({...e})),this._positions=t.map(e=>({...e}));for(const e of this._bookListeners)e(this._orders,this._positions)}emitLtp(e,t){for(const r of this._ltpListeners)r(e,t)}onDepth(e){this._depthListeners.push(e)}emitDepth(e,t){for(const r of this._depthListeners)r(e,t)}static makeDepth(e,t,r=.05){const i=[],s=[];for(let o=1;o<=t;o++)i.push({price:e-o*r,qty:100+37*o%900}),s.push({price:e+o*r,qty:100+53*o%900});return{bids:i,asks:s,ltp:e}}orders(){return this._orders}positions(){return this._positions}async place(e){if(null!==this.rejectNextPlace){const e=this.rejectNextPlace;throw this.rejectNextPlace=null,new Error(e)}const t="B"+ ++this._idCounter;return this._orders.push({id:t,symbol:e.symbol,side:e.side,type:e.type,qty:e.qty,filledQty:0,price:e.price??0,triggerPrice:e.triggerPrice,status:"MARKET"===e.type?"filled":"working",role:void 0}),this._notify(),{orderId:t}}async modify(e,t){const r=this._orders.find(t=>t.id===e);if(void 0===r)throw new Error("unknown order");void 0!==t.price&&(r.price=t.price),void 0!==t.triggerPrice&&(r.triggerPrice=t.triggerPrice),void 0!==t.qty&&(r.qty=t.qty),this._notify()}async cancel(e){this._orders=this._orders.filter(t=>t.id!==e),this._notify()}fill(e){const t=this._orders.find(t=>t.id===e);void 0!==t&&(t.filledQty=t.qty,t.status="filled",this._orders=this._orders.filter(t=>t.id!==e),this._notify())}_notify(){for(const e of this._bookListeners)e(this._orders,this._positions)}}const m=new Set(["filled","cancelled","rejected","stale"]);function y(e){return m.has(e)}const v={pending_place:{ack:"working",partialFill:"partial",fill:"filled",reject:"rejected",reconnectAbsent:"stale"},working:{partialFill:"partial",fill:"filled",reject:"rejected",submitModify:"modify_pending",submitCancel:"cancel_pending",cancelled:"cancelled",reconnectAbsent:"stale"},partial:{partialFill:"partial",fill:"filled",reject:"rejected",submitModify:"modify_pending",submitCancel:"cancel_pending",cancelled:"cancelled",reconnectAbsent:"stale"},modify_pending:{ack:"working",reject:"working",fill:"filled",partialFill:"partial",cancelled:"cancelled",reconnectAbsent:"stale"},cancel_pending:{cancelled:"cancelled",reject:"working",fill:"filled",reconnectAbsent:"stale"},filled:{},cancelled:{},rejected:{},stale:{}};function k(e,t){return void 0!==v[e][t]}function M(e,t){return v[e][t]??e}function x(e,t){return e>=t.lower&&e<=t.upper}function w(e,t){if(!Number.isFinite(e))return{ok:!1,code:"QTY_INVALID",reason:"quantity must be a finite number"};if(e<=0)return{ok:!1,code:"QTY_INVALID",reason:"quantity must be positive"};const r=t.lotSize??(!0===t.allowFractionalQty?void 0:1);return void 0!==r&&function(e,t){if(!Number.isFinite(t)||t<=0)return!1;const r=e/t;return Math.abs(r-Math.round(r))>1e-6*Math.max(1,Math.abs(r))}(e,r)?{ok:!1,code:"QTY_STEP",reason:void 0!==t.lotSize?`quantity ${e} is not a multiple of lot size ${t.lotSize}`:`quantity ${e} must be a whole number`}:void 0!==t.freezeQty&&e>t.freezeQty?{ok:!1,code:"QTY_FREEZE_LIMIT",reason:`quantity ${e} exceeds freeze limit ${t.freezeQty}`}:{ok:!0}}function P(e,t){if(!Number.isFinite(e))return{ok:!1,code:"PRICE_INVALID",reason:"price must be a finite number"};const r=(i=e,(s=t.tickSize)<=0?i:Math.round(i/s)*s);var i,s;return t.priceBand&&!x(r,t.priceBand)?{ok:!1,code:"PRICE_OUT_OF_BAND",reason:`price ${r} outside band ${t.priceBand.lower} to ${t.priceBand.upper}`}:{ok:!0,price:r}}function O(e,t,r){const i=w(t,r);return i.ok?void 0===e?{ok:!0}:P(e,r):i}function S(e){return"object"==typeof e&&null!==e&&!0===e.preflight}const T={pending:void 0,working:"ack",partial:"partialFill",filled:"fill",cancelled:"cancelled",rejected:"reject"},I=new Set(["filled","cancelled","rejected"]);class j{constructor(e){Object.defineProperty(this,"_feed",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_constraints",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_mode",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_armed",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_gate",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_minModifyMs",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_now",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_idGen",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_onValidationError",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_maxSettled",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_orders",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),Object.defineProperty(this,"_byBroker",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),Object.defineProperty(this,"_sentTokens",{enumerable:!0,configurable:!0,writable:!0,value:new Set}),Object.defineProperty(this,"_lastModifyAt",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),Object.defineProperty(this,"_pendingModify",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),Object.defineProperty(this,"_settledIds",{enumerable:!0,configurable:!0,writable:!0,value:[]}),Object.defineProperty(this,"_counter",{enumerable:!0,configurable:!0,writable:!0,value:0}),this._feed=e.feed,this._constraints=e.constraints,this._mode=e.mode??"live",this._armed=e.armed??!1,this._gate=e.gate,this._minModifyMs=e.minModifyIntervalMs??150,this._now=e.now??(()=>"undefined"!=typeof performance?performance.now():0),this._idGen=e.idGen??(()=>"c"+ ++this._counter),this._onValidationError=e.onValidationError,this._maxSettled=Math.max(0,e.maxSettledOrders??500)}get mode(){return this._mode}state(e){return this._orders.get(e)?.state}intentState(e){return this._orders.get(e)?.intent}brokerStatus(e){return this._orders.get(e)?.brokerStatus}async placeOrder(e){const t=O(e.price,e.qty,this._constraints);if(!t.ok)return{ok:!1,reason:t.reason,intent:"BLOCKED"};const r=void 0===e.price?void 0:t.price;let i=e.triggerPrice;if(void 0!==e.triggerPrice){const t=O(e.triggerPrice,e.qty,this._constraints);if(!t.ok)return{ok:!1,reason:`trigger: ${t.reason}`,intent:"BLOCKED"};i=t.price}const s=e.clientToken??this._idGen();if(this._sentTokens.has(s)){const e=this._orders.get(s);return{ok:!1,reason:"AMBIGUOUS"===e?.intent?"duplicate clientToken: the first attempt was never confirmed and may be live":"duplicate clientToken (idempotent skip)",clientId:s,state:e?.state,intent:e?.intent}}if(this._sentTokens.add(s),!this._armed){let t=!1;try{t=await(this._gate?this._gate(e):Promise.resolve(!1))}catch(e){throw this._sentTokens.delete(s),e}if(!t)return this._sentTokens.delete(s),{ok:!1,reason:"not confirmed",intent:"BLOCKED"}}const o={...e,price:r,triggerPrice:i,clientToken:s},n={clientId:s,state:"pending_place",intent:"SUBMITTING",req:o};this._orders.set(s,n);try{const{orderId:e}=await this._feed.place({...o,mode:this._mode});return n.brokerId=e,this._byBroker.set(e,s),n.state=M(n.state,"ack"),n.intent="SUBMITTED",{ok:!0,clientId:s,state:n.state,intent:n.intent}}catch(e){const t=S(e);n.state=M(n.state,"reject"),n.intent=t?"BLOCKED":"AMBIGUOUS",t&&this._sentTokens.delete(s);const r=String(e.message??e);return this._settle(n),{ok:!1,clientId:s,state:n.state,intent:n.intent,reason:t?r:`${r} (may have reached the broker; check the order book before retrying)`}}}placeMarket(e,t,r,i){return this.placeOrder({symbol:e,side:t,type:"MARKET",qty:r,exchange:i?.exchange,product:i?.product,clientToken:i?.clientToken})}linkOco(e,t){const r=this._orders.get(e),i=this._orders.get(t);r&&i&&(r.ocoPeer=t,i.ocoPeer=e)}requestModify(e,t,r){const i=this._orders.get(e);if(void 0===i||y(i.state))return;const s=this._buildModifyPatch(i,t,r?.triggerPrice);if(!s.ok)return void this._onValidationError?.(s.reason);this._pendingModify.set(e,s.patch);const o=this._lastModifyAt.get(e)??-1/0;this._now()-o>=this._minModifyMs&&this._flushModify(e)}commitModify(e){return this._flushModify(e)}_buildModifyPatch(e,t,r){const i=O(t,e.req.qty,this._constraints);if(!i.ok)return{ok:!1,reason:i.reason??"invalid modify price"};const s=i.price??t;if("SL-M"===e.req.type){const t=r??s,i=this._validateTrigger(t,e.req.qty);return i.ok?{ok:!0,patch:{triggerPrice:i.price}}:i}if("SL"===e.req.type){const t=void 0!==e.req.triggerPrice&&void 0!==e.req.price?e.req.triggerPrice-e.req.price:0,i=r??s+t,o=this._validateTrigger(i,e.req.qty);return o.ok?{ok:!0,patch:{price:s,triggerPrice:o.price}}:o}if(void 0!==r){const t=this._validateTrigger(r,e.req.qty);return t.ok?{ok:!0,patch:{price:s,triggerPrice:t.price}}:t}return{ok:!0,patch:{price:s}}}_validateTrigger(e,t){const r=O(e,t,this._constraints);return r.ok?{ok:!0,price:r.price??e}:{ok:!1,reason:`trigger: ${r.reason??"invalid"}`}}async _flushModify(e){const t=this._pendingModify.get(e),r=this._orders.get(e);if(void 0!==t&&void 0!==r&&void 0!==r.brokerId){this._pendingModify.delete(e),this._lastModifyAt.set(e,this._now()),r.state=M(r.state,"submitModify"),r.intent="MODIFY_SUBMITTING";try{await this._feed.modify(r.brokerId,t),r.state=M(r.state,"ack"),r.intent="ACKNOWLEDGED",void 0!==t.price&&(r.req={...r.req,price:t.price}),void 0!==t.triggerPrice&&(r.req={...r.req,triggerPrice:t.triggerPrice})}catch(e){r.state=M(r.state,"reject"),r.intent=S(e)?"ACKNOWLEDGED":"AMBIGUOUS"}}}async cancelOrder(e){const t=this._orders.get(e);if(void 0!==t&&void 0!==t.brokerId&&!y(t.state)){t.state=M(t.state,"submitCancel"),t.intent="CANCEL_SUBMITTING";try{await this._feed.cancel(t.brokerId),t.state=M(t.state,"cancelled"),t.intent="ACKNOWLEDGED",this._cancelOcoPeer(t),this._settle(t)}catch(e){t.state=M(t.state,"reject"),t.intent=S(e)?"ACKNOWLEDGED":"AMBIGUOUS"}}}onFill(e,t){this.onBrokerUpdate(e,t?"filled":"partial")}onBrokerUpdate(e,t){const r=this._byBroker.get(e);if(void 0===r)return;const i=this._orders.get(r);if(void 0===i)return;i.brokerStatus=t;const s=T[t];void 0!==s&&(i.state=M(i.state,s)),i.intent=I.has(t)?"SETTLED":"ACKNOWLEDGED","filled"===t&&this._cancelOcoPeer(i),this._settle(i)}_cancelOcoPeer(e){if(void 0===e.ocoPeer)return;const t=this._orders.get(e.ocoPeer);t&&!y(t.state)&&this.cancelOrder(t.clientId)}beginReconcile(){for(const e of this._orders.values())y(e.state)||"AMBIGUOUS"===e.intent||(e.intent="RECONCILING")}onReconnect(e){for(const t of this._orders.values())y(t.state)||(void 0!==t.brokerId&&e.has(t.brokerId)?t.intent="ACKNOWLEDGED":(t.state=M(t.state,"reconnectAbsent"),t.intent="AMBIGUOUS"))}_settle(e){if(!0!==e.pruned&&y(e.state)&&"AMBIGUOUS"!==e.intent&&"RECONCILING"!==e.intent)for(e.pruned=!0,this._lastModifyAt.delete(e.clientId),this._pendingModify.delete(e.clientId),this._settledIds.push(e.clientId);this._settledIds.length>this._maxSettled;){const e=this._settledIds.shift();if(void 0===e)break;const t=this._orders.get(e);void 0!==t&&!0===t.pruned&&(this._orders.delete(e),void 0!==t.brokerId&&this._byBroker.delete(t.brokerId),this._lastModifyAt.delete(e),this._pendingModify.delete(e))}}}function $(e){const t=Math.max(e.bids.length,e.asks.length);return 0===t?"none":t<=5?"compact":"deep"}function L(e,t,r=1){const i=t*Math.max(1,r),s=new Map,o=(e,t,r)=>{const o=(n=e,Math.round(Math.round(n/i)*i*1e8)/1e8);var n;let a=s.get(o);void 0===a&&(a={price:o,bidQty:0,askQty:0},s.set(o,a)),"bid"===r?a.bidQty+=t:a.askQty+=t};for(const t of e.bids)o(t.price,t.qty,"bid");for(const t of e.asks)o(t.price,t.qty,"ask");return Array.from(s.values()).sort((e,t)=>t.price-e.price)}function N(e,t,r,i,s){const o=e.filter(e=>{const s=t(e.price);return s>=-i&&s<=r+i});if(o.length<=s)return o;const n=r/2;return o.map(e=>({r:e,d:Math.abs(t(e.price)-n)})).sort((e,t)=>e.d-t.d).slice(0,s).map(e=>e.r).sort((e,t)=>t.price-e.price)}const q={tickSize:.05,width:96,groupBy:1,maxRows:60,rowHeight:14};class B{constructor(e={}){Object.defineProperty(this,"_opts",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_depth",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"_host",{enumerable:!0,configurable:!0,writable:!0,value:null}),Object.defineProperty(this,"_rowHits",{enumerable:!0,configurable:!0,writable:!0,value:[]}),this._opts={...q,...e}}attached(e){this._host=e}detached(){this._host=null}zOrder(){return"top"}setDepth(e){this._depth=e,this._host?.requestUpdate()}tier(){return null===this._depth?"none":$(this._depth)}draw(e,t){if(this._rowHits=[],null===this._depth)return;const r=N(L(this._depth,this._opts.tickSize,this._opts.groupBy),e=>t.priceScale.priceToY(e),t.plotHeight,this._opts.rowHeight,this._opts.maxRows);if(0===r.length)return;const i=t.dpr,s=this._opts.width*i,o=(t.plotWidth-this._opts.width)*i,c=o+s/2,d=this._opts.rowHeight*i;let h=1;for(const e of r)h=Math.max(h,e.bidQty,e.askQty);const u=n(t.theme.buy)??{r:38,g:166,b:154},p=n(t.theme.sell)??{r:239,g:83,b:80},f=l(a("transparent"===t.theme.background?"#808080":t.theme.background),.9);e.save(),e.strokeStyle=l(t.theme.axisLine,.6),e.lineWidth=Math.max(1,Math.round(i)),e.beginPath(),e.moveTo(Math.round(o)+.5,0),e.lineTo(Math.round(o)+.5,Math.round(t.plotHeight*i)),e.stroke(),e.font=9*i+"px system-ui, sans-serif",e.textBaseline="middle";for(const n of r){const r=t.priceScale.priceToY(n.price)*i,a=t.hoverId===`ladder-bid:${n.price}`,b=t.hoverId===`ladder-ask:${n.price}`;if(n.bidQty>0){e.fillStyle=`rgba(${u.r},${u.g},${u.b},${.15+n.bidQty/h*.5+(a?.15:0)})`;const l=s/2*(n.bidQty/h);e.fillRect(c-l,r-d/2,l,d-1),e.fillStyle=f,e.textAlign="left",e.fillText(String(n.bidQty),o+2*i,r),this._rowHits.push({price:n.price,y:t.priceScale.priceToY(n.price),side:"bid"})}if(n.askQty>0){e.fillStyle=`rgba(${p.r},${p.g},${p.b},${.15+n.askQty/h*.5+(b?.15:0)})`;const a=s/2*(n.askQty/h);e.fillRect(c,r-d/2,a,d-1),e.fillStyle=f,e.textAlign="right",e.fillText(String(n.askQty),o+s-2*i,r),this._rowHits.push({price:n.price,y:t.priceScale.priceToY(n.price),side:"ask"})}(a||b)&&(e.strokeStyle=l(a?t.theme.buy:t.theme.sell,.9),e.lineWidth=Math.max(1,Math.round(i)),e.strokeRect(a?o+.5:c+.5,r-d/2+.5,s/2-1,d-2))}e.restore()}hitTest(e,t,r){if(null===this._depth)return null;if(e<r.plotWidth-this._opts.width||e>r.plotWidth)return null;let i=null;for(const e of this._rowHits){const r=Math.abs(e.y-t);r<=this._opts.rowHeight/2&&(null===i||r<i.d)&&(i={price:e.price,side:e.side,d:r})}return null===i?null:{externalId:`ladder-${i.side}:${i.price}`,zOrder:"top",distance:i.d,cursor:"pointer"}}}const A="trade";export{b as BracketGroup,q as DEFAULT_DOM_LADDER_OPTIONS,B as DomLadder,g as FakeBroker,j as OrderEngine,f as PositionMarker,A as TRADE_TIER,_ as TradeController,p as WorkingOrderLine,o as bracketValid,i as breakeven,L as buildRows,k as canTransition,S as isPreflightFailure,y as isTerminal,e as isWorking,$ as ladderCapability,s as riskReward,M as transition,t as unrealizedPnl,r as unrealizedPnlPercent,O as validateOrder,P as validatePrice,w as validateQuantity,N as visibleRows,x as withinPriceBand}; //# sourceMappingURL=openalgo-charts.trade.mjs.map